diff --git a/packages/tools/official/sprites-checkpoint-create/package.json b/packages/tools/official/sprites-checkpoint-create/package.json index a2fb1b4..775d689 100644 --- a/packages/tools/official/sprites-checkpoint-create/package.json +++ b/packages/tools/official/sprites-checkpoint-create/package.json @@ -1,6 +1,6 @@ { "name": "@tpmjs/tools-sprites-checkpoint-create", - "version": "0.1.2", + "version": "0.1.4", "description": "Create a point-in-time snapshot of a sprite's filesystem state for later restoration", "type": "module", "keywords": [ diff --git a/packages/tools/official/sprites-checkpoint-create/src/index.ts b/packages/tools/official/sprites-checkpoint-create/src/index.ts index 14f7537..edd7c0b 100644 --- a/packages/tools/official/sprites-checkpoint-create/src/index.ts +++ b/packages/tools/official/sprites-checkpoint-create/src/index.ts @@ -66,19 +66,18 @@ export const spritesCheckpointCreateTool = tool({ body.name = checkpointName; } - response = await fetch( - `${SPRITES_API_BASE}/sprites/${encodeURIComponent(name)}/checkpoints`, - { - method: 'POST', - headers: { - Authorization: `Bearer ${token}`, - 'Content-Type': 'application/json', - 'User-Agent': 'TPMJS/1.0', - }, - body: JSON.stringify(body), - signal: controller.signal, - } - ); + // API endpoint: POST /v1/sprites/{name}/checkpoint (singular, not plural) + // Note: Returns application/x-ndjson streaming progress events + response = await fetch(`${SPRITES_API_BASE}/sprites/${encodeURIComponent(name)}/checkpoint`, { + method: 'POST', + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + 'User-Agent': 'TPMJS/1.0', + }, + body: JSON.stringify(body), + signal: controller.signal, + }); clearTimeout(timeoutId); } catch (error) { @@ -104,19 +103,71 @@ export const spritesCheckpointCreateTool = tool({ ); } - let data: Record; + // API returns application/x-ndjson streaming progress events + // Each line is a JSON object, we need to parse all lines and find the result + let text: string; try { - data = (await response.json()) as Record; + text = await response.text(); } catch { - throw new Error('Failed to parse response from Sprites API'); + throw new Error('Failed to read response from Sprites API'); + } + + // Parse NDJSON - split by newlines and parse each line as JSON + // Format: {"type":"info"|"complete"|"error","data":"...","time":"..."} + // The checkpoint ID is in lines like: + // {"type":"info","data":" ID: v2",...} + // {"type":"complete","data":"Checkpoint v2 created successfully",...} + const lines = text + .trim() + .split('\n') + .filter((line) => line.trim()); + let checkpointId = ''; + let createdAt = ''; + + for (const line of lines) { + try { + const parsed = JSON.parse(line) as { type?: string; data?: string; time?: string }; + + // Check for error + if (parsed.type === 'error') { + throw new Error(`Checkpoint creation failed: ${parsed.data || 'Unknown error'}`); + } + + // Extract checkpoint ID from " ID: v2" line + if (parsed.type === 'info' && parsed.data?.trim().startsWith('ID:')) { + checkpointId = parsed.data.trim().replace('ID:', '').trim(); + } + + // Extract created time from " Created: 2026-01-14 18:23:26" line + if (parsed.type === 'info' && parsed.data?.trim().startsWith('Created:')) { + createdAt = parsed.data.trim().replace('Created:', '').trim(); + } + + // Also try to extract from complete message like "Checkpoint v2 created successfully" + if (parsed.type === 'complete' && parsed.data && !checkpointId) { + const match = parsed.data.match(/Checkpoint\s+(\S+)\s+created/); + if (match && match[1]) { + checkpointId = match[1]; + } + } + } catch (parseError) { + // Skip lines that aren't valid JSON (could be progress messages) + if (parseError instanceof SyntaxError) continue; + throw parseError; + } + } + + if (!checkpointId) { + throw new Error( + `Failed to extract checkpoint ID from response. Raw response: ${text.slice(0, 300)}` + ); } return { - id: (data.id as string) || '', - name: (data.name as string) || checkpointName, - createdAt: - (data.createdAt as string) || (data.created_at as string) || new Date().toISOString(), - size: data.size as number | undefined, + id: checkpointId, + name: checkpointName || checkpointId, + createdAt: createdAt || new Date().toISOString(), + size: undefined, }; }, }); diff --git a/packages/tools/official/sprites-policy-get/package.json b/packages/tools/official/sprites-policy-get/package.json index 2f38489..8ef169e 100644 --- a/packages/tools/official/sprites-policy-get/package.json +++ b/packages/tools/official/sprites-policy-get/package.json @@ -1,6 +1,6 @@ { "name": "@tpmjs/tools-sprites-policy-get", - "version": "0.1.2", + "version": "0.1.3", "description": "Retrieve the current network policy for a sprite including allowed domains", "type": "module", "keywords": [ diff --git a/packages/tools/official/sprites-policy-get/src/index.ts b/packages/tools/official/sprites-policy-get/src/index.ts index 010907b..49eb837 100644 --- a/packages/tools/official/sprites-policy-get/src/index.ts +++ b/packages/tools/official/sprites-policy-get/src/index.ts @@ -55,14 +55,18 @@ export const spritesPolicyGetTool = tool({ const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 30000); - response = await fetch(`${SPRITES_API_BASE}/sprites/${encodeURIComponent(name)}/policies`, { - method: 'GET', - headers: { - Authorization: `Bearer ${token}`, - 'User-Agent': 'TPMJS/1.0', - }, - signal: controller.signal, - }); + // API endpoint: GET /v1/sprites/{name}/policy/network + response = await fetch( + `${SPRITES_API_BASE}/sprites/${encodeURIComponent(name)}/policy/network`, + { + method: 'GET', + headers: { + Authorization: `Bearer ${token}`, + 'User-Agent': 'TPMJS/1.0', + }, + signal: controller.signal, + } + ); clearTimeout(timeoutId); } catch (error) { diff --git a/packages/tools/official/sprites-policy-set/package.json b/packages/tools/official/sprites-policy-set/package.json index 201e031..766e6c2 100644 --- a/packages/tools/official/sprites-policy-set/package.json +++ b/packages/tools/official/sprites-policy-set/package.json @@ -1,6 +1,6 @@ { "name": "@tpmjs/tools-sprites-policy-set", - "version": "0.1.2", + "version": "0.1.3", "description": "Update the network policy for a sprite to control outbound network access", "type": "module", "keywords": [ diff --git a/packages/tools/official/sprites-policy-set/src/index.ts b/packages/tools/official/sprites-policy-set/src/index.ts index 6e46d2b..fb1d679 100644 --- a/packages/tools/official/sprites-policy-set/src/index.ts +++ b/packages/tools/official/sprites-policy-set/src/index.ts @@ -79,16 +79,27 @@ export const spritesPolicySetTool = tool({ const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 30000); - response = await fetch(`${SPRITES_API_BASE}/sprites/${encodeURIComponent(name)}/policies`, { - method: 'POST', - headers: { - Authorization: `Bearer ${token}`, - 'Content-Type': 'application/json', - 'User-Agent': 'TPMJS/1.0', - }, - body: JSON.stringify({ mode, domains }), - signal: controller.signal, - }); + // API endpoint: POST /v1/sprites/{name}/policy/network + // Body format: { "rules": [{ "domain": "...", "action": "allow" | "deny" }] } + // Convert our mode/domains format to rules array + const rules = domains.map((domain) => ({ + domain, + action: mode, // 'allow' or 'deny' + })); + + response = await fetch( + `${SPRITES_API_BASE}/sprites/${encodeURIComponent(name)}/policy/network`, + { + method: 'POST', + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + 'User-Agent': 'TPMJS/1.0', + }, + body: JSON.stringify({ rules }), + signal: controller.signal, + } + ); clearTimeout(timeoutId); } catch (error) { diff --git a/packages/tools/official/sprites-sessions/package.json b/packages/tools/official/sprites-sessions/package.json index 7c505f6..1ebd731 100644 --- a/packages/tools/official/sprites-sessions/package.json +++ b/packages/tools/official/sprites-sessions/package.json @@ -1,6 +1,6 @@ { "name": "@tpmjs/tools-sprites-sessions", - "version": "0.1.2", + "version": "0.1.3", "description": "List active execution sessions for a sprite", "type": "module", "keywords": [ diff --git a/packages/tools/official/sprites-sessions/src/index.ts b/packages/tools/official/sprites-sessions/src/index.ts index 1820c88..1fc47d2 100644 --- a/packages/tools/official/sprites-sessions/src/index.ts +++ b/packages/tools/official/sprites-sessions/src/index.ts @@ -61,17 +61,15 @@ export const spritesSessionsTool = tool({ const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 30000); - response = await fetch( - `${SPRITES_API_BASE}/sprites/${encodeURIComponent(name)}/exec/sessions`, - { - method: 'GET', - headers: { - Authorization: `Bearer ${token}`, - 'User-Agent': 'TPMJS/1.0', - }, - signal: controller.signal, - } - ); + // API endpoint: GET /v1/sprites/{name}/exec lists active exec sessions + response = await fetch(`${SPRITES_API_BASE}/sprites/${encodeURIComponent(name)}/exec`, { + method: 'GET', + headers: { + Authorization: `Bearer ${token}`, + 'User-Agent': 'TPMJS/1.0', + }, + signal: controller.signal, + }); clearTimeout(timeoutId); } catch (error) {