@realqiyan/agent-task 0.0.1-rc.1 → 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 (91) hide show
  1. package/README.md +8 -5
  2. package/bin/agent-task.mjs +23 -41
  3. package/dist/application/launcher.js +215 -0
  4. package/dist/application/main.js +122 -0
  5. package/dist/application/startup.js +51 -0
  6. package/dist/application/task-runtime.js +42 -0
  7. package/dist/configuration/contract.js +28 -0
  8. package/dist/configuration/environment.js +32 -0
  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} +97 -42
  15. package/dist/executors/environment.js +10 -0
  16. package/dist/executors/model.js +2 -0
  17. package/dist/{permissions.js → executors/permissions.js} +3 -4
  18. package/dist/executors/registry.js +29 -0
  19. package/dist/executors/validation.js +10 -0
  20. package/dist/{lock.js → infrastructure/lock.js} +17 -1
  21. package/dist/{private-files.js → infrastructure/private-files.js} +17 -9
  22. package/dist/infrastructure/sqlite.js +19 -0
  23. package/dist/infrastructure/version.js +3 -0
  24. package/dist/{acp.js → integrations/acp/connection.js} +14 -11
  25. package/dist/{node-execution.js → integrations/acp/execution.js} +3 -4
  26. package/dist/integrations/acp/runtime.js +28 -0
  27. package/dist/{worker.js → integrations/acp/worker.js} +5 -4
  28. package/dist/{coordinator.js → integrations/coordinator/coordinator.js} +16 -7
  29. package/dist/{coordinator-provider.js → integrations/coordinator/provider.js} +1 -1
  30. package/dist/{coordinator-storage.js → integrations/coordinator/storage.js} +2 -2
  31. package/dist/integrations/local-service/control.js +33 -0
  32. package/dist/protocol/cli/admin-client.js +45 -0
  33. package/dist/protocol/cli/commands.js +394 -0
  34. package/dist/{http-auth.js → protocol/http/auth.js} +1 -1
  35. package/dist/protocol/http/server.js +111 -0
  36. package/dist/{web.js → protocol/http/web.js} +31 -18
  37. package/dist/{mcp.js → protocol/mcp.js} +13 -13
  38. package/dist/protocol/services.js +2 -0
  39. package/dist/protocol/settings-contract.js +40 -0
  40. package/dist/protocol/task-contract.js +28 -0
  41. package/dist/results/files.js +70 -0
  42. package/dist/results/ports.js +2 -0
  43. package/dist/results/service.js +42 -0
  44. package/dist/shared/errors.js +13 -0
  45. package/dist/shared/limits.js +3 -0
  46. package/dist/shared/time.js +2 -0
  47. package/dist/shared/validation.js +8 -0
  48. package/dist/tasks/definitions.js +83 -0
  49. package/dist/tasks/graph.js +23 -0
  50. package/dist/{human-input.js → tasks/human-input.js} +8 -7
  51. package/dist/tasks/model.js +2 -0
  52. package/dist/tasks/ports.js +2 -0
  53. package/dist/tasks/runs.js +263 -0
  54. package/dist/tasks/schedule.js +36 -0
  55. package/dist/tasks/sessions.js +62 -0
  56. package/dist/tasks/storage/records.js +45 -0
  57. package/dist/{task-storage.js → tasks/storage/store.js} +23 -36
  58. package/dist/tasks/supervisor.js +227 -0
  59. package/dist/tasks/validation.js +24 -0
  60. package/dist/web/api.js +1 -1
  61. package/dist/web/app.js +7 -0
  62. package/dist/web/settings-view.js +40 -28
  63. package/docs/architecture-guidelines.md +8 -6
  64. package/docs/cli.md +49 -0
  65. package/docs/deployment.md +32 -70
  66. package/docs/design-proposal.md +3 -3
  67. package/docs/release.md +17 -18
  68. package/docs/technical-design.md +47 -26
  69. package/docs/validation.md +57 -17
  70. package/docs/web-console.md +15 -19
  71. package/package.json +8 -8
  72. package/scripts/install-lib.mjs +7 -105
  73. package/scripts/mcp-call.mjs +1 -1
  74. package/scripts/probe-agent.mjs +11 -5
  75. package/scripts/service-runner.mjs +2 -7
  76. package/scripts/setup-wizard.mjs +3 -8
  77. package/scripts/setup.mjs +31 -40
  78. package/deploy/agent-task.service.example +0 -17
  79. package/dist/admin.js +0 -31
  80. package/dist/config.js +0 -113
  81. package/dist/environment.js +0 -23
  82. package/dist/main.js +0 -131
  83. package/dist/model.js +0 -28
  84. package/dist/results.js +0 -85
  85. package/dist/settings-contract.js +0 -16
  86. package/dist/task-contract.js +0 -37
  87. package/dist/task-model.js +0 -2
  88. package/dist/tasks.js +0 -509
  89. package/dist/version.js +0 -3
  90. package/scripts/uninstall.mjs +0 -53
  91. /package/dist/{workspace.js → infrastructure/workspace.js} +0 -0
package/dist/main.js DELETED
@@ -1,131 +0,0 @@
1
- import express from "express";
2
- import { resolve } from "node:path";
3
- import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
4
- import { ADMIN_TOKEN_ENV, loadConfig } from "./config.js";
5
- import { baseEnvironment, readEnvironment } from "./environment.js";
6
- import { bearerVerifier } from "./http-auth.js";
7
- import { Settings } from "./settings.js";
8
- import { createWebRouter } from "./web.js";
9
- import { acquireLock } from "./lock.js";
10
- import { createMcpServer } from "./mcp.js";
11
- import { errorText } from "./model.js";
12
- import { TaskStore } from "./task-storage.js";
13
- import { Tasks } from "./tasks.js";
14
- import { Results } from "./results.js";
15
- import { openExecution } from "./node-execution.js";
16
- import { createCoordinator } from "./coordinator.js";
17
- process.umask(0o077);
18
- const args = process.argv.slice(2);
19
- if (args[0] !== "--config" || (args.length !== 2 && !(args.length === 4 && args[2] === "--environment"))) {
20
- console.error("Usage: node dist/main.js --config /absolute/path/config.json [--environment /absolute/private/path/environment.json]");
21
- process.exit(2);
22
- }
23
- const configPath = resolve(args[1]), environmentPath = args[3] ? resolve(args[3]) : undefined;
24
- const base = baseEnvironment(process.env);
25
- if (environmentPath) {
26
- const environment = readEnvironment(environmentPath);
27
- for (const key of Object.keys(process.env))
28
- delete process.env[key];
29
- Object.assign(process.env, base, environment);
30
- }
31
- const config = loadConfig(configPath);
32
- const token = process.env[config.tokenEnv];
33
- if (!token || token.length < 32)
34
- throw new Error(config.tokenEnv + " must contain a token of at least 32 characters");
35
- const authorized = bearerVerifier(token), adminToken = process.env[ADMIN_TOKEN_ENV];
36
- if (adminToken !== undefined && (!environmentPath || !/^[!-~]{32,256}$/.test(adminToken) || adminToken === token)) {
37
- throw new Error("Console requires --environment and a distinct administrator token of 32–256 visible ASCII characters");
38
- }
39
- let http;
40
- let tasks, taskStore, settings;
41
- let release;
42
- let shutting;
43
- const connections = new Set();
44
- function shutdown(code) {
45
- process.exitCode = Math.max(process.exitCode ? Number(process.exitCode) : 0, code);
46
- return shutting ??= (async () => {
47
- settings?.close();
48
- try {
49
- await tasks?.close();
50
- }
51
- catch (error) {
52
- console.error(errorText(error));
53
- process.exitCode = 1;
54
- }
55
- for (const close of connections)
56
- await close().catch(() => { });
57
- if (http) {
58
- http.closeAllConnections();
59
- await new Promise(done => http.close(() => done()));
60
- }
61
- taskStore?.close();
62
- await release?.();
63
- })().catch(error => { console.error(errorText(error)); process.exitCode = 1; });
64
- }
65
- const fatal = (error) => { console.error(errorText(error)); void shutdown(1); };
66
- try {
67
- release = await acquireLock(config.dataDir);
68
- taskStore = new TaskStore(config.dataDir);
69
- tasks = new Tasks(taskStore, config.executors, new Results(config.dataDir), openExecution, config.coordinator ? createCoordinator(config.dataDir, config.coordinator) : undefined, fatal);
70
- const app = express();
71
- app.disable("x-powered-by");
72
- app.disable("etag");
73
- app.use((_req, res, next) => {
74
- if (shutting) {
75
- res.status(503).end();
76
- return;
77
- }
78
- next();
79
- });
80
- if (adminToken && environmentPath) {
81
- settings = new Settings(configPath, environmentPath, config, base, process.env);
82
- app.use("/admin", createWebRouter(tasks, settings, adminToken, fatal));
83
- }
84
- app.use("/mcp", (req, res, next) => {
85
- const suffix = req.socket.localPort === 80 ? "" : ":" + req.socket.localPort;
86
- if (req.headers.origin !== undefined || !["127.0.0.1" + suffix, "localhost" + suffix].includes(req.headers.host ?? "")) {
87
- res.status(403).end();
88
- return;
89
- }
90
- if (!authorized(req.headers.authorization)) {
91
- res.status(401).set("WWW-Authenticate", "Bearer").end();
92
- return;
93
- }
94
- next();
95
- }, express.json({ limit: "512kb" }));
96
- app.post("/mcp", async (req, res) => {
97
- const server = createMcpServer(tasks, fatal);
98
- const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, enableJsonResponse: true });
99
- const close = async () => { connections.delete(close); await transport.close(); await server.close(); };
100
- connections.add(close);
101
- res.once("close", () => { void close().catch(() => { }); });
102
- try {
103
- await server.connect(transport);
104
- await transport.handleRequest(req, res, req.body);
105
- }
106
- catch {
107
- if (!res.headersSent)
108
- res.status(500).json({ error: "MCP request failed" });
109
- await close();
110
- }
111
- });
112
- app.all("/mcp", (_req, res) => { res.status(405).set("Allow", "POST").end(); });
113
- app.use((_req, res) => { res.status(404).end(); });
114
- app.use(((error, _req, res, _next) => {
115
- res.status(error.status === 413 ? 413 : 400).json({ error: "Invalid request body" });
116
- }));
117
- http = await new Promise((done, reject) => {
118
- const listening = app.listen(config.port, "127.0.0.1", () => done(listening));
119
- listening.once("error", reject);
120
- });
121
- tasks.start();
122
- const address = http.address();
123
- const origin = "http://127.0.0.1:" + (typeof address === "object" && address ? address.port : config.port);
124
- console.log(JSON.stringify({ event: "ready", url: origin + "/mcp", consoleUrl: settings ? origin + "/admin/" : null }));
125
- process.once("SIGINT", () => { void shutdown(0); });
126
- process.once("SIGTERM", () => { void shutdown(0); });
127
- }
128
- catch (error) {
129
- fatal(error);
130
- }
131
- //# sourceMappingURL=main.js.map
package/dist/model.js DELETED
@@ -1,28 +0,0 @@
1
- import { z } from "zod";
2
- export const timestamp = z.string().datetime({ offset: true }).transform(value => new Date(value).toISOString());
3
- export const identifier = z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,99}$/);
4
- export const scheduleSchema = z.discriminatedUnion("type", [
5
- z.object({ type: z.literal("now") }).strict(),
6
- z.object({ type: z.literal("once"), at: timestamp }).strict(),
7
- z.object({ type: z.literal("interval"), startAt: timestamp,
8
- seconds: z.number().int().min(60).max(31_536_000) }).strict(),
9
- ]);
10
- export const executorSchema = z.object({
11
- id: identifier,
12
- command: z.string().min(1),
13
- args: z.array(z.string().max(4096)).max(100).default([]),
14
- cwd: z.string().min(1),
15
- envKeys: z.array(z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/)).max(50).default([]),
16
- permissionAllowlist: z.array(z.string().min(1).max(200)).max(100).default([]),
17
- unattendedApproved: z.boolean().default(false),
18
- resultReadRoots: z.array(z.string().min(1)).max(50).default([]),
19
- }).strict();
20
- export const OUTPUT_LIMIT = 262_144;
21
- export const EVENT_LIMIT = 2000;
22
- export const GRACE_MS = 30_000;
23
- export function iso(now = Date.now()) { return new Date(now).toISOString(); }
24
- export function errorText(error) { return (error instanceof Error ? error.message : String(error)).slice(0, 2000); }
25
- export function initialTime(schedule, now) {
26
- return schedule.type === "now" ? now : Date.parse(schedule.type === "once" ? schedule.at : schedule.startAt);
27
- }
28
- //# sourceMappingURL=model.js.map
package/dist/results.js DELETED
@@ -1,85 +0,0 @@
1
- import { mkdirSync, openSync, closeSync, writeFileSync, readFileSync, fstatSync, fsyncSync, realpathSync, statSync, accessSync, constants } from "node:fs";
2
- import { join, relative, isAbsolute } from "node:path";
3
- import { OUTPUT_LIMIT } from "./model.js";
4
- /** Exact per-run file allowlist intersected with administrator-granted roots. Never offers general filesystem access. */
5
- export function readInputFile(executor, allowed, path, line = 1, limit) {
6
- if (!allowed.includes(path) || realpathSync(path) !== path)
7
- throw new Error("Input file is not authorized for this run");
8
- if (!executor.resultReadRoots.some(root => {
9
- const rel = relative(realpathSync(root), path);
10
- return !rel.startsWith("..") && !isAbsolute(rel);
11
- }))
12
- throw new Error("Input file outside authorized read scope");
13
- if (!Number.isInteger(line) || line < 1 || (limit !== undefined && (!Number.isInteger(limit) || limit < 1)))
14
- throw new Error("Invalid line range");
15
- const fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW);
16
- try {
17
- const stat = fstatSync(fd);
18
- if (!stat.isFile() || stat.size > OUTPUT_LIMIT)
19
- throw new Error("Invalid or oversized input file");
20
- const content = readFileSync(fd, "utf8");
21
- if (Buffer.byteLength(content) > OUTPUT_LIMIT)
22
- throw new Error("Input file grew beyond limit");
23
- if (line === 1 && limit === undefined)
24
- return content;
25
- return content.split("\n").slice(line - 1, limit === undefined ? undefined : line - 1 + limit).join("\n");
26
- }
27
- finally {
28
- closeSync(fd);
29
- }
30
- }
31
- export class Results {
32
- root;
33
- constructor(directory) {
34
- mkdirSync(join(directory, "results"), { recursive: true, mode: 0o700 });
35
- this.root = realpathSync(join(directory, "results"));
36
- }
37
- inside(root, path) {
38
- const rel = relative(root, path);
39
- return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
40
- }
41
- authorize(executor) {
42
- if (!executor.resultReadRoots.some(root => this.inside(realpathSync(root), this.root))) {
43
- throw new Error("Executor " + executor.id + " lacks administrator-approved result read scope");
44
- }
45
- }
46
- readable(path, executor) {
47
- this.authorize(executor);
48
- if (realpathSync(path) !== path || !this.inside(this.root, path) || !statSync(path).isFile())
49
- throw new Error("Invalid result file");
50
- accessSync(path, constants.R_OK);
51
- // Actually open it. Native agent sandbox access remains an administrator-reviewed prerequisite.
52
- const fd = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW);
53
- closeSync(fd);
54
- }
55
- save(taskRunId, nodeId, runId, text) {
56
- for (const id of [taskRunId, nodeId, runId]) {
57
- if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,99}$/.test(id))
58
- throw new Error("Invalid result identity");
59
- }
60
- const directory = join(this.root, taskRunId);
61
- mkdirSync(directory, { recursive: true, mode: 0o700 });
62
- if (realpathSync(directory) !== directory)
63
- throw new Error("Result directory must not be a symlink");
64
- const path = join(directory, nodeId + "." + runId + ".txt");
65
- const fd = openSync(path, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
66
- try {
67
- writeFileSync(fd, text, "utf8");
68
- fsyncSync(fd);
69
- }
70
- finally {
71
- closeSync(fd);
72
- }
73
- for (const dir of [directory, this.root]) {
74
- const handle = openSync(dir, constants.O_RDONLY);
75
- try {
76
- fsyncSync(handle);
77
- }
78
- finally {
79
- closeSync(handle);
80
- }
81
- }
82
- return path;
83
- }
84
- }
85
- //# sourceMappingURL=results.js.map
@@ -1,16 +0,0 @@
1
- import { z } from "zod";
2
- import { configSchema } from "./config.js";
3
- export const settingsConfigSchema = z.object({
4
- editToken: z.string().uuid(),
5
- config: configSchema.pick({ executors: true, coordinator: true }),
6
- }).strict();
7
- const credential = {
8
- editToken: z.string().uuid(),
9
- key: z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/).max(200),
10
- };
11
- export const credentialUpdateSchema = z.discriminatedUnion("action", [
12
- z.object({ ...credential, action: z.literal("replace"),
13
- value: z.string().min(1).max(16_384).refine(value => Boolean(value.trim()) && !value.includes("\0")) }).strict(),
14
- z.object({ ...credential, action: z.literal("delete") }).strict(),
15
- ]);
16
- //# sourceMappingURL=settings-contract.js.map
@@ -1,37 +0,0 @@
1
- import { z } from "zod";
2
- import { isAbsolute } from "node:path";
3
- import { identifier, scheduleSchema, timestamp } from "./model.js";
4
- export const nodeSchema = z.object({
5
- id: identifier, prompt: z.string().trim().min(1).max(64_000),
6
- executorId: identifier.describe("Administrator-registered ACP executor assigned to this node; not a command or URL."),
7
- dependsOn: z.array(identifier).max(20).default([]),
8
- maxRunSeconds: z.number().int().min(1).max(3600).default(600),
9
- }).strict();
10
- export const questionSchema = z.object({
11
- nodeId: identifier.optional(), question: z.string().trim().min(1).max(4000),
12
- completedWork: z.string().trim().min(1).max(4000),
13
- }).strict();
14
- export const replySchema = z.object({
15
- runId: z.string().uuid(), questionId: z.string().uuid(), requestKey: identifier,
16
- answer: z.string().trim().min(1).max(16000),
17
- }).strict();
18
- export const taskIdSchema = z.object({ id: z.string().uuid() }).strict();
19
- export const taskListSchema = z.object({
20
- after: z.number().int().nonnegative().default(0), limit: z.number().int().min(1).max(100).default(20),
21
- }).strict();
22
- export const taskRunListSchema = taskListSchema.extend({ taskId: z.string().uuid().optional() });
23
- export const taskUpdateSchema = z.object({
24
- id: z.string().uuid(), action: z.enum(["pause", "resume", "cancel"]),
25
- }).strict();
26
- export const taskSchema = z.object({
27
- requestKey: identifier, name: z.string().trim().min(1).max(200),
28
- prompt: z.string().trim().min(1).max(64_000).describe("Overall goal and coordination constraints; does not generate or replace the DAG."),
29
- nodes: z.array(nodeSchema).min(1).max(20).describe("Caller-defined fixed DAG. Each node specifies its prompt, executorId and direct dependencies."),
30
- workspace: z.string().min(1).max(4096).refine(path => isAbsolute(path) && !path.includes("\0"), "Workspace must be an absolute path without NUL").optional(),
31
- schedule: scheduleSchema, expiresAt: timestamp.optional(),
32
- maxRunSeconds: z.number().int().min(1).max(86_400).default(1800),
33
- maxSteps: z.number().int().min(1).max(200).default(40),
34
- maxNodeTurns: z.number().int().min(1).max(10).default(3),
35
- inputWaitSeconds: z.number().int().min(1).max(604800).default(86400),
36
- }).strict();
37
- //# sourceMappingURL=task-contract.js.map
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=task-model.js.map