mandala-computer-mcp 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +462 -37
- package/dist/api.d.ts +12 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +262 -42
- package/dist/api.js.map +1 -1
- package/dist/artifacts.d.ts +63 -0
- package/dist/artifacts.d.ts.map +1 -0
- package/dist/artifacts.js +81 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +35 -21
- package/dist/cli.js.map +1 -1
- package/dist/credentials.d.ts +33 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +395 -0
- package/dist/credentials.js.map +1 -0
- package/dist/errors.d.ts +53 -10
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +122 -35
- package/dist/errors.js.map +1 -1
- package/dist/events.d.ts +11 -2
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +213 -95
- package/dist/events.js.map +1 -1
- package/dist/executions.d.ts +43 -0
- package/dist/executions.d.ts.map +1 -0
- package/dist/executions.js +162 -0
- package/dist/executions.js.map +1 -0
- package/dist/format.d.ts +11 -11
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +159 -16
- package/dist/format.js.map +1 -1
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +4 -0
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/limits.d.ts +17 -0
- package/dist/limits.d.ts.map +1 -0
- package/dist/limits.js +17 -0
- package/dist/limits.js.map +1 -0
- package/dist/paths.d.ts +42 -2
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +54 -3
- package/dist/paths.js.map +1 -1
- package/dist/poll.d.ts +5 -1
- package/dist/poll.d.ts.map +1 -1
- package/dist/poll.js +120 -16
- package/dist/poll.js.map +1 -1
- package/dist/results.d.ts +97 -0
- package/dist/results.d.ts.map +1 -0
- package/dist/results.js +263 -0
- package/dist/results.js.map +1 -0
- package/dist/server.d.ts +3 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +61 -9
- package/dist/server.js.map +1 -1
- package/dist/stdio.d.ts +5 -1
- package/dist/stdio.d.ts.map +1 -1
- package/dist/stdio.js +10 -4
- package/dist/stdio.js.map +1 -1
- package/dist/tool-filters.d.ts +38 -0
- package/dist/tool-filters.d.ts.map +1 -0
- package/dist/tool-filters.js +129 -0
- package/dist/tool-filters.js.map +1 -0
- package/dist/tools/account.d.ts +3 -0
- package/dist/tools/account.d.ts.map +1 -0
- package/dist/tools/account.js +122 -0
- package/dist/tools/account.js.map +1 -0
- package/dist/tools/activities.d.ts +3 -0
- package/dist/tools/activities.d.ts.map +1 -0
- package/dist/tools/activities.js +140 -0
- package/dist/tools/activities.js.map +1 -0
- package/dist/tools/agent.d.ts.map +1 -1
- package/dist/tools/agent.js +72 -5
- package/dist/tools/agent.js.map +1 -1
- package/dist/tools/artifacts.d.ts +3 -0
- package/dist/tools/artifacts.d.ts.map +1 -0
- package/dist/tools/artifacts.js +97 -0
- package/dist/tools/artifacts.js.map +1 -0
- package/dist/tools/chat.d.ts +6 -0
- package/dist/tools/chat.d.ts.map +1 -0
- package/dist/tools/chat.js +192 -0
- package/dist/tools/chat.js.map +1 -0
- package/dist/tools/computers.d.ts.map +1 -1
- package/dist/tools/computers.js +300 -247
- package/dist/tools/computers.js.map +1 -1
- package/dist/tools/directory.d.ts +13 -0
- package/dist/tools/directory.d.ts.map +1 -0
- package/dist/tools/directory.js +88 -0
- package/dist/tools/directory.js.map +1 -0
- package/dist/tools/events.d.ts.map +1 -1
- package/dist/tools/events.js +66 -22
- package/dist/tools/events.js.map +1 -1
- package/dist/tools/executions.d.ts +3 -0
- package/dist/tools/executions.d.ts.map +1 -0
- package/dist/tools/executions.js +87 -0
- package/dist/tools/executions.js.map +1 -0
- package/dist/tools/guest.d.ts.map +1 -1
- package/dist/tools/guest.js +57 -17
- package/dist/tools/guest.js.map +1 -1
- package/dist/tools/input.d.ts.map +1 -1
- package/dist/tools/input.js +63 -5
- package/dist/tools/input.js.map +1 -1
- package/dist/tools/results.d.ts +30 -0
- package/dist/tools/results.d.ts.map +1 -0
- package/dist/tools/results.js +106 -0
- package/dist/tools/results.js.map +1 -0
- package/dist/tools/secrets.d.ts +58 -0
- package/dist/tools/secrets.d.ts.map +1 -0
- package/dist/tools/secrets.js +204 -0
- package/dist/tools/secrets.js.map +1 -0
- package/dist/tools/signals.d.ts +3 -0
- package/dist/tools/signals.d.ts.map +1 -0
- package/dist/tools/signals.js +116 -0
- package/dist/tools/signals.js.map +1 -0
- package/dist/tools/snapshots.d.ts.map +1 -1
- package/dist/tools/snapshots.js +213 -167
- package/dist/tools/snapshots.js.map +1 -1
- package/dist/tools/ssh.d.ts +3 -0
- package/dist/tools/ssh.d.ts.map +1 -0
- package/dist/tools/ssh.js +186 -0
- package/dist/tools/ssh.js.map +1 -0
- package/dist/tools/templates.d.ts.map +1 -1
- package/dist/tools/templates.js +9 -13
- package/dist/tools/templates.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { said } from '../format.js';
|
|
3
|
+
import * as P from '../paths.js';
|
|
4
|
+
import { count, executionId, exitCode, label, metadata, metadataCall } from './directory.js';
|
|
5
|
+
import { computerSchema, readAnnotations } from './results.js';
|
|
6
|
+
const position = {
|
|
7
|
+
seq: count,
|
|
8
|
+
cursor: label,
|
|
9
|
+
at: label,
|
|
10
|
+
computer: label,
|
|
11
|
+
source: z.literal('daemon'),
|
|
12
|
+
};
|
|
13
|
+
const status = z.enum([
|
|
14
|
+
'running',
|
|
15
|
+
'stopped',
|
|
16
|
+
'suspended',
|
|
17
|
+
'building',
|
|
18
|
+
'build-failed',
|
|
19
|
+
'half-removed',
|
|
20
|
+
]);
|
|
21
|
+
const processData = z.union([
|
|
22
|
+
z.object({
|
|
23
|
+
pid: count.min(1).max(2147483647),
|
|
24
|
+
execution_id: executionId.optional(),
|
|
25
|
+
exit_code: exitCode,
|
|
26
|
+
lost: z.never().optional(),
|
|
27
|
+
}),
|
|
28
|
+
z.object({
|
|
29
|
+
pid: count.min(1).max(2147483647),
|
|
30
|
+
execution_id: executionId.optional(),
|
|
31
|
+
lost: z.literal(true),
|
|
32
|
+
exit_code: z.never().optional(),
|
|
33
|
+
}),
|
|
34
|
+
]);
|
|
35
|
+
const event = z.discriminatedUnion('type', [
|
|
36
|
+
z.object({ ...position, type: z.literal('process.exited'), data: processData }),
|
|
37
|
+
z.object({
|
|
38
|
+
...position,
|
|
39
|
+
type: z.literal('computer.started'),
|
|
40
|
+
data: z.object({ status: z.literal('running'), previous: status.optional() }),
|
|
41
|
+
}),
|
|
42
|
+
z.object({
|
|
43
|
+
...position,
|
|
44
|
+
type: z.literal('computer.stopped'),
|
|
45
|
+
data: z.object({ status: z.literal('stopped'), previous: status.optional() }),
|
|
46
|
+
}),
|
|
47
|
+
z.object({
|
|
48
|
+
...position,
|
|
49
|
+
type: z.literal('computer.suspended'),
|
|
50
|
+
data: z.object({ status: z.literal('suspended'), previous: status.optional() }),
|
|
51
|
+
}),
|
|
52
|
+
z.object({
|
|
53
|
+
...position,
|
|
54
|
+
type: z.literal('computer.idle'),
|
|
55
|
+
data: z.object({ idle_seconds: count }),
|
|
56
|
+
}),
|
|
57
|
+
]);
|
|
58
|
+
const page = z.object({
|
|
59
|
+
computer: label,
|
|
60
|
+
from: z.string(),
|
|
61
|
+
cursor: label,
|
|
62
|
+
events: z.array(event).max(100),
|
|
63
|
+
more: z.boolean(),
|
|
64
|
+
baseline: z.boolean(),
|
|
65
|
+
gap: z
|
|
66
|
+
.object({
|
|
67
|
+
cursor: label,
|
|
68
|
+
at: label,
|
|
69
|
+
type: z.literal('gap'),
|
|
70
|
+
computer: label,
|
|
71
|
+
source: z.literal('daemon'),
|
|
72
|
+
data: z.object({ oldest_cursor: label.optional(), detail: label }),
|
|
73
|
+
})
|
|
74
|
+
.optional(),
|
|
75
|
+
supported: z.array(label),
|
|
76
|
+
retention: z.literal('ephemeral'),
|
|
77
|
+
});
|
|
78
|
+
export const registerSignals = (server, session) => {
|
|
79
|
+
server.registerTool('read_signals', {
|
|
80
|
+
title: 'Read one passive platform signal page',
|
|
81
|
+
description: 'Read finite daemon facts without waking the computer, guest contact, event socket, watcher, capture or polling loop. Omit since or send empty for a head-only baseline with no replay; neither a baseline nor gap proves no earlier activity or task success. Keep the returned cursor even on an empty page: filtered-out rows can advance it. Retention is ephemeral, and resets are explicit gaps.',
|
|
82
|
+
inputSchema: {
|
|
83
|
+
computer_id: computerSchema,
|
|
84
|
+
since: z.string().max(2048).optional(),
|
|
85
|
+
limit: z.number().int().min(1).max(100).optional(),
|
|
86
|
+
},
|
|
87
|
+
annotations: readAnnotations,
|
|
88
|
+
}, ({ computer_id, since, limit }, extra) => metadataCall(async () => {
|
|
89
|
+
const id = session.resolve(computer_id);
|
|
90
|
+
const data = metadata(page, await session.api.json('GET', P.signals(id), {
|
|
91
|
+
query: { since, limit },
|
|
92
|
+
signal: extra.signal,
|
|
93
|
+
}));
|
|
94
|
+
// Compare opaque checkpoints without parsing or deriving a next cursor.
|
|
95
|
+
// Baselines and explicit resets describe one head; replay starts at since.
|
|
96
|
+
// Events must advance; the next cursor may also pass filtered rows.
|
|
97
|
+
const checkpoints = new Set([data.from, ...data.events.map((e) => e.cursor)]);
|
|
98
|
+
if (data.computer !== id ||
|
|
99
|
+
data.events.some((e, i) => e.computer !== id || (i > 0 && e.seq <= data.events[i - 1].seq)) ||
|
|
100
|
+
checkpoints.size !== data.events.length + 1 ||
|
|
101
|
+
(data.events.length > 0 &&
|
|
102
|
+
data.cursor !== data.events.at(-1)?.cursor &&
|
|
103
|
+
checkpoints.has(data.cursor)) ||
|
|
104
|
+
data.baseline !== (since === undefined || since === '') ||
|
|
105
|
+
(data.baseline && data.gap) ||
|
|
106
|
+
(data.gap && (data.gap.computer !== id || data.gap.cursor !== data.cursor)) ||
|
|
107
|
+
((data.baseline || data.gap) &&
|
|
108
|
+
(data.events.length || data.more || data.from !== data.cursor)) ||
|
|
109
|
+
(!data.baseline && !data.gap && data.from !== since) ||
|
|
110
|
+
(data.more && !data.events.length) ||
|
|
111
|
+
data.events.length > (limit ?? 50))
|
|
112
|
+
throw new Error('Inconsistent signal page; no checkpoint was established.');
|
|
113
|
+
return said(`${data.gap ? 'GAP: the checkpoint was reset. ' : ''}${data.baseline ? 'Head-only baseline; no historical replay. ' : ''}One ephemeral page; use the returned cursor, including after an empty page. These facts do not establish task success.`, data);
|
|
114
|
+
}));
|
|
115
|
+
};
|
|
116
|
+
//# sourceMappingURL=signals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signals.js","sourceRoot":"","sources":["../../src/tools/signals.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snapshots.d.ts","sourceRoot":"","sources":["../../src/tools/snapshots.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"snapshots.d.ts","sourceRoot":"","sources":["../../src/tools/snapshots.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AA4M5C,eAAO,MAAM,iBAAiB,EAAE,SA40B/B,CAAC"}
|
package/dist/tools/snapshots.js
CHANGED
|
@@ -123,21 +123,17 @@ const snapshotTurn = async (api, sid, unfinished) => {
|
|
|
123
123
|
}
|
|
124
124
|
return { kind: 'row', row };
|
|
125
125
|
};
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
*/
|
|
138
|
-
const retentionLine = (r) => {
|
|
139
|
-
const v = (r ?? {});
|
|
140
|
-
const n = (x) => (typeof x === 'number' && Number.isFinite(x) && x > 0 ? x : 0);
|
|
126
|
+
const retentionOf = (r) => {
|
|
127
|
+
if (!isRow(r))
|
|
128
|
+
return undefined;
|
|
129
|
+
const fields = [r.daily, r.weekly, r.monthly];
|
|
130
|
+
if (!fields.every((value) => typeof value === 'number' && Number.isSafeInteger(value) && value >= 0)) {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
return { daily: r.daily, weekly: r.weekly, monthly: r.monthly };
|
|
134
|
+
};
|
|
135
|
+
const retentionLine = (v) => {
|
|
136
|
+
const n = (x) => (x > 0 ? x : 0);
|
|
141
137
|
const parts = [
|
|
142
138
|
n(v.daily) && `${n(v.daily)} daily`,
|
|
143
139
|
n(v.weekly) && `${n(v.weekly)} weekly`,
|
|
@@ -295,7 +291,7 @@ export const registerSnapshots = (server, session, opts) => {
|
|
|
295
291
|
memory: z
|
|
296
292
|
.boolean()
|
|
297
293
|
.default(false)
|
|
298
|
-
.describe('Include the running session. A memory snapshot is a saved machine, so it only loads back into the shape it came off: resize the computer afterwards and the restore is refused, because the vCPU count and the memory size are part of the state rather than decoration around it. Clone it instead in that case, which restores the disk and boots fresh.'),
|
|
294
|
+
.describe('Include the running session. A memory snapshot is a saved machine, so it only loads back into the shape it came off: resize the computer afterwards and the restore is refused, because the vCPU count and the memory size are part of the state rather than decoration around it. Clone it with memory: false instead in that case, which restores the disk and boots fresh.'),
|
|
299
295
|
wait: z
|
|
300
296
|
.boolean()
|
|
301
297
|
.default(true)
|
|
@@ -419,93 +415,98 @@ export const registerSnapshots = (server, session, opts) => {
|
|
|
419
415
|
// single short request and there is nothing to report until there is a
|
|
420
416
|
// capture to report on.
|
|
421
417
|
const beat = heartbeat(extra, server.server);
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
`
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
if (
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
`
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
418
|
+
try {
|
|
419
|
+
await beat(`Capturing ${sid} of ${id} — the copy has started.`);
|
|
420
|
+
let blocked;
|
|
421
|
+
// `untilDeadline &&` rather than `!untilDeadline?.aborted`: the early
|
|
422
|
+
// return above is what guarantees it is armed here, and the optional
|
|
423
|
+
// form would turn a later edit that broke that guarantee into a loop
|
|
424
|
+
// with no deadline at all rather than into a visible mistake.
|
|
425
|
+
while (untilDeadline && !untilDeadline.aborted) {
|
|
426
|
+
// The caller giving up ends the wait. The signal aborts the request in
|
|
427
|
+
// flight, but nothing about an aborted request stops the next
|
|
428
|
+
// iteration from starting one.
|
|
429
|
+
if (extra.signal?.aborted) {
|
|
430
|
+
return refused(`Cancelled while waiting for the capture of ${id}. THE CAPTURE IS STILL RUNNING — nothing ` +
|
|
431
|
+
`was stopped, because a disk copy already under way cannot be called back. ${byHand}`, started);
|
|
432
|
+
}
|
|
433
|
+
const turn = await snapshotTurn(api, sid, false);
|
|
434
|
+
if (extra.signal?.aborted)
|
|
435
|
+
continue;
|
|
436
|
+
// A body stream can fail without either signal firing, so the
|
|
437
|
+
// deadline's own arrival is what tells a real timeout from an undici
|
|
438
|
+
// idle abort. Same shape as the two loops in computers.ts.
|
|
439
|
+
if (turn.kind === 'cancelled') {
|
|
440
|
+
if (untilDeadline?.aborted)
|
|
441
|
+
break;
|
|
442
|
+
blocked = turn.why;
|
|
443
|
+
await beat(`Capturing ${sid} of ${id} — the platform could not be asked: ${turn.why}`);
|
|
444
|
+
await sleep(POLL_MS, signal);
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
// A poll that failed for a reason worth riding out is weather; one
|
|
448
|
+
// that failed for any other reason is a real failure with a capture
|
|
449
|
+
// still running behind it, and a thrown error's handler has no way to
|
|
450
|
+
// say so. So it is said here rather than rethrown.
|
|
451
|
+
if (turn.kind === 'broken') {
|
|
452
|
+
return refused(`${turn.why}\n\nTHE CAPTURE IS STILL RUNNING — this was the poll failing, not the ` +
|
|
453
|
+
`capture. ${byHand}`, started);
|
|
454
|
+
}
|
|
455
|
+
if (turn.kind === 'blocked') {
|
|
456
|
+
blocked = turn.why;
|
|
457
|
+
await beat(`Capturing ${sid} of ${id} — the platform could not be asked: ${turn.why}`);
|
|
458
|
+
await sleep(turn.after ?? POLL_MS, signal);
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
// Absence is the ONLY signal a failed capture leaves, which is what
|
|
462
|
+
// makes snapshotTurn's guards load-bearing rather than tidy: a body
|
|
463
|
+
// that is not a list, and a listing the platform had to answer short,
|
|
464
|
+
// both come back `blocked` there rather than as this, because read as
|
|
465
|
+
// "the row is not there" they would announce that somebody's backup
|
|
466
|
+
// failed while it was still being taken.
|
|
467
|
+
if (turn.kind === 'absent') {
|
|
468
|
+
return refused(`THE CAPTURE OF ${id} FAILED and nothing was saved. Snapshot ${sid} is no longer listed and ` +
|
|
469
|
+
`nothing took its place, which is what a capture that starts and then fails leaves behind — ` +
|
|
470
|
+
`this is a failure during the copy, not a wait that ran out. The computer itself is ` +
|
|
471
|
+
`untouched and can be snapshotted again.`, started);
|
|
472
|
+
}
|
|
473
|
+
const row = turn.row;
|
|
474
|
+
const state = typeof row.state === 'string' ? row.state : undefined;
|
|
475
|
+
if (state === CAPTURING) {
|
|
476
|
+
blocked = undefined;
|
|
477
|
+
await beat(`Capturing ${sid} of ${id} — still copying.`);
|
|
478
|
+
await sleep(POLL_MS, signal);
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
// A row with no readable `state` is the platform failing to describe
|
|
482
|
+
// it, and it must not be read as "not capturing, therefore landed" —
|
|
483
|
+
// that sentence says a placeholder may be restored, which is the
|
|
484
|
+
// defect this whole tool is here to stop. Only a state that SAYS it is
|
|
485
|
+
// no longer capturing ends the wait; anything else rides out, and the
|
|
486
|
+
// deadline reports that the platform could not be asked.
|
|
487
|
+
if (state === undefined) {
|
|
488
|
+
blocked = `the row for ${sid} carried no state, so nothing said whether the capture had landed`;
|
|
489
|
+
await beat(`Capturing ${sid} of ${id} — its row carried no state to read.`);
|
|
490
|
+
await sleep(POLL_MS, signal);
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
return said(`${took} The capture landed: snapshot ${sid} reads "${state}" and can now be restored, ` +
|
|
494
|
+
`cloned or deleted.`, row);
|
|
495
495
|
}
|
|
496
|
-
|
|
497
|
-
|
|
496
|
+
// A refusal, for the reason move_computer's is: the wait never reached
|
|
497
|
+
// what it was told to wait for. What it must not do is read as a capture
|
|
498
|
+
// that failed — that is a different answer with a different id in it,
|
|
499
|
+
// and the difference between calling this again and going looking for a
|
|
500
|
+
// snapshot that is on its way.
|
|
501
|
+
return refused(blocked
|
|
502
|
+
? `${startedLine} Gave up watching after ${timeout_s}s; the platform could not be asked — the ` +
|
|
503
|
+
`last attempt said: ${blocked}. THE CAPTURE IS STILL RUNNING. ${byHand}`
|
|
504
|
+
: `${startedLine} Still capturing after ${timeout_s}s, which a large disk takes. THE CAPTURE IS ` +
|
|
505
|
+
`STILL RUNNING and nothing was changed by giving up on the wait. ${byHand}`, started);
|
|
506
|
+
}
|
|
507
|
+
finally {
|
|
508
|
+
await beat.stop();
|
|
498
509
|
}
|
|
499
|
-
// A refusal, for the reason move_computer's is: the wait never reached
|
|
500
|
-
// what it was told to wait for. What it must not do is read as a capture
|
|
501
|
-
// that failed — that is a different answer with a different id in it,
|
|
502
|
-
// and the difference between calling this again and going looking for a
|
|
503
|
-
// snapshot that is on its way.
|
|
504
|
-
return refused(blocked
|
|
505
|
-
? `${startedLine} Gave up watching after ${timeout_s}s; the platform could not be asked — the ` +
|
|
506
|
-
`last attempt said: ${blocked}. THE CAPTURE IS STILL RUNNING. ${byHand}`
|
|
507
|
-
: `${startedLine} Still capturing after ${timeout_s}s, which a large disk takes. THE CAPTURE IS ` +
|
|
508
|
-
`STILL RUNNING and nothing was changed by giving up on the wait. ${byHand}`, started);
|
|
509
510
|
}));
|
|
510
511
|
server.registerTool('restore_snapshot', {
|
|
511
512
|
title: 'Restore a snapshot',
|
|
@@ -580,7 +581,11 @@ export const registerSnapshots = (server, session, opts) => {
|
|
|
580
581
|
annotations: { readOnlyHint: true },
|
|
581
582
|
}, (_args, extra) => guarded(async () => {
|
|
582
583
|
const body = await session.api.with(extra.signal).json('GET', P.RETENTION);
|
|
583
|
-
|
|
584
|
+
const retention = retentionOf(body);
|
|
585
|
+
if (!retention) {
|
|
586
|
+
return refused('The retention policy could not be read: GET /retention must answer with nonnegative whole-number `daily`, `weekly`, and `monthly` fields. No retention window is inferred from this response.', body);
|
|
587
|
+
}
|
|
588
|
+
return said(retentionLine(retention), body);
|
|
584
589
|
}));
|
|
585
590
|
// clone_snapshot brings a billable machine into existence as surely as
|
|
586
591
|
// create_computer does, and delete_snapshot destroys bytes as surely as
|
|
@@ -594,27 +599,63 @@ export const registerSnapshots = (server, session, opts) => {
|
|
|
594
599
|
return;
|
|
595
600
|
server.registerTool('clone_snapshot', {
|
|
596
601
|
title: 'Fork a snapshot into a new computer',
|
|
597
|
-
description:
|
|
602
|
+
description: "Build a new computer from a snapshot, leaving the original untouched. This is the fork half of snapshot-and-fork, and the only thing that works on an orphaned snapshot. A MEMORY snapshot is resumed: the copy comes up running the captured session, sharing its source's network identity, so it cannot run on the same host while its source is running. Pass memory: false to build it from the disk alone instead, as a fresh boot with its own identity. A memory snapshot of a computer that held SECRETS is resumed only with inherit_secrets: true, and then the copy HOLDS THE SAME CREDENTIALS as the computer it came from; without it the clone is built from the disk and this tool says so.",
|
|
598
603
|
inputSchema: {
|
|
599
604
|
snapshot_id: z.string(),
|
|
600
605
|
name: z.string().optional().describe('A name for the new computer.'),
|
|
606
|
+
memory: z
|
|
607
|
+
.boolean()
|
|
608
|
+
.optional()
|
|
609
|
+
.describe('Memory snapshots only. false builds the new computer from the disk alone and boots it fresh; omitted or true resumes the captured session.'),
|
|
610
|
+
inherit_secrets: z
|
|
611
|
+
.boolean()
|
|
612
|
+
.optional()
|
|
613
|
+
.describe('Consent to resuming a memory snapshot of a computer that held secrets. The new computer holds the same credentials, bound to the same secrets. Only set this when that is what the user asked for.'),
|
|
601
614
|
select: z
|
|
602
615
|
.boolean()
|
|
603
616
|
.default(true)
|
|
604
617
|
.describe("Make the new computer this session's selected one."),
|
|
605
618
|
},
|
|
606
|
-
}, ({ snapshot_id, name, select }, extra) => guarded(async () => {
|
|
607
|
-
|
|
619
|
+
}, ({ snapshot_id, name, select, memory, inherit_secrets }, extra) => guarded(async () => {
|
|
620
|
+
// Each option only when set, so an ordinary clone sends what it always
|
|
621
|
+
// did; consent is sent only when given, since its absence is the default.
|
|
622
|
+
const body = {};
|
|
623
|
+
if (name !== undefined)
|
|
624
|
+
body.name = name;
|
|
625
|
+
if (memory !== undefined)
|
|
626
|
+
body.memory = memory;
|
|
627
|
+
if (inherit_secrets === true)
|
|
628
|
+
body.inherit_secrets = true;
|
|
629
|
+
const answer = await session.api
|
|
608
630
|
.with(extra.signal)
|
|
609
|
-
.json('POST', P.snapshotAction(snapshot_id, 'clone'), {
|
|
610
|
-
|
|
611
|
-
}));
|
|
631
|
+
.json('POST', P.snapshotAction(snapshot_id, 'clone'), { body });
|
|
632
|
+
const c = unwrapComputer(answer);
|
|
612
633
|
if (!c.id) {
|
|
613
634
|
return refused(`The platform accepted the clone of ${snapshot_id} but sent no computer id back, so the copy cannot be identified. It may exist and be billable — list_computers will say. The selected computer is unchanged.`, withoutCredentials(c));
|
|
614
635
|
}
|
|
615
636
|
if (select && c.id)
|
|
616
637
|
session.bind(c.id, c.resolution);
|
|
617
|
-
|
|
638
|
+
// The platform built it from the disk when the session could not come
|
|
639
|
+
// across (platform OPL-4964). Said first and in words, because a model
|
|
640
|
+
// that asked for a live fork and got a fresh boot would otherwise go on
|
|
641
|
+
// as if the session — its open windows, its running programs — were
|
|
642
|
+
// there.
|
|
643
|
+
//
|
|
644
|
+
// Read off the ANSWER as well as the computer: the platform sends the
|
|
645
|
+
// two fields beside the computer's own, flat, and unwrapComputer keeps
|
|
646
|
+
// only start_error from an envelope — so a future envelope carrying
|
|
647
|
+
// them would otherwise report a live fork for a fresh boot.
|
|
648
|
+
const outer = (answer && typeof answer === 'object' ? answer : {});
|
|
649
|
+
const raw = { ...c, ...outer };
|
|
650
|
+
if (raw.memory_dropped === true) {
|
|
651
|
+
const why = raw.memory_dropped_reason === 'bindings unrecorded'
|
|
652
|
+
? "it was taken before copies could be given its computer's secrets"
|
|
653
|
+
: raw.memory_dropped_reason === 'secrets'
|
|
654
|
+
? 'its computer held secrets and inherit_secrets was not set'
|
|
655
|
+
: 'the platform did not resume it';
|
|
656
|
+
return said(`Built ${describe(c)} from ${snapshot_id}'s DISK, booting fresh — its saved session was NOT resumed, because ${why}. No secrets are bound to it${select && c.id ? '. It is selected' : ''}.`, withoutCredentials(c));
|
|
657
|
+
}
|
|
658
|
+
return said(`${memory === false ? 'Built' : 'Forked'} ${snapshot_id} into ${describe(c)}${memory === false ? ' from its disk' : ''}${select && c.id ? ', and selected it' : ''}.`, withoutCredentials(c));
|
|
618
659
|
}));
|
|
619
660
|
server.registerTool('delete_snapshot', {
|
|
620
661
|
title: 'Delete a snapshot',
|
|
@@ -725,67 +766,72 @@ export const registerSnapshots = (server, session, opts) => {
|
|
|
725
766
|
`wait to find out. ${byHand}`, accepted);
|
|
726
767
|
}
|
|
727
768
|
const beat = heartbeat(extra, server.server);
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
`
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
if (
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
769
|
+
try {
|
|
770
|
+
await beat(`Deleting ${snapshot_id} — the platform has accepted it.`);
|
|
771
|
+
let blocked;
|
|
772
|
+
let seen;
|
|
773
|
+
while (untilDeadline && !untilDeadline.aborted) {
|
|
774
|
+
if (extra.signal?.aborted) {
|
|
775
|
+
return refused(`Cancelled while waiting for ${snapshot_id} to be deleted. THE DELETION IS STILL RUNNING — ` +
|
|
776
|
+
`nothing was called back, and objects it has already removed are already gone. ${byHand}`, accepted);
|
|
777
|
+
}
|
|
778
|
+
// `unfinished`, because the state a stalled deletion sits in is the
|
|
779
|
+
// one a bare listing hides. Asking without it would read a snapshot
|
|
780
|
+
// stuck half-deleted as a snapshot successfully deleted, which is the
|
|
781
|
+
// one wrong answer this tool must not give.
|
|
782
|
+
const turn = await snapshotTurn(api, snapshot_id, true);
|
|
783
|
+
if (extra.signal?.aborted)
|
|
784
|
+
continue;
|
|
785
|
+
if (turn.kind === 'cancelled') {
|
|
786
|
+
if (untilDeadline.aborted)
|
|
787
|
+
break;
|
|
788
|
+
blocked = turn.why;
|
|
789
|
+
await beat(`Deleting ${snapshot_id} — the platform could not be asked: ${turn.why}`);
|
|
790
|
+
await sleep(POLL_MS, signal);
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
if (turn.kind === 'broken') {
|
|
794
|
+
return refused(`${turn.why}\n\nTHE DELETION IS STILL RUNNING — this was the poll failing, not the ` +
|
|
795
|
+
`deletion. ${byHand}`, accepted);
|
|
796
|
+
}
|
|
797
|
+
if (turn.kind === 'blocked') {
|
|
798
|
+
blocked = turn.why;
|
|
799
|
+
await beat(`Deleting ${snapshot_id} — the platform could not be asked: ${turn.why}`);
|
|
800
|
+
await sleep(turn.after ?? POLL_MS, signal);
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
// The row is gone from a listing that was read whole and that ASKED
|
|
804
|
+
// for the unfinished ones. Both halves are what make this sentence
|
|
805
|
+
// true rather than merely likely.
|
|
806
|
+
if (turn.kind === 'absent')
|
|
807
|
+
return said(`Deleted snapshot ${snapshot_id}.`, accepted);
|
|
808
|
+
blocked = undefined;
|
|
809
|
+
seen = typeof turn.row.state === 'string' ? turn.row.state : undefined;
|
|
810
|
+
await beat(`Deleting ${snapshot_id} — still listed${seen ? `, reading "${seen}"` : ''}.`);
|
|
748
811
|
await sleep(POLL_MS, signal);
|
|
749
|
-
continue;
|
|
750
|
-
}
|
|
751
|
-
if (turn.kind === 'broken') {
|
|
752
|
-
return refused(`${turn.why}\n\nTHE DELETION IS STILL RUNNING — this was the poll failing, not the ` +
|
|
753
|
-
`deletion. ${byHand}`, accepted);
|
|
754
812
|
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
813
|
+
// Still listed. Three different things that can mean, and they are not
|
|
814
|
+
// one sentence: the platform could not be asked, the deletion got part
|
|
815
|
+
// way and stalled, or it never got started on — which is the shape the
|
|
816
|
+
// one conflict that arrives AFTER the 202 leaves behind, a dependent
|
|
817
|
+
// that is itself being deleted and so cannot be detached.
|
|
818
|
+
return refused(blocked
|
|
819
|
+
? `Gave up watching after ${timeout_s}s; the platform could not be asked whether ${snapshot_id} ` +
|
|
820
|
+
`is gone — the last attempt said: ${blocked}. THE DELETION IS STILL RUNNING. ${byHand}`
|
|
821
|
+
: seen === DELETING
|
|
822
|
+
? `${snapshot_id} is still listed after ${timeout_s}s and reads "${DELETING}": the deletion ` +
|
|
823
|
+
`started and has not finished. Nothing was undone by giving up on the wait, and the ` +
|
|
824
|
+
`platform retries a stalled deletion itself every fifteen minutes — so this usually needs ` +
|
|
825
|
+
`watching rather than repeating. ${byHand}`
|
|
826
|
+
: `${snapshot_id} is still listed after ${timeout_s}s${seen ? `, reading "${seen}"` : ''} — ` +
|
|
827
|
+
`the deletion has not reached the point of marking it "${DELETING}". That is what a big ` +
|
|
828
|
+
`chain looks like early on, and it is also what the one conflict that arrives after the ` +
|
|
829
|
+
`deletion is accepted looks like: a dependent snapshot that is ITSELF being deleted cannot ` +
|
|
830
|
+
`be detached, so this one waits for that one. Nothing was destroyed either way. ${byHand}`, accepted);
|
|
831
|
+
}
|
|
832
|
+
finally {
|
|
833
|
+
await beat.stop();
|
|
770
834
|
}
|
|
771
|
-
// Still listed. Three different things that can mean, and they are not
|
|
772
|
-
// one sentence: the platform could not be asked, the deletion got part
|
|
773
|
-
// way and stalled, or it never got started on — which is the shape the
|
|
774
|
-
// one conflict that arrives AFTER the 202 leaves behind, a dependent
|
|
775
|
-
// that is itself being deleted and so cannot be detached.
|
|
776
|
-
return refused(blocked
|
|
777
|
-
? `Gave up watching after ${timeout_s}s; the platform could not be asked whether ${snapshot_id} ` +
|
|
778
|
-
`is gone — the last attempt said: ${blocked}. THE DELETION IS STILL RUNNING. ${byHand}`
|
|
779
|
-
: seen === DELETING
|
|
780
|
-
? `${snapshot_id} is still listed after ${timeout_s}s and reads "${DELETING}": the deletion ` +
|
|
781
|
-
`started and has not finished. Nothing was undone by giving up on the wait, and the ` +
|
|
782
|
-
`platform retries a stalled deletion itself every fifteen minutes — so this usually needs ` +
|
|
783
|
-
`watching rather than repeating. ${byHand}`
|
|
784
|
-
: `${snapshot_id} is still listed after ${timeout_s}s${seen ? `, reading "${seen}"` : ''} — ` +
|
|
785
|
-
`the deletion has not reached the point of marking it "${DELETING}". That is what a big ` +
|
|
786
|
-
`chain looks like early on, and it is also what the one conflict that arrives after the ` +
|
|
787
|
-
`deletion is accepted looks like: a dependent snapshot that is ITSELF being deleted cannot ` +
|
|
788
|
-
`be detached, so this one waits for that one. Nothing was destroyed either way. ${byHand}`, accepted);
|
|
789
835
|
}));
|
|
790
836
|
};
|
|
791
837
|
//# sourceMappingURL=snapshots.js.map
|