caflip 0.3.0 → 0.3.1

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.
Files changed (3) hide show
  1. package/README.md +12 -1
  2. package/dist/cli.js +139 -72
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -67,6 +67,11 @@ bun run dev -- help
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
 
@@ -106,6 +111,9 @@ After switching, restart the target CLI (Claude Code or Codex) to pick up new au
106
111
  | `caflip` | Interactive provider picker (Claude/Codex) |
107
112
  | `caflip list` | List managed accounts for Claude and Codex |
108
113
  | `caflip status` | Show current 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 |
@@ -132,11 +140,14 @@ caflip claude alias work hi.lucienlee@gmail.com
132
140
  caflip codex alias work me@company.com
133
141
  ```
134
142
 
135
- `remove` target accepts email only. Omit it to choose from the interactive picker.
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
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.0",
620
+ version: "0.3.1",
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);
@@ -3653,6 +3724,9 @@ Commands:
3653
3724
  (no args) Interactive provider picker
3654
3725
  list List managed accounts for all providers
3655
3726
  status Show current account for all providers
3727
+ add [--alias <name>] Pick provider, then add current account
3728
+ login [-- <args...>] Pick provider, then run provider login
3729
+ remove [<email>] Pick provider, then remove an account
3656
3730
  <provider> Interactive account picker for provider
3657
3731
  <provider> <alias> Switch by alias for provider
3658
3732
  <provider> list List all managed accounts
@@ -3668,10 +3742,13 @@ Examples:
3668
3742
  caflip Pick provider interactively
3669
3743
  caflip list List managed accounts for Claude and Codex
3670
3744
  caflip status Show current account for Claude and Codex
3745
+ caflip add Pick provider, then add current account
3746
+ caflip login Pick provider, then run provider login
3747
+ caflip remove Pick provider, then remove an account interactively
3671
3748
  caflip claude Pick Claude account interactively
3672
3749
  caflip claude work Switch Claude account by alias
3673
3750
  caflip claude add --alias personal Add current Claude account with alias
3674
- caflip claude login Run Claude login and register session
3751
+ caflip claude login Run Claude login and register session
3675
3752
  caflip claude login -- --email me@example.com --sso
3676
3753
  Pass provider-specific flags after --
3677
3754
  caflip claude status --json Show Claude status as JSON
@@ -3681,76 +3758,7 @@ Examples:
3681
3758
  caflip codex alias work user@company.com
3682
3759
  Set Codex alias for target email`);
3683
3760
  }
3684
- async function main() {
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);
3761
+ async function executeProviderCommand(command, args, provider, runWithLock) {
3754
3762
  switch (command) {
3755
3763
  case "list":
3756
3764
  await cmdList();
@@ -3815,6 +3823,62 @@ async function main() {
3815
3823
  }
3816
3824
  }
3817
3825
  }
3826
+ async function main() {
3827
+ const context = await resolveCliContext(parseProviderArgs(process.argv.slice(2)));
3828
+ let lockHeld = false;
3829
+ const runWithLock = async (fn) => {
3830
+ setupDirectories();
3831
+ acquireLock(activeLockDir);
3832
+ lockHeld = true;
3833
+ try {
3834
+ return await fn();
3835
+ } finally {
3836
+ if (lockHeld) {
3837
+ releaseLock(activeLockDir);
3838
+ lockHeld = false;
3839
+ }
3840
+ }
3841
+ };
3842
+ const runWithProviderLock = async (targetProvider, fn) => {
3843
+ setActiveProvider(targetProvider);
3844
+ return await runWithLock(fn);
3845
+ };
3846
+ if (context.mode === "interactive-switch") {
3847
+ if (!context.provider) {
3848
+ const defaultProvider = readCliMeta().lastProvider;
3849
+ const selectedProvider = await pickProvider(defaultProvider);
3850
+ await writeLastProvider(selectedProvider);
3851
+ await runWithProviderLock(selectedProvider, async () => {
3852
+ await cmdInteractiveSwitch();
3853
+ });
3854
+ return;
3855
+ }
3856
+ await runWithProviderLock(context.provider, async () => {
3857
+ await cmdInteractiveSwitch();
3858
+ });
3859
+ return;
3860
+ }
3861
+ if (context.mode === "all-providers") {
3862
+ if (context.command === "list") {
3863
+ await cmdListAllProviders();
3864
+ return;
3865
+ }
3866
+ if (context.command === "status") {
3867
+ if (context.args.includes("--json")) {
3868
+ console.error("Error: Provider is required for status --json.");
3869
+ console.error("Try: caflip claude status --json");
3870
+ process.exit(2);
3871
+ }
3872
+ await cmdStatusAllProviders();
3873
+ return;
3874
+ }
3875
+ }
3876
+ const provider = context.provider;
3877
+ const args = context.args;
3878
+ const command = context.command;
3879
+ setActiveProvider(provider);
3880
+ await executeProviderCommand(command, args, provider, runWithLock);
3881
+ }
3818
3882
  if (__require.main == __require.module) {
3819
3883
  main().catch((err) => {
3820
3884
  if (err instanceof PromptCancelledError) {
@@ -3826,5 +3890,8 @@ if (__require.main == __require.module) {
3826
3890
  });
3827
3891
  }
3828
3892
  export {
3893
+ supportsInteractiveProviderSelection,
3894
+ resolveProviderForCommand,
3895
+ resolveCliContext,
3829
3896
  performSwitch
3830
3897
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "caflip",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "caflip": "bin/caflip"