@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
|
@@ -0,0 +1,218 @@
|
|
|
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.TURN_NONCE_HEADER = exports.TURN_NONCE_ENV = exports.DATA_MCP_URL_ENV = void 0;
|
|
40
|
+
exports.startDataGateway = startDataGateway;
|
|
41
|
+
/**
|
|
42
|
+
* Data-plane MCP gateway — a streamable-HTTP MCP server inside the serve (broker) process,
|
|
43
|
+
* bound to 127.0.0.1 (secure-obo-design §3.1/§3.2).
|
|
44
|
+
*
|
|
45
|
+
* Every request must present `Authorization: Bearer <turn nonce>`; the nonce resolves in
|
|
46
|
+
* the TurnRegistry to a verified {userId, sessionKey}. The credential is then selected
|
|
47
|
+
* SERVER-SIDE from the broker at call time (§5 step 7) — no parameter in the MCP request
|
|
48
|
+
* can name a user or a credential. Unknown/expired nonce ⇒ 401, fail closed.
|
|
49
|
+
*
|
|
50
|
+
* What nonce theft buys is documented in §3.3: loopback-only, turn-scoped, tool-surface-
|
|
51
|
+
* only access as the user the model was already acting for.
|
|
52
|
+
*/
|
|
53
|
+
const http = __importStar(require("http"));
|
|
54
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
55
|
+
const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
56
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
57
|
+
const serve_hardening_1 = require("../serve-hardening");
|
|
58
|
+
const registry_1 = require("./registry");
|
|
59
|
+
/** Loopback URL of the data-plane gateway; set per turn into the engine child env. */
|
|
60
|
+
exports.DATA_MCP_URL_ENV = 'AGENTHIPPO_DATA_MCP_URL';
|
|
61
|
+
/** The turn capability. Per-spawn env only; never written to any shared file. */
|
|
62
|
+
exports.TURN_NONCE_ENV = 'AGENTHIPPO_TURN_NONCE';
|
|
63
|
+
/**
|
|
64
|
+
* Request header carrying an EDGE-registered turn nonce (P1 fix, obo-inprogress §2). The
|
|
65
|
+
* trusted edge (IdP Caddy) registers the turn with the sidecar via forward_auth and copies
|
|
66
|
+
* this header onto the request it proxies to serve. Trusted exactly like the pinned identity
|
|
67
|
+
* header: only the in-network edge can reach serve, and the edge overwrites any client-
|
|
68
|
+
* supplied value. A forged value fails closed at the gateway (unknown nonce ⇒ 401).
|
|
69
|
+
*/
|
|
70
|
+
exports.TURN_NONCE_HEADER = 'x-agent-turn-nonce';
|
|
71
|
+
function bearerToken(header) {
|
|
72
|
+
const m = header?.match(/^Bearer\s+(\S+)$/i);
|
|
73
|
+
return m?.[1];
|
|
74
|
+
}
|
|
75
|
+
/** sha256 prefix for audit lines — never log a raw nonce (secure-obo-design §11). */
|
|
76
|
+
function noncePrefix(nonce) {
|
|
77
|
+
return nonce ? (0, registry_1.hashNonce)(nonce).slice(0, 12) : 'none';
|
|
78
|
+
}
|
|
79
|
+
async function startDataGateway(opts) {
|
|
80
|
+
const server = http.createServer(async (req, res) => {
|
|
81
|
+
try {
|
|
82
|
+
const urlPath = req.url?.split('?')[0] ?? '';
|
|
83
|
+
// Release-by-nonce (edge-registration mode): serve holds the nonce but no control-API
|
|
84
|
+
// authority, so it ends the turn here. Auth is the nonce itself — releasing is the one
|
|
85
|
+
// operation safe to grant any nonce holder (fail-closed: it only kills the capability
|
|
86
|
+
// early; the registry deadline remains the backstop when this is never called).
|
|
87
|
+
if (urlPath === '/turn' && req.method === 'DELETE') {
|
|
88
|
+
const nonce = bearerToken(req.headers.authorization);
|
|
89
|
+
if (nonce) {
|
|
90
|
+
const released = opts.registry.releaseByNonce(nonce);
|
|
91
|
+
opts.audit?.('edge', 'turn_release', { released, nonce_sha256_prefix: noncePrefix(nonce) });
|
|
92
|
+
}
|
|
93
|
+
res.writeHead(204);
|
|
94
|
+
res.end();
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (urlPath !== '/mcp') {
|
|
98
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
99
|
+
res.end(JSON.stringify({ error: 'not found' }));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (req.method !== 'POST') {
|
|
103
|
+
// Stateless transport: one POST per JSON-RPC exchange; no GET stream, no sessions.
|
|
104
|
+
res.writeHead(405, { 'Content-Type': 'application/json', 'Allow': 'POST' });
|
|
105
|
+
res.end(JSON.stringify({ error: 'method not allowed' }));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const nonce = bearerToken(req.headers.authorization);
|
|
109
|
+
const turn = nonce ? opts.registry.resolve(nonce) : undefined;
|
|
110
|
+
if (!turn) {
|
|
111
|
+
// Expired / unknown / missing — a stale engine subprocess fails CLOSED here
|
|
112
|
+
// instead of ever using a stale user's credential (§3.2).
|
|
113
|
+
opts.audit?.('unauthorized', 'nonce_reject', {
|
|
114
|
+
reason: nonce ? 'unknown_or_expired' : 'missing',
|
|
115
|
+
nonce_sha256_prefix: noncePrefix(nonce),
|
|
116
|
+
});
|
|
117
|
+
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
118
|
+
res.end(JSON.stringify({
|
|
119
|
+
jsonrpc: '2.0',
|
|
120
|
+
error: { code: -32001, message: 'Unauthorized: missing, unknown, or expired turn nonce' },
|
|
121
|
+
id: null,
|
|
122
|
+
}));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
opts.logger?.info(`[DataPlane] authorized request user=${turn.userId} session=${turn.sessionKey}`);
|
|
126
|
+
let body;
|
|
127
|
+
try {
|
|
128
|
+
body = JSON.parse(await (0, serve_hardening_1.readBodyWithCap)(req));
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
132
|
+
res.end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32700, message: 'Parse error' }, id: null }));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
// Fresh stateless server+transport per request (SDK stateless pattern) so a request
|
|
136
|
+
// carries exactly its own turn's authority and nothing outlives the response.
|
|
137
|
+
const mcp = buildTurnScopedMcpServer(opts, turn);
|
|
138
|
+
const transport = new streamableHttp_js_1.StreamableHTTPServerTransport({ sessionIdGenerator: undefined, enableJsonResponse: true });
|
|
139
|
+
res.on('close', () => {
|
|
140
|
+
void transport.close();
|
|
141
|
+
void mcp.close();
|
|
142
|
+
});
|
|
143
|
+
await mcp.connect(transport);
|
|
144
|
+
await transport.handleRequest(req, res, body);
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
opts.logger?.warn(`[DataPlane] gateway error: ${err instanceof Error ? err.message : String(err)}`);
|
|
148
|
+
if (!res.headersSent) {
|
|
149
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
150
|
+
}
|
|
151
|
+
res.end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32603, message: 'Internal error' }, id: null }));
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
const bindHost = opts.host ?? '127.0.0.1';
|
|
155
|
+
await new Promise((resolve, reject) => {
|
|
156
|
+
server.once('error', reject);
|
|
157
|
+
// Default loopback; a sidecar binds 0.0.0.0 for the private pod network only.
|
|
158
|
+
server.listen(opts.port ?? 0, bindHost, () => resolve());
|
|
159
|
+
});
|
|
160
|
+
const address = server.address();
|
|
161
|
+
if (!address || typeof address === 'string') {
|
|
162
|
+
throw new Error('Data gateway failed to bind a port');
|
|
163
|
+
}
|
|
164
|
+
const url = `http://${opts.advertiseHost ?? '127.0.0.1'}:${address.port}/mcp`;
|
|
165
|
+
opts.logger?.info(`[DataPlane] gateway listening at ${url}`);
|
|
166
|
+
return {
|
|
167
|
+
url,
|
|
168
|
+
close: () => new Promise((resolve) => server.close(() => resolve())),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function buildTurnScopedMcpServer(opts, turn) {
|
|
172
|
+
const server = new index_js_1.Server({ name: 'agenthippo-data-gateway', version: '1.0.0' }, { capabilities: { tools: {} } });
|
|
173
|
+
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
174
|
+
tools: await opts.connector.listTools(),
|
|
175
|
+
}));
|
|
176
|
+
server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
177
|
+
const toolName = request.params.name;
|
|
178
|
+
// Server-side credential selection at call time (§5 step 7): the registry entry's
|
|
179
|
+
// verified userId picks the credential — never anything in the MCP request.
|
|
180
|
+
let secretRef;
|
|
181
|
+
try {
|
|
182
|
+
const credential = await opts.broker.acquire(turn.userId, turn.inboundToken);
|
|
183
|
+
secretRef = credential.secretRef;
|
|
184
|
+
opts.audit?.(turn.sessionKey, 'tool_authorize', { tool: toolName, userId: turn.userId, provider: opts.broker.provider.name });
|
|
185
|
+
}
|
|
186
|
+
catch (err) {
|
|
187
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
188
|
+
opts.audit?.(turn.sessionKey, 'tool_deny', { tool: toolName, userId: turn.userId, reason: 'acquire_failed', error: message });
|
|
189
|
+
return {
|
|
190
|
+
content: [{ type: 'text', text: JSON.stringify({ ok: false, error: `credential acquire failed (fail-closed): ${message}` }) }],
|
|
191
|
+
isError: true,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
return await opts.connector.callTool(toolName, request.params.arguments ?? {}, { userId: turn.userId, credential: secretRef });
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
// Mid-turn credential expiry: drop the cache, re-acquire once, retry once (§5).
|
|
199
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
200
|
+
if (/401|unauthoriz|expired/i.test(message)) {
|
|
201
|
+
opts.broker.invalidate(turn.userId);
|
|
202
|
+
try {
|
|
203
|
+
const retryCred = await opts.broker.acquire(turn.userId, turn.inboundToken);
|
|
204
|
+
return await opts.connector.callTool(toolName, request.params.arguments ?? {}, { userId: turn.userId, credential: retryCred.secretRef });
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
// fall through to the original error below
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
opts.audit?.(turn.sessionKey, 'tool_deny', { tool: toolName, userId: turn.userId, reason: 'call_failed', error: message });
|
|
211
|
+
return {
|
|
212
|
+
content: [{ type: 'text', text: JSON.stringify({ ok: false, error: message }) }],
|
|
213
|
+
isError: true,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
return server;
|
|
218
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
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.DataPlane = exports.resolveEdgeDataPlane = exports.EdgeDataPlane = exports.resolveRemoteDataPlane = exports.RemoteDataPlane = exports.startDataPlaneSidecar = exports.EGRESS_URL_ENV = exports.startEgressBroker = exports.TURN_NONCE_HEADER = exports.TURN_NONCE_ENV = exports.DATA_MCP_URL_ENV = exports.startDataGateway = exports.TURN_META_KEY = exports.ConnectorHost = exports.identityProvider = exports.passthroughProvider = exports.resolveProvider = exports.CredentialBroker = exports.DEFAULT_TURN_DEADLINE_MS = exports.hashNonce = exports.TurnRegistry = void 0;
|
|
40
|
+
exports.resolveDataPlaneConfig = resolveDataPlaneConfig;
|
|
41
|
+
/**
|
|
42
|
+
* DataPlane — orchestration of the credential broker + data-plane gateway
|
|
43
|
+
* (secure-obo-design §3): TurnRegistry + CredentialBroker + broker-owned connector
|
|
44
|
+
* subprocess + loopback streamable-HTTP MCP gateway.
|
|
45
|
+
*
|
|
46
|
+
* serve starts one DataPlane at boot when the pack declares `spec.dataPlane`, then
|
|
47
|
+
* registers each verified turn to get `{ AGENTHIPPO_TURN_NONCE }` for the engine's
|
|
48
|
+
* per-spawn env. The pack's MCP config points at `${env:AGENTHIPPO_DATA_MCP_URL}` with
|
|
49
|
+
* `bearerTokenEnv: AGENTHIPPO_TURN_NONCE` — turn-invariant bytes, per-turn authority.
|
|
50
|
+
*/
|
|
51
|
+
const path = __importStar(require("path"));
|
|
52
|
+
const registry_1 = require("./registry");
|
|
53
|
+
const broker_1 = require("./broker");
|
|
54
|
+
const connector_host_1 = require("./connector-host");
|
|
55
|
+
const gateway_1 = require("./gateway");
|
|
56
|
+
var registry_2 = require("./registry");
|
|
57
|
+
Object.defineProperty(exports, "TurnRegistry", { enumerable: true, get: function () { return registry_2.TurnRegistry; } });
|
|
58
|
+
Object.defineProperty(exports, "hashNonce", { enumerable: true, get: function () { return registry_2.hashNonce; } });
|
|
59
|
+
Object.defineProperty(exports, "DEFAULT_TURN_DEADLINE_MS", { enumerable: true, get: function () { return registry_2.DEFAULT_TURN_DEADLINE_MS; } });
|
|
60
|
+
var broker_2 = require("./broker");
|
|
61
|
+
Object.defineProperty(exports, "CredentialBroker", { enumerable: true, get: function () { return broker_2.CredentialBroker; } });
|
|
62
|
+
Object.defineProperty(exports, "resolveProvider", { enumerable: true, get: function () { return broker_2.resolveProvider; } });
|
|
63
|
+
Object.defineProperty(exports, "passthroughProvider", { enumerable: true, get: function () { return broker_2.passthroughProvider; } });
|
|
64
|
+
Object.defineProperty(exports, "identityProvider", { enumerable: true, get: function () { return broker_2.identityProvider; } });
|
|
65
|
+
var connector_host_2 = require("./connector-host");
|
|
66
|
+
Object.defineProperty(exports, "ConnectorHost", { enumerable: true, get: function () { return connector_host_2.ConnectorHost; } });
|
|
67
|
+
Object.defineProperty(exports, "TURN_META_KEY", { enumerable: true, get: function () { return connector_host_2.TURN_META_KEY; } });
|
|
68
|
+
var gateway_2 = require("./gateway");
|
|
69
|
+
Object.defineProperty(exports, "startDataGateway", { enumerable: true, get: function () { return gateway_2.startDataGateway; } });
|
|
70
|
+
Object.defineProperty(exports, "DATA_MCP_URL_ENV", { enumerable: true, get: function () { return gateway_2.DATA_MCP_URL_ENV; } });
|
|
71
|
+
Object.defineProperty(exports, "TURN_NONCE_ENV", { enumerable: true, get: function () { return gateway_2.TURN_NONCE_ENV; } });
|
|
72
|
+
Object.defineProperty(exports, "TURN_NONCE_HEADER", { enumerable: true, get: function () { return gateway_2.TURN_NONCE_HEADER; } });
|
|
73
|
+
var egress_1 = require("./egress");
|
|
74
|
+
Object.defineProperty(exports, "startEgressBroker", { enumerable: true, get: function () { return egress_1.startEgressBroker; } });
|
|
75
|
+
Object.defineProperty(exports, "EGRESS_URL_ENV", { enumerable: true, get: function () { return egress_1.EGRESS_URL_ENV; } });
|
|
76
|
+
var sidecar_1 = require("./sidecar");
|
|
77
|
+
Object.defineProperty(exports, "startDataPlaneSidecar", { enumerable: true, get: function () { return sidecar_1.startDataPlaneSidecar; } });
|
|
78
|
+
var remote_1 = require("./remote");
|
|
79
|
+
Object.defineProperty(exports, "RemoteDataPlane", { enumerable: true, get: function () { return remote_1.RemoteDataPlane; } });
|
|
80
|
+
Object.defineProperty(exports, "resolveRemoteDataPlane", { enumerable: true, get: function () { return remote_1.resolveRemoteDataPlane; } });
|
|
81
|
+
Object.defineProperty(exports, "EdgeDataPlane", { enumerable: true, get: function () { return remote_1.EdgeDataPlane; } });
|
|
82
|
+
Object.defineProperty(exports, "resolveEdgeDataPlane", { enumerable: true, get: function () { return remote_1.resolveEdgeDataPlane; } });
|
|
83
|
+
/**
|
|
84
|
+
* Resolve the pack's data-plane declaration. The connector path resolves against the PACK
|
|
85
|
+
* SOURCE directory (operator-authored, broker trust domain) — not the agent-readable
|
|
86
|
+
* workspace copy.
|
|
87
|
+
*/
|
|
88
|
+
function resolveDataPlaneConfig(spec, packPath) {
|
|
89
|
+
const dp = spec?.dataPlane;
|
|
90
|
+
const connector = dp?.connector?.trim();
|
|
91
|
+
if (!connector) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
connectorPath: path.isAbsolute(connector) ? connector : path.join(packPath, connector),
|
|
96
|
+
provider: dp?.provider?.trim() || undefined,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
class DataPlane {
|
|
100
|
+
url;
|
|
101
|
+
registry;
|
|
102
|
+
broker;
|
|
103
|
+
connector;
|
|
104
|
+
gateway;
|
|
105
|
+
constructor(url, registry, broker, connector, gateway) {
|
|
106
|
+
this.url = url;
|
|
107
|
+
this.registry = registry;
|
|
108
|
+
this.broker = broker;
|
|
109
|
+
this.connector = connector;
|
|
110
|
+
this.gateway = gateway;
|
|
111
|
+
}
|
|
112
|
+
static async start(cfg, opts) {
|
|
113
|
+
// Reuse serve's shared registry when given, so one nonce grants BOTH data and egress
|
|
114
|
+
// capabilities (secure-obo-design §7.6 step 2). Standalone (probe/tests) get their own.
|
|
115
|
+
const registry = opts?.registry ?? new registry_1.TurnRegistry();
|
|
116
|
+
const broker = new broker_1.CredentialBroker((0, broker_1.resolveProvider)(cfg.provider));
|
|
117
|
+
const connector = await connector_host_1.ConnectorHost.spawn({ connectorPath: cfg.connectorPath, logger: opts?.logger });
|
|
118
|
+
try {
|
|
119
|
+
const gateway = await (0, gateway_1.startDataGateway)({
|
|
120
|
+
registry, broker, connector, logger: opts?.logger, audit: opts?.audit,
|
|
121
|
+
host: opts?.gateway?.host, port: opts?.gateway?.port, advertiseHost: opts?.gateway?.advertiseHost,
|
|
122
|
+
});
|
|
123
|
+
return new DataPlane(gateway.url, registry, broker, connector, gateway);
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
await connector.close();
|
|
127
|
+
throw err;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/** Register a turn directly in the local registry (used by the sidecar control API). */
|
|
131
|
+
registerTurnRaw(reg) {
|
|
132
|
+
return this.registry.register(reg);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Register a verified turn. Returns the per-spawn env for the engine and the release()
|
|
136
|
+
* to call in the turn's `finally`. The gateway URL itself is boot-stable and rides
|
|
137
|
+
* process.env (see serve), keeping codex's config.toml turn-invariant.
|
|
138
|
+
*/
|
|
139
|
+
registerTurn(reg) {
|
|
140
|
+
const { nonce, release } = this.registry.register(reg);
|
|
141
|
+
return {
|
|
142
|
+
nonce,
|
|
143
|
+
env: { [gateway_1.TURN_NONCE_ENV]: nonce, [gateway_1.DATA_MCP_URL_ENV]: this.url },
|
|
144
|
+
release,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
async close() {
|
|
148
|
+
await this.gateway.close();
|
|
149
|
+
await this.connector.close();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
exports.DataPlane = DataPlane;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*---------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) AgentHippo.ai. All rights reserved.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.TurnRegistry = exports.DEFAULT_TURN_DEADLINE_MS = void 0;
|
|
7
|
+
exports.hashNonce = hashNonce;
|
|
8
|
+
/**
|
|
9
|
+
* TurnRegistry — the per-turn capability table of the data-plane gateway
|
|
10
|
+
* (secure-obo-design §3.1). Replaces the on-disk token file entirely.
|
|
11
|
+
*
|
|
12
|
+
* Per turn: mint a random 128-bit nonce, store the turn's verified identity under
|
|
13
|
+
* `sha256(nonce)` (the raw nonce is never stored or used as a lookup key, so lookups leak
|
|
14
|
+
* no timing signal about live nonces), hand the nonce to the engine via per-spawn env only,
|
|
15
|
+
* and delete the entry in the same `finally` that ends the turn. `deadline` is the backstop
|
|
16
|
+
* for a stranded entry — a leaked nonce dies on its own.
|
|
17
|
+
*
|
|
18
|
+
* The registry holds NO data credential: the gateway selects the credential server-side at
|
|
19
|
+
* call time from the broker's per-user cache (§5 step 7) — nothing a client sends can name
|
|
20
|
+
* a user or a credential.
|
|
21
|
+
*/
|
|
22
|
+
const crypto_1 = require("crypto");
|
|
23
|
+
/** Backstop lifetime for a turn's nonce. Turns are seconds-to-minutes; 30 min is generous. */
|
|
24
|
+
exports.DEFAULT_TURN_DEADLINE_MS = 30 * 60_000;
|
|
25
|
+
function hashNonce(nonce) {
|
|
26
|
+
return (0, crypto_1.createHash)('sha256').update(nonce).digest('hex');
|
|
27
|
+
}
|
|
28
|
+
class TurnRegistry {
|
|
29
|
+
entries = new Map();
|
|
30
|
+
/**
|
|
31
|
+
* Mint a nonce and register the turn. Call `release()` in the turn's `finally`.
|
|
32
|
+
*/
|
|
33
|
+
register(reg) {
|
|
34
|
+
this.prune();
|
|
35
|
+
// Never derived from anything — pure randomness (secure-obo-design §3.1).
|
|
36
|
+
const nonce = (0, crypto_1.randomBytes)(16).toString('hex');
|
|
37
|
+
const key = hashNonce(nonce);
|
|
38
|
+
this.entries.set(key, {
|
|
39
|
+
userId: reg.userId,
|
|
40
|
+
sessionKey: reg.sessionKey,
|
|
41
|
+
inboundToken: reg.inboundToken,
|
|
42
|
+
egressStoreKey: reg.egressStoreKey,
|
|
43
|
+
deadline: reg.deadline ?? Date.now() + exports.DEFAULT_TURN_DEADLINE_MS,
|
|
44
|
+
});
|
|
45
|
+
let released = false;
|
|
46
|
+
return {
|
|
47
|
+
nonce,
|
|
48
|
+
release: () => {
|
|
49
|
+
if (!released) {
|
|
50
|
+
released = true;
|
|
51
|
+
this.entries.delete(key);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Release a turn by its nonce (idempotent). Lets a holder of the nonce end its own turn —
|
|
58
|
+
* used by the gateway's release endpoint for EDGE-registered turns, where serve holds the
|
|
59
|
+
* nonce but no registration authority. Strictly fail-closed: the worst a stolen nonce can
|
|
60
|
+
* do here is end the capability early.
|
|
61
|
+
*/
|
|
62
|
+
releaseByNonce(nonce) {
|
|
63
|
+
return this.entries.delete(hashNonce(nonce));
|
|
64
|
+
}
|
|
65
|
+
/** Resolve a presented nonce to its turn, enforcing the deadline. Fail-closed: unknown/expired → undefined. */
|
|
66
|
+
resolve(nonce, now = Date.now()) {
|
|
67
|
+
const entry = this.entries.get(hashNonce(nonce));
|
|
68
|
+
if (!entry) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
if (now > entry.deadline) {
|
|
72
|
+
this.entries.delete(hashNonce(nonce));
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
return entry;
|
|
76
|
+
}
|
|
77
|
+
/** Drop expired entries (called on every register; cheap at realistic turn counts). */
|
|
78
|
+
prune(now = Date.now()) {
|
|
79
|
+
for (const [key, entry] of this.entries) {
|
|
80
|
+
if (now > entry.deadline) {
|
|
81
|
+
this.entries.delete(key);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
get size() {
|
|
86
|
+
return this.entries.size;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
exports.TurnRegistry = TurnRegistry;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*---------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) AgentHippo.ai. All rights reserved.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.RemoteDataPlane = exports.EdgeDataPlane = void 0;
|
|
7
|
+
exports.resolveEdgeDataPlane = resolveEdgeDataPlane;
|
|
8
|
+
exports.resolveRemoteDataPlane = resolveRemoteDataPlane;
|
|
9
|
+
/**
|
|
10
|
+
* RemoteDataPlane — serve's client to a data-plane SIDECAR (secure-obo-design §7.6).
|
|
11
|
+
*
|
|
12
|
+
* When the connector runs in its own container, serve (in the agent container) holds NO base
|
|
13
|
+
* credential. It registers each verified turn with the sidecar's authenticated control API and
|
|
14
|
+
* hands the engine the returned nonce plus the sidecar's gateway URL. The engine dials the
|
|
15
|
+
* sidecar gateway with the nonce; the sidecar resolves nonce → user and runs the connector.
|
|
16
|
+
*
|
|
17
|
+
* Presents the same `registerTurn()` shape as the in-process `DataPlane`, so serve's turn
|
|
18
|
+
* handling is identical whether the connector is in-process or a sidecar.
|
|
19
|
+
*/
|
|
20
|
+
const gateway_1 = require("./gateway");
|
|
21
|
+
/** Read the edge-mode wiring from env. Returns null unless explicitly enabled. */
|
|
22
|
+
function resolveEdgeDataPlane(env = process.env) {
|
|
23
|
+
const enabled = env.AGENTHIPPO_DATA_PLANE_EDGE?.trim();
|
|
24
|
+
const gatewayUrl = env.AGENTHIPPO_DATA_MCP_URL?.trim();
|
|
25
|
+
if (!enabled || enabled === '0' || enabled.toLowerCase() === 'false' || !gatewayUrl) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
return { gatewayUrl };
|
|
29
|
+
}
|
|
30
|
+
class EdgeDataPlane {
|
|
31
|
+
cfg;
|
|
32
|
+
constructor(cfg) {
|
|
33
|
+
this.cfg = cfg;
|
|
34
|
+
}
|
|
35
|
+
/** The engine-facing gateway URL (boot-stable; set into process.env by serve like the local plane). */
|
|
36
|
+
get url() { return this.cfg.gatewayUrl; }
|
|
37
|
+
/**
|
|
38
|
+
* Adopt an edge-registered nonce for this turn. Release calls the gateway's
|
|
39
|
+
* nonce-authenticated `DELETE /turn` — the one operation a bare nonce may perform besides
|
|
40
|
+
* the tool surface, and strictly fail-closed (it only ends the capability early; the
|
|
41
|
+
* sidecar's registry deadline is the backstop when the call never lands).
|
|
42
|
+
*/
|
|
43
|
+
adoptTurn(nonce) {
|
|
44
|
+
return {
|
|
45
|
+
nonce,
|
|
46
|
+
env: { [gateway_1.TURN_NONCE_ENV]: nonce, [gateway_1.DATA_MCP_URL_ENV]: this.cfg.gatewayUrl },
|
|
47
|
+
release: async () => {
|
|
48
|
+
try {
|
|
49
|
+
await fetch(new URL('/turn', this.cfg.gatewayUrl), {
|
|
50
|
+
method: 'DELETE',
|
|
51
|
+
headers: { Authorization: `Bearer ${nonce}` },
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
catch { /* best-effort release; the sidecar deadline is the backstop */ }
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
exports.EdgeDataPlane = EdgeDataPlane;
|
|
60
|
+
/** Read the sidecar wiring from env (set at deploy on the agent container). Returns null if absent. */
|
|
61
|
+
function resolveRemoteDataPlane(env = process.env) {
|
|
62
|
+
const controlUrl = env.AGENTHIPPO_DATA_PLANE_CONTROL_URL?.trim();
|
|
63
|
+
const registerToken = env.AGENTHIPPO_DATA_PLANE_TOKEN?.trim();
|
|
64
|
+
const gatewayUrl = env.AGENTHIPPO_DATA_MCP_URL?.trim();
|
|
65
|
+
if (!controlUrl || !registerToken || !gatewayUrl) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
return { controlUrl, registerToken, gatewayUrl };
|
|
69
|
+
}
|
|
70
|
+
class RemoteDataPlane {
|
|
71
|
+
cfg;
|
|
72
|
+
constructor(cfg) {
|
|
73
|
+
this.cfg = cfg;
|
|
74
|
+
}
|
|
75
|
+
/** The engine-facing gateway URL (boot-stable; set into process.env by serve like the local plane). */
|
|
76
|
+
get url() { return this.cfg.gatewayUrl; }
|
|
77
|
+
/**
|
|
78
|
+
* Register a turn with the sidecar. Returns the per-spawn env + release, matching DataPlane.
|
|
79
|
+
* The base credential never touches this process — only the nonce comes back.
|
|
80
|
+
*/
|
|
81
|
+
async registerTurn(reg) {
|
|
82
|
+
const res = await fetch(`${this.cfg.controlUrl.replace(/\/$/, '')}/turns`, {
|
|
83
|
+
method: 'POST',
|
|
84
|
+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${this.cfg.registerToken}` },
|
|
85
|
+
body: JSON.stringify(reg),
|
|
86
|
+
});
|
|
87
|
+
if (!res.ok) {
|
|
88
|
+
throw new Error(`data-plane sidecar register failed: HTTP ${res.status}`);
|
|
89
|
+
}
|
|
90
|
+
const { nonce } = await res.json();
|
|
91
|
+
return {
|
|
92
|
+
nonce,
|
|
93
|
+
env: { [gateway_1.TURN_NONCE_ENV]: nonce, [gateway_1.DATA_MCP_URL_ENV]: this.cfg.gatewayUrl },
|
|
94
|
+
release: async () => {
|
|
95
|
+
try {
|
|
96
|
+
await fetch(`${this.cfg.controlUrl.replace(/\/$/, '')}/turns/${encodeURIComponent(nonce)}`, {
|
|
97
|
+
method: 'DELETE',
|
|
98
|
+
headers: { Authorization: `Bearer ${this.cfg.registerToken}` },
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
catch { /* best-effort release; the sidecar deadline is the backstop */ }
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
exports.RemoteDataPlane = RemoteDataPlane;
|