@hasna/accounts 0.1.3 → 0.1.5

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
@@ -2082,9 +2082,9 @@ var require_commander = __commonJS((exports) => {
2082
2082
 
2083
2083
  // src/cli.ts
2084
2084
  import { spawnSync } from "node:child_process";
2085
- import { existsSync as existsSync9, readFileSync as readFileSync5 } from "node:fs";
2085
+ import { existsSync as existsSync10, readFileSync as readFileSync6 } from "node:fs";
2086
2086
  import { homedir as homedir5 } from "node:os";
2087
- import { dirname as dirname5, join as join10 } from "node:path";
2087
+ import { dirname as dirname5, join as join11 } from "node:path";
2088
2088
  import { fileURLToPath } from "node:url";
2089
2089
 
2090
2090
  // node_modules/commander/esm.mjs
@@ -6577,6 +6577,7 @@ var toolDefSchema = exports_external.object({
6577
6577
  bin: exports_external.string().min(1),
6578
6578
  loginArgs: exports_external.array(exports_external.string()).optional(),
6579
6579
  loginHint: exports_external.string().optional(),
6580
+ resumeArgs: exports_external.array(exports_external.string()).optional(),
6580
6581
  accountFile: exports_external.string().optional(),
6581
6582
  emailPath: exports_external.array(exports_external.string()).optional()
6582
6583
  });
@@ -6745,6 +6746,7 @@ var BUILTIN_TOOLS = [
6745
6746
  defaultDir: join2(homedir2(), ".claude"),
6746
6747
  bin: "claude",
6747
6748
  loginHint: "run /login inside Claude, then /exit when done",
6749
+ resumeArgs: ["--continue"],
6748
6750
  accountFile: ".claude.json",
6749
6751
  emailPath: ["oauthAccount", "emailAddress"]
6750
6752
  },
@@ -6755,7 +6757,8 @@ var BUILTIN_TOOLS = [
6755
6757
  defaultDir: join2(homedir2(), ".codex"),
6756
6758
  bin: "codex",
6757
6759
  loginArgs: ["login"],
6758
- loginHint: "complete the Codex login flow for this CODEX_HOME"
6760
+ loginHint: "complete the Codex login flow for this CODEX_HOME",
6761
+ resumeArgs: ["resume", "--last"]
6759
6762
  },
6760
6763
  {
6761
6764
  id: "opencode",
@@ -6768,7 +6771,8 @@ var BUILTIN_TOOLS = [
6768
6771
  defaultDir: join2(homedir2(), ".config", "opencode"),
6769
6772
  bin: "opencode",
6770
6773
  loginArgs: ["auth", "login"],
6771
- loginHint: "complete opencode auth login for this isolated config/data root"
6774
+ loginHint: "complete opencode auth login for this isolated config/data root",
6775
+ resumeArgs: ["--continue"]
6772
6776
  },
6773
6777
  {
6774
6778
  id: "cursor",
@@ -7575,6 +7579,411 @@ function finalizeLogin(name, toolId) {
7575
7579
  return { profile: updated, applied: false };
7576
7580
  }
7577
7581
 
7582
+ // src/lib/switch.ts
7583
+ function shellQuote(value) {
7584
+ if (/^[A-Za-z0-9_./:=@+-]+$/.test(value))
7585
+ return value;
7586
+ return `'${value.replaceAll("'", "'\\''")}'`;
7587
+ }
7588
+ function commandLine(env2, command) {
7589
+ return `${formatEnvAssignments(env2)} ${command.map(shellQuote).join(" ")}`.trim();
7590
+ }
7591
+ function commandFor(tool, opts) {
7592
+ return [tool.bin, ...opts.resume ? tool.resumeArgs ?? [] : [], ...opts.args ?? []];
7593
+ }
7594
+ function switchProfile(name, opts = {}) {
7595
+ const profile = getProfile(name, opts.tool);
7596
+ const tool = getTool(profile.tool);
7597
+ const mode = opts.mode ?? "auto";
7598
+ if (!["auto", "apply", "env", "active"].includes(mode)) {
7599
+ throw new AccountsError(`invalid switch mode "${mode}"`);
7600
+ }
7601
+ const env2 = profileEnv(profile, tool);
7602
+ let applied = false;
7603
+ if (mode === "apply" || mode === "auto" && tool.id === "claude") {
7604
+ applyProfile(name, tool.id);
7605
+ applied = true;
7606
+ } else {
7607
+ useProfile(name, tool.id);
7608
+ }
7609
+ const command = commandFor(tool, opts);
7610
+ const restartRequired = opts.resume === true || applied || mode === "env";
7611
+ const message = applied ? `${profile.name} is now the live/default ${tool.label} profile` : `${profile.name} is now the active ${tool.label} profile`;
7612
+ return {
7613
+ profile: getProfile(name, tool.id),
7614
+ tool,
7615
+ applied,
7616
+ active: true,
7617
+ env: env2,
7618
+ exports: formatExportLines(env2),
7619
+ command,
7620
+ commandLine: commandLine(env2, command),
7621
+ restartRequired,
7622
+ message
7623
+ };
7624
+ }
7625
+
7626
+ // src/lib/supervisor.ts
7627
+ import { spawn } from "node:child_process";
7628
+ import { createHash } from "node:crypto";
7629
+ import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync5, readdirSync, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "node:fs";
7630
+ import { createConnection, createServer } from "node:net";
7631
+ import { basename, join as join10 } from "node:path";
7632
+ var STATE_SUFFIX = ".json";
7633
+ function supervisorDir() {
7634
+ return join10(accountsHome(), "supervisors");
7635
+ }
7636
+ function supervisorStatePath(toolId) {
7637
+ return join10(supervisorDir(), `${toolId}${STATE_SUFFIX}`);
7638
+ }
7639
+ function supervisorSocketPath(toolId) {
7640
+ if (process.platform === "win32") {
7641
+ const hash = createHash("sha1").update(accountsHome()).digest("hex").slice(0, 12);
7642
+ return `\\\\.\\pipe\\hasna-accounts-${hash}-${toolId}`;
7643
+ }
7644
+ return join10(supervisorDir(), `${toolId}.sock`);
7645
+ }
7646
+ function nowIso2() {
7647
+ return new Date().toISOString();
7648
+ }
7649
+ function parseState(raw) {
7650
+ const data = JSON.parse(raw);
7651
+ if (data.version !== 1 || typeof data.tool !== "string" || typeof data.profile !== "string" || typeof data.pid !== "number" || typeof data.socketPath !== "string" || !Array.isArray(data.command)) {
7652
+ return;
7653
+ }
7654
+ return data;
7655
+ }
7656
+ function readSupervisorState(toolId) {
7657
+ const path = supervisorStatePath(toolId);
7658
+ if (!existsSync9(path))
7659
+ return;
7660
+ try {
7661
+ return parseState(readFileSync5(path, "utf8"));
7662
+ } catch {
7663
+ return;
7664
+ }
7665
+ }
7666
+ function listSupervisorStates() {
7667
+ const dir = supervisorDir();
7668
+ if (!existsSync9(dir))
7669
+ return [];
7670
+ return readdirSync(dir).filter((name) => name.endsWith(STATE_SUFFIX)).map((name) => basename(name, STATE_SUFFIX)).map((toolId) => readSupervisorState(toolId)).filter((state) => state !== undefined);
7671
+ }
7672
+ function writeSupervisorState(state) {
7673
+ mkdirSync7(supervisorDir(), { recursive: true });
7674
+ writeFileSync5(supervisorStatePath(state.tool), JSON.stringify(state, null, 2) + `
7675
+ `, { mode: 384 });
7676
+ }
7677
+ function removeSupervisorFiles(toolId) {
7678
+ rmSync2(supervisorStatePath(toolId), { force: true });
7679
+ if (process.platform !== "win32")
7680
+ rmSync2(supervisorSocketPath(toolId), { force: true });
7681
+ }
7682
+ function processAlive(pid) {
7683
+ try {
7684
+ process.kill(pid, 0);
7685
+ return true;
7686
+ } catch {
7687
+ return false;
7688
+ }
7689
+ }
7690
+ function knownTool(id) {
7691
+ try {
7692
+ return getTool(id);
7693
+ } catch {
7694
+ return;
7695
+ }
7696
+ }
7697
+ function resolveSupervisorLaunch(target, opts = {}) {
7698
+ const targetTool = knownTool(target);
7699
+ if (opts.profile) {
7700
+ const profile2 = getProfile(opts.profile, opts.tool ?? targetTool?.id);
7701
+ if (targetTool && profile2.tool !== targetTool.id) {
7702
+ throw new AccountsError(`profile "${profile2.name}" belongs to ${profile2.tool}, not ${targetTool.id}`);
7703
+ }
7704
+ return { profile: profile2, tool: getTool(profile2.tool), targetKind: targetTool ? "tool" : "profile" };
7705
+ }
7706
+ if (targetTool && !opts.tool) {
7707
+ const profile2 = currentProfile(targetTool.id) ?? appliedProfile(targetTool.id);
7708
+ if (!profile2) {
7709
+ throw new AccountsError(`no active ${targetTool.label} profile. Run \`accounts use <name> --tool ${targetTool.id}\` or pass --profile.`);
7710
+ }
7711
+ return { profile: profile2, tool: targetTool, targetKind: "tool" };
7712
+ }
7713
+ const profile = getProfile(target, opts.tool);
7714
+ return { profile, tool: getTool(profile.tool), targetKind: "profile" };
7715
+ }
7716
+ function exitCode(code, signal) {
7717
+ if (code !== null)
7718
+ return code;
7719
+ if (signal === "SIGINT")
7720
+ return 130;
7721
+ if (signal === "SIGTERM")
7722
+ return 143;
7723
+ return signal ? 1 : 0;
7724
+ }
7725
+ function killChildProcess(child, signal) {
7726
+ if (!child.pid)
7727
+ return;
7728
+ if (process.platform !== "win32") {
7729
+ try {
7730
+ process.kill(-child.pid, signal);
7731
+ return;
7732
+ } catch {}
7733
+ }
7734
+ child.kill(signal);
7735
+ }
7736
+ function wait(ms) {
7737
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
7738
+ }
7739
+ async function listen(server, socketPath) {
7740
+ await new Promise((resolve3, reject) => {
7741
+ const onError = (err) => {
7742
+ server.off("listening", onListening);
7743
+ reject(err);
7744
+ };
7745
+ const onListening = () => {
7746
+ server.off("error", onError);
7747
+ resolve3();
7748
+ };
7749
+ server.once("error", onError);
7750
+ server.once("listening", onListening);
7751
+ server.listen(socketPath);
7752
+ });
7753
+ }
7754
+ async function sendSupervisorRequest(toolId, request, opts = {}) {
7755
+ const timeoutMs = opts.timeoutMs ?? 1500;
7756
+ const socketPath = supervisorSocketPath(toolId);
7757
+ return await new Promise((resolve3, reject) => {
7758
+ const socket = createConnection(socketPath);
7759
+ let buffer = "";
7760
+ let settled = false;
7761
+ const finish = (value) => {
7762
+ if (settled)
7763
+ return;
7764
+ settled = true;
7765
+ clearTimeout(timer);
7766
+ socket.destroy();
7767
+ resolve3(value);
7768
+ };
7769
+ const fail = (err) => {
7770
+ if (settled)
7771
+ return;
7772
+ settled = true;
7773
+ clearTimeout(timer);
7774
+ socket.destroy();
7775
+ if (opts.allowMissing && (err.code === "ENOENT" || err.code === "ECONNREFUSED")) {
7776
+ resolve3(undefined);
7777
+ } else {
7778
+ reject(new AccountsError(`could not contact accounts supervisor for ${toolId}: ${err.message}`));
7779
+ }
7780
+ };
7781
+ const timer = setTimeout(() => {
7782
+ fail(Object.assign(new Error(`timed out after ${timeoutMs}ms`), { code: "ETIMEDOUT" }));
7783
+ }, timeoutMs);
7784
+ socket.setEncoding("utf8");
7785
+ socket.once("connect", () => {
7786
+ socket.write(JSON.stringify(request) + `
7787
+ `);
7788
+ });
7789
+ socket.once("error", fail);
7790
+ socket.on("data", (chunk) => {
7791
+ buffer += chunk;
7792
+ const newline = buffer.indexOf(`
7793
+ `);
7794
+ if (newline === -1)
7795
+ return;
7796
+ try {
7797
+ finish(JSON.parse(buffer.slice(0, newline)));
7798
+ } catch (err) {
7799
+ fail(err);
7800
+ }
7801
+ });
7802
+ socket.once("end", () => {
7803
+ if (!settled)
7804
+ fail(new Error("connection closed without a response"));
7805
+ });
7806
+ });
7807
+ }
7808
+ async function runSupervisedTool(initialProfile, tool, initialArgs = [], opts = {}) {
7809
+ const socketPath = supervisorSocketPath(tool.id);
7810
+ const existing = readSupervisorState(tool.id);
7811
+ if (existing && processAlive(existing.pid)) {
7812
+ throw new AccountsError(`an accounts supervisor for ${tool.label} is already running (pid ${existing.pid})`);
7813
+ }
7814
+ removeSupervisorFiles(tool.id);
7815
+ mkdirSync7(supervisorDir(), { recursive: true });
7816
+ const startedAt = nowIso2();
7817
+ const restartDelayMs = opts.restartDelayMs ?? 350;
7818
+ const log = opts.log ?? (() => {
7819
+ return;
7820
+ });
7821
+ const server = createServer();
7822
+ let profile = initialProfile;
7823
+ let childArgs = initialArgs;
7824
+ let child;
7825
+ let stopping = false;
7826
+ let restarting = false;
7827
+ let settled = false;
7828
+ const state = () => ({
7829
+ version: 1,
7830
+ tool: tool.id,
7831
+ profile: profile.name,
7832
+ pid: process.pid,
7833
+ ...child?.pid ? { childPid: child.pid } : {},
7834
+ socketPath,
7835
+ command: [tool.bin, ...childArgs],
7836
+ startedAt,
7837
+ updatedAt: nowIso2()
7838
+ });
7839
+ const persist = () => writeSupervisorState(state());
7840
+ const stopChild = async () => {
7841
+ const target = child;
7842
+ if (!target || target.exitCode !== null)
7843
+ return;
7844
+ await new Promise((resolve3) => {
7845
+ let done2 = false;
7846
+ const finish = () => {
7847
+ if (done2)
7848
+ return;
7849
+ done2 = true;
7850
+ clearTimeout(killTimer);
7851
+ resolve3();
7852
+ };
7853
+ const killTimer = setTimeout(() => {
7854
+ try {
7855
+ killChildProcess(target, "SIGKILL");
7856
+ } catch {
7857
+ finish();
7858
+ }
7859
+ }, 2500);
7860
+ target.once("exit", finish);
7861
+ try {
7862
+ killChildProcess(target, "SIGTERM");
7863
+ } catch {
7864
+ finish();
7865
+ }
7866
+ });
7867
+ };
7868
+ const cleanup = () => {
7869
+ server.close();
7870
+ removeSupervisorFiles(tool.id);
7871
+ process.off("SIGINT", onSigint);
7872
+ process.off("SIGTERM", onSigterm);
7873
+ };
7874
+ let resolveRun;
7875
+ const done = new Promise((resolve3) => {
7876
+ resolveRun = resolve3;
7877
+ });
7878
+ const finishRun = (code) => {
7879
+ if (settled)
7880
+ return;
7881
+ settled = true;
7882
+ cleanup();
7883
+ resolveRun(code);
7884
+ };
7885
+ const startChild = (nextProfile, nextArgs) => {
7886
+ profile = nextProfile;
7887
+ childArgs = nextArgs;
7888
+ useProfile(profile.name, tool.id);
7889
+ const env2 = profileEnv(profile, tool);
7890
+ log(`accounts supervisor: starting ${tool.bin} for ${profile.name}`);
7891
+ const proc = spawn(tool.bin, childArgs, {
7892
+ stdio: opts.stdio ?? "inherit",
7893
+ env: { ...process.env, ...env2, ACCOUNTS_SUPERVISOR: "1", ACCOUNTS_ACTIVE: profile.name },
7894
+ detached: process.platform !== "win32"
7895
+ });
7896
+ child = proc;
7897
+ persist();
7898
+ proc.once("error", (err) => {
7899
+ log(`accounts supervisor: failed to start ${tool.bin}: ${err.message}`);
7900
+ if (!restarting && !stopping)
7901
+ finishRun(1);
7902
+ });
7903
+ proc.once("exit", (code, signal) => {
7904
+ if (child === proc)
7905
+ child = undefined;
7906
+ persist();
7907
+ if (restarting || stopping)
7908
+ return;
7909
+ finishRun(exitCode(code, signal));
7910
+ });
7911
+ };
7912
+ const restartWith = async (result) => {
7913
+ restarting = true;
7914
+ try {
7915
+ await wait(restartDelayMs);
7916
+ await stopChild();
7917
+ startChild(getProfile(result.profile.name, tool.id), result.command.slice(1));
7918
+ } finally {
7919
+ restarting = false;
7920
+ }
7921
+ };
7922
+ const shutdown = async (code) => {
7923
+ if (stopping)
7924
+ return;
7925
+ stopping = true;
7926
+ await stopChild();
7927
+ finishRun(code);
7928
+ };
7929
+ const handleRequest = async (request) => {
7930
+ if (request.type === "status")
7931
+ return { ok: true, state: state() };
7932
+ if (request.type === "stop") {
7933
+ setTimeout(() => void shutdown(0), 25);
7934
+ return { ok: true, stopping: true, state: state() };
7935
+ }
7936
+ if (request.type !== "switch_profile")
7937
+ return { ok: false, error: "unknown supervisor request" };
7938
+ if (request.tool && request.tool !== tool.id) {
7939
+ return { ok: false, error: `this supervisor runs ${tool.id}, not ${request.tool}` };
7940
+ }
7941
+ try {
7942
+ const result = switchProfile(request.name, {
7943
+ tool: tool.id,
7944
+ mode: request.mode ?? "auto",
7945
+ resume: request.resume ?? true,
7946
+ args: request.args ?? []
7947
+ });
7948
+ log(`accounts supervisor: switching ${tool.id} to ${result.profile.name}`);
7949
+ setTimeout(() => void restartWith(result), 0);
7950
+ return { ok: true, queued: true, result, state: state(), restartDelayMs };
7951
+ } catch (err) {
7952
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
7953
+ }
7954
+ };
7955
+ server.on("connection", (socket) => {
7956
+ socket.setEncoding("utf8");
7957
+ let buffer = "";
7958
+ socket.on("data", (chunk) => {
7959
+ buffer += chunk;
7960
+ const newline = buffer.indexOf(`
7961
+ `);
7962
+ if (newline === -1)
7963
+ return;
7964
+ const line = buffer.slice(0, newline);
7965
+ buffer = buffer.slice(newline + 1);
7966
+ (async () => {
7967
+ let response;
7968
+ try {
7969
+ response = await handleRequest(JSON.parse(line));
7970
+ } catch (err) {
7971
+ response = { ok: false, error: err instanceof Error ? err.message : String(err) };
7972
+ }
7973
+ socket.end(JSON.stringify(response) + `
7974
+ `);
7975
+ })();
7976
+ });
7977
+ });
7978
+ const onSigint = () => void shutdown(130);
7979
+ const onSigterm = () => void shutdown(143);
7980
+ process.once("SIGINT", onSigint);
7981
+ process.once("SIGTERM", onSigterm);
7982
+ await listen(server, socketPath);
7983
+ startChild(profile, childArgs);
7984
+ return await done;
7985
+ }
7986
+
7578
7987
  // src/cli.ts
7579
7988
  var program2 = new Command;
7580
7989
  function die(message) {
@@ -7584,7 +7993,11 @@ function die(message) {
7584
7993
  function action(fn) {
7585
7994
  return (...args) => {
7586
7995
  try {
7587
- fn(...args);
7996
+ Promise.resolve(fn(...args)).catch((err) => {
7997
+ if (err instanceof AccountsError)
7998
+ die(err.message);
7999
+ throw err;
8000
+ });
7588
8001
  } catch (err) {
7589
8002
  if (err instanceof AccountsError)
7590
8003
  die(err.message);
@@ -7637,7 +8050,7 @@ program2.command("show").argument("<name>", "profile name").description("show fu
7637
8050
  console.log(` tool: ${p.tool} (${getTool(p.tool).label})`);
7638
8051
  console.log(` active: ${active ? source_default.green("yes") : source_default.dim("no")}`);
7639
8052
  console.log(` applied: ${isApplied ? source_default.magenta("yes") : source_default.dim("no")}`);
7640
- console.log(` config dir: ${p.dir}${existsSync9(p.dir) ? "" : source_default.red(" [missing]")}`);
8053
+ console.log(` config dir: ${p.dir}${existsSync10(p.dir) ? "" : source_default.red(" [missing]")}`);
7641
8054
  console.log(` email: ${p.email ?? source_default.dim("(none)")}`);
7642
8055
  console.log(` created: ${p.createdAt}`);
7643
8056
  if (p.lastUsedAt)
@@ -7730,8 +8143,52 @@ program2.command("applied").argument("[tool]", "tool id (default: claude)").desc
7730
8143
  die(`no applied profile for "${tool}". Run \`accounts apply <name>\` first.`);
7731
8144
  console.log(p.name);
7732
8145
  }));
8146
+ program2.command("switch").argument("<name>", "profile name").argument("[args...]", "extra args passed when printing/launching the tool").description("switch to a profile and print a restart/resume command; use --launch to run it").option("-t, --tool <tool>", "tool when the profile name exists for multiple tools").option("--mode <mode>", "switch mode: auto, apply, env, active", "auto").option("--resume", "include the tool's resume/continue args in the handoff command").option("--launch", "launch the tool after switching").option("--supervisor", "ask a running accounts supervisor to restart the tool").option("--json", "output JSON").action(action(async (name, args, opts) => {
8147
+ if (opts.supervisor && opts.launch)
8148
+ die("--supervisor and --launch cannot be used together");
8149
+ if (opts.supervisor) {
8150
+ const profile = getProfile(name, opts.tool);
8151
+ const response = await sendSupervisorRequest(profile.tool, { type: "switch_profile", name: profile.name, tool: profile.tool, mode: opts.mode, resume: opts.resume ?? true, args }, { allowMissing: true });
8152
+ if (!response) {
8153
+ die(`no running accounts supervisor for ${getTool(profile.tool).label}. Start one with \`accounts run ${profile.tool}\`.`);
8154
+ }
8155
+ if (!response.ok)
8156
+ die(response.error);
8157
+ if (opts.json) {
8158
+ console.log(JSON.stringify(response, null, 2));
8159
+ } else if ("queued" in response) {
8160
+ console.log(source_default.green(`✓ queued supervisor switch to ${source_default.bold(response.result.profile.name)}`));
8161
+ console.log(source_default.dim(` ${response.state.command.join(" ")} will restart in ${response.restartDelayMs}ms`));
8162
+ } else {
8163
+ console.log(source_default.green("✓ supervisor responded"));
8164
+ }
8165
+ return;
8166
+ }
8167
+ const result = switchProfile(name, { tool: opts.tool, mode: opts.mode, resume: opts.resume, args });
8168
+ if (opts.json) {
8169
+ console.log(JSON.stringify(result, null, 2));
8170
+ } else {
8171
+ console.log(source_default.green(`✓ ${result.message}`));
8172
+ if (result.applied)
8173
+ console.log(source_default.dim(" live/default auth updated"));
8174
+ console.log(source_default.dim(` restart command: ${result.commandLine}`));
8175
+ if (!opts.launch) {
8176
+ console.log(source_default.yellow(" Exit the current agent session, then run the restart command above."));
8177
+ }
8178
+ }
8179
+ if (opts.launch) {
8180
+ const [bin, ...launchArgs] = result.command;
8181
+ const res = spawnSync(bin, launchArgs, {
8182
+ stdio: "inherit",
8183
+ env: { ...process.env, ...result.env }
8184
+ });
8185
+ if (res.error)
8186
+ die(`failed to launch ${bin}: ${res.error.message}`);
8187
+ process.exit(res.status ?? 0);
8188
+ }
8189
+ }));
7733
8190
  var hook = program2.command("hook").description("install a shell wrapper for claude");
7734
- hook.command("install").description(`write ${join10(accountsHome(), "claude-hook.sh")}`).action(action(() => {
8191
+ hook.command("install").description(`write ${join11(accountsHome(), "claude-hook.sh")}`).action(action(() => {
7735
8192
  const { path, created } = installHook();
7736
8193
  console.log(source_default.green(created ? `✓ installed hook at ${path}` : `✓ updated hook at ${path}`));
7737
8194
  console.log(source_default.dim(` add to ~/.zshrc: ${shellSnippet()}`));
@@ -7753,7 +8210,7 @@ program2.command("env").argument("[name]", "profile name (defaults to the active
7753
8210
  const tool = getTool(profile.tool);
7754
8211
  console.log(formatExportLines(profileEnv(profile, tool)));
7755
8212
  }));
7756
- program2.command("launch").alias("run").argument("<name>", "profile name").argument("[args...]", "extra args passed to the tool binary").description("launch the tool's binary with the profile's config dir active").option("-t, --tool <tool>", "tool when the profile name exists for multiple tools").action(action((name, args, opts) => {
8213
+ program2.command("launch").argument("<name>", "profile name").argument("[args...]", "extra args passed to the tool binary").description("launch the tool's binary with the profile's config dir active").option("-t, --tool <tool>", "tool when the profile name exists for multiple tools").action(action((name, args, opts) => {
7757
8214
  const profile = getProfile(name, opts.tool);
7758
8215
  const tool = getTool(profile.tool);
7759
8216
  const env2 = profileEnv(profile, tool);
@@ -7767,6 +8224,65 @@ program2.command("launch").alias("run").argument("<name>", "profile name").argum
7767
8224
  die(`failed to launch ${tool.bin}: ${res.error.message}`);
7768
8225
  process.exit(res.status ?? 0);
7769
8226
  }));
8227
+ program2.command("run").argument("<target>", "tool id to supervise (claude, codex, opencode...) or a profile name").argument("[args...]", "extra args passed to the tool binary").description("run a tool under the accounts supervisor so MCP can switch/restart it").option("-p, --profile <name>", "profile to run when target is a tool id").option("-t, --tool <tool>", "tool when target is a profile name").option("--resume", "start with the tool's resume/continue args").action(action(async (target, args, opts) => {
8228
+ const plan = resolveSupervisorLaunch(target, { profile: opts.profile, tool: opts.tool });
8229
+ const runArgs = [...opts.resume ? plan.tool.resumeArgs ?? [] : [], ...args];
8230
+ console.error(source_default.green(`✓ accounts supervisor running ${plan.tool.label} as ${source_default.bold(plan.profile.name)}`));
8231
+ console.error(source_default.dim(` control: accounts supervisor status ${plan.tool.id}`));
8232
+ console.error(source_default.dim(` switch: accounts switch <profile> --tool ${plan.tool.id} --supervisor`));
8233
+ const code = await runSupervisedTool(plan.profile, plan.tool, runArgs, {
8234
+ log: (message) => console.error(source_default.dim(message))
8235
+ });
8236
+ process.exit(code);
8237
+ }));
8238
+ var supervisor = program2.command("supervisor").description("inspect and control accounts-run supervisors");
8239
+ supervisor.command("status").argument("[tool]", "tool id").description("show running supervisor state").option("--json", "output JSON").action(action(async (toolId, opts) => {
8240
+ const state = toolId ? readSupervisorState(toolId) : undefined;
8241
+ const states = toolId ? state ? [state] : [] : listSupervisorStates();
8242
+ const live = [];
8243
+ for (const state2 of states) {
8244
+ const response = await sendSupervisorRequest(state2.tool, { type: "status" }, { allowMissing: true });
8245
+ live.push(response?.ok && "state" in response ? response.state : { ...state2, stale: true });
8246
+ }
8247
+ if (opts.json) {
8248
+ console.log(JSON.stringify(live, null, 2));
8249
+ return;
8250
+ }
8251
+ if (live.length === 0) {
8252
+ console.log(source_default.dim("no accounts supervisors running"));
8253
+ return;
8254
+ }
8255
+ for (const state2 of live) {
8256
+ const stale = "stale" in state2 ? source_default.yellow(" stale") : "";
8257
+ const child = state2.childPid ? ` child:${state2.childPid}` : "";
8258
+ console.log(`${source_default.cyan(state2.tool.padEnd(10))} ${source_default.bold(state2.profile)} pid:${state2.pid}${child}${stale}`);
8259
+ console.log(source_default.dim(` ${state2.command.join(" ")}`));
8260
+ }
8261
+ }));
8262
+ supervisor.command("switch").argument("<name>", "profile name").argument("[args...]", "extra args passed after resume/continue args").description("switch a running supervisor to another profile").option("-t, --tool <tool>", "tool when the profile name exists for multiple tools").option("--mode <mode>", "switch mode: auto, apply, env, active", "auto").option("--no-resume", "restart without the tool's resume/continue args").option("--json", "output JSON").action(action(async (name, args, opts) => {
8263
+ const profile = getProfile(name, opts.tool);
8264
+ const response = await sendSupervisorRequest(profile.tool, { type: "switch_profile", name: profile.name, tool: profile.tool, mode: opts.mode, resume: opts.resume !== false, args }, { allowMissing: true });
8265
+ if (!response)
8266
+ die(`no running accounts supervisor for ${getTool(profile.tool).label}`);
8267
+ if (!response.ok)
8268
+ die(response.error);
8269
+ if (opts.json) {
8270
+ console.log(JSON.stringify(response, null, 2));
8271
+ return;
8272
+ }
8273
+ if ("queued" in response) {
8274
+ console.log(source_default.green(`✓ queued supervisor switch to ${source_default.bold(response.result.profile.name)}`));
8275
+ console.log(source_default.dim(` restart command: ${response.result.commandLine}`));
8276
+ }
8277
+ }));
8278
+ supervisor.command("stop").argument("<tool>", "tool id").description("stop a running supervisor and its child process").action(action(async (toolId) => {
8279
+ const response = await sendSupervisorRequest(toolId, { type: "stop" }, { allowMissing: true });
8280
+ if (!response)
8281
+ die(`no running accounts supervisor for ${toolId}`);
8282
+ if (!response.ok)
8283
+ die(response.error);
8284
+ console.log(source_default.green(`✓ stopping ${toolId} supervisor`));
8285
+ }));
7770
8286
  program2.command("shell").argument("<name>", "profile name").description("open a subshell with the profile's config dir active").option("-t, --tool <tool>", "tool when the profile name exists for multiple tools").action(action((name, opts) => {
7771
8287
  const profile = getProfile(name, opts.tool);
7772
8288
  const tool = getTool(profile.tool);
@@ -7829,7 +8345,7 @@ tools.command("list", { isDefault: true }).description("list supported tools (bu
7829
8345
  console.log(`${source_default.cyan(t.id.padEnd(10))} ${t.label.padEnd(16)} ${source_default.dim(envNames)} → ${source_default.dim(t.defaultDir)} ${tag}`);
7830
8346
  }
7831
8347
  }));
7832
- tools.command("add").argument("<id>", "tool id, e.g. cursor").description("register a custom tool/app so profiles can target it").requiredOption("--label <label>", 'display name, e.g. "Cursor"').requiredOption("--env-var <VAR>", "env var that points the tool at its config dir").requiredOption("--bin <bin>", "binary to launch").option("--default-dir <path>", "default config dir (default: ~/.<id>)").option("--extra-env <VAR=VALUE...>", "additional env var templates; supports {profileDir}, {profileName}, {toolId}").option("--login-arg <arg...>", "arguments for `accounts login <profile> --tool <id>`").option("--account-file <file>", "file inside the config dir holding the email").option("--email-path <path>", "dot-path to the email inside that file (e.g. account.email)").action(action((id, opts) => {
8348
+ tools.command("add").argument("<id>", "tool id, e.g. cursor").description("register a custom tool/app so profiles can target it").requiredOption("--label <label>", 'display name, e.g. "Cursor"').requiredOption("--env-var <VAR>", "env var that points the tool at its config dir").requiredOption("--bin <bin>", "binary to launch").option("--default-dir <path>", "default config dir (default: ~/.<id>)").option("--extra-env <VAR=VALUE...>", "additional env var templates; supports {profileDir}, {profileName}, {toolId}").option("--login-arg <arg...>", "arguments for `accounts login <profile> --tool <id>`").option("--resume-arg <arg...>", "arguments for supervised resume/restart, e.g. --continue").option("--account-file <file>", "file inside the config dir holding the email").option("--email-path <path>", "dot-path to the email inside that file (e.g. account.email)").action(action((id, opts) => {
7833
8349
  const extraEnv = {};
7834
8350
  for (const entry of opts.extraEnv ?? []) {
7835
8351
  const idx = entry.indexOf("=");
@@ -7842,9 +8358,10 @@ tools.command("add").argument("<id>", "tool id, e.g. cursor").description("regis
7842
8358
  label: opts.label,
7843
8359
  envVar: opts.envVar,
7844
8360
  bin: opts.bin,
7845
- defaultDir: opts.defaultDir ? expandPath(opts.defaultDir) : join10(homedir5(), `.${id}`),
8361
+ defaultDir: opts.defaultDir ? expandPath(opts.defaultDir) : join11(homedir5(), `.${id}`),
7846
8362
  ...Object.keys(extraEnv).length > 0 ? { extraEnv } : {},
7847
8363
  ...opts.loginArg ? { loginArgs: opts.loginArg } : {},
8364
+ ...opts.resumeArg ? { resumeArgs: opts.resumeArg } : {},
7848
8365
  ...opts.accountFile ? { accountFile: opts.accountFile } : {},
7849
8366
  ...opts.emailPath ? { emailPath: opts.emailPath.split(".") } : {}
7850
8367
  };
@@ -7862,7 +8379,7 @@ program2.command("doctor").description("check the store and profile dirs for pro
7862
8379
  const profiles = listProfiles();
7863
8380
  let problems = 0;
7864
8381
  for (const p of profiles) {
7865
- const missing = !existsSync9(p.dir);
8382
+ const missing = !existsSync10(p.dir);
7866
8383
  const noEmail = !p.email;
7867
8384
  if (missing) {
7868
8385
  console.log(source_default.red(` ✗ ${p.name}: config dir missing (${p.dir})`));
@@ -7910,9 +8427,9 @@ program2.parseAsync(process.argv);
7910
8427
  function getVersion() {
7911
8428
  try {
7912
8429
  const here = dirname5(fileURLToPath(import.meta.url));
7913
- for (const candidate of [join10(here, "..", "package.json"), join10(here, "package.json")]) {
7914
- if (existsSync9(candidate)) {
7915
- const pkg = JSON.parse(readFileSync5(candidate, "utf8"));
8430
+ for (const candidate of [join11(here, "..", "package.json"), join11(here, "package.json")]) {
8431
+ if (existsSync10(candidate)) {
8432
+ const pkg = JSON.parse(readFileSync6(candidate, "utf8"));
7916
8433
  if (pkg.version)
7917
8434
  return pkg.version;
7918
8435
  }
package/dist/index.d.ts CHANGED
@@ -10,6 +10,10 @@ export { importProfile, ensureProfileForLogin } from "./lib/import-profile.js";
10
10
  export type { ImportOptions } from "./lib/import-profile.js";
11
11
  export { finalizeLogin } from "./lib/login.js";
12
12
  export type { FinalizeLoginResult } from "./lib/login.js";
13
+ export { switchProfile } from "./lib/switch.js";
14
+ export type { SwitchMode, SwitchOptions, SwitchResult } from "./lib/switch.js";
15
+ export { listSupervisorStates, readSupervisorState, resolveSupervisorLaunch, runSupervisedTool, sendSupervisorRequest, supervisorDir, supervisorSocketPath, supervisorStatePath, } from "./lib/supervisor.js";
16
+ export type { RunSupervisorOptions, SupervisorClientOptions, SupervisorLaunchPlan, SupervisorRequest, SupervisorResponse, SupervisorState, } from "./lib/supervisor.js";
13
17
  export { pickProfile } from "./lib/pick.js";
14
18
  export type { PickOptions, PickResult } from "./lib/pick.js";
15
19
  export { installHook, uninstallHook, hookPath, hookScript, shellSnippet } from "./lib/hook.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,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,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"}