agent-shim 8.15.0 → 8.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -14464,7 +14464,7 @@ var require_client = __commonJS({
14464
14464
  "use strict";
14465
14465
  var assert2 = require("node:assert");
14466
14466
  var net3 = require("node:net");
14467
- var http6 = require("node:http");
14467
+ var http7 = require("node:http");
14468
14468
  var util = require_util2();
14469
14469
  var { ClientStats } = require_stats();
14470
14470
  var { channels } = require_diagnostics();
@@ -14522,7 +14522,7 @@ var require_client = __commonJS({
14522
14522
  var connectH1 = require_client_h1();
14523
14523
  var connectH2 = require_client_h2();
14524
14524
  var kClosedResolve = /* @__PURE__ */ Symbol("kClosedResolve");
14525
- var getDefaultNodeMaxHeaderSize = http6 && http6.maxHeaderSize && Number.isInteger(http6.maxHeaderSize) && http6.maxHeaderSize > 0 ? () => http6.maxHeaderSize : () => {
14525
+ var getDefaultNodeMaxHeaderSize = http7 && http7.maxHeaderSize && Number.isInteger(http7.maxHeaderSize) && http7.maxHeaderSize > 0 ? () => http7.maxHeaderSize : () => {
14526
14526
  throw new InvalidArgumentError2("http module not available or http.maxHeaderSize invalid");
14527
14527
  };
14528
14528
  var noop = () => {
@@ -55242,6 +55242,8 @@ var ProviderEnvSchema = external_exports.record(external_exports.string().min(1)
55242
55242
  var PROVIDER_KINDS = ["http", "codex"];
55243
55243
  var CODEX_TIERS = ["fable", "opus", "sonnet", "haiku"];
55244
55244
  var CODEX_EFFORTS = ["none", "low", "medium", "high"];
55245
+ var CODEX_LOGINS = ["codex-cli", "chatgpt-sign-in"];
55246
+ var CODEX_DEFAULT_LOGIN = "codex-cli";
55245
55247
  var CODEX_DEFAULT_MODEL = "gpt-5.6-sol";
55246
55248
  var CODEX_DEFAULT_TIER_MODELS = Object.freeze({
55247
55249
  fable: "gpt-5.6-sol",
@@ -55261,7 +55263,9 @@ var CodexProviderConfigSchema = external_exports.strictObject({
55261
55263
  haiku: external_exports.string().min(1).optional()
55262
55264
  }).optional(),
55263
55265
  /** The reasoning effort used when the request's own `output_config.effort` is not one the backend accepts. */
55264
- effort: external_exports.enum(CODEX_EFFORTS).optional()
55266
+ effort: external_exports.enum(CODEX_EFFORTS).optional(),
55267
+ /** Which login the upstream call is made with; see `CODEX_LOGINS`. */
55268
+ login: external_exports.enum(CODEX_LOGINS).optional()
55265
55269
  });
55266
55270
  var HttpProviderSchema = external_exports.strictObject({
55267
55271
  $schema: external_exports.string().optional(),
@@ -60278,6 +60282,7 @@ function buildLayoutPaths(root) {
60278
60282
  frontdoorCaCertFile: import_node_path19.default.join(root, "frontdoor", "ca", "ca.pem"),
60279
60283
  frontdoorCaKeyFile: import_node_path19.default.join(root, "frontdoor", "ca", "ca.key"),
60280
60284
  frontdoorCaBundlesDir: import_node_path19.default.join(root, "frontdoor", "ca", "bundles"),
60285
+ chatgptSignInFile: import_node_path19.default.join(root, "codex", "chatgpt-sign-in.json"),
60281
60286
  frontdoorStateFile: import_node_path19.default.join(root, "frontdoor", "state.json"),
60282
60287
  frontdoorLockFile: import_node_path19.default.join(root, "frontdoor", "start.lock"),
60283
60288
  frontdoorControlTokenFile: import_node_path19.default.join(root, "frontdoor", "control-token"),
@@ -61744,7 +61749,7 @@ function registerIdentityCommand(program2, deps) {
61744
61749
  // package.json
61745
61750
  var package_default = {
61746
61751
  name: "agent-shim",
61747
- version: "8.15.0",
61752
+ version: "8.17.0",
61748
61753
  description: "A profile manager and launcher for Claude Code that lets one person run multiple logins from one machine while controlling what gets shared between them.",
61749
61754
  license: "MIT",
61750
61755
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -63484,7 +63489,7 @@ var ProviderKindMismatchError = class extends UsageError {
63484
63489
  }
63485
63490
  };
63486
63491
  function isEmptyCodexConfig(config2) {
63487
- return config2 === void 0 || config2.defaultModel === void 0 && config2.effort === void 0 && (config2.models === void 0 || Object.keys(config2.models).length === 0);
63492
+ return config2 === void 0 || config2.defaultModel === void 0 && config2.effort === void 0 && config2.login === void 0 && (config2.models === void 0 || Object.keys(config2.models).length === 0);
63488
63493
  }
63489
63494
  function addProvider(paths, name, input2) {
63490
63495
  if (providerExists(paths, name)) {
@@ -64121,7 +64126,7 @@ function registerCheckCommand(program2, deps) {
64121
64126
  }
64122
64127
 
64123
64128
  // src/codex/commands.ts
64124
- var import_node_crypto7 = require("node:crypto");
64129
+ var import_node_crypto9 = require("node:crypto");
64125
64130
  var import_node_fs11 = __toESM(require("node:fs"), 1);
64126
64131
  var import_node_os6 = __toESM(require("node:os"), 1);
64127
64132
  var import_node_path31 = __toESM(require("node:path"), 1);
@@ -64272,8 +64277,273 @@ function createCodexAuthStore(authPath, ports) {
64272
64277
  };
64273
64278
  }
64274
64279
 
64275
- // src/codex/upstream.ts
64280
+ // src/codex/siwc.ts
64276
64281
  var import_node_crypto6 = require("node:crypto");
64282
+ var SIWC_AUTHORIZE_URL = "https://auth.openai.com/api/accounts/authorize";
64283
+ var SIWC_TOKEN_URL = "https://auth.openai.com/api/accounts/oauth/token";
64284
+ var SIWC_REVOKE_URL = "https://auth.openai.com/api/accounts/oauth/revoke";
64285
+ var SIWC_ISSUER = "https://auth.openai.com";
64286
+ var SIWC_JWKS_URL = "https://auth.openai.com/.well-known/jwks.json";
64287
+ var SIWC_DYNAMIC_CLIENT_ID = "dynamic_agent_client";
64288
+ var SIWC_RESOURCE = "https://api.openai.com/v1";
64289
+ var SIWC_RESPONSES_URL = `${SIWC_RESOURCE}/responses`;
64290
+ var SIWC_SCOPES = ["openid", "profile", "email", "offline_access", "resource.invoke", "chatgpt.tokens.use.direct"];
64291
+ var SIWC_PLAN_SCOPE = "chatgpt.tokens.use.direct";
64292
+ var SIWC_CALLBACK_PORT = 1455;
64293
+ var SIWC_CALLBACK_PATH = "/auth/callback";
64294
+ var SIWC_RANDOM_BYTES = 32;
64295
+ var MS_PER_SECOND4 = 1e3;
64296
+ var ID_TOKEN_ALGORITHM = "RS256";
64297
+ var SiwcError = class extends Error {
64298
+ constructor(message) {
64299
+ super(message);
64300
+ this.name = "SiwcError";
64301
+ }
64302
+ };
64303
+ var SiwcInvalidGrantError = class extends SiwcError {
64304
+ constructor(message) {
64305
+ super(message);
64306
+ this.name = "SiwcInvalidGrantError";
64307
+ }
64308
+ };
64309
+ function base64Url(bytes) {
64310
+ return Buffer.from(bytes).toString("base64url");
64311
+ }
64312
+ function newPkce(randomBytes4) {
64313
+ const verifier = base64Url(randomBytes4(SIWC_RANDOM_BYTES));
64314
+ return { verifier, challenge: (0, import_node_crypto6.createHash)("sha256").update(verifier).digest("base64url") };
64315
+ }
64316
+ function newOpaqueValue(randomBytes4) {
64317
+ return base64Url(randomBytes4(SIWC_RANDOM_BYTES));
64318
+ }
64319
+ function buildAuthorizeUrl(request7) {
64320
+ const url2 = new URL(SIWC_AUTHORIZE_URL);
64321
+ const params = url2.searchParams;
64322
+ params.set("client_id", request7.clientId ?? SIWC_DYNAMIC_CLIENT_ID);
64323
+ if (request7.clientId === void 0) {
64324
+ params.set("agent_name_hint", request7.agentName);
64325
+ }
64326
+ if (request7.returning !== void 0) {
64327
+ params.set("id_token_hint", request7.returning.idToken);
64328
+ if (request7.returning.loginHint !== void 0) {
64329
+ params.set("login_hint", request7.returning.loginHint);
64330
+ }
64331
+ }
64332
+ params.set("response_type", "code");
64333
+ params.set("redirect_uri", request7.redirectUri);
64334
+ params.set("scope", SIWC_SCOPES.join(" "));
64335
+ params.set("resource", SIWC_RESOURCE);
64336
+ params.set("state", request7.state);
64337
+ params.set("nonce", request7.nonce);
64338
+ params.set("code_challenge_method", "S256");
64339
+ params.set("code_challenge", request7.challenge);
64340
+ return url2.toString();
64341
+ }
64342
+ function parseCallback(url2, expectedState) {
64343
+ const params = url2.searchParams;
64344
+ const error62 = params.get("error");
64345
+ if (error62 !== null) {
64346
+ throw new SiwcError(`the sign-in was refused: ${error62}${params.has("error_description") ? ` (${params.get("error_description") ?? ""})` : ""}`);
64347
+ }
64348
+ if (params.get("state") !== expectedState) {
64349
+ throw new SiwcError("the sign-in callback's state did not match the request, so it was discarded");
64350
+ }
64351
+ const code = params.get("code");
64352
+ if (code === null || code === "") {
64353
+ throw new SiwcError("the sign-in callback carried no authorization code");
64354
+ }
64355
+ const scope = params.get("scope");
64356
+ return { code, clientId: params.get("client_id") ?? void 0, scopes: scope === null ? [] : scope.split(" ").filter((part) => part !== "") };
64357
+ }
64358
+ var TokenResponseSchema = external_exports.looseObject({
64359
+ access_token: external_exports.string().min(1),
64360
+ refresh_token: external_exports.string().min(1).optional(),
64361
+ id_token: external_exports.string().min(1).optional(),
64362
+ expires_in: external_exports.number().positive(),
64363
+ scope: external_exports.string().optional()
64364
+ });
64365
+ var ERROR_BODY_CHARS = 300;
64366
+ var TOKEN_CALL_TIMEOUT_MS = 3e4;
64367
+ async function postToken(ports, form, what) {
64368
+ const response = await ports.fetch(SIWC_TOKEN_URL, {
64369
+ method: "POST",
64370
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
64371
+ body: new URLSearchParams({ ...form, resource: SIWC_RESOURCE }).toString(),
64372
+ signal: AbortSignal.timeout(TOKEN_CALL_TIMEOUT_MS)
64373
+ });
64374
+ if (!response.ok) {
64375
+ const detail = (await response.text()).slice(0, ERROR_BODY_CHARS);
64376
+ const message = `the ${what} failed with HTTP ${String(response.status)}: ${detail}`;
64377
+ throw /"error"\s*:\s*"invalid_grant"/.test(detail) ? new SiwcInvalidGrantError(message) : new SiwcError(message);
64378
+ }
64379
+ const parsed = TokenResponseSchema.safeParse(await response.json());
64380
+ if (!parsed.success) {
64381
+ throw new SiwcError(`the ${what} answered without an access token and a lifetime`);
64382
+ }
64383
+ return parsed.data;
64384
+ }
64385
+ function grantTokens(ports, answer) {
64386
+ return {
64387
+ accessToken: answer.access_token,
64388
+ refreshToken: answer.refresh_token,
64389
+ idToken: answer.id_token,
64390
+ scopes: answer.scope === void 0 ? void 0 : answer.scope.split(" ").filter((part) => part !== ""),
64391
+ expiresAt: ports.now().getTime() + answer.expires_in * MS_PER_SECOND4
64392
+ };
64393
+ }
64394
+ async function exchangeCode(ports, request7) {
64395
+ return grantTokens(
64396
+ ports,
64397
+ await postToken(ports, { grant_type: "authorization_code", client_id: request7.clientId, code: request7.code, code_verifier: request7.verifier, redirect_uri: request7.redirectUri }, "authorisation code exchange")
64398
+ );
64399
+ }
64400
+ async function refreshTokens(ports, request7) {
64401
+ return grantTokens(ports, await postToken(ports, { grant_type: "refresh_token", client_id: request7.clientId, refresh_token: request7.refreshToken }, "token refresh"));
64402
+ }
64403
+ var JwtHeaderSchema = external_exports.looseObject({ alg: external_exports.string(), kid: external_exports.string().optional() });
64404
+ var IdTokenClaimsSchema = external_exports.looseObject({
64405
+ iss: external_exports.string(),
64406
+ sub: external_exports.string().min(1),
64407
+ aud: external_exports.union([external_exports.string(), external_exports.array(external_exports.string())]),
64408
+ exp: external_exports.number(),
64409
+ nonce: external_exports.string().optional(),
64410
+ email: external_exports.string().optional()
64411
+ });
64412
+ var JwksSchema = external_exports.object({ keys: external_exports.array(external_exports.looseObject({ kid: external_exports.string().optional(), kty: external_exports.string() })) });
64413
+ function decodeSegment(segment) {
64414
+ if (segment === void 0) {
64415
+ throw new SiwcError("the ID token is not a three-part JWT");
64416
+ }
64417
+ try {
64418
+ const parsed = JSON.parse(Buffer.from(segment, "base64url").toString("utf8"));
64419
+ return parsed;
64420
+ } catch {
64421
+ throw new SiwcError("the ID token has an unreadable segment");
64422
+ }
64423
+ }
64424
+ async function verifyIdToken(ports, idToken, expected) {
64425
+ const [headerSegment, payloadSegment, signatureSegment, ...rest] = idToken.split(".");
64426
+ if (rest.length > 0 || headerSegment === void 0 || payloadSegment === void 0 || signatureSegment === void 0) {
64427
+ throw new SiwcError("the ID token is not a three-part JWT");
64428
+ }
64429
+ const header = JwtHeaderSchema.parse(decodeSegment(headerSegment));
64430
+ if (header.alg !== ID_TOKEN_ALGORITHM) {
64431
+ throw new SiwcError(`the ID token is signed with ${header.alg}, and only ${ID_TOKEN_ALGORITHM} is accepted`);
64432
+ }
64433
+ const response = await ports.fetch(SIWC_JWKS_URL, { method: "GET", headers: {}, signal: AbortSignal.timeout(TOKEN_CALL_TIMEOUT_MS) });
64434
+ if (!response.ok) {
64435
+ throw new SiwcError(`the issuer's key set could not be fetched (HTTP ${String(response.status)})`);
64436
+ }
64437
+ const jwk = JwksSchema.parse(await response.json()).keys.find((key) => key.kty === "RSA" && (header.kid === void 0 || key.kid === header.kid));
64438
+ if (jwk === void 0) {
64439
+ throw new SiwcError("the issuer's key set has no key for the ID token's signature");
64440
+ }
64441
+ const signed = Buffer.from(`${headerSegment}.${payloadSegment}`);
64442
+ if (!(0, import_node_crypto6.verify)("sha256", signed, (0, import_node_crypto6.createPublicKey)({ key: jwk, format: "jwk" }), Buffer.from(signatureSegment, "base64url"))) {
64443
+ throw new SiwcError("the ID token's signature did not verify");
64444
+ }
64445
+ const claims = IdTokenClaimsSchema.parse(decodeSegment(payloadSegment));
64446
+ if (claims.iss !== SIWC_ISSUER) {
64447
+ throw new SiwcError(`the ID token was issued by ${claims.iss}, not ${SIWC_ISSUER}`);
64448
+ }
64449
+ if (!(Array.isArray(claims.aud) ? claims.aud : [claims.aud]).includes(expected.clientId)) {
64450
+ throw new SiwcError("the ID token was issued for a different client");
64451
+ }
64452
+ if (claims.exp * MS_PER_SECOND4 <= ports.now().getTime()) {
64453
+ throw new SiwcError("the ID token has already expired");
64454
+ }
64455
+ if (expected.nonce !== void 0 && claims.nonce !== expected.nonce) {
64456
+ throw new SiwcError("the ID token's nonce did not match the request");
64457
+ }
64458
+ return { sub: claims.sub, email: claims.email };
64459
+ }
64460
+
64461
+ // src/codex/siwcLogin.ts
64462
+ var SIWC_AGENT_NAME = "agent-shim";
64463
+ var SIWC_LOGIN_TIMEOUT_MS = 3e5;
64464
+ async function runSiwcLogin(ports, options) {
64465
+ const existing = ports.store.read();
64466
+ const returning = existing?.grant;
64467
+ for (let attempt = 0; ; attempt += 1) {
64468
+ const clientId = ports.store.read()?.registeredClientId ?? returning?.clientId;
64469
+ try {
64470
+ return await authorizeOnce(ports, options, clientId, attempt === 0 ? returning : void 0);
64471
+ } catch (error62) {
64472
+ if (!(error62 instanceof SiwcInvalidGrantError) || attempt > 0) {
64473
+ throw error62;
64474
+ }
64475
+ ports.print("OpenAI did not accept the first code, which is expected once after registering: authorising again with the registration it issued.");
64476
+ }
64477
+ }
64478
+ }
64479
+ async function authorizeOnce(ports, options, savedClientId, returning) {
64480
+ const pkce = newPkce(ports.siwc.randomBytes);
64481
+ const state = newOpaqueValue(ports.siwc.randomBytes);
64482
+ const nonce = newOpaqueValue(ports.siwc.randomBytes);
64483
+ const redirectUri = `http://127.0.0.1:${String(SIWC_CALLBACK_PORT)}${SIWC_CALLBACK_PATH}`;
64484
+ const url2 = buildAuthorizeUrl({
64485
+ clientId: savedClientId,
64486
+ agentName: SIWC_AGENT_NAME,
64487
+ redirectUri,
64488
+ state,
64489
+ nonce,
64490
+ challenge: pkce.challenge,
64491
+ ...returning === void 0 ? {} : { returning: { idToken: returning.idToken, loginHint: returning.email } }
64492
+ });
64493
+ const listener = await ports.listen(SIWC_CALLBACK_PORT, state);
64494
+ try {
64495
+ ports.print(`Sign in at: ${url2}`);
64496
+ if (options.open) {
64497
+ ports.openBrowser(url2);
64498
+ }
64499
+ const callback = parseCallback(await listener.waitForCallback(AbortSignal.timeout(SIWC_LOGIN_TIMEOUT_MS)), state);
64500
+ const clientId = callback.clientId ?? savedClientId;
64501
+ if (clientId === void 0) {
64502
+ throw new SiwcError("the sign-in callback carried no client id and none was stored from an earlier sign-in");
64503
+ }
64504
+ const grantBefore = ports.store.read()?.grant;
64505
+ ports.store.write({ registeredClientId: clientId, ...grantBefore === void 0 ? {} : { grant: grantBefore } });
64506
+ const tokens = await exchangeCode(ports.siwc, { clientId, code: callback.code, verifier: pkce.verifier, redirectUri });
64507
+ if (tokens.refreshToken === void 0 || tokens.idToken === void 0) {
64508
+ throw new SiwcError("the token endpoint did not return a refresh token and an ID token, so the sign-in cannot be kept");
64509
+ }
64510
+ const scopes = tokens.scopes ?? callback.scopes;
64511
+ if (!scopes.includes(SIWC_PLAN_SCOPE)) {
64512
+ throw new SiwcError("the sign-in was not granted permission to use your ChatGPT plan: run the login again and allow it");
64513
+ }
64514
+ const identity = await verifyIdToken(ports.siwc, tokens.idToken, { clientId, nonce });
64515
+ ports.store.write({
64516
+ registeredClientId: clientId,
64517
+ grant: { clientId, sub: identity.sub, ...identity.email === void 0 ? {} : { email: identity.email }, idToken: tokens.idToken, accessToken: tokens.accessToken, refreshToken: tokens.refreshToken, scopes, expiresAt: tokens.expiresAt }
64518
+ });
64519
+ return { email: identity.email, sub: identity.sub };
64520
+ } finally {
64521
+ listener.close();
64522
+ }
64523
+ }
64524
+ var REVOKE_TIMEOUT_MS = 3e4;
64525
+ async function runSiwcLogout(ports) {
64526
+ const file2 = ports.store.read();
64527
+ if (file2?.grant === void 0) {
64528
+ return { hadGrant: false, revoked: false };
64529
+ }
64530
+ const revoked = await ports.siwc.fetch(SIWC_REVOKE_URL, {
64531
+ method: "POST",
64532
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
64533
+ body: new URLSearchParams({ token: file2.grant.refreshToken, token_type_hint: "refresh_token", client_id: file2.grant.clientId }).toString(),
64534
+ signal: AbortSignal.timeout(REVOKE_TIMEOUT_MS)
64535
+ }).then(
64536
+ (response) => response.ok,
64537
+ () => false
64538
+ );
64539
+ ports.store.write({ registeredClientId: file2.grant.clientId });
64540
+ return { hadGrant: true, revoked };
64541
+ }
64542
+
64543
+ // src/codex/siwcPorts.ts
64544
+ var import_node_child_process4 = require("node:child_process");
64545
+ var import_node_crypto7 = require("node:crypto");
64546
+ var import_node_http = __toESM(require("node:http"), 1);
64277
64547
 
64278
64548
  // src/codex/http.ts
64279
64549
  var HTTP_STATUS = {
@@ -64291,7 +64561,157 @@ var HTTP_STATUS = {
64291
64561
  badGateway: 502
64292
64562
  };
64293
64563
 
64564
+ // src/codex/siwcPorts.ts
64565
+ function realSiwcPorts(fetch3) {
64566
+ return { fetch: fetch3, randomBytes: (size) => (0, import_node_crypto7.randomBytes)(size), now: () => /* @__PURE__ */ new Date() };
64567
+ }
64568
+ var CALLBACK_PAGE = "<!doctype html><meta charset=utf-8><title>Signed in</title><p>You can close this tab and return to the terminal.</p>";
64569
+ async function listenForCallback(port, state) {
64570
+ let deliver;
64571
+ const arrived = new Promise((resolve) => {
64572
+ deliver = resolve;
64573
+ });
64574
+ const server = import_node_http.default.createServer((request7, response) => {
64575
+ const url2 = new URL(request7.url ?? "/", `http://127.0.0.1:${String(port)}`);
64576
+ if (request7.method !== "GET" || url2.pathname !== SIWC_CALLBACK_PATH || url2.searchParams.getAll("state").length !== 1 || url2.searchParams.get("state") !== state) {
64577
+ response.writeHead(HTTP_STATUS.notFound).end("Not found");
64578
+ return;
64579
+ }
64580
+ response.writeHead(HTTP_STATUS.ok, { "Content-Type": "text/html; charset=utf-8" });
64581
+ response.end(CALLBACK_PAGE);
64582
+ deliver?.(url2);
64583
+ });
64584
+ await new Promise((resolve, reject) => {
64585
+ server.once("error", (error62) => {
64586
+ reject(error62.code === "EADDRINUSE" ? new Error(`port ${String(port)} is in use (another sign-in, such as \`codex login\`, may be running): finish or stop it and try again`) : error62);
64587
+ });
64588
+ server.listen(port, "127.0.0.1", () => {
64589
+ resolve();
64590
+ });
64591
+ });
64592
+ return {
64593
+ waitForCallback: async (signal) => await Promise.race([
64594
+ arrived,
64595
+ new Promise((_resolve, reject) => {
64596
+ signal.addEventListener("abort", () => {
64597
+ reject(new Error("timed out waiting for the browser sign-in to finish"));
64598
+ });
64599
+ })
64600
+ ]),
64601
+ close: () => {
64602
+ server.close();
64603
+ server.closeAllConnections();
64604
+ }
64605
+ };
64606
+ }
64607
+ function openInBrowser(url2) {
64608
+ const command = process.platform === "darwin" ? { file: "open", args: [url2] } : process.platform === "win32" ? { file: "cmd", args: ["/c", "start", "", url2] } : { file: "xdg-open", args: [url2] };
64609
+ (0, import_node_child_process4.spawn)(command.file, command.args, { detached: true, stdio: "ignore" }).unref();
64610
+ }
64611
+
64612
+ // src/codex/siwcStore.ts
64613
+ var SiwcGrantSchema = external_exports.strictObject({
64614
+ clientId: external_exports.string().min(1),
64615
+ sub: external_exports.string().min(1),
64616
+ email: external_exports.string().optional(),
64617
+ idToken: external_exports.string().min(1),
64618
+ accessToken: external_exports.string().min(1),
64619
+ refreshToken: external_exports.string().min(1),
64620
+ scopes: external_exports.array(external_exports.string()).readonly(),
64621
+ /** When the access token stops working, in milliseconds since the epoch. */
64622
+ expiresAt: external_exports.number()
64623
+ });
64624
+ var SiwcFileSchema = external_exports.strictObject({ registeredClientId: external_exports.string().min(1).optional(), grant: SiwcGrantSchema.optional() });
64625
+ var SIWC_REFRESH_MARGIN_MS = 6e4;
64626
+ function parseSiwcFile(raw, filePath) {
64627
+ let json2;
64628
+ try {
64629
+ json2 = JSON.parse(raw);
64630
+ } catch {
64631
+ throw new CodexAuthError(`${filePath} is not valid JSON: run \`agent-shim codex login\` again`);
64632
+ }
64633
+ const parsed = SiwcFileSchema.safeParse(json2);
64634
+ if (!parsed.success) {
64635
+ throw new CodexAuthError(`${filePath} is not a Sign in with ChatGPT file: run \`agent-shim codex login\` again`);
64636
+ }
64637
+ return parsed.data;
64638
+ }
64639
+ function grantFrom(previous, tokens) {
64640
+ return {
64641
+ ...previous,
64642
+ accessToken: tokens.accessToken,
64643
+ refreshToken: tokens.refreshToken ?? previous.refreshToken,
64644
+ idToken: tokens.idToken ?? previous.idToken,
64645
+ scopes: tokens.scopes ?? previous.scopes,
64646
+ expiresAt: tokens.expiresAt
64647
+ };
64648
+ }
64649
+ function createSiwcStore(filePath, ports) {
64650
+ let cached2;
64651
+ let inFlight;
64652
+ const read = () => {
64653
+ const raw = ports.fs.read(filePath);
64654
+ return raw === void 0 ? void 0 : parseSiwcFile(raw, filePath);
64655
+ };
64656
+ const write = (file2) => {
64657
+ const temp = `${filePath}.${ports.tempSuffix}.tmp`;
64658
+ ports.fs.writePrivate(temp, `${JSON.stringify(file2, null, 2)}
64659
+ `);
64660
+ ports.fs.rename(temp, filePath);
64661
+ cached2 = file2.grant;
64662
+ };
64663
+ const loadSignedIn = () => {
64664
+ const file2 = read();
64665
+ if (file2?.grant === void 0) {
64666
+ throw new CodexAuthError(`no Sign in with ChatGPT login at ${filePath}: run \`agent-shim codex login\` first`);
64667
+ }
64668
+ if (!file2.grant.scopes.includes(SIWC_PLAN_SCOPE)) {
64669
+ throw new CodexAuthError(`the Sign in with ChatGPT login at ${filePath} was not granted permission to use your ChatGPT plan: run \`agent-shim codex login\` again and allow it`);
64670
+ }
64671
+ return { file: file2, grant: file2.grant };
64672
+ };
64673
+ const credentialsOf2 = (grant) => ({ accessToken: grant.accessToken, accountId: void 0 });
64674
+ const doRefresh = async (rejected) => {
64675
+ const { file: file2, grant } = loadSignedIn();
64676
+ if (rejected !== void 0 && grant.accessToken !== rejected) {
64677
+ cached2 = grant;
64678
+ return credentialsOf2(grant);
64679
+ }
64680
+ let tokens;
64681
+ try {
64682
+ tokens = await refreshTokens(ports.siwc, { clientId: grant.clientId, refreshToken: grant.refreshToken });
64683
+ } catch (error62) {
64684
+ if (error62 instanceof SiwcError) {
64685
+ throw new CodexAuthError(`${error62.message}: run \`agent-shim codex login\` again if the login was revoked`);
64686
+ }
64687
+ throw error62;
64688
+ }
64689
+ const next = grantFrom(grant, tokens);
64690
+ write({ ...file2, grant: next });
64691
+ return credentialsOf2(next);
64692
+ };
64693
+ const refreshOnce = async (rejected) => await (inFlight ??= doRefresh(rejected).finally(() => {
64694
+ inFlight = void 0;
64695
+ }));
64696
+ return {
64697
+ read,
64698
+ write,
64699
+ auth: {
64700
+ current: async () => {
64701
+ const grant = cached2 ?? loadSignedIn().grant;
64702
+ cached2 = grant;
64703
+ if (grant.expiresAt - SIWC_REFRESH_MARGIN_MS <= ports.siwc.now().getTime()) {
64704
+ return await refreshOnce(void 0);
64705
+ }
64706
+ return credentialsOf2(grant);
64707
+ },
64708
+ refresh: async (rejected) => await refreshOnce(rejected)
64709
+ }
64710
+ };
64711
+ }
64712
+
64294
64713
  // src/codex/upstream.ts
64714
+ var import_node_crypto8 = require("node:crypto");
64295
64715
  var CODEX_RESPONSES_URL = "https://chatgpt.com/backend-api/codex/responses";
64296
64716
  var CODEX_UPSTREAM_TIMEOUT_MS = 3e5;
64297
64717
  var CODEX_HEADERS_TIMEOUT_MS = 15e3;
@@ -64327,12 +64747,17 @@ function sessionIdFor(userId, randomId) {
64327
64747
  if (userId === void 0 || userId === "") {
64328
64748
  return randomId();
64329
64749
  }
64330
- const hex3 = (0, import_node_crypto6.createHash)("sha256").update(`agent-shim codex session
64750
+ const hex3 = (0, import_node_crypto8.createHash)("sha256").update(`agent-shim codex session
64331
64751
  ${userId}`).digest("hex");
64332
64752
  let next = 0;
64333
64753
  return UUID_TEMPLATE.replace(/x/g, () => hex3.charAt(next++));
64334
64754
  }
64335
- function headersFor(credentials, sessionId) {
64755
+ var CODEX_BACKEND_TARGET = { url: CODEX_RESPONSES_URL, headers: codexBackendHeaders };
64756
+ var RESPONSES_API_TARGET = {
64757
+ url: SIWC_RESPONSES_URL,
64758
+ headers: (credentials) => ({ "Content-Type": "application/json", Accept: "text/event-stream", Authorization: `Bearer ${credentials.accessToken}` })
64759
+ };
64760
+ function codexBackendHeaders(credentials, sessionId) {
64336
64761
  return {
64337
64762
  "Content-Type": "application/json",
64338
64763
  Accept: "text/event-stream",
@@ -64344,11 +64769,11 @@ function headersFor(credentials, sessionId) {
64344
64769
  };
64345
64770
  }
64346
64771
  async function callCodex(ports, request7, sessionId, signal) {
64347
- const url2 = ports.url ?? CODEX_RESPONSES_URL;
64772
+ const target = ports.target ?? CODEX_BACKEND_TARGET;
64348
64773
  const body = JSON.stringify(request7);
64349
- const send = async (credentials2) => await fetchUntilHeaders(ports.fetch, ports.timers, url2, {
64774
+ const send = async (credentials2) => await fetchUntilHeaders(ports.fetch, ports.timers, target.url, {
64350
64775
  method: "POST",
64351
- headers: headersFor(credentials2, sessionId),
64776
+ headers: target.headers(credentials2, sessionId),
64352
64777
  body,
64353
64778
  signal: AbortSignal.any([signal, AbortSignal.timeout(CODEX_UPSTREAM_TIMEOUT_MS)])
64354
64779
  });
@@ -64384,6 +64809,7 @@ var realCodexAuthFs = {
64384
64809
  }
64385
64810
  },
64386
64811
  writePrivate: (filePath, contents) => {
64812
+ import_node_fs11.default.mkdirSync(import_node_path31.default.dirname(filePath), { recursive: true });
64387
64813
  import_node_fs11.default.writeFileSync(filePath, contents, { encoding: "utf8", mode: PRIVATE_FILE_MODE });
64388
64814
  import_node_fs11.default.chmodSync(filePath, PRIVATE_FILE_MODE);
64389
64815
  },
@@ -64403,17 +64829,22 @@ function writeUsageSnapshot(log, snapshot) {
64403
64829
  log(`usage snapshot write failed: ${error62 instanceof Error ? error62.message : String(error62)}`);
64404
64830
  }
64405
64831
  }
64406
- function createCodexRoutePorts(log) {
64832
+ function createCodexRoutePorts(log, signInFile) {
64407
64833
  const agent = createUpstreamAgent();
64408
64834
  const upstreamFetch = createUpstreamFetch(agent);
64835
+ const tempSuffix = `${String(process.pid)}.${(0, import_node_crypto9.randomUUID)()}`;
64409
64836
  const auth = createCodexAuthStore(import_node_path31.default.join(codexHome(process.env, import_node_os6.default.homedir()), "auth.json"), {
64410
64837
  fs: realCodexAuthFs,
64411
64838
  fetch: upstreamFetch,
64412
64839
  now: () => /* @__PURE__ */ new Date(),
64413
- tempSuffix: `${String(process.pid)}.${(0, import_node_crypto7.randomUUID)()}`
64840
+ tempSuffix
64414
64841
  });
64842
+ const signIn = createSiwcStore(signInFile, { fs: realCodexAuthFs, siwc: realSiwcPorts(upstreamFetch), tempSuffix });
64415
64843
  return {
64416
- upstream: { fetch: upstreamFetch, auth, timers: realTimers, randomId: import_node_crypto7.randomUUID },
64844
+ upstreams: {
64845
+ "codex-cli": { fetch: upstreamFetch, auth, timers: realTimers, randomId: import_node_crypto9.randomUUID },
64846
+ "chatgpt-sign-in": { fetch: upstreamFetch, auth: signIn.auth, timers: realTimers, randomId: import_node_crypto9.randomUUID, target: RESPONSES_API_TARGET }
64847
+ },
64417
64848
  writeUsageSnapshot: (snapshot) => {
64418
64849
  writeUsageSnapshot(log, snapshot);
64419
64850
  },
@@ -64421,6 +64852,18 @@ function createCodexRoutePorts(log) {
64421
64852
  log
64422
64853
  };
64423
64854
  }
64855
+ function readSignInStatus(fsPort, filePath) {
64856
+ const raw = fsPort.readFileUtf8(filePath);
64857
+ if (raw === void 0) {
64858
+ return { state: "none" };
64859
+ }
64860
+ try {
64861
+ const file2 = parseSiwcFile(raw, filePath);
64862
+ return file2.grant === void 0 ? { state: "none" } : { state: "signed-in", email: file2.grant.email, planScope: file2.grant.scopes.includes(SIWC_PLAN_SCOPE), accessTokenExpiresAt: file2.grant.expiresAt };
64863
+ } catch (error62) {
64864
+ return { state: "unreadable", message: error62 instanceof Error ? error62.message : String(error62) };
64865
+ }
64866
+ }
64424
64867
  function codexProviderNames(fsPort, providersDir) {
64425
64868
  return readAllProviders(fsPort, providersDir).filter((entry) => isCodexProvider(entry.provider)).map((entry) => entry.name).sort();
64426
64869
  }
@@ -64431,12 +64874,22 @@ function collectCodexStatus(fsPort, paths, isRunning) {
64431
64874
  supervisorAlive: state.supervisorPid !== void 0 && isRunning(state.supervisorPid),
64432
64875
  sessions: listFrontDoorSessions(fsPort, paths.frontdoorSessionsDir).map((session) => ({ ...session, alive: isRunning(session.pid) })),
64433
64876
  codexProviders: codexProviderNames(fsPort, paths.providersDir),
64877
+ signIn: readSignInStatus(fsPort, paths.chatgptSignInFile),
64434
64878
  usageSnapshotPath: codexUsageSnapshotPath(),
64435
64879
  usageSnapshotExists: fsPort.readFileUtf8(codexUsageSnapshotPath()) !== void 0,
64436
64880
  logPath: paths.frontdoorLogPath,
64437
64881
  logExists: fsPort.readFileUtf8(paths.frontdoorLogPath) !== void 0
64438
64882
  };
64439
64883
  }
64884
+ function formatSignIn(signIn) {
64885
+ if (signIn.state === "none") {
64886
+ return "chatgpt sign-in: not signed in (run `agent-shim codex login`)";
64887
+ }
64888
+ if (signIn.state === "unreadable") {
64889
+ return `chatgpt sign-in: ${signIn.message}`;
64890
+ }
64891
+ return `chatgpt sign-in: signed in${signIn.email === void 0 ? "" : ` as ${signIn.email}`}${signIn.planScope ? "" : ", WITHOUT permission to use your ChatGPT plan (run `agent-shim codex login` again and allow it)"}`;
64892
+ }
64440
64893
  function formatCodexStatus(status) {
64441
64894
  const lines = [];
64442
64895
  lines.push(
@@ -64457,10 +64910,24 @@ function formatCodexStatus(status) {
64457
64910
  if (status.frontDoor.lastError !== void 0) {
64458
64911
  lines.push(`last error: ${status.frontDoor.lastError}`);
64459
64912
  }
64913
+ lines.push(formatSignIn(status.signIn));
64460
64914
  lines.push(`usage snapshot: ${status.usageSnapshotPath}${status.usageSnapshotExists ? "" : " (not created yet)"}`);
64461
64915
  lines.push(`daemon log: ${status.logPath}${status.logExists ? "" : " (not created yet)"}`);
64462
64916
  return lines;
64463
64917
  }
64918
+ function realSiwcLoginPorts(paths) {
64919
+ const siwc = realSiwcPorts(createUpstreamFetch(createUpstreamAgent()));
64920
+ const store = createSiwcStore(paths.chatgptSignInFile, { fs: realCodexAuthFs, siwc, tempSuffix: `${String(process.pid)}.${(0, import_node_crypto9.randomUUID)()}` });
64921
+ return {
64922
+ store,
64923
+ siwc,
64924
+ listen: listenForCallback,
64925
+ openBrowser: openInBrowser,
64926
+ print: (line) => {
64927
+ console.log(line);
64928
+ }
64929
+ };
64930
+ }
64464
64931
  function registerCodexCommand(program2, deps) {
64465
64932
  const { paths } = deps;
64466
64933
  const codex = withExamples(program2.command("codex").description("Inspect the codex translation the front-door daemon serves for codex providers."), ["agent-shim codex status"]);
@@ -64477,10 +64944,38 @@ function registerCodexCommand(program2, deps) {
64477
64944
  }),
64478
64945
  ["agent-shim codex status", "agent-shim codex status --json"]
64479
64946
  );
64947
+ withExamples(
64948
+ codex.command("login").description("Sign in with ChatGPT so a codex provider with `codex.login` set to `chatgpt-sign-in` can spend your ChatGPT plan on OpenAI's public Responses API. Opens your browser.").option("--no-open", "Print the sign-in address instead of opening the browser.").option("--json", "Print the result as JSON.").action(async (options) => {
64949
+ const ports = realSiwcLoginPorts(paths);
64950
+ const result = await runSiwcLogin(options.json === true ? { ...ports, print: () => void 0 } : ports, { open: options.open });
64951
+ if (options.json === true) {
64952
+ printJson({ action: "login", email: result.email ?? null, sub: result.sub });
64953
+ return;
64954
+ }
64955
+ console.log(`Signed in${result.email === void 0 ? "" : ` as ${result.email}`}. Set "codex": { "login": "chatgpt-sign-in" } on a codex provider to use it.`);
64956
+ }),
64957
+ ["agent-shim codex login", "agent-shim codex login --no-open"]
64958
+ );
64959
+ withExamples(
64960
+ codex.command("logout").description("Revoke the Sign in with ChatGPT login and remove it from this machine.").option("--json", "Print the result as JSON.").action(async (options) => {
64961
+ const ports = realSiwcLoginPorts(paths);
64962
+ const result = await runSiwcLogout(ports);
64963
+ if (options.json === true) {
64964
+ printJson({ action: "logout", ...result });
64965
+ return;
64966
+ }
64967
+ if (!result.hadGrant) {
64968
+ console.log("Not signed in.");
64969
+ return;
64970
+ }
64971
+ console.log(result.revoked ? "Signed out and revoked the login at OpenAI." : "Signed out here, but OpenAI could not be told to revoke the login: revoke it in ChatGPT settings.");
64972
+ }),
64973
+ ["agent-shim codex logout"]
64974
+ );
64480
64975
  }
64481
64976
 
64482
64977
  // src/frontdoor/commands.ts
64483
- var import_node_crypto12 = require("node:crypto");
64978
+ var import_node_crypto14 = require("node:crypto");
64484
64979
  var import_node_path36 = __toESM(require("node:path"), 1);
64485
64980
  var import_node_fs13 = __toESM(require("node:fs"), 1);
64486
64981
 
@@ -64585,7 +65080,7 @@ function createDoorPipelines(deps) {
64585
65080
  }
64586
65081
 
64587
65082
  // src/frontdoor/capability.ts
64588
- var import_node_crypto8 = require("node:crypto");
65083
+ var import_node_crypto10 = require("node:crypto");
64589
65084
  var CONNECT_PROXY_USER = "agent-shim";
64590
65085
  var CONNECT_PROXY_REALM = "agent-shim front door";
64591
65086
  var BASIC_SCHEME = "basic";
@@ -64607,13 +65102,13 @@ function capabilityFromProxyAuthorization(value2) {
64607
65102
  return password === "" ? void 0 : password;
64608
65103
  }
64609
65104
  function digest(token) {
64610
- return (0, import_node_crypto8.createHash)("sha256").update(token, "utf8").digest();
65105
+ return (0, import_node_crypto10.createHash)("sha256").update(token, "utf8").digest();
64611
65106
  }
64612
65107
  function isLiveCapability(presented, live) {
64613
65108
  const presentedDigest = digest(presented);
64614
65109
  let matched = false;
64615
65110
  for (const token of live) {
64616
- const equal = (0, import_node_crypto8.timingSafeEqual)(presentedDigest, digest(token));
65111
+ const equal = (0, import_node_crypto10.timingSafeEqual)(presentedDigest, digest(token));
64617
65112
  matched = equal || matched;
64618
65113
  }
64619
65114
  return matched;
@@ -64757,11 +65252,11 @@ function createFileCapture(file2, now = () => /* @__PURE__ */ new Date()) {
64757
65252
  }
64758
65253
 
64759
65254
  // src/frontdoor/connect.ts
64760
- var import_node_crypto10 = require("node:crypto");
65255
+ var import_node_crypto12 = require("node:crypto");
64761
65256
  var fs13 = __toESM(require("node:fs"), 1);
64762
65257
 
64763
65258
  // src/frontdoor/x509.ts
64764
- var import_node_crypto9 = require("node:crypto");
65259
+ var import_node_crypto11 = require("node:crypto");
64765
65260
  var import_node_net = __toESM(require("node:net"), 1);
64766
65261
  var TAG_BOOLEAN = 1;
64767
65262
  var TAG_INTEGER = 2;
@@ -64797,7 +65292,7 @@ var HIGH_BIT = 128;
64797
65292
  var BASE_128_PAYLOAD_MASK = 127;
64798
65293
  var FIRST_ARC_WEIGHT = 40;
64799
65294
  var SHORT_FORM_MAX_LENGTH = 127;
64800
- var MS_PER_SECOND4 = 1e3;
65295
+ var MS_PER_SECOND5 = 1e3;
64801
65296
  var UTC_TIME_LAST_YEAR = 2049;
64802
65297
  var YEAR_DIGITS = 4;
64803
65298
  var TWO_DIGIT_YEAR_DIGITS = 2;
@@ -64856,7 +65351,7 @@ function wholeBitString(content) {
64856
65351
  return tlv(TAG_BIT_STRING, Buffer.concat([Buffer.from([0]), content]));
64857
65352
  }
64858
65353
  function time3(instant) {
64859
- const iso = new Date(Math.floor(instant.getTime() / MS_PER_SECOND4) * MS_PER_SECOND4).toISOString();
65354
+ const iso = new Date(Math.floor(instant.getTime() / MS_PER_SECOND5) * MS_PER_SECOND5).toISOString();
64860
65355
  const compact = `${iso.replace(/[-:T]/g, "").slice(0, COMPACT_TIME_DIGITS)}Z`;
64861
65356
  if (instant.getUTCFullYear() > UTC_TIME_LAST_YEAR) {
64862
65357
  return tlv(TAG_GENERALIZED_TIME, Buffer.from(compact, "ascii"));
@@ -64913,7 +65408,7 @@ function ipBytes(address) {
64913
65408
  return bytes;
64914
65409
  }
64915
65410
  function keyIdentifier(publicKey) {
64916
- return (0, import_node_crypto9.createHash)("sha1").update(publicKey.export({ type: "pkcs1", format: "der" })).digest();
65411
+ return (0, import_node_crypto11.createHash)("sha1").update(publicKey.export({ type: "pkcs1", format: "der" })).digest();
64917
65412
  }
64918
65413
  function readElement(buffer, offset) {
64919
65414
  const tag = buffer[offset];
@@ -64959,7 +65454,7 @@ ${body.join("\n")}
64959
65454
  `;
64960
65455
  }
64961
65456
  function randomSerial() {
64962
- const bytes = (0, import_node_crypto9.randomBytes)(SERIAL_NUMBER_BYTES);
65457
+ const bytes = (0, import_node_crypto11.randomBytes)(SERIAL_NUMBER_BYTES);
64963
65458
  bytes[0] = (bytes[0] ?? 0) & BASE_128_PAYLOAD_MASK | HIGH_BIT >> 1;
64964
65459
  return bytes;
64965
65460
  }
@@ -64974,7 +65469,7 @@ function assemble(spec) {
64974
65469
  spec.subjectPublicKey.export({ type: "spki", format: "der" }),
64975
65470
  tlv(TAG_EXTENSIONS, sequence(...spec.extensions))
64976
65471
  );
64977
- const signature = (0, import_node_crypto9.sign)("sha256", tbs, spec.signingKey);
65472
+ const signature = (0, import_node_crypto11.sign)("sha256", tbs, spec.signingKey);
64978
65473
  return sequence(tbs, SIGNATURE_ALGORITHM, wholeBitString(signature));
64979
65474
  }
64980
65475
  function subjectKeyIdentifierExtension(publicKey) {
@@ -64984,7 +65479,7 @@ function basicConstraintsExtension(isCa) {
64984
65479
  return extension(OID_BASIC_CONSTRAINTS, true, sequence(...isCa ? [boolean4(true)] : []));
64985
65480
  }
64986
65481
  function rsaKeyPair() {
64987
- return (0, import_node_crypto9.generateKeyPairSync)("rsa", { modulusLength: RSA_MODULUS_BITS });
65482
+ return (0, import_node_crypto11.generateKeyPairSync)("rsa", { modulusLength: RSA_MODULUS_BITS });
64988
65483
  }
64989
65484
  function keyPem(privateKey) {
64990
65485
  return privateKey.export({ type: "pkcs8", format: "pem" });
@@ -65008,8 +65503,8 @@ function issueLeafCertificate(ca, names, notBefore, notAfter) {
65008
65503
  if (commonName === void 0) {
65009
65504
  throw new Error("a leaf certificate needs at least one name");
65010
65505
  }
65011
- const caCertificate = new import_node_crypto9.X509Certificate(ca.certPem);
65012
- const caPrivateKey = (0, import_node_crypto9.createPrivateKey)(ca.keyPem);
65506
+ const caCertificate = new import_node_crypto11.X509Certificate(ca.certPem);
65507
+ const caPrivateKey = (0, import_node_crypto11.createPrivateKey)(ca.keyPem);
65013
65508
  const { publicKey, privateKey } = rsaKeyPair();
65014
65509
  const generalNames = names.map((name) => import_node_net.default.isIP(name) === 0 ? tlv(TAG_GENERAL_NAME_DNS, Buffer.from(name, "ascii")) : tlv(TAG_GENERAL_NAME_IP, ipBytes(name)));
65015
65510
  const der = assemble({
@@ -65106,22 +65601,22 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
65106
65601
  };
65107
65602
  writeFrontDoorState(fs19, paths.frontdoorStateFile, { supervisorPid: ports.ownPid, ...sticky() });
65108
65603
  const previousSticky = lastPort;
65109
- let http6;
65604
+ let http7;
65110
65605
  try {
65111
- http6 = await ports.startProviderListener(lastPort);
65606
+ http7 = await ports.startProviderListener(lastPort);
65112
65607
  } catch (error62) {
65113
65608
  return fail(`could not start the provider listener: ${error62 instanceof Error ? error62.message : String(error62)}`);
65114
65609
  }
65115
- if (previousSticky !== void 0 && http6.port !== previousSticky) {
65116
- ports.log(`agent-shim frontdoor supervisor: sticky port ${String(previousSticky)} was occupied; moving to ${String(http6.port)} (sessions launched against the old port are stale until they relaunch)`);
65610
+ if (previousSticky !== void 0 && http7.port !== previousSticky) {
65611
+ ports.log(`agent-shim frontdoor supervisor: sticky port ${String(previousSticky)} was occupied; moving to ${String(http7.port)} (sessions launched against the old port are stale until they relaunch)`);
65117
65612
  }
65118
- lastPort = http6.port;
65613
+ lastPort = http7.port;
65119
65614
  const previousStickyConnect = lastConnectPort;
65120
65615
  let connect3;
65121
65616
  try {
65122
65617
  connect3 = await ports.startConnectListener(lastConnectPort);
65123
65618
  } catch (error62) {
65124
- await http6.close();
65619
+ await http7.close();
65125
65620
  return fail(`could not start the CONNECT surface: ${error62 instanceof Error ? error62.message : String(error62)}`);
65126
65621
  }
65127
65622
  if (previousStickyConnect !== void 0 && connect3.port !== previousStickyConnect) {
@@ -65134,16 +65629,16 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
65134
65629
  direct = await ports.startDirectListener(lastDirectPort);
65135
65630
  } catch (error62) {
65136
65631
  await connect3.close();
65137
- await http6.close();
65632
+ await http7.close();
65138
65633
  return fail(`could not start the direct listener: ${error62 instanceof Error ? error62.message : String(error62)}`);
65139
65634
  }
65140
65635
  if (previousStickyDirect !== void 0 && direct.port !== previousStickyDirect) {
65141
65636
  ports.log(`agent-shim frontdoor supervisor: sticky direct port ${String(previousStickyDirect)} was occupied; moving to ${String(direct.port)} (headroom must be restarted to admit the new address)`);
65142
65637
  }
65143
65638
  lastDirectPort = direct.port;
65144
- writeFrontDoorState(fs19, paths.frontdoorStateFile, { protocol: FRONT_DOOR_PROTOCOL, supervisorPid: ports.ownPid, port: http6.port, connectPort: connect3.port, directPort: direct.port, ...sticky() });
65639
+ writeFrontDoorState(fs19, paths.frontdoorStateFile, { protocol: FRONT_DOOR_PROTOCOL, supervisorPid: ports.ownPid, port: http7.port, connectPort: connect3.port, directPort: direct.port, ...sticky() });
65145
65640
  fs19.removeRecursive(paths.frontdoorLockFile);
65146
- ports.log(`agent-shim frontdoor supervisor ${String(ports.ownPid)}: front door on 127.0.0.1:${String(http6.port)}, CONNECT surface on 127.0.0.1:${String(connect3.port)}`);
65641
+ ports.log(`agent-shim frontdoor supervisor ${String(ports.ownPid)}: front door on 127.0.0.1:${String(http7.port)}, CONNECT surface on 127.0.0.1:${String(connect3.port)}`);
65147
65642
  let idleSince;
65148
65643
  let ticks = 0;
65149
65644
  for (; ; ) {
@@ -65151,14 +65646,17 @@ async function runFrontDoorSupervisor(idleShutdownMinutes, ports, options = {})
65151
65646
  return FRONTDOOR_SUPERVISOR_STILL_RUNNING;
65152
65647
  }
65153
65648
  ticks += 1;
65154
- pruneDeadFrontDoorSessions(fs19, paths.frontdoorSessionsDir, ports.isRunning);
65155
- if (listFrontDoorSessions(fs19, paths.frontdoorSessionsDir).length === 0) {
65649
+ const listed = listFrontDoorSessions(fs19, paths.frontdoorSessionsDir);
65650
+ const pruned = pruneDeadFrontDoorSessions(fs19, paths.frontdoorSessionsDir, ports.isRunning);
65651
+ ports.observeLaunchRegistry(listed, pruned);
65652
+ const prunedPids = new Set(pruned);
65653
+ if (listed.filter((session) => !prunedPids.has(session.pid)).length === 0) {
65156
65654
  idleSince ??= ports.now();
65157
65655
  if (ports.now() - idleSince >= idleShutdownMinutes * MS_PER_MINUTE2) {
65158
65656
  ports.log(`agent-shim frontdoor supervisor: no sessions for ${String(idleShutdownMinutes)} minute(s); closing the front door and exiting`);
65159
65657
  await direct.close();
65160
65658
  await connect3.close();
65161
- await http6.close();
65659
+ await http7.close();
65162
65660
  ports.closeRcStreamHub();
65163
65661
  writeFrontDoorState(fs19, paths.frontdoorStateFile, sticky());
65164
65662
  return 0;
@@ -65274,8 +65772,8 @@ function ensureCa(store, generate) {
65274
65772
  }
65275
65773
  function caParses(ca) {
65276
65774
  try {
65277
- new import_node_crypto10.X509Certificate(ca.certPem);
65278
- (0, import_node_crypto10.createPrivateKey)(ca.keyPem);
65775
+ new import_node_crypto12.X509Certificate(ca.certPem);
65776
+ (0, import_node_crypto12.createPrivateKey)(ca.keyPem);
65279
65777
  return true;
65280
65778
  } catch {
65281
65779
  return false;
@@ -65563,13 +66061,13 @@ function realConnectCertStore(paths) {
65563
66061
 
65564
66062
  // src/frontdoor/connectEffects.ts
65565
66063
  var import_node_dns = __toESM(require("node:dns"), 1);
65566
- var http2 = __toESM(require("node:http"), 1);
66064
+ var http3 = __toESM(require("node:http"), 1);
65567
66065
  var https3 = __toESM(require("node:https"), 1);
65568
66066
  var net2 = __toESM(require("node:net"), 1);
65569
66067
  var tls = __toESM(require("node:tls"), 1);
65570
66068
 
65571
66069
  // src/frontdoor/server.ts
65572
- var import_node_http = __toESM(require("node:http"), 1);
66070
+ var import_node_http2 = __toESM(require("node:http"), 1);
65573
66071
  var import_node_https = __toESM(require("node:https"), 1);
65574
66072
 
65575
66073
  // src/frontdoor/rcControl.ts
@@ -66595,7 +67093,7 @@ function frontDoorRcControl(transport, token) {
66595
67093
  var HEALTH_TIMEOUT_MS = 2e3;
66596
67094
  var HEALTH_START_BUDGET_MS = 5e3;
66597
67095
  var HEALTH_RETRY_MS = 50;
66598
- function createFrontDoorServer(pipeline, log, tls2, control, api) {
67096
+ function createFrontDoorServer(pipeline, log, tls2, control, apis) {
66599
67097
  const handler = (request7, response) => {
66600
67098
  if (request7.method === "GET" && request7.url === "/healthz") {
66601
67099
  response.writeHead(HTTP_STATUS.ok, { "Content-Type": "text/plain" });
@@ -66606,7 +67104,8 @@ function createFrontDoorServer(pipeline, log, tls2, control, api) {
66606
67104
  control(request7, response);
66607
67105
  return;
66608
67106
  }
66609
- if (api !== void 0 && (request7.url ?? "/").startsWith(api.pathPrefix)) {
67107
+ const api = apis?.find((candidate) => (request7.url ?? "/").startsWith(candidate.pathPrefix));
67108
+ if (api !== void 0) {
66610
67109
  api.handle(request7, response).then((result) => {
66611
67110
  if (!result.matched && !response.headersSent) {
66612
67111
  response.writeHead(HTTP_STATUS.notFound, { "Content-Type": "application/json" });
@@ -66646,7 +67145,7 @@ function createFrontDoorServer(pipeline, log, tls2, control, api) {
66646
67145
  });
66647
67146
  };
66648
67147
  if (tls2 === void 0) {
66649
- return import_node_http.default.createServer(handler);
67148
+ return import_node_http2.default.createServer(handler);
66650
67149
  }
66651
67150
  const server = import_node_https.default.createServer({ key: tls2.keyPem, cert: tls2.certPem }, handler);
66652
67151
  server.on("tlsClientError", (_error, socket) => {
@@ -66661,7 +67160,7 @@ async function frontDoorHealthy(port, ca) {
66661
67160
  resolve(response.statusCode === HTTP_STATUS.ok);
66662
67161
  };
66663
67162
  const options = { host: "127.0.0.1", port, path: "/healthz", agent: false, timeout: HEALTH_TIMEOUT_MS };
66664
- const request7 = ca === void 0 ? import_node_http.default.get(options, onResponse) : import_node_https.default.get({ ...options, ca: [ca] }, onResponse);
67163
+ const request7 = ca === void 0 ? import_node_http2.default.get(options, onResponse) : import_node_https.default.get({ ...options, ca: [ca] }, onResponse);
66665
67164
  request7.on("timeout", () => {
66666
67165
  request7.destroy();
66667
67166
  });
@@ -66816,7 +67315,7 @@ async function reservedDial(target, ports) {
66816
67315
  function requestServername(options, fallback) {
66817
67316
  return "servername" in options && typeof options.servername === "string" && options.servername !== "" ? options.servername : fallback;
66818
67317
  }
66819
- var ExemptHttpAgent = class extends http2.Agent {
67318
+ var ExemptHttpAgent = class extends http3.Agent {
66820
67319
  constructor(ports = DOOR_SOURCE_PORTS) {
66821
67320
  super();
66822
67321
  this.ports = ports;
@@ -67087,7 +67586,7 @@ function realConnectEffects(ports = DOOR_SOURCE_PORTS) {
67087
67586
  };
67088
67587
  },
67089
67588
  createHttpSession: (handler, onUpgrade) => {
67090
- const server = http2.createServer(handler);
67589
+ const server = http3.createServer(handler);
67091
67590
  server.on("clientError", (_error, socket) => {
67092
67591
  socket.destroy();
67093
67592
  });
@@ -67157,7 +67656,7 @@ function realConnectEffects(ports = DOOR_SOURCE_PORTS) {
67157
67656
  }
67158
67657
  upstreamResponse.pipe(response);
67159
67658
  };
67160
- const upstream = target.tls ? https3.request(options, onUpstreamResponse) : http2.request(options, onUpstreamResponse);
67659
+ const upstream = target.tls ? https3.request(options, onUpstreamResponse) : http3.request(options, onUpstreamResponse);
67161
67660
  upstream.on("error", (error62) => {
67162
67661
  if (response.headersSent) {
67163
67662
  response.destroy(error62);
@@ -67282,7 +67781,21 @@ function cacheClause(cacheEnv, owner, block2) {
67282
67781
  const state = describeCachedCredential({ port: cacheEnv.port, platform: cacheEnv.platform, owner, cache: block2.cache, nowMs: cacheEnv.now() });
67283
67782
  return { text: `; ${formatCachedCredentialState(state)}`, unreadable: state.status === "unreadable" };
67284
67783
  }
67285
- function pushProvider(push, entry, cacheEnv) {
67784
+ function signInProblem(signIn) {
67785
+ if (signIn.raw === void 0) {
67786
+ return "nobody is signed in: run `agent-shim codex login`.";
67787
+ }
67788
+ try {
67789
+ const grant = parseSiwcFile(signIn.raw, signIn.path).grant;
67790
+ if (grant === void 0) {
67791
+ return "nobody is signed in: run `agent-shim codex login`.";
67792
+ }
67793
+ return grant.scopes.includes(SIWC_PLAN_SCOPE) ? void 0 : "the login was not given permission to use your ChatGPT plan: run `agent-shim codex login` again and allow it.";
67794
+ } catch (error62) {
67795
+ return error62 instanceof Error ? error62.message : String(error62);
67796
+ }
67797
+ }
67798
+ function pushProvider(push, entry, cacheEnv, signIn) {
67286
67799
  if (entry.raw === void 0) {
67287
67800
  push("provider", "fail", `${entry.path} is missing.`, entry.name);
67288
67801
  return;
@@ -67309,6 +67822,12 @@ function pushProvider(push, entry, cacheEnv) {
67309
67822
  if (validated2.data.env?.ENABLE_TOOL_SEARCH === void 0) {
67310
67823
  push("provider", "warn", `${entry.name} does not set ENABLE_TOOL_SEARCH: a session on this provider loads MCP tools upfront rather than deferring them, because its base URL is the front door, which Claude Code treats as a non-first-party host. Set ENABLE_TOOL_SEARCH in the provider's env block to restore deferred loading, when the upstream backend supports the tool_reference payloads it sends.`, entry.name);
67311
67824
  }
67825
+ if (signIn !== void 0 && isCodexProvider(validated2.data) && validated2.data.codex?.login === "chatgpt-sign-in") {
67826
+ const problem = signInProblem(signIn);
67827
+ if (problem !== void 0) {
67828
+ push("provider", "warn", `${entry.name} uses the Sign in with ChatGPT login, but ${problem}`, entry.name);
67829
+ }
67830
+ }
67312
67831
  }
67313
67832
  function pushLegacyName(push, params) {
67314
67833
  const legacyVariables = Object.keys(params.env).filter((name) => name.startsWith(LEGACY_ENV_PREFIX) && params.env[name] !== void 0).sort();
@@ -67433,7 +67952,7 @@ function runDoctor(params) {
67433
67952
  return poolName === void 0 ? validIdentityNames.has(selector) : poolName in pools;
67434
67953
  };
67435
67954
  for (const entry of params.providers) {
67436
- pushProvider(push, entry, params.credentialCache);
67955
+ pushProvider(push, entry, params.credentialCache, params.chatgptSignIn);
67437
67956
  }
67438
67957
  if (params.platform !== "darwin" || params.run === void 0) {
67439
67958
  push("keychain", "pass", "Skipped (not macOS).");
@@ -67675,6 +68194,7 @@ function collectDoctorReport(params) {
67675
68194
  const report = runDoctor({
67676
68195
  env: params.env,
67677
68196
  credentialCache: realCredentialCacheEnv(paths),
68197
+ chatgptSignIn: { path: paths.chatgptSignInFile, raw: realFsPort.readFileUtf8(paths.chatgptSignInFile) },
67678
68198
  installedClaudeVersions: realInstalledClaudeVersions(),
67679
68199
  identities,
67680
68200
  configProfiles,
@@ -68870,6 +69390,56 @@ var DoctorRunOutputSchema = external_exports.strictObject({
68870
69390
  findings: external_exports.readonly(external_exports.array(DoctorFindingSchema))
68871
69391
  });
68872
69392
 
69393
+ // node_modules/.pnpm/@orpc+contract@1.15.4/node_modules/@orpc/contract/dist/shared/contract.D_dZrO__.mjs
69394
+ var ValidationError = class extends Error {
69395
+ issues;
69396
+ data;
69397
+ constructor(options) {
69398
+ super(options.message, options);
69399
+ this.issues = options.issues;
69400
+ this.data = options.data;
69401
+ }
69402
+ };
69403
+ function mergeErrorMap(errorMap1, errorMap2) {
69404
+ return { ...errorMap1, ...errorMap2 };
69405
+ }
69406
+ async function validateORPCError(map3, error62) {
69407
+ const { code, status, message, data, cause, defined } = error62;
69408
+ const config2 = map3?.[error62.code];
69409
+ if (!config2 || fallbackORPCErrorStatus(error62.code, config2.status) !== error62.status) {
69410
+ return defined ? new ORPCError(code, { defined: false, status, message, data, cause }) : error62;
69411
+ }
69412
+ if (!config2.data) {
69413
+ return defined ? error62 : new ORPCError(code, { defined: true, status, message, data, cause });
69414
+ }
69415
+ const validated2 = await config2.data["~standard"].validate(error62.data);
69416
+ if (validated2.issues) {
69417
+ return defined ? new ORPCError(code, { defined: false, status, message, data, cause }) : error62;
69418
+ }
69419
+ return new ORPCError(code, { defined: true, status, message, data: validated2.value, cause });
69420
+ }
69421
+ var ContractProcedure = class {
69422
+ /**
69423
+ * This property holds the defined options for the contract procedure.
69424
+ */
69425
+ "~orpc";
69426
+ constructor(def) {
69427
+ if (def.route?.successStatus && isORPCErrorStatus(def.route.successStatus)) {
69428
+ throw new Error("[ContractProcedure] Invalid successStatus.");
69429
+ }
69430
+ if (Object.values(def.errorMap).some((val) => val && val.status && !isORPCErrorStatus(val.status))) {
69431
+ throw new Error("[ContractProcedure] Invalid error status code.");
69432
+ }
69433
+ this["~orpc"] = def;
69434
+ }
69435
+ };
69436
+ function isContractProcedure(item) {
69437
+ if (item instanceof ContractProcedure) {
69438
+ return true;
69439
+ }
69440
+ return (typeof item === "object" || typeof item === "function") && item !== null && "~orpc" in item && typeof item["~orpc"] === "object" && item["~orpc"] !== null && "errorMap" in item["~orpc"] && "route" in item["~orpc"] && "meta" in item["~orpc"];
69441
+ }
69442
+
68873
69443
  // node_modules/.pnpm/@orpc+standard-server-fetch@1.15.4/node_modules/@orpc/standard-server-fetch/dist/index.mjs
68874
69444
  function toEventIterator(stream, options = {}) {
68875
69445
  const eventStream = stream?.pipeThrough(new TextDecoderStream()).pipeThrough(new EventDecoderStream());
@@ -69581,93 +70151,6 @@ var StandardRPCLink = class extends StandardLink {
69581
70151
  }
69582
70152
  };
69583
70153
 
69584
- // node_modules/.pnpm/@orpc+client@1.15.4/node_modules/@orpc/client/dist/adapters/fetch/index.mjs
69585
- var CompositeLinkFetchPlugin = class extends CompositeStandardLinkPlugin {
69586
- initRuntimeAdapter(options) {
69587
- for (const plugin of this.plugins) {
69588
- plugin.initRuntimeAdapter?.(options);
69589
- }
69590
- }
69591
- };
69592
- var LinkFetchClient = class {
69593
- fetch;
69594
- toFetchRequestOptions;
69595
- adapterInterceptors;
69596
- constructor(options) {
69597
- const plugin = new CompositeLinkFetchPlugin(options.plugins);
69598
- plugin.initRuntimeAdapter(options);
69599
- this.fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
69600
- this.toFetchRequestOptions = options;
69601
- this.adapterInterceptors = toArray(options.adapterInterceptors);
69602
- }
69603
- async call(standardRequest, options, path44, input2) {
69604
- const request7 = toFetchRequest(standardRequest, this.toFetchRequestOptions);
69605
- const fetchResponse = await intercept(
69606
- this.adapterInterceptors,
69607
- { ...options, request: request7, path: path44, input: input2, init: { redirect: "manual" } },
69608
- ({ request: request22, path: path210, input: input22, init, ...options2 }) => this.fetch(request22, init, options2, path210, input22)
69609
- );
69610
- const lazyResponse = toStandardLazyResponse(fetchResponse, { signal: request7.signal });
69611
- return lazyResponse;
69612
- }
69613
- };
69614
- var RPCLink = class extends StandardRPCLink {
69615
- constructor(options) {
69616
- const linkClient = new LinkFetchClient(options);
69617
- super(linkClient, options);
69618
- }
69619
- };
69620
-
69621
- // node_modules/.pnpm/@orpc+contract@1.15.4/node_modules/@orpc/contract/dist/shared/contract.D_dZrO__.mjs
69622
- var ValidationError = class extends Error {
69623
- issues;
69624
- data;
69625
- constructor(options) {
69626
- super(options.message, options);
69627
- this.issues = options.issues;
69628
- this.data = options.data;
69629
- }
69630
- };
69631
- function mergeErrorMap(errorMap1, errorMap2) {
69632
- return { ...errorMap1, ...errorMap2 };
69633
- }
69634
- async function validateORPCError(map3, error62) {
69635
- const { code, status, message, data, cause, defined } = error62;
69636
- const config2 = map3?.[error62.code];
69637
- if (!config2 || fallbackORPCErrorStatus(error62.code, config2.status) !== error62.status) {
69638
- return defined ? new ORPCError(code, { defined: false, status, message, data, cause }) : error62;
69639
- }
69640
- if (!config2.data) {
69641
- return defined ? error62 : new ORPCError(code, { defined: true, status, message, data, cause });
69642
- }
69643
- const validated2 = await config2.data["~standard"].validate(error62.data);
69644
- if (validated2.issues) {
69645
- return defined ? new ORPCError(code, { defined: false, status, message, data, cause }) : error62;
69646
- }
69647
- return new ORPCError(code, { defined: true, status, message, data: validated2.value, cause });
69648
- }
69649
- var ContractProcedure = class {
69650
- /**
69651
- * This property holds the defined options for the contract procedure.
69652
- */
69653
- "~orpc";
69654
- constructor(def) {
69655
- if (def.route?.successStatus && isORPCErrorStatus(def.route.successStatus)) {
69656
- throw new Error("[ContractProcedure] Invalid successStatus.");
69657
- }
69658
- if (Object.values(def.errorMap).some((val) => val && val.status && !isORPCErrorStatus(val.status))) {
69659
- throw new Error("[ContractProcedure] Invalid error status code.");
69660
- }
69661
- this["~orpc"] = def;
69662
- }
69663
- };
69664
- function isContractProcedure(item) {
69665
- if (item instanceof ContractProcedure) {
69666
- return true;
69667
- }
69668
- return (typeof item === "object" || typeof item === "function") && item !== null && "~orpc" in item && typeof item["~orpc"] === "object" && item["~orpc"] !== null && "errorMap" in item["~orpc"] && "route" in item["~orpc"] && "meta" in item["~orpc"];
69669
- }
69670
-
69671
70154
  // node_modules/.pnpm/@orpc+contract@1.15.4/node_modules/@orpc/contract/dist/index.mjs
69672
70155
  function mergeMeta(meta1, meta22) {
69673
70156
  return { ...meta1, ...meta22 };
@@ -70612,6 +71095,43 @@ var os7 = new Builder({
70612
71095
  dedupeLeadingMiddlewares: true
70613
71096
  });
70614
71097
 
71098
+ // node_modules/.pnpm/@orpc+client@1.15.4/node_modules/@orpc/client/dist/adapters/fetch/index.mjs
71099
+ var CompositeLinkFetchPlugin = class extends CompositeStandardLinkPlugin {
71100
+ initRuntimeAdapter(options) {
71101
+ for (const plugin of this.plugins) {
71102
+ plugin.initRuntimeAdapter?.(options);
71103
+ }
71104
+ }
71105
+ };
71106
+ var LinkFetchClient = class {
71107
+ fetch;
71108
+ toFetchRequestOptions;
71109
+ adapterInterceptors;
71110
+ constructor(options) {
71111
+ const plugin = new CompositeLinkFetchPlugin(options.plugins);
71112
+ plugin.initRuntimeAdapter(options);
71113
+ this.fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
71114
+ this.toFetchRequestOptions = options;
71115
+ this.adapterInterceptors = toArray(options.adapterInterceptors);
71116
+ }
71117
+ async call(standardRequest, options, path44, input2) {
71118
+ const request7 = toFetchRequest(standardRequest, this.toFetchRequestOptions);
71119
+ const fetchResponse = await intercept(
71120
+ this.adapterInterceptors,
71121
+ { ...options, request: request7, path: path44, input: input2, init: { redirect: "manual" } },
71122
+ ({ request: request22, path: path210, input: input22, init, ...options2 }) => this.fetch(request22, init, options2, path210, input22)
71123
+ );
71124
+ const lazyResponse = toStandardLazyResponse(fetchResponse, { signal: request7.signal });
71125
+ return lazyResponse;
71126
+ }
71127
+ };
71128
+ var RPCLink = class extends StandardRPCLink {
71129
+ constructor(options) {
71130
+ const linkClient = new LinkFetchClient(options);
71131
+ super(linkClient, options);
71132
+ }
71133
+ };
71134
+
70615
71135
  // node_modules/.pnpm/@orpc+standard-server-node@1.15.4/node_modules/@orpc/standard-server-node/dist/index.mjs
70616
71136
  var import_node_buffer = require("node:buffer");
70617
71137
  var import_node_stream = require("node:stream");
@@ -71335,6 +71855,62 @@ function frontDoorRcApiClient(port, ca, token) {
71335
71855
  return createORPCClient(frontDoorApiLink(port, ca, token));
71336
71856
  }
71337
71857
 
71858
+ // src/frontdoor/eventSchemas.ts
71859
+ var DOOR_EVENT_SOURCE_RC = "rc";
71860
+ var LAUNCH_EVENT_SOURCE = "launch";
71861
+ var DoorEventSchema = external_exports.strictObject({
71862
+ source: external_exports.string().min(1),
71863
+ /** A positive integer because the backbone's counters start at one and only ever increase. */
71864
+ sequence: external_exports.number().int().positive(),
71865
+ payload: external_exports.unknown()
71866
+ });
71867
+ var DoorEventSourceQuerySchema = external_exports.strictObject({
71868
+ sources: external_exports.array(external_exports.string().min(1)).min(1).optional()
71869
+ });
71870
+ var LaunchLifecycleEventSchema = external_exports.strictObject({
71871
+ kind: external_exports.enum(["registered", "pruned", "ended"]),
71872
+ pid: external_exports.number().int().positive(),
71873
+ startedAt: external_exports.number(),
71874
+ observedAt: external_exports.number()
71875
+ });
71876
+
71877
+ // src/frontdoor/eventsApi.ts
71878
+ function createEventsApiRouter(deps) {
71879
+ const authed = doorApiAuth(deps.expectedToken);
71880
+ return {
71881
+ events: {
71882
+ subscribe: authed.input(DoorEventSourceQuerySchema).output(eventIterator(DoorEventSchema)).handler(async function* ({ input: input2, signal }) {
71883
+ if (input2.sources !== void 0) {
71884
+ const live = deps.events.sources();
71885
+ const unknown2 = input2.sources.filter((source) => !live.includes(source));
71886
+ if (unknown2.length > 0) {
71887
+ throw new ORPCError("NOT_FOUND", { message: `the door publishes no event source named ${unknown2.map((source) => `"${source}"`).join(", ")}; the live sources are ${live.map((source) => `"${source}"`).join(", ")}` });
71888
+ }
71889
+ }
71890
+ const stream = deps.events.stream(input2.sources);
71891
+ const ended = new Promise((resolve) => {
71892
+ signal?.addEventListener("abort", () => {
71893
+ resolve();
71894
+ }, { once: true });
71895
+ });
71896
+ try {
71897
+ for (; ; ) {
71898
+ signal?.throwIfAborted();
71899
+ const next = await Promise.race([stream.next(), ended.then(() => ({ value: void 0, done: true }))]);
71900
+ if (next.done === true) {
71901
+ signal?.throwIfAborted();
71902
+ return;
71903
+ }
71904
+ yield withEventMeta(next.value, { id: String(next.value.sequence) });
71905
+ }
71906
+ } finally {
71907
+ stream.close();
71908
+ }
71909
+ })
71910
+ }
71911
+ };
71912
+ }
71913
+
71338
71914
  // src/frontdoor/controlApi.ts
71339
71915
  function createControlApiRouter(deps) {
71340
71916
  const authed = doorApiAuth(deps.expectedToken);
@@ -71373,14 +71949,156 @@ function createControlApiRouter(deps) {
71373
71949
  };
71374
71950
  }
71375
71951
  function createDoorApiRouter(deps) {
71376
- return { ...createRcApiRouter(deps), ...createControlApiRouter(deps) };
71952
+ return { ...createRcApiRouter(deps), ...createControlApiRouter(deps), ...createEventsApiRouter(deps) };
71377
71953
  }
71378
71954
  function createDoorApiNodeHandler(deps) {
71379
71955
  return doorApiNodeHandlerOf(createDoorApiRouter(deps));
71380
71956
  }
71381
71957
 
71958
+ // src/frontdoor/eventHub.ts
71959
+ var DOOR_EVENT_BUFFER_EVENTS = 100;
71960
+ function createDoorEventHub() {
71961
+ const sequences = /* @__PURE__ */ new Map();
71962
+ const registered = /* @__PURE__ */ new Set();
71963
+ const every = /* @__PURE__ */ new Set();
71964
+ const bySource = /* @__PURE__ */ new Map();
71965
+ const deliver = (event) => {
71966
+ for (const listener of every) {
71967
+ listener(event);
71968
+ }
71969
+ for (const listener of bySource.get(event.source) ?? []) {
71970
+ listener(event);
71971
+ }
71972
+ };
71973
+ const publish = (source, payload) => {
71974
+ const sequence2 = (sequences.get(source) ?? 0) + 1;
71975
+ sequences.set(source, sequence2);
71976
+ deliver({ source, sequence: sequence2, payload });
71977
+ };
71978
+ const addListener = (sources, listener) => {
71979
+ if (sources === void 0) {
71980
+ every.add(listener);
71981
+ return () => {
71982
+ every.delete(listener);
71983
+ };
71984
+ }
71985
+ const listenersFor = (source) => {
71986
+ const listeners = bySource.get(source) ?? /* @__PURE__ */ new Set();
71987
+ bySource.set(source, listeners);
71988
+ return listeners;
71989
+ };
71990
+ const detachers = sources.map((source) => {
71991
+ const listeners = listenersFor(source);
71992
+ listeners.add(listener);
71993
+ return () => {
71994
+ listeners.delete(listener);
71995
+ if (listeners.size === 0) {
71996
+ bySource.delete(source);
71997
+ }
71998
+ };
71999
+ });
72000
+ return () => {
72001
+ for (const detach of detachers) {
72002
+ detach();
72003
+ }
72004
+ };
72005
+ };
72006
+ return {
72007
+ publisher: (source) => {
72008
+ registered.add(source);
72009
+ return {
72010
+ source,
72011
+ publish: (payload) => {
72012
+ publish(source, payload);
72013
+ }
72014
+ };
72015
+ },
72016
+ subscribe: addListener,
72017
+ stream: (sources) => {
72018
+ const queue = [];
72019
+ let wake;
72020
+ let closed = false;
72021
+ const detach = addListener(sources, (event) => {
72022
+ if (queue.length >= DOOR_EVENT_BUFFER_EVENTS) {
72023
+ queue.shift();
72024
+ }
72025
+ queue.push(event);
72026
+ wake?.();
72027
+ });
72028
+ return {
72029
+ next: async () => {
72030
+ for (; ; ) {
72031
+ const event = queue.shift();
72032
+ if (event !== void 0) {
72033
+ return { value: event, done: false };
72034
+ }
72035
+ if (closed) {
72036
+ return { value: void 0, done: true };
72037
+ }
72038
+ await new Promise((resolve) => {
72039
+ wake = resolve;
72040
+ });
72041
+ }
72042
+ },
72043
+ close: () => {
72044
+ closed = true;
72045
+ detach();
72046
+ wake?.();
72047
+ }
72048
+ };
72049
+ },
72050
+ sources: () => [...registered].sort()
72051
+ };
72052
+ }
72053
+ function rcFanoutOnDoorHub(fanout, hub) {
72054
+ const publisher = hub.publisher(DOOR_EVENT_SOURCE_RC);
72055
+ return {
72056
+ publish: (event) => {
72057
+ fanout.publish(event);
72058
+ publisher.publish(event);
72059
+ },
72060
+ subscribe: (session, listener) => fanout.subscribe(session, listener)
72061
+ };
72062
+ }
72063
+
72064
+ // src/frontdoor/launchEvents.ts
72065
+ function createLaunchEventPublisher(hub, now) {
72066
+ const publisher = hub.publisher(LAUNCH_EVENT_SOURCE);
72067
+ let known;
72068
+ return {
72069
+ observe: (sessions, pruned) => {
72070
+ const previous = known;
72071
+ const listed = new Map(sessions.map((session) => [session.pid, session]));
72072
+ const prunedSet = new Set(pruned);
72073
+ known = new Map([...listed].filter(([pid]) => !prunedSet.has(pid)));
72074
+ if (previous === void 0) {
72075
+ return;
72076
+ }
72077
+ const emit = (kind, pid, startedAt) => {
72078
+ publisher.publish({ kind, pid, startedAt, observedAt: now() });
72079
+ };
72080
+ for (const pid of pruned) {
72081
+ const session = listed.get(pid);
72082
+ if (session !== void 0) {
72083
+ emit("pruned", pid, session.startedAt);
72084
+ }
72085
+ }
72086
+ for (const [pid, session] of previous) {
72087
+ if (!listed.has(pid) && !prunedSet.has(pid)) {
72088
+ emit("ended", pid, session.startedAt);
72089
+ }
72090
+ }
72091
+ for (const [pid, session] of listed) {
72092
+ if (!previous.has(pid) && !prunedSet.has(pid)) {
72093
+ emit("registered", pid, session.startedAt);
72094
+ }
72095
+ }
72096
+ }
72097
+ };
72098
+ }
72099
+
71382
72100
  // src/frontdoor/headroomHop.ts
71383
- var http3 = __toESM(require("node:http"), 1);
72101
+ var http4 = __toESM(require("node:http"), 1);
71384
72102
  var HTTP_BAD_GATEWAY2 = HTTP_STATUS.badGateway;
71385
72103
  function responseHeaders(headers) {
71386
72104
  const result = {};
@@ -71434,7 +72152,7 @@ async function answerBadGateway(response, message) {
71434
72152
  await response.write(JSON.stringify({ type: "error", error: { type: "api_error", message } }));
71435
72153
  response.end();
71436
72154
  }
71437
- var HOP_AGENT = new http3.Agent({ keepAlive: false });
72155
+ var HOP_AGENT = new http4.Agent({ keepAlive: false });
71438
72156
  async function forwardThroughHeadroom(request7, response, target) {
71439
72157
  const { socketPath, headers } = target;
71440
72158
  await new Promise((resolve) => {
@@ -71462,7 +72180,7 @@ async function forwardThroughHeadroom(request7, response, target) {
71462
72180
  };
71463
72181
  void stream();
71464
72182
  };
71465
- const hop = http3.request({ socketPath, method: request7.method, path: request7.url, headers, agent: HOP_AGENT }, onUpstreamResponse);
72183
+ const hop = http4.request({ socketPath, method: request7.method, path: request7.url, headers, agent: HOP_AGENT }, onUpstreamResponse);
71466
72184
  hop.on("error", (error62) => {
71467
72185
  const phase = response.headersSent ? "mid-stream" : "before the response started";
71468
72186
  const code = "code" in error62 && typeof error62.code === "string" ? ` ${error62.code}` : "";
@@ -71651,7 +72369,7 @@ async function serveRouted(request7, deps) {
71651
72369
  }
71652
72370
 
71653
72371
  // src/codex/translate.ts
71654
- var import_node_crypto11 = require("node:crypto");
72372
+ var import_node_crypto13 = require("node:crypto");
71655
72373
 
71656
72374
  // src/codex/anthropic.ts
71657
72375
  var TextBlockSchema = external_exports.looseObject({ type: external_exports.literal("text"), text: external_exports.string() });
@@ -71730,7 +72448,8 @@ function resolveCodexConfig(config2) {
71730
72448
  sonnet: overrides?.sonnet ?? CODEX_DEFAULT_TIER_MODELS.sonnet,
71731
72449
  haiku: overrides?.haiku ?? CODEX_DEFAULT_TIER_MODELS.haiku
71732
72450
  },
71733
- effort: config2?.effort ?? CODEX_DEFAULT_EFFORT
72451
+ effort: config2?.effort ?? CODEX_DEFAULT_EFFORT,
72452
+ login: config2?.login ?? CODEX_DEFAULT_LOGIN
71734
72453
  };
71735
72454
  }
71736
72455
  function mapModel(requested, config2) {
@@ -71763,7 +72482,7 @@ function shortToolName(name, names) {
71763
72482
  if (name.length <= CODEX_TOOL_NAME_LIMIT) {
71764
72483
  return name;
71765
72484
  }
71766
- const hash2 = (0, import_node_crypto11.createHash)("sha256").update(name).digest("hex").slice(0, TOOL_NAME_HASH_CHARS);
72485
+ const hash2 = (0, import_node_crypto13.createHash)("sha256").update(name).digest("hex").slice(0, TOOL_NAME_HASH_CHARS);
71767
72486
  const shortened = `${name.slice(0, CODEX_TOOL_NAME_LIMIT - TOOL_NAME_HASH_CHARS - 1)}_${hash2}`;
71768
72487
  names.set(shortened, name);
71769
72488
  return shortened;
@@ -72039,17 +72758,17 @@ function unifiedQuotaHeaders(upstream, rejected, quotaError) {
72039
72758
  }
72040
72759
  return headers;
72041
72760
  }
72042
- var MS_PER_SECOND5 = 1e3;
72761
+ var MS_PER_SECOND6 = 1e3;
72043
72762
  function buildUsageSnapshot(upstream, quotaError, nowMs) {
72044
72763
  const used7 = upstream.get("x-codex-primary-used-percent") ?? (quotaError === void 0 ? null : EXHAUSTED_PERCENT);
72045
72764
  const reset7 = upstream.get("x-codex-primary-reset-at") ?? (quotaError?.resets_at === void 0 ? null : String(quotaError.resets_at));
72046
- const sevenDay = used7 !== null && reset7 !== null ? { used_percentage: Number(used7), resets_at: new Date(Number(reset7) * MS_PER_SECOND5).toISOString() } : void 0;
72765
+ const sevenDay = used7 !== null && reset7 !== null ? { used_percentage: Number(used7), resets_at: new Date(Number(reset7) * MS_PER_SECOND6).toISOString() } : void 0;
72047
72766
  let fiveHour;
72048
72767
  if (Number(upstream.get("x-codex-secondary-window-minutes")) > 0) {
72049
72768
  const used5 = upstream.get("x-codex-secondary-used-percent");
72050
72769
  const reset5 = upstream.get("x-codex-secondary-reset-at");
72051
72770
  if (used5 !== null && reset5 !== null) {
72052
- fiveHour = { used_percentage: Number(used5), resets_at: new Date(Number(reset5) * MS_PER_SECOND5).toISOString() };
72771
+ fiveHour = { used_percentage: Number(used5), resets_at: new Date(Number(reset5) * MS_PER_SECOND6).toISOString() };
72053
72772
  }
72054
72773
  }
72055
72774
  if (sevenDay === void 0 && fiveHour === void 0) {
@@ -72266,12 +72985,13 @@ function createCodexRoute(ports) {
72266
72985
  }
72267
72986
  const body = parsed.data;
72268
72987
  const requestedModel = body.model ?? "";
72988
+ const upstreamPorts = ports.upstreams[config2.login];
72269
72989
  const { request: upstreamRequest, toolNames } = translateRequest(body, config2);
72270
72990
  const started = ports.now();
72271
72991
  const elapsed = () => `${String(ports.now() - started)}ms`;
72272
72992
  let upstream;
72273
72993
  try {
72274
- upstream = await callCodex(ports.upstream, upstreamRequest, sessionIdFor(body.metadata?.user_id, ports.upstream.randomId), request7.signal);
72994
+ upstream = await callCodex(upstreamPorts, upstreamRequest, sessionIdFor(body.metadata?.user_id, upstreamPorts.randomId), request7.signal);
72275
72995
  } catch (error62) {
72276
72996
  if (request7.signal.aborted) {
72277
72997
  return errorResponse(HTTP_STATUS.clientClosedRequest, "client disconnected");
@@ -72304,7 +73024,7 @@ function createCodexRoute(ports) {
72304
73024
  });
72305
73025
  }
72306
73026
  snapshot(void 0);
72307
- const relay = createRelay({ requestedModel, toolNames, fallbackId: () => `msg_${ports.upstream.randomId()}` });
73027
+ const relay = createRelay({ requestedModel, toolNames, fallbackId: () => `msg_${upstreamPorts.randomId()}` });
72308
73028
  const upstreamBody = upstream.body;
72309
73029
  const quota = { ...quotaHeaders(upstream.headers), ...unifiedQuotaHeaders(upstream.headers, false, void 0) };
72310
73030
  const logCompleted = (message, usage) => {
@@ -72469,7 +73189,7 @@ function createCodexRouteMount(ports, upstreamBase, provider) {
72469
73189
  }
72470
73190
 
72471
73191
  // src/frontdoor/passthrough.ts
72472
- var http4 = __toESM(require("node:http"), 1);
73192
+ var http5 = __toESM(require("node:http"), 1);
72473
73193
  var https4 = __toESM(require("node:https"), 1);
72474
73194
  function responseHeaders2(headers) {
72475
73195
  const result = {};
@@ -72523,7 +73243,7 @@ function createPassthroughRoute(name, target) {
72523
73243
  };
72524
73244
  void stream();
72525
73245
  };
72526
- const forward = tls2 ? https4.request(options, onUpstreamResponse) : http4.request(options, onUpstreamResponse);
73246
+ const forward = tls2 ? https4.request(options, onUpstreamResponse) : http5.request(options, onUpstreamResponse);
72527
73247
  forward.on("error", (error62) => {
72528
73248
  if (!response.headersSent) {
72529
73249
  response.start(HTTP_STATUS.badGateway, { "Content-Type": "application/json" });
@@ -72689,6 +73409,465 @@ function createRcCredentialStore(fs19, dir) {
72689
73409
  };
72690
73410
  }
72691
73411
 
73412
+ // src/frontdoor/rcClientPage.ts
73413
+ var RC_CLIENT_PATH = "/__agent-shim/client";
73414
+ var RC_CLIENT_PAGE_SCRIPT = `'use strict';
73415
+ // The door's typed API as this page speaks it: the oRPC RPC protocol over plain fetch, which is one POST per procedure with a {"json": input} body and a {"json": output} answer, so no client library is needed. These helpers are plain top-level functions (not boot-scoped) so the page's tests can evaluate this script outside a browser and drive them against a mounted listener.
73416
+ var API_PREFIX = '/__agent-shim/orpc';
73417
+ var TOKEN_KEY = 'agent-shim-rc-token';
73418
+ var token = null;
73419
+
73420
+ function apiBase() { return window.location.origin + API_PREFIX; }
73421
+
73422
+ // Calls one procedure: resolves the output's own value, rejects with the door's error message when it answered one.
73423
+ async function rpc(path, input) {
73424
+ var response = await fetch(apiBase() + '/' + path, {
73425
+ method: 'POST',
73426
+ headers: { 'content-type': 'application/json', authorization: 'Bearer ' + token },
73427
+ body: JSON.stringify({ json: input === undefined ? {} : input }),
73428
+ });
73429
+ var envelope = await response.json().catch(function () { return {}; });
73430
+ if (!response.ok) {
73431
+ var message = envelope && envelope.json && envelope.json.message ? envelope.json.message : ('HTTP ' + response.status);
73432
+ var error = new Error(message);
73433
+ error.status = response.status;
73434
+ throw error;
73435
+ }
73436
+ return envelope.json;
73437
+ }
73438
+
73439
+ // Parses every complete SSE frame out of the buffered text, returning the frames and the unparsed remainder. Comment frames (the door's keepalive opens with one) carry no event name and are dropped.
73440
+ function parseSseFrames(buffer) {
73441
+ var frames = [];
73442
+ var rest = buffer;
73443
+ for (;;) {
73444
+ var boundary = rest.indexOf('\\n\\n');
73445
+ if (boundary === -1) { break; }
73446
+ var raw = rest.slice(0, boundary);
73447
+ rest = rest.slice(boundary + 2);
73448
+ var frame = { event: 'message', id: '', data: '' };
73449
+ var lines = raw.split('\\n');
73450
+ for (var i = 0; i < lines.length; i++) {
73451
+ var line = lines[i];
73452
+ if (line === '' || line.charAt(0) === ':') { continue; }
73453
+ var colon = line.indexOf(':');
73454
+ var field = colon === -1 ? line : line.slice(0, colon);
73455
+ var value = colon === -1 ? '' : line.slice(colon + 1);
73456
+ if (value.charAt(0) === ' ') { value = value.slice(1); }
73457
+ if (field === 'event') { frame.event = value; }
73458
+ if (field === 'id') { frame.id = value; }
73459
+ if (field === 'data') { frame.data += (frame.data === '' ? '' : '\\n') + value; }
73460
+ }
73461
+ if (frame.data !== '') { frames.push(frame); }
73462
+ }
73463
+ return { frames: frames, rest: rest };
73464
+ }
73465
+
73466
+ // Whether one source-tagged event continues the per-source sequence the tracker holds: the count of events the stream dropped when it does not, zero when it does. The tracker keeps the high-water mark, so a number the door re-sends after a reconnect is a duplicate, not a step back, and never reports a negative gap. The backbone holds no replay, so a gap is reported, never hidden.
73467
+ function sequenceGap(tracker, event) {
73468
+ var previous = tracker[event.source];
73469
+ if (event.sequence > (previous === undefined ? 0 : previous)) { tracker[event.source] = event.sequence; }
73470
+ if (previous === undefined || event.sequence <= previous) { return 0; }
73471
+ return event.sequence - previous - 1;
73472
+ }
73473
+
73474
+ function shortId(id) { return id.length > 14 ? id.slice(0, 8) + '..' + id.slice(-4) : id; }
73475
+
73476
+ // A bounded sketch of any payload, the feed's fallback rendering: one JSON line, cut with a marker rather than silently.
73477
+ function sketch(value, cap) {
73478
+ var text;
73479
+ try { text = JSON.stringify(value); } catch (e) { text = String(value); }
73480
+ if (text === undefined) { text = 'undefined'; }
73481
+ return text.length > cap ? text.slice(0, cap) + '...' : text;
73482
+ }
73483
+
73484
+ // The headline text of one Remote Control stream event: the assistant and user payloads that carry a content array of text blocks render as their prose, everything else as its type plus a sketch.
73485
+ function rcLine(event) {
73486
+ var envelope = event.envelope;
73487
+ var payload = envelope.payload;
73488
+ var headline = '';
73489
+ if (payload !== undefined && payload !== null && typeof payload === 'object' && Array.isArray(payload.content)) {
73490
+ var parts = [];
73491
+ for (var i = 0; i < payload.content.length; i++) {
73492
+ var block = payload.content[i];
73493
+ if (block !== null && typeof block === 'object' && block.type === 'text' && typeof block.text === 'string') { parts.push(block.text); }
73494
+ }
73495
+ if (parts.length > 0) { headline = parts.join(' '); }
73496
+ }
73497
+ if (headline === '') {
73498
+ headline = envelope.event_type;
73499
+ if (payload !== undefined) { headline += ' ' + sketch(payload, 240); }
73500
+ }
73501
+ return new Date().toLocaleTimeString() + ' ' + shortId(event.session) + ' ' + envelope.event_type + ' ' + headline;
73502
+ }
73503
+
73504
+ function el(tag, className, text) {
73505
+ var node = document.createElement(tag);
73506
+ if (className) { node.className = className; }
73507
+ if (text !== undefined) { node.textContent = text; }
73508
+ return node;
73509
+ }
73510
+
73511
+ var ui = null;
73512
+
73513
+ function renderEvent(event, gap) {
73514
+ if (ui === null) { return; }
73515
+ var list = ui.eventList;
73516
+ if (gap > 0) {
73517
+ list.appendChild(el('div', 'event gap', 'dropped events: ' + String(gap) + ' on source ' + event.source + ' (the stream holds no replay)'));
73518
+ }
73519
+ var line;
73520
+ if (event.source === 'rc') {
73521
+ line = rcLine(event.payload);
73522
+ } else {
73523
+ line = new Date().toLocaleTimeString() + ' [' + event.source + '] ' + sketch(event.payload, 240);
73524
+ }
73525
+ list.appendChild(el('div', 'event', line));
73526
+ while (list.childElementCount > 500) { list.removeChild(list.firstChild); }
73527
+ list.scrollTop = list.scrollHeight;
73528
+ }
73529
+
73530
+ function renderSessions(statuses) {
73531
+ if (ui === null) { return; }
73532
+ var list = ui.sessionList;
73533
+ list.textContent = '';
73534
+ if (statuses.length === 0) {
73535
+ list.appendChild(el('div', 'event dim', 'no Remote Control sessions observed'));
73536
+ return;
73537
+ }
73538
+ for (var i = 0; i < statuses.length; i++) {
73539
+ var status = statuses[i];
73540
+ var worker = status.workerState === undefined ? 'worker unknown' : 'worker ' + status.workerState.value;
73541
+ var row = el('div', 'session' + (status.id === ui.selected ? ' selected' : ''));
73542
+ row.appendChild(el('div', null, shortId(status.id)));
73543
+ row.appendChild(el('div', 'dim', worker + (status.pending.length === 0 ? '' : ', ' + String(status.pending.length) + ' pending') + ', seen ' + new Date(status.lastSeenAt).toLocaleTimeString()));
73544
+ row.addEventListener('click', selectSession(status.id));
73545
+ list.appendChild(row);
73546
+ }
73547
+ }
73548
+
73549
+ function selectSession(id) {
73550
+ return function () {
73551
+ if (ui === null) { return; }
73552
+ ui.selected = ui.selected === id ? null : id;
73553
+ ui.promptTarget.textContent = ui.selected === null ? 'no session selected' : 'to ' + ui.selected;
73554
+ refreshSessions();
73555
+ refreshPending();
73556
+ };
73557
+ }
73558
+
73559
+ function renderPending(pending) {
73560
+ if (ui === null) { return; }
73561
+ var list = ui.pendingList;
73562
+ list.textContent = '';
73563
+ if (pending.length === 0) {
73564
+ list.appendChild(el('div', 'event dim', 'no control requests awaiting an answer'));
73565
+ return;
73566
+ }
73567
+ for (var i = 0; i < pending.length; i++) {
73568
+ var request = pending[i];
73569
+ var card = el('div', 'card');
73570
+ card.appendChild(el('div', null, request.type + ' ' + (request.summary === '' ? shortId(request.requestId) : request.summary)));
73571
+ card.appendChild(el('div', 'dim', shortId(request.sessionId) + ' ' + shortId(request.requestId) + ' observed ' + new Date(request.observedAt).toLocaleTimeString()));
73572
+ var row = el('div', 'row');
73573
+ var denial = el('input');
73574
+ denial.type = 'text';
73575
+ denial.placeholder = 'denial message (optional)';
73576
+ var approve = el('button', null, 'Approve');
73577
+ var deny = el('button', 'secondary', 'Deny');
73578
+ approve.addEventListener('click', answerRequest(request, true, denial));
73579
+ deny.addEventListener('click', answerRequest(request, false, denial));
73580
+ row.appendChild(denial);
73581
+ row.appendChild(approve);
73582
+ row.appendChild(deny);
73583
+ card.appendChild(row);
73584
+ list.appendChild(card);
73585
+ }
73586
+ }
73587
+
73588
+ function answerRequest(request, approve, denialInput) {
73589
+ return function () {
73590
+ var input = { session: request.sessionId, request: request.requestId, approve: approve };
73591
+ if (!approve && denialInput.value !== '') { input.text = denialInput.value; }
73592
+ rpc('rc/answer', input).then(function () {
73593
+ renderEvent({ source: 'page', sequence: 0, payload: { note: (approve ? 'approved' : 'denied') + ' ' + request.requestId } }, 0);
73594
+ refreshPending();
73595
+ }, function (error) { window.alert('answer failed: ' + error.message); });
73596
+ };
73597
+ }
73598
+
73599
+ function sendPrompt() {
73600
+ if (ui === null || ui.selected === null) { window.alert('select a session first'); return; }
73601
+ var text = ui.prompt.value;
73602
+ if (text === '') { return; }
73603
+ ui.send.disabled = true;
73604
+ rpc('rc/send', { session: ui.selected, text: text }).then(function (result) {
73605
+ ui.prompt.value = '';
73606
+ renderEvent({ source: 'page', sequence: 0, payload: { note: 'sent, sequence_num ' + result.sequenceNums.join(', ') } }, 0);
73607
+ }, function (error) { window.alert('send failed: ' + error.message); }).then(function () { ui.send.disabled = false; });
73608
+ }
73609
+
73610
+ function refreshSessions() {
73611
+ rpc('rc/status', {}).then(function (result) { renderSessions(result.statuses); }, authOrReport('rc/status'));
73612
+ }
73613
+
73614
+ function refreshPending() {
73615
+ rpc('rc/pending', ui !== null && ui.selected !== null ? { session: ui.selected } : {}).then(function (result) { renderPending(result.pending); }, authOrReport('rc/pending'));
73616
+ }
73617
+
73618
+ // The one error path every call shares: a 401 means this token is not this generation's, so the page says so and asks for it again; anything else is reported in the feed.
73619
+ function authOrReport(what) {
73620
+ return function (error) {
73621
+ if (error.status === 401) {
73622
+ handleUnauthorized();
73623
+ return;
73624
+ }
73625
+ renderEvent({ source: 'page', sequence: 0, payload: { note: what + ' failed: ' + error.message } }, 0);
73626
+ };
73627
+ }
73628
+
73629
+ function handleUnauthorized() {
73630
+ window.sessionStorage.removeItem(TOKEN_KEY);
73631
+ if (feedAbort !== null) { feedAbort.abort(); }
73632
+ streamState('');
73633
+ showTokenForm('this door refused the token: it names a different door generation, paste the current one');
73634
+ }
73635
+
73636
+ function streamState(text) { if (ui !== null) { ui.streamState.textContent = text; } }
73637
+
73638
+ var feedAbort = null;
73639
+
73640
+ // Subscribes to the door's backbone and renders what it yields, reconnecting on any drop. The stream holds no replay, so a reconnect starts at the live head and the per-source sequence numbers surface whatever the gap swallowed.
73641
+ async function runFeed() {
73642
+ var tracker = {};
73643
+ for (;;) {
73644
+ if (feedAbort !== null) { feedAbort.abort(); }
73645
+ feedAbort = new AbortController();
73646
+ var filter = document.getElementById('source-filter').value;
73647
+ var input = filter === '' ? {} : { sources: [filter] };
73648
+ try {
73649
+ streamState('stream: connecting');
73650
+ var response = await fetch(apiBase() + '/events/subscribe', {
73651
+ method: 'POST',
73652
+ headers: { 'content-type': 'application/json', authorization: 'Bearer ' + token },
73653
+ body: JSON.stringify({ json: input }),
73654
+ signal: feedAbort.signal,
73655
+ });
73656
+ if (response.status === 401) { handleUnauthorized(); return; }
73657
+ if (!response.ok || response.body === null) { throw new Error('HTTP ' + String(response.status)); }
73658
+ streamState('stream: live');
73659
+ var reader = response.body.getReader();
73660
+ var decoder = new TextDecoder();
73661
+ var buffer = '';
73662
+ for (;;) {
73663
+ var chunk = await reader.read();
73664
+ if (chunk.done) { break; }
73665
+ buffer += decoder.decode(chunk.value, { stream: true });
73666
+ var parsed = parseSseFrames(buffer);
73667
+ buffer = parsed.rest;
73668
+ for (var i = 0; i < parsed.frames.length; i++) {
73669
+ var frame = parsed.frames[i];
73670
+ if (frame.event !== 'message') { continue; }
73671
+ var event;
73672
+ try { event = JSON.parse(frame.data).json; } catch (e) { continue; }
73673
+ if (event === undefined || event.source === undefined) { continue; }
73674
+ renderEvent(event, sequenceGap(tracker, event));
73675
+ }
73676
+ }
73677
+ } catch (error) {
73678
+ if (feedAbort !== null && feedAbort.signal.aborted) { return; }
73679
+ }
73680
+ streamState('stream: dropped, reconnecting');
73681
+ await new Promise(function (resolve) { window.setTimeout(resolve, 1000); });
73682
+ }
73683
+ }
73684
+
73685
+ function showTokenForm(message) {
73686
+ if (feedAbort !== null) { feedAbort.abort(); }
73687
+ if (ui !== null) { ui.main.classList.add('hidden'); }
73688
+ var form = document.getElementById('token-form');
73689
+ form.classList.remove('hidden');
73690
+ document.getElementById('token-error').textContent = message === undefined ? '' : message;
73691
+ document.getElementById('token-input').focus();
73692
+ }
73693
+
73694
+ function startPage(heldToken) {
73695
+ token = heldToken;
73696
+ ui = {
73697
+ main: document.getElementById('main'),
73698
+ eventList: document.getElementById('event-list'),
73699
+ sessionList: document.getElementById('session-list'),
73700
+ pendingList: document.getElementById('pending-list'),
73701
+ prompt: document.getElementById('prompt'),
73702
+ promptTarget: document.getElementById('prompt-target'),
73703
+ send: document.getElementById('send'),
73704
+ streamState: document.getElementById('stream-state'),
73705
+ selected: null,
73706
+ };
73707
+ document.getElementById('token-form').classList.add('hidden');
73708
+ ui.main.classList.remove('hidden');
73709
+ document.getElementById('refresh').addEventListener('click', function () { refreshSessions(); refreshPending(); });
73710
+ document.getElementById('send').addEventListener('click', sendPrompt);
73711
+ document.getElementById('source-filter').addEventListener('change', function () { runFeed(); });
73712
+ ui.prompt.addEventListener('keydown', function (event) {
73713
+ if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) { sendPrompt(); }
73714
+ });
73715
+ refreshSessions();
73716
+ refreshPending();
73717
+ window.setInterval(function () { refreshSessions(); refreshPending(); }, 4000);
73718
+ runFeed();
73719
+ }
73720
+
73721
+ function boot() {
73722
+ var held = null;
73723
+ var query = new URLSearchParams(window.location.search);
73724
+ var fromQuery = query.get('token');
73725
+ if (fromQuery !== null && fromQuery !== '') {
73726
+ window.sessionStorage.setItem(TOKEN_KEY, fromQuery);
73727
+ // The token leaves the address bar the moment the page holds it, so a refresh or a shared screen shows the clean URL; sessionStorage keeps the tab working.
73728
+ window.history.replaceState(null, '', window.location.pathname);
73729
+ held = fromQuery;
73730
+ } else {
73731
+ held = window.sessionStorage.getItem(TOKEN_KEY);
73732
+ }
73733
+ document.getElementById('token-save').addEventListener('click', function () {
73734
+ var value = document.getElementById('token-input').value.trim();
73735
+ if (value === '') { return; }
73736
+ window.sessionStorage.setItem(TOKEN_KEY, value);
73737
+ startPage(value);
73738
+ });
73739
+ document.getElementById('token-input').addEventListener('keydown', function (event) {
73740
+ if (event.key === 'Enter') { document.getElementById('token-save').click(); }
73741
+ });
73742
+ if (held !== null && held !== '') { startPage(held); }
73743
+ }
73744
+
73745
+ if (typeof document !== 'undefined' && document.getElementById('event-list') !== null) {
73746
+ window.addEventListener('DOMContentLoaded', boot);
73747
+ }
73748
+ `;
73749
+ var RC_CLIENT_PAGE_HTML = `<!doctype html>
73750
+ <html lang="en">
73751
+ <head>
73752
+ <meta charset="utf-8">
73753
+ <meta name="viewport" content="width=device-width, initial-scale=1">
73754
+ <title>agent-shim Remote Control</title>
73755
+ <style>
73756
+ :root { color-scheme: dark; }
73757
+ * { box-sizing: border-box; }
73758
+ body { margin: 0; background: #101418; color: #d7dde3; font: 14px/1.45 ui-monospace, Menlo, Consolas, monospace; }
73759
+ header { display: flex; gap: 12px; align-items: baseline; padding: 10px 16px; background: #171d24; border-bottom: 1px solid #2a333d; }
73760
+ header h1 { font-size: 15px; margin: 0; font-weight: 600; }
73761
+ header .state { color: #8b98a5; }
73762
+ main { display: flex; gap: 1px; background: #2a333d; min-height: calc(100vh - 39px); }
73763
+ section { background: #101418; padding: 12px 16px; overflow-y: auto; }
73764
+ #sessions { flex: 0 0 320px; border-right: 1px solid #2a333d; }
73765
+ #right { flex: 1; display: flex; flex-direction: column; min-width: 0; }
73766
+ #feed { flex: 1; }
73767
+ #controls { border-top: 1px solid #2a333d; }
73768
+ h2 { font-size: 12px; text-transform: uppercase; letter-spacing: 0.08em; color: #8b98a5; margin: 8px 0 8px; font-weight: 600; }
73769
+ .session { padding: 6px 8px; border: 1px solid transparent; border-radius: 4px; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
73770
+ .session:hover { background: #171d24; }
73771
+ .session.selected { border-color: #3d6ea5; background: #17222e; }
73772
+ .session .dim { color: #8b98a5; }
73773
+ .event { padding: 2px 0; white-space: pre-wrap; word-break: break-word; }
73774
+ .event .dim { color: #8b98a5; }
73775
+ .event.gap { color: #d29922; }
73776
+ .event.error { color: #e5534b; }
73777
+ textarea, input[type=text], select { background: #171d24; color: #d7dde3; border: 1px solid #2a333d; border-radius: 4px; padding: 6px 8px; font: inherit; width: 100%; }
73778
+ textarea { resize: vertical; min-height: 44px; }
73779
+ button { background: #1f4a7a; color: #d7dde3; border: 1px solid #3d6ea5; border-radius: 4px; padding: 5px 12px; font: inherit; cursor: pointer; }
73780
+ button.secondary { background: #2a333d; border-color: #3a444f; }
73781
+ button:disabled { opacity: 0.5; cursor: default; }
73782
+ .row { display: flex; gap: 8px; margin: 6px 0; align-items: center; }
73783
+ .card { border: 1px solid #3d6ea5; border-radius: 4px; padding: 8px; margin: 6px 0; background: #17222e; }
73784
+ .card .dim { color: #8b98a5; }
73785
+ #token-form { max-width: 480px; margin: 48px auto; }
73786
+ #token-form p { color: #8b98a5; }
73787
+ .hidden { display: none !important; }
73788
+ </style>
73789
+ </head>
73790
+ <body data-agent-shim-rc-client>
73791
+ <header>
73792
+ <h1>agent-shim Remote Control</h1>
73793
+ <span class="state" id="door-state">not connected</span>
73794
+ <span class="state" id="stream-state"></span>
73795
+ </header>
73796
+ <div id="token-form">
73797
+ <h2>This door's control token</h2>
73798
+ <p>The page is served behind the door's per-generation control token. Paste this door generation's token (the owner-only value the door writes beside its state) to use the page; it stays in this tab only.</p>
73799
+ <div class="row"><input type="text" id="token-input" placeholder="control token" autocomplete="off"><button id="token-save">Use</button></div>
73800
+ <p id="token-error" class="event error"></p>
73801
+ </div>
73802
+ <main class="hidden" id="main">
73803
+ <section id="sessions">
73804
+ <h2>Sessions <button class="secondary" id="refresh">Refresh</button></h2>
73805
+ <div id="session-list"></div>
73806
+ </section>
73807
+ <section id="right">
73808
+ <div id="feed">
73809
+ <h2>Feed <select id="source-filter"><option value="">all sources</option><option value="rc">Remote Control only</option></select></h2>
73810
+ <div id="event-list"></div>
73811
+ </div>
73812
+ <div id="controls">
73813
+ <h2>Pending approvals</h2>
73814
+ <div id="pending-list"></div>
73815
+ <h2>Send a prompt</h2>
73816
+ <div class="row"><textarea id="prompt" placeholder="Prompt for the selected session"></textarea></div>
73817
+ <div class="row"><span class="dim" id="prompt-target">no session selected</span><span style="flex:1"></span><button id="send">Send</button></div>
73818
+ </div>
73819
+ </section>
73820
+ </main>
73821
+ <script>
73822
+ ${RC_CLIENT_PAGE_SCRIPT}
73823
+ </script>
73824
+ </body>
73825
+ </html>
73826
+ `;
73827
+ function pageTokenOf(request7, url2) {
73828
+ const presented = request7.headers.authorization;
73829
+ const bearerPrefix = "bearer ".length;
73830
+ const header = typeof presented === "string" && presented.slice(0, bearerPrefix).toLowerCase() === "bearer " ? presented.slice(bearerPrefix) : void 0;
73831
+ const query = url2.searchParams.get("token") ?? void 0;
73832
+ return header ?? query;
73833
+ }
73834
+ function serveRcClientPage(expectedToken, request7, response) {
73835
+ const url2 = new URL(request7.url ?? "/", "https://door.invalid");
73836
+ if (url2.pathname !== RC_CLIENT_PATH && url2.pathname !== `${RC_CLIENT_PATH}/`) {
73837
+ response.writeHead(HTTP_STATUS.notFound, { "Content-Type": "text/plain" });
73838
+ response.end("no such page under the door's client prefix");
73839
+ return;
73840
+ }
73841
+ if (request7.method !== "GET" && request7.method !== "HEAD") {
73842
+ response.writeHead(HTTP_STATUS.methodNotAllowed, { Allow: "GET, HEAD", "Content-Type": "text/plain" });
73843
+ response.end("the door's web client page is read-only");
73844
+ return;
73845
+ }
73846
+ const token = pageTokenOf(request7, url2);
73847
+ if (token === void 0 || !isLiveCapability(token, [expectedToken])) {
73848
+ response.writeHead(HTTP_STATUS.unauthorized, { "Content-Type": "application/json" });
73849
+ response.end(JSON.stringify({ error: "the door's web client demands this generation's control token, as a Bearer credential or the token query parameter" }));
73850
+ return;
73851
+ }
73852
+ response.writeHead(HTTP_STATUS.ok, {
73853
+ "Content-Type": "text/html; charset=utf-8",
73854
+ "Cache-Control": "no-store",
73855
+ "Referrer-Policy": "no-referrer",
73856
+ "Content-Security-Policy": "default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; connect-src 'self'"
73857
+ });
73858
+ response.end(request7.method === "HEAD" ? void 0 : RC_CLIENT_PAGE_HTML);
73859
+ }
73860
+ function createRcClientPage(expectedToken) {
73861
+ return {
73862
+ pathPrefix: RC_CLIENT_PATH,
73863
+ handle: async (request7, response) => {
73864
+ serveRcClientPage(expectedToken, request7, response);
73865
+ await Promise.resolve();
73866
+ return { matched: true };
73867
+ }
73868
+ };
73869
+ }
73870
+
72692
73871
  // src/frontdoor/rcSelfHost.ts
72693
73872
  var RC_SELF_HOST_ENV = "AGENT_SHIM_FRONTDOOR_RC_SELF_HOST";
72694
73873
  function rcSelfHostFromEnv(env) {
@@ -72701,7 +73880,7 @@ var RC_SESSION_ID_PREFIX2 = "cse_";
72701
73880
  var RC_SELF_HOST_API_BASE_URL = "https://api.anthropic.com";
72702
73881
  var RC_SELF_HOST_OAUTH_HOST = "platform.claude.com";
72703
73882
  var HTTP_CONFLICT = 409;
72704
- var MS_PER_SECOND6 = 1e3;
73883
+ var MS_PER_SECOND7 = 1e3;
72705
73884
  var HTTP_SERVICE_UNAVAILABLE = 503;
72706
73885
  var RC_SELF_HOST_WORKER_JWT_TTL_SECONDS = 46800;
72707
73886
  var RC_SELF_HOST_OAUTH_TOKEN_TTL_SECONDS = 3600;
@@ -72776,7 +73955,7 @@ function parseJsonObject2(body) {
72776
73955
  function mintWorkerJwt(randomToken, issuedAt) {
72777
73956
  const encode3 = (value2) => Buffer.from(JSON.stringify(value2), "utf8").toString("base64url");
72778
73957
  const header = encode3({ alg: "HS256", typ: "JWT" });
72779
- const payload = encode3({ exp: Math.floor((issuedAt + RC_SELF_HOST_WORKER_JWT_TTL_SECONDS * MS_PER_SECOND6) / MS_PER_SECOND6), iat: Math.floor(issuedAt / MS_PER_SECOND6) });
73958
+ const payload = encode3({ exp: Math.floor((issuedAt + RC_SELF_HOST_WORKER_JWT_TTL_SECONDS * MS_PER_SECOND7) / MS_PER_SECOND7), iat: Math.floor(issuedAt / MS_PER_SECOND7) });
72780
73959
  return `${header}.${payload}.${Buffer.from(randomToken(), "utf8").toString("base64url")}`;
72781
73960
  }
72782
73961
  function growthBookFeatures() {
@@ -73227,7 +74406,7 @@ data: ${JSON.stringify(envelope)}
73227
74406
  }
73228
74407
  if (method === "POST" && pathname.startsWith("/api/eval/")) {
73229
74408
  await readBody3(request7, CONTROL_BODY_CAP_BYTES);
73230
- answerServerJson(response, HTTP_STATUS.ok, { features: growthBookFeatures(), dateUpdated: Math.floor(deps.now() / MS_PER_SECOND6) });
74409
+ answerServerJson(response, HTTP_STATUS.ok, { features: growthBookFeatures(), dateUpdated: Math.floor(deps.now() / MS_PER_SECOND7) });
73231
74410
  return true;
73232
74411
  }
73233
74412
  if (method === "GET" && pathname === "/api/oauth/profile") {
@@ -73715,7 +74894,7 @@ var ProviderQuotaError = class extends Error {
73715
74894
  this.name = "ProviderQuotaError";
73716
74895
  }
73717
74896
  };
73718
- var MS_PER_SECOND7 = 1e3;
74897
+ var MS_PER_SECOND8 = 1e3;
73719
74898
  var SECONDS_PER_HOUR = 3600;
73720
74899
  var HOURS_PER_DAY3 = 24;
73721
74900
  var DAYS_PER_MONTH = 30;
@@ -73727,8 +74906,8 @@ var ZAI_QUOTA_URL = `https://${ZAI_HOST}/api/monitor/usage/quota/limit`;
73727
74906
  var ZAI_UNIT_HOURS = 3;
73728
74907
  var ZAI_UNIT_MONTHS = 5;
73729
74908
  var ZAI_UNIT_MS = /* @__PURE__ */ new Map([
73730
- [ZAI_UNIT_HOURS, SECONDS_PER_HOUR * MS_PER_SECOND7],
73731
- [ZAI_UNIT_MONTHS, DAYS_PER_MONTH * HOURS_PER_DAY3 * SECONDS_PER_HOUR * MS_PER_SECOND7]
74909
+ [ZAI_UNIT_HOURS, SECONDS_PER_HOUR * MS_PER_SECOND8],
74910
+ [ZAI_UNIT_MONTHS, DAYS_PER_MONTH * HOURS_PER_DAY3 * SECONDS_PER_HOUR * MS_PER_SECOND8]
73732
74911
  ]);
73733
74912
  var ZAI_MEASURES = { TOKENS_LIMIT: "tokens", TIME_LIMIT: "tool-calls" };
73734
74913
  var ZaiLimitSchema = external_exports.object({
@@ -73761,8 +74940,8 @@ function zaiWindow(limit) {
73761
74940
  var zaiQuotaAdapter = {
73762
74941
  source: ZAI_SOURCE,
73763
74942
  matches: (baseUrl) => URL.canParse(baseUrl) && new URL(baseUrl).hostname === ZAI_HOST,
73764
- fetch: async (apiKey, http6, signal, nowMs) => {
73765
- const response = await http6(ZAI_QUOTA_URL, { authorization: apiKey, accept: "application/json" }, signal);
74943
+ fetch: async (apiKey, http7, signal, nowMs) => {
74944
+ const response = await http7(ZAI_QUOTA_URL, { authorization: apiKey, accept: "application/json" }, signal);
73766
74945
  if (response.status !== HTTP_OK) {
73767
74946
  throw new ProviderQuotaError(ZAI_SOURCE, `answered HTTP ${String(response.status)}`);
73768
74947
  }
@@ -73792,7 +74971,7 @@ var OPENROUTER_HOST = "openrouter.ai";
73792
74971
  var OPENROUTER_KEY_URL = `https://${OPENROUTER_HOST}/api/v1/key`;
73793
74972
  var OPENROUTER_CREDITS_URL = `https://${OPENROUTER_HOST}/api/v1/credits`;
73794
74973
  var HTTP_FORBIDDEN = 403;
73795
- var MS_PER_DAY2 = HOURS_PER_DAY3 * SECONDS_PER_HOUR * MS_PER_SECOND7;
74974
+ var MS_PER_DAY2 = HOURS_PER_DAY3 * SECONDS_PER_HOUR * MS_PER_SECOND8;
73796
74975
  var OpenRouterKeySchema = external_exports.object({
73797
74976
  data: external_exports.object({
73798
74977
  limit: external_exports.number().nonnegative().nullable(),
@@ -73803,8 +74982,8 @@ var OpenRouterKeySchema = external_exports.object({
73803
74982
  })
73804
74983
  });
73805
74984
  var OpenRouterCreditsSchema = external_exports.object({ data: external_exports.object({ total_credits: external_exports.number().nonnegative(), total_usage: external_exports.number().nonnegative() }) });
73806
- async function openRouterGet(url2, schema, apiKey, http6, signal) {
73807
- const response = await http6(url2, { authorization: `Bearer ${apiKey}`, accept: "application/json" }, signal);
74985
+ async function openRouterGet(url2, schema, apiKey, http7, signal) {
74986
+ const response = await http7(url2, { authorization: `Bearer ${apiKey}`, accept: "application/json" }, signal);
73808
74987
  if (response.status === HTTP_FORBIDDEN) {
73809
74988
  return "forbidden";
73810
74989
  }
@@ -73829,12 +75008,12 @@ function nextUtcMidnight(nowMs) {
73829
75008
  var openRouterQuotaAdapter = {
73830
75009
  source: OPENROUTER_SOURCE,
73831
75010
  matches: (baseUrl) => URL.canParse(baseUrl) && new URL(baseUrl).hostname === OPENROUTER_HOST,
73832
- fetch: async (apiKey, http6, signal, nowMs) => {
73833
- const key = await openRouterGet(OPENROUTER_KEY_URL, OpenRouterKeySchema, apiKey, http6, signal);
75011
+ fetch: async (apiKey, http7, signal, nowMs) => {
75012
+ const key = await openRouterGet(OPENROUTER_KEY_URL, OpenRouterKeySchema, apiKey, http7, signal);
73834
75013
  if (key === "forbidden") {
73835
75014
  throw new ProviderQuotaError(OPENROUTER_SOURCE, "/api/v1/key refused the key (HTTP 403)");
73836
75015
  }
73837
- const credits = await openRouterGet(OPENROUTER_CREDITS_URL, OpenRouterCreditsSchema, apiKey, http6, signal);
75016
+ const credits = await openRouterGet(OPENROUTER_CREDITS_URL, OpenRouterCreditsSchema, apiKey, http7, signal);
73838
75017
  const { limit, limit_remaining: limitRemaining, limit_reset: limitReset, free_model_daily_requests: free, is_free_tier: isFreeTier } = key.data;
73839
75018
  const windows = [];
73840
75019
  if (limit !== null && limit > 0) {
@@ -74010,7 +75189,7 @@ function realFrontDoorSupervisorPorts(paths) {
74010
75189
  };
74011
75190
  let directPort = 0;
74012
75191
  const rcSelfHost = rcSelfHostFromEnv(process.env);
74013
- const codexPorts = createCodexRoutePorts(log);
75192
+ const codexPorts = createCodexRoutePorts(log, paths.chatgptSignInFile);
74014
75193
  let ca;
74015
75194
  const loadCa = () => {
74016
75195
  ca ??= ensureCa(realConnectCertStore(paths), () => generateCa(/* @__PURE__ */ new Date()));
@@ -74027,14 +75206,15 @@ function realFrontDoorSupervisorPorts(paths) {
74027
75206
  };
74028
75207
  const rcSelfHostSurface = rcSelfHost ? createRcSelfHostSurface({
74029
75208
  now: () => Date.now(),
74030
- newUuid: import_node_crypto12.randomUUID,
74031
- randomToken: () => (0, import_node_crypto12.randomBytes)(MINTED_TOKEN_RANDOM_BYTES).toString("base64url"),
75209
+ newUuid: import_node_crypto14.randomUUID,
75210
+ randomToken: () => (0, import_node_crypto14.randomBytes)(MINTED_TOKEN_RANDOM_BYTES).toString("base64url"),
74032
75211
  credentialRecord: () => readRcSelfHostRecord(realFarmFs, paths.frontdoorDir),
74033
75212
  log
74034
75213
  }) : void 0;
74035
75214
  const rcCredentialStore = createRcCredentialStore(realFarmFs, paths.frontdoorRcCredentialsDir);
74036
75215
  const rcTracker = createRcSessionTracker({ now: () => Date.now(), idleMs: RC_IDLE_EXPIRY_MS, credentialStore: rcCredentialStore });
74037
- const rcFanout = createRcEventFanout();
75216
+ const doorEvents = createDoorEventHub();
75217
+ const rcFanout = rcFanoutOnDoorHub(createRcEventFanout(), doorEvents);
74038
75218
  const rcHub = createRcStreamHub({
74039
75219
  now: () => Date.now(),
74040
75220
  credentialOf: rcTracker.credentialOf,
@@ -74045,7 +75225,7 @@ function realFrontDoorSupervisorPorts(paths) {
74045
75225
  saveSequenceNum: rcCredentialStore.writeCursor,
74046
75226
  dial: rcSelfHost ? lateRcStreamDial(rcSelfHostDialTarget) : realRcStreamDial(),
74047
75227
  fanout: rcFanout,
74048
- newClientId: import_node_crypto12.randomUUID,
75228
+ newClientId: import_node_crypto14.randomUUID,
74049
75229
  backoffMs: RC_STREAM_BACKOFF_MS,
74050
75230
  sleep: async (ms) => {
74051
75231
  await new Promise((resolve) => {
@@ -74056,7 +75236,7 @@ function realFrontDoorSupervisorPorts(paths) {
74056
75236
  appendLog(paths, `frontdoor ${String(process.pid)}: ${line}`);
74057
75237
  }
74058
75238
  });
74059
- const rcControlToken = (0, import_node_crypto12.randomUUID)();
75239
+ const rcControlToken = (0, import_node_crypto14.randomUUID)();
74060
75240
  const resolveRoute = rcObservingResolver(
74061
75241
  createProviderRouteResolver({ fs: realFsPort, providersDir: paths.providersDir, codexPorts, directPort: () => directPort, ...rcSelfHostSurface === void 0 ? {} : { rcSelfHostRoute: rcSelfHostSurface.route } }),
74062
75242
  rcTracker,
@@ -74087,8 +75267,8 @@ function realFrontDoorSupervisorPorts(paths) {
74087
75267
  now: () => Date.now(),
74088
75268
  headroomSocket: () => headroomSocketTarget(realFarmFs, realHeadroomSocketTrust, paths),
74089
75269
  // The per-generation capability the direct listener demands: held only in this process's memory, so a loopback process that discovers the direct port still cannot use it.
74090
- hopSecret: (0, import_node_crypto12.randomUUID)(),
74091
- custody: createCredentialCustody(() => (0, import_node_crypto12.randomUUID)()),
75270
+ hopSecret: (0, import_node_crypto14.randomUUID)(),
75271
+ custody: createCredentialCustody(() => (0, import_node_crypto14.randomUUID)()),
74092
75272
  log
74093
75273
  });
74094
75274
  const onListenerError = (name) => (error62) => {
@@ -74109,9 +75289,9 @@ function realFrontDoorSupervisorPorts(paths) {
74109
75289
  startProviderListener: async (preferredPort) => {
74110
75290
  const authority = loadCa();
74111
75291
  const rcDial = rcSelfHost ? lateRcEventDial(rcSelfHostDialTarget) : realRcEventDial();
74112
- const rcInject = async (sessionId, text2) => await injectRcUserMessage({ credentialOf: rcTracker.credentialOf, noteSequenceNums: rcTracker.noteSequenceNums, dial: rcDial, newUuid: import_node_crypto12.randomUUID }, sessionId, text2);
75292
+ const rcInject = async (sessionId, text2) => await injectRcUserMessage({ credentialOf: rcTracker.credentialOf, noteSequenceNums: rcTracker.noteSequenceNums, dial: rcDial, newUuid: import_node_crypto14.randomUUID }, sessionId, text2);
74113
75293
  const rcAnswer = async (sessionId, requestId, decision) => await answerRcControlRequest({ credentialOf: rcTracker.credentialOf, pendingOf: (id) => rcTracker.pendingOf(id), completePending: rcTracker.completePending, noteSequenceNums: rcTracker.noteSequenceNums, dial: rcDial }, sessionId, requestId, decision);
74114
- const rcControlRequestDeps = { credentialOf: rcTracker.credentialOf, noteSequenceNums: rcTracker.noteSequenceNums, dial: rcDial, newUuid: import_node_crypto12.randomUUID };
75294
+ const rcControlRequestDeps = { credentialOf: rcTracker.credentialOf, noteSequenceNums: rcTracker.noteSequenceNums, dial: rcDial, newUuid: import_node_crypto14.randomUUID };
74115
75295
  const rcInterrupt = async (sessionId) => await interruptRcSession(rcControlRequestDeps, sessionId);
74116
75296
  const rcSetModel = async (sessionId, model) => await setRcSessionModel(rcControlRequestDeps, sessionId, model);
74117
75297
  const rcSetPermissionMode = async (sessionId, mode) => await setRcSessionPermissionMode(rcControlRequestDeps, sessionId, mode);
@@ -74133,19 +75313,24 @@ function realFrontDoorSupervisorPorts(paths) {
74133
75313
  log,
74134
75314
  mintLeaf(authority, LOOPBACK_LEAF_NAMES, /* @__PURE__ */ new Date()),
74135
75315
  createRcControlHandler({ expectedToken: rcControlToken, list: rcTracker.list, statusOf: rcTracker.statusOf, pendingOf: rcTracker.pendingOf, inject: rcInject, answer: rcAnswer, interrupt: rcInterrupt, setModel: rcSetModel, setPermissionMode: rcSetPermissionMode }),
74136
- createDoorApiNodeHandler({
74137
- expectedToken: rcControlToken,
74138
- list: rcTracker.list,
74139
- statusOf: rcTracker.statusOf,
74140
- pendingOf: rcTracker.pendingOf,
74141
- inject: rcInject,
74142
- answer: rcAnswer,
74143
- interrupt: rcInterrupt,
74144
- setModel: rcSetModel,
74145
- setPermissionMode: rcSetPermissionMode,
74146
- fanout: rcFanout,
74147
- ...controlDeps
74148
- })
75316
+ [
75317
+ createDoorApiNodeHandler({
75318
+ expectedToken: rcControlToken,
75319
+ list: rcTracker.list,
75320
+ statusOf: rcTracker.statusOf,
75321
+ pendingOf: rcTracker.pendingOf,
75322
+ inject: rcInject,
75323
+ answer: rcAnswer,
75324
+ interrupt: rcInterrupt,
75325
+ setModel: rcSetModel,
75326
+ setPermissionMode: rcSetPermissionMode,
75327
+ fanout: rcFanout,
75328
+ ...controlDeps,
75329
+ events: doorEvents
75330
+ }),
75331
+ // The self-hosted web client rides the same listener under the same per-generation token as the typed API it speaks: no extra process, no new trust surface.
75332
+ createRcClientPage(rcControlToken)
75333
+ ]
74149
75334
  );
74150
75335
  const handle = await listenFrontDoor(server, { ...preferredPort === void 0 ? {} : { preferredPort }, ca: authority.certPem, onError: onListenerError("provider") });
74151
75336
  return {
@@ -74158,7 +75343,7 @@ function realFrontDoorSupervisorPorts(paths) {
74158
75343
  },
74159
75344
  startConnectListener: async (preferredPort) => {
74160
75345
  const wantedTransparentPort = process.env.AGENT_SHIM_TRANSPARENT_SURFACE === void 0 ? void 0 : Number(process.env.AGENT_SHIM_TRANSPARENT_SURFACE);
74161
- const transparentCapability = wantedTransparentPort === void 0 || Number.isNaN(wantedTransparentPort) ? void 0 : (0, import_node_crypto12.randomUUID)();
75346
+ const transparentCapability = wantedTransparentPort === void 0 || Number.isNaN(wantedTransparentPort) ? void 0 : (0, import_node_crypto14.randomUUID)();
74162
75347
  if (transparentCapability !== void 0) {
74163
75348
  writeFrontDoorSession(realFarmFs, paths.frontdoorSessionsDir, { pid: process.pid, startedAt: Date.now(), token: transparentCapability });
74164
75349
  }
@@ -74231,6 +75416,8 @@ function realFrontDoorSupervisorPorts(paths) {
74231
75416
  return { port: handle.port, close: handle.close };
74232
75417
  },
74233
75418
  closeRcStreamHub: rcHub.close,
75419
+ // The launch lifecycle publisher over the same backbone the typed API serves: the tick's registry facts go in here, and the door's own register, prune and end moments come out as source-tagged events.
75420
+ observeLaunchRegistry: createLaunchEventPublisher(doorEvents, () => Date.now()).observe,
74234
75421
  log: (line) => {
74235
75422
  appendLog(paths, line);
74236
75423
  }
@@ -74501,8 +75688,8 @@ function registerFrontDoorCommand(program2, deps) {
74501
75688
  identitiesDir: paths.identitiesDir,
74502
75689
  frontdoorDir: paths.frontdoorDir,
74503
75690
  identity,
74504
- newUuid: import_node_crypto12.randomUUID,
74505
- randomToken: () => (0, import_node_crypto12.randomBytes)(MINTED_TOKEN_RANDOM_BYTES).toString("base64url"),
75691
+ newUuid: import_node_crypto14.randomUUID,
75692
+ randomToken: () => (0, import_node_crypto14.randomBytes)(MINTED_TOKEN_RANDOM_BYTES).toString("base64url"),
74506
75693
  force: options.force === true,
74507
75694
  now: () => Date.now()
74508
75695
  });
@@ -74816,7 +76003,7 @@ function registerCompletionCommand(program2) {
74816
76003
  }
74817
76004
 
74818
76005
  // src/credentialCommand.ts
74819
- var import_node_child_process4 = require("node:child_process");
76006
+ var import_node_child_process5 = require("node:child_process");
74820
76007
 
74821
76008
  // src/identityToken.ts
74822
76009
  var import_node_fs14 = __toESM(require("node:fs"), 1);
@@ -74868,7 +76055,7 @@ function realCommandPorts(deps) {
74868
76055
  cache: createRealCredentialCache(deps.paths.root),
74869
76056
  platform: process.platform,
74870
76057
  now: () => Date.now(),
74871
- ssh: (host, remoteCommand, input2) => (0, import_node_child_process4.spawnSync)("ssh", ["--", host, remoteCommand], { input: input2, stdio: ["pipe", "inherit", "inherit"] }).status
76058
+ ssh: (host, remoteCommand, input2) => (0, import_node_child_process5.spawnSync)("ssh", ["--", host, remoteCommand], { input: input2, stdio: ["pipe", "inherit", "inherit"] }).status
74872
76059
  };
74873
76060
  }
74874
76061
  var REMOTE_ROOT_SCRIPT = 'root="${AGENT_SHIM_HOME:-${CLAUDE_USE_HOME:-}}"; if [ -z "$root" ]; then if [ ! -d "$HOME/.agent-shim" ] && [ -d "$HOME/.claude-use" ]; then root="$HOME/.claude-use"; else root="$HOME/.agent-shim"; fi; fi';
@@ -74999,15 +76186,15 @@ function registerDoctorCommand(program2, deps) {
74999
76186
 
75000
76187
  // src/headroom/commands.ts
75001
76188
  var import_node_fs16 = __toESM(require("node:fs"), 1);
75002
- var import_node_http2 = __toESM(require("node:http"), 1);
76189
+ var import_node_http3 = __toESM(require("node:http"), 1);
75003
76190
  var import_node_path39 = __toESM(require("node:path"), 1);
75004
- var import_node_child_process5 = require("node:child_process");
76191
+ var import_node_child_process6 = require("node:child_process");
75005
76192
 
75006
76193
  // src/frontdoor/realFrontDoorPort.ts
75007
76194
  var import_node_fs15 = __toESM(require("node:fs"), 1);
75008
76195
 
75009
76196
  // src/frontdoor/ensure.ts
75010
- var import_node_crypto13 = require("node:crypto");
76197
+ var import_node_crypto15 = require("node:crypto");
75011
76198
  var FRONTDOOR_START_TIMEOUT_MS = 3e4;
75012
76199
  var FRONTDOOR_LAUNCHER_POLL_MS = 100;
75013
76200
  var FrontDoorStartError = class extends CliError {
@@ -75041,7 +76228,7 @@ function ensureFrontDoor(params) {
75041
76228
  continue;
75042
76229
  }
75043
76230
  if (verdict.ok) {
75044
- const token = (0, import_node_crypto13.randomUUID)();
76231
+ const token = (0, import_node_crypto15.randomUUID)();
75045
76232
  writeFrontDoorSession(ports.fs, paths.frontdoorSessionsDir, { pid: params.launcherPid, startedAt: ports.now(), token });
75046
76233
  return { port: state.port, connectPort: state.connectPort, token };
75047
76234
  }
@@ -75145,7 +76332,7 @@ function probeFrontDoorSync(port, caPem) {
75145
76332
  }
75146
76333
 
75147
76334
  // src/frontdoor/trust.ts
75148
- var import_node_crypto14 = require("node:crypto");
76335
+ var import_node_crypto16 = require("node:crypto");
75149
76336
  var import_node_path38 = __toESM(require("node:path"), 1);
75150
76337
  var BUNDLE_NAME_HEX_DIGITS = 16;
75151
76338
  function resolveTrustBundle(params) {
@@ -75168,7 +76355,7 @@ function resolveTrustBundle(params) {
75168
76355
  }
75169
76356
  const combined = `${inheritedPem.trimEnd()}
75170
76357
  ${caPem}`;
75171
- const bundle = import_node_path38.default.join(bundlesDir, `${(0, import_node_crypto14.createHash)("sha256").update(combined).digest("hex").slice(0, BUNDLE_NAME_HEX_DIGITS)}.pem`);
76358
+ const bundle = import_node_path38.default.join(bundlesDir, `${(0, import_node_crypto16.createHash)("sha256").update(combined).digest("hex").slice(0, BUNDLE_NAME_HEX_DIGITS)}.pem`);
75172
76359
  if (!fs19.exists(bundle)) {
75173
76360
  fs19.mkdirp(bundlesDir);
75174
76361
  fs19.writeFileAtomic(bundle, combined);
@@ -75308,7 +76495,7 @@ function headroomProxyArgs(socketPath, settings2) {
75308
76495
  }
75309
76496
  async function readyOverSocket(socketPath) {
75310
76497
  return await new Promise((resolve) => {
75311
- const probe = import_node_http2.default.get({ socketPath, path: "/readyz", timeout: READY_FETCH_TIMEOUT_MS }, (response) => {
76498
+ const probe = import_node_http3.default.get({ socketPath, path: "/readyz", timeout: READY_FETCH_TIMEOUT_MS }, (response) => {
75312
76499
  response.resume();
75313
76500
  resolve(response.statusCode === HTTP_STATUS.ok);
75314
76501
  });
@@ -75322,7 +76509,7 @@ async function readyOverSocket(socketPath) {
75322
76509
  }
75323
76510
  var HEADROOM_TOOL_NAME = "headroom-ai";
75324
76511
  function readInstalledHeadroomCommit() {
75325
- const toolDir = (0, import_node_child_process5.spawnSync)("uv", ["tool", "dir"], { encoding: "utf8" });
76512
+ const toolDir = (0, import_node_child_process6.spawnSync)("uv", ["tool", "dir"], { encoding: "utf8" });
75326
76513
  if (toolDir.status !== 0) {
75327
76514
  return void 0;
75328
76515
  }
@@ -75362,7 +76549,7 @@ function realSupervisorPorts(paths) {
75362
76549
  }
75363
76550
  const logFd = import_node_fs16.default.openSync(paths.headroomLogPath, "a");
75364
76551
  try {
75365
- const child = (0, import_node_child_process5.spawn)("headroom", headroomProxyArgs(socketPath, settings2), {
76552
+ const child = (0, import_node_child_process6.spawn)("headroom", headroomProxyArgs(socketPath, settings2), {
75366
76553
  detached: true,
75367
76554
  stdio: ["ignore", logFd, logFd],
75368
76555
  env: headroomSpawnEnv(process.env, allowlist, settings2, trust?.path)
@@ -75402,7 +76589,7 @@ function realSupervisorPorts(paths) {
75402
76589
  },
75403
76590
  ready: readyOverSocket,
75404
76591
  install: (spec) => {
75405
- const result = (0, import_node_child_process5.spawnSync)("uv", ["tool", "install", spec], { encoding: "utf8" });
76592
+ const result = (0, import_node_child_process6.spawnSync)("uv", ["tool", "install", spec], { encoding: "utf8" });
75406
76593
  if (result.error !== void 0) {
75407
76594
  return { ok: false, error: `uv is not installed or not on PATH (${result.error.message})` };
75408
76595
  }
@@ -75415,7 +76602,7 @@ function realSupervisorPorts(paths) {
75415
76602
  return result.status === 0 ? { ok: true } : { ok: false, error: `uv tool install exited ${String(result.status)}` };
75416
76603
  },
75417
76604
  headroomVersion: () => {
75418
- const result = (0, import_node_child_process5.spawnSync)("headroom", ["--version"], { encoding: "utf8" });
76605
+ const result = (0, import_node_child_process6.spawnSync)("headroom", ["--version"], { encoding: "utf8" });
75419
76606
  return result.status === 0 ? result.stdout.trim() : void 0;
75420
76607
  },
75421
76608
  installedCommit: readInstalledHeadroomCommit,
@@ -75598,13 +76785,14 @@ function toProviderView(name, provider) {
75598
76785
  };
75599
76786
  }
75600
76787
  function codexConfigFromOptions(options) {
75601
- if (options.codexDefaultModel === void 0 && options.codexModel === void 0 && options.codexEffort === void 0) {
76788
+ if (options.codexDefaultModel === void 0 && options.codexModel === void 0 && options.codexEffort === void 0 && options.codexLogin === void 0) {
75602
76789
  return void 0;
75603
76790
  }
75604
76791
  const candidate = {
75605
76792
  ...options.codexDefaultModel === void 0 ? {} : { defaultModel: options.codexDefaultModel },
75606
76793
  ...options.codexModel === void 0 ? {} : { models: options.codexModel },
75607
- ...options.codexEffort === void 0 ? {} : { effort: options.codexEffort }
76794
+ ...options.codexEffort === void 0 ? {} : { effort: options.codexEffort },
76795
+ ...options.codexLogin === void 0 ? {} : { login: options.codexLogin }
75608
76796
  };
75609
76797
  const parsed = CodexProviderConfigSchema.safeParse(candidate);
75610
76798
  if (!parsed.success) {
@@ -75627,7 +76815,7 @@ function registerProviderCommand(program2, deps) {
75627
76815
  new Option("--credential-target <target>", "Where the token goes: bearer sets ANTHROPIC_AUTH_TOKEN (default), apiKey sets ANTHROPIC_API_KEY.").choices(
75628
76816
  PROVIDER_CREDENTIAL_TARGETS
75629
76817
  )
75630
- ).option("--env <KEY=VALUE>", "Extra environment entry for the child (repeatable).", collectStringPair).option("--codex-default-model <model>", "Codex providers: the codex model a request maps to when no tier matches.").option("--codex-model <tier=model>", "Codex providers: the codex model one tier (fable, opus, sonnet or haiku) maps to (repeatable).", collectStringPair).addOption(new Option("--codex-effort <effort>", "Codex providers: the reasoning effort when a request asks for none the backend accepts.").choices(CODEX_EFFORTS)).option("--json", "Print the result as JSON.").action((name, options) => {
76818
+ ).option("--env <KEY=VALUE>", "Extra environment entry for the child (repeatable).", collectStringPair).option("--codex-default-model <model>", "Codex providers: the codex model a request maps to when no tier matches.").option("--codex-model <tier=model>", "Codex providers: the codex model one tier (fable, opus, sonnet or haiku) maps to (repeatable).", collectStringPair).addOption(new Option("--codex-effort <effort>", "Codex providers: the reasoning effort when a request asks for none the backend accepts.").choices(CODEX_EFFORTS)).addOption(new Option("--codex-login <login>", "Codex providers: which login the upstream call uses, the Codex CLI's own login or a Sign in with ChatGPT grant from `agent-shim codex login`.").choices(CODEX_LOGINS)).option("--json", "Print the result as JSON.").action((name, options) => {
75631
76819
  const codex = codexConfigFromOptions(options);
75632
76820
  const created = addProvider(paths, name, {
75633
76821
  ...options.kind === void 0 ? {} : { kind: options.kind },
@@ -75659,14 +76847,14 @@ function registerProviderCommand(program2, deps) {
75659
76847
  new Option("--credential-target <target>", "Where the token goes: bearer sets ANTHROPIC_AUTH_TOKEN, apiKey sets ANTHROPIC_API_KEY.").choices(
75660
76848
  PROVIDER_CREDENTIAL_TARGETS
75661
76849
  )
75662
- ).option("--env <KEY=VALUE>", "Add or replace an environment entry for the child (repeatable).", collectStringPair).option("--unset-env <KEY>", "Remove an environment entry (repeatable).", collectRepeated).option("--codex-default-model <model>", "Codex providers: replace the model a request maps to when no tier matches.").option("--codex-model <tier=model>", "Codex providers: set the codex model one tier maps to (repeatable; other tiers are kept).", collectStringPair).addOption(new Option("--codex-effort <effort>", "Codex providers: replace the default reasoning effort.").choices(CODEX_EFFORTS));
76850
+ ).option("--env <KEY=VALUE>", "Add or replace an environment entry for the child (repeatable).", collectStringPair).option("--unset-env <KEY>", "Remove an environment entry (repeatable).", collectRepeated).option("--codex-default-model <model>", "Codex providers: replace the model a request maps to when no tier matches.").option("--codex-model <tier=model>", "Codex providers: set the codex model one tier maps to (repeatable; other tiers are kept).", collectStringPair).addOption(new Option("--codex-effort <effort>", "Codex providers: replace the default reasoning effort.").choices(CODEX_EFFORTS)).addOption(new Option("--codex-login <login>", "Codex providers: replace which login the upstream call uses.").choices(CODEX_LOGINS));
75663
76851
  addCredentialCacheOptions(providerSet);
75664
76852
  withExamples(
75665
76853
  providerSet.option("--json", "Print the result as JSON.").action((name, allOptions) => {
75666
76854
  const { json: json2, ...options } = allOptions;
75667
76855
  if (Object.entries(allOptions).every(([key, value2]) => key === "json" || value2 === void 0)) {
75668
76856
  throw new UsageError(
75669
- "Nothing to change: pass --display-name, --base-url, --credential, --credential-target, a --credential-cache option, --env, --unset-env, --codex-default-model, --codex-model or --codex-effort."
76857
+ "Nothing to change: pass --display-name, --base-url, --credential, --credential-target, a --credential-cache option, --env, --unset-env, --codex-default-model, --codex-model, --codex-effort or --codex-login."
75670
76858
  );
75671
76859
  }
75672
76860
  const codex = codexConfigFromOptions(options);
@@ -75727,6 +76915,7 @@ function registerProviderCommand(program2, deps) {
75727
76915
  const config2 = resolveCodexConfig(found.codex);
75728
76916
  console.log(`Codex models: ${CODEX_TIERS.map((tier) => `${tier}=${config2.models[tier]}`).join(", ")}, otherwise ${config2.defaultModel}`);
75729
76917
  console.log(`Codex effort: ${config2.effort}`);
76918
+ console.log(`Codex login: ${config2.login}`);
75730
76919
  } else {
75731
76920
  console.log(`Base URL: ${found.baseUrl}`);
75732
76921
  }
@@ -76134,10 +77323,10 @@ function buildProgram(deps) {
76134
77323
 
76135
77324
  // src/launchWiring.ts
76136
77325
  var import_node_os8 = __toESM(require("node:os"), 1);
76137
- var import_node_crypto16 = require("node:crypto");
77326
+ var import_node_crypto18 = require("node:crypto");
76138
77327
 
76139
77328
  // src/launcher.ts
76140
- var import_node_crypto15 = require("node:crypto");
77329
+ var import_node_crypto17 = require("node:crypto");
76141
77330
  var import_node_path41 = __toESM(require("node:path"), 1);
76142
77331
 
76143
77332
  // src/launcher/cliOverride.ts
@@ -76638,7 +77827,7 @@ function prepareLaunch(params) {
76638
77827
  ...identityCredential === void 0 ? {} : { identityCredential },
76639
77828
  ...frontDoor === void 0 ? {} : { frontdoor: frontDoor },
76640
77829
  ...headroom === void 0 ? {} : { headroom },
76641
- sessionId: (0, import_node_crypto15.randomUUID)()
77830
+ sessionId: (0, import_node_crypto17.randomUUID)()
76642
77831
  });
76643
77832
  const releases = [...headroom === void 0 || headroomPort === void 0 ? [] : [headroomPort.release], ...frontDoorRelease === void 0 ? [] : [frontDoorRelease]];
76644
77833
  const release = () => {
@@ -76683,7 +77872,7 @@ function buildFarmRuntime(paths, cwd) {
76683
77872
  ...cliOverride === void 0 ? {} : { cliOverride }
76684
77873
  }).input,
76685
77874
  now: () => Date.now(),
76686
- uniqueSuffix: `${String(process.pid)}.${(0, import_node_crypto16.randomUUID)()}`,
77875
+ uniqueSuffix: `${String(process.pid)}.${(0, import_node_crypto18.randomUUID)()}`,
76687
77876
  // Zombie-aware on purpose: a previous launcher that crashed out of a resync without releasing the lock may sit unreaped, still answering signal 0 as alive, and must read as a dead holder so this launch takes the lock over instead of timing out.
76688
77877
  lock: { pid: process.pid, isRunning: realIsProcessRunning, sleep: realSleepSync }
76689
77878
  },