claude-use 9.7.2 → 9.8.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/index.cjs CHANGED
@@ -2663,7 +2663,7 @@ var init_package = __esm({
2663
2663
  "package.json"() {
2664
2664
  package_default = {
2665
2665
  name: "claude-use",
2666
- version: "9.7.2",
2666
+ version: "9.8.0",
2667
2667
  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.",
2668
2668
  license: "MIT",
2669
2669
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -6567,7 +6567,7 @@ function buildEnv(params) {
6567
6567
  const parentCustomHeaders = params.baseEnv.ANTHROPIC_CUSTOM_HEADERS;
6568
6568
  let providerCustomHeaders;
6569
6569
  if (!params.configDirEscapeHatch && params.resolvedIdentityName !== void 0) {
6570
- env.CLAUDE_CONFIG_DIR = import_node_path32.default.join(params.identitiesDir, params.resolvedIdentityName);
6570
+ env.CLAUDE_CONFIG_DIR = import_node_path33.default.join(params.identitiesDir, params.resolvedIdentityName);
6571
6571
  }
6572
6572
  if (params.provider !== void 0) {
6573
6573
  const providerEnv = params.provider.definition.env ?? {};
@@ -6611,11 +6611,11 @@ function buildEnv(params) {
6611
6611
  }
6612
6612
  return env;
6613
6613
  }
6614
- var import_node_path32, LOOPBACK_HOSTS;
6614
+ var import_node_path33, LOOPBACK_HOSTS;
6615
6615
  var init_flags = __esm({
6616
6616
  "src/launcher/flags.ts"() {
6617
6617
  "use strict";
6618
- import_node_path32 = __toESM(require("node:path"), 1);
6618
+ import_node_path33 = __toESM(require("node:path"), 1);
6619
6619
  init_envBool();
6620
6620
  init_credential();
6621
6621
  init_headers();
@@ -6938,7 +6938,7 @@ function decideLaunch(params, readOnly) {
6938
6938
  identityDefaultConfigProfile: loadedIdentity?.config.defaultConfigProfile,
6939
6939
  globalDefaultConfigProfile: params.globalDefaultConfigProfile
6940
6940
  });
6941
- if (configProfileDecision.name !== void 0 && params.allowMissingConfigProfile !== true && fs12.readConfigFile(import_node_path33.default.join(paths.configProfilesDir, `${configProfileDecision.name}.json`)) === void 0) {
6941
+ if (configProfileDecision.name !== void 0 && params.allowMissingConfigProfile !== true && fs12.readConfigFile(import_node_path34.default.join(paths.configProfilesDir, `${configProfileDecision.name}.json`)) === void 0) {
6942
6942
  log.error(
6943
6943
  `agent-shim: no configuration profile named "${configProfileDecision.name}" (selected via ${configProfileDecision.source}). Run \`agent-shim profile add ${configProfileDecision.name}\` first.`
6944
6944
  );
@@ -7050,7 +7050,7 @@ function decideLaunch(params, readOnly) {
7050
7050
  extraFlags: splitExtraFlags(env.CLAUDE_EXTRA_FLAGS),
7051
7051
  passthrough: parsedArgv.rest
7052
7052
  });
7053
- const configDir = configDirEscapeHatchApplies || identityDecision.name === void 0 ? env.CLAUDE_CONFIG_DIR : import_node_path33.default.join(paths.identitiesDir, identityDecision.name);
7053
+ const configDir = configDirEscapeHatchApplies || identityDecision.name === void 0 ? env.CLAUDE_CONFIG_DIR : import_node_path34.default.join(paths.identitiesDir, identityDecision.name);
7054
7054
  const decision = {
7055
7055
  identitySource: identityDecision.source,
7056
7056
  configDirEscapeHatch: identityDecision.configDirEscapeHatch,
@@ -7091,7 +7091,7 @@ function prepareLaunch(params) {
7091
7091
  );
7092
7092
  const updateCheck = runLaunchUpdateCheck({
7093
7093
  mode: params.updateMode ?? "off",
7094
- checkPath: import_node_path33.default.join(paths.root, "update.check"),
7094
+ checkPath: import_node_path34.default.join(paths.root, "update.check"),
7095
7095
  currentVersion: package_default.version,
7096
7096
  port: params.update
7097
7097
  });
@@ -7222,12 +7222,12 @@ function runLaunchPlan(plan, spawn2) {
7222
7222
  function runLauncher(params) {
7223
7223
  params.proc.exit(runLaunchPlan(prepareLaunch(params), params.spawn));
7224
7224
  }
7225
- var import_node_crypto10, import_node_path33;
7225
+ var import_node_crypto10, import_node_path34;
7226
7226
  var init_launcher = __esm({
7227
7227
  "src/launcher.ts"() {
7228
7228
  "use strict";
7229
7229
  import_node_crypto10 = require("node:crypto");
7230
- import_node_path33 = __toESM(require("node:path"), 1);
7230
+ import_node_path34 = __toESM(require("node:path"), 1);
7231
7231
  init_package();
7232
7232
  init_envBool();
7233
7233
  init_argv();
@@ -7258,13 +7258,13 @@ function agentShimCliPath(distDir = typeof __AGENT_SHIM_DIST_DIR__ === "string"
7258
7258
  if (distDir === void 0) {
7259
7259
  throw new OwnCliPathUnknownError();
7260
7260
  }
7261
- return import_node_path34.default.join(distDir, OWN_CLI_FILE_NAME);
7261
+ return import_node_path35.default.join(distDir, OWN_CLI_FILE_NAME);
7262
7262
  }
7263
- var import_node_path34, OWN_CLI_FILE_NAME, OwnCliPathUnknownError;
7263
+ var import_node_path35, OWN_CLI_FILE_NAME, OwnCliPathUnknownError;
7264
7264
  var init_ownCli = __esm({
7265
7265
  "src/ownCli.ts"() {
7266
7266
  "use strict";
7267
- import_node_path34 = __toESM(require("node:path"), 1);
7267
+ import_node_path35 = __toESM(require("node:path"), 1);
7268
7268
  init_cliError();
7269
7269
  OWN_CLI_FILE_NAME = "cli.cjs";
7270
7270
  OwnCliPathUnknownError = class extends CliError {
@@ -7349,7 +7349,7 @@ request.on("error", (error) => finish(failed, (error.code ? error.code + ": " :
7349
7349
  // src/frontdoor/trust.ts
7350
7350
  function resolveTrustBundle(params) {
7351
7351
  const { caCertFile, bundlesDir, variable, inherited, fs: fs12 } = params;
7352
- if (inherited === void 0 || inherited === "" || import_node_path35.default.resolve(inherited) === import_node_path35.default.resolve(caCertFile)) {
7352
+ if (inherited === void 0 || inherited === "" || import_node_path36.default.resolve(inherited) === import_node_path36.default.resolve(caCertFile)) {
7353
7353
  return { path: caCertFile };
7354
7354
  }
7355
7355
  const caPem = fs12.readFileUtf8(caCertFile);
@@ -7367,19 +7367,19 @@ function resolveTrustBundle(params) {
7367
7367
  }
7368
7368
  const combined = `${inheritedPem.trimEnd()}
7369
7369
  ${caPem}`;
7370
- const bundle = import_node_path35.default.join(bundlesDir, `${(0, import_node_crypto11.createHash)("sha256").update(combined).digest("hex").slice(0, BUNDLE_NAME_HEX_DIGITS)}.pem`);
7370
+ const bundle = import_node_path36.default.join(bundlesDir, `${(0, import_node_crypto11.createHash)("sha256").update(combined).digest("hex").slice(0, BUNDLE_NAME_HEX_DIGITS)}.pem`);
7371
7371
  if (!fs12.exists(bundle)) {
7372
7372
  fs12.mkdirp(bundlesDir);
7373
7373
  fs12.writeFileAtomic(bundle, combined);
7374
7374
  }
7375
7375
  return { path: bundle };
7376
7376
  }
7377
- var import_node_crypto11, import_node_path35, BUNDLE_NAME_HEX_DIGITS;
7377
+ var import_node_crypto11, import_node_path36, BUNDLE_NAME_HEX_DIGITS;
7378
7378
  var init_trust = __esm({
7379
7379
  "src/frontdoor/trust.ts"() {
7380
7380
  "use strict";
7381
7381
  import_node_crypto11 = require("node:crypto");
7382
- import_node_path35 = __toESM(require("node:path"), 1);
7382
+ import_node_path36 = __toESM(require("node:path"), 1);
7383
7383
  BUNDLE_NAME_HEX_DIGITS = 16;
7384
7384
  }
7385
7385
  });
@@ -8723,8 +8723,8 @@ function parseConnectTarget(authority) {
8723
8723
  function isInterceptedHost(host, interceptHosts) {
8724
8724
  return interceptHosts.includes(host);
8725
8725
  }
8726
- function servedByPipeline(path36) {
8727
- return path36?.startsWith(ROUTED_PATH_PREFIX) === true;
8726
+ function servedByPipeline(path37) {
8727
+ return path37?.startsWith(ROUTED_PATH_PREFIX) === true;
8728
8728
  }
8729
8729
  var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
8730
8730
  "connection",
@@ -9259,11 +9259,11 @@ function createRcSessionTracker(deps) {
9259
9259
  };
9260
9260
  const observeExchange = (request4) => {
9261
9261
  const method = (request4.method ?? "").toUpperCase();
9262
- const path36 = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
9263
- if (!path36.startsWith(RC_SESSIONS_PATH_PREFIX)) {
9262
+ const path37 = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
9263
+ if (!path37.startsWith(RC_SESSIONS_PATH_PREFIX)) {
9264
9264
  return void 0;
9265
9265
  }
9266
- const tail = path36.slice(RC_SESSIONS_PATH_PREFIX.length);
9266
+ const tail = path37.slice(RC_SESSIONS_PATH_PREFIX.length);
9267
9267
  const facts = {
9268
9268
  oauthAuthorization: oauthAuthorizationOf(request4.headers),
9269
9269
  anthropicVersion: singleHeader(request4.headers, "anthropic-version"),
@@ -9645,8 +9645,8 @@ var RC_READ_FILE_ENCODINGS = ["utf-8", "base64"];
9645
9645
  function isRcReadFileEncoding(value) {
9646
9646
  return RC_READ_FILE_ENCODINGS.some((encoding) => encoding === value);
9647
9647
  }
9648
- function buildRcReadFilePayload(requestId, path36, options) {
9649
- return buildRcControlRequestPayload(requestId, { subtype: "read_file", path: path36, ...options?.maxBytes === void 0 ? {} : { max_bytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } });
9648
+ function buildRcReadFilePayload(requestId, path37, options) {
9649
+ return buildRcControlRequestPayload(requestId, { subtype: "read_file", path: path37, ...options?.maxBytes === void 0 ? {} : { max_bytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } });
9650
9650
  }
9651
9651
  function buildRcFileSuggestionsPayload(requestId, query) {
9652
9652
  return buildRcControlRequestPayload(requestId, { subtype: "file_suggestions", query });
@@ -9748,15 +9748,15 @@ async function getRcSessionContextUsage(deps, sessionId, detail) {
9748
9748
  const result = await deliverRcClientPayload(deps, sessionId, buildRcGetContextUsagePayload(requestId, detail), `request the context usage of session ${sessionId}`);
9749
9749
  return result.ok ? { ...result, requestId } : result;
9750
9750
  }
9751
- async function readRcSessionFile(deps, sessionId, path36, options) {
9752
- if (path36 === "") {
9751
+ async function readRcSessionFile(deps, sessionId, path37, options) {
9752
+ if (path37 === "") {
9753
9753
  return { ok: false, message: "a read-file write needs a non-empty path: the SDK's own field names the file the worker resolves against the session's cwd, and an empty string names none" };
9754
9754
  }
9755
9755
  if (options?.maxBytes !== void 0 && (!Number.isInteger(options.maxBytes) || options.maxBytes <= 0)) {
9756
9756
  return { ok: false, message: "a read-file write's max-bytes is a positive whole number of bytes: the SDK's own field caps the read, and a zero or negative cap reads nothing" };
9757
9757
  }
9758
9758
  const requestId = deps.newUuid();
9759
- const result = await deliverRcClientPayload(deps, sessionId, buildRcReadFilePayload(requestId, path36, options), `read ${path36} from session ${sessionId}`);
9759
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcReadFilePayload(requestId, path37, options), `read ${path37} from session ${sessionId}`);
9760
9760
  return result.ok ? { ...result, requestId } : result;
9761
9761
  }
9762
9762
  async function suggestRcSessionFiles(deps, sessionId, query) {
@@ -9925,11 +9925,11 @@ function createRcControlHandler(deps) {
9925
9925
  path: "read-file",
9926
9926
  read: (body) => {
9927
9927
  const session = bodyString(body, "session");
9928
- const path36 = bodyString(body, "path");
9928
+ const path37 = bodyString(body, "path");
9929
9929
  const maxBytes = "maxBytes" in body ? bodyPositiveInteger(body, "maxBytes") : void 0;
9930
9930
  const encoding = "encoding" in body && isRcReadFileEncoding(body.encoding) ? body.encoding : void 0;
9931
- const refused2 = session === void 0 || path36 === void 0 || "maxBytes" in body && maxBytes === void 0 || "encoding" in body && encoding === void 0;
9932
- return refused2 ? { error: `the read-file body must be JSON naming a non-empty session id and path, maxBytes only as a positive whole number or absent, and an encoding the SDK's own type permits (${RC_READ_FILE_ENCODINGS.join(", ")}) or none` } : { session, run: async () => await deps.readFile(session, path36, { ...maxBytes === void 0 ? {} : { maxBytes }, ...encoding === void 0 ? {} : { encoding } }) };
9931
+ const refused2 = session === void 0 || path37 === void 0 || "maxBytes" in body && maxBytes === void 0 || "encoding" in body && encoding === void 0;
9932
+ return refused2 ? { error: `the read-file body must be JSON naming a non-empty session id and path, maxBytes only as a positive whole number or absent, and an encoding the SDK's own type permits (${RC_READ_FILE_ENCODINGS.join(", ")}) or none` } : { session, run: async () => await deps.readFile(session, path37, { ...maxBytes === void 0 ? {} : { maxBytes }, ...encoding === void 0 ? {} : { encoding } }) };
9933
9933
  }
9934
9934
  },
9935
9935
  {
@@ -10239,9 +10239,9 @@ function requestIdOfBody(body) {
10239
10239
  }
10240
10240
  function frontDoorRcControl(transport, token) {
10241
10241
  const headers = { authorization: `Bearer ${token}` };
10242
- const listAnswerOf = async (path36, what) => {
10242
+ const listAnswerOf = async (path37, what) => {
10243
10243
  try {
10244
- return await transport.request({ method: "GET", path: path36, headers });
10244
+ return await transport.request({ method: "GET", path: path37, headers });
10245
10245
  } catch (error) {
10246
10246
  throw new Error(`${what}: could not reach the front door's control listener: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
10247
10247
  }
@@ -10313,10 +10313,10 @@ function frontDoorRcControl(transport, token) {
10313
10313
  }
10314
10314
  return pending;
10315
10315
  };
10316
- const writeOf = async (path36, body, describeFailure) => {
10316
+ const writeOf = async (path37, body, describeFailure) => {
10317
10317
  let answer;
10318
10318
  try {
10319
- answer = await transport.request({ method: "POST", path: path36, headers: { ...headers, "content-type": "application/json" }, body });
10319
+ answer = await transport.request({ method: "POST", path: path37, headers: { ...headers, "content-type": "application/json" }, body });
10320
10320
  } catch (error) {
10321
10321
  return { ok: false, message: `could not reach the front door's control listener: ${error instanceof Error ? error.message : String(error)}` };
10322
10322
  }
@@ -10347,10 +10347,10 @@ function frontDoorRcControl(transport, token) {
10347
10347
  endSession: async (sessionId, reason) => await writeOf(`${CONTROL_PATH_PREFIX}/end-session`, JSON.stringify({ session: sessionId, ...reason === void 0 ? {} : { reason } }), (status, message) => `ending ${sessionId} failed (HTTP ${String(status)}): ${message}`),
10348
10348
  getUsage: async (sessionId, skipBehaviors) => await writeOf(`${CONTROL_PATH_PREFIX}/get-usage`, JSON.stringify({ session: sessionId, ...skipBehaviors === void 0 ? {} : { skipBehaviors } }), (status, message) => `requesting the usage of ${sessionId} failed (HTTP ${String(status)}): ${message}`),
10349
10349
  getContextUsage: async (sessionId, detail) => await writeOf(`${CONTROL_PATH_PREFIX}/get-context-usage`, JSON.stringify({ session: sessionId, ...detail === void 0 ? {} : { detail } }), (status, message) => `requesting the context usage of ${sessionId} failed (HTTP ${String(status)}): ${message}`),
10350
- readFile: async (sessionId, path36, options) => await writeOf(
10350
+ readFile: async (sessionId, path37, options) => await writeOf(
10351
10351
  `${CONTROL_PATH_PREFIX}/read-file`,
10352
- JSON.stringify({ session: sessionId, path: path36, ...options?.maxBytes === void 0 ? {} : { maxBytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } }),
10353
- (status, message) => `reading ${path36} from ${sessionId} failed (HTTP ${String(status)}): ${message}`
10352
+ JSON.stringify({ session: sessionId, path: path37, ...options?.maxBytes === void 0 ? {} : { maxBytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } }),
10353
+ (status, message) => `reading ${path37} from ${sessionId} failed (HTTP ${String(status)}): ${message}`
10354
10354
  ),
10355
10355
  fileSuggestions: async (sessionId, query) => await writeOf(`${CONTROL_PATH_PREFIX}/file-suggestions`, JSON.stringify({ session: sessionId, query }), (status, message) => `requesting file suggestions from ${sessionId} failed (HTTP ${String(status)}): ${message}`),
10356
10356
  keepAlive: async (sessionId) => await writeOf(`${CONTROL_PATH_PREFIX}/keep-alive`, JSON.stringify({ session: sessionId }), (status, message) => `keeping ${sessionId} alive failed (HTTP ${String(status)}): ${message}`),
@@ -10509,12 +10509,12 @@ function tlsFacts(options) {
10509
10509
  }
10510
10510
  function realRcEventDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
10511
10511
  const agent = new ExemptTlsAgent();
10512
- const post = async (path36, headers, body) => await new Promise((resolve, reject) => {
10512
+ const post = async (path37, headers, body) => await new Promise((resolve, reject) => {
10513
10513
  const options = {
10514
10514
  host: target.host,
10515
10515
  port: target.port,
10516
10516
  method: "POST",
10517
- path: path36,
10517
+ path: path37,
10518
10518
  // The Host header names the host whose API this is, not the address dialled: a redirected test dials a loopback stand-in while still speaking to the API host by name, exactly as the forwarded paths present their SNI.
10519
10519
  headers: { ...headers, host: target.servername ?? target.host, "content-length": String(Buffer.byteLength(body, "utf8")) },
10520
10520
  agent
@@ -10579,12 +10579,12 @@ function lateRcStreamDial(resolve) {
10579
10579
  }
10580
10580
  function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
10581
10581
  const agent = new ExemptTlsAgent();
10582
- const request4 = async (method, path36, headers, body, signal) => await new Promise((resolve, reject) => {
10582
+ const request4 = async (method, path37, headers, body, signal) => await new Promise((resolve, reject) => {
10583
10583
  const options = {
10584
10584
  host: target.host,
10585
10585
  port: target.port,
10586
10586
  method,
10587
- path: path36,
10587
+ path: path37,
10588
10588
  // The Host header names the host whose API this is, not the address dialled: a redirected test dials a loopback stand-in while still speaking to the API host by name, exactly as the forwarded paths present their SNI.
10589
10589
  headers: { ...headers, host: target.servername ?? target.host, ...body === void 0 ? {} : { "content-length": String(Buffer.byteLength(body, "utf8")) } },
10590
10590
  agent,
@@ -10626,9 +10626,9 @@ function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_P
10626
10626
  return { status, body: await readAll(response) };
10627
10627
  },
10628
10628
  openStream: async (sessionId, headers, resume, signal) => {
10629
- const path36 = resume === void 0 ? `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream` : `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream?from_sequence_num=${String(resume.fromSequenceNum)}`;
10629
+ const path37 = resume === void 0 ? `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream` : `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream?from_sequence_num=${String(resume.fromSequenceNum)}`;
10630
10630
  const streamHeaders = resume === void 0 ? headers : { ...headers, "last-event-id": String(resume.fromSequenceNum) };
10631
- const { status, response } = await request4("GET", path36, streamHeaders, void 0, signal);
10631
+ const { status, response } = await request4("GET", path37, streamHeaders, void 0, signal);
10632
10632
  if (!isSuccessful2(status)) {
10633
10633
  await readAll(response);
10634
10634
  return { status, chunks: NO_CHUNKS };
@@ -11543,19 +11543,19 @@ function writePools(paths, pools) {
11543
11543
  applyPatch(paths.globalConfigFile, GlobalConfigSchema, { pools }, { defaults: {} });
11544
11544
  }
11545
11545
  function poolCycleOf(pools, name) {
11546
- const walk = (current, path36) => {
11546
+ const walk = (current, path37) => {
11547
11547
  for (const entry of pools[current]?.identities ?? []) {
11548
11548
  const nested = poolNameOf(poolMemberIdentity(entry));
11549
11549
  if (nested === void 0) {
11550
11550
  continue;
11551
11551
  }
11552
11552
  if (nested === name) {
11553
- return [...path36, nested];
11553
+ return [...path37, nested];
11554
11554
  }
11555
- if (path36.includes(nested)) {
11555
+ if (path37.includes(nested)) {
11556
11556
  continue;
11557
11557
  }
11558
- const found = walk(nested, [...path36, nested]);
11558
+ const found = walk(nested, [...path37, nested]);
11559
11559
  if (found !== void 0) {
11560
11560
  return found;
11561
11561
  }
@@ -12228,7 +12228,7 @@ init_middleware();
12228
12228
  init_preflight();
12229
12229
 
12230
12230
  // src/frontdoor/controlSchemas.ts
12231
- var import_node_path29 = __toESM(require("node:path"), 1);
12231
+ var import_node_path30 = __toESM(require("node:path"), 1);
12232
12232
  var import_zod19 = require("zod");
12233
12233
 
12234
12234
  // src/doctorReport.ts
@@ -12399,7 +12399,29 @@ function removeIdentity(paths, name) {
12399
12399
  // src/doctorReport.ts
12400
12400
  init_realPorts();
12401
12401
  var import_node_fs9 = __toESM(require("node:fs"), 1);
12402
+ var import_node_path29 = __toESM(require("node:path"), 1);
12403
+
12404
+ // src/usage/signIn.ts
12402
12405
  var import_node_path28 = __toESM(require("node:path"), 1);
12406
+ var CREDENTIALS_FILE = ".credentials.json";
12407
+ function keychainHasItem(run, account) {
12408
+ return run.run("security", ["find-generic-password", "-a", account]).status === 0;
12409
+ }
12410
+ function decideSignIn(facts) {
12411
+ if (facts.hasCredentialBlock) {
12412
+ return { state: "token", detail: "supplies its own credential through a credential block, so a stored login is not what a launch uses" };
12413
+ }
12414
+ if (facts.keychainItem === true) {
12415
+ return { state: "signed-in", detail: "a login is stored in the macOS Keychain" };
12416
+ }
12417
+ if (facts.credentialsFile) {
12418
+ return { state: "signed-in", detail: `a login is stored in ${CREDENTIALS_FILE}` };
12419
+ }
12420
+ return { state: "signed-out", detail: facts.platform === "darwin" ? `no Keychain item and no ${CREDENTIALS_FILE}` : `no ${CREDENTIALS_FILE}` };
12421
+ }
12422
+ function credentialsFilePath(farmRoot) {
12423
+ return import_node_path28.default.join(farmRoot, CREDENTIALS_FILE);
12424
+ }
12403
12425
 
12404
12426
  // src/codex/siwcStore.ts
12405
12427
  var import_zod18 = require("zod");
@@ -12500,6 +12522,7 @@ var DOCTOR_SECTIONS = [
12500
12522
  "legacy-name",
12501
12523
  "config-profile",
12502
12524
  "identity",
12525
+ "sign-in",
12503
12526
  "pool",
12504
12527
  "provider",
12505
12528
  "keychain",
@@ -12542,7 +12565,7 @@ function tcpPortEraRecord(raw) {
12542
12565
  return (has("port") || has("lastPort")) && !has("socketPath");
12543
12566
  }
12544
12567
  function pushPathResolution(push, resolution) {
12545
- const ownDir = import_node_path28.default.dirname(resolution.ownExecutablePath);
12568
+ const ownDir = import_node_path29.default.dirname(resolution.ownExecutablePath);
12546
12569
  switch (resolution.agentShim.status) {
12547
12570
  case "ok":
12548
12571
  push("path-resolution", "pass", `\`agent-shim\` on PATH resolves to this running executable, ${resolution.ownExecutablePath}.`, "agent-shim");
@@ -12641,14 +12664,14 @@ function pushProvider(push, entry, cacheEnv, signIn) {
12641
12664
  }
12642
12665
  function pushLegacyName(push, params) {
12643
12666
  const legacyVariables = Object.keys(params.env).filter((name) => name.startsWith(LEGACY_ENV_PREFIX) && params.env[name] !== void 0).sort();
12644
- if (legacyVariables.length === 0 && import_node_path28.default.basename(params.rootPath) !== LEGACY_HOME_DIRNAME) {
12667
+ if (legacyVariables.length === 0 && import_node_path29.default.basename(params.rootPath) !== LEGACY_HOME_DIRNAME) {
12645
12668
  push("legacy-name", "pass", "Nothing here still uses the former `claude-use` name.");
12646
12669
  return;
12647
12670
  }
12648
12671
  for (const name of legacyVariables) {
12649
12672
  push("legacy-name", "warn", `${name} is the former name of ${name.replace(LEGACY_ENV_PREFIX, ENV_PREFIX)}. Rename it; the old name still works but is only a fallback.`, name);
12650
12673
  }
12651
- if (import_node_path28.default.basename(params.rootPath) === LEGACY_HOME_DIRNAME) {
12674
+ if (import_node_path29.default.basename(params.rootPath) === LEGACY_HOME_DIRNAME) {
12652
12675
  push(
12653
12676
  "legacy-name",
12654
12677
  "warn",
@@ -12745,6 +12768,18 @@ function runDoctor(params) {
12745
12768
  `${entry.name} is valid and authenticates with ${credential === void 0 ? "its stored login" : `credential ${describeCredential(credential)}`}${cache.text}.`,
12746
12769
  entry.name
12747
12770
  );
12771
+ const signIn = decideSignIn({
12772
+ hasCredentialBlock: credential !== void 0,
12773
+ keychainItem: params.platform === "darwin" && params.run !== void 0 ? keychainHasItem(params.run, entry.farmRoot) : void 0,
12774
+ credentialsFile: entry.credentialsFilePresent,
12775
+ platform: params.platform
12776
+ });
12777
+ push(
12778
+ "sign-in",
12779
+ signIn.state === "signed-out" ? "warn" : "pass",
12780
+ signIn.state === "signed-out" ? `${entry.name} is signed out (${signIn.detail}). Run \`agent-shim run @${entry.name}\` and sign in.` : `${entry.name} ${signIn.state === "token" ? "uses a credential block" : "is signed in"} (${signIn.detail}).`,
12781
+ entry.name
12782
+ );
12748
12783
  }
12749
12784
  }
12750
12785
  const validatedGlobalConfig = params.globalConfig.raw === void 0 ? void 0 : validateJson(GlobalConfigSchema, params.globalConfig);
@@ -12790,7 +12825,7 @@ function runDoctor(params) {
12790
12825
  } else {
12791
12826
  for (const entry of params.identities) {
12792
12827
  const result = lookupKeychainService(params.run, entry.farmRoot);
12793
- push("keychain", result.found ? "pass" : "warn", result.note, entry.name);
12828
+ push("keychain", "pass", result.found ? result.note : `${result.note} The sign-in section reports whether a login is stored elsewhere.`, entry.name);
12794
12829
  }
12795
12830
  }
12796
12831
  if (params.directoryRules.raw === void 0) {
@@ -12925,6 +12960,7 @@ var SECTION_TITLES = {
12925
12960
  "legacy-name": "Former `claude-use` name",
12926
12961
  "config-profile": "Configuration profiles",
12927
12962
  identity: "Identities",
12963
+ "sign-in": "Sign-in",
12928
12964
  pool: "Pools",
12929
12965
  provider: "Providers",
12930
12966
  keychain: "macOS Keychain",
@@ -12941,6 +12977,7 @@ var SECTION_ORDER = [
12941
12977
  "path-resolution",
12942
12978
  "config-profile",
12943
12979
  "identity",
12980
+ "sign-in",
12944
12981
  "pool",
12945
12982
  "provider",
12946
12983
  "keychain",
@@ -12990,18 +13027,18 @@ function collectDoctorReport(params) {
12990
13027
  const { paths } = params;
12991
13028
  const identityNames = import_node_fs9.default.existsSync(paths.identitiesDir) ? import_node_fs9.default.readdirSync(paths.identitiesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && isIdentityDirectoryName(entry.name)).map((entry) => entry.name).sort() : [];
12992
13029
  const identities = identityNames.map((name) => {
12993
- const farmRoot = import_node_path28.default.join(paths.identitiesDir, name);
12994
- const identityPath = import_node_path28.default.join(farmRoot, "identity.json");
12995
- return { name, path: identityPath, raw: realFsPort.readFileUtf8(identityPath), farmRoot };
13030
+ const farmRoot = import_node_path29.default.join(paths.identitiesDir, name);
13031
+ const identityPath = import_node_path29.default.join(farmRoot, "identity.json");
13032
+ return { name, path: identityPath, raw: realFsPort.readFileUtf8(identityPath), farmRoot, credentialsFilePresent: import_node_fs9.default.existsSync(credentialsFilePath(farmRoot)) };
12996
13033
  });
12997
13034
  const profileNames = import_node_fs9.default.existsSync(paths.configProfilesDir) ? import_node_fs9.default.readdirSync(paths.configProfilesDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
12998
13035
  const configProfiles = profileNames.map((name) => {
12999
- const profilePath = import_node_path28.default.join(paths.configProfilesDir, `${name}.json`);
13036
+ const profilePath = import_node_path29.default.join(paths.configProfilesDir, `${name}.json`);
13000
13037
  return { name, path: profilePath, raw: realFsPort.readFileUtf8(profilePath) };
13001
13038
  });
13002
13039
  const providerNames = import_node_fs9.default.existsSync(paths.providersDir) ? import_node_fs9.default.readdirSync(paths.providersDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
13003
13040
  const providers = providerNames.map((name) => {
13004
- const providerPath = import_node_path28.default.join(paths.providersDir, `${name}.json`);
13041
+ const providerPath = import_node_path29.default.join(paths.providersDir, `${name}.json`);
13005
13042
  return { name, path: providerPath, raw: realFsPort.readFileUtf8(providerPath) };
13006
13043
  });
13007
13044
  const ownExecutablePath = realOwnExecutablePath();
@@ -13013,12 +13050,12 @@ function collectDoctorReport(params) {
13013
13050
  binaryDiscovery = { ok: false, message: error instanceof Error ? error.message : String(error) };
13014
13051
  }
13015
13052
  const shimState = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
13016
- const pathDirs = (params.env.PATH ?? "").split(import_node_path28.default.delimiter).filter((dir) => dir !== "");
13053
+ const pathDirs = (params.env.PATH ?? "").split(import_node_path29.default.delimiter).filter((dir) => dir !== "");
13017
13054
  const claudeShimShadow = shimState === void 0 ? void 0 : refinePathShadow(
13018
13055
  findPathShadow({
13019
13056
  pathDirs,
13020
- targetDir: import_node_path28.default.dirname(shimState.targetPath),
13021
- targetFilename: import_node_path28.default.basename(shimState.targetPath),
13057
+ targetDir: import_node_path29.default.dirname(shimState.targetPath),
13058
+ targetFilename: import_node_path29.default.basename(shimState.targetPath),
13022
13059
  findExecutableInDir
13023
13060
  }),
13024
13061
  shimState.targetPath,
@@ -13044,7 +13081,7 @@ function collectDoctorReport(params) {
13044
13081
  agentShim: refinePathShadow(
13045
13082
  findPathShadow({
13046
13083
  pathDirs,
13047
- targetDir: import_node_path28.default.dirname(ownExecutablePath),
13084
+ targetDir: import_node_path29.default.dirname(ownExecutablePath),
13048
13085
  targetFilename: commandFilename(ownExecutablePath, "agent-shim"),
13049
13086
  findExecutableInDir
13050
13087
  }),
@@ -13118,11 +13155,11 @@ var FrontDoorSessionsOutputSchema = import_zod19.z.strictObject({ sessions: impo
13118
13155
  var CheckRunInputSchema = import_zod19.z.strictObject({
13119
13156
  path: import_zod19.z.string().min(1),
13120
13157
  identity: import_zod19.z.string().min(1).optional()
13121
- }).refine((input) => import_node_path29.default.isAbsolute(input.path), { message: "path must be absolute: the door cannot resolve a relative path against the caller's working directory" });
13158
+ }).refine((input) => import_node_path30.default.isAbsolute(input.path), { message: "path must be absolute: the door cannot resolve a relative path against the caller's working directory" });
13122
13159
  var PoolPickInputSchema = import_zod19.z.strictObject({
13123
13160
  pool: import_zod19.z.string().min(1),
13124
13161
  path: import_zod19.z.string().min(1)
13125
- }).refine((input) => import_node_path29.default.isAbsolute(input.path), { message: "path must be absolute: the door cannot resolve a relative path against the caller's working directory" });
13162
+ }).refine((input) => import_node_path30.default.isAbsolute(input.path), { message: "path must be absolute: the door cannot resolve a relative path against the caller's working directory" });
13126
13163
  var DoctorFindingSchema = import_zod19.z.strictObject({
13127
13164
  section: import_zod19.z.enum(DOCTOR_SECTIONS),
13128
13165
  subject: import_zod19.z.string().optional(),
@@ -13498,7 +13535,7 @@ function profileValue(name, paths) {
13498
13535
  function createConfigApiRouter(deps) {
13499
13536
  const authed = doorApiAuth(deps.expectedToken);
13500
13537
  const { paths } = deps;
13501
- const route = (path36, summary) => authed.meta((0, import_openapi3.openapi)({ method: "POST", path: path36, summary, tags: [CONFIG_API_TAG] }));
13538
+ const route = (path37, summary) => authed.meta((0, import_openapi3.openapi)({ method: "POST", path: path37, summary, tags: [CONFIG_API_TAG] }));
13502
13539
  return {
13503
13540
  config: {
13504
13541
  identity: {
@@ -13857,7 +13894,7 @@ var import_openapi6 = require("@orpc/openapi");
13857
13894
  init_cliError();
13858
13895
 
13859
13896
  // src/frontdoor/launchSchemas.ts
13860
- var import_node_path30 = __toESM(require("node:path"), 1);
13897
+ var import_node_path31 = __toESM(require("node:path"), 1);
13861
13898
  var import_zod22 = require("zod");
13862
13899
  init_claudeVersion();
13863
13900
  init_identity();
@@ -13867,7 +13904,7 @@ var LaunchResolveInputSchema = import_zod22.z.strictObject({
13867
13904
  argv: import_zod22.z.readonly(import_zod22.z.array(import_zod22.z.string())).optional(),
13868
13905
  /** The launch's inherited environment. Absent means an empty one: the door's own environment is never a substitute for the caller's, so a caller that wants `AGENT_SHIM_*` switches or a `CLAUDE_CONFIG_DIR` considered names them here. Values are read, never reported. */
13869
13906
  env: import_zod22.z.record(import_zod22.z.string(), import_zod22.z.string()).optional()
13870
- }).refine((input) => import_node_path30.default.isAbsolute(input.path), { message: "path must be absolute: the door cannot resolve a relative path against the caller's working directory" });
13907
+ }).refine((input) => import_node_path31.default.isAbsolute(input.path), { message: "path must be absolute: the door cannot resolve a relative path against the caller's working directory" });
13871
13908
  var LaunchPoolDecisionSchema = import_zod22.z.strictObject({
13872
13909
  name: import_zod22.z.string(),
13873
13910
  reasons: import_zod22.z.readonly(import_zod22.z.array(import_zod22.z.string())),
@@ -15962,9 +15999,9 @@ data: ${JSON.stringify(envelope)}
15962
15999
  }
15963
16000
 
15964
16001
  // src/frontdoor/rcSelfHostMint.ts
15965
- var import_node_path31 = __toESM(require("node:path"), 1);
16002
+ var import_node_path32 = __toESM(require("node:path"), 1);
15966
16003
  init_account();
15967
- var CREDENTIALS_FILE = ".credentials.json";
16004
+ var CREDENTIALS_FILE2 = ".credentials.json";
15968
16005
  var CLAUDE_JSON2 = ".claude.json";
15969
16006
  var RC_SELF_HOST_RECORD_DIR = "rc-selfhost";
15970
16007
  var RC_SELF_HOST_RECORD_FILE = "credential.json";
@@ -15987,7 +16024,7 @@ function readJsonObject(fs12, file) {
15987
16024
  return isRecord11(parsed) ? parsed : void 0;
15988
16025
  }
15989
16026
  function readRcSelfHostRecord(fs12, frontdoorDir) {
15990
- const parsed = readJsonObject(fs12, import_node_path31.default.join(frontdoorDir, RC_SELF_HOST_RECORD_DIR, RC_SELF_HOST_RECORD_FILE));
16027
+ const parsed = readJsonObject(fs12, import_node_path32.default.join(frontdoorDir, RC_SELF_HOST_RECORD_DIR, RC_SELF_HOST_RECORD_FILE));
15991
16028
  if (parsed === void 0) {
15992
16029
  return void 0;
15993
16030
  }
@@ -16001,11 +16038,11 @@ function mintRcSelfHostCredential(deps) {
16001
16038
  if (!isIdentityName(deps.identity)) {
16002
16039
  throw new Error(`"${deps.identity}" is not a valid identity name.`);
16003
16040
  }
16004
- const identityDir = import_node_path31.default.join(deps.identitiesDir, deps.identity);
16005
- const credentialsFile = import_node_path31.default.join(identityDir, CREDENTIALS_FILE);
16006
- const claudeJsonFile = import_node_path31.default.join(identityDir, CLAUDE_JSON2);
16007
- const recordDir = import_node_path31.default.join(deps.frontdoorDir, RC_SELF_HOST_RECORD_DIR);
16008
- const recordFile = import_node_path31.default.join(recordDir, RC_SELF_HOST_RECORD_FILE);
16041
+ const identityDir = import_node_path32.default.join(deps.identitiesDir, deps.identity);
16042
+ const credentialsFile = import_node_path32.default.join(identityDir, CREDENTIALS_FILE2);
16043
+ const claudeJsonFile = import_node_path32.default.join(identityDir, CLAUDE_JSON2);
16044
+ const recordDir = import_node_path32.default.join(deps.frontdoorDir, RC_SELF_HOST_RECORD_DIR);
16045
+ const recordFile = import_node_path32.default.join(recordDir, RC_SELF_HOST_RECORD_FILE);
16009
16046
  const previousRecord = readRcSelfHostRecord(deps.fs, deps.frontdoorDir);
16010
16047
  const existingCredentials = readJsonObject(deps.fs, credentialsFile);
16011
16048
  const existingOauth = isRecord11(existingCredentials?.claudeAiOauth) ? existingCredentials.claudeAiOauth : void 0;
@@ -16021,7 +16058,7 @@ function mintRcSelfHostCredential(deps) {
16021
16058
  const organizationUuid = deps.newUuid();
16022
16059
  const accountUuid = deps.newUuid();
16023
16060
  const mintedAt = new Date(deps.now()).toISOString();
16024
- deps.fs.mkdirPrivate(import_node_path31.default.dirname(credentialsFile));
16061
+ deps.fs.mkdirPrivate(import_node_path32.default.dirname(credentialsFile));
16025
16062
  deps.fs.writeFilePrivate(credentialsFile, `${JSON.stringify({ ...existingCredentials, claudeAiOauth: { accessToken, refreshToken, expiresAt: null, scopes: [...RC_SELF_HOST_SCOPE_LIST], subscriptionType: SELF_HOST_SUBSCRIPTION_TYPE, rateLimitTier: null } }, null, 2)}
16026
16063
  `);
16027
16064
  const claudeJson = readJsonObject(deps.fs, claudeJsonFile) ?? {};