caflip 0.3.0 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -4
- package/dist/cli.js +155 -85
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,10 +63,15 @@ bun run dev -- help
|
|
|
63
63
|
## Quick Start
|
|
64
64
|
|
|
65
65
|
```bash
|
|
66
|
-
# Show current account / managed accounts across both providers
|
|
66
|
+
# Show current active account / all managed accounts across both providers
|
|
67
67
|
caflip status
|
|
68
68
|
caflip list
|
|
69
69
|
|
|
70
|
+
# Pick provider interactively, then add/remove/login
|
|
71
|
+
caflip add
|
|
72
|
+
caflip remove
|
|
73
|
+
caflip login
|
|
74
|
+
|
|
70
75
|
# Add your first Claude account (must already be logged in)
|
|
71
76
|
caflip claude add --alias personal
|
|
72
77
|
|
|
@@ -105,7 +110,10 @@ After switching, restart the target CLI (Claude Code or Codex) to pick up new au
|
|
|
105
110
|
|---|---|
|
|
106
111
|
| `caflip` | Interactive provider picker (Claude/Codex) |
|
|
107
112
|
| `caflip list` | List managed accounts for Claude and Codex |
|
|
108
|
-
| `caflip status` | Show current account for Claude and Codex |
|
|
113
|
+
| `caflip status` | Show current active account for Claude and Codex |
|
|
114
|
+
| `caflip add [--alias name]` | Pick provider, then add current account |
|
|
115
|
+
| `caflip login [-- <args...>]` | Pick provider, then run provider login and register the resulting session |
|
|
116
|
+
| `caflip remove [email]` | Pick provider, then remove an account |
|
|
109
117
|
| `caflip claude [command]` | Run command for Claude provider |
|
|
110
118
|
| `caflip codex [command]` | Run command for Codex provider |
|
|
111
119
|
| `caflip [provider]` | Interactive account picker for that provider |
|
|
@@ -115,7 +123,7 @@ After switching, restart the target CLI (Claude Code or Codex) to pick up new au
|
|
|
115
123
|
| `caflip [provider] login [-- <args...>]` | Run provider login and register the resulting session |
|
|
116
124
|
| `caflip [provider] remove [email]` | Remove an account |
|
|
117
125
|
| `caflip [provider] next` | Rotate to next account |
|
|
118
|
-
| `caflip [provider] status` | Show current account |
|
|
126
|
+
| `caflip [provider] status` | Show current active account |
|
|
119
127
|
| `caflip [provider] alias <name> [email]` | Set alias for current or target account |
|
|
120
128
|
| `caflip help` | Show help |
|
|
121
129
|
|
|
@@ -132,16 +140,23 @@ caflip claude alias work hi.lucienlee@gmail.com
|
|
|
132
140
|
caflip codex alias work me@company.com
|
|
133
141
|
```
|
|
134
142
|
|
|
135
|
-
`remove`
|
|
143
|
+
`add`, `remove`, and `login` can be used without a provider prefix. In that case, caflip asks you to choose Claude or Codex first, then continues the normal command flow.
|
|
144
|
+
|
|
145
|
+
`remove` target accepts email only. Omit it to choose from the interactive picker after selecting a provider.
|
|
136
146
|
|
|
137
147
|
`login` can be used without arguments for the default login flow. Pass provider-specific flags after `--`:
|
|
138
148
|
|
|
139
149
|
```bash
|
|
150
|
+
caflip login
|
|
140
151
|
caflip claude login
|
|
141
152
|
caflip claude login -- --email lucien@aibor.io --sso
|
|
142
153
|
caflip codex login -- --device-auth
|
|
143
154
|
```
|
|
144
155
|
|
|
156
|
+
`status` shows the currently active account for the selected provider. It does not list every saved account.
|
|
157
|
+
|
|
158
|
+
Use `list` when you want to inspect all managed accounts for a provider.
|
|
159
|
+
|
|
145
160
|
## Shell Prompt Integration
|
|
146
161
|
|
|
147
162
|
Show the current account in your prompt:
|
package/dist/cli.js
CHANGED
|
@@ -617,7 +617,7 @@ function validateAlias(alias) {
|
|
|
617
617
|
// package.json
|
|
618
618
|
var package_default = {
|
|
619
619
|
name: "caflip",
|
|
620
|
-
version: "0.3.
|
|
620
|
+
version: "0.3.3",
|
|
621
621
|
type: "module",
|
|
622
622
|
bin: {
|
|
623
623
|
caflip: "bin/caflip"
|
|
@@ -3166,6 +3166,7 @@ async function writeLastProvider(provider) {
|
|
|
3166
3166
|
|
|
3167
3167
|
// src/index.ts
|
|
3168
3168
|
var ADD_CURRENT_ACCOUNT_CHOICE = "__add_current_account__";
|
|
3169
|
+
var INTERACTIVE_PROVIDER_COMMANDS = ["add", "remove", "login"];
|
|
3169
3170
|
var activeBackupDir = BACKUP_DIR;
|
|
3170
3171
|
var activeSequenceFile = SEQUENCE_FILE;
|
|
3171
3172
|
var activeLockDir = LOCK_DIR;
|
|
@@ -3196,6 +3197,76 @@ function showProviderRequiredError(command) {
|
|
|
3196
3197
|
console.error(`Try: caflip claude ${command} or caflip codex ${command}`);
|
|
3197
3198
|
process.exit(2);
|
|
3198
3199
|
}
|
|
3200
|
+
function supportsInteractiveProviderSelection(command) {
|
|
3201
|
+
return INTERACTIVE_PROVIDER_COMMANDS.includes(command ?? "");
|
|
3202
|
+
}
|
|
3203
|
+
async function resolveProviderForCommand(provider, command, deps = {
|
|
3204
|
+
readCliMeta,
|
|
3205
|
+
pickProvider,
|
|
3206
|
+
writeLastProvider
|
|
3207
|
+
}) {
|
|
3208
|
+
if (provider || !supportsInteractiveProviderSelection(command)) {
|
|
3209
|
+
return provider;
|
|
3210
|
+
}
|
|
3211
|
+
const defaultProvider = deps.readCliMeta().lastProvider;
|
|
3212
|
+
const selectedProvider = await deps.pickProvider(defaultProvider);
|
|
3213
|
+
await deps.writeLastProvider(selectedProvider);
|
|
3214
|
+
return selectedProvider;
|
|
3215
|
+
}
|
|
3216
|
+
async function resolveCliContext(parsed, deps = { resolveProviderForCommand }) {
|
|
3217
|
+
let provider = parsed.provider;
|
|
3218
|
+
const args = parsed.commandArgs;
|
|
3219
|
+
const command = args[0];
|
|
3220
|
+
const isHelpCommand = command === "help" || command === "--help" || command === "-h";
|
|
3221
|
+
const isProviderOptionalReadOnlyCommand = command === "list" || command === "status";
|
|
3222
|
+
const supportsInteractiveProvider = supportsInteractiveProviderSelection(command);
|
|
3223
|
+
if (!parsed.isProviderQualified && command && !isHelpCommand && !isProviderOptionalReadOnlyCommand) {
|
|
3224
|
+
const isReservedCommand = RESERVED_COMMANDS.includes(command);
|
|
3225
|
+
if (!isReservedCommand) {
|
|
3226
|
+
console.error("Error: Alias requires provider prefix.");
|
|
3227
|
+
console.error("Try: caflip claude <alias> or caflip codex <alias>");
|
|
3228
|
+
process.exit(2);
|
|
3229
|
+
}
|
|
3230
|
+
if (!supportsInteractiveProvider) {
|
|
3231
|
+
showProviderRequiredError(command);
|
|
3232
|
+
}
|
|
3233
|
+
}
|
|
3234
|
+
if (!command) {
|
|
3235
|
+
return {
|
|
3236
|
+
mode: "interactive-switch",
|
|
3237
|
+
provider,
|
|
3238
|
+
args,
|
|
3239
|
+
command: undefined
|
|
3240
|
+
};
|
|
3241
|
+
}
|
|
3242
|
+
if (isHelpCommand) {
|
|
3243
|
+
showHelp();
|
|
3244
|
+
process.exit(0);
|
|
3245
|
+
}
|
|
3246
|
+
if (!provider) {
|
|
3247
|
+
if (command === "list" || command === "status") {
|
|
3248
|
+
return {
|
|
3249
|
+
mode: "all-providers",
|
|
3250
|
+
provider: null,
|
|
3251
|
+
args,
|
|
3252
|
+
command
|
|
3253
|
+
};
|
|
3254
|
+
}
|
|
3255
|
+
provider = await deps.resolveProviderForCommand(provider, command);
|
|
3256
|
+
if (!provider) {
|
|
3257
|
+
showProviderRequiredError(command);
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
if (!provider) {
|
|
3261
|
+
throw new Error("Provider resolution failed");
|
|
3262
|
+
}
|
|
3263
|
+
return {
|
|
3264
|
+
mode: "provider-command",
|
|
3265
|
+
provider,
|
|
3266
|
+
args,
|
|
3267
|
+
command
|
|
3268
|
+
};
|
|
3269
|
+
}
|
|
3199
3270
|
async function syncSequenceActiveAccount(seq) {
|
|
3200
3271
|
const currentEmail = getCurrentAccount();
|
|
3201
3272
|
const resolvedActive = resolveManagedAccountNumberForEmail(seq, currentEmail);
|
|
@@ -3502,20 +3573,21 @@ async function cmdStatus(options) {
|
|
|
3502
3573
|
}
|
|
3503
3574
|
if (summary.email === "none") {
|
|
3504
3575
|
console.log("none");
|
|
3576
|
+
} else if (summary.alias) {
|
|
3577
|
+
console.log(`${summary.email} [${summary.alias}]`);
|
|
3505
3578
|
} else {
|
|
3506
|
-
if (summary.alias) {
|
|
3507
|
-
console.log(`${summary.email} [${summary.alias}]`);
|
|
3508
|
-
return;
|
|
3509
|
-
}
|
|
3510
3579
|
console.log(summary.email);
|
|
3511
3580
|
}
|
|
3581
|
+
console.log(`managed accounts: ${summary.managedCount}`);
|
|
3512
3582
|
}
|
|
3513
3583
|
async function getStatusSummaryForActiveProvider() {
|
|
3514
3584
|
const email = getCurrentAccount();
|
|
3515
3585
|
let alias = null;
|
|
3516
3586
|
let managed = false;
|
|
3587
|
+
let managedCount = 0;
|
|
3517
3588
|
if (email !== "none" && existsSync9(activeSequenceFile)) {
|
|
3518
3589
|
const seq = await loadSequence(activeSequenceFile);
|
|
3590
|
+
managedCount = Object.keys(seq.accounts).length;
|
|
3519
3591
|
for (const account of Object.values(seq.accounts)) {
|
|
3520
3592
|
if (account.email === email) {
|
|
3521
3593
|
managed = true;
|
|
@@ -3523,8 +3595,11 @@ async function getStatusSummaryForActiveProvider() {
|
|
|
3523
3595
|
break;
|
|
3524
3596
|
}
|
|
3525
3597
|
}
|
|
3598
|
+
} else if (existsSync9(activeSequenceFile)) {
|
|
3599
|
+
const seq = await loadSequence(activeSequenceFile);
|
|
3600
|
+
managedCount = Object.keys(seq.accounts).length;
|
|
3526
3601
|
}
|
|
3527
|
-
return { email, alias, managed };
|
|
3602
|
+
return { email, alias, managed, managedCount };
|
|
3528
3603
|
}
|
|
3529
3604
|
async function withActiveProvider(provider, fn) {
|
|
3530
3605
|
const previousProvider = activeProvider.name;
|
|
@@ -3574,13 +3649,12 @@ async function cmdStatusAllProviders() {
|
|
|
3574
3649
|
console.log(summary.heading);
|
|
3575
3650
|
if (summary.email === "none") {
|
|
3576
3651
|
console.log(" none");
|
|
3577
|
-
|
|
3578
|
-
}
|
|
3579
|
-
if (summary.alias) {
|
|
3652
|
+
} else if (summary.alias) {
|
|
3580
3653
|
console.log(` ${summary.email} [${summary.alias}]`);
|
|
3581
|
-
|
|
3654
|
+
} else {
|
|
3655
|
+
console.log(` ${summary.email}`);
|
|
3582
3656
|
}
|
|
3583
|
-
console.log(` ${summary.
|
|
3657
|
+
console.log(` managed accounts: ${summary.managedCount}`);
|
|
3584
3658
|
}
|
|
3585
3659
|
}
|
|
3586
3660
|
async function cmdAlias(alias, identifier) {
|
|
@@ -3652,7 +3726,10 @@ Usage:
|
|
|
3652
3726
|
Commands:
|
|
3653
3727
|
(no args) Interactive provider picker
|
|
3654
3728
|
list List managed accounts for all providers
|
|
3655
|
-
status Show current account for all providers
|
|
3729
|
+
status Show current active account for all providers
|
|
3730
|
+
add [--alias <name>] Pick provider, then add current account
|
|
3731
|
+
login [-- <args...>] Pick provider, then run provider login
|
|
3732
|
+
remove [<email>] Pick provider, then remove an account
|
|
3656
3733
|
<provider> Interactive account picker for provider
|
|
3657
3734
|
<provider> <alias> Switch by alias for provider
|
|
3658
3735
|
<provider> list List all managed accounts
|
|
@@ -3660,18 +3737,21 @@ Commands:
|
|
|
3660
3737
|
<provider> login [-- <args...>] Run provider login and register session
|
|
3661
3738
|
<provider> remove [<email>] Remove an account
|
|
3662
3739
|
<provider> next Rotate to next account
|
|
3663
|
-
<provider> status [--json] Show current account
|
|
3740
|
+
<provider> status [--json] Show current active account
|
|
3664
3741
|
<provider> alias <name> [<email>] Set alias for current or target account
|
|
3665
3742
|
help Show this help
|
|
3666
3743
|
|
|
3667
3744
|
Examples:
|
|
3668
3745
|
caflip Pick provider interactively
|
|
3669
3746
|
caflip list List managed accounts for Claude and Codex
|
|
3670
|
-
caflip status Show current account for Claude and Codex
|
|
3747
|
+
caflip status Show current active account for Claude and Codex
|
|
3748
|
+
caflip add Pick provider, then add current account
|
|
3749
|
+
caflip login Pick provider, then run provider login
|
|
3750
|
+
caflip remove Pick provider, then remove an account interactively
|
|
3671
3751
|
caflip claude Pick Claude account interactively
|
|
3672
3752
|
caflip claude work Switch Claude account by alias
|
|
3673
3753
|
caflip claude add --alias personal Add current Claude account with alias
|
|
3674
|
-
caflip claude login
|
|
3754
|
+
caflip claude login Run Claude login and register session
|
|
3675
3755
|
caflip claude login -- --email me@example.com --sso
|
|
3676
3756
|
Pass provider-specific flags after --
|
|
3677
3757
|
caflip claude status --json Show Claude status as JSON
|
|
@@ -3681,76 +3761,7 @@ Examples:
|
|
|
3681
3761
|
caflip codex alias work user@company.com
|
|
3682
3762
|
Set Codex alias for target email`);
|
|
3683
3763
|
}
|
|
3684
|
-
async function
|
|
3685
|
-
const parsed = parseProviderArgs(process.argv.slice(2));
|
|
3686
|
-
const provider = parsed.provider;
|
|
3687
|
-
const args = parsed.commandArgs;
|
|
3688
|
-
const command = args[0];
|
|
3689
|
-
let lockHeld = false;
|
|
3690
|
-
const runWithLock = async (fn) => {
|
|
3691
|
-
setupDirectories();
|
|
3692
|
-
acquireLock(activeLockDir);
|
|
3693
|
-
lockHeld = true;
|
|
3694
|
-
try {
|
|
3695
|
-
return await fn();
|
|
3696
|
-
} finally {
|
|
3697
|
-
if (lockHeld) {
|
|
3698
|
-
releaseLock(activeLockDir);
|
|
3699
|
-
lockHeld = false;
|
|
3700
|
-
}
|
|
3701
|
-
}
|
|
3702
|
-
};
|
|
3703
|
-
const runWithProviderLock = async (targetProvider, fn) => {
|
|
3704
|
-
setActiveProvider(targetProvider);
|
|
3705
|
-
return await runWithLock(fn);
|
|
3706
|
-
};
|
|
3707
|
-
const isHelpCommand = command === "help" || command === "--help" || command === "-h";
|
|
3708
|
-
const isProviderOptionalReadOnlyCommand = command === "list" || command === "status";
|
|
3709
|
-
if (!parsed.isProviderQualified && command && !isHelpCommand && !isProviderOptionalReadOnlyCommand) {
|
|
3710
|
-
if (RESERVED_COMMANDS.includes(command)) {
|
|
3711
|
-
showProviderRequiredError(command);
|
|
3712
|
-
} else {
|
|
3713
|
-
console.error("Error: Alias requires provider prefix.");
|
|
3714
|
-
console.error("Try: caflip claude <alias> or caflip codex <alias>");
|
|
3715
|
-
process.exit(2);
|
|
3716
|
-
}
|
|
3717
|
-
}
|
|
3718
|
-
if (!command) {
|
|
3719
|
-
if (!provider) {
|
|
3720
|
-
const defaultProvider = readCliMeta().lastProvider;
|
|
3721
|
-
const selectedProvider = await pickProvider(defaultProvider);
|
|
3722
|
-
await writeLastProvider(selectedProvider);
|
|
3723
|
-
await runWithProviderLock(selectedProvider, async () => {
|
|
3724
|
-
await cmdInteractiveSwitch();
|
|
3725
|
-
});
|
|
3726
|
-
return;
|
|
3727
|
-
}
|
|
3728
|
-
await runWithProviderLock(provider, async () => {
|
|
3729
|
-
await cmdInteractiveSwitch();
|
|
3730
|
-
});
|
|
3731
|
-
return;
|
|
3732
|
-
}
|
|
3733
|
-
if (isHelpCommand) {
|
|
3734
|
-
showHelp();
|
|
3735
|
-
return;
|
|
3736
|
-
}
|
|
3737
|
-
if (!provider) {
|
|
3738
|
-
if (command === "list") {
|
|
3739
|
-
await cmdListAllProviders();
|
|
3740
|
-
return;
|
|
3741
|
-
}
|
|
3742
|
-
if (command === "status") {
|
|
3743
|
-
if (args.includes("--json")) {
|
|
3744
|
-
console.error("Error: Provider is required for status --json.");
|
|
3745
|
-
console.error("Try: caflip claude status --json");
|
|
3746
|
-
process.exit(2);
|
|
3747
|
-
}
|
|
3748
|
-
await cmdStatusAllProviders();
|
|
3749
|
-
return;
|
|
3750
|
-
}
|
|
3751
|
-
showProviderRequiredError(command);
|
|
3752
|
-
}
|
|
3753
|
-
setActiveProvider(provider);
|
|
3764
|
+
async function executeProviderCommand(command, args, provider, runWithLock) {
|
|
3754
3765
|
switch (command) {
|
|
3755
3766
|
case "list":
|
|
3756
3767
|
await cmdList();
|
|
@@ -3815,6 +3826,62 @@ async function main() {
|
|
|
3815
3826
|
}
|
|
3816
3827
|
}
|
|
3817
3828
|
}
|
|
3829
|
+
async function main() {
|
|
3830
|
+
const context = await resolveCliContext(parseProviderArgs(process.argv.slice(2)));
|
|
3831
|
+
let lockHeld = false;
|
|
3832
|
+
const runWithLock = async (fn) => {
|
|
3833
|
+
setupDirectories();
|
|
3834
|
+
acquireLock(activeLockDir);
|
|
3835
|
+
lockHeld = true;
|
|
3836
|
+
try {
|
|
3837
|
+
return await fn();
|
|
3838
|
+
} finally {
|
|
3839
|
+
if (lockHeld) {
|
|
3840
|
+
releaseLock(activeLockDir);
|
|
3841
|
+
lockHeld = false;
|
|
3842
|
+
}
|
|
3843
|
+
}
|
|
3844
|
+
};
|
|
3845
|
+
const runWithProviderLock = async (targetProvider, fn) => {
|
|
3846
|
+
setActiveProvider(targetProvider);
|
|
3847
|
+
return await runWithLock(fn);
|
|
3848
|
+
};
|
|
3849
|
+
if (context.mode === "interactive-switch") {
|
|
3850
|
+
if (!context.provider) {
|
|
3851
|
+
const defaultProvider = readCliMeta().lastProvider;
|
|
3852
|
+
const selectedProvider = await pickProvider(defaultProvider);
|
|
3853
|
+
await writeLastProvider(selectedProvider);
|
|
3854
|
+
await runWithProviderLock(selectedProvider, async () => {
|
|
3855
|
+
await cmdInteractiveSwitch();
|
|
3856
|
+
});
|
|
3857
|
+
return;
|
|
3858
|
+
}
|
|
3859
|
+
await runWithProviderLock(context.provider, async () => {
|
|
3860
|
+
await cmdInteractiveSwitch();
|
|
3861
|
+
});
|
|
3862
|
+
return;
|
|
3863
|
+
}
|
|
3864
|
+
if (context.mode === "all-providers") {
|
|
3865
|
+
if (context.command === "list") {
|
|
3866
|
+
await cmdListAllProviders();
|
|
3867
|
+
return;
|
|
3868
|
+
}
|
|
3869
|
+
if (context.command === "status") {
|
|
3870
|
+
if (context.args.includes("--json")) {
|
|
3871
|
+
console.error("Error: Provider is required for status --json.");
|
|
3872
|
+
console.error("Try: caflip claude status --json");
|
|
3873
|
+
process.exit(2);
|
|
3874
|
+
}
|
|
3875
|
+
await cmdStatusAllProviders();
|
|
3876
|
+
return;
|
|
3877
|
+
}
|
|
3878
|
+
}
|
|
3879
|
+
const provider = context.provider;
|
|
3880
|
+
const args = context.args;
|
|
3881
|
+
const command = context.command;
|
|
3882
|
+
setActiveProvider(provider);
|
|
3883
|
+
await executeProviderCommand(command, args, provider, runWithLock);
|
|
3884
|
+
}
|
|
3818
3885
|
if (__require.main == __require.module) {
|
|
3819
3886
|
main().catch((err) => {
|
|
3820
3887
|
if (err instanceof PromptCancelledError) {
|
|
@@ -3826,5 +3893,8 @@ if (__require.main == __require.module) {
|
|
|
3826
3893
|
});
|
|
3827
3894
|
}
|
|
3828
3895
|
export {
|
|
3896
|
+
supportsInteractiveProviderSelection,
|
|
3897
|
+
resolveProviderForCommand,
|
|
3898
|
+
resolveCliContext,
|
|
3829
3899
|
performSwitch
|
|
3830
3900
|
};
|