@realqiyan/agent-task 0.0.1-rc.1 → 0.0.1-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/web.js CHANGED
@@ -16,7 +16,7 @@ function query(schema, request) {
16
16
  }
17
17
  return schema.parse(value);
18
18
  }
19
- export function createWebRouter(tasks, settings, token, fatal) {
19
+ export function createWebRouter(tasks, settings, token, fatal, restart, instanceId) {
20
20
  const router = express.Router(), authorized = bearerVerifier(token);
21
21
  router.use((req, res, next) => {
22
22
  res.set({
@@ -75,7 +75,7 @@ export function createWebRouter(tasks, settings, token, fatal) {
75
75
  res.status(400).json({ error: "Request rejected; check inputs and refresh the current state" });
76
76
  }
77
77
  };
78
- router.get("/api/status", respond(() => ({ ...tasks.status(), settings: settings.status() })));
78
+ router.get("/api/status", respond(() => ({ ...tasks.status(), settings: settings.status(), instanceId })));
79
79
  router.get("/api/tasks", respond(req => {
80
80
  const input = query(taskListSchema, req), page = tasks.list(input.after, input.limit);
81
81
  return { ...page, items: page.items.map(task => ({ id: task.id, name: task.input.name, status: task.status, nextAt: task.nextAt, schedule: task.input.schedule })) };
@@ -96,6 +96,18 @@ export function createWebRouter(tasks, settings, token, fatal) {
96
96
  }));
97
97
  router.post("/api/settings/config", respond(req => settings.saveConfig(req.body)));
98
98
  router.post("/api/settings/credential", respond(req => settings.saveCredential(req.body)));
99
+ router.post("/api/restart", async (req, res) => {
100
+ if (!req.body || Array.isArray(req.body) || typeof req.body !== "object" || Object.keys(req.body).length) {
101
+ res.status(400).json({ error: "Expected an empty request" });
102
+ return;
103
+ }
104
+ try {
105
+ res.json({ data: await restart() });
106
+ }
107
+ catch {
108
+ res.status(503).json({ error: "Restart could not be confirmed; inspect private logs and use CLI status before retrying" });
109
+ }
110
+ });
99
111
  router.use((_req, res) => { res.status(404).end(); });
100
112
  router.use(((error, _req, res, _next) => {
101
113
  res.status(error.status === 413 ? 413 : error.status === 415 ? 415 : 400).json({ error: "Invalid request body" });
package/dist/worker.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { openAcp } from "./acp.js";
2
2
  import { z } from "zod";
3
3
  import { errorText, executorSchema } from "./model.js";
4
- const inputSchema = z.object({ executor: executorSchema, inputFiles: z.array(z.string()).max(20) }).strict();
4
+ const inputSchema = z.object({ executor: executorSchema, inputFiles: z.array(z.string()).max(20), resultsRoot: z.string().min(1) }).strict();
5
5
  const controller = new AbortController();
6
6
  const pending = new Map();
7
7
  let sequence = 0, started = false, busy = false;
@@ -64,7 +64,7 @@ process.on("message", (message) => {
64
64
  started = true;
65
65
  opening = (async () => {
66
66
  const input = inputSchema.parse(message.input);
67
- session = await openAcp(input.executor, controller.signal, emit, input.inputFiles);
67
+ session = await openAcp(input.executor, controller.signal, emit, input.resultsRoot, input.inputFiles);
68
68
  void session.closed.then(() => { if (!closing)
69
69
  void fail(new Error("ACP process exited")); });
70
70
  if (!controller.signal.aborted)
@@ -33,10 +33,10 @@
33
33
 
34
34
  - 执行端声明能力、管理员授权、真实验证证据分别表达,不能相互推导。
35
35
  - 只有管理员注册的执行端可启动。命令、凭据和能力端点属于管理员;任务可选择现有绝对工作空间,但路径校验不是授权或沙箱。
36
- - 授权须在可控制的实际操作入口检查。ACP 审批只约束经过回调的请求,白名单及 `unattendedApproved` 不能证明所有原生工具已受控。
36
+ - 授权须在可控制的实际操作入口检查。沿用执行端原生授权,额外 ACP 审批请求一律拒绝;`unattendedApproved` 不能证明所有原生工具已受控。
37
37
  - Prompt、角色、技能、工具标题、协议成功和人工事实回复都不授予权限。取消、恢复和跨 Agent 传递不能扩大授权。
38
38
  - 无人值守执行不等待交互审批;授权不充分或元数据不足时拒绝,不退回更宽权限或自动永久允许。
39
- - MCP token、管理员 token、协调模型与执行端凭据分开,不整套继承服务环境,不有意记录秘密。Agent 输出和协调日志仍可能敏感。
39
+ - MCP token、管理员 token、协调模型与执行端凭据分开;执行端继承用户原生环境前过滤服务私密变量,不有意记录秘密。Agent 输出和协调日志仍可能敏感。
40
40
  - 优先使用执行端原生配置,不读取其私有业务存储格式,不将特定领域规则塞进平台。
41
41
 
42
42
  ## 存储与发布
@@ -1,113 +1,115 @@
1
1
  # 安装与运行
2
2
 
3
- 支持 Linux 和 macOS,要求 Node.js **22.19+** 与 npm。安装包包含编译后的服务、worker、Web 资源和操作脚本,不需要源码、TypeScript 或用户自行构建。当前发布状态和平台证据见[验证说明](validation.md#npm-发布候选验证),发布步骤见[发布说明](release.md)。
3
+ 支持 Linux 和 macOS,要求 Node.js **22.19+** 与 npm。安装包包含运行产物,不需要源码或 TypeScript。发布状态和平台证据见[验证说明](validation.md),发布操作见[发布说明](release.md)。
4
4
 
5
5
  ## 安装与交互配置
6
6
 
7
7
  ```bash
8
8
  npm install -g @realqiyan/agent-task
9
- agent-task --version
10
9
  agent-task setup
11
10
  agent-task start
12
11
  ```
13
12
 
14
- 使用当前用户可写的 npm prefix,不以 root 运行任务服务。若使用 nvm 等版本管理器,先选定后台服务长期使用的 Node 安装;服务记录绝对路径,移除该 Node 或全局包会使它无法启动。
13
+ 使用当前用户可写的 npm prefix,不以 root 运行。若使用 nvm 等版本管理器,先选定后台长期使用的 Node;服务记录绝对路径,移除该 Node 或全局包会使它无法启动。
15
14
 
16
- 向导收集工作空间、可选执行端、可选协调模型及端口,并询问是否安装后台服务。默认只准备配置,不启动服务、不安装执行端、不请求密钥。取消确认不会写入配置。无终端环境必须明确提供参数:
15
+ 向导收集工作空间、可选执行端、可选协调模型及端口,并询问是否安装后台服务。默认只准备配置,不启动服务、不请求密钥。取消确认不写配置。无终端环境须明确提供参数:
17
16
 
18
17
  ```bash
19
18
  agent-task setup --executors none --cwd /absolute/workspace --prepare-only
20
- # 可选:安装 Pi ACP 组件,但仍不授权执行。
19
+ # 另一个首次安装选择:安装 Pi ACP 组件,但不授权执行。
21
20
  agent-task setup --home /absolute/private/agent-task \
22
21
  --executors pi --cwd /absolute/workspace \
23
22
  --provider deepseek --model deepseek-flash --prepare-only
24
23
  ```
25
24
 
26
- 这两个例子是不同的首次安装选择,不要用第二条覆盖已有安装。工作空间须预先存在,且位于安装目录及 npm 包之外。完整选项见 `agent-task setup --help`;执行端固定版本及平台实现见 [install-lib.mjs](../scripts/install-lib.mjs)。
25
+ 工作空间须预先存在,且位于安装目录及 npm 包之外。完整选项见 `agent-task setup --help`;组件版本及平台实现见 [install-lib.mjs](../scripts/install-lib.mjs)。安装不执行原生登录或批准任务。
27
26
 
28
- 默认私有目录为 Linux 的 `~/.local/share/agent-task` 或 macOS 的 `~/Library/Application Support/agent-task`。setup 输出配置、凭据文件和控制台地址,**不打印 token**。使用自定义 `--home` 时,后续命令也需传同一路径。
27
+ 默认安装目录为 Linux 的 `~/.local/share/agent-task` 或 macOS 的 `~/Library/Application Support/agent-task`。自定义 `--home` 后,后续命令也传同一路径。setup 输出私有文件路径和控制台地址,**不打印 token**。
29
28
 
30
- 配置、凭据和任务数据不写入 npm 包目录。npm 管理应用包;setup 只管理私有目录、选定的 ACP 组件和用户服务。不要移动 npm prefix、Node 或安装目录后直接继续运行;当前不提供既有安装的自动路径重绑。
29
+ ## 数据目录与配置
31
30
 
32
- ## 配置与认证
31
+ 初始 `dataDir` 是安装目录下的 `data/`。运行配置、私有环境文件、任务和协调会话记录、结果及服务文件日志均位于当前 `dataDir`。配置契约见 [config.ts](../src/config.ts),路径选择与新目录准备见 [startup.ts](../src/startup.ts)。
33
32
 
34
- 1. 前台运行 `agent-task start`,打开 setup 输出的本机控制台地址。用本地编辑器读取私有 `environment.json` 的 `AGENT_TASK_ADMIN_TOKEN`,在连接表单输入。不要把 token 放进 URL、命令行、Git、截图或日志。
35
- 2. 在[控制台](web-console.md)配置协调模型、单独保存模型密钥及注册 ACP 执行端。未配置协调模型时,服务仍可启动和配置,但不能接收可执行任务。
36
- 3. 如需安装器提供的执行端,用 setup 输出的专属路径按原生方式认证:[Pi](https://github.com/earendil-works/pi/tree/v1.3.0/packages/coding-agent/docs)、[Codex ACP](https://github.com/agentclientprotocol/codex-acp#readme)、[OpenCode](https://opencode.ai/docs/acp/)。后台服务与原生登录须使用一致的 HOME/配置目录。
37
- 4. 停服后审核执行端原生工具、沙箱、凭据传递及结果读取范围,再由管理员明确批准 `unattendedApproved`。安装器从不登录、自动批准或扩大权限;ACP allowlist 不是完整工具授权或 OS 沙箱。
38
- 5. 保存后手动重启。正在运行或等待人工回复的任务可能持有 ACP 会话,重启前须核查;不保证无损续接。
33
+ 安装目录保留固定的 `launch.json` 定位文件、安装清单、组件及启动器控制资源;它不是另一份运行配置。这个固定入口让原有 CLI、systemd 和 launchd 注册在更换数据目录后仍能找到服务。npm 包、用户工作空间和执行端原生配置不随 `dataDir` 移动。
39
34
 
40
- 后台和托管前台启动都只读取明确的私有环境文件,不自动继承 shell 模型密钥。MCP token、管理员 token、协调模型和执行端凭据必须分开。工作空间校验不是授权。
35
+ 在 Web 修改数据目录后,下次启动只在目标目录准备已保存的配置和私有环境,并切换定位文件;**不复制任务、会话、结果或日志,不删除旧目录**。目标已有不同启动文件时拒绝覆盖;须停服核对并显式准备目标,不自动合并或修复。新目录没有旧任务,不能把切换目录当成故障恢复或重放许可。
41
36
 
42
- 显式执行握手检查:
37
+ 1. 运行 `agent-task start`,打开输出的本机控制台地址。用本地编辑器读取当前 `dataDir/environment.json` 的 `AGENT_TASK_ADMIN_TOKEN`,在连接表单输入。不要把 token 放进 URL、命令行、Git、截图或日志。
38
+ 2. 在[控制台](web-console.md)配置协调模型、单独保存模型密钥及注册执行端。未配置协调模型时可管理服务,但不能接收可执行任务。
39
+ 3. 按执行端官方方式准备原生认证及权限:[Pi](https://github.com/earendil-works/pi/tree/v1.3.0/packages/coding-agent/docs)、[Codex ACP](https://github.com/agentclientprotocol/codex-acp#readme)、[Claude ACP](https://github.com/zed-industries/claude-agent-acp)、[OpenCode](https://opencode.ai/docs/acp/)。后台服务与原生登录须使用同一 OS 用户及相应 HOME/配置目录。
40
+ 4. 审核原生工具、沙箱、凭据和结果文件读取权限后,再明确批准无人值守执行。注册只选类型,不配置命令、参数、环境变量清单或 ACP 工具白名单;系统映射见 [executor-runtime.ts](../src/executor-runtime.ts)。额外 ACP 审批请求被拒绝,不自动批准。
41
+
42
+ 服务和协调员的秘密只从私有环境文件加载,不回退到 shell 模型密钥。执行端沿用启动上下文中的用户原生环境,但排除私有文件变量、服务/管理员令牌、协调员凭据及同值别名。不要把执行端原生凭据放入服务的私有环境文件。后台管理器的环境未必等于交互式 shell;启动器不执行 shell 启动脚本,也不导入原生认证文件。隔离实现见 [environment.ts](../src/environment.ts)、[executor-runtime.ts](../src/executor-runtime.ts)。这不是同一 OS 用户下的沙箱。
43
+
44
+ 显式握手检查:
43
45
 
44
46
  ```bash
45
47
  agent-task setup --check --probe
46
48
  ```
47
49
 
48
- 这只执行 ACP initialize,不验证模型认证、任务成功或工具权限。普通 Pi CLI 不是 ACP server,需配合 pi-acp。已有执行端也可直接由控制台注册绝对 ACP 命令路径,不必重新安装或使用全局执行端。
50
+ 这只做 ACP initialize,不验证模型认证、任务成功或原生工具权限。普通 Pi CLI 不是 ACP server,须配合 pi-acp。自行安装的受支持适配器也可使用,只要在服务的原生 PATH 中;无需填写绝对可执行文件路径。
49
51
 
50
- ## 前台运行与健康检查
52
+ ## 前台、重启与健康检查
51
53
 
52
54
  ```bash
53
55
  agent-task start
54
- # 另一个终端;自动从私有文件读取 MCP token,不输出凭据。
56
+ # 另一个终端:
55
57
  agent-task status
58
+ agent-task restart
56
59
  ```
57
60
 
58
- Ctrl-C 停止前台服务。也可显式指定已有的私有文件:
61
+ Ctrl-C 关闭前台启动器及其运行进程。`restart` 和 Web 的“重启并检查可用性”使用同一启动器:先停止新派发、关闭活动执行并释放数据所有权,再用已保存配置创建新进程。前台终端和后台服务管理器继续监督启动器,不需另开一个脱离监督的进程。
59
62
 
60
- ```bash
61
- agent-task start --config /absolute/private/config.json \
62
- --environment /absolute/private/environment.json
63
- ```
63
+ 只有新实例通过带 MCP token 的健康检查才返回成功,并给出新实例标识及地址。端口变化或自动分配端口时,以返回地址为准;Web 不通过链接传递管理员令牌,须重新连接。
64
+
65
+ 重启会关闭 ACP 会话,不保证等待人工回复的运行能无损继续。结果不明的执行保持阻断,不自动重试、重放或改派执行端。请求超时不代表重启没发生,先用 `agent-task status` 核对;不要盲目重复操作。
64
66
 
65
- 文件权限、Web 认证与配置保存约束见[控制台说明](web-console.md)。显式文件模式不会生成安装清单;此时用自己的 MCP 客户端调用 `service_status`,不要使用依赖清单的 `agent-task status`。
67
+ 启动失败不会回滚配置或重放工作。查看当前 `dataDir/service.log`;确认没有运行实例持有数据后,修正私有文件,再显式 `agent-task restart`。启动器也已退出时,用原启动方式重新启动。后台管理器的“进程活动”不等于 HTTP 服务可用,以认证健康检查为准。
66
68
 
67
- 配置契约以 [config.example.json](../config.example.json)、[config.ts](../src/config.ts) 为准。开发者仍可 `npm ci --ignore-scripts && npm run build` 后使用 `npm start -- --config ... --environment ...`,但 npm 安装用户不需要这些步骤。
69
+ 源码开发者执行 `npm ci --ignore-scripts && npm run build` 后,可用 `npm run setup -- ...` 和 `npm start -- --home ...`;npm 安装用户不需要构建。内部 `dist/main.js` 不是独立的重启入口。
68
70
 
69
71
  ## 后台运行
70
72
 
71
- 先停止前台实例,再安装并启动后台服务:
73
+ 先停止前台实例,再注册并启动用户服务:
72
74
 
73
75
  ```bash
74
76
  agent-task service install
75
77
  agent-task status
78
+ agent-task restart
76
79
  ```
77
80
 
78
- 命令在真实服务管理器中注册服务,并等待带 MCP token 的健康检查。重复安装不会覆盖配置或授权;已有同名但内容不同的服务会被拒绝覆盖。每个 OS 用户只管理一个同名服务。
81
+ 重复安装不会覆盖配置或批准执行端。同名但内容不同的服务文件会被拒绝覆盖。每个 OS 用户只管理一个同名服务。
79
82
 
80
83
  Linux 需要可连接的 systemd 用户管理器:
81
84
 
82
85
  ```bash
83
86
  systemctl --user status agent-task.service
84
87
  systemctl --user stop agent-task.service
85
- systemctl --user restart agent-task.service
86
88
  journalctl --user -u agent-task.service
87
89
  ```
88
90
 
89
- SSH/容器会话若没有用户 bus,应先配置正常用户会话;不要以 root 启动本服务。注销后运行需要管理员按策略配置 lingering,安装器不会启用。手动部署模板见 [deploy/agent-task.service.example](../deploy/agent-task.service.example)。
91
+ SSH/容器会话若没有用户 bus,先配置正常用户会话,不以 root 运行。注销后存续需要管理员按策略配置 lingering,安装器不会启用。手动部署模板见 [deploy/agent-task.service.example](../deploy/agent-task.service.example)。
90
92
 
91
93
  macOS 需要已登录的图形用户会话:
92
94
 
93
95
  ```bash
94
96
  launchctl print gui/$(id -u)/com.agent-task.service
97
+ agent-task restart
95
98
  agent-task service remove
96
- agent-task service install
97
99
  ```
98
100
 
99
- remove 停止并卸载服务注册,install 重新加载配置。LaunchAgent 不是系统 LaunchDaemon,不保证用户注销后运行。日志位于 setup 的私有目录内。launchd 不等同于 Linux cgroup;两平台都不保证休眠或关机期间准点执行。
101
+ LaunchAgent 不是系统 LaunchDaemon,不保证用户注销后运行,也不等同于 Linux cgroup。两平台都不保证休眠或关机期间准点执行。服务文件日志由启动器写入当前数据目录;systemd journal 是操作系统另行管理的日志。
100
102
 
101
- ## 重入、更新与卸载
103
+ ## 更新与卸载
102
104
 
103
- 重复 setup 保留配置和授权。中断安装只能显式 `agent-task setup --resume`;所有权、清单或服务状态不明时停止核查,不删除历史或工作空间修复。
105
+ 重复 setup 保留配置和授权。中断安装只能显式 `agent-task setup --resume`;所有权或清单不明时停止核查,不删除数据修复。
104
106
 
105
- 本项目仍处于调研阶段,不保证旧数据兼容。更新前阅读发行说明,停止服务并备份私有目录;普通启动不迁移既有数据。更换 Node 或 npm prefix 前先移除服务注册。不要在服务运行期间删除或覆盖应用包。
107
+ 调研阶段不保证旧数据兼容,也不提供普通启动迁移。更新前停服并备份安装目录和当前数据目录。更换 Node、npm prefix 或安装目录前先移除服务注册;不要在运行时删除应用包。
106
108
 
107
109
  ```bash
108
- agent-task service remove # 仅停服、移除服务注册;保留组件和数据
109
- agent-task uninstall # 再移除本安装拥有的 ACP 组件
110
+ agent-task service remove # 仅停止并移除服务注册
111
+ agent-task uninstall # 再移除本安装拥有的 ACP 组件
110
112
  npm uninstall -g @realqiyan/agent-task # 最后由 npm 移除应用包
111
113
  ```
112
114
 
113
- `agent-task uninstall` 可重复运行,保留配置、凭据、日志、任务历史、工作空间和未入清单文件,不操作全局执行端及原生认证。卸载不是凭据撤销或数据销毁;停服不能回滚已发生的外部操作。实现见 [uninstall.mjs](../scripts/uninstall.mjs)。
115
+ 卸载可重复运行,保留配置、凭据、日志、任务历史、工作空间及未入清单文件,不操作全局执行端和原生认证。卸载不是凭据撤销或数据销毁,停服也不能回滚外部操作。实现见 [uninstall.mjs](../scripts/uninstall.mjs)。
@@ -8,9 +8,9 @@
8
8
  - 任务不依赖发起客户端持续在线,保留执行、交付、失败和缺少用户事实的记录。
9
9
  - 协调员推进既定 DAG、解释中间结果、处理澄清并组织交付;领域分析和工具操作交给执行端。
10
10
  - 缺少用户事实时可持久等待,回复后续接原运行,不丢失已接受工作。
11
- - 提供本机 Web 控制台,管理配置、查看任务,并只控制任务的未来触发;配置保存后手动重启生效。
11
+ - 提供本机 Web 控制台,管理配置、查看任务,并只控制任务的未来触发;配置保存后通过 Web 或 CLI 重启生效,支持前台及托管服务;切换数据目录不搬迁或删除旧历史。
12
12
 
13
- Pi Agent、Codex、OpenCode 是 ACP 集成目标,不是嵌入本服务的领域执行器。声明支持、代码实现和真实验收必须区分。
13
+ Pi Agent、Codex、Claude、OpenCode 是 ACP 集成目标,不是嵌入本服务的领域执行器。声明支持、代码实现和真实验收必须区分。
14
14
 
15
15
  ## 架构选择
16
16
 
package/docs/release.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # npm 发布
2
2
 
3
- 目标包为 [@realqiyan/agent-task](https://www.npmjs.com/package/@realqiyan/agent-task),首个候选版本 **0.0.1-rc.1**,发布账号 **realqiyan**,许可证 [MIT](../LICENSE)。该候选版有意使用 **latest**,因此默认 `npm install -g @realqiyan/agent-task` 会安装它;这不表示已达到稳定或生产可用状态。实际完成情况见[验证说明](validation.md#npm-发布候选验证)。
3
+ npm 包为 [@realqiyan/agent-task](https://www.npmjs.com/package/@realqiyan/agent-task),发布账号 **realqiyan**,许可证 [MIT](../LICENSE)。候选版有意使用 **latest**,因此默认 `npm install -g @realqiyan/agent-task` 会安装最新候选版;这不表示已达到稳定或生产可用状态。实际完成情况见[验证说明](validation.md#npm-发布候选验证)。
4
4
 
5
5
  版本、引擎、公开文件和 registry 设置以 [package.json](../package.json) 为准。[打包门禁](../scripts/pack-release.mjs)检查实际包清单;消费者安装不运行构建脚本。不要发布工作目录、私有环境、SQLite、运行日志、模型会话或测试证据。
6
6
 
@@ -12,7 +12,7 @@ npm run check
12
12
  npm test
13
13
  git diff --check
14
14
  npm run pack:release
15
- npm run test:install -- ./realqiyan-agent-task-0.0.1-rc.1.tgz --service
15
+ npm run test:install -- ./realqiyan-agent-task-0.0.1-rc.2.tgz --service
16
16
  ```
17
17
 
18
18
  安装验证在独立临时目录执行真正的全局 npm 安装,禁用 lifecycle scripts,不使用项目源码或编译器;再配置、前台启动、调用 Web 配置接口、重启、运行用户服务、进行认证健康检查并卸载。验证脚本不调用模型或启动 ACP 工作。仅 `--service` 会触及真实用户服务管理器,遇到已有同名服务会拒绝,不覆盖其他安装。
@@ -23,7 +23,7 @@ npm run test:install -- ./realqiyan-agent-task-0.0.1-rc.1.tgz --service
23
23
 
24
24
  ## GitHub Actions 门禁与受控发布
25
25
 
26
- - [Validate](../.github/workflows/ci.yml):PR/master 执行 TypeScript 检查、基础测试及构建;检查包清单。Linux 和 macOS 安装任务**不 checkout 项目源码**,只接收 tarball 和验收脚本,要求真实服务管理器及认证检查通过。
26
+ - [Validate](../.github/workflows/ci.yml):PR/master 或手动触发时执行 TypeScript 检查、基础测试及构建;检查包清单。Linux 和 macOS 安装任务**不 checkout 项目源码**,只接收 tarball 和验收脚本,要求真实服务管理器及认证检查通过。
27
27
  - [Publish npm](../.github/workflows/publish.yml):仅手动触发,限制 master 和账号 realqiyan,输入须匹配已验收包版本。再次执行全部门禁,再发布同一 tarball,不重建、不执行包脚本。发布后核查 latest 并执行默认全局安装验证。
28
28
  - 普通 CI 没有 npm 凭据。发布作业单独获得凭据及 provenance 所需权限。并发发布被串行化。
29
29
 
@@ -31,21 +31,21 @@ npm run test:install -- ./realqiyan-agent-task-0.0.1-rc.1.tgz --service
31
31
  1. 配置必要审核人及仅允许 master 部署。若要禁止发起人自审,需要另一个审核人。
32
32
  2. 在该 Environment 中添加 **NPM_TOKEN**,使用 realqiyan 的最小发布权限、短有效期 token,允许直接发布(publish and stage),并满足 npm 的 2FA/自动发布策略。不要放入仓库文件或普通 CI 环境。
33
33
  3. 核查分支保护及工作流修改权限。工作流中的 environment 名称本身**不自动创建审核保护**。
34
- 4. 将代码审查后提交到 master,再在 Actions 选择 Publish npm,输入 `0.0.1-rc.1`。包尚不存在时,应确认账号允许首次创建此名称;认证/权限失败须停止,不能换账号或跳过验证。
34
+ 4. 将代码审查后提交到 master,再在 Actions 选择 Publish npm,输入已验收的版本(本次为 `0.0.1-rc.2`)。包尚不存在时,应确认账号允许首次创建此名称;认证/权限失败须停止,不能换账号或跳过验证。
35
35
 
36
36
  发布命令明确传 `--tag latest --access public --provenance`。provenance 依赖 GitHub OIDC,不代替 npm 发布授权。当前流程要求 Environment token 并检查 `npm whoami` 为 realqiyan;不宣称已经配置 npm trusted publishing。
37
37
 
38
- ## 经授权的本机首次发布
38
+ ## 经授权的本机发布
39
39
 
40
- 若由维护者明确批准在本机首次发布,须先完成同样的候选验收;这条路径没有 GitHub provenance。通过 npm 官方交互登录,不通过聊天、命令参数或 Git 传递 token:
40
+ 若由维护者明确批准在本机发布,须先完成同样的候选验收;这条路径没有 GitHub provenance。通过 npm 官方交互登录,不通过聊天、命令参数或 Git 传递 token:
41
41
 
42
42
  ```bash
43
43
  npm login --registry=https://registry.npmjs.org
44
44
  npm whoami --registry=https://registry.npmjs.org
45
45
  # 必须确认输出 realqiyan,再发布已检查的 tarball:
46
- npm publish ./realqiyan-agent-task-0.0.1-rc.1.tgz --ignore-scripts \
46
+ npm publish ./realqiyan-agent-task-0.0.1-rc.2.tgz --ignore-scripts \
47
47
  --access public --tag latest --registry=https://registry.npmjs.org
48
- npm view @realqiyan/agent-task@0.0.1-rc.1 version license --registry=https://registry.npmjs.org
48
+ npm view @realqiyan/agent-task@0.0.1-rc.2 version license --registry=https://registry.npmjs.org
49
49
  npm view @realqiyan/agent-task dist-tags --json --registry=https://registry.npmjs.org
50
50
  node scripts/verify-install.mjs --registry --service
51
51
  ```
@@ -6,17 +6,17 @@
6
6
 
7
7
  | 问题 | 代码入口 | 基础验证 |
8
8
  | --- | --- | --- |
9
- | 服务如何装配、启动和关闭 | [main.ts](../src/main.ts)、[lock.ts](../src/lock.ts) | [service.test.mjs](../tests/service.test.mjs)、[results-admin.test.mjs](../tests/results-admin.test.mjs) |
9
+ | 服务如何装配、启动和关闭 | [main.ts](../src/main.ts)、[launcher.ts](../src/launcher.ts)、[lock.ts](../src/lock.ts) | [restart.test.mjs](../tests/restart.test.mjs)、[service.test.mjs](../tests/service.test.mjs) |
10
10
  | MCP 有哪些工具、输入是什么 | [mcp.ts](../src/mcp.ts)、[task-contract.ts](../src/task-contract.ts) | [service.test.mjs](../tests/service.test.mjs) |
11
11
  | Task、Run、节点的数据与推进规则 | [task-model.ts](../src/task-model.ts)、[tasks.ts](../src/tasks.ts) | [tasks.test.mjs](../tests/tasks.test.mjs) |
12
12
  | 人工等待、回复及续接 | [human-input.ts](../src/human-input.ts) | [human-input.test.mjs](../tests/human-input.test.mjs) |
13
13
  | durable 工具与模型接入 | [coordinator.ts](../src/coordinator.ts)、[coordinator-provider.ts](../src/coordinator-provider.ts) | [coordinator-provider.test.mjs](../tests/coordinator-provider.test.mjs) |
14
14
  | 持久数据校验和提交 | [task-storage.ts](../src/task-storage.ts)、[coordinator-storage.ts](../src/coordinator-storage.ts) | [durable.test.mjs](../tests/durable.test.mjs)、[tasks.test.mjs](../tests/tasks.test.mjs) |
15
15
  | ACP、worker 和进程关闭 | [acp.ts](../src/acp.ts)、[node-execution.ts](../src/node-execution.ts)、[worker.ts](../src/worker.ts) | [execution.test.mjs](../tests/execution.test.mjs)、[tasks.test.mjs](../tests/tasks.test.mjs) |
16
- | 审批、环境与结果文件范围 | [permissions.ts](../src/permissions.ts)、[config.ts](../src/config.ts)、[results.ts](../src/results.ts) | [policy.test.mjs](../tests/policy.test.mjs)、[results-admin.test.mjs](../tests/results-admin.test.mjs) |
16
+ | 审批、环境与结果文件范围 | [permissions.ts](../src/permissions.ts)、[executor-runtime.ts](../src/executor-runtime.ts)、[results.ts](../src/results.ts) | [policy.test.mjs](../tests/policy.test.mjs)、[results-admin.test.mjs](../tests/results-admin.test.mjs) |
17
17
  | 配置、管理员注册和工作空间 | [config.ts](../src/config.ts)、[model.ts](../src/model.ts)、[admin.ts](../src/admin.ts)、[workspace.ts](../src/workspace.ts) | [config-preflight.test.mjs](../tests/config-preflight.test.mjs)、[tasks.test.mjs](../tests/tasks.test.mjs) |
18
18
  | 控制台 HTTP 边界与页面 | [web.ts](../src/web.ts)、[http-auth.ts](../src/http-auth.ts)、[页面模块](../src/web/) | [web.test.mjs](../tests/web.test.mjs) |
19
- | 配置和凭据的校验、保存与下次启动 | [settings.ts](../src/settings.ts)、[settings-contract.ts](../src/settings-contract.ts)、[private-files.ts](../src/private-files.ts)、[environment.ts](../src/environment.ts) | [settings.test.mjs](../tests/settings.test.mjs)、[web.test.mjs](../tests/web.test.mjs) |
19
+ | 配置和凭据的校验、保存与下次启动 | [settings.ts](../src/settings.ts)、[settings-contract.ts](../src/settings-contract.ts)、[startup.ts](../src/startup.ts)、[private-files.ts](../src/private-files.ts)、[environment.ts](../src/environment.ts) | [settings.test.mjs](../tests/settings.test.mjs)、[web.test.mjs](../tests/web.test.mjs) |
20
20
  | npm 命令、安装、启动环境与卸载 | [agent-task.mjs](../bin/agent-task.mjs)、[setup-wizard.mjs](../scripts/setup-wizard.mjs)、[setup.mjs](../scripts/setup.mjs)、[install-lib.mjs](../scripts/install-lib.mjs)、[service-runner.mjs](../scripts/service-runner.mjs)、[uninstall.mjs](../scripts/uninstall.mjs) | [install.test.mjs](../tests/install.test.mjs)、[setup.test.mjs](../tests/setup.test.mjs)、[verify-install.mjs](../scripts/verify-install.mjs) |
21
21
  | npm 包清单与发布门禁 | [package.json](../package.json)、[pack-release.mjs](../scripts/pack-release.mjs)、[工作流](../.github/workflows/) | [发布说明](release.md) |
22
22
 
@@ -14,12 +14,18 @@ git diff --check
14
14
 
15
15
  ## 控制台验证
16
16
 
17
- [HTTP 用例](../tests/web.test.mjs)使用真实本机服务进程验证独立管理员认证、Host/Origin/请求类型边界、有限任务写入、密钥不回显或进入日志,以及保存前后和重启后的配置差异。实际任务记录来自业务服务,不使用假模型输出。
17
+ [HTTP 用例](../tests/web.test.mjs)使用真实本机服务进程验证独立管理员认证、Host/Origin/请求类型边界、有限任务写入、密钥不回显或进入日志,以及 Web/CLI 重启后的配置差异和目录切换不复制历史。[重启用例](../tests/restart.test.mjs)验证前台 CLI、固定端口进程更换、绑定失败不能报成功,以及持久化的不确定派发在重复重启后仍被阻断。实际任务记录来自业务服务,不使用假模型输出。
18
18
 
19
- [配置用例](../tests/settings.test.mjs)验证私有文件边界、旧编辑拒绝、显式凭据环境、端点与密钥引用分离、失败时保留文件,以及关闭后拒绝操作。这些用例不构成断电、磁盘满或目录同步失败窗口的完整证据。
19
+ [配置用例](../tests/settings.test.mjs)验证私有文件边界、旧编辑拒绝、显式凭据环境、端点与密钥引用分离、服务令牌别名不能作为模型凭据、失败时保留文件,以及关闭后拒绝操作。这些用例不构成断电、磁盘满或目录同步失败窗口的完整证据。
20
20
 
21
21
  尚未完成真实浏览器交互、CSP 执行和跨浏览器缓存/自动填充验收。需在浏览器核对:不可信任务文本不执行脚本或加载外链;离开、刷新和退出后无令牌残留;配置草稿不被轮询覆盖;密钥保存与配置保存的提示明确。HTTP 头检查不能替代这些验证,也不证明模型认证或 ACP 执行成功。
22
22
 
23
+ ### 本次启动与注册调整
24
+
25
+ Linux x64、Node 24.15.0:35 项基础测试、TypeScript 检查及 npm 包清单门禁通过。使用本次本地 tarball 执行 `node scripts/verify-install.mjs realqiyan-agent-task-0.0.1-rc.1.tgz --service`,前台和真实 systemd 用户服务的 Web/CLI 重启、认证健康检查、后台数据目录/端口/token 变量名切换及卸载保留数据均通过。未调用模型或启动 ACP 任务,未发现验证凭据进入捕获输出。报告:`/tmp/agent-task-npm-8kUj45/report.json`。
26
+
27
+ 本次尚未在 macOS 复验 launchd,历史 macOS 安装证据不代表新启动器已验收。四类执行端的固定命令映射和凭据过滤有本地契约测试;Claude ACP 新增注册目标没有真实握手、认证或任务证据。其他执行端的历史结果也不能代替新原生环境和审批策略下的真实复验。
28
+
23
29
  ## 真实模型验收入口
24
30
 
25
31
  仅在获准环境显式运行,会产生模型费用;不使用个人凭据或真实交易:
@@ -67,11 +73,11 @@ Linux x64、Node 24.15.0 的本机记录:
67
73
 
68
74
  证据目录:`/tmp/agent-task-deploy-verified`、`/tmp/agent-task-deploy-final`;工作空间:`/tmp/agent-task-deploy-workspace`;空认证 HOME:`/tmp/agent-task-deploy-native-home`。临时文件可能被系统清理,长期留存需管理员安全归档。
69
75
 
70
- macOS 已有 launchd 实现,但没有安装、后台运行、任务执行或卸载的实机证据。待验收重点是原生认证与权限、关闭终端后的任务存续、取消及进程清理、重复卸载的数据保留;模板生成和 Linux 测试不能替代。
76
+ macOS 的 npm 安装、LaunchAgent 启停及卸载保留数据已在 GitHub macOS ARM64 runner 验证,见下文。原生认证与权限、真实任务、终端关闭/用户注销后的存续,以及执行端取消和进程清理仍未验收;CI 服务启动不能替代这些证据。
71
77
 
72
78
  ## npm 发布候选验证
73
79
 
74
- 2026-10-22,候选包 `@realqiyan/agent-task@0.0.1-rc.1`。本节记录打包前验收;发布后结果以[仓库中的最新记录](https://github.com/realqiyan/agent-task/blob/master/docs/validation.md#npm-发布候选验证)及 npmjs 查询为准,不以包内文档判断实时 registry 状态。发布前已确认 npm 账号为 `realqiyan`;不能把本地打包或 publish dry-run 当作发布完成。
80
+ 2026-10-22,`realqiyan` 已将 **[@realqiyan/agent-task@0.0.1-rc.1](https://www.npmjs.com/package/@realqiyan/agent-task/v/0.0.1-rc.1)** 发布到 npmjs。匿名 registry 查询确认版本、发布账号、MIT 许可证及 `latest=0.0.1-rc.1`。本次经授权从本机发布,没有 GitHub provenance。包内文档是打包时快照,后续证据以[仓库中的最新记录](https://github.com/realqiyan/agent-task/blob/master/docs/validation.md#npm-发布候选验证)为准。
75
81
 
76
82
  npm 因包名保护拒绝了未加 scope 的 `agent-task`:名称被判定与 `agent-base` 过于相似。维护者已明确批准使用 `@realqiyan/agent-task`,安装命令随之改为 `npm install -g @realqiyan/agent-task`,CLI 仍为 `agent-task`。未关闭账号 2FA,也未上传本机凭据。
77
83
 
@@ -79,13 +85,15 @@ npm 因包名保护拒绝了未加 scope 的 `agent-task`:名称被判定与 `
79
85
 
80
86
  - Node 22.19.0 和 24.15.0 的 TypeScript 检查、33 项基础测试及构建均通过;真实 PTY 用例验证 setup 的确认/取消、生成独立 token 和不输出凭据。
81
87
  - [包清单门禁](../scripts/pack-release.mjs)通过:仅包含预编译 JS、Web 资源、明确列出的操作脚本、示例及公开文档;不包含源码、开发工具、凭据文件、运行数据库或本地记录。
82
- - Node 22.19.0 从 tarball 全局安装到独立临时 prefix,禁止 lifecycle scripts,不使用项目源码或 TypeScript。前台启动、Web HTTP 配置保存/重启、真实 systemd 用户服务及带认证的健康检查通过。
88
+ - Node 22.19.0 先验收 scoped tarball,再从 npmjs 按 `npm install -g @realqiyan/agent-task` 的默认版本选择全局安装到独立临时 prefix;没有指定版本或使用本地 tarball 替代 registry 安装。禁用 lifecycle scripts,不使用项目源码或 TypeScript。前台启动、Web HTTP 配置保存/重启、真实 systemd 用户服务及带认证的健康检查均通过。
83
89
  - 重复 setup 保留 token;重复卸载保留配置、凭据、任务库和工作空间。显式注册的执行端仍未授权,没有 ACP 工作或模型调用。捕获的命令、前台输出及用户服务日志未出现验证用凭据,测试服务已移除。
84
- - 依赖安装使用 `https://registry.npmmirror.com` 和 npm 下载缓存。npmjs 直连安装曾分别在 5 分钟、15 分钟限时内未完成依赖下载;不据此推断包损坏,也不宣称官方 registry 安装已通过。
90
+ - tarball 预验收使用 npm 镜像依赖;发布后验收固定使用 `https://registry.npmjs.org/`,通过独立空 npm 配置文件进行匿名安装。复用 npm 下载缓存,没有复制项目 node_modules;不构成空缓存下载时延的保证。
91
+
92
+ Linux 22.19.0 的发布后 registry 安装报告:`/tmp/agent-task-npm-uM8FT5/report.json`;scoped tarball 预验收报告:`/tmp/agent-task-npm-Zgkggc/report.json`。临时目录包含验证专用随机凭据和空业务库,不上传或打包;报告仅含结果。Node 22.19.0 会输出内置 SQLite 的 experimental warning,这不是认证错误。
85
93
 
86
- 完整 Linux 22.19.0 报告:`/tmp/agent-task-0.0.1-rc.1-linux-node22-report.json`。Linux 24.15.0 的独立前台验收报告:`/tmp/agent-task-npm-CrtOcO/report.json`(未测试后台)。临时目录包含验证专用随机凭据和空业务库,不上传或打包;报告仅含结果。Node 22.19.0 会输出内置 SQLite 的 experimental warning,这不是认证错误。
94
+ 同日,[GitHub Validate 运行 39813423968](https://github.com/realqiyan/agent-task/actions/runs/39813423968)在提交 `461a1b0` 上全部通过:Node 22.19.0/24.x 检查、基础测试、构建及包清单门禁;Ubuntu x64 与 macOS ARM64 的 Node 22.19.0 安装任务均不 checkout 源码,使用 CI tarball 完成前台、Web HTTP 配置、真实 systemd/LaunchAgent、认证健康检查和卸载保留数据验收。报告确认输出无验证凭据,没有模型或 ACP 工作。macOS 的证据来自 hosted runner,不包括 Intel Mac、交互式终端/浏览器、休眠或用户注销场景。
87
95
 
88
- 已添加 [CI](../.github/workflows/ci.yml) 和[受控发布工作流](../.github/workflows/publish.yml),但本次尚未提交/推送工作流、在 GitHub 执行、配置发布 Environment 审核或注入 npm 发布凭据。工作流定义不是实际运行证据。macOS LaunchAgent、真实浏览器交互、默认 `npm install -g @realqiyan/agent-task` 及 npmjs 目标版本仍待验收;不得标记完成。
96
+ [受控发布工作流](../.github/workflows/publish.yml)已推送,但发布 Environment 审核和 npm 发布 secret 尚未配置,发布作业没有实际执行;本机首发和 Validate 通过不等于 Actions 发布已经验收。
89
97
 
90
98
  操作和发布条件见[部署说明](deployment.md)、[发布说明](release.md)。此前源码安装的历史部署证据不替代本次 npm 候选包验收。
91
99
 
@@ -94,7 +102,7 @@ npm 因包名保护拒绝了未加 scope 的 `agent-task`:名称被判定与 `
94
102
  - 独立单节点用例在当前接口上的真实模型复验、复杂任务的语义验收、并行分支与汇合、不同执行端之间的交付。
95
103
  - 各执行端原生权限、认证续期、沙箱、技能/MCP 发现及文件访问;其他模型和 OpenAI 兼容端点的协议差异。
96
104
  - 真实模型崩溃窗口、重复收费和预算行为;人工等待期间保留真实 ACP 会话的续答、并发回复、长时间等待及重启。
97
- - 系统级强杀、断电、业务库磁盘满、Linux 注销/重启与 cgroup 清理,以及 macOS 部署和监督。
105
+ - 系统级强杀、断电、业务库磁盘满、Linux 注销/重启与 cgroup 清理,以及 macOS 真实任务的长期监督和原生权限。
98
106
  - SDK 升级、历史数据转换,以及执行中修改计划等当前未开放能力。
99
107
 
100
108
  实现和操作入口见[代码导航](technical-design.md)、[部署说明](deployment.md)。
@@ -4,18 +4,11 @@
4
4
 
5
5
  ## 启用与连接
6
6
 
7
- 新安装由 `agent-task setup`([实现](../scripts/setup.mjs)) 在私有 `environment.json` 中生成独立的 `AGENT_TASK_ADMIN_TOKEN`。用本地编辑器读取该字段并在连接表单输入;它不是 MCP token,不能放进 URL、命令参数、截图或日志。
7
+ 新安装由 `agent-task setup`([实现](../scripts/setup.mjs))在当前 `dataDir/environment.json` 中生成独立的 `AGENT_TASK_ADMIN_TOKEN`。用本地编辑器读取该字段并在连接表单输入;它不是 MCP token,不能放进 URL、命令参数、截图或日志。
8
8
 
9
- 已有文件不会由 setup 补写或覆盖。缺少管理员令牌时,管理接口关闭。需要启用时先停服,用本地密码管理器生成独立随机令牌,离线加入该字段;令牌约束见 [main.ts](../src/main.ts)。不要复用服务或模型密钥。
9
+ 已有文件不会由 setup 补写或覆盖。缺少管理员令牌时,管理接口关闭。需要启用时先停服,用本地密码管理器生成独立随机令牌,离线加入该字段;令牌约束见 [environment.ts](../src/environment.ts)。不要复用服务或模型密钥。
10
10
 
11
- 托管服务已绑定配置和环境文件。手动部署使用:
12
-
13
- ```bash
14
- agent-task start --config /absolute/private/config.json \
15
- --environment /absolute/private/environment.json
16
- ```
17
-
18
- 两个文件须属于当前用户、权限为 `0600`,所在目录须为规范绝对路径、权限为 `0700`,不接受符号链接。环境文件仅在进程启动时加载;显式文件模式不回退到 shell 凭据。文件格式和基础环境来源见 [environment.ts](../src/environment.ts)、[install-lib.mjs](../scripts/install-lib.mjs)。
11
+ 前台和托管服务使用同一固定启动入口,见[部署说明](deployment.md)。配置和环境文件须属于当前用户、权限为 `0600`,所在目录须为规范绝对路径、权限为 `0700`,不接受符号链接。环境文件仅在运行进程启动时加载。文件格式见 [environment.ts](../src/environment.ts)。
19
12
 
20
13
  令牌仅保留在页面内存中;退出、离开或刷新后重新连接。不设置 Cookie,不写浏览器持久存储。配置和密钥文件仍是本地明文,须按凭据保护;此方案不隔离同一 OS 用户的其他进程或恶意浏览器扩展。
21
14
 
@@ -23,19 +16,20 @@ agent-task start --config /absolute/private/config.json \
23
16
 
24
17
  - 任务列表、固定 DAG、运行历史、节点进度、已接受交付、错误和问题只读。触发计划结束不等于运行成功,原始输出不等于交付。
25
18
  - 任务写操作仅复用 `task_update`,暂停、恢复或取消未来触发。不停止当前运行,不补跑错过的触发,不回答问题或修改 DAG。
26
- - 注册信息、ACP 权限和协调模型配置可编辑;数据目录、监听端口和服务认证配置只读。执行端原生认证及权限继续使用其官方工具。
19
+ - 数据目录、监听端口、MCP token 环境变量名、执行端注册和协调模型可编辑,下次启动生效。修改 token 变量名保留令牌值,不在页面回显。
20
+ - 执行端只选择 pi、codex、claude 或 opencode,保留 ID、默认工作空间和无人值守审核,并可填写备注。启动方式由系统决定;原生认证和权限继续用官方工具管理。没有命令、参数、环境变量清单、结果目录或 ACP 工具白名单配置。
27
21
 
28
- 页面与接口入口见 [web.ts](../src/web.ts)、[页面模块](../src/web/),任务契约见 [task-contract.ts](../src/task-contract.ts)。没有通用 MCP 代理、文件浏览器或重启接口。
22
+ 页面与接口入口见 [web.ts](../src/web.ts)、[页面模块](../src/web/),任务契约见 [task-contract.ts](../src/task-contract.ts)。提供受管理员认证保护的重启入口;没有通用 MCP 代理、文件浏览器或下载接口。固定结果目录不授予整个 dataDir 的读取能力。
29
23
 
30
24
  ## 保存与重启
31
25
 
32
26
  1. 密钥只显示是否设置,原值不回显。不提交即保留;替换和删除必须明确操作。先保存新密钥,再保存引用它的配置。新凭据命名和受保护变量见 [settings.ts](../src/settings.ts)。
33
- 2. 配置先校验并预览影响,再确认保存。校验不调用模型或启动 ACP,不能证明认证、原生权限或实际执行成功。修改执行端字段后,页面要求重新明确批准无人值守执行。
27
+ 2. 配置先校验并预览影响,再确认保存。校验不调用模型或启动 ACP,不能证明认证、原生权限或实际执行成功。修改执行端类型、ID 或工作空间后,页面要求重新明确批准无人值守执行;仅修改备注不改变授权。
34
28
  3. 配置与密钥分别原子替换,不是跨文件事务。更换模型端点须使用新的凭据引用,避免中途重启将新密钥交给旧端点。旧凭据不自动清理。
35
- 4. 保存不更新当前进程的配置、权限或环境。检查活动运行及待回答运行后,按[后台管理](deployment.md#后台管理)手动重启;故障后的自动拉起也会读取已保存值。
36
- 5. 重启后重新连接并核对运行配置。启动失败须从终端修正私有文件;提交结果不确定时停止继续保存并停服核查。不删除历史、自动回滚或重放执行来修复。
29
+ 4. 保存不热更新。检查活动运行及待回答运行后,点击“重启并检查可用性”或执行 `agent-task restart`;两者均支持前台和 systemd/launchd 托管实例。目录切换、原生环境来源及故障处理见[部署说明](deployment.md)。
30
+ 5. 只有新进程通过认证健康检查才显示成功。页面清除令牌并给出新地址,重新连接后核对运行配置。请求未确认时先用 CLI status 核查,不删除历史、自动回滚或重放执行来修复。
37
31
 
38
- 待回答运行也可能持有 ACP 会话,重启不保证无损续接。执行端注册信息与任务快照不一致时,旧任务的派发会被拒绝;页面显示受影响任务,但不改写快照、DAG 或执行端指派。需要新任务时由 MCP 调用方处理。
32
+ 待回答运行也可能持有 ACP 会话,重启不保证无损续接。执行端类型、工作空间或批准状态与任务快照不一致时,旧任务的派发会被拒绝;页面显示受影响任务,但不改写快照、DAG 或执行端指派。需要新任务时由 MCP 调用方处理。
39
33
 
40
34
  多个页面的旧编辑会被拒绝,不做自动合并。离线编辑配置须先停服。校验与保存职责见 [settings.ts](../src/settings.ts)、[settings-contract.ts](../src/settings-contract.ts)、[private-files.ts](../src/private-files.ts)。
41
35
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@realqiyan/agent-task",
3
- "version": "0.0.1-rc.1",
3
+ "version": "0.0.1-rc.2",
4
4
  "license": "MIT",
5
5
  "author": "realqiyan",
6
6
  "repository": {
@@ -46,7 +46,7 @@
46
46
  "test": "npm run build && node --test --test-concurrency=1 tests/*.test.mjs",
47
47
  "test:e2e": "npm run build && node --test --test-concurrency=1 tests/e2e/deepseek-acp.test.mjs",
48
48
  "test:e2e:human": "npm run build && node scripts/run-pi-human-e2e.mjs",
49
- "start": "node dist/main.js",
49
+ "start": "node bin/agent-task.mjs start",
50
50
  "setup": "node bin/agent-task.mjs setup",
51
51
  "prepack": "npm run build",
52
52
  "pack:release": "node scripts/pack-release.mjs",
@@ -5,6 +5,7 @@ import { dirname, join, resolve, isAbsolute, basename } from "node:path";
5
5
  import { homedir } from "node:os";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { setTimeout as delay } from "node:timers/promises";
8
+ import { controlService } from "../dist/launcher.js";
8
9
 
9
10
  export const packageRoot = realpathSync(fileURLToPath(new URL("..", import.meta.url)));
10
11
 
@@ -13,6 +14,7 @@ export const unit = "agent-task.service";
13
14
  export const packages = {
14
15
  pi: ["@earendil-works/pi-coding-agent@1.3.0", "pi-acp@0.0.34"],
15
16
  codex: ["@agentclientprotocol/codex-acp@2.2.2"],
17
+ claude: ["@zed-industries/claude-agent-acp@0.23.1"],
16
18
  opencode: ["opencode-ai@1.19.3"],
17
19
  };
18
20
  export function argumentsFor(argv) {
@@ -85,13 +87,8 @@ export function saveManifest(root, manifest) {
85
87
  }
86
88
  }
87
89
  export function runtimeBaseEnv(manifest) {
88
- // Do not inherit shell/service-manager credentials, loader hooks or executor overrides.
89
- return { HOME: manifest.home, PATH: [join(manifest.root, "components/node_modules/.bin"), dirname(manifest.node), "/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"].join(":"), LANG: "en_US.UTF-8" };
90
- }
91
- export function runtimeEnv(manifest) {
92
- const values = json(join(manifest.root, "environment.json"));
93
- if (!values || typeof values !== "object" || Array.isArray(values) || Object.entries(values).some(([key, value]) => !/^[A-Za-z_][A-Za-z0-9_]*$/.test(key) || ["__proto__", "constructor", "prototype"].includes(key) || typeof value !== "string" || value.includes("\0"))) throw new Error("Invalid environment.json");
94
- return { ...runtimeBaseEnv(manifest), ...values };
90
+ // Capture the OS user environment; the runtime isolates private service values before worker creation.
91
+ return { ...process.env, HOME: manifest.home, PATH: [join(manifest.root, "components/node_modules/.bin"), dirname(manifest.node), process.env.PATH ?? "", "/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"].join(":"), LANG: process.env.LANG ?? "en_US.UTF-8" };
95
92
  }
96
93
  const xml = value => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
97
94
  const systemdQuote = value => '"' + value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("%", "%%").replaceAll("$", "$$") + '"';
@@ -99,33 +96,24 @@ export function serviceDefinition(manifest) {
99
96
  const runner = join(manifest.app, "scripts/service-runner.mjs");
100
97
  if (manifest.platform === "linux") return {
101
98
  path: join(manifest.home, ".config/systemd/user", unit),
102
- text: `[Unit]\nDescription=Agent Task (managed by setup)\nAfter=network.target\n\n[Service]\nType=simple\nExecStart=${[manifest.node, runner, manifest.root].map(systemdQuote).join(" ")}\nRestart=on-failure\nRestartSec=5\nTimeoutStopSec=20\nKillMode=control-group\nUMask=0077\n\n[Install]\nWantedBy=default.target\n`,
99
+ text: `[Unit]\nDescription=Agent Task (managed by setup)\nAfter=network.target\n\n[Service]\nType=simple\nExecStart=${[manifest.node, runner, manifest.root].map(systemdQuote).join(" ")}\nRestart=on-failure\nRestartSec=5\nTimeoutStopSec=45\nKillMode=control-group\nUMask=0077\n\n[Install]\nWantedBy=default.target\n`,
103
100
  };
104
101
  return { path: join(manifest.home, "Library/LaunchAgents", label + ".plist"), text:
105
- `<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0"><dict>\n<key>Label</key><string>${label}</string>\n<key>ProgramArguments</key><array>${[manifest.node, runner, manifest.root].map(value => `<string>${xml(value)}</string>`).join("")}</array>\n<key>RunAtLoad</key><true/>\n<key>KeepAlive</key><dict><key>SuccessfulExit</key><false/></dict>\n<key>ThrottleInterval</key><integer>5</integer>\n<key>ExitTimeOut</key><integer>20</integer>\n<key>AbandonProcessGroup</key><false/>\n<key>Umask</key><integer>63</integer>\n<key>StandardOutPath</key><string>${xml(join(manifest.root, "service.log"))}</string>\n<key>StandardErrorPath</key><string>${xml(join(manifest.root, "service-error.log"))}</string>\n</dict></plist>\n` };
102
+ `<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0"><dict>\n<key>Label</key><string>${label}</string>\n<key>ProgramArguments</key><array>${[manifest.node, runner, manifest.root].map(value => `<string>${xml(value)}</string>`).join("")}</array>\n<key>RunAtLoad</key><true/>\n<key>KeepAlive</key><dict><key>SuccessfulExit</key><false/></dict>\n<key>ThrottleInterval</key><integer>5</integer>\n<key>ExitTimeOut</key><integer>45</integer>\n<key>AbandonProcessGroup</key><false/>\n<key>Umask</key><integer>63</integer>\n</dict></plist>\n` };
106
103
  }
107
104
  export function serviceEnv() {
108
105
  const keys = ["HOME", "PATH", "LANG", "LC_ALL", "DBUS_SESSION_BUS_ADDRESS"];
109
106
  return { ...Object.fromEntries(keys.filter(key => process.env[key] !== undefined).map(key => [key, process.env[key]])),
110
107
  XDG_RUNTIME_DIR: process.env.XDG_RUNTIME_DIR ?? `/run/user/${process.getuid()}` };
111
108
  }
112
- export async function checkHealth(config, env, waitMs = 5000) {
113
- const token = env[config.tokenEnv ?? "AGENT_TASK_TOKEN"];
114
- if (!token || token.length < 32) throw new Error("A valid service token is required for the health check");
109
+ export async function checkHealth(home, waitMs = 5000) {
115
110
  const deadline = Date.now() + waitMs;
116
111
  do {
117
- try {
118
- const response = await fetch(`http://127.0.0.1:${config.port}/mcp`, {
119
- method: "POST", headers: { Authorization: "Bearer " + token, "Content-Type": "application/json", Accept: "application/json, text/event-stream" },
120
- body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "service_status", arguments: {} } }),
121
- signal: AbortSignal.timeout(1000),
122
- });
123
- const body = await response.json();
124
- if (response.ok && body.result?.structuredContent?.ok && body.result.structuredContent.data?.running) return;
125
- } catch { /* Startup can take several seconds; never log the authenticated request. */ }
112
+ try { return await controlService(home, "status"); }
113
+ catch { /* The launcher may still be starting; never log credentials. */ }
126
114
  await delay(200);
127
115
  } while (Date.now() < deadline);
128
- throw new Error("Authenticated MCP health check failed; inspect the service manager and private configuration");
116
+ throw new Error("Authenticated MCP health check failed; inspect private logs and startup files");
129
117
  }
130
118
  export function verifyServiceFile(manifest) {
131
119
  const definition = serviceDefinition(manifest);
@@ -150,7 +138,7 @@ export function activate(manifest) {
150
138
  run("launchctl", ["print", domain + "/" + label]);
151
139
  }
152
140
  }
153
- export function deactivate(manifest) {
141
+ export async function deactivate(manifest) {
154
142
  const definition = verifyServiceFile(manifest);
155
143
  if (!existsSync(definition.path)) {
156
144
  if (manifest.serviceInstalled) throw new Error("Managed service file is missing; verify it is unloaded before cleanup");
@@ -165,7 +153,12 @@ export function deactivate(manifest) {
165
153
  const target = `gui/${process.getuid()}/${label}`;
166
154
  const active = spawnSync("launchctl", ["print", target], { stdio: "ignore" });
167
155
  if (active.status === 0) run("launchctl", ["bootout", target]);
168
- if (spawnSync("launchctl", ["print", target], { stdio: "ignore" }).status === 0) throw new Error("LaunchAgent is still loaded");
156
+ // bootout can return while launchd still reports the job during graceful shutdown.
157
+ const deadline = Date.now() + 50_000;
158
+ while (spawnSync("launchctl", ["print", target], { stdio: "ignore" }).status === 0) {
159
+ if (Date.now() >= deadline) throw new Error("LaunchAgent is still loaded");
160
+ await delay(100);
161
+ }
169
162
  }
170
163
  unlinkSync(definition.path);
171
164
  if (manifest.platform === "linux") run("systemctl", ["--user", "daemon-reload"], { env: serviceEnv() });
@@ -2,7 +2,10 @@
2
2
  import { spawn } from "node:child_process";
3
3
  import { Readable, Writable } from "node:stream";
4
4
  import * as acp from "@agentclientprotocol/sdk";
5
- import { loadConfig, executorEnv } from "../dist/config.js";
5
+ import { loadConfig } from "../dist/config.js";
6
+ import { executorCommand, executorEnvironment } from "../dist/executor-runtime.js";
7
+ import { readEnvironment } from "../dist/environment.js";
8
+ import { join } from "node:path";
6
9
  import { terminateGroup } from "../dist/acp.js";
7
10
  import { version } from "../dist/version.js";
8
11
  const [file, id] = process.argv.slice(2);
@@ -10,8 +13,10 @@ if (!file || !id) { console.error("Usage: node scripts/probe-agent.mjs <config.j
10
13
  const config = loadConfig(file);
11
14
  const executor = config.executors.find(value => value.id === id);
12
15
  if (!executor) throw new Error("Executor not found");
13
- const child = spawn(executor.command, executor.args, {
14
- cwd: executor.cwd, env: executorEnv(executor.envKeys), detached: true, stdio: ["pipe", "pipe", "pipe"],
16
+ const env = executorEnvironment(process.env, readEnvironment(join(config.dataDir, "environment.json")), config);
17
+ const launch = executorCommand(executor.type, env);
18
+ const child = spawn(launch.command, launch.args, {
19
+ cwd: executor.cwd, env, detached: true, stdio: ["pipe", "pipe", "pipe"],
15
20
  });
16
21
  child.stderr.resume();
17
22
  let connection;