@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
|
@@ -48,9 +48,10 @@ const fs = __importStar(require("fs"));
|
|
|
48
48
|
const path = __importStar(require("path"));
|
|
49
49
|
const yaml = __importStar(require("js-yaml"));
|
|
50
50
|
const deploy_init_1 = require("../deploy-init");
|
|
51
|
+
const registry_1 = require("../deploy/blueprints/registry");
|
|
51
52
|
const utils_1 = require("../utils");
|
|
52
53
|
const DEFAULT_PORT = 8080;
|
|
53
|
-
/** Read pack id + engine + model from agent.yaml without the full loader (no workspace/sync needed). */
|
|
54
|
+
/** Read pack id + version + engine + model from agent.yaml without the full loader (no workspace/sync needed). */
|
|
54
55
|
function readManifest(packDir) {
|
|
55
56
|
const manifestPath = path.join(packDir, 'agent.yaml');
|
|
56
57
|
if (!fs.existsSync(manifestPath)) {
|
|
@@ -61,9 +62,14 @@ function readManifest(packDir) {
|
|
|
61
62
|
if (!name) {
|
|
62
63
|
throw new Error(`agent.yaml is missing metadata.name in ${packDir}`);
|
|
63
64
|
}
|
|
65
|
+
const version = parsed?.metadata?.version?.trim();
|
|
66
|
+
if (!version) {
|
|
67
|
+
throw new Error(`agent.yaml is missing metadata.version in ${packDir}`);
|
|
68
|
+
}
|
|
64
69
|
const model = parsed?.spec?.model?.trim();
|
|
65
70
|
return {
|
|
66
71
|
name,
|
|
72
|
+
version,
|
|
67
73
|
engine: parsed?.spec?.engine?.trim() || 'claude',
|
|
68
74
|
...(model ? { model } : {}),
|
|
69
75
|
};
|
|
@@ -123,7 +129,7 @@ function copyPackToStaging(packDir, stagingDir) {
|
|
|
123
129
|
return false;
|
|
124
130
|
if (base.endsWith('.log'))
|
|
125
131
|
return false;
|
|
126
|
-
if (
|
|
132
|
+
if ((0, deploy_init_1.isSecretLikeBasename)(base))
|
|
127
133
|
return false;
|
|
128
134
|
return true;
|
|
129
135
|
},
|
|
@@ -135,8 +141,35 @@ async function deployCommand(action, packArg, options) {
|
|
|
135
141
|
console.error(`Unknown deploy action "${action}". Supported: init`);
|
|
136
142
|
process.exit(1);
|
|
137
143
|
}
|
|
144
|
+
// Blueprint resolution (PLAN/deploy.md §3) picks --target/--ingress for you; --platform
|
|
145
|
+
// selects which platform within the blueprint's supported set. Raw --target/--ingress stay
|
|
146
|
+
// fully usable when --blueprint is omitted — nothing here changes that path.
|
|
147
|
+
let resolvedTarget = options.target;
|
|
148
|
+
let resolvedIngress = options.ingress;
|
|
149
|
+
if (options.blueprint) {
|
|
150
|
+
if (!(0, registry_1.isBlueprintId)(options.blueprint)) {
|
|
151
|
+
console.error(`Unknown --blueprint "${options.blueprint}". Supported: ${Object.keys(registry_1.BLUEPRINTS).join(', ')}`);
|
|
152
|
+
process.exit(1);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (options.platform && !(0, deploy_init_1.isDeployTarget)(options.platform)) {
|
|
156
|
+
console.error(`Unknown --platform "${options.platform}". Supported: ${deploy_init_1.DEPLOY_TARGETS.join(', ')}`);
|
|
157
|
+
process.exit(1);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
const resolved = (0, registry_1.resolveBlueprint)(options.blueprint, options.platform);
|
|
162
|
+
resolvedTarget = resolved.target;
|
|
163
|
+
resolvedIngress = resolved.ingress;
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
console.error(`Error: ${err instanceof Error ? err.message : err}`);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
138
171
|
const packDir = path.resolve(options.packdir || packArg || process.cwd());
|
|
139
|
-
const targetsRequested =
|
|
172
|
+
const targetsRequested = resolvedTarget === 'all' ? [...deploy_init_1.DEPLOY_TARGETS] : [resolvedTarget];
|
|
140
173
|
for (const t of targetsRequested) {
|
|
141
174
|
if (!(0, deploy_init_1.isDeployTarget)(t)) {
|
|
142
175
|
console.error(`Unknown --target "${t}". Supported: ${deploy_init_1.DEPLOY_TARGETS.join(', ')}, all`);
|
|
@@ -162,8 +195,14 @@ async function deployCommand(action, packArg, options) {
|
|
|
162
195
|
}
|
|
163
196
|
}
|
|
164
197
|
}
|
|
198
|
+
const modelRouting = options.modelRouting === 'gateway' ? 'gateway' : options.modelRouting === 'sidecar' ? 'sidecar' : undefined;
|
|
199
|
+
if (options.modelRouting && options.modelRouting !== 'gateway' && options.modelRouting !== 'sidecar') {
|
|
200
|
+
console.error(`Unknown --model-routing "${options.modelRouting}". Supported: sidecar, gateway`);
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
165
203
|
const ctx = {
|
|
166
204
|
packName: manifest.name,
|
|
205
|
+
packVersion: manifest.version,
|
|
167
206
|
image: options.image || (0, deploy_init_1.defaultRuntimeImage)(manifest.engine),
|
|
168
207
|
port: options.port || DEFAULT_PORT,
|
|
169
208
|
engine: manifest.engine,
|
|
@@ -171,18 +210,18 @@ async function deployCommand(action, packArg, options) {
|
|
|
171
210
|
envVars,
|
|
172
211
|
gatewayChannels: gateway,
|
|
173
212
|
publicHostname: (0, deploy_init_1.resolveDeployPublicHostname)(options.hostname),
|
|
174
|
-
ingress:
|
|
213
|
+
ingress: resolvedIngress,
|
|
175
214
|
idpUrl: options.idpUrl,
|
|
176
215
|
cookieDomain: options.cookieDomain,
|
|
216
|
+
colocate: options.colocate,
|
|
217
|
+
modelRouting,
|
|
218
|
+
edgeTurnConnector: options.edgeTurnConnector?.trim() || undefined,
|
|
177
219
|
};
|
|
178
220
|
for (const target of targetsRequested) {
|
|
179
|
-
|
|
180
|
-
// ./deploy-out/<pack>-<target> (cwd-relative); --out overrides (per-target subdir if
|
|
181
|
-
// multiple targets). Build context = staging root, so COPY . /app/pack/ is correct
|
|
182
|
-
// regardless of where the script is invoked from.
|
|
221
|
+
const routingSuffix = modelRouting === 'gateway' ? '-gateway' : '';
|
|
183
222
|
const outDir = options.out
|
|
184
|
-
? (targetsRequested.length > 1 ? path.join(path.resolve(options.out), target) : path.resolve(options.out))
|
|
185
|
-
: path.join(process.cwd(), 'deploy-out', `${manifest.name}-${target}`);
|
|
223
|
+
? (targetsRequested.length > 1 ? path.join(path.resolve(options.out), target + routingSuffix) : path.resolve(options.out))
|
|
224
|
+
: path.join(process.cwd(), 'deploy-out', `${manifest.name}-${target}${routingSuffix}`);
|
|
186
225
|
fs.mkdirSync(outDir, { recursive: true });
|
|
187
226
|
// Copy the pack into the staging root (filtered: no VCS/caches/secrets, no source deploy/).
|
|
188
227
|
const brokenLinks = copyPackToStaging(packDir, outDir);
|
|
@@ -220,4 +259,5 @@ async function deployCommand(action, packArg, options) {
|
|
|
220
259
|
console.log(`\n${utils_1.COLORS.dim}Scanned pack secrets into .env.example: ${envVars.join(', ')}${utils_1.COLORS.reset}`);
|
|
221
260
|
}
|
|
222
261
|
console.log(`\nNext: cd into the staging dir above, \`cp deploy/.env.example deploy/.env\`, then run the command in deploy/README.md (scripts anchor themselves to the staging root).`);
|
|
262
|
+
console.log(`${utils_1.COLORS.dim}Tip: run deploy init from the pack's parent directory — not from inside the pack (default deploy-out/ inside the pack copies into itself and fails).${utils_1.COLORS.reset}`);
|
|
223
263
|
}
|
|
@@ -60,7 +60,65 @@ async function ensureStoreFileExists(storePath) {
|
|
|
60
60
|
process.exit(1);
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Broker-mediated send (secure-obo-design §7.6): under `agenthippo serve` with the egress
|
|
65
|
+
* broker active, the agent's env carries AGENTHIPPO_EGRESS_URL + AGENTHIPPO_TURN_NONCE but NOT
|
|
66
|
+
* the gateway token. Route the send through the broker, which resolves the destination
|
|
67
|
+
* server-side from THIS turn's bound handles and delivers with the broker-held token.
|
|
68
|
+
* `--outbound`/`--agent` are ignored here — the broker derives both from the verified nonce.
|
|
69
|
+
*/
|
|
70
|
+
async function sendViaEgressBroker(egressUrl, nonce, options) {
|
|
71
|
+
const message = options.message.trim();
|
|
72
|
+
if (!message) {
|
|
73
|
+
console.error('Error: --message is required');
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
if (options.dryRun) {
|
|
77
|
+
console.log(`${utils_js_1.COLORS.dim}Dry run (egress broker):${utils_js_1.COLORS.reset} POST ${egressUrl} { to: ${options.target ?? '(reply-to-sender)'} }`);
|
|
78
|
+
process.exit(0);
|
|
79
|
+
}
|
|
80
|
+
let res;
|
|
81
|
+
try {
|
|
82
|
+
res = await fetch(egressUrl, {
|
|
83
|
+
method: 'POST',
|
|
84
|
+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${nonce}` },
|
|
85
|
+
body: JSON.stringify({ message, to: options.target?.trim() || undefined, media: options.media?.trim() || undefined }),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
console.error(`${utils_js_1.COLORS.red}✗${utils_js_1.COLORS.reset} Egress broker unreachable: ${err instanceof Error ? err.message : String(err)}`);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
const bodyText = await res.text();
|
|
93
|
+
let body = {};
|
|
94
|
+
try {
|
|
95
|
+
body = JSON.parse(bodyText);
|
|
96
|
+
}
|
|
97
|
+
catch { /* non-JSON error body */ }
|
|
98
|
+
if (options.json) {
|
|
99
|
+
console.log(JSON.stringify({ via: 'egress-broker', status: res.status, result: body }, null, 2));
|
|
100
|
+
}
|
|
101
|
+
else if (res.ok && body.ok) {
|
|
102
|
+
console.log(`${utils_js_1.COLORS.green}✓${utils_js_1.COLORS.reset} Sent (via egress broker${body.via ? `, ${body.via}` : ''})`);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
console.error(`${utils_js_1.COLORS.red}✗${utils_js_1.COLORS.reset} Egress send rejected (HTTP ${res.status}): ${body.error || bodyText || 'unknown error'}`);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
process.exit(res.ok && body.ok ? 0 : 1);
|
|
109
|
+
}
|
|
63
110
|
async function gatewaySendCommand(options) {
|
|
111
|
+
// Prefer the broker whenever serve exposes it: the agent has no gateway token to send directly.
|
|
112
|
+
const egressUrl = process.env.AGENTHIPPO_EGRESS_URL?.trim();
|
|
113
|
+
const nonce = process.env.AGENTHIPPO_TURN_NONCE?.trim();
|
|
114
|
+
if (egressUrl && nonce) {
|
|
115
|
+
await sendViaEgressBroker(egressUrl, nonce, options);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (egressUrl) {
|
|
119
|
+
console.error('Error: egress broker is active but AGENTHIPPO_TURN_NONCE is missing — refusing direct gateway send');
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
64
122
|
const storePath = resolveOutboundPath(options.outbound);
|
|
65
123
|
await ensureStoreFileExists(storePath);
|
|
66
124
|
const agentId = options.agent.trim();
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*---------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) AgentHippo.ai. All rights reserved.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.idpCommand = idpCommand;
|
|
40
|
+
/**
|
|
41
|
+
* IdP Command — `agenthippo idp init --out <path> --hostname <host>`
|
|
42
|
+
*
|
|
43
|
+
* Generates the central IdP stack (PLAN/deploy.md) into a self-contained directory. Generates
|
|
44
|
+
* only; never builds images or calls a cloud — same rule as `deploy init`.
|
|
45
|
+
*/
|
|
46
|
+
const fs = __importStar(require("fs"));
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const idp_init_1 = require("../idp-init");
|
|
49
|
+
const utils_1 = require("../utils");
|
|
50
|
+
async function idpCommand(action, options) {
|
|
51
|
+
if (action !== 'init') {
|
|
52
|
+
console.error(`Unknown idp action "${action}". Supported: init`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (!options.hostname?.trim()) {
|
|
57
|
+
console.error('idp init requires --hostname <public-hostname> (e.g. auth.corp.example)');
|
|
58
|
+
process.exit(1);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const outDir = path.resolve(options.out || './enterprise-deploy/idp');
|
|
62
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
63
|
+
const ctx = { hostname: options.hostname.trim(), cookieDomain: options.cookieDomain };
|
|
64
|
+
const files = (0, idp_init_1.renderIdpArtifacts)(ctx);
|
|
65
|
+
const written = [];
|
|
66
|
+
const skipped = [];
|
|
67
|
+
for (const [rel, content] of Object.entries(files)) {
|
|
68
|
+
const dest = path.join(outDir, rel);
|
|
69
|
+
if (fs.existsSync(dest) && !options.force) {
|
|
70
|
+
skipped.push(rel);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
74
|
+
fs.writeFileSync(dest, content);
|
|
75
|
+
written.push(rel);
|
|
76
|
+
}
|
|
77
|
+
console.log(`${utils_1.COLORS.green}✓${utils_1.COLORS.reset} idp → ${path.relative(process.cwd(), outDir) || '.'}`);
|
|
78
|
+
for (const f of written) {
|
|
79
|
+
console.log(` ${utils_1.COLORS.dim}+ ${f}${utils_1.COLORS.reset}`);
|
|
80
|
+
}
|
|
81
|
+
for (const f of skipped) {
|
|
82
|
+
console.log(` ${utils_1.COLORS.yellow}• ${f} (exists; --force to overwrite)${utils_1.COLORS.reset}`);
|
|
83
|
+
}
|
|
84
|
+
console.log(`Next: cd ${path.relative(process.cwd(), outDir) || '.'}, cp .env.example ~/.agent-hippo/.env, cp .env.auth.example ~/.agent-hippo/.env.auth (fill in), then ./run.sh`);
|
|
85
|
+
}
|
package/out/cli/commands/pack.js
CHANGED
|
@@ -44,6 +44,10 @@ const fs = __importStar(require("fs"));
|
|
|
44
44
|
const path = __importStar(require("path"));
|
|
45
45
|
const pack_loader_1 = require("../pack-loader");
|
|
46
46
|
const branding_1 = require("../../branding");
|
|
47
|
+
const warrant_1 = require("../../authority/warrant");
|
|
48
|
+
const signature_1 = require("../../pack/signature");
|
|
49
|
+
const store_1 = require("./store");
|
|
50
|
+
const pack_install_ref_1 = require("../pack-install-ref");
|
|
47
51
|
const BANNER_LINE = '-'.repeat(62);
|
|
48
52
|
async function packCommand(options) {
|
|
49
53
|
const workspace = options.workspace;
|
|
@@ -65,9 +69,20 @@ async function packCommand(options) {
|
|
|
65
69
|
}
|
|
66
70
|
await showPackInfo(options.name, workspace);
|
|
67
71
|
break;
|
|
72
|
+
case 'sign':
|
|
73
|
+
await signPackCommand(options);
|
|
74
|
+
break;
|
|
75
|
+
case 'verify':
|
|
76
|
+
await verifyPackCommand(options);
|
|
77
|
+
break;
|
|
78
|
+
case 'digest':
|
|
79
|
+
await digestCommand(options);
|
|
80
|
+
break;
|
|
81
|
+
case 'keygen':
|
|
82
|
+
keygenCommand(options);
|
|
83
|
+
break;
|
|
68
84
|
case 'install':
|
|
69
|
-
|
|
70
|
-
console.log(` For now, manually copy your pack to ~/${branding_1.AGENTIDE_DIR_NAME}/agents/<name>/`);
|
|
85
|
+
await installPackCommand(options);
|
|
71
86
|
break;
|
|
72
87
|
case 'export':
|
|
73
88
|
console.log('Pack export is not yet implemented.');
|
|
@@ -79,11 +94,44 @@ async function packCommand(options) {
|
|
|
79
94
|
console.log(' list List installed packs');
|
|
80
95
|
console.log(' validate Validate a pack');
|
|
81
96
|
console.log(' info Show pack details');
|
|
82
|
-
console.log('
|
|
97
|
+
console.log(' sign Sign a pack so a fleet can verify it before running');
|
|
98
|
+
console.log(' verify Verify a pack against trusted public keys');
|
|
99
|
+
console.log(' digest Print the pack content digest (what a signature covers)');
|
|
100
|
+
console.log(' keygen Generate an ES256 signing keypair');
|
|
101
|
+
console.log(' install Install a pack from the store to a folder');
|
|
83
102
|
console.log(' export Export a pack (coming soon)');
|
|
84
103
|
process.exit(1);
|
|
85
104
|
}
|
|
86
105
|
}
|
|
106
|
+
async function installPackCommand(options) {
|
|
107
|
+
if (!options.name?.trim()) {
|
|
108
|
+
console.error('Error: pack slug required.');
|
|
109
|
+
console.log('Usage: agenthippo pack install <slug>[@version] --to <dir>');
|
|
110
|
+
console.log('');
|
|
111
|
+
console.log('Examples:');
|
|
112
|
+
console.log(' agenthippo pack install joker1 --to ./packs/joker1');
|
|
113
|
+
console.log(' agenthippo pack install code-reviewer@1.0.0 --to ./packs/code-reviewer');
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
const dest = options.to?.trim();
|
|
117
|
+
if (!dest) {
|
|
118
|
+
console.error('Error: --to <dir> is required.');
|
|
119
|
+
console.log('Usage: agenthippo pack install <slug>[@version] --to <dir>');
|
|
120
|
+
console.log('');
|
|
121
|
+
console.log('Same download as `agenthippo store install pack/<slug>`, extracted flat for serve/deploy.');
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
const { slug, version } = (0, pack_install_ref_1.parsePackInstallRef)(options.name);
|
|
125
|
+
await (0, store_1.installFromStore)({
|
|
126
|
+
action: 'install',
|
|
127
|
+
query: `pack/${slug}${version ? `@${version}` : ''}`,
|
|
128
|
+
workspace: options.workspace,
|
|
129
|
+
version,
|
|
130
|
+
force: options.force,
|
|
131
|
+
installDir: path.resolve(dest),
|
|
132
|
+
verbose: options.verbose,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
87
135
|
async function listPacks(workspace) {
|
|
88
136
|
const packs = (0, pack_loader_1.listAvailablePacks)(workspace);
|
|
89
137
|
if (packs.length === 0) {
|
|
@@ -218,3 +266,134 @@ async function showPackInfo(nameOrPath, workspace) {
|
|
|
218
266
|
process.exit(1);
|
|
219
267
|
}
|
|
220
268
|
}
|
|
269
|
+
// -----------------------------------------------------------------------------
|
|
270
|
+
// Signing
|
|
271
|
+
//
|
|
272
|
+
// Publishing has to be a one-liner before `--require-signed-pack` can be turned
|
|
273
|
+
// on in anger: a requirement nobody can satisfy just gets switched off again.
|
|
274
|
+
// -----------------------------------------------------------------------------
|
|
275
|
+
/** Resolve a pack path and its declared identity — a signature binds both, not just bytes. */
|
|
276
|
+
async function loadForSigning(nameOrPath, workspace) {
|
|
277
|
+
if (!nameOrPath) {
|
|
278
|
+
console.error('Error: pack name or path required');
|
|
279
|
+
process.exit(1);
|
|
280
|
+
}
|
|
281
|
+
const packPath = (0, pack_loader_1.resolvePackPath)(nameOrPath, workspace);
|
|
282
|
+
if (!packPath) {
|
|
283
|
+
console.error(`Error: pack not found: ${nameOrPath}`);
|
|
284
|
+
process.exit(1);
|
|
285
|
+
}
|
|
286
|
+
const loaded = await (0, pack_loader_1.loadAgentPack)(packPath, workspace);
|
|
287
|
+
return {
|
|
288
|
+
packPath: loaded.packPath,
|
|
289
|
+
name: loaded.manifest.metadata.name,
|
|
290
|
+
version: loaded.manifest.metadata.version ?? '0.0.0',
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
function keygenCommand(options) {
|
|
294
|
+
const kid = options.kid || `sign-${new Date().toISOString().slice(0, 10)}`;
|
|
295
|
+
const key = (0, warrant_1.generateWarrantKeypair)(kid);
|
|
296
|
+
const out = options.output;
|
|
297
|
+
if (out) {
|
|
298
|
+
fs.writeFileSync(out, key.privateKeyPem, { mode: 0o600 });
|
|
299
|
+
console.log(`Private key written to ${out} (mode 0600) — keep it off shared disks.`);
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
console.log(key.privateKeyPem);
|
|
303
|
+
}
|
|
304
|
+
console.log('');
|
|
305
|
+
console.log(`kid: ${kid}`);
|
|
306
|
+
console.log('Public half — publish this to the control plane so instances can verify:');
|
|
307
|
+
console.log(key.publicKeyPem.trim());
|
|
308
|
+
console.log('');
|
|
309
|
+
console.log(` curl -X POST "$CONTROL/api/keys" -H 'content-type: application/json' \\`);
|
|
310
|
+
console.log(` -d '{"kid":"${kid}","publicPem":"…"}'`);
|
|
311
|
+
}
|
|
312
|
+
async function signPackCommand(options) {
|
|
313
|
+
const { packPath, name, version } = await loadForSigning(options.name, options.workspace);
|
|
314
|
+
if (!options.key) {
|
|
315
|
+
console.error('Error: --key <private-key.pem> required. Generate one with: agenthippo pack keygen -o key.pem');
|
|
316
|
+
process.exit(1);
|
|
317
|
+
}
|
|
318
|
+
let privateKeyPem;
|
|
319
|
+
try {
|
|
320
|
+
privateKeyPem = fs.readFileSync(options.key, 'utf8');
|
|
321
|
+
}
|
|
322
|
+
catch (err) {
|
|
323
|
+
console.error(`Error: cannot read key ${options.key}: ${err instanceof Error ? err.message : err}`);
|
|
324
|
+
process.exit(1);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
const kid = options.kid;
|
|
328
|
+
if (!kid) {
|
|
329
|
+
console.error('Error: --kid <id> required — it is how a verifier finds the public half in JWKS.');
|
|
330
|
+
process.exit(1);
|
|
331
|
+
}
|
|
332
|
+
const { digest } = (0, signature_1.signPack)({ packDir: packPath, pack: name, packVersion: version, privateKeyPem, kid: kid });
|
|
333
|
+
console.log(`Signed ${name}@${version}`);
|
|
334
|
+
console.log(` digest ${digest}`);
|
|
335
|
+
console.log(` key ${kid}`);
|
|
336
|
+
console.log(` written ${path.join(packPath, signature_1.PACK_SIGNATURE_FILE)}`);
|
|
337
|
+
console.log('');
|
|
338
|
+
console.log('Commit pack.sig with the pack: it travels WITH the artifact, and a fleet that');
|
|
339
|
+
console.log('requires signatures refuses anything whose contents no longer match it.');
|
|
340
|
+
}
|
|
341
|
+
async function verifyPackCommand(options) {
|
|
342
|
+
const { packPath, name, version } = await loadForSigning(options.name, options.workspace);
|
|
343
|
+
const token = (0, signature_1.readPackSignature)(packPath);
|
|
344
|
+
if (!token) {
|
|
345
|
+
console.error(`UNSIGNED: no ${signature_1.PACK_SIGNATURE_FILE} at ${packPath}`);
|
|
346
|
+
console.error((0, signature_1.describePackRefusal)({ kind: 'unsigned', detail: `no ${signature_1.PACK_SIGNATURE_FILE}`, pack: name, packVersion: version, packDir: packPath }));
|
|
347
|
+
process.exit(1);
|
|
348
|
+
}
|
|
349
|
+
let keys = {};
|
|
350
|
+
if (options.keys) {
|
|
351
|
+
try {
|
|
352
|
+
keys = JSON.parse(fs.readFileSync(options.keys, 'utf8'));
|
|
353
|
+
}
|
|
354
|
+
catch (err) {
|
|
355
|
+
console.error(`Error: cannot read keys ${options.keys}: ${err instanceof Error ? err.message : err}`);
|
|
356
|
+
process.exit(1);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
const trust = await (0, signature_1.loadPackTrustRoot)({
|
|
361
|
+
env: options.jwks ? { ...process.env, [signature_1.PACK_JWKS_URL_ENV]: options.jwks } : process.env,
|
|
362
|
+
leaseUrl: process.env.AGENTHIPPO_LEASE_URL,
|
|
363
|
+
});
|
|
364
|
+
for (const w of trust.warnings) {
|
|
365
|
+
console.error(`Warning: ${w}`);
|
|
366
|
+
}
|
|
367
|
+
if (trust.status === 'unavailable') {
|
|
368
|
+
// Distinct from "no keys configured", and with a different fix. Collapsing the two into
|
|
369
|
+
// an empty keyring is finding A4 — a 503 read exactly like an unmanaged install.
|
|
370
|
+
console.error(`REFUSED: a trust root is configured but could not be loaded — ${trust.reason}`);
|
|
371
|
+
console.error((0, signature_1.describePackRefusal)({
|
|
372
|
+
kind: 'trust-root-unavailable', detail: trust.reason ?? 'unavailable', pack: name, packVersion: version,
|
|
373
|
+
packDir: packPath, trustRootHint: trust.url ? `Trust root: ${trust.url}` : undefined,
|
|
374
|
+
}));
|
|
375
|
+
process.exit(1);
|
|
376
|
+
}
|
|
377
|
+
keys = trust.keys;
|
|
378
|
+
}
|
|
379
|
+
if (Object.keys(keys).length === 0) {
|
|
380
|
+
// "No keys" is a refusal, never a pass: verifying against an empty keyring would report
|
|
381
|
+
// success for any signature at all.
|
|
382
|
+
console.error('REFUSED: no trusted public keys. Pass --keys <file>, --jwks <url>, or set AGENTHIPPO_PACK_SIGNING_KEYS.');
|
|
383
|
+
process.exit(1);
|
|
384
|
+
}
|
|
385
|
+
const res = (0, signature_1.verifyPack)(token, keys, { packDir: packPath, pack: name, packVersion: version });
|
|
386
|
+
if (!res.ok) {
|
|
387
|
+
console.error(`INVALID: ${res.reason}`);
|
|
388
|
+
console.error((0, signature_1.describePackRefusal)({ kind: res.kind, detail: res.reason, pack: name, packVersion: version, packDir: packPath }));
|
|
389
|
+
process.exit(1);
|
|
390
|
+
}
|
|
391
|
+
console.log(`OK: ${name}@${version} verified`);
|
|
392
|
+
console.log(` digest ${res.claims.digest}`);
|
|
393
|
+
console.log(` NOTE: .claude/, .codex/ and .agent-hippo/ ARE covered by this digest — they carry hooks`);
|
|
394
|
+
console.log(' and other executable content, and were previously outside the signature.');
|
|
395
|
+
}
|
|
396
|
+
async function digestCommand(options) {
|
|
397
|
+
const { packPath, name, version } = await loadForSigning(options.name, options.workspace);
|
|
398
|
+
console.log(`${(0, signature_1.computePackDigest)(packPath)} ${name}@${version}`);
|
|
399
|
+
}
|