@zenera/cli 1.1.36 → 1.1.39
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 +158 -900
- package/dist/archive.js +1 -1
- package/dist/batch.js +2 -3
- package/dist/cache.js +1 -1
- package/dist/candidates.js +1 -1
- package/dist/catalog.d.ts +1 -1
- package/dist/catalog.js +1 -1
- package/dist/commands/cache.js +1 -1
- package/dist/commands/check.js +1 -3
- package/dist/commands/export.js +1 -3
- package/dist/commands/import.js +1 -2
- package/dist/commands/index.js +6 -7
- package/dist/commands/init.js +1 -4
- package/dist/commands/inspect.d.ts +1 -1
- package/dist/commands/inspect.js +1 -4
- package/dist/commands/key.js +1 -2
- package/dist/commands/list.js +4 -5
- package/dist/commands/memory.js +1 -2
- package/dist/commands/meta.js +2 -4
- package/dist/commands/models.d.ts +1 -1
- package/dist/commands/models.js +1 -2
- package/dist/commands/run.js +2 -2
- package/dist/commands/sandbox.js +6 -7
- package/dist/engine.d.ts +3 -90
- package/dist/engine.js +16 -302
- package/dist/external.js +1 -1
- package/dist/history.js +1 -1
- package/dist/lib.d.ts +1 -2
- package/dist/lib.js +1 -2
- package/dist/liveness.d.ts +1 -1
- package/dist/liveness.js +1 -1
- package/dist/meta/host.d.ts +1 -2
- package/dist/meta/host.js +1 -2
- package/dist/modelref.d.ts +1 -1
- package/dist/podman.d.ts +1 -1
- package/dist/podman.js +4 -4
- package/dist/resolve.d.ts +2 -2
- package/dist/resolve.js +2 -3
- package/dist/resources.d.ts +2 -2
- package/dist/resources.js +6 -6
- package/dist/sandbox.d.ts +2 -60
- package/dist/sandbox.js +1 -200
- package/dist/term.d.ts +1 -22
- package/dist/term.js +3 -26
- package/dist/tui/app.js +1 -1
- package/dist/tui/models.d.ts +1 -1
- package/dist/validate.d.ts +1 -2
- package/dist/validate.js +1 -3
- package/package.json +7 -6
- package/templates/editor/.github/skills/zen-cli/SKILL.md +3 -3
- package/templates/editor/.github/skills/zen-cli/references/frame.md +2 -2
- package/templates/editor/.github/skills/zen-cli/references/{faker.md → gym.md} +45 -40
- package/templates/editor/.github/skills/zen-cli/references/keys.md +2 -2
- package/templates/editor/.github/skills/zen-cli/references/models.md +1 -1
- package/templates/editor/.github/skills/zen-cli/references/rag.md +1 -1
- package/templates/editor/.vscode/agents.schema.json +1 -1
- package/dist/audit.d.ts +0 -58
- package/dist/audit.js +0 -148
- package/dist/env.d.ts +0 -20
- package/dist/env.js +0 -65
- package/dist/home.d.ts +0 -35
- package/dist/home.js +0 -112
- package/dist/ids.d.ts +0 -12
- package/dist/ids.js +0 -44
- package/dist/image.d.ts +0 -16
- package/dist/image.js +0 -85
- package/dist/keys.d.ts +0 -269
- package/dist/keys.js +0 -624
- package/dist/projects.d.ts +0 -91
- package/dist/projects.js +0 -303
- package/dist/session.d.ts +0 -112
- package/dist/session.js +0 -188
package/dist/engine.js
CHANGED
|
@@ -1,306 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { resolve } from 'node:path';
|
|
5
|
-
import { pathToFileURL } from 'node:url';
|
|
6
|
-
import { auditModels, describeIssue } from "./audit.js";
|
|
7
|
-
import { loadProjectEnv } from "./env.js";
|
|
8
|
-
import { readJson, writeJson } from "./home.js";
|
|
9
|
-
import { KeyStore, assertUsable } from "./keys.js";
|
|
10
|
-
import { projectMounts } from "./projects.js";
|
|
11
|
-
import { buildSandbox, preflight, teardown, usesSandbox } from "./sandbox.js";
|
|
12
|
-
import { acquire, createRun, readSessionMeta, writeRunMeta, writeSessionMeta, } from "./session.js";
|
|
13
|
-
import { CliError, EXIT, invalidError, warn } from "./term.js";
|
|
1
|
+
import { describeIssue, openEngine, runEngine, } from '@zenera/engine';
|
|
2
|
+
import { preflight } from "./sandbox.js";
|
|
3
|
+
import { bold, dim, warn } from "./term.js";
|
|
14
4
|
import { traceMermaid } from "./trace.js";
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
assertUsable(keys);
|
|
28
|
-
// Said before the load, because the load stops at the first model it cannot
|
|
29
|
-
// build: one SDK's words about one model, when the useful answer is which
|
|
30
|
-
// of the project's models are reachable and which are not.
|
|
31
|
-
for (const issue of auditModels(opts.project.dir, keys)) {
|
|
32
|
-
if (!opts.quiet) {
|
|
33
|
-
warn(describeIssue(issue));
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
const meta = await readSessionMeta(opts.session);
|
|
37
|
-
const workspace = resolve(meta.workspace);
|
|
38
|
-
const payloads = new FilePayloadStore({ dir: opts.session.blobs, id: 'file' });
|
|
39
|
-
// The config is read before the project is loaded because the sandbox is
|
|
40
|
-
// configured by it and the tools have to exist before selectors can be
|
|
41
|
-
// resolved against them. They are always registered, whether or not anyone
|
|
42
|
-
// selects them: a project that names `sandbox:*` against an empty tool list
|
|
43
|
-
// fails to load with "unknown tool", which would be a lie.
|
|
44
|
-
let sandbox;
|
|
45
|
-
let project;
|
|
46
|
-
let files;
|
|
47
|
-
let memory;
|
|
48
|
-
try {
|
|
49
|
-
const { root, config } = readProjectConfig(opts.project.dir);
|
|
50
|
-
// Naming a directory declares the graph, but it cannot bind anyone to
|
|
51
|
-
// it — so say when the run is pointed at a memory no agent will touch.
|
|
52
|
-
if (opts.memoryDir && !opts.quiet && !config.agents.some((a) => a.memory)) {
|
|
53
|
-
warn('--memory names a directory no agent uses — none has `memory: true`');
|
|
54
|
-
}
|
|
55
|
-
memory = memoryDir(root, config, opts.memoryDir);
|
|
56
|
-
// Assets and the skill catalog are mounted for both, under one name.
|
|
57
|
-
const mounts = projectMounts(root, config, opts.memoryDir);
|
|
58
|
-
sandbox = buildSandbox({
|
|
59
|
-
config,
|
|
60
|
-
root,
|
|
61
|
-
session: opts.session,
|
|
62
|
-
workspace,
|
|
63
|
-
readOnly: opts.readOnly,
|
|
64
|
-
image: opts.image,
|
|
65
|
-
keys: opts.keys,
|
|
66
|
-
env: env?.names,
|
|
67
|
-
mounts,
|
|
68
|
-
...(opts.disposable ? { persist: false } : {}),
|
|
69
|
-
});
|
|
70
|
-
const workspaceOptions = {
|
|
71
|
-
root: workspace,
|
|
72
|
-
readOnly: opts.readOnly,
|
|
73
|
-
mount: sandbox.spec.workdir ?? SANDBOX_MOUNT,
|
|
74
|
-
mounts,
|
|
75
|
-
};
|
|
76
|
-
// The same view the file tools have, so a path the agent just wrote
|
|
77
|
-
// resolves the same way when it asks memory to keep a copy.
|
|
78
|
-
files = new Workspace(workspaceOptions);
|
|
79
|
-
project = await loadProject(opts.project.dir, {
|
|
80
|
-
tools: [
|
|
81
|
-
// Both groups are pointed at one directory, so both are told the
|
|
82
|
-
// one name for it: whatever `run_command` prints a path as,
|
|
83
|
-
// `read_file` accepts.
|
|
84
|
-
...fileTools(workspaceOptions),
|
|
85
|
-
...sandboxTools(sandbox.pool),
|
|
86
|
-
// Registered whether or not a key exists: the credential is
|
|
87
|
-
// read when a tool is called, so a project that names `exa:*`
|
|
88
|
-
// loads on a machine that cannot yet search, and says so on the
|
|
89
|
-
// turn that tried.
|
|
90
|
-
...exaTools(),
|
|
91
|
-
],
|
|
92
|
-
skillsAt: SKILLS_MOUNT,
|
|
93
|
-
memoryDir: opts.memoryDir,
|
|
94
|
-
memoryReadOnly: opts.memoryReadOnly,
|
|
95
|
-
payloads,
|
|
96
|
-
resolveFile: (path) => files.within(path),
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
catch (err) {
|
|
100
|
-
throw invalidError(err instanceof Error ? err.message : String(err), `while loading ${opts.project.dir}`);
|
|
101
|
-
}
|
|
102
|
-
// Asked here rather than at the first `run_command`, so a host with no
|
|
103
|
-
// container engine costs an error instead of a round trip. Nothing is
|
|
104
|
-
// started: the container itself waits until something actually runs.
|
|
105
|
-
if (usesSandbox(project)) {
|
|
106
|
-
await preflight(sandbox, opts.yes);
|
|
107
|
-
}
|
|
108
|
-
// `recordRequests` is what makes the report show the exact bytes sent
|
|
109
|
-
// rather than a reconstruction. It costs state size, and a CLI run is
|
|
110
|
-
// written to disk once and inspected by a human — exactly the case the
|
|
111
|
-
// library leaves it off for by default.
|
|
112
|
-
//
|
|
113
|
-
// `--model` becomes the runner's fallback rather than an alias, so it
|
|
114
|
-
// overrides the config's `model:` without touching agents that pinned one.
|
|
115
|
-
const runner = project.runner({
|
|
116
|
-
recordRequests: true,
|
|
117
|
-
model: opts.model ? project.models.model(opts.model) : undefined,
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// `@zenera/engine` as the terminal hosts it: warnings in colour on stderr, a
|
|
7
|
+
// podman that can be installed on the spot, and a graph beside every run.
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
export { envelope, mounts, } from '@zenera/engine';
|
|
10
|
+
export function open(opts) {
|
|
11
|
+
return openEngine({
|
|
12
|
+
logger: { warn },
|
|
13
|
+
onIssue: (issue) => warn(describeIssue(issue, { bold, dim })),
|
|
14
|
+
preflight,
|
|
15
|
+
graph: traceMermaid,
|
|
16
|
+
...opts,
|
|
118
17
|
});
|
|
119
|
-
const state = await loadState(opts.session);
|
|
120
|
-
const lock = acquire(opts.session);
|
|
121
|
-
return {
|
|
122
|
-
project,
|
|
123
|
-
runner,
|
|
124
|
-
name: opts.project.name,
|
|
125
|
-
workspace,
|
|
126
|
-
workspaceMount: sandbox.spec.workdir ?? SANDBOX_MOUNT,
|
|
127
|
-
files,
|
|
128
|
-
readOnly: Boolean(opts.readOnly),
|
|
129
|
-
session: opts.session,
|
|
130
|
-
memory,
|
|
131
|
-
state,
|
|
132
|
-
sandbox,
|
|
133
|
-
lock,
|
|
134
|
-
close: async () => {
|
|
135
|
-
await teardown(sandbox.pool);
|
|
136
|
-
project.close();
|
|
137
|
-
lock.release();
|
|
138
|
-
},
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
async function loadState(session) {
|
|
142
|
-
if (!existsSync(session.state)) {
|
|
143
|
-
return undefined;
|
|
144
|
-
}
|
|
145
|
-
const json = await readJson(session.state, undefined);
|
|
146
|
-
try {
|
|
147
|
-
return assertState(json);
|
|
148
|
-
}
|
|
149
|
-
catch {
|
|
150
|
-
throw invalidError(`${session.state} is not a usable run state`, 'start a fresh session with --new');
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
/** Every tree the agent can see, the workspace first. */
|
|
154
|
-
export function mounts(engine) {
|
|
155
|
-
return [
|
|
156
|
-
{ host: engine.workspace, at: engine.workspaceMount, readOnly: engine.readOnly },
|
|
157
|
-
...engine.sandbox.mounts.map((m) => ({
|
|
158
|
-
host: m.host,
|
|
159
|
-
at: m.at,
|
|
160
|
-
readOnly: Boolean(m.readOnly),
|
|
161
|
-
})),
|
|
162
|
-
];
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* What one finished run looks like to a program: `zen run --json` prints it,
|
|
166
|
-
* and a batch writes one beside every item. The same function on purpose — a
|
|
167
|
-
* batch whose items were shaped differently would make every consumer of a
|
|
168
|
-
* single run a special case.
|
|
169
|
-
*/
|
|
170
|
-
export function envelope(engine, outcome) {
|
|
171
|
-
return {
|
|
172
|
-
session: { id: engine.session.id, dir: engine.session.dir },
|
|
173
|
-
run: {
|
|
174
|
-
id: outcome.run.id,
|
|
175
|
-
dir: outcome.run.dir,
|
|
176
|
-
input: outcome.run.input,
|
|
177
|
-
output: outcome.run.output,
|
|
178
|
-
state: outcome.run.state,
|
|
179
|
-
meta: outcome.run.meta,
|
|
180
|
-
...(outcome.report ? { report: outcome.report } : {}),
|
|
181
|
-
...(outcome.graph ? { graph: outcome.graph } : {}),
|
|
182
|
-
},
|
|
183
|
-
mounts: mounts(engine),
|
|
184
|
-
agent: outcome.result.agent,
|
|
185
|
-
stopReason: outcome.result.stopReason,
|
|
186
|
-
durationMs: outcome.durationMs,
|
|
187
|
-
usage: outcome.result.usage,
|
|
188
|
-
output: outcome.text,
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* One turn. The session's state decides whether this starts a run or continues
|
|
193
|
-
* one — which is the whole of what `resume` used to be, and why it is not a
|
|
194
|
-
* command.
|
|
195
|
-
*/
|
|
196
|
-
export async function run(engine, input, onEvent, signal) {
|
|
197
|
-
const startedAt = new Date();
|
|
198
|
-
const stream = engine.state
|
|
199
|
-
? engine.runner.send(engine.state, input, { signal })
|
|
200
|
-
: engine.runner.run(engine.project.entry, input, { signal });
|
|
201
|
-
if (onEvent) {
|
|
202
|
-
for await (const event of stream) {
|
|
203
|
-
onEvent(event);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
const result = await stream.final();
|
|
207
|
-
const durationMs = Date.now() - startedAt.getTime();
|
|
208
|
-
engine.state = result.state;
|
|
209
|
-
const outcome = await record(engine, input, result, startedAt, durationMs);
|
|
210
|
-
if (result.stopReason === 'failed') {
|
|
211
|
-
throw new CliError(result.state.error ?? 'the run failed', EXIT.failed, `report: ${outcome.run.report}`);
|
|
212
|
-
}
|
|
213
|
-
return outcome;
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* Everything a finished turn leaves behind. The session state is written first:
|
|
217
|
-
* if the report fails to render, the conversation is still resumable, which is
|
|
218
|
-
* the ordering that loses least.
|
|
219
|
-
*/
|
|
220
|
-
async function record(engine, input, result, startedAt, durationMs) {
|
|
221
|
-
const { session } = engine;
|
|
222
|
-
writeJson(session.state, result.state, 0o644);
|
|
223
|
-
const meta = await readSessionMeta(session);
|
|
224
|
-
meta.lastRunAt = new Date().toISOString();
|
|
225
|
-
meta.title ??= title(input);
|
|
226
|
-
writeSessionMeta(session, meta);
|
|
227
|
-
const run = createRun(session);
|
|
228
|
-
const text = await lastText(result.state, engine.runner.services.payloads);
|
|
229
|
-
await writeFile(run.input, `${asText(input)}\n`, 'utf8');
|
|
230
|
-
await writeFile(run.output, `${text}\n`, 'utf8');
|
|
231
|
-
writeJson(run.state, result.state, 0o644);
|
|
232
|
-
let report;
|
|
233
|
-
try {
|
|
234
|
-
const built = await buildRunReport(result.state, engine.runner.services.payloads, {
|
|
235
|
-
title: `${engine.name} · ${run.id}`,
|
|
236
|
-
architecture: await engine.runner.describe(),
|
|
237
|
-
memory: engine.runner.services.memory?.store,
|
|
238
|
-
mounts: [
|
|
239
|
-
{ at: engine.workspaceMount, href: pathToFileURL(engine.files.root).href },
|
|
240
|
-
...engine.files.mounts.map((m) => ({ at: m.at, href: pathToFileURL(m.host).href })),
|
|
241
|
-
],
|
|
242
|
-
});
|
|
243
|
-
await writeFile(run.report, renderReportHtml(built), 'utf8');
|
|
244
|
-
report = run.report;
|
|
245
|
-
}
|
|
246
|
-
catch {
|
|
247
|
-
// A report is a convenience. Losing it must not lose the run.
|
|
248
|
-
}
|
|
249
|
-
// The same trajectory for the other reader. `report.html` is for a person;
|
|
250
|
-
// this is what `zen inspect graph` prints, written down while the state is
|
|
251
|
-
// already in hand — a batch of a hundred items is otherwise a hundred more
|
|
252
|
-
// commands to run before anything can be read.
|
|
253
|
-
let graph;
|
|
254
|
-
try {
|
|
255
|
-
await writeFile(run.graph, traceMermaid(result.state, {
|
|
256
|
-
runId: run.id,
|
|
257
|
-
dir: run.dir,
|
|
258
|
-
workspace: engine.workspace,
|
|
259
|
-
memory: engine.memory,
|
|
260
|
-
}), 'utf8');
|
|
261
|
-
graph = run.graph;
|
|
262
|
-
}
|
|
263
|
-
catch {
|
|
264
|
-
// Same rule as the report: a view of the run is not the run.
|
|
265
|
-
}
|
|
266
|
-
writeRunMeta(run, {
|
|
267
|
-
version: 1,
|
|
268
|
-
id: run.id,
|
|
269
|
-
session: session.id,
|
|
270
|
-
startedAt: startedAt.toISOString(),
|
|
271
|
-
finishedAt: new Date().toISOString(),
|
|
272
|
-
durationMs,
|
|
273
|
-
agent: result.agent,
|
|
274
|
-
stopReason: result.stopReason,
|
|
275
|
-
turns: turns(result.state),
|
|
276
|
-
usage: result.usage,
|
|
277
|
-
workspace: engine.workspace,
|
|
278
|
-
memory: engine.memory,
|
|
279
|
-
error: result.state.error,
|
|
280
|
-
});
|
|
281
|
-
return { run, result, text, durationMs, report, graph };
|
|
282
|
-
}
|
|
283
|
-
function asText(input) {
|
|
284
|
-
if (typeof input === 'string') {
|
|
285
|
-
return input;
|
|
286
|
-
}
|
|
287
|
-
return input
|
|
288
|
-
.map((part) => {
|
|
289
|
-
if (typeof part === 'string') {
|
|
290
|
-
return part;
|
|
291
|
-
}
|
|
292
|
-
if ('text' in part && typeof part.text === 'string') {
|
|
293
|
-
return part.text;
|
|
294
|
-
}
|
|
295
|
-
// A media shorthand is a single key; naming it is more useful in a
|
|
296
|
-
// transcript than printing the url or the base64 behind it.
|
|
297
|
-
return `[${Object.keys(part)[0] ?? 'part'}]`;
|
|
298
|
-
})
|
|
299
|
-
.join('\n');
|
|
300
|
-
}
|
|
301
|
-
/** A session's label in a picker: the first thing that was ever asked of it. */
|
|
302
|
-
function title(input) {
|
|
303
|
-
const text = asText(input).replace(/\s+/g, ' ').trim();
|
|
304
|
-
return text.length > 60 ? `${text.slice(0, 57)}…` : text;
|
|
305
18
|
}
|
|
19
|
+
export const run = runEngine;
|
|
306
20
|
//# sourceMappingURL=engine.js.map
|
package/dist/external.js
CHANGED
|
@@ -11,7 +11,7 @@ import { CliError, EXIT, bold } from "./term.js";
|
|
|
11
11
|
// Loading a command out of a sibling package
|
|
12
12
|
//
|
|
13
13
|
// The specifier is built rather than written, and that is the whole mechanism:
|
|
14
|
-
// a literal `import('@zenera/
|
|
14
|
+
// a literal `import('@zenera/gym/command')` would make the sibling a
|
|
15
15
|
// compile-time dependency of `zen` — a project reference, a package.json entry,
|
|
16
16
|
// and a cycle, since the sibling already depends on `zen`. Built, it is
|
|
17
17
|
// resolved by Node at the moment the user asks for it and by nobody before.
|
package/dist/history.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { sessionsDir } from '@zenera/engine';
|
|
1
2
|
import { randomUUID } from 'node:crypto';
|
|
2
3
|
import { appendFileSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from 'node:fs';
|
|
3
4
|
import { dirname, join } from 'node:path';
|
|
4
|
-
import { sessionsDir } from "./projects.js";
|
|
5
5
|
// ---------------------------------------------------------------------------
|
|
6
6
|
// The prompt's history
|
|
7
7
|
//
|
package/dist/lib.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
+
export { assertNotEmpty, assertOwner, assertPrivate, assertUsable, describe, ensureDir, ensureHome, envNames, envOf, form, home, isOwner, isProvider, keyId, KeyStore, mask, OWNERS, parseRef, paths, PROVIDERS, readJson, SERVICES, SHAPES, writeJson, type CredentialForm, type KeyCheck, type KeyEntry, type KeyOwner, type Liveness, type Provider, type Service, } from '@zenera/engine';
|
|
1
2
|
export { extract, invokedAs, one, parse, split, type Parsed, type Split } from './args.ts';
|
|
2
3
|
export { bannerArt, printBanner, type BannerText } from './banner.ts';
|
|
3
4
|
export { Cache, items as cacheItems, cacheKey, kinds as cacheKinds, clear as clearCache, NO_CACHE, sweep as sweepCache, type CacheEntry, type CacheItem, type CacheKind, type CacheOptions, type CacheStore, type Swept, } from './cache.ts';
|
|
4
5
|
export { CATALOG_TTL_MS, CURATED, fetchCatalog, loadCatalog, loadCatalogs, matches, PREFERRED, type Catalog, type CatalogEntry, type CatalogOptions, type Filters, type Role, } from './catalog.ts';
|
|
5
6
|
export type { Command, Context } from './command.ts';
|
|
6
|
-
export { assertPrivate, ensureDir, ensureHome, home, paths, readJson, writeJson } from './home.ts';
|
|
7
|
-
export { assertNotEmpty, assertOwner, assertUsable, describe, envNames, envOf, form, isOwner, isProvider, keyId, KeyStore, mask, OWNERS, parseRef, PROVIDERS, SERVICES, SHAPES, type CredentialForm, type KeyCheck, type KeyEntry, type KeyOwner, type Liveness, type Provider, type Service, } from './keys.ts';
|
|
8
7
|
export { probe, probeAll } from './liveness.ts';
|
|
9
8
|
export { engineDisk, ensurePodmanReady, ownedContainers, podmanStatus, removeContainers, type DiskLine, type EngineDisk, type OwnedContainer, type PodmanOptions, type PodmanStatus, } from './podman.ts';
|
|
10
9
|
export { ago, bold, CliError, count, credentialError, cyan, dim, EXIT, fail, green, invalidError, isInteractive, italic, json, note, pad, red, table, underline, usageError, warn, write, writeAll, yellow, type ExitCode, } from './term.ts';
|
package/dist/lib.js
CHANGED
|
@@ -21,12 +21,11 @@
|
|
|
21
21
|
// writers go to stdout/stderr. That vocabulary belongs to startup. A server
|
|
22
22
|
// must not let a `CliError` escape into a request handler.
|
|
23
23
|
// ---------------------------------------------------------------------------
|
|
24
|
+
export { assertNotEmpty, assertOwner, assertPrivate, assertUsable, describe, ensureDir, ensureHome, envNames, envOf, form, home, isOwner, isProvider, keyId, KeyStore, mask, OWNERS, parseRef, paths, PROVIDERS, readJson, SERVICES, SHAPES, writeJson, } from '@zenera/engine';
|
|
24
25
|
export { extract, invokedAs, one, parse, split } from "./args.js";
|
|
25
26
|
export { bannerArt, printBanner } from "./banner.js";
|
|
26
27
|
export { Cache, items as cacheItems, cacheKey, kinds as cacheKinds, clear as clearCache, NO_CACHE, sweep as sweepCache, } from "./cache.js";
|
|
27
28
|
export { CATALOG_TTL_MS, CURATED, fetchCatalog, loadCatalog, loadCatalogs, matches, PREFERRED, } from "./catalog.js";
|
|
28
|
-
export { assertPrivate, ensureDir, ensureHome, home, paths, readJson, writeJson } from "./home.js";
|
|
29
|
-
export { assertNotEmpty, assertOwner, assertUsable, describe, envNames, envOf, form, isOwner, isProvider, keyId, KeyStore, mask, OWNERS, parseRef, PROVIDERS, SERVICES, SHAPES, } from "./keys.js";
|
|
30
29
|
export { probe, probeAll } from "./liveness.js";
|
|
31
30
|
export { engineDisk, ensurePodmanReady, ownedContainers, podmanStatus, removeContainers, } from "./podman.js";
|
|
32
31
|
export { ago, bold, CliError, count, credentialError, cyan, dim, EXIT, fail, green, invalidError, isInteractive, italic, json, note, pad, red, table, underline, usageError, warn, write, writeAll, yellow, } from "./term.js";
|
package/dist/liveness.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { type KeyCheck, type KeyEntry, type KeyStore } from '@zenera/engine';
|
|
1
2
|
import { type Embedder, type Model } from '@zenera/neo';
|
|
2
|
-
import { type KeyCheck, type KeyEntry, type KeyStore } from './keys.ts';
|
|
3
3
|
export declare function classify(err: unknown): KeyCheck;
|
|
4
4
|
/**
|
|
5
5
|
* The cheapest authenticated call each SDK has. Nothing here reads a model or
|
package/dist/liveness.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { envOf, SHAPES, } from '@zenera/engine';
|
|
1
2
|
import { createModel, EXA_BASE_URL, GATEWAY_KEY_HEADER, isUnfunded, ModelRegistry, text, vertexRoute, } from '@zenera/neo';
|
|
2
|
-
import { envOf, SHAPES, } from "./keys.js";
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
// Liveness
|
|
5
5
|
//
|
package/dist/meta/host.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
+
export { envOf, paths, PROVIDERS, readJson, writeJson, type KeyEntry, type KeyStore, type Provider, } from '@zenera/engine';
|
|
1
2
|
export { invokedAs, parse } from '../args.ts';
|
|
2
3
|
export { loadCatalogs } from '../catalog.ts';
|
|
3
4
|
export type { EventLine } from '../eventlog.ts';
|
|
4
|
-
export { paths, readJson, writeJson } from '../home.ts';
|
|
5
|
-
export { envOf, PROVIDERS, type KeyEntry, type KeyStore, type Provider } from '../keys.ts';
|
|
6
5
|
export { PROVIDER_NAMES, splitRef } from '../modelref.ts';
|
|
7
6
|
export { duration } from '../narrate.ts';
|
|
8
7
|
export { rankModels } from '../rank.ts';
|
package/dist/meta/host.js
CHANGED
|
@@ -5,10 +5,9 @@
|
|
|
5
5
|
// other, so the layer can leave for a package of its own by turning this into
|
|
6
6
|
// a dependency. `test/meta-boundary.test.ts` holds the line.
|
|
7
7
|
// ---------------------------------------------------------------------------
|
|
8
|
+
export { envOf, paths, PROVIDERS, readJson, writeJson, } from '@zenera/engine';
|
|
8
9
|
export { invokedAs, parse } from "../args.js";
|
|
9
10
|
export { loadCatalogs } from "../catalog.js";
|
|
10
|
-
export { paths, readJson, writeJson } from "../home.js";
|
|
11
|
-
export { envOf, PROVIDERS } from "../keys.js";
|
|
12
11
|
export { PROVIDER_NAMES, splitRef } from "../modelref.js";
|
|
13
12
|
export { duration } from "../narrate.js";
|
|
14
13
|
export { rankModels } from "../rank.js";
|
package/dist/modelref.d.ts
CHANGED
package/dist/podman.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { type ResolvedBuild } from '@zenera/engine';
|
|
1
2
|
import { runProcess } from '@zenera/neo';
|
|
2
|
-
import type { ResolvedBuild } from './image.ts';
|
|
3
3
|
import { CliError } from './term.ts';
|
|
4
4
|
export interface PodmanOptions {
|
|
5
5
|
/** the image the project needs on disk before the first run */
|
package/dist/podman.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { isBusy, isProjectDir, Registry, sessionIds, sessionsDir, } from '@zenera/engine';
|
|
1
2
|
import { pidAlive, runProcess, SandboxError } from '@zenera/neo';
|
|
2
3
|
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
|
3
4
|
import { homedir, platform } from 'node:os';
|
|
4
5
|
import { join } from 'node:path';
|
|
5
|
-
import { isBusy, isProjectDir, Registry, sessionIds, sessionsDir } from "./projects.js";
|
|
6
6
|
import { CliError, confirm, dim, EXIT, isInteractive, note, progress } from "./term.js";
|
|
7
7
|
const DEFAULT_MACHINE_CPUS = 2;
|
|
8
8
|
const DEFAULT_MACHINE_MEMORY = 2048;
|
|
@@ -485,9 +485,9 @@ export function idleContainers(containers, owners) {
|
|
|
485
485
|
if (c.state !== 'running') {
|
|
486
486
|
return true;
|
|
487
487
|
}
|
|
488
|
-
const
|
|
489
|
-
if (
|
|
490
|
-
return !pidAlive(Number(
|
|
488
|
+
const gym = /^gym-(\d+)$/.exec(c.key ?? '');
|
|
489
|
+
if (gym) {
|
|
490
|
+
return !pidAlive(Number(gym[1]));
|
|
491
491
|
}
|
|
492
492
|
const dir = c.key === undefined ? undefined : owners.get(c.key);
|
|
493
493
|
return dir !== undefined && !isBusy(dir);
|
package/dist/resolve.d.ts
CHANGED
package/dist/resolve.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
+
import * as Projects from '@zenera/engine';
|
|
2
|
+
import { createSession, listSessions, readSessionMeta, requireSession, sessionPaths, stamp, } from '@zenera/engine';
|
|
1
3
|
import { existsSync } from 'node:fs';
|
|
2
4
|
import { isAbsolute, relative, resolve, sep } from 'node:path';
|
|
3
|
-
import { stamp } from "./ids.js";
|
|
4
|
-
import * as Projects from "./projects.js";
|
|
5
|
-
import { createSession, listSessions, readSessionMeta, requireSession, sessionPaths, } from "./session.js";
|
|
6
5
|
import * as term from "./term.js";
|
|
7
6
|
import { ago, bold, cyan, dim, usageError, yellow } from "./term.js";
|
|
8
7
|
// ---------------------------------------------------------------------------
|
package/dist/resources.d.ts
CHANGED
|
@@ -91,7 +91,7 @@ export interface MachineView {
|
|
|
91
91
|
export interface ContainerView {
|
|
92
92
|
name: string;
|
|
93
93
|
id: string;
|
|
94
|
-
kind: 'zen' | '
|
|
94
|
+
kind: 'zen' | 'gym' | 'other';
|
|
95
95
|
project?: string;
|
|
96
96
|
orphaned: boolean;
|
|
97
97
|
/** percent of one core */
|
|
@@ -128,7 +128,7 @@ export interface ResourceView {
|
|
|
128
128
|
totals: {
|
|
129
129
|
running: number;
|
|
130
130
|
zen: number;
|
|
131
|
-
|
|
131
|
+
gym: number;
|
|
132
132
|
other: number;
|
|
133
133
|
orphaned: number;
|
|
134
134
|
memory: number;
|
package/dist/resources.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { isProjectDir, Registry, sessionIds, sessionsDir } from '@zenera/engine';
|
|
1
2
|
import { runProcess } from '@zenera/neo';
|
|
2
3
|
import { cpus as hostCpus, platform as hostPlatform, totalmem } from 'node:os';
|
|
3
4
|
import { basename, join } from 'node:path';
|
|
4
5
|
import { idleContainers, ownedContainers } from "./podman.js";
|
|
5
|
-
import { isProjectDir, Registry, sessionIds, sessionsDir } from "./projects.js";
|
|
6
6
|
import { ago, bytes } from "./term.js";
|
|
7
7
|
import { wrap } from "./tui/wrap.js";
|
|
8
8
|
const MIB = 1024 * 1024;
|
|
@@ -352,7 +352,7 @@ export function viewOf(now, before) {
|
|
|
352
352
|
const prior = before?.stats.find((p) => p.id === s.id);
|
|
353
353
|
const span = prior ? s.systemNano - prior.systemNano : 0;
|
|
354
354
|
const measured = prior !== undefined && span > 0;
|
|
355
|
-
const kind = !own ? 'other' : own.key?.startsWith('
|
|
355
|
+
const kind = !own ? 'other' : own.key?.startsWith('gym-') ? 'gym' : 'zen';
|
|
356
356
|
return {
|
|
357
357
|
name: s.name,
|
|
358
358
|
id: s.id,
|
|
@@ -393,7 +393,7 @@ export function viewOf(now, before) {
|
|
|
393
393
|
totals: {
|
|
394
394
|
running: containers.length,
|
|
395
395
|
zen: containers.filter((c) => c.kind === 'zen').length,
|
|
396
|
-
|
|
396
|
+
gym: containers.filter((c) => c.kind === 'gym').length,
|
|
397
397
|
other: containers.filter((c) => c.kind === 'other').length,
|
|
398
398
|
orphaned: containers.filter((c) => c.orphaned).length,
|
|
399
399
|
memory: containers.reduce((n, c) => n + c.memory, 0),
|
|
@@ -684,7 +684,7 @@ function containerHead(l, v, o) {
|
|
|
684
684
|
const t = v.totals;
|
|
685
685
|
const kinds = [
|
|
686
686
|
t.zen ? `${t.zen} zen` : '',
|
|
687
|
-
t.
|
|
687
|
+
t.gym ? `${t.gym} gym` : '',
|
|
688
688
|
t.other ? `${t.other} other` : '',
|
|
689
689
|
]
|
|
690
690
|
.filter(Boolean)
|
|
@@ -700,7 +700,7 @@ function containerHead(l, v, o) {
|
|
|
700
700
|
.filter(Boolean)
|
|
701
701
|
.join(' · '));
|
|
702
702
|
l.intro('Running ones only: a stopped container keeps its files but uses no memory or cpu. ' +
|
|
703
|
-
'zen makes one per session; zen
|
|
703
|
+
'zen makes one per session; zen gym servers and anything else in podman are listed too.');
|
|
704
704
|
if (t.running === 0) {
|
|
705
705
|
return;
|
|
706
706
|
}
|
|
@@ -796,7 +796,7 @@ const COLUMNS = [
|
|
|
796
796
|
width: 14,
|
|
797
797
|
align: 'left',
|
|
798
798
|
keep: 5,
|
|
799
|
-
cell: (c) => [sp(clipTo(c.project ?? (c.kind === '
|
|
799
|
+
cell: (c) => [sp(clipTo(c.project ?? (c.kind === 'gym' ? '(gym)' : '—'), 14))],
|
|
800
800
|
},
|
|
801
801
|
];
|
|
802
802
|
/** The columns that fit, in order, dropping the least needed until the name has room. */
|
package/dist/sandbox.d.ts
CHANGED
|
@@ -1,62 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import type { SessionPaths } from './session.ts';
|
|
4
|
-
export interface SandboxSetup {
|
|
5
|
-
pool: SandboxPool;
|
|
6
|
-
/** the resolved base spec, for `zn sandbox status` and the image pre-warm */
|
|
7
|
-
spec: SandboxSpec;
|
|
8
|
-
image: string;
|
|
9
|
-
/** the Dockerfile behind `image`, when it came from one */
|
|
10
|
-
build?: ResolvedBuild;
|
|
11
|
-
/** host side of the container's `$HOME`, created only if it is ever needed */
|
|
12
|
-
home: string;
|
|
13
|
-
/** every tree bound into the container besides the workspace itself */
|
|
14
|
-
mounts: readonly SandboxMount[];
|
|
15
|
-
}
|
|
16
|
-
export interface SandboxInputs {
|
|
17
|
-
config: ProjectConfig;
|
|
18
|
-
session: SessionPaths;
|
|
19
|
-
workspace: string;
|
|
20
|
-
/** the project directory, which `build:` paths are relative to */
|
|
21
|
-
root: string;
|
|
22
|
-
readOnly?: boolean;
|
|
23
|
-
/**
|
|
24
|
-
* Trees to mount besides the workspace and the home — the project's assets
|
|
25
|
-
* and its skill catalog. The same array goes to the file tools, so a path
|
|
26
|
-
* a command prints is a path `read_file` accepts.
|
|
27
|
-
*/
|
|
28
|
-
mounts?: readonly SandboxMount[];
|
|
29
|
-
/** `--image` */
|
|
30
|
-
image?: string;
|
|
31
|
-
/** `--no-keys`: refuse to forward credentials, whatever the config says */
|
|
32
|
-
keys?: boolean;
|
|
33
|
-
/**
|
|
34
|
-
* Names from the project's `.env`, forwarded into the container the same
|
|
35
|
-
* way a credential is — by name, so the value stays out of the argv.
|
|
36
|
-
*
|
|
37
|
-
* They travel with the keys rather than beside them: a `.env` is where an
|
|
38
|
-
* api token lives, so `--no-keys` has to withhold it too or the flag is a
|
|
39
|
-
* promise it does not keep.
|
|
40
|
-
*/
|
|
41
|
-
env?: readonly string[];
|
|
42
|
-
/** `false` overrides every `persist:` in the config, the agents' included */
|
|
43
|
-
persist?: boolean;
|
|
44
|
-
}
|
|
45
|
-
export declare function buildSandbox(opts: SandboxInputs): SandboxSetup;
|
|
46
|
-
/**
|
|
47
|
-
* Whether anything in this project can reach a shell.
|
|
48
|
-
*
|
|
49
|
-
* Read off the *resolved* tool lists rather than off the config's selectors,
|
|
50
|
-
* because `sandbox:*`, `*` and a bare name all mean the same thing by the time
|
|
51
|
-
* the loader is done, and only one of those three is greppable.
|
|
52
|
-
*/
|
|
53
|
-
export declare function usesSandbox(project: AgentProject): boolean;
|
|
54
|
-
/**
|
|
55
|
-
* Asked before the first turn rather than at the first tool call, so a missing
|
|
56
|
-
* container engine costs nothing instead of costing a round trip and half a
|
|
57
|
-
* plan. A project that never shells out never gets here at all.
|
|
58
|
-
*/
|
|
1
|
+
import type { SandboxSetup } from '@zenera/engine';
|
|
2
|
+
/** The engine's preflight on a terminal: podman is offered for install and started if needed. */
|
|
59
3
|
export declare function preflight(setup: SandboxSetup, yes?: boolean): Promise<void>;
|
|
60
|
-
/** Best-effort teardown: losing a container must never lose a run. */
|
|
61
|
-
export declare function teardown(pool: SandboxPool): Promise<void>;
|
|
62
4
|
//# sourceMappingURL=sandbox.d.ts.map
|