typescript-virtual-container 1.2.9 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (281) hide show
  1. package/.vscode/settings.json +0 -1
  2. package/README.md +141 -50
  3. package/biome.json +7 -0
  4. package/dist/SSHMimic/exec.d.ts.map +1 -1
  5. package/dist/SSHMimic/executor.d.ts.map +1 -1
  6. package/dist/SSHMimic/executor.js +32 -16
  7. package/dist/SSHMimic/index.d.ts.map +1 -1
  8. package/dist/SSHMimic/index.js +20 -6
  9. package/dist/VirtualFileSystem/binaryPack.d.ts.map +1 -1
  10. package/dist/VirtualFileSystem/binaryPack.js +29 -6
  11. package/dist/VirtualFileSystem/index.d.ts.map +1 -1
  12. package/dist/VirtualFileSystem/index.js +36 -13
  13. package/dist/VirtualPackageManager/index.d.ts.map +1 -1
  14. package/dist/VirtualPackageManager/index.js +192 -43
  15. package/dist/VirtualShell/index.d.ts +10 -4
  16. package/dist/VirtualShell/index.d.ts.map +1 -1
  17. package/dist/VirtualShell/index.js +18 -7
  18. package/dist/VirtualShell/shell.d.ts.map +1 -1
  19. package/dist/VirtualShell/shell.js +3 -1
  20. package/dist/VirtualShell/shellParser.d.ts.map +1 -1
  21. package/dist/VirtualUserManager/index.d.ts.map +1 -1
  22. package/dist/commands/adduser.d.ts +6 -0
  23. package/dist/commands/adduser.d.ts.map +1 -1
  24. package/dist/commands/adduser.js +6 -0
  25. package/dist/commands/alias.d.ts +5 -0
  26. package/dist/commands/alias.d.ts.map +1 -1
  27. package/dist/commands/alias.js +5 -0
  28. package/dist/commands/apt.d.ts +5 -0
  29. package/dist/commands/apt.d.ts.map +1 -1
  30. package/dist/commands/apt.js +32 -9
  31. package/dist/commands/awk.d.ts +11 -0
  32. package/dist/commands/awk.d.ts.map +1 -1
  33. package/dist/commands/awk.js +15 -2
  34. package/dist/commands/base64.d.ts +5 -0
  35. package/dist/commands/base64.d.ts.map +1 -1
  36. package/dist/commands/base64.js +9 -1
  37. package/dist/commands/cat.d.ts +5 -0
  38. package/dist/commands/cat.d.ts.map +1 -1
  39. package/dist/commands/cat.js +10 -2
  40. package/dist/commands/cd.d.ts +5 -0
  41. package/dist/commands/cd.d.ts.map +1 -1
  42. package/dist/commands/cd.js +5 -0
  43. package/dist/commands/chmod.d.ts +5 -0
  44. package/dist/commands/chmod.d.ts.map +1 -1
  45. package/dist/commands/chmod.js +5 -0
  46. package/dist/commands/cp.d.ts +5 -0
  47. package/dist/commands/cp.d.ts.map +1 -1
  48. package/dist/commands/cp.js +5 -0
  49. package/dist/commands/curl.d.ts +5 -0
  50. package/dist/commands/curl.d.ts.map +1 -1
  51. package/dist/commands/curl.js +34 -6
  52. package/dist/commands/cut.d.ts +5 -0
  53. package/dist/commands/cut.d.ts.map +1 -1
  54. package/dist/commands/cut.js +8 -1
  55. package/dist/commands/date.d.ts +5 -0
  56. package/dist/commands/date.d.ts.map +1 -1
  57. package/dist/commands/date.js +7 -1
  58. package/dist/commands/declare.d.ts +3 -0
  59. package/dist/commands/declare.d.ts.map +1 -0
  60. package/dist/commands/declare.js +39 -0
  61. package/dist/commands/diff.d.ts +5 -0
  62. package/dist/commands/diff.d.ts.map +1 -1
  63. package/dist/commands/diff.js +5 -0
  64. package/dist/commands/dpkg.d.ts +5 -0
  65. package/dist/commands/dpkg.d.ts.map +1 -1
  66. package/dist/commands/dpkg.js +24 -7
  67. package/dist/commands/du.d.ts.map +1 -1
  68. package/dist/commands/du.js +8 -2
  69. package/dist/commands/echo.d.ts +5 -0
  70. package/dist/commands/echo.d.ts.map +1 -1
  71. package/dist/commands/echo.js +13 -4
  72. package/dist/commands/env.d.ts +5 -0
  73. package/dist/commands/env.d.ts.map +1 -1
  74. package/dist/commands/env.js +11 -1
  75. package/dist/commands/exit.d.ts +5 -0
  76. package/dist/commands/exit.d.ts.map +1 -1
  77. package/dist/commands/exit.js +12 -2
  78. package/dist/commands/export.d.ts.map +1 -1
  79. package/dist/commands/export.js +3 -1
  80. package/dist/commands/find.d.ts +5 -0
  81. package/dist/commands/find.d.ts.map +1 -1
  82. package/dist/commands/find.js +5 -0
  83. package/dist/commands/free.d.ts +5 -0
  84. package/dist/commands/free.d.ts.map +1 -1
  85. package/dist/commands/free.js +5 -0
  86. package/dist/commands/grep.d.ts +5 -0
  87. package/dist/commands/grep.d.ts.map +1 -1
  88. package/dist/commands/grep.js +12 -2
  89. package/dist/commands/gzip.d.ts +5 -0
  90. package/dist/commands/gzip.d.ts.map +1 -1
  91. package/dist/commands/gzip.js +18 -2
  92. package/dist/commands/head.d.ts +5 -0
  93. package/dist/commands/head.d.ts.map +1 -1
  94. package/dist/commands/head.js +5 -0
  95. package/dist/commands/help.d.ts.map +1 -1
  96. package/dist/commands/help.js +98 -45
  97. package/dist/commands/history.d.ts +5 -0
  98. package/dist/commands/history.d.ts.map +1 -1
  99. package/dist/commands/history.js +5 -0
  100. package/dist/commands/hostname.d.ts +5 -0
  101. package/dist/commands/hostname.d.ts.map +1 -1
  102. package/dist/commands/hostname.js +5 -0
  103. package/dist/commands/id.d.ts.map +1 -1
  104. package/dist/commands/id.js +4 -1
  105. package/dist/commands/index.d.ts +2 -17
  106. package/dist/commands/index.d.ts.map +1 -1
  107. package/dist/commands/index.js +2 -340
  108. package/dist/commands/ls.d.ts.map +1 -1
  109. package/dist/commands/ls.js +3 -1
  110. package/dist/commands/lsb-release.d.ts.map +1 -1
  111. package/dist/commands/lsb-release.js +8 -2
  112. package/dist/commands/nano.js +1 -1
  113. package/dist/commands/neofetch.js +1 -1
  114. package/dist/commands/node.d.ts +9 -0
  115. package/dist/commands/node.d.ts.map +1 -0
  116. package/dist/commands/node.js +316 -0
  117. package/dist/commands/npm.d.ts +19 -0
  118. package/dist/commands/npm.d.ts.map +1 -0
  119. package/dist/commands/npm.js +109 -0
  120. package/dist/commands/ping.d.ts.map +1 -1
  121. package/dist/commands/ping.js +3 -1
  122. package/dist/commands/printf.d.ts +3 -0
  123. package/dist/commands/printf.d.ts.map +1 -0
  124. package/dist/commands/printf.js +113 -0
  125. package/dist/commands/ps.d.ts.map +1 -1
  126. package/dist/commands/ps.js +4 -1
  127. package/dist/commands/python.d.ts +30 -0
  128. package/dist/commands/python.d.ts.map +1 -0
  129. package/dist/commands/python.js +2058 -0
  130. package/dist/commands/read.d.ts +3 -0
  131. package/dist/commands/read.d.ts.map +1 -0
  132. package/dist/commands/read.js +34 -0
  133. package/dist/commands/registry.d.ts +8 -0
  134. package/dist/commands/registry.d.ts.map +1 -0
  135. package/dist/commands/registry.js +229 -0
  136. package/dist/commands/runtime.d.ts +6 -0
  137. package/dist/commands/runtime.d.ts.map +1 -0
  138. package/dist/commands/runtime.js +280 -0
  139. package/dist/commands/sed.d.ts.map +1 -1
  140. package/dist/commands/sed.js +11 -3
  141. package/dist/commands/set.d.ts.map +1 -1
  142. package/dist/commands/set.js +9 -3
  143. package/dist/commands/sh.d.ts.map +1 -1
  144. package/dist/commands/sh.js +57 -36
  145. package/dist/commands/shift.d.ts +5 -0
  146. package/dist/commands/shift.d.ts.map +1 -0
  147. package/dist/commands/shift.js +52 -0
  148. package/dist/commands/sleep.d.ts.map +1 -1
  149. package/dist/commands/sort.d.ts.map +1 -1
  150. package/dist/commands/sort.js +4 -2
  151. package/dist/commands/source.d.ts.map +1 -1
  152. package/dist/commands/source.js +5 -2
  153. package/dist/commands/sudo.js +1 -1
  154. package/dist/commands/tar.d.ts.map +1 -1
  155. package/dist/commands/tar.js +11 -3
  156. package/dist/commands/tee.d.ts.map +1 -1
  157. package/dist/commands/tee.js +8 -6
  158. package/dist/commands/test.d.ts.map +1 -1
  159. package/dist/commands/test.js +46 -24
  160. package/dist/commands/tr.d.ts.map +1 -1
  161. package/dist/commands/tr.js +3 -1
  162. package/dist/commands/true.d.ts +4 -0
  163. package/dist/commands/true.d.ts.map +1 -0
  164. package/dist/commands/true.js +14 -0
  165. package/dist/commands/type.d.ts.map +1 -1
  166. package/dist/commands/type.js +1 -1
  167. package/dist/commands/uname.d.ts.map +1 -1
  168. package/dist/commands/uname.js +4 -1
  169. package/dist/commands/uniq.d.ts.map +1 -1
  170. package/dist/commands/uptime.d.ts.map +1 -1
  171. package/dist/commands/uptime.js +4 -1
  172. package/dist/commands/wget.d.ts.map +1 -1
  173. package/dist/commands/wget.js +32 -7
  174. package/dist/commands/which.d.ts.map +1 -1
  175. package/dist/commands/xargs.d.ts.map +1 -1
  176. package/dist/commands/xargs.js +1 -1
  177. package/dist/index.d.ts +15 -14
  178. package/dist/index.d.ts.map +1 -1
  179. package/dist/index.js +9 -9
  180. package/dist/modules/linuxRootfs.d.ts +18 -1
  181. package/dist/modules/linuxRootfs.d.ts.map +1 -1
  182. package/dist/modules/linuxRootfs.js +160 -17
  183. package/dist/standalone-wo-sftp.d.ts +2 -0
  184. package/dist/standalone-wo-sftp.d.ts.map +1 -0
  185. package/dist/standalone-wo-sftp.js +30 -0
  186. package/dist/utils/expand.d.ts +50 -0
  187. package/dist/utils/expand.d.ts.map +1 -0
  188. package/dist/utils/expand.js +183 -0
  189. package/dist/utils/vfsDiff.d.ts +90 -0
  190. package/dist/utils/vfsDiff.d.ts.map +1 -0
  191. package/dist/utils/vfsDiff.js +177 -0
  192. package/package.json +2 -1
  193. package/src/SSHMimic/exec.ts +10 -1
  194. package/src/SSHMimic/executor.ts +104 -18
  195. package/src/SSHMimic/index.ts +49 -15
  196. package/src/VirtualFileSystem/binaryPack.ts +35 -8
  197. package/src/VirtualFileSystem/index.ts +78 -28
  198. package/src/VirtualPackageManager/index.ts +208 -49
  199. package/src/VirtualShell/index.ts +35 -7
  200. package/src/VirtualShell/shell.ts +23 -3
  201. package/src/VirtualShell/shellParser.ts +134 -36
  202. package/src/VirtualUserManager/index.ts +7 -2
  203. package/src/commands/adduser.ts +6 -0
  204. package/src/commands/alias.ts +5 -1
  205. package/src/commands/apt.ts +47 -17
  206. package/src/commands/awk.ts +20 -6
  207. package/src/commands/base64.ts +13 -2
  208. package/src/commands/cat.ts +13 -5
  209. package/src/commands/cd.ts +5 -0
  210. package/src/commands/chmod.ts +5 -0
  211. package/src/commands/cp.ts +5 -0
  212. package/src/commands/curl.ts +56 -12
  213. package/src/commands/cut.ts +8 -1
  214. package/src/commands/date.ts +7 -1
  215. package/src/commands/declare.ts +44 -0
  216. package/src/commands/diff.ts +17 -3
  217. package/src/commands/dpkg.ts +33 -11
  218. package/src/commands/du.ts +17 -5
  219. package/src/commands/echo.ts +22 -9
  220. package/src/commands/env.ts +11 -1
  221. package/src/commands/exit.ts +12 -2
  222. package/src/commands/export.ts +3 -1
  223. package/src/commands/find.ts +5 -0
  224. package/src/commands/free.ts +9 -2
  225. package/src/commands/grep.ts +12 -2
  226. package/src/commands/gzip.ts +28 -4
  227. package/src/commands/head.ts +5 -0
  228. package/src/commands/help.ts +121 -47
  229. package/src/commands/history.ts +7 -2
  230. package/src/commands/hostname.ts +5 -0
  231. package/src/commands/id.ts +4 -1
  232. package/src/commands/index.ts +9 -360
  233. package/src/commands/ls.ts +5 -3
  234. package/src/commands/lsb-release.ts +8 -2
  235. package/src/commands/nano.ts +1 -1
  236. package/src/commands/neofetch.ts +1 -1
  237. package/src/commands/node.ts +341 -0
  238. package/src/commands/npm.ts +132 -0
  239. package/src/commands/ping.ts +6 -2
  240. package/src/commands/printf.ts +112 -0
  241. package/src/commands/ps.ts +21 -9
  242. package/src/commands/python.ts +2229 -0
  243. package/src/commands/read.ts +41 -0
  244. package/src/commands/registry.ts +244 -0
  245. package/src/commands/runtime.ts +353 -0
  246. package/src/commands/sed.ts +27 -9
  247. package/src/commands/set.ts +9 -3
  248. package/src/commands/sh.ts +159 -55
  249. package/src/commands/shift.ts +53 -0
  250. package/src/commands/sleep.ts +2 -1
  251. package/src/commands/sort.ts +10 -6
  252. package/src/commands/source.ts +15 -3
  253. package/src/commands/sudo.ts +1 -1
  254. package/src/commands/tar.ts +28 -7
  255. package/src/commands/tee.ts +7 -1
  256. package/src/commands/test.ts +61 -26
  257. package/src/commands/tr.ts +3 -1
  258. package/src/commands/true.ts +17 -0
  259. package/src/commands/type.ts +6 -3
  260. package/src/commands/uname.ts +5 -1
  261. package/src/commands/uniq.ts +8 -2
  262. package/src/commands/uptime.ts +4 -1
  263. package/src/commands/wget.ts +51 -12
  264. package/src/commands/which.ts +5 -2
  265. package/src/commands/xargs.ts +11 -2
  266. package/src/index.ts +23 -24
  267. package/src/modules/linuxRootfs.ts +233 -30
  268. package/src/standalone-wo-sftp.ts +38 -0
  269. package/src/utils/expand.ts +238 -0
  270. package/src/utils/vfsDiff.ts +275 -0
  271. package/standalone-wo-sftp.js +507 -0
  272. package/standalone-wo-sftp.js.map +7 -0
  273. package/standalone.js +253 -191
  274. package/standalone.js.map +4 -4
  275. package/tests/bun-test-shim.ts +9 -1
  276. package/tests/command-helpers.test.ts +1 -5
  277. package/tests/new-features.test.ts +415 -5
  278. package/tests/parser-executor.test.ts +27 -27
  279. package/tests/sftp.test.ts +122 -42
  280. package/tests/users.test.ts +23 -5
  281. package/CHANGELOG.md +0 -150
@@ -1,32 +1,13 @@
1
+ import { expandAsync } from "../utils/expand";
1
2
  import { ifFlag } from "./command-helpers";
2
3
  import { resolvePath } from "./helpers";
3
- import { runCommand } from "./index";
4
- /** Expand $VAR and ${VAR:-default} in a line using the current env (sync, no $(cmd)) */
5
- function expandVarsSync(line, env, lastExit) {
6
- return line
7
- .replace(/\$\?/g, String(lastExit))
8
- .replace(/\$\{([^}:]+):-([^}]*)\}/g, (_, n, d) => env[n] ?? d)
9
- .replace(/\$\{([^}]+)\}/g, (_, n) => env[n] ?? "")
10
- .replace(/\$([A-Za-z_][A-Za-z0-9_]*)/g, (_, n) => env[n] ?? "")
11
- .replace(/^~(\/|$)/, `${env.HOME ?? "/home/user"}$1`);
12
- }
4
+ import { runCommand } from "./runtime";
13
5
  /**
14
- * Expand $VAR, ${VAR:-default}, and $(cmd) substitution asynchronously.
15
- * Used before executing each line in sh script context.
6
+ * Expand all shell forms including $(cmd) substitution.
7
+ * Delegates to centralised expandAsync (single-quote-aware, depth-tracked).
16
8
  */
17
9
  async function expandVars(line, env, lastExit, ctx) {
18
- // $(cmd) substitution first
19
- if (line.includes("$(")) {
20
- const subRe = /\$\(([^)]+)\)/g;
21
- const matches = [...line.matchAll(subRe)];
22
- for (const m of matches) {
23
- const sub = m[1]?.trim() ?? "";
24
- const subResult = await runCommand(sub, ctx.authUser, ctx.hostname, ctx.mode, ctx.cwd, ctx.shell, undefined, ctx.env);
25
- const subOut = (subResult.stdout ?? "").replace(/\n$/, "");
26
- line = line.replace(m[0], subOut);
27
- }
28
- }
29
- return expandVarsSync(line, env, lastExit);
10
+ return expandAsync(line, env, lastExit, (sub) => runCommand(sub, ctx.authUser, ctx.hostname, ctx.mode, ctx.cwd, ctx.shell, undefined, ctx.env).then((r) => r.stdout ?? ""));
30
11
  }
31
12
  /** Very small shell interpreter: supports if/elif/else/fi, for/do/done, while/do/done */
32
13
  function parseBlocks(lines) {
@@ -39,7 +20,10 @@ function parseBlocks(lines) {
39
20
  continue;
40
21
  }
41
22
  if (line.startsWith("if ") || line === "if") {
42
- const cond = line.replace(/^if\s+/, "").replace(/;\s*then\s*$/, "").trim();
23
+ const cond = line
24
+ .replace(/^if\s+/, "")
25
+ .replace(/;\s*then\s*$/, "")
26
+ .trim();
43
27
  const thenLines = [];
44
28
  const elifBlocks = [];
45
29
  const elseLines = [];
@@ -50,7 +34,10 @@ function parseBlocks(lines) {
50
34
  const l = lines[i].trim();
51
35
  if (l.startsWith("elif ")) {
52
36
  section = "elif";
53
- elifCond = l.replace(/^elif\s+/, "").replace(/;\s*then\s*$/, "").trim();
37
+ elifCond = l
38
+ .replace(/^elif\s+/, "")
39
+ .replace(/;\s*then\s*$/, "")
40
+ .trim();
54
41
  elifBlocks.push({ cond: elifCond, body: [] });
55
42
  }
56
43
  else if (l === "else") {
@@ -66,8 +53,13 @@ function parseBlocks(lines) {
66
53
  }
67
54
  i++;
68
55
  }
69
- // biome-ignore lint/suspicious/noThenProperty: expected behavior for if/elif parsing
70
- blocks.push({ type: "if", cond, then: thenLines, elif: elifBlocks, else_: elseLines });
56
+ blocks.push({
57
+ type: "if",
58
+ cond,
59
+ then_: thenLines,
60
+ elif: elifBlocks,
61
+ else_: elseLines,
62
+ });
71
63
  }
72
64
  else if (line.startsWith("for ")) {
73
65
  const m = line.match(/^for\s+(\w+)\s+in\s+(.+?)(?:\s*;\s*do)?$/);
@@ -87,7 +79,10 @@ function parseBlocks(lines) {
87
79
  }
88
80
  }
89
81
  else if (line.startsWith("while ")) {
90
- const cond = line.replace(/^while\s+/, "").replace(/;\s*do\s*$/, "").trim();
82
+ const cond = line
83
+ .replace(/^while\s+/, "")
84
+ .replace(/;\s*do\s*$/, "")
85
+ .trim();
91
86
  const body = [];
92
87
  i++;
93
88
  while (i < lines.length && lines[i]?.trim() !== "done") {
@@ -119,7 +114,7 @@ async function evalCondition(cond, ctx) {
119
114
  if (flag === "f")
120
115
  return ctx.shell.vfs.exists(p) && ctx.shell.vfs.stat(p).type === "file";
121
116
  if (flag === "d")
122
- return ctx.shell.vfs.exists(p) && ctx.shell.vfs.stat(p).type === "directory";
117
+ return (ctx.shell.vfs.exists(p) && ctx.shell.vfs.stat(p).type === "directory");
123
118
  if (flag === "e")
124
119
  return ctx.shell.vfs.exists(p);
125
120
  if (flag === "z")
@@ -165,6 +160,20 @@ async function runBlocks(blocks, ctx) {
165
160
  for (const block of blocks) {
166
161
  if (block.type === "cmd") {
167
162
  const expanded = await expandVars(block.line, ctx.env.vars, ctx.env.lastExitCode, ctx);
163
+ // Bare VAR=val assignment(s) — handle before dispatching to runCommand
164
+ const assignRe = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)/;
165
+ const tokens = expanded.trim().split(/\s+/);
166
+ if (tokens.length > 0 && assignRe.test(tokens[0])) {
167
+ const allAssign = tokens.every((t) => assignRe.test(t));
168
+ if (allAssign) {
169
+ for (const tok of tokens) {
170
+ const m = tok.match(assignRe);
171
+ ctx.env.vars[m[1]] = m[2];
172
+ }
173
+ ctx.env.lastExitCode = 0;
174
+ continue;
175
+ }
176
+ }
168
177
  const r = await runCommand(expanded, ctx.authUser, ctx.hostname, ctx.mode, ctx.cwd, ctx.shell, undefined, ctx.env);
169
178
  ctx.env.lastExitCode = r.exitCode ?? 0;
170
179
  if (r.stdout)
@@ -176,7 +185,7 @@ async function runBlocks(blocks, ctx) {
176
185
  else if (block.type === "if") {
177
186
  let ran = false;
178
187
  if (await evalCondition(block.cond, ctx)) {
179
- const sub = await runBlocks(parseBlocks(block.then), ctx);
188
+ const sub = await runBlocks(parseBlocks(block.then_), ctx);
180
189
  if (sub.stdout)
181
190
  output += `${sub.stdout}\n`;
182
191
  ran = true;
@@ -212,7 +221,7 @@ async function runBlocks(blocks, ctx) {
212
221
  }
213
222
  else if (block.type === "while") {
214
223
  let iterations = 0;
215
- while (iterations < 1000 && await evalCondition(block.cond, ctx)) {
224
+ while (iterations < 1000 && (await evalCondition(block.cond, ctx))) {
216
225
  const sub = await runBlocks(parseBlocks(block.body), ctx);
217
226
  if (sub.stdout)
218
227
  output += `${sub.stdout}\n`;
@@ -237,7 +246,10 @@ export const shCommand = {
237
246
  const script = args[args.indexOf("-c") + 1] ?? "";
238
247
  if (!script)
239
248
  return { stderr: "sh: -c requires a script", exitCode: 1 };
240
- const lines = script.split(/[;\n]/).map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
249
+ const lines = script
250
+ .split(/[;\n]/)
251
+ .map((l) => l.trim())
252
+ .filter((l) => l && !l.startsWith("#"));
241
253
  const blocks = parseBlocks(lines);
242
254
  return runBlocks(blocks, ctx);
243
255
  }
@@ -246,12 +258,21 @@ export const shCommand = {
246
258
  if (fileArg) {
247
259
  const p = resolvePath(cwd, fileArg);
248
260
  if (!shell.vfs.exists(p))
249
- return { stderr: `sh: ${fileArg}: No such file or directory`, exitCode: 1 };
261
+ return {
262
+ stderr: `sh: ${fileArg}: No such file or directory`,
263
+ exitCode: 1,
264
+ };
250
265
  const content = shell.vfs.readFile(p);
251
- const lines = content.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
266
+ const lines = content
267
+ .split("\n")
268
+ .map((l) => l.trim())
269
+ .filter((l) => l && !l.startsWith("#"));
252
270
  const blocks = parseBlocks(lines);
253
271
  return runBlocks(blocks, ctx);
254
272
  }
255
- return { stderr: "sh: invalid usage. Use: sh -c 'cmd' or sh <file>", exitCode: 1 };
273
+ return {
274
+ stderr: "sh: invalid usage. Use: sh -c 'cmd' or sh <file>",
275
+ exitCode: 1,
276
+ };
256
277
  },
257
278
  };
@@ -0,0 +1,5 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const shiftCommand: ShellModule;
3
+ export declare const trapCommand: ShellModule;
4
+ export declare const returnCommand: ShellModule;
5
+ //# sourceMappingURL=shift.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shift.d.ts","sourceRoot":"","sources":["../../src/commands/shift.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,YAAY,EAAE,WAoB1B,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,WAezB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,WAW3B,CAAC"}
@@ -0,0 +1,52 @@
1
+ export const shiftCommand = {
2
+ name: "shift",
3
+ description: "Shift positional parameters",
4
+ category: "shell",
5
+ params: ["[n]"],
6
+ // shift is meaningful only inside sh scripts where positional params exist.
7
+ // In the current impl, positional params ($1 $2 …) aren't tracked in env by default.
8
+ // We store them under env.vars.__argv and shift there if present.
9
+ run: ({ args, env }) => {
10
+ if (!env)
11
+ return { exitCode: 0 };
12
+ const n = parseInt(args[0] ?? "1", 10) || 1;
13
+ const argv = env.vars.__argv?.split("\x00").filter(Boolean) ?? [];
14
+ env.vars.__argv = argv.slice(n).join("\x00");
15
+ // Update $1 $2 … in env
16
+ const shifted = argv.slice(n);
17
+ for (let i = 1; i <= 9; i++) {
18
+ env.vars[String(i)] = shifted[i - 1] ?? "";
19
+ }
20
+ return { exitCode: 0 };
21
+ },
22
+ };
23
+ export const trapCommand = {
24
+ name: "trap",
25
+ description: "Trap signals and events",
26
+ category: "shell",
27
+ params: ["[action] [signal...]"],
28
+ // Store trap handlers in env for EXIT signal support
29
+ run: ({ args, env }) => {
30
+ if (!env || args.length === 0)
31
+ return { exitCode: 0 };
32
+ const action = args[0] ?? "";
33
+ const signals = args.slice(1);
34
+ for (const sig of signals) {
35
+ env.vars[`__trap_${sig.toUpperCase()}`] = action;
36
+ }
37
+ return { exitCode: 0 };
38
+ },
39
+ };
40
+ export const returnCommand = {
41
+ name: "return",
42
+ description: "Return from a shell function",
43
+ category: "shell",
44
+ params: ["[n]"],
45
+ run: ({ args, env }) => {
46
+ const code = parseInt(args[0] ?? "0", 10);
47
+ if (env)
48
+ env.lastExitCode = code;
49
+ // Signal the caller via exitCode; function return is handled by runBlocks
50
+ return { exitCode: code };
51
+ },
52
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"sleep.d.ts","sourceRoot":"","sources":["../../src/commands/sleep.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,YAAY,EAAE,WAW1B,CAAC"}
1
+ {"version":3,"file":"sleep.d.ts","sourceRoot":"","sources":["../../src/commands/sleep.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,YAAY,EAAE,WAY1B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"sort.d.ts","sourceRoot":"","sources":["../../src/commands/sort.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,WAAW,EAAE,WAgCzB,CAAC"}
1
+ {"version":3,"file":"sort.d.ts","sourceRoot":"","sources":["../../src/commands/sort.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,WAAW,EAAE,WAoCzB,CAAC"}
@@ -12,7 +12,8 @@ export const sortCommand = {
12
12
  const files = args.filter((a) => !a.startsWith("-"));
13
13
  const getContent = () => {
14
14
  if (files.length > 0) {
15
- return files.map((f) => {
15
+ return files
16
+ .map((f) => {
16
17
  try {
17
18
  assertPathAccess(authUser, resolvePath(cwd, f), "sort");
18
19
  return shell.vfs.readFile(resolvePath(cwd, f));
@@ -20,7 +21,8 @@ export const sortCommand = {
20
21
  catch {
21
22
  return "";
22
23
  }
23
- }).join("\n");
24
+ })
25
+ .join("\n");
24
26
  }
25
27
  return stdin ?? "";
26
28
  };
@@ -1 +1 @@
1
- {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../src/commands/source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,aAAa,EAAE,WA8B3B,CAAC"}
1
+ {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../src/commands/source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,aAAa,EAAE,WA0C3B,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { resolvePath } from "./helpers";
2
- import { runCommand } from ".";
2
+ import { runCommand } from "./runtime";
3
3
  export const sourceCommand = {
4
4
  name: "source",
5
5
  aliases: ["."],
@@ -13,7 +13,10 @@ export const sourceCommand = {
13
13
  }
14
14
  const filePath = resolvePath(cwd, fileArg);
15
15
  if (!shell.vfs.exists(filePath)) {
16
- return { stderr: `source: ${fileArg}: No such file or directory`, exitCode: 1 };
16
+ return {
17
+ stderr: `source: ${fileArg}: No such file or directory`,
18
+ exitCode: 1,
19
+ };
17
20
  }
18
21
  const content = shell.vfs.readFile(filePath);
19
22
  let lastExitCode = 0;
@@ -1,5 +1,5 @@
1
1
  import { parseArgs } from "./command-helpers";
2
- import { runCommand } from "./index";
2
+ import { runCommand } from "./runtime";
3
3
  function parseSudoArgs(args) {
4
4
  const { flags, flagsWithValues, positionals } = parseArgs(args, {
5
5
  flags: ["-i", "-S"],
@@ -1 +1 @@
1
- {"version":3,"file":"tar.d.ts","sourceRoot":"","sources":["../../src/commands/tar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,UAAU,EAAE,WAqDxB,CAAC"}
1
+ {"version":3,"file":"tar.d.ts","sourceRoot":"","sources":["../../src/commands/tar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,UAAU,EAAE,WA0ExB,CAAC"}
@@ -10,7 +10,9 @@ export const tarCommand = {
10
10
  const extract = ifFlag(args, ["-x"]);
11
11
  const list = ifFlag(args, ["-t"]);
12
12
  const fFlag = args.findIndex((a) => a.includes("f"));
13
- const archiveName = fFlag !== -1 ? args[fFlag + 1] : args.find((a) => a.endsWith(".tar") || a.endsWith(".tar.gz") || a.endsWith(".tgz"));
13
+ const archiveName = fFlag !== -1
14
+ ? args[fFlag + 1]
15
+ : args.find((a) => a.endsWith(".tar") || a.endsWith(".tar.gz") || a.endsWith(".tgz"));
14
16
  if (!archiveName)
15
17
  return { stderr: "tar: no archive specified", exitCode: 1 };
16
18
  const archivePath = resolvePath(cwd, archiveName);
@@ -38,7 +40,10 @@ export const tarCommand = {
38
40
  }
39
41
  }
40
42
  catch {
41
- return { stderr: `tar: ${f}: No such file or directory`, exitCode: 1 };
43
+ return {
44
+ stderr: `tar: ${f}: No such file or directory`,
45
+ exitCode: 1,
46
+ };
42
47
  }
43
48
  }
44
49
  shell.writeFileAsUser(authUser, archivePath, JSON.stringify(entries));
@@ -50,7 +55,10 @@ export const tarCommand = {
50
55
  entries = JSON.parse(shell.vfs.readFile(archivePath));
51
56
  }
52
57
  catch {
53
- return { stderr: `tar: ${archiveName}: cannot open archive`, exitCode: 1 };
58
+ return {
59
+ stderr: `tar: ${archiveName}: cannot open archive`,
60
+ exitCode: 1,
61
+ };
54
62
  }
55
63
  if (list)
56
64
  return { stdout: Object.keys(entries).join("\n"), exitCode: 0 };
@@ -1 +1 @@
1
- {"version":3,"file":"tee.d.ts","sourceRoot":"","sources":["../../src/commands/tee.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,UAAU,EAAE,WAoBxB,CAAC"}
1
+ {"version":3,"file":"tee.d.ts","sourceRoot":"","sources":["../../src/commands/tee.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,UAAU,EAAE,WA0BxB,CAAC"}
@@ -12,12 +12,14 @@ export const teeCommand = {
12
12
  for (const f of files) {
13
13
  const p = resolvePath(cwd, f);
14
14
  if (append) {
15
- const existing = (() => { try {
16
- return shell.vfs.readFile(p);
17
- }
18
- catch {
19
- return "";
20
- } })();
15
+ const existing = (() => {
16
+ try {
17
+ return shell.vfs.readFile(p);
18
+ }
19
+ catch {
20
+ return "";
21
+ }
22
+ })();
21
23
  shell.writeFileAsUser(authUser, p, existing + input);
22
24
  }
23
25
  else {
@@ -1 +1 @@
1
- {"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../src/commands/test.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAqFrD,eAAO,MAAM,WAAW,EAAE,WAczB,CAAC"}
1
+ {"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../src/commands/test.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAwHrD,eAAO,MAAM,WAAW,EAAE,WAczB,CAAC"}
@@ -22,13 +22,13 @@ function evalTest(tokens, shell, cwd) {
22
22
  // Boolean -a / -o (simple left-right, no precedence)
23
23
  const andIdx = tokens.indexOf("-a");
24
24
  if (andIdx !== -1) {
25
- return evalTest(tokens.slice(0, andIdx), shell, cwd) &&
26
- evalTest(tokens.slice(andIdx + 1), shell, cwd);
25
+ return (evalTest(tokens.slice(0, andIdx), shell, cwd) &&
26
+ evalTest(tokens.slice(andIdx + 1), shell, cwd));
27
27
  }
28
28
  const orIdx = tokens.indexOf("-o");
29
29
  if (orIdx !== -1) {
30
- return evalTest(tokens.slice(0, orIdx), shell, cwd) ||
31
- evalTest(tokens.slice(orIdx + 1), shell, cwd);
30
+ return (evalTest(tokens.slice(0, orIdx), shell, cwd) ||
31
+ evalTest(tokens.slice(orIdx + 1), shell, cwd));
32
32
  }
33
33
  // Unary file tests
34
34
  if (tokens.length === 2) {
@@ -36,16 +36,28 @@ function evalTest(tokens, shell, cwd) {
36
36
  const resolvePath = (p) => p.startsWith("/") ? p : `${cwd}/${p}`.replace(/\/+/g, "/");
37
37
  const path = resolvePath(operand);
38
38
  switch (flag) {
39
- case "-e": return shell.vfs.exists(path);
40
- case "-f": return shell.vfs.exists(path) && shell.vfs.stat(path).type === "file";
41
- case "-d": return shell.vfs.exists(path) && shell.vfs.stat(path).type === "directory";
42
- case "-r": return shell.vfs.exists(path); // all readable in virtual env
43
- case "-w": return shell.vfs.exists(path);
44
- case "-x": return shell.vfs.exists(path) && !!(shell.vfs.stat(path).mode & 0o111);
45
- case "-s": return shell.vfs.exists(path) && shell.vfs.stat(path).type === "file" && shell.vfs.stat(path).size > 0;
46
- case "-z": return operand.length === 0;
47
- case "-n": return operand.length > 0;
48
- case "-L": return shell.vfs.isSymlink(path);
39
+ case "-e":
40
+ return shell.vfs.exists(path);
41
+ case "-f":
42
+ return shell.vfs.exists(path) && shell.vfs.stat(path).type === "file";
43
+ case "-d":
44
+ return (shell.vfs.exists(path) && shell.vfs.stat(path).type === "directory");
45
+ case "-r":
46
+ return shell.vfs.exists(path); // all readable in virtual env
47
+ case "-w":
48
+ return shell.vfs.exists(path);
49
+ case "-x":
50
+ return shell.vfs.exists(path) && !!(shell.vfs.stat(path).mode & 0o111);
51
+ case "-s":
52
+ return (shell.vfs.exists(path) &&
53
+ shell.vfs.stat(path).type === "file" &&
54
+ shell.vfs.stat(path).size > 0);
55
+ case "-z":
56
+ return operand.length === 0;
57
+ case "-n":
58
+ return operand.length > 0;
59
+ case "-L":
60
+ return shell.vfs.isSymlink(path);
49
61
  }
50
62
  }
51
63
  // Binary comparisons
@@ -56,17 +68,27 @@ function evalTest(tokens, shell, cwd) {
56
68
  switch (op) {
57
69
  // String
58
70
  case "=":
59
- case "==": return left === right;
60
- case "!=": return left !== right;
61
- case "<": return left < right;
62
- case ">": return left > right;
71
+ case "==":
72
+ return left === right;
73
+ case "!=":
74
+ return left !== right;
75
+ case "<":
76
+ return left < right;
77
+ case ">":
78
+ return left > right;
63
79
  // Numeric
64
- case "-eq": return leftN === rightN;
65
- case "-ne": return leftN !== rightN;
66
- case "-lt": return leftN < rightN;
67
- case "-le": return leftN <= rightN;
68
- case "-gt": return leftN > rightN;
69
- case "-ge": return leftN >= rightN;
80
+ case "-eq":
81
+ return leftN === rightN;
82
+ case "-ne":
83
+ return leftN !== rightN;
84
+ case "-lt":
85
+ return leftN < rightN;
86
+ case "-le":
87
+ return leftN <= rightN;
88
+ case "-gt":
89
+ return leftN > rightN;
90
+ case "-ge":
91
+ return leftN >= rightN;
70
92
  }
71
93
  }
72
94
  // Single string (truthy if non-empty)
@@ -1 +1 @@
1
- {"version":3,"file":"tr.d.ts","sourceRoot":"","sources":["../../src/commands/tr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,SAAS,EAAE,WAoBvB,CAAC"}
1
+ {"version":3,"file":"tr.d.ts","sourceRoot":"","sources":["../../src/commands/tr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,SAAS,EAAE,WAsBvB,CAAC"}
@@ -16,7 +16,9 @@ export const trCommand = {
16
16
  }
17
17
  else if (set2) {
18
18
  for (let i = 0; i < set1.length; i++) {
19
- input = input.split(set1[i]).join(set2[i] ?? set2[set2.length - 1] ?? "");
19
+ input = input
20
+ .split(set1[i])
21
+ .join(set2[i] ?? set2[set2.length - 1] ?? "");
20
22
  }
21
23
  }
22
24
  return { stdout: input, exitCode: 0 };
@@ -0,0 +1,4 @@
1
+ import type { ShellModule } from "../types/commands";
2
+ export declare const trueCommand: ShellModule;
3
+ export declare const falseCommand: ShellModule;
4
+ //# sourceMappingURL=true.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"true.d.ts","sourceRoot":"","sources":["../../src/commands/true.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,WAAW,EAAE,WAMzB,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,WAM1B,CAAC"}
@@ -0,0 +1,14 @@
1
+ export const trueCommand = {
2
+ name: "true",
3
+ description: "Return success exit code",
4
+ category: "shell",
5
+ params: [],
6
+ run: () => ({ exitCode: 0 }),
7
+ };
8
+ export const falseCommand = {
9
+ name: "false",
10
+ description: "Return failure exit code",
11
+ category: "shell",
12
+ params: [],
13
+ run: () => ({ exitCode: 1 }),
14
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../src/commands/type.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,WAAW,EAAE,WAoCzB,CAAC"}
1
+ {"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../src/commands/type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,WAAW,EAAE,WAuCzB,CAAC"}
@@ -1,4 +1,4 @@
1
- import { resolveModule } from ".";
1
+ import { resolveModule } from "./registry";
2
2
  export const typeCommand = {
3
3
  name: "type",
4
4
  description: "Describe how a command would be interpreted",
@@ -1 +1 @@
1
- {"version":3,"file":"uname.d.ts","sourceRoot":"","sources":["../../src/commands/uname.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,YAAY,EAAE,WAgB1B,CAAC"}
1
+ {"version":3,"file":"uname.d.ts","sourceRoot":"","sources":["../../src/commands/uname.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,YAAY,EAAE,WAoB1B,CAAC"}
@@ -11,7 +11,10 @@ export const unameCommand = {
11
11
  const machine = shell.properties?.arch ?? "x86_64";
12
12
  const hostname = shell.hostname;
13
13
  if (all)
14
- return { stdout: `${sysname} ${hostname} ${release} #1 SMP ${machine} GNU/Linux`, exitCode: 0 };
14
+ return {
15
+ stdout: `${sysname} ${hostname} ${release} #1 SMP ${machine} GNU/Linux`,
16
+ exitCode: 0,
17
+ };
15
18
  if (ifFlag(args, ["-r"]))
16
19
  return { stdout: release, exitCode: 0 };
17
20
  if (ifFlag(args, ["-m"]))
@@ -1 +1 @@
1
- {"version":3,"file":"uniq.d.ts","sourceRoot":"","sources":["../../src/commands/uniq.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,WAAW,EAAE,WAwBzB,CAAC"}
1
+ {"version":3,"file":"uniq.d.ts","sourceRoot":"","sources":["../../src/commands/uniq.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,WAAW,EAAE,WA8BzB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"uptime.d.ts","sourceRoot":"","sources":["../../src/commands/uptime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,aAAa,EAAE,WA0C3B,CAAC"}
1
+ {"version":3,"file":"uptime.d.ts","sourceRoot":"","sources":["../../src/commands/uptime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,eAAO,MAAM,aAAa,EAAE,WA6C3B,CAAC"}
@@ -13,7 +13,10 @@ export const uptimeCommand = {
13
13
  const mins = Math.floor((uptimeSec % 3600) / 60);
14
14
  if (since) {
15
15
  return {
16
- stdout: new Date(shell.startTime).toISOString().slice(0, 19).replace("T", " "),
16
+ stdout: new Date(shell.startTime)
17
+ .toISOString()
18
+ .slice(0, 19)
19
+ .replace("T", " "),
17
20
  exitCode: 0,
18
21
  };
19
22
  }
@@ -1 +1 @@
1
- {"version":3,"file":"wget.d.ts","sourceRoot":"","sources":["../../src/commands/wget.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,WAAW,EAAE,WAwFzB,CAAC"}
1
+ {"version":3,"file":"wget.d.ts","sourceRoot":"","sources":["../../src/commands/wget.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,eAAO,MAAM,WAAW,EAAE,WA+HzB,CAAC"}
@@ -7,7 +7,16 @@ export const wgetCommand = {
7
7
  params: ["[options] <url>"],
8
8
  run: async ({ authUser, cwd, args, shell }) => {
9
9
  const { flagsWithValues, positionals } = parseArgs(args, {
10
- flagsWithValue: ["-O", "--output-document", "-o", "--output-file", "-P", "--directory-prefix", "--tries", "--timeout"],
10
+ flagsWithValue: [
11
+ "-O",
12
+ "--output-document",
13
+ "-o",
14
+ "--output-file",
15
+ "-P",
16
+ "--directory-prefix",
17
+ "--tries",
18
+ "--timeout",
19
+ ],
11
20
  });
12
21
  if (ifFlag(args, ["-h", "--help"])) {
13
22
  return {
@@ -25,13 +34,23 @@ export const wgetCommand = {
25
34
  };
26
35
  }
27
36
  if (ifFlag(args, ["-V", "--version"])) {
28
- return { stdout: "GNU Wget 1.21.3 (virtual) built on Fortune GNU/Linux.", exitCode: 0 };
37
+ return {
38
+ stdout: "GNU Wget 1.21.3 (virtual) built on Fortune GNU/Linux.",
39
+ exitCode: 0,
40
+ };
29
41
  }
30
42
  const url = positionals[0];
31
43
  if (!url)
32
- return { stderr: "wget: missing URL\nUsage: wget [OPTION]... [URL]...", exitCode: 1 };
33
- const outputArg = flagsWithValues.get("-O") ?? flagsWithValues.get("--output-document") ?? null;
34
- const dirPrefix = flagsWithValues.get("-P") ?? flagsWithValues.get("--directory-prefix") ?? null;
44
+ return {
45
+ stderr: "wget: missing URL\nUsage: wget [OPTION]... [URL]...",
46
+ exitCode: 1,
47
+ };
48
+ const outputArg = flagsWithValues.get("-O") ??
49
+ flagsWithValues.get("--output-document") ??
50
+ null;
51
+ const dirPrefix = flagsWithValues.get("-P") ??
52
+ flagsWithValues.get("--directory-prefix") ??
53
+ null;
35
54
  const quiet = ifFlag(args, ["-q", "--quiet"]);
36
55
  // Derive target filename
37
56
  const filename = outputArg === "-" ? null : (outputArg ?? stripUrlFilename(url));
@@ -48,7 +67,9 @@ export const wgetCommand = {
48
67
  }
49
68
  let response;
50
69
  try {
51
- response = await fetch(url, { headers: { "User-Agent": "Wget/1.21.3 (Fortune GNU/Linux)" } });
70
+ response = await fetch(url, {
71
+ headers: { "User-Agent": "Wget/1.21.3 (Fortune GNU/Linux)" },
72
+ });
52
73
  }
53
74
  catch (err) {
54
75
  const msg = err instanceof Error ? err.message : String(err);
@@ -73,7 +94,11 @@ export const wgetCommand = {
73
94
  }
74
95
  // Output to stdout (pipe) or file
75
96
  if (outputArg === "-") {
76
- return { stdout: body, stderr: stderrLines.join("\n") || undefined, exitCode: 0 };
97
+ return {
98
+ stdout: body,
99
+ stderr: stderrLines.join("\n") || undefined,
100
+ exitCode: 0,
101
+ };
77
102
  }
78
103
  if (targetPath) {
79
104
  shell.writeFileAsUser(authUser, targetPath, body);