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.
- package/README.md +36 -23
- package/README.zh_cn.md +36 -23
- package/THIRD_PARTY_NOTICES.md +11 -2
- package/backend/acp-checkpoint-store.js +198 -0
- package/backend/acp-runtime.js +306 -83
- package/backend/acp-session-state.js +202 -6
- package/backend/acp-transcript.js +112 -0
- package/backend/agent-activity.js +6 -157
- package/backend/agent-manager.js +1482 -730
- package/backend/agent-provider-session.js +17 -242
- package/backend/agent-runtime-binding.js +219 -0
- package/backend/agent-session-history.js +66 -1
- package/backend/auth.js +79 -6
- package/backend/codex-models.js +81 -84
- package/backend/codex-session-archive.js +45 -0
- package/backend/codex-terminal-profile.js +500 -0
- package/backend/codex-transcript-sanitizer.js +12 -0
- package/backend/codex-transcript.js +230 -8
- package/backend/config-manager.js +30 -0
- package/backend/control-api.js +192 -17
- package/backend/farming-net-pass.js +285 -0
- package/backend/farming-net-registry.js +112 -0
- package/backend/farming-net-server.js +298 -0
- package/backend/farming-session-store.js +5 -13
- package/backend/git-worktree-info.js +181 -0
- package/backend/local-session-engine.js +411 -186
- package/backend/main-page-session.js +5 -2
- package/backend/native-pty-controller-generation.js +106 -0
- package/backend/native-pty-host-client.js +275 -7
- package/backend/native-pty-host-identity.js +86 -0
- package/backend/native-pty-host.js +813 -114
- package/backend/native-session-engine.js +100 -28
- package/backend/packaged-node-pty.js +22 -2
- package/backend/provider-adapters.js +253 -0
- package/backend/provider-session-service.js +241 -0
- package/backend/runtime-observation.js +81 -0
- package/backend/server.js +294 -86
- package/backend/session-engine-bridge.js +21 -2
- package/backend/session-engine-router.js +1 -1
- package/backend/session-engine.js +1 -1
- package/backend/session-stream-protocol.js +185 -0
- package/backend/storage-layout.js +55 -0
- package/backend/terminal-attach-checkpoint.js +74 -0
- package/backend/terminal-exit-quiescence.js +39 -0
- package/backend/terminal-reducer-flow-control.js +97 -0
- package/backend/terminal-screen-state.js +11 -2
- package/backend/terminal-screen-worker-pool.js +59 -6
- package/backend/terminal-screen-worker-thread.js +97 -57
- package/backend/terminal-screen-worker.js +133 -51
- package/backend/terminal-state-serialization.js +127 -0
- package/backend/terminal-status.js +23 -4
- package/backend/usage-monitor.js +81 -4
- package/backend/workspace-directory.js +152 -0
- package/backend/workspace-file-router.js +182 -76
- package/backend/workspace-file-service.js +319 -4
- package/backend/workspace-root-registry.js +164 -0
- package/dist/assets/App-CHYpgECZ.js +208 -0
- package/dist/assets/{FileEditorMarkdownPreview-elKWc8Im.js → FileEditorMarkdownPreview--Cri80nQ.js} +92 -92
- package/dist/assets/FileEditorPane-DGJlEKdf.js +2 -0
- package/dist/assets/IconGlyphs-Cc14sepw.js +1 -0
- package/dist/assets/ProjectFilesSection-Cz1wG4NT.js +12 -0
- package/dist/assets/{ReviewPage-BaXu1ZdX.js → ReviewPage-LonRc6Fz.js} +1 -1
- package/dist/assets/code-dark-B1pAtO_P.css +1 -0
- package/dist/assets/file-icons-Bw2qd5iT.js +1 -0
- package/dist/assets/{index-BrbljRqn.js → index-miO2r9IZ.js} +3 -3
- package/dist/assets/main-DNe0jPw-.css +1 -0
- package/dist/assets/workspace-editor-model-BQol4qbA.js +1 -0
- package/dist/assets/workspace-editor-monaco-5gFbeDPF.js +1 -0
- package/dist/assets/workspace-editor-monaco-xPv8JzHH.js +4 -0
- package/dist/assets/workspace-view-state-DvYG_9PH.js +7 -0
- package/dist/assets/workspace-working-copy-D8-s_Sgh.js +1 -0
- package/dist/index.html +1 -1
- package/frontend/farming-net/app.css +625 -0
- package/frontend/farming-net/app.js +268 -0
- package/frontend/farming-net/index.html +86 -0
- package/frontend/reading-anchor.js +198 -0
- package/frontend/session-bridge.js +12 -3
- package/frontend/session-modal-bridge.js +5 -12
- package/frontend/skins/crt/app.js +1976 -793
- package/frontend/skins/crt/index.html +313 -23
- package/frontend/skins/crt/styles/billing.css +294 -223
- package/frontend/skins/crt/styles/monochrome-green.css +7 -2
- package/frontend/terminal-replay.js +372 -0
- package/package.json +10 -3
- package/shared/browser-protocol.d.ts +5 -0
- package/shared/browser-protocol.js +130 -0
- package/dist/assets/App-8dYAM6ql.js +0 -124
- package/dist/assets/FileEditorPane-RWiFD2cq.js +0 -5
- package/dist/assets/IconGlyphs-DfL0EBnj.js +0 -1
- package/dist/assets/ProjectFilesSection-Q4PDsWmM.js +0 -12
- package/dist/assets/code-dark-CDkOQAtK.css +0 -1
- package/dist/assets/file-icons-EFUGSSwf.js +0 -1
- package/dist/assets/main-D073SnW4.css +0 -1
- package/dist/assets/qoder-C9LmmOSf.svg +0 -1
- package/dist/assets/qoder-Cf9gl0Y5.svg +0 -1
- package/dist/assets/qoder-gHCinseV.svg +0 -1
- package/dist/assets/workspace-view-state-CTyDzk2D.js +0 -1
- package/dist/assets/zsh-CLpveKlF.svg +0 -1
- package/dist/assets/zsh-FxSpMPbz.svg +0 -1
- /package/dist/assets/{api-D1lyBYIQ.js → api-D8nyOEbz.js} +0 -0
- /package/dist/assets/{core-ZlAPicox.js → core-D0LFJkDt.js} +0 -0
- /package/dist/assets/{useWorkspaceMenuKeyboard-CneKAZUJ.js → useWorkspaceMenuKeyboard-Brws6Ar9.js} +0 -0
package/README.md
CHANGED
|
@@ -9,9 +9,21 @@
|
|
|
9
9
|

|
|
10
10
|

|
|
11
11
|
|
|
12
|
-
Farming is
|
|
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
|
|
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
|
+

|
|
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
|
|
34
|
+
The default workbench for reading conversations, intervening in tasks, editing files, and reviewing workspace changes.
|
|
23
35
|
|
|
24
36
|

|
|
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,
|
|
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
|
-
-
|
|
49
|
-
-
|
|
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
|
-
-
|
|
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
|

|
|
57
76
|
|
|
58
|
-

|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
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
|

|
|
10
10
|

|
|
11
11
|
|
|
12
|
-
Farming
|
|
12
|
+
Farming 是一个开源、可自定义、运行在开发机上的浏览器 AI Coding Agent 工作台。它把多个实时 Agent、结构化对话、真实终端、项目文件、Review、历史记录和运行时控制放在一起,同时代码仓库和 Agent 进程仍然留在开发机上。
|
|
13
13
|
|
|
14
|
-
|
|
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
|
+

|
|
15
27
|
|
|
16
28
|
## 两套界面,同一套运行时
|
|
17
29
|
|
|
@@ -19,7 +31,7 @@ Farming 2 在同一批 Agent 和 Session 上提供两套完整的浏览器界面
|
|
|
19
31
|
|
|
20
32
|
### Farming Code
|
|
21
33
|
|
|
22
|
-
|
|
34
|
+
默认工作台,适合阅读对话、介入任务、编辑文件和检查工作区修改。
|
|
23
35
|
|
|
24
36
|

|
|
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
|
-
-
|
|
49
|
-
-
|
|
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 Changes、Diff 和 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
|

|
|
57
76
|
|
|
58
|
-

|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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)
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -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.
|
|
19
|
-
| `@agentclientprotocol/claude-agent-acp` | 0.
|
|
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
|
+
};
|