claude-use 9.7.1 → 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.1",
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
  );
@@ -6992,10 +6992,16 @@ function decideLaunch(params, readOnly) {
6992
6992
  const assembled = assembleCascade(launchCascade);
6993
6993
  const flattened = flattenLayers(assembled.layers, { home: params.farm.home });
6994
6994
  cascadeLaunch = flattened.launch;
6995
- if (readOnly) {
6995
+ if (readOnly || farmContext === void 0) {
6996
6996
  for (const diagnostic of [...assembled.diagnostics, ...flattened.diagnostics]) {
6997
- if (diagnostic.severity !== "info") {
6998
- log.warn(`agent-shim: ${diagnostic.code}: ${diagnostic.message}`);
6997
+ if (diagnostic.severity === "info") {
6998
+ continue;
6999
+ }
7000
+ const message = `agent-shim: ${diagnostic.code}: ${diagnostic.message}`;
7001
+ if (diagnostic.severity === "error" && !readOnly) {
7002
+ log.error(message);
7003
+ } else {
7004
+ log.warn(message);
6999
7005
  }
7000
7006
  }
7001
7007
  }
@@ -7019,7 +7025,7 @@ function decideLaunch(params, readOnly) {
7019
7025
  extraFlags: splitExtraFlags(env.CLAUDE_EXTRA_FLAGS),
7020
7026
  passthrough: parsedArgv.rest
7021
7027
  });
7022
- 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);
7023
7029
  const decision = {
7024
7030
  identitySource: identityDecision.source,
7025
7031
  configDirEscapeHatch: identityDecision.configDirEscapeHatch,
@@ -7060,7 +7066,7 @@ function prepareLaunch(params) {
7060
7066
  );
7061
7067
  const updateCheck = runLaunchUpdateCheck({
7062
7068
  mode: params.updateMode ?? "off",
7063
- checkPath: path33.join(paths.root, "update.check"),
7069
+ checkPath: path34.join(paths.root, "update.check"),
7064
7070
  currentVersion: package_default.version,
7065
7071
  port: params.update
7066
7072
  });
@@ -7220,12 +7226,12 @@ var init_launcher = __esm({
7220
7226
  });
7221
7227
 
7222
7228
  // src/ownCli.ts
7223
- import path34 from "node:path";
7229
+ import path35 from "node:path";
7224
7230
  function agentShimCliPath(distDir = typeof __AGENT_SHIM_DIST_DIR__ === "string" ? __AGENT_SHIM_DIST_DIR__ : void 0) {
7225
7231
  if (distDir === void 0) {
7226
7232
  throw new OwnCliPathUnknownError();
7227
7233
  }
7228
- return path34.join(distDir, OWN_CLI_FILE_NAME);
7234
+ return path35.join(distDir, OWN_CLI_FILE_NAME);
7229
7235
  }
7230
7236
  var OWN_CLI_FILE_NAME, OwnCliPathUnknownError;
7231
7237
  var init_ownCli = __esm({
@@ -7314,10 +7320,10 @@ request.on("error", (error) => finish(failed, (error.code ? error.code + ": " :
7314
7320
 
7315
7321
  // src/frontdoor/trust.ts
7316
7322
  import { createHash as createHash6 } from "node:crypto";
7317
- import path35 from "node:path";
7323
+ import path36 from "node:path";
7318
7324
  function resolveTrustBundle(params) {
7319
7325
  const { caCertFile, bundlesDir, variable, inherited, fs: fs12 } = params;
7320
- if (inherited === void 0 || inherited === "" || path35.resolve(inherited) === path35.resolve(caCertFile)) {
7326
+ if (inherited === void 0 || inherited === "" || path36.resolve(inherited) === path36.resolve(caCertFile)) {
7321
7327
  return { path: caCertFile };
7322
7328
  }
7323
7329
  const caPem = fs12.readFileUtf8(caCertFile);
@@ -7335,7 +7341,7 @@ function resolveTrustBundle(params) {
7335
7341
  }
7336
7342
  const combined = `${inheritedPem.trimEnd()}
7337
7343
  ${caPem}`;
7338
- 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`);
7339
7345
  if (!fs12.exists(bundle)) {
7340
7346
  fs12.mkdirp(bundlesDir);
7341
7347
  fs12.writeFileAtomic(bundle, combined);
@@ -8345,8 +8351,8 @@ function parseConnectTarget(authority) {
8345
8351
  function isInterceptedHost(host, interceptHosts) {
8346
8352
  return interceptHosts.includes(host);
8347
8353
  }
8348
- function servedByPipeline(path36) {
8349
- return path36?.startsWith(ROUTED_PATH_PREFIX) === true;
8354
+ function servedByPipeline(path37) {
8355
+ return path37?.startsWith(ROUTED_PATH_PREFIX) === true;
8350
8356
  }
8351
8357
  var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
8352
8358
  "connection",
@@ -8881,11 +8887,11 @@ function createRcSessionTracker(deps) {
8881
8887
  };
8882
8888
  const observeExchange = (request4) => {
8883
8889
  const method = (request4.method ?? "").toUpperCase();
8884
- const path36 = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
8885
- 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)) {
8886
8892
  return void 0;
8887
8893
  }
8888
- const tail = path36.slice(RC_SESSIONS_PATH_PREFIX.length);
8894
+ const tail = path37.slice(RC_SESSIONS_PATH_PREFIX.length);
8889
8895
  const facts = {
8890
8896
  oauthAuthorization: oauthAuthorizationOf(request4.headers),
8891
8897
  anthropicVersion: singleHeader(request4.headers, "anthropic-version"),
@@ -9267,8 +9273,8 @@ var RC_READ_FILE_ENCODINGS = ["utf-8", "base64"];
9267
9273
  function isRcReadFileEncoding(value) {
9268
9274
  return RC_READ_FILE_ENCODINGS.some((encoding) => encoding === value);
9269
9275
  }
9270
- function buildRcReadFilePayload(requestId, path36, options) {
9271
- 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 } });
9272
9278
  }
9273
9279
  function buildRcFileSuggestionsPayload(requestId, query) {
9274
9280
  return buildRcControlRequestPayload(requestId, { subtype: "file_suggestions", query });
@@ -9370,15 +9376,15 @@ async function getRcSessionContextUsage(deps, sessionId, detail) {
9370
9376
  const result = await deliverRcClientPayload(deps, sessionId, buildRcGetContextUsagePayload(requestId, detail), `request the context usage of session ${sessionId}`);
9371
9377
  return result.ok ? { ...result, requestId } : result;
9372
9378
  }
9373
- async function readRcSessionFile(deps, sessionId, path36, options) {
9374
- if (path36 === "") {
9379
+ async function readRcSessionFile(deps, sessionId, path37, options) {
9380
+ if (path37 === "") {
9375
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" };
9376
9382
  }
9377
9383
  if (options?.maxBytes !== void 0 && (!Number.isInteger(options.maxBytes) || options.maxBytes <= 0)) {
9378
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" };
9379
9385
  }
9380
9386
  const requestId = deps.newUuid();
9381
- 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}`);
9382
9388
  return result.ok ? { ...result, requestId } : result;
9383
9389
  }
9384
9390
  async function suggestRcSessionFiles(deps, sessionId, query) {
@@ -9547,11 +9553,11 @@ function createRcControlHandler(deps) {
9547
9553
  path: "read-file",
9548
9554
  read: (body) => {
9549
9555
  const session = bodyString(body, "session");
9550
- const path36 = bodyString(body, "path");
9556
+ const path37 = bodyString(body, "path");
9551
9557
  const maxBytes = "maxBytes" in body ? bodyPositiveInteger(body, "maxBytes") : void 0;
9552
9558
  const encoding = "encoding" in body && isRcReadFileEncoding(body.encoding) ? body.encoding : void 0;
9553
- const refused2 = session === void 0 || path36 === void 0 || "maxBytes" in body && maxBytes === void 0 || "encoding" in body && encoding === void 0;
9554
- 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 } }) };
9555
9561
  }
9556
9562
  },
9557
9563
  {
@@ -9861,9 +9867,9 @@ function requestIdOfBody(body) {
9861
9867
  }
9862
9868
  function frontDoorRcControl(transport, token) {
9863
9869
  const headers = { authorization: `Bearer ${token}` };
9864
- const listAnswerOf = async (path36, what) => {
9870
+ const listAnswerOf = async (path37, what) => {
9865
9871
  try {
9866
- return await transport.request({ method: "GET", path: path36, headers });
9872
+ return await transport.request({ method: "GET", path: path37, headers });
9867
9873
  } catch (error) {
9868
9874
  throw new Error(`${what}: could not reach the front door's control listener: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
9869
9875
  }
@@ -9935,10 +9941,10 @@ function frontDoorRcControl(transport, token) {
9935
9941
  }
9936
9942
  return pending;
9937
9943
  };
9938
- const writeOf = async (path36, body, describeFailure) => {
9944
+ const writeOf = async (path37, body, describeFailure) => {
9939
9945
  let answer;
9940
9946
  try {
9941
- 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 });
9942
9948
  } catch (error) {
9943
9949
  return { ok: false, message: `could not reach the front door's control listener: ${error instanceof Error ? error.message : String(error)}` };
9944
9950
  }
@@ -9969,10 +9975,10 @@ function frontDoorRcControl(transport, token) {
9969
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}`),
9970
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}`),
9971
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}`),
9972
- readFile: async (sessionId, path36, options) => await writeOf(
9978
+ readFile: async (sessionId, path37, options) => await writeOf(
9973
9979
  `${CONTROL_PATH_PREFIX}/read-file`,
9974
- JSON.stringify({ session: sessionId, path: path36, ...options?.maxBytes === void 0 ? {} : { maxBytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } }),
9975
- (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}`
9976
9982
  ),
9977
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}`),
9978
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}`),
@@ -10131,12 +10137,12 @@ function tlsFacts(options) {
10131
10137
  }
10132
10138
  function realRcEventDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
10133
10139
  const agent = new ExemptTlsAgent();
10134
- const post = async (path36, headers, body) => await new Promise((resolve, reject) => {
10140
+ const post = async (path37, headers, body) => await new Promise((resolve, reject) => {
10135
10141
  const options = {
10136
10142
  host: target.host,
10137
10143
  port: target.port,
10138
10144
  method: "POST",
10139
- path: path36,
10145
+ path: path37,
10140
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.
10141
10147
  headers: { ...headers, host: target.servername ?? target.host, "content-length": String(Buffer.byteLength(body, "utf8")) },
10142
10148
  agent
@@ -10201,12 +10207,12 @@ function lateRcStreamDial(resolve) {
10201
10207
  }
10202
10208
  function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
10203
10209
  const agent = new ExemptTlsAgent();
10204
- 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) => {
10205
10211
  const options = {
10206
10212
  host: target.host,
10207
10213
  port: target.port,
10208
10214
  method,
10209
- path: path36,
10215
+ path: path37,
10210
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.
10211
10217
  headers: { ...headers, host: target.servername ?? target.host, ...body === void 0 ? {} : { "content-length": String(Buffer.byteLength(body, "utf8")) } },
10212
10218
  agent,
@@ -10248,9 +10254,9 @@ function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_P
10248
10254
  return { status, body: await readAll(response) };
10249
10255
  },
10250
10256
  openStream: async (sessionId, headers, resume, signal) => {
10251
- 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)}`;
10252
10258
  const streamHeaders = resume === void 0 ? headers : { ...headers, "last-event-id": String(resume.fromSequenceNum) };
10253
- const { status, response } = await request4("GET", path36, streamHeaders, void 0, signal);
10259
+ const { status, response } = await request4("GET", path37, streamHeaders, void 0, signal);
10254
10260
  if (!isSuccessful2(status)) {
10255
10261
  await readAll(response);
10256
10262
  return { status, chunks: NO_CHUNKS };
@@ -11165,19 +11171,19 @@ function writePools(paths, pools) {
11165
11171
  applyPatch(paths.globalConfigFile, GlobalConfigSchema, { pools }, { defaults: {} });
11166
11172
  }
11167
11173
  function poolCycleOf(pools, name) {
11168
- const walk = (current, path36) => {
11174
+ const walk = (current, path37) => {
11169
11175
  for (const entry of pools[current]?.identities ?? []) {
11170
11176
  const nested = poolNameOf(poolMemberIdentity(entry));
11171
11177
  if (nested === void 0) {
11172
11178
  continue;
11173
11179
  }
11174
11180
  if (nested === name) {
11175
- return [...path36, nested];
11181
+ return [...path37, nested];
11176
11182
  }
11177
- if (path36.includes(nested)) {
11183
+ if (path37.includes(nested)) {
11178
11184
  continue;
11179
11185
  }
11180
- const found = walk(nested, [...path36, nested]);
11186
+ const found = walk(nested, [...path37, nested]);
11181
11187
  if (found !== void 0) {
11182
11188
  return found;
11183
11189
  }
@@ -11850,7 +11856,7 @@ init_middleware();
11850
11856
  init_preflight();
11851
11857
 
11852
11858
  // src/frontdoor/controlSchemas.ts
11853
- import path29 from "node:path";
11859
+ import path30 from "node:path";
11854
11860
  import { z as z19 } from "zod";
11855
11861
 
11856
11862
  // src/doctorReport.ts
@@ -12021,7 +12027,29 @@ function removeIdentity(paths, name) {
12021
12027
  // src/doctorReport.ts
12022
12028
  init_realPorts();
12023
12029
  import fs10 from "node:fs";
12030
+ import path29 from "node:path";
12031
+
12032
+ // src/usage/signIn.ts
12024
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
+ }
12025
12053
 
12026
12054
  // src/codex/siwcStore.ts
12027
12055
  import { z as z18 } from "zod";
@@ -12122,6 +12150,7 @@ var DOCTOR_SECTIONS = [
12122
12150
  "legacy-name",
12123
12151
  "config-profile",
12124
12152
  "identity",
12153
+ "sign-in",
12125
12154
  "pool",
12126
12155
  "provider",
12127
12156
  "keychain",
@@ -12164,7 +12193,7 @@ function tcpPortEraRecord(raw) {
12164
12193
  return (has("port") || has("lastPort")) && !has("socketPath");
12165
12194
  }
12166
12195
  function pushPathResolution(push, resolution) {
12167
- const ownDir = path28.dirname(resolution.ownExecutablePath);
12196
+ const ownDir = path29.dirname(resolution.ownExecutablePath);
12168
12197
  switch (resolution.agentShim.status) {
12169
12198
  case "ok":
12170
12199
  push("path-resolution", "pass", `\`agent-shim\` on PATH resolves to this running executable, ${resolution.ownExecutablePath}.`, "agent-shim");
@@ -12263,14 +12292,14 @@ function pushProvider(push, entry, cacheEnv, signIn) {
12263
12292
  }
12264
12293
  function pushLegacyName(push, params) {
12265
12294
  const legacyVariables = Object.keys(params.env).filter((name) => name.startsWith(LEGACY_ENV_PREFIX) && params.env[name] !== void 0).sort();
12266
- if (legacyVariables.length === 0 && path28.basename(params.rootPath) !== LEGACY_HOME_DIRNAME) {
12295
+ if (legacyVariables.length === 0 && path29.basename(params.rootPath) !== LEGACY_HOME_DIRNAME) {
12267
12296
  push("legacy-name", "pass", "Nothing here still uses the former `claude-use` name.");
12268
12297
  return;
12269
12298
  }
12270
12299
  for (const name of legacyVariables) {
12271
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);
12272
12301
  }
12273
- if (path28.basename(params.rootPath) === LEGACY_HOME_DIRNAME) {
12302
+ if (path29.basename(params.rootPath) === LEGACY_HOME_DIRNAME) {
12274
12303
  push(
12275
12304
  "legacy-name",
12276
12305
  "warn",
@@ -12367,6 +12396,18 @@ function runDoctor(params) {
12367
12396
  `${entry.name} is valid and authenticates with ${credential === void 0 ? "its stored login" : `credential ${describeCredential(credential)}`}${cache.text}.`,
12368
12397
  entry.name
12369
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
+ );
12370
12411
  }
12371
12412
  }
12372
12413
  const validatedGlobalConfig = params.globalConfig.raw === void 0 ? void 0 : validateJson(GlobalConfigSchema, params.globalConfig);
@@ -12412,7 +12453,7 @@ function runDoctor(params) {
12412
12453
  } else {
12413
12454
  for (const entry of params.identities) {
12414
12455
  const result = lookupKeychainService(params.run, entry.farmRoot);
12415
- 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);
12416
12457
  }
12417
12458
  }
12418
12459
  if (params.directoryRules.raw === void 0) {
@@ -12547,6 +12588,7 @@ var SECTION_TITLES = {
12547
12588
  "legacy-name": "Former `claude-use` name",
12548
12589
  "config-profile": "Configuration profiles",
12549
12590
  identity: "Identities",
12591
+ "sign-in": "Sign-in",
12550
12592
  pool: "Pools",
12551
12593
  provider: "Providers",
12552
12594
  keychain: "macOS Keychain",
@@ -12563,6 +12605,7 @@ var SECTION_ORDER = [
12563
12605
  "path-resolution",
12564
12606
  "config-profile",
12565
12607
  "identity",
12608
+ "sign-in",
12566
12609
  "pool",
12567
12610
  "provider",
12568
12611
  "keychain",
@@ -12612,18 +12655,18 @@ function collectDoctorReport(params) {
12612
12655
  const { paths } = params;
12613
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() : [];
12614
12657
  const identities = identityNames.map((name) => {
12615
- const farmRoot = path28.join(paths.identitiesDir, name);
12616
- const identityPath = path28.join(farmRoot, "identity.json");
12617
- 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)) };
12618
12661
  });
12619
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() : [];
12620
12663
  const configProfiles = profileNames.map((name) => {
12621
- const profilePath = path28.join(paths.configProfilesDir, `${name}.json`);
12664
+ const profilePath = path29.join(paths.configProfilesDir, `${name}.json`);
12622
12665
  return { name, path: profilePath, raw: realFsPort.readFileUtf8(profilePath) };
12623
12666
  });
12624
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() : [];
12625
12668
  const providers = providerNames.map((name) => {
12626
- const providerPath = path28.join(paths.providersDir, `${name}.json`);
12669
+ const providerPath = path29.join(paths.providersDir, `${name}.json`);
12627
12670
  return { name, path: providerPath, raw: realFsPort.readFileUtf8(providerPath) };
12628
12671
  });
12629
12672
  const ownExecutablePath = realOwnExecutablePath();
@@ -12635,12 +12678,12 @@ function collectDoctorReport(params) {
12635
12678
  binaryDiscovery = { ok: false, message: error instanceof Error ? error.message : String(error) };
12636
12679
  }
12637
12680
  const shimState = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
12638
- const pathDirs = (params.env.PATH ?? "").split(path28.delimiter).filter((dir) => dir !== "");
12681
+ const pathDirs = (params.env.PATH ?? "").split(path29.delimiter).filter((dir) => dir !== "");
12639
12682
  const claudeShimShadow = shimState === void 0 ? void 0 : refinePathShadow(
12640
12683
  findPathShadow({
12641
12684
  pathDirs,
12642
- targetDir: path28.dirname(shimState.targetPath),
12643
- targetFilename: path28.basename(shimState.targetPath),
12685
+ targetDir: path29.dirname(shimState.targetPath),
12686
+ targetFilename: path29.basename(shimState.targetPath),
12644
12687
  findExecutableInDir
12645
12688
  }),
12646
12689
  shimState.targetPath,
@@ -12666,7 +12709,7 @@ function collectDoctorReport(params) {
12666
12709
  agentShim: refinePathShadow(
12667
12710
  findPathShadow({
12668
12711
  pathDirs,
12669
- targetDir: path28.dirname(ownExecutablePath),
12712
+ targetDir: path29.dirname(ownExecutablePath),
12670
12713
  targetFilename: commandFilename(ownExecutablePath, "agent-shim"),
12671
12714
  findExecutableInDir
12672
12715
  }),
@@ -12740,11 +12783,11 @@ var FrontDoorSessionsOutputSchema = z19.strictObject({ sessions: z19.readonly(z1
12740
12783
  var CheckRunInputSchema = z19.strictObject({
12741
12784
  path: z19.string().min(1),
12742
12785
  identity: z19.string().min(1).optional()
12743
- }).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" });
12744
12787
  var PoolPickInputSchema = z19.strictObject({
12745
12788
  pool: z19.string().min(1),
12746
12789
  path: z19.string().min(1)
12747
- }).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" });
12748
12791
  var DoctorFindingSchema = z19.strictObject({
12749
12792
  section: z19.enum(DOCTOR_SECTIONS),
12750
12793
  subject: z19.string().optional(),
@@ -13120,7 +13163,7 @@ function profileValue(name, paths) {
13120
13163
  function createConfigApiRouter(deps) {
13121
13164
  const authed = doorApiAuth(deps.expectedToken);
13122
13165
  const { paths } = deps;
13123
- 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] }));
13124
13167
  return {
13125
13168
  config: {
13126
13169
  identity: {
@@ -13479,7 +13522,7 @@ import { ORPCError as ORPCError5 } from "@orpc/client";
13479
13522
  import { openapi as openapi6 } from "@orpc/openapi";
13480
13523
 
13481
13524
  // src/frontdoor/launchSchemas.ts
13482
- import path30 from "node:path";
13525
+ import path31 from "node:path";
13483
13526
  import { z as z22 } from "zod";
13484
13527
  init_claudeVersion();
13485
13528
  init_identity();
@@ -13489,7 +13532,7 @@ var LaunchResolveInputSchema = z22.strictObject({
13489
13532
  argv: z22.readonly(z22.array(z22.string())).optional(),
13490
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. */
13491
13534
  env: z22.record(z22.string(), z22.string()).optional()
13492
- }).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" });
13493
13536
  var LaunchPoolDecisionSchema = z22.strictObject({
13494
13537
  name: z22.string(),
13495
13538
  reasons: z22.readonly(z22.array(z22.string())),
@@ -15585,8 +15628,8 @@ data: ${JSON.stringify(envelope)}
15585
15628
 
15586
15629
  // src/frontdoor/rcSelfHostMint.ts
15587
15630
  init_account();
15588
- import path31 from "node:path";
15589
- var CREDENTIALS_FILE = ".credentials.json";
15631
+ import path32 from "node:path";
15632
+ var CREDENTIALS_FILE2 = ".credentials.json";
15590
15633
  var CLAUDE_JSON2 = ".claude.json";
15591
15634
  var RC_SELF_HOST_RECORD_DIR = "rc-selfhost";
15592
15635
  var RC_SELF_HOST_RECORD_FILE = "credential.json";
@@ -15609,7 +15652,7 @@ function readJsonObject(fs12, file) {
15609
15652
  return isRecord11(parsed) ? parsed : void 0;
15610
15653
  }
15611
15654
  function readRcSelfHostRecord(fs12, frontdoorDir) {
15612
- 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));
15613
15656
  if (parsed === void 0) {
15614
15657
  return void 0;
15615
15658
  }
@@ -15623,11 +15666,11 @@ function mintRcSelfHostCredential(deps) {
15623
15666
  if (!isIdentityName(deps.identity)) {
15624
15667
  throw new Error(`"${deps.identity}" is not a valid identity name.`);
15625
15668
  }
15626
- const identityDir = path31.join(deps.identitiesDir, deps.identity);
15627
- const credentialsFile = path31.join(identityDir, CREDENTIALS_FILE);
15628
- const claudeJsonFile = path31.join(identityDir, CLAUDE_JSON2);
15629
- const recordDir = path31.join(deps.frontdoorDir, RC_SELF_HOST_RECORD_DIR);
15630
- 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);
15631
15674
  const previousRecord = readRcSelfHostRecord(deps.fs, deps.frontdoorDir);
15632
15675
  const existingCredentials = readJsonObject(deps.fs, credentialsFile);
15633
15676
  const existingOauth = isRecord11(existingCredentials?.claudeAiOauth) ? existingCredentials.claudeAiOauth : void 0;
@@ -15643,7 +15686,7 @@ function mintRcSelfHostCredential(deps) {
15643
15686
  const organizationUuid = deps.newUuid();
15644
15687
  const accountUuid = deps.newUuid();
15645
15688
  const mintedAt = new Date(deps.now()).toISOString();
15646
- deps.fs.mkdirPrivate(path31.dirname(credentialsFile));
15689
+ deps.fs.mkdirPrivate(path32.dirname(credentialsFile));
15647
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)}
15648
15691
  `);
15649
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.1",
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>",