wtagent 0.2.2 → 0.3.0
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 +2 -2
- package/package.json +2 -2
- package/src/browser/base-web-adapter.js +4 -34
- package/src/browser/chatgpt-web-adapter.js +4 -217
- package/src/browser/deepseek-web-adapter.js +0 -106
- package/src/browser/glm-web-adapter.js +2 -89
- package/src/browser/grok-web-adapter.js +145 -0
- package/src/browser/kimi-web-adapter.js +2 -74
- package/src/browser/provider-registry.js +8 -50
- package/src/cli/i18n.js +949 -0
- package/src/cli/main.js +125 -186
- package/src/cli/prompt-input.js +349 -151
- package/src/cli/render-events.js +45 -35
- package/src/cli/self-update.js +10 -3
- package/src/runtime/agent-runtime.js +14 -51
- package/src/shared/limits.js +2 -9
- package/src/browser/mode-selection.js +0 -214
- package/src/cli/mode-choice.js +0 -25
package/src/cli/main.js
CHANGED
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
getProviderProfileDir,
|
|
12
12
|
isProviderProfileBasename,
|
|
13
13
|
listActiveProviderIds,
|
|
14
|
-
resolveCliProviderSelection,
|
|
15
14
|
resolveProvider,
|
|
16
15
|
} from "../browser/provider-registry.js";
|
|
17
16
|
import {
|
|
@@ -45,11 +44,7 @@ import {
|
|
|
45
44
|
ShellChatInput,
|
|
46
45
|
} from "./prompt-input.js";
|
|
47
46
|
import { createRenderer } from "./render-events.js";
|
|
48
|
-
import {
|
|
49
|
-
CHATGPT_MODE_CHOICES,
|
|
50
|
-
modeFromPromptChoice,
|
|
51
|
-
normalizeConfiguredMode,
|
|
52
|
-
} from "./mode-choice.js";
|
|
47
|
+
import { t } from "./i18n.js";
|
|
53
48
|
import { runSelfUpdate } from "./self-update.js";
|
|
54
49
|
import { runStartupChecks } from "./startup-notices.js";
|
|
55
50
|
|
|
@@ -58,10 +53,6 @@ import { runStartupChecks } from "./startup-notices.js";
|
|
|
58
53
|
// (each provider logs in independently); an explicit `--profile-dir` still
|
|
59
54
|
// overrides it. Defaults to the ChatGPT profile so callers that predate
|
|
60
55
|
// multi-provider support are unaffected.
|
|
61
|
-
function withResolvedProviderSelection(options) {
|
|
62
|
-
return { ...options, ...resolveCliProviderSelection(options) };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
56
|
function resolveRuntimePaths(options, providerId = DEFAULT_PROVIDER) {
|
|
66
57
|
const appDataDir = path.resolve(options.home ?? getAppDataDir());
|
|
67
58
|
return {
|
|
@@ -83,15 +74,12 @@ async function assertDirectory(directory) {
|
|
|
83
74
|
|
|
84
75
|
async function runLogin(options) {
|
|
85
76
|
assertNativeRuntimeSupported();
|
|
86
|
-
options = withResolvedProviderSelection(options);
|
|
87
77
|
const provider = resolveProvider(options.model ?? DEFAULT_PROVIDER);
|
|
88
78
|
const { profileDir } = resolveRuntimePaths(options, provider.id);
|
|
89
79
|
const { label, baseUrl } = provider;
|
|
90
80
|
for (;;) {
|
|
91
|
-
console.log(
|
|
92
|
-
console.log(
|
|
93
|
-
`This window has no CDP flags. Finish until ${label} shows your signed-in home/chat history and no Log in button.`,
|
|
94
|
-
);
|
|
81
|
+
console.log(t("login.openingProfile", { profileDir }));
|
|
82
|
+
console.log(t("login.finishSignIn", { provider: label }));
|
|
95
83
|
const browser = await launchNativeLoginBrowser({
|
|
96
84
|
profileDir,
|
|
97
85
|
chromePath: options.chromePath,
|
|
@@ -100,13 +88,12 @@ async function runLogin(options) {
|
|
|
100
88
|
|
|
101
89
|
try {
|
|
102
90
|
const answer = await promptForText({
|
|
103
|
-
message:
|
|
104
|
-
`After the signed-in ${label} home is visible, press Enter here to save and verify`,
|
|
91
|
+
message: t("login.pressEnterVerify", { provider: label }),
|
|
105
92
|
});
|
|
106
93
|
if (answer == null) {
|
|
107
94
|
return;
|
|
108
95
|
}
|
|
109
|
-
console.log("
|
|
96
|
+
console.log(t("login.savingProfile"));
|
|
110
97
|
await new Promise((resolve) => setTimeout(resolve, 1_000));
|
|
111
98
|
} finally {
|
|
112
99
|
await browser.close();
|
|
@@ -129,16 +116,14 @@ async function runLogin(options) {
|
|
|
129
116
|
}
|
|
130
117
|
}
|
|
131
118
|
if (authenticated) {
|
|
132
|
-
console.log(
|
|
119
|
+
console.log(t("login.verified", { provider: label }));
|
|
133
120
|
return;
|
|
134
121
|
}
|
|
135
122
|
} finally {
|
|
136
123
|
await verifier.close();
|
|
137
124
|
}
|
|
138
125
|
|
|
139
|
-
console.log(
|
|
140
|
-
`${label} is still in guest mode. Reopening native Chrome; complete the final ${label} sign-in/continue step.`,
|
|
141
|
-
);
|
|
126
|
+
console.log(t("login.stillGuest", { provider: label }));
|
|
142
127
|
}
|
|
143
128
|
}
|
|
144
129
|
|
|
@@ -150,14 +135,13 @@ async function runLogout(options) {
|
|
|
150
135
|
// `logout` targets one provider's profile; unknown ids are rejected but a
|
|
151
136
|
// "planned" provider is still allowed (its profile may exist from a prior
|
|
152
137
|
// login attempt), so use getProvider rather than resolveProvider.
|
|
153
|
-
options = withResolvedProviderSelection(options);
|
|
154
138
|
const provider = getProvider(options.model ?? DEFAULT_PROVIDER);
|
|
155
139
|
const { profileDir } = resolveRuntimePaths(options, provider.id);
|
|
156
140
|
const exists = await fs.stat(profileDir)
|
|
157
141
|
.then((stat) => stat.isDirectory())
|
|
158
142
|
.catch(() => false);
|
|
159
143
|
if (!exists) {
|
|
160
|
-
console.log(
|
|
144
|
+
console.log(t("logout.none", { profileDir }));
|
|
161
145
|
return;
|
|
162
146
|
}
|
|
163
147
|
|
|
@@ -168,30 +152,29 @@ async function runLogout(options) {
|
|
|
168
152
|
.then((stat) => stat.isDirectory())
|
|
169
153
|
.catch(() => false);
|
|
170
154
|
if (!looksLikeProfile && !isProviderProfileBasename(path.basename(profileDir))) {
|
|
171
|
-
throw new Error(
|
|
172
|
-
`Refusing to delete ${profileDir}: it does not look like a wtagent Chrome profile.`,
|
|
173
|
-
);
|
|
155
|
+
throw new Error(t("logout.refuse", { profileDir }));
|
|
174
156
|
}
|
|
175
157
|
|
|
176
158
|
if (!options.yes) {
|
|
177
159
|
const confirmed = await confirm({
|
|
178
|
-
message:
|
|
179
|
-
|
|
180
|
-
|
|
160
|
+
message: t("logout.confirm", {
|
|
161
|
+
provider: provider.label,
|
|
162
|
+
profileDir,
|
|
163
|
+
}),
|
|
181
164
|
default: false,
|
|
182
165
|
});
|
|
183
166
|
if (!confirmed) {
|
|
184
|
-
console.log("
|
|
167
|
+
console.log(t("logout.cancelled"));
|
|
185
168
|
return;
|
|
186
169
|
}
|
|
187
170
|
}
|
|
188
171
|
|
|
189
172
|
await fs.rm(profileDir, { recursive: true, force: true });
|
|
190
|
-
console.log(
|
|
173
|
+
console.log(t("logout.done", { profileDir }));
|
|
191
174
|
console.log(
|
|
192
175
|
provider.id === DEFAULT_PROVIDER
|
|
193
|
-
? "
|
|
194
|
-
:
|
|
176
|
+
? t("logout.loginAgainDefault")
|
|
177
|
+
: t("logout.loginAgainProvider", { provider: provider.id }),
|
|
195
178
|
);
|
|
196
179
|
}
|
|
197
180
|
|
|
@@ -231,6 +214,8 @@ class ConversationRunner {
|
|
|
231
214
|
// The profile dir and adapter follow from it, so resumes reuse the right
|
|
232
215
|
// login even when the CLI is invoked without --model.
|
|
233
216
|
const provider = resolveProvider(session.state.provider ?? DEFAULT_PROVIDER);
|
|
217
|
+
this.provider = provider;
|
|
218
|
+
this.interactive = interactive;
|
|
234
219
|
this.paths = resolveRuntimePaths(options, provider.id);
|
|
235
220
|
this.limits = resolveLimits({
|
|
236
221
|
modelTurnTimeoutMs: options.modelTurnTimeoutMs,
|
|
@@ -267,20 +252,45 @@ class ConversationRunner {
|
|
|
267
252
|
policy: new PolicyEngine({ store: this.approvalStore }),
|
|
268
253
|
session: this.session,
|
|
269
254
|
limits: this.limits,
|
|
255
|
+
postAuthSetup: (
|
|
256
|
+
this.interactive
|
|
257
|
+
? async ({ adapter }) => {
|
|
258
|
+
this.renderer.stopSpinner();
|
|
259
|
+
await adapter.restoreWindow?.();
|
|
260
|
+
try {
|
|
261
|
+
const answer = await promptForText({
|
|
262
|
+
message: t("model.chooseInBrowser", {
|
|
263
|
+
provider: this.provider.label,
|
|
264
|
+
}),
|
|
265
|
+
});
|
|
266
|
+
if (answer == null) {
|
|
267
|
+
throw new Error(t("model.setupCancelled"));
|
|
268
|
+
}
|
|
269
|
+
} finally {
|
|
270
|
+
await adapter.minimizeWindow?.();
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
: null
|
|
274
|
+
),
|
|
270
275
|
approval: async ({ toolCall, reasons }) => {
|
|
271
276
|
this.renderer.stopSpinner();
|
|
272
|
-
console.log(
|
|
277
|
+
console.log(
|
|
278
|
+
`\n${"\x1b[33m"}${t("approval.required", { tool: toolCall.name })}${"\x1b[0m"}`,
|
|
279
|
+
);
|
|
273
280
|
for (const reason of reasons) {
|
|
274
281
|
console.log(`- ${reason}`);
|
|
275
282
|
}
|
|
276
283
|
console.log(JSON.stringify(toolCall.args, null, 2));
|
|
277
284
|
const choice = await select({
|
|
278
|
-
message: "
|
|
285
|
+
message: t("approval.how"),
|
|
279
286
|
choices: [
|
|
280
|
-
{ name: "
|
|
281
|
-
{
|
|
282
|
-
|
|
283
|
-
|
|
287
|
+
{ name: t("approval.once"), value: "once" },
|
|
288
|
+
{
|
|
289
|
+
name: t("approval.alwaysTool", { tool: toolCall.name }),
|
|
290
|
+
value: "always-tool",
|
|
291
|
+
},
|
|
292
|
+
{ name: t("approval.alwaysAll"), value: "always-all" },
|
|
293
|
+
{ name: t("approval.deny"), value: "deny" },
|
|
284
294
|
],
|
|
285
295
|
});
|
|
286
296
|
if (choice === "deny") {
|
|
@@ -289,17 +299,16 @@ class ConversationRunner {
|
|
|
289
299
|
if (choice === "always-tool") {
|
|
290
300
|
this.approvalStore.setAlwaysAllowedTool(toolCall.name);
|
|
291
301
|
await this.approvalStore.save();
|
|
292
|
-
console.log(
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
);
|
|
302
|
+
console.log(t("approval.savedTool", {
|
|
303
|
+
tool: toolCall.name,
|
|
304
|
+
file: this.approvalStore.filePath,
|
|
305
|
+
}));
|
|
296
306
|
} else if (choice === "always-all") {
|
|
297
307
|
this.approvalStore.setAlwaysAllowAll();
|
|
298
308
|
await this.approvalStore.save();
|
|
299
|
-
console.log(
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
);
|
|
309
|
+
console.log(t("approval.savedAll", {
|
|
310
|
+
file: this.approvalStore.filePath,
|
|
311
|
+
}));
|
|
303
312
|
}
|
|
304
313
|
return true;
|
|
305
314
|
},
|
|
@@ -430,18 +439,18 @@ async function promptToResolveProfileLock(error) {
|
|
|
430
439
|
const pid = Number(error.details?.pid);
|
|
431
440
|
console.log(`\n${"\x1b[33m"}${error.message}${"\x1b[0m"}`);
|
|
432
441
|
const choice = await promptForSelect({
|
|
433
|
-
message: "
|
|
442
|
+
message: t("profileLock.how"),
|
|
434
443
|
choices: [
|
|
435
|
-
{ name: "
|
|
436
|
-
{ name: "
|
|
437
|
-
{ name: "
|
|
444
|
+
{ name: t("profileLock.kill"), value: "kill" },
|
|
445
|
+
{ name: t("profileLock.retry"), value: "retry" },
|
|
446
|
+
{ name: t("profileLock.quit"), value: "quit" },
|
|
438
447
|
],
|
|
439
448
|
});
|
|
440
449
|
if (choice == null || choice === "quit") {
|
|
441
450
|
return false;
|
|
442
451
|
}
|
|
443
452
|
if (choice === "kill" && Number.isSafeInteger(pid) && pid > 0) {
|
|
444
|
-
console.log(
|
|
453
|
+
console.log(t("profileLock.stopping", { pid }));
|
|
445
454
|
try {
|
|
446
455
|
process.kill(pid, "SIGTERM");
|
|
447
456
|
} catch {
|
|
@@ -477,17 +486,17 @@ async function executeSession({
|
|
|
477
486
|
|
|
478
487
|
const onInterrupt = () => {
|
|
479
488
|
if (runner.interrupted) {
|
|
480
|
-
console.log("
|
|
489
|
+
console.log(`\n${t("session.forceQuit")}`);
|
|
481
490
|
// process.exit skips the finally block, so repeat the session/resume
|
|
482
491
|
// hint here — the user should always know how to continue.
|
|
483
|
-
console.log(
|
|
492
|
+
console.log(t("session.savedAt", { directory: session.directory }));
|
|
484
493
|
printResumeHint(session.sessionId);
|
|
485
494
|
process.exit(130);
|
|
486
495
|
}
|
|
487
496
|
runner.interrupted = true;
|
|
488
497
|
runner.adapter.escCancelRequested = true;
|
|
489
498
|
runner.renderer.stopSpinner();
|
|
490
|
-
console.log("
|
|
499
|
+
console.log(`\n${t("session.ctrlC")}`);
|
|
491
500
|
process.exitCode = 130;
|
|
492
501
|
};
|
|
493
502
|
process.on("SIGINT", onInterrupt);
|
|
@@ -535,7 +544,7 @@ async function executeSession({
|
|
|
535
544
|
if (!interactive) {
|
|
536
545
|
throw result.error;
|
|
537
546
|
}
|
|
538
|
-
runner.renderer.hint("
|
|
547
|
+
runner.renderer.hint(t("session.cancelledHint"));
|
|
539
548
|
turnResume = true;
|
|
540
549
|
turnInPlaceRecovery = false;
|
|
541
550
|
continue;
|
|
@@ -545,7 +554,9 @@ async function executeSession({
|
|
|
545
554
|
throw result.error;
|
|
546
555
|
}
|
|
547
556
|
runner.renderer.hint(
|
|
548
|
-
|
|
557
|
+
t("session.recoveryHint", {
|
|
558
|
+
provider: runner.renderer.providerLabel,
|
|
559
|
+
}),
|
|
549
560
|
);
|
|
550
561
|
const next = await promptForNextMessage(runner, activeChatInput);
|
|
551
562
|
if (next == null) {
|
|
@@ -574,7 +585,7 @@ async function executeSession({
|
|
|
574
585
|
(item) => item.status === "running",
|
|
575
586
|
);
|
|
576
587
|
if (running.length > 0) {
|
|
577
|
-
runner.renderer.hint("
|
|
588
|
+
runner.renderer.hint(t("session.processesRunning"));
|
|
578
589
|
for (const item of running) {
|
|
579
590
|
runner.renderer.hint(
|
|
580
591
|
` ${item.processId} pid=${item.pid} ${item.detectedUrls.join(" ")}`,
|
|
@@ -599,7 +610,7 @@ async function executeSession({
|
|
|
599
610
|
process.removeListener("SIGINT", onInterrupt);
|
|
600
611
|
activeChatInput?.close();
|
|
601
612
|
await runner.close({ keepBrowser: runFailed });
|
|
602
|
-
console.log(
|
|
613
|
+
console.log(t("session.savedAt", { directory: session.directory }));
|
|
603
614
|
printResumeHint(session.sessionId);
|
|
604
615
|
}
|
|
605
616
|
}
|
|
@@ -610,7 +621,7 @@ function printResumeHint(sessionId) {
|
|
|
610
621
|
const rule = "─".repeat(48);
|
|
611
622
|
console.log("");
|
|
612
623
|
console.log(rule);
|
|
613
|
-
console.log(
|
|
624
|
+
console.log(t("session.resumeWith", { sessionId }));
|
|
614
625
|
console.log(rule);
|
|
615
626
|
console.log("");
|
|
616
627
|
}
|
|
@@ -624,7 +635,7 @@ async function promptForNextMessage(runner, chatInput) {
|
|
|
624
635
|
const answer = chatInput
|
|
625
636
|
? await chatInput.read()
|
|
626
637
|
: await promptForText({
|
|
627
|
-
message: "
|
|
638
|
+
message: t("task.youPrompt"),
|
|
628
639
|
theme: { prefix: "" },
|
|
629
640
|
});
|
|
630
641
|
if (answer == null) {
|
|
@@ -652,12 +663,16 @@ async function resolveMessageAttachments(runner, text) {
|
|
|
652
663
|
const { files, missing } = await extractAtMentions(text, projectRoot);
|
|
653
664
|
if (files.length > 0) {
|
|
654
665
|
runner.renderer.hint(
|
|
655
|
-
|
|
666
|
+
t("attachment.attaching", {
|
|
667
|
+
files: files.map((file) => file.name).join(", "),
|
|
668
|
+
}),
|
|
656
669
|
);
|
|
657
670
|
}
|
|
658
671
|
if (missing.length > 0) {
|
|
659
672
|
runner.renderer.hint(
|
|
660
|
-
|
|
673
|
+
t("attachment.notAttached", {
|
|
674
|
+
details: missing.map((m) => `${m.requested}: ${m.reason}`).join("; "),
|
|
675
|
+
}),
|
|
661
676
|
);
|
|
662
677
|
}
|
|
663
678
|
return files;
|
|
@@ -669,7 +684,7 @@ function isInteractiveSession(options) {
|
|
|
669
684
|
|
|
670
685
|
async function promptForUpdate({ currentVersion, latest }) {
|
|
671
686
|
return await promptForConfirm({
|
|
672
|
-
message:
|
|
687
|
+
message: t("update.prompt", { currentVersion, latest }),
|
|
673
688
|
default: true,
|
|
674
689
|
});
|
|
675
690
|
}
|
|
@@ -692,7 +707,6 @@ async function runUpdate() {
|
|
|
692
707
|
|
|
693
708
|
async function runAgent(taskParts, options) {
|
|
694
709
|
assertNativeRuntimeSupported();
|
|
695
|
-
options = withResolvedProviderSelection(options);
|
|
696
710
|
const projectRoot = path.resolve(options.project ?? process.cwd());
|
|
697
711
|
await assertDirectory(projectRoot);
|
|
698
712
|
|
|
@@ -711,35 +725,6 @@ async function runAgent(taskParts, options) {
|
|
|
711
725
|
printChatBanner(projectRoot, provider);
|
|
712
726
|
}
|
|
713
727
|
|
|
714
|
-
// Mode selection is abstracted per provider: some prompt (ChatGPT's
|
|
715
|
-
// Pro/Current), others silently apply their configured default at
|
|
716
|
-
// conversation start (DeepSeek → 专家模式 + 深度思考). The chosen value is
|
|
717
|
-
// handed to the adapter's selectMode() by the runtime.
|
|
718
|
-
let requestedMode;
|
|
719
|
-
if (!provider.promptsForMode) {
|
|
720
|
-
if (options.mode != null) {
|
|
721
|
-
console.log(`Note: --mode is ignored for ${provider.label}.`);
|
|
722
|
-
}
|
|
723
|
-
requestedMode = provider.defaultMode;
|
|
724
|
-
} else if (options.mode != null) {
|
|
725
|
-
requestedMode = normalizeConfiguredMode(options.mode);
|
|
726
|
-
} else if (interactive) {
|
|
727
|
-
const modeChoice = await promptForSelect({
|
|
728
|
-
message: `${provider.label} mode`,
|
|
729
|
-
choices: CHATGPT_MODE_CHOICES,
|
|
730
|
-
});
|
|
731
|
-
if (modeChoice == null) {
|
|
732
|
-
chatInput?.close();
|
|
733
|
-
console.log("");
|
|
734
|
-
return null;
|
|
735
|
-
}
|
|
736
|
-
requestedMode = modeFromPromptChoice(modeChoice);
|
|
737
|
-
} else {
|
|
738
|
-
// Non-interactive callers cannot answer a picker. Preserve the current web
|
|
739
|
-
// setting unless they explicitly opt into `--mode Pro`.
|
|
740
|
-
requestedMode = null;
|
|
741
|
-
}
|
|
742
|
-
|
|
743
728
|
// In interactive mode an initial task is optional: the user can just start
|
|
744
729
|
// typing at the prompt. In one-shot mode a task is required.
|
|
745
730
|
let task = taskParts.join(" ").trim();
|
|
@@ -747,8 +732,8 @@ async function runAgent(taskParts, options) {
|
|
|
747
732
|
const initialMessage = interactive
|
|
748
733
|
? await readChatMessage(() => chatInput.read())
|
|
749
734
|
: await promptForText({
|
|
750
|
-
message: "
|
|
751
|
-
validate: (value) => value.trim() ? true : "
|
|
735
|
+
message: t("task.prompt"),
|
|
736
|
+
validate: (value) => value.trim() ? true : t("task.required"),
|
|
752
737
|
});
|
|
753
738
|
if (initialMessage == null) {
|
|
754
739
|
chatInput?.close();
|
|
@@ -767,7 +752,7 @@ async function runAgent(taskParts, options) {
|
|
|
767
752
|
task,
|
|
768
753
|
projectRoot,
|
|
769
754
|
provider: provider.id,
|
|
770
|
-
mode:
|
|
755
|
+
mode: null,
|
|
771
756
|
});
|
|
772
757
|
|
|
773
758
|
// Resolve @file attachments in the opening task, if any. The task itself is
|
|
@@ -778,12 +763,14 @@ async function runAgent(taskParts, options) {
|
|
|
778
763
|
const { files: found, missing } = await extractAtMentions(task, projectRoot);
|
|
779
764
|
files = found;
|
|
780
765
|
if (found.length > 0) {
|
|
781
|
-
console.log(
|
|
766
|
+
console.log(t("attachment.attaching", {
|
|
767
|
+
files: found.map((file) => file.name).join(", "),
|
|
768
|
+
}));
|
|
782
769
|
}
|
|
783
770
|
if (missing.length > 0) {
|
|
784
|
-
console.log(
|
|
785
|
-
|
|
786
|
-
);
|
|
771
|
+
console.log(t("attachment.notAttached", {
|
|
772
|
+
details: missing.map((m) => `${m.requested}: ${m.reason}`).join("; "),
|
|
773
|
+
}));
|
|
787
774
|
}
|
|
788
775
|
}
|
|
789
776
|
|
|
@@ -796,7 +783,7 @@ function printChatBanner(projectRoot, provider) {
|
|
|
796
783
|
const RESET = "\x1b[0m";
|
|
797
784
|
console.log("");
|
|
798
785
|
console.log(`${CYAN}WTAgent${RESET} ${DIM}· ${provider.label} · ${projectRoot}${RESET}`);
|
|
799
|
-
console.log(`${DIM}
|
|
786
|
+
console.log(`${DIM}${t("banner.controls")}${RESET}`);
|
|
800
787
|
console.log("");
|
|
801
788
|
}
|
|
802
789
|
|
|
@@ -819,7 +806,6 @@ async function loadSession(paths, sessionId) {
|
|
|
819
806
|
|
|
820
807
|
async function runResume(sessionId, instructionParts, options) {
|
|
821
808
|
assertNativeRuntimeSupported();
|
|
822
|
-
options = withResolvedProviderSelection(options);
|
|
823
809
|
// Loading only needs the sessions dir (provider-independent); the provider is
|
|
824
810
|
// then read from the session so the run reuses the right adapter + profile.
|
|
825
811
|
const paths = resolveRuntimePaths(options);
|
|
@@ -842,36 +828,6 @@ async function runResume(sessionId, instructionParts, options) {
|
|
|
842
828
|
return null;
|
|
843
829
|
}
|
|
844
830
|
|
|
845
|
-
// Like a fresh run, interactive resumes offer a mode choice (prompting
|
|
846
|
-
// providers only), defaulting to the mode the conversation is already on.
|
|
847
|
-
// `--mode` overrides it and also works non-interactively. This matters after
|
|
848
|
-
// a usage limit, where switching thinking levels can get past the block.
|
|
849
|
-
// Non-prompting providers (e.g. DeepSeek) keep the mode the existing
|
|
850
|
-
// conversation was created with — it does not need re-asserting on resume.
|
|
851
|
-
let requestedMode;
|
|
852
|
-
if (!provider.promptsForMode) {
|
|
853
|
-
requestedMode = null;
|
|
854
|
-
} else if (options.mode != null) {
|
|
855
|
-
requestedMode = normalizeConfiguredMode(options.mode);
|
|
856
|
-
} else if (
|
|
857
|
-
!options.once
|
|
858
|
-
&& process.stdin.isTTY
|
|
859
|
-
&& process.stdout.isTTY
|
|
860
|
-
) {
|
|
861
|
-
const modeChoice = await promptForSelect({
|
|
862
|
-
message: `${provider.label} mode`,
|
|
863
|
-
choices: CHATGPT_MODE_CHOICES,
|
|
864
|
-
default: session.state.activeMode === "Pro" ? "pro" : "current",
|
|
865
|
-
});
|
|
866
|
-
if (modeChoice == null) {
|
|
867
|
-
console.log("");
|
|
868
|
-
return null;
|
|
869
|
-
}
|
|
870
|
-
requestedMode = modeFromPromptChoice(modeChoice);
|
|
871
|
-
} else {
|
|
872
|
-
requestedMode = null;
|
|
873
|
-
}
|
|
874
|
-
|
|
875
831
|
const instruction = instructionParts.join(" ").trim();
|
|
876
832
|
let files = [];
|
|
877
833
|
if (instruction) {
|
|
@@ -879,12 +835,14 @@ async function runResume(sessionId, instructionParts, options) {
|
|
|
879
835
|
const { files: found, missing } = await extractAtMentions(instruction, projectRoot);
|
|
880
836
|
files = found;
|
|
881
837
|
if (found.length > 0) {
|
|
882
|
-
console.log(
|
|
838
|
+
console.log(t("attachment.attaching", {
|
|
839
|
+
files: found.map((file) => file.name).join(", "),
|
|
840
|
+
}));
|
|
883
841
|
}
|
|
884
842
|
if (missing.length > 0) {
|
|
885
|
-
console.log(
|
|
886
|
-
|
|
887
|
-
);
|
|
843
|
+
console.log(t("attachment.notAttached", {
|
|
844
|
+
details: missing.map((m) => `${m.requested}: ${m.reason}`).join("; "),
|
|
845
|
+
}));
|
|
888
846
|
}
|
|
889
847
|
await session.appendInstruction(instruction, { files });
|
|
890
848
|
}
|
|
@@ -895,7 +853,6 @@ async function runResume(sessionId, instructionParts, options) {
|
|
|
895
853
|
resume: true,
|
|
896
854
|
instruction: instruction || null,
|
|
897
855
|
files,
|
|
898
|
-
mode: requestedMode,
|
|
899
856
|
});
|
|
900
857
|
}
|
|
901
858
|
|
|
@@ -923,7 +880,7 @@ async function runStatus(sessionId, options) {
|
|
|
923
880
|
.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt))
|
|
924
881
|
.slice(0, 20);
|
|
925
882
|
if (sessions.length === 0) {
|
|
926
|
-
console.log("
|
|
883
|
+
console.log(t("status.none"));
|
|
927
884
|
return;
|
|
928
885
|
}
|
|
929
886
|
|
|
@@ -969,74 +926,56 @@ async function runExport(sessionId, options) {
|
|
|
969
926
|
|
|
970
927
|
const program = new Command()
|
|
971
928
|
.name("wtagent")
|
|
972
|
-
.description("
|
|
929
|
+
.description(t("app.description"))
|
|
973
930
|
.version(getPackageVersion())
|
|
974
|
-
.option("--home <path>", "
|
|
975
|
-
.option("--profile-dir <path>", "
|
|
976
|
-
.option("--chrome-path <path>", "
|
|
977
|
-
.option("-C, --project <path>", "
|
|
931
|
+
.option("--home <path>", t("option.home"))
|
|
932
|
+
.option("--profile-dir <path>", t("option.profileDir"))
|
|
933
|
+
.option("--chrome-path <path>", t("option.chromePath"))
|
|
934
|
+
.option("-C, --project <path>", t("option.project"), process.cwd())
|
|
978
935
|
.option(
|
|
979
936
|
"--model <provider>",
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
"ChatGPT mode (Pro/Current), or a provider alias such as kimi",
|
|
985
|
-
)
|
|
986
|
-
.option(
|
|
987
|
-
"--once",
|
|
988
|
-
"Run a single request and exit instead of a conversation",
|
|
989
|
-
false,
|
|
990
|
-
)
|
|
991
|
-
.option(
|
|
992
|
-
"--model-turn-timeout-ms <milliseconds>",
|
|
993
|
-
"Maximum wait for one model response (default: 10 minutes; 16 minutes for ChatGPT Pro)",
|
|
994
|
-
)
|
|
995
|
-
.option(
|
|
996
|
-
"--no-minimize",
|
|
997
|
-
"Keep the Chrome window visible instead of minimizing it",
|
|
998
|
-
)
|
|
999
|
-
.option("--debug", "Write browser diagnostics", false)
|
|
1000
|
-
.argument(
|
|
1001
|
-
"[task...]",
|
|
1002
|
-
"Initial request (optional; you can also type at the prompt)",
|
|
937
|
+
t("option.model", {
|
|
938
|
+
providers: listActiveProviderIds().join(", "),
|
|
939
|
+
defaultProvider: DEFAULT_PROVIDER,
|
|
940
|
+
}),
|
|
1003
941
|
)
|
|
942
|
+
.option("--once", t("option.once"), false)
|
|
943
|
+
.option("--model-turn-timeout-ms <milliseconds>", t("option.timeout"))
|
|
944
|
+
.option("--no-minimize", t("option.noMinimize"))
|
|
945
|
+
.option("--debug", t("option.debug"), false)
|
|
946
|
+
.argument("[task...]", t("argument.task"))
|
|
1004
947
|
.action(async (task, _, command) => {
|
|
1005
948
|
await runAgent(task, command.optsWithGlobals());
|
|
1006
949
|
});
|
|
1007
950
|
|
|
1008
951
|
program
|
|
1009
952
|
.command("update")
|
|
1010
|
-
.description("
|
|
953
|
+
.description(t("command.update"))
|
|
1011
954
|
.action(async () => runUpdate());
|
|
1012
955
|
|
|
1013
956
|
program
|
|
1014
957
|
.command("doctor")
|
|
1015
|
-
.description("
|
|
958
|
+
.description(t("command.doctor"))
|
|
1016
959
|
.action(async (_, command) => runDoctor(command.optsWithGlobals()));
|
|
1017
960
|
|
|
1018
961
|
program
|
|
1019
962
|
.command("login")
|
|
1020
|
-
.description("
|
|
963
|
+
.description(t("command.login"))
|
|
1021
964
|
.action(async (_, command) => runLogin(command.optsWithGlobals()));
|
|
1022
965
|
|
|
1023
966
|
program
|
|
1024
967
|
.command("logout")
|
|
1025
|
-
.description("
|
|
1026
|
-
.option("--yes", "
|
|
968
|
+
.description(t("command.logout"))
|
|
969
|
+
.option("--yes", t("option.yes"), false)
|
|
1027
970
|
.action(async (options, command) => {
|
|
1028
971
|
await runLogout({ ...command.optsWithGlobals(), ...options });
|
|
1029
972
|
});
|
|
1030
973
|
|
|
1031
974
|
program
|
|
1032
975
|
.command("resume")
|
|
1033
|
-
.description("
|
|
1034
|
-
.argument("<session-id>", "
|
|
1035
|
-
.argument("[instruction...]", "
|
|
1036
|
-
.option(
|
|
1037
|
-
"--mode <name>",
|
|
1038
|
-
"ChatGPT mode (Pro/Current), or a provider alias such as kimi",
|
|
1039
|
-
)
|
|
976
|
+
.description(t("command.resume"))
|
|
977
|
+
.argument("<session-id>", t("argument.sessionId"))
|
|
978
|
+
.argument("[instruction...]", t("argument.instruction"))
|
|
1040
979
|
.action(async (sessionId, instruction, _, command) => {
|
|
1041
980
|
await runResume(
|
|
1042
981
|
sessionId,
|
|
@@ -1047,18 +986,18 @@ program
|
|
|
1047
986
|
|
|
1048
987
|
program
|
|
1049
988
|
.command("status")
|
|
1050
|
-
.description("
|
|
1051
|
-
.argument("[session-id]", "
|
|
989
|
+
.description(t("command.status"))
|
|
990
|
+
.argument("[session-id]", t("argument.sessionId"))
|
|
1052
991
|
.action(async (sessionId, _, command) => {
|
|
1053
992
|
await runStatus(sessionId, command.optsWithGlobals());
|
|
1054
993
|
});
|
|
1055
994
|
|
|
1056
995
|
program
|
|
1057
996
|
.command("export")
|
|
1058
|
-
.description("
|
|
1059
|
-
.argument("<session-id>", "
|
|
1060
|
-
.option("--format <name>", "
|
|
1061
|
-
.option("-o, --output <path>", "
|
|
997
|
+
.description(t("command.export"))
|
|
998
|
+
.argument("<session-id>", t("argument.sessionId"))
|
|
999
|
+
.option("--format <name>", t("option.format"), "codex")
|
|
1000
|
+
.option("-o, --output <path>", t("option.output"))
|
|
1062
1001
|
.action(async (sessionId, options, command) => {
|
|
1063
1002
|
await runExport(sessionId, { ...command.optsWithGlobals(), ...options });
|
|
1064
1003
|
});
|