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

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 (88) hide show
  1. package/README.md +4 -3
  2. package/bin/agent-task.mjs +23 -36
  3. package/dist/{launcher.js → application/launcher.js} +19 -42
  4. package/dist/application/main.js +122 -0
  5. package/dist/{startup.js → application/startup.js} +6 -11
  6. package/dist/application/task-runtime.js +42 -0
  7. package/dist/configuration/contract.js +28 -0
  8. package/dist/{environment.js → configuration/environment.js} +1 -5
  9. package/dist/configuration/files.js +43 -0
  10. package/dist/configuration/management.js +6 -0
  11. package/dist/configuration/model.js +2 -0
  12. package/dist/configuration/policy.js +29 -0
  13. package/dist/configuration/ports.js +2 -0
  14. package/dist/{settings.js → configuration/settings.js} +77 -39
  15. package/dist/executors/environment.js +10 -0
  16. package/dist/executors/model.js +2 -0
  17. package/dist/executors/registry.js +29 -0
  18. package/dist/executors/validation.js +10 -0
  19. package/dist/{lock.js → infrastructure/lock.js} +2 -2
  20. package/dist/{private-files.js → infrastructure/private-files.js} +1 -7
  21. package/dist/infrastructure/sqlite.js +19 -0
  22. package/dist/infrastructure/version.js +3 -0
  23. package/dist/{acp.js → integrations/acp/connection.js} +9 -7
  24. package/dist/{node-execution.js → integrations/acp/execution.js} +1 -1
  25. package/dist/{executor-runtime.js → integrations/acp/runtime.js} +1 -10
  26. package/dist/{worker.js → integrations/acp/worker.js} +3 -2
  27. package/dist/{coordinator.js → integrations/coordinator/coordinator.js} +16 -7
  28. package/dist/{coordinator-provider.js → integrations/coordinator/provider.js} +1 -1
  29. package/dist/{coordinator-storage.js → integrations/coordinator/storage.js} +2 -2
  30. package/dist/integrations/local-service/control.js +33 -0
  31. package/dist/protocol/cli/admin-client.js +45 -0
  32. package/dist/protocol/cli/commands.js +394 -0
  33. package/dist/{http-auth.js → protocol/http/auth.js} +1 -1
  34. package/dist/protocol/http/server.js +111 -0
  35. package/dist/{web.js → protocol/http/web.js} +18 -17
  36. package/dist/{mcp.js → protocol/mcp.js} +13 -13
  37. package/dist/protocol/services.js +2 -0
  38. package/dist/protocol/settings-contract.js +40 -0
  39. package/dist/protocol/task-contract.js +28 -0
  40. package/dist/results/files.js +70 -0
  41. package/dist/results/ports.js +2 -0
  42. package/dist/results/service.js +42 -0
  43. package/dist/shared/errors.js +13 -0
  44. package/dist/shared/limits.js +3 -0
  45. package/dist/shared/time.js +2 -0
  46. package/dist/shared/validation.js +8 -0
  47. package/dist/tasks/definitions.js +83 -0
  48. package/dist/tasks/graph.js +23 -0
  49. package/dist/{human-input.js → tasks/human-input.js} +8 -7
  50. package/dist/tasks/model.js +2 -0
  51. package/dist/tasks/ports.js +2 -0
  52. package/dist/tasks/runs.js +263 -0
  53. package/dist/tasks/schedule.js +36 -0
  54. package/dist/tasks/sessions.js +62 -0
  55. package/dist/tasks/storage/records.js +45 -0
  56. package/dist/{task-storage.js → tasks/storage/store.js} +23 -36
  57. package/dist/tasks/supervisor.js +227 -0
  58. package/dist/tasks/validation.js +24 -0
  59. package/dist/web/settings-view.js +13 -6
  60. package/docs/architecture-guidelines.md +6 -4
  61. package/docs/cli.md +49 -0
  62. package/docs/deployment.md +16 -56
  63. package/docs/design-proposal.md +2 -2
  64. package/docs/release.md +14 -15
  65. package/docs/technical-design.md +47 -26
  66. package/docs/validation.md +49 -17
  67. package/docs/web-console.md +10 -8
  68. package/package.json +7 -7
  69. package/scripts/install-lib.mjs +4 -95
  70. package/scripts/mcp-call.mjs +1 -1
  71. package/scripts/probe-agent.mjs +6 -5
  72. package/scripts/service-runner.mjs +1 -1
  73. package/scripts/setup-wizard.mjs +1 -6
  74. package/scripts/setup.mjs +17 -30
  75. package/deploy/agent-task.service.example +0 -17
  76. package/dist/admin.js +0 -31
  77. package/dist/config.js +0 -72
  78. package/dist/main.js +0 -175
  79. package/dist/model.js +0 -25
  80. package/dist/results.js +0 -79
  81. package/dist/settings-contract.js +0 -16
  82. package/dist/task-contract.js +0 -37
  83. package/dist/task-model.js +0 -2
  84. package/dist/tasks.js +0 -508
  85. package/dist/version.js +0 -3
  86. package/scripts/uninstall.mjs +0 -57
  87. /package/dist/{permissions.js → executors/permissions.js} +0 -0
  88. /package/dist/{workspace.js → infrastructure/workspace.js} +0 -0
package/README.md CHANGED
@@ -8,8 +8,9 @@
8
8
 
9
9
  | 目的 | 入口 |
10
10
  | --- | --- |
11
- | 安装、认证、后台运行、卸载 | [部署说明](docs/deployment.md) |
11
+ | 安装、配置、运行 | [部署说明](docs/deployment.md) |
12
12
  | 本机控制台、配置保存与 Web/CLI 重启 | [控制台说明](docs/web-console.md) |
13
+ | 命令行配置、凭据和任务管理 | [CLI 说明](docs/cli.md) |
13
14
  | 理解产品目标和架构选择 | [总体方案](docs/design-proposal.md) |
14
15
  | 查找实现与对应测试 | [代码导航](docs/technical-design.md) |
15
16
  | 查询测试命令、真实验收及缺口 | [验证说明](docs/validation.md) |
@@ -32,7 +33,7 @@ agent-task status
32
33
  agent-task restart
33
34
  ```
34
35
 
35
- npm 包名为 `@realqiyan/agent-task`,安装后的命令仍为 `agent-task`。setup 提供交互配置;启动后可使用本机 Web 控制台。先停止前台实例,再用 `agent-task service install` 安装并启动用户后台服务。安装器不会替用户登录或批准执行端;详细操作见[部署说明](docs/deployment.md)。
36
+ npm 包名为 `@realqiyan/agent-task`,安装后的命令仍为 `agent-task`。setup 提供交互配置;启动后可使用本机 Web 控制台或 [CLI](docs/cli.md),两端共用管理服务。安装器不会替用户登录或批准执行端;详细操作见[部署说明](docs/deployment.md)。
36
37
 
37
38
  MCP 客户端使用独立服务 token。源码开发辅助命令(凭据须安全注入环境,不写命令行):
38
39
 
@@ -41,7 +42,7 @@ node scripts/mcp-call.mjs --list
41
42
  node scripts/mcp-call.mjs service_status
42
43
  ```
43
44
 
44
- 任务由 `task_create` 提交固定 DAG,每个节点选择已注册执行端。具体契约见 [task-contract.ts](src/task-contract.ts),管理工具见 [mcp.ts](src/mcp.ts)。Prompt、人工回复和工作空间选择都不授予工具权限。
45
+ 任务由 `task_create` 提交固定 DAG,每个节点选择已注册执行端。具体契约见 [task-contract.ts](src/protocol/task-contract.ts),管理工具见 [mcp.ts](src/protocol/mcp.ts)。Prompt、人工回复和工作空间选择都不授予工具权限。
45
46
 
46
47
  ## 许可证
47
48
 
@@ -4,16 +4,17 @@ import { readFileSync } from "node:fs";
4
4
  const help = `Usage: agent-task <command>
5
5
  setup [options] Interactive configuration (setup --help for flags)
6
6
  start [--home PATH] Foreground service using the private installation
7
- restart [--home PATH] Restart foreground or managed runtime; verify readiness
8
- status [--home PATH] Authenticated MCP health check; no token printed
9
- service install [--home PATH]
10
- Install/start systemd user service or LaunchAgent
11
- service remove [--home PATH]
12
- Stop/remove only the owned service
13
- uninstall [--home PATH] Remove service and owned executor components;
14
- preserve configuration, credentials and task data
7
+ restart [--home PATH] Restart runtime; verify readiness
8
+ status [--home PATH] Health, active work and pending configuration
9
+ config Show, edit, validate and apply configuration
10
+ executor Manage registrations and unattended authorization
11
+ coordinator Configure the coordination model
12
+ credential List, replace or delete private credentials
13
+ task Inspect tasks and control future triggers
14
+ run Inspect run history and deliveries
15
+ <command> --help Command usage; management supports --json
15
16
  --version | --help
16
- Node.js 22.19+; Linux or macOS. Setup never grants executor permissions.`;
17
+ Node.js 22.19+; Linux or macOS.`;
17
18
  try {
18
19
  const [major, minor] = process.versions.node.split(".").map(Number);
19
20
  if (major < 22 || (major === 22 && minor < 19)) throw new Error("Node.js 22.19+ is required");
@@ -22,40 +23,26 @@ try {
22
23
  console.log(help);
23
24
  } else if (command === "--version") {
24
25
  console.log(JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version);
25
- } else if (command === "setup" || command === "uninstall") {
26
+ } else if (command === "setup") {
26
27
  process.argv = [process.execPath, command, ...args];
27
28
  await import(`../scripts/${command}.mjs`);
28
29
  } else {
29
30
  const lib = await import("../scripts/install-lib.mjs");
30
- const action = command === "service" ? args.shift() : undefined;
31
- if (!["start", "status", "restart", "service"].includes(command) || (command === "service" && !["install", "remove"].includes(action))) throw new Error(help);
32
- const options = lib.argumentsFor(args);
33
- if (Object.keys(options).some(key => key !== "home")) throw new Error("Only --home is accepted here");
34
- const root = lib.installRoot(options);
35
- if (action === "install") {
36
- process.argv = [process.execPath, "setup", "--home", root, "--service"];
37
- await import("../scripts/setup.mjs");
38
- } else if (action === "remove") {
39
- lib.readManifest(root);
40
- const unlock = lib.operationLock(root);
41
- try {
42
- const manifest = lib.readManifest(root);
43
- await lib.deactivate(manifest);
44
- manifest.serviceInstalled = false;
45
- lib.saveManifest(root, manifest);
46
- console.log("Owned service removed; configuration, credentials, components and data preserved.");
47
- } finally { unlock(); }
48
- } else if (command === "start") {
49
- if (lib.readManifest(root).serviceInstalled) throw new Error("Remove the managed service before starting a foreground instance");
50
- process.argv = [process.execPath, "service-runner", root];
51
- await import("../scripts/service-runner.mjs");
31
+ if (command === "start") {
32
+ if (args.includes("--help")) { console.log("Usage: agent-task start [--home ABSOLUTE_DIR]"); }
33
+ else {
34
+ const options = lib.argumentsFor(args);
35
+ if (Object.keys(options).some(key => key !== "home")) throw new Error("Only --home is accepted here");
36
+ const root = lib.installRoot(options);
37
+ process.argv = [process.execPath, "service-runner", root];
38
+ await import("../scripts/service-runner.mjs");
39
+ }
52
40
  } else {
53
- const { controlService } = await import("../dist/launcher.js");
54
- const ready = await controlService(root, command);
55
- console.log(JSON.stringify({ event: command === "restart" ? "restarted" : "healthy", ...ready }));
41
+ const { runCli } = await import("../dist/protocol/cli/commands.js");
42
+ await runCli(command, args, home => lib.installRoot({ home }));
56
43
  }
57
44
  }
58
45
  } catch (error) {
59
- console.error(error.message);
46
+ console.error(process.argv.includes("--json") ? JSON.stringify({ error: error.message }) : error.message);
60
47
  process.exitCode = 1;
61
48
  }
@@ -1,13 +1,12 @@
1
1
  import { fork } from "node:child_process";
2
- import { createServer, request } from "node:http";
2
+ import { createServer } from "node:http";
3
3
  import { appendFileSync, chmodSync, closeSync, constants, existsSync, fstatSync, lstatSync, openSync, unlinkSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { randomUUID } from "node:crypto";
7
- import { acquireLauncherLock } from "./lock.js";
8
- import { prepareStartup, startupPaths } from "./startup.js";
9
- import { readEnvironment } from "./environment.js";
10
- import { loadConfig } from "./config.js";
7
+ import { acquireLauncherLock } from "../infrastructure/lock.js";
8
+ import { prepareStartup } from "./startup.js";
9
+ import { startupPaths } from "../configuration/files.js";
11
10
  function message(child, type, timeout = 30_000) {
12
11
  return new Promise((resolve, reject) => {
13
12
  const cleanup = () => { clearTimeout(timer); child.off("message", receive); child.off("exit", exit); child.off("error", fail); };
@@ -144,7 +143,7 @@ export async function launch(home, nativeEnvironment) {
144
143
  const value = (await message(child, "ready")).ready;
145
144
  await health(value, inputs.environment[inputs.config.tokenEnv]);
146
145
  current = child;
147
- ready = value;
146
+ ready = { info: value, token: inputs.environment[inputs.config.tokenEnv] };
148
147
  return value;
149
148
  }
150
149
  catch (error) {
@@ -155,28 +154,35 @@ export async function launch(home, nativeEnvironment) {
155
154
  const status = async () => {
156
155
  if (!ready || restarting || closing)
157
156
  throw new Error("Service unavailable");
158
- const paths = startupPaths(home), config = loadConfig(paths.configPath), env = readEnvironment(paths.environmentPath);
159
- await health(ready, env[config.tokenEnv]);
160
- return ready;
157
+ // Saved inputs may need repair. Health belongs to the running instance,
158
+ // including the credential with which that instance actually started.
159
+ const running = ready;
160
+ await health(running.info, running.token);
161
+ return running.info;
161
162
  };
162
163
  const restart = async (requester) => {
163
164
  if (restarting || closing)
164
165
  throw new Error("Restart already in progress or closed");
165
166
  restarting = true;
166
167
  const old = current;
167
- current = undefined;
168
- ready = undefined;
169
168
  try {
170
169
  if (old) {
171
170
  const drained = message(old, "drained");
172
171
  old.send({ type: "drain" });
172
+ let result;
173
173
  try {
174
- await drained;
174
+ result = await drained;
175
175
  }
176
176
  catch (error) {
177
+ current = undefined;
178
+ ready = undefined;
177
179
  await stop(old);
178
180
  throw error;
179
181
  }
182
+ if (result.error)
183
+ throw new Error(result.error);
184
+ current = undefined;
185
+ ready = undefined;
180
186
  // Keep only the authenticated Web response alive until the new process is verified.
181
187
  if (old !== requester)
182
188
  await stop(old);
@@ -186,7 +192,7 @@ export async function launch(home, nativeEnvironment) {
186
192
  }
187
193
  finally {
188
194
  restarting = false;
189
- if (requester) {
195
+ if (requester && requester !== current) {
190
196
  const timer = setTimeout(() => { void stop(requester); }, 5000);
191
197
  requester.once("close", () => clearTimeout(timer));
192
198
  }
@@ -206,33 +212,4 @@ export async function launch(home, nativeEnvironment) {
206
212
  process.off("SIGTERM", signal);
207
213
  }
208
214
  }
209
- /** Local administrator capability: the socket lives in the private launcher directory. */
210
- export function controlService(home, action) {
211
- startupPaths(home);
212
- return new Promise((resolve, reject) => {
213
- let timedOut = false;
214
- const req = request({ socketPath: join(home, "control.sock"), path: "/" + action, method: "POST" }, res => {
215
- let body = "";
216
- res.setEncoding("utf8");
217
- res.on("data", chunk => { body += chunk; });
218
- res.on("end", () => {
219
- if (res.statusCode !== 200) {
220
- reject(new Error("Service unavailable; inspect private logs and startup files"));
221
- return;
222
- }
223
- try {
224
- resolve(JSON.parse(body));
225
- }
226
- catch {
227
- reject(new Error("Invalid launcher response"));
228
- }
229
- });
230
- });
231
- req.setTimeout(60_000, () => { timedOut = true; req.destroy(); });
232
- req.once("error", () => reject(new Error(timedOut
233
- ? "Service control timed out; outcome is unknown, check status before another restart"
234
- : "Cannot contact launcher; start the foreground or managed service first")));
235
- req.end();
236
- });
237
- }
238
215
  //# sourceMappingURL=launcher.js.map
@@ -0,0 +1,122 @@
1
+ import { HttpServer } from "../protocol/http/server.js";
2
+ import { createTaskRuntime } from "./task-runtime.js";
3
+ import { LocalSettingsFiles, configurationPaths } from "../configuration/files.js";
4
+ import { resolve } from "node:path";
5
+ import { randomUUID } from "node:crypto";
6
+ import { loadConfig } from "../configuration/files.js";
7
+ import { baseEnvironment, validateServiceEnvironment } from "../configuration/environment.js";
8
+ import { readEnvironment } from "../configuration/files.js";
9
+ import { Settings } from "../configuration/settings.js";
10
+ import { acquireLock } from "../infrastructure/lock.js";
11
+ import { errorText } from "../shared/errors.js";
12
+ import { createExecution } from "../integrations/acp/execution.js";
13
+ import { executorEnvironment } from "../executors/environment.js";
14
+ import { createCoordinator } from "../integrations/coordinator/coordinator.js";
15
+ process.umask(0o077);
16
+ const args = process.argv.slice(2);
17
+ if (args[0] !== "--config" || args.length !== 4 || args[2] !== "--environment") {
18
+ console.error("Internal runtime requires --config PATH --environment PATH; use agent-task start");
19
+ process.exit(2);
20
+ }
21
+ const configPath = resolve(args[1]), environmentPath = resolve(args[3]);
22
+ const native = { ...process.env }, base = baseEnvironment(native);
23
+ const environment = readEnvironment(environmentPath);
24
+ const instanceId = process.env.AGENT_TASK_INSTANCE_ID ?? randomUUID();
25
+ for (const key of Object.keys(process.env))
26
+ delete process.env[key];
27
+ Object.assign(process.env, base, environment);
28
+ const config = loadConfig(configPath);
29
+ const { token, adminToken } = validateServiceEnvironment(config, environment);
30
+ let http;
31
+ let tasks, settings;
32
+ let release;
33
+ let shutting;
34
+ function shutdown(code, drain = false) {
35
+ process.exitCode = Math.max(process.exitCode ? Number(process.exitCode) : 0, code);
36
+ return shutting ??= (async () => {
37
+ http?.stopAccepting();
38
+ settings?.close();
39
+ try {
40
+ await tasks?.close();
41
+ }
42
+ catch (error) {
43
+ console.error(errorText(error));
44
+ process.exitCode = 1;
45
+ }
46
+ await http?.close(drain);
47
+ await release?.();
48
+ })().catch(error => { console.error(errorText(error)); process.exitCode = 1; });
49
+ }
50
+ const fatal = (error) => { console.error(errorText(error)); void shutdown(1).finally(() => { if (process.connected)
51
+ process.disconnect(); }); };
52
+ let restartPending = false;
53
+ const restart = async () => {
54
+ if (!process.connected || restartPending || shutting)
55
+ throw new Error("Restart unavailable");
56
+ restartPending = true;
57
+ return new Promise((resolve, reject) => {
58
+ process.once("message", function receive(value) {
59
+ if (value.type !== "restarted") {
60
+ process.once("message", receive);
61
+ return;
62
+ }
63
+ if (value.error && !shutting) {
64
+ restartPending = false;
65
+ reject(new Error(value.error));
66
+ return;
67
+ }
68
+ // The HTTP adapter finishes the authenticated response before this child exits.
69
+ setTimeout(() => { if (process.connected)
70
+ process.disconnect(); http?.closeConnections(); }, 200);
71
+ if (value.error)
72
+ reject(new Error(value.error));
73
+ else
74
+ resolve(value.ready);
75
+ });
76
+ process.send({ type: "restart" });
77
+ });
78
+ };
79
+ process.on("message", (value) => {
80
+ if (value.type !== "drain")
81
+ return;
82
+ // Both Web and CLI restarts pass this boundary before changing runtime ownership.
83
+ try {
84
+ if (settings?.status().writeBlocked)
85
+ throw new Error("Configuration publication is uncertain");
86
+ }
87
+ catch {
88
+ process.send?.({ type: "drained", error: "Restart blocked; stop and inspect private configuration files" });
89
+ return;
90
+ }
91
+ void shutdown(0, true).then(() => {
92
+ if (process.exitCode) {
93
+ if (process.connected)
94
+ process.disconnect();
95
+ }
96
+ else
97
+ process.send?.({ type: "drained" });
98
+ });
99
+ });
100
+ const stop = () => { void shutdown(0).finally(() => { http?.closeConnections(); if (process.connected)
101
+ process.disconnect(); }); };
102
+ process.once("SIGINT", stop);
103
+ process.once("SIGTERM", stop);
104
+ process.once("disconnect", stop);
105
+ try {
106
+ release = await acquireLock(config.dataDir);
107
+ tasks = createTaskRuntime(config.dataDir, config.executors, createExecution(executorEnvironment(native, environment, config)), config.coordinator ? createCoordinator(config.dataDir, config.coordinator) : undefined, fatal);
108
+ if (adminToken)
109
+ settings = new Settings(new LocalSettingsFiles(configPath, environmentPath), configurationPaths, config, base, process.env);
110
+ http = new HttpServer({ tasks, settings, token, adminToken, fatal, restart, instanceId });
111
+ const origin = await http.listen(config.port);
112
+ if (shutting)
113
+ throw new Error("Startup stopped");
114
+ tasks.supervisor.start();
115
+ const ready = { instanceId, pid: process.pid, url: origin + "/mcp", consoleUrl: settings ? origin + "/admin/" : null };
116
+ console.log(JSON.stringify({ event: "ready", ...ready }));
117
+ process.send?.({ type: "ready", ready });
118
+ }
119
+ catch (error) {
120
+ fatal(error);
121
+ }
122
+ //# sourceMappingURL=main.js.map
@@ -1,16 +1,11 @@
1
+ import { startupPaths } from "../configuration/files.js";
1
2
  import { existsSync, mkdirSync, writeFileSync, openSync, fsyncSync, closeSync } from "node:fs";
2
3
  import { join } from "node:path";
3
- import { z } from "zod";
4
- import { loadConfig } from "./config.js";
5
- import { readEnvironment, validateServiceEnvironment } from "./environment.js";
6
- import { checkDataDirectory, readPrivateText, replacePrivateText } from "./private-files.js";
7
- import { acquireLock } from "./lock.js";
8
- /** A stable launch locator is the only runtime input outside the selected dataDir. */
9
- export function startupPaths(home) {
10
- const { dataDir } = z.object({ dataDir: z.string() }).strict().parse(JSON.parse(readPrivateText(join(home, "launch.json"))));
11
- checkDataDirectory(dataDir);
12
- return { dataDir, configPath: join(dataDir, "config.json"), environmentPath: join(dataDir, "environment.json") };
13
- }
4
+ import { loadConfig } from "../configuration/files.js";
5
+ import { readEnvironment } from "../configuration/files.js";
6
+ import { validateServiceEnvironment } from "../configuration/environment.js";
7
+ import { checkDataDirectory, readPrivateText, replacePrivateText } from "../infrastructure/private-files.js";
8
+ import { acquireLock } from "../infrastructure/lock.js";
14
9
  function prepareFile(path, text) {
15
10
  if (existsSync(path)) {
16
11
  if (readPrivateText(path) !== text)
@@ -0,0 +1,42 @@
1
+ import { join } from "node:path";
2
+ import { ExecutorRegistry } from "../executors/registry.js";
3
+ import { checkWorkspace, resolveWorkspace } from "../infrastructure/workspace.js";
4
+ import { LocalResultFiles } from "../results/files.js";
5
+ import { Results } from "../results/service.js";
6
+ import { TaskDefinitions } from "../tasks/definitions.js";
7
+ import { TaskRuns } from "../tasks/runs.js";
8
+ import { RunSupervisor } from "../tasks/supervisor.js";
9
+ import { TaskStore } from "../tasks/storage/store.js";
10
+ /** Creates and owns the business store and services; callers must stop ingress before closing. */
11
+ export function createTaskRuntime(directory, profiles, openExecution, coordinate, fatal) {
12
+ const store = new TaskStore(directory);
13
+ try {
14
+ const executors = new ExecutorRegistry(profiles);
15
+ const workspaces = { resolve: resolveWorkspace, check: checkWorkspace };
16
+ const results = new Results(new LocalResultFiles(join(directory, "results"), true));
17
+ const definitions = new TaskDefinitions(store, executors, workspaces, Boolean(coordinate));
18
+ const runs = new TaskRuns(store, executors, results, workspaces);
19
+ const supervisor = new RunSupervisor(runs, openExecution, coordinate, fatal);
20
+ let closing;
21
+ return { definitions, runs, supervisor, executors,
22
+ close() {
23
+ return closing ??= (async () => {
24
+ definitions.close();
25
+ const stopped = supervisor.close();
26
+ runs.close();
27
+ try {
28
+ await stopped;
29
+ }
30
+ finally {
31
+ store.close();
32
+ }
33
+ })();
34
+ },
35
+ };
36
+ }
37
+ catch (error) {
38
+ store.close();
39
+ throw error;
40
+ }
41
+ }
42
+ //# sourceMappingURL=task-runtime.js.map
@@ -0,0 +1,28 @@
1
+ import { z } from "zod";
2
+ import { executorSchema } from "../executors/validation.js";
3
+ import { baseEnvKeys } from "./policy.js";
4
+ const envKey = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/);
5
+ const modelId = z.string().min(1).max(200);
6
+ const coordinatorSchema = z.discriminatedUnion("provider", [
7
+ z.object({ provider: z.enum(["openai", "anthropic", "deepseek"]), model: modelId }).strict(),
8
+ z.object({
9
+ provider: z.literal("openai-compatible"), model: modelId,
10
+ api: z.enum(["openai-completions", "openai-responses"]).default("openai-completions"),
11
+ baseUrl: z.string().url().refine(value => {
12
+ const url = new URL(value);
13
+ return ["https:", "http:"].includes(url.protocol) && !url.username && !url.password && !url.search && !url.hash;
14
+ }, "baseUrl must be HTTP(S) without credentials, query or fragment"),
15
+ apiKeyEnv: envKey.refine(key => !baseEnvKeys.includes(key), "Credential must not use a base environment key"),
16
+ contextWindow: z.number().int().positive(),
17
+ maxTokens: z.number().int().positive(),
18
+ reasoning: z.boolean().default(false),
19
+ }).strict(),
20
+ ]).refine(value => value.provider !== "openai-compatible" || value.maxTokens <= value.contextWindow, "maxTokens must not exceed contextWindow");
21
+ export const configSchema = z.object({
22
+ dataDir: z.string().min(1),
23
+ port: z.number().int().min(0).max(65535).default(8765),
24
+ tokenEnv: z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/).default("AGENT_TASK_TOKEN"),
25
+ coordinator: coordinatorSchema.optional(),
26
+ executors: z.array(executorSchema).max(100).default([]),
27
+ }).strict();
28
+ //# sourceMappingURL=contract.js.map
@@ -1,5 +1,4 @@
1
- import { ADMIN_TOKEN_ENV, coordinatorCredentialEnv } from "./config.js";
2
- import { readPrivateText } from "./private-files.js";
1
+ import { ADMIN_TOKEN_ENV, coordinatorCredentialEnv } from "./policy.js";
3
2
  export function parseEnvironment(text) {
4
3
  let value;
5
4
  try {
@@ -13,9 +12,6 @@ export function parseEnvironment(text) {
13
12
  throw new Error("Invalid environment file");
14
13
  return value;
15
14
  }
16
- export function readEnvironment(path) {
17
- return parseEnvironment(readPrivateText(path));
18
- }
19
15
  /** Credential names and values must stay separate, including historical token aliases. */
20
16
  export function validateServiceEnvironment(config, environment) {
21
17
  const token = environment[config.tokenEnv], adminToken = environment[ADMIN_TOKEN_ENV];
@@ -0,0 +1,43 @@
1
+ import { join } from "node:path";
2
+ import { z } from "zod";
3
+ import { checkDataDirectory, readPrivateText, replacePrivateText } from "../infrastructure/private-files.js";
4
+ import { resolveWorkspace } from "../infrastructure/workspace.js";
5
+ import { configSchema } from "./contract.js";
6
+ import { parseEnvironment } from "./environment.js";
7
+ import { validateConfig } from "./policy.js";
8
+ export const configurationPaths = { resolveWorkspace, checkDataDirectory };
9
+ export function loadConfig(path) {
10
+ let value;
11
+ try {
12
+ value = JSON.parse(readPrivateText(path));
13
+ }
14
+ catch {
15
+ throw new Error("Cannot read configuration JSON");
16
+ }
17
+ return validateConfig(configSchema.parse(value), resolveWorkspace);
18
+ }
19
+ export function readEnvironment(path) { return parseEnvironment(readPrivateText(path)); }
20
+ export class LocalSettingsFiles {
21
+ configPath;
22
+ environmentPath;
23
+ constructor(configPath, environmentPath) {
24
+ this.configPath = configPath;
25
+ this.environmentPath = environmentPath;
26
+ if (configPath === environmentPath)
27
+ throw new Error("Configuration and environment must be separate files");
28
+ }
29
+ read() {
30
+ const configText = readPrivateText(this.configPath), environmentText = readPrivateText(this.environmentPath);
31
+ return { configText, environmentText, config: configSchema.parse(JSON.parse(configText)), environment: parseEnvironment(environmentText) };
32
+ }
33
+ publish(target, value) {
34
+ replacePrivateText(target === "config" ? this.configPath : this.environmentPath, JSON.stringify(value, null, 2) + "\n");
35
+ }
36
+ }
37
+ /** A stable launch locator is the only runtime input outside the selected dataDir. */
38
+ export function startupPaths(home) {
39
+ const { dataDir } = z.object({ dataDir: z.string() }).strict().parse(JSON.parse(readPrivateText(join(home, "launch.json"))));
40
+ checkDataDirectory(dataDir);
41
+ return { dataDir, configPath: join(dataDir, "config.json"), environmentPath: join(dataDir, "environment.json") };
42
+ }
43
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1,6 @@
1
+ /** Configuration preview and task impact are one management operation for all clients. */
2
+ export function previewSettings(settings, tasks, input) {
3
+ const preview = settings.preview(input);
4
+ return { ...preview, affectedTasks: tasks.executorImpact(preview.config.executors) };
5
+ }
6
+ //# sourceMappingURL=management.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1,29 @@
1
+ export const ADMIN_TOKEN_ENV = "AGENT_TASK_ADMIN_TOKEN";
2
+ export const baseEnvKeys = ["HOME", "PATH", "LANG", "LC_ALL", "TZ", "TMPDIR", "NODE_OPTIONS", "NODE_PATH", "__proto__", "constructor", "prototype"];
3
+ export function coordinatorCredentialEnv(config) {
4
+ switch (config.provider) {
5
+ case "openai": return "OPENAI_API_KEY";
6
+ case "anthropic": return "ANTHROPIC_API_KEY";
7
+ case "deepseek": return "DEEPSEEK_API_KEY";
8
+ case "openai-compatible": return config.apiKeyEnv;
9
+ }
10
+ }
11
+ export function validateConfig(value, resolveWorkspace) {
12
+ const config = structuredClone(value);
13
+ if (!config.dataDir.startsWith("/") || /[\x00-\x1f\x7f]/.test(config.dataDir))
14
+ throw new Error("dataDir must be absolute without control characters");
15
+ const coordinatorKey = config.coordinator ? coordinatorCredentialEnv(config.coordinator) : undefined;
16
+ if (/^(NODE_|LD_|DYLD_)/.test(config.tokenEnv) || [...baseEnvKeys, ADMIN_TOKEN_ENV, "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "DEEPSEEK_API_KEY", coordinatorKey].includes(config.tokenEnv))
17
+ throw new Error("tokenEnv must be separate from base environment, administrator and model credentials");
18
+ if (coordinatorKey === ADMIN_TOKEN_ENV)
19
+ throw new Error("Administrator token must not be used as a model credential");
20
+ const ids = new Set();
21
+ for (const executor of config.executors) {
22
+ if (ids.has(executor.id))
23
+ throw new Error("Duplicate executor ID");
24
+ ids.add(executor.id);
25
+ executor.cwd = resolveWorkspace(executor.cwd);
26
+ }
27
+ return config;
28
+ }
29
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ports.js.map