tesseract-agent 0.11.39 → 0.11.41
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/dist-bundle/builtin-skills/browser-automation/SKILL.md +183 -48
- package/dist-bundle/builtin-skills/browser-automation/references/recipes.md +18 -31
- package/dist-bundle/builtin-skills/computer-control/SKILL.md +7 -2
- package/dist-bundle/builtin-skills/ux-review/SKILL.md +1 -1
- package/dist-bundle/cli.js +29410 -19851
- package/dist-bundle/codex-rm-hook.js +518 -0
- package/dist-bundle/mesh-core/package.json +1 -1
- package/dist-bundle/serpapi-mcp.js +146486 -0
- package/dist-bundle/tesseract-mcp-proxy.js +371 -0
- package/package.json +3 -3
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
import { createRequire as __cr } from 'module';
|
|
2
|
+
import { dirname as __dn } from 'path';
|
|
3
|
+
import { fileURLToPath as __fu } from 'url';
|
|
4
|
+
const require = __cr(import.meta.url);
|
|
5
|
+
const __dirname = __dn(__fu(import.meta.url));
|
|
6
|
+
|
|
7
|
+
// ../../packages/agent-core/dist/rm-guard.js
|
|
8
|
+
import { lstatSync } from "node:fs";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
var RM_RULES = [
|
|
11
|
+
"Rules for deleting in this app (they hold in every mode and cannot be switched off):",
|
|
12
|
+
"1. rm, rmdir and unlink take only exact absolute paths \u2014 starting with / or ~/. Never a relative path, never . or .., never $VARIABLES, $(\u2026) or backticks.",
|
|
13
|
+
"2. A wildcard is allowed only inside an absolute folder that exists \u2014 rm -f /full/path/to/folder/* \u2014 never in /, a top-level folder, a mounted volume or the home folder.",
|
|
14
|
+
"3. The path must exist. Check it first (ls -d /full/path) instead of deleting blind.",
|
|
15
|
+
"4. find \u2026 -delete (or -exec rm) needs an absolute start folder under the same rules. xargs rm is refused: pass the exact paths to rm instead."
|
|
16
|
+
].join("\n");
|
|
17
|
+
var DELETERS = /* @__PURE__ */ new Set(["rm", "rmdir", "unlink"]);
|
|
18
|
+
function checkRmCommand(command, env = {}) {
|
|
19
|
+
const ctx = { home: env.home ?? homedir(), exists: env.exists ?? existsOnDisk };
|
|
20
|
+
return checkScript(command, ctx, 0);
|
|
21
|
+
}
|
|
22
|
+
function existsOnDisk(p) {
|
|
23
|
+
try {
|
|
24
|
+
lstatSync(p);
|
|
25
|
+
return true;
|
|
26
|
+
} catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
var SEPARATORS = /* @__PURE__ */ new Set([";", "&", "|", "\n", "(", ")"]);
|
|
31
|
+
function newWord() {
|
|
32
|
+
return { text: "", glob: false, brace: false, dynamic: false, tilde: false };
|
|
33
|
+
}
|
|
34
|
+
function balanced(src, i) {
|
|
35
|
+
let depth = 1;
|
|
36
|
+
let quote = null;
|
|
37
|
+
const start = i;
|
|
38
|
+
for (; i < src.length; i++) {
|
|
39
|
+
const ch = src[i];
|
|
40
|
+
if (quote) {
|
|
41
|
+
if (ch === "\\" && quote === '"') {
|
|
42
|
+
i++;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (ch === quote)
|
|
46
|
+
quote = null;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (ch === "\\") {
|
|
50
|
+
i++;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (ch === '"' || ch === "'") {
|
|
54
|
+
quote = ch;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (ch === "(")
|
|
58
|
+
depth++;
|
|
59
|
+
else if (ch === ")" && --depth === 0)
|
|
60
|
+
return { body: src.slice(start, i), end: i + 1 };
|
|
61
|
+
}
|
|
62
|
+
return { body: src.slice(start), end: src.length };
|
|
63
|
+
}
|
|
64
|
+
function parse(src) {
|
|
65
|
+
const commands = [];
|
|
66
|
+
const inner = [];
|
|
67
|
+
let words = [];
|
|
68
|
+
let w = null;
|
|
69
|
+
let redirectTarget = false;
|
|
70
|
+
const heredocs = [];
|
|
71
|
+
let pendingHeredoc = null;
|
|
72
|
+
const endWord = () => {
|
|
73
|
+
if (!w)
|
|
74
|
+
return;
|
|
75
|
+
if (pendingHeredoc)
|
|
76
|
+
heredocs.push({ tag: w.text, strip: pendingHeredoc.strip });
|
|
77
|
+
else if (!redirectTarget)
|
|
78
|
+
words.push(w);
|
|
79
|
+
pendingHeredoc = null;
|
|
80
|
+
redirectTarget = false;
|
|
81
|
+
w = null;
|
|
82
|
+
};
|
|
83
|
+
const endCommand = () => {
|
|
84
|
+
endWord();
|
|
85
|
+
if (words.length)
|
|
86
|
+
commands.push(words);
|
|
87
|
+
words = [];
|
|
88
|
+
};
|
|
89
|
+
const word = () => w ??= newWord();
|
|
90
|
+
let i = 0;
|
|
91
|
+
while (i < src.length) {
|
|
92
|
+
const ch = src[i];
|
|
93
|
+
if (ch === "\n") {
|
|
94
|
+
endCommand();
|
|
95
|
+
i++;
|
|
96
|
+
while (heredocs.length) {
|
|
97
|
+
const { tag, strip } = heredocs.shift();
|
|
98
|
+
for (; ; ) {
|
|
99
|
+
if (i >= src.length)
|
|
100
|
+
break;
|
|
101
|
+
const nl = src.indexOf("\n", i);
|
|
102
|
+
const line = src.slice(i, nl < 0 ? src.length : nl);
|
|
103
|
+
i = nl < 0 ? src.length : nl + 1;
|
|
104
|
+
if ((strip ? line.replace(/^\t+/, "") : line) === tag)
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (ch === " " || ch === " ") {
|
|
111
|
+
endWord();
|
|
112
|
+
i++;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (ch === "#" && !w) {
|
|
116
|
+
const nl = src.indexOf("\n", i);
|
|
117
|
+
i = nl < 0 ? src.length : nl;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (ch === "\\") {
|
|
121
|
+
if (src[i + 1] === "\n") {
|
|
122
|
+
i += 2;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (i + 1 < src.length)
|
|
126
|
+
word().text += src[i + 1];
|
|
127
|
+
i += 2;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (ch === "'") {
|
|
131
|
+
const close = src.indexOf("'", i + 1);
|
|
132
|
+
word().text += src.slice(i + 1, close < 0 ? src.length : close);
|
|
133
|
+
i = close < 0 ? src.length : close + 1;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (ch === '"') {
|
|
137
|
+
const cur2 = word();
|
|
138
|
+
i++;
|
|
139
|
+
while (i < src.length && src[i] !== '"') {
|
|
140
|
+
const c = src[i];
|
|
141
|
+
if (c === "\\" && i + 1 < src.length && '$`"\\\n'.includes(src[i + 1])) {
|
|
142
|
+
if (src[i + 1] !== "\n")
|
|
143
|
+
cur2.text += src[i + 1];
|
|
144
|
+
i += 2;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (c === "$" && src[i + 1] === "(") {
|
|
148
|
+
cur2.dynamic = true;
|
|
149
|
+
const b = balanced(src, i + 2);
|
|
150
|
+
inner.push(b.body);
|
|
151
|
+
i = b.end;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (c === "$" && /[A-Za-z_{0-9@*#?$!-]/.test(src[i + 1] ?? "")) {
|
|
155
|
+
cur2.dynamic = true;
|
|
156
|
+
cur2.text += c;
|
|
157
|
+
i++;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (c === "`") {
|
|
161
|
+
cur2.dynamic = true;
|
|
162
|
+
const close = src.indexOf("`", i + 1);
|
|
163
|
+
inner.push(src.slice(i + 1, close < 0 ? src.length : close));
|
|
164
|
+
i = close < 0 ? src.length : close + 1;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
cur2.text += c;
|
|
168
|
+
i++;
|
|
169
|
+
}
|
|
170
|
+
i++;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (ch === "$") {
|
|
174
|
+
const next = src[i + 1];
|
|
175
|
+
if (next === "'") {
|
|
176
|
+
const close = src.indexOf("'", i + 2);
|
|
177
|
+
word().text += src.slice(i + 2, close < 0 ? src.length : close);
|
|
178
|
+
i = close < 0 ? src.length : close + 1;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (next === "(") {
|
|
182
|
+
const cur2 = word();
|
|
183
|
+
cur2.dynamic = true;
|
|
184
|
+
if (src[i + 2] === "(") {
|
|
185
|
+
const b2 = balanced(src, i + 3);
|
|
186
|
+
i = Math.min(src.length, b2.end + 1);
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const b = balanced(src, i + 2);
|
|
190
|
+
inner.push(b.body);
|
|
191
|
+
i = b.end;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (next !== void 0 && /[A-Za-z_{0-9@*#?$!-]/.test(next)) {
|
|
195
|
+
const cur2 = word();
|
|
196
|
+
cur2.dynamic = true;
|
|
197
|
+
cur2.text += "$";
|
|
198
|
+
i++;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
word().text += "$";
|
|
202
|
+
i++;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (ch === "`") {
|
|
206
|
+
const cur2 = word();
|
|
207
|
+
cur2.dynamic = true;
|
|
208
|
+
const close = src.indexOf("`", i + 1);
|
|
209
|
+
inner.push(src.slice(i + 1, close < 0 ? src.length : close));
|
|
210
|
+
i = close < 0 ? src.length : close + 1;
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (ch === "<" || ch === ">" || ch === "&" && src[i + 1] === ">") {
|
|
214
|
+
if ((ch === "<" || ch === ">") && src[i + 1] === "(") {
|
|
215
|
+
endWord();
|
|
216
|
+
const b = balanced(src, i + 2);
|
|
217
|
+
inner.push(b.body);
|
|
218
|
+
i = b.end;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
const before = w;
|
|
222
|
+
if (before && /^\d+$/.test(before.text) && !before.dynamic)
|
|
223
|
+
w = null;
|
|
224
|
+
else
|
|
225
|
+
endWord();
|
|
226
|
+
if (ch === "<" && src.startsWith("<<<", i)) {
|
|
227
|
+
i += 3;
|
|
228
|
+
redirectTarget = true;
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
if (ch === "<" && src[i + 1] === "<") {
|
|
232
|
+
const strip = src[i + 2] === "-";
|
|
233
|
+
i += strip ? 3 : 2;
|
|
234
|
+
pendingHeredoc = { strip };
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
i++;
|
|
238
|
+
while (i < src.length && "<>&|".includes(src[i]))
|
|
239
|
+
i++;
|
|
240
|
+
redirectTarget = true;
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (SEPARATORS.has(ch)) {
|
|
244
|
+
endCommand();
|
|
245
|
+
i++;
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
const cur = word();
|
|
249
|
+
if (ch === "*" || ch === "?" || ch === "[")
|
|
250
|
+
cur.glob = true;
|
|
251
|
+
if (ch === "~" && cur.text === "" && !cur.dynamic)
|
|
252
|
+
cur.tilde = true;
|
|
253
|
+
if (ch === "{") {
|
|
254
|
+
const close = src.indexOf("}", i);
|
|
255
|
+
const body = close < 0 ? "" : src.slice(i + 1, close);
|
|
256
|
+
if (body.includes(",") || body.includes(".."))
|
|
257
|
+
cur.brace = true;
|
|
258
|
+
}
|
|
259
|
+
cur.text += ch;
|
|
260
|
+
i++;
|
|
261
|
+
}
|
|
262
|
+
endCommand();
|
|
263
|
+
return { commands, inner };
|
|
264
|
+
}
|
|
265
|
+
var KEYWORDS = /* @__PURE__ */ new Set(["{", "}", "!", "then", "do", "else", "elif", "if", "while", "until", "fi", "done", "esac"]);
|
|
266
|
+
var WRAPPERS = {
|
|
267
|
+
sudo: /* @__PURE__ */ new Set(["-u", "-g", "-C", "-h", "-p", "-r", "-t", "-U", "-D", "-T"]),
|
|
268
|
+
doas: /* @__PURE__ */ new Set(["-u", "-C"]),
|
|
269
|
+
env: /* @__PURE__ */ new Set(["-u", "-C", "-S", "-P"]),
|
|
270
|
+
command: /* @__PURE__ */ new Set(),
|
|
271
|
+
builtin: /* @__PURE__ */ new Set(),
|
|
272
|
+
exec: /* @__PURE__ */ new Set(["-a"]),
|
|
273
|
+
nohup: /* @__PURE__ */ new Set(),
|
|
274
|
+
nice: /* @__PURE__ */ new Set(["-n"]),
|
|
275
|
+
ionice: /* @__PURE__ */ new Set(["-c", "-n", "-p"]),
|
|
276
|
+
stdbuf: /* @__PURE__ */ new Set(["-i", "-o", "-e"]),
|
|
277
|
+
time: /* @__PURE__ */ new Set(["-f", "-o"]),
|
|
278
|
+
timeout: /* @__PURE__ */ new Set(["-s", "-k"]),
|
|
279
|
+
caffeinate: /* @__PURE__ */ new Set(["-t", "-w"]),
|
|
280
|
+
xargs: /* @__PURE__ */ new Set(["-n", "-P", "-I", "-L", "-d", "-E", "-s", "-a", "-J", "-R", "-S"])
|
|
281
|
+
};
|
|
282
|
+
var ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=/;
|
|
283
|
+
function baseName(text) {
|
|
284
|
+
const slash = text.lastIndexOf("/");
|
|
285
|
+
return slash < 0 ? text : text.slice(slash + 1);
|
|
286
|
+
}
|
|
287
|
+
function commandOf(words) {
|
|
288
|
+
let k = 0;
|
|
289
|
+
let viaXargs = false;
|
|
290
|
+
while (k < words.length && (KEYWORDS.has(words[k].text) || ASSIGNMENT.test(words[k].text)))
|
|
291
|
+
k++;
|
|
292
|
+
while (k < words.length) {
|
|
293
|
+
const name = baseName(words[k].text);
|
|
294
|
+
const takesValue = WRAPPERS[name];
|
|
295
|
+
if (!takesValue)
|
|
296
|
+
break;
|
|
297
|
+
if (name === "xargs")
|
|
298
|
+
viaXargs = true;
|
|
299
|
+
k++;
|
|
300
|
+
while (k < words.length) {
|
|
301
|
+
const t = words[k].text;
|
|
302
|
+
if (t === "--") {
|
|
303
|
+
k++;
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
if (t.startsWith("-") && t.length > 1) {
|
|
307
|
+
k += takesValue.has(t) ? 2 : 1;
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
if (name === "env" && ASSIGNMENT.test(t)) {
|
|
311
|
+
k++;
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (name === "timeout" && /^\d+(\.\d+)?[smhd]?$/.test(t)) {
|
|
315
|
+
k++;
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (k >= words.length)
|
|
322
|
+
return null;
|
|
323
|
+
return { name: baseName(words[k].text), args: words.slice(k + 1), viaXargs, nameWord: words[k] };
|
|
324
|
+
}
|
|
325
|
+
function scriptOf(name, args) {
|
|
326
|
+
if (name === "eval")
|
|
327
|
+
return args.map((a) => a.text).join(" ");
|
|
328
|
+
if (!/^(ba|z|k|da|fi)?sh$/.test(name))
|
|
329
|
+
return void 0;
|
|
330
|
+
for (let j = 0; j < args.length; j++) {
|
|
331
|
+
const t = args[j].text;
|
|
332
|
+
if (/^-[A-Za-z]*c[A-Za-z]*$/.test(t))
|
|
333
|
+
return args[j + 1]?.text;
|
|
334
|
+
if (!t.startsWith("-"))
|
|
335
|
+
return void 0;
|
|
336
|
+
}
|
|
337
|
+
return void 0;
|
|
338
|
+
}
|
|
339
|
+
function checkScript(src, ctx, depth) {
|
|
340
|
+
if (depth > 4)
|
|
341
|
+
return null;
|
|
342
|
+
const { commands, inner } = parse(src);
|
|
343
|
+
for (const words of commands) {
|
|
344
|
+
const cmd = commandOf(words);
|
|
345
|
+
if (!cmd)
|
|
346
|
+
continue;
|
|
347
|
+
if (cmd.name === "cd") {
|
|
348
|
+
ctx.cdHint = literalAbsolute(cmd.args[0], ctx.home);
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
const script = scriptOf(cmd.name, cmd.args);
|
|
352
|
+
if (script) {
|
|
353
|
+
const r = checkScript(script, ctx, depth + 1);
|
|
354
|
+
if (r)
|
|
355
|
+
return r;
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
if (DELETERS.has(cmd.name) && !cmd.nameWord?.dynamic) {
|
|
359
|
+
const r = checkDeletion(cmd.name, cmd.args, cmd.viaXargs, ctx);
|
|
360
|
+
if (r)
|
|
361
|
+
return r;
|
|
362
|
+
} else if (cmd.name === "find") {
|
|
363
|
+
const r = checkFind(cmd.args, ctx);
|
|
364
|
+
if (r)
|
|
365
|
+
return r;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
for (const s of inner) {
|
|
369
|
+
const r = checkScript(s, ctx, depth + 1);
|
|
370
|
+
if (r)
|
|
371
|
+
return r;
|
|
372
|
+
}
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
function checkDeletion(name, args, viaXargs, ctx) {
|
|
376
|
+
const operands = [];
|
|
377
|
+
let options = true;
|
|
378
|
+
for (const a of args) {
|
|
379
|
+
if (options && a.text === "--") {
|
|
380
|
+
options = false;
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
if (options && a.text.startsWith("-") && a.text.length > 1 && !a.dynamic)
|
|
384
|
+
continue;
|
|
385
|
+
operands.push(a);
|
|
386
|
+
}
|
|
387
|
+
if (viaXargs) {
|
|
388
|
+
return refuse("rm-unseen-targets", `xargs ${name}`, `\`xargs ${name}\` takes its targets from another command's output, so they cannot be checked before they are deleted. Pass the exact absolute paths to ${name} instead, or use \`find /absolute/folder \u2026 -delete\`.`);
|
|
389
|
+
}
|
|
390
|
+
for (const op of operands) {
|
|
391
|
+
const r = checkTarget(name, op, ctx);
|
|
392
|
+
if (r)
|
|
393
|
+
return r;
|
|
394
|
+
}
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
function checkFind(args, ctx) {
|
|
398
|
+
const deletes = args.some((a, j2) => a.text === "-delete" || /^-(exec|execdir|ok|okdir)$/.test(a.text) && DELETERS.has(baseName(args[j2 + 1]?.text ?? "")));
|
|
399
|
+
if (!deletes)
|
|
400
|
+
return null;
|
|
401
|
+
let j = 0;
|
|
402
|
+
while (j < args.length && /^-[HLP]$|^-O\d$/.test(args[j].text))
|
|
403
|
+
j++;
|
|
404
|
+
if (args[j]?.text === "-D")
|
|
405
|
+
j += 2;
|
|
406
|
+
const starts = [];
|
|
407
|
+
for (; j < args.length; j++) {
|
|
408
|
+
const t = args[j].text;
|
|
409
|
+
if (t.startsWith("-") || t === "(" || t === "!")
|
|
410
|
+
break;
|
|
411
|
+
starts.push(args[j]);
|
|
412
|
+
}
|
|
413
|
+
if (!starts.length) {
|
|
414
|
+
return refuse("rm-relative", "find (no folder)", "`find` without a start folder searches the folder it runs in, which may not be the one you expect. Give it the exact absolute folder: `find /absolute/folder \u2026 -delete`.");
|
|
415
|
+
}
|
|
416
|
+
for (const s of starts) {
|
|
417
|
+
const r = checkTarget("find", s, ctx, true);
|
|
418
|
+
if (r)
|
|
419
|
+
return r;
|
|
420
|
+
}
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
function tooBroad(path, home) {
|
|
424
|
+
const p = trimSlashes(path);
|
|
425
|
+
if (p === "" || p === "/")
|
|
426
|
+
return true;
|
|
427
|
+
const parts = p.split("/").filter(Boolean);
|
|
428
|
+
if (parts.length <= 1)
|
|
429
|
+
return true;
|
|
430
|
+
if ((parts[0] === "Volumes" || parts[0] === "mnt") && parts.length <= 2)
|
|
431
|
+
return true;
|
|
432
|
+
if (parts[0] === "media" && parts.length <= 3)
|
|
433
|
+
return true;
|
|
434
|
+
const h = trimSlashes(home);
|
|
435
|
+
return !!h && (p === h || h.startsWith(`${p}/`));
|
|
436
|
+
}
|
|
437
|
+
function trimSlashes(p) {
|
|
438
|
+
const t = p.replace(/\/+$/, "");
|
|
439
|
+
return t === "" && p.startsWith("/") ? "/" : t;
|
|
440
|
+
}
|
|
441
|
+
function literalAbsolute(w, home) {
|
|
442
|
+
if (!w || w.dynamic || w.glob || w.brace)
|
|
443
|
+
return void 0;
|
|
444
|
+
const t = w.tilde && (w.text === "~" || w.text.startsWith("~/")) ? home + w.text.slice(1) : w.text;
|
|
445
|
+
return t.startsWith("/") && !t.split("/").some((s) => s === "." || s === "..") ? trimSlashes(t) : void 0;
|
|
446
|
+
}
|
|
447
|
+
function checkTarget(name, op, ctx, isFindStart = false) {
|
|
448
|
+
const shown = op.text || "''";
|
|
449
|
+
const verb = isFindStart ? "find \u2026 -delete from" : name;
|
|
450
|
+
if (op.dynamic) {
|
|
451
|
+
return refuse("rm-dynamic", shown, `\`${verb} ${shown}\` uses a variable or a command's output ($VAR, $(\u2026), backticks), so what it deletes is not known until it runs \u2014 and an empty variable turns \`$DIR/*\` into \`/*\`. Write the exact absolute path instead.`);
|
|
452
|
+
}
|
|
453
|
+
let path = op.text;
|
|
454
|
+
if (op.tilde) {
|
|
455
|
+
if (path === "~" || path.startsWith("~/"))
|
|
456
|
+
path = ctx.home + path.slice(1);
|
|
457
|
+
else
|
|
458
|
+
return refuse("rm-dynamic", shown, `\`${shown}\` names another user's home folder. Write the exact absolute path instead.`);
|
|
459
|
+
}
|
|
460
|
+
if (!path.startsWith("/")) {
|
|
461
|
+
return refuse("rm-relative", shown, `\`${verb} ${shown}\` uses a relative path, which depends on the folder the command runs in \u2014 and that is not always the folder you think (a \`cd\` that went elsewhere, a different shell). Write the exact absolute path: \`${relativeFix(name, shown, ctx.cdHint)}\`${ctx.cdHint ? ` (this command \`cd\`s to ${ctx.cdHint} first \u2014 if that is the folder you mean, check it exists, then name it in full)` : ""}.`);
|
|
462
|
+
}
|
|
463
|
+
const segments = path.split("/");
|
|
464
|
+
if (segments.some((s) => s === "." || s === ".." || s.startsWith(".") && /[*?[]/.test(s) && op.glob)) {
|
|
465
|
+
return refuse("rm-dots", shown, `\`${verb} ${shown}\` has a \`.\` or \`..\` in it (or a \`.*\` wildcard, which also matches them). Write the exact absolute path with no dot segments.`);
|
|
466
|
+
}
|
|
467
|
+
const wild = op.glob || op.brace;
|
|
468
|
+
let anchor = path;
|
|
469
|
+
if (wild) {
|
|
470
|
+
const first = segments.findIndex((s) => /[*?[{]/.test(s));
|
|
471
|
+
anchor = segments.slice(0, Math.max(1, first)).join("/") || "/";
|
|
472
|
+
}
|
|
473
|
+
if (tooBroad(anchor, ctx.home)) {
|
|
474
|
+
const what = wild ? `a wildcard in \`${anchor}\`` : `\`${anchor}\``;
|
|
475
|
+
return refuse("rm-too-broad", shown, `\`${verb} ${shown}\` would delete ${what} \u2014 the disk root, a top-level folder, a mounted volume or the home folder. Name the exact folder or files to delete, deeper down.`);
|
|
476
|
+
}
|
|
477
|
+
if (!ctx.exists(trimSlashes(anchor))) {
|
|
478
|
+
return refuse("rm-missing", shown, wild ? `\`${verb} ${shown}\`: the folder \`${anchor}\` does not exist, so this is not the folder you meant. Check the path (ls -d ${anchor}) and use the one that exists.` : `\`${verb} ${shown}\`: \`${path}\` does not exist. Check the path (ls -d ${path}) \u2014 if there is nothing to delete, leave this out.`);
|
|
479
|
+
}
|
|
480
|
+
return null;
|
|
481
|
+
}
|
|
482
|
+
function relativeFix(name, shown, cdHint) {
|
|
483
|
+
const folder = cdHint ?? "/full/path/to";
|
|
484
|
+
const rest = shown.replace(/^\.\/+/, "");
|
|
485
|
+
if (name === "find")
|
|
486
|
+
return `find ${cdHint ?? "/full/path/to/folder"} \u2026 -delete`;
|
|
487
|
+
if (rest === "." || rest === ".." || rest.startsWith("../"))
|
|
488
|
+
return `${name} /full/path/to/folder`;
|
|
489
|
+
return `${name} ${folder === "/" ? "" : folder}/${rest}`;
|
|
490
|
+
}
|
|
491
|
+
function refuse(rule, target, why) {
|
|
492
|
+
return { rule, target, message: `Tesseract refused this command: ${why}
|
|
493
|
+
|
|
494
|
+
${RM_RULES}` };
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// ../host-daemon/src/codex-rm-hook.ts
|
|
498
|
+
var refuse2 = (reason) => {
|
|
499
|
+
process.stderr.write(reason);
|
|
500
|
+
process.exit(2);
|
|
501
|
+
};
|
|
502
|
+
var input = "";
|
|
503
|
+
process.stdin.setEncoding("utf8");
|
|
504
|
+
process.stdin.on("data", (chunk) => {
|
|
505
|
+
input += chunk;
|
|
506
|
+
});
|
|
507
|
+
process.stdin.on("end", () => {
|
|
508
|
+
try {
|
|
509
|
+
const call = JSON.parse(input);
|
|
510
|
+
const command = call.tool_input?.command;
|
|
511
|
+
if (typeof command !== "string") process.exit(0);
|
|
512
|
+
const refusal = checkRmCommand(command);
|
|
513
|
+
if (refusal) refuse2(refusal.message);
|
|
514
|
+
process.exit(0);
|
|
515
|
+
} catch (e) {
|
|
516
|
+
refuse2(`Tesseract could not check this command (${e instanceof Error ? e.message : String(e)}), so it was not run. Try it again.`);
|
|
517
|
+
}
|
|
518
|
+
});
|