@drakon-systems/multi-clawd 1.9.3 → 1.9.4
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 +1 -1
- package/dist/account-identity.js +41 -26
- package/dist/chain-audit.js +2 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/scripts/doctor.mjs +12 -7
package/README.md
CHANGED
|
@@ -375,7 +375,7 @@ openclaw plugins install (Get-Location).Path
|
|
|
375
375
|
**Or let your agent install it.** Running an OpenClaw assistant or Claude
|
|
376
376
|
Code on the target machine already? Paste it this and go make coffee:
|
|
377
377
|
|
|
378
|
-
> Read https://raw.githubusercontent.com/Drakon-Systems-Ltd/multi-clawd/v1.9.
|
|
378
|
+
> Read https://raw.githubusercontent.com/Drakon-Systems-Ltd/multi-clawd/v1.9.4/SETUP-AGENT.md
|
|
379
379
|
> and follow it to set up multi-clawd on this machine. I own a second
|
|
380
380
|
> Claude account — ask me when you need me to log in.
|
|
381
381
|
|
package/dist/account-identity.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
export function
|
|
2
|
-
const dir = !account.native && account.configDir ? io.expandHome(account.configDir) : io.defaultConfigDir;
|
|
3
|
-
if (!dir)
|
|
4
|
-
return undefined;
|
|
1
|
+
export function identityFilePaths(account, io) {
|
|
5
2
|
if (account.oauthTokenFile || account.oauthTokenRef)
|
|
6
|
-
return
|
|
7
|
-
|
|
3
|
+
return [];
|
|
4
|
+
const file = (dir) => `${dir.replace(/\/+$/, "")}/.claude.json`;
|
|
5
|
+
if (!account.native && account.configDir)
|
|
6
|
+
return [file(io.expandHome(account.configDir))];
|
|
7
|
+
const candidates = [io.homeDir ? file(io.homeDir) : undefined, io.defaultConfigDir ? file(io.defaultConfigDir) : undefined];
|
|
8
|
+
return [...new Set(candidates.filter((c) => Boolean(c)))];
|
|
8
9
|
}
|
|
9
10
|
export function formatPlan(oauthAccount) {
|
|
10
11
|
const tier = typeof oauthAccount.organizationRateLimitTier === "string"
|
|
@@ -47,14 +48,15 @@ export function parseIdentityFile(text) {
|
|
|
47
48
|
email: str("emailAddress"),
|
|
48
49
|
organizationName: str("organizationName"),
|
|
49
50
|
plan: formatPlan(account),
|
|
51
|
+
profileFetchedAt: typeof account.profileFetchedAt === "number" ? account.profileFetchedAt : undefined,
|
|
50
52
|
};
|
|
51
53
|
if (!identity.accountUuid && !identity.email)
|
|
52
54
|
return undefined;
|
|
53
55
|
return identity;
|
|
54
56
|
}
|
|
55
57
|
export function resolveAccountIdentity(account, io) {
|
|
56
|
-
const
|
|
57
|
-
if (
|
|
58
|
+
const paths = identityFilePaths(account, io);
|
|
59
|
+
if (paths.length === 0) {
|
|
58
60
|
return {
|
|
59
61
|
accountId: account.id,
|
|
60
62
|
status: "unknown",
|
|
@@ -63,28 +65,41 @@ export function resolveAccountIdentity(account, io) {
|
|
|
63
65
|
: "no config dir to read",
|
|
64
66
|
};
|
|
65
67
|
}
|
|
66
|
-
let
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
68
|
+
let best;
|
|
69
|
+
const problems = [];
|
|
70
|
+
for (const path of paths) {
|
|
71
|
+
let text;
|
|
72
|
+
try {
|
|
73
|
+
text = io.readFile(path);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
problems.push(`no readable ${path}`);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const identity = parseIdentityFile(text);
|
|
80
|
+
if (!identity) {
|
|
81
|
+
problems.push(`${path} carries no oauthAccount record`);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const candidate = {
|
|
81
85
|
accountId: account.id,
|
|
82
|
-
status: "
|
|
83
|
-
reason: `${path} carries no oauthAccount record`,
|
|
86
|
+
status: "resolved",
|
|
84
87
|
source: path,
|
|
88
|
+
...identity,
|
|
85
89
|
};
|
|
90
|
+
if (!best || (candidate.profileFetchedAt ?? 0) > (best.profileFetchedAt ?? 0))
|
|
91
|
+
best = candidate;
|
|
86
92
|
}
|
|
87
|
-
|
|
93
|
+
if (best)
|
|
94
|
+
return best;
|
|
95
|
+
return {
|
|
96
|
+
accountId: account.id,
|
|
97
|
+
status: "unknown",
|
|
98
|
+
reason: problems.length === 1
|
|
99
|
+
? `${problems[0]} — this account has never completed a login here`
|
|
100
|
+
: `${problems.join("; ")} — this account has never completed a login here`,
|
|
101
|
+
source: paths[0],
|
|
102
|
+
};
|
|
88
103
|
}
|
|
89
104
|
export function maskEmail(email) {
|
|
90
105
|
const at = email.lastIndexOf("@");
|
package/dist/chain-audit.js
CHANGED
|
@@ -271,6 +271,8 @@ export function auditSessionOverrides(sessions, poolConfigured, opts = {}) {
|
|
|
271
271
|
const source = entry.modelOverrideSource;
|
|
272
272
|
if (typeof source !== "string" || source === "auto")
|
|
273
273
|
continue;
|
|
274
|
+
if (source === "default" && !entry.modelOverride)
|
|
275
|
+
continue;
|
|
274
276
|
const provider = entry.providerOverride ?? entry.modelProvider;
|
|
275
277
|
const model = entry.modelOverride;
|
|
276
278
|
if (!provider || !model) {
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "multi-clawd",
|
|
3
3
|
"name": "multi-clawd",
|
|
4
|
-
"version": "1.9.
|
|
4
|
+
"version": "1.9.4",
|
|
5
5
|
"description": "Register additional Claude Code logins (Max/Pro accounts) as first-class OpenClaw CLI backends for cross-account failover, keeping the full skills/MCP harness on every account.",
|
|
6
6
|
"cliBackends": [
|
|
7
7
|
"claw1",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drakon-systems/multi-clawd",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.4",
|
|
4
4
|
"description": "Multi-account Claude Code failover for OpenClaw — register additional Claude (Max/Pro) logins as first-class CLI backends and keep the full skills/MCP harness across every account. Also imports those accounts' setup tokens into Hermes Agent's Anthropic credential pool.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/scripts/doctor.mjs
CHANGED
|
@@ -288,13 +288,17 @@ const { summarizeWindowUsage, classifyAccountHealth } = await importDist("health
|
|
|
288
288
|
"summarizeWindowUsage",
|
|
289
289
|
"classifyAccountHealth",
|
|
290
290
|
]);
|
|
291
|
+
// "cli", like the chain audits: resolving WHICH login backs an account is
|
|
292
|
+
// doctor's own diagnostic, not a description of the running plugin's
|
|
293
|
+
// behaviour. Asking the installed copy meant a fix to the resolver could not
|
|
294
|
+
// take effect until the plugin itself was reinstalled — so the CLI reported a
|
|
295
|
+
// bug it had already been taught not to make.
|
|
291
296
|
const { resolveAccountIdentity, describeIdentity, findDuplicateLogins, maskEmail } =
|
|
292
|
-
await importDist(
|
|
293
|
-
"
|
|
294
|
-
"describeIdentity",
|
|
295
|
-
"
|
|
296
|
-
|
|
297
|
-
]);
|
|
297
|
+
await importDist(
|
|
298
|
+
"account-identity.js",
|
|
299
|
+
["resolveAccountIdentity", "describeIdentity", "findDuplicateLogins", "maskEmail"],
|
|
300
|
+
"cli",
|
|
301
|
+
);
|
|
298
302
|
const { decideStickySelection } = await importDist("sticky.js", ["decideStickySelection"]);
|
|
299
303
|
const io = {
|
|
300
304
|
readFile: (p) => readFileSync(expandHome(p), "utf8"),
|
|
@@ -322,6 +326,7 @@ const identityIo = {
|
|
|
322
326
|
defaultConfigDir: process.env.CLAUDE_CONFIG_DIR
|
|
323
327
|
? expandHome(process.env.CLAUDE_CONFIG_DIR)
|
|
324
328
|
: join(HOME, ".claude"),
|
|
329
|
+
homeDir: HOME,
|
|
325
330
|
};
|
|
326
331
|
const accounts = pluginConfig.accounts ?? [];
|
|
327
332
|
const identities = [];
|
|
@@ -334,7 +339,7 @@ for (const account of accounts) {
|
|
|
334
339
|
const identity = resolveAccountIdentity(account, identityIo);
|
|
335
340
|
identities.push(identity);
|
|
336
341
|
const source = account.native
|
|
337
|
-
? `native login, ${identityIo.defaultConfigDir}`
|
|
342
|
+
? `native login, ${identity.source ?? identityIo.defaultConfigDir}`
|
|
338
343
|
: account.configDir
|
|
339
344
|
? `config dir ${account.configDir}`
|
|
340
345
|
: account.oauthTokenFile
|