feat(sprites): add sprites-url-get and sprites-url-set tools

New tools for managing sprite URL access settings:
- sprites-url-get: Get the public URL and auth settings for a sprite
- sprites-url-set: Set URL auth to 'public' or 'sprite' (private)

Both published as @tpmjs/tools-sprites-url-get and @tpmjs/tools-sprites-url-set v0.1.0
This commit is contained in:
Ajax Davis 2026-01-15 04:56:52 +10:00
parent e4c09a475f
commit 27e7d45646
9 changed files with 455 additions and 0 deletions

View file

@ -0,0 +1,70 @@
{
"name": "@tpmjs/tools-sprites-url-get",
"version": "0.1.0",
"description": "Get the public URL and access settings for a sprite",
"type": "module",
"keywords": [
"tpmjs",
"sprites",
"sandbox",
"code-execution",
"ai"
],
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"files": [
"dist"
],
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"type-check": "tsc --noEmit",
"clean": "rm -rf dist .turbo"
},
"devDependencies": {
"@tpmjs/tsconfig": "workspace:*",
"tsup": "^8.5.1",
"typescript": "^5.9.3"
},
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/anthropics/tpmjs.git",
"directory": "packages/tools/official/sprites-url-get"
},
"homepage": "https://tpmjs.com",
"license": "MIT",
"tpmjs": {
"category": "sandbox",
"frameworks": [
"vercel-ai"
],
"tools": [
{
"name": "spritesUrlGetTool",
"description": "Get the public URL and access settings for a sprite. Returns the URL that can be used to access web servers running on port 8080 in the sprite.",
"parameters": [
{
"name": "name",
"type": "string",
"description": "Name of the sprite to get URL for",
"required": true
}
],
"returns": {
"type": "SpriteUrl",
"description": "Sprite URL and auth settings"
}
}
]
},
"dependencies": {
"ai": "6.0.23"
}
}

View file

@ -0,0 +1,110 @@
/**
* Sprites URL Get Tool for TPMJS
* Retrieves the public URL and access settings for a sprite.
*
* @requires SPRITES_TOKEN environment variable
*/
import { jsonSchema, tool } from 'ai';
const SPRITES_API_BASE = 'https://api.sprites.dev/v1';
export interface SpriteUrl {
url: string;
auth: 'sprite' | 'public';
name: string;
status: string;
}
type SpritesUrlGetInput = {
name: string;
};
function getSpritesToken(): string {
const token = process.env.SPRITES_TOKEN;
if (!token) {
throw new Error(
'SPRITES_TOKEN environment variable is required. Get your token from https://sprites.dev'
);
}
return token;
}
export const spritesUrlGetTool = tool({
description:
'Get the public URL and access settings for a sprite. Returns the URL that can be used to access web servers running on port 8080 in the sprite. The URL format is https://{name}-{id}.sprites.app',
inputSchema: jsonSchema<SpritesUrlGetInput>({
type: 'object',
properties: {
name: {
type: 'string',
description: 'Name of the sprite to get URL for',
},
},
required: ['name'],
additionalProperties: false,
}),
async execute({ name }): Promise<SpriteUrl> {
if (!name || typeof name !== 'string') {
throw new Error('Sprite name is required and must be a string');
}
const token = getSpritesToken();
let response: Response;
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 30000);
response = await fetch(`${SPRITES_API_BASE}/sprites/${encodeURIComponent(name)}`, {
method: 'GET',
headers: {
Authorization: `Bearer ${token}`,
'User-Agent': 'TPMJS/1.0',
},
signal: controller.signal,
});
clearTimeout(timeoutId);
} catch (error) {
if (error instanceof Error) {
if (error.name === 'AbortError') {
throw new Error(`Request to get URL for sprite "${name}" timed out`);
}
throw new Error(`Failed to get URL for sprite "${name}": ${error.message}`);
}
throw new Error(`Failed to get URL for sprite "${name}": Unknown network error`);
}
if (!response.ok) {
if (response.status === 404) {
throw new Error(`Sprite "${name}" not found`);
}
if (response.status === 401) {
throw new Error('Invalid SPRITES_TOKEN. Check your API token at https://sprites.dev');
}
const errorText = await response.text().catch(() => 'Unknown error');
throw new Error(
`Failed to get URL for sprite "${name}": HTTP ${response.status} - ${errorText}`
);
}
let data: Record<string, unknown>;
try {
data = (await response.json()) as Record<string, unknown>;
} catch {
throw new Error('Failed to parse response from Sprites API');
}
const urlSettings = data.url_settings as Record<string, unknown> | undefined;
return {
url: (data.url as string) || '',
auth: (urlSettings?.auth as 'sprite' | 'public') || 'sprite',
name: (data.name as string) || name,
status: (data.status as string) || 'unknown',
};
},
});
export default spritesUrlGetTool;

View file

@ -0,0 +1,11 @@
{
"extends": "@tpmjs/tsconfig/base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"incremental": false,
"composite": false
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}

View file

@ -0,0 +1,10 @@
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm'],
dts: true,
clean: true,
treeshake: true,
splitting: false,
});

View file

@ -0,0 +1,76 @@
{
"name": "@tpmjs/tools-sprites-url-set",
"version": "0.1.0",
"description": "Set the access settings for a sprite URL (public or private)",
"type": "module",
"keywords": [
"tpmjs",
"sprites",
"sandbox",
"code-execution",
"ai"
],
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"files": [
"dist"
],
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"type-check": "tsc --noEmit",
"clean": "rm -rf dist .turbo"
},
"devDependencies": {
"@tpmjs/tsconfig": "workspace:*",
"tsup": "^8.5.1",
"typescript": "^5.9.3"
},
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/anthropics/tpmjs.git",
"directory": "packages/tools/official/sprites-url-set"
},
"homepage": "https://tpmjs.com",
"license": "MIT",
"tpmjs": {
"category": "sandbox",
"frameworks": [
"vercel-ai"
],
"tools": [
{
"name": "spritesUrlSetTool",
"description": "Set the access settings for a sprite URL. Use 'public' to allow anyone to access the sprite URL, or 'sprite' to require authentication.",
"parameters": [
{
"name": "name",
"type": "string",
"description": "Name of the sprite to update",
"required": true
},
{
"name": "auth",
"type": "string",
"description": "Access mode - 'public' for public access, 'sprite' for authenticated access only",
"required": true
}
],
"returns": {
"type": "SpriteUrlResult",
"description": "Updated sprite URL and settings"
}
}
]
},
"dependencies": {
"ai": "6.0.23"
}
}

View file

@ -0,0 +1,125 @@
/**
* Sprites URL Set Tool for TPMJS
* Sets the access settings for a sprite URL (public or private).
*
* @requires SPRITES_TOKEN environment variable
*/
import { jsonSchema, tool } from 'ai';
const SPRITES_API_BASE = 'https://api.sprites.dev/v1';
export interface SpriteUrlResult {
url: string;
auth: 'sprite' | 'public';
name: string;
updated: boolean;
}
type SpritesUrlSetInput = {
name: string;
auth: 'sprite' | 'public';
};
function getSpritesToken(): string {
const token = process.env.SPRITES_TOKEN;
if (!token) {
throw new Error(
'SPRITES_TOKEN environment variable is required. Get your token from https://sprites.dev'
);
}
return token;
}
export const spritesUrlSetTool = tool({
description:
"Set the access settings for a sprite URL. Use 'public' to allow anyone to access the sprite URL (for web servers, APIs, webhooks), or 'sprite' to require authentication. The sprite must have a server listening on port 8080 for the URL to work.",
inputSchema: jsonSchema<SpritesUrlSetInput>({
type: 'object',
properties: {
name: {
type: 'string',
description: 'Name of the sprite to update',
},
auth: {
type: 'string',
enum: ['sprite', 'public'],
description:
"Access mode - 'public' for public access, 'sprite' for authenticated access only",
},
},
required: ['name', 'auth'],
additionalProperties: false,
}),
async execute({ name, auth }): Promise<SpriteUrlResult> {
if (!name || typeof name !== 'string') {
throw new Error('Sprite name is required and must be a string');
}
if (auth !== 'sprite' && auth !== 'public') {
throw new Error("Auth must be 'sprite' or 'public'");
}
const token = getSpritesToken();
let response: Response;
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 30000);
// PUT /v1/sprites/{name} with url_settings
response = await fetch(`${SPRITES_API_BASE}/sprites/${encodeURIComponent(name)}`, {
method: 'PUT',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
'User-Agent': 'TPMJS/1.0',
},
body: JSON.stringify({
url_settings: { auth },
}),
signal: controller.signal,
});
clearTimeout(timeoutId);
} catch (error) {
if (error instanceof Error) {
if (error.name === 'AbortError') {
throw new Error(`Request to set URL auth for sprite "${name}" timed out`);
}
throw new Error(`Failed to set URL auth for sprite "${name}": ${error.message}`);
}
throw new Error(`Failed to set URL auth for sprite "${name}": Unknown network error`);
}
if (!response.ok) {
if (response.status === 404) {
throw new Error(`Sprite "${name}" not found`);
}
if (response.status === 401) {
throw new Error('Invalid SPRITES_TOKEN. Check your API token at https://sprites.dev');
}
const errorText = await response.text().catch(() => 'Unknown error');
throw new Error(
`Failed to set URL auth for sprite "${name}": HTTP ${response.status} - ${errorText}`
);
}
let data: Record<string, unknown>;
try {
data = (await response.json()) as Record<string, unknown>;
} catch {
throw new Error('Failed to parse response from Sprites API');
}
const urlSettings = data.url_settings as Record<string, unknown> | undefined;
return {
url: (data.url as string) || '',
auth: (urlSettings?.auth as 'sprite' | 'public') || auth,
name: (data.name as string) || name,
updated: true,
};
},
});
export default spritesUrlSetTool;

View file

@ -0,0 +1,11 @@
{
"extends": "@tpmjs/tsconfig/base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"incremental": false,
"composite": false
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}

View file

@ -0,0 +1,10 @@
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm'],
dts: true,
clean: true,
treeshake: true,
splitting: false,
});

32
pnpm-lock.yaml generated
View file

@ -3187,6 +3187,38 @@ importers:
specifier: ^5.9.3
version: 5.9.3
packages/tools/official/sprites-url-get:
dependencies:
ai:
specifier: 6.0.23
version: 6.0.23(zod@4.3.5)
devDependencies:
'@tpmjs/tsconfig':
specifier: workspace:*
version: link:../../../config/tsconfig
tsup:
specifier: ^8.5.1
version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)
typescript:
specifier: ^5.9.3
version: 5.9.3
packages/tools/official/sprites-url-set:
dependencies:
ai:
specifier: 6.0.23
version: 6.0.23(zod@4.3.5)
devDependencies:
'@tpmjs/tsconfig':
specifier: workspace:*
version: link:../../../config/tsconfig
tsup:
specifier: ^8.5.1
version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)
typescript:
specifier: ^5.9.3
version: 5.9.3
packages/tools/official/stacktrace-parse:
dependencies:
ai: