@zaly/agent 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +50 -0
- package/dist/agent-BaD2kax9.mjs +68 -0
- package/dist/agent-BaD2kax9.mjs.map +1 -0
- package/dist/agent-GDLdDc2w.mjs +902 -0
- package/dist/agent-GDLdDc2w.mjs.map +1 -0
- package/dist/bash-QNMTQ1PD.mjs +142 -0
- package/dist/bash-QNMTQ1PD.mjs.map +1 -0
- package/dist/compactions-3AjPqjeZ.mjs +190 -0
- package/dist/compactions-3AjPqjeZ.mjs.map +1 -0
- package/dist/edit-Qxl4w1C2.mjs +170 -0
- package/dist/edit-Qxl4w1C2.mjs.map +1 -0
- package/dist/env-UUFv5Mye.mjs +30 -0
- package/dist/env-UUFv5Mye.mjs.map +1 -0
- package/dist/fetch-D8cmmA3E.mjs +111 -0
- package/dist/fetch-D8cmmA3E.mjs.map +1 -0
- package/dist/find-Bah9SKWX.mjs +87 -0
- package/dist/find-Bah9SKWX.mjs.map +1 -0
- package/dist/grep-B2Ax7ewy.mjs +244 -0
- package/dist/grep-B2Ax7ewy.mjs.map +1 -0
- package/dist/index-node-esm-D6YQRKEz.mjs +1537 -0
- package/dist/index-node-esm-D6YQRKEz.mjs.map +1 -0
- package/dist/index.d.mts +1605 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +948 -0
- package/dist/index.mjs.map +1 -0
- package/dist/jsonl-Dw_VBp46.mjs +134 -0
- package/dist/jsonl-Dw_VBp46.mjs.map +1 -0
- package/dist/manager-D-lWBTHG.mjs +271 -0
- package/dist/manager-D-lWBTHG.mjs.map +1 -0
- package/dist/markdown-Bv4J8cg3.mjs +53 -0
- package/dist/markdown-Bv4J8cg3.mjs.map +1 -0
- package/dist/masker-Bv9KjkDF.mjs +396 -0
- package/dist/masker-Bv9KjkDF.mjs.map +1 -0
- package/dist/model-_3ZA2xqm.mjs +27 -0
- package/dist/model-_3ZA2xqm.mjs.map +1 -0
- package/dist/notify-DpG_m9iP.mjs +96 -0
- package/dist/notify-DpG_m9iP.mjs.map +1 -0
- package/dist/read-Djsx2NZl.mjs +205 -0
- package/dist/read-Djsx2NZl.mjs.map +1 -0
- package/dist/registry-CFmR5G3J.mjs +564 -0
- package/dist/registry-CFmR5G3J.mjs.map +1 -0
- package/dist/search-CRw247ES.mjs +121 -0
- package/dist/search-CRw247ES.mjs.map +1 -0
- package/dist/session/claude.d.mts +59 -0
- package/dist/session/claude.d.mts.map +1 -0
- package/dist/session/claude.mjs +455 -0
- package/dist/session/claude.mjs.map +1 -0
- package/dist/session-CUUdWFC5.mjs +450 -0
- package/dist/session-CUUdWFC5.mjs.map +1 -0
- package/dist/session-Ds5nJz5A.d.mts +346 -0
- package/dist/session-Ds5nJz5A.d.mts.map +1 -0
- package/dist/session.d.mts +37 -0
- package/dist/session.d.mts.map +1 -0
- package/dist/session.mjs +106 -0
- package/dist/session.mjs.map +1 -0
- package/dist/subagent-baqzjRMc.mjs +136 -0
- package/dist/subagent-baqzjRMc.mjs.map +1 -0
- package/dist/swarm-BJ3gQA1Z.mjs +194 -0
- package/dist/swarm-BJ3gQA1Z.mjs.map +1 -0
- package/dist/swarm-BuFx_xeY.mjs +109 -0
- package/dist/swarm-BuFx_xeY.mjs.map +1 -0
- package/dist/tasks-D8rARHZ1.mjs +65 -0
- package/dist/tasks-D8rARHZ1.mjs.map +1 -0
- package/dist/tasks-WmC270X2.mjs +615 -0
- package/dist/tasks-WmC270X2.mjs.map +1 -0
- package/dist/tools-DW1dbcCz.mjs +695 -0
- package/dist/tools-DW1dbcCz.mjs.map +1 -0
- package/dist/truncate-BSMXgiXj.mjs +102 -0
- package/dist/truncate-BSMXgiXj.mjs.map +1 -0
- package/dist/uuid-ByXP5SO5.mjs +59 -0
- package/dist/uuid-ByXP5SO5.mjs.map +1 -0
- package/dist/wakeup-BYqTnfSp.mjs +29 -0
- package/dist/wakeup-BYqTnfSp.mjs.map +1 -0
- package/dist/write-D3L-92vj.mjs +89 -0
- package/dist/write-D3L-92vj.mjs.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,695 @@
|
|
|
1
|
+
import { safeStringify } from "@zaly/shared";
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
10
|
+
var __exportAll = (all, no_symbols) => {
|
|
11
|
+
let target = {};
|
|
12
|
+
for (var name in all) __defProp(target, name, {
|
|
13
|
+
get: all[name],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
17
|
+
return target;
|
|
18
|
+
};
|
|
19
|
+
var __copyProps = (to, from, except, desc) => {
|
|
20
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
21
|
+
key = keys[i];
|
|
22
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
23
|
+
get: ((k) => from[k]).bind(null, key),
|
|
24
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return to;
|
|
28
|
+
};
|
|
29
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
30
|
+
value: mod,
|
|
31
|
+
enumerable: true
|
|
32
|
+
}) : target, mod));
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region ../../node_modules/.bun/shell-quote@1.9.0/node_modules/shell-quote/quote.js
|
|
35
|
+
var require_quote = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
36
|
+
/** @import { ControlOperator } from './parse' */
|
|
37
|
+
/** @type {ControlOperator['op'][]} */
|
|
38
|
+
var OPS = [
|
|
39
|
+
"||",
|
|
40
|
+
"&&",
|
|
41
|
+
";;",
|
|
42
|
+
"|&",
|
|
43
|
+
"<(",
|
|
44
|
+
"<<<",
|
|
45
|
+
">>",
|
|
46
|
+
">&",
|
|
47
|
+
"<&",
|
|
48
|
+
"&",
|
|
49
|
+
";",
|
|
50
|
+
"(",
|
|
51
|
+
")",
|
|
52
|
+
"|",
|
|
53
|
+
"<",
|
|
54
|
+
">"
|
|
55
|
+
];
|
|
56
|
+
var LINE_TERMINATORS = /[\n\r\u2028\u2029]/;
|
|
57
|
+
var GLOB_SHELL_SPECIAL = /[\s#!"$&'():;<=>@\\^`|]/g;
|
|
58
|
+
/** @type {import('./quote')} */
|
|
59
|
+
module.exports = function quote(xs) {
|
|
60
|
+
return xs.map(function(s) {
|
|
61
|
+
if (s === "") return "''";
|
|
62
|
+
if (s && typeof s === "object") {
|
|
63
|
+
if ("op" in s && s.op === "glob") {
|
|
64
|
+
if (typeof s.pattern !== "string") throw new TypeError("glob token requires a string `pattern`");
|
|
65
|
+
if (LINE_TERMINATORS.test(s.pattern)) throw new TypeError("glob `pattern` must not contain line terminators");
|
|
66
|
+
return s.pattern.replace(GLOB_SHELL_SPECIAL, "\\$&");
|
|
67
|
+
}
|
|
68
|
+
if ("op" in s && typeof s.op === "string") {
|
|
69
|
+
if (OPS.indexOf(s.op) < 0) throw new TypeError("invalid `op` value: " + JSON.stringify(s.op));
|
|
70
|
+
return s.op.replace(/[\s\S]/g, "\\$&");
|
|
71
|
+
}
|
|
72
|
+
if ("comment" in s && typeof s.comment === "string") {
|
|
73
|
+
if (LINE_TERMINATORS.test(s.comment)) throw new TypeError("`comment` must not contain line terminators");
|
|
74
|
+
return "#" + s.comment;
|
|
75
|
+
}
|
|
76
|
+
throw new TypeError("unrecognized object token shape");
|
|
77
|
+
}
|
|
78
|
+
if (/["\s\\]/.test(s) && !/'/.test(s)) return "'" + s.replace(/(['])/g, "\\$1") + "'";
|
|
79
|
+
if (/["'\s]/.test(s)) return "\"" + s.replace(/(["\\$`!])/g, "\\$1") + "\"";
|
|
80
|
+
return String(s).replace(/([A-Za-z]:)?([#!"$&'()*,:;<=>?@[\\\]^`{|}~])/g, "$1\\$2");
|
|
81
|
+
}).join(" ");
|
|
82
|
+
};
|
|
83
|
+
}));
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region ../../node_modules/.bun/shell-quote@1.9.0/node_modules/shell-quote/parse.js
|
|
86
|
+
var require_parse = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
87
|
+
/**
|
|
88
|
+
* @import {
|
|
89
|
+
* ControlOperator,
|
|
90
|
+
* Env,
|
|
91
|
+
* GlobPattern,
|
|
92
|
+
* ParseEntry,
|
|
93
|
+
* } from './parse' */
|
|
94
|
+
var CONTROL = "(?:" + [
|
|
95
|
+
"\\|\\|",
|
|
96
|
+
"\\&\\&",
|
|
97
|
+
";;",
|
|
98
|
+
"\\|\\&",
|
|
99
|
+
"\\<\\(",
|
|
100
|
+
"\\<\\<\\<",
|
|
101
|
+
">>",
|
|
102
|
+
">\\&",
|
|
103
|
+
"<\\&",
|
|
104
|
+
"[&;()|<>]"
|
|
105
|
+
].join("|") + ")";
|
|
106
|
+
var controlRE = new RegExp("^" + CONTROL + "$");
|
|
107
|
+
var META = "|&;()<> \\t";
|
|
108
|
+
var SINGLE_QUOTE = "\"((\\\\\"|[^\"])*?)\"";
|
|
109
|
+
var DOUBLE_QUOTE = "'((\\\\'|[^'])*?)'";
|
|
110
|
+
var hash = /^#$/;
|
|
111
|
+
var SQ = "'";
|
|
112
|
+
var DQ = "\"";
|
|
113
|
+
var DS = "$";
|
|
114
|
+
var TOKEN = "";
|
|
115
|
+
var mult = 4294967296;
|
|
116
|
+
for (var i = 0; i < 4; i++) TOKEN += (mult * Math.random()).toString(16);
|
|
117
|
+
var startsWithToken = new RegExp("^" + TOKEN);
|
|
118
|
+
/**
|
|
119
|
+
* @param {string} s
|
|
120
|
+
* @param {RegExp} r
|
|
121
|
+
*/
|
|
122
|
+
function matchAll(s, r) {
|
|
123
|
+
var origIndex = r.lastIndex;
|
|
124
|
+
var matches = [];
|
|
125
|
+
var matchObj;
|
|
126
|
+
while (matchObj = r.exec(s)) {
|
|
127
|
+
matches[matches.length] = matchObj;
|
|
128
|
+
if (r.lastIndex === matchObj.index) r.lastIndex += 1;
|
|
129
|
+
}
|
|
130
|
+
r.lastIndex = origIndex;
|
|
131
|
+
return matches;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* @param {Env} env
|
|
135
|
+
* @param {string} pre
|
|
136
|
+
* @param {string} key
|
|
137
|
+
*/
|
|
138
|
+
function getVar(env, pre, key) {
|
|
139
|
+
var r = typeof env === "function" ? env(key) : env[key];
|
|
140
|
+
if (typeof r === "undefined" && key != "") r = "";
|
|
141
|
+
else if (typeof r === "undefined") r = "$";
|
|
142
|
+
if (typeof r === "object") return pre + TOKEN + JSON.stringify(r) + TOKEN;
|
|
143
|
+
return pre + r;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* @param {string} string
|
|
147
|
+
* @param {Env} [env]
|
|
148
|
+
* @param {{ escape?: string }} [opts]
|
|
149
|
+
* @returns {ParseEntry[]}
|
|
150
|
+
*/
|
|
151
|
+
function parseInternal(string, env, opts) {
|
|
152
|
+
if (!opts) opts = {};
|
|
153
|
+
var BS = opts.escape || "\\";
|
|
154
|
+
var BAREWORD = "(\\" + BS + "['\"" + META + "]|[^\\s'\"" + META + "])+";
|
|
155
|
+
var matches = matchAll(string, new RegExp(["(" + CONTROL + ")", "(" + BAREWORD + "|" + SINGLE_QUOTE + "|" + DOUBLE_QUOTE + ")+"].join("|"), "g"));
|
|
156
|
+
if (matches.length === 0) return [];
|
|
157
|
+
if (!env) env = {};
|
|
158
|
+
var commented = false;
|
|
159
|
+
return matches.map(function(match) {
|
|
160
|
+
var s = match[0];
|
|
161
|
+
if (!s || commented) return;
|
|
162
|
+
if (controlRE.test(s)) return { op: s };
|
|
163
|
+
/** @type {string | boolean} */
|
|
164
|
+
var quote = false;
|
|
165
|
+
var esc = false;
|
|
166
|
+
var out = "";
|
|
167
|
+
var isGlob = false;
|
|
168
|
+
/** @type {number} */
|
|
169
|
+
var i;
|
|
170
|
+
function parseEnvVar() {
|
|
171
|
+
i += 1;
|
|
172
|
+
/** @type {number | RegExpMatchArray | null} */
|
|
173
|
+
var varend;
|
|
174
|
+
/** @type {string} */
|
|
175
|
+
var varname;
|
|
176
|
+
var char = s.charAt(i);
|
|
177
|
+
if (char === "{") {
|
|
178
|
+
i += 1;
|
|
179
|
+
if (s.charAt(i) === "}") throw new Error("Bad substitution: " + s.slice(i - 2, i + 1));
|
|
180
|
+
varend = s.indexOf("}", i);
|
|
181
|
+
if (varend < 0) throw new Error("Bad substitution: " + s.slice(i));
|
|
182
|
+
varname = s.slice(i, varend);
|
|
183
|
+
i = varend;
|
|
184
|
+
} else if (/[*@#?$!_-]/.test(char)) {
|
|
185
|
+
varname = char;
|
|
186
|
+
i += 1;
|
|
187
|
+
} else {
|
|
188
|
+
var slicedFromI = s.slice(i);
|
|
189
|
+
varend = slicedFromI.match(/[^\w\d_]/);
|
|
190
|
+
if (!varend) {
|
|
191
|
+
varname = slicedFromI;
|
|
192
|
+
i = s.length;
|
|
193
|
+
} else {
|
|
194
|
+
varname = slicedFromI.slice(0, varend.index);
|
|
195
|
+
i += varend.index - 1;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return getVar(env, "", varname);
|
|
199
|
+
}
|
|
200
|
+
for (i = 0; i < s.length; i++) {
|
|
201
|
+
var c = s.charAt(i);
|
|
202
|
+
isGlob = isGlob || !quote && (c === "*" || c === "?");
|
|
203
|
+
if (esc) {
|
|
204
|
+
out += c;
|
|
205
|
+
esc = false;
|
|
206
|
+
} else if (quote) if (c === quote) quote = false;
|
|
207
|
+
else if (quote == SQ) out += c;
|
|
208
|
+
else if (c === BS) {
|
|
209
|
+
i += 1;
|
|
210
|
+
c = s.charAt(i);
|
|
211
|
+
if (c === DQ || c === BS || c === DS) out += c;
|
|
212
|
+
else out += BS + c;
|
|
213
|
+
} else if (c === DS) out += parseEnvVar();
|
|
214
|
+
else out += c;
|
|
215
|
+
else if (c === DQ || c === SQ) quote = c;
|
|
216
|
+
else if (controlRE.test(c)) return { op: s };
|
|
217
|
+
else if (hash.test(c)) {
|
|
218
|
+
commented = true;
|
|
219
|
+
var commentObj = { comment: string.slice(match.index + i + 1) };
|
|
220
|
+
if (out.length) return [out, commentObj];
|
|
221
|
+
return [commentObj];
|
|
222
|
+
} else if (c === BS) esc = true;
|
|
223
|
+
else if (c === DS) out += parseEnvVar();
|
|
224
|
+
else out += c;
|
|
225
|
+
}
|
|
226
|
+
if (isGlob) return {
|
|
227
|
+
op: "glob",
|
|
228
|
+
pattern: out
|
|
229
|
+
};
|
|
230
|
+
return out;
|
|
231
|
+
}).reduce(function(prev, arg) {
|
|
232
|
+
if (typeof arg === "undefined") return prev;
|
|
233
|
+
/** @type {ParseEntry[]} */ [].concat(arg).forEach(function(entry) {
|
|
234
|
+
prev[prev.length] = entry;
|
|
235
|
+
});
|
|
236
|
+
return prev;
|
|
237
|
+
}, []);
|
|
238
|
+
}
|
|
239
|
+
/** @type {import('./parse')} */
|
|
240
|
+
module.exports = function parse(s, env, opts) {
|
|
241
|
+
var mapped = parseInternal(s, env, opts);
|
|
242
|
+
if (typeof env !== "function") return mapped;
|
|
243
|
+
return mapped.reduce(function(acc, s) {
|
|
244
|
+
if (typeof s === "object") {
|
|
245
|
+
acc[acc.length] = s;
|
|
246
|
+
return acc;
|
|
247
|
+
}
|
|
248
|
+
var xs = s.split(RegExp("(" + TOKEN + ".*?" + TOKEN + ")", "g"));
|
|
249
|
+
if (xs.length === 1) {
|
|
250
|
+
acc[acc.length] = xs[0];
|
|
251
|
+
return acc;
|
|
252
|
+
}
|
|
253
|
+
xs.filter(Boolean).forEach(function(x) {
|
|
254
|
+
acc[acc.length] = startsWithToken.test(x) ? JSON.parse(x.split(TOKEN)[1]) : x;
|
|
255
|
+
});
|
|
256
|
+
return acc;
|
|
257
|
+
}, []);
|
|
258
|
+
};
|
|
259
|
+
}));
|
|
260
|
+
//#endregion
|
|
261
|
+
//#region src/utils/bash/parser.ts
|
|
262
|
+
var import_shell_quote = (/* @__PURE__ */ __commonJSMin(((exports) => {
|
|
263
|
+
exports.quote = require_quote();
|
|
264
|
+
exports.parse = require_parse();
|
|
265
|
+
})))();
|
|
266
|
+
/** Targets a redirect can write to without filesystem implications.
|
|
267
|
+
* Anything else is treated as a file write and goes through fileWrite
|
|
268
|
+
* policy. */
|
|
269
|
+
const SAFE_REDIRECT_TARGETS = /* @__PURE__ */ new Set([
|
|
270
|
+
"/dev/null",
|
|
271
|
+
"/dev/stderr",
|
|
272
|
+
"/dev/stdout"
|
|
273
|
+
]);
|
|
274
|
+
/** A redirect target like `&1`, `&2`, `&-` — duplicates an existing fd,
|
|
275
|
+
* no filesystem effect. */
|
|
276
|
+
function isFdRedirect(target) {
|
|
277
|
+
return /^&(?:\d+|-)$/.test(target);
|
|
278
|
+
}
|
|
279
|
+
const COMMAND_SEPS = /* @__PURE__ */ new Set([
|
|
280
|
+
"|",
|
|
281
|
+
"&&",
|
|
282
|
+
"||",
|
|
283
|
+
";"
|
|
284
|
+
]);
|
|
285
|
+
const REDIRECT_OPS = /* @__PURE__ */ new Set([
|
|
286
|
+
">",
|
|
287
|
+
">>",
|
|
288
|
+
"<",
|
|
289
|
+
">&",
|
|
290
|
+
"<&"
|
|
291
|
+
]);
|
|
292
|
+
const DYN_SENTINEL = "";
|
|
293
|
+
/** Parse a bash command line. Returns segments with a flag for
|
|
294
|
+
* unsupported constructs that force "ask" downstream. Heredocs,
|
|
295
|
+
* subshells, command substitution, and backticks all surface as
|
|
296
|
+
* `hasCommandSubst: true` (the umbrella "we can't reason about this"
|
|
297
|
+
* signal) rather than parse failures, so the policy can still decide. */
|
|
298
|
+
function parseBash(input) {
|
|
299
|
+
if (/<<-?\s*['"]?\w+/.test(input)) return {
|
|
300
|
+
ok: false,
|
|
301
|
+
reason: "heredoc not supported"
|
|
302
|
+
};
|
|
303
|
+
const hasBackticks = hasUnquotedBacktick(input);
|
|
304
|
+
let rawTokens;
|
|
305
|
+
try {
|
|
306
|
+
rawTokens = (0, import_shell_quote.parse)(input, (e) => e === "" ? void 0 : `${DYN_SENTINEL}\${${e}}`);
|
|
307
|
+
} catch (error) {
|
|
308
|
+
return {
|
|
309
|
+
ok: false,
|
|
310
|
+
reason: `tokenize: ${error.message}`
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
const flat = flattenParens(rawTokens);
|
|
314
|
+
const hasCommandSubst = hasBackticks || flat.hasCommandSubst;
|
|
315
|
+
const segmentTokens = [[]];
|
|
316
|
+
for (const t of flat.tokens) {
|
|
317
|
+
if (typeof t === "object" && "comment" in t) continue;
|
|
318
|
+
if (typeof t === "object" && "op" in t && COMMAND_SEPS.has(t.op)) {
|
|
319
|
+
if (segmentTokens.at(-1).length > 0) segmentTokens.push([]);
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
segmentTokens.at(-1).push(t);
|
|
323
|
+
}
|
|
324
|
+
if (segmentTokens.at(-1).length === 0) segmentTokens.pop();
|
|
325
|
+
const segments = [];
|
|
326
|
+
for (const segToks of segmentTokens) {
|
|
327
|
+
const seg = parseSegment(segToks);
|
|
328
|
+
if (!seg.ok) return seg;
|
|
329
|
+
if (seg.segment) segments.push({
|
|
330
|
+
...seg.segment,
|
|
331
|
+
hasCommandSubst
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
if (segments.length === 0) return {
|
|
335
|
+
ok: false,
|
|
336
|
+
reason: "empty input"
|
|
337
|
+
};
|
|
338
|
+
return {
|
|
339
|
+
ok: true,
|
|
340
|
+
segments
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
function parseSegment(tokens) {
|
|
344
|
+
let args = [];
|
|
345
|
+
let reads = [];
|
|
346
|
+
let writes = [];
|
|
347
|
+
const dyn = /* @__PURE__ */ new Set();
|
|
348
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
349
|
+
const t = tokens[i];
|
|
350
|
+
if (typeof t === "string") {
|
|
351
|
+
if (/^\d+$/.test(t)) {
|
|
352
|
+
const next = tokens[i + 1];
|
|
353
|
+
if (typeof next === "object" && "op" in next && REDIRECT_OPS.has(next.op)) continue;
|
|
354
|
+
}
|
|
355
|
+
args.push(t);
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
if ("comment" in t) continue;
|
|
359
|
+
if ("pattern" in t) {
|
|
360
|
+
args.push(t.pattern);
|
|
361
|
+
dyn.add(t.pattern);
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
if (!("op" in t)) return {
|
|
365
|
+
ok: false,
|
|
366
|
+
reason: `unsupported token: ${safeStringify(t)}`
|
|
367
|
+
};
|
|
368
|
+
if (REDIRECT_OPS.has(t.op)) {
|
|
369
|
+
const next = tokens[i + 1];
|
|
370
|
+
if (typeof next !== "string") return {
|
|
371
|
+
ok: false,
|
|
372
|
+
reason: `redirect target missing after ${t.op}`
|
|
373
|
+
};
|
|
374
|
+
i++;
|
|
375
|
+
if (t.op === ">" || t.op === ">>") {
|
|
376
|
+
if (SAFE_REDIRECT_TARGETS.has(next) || isFdRedirect(next)) continue;
|
|
377
|
+
writes.push({
|
|
378
|
+
mode: t.op === ">>" ? "append" : "trunc",
|
|
379
|
+
path: next
|
|
380
|
+
});
|
|
381
|
+
} else if (t.op === "<") {
|
|
382
|
+
if (SAFE_REDIRECT_TARGETS.has(next) || isFdRedirect(next)) continue;
|
|
383
|
+
reads.push(next);
|
|
384
|
+
}
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
return {
|
|
388
|
+
ok: false,
|
|
389
|
+
reason: `unsupported op: ${t.op}`
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
const dynPath = (p) => {
|
|
393
|
+
if (!p.includes(DYN_SENTINEL)) return p;
|
|
394
|
+
p = p.replaceAll(DYN_SENTINEL, "");
|
|
395
|
+
dyn.add(p);
|
|
396
|
+
return p;
|
|
397
|
+
};
|
|
398
|
+
args = args.map((a) => dynPath(a));
|
|
399
|
+
writes = writes.map((w) => ({
|
|
400
|
+
...w,
|
|
401
|
+
path: dynPath(w.path)
|
|
402
|
+
}));
|
|
403
|
+
reads = reads.map(dynPath);
|
|
404
|
+
const unwrapped = unwrapWrappers(args);
|
|
405
|
+
if (unwrapped.length === 0) return {
|
|
406
|
+
ok: true,
|
|
407
|
+
segment: void 0
|
|
408
|
+
};
|
|
409
|
+
return {
|
|
410
|
+
ok: true,
|
|
411
|
+
segment: {
|
|
412
|
+
args: unwrapped.slice(1),
|
|
413
|
+
cmd: unwrapped[0],
|
|
414
|
+
dyn,
|
|
415
|
+
reads,
|
|
416
|
+
writes
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
/** Commands that wrap another command — they're transparent for
|
|
421
|
+
* permission purposes, since the security-relevant action is whatever
|
|
422
|
+
* they run. `sudo` is intentionally absent: it changes the effective
|
|
423
|
+
* user and must always be flagged. */
|
|
424
|
+
const WRAPPERS = {
|
|
425
|
+
command: {},
|
|
426
|
+
ionice: { valueFlags: /* @__PURE__ */ new Set([
|
|
427
|
+
"-c",
|
|
428
|
+
"-n",
|
|
429
|
+
"-p",
|
|
430
|
+
"-P",
|
|
431
|
+
"-u"
|
|
432
|
+
]) },
|
|
433
|
+
nice: { valueFlags: /* @__PURE__ */ new Set(["-n", "--adjustment"]) },
|
|
434
|
+
nohup: {},
|
|
435
|
+
time: {}
|
|
436
|
+
};
|
|
437
|
+
/** Detect backticks outside single-quoted regions. Single quotes in
|
|
438
|
+
* bash can't be escaped from within, so the toggle is a simple state
|
|
439
|
+
* flip on every `'`. Double quotes do not suppress backtick
|
|
440
|
+
* substitution and are ignored here. */
|
|
441
|
+
function hasUnquotedBacktick(input) {
|
|
442
|
+
let inSingle = false;
|
|
443
|
+
for (const c of input) {
|
|
444
|
+
if (c === "'") {
|
|
445
|
+
inSingle = !inSingle;
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
if (c === "`" && !inSingle) return true;
|
|
449
|
+
}
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
/** Strip leading wrapper-command prefixes from an arg list. Repeats
|
|
453
|
+
* while the head is a wrapper (so `time nice -n 5 cmd` → `cmd`).
|
|
454
|
+
* Wrapper-flags (and their values, where applicable) are consumed
|
|
455
|
+
* along with the wrapper itself. */
|
|
456
|
+
function unwrapWrappers(args) {
|
|
457
|
+
let current = args;
|
|
458
|
+
while (current.length > 0) {
|
|
459
|
+
const spec = WRAPPERS[current[0]];
|
|
460
|
+
if (!spec) break;
|
|
461
|
+
const valueFlags = spec.valueFlags ?? /* @__PURE__ */ new Set();
|
|
462
|
+
let i = 1;
|
|
463
|
+
while (i < current.length && current[i].startsWith("-")) {
|
|
464
|
+
const flag = current[i];
|
|
465
|
+
i++;
|
|
466
|
+
if (valueFlags.has(flag) && i < current.length) i++;
|
|
467
|
+
}
|
|
468
|
+
current = current.slice(i);
|
|
469
|
+
}
|
|
470
|
+
return current;
|
|
471
|
+
}
|
|
472
|
+
/** Walk a token list and resolve paren constructs:
|
|
473
|
+
* - Plain `(subshell)` → tokens inlined with `;` boundaries on each
|
|
474
|
+
* side, so the contents become independent segments evaluated by
|
|
475
|
+
* the same policy. The subshell's own scope (cwd changes etc.) is
|
|
476
|
+
* irrelevant to permissions — each command is checked individually.
|
|
477
|
+
* - `$(...)` (command substitution) → stripped entirely, with the
|
|
478
|
+
* `hasCommandSubst` flag raised. The dynamic value can't be
|
|
479
|
+
* statically reasoned about. Detected by the leading `$` token
|
|
480
|
+
* that shell-quote emits before the `(`.
|
|
481
|
+
*
|
|
482
|
+
* Recurses into subshell contents to handle nesting and to catch
|
|
483
|
+
* `$(...)` constructs nested inside subshells. */
|
|
484
|
+
function flattenParens(tokens) {
|
|
485
|
+
const out = [];
|
|
486
|
+
let hasCommandSubst = false;
|
|
487
|
+
let i = 0;
|
|
488
|
+
while (i < tokens.length) {
|
|
489
|
+
const t = tokens[i];
|
|
490
|
+
if (typeof t === "object" && "op" in t && t.op === "(") {
|
|
491
|
+
const isSubst = out.at(-1) === "$";
|
|
492
|
+
if (isSubst) out.pop();
|
|
493
|
+
let depth = 1;
|
|
494
|
+
let j = i + 1;
|
|
495
|
+
while (j < tokens.length && depth > 0) {
|
|
496
|
+
const tj = tokens[j];
|
|
497
|
+
if (typeof tj === "object" && "op" in tj) {
|
|
498
|
+
if (tj.op === "(") depth++;
|
|
499
|
+
else if (tj.op === ")") depth--;
|
|
500
|
+
}
|
|
501
|
+
if (depth > 0) j++;
|
|
502
|
+
}
|
|
503
|
+
const inner = tokens.slice(i + 1, j);
|
|
504
|
+
if (isSubst) hasCommandSubst = true;
|
|
505
|
+
else {
|
|
506
|
+
const sub = flattenParens(inner);
|
|
507
|
+
if (sub.hasCommandSubst) hasCommandSubst = true;
|
|
508
|
+
out.push({ op: ";" }, ...sub.tokens, { op: ";" });
|
|
509
|
+
}
|
|
510
|
+
i = j + 1;
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
if (typeof t === "object" && "op" in t && t.op === ")") {
|
|
514
|
+
i++;
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
out.push(t);
|
|
518
|
+
i++;
|
|
519
|
+
}
|
|
520
|
+
return {
|
|
521
|
+
hasCommandSubst,
|
|
522
|
+
tokens: out
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
//#endregion
|
|
526
|
+
//#region src/utils/bash/tools.ts
|
|
527
|
+
/** All bare positional args (no leading dash). Default for many
|
|
528
|
+
* read-only utilities. */
|
|
529
|
+
function positionals(args) {
|
|
530
|
+
return args.filter((a) => !a.startsWith("-") && a !== "");
|
|
531
|
+
}
|
|
532
|
+
/** All positionals after the first one (e.g. `grep PATTERN file...`). */
|
|
533
|
+
function positionalsAfterFirst(args) {
|
|
534
|
+
const out = [];
|
|
535
|
+
let seenFirst = false;
|
|
536
|
+
for (const a of args) {
|
|
537
|
+
if (a.startsWith("-") || a === "") continue;
|
|
538
|
+
if (!seenFirst) {
|
|
539
|
+
seenFirst = true;
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
out.push(a);
|
|
543
|
+
}
|
|
544
|
+
return out;
|
|
545
|
+
}
|
|
546
|
+
/** Flags whose value is the *next* arg, not part of the script. */
|
|
547
|
+
const SED_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
548
|
+
"-e",
|
|
549
|
+
"--expression",
|
|
550
|
+
"-f",
|
|
551
|
+
"--file"
|
|
552
|
+
]);
|
|
553
|
+
/** Walk sed args, returning [scripts, positionalsAfterScript]. The
|
|
554
|
+
* "script" is everything passed via `-e` plus the first bare arg
|
|
555
|
+
* (the implicit script when no `-e` is given). Positionals after
|
|
556
|
+
* that are input files. */
|
|
557
|
+
function sedSplit(args) {
|
|
558
|
+
const scripts = [];
|
|
559
|
+
const files = [];
|
|
560
|
+
let needsImplicitScript = !args.some((a) => a === "-e" || a === "--expression" || a === "-f" || a === "--file");
|
|
561
|
+
for (let i = 0; i < args.length; i++) {
|
|
562
|
+
const a = args[i];
|
|
563
|
+
if (a.startsWith("-")) {
|
|
564
|
+
if (SED_VALUE_FLAGS.has(a) && i + 1 < args.length) {
|
|
565
|
+
if (a === "-e" || a === "--expression") scripts.push(args[i + 1]);
|
|
566
|
+
i++;
|
|
567
|
+
}
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
if (needsImplicitScript) {
|
|
571
|
+
scripts.push(a);
|
|
572
|
+
needsImplicitScript = false;
|
|
573
|
+
continue;
|
|
574
|
+
}
|
|
575
|
+
files.push(a);
|
|
576
|
+
}
|
|
577
|
+
return {
|
|
578
|
+
files,
|
|
579
|
+
scripts
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
const SED_INPLACE = /* @__PURE__ */ new Set(["-i", "--in-place"]);
|
|
583
|
+
function sedReads(args) {
|
|
584
|
+
return sedSplit(args).files;
|
|
585
|
+
}
|
|
586
|
+
function sedWrites(args) {
|
|
587
|
+
return args.some((a) => SED_INPLACE.has(a) || a.startsWith("-i.")) ? sedSplit(args).files : [];
|
|
588
|
+
}
|
|
589
|
+
function sedUnsafe(args) {
|
|
590
|
+
for (const script of sedSplit(args).scripts) if (/(?:^|;|\n)\s*[wWrR]\s+\S/.test(script)) return true;
|
|
591
|
+
return false;
|
|
592
|
+
}
|
|
593
|
+
function awkReads(args) {
|
|
594
|
+
let sawScript = false;
|
|
595
|
+
let usingFFlag = false;
|
|
596
|
+
const files = [];
|
|
597
|
+
for (let i = 0; i < args.length; i++) {
|
|
598
|
+
const a = args[i];
|
|
599
|
+
if (a === "-f" || a === "--file") {
|
|
600
|
+
if (i + 1 < args.length) files.push(args[i + 1]);
|
|
601
|
+
i++;
|
|
602
|
+
sawScript = true;
|
|
603
|
+
usingFFlag = true;
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
if (a.startsWith("-")) {
|
|
607
|
+
if (a === "-v" || a === "-F") i++;
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
if (!sawScript) {
|
|
611
|
+
sawScript = true;
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
files.push(a);
|
|
615
|
+
}
|
|
616
|
+
return usingFFlag ? files : files;
|
|
617
|
+
}
|
|
618
|
+
function awkUnsafe(args) {
|
|
619
|
+
let sawScript = false;
|
|
620
|
+
for (let i = 0; i < args.length; i++) {
|
|
621
|
+
const a = args[i];
|
|
622
|
+
if (a === "-f" || a === "--file" || a === "-v" || a === "-F") {
|
|
623
|
+
i++;
|
|
624
|
+
continue;
|
|
625
|
+
}
|
|
626
|
+
if (a.startsWith("-")) continue;
|
|
627
|
+
if (!sawScript) {
|
|
628
|
+
sawScript = true;
|
|
629
|
+
if (/[>|]/.test(a)) return true;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return false;
|
|
633
|
+
}
|
|
634
|
+
function findReads(args) {
|
|
635
|
+
const out = [];
|
|
636
|
+
for (const a of args) {
|
|
637
|
+
if (a.startsWith("-")) break;
|
|
638
|
+
out.push(a);
|
|
639
|
+
}
|
|
640
|
+
return out.length > 0 ? out : ["."];
|
|
641
|
+
}
|
|
642
|
+
function findUnsafe(args) {
|
|
643
|
+
return args.some((a) => a === "-delete" || a === "-exec" || a === "-execdir" || a === "-ok" || a === "-okdir");
|
|
644
|
+
}
|
|
645
|
+
const dynamicExec = () => true;
|
|
646
|
+
const TOOLS = {
|
|
647
|
+
".": { unsafe: dynamicExec },
|
|
648
|
+
awk: {
|
|
649
|
+
reads: awkReads,
|
|
650
|
+
unsafe: awkUnsafe
|
|
651
|
+
},
|
|
652
|
+
basename: {},
|
|
653
|
+
cat: { reads: positionals },
|
|
654
|
+
cd: {},
|
|
655
|
+
cut: { reads: positionals },
|
|
656
|
+
diff: { reads: positionals },
|
|
657
|
+
dirname: {},
|
|
658
|
+
echo: {},
|
|
659
|
+
eval: { unsafe: dynamicExec },
|
|
660
|
+
exec: { unsafe: dynamicExec },
|
|
661
|
+
file: { reads: positionals },
|
|
662
|
+
find: {
|
|
663
|
+
reads: findReads,
|
|
664
|
+
unsafe: findUnsafe
|
|
665
|
+
},
|
|
666
|
+
grep: { reads: positionalsAfterFirst },
|
|
667
|
+
head: { reads: positionals },
|
|
668
|
+
hexdump: { reads: positionals },
|
|
669
|
+
less: { reads: positionals },
|
|
670
|
+
ls: { reads: positionals },
|
|
671
|
+
more: { reads: positionals },
|
|
672
|
+
od: { reads: positionals },
|
|
673
|
+
printf: {},
|
|
674
|
+
pwd: {},
|
|
675
|
+
readlink: { reads: positionals },
|
|
676
|
+
realpath: { reads: positionals },
|
|
677
|
+
rg: { reads: positionalsAfterFirst },
|
|
678
|
+
sed: {
|
|
679
|
+
reads: sedReads,
|
|
680
|
+
unsafe: sedUnsafe,
|
|
681
|
+
writes: sedWrites
|
|
682
|
+
},
|
|
683
|
+
sort: { reads: positionals },
|
|
684
|
+
source: { unsafe: dynamicExec },
|
|
685
|
+
stat: { reads: positionals },
|
|
686
|
+
tail: { reads: positionals },
|
|
687
|
+
tree: { reads: positionals },
|
|
688
|
+
uniq: { reads: positionals },
|
|
689
|
+
wc: { reads: positionals },
|
|
690
|
+
xxd: { reads: positionals }
|
|
691
|
+
};
|
|
692
|
+
//#endregion
|
|
693
|
+
export { __toESM as a, __exportAll as i, parseBash as n, __commonJSMin as r, TOOLS as t };
|
|
694
|
+
|
|
695
|
+
//# sourceMappingURL=tools-DW1dbcCz.mjs.map
|