@anna-ai/cli 0.1.14 → 0.1.17
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/dist/{apps-BEJUn9Ws.js → apps-ClgEOdKD.js} +1 -1
- package/dist/bridge-B1vq1oG3.js +3 -0
- package/dist/{bridge-D6YyP9DM.js → bridge-Dffh9JUd.js} +80 -2
- package/dist/cli.js +15 -8
- package/dist/{dev-BfLGxpiT.js → dev-BUetXnfG.js} +37 -10
- package/dist/dev-Bi6rkb1x.js +3 -0
- package/dist/dev-app-cache-3Pfesngr.js +189 -0
- package/dist/dev-app-cache-CZ1UjMz0.js +4 -0
- package/dist/{doctor-B3u0edUg.js → doctor-Dxkx0eqv.js} +1 -1
- package/dist/{executa-dev-BhouP8jh.js → executa-dev-BzhSd_A2.js} +59 -12
- package/dist/host_upload-C_pGOS6p.js +136 -0
- package/dist/image-bwolX7pa.js +131 -0
- package/dist/{runner-Bral1LFW.js → runner-DmGLdat0.js} +44 -1
- package/dist/{server-q6nKCeEV.js → server-6WHNkydc.js} +302 -2
- package/package.json +1 -1
- package/templates/minimal/bundle/app.js +2 -0
- package/templates/minimal/bundle/index.html +1 -2
- package/dist/bridge-C0DWb5eQ.js +0 -3
- package/dist/dev-C81H9c9_.js +0 -3
- package/dist/dev-app-cache-C3D1Sp_V.js +0 -93
- package/dist/dev-app-cache-CZ8lIKiw.js +0 -4
- /package/dist/{sampling-3EfSlDHM.js → sampling-CJUDG-mf.js} +0 -0
- /package/dist/{storage-CnWTZqq_.js → storage-EQJA_0UW.js} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getAccount } from "./credentials-BTv2IfUZ.js";
|
|
2
|
-
import { listDevApps } from "./dev-app-cache-
|
|
2
|
+
import { listDevApps } from "./dev-app-cache-3Pfesngr.js";
|
|
3
3
|
import { bold, cyan, dim, green, red, yellow } from "kleur/colors";
|
|
4
4
|
|
|
5
5
|
//#region src/commands/apps.ts
|
|
@@ -9,17 +9,39 @@ import { createInterface } from "node:readline";
|
|
|
9
9
|
* `uvx <pkg>@<version>` so end users always run the dispatcher version
|
|
10
10
|
* the CLI was tested against.
|
|
11
11
|
*/
|
|
12
|
-
const PINNED_RUNTIME_VERSION = "0.2.
|
|
12
|
+
const PINNED_RUNTIME_VERSION = "0.2.0a4";
|
|
13
|
+
/**
|
|
14
|
+
* Throwable from a {@link RequestHandler} to send a structured JSON-RPC
|
|
15
|
+
* error back to the python bridge with a stable string ``code`` (e.g.
|
|
16
|
+
* ``"llm_disabled"``, ``"provider_error"``). The python side decodes the
|
|
17
|
+
* code from ``error.data.errorCode``.
|
|
18
|
+
*/
|
|
19
|
+
var BridgeRequestError = class extends Error {
|
|
20
|
+
constructor(code, message, details) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.code = code;
|
|
23
|
+
this.details = details;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
13
26
|
var PythonBridge = class {
|
|
14
27
|
proc = null;
|
|
15
28
|
nextId = 1;
|
|
16
29
|
pending = new Map();
|
|
30
|
+
requestHandlers = new Map();
|
|
17
31
|
readyPromise = null;
|
|
18
32
|
closed = false;
|
|
19
33
|
constructor(opts) {
|
|
20
34
|
this.opts = opts;
|
|
21
35
|
}
|
|
22
36
|
/**
|
|
37
|
+
* Register a handler for an outbound python→node RPC method (e.g.
|
|
38
|
+
* ``host.llm.complete``). Subsequent registrations for the same method
|
|
39
|
+
* replace the previous handler.
|
|
40
|
+
*/
|
|
41
|
+
onRequest(method, handler) {
|
|
42
|
+
this.requestHandlers.set(method, handler);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
23
45
|
* Build the default launch command from `opts.mode`. uvx by default; for
|
|
24
46
|
* `nexus-source` mode, `matrixNexusRoot` must be set.
|
|
25
47
|
*/
|
|
@@ -93,6 +115,10 @@ var PythonBridge = class {
|
|
|
93
115
|
resolve$1();
|
|
94
116
|
return;
|
|
95
117
|
}
|
|
118
|
+
if (typeof env$1.method === "string") {
|
|
119
|
+
this.handleIncomingRequest(env$1);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
96
122
|
this.handleResponse(env$1);
|
|
97
123
|
});
|
|
98
124
|
this.proc.on("error", (e) => reject(e));
|
|
@@ -102,6 +128,58 @@ var PythonBridge = class {
|
|
|
102
128
|
});
|
|
103
129
|
await this.readyPromise;
|
|
104
130
|
}
|
|
131
|
+
async handleIncomingRequest(env) {
|
|
132
|
+
const method = env.method;
|
|
133
|
+
const id = env.id;
|
|
134
|
+
if (id == null) return;
|
|
135
|
+
const params = env.params ?? {};
|
|
136
|
+
const handler = this.requestHandlers.get(method);
|
|
137
|
+
if (!handler) {
|
|
138
|
+
this.writeFrame({
|
|
139
|
+
jsonrpc: "2.0",
|
|
140
|
+
id,
|
|
141
|
+
error: {
|
|
142
|
+
code: -32601,
|
|
143
|
+
message: `method not found: ${method}`
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
const result = await handler(params);
|
|
150
|
+
this.writeFrame({
|
|
151
|
+
jsonrpc: "2.0",
|
|
152
|
+
id,
|
|
153
|
+
result: result ?? {}
|
|
154
|
+
});
|
|
155
|
+
} catch (e) {
|
|
156
|
+
if (e instanceof BridgeRequestError) this.writeFrame({
|
|
157
|
+
jsonrpc: "2.0",
|
|
158
|
+
id,
|
|
159
|
+
error: {
|
|
160
|
+
code: -32e3,
|
|
161
|
+
message: e.message,
|
|
162
|
+
data: {
|
|
163
|
+
errorCode: e.code,
|
|
164
|
+
...e.details ?? {}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
else this.writeFrame({
|
|
169
|
+
jsonrpc: "2.0",
|
|
170
|
+
id,
|
|
171
|
+
error: {
|
|
172
|
+
code: -32603,
|
|
173
|
+
message: e.message,
|
|
174
|
+
data: { errorCode: "internal_error" }
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
writeFrame(env) {
|
|
180
|
+
if (!this.proc) return;
|
|
181
|
+
this.proc.stdin.write(`${JSON.stringify(env)}\n`);
|
|
182
|
+
}
|
|
105
183
|
handleResponse(env) {
|
|
106
184
|
const id = env.id;
|
|
107
185
|
if (id == null) return;
|
|
@@ -143,4 +221,4 @@ var PythonBridge = class {
|
|
|
143
221
|
};
|
|
144
222
|
|
|
145
223
|
//#endregion
|
|
146
|
-
export { PINNED_RUNTIME_VERSION, PythonBridge };
|
|
224
|
+
export { BridgeRequestError, PINNED_RUNTIME_VERSION, PythonBridge };
|
package/dist/cli.js
CHANGED
|
@@ -450,8 +450,8 @@ program.command("validate").description("Run schema + ACL checks on a manifest+b
|
|
|
450
450
|
const code = printResult(result);
|
|
451
451
|
process.exit(code);
|
|
452
452
|
});
|
|
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-
|
|
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)").option("--storage <mode>", "Storage backend: \"legacy\" (in-memory runtime_state, default) or \"aps\" (real nexus APS via /api/v1/storage/* — requires `anna-app login`).", "legacy").action(async (opts) => {
|
|
454
|
+
const { runDev, parseExecutaSpec } = await import("./dev-Bi6rkb1x.js");
|
|
455
455
|
const cwd = opts.cwd ?? process.cwd();
|
|
456
456
|
let executas;
|
|
457
457
|
if (opts.executa && opts.executa.length > 0) {
|
|
@@ -479,7 +479,8 @@ program.command("dev").description("Run a local harness (in-process dispatcher +
|
|
|
479
479
|
noLlm: opts.llm === false,
|
|
480
480
|
mockLlm: opts.mockLlm,
|
|
481
481
|
llmAccount: opts.llmAccount,
|
|
482
|
-
llmAppSlug: opts.llmAppSlug
|
|
482
|
+
llmAppSlug: opts.llmAppSlug,
|
|
483
|
+
storageMode: opts.storage
|
|
483
484
|
});
|
|
484
485
|
process.exit(code);
|
|
485
486
|
});
|
|
@@ -509,7 +510,7 @@ fixture.command("replay <file>").description("Dry-run replay of a harness record
|
|
|
509
510
|
process.exit(code);
|
|
510
511
|
});
|
|
511
512
|
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) => {
|
|
512
|
-
const { runDoctor } = await import("./doctor-
|
|
513
|
+
const { runDoctor } = await import("./doctor-Dxkx0eqv.js");
|
|
513
514
|
const code = await runDoctor({ matrixNexusRoot: opts.matrixNexusRoot });
|
|
514
515
|
process.exit(code);
|
|
515
516
|
});
|
|
@@ -535,7 +536,7 @@ program.command("whoami").description("Show the current account (and any others)
|
|
|
535
536
|
process.exit(code);
|
|
536
537
|
});
|
|
537
538
|
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-
|
|
539
|
+
const { runAppsList } = await import("./apps-ClgEOdKD.js");
|
|
539
540
|
const code = await runAppsList({
|
|
540
541
|
account: opts.account,
|
|
541
542
|
json: opts.json
|
|
@@ -569,8 +570,8 @@ executa.command("register").description("Register a standalone executa with nexu
|
|
|
569
570
|
const code = await runExecutaRegister(opts);
|
|
570
571
|
process.exit(code);
|
|
571
572
|
});
|
|
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-
|
|
573
|
+
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)").option("--no-image", "Hard-disable image reverse RPC (returns image_not_granted)").option("--mock-image <fixture>", "Serve canned image/generate + image/edit responses from a JSONL fixture").option("--image-account <host>", "Saved account host for nexus image (default: --sampling-account or current)").option("--no-upload", "Hard-disable host/uploadFile reverse RPC (returns upload_not_granted)").option("--mock-upload <fixture>", "Serve canned host/uploadFile responses from a JSONL fixture").option("--upload-account <host>", "Saved account host for nexus uploads (default: --sampling-account or current)").action(async (opts) => {
|
|
574
|
+
const { runExecutaDev } = await import("./executa-dev-BzhSd_A2.js");
|
|
574
575
|
const storageMode = opts.storage === void 0 ? void 0 : (() => {
|
|
575
576
|
const m = opts.storage;
|
|
576
577
|
if (m === "off" || m === "memory" || m === "mock" || m === "real") return m;
|
|
@@ -595,7 +596,13 @@ executa.command("dev").description("Run one Executa plugin in isolation (REPL or
|
|
|
595
596
|
storage: storageMode,
|
|
596
597
|
mockStorage: opts.mockStorage,
|
|
597
598
|
storageAccount: opts.storageAccount,
|
|
598
|
-
storageScopes: opts.storageScopes
|
|
599
|
+
storageScopes: opts.storageScopes,
|
|
600
|
+
noImage: opts.image === false,
|
|
601
|
+
mockImage: opts.mockImage,
|
|
602
|
+
imageAccount: opts.imageAccount,
|
|
603
|
+
noUpload: opts.upload === false,
|
|
604
|
+
mockUpload: opts.mockUpload,
|
|
605
|
+
uploadAccount: opts.uploadAccount
|
|
599
606
|
});
|
|
600
607
|
process.exit(code);
|
|
601
608
|
});
|
|
@@ -31,8 +31,20 @@ async function runDev(opts) {
|
|
|
31
31
|
}
|
|
32
32
|
const matrixNexusRoot = await resolveMatrixNexusRoot(opts.matrixNexusRoot, cwd);
|
|
33
33
|
const mode = matrixNexusRoot ? "nexus-source" : "uvx";
|
|
34
|
-
const
|
|
35
|
-
|
|
34
|
+
const storageMode = opts.storageMode === "aps" ? "aps" : "legacy";
|
|
35
|
+
if (opts.storageMode && opts.storageMode !== "legacy" && opts.storageMode !== "aps") {
|
|
36
|
+
console.error(red(`✗ --storage must be "legacy" or "aps", got "${opts.storageMode}"`));
|
|
37
|
+
return 2;
|
|
38
|
+
}
|
|
39
|
+
if (storageMode === "aps") {
|
|
40
|
+
if (opts.noLlm || opts.mockLlm) {
|
|
41
|
+
console.error(red("✗ --storage aps requires a real LLM bridge (PAT on disk); drop --no-llm / --mock-llm or switch back to --storage legacy."));
|
|
42
|
+
return 2;
|
|
43
|
+
}
|
|
44
|
+
process.env.ANNA_APP_RUNTIME_STORAGE_MODE = "aps";
|
|
45
|
+
}
|
|
46
|
+
const { PythonBridge, PINNED_RUNTIME_VERSION } = await import("./bridge-B1vq1oG3.js");
|
|
47
|
+
const { HarnessServer } = await import("./server-6WHNkydc.js");
|
|
36
48
|
const bridge = new PythonBridge({
|
|
37
49
|
mode,
|
|
38
50
|
matrixNexusRoot: matrixNexusRoot ?? void 0,
|
|
@@ -75,7 +87,9 @@ async function runDev(opts) {
|
|
|
75
87
|
manifest
|
|
76
88
|
});
|
|
77
89
|
if (llm === null) return 2;
|
|
90
|
+
llm.storageMode = storageMode;
|
|
78
91
|
console.log(` llm bridge ${dim(llm.mode === "off" ? "disabled (--no-llm)" : llm.mode === "mock" ? `mock (${opts.mockLlm})` : `real${opts.llmAccount ? ` [${opts.llmAccount}]` : ""} → app_slug=${llm.appSlug}`)}`);
|
|
92
|
+
console.log(` storage backend ${dim(storageMode === "aps" ? "aps (real nexus APS via /api/v1/storage/*)" : "legacy (in-memory runtime_state)")}`);
|
|
79
93
|
const server = new HarnessServer({
|
|
80
94
|
slug,
|
|
81
95
|
manifest,
|
|
@@ -123,7 +137,7 @@ async function runDev(opts) {
|
|
|
123
137
|
*/
|
|
124
138
|
async function resolveRealLlm(args) {
|
|
125
139
|
const { getAccount } = await import("./credentials-DDqx6XMQ.js");
|
|
126
|
-
const { ensureDevAppRegistered } = await import("./dev-app-cache-
|
|
140
|
+
const { ensureDevAppRegistered } = await import("./dev-app-cache-CZ1UjMz0.js");
|
|
127
141
|
const acc = getAccount(args.account);
|
|
128
142
|
if (!acc) {
|
|
129
143
|
console.error(red("✗ no developer PAT on disk — run `anna-app login --host <nexus-url>` first.\n (or use `--no-llm` / `--mock-llm <fixture>` to develop offline.)"));
|
|
@@ -134,22 +148,35 @@ async function resolveRealLlm(args) {
|
|
|
134
148
|
return null;
|
|
135
149
|
}
|
|
136
150
|
const manifest = args.manifest;
|
|
151
|
+
const registerInput = {
|
|
152
|
+
slug: args.appSlug,
|
|
153
|
+
name: manifest.name,
|
|
154
|
+
category: manifest.category,
|
|
155
|
+
tagline: manifest.tagline
|
|
156
|
+
};
|
|
137
157
|
try {
|
|
138
158
|
const entry = await ensureDevAppRegistered({
|
|
139
159
|
cwd: args.cwd,
|
|
140
160
|
host: acc.host,
|
|
141
161
|
pat: acc.pat,
|
|
142
|
-
input:
|
|
143
|
-
slug: args.appSlug,
|
|
144
|
-
name: manifest.name,
|
|
145
|
-
category: manifest.category,
|
|
146
|
-
tagline: manifest.tagline
|
|
147
|
-
}
|
|
162
|
+
input: registerInput
|
|
148
163
|
});
|
|
149
164
|
return {
|
|
150
165
|
mode: "real",
|
|
151
166
|
account: args.account,
|
|
152
|
-
appSlug: entry.slug
|
|
167
|
+
appSlug: entry.slug,
|
|
168
|
+
onAppSlugNotFound: async () => {
|
|
169
|
+
const { invalidateDevAppCache } = await import("./dev-app-cache-CZ1UjMz0.js");
|
|
170
|
+
invalidateDevAppCache(args.cwd);
|
|
171
|
+
const fresh = getAccount(args.account);
|
|
172
|
+
if (!fresh) throw new Error("PAT not found while re-registering dev app — run `anna-app login` again");
|
|
173
|
+
await ensureDevAppRegistered({
|
|
174
|
+
cwd: args.cwd,
|
|
175
|
+
host: fresh.host,
|
|
176
|
+
pat: fresh.pat,
|
|
177
|
+
input: registerInput
|
|
178
|
+
});
|
|
179
|
+
}
|
|
153
180
|
};
|
|
154
181
|
} catch (e) {
|
|
155
182
|
console.error(red(`✗ failed to register dev app on nexus: ${e.message}`));
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { canonicalHost } from "./credentials-BTv2IfUZ.js";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
|
+
|
|
5
|
+
//#region src/dev-app-cache.ts
|
|
6
|
+
const CACHE_DIR = ".anna";
|
|
7
|
+
const CACHE_FILE = "dev-app.json";
|
|
8
|
+
function cachePath(cwd) {
|
|
9
|
+
return resolve(cwd, CACHE_DIR, CACHE_FILE);
|
|
10
|
+
}
|
|
11
|
+
function readDevAppCache(cwd) {
|
|
12
|
+
const p = cachePath(cwd);
|
|
13
|
+
if (!existsSync(p)) return null;
|
|
14
|
+
try {
|
|
15
|
+
const raw = JSON.parse(readFileSync(p, "utf-8"));
|
|
16
|
+
if (typeof raw.host === "string" && typeof raw.slug === "string" && typeof raw.app_id === "number") return {
|
|
17
|
+
host: raw.host,
|
|
18
|
+
slug: raw.slug,
|
|
19
|
+
app_id: raw.app_id,
|
|
20
|
+
name: raw.name ?? raw.slug,
|
|
21
|
+
registered_at: raw.registered_at ?? ""
|
|
22
|
+
};
|
|
23
|
+
} catch {}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
function writeDevAppCache(cwd, entry) {
|
|
27
|
+
const p = cachePath(cwd);
|
|
28
|
+
mkdirSync(dirname(p), { recursive: true });
|
|
29
|
+
writeFileSync(p, JSON.stringify(entry, null, 2) + "\n", "utf-8");
|
|
30
|
+
}
|
|
31
|
+
/** Delete the local dev-app cache file.
|
|
32
|
+
*
|
|
33
|
+
* Used by the LLM bridge's recovery flow: when `/dev/session/mint`
|
|
34
|
+
* returns 404 with "app slug ... not found", the cache is stale (the
|
|
35
|
+
* nexus DB no longer has the row this cache asserts is registered).
|
|
36
|
+
* Invalidating forces the next `ensureDevAppRegistered` call to
|
|
37
|
+
* re-POST `/dev/apps/register`. Idempotent: a missing file is a no-op.
|
|
38
|
+
*/
|
|
39
|
+
function invalidateDevAppCache(cwd) {
|
|
40
|
+
const p = cachePath(cwd);
|
|
41
|
+
if (existsSync(p)) try {
|
|
42
|
+
unlinkSync(p);
|
|
43
|
+
} catch {}
|
|
44
|
+
}
|
|
45
|
+
/** Call POST /api/v1/anna-apps/dev/apps/register. Idempotent server-side. */
|
|
46
|
+
async function registerDevApp(args) {
|
|
47
|
+
const url = `${canonicalHost(args.host)}/api/v1/anna-apps/dev/apps/register`;
|
|
48
|
+
const res = await fetch(url, {
|
|
49
|
+
method: "POST",
|
|
50
|
+
headers: { "content-type": "application/json" },
|
|
51
|
+
body: JSON.stringify({
|
|
52
|
+
pat: args.pat,
|
|
53
|
+
slug: args.input.slug,
|
|
54
|
+
name: args.input.name,
|
|
55
|
+
category: args.input.category,
|
|
56
|
+
tagline: args.input.tagline
|
|
57
|
+
})
|
|
58
|
+
});
|
|
59
|
+
if (!res.ok) {
|
|
60
|
+
const text = await res.text().catch(() => "");
|
|
61
|
+
throw new Error(`/dev/apps/register failed: HTTP ${res.status} ${text}`);
|
|
62
|
+
}
|
|
63
|
+
return await res.json();
|
|
64
|
+
}
|
|
65
|
+
/** Call GET /api/v1/anna-apps/dev/apps. */
|
|
66
|
+
async function listDevApps(args) {
|
|
67
|
+
const url = new URL(`${canonicalHost(args.host)}/api/v1/anna-apps/dev/apps`);
|
|
68
|
+
url.searchParams.set("pat", args.pat);
|
|
69
|
+
const res = await fetch(url, { method: "GET" });
|
|
70
|
+
if (!res.ok) {
|
|
71
|
+
const text = await res.text().catch(() => "");
|
|
72
|
+
throw new Error(`/dev/apps failed: HTTP ${res.status} ${text}`);
|
|
73
|
+
}
|
|
74
|
+
const body = await res.json();
|
|
75
|
+
return body.apps;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Convenience helper for `anna-app dev`: returns a valid cached entry if
|
|
79
|
+
* it still matches the manifest slug, otherwise hits the server to
|
|
80
|
+
* (re-)register. Throws if no PAT is available on disk.
|
|
81
|
+
*/
|
|
82
|
+
async function ensureDevAppRegistered(args) {
|
|
83
|
+
const canonical = canonicalHost(args.host);
|
|
84
|
+
const cached = readDevAppCache(args.cwd);
|
|
85
|
+
if (cached && cached.host === canonical && cached.slug === args.input.slug) return cached;
|
|
86
|
+
const r = await registerDevApp({
|
|
87
|
+
host: args.host,
|
|
88
|
+
pat: args.pat,
|
|
89
|
+
input: args.input
|
|
90
|
+
});
|
|
91
|
+
const entry = {
|
|
92
|
+
host: canonical,
|
|
93
|
+
slug: r.slug,
|
|
94
|
+
app_id: r.app_id,
|
|
95
|
+
name: r.name,
|
|
96
|
+
registered_at: new Date().toISOString()
|
|
97
|
+
};
|
|
98
|
+
writeDevAppCache(args.cwd, entry);
|
|
99
|
+
return entry;
|
|
100
|
+
}
|
|
101
|
+
const _internal = {
|
|
102
|
+
cachePath,
|
|
103
|
+
CACHE_DIR: join(CACHE_DIR, CACHE_FILE)
|
|
104
|
+
};
|
|
105
|
+
const EXECUTA_CACHE_FILE = "dev-executa.json";
|
|
106
|
+
/**
|
|
107
|
+
* Bump this whenever the server-side dev-executa grant shape changes in
|
|
108
|
+
* a way that requires a re-register to pick up. Cached entries written
|
|
109
|
+
* with a lower `cache_version` are treated as stale and re-POSTed.
|
|
110
|
+
*
|
|
111
|
+
* 1 — initial (llm_grant only)
|
|
112
|
+
* 2 — adds image_grant + upload_grant; fixes `allowedPurposes` to
|
|
113
|
+
* protocol whitelist {image_input,image_reference,user_artifact}
|
|
114
|
+
*/
|
|
115
|
+
const EXECUTA_CACHE_VERSION = 2;
|
|
116
|
+
function executaCachePath(cwd) {
|
|
117
|
+
return resolve(cwd, CACHE_DIR, EXECUTA_CACHE_FILE);
|
|
118
|
+
}
|
|
119
|
+
function readDevExecutaCache(cwd) {
|
|
120
|
+
const p = executaCachePath(cwd);
|
|
121
|
+
if (!existsSync(p)) return null;
|
|
122
|
+
try {
|
|
123
|
+
const raw = JSON.parse(readFileSync(p, "utf-8"));
|
|
124
|
+
if (typeof raw.host === "string" && typeof raw.slug === "string" && typeof raw.app_id === "number" && typeof raw.tool_id === "string") return {
|
|
125
|
+
host: raw.host,
|
|
126
|
+
slug: raw.slug,
|
|
127
|
+
app_id: raw.app_id,
|
|
128
|
+
tool_id: raw.tool_id,
|
|
129
|
+
name: raw.name ?? raw.tool_id,
|
|
130
|
+
registered_at: raw.registered_at ?? "",
|
|
131
|
+
cache_version: typeof raw.cache_version === "number" ? raw.cache_version : 0
|
|
132
|
+
};
|
|
133
|
+
} catch {}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
function writeDevExecutaCache(cwd, entry) {
|
|
137
|
+
const p = executaCachePath(cwd);
|
|
138
|
+
mkdirSync(dirname(p), { recursive: true });
|
|
139
|
+
writeFileSync(p, JSON.stringify(entry, null, 2) + "\n", "utf-8");
|
|
140
|
+
}
|
|
141
|
+
/** Call `POST /api/v1/anna-apps/dev/executas/register`. Idempotent. */
|
|
142
|
+
async function registerDevExecuta(args) {
|
|
143
|
+
const url = `${canonicalHost(args.host)}/api/v1/anna-apps/dev/executas/register`;
|
|
144
|
+
const res = await fetch(url, {
|
|
145
|
+
method: "POST",
|
|
146
|
+
headers: { "content-type": "application/json" },
|
|
147
|
+
body: JSON.stringify({
|
|
148
|
+
pat: args.pat,
|
|
149
|
+
tool_id: args.toolId,
|
|
150
|
+
slug: args.slug,
|
|
151
|
+
name: args.name
|
|
152
|
+
})
|
|
153
|
+
});
|
|
154
|
+
if (!res.ok) {
|
|
155
|
+
const text = await res.text().catch(() => "");
|
|
156
|
+
throw new Error(`/dev/executas/register failed: HTTP ${res.status} ${text}`);
|
|
157
|
+
}
|
|
158
|
+
return await res.json();
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Convenience: return a cached executa registration if still valid for
|
|
162
|
+
* this host+tool_id, otherwise hit the server. Throws if no PAT.
|
|
163
|
+
*/
|
|
164
|
+
async function ensureDevExecutaRegistered(args) {
|
|
165
|
+
const canonical = canonicalHost(args.host);
|
|
166
|
+
const cached = readDevExecutaCache(args.cwd);
|
|
167
|
+
if (cached && cached.host === canonical && cached.tool_id === args.toolId && cached.cache_version >= EXECUTA_CACHE_VERSION && (!args.slug || cached.slug === args.slug)) return cached;
|
|
168
|
+
const r = await registerDevExecuta({
|
|
169
|
+
host: args.host,
|
|
170
|
+
pat: args.pat,
|
|
171
|
+
toolId: args.toolId,
|
|
172
|
+
slug: args.slug,
|
|
173
|
+
name: args.name
|
|
174
|
+
});
|
|
175
|
+
const entry = {
|
|
176
|
+
host: canonical,
|
|
177
|
+
slug: r.slug,
|
|
178
|
+
app_id: r.app_id,
|
|
179
|
+
tool_id: r.tool_id,
|
|
180
|
+
name: r.name,
|
|
181
|
+
registered_at: new Date().toISOString(),
|
|
182
|
+
cache_version: EXECUTA_CACHE_VERSION
|
|
183
|
+
};
|
|
184
|
+
writeDevExecutaCache(args.cwd, entry);
|
|
185
|
+
return entry;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
//#endregion
|
|
189
|
+
export { ensureDevAppRegistered, ensureDevExecutaRegistered, invalidateDevAppCache, listDevApps, readDevAppCache, readDevExecutaCache, registerDevApp, registerDevExecuta, writeDevAppCache, writeDevExecutaCache };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import "./credentials-BTv2IfUZ.js";
|
|
2
|
+
import { ensureDevAppRegistered, ensureDevExecutaRegistered, invalidateDevAppCache, listDevApps, readDevAppCache, readDevExecutaCache, registerDevApp, registerDevExecuta, writeDevAppCache, writeDevExecutaCache } from "./dev-app-cache-3Pfesngr.js";
|
|
3
|
+
|
|
4
|
+
export { ensureDevAppRegistered, ensureDevExecutaRegistered, invalidateDevAppCache };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PINNED_RUNTIME_VERSION } from "./bridge-
|
|
1
|
+
import { PINNED_RUNTIME_VERSION } from "./bridge-Dffh9JUd.js";
|
|
2
2
|
import { dirname, isAbsolute, resolve } from "node:path";
|
|
3
3
|
import { existsSync, statSync } from "node:fs";
|
|
4
4
|
import { spawnSync } from "node:child_process";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parseExecutaSpec } from "./dev-
|
|
1
|
+
import { parseExecutaSpec } from "./dev-BUetXnfG.js";
|
|
2
2
|
import { isAbsolute, resolve } from "node:path";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import { bold, cyan, dim, green, red, yellow } from "kleur/colors";
|
|
@@ -33,54 +33,101 @@ async function runExecutaDev(opts) {
|
|
|
33
33
|
}
|
|
34
34
|
const oneShot = !!(opts.describe || opts.health || opts.invoke);
|
|
35
35
|
const quiet = oneShot && (opts.json ?? false);
|
|
36
|
-
const {
|
|
36
|
+
const { getAccount } = await import("./credentials-DDqx6XMQ.js");
|
|
37
|
+
const { ensureDevExecutaRegistered } = await import("./dev-app-cache-CZ1UjMz0.js");
|
|
38
|
+
const needsRealMint = !opts.noSampling && !opts.mockSampling || !opts.noAgent && !opts.mockAgent || !opts.noImage && !opts.mockImage || !opts.noUpload && !opts.mockUpload || opts.storage === "real";
|
|
39
|
+
let effectiveAppSlug = opts.appSlug;
|
|
40
|
+
let autoRegistered = false;
|
|
41
|
+
if (!effectiveAppSlug && needsRealMint) {
|
|
42
|
+
const acc = getAccount(opts.samplingAccount);
|
|
43
|
+
if (acc) try {
|
|
44
|
+
const reg = await ensureDevExecutaRegistered({
|
|
45
|
+
cwd: parsed.project_dir,
|
|
46
|
+
host: acc.host,
|
|
47
|
+
pat: acc.pat,
|
|
48
|
+
toolId: parsed.tool_id
|
|
49
|
+
});
|
|
50
|
+
effectiveAppSlug = reg.slug;
|
|
51
|
+
autoRegistered = true;
|
|
52
|
+
} catch (e) {
|
|
53
|
+
if (!quiet) {
|
|
54
|
+
console.warn(yellow(`! executa auto-register failed: ${e.message}`));
|
|
55
|
+
console.warn(yellow(" reverse-RPC bridges (sampling/agent/image/upload) will run in disabled mode. Pass `--app-slug <slug>` to override, or `--no-sampling --no-agent --no-image --no-upload` to silence."));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const { SamplingBridge } = await import("./sampling-CJUDG-mf.js");
|
|
37
60
|
const sampling = opts.noSampling ? new SamplingBridge({ mode: "off" }) : opts.mockSampling ? new SamplingBridge({
|
|
38
61
|
mode: "mock",
|
|
39
62
|
mockFile: opts.mockSampling
|
|
40
|
-
}) :
|
|
63
|
+
}) : effectiveAppSlug ? new SamplingBridge({
|
|
41
64
|
mode: "real",
|
|
42
65
|
account: opts.samplingAccount,
|
|
43
|
-
appSlug:
|
|
66
|
+
appSlug: effectiveAppSlug
|
|
44
67
|
}) : new SamplingBridge({ mode: "off" });
|
|
45
68
|
const { AgentBridge } = await import("./agent-DUmINbo4.js");
|
|
46
69
|
const agent = opts.noAgent ? new AgentBridge({ mode: "off" }) : opts.mockAgent ? new AgentBridge({
|
|
47
70
|
mode: "mock",
|
|
48
71
|
mockFile: opts.mockAgent
|
|
49
|
-
}) :
|
|
72
|
+
}) : effectiveAppSlug ? new AgentBridge({
|
|
50
73
|
mode: "real",
|
|
51
74
|
account: opts.agentAccount ?? opts.samplingAccount,
|
|
52
|
-
appSlug:
|
|
75
|
+
appSlug: effectiveAppSlug
|
|
53
76
|
}) : new AgentBridge({ mode: "off" });
|
|
54
|
-
const { StorageBridge } = await import("./storage-
|
|
77
|
+
const { StorageBridge } = await import("./storage-EQJA_0UW.js");
|
|
55
78
|
const storageMode = opts.storage ?? (opts.mockStorage ? "mock" : "memory");
|
|
56
79
|
const storage = new StorageBridge({
|
|
57
80
|
mode: storageMode,
|
|
58
81
|
mockFile: opts.mockStorage,
|
|
59
82
|
account: opts.storageAccount ?? opts.samplingAccount,
|
|
60
|
-
appSlug:
|
|
83
|
+
appSlug: effectiveAppSlug,
|
|
61
84
|
scopes: opts.storageScopes ? opts.storageScopes.split(",").map((s) => s.trim()).filter(Boolean) : void 0,
|
|
62
85
|
pluginName: parsed.tool_id
|
|
63
86
|
});
|
|
64
|
-
const { ExecutaRunner } = await import("./runner-
|
|
87
|
+
const { ExecutaRunner } = await import("./runner-DmGLdat0.js");
|
|
88
|
+
const { ImageBridge } = await import("./image-bwolX7pa.js");
|
|
89
|
+
const image = opts.noImage ? new ImageBridge({ mode: "off" }) : opts.mockImage ? new ImageBridge({
|
|
90
|
+
mode: "mock",
|
|
91
|
+
mockFile: opts.mockImage
|
|
92
|
+
}) : effectiveAppSlug ? new ImageBridge({
|
|
93
|
+
mode: "real",
|
|
94
|
+
account: opts.imageAccount ?? opts.samplingAccount,
|
|
95
|
+
appSlug: effectiveAppSlug
|
|
96
|
+
}) : new ImageBridge({ mode: "off" });
|
|
97
|
+
const { HostUploadBridge } = await import("./host_upload-C_pGOS6p.js");
|
|
98
|
+
const hostUpload = opts.noUpload ? new HostUploadBridge({ mode: "off" }) : opts.mockUpload ? new HostUploadBridge({
|
|
99
|
+
mode: "mock",
|
|
100
|
+
mockFile: opts.mockUpload
|
|
101
|
+
}) : effectiveAppSlug ? new HostUploadBridge({
|
|
102
|
+
mode: "real",
|
|
103
|
+
account: opts.uploadAccount ?? opts.samplingAccount,
|
|
104
|
+
appSlug: effectiveAppSlug
|
|
105
|
+
}) : new HostUploadBridge({ mode: "off" });
|
|
65
106
|
const runner = new ExecutaRunner({
|
|
66
107
|
command: parsed.command,
|
|
67
108
|
cwd: parsed.project_dir,
|
|
68
109
|
sampling,
|
|
69
110
|
agent,
|
|
70
111
|
storage,
|
|
112
|
+
image,
|
|
113
|
+
hostUpload,
|
|
71
114
|
onStderr: (line) => {
|
|
72
115
|
const text = quiet ? `${line}\n` : dim(`[executa] ${line}\n`);
|
|
73
116
|
writeStderrCooperative(text);
|
|
74
117
|
}
|
|
75
118
|
});
|
|
76
119
|
if (!quiet) {
|
|
120
|
+
const slugTag = (mode) => effectiveAppSlug ? `${mode} → app_slug=${effectiveAppSlug}${autoRegistered ? " (auto)" : ""}` : "disabled (no fixture, no --app-slug, no PAT)";
|
|
77
121
|
console.log(bold(cyan("anna-app executa dev")));
|
|
78
122
|
console.log(` tool_id ${dim(parsed.tool_id)}`);
|
|
79
123
|
console.log(` dir ${dim(parsed.project_dir)}`);
|
|
80
124
|
console.log(` command ${dim(parsed.command.join(" "))}`);
|
|
81
|
-
console.log(`
|
|
82
|
-
console.log(`
|
|
83
|
-
console.log(`
|
|
125
|
+
if (autoRegistered && effectiveAppSlug) console.log(` executa ${dim(`auto-registered as ${effectiveAppSlug}`)}`);
|
|
126
|
+
console.log(` sampling ${dim(opts.noSampling ? "disabled (--no-sampling)" : opts.mockSampling ? `mock (${opts.mockSampling})` : slugTag("real"))}`);
|
|
127
|
+
console.log(` agent ${dim(opts.noAgent ? "disabled (--no-agent)" : opts.mockAgent ? `mock (${opts.mockAgent})` : slugTag("real"))}`);
|
|
128
|
+
console.log(` storage ${dim(storageMode === "mock" ? `mock (${opts.mockStorage ?? "<no-fixture>"})` : storageMode === "real" ? `real → app_slug=${effectiveAppSlug ?? "<unset>"}` : storageMode)}`);
|
|
129
|
+
console.log(` image ${dim(opts.noImage ? "disabled (--no-image)" : opts.mockImage ? `mock (${opts.mockImage})` : slugTag("real"))}`);
|
|
130
|
+
console.log(` upload ${dim(opts.noUpload ? "disabled (--no-upload)" : opts.mockUpload ? `mock (${opts.mockUpload})` : slugTag("real"))}`);
|
|
84
131
|
}
|
|
85
132
|
let init;
|
|
86
133
|
try {
|