nylorun 0.15.1-beta → 0.17.0-beta
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/CHANGELOG.md +134 -0
- package/README.md +136 -74
- package/dist/cli.js +31 -12
- package/dist/client/agent-manifests.d.ts +4 -0
- package/dist/client/agent-manifests.js +100 -0
- package/dist/client/agents.d.ts +1 -3
- package/dist/client/agents.js +459 -117
- package/dist/client/apply.d.ts +7 -0
- package/dist/client/apply.js +99 -0
- package/dist/client/artifacts.js +3 -3
- package/dist/client/doctor.d.ts +1 -1
- package/dist/client/doctor.js +2 -2
- package/dist/client/io.d.ts +2 -0
- package/dist/client/io.js +11 -0
- package/dist/client/mcp.d.ts +5 -3
- package/dist/client/mcp.js +28 -25
- package/dist/client/migrate.d.ts +4 -0
- package/dist/client/migrate.js +140 -0
- package/dist/client/model/pi-ai.d.ts +1 -1
- package/dist/client/model/pi-ai.js +1 -1
- package/dist/client/models.d.ts +5 -2
- package/dist/client/models.js +17 -8
- package/dist/client/runtime.js +3 -3
- package/dist/client/sessions.js +29 -18
- package/dist/client/settings.d.ts +5 -5
- package/dist/client/settings.js +38 -18
- package/dist/client/tenant.d.ts +1 -1
- package/dist/client/tenant.js +10 -7
- package/dist/project/seed.js +2 -1
- package/dist/sandbox/commands.js +10 -8
- package/dist/sandbox/kubectl.js +2 -1
- package/dist/sandbox/manifests.js +3 -2
- package/dist/sandbox/probe.js +9 -7
- package/dist/stack/commands.js +12 -11
- package/dist/stack/compose-file.d.ts +3 -0
- package/dist/stack/compose-file.js +32 -36
- package/dist/stack/operator-keys.js +7 -6
- package/dist/stack/ports.js +2 -1
- package/dist/stack/studio-login.js +2 -1
- package/dist/tuning.d.ts +90 -0
- package/dist/tuning.js +143 -0
- package/package.json +9 -9
- package/dist/client/environment-manifests.d.ts +0 -10
- package/dist/client/environment-manifests.js +0 -173
- package/dist/client/environments.d.ts +0 -4
- package/dist/client/environments.js +0 -295
package/dist/client/agents.js
CHANGED
|
@@ -1,148 +1,490 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
*/
|
|
6
|
-
import { readdir, readFile } from "node:fs/promises";
|
|
7
|
-
import { join } from "node:path";
|
|
8
|
-
import { definitionFileHash } from "@nylorun/agents";
|
|
9
|
-
import { CliError } from "../errors.js";
|
|
10
|
-
import { parseFlags, positional, printJson, printTable, readJson, runtimeCall, runtimeClient, runtimeFlagsUsage, usageError, } from "./runtime.js";
|
|
11
|
-
export const agentsUsage = `nylorun agents <list|get|save>
|
|
1
|
+
import { parseAgentManifestRef } from "@nylorun/core/contracts";
|
|
2
|
+
import { readSecret, readText } from "./io.js";
|
|
3
|
+
import { integerFlag, labelsOf, parseFlags, positional, printCursor, printJson, printTable, readJson, runtimeCall, runtimeClient, runtimeFlagsUsage, usageError, } from "./runtime.js";
|
|
4
|
+
export const agentsUsage = `nylorun agents <command>
|
|
12
5
|
|
|
13
|
-
The Tenant's
|
|
6
|
+
The Tenant's Agents (each one version of an agent manifest made live, with one environment and
|
|
7
|
+
one vault; sessions run on them), through the Runtime API with an application key (or
|
|
8
|
+
NYLORUN_TOKEN).
|
|
14
9
|
|
|
15
|
-
list [--
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
10
|
+
list [--state <state>] [--manifest <name>] [--label <key=value>]... [--outdated]
|
|
11
|
+
[--limit <n>] [--cursor <c>] [--json]
|
|
12
|
+
id, manifest version (tracks or pinned, and the version
|
|
13
|
+
it moves to), kind, state, sessions, owner and labels;
|
|
14
|
+
--outdated (or --behind): those moving to another version
|
|
15
|
+
get <id> the Agent, as JSON
|
|
16
|
+
put <id> --manifest <name[@n]> [--owner <user>] [--label <key=value>]... [--dry-run]
|
|
17
|
+
create it from a saved manifest (name tracks its latest
|
|
18
|
+
version, name@n pins version n), or find it, change its
|
|
19
|
+
owner and labels and move it to that version; --dry-run
|
|
20
|
+
prints what the move would do
|
|
21
|
+
pin <id> <version> move it to that version of its manifest, and keep it there
|
|
22
|
+
track <id> move it to its manifest's latest version, and with each save
|
|
23
|
+
restart <id> [--retry] apply the version it waits to start on now (a running pod
|
|
24
|
+
stops; the next turn starts it); --retry: the version that
|
|
25
|
+
failed to start, again
|
|
26
|
+
fork <id> <new-id> [--manifest <name[@n]>] [--disk <latest|n|empty>]
|
|
27
|
+
[--secrets <copy|none>] [--label <key=value>]... [--dry-run]
|
|
28
|
+
a new Agent from a manifest version (default its
|
|
29
|
+
manifest's latest), with this one's disk and the secrets
|
|
30
|
+
it declares; --dry-run prints the plan
|
|
31
|
+
stop <id> | reset <id> stop its sandbox; reset its disk
|
|
32
|
+
rm <id> delete it: its sandbox, files and vault (refused while a
|
|
33
|
+
turn runs on it)
|
|
34
|
+
snapshot <id> keep a version of its disk
|
|
35
|
+
versions <id> its disk versions
|
|
36
|
+
restore <id> <version|latest> put a version of its disk back
|
|
37
|
+
delete-version <id> <version>
|
|
38
|
+
events <id> [--from <n>] [--types <type,…>] [--since <time>] [--until <time>]
|
|
39
|
+
its events (lifecycle, and agent.egress for every
|
|
40
|
+
connection, naming the turns running), as JSON
|
|
41
|
+
secrets list <id> its secrets' names, types and bindings (never a value)
|
|
42
|
+
secrets get <id> <name>
|
|
43
|
+
secrets put <id> <name> the value of a slot its manifest declares, piped in
|
|
44
|
+
on standard input
|
|
45
|
+
secrets put <id> <name> <secret.json|->
|
|
46
|
+
create or replace a secret: {"type": "bearer", ...}, read
|
|
47
|
+
from a file or standard input
|
|
48
|
+
secrets rm <id> <name>
|
|
49
|
+
triggers list <id> its triggers: status, next and last run, as JSON
|
|
50
|
+
triggers pause|resume <id> <trigger>
|
|
51
|
+
triggers run <id> <trigger> [--idempotency-key <k>]
|
|
52
|
+
run it once now, as if it fired
|
|
53
|
+
triggers new-secret <id> <trigger> a webhook trigger's new signing secret (whsec_…), shown
|
|
54
|
+
once with its URL; the one before stops working
|
|
55
|
+
runs <id> [--trigger <id>] [--limit <n>] [--cursor <c>]
|
|
56
|
+
what each fire came to (started, queued, skipped,
|
|
57
|
+
failed), newest first, as JSON
|
|
58
|
+
memories ls <id> [--prefix <folder>] [--limit <n>] [--cursor <c>]
|
|
59
|
+
its memories (track T5) by path, as JSON
|
|
60
|
+
memories cat <id> <path> one memory's content
|
|
61
|
+
memories put <id> <path> <file|-> write a memory whole (seed it), from a file or stdin
|
|
62
|
+
memories rm <id> <path> delete a memory, or every memory in a folder
|
|
63
|
+
memories versions <id> [--path <path>] [--limit <n>] [--cursor <c>]
|
|
64
|
+
every change to its memories, newest first, as JSON
|
|
22
65
|
|
|
23
66
|
${runtimeFlagsUsage}`;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
walk(manifest, "manifest");
|
|
44
|
-
return named;
|
|
45
|
-
}
|
|
46
|
-
/** The files under `dirs`, by their hash. */
|
|
47
|
-
async function filesByHash(dirs) {
|
|
48
|
-
const found = new Map();
|
|
49
|
-
const visit = async (dir) => {
|
|
50
|
-
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
51
|
-
const path = join(dir, entry.name);
|
|
52
|
-
if (entry.isDirectory())
|
|
53
|
-
await visit(path);
|
|
54
|
-
else if (entry.isFile())
|
|
55
|
-
found.set(await definitionFileHash(new Uint8Array(await readFile(path))), path);
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
for (const dir of dirs) {
|
|
59
|
-
try {
|
|
60
|
-
await visit(dir);
|
|
61
|
-
}
|
|
62
|
-
catch (error) {
|
|
63
|
-
throw new CliError(`Cannot read --files ${dir}: ${error.message}`, 1);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return found;
|
|
67
|
+
/** What `agents save` (a definition, before protocol 16) says now. */
|
|
68
|
+
const SAVE_REMOVED = "nylorun agents save was removed in protocol 16: agents run from saved agent manifests. Save one with nylorun apply <dir> (agent.manifest.json; nylorun migrate v8 <agent.json> writes one from a v7 manifest), then make an Agent from it with nylorun agents put <id> --manifest <name>.";
|
|
69
|
+
/**
|
|
70
|
+
* The manifest an Agent runs, as the list prints it: `name@version`, the version it moves to
|
|
71
|
+
* (track T3), and whether it tracks.
|
|
72
|
+
*/
|
|
73
|
+
function manifestOf(agent) {
|
|
74
|
+
const name = parseAgentManifestRef(agent.manifest)?.name ?? agent.manifest;
|
|
75
|
+
const target = agent.targetVersion ? ` → ${agent.targetVersion}` : "";
|
|
76
|
+
return `${name}@${agent.resolvedVersion}${target} ${agent.tracking === "tracking" ? "tracks" : "pinned"}`;
|
|
77
|
+
}
|
|
78
|
+
/** What a move left pending, for a person: the version, and what it waits for. */
|
|
79
|
+
function pendingOf(agent) {
|
|
80
|
+
if (!agent.targetVersion)
|
|
81
|
+
return "";
|
|
82
|
+
return agent.waitingFor === "secrets"
|
|
83
|
+
? `; version ${agent.targetVersion} waits for its required secrets (nylorun agents secrets put ${agent.id} <name>)`
|
|
84
|
+
: `; version ${agent.targetVersion} applies at its pod's next start (nylorun agents restart ${agent.id} applies it now)`;
|
|
67
85
|
}
|
|
68
86
|
async function list(deps, args) {
|
|
69
|
-
const usage = "nylorun agents list [--json]";
|
|
70
|
-
const flags = parseFlags(args, {
|
|
87
|
+
const usage = "nylorun agents list [--state <state>] [--manifest <name>] [--label <key=value>]... [--outdated] [--limit <n>] [--cursor <c>] [--json]";
|
|
88
|
+
const flags = parseFlags(args, {
|
|
89
|
+
booleans: ["--json", "--outdated", "--behind"],
|
|
90
|
+
values: ["--limit", "--cursor", "--state", "--manifest"],
|
|
91
|
+
lists: ["--label"],
|
|
92
|
+
}, usage);
|
|
71
93
|
positional(flags, 0, usage);
|
|
72
|
-
const
|
|
73
|
-
const
|
|
94
|
+
const labels = labelsOf(flags);
|
|
95
|
+
const state = flags.values.get("--state");
|
|
96
|
+
const manifest = flags.values.get("--manifest");
|
|
97
|
+
// Moving to another version (track T3); --behind is its former name.
|
|
98
|
+
const outdated = flags.booleans.has("--outdated") || flags.booleans.has("--behind");
|
|
99
|
+
const filter = {
|
|
100
|
+
...(labels ? { labels } : {}),
|
|
101
|
+
...(state ? { state } : {}),
|
|
102
|
+
...(manifest ? { manifest } : {}),
|
|
103
|
+
...(outdated ? { outdated: true } : {}),
|
|
104
|
+
};
|
|
105
|
+
const limit = integerFlag(flags, "--limit");
|
|
106
|
+
const cursor = flags.values.get("--cursor");
|
|
107
|
+
const { client, connection } = await runtimeClient(deps, flags);
|
|
108
|
+
const paged = limit !== undefined || cursor !== undefined;
|
|
109
|
+
const page = paged
|
|
110
|
+
? await runtimeCall(() => client.agents.page({
|
|
111
|
+
...filter,
|
|
112
|
+
...(limit === undefined ? {} : { limit }),
|
|
113
|
+
...(cursor ? { cursor } : {}),
|
|
114
|
+
}))
|
|
115
|
+
: { agents: await runtimeCall(() => client.agents.list(filter)), nextCursor: null };
|
|
116
|
+
const agents = page.agents;
|
|
74
117
|
if (flags.booleans.has("--json")) {
|
|
75
|
-
printJson(deps, agents);
|
|
118
|
+
printJson(deps, paged ? page : agents);
|
|
76
119
|
return 0;
|
|
77
120
|
}
|
|
78
|
-
if (agents.length === 0)
|
|
79
|
-
deps.out(
|
|
121
|
+
if (agents.length === 0)
|
|
122
|
+
deps.out(`No Agents on ${connection.label}.`);
|
|
123
|
+
else
|
|
124
|
+
printTable(deps, [
|
|
125
|
+
["ID", "MANIFEST", "KIND", "STATE", "SESSIONS", "OWNER", "LABELS"],
|
|
126
|
+
...agents.map((agent) => [
|
|
127
|
+
agent.id,
|
|
128
|
+
manifestOf(agent),
|
|
129
|
+
agent.kind,
|
|
130
|
+
agent.state,
|
|
131
|
+
String(agent.sessions.length),
|
|
132
|
+
agent.owner ?? "-",
|
|
133
|
+
Object.entries(agent.labels)
|
|
134
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
135
|
+
.join(",") || "-",
|
|
136
|
+
]),
|
|
137
|
+
]);
|
|
138
|
+
printCursor(deps, page.nextCursor);
|
|
139
|
+
return 0;
|
|
140
|
+
}
|
|
141
|
+
/** A command that takes one Agent id and prints the Runtime's answer. */
|
|
142
|
+
function byId(name, call) {
|
|
143
|
+
return async (deps, args) => {
|
|
144
|
+
const usage = `nylorun agents ${name} <id>`;
|
|
145
|
+
const flags = parseFlags(args, {}, usage);
|
|
146
|
+
const [id] = positional(flags, 1, usage);
|
|
147
|
+
const { client } = await runtimeClient(deps, flags);
|
|
148
|
+
printJson(deps, await runtimeCall(() => call(client, id)));
|
|
149
|
+
return 0;
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
async function put(deps, args) {
|
|
153
|
+
const usage = "nylorun agents put <id> --manifest <name[@n]> [--owner <user>] [--label <key=value>]... [--dry-run]";
|
|
154
|
+
for (const removed of ["--kind", "--image"])
|
|
155
|
+
if (args.includes(removed))
|
|
156
|
+
throw usageError(`${removed} was removed in protocol 16: an Agent is made from a saved manifest only. Put what it runs in the manifest's runtime section (nylorun apply), then --manifest <name>.`);
|
|
157
|
+
const flags = parseFlags(args, { booleans: ["--dry-run"], values: ["--manifest", "--owner"], lists: ["--label"] }, usage);
|
|
158
|
+
const [id] = positional(flags, 1, usage);
|
|
159
|
+
const manifest = flags.values.get("--manifest");
|
|
160
|
+
if (manifest === undefined)
|
|
161
|
+
throw usageError(`Usage: ${usage}`);
|
|
162
|
+
if (parseAgentManifestRef(manifest) === undefined)
|
|
163
|
+
throw usageError(`--manifest is an agent manifest's name, or name@version such as triage@3, not ${manifest}.`);
|
|
164
|
+
const owner = flags.values.get("--owner");
|
|
165
|
+
const labels = labelsOf(flags);
|
|
166
|
+
const { client, connection } = await runtimeClient(deps, flags);
|
|
167
|
+
const input = { manifest, ...(owner ? { owner } : {}), ...(labels ? { labels } : {}) };
|
|
168
|
+
if (flags.booleans.has("--dry-run")) {
|
|
169
|
+
printJson(deps, await runtimeCall(() => client.agents.plan(id, input)));
|
|
80
170
|
return 0;
|
|
81
171
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const manifest = "manifest" in agent ? agent.manifest : undefined;
|
|
86
|
-
const description = ("description" in agent ? agent.description : undefined) ?? manifest?.description;
|
|
87
|
-
return [agent.agentId, manifest?.kind ?? "agent", description ?? "-"];
|
|
88
|
-
}),
|
|
89
|
-
]);
|
|
172
|
+
const agent = await runtimeCall(() => client.agents.put(id, input));
|
|
173
|
+
deps.err(`Agent ${agent.id} runs ${manifestOf(agent)} on ${connection.label}${pendingOf(agent)}.`);
|
|
174
|
+
printJson(deps, agent);
|
|
90
175
|
return 0;
|
|
91
176
|
}
|
|
92
|
-
|
|
93
|
-
|
|
177
|
+
/** `pin <id> <version>` and `track <id>`: moves an Agent within its manifest (track T3). */
|
|
178
|
+
async function move(deps, args, pin) {
|
|
179
|
+
const usage = pin ? "nylorun agents pin <id> <version>" : "nylorun agents track <id>";
|
|
94
180
|
const flags = parseFlags(args, {}, usage);
|
|
181
|
+
const [id, version] = positional(flags, pin ? 2 : 1, usage);
|
|
182
|
+
if (pin && !/^[1-9]\d*$/.test(version))
|
|
183
|
+
throw usageError("The version is a positive number.");
|
|
184
|
+
const { client, connection } = await runtimeClient(deps, flags);
|
|
185
|
+
const current = await runtimeCall(() => client.agents.get(id));
|
|
186
|
+
const name = parseAgentManifestRef(current.manifest)?.name ?? current.manifest;
|
|
187
|
+
const agent = await runtimeCall(() => client.agents.put(id, { manifest: pin ? `${name}@${version}` : name }));
|
|
188
|
+
deps.err(`Agent ${agent.id} runs ${manifestOf(agent)} on ${connection.label}${pendingOf(agent)}.`);
|
|
189
|
+
printJson(deps, agent);
|
|
190
|
+
return 0;
|
|
191
|
+
}
|
|
192
|
+
async function restart(deps, args) {
|
|
193
|
+
const usage = "nylorun agents restart <id> [--retry]";
|
|
194
|
+
const flags = parseFlags(args, { booleans: ["--retry"] }, usage);
|
|
95
195
|
const [id] = positional(flags, 1, usage);
|
|
96
196
|
const { client, connection } = await runtimeClient(deps, flags);
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
if (!agent) {
|
|
100
|
-
deps.err(`No agent ${id} on ${connection.label}.`);
|
|
101
|
-
return 1;
|
|
102
|
-
}
|
|
197
|
+
const agent = await runtimeCall(() => client.agents.restart(id, { ...(flags.booleans.has("--retry") ? { retry: true } : {}) }));
|
|
198
|
+
deps.err(`Agent ${agent.id} runs ${manifestOf(agent)} on ${connection.label}${pendingOf(agent)}.`);
|
|
103
199
|
printJson(deps, agent);
|
|
104
200
|
return 0;
|
|
105
201
|
}
|
|
106
|
-
async function
|
|
107
|
-
const usage = "nylorun agents
|
|
108
|
-
const flags = parseFlags(args, { booleans: ["--dry-run"], values: ["--
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
202
|
+
async function fork(deps, args) {
|
|
203
|
+
const usage = "nylorun agents fork <id> <new-id> [--manifest <name[@n]>] [--disk <latest|n|empty>] [--secrets <copy|none>] [--label <key=value>]... [--dry-run]";
|
|
204
|
+
const flags = parseFlags(args, { booleans: ["--dry-run"], values: ["--manifest", "--disk", "--secrets"], lists: ["--label"] }, usage);
|
|
205
|
+
const [id, next] = positional(flags, 2, usage);
|
|
206
|
+
const disk = flags.values.get("--disk");
|
|
207
|
+
if (disk !== undefined && disk !== "latest" && disk !== "empty" && !/^[1-9]\d*$/.test(disk))
|
|
208
|
+
throw usageError("--disk is latest, empty or a version number.");
|
|
209
|
+
const secrets = flags.values.get("--secrets");
|
|
210
|
+
if (secrets !== undefined && secrets !== "copy" && secrets !== "none")
|
|
211
|
+
throw usageError("--secrets is copy or none.");
|
|
212
|
+
const manifest = flags.values.get("--manifest");
|
|
213
|
+
const labels = labelsOf(flags);
|
|
214
|
+
const options = {
|
|
215
|
+
id: next,
|
|
216
|
+
...(manifest ? { manifest } : {}),
|
|
217
|
+
...(disk === undefined ? {} : { disk: disk === "latest" || disk === "empty" ? disk : Number(disk) }),
|
|
218
|
+
...(secrets ? { secrets: secrets } : {}),
|
|
219
|
+
...(labels ? { labels } : {}),
|
|
220
|
+
};
|
|
119
221
|
const { client, connection } = await runtimeClient(deps, flags);
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
222
|
+
if (flags.booleans.has("--dry-run")) {
|
|
223
|
+
printJson(deps, await runtimeCall(() => client.agents.planFork(id, options)));
|
|
224
|
+
return 0;
|
|
225
|
+
}
|
|
226
|
+
const forked = await runtimeCall(() => client.agents.fork(id, options));
|
|
227
|
+
const { plan } = forked;
|
|
228
|
+
deps.err(`Forked Agent ${id} into ${next} on ${connection.label}: ${plan.manifest.name}@${plan.manifest.version}, ${plan.disk === null ? "an empty disk" : `disk version ${plan.disk}`}, ${plan.secrets.copied.length} secrets copied${plan.setup ? "; its setup runs before the first turn" : ""}.`);
|
|
229
|
+
if (plan.secrets.missing.length > 0)
|
|
230
|
+
deps.err(` set before use: ${plan.secrets.missing.join(", ")} (nylorun agents secrets put ${next} <name>)`);
|
|
231
|
+
printJson(deps, forked);
|
|
232
|
+
return 0;
|
|
233
|
+
}
|
|
234
|
+
async function deleteAgent(deps, args) {
|
|
235
|
+
const usage = "nylorun agents rm <id>";
|
|
236
|
+
const flags = parseFlags(args, {}, usage);
|
|
237
|
+
const [id] = positional(flags, 1, usage);
|
|
238
|
+
const { client, connection } = await runtimeClient(deps, flags);
|
|
239
|
+
const deleted = await runtimeCall(async () => {
|
|
240
|
+
try {
|
|
241
|
+
return await client.agents.delete(id);
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
if (error.status === 404)
|
|
245
|
+
return undefined;
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
if (!deleted || deleted.deleted === false) {
|
|
250
|
+
deps.err(`No Agent ${id} on ${connection.label}.`);
|
|
251
|
+
return 1;
|
|
252
|
+
}
|
|
253
|
+
deps.out(`Deleted Agent ${id}: its sandbox, files and vault.`);
|
|
254
|
+
return 0;
|
|
255
|
+
}
|
|
256
|
+
async function restore(deps, args) {
|
|
257
|
+
const usage = "nylorun agents restore <id> <version|latest>";
|
|
258
|
+
const flags = parseFlags(args, {}, usage);
|
|
259
|
+
const [id, version] = positional(flags, 2, usage);
|
|
260
|
+
if (version !== "latest" && !/^[1-9]\d*$/.test(version))
|
|
261
|
+
throw usageError("The version is a positive number or latest.");
|
|
262
|
+
const { client } = await runtimeClient(deps, flags);
|
|
263
|
+
printJson(deps, await runtimeCall(() => client.agents.restore(id, version === "latest" ? "latest" : Number(version))));
|
|
264
|
+
return 0;
|
|
265
|
+
}
|
|
266
|
+
async function deleteVersion(deps, args) {
|
|
267
|
+
const usage = "nylorun agents delete-version <id> <version>";
|
|
268
|
+
const flags = parseFlags(args, {}, usage);
|
|
269
|
+
const [id, version] = positional(flags, 2, usage);
|
|
270
|
+
if (!/^[1-9]\d*$/.test(version))
|
|
271
|
+
throw usageError("The version is a positive number.");
|
|
272
|
+
const { client } = await runtimeClient(deps, flags);
|
|
273
|
+
printJson(deps, await runtimeCall(() => client.agents.deleteVersion(id, Number(version))));
|
|
274
|
+
return 0;
|
|
275
|
+
}
|
|
276
|
+
async function events(deps, args) {
|
|
277
|
+
const usage = "nylorun agents events <id> [--from <n>] [--types <type,…>] [--since <time>] [--until <time>]";
|
|
278
|
+
const flags = parseFlags(args, { values: ["--from", "--types", "--since", "--until"] }, usage);
|
|
279
|
+
const [id] = positional(flags, 1, usage);
|
|
280
|
+
const from = integerFlag(flags, "--from");
|
|
281
|
+
const types = flags.values.get("--types")?.split(",").map((type) => type.trim()).filter(Boolean);
|
|
282
|
+
const since = flags.values.get("--since");
|
|
283
|
+
const until = flags.values.get("--until");
|
|
284
|
+
const { client } = await runtimeClient(deps, flags);
|
|
285
|
+
printJson(deps, await runtimeCall(() => client.agents.events(id, {
|
|
286
|
+
...(from === undefined ? {} : { from }),
|
|
287
|
+
...(types === undefined || types.length === 0 ? {} : { types }),
|
|
288
|
+
...(since === undefined ? {} : { since }),
|
|
289
|
+
...(until === undefined ? {} : { until }),
|
|
290
|
+
})));
|
|
291
|
+
return 0;
|
|
292
|
+
}
|
|
293
|
+
async function secrets(deps, args) {
|
|
294
|
+
const [verb, ...rest] = args;
|
|
295
|
+
if (verb === "list" || verb === "ls") {
|
|
296
|
+
const usage = "nylorun agents secrets list <id>";
|
|
297
|
+
const flags = parseFlags(rest, {}, usage);
|
|
298
|
+
const [id] = positional(flags, 1, usage);
|
|
299
|
+
const { client } = await runtimeClient(deps, flags);
|
|
300
|
+
printJson(deps, await runtimeCall(() => client.agents.secrets.list(id)));
|
|
301
|
+
return 0;
|
|
302
|
+
}
|
|
303
|
+
if (verb === "get") {
|
|
304
|
+
const usage = "nylorun agents secrets get <id> <name>";
|
|
305
|
+
const flags = parseFlags(rest, {}, usage);
|
|
306
|
+
const [id, name] = positional(flags, 2, usage);
|
|
307
|
+
const { client } = await runtimeClient(deps, flags);
|
|
308
|
+
printJson(deps, await runtimeCall(() => client.agents.secrets.get(id, name)));
|
|
309
|
+
return 0;
|
|
310
|
+
}
|
|
311
|
+
if (verb === "put") {
|
|
312
|
+
const usage = "nylorun agents secrets put <id> <name> [<secret.json|->]";
|
|
313
|
+
const flags = parseFlags(rest, {}, usage);
|
|
314
|
+
if (flags.rest.length < 2 || flags.rest.length > 3)
|
|
315
|
+
throw usageError(`Usage: ${usage}`);
|
|
316
|
+
const [id, name, path] = flags.rest;
|
|
317
|
+
if (path === undefined) {
|
|
318
|
+
// A slot its manifest declares (D64): the value only, piped in; the binding is the slot's.
|
|
319
|
+
const value = await readSecret(deps, `value of ${name}`, `printf %s "$TOKEN" | nylorun agents secrets put ${id} ${name}`);
|
|
320
|
+
const { client } = await runtimeClient(deps, flags);
|
|
321
|
+
printJson(deps, await runtimeCall(() => client.agents.secrets.put(id, name, { value })));
|
|
322
|
+
return 0;
|
|
323
|
+
}
|
|
324
|
+
const secret = await readJson(path, "The secret");
|
|
325
|
+
if (typeof secret.type !== "string")
|
|
326
|
+
throw usageError('The secret needs a "type".');
|
|
327
|
+
const { client } = await runtimeClient(deps, flags);
|
|
328
|
+
printJson(deps, await runtimeCall(() => client.agents.secrets.put(id, name, secret)));
|
|
329
|
+
return 0;
|
|
330
|
+
}
|
|
331
|
+
if (verb === "rm" || verb === "delete") {
|
|
332
|
+
const usage = "nylorun agents secrets rm <id> <name>";
|
|
333
|
+
const flags = parseFlags(rest, {}, usage);
|
|
334
|
+
const [id, name] = positional(flags, 2, usage);
|
|
335
|
+
const { client } = await runtimeClient(deps, flags);
|
|
336
|
+
await runtimeCall(() => client.agents.secrets.delete(id, name));
|
|
337
|
+
deps.out(`Deleted secret ${name} of Agent ${id}.`);
|
|
338
|
+
return 0;
|
|
339
|
+
}
|
|
340
|
+
throw usageError(`Usage: nylorun agents secrets <list|get|put|rm> …\n\n${agentsUsage}`);
|
|
341
|
+
}
|
|
342
|
+
/** `nylorun agents triggers <list|pause|resume|run> …` (track T4). */
|
|
343
|
+
async function triggers(deps, args) {
|
|
344
|
+
const [verb, ...rest] = args;
|
|
345
|
+
if (verb === "list" || verb === "ls") {
|
|
346
|
+
const usage = "nylorun agents triggers list <id>";
|
|
347
|
+
const flags = parseFlags(rest, {}, usage);
|
|
348
|
+
const [id] = positional(flags, 1, usage);
|
|
349
|
+
const { client } = await runtimeClient(deps, flags);
|
|
350
|
+
printJson(deps, await runtimeCall(() => client.agents.triggers(id)));
|
|
351
|
+
return 0;
|
|
352
|
+
}
|
|
353
|
+
if (verb === "pause" || verb === "resume") {
|
|
354
|
+
const usage = `nylorun agents triggers ${verb} <id> <trigger>`;
|
|
355
|
+
const flags = parseFlags(rest, {}, usage);
|
|
356
|
+
const [id, trigger] = positional(flags, 2, usage);
|
|
357
|
+
const { client } = await runtimeClient(deps, flags);
|
|
358
|
+
printJson(deps, await runtimeCall(() => verb === "pause" ? client.agents.pauseTrigger(id, trigger) : client.agents.resumeTrigger(id, trigger)));
|
|
359
|
+
return 0;
|
|
360
|
+
}
|
|
361
|
+
if (verb === "run") {
|
|
362
|
+
const usage = "nylorun agents triggers run <id> <trigger> [--idempotency-key <k>]";
|
|
363
|
+
const flags = parseFlags(rest, { values: ["--idempotency-key"] }, usage);
|
|
364
|
+
const [id, trigger] = positional(flags, 2, usage);
|
|
365
|
+
const key = flags.values.get("--idempotency-key");
|
|
366
|
+
const { client } = await runtimeClient(deps, flags);
|
|
367
|
+
printJson(deps, await runtimeCall(() => client.agents.runTrigger(id, trigger, key === undefined ? {} : { idempotencyKey: key })));
|
|
368
|
+
return 0;
|
|
369
|
+
}
|
|
370
|
+
if (verb === "new-secret") {
|
|
371
|
+
const usage = "nylorun agents triggers new-secret <id> <trigger>";
|
|
372
|
+
const flags = parseFlags(rest, {}, usage);
|
|
373
|
+
const [id, trigger] = positional(flags, 2, usage);
|
|
374
|
+
const { client } = await runtimeClient(deps, flags);
|
|
375
|
+
printJson(deps, await runtimeCall(() => client.agents.rotateWebhookSecret(id, trigger)));
|
|
376
|
+
return 0;
|
|
377
|
+
}
|
|
378
|
+
throw usageError(`Usage: nylorun agents triggers <list|pause|resume|run|new-secret> …\n\n${agentsUsage}`);
|
|
379
|
+
}
|
|
380
|
+
/** `nylorun agents memories <ls|cat|put|rm|versions> …` (track T5). */
|
|
381
|
+
async function memories(deps, args) {
|
|
382
|
+
const [verb, ...rest] = args;
|
|
383
|
+
const pageOf = (flags) => {
|
|
384
|
+
const limit = flags.values.get("--limit");
|
|
385
|
+
if (limit !== undefined && !/^[1-9]\d*$/.test(limit))
|
|
386
|
+
throw usageError("--limit must be a positive number.");
|
|
387
|
+
const cursor = flags.values.get("--cursor");
|
|
388
|
+
return { ...(limit === undefined ? {} : { limit: Number(limit) }), ...(cursor === undefined ? {} : { cursor }) };
|
|
389
|
+
};
|
|
390
|
+
if (verb === "ls" || verb === "list") {
|
|
391
|
+
const usage = "nylorun agents memories ls <id> [--prefix <folder>] [--limit <n>] [--cursor <c>]";
|
|
392
|
+
const flags = parseFlags(rest, { values: ["--prefix", "--limit", "--cursor"] }, usage);
|
|
393
|
+
const [id] = positional(flags, 1, usage);
|
|
394
|
+
const prefix = flags.values.get("--prefix");
|
|
395
|
+
const { client } = await runtimeClient(deps, flags);
|
|
396
|
+
printJson(deps, await runtimeCall(() => client.agents.memories.list(id, { ...(prefix === undefined ? {} : { pathPrefix: prefix }), ...pageOf(flags) })));
|
|
397
|
+
return 0;
|
|
398
|
+
}
|
|
399
|
+
if (verb === "cat") {
|
|
400
|
+
const usage = "nylorun agents memories cat <id> <path>";
|
|
401
|
+
const flags = parseFlags(rest, {}, usage);
|
|
402
|
+
const [id, path] = positional(flags, 2, usage);
|
|
403
|
+
const { client } = await runtimeClient(deps, flags);
|
|
404
|
+
deps.out((await runtimeCall(() => client.agents.memories.get(id, path))).content);
|
|
405
|
+
return 0;
|
|
406
|
+
}
|
|
407
|
+
if (verb === "put") {
|
|
408
|
+
const usage = "nylorun agents memories put <id> <path> <file|->";
|
|
409
|
+
const flags = parseFlags(rest, {}, usage);
|
|
410
|
+
const [id, path, source] = positional(flags, 3, usage);
|
|
411
|
+
const content = await readText(deps, source);
|
|
412
|
+
const { client } = await runtimeClient(deps, flags);
|
|
413
|
+
printJson(deps, await runtimeCall(() => client.agents.memories.put(id, path, content)));
|
|
414
|
+
return 0;
|
|
415
|
+
}
|
|
416
|
+
if (verb === "rm" || verb === "delete") {
|
|
417
|
+
const usage = "nylorun agents memories rm <id> <path>";
|
|
418
|
+
const flags = parseFlags(rest, {}, usage);
|
|
419
|
+
const [id, path] = positional(flags, 2, usage);
|
|
420
|
+
const { client } = await runtimeClient(deps, flags);
|
|
421
|
+
const deleted = await runtimeCall(() => client.agents.memories.delete(id, path));
|
|
422
|
+
deps.out(`Deleted ${deleted.deleted} ${deleted.deleted === 1 ? "memory" : "memories"} at ${deleted.path} of Agent ${id}.`);
|
|
423
|
+
return 0;
|
|
424
|
+
}
|
|
425
|
+
if (verb === "versions") {
|
|
426
|
+
const usage = "nylorun agents memories versions <id> [--path <path>] [--limit <n>] [--cursor <c>]";
|
|
427
|
+
const flags = parseFlags(rest, { values: ["--path", "--limit", "--cursor"] }, usage);
|
|
428
|
+
const [id] = positional(flags, 1, usage);
|
|
429
|
+
const path = flags.values.get("--path");
|
|
430
|
+
const { client } = await runtimeClient(deps, flags);
|
|
431
|
+
printJson(deps, await runtimeCall(() => client.agents.memories.versions(id, { ...(path === undefined ? {} : { path }), ...pageOf(flags) })));
|
|
432
|
+
return 0;
|
|
433
|
+
}
|
|
434
|
+
throw usageError(`Usage: nylorun agents memories <ls|cat|put|rm|versions> …\n\n${agentsUsage}`);
|
|
435
|
+
}
|
|
436
|
+
/** `nylorun agents runs <id>` (track T4). */
|
|
437
|
+
async function runs(deps, args) {
|
|
438
|
+
const usage = "nylorun agents runs <id> [--trigger <id>] [--limit <n>] [--cursor <c>]";
|
|
439
|
+
const flags = parseFlags(args, { values: ["--trigger", "--limit", "--cursor"] }, usage);
|
|
440
|
+
const [id] = positional(flags, 1, usage);
|
|
441
|
+
const limit = flags.values.get("--limit");
|
|
442
|
+
if (limit !== undefined && !/^[1-9]\d*$/.test(limit))
|
|
443
|
+
throw usageError("--limit must be a positive number.");
|
|
444
|
+
const trigger = flags.values.get("--trigger");
|
|
445
|
+
const cursor = flags.values.get("--cursor");
|
|
446
|
+
const { client } = await runtimeClient(deps, flags);
|
|
447
|
+
printJson(deps, await runtimeCall(() => client.agents.runs(id, {
|
|
448
|
+
...(trigger === undefined ? {} : { triggerId: trigger }),
|
|
449
|
+
...(limit === undefined ? {} : { limit: Number(limit) }),
|
|
450
|
+
...(cursor === undefined ? {} : { cursor }),
|
|
451
|
+
})));
|
|
132
452
|
return 0;
|
|
133
453
|
}
|
|
134
|
-
|
|
454
|
+
const COMMANDS = {
|
|
455
|
+
list,
|
|
456
|
+
ls: list,
|
|
457
|
+
get: byId("get", (client, id) => client.agents.get(id)),
|
|
458
|
+
put,
|
|
459
|
+
pin: (deps, args) => move(deps, args, true),
|
|
460
|
+
track: (deps, args) => move(deps, args, false),
|
|
461
|
+
restart,
|
|
462
|
+
stop: byId("stop", (client, id) => client.agents.stop(id)),
|
|
463
|
+
reset: byId("reset", (client, id) => client.agents.reset(id)),
|
|
464
|
+
rm: deleteAgent,
|
|
465
|
+
delete: deleteAgent,
|
|
466
|
+
snapshot: byId("snapshot", (client, id) => client.agents.snapshot(id)),
|
|
467
|
+
versions: byId("versions", (client, id) => client.agents.versions(id)),
|
|
468
|
+
restore,
|
|
469
|
+
"delete-version": deleteVersion,
|
|
470
|
+
events,
|
|
471
|
+
secrets,
|
|
472
|
+
fork,
|
|
473
|
+
triggers,
|
|
474
|
+
runs,
|
|
475
|
+
memories,
|
|
476
|
+
};
|
|
477
|
+
/** `nylorun agents <command> ...` */
|
|
135
478
|
export async function agentsCommand(deps, args) {
|
|
136
479
|
const [sub, ...rest] = args;
|
|
137
480
|
if (sub === "--help" || sub === "-h") {
|
|
138
481
|
deps.out(agentsUsage);
|
|
139
482
|
return 0;
|
|
140
483
|
}
|
|
141
|
-
if (sub === "list" || sub === "ls")
|
|
142
|
-
return list(deps, rest);
|
|
143
|
-
if (sub === "get")
|
|
144
|
-
return get(deps, rest);
|
|
145
484
|
if (sub === "save")
|
|
146
|
-
|
|
147
|
-
|
|
485
|
+
throw usageError(SAVE_REMOVED);
|
|
486
|
+
const command = sub !== undefined && Object.hasOwn(COMMANDS, sub) ? COMMANDS[sub] : undefined;
|
|
487
|
+
if (!command)
|
|
488
|
+
throw usageError(sub ? `Unknown agents command ${sub}.\n${agentsUsage}` : agentsUsage);
|
|
489
|
+
return command(deps, rest);
|
|
148
490
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type AgentManifestPlan } from "@nylorun/agents";
|
|
2
|
+
import type { StackDeps } from "../stack/commands.js";
|
|
3
|
+
export declare const applyUsage: string;
|
|
4
|
+
/** A plan as lines: the move, then each change, warning and error. */
|
|
5
|
+
export declare function planLines(plan: AgentManifestPlan): string[];
|
|
6
|
+
/** `nylorun apply [dir…] [--dry-run] [--if-match <n>]` */
|
|
7
|
+
export declare function applyCommand(deps: StackDeps, args: readonly string[]): Promise<number>;
|