@keyou007/agent-bot 0.1.17-alpha.0 → 0.1.17-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.
Files changed (51) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +3 -1
  3. package/README.zh.md +3 -1
  4. package/config.example.yaml +12 -0
  5. package/dist/codex/CodexProcessManager.js +2 -1
  6. package/dist/codex/CodexProcessManager.js.map +1 -1
  7. package/dist/codex/CodexRuntime.js +22 -2
  8. package/dist/codex/CodexRuntime.js.map +1 -1
  9. package/dist/config/loadConfig.js +10 -0
  10. package/dist/config/loadConfig.js.map +1 -1
  11. package/dist/config/schema.js +6 -0
  12. package/dist/config/schema.js.map +1 -1
  13. package/dist/feishu/FeishuConnector.js +61 -5
  14. package/dist/feishu/FeishuConnector.js.map +1 -1
  15. package/dist/feishu/FeishuMarkdown.js +42 -13
  16. package/dist/feishu/FeishuMarkdown.js.map +1 -1
  17. package/dist/feishu/FeishuTurnPresenter.js +35 -12
  18. package/dist/feishu/FeishuTurnPresenter.js.map +1 -1
  19. package/dist/feishu/LocalImageMarkdown.js +16 -2
  20. package/dist/feishu/LocalImageMarkdown.js.map +1 -1
  21. package/dist/index.js +33 -5
  22. package/dist/index.js.map +1 -1
  23. package/dist/local-files/LocalFileViewerServer.js +706 -0
  24. package/dist/local-files/LocalFileViewerServer.js.map +1 -0
  25. package/dist/local-files/NetworkAddressSelector.js +97 -0
  26. package/dist/local-files/NetworkAddressSelector.js.map +1 -0
  27. package/dist/proxy/ProxySessionController.js +43 -29
  28. package/dist/proxy/ProxySessionController.js.map +1 -1
  29. package/dist/supervision/SafeRestartScheduler.js +15 -2
  30. package/dist/supervision/SafeRestartScheduler.js.map +1 -1
  31. package/dist/utils/spawnCommand.js +2 -1
  32. package/dist/utils/spawnCommand.js.map +1 -1
  33. package/docs/technical-reference.md +8 -0
  34. package/docs/technical-reference.zh.md +8 -0
  35. package/npm-shrinkwrap.json +9 -9
  36. package/package.json +2 -2
  37. package/skills/agent-bot/SKILL.md +1 -1
  38. package/src/codex/CodexProcessManager.ts +3 -1
  39. package/src/codex/CodexRuntime.ts +25 -2
  40. package/src/config/loadConfig.ts +10 -0
  41. package/src/config/schema.ts +9 -0
  42. package/src/feishu/FeishuConnector.ts +70 -5
  43. package/src/feishu/FeishuMarkdown.ts +51 -12
  44. package/src/feishu/FeishuTurnPresenter.ts +43 -13
  45. package/src/feishu/LocalImageMarkdown.ts +23 -2
  46. package/src/index.ts +33 -4
  47. package/src/local-files/LocalFileViewerServer.ts +774 -0
  48. package/src/local-files/NetworkAddressSelector.ts +111 -0
  49. package/src/proxy/ProxySessionController.ts +33 -20
  50. package/src/supervision/SafeRestartScheduler.ts +23 -2
  51. package/src/utils/spawnCommand.ts +2 -0
@@ -8,6 +8,7 @@ export class SafeRestartScheduler {
8
8
  idleSince;
9
9
  idleInboundAt;
10
10
  scheduleId = 0;
11
+ restartImmediatelyIfIdle = false;
11
12
  constructor(options) {
12
13
  this.options = options;
13
14
  }
@@ -17,10 +18,14 @@ export class SafeRestartScheduler {
17
18
  get pendingReason() {
18
19
  return this.reason;
19
20
  }
20
- schedule(reason, notificationTarget) {
21
+ schedule(reason, notificationTarget, options = {}) {
21
22
  const newlyScheduled = !this.reason;
22
23
  if (newlyScheduled) {
23
24
  this.notificationTargets = [];
25
+ this.restartImmediatelyIfIdle = options.restartImmediatelyIfIdle === true;
26
+ }
27
+ else if (options.restartImmediatelyIfIdle) {
28
+ this.restartImmediatelyIfIdle = true;
24
29
  }
25
30
  this.scheduleId += 1;
26
31
  this.addNotificationTarget(notificationTarget, reason);
@@ -65,6 +70,7 @@ export class SafeRestartScheduler {
65
70
  this.notificationTargets = [];
66
71
  this.idleSince = undefined;
67
72
  this.idleInboundAt = undefined;
73
+ this.restartImmediatelyIfIdle = false;
68
74
  }
69
75
  async poll() {
70
76
  const reason = this.reason;
@@ -84,6 +90,10 @@ export class SafeRestartScheduler {
84
90
  });
85
91
  return;
86
92
  }
93
+ if (this.restartImmediatelyIfIdle) {
94
+ await this.restart(reason, scheduleId, state);
95
+ return;
96
+ }
87
97
  const quietPeriodMs = this.options.quietPeriodMs ?? 15_000;
88
98
  const latestInboundAt = state.latestInboundAt ?? "";
89
99
  if (this.idleSince === undefined || this.idleInboundAt !== latestInboundAt) {
@@ -138,6 +148,9 @@ export class SafeRestartScheduler {
138
148
  }
139
149
  return;
140
150
  }
151
+ await this.restart(reason, scheduleId, confirmed);
152
+ }
153
+ async restart(reason, scheduleId, activity) {
141
154
  const notificationTargets = this.notificationTargetsSnapshot();
142
155
  this.clear();
143
156
  await this.emitStatus({
@@ -145,7 +158,7 @@ export class SafeRestartScheduler {
145
158
  reason,
146
159
  notificationTargets,
147
160
  phase: "restarting",
148
- activity: confirmed,
161
+ activity,
149
162
  remainingMs: 0,
150
163
  });
151
164
  await this.options.onReady(reason, notificationTargets);
@@ -1 +1 @@
1
- {"version":3,"file":"SafeRestartScheduler.js","sourceRoot":"","sources":["../../src/supervision/SafeRestartScheduler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SafeRestartScheduler.js","sourceRoot":"","sources":["../../src/supervision/SafeRestartScheduler.ts"],"names":[],"mappings":"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"}
@@ -5,9 +5,10 @@ export function prepareCommand(command, args, platform = process.platform, comsp
5
5
  const commandLine = [command, ...args].map(quoteCmdToken).join(" ");
6
6
  return { command: comspec, args: ["/d", "/s", "/c", commandLine] };
7
7
  }
8
- export function spawnStdioCommand(command, args, env) {
8
+ export function spawnStdioCommand(command, args, env, cwd) {
9
9
  const prepared = prepareCommand(command, args);
10
10
  return spawn(prepared.command, prepared.args, {
11
+ cwd,
11
12
  env,
12
13
  stdio: ["pipe", "pipe", "pipe"],
13
14
  windowsHide: true,
@@ -1 +1 @@
1
- {"version":3,"file":"spawnCommand.js","sourceRoot":"","sources":["../../src/utils/spawnCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAuC,MAAM,oBAAoB,CAAC;AAOhF,MAAM,UAAU,cAAc,CAC5B,OAAe,EACf,IAAc,EACd,QAAQ,GAAoB,OAAO,CAAC,QAAQ,EAC5C,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,SAAS;IAEjE,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACnD,MAAM,WAAW,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,OAAe,EACf,IAAc,EACd,GAAsB;IAEtB,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC/C,OAAO,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,EAAE;QAC5C,GAAG;QACH,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;QAC/B,WAAW,EAAE,IAAI;KAClB,CAAC,CAAC;AACL,CAAC;AAED,SAAS,aAAa,CAAC,KAAa;IAClC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACpE,OAAO,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC;AAC5C,CAAC"}
1
+ {"version":3,"file":"spawnCommand.js","sourceRoot":"","sources":["../../src/utils/spawnCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAuC,MAAM,oBAAoB,CAAC;AAOhF,MAAM,UAAU,cAAc,CAC5B,OAAe,EACf,IAAc,EACd,QAAQ,GAAoB,OAAO,CAAC,QAAQ,EAC5C,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,SAAS;IAEjE,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACnD,MAAM,WAAW,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,OAAe,EACf,IAAc,EACd,GAAsB,EACtB,GAAY;IAEZ,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC/C,OAAO,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,EAAE;QAC5C,GAAG;QACH,GAAG;QACH,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;QAC/B,WAAW,EAAE,IAAI;KAClB,CAAC,CAAC;AACL,CAAC;AAED,SAAS,aAAa,CAAC,KAAa;IAClC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACpE,OAAO,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC;AAC5C,CAAC"}
@@ -156,6 +156,12 @@ feishu:
156
156
  console:
157
157
  enabled: true
158
158
 
159
+ fileViewer:
160
+ enabled: true
161
+ host: "127.0.0.1"
162
+ port: 0
163
+ # publicBaseUrl: "https://agentbot.example.com/files"
164
+
159
165
  agents:
160
166
  codex:
161
167
  kind: "app-server"
@@ -187,6 +193,8 @@ logging:
187
193
  path: "./logs/agent-bot.log"
188
194
  ```
189
195
 
196
+ `fileViewer` turns local non-image files and directories in final replies into signed, read-only HTTP links. The default `host: "127.0.0.1"` accepts connections only from the Agent Bot machine. With `0.0.0.0` or `::`, the server listens on every interface and automatically chooses a non-internal IPv4 link address in wired, Wi-Fi, other physical, then VPN order. Docker, Hyper-V, VMware, VirtualBox, WSL, loopback, and link-local interfaces are excluded; if no candidate remains, links fall back to loopback. `publicBaseUrl` has the highest priority and overrides automatic selection for domains, HTTPS reverse proxies, NAT, or port mappings. With `port: 0`, the first startup selects a free port and stores it in `<data>/file-viewer/port`; later restarts reuse it, while separate Profiles avoid a shared fixed port. The signing key is stored beside it as `secret`. File URLs can read only their signed absolute path. Directory URLs list direct children and allow downward navigation without exposing a parent-directory action. Text identity is detected from a content sample, and text pages preview the first 2 MiB with line anchors; images, PDFs, audio, and video use native browser previews; other binary files expose raw and download actions. Treat each signed URL as a bearer credential and do not share it publicly.
197
+
190
198
  `feishu.respondToOwnerOnly` defaults to `true`. It compares each message sender and card-action operator Open ID with `feishu.userOpenId`, ignoring non-owner input before durable event claims, reactions, downloads, commands, or Agent work. Set it to `false` to allow other users. If it is enabled without a configured owner Open ID, all Feishu messages and card actions are ignored; configure `FEISHU_USER_OPEN_ID` or temporarily disable the restriction to bootstrap from a private message.
191
199
 
192
200
  `feishu.respondToAllGroupMessages` defaults to `true` in the template, while first initialization and `init --reset` write the user's selected mode. Set it to `false` to ignore owner group messages unless they mention the current bot. The worker resolves the bot's Open ID at startup so mentioning another member does not trigger it. Owner private messages are unaffected. Initialization requests all-user group-message delivery only when this value is `true`; changing a mention-only Profile to `true` later requires rerunning `agentbot init` to add the permission.
@@ -156,6 +156,12 @@ feishu:
156
156
  console:
157
157
  enabled: true
158
158
 
159
+ fileViewer:
160
+ enabled: true
161
+ host: "127.0.0.1"
162
+ port: 0
163
+ # publicBaseUrl: "https://agentbot.example.com/files"
164
+
159
165
  agents:
160
166
  codex:
161
167
  kind: "app-server"
@@ -187,6 +193,8 @@ logging:
187
193
  path: "./logs/agent-bot.log"
188
194
  ```
189
195
 
196
+ `fileViewer` 为最终回答中的本地非图片文件和目录生成带签名的只读 HTTP 链接。默认 `host: "127.0.0.1"`,只能从运行 Agent Bot 的机器访问。设为 `0.0.0.0` 或 `::` 时,服务会监听所有网卡,并从非内部 IPv4 地址中按有线、Wi-Fi、其他物理网卡、VPN 的顺序自动选择链接地址;Docker、Hyper-V、VMware、VirtualBox、WSL、回环和链路本地地址会被排除,没有候选地址时回退到本机环回地址。`publicBaseUrl` 的优先级最高,用于域名、HTTPS 反向代理、NAT 或端口映射。`port: 0` 会在第一次启动时选择空闲端口,并保存到 `<data>/file-viewer/port`,以后重启继续使用,同一机器上的多个 Profile 不会争用固定端口。签名密钥保存在同目录的 `secret` 文件中。文件 URL 只能读取签发时对应的绝对路径;目录 URL 会列出其直接子项并允许继续向下浏览,但不提供向父目录跳转。文本身份由内容采样识别,页面显示带行号的开头 2 MiB;图片、PDF、音视频使用浏览器原生预览,其他二进制文件提供原始打开和下载入口。签名 URL 是持有者凭证,不应公开分享。
197
+
190
198
  `feishu.respondToOwnerOnly` 默认为 `true`。每条消息的发送者及卡片操作人的 Open ID 都会与 `feishu.userOpenId` 比较;非拥有者输入会在持久化事件、添加 reaction、下载图片、执行命令或启动 Agent 前被忽略。设为 `false` 可允许其他用户。开启后若未配置拥有者 Open ID,所有飞书消息和卡片操作都会被忽略;需先配置 `FEISHU_USER_OPEN_ID`,或临时关闭限制后通过私聊完成补全。
191
199
 
192
200
  `feishu.respondToAllGroupMessages` 在模板中默认为 `true`,第一次初始化和 `init --reset` 会按用户选择写入实际值。设为 `false` 后,拥有者未 @ 当前机器人的群消息也会被忽略;Worker 启动时会解析机器人的 Open ID,因此 @ 其他成员不会误触发。拥有者私聊不受影响。初始化只在该值为 `true` 时申请接收全部群消息的权限;仅 @ 响应的 Profile 后续改为 `true` 时,需要重新运行 `agentbot init` 补充权限。
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@keyou007/agent-bot",
3
- "version": "0.1.17-alpha.0",
3
+ "version": "0.1.17-alpha.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@keyou007/agent-bot",
9
- "version": "0.1.17-alpha.0",
9
+ "version": "0.1.17-alpha.2",
10
10
  "license": "GPL-3.0-only",
11
11
  "dependencies": {
12
12
  "@agentclientprotocol/sdk": "1.2.1",
13
13
  "@clack/prompts": "1.7.0",
14
- "@larksuiteoapi/node-sdk": "1.56.1",
14
+ "@larksuiteoapi/node-sdk": "1.73.0",
15
15
  "@resvg/resvg-js": "2.6.2",
16
16
  "better-sqlite3": "12.11.1",
17
17
  "dotenv": "17.4.2",
@@ -730,18 +730,18 @@
730
730
  "license": "MIT"
731
731
  },
732
732
  "node_modules/@larksuiteoapi/node-sdk": {
733
- "version": "1.56.1",
734
- "resolved": "https://registry.npmjs.org/@larksuiteoapi/node-sdk/-/node-sdk-1.56.1.tgz",
735
- "integrity": "sha512-/ixtyJnWOmcupKgDXz+6G6qTLMi3cNrR+LGOuq2PMwcJ6hhXTUJNyAF+ADY7ah9OoeDniGU/UJwMb2gqKdxwcA==",
733
+ "version": "1.73.0",
734
+ "resolved": "https://registry.npmjs.org/@larksuiteoapi/node-sdk/-/node-sdk-1.73.0.tgz",
735
+ "integrity": "sha512-gs4EiupJ6RdYAEJGtuYrCVsB7wSMBwtUDK+ilpmOHD6RTvbtciW6AWciQO9coOQVuBZfh7hvAi25dPfCsny/nA==",
736
736
  "license": "MIT",
737
737
  "dependencies": {
738
- "axios": "0.27.2",
738
+ "axios": "^1.16.0",
739
739
  "lodash.identity": "^3.0.0",
740
740
  "lodash.merge": "^4.6.2",
741
741
  "lodash.pickby": "^4.6.0",
742
742
  "protobufjs": "^7.2.6",
743
- "qs": "^6.13.0",
744
- "ws": "^8.16.0"
743
+ "qs": "^6.14.2",
744
+ "ws": "^8.19.0"
745
745
  }
746
746
  },
747
747
  "node_modules/@napi-rs/wasm-runtime": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keyou007/agent-bot",
3
- "version": "0.1.17-alpha.0",
3
+ "version": "0.1.17-alpha.2",
4
4
  "type": "module",
5
5
  "description": "Use Codex, TraeX, and compatible coding agents from Lark/Feishu.",
6
6
  "license": "GPL-3.0-only",
@@ -69,7 +69,7 @@
69
69
  "dependencies": {
70
70
  "@agentclientprotocol/sdk": "1.2.1",
71
71
  "@clack/prompts": "1.7.0",
72
- "@larksuiteoapi/node-sdk": "1.56.1",
72
+ "@larksuiteoapi/node-sdk": "1.73.0",
73
73
  "@resvg/resvg-js": "2.6.2",
74
74
  "better-sqlite3": "12.11.1",
75
75
  "dotenv": "17.4.2",
@@ -144,7 +144,7 @@ agentbot server restart --task <task> --reason "<reason>"
144
144
  agentbot server stop
145
145
  ```
146
146
 
147
- `update` is only for npm-installed Agent Bot packages. It verifies the new package before waiting for a safe service stop and automatically restores the prior version if activation fails. It refuses source checkouts and `npm link` installations.
147
+ `update` is only for npm-installed Agent Bot packages. It verifies the new package, sends a safe-restart card, waits for active tasks, and restarts immediately when the service is already idle. It automatically restores the prior version if activation fails. It refuses source checkouts and `npm link` installations.
148
148
 
149
149
  Autostart is Profile-specific. Use `server autostart enable` for login startup, `server autostart enable --linger` on Linux only when the user explicitly requests startup before login, and `server autostart disable` to remove registration without stopping the current Server. Disabling Agent Bot autostart must not disable Linux user lingering because other services may use it.
150
150
 
@@ -29,10 +29,12 @@ export class CodexProcessManager implements AppServerClientProvider {
29
29
  async getClient(): Promise<AppServerClient> {
30
30
  if (this.client) return this.client;
31
31
  this.version = undefined;
32
+ const environmentContext = this.environmentContext();
32
33
  const child = spawnStdioCommand(
33
34
  this.command,
34
35
  this.args,
35
- agentBotEnvironment(process.env, this.env, this.environmentContext()),
36
+ agentBotEnvironment(process.env, this.env, environmentContext),
37
+ environmentContext.profilePath ?? os.homedir(),
36
38
  );
37
39
  this.child = child;
38
40
  const client = new AppServerConnection(child, this.logger.child({ component: "codex-app-server" }));
@@ -191,7 +191,7 @@ export class CodexRuntime implements AgentRuntime {
191
191
  const session = this.requireSession(sessionId);
192
192
  const client = await this.client();
193
193
  await this.ensureSessionResumed(session, client);
194
- const response = await client.request<{ turn: { id: string } }>("turn/start", {
194
+ const start = () => client.request<{ turn: { id: string } }>("turn/start", {
195
195
  threadId: session.remoteSessionId,
196
196
  input: codexUserInput(prompt),
197
197
  cwd: session.cwd,
@@ -200,6 +200,19 @@ export class CodexRuntime implements AgentRuntime {
200
200
  summary: "auto",
201
201
  approvalPolicy: session.permissionMode === "auto" ? "never" : "on-request",
202
202
  }, SESSION_REQUEST_TIMEOUT_MS);
203
+ let response: { turn: { id: string } };
204
+ try {
205
+ response = await start();
206
+ } catch (error) {
207
+ if (!isInvalidWorkingDirectoryError(error)) throw error;
208
+ this.logger.warn(
209
+ { error, sessionId: session.localSessionId, cwd: session.cwd },
210
+ "App Server rejected the task working directory; refreshing the current thread before retrying.",
211
+ );
212
+ await this.resumeAppServerSession(session, client);
213
+ session.needsResume = false;
214
+ response = await start();
215
+ }
203
216
  if (session.activeTurnId !== response.turn.id) {
204
217
  this.adoptTurn(session, response.turn.id, Date.now());
205
218
  }
@@ -746,6 +759,11 @@ export class CodexRuntime implements AgentRuntime {
746
759
 
747
760
  private async ensureSessionResumed(session: CodexSession, client: AppServerClient): Promise<void> {
748
761
  if (!session.needsResume) return;
762
+ await this.resumeAppServerSession(session, client);
763
+ session.needsResume = false;
764
+ }
765
+
766
+ private async resumeAppServerSession(session: CodexSession, client: AppServerClient): Promise<void> {
749
767
  await client.request("thread/resume", {
750
768
  threadId: session.remoteSessionId,
751
769
  excludeTurns: true,
@@ -755,7 +773,6 @@ export class CodexRuntime implements AgentRuntime {
755
773
  ...threadLifecycleParams(session.cwd),
756
774
  ...permissionParams(session.permissionMode),
757
775
  }, SESSION_REQUEST_TIMEOUT_MS);
758
- session.needsResume = false;
759
776
  }
760
777
 
761
778
  private async synchronizeSessionNow(sessionId: string): Promise<RuntimeSession> {
@@ -1084,6 +1101,12 @@ function isUnsupportedExcludeTurnsError(error: unknown): boolean {
1084
1101
  || /unknown|unrecognized|unexpected|unsupported|not supported|experimental/i.test(details);
1085
1102
  }
1086
1103
 
1104
+ function isInvalidWorkingDirectoryError(error: unknown): boolean {
1105
+ return error instanceof AppServerRequestError
1106
+ && error.method === "turn/start"
1107
+ && /invalid cwd/i.test(`${error.serverMessage} ${error.data === undefined ? "" : JSON.stringify(error.data)}`);
1108
+ }
1109
+
1087
1110
  function isUnsupportedRecencySortError(error: unknown): boolean {
1088
1111
  if (!(error instanceof AppServerRequestError) || error.method !== "thread/list") return false;
1089
1112
  const data = typeof error.data === "string"
@@ -26,6 +26,16 @@ const DEFAULT_CONFIG = `feishu:
26
26
  console:
27
27
  enabled: true
28
28
 
29
+ fileViewer:
30
+ enabled: true
31
+ # Use 0.0.0.0 to accept LAN connections and automatically prefer
32
+ # wired, Wi-Fi, other physical, then VPN addresses in generated links.
33
+ host: "127.0.0.1"
34
+ # 0 selects a free port once and reuses it for this Profile.
35
+ port: 0
36
+ # Overrides the automatically selected URL for domains or reverse proxies.
37
+ # publicBaseUrl: "https://agentbot.example.com/files"
38
+
29
39
  agents:
30
40
  codex:
31
41
  kind: "app-server"
@@ -62,6 +62,15 @@ export const appConfigSchema = z.object({
62
62
  enabled: z.boolean().default(true),
63
63
  })
64
64
  .default({ enabled: true }),
65
+ fileViewer: z.object({
66
+ enabled: z.boolean().default(true),
67
+ host: z.string().min(1).default("127.0.0.1"),
68
+ port: z.number().int().min(0).max(65_535).default(0),
69
+ publicBaseUrl: z.string().url().refine(
70
+ (value) => ["http:", "https:"].includes(new URL(value).protocol),
71
+ "fileViewer.publicBaseUrl must use http or https.",
72
+ ).optional(),
73
+ }).default({ enabled: true, host: "127.0.0.1", port: 0 }),
65
74
  feishu: z.object({
66
75
  transport: z.enum(["auto", "sdk", "console"]).default("auto"),
67
76
  appId: z.string().optional(),
@@ -1,4 +1,5 @@
1
1
  import type { Logger } from "pino";
2
+ import type { WSClient as LarkWsClient, WSConnectionStatus } from "@larksuiteoapi/node-sdk";
2
3
  import type { AppConfig } from "../config/schema.js";
3
4
  import { threadContextKey } from "./contextKey.js";
4
5
  import { resolveFeishuBotOpenId } from "./FeishuBotIdentity.js";
@@ -9,7 +10,12 @@ import type { CardAction, ChatUpdatedEvent, FeishuEventHandler, IncomingMessage
9
10
  type BotOpenIdResolver = (appId: string, appSecret: string) => Promise<string>;
10
11
  type BotOpenIdListener = (botOpenId: string) => void;
11
12
 
13
+ const LARK_WS_PING_TIMEOUT_SECONDS = 60;
14
+ const LARK_WS_HANDSHAKE_TIMEOUT_MS = 60_000;
15
+
12
16
  export class FeishuConnector {
17
+ private wsClient?: LarkWsClient;
18
+
13
19
  constructor(
14
20
  private readonly config: AppConfig,
15
21
  private readonly handler: FeishuEventHandler,
@@ -39,7 +45,12 @@ export class FeishuConnector {
39
45
  }
40
46
 
41
47
  stop(): void {
42
- // SDK connector currently relies on process lifetime.
48
+ this.wsClient?.close();
49
+ this.wsClient = undefined;
50
+ }
51
+
52
+ getConnectionStatus(): WSConnectionStatus | undefined {
53
+ return this.wsClient?.getConnectionStatus();
43
54
  }
44
55
 
45
56
  private async startFeishuWs(
@@ -48,7 +59,7 @@ export class FeishuConnector {
48
59
  botOpenId?: string,
49
60
  requireMention = false,
50
61
  ): Promise<void> {
51
- const lark = (await import("@larksuiteoapi/node-sdk")) as Record<string, any>;
62
+ const lark = await import("@larksuiteoapi/node-sdk");
52
63
  const eventDispatcher = new lark.EventDispatcher({}).register({
53
64
  "im.message.receive_v1": async (data: unknown) => {
54
65
  const senderOpenId = getFeishuEvent(data)?.sender?.sender_id?.open_id;
@@ -117,12 +128,66 @@ export class FeishuConnector {
117
128
  };
118
129
  },
119
130
  });
120
- const wsClient = new lark.WSClient({
131
+ let resolveInitialConnection!: () => void;
132
+ let rejectInitialConnection!: (error: Error) => void;
133
+ let initialConnectionSettled = false;
134
+ const initialConnection = new Promise<void>((resolve, reject) => {
135
+ resolveInitialConnection = resolve;
136
+ rejectInitialConnection = reject;
137
+ });
138
+ let wsClient: LarkWsClient | undefined;
139
+ wsClient = new lark.WSClient({
121
140
  appId,
122
141
  appSecret,
142
+ handshakeTimeoutMs: LARK_WS_HANDSHAKE_TIMEOUT_MS,
143
+ wsConfig: {
144
+ pingTimeout: LARK_WS_PING_TIMEOUT_SECONDS,
145
+ },
146
+ onReady: () => {
147
+ if (initialConnectionSettled) return;
148
+ initialConnectionSettled = true;
149
+ resolveInitialConnection();
150
+ },
151
+ onError: (error) => {
152
+ this.logger.error(
153
+ { error, connectionStatus: wsClient?.getConnectionStatus() },
154
+ "Feishu WebSocket connection failed.",
155
+ );
156
+ if (initialConnectionSettled) return;
157
+ initialConnectionSettled = true;
158
+ rejectInitialConnection(error);
159
+ },
160
+ onReconnecting: () => {
161
+ this.logger.warn(
162
+ { connectionStatus: wsClient?.getConnectionStatus() },
163
+ "Feishu WebSocket connection lost; reconnecting.",
164
+ );
165
+ },
166
+ onReconnected: () => {
167
+ this.logger.info("Feishu WebSocket connection restored.");
168
+ },
123
169
  });
124
- await wsClient.start({ eventDispatcher });
125
- this.logger.info("Feishu WebSocket connector started.");
170
+ this.wsClient = wsClient;
171
+ try {
172
+ await wsClient.start({ eventDispatcher });
173
+ const initialStatus = wsClient.getConnectionStatus();
174
+ if ((initialStatus.state === "idle" || initialStatus.state === "failed") && !initialConnectionSettled) {
175
+ throw new Error(`Feishu WebSocket failed to start (state: ${initialStatus.state}).`);
176
+ }
177
+ await initialConnection;
178
+ const connectedStatus = wsClient.getConnectionStatus();
179
+ if (connectedStatus.state !== "connected") {
180
+ throw new Error(`Feishu WebSocket failed to become ready (state: ${connectedStatus.state}).`);
181
+ }
182
+ this.logger.info(
183
+ { connectionStatus: connectedStatus },
184
+ "Feishu WebSocket connector started.",
185
+ );
186
+ } catch (error) {
187
+ wsClient.close({ force: true });
188
+ if (this.wsClient === wsClient) this.wsClient = undefined;
189
+ throw error;
190
+ }
126
191
  }
127
192
 
128
193
  private deferCardAction(action: CardAction, receivedAt: number): void {
@@ -8,10 +8,17 @@ interface FenceState {
8
8
  }
9
9
 
10
10
  const FENCE_OPENER = /^([ \t]*)((?:`{3,})|(?:~{3,}))(.*)$/;
11
- const MARKDOWN_LINK = /(!?)\[([^\]\r\n]*)\]\((<?[^)\r\n]+>?)\)/g;
11
+ const MARKDOWN_LINK = /(!?)\[([^\]\r\n]*)\]\((<[^>\r\n]+>|[^)\r\n]+)\)/g;
12
12
  const LOCAL_FILE_LINE_REFERENCE = /(:\d+(?::\d+)?)$/;
13
+ const LOCAL_IMAGE_EXTENSIONS = new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".tiff", ".bmp", ".ico"]);
13
14
 
14
- export function normalizeFeishuMarkdown(markdown: string, projectCwd?: string): string {
15
+ export type LocalFileUrlResolver = (filePath: string, reference?: string) => string | undefined;
16
+
17
+ export function normalizeFeishuMarkdown(
18
+ markdown: string,
19
+ projectCwd?: string,
20
+ localFileUrl?: LocalFileUrlResolver,
21
+ ): string {
15
22
  const lines = markdown.match(/[^\r\n]*(?:\r\n|\n)|[^\r\n]+$/g) ?? [];
16
23
  let fence: FenceState | undefined;
17
24
 
@@ -21,7 +28,7 @@ export function normalizeFeishuMarkdown(markdown: string, projectCwd?: string):
21
28
 
22
29
  if (!fence) {
23
30
  const opener = FENCE_OPENER.exec(body);
24
- if (!opener?.[2]) return `${includeLocalFileLineReferences(body, projectCwd)}${ending}`;
31
+ if (!opener?.[2]) return `${includeLocalFileLineReferences(body, projectCwd, localFileUrl)}${ending}`;
25
32
 
26
33
  const indent = opener[1] ?? "";
27
34
  const delimiter = opener[2];
@@ -42,18 +49,38 @@ export function normalizeFeishuMarkdown(markdown: string, projectCwd?: string):
42
49
  }).join("");
43
50
  }
44
51
 
45
- function includeLocalFileLineReferences(markdown: string, projectCwd?: string): string {
52
+ function includeLocalFileLineReferences(
53
+ markdown: string,
54
+ projectCwd?: string,
55
+ localFileUrl?: LocalFileUrlResolver,
56
+ ): string {
46
57
  return markdown.replace(MARKDOWN_LINK, (link, imageMarker: string, label: string, rawTarget: string) => {
47
58
  if (imageMarker) return link;
48
59
 
49
60
  const target = unwrapMarkdownTarget(rawTarget.trim());
50
61
  if (!isLocalFileTarget(target)) return link;
62
+ if (isLocalImageTarget(target)) return link;
51
63
 
52
64
  const reference = LOCAL_FILE_LINE_REFERENCE.exec(target)?.[1];
65
+ const targetWithoutReference = reference ? target.slice(0, -reference.length) : target;
66
+ const filePath = localFilePath(targetWithoutReference);
67
+ const viewerUrl = filePath ? localFileUrl?.(filePath, reference) : undefined;
53
68
  const projectPathLabel = projectCwd ? projectFilePathLabel(target, projectCwd, reference) : undefined;
54
- const normalizedLabel = projectPathLabel
55
- ?? (reference && !label.endsWith(reference) ? `${label}${reference}` : label);
56
- return normalizedLabel === label ? link : `[${normalizedLabel}](${rawTarget})`;
69
+ if (!projectPathLabel) {
70
+ const normalizedLabel = reference && !label.endsWith(reference) ? `${label}${reference}` : label;
71
+ if (viewerUrl) return `[${normalizedLabel}](${viewerUrl})`;
72
+ return normalizedLabel === label ? link : `[${normalizedLabel}](${rawTarget})`;
73
+ }
74
+
75
+ if (projectPathLabel === label) return viewerUrl ? `[${label}](${viewerUrl})` : link;
76
+ if (reference && projectPathLabel === `${label}${reference}`) {
77
+ return `[${projectPathLabel}](${viewerUrl ?? rawTarget})`;
78
+ }
79
+
80
+ if (!label) return viewerUrl ? `[${projectPathLabel}](${viewerUrl})` : inlineCode(projectPathLabel);
81
+ return viewerUrl
82
+ ? `[${label}](${viewerUrl})(${inlineCode(projectPathLabel)})`
83
+ : `${label}(${inlineCode(projectPathLabel)})`;
57
84
  });
58
85
  }
59
86
 
@@ -71,11 +98,7 @@ function projectFilePathLabel(
71
98
 
72
99
  const pathApi = usesWindowsPaths(filePath, projectCwd) ? path.win32 : path.posix;
73
100
  const parsed = pathApi.parse(filePath);
74
- if (Array.from(parsed.name).length > 5) return undefined;
75
-
76
- const parentName = pathApi.basename(pathApi.dirname(filePath));
77
- if (!parentName) return undefined;
78
- return `${parentName}${pathApi.sep}${parsed.base}${reference ?? ""}`;
101
+ return `${parsed.base}${reference ?? ""}`;
79
102
  }
80
103
 
81
104
  function localFilePath(target: string): string | undefined {
@@ -108,10 +131,26 @@ function displayAbsolutePath(filePath: string): string {
108
131
  return path.win32.normalize(filePath);
109
132
  }
110
133
 
134
+ function inlineCode(value: string): string {
135
+ const longestDelimiter = Math.max(0, ...(value.match(/`+/g) ?? []).map((run) => run.length));
136
+ const delimiter = "`".repeat(longestDelimiter + 1);
137
+ const padding = value.startsWith("`") || value.endsWith("`") ? " " : "";
138
+ return `${delimiter}${padding}${value}${padding}${delimiter}`;
139
+ }
140
+
111
141
  function usesWindowsPaths(...values: string[]): boolean {
112
142
  return values.some((value) => /^\/?[a-z]:[\\/]/i.test(value) || /^\\\\/.test(value));
113
143
  }
114
144
 
145
+ function isLocalImageTarget(target: string): boolean {
146
+ const reference = LOCAL_FILE_LINE_REFERENCE.exec(target)?.[1];
147
+ const targetWithoutReference = reference ? target.slice(0, -reference.length) : target;
148
+ const filePath = localFilePath(targetWithoutReference);
149
+ if (!filePath) return false;
150
+ const pathApi = usesWindowsPaths(filePath) ? path.win32 : path.posix;
151
+ return LOCAL_IMAGE_EXTENSIONS.has(pathApi.extname(filePath).toLowerCase());
152
+ }
153
+
115
154
  function unwrapMarkdownTarget(target: string): string {
116
155
  return target.startsWith("<") && target.endsWith(">") ? target.slice(1, -1) : target;
117
156
  }