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",
|
||||
"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": [
|
||||
|
|
|
|||
|
|
@ -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<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 {
|
||||
data = (await response.json()) as Record<string, unknown>;
|
||||
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,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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": [
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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": [
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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": [
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue