typescript-virtual-container 1.5.4 → 1.5.6
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/README.md +107 -541
- package/dist/.tsbuildinfo +1 -1
- package/dist/VirtualShell/shell.js +158 -11
- package/dist/commands/awk.d.ts +6 -11
- package/dist/commands/awk.js +462 -109
- package/dist/commands/bc.d.ts +2 -0
- package/dist/commands/bc.js +28 -0
- package/dist/commands/bzip2.d.ts +11 -0
- package/dist/commands/bzip2.js +91 -0
- package/dist/commands/find.d.ts +2 -2
- package/dist/commands/find.js +212 -37
- package/dist/commands/{ifconfig.d.ts → ip.d.ts} +1 -1
- package/dist/commands/{ifconfig.js → ip.js} +3 -3
- package/dist/commands/jobs.d.ts +4 -0
- package/dist/commands/jobs.js +27 -0
- package/dist/commands/lsof.d.ts +6 -0
- package/dist/commands/lsof.js +30 -0
- package/dist/commands/perl.d.ts +6 -0
- package/dist/commands/perl.js +76 -0
- package/dist/commands/registry.js +22 -3
- package/dist/commands/runtime.js +2 -1
- package/dist/commands/sed.d.ts +2 -2
- package/dist/commands/sed.js +216 -34
- package/dist/commands/set.js +20 -0
- package/dist/commands/sh.js +111 -1
- package/dist/commands/strace.d.ts +6 -0
- package/dist/commands/strace.js +26 -0
- package/dist/commands/tar.d.ts +2 -1
- package/dist/commands/tar.js +138 -52
- package/dist/commands/zip.d.ts +11 -0
- package/dist/commands/zip.js +232 -0
- package/dist/modules/linuxRootfs.js +1 -1
- package/dist/utils/expand.js +67 -1
- package/package.json +5 -4
- package/dist/self-standalone.d.ts +0 -1
- package/dist/self-standalone.js +0 -444
- package/dist/standalone-wo-sftp.d.ts +0 -1
- package/dist/standalone-wo-sftp.js +0 -30
- package/dist/standalone.d.ts +0 -1
- package/dist/standalone.js +0 -61
package/dist/commands/sed.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ShellModule } from "../types/commands";
|
|
2
2
|
/**
|
|
3
|
-
* Stream editor
|
|
3
|
+
* Stream editor — supports s/pat/rep/[gI], d, p, q, =, addresses (N, /re/, N,M, /re/,/re/, $).
|
|
4
4
|
* @category text
|
|
5
|
-
* @params ["-e <expr> [file]"
|
|
5
|
+
* @params ["[-n] [-e <expr>] [file]"]
|
|
6
6
|
*/
|
|
7
7
|
export declare const sedCommand: ShellModule;
|
package/dist/commands/sed.js
CHANGED
|
@@ -1,22 +1,74 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ifFlag } from "./command-helpers";
|
|
2
2
|
import { resolvePath } from "./helpers";
|
|
3
3
|
/**
|
|
4
|
-
* Stream editor
|
|
4
|
+
* Stream editor — supports s/pat/rep/[gI], d, p, q, =, addresses (N, /re/, N,M, /re/,/re/, $).
|
|
5
5
|
* @category text
|
|
6
|
-
* @params ["-e <expr> [file]"
|
|
6
|
+
* @params ["[-n] [-e <expr>] [file]"]
|
|
7
7
|
*/
|
|
8
8
|
export const sedCommand = {
|
|
9
9
|
name: "sed",
|
|
10
10
|
description: "Stream editor for filtering and transforming text",
|
|
11
11
|
category: "text",
|
|
12
|
-
params: ["-e <expr> [file]"
|
|
12
|
+
params: ["[-n] [-e <expr>] [file]"],
|
|
13
13
|
run: ({ authUser, shell, cwd, args, stdin }) => {
|
|
14
14
|
const inPlace = ifFlag(args, ["-i"]);
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
15
|
+
const suppressAuto = ifFlag(args, ["-n"]);
|
|
16
|
+
// Collect all -e expressions and the first non-flag positional
|
|
17
|
+
const exprs = [];
|
|
18
|
+
let fileArg;
|
|
19
|
+
let i = 0;
|
|
20
|
+
while (i < args.length) {
|
|
21
|
+
const a = args[i];
|
|
22
|
+
if (a === "-e" || a === "--expression") {
|
|
23
|
+
i++;
|
|
24
|
+
if (args[i])
|
|
25
|
+
exprs.push(args[i]);
|
|
26
|
+
i++;
|
|
27
|
+
}
|
|
28
|
+
else if (a === "-n" || a === "-i") {
|
|
29
|
+
i++;
|
|
30
|
+
}
|
|
31
|
+
else if (a.startsWith("-e")) {
|
|
32
|
+
exprs.push(a.slice(2));
|
|
33
|
+
i++;
|
|
34
|
+
}
|
|
35
|
+
else if (!a.startsWith("-")) {
|
|
36
|
+
if (exprs.length === 0)
|
|
37
|
+
exprs.push(a);
|
|
38
|
+
else
|
|
39
|
+
fileArg = a;
|
|
40
|
+
i++;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
i++;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// If only one positional collected as expr and no file yet, check for file after
|
|
47
|
+
// Re-parse: first non-flag that follows all -e is the file
|
|
48
|
+
if (exprs.length === 0)
|
|
19
49
|
return { stderr: "sed: no expression", exitCode: 1 };
|
|
50
|
+
// Re-check: if exprs[0] was set from positional, remaining positionals are files
|
|
51
|
+
{
|
|
52
|
+
let foundExprFromFlag = false;
|
|
53
|
+
let j = 0;
|
|
54
|
+
while (j < args.length) {
|
|
55
|
+
const a = args[j];
|
|
56
|
+
if (a === "-e" || a === "--expression") {
|
|
57
|
+
foundExprFromFlag = true;
|
|
58
|
+
j += 2;
|
|
59
|
+
}
|
|
60
|
+
else if (a.startsWith("-e")) {
|
|
61
|
+
foundExprFromFlag = true;
|
|
62
|
+
j++;
|
|
63
|
+
}
|
|
64
|
+
else
|
|
65
|
+
j++;
|
|
66
|
+
}
|
|
67
|
+
if (!foundExprFromFlag) {
|
|
68
|
+
// expr is first positional, file is second
|
|
69
|
+
fileArg = args.filter((a) => !a.startsWith("-")).slice(1)[0];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
20
72
|
let content = stdin ?? "";
|
|
21
73
|
if (fileArg) {
|
|
22
74
|
const p = resolvePath(cwd, fileArg);
|
|
@@ -24,32 +76,162 @@ export const sedCommand = {
|
|
|
24
76
|
content = shell.vfs.readFile(p);
|
|
25
77
|
}
|
|
26
78
|
catch {
|
|
27
|
-
return {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
79
|
+
return { stderr: `sed: ${fileArg}: No such file or directory`, exitCode: 1 };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function parseAddr(s) {
|
|
83
|
+
if (!s)
|
|
84
|
+
return [undefined, s];
|
|
85
|
+
if (s[0] === "$")
|
|
86
|
+
return [{ type: "last" }, s.slice(1)];
|
|
87
|
+
if (/^\d/.test(s)) {
|
|
88
|
+
const m = s.match(/^(\d+)(.*)/s);
|
|
89
|
+
if (m)
|
|
90
|
+
return [{ type: "line", n: parseInt(m[1], 10) }, m[2]];
|
|
91
|
+
}
|
|
92
|
+
if (s[0] === "/") {
|
|
93
|
+
const end = s.indexOf("/", 1);
|
|
94
|
+
if (end !== -1) {
|
|
95
|
+
try {
|
|
96
|
+
const re = new RegExp(s.slice(1, end));
|
|
97
|
+
return [{ type: "regex", re }, s.slice(end + 1)];
|
|
98
|
+
}
|
|
99
|
+
catch { /* bad regex */ }
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return [undefined, s];
|
|
103
|
+
}
|
|
104
|
+
function parseInstrs(expr) {
|
|
105
|
+
const instrs = [];
|
|
106
|
+
// Split on unquoted semicolons or newlines
|
|
107
|
+
const parts = expr.split(/\n|(?<=^|[^\\]);/);
|
|
108
|
+
for (const raw of parts) {
|
|
109
|
+
const part = raw.trim();
|
|
110
|
+
if (!part || part.startsWith("#"))
|
|
111
|
+
continue;
|
|
112
|
+
let rest = part;
|
|
113
|
+
const [addr1, after1] = parseAddr(rest);
|
|
114
|
+
rest = after1.trim();
|
|
115
|
+
let addr2;
|
|
116
|
+
if (rest[0] === ",") {
|
|
117
|
+
rest = rest.slice(1).trim();
|
|
118
|
+
const [a2, after2] = parseAddr(rest);
|
|
119
|
+
addr2 = a2;
|
|
120
|
+
rest = after2.trim();
|
|
121
|
+
}
|
|
122
|
+
const op = rest[0];
|
|
123
|
+
if (!op)
|
|
124
|
+
continue;
|
|
125
|
+
if (op === "s") {
|
|
126
|
+
// s/from/to/flags
|
|
127
|
+
const delim = rest[1] ?? "/";
|
|
128
|
+
const sRe = new RegExp(`^s${re(delim)}((?:[^${re(delim)}\\\\]|\\\\.)*)${re(delim)}((?:[^${re(delim)}\\\\]|\\\\.)*)${re(delim)}([gGiIp]*)$`);
|
|
129
|
+
const m = rest.match(sRe);
|
|
130
|
+
if (!m) {
|
|
131
|
+
instrs.push({ op: "d", addr1, addr2 });
|
|
132
|
+
continue;
|
|
133
|
+
} // bad expr, skip
|
|
134
|
+
const flags = m[3] ?? "";
|
|
135
|
+
let from;
|
|
136
|
+
try {
|
|
137
|
+
from = new RegExp(m[1], flags.includes("i") || flags.includes("I") ? "i" : "");
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
instrs.push({ op: "s", addr1, addr2, from, to: m[2], global: flags.includes("g") || flags.includes("G"), print: flags.includes("p") });
|
|
143
|
+
}
|
|
144
|
+
else if (op === "d") {
|
|
145
|
+
instrs.push({ op: "d", addr1, addr2 });
|
|
146
|
+
}
|
|
147
|
+
else if (op === "p") {
|
|
148
|
+
instrs.push({ op: "p", addr1, addr2 });
|
|
149
|
+
}
|
|
150
|
+
else if (op === "q") {
|
|
151
|
+
instrs.push({ op: "q", addr1 });
|
|
152
|
+
}
|
|
153
|
+
else if (op === "=") {
|
|
154
|
+
instrs.push({ op: "=", addr1, addr2 });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return instrs;
|
|
158
|
+
}
|
|
159
|
+
function re(c) {
|
|
160
|
+
return c.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
161
|
+
}
|
|
162
|
+
const allInstrs = exprs.flatMap(parseInstrs);
|
|
163
|
+
const lines = content.split("\n");
|
|
164
|
+
// Remove trailing empty string from trailing newline
|
|
165
|
+
if (lines[lines.length - 1] === "")
|
|
166
|
+
lines.pop();
|
|
167
|
+
const total = lines.length;
|
|
168
|
+
function matchesAddr(addr, lineNo, line) {
|
|
169
|
+
if (!addr)
|
|
170
|
+
return true;
|
|
171
|
+
if (addr.type === "line")
|
|
172
|
+
return lineNo === addr.n;
|
|
173
|
+
if (addr.type === "last")
|
|
174
|
+
return lineNo === total;
|
|
175
|
+
return addr.re.test(line);
|
|
176
|
+
}
|
|
177
|
+
function inRange(instr, lineNo, line, rangeActive) {
|
|
178
|
+
const { addr1, addr2 } = instr;
|
|
179
|
+
if (!addr1)
|
|
180
|
+
return true;
|
|
181
|
+
if (!addr2)
|
|
182
|
+
return matchesAddr(addr1, lineNo, line);
|
|
183
|
+
// Two-address range
|
|
184
|
+
let active = rangeActive.get(instr) ?? false;
|
|
185
|
+
if (!active && matchesAddr(addr1, lineNo, line)) {
|
|
186
|
+
active = true;
|
|
187
|
+
rangeActive.set(instr, true);
|
|
188
|
+
}
|
|
189
|
+
if (active && matchesAddr(addr2, lineNo, line)) {
|
|
190
|
+
rangeActive.set(instr, false);
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
if (active)
|
|
194
|
+
return true;
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
const out = [];
|
|
198
|
+
const rangeActive = new Map();
|
|
199
|
+
let quit = false;
|
|
200
|
+
for (let li = 0; li < lines.length && !quit; li++) {
|
|
201
|
+
let line = lines[li];
|
|
202
|
+
const lineNo = li + 1;
|
|
203
|
+
let deleted = false;
|
|
204
|
+
for (const instr of allInstrs) {
|
|
205
|
+
if (!inRange(instr, lineNo, line, rangeActive))
|
|
206
|
+
continue;
|
|
207
|
+
if (instr.op === "d") {
|
|
208
|
+
deleted = true;
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
if (instr.op === "p") {
|
|
212
|
+
out.push(line);
|
|
213
|
+
}
|
|
214
|
+
if (instr.op === "=") {
|
|
215
|
+
out.push(String(lineNo));
|
|
216
|
+
}
|
|
217
|
+
if (instr.op === "q") {
|
|
218
|
+
quit = true;
|
|
219
|
+
}
|
|
220
|
+
if (instr.op === "s") {
|
|
221
|
+
const replaced = instr.global
|
|
222
|
+
? line.replace(new RegExp(instr.from.source, instr.from.flags.includes("i") ? "gi" : "g"), instr.to)
|
|
223
|
+
: line.replace(instr.from, instr.to);
|
|
224
|
+
if (replaced !== line) {
|
|
225
|
+
line = replaced;
|
|
226
|
+
if (instr.print && suppressAuto)
|
|
227
|
+
out.push(line);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (!deleted && !suppressAuto)
|
|
232
|
+
out.push(line);
|
|
233
|
+
}
|
|
234
|
+
const result = out.join("\n") + (out.length > 0 ? "\n" : "");
|
|
53
235
|
if (inPlace && fileArg) {
|
|
54
236
|
const p = resolvePath(cwd, fileArg);
|
|
55
237
|
shell.writeFileAsUser(authUser, p, result);
|
package/dist/commands/set.js
CHANGED
|
@@ -11,11 +11,31 @@ export const setCommand = {
|
|
|
11
11
|
run: ({ args, env }) => {
|
|
12
12
|
if (args.length === 0) {
|
|
13
13
|
const out = Object.entries(env.vars)
|
|
14
|
+
.filter(([k]) => !k.startsWith("__"))
|
|
14
15
|
.map(([k, v]) => `${k}=${v}`)
|
|
15
16
|
.join("\n");
|
|
16
17
|
return { stdout: out, exitCode: 0 };
|
|
17
18
|
}
|
|
18
19
|
for (const arg of args) {
|
|
20
|
+
const flagMatch = arg.match(/^([+-])([a-zA-Z]+)$/);
|
|
21
|
+
if (flagMatch) {
|
|
22
|
+
const on = flagMatch[1] === "-";
|
|
23
|
+
for (const flag of flagMatch[2]) {
|
|
24
|
+
if (flag === "e") {
|
|
25
|
+
if (on)
|
|
26
|
+
env.vars.__errexit = "1";
|
|
27
|
+
else
|
|
28
|
+
delete env.vars.__errexit;
|
|
29
|
+
}
|
|
30
|
+
if (flag === "x") {
|
|
31
|
+
if (on)
|
|
32
|
+
env.vars.__xtrace = "1";
|
|
33
|
+
else
|
|
34
|
+
delete env.vars.__xtrace;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
19
39
|
if (arg.includes("=")) {
|
|
20
40
|
const eq = arg.indexOf("=");
|
|
21
41
|
env.vars[arg.slice(0, eq)] = arg.slice(eq + 1);
|
package/dist/commands/sh.js
CHANGED
|
@@ -125,6 +125,69 @@ function parseBlocks(lines) {
|
|
|
125
125
|
}
|
|
126
126
|
blocks.push({ type: "while", cond, body });
|
|
127
127
|
}
|
|
128
|
+
else if (line.startsWith("until ")) {
|
|
129
|
+
const cond = line
|
|
130
|
+
.replace(/^until\s+/, "")
|
|
131
|
+
.replace(/;\s*do\s*$/, "")
|
|
132
|
+
.trim();
|
|
133
|
+
const body = [];
|
|
134
|
+
i++;
|
|
135
|
+
while (i < lines.length && lines[i]?.trim() !== "done") {
|
|
136
|
+
const l = lines[i].trim().replace(/^do\s+/, "");
|
|
137
|
+
if (l && l !== "do")
|
|
138
|
+
body.push(l);
|
|
139
|
+
i++;
|
|
140
|
+
}
|
|
141
|
+
blocks.push({ type: "until", cond, body });
|
|
142
|
+
}
|
|
143
|
+
else if (/^[A-Za-z_][A-Za-z0-9_]*=\s*\(/.test(line)) {
|
|
144
|
+
// Array assignment: arr=(elem1 elem2 ...)
|
|
145
|
+
const arrMatch = line.match(/^([A-Za-z_][A-Za-z0-9_]*)=\s*\(([^)]*)\)$/);
|
|
146
|
+
if (arrMatch) {
|
|
147
|
+
const elems = arrMatch[2].trim().split(/\s+/).filter(Boolean);
|
|
148
|
+
blocks.push({ type: "array", name: arrMatch[1], elements: elems });
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
blocks.push({ type: "cmd", line });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
else if (line.startsWith("case ") && line.endsWith(" in") || line.match(/^case\s+.+\s+in$/)) {
|
|
155
|
+
const caseExpr = line.replace(/^case\s+/, "").replace(/\s+in$/, "").trim();
|
|
156
|
+
const patterns = [];
|
|
157
|
+
i++;
|
|
158
|
+
while (i < lines.length && lines[i]?.trim() !== "esac") {
|
|
159
|
+
const pl = lines[i].trim();
|
|
160
|
+
if (!pl || pl === "esac") {
|
|
161
|
+
i++;
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
// pattern) or pattern1|pattern2)
|
|
165
|
+
const patMatch = pl.match(/^(.+?)\)\s*(.*)$/);
|
|
166
|
+
if (patMatch) {
|
|
167
|
+
const pat = patMatch[1].trim();
|
|
168
|
+
const body = [];
|
|
169
|
+
if (patMatch[2]?.trim() && patMatch[2].trim() !== ";;") {
|
|
170
|
+
body.push(patMatch[2].trim());
|
|
171
|
+
}
|
|
172
|
+
i++;
|
|
173
|
+
while (i < lines.length) {
|
|
174
|
+
const bl = lines[i].trim();
|
|
175
|
+
if (bl === ";;" || bl === "esac")
|
|
176
|
+
break;
|
|
177
|
+
if (bl)
|
|
178
|
+
body.push(bl);
|
|
179
|
+
i++;
|
|
180
|
+
}
|
|
181
|
+
if (lines[i]?.trim() === ";;")
|
|
182
|
+
i++; // skip ;;
|
|
183
|
+
patterns.push({ pattern: pat, body });
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
i++;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
blocks.push({ type: "case", expr: caseExpr, patterns });
|
|
190
|
+
}
|
|
128
191
|
else {
|
|
129
192
|
blocks.push({ type: "cmd", line });
|
|
130
193
|
}
|
|
@@ -189,9 +252,12 @@ async function evalCondition(cond, ctx) {
|
|
|
189
252
|
async function runBlocks(blocks, ctx) {
|
|
190
253
|
let lastResult = { exitCode: 0 };
|
|
191
254
|
let output = "";
|
|
255
|
+
let traceOutput = "";
|
|
192
256
|
for (const block of blocks) {
|
|
193
257
|
if (block.type === "cmd") {
|
|
194
258
|
const expanded = await expandVars(block.line, ctx.env.vars, ctx.env.lastExitCode, ctx);
|
|
259
|
+
if (ctx.env.vars.__xtrace)
|
|
260
|
+
traceOutput += `+ ${expanded}\n`;
|
|
195
261
|
// Bare VAR=val assignment(s) — handle before dispatching to runCommand
|
|
196
262
|
const assignRe = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)/;
|
|
197
263
|
const tokens = expanded.trim().split(/\s+/);
|
|
@@ -231,6 +297,8 @@ async function runBlocks(blocks, ctx) {
|
|
|
231
297
|
output += `${r.stdout}\n`;
|
|
232
298
|
if (r.stderr)
|
|
233
299
|
return { ...r, stdout: output.trim() };
|
|
300
|
+
if (ctx.env.vars.__errexit && (r.exitCode ?? 0) !== 0)
|
|
301
|
+
return { ...r, stdout: output.trim() };
|
|
234
302
|
lastResult = r;
|
|
235
303
|
}
|
|
236
304
|
else if (block.type === "if") {
|
|
@@ -311,8 +379,50 @@ async function runBlocks(blocks, ctx) {
|
|
|
311
379
|
iterations++;
|
|
312
380
|
}
|
|
313
381
|
}
|
|
382
|
+
else if (block.type === "until") {
|
|
383
|
+
let iterations = 0;
|
|
384
|
+
while (iterations < 1000 && !(await evalCondition(block.cond, ctx))) {
|
|
385
|
+
const sub = await runBlocks(parseBlocks(block.body), ctx);
|
|
386
|
+
if (sub.stdout)
|
|
387
|
+
output += `${sub.stdout}\n`;
|
|
388
|
+
if (sub.closeSession)
|
|
389
|
+
return sub;
|
|
390
|
+
iterations++;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
else if (block.type === "array") {
|
|
394
|
+
// Store array: arr[0]=e0, arr[1]=e1, ..., arr=space-joined (for ${arr[@]})
|
|
395
|
+
block.elements.forEach((el, idx) => { ctx.env.vars[`${block.name}[${idx}]`] = el; });
|
|
396
|
+
ctx.env.vars[block.name] = block.elements.join(" ");
|
|
397
|
+
}
|
|
398
|
+
else if (block.type === "case") {
|
|
399
|
+
const expanded = await expandVars(block.expr, ctx.env.vars, ctx.env.lastExitCode, ctx);
|
|
400
|
+
for (const pat of block.patterns) {
|
|
401
|
+
const alts = pat.pattern.split("|").map((p) => p.trim());
|
|
402
|
+
const matched = alts.some((p) => {
|
|
403
|
+
if (p === "*")
|
|
404
|
+
return true;
|
|
405
|
+
if (p.includes("*") || p.includes("?")) {
|
|
406
|
+
const re = new RegExp(`^${p.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".")}$`);
|
|
407
|
+
return re.test(expanded);
|
|
408
|
+
}
|
|
409
|
+
return p === expanded;
|
|
410
|
+
});
|
|
411
|
+
if (matched) {
|
|
412
|
+
const sub = await runBlocks(parseBlocks(pat.body), ctx);
|
|
413
|
+
if (sub.stdout)
|
|
414
|
+
output += `${sub.stdout}\n`;
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
const finalStdout = output.trim() || lastResult.stdout;
|
|
421
|
+
if (traceOutput) {
|
|
422
|
+
const traceStderr = (lastResult.stderr ? `${lastResult.stderr}\n` : "") + traceOutput.trim();
|
|
423
|
+
return { ...lastResult, stdout: finalStdout, stderr: traceStderr || lastResult.stderr };
|
|
314
424
|
}
|
|
315
|
-
return { ...lastResult, stdout:
|
|
425
|
+
return { ...lastResult, stdout: finalStdout };
|
|
316
426
|
}
|
|
317
427
|
/**
|
|
318
428
|
* Execute shell scripts or commands with a minimal shell interpreter.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trace system calls and signals (stub — runs command, emits fake strace output).
|
|
3
|
+
* @category system
|
|
4
|
+
*/
|
|
5
|
+
export const straceCommand = {
|
|
6
|
+
name: "strace",
|
|
7
|
+
description: "Trace system calls and signals",
|
|
8
|
+
category: "system",
|
|
9
|
+
params: ["[-e <expr>] [-o <file>] <command> [args]"],
|
|
10
|
+
run: ({ args }) => {
|
|
11
|
+
const cmd = args.find((a) => !a.startsWith("-"));
|
|
12
|
+
if (!cmd)
|
|
13
|
+
return { stderr: "strace: must have PROG [ARGS] or -p PID", exitCode: 1 };
|
|
14
|
+
const pid = Math.floor(Math.random() * 30000) + 1000;
|
|
15
|
+
const lines = [
|
|
16
|
+
`execve("/usr/bin/${cmd}", ["${cmd}"${args.slice(1).map((a) => `, "${a}"`).join("")}], 0x... /* ... vars */) = 0`,
|
|
17
|
+
`brk(NULL) = 0x${(Math.random() * 0xfffff | 0).toString(16)}000`,
|
|
18
|
+
`access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)`,
|
|
19
|
+
`openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3`,
|
|
20
|
+
`fstat(3, {st_mode=S_IFREG|0644, st_size=...}) = 0`,
|
|
21
|
+
`close(3) = 0`,
|
|
22
|
+
`+++ exited with 0 +++`,
|
|
23
|
+
];
|
|
24
|
+
return { stderr: lines.join("\n"), exitCode: 0 };
|
|
25
|
+
},
|
|
26
|
+
};
|
package/dist/commands/tar.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ShellModule } from "../types/commands";
|
|
2
2
|
/**
|
|
3
|
-
* Archive or extract files with tar
|
|
3
|
+
* Archive or extract files with tar — writes real POSIX ustar binary format.
|
|
4
|
+
* Supports -c/-x/-t, -z (gzip), -j (bzip2 stub), -v (verbose), -f.
|
|
4
5
|
* @category archive
|
|
5
6
|
* @params ["[-czf|-xzf|-tf] <archive> [files...]"]
|
|
6
7
|
*/
|