fix: correct API endpoints for sprites tools v0.1.3/v0.1.4
Fixed URL endpoints based on Sprites API documentation: - sprites-sessions: /exec/sessions -> /exec (v0.1.3) - sprites-policy-get: /policies -> /policy/network (v0.1.3) - sprites-policy-set: /policies -> /policy/network with rules[] body (v0.1.3) - sprites-checkpoint-create: /checkpoints -> /checkpoint singular (v0.1.4) Also fixed checkpoint-create to properly parse NDJSON streaming response format with type/data/time fields.
This commit is contained in:
parent
e6857e245e
commit
e4c09a475f
8 changed files with 118 additions and 54 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@tpmjs/tools-sprites-checkpoint-create",
|
"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",
|
"description": "Create a point-in-time snapshot of a sprite's filesystem state for later restoration",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|
|
||||||
|
|
@ -66,19 +66,18 @@ export const spritesCheckpointCreateTool = tool({
|
||||||
body.name = checkpointName;
|
body.name = checkpointName;
|
||||||
}
|
}
|
||||||
|
|
||||||
response = await fetch(
|
// API endpoint: POST /v1/sprites/{name}/checkpoint (singular, not plural)
|
||||||
`${SPRITES_API_BASE}/sprites/${encodeURIComponent(name)}/checkpoints`,
|
// Note: Returns application/x-ndjson streaming progress events
|
||||||
{
|
response = await fetch(`${SPRITES_API_BASE}/sprites/${encodeURIComponent(name)}/checkpoint`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${token}`,
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'User-Agent': 'TPMJS/1.0',
|
'User-Agent': 'TPMJS/1.0',
|
||||||
},
|
},
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -104,19 +103,71 @@ export const spritesCheckpointCreateTool = tool({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let data: Record<string, unknown>;
|
// 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 {
|
try {
|
||||||
data = (await response.json()) as Record<string, unknown>;
|
text = await response.text();
|
||||||
} catch {
|
} 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 {
|
return {
|
||||||
id: (data.id as string) || '',
|
id: checkpointId,
|
||||||
name: (data.name as string) || checkpointName,
|
name: checkpointName || checkpointId,
|
||||||
createdAt:
|
createdAt: createdAt || new Date().toISOString(),
|
||||||
(data.createdAt as string) || (data.created_at as string) || new Date().toISOString(),
|
size: undefined,
|
||||||
size: data.size as number | undefined,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@tpmjs/tools-sprites-policy-get",
|
"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",
|
"description": "Retrieve the current network policy for a sprite including allowed domains",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|
|
||||||
|
|
@ -55,14 +55,18 @@ export const spritesPolicyGetTool = tool({
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeoutId = setTimeout(() => controller.abort(), 30000);
|
const timeoutId = setTimeout(() => controller.abort(), 30000);
|
||||||
|
|
||||||
response = await fetch(`${SPRITES_API_BASE}/sprites/${encodeURIComponent(name)}/policies`, {
|
// API endpoint: GET /v1/sprites/{name}/policy/network
|
||||||
method: 'GET',
|
response = await fetch(
|
||||||
headers: {
|
`${SPRITES_API_BASE}/sprites/${encodeURIComponent(name)}/policy/network`,
|
||||||
Authorization: `Bearer ${token}`,
|
{
|
||||||
'User-Agent': 'TPMJS/1.0',
|
method: 'GET',
|
||||||
},
|
headers: {
|
||||||
signal: controller.signal,
|
Authorization: `Bearer ${token}`,
|
||||||
});
|
'User-Agent': 'TPMJS/1.0',
|
||||||
|
},
|
||||||
|
signal: controller.signal,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@tpmjs/tools-sprites-policy-set",
|
"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",
|
"description": "Update the network policy for a sprite to control outbound network access",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|
|
||||||
|
|
@ -79,16 +79,27 @@ export const spritesPolicySetTool = tool({
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeoutId = setTimeout(() => controller.abort(), 30000);
|
const timeoutId = setTimeout(() => controller.abort(), 30000);
|
||||||
|
|
||||||
response = await fetch(`${SPRITES_API_BASE}/sprites/${encodeURIComponent(name)}/policies`, {
|
// API endpoint: POST /v1/sprites/{name}/policy/network
|
||||||
method: 'POST',
|
// Body format: { "rules": [{ "domain": "...", "action": "allow" | "deny" }] }
|
||||||
headers: {
|
// Convert our mode/domains format to rules array
|
||||||
Authorization: `Bearer ${token}`,
|
const rules = domains.map((domain) => ({
|
||||||
'Content-Type': 'application/json',
|
domain,
|
||||||
'User-Agent': 'TPMJS/1.0',
|
action: mode, // 'allow' or 'deny'
|
||||||
},
|
}));
|
||||||
body: JSON.stringify({ mode, domains }),
|
|
||||||
signal: controller.signal,
|
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);
|
clearTimeout(timeoutId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@tpmjs/tools-sprites-sessions",
|
"name": "@tpmjs/tools-sprites-sessions",
|
||||||
"version": "0.1.2",
|
"version": "0.1.3",
|
||||||
"description": "List active execution sessions for a sprite",
|
"description": "List active execution sessions for a sprite",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|
|
||||||
|
|
@ -61,17 +61,15 @@ export const spritesSessionsTool = tool({
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeoutId = setTimeout(() => controller.abort(), 30000);
|
const timeoutId = setTimeout(() => controller.abort(), 30000);
|
||||||
|
|
||||||
response = await fetch(
|
// API endpoint: GET /v1/sprites/{name}/exec lists active exec sessions
|
||||||
`${SPRITES_API_BASE}/sprites/${encodeURIComponent(name)}/exec/sessions`,
|
response = await fetch(`${SPRITES_API_BASE}/sprites/${encodeURIComponent(name)}/exec`, {
|
||||||
{
|
method: 'GET',
|
||||||
method: 'GET',
|
headers: {
|
||||||
headers: {
|
Authorization: `Bearer ${token}`,
|
||||||
Authorization: `Bearer ${token}`,
|
'User-Agent': 'TPMJS/1.0',
|
||||||
'User-Agent': 'TPMJS/1.0',
|
},
|
||||||
},
|
signal: controller.signal,
|
||||||
signal: controller.signal,
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue