farming-code 2.2.8 → 2.2.12

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 (107) hide show
  1. package/README.md +42 -28
  2. package/README.zh_cn.md +42 -28
  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 +1592 -741
  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 +54 -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 +35 -0
  20. package/backend/control-api.js +192 -17
  21. package/backend/executable-discovery.js +2 -0
  22. package/backend/farming-net-pass.js +285 -0
  23. package/backend/farming-net-registry.js +112 -0
  24. package/backend/farming-net-server.js +298 -0
  25. package/backend/farming-session-store.js +5 -13
  26. package/backend/git-worktree-info.js +181 -0
  27. package/backend/local-session-engine.js +411 -186
  28. package/backend/main-page-session.js +5 -2
  29. package/backend/native-pty-controller-generation.js +106 -0
  30. package/backend/native-pty-host-client.js +275 -7
  31. package/backend/native-pty-host-identity.js +86 -0
  32. package/backend/native-pty-host.js +813 -114
  33. package/backend/native-session-engine.js +100 -28
  34. package/backend/packaged-node-pty.js +22 -2
  35. package/backend/provider-adapters.js +253 -0
  36. package/backend/provider-session-service.js +241 -0
  37. package/backend/runtime-observation.js +81 -0
  38. package/backend/server.js +354 -84
  39. package/backend/session-engine-bridge.js +21 -2
  40. package/backend/session-engine-router.js +1 -1
  41. package/backend/session-engine.js +1 -1
  42. package/backend/session-stream-protocol.js +185 -0
  43. package/backend/storage-layout.js +55 -0
  44. package/backend/terminal-attach-checkpoint.js +74 -0
  45. package/backend/terminal-exit-quiescence.js +39 -0
  46. package/backend/terminal-reducer-flow-control.js +97 -0
  47. package/backend/terminal-screen-state.js +11 -2
  48. package/backend/terminal-screen-worker-pool.js +59 -6
  49. package/backend/terminal-screen-worker-thread.js +97 -57
  50. package/backend/terminal-screen-worker.js +133 -51
  51. package/backend/terminal-state-serialization.js +127 -0
  52. package/backend/terminal-status.js +23 -4
  53. package/backend/usage-monitor.js +176 -17
  54. package/backend/workspace-directory.js +232 -0
  55. package/backend/workspace-file-router.js +182 -76
  56. package/backend/workspace-file-service.js +319 -4
  57. package/backend/workspace-root-registry.js +164 -0
  58. package/dist/assets/App-DWsa7DXG.js +206 -0
  59. package/dist/assets/{FileEditorMarkdownPreview-elKWc8Im.js → FileEditorMarkdownPreview-UlVd0O4D.js} +92 -92
  60. package/dist/assets/FileEditorPane-Dxx4rKOg.js +2 -0
  61. package/dist/assets/IconGlyphs-AU22gPDY.js +1 -0
  62. package/dist/assets/ProjectFilesSection-DE7Nb9Jk.js +12 -0
  63. package/dist/assets/ReviewPage-CeDKDyZh.js +3 -0
  64. package/dist/assets/code-dark-DPiuyeSp.css +1 -0
  65. package/dist/assets/file-icons-Bw2qd5iT.js +1 -0
  66. package/dist/assets/{index-BrbljRqn.js → index-CUFPJoa5.js} +3 -3
  67. package/dist/assets/main-Bqtb5kuz.css +1 -0
  68. package/dist/assets/{monaco.contribution-CyLosfZI.js → monaco.contribution-BXz-lfFB.js} +2 -2
  69. package/dist/assets/{tsMode-DGZTlTj8.js → tsMode-B0oUTcXQ.js} +1 -1
  70. package/dist/assets/workspace-editor-model-BQol4qbA.js +1 -0
  71. package/dist/assets/workspace-editor-monaco-BICJESG0.js +4 -0
  72. package/dist/assets/workspace-editor-monaco-DRJ7IaXk.js +1 -0
  73. package/dist/assets/workspace-view-state-DvYG_9PH.js +7 -0
  74. package/dist/assets/workspace-working-copy-D8-s_Sgh.js +1 -0
  75. package/dist/farming-2/site.webmanifest +2 -0
  76. package/dist/index.html +1 -1
  77. package/frontend/farming-net/app.css +625 -0
  78. package/frontend/farming-net/app.js +268 -0
  79. package/frontend/farming-net/index.html +86 -0
  80. package/frontend/reading-anchor.js +198 -0
  81. package/frontend/session-bridge.js +12 -3
  82. package/frontend/session-modal-bridge.js +5 -12
  83. package/frontend/skins/crt/app.js +1978 -793
  84. package/frontend/skins/crt/index.html +313 -23
  85. package/frontend/skins/crt/styles/billing.css +294 -223
  86. package/frontend/skins/crt/styles/monochrome-green.css +7 -2
  87. package/frontend/terminal-replay.js +372 -0
  88. package/package.json +10 -3
  89. package/shared/browser-protocol.d.ts +5 -0
  90. package/shared/browser-protocol.js +130 -0
  91. package/dist/assets/App-8dYAM6ql.js +0 -124
  92. package/dist/assets/FileEditorPane-RWiFD2cq.js +0 -5
  93. package/dist/assets/IconGlyphs-DfL0EBnj.js +0 -1
  94. package/dist/assets/ProjectFilesSection-Q4PDsWmM.js +0 -12
  95. package/dist/assets/ReviewPage-BaXu1ZdX.js +0 -3
  96. package/dist/assets/code-dark-CDkOQAtK.css +0 -1
  97. package/dist/assets/file-icons-EFUGSSwf.js +0 -1
  98. package/dist/assets/main-D073SnW4.css +0 -1
  99. package/dist/assets/qoder-C9LmmOSf.svg +0 -1
  100. package/dist/assets/qoder-Cf9gl0Y5.svg +0 -1
  101. package/dist/assets/qoder-gHCinseV.svg +0 -1
  102. package/dist/assets/workspace-view-state-CTyDzk2D.js +0 -1
  103. package/dist/assets/zsh-CLpveKlF.svg +0 -1
  104. package/dist/assets/zsh-FxSpMPbz.svg +0 -1
  105. /package/dist/assets/{api-D1lyBYIQ.js → api-D8nyOEbz.js} +0 -0
  106. /package/dist/assets/{core-ZlAPicox.js → core-D0LFJkDt.js} +0 -0
  107. /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
 
@@ -39,24 +51,36 @@ A keyboard-first control room for watching many agents, opening structured Chat
39
51
 
40
52
  Switching interfaces does not restart or duplicate an Agent. If Farming Code cannot start or render, its bounded diagnostic view leaves the live CRT surface available rather than hiding the running sessions.
41
53
 
54
+ To switch without editing the URL:
55
+
56
+ - **Farming Code → CRT:** click the gear at the bottom-left, open **Interface**, then choose **Farming CRT**.
57
+ - **Farming CRT → Code:** press `S` (or choose **[S] SETTINGS**), then choose **Farming Code** under **UI Theme**.
58
+
59
+ Farming carries the focused Agent into the other interface when possible. Both views keep using the same live Agent and provider session; this is an interface switch, not a restart.
60
+
42
61
  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
62
 
63
+ ## Farming Net: One Portal For Deployments
64
+
65
+ 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.
66
+
67
+ ```bash
68
+ FARMING_NET_PORT=6693 FARMING_NET_BASE_PATH=/farming-net npm run start:net
69
+ ```
70
+
71
+ 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.
72
+
44
73
  ## What You Can Do
45
74
 
46
- - Group live agents by project, pin important work, rename sessions, track unread activity, and archive or resume work.
75
+ - Group live agents by project, pin or rename important work, track unread activity, search live and historical sessions, and archive or resume tasks.
47
76
  - 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.
77
+ - 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.
78
+ - 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
79
  - 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.
80
+ - Continue the same Farming Code task from desktop or phone without moving the Agent process away from the development host.
55
81
 
56
82
  ![Farming Code project files and blame](./docs/products/code/assets/04-files-editor-blame.png)
57
83
 
58
- ![Farming Review](./docs/products/code/assets/10-review-workflow.png)
59
-
60
84
  ## Supported Agent Paths
61
85
 
62
86
  Farming discovers installed CLIs on the host. The richer structured runtime currently applies to providers with ACP support; other detected coding agents remain first-class terminal sessions.
@@ -75,14 +99,7 @@ Farming discovers installed CLIs on the host. The richer structured runtime curr
75
99
 
76
100
  Farming hosts CLIs that already work on the same machine. It does not replace provider installation, login, or account configuration.
77
101
 
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
- ```
102
+ ## Runtime Defaults And Daemon Commands
86
103
 
87
104
  Farming defaults to port `6694`, base path `/farming`, config directory `~/.farming`, and token authentication. The startup log prints a URL similar to:
88
105
 
@@ -90,7 +107,7 @@ Farming defaults to port `6694`, base path `/farming`, config directory `~/.farm
90
107
  http://development-host:6694/farming?token=<startup-token>
91
108
  ```
92
109
 
93
- Open it, choose **New Agent**, select an Agent and workspace, and start in Chat or Terminal. Useful daemon commands are:
110
+ Useful daemon commands are:
94
111
 
95
112
  ```bash
96
113
  farming status
@@ -107,11 +124,7 @@ The first authenticated start stores a readable random token in `~/.farming/.ses
107
124
 
108
125
  Desktop keeps the project, conversation, files, and review close together. Mobile focuses one conversation, terminal, or file at a time and moves navigation into a drawer, making it useful for checking progress and sending a short intervention.
109
126
 
110
- <p align="center">
111
- <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
- </p>
127
+ Farming CRT is currently a desktop interface. Use Farming Code from a phone; CRT mobile concepts are not part of the supported product yet.
115
128
 
116
129
  ## Installation And Updates
117
130
 
@@ -143,9 +156,9 @@ Development host
143
156
  repositories, shells, Codex, Claude Code, OpenCode, Qoder, ...
144
157
  ```
145
158
 
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.
159
+ 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
160
 
148
- 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.
161
+ 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 except for explicit Codex archive and unarchive lifecycle actions.
149
162
 
150
163
  ## Security
151
164
 
@@ -158,6 +171,7 @@ Token authentication protects HTTP and WebSocket traffic. `FARMING_DISABLE_AUTH=
158
171
  - [Farming 2 product overview and capability map](./docs/products/README.md)
159
172
  - [Farming Code guide](./docs/products/code/README.md)
160
173
  - [Farming CRT guide](./docs/products/crt/README.md)
174
+ - [Farming Net deployment portal](./docs/products/net/README.md)
161
175
  - [Mobile guide](./docs/products/code/mobile-guide.md)
162
176
  - [ACP runtime](./docs/products/code/acp-runtime.md)
163
177
  - [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
 
@@ -39,24 +51,36 @@ Farming 2 在同一批 Agent 和 Session 上提供两套完整的浏览器界面
39
51
 
40
52
  切换界面不会重启或复制 Agent。如果 Farming Code 启动或渲染失败,有限范围的诊断层仍会保留后面的实时 CRT 界面,而不是把正在运行的 Session 一起遮掉。
41
53
 
54
+ 不修改地址也可以直接切换:
55
+
56
+ - **Farming Code → CRT:**点击左下角齿轮,打开**界面**,再选择 **Farming CRT**。
57
+ - **Farming CRT → Code:**按 `S`(或点击 **[S] SETTINGS**),在 **UI Theme** 中选择 **Farming Code**。
58
+
59
+ 条件允许时,Farming 会把当前聚焦的 Agent 一起带到另一套界面。两边继续使用同一个实时 Agent 和 Provider Session;这里只切换界面,不会重启会话。
60
+
42
61
  完整能力矩阵和截图导览见 [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
62
 
63
+ ## Farming Net:部署门户
64
+
65
+ Farming Net 是一套独立、带 Token 鉴权的 Farming 环境目录。卡片可以指向当前设备、远程开发机、内网地址或隧道中的 Farming;已登记的目标接受短时签名通行证,让用户只保留一个门户登录,不再分别记录每个部署地址和目标 Token。
66
+
67
+ ```bash
68
+ FARMING_NET_PORT=6693 FARMING_NET_BASE_PATH=/farming-net npm run start:net
69
+ ```
70
+
71
+ 门户自己的 Token、签名身份和私有 `instances.json` 注册表放在 `~/.farming-net/`。它不代理目标流量,也不保存目标 Token;每个目标仍是独立 Farming 服务,并且主动决定是否信任门户。登记方式和安全边界见 [Farming Net 指南](./docs/products/net/README.zh_cn.md)。
72
+
44
73
  ## 现在可以做什么
45
74
 
46
- - 按项目组织实时 Agent,置顶重点工作、重命名、查看未读状态,并归档或恢复任务。
75
+ - 按项目组织实时 Agent,置顶或重命名重点工作、查看未读状态、搜索实时与历史 Session,并归档或恢复任务。
47
76
  - 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 对应工作。
77
+ - 在结构化 Chat 与真实 PTY Terminal 之间切换同一个 Provider Session。支持的 Codex 模型、思考强度、Fast、Ultra 和权限修改会作用到实时工作流;兼容 Terminal 会立即应用模型修改,并在接受下一条 Composer 消息前确认 CLI 的真实状态。
78
+ - 浏览、搜索并轻量编辑 Project Files,通过复用 VS Code 图算法的 Git History 查看提交树与变更文件,检查 Git ChangesDiff Blame,再把 Commit 或 Working Copy 修改送入带 Revision、行内评论和 Reviewed 状态的 Review
53
79
  - 在 Provider 提供所需数据时查看 CPU/MEM、Token Rate、Context、Quota、Provider 用量,以及 CRT 的按日/实时 Token 遥测。
54
- - 在电脑和手机浏览器中使用同一个服务;不同布局围绕不同设备上可用的注意力设计。
80
+ - 从电脑或手机继续同一个 Farming Code 任务,Agent 进程始终留在开发机上。
55
81
 
56
82
  ![Farming Code 项目文件与 Blame](./docs/products/code/assets/04-files-editor-blame.png)
57
83
 
58
- ![Farming Review](./docs/products/code/assets/10-review-workflow.png)
59
-
60
84
  ## 支持的 Agent 路径
61
85
 
62
86
  Farming 会发现开发机上已经安装的 CLI。有 ACP 支持的 Provider 使用更完整的结构化运行时,其他检测到的 Coding Agent 仍然可以作为一等 Terminal Session 使用。
@@ -75,14 +99,7 @@ Farming 会发现开发机上已经安装的 CLI。有 ACP 支持的 Provider
75
99
 
76
100
  Farming 承载的是已经能在同一台机器正常工作的 CLI,不替代 Provider 的安装、登录和账户配置。
77
101
 
78
- ## 快速开始
79
-
80
- 安装 Node.js 22 或更新版本,并先安装、登录至少一个受支持的 Coding CLI:
81
-
82
- ```bash
83
- npm install --global farming-code
84
- farming daemon
85
- ```
102
+ ## 运行默认值与 Daemon 命令
86
103
 
87
104
  默认端口是 `6694`,Base Path 是 `/farming`,配置目录是 `~/.farming`,Token 鉴权默认开启。启动日志会打印类似下面的 URL:
88
105
 
@@ -90,7 +107,7 @@ farming daemon
90
107
  http://development-host:6694/farming?token=<startup-token>
91
108
  ```
92
109
 
93
- 打开 URL,选择 **New Agent**、Agent 类型和 Workspace,然后从 Chat 或 Terminal 开始工作。常用守护进程命令:
110
+ 常用守护进程命令:
94
111
 
95
112
  ```bash
96
113
  farming status
@@ -107,11 +124,7 @@ farming stop
107
124
 
108
125
  桌面端把项目、对话、文件和 Review 放在彼此靠近的位置。移动端一次聚焦一段对话、一个终端或一个文件,并把导航移入抽屉,更适合查看进度和发送短介入。
109
126
 
110
- <p align="center">
111
- <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
- </p>
127
+ Farming CRT 当前只作为桌面界面使用。手机请使用 Farming Code;CRT 手机方案目前仍是概念设计,不属于已支持的产品能力。
115
128
 
116
129
  ## 安装与更新
117
130
 
@@ -143,9 +156,9 @@ Development host
143
156
  repositories, shells, Codex, Claude Code, OpenCode, Qoder, ...
144
157
  ```
145
158
 
146
- 后端负责生命周期、鉴权、Session 路由、Workspace 边界、History 和配置。交互式 Terminal 默认由独立的原生 PTY Host 持有,因此浏览器和 Server 可以重新连接,而不需要替换实际进程。xterm.js 是产品默认终端渲染器;Ghostty Web Adapter 只保留为显式调试路径。
159
+ 后端负责生命周期、鉴权、Session 路由、Workspace 边界、History 和配置。交互式 Terminal 默认由独立的原生 PTY Host 持有,因此浏览器和 Server 可以重新连接,而不需要替换实际进程。xterm.js WebGL 是唯一受支持的产品 Terminal Renderer;Ghostty Web Adapter 只保留为显式调试路径,不作为运行时 Fallback。
147
160
 
148
- 运行时设置存放在 `~/.farming/settings.json`。Farming Session 元数据、项目成员索引、归档运行、主题设置、更新状态、日志和启动 Token 使用 `~/.farming/` 下彼此独立的文件。外部 Provider History 仍然只读。
161
+ 运行时设置存放在 `~/.farming/settings.json`。Farming Session 元数据、项目成员索引、归档运行、主题设置、更新状态、日志和启动 Token 使用 `~/.farming/` 下彼此独立的文件。外部 Provider History 保持只读,但明确的 Codex archive / unarchive 生命周期操作除外。
149
162
 
150
163
  ## 安全
151
164
 
@@ -158,6 +171,7 @@ Token 鉴权同时保护 HTTP 和 WebSocket。`FARMING_DISABLE_AUTH=1` 只适合
158
171
  - [Farming 2 产品总览与能力矩阵](./docs/products/README.zh_cn.md)
159
172
  - [Farming Code 指南](./docs/products/code/README.zh_cn.md)
160
173
  - [Farming CRT 指南](./docs/products/crt/README.zh_cn.md)
174
+ - [Farming Net 部署门户](./docs/products/net/README.zh_cn.md)
161
175
  - [移动端指南](./docs/products/code/mobile-guide.zh_cn.md)
162
176
  - [ACP 运行时](./docs/products/code/acp-runtime.zh_cn.md)
163
177
  - [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
+ };