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.mjs CHANGED
@@ -2639,7 +2639,7 @@ var init_package = __esm({
2639
2639
  "package.json"() {
2640
2640
  package_default = {
2641
2641
  name: "claude-use",
2642
- version: "9.7.2",
2642
+ version: "9.8.0",
2643
2643
  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.",
2644
2644
  license: "MIT",
2645
2645
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -6510,7 +6510,7 @@ var init_headers = __esm({
6510
6510
  });
6511
6511
 
6512
6512
  // src/launcher/flags.ts
6513
- import path32 from "node:path";
6513
+ import path33 from "node:path";
6514
6514
  function resolveLaunchFlags(params) {
6515
6515
  return {
6516
6516
  skipPermissions: params.flags?.skipPermissions ?? parseEnvBool("AGENT_SHIM_SKIP_PERMISSIONS", params.env.AGENT_SHIM_SKIP_PERMISSIONS) ?? params.cascade?.skipPermissions ?? false,
@@ -6541,7 +6541,7 @@ function buildEnv(params) {
6541
6541
  const parentCustomHeaders = params.baseEnv.ANTHROPIC_CUSTOM_HEADERS;
6542
6542
  let providerCustomHeaders;
6543
6543
  if (!params.configDirEscapeHatch && params.resolvedIdentityName !== void 0) {
6544
- env.CLAUDE_CONFIG_DIR = path32.join(params.identitiesDir, params.resolvedIdentityName);
6544
+ env.CLAUDE_CONFIG_DIR = path33.join(params.identitiesDir, params.resolvedIdentityName);
6545
6545
  }
6546
6546
  if (params.provider !== void 0) {
6547
6547
  const providerEnv = params.provider.definition.env ?? {};
@@ -6803,7 +6803,7 @@ var init_launchHook = __esm({
6803
6803
 
6804
6804
  // src/launcher.ts
6805
6805
  import { randomUUID as randomUUID4 } from "node:crypto";
6806
- import path33 from "node:path";
6806
+ import path34 from "node:path";
6807
6807
  function resolveIdentityCredential(identity, credential, env, port) {
6808
6808
  if (port === void 0) {
6809
6809
  return { ok: false, status: 1, message: `agent-shim: identity ${identity} needs its credential resolved, but this launcher has no credential port wired` };
@@ -6913,7 +6913,7 @@ function decideLaunch(params, readOnly) {
6913
6913
  identityDefaultConfigProfile: loadedIdentity?.config.defaultConfigProfile,
6914
6914
  globalDefaultConfigProfile: params.globalDefaultConfigProfile
6915
6915
  });
6916
- if (configProfileDecision.name !== void 0 && params.allowMissingConfigProfile !== true && fs12.readConfigFile(path33.join(paths.configProfilesDir, `${configProfileDecision.name}.json`)) === void 0) {
6916
+ if (configProfileDecision.name !== void 0 && params.allowMissingConfigProfile !== true && fs12.readConfigFile(path34.join(paths.configProfilesDir, `${configProfileDecision.name}.json`)) === void 0) {
6917
6917
  log.error(
6918
6918
  `agent-shim: no configuration profile named "${configProfileDecision.name}" (selected via ${configProfileDecision.source}). Run \`agent-shim profile add ${configProfileDecision.name}\` first.`
6919
6919
  );
@@ -7025,7 +7025,7 @@ function decideLaunch(params, readOnly) {
7025
7025
  extraFlags: splitExtraFlags(env.CLAUDE_EXTRA_FLAGS),
7026
7026
  passthrough: parsedArgv.rest
7027
7027
  });
7028
- const configDir = configDirEscapeHatchApplies || identityDecision.name === void 0 ? env.CLAUDE_CONFIG_DIR : path33.join(paths.identitiesDir, identityDecision.name);
7028
+ const configDir = configDirEscapeHatchApplies || identityDecision.name === void 0 ? env.CLAUDE_CONFIG_DIR : path34.join(paths.identitiesDir, identityDecision.name);
7029
7029
  const decision = {
7030
7030
  identitySource: identityDecision.source,
7031
7031
  configDirEscapeHatch: identityDecision.configDirEscapeHatch,
@@ -7066,7 +7066,7 @@ function prepareLaunch(params) {
7066
7066
  );
7067
7067
  const updateCheck = runLaunchUpdateCheck({
7068
7068
  mode: params.updateMode ?? "off",
7069
- checkPath: path33.join(paths.root, "update.check"),
7069
+ checkPath: path34.join(paths.root, "update.check"),
7070
7070
  currentVersion: package_default.version,
7071
7071
  port: params.update
7072
7072
  });
@@ -7226,12 +7226,12 @@ var init_launcher = __esm({
7226
7226
  });
7227
7227
 
7228
7228
  // src/ownCli.ts
7229
- import path34 from "node:path";
7229
+ import path35 from "node:path";
7230
7230
  function agentShimCliPath(distDir = typeof __AGENT_SHIM_DIST_DIR__ === "string" ? __AGENT_SHIM_DIST_DIR__ : void 0) {
7231
7231
  if (distDir === void 0) {
7232
7232
  throw new OwnCliPathUnknownError();
7233
7233
  }
7234
- return path34.join(distDir, OWN_CLI_FILE_NAME);
7234
+ return path35.join(distDir, OWN_CLI_FILE_NAME);
7235
7235
  }
7236
7236
  var OWN_CLI_FILE_NAME, OwnCliPathUnknownError;
7237
7237
  var init_ownCli = __esm({
@@ -7320,10 +7320,10 @@ request.on("error", (error) => finish(failed, (error.code ? error.code + ": " :
7320
7320
 
7321
7321
  // src/frontdoor/trust.ts
7322
7322
  import { createHash as createHash6 } from "node:crypto";
7323
- import path35 from "node:path";
7323
+ import path36 from "node:path";
7324
7324
  function resolveTrustBundle(params) {
7325
7325
  const { caCertFile, bundlesDir, variable, inherited, fs: fs12 } = params;
7326
- if (inherited === void 0 || inherited === "" || path35.resolve(inherited) === path35.resolve(caCertFile)) {
7326
+ if (inherited === void 0 || inherited === "" || path36.resolve(inherited) === path36.resolve(caCertFile)) {
7327
7327
  return { path: caCertFile };
7328
7328
  }
7329
7329
  const caPem = fs12.readFileUtf8(caCertFile);
@@ -7341,7 +7341,7 @@ function resolveTrustBundle(params) {
7341
7341
  }
7342
7342
  const combined = `${inheritedPem.trimEnd()}
7343
7343
  ${caPem}`;
7344
- const bundle = path35.join(bundlesDir, `${createHash6("sha256").update(combined).digest("hex").slice(0, BUNDLE_NAME_HEX_DIGITS)}.pem`);
7344
+ const bundle = path36.join(bundlesDir, `${createHash6("sha256").update(combined).digest("hex").slice(0, BUNDLE_NAME_HEX_DIGITS)}.pem`);
7345
7345
  if (!fs12.exists(bundle)) {
7346
7346
  fs12.mkdirp(bundlesDir);
7347
7347
  fs12.writeFileAtomic(bundle, combined);
@@ -8351,8 +8351,8 @@ function parseConnectTarget(authority) {
8351
8351
  function isInterceptedHost(host, interceptHosts) {
8352
8352
  return interceptHosts.includes(host);
8353
8353
  }
8354
- function servedByPipeline(path36) {
8355
- return path36?.startsWith(ROUTED_PATH_PREFIX) === true;
8354
+ function servedByPipeline(path37) {
8355
+ return path37?.startsWith(ROUTED_PATH_PREFIX) === true;
8356
8356
  }
8357
8357
  var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
8358
8358
  "connection",
@@ -8887,11 +8887,11 @@ function createRcSessionTracker(deps) {
8887
8887
  };
8888
8888
  const observeExchange = (request4) => {
8889
8889
  const method = (request4.method ?? "").toUpperCase();
8890
- const path36 = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
8891
- if (!path36.startsWith(RC_SESSIONS_PATH_PREFIX)) {
8890
+ const path37 = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
8891
+ if (!path37.startsWith(RC_SESSIONS_PATH_PREFIX)) {
8892
8892
  return void 0;
8893
8893
  }
8894
- const tail = path36.slice(RC_SESSIONS_PATH_PREFIX.length);
8894
+ const tail = path37.slice(RC_SESSIONS_PATH_PREFIX.length);
8895
8895
  const facts = {
8896
8896
  oauthAuthorization: oauthAuthorizationOf(request4.headers),
8897
8897
  anthropicVersion: singleHeader(request4.headers, "anthropic-version"),
@@ -9273,8 +9273,8 @@ var RC_READ_FILE_ENCODINGS = ["utf-8", "base64"];
9273
9273
  function isRcReadFileEncoding(value) {
9274
9274
  return RC_READ_FILE_ENCODINGS.some((encoding) => encoding === value);
9275
9275
  }
9276
- function buildRcReadFilePayload(requestId, path36, options) {
9277
- return buildRcControlRequestPayload(requestId, { subtype: "read_file", path: path36, ...options?.maxBytes === void 0 ? {} : { max_bytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } });
9276
+ function buildRcReadFilePayload(requestId, path37, options) {
9277
+ return buildRcControlRequestPayload(requestId, { subtype: "read_file", path: path37, ...options?.maxBytes === void 0 ? {} : { max_bytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } });
9278
9278
  }
9279
9279
  function buildRcFileSuggestionsPayload(requestId, query) {
9280
9280
  return buildRcControlRequestPayload(requestId, { subtype: "file_suggestions", query });
@@ -9376,15 +9376,15 @@ async function getRcSessionContextUsage(deps, sessionId, detail) {
9376
9376
  const result = await deliverRcClientPayload(deps, sessionId, buildRcGetContextUsagePayload(requestId, detail), `request the context usage of session ${sessionId}`);
9377
9377
  return result.ok ? { ...result, requestId } : result;
9378
9378
  }
9379
- async function readRcSessionFile(deps, sessionId, path36, options) {
9380
- if (path36 === "") {
9379
+ async function readRcSessionFile(deps, sessionId, path37, options) {
9380
+ if (path37 === "") {
9381
9381
  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" };
9382
9382
  }
9383
9383
  if (options?.maxBytes !== void 0 && (!Number.isInteger(options.maxBytes) || options.maxBytes <= 0)) {
9384
9384
  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" };
9385
9385
  }
9386
9386
  const requestId = deps.newUuid();
9387
- const result = await deliverRcClientPayload(deps, sessionId, buildRcReadFilePayload(requestId, path36, options), `read ${path36} from session ${sessionId}`);
9387
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcReadFilePayload(requestId, path37, options), `read ${path37} from session ${sessionId}`);
9388
9388
  return result.ok ? { ...result, requestId } : result;
9389
9389
  }
9390
9390
  async function suggestRcSessionFiles(deps, sessionId, query) {
@@ -9553,11 +9553,11 @@ function createRcControlHandler(deps) {
9553
9553
  path: "read-file",
9554
9554
  read: (body) => {
9555
9555
  const session = bodyString(body, "session");
9556
- const path36 = bodyString(body, "path");
9556
+ const path37 = bodyString(body, "path");
9557
9557
  const maxBytes = "maxBytes" in body ? bodyPositiveInteger(body, "maxBytes") : void 0;
9558
9558
  const encoding = "encoding" in body && isRcReadFileEncoding(body.encoding) ? body.encoding : void 0;
9559
- const refused2 = session === void 0 || path36 === void 0 || "maxBytes" in body && maxBytes === void 0 || "encoding" in body && encoding === void 0;
9560
- 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 } }) };
9559
+ const refused2 = session === void 0 || path37 === void 0 || "maxBytes" in body && maxBytes === void 0 || "encoding" in body && encoding === void 0;
9560
+ 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 } }) };
9561
9561
  }
9562
9562
  },
9563
9563
  {
@@ -9867,9 +9867,9 @@ function requestIdOfBody(body) {
9867
9867
  }
9868
9868
  function frontDoorRcControl(transport, token) {
9869
9869
  const headers = { authorization: `Bearer ${token}` };
9870
- const listAnswerOf = async (path36, what) => {
9870
+ const listAnswerOf = async (path37, what) => {
9871
9871
  try {
9872
- return await transport.request({ method: "GET", path: path36, headers });
9872
+ return await transport.request({ method: "GET", path: path37, headers });
9873
9873
  } catch (error) {
9874
9874
  throw new Error(`${what}: could not reach the front door's control listener: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
9875
9875
  }
@@ -9941,10 +9941,10 @@ function frontDoorRcControl(transport, token) {
9941
9941
  }
9942
9942
  return pending;
9943
9943
  };
9944
- const writeOf = async (path36, body, describeFailure) => {
9944
+ const writeOf = async (path37, body, describeFailure) => {
9945
9945
  let answer;
9946
9946
  try {
9947
- answer = await transport.request({ method: "POST", path: path36, headers: { ...headers, "content-type": "application/json" }, body });
9947
+ answer = await transport.request({ method: "POST", path: path37, headers: { ...headers, "content-type": "application/json" }, body });
9948
9948
  } catch (error) {
9949
9949
  return { ok: false, message: `could not reach the front door's control listener: ${error instanceof Error ? error.message : String(error)}` };
9950
9950
  }
@@ -9975,10 +9975,10 @@ function frontDoorRcControl(transport, token) {
9975
9975
  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}`),
9976
9976
  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}`),
9977
9977
  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}`),
9978
- readFile: async (sessionId, path36, options) => await writeOf(
9978
+ readFile: async (sessionId, path37, options) => await writeOf(
9979
9979
  `${CONTROL_PATH_PREFIX}/read-file`,
9980
- JSON.stringify({ session: sessionId, path: path36, ...options?.maxBytes === void 0 ? {} : { maxBytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } }),
9981
- (status, message) => `reading ${path36} from ${sessionId} failed (HTTP ${String(status)}): ${message}`
9980
+ JSON.stringify({ session: sessionId, path: path37, ...options?.maxBytes === void 0 ? {} : { maxBytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } }),
9981
+ (status, message) => `reading ${path37} from ${sessionId} failed (HTTP ${String(status)}): ${message}`
9982
9982
  ),
9983
9983
  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}`),
9984
9984
  keepAlive: async (sessionId) => await writeOf(`${CONTROL_PATH_PREFIX}/keep-alive`, JSON.stringify({ session: sessionId }), (status, message) => `keeping ${sessionId} alive failed (HTTP ${String(status)}): ${message}`),
@@ -10137,12 +10137,12 @@ function tlsFacts(options) {
10137
10137
  }
10138
10138
  function realRcEventDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
10139
10139
  const agent = new ExemptTlsAgent();
10140
- const post = async (path36, headers, body) => await new Promise((resolve, reject) => {
10140
+ const post = async (path37, headers, body) => await new Promise((resolve, reject) => {
10141
10141
  const options = {
10142
10142
  host: target.host,
10143
10143
  port: target.port,
10144
10144
  method: "POST",
10145
- path: path36,
10145
+ path: path37,
10146
10146
  // 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.
10147
10147
  headers: { ...headers, host: target.servername ?? target.host, "content-length": String(Buffer.byteLength(body, "utf8")) },
10148
10148
  agent
@@ -10207,12 +10207,12 @@ function lateRcStreamDial(resolve) {
10207
10207
  }
10208
10208
  function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
10209
10209
  const agent = new ExemptTlsAgent();
10210
- const request4 = async (method, path36, headers, body, signal) => await new Promise((resolve, reject) => {
10210
+ const request4 = async (method, path37, headers, body, signal) => await new Promise((resolve, reject) => {
10211
10211
  const options = {
10212
10212
  host: target.host,
10213
10213
  port: target.port,
10214
10214
  method,
10215
- path: path36,
10215
+ path: path37,
10216
10216
  // 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.
10217
10217
  headers: { ...headers, host: target.servername ?? target.host, ...body === void 0 ? {} : { "content-length": String(Buffer.byteLength(body, "utf8")) } },
10218
10218
  agent,
@@ -10254,9 +10254,9 @@ function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_P
10254
10254
  return { status, body: await readAll(response) };
10255
10255
  },
10256
10256
  openStream: async (sessionId, headers, resume, signal) => {
10257
- 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)}`;
10257
+ 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)}`;
10258
10258
  const streamHeaders = resume === void 0 ? headers : { ...headers, "last-event-id": String(resume.fromSequenceNum) };
10259
- const { status, response } = await request4("GET", path36, streamHeaders, void 0, signal);
10259
+ const { status, response } = await request4("GET", path37, streamHeaders, void 0, signal);
10260
10260
  if (!isSuccessful2(status)) {
10261
10261
  await readAll(response);
10262
10262
  return { status, chunks: NO_CHUNKS };
@@ -11171,19 +11171,19 @@ function writePools(paths, pools) {
11171
11171
  applyPatch(paths.globalConfigFile, GlobalConfigSchema, { pools }, { defaults: {} });
11172
11172
  }
11173
11173
  function poolCycleOf(pools, name) {
11174
- const walk = (current, path36) => {
11174
+ const walk = (current, path37) => {
11175
11175
  for (const entry of pools[current]?.identities ?? []) {
11176
11176
  const nested = poolNameOf(poolMemberIdentity(entry));
11177
11177
  if (nested === void 0) {
11178
11178
  continue;
11179
11179
  }
11180
11180
  if (nested === name) {
11181
- return [...path36, nested];
11181
+ return [...path37, nested];
11182
11182
  }
11183
- if (path36.includes(nested)) {
11183
+ if (path37.includes(nested)) {
11184
11184
  continue;
11185
11185
  }
11186
- const found = walk(nested, [...path36, nested]);
11186
+ const found = walk(nested, [...path37, nested]);
11187
11187
  if (found !== void 0) {
11188
11188
  return found;
11189
11189
  }
@@ -11856,7 +11856,7 @@ init_middleware();
11856
11856
  init_preflight();
11857
11857
 
11858
11858
  // src/frontdoor/controlSchemas.ts
11859
- import path29 from "node:path";
11859
+ import path30 from "node:path";
11860
11860
  import { z as z19 } from "zod";
11861
11861
 
11862
11862
  // src/doctorReport.ts
@@ -12027,7 +12027,29 @@ function removeIdentity(paths, name) {
12027
12027
  // src/doctorReport.ts
12028
12028
  init_realPorts();
12029
12029
  import fs10 from "node:fs";
12030
+ import path29 from "node:path";
12031
+
12032
+ // src/usage/signIn.ts
12030
12033
  import path28 from "node:path";
12034
+ var CREDENTIALS_FILE = ".credentials.json";
12035
+ function keychainHasItem(run, account) {
12036
+ return run.run("security", ["find-generic-password", "-a", account]).status === 0;
12037
+ }
12038
+ function decideSignIn(facts) {
12039
+ if (facts.hasCredentialBlock) {
12040
+ return { state: "token", detail: "supplies its own credential through a credential block, so a stored login is not what a launch uses" };
12041
+ }
12042
+ if (facts.keychainItem === true) {
12043
+ return { state: "signed-in", detail: "a login is stored in the macOS Keychain" };
12044
+ }
12045
+ if (facts.credentialsFile) {
12046
+ return { state: "signed-in", detail: `a login is stored in ${CREDENTIALS_FILE}` };
12047
+ }
12048
+ return { state: "signed-out", detail: facts.platform === "darwin" ? `no Keychain item and no ${CREDENTIALS_FILE}` : `no ${CREDENTIALS_FILE}` };
12049
+ }
12050
+ function credentialsFilePath(farmRoot) {
12051
+ return path28.join(farmRoot, CREDENTIALS_FILE);
12052
+ }
12031
12053
 
12032
12054
  // src/codex/siwcStore.ts
12033
12055
  import { z as z18 } from "zod";
@@ -12128,6 +12150,7 @@ var DOCTOR_SECTIONS = [
12128
12150
  "legacy-name",
12129
12151
  "config-profile",
12130
12152
  "identity",
12153
+ "sign-in",
12131
12154
  "pool",
12132
12155
  "provider",
12133
12156
  "keychain",
@@ -12170,7 +12193,7 @@ function tcpPortEraRecord(raw) {
12170
12193
  return (has("port") || has("lastPort")) && !has("socketPath");
12171
12194
  }
12172
12195
  function pushPathResolution(push, resolution) {
12173
- const ownDir = path28.dirname(resolution.ownExecutablePath);
12196
+ const ownDir = path29.dirname(resolution.ownExecutablePath);
12174
12197
  switch (resolution.agentShim.status) {
12175
12198
  case "ok":
12176
12199
  push("path-resolution", "pass", `\`agent-shim\` on PATH resolves to this running executable, ${resolution.ownExecutablePath}.`, "agent-shim");
@@ -12269,14 +12292,14 @@ function pushProvider(push, entry, cacheEnv, signIn) {
12269
12292
  }
12270
12293
  function pushLegacyName(push, params) {
12271
12294
  const legacyVariables = Object.keys(params.env).filter((name) => name.startsWith(LEGACY_ENV_PREFIX) && params.env[name] !== void 0).sort();
12272
- if (legacyVariables.length === 0 && path28.basename(params.rootPath) !== LEGACY_HOME_DIRNAME) {
12295
+ if (legacyVariables.length === 0 && path29.basename(params.rootPath) !== LEGACY_HOME_DIRNAME) {
12273
12296
  push("legacy-name", "pass", "Nothing here still uses the former `claude-use` name.");
12274
12297
  return;
12275
12298
  }
12276
12299
  for (const name of legacyVariables) {
12277
12300
  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);
12278
12301
  }
12279
- if (path28.basename(params.rootPath) === LEGACY_HOME_DIRNAME) {
12302
+ if (path29.basename(params.rootPath) === LEGACY_HOME_DIRNAME) {
12280
12303
  push(
12281
12304
  "legacy-name",
12282
12305
  "warn",
@@ -12373,6 +12396,18 @@ function runDoctor(params) {
12373
12396
  `${entry.name} is valid and authenticates with ${credential === void 0 ? "its stored login" : `credential ${describeCredential(credential)}`}${cache.text}.`,
12374
12397
  entry.name
12375
12398
  );
12399
+ const signIn = decideSignIn({
12400
+ hasCredentialBlock: credential !== void 0,
12401
+ keychainItem: params.platform === "darwin" && params.run !== void 0 ? keychainHasItem(params.run, entry.farmRoot) : void 0,
12402
+ credentialsFile: entry.credentialsFilePresent,
12403
+ platform: params.platform
12404
+ });
12405
+ push(
12406
+ "sign-in",
12407
+ signIn.state === "signed-out" ? "warn" : "pass",
12408
+ 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}).`,
12409
+ entry.name
12410
+ );
12376
12411
  }
12377
12412
  }
12378
12413
  const validatedGlobalConfig = params.globalConfig.raw === void 0 ? void 0 : validateJson(GlobalConfigSchema, params.globalConfig);
@@ -12418,7 +12453,7 @@ function runDoctor(params) {
12418
12453
  } else {
12419
12454
  for (const entry of params.identities) {
12420
12455
  const result = lookupKeychainService(params.run, entry.farmRoot);
12421
- push("keychain", result.found ? "pass" : "warn", result.note, entry.name);
12456
+ push("keychain", "pass", result.found ? result.note : `${result.note} The sign-in section reports whether a login is stored elsewhere.`, entry.name);
12422
12457
  }
12423
12458
  }
12424
12459
  if (params.directoryRules.raw === void 0) {
@@ -12553,6 +12588,7 @@ var SECTION_TITLES = {
12553
12588
  "legacy-name": "Former `claude-use` name",
12554
12589
  "config-profile": "Configuration profiles",
12555
12590
  identity: "Identities",
12591
+ "sign-in": "Sign-in",
12556
12592
  pool: "Pools",
12557
12593
  provider: "Providers",
12558
12594
  keychain: "macOS Keychain",
@@ -12569,6 +12605,7 @@ var SECTION_ORDER = [
12569
12605
  "path-resolution",
12570
12606
  "config-profile",
12571
12607
  "identity",
12608
+ "sign-in",
12572
12609
  "pool",
12573
12610
  "provider",
12574
12611
  "keychain",
@@ -12618,18 +12655,18 @@ function collectDoctorReport(params) {
12618
12655
  const { paths } = params;
12619
12656
  const identityNames = fs10.existsSync(paths.identitiesDir) ? fs10.readdirSync(paths.identitiesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && isIdentityDirectoryName(entry.name)).map((entry) => entry.name).sort() : [];
12620
12657
  const identities = identityNames.map((name) => {
12621
- const farmRoot = path28.join(paths.identitiesDir, name);
12622
- const identityPath = path28.join(farmRoot, "identity.json");
12623
- return { name, path: identityPath, raw: realFsPort.readFileUtf8(identityPath), farmRoot };
12658
+ const farmRoot = path29.join(paths.identitiesDir, name);
12659
+ const identityPath = path29.join(farmRoot, "identity.json");
12660
+ return { name, path: identityPath, raw: realFsPort.readFileUtf8(identityPath), farmRoot, credentialsFilePresent: fs10.existsSync(credentialsFilePath(farmRoot)) };
12624
12661
  });
12625
12662
  const profileNames = fs10.existsSync(paths.configProfilesDir) ? fs10.readdirSync(paths.configProfilesDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
12626
12663
  const configProfiles = profileNames.map((name) => {
12627
- const profilePath = path28.join(paths.configProfilesDir, `${name}.json`);
12664
+ const profilePath = path29.join(paths.configProfilesDir, `${name}.json`);
12628
12665
  return { name, path: profilePath, raw: realFsPort.readFileUtf8(profilePath) };
12629
12666
  });
12630
12667
  const providerNames = fs10.existsSync(paths.providersDir) ? fs10.readdirSync(paths.providersDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => entry.name.slice(0, -".json".length)).sort() : [];
12631
12668
  const providers = providerNames.map((name) => {
12632
- const providerPath = path28.join(paths.providersDir, `${name}.json`);
12669
+ const providerPath = path29.join(paths.providersDir, `${name}.json`);
12633
12670
  return { name, path: providerPath, raw: realFsPort.readFileUtf8(providerPath) };
12634
12671
  });
12635
12672
  const ownExecutablePath = realOwnExecutablePath();
@@ -12641,12 +12678,12 @@ function collectDoctorReport(params) {
12641
12678
  binaryDiscovery = { ok: false, message: error instanceof Error ? error.message : String(error) };
12642
12679
  }
12643
12680
  const shimState = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
12644
- const pathDirs = (params.env.PATH ?? "").split(path28.delimiter).filter((dir) => dir !== "");
12681
+ const pathDirs = (params.env.PATH ?? "").split(path29.delimiter).filter((dir) => dir !== "");
12645
12682
  const claudeShimShadow = shimState === void 0 ? void 0 : refinePathShadow(
12646
12683
  findPathShadow({
12647
12684
  pathDirs,
12648
- targetDir: path28.dirname(shimState.targetPath),
12649
- targetFilename: path28.basename(shimState.targetPath),
12685
+ targetDir: path29.dirname(shimState.targetPath),
12686
+ targetFilename: path29.basename(shimState.targetPath),
12650
12687
  findExecutableInDir
12651
12688
  }),
12652
12689
  shimState.targetPath,
@@ -12672,7 +12709,7 @@ function collectDoctorReport(params) {
12672
12709
  agentShim: refinePathShadow(
12673
12710
  findPathShadow({
12674
12711
  pathDirs,
12675
- targetDir: path28.dirname(ownExecutablePath),
12712
+ targetDir: path29.dirname(ownExecutablePath),
12676
12713
  targetFilename: commandFilename(ownExecutablePath, "agent-shim"),
12677
12714
  findExecutableInDir
12678
12715
  }),
@@ -12746,11 +12783,11 @@ var FrontDoorSessionsOutputSchema = z19.strictObject({ sessions: z19.readonly(z1
12746
12783
  var CheckRunInputSchema = z19.strictObject({
12747
12784
  path: z19.string().min(1),
12748
12785
  identity: z19.string().min(1).optional()
12749
- }).refine((input) => path29.isAbsolute(input.path), { message: "path must be absolute: the door cannot resolve a relative path against the caller's working directory" });
12786
+ }).refine((input) => path30.isAbsolute(input.path), { message: "path must be absolute: the door cannot resolve a relative path against the caller's working directory" });
12750
12787
  var PoolPickInputSchema = z19.strictObject({
12751
12788
  pool: z19.string().min(1),
12752
12789
  path: z19.string().min(1)
12753
- }).refine((input) => path29.isAbsolute(input.path), { message: "path must be absolute: the door cannot resolve a relative path against the caller's working directory" });
12790
+ }).refine((input) => path30.isAbsolute(input.path), { message: "path must be absolute: the door cannot resolve a relative path against the caller's working directory" });
12754
12791
  var DoctorFindingSchema = z19.strictObject({
12755
12792
  section: z19.enum(DOCTOR_SECTIONS),
12756
12793
  subject: z19.string().optional(),
@@ -13126,7 +13163,7 @@ function profileValue(name, paths) {
13126
13163
  function createConfigApiRouter(deps) {
13127
13164
  const authed = doorApiAuth(deps.expectedToken);
13128
13165
  const { paths } = deps;
13129
- const route = (path36, summary) => authed.meta(openapi3({ method: "POST", path: path36, summary, tags: [CONFIG_API_TAG] }));
13166
+ const route = (path37, summary) => authed.meta(openapi3({ method: "POST", path: path37, summary, tags: [CONFIG_API_TAG] }));
13130
13167
  return {
13131
13168
  config: {
13132
13169
  identity: {
@@ -13485,7 +13522,7 @@ import { ORPCError as ORPCError5 } from "@orpc/client";
13485
13522
  import { openapi as openapi6 } from "@orpc/openapi";
13486
13523
 
13487
13524
  // src/frontdoor/launchSchemas.ts
13488
- import path30 from "node:path";
13525
+ import path31 from "node:path";
13489
13526
  import { z as z22 } from "zod";
13490
13527
  init_claudeVersion();
13491
13528
  init_identity();
@@ -13495,7 +13532,7 @@ var LaunchResolveInputSchema = z22.strictObject({
13495
13532
  argv: z22.readonly(z22.array(z22.string())).optional(),
13496
13533
  /** 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. */
13497
13534
  env: z22.record(z22.string(), z22.string()).optional()
13498
- }).refine((input) => path30.isAbsolute(input.path), { message: "path must be absolute: the door cannot resolve a relative path against the caller's working directory" });
13535
+ }).refine((input) => path31.isAbsolute(input.path), { message: "path must be absolute: the door cannot resolve a relative path against the caller's working directory" });
13499
13536
  var LaunchPoolDecisionSchema = z22.strictObject({
13500
13537
  name: z22.string(),
13501
13538
  reasons: z22.readonly(z22.array(z22.string())),
@@ -15591,8 +15628,8 @@ data: ${JSON.stringify(envelope)}
15591
15628
 
15592
15629
  // src/frontdoor/rcSelfHostMint.ts
15593
15630
  init_account();
15594
- import path31 from "node:path";
15595
- var CREDENTIALS_FILE = ".credentials.json";
15631
+ import path32 from "node:path";
15632
+ var CREDENTIALS_FILE2 = ".credentials.json";
15596
15633
  var CLAUDE_JSON2 = ".claude.json";
15597
15634
  var RC_SELF_HOST_RECORD_DIR = "rc-selfhost";
15598
15635
  var RC_SELF_HOST_RECORD_FILE = "credential.json";
@@ -15615,7 +15652,7 @@ function readJsonObject(fs12, file) {
15615
15652
  return isRecord11(parsed) ? parsed : void 0;
15616
15653
  }
15617
15654
  function readRcSelfHostRecord(fs12, frontdoorDir) {
15618
- const parsed = readJsonObject(fs12, path31.join(frontdoorDir, RC_SELF_HOST_RECORD_DIR, RC_SELF_HOST_RECORD_FILE));
15655
+ const parsed = readJsonObject(fs12, path32.join(frontdoorDir, RC_SELF_HOST_RECORD_DIR, RC_SELF_HOST_RECORD_FILE));
15619
15656
  if (parsed === void 0) {
15620
15657
  return void 0;
15621
15658
  }
@@ -15629,11 +15666,11 @@ function mintRcSelfHostCredential(deps) {
15629
15666
  if (!isIdentityName(deps.identity)) {
15630
15667
  throw new Error(`"${deps.identity}" is not a valid identity name.`);
15631
15668
  }
15632
- const identityDir = path31.join(deps.identitiesDir, deps.identity);
15633
- const credentialsFile = path31.join(identityDir, CREDENTIALS_FILE);
15634
- const claudeJsonFile = path31.join(identityDir, CLAUDE_JSON2);
15635
- const recordDir = path31.join(deps.frontdoorDir, RC_SELF_HOST_RECORD_DIR);
15636
- const recordFile = path31.join(recordDir, RC_SELF_HOST_RECORD_FILE);
15669
+ const identityDir = path32.join(deps.identitiesDir, deps.identity);
15670
+ const credentialsFile = path32.join(identityDir, CREDENTIALS_FILE2);
15671
+ const claudeJsonFile = path32.join(identityDir, CLAUDE_JSON2);
15672
+ const recordDir = path32.join(deps.frontdoorDir, RC_SELF_HOST_RECORD_DIR);
15673
+ const recordFile = path32.join(recordDir, RC_SELF_HOST_RECORD_FILE);
15637
15674
  const previousRecord = readRcSelfHostRecord(deps.fs, deps.frontdoorDir);
15638
15675
  const existingCredentials = readJsonObject(deps.fs, credentialsFile);
15639
15676
  const existingOauth = isRecord11(existingCredentials?.claudeAiOauth) ? existingCredentials.claudeAiOauth : void 0;
@@ -15649,7 +15686,7 @@ function mintRcSelfHostCredential(deps) {
15649
15686
  const organizationUuid = deps.newUuid();
15650
15687
  const accountUuid = deps.newUuid();
15651
15688
  const mintedAt = new Date(deps.now()).toISOString();
15652
- deps.fs.mkdirPrivate(path31.dirname(credentialsFile));
15689
+ deps.fs.mkdirPrivate(path32.dirname(credentialsFile));
15653
15690
  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)}
15654
15691
  `);
15655
15692
  const claudeJson = readJsonObject(deps.fs, claudeJsonFile) ?? {};
@@ -4467,7 +4467,7 @@ type ClaudeBinaryResolver = (request?: Readonly<ClaudeBinaryRequest>) => Discove
4467
4467
 
4468
4468
  declare const DOCTOR_SEVERITIES: readonly ["pass", "warn", "fail"];
4469
4469
  type DoctorSeverity = (typeof DOCTOR_SEVERITIES)[number];
4470
- declare const DOCTOR_SECTIONS: readonly ["ambient-credential", "binary-discovery", "claude-shim", "path-resolution", "legacy-name", "config-profile", "identity", "pool", "provider", "keychain", "directory-rules", "global-config", "categories-local", "active-identity", "headroom"];
4470
+ declare const DOCTOR_SECTIONS: readonly ["ambient-credential", "binary-discovery", "claude-shim", "path-resolution", "legacy-name", "config-profile", "identity", "sign-in", "pool", "provider", "keychain", "directory-rules", "global-config", "categories-local", "active-identity", "headroom"];
4471
4471
  type DoctorSection = (typeof DOCTOR_SECTIONS)[number];
4472
4472
  /** One line of `agent-shim doctor`'s report. `subject` names the identity/profile/rule the finding is about, when the section has more than one of those. */
4473
4473
  interface DoctorFinding {
@@ -4488,6 +4488,8 @@ interface DoctorIdentityInput {
4488
4488
  readonly raw: string | undefined;
4489
4489
  /** The identity's own farm root (`identitiesDir/<name>`) — the account name the Keychain lookup uses. */
4490
4490
  readonly farmRoot: string;
4491
+ /** Whether the identity's `.credentials.json` exists, pre-resolved by the wiring layer since it is a file-existence check. */
4492
+ readonly credentialsFilePresent: boolean;
4491
4493
  }
4492
4494
  /** One configuration profile's raw `<name>.json`, unparsed. */
4493
4495
  interface DoctorConfigProfileInput {
@@ -7243,6 +7245,7 @@ declare function createControlApiRouter(deps: ControlApiDeps): {
7243
7245
  "claude-shim": "claude-shim";
7244
7246
  "path-resolution": "path-resolution";
7245
7247
  "legacy-name": "legacy-name";
7248
+ "sign-in": "sign-in";
7246
7249
  "directory-rules": "directory-rules";
7247
7250
  "categories-local": "categories-local";
7248
7251
  "active-identity": "active-identity";
@@ -9212,6 +9215,7 @@ declare function createDoorApiRouter(deps: DoorApiDeps): {
9212
9215
  "claude-shim": "claude-shim";
9213
9216
  "path-resolution": "path-resolution";
9214
9217
  "legacy-name": "legacy-name";
9218
+ "sign-in": "sign-in";
9215
9219
  "directory-rules": "directory-rules";
9216
9220
  "categories-local": "categories-local";
9217
9221
  "active-identity": "active-identity";
@@ -11017,6 +11021,7 @@ declare const DoctorFindingSchema: z.ZodObject<{
11017
11021
  "claude-shim": "claude-shim";
11018
11022
  "path-resolution": "path-resolution";
11019
11023
  "legacy-name": "legacy-name";
11024
+ "sign-in": "sign-in";
11020
11025
  "directory-rules": "directory-rules";
11021
11026
  "categories-local": "categories-local";
11022
11027
  "active-identity": "active-identity";
@@ -11046,6 +11051,7 @@ declare const DoctorRunOutputSchema: z.ZodObject<{
11046
11051
  "claude-shim": "claude-shim";
11047
11052
  "path-resolution": "path-resolution";
11048
11053
  "legacy-name": "legacy-name";
11054
+ "sign-in": "sign-in";
11049
11055
  "directory-rules": "directory-rules";
11050
11056
  "categories-local": "categories-local";
11051
11057
  "active-identity": "active-identity";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-use",
3
- "version": "9.7.2",
3
+ "version": "9.8.0",
4
4
  "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.",
5
5
  "license": "MIT",
6
6
  "author": "Joseph Mearman <joseph@mearman.co.uk>",