dual-brain 7.1.19 → 7.1.20
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 +67 -52
- package/package.json +1 -1
- package/src/profile.mjs +26 -25
package/bin/dual-brain.mjs
CHANGED
|
@@ -739,18 +739,20 @@ async function welcomeScreen(rl, ask) {
|
|
|
739
739
|
const claudeReady = auth.claude.found;
|
|
740
740
|
const openaiReady = auth.openai.found;
|
|
741
741
|
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
742
|
+
// Plan labels are inferred from auth config (rate-limit tier / JWT),
|
|
743
|
+
// not reported directly by the CLI. Suffix shows configured tier, not plan name.
|
|
744
|
+
const claudePlanSuffix = claudeReady && plans.claude
|
|
745
|
+
? ` · ${plans.claude} configured`
|
|
746
|
+
: '';
|
|
747
|
+
const openaiPlanSuffix = openaiReady && plans.openai
|
|
748
|
+
? ` · ${plans.openai} configured`
|
|
749
|
+
: '';
|
|
748
750
|
|
|
749
751
|
const detectedLines = [];
|
|
750
|
-
if (claudeReady) detectedLines.push(` Claude
|
|
751
|
-
else detectedLines.push(` Claude
|
|
752
|
-
if (openaiReady) detectedLines.push(` Codex
|
|
753
|
-
else detectedLines.push(` Codex
|
|
752
|
+
if (claudeReady) detectedLines.push(` Claude: authenticated${claudePlanSuffix}`);
|
|
753
|
+
else detectedLines.push(` Claude: not connected`);
|
|
754
|
+
if (openaiReady) detectedLines.push(` Codex: authenticated${openaiPlanSuffix}`);
|
|
755
|
+
else detectedLines.push(` Codex: not connected`);
|
|
754
756
|
|
|
755
757
|
console.log('');
|
|
756
758
|
console.log('Detected:');
|
|
@@ -1039,25 +1041,6 @@ async function mainScreen(rl, ask) {
|
|
|
1039
1041
|
}
|
|
1040
1042
|
console.log('');
|
|
1041
1043
|
|
|
1042
|
-
// Help shortcuts box (matching data-tools style)
|
|
1043
|
-
const W = 37;
|
|
1044
|
-
const helpTop = ` ┌${'─'.repeat(W)}┐`;
|
|
1045
|
-
const helpSep = ` ├${'─'.repeat(W)}┤`;
|
|
1046
|
-
const helpBottom = ` └${'─'.repeat(W)}┘`;
|
|
1047
|
-
const helpPad = (s) => s + ' '.repeat(Math.max(0, W - s.length));
|
|
1048
|
-
|
|
1049
|
-
console.log(helpTop);
|
|
1050
|
-
console.log(` │ ${helpPad('At ~/workspace$ prompt:')}│`);
|
|
1051
|
-
console.log(` │ ${helpPad('db = show this menu')}│`);
|
|
1052
|
-
console.log(` │ ${helpPad('j = login to claude')}│`);
|
|
1053
|
-
console.log(` │ ${helpPad('k = login to codex')}│`);
|
|
1054
|
-
console.log(helpSep);
|
|
1055
|
-
console.log(` │ ${helpPad('In Claude:')}│`);
|
|
1056
|
-
console.log(` │ ${helpPad('Ctrl+C x2 = back to menu')}│`);
|
|
1057
|
-
console.log(` │ ${helpPad('Ctrl+C x3 = exit to shell')}│`);
|
|
1058
|
-
console.log(helpBottom);
|
|
1059
|
-
console.log('');
|
|
1060
|
-
|
|
1061
1044
|
// Provider status (outside the box)
|
|
1062
1045
|
for (const line of headerLines) {
|
|
1063
1046
|
console.log(` ${line}`);
|
|
@@ -1133,7 +1116,12 @@ async function mainScreen(rl, ask) {
|
|
|
1133
1116
|
const active = sess.isActive ? ' ●' : '';
|
|
1134
1117
|
const cat = sess.category ? ` [${sess.category}]` : '';
|
|
1135
1118
|
const tool = (sess.tool === 'codex') ? 'cdx' : 'cld';
|
|
1136
|
-
|
|
1119
|
+
// If the name is still the "Session XXXXXXXX" fallback, try the project path instead
|
|
1120
|
+
let rawName = sess.name || '';
|
|
1121
|
+
if (/^Session [0-9a-f]{8,}$/i.test(rawName)) {
|
|
1122
|
+
rawName = sess.project ? sess.project.replace(/^-/, '/').replace(/-/g, '/') : sess.id.slice(0, 8);
|
|
1123
|
+
}
|
|
1124
|
+
const displayName = rawName.length > 40 ? rawName.slice(0, 37) + '...' : (rawName || sess.id.slice(0, 8));
|
|
1137
1125
|
console.log(` [${i + 1}] ${pin}${tool} ${sess.age.padEnd(8)} ${displayName}${active}${cat}`);
|
|
1138
1126
|
});
|
|
1139
1127
|
console.log('');
|
|
@@ -1171,15 +1159,41 @@ async function mainScreen(rl, ask) {
|
|
|
1171
1159
|
console.log(' [r] Resume (full list)');
|
|
1172
1160
|
console.log(' [/] Search sessions');
|
|
1173
1161
|
console.log(' [e] Manage sessions');
|
|
1174
|
-
console.log(' [i] Import from replit-tools');
|
|
1175
1162
|
console.log(' [m] Manage subscriptions');
|
|
1176
|
-
console.log(' [d] Switch to data-tools');
|
|
1177
1163
|
console.log(' [s] Settings');
|
|
1164
|
+
console.log(' [?] Help & shortcuts');
|
|
1165
|
+
console.log('');
|
|
1166
|
+
console.log(' \x1b[2mreplit-tools:\x1b[0m');
|
|
1167
|
+
console.log(' [i] Import sessions');
|
|
1168
|
+
console.log(' [d] Switch to data-tools');
|
|
1169
|
+
console.log('');
|
|
1178
1170
|
console.log(' [q] Exit');
|
|
1179
1171
|
console.log('');
|
|
1180
1172
|
|
|
1181
1173
|
const choice = (await ask(' Choice: ')).trim().toLowerCase();
|
|
1182
1174
|
|
|
1175
|
+
if (choice === '?') {
|
|
1176
|
+
const W = 37;
|
|
1177
|
+
const helpTop = ` ┌${'─'.repeat(W)}┐`;
|
|
1178
|
+
const helpSep = ` ├${'─'.repeat(W)}┤`;
|
|
1179
|
+
const helpBottom = ` └${'─'.repeat(W)}┘`;
|
|
1180
|
+
const helpPad = (s) => s + ' '.repeat(Math.max(0, W - s.length));
|
|
1181
|
+
console.log('');
|
|
1182
|
+
console.log(helpTop);
|
|
1183
|
+
console.log(` │ ${helpPad('At ~/workspace$ prompt:')}│`);
|
|
1184
|
+
console.log(` │ ${helpPad('db = show this menu')}│`);
|
|
1185
|
+
console.log(` │ ${helpPad('j = login to claude')}│`);
|
|
1186
|
+
console.log(` │ ${helpPad('k = login to codex')}│`);
|
|
1187
|
+
console.log(helpSep);
|
|
1188
|
+
console.log(` │ ${helpPad('In Claude:')}│`);
|
|
1189
|
+
console.log(` │ ${helpPad('Ctrl+C x2 = back to menu')}│`);
|
|
1190
|
+
console.log(` │ ${helpPad('Ctrl+C x3 = exit to shell')}│`);
|
|
1191
|
+
console.log(helpBottom);
|
|
1192
|
+
console.log('');
|
|
1193
|
+
await ask(' Press Enter to continue...');
|
|
1194
|
+
return { next: 'main' };
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1183
1197
|
if (choice === 'n') { return { next: 'new-session' }; }
|
|
1184
1198
|
|
|
1185
1199
|
if (choice === 'c') {
|
|
@@ -1696,18 +1710,16 @@ async function runOnboardingWizard(detection, cwd, rl) {
|
|
|
1696
1710
|
console.log(wRow(`Step 1 of 5: Detected providers`));
|
|
1697
1711
|
console.log(wSep);
|
|
1698
1712
|
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
const
|
|
1703
|
-
? (OPENAI_PLAN_LABELS[plans.openai] ?? plans.openai ?? 'plan unknown')
|
|
1704
|
-
: null;
|
|
1713
|
+
// Plan tier is inferred from auth config signals — not the actual plan name.
|
|
1714
|
+
// Show the tier ($20/$100/$200) with "configured" suffix to be honest.
|
|
1715
|
+
const claudePlanSuffix = claudeReady && plans.claude ? ` · ${plans.claude} configured` : '';
|
|
1716
|
+
const openaiPlanSuffix = openaiReady && plans.openai ? ` · ${plans.openai} configured` : '';
|
|
1705
1717
|
|
|
1706
1718
|
console.log(wRow(claudeReady
|
|
1707
|
-
? `✓ Claude CLI
|
|
1719
|
+
? `✓ Claude CLI${claudePlanSuffix}`
|
|
1708
1720
|
: `✗ Claude CLI not logged in`));
|
|
1709
1721
|
console.log(wRow(openaiReady
|
|
1710
|
-
? `✓ Codex CLI
|
|
1722
|
+
? `✓ Codex CLI${openaiPlanSuffix}`
|
|
1711
1723
|
: `✗ Codex CLI not logged in`));
|
|
1712
1724
|
if (existingSessions.length > 0) {
|
|
1713
1725
|
console.log(wRow(`✓ ${existingSessions.length} data-tools session${existingSessions.length !== 1 ? 's' : ''} found`));
|
|
@@ -1747,31 +1759,33 @@ async function runOnboardingWizard(detection, cwd, rl) {
|
|
|
1747
1759
|
console.log(wSep);
|
|
1748
1760
|
|
|
1749
1761
|
if (claudeReady) {
|
|
1750
|
-
|
|
1751
|
-
const
|
|
1752
|
-
|
|
1762
|
+
// Plan tier is inferred from auth config (rate-limit signal), not the actual plan name.
|
|
1763
|
+
const configuredClaudePlan = plans.claude || '$20';
|
|
1764
|
+
const configuredClaudeDesc = configuredClaudePlan + ' configured';
|
|
1765
|
+
console.log(wRow(`Claude — ${configuredClaudeDesc}`));
|
|
1753
1766
|
console.log(wRow(` [1] Pro ($20/mo)`));
|
|
1754
1767
|
console.log(wRow(` [2] Max x5 ($100/mo)`));
|
|
1755
1768
|
console.log(wRow(` [3] Max x20 ($200/mo)`));
|
|
1756
|
-
console.log(wRow(` [Enter] Keep
|
|
1769
|
+
console.log(wRow(` [Enter] Keep configured (${configuredClaudePlan})`));
|
|
1757
1770
|
console.log(wSep);
|
|
1758
1771
|
const claudeChoice = (await ask(' Claude plan [1/2/3/Enter]: ')).trim();
|
|
1759
1772
|
const claudePlanMap = { '1': 'pro', '2': 'max5', '3': 'max20' };
|
|
1760
|
-
state.claudePlan = claudePlanMap[claudeChoice] ||
|
|
1773
|
+
state.claudePlan = claudePlanMap[claudeChoice] || configuredClaudePlan;
|
|
1761
1774
|
}
|
|
1762
1775
|
|
|
1763
1776
|
if (openaiReady) {
|
|
1764
|
-
|
|
1765
|
-
const
|
|
1766
|
-
|
|
1777
|
+
// Plan tier is inferred from JWT claim in auth config, not the actual plan name.
|
|
1778
|
+
const configuredOpenaiPlan = plans.openai || '$20';
|
|
1779
|
+
const configuredOpenaiDesc = configuredOpenaiPlan + ' configured';
|
|
1780
|
+
console.log(wRow(`OpenAI — ${configuredOpenaiDesc}`));
|
|
1767
1781
|
console.log(wRow(` [1] Plus ($20/mo)`));
|
|
1768
1782
|
console.log(wRow(` [2] Pro ($100/mo)`));
|
|
1769
1783
|
console.log(wRow(` [3] Pro ($200/mo higher limits)`));
|
|
1770
|
-
console.log(wRow(` [Enter] Keep
|
|
1784
|
+
console.log(wRow(` [Enter] Keep configured (${configuredOpenaiPlan})`));
|
|
1771
1785
|
console.log(wSep);
|
|
1772
1786
|
const openaiChoice = (await ask(' OpenAI plan [1/2/3/Enter]: ')).trim();
|
|
1773
1787
|
const openaiPlanMap = { '1': 'plus', '2': 'pro', '3': 'pro200' };
|
|
1774
|
-
state.openaiPlan = openaiPlanMap[openaiChoice] ||
|
|
1788
|
+
state.openaiPlan = openaiPlanMap[openaiChoice] || configuredOpenaiPlan;
|
|
1775
1789
|
}
|
|
1776
1790
|
|
|
1777
1791
|
console.log(wBottom);
|
|
@@ -2053,8 +2067,9 @@ async function diagnosticsScreen(rl, ask) {
|
|
|
2053
2067
|
|
|
2054
2068
|
const claudeHealthBadge = auth.claude.found ? _providerBadge('claude') : 'not logged in';
|
|
2055
2069
|
const openaiHealthBadge = auth.openai.found ? _providerBadge('openai') : 'not logged in';
|
|
2056
|
-
|
|
2057
|
-
const
|
|
2070
|
+
// Plan tier is inferred from auth config signals — show tier with "configured" to be honest.
|
|
2071
|
+
const claudePlanStr = plans.claude ? `${plans.claude} configured` : 'unknown';
|
|
2072
|
+
const openaiPlanStr = plans.openai ? `${plans.openai} configured` : 'unknown';
|
|
2058
2073
|
|
|
2059
2074
|
// ── Enforcement checks ────────────────────────────────────────────────────
|
|
2060
2075
|
const hooksDir = join(cwd, '.claude', 'hooks');
|
package/package.json
CHANGED
package/src/profile.mjs
CHANGED
|
@@ -240,9 +240,14 @@ function decodeJwtPayload(token) {
|
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
/**
|
|
243
|
-
*
|
|
243
|
+
* Infer plan tier from Claude Code and Codex auth config files.
|
|
244
244
|
* Returns { claude: '$20'|'$100'|'$200'|null, openai: '$20'|'$100'|'$200'|null }.
|
|
245
245
|
* Returns nulls for any provider whose config cannot be read — never throws.
|
|
246
|
+
*
|
|
247
|
+
* NOTE: This reads rate-limit tier signals (organizationRateLimitTier for Claude,
|
|
248
|
+
* chatgpt_plan_type JWT claim for OpenAI) and maps them to price tiers.
|
|
249
|
+
* It does NOT retrieve the actual subscription plan name from the provider —
|
|
250
|
+
* labels like "Max x5" or "Pro" are our own interpretations of those signals.
|
|
246
251
|
*/
|
|
247
252
|
function detectPlans() {
|
|
248
253
|
const plans = { claude: null, openai: null };
|
|
@@ -371,23 +376,19 @@ function loadProfile(cwd) {
|
|
|
371
376
|
}
|
|
372
377
|
if (!profile) profile = defaultProfile();
|
|
373
378
|
|
|
374
|
-
//
|
|
379
|
+
// Read plan tier from auth config files (JWT or organizationRateLimitTier) and
|
|
380
|
+
// apply if it differs from the stored profile value.
|
|
381
|
+
// NOTE: detectPlans() reads rate-limit tier data from the auth config — it infers
|
|
382
|
+
// a price tier ($20/$100/$200) from that signal, not from the subscription name itself.
|
|
383
|
+
// The plan label (e.g. "Max x5") comes from our own mapping, not from Claude/OpenAI.
|
|
375
384
|
const detected = detectPlans();
|
|
376
385
|
for (const [provider, detectedPlan] of Object.entries(detected)) {
|
|
377
386
|
if (!detectedPlan) continue;
|
|
378
387
|
if (!profile.providers[provider]) continue;
|
|
379
388
|
const stored = profile.providers[provider].plan;
|
|
380
389
|
if (stored !== detectedPlan) {
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
'$20': 'Claude Pro ($20)', '$100': 'Claude Max x5 ($100)', '$200': 'Claude Max x20 ($200)',
|
|
384
|
-
},
|
|
385
|
-
openai: {
|
|
386
|
-
'$20': 'ChatGPT Plus ($20)', '$100': 'ChatGPT Pro ($100)', '$200': 'ChatGPT Pro ($200)',
|
|
387
|
-
},
|
|
388
|
-
};
|
|
389
|
-
const label = labels[provider]?.[detectedPlan] ?? `${provider} ${detectedPlan}`;
|
|
390
|
-
process.stderr.write(`[dual-brain] Detected ${label} plan\n`);
|
|
390
|
+
const providerName = provider === 'claude' ? 'Claude' : 'OpenAI';
|
|
391
|
+
process.stderr.write(`[dual-brain] ${providerName}: plan updated to ${detectedPlan} (from auth config)\n`);
|
|
391
392
|
profile.providers[provider].plan = detectedPlan;
|
|
392
393
|
}
|
|
393
394
|
}
|
|
@@ -622,12 +623,10 @@ async function autoSetup(cwd) {
|
|
|
622
623
|
if (auth.claude.found) {
|
|
623
624
|
profile.providers.claude.enabled = true;
|
|
624
625
|
profile.providers.claude.plan = plans.claude || '$20';
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
}[profile.providers.claude.plan] || profile.providers.claude.plan;
|
|
630
|
-
result.actions.push(`${planLabel} (${auth.claude.source})`);
|
|
626
|
+
// Plan tier is inferred from auth config signal — show tier with "configured",
|
|
627
|
+
// not a plan name we didn't actually detect.
|
|
628
|
+
const claudeTierLabel = plans.claude ? `${plans.claude} configured` : 'connected';
|
|
629
|
+
result.actions.push(`Claude: ${claudeTierLabel} (${auth.claude.source})`);
|
|
631
630
|
} else {
|
|
632
631
|
profile.providers.claude.enabled = false;
|
|
633
632
|
result.warnings.push('Claude CLI not logged in — run: claude login');
|
|
@@ -637,12 +636,10 @@ async function autoSetup(cwd) {
|
|
|
637
636
|
if (auth.openai.found) {
|
|
638
637
|
profile.providers.openai.enabled = true;
|
|
639
638
|
profile.providers.openai.plan = plans.openai || '$20';
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
}[profile.providers.openai.plan] || profile.providers.openai.plan;
|
|
645
|
-
result.actions.push(`${planLabel} (${auth.openai.source})`);
|
|
639
|
+
// Plan tier is inferred from JWT claim in auth config — show tier with "configured",
|
|
640
|
+
// not a plan name we didn't actually detect.
|
|
641
|
+
const openaiTierLabel = plans.openai ? `${plans.openai} configured` : 'connected';
|
|
642
|
+
result.actions.push(`OpenAI: ${openaiTierLabel} (${auth.openai.source})`);
|
|
646
643
|
} else {
|
|
647
644
|
profile.providers.openai.enabled = false;
|
|
648
645
|
result.warnings.push('Codex CLI not logged in — run: codex login');
|
|
@@ -938,7 +935,11 @@ async function detectExistingAuth(cwd) {
|
|
|
938
935
|
}
|
|
939
936
|
|
|
940
937
|
// -------------------------------------------------------------------------
|
|
941
|
-
// Plan
|
|
938
|
+
// Plan tier inference (re-uses detectPlans which reads auth config files)
|
|
939
|
+
// NOTE: This is NOT subscription detection — we infer a price tier ($20/$100/$200)
|
|
940
|
+
// from rate-limit tier signals in the auth config (organizationRateLimitTier for
|
|
941
|
+
// Claude, JWT chatgpt_plan_type for OpenAI). The CLI does not report the actual
|
|
942
|
+
// plan name or price. Any plan label shown to the user comes from our own mapping.
|
|
942
943
|
// -------------------------------------------------------------------------
|
|
943
944
|
const plans = detectPlans();
|
|
944
945
|
if (result.claude.found && plans.claude) result.claude.plan = plans.claude;
|