@keyou007/agent-bot 0.1.15-alpha.3 → 0.1.15-alpha.5
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 +11 -0
- package/README.md +4 -1
- package/README.zh.md +5 -2
- package/dist/cli/VerificationOutput.js +1 -6
- package/dist/cli/VerificationOutput.js.map +1 -1
- package/dist/cli.js +7 -13
- package/dist/cli.js.map +1 -1
- package/dist/commands/CommandRouter.js +7 -0
- package/dist/commands/CommandRouter.js.map +1 -1
- package/dist/feishu/CardRenderer.js +76 -9
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/feishu/FinalResponseBranding.js +1 -1
- package/dist/feishu/FinalResponseBranding.js.map +1 -1
- package/dist/proxy/ProxySessionController.js +161 -83
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/shell/ShellCommandJobManager.js +264 -0
- package/dist/shell/ShellCommandJobManager.js.map +1 -0
- package/dist/shell/shellCommandRunner.js +157 -0
- package/dist/shell/shellCommandRunner.js.map +1 -0
- package/dist/utils/executeShellCommand.js +1 -1
- package/dist/utils/executeShellCommand.js.map +1 -1
- package/docs/technical-reference.md +5 -3
- package/docs/technical-reference.zh.md +5 -3
- package/npm-shrinkwrap.json +2 -19
- package/package.json +1 -3
- package/src/cli/VerificationOutput.ts +1 -12
- package/src/cli.ts +6 -22
- package/src/commands/CommandRouter.ts +6 -0
- package/src/commands/commandTypes.ts +1 -0
- package/src/feishu/CardRenderer.ts +91 -17
- package/src/feishu/FinalResponseBranding.ts +2 -1
- package/src/proxy/ProxySessionController.ts +196 -89
- package/src/shell/ShellCommandJobManager.ts +341 -0
- package/src/shell/shellCommandRunner.ts +163 -0
- package/src/utils/executeShellCommand.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keyou007/agent-bot",
|
|
3
|
-
"version": "0.1.15-alpha.
|
|
3
|
+
"version": "0.1.15-alpha.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Use Codex, TraeX, and compatible coding agents from Lark/Feishu.",
|
|
6
6
|
"license": "GPL-3.0-only",
|
|
@@ -70,14 +70,12 @@
|
|
|
70
70
|
"better-sqlite3": "12.11.1",
|
|
71
71
|
"dotenv": "17.4.2",
|
|
72
72
|
"pino": "10.3.1",
|
|
73
|
-
"qrcode-terminal": "0.12.0",
|
|
74
73
|
"yaml": "2.9.0",
|
|
75
74
|
"zod": "4.4.3"
|
|
76
75
|
},
|
|
77
76
|
"devDependencies": {
|
|
78
77
|
"@types/better-sqlite3": "7.6.13",
|
|
79
78
|
"@types/node": "22.18.11",
|
|
80
|
-
"@types/qrcode-terminal": "0.12.2",
|
|
81
79
|
"eslint": "10.6.0",
|
|
82
80
|
"prettier": "3.9.5",
|
|
83
81
|
"tsx": "4.23.0",
|
|
@@ -1,25 +1,14 @@
|
|
|
1
|
-
import qrcode from "qrcode-terminal";
|
|
2
1
|
import { cliText, type CliLanguage } from "./i18n.js";
|
|
3
2
|
|
|
4
3
|
export interface VerificationOutputOptions {
|
|
5
4
|
verificationUrl: string;
|
|
6
|
-
json: boolean;
|
|
7
|
-
qrInstruction: string;
|
|
8
5
|
linkLabel?: string;
|
|
9
6
|
language?: CliLanguage;
|
|
10
7
|
}
|
|
11
8
|
|
|
12
|
-
export function
|
|
9
|
+
export function printVerificationLink(
|
|
13
10
|
options: VerificationOutputOptions,
|
|
14
11
|
write: (value: string) => void = (value) => process.stderr.write(value),
|
|
15
|
-
renderQr: (
|
|
16
|
-
value: string,
|
|
17
|
-
callback: (output: string) => void,
|
|
18
|
-
) => void = (value, callback) => qrcode.generate(value, { small: true }, callback),
|
|
19
12
|
): void {
|
|
20
|
-
if (!options.json) {
|
|
21
|
-
write(`\n${options.qrInstruction}\n\n`);
|
|
22
|
-
renderQr(options.verificationUrl, (output) => write(`${output}\n`));
|
|
23
|
-
}
|
|
24
13
|
write(`${options.linkLabel ?? cliText("Authorization link", "授权链接", options.language)}${cliText(":", ":", options.language)}\n${options.verificationUrl}\n\n`);
|
|
25
14
|
}
|
package/src/cli.ts
CHANGED
|
@@ -79,7 +79,7 @@ import {
|
|
|
79
79
|
type InitializationWelcomeResult,
|
|
80
80
|
} from "./cli/InitializationWelcome.js";
|
|
81
81
|
import { applyExplicitProfile, parseGlobalOptions } from "./cli/profile.js";
|
|
82
|
-
import {
|
|
82
|
+
import { printVerificationLink } from "./cli/VerificationOutput.js";
|
|
83
83
|
import {
|
|
84
84
|
startInitializedServer,
|
|
85
85
|
startServer,
|
|
@@ -818,7 +818,7 @@ async function initializeFeishu(
|
|
|
818
818
|
}
|
|
819
819
|
credentials = await registerFeishuApp({
|
|
820
820
|
signal: controller.signal,
|
|
821
|
-
onVerification: (challenge) => printFeishuVerification(challenge
|
|
821
|
+
onVerification: (challenge) => printFeishuVerification(challenge),
|
|
822
822
|
});
|
|
823
823
|
writeFeishuCredentials(paths.env.path, credentials);
|
|
824
824
|
status = "created";
|
|
@@ -839,7 +839,7 @@ async function initializeFeishu(
|
|
|
839
839
|
manualPermissionSkipSignal: manualPermissionSkip.signal,
|
|
840
840
|
optionalSkipSignal: optionalSkip.signal,
|
|
841
841
|
onVerification: (challenge) => {
|
|
842
|
-
printFeishuConfigurationVerification(challenge
|
|
842
|
+
printFeishuConfigurationVerification(challenge);
|
|
843
843
|
if (challenge.kind === "manual_scope" && challenge.blocking) {
|
|
844
844
|
skipListener?.close();
|
|
845
845
|
skipListener = listenForManualPermissionSkip(() => manualPermissionSkip.abort());
|
|
@@ -1981,15 +1981,10 @@ function printServerStartResult(result: ServerStartResult): void {
|
|
|
1981
1981
|
);
|
|
1982
1982
|
}
|
|
1983
1983
|
|
|
1984
|
-
function printFeishuVerification(challenge: FeishuAppRegistrationChallenge
|
|
1985
|
-
|
|
1984
|
+
function printFeishuVerification(challenge: FeishuAppRegistrationChallenge): void {
|
|
1985
|
+
printVerificationLink(
|
|
1986
1986
|
{
|
|
1987
1987
|
verificationUrl: challenge.verificationUrl,
|
|
1988
|
-
json,
|
|
1989
|
-
qrInstruction: cliText(
|
|
1990
|
-
"Scan this QR code with Lark to create the bot app:",
|
|
1991
|
-
"使用飞书扫描此二维码以创建机器人应用:",
|
|
1992
|
-
),
|
|
1993
1988
|
},
|
|
1994
1989
|
);
|
|
1995
1990
|
process.stderr.write(cliText(
|
|
@@ -2000,7 +1995,6 @@ function printFeishuVerification(challenge: FeishuAppRegistrationChallenge, json
|
|
|
2000
1995
|
|
|
2001
1996
|
function printFeishuConfigurationVerification(
|
|
2002
1997
|
challenge: FeishuConfigurationChallenge,
|
|
2003
|
-
json: boolean,
|
|
2004
1998
|
): void {
|
|
2005
1999
|
process.stderr.write(challenge.kind === "manual_scope"
|
|
2006
2000
|
? cliText(
|
|
@@ -2020,19 +2014,9 @@ function printFeishuConfigurationVerification(
|
|
|
2020
2014
|
if (challenge.missing.callbacks.length > 0) {
|
|
2021
2015
|
process.stderr.write(`${cliText("Callbacks: ", "回调:")}${challenge.missing.callbacks.join(", ")}\n`);
|
|
2022
2016
|
}
|
|
2023
|
-
|
|
2017
|
+
printVerificationLink(
|
|
2024
2018
|
{
|
|
2025
2019
|
verificationUrl: challenge.verificationUrl,
|
|
2026
|
-
json,
|
|
2027
|
-
qrInstruction: challenge.kind === "manual_scope"
|
|
2028
|
-
? cliText(
|
|
2029
|
-
"Scan this QR code with Lark to open the filtered permission page:",
|
|
2030
|
-
"使用飞书扫描此二维码以打开已筛选的权限页面:",
|
|
2031
|
-
)
|
|
2032
|
-
: cliText(
|
|
2033
|
-
"Scan this QR code with Lark to complete the configuration:",
|
|
2034
|
-
"使用飞书扫描此二维码以完成配置:",
|
|
2035
|
-
),
|
|
2036
2020
|
linkLabel: challenge.kind === "manual_scope"
|
|
2037
2021
|
? cliText("Permission page", "权限页面")
|
|
2038
2022
|
: undefined,
|
|
@@ -5,6 +5,7 @@ export const COMMAND_NAMES = [
|
|
|
5
5
|
"archive",
|
|
6
6
|
"dir",
|
|
7
7
|
"dismiss",
|
|
8
|
+
"file",
|
|
8
9
|
"fork",
|
|
9
10
|
"forkgroup",
|
|
10
11
|
"goal",
|
|
@@ -56,6 +57,11 @@ export class CommandRouter {
|
|
|
56
57
|
switch (command) {
|
|
57
58
|
case "dir":
|
|
58
59
|
return { type: "dir", directory: args.join(" ").trim() || undefined };
|
|
60
|
+
case "file": {
|
|
61
|
+
const filePath = args.join(" ").trim();
|
|
62
|
+
if (!filePath) throw new Error("请输入文件路径,例如:/file ./report.pdf。");
|
|
63
|
+
return { type: "file", filePath };
|
|
64
|
+
}
|
|
59
65
|
case "new":
|
|
60
66
|
return parseNewCommand(args);
|
|
61
67
|
case "newgroup":
|
|
@@ -3,6 +3,7 @@ export type Command =
|
|
|
3
3
|
| { type: "new"; title?: string; cwd?: string; projectless?: boolean }
|
|
4
4
|
| { type: "newgroup"; title?: string; cwd?: string; projectless?: boolean }
|
|
5
5
|
| { type: "dir"; directory?: string }
|
|
6
|
+
| { type: "file"; filePath: string }
|
|
6
7
|
| { type: "forkgroup"; title?: string }
|
|
7
8
|
| { type: "fork"; sessionId?: string }
|
|
8
9
|
| { type: "title"; title: string }
|
|
@@ -240,11 +240,12 @@ export interface DismissGroupCardView {
|
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
export interface ShellCommandCardView {
|
|
243
|
+
jobId?: string;
|
|
244
|
+
contextKey?: string;
|
|
243
245
|
command: string;
|
|
244
246
|
cwd: string;
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
status: "running" | "completed" | "failed" | "timed_out";
|
|
247
|
+
output: string;
|
|
248
|
+
status: "running" | "cancelling" | "completed" | "failed" | "cancelled" | "timed_out";
|
|
248
249
|
exitCode?: number | null;
|
|
249
250
|
elapsedMs: number;
|
|
250
251
|
outputTruncated: boolean;
|
|
@@ -258,12 +259,7 @@ export class CardRenderer {
|
|
|
258
259
|
}
|
|
259
260
|
|
|
260
261
|
renderShellCommandCard(view: ShellCommandCardView): Record<string, unknown> {
|
|
261
|
-
const
|
|
262
|
-
const stderr = normalizeTerminalOutput(view.stderr).trimEnd();
|
|
263
|
-
const rawOutput = [
|
|
264
|
-
stdout,
|
|
265
|
-
stderr ? `[stderr]\n${stderr}` : "",
|
|
266
|
-
].filter(Boolean).join("\n");
|
|
262
|
+
const rawOutput = normalizeTerminalOutput(view.output).trimEnd();
|
|
267
263
|
const displayOutput = truncateMiddle(
|
|
268
264
|
rawOutput,
|
|
269
265
|
6_000,
|
|
@@ -273,15 +269,32 @@ export class CardRenderer {
|
|
|
273
269
|
const output = displayOutput || (view.status === "running" ? "等待输出..." : "(无输出)");
|
|
274
270
|
const status = shellCommandStatus(view);
|
|
275
271
|
const outputWasTruncated = view.outputTruncated || rawOutput.length > 6_000;
|
|
272
|
+
const elements = [
|
|
273
|
+
markdown(codeBlock(`$ ${command}\n${output}`, 6_700)),
|
|
274
|
+
markdown([
|
|
275
|
+
`${inlineCode(view.cwd)} · ${status.label} · ${formatTurnDuration(view.elapsedMs)}`,
|
|
276
|
+
...(outputWasTruncated ? ["输出过长,已保留开头和结尾并截断中间内容。"] : []),
|
|
277
|
+
].join("\n")),
|
|
278
|
+
];
|
|
279
|
+
if (view.status === "running" && view.jobId && view.contextKey) {
|
|
280
|
+
elements.push({
|
|
281
|
+
tag: "button",
|
|
282
|
+
text: { tag: "plain_text", content: "Cancel" },
|
|
283
|
+
type: "danger",
|
|
284
|
+
size: "small",
|
|
285
|
+
behaviors: [{
|
|
286
|
+
type: "callback",
|
|
287
|
+
value: {
|
|
288
|
+
action: "shell_command_cancel",
|
|
289
|
+
jobId: view.jobId,
|
|
290
|
+
contextKey: view.contextKey,
|
|
291
|
+
},
|
|
292
|
+
}],
|
|
293
|
+
});
|
|
294
|
+
}
|
|
276
295
|
return sectionCard(
|
|
277
296
|
status.title,
|
|
278
|
-
|
|
279
|
-
markdown(codeBlock(`$ ${command}\n${output}`, 6_700)),
|
|
280
|
-
markdown([
|
|
281
|
-
`${inlineCode(view.cwd)} · ${status.label} · ${formatTurnDuration(view.elapsedMs)}`,
|
|
282
|
-
...(outputWasTruncated ? ["输出过长,已保留开头和结尾并截断中间内容。"] : []),
|
|
283
|
-
].join("\n")),
|
|
284
|
-
],
|
|
297
|
+
elements,
|
|
285
298
|
status.template,
|
|
286
299
|
);
|
|
287
300
|
}
|
|
@@ -2052,11 +2065,66 @@ function renderToolDetails(tool: ToolState, projectCwd?: string): string {
|
|
|
2052
2065
|
const displayCommand = tool.kind === "command"
|
|
2053
2066
|
? unwrapShellCommand(normalizedCommand) ?? normalizedCommand
|
|
2054
2067
|
: normalizedCommand;
|
|
2055
|
-
const commandText = truncateText(
|
|
2068
|
+
const commandText = truncateText(
|
|
2069
|
+
tool.kind === "command" ? formatShellCommandForDisplay(displayCommand) : displayCommand,
|
|
2070
|
+
600,
|
|
2071
|
+
);
|
|
2056
2072
|
const resultText = result ? truncateToolResult(normalizeTerminalOutput(result).trim()) : undefined;
|
|
2057
2073
|
return codeBlock([`$ ${commandText}`, resultText].filter((part): part is string => part !== undefined).join("\n"), 2_003);
|
|
2058
2074
|
}
|
|
2059
2075
|
|
|
2076
|
+
function formatShellCommandForDisplay(command: string): string {
|
|
2077
|
+
const normalized = command.replace(/\r\n/g, "\n");
|
|
2078
|
+
let result = "";
|
|
2079
|
+
let quote: "'" | "\"" | undefined;
|
|
2080
|
+
|
|
2081
|
+
for (let index = 0; index < normalized.length; index += 1) {
|
|
2082
|
+
const character = normalized[index] ?? "";
|
|
2083
|
+
|
|
2084
|
+
if (quote) {
|
|
2085
|
+
result += character;
|
|
2086
|
+
if ((character === "\\" || character === "`") && quote === "\"") {
|
|
2087
|
+
if (index + 1 < normalized.length) result += normalized[++index];
|
|
2088
|
+
} else if (character === quote) {
|
|
2089
|
+
quote = undefined;
|
|
2090
|
+
}
|
|
2091
|
+
continue;
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
if (character === "'" || character === "\"") {
|
|
2095
|
+
quote = character;
|
|
2096
|
+
result += character;
|
|
2097
|
+
continue;
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
if (character === "\\" || character === "`" || character === "^") {
|
|
2101
|
+
result += character;
|
|
2102
|
+
if (index + 1 < normalized.length) result += normalized[++index];
|
|
2103
|
+
continue;
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
const operator = readShellDisplayOperator(normalized, index);
|
|
2107
|
+
if (!operator) {
|
|
2108
|
+
result += character;
|
|
2109
|
+
continue;
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
result += operator;
|
|
2113
|
+
index += operator.length - 1;
|
|
2114
|
+
while (index + 1 < normalized.length && /\s/.test(normalized[index + 1] ?? "")) index += 1;
|
|
2115
|
+
if (index + 1 < normalized.length) result += " \\\n ";
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
return result;
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
function readShellDisplayOperator(command: string, index: number): string | undefined {
|
|
2122
|
+
for (const operator of ["&&", "||", "|&", ";", "|"]) {
|
|
2123
|
+
if (command.startsWith(operator, index)) return operator;
|
|
2124
|
+
}
|
|
2125
|
+
return undefined;
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2060
2128
|
function normalizeTerminalOutput(value: string): string {
|
|
2061
2129
|
const clean = stripAnsi(value).replace(/\r\n/g, "\n");
|
|
2062
2130
|
return clean
|
|
@@ -2167,6 +2235,12 @@ function shellCommandStatus(view: ShellCommandCardView): {
|
|
|
2167
2235
|
if (view.status === "running") {
|
|
2168
2236
|
return { title: "正在执行命令", label: "运行中", template: "blue" };
|
|
2169
2237
|
}
|
|
2238
|
+
if (view.status === "cancelling") {
|
|
2239
|
+
return { title: "正在取消命令", label: "正在取消", template: "orange" };
|
|
2240
|
+
}
|
|
2241
|
+
if (view.status === "cancelled") {
|
|
2242
|
+
return { title: "命令已取消", label: "已取消", template: "grey" };
|
|
2243
|
+
}
|
|
2170
2244
|
if (view.status === "timed_out") {
|
|
2171
2245
|
return { title: "命令执行超时", label: "已超时(120s)", template: "orange" };
|
|
2172
2246
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export const FINAL_RESPONSE_BRANDING =
|
|
1
|
+
export const FINAL_RESPONSE_BRANDING =
|
|
2
|
+
"> Powered by [AgentBot](https://bytedance.larkoffice.com/docx/UTukdYBopojSivxRVeUcCXTtnid)";
|
|
2
3
|
export const FINAL_RESPONSE_BRANDING_BLOCK = `----\n\n${FINAL_RESPONSE_BRANDING}`;
|
|
3
4
|
|
|
4
5
|
export function splitFinalResponseBranding(markdown: string): {
|