@drakon-systems/multi-clawd 1.2.2 → 1.2.3
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/setup-core.js +19 -0
- package/package.json +1 -1
- package/scripts/setup.mjs +35 -8
package/dist/setup-core.js
CHANGED
|
@@ -138,3 +138,22 @@ export function mergeSetupIntoConfig(existing, plan) {
|
|
|
138
138
|
}
|
|
139
139
|
return { config, changes };
|
|
140
140
|
}
|
|
141
|
+
export function existingAccountDefaults(config, id) {
|
|
142
|
+
const entries = asRecord(asRecord(asRecord(config)?.plugins)?.entries);
|
|
143
|
+
const entryConfig = asRecord(asRecord(entries?.["multi-clawd"])?.config);
|
|
144
|
+
const accounts = Array.isArray(entryConfig?.accounts) ? entryConfig?.accounts : [];
|
|
145
|
+
const acc = accounts.map(asRecord).find((a) => a?.id === id);
|
|
146
|
+
if (!acc)
|
|
147
|
+
return undefined;
|
|
148
|
+
return {
|
|
149
|
+
configDir: typeof acc.configDir === "string" ? acc.configDir : undefined,
|
|
150
|
+
label: typeof acc.label === "string" ? acc.label : undefined,
|
|
151
|
+
hasCredentials: acc.native === true ||
|
|
152
|
+
acc.oauthTokenRef !== undefined ||
|
|
153
|
+
acc.oauthTokenFile !== undefined ||
|
|
154
|
+
typeof acc.configDir === "string",
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
export function looksLikeSecretRef(id) {
|
|
158
|
+
return id.includes("://");
|
|
159
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drakon-systems/multi-clawd",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
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.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/scripts/setup.mjs
CHANGED
|
@@ -41,7 +41,7 @@ try {
|
|
|
41
41
|
console.error("setup: built dist/ modules are missing — run `npm run build` first (source checkout) or reinstall the plugin.");
|
|
42
42
|
process.exit(1);
|
|
43
43
|
}
|
|
44
|
-
const { buildMainAccount, buildSecondAccount, buildPool, validateSecondConfigDir, planFromExisting, mergeSetupIntoConfig } = core;
|
|
44
|
+
const { buildMainAccount, buildSecondAccount, buildPool, validateSecondConfigDir, planFromExisting, mergeSetupIntoConfig, existingAccountDefaults, looksLikeSecretRef } = core;
|
|
45
45
|
|
|
46
46
|
// Line-queued prompts: interactive AND pipe-safe. With piped stdin, readline
|
|
47
47
|
// emits every buffered line immediately — a plain question() would capture one
|
|
@@ -128,9 +128,29 @@ if (await yes("Add your MAIN account (the machine's existing `claude` login) to
|
|
|
128
128
|
// ── second account ───────────────────────────────────────────────────────────
|
|
129
129
|
if (await yes("Set up a SECOND Claude account (its own isolated config dir)?")) {
|
|
130
130
|
const id = await ask(" id for the second account:", "claw2");
|
|
131
|
+
// Existing-aware: this account may already be fully configured. Pressing
|
|
132
|
+
// Enter through prompts must NEVER overwrite a working account, so the
|
|
133
|
+
// default here is to keep it exactly as it is.
|
|
134
|
+
const prior = existingAccountDefaults(existing, id);
|
|
135
|
+
if (prior?.hasCredentials) {
|
|
136
|
+
console.log(
|
|
137
|
+
` "${id}" is already configured${prior.label ? ` (${prior.label}` : " ("}${prior.configDir ? `, dir ${prior.configDir})` : ")"}.`,
|
|
138
|
+
);
|
|
139
|
+
if (await yes(" Keep its existing credentials and config unchanged?", true)) {
|
|
140
|
+
accounts.push({ id });
|
|
141
|
+
console.log(" ✅ keeping as-is");
|
|
142
|
+
} else {
|
|
143
|
+
await secondAccountFlow(id, prior);
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
await secondAccountFlow(id, prior);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function secondAccountFlow(id, prior) {
|
|
131
151
|
let configDir;
|
|
132
152
|
for (;;) {
|
|
133
|
-
configDir = await ask(" isolated config dir:", `~/.${id}`);
|
|
153
|
+
configDir = await ask(" isolated config dir:", prior?.configDir ?? `~/.${id}`);
|
|
134
154
|
const err = validateSecondConfigDir(configDir);
|
|
135
155
|
if (!err) break;
|
|
136
156
|
console.log(` ✗ ${err}`);
|
|
@@ -158,12 +178,19 @@ if (await yes("Set up a SECOND Claude account (its own isolated config dir)?"))
|
|
|
158
178
|
let refId;
|
|
159
179
|
for (;;) {
|
|
160
180
|
refId = await ask(" secret reference (e.g. op://Vault/Item/field):");
|
|
161
|
-
if (refId)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
181
|
+
if (!refId) {
|
|
182
|
+
if (stdinClosed) {
|
|
183
|
+
console.error("setup: a secret reference is required for token source 1 — aborting (nothing written).");
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
console.log(" ✗ the reference is required (it is NOT the token itself — just the pointer to it; answer 3 above for no token)");
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
if (!looksLikeSecretRef(refId)) {
|
|
190
|
+
console.log(` ⚠ "${refId}" doesn't look like a secret reference (expected something URI-like, e.g. op://Vault/Item/field)`);
|
|
191
|
+
if (!(await yes(" Use it anyway?", false))) continue;
|
|
165
192
|
}
|
|
166
|
-
|
|
193
|
+
break;
|
|
167
194
|
}
|
|
168
195
|
tokenSource = { kind: "ref", ref: { source: "exec", provider, id: refId } };
|
|
169
196
|
} else if (choice === "2") {
|
|
@@ -171,7 +198,7 @@ if (await yes("Set up a SECOND Claude account (its own isolated config dir)?"))
|
|
|
171
198
|
} else {
|
|
172
199
|
tokenSource = { kind: "dir-login" };
|
|
173
200
|
}
|
|
174
|
-
accounts.push(buildSecondAccount({ id, label: await ask(" label:", "Second Claude"), configDir, tokenSource }));
|
|
201
|
+
accounts.push(buildSecondAccount({ id, label: await ask(" label:", prior?.label ?? "Second Claude"), configDir, tokenSource }));
|
|
175
202
|
}
|
|
176
203
|
|
|
177
204
|
if (accounts.length === 0 && state.accountIds.length === 0) {
|