@agent-native/core 0.51.15 → 0.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +3 -3
  2. package/dist/cli/connect.d.ts +4 -3
  3. package/dist/cli/connect.d.ts.map +1 -1
  4. package/dist/cli/connect.js +67 -26
  5. package/dist/cli/connect.js.map +1 -1
  6. package/dist/cli/mcp-config-writers.d.ts +20 -13
  7. package/dist/cli/mcp-config-writers.d.ts.map +1 -1
  8. package/dist/cli/mcp-config-writers.js +152 -13
  9. package/dist/cli/mcp-config-writers.js.map +1 -1
  10. package/dist/cli/mcp.d.ts +2 -2
  11. package/dist/cli/mcp.d.ts.map +1 -1
  12. package/dist/cli/mcp.js +41 -193
  13. package/dist/cli/mcp.js.map +1 -1
  14. package/dist/cli/plan-local.d.ts +3 -1
  15. package/dist/cli/plan-local.d.ts.map +1 -1
  16. package/dist/cli/plan-local.js +24 -6
  17. package/dist/cli/plan-local.js.map +1 -1
  18. package/dist/cli/recap.d.ts.map +1 -1
  19. package/dist/cli/recap.js +1 -1
  20. package/dist/cli/recap.js.map +1 -1
  21. package/dist/cli/skills.d.ts +11 -4
  22. package/dist/cli/skills.d.ts.map +1 -1
  23. package/dist/cli/skills.js +218 -53
  24. package/dist/cli/skills.js.map +1 -1
  25. package/dist/client/agent-engine-key.d.ts +6 -4
  26. package/dist/client/agent-engine-key.d.ts.map +1 -1
  27. package/dist/client/agent-engine-key.js +9 -6
  28. package/dist/client/agent-engine-key.js.map +1 -1
  29. package/dist/client/chat/run-recovery.js +1 -1
  30. package/dist/client/chat/run-recovery.js.map +1 -1
  31. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  32. package/dist/client/settings/SettingsPanel.js +7 -14
  33. package/dist/client/settings/SettingsPanel.js.map +1 -1
  34. package/dist/coding-tools/run-code.d.ts +7 -0
  35. package/dist/coding-tools/run-code.d.ts.map +1 -1
  36. package/dist/coding-tools/run-code.js +21 -106
  37. package/dist/coding-tools/run-code.js.map +1 -1
  38. package/dist/coding-tools/sandbox/adapter.d.ts +79 -0
  39. package/dist/coding-tools/sandbox/adapter.d.ts.map +1 -0
  40. package/dist/coding-tools/sandbox/adapter.js +24 -0
  41. package/dist/coding-tools/sandbox/adapter.js.map +1 -0
  42. package/dist/coding-tools/sandbox/index.d.ts +51 -0
  43. package/dist/coding-tools/sandbox/index.d.ts.map +1 -0
  44. package/dist/coding-tools/sandbox/index.js +79 -0
  45. package/dist/coding-tools/sandbox/index.js.map +1 -0
  46. package/dist/coding-tools/sandbox/local-child-process-adapter.d.ts +24 -0
  47. package/dist/coding-tools/sandbox/local-child-process-adapter.d.ts.map +1 -0
  48. package/dist/coding-tools/sandbox/local-child-process-adapter.js +141 -0
  49. package/dist/coding-tools/sandbox/local-child-process-adapter.js.map +1 -0
  50. package/dist/server/agent-engine-api-key-route.d.ts +37 -0
  51. package/dist/server/agent-engine-api-key-route.d.ts.map +1 -0
  52. package/dist/server/agent-engine-api-key-route.js +105 -0
  53. package/dist/server/agent-engine-api-key-route.js.map +1 -0
  54. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  55. package/dist/server/core-routes-plugin.js +10 -6
  56. package/dist/server/core-routes-plugin.js.map +1 -1
  57. package/dist/server/create-server.js +1 -1
  58. package/dist/server/create-server.js.map +1 -1
  59. package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +7 -4
  60. package/package.json +1 -1
  61. package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +7 -4
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Sandbox-adapter selection seam.
3
+ *
4
+ * `getSandboxAdapter()` resolves which backend the `run-code` tool executes in.
5
+ * By default it returns the local child-process adapter (preserving today's
6
+ * behavior). The active adapter can be overridden in two ways, both designed so
7
+ * a remote/durable backend can be plugged in later WITHOUT touching the agent
8
+ * loop or `run-code.ts`:
9
+ *
10
+ * 1. Programmatically, via `registerSandboxAdapter(adapter)` — e.g. a host
11
+ * process that wants every `run-code` call to run in a remote container.
12
+ * 2. By env var `AGENT_NATIVE_SANDBOX` for built-in adapters. Currently only
13
+ * `local` (the default) is wired; unknown values fall back to local.
14
+ *
15
+ * Resolution order: an explicitly registered adapter wins; otherwise the env
16
+ * var selects a built-in; otherwise the local adapter is used.
17
+ *
18
+ * // TODO: remote/durable adapter for long jobs. The local child-process
19
+ * // adapter is bounded by the hosting process — on the hosted platform that
20
+ * // means the agent loop's soft execution ceiling (~40s before timeout/
21
+ * // continuation thrash). A remote or durable adapter (Docker, a
22
+ * // Vercel-Sandbox-style runner, or a queued background worker) is the lever
23
+ * // to exceed that ceiling: it would implement `SandboxAdapter.run` against an
24
+ * // out-of-process runtime, tunnel the loopback bridge (or proxy bridge calls
25
+ * // back to the parent), and let large data jobs run to completion
26
+ * // independently of the request lifecycle. Register it here under a new
27
+ * // `AGENT_NATIVE_SANDBOX` value (e.g. `remote`) and via
28
+ * // `registerSandboxAdapter()`.
29
+ */
30
+ import type { SandboxAdapter } from "./adapter.js";
31
+ export type { SandboxAdapter, SandboxRunRequest, SandboxRunResult, SandboxEnv, } from "./adapter.js";
32
+ export { LocalChildProcessAdapter } from "./local-child-process-adapter.js";
33
+ /**
34
+ * Override the sandbox backend for all subsequent `run-code` invocations.
35
+ * Intended for hosts that want to plug in a Docker/remote/durable adapter. Pass
36
+ * `null` to clear the override and fall back to env-var / default resolution.
37
+ */
38
+ export declare function registerSandboxAdapter(adapter: SandboxAdapter | null): void;
39
+ /**
40
+ * Resolve the active sandbox adapter.
41
+ *
42
+ * Order: explicitly registered adapter → built-in selected by
43
+ * `AGENT_NATIVE_SANDBOX` → local child-process default.
44
+ */
45
+ export declare function getSandboxAdapter(): SandboxAdapter;
46
+ /**
47
+ * Reset selection state (registered override + cached default). Test-only helper
48
+ * so specs can exercise selection without leaking adapters across cases.
49
+ */
50
+ export declare function resetSandboxAdapterForTests(): void;
51
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/coding-tools/sandbox/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAGnD,YAAY,EACV,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,GACX,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAa5E;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,GAAG,IAAI,CAE3E;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,CAelD;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAGlD"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Sandbox-adapter selection seam.
3
+ *
4
+ * `getSandboxAdapter()` resolves which backend the `run-code` tool executes in.
5
+ * By default it returns the local child-process adapter (preserving today's
6
+ * behavior). The active adapter can be overridden in two ways, both designed so
7
+ * a remote/durable backend can be plugged in later WITHOUT touching the agent
8
+ * loop or `run-code.ts`:
9
+ *
10
+ * 1. Programmatically, via `registerSandboxAdapter(adapter)` — e.g. a host
11
+ * process that wants every `run-code` call to run in a remote container.
12
+ * 2. By env var `AGENT_NATIVE_SANDBOX` for built-in adapters. Currently only
13
+ * `local` (the default) is wired; unknown values fall back to local.
14
+ *
15
+ * Resolution order: an explicitly registered adapter wins; otherwise the env
16
+ * var selects a built-in; otherwise the local adapter is used.
17
+ *
18
+ * // TODO: remote/durable adapter for long jobs. The local child-process
19
+ * // adapter is bounded by the hosting process — on the hosted platform that
20
+ * // means the agent loop's soft execution ceiling (~40s before timeout/
21
+ * // continuation thrash). A remote or durable adapter (Docker, a
22
+ * // Vercel-Sandbox-style runner, or a queued background worker) is the lever
23
+ * // to exceed that ceiling: it would implement `SandboxAdapter.run` against an
24
+ * // out-of-process runtime, tunnel the loopback bridge (or proxy bridge calls
25
+ * // back to the parent), and let large data jobs run to completion
26
+ * // independently of the request lifecycle. Register it here under a new
27
+ * // `AGENT_NATIVE_SANDBOX` value (e.g. `remote`) and via
28
+ * // `registerSandboxAdapter()`.
29
+ */
30
+ import { LocalChildProcessAdapter } from "./local-child-process-adapter.js";
31
+ export { LocalChildProcessAdapter } from "./local-child-process-adapter.js";
32
+ /** Built-in adapter ids selectable via the `AGENT_NATIVE_SANDBOX` env var. */
33
+ const BUILT_IN_ADAPTERS = {
34
+ local: () => new LocalChildProcessAdapter(),
35
+ };
36
+ /** Lazily-constructed default (local) adapter, shared across calls. */
37
+ let defaultAdapter;
38
+ /** Explicitly registered adapter, if any. Takes precedence over the env var. */
39
+ let registeredAdapter;
40
+ /**
41
+ * Override the sandbox backend for all subsequent `run-code` invocations.
42
+ * Intended for hosts that want to plug in a Docker/remote/durable adapter. Pass
43
+ * `null` to clear the override and fall back to env-var / default resolution.
44
+ */
45
+ export function registerSandboxAdapter(adapter) {
46
+ registeredAdapter = adapter ?? undefined;
47
+ }
48
+ /**
49
+ * Resolve the active sandbox adapter.
50
+ *
51
+ * Order: explicitly registered adapter → built-in selected by
52
+ * `AGENT_NATIVE_SANDBOX` → local child-process default.
53
+ */
54
+ export function getSandboxAdapter() {
55
+ if (registeredAdapter)
56
+ return registeredAdapter;
57
+ const selected = (process.env.AGENT_NATIVE_SANDBOX ?? "")
58
+ .trim()
59
+ .toLowerCase();
60
+ if (selected && selected !== "local") {
61
+ const factory = BUILT_IN_ADAPTERS[selected];
62
+ if (factory)
63
+ return factory();
64
+ // Unknown value: fall through to the local default rather than failing the
65
+ // run. (A remote adapter is registered programmatically; see the TODO above.)
66
+ }
67
+ if (!defaultAdapter)
68
+ defaultAdapter = new LocalChildProcessAdapter();
69
+ return defaultAdapter;
70
+ }
71
+ /**
72
+ * Reset selection state (registered override + cached default). Test-only helper
73
+ * so specs can exercise selection without leaking adapters across cases.
74
+ */
75
+ export function resetSandboxAdapterForTests() {
76
+ registeredAdapter = undefined;
77
+ defaultAdapter = undefined;
78
+ }
79
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/coding-tools/sandbox/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAGH,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAQ5E,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAE5E,8EAA8E;AAC9E,MAAM,iBAAiB,GAAyC;IAC9D,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,wBAAwB,EAAE;CAC5C,CAAC;AAEF,uEAAuE;AACvE,IAAI,cAA0C,CAAC;AAE/C,gFAAgF;AAChF,IAAI,iBAA6C,CAAC;AAElD;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAA8B;IACnE,iBAAiB,GAAG,OAAO,IAAI,SAAS,CAAC;AAC3C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB;IAC/B,IAAI,iBAAiB;QAAE,OAAO,iBAAiB,CAAC;IAEhD,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,EAAE,CAAC;SACtD,IAAI,EAAE;SACN,WAAW,EAAE,CAAC;IACjB,IAAI,QAAQ,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACrC,MAAM,OAAO,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,OAAO;YAAE,OAAO,OAAO,EAAE,CAAC;QAC9B,2EAA2E;QAC3E,8EAA8E;IAChF,CAAC;IAED,IAAI,CAAC,cAAc;QAAE,cAAc,GAAG,IAAI,wBAAwB,EAAE,CAAC;IACrE,OAAO,cAAc,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,2BAA2B;IACzC,iBAAiB,GAAG,SAAS,CAAC;IAC9B,cAAc,GAAG,SAAS,CAAC;AAC7B,CAAC","sourcesContent":["/**\n * Sandbox-adapter selection seam.\n *\n * `getSandboxAdapter()` resolves which backend the `run-code` tool executes in.\n * By default it returns the local child-process adapter (preserving today's\n * behavior). The active adapter can be overridden in two ways, both designed so\n * a remote/durable backend can be plugged in later WITHOUT touching the agent\n * loop or `run-code.ts`:\n *\n * 1. Programmatically, via `registerSandboxAdapter(adapter)` — e.g. a host\n * process that wants every `run-code` call to run in a remote container.\n * 2. By env var `AGENT_NATIVE_SANDBOX` for built-in adapters. Currently only\n * `local` (the default) is wired; unknown values fall back to local.\n *\n * Resolution order: an explicitly registered adapter wins; otherwise the env\n * var selects a built-in; otherwise the local adapter is used.\n *\n * // TODO: remote/durable adapter for long jobs. The local child-process\n * // adapter is bounded by the hosting process — on the hosted platform that\n * // means the agent loop's soft execution ceiling (~40s before timeout/\n * // continuation thrash). A remote or durable adapter (Docker, a\n * // Vercel-Sandbox-style runner, or a queued background worker) is the lever\n * // to exceed that ceiling: it would implement `SandboxAdapter.run` against an\n * // out-of-process runtime, tunnel the loopback bridge (or proxy bridge calls\n * // back to the parent), and let large data jobs run to completion\n * // independently of the request lifecycle. Register it here under a new\n * // `AGENT_NATIVE_SANDBOX` value (e.g. `remote`) and via\n * // `registerSandboxAdapter()`.\n */\n\nimport type { SandboxAdapter } from \"./adapter.js\";\nimport { LocalChildProcessAdapter } from \"./local-child-process-adapter.js\";\n\nexport type {\n SandboxAdapter,\n SandboxRunRequest,\n SandboxRunResult,\n SandboxEnv,\n} from \"./adapter.js\";\nexport { LocalChildProcessAdapter } from \"./local-child-process-adapter.js\";\n\n/** Built-in adapter ids selectable via the `AGENT_NATIVE_SANDBOX` env var. */\nconst BUILT_IN_ADAPTERS: Record<string, () => SandboxAdapter> = {\n local: () => new LocalChildProcessAdapter(),\n};\n\n/** Lazily-constructed default (local) adapter, shared across calls. */\nlet defaultAdapter: SandboxAdapter | undefined;\n\n/** Explicitly registered adapter, if any. Takes precedence over the env var. */\nlet registeredAdapter: SandboxAdapter | undefined;\n\n/**\n * Override the sandbox backend for all subsequent `run-code` invocations.\n * Intended for hosts that want to plug in a Docker/remote/durable adapter. Pass\n * `null` to clear the override and fall back to env-var / default resolution.\n */\nexport function registerSandboxAdapter(adapter: SandboxAdapter | null): void {\n registeredAdapter = adapter ?? undefined;\n}\n\n/**\n * Resolve the active sandbox adapter.\n *\n * Order: explicitly registered adapter → built-in selected by\n * `AGENT_NATIVE_SANDBOX` → local child-process default.\n */\nexport function getSandboxAdapter(): SandboxAdapter {\n if (registeredAdapter) return registeredAdapter;\n\n const selected = (process.env.AGENT_NATIVE_SANDBOX ?? \"\")\n .trim()\n .toLowerCase();\n if (selected && selected !== \"local\") {\n const factory = BUILT_IN_ADAPTERS[selected];\n if (factory) return factory();\n // Unknown value: fall through to the local default rather than failing the\n // run. (A remote adapter is registered programmatically; see the TODO above.)\n }\n\n if (!defaultAdapter) defaultAdapter = new LocalChildProcessAdapter();\n return defaultAdapter;\n}\n\n/**\n * Reset selection state (registered override + cached default). Test-only helper\n * so specs can exercise selection without leaking adapters across cases.\n */\nexport function resetSandboxAdapterForTests(): void {\n registeredAdapter = undefined;\n defaultAdapter = undefined;\n}\n"]}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Default sandbox adapter: a local Node.js child process.
3
+ *
4
+ * This is the historical `run-code` execution path, extracted verbatim so the
5
+ * default runtime behavior is byte-for-byte equivalent to the previous inline
6
+ * implementation in `run-code.ts`:
7
+ * - The prepared module source is written to a fresh temp dir.
8
+ * - The child runs with the scrubbed env supplied by the parent (no secrets).
9
+ * - When the Node permission model is available (`--permission`, or
10
+ * `--experimental-permission` on Node 20), the child is denied filesystem
11
+ * access outside its own temp dir, child processes, workers, and native
12
+ * addons. Outbound network is NOT blocked by the permission model; the env
13
+ * scrub means such requests carry no credentials, and all authenticated calls
14
+ * go through the parent's loopback bridge.
15
+ * - A timeout sends SIGTERM, then SIGKILL after a 2 s grace period.
16
+ * - Temp files are cleaned up best-effort after the run.
17
+ */
18
+ import type { SandboxAdapter, SandboxRunRequest, SandboxRunResult } from "./adapter.js";
19
+ /** Sandbox adapter that runs code in a locked-down local Node child process. */
20
+ export declare class LocalChildProcessAdapter implements SandboxAdapter {
21
+ readonly id = "local-child-process";
22
+ run(request: SandboxRunRequest): Promise<SandboxRunResult>;
23
+ }
24
+ //# sourceMappingURL=local-child-process-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-child-process-adapter.d.ts","sourceRoot":"","sources":["../../../src/coding-tools/sandbox/local-child-process-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,cAAc,CAAC;AAmDtB,gFAAgF;AAChF,qBAAa,wBAAyB,YAAW,cAAc;IAC7D,QAAQ,CAAC,EAAE,yBAAyB;IAE9B,GAAG,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;CA6EjE"}
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Default sandbox adapter: a local Node.js child process.
3
+ *
4
+ * This is the historical `run-code` execution path, extracted verbatim so the
5
+ * default runtime behavior is byte-for-byte equivalent to the previous inline
6
+ * implementation in `run-code.ts`:
7
+ * - The prepared module source is written to a fresh temp dir.
8
+ * - The child runs with the scrubbed env supplied by the parent (no secrets).
9
+ * - When the Node permission model is available (`--permission`, or
10
+ * `--experimental-permission` on Node 20), the child is denied filesystem
11
+ * access outside its own temp dir, child processes, workers, and native
12
+ * addons. Outbound network is NOT blocked by the permission model; the env
13
+ * scrub means such requests carry no credentials, and all authenticated calls
14
+ * go through the parent's loopback bridge.
15
+ * - A timeout sends SIGTERM, then SIGKILL after a 2 s grace period.
16
+ * - Temp files are cleaned up best-effort after the run.
17
+ */
18
+ import fs from "node:fs";
19
+ import os from "node:os";
20
+ import path from "node:path";
21
+ import { spawn, spawnSync } from "node:child_process";
22
+ /** Grace period between SIGTERM and SIGKILL when a run times out. */
23
+ const SIGKILL_GRACE_MS = 2_000;
24
+ function sandboxReadAllowPaths(tmpDir) {
25
+ const paths = new Set([tmpDir]);
26
+ try {
27
+ paths.add(fs.realpathSync(tmpDir));
28
+ }
29
+ catch { }
30
+ return [...paths];
31
+ }
32
+ function sandboxWriteAllowPaths(tmpDir) {
33
+ const paths = new Set([tmpDir]);
34
+ try {
35
+ paths.add(fs.realpathSync(tmpDir));
36
+ }
37
+ catch { }
38
+ return [...paths];
39
+ }
40
+ /**
41
+ * Resolve the Node permission-model flag supported by the current runtime,
42
+ * probing once and caching. Returns null when the permission model is
43
+ * unavailable (the sandbox then falls back to env-scrub isolation only).
44
+ */
45
+ let cachedPermissionFlag;
46
+ function resolvePermissionFlag() {
47
+ if (cachedPermissionFlag !== undefined)
48
+ return cachedPermissionFlag;
49
+ for (const flag of ["--permission", "--experimental-permission"]) {
50
+ try {
51
+ const probe = spawnSync(process.execPath, [flag, "-e", "process.exit(0)"], {
52
+ timeout: 10_000,
53
+ stdio: "ignore",
54
+ });
55
+ if (probe.status === 0) {
56
+ cachedPermissionFlag = flag;
57
+ return flag;
58
+ }
59
+ }
60
+ catch {
61
+ // Probe failure means the flag is unsupported; try the next one.
62
+ }
63
+ }
64
+ cachedPermissionFlag = null;
65
+ return null;
66
+ }
67
+ /** Sandbox adapter that runs code in a locked-down local Node child process. */
68
+ export class LocalChildProcessAdapter {
69
+ id = "local-child-process";
70
+ async run(request) {
71
+ let tmpDir;
72
+ let tmpFile;
73
+ try {
74
+ // Write code to a temp ESM file (top-level await needs a module).
75
+ const tmpBaseDir = fs.realpathSync(os.tmpdir());
76
+ tmpDir = fs.mkdtempSync(path.join(tmpBaseDir, "agent-run-code-"));
77
+ tmpFile = path.join(tmpDir, "sandbox.mjs");
78
+ fs.writeFileSync(tmpFile, request.moduleSource, "utf8");
79
+ // The parent supplies an already-scrubbed env (no secrets). Point TMPDIR
80
+ // inside the sandbox dir so in-sandbox temp writes stay within the
81
+ // permission-model allow list.
82
+ const safeEnv = { ...request.env };
83
+ safeEnv.TMPDIR = tmpDir;
84
+ safeEnv.TEMP = tmpDir;
85
+ safeEnv.TMP = tmpDir;
86
+ // Lock the child down with the Node permission model when available:
87
+ // filesystem restricted to the sandbox temp dir, and child processes,
88
+ // workers, and native addons denied entirely.
89
+ const permissionFlag = resolvePermissionFlag();
90
+ const nodeArgs = permissionFlag
91
+ ? [
92
+ permissionFlag,
93
+ ...sandboxReadAllowPaths(tmpDir).map((allowedPath) => `--allow-fs-read=${allowedPath}`),
94
+ ...sandboxWriteAllowPaths(tmpDir).map((allowedPath) => `--allow-fs-write=${allowedPath}`),
95
+ tmpFile,
96
+ ]
97
+ : [tmpFile];
98
+ const child = spawn(process.execPath, nodeArgs, {
99
+ cwd: tmpDir,
100
+ env: safeEnv,
101
+ stdio: ["ignore", "pipe", "pipe"],
102
+ });
103
+ let stdout = "";
104
+ let stderr = "";
105
+ let timedOut = false;
106
+ const timer = setTimeout(() => {
107
+ timedOut = true;
108
+ child.kill("SIGTERM");
109
+ setTimeout(() => {
110
+ try {
111
+ child.kill("SIGKILL");
112
+ }
113
+ catch { }
114
+ }, SIGKILL_GRACE_MS);
115
+ }, request.timeoutMs);
116
+ child.stdout?.on("data", (chunk) => {
117
+ stdout += chunk.toString();
118
+ });
119
+ child.stderr?.on("data", (chunk) => {
120
+ stderr += chunk.toString();
121
+ });
122
+ const exitCode = await new Promise((resolve, reject) => {
123
+ child.once("error", reject);
124
+ child.once("exit", resolve);
125
+ });
126
+ clearTimeout(timer);
127
+ return { stdout, stderr, exitCode, timedOut };
128
+ }
129
+ finally {
130
+ // Clean up temp files (best-effort).
131
+ try {
132
+ if (tmpFile)
133
+ fs.rmSync(tmpFile, { force: true });
134
+ if (tmpDir)
135
+ fs.rmSync(tmpDir, { recursive: true, force: true });
136
+ }
137
+ catch { }
138
+ }
139
+ }
140
+ }
141
+ //# sourceMappingURL=local-child-process-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-child-process-adapter.js","sourceRoot":"","sources":["../../../src/coding-tools/sandbox/local-child-process-adapter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Default sandbox adapter: a local Node.js child process.\n *\n * This is the historical `run-code` execution path, extracted verbatim so the\n * default runtime behavior is byte-for-byte equivalent to the previous inline\n * implementation in `run-code.ts`:\n * - The prepared module source is written to a fresh temp dir.\n * - The child runs with the scrubbed env supplied by the parent (no secrets).\n * - When the Node permission model is available (`--permission`, or\n * `--experimental-permission` on Node 20), the child is denied filesystem\n * access outside its own temp dir, child processes, workers, and native\n * addons. Outbound network is NOT blocked by the permission model; the env\n * scrub means such requests carry no credentials, and all authenticated calls\n * go through the parent's loopback bridge.\n * - A timeout sends SIGTERM, then SIGKILL after a 2 s grace period.\n * - Temp files are cleaned up best-effort after the run.\n */\n\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { spawn, spawnSync } from \"node:child_process\";\n\nimport type {\n SandboxAdapter,\n SandboxRunRequest,\n SandboxRunResult,\n} from \"./adapter.js\";\n\n/** Grace period between SIGTERM and SIGKILL when a run times out. */\nconst SIGKILL_GRACE_MS = 2_000;\n\nfunction sandboxReadAllowPaths(tmpDir: string): string[] {\n const paths = new Set<string>([tmpDir]);\n try {\n paths.add(fs.realpathSync(tmpDir));\n } catch {}\n return [...paths];\n}\n\nfunction sandboxWriteAllowPaths(tmpDir: string): string[] {\n const paths = new Set<string>([tmpDir]);\n try {\n paths.add(fs.realpathSync(tmpDir));\n } catch {}\n return [...paths];\n}\n\n/**\n * Resolve the Node permission-model flag supported by the current runtime,\n * probing once and caching. Returns null when the permission model is\n * unavailable (the sandbox then falls back to env-scrub isolation only).\n */\nlet cachedPermissionFlag: string | null | undefined;\nfunction resolvePermissionFlag(): string | null {\n if (cachedPermissionFlag !== undefined) return cachedPermissionFlag;\n for (const flag of [\"--permission\", \"--experimental-permission\"]) {\n try {\n const probe = spawnSync(\n process.execPath,\n [flag, \"-e\", \"process.exit(0)\"],\n {\n timeout: 10_000,\n stdio: \"ignore\",\n },\n );\n if (probe.status === 0) {\n cachedPermissionFlag = flag;\n return flag;\n }\n } catch {\n // Probe failure means the flag is unsupported; try the next one.\n }\n }\n cachedPermissionFlag = null;\n return null;\n}\n\n/** Sandbox adapter that runs code in a locked-down local Node child process. */\nexport class LocalChildProcessAdapter implements SandboxAdapter {\n readonly id = \"local-child-process\";\n\n async run(request: SandboxRunRequest): Promise<SandboxRunResult> {\n let tmpDir: string | undefined;\n let tmpFile: string | undefined;\n try {\n // Write code to a temp ESM file (top-level await needs a module).\n const tmpBaseDir = fs.realpathSync(os.tmpdir());\n tmpDir = fs.mkdtempSync(path.join(tmpBaseDir, \"agent-run-code-\"));\n tmpFile = path.join(tmpDir, \"sandbox.mjs\");\n fs.writeFileSync(tmpFile, request.moduleSource, \"utf8\");\n\n // The parent supplies an already-scrubbed env (no secrets). Point TMPDIR\n // inside the sandbox dir so in-sandbox temp writes stay within the\n // permission-model allow list.\n const safeEnv: Record<string, string> = { ...request.env };\n safeEnv.TMPDIR = tmpDir;\n safeEnv.TEMP = tmpDir;\n safeEnv.TMP = tmpDir;\n\n // Lock the child down with the Node permission model when available:\n // filesystem restricted to the sandbox temp dir, and child processes,\n // workers, and native addons denied entirely.\n const permissionFlag = resolvePermissionFlag();\n const nodeArgs = permissionFlag\n ? [\n permissionFlag,\n ...sandboxReadAllowPaths(tmpDir).map(\n (allowedPath) => `--allow-fs-read=${allowedPath}`,\n ),\n ...sandboxWriteAllowPaths(tmpDir).map(\n (allowedPath) => `--allow-fs-write=${allowedPath}`,\n ),\n tmpFile,\n ]\n : [tmpFile];\n\n const child = spawn(process.execPath, nodeArgs, {\n cwd: tmpDir,\n env: safeEnv,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n let timedOut = false;\n\n const timer = setTimeout(() => {\n timedOut = true;\n child.kill(\"SIGTERM\");\n setTimeout(() => {\n try {\n child.kill(\"SIGKILL\");\n } catch {}\n }, SIGKILL_GRACE_MS);\n }, request.timeoutMs);\n\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n stdout += chunk.toString();\n });\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n stderr += chunk.toString();\n });\n\n const exitCode = await new Promise<number | null>((resolve, reject) => {\n child.once(\"error\", reject);\n child.once(\"exit\", resolve);\n });\n clearTimeout(timer);\n\n return { stdout, stderr, exitCode, timedOut };\n } finally {\n // Clean up temp files (best-effort).\n try {\n if (tmpFile) fs.rmSync(tmpFile, { force: true });\n if (tmpDir) fs.rmSync(tmpDir, { recursive: true, force: true });\n } catch {}\n }\n }\n}\n"]}
@@ -0,0 +1,37 @@
1
+ import { type H3Event } from "h3";
2
+ type AgentEngineApiKeyScope = "user" | "org";
3
+ export interface AgentEngineApiKeyWriteTarget {
4
+ scope: AgentEngineApiKeyScope;
5
+ scopeId: string;
6
+ }
7
+ export declare function normalizeAgentEngineApiKeyPayload(body: unknown): {
8
+ ok: true;
9
+ key: string;
10
+ value: string;
11
+ scope: AgentEngineApiKeyScope;
12
+ } | {
13
+ ok: false;
14
+ statusCode: number;
15
+ error: string;
16
+ };
17
+ export declare function resolveAgentEngineApiKeyWriteTarget(event: H3Event, scope: AgentEngineApiKeyScope): Promise<{
18
+ ok: true;
19
+ target: AgentEngineApiKeyWriteTarget;
20
+ } | {
21
+ ok: false;
22
+ statusCode: number;
23
+ error: string;
24
+ }>;
25
+ export declare function createAgentEngineApiKeyHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
26
+ error: any;
27
+ ok?: undefined;
28
+ key?: undefined;
29
+ scope?: undefined;
30
+ } | {
31
+ ok: boolean;
32
+ key: string;
33
+ scope: AgentEngineApiKeyScope;
34
+ error?: undefined;
35
+ }>>;
36
+ export {};
37
+ //# sourceMappingURL=agent-engine-api-key-route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-engine-api-key-route.d.ts","sourceRoot":"","sources":["../../src/server/agent-engine-api-key-route.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,OAAO,EACb,MAAM,IAAI,CAAC;AAeZ,KAAK,sBAAsB,GAAG,MAAM,GAAG,KAAK,CAAC;AAE7C,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,sBAAsB,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,OAAO,GAC3D;IACE,EAAE,EAAE,IAAI,CAAC;IACT,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,sBAAsB,CAAC;CAC/B,GACD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAgDnD;AAED,wBAAsB,mCAAmC,CACvD,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,sBAAsB,GAC5B,OAAO,CACN;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,4BAA4B,CAAA;CAAE,GAClD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CACnD,CA6BA;AAED,wBAAgB,8BAA8B;;;;;;;;;;IAqC7C"}
@@ -0,0 +1,105 @@
1
+ import { defineEventHandler, getMethod, setResponseStatus, } from "h3";
2
+ import { PROVIDER_ENV_META } from "../agent/engine/provider-env-vars.js";
3
+ import { getOrgContext } from "../org/context.js";
4
+ import { writeAppSecret } from "../secrets/storage.js";
5
+ import { getSession } from "./auth.js";
6
+ import { readBody } from "./h3-helpers.js";
7
+ const PROVIDER_TO_ENV_VAR = new Map(Object.entries(PROVIDER_ENV_META).map(([provider, meta]) => [
8
+ provider,
9
+ meta.envVar,
10
+ ]));
11
+ const PROVIDER_ENV_VAR_KEYS = new Set(PROVIDER_TO_ENV_VAR.values());
12
+ export function normalizeAgentEngineApiKeyPayload(body) {
13
+ const payload = body && typeof body === "object" ? body : {};
14
+ const raw = payload;
15
+ const key = typeof raw.key === "string"
16
+ ? raw.key.trim()
17
+ : typeof raw.provider === "string"
18
+ ? (PROVIDER_TO_ENV_VAR.get(raw.provider.trim()) ?? "")
19
+ : "";
20
+ if (!key || !PROVIDER_ENV_VAR_KEYS.has(key)) {
21
+ return {
22
+ ok: false,
23
+ statusCode: 400,
24
+ error: "Unsupported agent engine provider key.",
25
+ };
26
+ }
27
+ const value = typeof raw.value === "string"
28
+ ? raw.value.trim()
29
+ : typeof raw.apiKey === "string"
30
+ ? raw.apiKey.trim()
31
+ : "";
32
+ if (!value) {
33
+ return { ok: false, statusCode: 400, error: "value is required" };
34
+ }
35
+ if (raw.scope != null && raw.scope !== "user" && raw.scope !== "org") {
36
+ return {
37
+ ok: false,
38
+ statusCode: 400,
39
+ error: 'scope must be "user" or "org"',
40
+ };
41
+ }
42
+ return {
43
+ ok: true,
44
+ key,
45
+ value,
46
+ scope: raw.scope === "org" ? "org" : "user",
47
+ };
48
+ }
49
+ export async function resolveAgentEngineApiKeyWriteTarget(event, scope) {
50
+ const session = await getSession(event).catch(() => null);
51
+ if (!session?.email) {
52
+ return { ok: false, statusCode: 401, error: "Authentication required" };
53
+ }
54
+ if (scope === "user") {
55
+ return {
56
+ ok: true,
57
+ target: { scope: "user", scopeId: session.email },
58
+ };
59
+ }
60
+ const ctx = await getOrgContext(event).catch(() => null);
61
+ if (!ctx?.orgId) {
62
+ return { ok: false, statusCode: 400, error: "No active organization" };
63
+ }
64
+ if (ctx.role !== "owner" && ctx.role !== "admin") {
65
+ return {
66
+ ok: false,
67
+ statusCode: 403,
68
+ error: "Only organization owners and admins can set org-scoped keys",
69
+ };
70
+ }
71
+ return {
72
+ ok: true,
73
+ target: { scope: "org", scopeId: ctx.orgId },
74
+ };
75
+ }
76
+ export function createAgentEngineApiKeyHandler() {
77
+ return defineEventHandler(async (event) => {
78
+ if (getMethod(event) !== "POST") {
79
+ setResponseStatus(event, 405);
80
+ return { error: "Method not allowed" };
81
+ }
82
+ const payload = normalizeAgentEngineApiKeyPayload(await readBody(event).catch(() => ({})));
83
+ if (!payload.ok) {
84
+ setResponseStatus(event, payload.statusCode);
85
+ return { error: payload.error };
86
+ }
87
+ const resolved = await resolveAgentEngineApiKeyWriteTarget(event, payload.scope);
88
+ if (!resolved.ok) {
89
+ setResponseStatus(event, resolved.statusCode);
90
+ return { error: resolved.error };
91
+ }
92
+ await writeAppSecret({
93
+ key: payload.key,
94
+ value: payload.value,
95
+ scope: resolved.target.scope,
96
+ scopeId: resolved.target.scopeId,
97
+ });
98
+ return {
99
+ ok: true,
100
+ key: payload.key,
101
+ scope: resolved.target.scope,
102
+ };
103
+ });
104
+ }
105
+ //# sourceMappingURL=agent-engine-api-key-route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-engine-api-key-route.js","sourceRoot":"","sources":["../../src/server/agent-engine-api-key-route.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,SAAS,EACT,iBAAiB,GAElB,MAAM,IAAI,CAAC;AACZ,OAAO,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C,MAAM,mBAAmB,GAAG,IAAI,GAAG,CACjC,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;IAC1D,QAAQ;IACR,IAAI,CAAC,MAAM;CACZ,CAAC,CACH,CAAC;AACF,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC,mBAAmB,CAAC,MAAM,EAAE,CAAC,CAAC;AASpE,MAAM,UAAU,iCAAiC,CAAC,IAAa;IAQ7D,MAAM,OAAO,GAAG,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7D,MAAM,GAAG,GAAG,OAMX,CAAC;IAEF,MAAM,GAAG,GACP,OAAO,GAAG,CAAC,GAAG,KAAK,QAAQ;QACzB,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE;QAChB,CAAC,CAAC,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ;YAChC,CAAC,CAAC,CAAC,mBAAmB,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;YACtD,CAAC,CAAC,EAAE,CAAC;IACX,IAAI,CAAC,GAAG,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5C,OAAO;YACL,EAAE,EAAE,KAAK;YACT,UAAU,EAAE,GAAG;YACf,KAAK,EAAE,wCAAwC;SAChD,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GACT,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ;QAC3B,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE;QAClB,CAAC,CAAC,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ;YAC9B,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE;YACnB,CAAC,CAAC,EAAE,CAAC;IACX,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC;IACpE,CAAC;IAED,IAAI,GAAG,CAAC,KAAK,IAAI,IAAI,IAAI,GAAG,CAAC,KAAK,KAAK,MAAM,IAAI,GAAG,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;QACrE,OAAO;YACL,EAAE,EAAE,KAAK;YACT,UAAU,EAAE,GAAG;YACf,KAAK,EAAE,+BAA+B;SACvC,CAAC;IACJ,CAAC;IAED,OAAO;QACL,EAAE,EAAE,IAAI;QACR,GAAG;QACH,KAAK;QACL,KAAK,EAAE,GAAG,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM;KAC5C,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mCAAmC,CACvD,KAAc,EACd,KAA6B;IAK7B,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAC1D,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;QACpB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC;IAC1E,CAAC;IAED,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;QACrB,OAAO;YACL,EAAE,EAAE,IAAI;YACR,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,EAAE;SAClD,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACzD,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC;QAChB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,wBAAwB,EAAE,CAAC;IACzE,CAAC;IACD,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QACjD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,UAAU,EAAE,GAAG;YACf,KAAK,EAAE,6DAA6D;SACrE,CAAC;IACJ,CAAC;IAED,OAAO;QACL,EAAE,EAAE,IAAI;QACR,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,KAAK,EAAE;KAC7C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,8BAA8B;IAC5C,OAAO,kBAAkB,CAAC,KAAK,EAAE,KAAc,EAAE,EAAE;QACjD,IAAI,SAAS,CAAC,KAAK,CAAC,KAAK,MAAM,EAAE,CAAC;YAChC,iBAAiB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YAC9B,OAAO,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC;QACzC,CAAC;QAED,MAAM,OAAO,GAAG,iCAAiC,CAC/C,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CACxC,CAAC;QACF,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;YAChB,iBAAiB,CAAC,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;YAC7C,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;QAClC,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,mCAAmC,CACxD,KAAK,EACL,OAAO,CAAC,KAAK,CACd,CAAC;QACF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,iBAAiB,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;YAC9C,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;QACnC,CAAC;QAED,MAAM,cAAc,CAAC;YACnB,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK;YAC5B,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO;SACjC,CAAC,CAAC;QAEH,OAAO;YACL,EAAE,EAAE,IAAI;YACR,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK;SAC7B,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import {\n defineEventHandler,\n getMethod,\n setResponseStatus,\n type H3Event,\n} from \"h3\";\nimport { PROVIDER_ENV_META } from \"../agent/engine/provider-env-vars.js\";\nimport { getOrgContext } from \"../org/context.js\";\nimport { writeAppSecret } from \"../secrets/storage.js\";\nimport { getSession } from \"./auth.js\";\nimport { readBody } from \"./h3-helpers.js\";\n\nconst PROVIDER_TO_ENV_VAR = new Map(\n Object.entries(PROVIDER_ENV_META).map(([provider, meta]) => [\n provider,\n meta.envVar,\n ]),\n);\nconst PROVIDER_ENV_VAR_KEYS = new Set(PROVIDER_TO_ENV_VAR.values());\n\ntype AgentEngineApiKeyScope = \"user\" | \"org\";\n\nexport interface AgentEngineApiKeyWriteTarget {\n scope: AgentEngineApiKeyScope;\n scopeId: string;\n}\n\nexport function normalizeAgentEngineApiKeyPayload(body: unknown):\n | {\n ok: true;\n key: string;\n value: string;\n scope: AgentEngineApiKeyScope;\n }\n | { ok: false; statusCode: number; error: string } {\n const payload = body && typeof body === \"object\" ? body : {};\n const raw = payload as {\n key?: unknown;\n provider?: unknown;\n value?: unknown;\n apiKey?: unknown;\n scope?: unknown;\n };\n\n const key =\n typeof raw.key === \"string\"\n ? raw.key.trim()\n : typeof raw.provider === \"string\"\n ? (PROVIDER_TO_ENV_VAR.get(raw.provider.trim()) ?? \"\")\n : \"\";\n if (!key || !PROVIDER_ENV_VAR_KEYS.has(key)) {\n return {\n ok: false,\n statusCode: 400,\n error: \"Unsupported agent engine provider key.\",\n };\n }\n\n const value =\n typeof raw.value === \"string\"\n ? raw.value.trim()\n : typeof raw.apiKey === \"string\"\n ? raw.apiKey.trim()\n : \"\";\n if (!value) {\n return { ok: false, statusCode: 400, error: \"value is required\" };\n }\n\n if (raw.scope != null && raw.scope !== \"user\" && raw.scope !== \"org\") {\n return {\n ok: false,\n statusCode: 400,\n error: 'scope must be \"user\" or \"org\"',\n };\n }\n\n return {\n ok: true,\n key,\n value,\n scope: raw.scope === \"org\" ? \"org\" : \"user\",\n };\n}\n\nexport async function resolveAgentEngineApiKeyWriteTarget(\n event: H3Event,\n scope: AgentEngineApiKeyScope,\n): Promise<\n | { ok: true; target: AgentEngineApiKeyWriteTarget }\n | { ok: false; statusCode: number; error: string }\n> {\n const session = await getSession(event).catch(() => null);\n if (!session?.email) {\n return { ok: false, statusCode: 401, error: \"Authentication required\" };\n }\n\n if (scope === \"user\") {\n return {\n ok: true,\n target: { scope: \"user\", scopeId: session.email },\n };\n }\n\n const ctx = await getOrgContext(event).catch(() => null);\n if (!ctx?.orgId) {\n return { ok: false, statusCode: 400, error: \"No active organization\" };\n }\n if (ctx.role !== \"owner\" && ctx.role !== \"admin\") {\n return {\n ok: false,\n statusCode: 403,\n error: \"Only organization owners and admins can set org-scoped keys\",\n };\n }\n\n return {\n ok: true,\n target: { scope: \"org\", scopeId: ctx.orgId },\n };\n}\n\nexport function createAgentEngineApiKeyHandler() {\n return defineEventHandler(async (event: H3Event) => {\n if (getMethod(event) !== \"POST\") {\n setResponseStatus(event, 405);\n return { error: \"Method not allowed\" };\n }\n\n const payload = normalizeAgentEngineApiKeyPayload(\n await readBody(event).catch(() => ({})),\n );\n if (!payload.ok) {\n setResponseStatus(event, payload.statusCode);\n return { error: payload.error };\n }\n\n const resolved = await resolveAgentEngineApiKeyWriteTarget(\n event,\n payload.scope,\n );\n if (!resolved.ok) {\n setResponseStatus(event, resolved.statusCode);\n return { error: resolved.error };\n }\n\n await writeAppSecret({\n key: payload.key,\n value: payload.value,\n scope: resolved.target.scope,\n scopeId: resolved.target.scopeId,\n });\n\n return {\n ok: true,\n key: payload.key,\n scope: resolved.target.scope,\n };\n });\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"core-routes-plugin.d.ts","sourceRoot":"","sources":["../../src/server/core-routes-plugin.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAuBlC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AA+CvD,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAyDzD;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,mBAAmB,CAAC;AACvD,eAAO,MAAM,sBAAsB,0BAAqC,CAAC;AACzE,eAAO,MAAM,6BAA6B,+BAA0C,CAAC;AAErF,wBAAgB,yBAAyB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAQrE;AAmBD,eAAO,MAAM,kBAAkB,QAAwC,CAAC;AAsHxE,KAAK,6BAA6B,GAAG,CACnC,KAAK,EAAE,OAAO,KACX,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAE5C,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,wBAAsB,oCAAoC,CACxD,KAAK,EAAE,OAAO,EACd,OAAO,GAAE;IACP,cAAc,CAAC,EAAE,6BAA6B,CAAC;IAC/C,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CACjE,EACN,IAAI,CAAC,EAAE,SAAS,GAAG,UAAU,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAsD9B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb,MAAM,GAAG,IAAI,CASf;AAUD,KAAK,cAAc,GAAG,CAAC,QAAQ,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE9D,MAAM,WAAW,uBAAuB;IACtC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oDAAoD;IACpD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,2DAA2D;IAC3D,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uDAAuD;IACvD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,sEAAsE;IACtE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,sEAAsE;IACtE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,qEAAqE;IACrE,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,oDAAoD;IACpD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;wCACoC;IACpC,eAAe,CAAC,EAAE,OAAO,iBAAiB,EAAE,gBAAgB,CAAC,iBAAiB,CAAC,CAAC;IAChF,qEAAqE;IACrE,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB;;;;OAIG;IACH,cAAc,CAAC,EAAE,6BAA6B,CAAC;CAChD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,GAAE,uBAA4B,GACpC,cAAc,CA4hFhB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,EAAE,cAAyC,CAAC"}
1
+ {"version":3,"file":"core-routes-plugin.d.ts","sourceRoot":"","sources":["../../src/server/core-routes-plugin.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAuBlC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AA+CvD,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AA6DzD;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,mBAAmB,CAAC;AACvD,eAAO,MAAM,sBAAsB,0BAAqC,CAAC;AACzE,eAAO,MAAM,6BAA6B,+BAA0C,CAAC;AAErF,wBAAgB,yBAAyB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAQrE;AAmBD,eAAO,MAAM,kBAAkB,QAAwC,CAAC;AAsHxE,KAAK,6BAA6B,GAAG,CACnC,KAAK,EAAE,OAAO,KACX,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAE5C,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,wBAAsB,oCAAoC,CACxD,KAAK,EAAE,OAAO,EACd,OAAO,GAAE;IACP,cAAc,CAAC,EAAE,6BAA6B,CAAC;IAC/C,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CACjE,EACN,IAAI,CAAC,EAAE,SAAS,GAAG,UAAU,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAsD9B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb,MAAM,GAAG,IAAI,CASf;AAUD,KAAK,cAAc,GAAG,CAAC,QAAQ,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE9D,MAAM,WAAW,uBAAuB;IACtC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oDAAoD;IACpD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,2DAA2D;IAC3D,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uDAAuD;IACvD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,sEAAsE;IACtE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,sEAAsE;IACtE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,qEAAqE;IACrE,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,oDAAoD;IACpD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;wCACoC;IACpC,eAAe,CAAC,EAAE,OAAO,iBAAiB,EAAE,gBAAgB,CAAC,iBAAiB,CAAC,CAAC;IAChF,qEAAqE;IACrE,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB;;;;OAIG;IACH,cAAc,CAAC,EAAE,6BAA6B,CAAC;CAChD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,GAAE,uBAA4B,GACpC,cAAc,CAkiFhB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,EAAE,cAAyC,CAAC"}
@@ -40,7 +40,8 @@ import { runWithRequestContext } from "./request-context.js";
40
40
  import { createVoiceProvidersStatusHandler } from "./voice-providers-status.js";
41
41
  import { PROVIDER_ENV_META } from "../agent/engine/provider-env-vars.js";
42
42
  import { DEFAULT_MODEL } from "../agent/default-model.js";
43
- import { canUseDeployCredentialFallbackForRequest } from "./credential-provider.js";
43
+ import { canUseDeployCredentialFallbackForRequest, resolveSecret, } from "./credential-provider.js";
44
+ import { createAgentEngineApiKeyHandler } from "./agent-engine-api-key-route.js";
44
45
  import { canUpdateAgentLoopSettings, readAgentLoopSettings, resetAgentLoopSettings, validateMaxIterationsInput, writeAgentLoopSettings, } from "../agent/loop-settings.js";
45
46
  import { isAgentEngineSettingConfigured, getAgentEngineEntry, detectEngineFromEnv, detectEngineFromUserSecrets, isAgentEnginePackageInstalled, isStoredEngineUsableForRequest, } from "../agent/engine/registry.js";
46
47
  import { registerBuiltinEngines } from "../agent/engine/builtin.js";
@@ -1410,17 +1411,19 @@ export function createCoreRoutesPlugin(options = {}) {
1410
1411
  /* org module not present in this template */
1411
1412
  }
1412
1413
  }
1413
- const canUseDeployEnv = await runWithRequestContext({ userEmail, orgId }, () => canUseDeployCredentialFallbackForRequest());
1414
- return envKeys.map((cfg) => {
1414
+ return Promise.all(envKeys.map(async (cfg) => {
1415
1415
  const isProviderKey = PROVIDER_ENV_VAR_KEYS.has(cfg.key);
1416
+ const configured = isProviderKey
1417
+ ? await runWithRequestContext({ userEmail, orgId }, () => resolveSecret(cfg.key).then(Boolean))
1418
+ : !!process.env[cfg.key];
1416
1419
  return {
1417
1420
  key: cfg.key,
1418
1421
  label: cfg.label,
1419
1422
  required: cfg.required ?? false,
1420
- configured: !!process.env[cfg.key] && (!isProviderKey || canUseDeployEnv),
1423
+ configured,
1421
1424
  ...(cfg.helpText ? { helpText: cfg.helpText } : {}),
1422
1425
  };
1423
- });
1426
+ }));
1424
1427
  }));
1425
1428
  getH3App(nitroApp).use(`${P}/env-vars`, defineEventHandler(async (event) => {
1426
1429
  if (getMethod(event) !== "POST") {
@@ -1436,7 +1439,7 @@ export function createCoreRoutesPlugin(options = {}) {
1436
1439
  if (!isEnvVarWriteAllowed()) {
1437
1440
  setResponseStatus(event, 403);
1438
1441
  return {
1439
- error: "env-vars endpoint disabled on multi-tenant deployments. Use saveCredential(key, value, { userEmail, orgId, scope: 'org' }) to store per-org credentials.",
1442
+ error: "env-vars endpoint disabled on multi-tenant deployments. Use scoped secrets or credentials for user/org API keys.",
1440
1443
  };
1441
1444
  }
1442
1445
  const body = await readBody(event);
@@ -1503,6 +1506,7 @@ export function createCoreRoutesPlugin(options = {}) {
1503
1506
  return { saved: filtered.map((v) => v.key) };
1504
1507
  }));
1505
1508
  }
1509
+ getH3App(nitroApp).use(`${P}/agent-engine/api-key`, createAgentEngineApiKeyHandler());
1506
1510
  // GET /_agent-native/agent-engine/status — reports whether an engine
1507
1511
  // is configured (settings row, settings+env, or auto-detected from env).
1508
1512
  // The agent-chat UI uses this to skip the onboarding gate for providers