@hasna/accounts 0.1.5 → 0.1.7
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 +29 -4
- package/dist/cli.js +35110 -27
- package/dist/index.js +81 -16
- package/dist/lib/agents.d.ts +37 -0
- package/dist/lib/agents.d.ts.map +1 -0
- package/dist/lib/apply.d.ts.map +1 -1
- package/dist/lib/claude-auth.d.ts +9 -1
- package/dist/lib/claude-auth.d.ts.map +1 -1
- package/dist/lib/tools.d.ts.map +1 -1
- package/dist/mcp.js +82 -17
- package/dist/storage.d.ts +74 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +4336 -0
- package/package.json +11 -2
package/dist/index.js
CHANGED
|
@@ -4085,6 +4085,16 @@ function assertSafeWritePath(filePath, opts) {
|
|
|
4085
4085
|
}
|
|
4086
4086
|
|
|
4087
4087
|
// src/storage.ts
|
|
4088
|
+
var ACCOUNTS_STORAGE_ENV = {
|
|
4089
|
+
mode: "HASNA_ACCOUNTS_STORAGE_MODE",
|
|
4090
|
+
s3Bucket: "HASNA_ACCOUNTS_S3_BUCKET",
|
|
4091
|
+
s3Prefix: "HASNA_ACCOUNTS_S3_PREFIX",
|
|
4092
|
+
awsRegion: "HASNA_ACCOUNTS_AWS_REGION",
|
|
4093
|
+
s3Endpoint: "HASNA_ACCOUNTS_S3_ENDPOINT",
|
|
4094
|
+
s3ForcePathStyle: "HASNA_ACCOUNTS_S3_FORCE_PATH_STYLE",
|
|
4095
|
+
machineId: "HASNA_ACCOUNTS_MACHINE_ID"
|
|
4096
|
+
};
|
|
4097
|
+
var STORAGE_MODE_ENV = ACCOUNTS_STORAGE_ENV.mode;
|
|
4088
4098
|
function validateEnvPath(value, label) {
|
|
4089
4099
|
const trimmed = value.trim();
|
|
4090
4100
|
if (!trimmed || trimmed.includes("\x00") || /[\r\n]/.test(trimmed)) {
|
|
@@ -4177,6 +4187,25 @@ var BUILTIN_TOOLS = [
|
|
|
4177
4187
|
loginHint: "complete the Codex login flow for this CODEX_HOME",
|
|
4178
4188
|
resumeArgs: ["resume", "--last"]
|
|
4179
4189
|
},
|
|
4190
|
+
{
|
|
4191
|
+
id: "takumi",
|
|
4192
|
+
label: "Takumi",
|
|
4193
|
+
envVar: "TAKUMI_CONFIG_DIR",
|
|
4194
|
+
defaultDir: join2(homedir2(), ".takumi"),
|
|
4195
|
+
bin: "takumi",
|
|
4196
|
+
loginHint: "complete Takumi auth in this TAKUMI_CONFIG_DIR",
|
|
4197
|
+
resumeArgs: ["--continue"],
|
|
4198
|
+
accountFile: ".claude.json",
|
|
4199
|
+
emailPath: ["oauthAccount", "emailAddress"]
|
|
4200
|
+
},
|
|
4201
|
+
{
|
|
4202
|
+
id: "gemini",
|
|
4203
|
+
label: "Gemini CLI",
|
|
4204
|
+
envVar: "GEMINI_CONFIG_DIR",
|
|
4205
|
+
defaultDir: join2(homedir2(), ".gemini"),
|
|
4206
|
+
bin: "gemini",
|
|
4207
|
+
loginHint: "complete Gemini auth in this GEMINI_CONFIG_DIR"
|
|
4208
|
+
},
|
|
4180
4209
|
{
|
|
4181
4210
|
id: "opencode",
|
|
4182
4211
|
label: "opencode",
|
|
@@ -4200,6 +4229,22 @@ var BUILTIN_TOOLS = [
|
|
|
4200
4229
|
loginArgs: ["login"],
|
|
4201
4230
|
loginHint: "complete cursor-agent login for this CURSOR_CONFIG_DIR"
|
|
4202
4231
|
},
|
|
4232
|
+
{
|
|
4233
|
+
id: "pi",
|
|
4234
|
+
label: "Pi Coding Agent",
|
|
4235
|
+
envVar: "PI_CODING_AGENT_HOME",
|
|
4236
|
+
defaultDir: join2(homedir2(), ".pi"),
|
|
4237
|
+
bin: "pi",
|
|
4238
|
+
loginHint: "complete Pi coding agent auth in this PI_CODING_AGENT_HOME"
|
|
4239
|
+
},
|
|
4240
|
+
{
|
|
4241
|
+
id: "hermes",
|
|
4242
|
+
label: "Hermes",
|
|
4243
|
+
envVar: "HERMES_HOME",
|
|
4244
|
+
defaultDir: join2(homedir2(), ".hermes"),
|
|
4245
|
+
bin: "hermes",
|
|
4246
|
+
loginHint: "complete Hermes auth in this HERMES_HOME"
|
|
4247
|
+
},
|
|
4203
4248
|
{
|
|
4204
4249
|
id: "kimi",
|
|
4205
4250
|
label: "Kimi Code",
|
|
@@ -4516,7 +4561,7 @@ function currentProfile(toolId) {
|
|
|
4516
4561
|
return store.profiles.find((p) => p.name === name);
|
|
4517
4562
|
}
|
|
4518
4563
|
// src/lib/claude-auth.ts
|
|
4519
|
-
import { copyFileSync, existsSync as existsSync5, lstatSync as lstatSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync3, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
4564
|
+
import { copyFileSync, existsSync as existsSync5, lstatSync as lstatSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync3, statSync, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
4520
4565
|
import { dirname as dirname4, join as join6 } from "node:path";
|
|
4521
4566
|
|
|
4522
4567
|
// src/lib/claude-layout.ts
|
|
@@ -4638,14 +4683,26 @@ function writeJsonFile(path, data, stayUnder) {
|
|
|
4638
4683
|
`, { mode: 384 });
|
|
4639
4684
|
}
|
|
4640
4685
|
function readOAuthFromPaths(paths) {
|
|
4686
|
+
return findOAuthSource(paths)?.oauth;
|
|
4687
|
+
}
|
|
4688
|
+
function findOAuthSource(paths) {
|
|
4641
4689
|
for (const p of paths) {
|
|
4642
4690
|
const data = readJsonFile(p);
|
|
4643
4691
|
const oauth = data?.oauthAccount;
|
|
4644
4692
|
if (oauth && typeof oauth === "object")
|
|
4645
|
-
return oauth;
|
|
4693
|
+
return { path: p, oauth };
|
|
4646
4694
|
}
|
|
4647
4695
|
return;
|
|
4648
4696
|
}
|
|
4697
|
+
function snapshotIsStale(sourcePath, snapshotPath) {
|
|
4698
|
+
if (!existsSync5(snapshotPath))
|
|
4699
|
+
return true;
|
|
4700
|
+
try {
|
|
4701
|
+
return statSync(sourcePath).mtimeMs > statSync(snapshotPath).mtimeMs;
|
|
4702
|
+
} catch {
|
|
4703
|
+
return false;
|
|
4704
|
+
}
|
|
4705
|
+
}
|
|
4649
4706
|
function mergeOAuthInto(paths, oauth, allowDelete, stayUnder) {
|
|
4650
4707
|
const primary = paths[0];
|
|
4651
4708
|
if (!primary)
|
|
@@ -4669,6 +4726,12 @@ function mergeOAuthInto(paths, oauth, allowDelete, stayUnder) {
|
|
|
4669
4726
|
}
|
|
4670
4727
|
}
|
|
4671
4728
|
}
|
|
4729
|
+
function liveOAuthEmail() {
|
|
4730
|
+
const live = liveClaudePaths();
|
|
4731
|
+
const oauth = readOAuthFromPaths([live.homeJson]);
|
|
4732
|
+
const email = oauth?.emailAddress;
|
|
4733
|
+
return typeof email === "string" && email ? email : undefined;
|
|
4734
|
+
}
|
|
4672
4735
|
function snapshotLiveAuthToProfile(profileDir, _tool) {
|
|
4673
4736
|
const authDir = profileAuthDir(profileDir);
|
|
4674
4737
|
assertSafeWritePath(join6(authDir, OAUTH_SNAPSHOT), { mustStayUnder: profileDir });
|
|
@@ -4692,19 +4755,19 @@ function snapshotClaudeAuthToProfile(profileDir, tool) {
|
|
|
4692
4755
|
snapshotLiveAuthToProfile(profileDir, tool);
|
|
4693
4756
|
}
|
|
4694
4757
|
function ensureProfileAuthSnapshot(profileDir, tool, opts = {}) {
|
|
4695
|
-
if (!opts.overwrite && hasAuthSnapshot(profileDir))
|
|
4696
|
-
return;
|
|
4697
4758
|
const authDir = profileAuthDir(profileDir);
|
|
4698
4759
|
assertSafeWritePath(join6(authDir, OAUTH_SNAPSHOT), { mustStayUnder: profileDir });
|
|
4699
4760
|
mkdirSync4(authDir, { recursive: true });
|
|
4700
|
-
const
|
|
4701
|
-
|
|
4702
|
-
|
|
4761
|
+
const oauthSource = findOAuthSource(profileAccountJsonPaths(profileDir, tool));
|
|
4762
|
+
const oauthSnap = profileOAuthSnapshot(profileDir);
|
|
4763
|
+
if (oauthSource && (opts.overwrite || snapshotIsStale(oauthSource.path, oauthSnap))) {
|
|
4764
|
+
writeJsonFile(oauthSnap, { oauthAccount: oauthSource.oauth }, profileDir);
|
|
4765
|
+
}
|
|
4703
4766
|
const credFile = join6(profileDir, ".credentials.json");
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
assertSafeWritePath(
|
|
4707
|
-
copyFileSync(credFile,
|
|
4767
|
+
const credSnap = profileCredentialsSnapshot(profileDir);
|
|
4768
|
+
if (existsSync5(credFile) && (opts.overwrite || snapshotIsStale(credFile, credSnap))) {
|
|
4769
|
+
assertSafeWritePath(credSnap, { mustStayUnder: profileDir });
|
|
4770
|
+
copyFileSync(credFile, credSnap);
|
|
4708
4771
|
}
|
|
4709
4772
|
}
|
|
4710
4773
|
function profileHasAuth(profileDir, tool) {
|
|
@@ -4794,6 +4857,9 @@ function withApplyLock(fn) {
|
|
|
4794
4857
|
}
|
|
4795
4858
|
|
|
4796
4859
|
// src/lib/apply.ts
|
|
4860
|
+
function singleMatch(profiles) {
|
|
4861
|
+
return profiles.length === 1 ? profiles[0] : undefined;
|
|
4862
|
+
}
|
|
4797
4863
|
function appliedProfile(toolId) {
|
|
4798
4864
|
const store = loadStore();
|
|
4799
4865
|
const name = store.applied[toolId];
|
|
@@ -4815,11 +4881,10 @@ function applyProfileUnlocked(name, toolId) {
|
|
|
4815
4881
|
}
|
|
4816
4882
|
const store = loadStore();
|
|
4817
4883
|
const previous = store.applied[tool.id];
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
}
|
|
4884
|
+
const liveEmail = liveOAuthEmail();
|
|
4885
|
+
const owner = liveEmail && singleMatch(store.profiles.filter((p) => p.tool === tool.id && p.email === liveEmail)) || (previous ? store.profiles.find((p) => p.name === previous && p.tool === tool.id) : undefined);
|
|
4886
|
+
if (owner)
|
|
4887
|
+
snapshotLiveAuthToProfile(owner.dir, tool);
|
|
4823
4888
|
ensureProfileAuthSnapshot(profile.dir, tool);
|
|
4824
4889
|
restoreClaudeAuthFromProfile(profile.dir, tool, name);
|
|
4825
4890
|
store.applied[tool.id] = name;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Profile } from "../types.js";
|
|
2
|
+
export type AgentEntry = Record<string, unknown>;
|
|
3
|
+
export interface ProfileAgents {
|
|
4
|
+
profile: string;
|
|
5
|
+
tool: string;
|
|
6
|
+
email?: string;
|
|
7
|
+
dir: string;
|
|
8
|
+
agents: AgentEntry[];
|
|
9
|
+
error?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface AgentsRunnerResult {
|
|
12
|
+
ok: boolean;
|
|
13
|
+
raw: string;
|
|
14
|
+
error?: string;
|
|
15
|
+
}
|
|
16
|
+
export type AgentsRunner = (profile: Profile) => AgentsRunnerResult;
|
|
17
|
+
/**
|
|
18
|
+
* Extract the first top-level JSON array from output that may be wrapped in
|
|
19
|
+
* pty/ANSI noise (`claude agents --json` only works on a TTY, so we run it
|
|
20
|
+
* under `script` and the JSON arrives surrounded by control sequences).
|
|
21
|
+
*/
|
|
22
|
+
export declare function extractJsonArray(raw: string): unknown[] | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Run `<bin> agents --json` for a profile's config dir under a pseudo-TTY.
|
|
25
|
+
* Claude Code switches to print-mode argument parsing when stdout is not a
|
|
26
|
+
* TTY and never reaches the `agents` subcommand, so a plain pipe won't work.
|
|
27
|
+
*/
|
|
28
|
+
export declare function runClaudeAgentsJson(profile: Profile, timeoutMs?: number): AgentsRunnerResult;
|
|
29
|
+
export interface ListAgentsOptions {
|
|
30
|
+
tool?: string;
|
|
31
|
+
profile?: string;
|
|
32
|
+
backgroundOnly?: boolean;
|
|
33
|
+
runner?: AgentsRunner;
|
|
34
|
+
}
|
|
35
|
+
/** List agent sessions for every profile of a tool (default: claude). */
|
|
36
|
+
export declare function listAgentsAcrossProfiles(opts?: ListAgentsOptions): ProfileAgents[];
|
|
37
|
+
//# sourceMappingURL=agents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/lib/agents.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAI3C,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,OAAO,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,kBAAkB,CAAC;AAEpE;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,EAAE,GAAG,SAAS,CA+BnE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,SAAS,GAAG,kBAAkB,CAqB5F;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAED,yEAAyE;AACzE,wBAAgB,wBAAwB,CAAC,IAAI,GAAE,iBAAsB,GAAG,aAAa,EAAE,CAwBtF"}
|
package/dist/lib/apply.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/lib/apply.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/lib/apply.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAkB3C,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAKlE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAEnG"}
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import type { ToolDef } from "../types.js";
|
|
2
|
+
/** Email address of the account currently authenticated on the live Claude paths. */
|
|
3
|
+
export declare function liveOAuthEmail(): string | undefined;
|
|
2
4
|
/** Snapshot live Claude auth into a profile directory (used when switching away on apply). */
|
|
3
5
|
export declare function snapshotLiveAuthToProfile(profileDir: string, _tool: ToolDef): void;
|
|
4
6
|
/** @deprecated Use snapshotLiveAuthToProfile */
|
|
5
7
|
export declare function snapshotClaudeAuthToProfile(profileDir: string, tool: ToolDef): void;
|
|
6
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Build auth snapshots from files already present in the profile config dir.
|
|
10
|
+
* Snapshots are refreshed per-file whenever the source in the profile dir is
|
|
11
|
+
* newer than the existing snapshot — a running tool rotates its OAuth tokens
|
|
12
|
+
* in place, and restoring a login-time snapshot over rotated tokens logs the
|
|
13
|
+
* account out (rotated-out refresh tokens are revoked server-side).
|
|
14
|
+
*/
|
|
7
15
|
export declare function ensureProfileAuthSnapshot(profileDir: string, tool: ToolDef, opts?: {
|
|
8
16
|
overwrite?: boolean;
|
|
9
17
|
}): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-auth.d.ts","sourceRoot":"","sources":["../../src/lib/claude-auth.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"claude-auth.d.ts","sourceRoot":"","sources":["../../src/lib/claude-auth.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAyF3C,qFAAqF;AACrF,wBAAgB,cAAc,IAAI,MAAM,GAAG,SAAS,CAKnD;AAED,8FAA8F;AAC9F,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAmBlF;AAED,gDAAgD;AAChD,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAEnF;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,OAAO,EACb,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,IAAI,CAiBN;AAED,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAEzE;AAED,6DAA6D;AAC7D,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,OAAO,EACb,WAAW,CAAC,EAAE,MAAM,GACnB,IAAI,CAqDN;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAM3D"}
|
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,EAkGlC,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.js
CHANGED
|
@@ -16721,6 +16721,16 @@ function assertSafeWritePath(filePath, opts) {
|
|
|
16721
16721
|
}
|
|
16722
16722
|
|
|
16723
16723
|
// src/storage.ts
|
|
16724
|
+
var ACCOUNTS_STORAGE_ENV = {
|
|
16725
|
+
mode: "HASNA_ACCOUNTS_STORAGE_MODE",
|
|
16726
|
+
s3Bucket: "HASNA_ACCOUNTS_S3_BUCKET",
|
|
16727
|
+
s3Prefix: "HASNA_ACCOUNTS_S3_PREFIX",
|
|
16728
|
+
awsRegion: "HASNA_ACCOUNTS_AWS_REGION",
|
|
16729
|
+
s3Endpoint: "HASNA_ACCOUNTS_S3_ENDPOINT",
|
|
16730
|
+
s3ForcePathStyle: "HASNA_ACCOUNTS_S3_FORCE_PATH_STYLE",
|
|
16731
|
+
machineId: "HASNA_ACCOUNTS_MACHINE_ID"
|
|
16732
|
+
};
|
|
16733
|
+
var STORAGE_MODE_ENV = ACCOUNTS_STORAGE_ENV.mode;
|
|
16724
16734
|
function validateEnvPath(value, label) {
|
|
16725
16735
|
const trimmed = value.trim();
|
|
16726
16736
|
if (!trimmed || trimmed.includes("\x00") || /[\r\n]/.test(trimmed)) {
|
|
@@ -16811,6 +16821,25 @@ var BUILTIN_TOOLS = [
|
|
|
16811
16821
|
loginHint: "complete the Codex login flow for this CODEX_HOME",
|
|
16812
16822
|
resumeArgs: ["resume", "--last"]
|
|
16813
16823
|
},
|
|
16824
|
+
{
|
|
16825
|
+
id: "takumi",
|
|
16826
|
+
label: "Takumi",
|
|
16827
|
+
envVar: "TAKUMI_CONFIG_DIR",
|
|
16828
|
+
defaultDir: join2(homedir2(), ".takumi"),
|
|
16829
|
+
bin: "takumi",
|
|
16830
|
+
loginHint: "complete Takumi auth in this TAKUMI_CONFIG_DIR",
|
|
16831
|
+
resumeArgs: ["--continue"],
|
|
16832
|
+
accountFile: ".claude.json",
|
|
16833
|
+
emailPath: ["oauthAccount", "emailAddress"]
|
|
16834
|
+
},
|
|
16835
|
+
{
|
|
16836
|
+
id: "gemini",
|
|
16837
|
+
label: "Gemini CLI",
|
|
16838
|
+
envVar: "GEMINI_CONFIG_DIR",
|
|
16839
|
+
defaultDir: join2(homedir2(), ".gemini"),
|
|
16840
|
+
bin: "gemini",
|
|
16841
|
+
loginHint: "complete Gemini auth in this GEMINI_CONFIG_DIR"
|
|
16842
|
+
},
|
|
16814
16843
|
{
|
|
16815
16844
|
id: "opencode",
|
|
16816
16845
|
label: "opencode",
|
|
@@ -16834,6 +16863,22 @@ var BUILTIN_TOOLS = [
|
|
|
16834
16863
|
loginArgs: ["login"],
|
|
16835
16864
|
loginHint: "complete cursor-agent login for this CURSOR_CONFIG_DIR"
|
|
16836
16865
|
},
|
|
16866
|
+
{
|
|
16867
|
+
id: "pi",
|
|
16868
|
+
label: "Pi Coding Agent",
|
|
16869
|
+
envVar: "PI_CODING_AGENT_HOME",
|
|
16870
|
+
defaultDir: join2(homedir2(), ".pi"),
|
|
16871
|
+
bin: "pi",
|
|
16872
|
+
loginHint: "complete Pi coding agent auth in this PI_CODING_AGENT_HOME"
|
|
16873
|
+
},
|
|
16874
|
+
{
|
|
16875
|
+
id: "hermes",
|
|
16876
|
+
label: "Hermes",
|
|
16877
|
+
envVar: "HERMES_HOME",
|
|
16878
|
+
defaultDir: join2(homedir2(), ".hermes"),
|
|
16879
|
+
bin: "hermes",
|
|
16880
|
+
loginHint: "complete Hermes auth in this HERMES_HOME"
|
|
16881
|
+
},
|
|
16837
16882
|
{
|
|
16838
16883
|
id: "kimi",
|
|
16839
16884
|
label: "Kimi Code",
|
|
@@ -16921,7 +16966,7 @@ function currentProfile(toolId) {
|
|
|
16921
16966
|
}
|
|
16922
16967
|
|
|
16923
16968
|
// src/lib/claude-auth.ts
|
|
16924
|
-
import { copyFileSync, existsSync as existsSync3, lstatSync as lstatSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync2, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
16969
|
+
import { copyFileSync, existsSync as existsSync3, lstatSync as lstatSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync2, statSync, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
16925
16970
|
import { dirname as dirname3, join as join4 } from "node:path";
|
|
16926
16971
|
|
|
16927
16972
|
// src/lib/claude-layout.ts
|
|
@@ -17043,14 +17088,26 @@ function writeJsonFile(path, data, stayUnder) {
|
|
|
17043
17088
|
`, { mode: 384 });
|
|
17044
17089
|
}
|
|
17045
17090
|
function readOAuthFromPaths(paths) {
|
|
17091
|
+
return findOAuthSource(paths)?.oauth;
|
|
17092
|
+
}
|
|
17093
|
+
function findOAuthSource(paths) {
|
|
17046
17094
|
for (const p of paths) {
|
|
17047
17095
|
const data = readJsonFile(p);
|
|
17048
17096
|
const oauth = data?.oauthAccount;
|
|
17049
17097
|
if (oauth && typeof oauth === "object")
|
|
17050
|
-
return oauth;
|
|
17098
|
+
return { path: p, oauth };
|
|
17051
17099
|
}
|
|
17052
17100
|
return;
|
|
17053
17101
|
}
|
|
17102
|
+
function snapshotIsStale(sourcePath, snapshotPath) {
|
|
17103
|
+
if (!existsSync3(snapshotPath))
|
|
17104
|
+
return true;
|
|
17105
|
+
try {
|
|
17106
|
+
return statSync(sourcePath).mtimeMs > statSync(snapshotPath).mtimeMs;
|
|
17107
|
+
} catch {
|
|
17108
|
+
return false;
|
|
17109
|
+
}
|
|
17110
|
+
}
|
|
17054
17111
|
function mergeOAuthInto(paths, oauth, allowDelete, stayUnder) {
|
|
17055
17112
|
const primary = paths[0];
|
|
17056
17113
|
if (!primary)
|
|
@@ -17074,6 +17131,12 @@ function mergeOAuthInto(paths, oauth, allowDelete, stayUnder) {
|
|
|
17074
17131
|
}
|
|
17075
17132
|
}
|
|
17076
17133
|
}
|
|
17134
|
+
function liveOAuthEmail() {
|
|
17135
|
+
const live = liveClaudePaths();
|
|
17136
|
+
const oauth = readOAuthFromPaths([live.homeJson]);
|
|
17137
|
+
const email2 = oauth?.emailAddress;
|
|
17138
|
+
return typeof email2 === "string" && email2 ? email2 : undefined;
|
|
17139
|
+
}
|
|
17077
17140
|
function snapshotLiveAuthToProfile(profileDir, _tool) {
|
|
17078
17141
|
const authDir = profileAuthDir(profileDir);
|
|
17079
17142
|
assertSafeWritePath(join4(authDir, OAUTH_SNAPSHOT), { mustStayUnder: profileDir });
|
|
@@ -17094,19 +17157,19 @@ function snapshotLiveAuthToProfile(profileDir, _tool) {
|
|
|
17094
17157
|
}
|
|
17095
17158
|
}
|
|
17096
17159
|
function ensureProfileAuthSnapshot(profileDir, tool, opts = {}) {
|
|
17097
|
-
if (!opts.overwrite && hasAuthSnapshot(profileDir))
|
|
17098
|
-
return;
|
|
17099
17160
|
const authDir = profileAuthDir(profileDir);
|
|
17100
17161
|
assertSafeWritePath(join4(authDir, OAUTH_SNAPSHOT), { mustStayUnder: profileDir });
|
|
17101
17162
|
mkdirSync3(authDir, { recursive: true });
|
|
17102
|
-
const
|
|
17103
|
-
|
|
17104
|
-
|
|
17163
|
+
const oauthSource = findOAuthSource(profileAccountJsonPaths(profileDir, tool));
|
|
17164
|
+
const oauthSnap = profileOAuthSnapshot(profileDir);
|
|
17165
|
+
if (oauthSource && (opts.overwrite || snapshotIsStale(oauthSource.path, oauthSnap))) {
|
|
17166
|
+
writeJsonFile(oauthSnap, { oauthAccount: oauthSource.oauth }, profileDir);
|
|
17167
|
+
}
|
|
17105
17168
|
const credFile = join4(profileDir, ".credentials.json");
|
|
17106
|
-
|
|
17107
|
-
|
|
17108
|
-
assertSafeWritePath(
|
|
17109
|
-
copyFileSync(credFile,
|
|
17169
|
+
const credSnap = profileCredentialsSnapshot(profileDir);
|
|
17170
|
+
if (existsSync3(credFile) && (opts.overwrite || snapshotIsStale(credFile, credSnap))) {
|
|
17171
|
+
assertSafeWritePath(credSnap, { mustStayUnder: profileDir });
|
|
17172
|
+
copyFileSync(credFile, credSnap);
|
|
17110
17173
|
}
|
|
17111
17174
|
}
|
|
17112
17175
|
function profileHasAuth(profileDir, tool) {
|
|
@@ -17196,6 +17259,9 @@ function withApplyLock(fn) {
|
|
|
17196
17259
|
}
|
|
17197
17260
|
|
|
17198
17261
|
// src/lib/apply.ts
|
|
17262
|
+
function singleMatch(profiles) {
|
|
17263
|
+
return profiles.length === 1 ? profiles[0] : undefined;
|
|
17264
|
+
}
|
|
17199
17265
|
function appliedProfile(toolId) {
|
|
17200
17266
|
const store = loadStore();
|
|
17201
17267
|
const name = store.applied[toolId];
|
|
@@ -17217,11 +17283,10 @@ function applyProfileUnlocked(name, toolId) {
|
|
|
17217
17283
|
}
|
|
17218
17284
|
const store = loadStore();
|
|
17219
17285
|
const previous = store.applied[tool.id];
|
|
17220
|
-
|
|
17221
|
-
|
|
17222
|
-
|
|
17223
|
-
|
|
17224
|
-
}
|
|
17286
|
+
const liveEmail = liveOAuthEmail();
|
|
17287
|
+
const owner = liveEmail && singleMatch(store.profiles.filter((p) => p.tool === tool.id && p.email === liveEmail)) || (previous ? store.profiles.find((p) => p.name === previous && p.tool === tool.id) : undefined);
|
|
17288
|
+
if (owner)
|
|
17289
|
+
snapshotLiveAuthToProfile(owner.dir, tool);
|
|
17225
17290
|
ensureProfileAuthSnapshot(profile.dir, tool);
|
|
17226
17291
|
restoreClaudeAuthFromProfile(profile.dir, tool, name);
|
|
17227
17292
|
store.applied[tool.id] = name;
|
|
@@ -17399,7 +17464,7 @@ function ok(data) {
|
|
|
17399
17464
|
function fail(message) {
|
|
17400
17465
|
return { content: [{ type: "text", text: JSON.stringify({ error: message }) }], isError: true };
|
|
17401
17466
|
}
|
|
17402
|
-
var server = new Server({ name: "accounts", version: "0.1.
|
|
17467
|
+
var server = new Server({ name: "accounts", version: "0.1.7" }, { capabilities: { tools: {} } });
|
|
17403
17468
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
17404
17469
|
tools: [
|
|
17405
17470
|
{
|
package/dist/storage.d.ts
CHANGED
|
@@ -1,4 +1,69 @@
|
|
|
1
1
|
import { type Store } from "./types.js";
|
|
2
|
+
export declare const ACCOUNTS_STORAGE_ENV: {
|
|
3
|
+
readonly mode: "HASNA_ACCOUNTS_STORAGE_MODE";
|
|
4
|
+
readonly s3Bucket: "HASNA_ACCOUNTS_S3_BUCKET";
|
|
5
|
+
readonly s3Prefix: "HASNA_ACCOUNTS_S3_PREFIX";
|
|
6
|
+
readonly awsRegion: "HASNA_ACCOUNTS_AWS_REGION";
|
|
7
|
+
readonly s3Endpoint: "HASNA_ACCOUNTS_S3_ENDPOINT";
|
|
8
|
+
readonly s3ForcePathStyle: "HASNA_ACCOUNTS_S3_FORCE_PATH_STYLE";
|
|
9
|
+
readonly machineId: "HASNA_ACCOUNTS_MACHINE_ID";
|
|
10
|
+
};
|
|
11
|
+
export declare const ACCOUNTS_STORAGE_FALLBACK_ENV: {
|
|
12
|
+
readonly mode: "ACCOUNTS_STORAGE_MODE";
|
|
13
|
+
readonly s3Bucket: "ACCOUNTS_S3_BUCKET";
|
|
14
|
+
readonly s3Prefix: "ACCOUNTS_S3_PREFIX";
|
|
15
|
+
readonly awsRegion: "ACCOUNTS_AWS_REGION";
|
|
16
|
+
readonly s3Endpoint: "ACCOUNTS_S3_ENDPOINT";
|
|
17
|
+
readonly s3ForcePathStyle: "ACCOUNTS_S3_FORCE_PATH_STYLE";
|
|
18
|
+
readonly machineId: "ACCOUNTS_MACHINE_ID";
|
|
19
|
+
};
|
|
20
|
+
export declare const STORAGE_MODE_ENV: "HASNA_ACCOUNTS_STORAGE_MODE";
|
|
21
|
+
export declare const STORAGE_TABLES: readonly [];
|
|
22
|
+
export type AccountsStorageMode = "local" | "remote" | "hybrid";
|
|
23
|
+
export interface AccountsStorageConfig {
|
|
24
|
+
mode: AccountsStorageMode;
|
|
25
|
+
s3Bucket?: string;
|
|
26
|
+
s3Prefix: string;
|
|
27
|
+
awsRegion?: string;
|
|
28
|
+
s3Endpoint?: string;
|
|
29
|
+
s3ForcePathStyle?: boolean;
|
|
30
|
+
machineId: string;
|
|
31
|
+
}
|
|
32
|
+
export interface AccountsStorageStatus {
|
|
33
|
+
configured: boolean;
|
|
34
|
+
mode: AccountsStorageMode;
|
|
35
|
+
local: {
|
|
36
|
+
home: string;
|
|
37
|
+
storePath: string;
|
|
38
|
+
profilesDir: string;
|
|
39
|
+
};
|
|
40
|
+
remote: {
|
|
41
|
+
configured: boolean;
|
|
42
|
+
bucketEnv: string;
|
|
43
|
+
bucket?: string;
|
|
44
|
+
prefix: string;
|
|
45
|
+
regionEnv: string;
|
|
46
|
+
endpointConfigured: boolean;
|
|
47
|
+
};
|
|
48
|
+
env: typeof ACCOUNTS_STORAGE_ENV;
|
|
49
|
+
fallbackEnv: typeof ACCOUNTS_STORAGE_FALLBACK_ENV;
|
|
50
|
+
tables: readonly [];
|
|
51
|
+
}
|
|
52
|
+
export interface AccountsStorageSnapshot {
|
|
53
|
+
schemaVersion: 1;
|
|
54
|
+
source: "accounts";
|
|
55
|
+
createdAt: string;
|
|
56
|
+
machineId: string;
|
|
57
|
+
store: Store;
|
|
58
|
+
}
|
|
59
|
+
export interface AccountsStorageSyncResult {
|
|
60
|
+
mode: AccountsStorageMode;
|
|
61
|
+
pushed: number;
|
|
62
|
+
pulled: number;
|
|
63
|
+
skipped: boolean;
|
|
64
|
+
key: string;
|
|
65
|
+
reason?: string;
|
|
66
|
+
}
|
|
2
67
|
/** Base directory for all accounts state. Override with `ACCOUNTS_HOME`. */
|
|
3
68
|
export declare function accountsHome(): string;
|
|
4
69
|
/** Path to the registry file. Override with `ACCOUNTS_STORE_PATH`. */
|
|
@@ -7,4 +72,13 @@ export declare function storePath(): string;
|
|
|
7
72
|
export declare function profilesDir(): string;
|
|
8
73
|
export declare function loadStore(): Store;
|
|
9
74
|
export declare function saveStore(store: Store): void;
|
|
75
|
+
export declare function getAccountsStorageConfig(env?: NodeJS.ProcessEnv): AccountsStorageConfig;
|
|
76
|
+
export declare function getAccountsStorageStatus(env?: NodeJS.ProcessEnv): AccountsStorageStatus;
|
|
77
|
+
export declare function createAccountsStorageSnapshot(env?: NodeJS.ProcessEnv): AccountsStorageSnapshot;
|
|
78
|
+
export declare function restoreAccountsStorageSnapshot(snapshot: AccountsStorageSnapshot): void;
|
|
79
|
+
export declare function accountsStorageSnapshotKey(env?: NodeJS.ProcessEnv): string;
|
|
80
|
+
export declare function storagePush(env?: NodeJS.ProcessEnv): Promise<AccountsStorageSyncResult>;
|
|
81
|
+
export declare function storagePull(env?: NodeJS.ProcessEnv): Promise<AccountsStorageSyncResult>;
|
|
82
|
+
export declare function storageSync(env?: NodeJS.ProcessEnv): Promise<AccountsStorageSyncResult>;
|
|
83
|
+
export declare const getStorageStatus: typeof getAccountsStorageStatus;
|
|
10
84
|
//# sourceMappingURL=storage.d.ts.map
|
package/dist/storage.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,KAAK,EAAiD,MAAM,YAAY,CAAC;AAGvF,eAAO,MAAM,oBAAoB;;;;;;;;CAQvB,CAAC;AAEX,eAAO,MAAM,6BAA6B;;;;;;;;CAQhC,CAAC;AAEX,eAAO,MAAM,gBAAgB,+BAA4B,CAAC;AAC1D,eAAO,MAAM,cAAc,aAAc,CAAC;AAE1C,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEhE,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,mBAAmB,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,OAAO,CAAC;IACpB,IAAI,EAAE,mBAAmB,CAAC;IAC1B,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,MAAM,EAAE;QACN,UAAU,EAAE,OAAO,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,kBAAkB,EAAE,OAAO,CAAC;KAC7B,CAAC;IACF,GAAG,EAAE,OAAO,oBAAoB,CAAC;IACjC,WAAW,EAAE,OAAO,6BAA6B,CAAC;IAClD,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,uBAAuB;IACtC,aAAa,EAAE,CAAC,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,KAAK,CAAC;CACd;AAED,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,mBAAmB,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAUD,4EAA4E;AAC5E,wBAAgB,YAAY,IAAI,MAAM,CAIrC;AAED,sEAAsE;AACtE,wBAAgB,SAAS,IAAI,MAAM,CAIlC;AAED,0EAA0E;AAC1E,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAID,wBAAgB,SAAS,IAAI,KAAK,CA+BjC;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAK5C;AAwBD,wBAAgB,wBAAwB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,qBAAqB,CAUpG;AAED,wBAAgB,wBAAwB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,qBAAqB,CAsBpG;AAED,wBAAgB,6BAA6B,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,uBAAuB,CAS3G;AAED,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,uBAAuB,GAAG,IAAI,CAKtF;AAED,wBAAgB,0BAA0B,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAGvF;AAoBD,wBAAsB,WAAW,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAiB1G;AAED,wBAAsB,WAAW,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAa1G;AAED,wBAAsB,WAAW,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAS1G;AAED,eAAO,MAAM,gBAAgB,iCAA2B,CAAC"}
|