typescript-virtual-container 1.5.2 → 1.5.4

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 (364) hide show
  1. package/README.md +43 -23
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/SSHMimic/executor.js +23 -5
  4. package/dist/commands/basename.d.ts +13 -0
  5. package/dist/commands/basename.js +45 -0
  6. package/dist/commands/file.d.ts +8 -0
  7. package/dist/commands/file.js +57 -0
  8. package/dist/commands/fun.d.ts +32 -0
  9. package/dist/commands/fun.js +172 -0
  10. package/dist/commands/ifconfig.d.ts +7 -0
  11. package/dist/commands/ifconfig.js +52 -0
  12. package/dist/commands/last.d.ts +13 -0
  13. package/dist/commands/last.js +68 -0
  14. package/dist/commands/manuals-bundle.js +598 -6
  15. package/dist/commands/registry.js +24 -2
  16. package/dist/commands/runtime.js +159 -106
  17. package/dist/commands/sh.js +5 -0
  18. package/dist/commands/tput.d.ts +13 -0
  19. package/dist/commands/tput.js +76 -0
  20. package/dist/commands/w.d.ts +7 -0
  21. package/dist/commands/w.js +38 -0
  22. package/dist/utils/expand.d.ts +12 -0
  23. package/dist/utils/expand.js +84 -0
  24. package/package.json +9 -3
  25. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -50
  26. package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -31
  27. package/.github/dependabot.yml +0 -27
  28. package/.github/pull_request_template.md +0 -21
  29. package/.github/workflows/create-pull-request.yml +0 -85
  30. package/.github/workflows/publish.yml +0 -25
  31. package/.github/workflows/test-battery.yml +0 -102
  32. package/.vscode/settings.json +0 -20
  33. package/CODE_OF_CONDUCT.md +0 -39
  34. package/CONTRIBUTING.md +0 -59
  35. package/HONEYPOT.md +0 -358
  36. package/SECURITY.md +0 -33
  37. package/benchmark-results.txt +0 -40
  38. package/benchmark-virtualshell.ts +0 -88
  39. package/biome.json +0 -37
  40. package/build.js +0 -22
  41. package/builds/fortune-nyx-v1.5.1-directbash-k6.1.0.mjs +0 -1768
  42. package/builds/fortune-nyx-v1.5.1-ssh-nosftp.js +0 -1768
  43. package/builds/fortune-nyx-v1.5.1-ssh.cjs +0 -1769
  44. package/builds/fortune-nyx-v1.5.1-web.min.js +0 -17022
  45. package/bun.lock +0 -244
  46. package/docs/.nojekyll +0 -1
  47. package/docs/app.js +0 -1755
  48. package/docs/assets/hierarchy.js +0 -1
  49. package/docs/assets/highlight.css +0 -162
  50. package/docs/assets/icons.js +0 -18
  51. package/docs/assets/icons.svg +0 -1
  52. package/docs/assets/main.js +0 -60
  53. package/docs/assets/navigation.js +0 -1
  54. package/docs/assets/search.js +0 -1
  55. package/docs/assets/style.css +0 -1633
  56. package/docs/classes/HoneyPot.html +0 -31
  57. package/docs/classes/IdleManager.html +0 -162
  58. package/docs/classes/SshClient.html +0 -66
  59. package/docs/classes/VirtualFileSystem.html +0 -279
  60. package/docs/classes/VirtualPackageManager.html +0 -63
  61. package/docs/classes/VirtualSftpServer.html +0 -169
  62. package/docs/classes/VirtualShell.html +0 -285
  63. package/docs/classes/VirtualSshServer.html +0 -182
  64. package/docs/classes/VirtualUserManager.html +0 -276
  65. package/docs/demo.html +0 -82
  66. package/docs/functions/assertDiff.html +0 -6
  67. package/docs/functions/diffSnapshots.html +0 -7
  68. package/docs/functions/formatDiff.html +0 -6
  69. package/docs/functions/getArg.html +0 -13
  70. package/docs/functions/getFlag.html +0 -15
  71. package/docs/functions/ifFlag.html +0 -11
  72. package/docs/hierarchy.html +0 -1
  73. package/docs/index.html +0 -1869
  74. package/docs/interfaces/AuditLogEntry.html +0 -6
  75. package/docs/interfaces/CommandContext.html +0 -22
  76. package/docs/interfaces/CommandResult.html +0 -26
  77. package/docs/interfaces/ExecStream.html +0 -11
  78. package/docs/interfaces/HoneyPotStats.html +0 -16
  79. package/docs/interfaces/IdleManagerOptions.html +0 -7
  80. package/docs/interfaces/InstalledPackage.html +0 -20
  81. package/docs/interfaces/NanoEditorSession.html +0 -8
  82. package/docs/interfaces/PackageDefinition.html +0 -30
  83. package/docs/interfaces/PackageFile.html +0 -8
  84. package/docs/interfaces/PasswordChallenge.html +0 -16
  85. package/docs/interfaces/RemoveOptions.html +0 -4
  86. package/docs/interfaces/ShellEnv.html +0 -6
  87. package/docs/interfaces/ShellModule.html +0 -14
  88. package/docs/interfaces/ShellProperties.html +0 -14
  89. package/docs/interfaces/ShellStream.html +0 -11
  90. package/docs/interfaces/SudoChallenge.html +0 -24
  91. package/docs/interfaces/VfsBaseNode.html +0 -12
  92. package/docs/interfaces/VfsDiff.html +0 -10
  93. package/docs/interfaces/VfsDiffEntry.html +0 -6
  94. package/docs/interfaces/VfsDiffModified.html +0 -10
  95. package/docs/interfaces/VfsDirectoryNode.html +0 -15
  96. package/docs/interfaces/VfsFileNode.html +0 -17
  97. package/docs/interfaces/VfsOptions.html +0 -26
  98. package/docs/interfaces/VfsSnapshot.html +0 -3
  99. package/docs/interfaces/VfsSnapshotBaseNode.html +0 -8
  100. package/docs/interfaces/VfsSnapshotDirectoryNode.html +0 -10
  101. package/docs/interfaces/VfsSnapshotFileNode.html +0 -12
  102. package/docs/interfaces/VirtualActiveSession.html +0 -12
  103. package/docs/interfaces/VirtualSftpServerOptions.html +0 -7
  104. package/docs/interfaces/VirtualShellVfsLike.html +0 -15
  105. package/docs/interfaces/VirtualShellVfsOptions.html +0 -3
  106. package/docs/interfaces/WriteFileOptions.html +0 -6
  107. package/docs/media/LICENSE +0 -21
  108. package/docs/modules.html +0 -1
  109. package/docs/types/ArgParseOptions.html +0 -4
  110. package/docs/types/CommandMode.html +0 -2
  111. package/docs/types/CommandOutcome.html +0 -2
  112. package/docs/types/IdleState.html +0 -1
  113. package/docs/types/VfsNodeStats.html +0 -2
  114. package/docs/types/VfsNodeType.html +0 -2
  115. package/docs/types/VfsPersistenceMode.html +0 -5
  116. package/docs/types/VfsSnapshotNode.html +0 -2
  117. package/examples/README.md +0 -288
  118. package/examples/app.js +0 -1755
  119. package/examples/app.ts +0 -299
  120. package/examples/build.js +0 -27
  121. package/examples/demo.html +0 -33
  122. package/examples/honeypot-audit.ts +0 -180
  123. package/examples/honeypot-export.ts +0 -253
  124. package/examples/honeypot-quickstart.ts +0 -110
  125. package/examples/index.html +0 -82
  126. package/examples/server.js +0 -55
  127. package/polyfills/buffer.js +0 -117
  128. package/polyfills/node_child_process/index.js +0 -2
  129. package/polyfills/node_crypto/index.js +0 -167
  130. package/polyfills/node_events/index.js +0 -9
  131. package/polyfills/node_fs/index.js +0 -202
  132. package/polyfills/node_fs/promises.js +0 -4
  133. package/polyfills/node_os/index.js +0 -9
  134. package/polyfills/node_path/index.js +0 -28
  135. package/polyfills/node_vm/index.js +0 -7
  136. package/polyfills/node_zlib/index.js +0 -3
  137. package/polyfills/process.js +0 -14
  138. package/polyfills/ssh2/index.js +0 -75
  139. package/scripts/build-all.mjs +0 -226
  140. package/scripts/build-names.mjs +0 -43
  141. package/scripts/generate-manuals-bundle.mjs +0 -49
  142. package/scripts/postinstall.js +0 -42
  143. package/scripts/publish-package.sh +0 -70
  144. package/src/Honeypot/index.ts +0 -457
  145. package/src/SSHClient/index.ts +0 -270
  146. package/src/SSHMimic/exec.ts +0 -49
  147. package/src/SSHMimic/executor.ts +0 -251
  148. package/src/SSHMimic/hostKey.ts +0 -21
  149. package/src/SSHMimic/index.ts +0 -337
  150. package/src/SSHMimic/loginBanner.ts +0 -36
  151. package/src/SSHMimic/loginFormat.ts +0 -10
  152. package/src/SSHMimic/prompt.ts +0 -14
  153. package/src/SSHMimic/sftp.ts +0 -883
  154. package/src/VirtualFileSystem/binaryPack.ts +0 -258
  155. package/src/VirtualFileSystem/index.ts +0 -1193
  156. package/src/VirtualFileSystem/internalTypes.ts +0 -43
  157. package/src/VirtualFileSystem/journal.ts +0 -171
  158. package/src/VirtualFileSystem/path.ts +0 -74
  159. package/src/VirtualPackageManager/index.ts +0 -1006
  160. package/src/VirtualShell/idleManager.ts +0 -137
  161. package/src/VirtualShell/index.ts +0 -475
  162. package/src/VirtualShell/shell.ts +0 -700
  163. package/src/VirtualShell/shellParser.ts +0 -285
  164. package/src/VirtualUserManager/index.ts +0 -758
  165. package/src/bun.d.ts +0 -1
  166. package/src/commands/adduser.ts +0 -103
  167. package/src/commands/alias.ts +0 -69
  168. package/src/commands/apt.ts +0 -233
  169. package/src/commands/awk.ts +0 -168
  170. package/src/commands/base64.ts +0 -29
  171. package/src/commands/cat.ts +0 -52
  172. package/src/commands/cd.ts +0 -25
  173. package/src/commands/chmod.ts +0 -85
  174. package/src/commands/clear.ts +0 -15
  175. package/src/commands/command-helpers.ts +0 -286
  176. package/src/commands/cp.ts +0 -83
  177. package/src/commands/curl.ts +0 -147
  178. package/src/commands/cut.ts +0 -36
  179. package/src/commands/date.ts +0 -30
  180. package/src/commands/declare.ts +0 -49
  181. package/src/commands/deluser.ts +0 -98
  182. package/src/commands/df.ts +0 -23
  183. package/src/commands/diff.ts +0 -43
  184. package/src/commands/dpkg.ts +0 -180
  185. package/src/commands/du.ts +0 -56
  186. package/src/commands/echo.ts +0 -58
  187. package/src/commands/env.ts +0 -23
  188. package/src/commands/exit.ts +0 -18
  189. package/src/commands/export.ts +0 -34
  190. package/src/commands/find.ts +0 -68
  191. package/src/commands/free.ts +0 -47
  192. package/src/commands/grep.ts +0 -116
  193. package/src/commands/groups.ts +0 -19
  194. package/src/commands/gzip.ts +0 -88
  195. package/src/commands/head.ts +0 -52
  196. package/src/commands/help.ts +0 -152
  197. package/src/commands/helpers.ts +0 -234
  198. package/src/commands/history.ts +0 -34
  199. package/src/commands/hostname.ts +0 -14
  200. package/src/commands/htop.ts +0 -20
  201. package/src/commands/id.ts +0 -19
  202. package/src/commands/index.ts +0 -9
  203. package/src/commands/kill.ts +0 -19
  204. package/src/commands/ln.ts +0 -71
  205. package/src/commands/ls.ts +0 -243
  206. package/src/commands/lsb-release.ts +0 -63
  207. package/src/commands/man.ts +0 -31
  208. package/src/commands/manuals/adduser.txt +0 -11
  209. package/src/commands/manuals/apt-cache.txt +0 -12
  210. package/src/commands/manuals/apt.txt +0 -20
  211. package/src/commands/manuals/awk.txt +0 -13
  212. package/src/commands/manuals/cat.txt +0 -14
  213. package/src/commands/manuals/cd.txt +0 -16
  214. package/src/commands/manuals/chmod.txt +0 -16
  215. package/src/commands/manuals/clear.txt +0 -10
  216. package/src/commands/manuals/cp.txt +0 -10
  217. package/src/commands/manuals/curl.txt +0 -20
  218. package/src/commands/manuals/date.txt +0 -14
  219. package/src/commands/manuals/declare.txt +0 -12
  220. package/src/commands/manuals/deluser.txt +0 -10
  221. package/src/commands/manuals/df.txt +0 -10
  222. package/src/commands/manuals/dpkg-query.txt +0 -11
  223. package/src/commands/manuals/dpkg.txt +0 -14
  224. package/src/commands/manuals/du.txt +0 -11
  225. package/src/commands/manuals/echo.txt +0 -11
  226. package/src/commands/manuals/false.txt +0 -10
  227. package/src/commands/manuals/find.txt +0 -11
  228. package/src/commands/manuals/free.txt +0 -12
  229. package/src/commands/manuals/grep.txt +0 -13
  230. package/src/commands/manuals/groups.txt +0 -10
  231. package/src/commands/manuals/gzip.txt +0 -11
  232. package/src/commands/manuals/head.txt +0 -10
  233. package/src/commands/manuals/help.txt +0 -11
  234. package/src/commands/manuals/history.txt +0 -11
  235. package/src/commands/manuals/hostname.txt +0 -10
  236. package/src/commands/manuals/id.txt +0 -10
  237. package/src/commands/manuals/kill.txt +0 -13
  238. package/src/commands/manuals/ls.txt +0 -20
  239. package/src/commands/manuals/lsb_release.txt +0 -14
  240. package/src/commands/manuals/mkdir.txt +0 -10
  241. package/src/commands/manuals/mv.txt +0 -10
  242. package/src/commands/manuals/nano.txt +0 -11
  243. package/src/commands/manuals/neofetch.txt +0 -10
  244. package/src/commands/manuals/node.txt +0 -13
  245. package/src/commands/manuals/npm.txt +0 -13
  246. package/src/commands/manuals/npx.txt +0 -13
  247. package/src/commands/manuals/passwd.txt +0 -11
  248. package/src/commands/manuals/ping.txt +0 -10
  249. package/src/commands/manuals/printf.txt +0 -11
  250. package/src/commands/manuals/ps.txt +0 -10
  251. package/src/commands/manuals/pwd.txt +0 -10
  252. package/src/commands/manuals/python3.txt +0 -13
  253. package/src/commands/manuals/readlink.txt +0 -10
  254. package/src/commands/manuals/return.txt +0 -10
  255. package/src/commands/manuals/rm.txt +0 -10
  256. package/src/commands/manuals/sed.txt +0 -11
  257. package/src/commands/manuals/set.txt +0 -11
  258. package/src/commands/manuals/shift.txt +0 -10
  259. package/src/commands/manuals/sleep.txt +0 -10
  260. package/src/commands/manuals/sort.txt +0 -12
  261. package/src/commands/manuals/source.txt +0 -11
  262. package/src/commands/manuals/ssh.txt +0 -11
  263. package/src/commands/manuals/stat.txt +0 -10
  264. package/src/commands/manuals/su.txt +0 -13
  265. package/src/commands/manuals/sudo.txt +0 -11
  266. package/src/commands/manuals/tail.txt +0 -10
  267. package/src/commands/manuals/tar.txt +0 -19
  268. package/src/commands/manuals/tee.txt +0 -10
  269. package/src/commands/manuals/test.txt +0 -11
  270. package/src/commands/manuals/touch.txt +0 -11
  271. package/src/commands/manuals/tr.txt +0 -10
  272. package/src/commands/manuals/trap.txt +0 -10
  273. package/src/commands/manuals/true.txt +0 -10
  274. package/src/commands/manuals/type.txt +0 -10
  275. package/src/commands/manuals/uname.txt +0 -12
  276. package/src/commands/manuals/uniq.txt +0 -12
  277. package/src/commands/manuals/unset.txt +0 -10
  278. package/src/commands/manuals/uptime.txt +0 -11
  279. package/src/commands/manuals/wc.txt +0 -12
  280. package/src/commands/manuals/wget.txt +0 -12
  281. package/src/commands/manuals/which.txt +0 -10
  282. package/src/commands/manuals/whoami.txt +0 -10
  283. package/src/commands/manuals/xargs.txt +0 -10
  284. package/src/commands/manuals-bundle.ts +0 -898
  285. package/src/commands/mkdir.ts +0 -31
  286. package/src/commands/mv.ts +0 -50
  287. package/src/commands/nano.ts +0 -38
  288. package/src/commands/neofetch.ts +0 -53
  289. package/src/commands/node.ts +0 -341
  290. package/src/commands/npm.ts +0 -132
  291. package/src/commands/passwd.ts +0 -50
  292. package/src/commands/ping.ts +0 -32
  293. package/src/commands/printf.ts +0 -129
  294. package/src/commands/ps.ts +0 -58
  295. package/src/commands/pwd.ts +0 -9
  296. package/src/commands/python.ts +0 -2229
  297. package/src/commands/read.ts +0 -46
  298. package/src/commands/registry.ts +0 -249
  299. package/src/commands/rm.ts +0 -42
  300. package/src/commands/runtime.ts +0 -378
  301. package/src/commands/sed.ts +0 -68
  302. package/src/commands/seq.ts +0 -43
  303. package/src/commands/set.ts +0 -29
  304. package/src/commands/sh.ts +0 -467
  305. package/src/commands/shift.ts +0 -63
  306. package/src/commands/sleep.ts +0 -20
  307. package/src/commands/sort.ts +0 -46
  308. package/src/commands/source.ts +0 -52
  309. package/src/commands/stat.ts +0 -61
  310. package/src/commands/su.ts +0 -72
  311. package/src/commands/sudo.ts +0 -76
  312. package/src/commands/tail.ts +0 -53
  313. package/src/commands/tar.ts +0 -102
  314. package/src/commands/tee.ts +0 -36
  315. package/src/commands/test.ts +0 -137
  316. package/src/commands/touch.ts +0 -28
  317. package/src/commands/tr.ts +0 -70
  318. package/src/commands/tree.ts +0 -20
  319. package/src/commands/true.ts +0 -27
  320. package/src/commands/type.ts +0 -48
  321. package/src/commands/uname.ts +0 -29
  322. package/src/commands/uniq.ts +0 -39
  323. package/src/commands/unset.ts +0 -17
  324. package/src/commands/uptime.ts +0 -54
  325. package/src/commands/wc.ts +0 -55
  326. package/src/commands/wget.ts +0 -148
  327. package/src/commands/which.ts +0 -37
  328. package/src/commands/who.ts +0 -25
  329. package/src/commands/whoami.ts +0 -14
  330. package/src/commands/xargs.ts +0 -31
  331. package/src/index.ts +0 -67
  332. package/src/modules/linuxRootfs.ts +0 -1961
  333. package/src/modules/neofetch.ts +0 -358
  334. package/src/modules/shellInteractive.ts +0 -57
  335. package/src/modules/shellRuntime.ts +0 -76
  336. package/src/self-standalone.ts +0 -542
  337. package/src/standalone-wo-sftp.ts +0 -38
  338. package/src/standalone.ts +0 -72
  339. package/src/types/commands.ts +0 -146
  340. package/src/types/pipeline.ts +0 -52
  341. package/src/types/streams.ts +0 -32
  342. package/src/types/tar-stream.d.ts +0 -38
  343. package/src/types/vfs.ts +0 -98
  344. package/src/utils/expand.ts +0 -491
  345. package/src/utils/perfLogger.ts +0 -72
  346. package/src/utils/tokenize.ts +0 -98
  347. package/src/utils/vfsDiff.ts +0 -275
  348. package/tests/command-helpers.test.ts +0 -116
  349. package/tests/commands-admin-net.test.ts +0 -441
  350. package/tests/commands-advanced.test.ts +0 -456
  351. package/tests/commands-core.test.ts +0 -562
  352. package/tests/commands-missing.test.ts +0 -570
  353. package/tests/commands-specific-units.test.ts +0 -327
  354. package/tests/commands-text-sys.test.ts +0 -445
  355. package/tests/expand.test.ts +0 -170
  356. package/tests/helpers.test.ts +0 -97
  357. package/tests/new-features.test.ts +0 -1036
  358. package/tests/parser-executor.test.ts +0 -37
  359. package/tests/sftp.test.ts +0 -323
  360. package/tests/ssh-exec.test.ts +0 -45
  361. package/tests/test-helper.ts +0 -79
  362. package/tests/users.test.ts +0 -86
  363. package/tsconfig.json +0 -49
  364. package/typedoc.json +0 -47
@@ -1,68 +0,0 @@
1
- import type { ShellModule } from "../types/commands";
2
- import { getFlag, ifFlag } from "./command-helpers";
3
- import { resolvePath } from "./helpers";
4
-
5
- /**
6
- * Stream editor for filtering and transforming text lines.
7
- * @category text
8
- * @params ["-e <expr> [file]", "s/pattern/replace/[g]"]
9
- */
10
- export const sedCommand: ShellModule = {
11
- name: "sed",
12
- description: "Stream editor for filtering and transforming text",
13
- category: "text",
14
- params: ["-e <expr> [file]", "s/pattern/replace/[g]"],
15
- run: ({ authUser, shell, cwd, args, stdin }) => {
16
- const inPlace = ifFlag(args, ["-i"]);
17
- const expr =
18
- (getFlag(args, ["-e"]) as string | undefined) ??
19
- args.find((a) => !a.startsWith("-"));
20
- const fileArg = args.filter((a) => !a.startsWith("-") && a !== expr).pop();
21
-
22
- if (!expr) return { stderr: "sed: no expression", exitCode: 1 };
23
-
24
- let content = stdin ?? "";
25
- if (fileArg) {
26
- const p = resolvePath(cwd, fileArg);
27
- try {
28
- content = shell.vfs.readFile(p);
29
- } catch {
30
- return {
31
- stderr: `sed: ${fileArg}: No such file or directory`,
32
- exitCode: 1,
33
- };
34
- }
35
- }
36
-
37
- // Parse s/from/to/[g]
38
- const sMatch = expr.match(/^s([^a-zA-Z0-9])(.+?)\1(.*?)\1([gi]*)$/);
39
- if (!sMatch)
40
- return { stderr: `sed: unrecognized command: ${expr}`, exitCode: 1 };
41
-
42
- const [, , from, to, flags] = sMatch;
43
- const regexFlags = (flags ?? "").includes("i")
44
- ? "gi"
45
- : (flags ?? "").includes("g")
46
- ? "g"
47
- : "";
48
- let regex: RegExp;
49
- try {
50
- regex = new RegExp(from!, regexFlags || "");
51
- } catch (_e) {
52
- return { stderr: `sed: invalid regex: ${from}`, exitCode: 1 };
53
- }
54
-
55
- const result =
56
- (flags ?? "").includes("g") || regexFlags.includes("g")
57
- ? content.replace(regex, to ?? "")
58
- : content.replace(regex, to ?? "");
59
-
60
- if (inPlace && fileArg) {
61
- const p = resolvePath(cwd, fileArg);
62
- shell.writeFileAsUser(authUser, p, result);
63
- return { exitCode: 0 };
64
- }
65
-
66
- return { stdout: result, exitCode: 0 };
67
- },
68
- };
@@ -1,43 +0,0 @@
1
- import type { ShellModule } from "../types/commands";
2
-
3
- /** Generate sequences of numbers. seq LAST / seq FIRST LAST / seq FIRST INCREMENT LAST */
4
- export const seqCommand: ShellModule = {
5
- name: "seq",
6
- description: "Print a sequence of numbers",
7
- category: "text",
8
- params: ["[FIRST [INCREMENT]] LAST"],
9
- run: ({ args }) => {
10
- const nums = args.filter((a) => !a.startsWith("-") || /^-[\d.]/.test(a)).map(Number);
11
- const sep = (() => { const i = args.indexOf("-s"); return i !== -1 ? (args[i + 1] ?? "\n") : "\n"; })();
12
- const fmt = (() => { const i = args.indexOf("-f"); return i !== -1 ? (args[i + 1] ?? "%g") : null; })();
13
- const width = args.includes("-w");
14
-
15
- let first = 1, inc = 1, last: number;
16
- if (nums.length === 1) { last = nums[0]!; }
17
- else if (nums.length === 2) { first = nums[0]!; last = nums[1]!; }
18
- else { first = nums[0]!; inc = nums[1]!; last = nums[2]!; }
19
-
20
- if (inc === 0) return { stderr: "seq: zero increment\n", exitCode: 1 };
21
- if ((inc > 0 && first > last) || (inc < 0 && first < last)) return { stdout: "", exitCode: 0 };
22
-
23
- const results: string[] = [];
24
- const maxSteps = 100000;
25
- let steps = 0;
26
- for (let n = first; inc > 0 ? n <= last : n >= last; n = Math.round((n + inc) * 1e10) / 1e10) {
27
- if (++steps > maxSteps) break;
28
- let s: string;
29
- if (fmt) {
30
- s = fmt.replace("%g", String(n)).replace("%f", n.toFixed(6)).replace("%d", String(Math.trunc(n)));
31
- } else {
32
- s = Number.isInteger(n) ? String(n) : n.toPrecision(12).replace(/\.?0+$/, "");
33
- }
34
- if (width) {
35
- const maxLen = String(Math.trunc(last)).length;
36
- s = s.padStart(maxLen, "0");
37
- }
38
- results.push(s);
39
- }
40
-
41
- return { stdout: `${results.join(sep)}\n`, exitCode: 0 };
42
- },
43
- };
@@ -1,29 +0,0 @@
1
- /** biome-ignore-all lint/style/useNamingConvention: env variables */
2
- import type { ShellModule } from "../types/commands";
3
-
4
- /**
5
- * Display or set shell variables and options.
6
- * @category shell
7
- * @params ["[VAR=value]"]
8
- */
9
- export const setCommand: ShellModule = {
10
- name: "set",
11
- description: "Display or set shell variables",
12
- category: "shell",
13
- params: ["[VAR=value]"],
14
- run: ({ args, env }) => {
15
- if (args.length === 0) {
16
- const out = Object.entries(env.vars)
17
- .map(([k, v]) => `${k}=${v}`)
18
- .join("\n");
19
- return { stdout: out, exitCode: 0 };
20
- }
21
- for (const arg of args) {
22
- if (arg.includes("=")) {
23
- const eq = arg.indexOf("=");
24
- env.vars[arg.slice(0, eq)] = arg.slice(eq + 1);
25
- }
26
- }
27
- return { exitCode: 0 };
28
- },
29
- };
@@ -1,467 +0,0 @@
1
- import type {
2
- CommandContext,
3
- CommandResult,
4
- ShellModule,
5
- } from "../types/commands";
6
- import { evalArith, expandAsync, expandBraces } from "../utils/expand";
7
- import { ifFlag } from "./command-helpers";
8
- import { resolvePath } from "./helpers";
9
- import { runCommand } from "./runtime";
10
-
11
- /** Alias for clarity inside sh.ts */
12
- type ShellContext = CommandContext;
13
-
14
- /**
15
- * Expand all shell forms including $(cmd) substitution.
16
- * Delegates to centralised expandAsync (single-quote-aware, depth-tracked).
17
- */
18
- async function expandVars(
19
- line: string,
20
- env: Record<string, string>,
21
- lastExit: number,
22
- ctx: ShellContext,
23
- ): Promise<string> {
24
- return expandAsync(line, env, lastExit, (sub) =>
25
- runCommand(
26
- sub,
27
- ctx.authUser,
28
- ctx.hostname,
29
- ctx.mode,
30
- ctx.cwd,
31
- ctx.shell,
32
- undefined,
33
- ctx.env,
34
- ).then((r) => r.stdout ?? ""),
35
- );
36
- }
37
-
38
- type Block =
39
- | {
40
- type: "if";
41
- cond: string;
42
- then_: string[];
43
- elif: Array<{ cond: string; body: string[] }>;
44
- else_: string[];
45
- }
46
- | { type: "for"; var: string; list: string; body: string[] }
47
- | { type: "while"; cond: string; body: string[] }
48
- | { type: "func"; name: string; body: string[] }
49
- | { type: "arith"; expr: string }
50
- | { type: "cmd"; line: string };
51
-
52
- /** Very small shell interpreter: supports if/elif/else/fi, for/do/done, while/do/done */
53
- function parseBlocks(lines: string[]): Block[] {
54
- const blocks: Block[] = [];
55
- let i = 0;
56
- while (i < lines.length) {
57
- const line = lines[i]!.trim();
58
- if (!line || line.startsWith("#")) {
59
- i++;
60
- continue;
61
- }
62
-
63
- // Function definition: name() { or function name { or name() { body }
64
- const funcMatchInline = line.match(/^(?:function\s+)?(\w+)\s*\(\s*\)\s*\{(.+)\}\s*$/);
65
- const funcMatch = funcMatchInline ?? (
66
- line.match(/^(?:function\s+)?(\w+)\s*\(\s*\)\s*\{?\s*$/) ||
67
- line.match(/^function\s+(\w+)\s*\{?\s*$/)
68
- );
69
- if (funcMatch) {
70
- const funcName = funcMatch[1]!;
71
- const body: string[] = [];
72
- // Inline: name() { cmd; } — single-line form
73
- if (funcMatchInline) {
74
- body.push(...funcMatchInline[2]!.split(";").map((s: string) => s.trim()).filter(Boolean));
75
- blocks.push({ type: "func", name: funcName, body });
76
- i++;
77
- continue;
78
- }
79
- i++;
80
- while (i < lines.length && lines[i]?.trim() !== "}" && i < lines.length + 1) {
81
- const l = lines[i]!.trim().replace(/^do\s+/, "");
82
- if (l && l !== "{") body.push(l);
83
- i++;
84
- }
85
- i++; // skip closing }
86
- blocks.push({ type: "func", name: funcName, body });
87
- continue;
88
- }
89
-
90
- // (( expr )) arithmetic statement
91
- const arithMatch = line.match(/^\(\(\s*(.+?)\s*\)\)$/);
92
- if (arithMatch) {
93
- blocks.push({ type: "arith", expr: arithMatch[1]! });
94
- i++;
95
- continue;
96
- }
97
-
98
- if (line.startsWith("if ") || line === "if") {
99
- const cond = line
100
- .replace(/^if\s+/, "")
101
- .replace(/;\s*then\s*$/, "")
102
- .trim();
103
- const thenLines: string[] = [];
104
- const elifBlocks: Array<{ cond: string; body: string[] }> = [];
105
- const elseLines: string[] = [];
106
- let section: "then" | "elif" | "else" = "then";
107
- let elifCond = "";
108
- i++;
109
- while (i < lines.length && lines[i]?.trim() !== "fi") {
110
- const l = lines[i]!.trim();
111
- if (l.startsWith("elif ")) {
112
- section = "elif";
113
- elifCond = l
114
- .replace(/^elif\s+/, "")
115
- .replace(/;\s*then\s*$/, "")
116
- .trim();
117
- elifBlocks.push({ cond: elifCond, body: [] });
118
- } else if (l === "else") {
119
- section = "else";
120
- } else if (l !== "then") {
121
- if (section === "then") thenLines.push(l);
122
- else if (section === "elif" && elifBlocks.length > 0)
123
- elifBlocks[elifBlocks.length - 1]!.body.push(l);
124
- else elseLines.push(l);
125
- }
126
- i++;
127
- }
128
- blocks.push({
129
- type: "if",
130
- cond,
131
- then_: thenLines,
132
- elif: elifBlocks,
133
- else_: elseLines,
134
- });
135
- } else if (line.startsWith("for ")) {
136
- const m = line.match(/^for\s+(\w+)\s+in\s+(.+?)(?:\s*;\s*do)?$/);
137
- if (m) {
138
- const body: string[] = [];
139
- i++;
140
- while (i < lines.length && lines[i]?.trim() !== "done") {
141
- const l = lines[i]!.trim().replace(/^do\s+/, "");
142
- if (l && l !== "do") body.push(l);
143
- i++;
144
- }
145
- blocks.push({ type: "for", var: m[1]!, list: m[2]!, body });
146
- } else {
147
- blocks.push({ type: "cmd", line });
148
- }
149
- } else if (line.startsWith("while ")) {
150
- const cond = line
151
- .replace(/^while\s+/, "")
152
- .replace(/;\s*do\s*$/, "")
153
- .trim();
154
- const body: string[] = [];
155
- i++;
156
- while (i < lines.length && lines[i]?.trim() !== "done") {
157
- const l = lines[i]!.trim().replace(/^do\s+/, "");
158
- if (l && l !== "do") body.push(l);
159
- i++;
160
- }
161
- blocks.push({ type: "while", cond, body });
162
- } else {
163
- blocks.push({ type: "cmd", line });
164
- }
165
- i++;
166
- }
167
- return blocks;
168
- }
169
-
170
- async function evalCondition(
171
- cond: string,
172
- ctx: CommandContext,
173
- ): Promise<boolean> {
174
- const expanded = await expandVars(
175
- cond,
176
- ctx.env.vars,
177
- ctx.env.lastExitCode,
178
- ctx,
179
- );
180
- // test -f / test -d / [ ... ]
181
- const testMatch = expanded.match(/^\[?\s*(.+?)\s*\]?$/);
182
- if (testMatch) {
183
- const expr = testMatch[1]!;
184
- // -f file
185
- const fTest = expr.match(/^-([fdeznr])\s+(.+)$/);
186
- if (fTest) {
187
- const [, flag, arg] = fTest;
188
- const p = resolvePath(ctx.cwd, arg!);
189
- if (flag === "f")
190
- return ctx.shell.vfs.exists(p) && ctx.shell.vfs.stat(p).type === "file";
191
- if (flag === "d")
192
- return (
193
- ctx.shell.vfs.exists(p) && ctx.shell.vfs.stat(p).type === "directory"
194
- );
195
- if (flag === "e") return ctx.shell.vfs.exists(p);
196
- if (flag === "z") return (arg ?? "").length === 0;
197
- if (flag === "n") return (arg ?? "").length > 0;
198
- }
199
- // string comparison
200
- const cmpMatch = expr.match(/^"?([^"]*)"?\s*(==|!=|=|<|>)\s*"?([^"]*)"?$/);
201
- if (cmpMatch) {
202
- const [, a, op, b] = cmpMatch;
203
- if (op === "==" || op === "=") return a === b;
204
- if (op === "!=") return a !== b;
205
- }
206
- // numeric
207
- const numMatch = expr.match(/^(\S+)\s+(-eq|-ne|-lt|-le|-gt|-ge)\s+(\S+)$/);
208
- if (numMatch) {
209
- const [, a, op, b] = numMatch;
210
- const na = Number(a),
211
- nb = Number(b);
212
- if (op === "-eq") return na === nb;
213
- if (op === "-ne") return na !== nb;
214
- if (op === "-lt") return na < nb;
215
- if (op === "-le") return na <= nb;
216
- if (op === "-gt") return na > nb;
217
- if (op === "-ge") return na >= nb;
218
- }
219
- }
220
- // fallback: run command and check exit code
221
- const r = await runCommand(
222
- expanded,
223
- ctx.authUser,
224
- ctx.hostname,
225
- ctx.mode,
226
- ctx.cwd,
227
- ctx.shell,
228
- undefined,
229
- ctx.env,
230
- );
231
- return (r.exitCode ?? 0) === 0;
232
- }
233
-
234
- async function runBlocks(
235
- blocks: Block[],
236
- ctx: CommandContext,
237
- ): Promise<CommandResult> {
238
- let lastResult: CommandResult = { exitCode: 0 };
239
- let output = "";
240
-
241
- for (const block of blocks) {
242
- if (block.type === "cmd") {
243
- const expanded = await expandVars(
244
- block.line,
245
- ctx.env.vars,
246
- ctx.env.lastExitCode,
247
- ctx,
248
- );
249
-
250
- // Bare VAR=val assignment(s) — handle before dispatching to runCommand
251
- const assignRe = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)/;
252
- const tokens = expanded.trim().split(/\s+/);
253
- if (tokens.length > 0 && assignRe.test(tokens[0]!)) {
254
- const allAssign = tokens.every((t) => assignRe.test(t));
255
- if (allAssign) {
256
- for (const tok of tokens) {
257
- const m = tok.match(assignRe)!;
258
- ctx.env.vars[m[1]!] = m[2]!;
259
- }
260
- ctx.env.lastExitCode = 0;
261
- continue;
262
- }
263
- }
264
-
265
- const r = await (async () => {
266
- // Check if expanded matches a registered function
267
- const cmdName = expanded.trim().split(/\s+/)[0] ?? "";
268
- const funcBody = ctx.env.vars[`__func_${cmdName}`];
269
- if (funcBody) {
270
- // Set positional params $1 $2 ... from remaining args
271
- const funcArgs = expanded.trim().split(/\s+/).slice(1);
272
- const savedVars = { ...ctx.env.vars };
273
- funcArgs.forEach((a, i) => { ctx.env.vars[String(i + 1)] = a; });
274
- ctx.env.vars["0"] = cmdName;
275
- const funcLines = funcBody.split("\n");
276
- const funcResult = await runBlocks(parseBlocks(funcLines), ctx);
277
- // Restore positional params
278
- for (let pi = 1; pi <= funcArgs.length; pi++) delete ctx.env.vars[String(pi)];
279
- Object.assign(ctx.env.vars, { ...savedVars, ...ctx.env.vars });
280
- return funcResult;
281
- }
282
- return runCommand(
283
- expanded,
284
- ctx.authUser,
285
- ctx.hostname,
286
- ctx.mode,
287
- ctx.cwd,
288
- ctx.shell,
289
- undefined,
290
- ctx.env,
291
- );
292
- })();
293
- ctx.env.lastExitCode = r.exitCode ?? 0;
294
- if (r.stdout) output += `${r.stdout}\n`;
295
- if (r.stderr) return { ...r, stdout: output.trim() };
296
- lastResult = r;
297
- } else if (block.type === "if") {
298
- let ran = false;
299
- if (await evalCondition(block.cond, ctx)) {
300
- const sub = await runBlocks(parseBlocks(block.then_), ctx);
301
- if (sub.stdout) output += `${sub.stdout}\n`;
302
- ran = true;
303
- } else {
304
- for (const elif of block.elif) {
305
- if (await evalCondition(elif.cond, ctx)) {
306
- const sub = await runBlocks(parseBlocks(elif.body), ctx);
307
- if (sub.stdout) output += `${sub.stdout}\n`;
308
- ran = true;
309
- break;
310
- }
311
- }
312
- if (!ran && block.else_.length > 0) {
313
- const sub = await runBlocks(parseBlocks(block.else_), ctx);
314
- if (sub.stdout) output += `${sub.stdout}\n`;
315
- }
316
- }
317
- } else if (block.type === "func") {
318
- // Register function in env vars as __func_<name>=<body>
319
- ctx.env.vars[`__func_${block.name}`] = block.body.join("\n");
320
- } else if (block.type === "arith") {
321
- // (( expr )) — evaluate arithmetic, update vars
322
- const expr = block.expr.trim();
323
- // Handle i++ / i-- / i+=N / i-=N
324
- const incMatch = expr.match(/^(\w+)\s*(\+\+|--)$/);
325
- if (incMatch) {
326
- const val = parseInt(ctx.env.vars[incMatch[1]!] ?? "0", 10);
327
- ctx.env.vars[incMatch[1]!] = String(incMatch[2] === "++" ? val + 1 : val - 1);
328
- } else {
329
- const assignMatch = expr.match(/^(\w+)\s*([+\-*/])=\s*(.+)$/);
330
- if (assignMatch) {
331
- const lhs = parseInt(ctx.env.vars[assignMatch[1]!] ?? "0", 10);
332
- const rhs = parseInt(assignMatch[3]!, 10);
333
- const ops: Record<string, number> = { "+": lhs + rhs, "-": lhs - rhs, "*": lhs * rhs, "/": Math.floor(lhs / rhs) };
334
- ctx.env.vars[assignMatch[1]!] = String(ops[assignMatch[2]!] ?? lhs);
335
- } else {
336
- const value = evalArith(expr, ctx.env.vars);
337
- if (!Number.isNaN(value)) {
338
- ctx.env.lastExitCode = value === 0 ? 1 : 0;
339
- }
340
- }
341
- }
342
- } else if (block.type === "for") {
343
- const listExpanded = await expandVars(
344
- block.list,
345
- ctx.env.vars,
346
- ctx.env.lastExitCode,
347
- ctx,
348
- );
349
- // Apply brace expansion to each token in the list
350
- const items = listExpanded.trim().split(/\s+/).flatMap(expandBraces);
351
- for (const item of items) {
352
- ctx.env.vars[block.var] = item;
353
- const sub = await runBlocks(parseBlocks(block.body), ctx);
354
- if (sub.stdout) output += `${sub.stdout}\n`;
355
- if (sub.closeSession) return sub;
356
- }
357
- } else if (block.type === "while") {
358
- let iterations = 0;
359
- while (iterations < 1000 && (await evalCondition(block.cond, ctx))) {
360
- const sub = await runBlocks(parseBlocks(block.body), ctx);
361
- if (sub.stdout) output += `${sub.stdout}\n`;
362
- if (sub.closeSession) return sub;
363
- iterations++;
364
- }
365
- }
366
- }
367
- return { ...lastResult, stdout: output.trim() || lastResult.stdout };
368
- }
369
-
370
- /**
371
- * Execute shell scripts or commands with a minimal shell interpreter.
372
- * Supports if/elif/else, for loops, while loops, and variable expansion.
373
- * @category shell
374
- * @params ["-c <script>", "[<file>]"]
375
- */
376
-
377
- /**
378
- * Split a sh script into logical lines, respecting:
379
- * - `{...}` braces (function bodies)
380
- * - Newlines and semicolons at depth 0 only
381
- */
382
- function splitShScript(script: string): string[] {
383
- const lines: string[] = [];
384
- let current = "";
385
- let depth = 0;
386
- let inSingleQ = false;
387
- let inDoubleQ = false;
388
- let i = 0;
389
- while (i < script.length) {
390
- const ch = script[i]!;
391
- if (!inSingleQ && !inDoubleQ) {
392
- if (ch === "'") { inSingleQ = true; current += ch; i++; continue; }
393
- if (ch === '"') { inDoubleQ = true; current += ch; i++; continue; }
394
- if (ch === "{") { depth++; current += ch; i++; continue; }
395
- if (ch === "}") {
396
- depth--;
397
- current += ch;
398
- i++;
399
- // At depth 0, closing } ends the function body line
400
- if (depth === 0) {
401
- const t = current.trim();
402
- if (t) lines.push(t);
403
- current = "";
404
- // Skip trailing ; or whitespace
405
- while (i < script.length && (script[i] === ";" || script[i] === " ")) i++;
406
- }
407
- continue;
408
- }
409
- if (depth === 0 && (ch === ";" || ch === "\n")) {
410
- const t = current.trim();
411
- if (t && !t.startsWith("#")) lines.push(t);
412
- current = "";
413
- i++;
414
- continue;
415
- }
416
- } else if (inSingleQ && ch === "'") {
417
- inSingleQ = false;
418
- } else if (inDoubleQ && ch === '"') {
419
- inDoubleQ = false;
420
- }
421
- current += ch;
422
- i++;
423
- }
424
- const t = current.trim();
425
- if (t && !t.startsWith("#")) lines.push(t);
426
- return lines;
427
- }
428
-
429
- export const shCommand: ShellModule = {
430
- name: "sh",
431
- aliases: ["bash"],
432
- description: "Execute shell script or command",
433
- category: "shell",
434
- params: ["-c <script>", "[<file>]"],
435
- run: async (ctx: CommandContext) => {
436
- const { args, shell, cwd } = ctx;
437
-
438
- // sh -c "inline script"
439
- if (ifFlag(args, "-c")) {
440
- const script = args[args.indexOf("-c") + 1] ?? "";
441
- if (!script) return { stderr: "sh: -c requires a script", exitCode: 1 };
442
- const lines = splitShScript(script);
443
- const blocks = parseBlocks(lines);
444
- return runBlocks(blocks, ctx);
445
- }
446
-
447
- // sh <file>
448
- const fileArg = args[0];
449
- if (fileArg) {
450
- const p = resolvePath(cwd, fileArg);
451
- if (!shell.vfs.exists(p))
452
- return {
453
- stderr: `sh: ${fileArg}: No such file or directory`,
454
- exitCode: 1,
455
- };
456
- const content = shell.vfs.readFile(p);
457
- const lines = splitShScript(content);
458
- const blocks = parseBlocks(lines);
459
- return runBlocks(blocks, ctx);
460
- }
461
-
462
- return {
463
- stderr: "sh: invalid usage. Use: sh -c 'cmd' or sh <file>",
464
- exitCode: 1,
465
- };
466
- },
467
- };
@@ -1,63 +0,0 @@
1
- import type { ShellModule } from "../types/commands";
2
-
3
- /**
4
- * Shift positional parameters (remove first N arguments).
5
- * @category shell
6
- * @params ["[n]"]
7
- */
8
- export const shiftCommand: ShellModule = {
9
- name: "shift",
10
- description: "Shift positional parameters",
11
- category: "shell",
12
- params: ["[n]"],
13
- // shift is meaningful only inside sh scripts where positional params exist.
14
- // In the current impl, positional params ($1 $2 …) aren't tracked in env by default.
15
- // We store them under env.vars.__argv and shift there if present.
16
- run: ({ args, env }) => {
17
- if (!env) return { exitCode: 0 };
18
- const n = parseInt(args[0] ?? "1", 10) || 1;
19
- const argv = env.vars.__argv?.split("\x00").filter(Boolean) ?? [];
20
- env.vars.__argv = argv.slice(n).join("\x00");
21
- // Update $1 $2 … in env
22
- const shifted = argv.slice(n);
23
- for (let i = 1; i <= 9; i++) {
24
- env.vars[String(i)] = shifted[i - 1] ?? "";
25
- }
26
- return { exitCode: 0 };
27
- },
28
- };
29
-
30
- /**
31
- * Trap signals and execute actions on signal receipt or shell exit.
32
- * @category shell
33
- * @params ["[action] [signal...]"]
34
- */
35
- export const trapCommand: ShellModule = {
36
- name: "trap",
37
- description: "Trap signals and events",
38
- category: "shell",
39
- params: ["[action] [signal...]"],
40
- // Store trap handlers in env for EXIT signal support
41
- run: ({ args, env }) => {
42
- if (!env || args.length === 0) return { exitCode: 0 };
43
- const action = args[0] ?? "";
44
- const signals = args.slice(1);
45
- for (const sig of signals) {
46
- env.vars[`__trap_${sig.toUpperCase()}`] = action;
47
- }
48
- return { exitCode: 0 };
49
- },
50
- };
51
-
52
- export const returnCommand: ShellModule = {
53
- name: "return",
54
- description: "Return from a shell function",
55
- category: "shell",
56
- params: ["[n]"],
57
- run: ({ args, env }) => {
58
- const code = parseInt(args[0] ?? "0", 10);
59
- if (env) env.lastExitCode = code;
60
- // Signal the caller via exitCode; function return is handled by runBlocks
61
- return { exitCode: code };
62
- },
63
- };
@@ -1,20 +0,0 @@
1
- import type { ShellModule } from "../types/commands";
2
-
3
- /**
4
- * Delay execution for a specified number of seconds.
5
- * @category system
6
- * @params ["<seconds>"]
7
- */
8
- export const sleepCommand: ShellModule = {
9
- name: "sleep",
10
- description: "Delay execution",
11
- category: "system",
12
- params: ["<seconds>"],
13
- run: async ({ args }) => {
14
- const secs = parseFloat(args[0] ?? "1");
15
- if (Number.isNaN(secs) || secs < 0)
16
- return { stderr: "sleep: invalid time", exitCode: 1 };
17
- await new Promise((r) => setTimeout(r, secs * 1000));
18
- return { exitCode: 0 };
19
- },
20
- };