mandala-computer-mcp 0.4.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 +450 -29
- package/dist/api.d.ts +12 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +160 -15
- 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 +29 -9
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +71 -34
- package/dist/errors.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 +81 -15
- 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/paths.d.ts +40 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +50 -0
- package/dist/paths.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 +60 -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 +21 -9
- 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 +18 -7
- 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/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 +50 -15
- package/dist/tools/guest.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 +44 -8
- 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/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"results.js","sourceRoot":"","sources":["../../src/tools/results.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { Registrar } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* A computer's secret bindings: which of the account's secrets it receives, at
|
|
5
|
+
* which revision, and where — as an environment variable (`env`) or as a file
|
|
6
|
+
* under FILES_DIR (`file`).
|
|
7
|
+
*
|
|
8
|
+
* No value ever crosses these routes, in either direction — only secret ids,
|
|
9
|
+
* revision ids, env names and file names — so nothing here can show one, and
|
|
10
|
+
* nothing a model sends can set one. Secrets themselves are created and rotated
|
|
11
|
+
* elsewhere.
|
|
12
|
+
*
|
|
13
|
+
* A change of bindings takes effect at the computer's NEXT START OR RESTART. A
|
|
14
|
+
* running computer keeps what it booted with until then, and a suspended one
|
|
15
|
+
* resumes with its old values; the sentences below say so every time, because a
|
|
16
|
+
* model that reads "saved" as "delivered" goes on to use a credential the guest
|
|
17
|
+
* does not have yet. The one thing that reaches a running computer sooner is a
|
|
18
|
+
* replaced VALUE of a secret bound as a file: that file is rewritten in place.
|
|
19
|
+
*/
|
|
20
|
+
/** Where a file binding is published in the guest. */
|
|
21
|
+
export declare const FILES_DIR = "/run/mandala-secrets/user/files";
|
|
22
|
+
/** At most this many secrets per computer, and this many of them as files. */
|
|
23
|
+
export declare const SECRET_BINDINGS_MAX = 32;
|
|
24
|
+
export declare const SECRET_FILES_MAX = 8;
|
|
25
|
+
/**
|
|
26
|
+
* A binding list as the platform takes it, checked here for what it would
|
|
27
|
+
* refuse anyway — so the mistake is named against the entry that made it
|
|
28
|
+
* rather than coming back as a 400. `revision` adds the optional `revision_id`
|
|
29
|
+
* a rebind may send; a create has nothing to keep and takes none.
|
|
30
|
+
*/
|
|
31
|
+
export declare function secretBindingsSchema(revision: boolean): z.ZodEffects<z.ZodArray<z.ZodObject<{
|
|
32
|
+
revision_id?: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>> | undefined;
|
|
33
|
+
secret_id: z.ZodEffects<z.ZodString, string, string>;
|
|
34
|
+
env: z.ZodOptional<z.ZodString>;
|
|
35
|
+
file: z.ZodOptional<z.ZodString>;
|
|
36
|
+
}, "strict", z.ZodTypeAny, {
|
|
37
|
+
secret_id: string;
|
|
38
|
+
file?: string | undefined;
|
|
39
|
+
env?: string | undefined;
|
|
40
|
+
revision_id?: unknown;
|
|
41
|
+
}, {
|
|
42
|
+
secret_id: string;
|
|
43
|
+
file?: string | undefined;
|
|
44
|
+
env?: string | undefined;
|
|
45
|
+
revision_id?: unknown;
|
|
46
|
+
}>, "many">, {
|
|
47
|
+
secret_id: string;
|
|
48
|
+
file?: string | undefined;
|
|
49
|
+
env?: string | undefined;
|
|
50
|
+
revision_id?: unknown;
|
|
51
|
+
}[], {
|
|
52
|
+
secret_id: string;
|
|
53
|
+
file?: string | undefined;
|
|
54
|
+
env?: string | undefined;
|
|
55
|
+
revision_id?: unknown;
|
|
56
|
+
}[]>;
|
|
57
|
+
export declare const registerSecrets: Registrar;
|
|
58
|
+
//# sourceMappingURL=secrets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../../src/tools/secrets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;;;;;;;;;;;;;;GAgBG;AAEH,sDAAsD;AACtD,eAAO,MAAM,SAAS,oCAAoC,CAAC;AAE3D,8EAA8E;AAC9E,eAAO,MAAM,mBAAmB,KAAK,CAAC;AACtC,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAiBlC;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;KA0DrD;AAyED,eAAO,MAAM,eAAe,EAAE,SA+E7B,CAAC"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { guarded, refused, said } from '../format.js';
|
|
3
|
+
import * as P from '../paths.js';
|
|
4
|
+
/**
|
|
5
|
+
* A computer's secret bindings: which of the account's secrets it receives, at
|
|
6
|
+
* which revision, and where — as an environment variable (`env`) or as a file
|
|
7
|
+
* under FILES_DIR (`file`).
|
|
8
|
+
*
|
|
9
|
+
* No value ever crosses these routes, in either direction — only secret ids,
|
|
10
|
+
* revision ids, env names and file names — so nothing here can show one, and
|
|
11
|
+
* nothing a model sends can set one. Secrets themselves are created and rotated
|
|
12
|
+
* elsewhere.
|
|
13
|
+
*
|
|
14
|
+
* A change of bindings takes effect at the computer's NEXT START OR RESTART. A
|
|
15
|
+
* running computer keeps what it booted with until then, and a suspended one
|
|
16
|
+
* resumes with its old values; the sentences below say so every time, because a
|
|
17
|
+
* model that reads "saved" as "delivered" goes on to use a credential the guest
|
|
18
|
+
* does not have yet. The one thing that reaches a running computer sooner is a
|
|
19
|
+
* replaced VALUE of a secret bound as a file: that file is rewritten in place.
|
|
20
|
+
*/
|
|
21
|
+
/** Where a file binding is published in the guest. */
|
|
22
|
+
export const FILES_DIR = '/run/mandala-secrets/user/files';
|
|
23
|
+
/** At most this many secrets per computer, and this many of them as files. */
|
|
24
|
+
export const SECRET_BINDINGS_MAX = 32;
|
|
25
|
+
export const SECRET_FILES_MAX = 8;
|
|
26
|
+
const SECRET_ENV = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/;
|
|
27
|
+
const SECRET_FILE = /^[a-z][a-z0-9_-]{0,47}$/;
|
|
28
|
+
const ENV_RULE = 'letters, digits and underscores, not starting with a digit, at most 64 characters';
|
|
29
|
+
const FILE_RULE = 'lowercase letters, digits, - and _, starting with a letter, at most 48 characters';
|
|
30
|
+
/** An id as the platform spells one: not empty, and nothing around it. */
|
|
31
|
+
const isId = (v) => typeof v === 'string' && v !== '' && v.trim() === v;
|
|
32
|
+
const idString = (what) => z.string().refine(isId, `${what} must not be empty or have spaces around it`);
|
|
33
|
+
/**
|
|
34
|
+
* A binding list as the platform takes it, checked here for what it would
|
|
35
|
+
* refuse anyway — so the mistake is named against the entry that made it
|
|
36
|
+
* rather than coming back as a 400. `revision` adds the optional `revision_id`
|
|
37
|
+
* a rebind may send; a create has nothing to keep and takes none.
|
|
38
|
+
*/
|
|
39
|
+
export function secretBindingsSchema(revision) {
|
|
40
|
+
// Strict: a key this tool does not take is refused, not dropped. A create
|
|
41
|
+
// that carried a `revision_id` would otherwise validate, bind the latest, and
|
|
42
|
+
// leave the caller believing it had chosen a revision.
|
|
43
|
+
const entry = z.strictObject({
|
|
44
|
+
secret_id: idString('secret_id').describe('The secret to bind — `csec-` and sixteen hex.'),
|
|
45
|
+
env: z
|
|
46
|
+
.string()
|
|
47
|
+
.regex(SECRET_ENV, `env must be ${ENV_RULE}`)
|
|
48
|
+
.optional()
|
|
49
|
+
.describe(`Deliver it as this environment variable in the desktop session: ${ENV_RULE}. Exactly one of env and file.`),
|
|
50
|
+
file: z
|
|
51
|
+
.string()
|
|
52
|
+
.regex(SECRET_FILE, `file must be ${FILE_RULE}`)
|
|
53
|
+
.optional()
|
|
54
|
+
.describe(`Deliver it as the file ${FILES_DIR}/<file>, for a program that reads a path (a kubeconfig, a key): ${FILE_RULE}. May hold any bytes. Exactly one of env and file.`),
|
|
55
|
+
...(revision
|
|
56
|
+
? {
|
|
57
|
+
revision_id: idString('revision_id')
|
|
58
|
+
.optional()
|
|
59
|
+
.describe('Omit to record the latest revision. Name the one the computer holds now to keep it: a revision is what the computer last received, not a pin, and every start or restart delivers the latest value regardless.'),
|
|
60
|
+
}
|
|
61
|
+
: {}),
|
|
62
|
+
});
|
|
63
|
+
return z
|
|
64
|
+
.array(entry)
|
|
65
|
+
.max(SECRET_BINDINGS_MAX, `at most ${SECRET_BINDINGS_MAX} secrets per computer`)
|
|
66
|
+
.superRefine((list, ctx) => {
|
|
67
|
+
const ids = new Set();
|
|
68
|
+
const envs = new Set();
|
|
69
|
+
const files = new Set();
|
|
70
|
+
list.forEach((b, i) => {
|
|
71
|
+
const at = (message) => ctx.addIssue({ code: 'custom', path: [i], message });
|
|
72
|
+
if ((b.env === undefined) === (b.file === undefined)) {
|
|
73
|
+
at(`secrets[${i}] must name exactly one of env and file`);
|
|
74
|
+
}
|
|
75
|
+
if (ids.has(b.secret_id))
|
|
76
|
+
at(`secrets[${i}]: ${b.secret_id} is bound twice`);
|
|
77
|
+
ids.add(b.secret_id);
|
|
78
|
+
// Two namespaces: a variable and a file may share a spelling.
|
|
79
|
+
if (b.env !== undefined) {
|
|
80
|
+
if (envs.has(b.env))
|
|
81
|
+
at(`secrets[${i}].env ${b.env} is bound twice`);
|
|
82
|
+
envs.add(b.env);
|
|
83
|
+
}
|
|
84
|
+
if (b.file !== undefined) {
|
|
85
|
+
if (files.has(b.file))
|
|
86
|
+
at(`secrets[${i}].file ${b.file} is bound twice`);
|
|
87
|
+
files.add(b.file);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
if (files.size > SECRET_FILES_MAX) {
|
|
91
|
+
ctx.addIssue({ code: 'custom', message: `at most ${SECRET_FILES_MAX} secrets as files` });
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
const idArg = {
|
|
96
|
+
computer_id: z
|
|
97
|
+
.string()
|
|
98
|
+
.optional()
|
|
99
|
+
.describe('Which computer. Defaults to the one selected with use_computer.'),
|
|
100
|
+
};
|
|
101
|
+
const isRecord = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
102
|
+
const shapeOf = (v) => v === undefined ? 'no body at all' : v === null ? 'null' : Array.isArray(v) ? 'a list' : typeof v;
|
|
103
|
+
function bindingsOf(body) {
|
|
104
|
+
if (!isRecord(body))
|
|
105
|
+
return undefined;
|
|
106
|
+
const { secrets, version } = body;
|
|
107
|
+
if (!Array.isArray(secrets))
|
|
108
|
+
return undefined;
|
|
109
|
+
if (typeof version !== 'number' || !Number.isSafeInteger(version) || version < 0)
|
|
110
|
+
return undefined;
|
|
111
|
+
const out = [];
|
|
112
|
+
for (const entry of secrets) {
|
|
113
|
+
if (!isRecord(entry))
|
|
114
|
+
return undefined;
|
|
115
|
+
const { secret_id, revision_id, env, file } = entry;
|
|
116
|
+
if (!isId(secret_id) || !isId(revision_id))
|
|
117
|
+
return undefined;
|
|
118
|
+
// Exactly one, spelled as the platform would accept it. Null reads as
|
|
119
|
+
// absent; anything else present — an empty string included — counts, so a
|
|
120
|
+
// row naming both or neither is refused rather than guessed at.
|
|
121
|
+
const hasEnv = env !== undefined && env !== null;
|
|
122
|
+
const hasFile = file !== undefined && file !== null;
|
|
123
|
+
if (hasEnv === hasFile)
|
|
124
|
+
return undefined;
|
|
125
|
+
if (hasEnv) {
|
|
126
|
+
if (typeof env !== 'string' || !SECRET_ENV.test(env))
|
|
127
|
+
return undefined;
|
|
128
|
+
out.push({ secret_id, revision_id, env });
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
if (typeof file !== 'string' || !SECRET_FILE.test(file))
|
|
132
|
+
return undefined;
|
|
133
|
+
out.push({ secret_id, revision_id, file });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return { secrets: out, version };
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* One binding in a line: where the guest finds it — a variable name, or a file's
|
|
140
|
+
* full path — then what it is bound to.
|
|
141
|
+
*/
|
|
142
|
+
const bindingLine = (b) => `${b.env ?? `${FILES_DIR}/${b.file}`} = ${b.secret_id} @ ${b.revision_id}`;
|
|
143
|
+
const LEGEND = 'variable or file = secret @ revision, values never shown';
|
|
144
|
+
/** The bindings, one per line. Callers say "none" themselves for an empty list. */
|
|
145
|
+
function listing(b) {
|
|
146
|
+
return `\n${b.secrets.map(bindingLine).join('\n')}`;
|
|
147
|
+
}
|
|
148
|
+
const TIMING = 'The new bindings reach the guest at its NEXT START OR RESTART: a running computer keeps what it booted with until then, and a suspended one resumes with its old values.';
|
|
149
|
+
/** What reaches a running computer without a restart: a replaced value, as a file. */
|
|
150
|
+
const LIVE_FILES = "Every start and restart delivers each secret's latest value. Separately, when a secret's value is replaced, a running computer bound to it as a file has that file rewritten within seconds; a variable waits for the next start or restart.";
|
|
151
|
+
export const registerSecrets = (server, session) => {
|
|
152
|
+
server.registerTool('get_computer_secrets', {
|
|
153
|
+
title: "Read a computer's secret bindings",
|
|
154
|
+
description: `Which of the account's secrets a computer is bound to: for each, the secret id, the revision last delivered, and where the guest finds it — an environment variable name, or a file under ${FILES_DIR}. Values are NEVER shown — no value crosses this route. Also answers \`version\`, the number to send to set_computer_secrets so it changes only this list (0 for a computer never bound). Any role may read it. The list is what the computer receives at its next start or restart, which a running computer may not have yet. ${LIVE_FILES}`,
|
|
155
|
+
inputSchema: idArg,
|
|
156
|
+
annotations: { readOnlyHint: true },
|
|
157
|
+
}, ({ computer_id }, extra) => guarded(async () => {
|
|
158
|
+
const id = session.resolve(computer_id);
|
|
159
|
+
const body = await session.api
|
|
160
|
+
.with(extra.signal)
|
|
161
|
+
.json('GET', P.computerAction(id, 'secrets'));
|
|
162
|
+
const bindings = bindingsOf(body);
|
|
163
|
+
if (!bindings) {
|
|
164
|
+
return refused(`GET /computers/${id}/secrets answered with ${shapeOf(body)}, not a list of secret bindings. This does not mean the computer has none.`, body);
|
|
165
|
+
}
|
|
166
|
+
if (!bindings.secrets.length) {
|
|
167
|
+
return said(`${id} is bound to no secrets (version ${bindings.version}). set_computer_secrets binds some.`, body);
|
|
168
|
+
}
|
|
169
|
+
const n = bindings.secrets.length;
|
|
170
|
+
return said(`${id} is bound to ${n} secret${n === 1 ? '' : 's'} (version ${bindings.version}; ${LEGEND}):${listing(bindings)}`, body);
|
|
171
|
+
}));
|
|
172
|
+
server.registerTool('set_computer_secrets', {
|
|
173
|
+
title: "Replace a computer's secret bindings",
|
|
174
|
+
description: `Replace the WHOLE list of secrets a computer is bound to — not a merge: any binding left out is removed, and \`secrets: []\` removes every binding. Each entry names a secret id and exactly one of \`env\` (the environment variable it is delivered under) or \`file\` (delivered as ${FILES_DIR}/<file>) — at most ${SECRET_BINDINGS_MAX} secrets, ${SECRET_FILES_MAX} of them as files, none twice and no variable or file name twice — and is recorded at that secret's latest revision unless it names \`revision_id\`, which must be the revision the computer holds now (any other revision is refused). A binding's revision is what the computer last received, never a pin: naming it only says this change keeps the value the running computer already has, so the change does not stop it being suspended. Only ids, revisions and names are sent and answered — values are NEVER shown or set here. ${TIMING} ${LIVE_FILES} Binding a computer that has NO secrets yet requires it to be STOPPED: that first binding is refused with 409 while the computer is running or suspended, because a computer started without secrets cannot receive them until it starts again, and a restart does not do that. Start it afterwards to deliver them. Send \`version\` from get_computer_secrets to change only the list you read: if it changed since, this is refused with 409 and nothing changes. Also 409 while a delivery to the computer is in progress. After a change that drops a secret or moves one to another revision, a running computer cannot be suspended or memory-snapshotted until it restarts. Owners and members only; Linux computers only, and binding one for the first time needs a template whose image can receive secrets.`,
|
|
175
|
+
inputSchema: {
|
|
176
|
+
...idArg,
|
|
177
|
+
secrets: secretBindingsSchema(true).describe('The complete new list. Every binding not in it is removed; [] removes them all.'),
|
|
178
|
+
version: z
|
|
179
|
+
.number()
|
|
180
|
+
.int()
|
|
181
|
+
.nonnegative()
|
|
182
|
+
.optional()
|
|
183
|
+
.describe('The `version` a read answered. Sent, the change is refused (409) if the list changed since; omitted, it replaces whatever is there.'),
|
|
184
|
+
},
|
|
185
|
+
annotations: { destructiveHint: true },
|
|
186
|
+
}, ({ computer_id, secrets, version }, extra) => guarded(async () => {
|
|
187
|
+
const id = session.resolve(computer_id);
|
|
188
|
+
const body = await session.api
|
|
189
|
+
.with(extra.signal)
|
|
190
|
+
.json('PUT', P.computerAction(id, 'secrets'), {
|
|
191
|
+
body: { secrets, ...(version === undefined ? {} : { version }) },
|
|
192
|
+
});
|
|
193
|
+
const bindings = bindingsOf(body);
|
|
194
|
+
if (!bindings) {
|
|
195
|
+
return refused(`PUT /computers/${id}/secrets answered with ${shapeOf(body)}, not a list of secret bindings. THE CHANGE MAY HAVE BEEN MADE — get_computer_secrets says what the computer is bound to now.`, body);
|
|
196
|
+
}
|
|
197
|
+
const n = bindings.secrets.length;
|
|
198
|
+
const what = n
|
|
199
|
+
? `${id} is now bound to ${n} secret${n === 1 ? '' : 's'} (version ${bindings.version}; ${LEGEND}):${listing(bindings)}`
|
|
200
|
+
: `${id} is now bound to no secrets (version ${bindings.version}).`;
|
|
201
|
+
return said(`${what}\n${TIMING} ${LIVE_FILES}`, body);
|
|
202
|
+
}));
|
|
203
|
+
};
|
|
204
|
+
//# sourceMappingURL=secrets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/tools/secrets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signals.d.ts","sourceRoot":"","sources":["../../src/tools/signals.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AA0E5C,eAAO,MAAM,eAAe,EAAE,SAqD7B,CAAC"}
|
|
@@ -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;AA4M5C,eAAO,MAAM,iBAAiB,EAAE,
|
|
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
|
@@ -291,7 +291,7 @@ export const registerSnapshots = (server, session, opts) => {
|
|
|
291
291
|
memory: z
|
|
292
292
|
.boolean()
|
|
293
293
|
.default(false)
|
|
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 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.'),
|
|
295
295
|
wait: z
|
|
296
296
|
.boolean()
|
|
297
297
|
.default(true)
|
|
@@ -599,27 +599,63 @@ export const registerSnapshots = (server, session, opts) => {
|
|
|
599
599
|
return;
|
|
600
600
|
server.registerTool('clone_snapshot', {
|
|
601
601
|
title: 'Fork a snapshot into a new computer',
|
|
602
|
-
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.",
|
|
603
603
|
inputSchema: {
|
|
604
604
|
snapshot_id: z.string(),
|
|
605
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.'),
|
|
606
614
|
select: z
|
|
607
615
|
.boolean()
|
|
608
616
|
.default(true)
|
|
609
617
|
.describe("Make the new computer this session's selected one."),
|
|
610
618
|
},
|
|
611
|
-
}, ({ snapshot_id, name, select }, extra) => guarded(async () => {
|
|
612
|
-
|
|
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
|
|
613
630
|
.with(extra.signal)
|
|
614
|
-
.json('POST', P.snapshotAction(snapshot_id, 'clone'), {
|
|
615
|
-
|
|
616
|
-
}));
|
|
631
|
+
.json('POST', P.snapshotAction(snapshot_id, 'clone'), { body });
|
|
632
|
+
const c = unwrapComputer(answer);
|
|
617
633
|
if (!c.id) {
|
|
618
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));
|
|
619
635
|
}
|
|
620
636
|
if (select && c.id)
|
|
621
637
|
session.bind(c.id, c.resolution);
|
|
622
|
-
|
|
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));
|
|
623
659
|
}));
|
|
624
660
|
server.registerTool('delete_snapshot', {
|
|
625
661
|
title: 'Delete a snapshot',
|