@aria-cli/tools 1.0.9 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +9 -5
- package/src/__tests__/web-fetch-download.test.ts +0 -433
- package/src/__tests__/web-tools.test.ts +0 -619
- package/src/ask-user-interaction.ts +0 -33
- package/src/cache/web-cache.ts +0 -110
- package/src/definitions/arion.ts +0 -118
- package/src/definitions/browser/browser.ts +0 -502
- package/src/definitions/browser/index.ts +0 -5
- package/src/definitions/browser/pw-downloads.ts +0 -142
- package/src/definitions/browser/pw-interactions.ts +0 -282
- package/src/definitions/browser/pw-responses.ts +0 -98
- package/src/definitions/browser/pw-session.ts +0 -405
- package/src/definitions/browser/pw-shared.ts +0 -85
- package/src/definitions/browser/pw-snapshot.ts +0 -383
- package/src/definitions/browser/pw-state.ts +0 -101
- package/src/definitions/browser/types.ts +0 -203
- package/src/definitions/code-intelligence.ts +0 -526
- package/src/definitions/core.ts +0 -118
- package/src/definitions/delegation.ts +0 -567
- package/src/definitions/deploy.ts +0 -73
- package/src/definitions/filesystem.ts +0 -217
- package/src/definitions/frg.ts +0 -67
- package/src/definitions/index.ts +0 -28
- package/src/definitions/memory.ts +0 -150
- package/src/definitions/messaging.ts +0 -734
- package/src/definitions/meta.ts +0 -392
- package/src/definitions/network.ts +0 -179
- package/src/definitions/outlook.ts +0 -318
- package/src/definitions/patch/apply-patch.ts +0 -235
- package/src/definitions/patch/fuzzy-match.ts +0 -217
- package/src/definitions/patch/index.ts +0 -1
- package/src/definitions/patch/patch-parser.ts +0 -297
- package/src/definitions/patch/sandbox-paths.ts +0 -129
- package/src/definitions/process/index.ts +0 -5
- package/src/definitions/process/process-registry.ts +0 -303
- package/src/definitions/process/process.ts +0 -456
- package/src/definitions/process/pty-keys.ts +0 -298
- package/src/definitions/process/session-slug.ts +0 -147
- package/src/definitions/quip.ts +0 -225
- package/src/definitions/search.ts +0 -67
- package/src/definitions/session-history.ts +0 -79
- package/src/definitions/shell.ts +0 -202
- package/src/definitions/slack.ts +0 -211
- package/src/definitions/web.ts +0 -119
- package/src/executors/apply-patch.ts +0 -1035
- package/src/executors/arion.ts +0 -199
- package/src/executors/code-intelligence.ts +0 -1179
- package/src/executors/deploy.ts +0 -1066
- package/src/executors/filesystem.ts +0 -1428
- package/src/executors/frg-freshness.ts +0 -743
- package/src/executors/frg.ts +0 -394
- package/src/executors/index.ts +0 -280
- package/src/executors/learning-meta.ts +0 -1367
- package/src/executors/lsp-client.ts +0 -355
- package/src/executors/memory.ts +0 -978
- package/src/executors/meta.ts +0 -293
- package/src/executors/process-registry.ts +0 -570
- package/src/executors/pty-session-store.ts +0 -43
- package/src/executors/pty.ts +0 -342
- package/src/executors/restart.ts +0 -133
- package/src/executors/search-freshness.ts +0 -249
- package/src/executors/search-types.ts +0 -98
- package/src/executors/search.ts +0 -89
- package/src/executors/self-diagnose.ts +0 -552
- package/src/executors/session-history.ts +0 -435
- package/src/executors/shell-safety.ts +0 -519
- package/src/executors/shell.ts +0 -1243
- package/src/executors/utils.ts +0 -40
- package/src/executors/web.ts +0 -786
- package/src/extraction/content-extraction.ts +0 -281
- package/src/extraction/index.ts +0 -5
- package/src/headless-control-contract.ts +0 -1149
- package/src/index.ts +0 -788
- package/src/local-control-http-auth.ts +0 -2
- package/src/mcp/client.ts +0 -218
- package/src/mcp/connection.ts +0 -568
- package/src/mcp/index.ts +0 -11
- package/src/mcp/jsonrpc.ts +0 -195
- package/src/mcp/types.ts +0 -199
- package/src/network-control-adapter.ts +0 -88
- package/src/network-runtime/address-types.ts +0 -218
- package/src/network-runtime/db-owner-fencing.ts +0 -91
- package/src/network-runtime/delivery-receipts.ts +0 -372
- package/src/network-runtime/direct-endpoint-authority.ts +0 -35
- package/src/network-runtime/index.ts +0 -316
- package/src/network-runtime/local-control-contract.ts +0 -784
- package/src/network-runtime/node-store-contract.ts +0 -46
- package/src/network-runtime/pair-route-contract.ts +0 -97
- package/src/network-runtime/peer-capabilities.ts +0 -48
- package/src/network-runtime/peer-principal-ref.ts +0 -20
- package/src/network-runtime/peer-state-machine.ts +0 -160
- package/src/network-runtime/protocol-schemas.ts +0 -265
- package/src/network-runtime/runtime-bootstrap-contract.ts +0 -83
- package/src/outlook/desktop-session.ts +0 -409
- package/src/policy.ts +0 -171
- package/src/providers/brave.ts +0 -80
- package/src/providers/duckduckgo.ts +0 -199
- package/src/providers/exa.ts +0 -85
- package/src/providers/firecrawl.ts +0 -77
- package/src/providers/index.ts +0 -8
- package/src/providers/jina.ts +0 -70
- package/src/providers/router.ts +0 -121
- package/src/providers/search-provider.ts +0 -74
- package/src/providers/tavily.ts +0 -74
- package/src/quip/desktop-session.ts +0 -435
- package/src/registry/index.ts +0 -1
- package/src/registry/registry.ts +0 -905
- package/src/runtime-socket-local-control-client.ts +0 -632
- package/src/security/dns-normalization.ts +0 -34
- package/src/security/dns-pinning.ts +0 -138
- package/src/security/external-content.ts +0 -129
- package/src/security/ssrf.ts +0 -207
- package/src/slack/desktop-session.ts +0 -493
- package/src/tool-factory.ts +0 -91
- package/src/types.ts +0 -1341
- package/src/utils/retry.ts +0 -163
- package/src/utils/safe-parse-json.ts +0 -176
- package/src/utils/url.ts +0 -20
- package/tests/benchmarks/registry.bench.ts +0 -57
- package/tests/cache/web-cache.test.ts +0 -147
- package/tests/critical-integration.test.ts +0 -1465
- package/tests/definitions/apply-patch.test.ts +0 -586
- package/tests/definitions/browser.test.ts +0 -495
- package/tests/definitions/delegation-pause-resume.test.ts +0 -758
- package/tests/definitions/execution.test.ts +0 -671
- package/tests/definitions/messaging-inbox-scope.test.ts +0 -229
- package/tests/definitions/messaging.test.ts +0 -1468
- package/tests/definitions/outlook.test.ts +0 -30
- package/tests/definitions/process.test.ts +0 -469
- package/tests/definitions/slack.test.ts +0 -28
- package/tests/definitions/tool-inventory.test.ts +0 -218
- package/tests/e2e/delegation-quest-orchestration.e2e.test.ts +0 -433
- package/tests/e2e/memory-tool-discovery-contract.e2e.test.ts +0 -81
- package/tests/executors/apply-patch.test.ts +0 -538
- package/tests/executors/arion.test.ts +0 -309
- package/tests/executors/conversation-primitives.test.ts +0 -250
- package/tests/executors/deploy.test.ts +0 -746
- package/tests/executors/filesystem-tools.test.ts +0 -357
- package/tests/executors/filesystem.test.ts +0 -959
- package/tests/executors/frg-freshness.test.ts +0 -136
- package/tests/executors/frg-merge.test.ts +0 -70
- package/tests/executors/frg-session-content.test.ts +0 -40
- package/tests/executors/frg.test.ts +0 -56
- package/tests/executors/memory-bugfixes.test.ts +0 -257
- package/tests/executors/memory-real-memoria.integration.test.ts +0 -316
- package/tests/executors/memory.test.ts +0 -853
- package/tests/executors/meta-tools.test.ts +0 -411
- package/tests/executors/meta.test.ts +0 -683
- package/tests/executors/path-containment.test.ts +0 -51
- package/tests/executors/process-registry.test.ts +0 -505
- package/tests/executors/pty.test.ts +0 -664
- package/tests/executors/quest-security.test.ts +0 -249
- package/tests/executors/read-file-media.test.ts +0 -230
- package/tests/executors/recall-knowledge-schema.test.ts +0 -209
- package/tests/executors/recall-tags.test.ts +0 -278
- package/tests/executors/remember-null-safety.contract.test.ts +0 -41
- package/tests/executors/restart.test.ts +0 -67
- package/tests/executors/search-unified.test.ts +0 -381
- package/tests/executors/session-history.test.ts +0 -340
- package/tests/executors/session-transcript.test.ts +0 -561
- package/tests/executors/shell-abort.test.ts +0 -416
- package/tests/executors/shell-env-blocklist.test.ts +0 -648
- package/tests/executors/shell-env-process.test.ts +0 -245
- package/tests/executors/shell-process-registry.test.ts +0 -334
- package/tests/executors/shell-tools.test.ts +0 -393
- package/tests/executors/shell.test.ts +0 -690
- package/tests/executors/web-abort-vs-timeout.test.ts +0 -213
- package/tests/executors/web-integration.test.ts +0 -633
- package/tests/executors/web-symlink.test.ts +0 -18
- package/tests/executors/web.test.ts +0 -1400
- package/tests/executors/write-stdin.test.ts +0 -145
- package/tests/extraction/content-extraction.test.ts +0 -153
- package/tests/guards/tools-default-test-lane.integration.test.ts +0 -21
- package/tests/guards/tools-package-test-commands.e2e.test.ts +0 -43
- package/tests/guards/tools-test-lane-manifest.contract.test.ts +0 -76
- package/tests/guards/tools-vitest-workspace-alias.contract.test.ts +0 -63
- package/tests/helpers/async-waits.ts +0 -53
- package/tests/integration/headless-control-contract.integration.test.ts +0 -153
- package/tests/integration/memory-tool-schema-parity.integration.test.ts +0 -67
- package/tests/integration/meta-tools-round-trip.integration.test.ts +0 -506
- package/tests/integration/quest-round-trip.test.ts +0 -303
- package/tests/integration/registry-executor-flow.test.ts +0 -85
- package/tests/integration.test.ts +0 -177
- package/tests/loading-tier.test.ts +0 -126
- package/tests/mcp/client-reconnect.test.ts +0 -267
- package/tests/mcp/connection.test.ts +0 -846
- package/tests/mcp/injectable-logger.test.ts +0 -83
- package/tests/mcp/jsonrpc.test.ts +0 -109
- package/tests/mcp/lifecycle.test.ts +0 -879
- package/tests/network-runtime/address-types.contract.test.ts +0 -143
- package/tests/network-runtime/continuity-bind-schema.contract.test.ts +0 -203
- package/tests/network-runtime/local-control-contract.test.ts +0 -869
- package/tests/network-runtime/local-control-invite-token.contract.test.ts +0 -146
- package/tests/network-runtime/node-store-contract.test.ts +0 -11
- package/tests/network-runtime/pair-protocol-nodeid.contract.test.ts +0 -15
- package/tests/network-runtime/peer-state-machine.contract.test.ts +0 -148
- package/tests/network-runtime/protocol-schemas.contract.test.ts +0 -512
- package/tests/network-runtime/relay-pending-nodeid.contract.test.ts +0 -62
- package/tests/network-runtime/runtime-bootstrap-contract.test.ts +0 -227
- package/tests/network-runtime/runtime-socket-local-control-client.test.ts +0 -621
- package/tests/network-runtime/wait-for-message-script.test.ts +0 -288
- package/tests/parallel.test.ts +0 -71
- package/tests/policy.test.ts +0 -184
- package/tests/print-default-test-lane.ts +0 -14
- package/tests/print-test-lane-manifest.ts +0 -22
- package/tests/providers/brave.test.ts +0 -159
- package/tests/providers/duckduckgo.test.ts +0 -207
- package/tests/providers/exa.test.ts +0 -175
- package/tests/providers/firecrawl.test.ts +0 -168
- package/tests/providers/jina.test.ts +0 -144
- package/tests/providers/router.test.ts +0 -328
- package/tests/providers/tavily.test.ts +0 -165
- package/tests/registry/discovery.test.ts +0 -154
- package/tests/registry/injectable-logger.test.ts +0 -230
- package/tests/registry/input-validation.test.ts +0 -361
- package/tests/registry/interface-completeness.test.ts +0 -85
- package/tests/registry/mcp-integration.test.ts +0 -103
- package/tests/registry/mcp-read-only-hint.test.ts +0 -60
- package/tests/registry/memoria-discovery.test.ts +0 -390
- package/tests/registry/nested-validation.test.ts +0 -283
- package/tests/registry/pseudo-tool-filtering.test.ts +0 -258
- package/tests/registry/registration-lifecycle.test.ts +0 -133
- package/tests/registry-validation.test.ts +0 -424
- package/tests/registry.test.ts +0 -460
- package/tests/security/dns-pinning.test.ts +0 -162
- package/tests/security/external-content.test.ts +0 -144
- package/tests/security/ssrf.test.ts +0 -118
- package/tests/shell-safety-integration.test.ts +0 -32
- package/tests/shell-safety.test.ts +0 -365
- package/tests/slack/desktop-session.test.ts +0 -50
- package/tests/test-lane-manifest.ts +0 -440
- package/tests/test-utils.ts +0 -27
- package/tests/tool-factory.test.ts +0 -188
- package/tests/utils/retry.test.ts +0 -231
- package/tests/utils/url.test.ts +0 -63
- package/tsconfig.cjs.json +0 -24
- package/tsconfig.json +0 -12
- package/vitest.config.ts +0 -55
- package/vitest.e2e.config.ts +0 -24
- package/vitest.integration.config.ts +0 -24
- package/vitest.native.config.ts +0 -24
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
// Originally from OpenClaw (MIT License, Copyright 2025 Peter Steinberger)
|
|
2
|
-
// https://github.com/openclaw/openclaw
|
|
3
|
-
// Modified for ARIA — stripped OpenClaw runtime, rewired to ARIA Tool interface
|
|
4
|
-
|
|
5
|
-
import fs from "node:fs/promises";
|
|
6
|
-
|
|
7
|
-
type UpdateFileChunk = {
|
|
8
|
-
changeContext?: string;
|
|
9
|
-
oldLines: string[];
|
|
10
|
-
newLines: string[];
|
|
11
|
-
isEndOfFile: boolean;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
async function defaultReadFile(filePath: string): Promise<string> {
|
|
15
|
-
return fs.readFile(filePath, "utf8");
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export async function applyUpdateHunk(
|
|
19
|
-
filePath: string,
|
|
20
|
-
chunks: UpdateFileChunk[],
|
|
21
|
-
options?: { readFile?: (filePath: string) => Promise<string> },
|
|
22
|
-
): Promise<string> {
|
|
23
|
-
const reader = options?.readFile ?? defaultReadFile;
|
|
24
|
-
const originalContents = await reader(filePath).catch((err) => {
|
|
25
|
-
throw new Error(`Failed to read file to update ${filePath}: ${err}`);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
const originalLines = originalContents.split("\n");
|
|
29
|
-
if (originalLines.length > 0 && originalLines[originalLines.length - 1] === "") {
|
|
30
|
-
originalLines.pop();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const replacements = computeReplacements(originalLines, filePath, chunks);
|
|
34
|
-
let newLines = applyReplacements(originalLines, replacements);
|
|
35
|
-
if (newLines.length === 0 || newLines[newLines.length - 1] !== "") {
|
|
36
|
-
newLines = [...newLines, ""];
|
|
37
|
-
}
|
|
38
|
-
return newLines.join("\n");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function computeReplacements(
|
|
42
|
-
originalLines: string[],
|
|
43
|
-
filePath: string,
|
|
44
|
-
chunks: UpdateFileChunk[],
|
|
45
|
-
): Array<[number, number, string[]]> {
|
|
46
|
-
const replacements: Array<[number, number, string[]]> = [];
|
|
47
|
-
let lineIndex = 0;
|
|
48
|
-
|
|
49
|
-
for (const chunk of chunks) {
|
|
50
|
-
if (chunk.changeContext) {
|
|
51
|
-
const ctxIndex = seekSequence(originalLines, [chunk.changeContext], lineIndex, false);
|
|
52
|
-
if (ctxIndex === null) {
|
|
53
|
-
throw new Error(`Failed to find context '${chunk.changeContext}' in ${filePath}`);
|
|
54
|
-
}
|
|
55
|
-
lineIndex = ctxIndex + 1;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (chunk.oldLines.length === 0) {
|
|
59
|
-
const insertionIndex =
|
|
60
|
-
originalLines.length > 0 && originalLines[originalLines.length - 1] === ""
|
|
61
|
-
? originalLines.length - 1
|
|
62
|
-
: originalLines.length;
|
|
63
|
-
replacements.push([insertionIndex, 0, chunk.newLines]);
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
let pattern = chunk.oldLines;
|
|
68
|
-
let newSlice = chunk.newLines;
|
|
69
|
-
let found = seekSequence(originalLines, pattern, lineIndex, chunk.isEndOfFile);
|
|
70
|
-
|
|
71
|
-
if (found === null && pattern[pattern.length - 1] === "") {
|
|
72
|
-
pattern = pattern.slice(0, -1);
|
|
73
|
-
if (newSlice.length > 0 && newSlice[newSlice.length - 1] === "") {
|
|
74
|
-
newSlice = newSlice.slice(0, -1);
|
|
75
|
-
}
|
|
76
|
-
found = seekSequence(originalLines, pattern, lineIndex, chunk.isEndOfFile);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (found === null) {
|
|
80
|
-
throw new Error(
|
|
81
|
-
`Failed to find expected lines in ${filePath}:\n${chunk.oldLines.join("\n")}`,
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
replacements.push([found, pattern.length, newSlice]);
|
|
86
|
-
lineIndex = found + pattern.length;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
replacements.sort((a, b) => a[0] - b[0]);
|
|
90
|
-
return replacements;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function applyReplacements(
|
|
94
|
-
lines: string[],
|
|
95
|
-
replacements: Array<[number, number, string[]]>,
|
|
96
|
-
): string[] {
|
|
97
|
-
const result = [...lines];
|
|
98
|
-
for (let rIdx = replacements.length - 1; rIdx >= 0; rIdx -= 1) {
|
|
99
|
-
const entry = replacements[rIdx]!;
|
|
100
|
-
const startIndex = entry[0];
|
|
101
|
-
const oldLen = entry[1];
|
|
102
|
-
const newLines = entry[2];
|
|
103
|
-
for (let i = 0; i < oldLen; i += 1) {
|
|
104
|
-
if (startIndex < result.length) {
|
|
105
|
-
result.splice(startIndex, 1);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
for (let i = 0; i < newLines.length; i += 1) {
|
|
109
|
-
result.splice(startIndex + i, 0, newLines[i]!);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return result;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function seekSequence(
|
|
116
|
-
lines: string[],
|
|
117
|
-
pattern: string[],
|
|
118
|
-
start: number,
|
|
119
|
-
eof: boolean,
|
|
120
|
-
): number | null {
|
|
121
|
-
if (pattern.length === 0) {
|
|
122
|
-
return start;
|
|
123
|
-
}
|
|
124
|
-
if (pattern.length > lines.length) {
|
|
125
|
-
return null;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const maxStart = lines.length - pattern.length;
|
|
129
|
-
const searchStart = eof && lines.length >= pattern.length ? maxStart : start;
|
|
130
|
-
if (searchStart > maxStart) {
|
|
131
|
-
return null;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Pass 1: exact match
|
|
135
|
-
for (let i = searchStart; i <= maxStart; i += 1) {
|
|
136
|
-
if (linesMatch(lines, pattern, i, (value) => value)) {
|
|
137
|
-
return i;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
// Pass 2: trim trailing whitespace
|
|
141
|
-
for (let i = searchStart; i <= maxStart; i += 1) {
|
|
142
|
-
if (linesMatch(lines, pattern, i, (value) => value.trimEnd())) {
|
|
143
|
-
return i;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
// Pass 3: trim all whitespace
|
|
147
|
-
for (let i = searchStart; i <= maxStart; i += 1) {
|
|
148
|
-
if (linesMatch(lines, pattern, i, (value) => value.trim())) {
|
|
149
|
-
return i;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
// Pass 4: normalize punctuation + trim
|
|
153
|
-
for (let i = searchStart; i <= maxStart; i += 1) {
|
|
154
|
-
if (linesMatch(lines, pattern, i, (value) => normalizePunctuation(value.trim()))) {
|
|
155
|
-
return i;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return null;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function linesMatch(
|
|
163
|
-
lines: string[],
|
|
164
|
-
pattern: string[],
|
|
165
|
-
start: number,
|
|
166
|
-
normalize: (value: string) => string,
|
|
167
|
-
): boolean {
|
|
168
|
-
for (let idx = 0; idx < pattern.length; idx += 1) {
|
|
169
|
-
if (normalize(lines[start + idx]!) !== normalize(pattern[idx]!)) {
|
|
170
|
-
return false;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
return true;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function normalizePunctuation(value: string): string {
|
|
177
|
-
return Array.from(value)
|
|
178
|
-
.map((char) => {
|
|
179
|
-
switch (char) {
|
|
180
|
-
case "\u2010":
|
|
181
|
-
case "\u2011":
|
|
182
|
-
case "\u2012":
|
|
183
|
-
case "\u2013":
|
|
184
|
-
case "\u2014":
|
|
185
|
-
case "\u2015":
|
|
186
|
-
case "\u2212":
|
|
187
|
-
return "-";
|
|
188
|
-
case "\u2018":
|
|
189
|
-
case "\u2019":
|
|
190
|
-
case "\u201A":
|
|
191
|
-
case "\u201B":
|
|
192
|
-
return "'";
|
|
193
|
-
case "\u201C":
|
|
194
|
-
case "\u201D":
|
|
195
|
-
case "\u201E":
|
|
196
|
-
case "\u201F":
|
|
197
|
-
return '"';
|
|
198
|
-
case "\u00A0":
|
|
199
|
-
case "\u2002":
|
|
200
|
-
case "\u2003":
|
|
201
|
-
case "\u2004":
|
|
202
|
-
case "\u2005":
|
|
203
|
-
case "\u2006":
|
|
204
|
-
case "\u2007":
|
|
205
|
-
case "\u2008":
|
|
206
|
-
case "\u2009":
|
|
207
|
-
case "\u200A":
|
|
208
|
-
case "\u202F":
|
|
209
|
-
case "\u205F":
|
|
210
|
-
case "\u3000":
|
|
211
|
-
return " ";
|
|
212
|
-
default:
|
|
213
|
-
return char;
|
|
214
|
-
}
|
|
215
|
-
})
|
|
216
|
-
.join("");
|
|
217
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { applyPatchTool } from "./apply-patch.js";
|
|
@@ -1,297 +0,0 @@
|
|
|
1
|
-
// Originally from OpenClaw (MIT License, Copyright 2025 Peter Steinberger)
|
|
2
|
-
// https://github.com/openclaw/openclaw
|
|
3
|
-
// Modified for ARIA — stripped OpenClaw runtime, rewired to ARIA Tool interface
|
|
4
|
-
|
|
5
|
-
// ── Marker Constants ───────────────────────────────────────────────
|
|
6
|
-
|
|
7
|
-
export const BEGIN_PATCH_MARKER = "*** Begin Patch";
|
|
8
|
-
export const END_PATCH_MARKER = "*** End Patch";
|
|
9
|
-
export const ADD_FILE_MARKER = "*** Add File: ";
|
|
10
|
-
export const DELETE_FILE_MARKER = "*** Delete File: ";
|
|
11
|
-
export const UPDATE_FILE_MARKER = "*** Update File: ";
|
|
12
|
-
export const MOVE_TO_MARKER = "*** Move to: ";
|
|
13
|
-
export const EOF_MARKER = "*** End of File";
|
|
14
|
-
export const CHANGE_CONTEXT_MARKER = "@@ ";
|
|
15
|
-
export const EMPTY_CHANGE_CONTEXT_MARKER = "@@";
|
|
16
|
-
|
|
17
|
-
// ── Types ──────────────────────────────────────────────────────────
|
|
18
|
-
|
|
19
|
-
export type AddFileHunk = {
|
|
20
|
-
kind: "add";
|
|
21
|
-
path: string;
|
|
22
|
-
contents: string;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export type DeleteFileHunk = {
|
|
26
|
-
kind: "delete";
|
|
27
|
-
path: string;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export type UpdateFileChunk = {
|
|
31
|
-
changeContext?: string;
|
|
32
|
-
oldLines: string[];
|
|
33
|
-
newLines: string[];
|
|
34
|
-
isEndOfFile: boolean;
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
export type UpdateFileHunk = {
|
|
38
|
-
kind: "update";
|
|
39
|
-
path: string;
|
|
40
|
-
movePath?: string;
|
|
41
|
-
chunks: UpdateFileChunk[];
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export type Hunk = AddFileHunk | DeleteFileHunk | UpdateFileHunk;
|
|
45
|
-
|
|
46
|
-
export type ApplyPatchSummary = {
|
|
47
|
-
added: string[];
|
|
48
|
-
modified: string[];
|
|
49
|
-
deleted: string[];
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
// ── Unicode Normalization ──────────────────────────────────────────
|
|
53
|
-
|
|
54
|
-
const UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g;
|
|
55
|
-
|
|
56
|
-
export function normalizeUnicodeSpaces(value: string): string {
|
|
57
|
-
return value.replace(UNICODE_SPACES, " ");
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// ── Parser ─────────────────────────────────────────────────────────
|
|
61
|
-
|
|
62
|
-
export function parsePatchText(input: string): { hunks: Hunk[]; patch: string } {
|
|
63
|
-
const trimmed = input.trim();
|
|
64
|
-
if (!trimmed) {
|
|
65
|
-
throw new Error("Invalid patch: input is empty.");
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const lines = trimmed.split(/\r?\n/);
|
|
69
|
-
const validated = checkPatchBoundariesLenient(lines);
|
|
70
|
-
const hunks: Hunk[] = [];
|
|
71
|
-
|
|
72
|
-
const lastLineIndex = validated.length - 1;
|
|
73
|
-
let remaining = validated.slice(1, lastLineIndex);
|
|
74
|
-
let lineNumber = 2;
|
|
75
|
-
|
|
76
|
-
while (remaining.length > 0) {
|
|
77
|
-
const { hunk, consumed } = parseOneHunk(remaining, lineNumber);
|
|
78
|
-
hunks.push(hunk);
|
|
79
|
-
lineNumber += consumed;
|
|
80
|
-
remaining = remaining.slice(consumed);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return { hunks, patch: validated.join("\n") };
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export function checkPatchBoundariesLenient(lines: string[]): string[] {
|
|
87
|
-
const strictError = checkPatchBoundariesStrict(lines);
|
|
88
|
-
if (!strictError) {
|
|
89
|
-
return lines;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (lines.length < 4) {
|
|
93
|
-
throw new Error(strictError);
|
|
94
|
-
}
|
|
95
|
-
const first = lines[0];
|
|
96
|
-
const last = lines[lines.length - 1];
|
|
97
|
-
if ((first === "<<EOF" || first === "<<'EOF'" || first === '<<"EOF"') && last?.endsWith("EOF")) {
|
|
98
|
-
const inner = lines.slice(1, lines.length - 1);
|
|
99
|
-
const innerError = checkPatchBoundariesStrict(inner);
|
|
100
|
-
if (!innerError) {
|
|
101
|
-
return inner;
|
|
102
|
-
}
|
|
103
|
-
throw new Error(innerError);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
throw new Error(strictError);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function checkPatchBoundariesStrict(lines: string[]): string | null {
|
|
110
|
-
const firstLine = lines[0]?.trim();
|
|
111
|
-
const lastLine = lines[lines.length - 1]?.trim();
|
|
112
|
-
|
|
113
|
-
if (firstLine === BEGIN_PATCH_MARKER && lastLine === END_PATCH_MARKER) {
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
if (firstLine !== BEGIN_PATCH_MARKER) {
|
|
117
|
-
return "The first line of the patch must be '*** Begin Patch'";
|
|
118
|
-
}
|
|
119
|
-
return "The last line of the patch must be '*** End Patch'";
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export function parseOneHunk(
|
|
123
|
-
lines: string[],
|
|
124
|
-
lineNumber: number,
|
|
125
|
-
): { hunk: Hunk; consumed: number } {
|
|
126
|
-
if (lines.length === 0) {
|
|
127
|
-
throw new Error(`Invalid patch hunk at line ${lineNumber}: empty hunk`);
|
|
128
|
-
}
|
|
129
|
-
const firstLine = lines[0]!.trim();
|
|
130
|
-
if (firstLine.startsWith(ADD_FILE_MARKER)) {
|
|
131
|
-
const targetPath = firstLine.slice(ADD_FILE_MARKER.length);
|
|
132
|
-
let contents = "";
|
|
133
|
-
let consumed = 1;
|
|
134
|
-
for (const addLine of lines.slice(1)) {
|
|
135
|
-
if (addLine.startsWith("+")) {
|
|
136
|
-
contents += `${addLine.slice(1)}\n`;
|
|
137
|
-
consumed += 1;
|
|
138
|
-
} else {
|
|
139
|
-
break;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return {
|
|
143
|
-
hunk: { kind: "add", path: targetPath, contents },
|
|
144
|
-
consumed,
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (firstLine.startsWith(DELETE_FILE_MARKER)) {
|
|
149
|
-
const targetPath = firstLine.slice(DELETE_FILE_MARKER.length);
|
|
150
|
-
return {
|
|
151
|
-
hunk: { kind: "delete", path: targetPath },
|
|
152
|
-
consumed: 1,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (firstLine.startsWith(UPDATE_FILE_MARKER)) {
|
|
157
|
-
const targetPath = firstLine.slice(UPDATE_FILE_MARKER.length);
|
|
158
|
-
let remaining = lines.slice(1);
|
|
159
|
-
let consumed = 1;
|
|
160
|
-
let movePath: string | undefined;
|
|
161
|
-
|
|
162
|
-
const moveCandidate = remaining[0]?.trim();
|
|
163
|
-
if (moveCandidate?.startsWith(MOVE_TO_MARKER)) {
|
|
164
|
-
movePath = moveCandidate.slice(MOVE_TO_MARKER.length);
|
|
165
|
-
remaining = remaining.slice(1);
|
|
166
|
-
consumed += 1;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const chunks: UpdateFileChunk[] = [];
|
|
170
|
-
while (remaining.length > 0) {
|
|
171
|
-
if (remaining[0]!.trim() === "") {
|
|
172
|
-
remaining = remaining.slice(1);
|
|
173
|
-
consumed += 1;
|
|
174
|
-
continue;
|
|
175
|
-
}
|
|
176
|
-
if (remaining[0]!.startsWith("***")) {
|
|
177
|
-
break;
|
|
178
|
-
}
|
|
179
|
-
const { chunk, consumed: chunkLines } = parseUpdateFileChunk(
|
|
180
|
-
remaining,
|
|
181
|
-
lineNumber + consumed,
|
|
182
|
-
chunks.length === 0,
|
|
183
|
-
);
|
|
184
|
-
chunks.push(chunk);
|
|
185
|
-
remaining = remaining.slice(chunkLines);
|
|
186
|
-
consumed += chunkLines;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if (chunks.length === 0) {
|
|
190
|
-
throw new Error(
|
|
191
|
-
`Invalid patch hunk at line ${lineNumber}: Update file hunk for path '${targetPath}' is empty`,
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return {
|
|
196
|
-
hunk: {
|
|
197
|
-
kind: "update",
|
|
198
|
-
path: targetPath,
|
|
199
|
-
movePath,
|
|
200
|
-
chunks,
|
|
201
|
-
},
|
|
202
|
-
consumed,
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
throw new Error(
|
|
207
|
-
`Invalid patch hunk at line ${lineNumber}: '${lines[0]}' is not a valid hunk header. Valid hunk headers: '*** Add File: {path}', '*** Delete File: {path}', '*** Update File: {path}'`,
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
export function parseUpdateFileChunk(
|
|
212
|
-
lines: string[],
|
|
213
|
-
lineNumber: number,
|
|
214
|
-
allowMissingContext: boolean,
|
|
215
|
-
): { chunk: UpdateFileChunk; consumed: number } {
|
|
216
|
-
if (lines.length === 0) {
|
|
217
|
-
throw new Error(
|
|
218
|
-
`Invalid patch hunk at line ${lineNumber}: Update hunk does not contain any lines`,
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const firstLine = lines[0]!;
|
|
223
|
-
let changeContext: string | undefined;
|
|
224
|
-
let startIndex = 0;
|
|
225
|
-
if (firstLine === EMPTY_CHANGE_CONTEXT_MARKER) {
|
|
226
|
-
startIndex = 1;
|
|
227
|
-
} else if (firstLine.startsWith(CHANGE_CONTEXT_MARKER)) {
|
|
228
|
-
changeContext = firstLine.slice(CHANGE_CONTEXT_MARKER.length);
|
|
229
|
-
startIndex = 1;
|
|
230
|
-
} else if (!allowMissingContext) {
|
|
231
|
-
throw new Error(
|
|
232
|
-
`Invalid patch hunk at line ${lineNumber}: Expected update hunk to start with a @@ context marker, got: '${firstLine}'`,
|
|
233
|
-
);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
if (startIndex >= lines.length) {
|
|
237
|
-
throw new Error(
|
|
238
|
-
`Invalid patch hunk at line ${lineNumber + 1}: Update hunk does not contain any lines`,
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
const chunk: UpdateFileChunk = {
|
|
243
|
-
changeContext,
|
|
244
|
-
oldLines: [],
|
|
245
|
-
newLines: [],
|
|
246
|
-
isEndOfFile: false,
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
let parsedLines = 0;
|
|
250
|
-
for (const line of lines.slice(startIndex)) {
|
|
251
|
-
if (line === EOF_MARKER) {
|
|
252
|
-
if (parsedLines === 0) {
|
|
253
|
-
throw new Error(
|
|
254
|
-
`Invalid patch hunk at line ${lineNumber + 1}: Update hunk does not contain any lines`,
|
|
255
|
-
);
|
|
256
|
-
}
|
|
257
|
-
chunk.isEndOfFile = true;
|
|
258
|
-
parsedLines += 1;
|
|
259
|
-
break;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
const marker = line[0];
|
|
263
|
-
if (!marker) {
|
|
264
|
-
chunk.oldLines.push("");
|
|
265
|
-
chunk.newLines.push("");
|
|
266
|
-
parsedLines += 1;
|
|
267
|
-
continue;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (marker === " ") {
|
|
271
|
-
const content = line.slice(1);
|
|
272
|
-
chunk.oldLines.push(content);
|
|
273
|
-
chunk.newLines.push(content);
|
|
274
|
-
parsedLines += 1;
|
|
275
|
-
continue;
|
|
276
|
-
}
|
|
277
|
-
if (marker === "+") {
|
|
278
|
-
chunk.newLines.push(line.slice(1));
|
|
279
|
-
parsedLines += 1;
|
|
280
|
-
continue;
|
|
281
|
-
}
|
|
282
|
-
if (marker === "-") {
|
|
283
|
-
chunk.oldLines.push(line.slice(1));
|
|
284
|
-
parsedLines += 1;
|
|
285
|
-
continue;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
if (parsedLines === 0) {
|
|
289
|
-
throw new Error(
|
|
290
|
-
`Invalid patch hunk at line ${lineNumber + 1}: Unexpected line found in update hunk: '${line}'. Every line should start with ' ' (context line), '+' (added line), or '-' (removed line)`,
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
break;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
return { chunk, consumed: parsedLines + startIndex };
|
|
297
|
-
}
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
// Originally from OpenClaw (MIT License, Copyright 2025 Peter Steinberger)
|
|
2
|
-
// https://github.com/openclaw/openclaw
|
|
3
|
-
// Modified for ARIA — stripped OpenClaw runtime, rewired to ARIA Tool interface
|
|
4
|
-
|
|
5
|
-
import fs from "node:fs/promises";
|
|
6
|
-
import os from "node:os";
|
|
7
|
-
import path from "node:path";
|
|
8
|
-
|
|
9
|
-
const UNICODE_SPACES = /[\u00A0\u2000-\u200A\u202F\u205F\u3000]/g;
|
|
10
|
-
|
|
11
|
-
function normalizeUnicodeSpaces(str: string): string {
|
|
12
|
-
return str.replace(UNICODE_SPACES, " ");
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function expandPath(filePath: string): string {
|
|
16
|
-
const normalized = normalizeUnicodeSpaces(filePath);
|
|
17
|
-
if (normalized === "~") {
|
|
18
|
-
return os.homedir();
|
|
19
|
-
}
|
|
20
|
-
if (normalized.startsWith("~/")) {
|
|
21
|
-
return os.homedir() + normalized.slice(1);
|
|
22
|
-
}
|
|
23
|
-
return normalized;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function resolveToCwd(filePath: string, cwd: string): string {
|
|
27
|
-
const expanded = expandPath(filePath);
|
|
28
|
-
if (path.isAbsolute(expanded)) {
|
|
29
|
-
return expanded;
|
|
30
|
-
}
|
|
31
|
-
return path.resolve(cwd, expanded);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function resolveSandboxInputPath(filePath: string, cwd: string): string {
|
|
35
|
-
return resolveToCwd(filePath, cwd);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function resolveSandboxPath(params: { filePath: string; cwd: string; root: string }): {
|
|
39
|
-
resolved: string;
|
|
40
|
-
relative: string;
|
|
41
|
-
} {
|
|
42
|
-
const resolved = resolveSandboxInputPath(params.filePath, params.cwd);
|
|
43
|
-
const rootResolved = path.resolve(params.root);
|
|
44
|
-
const relative = path.relative(rootResolved, resolved);
|
|
45
|
-
if (!relative || relative === "") {
|
|
46
|
-
return { resolved, relative: "" };
|
|
47
|
-
}
|
|
48
|
-
if (relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
49
|
-
throw new Error(`Path escapes sandbox root (${shortPath(rootResolved)}): ${params.filePath}`);
|
|
50
|
-
}
|
|
51
|
-
return { resolved, relative };
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export async function assertSandboxPath(params: {
|
|
55
|
-
filePath: string;
|
|
56
|
-
cwd: string;
|
|
57
|
-
root: string;
|
|
58
|
-
allowFinalSymlink?: boolean;
|
|
59
|
-
}) {
|
|
60
|
-
const resolved = resolveSandboxPath(params);
|
|
61
|
-
await assertNoSymlinkEscape(resolved.relative, path.resolve(params.root), {
|
|
62
|
-
allowFinalSymlink: params.allowFinalSymlink,
|
|
63
|
-
});
|
|
64
|
-
return resolved;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async function assertNoSymlinkEscape(
|
|
68
|
-
relative: string,
|
|
69
|
-
root: string,
|
|
70
|
-
options?: { allowFinalSymlink?: boolean },
|
|
71
|
-
) {
|
|
72
|
-
if (!relative) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
const rootReal = await tryRealpath(root);
|
|
76
|
-
const parts = relative.split(path.sep).filter(Boolean);
|
|
77
|
-
let current = root;
|
|
78
|
-
for (let idx = 0; idx < parts.length; idx += 1) {
|
|
79
|
-
const part = parts[idx]!;
|
|
80
|
-
const isLast = idx === parts.length - 1;
|
|
81
|
-
current = path.join(current, part);
|
|
82
|
-
try {
|
|
83
|
-
const stat = await fs.lstat(current);
|
|
84
|
-
if (stat.isSymbolicLink()) {
|
|
85
|
-
// Unlinking a symlink itself is safe even if it points outside the root. What we
|
|
86
|
-
// must prevent is traversing through a symlink to reach targets outside root.
|
|
87
|
-
if (options?.allowFinalSymlink && isLast) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
const target = await tryRealpath(current);
|
|
91
|
-
if (!isPathInside(rootReal, target)) {
|
|
92
|
-
throw new Error(
|
|
93
|
-
`Symlink escapes sandbox root (${shortPath(rootReal)}): ${shortPath(current)}`,
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
current = target;
|
|
97
|
-
}
|
|
98
|
-
} catch (err) {
|
|
99
|
-
const anyErr = err as { code?: string };
|
|
100
|
-
if (anyErr.code === "ENOENT") {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
throw err;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
async function tryRealpath(value: string): Promise<string> {
|
|
109
|
-
try {
|
|
110
|
-
return await fs.realpath(value);
|
|
111
|
-
} catch {
|
|
112
|
-
return path.resolve(value);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function isPathInside(root: string, target: string): boolean {
|
|
117
|
-
const relative = path.relative(root, target);
|
|
118
|
-
if (!relative || relative === "") {
|
|
119
|
-
return true;
|
|
120
|
-
}
|
|
121
|
-
return !(relative.startsWith("..") || path.isAbsolute(relative));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function shortPath(value: string) {
|
|
125
|
-
if (value.startsWith(os.homedir())) {
|
|
126
|
-
return `~${value.slice(os.homedir().length)}`;
|
|
127
|
-
}
|
|
128
|
-
return value;
|
|
129
|
-
}
|