@agenthippo-ai/agenthippo 0.1.1 → 1.110.9
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/out/agentpack/index.js +2 -1
- package/out/agentpack/paths.js +23 -0
- package/out/analytics/dashboard/fleetDisplayUtils.js +3 -0
- package/out/analytics/dashboard/provider.js +13 -2
- package/out/analytics/dashboard/webview/panel.js +293 -82
- package/out/analytics/identity.js +0 -0
- package/out/analytics/service.js +23 -25
- package/out/analytics/sessionEvents.js +109 -22
- package/out/auth/authBaseUrl.js +59 -0
- package/out/auth/bootstrapApply.js +9 -10
- package/out/auth/brokerClient.js +49 -15
- package/out/auth/onboardingPrompt.js +112 -0
- package/out/auth/provider.js +9 -22
- package/out/auth/sessionStore.js +3 -40
- package/out/auth/signInFlow.js +2 -1
- package/out/auth-broker/config.js +54 -4
- package/out/auth-broker/server.js +152 -20
- package/out/auth-broker/store.js +25 -24
- package/out/authority/warrant.js +206 -0
- package/out/branding.js +27 -1
- package/out/cli/commands/ask.js +3 -0
- package/out/cli/commands/chat.js +3 -0
- package/out/cli/commands/conformance.js +249 -0
- package/out/cli/commands/data-plane.js +67 -0
- package/out/cli/commands/deploy.js +50 -10
- package/out/cli/commands/gateway.js +58 -0
- package/out/cli/commands/idp.js +85 -0
- package/out/cli/commands/pack.js +182 -3
- package/out/cli/commands/sandbox.js +332 -0
- package/out/cli/commands/serve.js +1165 -82
- package/out/cli/commands/store.js +12 -4
- package/out/cli/credential-context.js +42 -80
- package/out/cli/data-plane/broker.js +78 -0
- package/out/cli/data-plane/connector-host.js +121 -0
- package/out/cli/data-plane/egress.js +189 -0
- package/out/cli/data-plane/gateway.js +218 -0
- package/out/cli/data-plane/index.js +152 -0
- package/out/cli/data-plane/registry.js +89 -0
- package/out/cli/data-plane/remote.js +106 -0
- package/out/cli/data-plane/sidecar.js +161 -0
- package/out/cli/deploy/blueprints/registry.js +84 -0
- package/out/cli/deploy/layers/identity.js +172 -0
- package/out/cli/deploy-init.js +1182 -347
- package/out/cli/deps.js +106 -2
- package/out/cli/idp-init.js +338 -0
- package/out/cli/index.js +192 -23
- package/out/cli/pack-install-ref.js +15 -0
- package/out/cli/pack-loader.js +45 -3
- package/out/cli/pack-runtime.js +61 -0
- package/out/cli/runtime.js +44 -3
- package/out/cli/serve-hardening.js +139 -3
- package/out/cli/state-sync.js +16 -15
- package/out/cli/utils.js +12 -1
- package/out/engines/base.js +32 -3
- package/out/engines/claude.js +47 -8
- package/out/engines/codex.js +125 -22
- package/out/engines/custom.js +54 -12
- package/out/evidence/sessionShip.js +610 -0
- package/out/extension.js +13 -2
- package/out/gateway/bridge.js +40 -2
- package/out/gateway/client.js +34 -1
- package/out/gateway/installSource.js +68 -0
- package/out/gateway/lifecycle.js +185 -51
- package/out/gateway/outboundPaths.js +4 -1
- package/out/gateway/outboundSend.js +6 -6
- package/out/gateway/secretsPreflight.js +90 -0
- package/out/gateway/service.js +7 -2
- package/out/headless/index.js +40 -9
- package/out/live-e2e/mcp-demo/server.mjs +32 -0
- package/out/live-probe/connector.mjs +8 -0
- package/out/live-probe/env-report.mjs +8 -0
- package/out/mcp.js +115 -67
- package/out/pack/caps.js +271 -0
- package/out/pack/governor.js +309 -0
- package/out/pack/lease-schema.js +270 -0
- package/out/pack/lease.js +640 -0
- package/out/pack/reconcile.js +217 -0
- package/out/pack/signature.js +839 -0
- package/out/remote/handleRemoteChat.js +44 -3
- package/out/remote/remoteTrust.js +277 -0
- package/out/remote/serveClient.js +52 -5
- package/out/runner/host.js +131 -0
- package/out/runner/protocol.js +108 -0
- package/out/runner/remoteEngine.js +130 -0
- package/out/runtime/interfaces/edit-tracker.js +2 -0
- package/out/runtime/vscode/vscode-edit-tracker.js +2 -0
- package/out/sandbox/containerDriver.js +570 -0
- package/out/sandbox/processDriver.js +315 -0
- package/out/sandbox/sandboxRunner.js +191 -0
- package/out/sandbox/support.js +301 -0
- package/out/sandbox/types.js +55 -0
- package/out/scripts/agentide-litellm.sh +67 -5
- package/out/scripts/agentide_litellm_hooks_v1.py +182 -21
- package/out/scripts/analytics/agentide-litellm-otel.config.yaml +94 -118
- package/out/scripts/analytics/setup-analytics.ps1 +37 -13
- package/out/scripts/analytics/setup-analytics.sh +93 -13
- package/out/scripts/analytics/start-analytics.sh +86 -14
- package/out/scripts/gateway/setup-gateway.sh +72 -32
- package/out/shared/models.js +12 -111
- package/out/shared/providers.js +2 -2
- package/out/spotlight/install.js +7 -1
- package/out/store/index.js +41 -10
- package/out/sync.js +3 -1
- package/out/test/analytics.test.js +198 -34
- package/out/test/auth-baseurl.test.js +132 -0
- package/out/test/auth-bootstrapApply.test.js +27 -6
- package/out/test/auth-broker-hardening.test.js +227 -0
- package/out/test/auth-broker.test.js +112 -9
- package/out/test/blueprint-registry.test.js +105 -0
- package/out/test/branding.test.js +13 -0
- package/out/test/caps-governor.test.js +455 -0
- package/out/test/codex-new-file-survival.test.js +119 -0
- package/out/test/configuration-scopes.test.js +131 -0
- package/out/test/credential-context.test.js +23 -28
- package/out/test/custom-env-layering.test.js +151 -0
- package/out/test/data-plane-engine-wiring.test.js +250 -0
- package/out/test/data-plane.test.js +311 -0
- package/out/test/deploy-init.test.js +295 -4
- package/out/test/egress-broker.test.js +172 -0
- package/out/test/fleetDisplayUtils.test.js +4 -0
- package/out/test/gateway-client.test.js +28 -0
- package/out/test/gateway-install-source.test.js +111 -0
- package/out/test/gateway-lifecycle.test.js +1 -1
- package/out/test/gateway-outbound.test.js +7 -0
- package/out/test/gateway-secrets-preflight.test.js +127 -0
- package/out/test/gateway-session-routing.test.js +18 -0
- package/out/test/governor-hardening.test.js +217 -0
- package/out/test/headless-sync.test.js +22 -0
- package/out/test/idp-init.test.js +151 -0
- package/out/test/lease-signature.test.js +357 -0
- package/out/test/lease-trust-root.test.js +273 -0
- package/out/test/lease-validation.test.js +169 -0
- package/out/test/lease.test.js +114 -0
- package/out/test/mcp-obo.test.js +52 -27
- package/out/test/mcp.test.js +69 -3
- package/out/test/model-routing.test.js +133 -0
- package/out/test/pack-admission.test.js +216 -0
- package/out/test/pack-digest-parity.test.js +140 -0
- package/out/test/pack-reconcile.test.js +183 -0
- package/out/test/pack-signature-artifact.test.js +103 -0
- package/out/test/pack-signature.test.js +176 -0
- package/out/test/pack-trust-root.test.js +202 -0
- package/out/test/remote-trust.test.js +242 -0
- package/out/test/runner-protocol.test.js +249 -0
- package/out/test/sandbox-driver.test.js +553 -0
- package/out/test/sandbox-hardening.test.js +395 -0
- package/out/test/sandbox-runner.test.js +343 -0
- package/out/test/serve-gateway-turn.test.js +267 -0
- package/out/test/serve-hardening.test.js +125 -6
- package/out/test/serveClient.test.js +38 -5
- package/out/test/sessionShip.test.js +560 -0
- package/out/test/spotlightPublish.test.js +28 -0
- package/out/test/state-sync-peer.test.js +18 -5
- package/out/test/store-install-safe.test.js +69 -0
- package/out/test/tls-bypass.test.js +90 -0
- package/out/test/warrant.test.js +138 -0
- package/out/utils.js +73 -20
- package/package.json +1 -1
package/out/cli/index.js
CHANGED
|
@@ -75,8 +75,10 @@ const commander_1 = require("commander");
|
|
|
75
75
|
const chat_1 = require("./commands/chat");
|
|
76
76
|
const ask_1 = require("./commands/ask");
|
|
77
77
|
const serve_1 = require("./commands/serve");
|
|
78
|
+
const data_plane_1 = require("./commands/data-plane");
|
|
78
79
|
const auth_broker_1 = require("./commands/auth-broker");
|
|
79
80
|
const deploy_1 = require("./commands/deploy");
|
|
81
|
+
const idp_1 = require("./commands/idp");
|
|
80
82
|
const pack_1 = require("./commands/pack");
|
|
81
83
|
const store_1 = require("./commands/store");
|
|
82
84
|
const anywhere_1 = require("./commands/anywhere");
|
|
@@ -85,6 +87,26 @@ const probe_custom_engine_1 = require("./commands/probe-custom-engine");
|
|
|
85
87
|
const deps_1 = require("./deps");
|
|
86
88
|
const branding_1 = require("../branding");
|
|
87
89
|
const agentEnvFile_1 = require("../utils/agentEnvFile");
|
|
90
|
+
const caps_1 = require("../pack/caps");
|
|
91
|
+
/**
|
|
92
|
+
* `--max-concurrent-sandboxes`, honouring the deprecated `--max-concurrent-cells`.
|
|
93
|
+
*
|
|
94
|
+
* The current spelling wins outright when both are given; the old one is honoured with a loud
|
|
95
|
+
* notice rather than dropped, because dropping it would silently REMOVE a blast-radius cap from a
|
|
96
|
+
* deployment that believes it still has one.
|
|
97
|
+
*/
|
|
98
|
+
function resolveMaxConcurrentSandboxes(options) {
|
|
99
|
+
const current = parseInt(options.maxConcurrentSandboxes ?? '', 10);
|
|
100
|
+
if (Number.isFinite(current) && current > 0) {
|
|
101
|
+
return current;
|
|
102
|
+
}
|
|
103
|
+
const legacy = parseInt(options.maxConcurrentCells ?? '', 10);
|
|
104
|
+
if (Number.isFinite(legacy) && legacy > 0) {
|
|
105
|
+
console.warn(`Warning: --max-concurrent-cells is DEPRECATED; use ${caps_1.DEPRECATED_CAP_SPELLINGS['--max-concurrent-cells']}. Honouring it for now.`);
|
|
106
|
+
return legacy;
|
|
107
|
+
}
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
88
110
|
const VERSION = '1.110.8';
|
|
89
111
|
const CLI_DESCRIPTION = [
|
|
90
112
|
`${branding_1.AGENTIDE_BRAND_NAME} CLI - Run agent packs from the command line`,
|
|
@@ -101,14 +123,22 @@ const CLI_DESCRIPTION = [
|
|
|
101
123
|
' AGENTHIPPO_STORE_REPO Override store GitHub repo (owner/repo; CLI store commands)',
|
|
102
124
|
'',
|
|
103
125
|
'Quickstart:',
|
|
104
|
-
` AGENTHIPPO_LITELLM_BASE_URL="http://localhost:4000" AGENTHIPPO_LITELLM_API_KEY="dummy" AGENTHIPPO_DEFAULT_MODEL="litellm/gpt-5.
|
|
126
|
+
` AGENTHIPPO_LITELLM_BASE_URL="http://localhost:4000" AGENTHIPPO_LITELLM_API_KEY="dummy" AGENTHIPPO_DEFAULT_MODEL="litellm/gpt-5.5" AGENTHIPPO_DEFAULT_ENGINE="codex" ${branding_1.AGENTIDE_CLI_NAME} ask "Reply with exactly: CLI_TEST_OK"`,
|
|
105
127
|
'',
|
|
106
128
|
'Agent Packs:',
|
|
107
129
|
` Loaded from ~/${branding_1.AGENTIDE_DIR_NAME}/agents/<name>/ or absolute/relative paths.`,
|
|
108
130
|
' Pack permissions control filesystem access scope.',
|
|
109
131
|
].join('\n');
|
|
110
132
|
const program = new commander_1.Command();
|
|
111
|
-
program.hook('preAction', async () => {
|
|
133
|
+
program.hook('preAction', async (_thisCommand, actionCommand) => {
|
|
134
|
+
// A SANDBOX must never absorb ambient credentials. Every other command merges ~/.agent-hippo/.env
|
|
135
|
+
// as a convenience, but doing that inside a sandbox would silently hand the sandboxed engine
|
|
136
|
+
// every key on the host — defeating the driver's curated env allowlist (observed live: the
|
|
137
|
+
// cursor engine picked up CURSOR_API_KEY this way). A sandbox receives only what its driver
|
|
138
|
+
// explicitly passes.
|
|
139
|
+
if (actionCommand?.name() === 'sandbox') {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
112
142
|
await (0, agentEnvFile_1.mergeAgentHomeEnvIntoProcessEnv)();
|
|
113
143
|
});
|
|
114
144
|
program
|
|
@@ -125,7 +155,7 @@ program
|
|
|
125
155
|
.description('Start interactive chat session')
|
|
126
156
|
.option('-a, --agent <name>', 'Agent pack to use')
|
|
127
157
|
.option('-e, --engine <id>', 'Override pack engine: claude|codex|<custom-engine-id>')
|
|
128
|
-
.option('-m, --model <id>', 'Override pack model (e.g. litellm/gpt-
|
|
158
|
+
.option('-m, --model <id>', 'Override pack model (e.g. litellm/gpt-5.6-luna)')
|
|
129
159
|
.option('-w, --workspace <path>', 'Workspace directory', process.cwd())
|
|
130
160
|
.action(async (options) => {
|
|
131
161
|
const verbose = program.opts().verbose;
|
|
@@ -144,7 +174,7 @@ program
|
|
|
144
174
|
.description('Send a single message and exit')
|
|
145
175
|
.option('-a, --agent <name>', 'Agent pack to use')
|
|
146
176
|
.option('-e, --engine <id>', 'Override pack engine: claude|codex|<custom-engine-id>')
|
|
147
|
-
.option('-m, --model <id>', 'Override pack model (e.g. litellm/gpt-
|
|
177
|
+
.option('-m, --model <id>', 'Override pack model (e.g. litellm/gpt-5.6-luna)')
|
|
148
178
|
.option('-w, --workspace <path>', 'Workspace directory', process.cwd())
|
|
149
179
|
.option('-f, --file <path>', 'Include file content in context')
|
|
150
180
|
.option('--json', 'Output as JSON')
|
|
@@ -175,7 +205,7 @@ program
|
|
|
175
205
|
.requiredOption('--prompt <message>', 'Prompt text')
|
|
176
206
|
.option('--pack <name>', 'Agent pack to use')
|
|
177
207
|
.option('-e, --engine <id>', 'Override pack engine: claude|codex|<custom-engine-id>')
|
|
178
|
-
.option('-m, --model <id>', 'Override pack model (e.g. litellm/gpt-
|
|
208
|
+
.option('-m, --model <id>', 'Override pack model (e.g. litellm/gpt-5.6-luna)')
|
|
179
209
|
.option('-w, --workspace <path>', 'Workspace directory', process.cwd())
|
|
180
210
|
.option('--session-id <id>', 'Optional session id for the caller')
|
|
181
211
|
.option('--system-prompt <text>', 'Optional system prompt override')
|
|
@@ -203,7 +233,7 @@ program
|
|
|
203
233
|
.description('Start HTTP server')
|
|
204
234
|
.requiredOption('-a, --agent <name>', 'Agent pack to use (required)')
|
|
205
235
|
.option('-e, --engine <id>', 'Override pack engine: claude|codex|<custom-engine-id>')
|
|
206
|
-
.option('-m, --model <id>', 'Override pack model (e.g. litellm/gpt-
|
|
236
|
+
.option('-m, --model <id>', 'Override pack model (e.g. litellm/gpt-5.6-luna)')
|
|
207
237
|
.option('-p, --port <port>', 'Port to listen on', '3000')
|
|
208
238
|
.option('--host <address>', 'Address to bind the HTTP server (default 127.0.0.1; use 0.0.0.0 in containers/behind a proxy)')
|
|
209
239
|
.option('-w, --workspace <path>', 'Default workspace directory', process.cwd())
|
|
@@ -212,18 +242,52 @@ program
|
|
|
212
242
|
.option('--state-dir <path>', 'Persist session state here (honors AGENTHIPPO_HOME; replaces the tmpdir default)')
|
|
213
243
|
.option('--max-turns-per-session <n>', 'Cap turns per session (abuse/runaway guard); 0 = unlimited', '0')
|
|
214
244
|
.option('--www-dir <path>', 'Serve deploy/www/index.html at GET / (static browser chat UI)')
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
.option('--
|
|
219
|
-
.option('--
|
|
245
|
+
.option('--sandbox <mode>', 'Run each session\'s engine in a sandbox: process (tier 0, no boundary) or container (tier 2, one disposable container per session with only its declared routes). Unset = in-process (default, unchanged)')
|
|
246
|
+
.option('--sandbox-image <ref>', '--sandbox container: sandbox image pinned BY DIGEST (image@sha256:...)')
|
|
247
|
+
.option('--pull-packs', 'Fetch the version the lease pins from the store instead of only refusing others. Always verifies the signature; a fetch or verification failure keeps serving the current version')
|
|
248
|
+
.option('--require-signed-pack', 'Refuse to start a sandbox unless the pack is signed by a trusted key (the lease may also require this; neither side can waive the other). Now the DEFAULT — kept so existing invocations still mean something')
|
|
249
|
+
.option('--allow-unsigned-pack', 'LOCAL DEVELOPMENT ONLY: run a pack this instance cannot verify. Signature checking is required by default; this disables it and warns loudly on every boot. A lease that sets requireSignedPack still overrides it')
|
|
250
|
+
.option('--require-signed-lease', 'Refuse to act on a lease that is not signed (ES256 compact JWS) by a trusted key. Tighten-only with lease.requireSignedLease, and sticky once a signed lease has been seen')
|
|
251
|
+
.option('--lease-max-staleness <seconds>', 'Refuse turns after this long with no successful lease refresh, so network denial cannot hold a revoked fleet open (default 86400; 0 disables)')
|
|
252
|
+
.option('--sandbox-ttl <seconds>', '--sandbox: reap an idle sandbox after N seconds; the next message recreates it and resumes the session', '1800')
|
|
253
|
+
.option('--shared-workspace', '--sandbox container: mount ONE workspace into every session\'s sandbox (default: one per session). Sessions can then read and overwrite each other\'s files — only for a single trust domain')
|
|
254
|
+
// Control plane (serve-v4-clean §5). Caps are enforced by revoking a sandbox's routes, so they
|
|
255
|
+
// need --sandbox; a lease may impose tighter values than these, and never looser ones.
|
|
256
|
+
.option('--max-session-seconds <seconds>', '--sandbox: cap one session\'s wall-clock lifetime; a breach revokes or destroys its sandbox')
|
|
257
|
+
.option('--max-concurrent-sandboxes <n>', '--sandbox: cap live sandboxes on this instance (blast radius)')
|
|
258
|
+
// Deprecated pre-rename spelling. Hidden so it does not advertise itself, honoured so an
|
|
259
|
+
// existing deployment does not silently lose its cap, and loud so it gets migrated.
|
|
260
|
+
.addOption(new commander_1.Option('--max-concurrent-cells <n>', 'DEPRECATED alias for --max-concurrent-sandboxes').hideHelp())
|
|
261
|
+
.option('--cap-on-breach <action>', '--sandbox: what a cap breach does — revoke-inference (default) or destroy-sandbox. An unrecognised value fails CLOSED to destroy-sandbox')
|
|
262
|
+
.option('--lease-poll <seconds>', '--sandbox: how often to re-check the lease, so a kill switch stops RUNNING sandboxes. Worst-case kill latency is 2x this (poll + cache); an admin refresh is immediate', '30')
|
|
220
263
|
.action(async (options) => {
|
|
221
|
-
if (options.production || options.corsOrigin) {
|
|
222
|
-
console.warn('[serve] --production/--cors-origin are deprecated no-ops; auth and CORS are enforced by the deploy edge proxy.');
|
|
223
|
-
}
|
|
224
264
|
const verbose = program.opts().verbose;
|
|
225
265
|
await (0, deps_1.ensureDeps)(verbose);
|
|
226
266
|
await (0, serve_1.serveCommand)({
|
|
267
|
+
sandbox: options.sandbox === 'container' ? 'container' : options.sandbox === 'process' ? 'process' : undefined,
|
|
268
|
+
sandboxImage: options.sandboxImage,
|
|
269
|
+
requireSignedPack: Boolean(options.requireSignedPack),
|
|
270
|
+
allowUnsignedPack: Boolean(options.allowUnsignedPack),
|
|
271
|
+
requireSignedLease: Boolean(options.requireSignedLease),
|
|
272
|
+
leaseMaxStalenessSeconds: options.leaseMaxStaleness !== undefined
|
|
273
|
+
? (Number.isFinite(Number(options.leaseMaxStaleness)) ? Number(options.leaseMaxStaleness) : undefined)
|
|
274
|
+
: undefined,
|
|
275
|
+
pullPacks: Boolean(options.pullPacks),
|
|
276
|
+
sandboxTtlSeconds: parseInt(options.sandboxTtl, 10) || undefined,
|
|
277
|
+
sharedWorkspace: Boolean(options.sharedWorkspace),
|
|
278
|
+
maxSessionSeconds: parseInt(options.maxSessionSeconds, 10) || undefined,
|
|
279
|
+
maxConcurrentSandboxes: resolveMaxConcurrentSandboxes(options),
|
|
280
|
+
// Shared with the env path (pack/caps.ts) so a value that is valid in one is valid in the
|
|
281
|
+
// other. An unrecognised action no longer becomes `undefined` — which mergeCaps then
|
|
282
|
+
// defaulted to the WEAKER revoke-inference, silently downgrading what the operator typed.
|
|
283
|
+
capOnBreach: (() => {
|
|
284
|
+
const r = (0, caps_1.resolveOnBreach)(options.capOnBreach, '--cap-on-breach');
|
|
285
|
+
for (const w of r.warnings) {
|
|
286
|
+
console.warn(`Warning: ${w}`);
|
|
287
|
+
}
|
|
288
|
+
return r.action;
|
|
289
|
+
})(),
|
|
290
|
+
leasePollSeconds: parseInt(options.leasePoll, 10) || undefined,
|
|
227
291
|
agent: options.agent,
|
|
228
292
|
engine: options.engine,
|
|
229
293
|
model: options.model,
|
|
@@ -238,6 +302,68 @@ program
|
|
|
238
302
|
verbose,
|
|
239
303
|
});
|
|
240
304
|
});
|
|
305
|
+
// Conformance — the deployment proves its own claims (serve-v4-clean.md §11 step 5).
|
|
306
|
+
program
|
|
307
|
+
.command('conformance')
|
|
308
|
+
.description('Probe a deployment against its own claims: credential confinement, isolation tier, cost-cap binding, warrant integrity, rollback/kill switch. Exit 1 on any failure, 2 when no deployment was probed (--sandbox/--tier omitted) — only exit 0 means a real deployment was measured.')
|
|
309
|
+
.option('--sandbox <id>', 'Running sandbox container id/name — required to probe INSIDE a sandbox')
|
|
310
|
+
.option('--tier <n>', 'Declared isolation tier (0-3); probed, not trusted')
|
|
311
|
+
.option('--runtime <bin>', 'Container runtime binary', 'docker')
|
|
312
|
+
.option('--json', 'Emit JSON')
|
|
313
|
+
.action(async (options) => {
|
|
314
|
+
const { conformanceCommand } = await import('./commands/conformance.js');
|
|
315
|
+
const code = await conformanceCommand({
|
|
316
|
+
sandbox: options.sandbox,
|
|
317
|
+
tier: options.tier === undefined ? undefined : parseInt(options.tier, 10),
|
|
318
|
+
runtime: options.runtime,
|
|
319
|
+
json: Boolean(options.json),
|
|
320
|
+
});
|
|
321
|
+
process.exitCode = code;
|
|
322
|
+
});
|
|
323
|
+
// Sandbox runner — runs INSIDE a sandbox; holds no credential (serve-v4-clean.md §3).
|
|
324
|
+
program
|
|
325
|
+
.command('sandbox')
|
|
326
|
+
.description('Run the in-sandbox engine host (sandbox runner). Speaks the frame protocol to serve over a unix socket or stdio; holds no data or model credential.')
|
|
327
|
+
.requiredOption('-a, --agent <path>', 'Pack directory mounted into the sandbox')
|
|
328
|
+
.option('--socket <path>', 'Unix socket to listen on (container driver)')
|
|
329
|
+
.option('--stdio', 'Speak the protocol on stdin/stdout instead (process driver)')
|
|
330
|
+
// No default: it must fall back to <state-dir>/workspace inside the command, or a
|
|
331
|
+
// host-launched sandbox (where --state-dir is a real host path) inherits the container-image
|
|
332
|
+
// path and dies on mkdir.
|
|
333
|
+
.option('-w, --workspace <path>', 'Workspace directory (default: <state-dir>/workspace)')
|
|
334
|
+
.option('--state-dir <path>', 'State directory', '/state')
|
|
335
|
+
.option('--sandbox-id <id>', 'Sandbox id (joins evidence records)')
|
|
336
|
+
.action(async (options) => {
|
|
337
|
+
const { sandboxCommand } = await import('./commands/sandbox.js');
|
|
338
|
+
await sandboxCommand({
|
|
339
|
+
agent: options.agent,
|
|
340
|
+
socket: options.socket,
|
|
341
|
+
stdio: Boolean(options.stdio),
|
|
342
|
+
workspace: options.workspace,
|
|
343
|
+
stateDir: options.stateDir,
|
|
344
|
+
sandboxId: options.sandboxId,
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
program
|
|
348
|
+
.command('data-plane')
|
|
349
|
+
.description('Run the connector as a sidecar (holds the backend base credential; the agent container does not)')
|
|
350
|
+
.requiredOption('-a, --agent <name>', 'Agent pack (reads spec.dataPlane connector + provider)')
|
|
351
|
+
.option('-w, --workspace <path>', 'Workspace directory', process.cwd())
|
|
352
|
+
.option('--gateway-port <port>', 'Port the engine dials (nonce-auth MCP)', '9090')
|
|
353
|
+
.option('--control-port <port>', 'Port serve dials to register turns (bearer-auth)', '9091')
|
|
354
|
+
.option('--host <address>', 'Bind address (default 0.0.0.0 for the pod network)')
|
|
355
|
+
.action(async (options) => {
|
|
356
|
+
const verbose = program.opts().verbose;
|
|
357
|
+
await (0, deps_1.ensureDeps)(verbose);
|
|
358
|
+
await (0, data_plane_1.dataPlaneSidecarCommand)({
|
|
359
|
+
agent: options.agent,
|
|
360
|
+
workspace: options.workspace,
|
|
361
|
+
gatewayPort: parseInt(options.gatewayPort, 10),
|
|
362
|
+
controlPort: parseInt(options.controlPort, 10),
|
|
363
|
+
host: options.host,
|
|
364
|
+
verbose,
|
|
365
|
+
});
|
|
366
|
+
});
|
|
241
367
|
program
|
|
242
368
|
.command('auth-broker')
|
|
243
369
|
.description('Start the AgentHippo auth broker (OAuth + IDE bootstrap API)')
|
|
@@ -247,22 +373,30 @@ program
|
|
|
247
373
|
// Deploy command - generate platform-native deploy artifacts
|
|
248
374
|
program
|
|
249
375
|
.command('deploy <action> [pack]')
|
|
250
|
-
.description('Generate a self-contained deploy staging dir (init). Targets: vm, compose, cloudrun, databricks-app, spcs, cloudflare, all')
|
|
376
|
+
.description('Generate a self-contained deploy staging dir (init). Targets: vm, compose, cloudrun, databricks-app, spcs, cloudflare, aws-ecs, azure-container-apps, all. Or pick a --blueprint instead of raw --target/--ingress.')
|
|
251
377
|
.option('-t, --target <target>', 'Deploy target', 'vm')
|
|
252
|
-
.option('
|
|
378
|
+
.option('--blueprint <id>', 'Named preset (sandbox, prospect-demo, team-pilot, regulated-air-gapped, enterprise-fleet) — resolves --target/--ingress for you (PLAN/deploy.md §3); overrides both when set')
|
|
379
|
+
.option('--platform <platform>', 'Which platform to use within --blueprint\'s supported set (defaults to the blueprint\'s first); ignored without --blueprint')
|
|
380
|
+
.option('-o, --out <path>', 'Staging directory (default: ./deploy-out/<pack>-<target> relative to cwd; run from the pack parent, not from inside the pack)')
|
|
253
381
|
.option('--packdir <path>', 'Pack directory (defaults to [pack] arg or cwd)')
|
|
254
382
|
.option('--image <ref>', 'Runtime image ref (default: engine runtime:latest, or :latest-cursor for cursor packs)')
|
|
255
383
|
.option('-p, --port <port>', 'Container port', '8080')
|
|
256
384
|
.option('--gateway <channels>', 'Agent Anywhere: bind channels (e.g. "slack") + add OpenClaw gateway service (compose target)')
|
|
257
385
|
.option('--hostname <host>', 'Public hostname for SSO edge (Caddy, OAuth, tunnel)', 'auth.agenthippo.ai')
|
|
258
|
-
.option('--ingress <mode>', 'Cloud Run
|
|
386
|
+
.option('--ingress <mode>', 'iap (Cloud Run built-in IAP only), edge (self-issued AgentHippo edge + Google OAuth — vm/compose/cloudrun/aws-ecs/azure-container-apps), or edge-fed (federated: auth via the central IdP, no per-target OAuth client — same targets as edge)', 'iap')
|
|
259
387
|
.option('--idp-url <url>', 'edge-fed: central IdP base URL browsers redirect to for login (e.g. https://auth.agenthippo.ai)', 'https://auth.agenthippo.ai')
|
|
260
388
|
.option('--cookie-domain <domain>', 'edge-fed: parent domain the shared auth cookie is scoped to (e.g. .agenthippo.ai)', '.agenthippo.ai')
|
|
389
|
+
.option('--colocate', 'edge-fed + vm/compose only: ride the org IdP\'s already-running Caddy + auth-broker instead of shipping this agent\'s own (PLAN/deploy.md §4 state 4)')
|
|
390
|
+
.option('--edge-turn-connector <addr>', 'EDGE turn registration (P1 fix): connector sidecar control-API address (e.g. mypack-connector:9091); the generated route registers each chat turn via forward_auth /edge/turn and copies X-Agent-Turn-Nonce — the agent container never holds the register token')
|
|
391
|
+
.option('--model-routing <mode>', 'Cloud Run + edge-fed: sidecar (in-pod LiteLLM+OTEL, default) or gateway (agent → gateway.agenthippo.ai directly, lean)')
|
|
261
392
|
.option('--force', 'Overwrite existing files')
|
|
262
393
|
.action(async (action, pack, options) => {
|
|
263
394
|
const ingress = options.ingress === 'edge' ? 'edge' : options.ingress === 'edge-fed' ? 'edge-fed' : 'iap';
|
|
395
|
+
const modelRouting = options.modelRouting === 'gateway' ? 'gateway' : options.modelRouting === 'sidecar' ? 'sidecar' : undefined;
|
|
264
396
|
await (0, deploy_1.deployCommand)(action, pack, {
|
|
265
397
|
target: options.target,
|
|
398
|
+
blueprint: options.blueprint,
|
|
399
|
+
platform: options.platform,
|
|
266
400
|
out: options.out,
|
|
267
401
|
image: options.image,
|
|
268
402
|
gateway: options.gateway,
|
|
@@ -273,21 +407,52 @@ program
|
|
|
273
407
|
ingress,
|
|
274
408
|
idpUrl: options.idpUrl,
|
|
275
409
|
cookieDomain: options.cookieDomain,
|
|
410
|
+
colocate: Boolean(options.colocate),
|
|
411
|
+
modelRouting,
|
|
412
|
+
edgeTurnConnector: options.edgeTurnConnector,
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
// IdP command - generate the central IdP stack (org identity infra, set up once)
|
|
416
|
+
program
|
|
417
|
+
.command('idp <action>')
|
|
418
|
+
.description('Generate the central IdP stack (init) — Caddy + oauth2-proxy + auth-broker. One per organization; agents federate against it with `deploy init --ingress edge-fed`.')
|
|
419
|
+
.option('--hostname <host>', 'Public hostname the IdP is reachable at (e.g. auth.corp.example) — required')
|
|
420
|
+
.option('--cookie-domain <domain>', 'Parent domain the shared auth cookie is scoped to (e.g. .corp.example); defaults to the hostname\'s own domain')
|
|
421
|
+
.option('-o, --out <path>', 'Output directory (default: ./enterprise-deploy/idp)')
|
|
422
|
+
.option('--force', 'Overwrite existing files')
|
|
423
|
+
.action(async (action, options) => {
|
|
424
|
+
await (0, idp_1.idpCommand)(action, {
|
|
425
|
+
hostname: options.hostname,
|
|
426
|
+
cookieDomain: options.cookieDomain,
|
|
427
|
+
out: options.out,
|
|
428
|
+
force: Boolean(options.force),
|
|
276
429
|
});
|
|
277
430
|
});
|
|
278
431
|
// Pack command - pack management
|
|
279
432
|
program
|
|
280
433
|
.command('pack <action>')
|
|
281
|
-
.description('Manage agent packs (list, validate, install)')
|
|
434
|
+
.description('Manage agent packs (list, validate, sign, verify, digest, keygen, install)')
|
|
282
435
|
.argument('[name]', 'Pack name or path')
|
|
283
|
-
.option('-o, --output <path>', 'Output path for export')
|
|
436
|
+
.option('-o, --output <path>', 'Output path for export, or for a generated signing key')
|
|
437
|
+
.option('--key <path>', 'pack sign: PEM file holding the ES256 private key')
|
|
438
|
+
.option('--kid <id>', 'pack sign/keygen: key id published in JWKS so verifiers can find the public half')
|
|
439
|
+
.option('--keys <path>', 'pack verify: JSON file of kid -> public PEM')
|
|
440
|
+
.option('--jwks <url>', 'pack verify: JWKS endpoint to fetch public halves from')
|
|
441
|
+
.option('--to <dir>', 'pack install: destination folder (flat layout for serve/deploy)')
|
|
442
|
+
.option('-f, --force', 'pack install: overwrite an existing folder')
|
|
284
443
|
.option('-w, --workspace <path>', 'Workspace directory for local packs', process.cwd())
|
|
285
444
|
.action(async (action, name, options) => {
|
|
286
445
|
await (0, pack_1.packCommand)({
|
|
287
446
|
action,
|
|
288
447
|
name,
|
|
289
448
|
output: options.output,
|
|
449
|
+
to: options.to,
|
|
450
|
+
force: options.force,
|
|
290
451
|
workspace: options.workspace,
|
|
452
|
+
key: options.key,
|
|
453
|
+
kid: options.kid,
|
|
454
|
+
keys: options.keys,
|
|
455
|
+
jwks: options.jwks,
|
|
291
456
|
verbose: program.opts().verbose,
|
|
292
457
|
});
|
|
293
458
|
});
|
|
@@ -326,8 +491,12 @@ const gatewayCmd = program.command('gateway').description('Gateway outbound mess
|
|
|
326
491
|
gatewayCmd
|
|
327
492
|
.command('send')
|
|
328
493
|
.description('Send a proactive message via the gateway (WebSocket RPC; hippoclaw CLI fallback)')
|
|
329
|
-
|
|
330
|
-
|
|
494
|
+
// --outbound/--agent are optional: under `serve` with the egress broker active
|
|
495
|
+
// (AGENTHIPPO_EGRESS_URL + AGENTHIPPO_TURN_NONCE set), the broker resolves the destination
|
|
496
|
+
// server-side from the turn nonce and neither is needed (secure-obo-design §7.6). Off-serve
|
|
497
|
+
// the direct path still requires both (validated in gatewaySendCommand).
|
|
498
|
+
.option('--outbound <path>', 'Session gateway-outbound.json (see AGENTHIPPO_GATEWAY_OUTBOUND_FILE)')
|
|
499
|
+
.option('-a, --agent <id>', 'Agent pack id')
|
|
331
500
|
.requiredOption('-m, --message <text>', 'Message to send')
|
|
332
501
|
.option('--media <path-or-url>', 'Attach media (image/audio/video/document); local path or URL. Sent via gateway RPC so the bot token resolves from the gateway runtime snapshot.')
|
|
333
502
|
.option('-t, --target <target>', 'Explicit target (e.g. channel:C01234567); defaults to last inbound route')
|
|
@@ -336,8 +505,8 @@ gatewayCmd
|
|
|
336
505
|
.option('--json', 'Output handle + result as JSON')
|
|
337
506
|
.action(async (options) => {
|
|
338
507
|
await (0, gateway_1.gatewaySendCommand)({
|
|
339
|
-
outbound: options.outbound,
|
|
340
|
-
agent: options.agent,
|
|
508
|
+
outbound: options.outbound ?? '',
|
|
509
|
+
agent: options.agent ?? '',
|
|
341
510
|
message: options.message,
|
|
342
511
|
media: options.media,
|
|
343
512
|
target: options.target,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*---------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) AgentHippo.ai. All rights reserved.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parsePackInstallRef = parsePackInstallRef;
|
|
7
|
+
/** Parse `slug` or `slug@version` from `pack install` positional arg. */
|
|
8
|
+
function parsePackInstallRef(name) {
|
|
9
|
+
const trimmed = name.trim();
|
|
10
|
+
const at = trimmed.lastIndexOf('@');
|
|
11
|
+
if (at > 0) {
|
|
12
|
+
return { slug: trimmed.slice(0, at), version: trimmed.slice(at + 1) || undefined };
|
|
13
|
+
}
|
|
14
|
+
return { slug: trimmed };
|
|
15
|
+
}
|
package/out/cli/pack-loader.js
CHANGED
|
@@ -42,6 +42,7 @@ exports.loadAgentPack = loadAgentPack;
|
|
|
42
42
|
exports.getDefaultConfig = getDefaultConfig;
|
|
43
43
|
exports.getEnvDefaultModel = getEnvDefaultModel;
|
|
44
44
|
exports.getApiConfig = getApiConfig;
|
|
45
|
+
exports.hasCredentialForEngine = hasCredentialForEngine;
|
|
45
46
|
exports.getFileAccess = getFileAccess;
|
|
46
47
|
/**
|
|
47
48
|
* Agent Pack Loader
|
|
@@ -330,6 +331,14 @@ function validateManifest(manifest, sourcePath) {
|
|
|
330
331
|
if (manifest.spec?.cheapModel !== undefined && typeof manifest.spec.cheapModel !== 'string') {
|
|
331
332
|
errors.push('Invalid spec.cheapModel: must be a string when provided');
|
|
332
333
|
}
|
|
334
|
+
if (manifest.spec?.dataPlane !== undefined) {
|
|
335
|
+
if (typeof manifest.spec.dataPlane !== 'object' || !manifest.spec.dataPlane.connector?.trim()) {
|
|
336
|
+
errors.push('Invalid spec.dataPlane: requires a `connector` path (stdio MCP module run broker-side)');
|
|
337
|
+
}
|
|
338
|
+
if (manifest.spec.dataPlane?.provider !== undefined && typeof manifest.spec.dataPlane.provider !== 'string') {
|
|
339
|
+
errors.push('Invalid spec.dataPlane.provider: must be a string when provided');
|
|
340
|
+
}
|
|
341
|
+
}
|
|
333
342
|
if (errors.length > 0) {
|
|
334
343
|
throw new Error(`Invalid agent manifest at ${sourcePath}:\n - ${errors.join('\n - ')}`);
|
|
335
344
|
}
|
|
@@ -354,20 +363,53 @@ function getEnvDefaultModel() {
|
|
|
354
363
|
}
|
|
355
364
|
/**
|
|
356
365
|
* Get API configuration from environment.
|
|
366
|
+
*
|
|
367
|
+
* Returns the credentials keyed by destination (litellm / openai / anthropic) rather than a
|
|
368
|
+
* single collapsed key. The engine selects the one matching the endpoint it dials — see
|
|
369
|
+
* resolveApiRouting — so a codex deployment can no longer send an Anthropic key to OpenAI.
|
|
357
370
|
*/
|
|
358
371
|
function getApiConfig() {
|
|
359
372
|
const litellmApiKey = getEnvVar('AGENTHIPPO_LITELLM_API_KEY', 'AGENTIDE_LITELLM_API_KEY');
|
|
360
373
|
const litellmBaseUrl = getEnvVar('AGENTHIPPO_LITELLM_BASE_URL', 'AGENTIDE_LITELLM_BASE_URL');
|
|
361
374
|
const anthropicApiKey = process.env.ANTHROPIC_API_KEY || process.env.CLAUDE_API_KEY;
|
|
362
375
|
const openaiApiKey = process.env.OPENAI_API_KEY;
|
|
363
|
-
|
|
364
|
-
|
|
376
|
+
// The URL alone defines the trust domain. When a LiteLLM/gateway URL is configured, ALL
|
|
377
|
+
// engine traffic routes there — so it must authenticate with the LiteLLM key (or
|
|
378
|
+
// resolveApiRouting's 'sk-dummy' fallback), NEVER a provider secret, and must carry the
|
|
379
|
+
// analytics metadata headers. Requiring key+URL here once created a silent half-state:
|
|
380
|
+
// URL-only routed every call to the gateway with the REAL provider key as auth and no
|
|
381
|
+
// metadata at all (verified live 2026-07-27 with a header-capture stub). A missing key now
|
|
382
|
+
// yields a clean 401 against a strict gateway instead of a leaked credential.
|
|
383
|
+
const useLiteLLM = Boolean(litellmBaseUrl);
|
|
365
384
|
return {
|
|
366
|
-
|
|
385
|
+
// Kept only for the "is any credential present at all?" boot check; routing never uses it.
|
|
386
|
+
apiKey: litellmApiKey || anthropicApiKey || openaiApiKey,
|
|
367
387
|
baseUrl: litellmBaseUrl,
|
|
368
388
|
useLiteLLM,
|
|
389
|
+
litellmApiKey,
|
|
390
|
+
openaiApiKey,
|
|
391
|
+
anthropicApiKey,
|
|
369
392
|
};
|
|
370
393
|
}
|
|
394
|
+
/**
|
|
395
|
+
* Does the environment carry a usable credential for `engine`? Engine-aware so a codex
|
|
396
|
+
* deployment with only an Anthropic key (or vice-versa) fails loudly at boot instead of
|
|
397
|
+
* 401-ing at the first model call. A configured LiteLLM proxy satisfies every engine.
|
|
398
|
+
*/
|
|
399
|
+
function hasCredentialForEngine(engine, cfg = getApiConfig()) {
|
|
400
|
+
if (cfg.useLiteLLM) {
|
|
401
|
+
return true;
|
|
402
|
+
}
|
|
403
|
+
const e = engine.trim().toLowerCase();
|
|
404
|
+
if (e === 'claude') {
|
|
405
|
+
return Boolean(cfg.anthropicApiKey);
|
|
406
|
+
}
|
|
407
|
+
if (e === 'codex') {
|
|
408
|
+
return Boolean(cfg.openaiApiKey);
|
|
409
|
+
}
|
|
410
|
+
// Custom engines can authenticate via their own manifest-declared env var — don't gate them here.
|
|
411
|
+
return true;
|
|
412
|
+
}
|
|
371
413
|
/**
|
|
372
414
|
* Get file access mode from pack manifest.
|
|
373
415
|
*/
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*---------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) AgentHippo.ai. All rights reserved.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createPackRunner = createPackRunner;
|
|
7
|
+
/**
|
|
8
|
+
* One way to stand up a `HeadlessRunner` for an agent pack.
|
|
9
|
+
*
|
|
10
|
+
* `serve` and `sandbox` run the SAME engines against the SAME pack; the only difference is where
|
|
11
|
+
* the process sits. Every time the sandbox re-derived a piece of that setup itself it drifted from
|
|
12
|
+
* serve and broke live (the hand-rolled agent.yaml parser, the engine executable paths, the
|
|
13
|
+
* skipped `ensureDeps`). So the wiring lives here once and both call it: engine discovery,
|
|
14
|
+
* bundled prompts, the child env patch, and the credential shape.
|
|
15
|
+
*
|
|
16
|
+
* What is NOT here is deliberately not shared: credentials are the caller's business. serve
|
|
17
|
+
* holds provider keys; a sandbox only ever gets a per-sandbox, budget-capped model token that arrives
|
|
18
|
+
* on the run frame. Both express that through `credentials`, and nothing in this module can
|
|
19
|
+
* widen it.
|
|
20
|
+
*/
|
|
21
|
+
const headless_1 = require("../headless");
|
|
22
|
+
const runtime_1 = require("./runtime");
|
|
23
|
+
const utils_1 = require("./utils");
|
|
24
|
+
/**
|
|
25
|
+
* Build the runner. Engine executables are resolved here rather than by the SDKs themselves:
|
|
26
|
+
* the engines ship WITH agenthippo, and a runtime image does not carry the SDK's own bundled
|
|
27
|
+
* cli.js — without these paths every turn inside a sandbox fails with "executable not found".
|
|
28
|
+
*/
|
|
29
|
+
function createPackRunner(options) {
|
|
30
|
+
return (0, headless_1.createHeadlessRunner)({
|
|
31
|
+
storagePath: options.storagePath,
|
|
32
|
+
analyticsSessionsDir: options.analyticsSessionsDir,
|
|
33
|
+
packId: options.packId,
|
|
34
|
+
packVersion: options.packVersion,
|
|
35
|
+
provenance: options.provenance,
|
|
36
|
+
bundledPromptsDir: (0, utils_1.findBundledBasePromptsDir)(),
|
|
37
|
+
apiKey: options.credentials.apiKey,
|
|
38
|
+
baseUrl: options.credentials.baseUrl,
|
|
39
|
+
useLiteLLM: options.credentials.useLiteLLM,
|
|
40
|
+
litellmApiKey: options.credentials.litellmApiKey,
|
|
41
|
+
openaiApiKey: options.credentials.openaiApiKey,
|
|
42
|
+
anthropicApiKey: options.credentials.anthropicApiKey,
|
|
43
|
+
logger: options.logger,
|
|
44
|
+
freshState: options.freshState ?? false,
|
|
45
|
+
claudeCodeExecutablePath: (0, utils_1.findClaudeCodePath)(),
|
|
46
|
+
codexExecutablePath: (0, utils_1.findCodexPath)(),
|
|
47
|
+
permissionHandler: options.permissionHandler,
|
|
48
|
+
// Gives engine children AGENTHIPPO_WORKSPACE_ROOT / AGENTHIPPO_ACTIVE_AGENT and puts the
|
|
49
|
+
// agenthippo binary on their PATH — without it a pack skill that shells out to the CLI
|
|
50
|
+
// cannot find it, which is exactly the kind of silent difference a sandbox must not have.
|
|
51
|
+
cliEnvPatch: (0, runtime_1.buildCliEnvPatch)({
|
|
52
|
+
cliPath: process.execPath,
|
|
53
|
+
workspaceRoot: options.workspaceRoot,
|
|
54
|
+
agentId: options.packId,
|
|
55
|
+
chatSessionId: options.chatSessionId,
|
|
56
|
+
baseEnv: process.env,
|
|
57
|
+
omitGatewayToken: options.omitGatewayToken,
|
|
58
|
+
}),
|
|
59
|
+
...(options.claudeCheapModel ? { engineSettings: { claudeCheapModel: options.claudeCheapModel } } : {}),
|
|
60
|
+
});
|
|
61
|
+
}
|
package/out/cli/runtime.js
CHANGED
|
@@ -42,6 +42,8 @@ exports.getBundledToolsHealth = getBundledToolsHealth;
|
|
|
42
42
|
exports.getCliHealth = getCliHealth;
|
|
43
43
|
exports.buildSessionEnvPatch = buildSessionEnvPatch;
|
|
44
44
|
exports.buildCliEnvPatch = buildCliEnvPatch;
|
|
45
|
+
exports.readProvenanceEnvFromProcess = readProvenanceEnvFromProcess;
|
|
46
|
+
exports.buildProvenanceEnv = buildProvenanceEnv;
|
|
45
47
|
exports.runCli = runCli;
|
|
46
48
|
const fs = __importStar(require("fs"));
|
|
47
49
|
const os = __importStar(require("os"));
|
|
@@ -315,9 +317,16 @@ function buildCliEnvPatch(args) {
|
|
|
315
317
|
chatSessionId: args.chatSessionId,
|
|
316
318
|
}),
|
|
317
319
|
};
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
320
|
+
// When serve runs the egress broker (secure-obo-design §7.6), the gateway token must NOT
|
|
321
|
+
// reach the agent's trust domain: the broker holds it, the agent presents only the turn
|
|
322
|
+
// nonce. AGENTHIPPO_EGRESS_URL in the process env is that signal — with it set, never bake
|
|
323
|
+
// the token into a child env (this closes §7.4 gap 1 for custom engines; codex/claude are
|
|
324
|
+
// covered by serve stripping the token from its own process.env).
|
|
325
|
+
if (!args.omitGatewayToken && !process.env.AGENTHIPPO_EGRESS_URL?.trim()) {
|
|
326
|
+
const gatewayToken = readOpenClawGatewayToken(args.baseEnv);
|
|
327
|
+
if (gatewayToken) {
|
|
328
|
+
env[exports.OPENCLAW_GATEWAY_TOKEN_ENV] = gatewayToken;
|
|
329
|
+
}
|
|
321
330
|
}
|
|
322
331
|
// Claude SDK refuses nested sessions when CLAUDECODE is present.
|
|
323
332
|
// Force-unset in child runtime for CLI ask/chat/serve subprocesses.
|
|
@@ -361,6 +370,38 @@ function buildCliEnvPatch(args) {
|
|
|
361
370
|
}
|
|
362
371
|
return env;
|
|
363
372
|
}
|
|
373
|
+
/** Read provenance stamp env vars injected into sandbox children by serve. */
|
|
374
|
+
function readProvenanceEnvFromProcess() {
|
|
375
|
+
const pick = (key) => process.env[key]?.trim() || undefined;
|
|
376
|
+
const tierRaw = pick('AGENTIDE_ANALYTICS_TIER');
|
|
377
|
+
const tier = tierRaw !== undefined ? Number(tierRaw) : undefined;
|
|
378
|
+
return {
|
|
379
|
+
policyVersion: pick('AGENTIDE_ANALYTICS_POLICY_VERSION'),
|
|
380
|
+
packDigest: pick('AGENTIDE_ANALYTICS_PACK_DIGEST'),
|
|
381
|
+
engineVersion: pick('AGENTIDE_ANALYTICS_ENGINE_VERSION'),
|
|
382
|
+
tier: tier !== undefined && Number.isFinite(tier) ? tier : undefined,
|
|
383
|
+
instanceId: pick('AGENTIDE_ANALYTICS_INSTANCE_ID'),
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
function buildProvenanceEnv(stamp) {
|
|
387
|
+
const env = {};
|
|
388
|
+
if (stamp.policyVersion?.trim()) {
|
|
389
|
+
env.AGENTIDE_ANALYTICS_POLICY_VERSION = stamp.policyVersion.trim();
|
|
390
|
+
}
|
|
391
|
+
if (stamp.packDigest?.trim()) {
|
|
392
|
+
env.AGENTIDE_ANALYTICS_PACK_DIGEST = stamp.packDigest.trim();
|
|
393
|
+
}
|
|
394
|
+
if (stamp.engineVersion?.trim()) {
|
|
395
|
+
env.AGENTIDE_ANALYTICS_ENGINE_VERSION = stamp.engineVersion.trim();
|
|
396
|
+
}
|
|
397
|
+
if (stamp.tier !== undefined) {
|
|
398
|
+
env.AGENTIDE_ANALYTICS_TIER = String(stamp.tier);
|
|
399
|
+
}
|
|
400
|
+
if (stamp.instanceId?.trim()) {
|
|
401
|
+
env.AGENTIDE_ANALYTICS_INSTANCE_ID = stamp.instanceId.trim();
|
|
402
|
+
}
|
|
403
|
+
return env;
|
|
404
|
+
}
|
|
364
405
|
async function runCli(binaryPath, args, opts) {
|
|
365
406
|
return await new Promise((resolve, reject) => {
|
|
366
407
|
const child = (0, child_process_1.spawn)(binaryPath, args, {
|