theclawbay 0.5.1 → 0.6.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.
- package/dist/commands/codex.js +2 -0
- package/dist/commands/logout.js +3 -0
- package/dist/commands/setup.js +75 -14
- package/dist/commands/usage.js +54 -9
- package/dist/lib/base-command.d.ts +1 -7
- package/dist/lib/base-command.js +12 -65
- package/dist/lib/clients/codex.d.ts +8 -3
- package/dist/lib/clients/codex.js +26 -30
- package/dist/lib/codex-history-migration.d.ts +26 -0
- package/dist/lib/codex-history-migration.js +109 -40
- package/dist/lib/main-menu.js +4 -1
- package/dist/lib/shared/app-shell.d.ts +11 -0
- package/dist/lib/shared/app-shell.js +96 -0
- package/dist/lib/shared/prompts.d.ts +8 -0
- package/dist/lib/shared/prompts.js +75 -0
- package/dist/lib/shared/tui.d.ts +34 -1
- package/dist/lib/shared/tui.js +182 -32
- package/package.json +1 -1
- package/theclawbay-supported-models.json +2 -2
package/dist/commands/codex.js
CHANGED
|
@@ -4,6 +4,7 @@ const core_1 = require("@oclif/core");
|
|
|
4
4
|
const promises_1 = require("node:readline/promises");
|
|
5
5
|
const base_command_1 = require("../lib/base-command");
|
|
6
6
|
const errors_1 = require("../lib/errors");
|
|
7
|
+
const app_shell_1 = require("../lib/shared/app-shell");
|
|
7
8
|
const tui_1 = require("../lib/shared/tui");
|
|
8
9
|
const codex_profiles_1 = require("../lib/clients/codex-profiles");
|
|
9
10
|
const fsx_1 = require("../lib/shared/fsx");
|
|
@@ -18,6 +19,7 @@ function kindGlyph(profile) {
|
|
|
18
19
|
class CodexCommand extends base_command_1.BaseCommand {
|
|
19
20
|
async run() {
|
|
20
21
|
await this.runSafe(async () => {
|
|
22
|
+
app_shell_1.AppShell.printHeaderOnce("Switch your Codex account");
|
|
21
23
|
const { args, flags } = await this.parse(CodexCommand);
|
|
22
24
|
const nowIso = new Date().toISOString();
|
|
23
25
|
const rawAction = (args.action ?? "status").toLowerCase();
|
package/dist/commands/logout.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const format_1 = require("../lib/shared/format");
|
|
4
4
|
const base_command_1 = require("../lib/base-command");
|
|
5
|
+
const app_shell_1 = require("../lib/shared/app-shell");
|
|
5
6
|
const tui_1 = require("../lib/shared/tui");
|
|
6
7
|
const core_1 = require("@oclif/core");
|
|
7
8
|
const config_1 = require("../lib/managed/config");
|
|
@@ -74,6 +75,8 @@ class LogoutCommand extends base_command_1.BaseCommand {
|
|
|
74
75
|
await this.runSafe(async () => {
|
|
75
76
|
const { flags } = await this.parse(LogoutCommand);
|
|
76
77
|
const debugOutput = flags["debug-output"];
|
|
78
|
+
if (!debugOutput)
|
|
79
|
+
app_shell_1.AppShell.printHeaderOnce("Revert The Claw Bay on this machine");
|
|
77
80
|
let managed = null;
|
|
78
81
|
try {
|
|
79
82
|
managed = await (0, config_1.readManagedConfig)();
|
package/dist/commands/setup.js
CHANGED
|
@@ -38,6 +38,7 @@ const paths_1 = require("../lib/config/paths");
|
|
|
38
38
|
const providers_1 = require("../lib/shared/providers");
|
|
39
39
|
const openclaw_1 = require("../lib/clients/openclaw");
|
|
40
40
|
const node_path_1 = __importDefault(require("node:path"));
|
|
41
|
+
const app_shell_1 = require("../lib/shared/app-shell");
|
|
41
42
|
const tui_1 = require("../lib/shared/tui");
|
|
42
43
|
function logSetupCompactSummary(params) {
|
|
43
44
|
const configured = params.setupClients
|
|
@@ -76,18 +77,78 @@ function logSetupCompactSummary(params) {
|
|
|
76
77
|
for (const note of params.notes) {
|
|
77
78
|
lines.push((0, tui_1.statusLine)("info", note));
|
|
78
79
|
}
|
|
79
|
-
|
|
80
|
+
params.log("");
|
|
81
|
+
for (const line of (0, tui_1.renderBox)({ title: `${tui_1.glyph.tick} Setup complete`, accent: "coral", lines })) {
|
|
80
82
|
params.log(line);
|
|
81
83
|
}
|
|
82
84
|
}
|
|
85
|
+
// Convert the chosen slice of Codex history while painting a live bar with an
|
|
86
|
+
// ETA, then leave a single permanent summary line. This replaces the old silent
|
|
87
|
+
// full-tree pass that could appear to hang for hours.
|
|
88
|
+
async function convertConversationsWithProgress(plan, log) {
|
|
89
|
+
const total = plan.files.length;
|
|
90
|
+
const isTTY = process.stdout.isTTY;
|
|
91
|
+
const painter = isTTY ? (0, tui_1.createFramePainter)(process.stdout) : null;
|
|
92
|
+
let latest = null;
|
|
93
|
+
let frameIndex = 0;
|
|
94
|
+
const draw = () => {
|
|
95
|
+
if (!painter || !latest)
|
|
96
|
+
return;
|
|
97
|
+
const p = latest;
|
|
98
|
+
const spinner = tui_1.tint.accent(tui_1.SPINNER_FRAMES[frameIndex % tui_1.SPINNER_FRAMES.length] ?? "");
|
|
99
|
+
const rate = p.elapsedMs > 0 ? (p.processed / (p.elapsedMs / 1000)) : 0;
|
|
100
|
+
const bar = (0, tui_1.gauge)({ value: p.processed, max: p.total, width: 26, color: tui_1.tint.sea });
|
|
101
|
+
painter.paint([
|
|
102
|
+
"",
|
|
103
|
+
...(0, tui_1.renderBox)({
|
|
104
|
+
title: "Converting conversations",
|
|
105
|
+
accent: "sea",
|
|
106
|
+
lines: [
|
|
107
|
+
`${spinner} ${tui_1.tint.foam("Keeping your Codex history visible under The Claw Bay")}`,
|
|
108
|
+
"",
|
|
109
|
+
`${bar} ${tui_1.tint.dim(`${(0, tui_1.formatCount)(p.processed)} / ${(0, tui_1.formatCount)(p.total)}`)}`,
|
|
110
|
+
tui_1.tint.dim(`${(0, tui_1.formatCount)(p.rewritten)} updated · ${(0, tui_1.formatEta)(p.etaSeconds)} left · ${rate >= 1 ? `${Math.round(rate)}/s` : "…"}`),
|
|
111
|
+
],
|
|
112
|
+
}),
|
|
113
|
+
]);
|
|
114
|
+
};
|
|
115
|
+
const timer = isTTY ? setInterval(() => { frameIndex += 1; draw(); }, 90) : null;
|
|
116
|
+
if (timer && typeof timer.unref === "function")
|
|
117
|
+
timer.unref();
|
|
118
|
+
let result;
|
|
119
|
+
try {
|
|
120
|
+
result = await (0, codex_history_migration_1.migrateSessionProviders)({
|
|
121
|
+
codexHome: paths_1.codexDir,
|
|
122
|
+
migrationStateFile: codex_1.MIGRATION_STATE_FILE,
|
|
123
|
+
neutralizeSources: codex_1.HISTORY_PROVIDER_NEUTRALIZE_SOURCES,
|
|
124
|
+
files: plan.files,
|
|
125
|
+
onProgress: (progress) => {
|
|
126
|
+
latest = progress;
|
|
127
|
+
draw();
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
if (timer)
|
|
133
|
+
clearInterval(timer);
|
|
134
|
+
if (painter)
|
|
135
|
+
painter.clear();
|
|
136
|
+
}
|
|
137
|
+
const converted = result.rewritten;
|
|
138
|
+
const summary = converted > 0
|
|
139
|
+
? `Converted ${(0, tui_1.formatCount)(converted)} of ${(0, tui_1.formatCount)(total)} conversation${total === 1 ? "" : "s"}`
|
|
140
|
+
: `Checked ${(0, tui_1.formatCount)(total)} conversation${total === 1 ? "" : "s"} — nothing needed converting`;
|
|
141
|
+
if (isTTY)
|
|
142
|
+
log(` ${tui_1.tint.green(tui_1.glyph.tick)} ${tui_1.tint.foam(summary)}`);
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
83
145
|
class SetupCommand extends base_command_1.BaseCommand {
|
|
84
146
|
async run() {
|
|
85
147
|
await this.runSafe(async () => {
|
|
86
148
|
const { flags } = await this.parse(SetupCommand);
|
|
87
149
|
const debugOutput = flags["debug-output"];
|
|
88
|
-
if (!debugOutput
|
|
89
|
-
|
|
90
|
-
this.log("");
|
|
150
|
+
if (!debugOutput) {
|
|
151
|
+
app_shell_1.AppShell.printHeaderOnce("Connect your coding tools to The Claw Bay relay");
|
|
91
152
|
}
|
|
92
153
|
let managed = null;
|
|
93
154
|
try {
|
|
@@ -296,14 +357,15 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
296
357
|
selectedClientIds: selectedSetupClients,
|
|
297
358
|
log: (message) => this.log(message),
|
|
298
359
|
});
|
|
299
|
-
|
|
360
|
+
if (flags["migrate-conversations"] !== undefined && !selectedSetupClients.has("codex")) {
|
|
361
|
+
throw new Error("--migrate-conversations requires Codex to be selected for setup.");
|
|
362
|
+
}
|
|
363
|
+
const migrationPlan = await (0, codex_1.resolveCodexConversationMigrationPlan)({
|
|
300
364
|
codexSelected: selectedSetupClients.has("codex"),
|
|
301
365
|
flagValue: flags["migrate-conversations"],
|
|
302
366
|
skipPrompt: flags.yes,
|
|
303
367
|
});
|
|
304
|
-
|
|
305
|
-
throw new Error("--migrate-conversations requires Codex to be selected for setup.");
|
|
306
|
-
}
|
|
368
|
+
const migrateCodexConversations = migrationPlan.enabled;
|
|
307
369
|
const linkFreshDeviceSession = async () => {
|
|
308
370
|
deviceLabel = await (0, backend_1.resolveDeviceLabel)({
|
|
309
371
|
flagValue: flags["device-name"],
|
|
@@ -490,11 +552,9 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
490
552
|
apiKey: authCredential,
|
|
491
553
|
});
|
|
492
554
|
if (migrateCodexConversations) {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
neutralizeSources: codex_1.HISTORY_PROVIDER_NEUTRALIZE_SOURCES,
|
|
497
|
-
});
|
|
555
|
+
// Hand off from the checklist spinner to the live conversion bar.
|
|
556
|
+
progress.stop();
|
|
557
|
+
sessionMigration = await convertConversationsWithProgress(migrationPlan, (m) => this.log(m));
|
|
498
558
|
}
|
|
499
559
|
authSeedCleanup = await (0, codex_auth_seeding_1.cleanupSeededCodexAuth)({
|
|
500
560
|
codexHome: paths_1.codexDir,
|
|
@@ -618,7 +678,8 @@ class SetupCommand extends base_command_1.BaseCommand {
|
|
|
618
678
|
throw error;
|
|
619
679
|
}
|
|
620
680
|
if (!debugOutput && process.stdout.isTTY) {
|
|
621
|
-
|
|
681
|
+
// Finalize the last live step; the summary box below is the headline.
|
|
682
|
+
progress.stop();
|
|
622
683
|
}
|
|
623
684
|
else {
|
|
624
685
|
this.log("Setup complete");
|
package/dist/commands/usage.js
CHANGED
|
@@ -5,6 +5,8 @@ const base_command_1 = require("../lib/base-command");
|
|
|
5
5
|
const api_key_1 = require("../lib/managed/api-key");
|
|
6
6
|
const config_1 = require("../lib/managed/config");
|
|
7
7
|
const errors_1 = require("../lib/managed/errors");
|
|
8
|
+
const app_shell_1 = require("../lib/shared/app-shell");
|
|
9
|
+
const tui_1 = require("../lib/shared/tui");
|
|
8
10
|
const DEFAULT_BACKEND_URL = "https://theclawbay.com";
|
|
9
11
|
function formatPercent(value) {
|
|
10
12
|
if (typeof value !== "number" || !Number.isFinite(value))
|
|
@@ -62,6 +64,26 @@ function printWindow(log, label, window, showCosts) {
|
|
|
62
64
|
log(" Status: limit reached");
|
|
63
65
|
}
|
|
64
66
|
}
|
|
67
|
+
// A framed usage window with a coral/teal meter, sized to the terminal.
|
|
68
|
+
function renderWindowBox(label, window, showCosts) {
|
|
69
|
+
const usedPercent = window.progressPercentUsed ?? window.percentUsed ?? 0;
|
|
70
|
+
const meterColor = window.limitReached ? tui_1.tint.red : usedPercent >= 80 ? tui_1.tint.yellow : tui_1.tint.sea;
|
|
71
|
+
const lines = [
|
|
72
|
+
`${(0, tui_1.gauge)({ value: usedPercent, max: 100, width: 30, color: meterColor })} ${tui_1.tint.dim("used")}`,
|
|
73
|
+
"",
|
|
74
|
+
`${tui_1.tint.dim("Remaining")} ${tui_1.tint.foam(formatPercent(window.percentRemaining))}`,
|
|
75
|
+
`${tui_1.tint.dim("Requests")} ${tui_1.tint.foam(String(window.requestCount))} total ${tui_1.tint.green(`${window.successCount} ok`)} ${window.failedCount > 0 ? tui_1.tint.red(`${window.failedCount} failed`) : tui_1.tint.dim("0 failed")}`,
|
|
76
|
+
`${tui_1.tint.dim("Resets in")} ${tui_1.tint.foam(formatDuration(window.secondsUntilReset))} ${tui_1.tint.dim(`(${formatTimestamp(window.windowEnd)})`)}`,
|
|
77
|
+
];
|
|
78
|
+
if (showCosts) {
|
|
79
|
+
lines.push(`${tui_1.tint.dim("Cost")} ${tui_1.tint.foam(formatUsd(window.estimatedCostUsdUsed))} ${tui_1.tint.dim("of")} ${tui_1.tint.foam(formatUsd(window.costUsdLimit))} ${tui_1.tint.dim(`(${formatUsd(window.costUsdRemaining)} left)`)}`);
|
|
80
|
+
}
|
|
81
|
+
if (window.limitReached) {
|
|
82
|
+
lines.push("");
|
|
83
|
+
lines.push((0, tui_1.statusLine)("warn", tui_1.tint.yellow("Limit reached for this window")));
|
|
84
|
+
}
|
|
85
|
+
return (0, tui_1.renderBox)({ title: label, accent: window.limitReached ? "danger" : "sea", lines });
|
|
86
|
+
}
|
|
65
87
|
class UsageCommand extends base_command_1.BaseCommand {
|
|
66
88
|
async run() {
|
|
67
89
|
await this.runSafe(async () => {
|
|
@@ -99,19 +121,42 @@ class UsageCommand extends base_command_1.BaseCommand {
|
|
|
99
121
|
return;
|
|
100
122
|
}
|
|
101
123
|
const showCosts = body.usageLimitPresentation !== "percent_only";
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
124
|
+
if (!process.stdout.isTTY) {
|
|
125
|
+
this.log("The Claw Bay usage");
|
|
126
|
+
this.log(`Observed: ${formatTimestamp(body.observedAt)}`);
|
|
127
|
+
this.log(`Auth scope: ${body.pooled ? "shared pooled usage" : body.mode}`);
|
|
128
|
+
if (body.mode === "direct" && typeof body.planMultiplier === "number" && Number.isFinite(body.planMultiplier)) {
|
|
129
|
+
this.log(`Plan multiplier: ${body.planMultiplier}x`);
|
|
130
|
+
}
|
|
131
|
+
if (body.anyLimitReached) {
|
|
132
|
+
this.log("Status: at least one usage window has been reached");
|
|
133
|
+
}
|
|
134
|
+
this.log("");
|
|
135
|
+
printWindow((message) => this.log(message), "5-hour window", body.usage.fiveHour, showCosts);
|
|
136
|
+
this.log("");
|
|
137
|
+
printWindow((message) => this.log(message), "Weekly window", body.usage.weekly, showCosts);
|
|
138
|
+
return;
|
|
107
139
|
}
|
|
108
|
-
|
|
109
|
-
|
|
140
|
+
app_shell_1.AppShell.printHeaderOnce("Your pooled Claw Bay usage");
|
|
141
|
+
const scope = body.pooled ? "shared pooled usage" : body.mode;
|
|
142
|
+
const overviewLines = [
|
|
143
|
+
`${tui_1.tint.dim("Scope")} ${tui_1.tint.foam(scope)}`,
|
|
144
|
+
`${tui_1.tint.dim("Observed")} ${tui_1.tint.foam(formatTimestamp(body.observedAt))}`,
|
|
145
|
+
];
|
|
146
|
+
if (body.mode === "direct" && typeof body.planMultiplier === "number" && Number.isFinite(body.planMultiplier)) {
|
|
147
|
+
overviewLines.push(`${tui_1.tint.dim("Plan")} ${tui_1.tint.foam(`${body.planMultiplier}x multiplier`)}`);
|
|
110
148
|
}
|
|
149
|
+
overviewLines.push(body.anyLimitReached
|
|
150
|
+
? (0, tui_1.statusLine)("warn", tui_1.tint.yellow("At least one usage window has been reached"))
|
|
151
|
+
: (0, tui_1.statusLine)("ok", tui_1.tint.foam("Within limits on all windows")));
|
|
152
|
+
for (const line of (0, tui_1.renderBox)({ title: `${tui_1.glyph.claw} Usage`, accent: "coral", lines: overviewLines }))
|
|
153
|
+
this.log(line);
|
|
111
154
|
this.log("");
|
|
112
|
-
|
|
155
|
+
for (const line of renderWindowBox("5-hour window", body.usage.fiveHour, showCosts))
|
|
156
|
+
this.log(line);
|
|
113
157
|
this.log("");
|
|
114
|
-
|
|
158
|
+
for (const line of renderWindowBox("Weekly window", body.usage.weekly, showCosts))
|
|
159
|
+
this.log(line);
|
|
115
160
|
});
|
|
116
161
|
}
|
|
117
162
|
}
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import { Command } from "@oclif/core";
|
|
2
|
-
|
|
3
|
-
update(message: string): void;
|
|
4
|
-
succeed(message?: string): void;
|
|
5
|
-
fail(message?: string): void;
|
|
6
|
-
stop(): void;
|
|
7
|
-
};
|
|
2
|
+
import { ProgressHandle } from "./shared/app-shell";
|
|
8
3
|
export declare abstract class BaseCommand extends Command {
|
|
9
4
|
protected runSafe(action: () => Promise<void>): Promise<void>;
|
|
10
5
|
private handleError;
|
|
11
6
|
protected createProgressHandle(enabled: boolean): ProgressHandle;
|
|
12
7
|
}
|
|
13
|
-
export {};
|
package/dist/lib/base-command.js
CHANGED
|
@@ -4,7 +4,7 @@ exports.BaseCommand = void 0;
|
|
|
4
4
|
const core_1 = require("@oclif/core");
|
|
5
5
|
const errors_1 = require("./errors");
|
|
6
6
|
const update_check_1 = require("./update-check");
|
|
7
|
-
const
|
|
7
|
+
const app_shell_1 = require("./shared/app-shell");
|
|
8
8
|
class BaseCommand extends core_1.Command {
|
|
9
9
|
async runSafe(action) {
|
|
10
10
|
try {
|
|
@@ -20,72 +20,19 @@ class BaseCommand extends core_1.Command {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
handleError(error) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
this.error(String(error));
|
|
23
|
+
// Always hand oclif a non-empty string. A blank/undefined message would make
|
|
24
|
+
// oclif throw its own "first argument must be a string" TypeError, which then
|
|
25
|
+
// masks the real failure with a confusing message.
|
|
26
|
+
const message = error instanceof errors_1.ClawBayError || error instanceof Error
|
|
27
|
+
? error.message
|
|
28
|
+
: String(error);
|
|
29
|
+
this.error(typeof message === "string" && message.trim().length > 0 ? message : String(error));
|
|
30
30
|
}
|
|
31
31
|
createProgressHandle(enabled) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
fail() { },
|
|
37
|
-
stop() { },
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
41
|
-
let message = "";
|
|
42
|
-
let frameIndex = 0;
|
|
43
|
-
let timer = null;
|
|
44
|
-
const render = (prefix) => {
|
|
45
|
-
process.stdout.write(`\r\x1b[2K${prefix} ${message}`);
|
|
46
|
-
};
|
|
47
|
-
const spinnerFrame = () => tui_1.tint.accent(frames[frameIndex] ?? frames[0] ?? "•");
|
|
48
|
-
const ensureTimer = () => {
|
|
49
|
-
if (timer)
|
|
50
|
-
return;
|
|
51
|
-
timer = setInterval(() => {
|
|
52
|
-
frameIndex = (frameIndex + 1) % frames.length;
|
|
53
|
-
render(spinnerFrame());
|
|
54
|
-
}, 80);
|
|
55
|
-
if (typeof timer.unref === "function")
|
|
56
|
-
timer.unref();
|
|
57
|
-
};
|
|
58
|
-
const clearTimer = () => {
|
|
59
|
-
if (!timer)
|
|
60
|
-
return;
|
|
61
|
-
clearInterval(timer);
|
|
62
|
-
timer = null;
|
|
63
|
-
};
|
|
64
|
-
return {
|
|
65
|
-
update(nextMessage) {
|
|
66
|
-
message = nextMessage;
|
|
67
|
-
ensureTimer();
|
|
68
|
-
render(spinnerFrame());
|
|
69
|
-
},
|
|
70
|
-
succeed(nextMessage) {
|
|
71
|
-
if (nextMessage)
|
|
72
|
-
message = tui_1.tint.bold(nextMessage);
|
|
73
|
-
clearTimer();
|
|
74
|
-
render(tui_1.tint.green(tui_1.glyph.tick));
|
|
75
|
-
process.stdout.write("\n");
|
|
76
|
-
},
|
|
77
|
-
fail(nextMessage) {
|
|
78
|
-
if (nextMessage)
|
|
79
|
-
message = nextMessage;
|
|
80
|
-
clearTimer();
|
|
81
|
-
render(tui_1.tint.red(tui_1.glyph.cross));
|
|
82
|
-
process.stdout.write("\n");
|
|
83
|
-
},
|
|
84
|
-
stop() {
|
|
85
|
-
clearTimer();
|
|
86
|
-
process.stdout.write("\r\x1b[2K");
|
|
87
|
-
},
|
|
88
|
-
};
|
|
32
|
+
// Backed by the persistent AppShell: each completed step commits a lasting
|
|
33
|
+
// checklist line while only the active step animates, so the surface never
|
|
34
|
+
// wipes itself between phases.
|
|
35
|
+
return app_shell_1.AppShell.progress(enabled);
|
|
89
36
|
}
|
|
90
37
|
}
|
|
91
38
|
exports.BaseCommand = BaseCommand;
|
|
@@ -16,12 +16,17 @@ export type CodexFeatureFlagSnapshot = {
|
|
|
16
16
|
export declare function removeProviderTable(source: string, providerId: string): string;
|
|
17
17
|
export declare function restoreManagedCodexFeatureFlags(source: string): Promise<string>;
|
|
18
18
|
export declare function ensureManagedCodexFeatureFlags(source: string): Promise<string>;
|
|
19
|
-
export
|
|
20
|
-
|
|
19
|
+
export type CodexMigrationPlan = {
|
|
20
|
+
enabled: boolean;
|
|
21
|
+
limit: number | null;
|
|
22
|
+
files: string[];
|
|
23
|
+
total: number;
|
|
24
|
+
};
|
|
25
|
+
export declare function resolveCodexConversationMigrationPlan(params: {
|
|
21
26
|
codexSelected: boolean;
|
|
22
27
|
flagValue: boolean | undefined;
|
|
23
28
|
skipPrompt: boolean;
|
|
24
|
-
}): Promise<
|
|
29
|
+
}): Promise<CodexMigrationPlan>;
|
|
25
30
|
export declare function detectCodexClient(): Promise<boolean>;
|
|
26
31
|
export declare function writeCodexConfig(params: {
|
|
27
32
|
backendUrl: string;
|
|
@@ -7,8 +7,7 @@ exports.CODEX_MODEL_AUTO_COMPACT_TOKEN_LIMIT = exports.CODEX_MODEL_CONTEXT_WINDO
|
|
|
7
7
|
exports.removeProviderTable = removeProviderTable;
|
|
8
8
|
exports.restoreManagedCodexFeatureFlags = restoreManagedCodexFeatureFlags;
|
|
9
9
|
exports.ensureManagedCodexFeatureFlags = ensureManagedCodexFeatureFlags;
|
|
10
|
-
exports.
|
|
11
|
-
exports.resolveCodexConversationMigrationSelection = resolveCodexConversationMigrationSelection;
|
|
10
|
+
exports.resolveCodexConversationMigrationPlan = resolveCodexConversationMigrationPlan;
|
|
12
11
|
exports.detectCodexClient = detectCodexClient;
|
|
13
12
|
exports.writeCodexConfig = writeCodexConfig;
|
|
14
13
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -16,9 +15,10 @@ const paths_1 = require("../config/paths");
|
|
|
16
15
|
const providers_1 = require("../shared/providers");
|
|
17
16
|
const fsx_1 = require("../shared/fsx");
|
|
18
17
|
const toml_1 = require("../shared/toml");
|
|
19
|
-
const
|
|
18
|
+
const codex_history_migration_1 = require("../codex-history-migration");
|
|
19
|
+
const prompts_1 = require("../shared/prompts");
|
|
20
20
|
const platform_1 = require("../shared/platform");
|
|
21
|
-
const
|
|
21
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
22
22
|
const urls_1 = require("../shared/urls");
|
|
23
23
|
// Extracted from src/commands/setup.ts (theclawbay CLI restructure).
|
|
24
24
|
// Code motion only: bodies are verbatim from the original file.
|
|
@@ -104,31 +104,27 @@ async function ensureManagedCodexFeatureFlags(source) {
|
|
|
104
104
|
next = (0, toml_1.setTopLevelTableKey)(next, "features", "apps_mcp_gateway", "true");
|
|
105
105
|
return next;
|
|
106
106
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
return false;
|
|
110
|
-
const rl = (0, promises_1.createInterface)({
|
|
111
|
-
input: process.stdin,
|
|
112
|
-
output: process.stdout,
|
|
113
|
-
});
|
|
114
|
-
try {
|
|
115
|
-
const answer = await rl.question("\nConvert old Codex conversations so they stay visible under The Claw Bay? [y/N] ");
|
|
116
|
-
const normalized = answer.trim().toLowerCase();
|
|
117
|
-
return normalized === "y" || normalized === "yes";
|
|
118
|
-
}
|
|
119
|
-
finally {
|
|
120
|
-
rl.close();
|
|
121
|
-
process.stdin.resume();
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
async function resolveCodexConversationMigrationSelection(params) {
|
|
107
|
+
const DISABLED_MIGRATION_PLAN = { enabled: false, limit: null, files: [], total: 0 };
|
|
108
|
+
async function resolveCodexConversationMigrationPlan(params) {
|
|
125
109
|
if (!params.codexSelected)
|
|
126
|
-
return
|
|
127
|
-
if (params.flagValue
|
|
128
|
-
return
|
|
110
|
+
return DISABLED_MIGRATION_PLAN;
|
|
111
|
+
if (params.flagValue === false)
|
|
112
|
+
return DISABLED_MIGRATION_PLAN;
|
|
113
|
+
const scope = await (0, codex_history_migration_1.buildConversationScope)(paths_1.codexDir);
|
|
114
|
+
if (scope.total === 0)
|
|
115
|
+
return DISABLED_MIGRATION_PLAN;
|
|
116
|
+
// `--migrate-conversations` keeps its historical meaning: a full pass.
|
|
117
|
+
if (params.flagValue === true) {
|
|
118
|
+
return { enabled: true, limit: null, files: scope.paths, total: scope.total };
|
|
119
|
+
}
|
|
120
|
+
// `--yes` / non-interactive still defaults to leaving history untouched.
|
|
129
121
|
if (params.skipPrompt || !process.stdin.isTTY || !process.stdout.isTTY)
|
|
130
|
-
return
|
|
131
|
-
|
|
122
|
+
return DISABLED_MIGRATION_PLAN;
|
|
123
|
+
const choice = await (0, prompts_1.promptConversationScope)({ total: scope.total, options: scope.options });
|
|
124
|
+
if (!choice)
|
|
125
|
+
return DISABLED_MIGRATION_PLAN;
|
|
126
|
+
const files = choice.limit === null ? scope.paths : scope.paths.slice(0, choice.limit);
|
|
127
|
+
return { enabled: files.length > 0, limit: choice.limit, files, total: scope.total };
|
|
132
128
|
}
|
|
133
129
|
async function detectCodexClient() {
|
|
134
130
|
if ((0, platform_1.hasCommand)("codex"))
|
|
@@ -147,10 +143,10 @@ async function detectCodexClient() {
|
|
|
147
143
|
}
|
|
148
144
|
async function writeCodexConfig(params) {
|
|
149
145
|
const configPath = node_path_1.default.join(paths_1.codexDir, "config.toml");
|
|
150
|
-
await
|
|
146
|
+
await promises_1.default.mkdir(paths_1.codexDir, { recursive: true });
|
|
151
147
|
let existing = "";
|
|
152
148
|
try {
|
|
153
|
-
existing = await
|
|
149
|
+
existing = await promises_1.default.readFile(configPath, "utf8");
|
|
154
150
|
}
|
|
155
151
|
catch (error) {
|
|
156
152
|
const err = error;
|
|
@@ -183,6 +179,6 @@ async function writeCodexConfig(params) {
|
|
|
183
179
|
exports.MANAGED_END,
|
|
184
180
|
]);
|
|
185
181
|
next = (0, fsx_1.appendManagedBlock)(next, managedBlock.trimEnd().split("\n"));
|
|
186
|
-
await
|
|
182
|
+
await promises_1.default.writeFile(configPath, next, "utf8");
|
|
187
183
|
return configPath;
|
|
188
184
|
}
|
|
@@ -11,10 +11,36 @@ export type StateDbProviderMigrationResult = {
|
|
|
11
11
|
failed: string[];
|
|
12
12
|
warning?: string;
|
|
13
13
|
};
|
|
14
|
+
export type SessionCandidate = {
|
|
15
|
+
path: string;
|
|
16
|
+
size: number;
|
|
17
|
+
mtimeMs: number;
|
|
18
|
+
};
|
|
19
|
+
export type ConversationScopeOption = {
|
|
20
|
+
limit: number | null;
|
|
21
|
+
count: number;
|
|
22
|
+
estSeconds: number;
|
|
23
|
+
};
|
|
24
|
+
export type ConversationScope = {
|
|
25
|
+
total: number;
|
|
26
|
+
paths: string[];
|
|
27
|
+
options: ConversationScopeOption[];
|
|
28
|
+
};
|
|
29
|
+
export type MigrationProgress = {
|
|
30
|
+
processed: number;
|
|
31
|
+
total: number;
|
|
32
|
+
rewritten: number;
|
|
33
|
+
elapsedMs: number;
|
|
34
|
+
etaSeconds: number;
|
|
35
|
+
currentFile: string;
|
|
36
|
+
};
|
|
37
|
+
export declare function buildConversationScope(codexHome: string): Promise<ConversationScope>;
|
|
14
38
|
export declare function migrateSessionProviders(params: {
|
|
15
39
|
codexHome: string;
|
|
16
40
|
migrationStateFile: string;
|
|
17
41
|
neutralizeSources: Set<string>;
|
|
42
|
+
files?: string[];
|
|
43
|
+
onProgress?: (progress: MigrationProgress) => void;
|
|
18
44
|
}): Promise<SessionProviderMigrationResult>;
|
|
19
45
|
export declare function migrateStateDbProviders(params: {
|
|
20
46
|
codexHome: string;
|