@yuandc/aica 0.1.0 → 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 -26
  47. package/dist/worker-single-cli.js +1 -16
  48. package/package.json +1 -1
@@ -1,108 +1 @@
1
- const MAX_TEXT_CHARS = 1024;
2
- const TEXT_EVENT_TYPES = new Set(["agent_message_chunk", "agent_progress_chunk", "agent_thought_chunk", "user_message_chunk"]);
3
- const SNAPSHOT_EVENT_TYPES = new Set(["usage_update", "session_info_update"]);
4
- /**
5
- * Coalesce only adjacent compatible events in the Worker batch. Tool calls,
6
- * plans, permissions and errors naturally act as barriers because their type
7
- * is neither text nor a replaceable snapshot.
8
- */
9
- export function appendCoalescedAcpEvent(buffer, incoming) {
10
- const previous = buffer.at(-1);
11
- if (!previous) {
12
- buffer.push(incoming);
13
- return false;
14
- }
15
- if (canMergeText(previous, incoming)) {
16
- const mergedText = `${previous.text ?? ""}${incoming.text ?? ""}`;
17
- if (mergedText.length <= MAX_TEXT_CHARS) {
18
- previous.text = mergedText;
19
- previous.raw = mergeRaw(previous.raw, incoming.raw, mergedText);
20
- return true;
21
- }
22
- }
23
- if (canReplaceSnapshot(previous, incoming)) {
24
- const firstSequence = sequenceStart(previous.raw);
25
- previous.label = incoming.label;
26
- previous.text = incoming.text;
27
- previous.status = incoming.status;
28
- previous.toolCallId = incoming.toolCallId;
29
- previous.raw = mergeRaw(previous.raw, incoming.raw, incoming.text, firstSequence);
30
- return true;
31
- }
32
- buffer.push(incoming);
33
- return false;
34
- }
35
- function canMergeText(previous, incoming) {
36
- return TEXT_EVENT_TYPES.has(previous.eventType)
37
- && previous.eventType === incoming.eventType
38
- && previous.turnId === incoming.turnId
39
- && previous.label === incoming.label
40
- && previous.status === incoming.status
41
- && !previous.toolCallId
42
- && !incoming.toolCallId
43
- && typeof previous.text === "string"
44
- && typeof incoming.text === "string";
45
- }
46
- function canReplaceSnapshot(previous, incoming) {
47
- if (previous.eventType !== incoming.eventType || previous.turnId !== incoming.turnId)
48
- return false;
49
- if (SNAPSHOT_EVENT_TYPES.has(previous.eventType))
50
- return true;
51
- if (previous.eventType !== "config_option_update")
52
- return false;
53
- const previousKey = configOptionKey(previous.raw);
54
- const incomingKey = configOptionKey(incoming.raw);
55
- return Boolean(previousKey) && previousKey === incomingKey;
56
- }
57
- function configOptionKey(raw) {
58
- const update = rawUpdate(raw);
59
- for (const key of ["configId", "optionId", "id", "key", "name"]) {
60
- if (typeof update?.[key] === "string" && update[key].trim())
61
- return update[key].trim();
62
- }
63
- return "";
64
- }
65
- function mergeRaw(previous, incoming, mergedText, firstSequence = sequenceStart(previous)) {
66
- const next = asRecord(incoming);
67
- if (!next)
68
- return incoming;
69
- const end = sequenceEnd(incoming);
70
- const count = sourceCount(previous) + sourceCount(incoming);
71
- const raw = {
72
- ...next,
73
- acaSequence: firstSequence,
74
- acaSequenceEnd: end,
75
- sourceEventCount: count
76
- };
77
- if (mergedText !== undefined) {
78
- const update = asRecord(next.update);
79
- if (update) {
80
- raw.update = {
81
- ...update,
82
- content: { type: "text", text: mergedText }
83
- };
84
- }
85
- }
86
- return raw;
87
- }
88
- function rawUpdate(raw) {
89
- return asRecord(asRecord(raw)?.update);
90
- }
91
- function sequenceStart(raw) {
92
- const value = asRecord(raw)?.acaSequence;
93
- return typeof value === "number" ? value : undefined;
94
- }
95
- function sequenceEnd(raw) {
96
- const record = asRecord(raw);
97
- const end = record?.acaSequenceEnd;
98
- if (typeof end === "number")
99
- return end;
100
- return typeof record?.acaSequence === "number" ? record.acaSequence : undefined;
101
- }
102
- function sourceCount(raw) {
103
- const value = asRecord(raw)?.sourceEventCount;
104
- return typeof value === "number" && value > 0 ? value : 1;
105
- }
106
- function asRecord(value) {
107
- return value && typeof value === "object" && !Array.isArray(value) ? value : null;
108
- }
1
+ const h=1024,_=new Set(["agent_message_chunk","agent_progress_chunk","agent_thought_chunk","user_message_chunk"]),T=new Set(["usage_update","session_info_update"]);function C(t,e){const n=t.at(-1);if(!n)return t.push(e),!1;if(S(n,e)){const r=`${n.text??""}${e.text??""}`;if(r.length<=1024)return n.text=r,n.raw=f(n.raw,e.raw,r),!0}if(E(n,e)){const r=d(n.raw);return n.label=e.label,n.text=e.text,n.status=e.status,n.toolCallId=e.toolCallId,n.raw=f(n.raw,e.raw,e.text,r),!0}return t.push(e),!1}function S(t,e){return _.has(t.eventType)&&t.eventType===e.eventType&&t.turnId===e.turnId&&t.label===e.label&&t.status===e.status&&!t.toolCallId&&!e.toolCallId&&typeof t.text=="string"&&typeof e.text=="string"}function E(t,e){if(t.eventType!==e.eventType||t.turnId!==e.turnId)return!1;if(T.has(t.eventType))return!0;if(t.eventType!=="config_option_update")return!1;const n=c(t.raw),r=c(e.raw);return!!n&&n===r}function c(t){const e=w(t);for(const n of["configId","optionId","id","key","name"])if(typeof e?.[n]=="string"&&e[n].trim())return e[n].trim();return""}function f(t,e,n,r=d(t)){const u=a(e);if(!u)return e;const p=x(e),y=l(t)+l(e),s={...u,acaSequence:r,acaSequenceEnd:p,sourceEventCount:y};if(n!==void 0){const o=a(u.update);o&&(s.update={...o,content:{type:"text",text:n}})}return s}function w(t){return a(a(t)?.update)}function d(t){const e=a(t)?.acaSequence;return typeof e=="number"?e:void 0}function x(t){const e=a(t),n=e?.acaSequenceEnd;return typeof n=="number"?n:typeof e?.acaSequence=="number"?e.acaSequence:void 0}function l(t){const e=a(t)?.sourceEventCount;return typeof e=="number"&&e>0?e:1}function a(t){return t&&typeof t=="object"&&!Array.isArray(t)?t:null}export{C as appendCoalescedAcpEvent};
@@ -1,16 +1 @@
1
- /** Matches the Server-side protocol marker filter before it reaches the batcher.
2
- * Keeping this marker out of the buffer prevents it from splitting adjacent
3
- * visible assistant text into separate coalescing groups. */
4
- export function shouldUploadAcpEvent(event) {
5
- if (event.eventType !== "agent_message_chunk" || event.text?.trim() !== "text")
6
- return true;
7
- const raw = asRecord(event.raw);
8
- if (!raw)
9
- return false;
10
- const update = asRecord(raw.update);
11
- const content = asRecord(update?.content);
12
- return !(content?.type === "text" && content.text === "text");
13
- }
14
- function asRecord(value) {
15
- return value && typeof value === "object" && !Array.isArray(value) ? value : null;
16
- }
1
+ function c(t){if(t.eventType!=="agent_message_chunk"||t.text?.trim()!=="text")return!0;const n=e(t.raw);if(!n)return!1;const o=e(n.update),r=e(o?.content);return!(r?.type==="text"&&r.text==="text")}function e(t){return t&&typeof t=="object"&&!Array.isArray(t)?t:null}export{c as shouldUploadAcpEvent};
@@ -1,91 +1 @@
1
- import fs from "node:fs";
2
- import { ACA_ACP_CHILD_ENV, ACA_ACP_OWNER_PID_ENV, ACA_ACP_OWNER_START_TICKS_ENV, ACA_ACP_WORKER_INSTANCE_ENV, ACA_WORKER_INSTANCE_ENV, readProcessStartTicks } from "./process-identity.js";
3
- export async function cleanupOrphanedAcpProcesses() {
4
- if (process.platform !== "linux")
5
- return { inspected: 0, terminated: 0, pids: [] };
6
- let inspected = 0;
7
- const pids = [];
8
- for (const entry of safeReadDir("/proc")) {
9
- if (!/^\d+$/.test(entry))
10
- continue;
11
- const pid = Number(entry);
12
- if (!Number.isInteger(pid) || pid <= 1 || pid === process.pid)
13
- continue;
14
- const environment = readProcessEnvironment(pid);
15
- if (environmentValue(environment, ACA_ACP_CHILD_ENV) !== "1")
16
- continue;
17
- inspected += 1;
18
- const ownerPid = Number.parseInt(environmentValue(environment, ACA_ACP_OWNER_PID_ENV), 10);
19
- const ownerStartTicks = environmentValue(environment, ACA_ACP_OWNER_START_TICKS_ENV);
20
- const workerInstanceId = environmentValue(environment, ACA_ACP_WORKER_INSTANCE_ENV);
21
- if (isMatchingWorkerProcess(ownerPid, ownerStartTicks, workerInstanceId))
22
- continue;
23
- try {
24
- process.kill(pid, "SIGTERM");
25
- pids.push(pid);
26
- }
27
- catch {
28
- // Process may have exited between /proc inspection and signal delivery.
29
- }
30
- }
31
- if (pids.length > 0) {
32
- await delay(2_000);
33
- for (const pid of pids) {
34
- if (!isProcessAlive(pid))
35
- continue;
36
- try {
37
- process.kill(pid, "SIGKILL");
38
- }
39
- catch {
40
- // Process may have exited after the liveness check.
41
- }
42
- }
43
- }
44
- return { inspected, terminated: pids.length, pids };
45
- }
46
- function readProcessEnvironment(pid) {
47
- try {
48
- return fs.readFileSync(`/proc/${pid}/environ`, "utf8").split("\0").filter(Boolean);
49
- }
50
- catch {
51
- return [];
52
- }
53
- }
54
- function isMatchingWorkerProcess(pid, expectedStartTicks, expectedInstanceId) {
55
- if (!Number.isInteger(pid) || pid <= 1 || !expectedStartTicks)
56
- return false;
57
- try {
58
- process.kill(pid, 0);
59
- if (readProcessStartTicks(pid) !== expectedStartTicks)
60
- return false;
61
- const ownerInstanceId = environmentValue(readProcessEnvironment(pid), ACA_WORKER_INSTANCE_ENV);
62
- return !expectedInstanceId || !ownerInstanceId || ownerInstanceId === expectedInstanceId;
63
- }
64
- catch {
65
- return false;
66
- }
67
- }
68
- function environmentValue(environment, name) {
69
- const prefix = `${name}=`;
70
- return environment.find((value) => value.startsWith(prefix))?.slice(prefix.length) ?? "";
71
- }
72
- function isProcessAlive(pid) {
73
- try {
74
- process.kill(pid, 0);
75
- return true;
76
- }
77
- catch {
78
- return false;
79
- }
80
- }
81
- function safeReadDir(directory) {
82
- try {
83
- return fs.readdirSync(directory);
84
- }
85
- catch {
86
- return [];
87
- }
88
- }
89
- function delay(ms) {
90
- return new Promise((resolve) => setTimeout(resolve, ms));
91
- }
1
+ import i from"node:fs";import{ACA_ACP_CHILD_ENV as l,ACA_ACP_OWNER_PID_ENV as p,ACA_ACP_OWNER_START_TICKS_ENV as _,ACA_ACP_WORKER_INSTANCE_ENV as A,ACA_WORKER_INSTANCE_ENV as m,readProcessStartTicks as d}from"./process-identity.js";async function y(){if(process.platform!=="linux")return{inspected:0,terminated:0,pids:[]};let r=0;const e=[];for(const n of C("/proc")){if(!/^\d+$/.test(n))continue;const t=Number(n);if(!Number.isInteger(t)||t<=1||t===process.pid)continue;const o=c(t);if(s(o,l)!=="1")continue;r+=1;const u=Number.parseInt(s(o,p),10),f=s(o,_),a=s(o,A);if(!N(u,f,a))try{process.kill(t,"SIGTERM"),e.push(t)}catch{}}if(e.length>0){await E(2e3);for(const n of e)if(h(n))try{process.kill(n,"SIGKILL")}catch{}}return{inspected:r,terminated:e.length,pids:e}}function c(r){try{return i.readFileSync(`/proc/${r}/environ`,"utf8").split("\0").filter(Boolean)}catch{return[]}}function N(r,e,n){if(!Number.isInteger(r)||r<=1||!e)return!1;try{if(process.kill(r,0),d(r)!==e)return!1;const t=s(c(r),m);return!n||!t||t===n}catch{return!1}}function s(r,e){const n=`${e}=`;return r.find(t=>t.startsWith(n))?.slice(n.length)??""}function h(r){try{return process.kill(r,0),!0}catch{return!1}}function C(r){try{return i.readdirSync(r)}catch{return[]}}function E(r){return new Promise(e=>setTimeout(e,r))}export{y as cleanupOrphanedAcpProcesses};
@@ -1,268 +1,2 @@
1
- import { execFileSync } from "node:child_process";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- import crypto from "node:crypto";
5
- const MAX_HASH_BYTES = 50 * 1024 * 1024;
6
- const PATH_KEYS = ["path", "file", "filePath", "file_path", "filename", "targetPath", "target_path", "move_path"];
7
- export function captureGitSnapshot(cwd) {
8
- if (!isGitWorktree(cwd))
9
- return null;
10
- const entries = parseGitStatus(cwd);
11
- if (!entries)
12
- return null;
13
- const snapshot = new Map();
14
- for (const entry of entries) {
15
- snapshot.set(entry.path, {
16
- status: entry.status,
17
- objectHash: entry.status.includes("D") ? null : gitObjectHash(cwd, entry.path)
18
- });
19
- }
20
- return snapshot;
21
- }
22
- export function collectAffectedFiles(input) {
23
- const items = new Map();
24
- for (const filePath of input.evidencePaths) {
25
- addAffectedFile(items, input.cwd, filePath, "unknown", "acp_tool");
26
- }
27
- return [...items.values()]
28
- .filter((item) => item.relativePath && !item.relativePath.startsWith(".git/"))
29
- .sort((a, b) => a.relativePath.localeCompare(b.relativePath));
30
- }
31
- export function extractAffectedFilePathsFromAcpRaw(raw) {
32
- const paths = new Set();
33
- visitRaw(raw, (value, key) => {
34
- if (typeof value !== "string")
35
- return;
36
- if (looksLikeFilePathKey(key))
37
- addPathCandidate(paths, value);
38
- if (key === "command") {
39
- for (const filePath of extractShellWritePaths(value))
40
- addPathCandidate(paths, filePath);
41
- }
42
- });
43
- visitDiffBlocks(raw, paths);
44
- visitRawChanges(raw, paths);
45
- return [...paths];
46
- }
47
- function addAffectedFile(items, cwd, filePath, changeType, source) {
48
- const resolved = resolveInside(cwd, filePath);
49
- if (!resolved)
50
- return;
51
- const existing = items.get(resolved.relativePath);
52
- const stat = safeStat(resolved.absolutePath);
53
- const next = {
54
- path: resolved.absolutePath,
55
- absolutePath: resolved.absolutePath,
56
- relativePath: resolved.relativePath,
57
- changeType: mergeChangeType(existing?.changeType, changeType),
58
- source: existing?.source === "acp_tool" || existing?.source === "acp_diff" ? existing.source : source,
59
- exists: Boolean(stat?.isFile()),
60
- downloadable: Boolean(stat?.isFile()),
61
- ...(stat?.isFile() ? {
62
- size: stat.size,
63
- mime: mimeForPath(resolved.absolutePath),
64
- sha256: stat.size <= MAX_HASH_BYTES ? sha256File(resolved.absolutePath) : undefined
65
- } : {})
66
- };
67
- items.set(resolved.relativePath, next);
68
- }
69
- function mergeChangeType(current, next) {
70
- if (!current || current === "unknown")
71
- return next;
72
- if (next === "unknown")
73
- return current;
74
- if (current === next)
75
- return current;
76
- if (current === "created" && next === "modified")
77
- return "created";
78
- return next;
79
- }
80
- function resolveInside(root, filePath) {
81
- if (!filePath || filePath.includes("\0"))
82
- return null;
83
- const rootAbs = path.resolve(root);
84
- const absolutePath = path.isAbsolute(filePath) ? path.resolve(filePath) : path.resolve(rootAbs, filePath);
85
- const relativePath = path.relative(rootAbs, absolutePath).replace(/\\/g, "/");
86
- if (!relativePath || relativePath.startsWith("..") || path.isAbsolute(relativePath))
87
- return null;
88
- return { absolutePath, relativePath };
89
- }
90
- function isGitWorktree(cwd) {
91
- try {
92
- execFileSync("git", ["-C", cwd, "rev-parse", "--is-inside-work-tree"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
93
- return true;
94
- }
95
- catch {
96
- return false;
97
- }
98
- }
99
- function parseGitStatus(cwd) {
100
- try {
101
- const output = execFileSync("git", ["-C", cwd, "status", "--porcelain=v1", "-z", "--untracked-files=all"], {
102
- encoding: "buffer",
103
- maxBuffer: 20 * 1024 * 1024
104
- });
105
- const parts = output.toString("utf8").split("\0").filter(Boolean);
106
- const entries = [];
107
- for (let index = 0; index < parts.length; index += 1) {
108
- const item = parts[index];
109
- const status = item.slice(0, 2);
110
- const rest = item.slice(3);
111
- if (!rest)
112
- continue;
113
- if (status.startsWith("R") || status.startsWith("C")) {
114
- const target = parts[index + 1];
115
- if (target) {
116
- entries.push({ status, path: target });
117
- index += 1;
118
- }
119
- }
120
- else {
121
- entries.push({ status, path: rest });
122
- }
123
- }
124
- return entries;
125
- }
126
- catch {
127
- return null;
128
- }
129
- }
130
- function gitObjectHash(cwd, relativePath) {
131
- try {
132
- const output = execFileSync("git", ["-C", cwd, "hash-object", "--", relativePath], {
133
- encoding: "utf8",
134
- stdio: ["ignore", "pipe", "ignore"],
135
- maxBuffer: 1024 * 1024
136
- }).trim();
137
- return /^[0-9a-f]{40,64}$/i.test(output) ? output : null;
138
- }
139
- catch {
140
- return null;
141
- }
142
- }
143
- function visitRaw(value, visitor, key, depth = 0) {
144
- if (depth > 8)
145
- return;
146
- visitor(value, key);
147
- if (!value || typeof value !== "object")
148
- return;
149
- if (Array.isArray(value)) {
150
- for (const item of value)
151
- visitRaw(item, visitor, undefined, depth + 1);
152
- return;
153
- }
154
- for (const [childKey, childValue] of Object.entries(value)) {
155
- visitRaw(childValue, visitor, childKey, depth + 1);
156
- }
157
- }
158
- function visitDiffBlocks(raw, paths) {
159
- visitRaw(raw, (value) => {
160
- if (!value || typeof value !== "object" || Array.isArray(value))
161
- return;
162
- const record = value;
163
- if (record.type !== "diff")
164
- return;
165
- addPathCandidate(paths, record.path);
166
- });
167
- }
168
- function visitRawChanges(raw, paths) {
169
- visitRaw(raw, (value, key) => {
170
- if (key !== "changes" || !value || typeof value !== "object" || Array.isArray(value))
171
- return;
172
- for (const changePath of Object.keys(value)) {
173
- addPathCandidate(paths, changePath);
174
- }
175
- });
176
- }
177
- function looksLikeFilePathKey(key) {
178
- if (!key)
179
- return false;
180
- return PATH_KEYS.includes(key);
181
- }
182
- function addPathCandidate(paths, value) {
183
- if (typeof value !== "string")
184
- return;
185
- const trimmed = cleanPathCandidate(value);
186
- if (!trimmed || trimmed.length > 500 || trimmed.includes("\n"))
187
- return;
188
- if (trimmed === "." || trimmed === "..")
189
- return;
190
- paths.add(trimmed);
191
- }
192
- function cleanPathCandidate(value) {
193
- // ACP/diff 输出里有时会把 “+19/-7 source=...” 拼在路径后面,这里只保留真实文件路径。
194
- let candidate = value.trim();
195
- candidate = candidate.replace(/\s+\+\d+\s*\/\s*-\d+(?:\s+.*)?$/, "");
196
- candidate = candidate.replace(/\s+\(\s*\+\d+\s*[,/ ]\s*-\d+\s*\)(?:\s+.*)?$/, "");
197
- candidate = candidate.replace(/\s+(?:source|event|workspace|session|last)=\S+.*$/, "");
198
- const token = candidate.split(/\s+/)[0] || "";
199
- if (token && /\.[A-Za-z][A-Za-z0-9+-]{0,12}$/.test(token))
200
- candidate = token;
201
- return candidate.replace(/^["']|["']$/g, "").trim();
202
- }
203
- function extractShellWritePaths(command) {
204
- const paths = [];
205
- const redirectPattern = /(?:^|\s)(?:\d?>|>>)\s*(?:"([^"]+)"|'([^']+)'|([^\s;&|<>]+))/g;
206
- let match;
207
- while ((match = redirectPattern.exec(command))) {
208
- const value = match[1] || match[2] || match[3];
209
- if (value)
210
- paths.push(value);
211
- }
212
- const touchPattern = /(?:^|\s)touch\s+(?:"([^"]+)"|'([^']+)'|([^\s;&|<>]+))/g;
213
- while ((match = touchPattern.exec(command))) {
214
- const value = match[1] || match[2] || match[3];
215
- if (value && !value.startsWith("-"))
216
- paths.push(value);
217
- }
218
- return paths;
219
- }
220
- function safeStat(filePath) {
221
- try {
222
- return fs.statSync(filePath);
223
- }
224
- catch {
225
- return null;
226
- }
227
- }
228
- function sha256File(filePath) {
229
- try {
230
- return crypto.createHash("sha256").update(fs.readFileSync(filePath)).digest("hex");
231
- }
232
- catch {
233
- return undefined;
234
- }
235
- }
236
- function mimeForPath(filePath) {
237
- switch (path.extname(filePath).toLowerCase()) {
238
- case ".pdf":
239
- return "application/pdf";
240
- case ".md":
241
- case ".markdown":
242
- return "text/markdown; charset=utf-8";
243
- case ".txt":
244
- case ".log":
245
- return "text/plain; charset=utf-8";
246
- case ".json":
247
- return "application/json; charset=utf-8";
248
- case ".csv":
249
- return "text/csv; charset=utf-8";
250
- case ".png":
251
- return "image/png";
252
- case ".jpg":
253
- case ".jpeg":
254
- return "image/jpeg";
255
- case ".gif":
256
- return "image/gif";
257
- case ".webp":
258
- return "image/webp";
259
- case ".zip":
260
- return "application/zip";
261
- case ".docx":
262
- return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
263
- case ".xlsx":
264
- return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
265
- default:
266
- return "application/octet-stream";
267
- }
268
- }
1
+ import{execFileSync as p}from"node:child_process";import h from"node:fs";import c from"node:path";import d from"node:crypto";const g=50*1024*1024,m=["path","file","filePath","file_path","filename","targetPath","target_path","move_path"];function T(t){if(!x(t))return null;const e=w(t);if(!e)return null;const r=new Map;for(const n of e)r.set(n.path,{status:n.status,objectHash:n.status.includes("D")?null:A(t,n.path)});return r}function R(t){const e=new Map;for(const r of t.evidencePaths)P(e,t.cwd,r,"unknown","acp_tool");return[...e.values()].filter(r=>r.relativePath&&!r.relativePath.startsWith(".git/")).sort((r,n)=>r.relativePath.localeCompare(n.relativePath))}function G(t){const e=new Set;return u(t,(r,n)=>{if(typeof r=="string"&&(S(n)&&l(e,r),n==="command"))for(const s of F(r))l(e,s)}),v(t,e),j(t,e),[...e]}function P(t,e,r,n,s){const i=b(e,r);if(!i)return;const a=t.get(i.relativePath),o=C(i.absolutePath),f={path:i.absolutePath,absolutePath:i.absolutePath,relativePath:i.relativePath,changeType:y(a?.changeType,n),source:a?.source==="acp_tool"||a?.source==="acp_diff"?a.source:s,exists:!!o?.isFile(),downloadable:!!o?.isFile(),...o?.isFile()?{size:o.size,mime:z(i.absolutePath),sha256:o.size<=g?_(i.absolutePath):void 0}:{}};t.set(i.relativePath,f)}function y(t,e){return!t||t==="unknown"?e:e==="unknown"||t===e?t:t==="created"&&e==="modified"?"created":e}function b(t,e){if(!e||e.includes("\0"))return null;const r=c.resolve(t),n=c.isAbsolute(e)?c.resolve(e):c.resolve(r,e),s=c.relative(r,n).replace(/\\/g,"/");return!s||s.startsWith("..")||c.isAbsolute(s)?null:{absolutePath:n,relativePath:s}}function x(t){try{return p("git",["-C",t,"rev-parse","--is-inside-work-tree"],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}),!0}catch{return!1}}function w(t){try{const r=p("git",["-C",t,"status","--porcelain=v1","-z","--untracked-files=all"],{encoding:"buffer",maxBuffer:20971520}).toString("utf8").split("\0").filter(Boolean),n=[];for(let s=0;s<r.length;s+=1){const i=r[s],a=i.slice(0,2),o=i.slice(3);if(o)if(a.startsWith("R")||a.startsWith("C")){const f=r[s+1];f&&(n.push({status:a,path:f}),s+=1)}else n.push({status:a,path:o})}return n}catch{return null}}function A(t,e){try{const r=p("git",["-C",t,"hash-object","--",e],{encoding:"utf8",stdio:["ignore","pipe","ignore"],maxBuffer:1048576}).trim();return/^[0-9a-f]{40,64}$/i.test(r)?r:null}catch{return null}}function u(t,e,r,n=0){if(!(n>8)&&(e(t,r),!(!t||typeof t!="object"))){if(Array.isArray(t)){for(const s of t)u(s,e,void 0,n+1);return}for(const[s,i]of Object.entries(t))u(i,e,s,n+1)}}function v(t,e){u(t,r=>{if(!r||typeof r!="object"||Array.isArray(r))return;const n=r;n.type==="diff"&&l(e,n.path)})}function j(t,e){u(t,(r,n)=>{if(!(n!=="changes"||!r||typeof r!="object"||Array.isArray(r)))for(const s of Object.keys(r))l(e,s)})}function S(t){return t?m.includes(t):!1}function l(t,e){if(typeof e!="string")return;const r=k(e);!r||r.length>500||r.includes(`
2
+ `)||r==="."||r===".."||t.add(r)}function k(t){let e=t.trim();e=e.replace(/\s+\+\d+\s*\/\s*-\d+(?:\s+.*)?$/,""),e=e.replace(/\s+\(\s*\+\d+\s*[,/ ]\s*-\d+\s*\)(?:\s+.*)?$/,""),e=e.replace(/\s+(?:source|event|workspace|session|last)=\S+.*$/,"");const r=e.split(/\s+/)[0]||"";return r&&/\.[A-Za-z][A-Za-z0-9+-]{0,12}$/.test(r)&&(e=r),e.replace(/^["']|["']$/g,"").trim()}function F(t){const e=[],r=/(?:^|\s)(?:\d?>|>>)\s*(?:"([^"]+)"|'([^']+)'|([^\s;&|<>]+))/g;let n;for(;n=r.exec(t);){const i=n[1]||n[2]||n[3];i&&e.push(i)}const s=/(?:^|\s)touch\s+(?:"([^"]+)"|'([^']+)'|([^\s;&|<>]+))/g;for(;n=s.exec(t);){const i=n[1]||n[2]||n[3];i&&!i.startsWith("-")&&e.push(i)}return e}function C(t){try{return h.statSync(t)}catch{return null}}function _(t){try{return d.createHash("sha256").update(h.readFileSync(t)).digest("hex")}catch{return}}function z(t){switch(c.extname(t).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".json":return"application/json; charset=utf-8";case".csv":return"text/csv; 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{T as captureGitSnapshot,R as collectAffectedFiles,G as extractAffectedFilePathsFromAcpRaw};
package/dist/core/auth.js CHANGED
@@ -1,36 +1 @@
1
- import { z } from "zod";
2
- import { readJsonFile, writeJsonFile } from "./fs.js";
3
- import { getCredentialsPath } from "./paths.js";
4
- const CredentialsSchema = z.object({
5
- token: z.string().optional(),
6
- user: z
7
- .object({
8
- id: z.string().optional(),
9
- email: z.string().optional()
10
- })
11
- .passthrough()
12
- .optional(),
13
- machine: z
14
- .object({
15
- machineId: z.string().optional(),
16
- machineName: z.string().optional()
17
- })
18
- .passthrough()
19
- .optional()
20
- }).passthrough();
21
- export function loadCredentials() {
22
- const raw = readJsonFile(getCredentialsPath());
23
- if (!raw)
24
- return null;
25
- const parsed = CredentialsSchema.safeParse(raw);
26
- return parsed.success ? parsed.data : null;
27
- }
28
- export function saveApiKeyCredential(apiKey, machineName) {
29
- const credentials = {
30
- token: apiKey,
31
- user: { id: "local-user" },
32
- machine: { machineId: "local-machine", machineName }
33
- };
34
- writeJsonFile(getCredentialsPath(), credentials);
35
- return credentials;
36
- }
1
+ import{z as e}from"zod";import{readJsonFile as i,writeJsonFile as r}from"./fs.js";import{getCredentialsPath as a}from"./paths.js";const s=e.object({token:e.string().optional(),user:e.object({id:e.string().optional(),email:e.string().optional()}).passthrough().optional(),machine:e.object({machineId:e.string().optional(),machineName:e.string().optional()}).passthrough().optional()}).passthrough();function m(){const t=i(a());if(!t)return null;const o=s.safeParse(t);return o.success?o.data:null}function h(t,o){const n={token:t,user:{id:"local-user"},machine:{machineId:"local-machine",machineName:o}};return r(a(),n),n}export{m as loadCredentials,h as saveApiKeyCredential};