@hasna/accounts 0.1.19 → 0.1.20

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.js CHANGED
@@ -4029,57 +4029,126 @@ class AccountsError extends Error {
4029
4029
  }
4030
4030
  // src/storage.ts
4031
4031
  import { homedir } from "node:os";
4032
- import { join } from "node:path";
4032
+ import { join as join2 } from "node:path";
4033
4033
  import { chmodSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, writeFileSync } from "node:fs";
4034
4034
 
4035
4035
  // src/lib/safe-path.ts
4036
4036
  import { existsSync, lstatSync, mkdirSync, realpathSync } from "node:fs";
4037
- import { dirname, resolve } from "node:path";
4037
+ import { dirname, isAbsolute, join, parse, relative, resolve, sep } from "node:path";
4038
+ function lstatIfExists(path) {
4039
+ try {
4040
+ return lstatSync(path);
4041
+ } catch (err) {
4042
+ if (err.code !== "ENOENT")
4043
+ throw err;
4044
+ return;
4045
+ }
4046
+ }
4038
4047
  function throwIfSymlink(path, label) {
4039
- if (existsSync(path) && lstatSync(path).isSymbolicLink()) {
4048
+ if (lstatIfExists(path)?.isSymbolicLink()) {
4040
4049
  throw new AccountsError(`${label}: ${path}`);
4041
4050
  }
4042
4051
  }
4043
- function assertDirChainSafe(targetFile, mustStayUnder) {
4044
- const absFile = resolve(targetFile);
4045
- const parent = dirname(absFile);
4046
- const base = mustStayUnder ? resolve(mustStayUnder) : undefined;
4047
- if (base) {
4048
- const rel = absFile.startsWith(base + "/") ? absFile.slice(base.length + 1) : "";
4049
- if (!rel && absFile !== base) {
4050
- throw new AccountsError(`refusing to write outside profile directory: ${targetFile}`);
4051
- }
4052
- const segments = rel.split("/").filter(Boolean);
4053
- if (segments.some((s) => s === "..")) {
4054
- throw new AccountsError(`refusing path traversal: ${targetFile}`);
4055
- }
4056
- let cursor = base;
4057
- for (let i = 0;i < segments.length - 1; i++) {
4058
- cursor = resolve(cursor, segments[i]);
4059
- throwIfSymlink(cursor, "refusing to write under symlink directory");
4060
- }
4061
- } else {
4062
- let cursor = parent;
4063
- for (;; ) {
4064
- throwIfSymlink(cursor, "refusing to write under symlink directory");
4065
- const next = dirname(cursor);
4066
- if (next === cursor)
4067
- break;
4068
- cursor = next;
4052
+ function isAllowedSystemDirectorySymlink(path) {
4053
+ if (path !== "/var" && path !== "/tmp")
4054
+ return false;
4055
+ try {
4056
+ return realpathSync(path) === `/private${path}`;
4057
+ } catch {
4058
+ return false;
4059
+ }
4060
+ }
4061
+ function assertDirectory(path, label, opts) {
4062
+ const stat = lstatIfExists(path);
4063
+ if (!stat) {
4064
+ throw new AccountsError(`refusing to write under missing directory: ${path}`);
4065
+ }
4066
+ if (stat.isSymbolicLink()) {
4067
+ if (opts?.allowSystemSymlink && isAllowedSystemDirectorySymlink(path))
4068
+ return;
4069
+ throw new AccountsError(`${label}: ${path}`);
4070
+ }
4071
+ if (!stat.isDirectory()) {
4072
+ throw new AccountsError(`refusing to write under non-directory path: ${path}`);
4073
+ }
4074
+ }
4075
+ function assertInsideBase(absPath, base, originalPath) {
4076
+ const rel = relative(base, absPath);
4077
+ if (rel === "")
4078
+ return rel;
4079
+ if (rel === ".." || rel.startsWith(".." + sep) || isAbsolute(rel)) {
4080
+ throw new AccountsError(`refusing to write outside profile directory: ${originalPath}`);
4081
+ }
4082
+ return rel;
4083
+ }
4084
+ function assertExistingDirectoryComponentsSafe(path, label) {
4085
+ const root = parse(path).root;
4086
+ const rel = relative(root, path);
4087
+ const segments = rel ? rel.split(sep).filter(Boolean) : [];
4088
+ let cursor = root;
4089
+ assertDirectory(cursor, label, { allowSystemSymlink: true });
4090
+ for (const segment of segments) {
4091
+ cursor = join(cursor, segment);
4092
+ if (!lstatIfExists(cursor))
4093
+ return;
4094
+ assertDirectory(cursor, label, { allowSystemSymlink: true });
4095
+ }
4096
+ }
4097
+ function ensureBoundaryRootSafe(base) {
4098
+ const missing = [];
4099
+ let cursor = base;
4100
+ while (!lstatIfExists(cursor)) {
4101
+ missing.unshift(cursor);
4102
+ const parent = dirname(cursor);
4103
+ if (parent === cursor)
4104
+ break;
4105
+ cursor = parent;
4106
+ }
4107
+ assertDirectory(cursor, "refusing to use symlink base directory");
4108
+ for (const dir of missing) {
4109
+ if (lstatIfExists(dir)) {
4110
+ assertDirectory(dir, "refusing to use symlink base directory");
4111
+ } else {
4112
+ mkdirSync(dir);
4113
+ assertDirectory(dir, "refusing to use symlink base directory");
4114
+ }
4115
+ }
4116
+ assertDirectory(base, "refusing to use symlink base directory");
4117
+ }
4118
+ function ensureDirectoryChainSafe(parent, startAt) {
4119
+ const root = startAt ?? parse(parent).root;
4120
+ const rel = relative(root, parent);
4121
+ const segments = rel ? rel.split(sep).filter(Boolean) : [];
4122
+ let cursor = root;
4123
+ if (startAt)
4124
+ assertDirectory(startAt, "refusing to write under symlink directory");
4125
+ for (const segment of segments) {
4126
+ cursor = join(cursor, segment);
4127
+ if (lstatIfExists(cursor)) {
4128
+ assertDirectory(cursor, "refusing to write under symlink directory");
4129
+ } else {
4130
+ mkdirSync(cursor);
4131
+ assertDirectory(cursor, "refusing to write under symlink directory");
4069
4132
  }
4070
4133
  }
4071
4134
  }
4072
4135
  function assertSafeWritePath(filePath, opts) {
4073
4136
  const absFile = resolve(filePath);
4074
4137
  const parent = dirname(absFile);
4075
- if (!existsSync(parent))
4076
- mkdirSync(parent, { recursive: true });
4138
+ const base = opts?.mustStayUnder ? resolve(opts.mustStayUnder) : undefined;
4139
+ if (base) {
4140
+ assertInsideBase(absFile, base, filePath);
4141
+ assertExistingDirectoryComponentsSafe(base, "refusing to use symlink base directory");
4142
+ ensureBoundaryRootSafe(base);
4143
+ ensureDirectoryChainSafe(parent, base);
4144
+ } else {
4145
+ ensureDirectoryChainSafe(parent);
4146
+ }
4077
4147
  throwIfSymlink(absFile, "refusing to write through symlink");
4078
- assertDirChainSafe(absFile, opts?.mustStayUnder);
4079
4148
  const resolved = realpathSync(existsSync(absFile) ? absFile : parent);
4080
- if (opts?.mustStayUnder) {
4081
- const base = realpathSync(resolve(opts.mustStayUnder));
4082
- if (resolved !== base && !resolved.startsWith(base + "/")) {
4149
+ if (base) {
4150
+ const realBase = realpathSync(base);
4151
+ if (resolved !== realBase && !resolved.startsWith(realBase + sep)) {
4083
4152
  throw new AccountsError(`refusing to write outside profile directory: ${filePath}`);
4084
4153
  }
4085
4154
  }
@@ -4108,16 +4177,16 @@ function accountsHome() {
4108
4177
  const override = process.env.ACCOUNTS_HOME;
4109
4178
  if (override && override.trim())
4110
4179
  return validateEnvPath(override, "ACCOUNTS_HOME");
4111
- return join(homedir(), ".hasna", "accounts");
4180
+ return join2(homedir(), ".hasna", "accounts");
4112
4181
  }
4113
4182
  function storePath() {
4114
4183
  const override = process.env.ACCOUNTS_STORE_PATH;
4115
4184
  if (override && override.trim())
4116
4185
  return validateEnvPath(override, "ACCOUNTS_STORE_PATH");
4117
- return join(accountsHome(), "accounts.json");
4186
+ return join2(accountsHome(), "accounts.json");
4118
4187
  }
4119
4188
  function profilesDir() {
4120
- return join(accountsHome(), "profiles");
4189
+ return join2(accountsHome(), "profiles");
4121
4190
  }
4122
4191
  var EMPTY_STORE = { version: 1, current: {}, applied: {}, profiles: [], tools: [] };
4123
4192
  function loadStore() {
@@ -4160,7 +4229,7 @@ function loadStore() {
4160
4229
  function saveStore(store) {
4161
4230
  const path = storePath();
4162
4231
  assertSafeWritePath(path, { mustStayUnder: accountsHome() });
4163
- mkdirSync2(join(path, ".."), { recursive: true });
4232
+ mkdirSync2(join2(path, ".."), { recursive: true });
4164
4233
  if (existsSync2(path))
4165
4234
  chmodSync(path, 384);
4166
4235
  writeFileSync(path, JSON.stringify(store, null, 2) + `
@@ -4169,7 +4238,7 @@ function saveStore(store) {
4169
4238
  }
4170
4239
  // src/lib/tools.ts
4171
4240
  import { homedir as homedir2 } from "node:os";
4172
- import { join as join2 } from "node:path";
4241
+ import { join as join3 } from "node:path";
4173
4242
  var BUILTIN_TOOLS = [
4174
4243
  {
4175
4244
  id: "claude",
@@ -4178,7 +4247,7 @@ var BUILTIN_TOOLS = [
4178
4247
  extraEnv: {
4179
4248
  TELEGRAM_STATE_DIR: "{profileDir}/channels/telegram"
4180
4249
  },
4181
- defaultDir: join2(homedir2(), ".claude"),
4250
+ defaultDir: join3(homedir2(), ".claude"),
4182
4251
  bin: "claude",
4183
4252
  loginHint: "run /login inside Claude, then /exit when done",
4184
4253
  resumeArgs: ["--continue"],
@@ -4198,7 +4267,7 @@ var BUILTIN_TOOLS = [
4198
4267
  id: "codex-app",
4199
4268
  label: "Codex App",
4200
4269
  envVar: "CODEX_HOME",
4201
- defaultDir: join2(homedir2(), ".codex"),
4270
+ defaultDir: join3(homedir2(), ".codex"),
4202
4271
  bin: "/Applications/Codex.app/Contents/MacOS/Codex",
4203
4272
  loginHint: "sign in inside Codex.app, then quit the app when the profile is ready",
4204
4273
  launchArgs: ["--user-data-dir={profileDir}/electron-user-data"],
@@ -4208,7 +4277,7 @@ var BUILTIN_TOOLS = [
4208
4277
  id: "codex",
4209
4278
  label: "Codex CLI",
4210
4279
  envVar: "CODEX_HOME",
4211
- defaultDir: join2(homedir2(), ".codex"),
4280
+ defaultDir: join3(homedir2(), ".codex"),
4212
4281
  bin: "codex",
4213
4282
  loginArgs: ["login"],
4214
4283
  loginHint: "complete the Codex login flow for this CODEX_HOME",
@@ -4221,7 +4290,7 @@ var BUILTIN_TOOLS = [
4221
4290
  id: "takumi",
4222
4291
  label: "Takumi",
4223
4292
  envVar: "TAKUMI_CONFIG_DIR",
4224
- defaultDir: join2(homedir2(), ".takumi"),
4293
+ defaultDir: join3(homedir2(), ".takumi"),
4225
4294
  bin: "takumi",
4226
4295
  loginHint: "complete Takumi auth in this TAKUMI_CONFIG_DIR",
4227
4296
  resumeArgs: ["--continue"],
@@ -4241,7 +4310,7 @@ var BUILTIN_TOOLS = [
4241
4310
  id: "gemini",
4242
4311
  label: "Gemini CLI",
4243
4312
  envVar: "GEMINI_CONFIG_DIR",
4244
- defaultDir: join2(homedir2(), ".gemini"),
4313
+ defaultDir: join3(homedir2(), ".gemini"),
4245
4314
  bin: "gemini",
4246
4315
  loginHint: "complete Gemini auth in this GEMINI_CONFIG_DIR",
4247
4316
  permissionArgs: {
@@ -4259,7 +4328,7 @@ var BUILTIN_TOOLS = [
4259
4328
  XDG_CONFIG_HOME: "{profileDir}/xdg-config",
4260
4329
  XDG_DATA_HOME: "{profileDir}/xdg-data"
4261
4330
  },
4262
- defaultDir: join2(homedir2(), ".config", "opencode"),
4331
+ defaultDir: join3(homedir2(), ".config", "opencode"),
4263
4332
  bin: "opencode",
4264
4333
  loginArgs: ["auth", "login"],
4265
4334
  loginHint: "complete opencode auth login for this isolated config/data root",
@@ -4269,7 +4338,7 @@ var BUILTIN_TOOLS = [
4269
4338
  id: "cursor",
4270
4339
  label: "Cursor Agent",
4271
4340
  envVar: "CURSOR_CONFIG_DIR",
4272
- defaultDir: join2(homedir2(), ".cursor"),
4341
+ defaultDir: join3(homedir2(), ".cursor"),
4273
4342
  bin: "cursor-agent",
4274
4343
  loginArgs: ["login"],
4275
4344
  loginHint: "complete cursor-agent login for this CURSOR_CONFIG_DIR"
@@ -4278,7 +4347,7 @@ var BUILTIN_TOOLS = [
4278
4347
  id: "pi",
4279
4348
  label: "Pi Coding Agent",
4280
4349
  envVar: "PI_CODING_AGENT_HOME",
4281
- defaultDir: join2(homedir2(), ".pi"),
4350
+ defaultDir: join3(homedir2(), ".pi"),
4282
4351
  bin: "pi",
4283
4352
  loginHint: "complete Pi coding agent auth in this PI_CODING_AGENT_HOME"
4284
4353
  },
@@ -4286,7 +4355,7 @@ var BUILTIN_TOOLS = [
4286
4355
  id: "hermes",
4287
4356
  label: "Hermes",
4288
4357
  envVar: "HERMES_HOME",
4289
- defaultDir: join2(homedir2(), ".hermes"),
4358
+ defaultDir: join3(homedir2(), ".hermes"),
4290
4359
  bin: "hermes",
4291
4360
  loginHint: "complete Hermes auth in this HERMES_HOME",
4292
4361
  permissionArgs: {
@@ -4298,7 +4367,7 @@ var BUILTIN_TOOLS = [
4298
4367
  id: "kimi",
4299
4368
  label: "Kimi Code",
4300
4369
  envVar: "KIMI_CODE_HOME",
4301
- defaultDir: join2(homedir2(), ".kimi-code"),
4370
+ defaultDir: join3(homedir2(), ".kimi-code"),
4302
4371
  bin: "kimi",
4303
4372
  loginArgs: ["login"],
4304
4373
  loginHint: "complete kimi login for this KIMI_CODE_HOME",
@@ -4313,7 +4382,7 @@ var BUILTIN_TOOLS = [
4313
4382
  id: "grok",
4314
4383
  label: "Grok Build",
4315
4384
  envVar: "HOME",
4316
- defaultDir: join2(homedir2(), ".grok"),
4385
+ defaultDir: join3(homedir2(), ".grok"),
4317
4386
  bin: "grok",
4318
4387
  loginArgs: ["login"],
4319
4388
  loginHint: "complete grok login in this process-scoped HOME; prefer launch/shell over exporting HOME globally"
@@ -4419,11 +4488,11 @@ function removeCustomTool(id) {
4419
4488
  }
4420
4489
  // src/lib/claude-auth.ts
4421
4490
  import { copyFileSync, existsSync as existsSync3, lstatSync as lstatSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync2, statSync, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
4422
- import { dirname as dirname3, join as join4 } from "node:path";
4491
+ import { dirname as dirname3, join as join5 } from "node:path";
4423
4492
 
4424
4493
  // src/lib/claude-layout.ts
4425
4494
  import { homedir as homedir3 } from "node:os";
4426
- import { dirname as dirname2, join as join3 } from "node:path";
4495
+ import { dirname as dirname2, join as join4 } from "node:path";
4427
4496
  var CLAUDE_KEYCHAIN_SERVICE = "Claude Code-credentials";
4428
4497
  var ACCOUNTS_AUTH_DIR = ".accounts-auth";
4429
4498
  var OAUTH_SNAPSHOT = "oauth-account.json";
@@ -4435,32 +4504,32 @@ function liveClaudeBase() {
4435
4504
  }
4436
4505
  function liveClaudePaths() {
4437
4506
  const base = liveClaudeBase();
4438
- const configDir = join3(base, ".claude");
4507
+ const configDir = join4(base, ".claude");
4439
4508
  return {
4440
4509
  configDir,
4441
- homeJson: join3(base, ".claude.json"),
4442
- credentialsFile: join3(configDir, ".credentials.json")
4510
+ homeJson: join4(base, ".claude.json"),
4511
+ credentialsFile: join4(configDir, ".credentials.json")
4443
4512
  };
4444
4513
  }
4445
4514
  function profileAccountJsonPaths(profileDir, tool) {
4446
4515
  if (!tool.accountFile)
4447
4516
  return [];
4448
- const paths = [join3(profileDir, tool.accountFile)];
4517
+ const paths = [join4(profileDir, tool.accountFile)];
4449
4518
  if (profileDir === tool.defaultDir)
4450
- paths.push(join3(dirname2(profileDir), tool.accountFile));
4519
+ paths.push(join4(dirname2(profileDir), tool.accountFile));
4451
4520
  return paths;
4452
4521
  }
4453
4522
  function profileAuthDir(profileDir) {
4454
- return join3(profileDir, ACCOUNTS_AUTH_DIR);
4523
+ return join4(profileDir, ACCOUNTS_AUTH_DIR);
4455
4524
  }
4456
4525
  function profileOAuthSnapshot(profileDir) {
4457
- return join3(profileAuthDir(profileDir), OAUTH_SNAPSHOT);
4526
+ return join4(profileAuthDir(profileDir), OAUTH_SNAPSHOT);
4458
4527
  }
4459
4528
  function profileCredentialsSnapshot(profileDir) {
4460
- return join3(profileAuthDir(profileDir), CREDENTIALS_SNAPSHOT);
4529
+ return join4(profileAuthDir(profileDir), CREDENTIALS_SNAPSHOT);
4461
4530
  }
4462
4531
  function profileKeychainSnapshot(profileDir) {
4463
- return join3(profileAuthDir(profileDir), KEYCHAIN_SNAPSHOT);
4532
+ return join4(profileAuthDir(profileDir), KEYCHAIN_SNAPSHOT);
4464
4533
  }
4465
4534
 
4466
4535
  // src/lib/keychain.ts
@@ -4614,7 +4683,7 @@ function mergeOAuthInto(paths, oauth, allowDelete, stayUnder) {
4614
4683
  }
4615
4684
  }
4616
4685
  function sanitizeSettingsFile(configDir, stayUnder) {
4617
- const settingsPath = join4(configDir, "settings.json");
4686
+ const settingsPath = join5(configDir, "settings.json");
4618
4687
  const settings = readJsonFile(settingsPath);
4619
4688
  if (!settings)
4620
4689
  return false;
@@ -4661,7 +4730,7 @@ function liveOAuthEmail() {
4661
4730
  }
4662
4731
  function snapshotLiveAuthToProfile(profileDir, _tool) {
4663
4732
  const authDir = profileAuthDir(profileDir);
4664
- assertSafeWritePath(join4(authDir, OAUTH_SNAPSHOT), { mustStayUnder: profileDir });
4733
+ assertSafeWritePath(join5(authDir, OAUTH_SNAPSHOT), { mustStayUnder: profileDir });
4665
4734
  mkdirSync3(authDir, { recursive: true });
4666
4735
  const live = liveClaudePaths();
4667
4736
  const oauth = readOAuthFromPaths([live.homeJson]);
@@ -4683,14 +4752,14 @@ function snapshotClaudeAuthToProfile(profileDir, tool) {
4683
4752
  }
4684
4753
  function ensureProfileAuthSnapshot(profileDir, tool, opts = {}) {
4685
4754
  const authDir = profileAuthDir(profileDir);
4686
- assertSafeWritePath(join4(authDir, OAUTH_SNAPSHOT), { mustStayUnder: profileDir });
4755
+ assertSafeWritePath(join5(authDir, OAUTH_SNAPSHOT), { mustStayUnder: profileDir });
4687
4756
  mkdirSync3(authDir, { recursive: true });
4688
4757
  const oauthSource = findOAuthSource(profileAccountJsonPaths(profileDir, tool));
4689
4758
  const oauthSnap = profileOAuthSnapshot(profileDir);
4690
4759
  if (oauthSource && (opts.overwrite || snapshotIsStale(oauthSource.path, oauthSnap))) {
4691
4760
  writeJsonFile(oauthSnap, { oauthAccount: oauthSource.oauth }, profileDir);
4692
4761
  }
4693
- const credFile = join4(profileDir, ".credentials.json");
4762
+ const credFile = join5(profileDir, ".credentials.json");
4694
4763
  const credSnap = profileCredentialsSnapshot(profileDir);
4695
4764
  if (existsSync3(credFile) && (opts.overwrite || snapshotIsStale(credFile, credSnap))) {
4696
4765
  assertSafeWritePath(credSnap, { mustStayUnder: profileDir });
@@ -4752,7 +4821,7 @@ function hasAuthSnapshot(profileDir) {
4752
4821
 
4753
4822
  // src/lib/codex-app.ts
4754
4823
  import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
4755
- import { join as join5 } from "node:path";
4824
+ import { join as join6 } from "node:path";
4756
4825
  var FILE_CREDENTIALS_LINE = 'cli_auth_credentials_store = "file"';
4757
4826
  function insertRootConfigLine(config, line) {
4758
4827
  if (config.trim() === "")
@@ -4779,7 +4848,7 @@ ${after}${after.endsWith(`
4779
4848
  }
4780
4849
  function ensureCodexAppProfileConfig(profileDir) {
4781
4850
  mkdirSync4(profileDir, { recursive: true });
4782
- const configPath = join5(profileDir, "config.toml");
4851
+ const configPath = join6(profileDir, "config.toml");
4783
4852
  const current = existsSync4(configPath) ? readFileSync3(configPath, "utf8") : "";
4784
4853
  if (/^\s*cli_auth_credentials_store\s*=/.test(current))
4785
4854
  return;
@@ -4815,13 +4884,13 @@ function formatExportLines(env) {
4815
4884
  }
4816
4885
  // src/lib/detect.ts
4817
4886
  import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
4818
- import { dirname as dirname4, join as join6 } from "node:path";
4887
+ import { dirname as dirname4, join as join7 } from "node:path";
4819
4888
  function detectEmail(dir, tool) {
4820
4889
  if (!tool.accountFile || !tool.emailPath)
4821
4890
  return;
4822
- const candidates = [join6(dir, tool.accountFile)];
4891
+ const candidates = [join7(dir, tool.accountFile)];
4823
4892
  if (dir === tool.defaultDir)
4824
- candidates.push(join6(dirname4(dir), tool.accountFile));
4893
+ candidates.push(join7(dirname4(dir), tool.accountFile));
4825
4894
  for (const file of candidates) {
4826
4895
  const email = readEmail(file, tool.emailPath);
4827
4896
  if (email)
@@ -4849,7 +4918,7 @@ function readEmail(file, path) {
4849
4918
  }
4850
4919
  // src/lib/profiles.ts
4851
4920
  import { homedir as homedir4 } from "node:os";
4852
- import { isAbsolute, join as join7, relative, resolve as resolve2 } from "node:path";
4921
+ import { isAbsolute as isAbsolute2, join as join8, relative as relative2, resolve as resolve2 } from "node:path";
4853
4922
  import { existsSync as existsSync6, mkdirSync as mkdirSync5, rmSync } from "node:fs";
4854
4923
  function nowIso() {
4855
4924
  return new Date().toISOString();
@@ -4859,8 +4928,8 @@ function expandPath(p) {
4859
4928
  if (out === "~")
4860
4929
  out = homedir4();
4861
4930
  else if (out.startsWith("~/"))
4862
- out = join7(homedir4(), out.slice(2));
4863
- return isAbsolute(out) ? out : resolve2(process.cwd(), out);
4931
+ out = join8(homedir4(), out.slice(2));
4932
+ return isAbsolute2(out) ? out : resolve2(process.cwd(), out);
4864
4933
  }
4865
4934
  function listProfiles(toolId) {
4866
4935
  const profiles = loadStore().profiles;
@@ -4871,8 +4940,8 @@ function profileMatches(name, toolId) {
4871
4940
  return loadStore().profiles.filter((p) => p.name === name && (!toolId || p.tool === toolId));
4872
4941
  }
4873
4942
  function isManagedProfileDir(dir) {
4874
- const rel = relative(resolve2(profilesDir()), resolve2(dir));
4875
- return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
4943
+ const rel = relative2(resolve2(profilesDir()), resolve2(dir));
4944
+ return rel !== "" && !rel.startsWith("..") && !isAbsolute2(rel);
4876
4945
  }
4877
4946
  function findProfile(name, toolId) {
4878
4947
  const matches = profileMatches(name, toolId);
@@ -4901,7 +4970,7 @@ function addProfile(opts) {
4901
4970
  if (store.profiles.some((p) => p.name === name && p.tool === toolId)) {
4902
4971
  throw new AccountsError(`a ${toolId} profile named "${name}" already exists`);
4903
4972
  }
4904
- const dir = opts.dir ? expandPath(opts.dir) : join7(profilesDir(), toolId, name);
4973
+ const dir = opts.dir ? expandPath(opts.dir) : join8(profilesDir(), toolId, name);
4905
4974
  if (store.profiles.some((p) => p.dir === dir)) {
4906
4975
  throw new AccountsError(`a profile already uses config dir ${dir}`);
4907
4976
  }
@@ -5042,9 +5111,9 @@ function currentProfile(toolId) {
5042
5111
  }
5043
5112
  // src/lib/apply-lock.ts
5044
5113
  import { closeSync, existsSync as existsSync7, mkdirSync as mkdirSync6, openSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
5045
- import { join as join8 } from "node:path";
5114
+ import { join as join9 } from "node:path";
5046
5115
  function lockPath() {
5047
- return join8(accountsHome(), ".apply.lock");
5116
+ return join9(accountsHome(), ".apply.lock");
5048
5117
  }
5049
5118
  function withApplyLock(fn) {
5050
5119
  const home = accountsHome();
@@ -5114,7 +5183,7 @@ function applyProfileUnlocked(name, toolId) {
5114
5183
  }
5115
5184
  // src/lib/import-profile.ts
5116
5185
  import { cpSync, existsSync as existsSync8 } from "node:fs";
5117
- import { join as join9 } from "node:path";
5186
+ import { join as join10 } from "node:path";
5118
5187
  function importProfile(opts) {
5119
5188
  const toolId = opts.tool ?? DEFAULT_TOOL;
5120
5189
  const tool = getTool(toolId);
@@ -5124,7 +5193,7 @@ function importProfile(opts) {
5124
5193
  throw new AccountsError(`config dir does not exist: ${sourceDir}`);
5125
5194
  }
5126
5195
  if (opts.copy) {
5127
- const targetDir = join9(profilesDir(), toolId, name);
5196
+ const targetDir = join10(profilesDir(), toolId, name);
5128
5197
  if (existsSync8(targetDir)) {
5129
5198
  throw new AccountsError(`managed copy target already exists: ${targetDir}`);
5130
5199
  }
@@ -5230,20 +5299,20 @@ import { spawn } from "node:child_process";
5230
5299
  import { createHash } from "node:crypto";
5231
5300
  import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync5, readdirSync, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "node:fs";
5232
5301
  import { createConnection, createServer } from "node:net";
5233
- import { basename, join as join10 } from "node:path";
5302
+ import { basename, join as join11 } from "node:path";
5234
5303
  var STATE_SUFFIX = ".json";
5235
5304
  function supervisorDir() {
5236
- return join10(accountsHome(), "supervisors");
5305
+ return join11(accountsHome(), "supervisors");
5237
5306
  }
5238
5307
  function supervisorStatePath(toolId) {
5239
- return join10(supervisorDir(), `${toolId}${STATE_SUFFIX}`);
5308
+ return join11(supervisorDir(), `${toolId}${STATE_SUFFIX}`);
5240
5309
  }
5241
5310
  function supervisorSocketPath(toolId) {
5242
5311
  if (process.platform === "win32") {
5243
5312
  const hash = createHash("sha1").update(accountsHome()).digest("hex").slice(0, 12);
5244
5313
  return `\\\\.\\pipe\\hasna-accounts-${hash}-${toolId}`;
5245
5314
  }
5246
- return join10(supervisorDir(), `${toolId}.sock`);
5315
+ return join11(supervisorDir(), `${toolId}.sock`);
5247
5316
  }
5248
5317
  function nowIso2() {
5249
5318
  return new Date().toISOString();
@@ -5620,7 +5689,7 @@ async function pickProfile(opts = {}) {
5620
5689
  }
5621
5690
  // src/lib/hook.ts
5622
5691
  import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync6, unlinkSync as unlinkSync3, writeFileSync as writeFileSync6 } from "node:fs";
5623
- import { join as join11 } from "node:path";
5692
+ import { join as join12 } from "node:path";
5624
5693
  var HOOK_FILE = "claude-hook.sh";
5625
5694
  var MARKER = "# accounts-claude-hook";
5626
5695
  var NAME_PATTERN = "^[a-z0-9][a-z0-9-]*$";
@@ -5628,7 +5697,7 @@ function shellQuotePath(path) {
5628
5697
  return `'${path.replace(/'/g, `'\\''`)}'`;
5629
5698
  }
5630
5699
  function hookPath() {
5631
- return join11(accountsHome(), HOOK_FILE);
5700
+ return join12(accountsHome(), HOOK_FILE);
5632
5701
  }
5633
5702
  function hookScript() {
5634
5703
  const quotedHook = shellQuotePath(hookPath());
@@ -1 +1 @@
1
- {"version":3,"file":"safe-path.d.ts","sourceRoot":"","sources":["../../src/lib/safe-path.ts"],"names":[],"mappings":"AAyCA,8EAA8E;AAC9E,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAiB/F"}
1
+ {"version":3,"file":"safe-path.d.ts","sourceRoot":"","sources":["../../src/lib/safe-path.ts"],"names":[],"mappings":"AA2GA,8EAA8E;AAC9E,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAuB/F"}