@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/README.md +60 -1
- package/dist/cli.js +531 -14
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +426 -10
- package/dist/lib/supervisor.d.ts +69 -0
- package/dist/lib/supervisor.d.ts.map +1 -0
- package/dist/lib/switch.d.ts +22 -0
- package/dist/lib/switch.d.ts.map +1 -0
- package/dist/lib/tools.d.ts.map +1 -1
- package/dist/mcp.d.ts +3 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +17506 -0
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -3
package/dist/index.js
CHANGED
|
@@ -3998,6 +3998,7 @@ var toolDefSchema = exports_external.object({
|
|
|
3998
3998
|
bin: exports_external.string().min(1),
|
|
3999
3999
|
loginArgs: exports_external.array(exports_external.string()).optional(),
|
|
4000
4000
|
loginHint: exports_external.string().optional(),
|
|
4001
|
+
resumeArgs: exports_external.array(exports_external.string()).optional(),
|
|
4001
4002
|
accountFile: exports_external.string().optional(),
|
|
4002
4003
|
emailPath: exports_external.array(exports_external.string()).optional()
|
|
4003
4004
|
});
|
|
@@ -4162,6 +4163,7 @@ var BUILTIN_TOOLS = [
|
|
|
4162
4163
|
defaultDir: join2(homedir2(), ".claude"),
|
|
4163
4164
|
bin: "claude",
|
|
4164
4165
|
loginHint: "run /login inside Claude, then /exit when done",
|
|
4166
|
+
resumeArgs: ["--continue"],
|
|
4165
4167
|
accountFile: ".claude.json",
|
|
4166
4168
|
emailPath: ["oauthAccount", "emailAddress"]
|
|
4167
4169
|
},
|
|
@@ -4172,7 +4174,8 @@ var BUILTIN_TOOLS = [
|
|
|
4172
4174
|
defaultDir: join2(homedir2(), ".codex"),
|
|
4173
4175
|
bin: "codex",
|
|
4174
4176
|
loginArgs: ["login"],
|
|
4175
|
-
loginHint: "complete the Codex login flow for this CODEX_HOME"
|
|
4177
|
+
loginHint: "complete the Codex login flow for this CODEX_HOME",
|
|
4178
|
+
resumeArgs: ["resume", "--last"]
|
|
4176
4179
|
},
|
|
4177
4180
|
{
|
|
4178
4181
|
id: "opencode",
|
|
@@ -4185,7 +4188,8 @@ var BUILTIN_TOOLS = [
|
|
|
4185
4188
|
defaultDir: join2(homedir2(), ".config", "opencode"),
|
|
4186
4189
|
bin: "opencode",
|
|
4187
4190
|
loginArgs: ["auth", "login"],
|
|
4188
|
-
loginHint: "complete opencode auth login for this isolated config/data root"
|
|
4191
|
+
loginHint: "complete opencode auth login for this isolated config/data root",
|
|
4192
|
+
resumeArgs: ["--continue"]
|
|
4189
4193
|
},
|
|
4190
4194
|
{
|
|
4191
4195
|
id: "cursor",
|
|
@@ -4891,6 +4895,409 @@ function finalizeLogin(name, toolId) {
|
|
|
4891
4895
|
useProfile(name, tool.id);
|
|
4892
4896
|
return { profile: updated, applied: false };
|
|
4893
4897
|
}
|
|
4898
|
+
// src/lib/switch.ts
|
|
4899
|
+
function shellQuote(value) {
|
|
4900
|
+
if (/^[A-Za-z0-9_./:=@+-]+$/.test(value))
|
|
4901
|
+
return value;
|
|
4902
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
4903
|
+
}
|
|
4904
|
+
function commandLine(env, command) {
|
|
4905
|
+
return `${formatEnvAssignments(env)} ${command.map(shellQuote).join(" ")}`.trim();
|
|
4906
|
+
}
|
|
4907
|
+
function commandFor(tool, opts) {
|
|
4908
|
+
return [tool.bin, ...opts.resume ? tool.resumeArgs ?? [] : [], ...opts.args ?? []];
|
|
4909
|
+
}
|
|
4910
|
+
function switchProfile(name, opts = {}) {
|
|
4911
|
+
const profile = getProfile(name, opts.tool);
|
|
4912
|
+
const tool = getTool(profile.tool);
|
|
4913
|
+
const mode = opts.mode ?? "auto";
|
|
4914
|
+
if (!["auto", "apply", "env", "active"].includes(mode)) {
|
|
4915
|
+
throw new AccountsError(`invalid switch mode "${mode}"`);
|
|
4916
|
+
}
|
|
4917
|
+
const env = profileEnv(profile, tool);
|
|
4918
|
+
let applied = false;
|
|
4919
|
+
if (mode === "apply" || mode === "auto" && tool.id === "claude") {
|
|
4920
|
+
applyProfile(name, tool.id);
|
|
4921
|
+
applied = true;
|
|
4922
|
+
} else {
|
|
4923
|
+
useProfile(name, tool.id);
|
|
4924
|
+
}
|
|
4925
|
+
const command = commandFor(tool, opts);
|
|
4926
|
+
const restartRequired = opts.resume === true || applied || mode === "env";
|
|
4927
|
+
const message = applied ? `${profile.name} is now the live/default ${tool.label} profile` : `${profile.name} is now the active ${tool.label} profile`;
|
|
4928
|
+
return {
|
|
4929
|
+
profile: getProfile(name, tool.id),
|
|
4930
|
+
tool,
|
|
4931
|
+
applied,
|
|
4932
|
+
active: true,
|
|
4933
|
+
env,
|
|
4934
|
+
exports: formatExportLines(env),
|
|
4935
|
+
command,
|
|
4936
|
+
commandLine: commandLine(env, command),
|
|
4937
|
+
restartRequired,
|
|
4938
|
+
message
|
|
4939
|
+
};
|
|
4940
|
+
}
|
|
4941
|
+
// src/lib/supervisor.ts
|
|
4942
|
+
import { spawn } from "node:child_process";
|
|
4943
|
+
import { createHash } from "node:crypto";
|
|
4944
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync4, readdirSync, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
4945
|
+
import { createConnection, createServer } from "node:net";
|
|
4946
|
+
import { basename, join as join9 } from "node:path";
|
|
4947
|
+
var STATE_SUFFIX = ".json";
|
|
4948
|
+
function supervisorDir() {
|
|
4949
|
+
return join9(accountsHome(), "supervisors");
|
|
4950
|
+
}
|
|
4951
|
+
function supervisorStatePath(toolId) {
|
|
4952
|
+
return join9(supervisorDir(), `${toolId}${STATE_SUFFIX}`);
|
|
4953
|
+
}
|
|
4954
|
+
function supervisorSocketPath(toolId) {
|
|
4955
|
+
if (process.platform === "win32") {
|
|
4956
|
+
const hash = createHash("sha1").update(accountsHome()).digest("hex").slice(0, 12);
|
|
4957
|
+
return `\\\\.\\pipe\\hasna-accounts-${hash}-${toolId}`;
|
|
4958
|
+
}
|
|
4959
|
+
return join9(supervisorDir(), `${toolId}.sock`);
|
|
4960
|
+
}
|
|
4961
|
+
function nowIso2() {
|
|
4962
|
+
return new Date().toISOString();
|
|
4963
|
+
}
|
|
4964
|
+
function parseState(raw) {
|
|
4965
|
+
const data = JSON.parse(raw);
|
|
4966
|
+
if (data.version !== 1 || typeof data.tool !== "string" || typeof data.profile !== "string" || typeof data.pid !== "number" || typeof data.socketPath !== "string" || !Array.isArray(data.command)) {
|
|
4967
|
+
return;
|
|
4968
|
+
}
|
|
4969
|
+
return data;
|
|
4970
|
+
}
|
|
4971
|
+
function readSupervisorState(toolId) {
|
|
4972
|
+
const path = supervisorStatePath(toolId);
|
|
4973
|
+
if (!existsSync8(path))
|
|
4974
|
+
return;
|
|
4975
|
+
try {
|
|
4976
|
+
return parseState(readFileSync4(path, "utf8"));
|
|
4977
|
+
} catch {
|
|
4978
|
+
return;
|
|
4979
|
+
}
|
|
4980
|
+
}
|
|
4981
|
+
function listSupervisorStates() {
|
|
4982
|
+
const dir = supervisorDir();
|
|
4983
|
+
if (!existsSync8(dir))
|
|
4984
|
+
return [];
|
|
4985
|
+
return readdirSync(dir).filter((name) => name.endsWith(STATE_SUFFIX)).map((name) => basename(name, STATE_SUFFIX)).map((toolId) => readSupervisorState(toolId)).filter((state) => state !== undefined);
|
|
4986
|
+
}
|
|
4987
|
+
function writeSupervisorState(state) {
|
|
4988
|
+
mkdirSync6(supervisorDir(), { recursive: true });
|
|
4989
|
+
writeFileSync4(supervisorStatePath(state.tool), JSON.stringify(state, null, 2) + `
|
|
4990
|
+
`, { mode: 384 });
|
|
4991
|
+
}
|
|
4992
|
+
function removeSupervisorFiles(toolId) {
|
|
4993
|
+
rmSync2(supervisorStatePath(toolId), { force: true });
|
|
4994
|
+
if (process.platform !== "win32")
|
|
4995
|
+
rmSync2(supervisorSocketPath(toolId), { force: true });
|
|
4996
|
+
}
|
|
4997
|
+
function processAlive(pid) {
|
|
4998
|
+
try {
|
|
4999
|
+
process.kill(pid, 0);
|
|
5000
|
+
return true;
|
|
5001
|
+
} catch {
|
|
5002
|
+
return false;
|
|
5003
|
+
}
|
|
5004
|
+
}
|
|
5005
|
+
function knownTool(id) {
|
|
5006
|
+
try {
|
|
5007
|
+
return getTool(id);
|
|
5008
|
+
} catch {
|
|
5009
|
+
return;
|
|
5010
|
+
}
|
|
5011
|
+
}
|
|
5012
|
+
function resolveSupervisorLaunch(target, opts = {}) {
|
|
5013
|
+
const targetTool = knownTool(target);
|
|
5014
|
+
if (opts.profile) {
|
|
5015
|
+
const profile2 = getProfile(opts.profile, opts.tool ?? targetTool?.id);
|
|
5016
|
+
if (targetTool && profile2.tool !== targetTool.id) {
|
|
5017
|
+
throw new AccountsError(`profile "${profile2.name}" belongs to ${profile2.tool}, not ${targetTool.id}`);
|
|
5018
|
+
}
|
|
5019
|
+
return { profile: profile2, tool: getTool(profile2.tool), targetKind: targetTool ? "tool" : "profile" };
|
|
5020
|
+
}
|
|
5021
|
+
if (targetTool && !opts.tool) {
|
|
5022
|
+
const profile2 = currentProfile(targetTool.id) ?? appliedProfile(targetTool.id);
|
|
5023
|
+
if (!profile2) {
|
|
5024
|
+
throw new AccountsError(`no active ${targetTool.label} profile. Run \`accounts use <name> --tool ${targetTool.id}\` or pass --profile.`);
|
|
5025
|
+
}
|
|
5026
|
+
return { profile: profile2, tool: targetTool, targetKind: "tool" };
|
|
5027
|
+
}
|
|
5028
|
+
const profile = getProfile(target, opts.tool);
|
|
5029
|
+
return { profile, tool: getTool(profile.tool), targetKind: "profile" };
|
|
5030
|
+
}
|
|
5031
|
+
function exitCode(code, signal) {
|
|
5032
|
+
if (code !== null)
|
|
5033
|
+
return code;
|
|
5034
|
+
if (signal === "SIGINT")
|
|
5035
|
+
return 130;
|
|
5036
|
+
if (signal === "SIGTERM")
|
|
5037
|
+
return 143;
|
|
5038
|
+
return signal ? 1 : 0;
|
|
5039
|
+
}
|
|
5040
|
+
function killChildProcess(child, signal) {
|
|
5041
|
+
if (!child.pid)
|
|
5042
|
+
return;
|
|
5043
|
+
if (process.platform !== "win32") {
|
|
5044
|
+
try {
|
|
5045
|
+
process.kill(-child.pid, signal);
|
|
5046
|
+
return;
|
|
5047
|
+
} catch {}
|
|
5048
|
+
}
|
|
5049
|
+
child.kill(signal);
|
|
5050
|
+
}
|
|
5051
|
+
function wait(ms) {
|
|
5052
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
5053
|
+
}
|
|
5054
|
+
async function listen(server, socketPath) {
|
|
5055
|
+
await new Promise((resolve3, reject) => {
|
|
5056
|
+
const onError = (err) => {
|
|
5057
|
+
server.off("listening", onListening);
|
|
5058
|
+
reject(err);
|
|
5059
|
+
};
|
|
5060
|
+
const onListening = () => {
|
|
5061
|
+
server.off("error", onError);
|
|
5062
|
+
resolve3();
|
|
5063
|
+
};
|
|
5064
|
+
server.once("error", onError);
|
|
5065
|
+
server.once("listening", onListening);
|
|
5066
|
+
server.listen(socketPath);
|
|
5067
|
+
});
|
|
5068
|
+
}
|
|
5069
|
+
async function sendSupervisorRequest(toolId, request, opts = {}) {
|
|
5070
|
+
const timeoutMs = opts.timeoutMs ?? 1500;
|
|
5071
|
+
const socketPath = supervisorSocketPath(toolId);
|
|
5072
|
+
return await new Promise((resolve3, reject) => {
|
|
5073
|
+
const socket = createConnection(socketPath);
|
|
5074
|
+
let buffer = "";
|
|
5075
|
+
let settled = false;
|
|
5076
|
+
const finish = (value) => {
|
|
5077
|
+
if (settled)
|
|
5078
|
+
return;
|
|
5079
|
+
settled = true;
|
|
5080
|
+
clearTimeout(timer);
|
|
5081
|
+
socket.destroy();
|
|
5082
|
+
resolve3(value);
|
|
5083
|
+
};
|
|
5084
|
+
const fail = (err) => {
|
|
5085
|
+
if (settled)
|
|
5086
|
+
return;
|
|
5087
|
+
settled = true;
|
|
5088
|
+
clearTimeout(timer);
|
|
5089
|
+
socket.destroy();
|
|
5090
|
+
if (opts.allowMissing && (err.code === "ENOENT" || err.code === "ECONNREFUSED")) {
|
|
5091
|
+
resolve3(undefined);
|
|
5092
|
+
} else {
|
|
5093
|
+
reject(new AccountsError(`could not contact accounts supervisor for ${toolId}: ${err.message}`));
|
|
5094
|
+
}
|
|
5095
|
+
};
|
|
5096
|
+
const timer = setTimeout(() => {
|
|
5097
|
+
fail(Object.assign(new Error(`timed out after ${timeoutMs}ms`), { code: "ETIMEDOUT" }));
|
|
5098
|
+
}, timeoutMs);
|
|
5099
|
+
socket.setEncoding("utf8");
|
|
5100
|
+
socket.once("connect", () => {
|
|
5101
|
+
socket.write(JSON.stringify(request) + `
|
|
5102
|
+
`);
|
|
5103
|
+
});
|
|
5104
|
+
socket.once("error", fail);
|
|
5105
|
+
socket.on("data", (chunk) => {
|
|
5106
|
+
buffer += chunk;
|
|
5107
|
+
const newline = buffer.indexOf(`
|
|
5108
|
+
`);
|
|
5109
|
+
if (newline === -1)
|
|
5110
|
+
return;
|
|
5111
|
+
try {
|
|
5112
|
+
finish(JSON.parse(buffer.slice(0, newline)));
|
|
5113
|
+
} catch (err) {
|
|
5114
|
+
fail(err);
|
|
5115
|
+
}
|
|
5116
|
+
});
|
|
5117
|
+
socket.once("end", () => {
|
|
5118
|
+
if (!settled)
|
|
5119
|
+
fail(new Error("connection closed without a response"));
|
|
5120
|
+
});
|
|
5121
|
+
});
|
|
5122
|
+
}
|
|
5123
|
+
async function runSupervisedTool(initialProfile, tool, initialArgs = [], opts = {}) {
|
|
5124
|
+
const socketPath = supervisorSocketPath(tool.id);
|
|
5125
|
+
const existing = readSupervisorState(tool.id);
|
|
5126
|
+
if (existing && processAlive(existing.pid)) {
|
|
5127
|
+
throw new AccountsError(`an accounts supervisor for ${tool.label} is already running (pid ${existing.pid})`);
|
|
5128
|
+
}
|
|
5129
|
+
removeSupervisorFiles(tool.id);
|
|
5130
|
+
mkdirSync6(supervisorDir(), { recursive: true });
|
|
5131
|
+
const startedAt = nowIso2();
|
|
5132
|
+
const restartDelayMs = opts.restartDelayMs ?? 350;
|
|
5133
|
+
const log = opts.log ?? (() => {
|
|
5134
|
+
return;
|
|
5135
|
+
});
|
|
5136
|
+
const server = createServer();
|
|
5137
|
+
let profile = initialProfile;
|
|
5138
|
+
let childArgs = initialArgs;
|
|
5139
|
+
let child;
|
|
5140
|
+
let stopping = false;
|
|
5141
|
+
let restarting = false;
|
|
5142
|
+
let settled = false;
|
|
5143
|
+
const state = () => ({
|
|
5144
|
+
version: 1,
|
|
5145
|
+
tool: tool.id,
|
|
5146
|
+
profile: profile.name,
|
|
5147
|
+
pid: process.pid,
|
|
5148
|
+
...child?.pid ? { childPid: child.pid } : {},
|
|
5149
|
+
socketPath,
|
|
5150
|
+
command: [tool.bin, ...childArgs],
|
|
5151
|
+
startedAt,
|
|
5152
|
+
updatedAt: nowIso2()
|
|
5153
|
+
});
|
|
5154
|
+
const persist = () => writeSupervisorState(state());
|
|
5155
|
+
const stopChild = async () => {
|
|
5156
|
+
const target = child;
|
|
5157
|
+
if (!target || target.exitCode !== null)
|
|
5158
|
+
return;
|
|
5159
|
+
await new Promise((resolve3) => {
|
|
5160
|
+
let done2 = false;
|
|
5161
|
+
const finish = () => {
|
|
5162
|
+
if (done2)
|
|
5163
|
+
return;
|
|
5164
|
+
done2 = true;
|
|
5165
|
+
clearTimeout(killTimer);
|
|
5166
|
+
resolve3();
|
|
5167
|
+
};
|
|
5168
|
+
const killTimer = setTimeout(() => {
|
|
5169
|
+
try {
|
|
5170
|
+
killChildProcess(target, "SIGKILL");
|
|
5171
|
+
} catch {
|
|
5172
|
+
finish();
|
|
5173
|
+
}
|
|
5174
|
+
}, 2500);
|
|
5175
|
+
target.once("exit", finish);
|
|
5176
|
+
try {
|
|
5177
|
+
killChildProcess(target, "SIGTERM");
|
|
5178
|
+
} catch {
|
|
5179
|
+
finish();
|
|
5180
|
+
}
|
|
5181
|
+
});
|
|
5182
|
+
};
|
|
5183
|
+
const cleanup = () => {
|
|
5184
|
+
server.close();
|
|
5185
|
+
removeSupervisorFiles(tool.id);
|
|
5186
|
+
process.off("SIGINT", onSigint);
|
|
5187
|
+
process.off("SIGTERM", onSigterm);
|
|
5188
|
+
};
|
|
5189
|
+
let resolveRun;
|
|
5190
|
+
const done = new Promise((resolve3) => {
|
|
5191
|
+
resolveRun = resolve3;
|
|
5192
|
+
});
|
|
5193
|
+
const finishRun = (code) => {
|
|
5194
|
+
if (settled)
|
|
5195
|
+
return;
|
|
5196
|
+
settled = true;
|
|
5197
|
+
cleanup();
|
|
5198
|
+
resolveRun(code);
|
|
5199
|
+
};
|
|
5200
|
+
const startChild = (nextProfile, nextArgs) => {
|
|
5201
|
+
profile = nextProfile;
|
|
5202
|
+
childArgs = nextArgs;
|
|
5203
|
+
useProfile(profile.name, tool.id);
|
|
5204
|
+
const env = profileEnv(profile, tool);
|
|
5205
|
+
log(`accounts supervisor: starting ${tool.bin} for ${profile.name}`);
|
|
5206
|
+
const proc = spawn(tool.bin, childArgs, {
|
|
5207
|
+
stdio: opts.stdio ?? "inherit",
|
|
5208
|
+
env: { ...process.env, ...env, ACCOUNTS_SUPERVISOR: "1", ACCOUNTS_ACTIVE: profile.name },
|
|
5209
|
+
detached: process.platform !== "win32"
|
|
5210
|
+
});
|
|
5211
|
+
child = proc;
|
|
5212
|
+
persist();
|
|
5213
|
+
proc.once("error", (err) => {
|
|
5214
|
+
log(`accounts supervisor: failed to start ${tool.bin}: ${err.message}`);
|
|
5215
|
+
if (!restarting && !stopping)
|
|
5216
|
+
finishRun(1);
|
|
5217
|
+
});
|
|
5218
|
+
proc.once("exit", (code, signal) => {
|
|
5219
|
+
if (child === proc)
|
|
5220
|
+
child = undefined;
|
|
5221
|
+
persist();
|
|
5222
|
+
if (restarting || stopping)
|
|
5223
|
+
return;
|
|
5224
|
+
finishRun(exitCode(code, signal));
|
|
5225
|
+
});
|
|
5226
|
+
};
|
|
5227
|
+
const restartWith = async (result) => {
|
|
5228
|
+
restarting = true;
|
|
5229
|
+
try {
|
|
5230
|
+
await wait(restartDelayMs);
|
|
5231
|
+
await stopChild();
|
|
5232
|
+
startChild(getProfile(result.profile.name, tool.id), result.command.slice(1));
|
|
5233
|
+
} finally {
|
|
5234
|
+
restarting = false;
|
|
5235
|
+
}
|
|
5236
|
+
};
|
|
5237
|
+
const shutdown = async (code) => {
|
|
5238
|
+
if (stopping)
|
|
5239
|
+
return;
|
|
5240
|
+
stopping = true;
|
|
5241
|
+
await stopChild();
|
|
5242
|
+
finishRun(code);
|
|
5243
|
+
};
|
|
5244
|
+
const handleRequest = async (request) => {
|
|
5245
|
+
if (request.type === "status")
|
|
5246
|
+
return { ok: true, state: state() };
|
|
5247
|
+
if (request.type === "stop") {
|
|
5248
|
+
setTimeout(() => void shutdown(0), 25);
|
|
5249
|
+
return { ok: true, stopping: true, state: state() };
|
|
5250
|
+
}
|
|
5251
|
+
if (request.type !== "switch_profile")
|
|
5252
|
+
return { ok: false, error: "unknown supervisor request" };
|
|
5253
|
+
if (request.tool && request.tool !== tool.id) {
|
|
5254
|
+
return { ok: false, error: `this supervisor runs ${tool.id}, not ${request.tool}` };
|
|
5255
|
+
}
|
|
5256
|
+
try {
|
|
5257
|
+
const result = switchProfile(request.name, {
|
|
5258
|
+
tool: tool.id,
|
|
5259
|
+
mode: request.mode ?? "auto",
|
|
5260
|
+
resume: request.resume ?? true,
|
|
5261
|
+
args: request.args ?? []
|
|
5262
|
+
});
|
|
5263
|
+
log(`accounts supervisor: switching ${tool.id} to ${result.profile.name}`);
|
|
5264
|
+
setTimeout(() => void restartWith(result), 0);
|
|
5265
|
+
return { ok: true, queued: true, result, state: state(), restartDelayMs };
|
|
5266
|
+
} catch (err) {
|
|
5267
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
5268
|
+
}
|
|
5269
|
+
};
|
|
5270
|
+
server.on("connection", (socket) => {
|
|
5271
|
+
socket.setEncoding("utf8");
|
|
5272
|
+
let buffer = "";
|
|
5273
|
+
socket.on("data", (chunk) => {
|
|
5274
|
+
buffer += chunk;
|
|
5275
|
+
const newline = buffer.indexOf(`
|
|
5276
|
+
`);
|
|
5277
|
+
if (newline === -1)
|
|
5278
|
+
return;
|
|
5279
|
+
const line = buffer.slice(0, newline);
|
|
5280
|
+
buffer = buffer.slice(newline + 1);
|
|
5281
|
+
(async () => {
|
|
5282
|
+
let response;
|
|
5283
|
+
try {
|
|
5284
|
+
response = await handleRequest(JSON.parse(line));
|
|
5285
|
+
} catch (err) {
|
|
5286
|
+
response = { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
5287
|
+
}
|
|
5288
|
+
socket.end(JSON.stringify(response) + `
|
|
5289
|
+
`);
|
|
5290
|
+
})();
|
|
5291
|
+
});
|
|
5292
|
+
});
|
|
5293
|
+
const onSigint = () => void shutdown(130);
|
|
5294
|
+
const onSigterm = () => void shutdown(143);
|
|
5295
|
+
process.once("SIGINT", onSigint);
|
|
5296
|
+
process.once("SIGTERM", onSigterm);
|
|
5297
|
+
await listen(server, socketPath);
|
|
5298
|
+
startChild(profile, childArgs);
|
|
5299
|
+
return await done;
|
|
5300
|
+
}
|
|
4894
5301
|
// src/lib/pick.ts
|
|
4895
5302
|
import * as readline from "node:readline/promises";
|
|
4896
5303
|
import { stdin as input, stdout as output } from "node:process";
|
|
@@ -4924,8 +5331,8 @@ async function pickProfile(opts = {}) {
|
|
|
4924
5331
|
return { profile, mode };
|
|
4925
5332
|
}
|
|
4926
5333
|
// src/lib/hook.ts
|
|
4927
|
-
import { existsSync as
|
|
4928
|
-
import { join as
|
|
5334
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync5, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5 } from "node:fs";
|
|
5335
|
+
import { join as join10 } from "node:path";
|
|
4929
5336
|
var HOOK_FILE = "claude-hook.sh";
|
|
4930
5337
|
var MARKER = "# accounts-claude-hook";
|
|
4931
5338
|
var NAME_PATTERN = "^[a-z0-9][a-z0-9-]*$";
|
|
@@ -4933,7 +5340,7 @@ function shellQuotePath(path) {
|
|
|
4933
5340
|
return `'${path.replace(/'/g, `'\\''`)}'`;
|
|
4934
5341
|
}
|
|
4935
5342
|
function hookPath() {
|
|
4936
|
-
return
|
|
5343
|
+
return join10(accountsHome(), HOOK_FILE);
|
|
4937
5344
|
}
|
|
4938
5345
|
function hookScript() {
|
|
4939
5346
|
const quotedHook = shellQuotePath(hookPath());
|
|
@@ -4963,16 +5370,16 @@ claude() {
|
|
|
4963
5370
|
}
|
|
4964
5371
|
function installHook() {
|
|
4965
5372
|
const path = hookPath();
|
|
4966
|
-
|
|
4967
|
-
const created = !
|
|
4968
|
-
|
|
5373
|
+
mkdirSync7(accountsHome(), { recursive: true });
|
|
5374
|
+
const created = !existsSync9(path);
|
|
5375
|
+
writeFileSync5(path, hookScript(), { mode: 493 });
|
|
4969
5376
|
return { path, created };
|
|
4970
5377
|
}
|
|
4971
5378
|
function uninstallHook() {
|
|
4972
5379
|
const path = hookPath();
|
|
4973
|
-
if (!
|
|
5380
|
+
if (!existsSync9(path))
|
|
4974
5381
|
return false;
|
|
4975
|
-
const content =
|
|
5382
|
+
const content = readFileSync5(path, "utf8");
|
|
4976
5383
|
if (!content.includes(MARKER))
|
|
4977
5384
|
return false;
|
|
4978
5385
|
unlinkSync3(path);
|
|
@@ -4990,17 +5397,25 @@ export {
|
|
|
4990
5397
|
updateProfile,
|
|
4991
5398
|
uninstallHook,
|
|
4992
5399
|
toolDefSchema,
|
|
5400
|
+
switchProfile,
|
|
5401
|
+
supervisorStatePath,
|
|
5402
|
+
supervisorSocketPath,
|
|
5403
|
+
supervisorDir,
|
|
4993
5404
|
storeSchema,
|
|
4994
5405
|
storePath,
|
|
4995
5406
|
snapshotLiveAuthToProfile,
|
|
4996
5407
|
snapshotClaudeAuthToProfile,
|
|
4997
5408
|
shellSnippet,
|
|
5409
|
+
sendSupervisorRequest,
|
|
4998
5410
|
saveStore,
|
|
5411
|
+
runSupervisedTool,
|
|
4999
5412
|
restoreClaudeAuthFromProfile,
|
|
5413
|
+
resolveSupervisorLaunch,
|
|
5000
5414
|
renameProfile,
|
|
5001
5415
|
removeProfile,
|
|
5002
5416
|
removeCustomTool,
|
|
5003
5417
|
redetectEmail,
|
|
5418
|
+
readSupervisorState,
|
|
5004
5419
|
readClaudeKeychain,
|
|
5005
5420
|
profilesDir,
|
|
5006
5421
|
profileSchema,
|
|
@@ -5010,6 +5425,7 @@ export {
|
|
|
5010
5425
|
pickProfile,
|
|
5011
5426
|
loadStore,
|
|
5012
5427
|
listTools,
|
|
5428
|
+
listSupervisorStates,
|
|
5013
5429
|
listProfiles,
|
|
5014
5430
|
keychainSupported,
|
|
5015
5431
|
isSafeProfileName,
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type StdioOptions } from "node:child_process";
|
|
2
|
+
import type { Profile, ToolDef } from "../types.js";
|
|
3
|
+
import { type SwitchMode, type SwitchResult } from "./switch.js";
|
|
4
|
+
export interface SupervisorState {
|
|
5
|
+
version: 1;
|
|
6
|
+
tool: string;
|
|
7
|
+
profile: string;
|
|
8
|
+
pid: number;
|
|
9
|
+
childPid?: number;
|
|
10
|
+
socketPath: string;
|
|
11
|
+
command: string[];
|
|
12
|
+
startedAt: string;
|
|
13
|
+
updatedAt: string;
|
|
14
|
+
}
|
|
15
|
+
export type SupervisorRequest = {
|
|
16
|
+
type: "status";
|
|
17
|
+
} | {
|
|
18
|
+
type: "switch_profile";
|
|
19
|
+
name: string;
|
|
20
|
+
tool?: string;
|
|
21
|
+
mode?: SwitchMode;
|
|
22
|
+
resume?: boolean;
|
|
23
|
+
args?: string[];
|
|
24
|
+
} | {
|
|
25
|
+
type: "stop";
|
|
26
|
+
};
|
|
27
|
+
export type SupervisorResponse = {
|
|
28
|
+
ok: true;
|
|
29
|
+
state: SupervisorState;
|
|
30
|
+
} | {
|
|
31
|
+
ok: true;
|
|
32
|
+
queued: true;
|
|
33
|
+
result: SwitchResult;
|
|
34
|
+
state: SupervisorState;
|
|
35
|
+
restartDelayMs: number;
|
|
36
|
+
} | {
|
|
37
|
+
ok: true;
|
|
38
|
+
stopping: true;
|
|
39
|
+
state: SupervisorState;
|
|
40
|
+
} | {
|
|
41
|
+
ok: false;
|
|
42
|
+
error: string;
|
|
43
|
+
};
|
|
44
|
+
export interface SupervisorLaunchPlan {
|
|
45
|
+
profile: Profile;
|
|
46
|
+
tool: ToolDef;
|
|
47
|
+
targetKind: "tool" | "profile";
|
|
48
|
+
}
|
|
49
|
+
export interface RunSupervisorOptions {
|
|
50
|
+
stdio?: StdioOptions;
|
|
51
|
+
restartDelayMs?: number;
|
|
52
|
+
log?: (message: string) => void;
|
|
53
|
+
}
|
|
54
|
+
export interface SupervisorClientOptions {
|
|
55
|
+
timeoutMs?: number;
|
|
56
|
+
allowMissing?: boolean;
|
|
57
|
+
}
|
|
58
|
+
export declare function supervisorDir(): string;
|
|
59
|
+
export declare function supervisorStatePath(toolId: string): string;
|
|
60
|
+
export declare function supervisorSocketPath(toolId: string): string;
|
|
61
|
+
export declare function readSupervisorState(toolId: string): SupervisorState | undefined;
|
|
62
|
+
export declare function listSupervisorStates(): SupervisorState[];
|
|
63
|
+
export declare function resolveSupervisorLaunch(target: string, opts?: {
|
|
64
|
+
profile?: string;
|
|
65
|
+
tool?: string;
|
|
66
|
+
}): SupervisorLaunchPlan;
|
|
67
|
+
export declare function sendSupervisorRequest(toolId: string, request: SupervisorRequest, opts?: SupervisorClientOptions): Promise<SupervisorResponse | undefined>;
|
|
68
|
+
export declare function runSupervisedTool(initialProfile: Profile, tool: ToolDef, initialArgs?: string[], opts?: RunSupervisorOptions): Promise<number>;
|
|
69
|
+
//# sourceMappingURL=supervisor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supervisor.d.ts","sourceRoot":"","sources":["../../src/lib/supervisor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Profile, ToolDef } from "../types.js";
|
|
2
|
+
export type SwitchMode = "auto" | "apply" | "env" | "active";
|
|
3
|
+
export interface SwitchOptions {
|
|
4
|
+
tool?: string;
|
|
5
|
+
mode?: SwitchMode;
|
|
6
|
+
resume?: boolean;
|
|
7
|
+
args?: string[];
|
|
8
|
+
}
|
|
9
|
+
export interface SwitchResult {
|
|
10
|
+
profile: Profile;
|
|
11
|
+
tool: ToolDef;
|
|
12
|
+
applied: boolean;
|
|
13
|
+
active: boolean;
|
|
14
|
+
env: Record<string, string>;
|
|
15
|
+
exports: string;
|
|
16
|
+
command: string[];
|
|
17
|
+
commandLine: string;
|
|
18
|
+
restartRequired: boolean;
|
|
19
|
+
message: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function switchProfile(name: string, opts?: SwitchOptions): SwitchResult;
|
|
22
|
+
//# sourceMappingURL=switch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"switch.d.ts","sourceRoot":"","sources":["../../src/lib/switch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAOpD,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;AAE7D,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;CACjB;AAeD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB,GAAG,YAAY,CAmClF"}
|
package/dist/lib/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/lib/tools.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,OAAO,EAAgC,MAAM,aAAa,CAAC;AAGzE;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/lib/tools.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,OAAO,EAAgC,MAAM,aAAa,CAAC;AAGzE;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,OAAO,EA+DlC,CAAC;AAEF,eAAO,MAAM,YAAY,WAAW,CAAC;AAIrC,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED,oFAAoF;AACpF,wBAAgB,SAAS,IAAI,OAAO,EAAE,CAMrC;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAS3C;AAED,kEAAkE;AAClE,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAanD;AAED,kEAAkE;AAClE,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAWjD"}
|
package/dist/mcp.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":""}
|