@anna-ai/cli 0.1.11 → 0.1.14

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.
Files changed (42) hide show
  1. package/dist/agent-DUmINbo4.js +372 -0
  2. package/dist/apps-BEJUn9Ws.js +44 -0
  3. package/dist/bridge-C0DWb5eQ.js +3 -0
  4. package/dist/cli.js +81 -10
  5. package/dist/{credentials-ggdaz_-7.js → credentials-BTv2IfUZ.js} +1 -1
  6. package/dist/credentials-DDqx6XMQ.js +3 -0
  7. package/dist/dashboard.html +8 -4
  8. package/dist/{dev-BPIUX2Nh.js → dev-BfLGxpiT.js} +52 -7
  9. package/dist/dev-C81H9c9_.js +3 -0
  10. package/dist/dev-account-DCyjamBa.js +44 -0
  11. package/dist/dev-app-cache-C3D1Sp_V.js +93 -0
  12. package/dist/dev-app-cache-CZ8lIKiw.js +4 -0
  13. package/dist/{doctor-R1pjmBDG.js → doctor-B3u0edUg.js} +1 -1
  14. package/dist/executa-dev-BhouP8jh.js +212 -0
  15. package/dist/executa-init-COEmKDOE.js +68 -0
  16. package/dist/executa-register-66WKIwQQ.js +47 -0
  17. package/dist/{login-D8cmvBb6.js → login-CsIVbrmf.js} +1 -1
  18. package/dist/{logout-P6L9VU4W.js → logout-gfmKQxMj.js} +1 -1
  19. package/dist/mascot-wlYTJqMs.js +218 -0
  20. package/dist/runner-Bral1LFW.js +279 -0
  21. package/dist/sampling-3EfSlDHM.js +155 -0
  22. package/dist/{server-Cd5Lo-2v.js → server-q6nKCeEV.js} +10 -4
  23. package/dist/storage-CnWTZqq_.js +316 -0
  24. package/dist/{whoami-jqlQwe7Z.js → whoami-BS5wy-Nh.js} +1 -1
  25. package/package.json +3 -3
  26. package/templates/executa/go/README.md +10 -0
  27. package/templates/executa/go/executa.json +4 -0
  28. package/templates/executa/go/go.mod +3 -0
  29. package/templates/executa/go/main.go +148 -0
  30. package/templates/executa/node/README.md +12 -0
  31. package/templates/executa/node/executa.json +4 -0
  32. package/templates/executa/node/package.json +12 -0
  33. package/templates/executa/node/plugin.mjs +126 -0
  34. package/templates/executa/node/sampling-fixture.jsonl +1 -0
  35. package/templates/executa/python/README.md +23 -0
  36. package/templates/executa/python/__SLUG_PY___plugin.py +146 -0
  37. package/templates/executa/python/executa.json +4 -0
  38. package/templates/executa/python/pyproject.toml +15 -0
  39. package/templates/executa/python/sampling-fixture.jsonl +4 -0
  40. package/dist/bridge-BIO7ilgO.js +0 -3
  41. /package/dist/{bridge-Cpm3D2Wk.js → bridge-D6YyP9DM.js} +0 -0
  42. /package/dist/{fixture-RceUUd84.js → fixture-CATHyLLI.js} +0 -0
@@ -0,0 +1,372 @@
1
+ import { canonicalHost } from "./credentials-BTv2IfUZ.js";
2
+ import { hostOf, mintAppSession, requireAccount, withCode } from "./dev-account-DCyjamBa.js";
3
+ import { resolve } from "node:path";
4
+ import { existsSync, readFileSync } from "node:fs";
5
+
6
+ //#region src/executa/agent.ts
7
+ /** JSON-RPC error codes from matrix/src/executa/protocol.py. */
8
+ const AGENT_ERR_NOT_GRANTED = -32041;
9
+ const AGENT_ERR_SESSION_NOT_FOUND = -32042;
10
+ const AGENT_ERR_INVALID_REQUEST = -32043;
11
+ const AGENT_ERR_PROVIDER_ERROR = -32046;
12
+ const AGENT_ERR_RUN_TOO_LARGE = -32047;
13
+ const MAX_FRAMES_PER_RUN = 4096;
14
+ var AgentBridge = class {
15
+ mocks = [];
16
+ /** uuid → minted session token (one entry per `session.create`). */
17
+ sessions = new Map();
18
+ constructor(opts) {
19
+ this.opts = opts;
20
+ if (opts.mode === "mock" && opts.mockFile) {
21
+ const path = resolve(opts.mockFile);
22
+ if (existsSync(path)) for (const line of readFileSync(path, "utf8").split(/\r?\n/)) {
23
+ if (!line.trim() || line.startsWith("#")) continue;
24
+ try {
25
+ this.mocks.push(JSON.parse(line));
26
+ } catch {}
27
+ }
28
+ }
29
+ }
30
+ async call(method, params) {
31
+ if (this.opts.mode === "off") throw withCode(new Error("agent reverse-RPC disabled — rerun without `--no-agent`, or pass `--mock-agent <fixture>` / `--agent <real>`"), AGENT_ERR_NOT_GRANTED);
32
+ if (this.opts.mode === "mock") return this.mockCall(method, params);
33
+ return this.realCall(method, params);
34
+ }
35
+ mockCall(method, params) {
36
+ const shortName = method.startsWith("agent/") ? method.slice(6) : method;
37
+ const content = collectContent(params);
38
+ const matched = this.mocks.find((m) => m.ns === "agent" && m.method === shortName && (!m.match?.contentIncludes || content.includes(m.match.contentIncludes))) ?? this.mocks.find((m) => m.ns === "agent" && m.method === shortName);
39
+ if (matched && matched.result !== void 0) return matched.result;
40
+ if (shortName === "session.create") return {
41
+ app_session_uuid: `aps_mock_${Math.random().toString(16).slice(2, 10)}`,
42
+ expires_in: 600,
43
+ agent_submode: "auto",
44
+ granted_tools: []
45
+ };
46
+ if (shortName === "session.run") {
47
+ const text = String(params.content ?? "");
48
+ return {
49
+ run_id: "mock_run",
50
+ stream_id: "strm_mock",
51
+ frames: [{
52
+ event: "final",
53
+ text: `(mock) echoing: ${text}`,
54
+ seq: 0,
55
+ stream_id: "strm_mock"
56
+ }],
57
+ final: {
58
+ event: "final",
59
+ text: `(mock) echoing: ${text}`
60
+ },
61
+ frame_count: 1
62
+ };
63
+ }
64
+ if (shortName === "session.cancel") return { cancelled: true };
65
+ if (shortName === "session.history") return {
66
+ messages: [],
67
+ cursor: null
68
+ };
69
+ if (shortName === "session.delete") return { deleted: true };
70
+ if (shortName === "complete") return {
71
+ role: "assistant",
72
+ content: {
73
+ type: "text",
74
+ text: "(mock) no fixture matched"
75
+ },
76
+ model: "mock-model",
77
+ stopReason: "endTurn"
78
+ };
79
+ throw withCode(new Error(`unknown agent method: ${method}`), AGENT_ERR_INVALID_REQUEST);
80
+ }
81
+ requireAppSlug() {
82
+ if (!this.opts.appSlug) throw withCode(new Error("agent bridge has no app_slug — pass `--app-slug <slug>` so nexus can attribute the spend"), -32602);
83
+ return this.opts.appSlug;
84
+ }
85
+ async realCall(method, params) {
86
+ const shortName = method.startsWith("agent/") ? method.slice(6) : method;
87
+ switch (shortName) {
88
+ case "session.create": return this.sessionCreate(params);
89
+ case "session.run": return this.sessionRun(params);
90
+ case "session.cancel": return this.sessionCancel(params);
91
+ case "session.history": return this.sessionHistory(params);
92
+ case "session.delete": return this.sessionDelete(params);
93
+ case "complete": return this.complete(params);
94
+ default: throw withCode(new Error(`unknown agent method: ${method}`), AGENT_ERR_INVALID_REQUEST);
95
+ }
96
+ }
97
+ async sessionCreate(params) {
98
+ const acc = requireAccount(this.opts.account);
99
+ const submode = params.agent_submode ?? "auto";
100
+ if (submode !== "auto" && submode !== "fixed") throw withCode(new Error("agent_submode must be 'auto' or 'fixed'"), AGENT_ERR_INVALID_REQUEST);
101
+ const fixedClientId = params.fixed_client_id ?? params.fixedClientId ?? null;
102
+ if (submode === "fixed" && !fixedClientId) throw withCode(new Error("fixed_client_id required when agent_submode='fixed'"), AGENT_ERR_INVALID_REQUEST);
103
+ const minted = await mintAppSession(acc.host, {
104
+ pat: acc.pat,
105
+ kind: "agent",
106
+ app_slug: this.requireAppSlug(),
107
+ submode,
108
+ fixed_client_id: fixedClientId,
109
+ label: params.label ?? "anna-app executa dev",
110
+ quota_caps: params.quota_caps ?? null,
111
+ ttl_seconds: params.ttl_seconds ?? 600
112
+ });
113
+ this.sessions.set(minted.app_session_uuid, {
114
+ appSessionToken: minted.app_session_token,
115
+ appSessionUuid: minted.app_session_uuid,
116
+ expiresAt: Math.floor(Date.now() / 1e3) + (minted.expires_in || 600) - 30,
117
+ submode
118
+ });
119
+ return {
120
+ app_session_uuid: minted.app_session_uuid,
121
+ thread_id: minted.app_session_uuid,
122
+ agent_submode: submode,
123
+ fixed_client_id: fixedClientId,
124
+ expires_in: minted.expires_in,
125
+ granted_tools: []
126
+ };
127
+ }
128
+ getSession(params) {
129
+ const uuid = String(params.app_session_uuid ?? "");
130
+ if (!uuid.startsWith("aps_")) throw withCode(new Error("'app_session_uuid' must start with 'aps_'"), AGENT_ERR_INVALID_REQUEST);
131
+ const ms = this.sessions.get(uuid);
132
+ if (!ms) throw withCode(new Error(`app_session_uuid=${uuid} not in dev cache (expired or never created)`), AGENT_ERR_SESSION_NOT_FOUND);
133
+ if (ms.expiresAt < Math.floor(Date.now() / 1e3)) {
134
+ this.sessions.delete(uuid);
135
+ throw withCode(new Error(`app_session_uuid=${uuid} expired — call session.create again`), AGENT_ERR_SESSION_NOT_FOUND);
136
+ }
137
+ return ms;
138
+ }
139
+ async sessionRun(params) {
140
+ const acc = requireAccount(this.opts.account);
141
+ const ms = this.getSession(params);
142
+ const content = params.content;
143
+ if (typeof content !== "string" || !content.trim()) throw withCode(new Error("'content' must be a non-empty string"), AGENT_ERR_INVALID_REQUEST);
144
+ const runId = params.run_id ?? `run_${Math.random().toString(16).slice(2, 18)}`;
145
+ const streamId = `strm_${Math.random().toString(16).slice(2, 18)}`;
146
+ const body = {
147
+ app_session_uuid: ms.appSessionUuid,
148
+ content,
149
+ run_id: runId,
150
+ stream: true
151
+ };
152
+ if (params.attachments !== void 0) body.attachments = params.attachments;
153
+ if (params.recursion_limit !== void 0) body.recursion_limit = params.recursion_limit;
154
+ if (params.allowed_tools !== void 0) body.allowed_tools = params.allowed_tools;
155
+ if (params.system !== void 0) body.system = params.system;
156
+ const mp = params.modelPreferences ?? params.model_preferences;
157
+ if (mp !== void 0) body.model_preferences = mp;
158
+ const url = `${hostOf(acc)}/api/v1/copilot/app/agent`;
159
+ const res = await fetch(url, {
160
+ method: "POST",
161
+ headers: {
162
+ "content-type": "application/json",
163
+ authorization: `Bearer ${ms.appSessionToken}`,
164
+ accept: "text/event-stream"
165
+ },
166
+ body: JSON.stringify(body)
167
+ });
168
+ if (!res.ok || !res.body) {
169
+ const text = await res.text().catch(() => "");
170
+ throw withCode(new Error(`agent/session.run failed: HTTP ${res.status}: ${text}`), AGENT_ERR_PROVIDER_ERROR);
171
+ }
172
+ const frames = [];
173
+ let final = null;
174
+ const textAcc = [];
175
+ const reader = res.body.getReader();
176
+ const decoder = new TextDecoder();
177
+ let buf = "";
178
+ while (true) {
179
+ const { value, done } = await reader.read();
180
+ if (done) break;
181
+ buf += decoder.decode(value, { stream: true });
182
+ let idx;
183
+ while ((idx = buf.indexOf("\n\n")) >= 0) {
184
+ const frameRaw = buf.slice(0, idx);
185
+ buf = buf.slice(idx + 2);
186
+ const payload = parseSseFrame(frameRaw);
187
+ if (payload && typeof payload === "object") {
188
+ const frame = payload;
189
+ if (frame.seq === void 0) frame.seq = frames.length;
190
+ if (frame.stream_id === void 0) frame.stream_id = streamId;
191
+ frames.push(frame);
192
+ if (frame.event === "delta" && typeof frame.text === "string") textAcc.push(frame.text);
193
+ else if (frame.event === "final") final = frame;
194
+ if (frames.length > MAX_FRAMES_PER_RUN) throw withCode(new Error(`agent run exceeded ${MAX_FRAMES_PER_RUN} frames`), AGENT_ERR_RUN_TOO_LARGE);
195
+ }
196
+ }
197
+ }
198
+ if (buf.trim().length > 0) {
199
+ const payload = parseSseFrame(buf);
200
+ if (payload && typeof payload === "object") {
201
+ const frame = payload;
202
+ if (frame.seq === void 0) frame.seq = frames.length;
203
+ if (frame.stream_id === void 0) frame.stream_id = streamId;
204
+ frames.push(frame);
205
+ if (frame.event === "final") final = frame;
206
+ }
207
+ }
208
+ if (final === null && textAcc.length > 0) {
209
+ final = {
210
+ event: "final",
211
+ text: textAcc.join(""),
212
+ stream_id: streamId,
213
+ seq: frames.length,
214
+ synthesized: true
215
+ };
216
+ frames.push(final);
217
+ }
218
+ return {
219
+ run_id: runId,
220
+ stream_id: streamId,
221
+ frames,
222
+ final,
223
+ frame_count: frames.length
224
+ };
225
+ }
226
+ async sessionCancel(params) {
227
+ const acc = requireAccount(this.opts.account);
228
+ const ms = this.getSession(params);
229
+ const runId = params.run_id;
230
+ if (typeof runId !== "string" || !runId.trim()) throw withCode(new Error("'run_id' required"), AGENT_ERR_INVALID_REQUEST);
231
+ const url = `${hostOf(acc)}/api/v1/copilot/app/agent/cancel`;
232
+ const res = await fetch(url, {
233
+ method: "POST",
234
+ headers: {
235
+ "content-type": "application/json",
236
+ authorization: `Bearer ${ms.appSessionToken}`
237
+ },
238
+ body: JSON.stringify({
239
+ app_session_uuid: ms.appSessionUuid,
240
+ run_id: runId
241
+ })
242
+ });
243
+ if (!res.ok) {
244
+ const text = await res.text().catch(() => "");
245
+ throw withCode(new Error(`agent/session.cancel failed: HTTP ${res.status}: ${text}`), AGENT_ERR_PROVIDER_ERROR);
246
+ }
247
+ return await res.json().catch(() => ({}));
248
+ }
249
+ async sessionHistory(params) {
250
+ const acc = requireAccount(this.opts.account);
251
+ const ms = this.getSession(params);
252
+ const limit = Math.min(200, Math.max(1, Number(params.limit ?? 50)));
253
+ const cursor = params.cursor ?? null;
254
+ const url = new URL(`${hostOf(acc)}/api/v1/copilot/app/sessions/${encodeURIComponent(ms.appSessionUuid)}/history`);
255
+ url.searchParams.set("limit", String(limit));
256
+ if (cursor) url.searchParams.set("cursor", String(cursor));
257
+ const res = await fetch(url, {
258
+ method: "GET",
259
+ headers: { authorization: `Bearer ${ms.appSessionToken}` }
260
+ });
261
+ if (res.status === 404) return {
262
+ messages: [],
263
+ cursor: null
264
+ };
265
+ if (!res.ok) {
266
+ const text = await res.text().catch(() => "");
267
+ throw withCode(new Error(`agent/session.history failed: HTTP ${res.status}: ${text}`), AGENT_ERR_PROVIDER_ERROR);
268
+ }
269
+ return await res.json();
270
+ }
271
+ async sessionDelete(params) {
272
+ const acc = requireAccount(this.opts.account);
273
+ const uuid = String(params.app_session_uuid ?? "");
274
+ if (!uuid.startsWith("aps_")) throw withCode(new Error("'app_session_uuid' must start with 'aps_'"), AGENT_ERR_INVALID_REQUEST);
275
+ const ms = this.sessions.get(uuid);
276
+ this.sessions.delete(uuid);
277
+ if (!ms) return {
278
+ deleted: true,
279
+ app_session_uuid: uuid,
280
+ already_gone: true
281
+ };
282
+ const url = `${hostOf(acc)}/api/v1/copilot/app/sessions/${encodeURIComponent(uuid)}`;
283
+ const res = await fetch(url, {
284
+ method: "DELETE",
285
+ headers: { authorization: `Bearer ${ms.appSessionToken}` }
286
+ });
287
+ if (!res.ok && res.status !== 404) {
288
+ const text = await res.text().catch(() => "");
289
+ throw withCode(new Error(`agent/session.delete failed: HTTP ${res.status}: ${text}`), AGENT_ERR_PROVIDER_ERROR);
290
+ }
291
+ return {
292
+ deleted: true,
293
+ app_session_uuid: uuid
294
+ };
295
+ }
296
+ /**
297
+ * L1 `agent/complete` — single-turn completion convenience. Mints a
298
+ * transient kind=complete session, forwards to `/copilot/app/complete`,
299
+ * returns the assistant message.
300
+ */
301
+ async complete(params) {
302
+ const acc = requireAccount(this.opts.account);
303
+ const messages = params.messages;
304
+ if (!Array.isArray(messages) || messages.length === 0) throw withCode(new Error("'messages' must be a non-empty array"), AGENT_ERR_INVALID_REQUEST);
305
+ const minted = await mintAppSession(acc.host, {
306
+ pat: acc.pat,
307
+ kind: "complete",
308
+ app_slug: this.requireAppSlug()
309
+ });
310
+ const body = { messages };
311
+ if (params.system) body.system = params.system;
312
+ if (params.max_tokens != null) body.max_tokens = params.max_tokens;
313
+ if (params.maxTokens != null) body.max_tokens = params.maxTokens;
314
+ if (params.temperature != null) body.temperature = params.temperature;
315
+ if (params.stop) body.stop = params.stop;
316
+ if (params.model_hint) body.model_hint = params.model_hint;
317
+ if (params.metadata) body.metadata = params.metadata;
318
+ const res = await fetch(`${hostOf(acc)}/api/v1/copilot/app/complete`, {
319
+ method: "POST",
320
+ headers: {
321
+ "content-type": "application/json",
322
+ authorization: `Bearer ${minted.app_session_token}`
323
+ },
324
+ body: JSON.stringify(body)
325
+ });
326
+ if (!res.ok) {
327
+ const text = await res.text().catch(() => "");
328
+ throw withCode(new Error(`agent/complete failed: HTTP ${res.status}: ${text}`), AGENT_ERR_PROVIDER_ERROR);
329
+ }
330
+ return await res.json();
331
+ }
332
+ };
333
+ function collectContent(params) {
334
+ const parts = [];
335
+ if (typeof params.content === "string") parts.push(params.content);
336
+ if (typeof params.system === "string") parts.push(params.system);
337
+ const msgs = params.messages;
338
+ if (Array.isArray(msgs)) for (const m of msgs) {
339
+ const c = m.content;
340
+ if (typeof c === "string") parts.push(c);
341
+ else if (c && typeof c === "object" && "text" in c) parts.push(String(c.text ?? ""));
342
+ }
343
+ return parts.join("\n");
344
+ }
345
+ function parseSseFrame(frame) {
346
+ const lines = frame.split(/\r?\n/);
347
+ const dataLines = [];
348
+ let eventName = null;
349
+ for (const raw of lines) {
350
+ if (!raw || raw.startsWith(":")) continue;
351
+ if (raw.startsWith("data:")) dataLines.push(raw.slice(5).trimStart());
352
+ else if (raw.startsWith("event:")) eventName = raw.slice(6).trim();
353
+ }
354
+ if (dataLines.length === 0) return null;
355
+ const data = dataLines.join("\n");
356
+ try {
357
+ const obj = JSON.parse(data);
358
+ if (eventName && obj && typeof obj === "object" && !("event" in obj)) return {
359
+ ...obj,
360
+ event: eventName
361
+ };
362
+ return obj;
363
+ } catch {
364
+ return {
365
+ event: eventName ?? "raw",
366
+ text: data
367
+ };
368
+ }
369
+ }
370
+
371
+ //#endregion
372
+ export { AgentBridge };
@@ -0,0 +1,44 @@
1
+ import { getAccount } from "./credentials-BTv2IfUZ.js";
2
+ import { listDevApps } from "./dev-app-cache-C3D1Sp_V.js";
3
+ import { bold, cyan, dim, green, red, yellow } from "kleur/colors";
4
+
5
+ //#region src/commands/apps.ts
6
+ async function runAppsList(opts) {
7
+ const acc = getAccount(opts.account);
8
+ if (!acc) {
9
+ console.error(red("✗ no PAT on disk — run `anna-app login --host <nexus-url>` first."));
10
+ return 2;
11
+ }
12
+ let apps;
13
+ try {
14
+ apps = await listDevApps({
15
+ host: acc.host,
16
+ pat: acc.pat
17
+ });
18
+ } catch (e) {
19
+ console.error(red(`✗ ${e.message}`));
20
+ return 2;
21
+ }
22
+ if (opts.json) {
23
+ console.log(JSON.stringify({
24
+ host: acc.host,
25
+ apps
26
+ }, null, 2));
27
+ return 0;
28
+ }
29
+ console.log(bold(cyan("dev apps installed for")) + " " + cyan(acc.host));
30
+ if (apps.length === 0) {
31
+ console.log(dim(" (none — run `anna-app dev` in a project to register one)"));
32
+ return 0;
33
+ }
34
+ for (const a of apps) {
35
+ const tag = a.is_dev ? yellow("[dev]") : green("[prod]");
36
+ const enabled = a.is_enabled ? green("✓") : red("✗");
37
+ console.log(` ${tag} ${enabled} ${bold(a.slug)} ${dim(`(app_id=${a.app_id}, v=${a.installed_version})`)}`);
38
+ if (a.name && a.name !== a.slug) console.log(` ${dim(a.name)}`);
39
+ }
40
+ return 0;
41
+ }
42
+
43
+ //#endregion
44
+ export { runAppsList };
@@ -0,0 +1,3 @@
1
+ import { PINNED_RUNTIME_VERSION, PythonBridge } from "./bridge-D6YyP9DM.js";
2
+
3
+ export { PINNED_RUNTIME_VERSION, PythonBridge };
package/dist/cli.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { printMascot } from "./mascot-wlYTJqMs.js";
2
3
  import { dirname, extname, join, relative, resolve } from "node:path";
3
4
  import { createRequire } from "node:module";
4
5
  import { Command } from "commander";
@@ -53,6 +54,7 @@ function runInit(opts) {
53
54
  }
54
55
  const tplRoot = templateRoot(opts.template);
55
56
  copyDirWithSubst(tplRoot, target, opts.slug);
57
+ printMascot(`scaffolded a fresh app — happy hacking!`);
56
58
  console.log(kleur.green(`✓ scaffolded "${opts.slug}" at ${target}`));
57
59
  console.log(kleur.gray(` next: cd ${opts.targetDir} && anna-app validate`));
58
60
  return 0;
@@ -423,6 +425,10 @@ function printResult(result) {
423
425
  const pkg = createRequire(import.meta.url)("../package.json");
424
426
  const program = new Command();
425
427
  program.name("anna-app").description("Anna App developer CLI (scaffold, validate, harness)").version(pkg.version);
428
+ const argv = process.argv.slice(2);
429
+ const wantsTopLevelHelp = argv.length === 0 || argv[0] === "-h" || argv[0] === "--help" || argv[0] === "help";
430
+ const tagline = wantsTopLevelHelp ? `v${pkg.version} — run \`anna-app help <command>\` for details` : `v${pkg.version} — ${argv.join(" ")}`;
431
+ printMascot(tagline);
426
432
  program.command("init <dir>").description("Scaffold a new Anna App project").option("--slug <slug>", "App slug (lowercase, hyphens)", "").option("--template <name>", "Template to use", "minimal").option("--force", "Overwrite existing dir if non-empty", false).action((dir, opts) => {
427
433
  const slug = opts.slug || dir.split(/[\\/]/).pop() || "my-anna-app";
428
434
  const code = runInit({
@@ -444,8 +450,8 @@ program.command("validate").description("Run schema + ACL checks on a manifest+b
444
450
  const code = printResult(result);
445
451
  process.exit(code);
446
452
  });
447
- program.command("dev").description("Run a local harness (in-process dispatcher + iframe + SSE relay)").option("--manifest <path>", "manifest.json path", "manifest.json").option("--bundle <dir>", "bundle directory (default: ./bundle)").option("--slug <slug>", "App slug (overrides manifest.slug/name)").option("--view <name>", "View name to open (default: manifest default)").option("--matrix-nexus-root <path>", "matrix-nexus checkout (auto-detected if omitted; can also use $ANNA_NEXUS_ROOT)").option("--port <number>", "HTTP port", "5180").option("--user-id <id>", "Harness user_id", "1").option("--cwd <dir>", "Project root (default: cwd)").option("--no-watch", "Disable bundle file watcher (default: enabled)").option("--executa <spec>", "Explicit executa registration; repeatable. Spec: comma-separated key=value (dir=<path>[,tool_id=<id>][,type=python|node|go|binary][,command=\"<argv>\"]). When only `dir=` is given, the executa is auto-detected from executa.json / pyproject.toml / package.json / go.mod. Overrides directory auto-discovery under <manifest-dir>/executas/.", (val, prev) => prev ? [...prev, val] : [val]).option("--no-llm", "Disable LLM bridge (anna.llm/agent return llm_disabled)", false).option("--mock-llm <fixture>", "Serve canned LLM responses from a JSONL fixture").option("--llm-account <host>", "Saved account host to use (default: current)").option("--llm-app-id <id>", "app_id passed to /dev/session/mint").action(async (opts) => {
448
- const { runDev, parseExecutaSpec } = await import("./dev-BPIUX2Nh.js");
453
+ program.command("dev").description("Run a local harness (in-process dispatcher + iframe + SSE relay)").option("--manifest <path>", "manifest.json path", "manifest.json").option("--bundle <dir>", "bundle directory (default: ./bundle)").option("--slug <slug>", "App slug (overrides manifest.slug/name)").option("--view <name>", "View name to open (default: manifest default)").option("--matrix-nexus-root <path>", "matrix-nexus checkout (auto-detected if omitted; can also use $ANNA_NEXUS_ROOT)").option("--port <number>", "HTTP port", "5180").option("--user-id <id>", "Harness user_id", "1").option("--cwd <dir>", "Project root (default: cwd)").option("--no-watch", "Disable bundle file watcher (default: enabled)").option("--executa <spec>", "Explicit executa registration; repeatable. Spec: comma-separated key=value (dir=<path>[,tool_id=<id>][,type=python|node|go|binary][,command=\"<argv>\"]). When only `dir=` is given, the executa is auto-detected from executa.json / pyproject.toml / package.json / go.mod. Overrides directory auto-discovery under <manifest-dir>/executas/.", (val, prev) => prev ? [...prev, val] : [val]).option("--no-llm", "Disable LLM bridge (anna.llm/agent return llm_disabled)").option("--mock-llm <fixture>", "Serve canned LLM responses from a JSONL fixture").option("--llm-account <host>", "Saved account host to use (default: current)").option("--llm-app-slug <slug>", "Override the manifest slug used to register / look up the dev AnnaApp (default: manifest.slug)").action(async (opts) => {
454
+ const { runDev, parseExecutaSpec } = await import("./dev-C81H9c9_.js");
449
455
  const cwd = opts.cwd ?? process.cwd();
450
456
  let executas;
451
457
  if (opts.executa && opts.executa.length > 0) {
@@ -473,13 +479,13 @@ program.command("dev").description("Run a local harness (in-process dispatcher +
473
479
  noLlm: opts.llm === false,
474
480
  mockLlm: opts.mockLlm,
475
481
  llmAccount: opts.llmAccount,
476
- llmAppId: opts.llmAppId ? Number.parseInt(opts.llmAppId, 10) : void 0
482
+ llmAppSlug: opts.llmAppSlug
477
483
  });
478
484
  process.exit(code);
479
485
  });
480
486
  const fixture = program.command("fixture").description("Inspect / replay harness recordings (Phase 6)");
481
487
  fixture.command("verify <file>").description("Schema + invariant checks on a harness JSONL recording").option("--json", "Emit machine-readable JSON", false).action(async (file, opts) => {
482
- const { runFixtureVerify } = await import("./fixture-RceUUd84.js");
488
+ const { runFixtureVerify } = await import("./fixture-CATHyLLI.js");
483
489
  const code = await runFixtureVerify({
484
490
  file,
485
491
  json: opts.json
@@ -487,7 +493,7 @@ fixture.command("verify <file>").description("Schema + invariant checks on a har
487
493
  process.exit(code);
488
494
  });
489
495
  fixture.command("summarize <file>").description("Print a human-readable digest of a harness recording").option("--json", "Emit machine-readable JSON", false).action(async (file, opts) => {
490
- const { runFixtureSummarize } = await import("./fixture-RceUUd84.js");
496
+ const { runFixtureSummarize } = await import("./fixture-CATHyLLI.js");
491
497
  const code = await runFixtureSummarize({
492
498
  file,
493
499
  json: opts.json
@@ -495,7 +501,7 @@ fixture.command("summarize <file>").description("Print a human-readable digest o
495
501
  process.exit(code);
496
502
  });
497
503
  fixture.command("replay <file>").description("Dry-run replay of a harness recording (Phase 6 MVP)").option("--manifest <path>", "manifest.json path", "manifest.json").action(async (file, opts) => {
498
- const { runFixtureReplay } = await import("./fixture-RceUUd84.js");
504
+ const { runFixtureReplay } = await import("./fixture-CATHyLLI.js");
499
505
  const code = await runFixtureReplay({
500
506
  file,
501
507
  manifest: opts.manifest
@@ -503,12 +509,12 @@ fixture.command("replay <file>").description("Dry-run replay of a harness record
503
509
  process.exit(code);
504
510
  });
505
511
  program.command("doctor").description("Check environment for `anna-app dev` (uv, matrix-nexus, dev key)").option("--matrix-nexus-root <path>", "matrix-nexus checkout (optional)").action(async (opts) => {
506
- const { runDoctor } = await import("./doctor-R1pjmBDG.js");
512
+ const { runDoctor } = await import("./doctor-B3u0edUg.js");
507
513
  const code = await runDoctor({ matrixNexusRoot: opts.matrixNexusRoot });
508
514
  process.exit(code);
509
515
  });
510
516
  program.command("login").description("Device-flow login against a nexus host; saves a PAT to ~/.config/anna/credentials.json").requiredOption("--host <url>", "nexus base URL, e.g. https://nexus.example.com").option("--no-browser", "Do not open a browser window automatically", false).action(async (opts) => {
511
- const { runLogin } = await import("./login-D8cmvBb6.js");
517
+ const { runLogin } = await import("./login-CsIVbrmf.js");
512
518
  const code = await runLogin({
513
519
  host: opts.host,
514
520
  noBrowser: opts.browser === false
@@ -516,7 +522,7 @@ program.command("login").description("Device-flow login against a nexus host; sa
516
522
  process.exit(code);
517
523
  });
518
524
  program.command("logout").description("Remove a saved PAT entry").option("--host <url>", "Account to remove (default: current)").option("--all", "Remove every saved account", false).action(async (opts) => {
519
- const { runLogout } = await import("./logout-P6L9VU4W.js");
525
+ const { runLogout } = await import("./logout-gfmKQxMj.js");
520
526
  const code = await runLogout({
521
527
  host: opts.host,
522
528
  all: opts.all
@@ -524,10 +530,75 @@ program.command("logout").description("Remove a saved PAT entry").option("--host
524
530
  process.exit(code);
525
531
  });
526
532
  program.command("whoami").description("Show the current account (and any others)").option("--json", "Emit machine-readable JSON", false).action(async (opts) => {
527
- const { runWhoami } = await import("./whoami-jqlQwe7Z.js");
533
+ const { runWhoami } = await import("./whoami-BS5wy-Nh.js");
528
534
  const code = await runWhoami({ json: opts.json });
529
535
  process.exit(code);
530
536
  });
537
+ program.command("apps:list").description("List dev apps installed for the current PAT").option("--account <host>", "Saved account host (default: current)").option("--json", "Emit machine-readable JSON", false).action(async (opts) => {
538
+ const { runAppsList } = await import("./apps-BEJUn9Ws.js");
539
+ const code = await runAppsList({
540
+ account: opts.account,
541
+ json: opts.json
542
+ });
543
+ process.exit(code);
544
+ });
545
+ const executa = program.command("executa").description("Standalone Executa plugin scaffolding + local runner");
546
+ executa.command("init <dir>").description("Scaffold a standalone Executa plugin (python | node | go)").option("--slug <slug>", "Plugin slug (lowercase, hyphens)", "").option("--template <name>", "Language template: python | node | go", "python").option("--tool-id <id>", "Override the minted tool_id (default: tool-dev-<slug>)").option("--force", "Overwrite existing dir if non-empty", false).action(async (dir, opts) => {
547
+ const slug = opts.slug || dir.split(/[\\/]/).pop() || "my-executa";
548
+ const tpl = opts.template;
549
+ if (![
550
+ "python",
551
+ "node",
552
+ "go"
553
+ ].includes(tpl)) {
554
+ console.error(`✗ unknown template "${opts.template}" — expected python | node | go`);
555
+ process.exit(2);
556
+ }
557
+ const { runExecutaInit } = await import("./executa-init-COEmKDOE.js");
558
+ const code = runExecutaInit({
559
+ targetDir: dir,
560
+ slug,
561
+ template: tpl,
562
+ toolId: opts.toolId,
563
+ force: opts.force
564
+ });
565
+ process.exit(code);
566
+ });
567
+ executa.command("register").description("Register a standalone executa with nexus so `executa dev --storage real` (and future agent / sampling real-mode flows) can mint tokens for it").requiredOption("--tool-id <id>", "Executa tool_id (matches manifest.name)").option("--slug <slug>", "App slug (default: executa-<tool_id>; must be lowercase + hyphens)").option("--name <name>", "Human-readable display name (default: <tool_id>)").option("--account <host>", "Saved account host (default: current)").action(async (opts) => {
568
+ const { runExecutaRegister } = await import("./executa-register-66WKIwQQ.js");
569
+ const code = await runExecutaRegister(opts);
570
+ process.exit(code);
571
+ });
572
+ executa.command("dev").description("Run one Executa plugin in isolation (REPL or one-shot describe/invoke)").option("--dir <path>", "Executa project dir (default: CWD)").option("--spec <spec>", "Override discovery: comma-separated key=value (tool_id=...,type=...,command=\"...\")").option("--describe", "Print MANIFEST and exit", false).option("--health", "Print health and exit", false).option("--invoke <tool>", "Invoke one tool and exit").option("--args <json>", "JSON object passed as tool arguments", "{}").option("--json", "One-shot: emit compact JSON (no banners)", false).option("--no-sampling", "Hard-disable sampling reverse RPC (returns sampling_disabled)").option("--mock-sampling <fixture>", "Serve canned sampling responses from a JSONL fixture (offline)").option("--app-slug <slug>", "Forward sampling to nexus on behalf of this dev AnnaApp slug").option("--sampling-account <host>", "Saved account host for nexus sampling (default: current)").option("--no-agent", "Hard-disable agent reverse RPC (returns agent_not_granted)").option("--mock-agent <fixture>", "Serve canned agent/* responses from a JSONL fixture (offline)").option("--agent-account <host>", "Saved account host for nexus agent (default: --sampling-account or current)").option("--storage <mode>", "Storage backend: off | memory | mock | real (default: memory)").option("--mock-storage <fixture>", "Serve canned storage/* + files/* responses from a JSONL fixture").option("--storage-account <host>", "Saved account host for nexus storage (default: --sampling-account or current)").option("--storage-scopes <list>", "Comma-separated scopes for real storage tokens (default: user,app,tool)").action(async (opts) => {
573
+ const { runExecutaDev } = await import("./executa-dev-BhouP8jh.js");
574
+ const storageMode = opts.storage === void 0 ? void 0 : (() => {
575
+ const m = opts.storage;
576
+ if (m === "off" || m === "memory" || m === "mock" || m === "real") return m;
577
+ console.error(`✗ --storage must be one of off | memory | mock | real (got: ${m})`);
578
+ process.exit(2);
579
+ })();
580
+ const code = await runExecutaDev({
581
+ dir: opts.dir,
582
+ spec: opts.spec,
583
+ describe: opts.describe,
584
+ health: opts.health,
585
+ invoke: opts.invoke,
586
+ args: opts.args,
587
+ json: opts.json,
588
+ noSampling: opts.sampling === false,
589
+ mockSampling: opts.mockSampling,
590
+ appSlug: opts.appSlug,
591
+ samplingAccount: opts.samplingAccount,
592
+ noAgent: opts.agent === false,
593
+ mockAgent: opts.mockAgent,
594
+ agentAccount: opts.agentAccount,
595
+ storage: storageMode,
596
+ mockStorage: opts.mockStorage,
597
+ storageAccount: opts.storageAccount,
598
+ storageScopes: opts.storageScopes
599
+ });
600
+ process.exit(code);
601
+ });
531
602
  program.parseAsync(process.argv).catch((e) => {
532
603
  console.error(e);
533
604
  process.exit(2);
@@ -119,4 +119,4 @@ function credentialsAreLooselyPermissioned() {
119
119
  }
120
120
 
121
121
  //#endregion
122
- export { canonicalHost, credentialsAreLooselyPermissioned, getAccount, maskPat, readCredentials, removeAccount, saveAccount };
122
+ export { canonicalHost, credentialsAreLooselyPermissioned, credentialsPath, getAccount, maskPat, readCredentials, removeAccount, saveAccount, writeCredentials };
@@ -0,0 +1,3 @@
1
+ import { canonicalHost, credentialsAreLooselyPermissioned, credentialsPath, getAccount, maskPat, readCredentials, removeAccount, saveAccount, writeCredentials } from "./credentials-BTv2IfUZ.js";
2
+
3
+ export { getAccount };
@@ -337,20 +337,24 @@
337
337
  }
338
338
 
339
339
  function relayEventToIframe(ev) {
340
- // ev = { kind:"event", user_id, kind:..., payload, ts } from server.ts
340
+ // ev arrives from the WS server as { kind:"event", event:"<name>", payload }
341
+ // (see server.ts: `ws.send({ kind: "event", ...ev })`). The SDK keys its
342
+ // handlers off `event` (e.g. "rpc.stream", "auth.refresh"), so we MUST
343
+ // forward `ev.event` — not `ev.kind`, which is always the literal
344
+ // string "event" and would drop every frame on the floor.
341
345
  if (!iframe.contentWindow) return;
342
346
  const env = {
343
347
  wid: windowUuid,
344
348
  kind: "event",
345
- event: ev.kind,
349
+ event: ev.event,
346
350
  payload: ev.payload,
347
351
  };
348
352
  iframe.contentWindow.postMessage(env, "*");
349
353
  logLine(
350
354
  "event",
351
- `← event <span class="pill">${escapeHtml(ev.kind)}</span> ${escapeHtml(JSON.stringify(ev.payload))}`,
355
+ `← event <span class="pill">${escapeHtml(ev.event)}</span> ${escapeHtml(JSON.stringify(ev.payload))}`,
352
356
  );
353
- recPush("event", { event: ev.kind, payload: ev.payload });
357
+ recPush("event", { event: ev.event, payload: ev.payload });
354
358
  }
355
359
 
356
360
  // postMessage RPC bridge: iframe → POST /api/session/call → result