@hasna/accounts 0.1.4 → 0.1.6

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/mcp.js CHANGED
@@ -16811,6 +16811,25 @@ var BUILTIN_TOOLS = [
16811
16811
  loginHint: "complete the Codex login flow for this CODEX_HOME",
16812
16812
  resumeArgs: ["resume", "--last"]
16813
16813
  },
16814
+ {
16815
+ id: "takumi",
16816
+ label: "Takumi",
16817
+ envVar: "TAKUMI_CONFIG_DIR",
16818
+ defaultDir: join2(homedir2(), ".takumi"),
16819
+ bin: "takumi",
16820
+ loginHint: "complete Takumi auth in this TAKUMI_CONFIG_DIR",
16821
+ resumeArgs: ["--continue"],
16822
+ accountFile: ".claude.json",
16823
+ emailPath: ["oauthAccount", "emailAddress"]
16824
+ },
16825
+ {
16826
+ id: "gemini",
16827
+ label: "Gemini CLI",
16828
+ envVar: "GEMINI_CONFIG_DIR",
16829
+ defaultDir: join2(homedir2(), ".gemini"),
16830
+ bin: "gemini",
16831
+ loginHint: "complete Gemini auth in this GEMINI_CONFIG_DIR"
16832
+ },
16814
16833
  {
16815
16834
  id: "opencode",
16816
16835
  label: "opencode",
@@ -16834,6 +16853,22 @@ var BUILTIN_TOOLS = [
16834
16853
  loginArgs: ["login"],
16835
16854
  loginHint: "complete cursor-agent login for this CURSOR_CONFIG_DIR"
16836
16855
  },
16856
+ {
16857
+ id: "pi",
16858
+ label: "Pi Coding Agent",
16859
+ envVar: "PI_CODING_AGENT_HOME",
16860
+ defaultDir: join2(homedir2(), ".pi"),
16861
+ bin: "pi",
16862
+ loginHint: "complete Pi coding agent auth in this PI_CODING_AGENT_HOME"
16863
+ },
16864
+ {
16865
+ id: "hermes",
16866
+ label: "Hermes",
16867
+ envVar: "HERMES_HOME",
16868
+ defaultDir: join2(homedir2(), ".hermes"),
16869
+ bin: "hermes",
16870
+ loginHint: "complete Hermes auth in this HERMES_HOME"
16871
+ },
16837
16872
  {
16838
16873
  id: "kimi",
16839
16874
  label: "Kimi Code",
@@ -17295,6 +17330,103 @@ function switchProfile(name, opts = {}) {
17295
17330
  };
17296
17331
  }
17297
17332
 
17333
+ // src/lib/supervisor.ts
17334
+ import { createHash } from "node:crypto";
17335
+ import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync3, readdirSync, rmSync, writeFileSync as writeFileSync4 } from "node:fs";
17336
+ import { createConnection, createServer } from "node:net";
17337
+ import { basename, join as join6 } from "node:path";
17338
+ var STATE_SUFFIX = ".json";
17339
+ function supervisorDir() {
17340
+ return join6(accountsHome(), "supervisors");
17341
+ }
17342
+ function supervisorStatePath(toolId) {
17343
+ return join6(supervisorDir(), `${toolId}${STATE_SUFFIX}`);
17344
+ }
17345
+ function supervisorSocketPath(toolId) {
17346
+ if (process.platform === "win32") {
17347
+ const hash = createHash("sha1").update(accountsHome()).digest("hex").slice(0, 12);
17348
+ return `\\\\.\\pipe\\hasna-accounts-${hash}-${toolId}`;
17349
+ }
17350
+ return join6(supervisorDir(), `${toolId}.sock`);
17351
+ }
17352
+ function parseState(raw) {
17353
+ const data = JSON.parse(raw);
17354
+ if (data.version !== 1 || typeof data.tool !== "string" || typeof data.profile !== "string" || typeof data.pid !== "number" || typeof data.socketPath !== "string" || !Array.isArray(data.command)) {
17355
+ return;
17356
+ }
17357
+ return data;
17358
+ }
17359
+ function readSupervisorState(toolId) {
17360
+ const path = supervisorStatePath(toolId);
17361
+ if (!existsSync5(path))
17362
+ return;
17363
+ try {
17364
+ return parseState(readFileSync3(path, "utf8"));
17365
+ } catch {
17366
+ return;
17367
+ }
17368
+ }
17369
+ function listSupervisorStates() {
17370
+ const dir = supervisorDir();
17371
+ if (!existsSync5(dir))
17372
+ return [];
17373
+ return readdirSync(dir).filter((name) => name.endsWith(STATE_SUFFIX)).map((name) => basename(name, STATE_SUFFIX)).map((toolId) => readSupervisorState(toolId)).filter((state) => state !== undefined);
17374
+ }
17375
+ async function sendSupervisorRequest(toolId, request, opts = {}) {
17376
+ const timeoutMs = opts.timeoutMs ?? 1500;
17377
+ const socketPath = supervisorSocketPath(toolId);
17378
+ return await new Promise((resolve2, reject) => {
17379
+ const socket = createConnection(socketPath);
17380
+ let buffer = "";
17381
+ let settled = false;
17382
+ const finish = (value) => {
17383
+ if (settled)
17384
+ return;
17385
+ settled = true;
17386
+ clearTimeout(timer);
17387
+ socket.destroy();
17388
+ resolve2(value);
17389
+ };
17390
+ const fail = (err) => {
17391
+ if (settled)
17392
+ return;
17393
+ settled = true;
17394
+ clearTimeout(timer);
17395
+ socket.destroy();
17396
+ if (opts.allowMissing && (err.code === "ENOENT" || err.code === "ECONNREFUSED")) {
17397
+ resolve2(undefined);
17398
+ } else {
17399
+ reject(new AccountsError(`could not contact accounts supervisor for ${toolId}: ${err.message}`));
17400
+ }
17401
+ };
17402
+ const timer = setTimeout(() => {
17403
+ fail(Object.assign(new Error(`timed out after ${timeoutMs}ms`), { code: "ETIMEDOUT" }));
17404
+ }, timeoutMs);
17405
+ socket.setEncoding("utf8");
17406
+ socket.once("connect", () => {
17407
+ socket.write(JSON.stringify(request) + `
17408
+ `);
17409
+ });
17410
+ socket.once("error", fail);
17411
+ socket.on("data", (chunk) => {
17412
+ buffer += chunk;
17413
+ const newline = buffer.indexOf(`
17414
+ `);
17415
+ if (newline === -1)
17416
+ return;
17417
+ try {
17418
+ finish(JSON.parse(buffer.slice(0, newline)));
17419
+ } catch (err) {
17420
+ fail(err);
17421
+ }
17422
+ });
17423
+ socket.once("end", () => {
17424
+ if (!settled)
17425
+ fail(new Error("connection closed without a response"));
17426
+ });
17427
+ });
17428
+ }
17429
+
17298
17430
  // src/mcp.ts
17299
17431
  function ok(data) {
17300
17432
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
@@ -17302,7 +17434,7 @@ function ok(data) {
17302
17434
  function fail(message) {
17303
17435
  return { content: [{ type: "text", text: JSON.stringify({ error: message }) }], isError: true };
17304
17436
  }
17305
- var server = new Server({ name: "accounts", version: "0.1.4" }, { capabilities: { tools: {} } });
17437
+ var server = new Server({ name: "accounts", version: "0.1.6" }, { capabilities: { tools: {} } });
17306
17438
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
17307
17439
  tools: [
17308
17440
  {
@@ -17320,9 +17452,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
17320
17452
  description: "Show active and live/applied profile for a tool.",
17321
17453
  inputSchema: { type: "object", properties: { tool: { type: "string" } }, required: ["tool"] }
17322
17454
  },
17455
+ {
17456
+ name: "supervisor_status",
17457
+ description: "Show accounts-run supervisors that can restart an agent process after profile switches.",
17458
+ inputSchema: { type: "object", properties: { tool: { type: "string" } } }
17459
+ },
17323
17460
  {
17324
17461
  name: "switch_profile",
17325
- description: "Switch to a profile. For Claude this applies live/default auth. Returns restart/resume handoff command; MCP does not kill the parent agent process.",
17462
+ description: "Switch to a profile. If the current agent was started with accounts run, the supervisor restarts it under the new profile; otherwise this returns a restart/resume handoff command.",
17326
17463
  inputSchema: {
17327
17464
  type: "object",
17328
17465
  properties: {
@@ -17351,17 +17488,43 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
17351
17488
  return fail("tool is required");
17352
17489
  return ok({ tool, active: currentProfile(tool) ?? null, applied: appliedProfile(tool) ?? null });
17353
17490
  }
17491
+ case "supervisor_status": {
17492
+ const tool = args["tool"];
17493
+ const states = typeof tool === "string" ? listSupervisorStates().filter((state) => state.tool === tool) : listSupervisorStates();
17494
+ return ok({ supervisors: states });
17495
+ }
17354
17496
  case "switch_profile": {
17355
17497
  const name = args["name"];
17356
17498
  if (typeof name !== "string")
17357
17499
  return fail("name is required");
17500
+ const profile = getProfile(name, typeof args["tool"] === "string" ? args["tool"] : undefined);
17501
+ const resume = args["resume"] !== false;
17502
+ const switchArgs = Array.isArray(args["args"]) ? args["args"].filter((value) => typeof value === "string") : undefined;
17503
+ const supervisor = await sendSupervisorRequest(profile.tool, {
17504
+ type: "switch_profile",
17505
+ name: profile.name,
17506
+ tool: profile.tool,
17507
+ mode: typeof args["mode"] === "string" ? args["mode"] : "auto",
17508
+ resume,
17509
+ args: switchArgs
17510
+ }, { allowMissing: true });
17511
+ if (supervisor) {
17512
+ if (!supervisor.ok)
17513
+ return fail(supervisor.error);
17514
+ return ok({
17515
+ supervised: true,
17516
+ ...supervisor,
17517
+ instruction: "Profile switch queued. The accounts supervisor will close this agent process and restart it under the selected profile."
17518
+ });
17519
+ }
17358
17520
  const result = switchProfile(name, {
17359
- tool: typeof args["tool"] === "string" ? args["tool"] : undefined,
17521
+ tool: profile.tool,
17360
17522
  mode: typeof args["mode"] === "string" ? args["mode"] : "auto",
17361
- resume: args["resume"] === true,
17362
- args: Array.isArray(args["args"]) ? args["args"].filter((value) => typeof value === "string") : undefined
17523
+ resume,
17524
+ args: switchArgs
17363
17525
  });
17364
17526
  return ok({
17527
+ supervised: false,
17365
17528
  ...result,
17366
17529
  instruction: result.restartRequired ? "Exit the current agent session and run commandLine to resume under the selected profile." : "Profile switched."
17367
17530
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/accounts",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
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",
@@ -30,8 +30,12 @@
30
30
  "claude",
31
31
  "claude-code",
32
32
  "codex",
33
+ "takumi",
34
+ "gemini",
33
35
  "opencode",
34
36
  "cursor-agent",
37
+ "pi",
38
+ "hermes",
35
39
  "kimi-code",
36
40
  "grok-build",
37
41
  "profile",