farming-code 2.2.8 → 2.2.11

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 (102) hide show
  1. package/README.md +36 -23
  2. package/README.zh_cn.md +36 -23
  3. package/THIRD_PARTY_NOTICES.md +11 -2
  4. package/backend/acp-checkpoint-store.js +198 -0
  5. package/backend/acp-runtime.js +306 -83
  6. package/backend/acp-session-state.js +202 -6
  7. package/backend/acp-transcript.js +112 -0
  8. package/backend/agent-activity.js +6 -157
  9. package/backend/agent-manager.js +1482 -730
  10. package/backend/agent-provider-session.js +17 -242
  11. package/backend/agent-runtime-binding.js +219 -0
  12. package/backend/agent-session-history.js +66 -1
  13. package/backend/auth.js +79 -6
  14. package/backend/codex-models.js +81 -84
  15. package/backend/codex-session-archive.js +45 -0
  16. package/backend/codex-terminal-profile.js +500 -0
  17. package/backend/codex-transcript-sanitizer.js +12 -0
  18. package/backend/codex-transcript.js +230 -8
  19. package/backend/config-manager.js +30 -0
  20. package/backend/control-api.js +192 -17
  21. package/backend/farming-net-pass.js +285 -0
  22. package/backend/farming-net-registry.js +112 -0
  23. package/backend/farming-net-server.js +298 -0
  24. package/backend/farming-session-store.js +5 -13
  25. package/backend/git-worktree-info.js +181 -0
  26. package/backend/local-session-engine.js +411 -186
  27. package/backend/main-page-session.js +5 -2
  28. package/backend/native-pty-controller-generation.js +106 -0
  29. package/backend/native-pty-host-client.js +275 -7
  30. package/backend/native-pty-host-identity.js +86 -0
  31. package/backend/native-pty-host.js +813 -114
  32. package/backend/native-session-engine.js +100 -28
  33. package/backend/packaged-node-pty.js +22 -2
  34. package/backend/provider-adapters.js +253 -0
  35. package/backend/provider-session-service.js +241 -0
  36. package/backend/runtime-observation.js +81 -0
  37. package/backend/server.js +294 -86
  38. package/backend/session-engine-bridge.js +21 -2
  39. package/backend/session-engine-router.js +1 -1
  40. package/backend/session-engine.js +1 -1
  41. package/backend/session-stream-protocol.js +185 -0
  42. package/backend/storage-layout.js +55 -0
  43. package/backend/terminal-attach-checkpoint.js +74 -0
  44. package/backend/terminal-exit-quiescence.js +39 -0
  45. package/backend/terminal-reducer-flow-control.js +97 -0
  46. package/backend/terminal-screen-state.js +11 -2
  47. package/backend/terminal-screen-worker-pool.js +59 -6
  48. package/backend/terminal-screen-worker-thread.js +97 -57
  49. package/backend/terminal-screen-worker.js +133 -51
  50. package/backend/terminal-state-serialization.js +127 -0
  51. package/backend/terminal-status.js +23 -4
  52. package/backend/usage-monitor.js +81 -4
  53. package/backend/workspace-directory.js +152 -0
  54. package/backend/workspace-file-router.js +182 -76
  55. package/backend/workspace-file-service.js +319 -4
  56. package/backend/workspace-root-registry.js +164 -0
  57. package/dist/assets/App-CHYpgECZ.js +208 -0
  58. package/dist/assets/{FileEditorMarkdownPreview-elKWc8Im.js → FileEditorMarkdownPreview--Cri80nQ.js} +92 -92
  59. package/dist/assets/FileEditorPane-DGJlEKdf.js +2 -0
  60. package/dist/assets/IconGlyphs-Cc14sepw.js +1 -0
  61. package/dist/assets/ProjectFilesSection-Cz1wG4NT.js +12 -0
  62. package/dist/assets/{ReviewPage-BaXu1ZdX.js → ReviewPage-LonRc6Fz.js} +1 -1
  63. package/dist/assets/code-dark-B1pAtO_P.css +1 -0
  64. package/dist/assets/file-icons-Bw2qd5iT.js +1 -0
  65. package/dist/assets/{index-BrbljRqn.js → index-miO2r9IZ.js} +3 -3
  66. package/dist/assets/main-DNe0jPw-.css +1 -0
  67. package/dist/assets/workspace-editor-model-BQol4qbA.js +1 -0
  68. package/dist/assets/workspace-editor-monaco-5gFbeDPF.js +1 -0
  69. package/dist/assets/workspace-editor-monaco-xPv8JzHH.js +4 -0
  70. package/dist/assets/workspace-view-state-DvYG_9PH.js +7 -0
  71. package/dist/assets/workspace-working-copy-D8-s_Sgh.js +1 -0
  72. package/dist/index.html +1 -1
  73. package/frontend/farming-net/app.css +625 -0
  74. package/frontend/farming-net/app.js +268 -0
  75. package/frontend/farming-net/index.html +86 -0
  76. package/frontend/reading-anchor.js +198 -0
  77. package/frontend/session-bridge.js +12 -3
  78. package/frontend/session-modal-bridge.js +5 -12
  79. package/frontend/skins/crt/app.js +1976 -793
  80. package/frontend/skins/crt/index.html +313 -23
  81. package/frontend/skins/crt/styles/billing.css +294 -223
  82. package/frontend/skins/crt/styles/monochrome-green.css +7 -2
  83. package/frontend/terminal-replay.js +372 -0
  84. package/package.json +10 -3
  85. package/shared/browser-protocol.d.ts +5 -0
  86. package/shared/browser-protocol.js +130 -0
  87. package/dist/assets/App-8dYAM6ql.js +0 -124
  88. package/dist/assets/FileEditorPane-RWiFD2cq.js +0 -5
  89. package/dist/assets/IconGlyphs-DfL0EBnj.js +0 -1
  90. package/dist/assets/ProjectFilesSection-Q4PDsWmM.js +0 -12
  91. package/dist/assets/code-dark-CDkOQAtK.css +0 -1
  92. package/dist/assets/file-icons-EFUGSSwf.js +0 -1
  93. package/dist/assets/main-D073SnW4.css +0 -1
  94. package/dist/assets/qoder-C9LmmOSf.svg +0 -1
  95. package/dist/assets/qoder-Cf9gl0Y5.svg +0 -1
  96. package/dist/assets/qoder-gHCinseV.svg +0 -1
  97. package/dist/assets/workspace-view-state-CTyDzk2D.js +0 -1
  98. package/dist/assets/zsh-CLpveKlF.svg +0 -1
  99. package/dist/assets/zsh-FxSpMPbz.svg +0 -1
  100. /package/dist/assets/{api-D1lyBYIQ.js → api-D8nyOEbz.js} +0 -0
  101. /package/dist/assets/{core-ZlAPicox.js → core-D0LFJkDt.js} +0 -0
  102. /package/dist/assets/{useWorkspaceMenuKeyboard-CneKAZUJ.js → useWorkspaceMenuKeyboard-Brws6Ar9.js} +0 -0
package/README.md CHANGED
@@ -9,9 +9,21 @@
9
9
  ![Node](https://img.shields.io/badge/node-%3E%3D22-339933?logo=nodedotjs&logoColor=white)
10
10
  ![Platform](https://img.shields.io/badge/platform-macOS%20%7C%20Linux-555)
11
11
 
12
- Farming is a browser workspace for supervising AI coding agents on a development machine. It keeps several live agents, structured conversations, real terminals, project files, review, history, and runtime controls in one place—without moving the repository or agent processes into the browser.
12
+ Farming is an open-source, customizable browser workspace for supervising AI coding agents on a development machine. It keeps several live agents, structured conversations, real terminals, project files, review, history, and runtime controls in one place—without moving the repository or agent processes into the browser.
13
13
 
14
- Run Farming beside the coding CLIs you already use, then return to the same work from a desktop or phone. Closing the browser does not stop the agents; the native PTY host can also preserve live terminal sessions while the Farming server restarts.
14
+ Run Farming on the development machine where your coding CLIs already work, then return to the same tasks from a desktop or phone. Closing the browser does not stop the agents; the native PTY host can also preserve live terminal sessions while the Farming server restarts.
15
+
16
+ ## Quick Start
17
+
18
+ With Node.js 22 or newer and at least one supported coding CLI installed and signed in, install and start Farming in one command:
19
+
20
+ ```bash
21
+ npm install --global farming-code@latest && farming daemon
22
+ ```
23
+
24
+ Open the authenticated URL printed by the command, choose **New Agent**, select an Agent and workspace, and start in Chat or Terminal.
25
+
26
+ ![Farming Code workspace](./docs/products/code/assets/01-code-workspace.png)
15
27
 
16
28
  ## Two Interfaces, One Runtime
17
29
 
@@ -19,7 +31,7 @@ Farming 2 provides two complete browser interfaces over the same agents and sess
19
31
 
20
32
  ### Farming Code
21
33
 
22
- The default workbench for reading conversations, intervening in tasks, editing files, and reviewing an evolving change.
34
+ The default workbench for reading conversations, intervening in tasks, editing files, and reviewing workspace changes.
23
35
 
24
36
  ![Farming Code structured Chat](./docs/products/code/assets/11-code-agent-process.png)
25
37
 
@@ -41,21 +53,28 @@ Switching interfaces does not restart or duplicate an Agent. If Farming Code can
41
53
 
42
54
  The complete current capability map and screenshot tour are in the [Farming 2 product overview](./docs/products/README.md). See the focused [Farming Code guide](./docs/products/code/README.md) and [Farming CRT guide](./docs/products/crt/README.md) for the full workflows.
43
55
 
56
+ ## Farming Net: One Portal For Deployments
57
+
58
+ Farming Net is a separate, token-protected directory for the Farming instances you already run. Its cards can point to a Farming on the current device, a remote development host, an intranet address, or a tunnel. Enrolled targets accept short-lived signed passes, so users keep one portal login instead of a list of deployment URLs and target tokens.
59
+
60
+ ```bash
61
+ FARMING_NET_PORT=6693 FARMING_NET_BASE_PATH=/farming-net npm run start:net
62
+ ```
63
+
64
+ The portal keeps its token, signing identity, and private `instances.json` registry under `~/.farming-net/`. It does not proxy target traffic or store target tokens; each destination remains an independent Farming service and explicitly chooses whether to trust the portal. See the [Farming Net guide](./docs/products/net/README.md) for enrollment and the security boundary.
65
+
44
66
  ## What You Can Do
45
67
 
46
- - Group live agents by project, pin important work, rename sessions, track unread activity, and archive or resume work.
68
+ - Group live agents by project, pin or rename important work, track unread activity, search live and historical sessions, and archive or resume tasks.
47
69
  - Use structured ACP Chat for Codex, Claude Code, OpenCode, and Qoder. Plans, reasoning, tools, permission requests, embedded terminals, child sessions, attachments, queued follow-ups, and exact change summaries remain available without overwhelming the final answer.
48
- - Open a real PTY Terminal when exact CLI behavior matters. Chat / Terminal changes the actual runtime and safely resumes the same provider session when its identity is available.
49
- - Change supported live Codex model, reasoning, Fast, Ultra, and permission settings. Terminal changes are applied to the running workflow before the next message rather than being limited to a future launch profile.
50
- - Browse Project Files, Open Editors, search with ripgrep, edit with Monaco, preview Markdown and images, follow `path:line` links, inspect git changes, diffs, and blame.
51
- - Review one evolving change across revisions, keep findings tied to the compared revision, mark files reviewed, and inspect meaningful deltas instead of treating every Agent turn as a separate review.
52
- - Search live Agents and the supported provider-session archive, then open, continue, restore, or resume the matching work.
70
+ - Switch the same provider session between structured Chat and a real PTY Terminal. Supported Codex model, reasoning, Fast, Ultra, and permission changes reach the live workflow; a compatible Terminal applies model changes immediately and confirms the CLI state before accepting the next Composer message.
71
+ - Browse, search, and lightly edit Project Files; inspect a VS Code-derived Git history graph, changed files, Git changes, Diff, and Blame; then open commits or working-copy changes in Review with captured revisions, inline comments, and Reviewed state.
53
72
  - Observe CPU/MEM, token-rate, context, quota, provider usage, and CRT daily/live token telemetry when the provider exposes the required data.
54
- - Use the same service from desktop and mobile browsers, with layouts designed for the amount of attention available on each device.
73
+ - Continue the same Farming Code task from desktop or phone without moving the Agent process away from the development host.
55
74
 
56
75
  ![Farming Code project files and blame](./docs/products/code/assets/04-files-editor-blame.png)
57
76
 
58
- ![Farming Review](./docs/products/code/assets/10-review-workflow.png)
77
+ ![Separate Review entry points for tracked and untracked workspace changes](./docs/products/code/assets/10-review-workflow.png)
59
78
 
60
79
  ## Supported Agent Paths
61
80
 
@@ -75,14 +94,7 @@ Farming discovers installed CLIs on the host. The richer structured runtime curr
75
94
 
76
95
  Farming hosts CLIs that already work on the same machine. It does not replace provider installation, login, or account configuration.
77
96
 
78
- ## Quick Start
79
-
80
- Install Node.js 22 or newer, install and sign in to at least one supported coding CLI, then run:
81
-
82
- ```bash
83
- npm install --global farming-code
84
- farming daemon
85
- ```
97
+ ## Runtime Defaults And Daemon Commands
86
98
 
87
99
  Farming defaults to port `6694`, base path `/farming`, config directory `~/.farming`, and token authentication. The startup log prints a URL similar to:
88
100
 
@@ -90,7 +102,7 @@ Farming defaults to port `6694`, base path `/farming`, config directory `~/.farm
90
102
  http://development-host:6694/farming?token=<startup-token>
91
103
  ```
92
104
 
93
- Open it, choose **New Agent**, select an Agent and workspace, and start in Chat or Terminal. Useful daemon commands are:
105
+ Useful daemon commands are:
94
106
 
95
107
  ```bash
96
108
  farming status
@@ -109,10 +121,10 @@ Desktop keeps the project, conversation, files, and review close together. Mobil
109
121
 
110
122
  <p align="center">
111
123
  <img src="./docs/products/code/assets/05-mobile-agent-chat.jpg" alt="Farming Code on mobile" width="320">
112
- &nbsp;&nbsp;
113
- <img src="./docs/products/crt/assets/09-crt-mobile-dashboard.jpg" alt="Farming CRT on mobile" width="320">
114
124
  </p>
115
125
 
126
+ Farming CRT is currently a desktop interface. Use Farming Code from a phone; CRT mobile concepts are not part of the supported product yet.
127
+
116
128
  ## Installation And Updates
117
129
 
118
130
  The npm package is the default distribution. **Settings → Updates** can upgrade npm installations in place: Farming installs the new package while the current server stays alive, restarts only after installation succeeds, and attempts rollback when the new server cannot start.
@@ -143,7 +155,7 @@ Development host
143
155
  repositories, shells, Codex, Claude Code, OpenCode, Qoder, ...
144
156
  ```
145
157
 
146
- The backend owns lifecycle, authentication, session routing, workspace boundaries, history, and configuration. Interactive terminal sessions use a separate native PTY host by default, allowing browser and server reconnection without replacing the live process. The browser terminal renderer defaults to xterm.js; the Ghostty web adapter remains an explicit debug path.
158
+ The backend owns lifecycle, authentication, session routing, workspace boundaries, history, and configuration. Interactive terminal sessions use a separate native PTY host by default, allowing browser and server reconnection without replacing the live process. The browser terminal renderer defaults to xterm.js WebGL as its single supported product path; the Ghostty web adapter remains an explicit debug path, not a runtime fallback.
147
159
 
148
160
  Runtime settings live in `~/.farming/settings.json`. Farming session metadata, the project membership index, archived runs, theme settings, update state, logs, and the startup token use separate files under `~/.farming/`. External provider histories remain read-only integrations.
149
161
 
@@ -158,6 +170,7 @@ Token authentication protects HTTP and WebSocket traffic. `FARMING_DISABLE_AUTH=
158
170
  - [Farming 2 product overview and capability map](./docs/products/README.md)
159
171
  - [Farming Code guide](./docs/products/code/README.md)
160
172
  - [Farming CRT guide](./docs/products/crt/README.md)
173
+ - [Farming Net deployment portal](./docs/products/net/README.md)
161
174
  - [Mobile guide](./docs/products/code/mobile-guide.md)
162
175
  - [ACP runtime](./docs/products/code/acp-runtime.md)
163
176
  - [Review foundation](./docs/products/code/review-foundation.md)
package/README.zh_cn.md CHANGED
@@ -9,9 +9,21 @@
9
9
  ![Node](https://img.shields.io/badge/node-%3E%3D22-339933?logo=nodedotjs&logoColor=white)
10
10
  ![Platform](https://img.shields.io/badge/platform-macOS%20%7C%20Linux-555)
11
11
 
12
- Farming 是一个运行在开发机上的浏览器 AI Coding Agent 工作台。它把多个实时 Agent、结构化对话、真实终端、项目文件、Review、历史记录和运行时控制放在一起,同时代码仓库和 Agent 进程仍然留在开发机上。
12
+ Farming 是一个开源、可自定义、运行在开发机上的浏览器 AI Coding Agent 工作台。它把多个实时 Agent、结构化对话、真实终端、项目文件、Review、历史记录和运行时控制放在一起,同时代码仓库和 Agent 进程仍然留在开发机上。
13
13
 
14
- Farming 运行在你平时使用 Coding CLI 的机器旁边,就可以从电脑或手机回到同一批工作。关闭浏览器不会停止 Agent;Farming Server 重启时,独立的原生 PTY Host 也可以保留正在运行的终端会话。
14
+ Coding CLI 已经能正常工作的开发机上运行 Farming,就可以从电脑或手机回到同一批任务。关闭浏览器不会停止 Agent;Farming Server 重启时,独立的原生 PTY Host 也可以保留正在运行的终端会话。
15
+
16
+ ## 快速开始
17
+
18
+ 准备好 Node.js 22 或更新版本,以及至少一个已安装、已登录的受支持 Coding CLI,然后用一行命令安装并启动 Farming:
19
+
20
+ ```bash
21
+ npm install --global farming-code@latest && farming daemon
22
+ ```
23
+
24
+ 打开命令输出的带鉴权 URL,选择 **New Agent**、Agent 类型和 Workspace,然后从 Chat 或 Terminal 开始工作。
25
+
26
+ ![Farming Code 工作台](./docs/products/code/assets/01-code-workspace.png)
15
27
 
16
28
  ## 两套界面,同一套运行时
17
29
 
@@ -19,7 +31,7 @@ Farming 2 在同一批 Agent 和 Session 上提供两套完整的浏览器界面
19
31
 
20
32
  ### Farming Code
21
33
 
22
- 默认工作台,适合阅读对话、介入任务、编辑文件,以及持续 Review 同一个演进中的修改。
34
+ 默认工作台,适合阅读对话、介入任务、编辑文件和检查工作区修改。
23
35
 
24
36
  ![Farming Code 结构化 Chat](./docs/products/code/assets/11-code-agent-process.png)
25
37
 
@@ -41,21 +53,28 @@ Farming 2 在同一批 Agent 和 Session 上提供两套完整的浏览器界面
41
53
 
42
54
  完整能力矩阵和截图导览见 [Farming 2 产品总览](./docs/products/README.zh_cn.md)。两套界面的完整流程分别见 [Farming Code 指南](./docs/products/code/README.zh_cn.md) 和 [Farming CRT 指南](./docs/products/crt/README.zh_cn.md)。
43
55
 
56
+ ## Farming Net:部署门户
57
+
58
+ Farming Net 是一套独立、带 Token 鉴权的 Farming 环境目录。卡片可以指向当前设备、远程开发机、内网地址或隧道中的 Farming;已登记的目标接受短时签名通行证,让用户只保留一个门户登录,不再分别记录每个部署地址和目标 Token。
59
+
60
+ ```bash
61
+ FARMING_NET_PORT=6693 FARMING_NET_BASE_PATH=/farming-net npm run start:net
62
+ ```
63
+
64
+ 门户自己的 Token、签名身份和私有 `instances.json` 注册表放在 `~/.farming-net/`。它不代理目标流量,也不保存目标 Token;每个目标仍是独立 Farming 服务,并且主动决定是否信任门户。登记方式和安全边界见 [Farming Net 指南](./docs/products/net/README.zh_cn.md)。
65
+
44
66
  ## 现在可以做什么
45
67
 
46
- - 按项目组织实时 Agent,置顶重点工作、重命名、查看未读状态,并归档或恢复任务。
68
+ - 按项目组织实时 Agent,置顶或重命名重点工作、查看未读状态、搜索实时与历史 Session,并归档或恢复任务。
47
69
  - Codex、Claude Code、OpenCode 和 Qoder 使用结构化 ACP Chat。计划、推理、工具调用、权限请求、内嵌终端、子 Session、附件、排队追问和精确修改摘要都可以保留,但不会淹没最终答案。
48
- - 需要原汁原味 CLI 行为时打开真实 PTY Terminal。Chat / Terminal 切换会改变实际运行时,并在身份可用时安全地恢复同一个 Provider Session。
49
- - 修改运行中 Codex 所支持的模型、思考强度、FastUltra 和权限设置。Terminal 的修改会在下一条消息前作用到当前工作流,而不是只改未来的启动 Profile
50
- - 浏览 Project Files 和 Open Editors,用 ripgrep 搜索、Monaco 轻量编辑、预览 Markdown/图片、跟随 `path:line` 链接,并检查 Git Changes、Diff 和 Blame。
51
- - 围绕同一个演进中的 Change 跨 Revision Review,让 Finding 与对比版本绑定,标记文件已审阅,并关注多轮之间真正有意义的变化。
52
- - 搜索实时 Agent 和受支持 Provider 的完整 Session 归档,然后打开、继续、恢复或 Resume 对应工作。
70
+ - 在结构化 Chat 与真实 PTY Terminal 之间切换同一个 Provider Session。支持的 Codex 模型、思考强度、Fast、Ultra 和权限修改会作用到实时工作流;兼容 Terminal 会立即应用模型修改,并在接受下一条 Composer 消息前确认 CLI 的真实状态。
71
+ - 浏览、搜索并轻量编辑 Project Files,通过复用 VS Code 图算法的 Git History 查看提交树与变更文件,检查 Git ChangesDiff Blame,再把 Commit 或 Working Copy 修改送入带 Revision、行内评论和 Reviewed 状态的 Review
53
72
  - 在 Provider 提供所需数据时查看 CPU/MEM、Token Rate、Context、Quota、Provider 用量,以及 CRT 的按日/实时 Token 遥测。
54
- - 在电脑和手机浏览器中使用同一个服务;不同布局围绕不同设备上可用的注意力设计。
73
+ - 从电脑或手机继续同一个 Farming Code 任务,Agent 进程始终留在开发机上。
55
74
 
56
75
  ![Farming Code 项目文件与 Blame](./docs/products/code/assets/04-files-editor-blame.png)
57
76
 
58
- ![Farming Review](./docs/products/code/assets/10-review-workflow.png)
77
+ ![Files 中分开的已跟踪与未跟踪 Review 入口](./docs/products/code/assets/10-review-workflow.png)
59
78
 
60
79
  ## 支持的 Agent 路径
61
80
 
@@ -75,14 +94,7 @@ Farming 会发现开发机上已经安装的 CLI。有 ACP 支持的 Provider
75
94
 
76
95
  Farming 承载的是已经能在同一台机器正常工作的 CLI,不替代 Provider 的安装、登录和账户配置。
77
96
 
78
- ## 快速开始
79
-
80
- 安装 Node.js 22 或更新版本,并先安装、登录至少一个受支持的 Coding CLI:
81
-
82
- ```bash
83
- npm install --global farming-code
84
- farming daemon
85
- ```
97
+ ## 运行默认值与 Daemon 命令
86
98
 
87
99
  默认端口是 `6694`,Base Path 是 `/farming`,配置目录是 `~/.farming`,Token 鉴权默认开启。启动日志会打印类似下面的 URL:
88
100
 
@@ -90,7 +102,7 @@ farming daemon
90
102
  http://development-host:6694/farming?token=<startup-token>
91
103
  ```
92
104
 
93
- 打开 URL,选择 **New Agent**、Agent 类型和 Workspace,然后从 Chat 或 Terminal 开始工作。常用守护进程命令:
105
+ 常用守护进程命令:
94
106
 
95
107
  ```bash
96
108
  farming status
@@ -109,10 +121,10 @@ farming stop
109
121
 
110
122
  <p align="center">
111
123
  <img src="./docs/products/code/assets/05-mobile-agent-chat.jpg" alt="Farming Code 手机界面" width="320">
112
- &nbsp;&nbsp;
113
- <img src="./docs/products/crt/assets/09-crt-mobile-dashboard.jpg" alt="Farming CRT 手机界面" width="320">
114
124
  </p>
115
125
 
126
+ Farming CRT 当前只作为桌面界面使用。手机请使用 Farming Code;CRT 手机方案目前仍是概念设计,不属于已支持的产品能力。
127
+
116
128
  ## 安装与更新
117
129
 
118
130
  npm 包是默认分发方式。**Settings → Updates** 可以原地升级 npm 安装:Farming 会在当前 Server 仍运行时安装新包,只在安装成功后重启;新 Server 无法启动时会尝试回退。
@@ -143,7 +155,7 @@ Development host
143
155
  repositories, shells, Codex, Claude Code, OpenCode, Qoder, ...
144
156
  ```
145
157
 
146
- 后端负责生命周期、鉴权、Session 路由、Workspace 边界、History 和配置。交互式 Terminal 默认由独立的原生 PTY Host 持有,因此浏览器和 Server 可以重新连接,而不需要替换实际进程。xterm.js 是产品默认终端渲染器;Ghostty Web Adapter 只保留为显式调试路径。
158
+ 后端负责生命周期、鉴权、Session 路由、Workspace 边界、History 和配置。交互式 Terminal 默认由独立的原生 PTY Host 持有,因此浏览器和 Server 可以重新连接,而不需要替换实际进程。xterm.js WebGL 是唯一受支持的产品 Terminal Renderer;Ghostty Web Adapter 只保留为显式调试路径,不作为运行时 Fallback。
147
159
 
148
160
  运行时设置存放在 `~/.farming/settings.json`。Farming Session 元数据、项目成员索引、归档运行、主题设置、更新状态、日志和启动 Token 使用 `~/.farming/` 下彼此独立的文件。外部 Provider History 仍然只读。
149
161
 
@@ -158,6 +170,7 @@ Token 鉴权同时保护 HTTP 和 WebSocket。`FARMING_DISABLE_AUTH=1` 只适合
158
170
  - [Farming 2 产品总览与能力矩阵](./docs/products/README.zh_cn.md)
159
171
  - [Farming Code 指南](./docs/products/code/README.zh_cn.md)
160
172
  - [Farming CRT 指南](./docs/products/crt/README.zh_cn.md)
173
+ - [Farming Net 部署门户](./docs/products/net/README.zh_cn.md)
161
174
  - [移动端指南](./docs/products/code/mobile-guide.zh_cn.md)
162
175
  - [ACP 运行时](./docs/products/code/acp-runtime.zh_cn.md)
163
176
  - [Review 基础](./docs/products/code/review-foundation.zh_cn.md)
@@ -15,8 +15,8 @@ preserved in bundled `node_modules` when bundled dependencies are enabled.
15
15
  | Package | Version | License | Purpose |
16
16
  | --- | --- | --- | --- |
17
17
  | `@agentclientprotocol/sdk` | 1.2.1 | Apache-2.0 | ACP JSON-RPC client and protocol types |
18
- | `@agentclientprotocol/codex-acp` | 1.1.2 | Apache-2.0 | Codex ACP adapter |
19
- | `@agentclientprotocol/claude-agent-acp` | 0.58.1 | Apache-2.0 | Claude Code ACP adapter |
18
+ | `@agentclientprotocol/codex-acp` | 1.1.4 | Apache-2.0 | Codex ACP adapter |
19
+ | `@agentclientprotocol/claude-agent-acp` | 0.59.0 | Apache-2.0 | Claude Code ACP adapter |
20
20
  | `@xterm/addon-clipboard` | 0.2.0 | MIT | Browser terminal clipboard integration |
21
21
  | `@xterm/addon-fit` | 0.11.0 | MIT | Browser terminal sizing |
22
22
  | `@xterm/addon-search` | 0.16.0 | MIT | Browser terminal search |
@@ -65,6 +65,15 @@ The CRT skin bundles one font file under `frontend/skins/crt/fonts/`:
65
65
 
66
66
  The corresponding license texts are stored beside the font files.
67
67
 
68
+ ## Adapted Source Code
69
+
70
+ Farming's Git history swimlane transform and graph-row renderer adapt the
71
+ Visual Studio Code SCM history graph from Microsoft Visual Studio Code commit
72
+ `0217c2f1a0defc7fdbfb4feba74e71e366de6822`. The adapted files retain the
73
+ Microsoft copyright and MIT license header. Visual Studio Code is licensed
74
+ under the MIT License:
75
+ https://github.com/microsoft/vscode/blob/0217c2f1a0defc7fdbfb4feba74e71e366de6822/LICENSE.txt
76
+
68
77
  ## Bundled Data
69
78
 
70
79
  Farming includes a generated Chinese poetic token word list at
@@ -0,0 +1,198 @@
1
+ const crypto = require('crypto');
2
+ const fs = require('fs/promises');
3
+ const path = require('path');
4
+ const { promisify } = require('util');
5
+ const zlib = require('zlib');
6
+ const storageLayout = require('./storage-layout');
7
+
8
+ const gzip = promisify(zlib.gzip);
9
+ const gunzip = promisify(zlib.gunzip);
10
+ const CHECKPOINT_VERSION = 1;
11
+ const DEFAULT_WRITE_DELAY_MS = 250;
12
+
13
+ async function durableWrite(file, data) {
14
+ const handle = await fs.open(file, 'w');
15
+ try {
16
+ await handle.writeFile(data);
17
+ await handle.sync();
18
+ } finally {
19
+ await handle.close();
20
+ }
21
+ }
22
+
23
+ async function syncDirectory(directory) {
24
+ const handle = await fs.open(directory, 'r');
25
+ try {
26
+ await handle.sync();
27
+ } finally {
28
+ await handle.close();
29
+ }
30
+ }
31
+
32
+ function normalizeIdentity(value = {}) {
33
+ return {
34
+ provider: String(value.provider || '').trim().toLowerCase(),
35
+ providerHomeId: String(value.providerHomeId || 'default').trim() || 'default',
36
+ sessionId: String(value.sessionId || '').trim(),
37
+ cwd: path.resolve(String(value.cwd || process.cwd())),
38
+ };
39
+ }
40
+
41
+ function checkpointKey(identity) {
42
+ return crypto.createHash('sha256')
43
+ .update(JSON.stringify(normalizeIdentity(identity)))
44
+ .digest('hex');
45
+ }
46
+
47
+ function sameIdentity(left, right) {
48
+ return JSON.stringify(normalizeIdentity(left)) === JSON.stringify(normalizeIdentity(right));
49
+ }
50
+
51
+ class AcpCheckpointStore {
52
+ constructor(configDir, options = {}) {
53
+ this.dir = storageLayout.acpCheckpointsDir(configDir);
54
+ this.writeDelayMs = Number.isFinite(Number(options.writeDelayMs))
55
+ ? Math.max(0, Math.floor(Number(options.writeDelayMs)))
56
+ : DEFAULT_WRITE_DELAY_MS;
57
+ this.pending = new Map();
58
+ this.writeChains = new Map();
59
+ }
60
+
61
+ paths(identity) {
62
+ const key = checkpointKey(identity);
63
+ return {
64
+ key,
65
+ checkpoint: path.join(this.dir, `${key}.json.gz`),
66
+ dirty: path.join(this.dir, `${key}.dirty`),
67
+ };
68
+ }
69
+
70
+ enqueue(key, operation) {
71
+ const previous = this.writeChains.get(key) || Promise.resolve();
72
+ const next = previous.catch(() => {}).then(operation);
73
+ this.writeChains.set(key, next);
74
+ void next.then(() => {
75
+ if (this.writeChains.get(key) === next) this.writeChains.delete(key);
76
+ }, () => {
77
+ if (this.writeChains.get(key) === next) this.writeChains.delete(key);
78
+ });
79
+ return next;
80
+ }
81
+
82
+ async load(identity, options = {}) {
83
+ const normalized = normalizeIdentity(identity);
84
+ if (!normalized.provider || !normalized.sessionId) return null;
85
+ const files = this.paths(normalized);
86
+ const inFlight = this.writeChains.get(files.key);
87
+ if (inFlight) await inFlight.catch(() => {});
88
+ try {
89
+ const [compressed, dirty] = await Promise.all([
90
+ fs.readFile(files.checkpoint),
91
+ fs.access(files.dirty).then(() => true).catch(() => false),
92
+ ]);
93
+ const payload = JSON.parse((await gunzip(compressed)).toString('utf8'));
94
+ if (
95
+ payload?.version !== CHECKPOINT_VERSION
96
+ || !sameIdentity(payload.identity, normalized)
97
+ || !payload.state
98
+ ) return null;
99
+ if (dirty && options.allowDirty !== true) return null;
100
+ return { state: payload.state, exact: !dirty, savedAt: Number(payload.savedAt || 0) };
101
+ } catch (error) {
102
+ if (error?.code !== 'ENOENT') {
103
+ console.warn('Failed to read ACP checkpoint:', error && (error.message || error));
104
+ }
105
+ return null;
106
+ }
107
+ }
108
+
109
+ async markDirty(identity) {
110
+ const normalized = normalizeIdentity(identity);
111
+ if (!normalized.provider || !normalized.sessionId) return;
112
+ const files = this.paths(normalized);
113
+ const pending = this.pending.get(files.key);
114
+ if (pending?.timer) clearTimeout(pending.timer);
115
+ this.pending.delete(files.key);
116
+ return this.enqueue(files.key, async () => {
117
+ await fs.mkdir(this.dir, { recursive: true });
118
+ await durableWrite(files.dirty, `${Date.now()}\n`);
119
+ await syncDirectory(this.dir);
120
+ });
121
+ }
122
+
123
+ schedule(identity, state, options = {}) {
124
+ const normalized = normalizeIdentity(identity);
125
+ if (!normalized.provider || !normalized.sessionId || !state) return;
126
+ const files = this.paths(normalized);
127
+ const previous = this.pending.get(files.key);
128
+ if (previous?.timer) clearTimeout(previous.timer);
129
+ const pending = {
130
+ identity: normalized,
131
+ state,
132
+ exact: options.exact === true,
133
+ timer: null,
134
+ };
135
+ pending.timer = setTimeout(() => {
136
+ this.pending.delete(files.key);
137
+ void this.write(pending.identity, pending.state, { exact: pending.exact });
138
+ }, this.writeDelayMs);
139
+ pending.timer.unref?.();
140
+ this.pending.set(files.key, pending);
141
+ }
142
+
143
+ async write(identity, state, options = {}) {
144
+ const normalized = normalizeIdentity(identity);
145
+ if (!normalized.provider || !normalized.sessionId || !state) return;
146
+ const files = this.paths(normalized);
147
+ const pending = this.pending.get(files.key);
148
+ if (pending?.timer) clearTimeout(pending.timer);
149
+ this.pending.delete(files.key);
150
+ return this.enqueue(files.key, async () => {
151
+ const payload = {
152
+ version: CHECKPOINT_VERSION,
153
+ savedAt: Date.now(),
154
+ identity: normalized,
155
+ state: state.exportCheckpoint(),
156
+ };
157
+ const compressed = await gzip(Buffer.from(JSON.stringify(payload)), { level: zlib.constants.Z_BEST_SPEED });
158
+ await fs.mkdir(this.dir, { recursive: true });
159
+ if (options.exact !== true) {
160
+ await durableWrite(files.dirty, `${Date.now()}\n`);
161
+ await syncDirectory(this.dir);
162
+ }
163
+ const temporary = `${files.checkpoint}.${process.pid}.${Date.now()}.tmp`;
164
+ try {
165
+ await durableWrite(temporary, compressed);
166
+ await fs.rename(temporary, files.checkpoint);
167
+ await syncDirectory(this.dir);
168
+ } finally {
169
+ await fs.rm(temporary, { force: true }).catch(() => {});
170
+ }
171
+ if (options.exact === true) {
172
+ await fs.rm(files.dirty, { force: true });
173
+ await syncDirectory(this.dir);
174
+ }
175
+ });
176
+ }
177
+
178
+ async flush() {
179
+ const pending = [...this.pending.values()];
180
+ this.pending.clear();
181
+ pending.forEach(item => {
182
+ if (item.timer) clearTimeout(item.timer);
183
+ });
184
+ await Promise.all(pending.map(item => this.write(item.identity, item.state, { exact: item.exact })));
185
+ await Promise.all([...this.writeChains.values()].map(write => write.catch(() => {})));
186
+ }
187
+
188
+ async dispose() {
189
+ await this.flush();
190
+ }
191
+ }
192
+
193
+ module.exports = {
194
+ AcpCheckpointStore,
195
+ CHECKPOINT_VERSION,
196
+ checkpointKey,
197
+ normalizeIdentity,
198
+ };