@yuandc/aica 0.1.1 → 0.1.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.
Files changed (48) hide show
  1. package/dist/acp/agent.js +1 -54
  2. package/dist/acp/client/acp-client.js +1 -102
  3. package/dist/acp/client/acp-content.js +1 -13
  4. package/dist/acp/client/acp-events.js +1 -106
  5. package/dist/acp/client/acp-process.js +1 -34
  6. package/dist/acp/client/acp-runtime-pool.js +1 -248
  7. package/dist/acp/client/context-usage.js +1 -29
  8. package/dist/acp/client/json-rpc.js +4 -128
  9. package/dist/acp/provider-types.js +0 -1
  10. package/dist/acp/providers/codex/codex-process.js +1 -51
  11. package/dist/acp/providers/codex/events.js +28 -1473
  12. package/dist/acp/providers/codex/permissions.js +1 -49
  13. package/dist/acp/providers/codex/provider.js +1 -376
  14. package/dist/acp/providers/codex-acp/adapter.js +5 -947
  15. package/dist/acp/providers/codex-acp/context-maintenance.js +5 -148
  16. package/dist/acp/providers/codex-acp/launch.js +1 -35
  17. package/dist/acp/providers/codex-acp/provider.js +1 -486
  18. package/dist/acp/providers/mimo/provider.js +5 -448
  19. package/dist/acp/providers/opencode/provider.js +4 -489
  20. package/dist/acp/providers/registry.js +1 -23
  21. package/dist/acp/standard-events.js +1 -167
  22. package/dist/commands/start.js +1 -137
  23. package/dist/commands/worker-auth.js +4 -100
  24. package/dist/commands/worker-project.js +1 -57
  25. package/dist/core/aca-config.js +1 -74
  26. package/dist/core/aca-server-client.js +1 -57
  27. package/dist/core/acp-event-coalescer.js +1 -108
  28. package/dist/core/acp-event-upload-filter.js +1 -16
  29. package/dist/core/acp-orphan-cleanup.js +1 -91
  30. package/dist/core/affected-files.js +2 -268
  31. package/dist/core/auth.js +1 -36
  32. package/dist/core/file-transfer-worker.js +1 -169
  33. package/dist/core/fs.js +2 -28
  34. package/dist/core/heartbeat.js +3 -578
  35. package/dist/core/job-permission-policy.js +1 -42
  36. package/dist/core/job-worker.js +6 -749
  37. package/dist/core/logger.js +3 -42
  38. package/dist/core/long-poll-worker.js +1 -26
  39. package/dist/core/machine-filesystem-worker.js +3 -352
  40. package/dist/core/paths.js +1 -26
  41. package/dist/core/process-identity.js +1 -34
  42. package/dist/core/process.js +2 -33
  43. package/dist/core/provider-health.js +1 -54
  44. package/dist/core/runtime-options.js +1 -38
  45. package/dist/core/worktree.js +1 -95
  46. package/dist/worker-cli.js +1 -28
  47. package/dist/worker-single-cli.js +1 -16
  48. package/package.json +1 -1
@@ -1,169 +1 @@
1
- import fs from "node:fs";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import { Readable } from "node:stream";
5
- import { acaServerRequest, acaServerStreamRequest } from "./aca-server-client.js";
6
- import { loadAcaConfig } from "./aca-config.js";
7
- import { getAcaHome } from "./paths.js";
8
- import { startLongPollWorker } from "./long-poll-worker.js";
9
- const LONG_POLL_WAIT_MS = 25_000;
10
- export function startFileTransferWorkerLoop(logger) {
11
- return startLongPollWorker({
12
- run: async () => {
13
- const config = loadAcaConfig();
14
- if (!config.token)
15
- throw new Error("Worker 尚未配置认证令牌");
16
- const response = await acaServerRequest("GET", `/api/client/file-requests/claim?machineId=${encodeURIComponent(config.machineId)}&waitMs=${LONG_POLL_WAIT_MS}`);
17
- if (!response.item)
18
- return;
19
- try {
20
- await streamFileRequest(response.item, config, logger);
21
- }
22
- catch (error) {
23
- const message = error instanceof Error ? error.message : String(error);
24
- logger.warn(`file request failed request=${response.item.requestId}: ${message}`);
25
- try {
26
- await failFileRequest(response.item.requestId, message);
27
- }
28
- catch (failError) {
29
- logger.warn(`file request fail callback failed request=${response.item.requestId}: ${failError instanceof Error ? failError.message : String(failError)}`);
30
- }
31
- }
32
- },
33
- onError: (error, retryMs) => logger.warn(`file transfer worker failed, retry in ${retryMs}ms: ${error instanceof Error ? error.message : String(error)}`)
34
- });
35
- }
36
- async function failFileRequest(requestId, message) {
37
- await acaServerRequest("POST", `/api/client/file-requests/${encodeURIComponent(requestId)}/fail`, { message });
38
- }
39
- async function streamFileRequest(request, config, logger) {
40
- const project = config.projects.find((item) => item.projectId === request.projectId);
41
- const projectRoot = expandProjectRoot(request.projectRoot || project?.rootPath || "");
42
- if (!projectRoot)
43
- throw new Error(`file request ${request.requestId} project root is unknown`);
44
- const purpose = request.purpose === "temp-image" ? "temp-image" : "project-file";
45
- const resolved = purpose === "temp-image"
46
- ? resolveTempImageFile(request.path)
47
- : resolveProjectFile(projectRoot, request.path);
48
- const stat = fs.statSync(resolved);
49
- if (!stat.isFile())
50
- throw new Error(`file request ${request.requestId} target is not a file`);
51
- if (stat.size > request.maxBytes)
52
- throw new Error(`file request ${request.requestId} target is too large (${stat.size} bytes)`);
53
- const mimeType = purpose === "temp-image" ? detectSafeTempImageMime(resolved) : mimeForPath(resolved);
54
- logger.info(`streaming file request=${request.requestId} path=${resolved}`);
55
- await acaServerStreamRequest("POST", `/api/client/file-requests/${encodeURIComponent(request.requestId)}/stream`, Readable.toWeb(fs.createReadStream(resolved)), {
56
- "content-type": "application/octet-stream",
57
- "x-aca-file-name": encodeURIComponent(path.basename(resolved)),
58
- "x-aca-file-size": String(stat.size),
59
- "x-aca-file-mime": mimeType
60
- });
61
- }
62
- function resolveProjectFile(projectRoot, requestedPath) {
63
- const root = fs.realpathSync(expandProjectRoot(projectRoot));
64
- const candidate = path.isAbsolute(requestedPath)
65
- ? requestedPath
66
- : path.join(root, requestedPath);
67
- const target = fs.realpathSync(candidate);
68
- const relative = path.relative(root, target);
69
- if (relative === "" || relative.startsWith("..") || path.isAbsolute(relative)) {
70
- throw new Error("requested file is outside project root");
71
- }
72
- return target;
73
- }
74
- export function resolveTempImageFile(requestedPath) {
75
- const candidate = localPathFromUri(requestedPath);
76
- if (!path.isAbsolute(candidate))
77
- throw new Error("temporary image path must be absolute");
78
- const sourceStat = fs.lstatSync(candidate);
79
- if (sourceStat.isSymbolicLink())
80
- throw new Error("temporary image cannot be a symbolic link");
81
- if (!sourceStat.isFile())
82
- throw new Error("temporary image target is not a regular file");
83
- const tempRoot = fs.realpathSync(os.tmpdir());
84
- const target = fs.realpathSync(candidate);
85
- const relative = path.relative(tempRoot, target);
86
- if (relative === "" || relative.startsWith("..") || path.isAbsolute(relative)) {
87
- throw new Error("temporary image is outside the system temp directory");
88
- }
89
- return target;
90
- }
91
- export function detectSafeTempImageMime(filePath) {
92
- const extension = path.extname(filePath).toLowerCase();
93
- const header = Buffer.alloc(16);
94
- const file = fs.openSync(filePath, "r");
95
- let bytesRead = 0;
96
- try {
97
- bytesRead = fs.readSync(file, header, 0, header.length, 0);
98
- }
99
- finally {
100
- fs.closeSync(file);
101
- }
102
- const bytes = header.subarray(0, bytesRead);
103
- if (extension === ".png" && bytes.length >= 8 && bytes.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))) {
104
- return "image/png";
105
- }
106
- if ([".jpg", ".jpeg"].includes(extension) && bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff) {
107
- return "image/jpeg";
108
- }
109
- if (extension === ".gif" && bytes.length >= 6 && ["GIF87a", "GIF89a"].includes(bytes.subarray(0, 6).toString("ascii"))) {
110
- return "image/gif";
111
- }
112
- if (extension === ".webp" && bytes.length >= 12 && bytes.subarray(0, 4).toString("ascii") === "RIFF" && bytes.subarray(8, 12).toString("ascii") === "WEBP") {
113
- return "image/webp";
114
- }
115
- throw new Error("temporary file is not a supported raster image");
116
- }
117
- function localPathFromUri(value) {
118
- const trimmed = String(value || "").trim();
119
- if (!trimmed.startsWith("file://"))
120
- return trimmed;
121
- try {
122
- return decodeURIComponent(new URL(trimmed).pathname);
123
- }
124
- catch {
125
- throw new Error("temporary image path is invalid");
126
- }
127
- }
128
- function expandProjectRoot(value) {
129
- if (!value)
130
- return "";
131
- if (value === "~")
132
- return path.dirname(getAcaHome());
133
- if (value.startsWith("~/"))
134
- return path.join(path.dirname(getAcaHome()), value.slice(2));
135
- return value;
136
- }
137
- function mimeForPath(filePath) {
138
- switch (path.extname(filePath).toLowerCase()) {
139
- case ".pdf":
140
- return "application/pdf";
141
- case ".md":
142
- case ".markdown":
143
- return "text/markdown; charset=utf-8";
144
- case ".txt":
145
- case ".log":
146
- return "text/plain; charset=utf-8";
147
- case ".html":
148
- return "text/html; charset=utf-8";
149
- case ".json":
150
- return "application/json; charset=utf-8";
151
- case ".png":
152
- return "image/png";
153
- case ".jpg":
154
- case ".jpeg":
155
- return "image/jpeg";
156
- case ".gif":
157
- return "image/gif";
158
- case ".webp":
159
- return "image/webp";
160
- case ".zip":
161
- return "application/zip";
162
- case ".docx":
163
- return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
164
- case ".xlsx":
165
- return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
166
- default:
167
- return "application/octet-stream";
168
- }
169
- }
1
+ import s from"node:fs";import g from"node:os";import i from"node:path";import{Readable as h}from"node:stream";import{acaServerRequest as p,acaServerStreamRequest as w}from"./aca-server-client.js";import{loadAcaConfig as y}from"./aca-config.js";import{getAcaHome as f}from"./paths.js";import{startLongPollWorker as x}from"./long-poll-worker.js";const S=25e3;function P(e){return x({run:async()=>{const t=y();if(!t.token)throw new Error("Worker 尚未配置认证令牌");const a=await p("GET",`/api/client/file-requests/claim?machineId=${encodeURIComponent(t.machineId)}&waitMs=${S}`);if(a.item)try{await I(a.item,t,e)}catch(n){const o=n instanceof Error?n.message:String(n);e.warn(`file request failed request=${a.item.requestId}: ${o}`);try{await b(a.item.requestId,o)}catch(r){e.warn(`file request fail callback failed request=${a.item.requestId}: ${r instanceof Error?r.message:String(r)}`)}}},onError:(t,a)=>e.warn(`file transfer worker failed, retry in ${a}ms: ${t instanceof Error?t.message:String(t)}`)})}async function b(e,t){await p("POST",`/api/client/file-requests/${encodeURIComponent(e)}/fail`,{message:t})}async function I(e,t,a){const n=t.projects.find(d=>d.projectId===e.projectId),o=l(e.projectRoot||n?.rootPath||"");if(!o)throw new Error(`file request ${e.requestId} project root is unknown`);const r=e.purpose==="temp-image"?"temp-image":"project-file",c=r==="temp-image"?E(e.path):j(o,e.path),m=s.statSync(c);if(!m.isFile())throw new Error(`file request ${e.requestId} target is not a file`);if(m.size>e.maxBytes)throw new Error(`file request ${e.requestId} target is too large (${m.size} bytes)`);const u=r==="temp-image"?R(c):q(c);a.info(`streaming file request=${e.requestId} path=${c}`),await w("POST",`/api/client/file-requests/${encodeURIComponent(e.requestId)}/stream`,h.toWeb(s.createReadStream(c)),{"content-type":"application/octet-stream","x-aca-file-name":encodeURIComponent(i.basename(c)),"x-aca-file-size":String(m.size),"x-aca-file-mime":u})}function j(e,t){const a=s.realpathSync(l(e)),n=i.isAbsolute(t)?t:i.join(a,t),o=s.realpathSync(n),r=i.relative(a,o);if(r===""||r.startsWith("..")||i.isAbsolute(r))throw new Error("requested file is outside project root");return o}function E(e){const t=$(e);if(!i.isAbsolute(t))throw new Error("temporary image path must be absolute");const a=s.lstatSync(t);if(a.isSymbolicLink())throw new Error("temporary image cannot be a symbolic link");if(!a.isFile())throw new Error("temporary image target is not a regular file");const n=s.realpathSync(g.tmpdir()),o=s.realpathSync(t),r=i.relative(n,o);if(r===""||r.startsWith("..")||i.isAbsolute(r))throw new Error("temporary image is outside the system temp directory");return o}function R(e){const t=i.extname(e).toLowerCase(),a=Buffer.alloc(16),n=s.openSync(e,"r");let o=0;try{o=s.readSync(n,a,0,a.length,0)}finally{s.closeSync(n)}const r=a.subarray(0,o);if(t===".png"&&r.length>=8&&r.subarray(0,8).equals(Buffer.from([137,80,78,71,13,10,26,10])))return"image/png";if([".jpg",".jpeg"].includes(t)&&r.length>=3&&r[0]===255&&r[1]===216&&r[2]===255)return"image/jpeg";if(t===".gif"&&r.length>=6&&["GIF87a","GIF89a"].includes(r.subarray(0,6).toString("ascii")))return"image/gif";if(t===".webp"&&r.length>=12&&r.subarray(0,4).toString("ascii")==="RIFF"&&r.subarray(8,12).toString("ascii")==="WEBP")return"image/webp";throw new Error("temporary file is not a supported raster image")}function $(e){const t=String(e||"").trim();if(!t.startsWith("file://"))return t;try{return decodeURIComponent(new URL(t).pathname)}catch{throw new Error("temporary image path is invalid")}}function l(e){return e?e==="~"?i.dirname(f()):e.startsWith("~/")?i.join(i.dirname(f()),e.slice(2)):e:""}function q(e){switch(i.extname(e).toLowerCase()){case".pdf":return"application/pdf";case".md":case".markdown":return"text/markdown; charset=utf-8";case".txt":case".log":return"text/plain; charset=utf-8";case".html":return"text/html; charset=utf-8";case".json":return"application/json; charset=utf-8";case".png":return"image/png";case".jpg":case".jpeg":return"image/jpeg";case".gif":return"image/gif";case".webp":return"image/webp";case".zip":return"application/zip";case".docx":return"application/vnd.openxmlformats-officedocument.wordprocessingml.document";case".xlsx":return"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";default:return"application/octet-stream"}}export{R as detectSafeTempImageMime,E as resolveTempImageFile,P as startFileTransferWorkerLoop};
package/dist/core/fs.js CHANGED
@@ -1,28 +1,2 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- export function ensureDir(dir) {
4
- fs.mkdirSync(dir, { recursive: true });
5
- }
6
- export function writeJsonFile(file, value) {
7
- ensureDir(path.dirname(file));
8
- fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`, "utf8");
9
- }
10
- export function readJsonFile(file) {
11
- try {
12
- return JSON.parse(fs.readFileSync(file, "utf8"));
13
- }
14
- catch (error) {
15
- if (error.code === "ENOENT")
16
- return null;
17
- throw error;
18
- }
19
- }
20
- export function removeFileIfExists(file) {
21
- try {
22
- fs.unlinkSync(file);
23
- }
24
- catch (error) {
25
- if (error.code !== "ENOENT")
26
- throw error;
27
- }
28
- }
1
+ import t from"node:fs";import n from"node:path";function i(r){t.mkdirSync(r,{recursive:!0})}function u(r,e){i(n.dirname(r)),t.writeFileSync(r,`${JSON.stringify(e,null,2)}
2
+ `,"utf8")}function f(r){try{return JSON.parse(t.readFileSync(r,"utf8"))}catch(e){if(e.code==="ENOENT")return null;throw e}}function l(r){try{t.unlinkSync(r)}catch(e){if(e.code!=="ENOENT")throw e}}export{i as ensureDir,f as readJsonFile,l as removeFileIfExists,u as writeJsonFile};