claude-use 2.7.0 → 2.9.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.
Files changed (3) hide show
  1. package/README.md +4 -1
  2. package/dist/cli.cjs +85 -17
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -77,13 +77,16 @@ claude-use profile set <name> --category <cat>=<bool>[,<cat>=<bool>,...]
77
77
  claude-use profile set <name> --entry "<path>"=<bool>[,"<path>"=<bool>,...]
78
78
  claude-use profile set <name> [--skip-permissions] [--remote-control] [--headroom]
79
79
 
80
- claude-use provider add <name> --display-name <name> --base-url <url> --token-env <VAR> [--env KEY=VALUE]
80
+ claude-use provider add <name> --display-name <name> --base-url <url> (--token-env <VAR> | --token-command <argv...>) [--auth-scheme <bearer|apiKey>] [--env KEY=VALUE]
81
81
  claude-use provider list
82
82
  claude-use provider show <name>
83
83
  claude-use provider remove <name>
84
84
 
85
85
  claude-use headroom status
86
86
 
87
+ claude @<identity> --headroom # one launch routed through headroom
88
+ claude --no-headroom # one launch routed direct, overriding every layer
89
+
87
90
  claude-use rules add <path> [--profile <name>] [--identity <name>]
88
91
  claude-use rules list
89
92
  claude-use rules remove <path>
package/dist/cli.cjs CHANGED
@@ -239649,7 +239649,7 @@ var program = new Command();
239649
239649
  // package.json
239650
239650
  var package_default = {
239651
239651
  name: "claude-use",
239652
- version: "2.7.0",
239652
+ version: "2.9.0",
239653
239653
  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.",
239654
239654
  license: "Apache-2.0",
239655
239655
  author: "Joseph Mearman <joseph@mearman.co.uk>",
@@ -259498,14 +259498,34 @@ var LaunchSchema = external_exports.strictObject({
259498
259498
  provider: external_exports.string().min(1).optional(),
259499
259499
  headroom: external_exports.boolean().optional()
259500
259500
  });
259501
+ var AUTH_SCHEMES = ["bearer", "apiKey"];
259501
259502
  var ProviderSchema = external_exports.strictObject({
259502
259503
  $schema: external_exports.string().optional(),
259503
259504
  displayName: external_exports.string().min(1),
259504
259505
  baseUrl: external_exports.url(),
259505
259506
  tokenEnv: external_exports.string().min(1).optional(),
259507
+ tokenCommand: external_exports.tuple([external_exports.string().min(1)], external_exports.string()).optional(),
259508
+ authScheme: external_exports.enum(AUTH_SCHEMES).optional(),
259506
259509
  env: external_exports.record(external_exports.string().min(1), external_exports.string()).optional()
259507
- }).refine((provider) => provider.tokenEnv !== void 0 || (provider.env?.ANTHROPIC_AUTH_TOKEN ?? "") !== "", {
259508
- message: "a provider needs either tokenEnv (the name of the environment variable holding its token) or a non-empty env.ANTHROPIC_AUTH_TOKEN"
259510
+ }).superRefine((provider, ctx) => {
259511
+ const sources = [
259512
+ provider.tokenEnv !== void 0 ? "tokenEnv" : void 0,
259513
+ provider.tokenCommand !== void 0 ? "tokenCommand" : void 0,
259514
+ (provider.env?.ANTHROPIC_AUTH_TOKEN ?? "") !== "" ? "env.ANTHROPIC_AUTH_TOKEN" : void 0
259515
+ ].filter((source) => source !== void 0);
259516
+ if (sources.length !== 1) {
259517
+ ctx.addIssue({
259518
+ code: "custom",
259519
+ message: sources.length === 0 ? "a provider needs exactly one token source: tokenEnv (the name of the environment variable holding its token), tokenCommand (an argv whose stdout is the token), or a non-empty env.ANTHROPIC_AUTH_TOKEN" : `a provider takes exactly one token source, but this one has ${sources.join(", ")}`
259520
+ });
259521
+ }
259522
+ if (provider.authScheme === "apiKey" && (provider.env?.ANTHROPIC_API_KEY ?? "") !== "") {
259523
+ ctx.addIssue({
259524
+ code: "custom",
259525
+ path: ["env", "ANTHROPIC_API_KEY"],
259526
+ message: 'authScheme "apiKey" exports the token as ANTHROPIC_API_KEY, so env.ANTHROPIC_API_KEY must not be set to a value'
259527
+ });
259528
+ }
259509
259529
  });
259510
259530
  var ConfigProfileSchema = external_exports.strictObject({
259511
259531
  $schema: external_exports.string().optional(),
@@ -266045,6 +266065,13 @@ var READY_FETCH_TIMEOUT_MS = 2e3;
266045
266065
  function headroomPidRunning(pid) {
266046
266066
  return !exitedHeadroom.has(pid) && realIsProcessRunning(pid);
266047
266067
  }
266068
+ function headroomSpawnEnv(parentEnv, allowlist) {
266069
+ return {
266070
+ ...parentEnv,
266071
+ HEADROOM_ALLOWED_BASE_URLS: allowlist.join(","),
266072
+ HEADROOM_HTTP2: parentEnv.HEADROOM_HTTP2 ?? "0"
266073
+ };
266074
+ }
266048
266075
  function realSupervisorPorts(paths) {
266049
266076
  return {
266050
266077
  fs: realFarmFs,
@@ -266067,7 +266094,7 @@ function realSupervisorPorts(paths) {
266067
266094
  const child = (0, import_node_child_process3.spawn)("headroom", ["proxy", "--host", "127.0.0.1", "--port", String(port)], {
266068
266095
  detached: true,
266069
266096
  stdio: ["ignore", logFd, logFd],
266070
- env: { ...process.env, HEADROOM_ALLOWED_BASE_URLS: allowlist.join(",") }
266097
+ env: headroomSpawnEnv(process.env, allowlist)
266071
266098
  });
266072
266099
  if (child.pid === void 0) {
266073
266100
  throw new Error("spawning headroom returned no pid");
@@ -266270,6 +266297,8 @@ function addProvider(paths, name, input2) {
266270
266297
  displayName: input2.displayName,
266271
266298
  baseUrl: input2.baseUrl,
266272
266299
  ...input2.tokenEnv === void 0 ? {} : { tokenEnv: input2.tokenEnv },
266300
+ ...input2.tokenCommand === void 0 ? {} : { tokenCommand: input2.tokenCommand },
266301
+ ...input2.authScheme === void 0 ? {} : { authScheme: input2.authScheme },
266273
266302
  ...input2.env === void 0 || Object.keys(input2.env).length === 0 ? {} : { env: input2.env }
266274
266303
  });
266275
266304
  if (!parsed.success) {
@@ -266285,6 +266314,30 @@ function removeProvider(paths, name) {
266285
266314
  import_node_fs10.default.rmSync(providerJsonPath(paths, name));
266286
266315
  }
266287
266316
  var PROVIDER_MISSING_TOKEN_EXIT = 64;
266317
+ function obtainToken(name, definition, params) {
266318
+ const unusable = (message) => ({ ok: false, status: PROVIDER_MISSING_TOKEN_EXIT, message });
266319
+ if (definition.tokenCommand !== void 0) {
266320
+ const [command, ...args] = definition.tokenCommand;
266321
+ if (params.run === void 0) {
266322
+ return { ok: false, status: 1, message: `claude-use: provider ${name} needs its token command run, but this launcher has no command runner wired` };
266323
+ }
266324
+ const result = params.run.run(command, args);
266325
+ if (result.status !== 0) {
266326
+ const stderr = result.stderr.trim();
266327
+ const outcome = result.status === null ? "could not be run or was killed by a signal" : `exited with status ${String(result.status)}`;
266328
+ return unusable(`claude-use: provider ${name}: token command ${command} ${outcome}${stderr === "" ? "" : `: ${stderr}`}`);
266329
+ }
266330
+ const token2 = result.stdout.trim();
266331
+ return token2 === "" ? unusable(`claude-use: provider ${name}: token command ${command} printed no token`) : { ok: true, token: token2 };
266332
+ }
266333
+ const token = definition.tokenEnv !== void 0 ? params.env[definition.tokenEnv] : definition.env?.ANTHROPIC_AUTH_TOKEN;
266334
+ if (token === void 0 || token === "") {
266335
+ return unusable(
266336
+ definition.tokenEnv !== void 0 ? `claude-use: provider ${name} needs ${definition.tokenEnv} set in your environment` : `claude-use: provider ${name} has no usable credential: its env.ANTHROPIC_AUTH_TOKEN is empty`
266337
+ );
266338
+ }
266339
+ return { ok: true, token };
266340
+ }
266288
266341
  function resolveProvider(params) {
266289
266342
  const cliName = params.cliProvider !== void 0 && params.cliProvider !== "" ? params.cliProvider : void 0;
266290
266343
  let cascadeName;
@@ -266308,14 +266361,11 @@ function resolveProvider(params) {
266308
266361
  message: `claude-use: no provider named "${name}". ` + (known.length > 0 ? `Known providers: ${known.join(", ")}.` : "No providers are defined yet; run `claude-use provider add`.")
266309
266362
  };
266310
266363
  }
266311
- const token = definition.tokenEnv !== void 0 ? params.env[definition.tokenEnv] : definition.env?.ANTHROPIC_AUTH_TOKEN;
266312
- if (token === void 0 || token === "") {
266313
- return {
266314
- ok: false,
266315
- status: PROVIDER_MISSING_TOKEN_EXIT,
266316
- message: definition.tokenEnv !== void 0 ? `claude-use: provider ${name} needs ${definition.tokenEnv} set in your environment` : `claude-use: provider ${name} has no usable credential: its env.ANTHROPIC_AUTH_TOKEN is empty`
266317
- };
266364
+ const credential = obtainToken(name, definition, params);
266365
+ if (!credential.ok) {
266366
+ return credential;
266318
266367
  }
266368
+ const token = credential.token;
266319
266369
  return { ok: true, provider: { name, definition, token } };
266320
266370
  }
266321
266371
  function collectEnvPairs(value, previous = {}) {
@@ -266332,16 +266382,17 @@ function collectEnvPairs(value, previous = {}) {
266332
266382
  }
266333
266383
  function registerProviderCommand(program2, paths) {
266334
266384
  const provider = program2.command("provider").description("Manage API providers the launcher can route a session through.");
266335
- provider.command("add <name>").description("Create a new API provider definition.").requiredOption("--display-name <name>", "Human-readable name, exported to the child as CLAUDE_USE_PROVIDER.").requiredOption("--base-url <url>", "Anthropic-compatible base URL the child's requests are sent to.").option("--token-env <var>", "NAME of the environment variable holding the provider's token (never the token itself). Optional only when --env carries ANTHROPIC_AUTH_TOKEN (a local proxy's fixed dummy token).").option("--env <pair>", "Extra KEY=VALUE environment entry for the child (repeatable).", collectEnvPairs).action((name, options) => {
266385
+ provider.command("add <name>").description("Create a new API provider definition.").requiredOption("--display-name <name>", "Human-readable name, exported to the child as CLAUDE_USE_PROVIDER.").requiredOption("--base-url <url>", "Anthropic-compatible base URL the child's requests are sent to.").option("--token-env <var>", "NAME of the environment variable holding the provider's token (never the token itself). Exactly one of --token-env, --token-command or --env ANTHROPIC_AUTH_TOKEN=<fixed dummy token for a local proxy> is required.").option("--token-command <argv...>", "Command (program and arguments) run at launch whose trimmed stdout is the token, e.g. --token-command op read op://vault/item/field. Give it last on the command line, since it consumes every following word.").addOption(new Option("--auth-scheme <scheme>", "How the token reaches Claude Code: bearer sets ANTHROPIC_AUTH_TOKEN (default), apiKey sets ANTHROPIC_API_KEY.").choices(AUTH_SCHEMES)).option("--env <pair>", "Extra KEY=VALUE environment entry for the child (repeatable).", collectEnvPairs).action((name, options) => {
266336
266386
  addProvider(paths, name, {
266337
266387
  displayName: options.displayName,
266338
266388
  baseUrl: options.baseUrl,
266339
266389
  ...options.tokenEnv === void 0 ? {} : { tokenEnv: options.tokenEnv },
266390
+ ...options.tokenCommand === void 0 ? {} : { tokenCommand: options.tokenCommand },
266391
+ ...options.authScheme === void 0 ? {} : { authScheme: options.authScheme },
266340
266392
  ...options.env === void 0 ? {} : { env: options.env }
266341
266393
  });
266342
- console.log(
266343
- options.tokenEnv === void 0 ? `Created provider "${name}" (${options.baseUrl}, fixed env credential).` : `Created provider "${name}" (${options.baseUrl}, token from ${options.tokenEnv}).`
266344
- );
266394
+ const source = options.tokenEnv !== void 0 ? `token from ${options.tokenEnv}` : options.tokenCommand !== void 0 ? `token from ${options.tokenCommand[0]}` : "fixed env credential";
266395
+ console.log(`Created provider "${name}" (${options.baseUrl}, ${source}).`);
266345
266396
  });
266346
266397
  provider.command("list").description("List every API provider.").action(() => {
266347
266398
  const entries = listProviders(paths);
@@ -266394,6 +266445,7 @@ function parseLauncherArgv(argv) {
266394
266445
  const remaining = hasIdentity ? argv.slice(1) : argv;
266395
266446
  let configProfile;
266396
266447
  let provider;
266448
+ let headroom;
266397
266449
  const categoryFlags = [];
266398
266450
  const shareFlags = [];
266399
266451
  const hideFlags = [];
@@ -266403,6 +266455,10 @@ function parseLauncherArgv(argv) {
266403
266455
  if (token === void 0) {
266404
266456
  continue;
266405
266457
  }
266458
+ if (token === "--headroom" || token === "--no-headroom") {
266459
+ headroom = token === "--headroom";
266460
+ continue;
266461
+ }
266406
266462
  const matched = matchValuedFlag(token);
266407
266463
  if (matched === void 0) {
266408
266464
  rest.push(token);
@@ -266436,6 +266492,7 @@ function parseLauncherArgv(argv) {
266436
266492
  ...hasIdentity ? { identity: first.slice(1) } : {},
266437
266493
  ...configProfile === void 0 ? {} : { configProfile },
266438
266494
  ...provider === void 0 ? {} : { provider },
266495
+ ...headroom === void 0 ? {} : { headroom },
266439
266496
  categoryFlags,
266440
266497
  shareFlags,
266441
266498
  hideFlags,
@@ -266557,7 +266614,8 @@ function resolveLaunchFlags(params) {
266557
266614
  return {
266558
266615
  skipPermissions: params.cascade?.skipPermissions === true || isEnvFlagSet(params.env.CLAUDE_USE_SKIP_PERMISSIONS),
266559
266616
  remoteControl: params.cascade?.remoteControl === true || isEnvFlagSet(params.env.CLAUDE_USE_REMOTE_CONTROL),
266560
- headroom: params.cascade?.headroom === true || isEnvFlagSet(params.env.CLAUDE_USE_HEADROOM)
266617
+ // Precedence: an explicit --headroom/--no-headroom flag decides outright, then the CLAUDE_USE_HEADROOM escape hatch, then the cascade. The flag outranks the environment variable because it is the more deliberate of the two one-off forms (typed on this very command line, not inherited from a shell profile).
266618
+ headroom: params.headroomFlag ?? (isEnvFlagSet(params.env.CLAUDE_USE_HEADROOM) || params.cascade?.headroom === true)
266561
266619
  };
266562
266620
  }
266563
266621
  function buildFlagArgs(flags) {
@@ -266590,6 +266648,10 @@ function buildEnv(params) {
266590
266648
  for (const [key, value] of Object.entries(providerEnv)) {
266591
266649
  env[key] = value;
266592
266650
  }
266651
+ if (params.provider.definition.authScheme === "apiKey") {
266652
+ env.ANTHROPIC_API_KEY = params.provider.token;
266653
+ env.ANTHROPIC_AUTH_TOKEN = "";
266654
+ }
266593
266655
  }
266594
266656
  if (params.headroom !== void 0) {
266595
266657
  env.HEADROOM_PROXY_URL = `http://127.0.0.1:${String(params.headroom.port)}`;
@@ -266718,6 +266780,7 @@ function runLauncher(params) {
266718
266780
  paths,
266719
266781
  port: fs12,
266720
266782
  env,
266783
+ ...params.run === void 0 ? {} : { run: params.run },
266721
266784
  ...parsedArgv.provider === void 0 ? {} : { cliProvider: parsedArgv.provider },
266722
266785
  ...farmContext === void 0 ? {} : { cascade: farmContext.cascade }
266723
266786
  });
@@ -266784,7 +266847,11 @@ function runLauncher(params) {
266784
266847
  cascadeLaunch = flattenLayers(assembleCascade(params.farm.loadCascade(configProfileDecision.name, cliOverride)).layers, { home: params.farm.home }).launch;
266785
266848
  }
266786
266849
  const discovered = params.resolveClaudeBinary();
266787
- const resolvedFlags = resolveLaunchFlags({ env, ...cascadeLaunch === void 0 ? {} : { cascade: cascadeLaunch } });
266850
+ const resolvedFlags = resolveLaunchFlags({
266851
+ env,
266852
+ ...cascadeLaunch === void 0 ? {} : { cascade: cascadeLaunch },
266853
+ ...parsedArgv.headroom === void 0 ? {} : { headroomFlag: parsedArgv.headroom }
266854
+ });
266788
266855
  let headroom;
266789
266856
  const headroomPort = params.headroom;
266790
266857
  if (resolvedFlags.headroom && headroomPort !== void 0) {
@@ -266926,6 +266993,7 @@ async function runClaude(argvOverride) {
266926
266993
  resolveClaudeBinary: realResolveClaudeBinary(resolveOwnInstallDirs(paths, realOwnExecutablePath())),
266927
266994
  farm: farm.runtime,
266928
266995
  headroom: realHeadroomPort(paths),
266996
+ run: realRunPort,
266929
266997
  ...farm.directoryIdentity === void 0 ? {} : { directoryPinnedIdentity: farm.directoryIdentity },
266930
266998
  ...farm.directoryConfigProfile === void 0 ? {} : { directoryRuleConfigProfile: farm.directoryConfigProfile },
266931
266999
  ...farm.globalDefaultConfigProfile === void 0 ? {} : { globalDefaultConfigProfile: farm.globalDefaultConfigProfile }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-use",
3
- "version": "2.7.0",
3
+ "version": "2.9.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": "Apache-2.0",
6
6
  "author": "Joseph Mearman <joseph@mearman.co.uk>",