@agenthippo-ai/agenthippo 0.2.3-dataplane → 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.
Files changed (130) hide show
  1. package/out/analytics/dashboard/provider.js +13 -2
  2. package/out/analytics/dashboard/webview/panel.js +3 -2
  3. package/out/analytics/identity.js +0 -0
  4. package/out/analytics/service.js +23 -25
  5. package/out/analytics/sessionEvents.js +98 -23
  6. package/out/auth/authBaseUrl.js +59 -0
  7. package/out/auth/bootstrapApply.js +9 -10
  8. package/out/auth/brokerClient.js +48 -14
  9. package/out/auth/provider.js +8 -21
  10. package/out/auth/sessionStore.js +3 -40
  11. package/out/auth/signInFlow.js +2 -1
  12. package/out/auth-broker/config.js +50 -5
  13. package/out/auth-broker/server.js +122 -26
  14. package/out/auth-broker/store.js +25 -24
  15. package/out/authority/warrant.js +206 -0
  16. package/out/branding.js +27 -1
  17. package/out/cli/commands/conformance.js +249 -0
  18. package/out/cli/commands/data-plane.js +67 -0
  19. package/out/cli/commands/deploy.js +1 -0
  20. package/out/cli/commands/gateway.js +4 -0
  21. package/out/cli/commands/pack.js +182 -3
  22. package/out/cli/commands/sandbox.js +332 -0
  23. package/out/cli/commands/serve.js +875 -52
  24. package/out/cli/commands/store.js +12 -4
  25. package/out/cli/data-plane/gateway.js +28 -5
  26. package/out/cli/data-plane/index.js +17 -2
  27. package/out/cli/data-plane/registry.js +9 -0
  28. package/out/cli/data-plane/remote.js +106 -0
  29. package/out/cli/data-plane/sidecar.js +161 -0
  30. package/out/cli/deploy/layers/identity.js +85 -4
  31. package/out/cli/deploy-init.js +89 -24
  32. package/out/cli/deps.js +106 -2
  33. package/out/cli/idp-init.js +35 -0
  34. package/out/cli/index.js +155 -16
  35. package/out/cli/pack-install-ref.js +15 -0
  36. package/out/cli/pack-loader.js +8 -1
  37. package/out/cli/pack-runtime.js +61 -0
  38. package/out/cli/runtime.js +35 -1
  39. package/out/cli/serve-hardening.js +80 -3
  40. package/out/cli/utils.js +12 -1
  41. package/out/engines/base.js +32 -3
  42. package/out/engines/claude.js +33 -15
  43. package/out/engines/codex.js +110 -26
  44. package/out/engines/custom.js +8 -0
  45. package/out/evidence/sessionShip.js +610 -0
  46. package/out/extension.js +11 -2
  47. package/out/gateway/bridge.js +40 -2
  48. package/out/gateway/client.js +34 -1
  49. package/out/gateway/installSource.js +68 -0
  50. package/out/gateway/lifecycle.js +185 -51
  51. package/out/gateway/outboundPaths.js +4 -1
  52. package/out/gateway/secretsPreflight.js +90 -0
  53. package/out/gateway/service.js +7 -2
  54. package/out/headless/index.js +17 -1
  55. package/out/live-e2e/mcp-demo/server.mjs +32 -0
  56. package/out/mcp.js +30 -0
  57. package/out/pack/caps.js +271 -0
  58. package/out/pack/governor.js +309 -0
  59. package/out/pack/lease-schema.js +270 -0
  60. package/out/pack/lease.js +640 -0
  61. package/out/pack/reconcile.js +217 -0
  62. package/out/pack/signature.js +839 -0
  63. package/out/remote/handleRemoteChat.js +44 -3
  64. package/out/remote/remoteTrust.js +277 -0
  65. package/out/remote/serveClient.js +52 -5
  66. package/out/runner/host.js +131 -0
  67. package/out/runner/protocol.js +108 -0
  68. package/out/runner/remoteEngine.js +130 -0
  69. package/out/runtime/interfaces/edit-tracker.js +2 -0
  70. package/out/runtime/vscode/vscode-edit-tracker.js +2 -0
  71. package/out/sandbox/containerDriver.js +570 -0
  72. package/out/sandbox/processDriver.js +315 -0
  73. package/out/sandbox/sandboxRunner.js +191 -0
  74. package/out/sandbox/support.js +301 -0
  75. package/out/sandbox/types.js +55 -0
  76. package/out/scripts/agentide-litellm.sh +67 -5
  77. package/out/scripts/agentide_litellm_hooks_v1.py +182 -21
  78. package/out/scripts/analytics/agentide-litellm-otel.config.yaml +94 -132
  79. package/out/scripts/analytics/setup-analytics.ps1 +37 -13
  80. package/out/scripts/analytics/setup-analytics.sh +93 -13
  81. package/out/scripts/analytics/start-analytics.sh +86 -14
  82. package/out/scripts/gateway/setup-gateway.sh +72 -32
  83. package/out/shared/models.js +11 -110
  84. package/out/spotlight/install.js +7 -1
  85. package/out/store/index.js +41 -10
  86. package/out/sync.js +3 -1
  87. package/out/test/analytics.test.js +162 -23
  88. package/out/test/auth-baseurl.test.js +132 -0
  89. package/out/test/auth-bootstrapApply.test.js +27 -6
  90. package/out/test/auth-broker-hardening.test.js +227 -0
  91. package/out/test/auth-broker.test.js +59 -9
  92. package/out/test/branding.test.js +13 -0
  93. package/out/test/caps-governor.test.js +455 -0
  94. package/out/test/codex-new-file-survival.test.js +119 -0
  95. package/out/test/configuration-scopes.test.js +131 -0
  96. package/out/test/data-plane.test.js +67 -0
  97. package/out/test/deploy-init.test.js +40 -0
  98. package/out/test/gateway-client.test.js +28 -0
  99. package/out/test/gateway-install-source.test.js +111 -0
  100. package/out/test/gateway-lifecycle.test.js +1 -1
  101. package/out/test/gateway-outbound.test.js +7 -0
  102. package/out/test/gateway-secrets-preflight.test.js +127 -0
  103. package/out/test/gateway-session-routing.test.js +18 -0
  104. package/out/test/governor-hardening.test.js +217 -0
  105. package/out/test/headless-sync.test.js +22 -0
  106. package/out/test/lease-signature.test.js +357 -0
  107. package/out/test/lease-trust-root.test.js +273 -0
  108. package/out/test/lease-validation.test.js +169 -0
  109. package/out/test/lease.test.js +114 -0
  110. package/out/test/mcp.test.js +27 -1
  111. package/out/test/pack-admission.test.js +216 -0
  112. package/out/test/pack-digest-parity.test.js +140 -0
  113. package/out/test/pack-reconcile.test.js +183 -0
  114. package/out/test/pack-signature-artifact.test.js +103 -0
  115. package/out/test/pack-signature.test.js +176 -0
  116. package/out/test/pack-trust-root.test.js +202 -0
  117. package/out/test/remote-trust.test.js +242 -0
  118. package/out/test/runner-protocol.test.js +249 -0
  119. package/out/test/sandbox-driver.test.js +553 -0
  120. package/out/test/sandbox-hardening.test.js +395 -0
  121. package/out/test/sandbox-runner.test.js +343 -0
  122. package/out/test/serve-hardening.test.js +45 -6
  123. package/out/test/serveClient.test.js +6 -5
  124. package/out/test/sessionShip.test.js +560 -0
  125. package/out/test/spotlightPublish.test.js +28 -0
  126. package/out/test/store-install-safe.test.js +69 -0
  127. package/out/test/tls-bypass.test.js +90 -0
  128. package/out/test/warrant.test.js +138 -0
  129. package/out/utils.js +46 -9
  130. package/package.json +1 -1
@@ -0,0 +1,227 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) AgentHippo.ai. All rights reserved.
4
+ *
5
+ * The broker mints tokens. These tests pin the three things that stopped it from minting them for
6
+ * whoever asked: a bind that is not every interface, a proof that the identity header came from
7
+ * the trusted proxy, and a redirect_uri allow-list that admits only our own clients.
8
+ *--------------------------------------------------------------------------------------------*/
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ require("mocha");
44
+ const assert = __importStar(require("assert"));
45
+ const server_1 = require("../auth-broker/server");
46
+ const config_1 = require("../auth-broker/config");
47
+ const types_1 = require("../auth/types");
48
+ const pkce_1 = require("../auth-broker/pkce");
49
+ function prodConfig(over = {}) {
50
+ return {
51
+ port: 0,
52
+ publicBaseUrl: 'https://auth.example.com',
53
+ jwtSecret: 'a-real-secret-not-the-default',
54
+ accessTokenTtlSec: 3600,
55
+ refreshTokenTtlSec: 86400,
56
+ allowedEmailDomains: ['example.com'],
57
+ devMode: false,
58
+ rateLimitPerMin: 0,
59
+ logPii: false,
60
+ cookieName: 'ah_token',
61
+ cookieDomain: '.example.com',
62
+ idpBaseUrl: '',
63
+ proxySecret: 'proxy-shared-secret',
64
+ trustProxyHeadersUnverified: false,
65
+ teamConfigured: true,
66
+ team: {
67
+ id: 'team_test',
68
+ name: 'Test Team',
69
+ storeRepo: 'acme/agent-store',
70
+ storeIndexUrl: 'https://acme.github.io/agent-store',
71
+ githubOrg: 'acme',
72
+ gatewayUrl: 'https://gateway.example.com/v1',
73
+ },
74
+ ...over,
75
+ };
76
+ }
77
+ suite('auth-broker bind address', () => {
78
+ test('defaults to loopback, not every interface', () => {
79
+ const decision = (0, config_1.resolveAuthBrokerBind)({});
80
+ assert.strictEqual(decision.address, config_1.DEFAULT_AUTH_BIND);
81
+ assert.strictEqual(decision.address, '127.0.0.1');
82
+ assert.strictEqual(decision.isPublic, false);
83
+ });
84
+ test('an explicit loopback bind needs no extra flag', () => {
85
+ const decision = (0, config_1.resolveAuthBrokerBind)({ AGENTHIPPO_AUTH_BIND: '::1' });
86
+ assert.strictEqual(decision.isPublic, false);
87
+ });
88
+ test('a public bind is REFUSED unless separately opted into', () => {
89
+ assert.throws(() => (0, config_1.resolveAuthBrokerBind)({ AGENTHIPPO_AUTH_BIND: '0.0.0.0' }), /Refusing to bind/);
90
+ assert.throws(() => (0, config_1.resolveAuthBrokerBind)({ AGENTHIPPO_AUTH_BIND: '10.0.0.5' }), /AGENTHIPPO_AUTH_ALLOW_PUBLIC_BIND/);
91
+ });
92
+ test('the opt-in works and reports that the bind is public', () => {
93
+ const decision = (0, config_1.resolveAuthBrokerBind)({
94
+ AGENTHIPPO_AUTH_BIND: '0.0.0.0',
95
+ AGENTHIPPO_AUTH_ALLOW_PUBLIC_BIND: '1',
96
+ });
97
+ assert.strictEqual(decision.address, '0.0.0.0');
98
+ assert.strictEqual(decision.isPublic, true);
99
+ });
100
+ });
101
+ suite('auth-broker trusted-proxy proof', () => {
102
+ test('a matching secret passes', () => {
103
+ assert.strictEqual((0, server_1.hasTrustedProxyProof)({ [config_1.AUTH_PROXY_SECRET_HEADER]: 'shhh' }, 'shhh'), true);
104
+ });
105
+ test('a missing, empty, or wrong secret fails', () => {
106
+ assert.strictEqual((0, server_1.hasTrustedProxyProof)({}, 'shhh'), false);
107
+ assert.strictEqual((0, server_1.hasTrustedProxyProof)({ [config_1.AUTH_PROXY_SECRET_HEADER]: '' }, 'shhh'), false);
108
+ assert.strictEqual((0, server_1.hasTrustedProxyProof)({ [config_1.AUTH_PROXY_SECRET_HEADER]: 'nope' }, 'shhh'), false);
109
+ });
110
+ test('an unconfigured expected secret can never be satisfied', () => {
111
+ // Fail closed: "no secret configured" must not mean "any secret matches".
112
+ assert.strictEqual((0, server_1.hasTrustedProxyProof)({ [config_1.AUTH_PROXY_SECRET_HEADER]: 'anything' }, ''), false);
113
+ });
114
+ test('a length mismatch does not throw (it would leak the secret length)', () => {
115
+ assert.strictEqual((0, server_1.hasTrustedProxyProof)({ [config_1.AUTH_PROXY_SECRET_HEADER]: 'x' }, 'a-much-longer-secret'), false);
116
+ });
117
+ });
118
+ suite('auth-broker boot gate', () => {
119
+ test('refuses to boot in production without a proxy secret', () => {
120
+ assert.throws(() => (0, server_1.createAuthBrokerServer)(prodConfig({ proxySecret: '' })), /AGENTHIPPO_AUTH_PROXY_SECRET must be set/);
121
+ });
122
+ test('the explicit unverified opt-in allows boot', () => {
123
+ const broker = (0, server_1.createAuthBrokerServer)(prodConfig({ proxySecret: '', trustProxyHeadersUnverified: true }));
124
+ assert.ok(broker.server);
125
+ });
126
+ test('the pre-existing default-secret gates are untouched', () => {
127
+ assert.throws(() => (0, server_1.createAuthBrokerServer)(prodConfig({ jwtSecret: config_1.DEFAULT_JWT_SECRET })), /AGENTHIPPO_AUTH_JWT_SECRET must be set/);
128
+ });
129
+ test('dev mode is still gated on the explicit flag, not NODE_ENV', () => {
130
+ const asTest = (0, config_1.loadAuthBrokerConfig)({ NODE_ENV: 'test' });
131
+ assert.strictEqual(asTest.devMode, false);
132
+ const explicit = (0, config_1.loadAuthBrokerConfig)({ AGENTHIPPO_AUTH_DEV: '1' });
133
+ assert.strictEqual(explicit.devMode, true);
134
+ });
135
+ });
136
+ suite('auth-broker forged X-Auth-Request-Email', () => {
137
+ async function listen(config) {
138
+ const broker = (0, server_1.createAuthBrokerServer)(config);
139
+ await new Promise((resolve) => broker.server.listen(0, '127.0.0.1', resolve));
140
+ const address = broker.server.address();
141
+ if (!address || typeof address === 'string') {
142
+ throw new Error('bad listen address');
143
+ }
144
+ return { baseUrl: `http://127.0.0.1:${address.port}`, close: () => broker.close() };
145
+ }
146
+ function authorizeUrl(baseUrl) {
147
+ const challenge = (0, pkce_1.codeChallengeFromVerifier)((0, pkce_1.generateCodeVerifier)());
148
+ return `${baseUrl}/oauth/authorize?client_id=${types_1.AUTH_CLIENT_ID}`
149
+ + `&redirect_uri=${encodeURIComponent('http://127.0.0.1:54123/callback')}`
150
+ + `&code_challenge=${challenge}&code_challenge_method=S256&state=x`;
151
+ }
152
+ test('an unproven email header gets 401, not an authorization code', async () => {
153
+ const server = await listen(prodConfig());
154
+ try {
155
+ const res = await fetch(authorizeUrl(server.baseUrl), {
156
+ redirect: 'manual',
157
+ headers: { 'X-Auth-Request-Email': 'ceo@example.com' },
158
+ });
159
+ assert.strictEqual(res.status, 401);
160
+ assert.strictEqual(res.headers.get('location'), null, 'no auth code was handed out');
161
+ const body = await res.json();
162
+ assert.ok(body.error_description?.includes(config_1.AUTH_PROXY_SECRET_HEADER));
163
+ }
164
+ finally {
165
+ await server.close();
166
+ }
167
+ });
168
+ test('the same request WITH the proxy proof succeeds', async () => {
169
+ const server = await listen(prodConfig());
170
+ try {
171
+ const res = await fetch(authorizeUrl(server.baseUrl), {
172
+ redirect: 'manual',
173
+ headers: {
174
+ 'X-Auth-Request-Email': 'user@example.com',
175
+ [config_1.AUTH_PROXY_SECRET_HEADER]: 'proxy-shared-secret',
176
+ },
177
+ });
178
+ assert.strictEqual(res.status, 302);
179
+ const location = res.headers.get('location') || '';
180
+ assert.ok(new URL(location).searchParams.get('code'), 'a code is issued to the proven proxy');
181
+ }
182
+ finally {
183
+ await server.close();
184
+ }
185
+ });
186
+ test('/oauth/browser-login is gated the same way', async () => {
187
+ const server = await listen(prodConfig());
188
+ try {
189
+ const res = await fetch(`${server.baseUrl}/oauth/browser-login`, {
190
+ redirect: 'manual',
191
+ headers: { 'X-Auth-Request-Email': 'ceo@example.com' },
192
+ });
193
+ assert.strictEqual(res.status, 401);
194
+ assert.strictEqual(res.headers.get('set-cookie'), null, 'no session cookie was minted');
195
+ }
196
+ finally {
197
+ await server.close();
198
+ }
199
+ });
200
+ });
201
+ suite('auth-broker redirect_uri allow-list', () => {
202
+ test('accepts the shapes real clients send', () => {
203
+ assert.ok((0, server_1.isAllowedRedirectUri)('http://127.0.0.1:54123/callback'));
204
+ assert.ok((0, server_1.isAllowedRedirectUri)('http://localhost:61999/callback'));
205
+ assert.ok((0, server_1.isAllowedRedirectUri)('vscode://agenthippo.agenthippo/auth/callback'));
206
+ });
207
+ test('rejects an attacker-controlled destination', () => {
208
+ assert.ok(!(0, server_1.isAllowedRedirectUri)('https://attacker.example/collect'));
209
+ assert.ok(!(0, server_1.isAllowedRedirectUri)('http://attacker.example/callback'));
210
+ assert.ok(!(0, server_1.isAllowedRedirectUri)('http://127.0.0.1.attacker.example:8080/callback'));
211
+ });
212
+ test('rejects a permitted shape carrying a rider', () => {
213
+ // A query or fragment makes the destination different from the one bound to the code.
214
+ assert.ok(!(0, server_1.isAllowedRedirectUri)('http://127.0.0.1:54123/callback?next=https://attacker.example'));
215
+ assert.ok(!(0, server_1.isAllowedRedirectUri)('http://127.0.0.1:54123/callback#x'));
216
+ assert.ok(!(0, server_1.isAllowedRedirectUri)('http://user:pass@127.0.0.1:54123/callback'));
217
+ });
218
+ test('rejects a privileged or absent loopback port', () => {
219
+ // The loopback server binds an OS-assigned ephemeral port; port 80 or 22 is not it.
220
+ assert.ok(!(0, server_1.isAllowedRedirectUri)('http://127.0.0.1/callback'));
221
+ assert.ok(!(0, server_1.isAllowedRedirectUri)('http://127.0.0.1:22/callback'));
222
+ });
223
+ test('rejects a wrong path and a malformed URI', () => {
224
+ assert.ok(!(0, server_1.isAllowedRedirectUri)('http://127.0.0.1:54123/other'));
225
+ assert.ok(!(0, server_1.isAllowedRedirectUri)('not a uri'));
226
+ });
227
+ });
@@ -56,14 +56,16 @@ function testConfig(port) {
56
56
  cookieName: 'ah_token',
57
57
  cookieDomain: '.agenthippo.ai',
58
58
  idpBaseUrl: 'http://127.0.0.1:0',
59
+ proxySecret: '',
60
+ trustProxyHeadersUnverified: false,
61
+ teamConfigured: true,
59
62
  team: {
60
63
  id: 'team_test',
61
64
  name: 'Test Team',
62
65
  storeRepo: 'acme/agent-store',
63
66
  storeIndexUrl: 'https://acme.github.io/agent-store',
64
67
  githubOrg: 'acme',
65
- litellmBaseUrl: 'http://127.0.0.1:4000',
66
- litellmApiKey: 'ahk_test_key',
68
+ gatewayUrl: 'http://127.0.0.1:3456/v1',
67
69
  },
68
70
  };
69
71
  }
@@ -120,7 +122,9 @@ suite('AuthBroker', () => {
120
122
  assert.strictEqual(bootstrapRes.status, 200);
121
123
  const bootstrap = await bootstrapRes.json();
122
124
  assert.strictEqual(bootstrap.store.repo, 'acme/agent-store');
123
- assert.strictEqual(bootstrap.routing.litellmBaseUrl, 'http://127.0.0.1:4000');
125
+ assert.strictEqual(bootstrap.routing.inferenceGatewayUrl, 'http://127.0.0.1:3456/v1');
126
+ // The bootstrap carries no routing credential — the account JWT is the credential.
127
+ assert.strictEqual(bootstrap.routing.litellmApiKey, undefined);
124
128
  assert.strictEqual(bootstrap.team.id, 'team_test');
125
129
  const refreshRes = await postForm(`${baseUrl}/oauth/token`, {
126
130
  grant_type: 'refresh_token',
@@ -163,7 +167,7 @@ suite('AuthBroker', () => {
163
167
  await broker.close();
164
168
  }
165
169
  });
166
- test('bootstrap refresh re-issues routing credentials', async () => {
170
+ test('bootstrap refresh endpoint is gone — routing carries no credential to re-issue', async () => {
167
171
  const broker = (0, server_js_1.createAuthBrokerServer)(testConfig(0));
168
172
  await new Promise((resolve) => broker.server.listen(0, '127.0.0.1', resolve));
169
173
  const address = broker.server.address();
@@ -198,11 +202,57 @@ suite('AuthBroker', () => {
198
202
  method: 'POST',
199
203
  headers: { Authorization: `Bearer ${tokens.access_token}` },
200
204
  });
201
- assert.strictEqual(refreshRes.status, 200);
202
- const body = await refreshRes.json();
203
- assert.strictEqual(body.routing.litellmApiKey, 'ahk_test_key');
204
- // Static team key — the broker must not fabricate an expiry it can't enforce.
205
- assert.strictEqual(body.routing.litellmApiKeyExpiresAt, undefined);
205
+ assert.strictEqual(refreshRes.status, 404);
206
+ }
207
+ finally {
208
+ await broker.close();
209
+ }
210
+ });
211
+ test('solo deployment omits team in bootstrap and team_id in JWT', async () => {
212
+ const broker = (0, server_js_1.createAuthBrokerServer)({
213
+ ...testConfig(0),
214
+ teamConfigured: false,
215
+ });
216
+ await new Promise((resolve) => broker.server.listen(0, '127.0.0.1', resolve));
217
+ const address = broker.server.address();
218
+ if (!address || typeof address === 'string') {
219
+ throw new Error('bad listen address');
220
+ }
221
+ const baseUrl = `http://127.0.0.1:${address.port}`;
222
+ try {
223
+ const verifier = (0, pkce_js_1.generateCodeVerifier)();
224
+ const challenge = (0, pkce_js_1.codeChallengeFromVerifier)(verifier);
225
+ const redirectUri = 'agenthippo://auth/callback';
226
+ const devRes = await fetch(`${baseUrl}/oauth/dev/login`, {
227
+ method: 'POST',
228
+ headers: { 'Content-Type': 'application/json' },
229
+ body: JSON.stringify({
230
+ email: 'solo@agenthippo.ai',
231
+ client_id: types_js_1.AUTH_CLIENT_ID,
232
+ redirect_uri: redirectUri,
233
+ code_challenge: challenge,
234
+ }),
235
+ });
236
+ const devJson = await devRes.json();
237
+ const tokenRes = await postForm(`${baseUrl}/oauth/token`, {
238
+ grant_type: 'authorization_code',
239
+ client_id: types_js_1.AUTH_CLIENT_ID,
240
+ code: devJson.code,
241
+ redirect_uri: redirectUri,
242
+ code_verifier: verifier,
243
+ });
244
+ assert.strictEqual(tokenRes.status, 200);
245
+ const tokens = await tokenRes.json();
246
+ const payload = (0, jwt_js_1.verifyJwt)(tokens.access_token, 'test-jwt-secret');
247
+ assert.ok(payload);
248
+ assert.strictEqual(payload.team_id, undefined);
249
+ const bootstrapRes = await fetch(`${baseUrl}/api/ide/bootstrap`, {
250
+ headers: { Authorization: `Bearer ${tokens.access_token}` },
251
+ });
252
+ assert.strictEqual(bootstrapRes.status, 200);
253
+ const bootstrap = await bootstrapRes.json();
254
+ assert.strictEqual(bootstrap.team, undefined);
255
+ assert.deepStrictEqual(bootstrap.policy.teamManagedSettings, []);
206
256
  }
207
257
  finally {
208
258
  await broker.close();
@@ -143,6 +143,19 @@ suite('brandify', () => {
143
143
  });
144
144
  });
145
145
  // =============================================================================
146
+ // toSettingsDisplayKey Tests
147
+ // =============================================================================
148
+ suite('toSettingsDisplayKey', () => {
149
+ test('maps agentide.* keys to agenthippoai.* for user-facing copy', () => {
150
+ assert.strictEqual((0, branding_1.toSettingsDisplayKey)('agentide.anywhere.installSource'), 'agenthippoai.anywhere.installSource');
151
+ assert.strictEqual((0, branding_1.toSettingsDisplayKey)('agentide.cli.path'), 'agenthippoai.cli.path');
152
+ assert.strictEqual((0, branding_1.toSettingsDisplayKey)('agentide.providers.custom.acme.baseUrl'), 'agenthippoai.providers.custom.acme.baseUrl');
153
+ });
154
+ test('leaves non-agentide keys unchanged', () => {
155
+ assert.strictEqual((0, branding_1.toSettingsDisplayKey)('editor.fontSize'), 'editor.fontSize');
156
+ });
157
+ });
158
+ // =============================================================================
146
159
  // copyDirRecursiveWithTransform Tests
147
160
  // =============================================================================
148
161
  suite('copyDirRecursiveWithTransform', () => {