claude-use 11.22.0 → 11.23.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
@@ -602,7 +602,7 @@ function parseIgnoringUnrecognisedKeys(schema, raw) {
602
602
  return second.success ? { success: true, data: second.data, ignored } : { success: false, issues: second.error.issues };
603
603
  }
604
604
  function describeIgnoredKeys(filePath, ignored) {
605
- const places = ignored.map(({ path: path41, keys }) => `${path41.length === 0 ? "" : `${path41.map(String).join(".")}.`}${keys.join(", ")}`);
605
+ const places = ignored.map(({ path: path42, keys }) => `${path42.length === 0 ? "" : `${path42.map(String).join(".")}.`}${keys.join(", ")}`);
606
606
  return `${filePath} has settings this version of agent-shim does not know and ignores: ${places.join("; ")}. A newer release may read them.`;
607
607
  }
608
608
  function noteIgnoredKeys(filePath, ignored) {
@@ -624,9 +624,9 @@ function restoreIgnoredKeys(filePath, value) {
624
624
  return value;
625
625
  }
626
626
  const restored = structuredClone(value);
627
- for (const { path: path41, values } of ignored) {
627
+ for (const { path: path42, values } of ignored) {
628
628
  let holder = restored;
629
- for (const step of path41) {
629
+ for (const step of path42) {
630
630
  holder = isRecord(holder) ? holder[step] : void 0;
631
631
  }
632
632
  if (isRecord(holder)) {
@@ -2904,7 +2904,7 @@ var init_package = __esm({
2904
2904
  "package.json"() {
2905
2905
  package_default = {
2906
2906
  name: "claude-use",
2907
- version: "11.22.0",
2907
+ version: "11.23.0",
2908
2908
  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.",
2909
2909
  license: "MIT",
2910
2910
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -6706,6 +6706,49 @@ var init_providersStore = __esm({
6706
6706
  }
6707
6707
  });
6708
6708
 
6709
+ // src/usage/signIn.ts
6710
+ import { createHash as createHash6 } from "node:crypto";
6711
+ import path26 from "node:path";
6712
+ function claudeCodeKeychainService(configDir) {
6713
+ return `${KEYCHAIN_SERVICE_PREFIX}-${createHash6("sha256").update(configDir).digest("hex").slice(0, KEYCHAIN_SERVICE_HASH_DIGITS)}`;
6714
+ }
6715
+ function keychainHasItem(run, configDir) {
6716
+ return run.run("security", ["find-generic-password", "-s", claudeCodeKeychainService(configDir)]).status === 0;
6717
+ }
6718
+ function decideSignIn(facts) {
6719
+ if (facts.hasCredentialBlock) {
6720
+ return { state: "token", detail: "supplies its own credential through a credential block, so a stored login is not what a launch uses" };
6721
+ }
6722
+ if (facts.keychainItem === true) {
6723
+ return { state: "signed-in", detail: "a login is stored in the macOS Keychain" };
6724
+ }
6725
+ if (facts.credentialsFile) {
6726
+ return { state: "signed-in", detail: `a login is stored in ${CREDENTIALS_FILE}` };
6727
+ }
6728
+ return { state: "signed-out", detail: facts.platform === "darwin" ? `no Keychain item and no ${CREDENTIALS_FILE}` : `no ${CREDENTIALS_FILE}` };
6729
+ }
6730
+ function credentialsFilePath(farmRoot) {
6731
+ return path26.join(farmRoot, CREDENTIALS_FILE);
6732
+ }
6733
+ function identitySignIn(fs13, probe, identity) {
6734
+ const probing = !identity.hasCredentialBlock;
6735
+ return decideSignIn({
6736
+ hasCredentialBlock: identity.hasCredentialBlock,
6737
+ keychainItem: probing && probe.platform === "darwin" && probe.run !== void 0 ? keychainHasItem(probe.run, identity.farmRoot) : void 0,
6738
+ credentialsFile: probing && fs13.lstat(credentialsFilePath(identity.farmRoot)) !== void 0,
6739
+ platform: probe.platform
6740
+ });
6741
+ }
6742
+ var CREDENTIALS_FILE, KEYCHAIN_SERVICE_PREFIX, KEYCHAIN_SERVICE_HASH_DIGITS;
6743
+ var init_signIn = __esm({
6744
+ "src/usage/signIn.ts"() {
6745
+ "use strict";
6746
+ CREDENTIALS_FILE = ".credentials.json";
6747
+ KEYCHAIN_SERVICE_PREFIX = "Claude Code-credentials";
6748
+ KEYCHAIN_SERVICE_HASH_DIGITS = 8;
6749
+ }
6750
+ });
6751
+
6709
6752
  // src/cli/bool.ts
6710
6753
  function parseBoolWord(input) {
6711
6754
  switch (input) {
@@ -6855,17 +6898,17 @@ var init_commandDeps = __esm({
6855
6898
 
6856
6899
  // src/claudeShim.ts
6857
6900
  import fs8 from "node:fs";
6858
- import path27 from "node:path";
6901
+ import path28 from "node:path";
6859
6902
  import { z as z19 } from "zod";
6860
6903
  function commandFilename(ownExecutablePath, commandName) {
6861
6904
  return ownExecutablePath.toLowerCase().endsWith(".exe") ? `${commandName}.exe` : commandName;
6862
6905
  }
6863
6906
  function resolveOwnBinaryCheck(paths, contentSourcePath) {
6864
6907
  const state = readJson(paths.claudeShimFile, ClaudeShimStateSchema);
6865
- const recordedTarget = state === void 0 ? void 0 : path27.resolve(state.targetPath);
6908
+ const recordedTarget = state === void 0 ? void 0 : path28.resolve(state.targetPath);
6866
6909
  let ownStat;
6867
6910
  return (candidate) => {
6868
- if (recordedTarget !== void 0 && path27.resolve(candidate) === recordedTarget) {
6911
+ if (recordedTarget !== void 0 && path28.resolve(candidate) === recordedTarget) {
6869
6912
  return true;
6870
6913
  }
6871
6914
  ownStat ??= statOrUndefined(contentSourcePath);
@@ -6888,15 +6931,15 @@ function statOrUndefined(target) {
6888
6931
  }
6889
6932
  }
6890
6933
  function findPathShadow(params) {
6891
- const resolvedTargetDir = path27.resolve(params.targetDir);
6934
+ const resolvedTargetDir = path28.resolve(params.targetDir);
6892
6935
  for (const dir of params.pathDirs) {
6893
6936
  const found = params.findExecutableInDir(dir, params.targetFilename);
6894
6937
  if (found === void 0) {
6895
6938
  continue;
6896
6939
  }
6897
- return path27.resolve(dir) === resolvedTargetDir ? { status: "ok" } : { status: "shadowed", by: found };
6940
+ return path28.resolve(dir) === resolvedTargetDir ? { status: "ok" } : { status: "shadowed", by: found };
6898
6941
  }
6899
- return params.pathDirs.some((dir) => path27.resolve(dir) === resolvedTargetDir) ? { status: "ok" } : { status: "not-on-path" };
6942
+ return params.pathDirs.some((dir) => path28.resolve(dir) === resolvedTargetDir) ? { status: "ok" } : { status: "not-on-path" };
6900
6943
  }
6901
6944
  var ClaudeShimStateSchema, nodeLinkFs;
6902
6945
  var init_claudeShim = __esm({
@@ -7554,7 +7597,7 @@ function buildEnv(params) {
7554
7597
  }
7555
7598
  }
7556
7599
  if (params.provider !== void 0) {
7557
- env.ANTHROPIC_AUTH_TOKEN = void 0;
7600
+ env.ANTHROPIC_AUTH_TOKEN = params.remoteControl ? void 0 : PROVIDER_SESSION_PLACEHOLDER;
7558
7601
  env.ANTHROPIC_API_KEY = void 0;
7559
7602
  env.CLAUDE_CODE_OAUTH_TOKEN = void 0;
7560
7603
  } else if (params.identityCredential !== void 0) {
@@ -7586,7 +7629,7 @@ function buildEnv(params) {
7586
7629
  }
7587
7630
  return env;
7588
7631
  }
7589
- var LOOPBACK_HOSTS;
7632
+ var LOOPBACK_HOSTS, PROVIDER_SESSION_PLACEHOLDER;
7590
7633
  var init_flags = __esm({
7591
7634
  "src/launcher/flags.ts"() {
7592
7635
  "use strict";
@@ -7596,6 +7639,7 @@ var init_flags = __esm({
7596
7639
  init_capability();
7597
7640
  init_route();
7598
7641
  LOOPBACK_HOSTS = ["127.0.0.1", "localhost", "::1"];
7642
+ PROVIDER_SESSION_PLACEHOLDER = "agent-shim-provider-session";
7599
7643
  }
7600
7644
  });
7601
7645
 
@@ -8154,7 +8198,7 @@ function decideLaunch(params, readOnly) {
8154
8198
  }
8155
8199
  const pinnedVersion = resolveClaudeVersion({ env, ...parsedArgv.claudeVersion === void 0 ? {} : { flag: parsedArgv.claudeVersion }, ...cascadeLaunch?.claudeVersion === void 0 ? {} : { cascade: cascadeLaunch.claudeVersion } });
8156
8200
  const discovered = params.resolveClaudeBinary(pinnedVersion === void 0 ? void 0 : { version: pinnedVersion.version });
8157
- const flags = resolveLaunchFlags({
8201
+ const requestedFlags = resolveLaunchFlags({
8158
8202
  env,
8159
8203
  ...cascadeLaunch === void 0 ? {} : { cascade: cascadeLaunch },
8160
8204
  flags: {
@@ -8164,6 +8208,11 @@ function decideLaunch(params, readOnly) {
8164
8208
  ...parsedArgv.trackUsage === void 0 ? {} : { trackUsage: parsedArgv.trackUsage }
8165
8209
  }
8166
8210
  });
8211
+ const loginlessProvider = requestedFlags.remoteControl && selectedProvider !== void 0 && farmIdentity !== void 0 && params.hasStoredLogin?.(farmIdentity) === false ? { identity: farmIdentity, provider: selectedProvider.name } : void 0;
8212
+ if (loginlessProvider !== void 0 && !readOnly) {
8213
+ log.info(`agent-shim: identity ${loginlessProvider.identity} has no stored login, so Remote Control, which needs one, is off for this launch; provider ${loginlessProvider.provider} runs without it`);
8214
+ }
8215
+ const flags = loginlessProvider === void 0 ? requestedFlags : { ...requestedFlags, remoteControl: false };
8167
8216
  const args = buildArgv({
8168
8217
  toolFlags: buildFlagArgs(flags),
8169
8218
  extraFlags: splitExtraFlags(env.CLAUDE_EXTRA_FLAGS),
@@ -8289,7 +8338,8 @@ function prepareLaunch(params) {
8289
8338
  ...identityCredential === void 0 ? {} : { identityCredential },
8290
8339
  ...frontDoor === void 0 ? {} : { frontdoor: frontDoor },
8291
8340
  ...headroom === void 0 ? {} : { headroom },
8292
- sessionId
8341
+ sessionId,
8342
+ remoteControl: resolvedFlags.remoteControl
8293
8343
  });
8294
8344
  const releases = [...headroom === void 0 || headroomPort === void 0 ? [] : [headroomPort.release], ...frontDoorRelease === void 0 ? [] : [frontDoorRelease]];
8295
8345
  const release = () => {
@@ -8311,7 +8361,8 @@ function resolveLaunch(params) {
8311
8361
  ...decided.provider === void 0 ? {} : { provider: decided.provider },
8312
8362
  ...frontDoor ? { frontdoor: { port: 0, connectPort: 0, trustBundlePath: "", sessionToken: "" } } : {},
8313
8363
  ...decided.flags.headroom ? { headroom: { socketPath: "", projectId: "" } } : {},
8314
- sessionId: ""
8364
+ sessionId: "",
8365
+ remoteControl: decided.flags.remoteControl
8315
8366
  });
8316
8367
  const credentialNames = decided.identityCredentialBlock === void 0 ? [] : Object.keys(credentialVariables(decided.identityCredentialBlock.target ?? "bearer", ""));
8317
8368
  const environment = [.../* @__PURE__ */ new Set([...Object.keys(built).filter((name) => built[name] !== env[name]), ...credentialNames])].sort();
@@ -8407,7 +8458,7 @@ var init_selfIdentity = __esm({
8407
8458
  });
8408
8459
 
8409
8460
  // src/frontdoor/trust.ts
8410
- import { createHash as createHash6 } from "node:crypto";
8461
+ import { createHash as createHash7 } from "node:crypto";
8411
8462
  import path40 from "node:path";
8412
8463
  function resolveTrustBundle(params) {
8413
8464
  const { caCertFile, bundlesDir, variable, inherited, fs: fs13 } = params;
@@ -8429,7 +8480,7 @@ function resolveTrustBundle(params) {
8429
8480
  }
8430
8481
  const combined = `${inheritedPem.trimEnd()}
8431
8482
  ${caPem}`;
8432
- const bundle = path40.join(bundlesDir, `${createHash6("sha256").update(combined).digest("hex").slice(0, BUNDLE_NAME_HEX_DIGITS)}.pem`);
8483
+ const bundle = path40.join(bundlesDir, `${createHash7("sha256").update(combined).digest("hex").slice(0, BUNDLE_NAME_HEX_DIGITS)}.pem`);
8433
8484
  if (!fs13.exists(bundle)) {
8434
8485
  fs13.mkdirp(bundlesDir);
8435
8486
  fs13.writeFileAtomic(bundle, combined);
@@ -8507,6 +8558,7 @@ var init_realFrontDoorPort = __esm({
8507
8558
 
8508
8559
  // src/launchWiring.ts
8509
8560
  import os8 from "node:os";
8561
+ import path41 from "node:path";
8510
8562
  import { randomUUID as randomUUID5 } from "node:crypto";
8511
8563
  function buildFarmRuntime(paths, cwd) {
8512
8564
  const home = os8.homedir();
@@ -8560,6 +8612,7 @@ function realPrepareLaunchParams(paths, options) {
8560
8612
  headroom: realHeadroomPort(paths, { spawnDaemon: options.spawnDaemon, cwd: farm.runtime.cwd }),
8561
8613
  frontdoor: realFrontDoorPort(paths, options.spawnDaemon),
8562
8614
  credentials: { ...realCredentialPort, cache: realCredentialCacheEnv(paths) },
8615
+ hasStoredLogin: (identity) => identitySignIn(realFarmFs, { platform: process.platform, run: realRunPort }, { farmRoot: path41.join(paths.identitiesDir, identity), hasCredentialBlock: false }).state !== "signed-out",
8563
8616
  ...farm.directoryIdentity === void 0 ? {} : { directoryPinnedIdentity: farm.directoryIdentity },
8564
8617
  ...farm.directoryConfigProfile === void 0 ? {} : { directoryRuleConfigProfile: farm.directoryConfigProfile },
8565
8618
  ...farm.globalDefaultConfigProfile === void 0 ? {} : { globalDefaultConfigProfile: farm.globalDefaultConfigProfile },
@@ -8631,6 +8684,7 @@ var init_launchWiring = __esm({
8631
8684
  init_launchHook();
8632
8685
  init_cascade();
8633
8686
  init_paths();
8687
+ init_signIn();
8634
8688
  init_realPorts();
8635
8689
  LaunchRefusedError = class extends CliError {
8636
8690
  exitCode;
@@ -9538,8 +9592,8 @@ function parseConnectTarget(authority) {
9538
9592
  function isInterceptedHost(host, interceptHosts) {
9539
9593
  return interceptHosts.includes(host);
9540
9594
  }
9541
- function servedByPipeline(path41) {
9542
- return path41?.startsWith(ROUTED_PATH_PREFIX) === true;
9595
+ function servedByPipeline(path42) {
9596
+ return path42?.startsWith(ROUTED_PATH_PREFIX) === true;
9543
9597
  }
9544
9598
  var HOP_BY_HOP_HEADERS = /* @__PURE__ */ new Set([
9545
9599
  "connection",
@@ -10333,11 +10387,11 @@ function createRcSessionTracker(deps) {
10333
10387
  };
10334
10388
  const observeExchange = (request4) => {
10335
10389
  const method = (request4.method ?? "").toUpperCase();
10336
- const path41 = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
10337
- if (!path41.startsWith(RC_SESSIONS_PATH_PREFIX)) {
10390
+ const path42 = new URL(request4.url ?? "/", "http://127.0.0.1").pathname;
10391
+ if (!path42.startsWith(RC_SESSIONS_PATH_PREFIX)) {
10338
10392
  return void 0;
10339
10393
  }
10340
- const tail = path41.slice(RC_SESSIONS_PATH_PREFIX.length);
10394
+ const tail = path42.slice(RC_SESSIONS_PATH_PREFIX.length);
10341
10395
  const facts = {
10342
10396
  oauthAuthorization: oauthAuthorizationOf(request4.headers),
10343
10397
  anthropicVersion: singleHeader(request4.headers, "anthropic-version"),
@@ -10727,8 +10781,8 @@ var RC_READ_FILE_ENCODINGS = ["utf-8", "base64"];
10727
10781
  function isRcReadFileEncoding(value) {
10728
10782
  return RC_READ_FILE_ENCODINGS.some((encoding) => encoding === value);
10729
10783
  }
10730
- function buildRcReadFilePayload(requestId, path41, options) {
10731
- return buildRcControlRequestPayload(requestId, { subtype: "read_file", path: path41, ...options?.maxBytes === void 0 ? {} : { max_bytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } });
10784
+ function buildRcReadFilePayload(requestId, path42, options) {
10785
+ return buildRcControlRequestPayload(requestId, { subtype: "read_file", path: path42, ...options?.maxBytes === void 0 ? {} : { max_bytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } });
10732
10786
  }
10733
10787
  function buildRcFileSuggestionsPayload(requestId, query) {
10734
10788
  return buildRcControlRequestPayload(requestId, { subtype: "file_suggestions", query });
@@ -10830,15 +10884,15 @@ async function getRcSessionContextUsage(deps, sessionId, detail) {
10830
10884
  const result = await deliverRcClientPayload(deps, sessionId, buildRcGetContextUsagePayload(requestId, detail), `request the context usage of session ${sessionId}`);
10831
10885
  return result.ok ? { ...result, requestId } : result;
10832
10886
  }
10833
- async function readRcSessionFile(deps, sessionId, path41, options) {
10834
- if (path41 === "") {
10887
+ async function readRcSessionFile(deps, sessionId, path42, options) {
10888
+ if (path42 === "") {
10835
10889
  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" };
10836
10890
  }
10837
10891
  if (options?.maxBytes !== void 0 && (!Number.isInteger(options.maxBytes) || options.maxBytes <= 0)) {
10838
10892
  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" };
10839
10893
  }
10840
10894
  const requestId = deps.newUuid();
10841
- const result = await deliverRcClientPayload(deps, sessionId, buildRcReadFilePayload(requestId, path41, options), `read ${path41} from session ${sessionId}`);
10895
+ const result = await deliverRcClientPayload(deps, sessionId, buildRcReadFilePayload(requestId, path42, options), `read ${path42} from session ${sessionId}`);
10842
10896
  return result.ok ? { ...result, requestId } : result;
10843
10897
  }
10844
10898
  async function suggestRcSessionFiles(deps, sessionId, query) {
@@ -11007,11 +11061,11 @@ function createRcControlHandler(deps) {
11007
11061
  path: "read-file",
11008
11062
  read: (body) => {
11009
11063
  const session = bodyString(body, "session");
11010
- const path41 = bodyString(body, "path");
11064
+ const path42 = bodyString(body, "path");
11011
11065
  const maxBytes = "maxBytes" in body ? bodyPositiveInteger(body, "maxBytes") : void 0;
11012
11066
  const encoding = "encoding" in body && isRcReadFileEncoding(body.encoding) ? body.encoding : void 0;
11013
- const refused2 = session === void 0 || path41 === void 0 || "maxBytes" in body && maxBytes === void 0 || "encoding" in body && encoding === void 0;
11014
- 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, path41, { ...maxBytes === void 0 ? {} : { maxBytes }, ...encoding === void 0 ? {} : { encoding } }) };
11067
+ const refused2 = session === void 0 || path42 === void 0 || "maxBytes" in body && maxBytes === void 0 || "encoding" in body && encoding === void 0;
11068
+ 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, path42, { ...maxBytes === void 0 ? {} : { maxBytes }, ...encoding === void 0 ? {} : { encoding } }) };
11015
11069
  }
11016
11070
  },
11017
11071
  {
@@ -11321,9 +11375,9 @@ function requestIdOfBody(body) {
11321
11375
  }
11322
11376
  function frontDoorRcControl(transport, token) {
11323
11377
  const headers = { authorization: `Bearer ${token}` };
11324
- const listAnswerOf = async (path41, what) => {
11378
+ const listAnswerOf = async (path42, what) => {
11325
11379
  try {
11326
- return await transport.request({ method: "GET", path: path41, headers });
11380
+ return await transport.request({ method: "GET", path: path42, headers });
11327
11381
  } catch (error) {
11328
11382
  throw new Error(`${what}: could not reach the front door's control listener: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
11329
11383
  }
@@ -11395,10 +11449,10 @@ function frontDoorRcControl(transport, token) {
11395
11449
  }
11396
11450
  return pending;
11397
11451
  };
11398
- const writeOf = async (path41, body, describeFailure2) => {
11452
+ const writeOf = async (path42, body, describeFailure2) => {
11399
11453
  let answer;
11400
11454
  try {
11401
- answer = await transport.request({ method: "POST", path: path41, headers: { ...headers, "content-type": "application/json" }, body });
11455
+ answer = await transport.request({ method: "POST", path: path42, headers: { ...headers, "content-type": "application/json" }, body });
11402
11456
  } catch (error) {
11403
11457
  return { ok: false, message: `could not reach the front door's control listener: ${error instanceof Error ? error.message : String(error)}` };
11404
11458
  }
@@ -11429,10 +11483,10 @@ function frontDoorRcControl(transport, token) {
11429
11483
  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}`),
11430
11484
  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}`),
11431
11485
  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}`),
11432
- readFile: async (sessionId, path41, options) => await writeOf(
11486
+ readFile: async (sessionId, path42, options) => await writeOf(
11433
11487
  `${CONTROL_PATH_PREFIX}/read-file`,
11434
- JSON.stringify({ session: sessionId, path: path41, ...options?.maxBytes === void 0 ? {} : { maxBytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } }),
11435
- (status, message) => `reading ${path41} from ${sessionId} failed (HTTP ${String(status)}): ${message}`
11488
+ JSON.stringify({ session: sessionId, path: path42, ...options?.maxBytes === void 0 ? {} : { maxBytes: options.maxBytes }, ...options?.encoding === void 0 ? {} : { encoding: options.encoding } }),
11489
+ (status, message) => `reading ${path42} from ${sessionId} failed (HTTP ${String(status)}): ${message}`
11436
11490
  ),
11437
11491
  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}`),
11438
11492
  keepAlive: async (sessionId) => await writeOf(`${CONTROL_PATH_PREFIX}/keep-alive`, JSON.stringify({ session: sessionId }), (status, message) => `keeping ${sessionId} alive failed (HTTP ${String(status)}): ${message}`),
@@ -11591,12 +11645,12 @@ function tlsFacts(options) {
11591
11645
  }
11592
11646
  function realRcEventDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
11593
11647
  const agent = new ExemptTlsAgent();
11594
- const post = async (path41, headers, body) => await new Promise((resolve, reject) => {
11648
+ const post = async (path42, headers, body) => await new Promise((resolve, reject) => {
11595
11649
  const options = {
11596
11650
  host: target.host,
11597
11651
  port: target.port,
11598
11652
  method: "POST",
11599
- path: path41,
11653
+ path: path42,
11600
11654
  // 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.
11601
11655
  headers: { ...headers, host: target.servername ?? target.host, "content-length": String(Buffer.byteLength(body, "utf8")) },
11602
11656
  agent
@@ -11661,12 +11715,12 @@ function lateRcStreamDial(resolve) {
11661
11715
  }
11662
11716
  function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_PORT }) {
11663
11717
  const agent = new ExemptTlsAgent();
11664
- const request4 = async (method, path41, headers, body, signal) => await new Promise((resolve, reject) => {
11718
+ const request4 = async (method, path42, headers, body, signal) => await new Promise((resolve, reject) => {
11665
11719
  const options = {
11666
11720
  host: target.host,
11667
11721
  port: target.port,
11668
11722
  method,
11669
- path: path41,
11723
+ path: path42,
11670
11724
  // 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.
11671
11725
  headers: { ...headers, host: target.servername ?? target.host, ...body === void 0 ? {} : { "content-length": String(Buffer.byteLength(body, "utf8")) } },
11672
11726
  agent,
@@ -11708,9 +11762,9 @@ function realRcStreamDial(target = { host: CONNECT_INTERCEPT_HOST, port: HTTPS_P
11708
11762
  return { status, body: await readAll(response) };
11709
11763
  },
11710
11764
  openStream: async (sessionId, headers, resume, signal) => {
11711
- const path41 = resume === void 0 ? `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream` : `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream?from_sequence_num=${String(resume.fromSequenceNum)}`;
11765
+ const path42 = resume === void 0 ? `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream` : `/v1/code/sessions/${encodeURIComponent(sessionId)}/events/stream?from_sequence_num=${String(resume.fromSequenceNum)}`;
11712
11766
  const streamHeaders = resume === void 0 ? headers : { ...headers, "last-event-id": String(resume.fromSequenceNum) };
11713
- const { status, response } = await request4("GET", path41, streamHeaders, void 0, signal);
11767
+ const { status, response } = await request4("GET", path42, streamHeaders, void 0, signal);
11714
11768
  if (!isSuccessful2(status)) {
11715
11769
  await readAll(response);
11716
11770
  return { status, chunks: NO_CHUNKS };
@@ -12862,19 +12916,19 @@ function writePools(paths, pools) {
12862
12916
  applyPatch(paths.globalConfigFile, GlobalConfigSchema, { pools }, { defaults: {} });
12863
12917
  }
12864
12918
  function poolCycleOf(pools, name) {
12865
- const walk = (current, path41) => {
12919
+ const walk = (current, path42) => {
12866
12920
  for (const entry of pools[current]?.identities ?? []) {
12867
12921
  const nested = poolNameOf(poolMemberIdentity(entry));
12868
12922
  if (nested === void 0) {
12869
12923
  continue;
12870
12924
  }
12871
12925
  if (nested === name) {
12872
- return [...path41, nested];
12926
+ return [...path42, nested];
12873
12927
  }
12874
- if (path41.includes(nested)) {
12928
+ if (path42.includes(nested)) {
12875
12929
  continue;
12876
12930
  }
12877
- const found = walk(nested, [...path41, nested]);
12931
+ const found = walk(nested, [...path42, nested]);
12878
12932
  if (found !== void 0) {
12879
12933
  return found;
12880
12934
  }
@@ -12996,6 +13050,7 @@ init_cascade();
12996
13050
  init_paths();
12997
13051
  init_realCredentialCache();
12998
13052
  init_providersStore();
13053
+ init_signIn();
12999
13054
  init_realPorts();
13000
13055
  init_envBool();
13001
13056
  init_load();
@@ -13008,7 +13063,7 @@ init_guard();
13008
13063
  init_providersStore();
13009
13064
  init_projects();
13010
13065
  init_pipeline();
13011
- import path26 from "node:path";
13066
+ import path27 from "node:path";
13012
13067
  import { z as z15 } from "zod";
13013
13068
  var HISTORY_PROJECTS_KEY_PREFIX = "history/projects/";
13014
13069
  function projectFragmentOf(rawKey) {
@@ -13065,26 +13120,18 @@ function formatDecision(decision) {
13065
13120
  const eliminatedNote = decision.eliminated !== void 0 && decision.eliminated.length > 0 ? ` [${String(decision.eliminated.length)} more specific rule(s) eliminated by a failing when-condition]` : "";
13066
13121
  return `${decision.relPath}: ${status} \u2014 ${reason}${eliminatedNote}`;
13067
13122
  }
13068
- function parseKeychainServiceName(stderr) {
13069
- const match = /"svce"<blob>="([^"]*)"/.exec(stderr);
13070
- return match?.[1];
13071
- }
13072
13123
  function lookupKeychainService(run, farmRoot) {
13073
- const result = run.run("security", ["find-generic-password", "-a", farmRoot, "-g"]);
13124
+ const serviceName = claudeCodeKeychainService(farmRoot);
13125
+ const result = run.run("security", ["find-generic-password", "-s", serviceName]);
13074
13126
  if (result.status !== 0) {
13075
13127
  return {
13076
13128
  checked: true,
13077
13129
  found: false,
13078
- note: `No Keychain entry found for account "${farmRoot}" (security exited ${result.status === null ? "with no status" : String(result.status)}).`
13130
+ serviceName,
13131
+ note: `No Keychain entry found for service "${serviceName}" (security exited ${result.status === null ? "with no status" : String(result.status)}).`
13079
13132
  };
13080
13133
  }
13081
- const serviceName = parseKeychainServiceName(result.stderr);
13082
- return {
13083
- checked: true,
13084
- found: true,
13085
- ...serviceName === void 0 ? {} : { serviceName },
13086
- note: serviceName === void 0 ? `A Keychain entry was found for account "${farmRoot}" but its service name could not be parsed from security's output.` : `Keychain service name for this identity: "${serviceName}".`
13087
- };
13134
+ return { checked: true, found: true, serviceName, note: `Keychain service name for this identity: "${serviceName}".` };
13088
13135
  }
13089
13136
  var SettingsSecretsShapeSchema = z15.looseObject({
13090
13137
  env: z15.record(z15.string(), z15.unknown()).optional(),
@@ -13346,10 +13393,10 @@ function collectCheckReport(params) {
13346
13393
  }).input;
13347
13394
  const providerName = cascadeProviderName(cascade);
13348
13395
  const provider = providerName === void 0 ? void 0 : loadCheckProvider(paths, providerName);
13349
- const farmRoot = identityDecision.name === void 0 ? void 0 : path26.join(paths.identitiesDir, identityDecision.name);
13396
+ const farmRoot = identityDecision.name === void 0 ? void 0 : path27.join(paths.identitiesDir, identityDecision.name);
13350
13397
  const settingsFiles = {
13351
- "settings.json": realFsPort.readFileUtf8(path26.join(claudeHome, "settings.json")),
13352
- "settings.local.json": realFsPort.readFileUtf8(path26.join(claudeHome, "settings.local.json"))
13398
+ "settings.json": realFsPort.readFileUtf8(path27.join(claudeHome, "settings.json")),
13399
+ "settings.local.json": realFsPort.readFileUtf8(path27.join(claudeHome, "settings.local.json"))
13353
13400
  };
13354
13401
  const report = runCheck({
13355
13402
  cwd,
@@ -13655,7 +13702,7 @@ init_schema();
13655
13702
  init_cliError();
13656
13703
  init_identity();
13657
13704
  import fs9 from "node:fs";
13658
- import path28 from "node:path";
13705
+ import path29 from "node:path";
13659
13706
  init_lock();
13660
13707
  var IdentityNotFoundError = class extends CliError {
13661
13708
  constructor(name) {
@@ -13684,7 +13731,7 @@ var InvalidIdentityNameError = class extends CliError {
13684
13731
  attemptedName;
13685
13732
  };
13686
13733
  function identityJsonPath(paths, name) {
13687
- return path28.join(paths.identitiesDir, name, "identity.json");
13734
+ return path29.join(paths.identitiesDir, name, "identity.json");
13688
13735
  }
13689
13736
  function requireIdentityNames(paths, names) {
13690
13737
  const existing = new Set(listIdentities(paths).map((entry) => entry.name));
@@ -13799,7 +13846,7 @@ function setIdentityCredential(paths, identityName, change) {
13799
13846
  });
13800
13847
  }
13801
13848
  function belongsToIdentity(entry, name) {
13802
- return entry === name || entry === path28.basename(identityLockPath("", name)) || entry.startsWith(`.${name}.scratch.`) || entry.startsWith(`.${name}.previous.`);
13849
+ return entry === name || entry === path29.basename(identityLockPath("", name)) || entry.startsWith(`.${name}.scratch.`) || entry.startsWith(`.${name}.previous.`);
13803
13850
  }
13804
13851
  function removeIdentity(paths, name) {
13805
13852
  if (!identityExists(paths, name)) {
@@ -13807,7 +13854,7 @@ function removeIdentity(paths, name) {
13807
13854
  }
13808
13855
  for (const entry of fs9.readdirSync(paths.identitiesDir)) {
13809
13856
  if (belongsToIdentity(entry, name)) {
13810
- fs9.rmSync(path28.join(paths.identitiesDir, entry), { recursive: true, force: true });
13857
+ fs9.rmSync(path29.join(paths.identitiesDir, entry), { recursive: true, force: true });
13811
13858
  }
13812
13859
  }
13813
13860
  if (readActiveIdentity2(paths) === name) {
@@ -13819,28 +13866,7 @@ function removeIdentity(paths, name) {
13819
13866
  init_realPorts();
13820
13867
  import fs11 from "node:fs";
13821
13868
  import path30 from "node:path";
13822
-
13823
- // src/usage/signIn.ts
13824
- import path29 from "node:path";
13825
- var CREDENTIALS_FILE = ".credentials.json";
13826
- function keychainHasItem(run, account) {
13827
- return run.run("security", ["find-generic-password", "-a", account]).status === 0;
13828
- }
13829
- function decideSignIn(facts) {
13830
- if (facts.hasCredentialBlock) {
13831
- return { state: "token", detail: "supplies its own credential through a credential block, so a stored login is not what a launch uses" };
13832
- }
13833
- if (facts.keychainItem === true) {
13834
- return { state: "signed-in", detail: "a login is stored in the macOS Keychain" };
13835
- }
13836
- if (facts.credentialsFile) {
13837
- return { state: "signed-in", detail: `a login is stored in ${CREDENTIALS_FILE}` };
13838
- }
13839
- return { state: "signed-out", detail: facts.platform === "darwin" ? `no Keychain item and no ${CREDENTIALS_FILE}` : `no ${CREDENTIALS_FILE}` };
13840
- }
13841
- function credentialsFilePath(farmRoot) {
13842
- return path29.join(farmRoot, CREDENTIALS_FILE);
13843
- }
13869
+ init_signIn();
13844
13870
 
13845
13871
  // src/codex/siwcStore.ts
13846
13872
  import { z as z22 } from "zod";
@@ -15181,7 +15207,7 @@ function profileValue(name, paths) {
15181
15207
  function createConfigApiRouter(deps) {
15182
15208
  const authed = doorApiAuth(deps.expectedToken);
15183
15209
  const { paths } = deps;
15184
- const route = (path41, summary) => authed.meta(openapi3({ method: "POST", path: path41, summary, tags: [CONFIG_API_TAG] }));
15210
+ const route = (path42, summary) => authed.meta(openapi3({ method: "POST", path: path42, summary, tags: [CONFIG_API_TAG] }));
15185
15211
  return {
15186
15212
  config: {
15187
15213
  identity: {
@@ -7218,6 +7218,8 @@ interface PrepareLaunchParams {
7218
7218
  readonly supervised?: SupervisedLaunch;
7219
7219
  /** Wires the launch-time update check behind `update.mode`. Omitted by a caller that cannot resolve releases, re-invoke this binary, or write to the terminal; a mode of `notify` or `auto` with no port wired simply checks nothing. */
7220
7220
  readonly update?: UpdateLaunchPort;
7221
+ /** Whether an identity has a stored login Claude Code can use (a Keychain item or a credentials file). Absent when the caller cannot ask, in which case a launch assumes it does. */
7222
+ readonly hasStoredLogin?: (identity: string) => boolean;
7221
7223
  }
7222
7224
  /** Inputs to `runLauncher`: a launch's inputs plus the port that spawns the child. */
7223
7225
  interface RunLauncherParams extends PrepareLaunchParams {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-use",
3
- "version": "11.22.0",
3
+ "version": "11.23.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>",