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