caflip 0.2.0
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 +128 -0
- package/bin/caflip +33 -0
- package/dist/cli.js +3420 -0
- package/install.sh +47 -0
- package/package.json +31 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,3420 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// @bun
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
var __create = Object.create;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __defProp = Object.defineProperty;
|
|
7
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
10
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
11
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
12
|
+
for (let key of __getOwnPropNames(mod))
|
|
13
|
+
if (!__hasOwnProp.call(to, key))
|
|
14
|
+
__defProp(to, key, {
|
|
15
|
+
get: () => mod[key],
|
|
16
|
+
enumerable: true
|
|
17
|
+
});
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
21
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
22
|
+
|
|
23
|
+
// node_modules/cli-width/index.js
|
|
24
|
+
var require_cli_width = __commonJS((exports, module) => {
|
|
25
|
+
module.exports = cliWidth;
|
|
26
|
+
function normalizeOpts(options) {
|
|
27
|
+
const defaultOpts = {
|
|
28
|
+
defaultWidth: 0,
|
|
29
|
+
output: process.stdout,
|
|
30
|
+
tty: __require("tty")
|
|
31
|
+
};
|
|
32
|
+
if (!options) {
|
|
33
|
+
return defaultOpts;
|
|
34
|
+
}
|
|
35
|
+
Object.keys(defaultOpts).forEach(function(key) {
|
|
36
|
+
if (!options[key]) {
|
|
37
|
+
options[key] = defaultOpts[key];
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
return options;
|
|
41
|
+
}
|
|
42
|
+
function cliWidth(options) {
|
|
43
|
+
const opts = normalizeOpts(options);
|
|
44
|
+
if (opts.output.getWindowSize) {
|
|
45
|
+
return opts.output.getWindowSize()[0] || opts.defaultWidth;
|
|
46
|
+
}
|
|
47
|
+
if (opts.tty.getWindowSize) {
|
|
48
|
+
return opts.tty.getWindowSize()[1] || opts.defaultWidth;
|
|
49
|
+
}
|
|
50
|
+
if (opts.output.columns) {
|
|
51
|
+
return opts.output.columns;
|
|
52
|
+
}
|
|
53
|
+
if (process.env.CLI_WIDTH) {
|
|
54
|
+
const width = parseInt(process.env.CLI_WIDTH, 10);
|
|
55
|
+
if (!isNaN(width) && width !== 0) {
|
|
56
|
+
return width;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return opts.defaultWidth;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// node_modules/mute-stream/lib/index.js
|
|
64
|
+
var require_lib = __commonJS((exports, module) => {
|
|
65
|
+
var Stream = __require("stream");
|
|
66
|
+
|
|
67
|
+
class MuteStream extends Stream {
|
|
68
|
+
#isTTY = null;
|
|
69
|
+
constructor(opts = {}) {
|
|
70
|
+
super(opts);
|
|
71
|
+
this.writable = this.readable = true;
|
|
72
|
+
this.muted = false;
|
|
73
|
+
this.on("pipe", this._onpipe);
|
|
74
|
+
this.replace = opts.replace;
|
|
75
|
+
this._prompt = opts.prompt || null;
|
|
76
|
+
this._hadControl = false;
|
|
77
|
+
}
|
|
78
|
+
#destSrc(key, def) {
|
|
79
|
+
if (this._dest) {
|
|
80
|
+
return this._dest[key];
|
|
81
|
+
}
|
|
82
|
+
if (this._src) {
|
|
83
|
+
return this._src[key];
|
|
84
|
+
}
|
|
85
|
+
return def;
|
|
86
|
+
}
|
|
87
|
+
#proxy(method, ...args) {
|
|
88
|
+
if (typeof this._dest?.[method] === "function") {
|
|
89
|
+
this._dest[method](...args);
|
|
90
|
+
}
|
|
91
|
+
if (typeof this._src?.[method] === "function") {
|
|
92
|
+
this._src[method](...args);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
get isTTY() {
|
|
96
|
+
if (this.#isTTY !== null) {
|
|
97
|
+
return this.#isTTY;
|
|
98
|
+
}
|
|
99
|
+
return this.#destSrc("isTTY", false);
|
|
100
|
+
}
|
|
101
|
+
set isTTY(val) {
|
|
102
|
+
this.#isTTY = val;
|
|
103
|
+
}
|
|
104
|
+
get rows() {
|
|
105
|
+
return this.#destSrc("rows");
|
|
106
|
+
}
|
|
107
|
+
get columns() {
|
|
108
|
+
return this.#destSrc("columns");
|
|
109
|
+
}
|
|
110
|
+
mute() {
|
|
111
|
+
this.muted = true;
|
|
112
|
+
}
|
|
113
|
+
unmute() {
|
|
114
|
+
this.muted = false;
|
|
115
|
+
}
|
|
116
|
+
_onpipe(src) {
|
|
117
|
+
this._src = src;
|
|
118
|
+
}
|
|
119
|
+
pipe(dest, options) {
|
|
120
|
+
this._dest = dest;
|
|
121
|
+
return super.pipe(dest, options);
|
|
122
|
+
}
|
|
123
|
+
pause() {
|
|
124
|
+
if (this._src) {
|
|
125
|
+
return this._src.pause();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
resume() {
|
|
129
|
+
if (this._src) {
|
|
130
|
+
return this._src.resume();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
write(c) {
|
|
134
|
+
if (this.muted) {
|
|
135
|
+
if (!this.replace) {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
if (c.match(/^\u001b/)) {
|
|
139
|
+
if (c.indexOf(this._prompt) === 0) {
|
|
140
|
+
c = c.slice(this._prompt.length);
|
|
141
|
+
c = c.replace(/./g, this.replace);
|
|
142
|
+
c = this._prompt + c;
|
|
143
|
+
}
|
|
144
|
+
this._hadControl = true;
|
|
145
|
+
return this.emit("data", c);
|
|
146
|
+
} else {
|
|
147
|
+
if (this._prompt && this._hadControl && c.indexOf(this._prompt) === 0) {
|
|
148
|
+
this._hadControl = false;
|
|
149
|
+
this.emit("data", this._prompt);
|
|
150
|
+
c = c.slice(this._prompt.length);
|
|
151
|
+
}
|
|
152
|
+
c = c.toString().replace(/./g, this.replace);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
this.emit("data", c);
|
|
156
|
+
}
|
|
157
|
+
end(c) {
|
|
158
|
+
if (this.muted) {
|
|
159
|
+
if (c && this.replace) {
|
|
160
|
+
c = c.toString().replace(/./g, this.replace);
|
|
161
|
+
} else {
|
|
162
|
+
c = null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (c) {
|
|
166
|
+
this.emit("data", c);
|
|
167
|
+
}
|
|
168
|
+
this.emit("end");
|
|
169
|
+
}
|
|
170
|
+
destroy(...args) {
|
|
171
|
+
return this.#proxy("destroy", ...args);
|
|
172
|
+
}
|
|
173
|
+
destroySoon(...args) {
|
|
174
|
+
return this.#proxy("destroySoon", ...args);
|
|
175
|
+
}
|
|
176
|
+
close(...args) {
|
|
177
|
+
return this.#proxy("close", ...args);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
module.exports = MuteStream;
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// src/index.ts
|
|
184
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10, mkdirSync as mkdirSync6 } from "fs";
|
|
185
|
+
|
|
186
|
+
// src/config.ts
|
|
187
|
+
import { homedir } from "os";
|
|
188
|
+
import { existsSync, readFileSync } from "fs";
|
|
189
|
+
import { join } from "path";
|
|
190
|
+
function getBackupDir(provider) {
|
|
191
|
+
return join(homedir(), ".caflip-backup", provider);
|
|
192
|
+
}
|
|
193
|
+
function getSequenceFile(provider) {
|
|
194
|
+
return join(getBackupDir(provider), "sequence.json");
|
|
195
|
+
}
|
|
196
|
+
function getLockDir(provider) {
|
|
197
|
+
return join(getBackupDir(provider), ".lock");
|
|
198
|
+
}
|
|
199
|
+
function getConfigsDir(provider) {
|
|
200
|
+
return join(getBackupDir(provider), "configs");
|
|
201
|
+
}
|
|
202
|
+
function getCredentialsDir(provider) {
|
|
203
|
+
return join(getBackupDir(provider), "credentials");
|
|
204
|
+
}
|
|
205
|
+
var BACKUP_DIR = getBackupDir("claude");
|
|
206
|
+
var SEQUENCE_FILE = getSequenceFile("claude");
|
|
207
|
+
var LOCK_DIR = getLockDir("claude");
|
|
208
|
+
var CONFIGS_DIR = getConfigsDir("claude");
|
|
209
|
+
var CREDENTIALS_DIR = getCredentialsDir("claude");
|
|
210
|
+
var RESERVED_COMMANDS = [
|
|
211
|
+
"list",
|
|
212
|
+
"add",
|
|
213
|
+
"remove",
|
|
214
|
+
"next",
|
|
215
|
+
"status",
|
|
216
|
+
"alias",
|
|
217
|
+
"claude",
|
|
218
|
+
"codex",
|
|
219
|
+
"help"
|
|
220
|
+
];
|
|
221
|
+
function getClaudeConfigPath() {
|
|
222
|
+
const primary = join(homedir(), ".claude", ".claude.json");
|
|
223
|
+
const fallback = join(homedir(), ".claude.json");
|
|
224
|
+
if (existsSync(primary)) {
|
|
225
|
+
try {
|
|
226
|
+
const content = JSON.parse(readFileSync(primary, "utf-8"));
|
|
227
|
+
if (content.oauthAccount) {
|
|
228
|
+
return primary;
|
|
229
|
+
}
|
|
230
|
+
} catch {}
|
|
231
|
+
}
|
|
232
|
+
return fallback;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// src/accounts.ts
|
|
236
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
237
|
+
|
|
238
|
+
// src/files.ts
|
|
239
|
+
import { existsSync as existsSync2, mkdirSync, rmSync, chmodSync, renameSync, writeFileSync, readFileSync as readFileSync2 } from "fs";
|
|
240
|
+
import { dirname, join as join2 } from "path";
|
|
241
|
+
import { randomBytes } from "crypto";
|
|
242
|
+
async function writeJsonAtomic(filePath, data) {
|
|
243
|
+
const jsonStr = JSON.stringify(data, null, 2);
|
|
244
|
+
JSON.parse(jsonStr);
|
|
245
|
+
const dir = dirname(filePath);
|
|
246
|
+
if (!existsSync2(dir)) {
|
|
247
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
248
|
+
}
|
|
249
|
+
chmodSync(dir, 448);
|
|
250
|
+
const tempFile = `${filePath}.${Date.now()}.${randomBytes(8).toString("hex")}.tmp`;
|
|
251
|
+
try {
|
|
252
|
+
writeFileSync(tempFile, jsonStr, { mode: 384, flag: "wx" });
|
|
253
|
+
renameSync(tempFile, filePath);
|
|
254
|
+
chmodSync(filePath, 384);
|
|
255
|
+
} catch (err) {
|
|
256
|
+
try {
|
|
257
|
+
rmSync(tempFile, { force: true });
|
|
258
|
+
} catch {}
|
|
259
|
+
throw err;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// src/accounts.ts
|
|
264
|
+
async function initSequenceFile(path) {
|
|
265
|
+
if (existsSync3(path))
|
|
266
|
+
return;
|
|
267
|
+
const data = {
|
|
268
|
+
activeAccountNumber: null,
|
|
269
|
+
lastUpdated: new Date().toISOString(),
|
|
270
|
+
sequence: [],
|
|
271
|
+
accounts: {}
|
|
272
|
+
};
|
|
273
|
+
await writeJsonAtomic(path, data);
|
|
274
|
+
}
|
|
275
|
+
async function loadSequence(path) {
|
|
276
|
+
return JSON.parse(readFileSync3(path, "utf-8"));
|
|
277
|
+
}
|
|
278
|
+
function getNextAccountNumber(seq) {
|
|
279
|
+
const keys = Object.keys(seq.accounts).map(Number);
|
|
280
|
+
if (keys.length === 0)
|
|
281
|
+
return 1;
|
|
282
|
+
return Math.max(...keys) + 1;
|
|
283
|
+
}
|
|
284
|
+
function accountExists(seq, email) {
|
|
285
|
+
return Object.values(seq.accounts).some((a) => a.email === email);
|
|
286
|
+
}
|
|
287
|
+
function addAccountToSequence(seq, info) {
|
|
288
|
+
const num = getNextAccountNumber(seq);
|
|
289
|
+
const numStr = String(num);
|
|
290
|
+
const account = {
|
|
291
|
+
email: info.email,
|
|
292
|
+
uuid: info.uuid,
|
|
293
|
+
added: new Date().toISOString()
|
|
294
|
+
};
|
|
295
|
+
if (info.alias) {
|
|
296
|
+
account.alias = info.alias;
|
|
297
|
+
}
|
|
298
|
+
return {
|
|
299
|
+
...seq,
|
|
300
|
+
accounts: { ...seq.accounts, [numStr]: account },
|
|
301
|
+
sequence: [...seq.sequence, num],
|
|
302
|
+
activeAccountNumber: num,
|
|
303
|
+
lastUpdated: new Date().toISOString()
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
function removeAccountFromSequence(seq, accountNum) {
|
|
307
|
+
const numValue = Number(accountNum);
|
|
308
|
+
const { [accountNum]: _, ...remainingAccounts } = seq.accounts;
|
|
309
|
+
const remainingSequence = seq.sequence.filter((n) => n !== numValue);
|
|
310
|
+
let nextActive = seq.activeAccountNumber;
|
|
311
|
+
if (remainingSequence.length === 0) {
|
|
312
|
+
nextActive = null;
|
|
313
|
+
} else if (seq.activeAccountNumber === numValue) {
|
|
314
|
+
nextActive = remainingSequence[0];
|
|
315
|
+
}
|
|
316
|
+
return {
|
|
317
|
+
...seq,
|
|
318
|
+
accounts: remainingAccounts,
|
|
319
|
+
sequence: remainingSequence,
|
|
320
|
+
activeAccountNumber: nextActive,
|
|
321
|
+
lastUpdated: new Date().toISOString()
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
function getPostRemovalAction(original, updated, removedAccountNum) {
|
|
325
|
+
const removedNum = Number(removedAccountNum);
|
|
326
|
+
if (original.activeAccountNumber !== removedNum) {
|
|
327
|
+
return { type: "none" };
|
|
328
|
+
}
|
|
329
|
+
if (updated.activeAccountNumber === null) {
|
|
330
|
+
return { type: "logout" };
|
|
331
|
+
}
|
|
332
|
+
return {
|
|
333
|
+
type: "switch",
|
|
334
|
+
targetAccountNumber: String(updated.activeAccountNumber)
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
function resolveManagedAccountNumberForEmail(seq, currentEmail) {
|
|
338
|
+
if (!currentEmail || currentEmail === "none") {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
const accountNum = resolveAccountIdentifier(seq, currentEmail);
|
|
342
|
+
if (!accountNum) {
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
return Number(accountNum);
|
|
346
|
+
}
|
|
347
|
+
function getNextInSequence(seq) {
|
|
348
|
+
const currentIndex = seq.sequence.indexOf(seq.activeAccountNumber);
|
|
349
|
+
const nextIndex = (currentIndex + 1) % seq.sequence.length;
|
|
350
|
+
return seq.sequence[nextIndex];
|
|
351
|
+
}
|
|
352
|
+
function resolveAccountIdentifier(seq, identifier) {
|
|
353
|
+
if (/^\d+$/.test(identifier)) {
|
|
354
|
+
if (seq.accounts[identifier]) {
|
|
355
|
+
return identifier;
|
|
356
|
+
}
|
|
357
|
+
const uiIndex = Number(identifier) - 1;
|
|
358
|
+
if (uiIndex >= 0 && uiIndex < seq.sequence.length) {
|
|
359
|
+
return String(seq.sequence[uiIndex]);
|
|
360
|
+
}
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
for (const [num, account] of Object.entries(seq.accounts)) {
|
|
364
|
+
if (account.email === identifier)
|
|
365
|
+
return num;
|
|
366
|
+
}
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
function resolveAliasTargetAccount(seq, options) {
|
|
370
|
+
if (options.identifier) {
|
|
371
|
+
if (/^\d+$/.test(options.identifier)) {
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
for (const [num, account] of Object.entries(seq.accounts)) {
|
|
375
|
+
if (account.email === options.identifier)
|
|
376
|
+
return num;
|
|
377
|
+
}
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
380
|
+
if (!options.currentEmail || options.currentEmail === "none") {
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
return resolveAccountIdentifier(seq, options.currentEmail);
|
|
384
|
+
}
|
|
385
|
+
function getDisplayAccountNumber(seq, accountNum) {
|
|
386
|
+
const idx = seq.sequence.indexOf(Number(accountNum));
|
|
387
|
+
return idx === -1 ? null : idx + 1;
|
|
388
|
+
}
|
|
389
|
+
function getDisplayAccountLabel(seq, accountNum) {
|
|
390
|
+
const displayNum = getDisplayAccountNumber(seq, accountNum);
|
|
391
|
+
if (displayNum === null) {
|
|
392
|
+
return `Account-${String(accountNum)}`;
|
|
393
|
+
}
|
|
394
|
+
return `Account-${displayNum}`;
|
|
395
|
+
}
|
|
396
|
+
function setAlias(seq, accountNum, alias) {
|
|
397
|
+
for (const [num, account2] of Object.entries(seq.accounts)) {
|
|
398
|
+
if (num !== accountNum && account2.alias === alias) {
|
|
399
|
+
const displayLabel = getDisplayAccountLabel(seq, num);
|
|
400
|
+
throw new Error(`Alias "${alias}" is already in use by ${displayLabel} (${account2.email})`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
const account = seq.accounts[accountNum];
|
|
404
|
+
if (!account) {
|
|
405
|
+
throw new Error(`${getDisplayAccountLabel(seq, accountNum)} does not exist`);
|
|
406
|
+
}
|
|
407
|
+
return {
|
|
408
|
+
...seq,
|
|
409
|
+
accounts: {
|
|
410
|
+
...seq.accounts,
|
|
411
|
+
[accountNum]: { ...account, alias }
|
|
412
|
+
},
|
|
413
|
+
lastUpdated: new Date().toISOString()
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
function findAccountByAlias(seq, alias) {
|
|
417
|
+
for (const [num, account] of Object.entries(seq.accounts)) {
|
|
418
|
+
if (account.alias === alias)
|
|
419
|
+
return num;
|
|
420
|
+
}
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// src/files.ts
|
|
425
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2, rmSync as rmSync2, chmodSync as chmodSync2, renameSync as renameSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync4 } from "fs";
|
|
426
|
+
import { dirname as dirname2, join as join3 } from "path";
|
|
427
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
428
|
+
async function writeJsonAtomic2(filePath, data) {
|
|
429
|
+
const jsonStr = JSON.stringify(data, null, 2);
|
|
430
|
+
JSON.parse(jsonStr);
|
|
431
|
+
const dir = dirname2(filePath);
|
|
432
|
+
if (!existsSync4(dir)) {
|
|
433
|
+
mkdirSync2(dir, { recursive: true, mode: 448 });
|
|
434
|
+
}
|
|
435
|
+
chmodSync2(dir, 448);
|
|
436
|
+
const tempFile = `${filePath}.${Date.now()}.${randomBytes2(8).toString("hex")}.tmp`;
|
|
437
|
+
try {
|
|
438
|
+
writeFileSync2(tempFile, jsonStr, { mode: 384, flag: "wx" });
|
|
439
|
+
renameSync2(tempFile, filePath);
|
|
440
|
+
chmodSync2(filePath, 384);
|
|
441
|
+
} catch (err) {
|
|
442
|
+
try {
|
|
443
|
+
rmSync2(tempFile, { force: true });
|
|
444
|
+
} catch {}
|
|
445
|
+
throw err;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
function acquireLock(lockDir) {
|
|
449
|
+
const parentDir = dirname2(lockDir);
|
|
450
|
+
if (!existsSync4(parentDir)) {
|
|
451
|
+
mkdirSync2(parentDir, { recursive: true, mode: 448 });
|
|
452
|
+
}
|
|
453
|
+
try {
|
|
454
|
+
mkdirSync2(lockDir, { recursive: false, mode: 448 });
|
|
455
|
+
writeLockOwner(lockDir);
|
|
456
|
+
return;
|
|
457
|
+
} catch (err) {
|
|
458
|
+
const code = err instanceof Error && "code" in err ? err.code : undefined;
|
|
459
|
+
if (code === "EEXIST") {
|
|
460
|
+
if (isStaleLock(lockDir)) {
|
|
461
|
+
rmSync2(lockDir, { recursive: true, force: true });
|
|
462
|
+
mkdirSync2(lockDir, { recursive: false, mode: 448 });
|
|
463
|
+
writeLockOwner(lockDir);
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
throw new Error(`Another instance is running. If this is wrong, remove ${lockDir}`);
|
|
467
|
+
}
|
|
468
|
+
if (err instanceof Error) {
|
|
469
|
+
throw new Error(`Failed to acquire lock at ${lockDir}: ${err.message}`);
|
|
470
|
+
}
|
|
471
|
+
throw new Error(`Failed to acquire lock at ${lockDir}`);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
function releaseLock(lockDir) {
|
|
475
|
+
try {
|
|
476
|
+
rmSync2(lockDir, { recursive: true, force: true });
|
|
477
|
+
} catch {}
|
|
478
|
+
}
|
|
479
|
+
function getLockOwnerPath(lockDir) {
|
|
480
|
+
return join3(lockDir, "owner.json");
|
|
481
|
+
}
|
|
482
|
+
function writeLockOwner(lockDir) {
|
|
483
|
+
const ownerPath = getLockOwnerPath(lockDir);
|
|
484
|
+
const owner = {
|
|
485
|
+
pid: process.pid,
|
|
486
|
+
startedAt: new Date().toISOString()
|
|
487
|
+
};
|
|
488
|
+
writeFileSync2(ownerPath, JSON.stringify(owner), { mode: 384 });
|
|
489
|
+
chmodSync2(lockDir, 448);
|
|
490
|
+
}
|
|
491
|
+
function isStaleLock(lockDir) {
|
|
492
|
+
const ownerPath = getLockOwnerPath(lockDir);
|
|
493
|
+
if (!existsSync4(ownerPath)) {
|
|
494
|
+
return true;
|
|
495
|
+
}
|
|
496
|
+
try {
|
|
497
|
+
const owner = JSON.parse(readFileSync4(ownerPath, "utf-8"));
|
|
498
|
+
if (!owner.pid || !Number.isInteger(owner.pid) || owner.pid <= 0) {
|
|
499
|
+
return true;
|
|
500
|
+
}
|
|
501
|
+
return !isProcessAlive(owner.pid);
|
|
502
|
+
} catch {
|
|
503
|
+
return true;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
function isProcessAlive(pid) {
|
|
507
|
+
try {
|
|
508
|
+
process.kill(pid, 0);
|
|
509
|
+
return true;
|
|
510
|
+
} catch (err) {
|
|
511
|
+
if (err instanceof Error && "code" in err) {
|
|
512
|
+
const code = err.code;
|
|
513
|
+
if (code === "ESRCH")
|
|
514
|
+
return false;
|
|
515
|
+
if (code === "EPERM")
|
|
516
|
+
return true;
|
|
517
|
+
}
|
|
518
|
+
return true;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// src/config.ts
|
|
523
|
+
import { homedir as homedir2 } from "os";
|
|
524
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
|
|
525
|
+
import { join as join4 } from "path";
|
|
526
|
+
function getBackupDir2(provider) {
|
|
527
|
+
return join4(homedir2(), ".caflip-backup", provider);
|
|
528
|
+
}
|
|
529
|
+
function getSequenceFile2(provider) {
|
|
530
|
+
return join4(getBackupDir2(provider), "sequence.json");
|
|
531
|
+
}
|
|
532
|
+
function getLockDir2(provider) {
|
|
533
|
+
return join4(getBackupDir2(provider), ".lock");
|
|
534
|
+
}
|
|
535
|
+
function getConfigsDir2(provider) {
|
|
536
|
+
return join4(getBackupDir2(provider), "configs");
|
|
537
|
+
}
|
|
538
|
+
function getCredentialsDir2(provider) {
|
|
539
|
+
return join4(getBackupDir2(provider), "credentials");
|
|
540
|
+
}
|
|
541
|
+
var BACKUP_DIR2 = getBackupDir2("claude");
|
|
542
|
+
var SEQUENCE_FILE2 = getSequenceFile2("claude");
|
|
543
|
+
var LOCK_DIR2 = getLockDir2("claude");
|
|
544
|
+
var CONFIGS_DIR2 = getConfigsDir2("claude");
|
|
545
|
+
var CREDENTIALS_DIR2 = getCredentialsDir2("claude");
|
|
546
|
+
var RESERVED_COMMANDS2 = [
|
|
547
|
+
"list",
|
|
548
|
+
"add",
|
|
549
|
+
"remove",
|
|
550
|
+
"next",
|
|
551
|
+
"status",
|
|
552
|
+
"alias",
|
|
553
|
+
"claude",
|
|
554
|
+
"codex",
|
|
555
|
+
"help"
|
|
556
|
+
];
|
|
557
|
+
function detectPlatform() {
|
|
558
|
+
switch (process.platform) {
|
|
559
|
+
case "darwin":
|
|
560
|
+
return "macos";
|
|
561
|
+
case "linux":
|
|
562
|
+
return process.env.WSL_DISTRO_NAME ? "wsl" : "linux";
|
|
563
|
+
case "win32":
|
|
564
|
+
return "windows";
|
|
565
|
+
default:
|
|
566
|
+
return "unknown";
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
function getClaudeConfigPath2() {
|
|
570
|
+
const primary = join4(homedir2(), ".claude", ".claude.json");
|
|
571
|
+
const fallback = join4(homedir2(), ".claude.json");
|
|
572
|
+
if (existsSync5(primary)) {
|
|
573
|
+
try {
|
|
574
|
+
const content = JSON.parse(readFileSync5(primary, "utf-8"));
|
|
575
|
+
if (content.oauthAccount) {
|
|
576
|
+
return primary;
|
|
577
|
+
}
|
|
578
|
+
} catch {}
|
|
579
|
+
}
|
|
580
|
+
return fallback;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// src/validation.ts
|
|
584
|
+
var EMAIL_REGEX = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
|
585
|
+
var ALIAS_REGEX = /^[a-z][a-z0-9-]*$/;
|
|
586
|
+
function validateEmail(email) {
|
|
587
|
+
return EMAIL_REGEX.test(email);
|
|
588
|
+
}
|
|
589
|
+
function sanitizeEmailForFilename(email) {
|
|
590
|
+
if (!validateEmail(email))
|
|
591
|
+
return false;
|
|
592
|
+
if (email.includes("..") || email.includes("/") || email.includes("\x00")) {
|
|
593
|
+
return false;
|
|
594
|
+
}
|
|
595
|
+
return true;
|
|
596
|
+
}
|
|
597
|
+
function validateAlias(alias) {
|
|
598
|
+
if (alias.length < 2) {
|
|
599
|
+
return { valid: false, reason: "Alias must be at least 2 characters" };
|
|
600
|
+
}
|
|
601
|
+
if (/^\d+$/.test(alias)) {
|
|
602
|
+
return {
|
|
603
|
+
valid: false,
|
|
604
|
+
reason: "Alias cannot be purely numeric (would conflict with account numbers)"
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
if (!ALIAS_REGEX.test(alias)) {
|
|
608
|
+
return {
|
|
609
|
+
valid: false,
|
|
610
|
+
reason: "Alias must contain only lowercase letters, numbers, and hyphens, and start with a letter"
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
if (RESERVED_COMMANDS2.includes(alias)) {
|
|
614
|
+
return { valid: false, reason: `"${alias}" is a reserved command name` };
|
|
615
|
+
}
|
|
616
|
+
return { valid: true };
|
|
617
|
+
}
|
|
618
|
+
// package.json
|
|
619
|
+
var package_default = {
|
|
620
|
+
name: "caflip",
|
|
621
|
+
version: "0.2.0",
|
|
622
|
+
type: "module",
|
|
623
|
+
bin: {
|
|
624
|
+
caflip: "bin/caflip"
|
|
625
|
+
},
|
|
626
|
+
files: [
|
|
627
|
+
"bin",
|
|
628
|
+
"dist",
|
|
629
|
+
"README.md",
|
|
630
|
+
"install.sh"
|
|
631
|
+
],
|
|
632
|
+
scripts: {
|
|
633
|
+
build: "bun build --compile src/index.ts --outfile caflip",
|
|
634
|
+
buildjs: "bun build src/index.ts --target=node --outfile dist/cli.js",
|
|
635
|
+
dev: "bun run src/index.ts",
|
|
636
|
+
prepublishOnly: "bun test && bun run buildjs",
|
|
637
|
+
start: "bun run src/index.ts",
|
|
638
|
+
test: "bun test"
|
|
639
|
+
},
|
|
640
|
+
devDependencies: {
|
|
641
|
+
"@types/bun": "latest"
|
|
642
|
+
},
|
|
643
|
+
peerDependencies: {
|
|
644
|
+
typescript: "^5"
|
|
645
|
+
},
|
|
646
|
+
dependencies: {
|
|
647
|
+
"@inquirer/prompts": "^8.2.1"
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
|
|
651
|
+
// src/interactive.ts
|
|
652
|
+
import readline3 from "node:readline";
|
|
653
|
+
|
|
654
|
+
// node_modules/@inquirer/core/dist/lib/key.js
|
|
655
|
+
var isUpKey = (key, keybindings = []) => key.name === "up" || keybindings.includes("vim") && key.name === "k" || keybindings.includes("emacs") && key.ctrl && key.name === "p";
|
|
656
|
+
var isDownKey = (key, keybindings = []) => key.name === "down" || keybindings.includes("vim") && key.name === "j" || keybindings.includes("emacs") && key.ctrl && key.name === "n";
|
|
657
|
+
var isBackspaceKey = (key) => key.name === "backspace";
|
|
658
|
+
var isTabKey = (key) => key.name === "tab";
|
|
659
|
+
var isNumberKey = (key) => "1234567890".includes(key.name);
|
|
660
|
+
var isEnterKey = (key) => key.name === "enter" || key.name === "return";
|
|
661
|
+
// node_modules/@inquirer/core/dist/lib/errors.js
|
|
662
|
+
class AbortPromptError extends Error {
|
|
663
|
+
name = "AbortPromptError";
|
|
664
|
+
message = "Prompt was aborted";
|
|
665
|
+
constructor(options) {
|
|
666
|
+
super();
|
|
667
|
+
this.cause = options?.cause;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
class CancelPromptError extends Error {
|
|
672
|
+
name = "CancelPromptError";
|
|
673
|
+
message = "Prompt was canceled";
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
class ExitPromptError extends Error {
|
|
677
|
+
name = "ExitPromptError";
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
class HookError extends Error {
|
|
681
|
+
name = "HookError";
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
class ValidationError extends Error {
|
|
685
|
+
name = "ValidationError";
|
|
686
|
+
}
|
|
687
|
+
// node_modules/@inquirer/core/dist/lib/use-state.js
|
|
688
|
+
import { AsyncResource as AsyncResource2 } from "node:async_hooks";
|
|
689
|
+
|
|
690
|
+
// node_modules/@inquirer/core/dist/lib/hook-engine.js
|
|
691
|
+
import { AsyncLocalStorage, AsyncResource } from "node:async_hooks";
|
|
692
|
+
var hookStorage = new AsyncLocalStorage;
|
|
693
|
+
function createStore(rl) {
|
|
694
|
+
const store = {
|
|
695
|
+
rl,
|
|
696
|
+
hooks: [],
|
|
697
|
+
hooksCleanup: [],
|
|
698
|
+
hooksEffect: [],
|
|
699
|
+
index: 0,
|
|
700
|
+
handleChange() {}
|
|
701
|
+
};
|
|
702
|
+
return store;
|
|
703
|
+
}
|
|
704
|
+
function withHooks(rl, cb) {
|
|
705
|
+
const store = createStore(rl);
|
|
706
|
+
return hookStorage.run(store, () => {
|
|
707
|
+
function cycle(render) {
|
|
708
|
+
store.handleChange = () => {
|
|
709
|
+
store.index = 0;
|
|
710
|
+
render();
|
|
711
|
+
};
|
|
712
|
+
store.handleChange();
|
|
713
|
+
}
|
|
714
|
+
return cb(cycle);
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
function getStore() {
|
|
718
|
+
const store = hookStorage.getStore();
|
|
719
|
+
if (!store) {
|
|
720
|
+
throw new HookError("[Inquirer] Hook functions can only be called from within a prompt");
|
|
721
|
+
}
|
|
722
|
+
return store;
|
|
723
|
+
}
|
|
724
|
+
function readline() {
|
|
725
|
+
return getStore().rl;
|
|
726
|
+
}
|
|
727
|
+
function withUpdates(fn) {
|
|
728
|
+
const wrapped = (...args) => {
|
|
729
|
+
const store = getStore();
|
|
730
|
+
let shouldUpdate = false;
|
|
731
|
+
const oldHandleChange = store.handleChange;
|
|
732
|
+
store.handleChange = () => {
|
|
733
|
+
shouldUpdate = true;
|
|
734
|
+
};
|
|
735
|
+
const returnValue = fn(...args);
|
|
736
|
+
if (shouldUpdate) {
|
|
737
|
+
oldHandleChange();
|
|
738
|
+
}
|
|
739
|
+
store.handleChange = oldHandleChange;
|
|
740
|
+
return returnValue;
|
|
741
|
+
};
|
|
742
|
+
return AsyncResource.bind(wrapped);
|
|
743
|
+
}
|
|
744
|
+
function withPointer(cb) {
|
|
745
|
+
const store = getStore();
|
|
746
|
+
const { index } = store;
|
|
747
|
+
const pointer = {
|
|
748
|
+
get() {
|
|
749
|
+
return store.hooks[index];
|
|
750
|
+
},
|
|
751
|
+
set(value) {
|
|
752
|
+
store.hooks[index] = value;
|
|
753
|
+
},
|
|
754
|
+
initialized: index in store.hooks
|
|
755
|
+
};
|
|
756
|
+
const returnValue = cb(pointer);
|
|
757
|
+
store.index++;
|
|
758
|
+
return returnValue;
|
|
759
|
+
}
|
|
760
|
+
function handleChange() {
|
|
761
|
+
getStore().handleChange();
|
|
762
|
+
}
|
|
763
|
+
var effectScheduler = {
|
|
764
|
+
queue(cb) {
|
|
765
|
+
const store = getStore();
|
|
766
|
+
const { index } = store;
|
|
767
|
+
store.hooksEffect.push(() => {
|
|
768
|
+
store.hooksCleanup[index]?.();
|
|
769
|
+
const cleanFn = cb(readline());
|
|
770
|
+
if (cleanFn != null && typeof cleanFn !== "function") {
|
|
771
|
+
throw new ValidationError("useEffect return value must be a cleanup function or nothing.");
|
|
772
|
+
}
|
|
773
|
+
store.hooksCleanup[index] = cleanFn;
|
|
774
|
+
});
|
|
775
|
+
},
|
|
776
|
+
run() {
|
|
777
|
+
const store = getStore();
|
|
778
|
+
withUpdates(() => {
|
|
779
|
+
store.hooksEffect.forEach((effect) => {
|
|
780
|
+
effect();
|
|
781
|
+
});
|
|
782
|
+
store.hooksEffect.length = 0;
|
|
783
|
+
})();
|
|
784
|
+
},
|
|
785
|
+
clearAll() {
|
|
786
|
+
const store = getStore();
|
|
787
|
+
store.hooksCleanup.forEach((cleanFn) => {
|
|
788
|
+
cleanFn?.();
|
|
789
|
+
});
|
|
790
|
+
store.hooksEffect.length = 0;
|
|
791
|
+
store.hooksCleanup.length = 0;
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
|
|
795
|
+
// node_modules/@inquirer/core/dist/lib/use-state.js
|
|
796
|
+
function useState(defaultValue) {
|
|
797
|
+
return withPointer((pointer) => {
|
|
798
|
+
const setState = AsyncResource2.bind(function setState2(newValue) {
|
|
799
|
+
if (pointer.get() !== newValue) {
|
|
800
|
+
pointer.set(newValue);
|
|
801
|
+
handleChange();
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
if (pointer.initialized) {
|
|
805
|
+
return [pointer.get(), setState];
|
|
806
|
+
}
|
|
807
|
+
const value = typeof defaultValue === "function" ? defaultValue() : defaultValue;
|
|
808
|
+
pointer.set(value);
|
|
809
|
+
return [value, setState];
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// node_modules/@inquirer/core/dist/lib/use-effect.js
|
|
814
|
+
function useEffect(cb, depArray) {
|
|
815
|
+
withPointer((pointer) => {
|
|
816
|
+
const oldDeps = pointer.get();
|
|
817
|
+
const hasChanged = !Array.isArray(oldDeps) || depArray.some((dep, i) => !Object.is(dep, oldDeps[i]));
|
|
818
|
+
if (hasChanged) {
|
|
819
|
+
effectScheduler.queue(cb);
|
|
820
|
+
}
|
|
821
|
+
pointer.set(depArray);
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// node_modules/@inquirer/core/dist/lib/theme.js
|
|
826
|
+
import { styleText } from "node:util";
|
|
827
|
+
|
|
828
|
+
// node_modules/@inquirer/figures/dist/index.js
|
|
829
|
+
import process2 from "node:process";
|
|
830
|
+
function isUnicodeSupported() {
|
|
831
|
+
if (process2.platform !== "win32") {
|
|
832
|
+
return process2.env["TERM"] !== "linux";
|
|
833
|
+
}
|
|
834
|
+
return Boolean(process2.env["WT_SESSION"]) || Boolean(process2.env["TERMINUS_SUBLIME"]) || process2.env["ConEmuTask"] === "{cmd::Cmder}" || process2.env["TERM_PROGRAM"] === "Terminus-Sublime" || process2.env["TERM_PROGRAM"] === "vscode" || process2.env["TERM"] === "xterm-256color" || process2.env["TERM"] === "alacritty" || process2.env["TERMINAL_EMULATOR"] === "JetBrains-JediTerm";
|
|
835
|
+
}
|
|
836
|
+
var common = {
|
|
837
|
+
circleQuestionMark: "(?)",
|
|
838
|
+
questionMarkPrefix: "(?)",
|
|
839
|
+
square: "█",
|
|
840
|
+
squareDarkShade: "▓",
|
|
841
|
+
squareMediumShade: "▒",
|
|
842
|
+
squareLightShade: "░",
|
|
843
|
+
squareTop: "▀",
|
|
844
|
+
squareBottom: "▄",
|
|
845
|
+
squareLeft: "▌",
|
|
846
|
+
squareRight: "▐",
|
|
847
|
+
squareCenter: "■",
|
|
848
|
+
bullet: "●",
|
|
849
|
+
dot: "․",
|
|
850
|
+
ellipsis: "…",
|
|
851
|
+
pointerSmall: "›",
|
|
852
|
+
triangleUp: "▲",
|
|
853
|
+
triangleUpSmall: "▴",
|
|
854
|
+
triangleDown: "▼",
|
|
855
|
+
triangleDownSmall: "▾",
|
|
856
|
+
triangleLeftSmall: "◂",
|
|
857
|
+
triangleRightSmall: "▸",
|
|
858
|
+
home: "⌂",
|
|
859
|
+
heart: "♥",
|
|
860
|
+
musicNote: "♪",
|
|
861
|
+
musicNoteBeamed: "♫",
|
|
862
|
+
arrowUp: "↑",
|
|
863
|
+
arrowDown: "↓",
|
|
864
|
+
arrowLeft: "←",
|
|
865
|
+
arrowRight: "→",
|
|
866
|
+
arrowLeftRight: "↔",
|
|
867
|
+
arrowUpDown: "↕",
|
|
868
|
+
almostEqual: "≈",
|
|
869
|
+
notEqual: "≠",
|
|
870
|
+
lessOrEqual: "≤",
|
|
871
|
+
greaterOrEqual: "≥",
|
|
872
|
+
identical: "≡",
|
|
873
|
+
infinity: "∞",
|
|
874
|
+
subscriptZero: "₀",
|
|
875
|
+
subscriptOne: "₁",
|
|
876
|
+
subscriptTwo: "₂",
|
|
877
|
+
subscriptThree: "₃",
|
|
878
|
+
subscriptFour: "₄",
|
|
879
|
+
subscriptFive: "₅",
|
|
880
|
+
subscriptSix: "₆",
|
|
881
|
+
subscriptSeven: "₇",
|
|
882
|
+
subscriptEight: "₈",
|
|
883
|
+
subscriptNine: "₉",
|
|
884
|
+
oneHalf: "½",
|
|
885
|
+
oneThird: "⅓",
|
|
886
|
+
oneQuarter: "¼",
|
|
887
|
+
oneFifth: "⅕",
|
|
888
|
+
oneSixth: "⅙",
|
|
889
|
+
oneEighth: "⅛",
|
|
890
|
+
twoThirds: "⅔",
|
|
891
|
+
twoFifths: "⅖",
|
|
892
|
+
threeQuarters: "¾",
|
|
893
|
+
threeFifths: "⅗",
|
|
894
|
+
threeEighths: "⅜",
|
|
895
|
+
fourFifths: "⅘",
|
|
896
|
+
fiveSixths: "⅚",
|
|
897
|
+
fiveEighths: "⅝",
|
|
898
|
+
sevenEighths: "⅞",
|
|
899
|
+
line: "─",
|
|
900
|
+
lineBold: "━",
|
|
901
|
+
lineDouble: "═",
|
|
902
|
+
lineDashed0: "┄",
|
|
903
|
+
lineDashed1: "┅",
|
|
904
|
+
lineDashed2: "┈",
|
|
905
|
+
lineDashed3: "┉",
|
|
906
|
+
lineDashed4: "╌",
|
|
907
|
+
lineDashed5: "╍",
|
|
908
|
+
lineDashed6: "╴",
|
|
909
|
+
lineDashed7: "╶",
|
|
910
|
+
lineDashed8: "╸",
|
|
911
|
+
lineDashed9: "╺",
|
|
912
|
+
lineDashed10: "╼",
|
|
913
|
+
lineDashed11: "╾",
|
|
914
|
+
lineDashed12: "−",
|
|
915
|
+
lineDashed13: "–",
|
|
916
|
+
lineDashed14: "‐",
|
|
917
|
+
lineDashed15: "⁃",
|
|
918
|
+
lineVertical: "│",
|
|
919
|
+
lineVerticalBold: "┃",
|
|
920
|
+
lineVerticalDouble: "║",
|
|
921
|
+
lineVerticalDashed0: "┆",
|
|
922
|
+
lineVerticalDashed1: "┇",
|
|
923
|
+
lineVerticalDashed2: "┊",
|
|
924
|
+
lineVerticalDashed3: "┋",
|
|
925
|
+
lineVerticalDashed4: "╎",
|
|
926
|
+
lineVerticalDashed5: "╏",
|
|
927
|
+
lineVerticalDashed6: "╵",
|
|
928
|
+
lineVerticalDashed7: "╷",
|
|
929
|
+
lineVerticalDashed8: "╹",
|
|
930
|
+
lineVerticalDashed9: "╻",
|
|
931
|
+
lineVerticalDashed10: "╽",
|
|
932
|
+
lineVerticalDashed11: "╿",
|
|
933
|
+
lineDownLeft: "┐",
|
|
934
|
+
lineDownLeftArc: "╮",
|
|
935
|
+
lineDownBoldLeftBold: "┓",
|
|
936
|
+
lineDownBoldLeft: "┒",
|
|
937
|
+
lineDownLeftBold: "┑",
|
|
938
|
+
lineDownDoubleLeftDouble: "╗",
|
|
939
|
+
lineDownDoubleLeft: "╖",
|
|
940
|
+
lineDownLeftDouble: "╕",
|
|
941
|
+
lineDownRight: "┌",
|
|
942
|
+
lineDownRightArc: "╭",
|
|
943
|
+
lineDownBoldRightBold: "┏",
|
|
944
|
+
lineDownBoldRight: "┎",
|
|
945
|
+
lineDownRightBold: "┍",
|
|
946
|
+
lineDownDoubleRightDouble: "╔",
|
|
947
|
+
lineDownDoubleRight: "╓",
|
|
948
|
+
lineDownRightDouble: "╒",
|
|
949
|
+
lineUpLeft: "┘",
|
|
950
|
+
lineUpLeftArc: "╯",
|
|
951
|
+
lineUpBoldLeftBold: "┛",
|
|
952
|
+
lineUpBoldLeft: "┚",
|
|
953
|
+
lineUpLeftBold: "┙",
|
|
954
|
+
lineUpDoubleLeftDouble: "╝",
|
|
955
|
+
lineUpDoubleLeft: "╜",
|
|
956
|
+
lineUpLeftDouble: "╛",
|
|
957
|
+
lineUpRight: "└",
|
|
958
|
+
lineUpRightArc: "╰",
|
|
959
|
+
lineUpBoldRightBold: "┗",
|
|
960
|
+
lineUpBoldRight: "┖",
|
|
961
|
+
lineUpRightBold: "┕",
|
|
962
|
+
lineUpDoubleRightDouble: "╚",
|
|
963
|
+
lineUpDoubleRight: "╙",
|
|
964
|
+
lineUpRightDouble: "╘",
|
|
965
|
+
lineUpDownLeft: "┤",
|
|
966
|
+
lineUpBoldDownBoldLeftBold: "┫",
|
|
967
|
+
lineUpBoldDownBoldLeft: "┨",
|
|
968
|
+
lineUpDownLeftBold: "┥",
|
|
969
|
+
lineUpBoldDownLeftBold: "┩",
|
|
970
|
+
lineUpDownBoldLeftBold: "┪",
|
|
971
|
+
lineUpDownBoldLeft: "┧",
|
|
972
|
+
lineUpBoldDownLeft: "┦",
|
|
973
|
+
lineUpDoubleDownDoubleLeftDouble: "╣",
|
|
974
|
+
lineUpDoubleDownDoubleLeft: "╢",
|
|
975
|
+
lineUpDownLeftDouble: "╡",
|
|
976
|
+
lineUpDownRight: "├",
|
|
977
|
+
lineUpBoldDownBoldRightBold: "┣",
|
|
978
|
+
lineUpBoldDownBoldRight: "┠",
|
|
979
|
+
lineUpDownRightBold: "┝",
|
|
980
|
+
lineUpBoldDownRightBold: "┡",
|
|
981
|
+
lineUpDownBoldRightBold: "┢",
|
|
982
|
+
lineUpDownBoldRight: "┟",
|
|
983
|
+
lineUpBoldDownRight: "┞",
|
|
984
|
+
lineUpDoubleDownDoubleRightDouble: "╠",
|
|
985
|
+
lineUpDoubleDownDoubleRight: "╟",
|
|
986
|
+
lineUpDownRightDouble: "╞",
|
|
987
|
+
lineDownLeftRight: "┬",
|
|
988
|
+
lineDownBoldLeftBoldRightBold: "┳",
|
|
989
|
+
lineDownLeftBoldRightBold: "┯",
|
|
990
|
+
lineDownBoldLeftRight: "┰",
|
|
991
|
+
lineDownBoldLeftBoldRight: "┱",
|
|
992
|
+
lineDownBoldLeftRightBold: "┲",
|
|
993
|
+
lineDownLeftRightBold: "┮",
|
|
994
|
+
lineDownLeftBoldRight: "┭",
|
|
995
|
+
lineDownDoubleLeftDoubleRightDouble: "╦",
|
|
996
|
+
lineDownDoubleLeftRight: "╥",
|
|
997
|
+
lineDownLeftDoubleRightDouble: "╤",
|
|
998
|
+
lineUpLeftRight: "┴",
|
|
999
|
+
lineUpBoldLeftBoldRightBold: "┻",
|
|
1000
|
+
lineUpLeftBoldRightBold: "┷",
|
|
1001
|
+
lineUpBoldLeftRight: "┸",
|
|
1002
|
+
lineUpBoldLeftBoldRight: "┹",
|
|
1003
|
+
lineUpBoldLeftRightBold: "┺",
|
|
1004
|
+
lineUpLeftRightBold: "┶",
|
|
1005
|
+
lineUpLeftBoldRight: "┵",
|
|
1006
|
+
lineUpDoubleLeftDoubleRightDouble: "╩",
|
|
1007
|
+
lineUpDoubleLeftRight: "╨",
|
|
1008
|
+
lineUpLeftDoubleRightDouble: "╧",
|
|
1009
|
+
lineUpDownLeftRight: "┼",
|
|
1010
|
+
lineUpBoldDownBoldLeftBoldRightBold: "╋",
|
|
1011
|
+
lineUpDownBoldLeftBoldRightBold: "╈",
|
|
1012
|
+
lineUpBoldDownLeftBoldRightBold: "╇",
|
|
1013
|
+
lineUpBoldDownBoldLeftRightBold: "╊",
|
|
1014
|
+
lineUpBoldDownBoldLeftBoldRight: "╉",
|
|
1015
|
+
lineUpBoldDownLeftRight: "╀",
|
|
1016
|
+
lineUpDownBoldLeftRight: "╁",
|
|
1017
|
+
lineUpDownLeftBoldRight: "┽",
|
|
1018
|
+
lineUpDownLeftRightBold: "┾",
|
|
1019
|
+
lineUpBoldDownBoldLeftRight: "╂",
|
|
1020
|
+
lineUpDownLeftBoldRightBold: "┿",
|
|
1021
|
+
lineUpBoldDownLeftBoldRight: "╃",
|
|
1022
|
+
lineUpBoldDownLeftRightBold: "╄",
|
|
1023
|
+
lineUpDownBoldLeftBoldRight: "╅",
|
|
1024
|
+
lineUpDownBoldLeftRightBold: "╆",
|
|
1025
|
+
lineUpDoubleDownDoubleLeftDoubleRightDouble: "╬",
|
|
1026
|
+
lineUpDoubleDownDoubleLeftRight: "╫",
|
|
1027
|
+
lineUpDownLeftDoubleRightDouble: "╪",
|
|
1028
|
+
lineCross: "╳",
|
|
1029
|
+
lineBackslash: "╲",
|
|
1030
|
+
lineSlash: "╱"
|
|
1031
|
+
};
|
|
1032
|
+
var specialMainSymbols = {
|
|
1033
|
+
tick: "✔",
|
|
1034
|
+
info: "ℹ",
|
|
1035
|
+
warning: "⚠",
|
|
1036
|
+
cross: "✘",
|
|
1037
|
+
squareSmall: "◻",
|
|
1038
|
+
squareSmallFilled: "◼",
|
|
1039
|
+
circle: "◯",
|
|
1040
|
+
circleFilled: "◉",
|
|
1041
|
+
circleDotted: "◌",
|
|
1042
|
+
circleDouble: "◎",
|
|
1043
|
+
circleCircle: "ⓞ",
|
|
1044
|
+
circleCross: "ⓧ",
|
|
1045
|
+
circlePipe: "Ⓘ",
|
|
1046
|
+
radioOn: "◉",
|
|
1047
|
+
radioOff: "◯",
|
|
1048
|
+
checkboxOn: "☒",
|
|
1049
|
+
checkboxOff: "☐",
|
|
1050
|
+
checkboxCircleOn: "ⓧ",
|
|
1051
|
+
checkboxCircleOff: "Ⓘ",
|
|
1052
|
+
pointer: "❯",
|
|
1053
|
+
triangleUpOutline: "△",
|
|
1054
|
+
triangleLeft: "◀",
|
|
1055
|
+
triangleRight: "▶",
|
|
1056
|
+
lozenge: "◆",
|
|
1057
|
+
lozengeOutline: "◇",
|
|
1058
|
+
hamburger: "☰",
|
|
1059
|
+
smiley: "㋡",
|
|
1060
|
+
mustache: "෴",
|
|
1061
|
+
star: "★",
|
|
1062
|
+
play: "▶",
|
|
1063
|
+
nodejs: "⬢",
|
|
1064
|
+
oneSeventh: "⅐",
|
|
1065
|
+
oneNinth: "⅑",
|
|
1066
|
+
oneTenth: "⅒"
|
|
1067
|
+
};
|
|
1068
|
+
var specialFallbackSymbols = {
|
|
1069
|
+
tick: "√",
|
|
1070
|
+
info: "i",
|
|
1071
|
+
warning: "‼",
|
|
1072
|
+
cross: "×",
|
|
1073
|
+
squareSmall: "□",
|
|
1074
|
+
squareSmallFilled: "■",
|
|
1075
|
+
circle: "( )",
|
|
1076
|
+
circleFilled: "(*)",
|
|
1077
|
+
circleDotted: "( )",
|
|
1078
|
+
circleDouble: "( )",
|
|
1079
|
+
circleCircle: "(○)",
|
|
1080
|
+
circleCross: "(×)",
|
|
1081
|
+
circlePipe: "(│)",
|
|
1082
|
+
radioOn: "(*)",
|
|
1083
|
+
radioOff: "( )",
|
|
1084
|
+
checkboxOn: "[×]",
|
|
1085
|
+
checkboxOff: "[ ]",
|
|
1086
|
+
checkboxCircleOn: "(×)",
|
|
1087
|
+
checkboxCircleOff: "( )",
|
|
1088
|
+
pointer: ">",
|
|
1089
|
+
triangleUpOutline: "∆",
|
|
1090
|
+
triangleLeft: "◄",
|
|
1091
|
+
triangleRight: "►",
|
|
1092
|
+
lozenge: "♦",
|
|
1093
|
+
lozengeOutline: "◊",
|
|
1094
|
+
hamburger: "≡",
|
|
1095
|
+
smiley: "☺",
|
|
1096
|
+
mustache: "┌─┐",
|
|
1097
|
+
star: "✶",
|
|
1098
|
+
play: "►",
|
|
1099
|
+
nodejs: "♦",
|
|
1100
|
+
oneSeventh: "1/7",
|
|
1101
|
+
oneNinth: "1/9",
|
|
1102
|
+
oneTenth: "1/10"
|
|
1103
|
+
};
|
|
1104
|
+
var mainSymbols = {
|
|
1105
|
+
...common,
|
|
1106
|
+
...specialMainSymbols
|
|
1107
|
+
};
|
|
1108
|
+
var fallbackSymbols = {
|
|
1109
|
+
...common,
|
|
1110
|
+
...specialFallbackSymbols
|
|
1111
|
+
};
|
|
1112
|
+
var shouldUseMain = isUnicodeSupported();
|
|
1113
|
+
var figures = shouldUseMain ? mainSymbols : fallbackSymbols;
|
|
1114
|
+
var dist_default = figures;
|
|
1115
|
+
var replacements = Object.entries(specialMainSymbols);
|
|
1116
|
+
|
|
1117
|
+
// node_modules/@inquirer/core/dist/lib/theme.js
|
|
1118
|
+
var defaultTheme = {
|
|
1119
|
+
prefix: {
|
|
1120
|
+
idle: styleText("blue", "?"),
|
|
1121
|
+
done: styleText("green", dist_default.tick)
|
|
1122
|
+
},
|
|
1123
|
+
spinner: {
|
|
1124
|
+
interval: 80,
|
|
1125
|
+
frames: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"].map((frame) => styleText("yellow", frame))
|
|
1126
|
+
},
|
|
1127
|
+
style: {
|
|
1128
|
+
answer: (text) => styleText("cyan", text),
|
|
1129
|
+
message: (text) => styleText("bold", text),
|
|
1130
|
+
error: (text) => styleText("red", `> ${text}`),
|
|
1131
|
+
defaultAnswer: (text) => styleText("dim", `(${text})`),
|
|
1132
|
+
help: (text) => styleText("dim", text),
|
|
1133
|
+
highlight: (text) => styleText("cyan", text),
|
|
1134
|
+
key: (text) => styleText("cyan", styleText("bold", `<${text}>`))
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1137
|
+
|
|
1138
|
+
// node_modules/@inquirer/core/dist/lib/make-theme.js
|
|
1139
|
+
function isPlainObject(value) {
|
|
1140
|
+
if (typeof value !== "object" || value === null)
|
|
1141
|
+
return false;
|
|
1142
|
+
let proto = value;
|
|
1143
|
+
while (Object.getPrototypeOf(proto) !== null) {
|
|
1144
|
+
proto = Object.getPrototypeOf(proto);
|
|
1145
|
+
}
|
|
1146
|
+
return Object.getPrototypeOf(value) === proto;
|
|
1147
|
+
}
|
|
1148
|
+
function deepMerge(...objects) {
|
|
1149
|
+
const output = {};
|
|
1150
|
+
for (const obj of objects) {
|
|
1151
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
1152
|
+
const prevValue = output[key];
|
|
1153
|
+
output[key] = isPlainObject(prevValue) && isPlainObject(value) ? deepMerge(prevValue, value) : value;
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
return output;
|
|
1157
|
+
}
|
|
1158
|
+
function makeTheme(...themes) {
|
|
1159
|
+
const themesToMerge = [
|
|
1160
|
+
defaultTheme,
|
|
1161
|
+
...themes.filter((theme) => theme != null)
|
|
1162
|
+
];
|
|
1163
|
+
return deepMerge(...themesToMerge);
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
// node_modules/@inquirer/core/dist/lib/use-prefix.js
|
|
1167
|
+
function usePrefix({ status = "idle", theme }) {
|
|
1168
|
+
const [showLoader, setShowLoader] = useState(false);
|
|
1169
|
+
const [tick, setTick] = useState(0);
|
|
1170
|
+
const { prefix, spinner } = makeTheme(theme);
|
|
1171
|
+
useEffect(() => {
|
|
1172
|
+
if (status === "loading") {
|
|
1173
|
+
let tickInterval;
|
|
1174
|
+
let inc = -1;
|
|
1175
|
+
const delayTimeout = setTimeout(() => {
|
|
1176
|
+
setShowLoader(true);
|
|
1177
|
+
tickInterval = setInterval(() => {
|
|
1178
|
+
inc = inc + 1;
|
|
1179
|
+
setTick(inc % spinner.frames.length);
|
|
1180
|
+
}, spinner.interval);
|
|
1181
|
+
}, 300);
|
|
1182
|
+
return () => {
|
|
1183
|
+
clearTimeout(delayTimeout);
|
|
1184
|
+
clearInterval(tickInterval);
|
|
1185
|
+
};
|
|
1186
|
+
} else {
|
|
1187
|
+
setShowLoader(false);
|
|
1188
|
+
}
|
|
1189
|
+
}, [status]);
|
|
1190
|
+
if (showLoader) {
|
|
1191
|
+
return spinner.frames[tick];
|
|
1192
|
+
}
|
|
1193
|
+
const iconName = status === "loading" ? "idle" : status;
|
|
1194
|
+
return typeof prefix === "string" ? prefix : prefix[iconName] ?? prefix["idle"];
|
|
1195
|
+
}
|
|
1196
|
+
// node_modules/@inquirer/core/dist/lib/use-memo.js
|
|
1197
|
+
function useMemo(fn, dependencies) {
|
|
1198
|
+
return withPointer((pointer) => {
|
|
1199
|
+
const prev = pointer.get();
|
|
1200
|
+
if (!prev || prev.dependencies.length !== dependencies.length || prev.dependencies.some((dep, i) => dep !== dependencies[i])) {
|
|
1201
|
+
const value = fn();
|
|
1202
|
+
pointer.set({ value, dependencies });
|
|
1203
|
+
return value;
|
|
1204
|
+
}
|
|
1205
|
+
return prev.value;
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
// node_modules/@inquirer/core/dist/lib/use-ref.js
|
|
1209
|
+
function useRef(val) {
|
|
1210
|
+
return useState({ current: val })[0];
|
|
1211
|
+
}
|
|
1212
|
+
// node_modules/@inquirer/core/dist/lib/use-keypress.js
|
|
1213
|
+
function useKeypress(userHandler) {
|
|
1214
|
+
const signal = useRef(userHandler);
|
|
1215
|
+
signal.current = userHandler;
|
|
1216
|
+
useEffect((rl) => {
|
|
1217
|
+
let ignore = false;
|
|
1218
|
+
const handler = withUpdates((_input, event) => {
|
|
1219
|
+
if (ignore)
|
|
1220
|
+
return;
|
|
1221
|
+
signal.current(event, rl);
|
|
1222
|
+
});
|
|
1223
|
+
rl.input.on("keypress", handler);
|
|
1224
|
+
return () => {
|
|
1225
|
+
ignore = true;
|
|
1226
|
+
rl.input.removeListener("keypress", handler);
|
|
1227
|
+
};
|
|
1228
|
+
}, []);
|
|
1229
|
+
}
|
|
1230
|
+
// node_modules/@inquirer/core/dist/lib/utils.js
|
|
1231
|
+
var import_cli_width = __toESM(require_cli_width(), 1);
|
|
1232
|
+
|
|
1233
|
+
// node_modules/fast-string-truncated-width/dist/utils.js
|
|
1234
|
+
var getCodePointsLength = (() => {
|
|
1235
|
+
const SURROGATE_PAIR_RE = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
|
|
1236
|
+
return (input) => {
|
|
1237
|
+
let surrogatePairsNr = 0;
|
|
1238
|
+
SURROGATE_PAIR_RE.lastIndex = 0;
|
|
1239
|
+
while (SURROGATE_PAIR_RE.test(input)) {
|
|
1240
|
+
surrogatePairsNr += 1;
|
|
1241
|
+
}
|
|
1242
|
+
return input.length - surrogatePairsNr;
|
|
1243
|
+
};
|
|
1244
|
+
})();
|
|
1245
|
+
var isFullWidth = (x) => {
|
|
1246
|
+
return x === 12288 || x >= 65281 && x <= 65376 || x >= 65504 && x <= 65510;
|
|
1247
|
+
};
|
|
1248
|
+
var isWideNotCJKTNotEmoji = (x) => {
|
|
1249
|
+
return x === 8987 || x === 9001 || x >= 12272 && x <= 12287 || x >= 12289 && x <= 12350 || x >= 12441 && x <= 12543 || x >= 12549 && x <= 12591 || x >= 12593 && x <= 12686 || x >= 12688 && x <= 12771 || x >= 12783 && x <= 12830 || x >= 12832 && x <= 12871 || x >= 12880 && x <= 19903 || x >= 65040 && x <= 65049 || x >= 65072 && x <= 65106 || x >= 65108 && x <= 65126 || x >= 65128 && x <= 65131 || x >= 127488 && x <= 127490 || x >= 127504 && x <= 127547 || x >= 127552 && x <= 127560 || x >= 131072 && x <= 196605 || x >= 196608 && x <= 262141;
|
|
1250
|
+
};
|
|
1251
|
+
|
|
1252
|
+
// node_modules/fast-string-truncated-width/dist/index.js
|
|
1253
|
+
var ANSI_RE = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]|\u001b\]8;[^;]*;.*?(?:\u0007|\u001b\u005c)/y;
|
|
1254
|
+
var CONTROL_RE = /[\x00-\x08\x0A-\x1F\x7F-\x9F]{1,1000}/y;
|
|
1255
|
+
var CJKT_WIDE_RE = /(?:(?![\uFF61-\uFF9F\uFF00-\uFFEF])[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Tangut}]){1,1000}/yu;
|
|
1256
|
+
var TAB_RE = /\t{1,1000}/y;
|
|
1257
|
+
var EMOJI_RE = /[\u{1F1E6}-\u{1F1FF}]{2}|\u{1F3F4}[\u{E0061}-\u{E007A}]{2}[\u{E0030}-\u{E0039}\u{E0061}-\u{E007A}]{1,3}\u{E007F}|(?:\p{Emoji}\uFE0F\u20E3?|\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation})(?:\u200D(?:\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F\u20E3?))*/yu;
|
|
1258
|
+
var LATIN_RE = /(?:[\x20-\x7E\xA0-\xFF](?!\uFE0F)){1,1000}/y;
|
|
1259
|
+
var MODIFIER_RE = /\p{M}+/gu;
|
|
1260
|
+
var NO_TRUNCATION = { limit: Infinity, ellipsis: "" };
|
|
1261
|
+
var getStringTruncatedWidth = (input, truncationOptions = {}, widthOptions = {}) => {
|
|
1262
|
+
const LIMIT = truncationOptions.limit ?? Infinity;
|
|
1263
|
+
const ELLIPSIS = truncationOptions.ellipsis ?? "";
|
|
1264
|
+
const ELLIPSIS_WIDTH = truncationOptions?.ellipsisWidth ?? (ELLIPSIS ? getStringTruncatedWidth(ELLIPSIS, NO_TRUNCATION, widthOptions).width : 0);
|
|
1265
|
+
const ANSI_WIDTH = 0;
|
|
1266
|
+
const CONTROL_WIDTH = widthOptions.controlWidth ?? 0;
|
|
1267
|
+
const TAB_WIDTH = widthOptions.tabWidth ?? 8;
|
|
1268
|
+
const EMOJI_WIDTH = widthOptions.emojiWidth ?? 2;
|
|
1269
|
+
const FULL_WIDTH_WIDTH = 2;
|
|
1270
|
+
const REGULAR_WIDTH = widthOptions.regularWidth ?? 1;
|
|
1271
|
+
const WIDE_WIDTH = widthOptions.wideWidth ?? FULL_WIDTH_WIDTH;
|
|
1272
|
+
const PARSE_BLOCKS = [
|
|
1273
|
+
[LATIN_RE, REGULAR_WIDTH],
|
|
1274
|
+
[ANSI_RE, ANSI_WIDTH],
|
|
1275
|
+
[CONTROL_RE, CONTROL_WIDTH],
|
|
1276
|
+
[TAB_RE, TAB_WIDTH],
|
|
1277
|
+
[EMOJI_RE, EMOJI_WIDTH],
|
|
1278
|
+
[CJKT_WIDE_RE, WIDE_WIDTH]
|
|
1279
|
+
];
|
|
1280
|
+
let indexPrev = 0;
|
|
1281
|
+
let index = 0;
|
|
1282
|
+
let length = input.length;
|
|
1283
|
+
let lengthExtra = 0;
|
|
1284
|
+
let truncationEnabled = false;
|
|
1285
|
+
let truncationIndex = length;
|
|
1286
|
+
let truncationLimit = Math.max(0, LIMIT - ELLIPSIS_WIDTH);
|
|
1287
|
+
let unmatchedStart = 0;
|
|
1288
|
+
let unmatchedEnd = 0;
|
|
1289
|
+
let width = 0;
|
|
1290
|
+
let widthExtra = 0;
|
|
1291
|
+
outer:
|
|
1292
|
+
while (true) {
|
|
1293
|
+
if (unmatchedEnd > unmatchedStart || index >= length && index > indexPrev) {
|
|
1294
|
+
const unmatched = input.slice(unmatchedStart, unmatchedEnd) || input.slice(indexPrev, index);
|
|
1295
|
+
lengthExtra = 0;
|
|
1296
|
+
for (const char of unmatched.replaceAll(MODIFIER_RE, "")) {
|
|
1297
|
+
const codePoint = char.codePointAt(0) || 0;
|
|
1298
|
+
if (isFullWidth(codePoint)) {
|
|
1299
|
+
widthExtra = FULL_WIDTH_WIDTH;
|
|
1300
|
+
} else if (isWideNotCJKTNotEmoji(codePoint)) {
|
|
1301
|
+
widthExtra = WIDE_WIDTH;
|
|
1302
|
+
} else {
|
|
1303
|
+
widthExtra = REGULAR_WIDTH;
|
|
1304
|
+
}
|
|
1305
|
+
if (width + widthExtra > truncationLimit) {
|
|
1306
|
+
truncationIndex = Math.min(truncationIndex, Math.max(unmatchedStart, indexPrev) + lengthExtra);
|
|
1307
|
+
}
|
|
1308
|
+
if (width + widthExtra > LIMIT) {
|
|
1309
|
+
truncationEnabled = true;
|
|
1310
|
+
break outer;
|
|
1311
|
+
}
|
|
1312
|
+
lengthExtra += char.length;
|
|
1313
|
+
width += widthExtra;
|
|
1314
|
+
}
|
|
1315
|
+
unmatchedStart = unmatchedEnd = 0;
|
|
1316
|
+
}
|
|
1317
|
+
if (index >= length) {
|
|
1318
|
+
break outer;
|
|
1319
|
+
}
|
|
1320
|
+
for (let i = 0, l = PARSE_BLOCKS.length;i < l; i++) {
|
|
1321
|
+
const [BLOCK_RE, BLOCK_WIDTH] = PARSE_BLOCKS[i];
|
|
1322
|
+
BLOCK_RE.lastIndex = index;
|
|
1323
|
+
if (BLOCK_RE.test(input)) {
|
|
1324
|
+
lengthExtra = BLOCK_RE === CJKT_WIDE_RE ? getCodePointsLength(input.slice(index, BLOCK_RE.lastIndex)) : BLOCK_RE === EMOJI_RE ? 1 : BLOCK_RE.lastIndex - index;
|
|
1325
|
+
widthExtra = lengthExtra * BLOCK_WIDTH;
|
|
1326
|
+
if (width + widthExtra > truncationLimit) {
|
|
1327
|
+
truncationIndex = Math.min(truncationIndex, index + Math.floor((truncationLimit - width) / BLOCK_WIDTH));
|
|
1328
|
+
}
|
|
1329
|
+
if (width + widthExtra > LIMIT) {
|
|
1330
|
+
truncationEnabled = true;
|
|
1331
|
+
break outer;
|
|
1332
|
+
}
|
|
1333
|
+
width += widthExtra;
|
|
1334
|
+
unmatchedStart = indexPrev;
|
|
1335
|
+
unmatchedEnd = index;
|
|
1336
|
+
index = indexPrev = BLOCK_RE.lastIndex;
|
|
1337
|
+
continue outer;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
index += 1;
|
|
1341
|
+
}
|
|
1342
|
+
return {
|
|
1343
|
+
width: truncationEnabled ? truncationLimit : width,
|
|
1344
|
+
index: truncationEnabled ? truncationIndex : length,
|
|
1345
|
+
truncated: truncationEnabled,
|
|
1346
|
+
ellipsed: truncationEnabled && LIMIT >= ELLIPSIS_WIDTH
|
|
1347
|
+
};
|
|
1348
|
+
};
|
|
1349
|
+
var dist_default2 = getStringTruncatedWidth;
|
|
1350
|
+
|
|
1351
|
+
// node_modules/fast-string-width/dist/index.js
|
|
1352
|
+
var NO_TRUNCATION2 = {
|
|
1353
|
+
limit: Infinity,
|
|
1354
|
+
ellipsis: "",
|
|
1355
|
+
ellipsisWidth: 0
|
|
1356
|
+
};
|
|
1357
|
+
var fastStringWidth = (input, options = {}) => {
|
|
1358
|
+
return dist_default2(input, NO_TRUNCATION2, options).width;
|
|
1359
|
+
};
|
|
1360
|
+
var dist_default3 = fastStringWidth;
|
|
1361
|
+
|
|
1362
|
+
// node_modules/fast-wrap-ansi/lib/main.js
|
|
1363
|
+
var ESC = "\x1B";
|
|
1364
|
+
var CSI = "";
|
|
1365
|
+
var END_CODE = 39;
|
|
1366
|
+
var ANSI_ESCAPE_BELL = "\x07";
|
|
1367
|
+
var ANSI_CSI = "[";
|
|
1368
|
+
var ANSI_OSC = "]";
|
|
1369
|
+
var ANSI_SGR_TERMINATOR = "m";
|
|
1370
|
+
var ANSI_ESCAPE_LINK = `${ANSI_OSC}8;;`;
|
|
1371
|
+
var GROUP_REGEX = new RegExp(`(?:\\${ANSI_CSI}(?<code>\\d+)m|\\${ANSI_ESCAPE_LINK}(?<uri>.*)${ANSI_ESCAPE_BELL})`, "y");
|
|
1372
|
+
var getClosingCode = (openingCode) => {
|
|
1373
|
+
if (openingCode >= 30 && openingCode <= 37)
|
|
1374
|
+
return 39;
|
|
1375
|
+
if (openingCode >= 90 && openingCode <= 97)
|
|
1376
|
+
return 39;
|
|
1377
|
+
if (openingCode >= 40 && openingCode <= 47)
|
|
1378
|
+
return 49;
|
|
1379
|
+
if (openingCode >= 100 && openingCode <= 107)
|
|
1380
|
+
return 49;
|
|
1381
|
+
if (openingCode === 1 || openingCode === 2)
|
|
1382
|
+
return 22;
|
|
1383
|
+
if (openingCode === 3)
|
|
1384
|
+
return 23;
|
|
1385
|
+
if (openingCode === 4)
|
|
1386
|
+
return 24;
|
|
1387
|
+
if (openingCode === 7)
|
|
1388
|
+
return 27;
|
|
1389
|
+
if (openingCode === 8)
|
|
1390
|
+
return 28;
|
|
1391
|
+
if (openingCode === 9)
|
|
1392
|
+
return 29;
|
|
1393
|
+
if (openingCode === 0)
|
|
1394
|
+
return 0;
|
|
1395
|
+
return;
|
|
1396
|
+
};
|
|
1397
|
+
var wrapAnsiCode = (code) => `${ESC}${ANSI_CSI}${code}${ANSI_SGR_TERMINATOR}`;
|
|
1398
|
+
var wrapAnsiHyperlink = (url) => `${ESC}${ANSI_ESCAPE_LINK}${url}${ANSI_ESCAPE_BELL}`;
|
|
1399
|
+
var wrapWord = (rows, word, columns) => {
|
|
1400
|
+
const characters = word[Symbol.iterator]();
|
|
1401
|
+
let isInsideEscape = false;
|
|
1402
|
+
let isInsideLinkEscape = false;
|
|
1403
|
+
let lastRow = rows.at(-1);
|
|
1404
|
+
let visible = lastRow === undefined ? 0 : dist_default3(lastRow);
|
|
1405
|
+
let currentCharacter = characters.next();
|
|
1406
|
+
let nextCharacter = characters.next();
|
|
1407
|
+
let rawCharacterIndex = 0;
|
|
1408
|
+
while (!currentCharacter.done) {
|
|
1409
|
+
const character = currentCharacter.value;
|
|
1410
|
+
const characterLength = dist_default3(character);
|
|
1411
|
+
if (visible + characterLength <= columns) {
|
|
1412
|
+
rows[rows.length - 1] += character;
|
|
1413
|
+
} else {
|
|
1414
|
+
rows.push(character);
|
|
1415
|
+
visible = 0;
|
|
1416
|
+
}
|
|
1417
|
+
if (character === ESC || character === CSI) {
|
|
1418
|
+
isInsideEscape = true;
|
|
1419
|
+
isInsideLinkEscape = word.startsWith(ANSI_ESCAPE_LINK, rawCharacterIndex + 1);
|
|
1420
|
+
}
|
|
1421
|
+
if (isInsideEscape) {
|
|
1422
|
+
if (isInsideLinkEscape) {
|
|
1423
|
+
if (character === ANSI_ESCAPE_BELL) {
|
|
1424
|
+
isInsideEscape = false;
|
|
1425
|
+
isInsideLinkEscape = false;
|
|
1426
|
+
}
|
|
1427
|
+
} else if (character === ANSI_SGR_TERMINATOR) {
|
|
1428
|
+
isInsideEscape = false;
|
|
1429
|
+
}
|
|
1430
|
+
} else {
|
|
1431
|
+
visible += characterLength;
|
|
1432
|
+
if (visible === columns && !nextCharacter.done) {
|
|
1433
|
+
rows.push("");
|
|
1434
|
+
visible = 0;
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
currentCharacter = nextCharacter;
|
|
1438
|
+
nextCharacter = characters.next();
|
|
1439
|
+
rawCharacterIndex += character.length;
|
|
1440
|
+
}
|
|
1441
|
+
lastRow = rows.at(-1);
|
|
1442
|
+
if (!visible && lastRow !== undefined && lastRow.length && rows.length > 1) {
|
|
1443
|
+
rows[rows.length - 2] += rows.pop();
|
|
1444
|
+
}
|
|
1445
|
+
};
|
|
1446
|
+
var stringVisibleTrimSpacesRight = (string) => {
|
|
1447
|
+
const words = string.split(" ");
|
|
1448
|
+
let last = words.length;
|
|
1449
|
+
while (last) {
|
|
1450
|
+
if (dist_default3(words[last - 1])) {
|
|
1451
|
+
break;
|
|
1452
|
+
}
|
|
1453
|
+
last--;
|
|
1454
|
+
}
|
|
1455
|
+
if (last === words.length) {
|
|
1456
|
+
return string;
|
|
1457
|
+
}
|
|
1458
|
+
return words.slice(0, last).join(" ") + words.slice(last).join("");
|
|
1459
|
+
};
|
|
1460
|
+
var exec = (string, columns, options = {}) => {
|
|
1461
|
+
if (options.trim !== false && string.trim() === "") {
|
|
1462
|
+
return "";
|
|
1463
|
+
}
|
|
1464
|
+
let returnValue = "";
|
|
1465
|
+
let escapeCode;
|
|
1466
|
+
let escapeUrl;
|
|
1467
|
+
const words = string.split(" ");
|
|
1468
|
+
let rows = [""];
|
|
1469
|
+
let rowLength = 0;
|
|
1470
|
+
for (let index = 0;index < words.length; index++) {
|
|
1471
|
+
const word = words[index];
|
|
1472
|
+
if (options.trim !== false) {
|
|
1473
|
+
const row = rows.at(-1) ?? "";
|
|
1474
|
+
const trimmed = row.trimStart();
|
|
1475
|
+
if (row.length !== trimmed.length) {
|
|
1476
|
+
rows[rows.length - 1] = trimmed;
|
|
1477
|
+
rowLength = dist_default3(trimmed);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
if (index !== 0) {
|
|
1481
|
+
if (rowLength >= columns && (options.wordWrap === false || options.trim === false)) {
|
|
1482
|
+
rows.push("");
|
|
1483
|
+
rowLength = 0;
|
|
1484
|
+
}
|
|
1485
|
+
if (rowLength || options.trim === false) {
|
|
1486
|
+
rows[rows.length - 1] += " ";
|
|
1487
|
+
rowLength++;
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
const wordLength = dist_default3(word);
|
|
1491
|
+
if (options.hard && wordLength > columns) {
|
|
1492
|
+
const remainingColumns = columns - rowLength;
|
|
1493
|
+
const breaksStartingThisLine = 1 + Math.floor((wordLength - remainingColumns - 1) / columns);
|
|
1494
|
+
const breaksStartingNextLine = Math.floor((wordLength - 1) / columns);
|
|
1495
|
+
if (breaksStartingNextLine < breaksStartingThisLine) {
|
|
1496
|
+
rows.push("");
|
|
1497
|
+
}
|
|
1498
|
+
wrapWord(rows, word, columns);
|
|
1499
|
+
rowLength = dist_default3(rows.at(-1) ?? "");
|
|
1500
|
+
continue;
|
|
1501
|
+
}
|
|
1502
|
+
if (rowLength + wordLength > columns && rowLength && wordLength) {
|
|
1503
|
+
if (options.wordWrap === false && rowLength < columns) {
|
|
1504
|
+
wrapWord(rows, word, columns);
|
|
1505
|
+
rowLength = dist_default3(rows.at(-1) ?? "");
|
|
1506
|
+
continue;
|
|
1507
|
+
}
|
|
1508
|
+
rows.push("");
|
|
1509
|
+
rowLength = 0;
|
|
1510
|
+
}
|
|
1511
|
+
if (rowLength + wordLength > columns && options.wordWrap === false) {
|
|
1512
|
+
wrapWord(rows, word, columns);
|
|
1513
|
+
rowLength = dist_default3(rows.at(-1) ?? "");
|
|
1514
|
+
continue;
|
|
1515
|
+
}
|
|
1516
|
+
rows[rows.length - 1] += word;
|
|
1517
|
+
rowLength += wordLength;
|
|
1518
|
+
}
|
|
1519
|
+
if (options.trim !== false) {
|
|
1520
|
+
rows = rows.map((row) => stringVisibleTrimSpacesRight(row));
|
|
1521
|
+
}
|
|
1522
|
+
const preString = rows.join(`
|
|
1523
|
+
`);
|
|
1524
|
+
let inSurrogate = false;
|
|
1525
|
+
for (let i = 0;i < preString.length; i++) {
|
|
1526
|
+
const character = preString[i];
|
|
1527
|
+
returnValue += character;
|
|
1528
|
+
if (!inSurrogate) {
|
|
1529
|
+
inSurrogate = character >= "\uD800" && character <= "\uDBFF";
|
|
1530
|
+
if (inSurrogate) {
|
|
1531
|
+
continue;
|
|
1532
|
+
}
|
|
1533
|
+
} else {
|
|
1534
|
+
inSurrogate = false;
|
|
1535
|
+
}
|
|
1536
|
+
if (character === ESC || character === CSI) {
|
|
1537
|
+
GROUP_REGEX.lastIndex = i + 1;
|
|
1538
|
+
const groupsResult = GROUP_REGEX.exec(preString);
|
|
1539
|
+
const groups = groupsResult?.groups;
|
|
1540
|
+
if (groups?.code !== undefined) {
|
|
1541
|
+
const code = Number.parseFloat(groups.code);
|
|
1542
|
+
escapeCode = code === END_CODE ? undefined : code;
|
|
1543
|
+
} else if (groups?.uri !== undefined) {
|
|
1544
|
+
escapeUrl = groups.uri.length === 0 ? undefined : groups.uri;
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
if (preString[i + 1] === `
|
|
1548
|
+
`) {
|
|
1549
|
+
if (escapeUrl) {
|
|
1550
|
+
returnValue += wrapAnsiHyperlink("");
|
|
1551
|
+
}
|
|
1552
|
+
const closingCode = escapeCode ? getClosingCode(escapeCode) : undefined;
|
|
1553
|
+
if (escapeCode && closingCode) {
|
|
1554
|
+
returnValue += wrapAnsiCode(closingCode);
|
|
1555
|
+
}
|
|
1556
|
+
} else if (character === `
|
|
1557
|
+
`) {
|
|
1558
|
+
if (escapeCode && getClosingCode(escapeCode)) {
|
|
1559
|
+
returnValue += wrapAnsiCode(escapeCode);
|
|
1560
|
+
}
|
|
1561
|
+
if (escapeUrl) {
|
|
1562
|
+
returnValue += wrapAnsiHyperlink(escapeUrl);
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
return returnValue;
|
|
1567
|
+
};
|
|
1568
|
+
var CRLF_OR_LF = /\r?\n/;
|
|
1569
|
+
function wrapAnsi(string, columns, options) {
|
|
1570
|
+
return String(string).normalize().split(CRLF_OR_LF).map((line) => exec(line, columns, options)).join(`
|
|
1571
|
+
`);
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
// node_modules/@inquirer/core/dist/lib/utils.js
|
|
1575
|
+
function breakLines(content, width) {
|
|
1576
|
+
return content.split(`
|
|
1577
|
+
`).flatMap((line) => wrapAnsi(line, width, { trim: false, hard: true }).split(`
|
|
1578
|
+
`).map((str) => str.trimEnd())).join(`
|
|
1579
|
+
`);
|
|
1580
|
+
}
|
|
1581
|
+
function readlineWidth() {
|
|
1582
|
+
return import_cli_width.default({ defaultWidth: 80, output: readline().output });
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
// node_modules/@inquirer/core/dist/lib/pagination/use-pagination.js
|
|
1586
|
+
function usePointerPosition({ active, renderedItems, pageSize, loop }) {
|
|
1587
|
+
const state = useRef({
|
|
1588
|
+
lastPointer: active,
|
|
1589
|
+
lastActive: undefined
|
|
1590
|
+
});
|
|
1591
|
+
const { lastPointer, lastActive } = state.current;
|
|
1592
|
+
const middle = Math.floor(pageSize / 2);
|
|
1593
|
+
const renderedLength = renderedItems.reduce((acc, item) => acc + item.length, 0);
|
|
1594
|
+
const defaultPointerPosition = renderedItems.slice(0, active).reduce((acc, item) => acc + item.length, 0);
|
|
1595
|
+
let pointer = defaultPointerPosition;
|
|
1596
|
+
if (renderedLength > pageSize) {
|
|
1597
|
+
if (loop) {
|
|
1598
|
+
pointer = lastPointer;
|
|
1599
|
+
if (lastActive != null && lastActive < active && active - lastActive < pageSize) {
|
|
1600
|
+
pointer = Math.min(middle, Math.abs(active - lastActive) === 1 ? Math.min(lastPointer + (renderedItems[lastActive]?.length ?? 0), Math.max(defaultPointerPosition, lastPointer)) : lastPointer + active - lastActive);
|
|
1601
|
+
}
|
|
1602
|
+
} else {
|
|
1603
|
+
const spaceUnderActive = renderedItems.slice(active).reduce((acc, item) => acc + item.length, 0);
|
|
1604
|
+
pointer = spaceUnderActive < pageSize - middle ? pageSize - spaceUnderActive : Math.min(defaultPointerPosition, middle);
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
state.current.lastPointer = pointer;
|
|
1608
|
+
state.current.lastActive = active;
|
|
1609
|
+
return pointer;
|
|
1610
|
+
}
|
|
1611
|
+
function usePagination({ items, active, renderItem, pageSize, loop = true }) {
|
|
1612
|
+
const width = readlineWidth();
|
|
1613
|
+
const bound = (num) => (num % items.length + items.length) % items.length;
|
|
1614
|
+
const renderedItems = items.map((item, index) => {
|
|
1615
|
+
if (item == null)
|
|
1616
|
+
return [];
|
|
1617
|
+
return breakLines(renderItem({ item, index, isActive: index === active }), width).split(`
|
|
1618
|
+
`);
|
|
1619
|
+
});
|
|
1620
|
+
const renderedLength = renderedItems.reduce((acc, item) => acc + item.length, 0);
|
|
1621
|
+
const renderItemAtIndex = (index) => renderedItems[index] ?? [];
|
|
1622
|
+
const pointer = usePointerPosition({ active, renderedItems, pageSize, loop });
|
|
1623
|
+
const activeItem = renderItemAtIndex(active).slice(0, pageSize);
|
|
1624
|
+
const activeItemPosition = pointer + activeItem.length <= pageSize ? pointer : pageSize - activeItem.length;
|
|
1625
|
+
const pageBuffer = Array.from({ length: pageSize });
|
|
1626
|
+
pageBuffer.splice(activeItemPosition, activeItem.length, ...activeItem);
|
|
1627
|
+
const itemVisited = new Set([active]);
|
|
1628
|
+
let bufferPointer = activeItemPosition + activeItem.length;
|
|
1629
|
+
let itemPointer = bound(active + 1);
|
|
1630
|
+
while (bufferPointer < pageSize && !itemVisited.has(itemPointer) && (loop && renderedLength > pageSize ? itemPointer !== active : itemPointer > active)) {
|
|
1631
|
+
const lines = renderItemAtIndex(itemPointer);
|
|
1632
|
+
const linesToAdd = lines.slice(0, pageSize - bufferPointer);
|
|
1633
|
+
pageBuffer.splice(bufferPointer, linesToAdd.length, ...linesToAdd);
|
|
1634
|
+
itemVisited.add(itemPointer);
|
|
1635
|
+
bufferPointer += linesToAdd.length;
|
|
1636
|
+
itemPointer = bound(itemPointer + 1);
|
|
1637
|
+
}
|
|
1638
|
+
bufferPointer = activeItemPosition - 1;
|
|
1639
|
+
itemPointer = bound(active - 1);
|
|
1640
|
+
while (bufferPointer >= 0 && !itemVisited.has(itemPointer) && (loop && renderedLength > pageSize ? itemPointer !== active : itemPointer < active)) {
|
|
1641
|
+
const lines = renderItemAtIndex(itemPointer);
|
|
1642
|
+
const linesToAdd = lines.slice(Math.max(0, lines.length - bufferPointer - 1));
|
|
1643
|
+
pageBuffer.splice(bufferPointer - linesToAdd.length + 1, linesToAdd.length, ...linesToAdd);
|
|
1644
|
+
itemVisited.add(itemPointer);
|
|
1645
|
+
bufferPointer -= linesToAdd.length;
|
|
1646
|
+
itemPointer = bound(itemPointer - 1);
|
|
1647
|
+
}
|
|
1648
|
+
return pageBuffer.filter((line) => typeof line === "string").join(`
|
|
1649
|
+
`);
|
|
1650
|
+
}
|
|
1651
|
+
// node_modules/@inquirer/core/dist/lib/create-prompt.js
|
|
1652
|
+
var import_mute_stream = __toESM(require_lib(), 1);
|
|
1653
|
+
import * as readline2 from "node:readline";
|
|
1654
|
+
import { AsyncResource as AsyncResource3 } from "node:async_hooks";
|
|
1655
|
+
|
|
1656
|
+
// node_modules/signal-exit/dist/mjs/signals.js
|
|
1657
|
+
var signals = [];
|
|
1658
|
+
signals.push("SIGHUP", "SIGINT", "SIGTERM");
|
|
1659
|
+
if (process.platform !== "win32") {
|
|
1660
|
+
signals.push("SIGALRM", "SIGABRT", "SIGVTALRM", "SIGXCPU", "SIGXFSZ", "SIGUSR2", "SIGTRAP", "SIGSYS", "SIGQUIT", "SIGIOT");
|
|
1661
|
+
}
|
|
1662
|
+
if (process.platform === "linux") {
|
|
1663
|
+
signals.push("SIGIO", "SIGPOLL", "SIGPWR", "SIGSTKFLT");
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
// node_modules/signal-exit/dist/mjs/index.js
|
|
1667
|
+
var processOk = (process3) => !!process3 && typeof process3 === "object" && typeof process3.removeListener === "function" && typeof process3.emit === "function" && typeof process3.reallyExit === "function" && typeof process3.listeners === "function" && typeof process3.kill === "function" && typeof process3.pid === "number" && typeof process3.on === "function";
|
|
1668
|
+
var kExitEmitter = Symbol.for("signal-exit emitter");
|
|
1669
|
+
var global = globalThis;
|
|
1670
|
+
var ObjectDefineProperty = Object.defineProperty.bind(Object);
|
|
1671
|
+
|
|
1672
|
+
class Emitter {
|
|
1673
|
+
emitted = {
|
|
1674
|
+
afterExit: false,
|
|
1675
|
+
exit: false
|
|
1676
|
+
};
|
|
1677
|
+
listeners = {
|
|
1678
|
+
afterExit: [],
|
|
1679
|
+
exit: []
|
|
1680
|
+
};
|
|
1681
|
+
count = 0;
|
|
1682
|
+
id = Math.random();
|
|
1683
|
+
constructor() {
|
|
1684
|
+
if (global[kExitEmitter]) {
|
|
1685
|
+
return global[kExitEmitter];
|
|
1686
|
+
}
|
|
1687
|
+
ObjectDefineProperty(global, kExitEmitter, {
|
|
1688
|
+
value: this,
|
|
1689
|
+
writable: false,
|
|
1690
|
+
enumerable: false,
|
|
1691
|
+
configurable: false
|
|
1692
|
+
});
|
|
1693
|
+
}
|
|
1694
|
+
on(ev, fn) {
|
|
1695
|
+
this.listeners[ev].push(fn);
|
|
1696
|
+
}
|
|
1697
|
+
removeListener(ev, fn) {
|
|
1698
|
+
const list = this.listeners[ev];
|
|
1699
|
+
const i = list.indexOf(fn);
|
|
1700
|
+
if (i === -1) {
|
|
1701
|
+
return;
|
|
1702
|
+
}
|
|
1703
|
+
if (i === 0 && list.length === 1) {
|
|
1704
|
+
list.length = 0;
|
|
1705
|
+
} else {
|
|
1706
|
+
list.splice(i, 1);
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
emit(ev, code, signal) {
|
|
1710
|
+
if (this.emitted[ev]) {
|
|
1711
|
+
return false;
|
|
1712
|
+
}
|
|
1713
|
+
this.emitted[ev] = true;
|
|
1714
|
+
let ret = false;
|
|
1715
|
+
for (const fn of this.listeners[ev]) {
|
|
1716
|
+
ret = fn(code, signal) === true || ret;
|
|
1717
|
+
}
|
|
1718
|
+
if (ev === "exit") {
|
|
1719
|
+
ret = this.emit("afterExit", code, signal) || ret;
|
|
1720
|
+
}
|
|
1721
|
+
return ret;
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
class SignalExitBase {
|
|
1726
|
+
}
|
|
1727
|
+
var signalExitWrap = (handler) => {
|
|
1728
|
+
return {
|
|
1729
|
+
onExit(cb, opts) {
|
|
1730
|
+
return handler.onExit(cb, opts);
|
|
1731
|
+
},
|
|
1732
|
+
load() {
|
|
1733
|
+
return handler.load();
|
|
1734
|
+
},
|
|
1735
|
+
unload() {
|
|
1736
|
+
return handler.unload();
|
|
1737
|
+
}
|
|
1738
|
+
};
|
|
1739
|
+
};
|
|
1740
|
+
|
|
1741
|
+
class SignalExitFallback extends SignalExitBase {
|
|
1742
|
+
onExit() {
|
|
1743
|
+
return () => {};
|
|
1744
|
+
}
|
|
1745
|
+
load() {}
|
|
1746
|
+
unload() {}
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
class SignalExit extends SignalExitBase {
|
|
1750
|
+
#hupSig = process3.platform === "win32" ? "SIGINT" : "SIGHUP";
|
|
1751
|
+
#emitter = new Emitter;
|
|
1752
|
+
#process;
|
|
1753
|
+
#originalProcessEmit;
|
|
1754
|
+
#originalProcessReallyExit;
|
|
1755
|
+
#sigListeners = {};
|
|
1756
|
+
#loaded = false;
|
|
1757
|
+
constructor(process3) {
|
|
1758
|
+
super();
|
|
1759
|
+
this.#process = process3;
|
|
1760
|
+
this.#sigListeners = {};
|
|
1761
|
+
for (const sig of signals) {
|
|
1762
|
+
this.#sigListeners[sig] = () => {
|
|
1763
|
+
const listeners = this.#process.listeners(sig);
|
|
1764
|
+
let { count } = this.#emitter;
|
|
1765
|
+
const p = process3;
|
|
1766
|
+
if (typeof p.__signal_exit_emitter__ === "object" && typeof p.__signal_exit_emitter__.count === "number") {
|
|
1767
|
+
count += p.__signal_exit_emitter__.count;
|
|
1768
|
+
}
|
|
1769
|
+
if (listeners.length === count) {
|
|
1770
|
+
this.unload();
|
|
1771
|
+
const ret = this.#emitter.emit("exit", null, sig);
|
|
1772
|
+
const s = sig === "SIGHUP" ? this.#hupSig : sig;
|
|
1773
|
+
if (!ret)
|
|
1774
|
+
process3.kill(process3.pid, s);
|
|
1775
|
+
}
|
|
1776
|
+
};
|
|
1777
|
+
}
|
|
1778
|
+
this.#originalProcessReallyExit = process3.reallyExit;
|
|
1779
|
+
this.#originalProcessEmit = process3.emit;
|
|
1780
|
+
}
|
|
1781
|
+
onExit(cb, opts) {
|
|
1782
|
+
if (!processOk(this.#process)) {
|
|
1783
|
+
return () => {};
|
|
1784
|
+
}
|
|
1785
|
+
if (this.#loaded === false) {
|
|
1786
|
+
this.load();
|
|
1787
|
+
}
|
|
1788
|
+
const ev = opts?.alwaysLast ? "afterExit" : "exit";
|
|
1789
|
+
this.#emitter.on(ev, cb);
|
|
1790
|
+
return () => {
|
|
1791
|
+
this.#emitter.removeListener(ev, cb);
|
|
1792
|
+
if (this.#emitter.listeners["exit"].length === 0 && this.#emitter.listeners["afterExit"].length === 0) {
|
|
1793
|
+
this.unload();
|
|
1794
|
+
}
|
|
1795
|
+
};
|
|
1796
|
+
}
|
|
1797
|
+
load() {
|
|
1798
|
+
if (this.#loaded) {
|
|
1799
|
+
return;
|
|
1800
|
+
}
|
|
1801
|
+
this.#loaded = true;
|
|
1802
|
+
this.#emitter.count += 1;
|
|
1803
|
+
for (const sig of signals) {
|
|
1804
|
+
try {
|
|
1805
|
+
const fn = this.#sigListeners[sig];
|
|
1806
|
+
if (fn)
|
|
1807
|
+
this.#process.on(sig, fn);
|
|
1808
|
+
} catch (_) {}
|
|
1809
|
+
}
|
|
1810
|
+
this.#process.emit = (ev, ...a) => {
|
|
1811
|
+
return this.#processEmit(ev, ...a);
|
|
1812
|
+
};
|
|
1813
|
+
this.#process.reallyExit = (code) => {
|
|
1814
|
+
return this.#processReallyExit(code);
|
|
1815
|
+
};
|
|
1816
|
+
}
|
|
1817
|
+
unload() {
|
|
1818
|
+
if (!this.#loaded) {
|
|
1819
|
+
return;
|
|
1820
|
+
}
|
|
1821
|
+
this.#loaded = false;
|
|
1822
|
+
signals.forEach((sig) => {
|
|
1823
|
+
const listener = this.#sigListeners[sig];
|
|
1824
|
+
if (!listener) {
|
|
1825
|
+
throw new Error("Listener not defined for signal: " + sig);
|
|
1826
|
+
}
|
|
1827
|
+
try {
|
|
1828
|
+
this.#process.removeListener(sig, listener);
|
|
1829
|
+
} catch (_) {}
|
|
1830
|
+
});
|
|
1831
|
+
this.#process.emit = this.#originalProcessEmit;
|
|
1832
|
+
this.#process.reallyExit = this.#originalProcessReallyExit;
|
|
1833
|
+
this.#emitter.count -= 1;
|
|
1834
|
+
}
|
|
1835
|
+
#processReallyExit(code) {
|
|
1836
|
+
if (!processOk(this.#process)) {
|
|
1837
|
+
return 0;
|
|
1838
|
+
}
|
|
1839
|
+
this.#process.exitCode = code || 0;
|
|
1840
|
+
this.#emitter.emit("exit", this.#process.exitCode, null);
|
|
1841
|
+
return this.#originalProcessReallyExit.call(this.#process, this.#process.exitCode);
|
|
1842
|
+
}
|
|
1843
|
+
#processEmit(ev, ...args) {
|
|
1844
|
+
const og = this.#originalProcessEmit;
|
|
1845
|
+
if (ev === "exit" && processOk(this.#process)) {
|
|
1846
|
+
if (typeof args[0] === "number") {
|
|
1847
|
+
this.#process.exitCode = args[0];
|
|
1848
|
+
}
|
|
1849
|
+
const ret = og.call(this.#process, ev, ...args);
|
|
1850
|
+
this.#emitter.emit("exit", this.#process.exitCode, null);
|
|
1851
|
+
return ret;
|
|
1852
|
+
} else {
|
|
1853
|
+
return og.call(this.#process, ev, ...args);
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
var process3 = globalThis.process;
|
|
1858
|
+
var {
|
|
1859
|
+
onExit,
|
|
1860
|
+
load,
|
|
1861
|
+
unload
|
|
1862
|
+
} = signalExitWrap(processOk(process3) ? new SignalExit(process3) : new SignalExitFallback);
|
|
1863
|
+
|
|
1864
|
+
// node_modules/@inquirer/core/dist/lib/screen-manager.js
|
|
1865
|
+
import { stripVTControlCharacters } from "node:util";
|
|
1866
|
+
|
|
1867
|
+
// node_modules/@inquirer/ansi/dist/index.js
|
|
1868
|
+
var ESC2 = "\x1B[";
|
|
1869
|
+
var cursorLeft = ESC2 + "G";
|
|
1870
|
+
var cursorHide = ESC2 + "?25l";
|
|
1871
|
+
var cursorShow = ESC2 + "?25h";
|
|
1872
|
+
var cursorUp = (rows = 1) => rows > 0 ? `${ESC2}${rows}A` : "";
|
|
1873
|
+
var cursorDown = (rows = 1) => rows > 0 ? `${ESC2}${rows}B` : "";
|
|
1874
|
+
var cursorTo = (x, y) => {
|
|
1875
|
+
if (typeof y === "number" && !Number.isNaN(y)) {
|
|
1876
|
+
return `${ESC2}${y + 1};${x + 1}H`;
|
|
1877
|
+
}
|
|
1878
|
+
return `${ESC2}${x + 1}G`;
|
|
1879
|
+
};
|
|
1880
|
+
var eraseLine = ESC2 + "2K";
|
|
1881
|
+
var eraseLines = (lines) => lines > 0 ? (eraseLine + cursorUp(1)).repeat(lines - 1) + eraseLine + cursorLeft : "";
|
|
1882
|
+
|
|
1883
|
+
// node_modules/@inquirer/core/dist/lib/screen-manager.js
|
|
1884
|
+
var height = (content) => content.split(`
|
|
1885
|
+
`).length;
|
|
1886
|
+
var lastLine = (content) => content.split(`
|
|
1887
|
+
`).pop() ?? "";
|
|
1888
|
+
|
|
1889
|
+
class ScreenManager {
|
|
1890
|
+
height = 0;
|
|
1891
|
+
extraLinesUnderPrompt = 0;
|
|
1892
|
+
cursorPos;
|
|
1893
|
+
rl;
|
|
1894
|
+
constructor(rl) {
|
|
1895
|
+
this.rl = rl;
|
|
1896
|
+
this.cursorPos = rl.getCursorPos();
|
|
1897
|
+
}
|
|
1898
|
+
write(content) {
|
|
1899
|
+
this.rl.output.unmute();
|
|
1900
|
+
this.rl.output.write(content);
|
|
1901
|
+
this.rl.output.mute();
|
|
1902
|
+
}
|
|
1903
|
+
render(content, bottomContent = "") {
|
|
1904
|
+
const promptLine = lastLine(content);
|
|
1905
|
+
const rawPromptLine = stripVTControlCharacters(promptLine);
|
|
1906
|
+
let prompt = rawPromptLine;
|
|
1907
|
+
if (this.rl.line.length > 0) {
|
|
1908
|
+
prompt = prompt.slice(0, -this.rl.line.length);
|
|
1909
|
+
}
|
|
1910
|
+
this.rl.setPrompt(prompt);
|
|
1911
|
+
this.cursorPos = this.rl.getCursorPos();
|
|
1912
|
+
const width = readlineWidth();
|
|
1913
|
+
content = breakLines(content, width);
|
|
1914
|
+
bottomContent = breakLines(bottomContent, width);
|
|
1915
|
+
if (rawPromptLine.length % width === 0) {
|
|
1916
|
+
content += `
|
|
1917
|
+
`;
|
|
1918
|
+
}
|
|
1919
|
+
let output = content + (bottomContent ? `
|
|
1920
|
+
` + bottomContent : "");
|
|
1921
|
+
const promptLineUpDiff = Math.floor(rawPromptLine.length / width) - this.cursorPos.rows;
|
|
1922
|
+
const bottomContentHeight = promptLineUpDiff + (bottomContent ? height(bottomContent) : 0);
|
|
1923
|
+
if (bottomContentHeight > 0)
|
|
1924
|
+
output += cursorUp(bottomContentHeight);
|
|
1925
|
+
output += cursorTo(this.cursorPos.cols);
|
|
1926
|
+
this.write(cursorDown(this.extraLinesUnderPrompt) + eraseLines(this.height) + output);
|
|
1927
|
+
this.extraLinesUnderPrompt = bottomContentHeight;
|
|
1928
|
+
this.height = height(output);
|
|
1929
|
+
}
|
|
1930
|
+
checkCursorPos() {
|
|
1931
|
+
const cursorPos = this.rl.getCursorPos();
|
|
1932
|
+
if (cursorPos.cols !== this.cursorPos.cols) {
|
|
1933
|
+
this.write(cursorTo(cursorPos.cols));
|
|
1934
|
+
this.cursorPos = cursorPos;
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
done({ clearContent }) {
|
|
1938
|
+
this.rl.setPrompt("");
|
|
1939
|
+
let output = cursorDown(this.extraLinesUnderPrompt);
|
|
1940
|
+
output += clearContent ? eraseLines(this.height) : `
|
|
1941
|
+
`;
|
|
1942
|
+
output += cursorShow;
|
|
1943
|
+
this.write(output);
|
|
1944
|
+
this.rl.close();
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
// node_modules/@inquirer/core/dist/lib/promise-polyfill.js
|
|
1949
|
+
class PromisePolyfill extends Promise {
|
|
1950
|
+
static withResolver() {
|
|
1951
|
+
let resolve;
|
|
1952
|
+
let reject;
|
|
1953
|
+
const promise = new Promise((res, rej) => {
|
|
1954
|
+
resolve = res;
|
|
1955
|
+
reject = rej;
|
|
1956
|
+
});
|
|
1957
|
+
return { promise, resolve, reject };
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
// node_modules/@inquirer/core/dist/lib/create-prompt.js
|
|
1962
|
+
function getCallSites() {
|
|
1963
|
+
const _prepareStackTrace = Error.prepareStackTrace;
|
|
1964
|
+
let result = [];
|
|
1965
|
+
try {
|
|
1966
|
+
Error.prepareStackTrace = (_, callSites) => {
|
|
1967
|
+
const callSitesWithoutCurrent = callSites.slice(1);
|
|
1968
|
+
result = callSitesWithoutCurrent;
|
|
1969
|
+
return callSitesWithoutCurrent;
|
|
1970
|
+
};
|
|
1971
|
+
new Error().stack;
|
|
1972
|
+
} catch {
|
|
1973
|
+
return result;
|
|
1974
|
+
}
|
|
1975
|
+
Error.prepareStackTrace = _prepareStackTrace;
|
|
1976
|
+
return result;
|
|
1977
|
+
}
|
|
1978
|
+
function createPrompt(view) {
|
|
1979
|
+
const callSites = getCallSites();
|
|
1980
|
+
const prompt = (config, context = {}) => {
|
|
1981
|
+
const { input = process.stdin, signal } = context;
|
|
1982
|
+
const cleanups = new Set;
|
|
1983
|
+
const output = new import_mute_stream.default;
|
|
1984
|
+
output.pipe(context.output ?? process.stdout);
|
|
1985
|
+
const rl = readline2.createInterface({
|
|
1986
|
+
terminal: true,
|
|
1987
|
+
input,
|
|
1988
|
+
output
|
|
1989
|
+
});
|
|
1990
|
+
const screen = new ScreenManager(rl);
|
|
1991
|
+
const { promise, resolve, reject } = PromisePolyfill.withResolver();
|
|
1992
|
+
const cancel = () => reject(new CancelPromptError);
|
|
1993
|
+
if (signal) {
|
|
1994
|
+
const abort = () => reject(new AbortPromptError({ cause: signal.reason }));
|
|
1995
|
+
if (signal.aborted) {
|
|
1996
|
+
abort();
|
|
1997
|
+
return Object.assign(promise, { cancel });
|
|
1998
|
+
}
|
|
1999
|
+
signal.addEventListener("abort", abort);
|
|
2000
|
+
cleanups.add(() => signal.removeEventListener("abort", abort));
|
|
2001
|
+
}
|
|
2002
|
+
cleanups.add(onExit((code, signal2) => {
|
|
2003
|
+
reject(new ExitPromptError(`User force closed the prompt with ${code} ${signal2}`));
|
|
2004
|
+
}));
|
|
2005
|
+
const sigint = () => reject(new ExitPromptError(`User force closed the prompt with SIGINT`));
|
|
2006
|
+
rl.on("SIGINT", sigint);
|
|
2007
|
+
cleanups.add(() => rl.removeListener("SIGINT", sigint));
|
|
2008
|
+
const checkCursorPos = () => screen.checkCursorPos();
|
|
2009
|
+
rl.input.on("keypress", checkCursorPos);
|
|
2010
|
+
cleanups.add(() => rl.input.removeListener("keypress", checkCursorPos));
|
|
2011
|
+
return withHooks(rl, (cycle) => {
|
|
2012
|
+
const hooksCleanup = AsyncResource3.bind(() => effectScheduler.clearAll());
|
|
2013
|
+
rl.on("close", hooksCleanup);
|
|
2014
|
+
cleanups.add(() => rl.removeListener("close", hooksCleanup));
|
|
2015
|
+
cycle(() => {
|
|
2016
|
+
try {
|
|
2017
|
+
const nextView = view(config, (value) => {
|
|
2018
|
+
setImmediate(() => resolve(value));
|
|
2019
|
+
});
|
|
2020
|
+
if (nextView === undefined) {
|
|
2021
|
+
const callerFilename = callSites[1]?.getFileName();
|
|
2022
|
+
throw new Error(`Prompt functions must return a string.
|
|
2023
|
+
at ${callerFilename}`);
|
|
2024
|
+
}
|
|
2025
|
+
const [content, bottomContent] = typeof nextView === "string" ? [nextView] : nextView;
|
|
2026
|
+
screen.render(content, bottomContent);
|
|
2027
|
+
effectScheduler.run();
|
|
2028
|
+
} catch (error) {
|
|
2029
|
+
reject(error);
|
|
2030
|
+
}
|
|
2031
|
+
});
|
|
2032
|
+
return Object.assign(promise.then((answer) => {
|
|
2033
|
+
effectScheduler.clearAll();
|
|
2034
|
+
return answer;
|
|
2035
|
+
}, (error) => {
|
|
2036
|
+
effectScheduler.clearAll();
|
|
2037
|
+
throw error;
|
|
2038
|
+
}).finally(() => {
|
|
2039
|
+
cleanups.forEach((cleanup) => cleanup());
|
|
2040
|
+
screen.done({ clearContent: Boolean(context.clearPromptOnDone) });
|
|
2041
|
+
output.end();
|
|
2042
|
+
}).then(() => promise), { cancel });
|
|
2043
|
+
});
|
|
2044
|
+
};
|
|
2045
|
+
return prompt;
|
|
2046
|
+
}
|
|
2047
|
+
// node_modules/@inquirer/core/dist/lib/Separator.js
|
|
2048
|
+
import { styleText as styleText2 } from "node:util";
|
|
2049
|
+
class Separator {
|
|
2050
|
+
separator = styleText2("dim", Array.from({ length: 15 }).join(dist_default.line));
|
|
2051
|
+
type = "separator";
|
|
2052
|
+
constructor(separator) {
|
|
2053
|
+
if (separator) {
|
|
2054
|
+
this.separator = separator;
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
static isSeparator(choice) {
|
|
2058
|
+
return Boolean(choice && typeof choice === "object" && "type" in choice && choice.type === "separator");
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
// node_modules/@inquirer/confirm/dist/index.js
|
|
2062
|
+
function getBooleanValue(value, defaultValue) {
|
|
2063
|
+
let answer = defaultValue !== false;
|
|
2064
|
+
if (/^(y|yes)/i.test(value))
|
|
2065
|
+
answer = true;
|
|
2066
|
+
else if (/^(n|no)/i.test(value))
|
|
2067
|
+
answer = false;
|
|
2068
|
+
return answer;
|
|
2069
|
+
}
|
|
2070
|
+
function boolToString(value) {
|
|
2071
|
+
return value ? "Yes" : "No";
|
|
2072
|
+
}
|
|
2073
|
+
var dist_default4 = createPrompt((config, done) => {
|
|
2074
|
+
const { transformer = boolToString } = config;
|
|
2075
|
+
const [status, setStatus] = useState("idle");
|
|
2076
|
+
const [value, setValue] = useState("");
|
|
2077
|
+
const theme = makeTheme(config.theme);
|
|
2078
|
+
const prefix = usePrefix({ status, theme });
|
|
2079
|
+
useKeypress((key, rl) => {
|
|
2080
|
+
if (status !== "idle")
|
|
2081
|
+
return;
|
|
2082
|
+
if (isEnterKey(key)) {
|
|
2083
|
+
const answer = getBooleanValue(value, config.default);
|
|
2084
|
+
setValue(transformer(answer));
|
|
2085
|
+
setStatus("done");
|
|
2086
|
+
done(answer);
|
|
2087
|
+
} else if (isTabKey(key)) {
|
|
2088
|
+
const answer = boolToString(!getBooleanValue(value, config.default));
|
|
2089
|
+
rl.clearLine(0);
|
|
2090
|
+
rl.write(answer);
|
|
2091
|
+
setValue(answer);
|
|
2092
|
+
} else {
|
|
2093
|
+
setValue(rl.line);
|
|
2094
|
+
}
|
|
2095
|
+
});
|
|
2096
|
+
let formattedValue = value;
|
|
2097
|
+
let defaultValue = "";
|
|
2098
|
+
if (status === "done") {
|
|
2099
|
+
formattedValue = theme.style.answer(value);
|
|
2100
|
+
} else {
|
|
2101
|
+
defaultValue = ` ${theme.style.defaultAnswer(config.default === false ? "y/N" : "Y/n")}`;
|
|
2102
|
+
}
|
|
2103
|
+
const message = theme.style.message(config.message, status);
|
|
2104
|
+
return `${prefix} ${message}${defaultValue} ${formattedValue}`;
|
|
2105
|
+
});
|
|
2106
|
+
// node_modules/@inquirer/select/dist/index.js
|
|
2107
|
+
import { styleText as styleText3 } from "node:util";
|
|
2108
|
+
var selectTheme = {
|
|
2109
|
+
icon: { cursor: dist_default.pointer },
|
|
2110
|
+
style: {
|
|
2111
|
+
disabled: (text) => styleText3("dim", `- ${text}`),
|
|
2112
|
+
description: (text) => styleText3("cyan", text),
|
|
2113
|
+
keysHelpTip: (keys) => keys.map(([key, action]) => `${styleText3("bold", key)} ${styleText3("dim", action)}`).join(styleText3("dim", " • "))
|
|
2114
|
+
},
|
|
2115
|
+
indexMode: "hidden",
|
|
2116
|
+
keybindings: []
|
|
2117
|
+
};
|
|
2118
|
+
function isSelectable(item) {
|
|
2119
|
+
return !Separator.isSeparator(item) && !item.disabled;
|
|
2120
|
+
}
|
|
2121
|
+
function normalizeChoices(choices) {
|
|
2122
|
+
return choices.map((choice) => {
|
|
2123
|
+
if (Separator.isSeparator(choice))
|
|
2124
|
+
return choice;
|
|
2125
|
+
if (typeof choice !== "object" || choice === null || !("value" in choice)) {
|
|
2126
|
+
const name2 = String(choice);
|
|
2127
|
+
return {
|
|
2128
|
+
value: choice,
|
|
2129
|
+
name: name2,
|
|
2130
|
+
short: name2,
|
|
2131
|
+
disabled: false
|
|
2132
|
+
};
|
|
2133
|
+
}
|
|
2134
|
+
const name = choice.name ?? String(choice.value);
|
|
2135
|
+
const normalizedChoice = {
|
|
2136
|
+
value: choice.value,
|
|
2137
|
+
name,
|
|
2138
|
+
short: choice.short ?? name,
|
|
2139
|
+
disabled: choice.disabled ?? false
|
|
2140
|
+
};
|
|
2141
|
+
if (choice.description) {
|
|
2142
|
+
normalizedChoice.description = choice.description;
|
|
2143
|
+
}
|
|
2144
|
+
return normalizedChoice;
|
|
2145
|
+
});
|
|
2146
|
+
}
|
|
2147
|
+
var dist_default5 = createPrompt((config, done) => {
|
|
2148
|
+
const { loop = true, pageSize = 7 } = config;
|
|
2149
|
+
const theme = makeTheme(selectTheme, config.theme);
|
|
2150
|
+
const { keybindings } = theme;
|
|
2151
|
+
const [status, setStatus] = useState("idle");
|
|
2152
|
+
const prefix = usePrefix({ status, theme });
|
|
2153
|
+
const searchTimeoutRef = useRef();
|
|
2154
|
+
const searchEnabled = !keybindings.includes("vim");
|
|
2155
|
+
const items = useMemo(() => normalizeChoices(config.choices), [config.choices]);
|
|
2156
|
+
const bounds = useMemo(() => {
|
|
2157
|
+
const first = items.findIndex(isSelectable);
|
|
2158
|
+
const last = items.findLastIndex(isSelectable);
|
|
2159
|
+
if (first === -1) {
|
|
2160
|
+
throw new ValidationError("[select prompt] No selectable choices. All choices are disabled.");
|
|
2161
|
+
}
|
|
2162
|
+
return { first, last };
|
|
2163
|
+
}, [items]);
|
|
2164
|
+
const defaultItemIndex = useMemo(() => {
|
|
2165
|
+
if (!("default" in config))
|
|
2166
|
+
return -1;
|
|
2167
|
+
return items.findIndex((item) => isSelectable(item) && item.value === config.default);
|
|
2168
|
+
}, [config.default, items]);
|
|
2169
|
+
const [active, setActive] = useState(defaultItemIndex === -1 ? bounds.first : defaultItemIndex);
|
|
2170
|
+
const selectedChoice = items[active];
|
|
2171
|
+
useKeypress((key, rl) => {
|
|
2172
|
+
clearTimeout(searchTimeoutRef.current);
|
|
2173
|
+
if (isEnterKey(key)) {
|
|
2174
|
+
setStatus("done");
|
|
2175
|
+
done(selectedChoice.value);
|
|
2176
|
+
} else if (isUpKey(key, keybindings) || isDownKey(key, keybindings)) {
|
|
2177
|
+
rl.clearLine(0);
|
|
2178
|
+
if (loop || isUpKey(key, keybindings) && active !== bounds.first || isDownKey(key, keybindings) && active !== bounds.last) {
|
|
2179
|
+
const offset = isUpKey(key, keybindings) ? -1 : 1;
|
|
2180
|
+
let next = active;
|
|
2181
|
+
do {
|
|
2182
|
+
next = (next + offset + items.length) % items.length;
|
|
2183
|
+
} while (!isSelectable(items[next]));
|
|
2184
|
+
setActive(next);
|
|
2185
|
+
}
|
|
2186
|
+
} else if (isNumberKey(key) && !Number.isNaN(Number(rl.line))) {
|
|
2187
|
+
const selectedIndex = Number(rl.line) - 1;
|
|
2188
|
+
let selectableIndex = -1;
|
|
2189
|
+
const position = items.findIndex((item2) => {
|
|
2190
|
+
if (Separator.isSeparator(item2))
|
|
2191
|
+
return false;
|
|
2192
|
+
selectableIndex++;
|
|
2193
|
+
return selectableIndex === selectedIndex;
|
|
2194
|
+
});
|
|
2195
|
+
const item = items[position];
|
|
2196
|
+
if (item != null && isSelectable(item)) {
|
|
2197
|
+
setActive(position);
|
|
2198
|
+
}
|
|
2199
|
+
searchTimeoutRef.current = setTimeout(() => {
|
|
2200
|
+
rl.clearLine(0);
|
|
2201
|
+
}, 700);
|
|
2202
|
+
} else if (isBackspaceKey(key)) {
|
|
2203
|
+
rl.clearLine(0);
|
|
2204
|
+
} else if (searchEnabled) {
|
|
2205
|
+
const searchTerm = rl.line.toLowerCase();
|
|
2206
|
+
const matchIndex = items.findIndex((item) => {
|
|
2207
|
+
if (Separator.isSeparator(item) || !isSelectable(item))
|
|
2208
|
+
return false;
|
|
2209
|
+
return item.name.toLowerCase().startsWith(searchTerm);
|
|
2210
|
+
});
|
|
2211
|
+
if (matchIndex !== -1) {
|
|
2212
|
+
setActive(matchIndex);
|
|
2213
|
+
}
|
|
2214
|
+
searchTimeoutRef.current = setTimeout(() => {
|
|
2215
|
+
rl.clearLine(0);
|
|
2216
|
+
}, 700);
|
|
2217
|
+
}
|
|
2218
|
+
});
|
|
2219
|
+
useEffect(() => () => {
|
|
2220
|
+
clearTimeout(searchTimeoutRef.current);
|
|
2221
|
+
}, []);
|
|
2222
|
+
const message = theme.style.message(config.message, status);
|
|
2223
|
+
const helpLine = theme.style.keysHelpTip([
|
|
2224
|
+
["↑↓", "navigate"],
|
|
2225
|
+
["⏎", "select"]
|
|
2226
|
+
]);
|
|
2227
|
+
let separatorCount = 0;
|
|
2228
|
+
const page = usePagination({
|
|
2229
|
+
items,
|
|
2230
|
+
active,
|
|
2231
|
+
renderItem({ item, isActive, index }) {
|
|
2232
|
+
if (Separator.isSeparator(item)) {
|
|
2233
|
+
separatorCount++;
|
|
2234
|
+
return ` ${item.separator}`;
|
|
2235
|
+
}
|
|
2236
|
+
const indexLabel = theme.indexMode === "number" ? `${index + 1 - separatorCount}. ` : "";
|
|
2237
|
+
if (item.disabled) {
|
|
2238
|
+
const disabledLabel = typeof item.disabled === "string" ? item.disabled : "(disabled)";
|
|
2239
|
+
return theme.style.disabled(`${indexLabel}${item.name} ${disabledLabel}`);
|
|
2240
|
+
}
|
|
2241
|
+
const color = isActive ? theme.style.highlight : (x) => x;
|
|
2242
|
+
const cursor = isActive ? theme.icon.cursor : ` `;
|
|
2243
|
+
return color(`${cursor} ${indexLabel}${item.name}`);
|
|
2244
|
+
},
|
|
2245
|
+
pageSize,
|
|
2246
|
+
loop
|
|
2247
|
+
});
|
|
2248
|
+
if (status === "done") {
|
|
2249
|
+
return [prefix, message, theme.style.answer(selectedChoice.short)].filter(Boolean).join(" ");
|
|
2250
|
+
}
|
|
2251
|
+
const { description } = selectedChoice;
|
|
2252
|
+
const lines = [
|
|
2253
|
+
[prefix, message].filter(Boolean).join(" "),
|
|
2254
|
+
page,
|
|
2255
|
+
" ",
|
|
2256
|
+
description ? theme.style.description(description) : "",
|
|
2257
|
+
helpLine
|
|
2258
|
+
].filter(Boolean).join(`
|
|
2259
|
+
`).trimEnd();
|
|
2260
|
+
return `${lines}${cursorHide}`;
|
|
2261
|
+
});
|
|
2262
|
+
// src/interactive.ts
|
|
2263
|
+
class PromptCancelledError extends Error {
|
|
2264
|
+
constructor() {
|
|
2265
|
+
super("Prompt cancelled");
|
|
2266
|
+
this.name = "PromptCancelledError";
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
function isPromptCancellation(err) {
|
|
2270
|
+
return err instanceof Error && (err.name === "ExitPromptError" || err.name === "CancelPromptError");
|
|
2271
|
+
}
|
|
2272
|
+
function listenForEsc(promise) {
|
|
2273
|
+
if (typeof promise.cancel !== "function")
|
|
2274
|
+
return () => {};
|
|
2275
|
+
readline3.emitKeypressEvents(process.stdin);
|
|
2276
|
+
const wasRaw = process.stdin.isRaw;
|
|
2277
|
+
if (process.stdin.isTTY && !wasRaw) {
|
|
2278
|
+
process.stdin.setRawMode(true);
|
|
2279
|
+
}
|
|
2280
|
+
const onKeypress = (_str, key) => {
|
|
2281
|
+
if (key?.name === "escape") {
|
|
2282
|
+
promise.cancel();
|
|
2283
|
+
}
|
|
2284
|
+
};
|
|
2285
|
+
process.stdin.on("keypress", onKeypress);
|
|
2286
|
+
return () => {
|
|
2287
|
+
process.stdin.removeListener("keypress", onKeypress);
|
|
2288
|
+
if (process.stdin.isTTY && !wasRaw) {
|
|
2289
|
+
process.stdin.setRawMode(false);
|
|
2290
|
+
}
|
|
2291
|
+
};
|
|
2292
|
+
}
|
|
2293
|
+
async function wrapPromptCancellation(fn) {
|
|
2294
|
+
const promise = fn();
|
|
2295
|
+
const cleanup = listenForEsc(promise);
|
|
2296
|
+
try {
|
|
2297
|
+
return await promise;
|
|
2298
|
+
} catch (err) {
|
|
2299
|
+
if (isPromptCancellation(err)) {
|
|
2300
|
+
throw new PromptCancelledError;
|
|
2301
|
+
}
|
|
2302
|
+
throw err;
|
|
2303
|
+
} finally {
|
|
2304
|
+
cleanup();
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
function formatAccount(num, email, alias, isActive) {
|
|
2308
|
+
let label = `${num}: ${email}`;
|
|
2309
|
+
if (alias)
|
|
2310
|
+
label += ` [${alias}]`;
|
|
2311
|
+
if (isActive)
|
|
2312
|
+
label += " (active)";
|
|
2313
|
+
return label;
|
|
2314
|
+
}
|
|
2315
|
+
async function pickAccount(seq, message = "Switch to account:", promptSelect = dist_default5, extraChoices = []) {
|
|
2316
|
+
const choices = [
|
|
2317
|
+
...seq.sequence.map((num, index) => {
|
|
2318
|
+
const numStr = String(num);
|
|
2319
|
+
const account = seq.accounts[numStr];
|
|
2320
|
+
if (!account) {
|
|
2321
|
+
throw new Error(`Corrupt sequence data: missing account entry for id ${numStr}`);
|
|
2322
|
+
}
|
|
2323
|
+
const isActive = num === seq.activeAccountNumber;
|
|
2324
|
+
return {
|
|
2325
|
+
name: formatAccount(String(index + 1), account.email, account.alias, isActive),
|
|
2326
|
+
value: numStr
|
|
2327
|
+
};
|
|
2328
|
+
}),
|
|
2329
|
+
...extraChoices
|
|
2330
|
+
];
|
|
2331
|
+
const bold = (s) => `\x1B[1m${s}\x1B[22m`;
|
|
2332
|
+
const dim = (s) => `\x1B[2m${s}\x1B[22m`;
|
|
2333
|
+
const theme = {
|
|
2334
|
+
style: {
|
|
2335
|
+
keysHelpTip: (keys) => [...keys, ["⎋", "cancel"]].map(([k, a]) => `${bold(k)} ${dim(a)}`).join(dim(" • "))
|
|
2336
|
+
}
|
|
2337
|
+
};
|
|
2338
|
+
return wrapPromptCancellation(() => promptSelect({ message, choices, theme }));
|
|
2339
|
+
}
|
|
2340
|
+
async function pickChoice(message, choices, promptSelect = dist_default5) {
|
|
2341
|
+
const bold = (s) => `\x1B[1m${s}\x1B[22m`;
|
|
2342
|
+
const dim = (s) => `\x1B[2m${s}\x1B[22m`;
|
|
2343
|
+
const theme = {
|
|
2344
|
+
style: {
|
|
2345
|
+
keysHelpTip: (keys) => [...keys, ["⎋", "cancel"]].map(([k, a]) => `${bold(k)} ${dim(a)}`).join(dim(" • "))
|
|
2346
|
+
}
|
|
2347
|
+
};
|
|
2348
|
+
return wrapPromptCancellation(() => promptSelect({ message, choices, theme }));
|
|
2349
|
+
}
|
|
2350
|
+
async function pickProvider(defaultProvider, promptSelect = dist_default5) {
|
|
2351
|
+
const choices = [
|
|
2352
|
+
{
|
|
2353
|
+
name: defaultProvider === "claude" ? "Claude Code (last used)" : "Claude Code",
|
|
2354
|
+
value: "claude"
|
|
2355
|
+
},
|
|
2356
|
+
{
|
|
2357
|
+
name: defaultProvider === "codex" ? "Codex (last used)" : "Codex",
|
|
2358
|
+
value: "codex"
|
|
2359
|
+
}
|
|
2360
|
+
];
|
|
2361
|
+
const selected = await pickChoice("Select provider", choices, promptSelect);
|
|
2362
|
+
return selected;
|
|
2363
|
+
}
|
|
2364
|
+
async function pickAccountForRemoval(seq, promptSelect = dist_default5) {
|
|
2365
|
+
return pickAccount(seq, "Remove which account?", promptSelect);
|
|
2366
|
+
}
|
|
2367
|
+
async function confirmAction(message, promptConfirm = dist_default4) {
|
|
2368
|
+
return wrapPromptCancellation(() => promptConfirm({ message, default: false }));
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
// src/providers/types.ts
|
|
2372
|
+
var SUPPORTED_PROVIDERS = ["claude", "codex"];
|
|
2373
|
+
function isProviderName(value) {
|
|
2374
|
+
return SUPPORTED_PROVIDERS.includes(value);
|
|
2375
|
+
}
|
|
2376
|
+
function parseProviderArgs(args) {
|
|
2377
|
+
if (args[0] === "--provider") {
|
|
2378
|
+
throw new Error("Use positional provider syntax: caflip <claude|codex> <command>");
|
|
2379
|
+
}
|
|
2380
|
+
if (args[0] && isProviderName(args[0])) {
|
|
2381
|
+
return { provider: args[0], commandArgs: args.slice(1), isProviderQualified: true };
|
|
2382
|
+
}
|
|
2383
|
+
return { provider: null, commandArgs: args, isProviderQualified: false };
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
// src/providers/claude.ts
|
|
2387
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
|
|
2388
|
+
|
|
2389
|
+
// src/credentials.ts
|
|
2390
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6, mkdirSync as mkdirSync3, chmodSync as chmodSync3, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
2391
|
+
import { join as join5 } from "path";
|
|
2392
|
+
import { homedir as homedir3 } from "os";
|
|
2393
|
+
import { spawn, spawnSync } from "child_process";
|
|
2394
|
+
|
|
2395
|
+
// src/validation.ts
|
|
2396
|
+
var EMAIL_REGEX2 = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
|
2397
|
+
var ACCOUNT_NUMBER_REGEX = /^\d+$/;
|
|
2398
|
+
function validateEmail2(email) {
|
|
2399
|
+
return EMAIL_REGEX2.test(email);
|
|
2400
|
+
}
|
|
2401
|
+
function sanitizeEmailForFilename2(email) {
|
|
2402
|
+
if (!validateEmail2(email))
|
|
2403
|
+
return false;
|
|
2404
|
+
if (email.includes("..") || email.includes("/") || email.includes("\x00")) {
|
|
2405
|
+
return false;
|
|
2406
|
+
}
|
|
2407
|
+
return true;
|
|
2408
|
+
}
|
|
2409
|
+
function validateAccountNumber(accountNum) {
|
|
2410
|
+
return ACCOUNT_NUMBER_REGEX.test(accountNum);
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2413
|
+
// src/credentials.ts
|
|
2414
|
+
async function runCommand(cmd) {
|
|
2415
|
+
return await runCommandWithInput(cmd);
|
|
2416
|
+
}
|
|
2417
|
+
async function runCommandWithInput(cmd, input) {
|
|
2418
|
+
return await new Promise((resolve, reject) => {
|
|
2419
|
+
const proc = spawn(cmd[0], cmd.slice(1), {
|
|
2420
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2421
|
+
});
|
|
2422
|
+
let stdout = "";
|
|
2423
|
+
let stderr = "";
|
|
2424
|
+
proc.stdout.on("data", (chunk) => {
|
|
2425
|
+
stdout += String(chunk);
|
|
2426
|
+
});
|
|
2427
|
+
proc.stderr.on("data", (chunk) => {
|
|
2428
|
+
stderr += String(chunk);
|
|
2429
|
+
});
|
|
2430
|
+
proc.on("error", (err) => {
|
|
2431
|
+
reject(err);
|
|
2432
|
+
});
|
|
2433
|
+
proc.on("close", (code) => {
|
|
2434
|
+
resolve({
|
|
2435
|
+
stdout: stdout.trim(),
|
|
2436
|
+
stderr: stderr.trim(),
|
|
2437
|
+
exitCode: code ?? 1
|
|
2438
|
+
});
|
|
2439
|
+
});
|
|
2440
|
+
if (input && proc.stdin) {
|
|
2441
|
+
proc.stdin.write(input);
|
|
2442
|
+
}
|
|
2443
|
+
proc.stdin?.end();
|
|
2444
|
+
});
|
|
2445
|
+
}
|
|
2446
|
+
function isSecurityItemMissing(stderr) {
|
|
2447
|
+
const msg = stderr.toLowerCase();
|
|
2448
|
+
return msg.includes("could not be found") || msg.includes("item could not be found") || msg.includes("item not found");
|
|
2449
|
+
}
|
|
2450
|
+
function ensureAccountNumberSafe(accountNum) {
|
|
2451
|
+
if (!validateAccountNumber(accountNum)) {
|
|
2452
|
+
throw new Error(`Unsafe account number for filename: ${accountNum}`);
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
function hasSecretTool() {
|
|
2456
|
+
const result = spawnSync("sh", ["-c", "command -v secret-tool >/dev/null 2>&1"], {
|
|
2457
|
+
stdio: "ignore"
|
|
2458
|
+
});
|
|
2459
|
+
return result.status === 0;
|
|
2460
|
+
}
|
|
2461
|
+
function activeSecretToolAttrs() {
|
|
2462
|
+
return ["service", "claude-code", "account", "active"];
|
|
2463
|
+
}
|
|
2464
|
+
function backupSecretToolAttrs(accountNum, email) {
|
|
2465
|
+
return ["service", "ccflip", "account", accountNum, "email", email];
|
|
2466
|
+
}
|
|
2467
|
+
async function secretToolLookup(attrs) {
|
|
2468
|
+
const result = await runCommand(["secret-tool", "lookup", ...attrs]);
|
|
2469
|
+
if (result.exitCode === 0) {
|
|
2470
|
+
return result.stdout;
|
|
2471
|
+
}
|
|
2472
|
+
return "";
|
|
2473
|
+
}
|
|
2474
|
+
async function secretToolStore(attrs, secret) {
|
|
2475
|
+
const payload = secret.endsWith(`
|
|
2476
|
+
`) ? secret : `${secret}
|
|
2477
|
+
`;
|
|
2478
|
+
const result = await runCommandWithInput(["secret-tool", "store", "--label", "ccflip credentials", ...attrs], payload);
|
|
2479
|
+
if (result.exitCode !== 0) {
|
|
2480
|
+
throw new Error(`Failed to store credentials in secret-tool: ${result.stderr || `exit code ${result.exitCode}`}`);
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
async function secretToolClear(attrs) {
|
|
2484
|
+
const result = await runCommand(["secret-tool", "clear", ...attrs]);
|
|
2485
|
+
if (result.exitCode !== 0 && result.stderr.trim()) {
|
|
2486
|
+
throw new Error(`Failed to clear credentials from secret-tool: ${result.stderr || `exit code ${result.exitCode}`}`);
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2489
|
+
async function readCredentials() {
|
|
2490
|
+
const platform = detectPlatform();
|
|
2491
|
+
switch (platform) {
|
|
2492
|
+
case "macos": {
|
|
2493
|
+
const result = await runCommand([
|
|
2494
|
+
"security",
|
|
2495
|
+
"find-generic-password",
|
|
2496
|
+
"-s",
|
|
2497
|
+
"Claude Code-credentials",
|
|
2498
|
+
"-w"
|
|
2499
|
+
]);
|
|
2500
|
+
if (result.exitCode === 0) {
|
|
2501
|
+
return result.stdout;
|
|
2502
|
+
}
|
|
2503
|
+
if (isSecurityItemMissing(result.stderr)) {
|
|
2504
|
+
return "";
|
|
2505
|
+
}
|
|
2506
|
+
throw new Error(`Failed to read active credentials from keychain: ${result.stderr || `exit code ${result.exitCode}`}`);
|
|
2507
|
+
}
|
|
2508
|
+
case "linux":
|
|
2509
|
+
case "wsl": {
|
|
2510
|
+
if (hasSecretTool()) {
|
|
2511
|
+
const keyringValue = await secretToolLookup(activeSecretToolAttrs());
|
|
2512
|
+
if (keyringValue) {
|
|
2513
|
+
return keyringValue;
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
const credPath = join5(homedir3(), ".claude", ".credentials.json");
|
|
2517
|
+
if (existsSync6(credPath)) {
|
|
2518
|
+
return readFileSync6(credPath, "utf-8");
|
|
2519
|
+
}
|
|
2520
|
+
return "";
|
|
2521
|
+
}
|
|
2522
|
+
default:
|
|
2523
|
+
return "";
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
async function writeCredentials(credentials) {
|
|
2527
|
+
const platform = detectPlatform();
|
|
2528
|
+
switch (platform) {
|
|
2529
|
+
case "macos": {
|
|
2530
|
+
const result = await runCommand([
|
|
2531
|
+
"security",
|
|
2532
|
+
"add-generic-password",
|
|
2533
|
+
"-U",
|
|
2534
|
+
"-s",
|
|
2535
|
+
"Claude Code-credentials",
|
|
2536
|
+
"-a",
|
|
2537
|
+
process.env.USER ?? "unknown",
|
|
2538
|
+
"-w",
|
|
2539
|
+
credentials
|
|
2540
|
+
]);
|
|
2541
|
+
if (result.exitCode !== 0) {
|
|
2542
|
+
throw new Error(`Failed to write active credentials to keychain: ${result.stderr || `exit code ${result.exitCode}`}`);
|
|
2543
|
+
}
|
|
2544
|
+
break;
|
|
2545
|
+
}
|
|
2546
|
+
case "linux":
|
|
2547
|
+
case "wsl": {
|
|
2548
|
+
if (hasSecretTool()) {
|
|
2549
|
+
await secretToolStore(activeSecretToolAttrs(), credentials);
|
|
2550
|
+
return;
|
|
2551
|
+
}
|
|
2552
|
+
const claudeDir = join5(homedir3(), ".claude");
|
|
2553
|
+
mkdirSync3(claudeDir, { recursive: true, mode: 448 });
|
|
2554
|
+
chmodSync3(claudeDir, 448);
|
|
2555
|
+
const credPath = join5(claudeDir, ".credentials.json");
|
|
2556
|
+
writeFileSync3(credPath, credentials, { mode: 384 });
|
|
2557
|
+
chmodSync3(credPath, 384);
|
|
2558
|
+
break;
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
async function clearActiveCredentials() {
|
|
2563
|
+
const platform = detectPlatform();
|
|
2564
|
+
switch (platform) {
|
|
2565
|
+
case "macos": {
|
|
2566
|
+
const result = await runCommand([
|
|
2567
|
+
"security",
|
|
2568
|
+
"delete-generic-password",
|
|
2569
|
+
"-s",
|
|
2570
|
+
"Claude Code-credentials"
|
|
2571
|
+
]);
|
|
2572
|
+
if (result.exitCode !== 0 && !isSecurityItemMissing(result.stderr)) {
|
|
2573
|
+
throw new Error(`Failed to clear active credentials from keychain: ${result.stderr || `exit code ${result.exitCode}`}`);
|
|
2574
|
+
}
|
|
2575
|
+
break;
|
|
2576
|
+
}
|
|
2577
|
+
case "linux":
|
|
2578
|
+
case "wsl": {
|
|
2579
|
+
if (hasSecretTool()) {
|
|
2580
|
+
await secretToolClear(activeSecretToolAttrs());
|
|
2581
|
+
}
|
|
2582
|
+
const credPath = join5(homedir3(), ".claude", ".credentials.json");
|
|
2583
|
+
rmSync3(credPath, { force: true });
|
|
2584
|
+
break;
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
async function readAccountCredentials(accountNum, email, credentialsDir = CREDENTIALS_DIR2) {
|
|
2589
|
+
ensureAccountNumberSafe(accountNum);
|
|
2590
|
+
if (!sanitizeEmailForFilename2(email)) {
|
|
2591
|
+
throw new Error(`Unsafe email for filename: ${email}`);
|
|
2592
|
+
}
|
|
2593
|
+
const platform = detectPlatform();
|
|
2594
|
+
switch (platform) {
|
|
2595
|
+
case "macos": {
|
|
2596
|
+
const result = await runCommand([
|
|
2597
|
+
"security",
|
|
2598
|
+
"find-generic-password",
|
|
2599
|
+
"-s",
|
|
2600
|
+
`Claude Code-Account-${accountNum}-${email}`,
|
|
2601
|
+
"-w"
|
|
2602
|
+
]);
|
|
2603
|
+
if (result.exitCode === 0) {
|
|
2604
|
+
return result.stdout;
|
|
2605
|
+
}
|
|
2606
|
+
if (isSecurityItemMissing(result.stderr)) {
|
|
2607
|
+
return "";
|
|
2608
|
+
}
|
|
2609
|
+
throw new Error(`Failed to read account credentials from keychain: ${result.stderr || `exit code ${result.exitCode}`}`);
|
|
2610
|
+
}
|
|
2611
|
+
case "linux":
|
|
2612
|
+
case "wsl": {
|
|
2613
|
+
if (hasSecretTool()) {
|
|
2614
|
+
const keyringValue = await secretToolLookup(backupSecretToolAttrs(accountNum, email));
|
|
2615
|
+
if (keyringValue) {
|
|
2616
|
+
return keyringValue;
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
const credFile = join5(credentialsDir, `.claude-credentials-${accountNum}-${email}.json`);
|
|
2620
|
+
if (existsSync6(credFile)) {
|
|
2621
|
+
return readFileSync6(credFile, "utf-8");
|
|
2622
|
+
}
|
|
2623
|
+
return "";
|
|
2624
|
+
}
|
|
2625
|
+
default:
|
|
2626
|
+
return "";
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
async function writeAccountCredentials(accountNum, email, credentials, credentialsDir = CREDENTIALS_DIR2) {
|
|
2630
|
+
ensureAccountNumberSafe(accountNum);
|
|
2631
|
+
if (!sanitizeEmailForFilename2(email)) {
|
|
2632
|
+
throw new Error(`Unsafe email for filename: ${email}`);
|
|
2633
|
+
}
|
|
2634
|
+
const platform = detectPlatform();
|
|
2635
|
+
switch (platform) {
|
|
2636
|
+
case "macos": {
|
|
2637
|
+
const result = await runCommand([
|
|
2638
|
+
"security",
|
|
2639
|
+
"add-generic-password",
|
|
2640
|
+
"-U",
|
|
2641
|
+
"-s",
|
|
2642
|
+
`Claude Code-Account-${accountNum}-${email}`,
|
|
2643
|
+
"-a",
|
|
2644
|
+
process.env.USER ?? "unknown",
|
|
2645
|
+
"-w",
|
|
2646
|
+
credentials
|
|
2647
|
+
]);
|
|
2648
|
+
if (result.exitCode !== 0) {
|
|
2649
|
+
throw new Error(`Failed to write account credentials to keychain: ${result.stderr || `exit code ${result.exitCode}`}`);
|
|
2650
|
+
}
|
|
2651
|
+
break;
|
|
2652
|
+
}
|
|
2653
|
+
case "linux":
|
|
2654
|
+
case "wsl": {
|
|
2655
|
+
if (hasSecretTool()) {
|
|
2656
|
+
await secretToolStore(backupSecretToolAttrs(accountNum, email), credentials);
|
|
2657
|
+
return;
|
|
2658
|
+
}
|
|
2659
|
+
const credFile = join5(credentialsDir, `.claude-credentials-${accountNum}-${email}.json`);
|
|
2660
|
+
mkdirSync3(credentialsDir, { recursive: true, mode: 448 });
|
|
2661
|
+
chmodSync3(credentialsDir, 448);
|
|
2662
|
+
const parsed = JSON.parse(credentials);
|
|
2663
|
+
await writeJsonAtomic(credFile, parsed);
|
|
2664
|
+
break;
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
async function deleteAccountCredentials(accountNum, email, credentialsDir = CREDENTIALS_DIR2) {
|
|
2669
|
+
ensureAccountNumberSafe(accountNum);
|
|
2670
|
+
if (!sanitizeEmailForFilename2(email)) {
|
|
2671
|
+
throw new Error(`Unsafe email for filename: ${email}`);
|
|
2672
|
+
}
|
|
2673
|
+
const platform = detectPlatform();
|
|
2674
|
+
switch (platform) {
|
|
2675
|
+
case "macos": {
|
|
2676
|
+
const result = await runCommand([
|
|
2677
|
+
"security",
|
|
2678
|
+
"delete-generic-password",
|
|
2679
|
+
"-s",
|
|
2680
|
+
`Claude Code-Account-${accountNum}-${email}`
|
|
2681
|
+
]);
|
|
2682
|
+
if (result.exitCode !== 0 && !isSecurityItemMissing(result.stderr)) {
|
|
2683
|
+
throw new Error(`Failed to delete account credentials from keychain: ${result.stderr || `exit code ${result.exitCode}`}`);
|
|
2684
|
+
}
|
|
2685
|
+
break;
|
|
2686
|
+
}
|
|
2687
|
+
case "linux":
|
|
2688
|
+
case "wsl": {
|
|
2689
|
+
if (hasSecretTool()) {
|
|
2690
|
+
await secretToolClear(backupSecretToolAttrs(accountNum, email));
|
|
2691
|
+
}
|
|
2692
|
+
const credFile = join5(credentialsDir, `.claude-credentials-${accountNum}-${email}.json`);
|
|
2693
|
+
rmSync3(credFile, { force: true });
|
|
2694
|
+
break;
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
}
|
|
2698
|
+
function readAccountConfig(accountNum, email, configsDir) {
|
|
2699
|
+
ensureAccountNumberSafe(accountNum);
|
|
2700
|
+
if (!sanitizeEmailForFilename2(email)) {
|
|
2701
|
+
throw new Error(`Unsafe email for filename: ${email}`);
|
|
2702
|
+
}
|
|
2703
|
+
const configFile = join5(configsDir, `.claude-config-${accountNum}-${email}.json`);
|
|
2704
|
+
if (existsSync6(configFile)) {
|
|
2705
|
+
return readFileSync6(configFile, "utf-8");
|
|
2706
|
+
}
|
|
2707
|
+
return "";
|
|
2708
|
+
}
|
|
2709
|
+
async function writeAccountConfig(accountNum, email, config, configsDir) {
|
|
2710
|
+
ensureAccountNumberSafe(accountNum);
|
|
2711
|
+
if (!sanitizeEmailForFilename2(email)) {
|
|
2712
|
+
throw new Error(`Unsafe email for filename: ${email}`);
|
|
2713
|
+
}
|
|
2714
|
+
const configFile = join5(configsDir, `.claude-config-${accountNum}-${email}.json`);
|
|
2715
|
+
mkdirSync3(configsDir, { recursive: true, mode: 448 });
|
|
2716
|
+
const parsed = JSON.parse(config);
|
|
2717
|
+
await writeJsonAtomic(configFile, parsed);
|
|
2718
|
+
}
|
|
2719
|
+
function deleteAccountConfig(accountNum, email, configsDir) {
|
|
2720
|
+
ensureAccountNumberSafe(accountNum);
|
|
2721
|
+
if (!sanitizeEmailForFilename2(email)) {
|
|
2722
|
+
throw new Error(`Unsafe email for filename: ${email}`);
|
|
2723
|
+
}
|
|
2724
|
+
const configFile = join5(configsDir, `.claude-config-${accountNum}-${email}.json`);
|
|
2725
|
+
rmSync3(configFile, { force: true });
|
|
2726
|
+
}
|
|
2727
|
+
|
|
2728
|
+
// src/providers/codex.ts
|
|
2729
|
+
import { chmodSync as chmodSync4, existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync7, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
2730
|
+
import { homedir as homedir4 } from "os";
|
|
2731
|
+
import { join as join6 } from "path";
|
|
2732
|
+
function getCodexAuthPath() {
|
|
2733
|
+
return join6(process.env.HOME ?? homedir4(), ".codex", "auth.json");
|
|
2734
|
+
}
|
|
2735
|
+
function decodeJwtPayload(token) {
|
|
2736
|
+
const parts = token.split(".");
|
|
2737
|
+
if (parts.length < 2)
|
|
2738
|
+
return null;
|
|
2739
|
+
try {
|
|
2740
|
+
const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
2741
|
+
const padded = payload + "=".repeat((4 - payload.length % 4) % 4);
|
|
2742
|
+
const decoded = Buffer.from(padded, "base64").toString("utf-8");
|
|
2743
|
+
return JSON.parse(decoded);
|
|
2744
|
+
} catch {
|
|
2745
|
+
return null;
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
function ensureBackupKeySafe(accountNum, email) {
|
|
2749
|
+
if (!validateAccountNumber(accountNum)) {
|
|
2750
|
+
throw new Error(`Unsafe account number for filename: ${accountNum}`);
|
|
2751
|
+
}
|
|
2752
|
+
if (!sanitizeEmailForFilename2(email)) {
|
|
2753
|
+
throw new Error(`Unsafe email for filename: ${email}`);
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
async function readCodexActiveAuth() {
|
|
2757
|
+
const authPath = getCodexAuthPath();
|
|
2758
|
+
if (!existsSync7(authPath)) {
|
|
2759
|
+
return "";
|
|
2760
|
+
}
|
|
2761
|
+
return readFileSync7(authPath, "utf-8");
|
|
2762
|
+
}
|
|
2763
|
+
async function writeCodexActiveAuth(raw) {
|
|
2764
|
+
const codexDir = join6(process.env.HOME ?? homedir4(), ".codex");
|
|
2765
|
+
mkdirSync4(codexDir, { recursive: true, mode: 448 });
|
|
2766
|
+
chmodSync4(codexDir, 448);
|
|
2767
|
+
const authPath = getCodexAuthPath();
|
|
2768
|
+
writeFileSync4(authPath, raw, { mode: 384 });
|
|
2769
|
+
chmodSync4(authPath, 384);
|
|
2770
|
+
}
|
|
2771
|
+
async function clearCodexActiveAuth() {
|
|
2772
|
+
rmSync4(getCodexAuthPath(), { force: true });
|
|
2773
|
+
}
|
|
2774
|
+
async function readCodexAccountAuthBackup(accountNum, email, credentialsDir) {
|
|
2775
|
+
ensureBackupKeySafe(accountNum, email);
|
|
2776
|
+
const backupPath = join6(credentialsDir, `.codex-auth-${accountNum}-${email}.json`);
|
|
2777
|
+
if (!existsSync7(backupPath)) {
|
|
2778
|
+
return "";
|
|
2779
|
+
}
|
|
2780
|
+
return readFileSync7(backupPath, "utf-8");
|
|
2781
|
+
}
|
|
2782
|
+
async function writeCodexAccountAuthBackup(accountNum, email, raw, credentialsDir) {
|
|
2783
|
+
ensureBackupKeySafe(accountNum, email);
|
|
2784
|
+
mkdirSync4(credentialsDir, { recursive: true, mode: 448 });
|
|
2785
|
+
chmodSync4(credentialsDir, 448);
|
|
2786
|
+
const backupPath = join6(credentialsDir, `.codex-auth-${accountNum}-${email}.json`);
|
|
2787
|
+
writeFileSync4(backupPath, raw, { mode: 384 });
|
|
2788
|
+
chmodSync4(backupPath, 384);
|
|
2789
|
+
}
|
|
2790
|
+
async function deleteCodexAccountAuthBackup(accountNum, email, credentialsDir) {
|
|
2791
|
+
ensureBackupKeySafe(accountNum, email);
|
|
2792
|
+
const backupPath = join6(credentialsDir, `.codex-auth-${accountNum}-${email}.json`);
|
|
2793
|
+
rmSync4(backupPath, { force: true });
|
|
2794
|
+
}
|
|
2795
|
+
function getCodexCurrentAccount() {
|
|
2796
|
+
const authPath = getCodexAuthPath();
|
|
2797
|
+
if (!existsSync7(authPath)) {
|
|
2798
|
+
return null;
|
|
2799
|
+
}
|
|
2800
|
+
try {
|
|
2801
|
+
const authObj = JSON.parse(readFileSync7(authPath, "utf-8"));
|
|
2802
|
+
const idToken = authObj.tokens?.id_token;
|
|
2803
|
+
if (!idToken) {
|
|
2804
|
+
return null;
|
|
2805
|
+
}
|
|
2806
|
+
const payload = decodeJwtPayload(idToken);
|
|
2807
|
+
if (!payload) {
|
|
2808
|
+
return null;
|
|
2809
|
+
}
|
|
2810
|
+
const email = typeof payload.email === "string" ? payload.email : null;
|
|
2811
|
+
if (!email) {
|
|
2812
|
+
return null;
|
|
2813
|
+
}
|
|
2814
|
+
const authPayload = payload["https://api.openai.com/auth"];
|
|
2815
|
+
const accountId = authPayload?.chatgpt_account_id ?? authObj.tokens?.account_id;
|
|
2816
|
+
return {
|
|
2817
|
+
email,
|
|
2818
|
+
accountId
|
|
2819
|
+
};
|
|
2820
|
+
} catch {
|
|
2821
|
+
return null;
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2824
|
+
|
|
2825
|
+
// src/providers/claude.ts
|
|
2826
|
+
function getClaudeCurrentAccountEmail() {
|
|
2827
|
+
const configPath = getClaudeConfigPath2();
|
|
2828
|
+
if (!existsSync8(configPath))
|
|
2829
|
+
return "none";
|
|
2830
|
+
try {
|
|
2831
|
+
const content = JSON.parse(readFileSync8(configPath, "utf-8"));
|
|
2832
|
+
return content?.oauthAccount?.emailAddress ?? "none";
|
|
2833
|
+
} catch {
|
|
2834
|
+
return "none";
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
var claudeProvider = {
|
|
2838
|
+
name: "claude",
|
|
2839
|
+
getCurrentAccount: () => {
|
|
2840
|
+
const email = getClaudeCurrentAccountEmail();
|
|
2841
|
+
if (email === "none")
|
|
2842
|
+
return null;
|
|
2843
|
+
return { email };
|
|
2844
|
+
},
|
|
2845
|
+
getCurrentAccountEmail: getClaudeCurrentAccountEmail,
|
|
2846
|
+
readActiveAuth: readCredentials,
|
|
2847
|
+
writeActiveAuth: writeCredentials,
|
|
2848
|
+
clearActiveAuth: clearActiveCredentials,
|
|
2849
|
+
readAccountAuth: readAccountCredentials,
|
|
2850
|
+
writeAccountAuth: writeAccountCredentials,
|
|
2851
|
+
deleteAccountAuth: deleteAccountCredentials,
|
|
2852
|
+
readAccountConfig,
|
|
2853
|
+
writeAccountConfig,
|
|
2854
|
+
deleteAccountConfig
|
|
2855
|
+
};
|
|
2856
|
+
var codexProvider = {
|
|
2857
|
+
name: "codex",
|
|
2858
|
+
getCurrentAccount: getCodexCurrentAccount,
|
|
2859
|
+
getCurrentAccountEmail: () => getCodexCurrentAccount()?.email ?? "none",
|
|
2860
|
+
readActiveAuth: readCodexActiveAuth,
|
|
2861
|
+
writeActiveAuth: writeCodexActiveAuth,
|
|
2862
|
+
clearActiveAuth: clearCodexActiveAuth,
|
|
2863
|
+
readAccountAuth: readCodexAccountAuthBackup,
|
|
2864
|
+
writeAccountAuth: writeCodexAccountAuthBackup,
|
|
2865
|
+
deleteAccountAuth: deleteCodexAccountAuthBackup,
|
|
2866
|
+
readAccountConfig: () => "",
|
|
2867
|
+
writeAccountConfig: async () => {},
|
|
2868
|
+
deleteAccountConfig: () => {}
|
|
2869
|
+
};
|
|
2870
|
+
|
|
2871
|
+
// src/providers/index.ts
|
|
2872
|
+
var providers = {
|
|
2873
|
+
claude: claudeProvider,
|
|
2874
|
+
codex: codexProvider
|
|
2875
|
+
};
|
|
2876
|
+
function getProvider(name) {
|
|
2877
|
+
return providers[name];
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2880
|
+
// src/meta.ts
|
|
2881
|
+
import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
|
|
2882
|
+
import { mkdirSync as mkdirSync5 } from "fs";
|
|
2883
|
+
import { homedir as homedir5 } from "os";
|
|
2884
|
+
import { join as join7 } from "path";
|
|
2885
|
+
function getMetaFilePath() {
|
|
2886
|
+
const home = process.env.HOME ?? homedir5();
|
|
2887
|
+
return join7(home, ".caflip-backup", ".meta.json");
|
|
2888
|
+
}
|
|
2889
|
+
function readCliMeta() {
|
|
2890
|
+
const metaFile = getMetaFilePath();
|
|
2891
|
+
if (!existsSync9(metaFile)) {
|
|
2892
|
+
return { lastProvider: "claude" };
|
|
2893
|
+
}
|
|
2894
|
+
try {
|
|
2895
|
+
const parsed = JSON.parse(readFileSync9(metaFile, "utf-8"));
|
|
2896
|
+
if (parsed.lastProvider === "codex") {
|
|
2897
|
+
return { lastProvider: "codex" };
|
|
2898
|
+
}
|
|
2899
|
+
return { lastProvider: "claude" };
|
|
2900
|
+
} catch {
|
|
2901
|
+
return { lastProvider: "claude" };
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
async function writeLastProvider(provider) {
|
|
2905
|
+
const metaFile = getMetaFilePath();
|
|
2906
|
+
mkdirSync5(join7(process.env.HOME ?? homedir5(), ".caflip-backup"), {
|
|
2907
|
+
recursive: true,
|
|
2908
|
+
mode: 448
|
|
2909
|
+
});
|
|
2910
|
+
await writeJsonAtomic(metaFile, { lastProvider: provider });
|
|
2911
|
+
}
|
|
2912
|
+
|
|
2913
|
+
// src/index.ts
|
|
2914
|
+
var ADD_CURRENT_ACCOUNT_CHOICE = "__add_current_account__";
|
|
2915
|
+
var activeBackupDir = BACKUP_DIR;
|
|
2916
|
+
var activeSequenceFile = SEQUENCE_FILE;
|
|
2917
|
+
var activeLockDir = LOCK_DIR;
|
|
2918
|
+
var activeConfigsDir = CONFIGS_DIR;
|
|
2919
|
+
var activeCredentialsDir = CREDENTIALS_DIR;
|
|
2920
|
+
var activeProvider = getProvider("claude");
|
|
2921
|
+
function setActiveProvider(provider) {
|
|
2922
|
+
activeBackupDir = getBackupDir(provider);
|
|
2923
|
+
activeSequenceFile = getSequenceFile(provider);
|
|
2924
|
+
activeLockDir = getLockDir(provider);
|
|
2925
|
+
activeConfigsDir = getConfigsDir(provider);
|
|
2926
|
+
activeCredentialsDir = getCredentialsDir(provider);
|
|
2927
|
+
activeProvider = getProvider(provider);
|
|
2928
|
+
}
|
|
2929
|
+
function setupDirectories() {
|
|
2930
|
+
for (const dir of [activeBackupDir, activeConfigsDir, activeCredentialsDir]) {
|
|
2931
|
+
mkdirSync6(dir, { recursive: true, mode: 448 });
|
|
2932
|
+
}
|
|
2933
|
+
}
|
|
2934
|
+
function getCurrentAccount() {
|
|
2935
|
+
return activeProvider.getCurrentAccountEmail();
|
|
2936
|
+
}
|
|
2937
|
+
function getProviderLabel() {
|
|
2938
|
+
return activeProvider.name === "codex" ? "Codex" : "Claude Code";
|
|
2939
|
+
}
|
|
2940
|
+
async function clearActiveOAuthAccount() {
|
|
2941
|
+
const configPath = getClaudeConfigPath();
|
|
2942
|
+
let configObj = {};
|
|
2943
|
+
if (existsSync10(configPath)) {
|
|
2944
|
+
try {
|
|
2945
|
+
configObj = JSON.parse(readFileSync10(configPath, "utf-8"));
|
|
2946
|
+
} catch {
|
|
2947
|
+
configObj = {};
|
|
2948
|
+
}
|
|
2949
|
+
}
|
|
2950
|
+
delete configObj.oauthAccount;
|
|
2951
|
+
await writeJsonAtomic2(configPath, configObj);
|
|
2952
|
+
}
|
|
2953
|
+
async function syncSequenceActiveAccount(seq) {
|
|
2954
|
+
const currentEmail = getCurrentAccount();
|
|
2955
|
+
const resolvedActive = resolveManagedAccountNumberForEmail(seq, currentEmail);
|
|
2956
|
+
if (seq.activeAccountNumber !== resolvedActive) {
|
|
2957
|
+
seq.activeAccountNumber = resolvedActive;
|
|
2958
|
+
seq.lastUpdated = new Date().toISOString();
|
|
2959
|
+
await writeJsonAtomic2(activeSequenceFile, seq);
|
|
2960
|
+
}
|
|
2961
|
+
return seq;
|
|
2962
|
+
}
|
|
2963
|
+
async function performSwitch(seq, targetAccount, options) {
|
|
2964
|
+
const targetEmail = seq.accounts[targetAccount].email;
|
|
2965
|
+
const currentEmail = options?.currentEmail ?? getCurrentAccount();
|
|
2966
|
+
const currentAccount = currentEmail === "none" ? null : resolveAccountIdentifier(seq, currentEmail);
|
|
2967
|
+
if (currentEmail === targetEmail) {
|
|
2968
|
+
const account = seq.accounts[targetAccount];
|
|
2969
|
+
const aliasStr2 = account.alias ? ` [${account.alias}]` : "";
|
|
2970
|
+
const displayLabel2 = getDisplayAccountLabel(seq, targetAccount);
|
|
2971
|
+
if (seq.activeAccountNumber !== Number(targetAccount)) {
|
|
2972
|
+
seq.activeAccountNumber = Number(targetAccount);
|
|
2973
|
+
seq.lastUpdated = new Date().toISOString();
|
|
2974
|
+
await writeJsonAtomic2(activeSequenceFile, seq);
|
|
2975
|
+
}
|
|
2976
|
+
console.log(`Already using ${displayLabel2} (${account.email})${aliasStr2}`);
|
|
2977
|
+
return;
|
|
2978
|
+
}
|
|
2979
|
+
if (!sanitizeEmailForFilename(targetEmail)) {
|
|
2980
|
+
throw new Error("Target account email is not safe for storage");
|
|
2981
|
+
}
|
|
2982
|
+
if (currentEmail !== "none" && !sanitizeEmailForFilename(currentEmail)) {
|
|
2983
|
+
throw new Error("Current account email is not safe for storage");
|
|
2984
|
+
}
|
|
2985
|
+
if (currentEmail !== "none" && currentAccount) {
|
|
2986
|
+
const currentCreds = await activeProvider.readActiveAuth();
|
|
2987
|
+
if (currentCreds) {
|
|
2988
|
+
await activeProvider.writeAccountAuth(currentAccount, currentEmail, currentCreds, activeCredentialsDir);
|
|
2989
|
+
}
|
|
2990
|
+
if (activeProvider.name === "claude") {
|
|
2991
|
+
const configPath = getClaudeConfigPath();
|
|
2992
|
+
const currentConfig = existsSync10(configPath) ? readFileSync10(configPath, "utf-8") : "";
|
|
2993
|
+
if (currentConfig) {
|
|
2994
|
+
await activeProvider.writeAccountConfig(currentAccount, currentEmail, currentConfig, activeConfigsDir);
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
const targetCreds = await activeProvider.readAccountAuth(targetAccount, targetEmail, activeCredentialsDir);
|
|
2999
|
+
const targetConfig = activeProvider.readAccountConfig(targetAccount, targetEmail, activeConfigsDir);
|
|
3000
|
+
if (!targetCreds) {
|
|
3001
|
+
throw new Error(`Missing backup data for ${getDisplayAccountLabel(seq, targetAccount)}`);
|
|
3002
|
+
}
|
|
3003
|
+
if (activeProvider.name === "claude" && !targetConfig) {
|
|
3004
|
+
throw new Error(`Missing backup data for ${getDisplayAccountLabel(seq, targetAccount)}`);
|
|
3005
|
+
}
|
|
3006
|
+
await activeProvider.writeActiveAuth(targetCreds);
|
|
3007
|
+
if (activeProvider.name === "claude") {
|
|
3008
|
+
const targetConfigObj = JSON.parse(targetConfig);
|
|
3009
|
+
const oauthAccount = targetConfigObj.oauthAccount;
|
|
3010
|
+
if (!oauthAccount) {
|
|
3011
|
+
throw new Error("Invalid oauthAccount in backup");
|
|
3012
|
+
}
|
|
3013
|
+
const configPath = getClaudeConfigPath();
|
|
3014
|
+
let currentConfigObj = {};
|
|
3015
|
+
if (existsSync10(configPath)) {
|
|
3016
|
+
currentConfigObj = JSON.parse(readFileSync10(configPath, "utf-8"));
|
|
3017
|
+
}
|
|
3018
|
+
currentConfigObj.oauthAccount = oauthAccount;
|
|
3019
|
+
await writeJsonAtomic2(configPath, currentConfigObj);
|
|
3020
|
+
}
|
|
3021
|
+
seq.activeAccountNumber = Number(targetAccount);
|
|
3022
|
+
seq.lastUpdated = new Date().toISOString();
|
|
3023
|
+
await writeJsonAtomic2(activeSequenceFile, seq);
|
|
3024
|
+
const alias = seq.accounts[targetAccount].alias;
|
|
3025
|
+
const aliasStr = alias ? ` [${alias}]` : "";
|
|
3026
|
+
const displayLabel = getDisplayAccountLabel(seq, targetAccount);
|
|
3027
|
+
console.log(`Switched to ${displayLabel} (${targetEmail})${aliasStr}`);
|
|
3028
|
+
console.log(`
|
|
3029
|
+
Please restart ${getProviderLabel()} to use the new authentication.
|
|
3030
|
+
`);
|
|
3031
|
+
}
|
|
3032
|
+
async function cmdList() {
|
|
3033
|
+
if (!existsSync10(activeSequenceFile)) {
|
|
3034
|
+
const providerCmd = activeProvider.name === "codex" ? "caflip codex add" : "caflip claude add";
|
|
3035
|
+
console.log(`No accounts managed yet. Run: ${providerCmd}`);
|
|
3036
|
+
return;
|
|
3037
|
+
}
|
|
3038
|
+
const seq = await loadSequence(activeSequenceFile);
|
|
3039
|
+
await syncSequenceActiveAccount(seq);
|
|
3040
|
+
const currentEmail = getCurrentAccount();
|
|
3041
|
+
console.log("Accounts:");
|
|
3042
|
+
seq.sequence.forEach((num, index) => {
|
|
3043
|
+
const numStr = String(num);
|
|
3044
|
+
const account = seq.accounts[numStr];
|
|
3045
|
+
if (!account) {
|
|
3046
|
+
throw new Error(`Corrupt sequence data: missing account entry for id ${numStr}`);
|
|
3047
|
+
}
|
|
3048
|
+
const isActive = account.email === currentEmail;
|
|
3049
|
+
let line = ` ${index + 1}: ${account.email}`;
|
|
3050
|
+
if (account.alias)
|
|
3051
|
+
line += ` [${account.alias}]`;
|
|
3052
|
+
if (isActive)
|
|
3053
|
+
line += " (active)";
|
|
3054
|
+
console.log(line);
|
|
3055
|
+
});
|
|
3056
|
+
}
|
|
3057
|
+
async function cmdAdd(alias) {
|
|
3058
|
+
setupDirectories();
|
|
3059
|
+
await initSequenceFile(activeSequenceFile);
|
|
3060
|
+
const currentAccount = activeProvider.getCurrentAccount();
|
|
3061
|
+
const currentEmail = currentAccount?.email ?? "none";
|
|
3062
|
+
if (currentEmail === "none") {
|
|
3063
|
+
throw new Error(`No active ${getProviderLabel()} account found. Please log in first.`);
|
|
3064
|
+
}
|
|
3065
|
+
if (!sanitizeEmailForFilename(currentEmail)) {
|
|
3066
|
+
throw new Error("Current account email is not safe for storage");
|
|
3067
|
+
}
|
|
3068
|
+
const seq = await loadSequence(activeSequenceFile);
|
|
3069
|
+
await syncSequenceActiveAccount(seq);
|
|
3070
|
+
if (accountExists(seq, currentEmail)) {
|
|
3071
|
+
console.log(`Account ${currentEmail} is already managed.`);
|
|
3072
|
+
return;
|
|
3073
|
+
}
|
|
3074
|
+
if (alias) {
|
|
3075
|
+
const result = validateAlias(alias);
|
|
3076
|
+
if (!result.valid) {
|
|
3077
|
+
throw new Error(result.reason);
|
|
3078
|
+
}
|
|
3079
|
+
if (findAccountByAlias(seq, alias)) {
|
|
3080
|
+
throw new Error(`Alias "${alias}" is already in use`);
|
|
3081
|
+
}
|
|
3082
|
+
}
|
|
3083
|
+
const creds = await activeProvider.readActiveAuth();
|
|
3084
|
+
if (!creds) {
|
|
3085
|
+
throw new Error("No credentials found for current account");
|
|
3086
|
+
}
|
|
3087
|
+
let config = "";
|
|
3088
|
+
let uuid = currentAccount?.accountId ?? "";
|
|
3089
|
+
if (activeProvider.name === "claude") {
|
|
3090
|
+
const configPath = getClaudeConfigPath();
|
|
3091
|
+
config = readFileSync10(configPath, "utf-8");
|
|
3092
|
+
const configObj = JSON.parse(config);
|
|
3093
|
+
uuid = configObj.oauthAccount?.accountUuid ?? "";
|
|
3094
|
+
}
|
|
3095
|
+
const updated = addAccountToSequence(seq, {
|
|
3096
|
+
email: currentEmail,
|
|
3097
|
+
uuid,
|
|
3098
|
+
alias
|
|
3099
|
+
});
|
|
3100
|
+
const accountNum = String(updated.activeAccountNumber);
|
|
3101
|
+
const displayLabel = getDisplayAccountLabel(updated, accountNum);
|
|
3102
|
+
await activeProvider.writeAccountAuth(accountNum, currentEmail, creds, activeCredentialsDir);
|
|
3103
|
+
if (config) {
|
|
3104
|
+
await activeProvider.writeAccountConfig(accountNum, currentEmail, config, activeConfigsDir);
|
|
3105
|
+
}
|
|
3106
|
+
await writeJsonAtomic2(activeSequenceFile, updated);
|
|
3107
|
+
const aliasStr = alias ? ` [${alias}]` : "";
|
|
3108
|
+
console.log(`Added ${displayLabel}: ${currentEmail}${aliasStr}`);
|
|
3109
|
+
}
|
|
3110
|
+
async function cmdRemove(identifier) {
|
|
3111
|
+
if (!existsSync10(activeSequenceFile)) {
|
|
3112
|
+
throw new Error("No accounts managed yet");
|
|
3113
|
+
}
|
|
3114
|
+
const seq = await loadSequence(activeSequenceFile);
|
|
3115
|
+
await syncSequenceActiveAccount(seq);
|
|
3116
|
+
let accountNum;
|
|
3117
|
+
if (!identifier) {
|
|
3118
|
+
accountNum = await pickAccountForRemoval(seq);
|
|
3119
|
+
} else {
|
|
3120
|
+
if (/^\d+$/.test(identifier)) {
|
|
3121
|
+
throw new Error("Remove target must be an email, not a number");
|
|
3122
|
+
}
|
|
3123
|
+
const resolved = resolveAccountIdentifier(seq, identifier);
|
|
3124
|
+
if (!resolved) {
|
|
3125
|
+
throw new Error(`Account not found: ${identifier}`);
|
|
3126
|
+
}
|
|
3127
|
+
accountNum = resolved;
|
|
3128
|
+
}
|
|
3129
|
+
const account = seq.accounts[accountNum];
|
|
3130
|
+
if (!account) {
|
|
3131
|
+
throw new Error(`${getDisplayAccountLabel(seq, accountNum)} does not exist`);
|
|
3132
|
+
}
|
|
3133
|
+
if (seq.activeAccountNumber === Number(accountNum)) {
|
|
3134
|
+
console.log(`Warning: ${getDisplayAccountLabel(seq, accountNum)} (${account.email}) is currently active`);
|
|
3135
|
+
}
|
|
3136
|
+
const confirmed = await confirmAction(`Permanently remove ${getDisplayAccountLabel(seq, accountNum)} (${account.email})?`);
|
|
3137
|
+
if (!confirmed) {
|
|
3138
|
+
console.log("Cancelled");
|
|
3139
|
+
return;
|
|
3140
|
+
}
|
|
3141
|
+
const updated = removeAccountFromSequence(seq, accountNum);
|
|
3142
|
+
const action = getPostRemovalAction(seq, updated, accountNum);
|
|
3143
|
+
if (action.type === "switch") {
|
|
3144
|
+
await performSwitch(seq, action.targetAccountNumber);
|
|
3145
|
+
} else if (action.type === "logout") {
|
|
3146
|
+
await activeProvider.clearActiveAuth();
|
|
3147
|
+
if (activeProvider.name === "claude") {
|
|
3148
|
+
await clearActiveOAuthAccount();
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
await activeProvider.deleteAccountAuth(accountNum, account.email, activeCredentialsDir);
|
|
3152
|
+
activeProvider.deleteAccountConfig(accountNum, account.email, activeConfigsDir);
|
|
3153
|
+
await writeJsonAtomic2(activeSequenceFile, updated);
|
|
3154
|
+
console.log(`${getDisplayAccountLabel(seq, accountNum)} (${account.email}) has been removed`);
|
|
3155
|
+
}
|
|
3156
|
+
async function cmdNext() {
|
|
3157
|
+
if (!existsSync10(activeSequenceFile)) {
|
|
3158
|
+
throw new Error("No accounts managed yet");
|
|
3159
|
+
}
|
|
3160
|
+
const seq = await loadSequence(activeSequenceFile);
|
|
3161
|
+
await syncSequenceActiveAccount(seq);
|
|
3162
|
+
if (seq.sequence.length < 2) {
|
|
3163
|
+
throw new Error("Need at least 2 accounts to rotate");
|
|
3164
|
+
}
|
|
3165
|
+
const nextNum = getNextInSequence(seq);
|
|
3166
|
+
await performSwitch(seq, String(nextNum));
|
|
3167
|
+
}
|
|
3168
|
+
async function cmdStatus(options) {
|
|
3169
|
+
const jsonMode = options?.json ?? false;
|
|
3170
|
+
const email = getCurrentAccount();
|
|
3171
|
+
let alias = null;
|
|
3172
|
+
let managed = false;
|
|
3173
|
+
if (email !== "none" && existsSync10(activeSequenceFile)) {
|
|
3174
|
+
const seq = await loadSequence(activeSequenceFile);
|
|
3175
|
+
for (const account of Object.values(seq.accounts)) {
|
|
3176
|
+
if (account.email === email) {
|
|
3177
|
+
managed = true;
|
|
3178
|
+
alias = account.alias ?? null;
|
|
3179
|
+
break;
|
|
3180
|
+
}
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3183
|
+
if (jsonMode) {
|
|
3184
|
+
console.log(JSON.stringify({
|
|
3185
|
+
provider: activeProvider.name,
|
|
3186
|
+
email: email === "none" ? null : email,
|
|
3187
|
+
alias,
|
|
3188
|
+
managed
|
|
3189
|
+
}));
|
|
3190
|
+
return;
|
|
3191
|
+
}
|
|
3192
|
+
if (email === "none") {
|
|
3193
|
+
console.log("none");
|
|
3194
|
+
} else {
|
|
3195
|
+
if (alias) {
|
|
3196
|
+
console.log(`${email} [${alias}]`);
|
|
3197
|
+
return;
|
|
3198
|
+
}
|
|
3199
|
+
console.log(email);
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
async function cmdAlias(alias, identifier) {
|
|
3203
|
+
if (!existsSync10(activeSequenceFile)) {
|
|
3204
|
+
throw new Error("No accounts managed yet");
|
|
3205
|
+
}
|
|
3206
|
+
const result = validateAlias(alias);
|
|
3207
|
+
if (!result.valid) {
|
|
3208
|
+
throw new Error(result.reason);
|
|
3209
|
+
}
|
|
3210
|
+
const seq = await loadSequence(activeSequenceFile);
|
|
3211
|
+
if (identifier && /^\d+$/.test(identifier)) {
|
|
3212
|
+
throw new Error("Alias target must be an email, not a number");
|
|
3213
|
+
}
|
|
3214
|
+
const currentEmail = getCurrentAccount();
|
|
3215
|
+
const accountNum = resolveAliasTargetAccount(seq, { identifier, currentEmail });
|
|
3216
|
+
if (!accountNum) {
|
|
3217
|
+
if (identifier) {
|
|
3218
|
+
throw new Error(`Account not found: ${identifier}`);
|
|
3219
|
+
} else if (currentEmail === "none") {
|
|
3220
|
+
throw new Error(`No active ${getProviderLabel()} account found. Please log in first.`);
|
|
3221
|
+
} else {
|
|
3222
|
+
throw new Error(`Current account is not managed: ${currentEmail}`);
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3225
|
+
const updated = setAlias(seq, accountNum, alias);
|
|
3226
|
+
await writeJsonAtomic2(activeSequenceFile, updated);
|
|
3227
|
+
const account = updated.accounts[accountNum];
|
|
3228
|
+
console.log(`Alias "${alias}" set for ${getDisplayAccountLabel(updated, accountNum)} (${account.email})`);
|
|
3229
|
+
}
|
|
3230
|
+
async function cmdInteractiveSwitch() {
|
|
3231
|
+
const currentEmail = getCurrentAccount();
|
|
3232
|
+
const hasSequence = existsSync10(activeSequenceFile);
|
|
3233
|
+
const seq = hasSequence ? await loadSequence(activeSequenceFile) : null;
|
|
3234
|
+
if (seq) {
|
|
3235
|
+
await syncSequenceActiveAccount(seq);
|
|
3236
|
+
}
|
|
3237
|
+
if (!seq || seq.sequence.length === 0) {
|
|
3238
|
+
const emptyStateChoices = [
|
|
3239
|
+
{
|
|
3240
|
+
name: `+ Add current logged-in account${currentEmail === "none" ? "" : ` (${currentEmail})`}`,
|
|
3241
|
+
value: ADD_CURRENT_ACCOUNT_CHOICE
|
|
3242
|
+
},
|
|
3243
|
+
{ name: "Back", value: "__back__" }
|
|
3244
|
+
];
|
|
3245
|
+
const selected2 = await pickChoice(`No managed ${getProviderLabel()} accounts yet`, emptyStateChoices);
|
|
3246
|
+
if (selected2 === "__back__") {
|
|
3247
|
+
return;
|
|
3248
|
+
}
|
|
3249
|
+
await cmdAdd();
|
|
3250
|
+
return;
|
|
3251
|
+
}
|
|
3252
|
+
const shouldOfferAddCurrent = currentEmail !== "none" && !accountExists(seq, currentEmail);
|
|
3253
|
+
const extraChoices = shouldOfferAddCurrent ? [{ name: `+ Add current logged-in account (${currentEmail})`, value: ADD_CURRENT_ACCOUNT_CHOICE }] : [];
|
|
3254
|
+
const selected = await pickAccount(seq, `caflip v${package_default.version} \u2014 Switch ${getProviderLabel()} account:`, undefined, extraChoices);
|
|
3255
|
+
if (selected === ADD_CURRENT_ACCOUNT_CHOICE) {
|
|
3256
|
+
await cmdAdd();
|
|
3257
|
+
return;
|
|
3258
|
+
}
|
|
3259
|
+
await performSwitch(seq, selected, { currentEmail });
|
|
3260
|
+
}
|
|
3261
|
+
function showHelp() {
|
|
3262
|
+
console.log(`caflip - Coding Agent Account Switch (Claude Code + Codex)
|
|
3263
|
+
|
|
3264
|
+
Usage:
|
|
3265
|
+
caflip
|
|
3266
|
+
caflip <claude|codex> [command]
|
|
3267
|
+
|
|
3268
|
+
Commands:
|
|
3269
|
+
(no args) Interactive provider picker
|
|
3270
|
+
<provider> Interactive account picker for provider
|
|
3271
|
+
<provider> <alias> Switch by alias for provider
|
|
3272
|
+
<provider> list List all managed accounts
|
|
3273
|
+
<provider> add [--alias <name>] Add current account
|
|
3274
|
+
<provider> remove [<email>] Remove an account
|
|
3275
|
+
<provider> next Rotate to next account
|
|
3276
|
+
<provider> status [--json] Show current account
|
|
3277
|
+
<provider> alias <name> [<email>] Set alias for current or target account
|
|
3278
|
+
help Show this help
|
|
3279
|
+
|
|
3280
|
+
Examples:
|
|
3281
|
+
caflip Pick provider interactively
|
|
3282
|
+
caflip claude Pick Claude account interactively
|
|
3283
|
+
caflip claude work Switch Claude account by alias
|
|
3284
|
+
caflip claude add --alias personal Add current Claude account with alias
|
|
3285
|
+
caflip claude status --json Show Claude status as JSON
|
|
3286
|
+
caflip codex list List managed Codex accounts
|
|
3287
|
+
caflip codex add --alias work Add current Codex account with alias
|
|
3288
|
+
caflip codex alias work user@company.com
|
|
3289
|
+
Set Codex alias for target email`);
|
|
3290
|
+
}
|
|
3291
|
+
async function main() {
|
|
3292
|
+
const parsed = parseProviderArgs(process.argv.slice(2));
|
|
3293
|
+
const provider = parsed.provider;
|
|
3294
|
+
const args = parsed.commandArgs;
|
|
3295
|
+
const command = args[0];
|
|
3296
|
+
let lockHeld = false;
|
|
3297
|
+
const runWithLock = async (fn) => {
|
|
3298
|
+
setupDirectories();
|
|
3299
|
+
acquireLock(activeLockDir);
|
|
3300
|
+
lockHeld = true;
|
|
3301
|
+
try {
|
|
3302
|
+
return await fn();
|
|
3303
|
+
} finally {
|
|
3304
|
+
if (lockHeld) {
|
|
3305
|
+
releaseLock(activeLockDir);
|
|
3306
|
+
lockHeld = false;
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
};
|
|
3310
|
+
const runWithProviderLock = async (targetProvider, fn) => {
|
|
3311
|
+
setActiveProvider(targetProvider);
|
|
3312
|
+
return await runWithLock(fn);
|
|
3313
|
+
};
|
|
3314
|
+
const isHelpCommand = command === "help" || command === "--help" || command === "-h";
|
|
3315
|
+
if (!parsed.isProviderQualified && command && !isHelpCommand) {
|
|
3316
|
+
if (RESERVED_COMMANDS.includes(command)) {
|
|
3317
|
+
console.error("Error: Provider is required for non-interactive commands.");
|
|
3318
|
+
console.error("Try: caflip claude list");
|
|
3319
|
+
process.exit(2);
|
|
3320
|
+
} else {
|
|
3321
|
+
console.error("Error: Alias requires provider prefix.");
|
|
3322
|
+
console.error("Try: caflip claude <alias> or caflip codex <alias>");
|
|
3323
|
+
process.exit(2);
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
if (!command) {
|
|
3327
|
+
if (!provider) {
|
|
3328
|
+
const defaultProvider = readCliMeta().lastProvider;
|
|
3329
|
+
const selectedProvider = await pickProvider(defaultProvider);
|
|
3330
|
+
await writeLastProvider(selectedProvider);
|
|
3331
|
+
await runWithProviderLock(selectedProvider, async () => {
|
|
3332
|
+
await cmdInteractiveSwitch();
|
|
3333
|
+
});
|
|
3334
|
+
return;
|
|
3335
|
+
}
|
|
3336
|
+
await runWithProviderLock(provider, async () => {
|
|
3337
|
+
await cmdInteractiveSwitch();
|
|
3338
|
+
});
|
|
3339
|
+
return;
|
|
3340
|
+
}
|
|
3341
|
+
if (isHelpCommand) {
|
|
3342
|
+
showHelp();
|
|
3343
|
+
return;
|
|
3344
|
+
}
|
|
3345
|
+
if (!provider) {
|
|
3346
|
+
console.error("Error: Provider is required for non-interactive commands.");
|
|
3347
|
+
console.error("Try: caflip claude list");
|
|
3348
|
+
process.exit(2);
|
|
3349
|
+
}
|
|
3350
|
+
setActiveProvider(provider);
|
|
3351
|
+
switch (command) {
|
|
3352
|
+
case "list":
|
|
3353
|
+
await cmdList();
|
|
3354
|
+
break;
|
|
3355
|
+
case "add": {
|
|
3356
|
+
await runWithLock(async () => {
|
|
3357
|
+
let alias;
|
|
3358
|
+
const aliasIdx = args.indexOf("--alias");
|
|
3359
|
+
if (aliasIdx !== -1 && args[aliasIdx + 1]) {
|
|
3360
|
+
alias = args[aliasIdx + 1];
|
|
3361
|
+
}
|
|
3362
|
+
await cmdAdd(alias);
|
|
3363
|
+
});
|
|
3364
|
+
break;
|
|
3365
|
+
}
|
|
3366
|
+
case "remove": {
|
|
3367
|
+
await runWithLock(async () => {
|
|
3368
|
+
await cmdRemove(args[1]);
|
|
3369
|
+
});
|
|
3370
|
+
break;
|
|
3371
|
+
}
|
|
3372
|
+
case "next": {
|
|
3373
|
+
await runWithLock(async () => {
|
|
3374
|
+
await cmdNext();
|
|
3375
|
+
});
|
|
3376
|
+
break;
|
|
3377
|
+
}
|
|
3378
|
+
case "status":
|
|
3379
|
+
await cmdStatus({ json: args.includes("--json") });
|
|
3380
|
+
break;
|
|
3381
|
+
case "alias": {
|
|
3382
|
+
if (!args[1]) {
|
|
3383
|
+
console.error("Usage: caflip alias <name> [<email>]");
|
|
3384
|
+
process.exit(1);
|
|
3385
|
+
}
|
|
3386
|
+
await runWithLock(async () => {
|
|
3387
|
+
await cmdAlias(args[1], args[2]);
|
|
3388
|
+
});
|
|
3389
|
+
break;
|
|
3390
|
+
}
|
|
3391
|
+
default: {
|
|
3392
|
+
if (existsSync10(activeSequenceFile)) {
|
|
3393
|
+
const seq = await loadSequence(activeSequenceFile);
|
|
3394
|
+
const accountNum = findAccountByAlias(seq, command);
|
|
3395
|
+
if (accountNum) {
|
|
3396
|
+
await runWithLock(async () => {
|
|
3397
|
+
await performSwitch(seq, accountNum);
|
|
3398
|
+
});
|
|
3399
|
+
return;
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
console.error(`Error: Unknown command "${command}"`);
|
|
3403
|
+
showHelp();
|
|
3404
|
+
process.exit(1);
|
|
3405
|
+
}
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
if (__require.main == __require.module) {
|
|
3409
|
+
main().catch((err) => {
|
|
3410
|
+
if (err instanceof PromptCancelledError) {
|
|
3411
|
+
console.log("Cancelled");
|
|
3412
|
+
process.exit(0);
|
|
3413
|
+
}
|
|
3414
|
+
console.error(`Error: ${err.message}`);
|
|
3415
|
+
process.exit(1);
|
|
3416
|
+
});
|
|
3417
|
+
}
|
|
3418
|
+
export {
|
|
3419
|
+
performSwitch
|
|
3420
|
+
};
|