dual-brain 7.1.9 → 7.1.11
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/bin/dual-brain.mjs +219 -113
- package/package.json +1 -1
package/bin/dual-brain.mjs
CHANGED
|
@@ -126,7 +126,7 @@ function printSubscriptionTable(auth, profile) {
|
|
|
126
126
|
|
|
127
127
|
const claudeLine1 = auth.claude.found
|
|
128
128
|
? ` Claude: logged in (${auth.claude.source})`
|
|
129
|
-
: ` Claude: not logged in — run: claude login`;
|
|
129
|
+
: ` Claude: not logged in — run: claude auth login`;
|
|
130
130
|
const claudeLine2 = ` plan: ${claudePlanLabel}${claudeLabel}`;
|
|
131
131
|
|
|
132
132
|
const openaiLine1 = auth.openai.found
|
|
@@ -156,7 +156,7 @@ async function cmdInit(rl) {
|
|
|
156
156
|
const noneFound = !auth.claude.found && !auth.openai.found;
|
|
157
157
|
if (noneFound) {
|
|
158
158
|
console.log('\nNo AI provider found. Log in first:');
|
|
159
|
-
console.log(' Claude: claude login');
|
|
159
|
+
console.log(' Claude: claude auth login');
|
|
160
160
|
console.log(' OpenAI: codex login\n');
|
|
161
161
|
console.log('Then re-run: dual-brain init');
|
|
162
162
|
return;
|
|
@@ -189,7 +189,7 @@ async function cmdAuth(subArgs = []) {
|
|
|
189
189
|
|
|
190
190
|
if (!auth.claude.found || !auth.openai.found) {
|
|
191
191
|
console.log('');
|
|
192
|
-
if (!auth.claude.found) console.log(' Claude not logged in. Run: claude login');
|
|
192
|
+
if (!auth.claude.found) console.log(' Claude not logged in. Run: claude auth login');
|
|
193
193
|
if (!auth.openai.found) console.log(' OpenAI not logged in. Run: codex login');
|
|
194
194
|
}
|
|
195
195
|
}
|
|
@@ -525,6 +525,35 @@ function cmdForget(text) {
|
|
|
525
525
|
|
|
526
526
|
// ─── Screen helpers ───────────────────────────────────────────────────────────
|
|
527
527
|
|
|
528
|
+
/**
|
|
529
|
+
* Render the data-tools-style rounded header box for the main screen.
|
|
530
|
+
* Inner width is 39 chars. Lines are padded with spaces to fill the box.
|
|
531
|
+
*/
|
|
532
|
+
function renderHeader(version, providerLines) {
|
|
533
|
+
const W = 39; // inner width
|
|
534
|
+
const pad = (s) => {
|
|
535
|
+
// Strip ANSI codes for length calculation
|
|
536
|
+
const visible = s.replace(/\x1b\[[0-9;]*m/g, '');
|
|
537
|
+
return s + ' '.repeat(Math.max(0, W - visible.length));
|
|
538
|
+
};
|
|
539
|
+
const top = ` ┌${'─'.repeat(W)}┐`;
|
|
540
|
+
const sep = ` ├${'─'.repeat(W)}┤`;
|
|
541
|
+
const bottom = ` └${'─'.repeat(W)}┘`;
|
|
542
|
+
|
|
543
|
+
const title = `DATA Tools - Dual Brain v${version}`;
|
|
544
|
+
const credit = `by Steve Moraco + dual-brain`;
|
|
545
|
+
|
|
546
|
+
const lines = [top];
|
|
547
|
+
lines.push(` │ ${pad(title)}│`);
|
|
548
|
+
lines.push(` │ ${pad(credit)}│`);
|
|
549
|
+
lines.push(sep);
|
|
550
|
+
for (const pl of providerLines) {
|
|
551
|
+
lines.push(` │ ${pad(pl)}│`);
|
|
552
|
+
}
|
|
553
|
+
lines.push(bottom);
|
|
554
|
+
return lines.join('\n');
|
|
555
|
+
}
|
|
556
|
+
|
|
528
557
|
function profileExists(cwd) {
|
|
529
558
|
const dir = cwd || process.cwd();
|
|
530
559
|
const globalPath = join(process.env.HOME || '/root', '.config', 'dual-brain', 'profile.json');
|
|
@@ -609,8 +638,8 @@ async function welcomeScreen(rl, ask) {
|
|
|
609
638
|
|
|
610
639
|
if (!claudeReady && !openaiReady) {
|
|
611
640
|
console.log('No CLI login found. Log in first:');
|
|
612
|
-
console.log(' claude login — for Claude');
|
|
613
|
-
console.log(' codex login
|
|
641
|
+
console.log(' claude auth login — for Claude');
|
|
642
|
+
console.log(' codex login — for OpenAI/Codex\n');
|
|
614
643
|
console.log('Then re-run: dual-brain init');
|
|
615
644
|
return { next: 'exit' };
|
|
616
645
|
}
|
|
@@ -777,7 +806,13 @@ async function mainScreen(rl, ask) {
|
|
|
777
806
|
|
|
778
807
|
function subLine(name, plan, found, expired, days, sub) {
|
|
779
808
|
const label = sub?.label ? ` [${sub.label}]` : '';
|
|
780
|
-
if (!found) return `⚠️ ${name}: not logged in — run: ${name === 'Claude' ? 'claude login' : 'codex login'}`;
|
|
809
|
+
if (!found) return `⚠️ ${name}: not logged in — run: ${name === 'Claude' ? 'claude auth login' : 'codex login'}`;
|
|
810
|
+
// Multi-sub: show aggregated counts when more than one sub exists
|
|
811
|
+
const subs = sub?.subs;
|
|
812
|
+
if (subs && subs.length > 1) {
|
|
813
|
+
const aggregate = aggregatePlans(subs);
|
|
814
|
+
return `✅ ${name}: ${aggregate} [${subs.length} subs]`;
|
|
815
|
+
}
|
|
781
816
|
if (expired) return `🔴 ${name}: ${plan} expired${label} — will re-auth`;
|
|
782
817
|
const daysNote = (days !== null && days <= 7) ? ` (${days}d left)` : '';
|
|
783
818
|
return `✅ ${name}: ${plan}${label}${daysNote}`;
|
|
@@ -789,7 +824,7 @@ async function mainScreen(rl, ask) {
|
|
|
789
824
|
];
|
|
790
825
|
|
|
791
826
|
console.log('');
|
|
792
|
-
console.log(
|
|
827
|
+
console.log(renderHeader(version, headerLines));
|
|
793
828
|
|
|
794
829
|
// Auto-refresh expired subscriptions
|
|
795
830
|
if (claudeExpired || openaiExpired) {
|
|
@@ -799,7 +834,7 @@ async function mainScreen(rl, ask) {
|
|
|
799
834
|
if (openaiExpired) expired.push('OpenAI');
|
|
800
835
|
console.log(`\n ${expired.join(' & ')} subscription expired. Re-authenticating...`);
|
|
801
836
|
if (claudeExpired) {
|
|
802
|
-
const r = spawnSync('claude', ['login'], { stdio: 'inherit', timeout: 30000 });
|
|
837
|
+
const r = spawnSync('claude', ['auth', 'login'], { stdio: 'inherit', timeout: 30000 });
|
|
803
838
|
if (r.status === 0) {
|
|
804
839
|
claudeSub.expiresAt = null;
|
|
805
840
|
saveProfile(profile, { cwd });
|
|
@@ -820,7 +855,7 @@ async function mainScreen(rl, ask) {
|
|
|
820
855
|
const recentSessions = enrichSessions(importReplitSessions(cwd), cwd).slice(0, 7);
|
|
821
856
|
|
|
822
857
|
if (recentSessions.length > 0) {
|
|
823
|
-
console.log(
|
|
858
|
+
console.log(' Recent Sessions:');
|
|
824
859
|
recentSessions.forEach((sess, i) => {
|
|
825
860
|
const pin = sess.pinned ? '📌 ' : ' ';
|
|
826
861
|
const active = sess.isActive ? ' ●' : '';
|
|
@@ -830,19 +865,17 @@ async function mainScreen(rl, ask) {
|
|
|
830
865
|
console.log('');
|
|
831
866
|
}
|
|
832
867
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
menuOpts.push({ key: 'n', label: 'New session', section: 'Sessions' });
|
|
868
|
+
console.log(' [c] Continue last session');
|
|
869
|
+
console.log(' [n] New session');
|
|
836
870
|
if (recentSessions.length > 0) {
|
|
837
|
-
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
console.log(menu(menuOpts));
|
|
871
|
+
console.log(' [1-9] Resume numbered above');
|
|
872
|
+
}
|
|
873
|
+
console.log(' [e] Manage sessions');
|
|
874
|
+
console.log(' [i] Import from replit-tools');
|
|
875
|
+
console.log(' [m] Manage subscriptions');
|
|
876
|
+
console.log(' [d] Switch to data-tools');
|
|
877
|
+
console.log(' [s] Settings');
|
|
878
|
+
console.log(' [q] Exit');
|
|
846
879
|
console.log('');
|
|
847
880
|
|
|
848
881
|
const choice = (await ask(' Choice: ')).trim().toLowerCase();
|
|
@@ -873,6 +906,18 @@ async function mainScreen(rl, ask) {
|
|
|
873
906
|
|
|
874
907
|
if (choice === 'e') { return { next: 'sessions' }; }
|
|
875
908
|
|
|
909
|
+
if (choice === 'i') {
|
|
910
|
+
const sessions = importReplitSessions(cwd);
|
|
911
|
+
if (sessions.length === 0) {
|
|
912
|
+
console.log('\n No replit-tools sessions found to import.\n');
|
|
913
|
+
} else {
|
|
914
|
+
console.log(`\n ✅ Found ${sessions.length} sessions from replit-tools.`);
|
|
915
|
+
console.log(' Sessions are automatically available in the list above.\n');
|
|
916
|
+
}
|
|
917
|
+
await ask(' Press Enter to continue...');
|
|
918
|
+
return { next: 'main' };
|
|
919
|
+
}
|
|
920
|
+
|
|
876
921
|
if (choice === 'd') {
|
|
877
922
|
const { spawnSync } = await import('node:child_process');
|
|
878
923
|
const which = spawnSync('which', ['claude-menu'], { encoding: 'utf8' });
|
|
@@ -885,17 +930,7 @@ async function mainScreen(rl, ask) {
|
|
|
885
930
|
return { next: 'main' };
|
|
886
931
|
}
|
|
887
932
|
|
|
888
|
-
if (choice === '
|
|
889
|
-
const { spawnSync } = await import('node:child_process');
|
|
890
|
-
spawnSync('claude', ['login'], { stdio: 'inherit' });
|
|
891
|
-
return { next: 'main' };
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
if (choice === 'k') {
|
|
895
|
-
const { spawnSync } = await import('node:child_process');
|
|
896
|
-
spawnSync('codex', ['login'], { stdio: 'inherit' });
|
|
897
|
-
return { next: 'main' };
|
|
898
|
-
}
|
|
933
|
+
if (choice === 'm') { return { next: 'subscriptions' }; }
|
|
899
934
|
|
|
900
935
|
if (choice === 's') { return { next: 'settings' }; }
|
|
901
936
|
if (choice === 'q' || choice === 'exit') { return { next: 'exit' }; }
|
|
@@ -1011,119 +1046,190 @@ async function settingsScreen(rl, ask) {
|
|
|
1011
1046
|
return { next: 'settings' };
|
|
1012
1047
|
}
|
|
1013
1048
|
|
|
1049
|
+
// ─── Helper: aggregatePlans ───────────────────────────────────────────────────
|
|
1050
|
+
|
|
1051
|
+
const PLAN_PRICES = {
|
|
1052
|
+
pro: '$20', max5: '$100', max20: '$200',
|
|
1053
|
+
plus: '$20', pro100: '$100', pro200: '$200',
|
|
1054
|
+
};
|
|
1055
|
+
|
|
1056
|
+
function aggregatePlans(subs) {
|
|
1057
|
+
if (!subs || subs.length === 0) return '';
|
|
1058
|
+
const counts = {};
|
|
1059
|
+
for (const s of subs) {
|
|
1060
|
+
const price = PLAN_PRICES[s.plan] || s.plan;
|
|
1061
|
+
counts[price] = (counts[price] || 0) + 1;
|
|
1062
|
+
}
|
|
1063
|
+
return Object.entries(counts)
|
|
1064
|
+
.sort((a, b) => parseInt(b[0].slice(1)) - parseInt(a[0].slice(1)))
|
|
1065
|
+
.map(([price, count]) => `${price}×${count}`)
|
|
1066
|
+
.join(' ');
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1014
1069
|
// ─── Screen: subscriptionsScreen ─────────────────────────────────────────────
|
|
1015
1070
|
|
|
1016
1071
|
async function subscriptionsScreen(rl, ask) {
|
|
1072
|
+
console.clear();
|
|
1017
1073
|
const cwd = process.cwd();
|
|
1018
1074
|
const profile = loadProfile(cwd);
|
|
1019
1075
|
const auth = await detectAuth();
|
|
1020
|
-
const plans = detectPlans();
|
|
1021
1076
|
|
|
1022
|
-
|
|
1023
|
-
const
|
|
1077
|
+
// Backward compat: migrate old single-sub format to subs array
|
|
1078
|
+
for (const prov of ['claude', 'openai']) {
|
|
1079
|
+
const p = profile?.providers?.[prov];
|
|
1080
|
+
if (p && !p.subs && p.plan) {
|
|
1081
|
+
p.subs = [{ plan: p.plan, label: p.label || null, expiresAt: p.expiresAt || null }];
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1024
1084
|
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
: 'disabled';
|
|
1028
|
-
const openaiPlanLabel = openaiSub?.enabled
|
|
1029
|
-
? (OPENAI_PLAN_LABELS[openaiSub.plan] ?? openaiSub.plan ?? 'n/a')
|
|
1030
|
-
: 'disabled';
|
|
1085
|
+
// Build status lines — roster format
|
|
1086
|
+
const lines = [];
|
|
1031
1087
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1088
|
+
function buildProviderLines(provKey, displayName, authFound) {
|
|
1089
|
+
const sub = profile?.providers?.[provKey];
|
|
1090
|
+
const subs = sub?.subs || [];
|
|
1091
|
+
if (!authFound && subs.length === 0) {
|
|
1092
|
+
lines.push(` ⚠️ ${displayName}: not linked`);
|
|
1093
|
+
return;
|
|
1094
|
+
}
|
|
1095
|
+
const aggregate = aggregatePlans(subs);
|
|
1096
|
+
const prefix = authFound ? '✅' : '⚠️ ';
|
|
1097
|
+
lines.push(` ${prefix} ${displayName}:${aggregate ? ' ' + aggregate : ' (no subs)'}`);
|
|
1098
|
+
subs.forEach((s, i) => {
|
|
1099
|
+
const planLabels = provKey === 'claude' ? CLAUDE_PLAN_LABELS : OPENAI_PLAN_LABELS;
|
|
1100
|
+
const planLabel = planLabels[s.plan] ?? s.plan ?? 'unknown';
|
|
1101
|
+
const nameStr = (s.label || '(no label)').padEnd(22);
|
|
1102
|
+
const d = s.expiresAt ? daysUntil(s.expiresAt) : null;
|
|
1103
|
+
const expiry = d === null ? '' : d < 0 ? ' (expired)' : d === 0 ? ' (today)' : ` (${d}d left)`;
|
|
1104
|
+
lines.push(` ${i + 1}. ${nameStr} ${planLabel}${expiry}`);
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1041
1107
|
|
|
1108
|
+
buildProviderLines('claude', 'Claude', auth.claude.found);
|
|
1109
|
+
lines.push('');
|
|
1110
|
+
buildProviderLines('openai', 'OpenAI', auth.openai.found);
|
|
1111
|
+
|
|
1112
|
+
console.log(box('Subscriptions', lines));
|
|
1042
1113
|
console.log('');
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
{ key: '
|
|
1047
|
-
{ key: '
|
|
1048
|
-
{ key: '
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
]));
|
|
1114
|
+
|
|
1115
|
+
const menuOpts = [
|
|
1116
|
+
{ key: '1', label: 'Add Claude sub', section: 'Link' },
|
|
1117
|
+
{ key: '2', label: 'Add Codex sub', section: 'Link' },
|
|
1118
|
+
{ key: 'r', label: 'Remove a sub', section: 'Link' },
|
|
1119
|
+
{ key: 'b', label: 'Back to home', section: '' },
|
|
1120
|
+
];
|
|
1121
|
+
console.log(menu(menuOpts));
|
|
1052
1122
|
console.log('');
|
|
1053
1123
|
|
|
1054
1124
|
const choice = (await ask(' Choice: ')).trim().toLowerCase();
|
|
1055
1125
|
|
|
1056
|
-
if (choice === '
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
if (choice === 'c') {
|
|
1071
|
-
console.log('');
|
|
1072
|
-
console.log(' Claude plan:');
|
|
1073
|
-
console.log(' (1) Pro ($20/mo)');
|
|
1074
|
-
console.log(' (2) Max x5 ($100/mo)');
|
|
1075
|
-
console.log(' (3) Max x20 ($200/mo)');
|
|
1076
|
-
const c = (await ask(' > ')).trim();
|
|
1077
|
-
const planMap = { '1': 'pro', '2': 'max5', '3': 'max20' };
|
|
1078
|
-
if (planMap[c]) {
|
|
1126
|
+
if (choice === '1') {
|
|
1127
|
+
console.log('\n Linking Claude subscription...');
|
|
1128
|
+
console.log(' A browser window will open — paste the code below when prompted.\n');
|
|
1129
|
+
const { spawnSync } = await import('node:child_process');
|
|
1130
|
+
const r = spawnSync('claude', ['auth', 'login'], { stdio: 'inherit', timeout: 60000 });
|
|
1131
|
+
if (r.status === 0) {
|
|
1132
|
+
console.log('\n ✅ Claude linked successfully!\n');
|
|
1133
|
+
const label = (await ask(" Label (e.g. \"Josh's $100 sub\", or Enter to skip): ")).trim();
|
|
1134
|
+
const expiry = await askExpiry(ask, 'Claude');
|
|
1135
|
+
const newPlans = detectPlans();
|
|
1136
|
+
const plan = newPlans.claude?.plan || 'pro';
|
|
1137
|
+
if (!profile.providers) profile.providers = {};
|
|
1079
1138
|
if (!profile.providers.claude) profile.providers.claude = { enabled: true };
|
|
1080
|
-
profile.providers.claude.plan =
|
|
1139
|
+
profile.providers.claude.plan = plan;
|
|
1081
1140
|
profile.providers.claude.enabled = true;
|
|
1141
|
+
// Push to subs array instead of overwriting
|
|
1142
|
+
if (!profile.providers.claude.subs) profile.providers.claude.subs = [];
|
|
1143
|
+
profile.providers.claude.subs.push({ plan, label: label || null, expiresAt: expiry || null });
|
|
1082
1144
|
saveProfile(profile, { cwd });
|
|
1083
|
-
console.log(
|
|
1145
|
+
console.log(' ✓ Saved\n');
|
|
1146
|
+
await ask(' Press Enter to continue...');
|
|
1147
|
+
} else {
|
|
1148
|
+
console.log('\n ❌ Claude login failed or was cancelled.\n');
|
|
1149
|
+
await ask(' Press Enter to continue...');
|
|
1084
1150
|
}
|
|
1085
1151
|
return { next: 'subscriptions' };
|
|
1086
1152
|
}
|
|
1087
1153
|
|
|
1088
|
-
if (choice === '
|
|
1089
|
-
console.log('');
|
|
1090
|
-
console.log('
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1154
|
+
if (choice === '2') {
|
|
1155
|
+
console.log('\n Linking Codex subscription...');
|
|
1156
|
+
console.log(' A browser window will open — paste the code below when prompted.\n');
|
|
1157
|
+
const { spawnSync } = await import('node:child_process');
|
|
1158
|
+
const r = spawnSync('codex', ['login'], { stdio: 'inherit', timeout: 60000 });
|
|
1159
|
+
if (r.status === 0) {
|
|
1160
|
+
console.log('\n ✅ Codex linked successfully!\n');
|
|
1161
|
+
const label = (await ask(' Label (e.g. "Team Codex Pro", or Enter to skip): ')).trim();
|
|
1162
|
+
const expiry = await askExpiry(ask, 'Codex');
|
|
1163
|
+
const newPlans = detectPlans();
|
|
1164
|
+
const plan = newPlans.openai?.plan || 'plus';
|
|
1165
|
+
if (!profile.providers) profile.providers = {};
|
|
1097
1166
|
if (!profile.providers.openai) profile.providers.openai = { enabled: true };
|
|
1098
|
-
profile.providers.openai.plan =
|
|
1167
|
+
profile.providers.openai.plan = plan;
|
|
1099
1168
|
profile.providers.openai.enabled = true;
|
|
1169
|
+
// Push to subs array instead of overwriting
|
|
1170
|
+
if (!profile.providers.openai.subs) profile.providers.openai.subs = [];
|
|
1171
|
+
profile.providers.openai.subs.push({ plan, label: label || null, expiresAt: expiry || null });
|
|
1100
1172
|
saveProfile(profile, { cwd });
|
|
1101
|
-
console.log(
|
|
1173
|
+
console.log(' ✓ Saved\n');
|
|
1174
|
+
await ask(' Press Enter to continue...');
|
|
1175
|
+
} else {
|
|
1176
|
+
console.log('\n ❌ Codex login failed or was cancelled.\n');
|
|
1177
|
+
await ask(' Press Enter to continue...');
|
|
1102
1178
|
}
|
|
1103
1179
|
return { next: 'subscriptions' };
|
|
1104
1180
|
}
|
|
1105
1181
|
|
|
1106
|
-
if (choice === '
|
|
1107
|
-
//
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
const
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
if (label === '-') { delete prov.label; }
|
|
1115
|
-
else if (label) { prov.label = label; }
|
|
1116
|
-
const expiry = await askExpiry(ask, provLabel);
|
|
1117
|
-
if (expiry) { prov.expiresAt = expiry; }
|
|
1182
|
+
if (choice === 'r') {
|
|
1183
|
+
// Build a flat numbered list of all subs across both providers
|
|
1184
|
+
const allSubs = [];
|
|
1185
|
+
for (const [provKey, displayName] of [['claude', 'Claude'], ['openai', 'OpenAI']]) {
|
|
1186
|
+
const subs = profile?.providers?.[provKey]?.subs || [];
|
|
1187
|
+
for (const s of subs) {
|
|
1188
|
+
allSubs.push({ provKey, displayName, sub: s });
|
|
1189
|
+
}
|
|
1118
1190
|
}
|
|
1119
|
-
|
|
1120
|
-
|
|
1191
|
+
|
|
1192
|
+
if (allSubs.length === 0) {
|
|
1193
|
+
console.log('\n No subscriptions to remove.\n');
|
|
1194
|
+
await ask(' Press Enter to continue...');
|
|
1195
|
+
return { next: 'subscriptions' };
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
console.log('\n Remove a subscription:\n');
|
|
1199
|
+
allSubs.forEach(({ displayName, sub }, i) => {
|
|
1200
|
+
const planLabels = displayName === 'Claude' ? CLAUDE_PLAN_LABELS : OPENAI_PLAN_LABELS;
|
|
1201
|
+
const planLabel = planLabels[sub.plan] ?? sub.plan ?? 'unknown';
|
|
1202
|
+
const labelStr = sub.label ? ` [${sub.label}]` : '';
|
|
1203
|
+
console.log(` (${i + 1}) ${displayName}: ${planLabel}${labelStr}`);
|
|
1204
|
+
});
|
|
1205
|
+
console.log(' (Enter) Cancel\n');
|
|
1206
|
+
|
|
1207
|
+
const numStr = (await ask(' Remove #: ')).trim();
|
|
1208
|
+
const numChoice = parseInt(numStr, 10);
|
|
1209
|
+
if (!isNaN(numChoice) && numChoice >= 1 && numChoice <= allSubs.length) {
|
|
1210
|
+
const { provKey, sub } = allSubs[numChoice - 1];
|
|
1211
|
+
const confirm = (await ask(` Remove "${sub.label || sub.plan}" from ${provKey}? (y/N): `)).trim().toLowerCase();
|
|
1212
|
+
if (confirm === 'y') {
|
|
1213
|
+
const subs = profile.providers[provKey].subs;
|
|
1214
|
+
const idx = subs.indexOf(sub);
|
|
1215
|
+
if (idx !== -1) subs.splice(idx, 1);
|
|
1216
|
+
// Update top-level plan to first remaining sub (or keep as-is)
|
|
1217
|
+
if (subs.length > 0) {
|
|
1218
|
+
profile.providers[provKey].plan = subs[0].plan;
|
|
1219
|
+
}
|
|
1220
|
+
saveProfile(profile, { cwd });
|
|
1221
|
+
console.log(' ✓ Removed\n');
|
|
1222
|
+
} else {
|
|
1223
|
+
console.log(' Cancelled.\n');
|
|
1224
|
+
}
|
|
1225
|
+
} else {
|
|
1226
|
+
console.log(' Cancelled.\n');
|
|
1227
|
+
}
|
|
1228
|
+
await ask(' Press Enter to continue...');
|
|
1121
1229
|
return { next: 'subscriptions' };
|
|
1122
1230
|
}
|
|
1123
1231
|
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
return { next: 'subscriptions' };
|
|
1232
|
+
return { next: 'main' };
|
|
1127
1233
|
}
|
|
1128
1234
|
|
|
1129
1235
|
// ─── Screen: dashboardScreen (kept for internal reference, unreachable) ───────
|
|
@@ -1152,7 +1258,7 @@ async function authScreen(rl, ask) {
|
|
|
1152
1258
|
'Claude:',
|
|
1153
1259
|
auth.claude.found
|
|
1154
1260
|
? ` logged in via ${auth.claude.source}`
|
|
1155
|
-
: ` not logged in — run: claude login`,
|
|
1261
|
+
: ` not logged in — run: claude auth login`,
|
|
1156
1262
|
` plan: ${claudePlanLabel}${claudeSub?.label ? ` [${claudeSub.label}]` : ''}`,
|
|
1157
1263
|
'',
|
|
1158
1264
|
'OpenAI:',
|
package/package.json
CHANGED