@hasna/accounts 0.1.10 → 0.1.12

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/cli.js CHANGED
@@ -992,7 +992,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
992
992
  this._exitCallback = (err) => {
993
993
  if (err.code !== "commander.executeSubCommandAsync") {
994
994
  throw err;
995
- }
995
+ } else {}
996
996
  };
997
997
  }
998
998
  return this;
@@ -12259,7 +12259,7 @@ var require_es5 = __commonJS((exports, module) => {
12259
12259
 
12260
12260
  // node_modules/@aws-sdk/core/dist-cjs/submodules/client/index.js
12261
12261
  var require_client2 = __commonJS((exports) => {
12262
- var __dirname = "/tmp/hasna-events-refresh-1781608411540/open-accounts/node_modules/@aws-sdk/core/dist-cjs/submodules/client";
12262
+ var __dirname = "/home/hasna/workspace/hasna/opensource/open-accounts/node_modules/@aws-sdk/core/dist-cjs/submodules/client";
12263
12263
  var retry = require_retry();
12264
12264
  var protocols = require_protocols();
12265
12265
  var lambdaInvokeStore = require_invoke_store();
@@ -42778,6 +42778,15 @@ function writeClaudeKeychain(cred) {
42778
42778
  }
42779
42779
 
42780
42780
  // src/lib/claude-auth.ts
42781
+ var CLAUDE_API_AUTH_ENV_KEYS = [
42782
+ "ANTHROPIC_API_KEY",
42783
+ "ANTHROPIC_AUTH_TOKEN",
42784
+ "ANTHROPIC_BASE_URL",
42785
+ "CLAUDE_CODE_API_KEY_HELPER",
42786
+ "CLAUDE_CODE_API_KEY_HELPER_TTL_MS",
42787
+ "CLAUDE_CODE_USE_BEDROCK",
42788
+ "CLAUDE_CODE_USE_VERTEX"
42789
+ ];
42781
42790
  function readJsonFile(path) {
42782
42791
  if (!existsSync6(path))
42783
42792
  return;
@@ -42796,6 +42805,11 @@ function writeJsonFile(path, data, stayUnder) {
42796
42805
  function readOAuthFromPaths(paths) {
42797
42806
  return findOAuthSource(paths)?.oauth;
42798
42807
  }
42808
+ function readOAuthSnapshot(profileDir) {
42809
+ const snap = readJsonFile(profileOAuthSnapshot(profileDir));
42810
+ const oauth = snap?.oauthAccount;
42811
+ return oauth && typeof oauth === "object" ? oauth : undefined;
42812
+ }
42799
42813
  function findOAuthSource(paths) {
42800
42814
  for (const p of paths) {
42801
42815
  const data = readJsonFile(p);
@@ -42837,6 +42851,46 @@ function mergeOAuthInto(paths, oauth, allowDelete, stayUnder) {
42837
42851
  }
42838
42852
  }
42839
42853
  }
42854
+ function sanitizeSettingsFile(configDir, stayUnder) {
42855
+ const settingsPath = join7(configDir, "settings.json");
42856
+ const settings = readJsonFile(settingsPath);
42857
+ if (!settings)
42858
+ return false;
42859
+ let changed = false;
42860
+ if ("apiKeyHelper" in settings) {
42861
+ delete settings.apiKeyHelper;
42862
+ changed = true;
42863
+ }
42864
+ const env2 = settings.env;
42865
+ if (env2 && typeof env2 === "object" && !Array.isArray(env2)) {
42866
+ const envRecord = env2;
42867
+ for (const key of CLAUDE_API_AUTH_ENV_KEYS) {
42868
+ if (key in envRecord) {
42869
+ delete envRecord[key];
42870
+ changed = true;
42871
+ }
42872
+ }
42873
+ }
42874
+ if (changed)
42875
+ writeJsonFile(settingsPath, settings, stayUnder);
42876
+ return changed;
42877
+ }
42878
+ function sanitizeClaudeProfileApiSettings(profileDir, tool) {
42879
+ if (tool.id !== "claude")
42880
+ return false;
42881
+ return sanitizeSettingsFile(profileDir, profileDir);
42882
+ }
42883
+ function sanitizeClaudeOAuthProfileSettings(profileDir, tool) {
42884
+ if (tool.id !== "claude")
42885
+ return false;
42886
+ if (!readOAuthSnapshot(profileDir) && !readOAuthFromPaths(profileAccountJsonPaths(profileDir, tool))) {
42887
+ return false;
42888
+ }
42889
+ return sanitizeClaudeProfileApiSettings(profileDir, tool);
42890
+ }
42891
+ function sanitizeLiveClaudeOAuthSettings() {
42892
+ return sanitizeSettingsFile(liveClaudePaths().configDir, liveClaudeBase());
42893
+ }
42840
42894
  function liveOAuthEmail() {
42841
42895
  const live = liveClaudePaths();
42842
42896
  const oauth = readOAuthFromPaths([live.homeJson]);
@@ -42877,6 +42931,7 @@ function ensureProfileAuthSnapshot(profileDir, tool, opts = {}) {
42877
42931
  assertSafeWritePath(credSnap, { mustStayUnder: profileDir });
42878
42932
  copyFileSync(credFile, credSnap);
42879
42933
  }
42934
+ sanitizeClaudeOAuthProfileSettings(profileDir, tool);
42880
42935
  }
42881
42936
  function profileHasAuth(profileDir, tool) {
42882
42937
  return hasAuthSnapshot(profileDir) || !!readOAuthFromPaths(profileAccountJsonPaths(profileDir, tool));
@@ -42895,6 +42950,8 @@ function restoreClaudeAuthFromProfile(profileDir, tool, profileName) {
42895
42950
  if (!oauth) {
42896
42951
  throw new AccountsError("profile has no OAuth account data to apply");
42897
42952
  }
42953
+ sanitizeClaudeOAuthProfileSettings(profileDir, tool);
42954
+ sanitizeLiveClaudeOAuthSettings();
42898
42955
  assertSafeWritePath(live.homeJson, { mustStayUnder: liveRoot });
42899
42956
  mergeOAuthInto([live.homeJson], oauth, false, liveRoot);
42900
42957
  const credSnap = profileCredentialsSnapshot(profileDir);
@@ -43345,6 +43402,11 @@ function profileEnv(profile, tool) {
43345
43402
  for (const [name, value] of Object.entries(tool.extraEnv ?? {})) {
43346
43403
  env2[name] = renderTemplate(value, profile);
43347
43404
  }
43405
+ if (tool.id === "claude") {
43406
+ sanitizeClaudeProfileApiSettings(profile.dir, tool);
43407
+ for (const key of CLAUDE_API_AUTH_ENV_KEYS)
43408
+ env2[key] = "";
43409
+ }
43348
43410
  return env2;
43349
43411
  }
43350
43412
  function formatEnvAssignments(env2) {
package/dist/index.d.ts CHANGED
@@ -17,7 +17,7 @@ export type { RunSupervisorOptions, SupervisorClientOptions, SupervisorLaunchPla
17
17
  export { pickProfile } from "./lib/pick.js";
18
18
  export type { PickOptions, PickResult } from "./lib/pick.js";
19
19
  export { installHook, uninstallHook, hookPath, hookScript, shellSnippet } from "./lib/hook.js";
20
- export { snapshotClaudeAuthToProfile, snapshotLiveAuthToProfile, restoreClaudeAuthFromProfile, ensureProfileAuthSnapshot, hasAuthSnapshot, profileHasAuth, } from "./lib/claude-auth.js";
20
+ export { snapshotClaudeAuthToProfile, snapshotLiveAuthToProfile, restoreClaudeAuthFromProfile, ensureProfileAuthSnapshot, hasAuthSnapshot, profileHasAuth, sanitizeClaudeProfileApiSettings, sanitizeClaudeOAuthProfileSettings, sanitizeLiveClaudeOAuthSettings, CLAUDE_API_AUTH_ENV_KEYS, } from "./lib/claude-auth.js";
21
21
  export { withApplyLock } from "./lib/apply-lock.js";
22
22
  export { isSafeProfileName } from "./lib/hook.js";
23
23
  export { readClaudeKeychain, keychainSupported } from "./lib/keychain.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC1F,OAAO,EACL,aAAa,EACb,YAAY,EACZ,OAAO,EACP,SAAS,EACT,aAAa,EACb,aAAa,EACb,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACnF,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EACL,UAAU,EACV,YAAY,EACZ,WAAW,EACX,UAAU,EACV,UAAU,EACV,aAAa,EACb,aAAa,EACb,aAAa,EACb,aAAa,EACb,UAAU,EACV,cAAc,GACf,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAC/E,YAAY,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,uBAAuB,EACvB,iBAAiB,EACjB,qBAAqB,EACrB,aAAa,EACb,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,oBAAoB,EACpB,uBAAuB,EACvB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC/F,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EACzB,4BAA4B,EAC5B,yBAAyB,EACzB,eAAe,EACf,cAAc,GACf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC1F,OAAO,EACL,aAAa,EACb,YAAY,EACZ,OAAO,EACP,SAAS,EACT,aAAa,EACb,aAAa,EACb,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACnF,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EACL,UAAU,EACV,YAAY,EACZ,WAAW,EACX,UAAU,EACV,UAAU,EACV,aAAa,EACb,aAAa,EACb,aAAa,EACb,aAAa,EACb,UAAU,EACV,cAAc,GACf,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAC/E,YAAY,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,uBAAuB,EACvB,iBAAiB,EACjB,qBAAqB,EACrB,aAAa,EACb,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,oBAAoB,EACpB,uBAAuB,EACvB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC/F,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EACzB,4BAA4B,EAC5B,yBAAyB,EACzB,eAAe,EACf,cAAc,EACd,gCAAgC,EAChC,kCAAkC,EAClC,+BAA+B,EAC/B,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -4317,256 +4317,13 @@ function removeCustomTool(id) {
4317
4317
  store.tools.splice(idx, 1);
4318
4318
  saveStore(store);
4319
4319
  }
4320
- // src/lib/env.ts
4321
- function renderTemplate(value, profile) {
4322
- return value.replaceAll("{profileDir}", profile.dir).replaceAll("{profileName}", profile.name).replaceAll("{toolId}", profile.tool);
4323
- }
4324
- function profileEnv(profile, tool) {
4325
- const env = {
4326
- [tool.envVar]: profile.dir
4327
- };
4328
- for (const [name, value] of Object.entries(tool.extraEnv ?? {})) {
4329
- env[name] = renderTemplate(value, profile);
4330
- }
4331
- return env;
4332
- }
4333
- function formatEnvAssignments(env) {
4334
- return Object.entries(env).map(([name, value]) => `${name}=${JSON.stringify(value)}`).join(" ");
4335
- }
4336
- function formatExportLines(env) {
4337
- return Object.entries(env).map(([name, value]) => `export ${name}=${JSON.stringify(value)}`).join(`
4338
- `);
4339
- }
4340
- // src/lib/detect.ts
4341
- import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
4342
- import { dirname as dirname2, join as join3 } from "node:path";
4343
- function detectEmail(dir, tool) {
4344
- if (!tool.accountFile || !tool.emailPath)
4345
- return;
4346
- const candidates = [join3(dir, tool.accountFile)];
4347
- if (dir === tool.defaultDir)
4348
- candidates.push(join3(dirname2(dir), tool.accountFile));
4349
- for (const file of candidates) {
4350
- const email = readEmail(file, tool.emailPath);
4351
- if (email)
4352
- return email;
4353
- }
4354
- return;
4355
- }
4356
- function readEmail(file, path) {
4357
- if (!existsSync3(file))
4358
- return;
4359
- let cursor;
4360
- try {
4361
- cursor = JSON.parse(readFileSync2(file, "utf8"));
4362
- } catch {
4363
- return;
4364
- }
4365
- for (const key of path) {
4366
- if (cursor && typeof cursor === "object" && key in cursor) {
4367
- cursor = cursor[key];
4368
- } else {
4369
- return;
4370
- }
4371
- }
4372
- return typeof cursor === "string" && cursor.includes("@") ? cursor : undefined;
4373
- }
4374
- // src/lib/profiles.ts
4375
- import { homedir as homedir3 } from "node:os";
4376
- import { isAbsolute, join as join4, resolve as resolve2 } from "node:path";
4377
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, rmSync } from "node:fs";
4378
- function nowIso() {
4379
- return new Date().toISOString();
4380
- }
4381
- function expandPath(p) {
4382
- let out = p;
4383
- if (out === "~")
4384
- out = homedir3();
4385
- else if (out.startsWith("~/"))
4386
- out = join4(homedir3(), out.slice(2));
4387
- return isAbsolute(out) ? out : resolve2(process.cwd(), out);
4388
- }
4389
- function listProfiles(toolId) {
4390
- const profiles = loadStore().profiles;
4391
- const filtered = toolId ? profiles.filter((p) => p.tool === toolId) : profiles;
4392
- return filtered.slice().sort((a, b) => a.tool.localeCompare(b.tool) || a.name.localeCompare(b.name));
4393
- }
4394
- function profileMatches(name, toolId) {
4395
- return loadStore().profiles.filter((p) => p.name === name && (!toolId || p.tool === toolId));
4396
- }
4397
- function findProfile(name, toolId) {
4398
- const matches = profileMatches(name, toolId);
4399
- return matches.length === 1 ? matches[0] : undefined;
4400
- }
4401
- function getProfile(name, toolId) {
4402
- const matches = profileMatches(name, toolId);
4403
- if (matches.length === 0) {
4404
- const suffix = toolId ? ` for tool "${toolId}"` : "";
4405
- throw new AccountsError(`no profile named "${name}"${suffix}. Run \`accounts list\` to see profiles.`);
4406
- }
4407
- if (matches.length > 1) {
4408
- throw new AccountsError(`profile "${name}" exists for multiple tools (${matches.map((p) => p.tool).join(", ")}); pass --tool`);
4409
- }
4410
- const profile = matches[0];
4411
- return profile;
4412
- }
4413
- function addProfile(opts) {
4414
- const name = opts.name;
4415
- const nameCheck = profileNameSchema.safeParse(name);
4416
- if (!nameCheck.success)
4417
- throw new AccountsError(nameCheck.error.issues[0]?.message ?? "invalid profile name");
4418
- const toolId = opts.tool ?? DEFAULT_TOOL;
4419
- const tool = getTool(toolId);
4420
- const store = loadStore();
4421
- if (store.profiles.some((p) => p.name === name && p.tool === toolId)) {
4422
- throw new AccountsError(`a ${toolId} profile named "${name}" already exists`);
4423
- }
4424
- const dir = opts.dir ? expandPath(opts.dir) : join4(profilesDir(), toolId, name);
4425
- if (store.profiles.some((p) => p.dir === dir)) {
4426
- throw new AccountsError(`a profile already uses config dir ${dir}`);
4427
- }
4428
- mkdirSync3(dir, { recursive: true });
4429
- const email = opts.email ?? detectEmail(dir, tool);
4430
- const profile = {
4431
- name,
4432
- tool: toolId,
4433
- ...email ? { email } : {},
4434
- dir,
4435
- ...opts.description ? { description: opts.description } : {},
4436
- createdAt: nowIso()
4437
- };
4438
- store.profiles.push(profile);
4439
- saveStore(store);
4440
- return profile;
4441
- }
4442
- function removeProfile(name, opts = {}) {
4443
- const options = typeof opts === "boolean" ? { purge: opts } : opts;
4444
- const store = loadStore();
4445
- const matches = store.profiles.map((profile2, idx2) => ({ profile: profile2, idx: idx2 })).filter(({ profile: profile2 }) => profile2.name === name && (!options.tool || profile2.tool === options.tool));
4446
- if (matches.length === 0) {
4447
- const suffix = options.tool ? ` for tool "${options.tool}"` : "";
4448
- throw new AccountsError(`no profile named "${name}"${suffix}`);
4449
- }
4450
- if (matches.length > 1) {
4451
- throw new AccountsError(`profile "${name}" exists for multiple tools (${matches.map(({ profile: profile2 }) => profile2.tool).join(", ")}); pass --tool`);
4452
- }
4453
- const idx = matches[0].idx;
4454
- const profile = store.profiles[idx];
4455
- store.profiles.splice(idx, 1);
4456
- if (store.current[profile.tool] === name)
4457
- delete store.current[profile.tool];
4458
- if (store.applied[profile.tool] === name)
4459
- delete store.applied[profile.tool];
4460
- saveStore(store);
4461
- let purged = false;
4462
- let purgeNote;
4463
- if (options.purge) {
4464
- const managed = profile.dir.startsWith(profilesDir());
4465
- const isDefault = profile.dir === getTool(profile.tool).defaultDir;
4466
- if (managed && !isDefault && existsSync4(profile.dir)) {
4467
- rmSync(profile.dir, { recursive: true, force: true });
4468
- purged = true;
4469
- } else {
4470
- purgeNote = `refused to delete ${profile.dir} (not a managed profile dir); remove it manually if intended`;
4471
- }
4472
- }
4473
- return { profile, purged, purgeNote };
4474
- }
4475
- function renameProfile(oldName, newName, toolId) {
4476
- const nameCheck = profileNameSchema.safeParse(newName);
4477
- if (!nameCheck.success)
4478
- throw new AccountsError(nameCheck.error.issues[0]?.message ?? "invalid profile name");
4479
- const store = loadStore();
4480
- const matches = store.profiles.filter((p) => p.name === oldName && (!toolId || p.tool === toolId));
4481
- if (matches.length === 0) {
4482
- const suffix = toolId ? ` for tool "${toolId}"` : "";
4483
- throw new AccountsError(`no profile named "${oldName}"${suffix}`);
4484
- }
4485
- if (matches.length > 1) {
4486
- throw new AccountsError(`profile "${oldName}" exists for multiple tools (${matches.map((p) => p.tool).join(", ")}); pass --tool`);
4487
- }
4488
- const profile = matches[0];
4489
- if (store.profiles.some((p) => p.name === newName && p.tool === profile.tool)) {
4490
- throw new AccountsError(`a ${profile.tool} profile named "${newName}" already exists`);
4491
- }
4492
- if (store.current[profile.tool] === oldName)
4493
- store.current[profile.tool] = newName;
4494
- if (store.applied[profile.tool] === oldName)
4495
- store.applied[profile.tool] = newName;
4496
- profile.name = newName;
4497
- saveStore(store);
4498
- return profile;
4499
- }
4500
- function updateProfile(name, opts) {
4501
- const store = loadStore();
4502
- const matches = store.profiles.filter((p) => p.name === name && (!opts.tool || p.tool === opts.tool));
4503
- if (matches.length === 0) {
4504
- const suffix = opts.tool ? ` for tool "${opts.tool}"` : "";
4505
- throw new AccountsError(`no profile named "${name}"${suffix}`);
4506
- }
4507
- if (matches.length > 1) {
4508
- throw new AccountsError(`profile "${name}" exists for multiple tools (${matches.map((p) => p.tool).join(", ")}); pass --tool`);
4509
- }
4510
- const profile = matches[0];
4511
- if (opts.email !== undefined)
4512
- profile.email = opts.email;
4513
- if (opts.description !== undefined)
4514
- profile.description = opts.description;
4515
- if (opts.dir !== undefined) {
4516
- const dir = expandPath(opts.dir);
4517
- mkdirSync3(dir, { recursive: true });
4518
- profile.dir = dir;
4519
- }
4520
- saveStore(store);
4521
- return profile;
4522
- }
4523
- function redetectEmail(name, toolId) {
4524
- const store = loadStore();
4525
- const matches = store.profiles.filter((p) => p.name === name && (!toolId || p.tool === toolId));
4526
- if (matches.length === 0) {
4527
- const suffix = toolId ? ` for tool "${toolId}"` : "";
4528
- throw new AccountsError(`no profile named "${name}"${suffix}`);
4529
- }
4530
- if (matches.length > 1) {
4531
- throw new AccountsError(`profile "${name}" exists for multiple tools (${matches.map((p) => p.tool).join(", ")}); pass --tool`);
4532
- }
4533
- const profile = matches[0];
4534
- const email = detectEmail(profile.dir, getTool(profile.tool));
4535
- if (email)
4536
- profile.email = email;
4537
- saveStore(store);
4538
- return profile;
4539
- }
4540
- function useProfile(name, toolId) {
4541
- const store = loadStore();
4542
- const matches = store.profiles.filter((p) => p.name === name && (!toolId || p.tool === toolId));
4543
- if (matches.length === 0) {
4544
- const suffix = toolId ? ` for tool "${toolId}"` : "";
4545
- throw new AccountsError(`no profile named "${name}"${suffix}`);
4546
- }
4547
- if (matches.length > 1) {
4548
- throw new AccountsError(`profile "${name}" exists for multiple tools (${matches.map((p) => p.tool).join(", ")}); pass --tool`);
4549
- }
4550
- const profile = matches[0];
4551
- store.current[profile.tool] = name;
4552
- profile.lastUsedAt = nowIso();
4553
- saveStore(store);
4554
- return { profile, toolId: profile.tool };
4555
- }
4556
- function currentProfile(toolId) {
4557
- const store = loadStore();
4558
- const name = store.current[toolId];
4559
- if (!name)
4560
- return;
4561
- return store.profiles.find((p) => p.name === name);
4562
- }
4563
4320
  // src/lib/claude-auth.ts
4564
- import { copyFileSync, existsSync as existsSync5, lstatSync as lstatSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync3, statSync, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
4565
- import { dirname as dirname4, join as join6 } from "node:path";
4321
+ import { copyFileSync, existsSync as existsSync3, lstatSync as lstatSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync2, statSync, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
4322
+ import { dirname as dirname3, join as join4 } from "node:path";
4566
4323
 
4567
4324
  // src/lib/claude-layout.ts
4568
- import { homedir as homedir4 } from "node:os";
4569
- import { dirname as dirname3, join as join5 } from "node:path";
4325
+ import { homedir as homedir3 } from "node:os";
4326
+ import { dirname as dirname2, join as join3 } from "node:path";
4570
4327
  var CLAUDE_KEYCHAIN_SERVICE = "Claude Code-credentials";
4571
4328
  var ACCOUNTS_AUTH_DIR = ".accounts-auth";
4572
4329
  var OAUTH_SNAPSHOT = "oauth-account.json";
@@ -4574,36 +4331,36 @@ var CREDENTIALS_SNAPSHOT = "credentials.json";
4574
4331
  var KEYCHAIN_SNAPSHOT = "keychain.json";
4575
4332
  function liveClaudeBase() {
4576
4333
  const testBase = process.env.ACCOUNTS_TEST_LIVE_DIR;
4577
- return testBase && testBase.trim() ? testBase : homedir4();
4334
+ return testBase && testBase.trim() ? testBase : homedir3();
4578
4335
  }
4579
4336
  function liveClaudePaths() {
4580
4337
  const base = liveClaudeBase();
4581
- const configDir = join5(base, ".claude");
4338
+ const configDir = join3(base, ".claude");
4582
4339
  return {
4583
4340
  configDir,
4584
- homeJson: join5(base, ".claude.json"),
4585
- credentialsFile: join5(configDir, ".credentials.json")
4341
+ homeJson: join3(base, ".claude.json"),
4342
+ credentialsFile: join3(configDir, ".credentials.json")
4586
4343
  };
4587
4344
  }
4588
4345
  function profileAccountJsonPaths(profileDir, tool) {
4589
4346
  if (!tool.accountFile)
4590
4347
  return [];
4591
- const paths = [join5(profileDir, tool.accountFile)];
4348
+ const paths = [join3(profileDir, tool.accountFile)];
4592
4349
  if (profileDir === tool.defaultDir)
4593
- paths.push(join5(dirname3(profileDir), tool.accountFile));
4350
+ paths.push(join3(dirname2(profileDir), tool.accountFile));
4594
4351
  return paths;
4595
4352
  }
4596
4353
  function profileAuthDir(profileDir) {
4597
- return join5(profileDir, ACCOUNTS_AUTH_DIR);
4354
+ return join3(profileDir, ACCOUNTS_AUTH_DIR);
4598
4355
  }
4599
4356
  function profileOAuthSnapshot(profileDir) {
4600
- return join5(profileAuthDir(profileDir), OAUTH_SNAPSHOT);
4357
+ return join3(profileAuthDir(profileDir), OAUTH_SNAPSHOT);
4601
4358
  }
4602
4359
  function profileCredentialsSnapshot(profileDir) {
4603
- return join5(profileAuthDir(profileDir), CREDENTIALS_SNAPSHOT);
4360
+ return join3(profileAuthDir(profileDir), CREDENTIALS_SNAPSHOT);
4604
4361
  }
4605
4362
  function profileKeychainSnapshot(profileDir) {
4606
- return join5(profileAuthDir(profileDir), KEYCHAIN_SNAPSHOT);
4363
+ return join3(profileAuthDir(profileDir), KEYCHAIN_SNAPSHOT);
4607
4364
  }
4608
4365
 
4609
4366
  // src/lib/keychain.ts
@@ -4667,24 +4424,38 @@ function writeClaudeKeychain(cred) {
4667
4424
  }
4668
4425
 
4669
4426
  // src/lib/claude-auth.ts
4427
+ var CLAUDE_API_AUTH_ENV_KEYS = [
4428
+ "ANTHROPIC_API_KEY",
4429
+ "ANTHROPIC_AUTH_TOKEN",
4430
+ "ANTHROPIC_BASE_URL",
4431
+ "CLAUDE_CODE_API_KEY_HELPER",
4432
+ "CLAUDE_CODE_API_KEY_HELPER_TTL_MS",
4433
+ "CLAUDE_CODE_USE_BEDROCK",
4434
+ "CLAUDE_CODE_USE_VERTEX"
4435
+ ];
4670
4436
  function readJsonFile(path) {
4671
- if (!existsSync5(path))
4437
+ if (!existsSync3(path))
4672
4438
  return;
4673
4439
  try {
4674
- return JSON.parse(readFileSync3(path, "utf8"));
4440
+ return JSON.parse(readFileSync2(path, "utf8"));
4675
4441
  } catch {
4676
4442
  return;
4677
4443
  }
4678
4444
  }
4679
4445
  function writeJsonFile(path, data, stayUnder) {
4680
4446
  assertSafeWritePath(path, stayUnder ? { mustStayUnder: stayUnder } : undefined);
4681
- mkdirSync4(dirname4(path), { recursive: true });
4447
+ mkdirSync3(dirname3(path), { recursive: true });
4682
4448
  writeFileSync2(path, JSON.stringify(data, null, 2) + `
4683
4449
  `, { mode: 384 });
4684
4450
  }
4685
4451
  function readOAuthFromPaths(paths) {
4686
4452
  return findOAuthSource(paths)?.oauth;
4687
4453
  }
4454
+ function readOAuthSnapshot(profileDir) {
4455
+ const snap = readJsonFile(profileOAuthSnapshot(profileDir));
4456
+ const oauth = snap?.oauthAccount;
4457
+ return oauth && typeof oauth === "object" ? oauth : undefined;
4458
+ }
4688
4459
  function findOAuthSource(paths) {
4689
4460
  for (const p of paths) {
4690
4461
  const data = readJsonFile(p);
@@ -4695,7 +4466,7 @@ function findOAuthSource(paths) {
4695
4466
  return;
4696
4467
  }
4697
4468
  function snapshotIsStale(sourcePath, snapshotPath) {
4698
- if (!existsSync5(snapshotPath))
4469
+ if (!existsSync3(snapshotPath))
4699
4470
  return true;
4700
4471
  try {
4701
4472
  return statSync(sourcePath).mtimeMs > statSync(snapshotPath).mtimeMs;
@@ -4726,6 +4497,46 @@ function mergeOAuthInto(paths, oauth, allowDelete, stayUnder) {
4726
4497
  }
4727
4498
  }
4728
4499
  }
4500
+ function sanitizeSettingsFile(configDir, stayUnder) {
4501
+ const settingsPath = join4(configDir, "settings.json");
4502
+ const settings = readJsonFile(settingsPath);
4503
+ if (!settings)
4504
+ return false;
4505
+ let changed = false;
4506
+ if ("apiKeyHelper" in settings) {
4507
+ delete settings.apiKeyHelper;
4508
+ changed = true;
4509
+ }
4510
+ const env = settings.env;
4511
+ if (env && typeof env === "object" && !Array.isArray(env)) {
4512
+ const envRecord = env;
4513
+ for (const key of CLAUDE_API_AUTH_ENV_KEYS) {
4514
+ if (key in envRecord) {
4515
+ delete envRecord[key];
4516
+ changed = true;
4517
+ }
4518
+ }
4519
+ }
4520
+ if (changed)
4521
+ writeJsonFile(settingsPath, settings, stayUnder);
4522
+ return changed;
4523
+ }
4524
+ function sanitizeClaudeProfileApiSettings(profileDir, tool) {
4525
+ if (tool.id !== "claude")
4526
+ return false;
4527
+ return sanitizeSettingsFile(profileDir, profileDir);
4528
+ }
4529
+ function sanitizeClaudeOAuthProfileSettings(profileDir, tool) {
4530
+ if (tool.id !== "claude")
4531
+ return false;
4532
+ if (!readOAuthSnapshot(profileDir) && !readOAuthFromPaths(profileAccountJsonPaths(profileDir, tool))) {
4533
+ return false;
4534
+ }
4535
+ return sanitizeClaudeProfileApiSettings(profileDir, tool);
4536
+ }
4537
+ function sanitizeLiveClaudeOAuthSettings() {
4538
+ return sanitizeSettingsFile(liveClaudePaths().configDir, liveClaudeBase());
4539
+ }
4729
4540
  function liveOAuthEmail() {
4730
4541
  const live = liveClaudePaths();
4731
4542
  const oauth = readOAuthFromPaths([live.homeJson]);
@@ -4734,13 +4545,13 @@ function liveOAuthEmail() {
4734
4545
  }
4735
4546
  function snapshotLiveAuthToProfile(profileDir, _tool) {
4736
4547
  const authDir = profileAuthDir(profileDir);
4737
- assertSafeWritePath(join6(authDir, OAUTH_SNAPSHOT), { mustStayUnder: profileDir });
4738
- mkdirSync4(authDir, { recursive: true });
4548
+ assertSafeWritePath(join4(authDir, OAUTH_SNAPSHOT), { mustStayUnder: profileDir });
4549
+ mkdirSync3(authDir, { recursive: true });
4739
4550
  const live = liveClaudePaths();
4740
4551
  const oauth = readOAuthFromPaths([live.homeJson]);
4741
4552
  if (oauth)
4742
4553
  writeJsonFile(profileOAuthSnapshot(profileDir), { oauthAccount: oauth }, profileDir);
4743
- if (existsSync5(live.credentialsFile)) {
4554
+ if (existsSync3(live.credentialsFile)) {
4744
4555
  const dest = profileCredentialsSnapshot(profileDir);
4745
4556
  assertSafeWritePath(dest, { mustStayUnder: profileDir });
4746
4557
  copyFileSync(live.credentialsFile, dest);
@@ -4756,19 +4567,20 @@ function snapshotClaudeAuthToProfile(profileDir, tool) {
4756
4567
  }
4757
4568
  function ensureProfileAuthSnapshot(profileDir, tool, opts = {}) {
4758
4569
  const authDir = profileAuthDir(profileDir);
4759
- assertSafeWritePath(join6(authDir, OAUTH_SNAPSHOT), { mustStayUnder: profileDir });
4760
- mkdirSync4(authDir, { recursive: true });
4570
+ assertSafeWritePath(join4(authDir, OAUTH_SNAPSHOT), { mustStayUnder: profileDir });
4571
+ mkdirSync3(authDir, { recursive: true });
4761
4572
  const oauthSource = findOAuthSource(profileAccountJsonPaths(profileDir, tool));
4762
4573
  const oauthSnap = profileOAuthSnapshot(profileDir);
4763
4574
  if (oauthSource && (opts.overwrite || snapshotIsStale(oauthSource.path, oauthSnap))) {
4764
4575
  writeJsonFile(oauthSnap, { oauthAccount: oauthSource.oauth }, profileDir);
4765
4576
  }
4766
- const credFile = join6(profileDir, ".credentials.json");
4577
+ const credFile = join4(profileDir, ".credentials.json");
4767
4578
  const credSnap = profileCredentialsSnapshot(profileDir);
4768
- if (existsSync5(credFile) && (opts.overwrite || snapshotIsStale(credFile, credSnap))) {
4579
+ if (existsSync3(credFile) && (opts.overwrite || snapshotIsStale(credFile, credSnap))) {
4769
4580
  assertSafeWritePath(credSnap, { mustStayUnder: profileDir });
4770
4581
  copyFileSync(credFile, credSnap);
4771
4582
  }
4583
+ sanitizeClaudeOAuthProfileSettings(profileDir, tool);
4772
4584
  }
4773
4585
  function profileHasAuth(profileDir, tool) {
4774
4586
  return hasAuthSnapshot(profileDir) || !!readOAuthFromPaths(profileAccountJsonPaths(profileDir, tool));
@@ -4781,21 +4593,23 @@ function restoreClaudeAuthFromProfile(profileDir, tool, profileName) {
4781
4593
  ensureProfileAuthSnapshot(profileDir, tool);
4782
4594
  const live = liveClaudePaths();
4783
4595
  const liveRoot = liveClaudeBase();
4784
- mkdirSync4(live.configDir, { recursive: true });
4596
+ mkdirSync3(live.configDir, { recursive: true });
4785
4597
  const oauthSnap = readJsonFile(profileOAuthSnapshot(profileDir));
4786
4598
  const oauth = oauthSnap?.oauthAccount && typeof oauthSnap.oauthAccount === "object" ? oauthSnap.oauthAccount : readOAuthFromPaths(profileAccountJsonPaths(profileDir, tool));
4787
4599
  if (!oauth) {
4788
4600
  throw new AccountsError("profile has no OAuth account data to apply");
4789
4601
  }
4602
+ sanitizeClaudeOAuthProfileSettings(profileDir, tool);
4603
+ sanitizeLiveClaudeOAuthSettings();
4790
4604
  assertSafeWritePath(live.homeJson, { mustStayUnder: liveRoot });
4791
4605
  mergeOAuthInto([live.homeJson], oauth, false, liveRoot);
4792
4606
  const credSnap = profileCredentialsSnapshot(profileDir);
4793
- if (existsSync5(credSnap)) {
4607
+ if (existsSync3(credSnap)) {
4794
4608
  assertSafeWritePath(live.credentialsFile, { mustStayUnder: liveRoot });
4795
4609
  assertSafeWritePath(credSnap, { mustStayUnder: profileDir });
4796
4610
  copyFileSync(credSnap, live.credentialsFile);
4797
- writeFileSync2(live.credentialsFile, readFileSync3(live.credentialsFile), { mode: 384 });
4798
- } else if (existsSync5(live.credentialsFile)) {
4611
+ writeFileSync2(live.credentialsFile, readFileSync2(live.credentialsFile), { mode: 384 });
4612
+ } else if (existsSync3(live.credentialsFile)) {
4799
4613
  if (!lstatSync2(live.credentialsFile).isSymbolicLink())
4800
4614
  unlinkSync(live.credentialsFile);
4801
4615
  }
@@ -4817,9 +4631,257 @@ function restoreClaudeAuthFromProfile(profileDir, tool, profileName) {
4817
4631
  }
4818
4632
  }
4819
4633
  function hasAuthSnapshot(profileDir) {
4820
- return existsSync5(profileOAuthSnapshot(profileDir)) || existsSync5(profileCredentialsSnapshot(profileDir)) || existsSync5(profileKeychainSnapshot(profileDir));
4634
+ return existsSync3(profileOAuthSnapshot(profileDir)) || existsSync3(profileCredentialsSnapshot(profileDir)) || existsSync3(profileKeychainSnapshot(profileDir));
4821
4635
  }
4822
4636
 
4637
+ // src/lib/env.ts
4638
+ function renderTemplate(value, profile) {
4639
+ return value.replaceAll("{profileDir}", profile.dir).replaceAll("{profileName}", profile.name).replaceAll("{toolId}", profile.tool);
4640
+ }
4641
+ function profileEnv(profile, tool) {
4642
+ const env = {
4643
+ [tool.envVar]: profile.dir
4644
+ };
4645
+ for (const [name, value] of Object.entries(tool.extraEnv ?? {})) {
4646
+ env[name] = renderTemplate(value, profile);
4647
+ }
4648
+ if (tool.id === "claude") {
4649
+ sanitizeClaudeProfileApiSettings(profile.dir, tool);
4650
+ for (const key of CLAUDE_API_AUTH_ENV_KEYS)
4651
+ env[key] = "";
4652
+ }
4653
+ return env;
4654
+ }
4655
+ function formatEnvAssignments(env) {
4656
+ return Object.entries(env).map(([name, value]) => `${name}=${JSON.stringify(value)}`).join(" ");
4657
+ }
4658
+ function formatExportLines(env) {
4659
+ return Object.entries(env).map(([name, value]) => `export ${name}=${JSON.stringify(value)}`).join(`
4660
+ `);
4661
+ }
4662
+ // src/lib/detect.ts
4663
+ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "node:fs";
4664
+ import { dirname as dirname4, join as join5 } from "node:path";
4665
+ function detectEmail(dir, tool) {
4666
+ if (!tool.accountFile || !tool.emailPath)
4667
+ return;
4668
+ const candidates = [join5(dir, tool.accountFile)];
4669
+ if (dir === tool.defaultDir)
4670
+ candidates.push(join5(dirname4(dir), tool.accountFile));
4671
+ for (const file of candidates) {
4672
+ const email = readEmail(file, tool.emailPath);
4673
+ if (email)
4674
+ return email;
4675
+ }
4676
+ return;
4677
+ }
4678
+ function readEmail(file, path) {
4679
+ if (!existsSync4(file))
4680
+ return;
4681
+ let cursor;
4682
+ try {
4683
+ cursor = JSON.parse(readFileSync3(file, "utf8"));
4684
+ } catch {
4685
+ return;
4686
+ }
4687
+ for (const key of path) {
4688
+ if (cursor && typeof cursor === "object" && key in cursor) {
4689
+ cursor = cursor[key];
4690
+ } else {
4691
+ return;
4692
+ }
4693
+ }
4694
+ return typeof cursor === "string" && cursor.includes("@") ? cursor : undefined;
4695
+ }
4696
+ // src/lib/profiles.ts
4697
+ import { homedir as homedir4 } from "node:os";
4698
+ import { isAbsolute, join as join6, resolve as resolve2 } from "node:path";
4699
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, rmSync } from "node:fs";
4700
+ function nowIso() {
4701
+ return new Date().toISOString();
4702
+ }
4703
+ function expandPath(p) {
4704
+ let out = p;
4705
+ if (out === "~")
4706
+ out = homedir4();
4707
+ else if (out.startsWith("~/"))
4708
+ out = join6(homedir4(), out.slice(2));
4709
+ return isAbsolute(out) ? out : resolve2(process.cwd(), out);
4710
+ }
4711
+ function listProfiles(toolId) {
4712
+ const profiles = loadStore().profiles;
4713
+ const filtered = toolId ? profiles.filter((p) => p.tool === toolId) : profiles;
4714
+ return filtered.slice().sort((a, b) => a.tool.localeCompare(b.tool) || a.name.localeCompare(b.name));
4715
+ }
4716
+ function profileMatches(name, toolId) {
4717
+ return loadStore().profiles.filter((p) => p.name === name && (!toolId || p.tool === toolId));
4718
+ }
4719
+ function findProfile(name, toolId) {
4720
+ const matches = profileMatches(name, toolId);
4721
+ return matches.length === 1 ? matches[0] : undefined;
4722
+ }
4723
+ function getProfile(name, toolId) {
4724
+ const matches = profileMatches(name, toolId);
4725
+ if (matches.length === 0) {
4726
+ const suffix = toolId ? ` for tool "${toolId}"` : "";
4727
+ throw new AccountsError(`no profile named "${name}"${suffix}. Run \`accounts list\` to see profiles.`);
4728
+ }
4729
+ if (matches.length > 1) {
4730
+ throw new AccountsError(`profile "${name}" exists for multiple tools (${matches.map((p) => p.tool).join(", ")}); pass --tool`);
4731
+ }
4732
+ const profile = matches[0];
4733
+ return profile;
4734
+ }
4735
+ function addProfile(opts) {
4736
+ const name = opts.name;
4737
+ const nameCheck = profileNameSchema.safeParse(name);
4738
+ if (!nameCheck.success)
4739
+ throw new AccountsError(nameCheck.error.issues[0]?.message ?? "invalid profile name");
4740
+ const toolId = opts.tool ?? DEFAULT_TOOL;
4741
+ const tool = getTool(toolId);
4742
+ const store = loadStore();
4743
+ if (store.profiles.some((p) => p.name === name && p.tool === toolId)) {
4744
+ throw new AccountsError(`a ${toolId} profile named "${name}" already exists`);
4745
+ }
4746
+ const dir = opts.dir ? expandPath(opts.dir) : join6(profilesDir(), toolId, name);
4747
+ if (store.profiles.some((p) => p.dir === dir)) {
4748
+ throw new AccountsError(`a profile already uses config dir ${dir}`);
4749
+ }
4750
+ mkdirSync4(dir, { recursive: true });
4751
+ const email = opts.email ?? detectEmail(dir, tool);
4752
+ const profile = {
4753
+ name,
4754
+ tool: toolId,
4755
+ ...email ? { email } : {},
4756
+ dir,
4757
+ ...opts.description ? { description: opts.description } : {},
4758
+ createdAt: nowIso()
4759
+ };
4760
+ store.profiles.push(profile);
4761
+ saveStore(store);
4762
+ return profile;
4763
+ }
4764
+ function removeProfile(name, opts = {}) {
4765
+ const options = typeof opts === "boolean" ? { purge: opts } : opts;
4766
+ const store = loadStore();
4767
+ const matches = store.profiles.map((profile2, idx2) => ({ profile: profile2, idx: idx2 })).filter(({ profile: profile2 }) => profile2.name === name && (!options.tool || profile2.tool === options.tool));
4768
+ if (matches.length === 0) {
4769
+ const suffix = options.tool ? ` for tool "${options.tool}"` : "";
4770
+ throw new AccountsError(`no profile named "${name}"${suffix}`);
4771
+ }
4772
+ if (matches.length > 1) {
4773
+ throw new AccountsError(`profile "${name}" exists for multiple tools (${matches.map(({ profile: profile2 }) => profile2.tool).join(", ")}); pass --tool`);
4774
+ }
4775
+ const idx = matches[0].idx;
4776
+ const profile = store.profiles[idx];
4777
+ store.profiles.splice(idx, 1);
4778
+ if (store.current[profile.tool] === name)
4779
+ delete store.current[profile.tool];
4780
+ if (store.applied[profile.tool] === name)
4781
+ delete store.applied[profile.tool];
4782
+ saveStore(store);
4783
+ let purged = false;
4784
+ let purgeNote;
4785
+ if (options.purge) {
4786
+ const managed = profile.dir.startsWith(profilesDir());
4787
+ const isDefault = profile.dir === getTool(profile.tool).defaultDir;
4788
+ if (managed && !isDefault && existsSync5(profile.dir)) {
4789
+ rmSync(profile.dir, { recursive: true, force: true });
4790
+ purged = true;
4791
+ } else {
4792
+ purgeNote = `refused to delete ${profile.dir} (not a managed profile dir); remove it manually if intended`;
4793
+ }
4794
+ }
4795
+ return { profile, purged, purgeNote };
4796
+ }
4797
+ function renameProfile(oldName, newName, toolId) {
4798
+ const nameCheck = profileNameSchema.safeParse(newName);
4799
+ if (!nameCheck.success)
4800
+ throw new AccountsError(nameCheck.error.issues[0]?.message ?? "invalid profile name");
4801
+ const store = loadStore();
4802
+ const matches = store.profiles.filter((p) => p.name === oldName && (!toolId || p.tool === toolId));
4803
+ if (matches.length === 0) {
4804
+ const suffix = toolId ? ` for tool "${toolId}"` : "";
4805
+ throw new AccountsError(`no profile named "${oldName}"${suffix}`);
4806
+ }
4807
+ if (matches.length > 1) {
4808
+ throw new AccountsError(`profile "${oldName}" exists for multiple tools (${matches.map((p) => p.tool).join(", ")}); pass --tool`);
4809
+ }
4810
+ const profile = matches[0];
4811
+ if (store.profiles.some((p) => p.name === newName && p.tool === profile.tool)) {
4812
+ throw new AccountsError(`a ${profile.tool} profile named "${newName}" already exists`);
4813
+ }
4814
+ if (store.current[profile.tool] === oldName)
4815
+ store.current[profile.tool] = newName;
4816
+ if (store.applied[profile.tool] === oldName)
4817
+ store.applied[profile.tool] = newName;
4818
+ profile.name = newName;
4819
+ saveStore(store);
4820
+ return profile;
4821
+ }
4822
+ function updateProfile(name, opts) {
4823
+ const store = loadStore();
4824
+ const matches = store.profiles.filter((p) => p.name === name && (!opts.tool || p.tool === opts.tool));
4825
+ if (matches.length === 0) {
4826
+ const suffix = opts.tool ? ` for tool "${opts.tool}"` : "";
4827
+ throw new AccountsError(`no profile named "${name}"${suffix}`);
4828
+ }
4829
+ if (matches.length > 1) {
4830
+ throw new AccountsError(`profile "${name}" exists for multiple tools (${matches.map((p) => p.tool).join(", ")}); pass --tool`);
4831
+ }
4832
+ const profile = matches[0];
4833
+ if (opts.email !== undefined)
4834
+ profile.email = opts.email;
4835
+ if (opts.description !== undefined)
4836
+ profile.description = opts.description;
4837
+ if (opts.dir !== undefined) {
4838
+ const dir = expandPath(opts.dir);
4839
+ mkdirSync4(dir, { recursive: true });
4840
+ profile.dir = dir;
4841
+ }
4842
+ saveStore(store);
4843
+ return profile;
4844
+ }
4845
+ function redetectEmail(name, toolId) {
4846
+ const store = loadStore();
4847
+ const matches = store.profiles.filter((p) => p.name === name && (!toolId || p.tool === toolId));
4848
+ if (matches.length === 0) {
4849
+ const suffix = toolId ? ` for tool "${toolId}"` : "";
4850
+ throw new AccountsError(`no profile named "${name}"${suffix}`);
4851
+ }
4852
+ if (matches.length > 1) {
4853
+ throw new AccountsError(`profile "${name}" exists for multiple tools (${matches.map((p) => p.tool).join(", ")}); pass --tool`);
4854
+ }
4855
+ const profile = matches[0];
4856
+ const email = detectEmail(profile.dir, getTool(profile.tool));
4857
+ if (email)
4858
+ profile.email = email;
4859
+ saveStore(store);
4860
+ return profile;
4861
+ }
4862
+ function useProfile(name, toolId) {
4863
+ const store = loadStore();
4864
+ const matches = store.profiles.filter((p) => p.name === name && (!toolId || p.tool === toolId));
4865
+ if (matches.length === 0) {
4866
+ const suffix = toolId ? ` for tool "${toolId}"` : "";
4867
+ throw new AccountsError(`no profile named "${name}"${suffix}`);
4868
+ }
4869
+ if (matches.length > 1) {
4870
+ throw new AccountsError(`profile "${name}" exists for multiple tools (${matches.map((p) => p.tool).join(", ")}); pass --tool`);
4871
+ }
4872
+ const profile = matches[0];
4873
+ store.current[profile.tool] = name;
4874
+ profile.lastUsedAt = nowIso();
4875
+ saveStore(store);
4876
+ return { profile, toolId: profile.tool };
4877
+ }
4878
+ function currentProfile(toolId) {
4879
+ const store = loadStore();
4880
+ const name = store.current[toolId];
4881
+ if (!name)
4882
+ return;
4883
+ return store.profiles.find((p) => p.name === name);
4884
+ }
4823
4885
  // src/lib/apply-lock.ts
4824
4886
  import { closeSync, existsSync as existsSync6, mkdirSync as mkdirSync5, openSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "node:fs";
4825
4887
  import { join as join7 } from "node:path";
@@ -5473,6 +5535,9 @@ export {
5473
5535
  shellSnippet,
5474
5536
  sendSupervisorRequest,
5475
5537
  saveStore,
5538
+ sanitizeLiveClaudeOAuthSettings,
5539
+ sanitizeClaudeProfileApiSettings,
5540
+ sanitizeClaudeOAuthProfileSettings,
5476
5541
  runSupervisedTool,
5477
5542
  restoreClaudeAuthFromProfile,
5478
5543
  resolveSupervisorLaunch,
@@ -5517,6 +5582,7 @@ export {
5517
5582
  addCustomTool,
5518
5583
  accountsHome,
5519
5584
  DEFAULT_TOOL,
5585
+ CLAUDE_API_AUTH_ENV_KEYS,
5520
5586
  BUILTIN_TOOLS,
5521
5587
  AccountsError
5522
5588
  };
@@ -1,4 +1,8 @@
1
1
  import type { ToolDef } from "../types.js";
2
+ export declare const CLAUDE_API_AUTH_ENV_KEYS: readonly ["ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN", "ANTHROPIC_BASE_URL", "CLAUDE_CODE_API_KEY_HELPER", "CLAUDE_CODE_API_KEY_HELPER_TTL_MS", "CLAUDE_CODE_USE_BEDROCK", "CLAUDE_CODE_USE_VERTEX"];
3
+ export declare function sanitizeClaudeProfileApiSettings(profileDir: string, tool: ToolDef): boolean;
4
+ export declare function sanitizeClaudeOAuthProfileSettings(profileDir: string, tool: ToolDef): boolean;
5
+ export declare function sanitizeLiveClaudeOAuthSettings(): boolean;
2
6
  /** Email address of the account currently authenticated on the live Claude paths. */
3
7
  export declare function liveOAuthEmail(): string | undefined;
4
8
  /** Snapshot live Claude auth into a profile directory (used when switching away on apply). */
@@ -1 +1 @@
1
- {"version":3,"file":"claude-auth.d.ts","sourceRoot":"","sources":["../../src/lib/claude-auth.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAyF3C,qFAAqF;AACrF,wBAAgB,cAAc,IAAI,MAAM,GAAG,SAAS,CAKnD;AAED,8FAA8F;AAC9F,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAmBlF;AAED,gDAAgD;AAChD,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAEnF;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,OAAO,EACb,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,IAAI,CAiBN;AAED,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAEzE;AAED,6DAA6D;AAC7D,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,OAAO,EACb,WAAW,CAAC,EAAE,MAAM,GACnB,IAAI,CAqDN;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAM3D"}
1
+ {"version":3,"file":"claude-auth.d.ts","sourceRoot":"","sources":["../../src/lib/claude-auth.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAuB3C,eAAO,MAAM,wBAAwB,sMAQ3B,CAAC;AAoGX,wBAAgB,gCAAgC,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAG3F;AAED,wBAAgB,kCAAkC,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAM7F;AAED,wBAAgB,+BAA+B,IAAI,OAAO,CAEzD;AAED,qFAAqF;AACrF,wBAAgB,cAAc,IAAI,MAAM,GAAG,SAAS,CAKnD;AAED,8FAA8F;AAC9F,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAmBlF;AAED,gDAAgD;AAChD,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAEnF;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,OAAO,EACb,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,IAAI,CAmBN;AAED,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAEzE;AAED,6DAA6D;AAC7D,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,OAAO,EACb,WAAW,CAAC,EAAE,MAAM,GACnB,IAAI,CAwDN;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAM3D"}
@@ -1 +1 @@
1
- {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/lib/env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAMpD,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQlF;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAIxE;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAIrE"}
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/lib/env.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAOpD,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAYlF;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAIxE;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAIrE"}
package/dist/mcp.js CHANGED
@@ -17072,6 +17072,15 @@ function writeClaudeKeychain(cred) {
17072
17072
  }
17073
17073
 
17074
17074
  // src/lib/claude-auth.ts
17075
+ var CLAUDE_API_AUTH_ENV_KEYS = [
17076
+ "ANTHROPIC_API_KEY",
17077
+ "ANTHROPIC_AUTH_TOKEN",
17078
+ "ANTHROPIC_BASE_URL",
17079
+ "CLAUDE_CODE_API_KEY_HELPER",
17080
+ "CLAUDE_CODE_API_KEY_HELPER_TTL_MS",
17081
+ "CLAUDE_CODE_USE_BEDROCK",
17082
+ "CLAUDE_CODE_USE_VERTEX"
17083
+ ];
17075
17084
  function readJsonFile(path) {
17076
17085
  if (!existsSync3(path))
17077
17086
  return;
@@ -17090,6 +17099,11 @@ function writeJsonFile(path, data, stayUnder) {
17090
17099
  function readOAuthFromPaths(paths) {
17091
17100
  return findOAuthSource(paths)?.oauth;
17092
17101
  }
17102
+ function readOAuthSnapshot(profileDir) {
17103
+ const snap = readJsonFile(profileOAuthSnapshot(profileDir));
17104
+ const oauth = snap?.oauthAccount;
17105
+ return oauth && typeof oauth === "object" ? oauth : undefined;
17106
+ }
17093
17107
  function findOAuthSource(paths) {
17094
17108
  for (const p of paths) {
17095
17109
  const data = readJsonFile(p);
@@ -17131,6 +17145,46 @@ function mergeOAuthInto(paths, oauth, allowDelete, stayUnder) {
17131
17145
  }
17132
17146
  }
17133
17147
  }
17148
+ function sanitizeSettingsFile(configDir, stayUnder) {
17149
+ const settingsPath = join4(configDir, "settings.json");
17150
+ const settings = readJsonFile(settingsPath);
17151
+ if (!settings)
17152
+ return false;
17153
+ let changed = false;
17154
+ if ("apiKeyHelper" in settings) {
17155
+ delete settings.apiKeyHelper;
17156
+ changed = true;
17157
+ }
17158
+ const env = settings.env;
17159
+ if (env && typeof env === "object" && !Array.isArray(env)) {
17160
+ const envRecord = env;
17161
+ for (const key of CLAUDE_API_AUTH_ENV_KEYS) {
17162
+ if (key in envRecord) {
17163
+ delete envRecord[key];
17164
+ changed = true;
17165
+ }
17166
+ }
17167
+ }
17168
+ if (changed)
17169
+ writeJsonFile(settingsPath, settings, stayUnder);
17170
+ return changed;
17171
+ }
17172
+ function sanitizeClaudeProfileApiSettings(profileDir, tool) {
17173
+ if (tool.id !== "claude")
17174
+ return false;
17175
+ return sanitizeSettingsFile(profileDir, profileDir);
17176
+ }
17177
+ function sanitizeClaudeOAuthProfileSettings(profileDir, tool) {
17178
+ if (tool.id !== "claude")
17179
+ return false;
17180
+ if (!readOAuthSnapshot(profileDir) && !readOAuthFromPaths(profileAccountJsonPaths(profileDir, tool))) {
17181
+ return false;
17182
+ }
17183
+ return sanitizeClaudeProfileApiSettings(profileDir, tool);
17184
+ }
17185
+ function sanitizeLiveClaudeOAuthSettings() {
17186
+ return sanitizeSettingsFile(liveClaudePaths().configDir, liveClaudeBase());
17187
+ }
17134
17188
  function liveOAuthEmail() {
17135
17189
  const live = liveClaudePaths();
17136
17190
  const oauth = readOAuthFromPaths([live.homeJson]);
@@ -17171,6 +17225,7 @@ function ensureProfileAuthSnapshot(profileDir, tool, opts = {}) {
17171
17225
  assertSafeWritePath(credSnap, { mustStayUnder: profileDir });
17172
17226
  copyFileSync(credFile, credSnap);
17173
17227
  }
17228
+ sanitizeClaudeOAuthProfileSettings(profileDir, tool);
17174
17229
  }
17175
17230
  function profileHasAuth(profileDir, tool) {
17176
17231
  return hasAuthSnapshot(profileDir) || !!readOAuthFromPaths(profileAccountJsonPaths(profileDir, tool));
@@ -17189,6 +17244,8 @@ function restoreClaudeAuthFromProfile(profileDir, tool, profileName) {
17189
17244
  if (!oauth) {
17190
17245
  throw new AccountsError("profile has no OAuth account data to apply");
17191
17246
  }
17247
+ sanitizeClaudeOAuthProfileSettings(profileDir, tool);
17248
+ sanitizeLiveClaudeOAuthSettings();
17192
17249
  assertSafeWritePath(live.homeJson, { mustStayUnder: liveRoot });
17193
17250
  mergeOAuthInto([live.homeJson], oauth, false, liveRoot);
17194
17251
  const credSnap = profileCredentialsSnapshot(profileDir);
@@ -17306,6 +17363,11 @@ function profileEnv(profile, tool) {
17306
17363
  for (const [name, value] of Object.entries(tool.extraEnv ?? {})) {
17307
17364
  env[name] = renderTemplate(value, profile);
17308
17365
  }
17366
+ if (tool.id === "claude") {
17367
+ sanitizeClaudeProfileApiSettings(profile.dir, tool);
17368
+ for (const key of CLAUDE_API_AUTH_ENV_KEYS)
17369
+ env[key] = "";
17370
+ }
17309
17371
  return env;
17310
17372
  }
17311
17373
  function formatEnvAssignments(env) {
@@ -17464,7 +17526,7 @@ function ok(data) {
17464
17526
  function fail(message) {
17465
17527
  return { content: [{ type: "text", text: JSON.stringify({ error: message }) }], isError: true };
17466
17528
  }
17467
- var server = new Server({ name: "accounts", version: "0.1.8" }, { capabilities: { tools: {} } });
17529
+ var server = new Server({ name: "accounts", version: "0.1.12" }, { capabilities: { tools: {} } });
17468
17530
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
17469
17531
  tools: [
17470
17532
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/accounts",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "Manage and switch between multiple Claude Code (and other AI coding tool) profiles/accounts locally — isolated config dirs, per-account email, one-command switching.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",