@keyou007/agent-bot 0.1.14-alpha.1 → 0.1.14-alpha.3
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/CHANGELOG.md +19 -1
- package/README.md +48 -15
- package/README.zh.md +48 -15
- package/config.example.yaml +1 -1
- package/dist/cli/AutostartManager.js +431 -0
- package/dist/cli/AutostartManager.js.map +1 -0
- package/dist/cli/FeishuAppConfiguration.js +21 -13
- package/dist/cli/FeishuAppConfiguration.js.map +1 -1
- package/dist/cli/Initializer.js +35 -0
- package/dist/cli/Initializer.js.map +1 -1
- package/dist/cli/SelfUpdateRunner.js +552 -0
- package/dist/cli/SelfUpdateRunner.js.map +1 -0
- package/dist/cli/SelfUpdater.js +431 -0
- package/dist/cli/SelfUpdater.js.map +1 -0
- package/dist/cli/autostartOutput.js +63 -0
- package/dist/cli/autostartOutput.js.map +1 -0
- package/dist/cli/controlProtocol.js.map +1 -1
- package/dist/cli/help.js +94 -18
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/taskGroupOptions.js +14 -11
- package/dist/cli/taskGroupOptions.js.map +1 -1
- package/dist/cli/taskListOutput.js +3 -0
- package/dist/cli/taskListOutput.js.map +1 -1
- package/dist/cli/taskTarget.js +99 -0
- package/dist/cli/taskTarget.js.map +1 -0
- package/dist/cli.js +661 -58
- package/dist/cli.js.map +1 -1
- package/dist/codex/CodexRuntime.js +8 -2
- package/dist/codex/CodexRuntime.js.map +1 -1
- package/dist/commands/CommandRouter.js +16 -1
- package/dist/commands/CommandRouter.js.map +1 -1
- package/dist/feishu/CardRenderer.js +266 -38
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/feishu/FeishuMarkdown.js +25 -1
- package/dist/feishu/FeishuMarkdown.js.map +1 -1
- package/dist/feishu/FeishuMessageClient.js +16 -0
- package/dist/feishu/FeishuMessageClient.js.map +1 -1
- package/dist/feishu/FeishuTurnPresenter.js +42 -6
- package/dist/feishu/FeishuTurnPresenter.js.map +1 -1
- package/dist/index.js +136 -2
- package/dist/index.js.map +1 -1
- package/dist/presentation/OutboundRouter.js +7 -0
- package/dist/presentation/OutboundRouter.js.map +1 -1
- package/dist/presentation/TurnStateReducer.js +21 -7
- package/dist/presentation/TurnStateReducer.js.map +1 -1
- package/dist/proxy/ProxySessionController.js +537 -13
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/state/StateStore.js +53 -0
- package/dist/state/StateStore.js.map +1 -1
- package/docs/technical-reference.md +17 -7
- package/docs/technical-reference.zh.md +17 -7
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -2
- package/skills/agent-bot/SKILL.md +109 -188
- package/src/cli/AutostartManager.ts +579 -0
- package/src/cli/FeishuAppConfiguration.ts +26 -7
- package/src/cli/Initializer.ts +57 -0
- package/src/cli/SelfUpdateRunner.ts +817 -0
- package/src/cli/SelfUpdater.ts +688 -0
- package/src/cli/autostartOutput.ts +75 -0
- package/src/cli/controlProtocol.ts +119 -1
- package/src/cli/help.ts +94 -18
- package/src/cli/taskGroupOptions.ts +27 -17
- package/src/cli/taskListOutput.ts +10 -0
- package/src/cli/taskTarget.ts +168 -0
- package/src/cli.ts +809 -65
- package/src/codex/CodexRuntime.ts +8 -2
- package/src/commands/CommandRouter.ts +14 -1
- package/src/commands/commandTypes.ts +2 -0
- package/src/feishu/CardRenderer.ts +313 -35
- package/src/feishu/FeishuMarkdown.ts +27 -1
- package/src/feishu/FeishuMessageClient.ts +33 -0
- package/src/feishu/FeishuTurnPresenter.ts +43 -6
- package/src/feishu/types.ts +1 -0
- package/src/index.ts +174 -2
- package/src/presentation/OutboundRouter.ts +7 -0
- package/src/presentation/TurnStateReducer.ts +21 -7
- package/src/proxy/ProxySessionController.ts +661 -16
- package/src/runtime/types.ts +1 -0
- package/src/state/StateStore.ts +56 -0
package/src/cli.ts
CHANGED
|
@@ -8,8 +8,17 @@ import { sendControlRequest, isServerReachable } from "./cli/LocalControlClient.
|
|
|
8
8
|
import {
|
|
9
9
|
controlEndpoint,
|
|
10
10
|
type ControlResponse,
|
|
11
|
+
type TaskAgentControlData,
|
|
12
|
+
type TaskDirectoryControlData,
|
|
13
|
+
type TaskDismissControlData,
|
|
14
|
+
type TaskForkControlData,
|
|
15
|
+
type TaskGoalControlData,
|
|
11
16
|
type TaskGroupControlData,
|
|
17
|
+
type TaskMuteControlData,
|
|
18
|
+
type TaskSettingsControlData,
|
|
19
|
+
type TaskShellControlData,
|
|
12
20
|
type TaskStatusControlData,
|
|
21
|
+
type TaskTurnsControlData,
|
|
13
22
|
} from "./cli/controlProtocol.js";
|
|
14
23
|
import {
|
|
15
24
|
acquireInitializationLock,
|
|
@@ -17,10 +26,14 @@ import {
|
|
|
17
26
|
initializeAgentBot,
|
|
18
27
|
readConfiguredAgentSelection,
|
|
19
28
|
readFeishuCredentials,
|
|
29
|
+
readGroupMessageResponseMode,
|
|
20
30
|
shouldCreateFeishuApp,
|
|
21
31
|
shouldConfigureAgentsDuringInitialization,
|
|
32
|
+
shouldConfigureGroupMessagesDuringInitialization,
|
|
22
33
|
writeConfiguredAgentSelection,
|
|
23
34
|
writeFeishuCredentials,
|
|
35
|
+
writeGroupMessageResponseMode,
|
|
36
|
+
type GroupMessageResponseMode,
|
|
24
37
|
type InitializationResult,
|
|
25
38
|
type InitializationStatus,
|
|
26
39
|
} from "./cli/Initializer.js";
|
|
@@ -77,13 +90,18 @@ import { taskChatRoute } from "./cli/taskChatRoute.js";
|
|
|
77
90
|
import {
|
|
78
91
|
currentAppServerThreadIds,
|
|
79
92
|
formatTaskList,
|
|
80
|
-
resolveCurrentTask,
|
|
81
93
|
taskStateLabel,
|
|
82
94
|
} from "./cli/taskListOutput.js";
|
|
95
|
+
import {
|
|
96
|
+
resolveCurrentTaskFromEnvironment,
|
|
97
|
+
resolveTask,
|
|
98
|
+
resolveTaskCommandTarget,
|
|
99
|
+
} from "./cli/taskTarget.js";
|
|
83
100
|
import { isThreadContextKey } from "./feishu/contextKey.js";
|
|
84
101
|
import { refreshedSystemEnvironment } from "./supervision/systemEnvironment.js";
|
|
85
102
|
import {
|
|
86
103
|
parseTaskForkGroupOptions,
|
|
104
|
+
parseTaskNewOptions,
|
|
87
105
|
parseTaskNewGroupOptions,
|
|
88
106
|
} from "./cli/taskGroupOptions.js";
|
|
89
107
|
import { StateStore, type SessionRecord } from "./state/StateStore.js";
|
|
@@ -93,6 +111,20 @@ import {
|
|
|
93
111
|
resolveSupervisorDiagnosticsPaths,
|
|
94
112
|
} from "./supervision/SupervisorDiagnostics.js";
|
|
95
113
|
import { agentBotHome, defaultSqlitePath } from "./config/paths.js";
|
|
114
|
+
import {
|
|
115
|
+
finalizeSelfUpdatePlan,
|
|
116
|
+
launchSelfUpdateRunner,
|
|
117
|
+
parseSelfUpdateOptions,
|
|
118
|
+
prepareSelfUpdate,
|
|
119
|
+
releaseSelfUpdatePlan,
|
|
120
|
+
requireNpmSelfUpdateInstallation,
|
|
121
|
+
} from "./cli/SelfUpdater.js";
|
|
122
|
+
import { createCurrentAutostartManager } from "./cli/AutostartManager.js";
|
|
123
|
+
import {
|
|
124
|
+
formatAutostartStatus,
|
|
125
|
+
type AutostartServerStatus,
|
|
126
|
+
type CombinedAutostartStatus,
|
|
127
|
+
} from "./cli/autostartOutput.js";
|
|
96
128
|
|
|
97
129
|
const args = process.argv.slice(2);
|
|
98
130
|
|
|
@@ -119,6 +151,10 @@ async function main(input: string[]): Promise<void> {
|
|
|
119
151
|
await initCommand(rest, parsed.configPath, Boolean(parsed.profilePath));
|
|
120
152
|
return;
|
|
121
153
|
}
|
|
154
|
+
if (command === "update") {
|
|
155
|
+
await updateCommand(rest);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
122
158
|
if (command === "console") {
|
|
123
159
|
await consoleCommand(rest);
|
|
124
160
|
return;
|
|
@@ -141,6 +177,88 @@ async function main(input: string[]): Promise<void> {
|
|
|
141
177
|
));
|
|
142
178
|
}
|
|
143
179
|
|
|
180
|
+
async function updateCommand(input: string[]): Promise<void> {
|
|
181
|
+
const options = parseSelfUpdateOptions(input);
|
|
182
|
+
requireNpmSelfUpdateInstallation();
|
|
183
|
+
const config = loadConfig();
|
|
184
|
+
const endpoint = controlEndpoint(config.storage.sqlitePath);
|
|
185
|
+
const serverRunning = await isServerReachable(endpoint);
|
|
186
|
+
if (!options.json) process.stdout.write(cliText(
|
|
187
|
+
"Checking the installed Agent Bot package and npm release channel...\n",
|
|
188
|
+
"正在检查已安装的 Agent Bot 包和 npm 发布通道……\n",
|
|
189
|
+
));
|
|
190
|
+
const prepared = await prepareSelfUpdate({
|
|
191
|
+
channel: options.channel,
|
|
192
|
+
version: options.version,
|
|
193
|
+
allowDowngrade: options.allowDowngrade,
|
|
194
|
+
});
|
|
195
|
+
if (prepared.status === "current") {
|
|
196
|
+
if (options.json) printJson(prepared);
|
|
197
|
+
else process.stdout.write(cliText(
|
|
198
|
+
`Agent Bot ${prepared.currentVersion} is already current on the ${prepared.channel} channel.\n`,
|
|
199
|
+
`Agent Bot ${prepared.currentVersion} 已是 ${prepared.channel} 通道的当前版本。\n`,
|
|
200
|
+
));
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const reason = cliText(
|
|
205
|
+
`Update Agent Bot from ${prepared.currentVersion} to ${prepared.targetVersion}`,
|
|
206
|
+
`将 Agent Bot 从 ${prepared.currentVersion} 更新到 ${prepared.targetVersion}`,
|
|
207
|
+
);
|
|
208
|
+
try {
|
|
209
|
+
const notificationSessionId = serverRunning
|
|
210
|
+
? resolveRestartNotificationSessionId(
|
|
211
|
+
config.storage.sqlitePath,
|
|
212
|
+
options.taskReference ? ["--task", options.taskReference] : [],
|
|
213
|
+
)
|
|
214
|
+
: undefined;
|
|
215
|
+
finalizeSelfUpdatePlan(prepared.planPath, {
|
|
216
|
+
controlEndpoint: endpoint,
|
|
217
|
+
databasePath: config.storage.sqlitePath,
|
|
218
|
+
restartService: serverRunning,
|
|
219
|
+
workingDirectory: process.cwd(),
|
|
220
|
+
reason,
|
|
221
|
+
notificationSessionId,
|
|
222
|
+
});
|
|
223
|
+
if (serverRunning) {
|
|
224
|
+
ensureOk(await sendControlRequest(endpoint, {
|
|
225
|
+
action: "server_update",
|
|
226
|
+
planPath: prepared.planPath,
|
|
227
|
+
reason,
|
|
228
|
+
notificationSessionId,
|
|
229
|
+
}, 15_000));
|
|
230
|
+
} else {
|
|
231
|
+
launchSelfUpdateRunner(prepared.planPath, { waitPids: [process.pid] });
|
|
232
|
+
}
|
|
233
|
+
} catch (error) {
|
|
234
|
+
releaseSelfUpdatePlan(prepared.planPath);
|
|
235
|
+
throw error;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const result = {
|
|
239
|
+
status: serverRunning ? "scheduled" : "started",
|
|
240
|
+
fromVersion: prepared.currentVersion,
|
|
241
|
+
toVersion: prepared.targetVersion,
|
|
242
|
+
channel: prepared.channel,
|
|
243
|
+
serviceWasRunning: serverRunning,
|
|
244
|
+
logPath: prepared.logPath,
|
|
245
|
+
resultPath: prepared.resultPath,
|
|
246
|
+
};
|
|
247
|
+
if (options.json) printJson(result);
|
|
248
|
+
else {
|
|
249
|
+
process.stdout.write(serverRunning
|
|
250
|
+
? cliText(
|
|
251
|
+
`Agent Bot ${prepared.targetVersion} is verified and ready. The update will run after active tasks finish and the server becomes idle.\n`,
|
|
252
|
+
`Agent Bot ${prepared.targetVersion} 已通过预检。更新将在活动任务完成且服务空闲后执行。\n`,
|
|
253
|
+
)
|
|
254
|
+
: cliText(
|
|
255
|
+
`Agent Bot ${prepared.targetVersion} is verified. Installation will continue after this command exits.\n`,
|
|
256
|
+
`Agent Bot ${prepared.targetVersion} 已通过预检。本命令退出后将继续安装。\n`,
|
|
257
|
+
));
|
|
258
|
+
process.stdout.write(`${cliText("Update log: ", "更新日志:")}${prepared.logPath}\n`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
144
262
|
type FeishuInitializationStatus = "created" | "existing" | "skipped";
|
|
145
263
|
|
|
146
264
|
interface InitCommandResult extends InitializationResult {
|
|
@@ -150,12 +268,17 @@ interface InitCommandResult extends InitializationResult {
|
|
|
150
268
|
name: string;
|
|
151
269
|
status: "selected" | "existing";
|
|
152
270
|
};
|
|
271
|
+
groupMessages: {
|
|
272
|
+
mode: GroupMessageResponseMode;
|
|
273
|
+
status: "selected" | "existing";
|
|
274
|
+
};
|
|
153
275
|
feishu: {
|
|
154
276
|
status: FeishuInitializationStatus;
|
|
155
277
|
appId?: string;
|
|
156
278
|
userOpenIdStatus?: "configured" | "pending";
|
|
157
279
|
configuration?: EnsureFeishuAppConfigurationResult;
|
|
158
280
|
};
|
|
281
|
+
skill: ReturnType<SkillRegistry["install"]>;
|
|
159
282
|
server: InitializationServerResult;
|
|
160
283
|
welcome: InitializationWelcomeResult;
|
|
161
284
|
}
|
|
@@ -192,6 +315,11 @@ async function initCommand(
|
|
|
192
315
|
options.json,
|
|
193
316
|
shouldConfigureAgentsDuringInitialization(paths.config.status),
|
|
194
317
|
);
|
|
318
|
+
const groupMessages = await configureGroupMessageResponse(
|
|
319
|
+
paths.config.path,
|
|
320
|
+
options.json,
|
|
321
|
+
shouldConfigureGroupMessagesDuringInitialization(paths.config.status),
|
|
322
|
+
);
|
|
195
323
|
const configuredAgents = readConfiguredAgentSelection(paths.config.path).agents.map((agent) => agent.name);
|
|
196
324
|
const configuredAgentNames = new Set(configuredAgents);
|
|
197
325
|
const agents = inspectedAgents.map((agent) => ({
|
|
@@ -203,8 +331,10 @@ async function initCommand(
|
|
|
203
331
|
const feishu = await initializeFeishu(
|
|
204
332
|
paths,
|
|
205
333
|
options,
|
|
334
|
+
groupMessages.mode === "all",
|
|
206
335
|
);
|
|
207
|
-
|
|
336
|
+
const skill = bundledSkillRegistry().install();
|
|
337
|
+
initialized = { ...paths, agents, configuredAgents, defaultAgent, groupMessages, feishu, skill };
|
|
208
338
|
} finally {
|
|
209
339
|
initializationLock?.release();
|
|
210
340
|
}
|
|
@@ -415,6 +545,76 @@ async function configureAgentsAndDefault(
|
|
|
415
545
|
return { name: selectedDefault.name, status: updated ? "selected" : "existing" };
|
|
416
546
|
}
|
|
417
547
|
|
|
548
|
+
async function configureGroupMessageResponse(
|
|
549
|
+
configPath: string,
|
|
550
|
+
json: boolean,
|
|
551
|
+
configure: boolean,
|
|
552
|
+
): Promise<InitCommandResult["groupMessages"]> {
|
|
553
|
+
const output = json ? process.stderr : process.stdout;
|
|
554
|
+
if (!configure) {
|
|
555
|
+
return { mode: readGroupMessageResponseMode(configPath), status: "existing" };
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
let mode: GroupMessageResponseMode = "mention-only";
|
|
559
|
+
if (process.stdin.isTTY) {
|
|
560
|
+
output.write(cliText(
|
|
561
|
+
"\nChoose how Agent Bot responds to group messages:\n 1. Receive all group messages\n The bot can respond without an @ mention. This requires an additional Lark permission.\n 2. Require an explicit @ mention\n The additional all-group-message permission will not be requested.\n",
|
|
562
|
+
"\n请选择 Agent Bot 响应群消息的方式:\n 1. 接收所有群消息\n 无需 @ 机器人即可响应,需要额外开通飞书权限。\n 2. 明确 @ 机器人才能接收消息\n 不会申请“免 @ 机器人”所需的额外权限。\n",
|
|
563
|
+
));
|
|
564
|
+
mode = await promptForGroupMessageResponseMode();
|
|
565
|
+
} else {
|
|
566
|
+
output.write(cliText(
|
|
567
|
+
"No interactive terminal is available. Group messages will require an explicit @ mention, and the all-group-message permission will not be requested.\n",
|
|
568
|
+
"当前没有交互式终端。群消息将要求明确 @ 机器人,并且不会申请接收所有群消息的额外权限。\n",
|
|
569
|
+
));
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
writeGroupMessageResponseMode(configPath, mode);
|
|
573
|
+
output.write(cliText(
|
|
574
|
+
`Group message response: ${mode === "all" ? "receive all group messages" : "require an explicit @ mention"}\n`,
|
|
575
|
+
`群消息响应方式:${mode === "all" ? "接收所有群消息" : "必须明确 @ 机器人"}\n`,
|
|
576
|
+
));
|
|
577
|
+
return { mode, status: "selected" };
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function promptForGroupMessageResponseMode(): Promise<GroupMessageResponseMode> {
|
|
581
|
+
const readline = createInterface({ input: process.stdin, output: process.stderr });
|
|
582
|
+
return new Promise((resolve, reject) => {
|
|
583
|
+
let settled = false;
|
|
584
|
+
const finish = (mode: GroupMessageResponseMode): void => {
|
|
585
|
+
settled = true;
|
|
586
|
+
readline.close();
|
|
587
|
+
resolve(mode);
|
|
588
|
+
};
|
|
589
|
+
const ask = (): void => {
|
|
590
|
+
readline.question(cliText(
|
|
591
|
+
"Select 1 or 2 [2]: ",
|
|
592
|
+
"请选择 1 或 2 [2]:",
|
|
593
|
+
), (answer) => {
|
|
594
|
+
const normalized = answer.trim().toLowerCase();
|
|
595
|
+
if (normalized === "1") {
|
|
596
|
+
finish("all");
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
if (normalized === "" || normalized === "2") {
|
|
600
|
+
finish("mention-only");
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
process.stderr.write(cliText(
|
|
604
|
+
"Enter 1 to receive all group messages, or 2 to require an @ mention.\n",
|
|
605
|
+
"请输入 1 接收所有群消息,或输入 2 要求明确 @ 机器人。\n",
|
|
606
|
+
));
|
|
607
|
+
ask();
|
|
608
|
+
});
|
|
609
|
+
};
|
|
610
|
+
listenForPromptCancellation(readline, () => {
|
|
611
|
+
settled = true;
|
|
612
|
+
reject(new Error(cliText("Initialization was cancelled.", "初始化已取消。")));
|
|
613
|
+
}, () => settled);
|
|
614
|
+
ask();
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
|
|
418
618
|
function promptForAgentMaintenance(choiceCount: number): Promise<number[]> {
|
|
419
619
|
const readline = createInterface({ input: process.stdin, output: process.stderr });
|
|
420
620
|
return new Promise((resolve, reject) => {
|
|
@@ -577,6 +777,7 @@ function refreshCurrentProcessPath(): void {
|
|
|
577
777
|
async function initializeFeishu(
|
|
578
778
|
paths: InitializationResult,
|
|
579
779
|
options: InitCommandOptions,
|
|
780
|
+
respondToAllGroupMessages: boolean,
|
|
580
781
|
): Promise<InitCommandResult["feishu"]> {
|
|
581
782
|
const existing = readFeishuCredentials(paths.env.path);
|
|
582
783
|
if (options.skipFeishu) {
|
|
@@ -634,6 +835,7 @@ async function initializeFeishu(
|
|
|
634
835
|
const optionalSkip = new AbortController();
|
|
635
836
|
let skipListener: OptionalAuthorizationSkipListener | undefined;
|
|
636
837
|
const configuration = await ensureFeishuAppConfiguration(credentials, {
|
|
838
|
+
respondToAllGroupMessages,
|
|
637
839
|
signal: controller.signal,
|
|
638
840
|
manualPermissionSkipSignal: manualPermissionSkip.signal,
|
|
639
841
|
optionalSkipSignal: optionalSkip.signal,
|
|
@@ -686,9 +888,8 @@ async function assertResetProfileServerStopped(configPath?: string): Promise<voi
|
|
|
686
888
|
|
|
687
889
|
function skillsCommand(input: string[]): void {
|
|
688
890
|
const [action = "status", ...rest] = input;
|
|
689
|
-
const sourcePath = fileURLToPath(new URL("../skills/agent-bot/", import.meta.url));
|
|
690
891
|
const targetRoot = optionValue(rest, "--target") ?? resolveSystemSkillsRoot();
|
|
691
|
-
const registry =
|
|
892
|
+
const registry = bundledSkillRegistry(targetRoot);
|
|
692
893
|
const json = rest.includes("--json");
|
|
693
894
|
|
|
694
895
|
if (action === "install" || action === "register") {
|
|
@@ -738,6 +939,11 @@ function skillsCommand(input: string[]): void {
|
|
|
738
939
|
throw new Error(cliText(`Unknown skills command: ${action}`, `未知的 skills 命令:${action}`));
|
|
739
940
|
}
|
|
740
941
|
|
|
942
|
+
function bundledSkillRegistry(targetRoot = resolveSystemSkillsRoot()): SkillRegistry {
|
|
943
|
+
const sourcePath = fileURLToPath(new URL("../skills/agent-bot/", import.meta.url));
|
|
944
|
+
return new SkillRegistry(sourcePath, targetRoot);
|
|
945
|
+
}
|
|
946
|
+
|
|
741
947
|
async function consoleCommand(input: string[]): Promise<void> {
|
|
742
948
|
const config = loadConfig();
|
|
743
949
|
const endpoint = controlEndpoint(config.storage.sqlitePath);
|
|
@@ -791,6 +997,10 @@ async function serverCommand(input: string[]): Promise<void> {
|
|
|
791
997
|
printServerStartResult(await startServer(config));
|
|
792
998
|
return;
|
|
793
999
|
}
|
|
1000
|
+
if (action === "autostart") {
|
|
1001
|
+
await serverAutostartCommand(config, endpoint, rest);
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
794
1004
|
if (action === "stop") {
|
|
795
1005
|
ensureOk(await sendControlRequest(endpoint, { action: "server_stop" }));
|
|
796
1006
|
process.stdout.write(cliText("Agent Bot server stop requested.\n", "已请求停止 Agent Bot 服务。\n"));
|
|
@@ -815,6 +1025,64 @@ async function serverCommand(input: string[]): Promise<void> {
|
|
|
815
1025
|
throw new Error(cliText(`Unknown server command: ${action}`, `未知的 server 命令:${action}`));
|
|
816
1026
|
}
|
|
817
1027
|
|
|
1028
|
+
async function serverAutostartCommand(
|
|
1029
|
+
config: ReturnType<typeof loadConfig>,
|
|
1030
|
+
endpoint: string,
|
|
1031
|
+
input: string[],
|
|
1032
|
+
): Promise<void> {
|
|
1033
|
+
const actionIndex = input.findIndex((value) => !value.startsWith("--"));
|
|
1034
|
+
const action = actionIndex < 0 ? "status" : input[actionIndex]!;
|
|
1035
|
+
const rest = input.filter((_value, index) => index !== actionIndex);
|
|
1036
|
+
const json = rest.includes("--json");
|
|
1037
|
+
const linger = rest.includes("--linger");
|
|
1038
|
+
const unsupported = rest.filter((value) => value !== "--json" && value !== "--linger");
|
|
1039
|
+
if (unsupported.length > 0) throw new Error(cliText(
|
|
1040
|
+
`Unsupported server autostart options: ${unsupported.join(" ")}`,
|
|
1041
|
+
`不支持这些 server autostart 参数:${unsupported.join(" ")}`,
|
|
1042
|
+
));
|
|
1043
|
+
if (action !== "enable" && linger) {
|
|
1044
|
+
throw new Error(cliText(
|
|
1045
|
+
"--linger can be used only with server autostart enable.",
|
|
1046
|
+
"--linger 只能与 server autostart enable 一起使用。",
|
|
1047
|
+
));
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
const manager = createCurrentAutostartManager(config);
|
|
1051
|
+
let registration;
|
|
1052
|
+
if (action === "enable") {
|
|
1053
|
+
registration = manager.enable({ linger });
|
|
1054
|
+
} else if (action === "disable") {
|
|
1055
|
+
registration = manager.disable();
|
|
1056
|
+
} else if (action === "status") {
|
|
1057
|
+
registration = manager.status();
|
|
1058
|
+
} else {
|
|
1059
|
+
throw new Error(cliText(
|
|
1060
|
+
`Unknown server autostart command: ${action}`,
|
|
1061
|
+
`未知的 server autostart 命令:${action}`,
|
|
1062
|
+
));
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
const result: CombinedAutostartStatus = {
|
|
1066
|
+
registration,
|
|
1067
|
+
server: await readAutostartServerStatus(endpoint),
|
|
1068
|
+
};
|
|
1069
|
+
if (json) printJson(result);
|
|
1070
|
+
else process.stdout.write(formatAutostartStatus(result));
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
async function readAutostartServerStatus(endpoint: string): Promise<AutostartServerStatus> {
|
|
1074
|
+
try {
|
|
1075
|
+
const response = await sendControlRequest(endpoint, { action: "health" }, 1_500);
|
|
1076
|
+
if (!response.ok) return { running: true, ready: false };
|
|
1077
|
+
const data = response.data && typeof response.data === "object"
|
|
1078
|
+
? response.data as Record<string, unknown>
|
|
1079
|
+
: {};
|
|
1080
|
+
return { running: true, ready: data.ready !== false };
|
|
1081
|
+
} catch {
|
|
1082
|
+
return { running: false, ready: false };
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
|
|
818
1086
|
function resolveRestartNotificationSessionId(sqlitePath: string, args: string[]): string | undefined {
|
|
819
1087
|
const store = new StateStore(sqlitePath);
|
|
820
1088
|
try {
|
|
@@ -857,6 +1125,10 @@ async function taskCommand(input: string[]): Promise<void> {
|
|
|
857
1125
|
const store = new StateStore(config.storage.sqlitePath);
|
|
858
1126
|
try {
|
|
859
1127
|
const allSessions = store.listAllSessions();
|
|
1128
|
+
if (action === "help" || action === "--help" || action === "-h") {
|
|
1129
|
+
printHelp();
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
860
1132
|
if (action === "current") {
|
|
861
1133
|
const unsupported = rest.filter((value) => value !== "--json");
|
|
862
1134
|
if (unsupported.length > 0) throw new Error(cliText(
|
|
@@ -868,29 +1140,323 @@ async function taskCommand(input: string[]): Promise<void> {
|
|
|
868
1140
|
return;
|
|
869
1141
|
}
|
|
870
1142
|
if (action === "prompt" || action === "send") {
|
|
871
|
-
const
|
|
872
|
-
|
|
873
|
-
`task ${action} requires a task number or task ID.`,
|
|
874
|
-
`task ${action} 需要任务序号或任务 ID。`,
|
|
875
|
-
));
|
|
876
|
-
const text = promptParts.join(" ").trim();
|
|
1143
|
+
const target = resolveTaskCommandTarget(allSessions, rest, action);
|
|
1144
|
+
const text = target.args.join(" ").trim();
|
|
877
1145
|
if (!text) throw new Error(cliText(
|
|
878
1146
|
`task ${action} requires a Prompt.`,
|
|
879
1147
|
`task ${action} 需要提示词。`,
|
|
880
1148
|
));
|
|
881
|
-
const session = resolveTask(allSessions, reference);
|
|
882
1149
|
const endpoint = controlEndpoint(config.storage.sqlitePath);
|
|
883
1150
|
ensureOk(await sendControlRequest(endpoint, {
|
|
884
1151
|
action: "task_prompt",
|
|
885
|
-
localSessionId: session.localSessionId,
|
|
1152
|
+
localSessionId: target.session.localSessionId,
|
|
886
1153
|
text,
|
|
887
1154
|
}, 60_000));
|
|
888
1155
|
process.stdout.write(cliText("Prompt submitted.\n", "提示词已提交。\n"));
|
|
889
1156
|
return;
|
|
890
1157
|
}
|
|
1158
|
+
if (action === "new") {
|
|
1159
|
+
const target = resolveTaskCommandTarget(allSessions, rest, action);
|
|
1160
|
+
const options = parseTaskNewOptions([target.session.localSessionId, ...target.args]);
|
|
1161
|
+
const source = target.session;
|
|
1162
|
+
const targetAgent = options.agentName ? config.agents[options.agentName] : undefined;
|
|
1163
|
+
if (options.agentName && !targetAgent) throw new Error(cliText(
|
|
1164
|
+
`Unknown Agent standard name: ${options.agentName}.`,
|
|
1165
|
+
`未知的 Agent 标准名:${options.agentName}。`,
|
|
1166
|
+
));
|
|
1167
|
+
if (options.projectless && targetAgent && targetAgent.kind !== "app-server") throw new Error(cliText(
|
|
1168
|
+
"task new --nodir is only available for App Server agents.",
|
|
1169
|
+
"task new --nodir 仅适用于 App Server Agent。",
|
|
1170
|
+
));
|
|
1171
|
+
const created = controlData<SessionRecord>(await sendControlRequest(controlEndpoint(config.storage.sqlitePath), {
|
|
1172
|
+
action: "task_new",
|
|
1173
|
+
localSessionId: source.localSessionId,
|
|
1174
|
+
...(options.title ? { title: options.title } : {}),
|
|
1175
|
+
...(options.cwd ? { cwd: options.cwd } : {}),
|
|
1176
|
+
...(options.agentName ? { agentName: options.agentName } : {}),
|
|
1177
|
+
...(options.projectless ? { projectless: true } : {}),
|
|
1178
|
+
}, 120_000));
|
|
1179
|
+
if (options.json) printJson(created);
|
|
1180
|
+
else printCreatedTask(created, "Task created");
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
if (action === "fork") {
|
|
1184
|
+
const target = resolveTaskCommandTarget(allSessions, rest, action);
|
|
1185
|
+
const options = target.args;
|
|
1186
|
+
rejectUnsupportedTaskOptions(action, options, ["--json"]);
|
|
1187
|
+
if (options.some((value) => !value.startsWith("--"))) throw new Error(cliText(
|
|
1188
|
+
"task fork accepts only one task reference.",
|
|
1189
|
+
"task fork 只接受一个任务引用。",
|
|
1190
|
+
));
|
|
1191
|
+
const result = controlData<TaskForkControlData>(await sendControlRequest(controlEndpoint(config.storage.sqlitePath), {
|
|
1192
|
+
action: "task_fork",
|
|
1193
|
+
localSessionId: target.session.localSessionId,
|
|
1194
|
+
}, 120_000));
|
|
1195
|
+
if (options.includes("--json")) printJson(result);
|
|
1196
|
+
else {
|
|
1197
|
+
printCreatedTask(result.task, "Fork created");
|
|
1198
|
+
process.stdout.write(`${cliText("Source turn: ", "来源 Turn:")}${result.sourceTurnId}\n`);
|
|
1199
|
+
}
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
if (action === "archive") {
|
|
1203
|
+
const target = resolveTaskCommandTarget(allSessions, rest, action);
|
|
1204
|
+
const options = target.args;
|
|
1205
|
+
rejectUnsupportedTaskOptions(action, options, ["--json"]);
|
|
1206
|
+
if (options.some((value) => !value.startsWith("--"))) throw new Error(cliText(
|
|
1207
|
+
"task archive accepts only one task reference.",
|
|
1208
|
+
"task archive 只接受一个任务引用。",
|
|
1209
|
+
));
|
|
1210
|
+
const archived = controlData<{
|
|
1211
|
+
localSessionId: string;
|
|
1212
|
+
remoteSessionId: string;
|
|
1213
|
+
title: string;
|
|
1214
|
+
}>(await sendControlRequest(controlEndpoint(config.storage.sqlitePath), {
|
|
1215
|
+
action: "task_archive",
|
|
1216
|
+
localSessionId: target.session.localSessionId,
|
|
1217
|
+
}, 60_000));
|
|
1218
|
+
if (options.includes("--json")) printJson(archived);
|
|
1219
|
+
else process.stdout.write(cliText(
|
|
1220
|
+
`Task archived: ${archived.title} (${archived.remoteSessionId})\n`,
|
|
1221
|
+
`任务已归档:${archived.title}(${archived.remoteSessionId})\n`,
|
|
1222
|
+
));
|
|
1223
|
+
return;
|
|
1224
|
+
}
|
|
1225
|
+
if (action === "dismiss") {
|
|
1226
|
+
const target = resolveTaskCommandTarget(allSessions, rest, action);
|
|
1227
|
+
const options = target.args;
|
|
1228
|
+
rejectUnsupportedTaskOptions(action, options, ["--yes", "--json"]);
|
|
1229
|
+
if (options.some((value) => !value.startsWith("--"))) throw new Error(cliText(
|
|
1230
|
+
"task dismiss accepts only one task reference.",
|
|
1231
|
+
"task dismiss 只接受一个任务引用。",
|
|
1232
|
+
));
|
|
1233
|
+
if (!options.includes("--yes")) throw new Error(cliText(
|
|
1234
|
+
"task dismiss permanently dissolves the bound group and archives its current task. Re-run with --yes.",
|
|
1235
|
+
"task dismiss 会永久解散绑定群并归档其当前任务。请添加 --yes 后重试。",
|
|
1236
|
+
));
|
|
1237
|
+
const dismissed = controlData<TaskDismissControlData>(await sendControlRequest(
|
|
1238
|
+
controlEndpoint(config.storage.sqlitePath),
|
|
1239
|
+
{
|
|
1240
|
+
action: "task_dismiss",
|
|
1241
|
+
localSessionId: target.session.localSessionId,
|
|
1242
|
+
},
|
|
1243
|
+
60_000,
|
|
1244
|
+
));
|
|
1245
|
+
if (options.includes("--json")) printJson(dismissed);
|
|
1246
|
+
else process.stdout.write(cliText(
|
|
1247
|
+
`Group dissolved and task archived: ${dismissed.title} (${dismissed.chatId})\n`,
|
|
1248
|
+
`群已解散,任务已归档:${dismissed.title}(${dismissed.chatId})\n`,
|
|
1249
|
+
));
|
|
1250
|
+
return;
|
|
1251
|
+
}
|
|
1252
|
+
if (action === "switch") {
|
|
1253
|
+
const targetSelection = resolveTaskCommandTarget(allSessions, rest, action, { preferCurrent: true });
|
|
1254
|
+
const switchArgs = targetSelection.args;
|
|
1255
|
+
rejectUnsupportedTaskOptions(action, switchArgs, ["--json", "--previous"]);
|
|
1256
|
+
const previous = switchArgs.includes("--previous");
|
|
1257
|
+
const targetReferences = switchArgs.filter((value) => !value.startsWith("--"));
|
|
1258
|
+
if (targetReferences.length > 1) throw new Error(cliText(
|
|
1259
|
+
"task switch accepts at most one target task.",
|
|
1260
|
+
"task switch 最多接受一个目标任务。",
|
|
1261
|
+
));
|
|
1262
|
+
const targetReference = targetReferences[0];
|
|
1263
|
+
if (previous && targetReference) throw new Error(cliText(
|
|
1264
|
+
"task switch cannot combine a target task with --previous.",
|
|
1265
|
+
"task switch 不能同时指定目标任务和 --previous。",
|
|
1266
|
+
));
|
|
1267
|
+
const target = targetReference ? resolveTask(allSessions, targetReference) : undefined;
|
|
1268
|
+
const switched = controlData<SessionRecord>(await sendControlRequest(controlEndpoint(config.storage.sqlitePath), {
|
|
1269
|
+
action: "task_switch",
|
|
1270
|
+
localSessionId: targetSelection.session.localSessionId,
|
|
1271
|
+
...(target ? { targetLocalSessionId: target.localSessionId } : {}),
|
|
1272
|
+
...(previous ? { previous: true } : {}),
|
|
1273
|
+
}));
|
|
1274
|
+
if (switchArgs.includes("--json")) printJson(switched);
|
|
1275
|
+
else printCreatedTask(switched, "Current task");
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
if (action === "queue" || action === "nosteer") {
|
|
1279
|
+
const target = resolveTaskCommandTarget(allSessions, rest, action);
|
|
1280
|
+
const text = target.args.join(" ").trim();
|
|
1281
|
+
if (!text) throw new Error(cliText(
|
|
1282
|
+
`task ${action} requires a Prompt.`,
|
|
1283
|
+
`task ${action} 需要提示词。`,
|
|
1284
|
+
));
|
|
1285
|
+
const result = controlData<{ promptId: string; queued: number }>(await sendControlRequest(
|
|
1286
|
+
controlEndpoint(config.storage.sqlitePath),
|
|
1287
|
+
{ action: "task_queue", localSessionId: target.session.localSessionId, text },
|
|
1288
|
+
));
|
|
1289
|
+
process.stdout.write(cliText(
|
|
1290
|
+
`Prompt queued (${result.queued} waiting): ${result.promptId}\n`,
|
|
1291
|
+
`提示词已排队(当前 ${result.queued} 条):${result.promptId}\n`,
|
|
1292
|
+
));
|
|
1293
|
+
return;
|
|
1294
|
+
}
|
|
1295
|
+
if (action === "agent") {
|
|
1296
|
+
const target = resolveTaskCommandTarget(allSessions, rest, action);
|
|
1297
|
+
const [agentName, ...options] = target.args;
|
|
1298
|
+
rejectUnsupportedTaskOptions(action, [agentName, ...options].filter((value): value is string => value !== undefined), ["--json"]);
|
|
1299
|
+
if (options.some((value) => !value.startsWith("--"))) throw new Error(cliText(
|
|
1300
|
+
"task agent accepts at most one Agent name.",
|
|
1301
|
+
"task agent 最多接受一个 Agent 名称。",
|
|
1302
|
+
));
|
|
1303
|
+
const result = controlData<TaskAgentControlData>(await sendControlRequest(controlEndpoint(config.storage.sqlitePath), {
|
|
1304
|
+
action: "task_agent",
|
|
1305
|
+
localSessionId: target.session.localSessionId,
|
|
1306
|
+
...(agentName && !agentName.startsWith("--") ? { agentName } : {}),
|
|
1307
|
+
}));
|
|
1308
|
+
const json = options.includes("--json") || agentName === "--json";
|
|
1309
|
+
if (json) printJson(result);
|
|
1310
|
+
else printTaskAgentSettings(result);
|
|
1311
|
+
return;
|
|
1312
|
+
}
|
|
1313
|
+
if (["provider", "model", "thinking", "permissions"].includes(action)) {
|
|
1314
|
+
const target = resolveTaskCommandTarget(allSessions, rest, action);
|
|
1315
|
+
const [rawValue, ...options] = target.args;
|
|
1316
|
+
rejectUnsupportedTaskOptions(action, [rawValue, ...options].filter((value): value is string => value !== undefined), ["--json"]);
|
|
1317
|
+
if (options.some((value) => !value.startsWith("--"))) throw new Error(cliText(
|
|
1318
|
+
`task ${action} accepts at most one value.`,
|
|
1319
|
+
`task ${action} 最多接受一个值。`,
|
|
1320
|
+
));
|
|
1321
|
+
const value = rawValue && !rawValue.startsWith("--") ? rawValue : undefined;
|
|
1322
|
+
const json = options.includes("--json") || rawValue === "--json";
|
|
1323
|
+
const result = controlData<TaskSettingsControlData>(await sendControlRequest(controlEndpoint(config.storage.sqlitePath), {
|
|
1324
|
+
action: "task_settings",
|
|
1325
|
+
localSessionId: target.session.localSessionId,
|
|
1326
|
+
...(value ? { setting: action as "provider" | "model" | "thinking" | "permissions", value } : {}),
|
|
1327
|
+
}, 60_000));
|
|
1328
|
+
if (json) printJson(result);
|
|
1329
|
+
else printTaskSettings(result, action as "provider" | "model" | "thinking" | "permissions");
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
1332
|
+
if (action === "goal") {
|
|
1333
|
+
const target = resolveTaskCommandTarget(allSessions, rest, action);
|
|
1334
|
+
const goalArgs = target.args;
|
|
1335
|
+
const json = goalArgs.includes("--json");
|
|
1336
|
+
const args = goalArgs.filter((value) => value !== "--json");
|
|
1337
|
+
rejectUnsupportedTaskOptions(action, goalArgs, ["--json"]);
|
|
1338
|
+
const parsedGoal = parseTaskGoal(args);
|
|
1339
|
+
const result = controlData<TaskGoalControlData>(await sendControlRequest(controlEndpoint(config.storage.sqlitePath), {
|
|
1340
|
+
action: "task_goal",
|
|
1341
|
+
localSessionId: target.session.localSessionId,
|
|
1342
|
+
goalAction: parsedGoal.action,
|
|
1343
|
+
...(parsedGoal.objective ? { objective: parsedGoal.objective } : {}),
|
|
1344
|
+
}));
|
|
1345
|
+
if (json) printJson(result);
|
|
1346
|
+
else printTaskGoal(result);
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
if (action === "turns") {
|
|
1350
|
+
const target = resolveTaskCommandTarget(allSessions, rest, action);
|
|
1351
|
+
const options = target.args;
|
|
1352
|
+
rejectUnsupportedTaskOptions(action, options, ["--json"]);
|
|
1353
|
+
if (options.some((value) => !value.startsWith("--"))) throw new Error(cliText(
|
|
1354
|
+
"task turns accepts only one task reference.",
|
|
1355
|
+
"task turns 只接受一个任务引用。",
|
|
1356
|
+
));
|
|
1357
|
+
const result = controlData<TaskTurnsControlData>(await sendControlRequest(controlEndpoint(config.storage.sqlitePath), {
|
|
1358
|
+
action: "task_turns",
|
|
1359
|
+
localSessionId: target.session.localSessionId,
|
|
1360
|
+
}, 60_000));
|
|
1361
|
+
if (options.includes("--json")) printJson(result);
|
|
1362
|
+
else printTaskTurns(result);
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
if (action === "reset") {
|
|
1366
|
+
const target = resolveTaskCommandTarget(allSessions, rest, action);
|
|
1367
|
+
const [turnId, ...options] = target.args;
|
|
1368
|
+
if (!turnId || turnId.startsWith("--")) throw new Error(cliText(
|
|
1369
|
+
"task reset requires a Turn ID.",
|
|
1370
|
+
"task reset 需要 Turn ID。",
|
|
1371
|
+
));
|
|
1372
|
+
rejectUnsupportedTaskOptions(action, options, ["--json"]);
|
|
1373
|
+
if (options.some((value) => !value.startsWith("--"))) throw new Error(cliText(
|
|
1374
|
+
"task reset accepts one task reference and one Turn ID.",
|
|
1375
|
+
"task reset 只接受一个任务引用和一个 Turn ID。",
|
|
1376
|
+
));
|
|
1377
|
+
const result = controlData<SessionRecord>(await sendControlRequest(controlEndpoint(config.storage.sqlitePath), {
|
|
1378
|
+
action: "task_reset",
|
|
1379
|
+
localSessionId: target.session.localSessionId,
|
|
1380
|
+
turnId,
|
|
1381
|
+
}, 120_000));
|
|
1382
|
+
if (options.includes("--json")) printJson(result);
|
|
1383
|
+
else process.stdout.write(cliText(`Task reset to turn ${turnId}.\n`, `任务已 Reset 到 Turn ${turnId}。\n`));
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1386
|
+
if (action === "mute") {
|
|
1387
|
+
const target = resolveTaskCommandTarget(allSessions, rest, action);
|
|
1388
|
+
const [rawMode, ...options] = target.args;
|
|
1389
|
+
rejectUnsupportedTaskOptions(action, [rawMode, ...options].filter((value): value is string => value !== undefined), ["--json"]);
|
|
1390
|
+
if (options.some((value) => !value.startsWith("--"))) throw new Error(cliText(
|
|
1391
|
+
"task mute accepts at most one mode.",
|
|
1392
|
+
"task mute 最多接受一个模式。",
|
|
1393
|
+
));
|
|
1394
|
+
const mode = rawMode && !rawMode.startsWith("--") ? rawMode.toLowerCase() : "on";
|
|
1395
|
+
if (mode && mode !== "on" && mode !== "off") throw new Error(cliText(
|
|
1396
|
+
"task mute accepts on or off.",
|
|
1397
|
+
"task mute 只接受 on 或 off。",
|
|
1398
|
+
));
|
|
1399
|
+
const result = controlData<TaskMuteControlData>(await sendControlRequest(controlEndpoint(config.storage.sqlitePath), {
|
|
1400
|
+
action: "task_mute",
|
|
1401
|
+
localSessionId: target.session.localSessionId,
|
|
1402
|
+
enabled: mode === "on",
|
|
1403
|
+
}));
|
|
1404
|
+
const json = options.includes("--json") || rawMode === "--json";
|
|
1405
|
+
if (json) printJson(result);
|
|
1406
|
+
else process.stdout.write(cliText(
|
|
1407
|
+
`Mention-only mode: ${result.enabled ? "on" : "off"}\n`,
|
|
1408
|
+
`仅 @ 响应模式:${result.enabled ? "开启" : "关闭"}\n`,
|
|
1409
|
+
));
|
|
1410
|
+
return;
|
|
1411
|
+
}
|
|
1412
|
+
if (action === "shell") {
|
|
1413
|
+
const target = resolveTaskCommandTarget(allSessions, rest, action);
|
|
1414
|
+
const command = target.args.join(" ").trim();
|
|
1415
|
+
if (!command) throw new Error(cliText("task shell requires a command.", "task shell 需要命令。"));
|
|
1416
|
+
const result = controlData<TaskShellControlData>(await sendControlRequest(controlEndpoint(config.storage.sqlitePath), {
|
|
1417
|
+
action: "task_shell",
|
|
1418
|
+
localSessionId: target.session.localSessionId,
|
|
1419
|
+
command,
|
|
1420
|
+
}, 130_000));
|
|
1421
|
+
printTaskShellResult(result);
|
|
1422
|
+
if (result.exitCode && result.exitCode !== 0) process.exitCode = result.exitCode;
|
|
1423
|
+
return;
|
|
1424
|
+
}
|
|
1425
|
+
if (action === "dir") {
|
|
1426
|
+
const target = resolveTaskCommandTarget(allSessions, rest, action);
|
|
1427
|
+
const parsedDirectory = parseTaskDirectoryArgs([target.session.localSessionId, ...target.args]);
|
|
1428
|
+
const result = controlData<TaskDirectoryControlData>(await sendControlRequest(controlEndpoint(config.storage.sqlitePath), {
|
|
1429
|
+
action: "task_directory",
|
|
1430
|
+
localSessionId: target.session.localSessionId,
|
|
1431
|
+
...(parsedDirectory.directory ? { directory: parsedDirectory.directory } : {}),
|
|
1432
|
+
...(parsedDirectory.page !== undefined ? { page: parsedDirectory.page } : {}),
|
|
1433
|
+
}, 60_000));
|
|
1434
|
+
if (parsedDirectory.json) printJson(result);
|
|
1435
|
+
else printTaskDirectory(result);
|
|
1436
|
+
return;
|
|
1437
|
+
}
|
|
1438
|
+
if (action === "file") {
|
|
1439
|
+
const target = resolveTaskCommandTarget(allSessions, rest, action);
|
|
1440
|
+
const filePath = target.args.join(" ").trim();
|
|
1441
|
+
if (!filePath) throw new Error(cliText("task file requires a path.", "task file 需要文件路径。"));
|
|
1442
|
+
ensureOk(await sendControlRequest(controlEndpoint(config.storage.sqlitePath), {
|
|
1443
|
+
action: "task_send_file",
|
|
1444
|
+
localSessionId: target.session.localSessionId,
|
|
1445
|
+
filePath,
|
|
1446
|
+
}, 120_000));
|
|
1447
|
+
process.stdout.write(cliText("File sent to the task conversation.\n", "文件已发送到任务会话。\n"));
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
if (action === "restart") {
|
|
1451
|
+
const target = resolveTaskCommandTarget(allSessions, rest, action);
|
|
1452
|
+
const translated = target.args.flatMap((value) => value === "--force" ? ["--immediate"] : [value]);
|
|
1453
|
+
await serverCommand(["restart", "--task", target.session.localSessionId, ...translated]);
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
891
1456
|
if (action === "newgroup") {
|
|
892
|
-
const
|
|
893
|
-
const
|
|
1457
|
+
const target = resolveTaskCommandTarget(allSessions, rest, action);
|
|
1458
|
+
const options = parseTaskNewGroupOptions([target.session.localSessionId, ...target.args]);
|
|
1459
|
+
const session = target.session;
|
|
894
1460
|
const targetAgentName = options.agentName ?? session.agentName;
|
|
895
1461
|
const targetAgent = config.agents[targetAgentName];
|
|
896
1462
|
if (!targetAgent) {
|
|
@@ -920,8 +1486,9 @@ async function taskCommand(input: string[]): Promise<void> {
|
|
|
920
1486
|
return;
|
|
921
1487
|
}
|
|
922
1488
|
if (action === "forkgroup") {
|
|
923
|
-
const
|
|
924
|
-
const
|
|
1489
|
+
const target = resolveTaskCommandTarget(allSessions, rest, action);
|
|
1490
|
+
const options = parseTaskForkGroupOptions([target.session.localSessionId, ...target.args]);
|
|
1491
|
+
const session = target.session;
|
|
925
1492
|
requireCliGroupUser(config.feishu.userOpenId);
|
|
926
1493
|
const response = await sendControlRequest(controlEndpoint(config.storage.sqlitePath), {
|
|
927
1494
|
action: "task_fork_group",
|
|
@@ -933,37 +1500,50 @@ async function taskCommand(input: string[]): Promise<void> {
|
|
|
933
1500
|
else printTaskGroupResult(result, "forkgroup");
|
|
934
1501
|
return;
|
|
935
1502
|
}
|
|
936
|
-
const sessions = filterSessions(
|
|
937
|
-
|
|
1503
|
+
const sessions = filterSessions(
|
|
1504
|
+
allSessions,
|
|
1505
|
+
rest,
|
|
1506
|
+
action === "sessions" ? taskSessionsSearchTerm(rest) : undefined,
|
|
1507
|
+
);
|
|
1508
|
+
if (action === "list" || action === "sessions") {
|
|
938
1509
|
if (rest.includes("--json")) printJson(sessions);
|
|
939
1510
|
else printTaskList(sessions);
|
|
940
1511
|
return;
|
|
941
1512
|
}
|
|
942
|
-
const
|
|
943
|
-
|
|
944
|
-
`task ${action} requires a task number or task ID.`,
|
|
945
|
-
`task ${action} 需要任务序号或任务 ID。`,
|
|
946
|
-
));
|
|
947
|
-
const session = resolveTask(sessions, reference);
|
|
1513
|
+
const target = resolveTaskCommandTarget(allSessions, rest, action);
|
|
1514
|
+
const session = target.session;
|
|
948
1515
|
if (action === "chat") {
|
|
1516
|
+
rejectUnsupportedTaskOptions(action, target.args, ["--json"]);
|
|
1517
|
+
if (target.args.some((value) => !value.startsWith("--"))) throw new Error(cliText(
|
|
1518
|
+
"task chat accepts only one task reference.",
|
|
1519
|
+
"task chat 只接受一个任务引用。",
|
|
1520
|
+
));
|
|
949
1521
|
const route = taskChatRoute(session);
|
|
950
|
-
if (
|
|
1522
|
+
if (target.args.includes("--json")) printJson(route);
|
|
951
1523
|
else process.stdout.write(`${route.chatId}\n`);
|
|
952
1524
|
return;
|
|
953
1525
|
}
|
|
954
1526
|
if (action === "status") {
|
|
955
|
-
|
|
1527
|
+
rejectUnsupportedTaskOptions(action, target.args, ["--json"]);
|
|
1528
|
+
if (target.args.some((value) => !value.startsWith("--"))) throw new Error(cliText(
|
|
1529
|
+
"task status accepts only one task reference.",
|
|
1530
|
+
"task status 只接受一个任务引用。",
|
|
1531
|
+
));
|
|
1532
|
+
await outputTaskStatus(config.storage.sqlitePath, store, session, target.args.includes("--json"));
|
|
956
1533
|
return;
|
|
957
1534
|
}
|
|
958
1535
|
const endpoint = controlEndpoint(config.storage.sqlitePath);
|
|
959
1536
|
if (action === "stop") {
|
|
1537
|
+
if (target.args.length > 0) throw new Error(cliText(
|
|
1538
|
+
"task stop accepts only one task reference.",
|
|
1539
|
+
"task stop 只接受一个任务引用。",
|
|
1540
|
+
));
|
|
960
1541
|
ensureOk(await sendControlRequest(endpoint, { action: "task_stop", localSessionId: session.localSessionId }));
|
|
961
1542
|
process.stdout.write(cliText("Task stop requested.\n", "已请求停止任务。\n"));
|
|
962
1543
|
return;
|
|
963
1544
|
}
|
|
964
1545
|
if (action === "title") {
|
|
965
|
-
const
|
|
966
|
-
const titleArgs = rest.slice(referenceIndex + 1);
|
|
1546
|
+
const titleArgs = target.args;
|
|
967
1547
|
const optionIndex = titleArgs.findIndex((value) => value.startsWith("--"));
|
|
968
1548
|
const title = titleArgs.slice(0, optionIndex < 0 ? undefined : optionIndex).join(" ").trim();
|
|
969
1549
|
if (!title) throw new Error(cliText(
|
|
@@ -984,20 +1564,179 @@ async function taskCommand(input: string[]): Promise<void> {
|
|
|
984
1564
|
}
|
|
985
1565
|
}
|
|
986
1566
|
|
|
987
|
-
function
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
"无法检测当前任务:CODEX_THREAD_ID 和 TRAECLI_THREAD_ID 均未设置。请从 Codex 或 TraeX 任务中运行此命令。",
|
|
993
|
-
));
|
|
994
|
-
if (resolution.status === "not-found") throw new Error(cliText(
|
|
995
|
-
`No AgentBot task matches the current App Server Thread ID (${resolution.threadIds.join(", ")}). Check that the correct Profile is selected.`,
|
|
996
|
-
`没有 AgentBot 任务匹配当前 App Server Thread ID(${resolution.threadIds.join(",")})。请检查是否选择了正确的 Profile。`,
|
|
1567
|
+
function requireTaskCommandReference(action: string, reference: string | undefined): asserts reference is string {
|
|
1568
|
+
if (reference && !reference.startsWith("--")) return;
|
|
1569
|
+
throw new Error(cliText(
|
|
1570
|
+
`task ${action} requires a task number or task ID.`,
|
|
1571
|
+
`task ${action} 需要任务序号或任务 ID。`,
|
|
997
1572
|
));
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
function rejectUnsupportedTaskOptions(action: string, args: string[], supported: string[]): void {
|
|
1576
|
+
const unsupported = args.find((value) => value.startsWith("--") && !supported.includes(value));
|
|
1577
|
+
if (!unsupported) return;
|
|
998
1578
|
throw new Error(cliText(
|
|
999
|
-
|
|
1000
|
-
|
|
1579
|
+
`task ${action} does not support option: ${unsupported}.`,
|
|
1580
|
+
`task ${action} 不支持参数:${unsupported}。`,
|
|
1581
|
+
));
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
function parseTaskGoal(args: string[]): {
|
|
1585
|
+
action: "show" | "set" | "edit" | "pause" | "resume" | "clear";
|
|
1586
|
+
objective?: string;
|
|
1587
|
+
} {
|
|
1588
|
+
if (args.length === 0 || args[0]?.toLowerCase() === "show") return { action: "show" };
|
|
1589
|
+
const first = args[0]!.toLowerCase();
|
|
1590
|
+
if (first === "pause" || first === "resume" || first === "clear") {
|
|
1591
|
+
if (args.length > 1) throw new Error(cliText(
|
|
1592
|
+
`task goal ${first} does not accept extra arguments.`,
|
|
1593
|
+
`task goal ${first} 不接受额外参数。`,
|
|
1594
|
+
));
|
|
1595
|
+
return { action: first };
|
|
1596
|
+
}
|
|
1597
|
+
const action = first === "set" || first === "edit" ? first : "set";
|
|
1598
|
+
const objective = (action === "set" && first !== "set" ? args : args.slice(1)).join(" ").trim();
|
|
1599
|
+
if (!objective) throw new Error(cliText(
|
|
1600
|
+
`task goal ${action} requires an objective.`,
|
|
1601
|
+
`task goal ${action} 需要目标。`,
|
|
1602
|
+
));
|
|
1603
|
+
return { action, objective };
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
function parseTaskDirectoryArgs(args: string[]): {
|
|
1607
|
+
reference: string;
|
|
1608
|
+
directory?: string;
|
|
1609
|
+
page?: number;
|
|
1610
|
+
json: boolean;
|
|
1611
|
+
} {
|
|
1612
|
+
const positionals: string[] = [];
|
|
1613
|
+
let page: number | undefined;
|
|
1614
|
+
let json = false;
|
|
1615
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
1616
|
+
const value = args[index]!;
|
|
1617
|
+
if (value === "--json") {
|
|
1618
|
+
json = true;
|
|
1619
|
+
continue;
|
|
1620
|
+
}
|
|
1621
|
+
if (value === "--page") {
|
|
1622
|
+
const rawPage = args[index + 1];
|
|
1623
|
+
const parsedPage = rawPage === undefined ? Number.NaN : Number(rawPage);
|
|
1624
|
+
if (!Number.isSafeInteger(parsedPage) || parsedPage < 1) throw new Error(cliText(
|
|
1625
|
+
"task dir --page requires a positive page number.",
|
|
1626
|
+
"task dir --page 需要正整数页码。",
|
|
1627
|
+
));
|
|
1628
|
+
page = parsedPage - 1;
|
|
1629
|
+
index += 1;
|
|
1630
|
+
continue;
|
|
1631
|
+
}
|
|
1632
|
+
if (value.startsWith("--")) throw new Error(cliText(
|
|
1633
|
+
`task dir does not support option: ${value}.`,
|
|
1634
|
+
`task dir 不支持参数:${value}。`,
|
|
1635
|
+
));
|
|
1636
|
+
positionals.push(value);
|
|
1637
|
+
}
|
|
1638
|
+
const [reference, ...directoryParts] = positionals;
|
|
1639
|
+
requireTaskCommandReference("dir", reference);
|
|
1640
|
+
return {
|
|
1641
|
+
reference,
|
|
1642
|
+
directory: directoryParts.join(" ").trim() || undefined,
|
|
1643
|
+
page,
|
|
1644
|
+
json,
|
|
1645
|
+
};
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
function controlData<T>(response: ControlResponse): T {
|
|
1649
|
+
ensureOk(response);
|
|
1650
|
+
if (response.data === undefined) throw new Error(cliText(
|
|
1651
|
+
"Agent Bot returned no control result.",
|
|
1652
|
+
"Agent Bot 没有返回控制结果。",
|
|
1653
|
+
));
|
|
1654
|
+
return response.data as T;
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
function printCreatedTask(session: SessionRecord, label: string): void {
|
|
1658
|
+
process.stdout.write(`${cliText(`${label}: `, `${label === "Current task" ? "当前任务" : label === "Fork created" ? "分支任务已创建" : "任务已创建"}:`)}${session.title ?? "-"}\n`);
|
|
1659
|
+
process.stdout.write(`${cliText("Local ID: ", "本地 ID:")}${session.localSessionId}\n`);
|
|
1660
|
+
if (session.remoteSessionId) process.stdout.write(`${cliText("Agent task ID: ", "Agent 任务 ID:")}${session.remoteSessionId}\n`);
|
|
1661
|
+
process.stdout.write(`${cliText("Agent: ", "Agent:")}${session.agentName}\n`);
|
|
1662
|
+
process.stdout.write(`${cliText("Directory: ", "目录:")}${session.cwd}\n`);
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
function printTaskAgentSettings(result: TaskAgentControlData): void {
|
|
1666
|
+
process.stdout.write(`${cliText("Default Agent: ", "默认 Agent:")}${result.current}\n`);
|
|
1667
|
+
process.stdout.write(`${cliText("Available Agents: ", "可用 Agent:")}${result.agents.map((agent) => `${agent.name} (${agent.title})`).join(", ")}\n`);
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
function printTaskSettings(
|
|
1671
|
+
result: TaskSettingsControlData,
|
|
1672
|
+
setting: "provider" | "model" | "thinking" | "permissions",
|
|
1673
|
+
): void {
|
|
1674
|
+
const session = result.session;
|
|
1675
|
+
process.stdout.write(`${cliText("Provider: ", "Provider:")}${session.modelProvider ?? cliText("Agent default", "Agent 默认")}\n`);
|
|
1676
|
+
process.stdout.write(`${cliText("Model: ", "模型:")}${session.model ?? cliText("default", "默认")}\n`);
|
|
1677
|
+
process.stdout.write(`${cliText("Thinking: ", "思考强度:")}${session.reasoningEffort ?? cliText("automatic", "自动")}\n`);
|
|
1678
|
+
process.stdout.write(`${cliText("Permissions: ", "权限:")}${session.permissionMode ?? "auto"}\n`);
|
|
1679
|
+
if (setting === "provider") {
|
|
1680
|
+
process.stdout.write(`${cliText("Available Providers: ", "可用 Provider:")}${result.providers.map((item) => item.id).join(", ") || "-"}\n`);
|
|
1681
|
+
} else if (setting === "model") {
|
|
1682
|
+
process.stdout.write(`${cliText("Available Models: ", "可用模型:")}${result.models.map((item) => item.id).join(", ") || "-"}\n`);
|
|
1683
|
+
} else if (setting === "thinking") {
|
|
1684
|
+
process.stdout.write(`${cliText("Available Thinking Levels: ", "可用思考强度:")}${result.reasoningOptions.map((item) => item.value).join(", ") || "-"}\n`);
|
|
1685
|
+
} else {
|
|
1686
|
+
process.stdout.write(`${cliText("Available Permission Modes: ", "可用权限模式:")}${result.permissionModes.join(", ")}\n`);
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
function printTaskGoal(result: TaskGoalControlData): void {
|
|
1691
|
+
if (!result.goal) {
|
|
1692
|
+
process.stdout.write(result.cleared
|
|
1693
|
+
? cliText("Goal cleared.\n", "Goal 已清除。\n")
|
|
1694
|
+
: cliText("No Goal is configured.\n", "当前没有 Goal。\n"));
|
|
1695
|
+
return;
|
|
1696
|
+
}
|
|
1697
|
+
process.stdout.write(`${cliText("Goal: ", "Goal:")}${result.goal.objective}\n`);
|
|
1698
|
+
process.stdout.write(`${cliText("Status: ", "状态:")}${result.goal.status}\n`);
|
|
1699
|
+
process.stdout.write(`${cliText("Tokens used: ", "已用 Tokens:")}${result.goal.tokensUsed}\n`);
|
|
1700
|
+
process.stdout.write(`${cliText("Time used: ", "已用时间:")}${result.goal.timeUsedSeconds}s\n`);
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
function printTaskTurns(result: TaskTurnsControlData): void {
|
|
1704
|
+
process.stdout.write(`${cliText("Task: ", "任务:")}${result.session.title ?? result.session.localSessionId}\n`);
|
|
1705
|
+
if (result.turns.length === 0) {
|
|
1706
|
+
process.stdout.write(cliText("No completed turns.\n", "没有已完成的 Turn。\n"));
|
|
1707
|
+
return;
|
|
1708
|
+
}
|
|
1709
|
+
for (const turn of result.turns) {
|
|
1710
|
+
const marker = turn.current ? "*" : " ";
|
|
1711
|
+
const parent = turn.parentTurnId ? ` <- ${turn.parentTurnId}` : "";
|
|
1712
|
+
process.stdout.write(`${marker} ${turn.sequence}. ${turn.prompt?.replace(/\s+/g, " ").trim() || "-"}\n`);
|
|
1713
|
+
process.stdout.write(` ${turn.turnId}${parent}\n`);
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
function printTaskShellResult(result: TaskShellControlData): void {
|
|
1718
|
+
if (result.stdout) process.stdout.write(result.stdout);
|
|
1719
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
1720
|
+
if (!result.stdout && !result.stderr) process.stdout.write(cliText("(no output)\n", "(无输出)\n"));
|
|
1721
|
+
if (result.timedOut) process.stderr.write(cliText("Command timed out after 120 seconds.\n", "命令在 120 秒后超时。\n"));
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
function printTaskDirectory(result: TaskDirectoryControlData): void {
|
|
1725
|
+
process.stdout.write(`${cliText("Directory: ", "目录:")}${result.directory}\n`);
|
|
1726
|
+
if (result.parentDirectory) process.stdout.write(` [D] .. -> ${result.parentDirectory}\n`);
|
|
1727
|
+
for (const entry of result.entries) {
|
|
1728
|
+
const marker = entry.kind === "directory" || entry.kind === "drive"
|
|
1729
|
+
? "D"
|
|
1730
|
+
: entry.kind === "image"
|
|
1731
|
+
? "I"
|
|
1732
|
+
: entry.kind === "binary"
|
|
1733
|
+
? "B"
|
|
1734
|
+
: "F";
|
|
1735
|
+
process.stdout.write(` [${marker}] ${entry.name}\n`);
|
|
1736
|
+
}
|
|
1737
|
+
process.stdout.write(cliText(
|
|
1738
|
+
`Page ${result.page + 1}/${result.totalPages}; ${result.totalEntries} entries.\n`,
|
|
1739
|
+
`第 ${result.page + 1}/${result.totalPages} 页;共 ${result.totalEntries} 项。\n`,
|
|
1001
1740
|
));
|
|
1002
1741
|
}
|
|
1003
1742
|
|
|
@@ -1031,37 +1770,35 @@ async function outputTaskStatus(
|
|
|
1031
1770
|
else printTaskStatus(statusSession, snapshot, live?.remote);
|
|
1032
1771
|
}
|
|
1033
1772
|
|
|
1034
|
-
function filterSessions(sessions: SessionRecord[], args: string[]): SessionRecord[] {
|
|
1773
|
+
function filterSessions(sessions: SessionRecord[], args: string[], aliasSearchTerm?: string): SessionRecord[] {
|
|
1035
1774
|
const context = optionValue(args, "--context");
|
|
1036
1775
|
const status = optionValue(args, "--status");
|
|
1776
|
+
const search = (optionValue(args, "--search") ?? aliasSearchTerm)?.trim().toLowerCase();
|
|
1037
1777
|
return sessions.filter((session) =>
|
|
1038
1778
|
(!context || session.contextKey === context)
|
|
1039
|
-
&& (!status || session.status === status || session.lastTurnStatus === status)
|
|
1779
|
+
&& (!status || session.status === status || session.lastTurnStatus === status)
|
|
1780
|
+
&& (!search || [
|
|
1781
|
+
session.localSessionId,
|
|
1782
|
+
session.remoteSessionId,
|
|
1783
|
+
session.acpSessionId,
|
|
1784
|
+
session.title,
|
|
1785
|
+
session.agentName,
|
|
1786
|
+
session.cwd,
|
|
1787
|
+
].some((value) => value?.toLowerCase().includes(search))));
|
|
1040
1788
|
}
|
|
1041
1789
|
|
|
1042
|
-
function
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
const
|
|
1046
|
-
if (
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
if (exact) return exact;
|
|
1055
|
-
const matches = sessions.filter((session) =>
|
|
1056
|
-
session.localSessionId.startsWith(reference)
|
|
1057
|
-
|| session.remoteSessionId?.startsWith(reference)
|
|
1058
|
-
|| session.acpSessionId?.startsWith(reference));
|
|
1059
|
-
if (matches.length === 1) return matches[0]!;
|
|
1060
|
-
if (matches.length > 1) throw new Error(cliText(
|
|
1061
|
-
`Task ID prefix is ambiguous: ${reference}`,
|
|
1062
|
-
`任务 ID 前缀不唯一:${reference}`,
|
|
1063
|
-
));
|
|
1064
|
-
throw new Error(cliText(`Task not found: ${reference}`, `未找到任务:${reference}`));
|
|
1790
|
+
function taskSessionsSearchTerm(args: string[]): string | undefined {
|
|
1791
|
+
const positionals: string[] = [];
|
|
1792
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
1793
|
+
const value = args[index]!;
|
|
1794
|
+
if (value === "--json") continue;
|
|
1795
|
+
if (value === "--context" || value === "--status" || value === "--search") {
|
|
1796
|
+
index += 1;
|
|
1797
|
+
continue;
|
|
1798
|
+
}
|
|
1799
|
+
if (!value.startsWith("--")) positionals.push(value);
|
|
1800
|
+
}
|
|
1801
|
+
return positionals.join(" ").trim() || undefined;
|
|
1065
1802
|
}
|
|
1066
1803
|
|
|
1067
1804
|
function printTaskList(sessions: SessionRecord[]): void {
|
|
@@ -1136,6 +1873,13 @@ function printInitializationResult(result: Omit<InitCommandResult, "server" | "w
|
|
|
1136
1873
|
));
|
|
1137
1874
|
process.stdout.write(`${cliText("Configured Agents: ", "已配置 Agent:")}${result.configuredAgents.join(", ")}\n`);
|
|
1138
1875
|
process.stdout.write(`${cliText("Default Agent: ", "默认 Agent:")}${result.defaultAgent.name}\n`);
|
|
1876
|
+
process.stdout.write(cliText(
|
|
1877
|
+
`Group message response: ${result.groupMessages.mode === "all" ? "receive all group messages" : "require an explicit @ mention"}\n`,
|
|
1878
|
+
`群消息响应方式:${result.groupMessages.mode === "all" ? "接收所有群消息" : "必须明确 @ 机器人"}\n`,
|
|
1879
|
+
));
|
|
1880
|
+
process.stdout.write(result.skill.updated
|
|
1881
|
+
? `${cliText("Agent Bot Skill: installed or updated at ", "Agent Bot Skill:已安装或更新至 ")}${result.skill.status.targetPath}\n`
|
|
1882
|
+
: `${cliText("Agent Bot Skill: already up to date at ", "Agent Bot Skill:已是最新版本,位于 ")}${result.skill.status.targetPath}\n`);
|
|
1139
1883
|
if (result.feishu.status === "created") {
|
|
1140
1884
|
process.stdout.write(cliText(
|
|
1141
1885
|
`Lark app: created and credentials saved (${result.feishu.appId})\n`,
|