typescript-virtual-container 1.3.4 → 1.4.0
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/.vscode/settings.json +0 -1
- package/README.md +674 -1504
- package/benchmark-results.txt +21 -21
- package/builds/self-standalone.js +274 -208
- package/builds/self-standalone.js.map +4 -4
- package/builds/standalone-wo-sftp.js +201 -149
- package/builds/standalone-wo-sftp.js.map +4 -4
- package/builds/standalone.js +263 -211
- package/builds/standalone.js.map +4 -4
- package/builds/web-full-api.min.js +3 -3
- package/builds/web-full-api.min.js.map +4 -4
- package/builds/web.min.js +2 -2
- package/builds/web.min.js.map +4 -4
- package/bun.lock +14 -12
- package/dist/SSHClient/index.d.ts.map +1 -1
- package/dist/SSHClient/index.js +5 -3
- package/dist/SSHMimic/executor.d.ts +1 -3
- package/dist/SSHMimic/executor.d.ts.map +1 -1
- package/dist/SSHMimic/executor.js +20 -22
- package/dist/SSHMimic/index.d.ts.map +1 -1
- package/dist/SSHMimic/index.js +5 -3
- package/dist/SSHMimic/sftp.d.ts.map +1 -1
- package/dist/SSHMimic/sftp.js +26 -21
- package/dist/VirtualShell/shell.d.ts.map +1 -1
- package/dist/VirtualShell/shell.js +25 -3
- package/dist/VirtualShell/shellParser.d.ts +1 -8
- package/dist/VirtualShell/shellParser.d.ts.map +1 -1
- package/dist/VirtualShell/shellParser.js +2 -81
- package/dist/VirtualUserManager/index.d.ts +7 -1
- package/dist/VirtualUserManager/index.d.ts.map +1 -1
- package/dist/VirtualUserManager/index.js +47 -16
- package/dist/commands/adduser.d.ts +10 -4
- package/dist/commands/adduser.d.ts.map +1 -1
- package/dist/commands/adduser.js +75 -12
- package/dist/commands/alias.d.ts +5 -0
- package/dist/commands/alias.d.ts.map +1 -1
- package/dist/commands/alias.js +5 -0
- package/dist/commands/apt.d.ts +5 -0
- package/dist/commands/apt.d.ts.map +1 -1
- package/dist/commands/apt.js +5 -0
- package/dist/commands/awk.d.ts +10 -8
- package/dist/commands/awk.d.ts.map +1 -1
- package/dist/commands/awk.js +156 -28
- package/dist/commands/cd.d.ts.map +1 -1
- package/dist/commands/cd.js +0 -3
- package/dist/commands/clear.d.ts +5 -0
- package/dist/commands/clear.d.ts.map +1 -1
- package/dist/commands/clear.js +5 -0
- package/dist/commands/command-helpers.d.ts.map +1 -1
- package/dist/commands/command-helpers.js +8 -0
- package/dist/commands/declare.d.ts +5 -0
- package/dist/commands/declare.d.ts.map +1 -1
- package/dist/commands/declare.js +5 -0
- package/dist/commands/deluser.d.ts +12 -0
- package/dist/commands/deluser.d.ts.map +1 -1
- package/dist/commands/deluser.js +72 -6
- package/dist/commands/df.d.ts +5 -0
- package/dist/commands/df.d.ts.map +1 -1
- package/dist/commands/df.js +5 -0
- package/dist/commands/du.d.ts +5 -0
- package/dist/commands/du.d.ts.map +1 -1
- package/dist/commands/du.js +5 -0
- package/dist/commands/export.d.ts +5 -0
- package/dist/commands/export.d.ts.map +1 -1
- package/dist/commands/export.js +5 -0
- package/dist/commands/grep.d.ts.map +1 -1
- package/dist/commands/grep.js +22 -4
- package/dist/commands/groups.d.ts +5 -0
- package/dist/commands/groups.d.ts.map +1 -1
- package/dist/commands/groups.js +5 -0
- package/dist/commands/gzip.d.ts +5 -2
- package/dist/commands/gzip.d.ts.map +1 -1
- package/dist/commands/gzip.js +48 -28
- package/dist/commands/head.d.ts.map +1 -1
- package/dist/commands/head.js +12 -3
- package/dist/commands/htop.d.ts +5 -0
- package/dist/commands/htop.d.ts.map +1 -1
- package/dist/commands/htop.js +5 -0
- package/dist/commands/kill.d.ts +5 -0
- package/dist/commands/kill.d.ts.map +1 -1
- package/dist/commands/kill.js +5 -0
- package/dist/commands/ln.d.ts +2 -0
- package/dist/commands/ln.d.ts.map +1 -1
- package/dist/commands/ln.js +22 -0
- package/dist/commands/ls.d.ts.map +1 -1
- package/dist/commands/ls.js +15 -0
- package/dist/commands/lsb-release.d.ts +5 -0
- package/dist/commands/lsb-release.d.ts.map +1 -1
- package/dist/commands/lsb-release.js +5 -0
- package/dist/commands/mkdir.d.ts +5 -0
- package/dist/commands/mkdir.d.ts.map +1 -1
- package/dist/commands/mkdir.js +5 -0
- package/dist/commands/mv.d.ts +5 -0
- package/dist/commands/mv.d.ts.map +1 -1
- package/dist/commands/mv.js +5 -0
- package/dist/commands/nano.d.ts +5 -0
- package/dist/commands/nano.d.ts.map +1 -1
- package/dist/commands/nano.js +5 -0
- package/dist/commands/neofetch.d.ts +5 -0
- package/dist/commands/neofetch.d.ts.map +1 -1
- package/dist/commands/neofetch.js +8 -5
- package/dist/commands/passwd.d.ts +8 -0
- package/dist/commands/passwd.d.ts.map +1 -1
- package/dist/commands/passwd.js +32 -11
- package/dist/commands/ping.d.ts +5 -0
- package/dist/commands/ping.d.ts.map +1 -1
- package/dist/commands/ping.js +5 -0
- package/dist/commands/printf.d.ts +5 -0
- package/dist/commands/printf.d.ts.map +1 -1
- package/dist/commands/printf.js +43 -12
- package/dist/commands/ps.d.ts +5 -0
- package/dist/commands/ps.d.ts.map +1 -1
- package/dist/commands/ps.js +5 -0
- package/dist/commands/read.d.ts +5 -0
- package/dist/commands/read.d.ts.map +1 -1
- package/dist/commands/read.js +5 -0
- package/dist/commands/registry.d.ts.map +1 -1
- package/dist/commands/registry.js +4 -1
- package/dist/commands/rm.d.ts +5 -0
- package/dist/commands/rm.d.ts.map +1 -1
- package/dist/commands/rm.js +5 -0
- package/dist/commands/runtime.d.ts.map +1 -1
- package/dist/commands/runtime.js +1 -57
- package/dist/commands/sed.d.ts +5 -0
- package/dist/commands/sed.d.ts.map +1 -1
- package/dist/commands/sed.js +5 -0
- package/dist/commands/set.d.ts +5 -6
- package/dist/commands/set.d.ts.map +1 -1
- package/dist/commands/set.js +5 -22
- package/dist/commands/sh.d.ts +6 -0
- package/dist/commands/sh.d.ts.map +1 -1
- package/dist/commands/sh.js +6 -0
- package/dist/commands/shift.d.ts +10 -0
- package/dist/commands/shift.d.ts.map +1 -1
- package/dist/commands/shift.js +10 -0
- package/dist/commands/sleep.d.ts +5 -0
- package/dist/commands/sleep.d.ts.map +1 -1
- package/dist/commands/sleep.js +5 -0
- package/dist/commands/sort.d.ts +5 -0
- package/dist/commands/sort.d.ts.map +1 -1
- package/dist/commands/sort.js +5 -0
- package/dist/commands/source.d.ts +5 -0
- package/dist/commands/source.d.ts.map +1 -1
- package/dist/commands/source.js +5 -0
- package/dist/commands/stat.d.ts +7 -0
- package/dist/commands/stat.d.ts.map +1 -0
- package/dist/commands/stat.js +56 -0
- package/dist/commands/su.d.ts +13 -0
- package/dist/commands/su.d.ts.map +1 -1
- package/dist/commands/su.js +45 -14
- package/dist/commands/sudo.d.ts.map +1 -1
- package/dist/commands/sudo.js +5 -0
- package/dist/commands/tail.d.ts +5 -0
- package/dist/commands/tail.d.ts.map +1 -1
- package/dist/commands/tail.js +15 -3
- package/dist/commands/tar.d.ts +5 -0
- package/dist/commands/tar.d.ts.map +1 -1
- package/dist/commands/tar.js +40 -10
- package/dist/commands/tee.d.ts +5 -0
- package/dist/commands/tee.d.ts.map +1 -1
- package/dist/commands/tee.js +5 -0
- package/dist/commands/touch.d.ts +5 -0
- package/dist/commands/touch.d.ts.map +1 -1
- package/dist/commands/touch.js +5 -0
- package/dist/commands/tr.d.ts.map +1 -1
- package/dist/commands/tr.js +45 -10
- package/dist/commands/tree.d.ts +5 -0
- package/dist/commands/tree.d.ts.map +1 -1
- package/dist/commands/tree.js +5 -0
- package/dist/commands/true.d.ts +10 -0
- package/dist/commands/true.d.ts.map +1 -1
- package/dist/commands/true.js +10 -0
- package/dist/commands/type.d.ts +5 -0
- package/dist/commands/type.d.ts.map +1 -1
- package/dist/commands/type.js +5 -0
- package/dist/commands/uname.d.ts +5 -0
- package/dist/commands/uname.d.ts.map +1 -1
- package/dist/commands/uname.js +5 -0
- package/dist/commands/uniq.d.ts +5 -0
- package/dist/commands/uniq.d.ts.map +1 -1
- package/dist/commands/uniq.js +5 -0
- package/dist/commands/unset.d.ts +5 -0
- package/dist/commands/unset.d.ts.map +1 -1
- package/dist/commands/unset.js +5 -0
- package/dist/commands/uptime.d.ts +5 -0
- package/dist/commands/uptime.d.ts.map +1 -1
- package/dist/commands/uptime.js +5 -0
- package/dist/commands/wc.d.ts +5 -0
- package/dist/commands/wc.d.ts.map +1 -1
- package/dist/commands/wc.js +5 -0
- package/dist/commands/wget.d.ts +5 -0
- package/dist/commands/wget.d.ts.map +1 -1
- package/dist/commands/wget.js +5 -0
- package/dist/commands/who.d.ts +5 -0
- package/dist/commands/who.d.ts.map +1 -1
- package/dist/commands/who.js +5 -0
- package/dist/commands/whoami.d.ts +5 -0
- package/dist/commands/whoami.d.ts.map +1 -1
- package/dist/commands/whoami.js +5 -0
- package/dist/commands/xargs.d.ts +5 -0
- package/dist/commands/xargs.d.ts.map +1 -1
- package/dist/commands/xargs.js +5 -0
- package/dist/self-standalone.js +254 -30
- package/dist/types/commands.d.ts +36 -0
- package/dist/types/commands.d.ts.map +1 -1
- package/dist/utils/tokenize.d.ts +20 -0
- package/dist/utils/tokenize.d.ts.map +1 -0
- package/dist/utils/tokenize.js +74 -0
- package/examples/web.min.js +2 -2
- package/package.json +1 -1
- package/src/SSHClient/index.ts +6 -3
- package/src/SSHMimic/executor.ts +21 -44
- package/src/SSHMimic/index.ts +7 -5
- package/src/SSHMimic/sftp.ts +28 -21
- package/src/VirtualShell/shell.ts +34 -4
- package/src/VirtualShell/shellParser.ts +2 -103
- package/src/VirtualUserManager/index.ts +43 -19
- package/src/commands/adduser.ts +86 -13
- package/src/commands/alias.ts +5 -0
- package/src/commands/apt.ts +5 -0
- package/src/commands/awk.ts +154 -29
- package/src/commands/cd.ts +0 -4
- package/src/commands/clear.ts +5 -0
- package/src/commands/command-helpers.ts +9 -0
- package/src/commands/declare.ts +5 -0
- package/src/commands/deluser.ts +84 -7
- package/src/commands/df.ts +5 -0
- package/src/commands/du.ts +5 -0
- package/src/commands/export.ts +5 -0
- package/src/commands/grep.ts +21 -8
- package/src/commands/groups.ts +5 -0
- package/src/commands/gzip.ts +54 -28
- package/src/commands/head.ts +14 -4
- package/src/commands/htop.ts +5 -0
- package/src/commands/kill.ts +5 -0
- package/src/commands/ln.ts +22 -0
- package/src/commands/ls.ts +17 -0
- package/src/commands/lsb-release.ts +5 -0
- package/src/commands/mkdir.ts +5 -0
- package/src/commands/mv.ts +5 -0
- package/src/commands/nano.ts +5 -0
- package/src/commands/neofetch.ts +8 -6
- package/src/commands/passwd.ts +35 -12
- package/src/commands/ping.ts +5 -0
- package/src/commands/printf.ts +30 -13
- package/src/commands/ps.ts +5 -0
- package/src/commands/read.ts +5 -0
- package/src/commands/registry.ts +4 -1
- package/src/commands/rm.ts +5 -0
- package/src/commands/runtime.ts +1 -61
- package/src/commands/sed.ts +5 -0
- package/src/commands/set.ts +5 -24
- package/src/commands/sh.ts +9 -3
- package/src/commands/shift.ts +10 -0
- package/src/commands/sleep.ts +5 -0
- package/src/commands/sort.ts +5 -0
- package/src/commands/source.ts +5 -0
- package/src/commands/stat.ts +61 -0
- package/src/commands/su.ts +54 -16
- package/src/commands/sudo.ts +5 -0
- package/src/commands/tail.ts +17 -3
- package/src/commands/tar.ts +38 -15
- package/src/commands/tee.ts +5 -0
- package/src/commands/touch.ts +5 -0
- package/src/commands/tr.ts +54 -10
- package/src/commands/tree.ts +5 -0
- package/src/commands/true.ts +10 -0
- package/src/commands/type.ts +5 -0
- package/src/commands/uname.ts +5 -0
- package/src/commands/uniq.ts +5 -0
- package/src/commands/unset.ts +5 -0
- package/src/commands/uptime.ts +5 -0
- package/src/commands/wc.ts +5 -0
- package/src/commands/wget.ts +5 -0
- package/src/commands/who.ts +5 -0
- package/src/commands/whoami.ts +5 -0
- package/src/commands/xargs.ts +5 -0
- package/src/self-standalone.ts +316 -33
- package/src/types/commands.ts +37 -0
- package/src/utils/tokenize.ts +78 -0
- package/builds/web-iife.min.js +0 -13
- package/builds/web-iife.min.js.map +0 -7
package/builds/web.min.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/VirtualShell/shellParser.ts", "../src/utils/expand.ts", "../src/web.ts"],
|
|
4
|
-
"sourcesContent": ["import type {\n\tPipeline,\n\tPipelineCommand,\n\tScript,\n\tStatement,\n\tLogicalOp,\n} from \"../types/pipeline\";\n\n// \u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Parse a shell input line into a Script (sequence of statements connected\n * by && / || / ;). Each statement contains one Pipeline (commands connected\n * by |).\n */\nexport function parseScript(rawInput: string): Script {\n\tconst trimmed = rawInput.trim();\n\tif (!trimmed) return { statements: [], isValid: true };\n\n\ttry {\n\t\tconst statements = parseStatements(trimmed);\n\t\treturn { statements, isValid: true };\n\t} catch (e) {\n\t\treturn { statements: [], isValid: false, error: (e as Error).message };\n\t}\n}\n\n/** Legacy compat: parse a single pipeline (no &&/||/;) */\nexport function parseShellPipeline(rawInput: string): Pipeline {\n\tconst trimmed = rawInput.trim();\n\tif (!trimmed) return { commands: [], isValid: true };\n\ttry {\n\t\tconst commands = parsePipeline(trimmed);\n\t\treturn { commands, isValid: true };\n\t} catch (e) {\n\t\treturn { commands: [], isValid: false, error: (e as Error).message };\n\t}\n}\n\n// \u2500\u2500 Variable & tilde expansion \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Expand ~ and $VAR / ${VAR} / ${VAR:-default} / $(cmd placeholder) in a\n * token, given the current env vars and home path.\n * Command substitution $(\u2026) is NOT executed here \u2014 it's left as a marker so\n * the executor can handle it.\n */\nexport function expandToken(\n\ttoken: string,\n\tenv: Record<string, string>,\n\tauthUser: string,\n\tlastExitCode = 0,\n): string {\n\t// tilde expansion\n\ttoken = token.replace(/^~(\\/|$)/, `/home/${authUser}$1`);\n\n\t// $? special var\n\ttoken = token.replace(/\\$\\?/g, String(lastExitCode));\n\t// $$ PID (mock)\n\ttoken = token.replace(/\\$\\$/g, \"1\");\n\t// $# argc (0 for interactive)\n\ttoken = token.replace(/\\$#/g, \"0\");\n\n\t// ${VAR:-default} and ${VAR:+value}\n\ttoken = token.replace(\n\t\t/\\$\\{([^}:]+):-([^}]*)\\}/g,\n\t\t(_, name, def) => env[name] ?? def,\n\t);\n\ttoken = token.replace(/\\$\\{([^}:]+):\\+([^}]*)\\}/g, (_, name, val) =>\n\t\tenv[name] ? val : \"\",\n\t);\n\n\t// ${VAR}\n\ttoken = token.replace(/\\$\\{([^}]+)\\}/g, (_, name) => env[name] ?? \"\");\n\n\t// $VAR (greedy: match longest valid identifier)\n\ttoken = token.replace(\n\t\t/\\$([A-Za-z_][A-Za-z0-9_]*)/g,\n\t\t(_, name) => env[name] ?? \"\",\n\t);\n\n\treturn token;\n}\n\n/**\n * Expand glob patterns (*, ?, [abc]) against a list of entries.\n * Returns the original pattern if no match.\n */\nexport function expandGlob(pattern: string, entries: string[]): string[] {\n\tif (!/[*?[]/.test(pattern)) return [pattern];\n\tconst regex = globToRegex(pattern);\n\tconst matches = entries.filter((e) => regex.test(e));\n\treturn matches.length > 0 ? matches.sort() : [pattern];\n}\n\nfunction globToRegex(pattern: string): RegExp {\n\tlet re = \"^\";\n\tfor (let i = 0; i < pattern.length; i++) {\n\t\tconst c = pattern[i]!;\n\t\tif (c === \"*\") re += \".*\";\n\t\telse if (c === \"?\") re += \".\";\n\t\telse if (c === \"[\") {\n\t\t\tconst close = pattern.indexOf(\"]\", i + 1);\n\t\t\tif (close === -1) re += \"\\\\[\";\n\t\t\telse {\n\t\t\t\tre += `[${pattern.slice(i + 1, close)}]`;\n\t\t\t\ti = close;\n\t\t\t}\n\t\t} else re += c.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\t}\n\treturn new RegExp(`${re}$`);\n}\n\n// \u2500\u2500 Internal parser \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction parseStatements(input: string): Statement[] {\n\t// Split by ;, &&, || \u2014 respecting quotes and parens\n\tconst segments = splitByLogicalOps(input);\n\tconst statements: Statement[] = [];\n\n\tfor (const seg of segments) {\n\t\tconst commands = parsePipeline(seg.text.trim());\n\t\tconst stmt: Statement = { pipeline: { commands, isValid: true } };\n\t\tif (seg.op) stmt.op = seg.op;\n\t\tstatements.push(stmt);\n\t}\n\n\treturn statements;\n}\n\ninterface Segment {\n\ttext: string;\n\top?: LogicalOp;\n}\n\nfunction splitByLogicalOps(input: string): Segment[] {\n\tconst segments: Segment[] = [];\n\tlet current = \"\";\n\tlet depth = 0; // parens/subshell depth\n\tlet inQ = false;\n\tlet qChar = \"\";\n\tlet i = 0;\n\n\tconst flush = (op?: LogicalOp) => {\n\t\tif (current.trim()) segments.push({ text: current, op });\n\t\tcurrent = \"\";\n\t};\n\n\twhile (i < input.length) {\n\t\tconst ch = input[i]!;\n\t\tconst ch2 = input.slice(i, i + 2);\n\n\t\tif ((ch === '\"' || ch === \"'\") && !inQ) {\n\t\t\tinQ = true;\n\t\t\tqChar = ch;\n\t\t\tcurrent += ch;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (inQ && ch === qChar) {\n\t\t\tinQ = false;\n\t\t\tcurrent += ch;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (inQ) {\n\t\t\tcurrent += ch;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (ch === \"(\") {\n\t\t\tdepth++;\n\t\t\tcurrent += ch;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === \")\") {\n\t\t\tdepth--;\n\t\t\tcurrent += ch;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (depth > 0) {\n\t\t\tcurrent += ch;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (ch2 === \"&&\") {\n\t\t\tflush(\"&&\");\n\t\t\ti += 2;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch2 === \"||\") {\n\t\t\tflush(\"||\");\n\t\t\ti += 2;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === \";\") {\n\t\t\tflush(\";\");\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tcurrent += ch;\n\t\ti++;\n\t}\n\tflush();\n\treturn segments;\n}\n\nfunction parsePipeline(input: string): PipelineCommand[] {\n\tconst pipeTokens = splitByPipe(input);\n\treturn pipeTokens.map(parseCommandWithRedirections);\n}\n\nfunction splitByPipe(input: string): string[] {\n\tconst tokens: string[] = [];\n\tlet current = \"\";\n\tlet inQ = false;\n\tlet qChar = \"\";\n\n\tfor (let i = 0; i < input.length; i++) {\n\t\tconst ch = input[i]!;\n\t\tif ((ch === '\"' || ch === \"'\") && !inQ) {\n\t\t\tinQ = true;\n\t\t\tqChar = ch;\n\t\t\tcurrent += ch;\n\t\t\tcontinue;\n\t\t}\n\t\tif (inQ && ch === qChar) {\n\t\t\tinQ = false;\n\t\t\tcurrent += ch;\n\t\t\tcontinue;\n\t\t}\n\t\tif (inQ) {\n\t\t\tcurrent += ch;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// || was already consumed at statement level, bare | is pipe\n\t\tif (ch === \"|\" && input[i + 1] !== \"|\") {\n\t\t\tif (!current.trim())\n\t\t\t\tthrow new Error(\"Syntax error near unexpected token '|'\");\n\t\t\ttokens.push(current.trim());\n\t\t\tcurrent = \"\";\n\t\t} else {\n\t\t\tcurrent += ch;\n\t\t}\n\t}\n\n\tconst tail = current.trim();\n\tif (!tail && tokens.length > 0)\n\t\tthrow new Error(\"Syntax error near unexpected token '|'\");\n\tif (tail) tokens.push(tail);\n\treturn tokens;\n}\n\nfunction parseCommandWithRedirections(token: string): PipelineCommand {\n\tconst parts = tokenizeCommand(token);\n\tif (parts.length === 0) return { name: \"\", args: [] };\n\n\tconst cmdParts: string[] = [];\n\tlet inputFile: string | undefined;\n\tlet outputFile: string | undefined;\n\tlet appendOutput = false;\n\tlet i = 0;\n\n\twhile (i < parts.length) {\n\t\tconst part = parts[i]!;\n\t\tif (part === \"<\") {\n\t\t\ti++;\n\t\t\tif (i >= parts.length)\n\t\t\t\tthrow new Error(\"Syntax error: expected filename after <\");\n\t\t\tinputFile = parts[i];\n\t\t\ti++;\n\t\t} else if (part === \">>\") {\n\t\t\ti++;\n\t\t\tif (i >= parts.length)\n\t\t\t\tthrow new Error(\"Syntax error: expected filename after >>\");\n\t\t\toutputFile = parts[i];\n\t\t\tappendOutput = true;\n\t\t\ti++;\n\t\t} else if (part === \">\") {\n\t\t\ti++;\n\t\t\tif (i >= parts.length)\n\t\t\t\tthrow new Error(\"Syntax error: expected filename after >\");\n\t\t\toutputFile = parts[i];\n\t\t\tappendOutput = false;\n\t\t\ti++;\n\t\t} else {\n\t\t\tcmdParts.push(part);\n\t\t\ti++;\n\t\t}\n\t}\n\n\tconst name = (cmdParts[0] ?? \"\").toLowerCase();\n\treturn { name, args: cmdParts.slice(1), inputFile, outputFile, appendOutput };\n}\n\nfunction tokenizeCommand(input: string): string[] {\n\tconst tokens: string[] = [];\n\tlet current = \"\";\n\tlet inQ = false;\n\tlet qChar = \"\";\n\tlet i = 0;\n\n\twhile (i < input.length) {\n\t\tconst ch = input[i]!;\n\t\tconst next = input[i + 1];\n\n\t\tif ((ch === '\"' || ch === \"'\") && !inQ) {\n\t\t\tinQ = true;\n\t\t\tqChar = ch;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (inQ && ch === qChar) {\n\t\t\tinQ = false;\n\t\t\tqChar = \"\";\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (inQ) {\n\t\t\tcurrent += ch;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (ch === \" \") {\n\t\t\tif (current) {\n\t\t\t\ttokens.push(current);\n\t\t\t\tcurrent = \"\";\n\t\t\t}\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif ((ch === \">\" || ch === \"<\") && !inQ) {\n\t\t\tif (current) {\n\t\t\t\ttokens.push(current);\n\t\t\t\tcurrent = \"\";\n\t\t\t}\n\t\t\tif (ch === \">\" && next === \">\") {\n\t\t\t\ttokens.push(\">>\");\n\t\t\t\ti += 2;\n\t\t\t} else {\n\t\t\t\ttokens.push(ch);\n\t\t\t\ti++;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tcurrent += ch;\n\t\ti++;\n\t}\n\tif (current) tokens.push(current);\n\treturn tokens;\n}\n", "/**\n * expand.ts\n *\n * Centralised shell variable and expression expansion.\n * Used by `runCommand` (index.ts), `echo`, and `sh.ts`.\n *\n * Handles (in order):\n * ~ tilde to $HOME\n * $? last exit code\n * $$ mock PID\n * $# argument count (0 outside scripts)\n * ${#VAR} string length\n * ${VAR:-def} default if unset/empty\n * ${VAR:=def} assign default if unset/empty\n * ${VAR:+val} alternate value if set\n * ${VAR} simple braced reference\n * $VAR simple reference\n * $((expr)) arithmetic (integer)\n */\n\n// \u2500\u2500\u2500 arithmetic evaluator \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Evaluate a simple integer arithmetic expression.\n * Supports: + - * / % ** unary- ( )\n * Variables are resolved from `env` before evaluation.\n * Returns NaN on syntax error.\n */\nexport function evalArith(expr: string, env: Record<string, string>): number {\n\t// Substitute variable names before evaluating\n\tconst substituted = expr.replace(\n\t\t/\\b([A-Za-z_][A-Za-z0-9_]*)\\b/g,\n\t\t(_, name) => {\n\t\t\tconst val = env[name];\n\t\t\treturn val !== undefined && val !== \"\" ? val : \"0\";\n\t\t},\n\t);\n\n\t// Whitelist: only digits, operators, spaces, parens\n\tif (!/^[\\d\\s+\\-*/%()^!&|<>=,. ]+$/.test(substituted)) return NaN;\n\n\ttry {\n\t\t// Use Function constructor for safe subset (no identifiers remain)\n\t\t// eslint-disable-next-line no-new-func\n\t\tconst result = Function(\n\t\t\t`\"use strict\"; return (${substituted.replace(/\\*\\*/g, \"**\")});`,\n\t\t)();\n\t\treturn typeof result === \"number\" ? Math.trunc(result) : NaN;\n\t} catch {\n\t\treturn NaN;\n\t}\n}\n\n// \u2500\u2500\u2500 synchronous expansion \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Apply a replacer only to portions of `input` that are NOT inside single quotes.\n * Single-quoted content is passed through verbatim (POSIX sh behaviour).\n */\nfunction outsideSingleQuotes(\n\tinput: string,\n\treplacer: (chunk: string) => string,\n): string {\n\tconst parts: string[] = [];\n\tlet i = 0;\n\twhile (i < input.length) {\n\t\tconst sqIdx = input.indexOf(\"'\", i);\n\t\tif (sqIdx === -1) {\n\t\t\t// No more single quotes \u2014 expand the rest\n\t\t\tparts.push(replacer(input.slice(i)));\n\t\t\tbreak;\n\t\t}\n\t\t// Expand the part before the single quote\n\t\tparts.push(replacer(input.slice(i, sqIdx)));\n\t\t// Find closing single quote \u2014 everything inside is literal\n\t\tconst closeIdx = input.indexOf(\"'\", sqIdx + 1);\n\t\tif (closeIdx === -1) {\n\t\t\t// Unclosed quote \u2014 treat rest as literal\n\t\t\tparts.push(input.slice(sqIdx));\n\t\t\tbreak;\n\t\t}\n\t\tparts.push(input.slice(sqIdx, closeIdx + 1)); // include quotes\n\t\ti = closeIdx + 1;\n\t}\n\treturn parts.join(\"\");\n}\n\n/**\n * Expand all shell variable and expression forms synchronously.\n * Does NOT handle `$(cmd)` \u2014 that requires async; see `expandAsync`.\n * Content inside single quotes is left verbatim per POSIX sh rules.\n *\n * @param input Raw string possibly containing `$VAR`, `${...}`, `$((...))`.\n * @param env Current session env vars.\n * @param lastExit Last command exit code (for `$?`).\n * @param home Home directory path (for `~`).\n */\nexport function expandSync(\n\tinput: string,\n\tenv: Record<string, string>,\n\tlastExit = 0,\n\thome?: string,\n): string {\n\tconst homePath = home ?? env.HOME ?? \"/home/user\";\n\n\treturn outsideSingleQuotes(input, (chunk) => {\n\t\tlet s = chunk;\n\n\t\t// Tilde expansion \u2014 only at start of token or after `:` or whitespace\n\t\ts = s.replace(\n\t\t\t/(^|[\\s:])~(\\/|$)/g,\n\t\t\t(_, pre, post) => `${pre}${homePath}${post}`,\n\t\t);\n\n\t\t// $? $$ $#\n\t\ts = s.replace(/\\$\\?/g, String(lastExit));\n\t\ts = s.replace(/\\$\\$/g, \"1\");\n\t\ts = s.replace(/\\$#/g, \"0\");\n\n\t\t// $(( arithmetic )) \u2014 must come before ${ and $VAR to avoid conflicts\n\t\ts = s.replace(/\\$\\(\\(([^)]+(?:\\([^)]*\\)[^)]*)*)\\)\\)/g, (_, expr) => {\n\t\t\tconst result = evalArith(expr, env);\n\t\t\treturn Number.isNaN(result) ? \"0\" : String(result);\n\t\t});\n\n\t\t// ${#VAR} \u2014 string length\n\t\ts = s.replace(/\\$\\{#([A-Za-z_][A-Za-z0-9_]*)\\}/g, (_, name) =>\n\t\t\tString((env[name] ?? \"\").length),\n\t\t);\n\n\t\t// ${VAR:-default}\n\t\ts = s.replace(/\\$\\{([A-Za-z_][A-Za-z0-9_]*):-([^}]*)\\}/g, (_, name, def) =>\n\t\t\tenv[name] !== undefined && env[name] !== \"\" ? (env[name] as string) : def,\n\t\t);\n\n\t\t// ${VAR:=default} \u2014 also assigns to env\n\t\ts = s.replace(\n\t\t\t/\\$\\{([A-Za-z_][A-Za-z0-9_]*):=([^}]*)\\}/g,\n\t\t\t(_, name, def) => {\n\t\t\t\tif (env[name] === undefined || env[name] === \"\") env[name] = def;\n\t\t\t\treturn env[name] as string;\n\t\t\t},\n\t\t);\n\n\t\t// ${VAR:+alternate}\n\t\ts = s.replace(\n\t\t\t/\\$\\{([A-Za-z_][A-Za-z0-9_]*):\\+([^}]*)\\}/g,\n\t\t\t(_, name, alt) =>\n\t\t\t\tenv[name] !== undefined && env[name] !== \"\" ? alt : \"\",\n\t\t);\n\n\t\t// ${VAR}\n\t\ts = s.replace(\n\t\t\t/\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g,\n\t\t\t(_, name) => env[name] ?? \"\",\n\t\t);\n\n\t\t// $VAR\n\t\ts = s.replace(/\\$([A-Za-z_][A-Za-z0-9_]*)/g, (_, name) => env[name] ?? \"\");\n\n\t\treturn s;\n\t});\n}\n\n// \u2500\u2500\u2500 async expansion (includes $(cmd)) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Expand all shell forms including `$(cmd)` command substitution.\n *\n * Processes `$(...)` blocks depth-first, respecting single-quote boundaries.\n * Then delegates to `expandSync` for the remaining forms.\n *\n * @param input Raw string.\n * @param env Current session env vars.\n * @param lastExit Last exit code.\n * @param runCmd Async callback to execute a command and return its stdout.\n */\nexport async function expandAsync(\n\tinput: string,\n\tenv: Record<string, string>,\n\tlastExit: number,\n\trunCmd: (cmd: string) => Promise<string>,\n): Promise<string> {\n\t// $(cmd) substitution \u2014 skip content inside single quotes\n\tif (input.includes(\"$(\")) {\n\t\tlet result = \"\";\n\t\tlet inSingle = false;\n\t\tlet i = 0;\n\n\t\twhile (i < input.length) {\n\t\t\tconst ch = input[i]!;\n\n\t\t\tif (ch === \"'\" && !inSingle) {\n\t\t\t\tinSingle = true;\n\t\t\t\tresult += ch;\n\t\t\t\ti++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (ch === \"'\" && inSingle) {\n\t\t\t\tinSingle = false;\n\t\t\t\tresult += ch;\n\t\t\t\ti++;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!inSingle && ch === \"$\" && input[i + 1] === \"(\") {\n\t\t\t\t// $((expr)) arithmetic \u2014 NOT a $(cmd) substitution, skip it\n\t\t\t\tif (input[i + 2] === \"(\") {\n\t\t\t\t\tresult += ch;\n\t\t\t\t\ti++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// Find matching ) with depth tracking\n\t\t\t\tlet depth = 0;\n\t\t\t\tlet j = i + 1;\n\t\t\t\twhile (j < input.length) {\n\t\t\t\t\tif (input[j] === \"(\") depth++;\n\t\t\t\t\telse if (input[j] === \")\") {\n\t\t\t\t\t\tdepth--;\n\t\t\t\t\t\tif (depth === 0) break;\n\t\t\t\t\t}\n\t\t\t\t\tj++;\n\t\t\t\t}\n\t\t\t\tconst sub = input.slice(i + 2, j).trim();\n\t\t\t\tconst out = (await runCmd(sub)).replace(/\\n$/, \"\");\n\t\t\t\tresult += out;\n\t\t\t\ti = j + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tresult += ch;\n\t\t\ti++;\n\t\t}\n\t\tinput = result;\n\t}\n\n\treturn expandSync(input, env, lastExit);\n}\n", "/** biome-ignore-all lint/style/useNamingConvention: env vars */\nimport { parseScript } from \"./VirtualShell/shellParser\";\nimport type { CommandResult, ShellEnv } from \"./types/commands\";\nimport type { PipelineCommand, Statement } from \"./types/pipeline\";\nimport { expandAsync, expandSync } from \"./utils/expand\";\n\ntype WebCommandContext = {\n\targs: string[];\n\tstdin?: string;\n\tcwd: string;\n\tenv: ShellEnv;\n\trawInput: string;\n\tshell: WebShell;\n};\n\ntype WebCommandHandler = (\n\tcontext: WebCommandContext,\n) => CommandResult | Promise<CommandResult>;\n\ninterface WebCommand {\n\tname: string;\n\tdescription: string;\n\tparams: string[];\n\trun: WebCommandHandler;\n\taliases?: string[];\n}\n\ntype WebFileNode = {\n\ttype: \"file\";\n\tname: string;\n\tmode: number;\n\tcreatedAt: string;\n\tupdatedAt: string;\n\tcontentBase64: string;\n};\n\ntype WebDirectoryNode = {\n\ttype: \"directory\";\n\tname: string;\n\tmode: number;\n\tcreatedAt: string;\n\tupdatedAt: string;\n\tchildren: WebNode[];\n};\n\ntype WebNode = WebFileNode | WebDirectoryNode;\n\ninterface WebVfsSnapshot {\n\troot: WebDirectoryNode;\n}\n\ninterface WebVfsOptions {\n\tdatabaseName?: string;\n\tstoreName?: string;\n\tkey?: string;\n}\n\ninterface WebShellOptions {\n\tcwd?: string;\n\tvfs?: WebVfsOptions;\n}\n\nconst textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder();\n\nfunction encodeBase64(bytes: Uint8Array): string {\n\tlet binary = \"\";\n\tfor (const byte of bytes) binary += String.fromCharCode(byte);\n\treturn btoa(binary);\n}\n\nfunction decodeBase64(base64: string): Uint8Array {\n\tconst binary = atob(base64);\n\tconst bytes = new Uint8Array(binary.length);\n\tfor (let index = 0; index < binary.length; index += 1) {\n\t\tbytes[index] = binary.charCodeAt(index);\n\t}\n\treturn bytes;\n}\n\nfunction normalizePath(inputPath: string, cwd = \"/\"): string {\n\tconst raw = inputPath.startsWith(\"/\") ? inputPath : `${cwd}/${inputPath}`;\n\tconst parts = raw.split(\"/\");\n\tconst stack: string[] = [];\n\n\tfor (const part of parts) {\n\t\tif (!part || part === \".\") continue;\n\t\tif (part === \"..\") {\n\t\t\tstack.pop();\n\t\t\tcontinue;\n\t\t}\n\t\tstack.push(part);\n\t}\n\n\treturn `/${stack.join(\"/\")}` || \"/\";\n}\n\nfunction dirname(inputPath: string): string {\n\tconst normalized = normalizePath(inputPath);\n\tif (normalized === \"/\") return \"/\";\n\tconst parts = normalized.split(\"/\").filter(Boolean);\n\tparts.pop();\n\treturn parts.length > 0 ? `/${parts.join(\"/\")}` : \"/\";\n}\n\nfunction basename(inputPath: string): string {\n\tconst normalized = normalizePath(inputPath);\n\tif (normalized === \"/\") return \"/\";\n\tconst parts = normalized.split(\"/\").filter(Boolean);\n\treturn parts.at(-1) ?? \"/\";\n}\n\nfunction cloneNode(node: WebNode): WebNode {\n\tif (node.type === \"file\") {\n\t\treturn {\n\t\t\t...node,\n\t\t\tcontentBase64: node.contentBase64,\n\t\t};\n\t}\n\n\treturn {\n\t\t...node,\n\t\tchildren: node.children.map((child) => cloneNode(child)),\n\t};\n}\n\nfunction makeDirectory(name: string, mode: number): WebDirectoryNode {\n\tconst now = new Date().toISOString();\n\treturn {\n\t\ttype: \"directory\",\n\t\tname,\n\t\tmode,\n\t\tcreatedAt: now,\n\t\tupdatedAt: now,\n\t\tchildren: [],\n\t};\n}\n\nfunction makeFile(name: string, content: Uint8Array, mode: number): WebFileNode {\n\tconst now = new Date().toISOString();\n\treturn {\n\t\ttype: \"file\",\n\t\tname,\n\t\tmode,\n\t\tcreatedAt: now,\n\t\tupdatedAt: now,\n\t\tcontentBase64: encodeBase64(content),\n\t};\n}\n\nfunction findChild(directory: WebDirectoryNode, name: string): WebNode | undefined {\n\treturn directory.children.find((child) => child.name === name);\n}\n\nfunction setChild(directory: WebDirectoryNode, node: WebNode): void {\n\tconst index = directory.children.findIndex((child) => child.name === node.name);\n\tif (index === -1) {\n\t\tdirectory.children.push(node);\n\t\treturn;\n\t}\n\tdirectory.children[index] = node;\n}\n\nfunction removeChild(directory: WebDirectoryNode, name: string): void {\n\tdirectory.children = directory.children.filter((child) => child.name !== name);\n}\n\nfunction splitPath(pathValue: string): string[] {\n\treturn normalizePath(pathValue).split(\"/\").filter(Boolean);\n}\n\ntype WebIndexedDbRequest<T> = {\n\tresult: T;\n\terror: unknown;\n\taddEventListener(type: \"success\" | \"error\", listener: () => void): void;\n};\n\ntype WebIndexedDbObjectStore = {\n\tget(key: string): WebIndexedDbRequest<unknown>;\n\tput(value: string, key: string): WebIndexedDbRequest<unknown>;\n};\n\ntype WebIndexedDbTransaction = {\n\tobjectStore(name: string): WebIndexedDbObjectStore;\n\terror: unknown;\n\taddEventListener(type: \"complete\" | \"error\" | \"abort\", listener: () => void): void;\n};\n\ntype WebIndexedDbDatabase = {\n\tobjectStoreNames: {\n\t\tcontains(name: string): boolean;\n\t};\n\tcreateObjectStore(name: string): unknown;\n\ttransaction(name: string, mode: \"readonly\" | \"readwrite\"): WebIndexedDbTransaction;\n\tclose(): void;\n};\n\ntype WebIndexedDbOpenRequest = {\n\tresult: WebIndexedDbDatabase;\n\terror: unknown;\n\taddEventListener(type: \"upgradeneeded\" | \"success\" | \"error\", listener: () => void): void;\n};\n\ntype WebIndexedDbFactory = {\n\topen(name: string, version?: number): WebIndexedDbOpenRequest;\n};\n\nconst webGlobal = globalThis as typeof globalThis & { indexedDB?: WebIndexedDbFactory };\n\nfunction promisifyRequest<T>(request: WebIndexedDbRequest<T>): Promise<T> {\n\treturn new Promise((resolve, reject) => {\n\t\trequest.addEventListener(\"success\", () => resolve(request.result));\n\t\trequest.addEventListener(\"error\", () => reject(request.error));\n\t});\n}\n\nclass IndexedDbMirrorVfs {\n\tprivate readonly databaseName: string;\n\tprivate readonly storeName: string;\n\tprivate readonly key: string;\n\tprivate root: WebDirectoryNode;\n\n\tconstructor(options: WebVfsOptions = {}) {\n\t\tthis.databaseName = options.databaseName ?? \"typescript-virtual-container-web\";\n\t\tthis.storeName = options.storeName ?? \"snapshots\";\n\t\tthis.key = options.key ?? \"current\";\n\t\tthis.root = makeDirectory(\"\", 0o755);\n\t}\n\n\tprivate async openDatabase(): Promise<WebIndexedDbDatabase> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst indexedDbFactory = webGlobal.indexedDB;\n\t\t\tif (!indexedDbFactory) {\n\t\t\t\treject(new Error(\"IndexedDB is not available in this environment\"));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst request = indexedDbFactory.open(this.databaseName, 1);\n\t\t\trequest.addEventListener(\"upgradeneeded\", () => {\n\t\t\t\tconst database = request.result;\n\t\t\t\tif (!database.objectStoreNames.contains(this.storeName)) {\n\t\t\t\t\tdatabase.createObjectStore(this.storeName);\n\t\t\t\t}\n\t\t\t});\n\t\t\trequest.addEventListener(\"success\", () => resolve(request.result));\n\t\t\trequest.addEventListener(\"error\", () => reject(request.error));\n\t\t});\n\t}\n\n\tprivate async readSnapshot(): Promise<WebVfsSnapshot | null> {\n\t\tconst database = await this.openDatabase();\n\t\ttry {\n\t\t\tconst transaction = database.transaction(this.storeName, \"readonly\");\n\t\t\tconst store = transaction.objectStore(this.storeName);\n\t\t\tconst request = store.get(this.key);\n\t\t\tconst result = (await promisifyRequest(request)) as string | undefined;\n\t\t\tif (!result) return null;\n\t\t\treturn JSON.parse(result) as WebVfsSnapshot;\n\t\t} finally {\n\t\t\tdatabase.close();\n\t\t}\n\t}\n\n\tprivate async writeSnapshot(snapshot: WebVfsSnapshot): Promise<void> {\n\t\tconst database = await this.openDatabase();\n\t\ttry {\n\t\t\tconst transaction = database.transaction(this.storeName, \"readwrite\");\n\t\t\tconst store = transaction.objectStore(this.storeName);\n\t\t\tawait promisifyRequest(store.put(JSON.stringify(snapshot), this.key));\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\ttransaction.addEventListener(\"complete\", () => resolve());\n\t\t\t\ttransaction.addEventListener(\"error\", () => reject(transaction.error));\n\t\t\t\ttransaction.addEventListener(\"abort\", () => reject(transaction.error));\n\t\t\t});\n\t\t} finally {\n\t\t\tdatabase.close();\n\t\t}\n\t}\n\n\tprivate serializeNode(node: WebNode): WebNode {\n\t\tif (node.type === \"file\") return { ...node };\n\t\treturn {\n\t\t\t...node,\n\t\t\tchildren: node.children.map((child) => this.serializeNode(child)),\n\t\t};\n\t}\n\n\tprivate deserializeNode(node: WebNode): WebNode {\n\t\tif (node.type === \"file\") return { ...node };\n\t\treturn {\n\t\t\t...node,\n\t\t\tchildren: node.children.map((child) => this.deserializeNode(child)),\n\t\t};\n\t}\n\n\tprivate getNode(targetPath: string): WebNode {\n\t\tconst normalized = normalizePath(targetPath);\n\t\tif (normalized === \"/\") return this.root;\n\n\t\tconst parts = splitPath(normalized);\n\t\tlet current: WebNode = this.root;\n\t\tfor (const part of parts) {\n\t\t\tif (current.type !== \"directory\") {\n\t\t\t\tthrow new Error(`Not a directory: ${normalized}`);\n\t\t\t}\n\t\t\tconst child = findChild(current, part);\n\t\t\tif (!child) throw new Error(`No such file or directory: ${normalized}`);\n\t\t\tcurrent = child;\n\t\t}\n\t\treturn current;\n\t}\n\n\tprivate ensureDirectory(targetPath: string, mode: number): WebDirectoryNode {\n\t\tconst normalized = normalizePath(targetPath);\n\t\tif (normalized === \"/\") return this.root;\n\n\t\tconst parts = splitPath(normalized);\n\t\tlet current = this.root;\n\t\tfor (const part of parts) {\n\t\t\tconst existing = findChild(current, part);\n\t\t\tif (!existing) {\n\t\t\t\tconst created = makeDirectory(part, mode);\n\t\t\t\tsetChild(current, created);\n\t\t\t\tcurrent = created;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (existing.type !== \"directory\") {\n\t\t\t\tthrow new Error(`Cannot create directory '${normalized}': path is a file.`);\n\t\t\t}\n\t\t\tcurrent = existing;\n\t\t}\n\t\treturn current;\n\t}\n\n\tprivate removeNode(targetPath: string, recursive: boolean): void {\n\t\tconst normalized = normalizePath(targetPath);\n\t\tif (normalized === \"/\") throw new Error(\"Cannot remove root directory\");\n\t\tconst parent = this.getNode(dirname(normalized));\n\t\tif (parent.type !== \"directory\") throw new Error(`Not a directory: ${dirname(normalized)}`);\n\t\tconst name = basename(normalized);\n\t\tconst node = findChild(parent, name);\n\t\tif (!node) throw new Error(`No such file or directory: ${normalized}`);\n\t\tif (node.type === \"directory\" && node.children.length > 0 && !recursive) {\n\t\t\tthrow new Error(`Cannot remove '${normalized}': directory not empty.`);\n\t\t}\n\t\tremoveChild(parent, name);\n\t}\n\n\tprivate copyNode(node: WebNode): WebNode {\n\t\tif (node.type === \"file\") {\n\t\t\treturn {\n\t\t\t\t...node,\n\t\t\t\tcontentBase64: node.contentBase64,\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\t...node,\n\t\t\tchildren: node.children.map((child) => this.copyNode(child)),\n\t\t};\n\t}\n\n\tpublic async restoreMirror(): Promise<void> {\n\t\tconst snapshot = await this.readSnapshot();\n\t\tif (!snapshot) return;\n\t\tthis.root = this.deserializeNode(snapshot.root) as WebDirectoryNode;\n\t}\n\n\tpublic async flushMirror(): Promise<void> {\n\t\tawait this.writeSnapshot({ root: this.serializeNode(this.root) as WebDirectoryNode });\n\t}\n\n\tpublic exists(targetPath: string): boolean {\n\t\ttry {\n\t\t\tthis.getNode(targetPath);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic list(targetPath: string): string[] {\n\t\tconst node = this.getNode(targetPath);\n\t\tif (node.type !== \"directory\") throw new Error(`Not a directory: ${targetPath}`);\n\t\treturn node.children.map((child) => child.name).sort((a, b) => a.localeCompare(b));\n\t}\n\n\tpublic stat(targetPath: string): {\n\t\ttype: \"file\" | \"directory\";\n\t\tmode: number;\n\t\tsize: number;\n\t\tname: string;\n\t} {\n\t\tconst node = this.getNode(targetPath);\n\t\tif (node.type === \"file\") {\n\t\t\treturn {\n\t\t\t\ttype: \"file\",\n\t\t\t\tmode: node.mode,\n\t\t\t\tsize: decodeBase64(node.contentBase64).byteLength,\n\t\t\t\tname: node.name,\n\t\t\t};\n\t\t}\n\t\treturn { type: \"directory\", mode: node.mode, size: 0, name: node.name };\n\t}\n\n\tpublic readFile(targetPath: string): string {\n\t\tconst node = this.getNode(targetPath);\n\t\tif (node.type !== \"file\") throw new Error(`Is a directory: ${targetPath}`);\n\t\treturn textDecoder.decode(decodeBase64(node.contentBase64));\n\t}\n\n\tpublic writeFile(\n\t\ttargetPath: string,\n\t\tcontent: string | Uint8Array,\n\t\tmode = 0o644,\n\t): void {\n\t\tconst normalized = normalizePath(targetPath);\n\t\tconst parent = this.ensureDirectory(dirname(normalized), 0o755);\n\t\tconst bytes = typeof content === \"string\" ? textEncoder.encode(content) : content;\n\t\tconst file = makeFile(basename(normalized), bytes, mode);\n\t\tsetChild(parent, file);\n\t}\n\n\tpublic mkdir(targetPath: string, mode = 0o755): void {\n\t\tthis.ensureDirectory(targetPath, mode);\n\t}\n\n\tpublic touch(targetPath: string): void {\n\t\tif (this.exists(targetPath)) return;\n\t\tthis.writeFile(targetPath, \"\");\n\t}\n\n\tpublic move(fromPath: string, toPath: string): void {\n\t\tconst source = this.getNode(fromPath);\n\t\tconst sourceParent = this.getNode(dirname(fromPath));\n\t\tconst destinationParent = this.ensureDirectory(dirname(toPath), 0o755);\n\t\tif (sourceParent.type !== \"directory\") throw new Error(`Not a directory: ${dirname(fromPath)}`);\n\t\tremoveChild(sourceParent, basename(fromPath));\n\t\tconst clone = cloneNode(source);\n\t\tclone.name = basename(toPath);\n\t\tsetChild(destinationParent, clone);\n\t}\n\n\tpublic copy(fromPath: string, toPath: string): void {\n\t\tconst source = this.getNode(fromPath);\n\t\tconst destinationParent = this.ensureDirectory(dirname(toPath), 0o755);\n\t\tconst clone = this.copyNode(source);\n\t\tclone.name = basename(toPath);\n\t\tsetChild(destinationParent, clone);\n\t}\n\n\tpublic remove(targetPath: string, options: { recursive?: boolean } = {}): void {\n\t\tthis.removeNode(targetPath, options.recursive ?? false);\n\t}\n\n\tpublic exportSnapshot(): WebVfsSnapshot {\n\t\treturn { root: this.serializeNode(this.root) as WebDirectoryNode };\n\t}\n\n\tpublic importSnapshot(snapshot: WebVfsSnapshot): void {\n\t\tthis.root = this.deserializeNode(snapshot.root) as WebDirectoryNode;\n\t}\n}\n\nclass WebShell {\n\treadonly hostname: string;\n\treadonly vfs: IndexedDbMirrorVfs;\n\treadonly env: ShellEnv;\n\tprivate cwd: string;\n\tprivate readonly commands = new Map<string, WebCommand>();\n\tprivate initialized = false;\n\n\tconstructor(hostname: string, options: WebShellOptions = {}) {\n\t\tthis.hostname = hostname;\n\t\tthis.cwd = options.cwd ?? \"/home/root\";\n\t\tthis.env = {\n\t\t\tvars: {\n\t\t\t\tPATH: \"/usr/bin:/bin\",\n\t\t\t\tHOME: \"/home/root\",\n\t\t\t\tUSER: \"root\",\n\t\t\t\tLOGNAME: \"root\",\n\t\t\t\tSHELL: \"/bin/sh\",\n\t\t\t\tHOSTNAME: hostname,\n\t\t\t\tPWD: this.cwd,\n\t\t\t},\n\t\t\tlastExitCode: 0,\n\t\t};\n\t\tthis.vfs = new IndexedDbMirrorVfs(options.vfs);\n\t\tthis.registerBuiltins();\n\t}\n\n\tprivate register(command: WebCommand): void {\n\t\tthis.commands.set(command.name.toLowerCase(), command);\n\t\tfor (const alias of command.aliases ?? []) {\n\t\t\tthis.commands.set(alias.toLowerCase(), command);\n\t\t}\n\t}\n\n\tprivate registerBuiltins(): void {\n\t\tthis.register({\n\t\t\tname: \"help\",\n\t\t\tdescription: \"List available web commands\",\n\t\t\tparams: [],\n\t\t\trun: () => ({ stdout: `${this.listCommands().join(\"\\n\")}\\n`, exitCode: 0 }),\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"pwd\",\n\t\t\tdescription: \"Print current directory\",\n\t\t\tparams: [],\n\t\t\trun: () => ({ stdout: `${this.cwd}\\n`, exitCode: 0 }),\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"cd\",\n\t\t\tdescription: \"Change current directory\",\n\t\t\tparams: [\"[dir]\"],\n\t\t\trun: ({ args }) => {\n\t\t\t\tconst target = args[0] ? normalizePath(args[0], this.cwd) : \"/home/root\";\n\t\t\t\tif (!this.vfs.exists(target) || this.vfs.stat(target).type !== \"directory\") {\n\t\t\t\t\treturn { stderr: `cd: no such file or directory: ${target}`, exitCode: 1 };\n\t\t\t\t}\n\t\t\t\tthis.cwd = target;\n\t\t\t\tthis.env.vars.PWD = target;\n\t\t\t\treturn { exitCode: 0, nextCwd: target };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"echo\",\n\t\t\tdescription: \"Display text\",\n\t\t\tparams: [\"[-n] [-e] [text...]\"],\n\t\t\trun: ({ args, stdin }) => {\n\t\t\t\tconst noNewline = args.includes(\"-n\");\n\t\t\t\tconst raw = args.filter((arg) => arg !== \"-n\" && arg !== \"-e\" && arg !== \"-E\");\n\t\t\t\tconst text = raw.length > 0 ? raw.join(\" \") : (stdin ?? \"\");\n\t\t\t\tconst expanded = expandSync(text, this.env.vars, this.env.lastExitCode, this.env.vars.HOME);\n\t\t\t\treturn { stdout: noNewline ? expanded : `${expanded}\\n`, exitCode: 0 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"env\",\n\t\t\tdescription: \"Print environment variables\",\n\t\t\tparams: [],\n\t\t\trun: () => ({ stdout: `${Object.entries(this.env.vars).map(([key, value]) => `${key}=${value}`).join(\"\\n\")}\\n`, exitCode: 0 }),\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"export\",\n\t\t\tdescription: \"Set environment variables\",\n\t\t\tparams: [\"KEY=VALUE...\"],\n\t\t\trun: ({ args }) => {\n\t\t\t\tfor (const arg of args) {\n\t\t\t\t\tconst eq = arg.indexOf(\"=\");\n\t\t\t\t\tif (eq === -1) continue;\n\t\t\t\t\tconst key = arg.slice(0, eq).trim();\n\t\t\t\t\tconst value = arg.slice(eq + 1);\n\t\t\t\t\tif (key) this.env.vars[key] = value;\n\t\t\t\t}\n\t\t\t\treturn { exitCode: 0 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"unset\",\n\t\t\tdescription: \"Unset environment variables\",\n\t\t\tparams: [\"NAME...\"],\n\t\t\trun: ({ args }) => {\n\t\t\t\tfor (const name of args) delete this.env.vars[name];\n\t\t\t\treturn { exitCode: 0 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"mkdir\",\n\t\t\tdescription: \"Create directories\",\n\t\t\tparams: [\"[-p] dir...\"],\n\t\t\trun: async ({ args }) => {\n\t\t\t\tconst paths = args.filter((arg) => arg !== \"-p\");\n\t\t\t\tfor (const target of paths) {\n\t\t\t\t\tthis.vfs.mkdir(normalizePath(target, this.cwd));\n\t\t\t\t}\n\t\t\t\tawait this.vfs.flushMirror();\n\t\t\t\treturn { exitCode: 0 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"touch\",\n\t\t\tdescription: \"Create files\",\n\t\t\tparams: [\"file...\"],\n\t\t\trun: async ({ args }) => {\n\t\t\t\tfor (const target of args) {\n\t\t\t\t\tthis.vfs.touch(normalizePath(target, this.cwd));\n\t\t\t\t}\n\t\t\t\tawait this.vfs.flushMirror();\n\t\t\t\treturn { exitCode: 0 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"rm\",\n\t\t\tdescription: \"Remove files or directories\",\n\t\t\tparams: [\"[-r] [-f] path...\"],\n\t\t\trun: async ({ args }) => {\n\t\t\t\tconst recursive = args.includes(\"-r\");\n\t\t\t\tconst targets = args.filter((arg) => arg !== \"-r\" && arg !== \"-f\");\n\t\t\t\tfor (const target of targets) {\n\t\t\t\t\tthis.vfs.remove(normalizePath(target, this.cwd), { recursive });\n\t\t\t\t}\n\t\t\t\tawait this.vfs.flushMirror();\n\t\t\t\treturn { exitCode: 0 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"cp\",\n\t\t\tdescription: \"Copy files or directories\",\n\t\t\tparams: [\"[-r] source destination\"],\n\t\t\trun: async ({ args }) => {\n\t\t\t\tconst recursive = args.includes(\"-r\");\n\t\t\t\tconst items = args.filter((arg) => arg !== \"-r\");\n\t\t\t\tif (items.length < 2) return { stderr: \"cp: missing destination file operand\", exitCode: 1 };\n\t\t\t\tconst dest = normalizePath(items.at(-1)!, this.cwd);\n\t\t\t\tfor (const source of items.slice(0, -1)) {\n\t\t\t\t\tconst sourcePath = normalizePath(source, this.cwd);\n\t\t\t\t\tif (!recursive && this.vfs.stat(sourcePath).type === \"directory\") {\n\t\t\t\t\t\treturn { stderr: `cp: -r not specified; omitting directory '${sourcePath}'`, exitCode: 1 };\n\t\t\t\t\t}\n\t\t\t\t\tthis.vfs.copy(sourcePath, dest);\n\t\t\t\t}\n\t\t\t\tawait this.vfs.flushMirror();\n\t\t\t\treturn { exitCode: 0 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"mv\",\n\t\t\tdescription: \"Move or rename files\",\n\t\t\tparams: [\"source destination\"],\n\t\t\trun: async ({ args }) => {\n\t\t\t\tif (args.length < 2) return { stderr: \"mv: missing destination file operand\", exitCode: 1 };\n\t\t\t\tconst source = normalizePath(args[0]!, this.cwd);\n\t\t\t\tconst destination = normalizePath(args[1]!, this.cwd);\n\t\t\t\tthis.vfs.move(source, destination);\n\t\t\t\tawait this.vfs.flushMirror();\n\t\t\t\treturn { exitCode: 0 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"cat\",\n\t\t\tdescription: \"Concatenate files\",\n\t\t\tparams: [\"[file...]\"],\n\t\t\trun: ({ args, stdin }) => {\n\t\t\t\tif (args.length === 0) return { stdout: stdin ?? \"\", exitCode: 0 };\n\t\t\t\tlet output = \"\";\n\t\t\t\tfor (const source of args) {\n\t\t\t\t\toutput += this.vfs.readFile(normalizePath(source, this.cwd));\n\t\t\t\t}\n\t\t\t\treturn { stdout: output, exitCode: 0 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"ls\",\n\t\t\tdescription: \"List files\",\n\t\t\tparams: [\"[path]\"],\n\t\t\trun: ({ args }) => {\n\t\t\t\tconst target = normalizePath(args[0] ?? \".\", this.cwd);\n\t\t\t\tconst entries = this.vfs.list(target);\n\t\t\t\treturn { stdout: `${entries.join(\" \")}\\n`, exitCode: 0 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"tee\",\n\t\t\tdescription: \"Read from stdin and write to files\",\n\t\t\tparams: [\"[-a] file...\"],\n\t\t\trun: async ({ args, stdin }) => {\n\t\t\t\tconst append = args.includes(\"-a\");\n\t\t\t\tconst targets = args.filter((arg) => arg !== \"-a\");\n\t\t\t\tconst content = stdin ?? \"\";\n\t\t\t\tfor (const target of targets) {\n\t\t\t\t\tconst normalized = normalizePath(target, this.cwd);\n\t\t\t\t\tif (append && this.vfs.exists(normalized)) {\n\t\t\t\t\t\tconst current = this.vfs.readFile(normalized);\n\t\t\t\t\t\tthis.vfs.writeFile(normalized, `${current}${content}`);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.vfs.writeFile(normalized, content);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tawait this.vfs.flushMirror();\n\t\t\t\treturn { stdout: content, exitCode: 0 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"curl\",\n\t\t\tdescription: \"Fetch a URL and optionally write to a file\",\n\t\t\tparams: [\"[-o file] URL\"],\n\t\t\trun: async ({ args }) => {\n\t\t\t\tconst outputIndex = args.indexOf(\"-o\");\n\t\t\t\tconst outputTarget = outputIndex !== -1 ? args[outputIndex + 1] : undefined;\n\t\t\t\tconst filtered = args.filter((arg, index) => arg !== \"-o\" && index !== outputIndex + 1);\n\t\t\t\tconst url = filtered.at(-1);\n\t\t\t\tif (!url) return { stderr: \"curl: missing URL\", exitCode: 2 };\n\t\t\t\tconst response = await fetch(url);\n\t\t\t\tconst body = await response.text();\n\t\t\t\tif (outputTarget) {\n\t\t\t\t\tthis.vfs.writeFile(normalizePath(outputTarget, this.cwd), body);\n\t\t\t\t\tawait this.vfs.flushMirror();\n\t\t\t\t\treturn { exitCode: response.ok ? 0 : 1 };\n\t\t\t\t}\n\t\t\t\treturn { stdout: body, exitCode: response.ok ? 0 : 1 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"wget\",\n\t\t\tdescription: \"Fetch a URL and optionally write to a file\",\n\t\t\tparams: [\"[-O file] URL\"],\n\t\t\trun: async ({ args }) => {\n\t\t\t\tconst outputIndex = args.indexOf(\"-O\");\n\t\t\t\tconst outputTarget = outputIndex !== -1 ? args[outputIndex + 1] : undefined;\n\t\t\t\tconst filtered = args.filter((arg, index) => arg !== \"-O\" && index !== outputIndex + 1);\n\t\t\t\tconst url = filtered.at(-1);\n\t\t\t\tif (!url) return { stderr: \"wget: missing URL\", exitCode: 2 };\n\t\t\t\tconst response = await fetch(url);\n\t\t\t\tconst body = await response.text();\n\t\t\t\tconst filename = outputTarget ?? basename(new URL(url).pathname || \"index.html\");\n\t\t\t\tthis.vfs.writeFile(normalizePath(filename, this.cwd), body);\n\t\t\t\tawait this.vfs.flushMirror();\n\t\t\t\treturn { exitCode: response.ok ? 0 : 1 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"true\",\n\t\t\tdescription: \"Return success\",\n\t\t\tparams: [],\n\t\t\trun: () => ({ exitCode: 0 }),\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"false\",\n\t\t\tdescription: \"Return failure\",\n\t\t\tparams: [],\n\t\t\trun: () => ({ exitCode: 1 }),\n\t\t});\n\t}\n\n\tprivate listCommands(): string[] {\n\t\tconst unique = new Map<string, WebCommand>();\n\t\tfor (const command of this.commands.values()) unique.set(command.name, command);\n\t\treturn Array.from(unique.values())\n\t\t\t.sort((a, b) => a.name.localeCompare(b.name))\n\t\t\t.map((command) => `${command.name}${command.params.length > 0 ? ` ${command.params.join(\" \")}` : \"\"}`);\n\t}\n\n\tprivate resolveCommand(name: string): WebCommand | undefined {\n\t\treturn this.commands.get(name.toLowerCase());\n\t}\n\n\tpublic async ensureInitialized(): Promise<void> {\n\t\tif (this.initialized) return;\n\t\tawait this.vfs.restoreMirror();\n\t\tif (!this.vfs.exists(\"/home\")) this.vfs.mkdir(\"/home\");\n\t\tif (!this.vfs.exists(\"/home/root\")) {\n\t\t\tthis.vfs.mkdir(\"/home/root\");\n\t\t\tthis.vfs.writeFile(\"/home/root/README.txt\", `Welcome to ${this.hostname}\\n`);\n\t\t}\n\t\tif (!this.vfs.exists(\"/tmp\")) this.vfs.mkdir(\"/tmp\");\n\t\tif (!this.vfs.exists(\"/etc\")) this.vfs.mkdir(\"/etc\");\n\t\tif (!this.vfs.exists(\"/etc/hostname\")) this.vfs.writeFile(\"/etc/hostname\", `${this.hostname}\\n`);\n\t\tif (!this.vfs.exists(\"/etc/hosts\")) {\n\t\t\tthis.vfs.writeFile(\"/etc/hosts\", \"127.0.0.1 localhost\\n::1 localhost\\n\");\n\t\t}\n\t\tthis.initialized = true;\n\t}\n\n\tpublic getCurrentWorkingDirectory(): string {\n\t\treturn this.cwd;\n\t}\n\n\tpublic async executeCommandLine(rawInput: string, persist = true): Promise<CommandResult> {\n\t\tawait this.ensureInitialized();\n\t\tconst trimmed = rawInput.trim();\n\t\tif (!trimmed) return { exitCode: 0 };\n\n\t\tconst expanded = await expandAsync(\n\t\t\ttrimmed,\n\t\t\tthis.env.vars,\n\t\t\tthis.env.lastExitCode,\n\t\t\t(subcommand) => this.executeCommandLine(subcommand, false).then((r) => r.stdout ?? \"\"),\n\t\t);\n\n\t\tconst script = parseScript(expanded);\n\t\tconst result = await this.executeStatements(script.statements);\n\t\tthis.env.lastExitCode = result.exitCode ?? 0;\n\t\tif (persist) await this.vfs.flushMirror();\n\t\treturn result;\n\t}\n\n\tprivate async executeStatements(statements: Statement[]): Promise<CommandResult> {\n\t\tlet last: CommandResult = { exitCode: 0 };\n\t\tlet index = 0;\n\n\t\twhile (index < statements.length) {\n\t\t\tconst stmt = statements[index]!;\n\t\t\tlast = await this.executePipeline(stmt.pipeline.commands as PipelineCommand[]);\n\t\t\tthis.env.lastExitCode = last.exitCode ?? 0;\n\t\t\tif (last.closeSession || last.switchUser) return last;\n\n\t\t\tconst op = stmt.op;\n\t\t\tif (!op || op === \";\") {\n\t\t\t\t// continue\n\t\t\t} else if (op === \"&&\") {\n\t\t\t\tif ((last.exitCode ?? 0) !== 0) {\n\t\t\t\t\twhile (index < statements.length && statements[index]?.op === \"&&\") index += 1;\n\t\t\t\t}\n\t\t\t} else if (op === \"||\") {\n\t\t\t\tif ((last.exitCode ?? 0) === 0) {\n\t\t\t\t\twhile (index < statements.length && statements[index]?.op === \"||\") index += 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tindex += 1;\n\t\t}\n\n\t\treturn last;\n\t}\n\n\tprivate async executePipeline(commands: PipelineCommand[]): Promise<CommandResult> {\n\t\tif (commands.length === 0) return { exitCode: 0 };\n\t\tif (commands.length === 1) {\n\t\t\treturn this.executeSingleCommandWithRedirections(commands[0]!);\n\t\t}\n\t\treturn this.executePipelineChain(commands);\n\t}\n\n\tprivate async executeSingleCommandWithRedirections(command: PipelineCommand): Promise<CommandResult> {\n\t\tlet stdin: string | undefined;\n\t\tif (command.inputFile) {\n\t\t\tconst inputPath = normalizePath(command.inputFile, this.cwd);\n\t\t\ttry {\n\t\t\t\tstdin = this.vfs.readFile(inputPath);\n\t\t\t} catch {\n\t\t\t\treturn { stderr: `${command.inputFile}: No such file or directory`, exitCode: 1 };\n\t\t\t}\n\t\t}\n\n\t\tconst result = await this.executeCommand(command.name, command.args, stdin);\n\n\t\tif (command.outputFile) {\n\t\t\tconst outputPath = normalizePath(command.outputFile, this.cwd);\n\t\t\tconst output = result.stdout ?? \"\";\n\t\t\tif (command.appendOutput && this.vfs.exists(outputPath)) {\n\t\t\t\tconst existing = this.vfs.readFile(outputPath);\n\t\t\t\tthis.vfs.writeFile(outputPath, `${existing}${output}`);\n\t\t\t} else {\n\t\t\t\tthis.vfs.writeFile(outputPath, output);\n\t\t\t}\n\t\t\treturn { ...result, stdout: \"\" };\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate async executePipelineChain(commands: PipelineCommand[]): Promise<CommandResult> {\n\t\tlet currentOutput = \"\";\n\t\tlet exitCode = 0;\n\n\t\tfor (let index = 0; index < commands.length; index += 1) {\n\t\t\tconst command = commands[index]!;\n\t\t\tif (index === 0 && command.inputFile) {\n\t\t\t\tconst inputPath = normalizePath(command.inputFile, this.cwd);\n\t\t\t\ttry {\n\t\t\t\t\tcurrentOutput = this.vfs.readFile(inputPath);\n\t\t\t\t} catch {\n\t\t\t\t\treturn { stderr: `${command.inputFile}: No such file or directory`, exitCode: 1 };\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst result = await this.executeCommand(command.name, command.args, currentOutput);\n\t\t\tcurrentOutput = result.stdout ?? \"\";\n\t\t\texitCode = result.exitCode ?? 0;\n\t\t}\n\n\t\treturn { stdout: currentOutput, exitCode };\n\t}\n\n\tprivate async executeCommand(\n\t\tname: string,\n\t\targs: string[],\n\t\tstdin?: string,\n\t): Promise<CommandResult> {\n\t\tconst command = this.resolveCommand(name);\n\t\tif (!command) {\n\t\t\treturn { stderr: `${name}: command not found`, exitCode: 127 };\n\t\t}\n\n\t\tconst expandedArgs = args.map((arg) => expandSync(arg, this.env.vars, this.env.lastExitCode, this.env.vars.HOME));\n\t\tconst context: WebCommandContext = {\n\t\t\targs: expandedArgs,\n\t\t\tstdin,\n\t\t\tcwd: this.cwd,\n\t\t\tenv: this.env,\n\t\t\trawInput: `${name} ${args.join(\" \")}`.trim(),\n\t\t\tshell: this,\n\t\t};\n\n\t\ttry {\n\t\t\tconst result = await command.run(context);\n\t\t\tif (result.nextCwd) {\n\t\t\t\tthis.cwd = result.nextCwd;\n\t\t\t\tthis.env.vars.PWD = result.nextCwd;\n\t\t\t}\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\treturn { stderr: message, exitCode: 1 };\n\t\t}\n\t}\n}\n\nexport { IndexedDbMirrorVfs, WebShell };\nexport function createWebShell(hostname = \"typescript-vm\", options: WebShellOptions = {}): WebShell {\n\treturn new WebShell(hostname, options);\n}\nexport type { WebCommand, WebCommandContext, WebShellOptions, WebVfsOptions };\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["parseScript", "rawInput", "trimmed", "parseStatements", "e", "parseStatements", "input", "segments", "splitByLogicalOps", "statements", "seg", "stmt", "parsePipeline", "current", "depth", "inQ", "qChar", "flush", "op", "ch", "ch2", "splitByPipe", "parseCommandWithRedirections", "tokens", "tail", "token", "parts", "tokenizeCommand", "cmdParts", "inputFile", "outputFile", "appendOutput", "part", "
|
|
3
|
+
"sources": ["../src/utils/tokenize.ts", "../src/VirtualShell/shellParser.ts", "../src/utils/expand.ts", "../src/web.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * tokenize.ts\n *\n * Shared shell tokenizer used by `shellParser.ts` and `runtime.ts`.\n * Splits a shell input string into tokens respecting single and double\n * quotes, and separates `>`, `>>`, `<` as standalone redirect tokens.\n */\n\n/**\n * Tokenize a shell command line respecting quoted strings and redirect\n * operators.\n *\n * - Single-quoted content is preserved verbatim.\n * - Double-quoted content is preserved (expansion happens later).\n * - `>`, `>>`, and `<` are emitted as standalone tokens.\n *\n * @param input Raw shell command string.\n * @returns Array of string tokens.\n */\nexport function tokenizeCommand(input: string): string[] {\n\tconst tokens: string[] = [];\n\tlet current = \"\";\n\tlet inQ = false;\n\tlet qChar = \"\";\n\tlet i = 0;\n\n\twhile (i < input.length) {\n\t\tconst ch = input[i]!;\n\t\tconst next = input[i + 1];\n\n\t\tif ((ch === '\"' || ch === \"'\") && !inQ) {\n\t\t\tinQ = true;\n\t\t\tqChar = ch;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (inQ && ch === qChar) {\n\t\t\tinQ = false;\n\t\t\tqChar = \"\";\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (inQ) {\n\t\t\tcurrent += ch;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (ch === \" \") {\n\t\t\tif (current) {\n\t\t\t\ttokens.push(current);\n\t\t\t\tcurrent = \"\";\n\t\t\t}\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif ((ch === \">\" || ch === \"<\") && !inQ) {\n\t\t\tif (current) {\n\t\t\t\ttokens.push(current);\n\t\t\t\tcurrent = \"\";\n\t\t\t}\n\t\t\tif (ch === \">\" && next === \">\") {\n\t\t\t\ttokens.push(\">>\");\n\t\t\t\ti += 2;\n\t\t\t} else {\n\t\t\t\ttokens.push(ch);\n\t\t\t\ti++;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tcurrent += ch;\n\t\ti++;\n\t}\n\tif (current) tokens.push(current);\n\treturn tokens;\n}\n", "import type {\n\tPipeline,\n\tPipelineCommand,\n\tScript,\n\tStatement,\n\tLogicalOp,\n} from \"../types/pipeline\";\nimport { tokenizeCommand } from \"../utils/tokenize\";\n\n// \u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Parse a shell input line into a Script (sequence of statements connected\n * by && / || / ;). Each statement contains one Pipeline (commands connected\n * by |).\n */\nexport function parseScript(rawInput: string): Script {\n\tconst trimmed = rawInput.trim();\n\tif (!trimmed) return { statements: [], isValid: true };\n\n\ttry {\n\t\tconst statements = parseStatements(trimmed);\n\t\treturn { statements, isValid: true };\n\t} catch (e) {\n\t\treturn { statements: [], isValid: false, error: (e as Error).message };\n\t}\n}\n\n/** Parse a single pipeline string (no &&/||/;) into a `Pipeline` object. */\nexport function parseShellPipeline(rawInput: string): Pipeline {\n\tconst trimmed = rawInput.trim();\n\tif (!trimmed) return { commands: [], isValid: true };\n\ttry {\n\t\tconst commands = parsePipeline(trimmed);\n\t\treturn { commands, isValid: true };\n\t} catch (e) {\n\t\treturn { commands: [], isValid: false, error: (e as Error).message };\n\t}\n}\n\n// \u2500\u2500 Variable & tilde expansion \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Expand glob patterns (*, ?, [abc]) against a list of entries.\n * Returns the original pattern if no match.\n */\nexport function expandGlob(pattern: string, entries: string[]): string[] {\n\tif (!/[*?[]/.test(pattern)) return [pattern];\n\tconst regex = globToRegex(pattern);\n\tconst matches = entries.filter((e) => regex.test(e));\n\treturn matches.length > 0 ? matches.sort() : [pattern];\n}\n\nfunction globToRegex(pattern: string): RegExp {\n\tlet re = \"^\";\n\tfor (let i = 0; i < pattern.length; i++) {\n\t\tconst c = pattern[i]!;\n\t\tif (c === \"*\") re += \".*\";\n\t\telse if (c === \"?\") re += \".\";\n\t\telse if (c === \"[\") {\n\t\t\tconst close = pattern.indexOf(\"]\", i + 1);\n\t\t\tif (close === -1) re += \"\\\\[\";\n\t\t\telse {\n\t\t\t\tre += `[${pattern.slice(i + 1, close)}]`;\n\t\t\t\ti = close;\n\t\t\t}\n\t\t} else re += c.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\t}\n\treturn new RegExp(`${re}$`);\n}\n\n// \u2500\u2500 Internal parser \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction parseStatements(input: string): Statement[] {\n\t// Split by ;, &&, || \u2014 respecting quotes and parens\n\tconst segments = splitByLogicalOps(input);\n\tconst statements: Statement[] = [];\n\n\tfor (const seg of segments) {\n\t\tconst commands = parsePipeline(seg.text.trim());\n\t\tconst stmt: Statement = { pipeline: { commands, isValid: true } };\n\t\tif (seg.op) stmt.op = seg.op;\n\t\tstatements.push(stmt);\n\t}\n\n\treturn statements;\n}\n\ninterface Segment {\n\ttext: string;\n\top?: LogicalOp;\n}\n\nfunction splitByLogicalOps(input: string): Segment[] {\n\tconst segments: Segment[] = [];\n\tlet current = \"\";\n\tlet depth = 0; // parens/subshell depth\n\tlet inQ = false;\n\tlet qChar = \"\";\n\tlet i = 0;\n\n\tconst flush = (op?: LogicalOp) => {\n\t\tif (current.trim()) segments.push({ text: current, op });\n\t\tcurrent = \"\";\n\t};\n\n\twhile (i < input.length) {\n\t\tconst ch = input[i]!;\n\t\tconst ch2 = input.slice(i, i + 2);\n\n\t\tif ((ch === '\"' || ch === \"'\") && !inQ) {\n\t\t\tinQ = true;\n\t\t\tqChar = ch;\n\t\t\tcurrent += ch;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (inQ && ch === qChar) {\n\t\t\tinQ = false;\n\t\t\tcurrent += ch;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (inQ) {\n\t\t\tcurrent += ch;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (ch === \"(\") {\n\t\t\tdepth++;\n\t\t\tcurrent += ch;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === \")\") {\n\t\t\tdepth--;\n\t\t\tcurrent += ch;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (depth > 0) {\n\t\t\tcurrent += ch;\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (ch2 === \"&&\") {\n\t\t\tflush(\"&&\");\n\t\t\ti += 2;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch2 === \"||\") {\n\t\t\tflush(\"||\");\n\t\t\ti += 2;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === \";\") {\n\t\t\tflush(\";\");\n\t\t\ti++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tcurrent += ch;\n\t\ti++;\n\t}\n\tflush();\n\treturn segments;\n}\n\nfunction parsePipeline(input: string): PipelineCommand[] {\n\tconst pipeTokens = splitByPipe(input);\n\treturn pipeTokens.map(parseCommandWithRedirections);\n}\n\nfunction splitByPipe(input: string): string[] {\n\tconst tokens: string[] = [];\n\tlet current = \"\";\n\tlet inQ = false;\n\tlet qChar = \"\";\n\n\tfor (let i = 0; i < input.length; i++) {\n\t\tconst ch = input[i]!;\n\t\tif ((ch === '\"' || ch === \"'\") && !inQ) {\n\t\t\tinQ = true;\n\t\t\tqChar = ch;\n\t\t\tcurrent += ch;\n\t\t\tcontinue;\n\t\t}\n\t\tif (inQ && ch === qChar) {\n\t\t\tinQ = false;\n\t\t\tcurrent += ch;\n\t\t\tcontinue;\n\t\t}\n\t\tif (inQ) {\n\t\t\tcurrent += ch;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// || was already consumed at statement level, bare | is pipe\n\t\tif (ch === \"|\" && input[i + 1] !== \"|\") {\n\t\t\tif (!current.trim())\n\t\t\t\tthrow new Error(\"Syntax error near unexpected token '|'\");\n\t\t\ttokens.push(current.trim());\n\t\t\tcurrent = \"\";\n\t\t} else {\n\t\t\tcurrent += ch;\n\t\t}\n\t}\n\n\tconst tail = current.trim();\n\tif (!tail && tokens.length > 0)\n\t\tthrow new Error(\"Syntax error near unexpected token '|'\");\n\tif (tail) tokens.push(tail);\n\treturn tokens;\n}\n\nfunction parseCommandWithRedirections(token: string): PipelineCommand {\n\tconst parts = tokenizeCommand(token);\n\tif (parts.length === 0) return { name: \"\", args: [] };\n\n\tconst cmdParts: string[] = [];\n\tlet inputFile: string | undefined;\n\tlet outputFile: string | undefined;\n\tlet appendOutput = false;\n\tlet i = 0;\n\n\twhile (i < parts.length) {\n\t\tconst part = parts[i]!;\n\t\tif (part === \"<\") {\n\t\t\ti++;\n\t\t\tif (i >= parts.length)\n\t\t\t\tthrow new Error(\"Syntax error: expected filename after <\");\n\t\t\tinputFile = parts[i];\n\t\t\ti++;\n\t\t} else if (part === \">>\") {\n\t\t\ti++;\n\t\t\tif (i >= parts.length)\n\t\t\t\tthrow new Error(\"Syntax error: expected filename after >>\");\n\t\t\toutputFile = parts[i];\n\t\t\tappendOutput = true;\n\t\t\ti++;\n\t\t} else if (part === \">\") {\n\t\t\ti++;\n\t\t\tif (i >= parts.length)\n\t\t\t\tthrow new Error(\"Syntax error: expected filename after >\");\n\t\t\toutputFile = parts[i];\n\t\t\tappendOutput = false;\n\t\t\ti++;\n\t\t} else {\n\t\t\tcmdParts.push(part);\n\t\t\ti++;\n\t\t}\n\t}\n\n\tconst name = (cmdParts[0] ?? \"\").toLowerCase();\n\treturn { name, args: cmdParts.slice(1), inputFile, outputFile, appendOutput };\n}\n\n", "/**\n * expand.ts\n *\n * Centralised shell variable and expression expansion.\n * Used by `runCommand` (index.ts), `echo`, and `sh.ts`.\n *\n * Handles (in order):\n * ~ tilde to $HOME\n * $? last exit code\n * $$ mock PID\n * $# argument count (0 outside scripts)\n * ${#VAR} string length\n * ${VAR:-def} default if unset/empty\n * ${VAR:=def} assign default if unset/empty\n * ${VAR:+val} alternate value if set\n * ${VAR} simple braced reference\n * $VAR simple reference\n * $((expr)) arithmetic (integer)\n */\n\n// \u2500\u2500\u2500 arithmetic evaluator \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Evaluate a simple integer arithmetic expression.\n * Supports: + - * / % ** unary- ( )\n * Variables are resolved from `env` before evaluation.\n * Returns NaN on syntax error.\n */\nexport function evalArith(expr: string, env: Record<string, string>): number {\n\t// Substitute variable names before evaluating\n\tconst substituted = expr.replace(\n\t\t/\\b([A-Za-z_][A-Za-z0-9_]*)\\b/g,\n\t\t(_, name) => {\n\t\t\tconst val = env[name];\n\t\t\treturn val !== undefined && val !== \"\" ? val : \"0\";\n\t\t},\n\t);\n\n\t// Whitelist: only digits, operators, spaces, parens\n\tif (!/^[\\d\\s+\\-*/%()^!&|<>=,. ]+$/.test(substituted)) return NaN;\n\n\ttry {\n\t\t// Use Function constructor for safe subset (no identifiers remain)\n\t\t// eslint-disable-next-line no-new-func\n\t\tconst result = Function(\n\t\t\t`\"use strict\"; return (${substituted.replace(/\\*\\*/g, \"**\")});`,\n\t\t)();\n\t\treturn typeof result === \"number\" ? Math.trunc(result) : NaN;\n\t} catch {\n\t\treturn NaN;\n\t}\n}\n\n// \u2500\u2500\u2500 synchronous expansion \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Apply a replacer only to portions of `input` that are NOT inside single quotes.\n * Single-quoted content is passed through verbatim (POSIX sh behaviour).\n */\nfunction outsideSingleQuotes(\n\tinput: string,\n\treplacer: (chunk: string) => string,\n): string {\n\tconst parts: string[] = [];\n\tlet i = 0;\n\twhile (i < input.length) {\n\t\tconst sqIdx = input.indexOf(\"'\", i);\n\t\tif (sqIdx === -1) {\n\t\t\t// No more single quotes \u2014 expand the rest\n\t\t\tparts.push(replacer(input.slice(i)));\n\t\t\tbreak;\n\t\t}\n\t\t// Expand the part before the single quote\n\t\tparts.push(replacer(input.slice(i, sqIdx)));\n\t\t// Find closing single quote \u2014 everything inside is literal\n\t\tconst closeIdx = input.indexOf(\"'\", sqIdx + 1);\n\t\tif (closeIdx === -1) {\n\t\t\t// Unclosed quote \u2014 treat rest as literal\n\t\t\tparts.push(input.slice(sqIdx));\n\t\t\tbreak;\n\t\t}\n\t\tparts.push(input.slice(sqIdx, closeIdx + 1)); // include quotes\n\t\ti = closeIdx + 1;\n\t}\n\treturn parts.join(\"\");\n}\n\n/**\n * Expand all shell variable and expression forms synchronously.\n * Does NOT handle `$(cmd)` \u2014 that requires async; see `expandAsync`.\n * Content inside single quotes is left verbatim per POSIX sh rules.\n *\n * @param input Raw string possibly containing `$VAR`, `${...}`, `$((...))`.\n * @param env Current session env vars.\n * @param lastExit Last command exit code (for `$?`).\n * @param home Home directory path (for `~`).\n */\nexport function expandSync(\n\tinput: string,\n\tenv: Record<string, string>,\n\tlastExit = 0,\n\thome?: string,\n): string {\n\tconst homePath = home ?? env.HOME ?? \"/home/user\";\n\n\treturn outsideSingleQuotes(input, (chunk) => {\n\t\tlet s = chunk;\n\n\t\t// Tilde expansion \u2014 only at start of token or after `:` or whitespace\n\t\ts = s.replace(\n\t\t\t/(^|[\\s:])~(\\/|$)/g,\n\t\t\t(_, pre, post) => `${pre}${homePath}${post}`,\n\t\t);\n\n\t\t// $? $$ $#\n\t\ts = s.replace(/\\$\\?/g, String(lastExit));\n\t\ts = s.replace(/\\$\\$/g, \"1\");\n\t\ts = s.replace(/\\$#/g, \"0\");\n\n\t\t// $(( arithmetic )) \u2014 must come before ${ and $VAR to avoid conflicts\n\t\ts = s.replace(/\\$\\(\\(([^)]+(?:\\([^)]*\\)[^)]*)*)\\)\\)/g, (_, expr) => {\n\t\t\tconst result = evalArith(expr, env);\n\t\t\treturn Number.isNaN(result) ? \"0\" : String(result);\n\t\t});\n\n\t\t// ${#VAR} \u2014 string length\n\t\ts = s.replace(/\\$\\{#([A-Za-z_][A-Za-z0-9_]*)\\}/g, (_, name) =>\n\t\t\tString((env[name] ?? \"\").length),\n\t\t);\n\n\t\t// ${VAR:-default}\n\t\ts = s.replace(/\\$\\{([A-Za-z_][A-Za-z0-9_]*):-([^}]*)\\}/g, (_, name, def) =>\n\t\t\tenv[name] !== undefined && env[name] !== \"\" ? (env[name] as string) : def,\n\t\t);\n\n\t\t// ${VAR:=default} \u2014 also assigns to env\n\t\ts = s.replace(\n\t\t\t/\\$\\{([A-Za-z_][A-Za-z0-9_]*):=([^}]*)\\}/g,\n\t\t\t(_, name, def) => {\n\t\t\t\tif (env[name] === undefined || env[name] === \"\") env[name] = def;\n\t\t\t\treturn env[name] as string;\n\t\t\t},\n\t\t);\n\n\t\t// ${VAR:+alternate}\n\t\ts = s.replace(\n\t\t\t/\\$\\{([A-Za-z_][A-Za-z0-9_]*):\\+([^}]*)\\}/g,\n\t\t\t(_, name, alt) =>\n\t\t\t\tenv[name] !== undefined && env[name] !== \"\" ? alt : \"\",\n\t\t);\n\n\t\t// ${VAR}\n\t\ts = s.replace(\n\t\t\t/\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g,\n\t\t\t(_, name) => env[name] ?? \"\",\n\t\t);\n\n\t\t// $VAR\n\t\ts = s.replace(/\\$([A-Za-z_][A-Za-z0-9_]*)/g, (_, name) => env[name] ?? \"\");\n\n\t\treturn s;\n\t});\n}\n\n// \u2500\u2500\u2500 async expansion (includes $(cmd)) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Expand all shell forms including `$(cmd)` command substitution.\n *\n * Processes `$(...)` blocks depth-first, respecting single-quote boundaries.\n * Then delegates to `expandSync` for the remaining forms.\n *\n * @param input Raw string.\n * @param env Current session env vars.\n * @param lastExit Last exit code.\n * @param runCmd Async callback to execute a command and return its stdout.\n */\nexport async function expandAsync(\n\tinput: string,\n\tenv: Record<string, string>,\n\tlastExit: number,\n\trunCmd: (cmd: string) => Promise<string>,\n): Promise<string> {\n\t// $(cmd) substitution \u2014 skip content inside single quotes\n\tif (input.includes(\"$(\")) {\n\t\tlet result = \"\";\n\t\tlet inSingle = false;\n\t\tlet i = 0;\n\n\t\twhile (i < input.length) {\n\t\t\tconst ch = input[i]!;\n\n\t\t\tif (ch === \"'\" && !inSingle) {\n\t\t\t\tinSingle = true;\n\t\t\t\tresult += ch;\n\t\t\t\ti++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (ch === \"'\" && inSingle) {\n\t\t\t\tinSingle = false;\n\t\t\t\tresult += ch;\n\t\t\t\ti++;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!inSingle && ch === \"$\" && input[i + 1] === \"(\") {\n\t\t\t\t// $((expr)) arithmetic \u2014 NOT a $(cmd) substitution, skip it\n\t\t\t\tif (input[i + 2] === \"(\") {\n\t\t\t\t\tresult += ch;\n\t\t\t\t\ti++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// Find matching ) with depth tracking\n\t\t\t\tlet depth = 0;\n\t\t\t\tlet j = i + 1;\n\t\t\t\twhile (j < input.length) {\n\t\t\t\t\tif (input[j] === \"(\") depth++;\n\t\t\t\t\telse if (input[j] === \")\") {\n\t\t\t\t\t\tdepth--;\n\t\t\t\t\t\tif (depth === 0) break;\n\t\t\t\t\t}\n\t\t\t\t\tj++;\n\t\t\t\t}\n\t\t\t\tconst sub = input.slice(i + 2, j).trim();\n\t\t\t\tconst out = (await runCmd(sub)).replace(/\\n$/, \"\");\n\t\t\t\tresult += out;\n\t\t\t\ti = j + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tresult += ch;\n\t\t\ti++;\n\t\t}\n\t\tinput = result;\n\t}\n\n\treturn expandSync(input, env, lastExit);\n}\n", "/** biome-ignore-all lint/style/useNamingConvention: env vars */\nimport { parseScript } from \"./VirtualShell/shellParser\";\nimport type { CommandResult, ShellEnv } from \"./types/commands\";\nimport type { PipelineCommand, Statement } from \"./types/pipeline\";\nimport { expandAsync, expandSync } from \"./utils/expand\";\n\ntype WebCommandContext = {\n\targs: string[];\n\tstdin?: string;\n\tcwd: string;\n\tenv: ShellEnv;\n\trawInput: string;\n\tshell: WebShell;\n};\n\ntype WebCommandHandler = (\n\tcontext: WebCommandContext,\n) => CommandResult | Promise<CommandResult>;\n\ninterface WebCommand {\n\tname: string;\n\tdescription: string;\n\tparams: string[];\n\trun: WebCommandHandler;\n\taliases?: string[];\n}\n\ntype WebFileNode = {\n\ttype: \"file\";\n\tname: string;\n\tmode: number;\n\tcreatedAt: string;\n\tupdatedAt: string;\n\tcontentBase64: string;\n};\n\ntype WebDirectoryNode = {\n\ttype: \"directory\";\n\tname: string;\n\tmode: number;\n\tcreatedAt: string;\n\tupdatedAt: string;\n\tchildren: WebNode[];\n};\n\ntype WebNode = WebFileNode | WebDirectoryNode;\n\ninterface WebVfsSnapshot {\n\troot: WebDirectoryNode;\n}\n\ninterface WebVfsOptions {\n\tdatabaseName?: string;\n\tstoreName?: string;\n\tkey?: string;\n}\n\ninterface WebShellOptions {\n\tcwd?: string;\n\tvfs?: WebVfsOptions;\n}\n\nconst textEncoder = new TextEncoder();\nconst textDecoder = new TextDecoder();\n\nfunction encodeBase64(bytes: Uint8Array): string {\n\tlet binary = \"\";\n\tfor (const byte of bytes) binary += String.fromCharCode(byte);\n\treturn btoa(binary);\n}\n\nfunction decodeBase64(base64: string): Uint8Array {\n\tconst binary = atob(base64);\n\tconst bytes = new Uint8Array(binary.length);\n\tfor (let index = 0; index < binary.length; index += 1) {\n\t\tbytes[index] = binary.charCodeAt(index);\n\t}\n\treturn bytes;\n}\n\nfunction normalizePath(inputPath: string, cwd = \"/\"): string {\n\tconst raw = inputPath.startsWith(\"/\") ? inputPath : `${cwd}/${inputPath}`;\n\tconst parts = raw.split(\"/\");\n\tconst stack: string[] = [];\n\n\tfor (const part of parts) {\n\t\tif (!part || part === \".\") continue;\n\t\tif (part === \"..\") {\n\t\t\tstack.pop();\n\t\t\tcontinue;\n\t\t}\n\t\tstack.push(part);\n\t}\n\n\treturn `/${stack.join(\"/\")}` || \"/\";\n}\n\nfunction dirname(inputPath: string): string {\n\tconst normalized = normalizePath(inputPath);\n\tif (normalized === \"/\") return \"/\";\n\tconst parts = normalized.split(\"/\").filter(Boolean);\n\tparts.pop();\n\treturn parts.length > 0 ? `/${parts.join(\"/\")}` : \"/\";\n}\n\nfunction basename(inputPath: string): string {\n\tconst normalized = normalizePath(inputPath);\n\tif (normalized === \"/\") return \"/\";\n\tconst parts = normalized.split(\"/\").filter(Boolean);\n\treturn parts.at(-1) ?? \"/\";\n}\n\nfunction cloneNode(node: WebNode): WebNode {\n\tif (node.type === \"file\") {\n\t\treturn {\n\t\t\t...node,\n\t\t\tcontentBase64: node.contentBase64,\n\t\t};\n\t}\n\n\treturn {\n\t\t...node,\n\t\tchildren: node.children.map((child) => cloneNode(child)),\n\t};\n}\n\nfunction makeDirectory(name: string, mode: number): WebDirectoryNode {\n\tconst now = new Date().toISOString();\n\treturn {\n\t\ttype: \"directory\",\n\t\tname,\n\t\tmode,\n\t\tcreatedAt: now,\n\t\tupdatedAt: now,\n\t\tchildren: [],\n\t};\n}\n\nfunction makeFile(name: string, content: Uint8Array, mode: number): WebFileNode {\n\tconst now = new Date().toISOString();\n\treturn {\n\t\ttype: \"file\",\n\t\tname,\n\t\tmode,\n\t\tcreatedAt: now,\n\t\tupdatedAt: now,\n\t\tcontentBase64: encodeBase64(content),\n\t};\n}\n\nfunction findChild(directory: WebDirectoryNode, name: string): WebNode | undefined {\n\treturn directory.children.find((child) => child.name === name);\n}\n\nfunction setChild(directory: WebDirectoryNode, node: WebNode): void {\n\tconst index = directory.children.findIndex((child) => child.name === node.name);\n\tif (index === -1) {\n\t\tdirectory.children.push(node);\n\t\treturn;\n\t}\n\tdirectory.children[index] = node;\n}\n\nfunction removeChild(directory: WebDirectoryNode, name: string): void {\n\tdirectory.children = directory.children.filter((child) => child.name !== name);\n}\n\nfunction splitPath(pathValue: string): string[] {\n\treturn normalizePath(pathValue).split(\"/\").filter(Boolean);\n}\n\ntype WebIndexedDbRequest<T> = {\n\tresult: T;\n\terror: unknown;\n\taddEventListener(type: \"success\" | \"error\", listener: () => void): void;\n};\n\ntype WebIndexedDbObjectStore = {\n\tget(key: string): WebIndexedDbRequest<unknown>;\n\tput(value: string, key: string): WebIndexedDbRequest<unknown>;\n};\n\ntype WebIndexedDbTransaction = {\n\tobjectStore(name: string): WebIndexedDbObjectStore;\n\terror: unknown;\n\taddEventListener(type: \"complete\" | \"error\" | \"abort\", listener: () => void): void;\n};\n\ntype WebIndexedDbDatabase = {\n\tobjectStoreNames: {\n\t\tcontains(name: string): boolean;\n\t};\n\tcreateObjectStore(name: string): unknown;\n\ttransaction(name: string, mode: \"readonly\" | \"readwrite\"): WebIndexedDbTransaction;\n\tclose(): void;\n};\n\ntype WebIndexedDbOpenRequest = {\n\tresult: WebIndexedDbDatabase;\n\terror: unknown;\n\taddEventListener(type: \"upgradeneeded\" | \"success\" | \"error\", listener: () => void): void;\n};\n\ntype WebIndexedDbFactory = {\n\topen(name: string, version?: number): WebIndexedDbOpenRequest;\n};\n\nconst webGlobal = globalThis as typeof globalThis & { indexedDB?: WebIndexedDbFactory };\n\nfunction promisifyRequest<T>(request: WebIndexedDbRequest<T>): Promise<T> {\n\treturn new Promise((resolve, reject) => {\n\t\trequest.addEventListener(\"success\", () => resolve(request.result));\n\t\trequest.addEventListener(\"error\", () => reject(request.error));\n\t});\n}\n\nclass IndexedDbMirrorVfs {\n\tprivate readonly databaseName: string;\n\tprivate readonly storeName: string;\n\tprivate readonly key: string;\n\tprivate root: WebDirectoryNode;\n\n\tconstructor(options: WebVfsOptions = {}) {\n\t\tthis.databaseName = options.databaseName ?? \"typescript-virtual-container-web\";\n\t\tthis.storeName = options.storeName ?? \"snapshots\";\n\t\tthis.key = options.key ?? \"current\";\n\t\tthis.root = makeDirectory(\"\", 0o755);\n\t}\n\n\tprivate async openDatabase(): Promise<WebIndexedDbDatabase> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst indexedDbFactory = webGlobal.indexedDB;\n\t\t\tif (!indexedDbFactory) {\n\t\t\t\treject(new Error(\"IndexedDB is not available in this environment\"));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst request = indexedDbFactory.open(this.databaseName, 1);\n\t\t\trequest.addEventListener(\"upgradeneeded\", () => {\n\t\t\t\tconst database = request.result;\n\t\t\t\tif (!database.objectStoreNames.contains(this.storeName)) {\n\t\t\t\t\tdatabase.createObjectStore(this.storeName);\n\t\t\t\t}\n\t\t\t});\n\t\t\trequest.addEventListener(\"success\", () => resolve(request.result));\n\t\t\trequest.addEventListener(\"error\", () => reject(request.error));\n\t\t});\n\t}\n\n\tprivate async readSnapshot(): Promise<WebVfsSnapshot | null> {\n\t\tconst database = await this.openDatabase();\n\t\ttry {\n\t\t\tconst transaction = database.transaction(this.storeName, \"readonly\");\n\t\t\tconst store = transaction.objectStore(this.storeName);\n\t\t\tconst request = store.get(this.key);\n\t\t\tconst result = (await promisifyRequest(request)) as string | undefined;\n\t\t\tif (!result) return null;\n\t\t\treturn JSON.parse(result) as WebVfsSnapshot;\n\t\t} finally {\n\t\t\tdatabase.close();\n\t\t}\n\t}\n\n\tprivate async writeSnapshot(snapshot: WebVfsSnapshot): Promise<void> {\n\t\tconst database = await this.openDatabase();\n\t\ttry {\n\t\t\tconst transaction = database.transaction(this.storeName, \"readwrite\");\n\t\t\tconst store = transaction.objectStore(this.storeName);\n\t\t\tawait promisifyRequest(store.put(JSON.stringify(snapshot), this.key));\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\ttransaction.addEventListener(\"complete\", () => resolve());\n\t\t\t\ttransaction.addEventListener(\"error\", () => reject(transaction.error));\n\t\t\t\ttransaction.addEventListener(\"abort\", () => reject(transaction.error));\n\t\t\t});\n\t\t} finally {\n\t\t\tdatabase.close();\n\t\t}\n\t}\n\n\tprivate serializeNode(node: WebNode): WebNode {\n\t\tif (node.type === \"file\") return { ...node };\n\t\treturn {\n\t\t\t...node,\n\t\t\tchildren: node.children.map((child) => this.serializeNode(child)),\n\t\t};\n\t}\n\n\tprivate deserializeNode(node: WebNode): WebNode {\n\t\tif (node.type === \"file\") return { ...node };\n\t\treturn {\n\t\t\t...node,\n\t\t\tchildren: node.children.map((child) => this.deserializeNode(child)),\n\t\t};\n\t}\n\n\tprivate getNode(targetPath: string): WebNode {\n\t\tconst normalized = normalizePath(targetPath);\n\t\tif (normalized === \"/\") return this.root;\n\n\t\tconst parts = splitPath(normalized);\n\t\tlet current: WebNode = this.root;\n\t\tfor (const part of parts) {\n\t\t\tif (current.type !== \"directory\") {\n\t\t\t\tthrow new Error(`Not a directory: ${normalized}`);\n\t\t\t}\n\t\t\tconst child = findChild(current, part);\n\t\t\tif (!child) throw new Error(`No such file or directory: ${normalized}`);\n\t\t\tcurrent = child;\n\t\t}\n\t\treturn current;\n\t}\n\n\tprivate ensureDirectory(targetPath: string, mode: number): WebDirectoryNode {\n\t\tconst normalized = normalizePath(targetPath);\n\t\tif (normalized === \"/\") return this.root;\n\n\t\tconst parts = splitPath(normalized);\n\t\tlet current = this.root;\n\t\tfor (const part of parts) {\n\t\t\tconst existing = findChild(current, part);\n\t\t\tif (!existing) {\n\t\t\t\tconst created = makeDirectory(part, mode);\n\t\t\t\tsetChild(current, created);\n\t\t\t\tcurrent = created;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (existing.type !== \"directory\") {\n\t\t\t\tthrow new Error(`Cannot create directory '${normalized}': path is a file.`);\n\t\t\t}\n\t\t\tcurrent = existing;\n\t\t}\n\t\treturn current;\n\t}\n\n\tprivate removeNode(targetPath: string, recursive: boolean): void {\n\t\tconst normalized = normalizePath(targetPath);\n\t\tif (normalized === \"/\") throw new Error(\"Cannot remove root directory\");\n\t\tconst parent = this.getNode(dirname(normalized));\n\t\tif (parent.type !== \"directory\") throw new Error(`Not a directory: ${dirname(normalized)}`);\n\t\tconst name = basename(normalized);\n\t\tconst node = findChild(parent, name);\n\t\tif (!node) throw new Error(`No such file or directory: ${normalized}`);\n\t\tif (node.type === \"directory\" && node.children.length > 0 && !recursive) {\n\t\t\tthrow new Error(`Cannot remove '${normalized}': directory not empty.`);\n\t\t}\n\t\tremoveChild(parent, name);\n\t}\n\n\tprivate copyNode(node: WebNode): WebNode {\n\t\tif (node.type === \"file\") {\n\t\t\treturn {\n\t\t\t\t...node,\n\t\t\t\tcontentBase64: node.contentBase64,\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\t...node,\n\t\t\tchildren: node.children.map((child) => this.copyNode(child)),\n\t\t};\n\t}\n\n\tpublic async restoreMirror(): Promise<void> {\n\t\tconst snapshot = await this.readSnapshot();\n\t\tif (!snapshot) return;\n\t\tthis.root = this.deserializeNode(snapshot.root) as WebDirectoryNode;\n\t}\n\n\tpublic async flushMirror(): Promise<void> {\n\t\tawait this.writeSnapshot({ root: this.serializeNode(this.root) as WebDirectoryNode });\n\t}\n\n\tpublic exists(targetPath: string): boolean {\n\t\ttry {\n\t\t\tthis.getNode(targetPath);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tpublic list(targetPath: string): string[] {\n\t\tconst node = this.getNode(targetPath);\n\t\tif (node.type !== \"directory\") throw new Error(`Not a directory: ${targetPath}`);\n\t\treturn node.children.map((child) => child.name).sort((a, b) => a.localeCompare(b));\n\t}\n\n\tpublic stat(targetPath: string): {\n\t\ttype: \"file\" | \"directory\";\n\t\tmode: number;\n\t\tsize: number;\n\t\tname: string;\n\t} {\n\t\tconst node = this.getNode(targetPath);\n\t\tif (node.type === \"file\") {\n\t\t\treturn {\n\t\t\t\ttype: \"file\",\n\t\t\t\tmode: node.mode,\n\t\t\t\tsize: decodeBase64(node.contentBase64).byteLength,\n\t\t\t\tname: node.name,\n\t\t\t};\n\t\t}\n\t\treturn { type: \"directory\", mode: node.mode, size: 0, name: node.name };\n\t}\n\n\tpublic readFile(targetPath: string): string {\n\t\tconst node = this.getNode(targetPath);\n\t\tif (node.type !== \"file\") throw new Error(`Is a directory: ${targetPath}`);\n\t\treturn textDecoder.decode(decodeBase64(node.contentBase64));\n\t}\n\n\tpublic writeFile(\n\t\ttargetPath: string,\n\t\tcontent: string | Uint8Array,\n\t\tmode = 0o644,\n\t): void {\n\t\tconst normalized = normalizePath(targetPath);\n\t\tconst parent = this.ensureDirectory(dirname(normalized), 0o755);\n\t\tconst bytes = typeof content === \"string\" ? textEncoder.encode(content) : content;\n\t\tconst file = makeFile(basename(normalized), bytes, mode);\n\t\tsetChild(parent, file);\n\t}\n\n\tpublic mkdir(targetPath: string, mode = 0o755): void {\n\t\tthis.ensureDirectory(targetPath, mode);\n\t}\n\n\tpublic touch(targetPath: string): void {\n\t\tif (this.exists(targetPath)) return;\n\t\tthis.writeFile(targetPath, \"\");\n\t}\n\n\tpublic move(fromPath: string, toPath: string): void {\n\t\tconst source = this.getNode(fromPath);\n\t\tconst sourceParent = this.getNode(dirname(fromPath));\n\t\tconst destinationParent = this.ensureDirectory(dirname(toPath), 0o755);\n\t\tif (sourceParent.type !== \"directory\") throw new Error(`Not a directory: ${dirname(fromPath)}`);\n\t\tremoveChild(sourceParent, basename(fromPath));\n\t\tconst clone = cloneNode(source);\n\t\tclone.name = basename(toPath);\n\t\tsetChild(destinationParent, clone);\n\t}\n\n\tpublic copy(fromPath: string, toPath: string): void {\n\t\tconst source = this.getNode(fromPath);\n\t\tconst destinationParent = this.ensureDirectory(dirname(toPath), 0o755);\n\t\tconst clone = this.copyNode(source);\n\t\tclone.name = basename(toPath);\n\t\tsetChild(destinationParent, clone);\n\t}\n\n\tpublic remove(targetPath: string, options: { recursive?: boolean } = {}): void {\n\t\tthis.removeNode(targetPath, options.recursive ?? false);\n\t}\n\n\tpublic exportSnapshot(): WebVfsSnapshot {\n\t\treturn { root: this.serializeNode(this.root) as WebDirectoryNode };\n\t}\n\n\tpublic importSnapshot(snapshot: WebVfsSnapshot): void {\n\t\tthis.root = this.deserializeNode(snapshot.root) as WebDirectoryNode;\n\t}\n}\n\nclass WebShell {\n\treadonly hostname: string;\n\treadonly vfs: IndexedDbMirrorVfs;\n\treadonly env: ShellEnv;\n\tprivate cwd: string;\n\tprivate readonly commands = new Map<string, WebCommand>();\n\tprivate initialized = false;\n\n\tconstructor(hostname: string, options: WebShellOptions = {}) {\n\t\tthis.hostname = hostname;\n\t\tthis.cwd = options.cwd ?? \"/home/root\";\n\t\tthis.env = {\n\t\t\tvars: {\n\t\t\t\tPATH: \"/usr/bin:/bin\",\n\t\t\t\tHOME: \"/home/root\",\n\t\t\t\tUSER: \"root\",\n\t\t\t\tLOGNAME: \"root\",\n\t\t\t\tSHELL: \"/bin/sh\",\n\t\t\t\tHOSTNAME: hostname,\n\t\t\t\tPWD: this.cwd,\n\t\t\t},\n\t\t\tlastExitCode: 0,\n\t\t};\n\t\tthis.vfs = new IndexedDbMirrorVfs(options.vfs);\n\t\tthis.registerBuiltins();\n\t}\n\n\tprivate register(command: WebCommand): void {\n\t\tthis.commands.set(command.name.toLowerCase(), command);\n\t\tfor (const alias of command.aliases ?? []) {\n\t\t\tthis.commands.set(alias.toLowerCase(), command);\n\t\t}\n\t}\n\n\tprivate registerBuiltins(): void {\n\t\tthis.register({\n\t\t\tname: \"help\",\n\t\t\tdescription: \"List available web commands\",\n\t\t\tparams: [],\n\t\t\trun: () => ({ stdout: `${this.listCommands().join(\"\\n\")}\\n`, exitCode: 0 }),\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"pwd\",\n\t\t\tdescription: \"Print current directory\",\n\t\t\tparams: [],\n\t\t\trun: () => ({ stdout: `${this.cwd}\\n`, exitCode: 0 }),\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"cd\",\n\t\t\tdescription: \"Change current directory\",\n\t\t\tparams: [\"[dir]\"],\n\t\t\trun: ({ args }) => {\n\t\t\t\tconst target = args[0] ? normalizePath(args[0], this.cwd) : \"/home/root\";\n\t\t\t\tif (!this.vfs.exists(target) || this.vfs.stat(target).type !== \"directory\") {\n\t\t\t\t\treturn { stderr: `cd: no such file or directory: ${target}`, exitCode: 1 };\n\t\t\t\t}\n\t\t\t\tthis.cwd = target;\n\t\t\t\tthis.env.vars.PWD = target;\n\t\t\t\treturn { exitCode: 0, nextCwd: target };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"echo\",\n\t\t\tdescription: \"Display text\",\n\t\t\tparams: [\"[-n] [-e] [text...]\"],\n\t\t\trun: ({ args, stdin }) => {\n\t\t\t\tconst noNewline = args.includes(\"-n\");\n\t\t\t\tconst raw = args.filter((arg) => arg !== \"-n\" && arg !== \"-e\" && arg !== \"-E\");\n\t\t\t\tconst text = raw.length > 0 ? raw.join(\" \") : (stdin ?? \"\");\n\t\t\t\tconst expanded = expandSync(text, this.env.vars, this.env.lastExitCode, this.env.vars.HOME);\n\t\t\t\treturn { stdout: noNewline ? expanded : `${expanded}\\n`, exitCode: 0 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"env\",\n\t\t\tdescription: \"Print environment variables\",\n\t\t\tparams: [],\n\t\t\trun: () => ({ stdout: `${Object.entries(this.env.vars).map(([key, value]) => `${key}=${value}`).join(\"\\n\")}\\n`, exitCode: 0 }),\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"export\",\n\t\t\tdescription: \"Set environment variables\",\n\t\t\tparams: [\"KEY=VALUE...\"],\n\t\t\trun: ({ args }) => {\n\t\t\t\tfor (const arg of args) {\n\t\t\t\t\tconst eq = arg.indexOf(\"=\");\n\t\t\t\t\tif (eq === -1) continue;\n\t\t\t\t\tconst key = arg.slice(0, eq).trim();\n\t\t\t\t\tconst value = arg.slice(eq + 1);\n\t\t\t\t\tif (key) this.env.vars[key] = value;\n\t\t\t\t}\n\t\t\t\treturn { exitCode: 0 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"unset\",\n\t\t\tdescription: \"Unset environment variables\",\n\t\t\tparams: [\"NAME...\"],\n\t\t\trun: ({ args }) => {\n\t\t\t\tfor (const name of args) delete this.env.vars[name];\n\t\t\t\treturn { exitCode: 0 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"mkdir\",\n\t\t\tdescription: \"Create directories\",\n\t\t\tparams: [\"[-p] dir...\"],\n\t\t\trun: async ({ args }) => {\n\t\t\t\tconst paths = args.filter((arg) => arg !== \"-p\");\n\t\t\t\tfor (const target of paths) {\n\t\t\t\t\tthis.vfs.mkdir(normalizePath(target, this.cwd));\n\t\t\t\t}\n\t\t\t\tawait this.vfs.flushMirror();\n\t\t\t\treturn { exitCode: 0 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"touch\",\n\t\t\tdescription: \"Create files\",\n\t\t\tparams: [\"file...\"],\n\t\t\trun: async ({ args }) => {\n\t\t\t\tfor (const target of args) {\n\t\t\t\t\tthis.vfs.touch(normalizePath(target, this.cwd));\n\t\t\t\t}\n\t\t\t\tawait this.vfs.flushMirror();\n\t\t\t\treturn { exitCode: 0 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"rm\",\n\t\t\tdescription: \"Remove files or directories\",\n\t\t\tparams: [\"[-r] [-f] path...\"],\n\t\t\trun: async ({ args }) => {\n\t\t\t\tconst recursive = args.includes(\"-r\");\n\t\t\t\tconst targets = args.filter((arg) => arg !== \"-r\" && arg !== \"-f\");\n\t\t\t\tfor (const target of targets) {\n\t\t\t\t\tthis.vfs.remove(normalizePath(target, this.cwd), { recursive });\n\t\t\t\t}\n\t\t\t\tawait this.vfs.flushMirror();\n\t\t\t\treturn { exitCode: 0 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"cp\",\n\t\t\tdescription: \"Copy files or directories\",\n\t\t\tparams: [\"[-r] source destination\"],\n\t\t\trun: async ({ args }) => {\n\t\t\t\tconst recursive = args.includes(\"-r\");\n\t\t\t\tconst items = args.filter((arg) => arg !== \"-r\");\n\t\t\t\tif (items.length < 2) return { stderr: \"cp: missing destination file operand\", exitCode: 1 };\n\t\t\t\tconst dest = normalizePath(items.at(-1)!, this.cwd);\n\t\t\t\tfor (const source of items.slice(0, -1)) {\n\t\t\t\t\tconst sourcePath = normalizePath(source, this.cwd);\n\t\t\t\t\tif (!recursive && this.vfs.stat(sourcePath).type === \"directory\") {\n\t\t\t\t\t\treturn { stderr: `cp: -r not specified; omitting directory '${sourcePath}'`, exitCode: 1 };\n\t\t\t\t\t}\n\t\t\t\t\tthis.vfs.copy(sourcePath, dest);\n\t\t\t\t}\n\t\t\t\tawait this.vfs.flushMirror();\n\t\t\t\treturn { exitCode: 0 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"mv\",\n\t\t\tdescription: \"Move or rename files\",\n\t\t\tparams: [\"source destination\"],\n\t\t\trun: async ({ args }) => {\n\t\t\t\tif (args.length < 2) return { stderr: \"mv: missing destination file operand\", exitCode: 1 };\n\t\t\t\tconst source = normalizePath(args[0]!, this.cwd);\n\t\t\t\tconst destination = normalizePath(args[1]!, this.cwd);\n\t\t\t\tthis.vfs.move(source, destination);\n\t\t\t\tawait this.vfs.flushMirror();\n\t\t\t\treturn { exitCode: 0 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"cat\",\n\t\t\tdescription: \"Concatenate files\",\n\t\t\tparams: [\"[file...]\"],\n\t\t\trun: ({ args, stdin }) => {\n\t\t\t\tif (args.length === 0) return { stdout: stdin ?? \"\", exitCode: 0 };\n\t\t\t\tlet output = \"\";\n\t\t\t\tfor (const source of args) {\n\t\t\t\t\toutput += this.vfs.readFile(normalizePath(source, this.cwd));\n\t\t\t\t}\n\t\t\t\treturn { stdout: output, exitCode: 0 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"ls\",\n\t\t\tdescription: \"List files\",\n\t\t\tparams: [\"[path]\"],\n\t\t\trun: ({ args }) => {\n\t\t\t\tconst target = normalizePath(args[0] ?? \".\", this.cwd);\n\t\t\t\tconst entries = this.vfs.list(target);\n\t\t\t\treturn { stdout: `${entries.join(\" \")}\\n`, exitCode: 0 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"tee\",\n\t\t\tdescription: \"Read from stdin and write to files\",\n\t\t\tparams: [\"[-a] file...\"],\n\t\t\trun: async ({ args, stdin }) => {\n\t\t\t\tconst append = args.includes(\"-a\");\n\t\t\t\tconst targets = args.filter((arg) => arg !== \"-a\");\n\t\t\t\tconst content = stdin ?? \"\";\n\t\t\t\tfor (const target of targets) {\n\t\t\t\t\tconst normalized = normalizePath(target, this.cwd);\n\t\t\t\t\tif (append && this.vfs.exists(normalized)) {\n\t\t\t\t\t\tconst current = this.vfs.readFile(normalized);\n\t\t\t\t\t\tthis.vfs.writeFile(normalized, `${current}${content}`);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.vfs.writeFile(normalized, content);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tawait this.vfs.flushMirror();\n\t\t\t\treturn { stdout: content, exitCode: 0 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"curl\",\n\t\t\tdescription: \"Fetch a URL and optionally write to a file\",\n\t\t\tparams: [\"[-o file] URL\"],\n\t\t\trun: async ({ args }) => {\n\t\t\t\tconst outputIndex = args.indexOf(\"-o\");\n\t\t\t\tconst outputTarget = outputIndex !== -1 ? args[outputIndex + 1] : undefined;\n\t\t\t\tconst filtered = args.filter((arg, index) => arg !== \"-o\" && index !== outputIndex + 1);\n\t\t\t\tconst url = filtered.at(-1);\n\t\t\t\tif (!url) return { stderr: \"curl: missing URL\", exitCode: 2 };\n\t\t\t\tconst response = await fetch(url);\n\t\t\t\tconst body = await response.text();\n\t\t\t\tif (outputTarget) {\n\t\t\t\t\tthis.vfs.writeFile(normalizePath(outputTarget, this.cwd), body);\n\t\t\t\t\tawait this.vfs.flushMirror();\n\t\t\t\t\treturn { exitCode: response.ok ? 0 : 1 };\n\t\t\t\t}\n\t\t\t\treturn { stdout: body, exitCode: response.ok ? 0 : 1 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"wget\",\n\t\t\tdescription: \"Fetch a URL and optionally write to a file\",\n\t\t\tparams: [\"[-O file] URL\"],\n\t\t\trun: async ({ args }) => {\n\t\t\t\tconst outputIndex = args.indexOf(\"-O\");\n\t\t\t\tconst outputTarget = outputIndex !== -1 ? args[outputIndex + 1] : undefined;\n\t\t\t\tconst filtered = args.filter((arg, index) => arg !== \"-O\" && index !== outputIndex + 1);\n\t\t\t\tconst url = filtered.at(-1);\n\t\t\t\tif (!url) return { stderr: \"wget: missing URL\", exitCode: 2 };\n\t\t\t\tconst response = await fetch(url);\n\t\t\t\tconst body = await response.text();\n\t\t\t\tconst filename = outputTarget ?? basename(new URL(url).pathname || \"index.html\");\n\t\t\t\tthis.vfs.writeFile(normalizePath(filename, this.cwd), body);\n\t\t\t\tawait this.vfs.flushMirror();\n\t\t\t\treturn { exitCode: response.ok ? 0 : 1 };\n\t\t\t},\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"true\",\n\t\t\tdescription: \"Return success\",\n\t\t\tparams: [],\n\t\t\trun: () => ({ exitCode: 0 }),\n\t\t});\n\n\t\tthis.register({\n\t\t\tname: \"false\",\n\t\t\tdescription: \"Return failure\",\n\t\t\tparams: [],\n\t\t\trun: () => ({ exitCode: 1 }),\n\t\t});\n\t}\n\n\tprivate listCommands(): string[] {\n\t\tconst unique = new Map<string, WebCommand>();\n\t\tfor (const command of this.commands.values()) unique.set(command.name, command);\n\t\treturn Array.from(unique.values())\n\t\t\t.sort((a, b) => a.name.localeCompare(b.name))\n\t\t\t.map((command) => `${command.name}${command.params.length > 0 ? ` ${command.params.join(\" \")}` : \"\"}`);\n\t}\n\n\tprivate resolveCommand(name: string): WebCommand | undefined {\n\t\treturn this.commands.get(name.toLowerCase());\n\t}\n\n\tpublic async ensureInitialized(): Promise<void> {\n\t\tif (this.initialized) return;\n\t\tawait this.vfs.restoreMirror();\n\t\tif (!this.vfs.exists(\"/home\")) this.vfs.mkdir(\"/home\");\n\t\tif (!this.vfs.exists(\"/home/root\")) {\n\t\t\tthis.vfs.mkdir(\"/home/root\");\n\t\t\tthis.vfs.writeFile(\"/home/root/README.txt\", `Welcome to ${this.hostname}\\n`);\n\t\t}\n\t\tif (!this.vfs.exists(\"/tmp\")) this.vfs.mkdir(\"/tmp\");\n\t\tif (!this.vfs.exists(\"/etc\")) this.vfs.mkdir(\"/etc\");\n\t\tif (!this.vfs.exists(\"/etc/hostname\")) this.vfs.writeFile(\"/etc/hostname\", `${this.hostname}\\n`);\n\t\tif (!this.vfs.exists(\"/etc/hosts\")) {\n\t\t\tthis.vfs.writeFile(\"/etc/hosts\", \"127.0.0.1 localhost\\n::1 localhost\\n\");\n\t\t}\n\t\tthis.initialized = true;\n\t}\n\n\tpublic getCurrentWorkingDirectory(): string {\n\t\treturn this.cwd;\n\t}\n\n\tpublic async executeCommandLine(rawInput: string, persist = true): Promise<CommandResult> {\n\t\tawait this.ensureInitialized();\n\t\tconst trimmed = rawInput.trim();\n\t\tif (!trimmed) return { exitCode: 0 };\n\n\t\tconst expanded = await expandAsync(\n\t\t\ttrimmed,\n\t\t\tthis.env.vars,\n\t\t\tthis.env.lastExitCode,\n\t\t\t(subcommand) => this.executeCommandLine(subcommand, false).then((r) => r.stdout ?? \"\"),\n\t\t);\n\n\t\tconst script = parseScript(expanded);\n\t\tconst result = await this.executeStatements(script.statements);\n\t\tthis.env.lastExitCode = result.exitCode ?? 0;\n\t\tif (persist) await this.vfs.flushMirror();\n\t\treturn result;\n\t}\n\n\tprivate async executeStatements(statements: Statement[]): Promise<CommandResult> {\n\t\tlet last: CommandResult = { exitCode: 0 };\n\t\tlet index = 0;\n\n\t\twhile (index < statements.length) {\n\t\t\tconst stmt = statements[index]!;\n\t\t\tlast = await this.executePipeline(stmt.pipeline.commands as PipelineCommand[]);\n\t\t\tthis.env.lastExitCode = last.exitCode ?? 0;\n\t\t\tif (last.closeSession || last.switchUser) return last;\n\n\t\t\tconst op = stmt.op;\n\t\t\tif (!op || op === \";\") {\n\t\t\t\t// continue\n\t\t\t} else if (op === \"&&\") {\n\t\t\t\tif ((last.exitCode ?? 0) !== 0) {\n\t\t\t\t\twhile (index < statements.length && statements[index]?.op === \"&&\") index += 1;\n\t\t\t\t}\n\t\t\t} else if (op === \"||\") {\n\t\t\t\tif ((last.exitCode ?? 0) === 0) {\n\t\t\t\t\twhile (index < statements.length && statements[index]?.op === \"||\") index += 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tindex += 1;\n\t\t}\n\n\t\treturn last;\n\t}\n\n\tprivate async executePipeline(commands: PipelineCommand[]): Promise<CommandResult> {\n\t\tif (commands.length === 0) return { exitCode: 0 };\n\t\tif (commands.length === 1) {\n\t\t\treturn this.executeSingleCommandWithRedirections(commands[0]!);\n\t\t}\n\t\treturn this.executePipelineChain(commands);\n\t}\n\n\tprivate async executeSingleCommandWithRedirections(command: PipelineCommand): Promise<CommandResult> {\n\t\tlet stdin: string | undefined;\n\t\tif (command.inputFile) {\n\t\t\tconst inputPath = normalizePath(command.inputFile, this.cwd);\n\t\t\ttry {\n\t\t\t\tstdin = this.vfs.readFile(inputPath);\n\t\t\t} catch {\n\t\t\t\treturn { stderr: `${command.inputFile}: No such file or directory`, exitCode: 1 };\n\t\t\t}\n\t\t}\n\n\t\tconst result = await this.executeCommand(command.name, command.args, stdin);\n\n\t\tif (command.outputFile) {\n\t\t\tconst outputPath = normalizePath(command.outputFile, this.cwd);\n\t\t\tconst output = result.stdout ?? \"\";\n\t\t\tif (command.appendOutput && this.vfs.exists(outputPath)) {\n\t\t\t\tconst existing = this.vfs.readFile(outputPath);\n\t\t\t\tthis.vfs.writeFile(outputPath, `${existing}${output}`);\n\t\t\t} else {\n\t\t\t\tthis.vfs.writeFile(outputPath, output);\n\t\t\t}\n\t\t\treturn { ...result, stdout: \"\" };\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate async executePipelineChain(commands: PipelineCommand[]): Promise<CommandResult> {\n\t\tlet currentOutput = \"\";\n\t\tlet exitCode = 0;\n\n\t\tfor (let index = 0; index < commands.length; index += 1) {\n\t\t\tconst command = commands[index]!;\n\t\t\tif (index === 0 && command.inputFile) {\n\t\t\t\tconst inputPath = normalizePath(command.inputFile, this.cwd);\n\t\t\t\ttry {\n\t\t\t\t\tcurrentOutput = this.vfs.readFile(inputPath);\n\t\t\t\t} catch {\n\t\t\t\t\treturn { stderr: `${command.inputFile}: No such file or directory`, exitCode: 1 };\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst result = await this.executeCommand(command.name, command.args, currentOutput);\n\t\t\tcurrentOutput = result.stdout ?? \"\";\n\t\t\texitCode = result.exitCode ?? 0;\n\t\t}\n\n\t\treturn { stdout: currentOutput, exitCode };\n\t}\n\n\tprivate async executeCommand(\n\t\tname: string,\n\t\targs: string[],\n\t\tstdin?: string,\n\t): Promise<CommandResult> {\n\t\tconst command = this.resolveCommand(name);\n\t\tif (!command) {\n\t\t\treturn { stderr: `${name}: command not found`, exitCode: 127 };\n\t\t}\n\n\t\tconst expandedArgs = args.map((arg) => expandSync(arg, this.env.vars, this.env.lastExitCode, this.env.vars.HOME));\n\t\tconst context: WebCommandContext = {\n\t\t\targs: expandedArgs,\n\t\t\tstdin,\n\t\t\tcwd: this.cwd,\n\t\t\tenv: this.env,\n\t\t\trawInput: `${name} ${args.join(\" \")}`.trim(),\n\t\t\tshell: this,\n\t\t};\n\n\t\ttry {\n\t\t\tconst result = await command.run(context);\n\t\t\tif (result.nextCwd) {\n\t\t\t\tthis.cwd = result.nextCwd;\n\t\t\t\tthis.env.vars.PWD = result.nextCwd;\n\t\t\t}\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\treturn { stderr: message, exitCode: 1 };\n\t\t}\n\t}\n}\n\nexport { IndexedDbMirrorVfs, WebShell };\nexport function createWebShell(hostname = \"typescript-vm\", options: WebShellOptions = {}): WebShell {\n\treturn new WebShell(hostname, options);\n}\nexport type { WebCommand, WebCommandContext, WebShellOptions, WebVfsOptions };\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["tokenizeCommand", "input", "tokens", "current", "inQ", "qChar", "i", "ch", "next", "parseScript", "rawInput", "trimmed", "parseStatements", "e", "parseStatements", "input", "segments", "splitByLogicalOps", "statements", "seg", "stmt", "parsePipeline", "current", "depth", "inQ", "qChar", "flush", "op", "ch", "ch2", "splitByPipe", "parseCommandWithRedirections", "tokens", "tail", "token", "parts", "tokenizeCommand", "cmdParts", "inputFile", "outputFile", "appendOutput", "part", "evalArith", "expr", "env", "substituted", "_", "name", "val", "result", "outsideSingleQuotes", "input", "replacer", "parts", "i", "sqIdx", "closeIdx", "expandSync", "lastExit", "home", "homePath", "chunk", "s", "pre", "post", "def", "alt", "expandAsync", "runCmd", "inSingle", "ch", "depth", "j", "sub", "out", "textEncoder", "textDecoder", "encodeBase64", "bytes", "binary", "byte", "decodeBase64", "base64", "index", "normalizePath", "inputPath", "cwd", "parts", "stack", "part", "dirname", "normalized", "basename", "cloneNode", "node", "child", "makeDirectory", "name", "mode", "now", "makeFile", "content", "findChild", "directory", "setChild", "removeChild", "splitPath", "pathValue", "webGlobal", "promisifyRequest", "request", "resolve", "reject", "IndexedDbMirrorVfs", "options", "__publicField", "indexedDbFactory", "database", "result", "snapshot", "transaction", "store", "targetPath", "current", "existing", "created", "recursive", "parent", "a", "b", "file", "fromPath", "toPath", "source", "sourceParent", "destinationParent", "clone", "WebShell", "hostname", "command", "alias", "args", "target", "stdin", "noNewline", "raw", "arg", "text", "expanded", "expandSync", "key", "value", "eq", "paths", "targets", "items", "dest", "sourcePath", "destination", "output", "append", "outputIndex", "outputTarget", "url", "response", "body", "filename", "unique", "rawInput", "persist", "trimmed", "expandAsync", "subcommand", "r", "script", "parseScript", "statements", "last", "stmt", "op", "commands", "outputPath", "currentOutput", "exitCode", "context", "error", "createWebShell", "hostname", "options", "WebShell"]
|
|
7
7
|
}
|
package/bun.lock
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
"ssh2": "^1.17.0",
|
|
9
9
|
},
|
|
10
10
|
"devDependencies": {
|
|
11
|
-
"@biomejs/biome": "^2.4.
|
|
11
|
+
"@biomejs/biome": "^2.4.15",
|
|
12
12
|
"@types/bun": "^1.3.13",
|
|
13
|
-
"@types/node": "^25.6.
|
|
13
|
+
"@types/node": "^25.6.2",
|
|
14
14
|
"@types/ssh2": "^1.15.5",
|
|
15
15
|
"typescript": "^6.0.3",
|
|
16
16
|
},
|
|
@@ -20,27 +20,27 @@
|
|
|
20
20
|
},
|
|
21
21
|
},
|
|
22
22
|
"packages": {
|
|
23
|
-
"@biomejs/biome": ["@biomejs/biome@2.4.
|
|
23
|
+
"@biomejs/biome": ["@biomejs/biome@2.4.15", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.15", "@biomejs/cli-darwin-x64": "2.4.15", "@biomejs/cli-linux-arm64": "2.4.15", "@biomejs/cli-linux-arm64-musl": "2.4.15", "@biomejs/cli-linux-x64": "2.4.15", "@biomejs/cli-linux-x64-musl": "2.4.15", "@biomejs/cli-win32-arm64": "2.4.15", "@biomejs/cli-win32-x64": "2.4.15" }, "bin": { "biome": "bin/biome" } }, "sha512-j5VH3a/h/HXTKBM50MDMxRCzkeLv9S2XJcW2WgnZT1+xyisi+0bISrXR82gCX+8S9lvK0skEvHJRN+3Ktr2hlw=="],
|
|
24
24
|
|
|
25
|
-
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.
|
|
25
|
+
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-rF3PPqLq1yoST79zaQbDjVJwsuIeci/O+9bgNmC5QpgOqz6aqYuzA4abyAGx+mgyiDXn4A049xAN8gijbuR1Qg=="],
|
|
26
26
|
|
|
27
|
-
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.
|
|
27
|
+
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-/5KHXYMfSJs1fNXiX30xFtI8JcCFV6zaVVLxOa0M2sfqBKHkpQhRTv94yxQWxeTY2lzo2OuTlNvPC+hDQt2wcQ=="],
|
|
28
28
|
|
|
29
|
-
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.
|
|
29
|
+
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-owaAMZD/T4LrD0ELNCk0Km3qrRHuM0X6EAyVE1FSqGY0rbLoiDLrO4Us2tllm6cAeB2Ioa9C2C08NZPdr8+0Ug=="],
|
|
30
30
|
|
|
31
|
-
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.
|
|
31
|
+
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZPcxznxm0pogHBLZhYntyR3sR+MrZjqJIKEr7ZqVen0Rl+P/4upVmfYXjftizi9RoqZntg33fv/1fbdhbYXpEQ=="],
|
|
32
32
|
|
|
33
|
-
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.
|
|
33
|
+
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.15", "", { "os": "linux", "cpu": "x64" }, "sha512-0jj7THz12GbUOLmMibktK6DZjqz2zV64KFxyBtcFTKPiiOIY0a7vns1elpO1dERvxpsZ5ik0oFfz0oGwFde1+g=="],
|
|
34
34
|
|
|
35
|
-
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.
|
|
35
|
+
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.15", "", { "os": "linux", "cpu": "x64" }, "sha512-CNq/9W38SYSH023lfcQ4KKU8K0YX8T//FZUhcgtMMRABDojx5XsMV7jlweAvGSl389wJQB29Qo6Zb/a+jdvt+w=="],
|
|
36
36
|
|
|
37
|
-
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.
|
|
37
|
+
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-ouhkYdlhp/1GghEJPdWwD/Vi3gQ1nFxuSpMolWsbq3Lsq3QUR4jl6UdhhscdCugKU5vOEuMiJhvKj66O0OCq+w=="],
|
|
38
38
|
|
|
39
|
-
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.
|
|
39
|
+
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.15", "", { "os": "win32", "cpu": "x64" }, "sha512-zBrGq5mx5wwpnow4+2BxUvleDM+GNd4sLbPaMapsSLQLD0NGRCquqPBTgN+7XkUteHvj7M+BstuI8tmnV7+HgQ=="],
|
|
40
40
|
|
|
41
41
|
"@types/bun": ["@types/bun@1.3.13", "", { "dependencies": { "bun-types": "1.3.13" } }, "sha512-9fqXWk5YIHGGnUau9TEi+qdlTYDAnOj+xLCmSTwXfAIqXr2x4tytJb43E9uCvt09zJURKXwAtkoH4nLQfzeTXw=="],
|
|
42
42
|
|
|
43
|
-
"@types/node": ["@types/node@25.6.
|
|
43
|
+
"@types/node": ["@types/node@25.6.2", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-sokuT28dxf9JT5Kady1fsXOvI4HVpjZa95NKT5y9PNTIrs2AsobR4GFAA90ZG8M+nxVRLysCXsVj6eGC7Vbrlw=="],
|
|
44
44
|
|
|
45
45
|
"@types/ssh2": ["@types/ssh2@1.15.5", "", { "dependencies": { "@types/node": "^18.11.18" } }, "sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ=="],
|
|
46
46
|
|
|
@@ -68,6 +68,8 @@
|
|
|
68
68
|
|
|
69
69
|
"@types/ssh2/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="],
|
|
70
70
|
|
|
71
|
+
"bun-types/@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="],
|
|
72
|
+
|
|
71
73
|
"@types/ssh2/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
|
|
72
74
|
}
|
|
73
75
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/SSHClient/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAkBpD,qBAAa,SAAS;IAUpB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,QAAQ;IAVjB,OAAO,CAAC,UAAU,CAAO;IAEzB;;;;;OAKG;gBAEM,KAAK,EAAE,YAAY,EACnB,QAAQ,EAAE,MAAM;IAKzB;;;;;OAKG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/SSHClient/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAkBpD,qBAAa,SAAS;IAUpB,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,QAAQ;IAVjB,OAAO,CAAC,UAAU,CAAO;IAEzB;;;;;OAKG;gBAEM,KAAK,EAAE,YAAY,EACnB,QAAQ,EAAE,MAAM;IAKzB;;;;;OAKG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IA8BnD;;;;;OAKG;IACG,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAM/C;;;;OAIG;IACG,GAAG,IAAI,OAAO,CAAC,aAAa,CAAC;IAKnC;;;;;OAKG;IACG,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAS9C;;;;;OAKG;IACG,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAK/C;;;;;;OAMG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,OAAO,CAAC,aAAa,CAAC;IAMpE;;;;;OAKG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAKjD;;;;;;OAMG;IACG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,OAAO,CAAC,aAAa,CAAC;IAMjE;;;;;;OAMG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAkBtE;;;;;OAKG;IACG,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAkBpD;;;;OAIG;IACH,MAAM,IAAI,MAAM;IAKhB;;;;OAIG;IACH,WAAW,IAAI,MAAM;IAKrB;;;;;OAKG;IACG,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAMjD;;;;OAIG;IACG,MAAM,IAAI,OAAO,CAAC,aAAa,CAAC;IAKtC;;;;OAIG;IACG,QAAQ,IAAI,OAAO,CAAC,aAAa,CAAC;IAKxC;;;;OAIG;IACG,GAAG,IAAI,OAAO,CAAC,aAAa,CAAC;CAInC"}
|
package/dist/SSHClient/index.js
CHANGED
|
@@ -46,10 +46,12 @@ export class SshClient {
|
|
|
46
46
|
}
|
|
47
47
|
const result = runCommand(command, this.username, hostname, "exec", this.currentCwd, this.shell);
|
|
48
48
|
// Handle async results
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
const resolved = result instanceof Promise ? await result : result;
|
|
50
|
+
// Propagate cwd changes (cd, su, etc.)
|
|
51
|
+
if (resolved.nextCwd && (resolved.exitCode ?? 0) === 0) {
|
|
52
|
+
this.currentCwd = resolved.nextCwd;
|
|
51
53
|
}
|
|
52
|
-
return
|
|
54
|
+
return resolved;
|
|
53
55
|
}
|
|
54
56
|
/**
|
|
55
57
|
* Lists directory contents.
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import type { CommandMode, CommandResult, ShellEnv } from "../types/commands";
|
|
2
|
-
import type { Pipeline,
|
|
2
|
+
import type { Pipeline, Statement } from "../types/pipeline";
|
|
3
3
|
import type { VirtualShell } from "../VirtualShell";
|
|
4
|
-
export declare function executeScript(script: Script, authUser: string, hostname: string, mode: CommandMode, cwd: string, shell: VirtualShell, env: ShellEnv): Promise<CommandResult>;
|
|
5
|
-
/** Execute statements connected by &&/||/; */
|
|
6
4
|
export declare function executeStatements(statements: Statement[], authUser: string, hostname: string, mode: CommandMode, cwd: string, shell: VirtualShell, env: ShellEnv): Promise<CommandResult>;
|
|
7
5
|
export declare function executePipeline(pipeline: Pipeline, authUser: string, hostname: string, mode: CommandMode, cwd: string, shell: VirtualShell, env?: ShellEnv): Promise<CommandResult>;
|
|
8
6
|
//# sourceMappingURL=executor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/SSHMimic/executor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC9E,OAAO,KAAK,EACX,QAAQ,EAER,
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/SSHMimic/executor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC9E,OAAO,KAAK,EACX,QAAQ,EAER,SAAS,EACT,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAKpD,wBAAsB,iBAAiB,CACtC,UAAU,EAAE,SAAS,EAAE,EACvB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,WAAW,EACjB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE,QAAQ,GACX,OAAO,CAAC,aAAa,CAAC,CAsDxB;AAID,wBAAsB,eAAe,CACpC,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,WAAW,EACjB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,YAAY,EACnB,GAAG,CAAC,EAAE,QAAQ,GACZ,OAAO,CAAC,aAAa,CAAC,CA4BxB"}
|
|
@@ -1,33 +1,28 @@
|
|
|
1
1
|
import { runCommandDirect } from "../commands";
|
|
2
2
|
import { resolvePath } from "../commands/helpers";
|
|
3
3
|
// ── Script executor (handles &&/||/;) ────────────────────────────────────────
|
|
4
|
-
export async function executeScript(script, authUser, hostname, mode, cwd, shell, env) {
|
|
5
|
-
if (!script.isValid)
|
|
6
|
-
return { stderr: script.error || "Syntax error", exitCode: 1 };
|
|
7
|
-
let lastResult = { exitCode: 0 };
|
|
8
|
-
for (const stmt of script.statements) {
|
|
9
|
-
// Decide whether to run this statement based on previous op
|
|
10
|
-
lastResult = await executePipeline(stmt.pipeline, authUser, hostname, mode, cwd, shell, env);
|
|
11
|
-
env.lastExitCode = lastResult.exitCode ?? 0;
|
|
12
|
-
// Propagate session-control signals
|
|
13
|
-
if (lastResult.closeSession ||
|
|
14
|
-
lastResult.switchUser ||
|
|
15
|
-
lastResult.nextCwd) {
|
|
16
|
-
break;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return lastResult;
|
|
20
|
-
}
|
|
21
|
-
/** Execute statements connected by &&/||/; */
|
|
22
4
|
export async function executeStatements(statements, authUser, hostname, mode, cwd, shell, env) {
|
|
23
5
|
let last = { exitCode: 0 };
|
|
6
|
+
const accumulatedStdout = [];
|
|
7
|
+
let currentCwd = cwd; // track cwd changes from cd, su, etc.
|
|
24
8
|
let i = 0;
|
|
25
9
|
while (i < statements.length) {
|
|
26
10
|
const stmt = statements[i];
|
|
27
|
-
last = await executePipeline(stmt.pipeline, authUser, hostname, mode,
|
|
11
|
+
last = await executePipeline(stmt.pipeline, authUser, hostname, mode, currentCwd, shell, env);
|
|
28
12
|
env.lastExitCode = last.exitCode ?? 0;
|
|
29
|
-
|
|
30
|
-
|
|
13
|
+
// Propagate cwd changes (cd, su -l, etc.)
|
|
14
|
+
if (last.nextCwd && (last.exitCode ?? 0) === 0) {
|
|
15
|
+
currentCwd = last.nextCwd;
|
|
16
|
+
}
|
|
17
|
+
// Collect stdout from each statement (for echo a; echo b → "a\nb\n")
|
|
18
|
+
if (last.stdout)
|
|
19
|
+
accumulatedStdout.push(last.stdout);
|
|
20
|
+
if (last.closeSession || last.switchUser) {
|
|
21
|
+
return {
|
|
22
|
+
...last,
|
|
23
|
+
stdout: accumulatedStdout.join("") || last.stdout,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
31
26
|
const op = stmt.op;
|
|
32
27
|
if (!op || op === ";") {
|
|
33
28
|
// always run next
|
|
@@ -48,7 +43,10 @@ export async function executeStatements(statements, authUser, hostname, mode, cw
|
|
|
48
43
|
}
|
|
49
44
|
i++;
|
|
50
45
|
}
|
|
51
|
-
|
|
46
|
+
// Merge accumulated stdout (for "echo a; echo b" → "a\nb\n")
|
|
47
|
+
const merged = accumulatedStdout.join("");
|
|
48
|
+
// Preserve the deepest cwd change across the whole pipeline
|
|
49
|
+
return { ...last, stdout: merged || last.stdout, nextCwd: currentCwd !== cwd ? currentCwd : undefined };
|
|
52
50
|
}
|
|
53
51
|
// ── Pipeline executor ─────────────────────────────────────────────────────────
|
|
54
52
|
export async function executePipeline(pipeline, authUser, hostname, mode, cwd, shell, env) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/SSHMimic/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/SSHMimic/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AA+B/C,cAAM,QAAS,SAAQ,YAAY;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,KAAK,CAAe;IAE5B,mEAAmE;IACnE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,2DAA2D;IAC3D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqC;IAElE;;;;;;;;OAQG;gBACS,EACX,IAAI,EACJ,QAA0B,EAC1B,KAAkC,EAClC,eAAmB,EACnB,iBAA0B,GAC1B,EAAE;QACF,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,YAAY,CAAC;QACrB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC3B;IAYD,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,aAAa;IAcrB;;;;OAIG;IACU,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IA0LrC;;OAEG;IACI,IAAI,IAAI,IAAI;IAUnB;;;OAGG;IACI,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;CAGrC;AAED,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EAAE,QAAQ,EAAE,CAAC"}
|