typescript-virtual-container 1.5.2 → 1.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -23
- package/dist/.tsbuildinfo +1 -0
- package/dist/SSHMimic/executor.js +23 -5
- package/dist/commands/basename.d.ts +13 -0
- package/dist/commands/basename.js +45 -0
- package/dist/commands/file.d.ts +8 -0
- package/dist/commands/file.js +57 -0
- package/dist/commands/fun.d.ts +32 -0
- package/dist/commands/fun.js +172 -0
- package/dist/commands/ifconfig.d.ts +7 -0
- package/dist/commands/ifconfig.js +52 -0
- package/dist/commands/last.d.ts +13 -0
- package/dist/commands/last.js +68 -0
- package/dist/commands/manuals-bundle.js +598 -6
- package/dist/commands/registry.js +24 -2
- package/dist/commands/runtime.js +159 -106
- package/dist/commands/sh.js +5 -0
- package/dist/commands/tput.d.ts +13 -0
- package/dist/commands/tput.js +76 -0
- package/dist/commands/w.d.ts +7 -0
- package/dist/commands/w.js +38 -0
- package/dist/utils/expand.d.ts +12 -0
- package/dist/utils/expand.js +84 -0
- package/package.json +9 -3
- package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -50
- package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -31
- package/.github/dependabot.yml +0 -27
- package/.github/pull_request_template.md +0 -21
- package/.github/workflows/create-pull-request.yml +0 -85
- package/.github/workflows/publish.yml +0 -25
- package/.github/workflows/test-battery.yml +0 -102
- package/.vscode/settings.json +0 -20
- package/CODE_OF_CONDUCT.md +0 -39
- package/CONTRIBUTING.md +0 -59
- package/HONEYPOT.md +0 -358
- package/SECURITY.md +0 -33
- package/benchmark-results.txt +0 -40
- package/benchmark-virtualshell.ts +0 -88
- package/biome.json +0 -37
- package/build.js +0 -22
- package/builds/fortune-nyx-v1.5.1-directbash-k6.1.0.mjs +0 -1768
- package/builds/fortune-nyx-v1.5.1-ssh-nosftp.js +0 -1768
- package/builds/fortune-nyx-v1.5.1-ssh.cjs +0 -1769
- package/builds/fortune-nyx-v1.5.1-web.min.js +0 -17022
- package/bun.lock +0 -244
- package/docs/.nojekyll +0 -1
- package/docs/app.js +0 -1755
- package/docs/assets/hierarchy.js +0 -1
- package/docs/assets/highlight.css +0 -162
- package/docs/assets/icons.js +0 -18
- package/docs/assets/icons.svg +0 -1
- package/docs/assets/main.js +0 -60
- package/docs/assets/navigation.js +0 -1
- package/docs/assets/search.js +0 -1
- package/docs/assets/style.css +0 -1633
- package/docs/classes/HoneyPot.html +0 -31
- package/docs/classes/IdleManager.html +0 -162
- package/docs/classes/SshClient.html +0 -66
- package/docs/classes/VirtualFileSystem.html +0 -279
- package/docs/classes/VirtualPackageManager.html +0 -63
- package/docs/classes/VirtualSftpServer.html +0 -169
- package/docs/classes/VirtualShell.html +0 -285
- package/docs/classes/VirtualSshServer.html +0 -182
- package/docs/classes/VirtualUserManager.html +0 -276
- package/docs/demo.html +0 -82
- package/docs/functions/assertDiff.html +0 -6
- package/docs/functions/diffSnapshots.html +0 -7
- package/docs/functions/formatDiff.html +0 -6
- package/docs/functions/getArg.html +0 -13
- package/docs/functions/getFlag.html +0 -15
- package/docs/functions/ifFlag.html +0 -11
- package/docs/hierarchy.html +0 -1
- package/docs/index.html +0 -1869
- package/docs/interfaces/AuditLogEntry.html +0 -6
- package/docs/interfaces/CommandContext.html +0 -22
- package/docs/interfaces/CommandResult.html +0 -26
- package/docs/interfaces/ExecStream.html +0 -11
- package/docs/interfaces/HoneyPotStats.html +0 -16
- package/docs/interfaces/IdleManagerOptions.html +0 -7
- package/docs/interfaces/InstalledPackage.html +0 -20
- package/docs/interfaces/NanoEditorSession.html +0 -8
- package/docs/interfaces/PackageDefinition.html +0 -30
- package/docs/interfaces/PackageFile.html +0 -8
- package/docs/interfaces/PasswordChallenge.html +0 -16
- package/docs/interfaces/RemoveOptions.html +0 -4
- package/docs/interfaces/ShellEnv.html +0 -6
- package/docs/interfaces/ShellModule.html +0 -14
- package/docs/interfaces/ShellProperties.html +0 -14
- package/docs/interfaces/ShellStream.html +0 -11
- package/docs/interfaces/SudoChallenge.html +0 -24
- package/docs/interfaces/VfsBaseNode.html +0 -12
- package/docs/interfaces/VfsDiff.html +0 -10
- package/docs/interfaces/VfsDiffEntry.html +0 -6
- package/docs/interfaces/VfsDiffModified.html +0 -10
- package/docs/interfaces/VfsDirectoryNode.html +0 -15
- package/docs/interfaces/VfsFileNode.html +0 -17
- package/docs/interfaces/VfsOptions.html +0 -26
- package/docs/interfaces/VfsSnapshot.html +0 -3
- package/docs/interfaces/VfsSnapshotBaseNode.html +0 -8
- package/docs/interfaces/VfsSnapshotDirectoryNode.html +0 -10
- package/docs/interfaces/VfsSnapshotFileNode.html +0 -12
- package/docs/interfaces/VirtualActiveSession.html +0 -12
- package/docs/interfaces/VirtualSftpServerOptions.html +0 -7
- package/docs/interfaces/VirtualShellVfsLike.html +0 -15
- package/docs/interfaces/VirtualShellVfsOptions.html +0 -3
- package/docs/interfaces/WriteFileOptions.html +0 -6
- package/docs/media/LICENSE +0 -21
- package/docs/modules.html +0 -1
- package/docs/types/ArgParseOptions.html +0 -4
- package/docs/types/CommandMode.html +0 -2
- package/docs/types/CommandOutcome.html +0 -2
- package/docs/types/IdleState.html +0 -1
- package/docs/types/VfsNodeStats.html +0 -2
- package/docs/types/VfsNodeType.html +0 -2
- package/docs/types/VfsPersistenceMode.html +0 -5
- package/docs/types/VfsSnapshotNode.html +0 -2
- package/examples/README.md +0 -288
- package/examples/app.js +0 -1755
- package/examples/app.ts +0 -299
- package/examples/build.js +0 -27
- package/examples/demo.html +0 -33
- package/examples/honeypot-audit.ts +0 -180
- package/examples/honeypot-export.ts +0 -253
- package/examples/honeypot-quickstart.ts +0 -110
- package/examples/index.html +0 -82
- package/examples/server.js +0 -55
- package/polyfills/buffer.js +0 -117
- package/polyfills/node_child_process/index.js +0 -2
- package/polyfills/node_crypto/index.js +0 -167
- package/polyfills/node_events/index.js +0 -9
- package/polyfills/node_fs/index.js +0 -202
- package/polyfills/node_fs/promises.js +0 -4
- package/polyfills/node_os/index.js +0 -9
- package/polyfills/node_path/index.js +0 -28
- package/polyfills/node_vm/index.js +0 -7
- package/polyfills/node_zlib/index.js +0 -3
- package/polyfills/process.js +0 -14
- package/polyfills/ssh2/index.js +0 -75
- package/scripts/build-all.mjs +0 -226
- package/scripts/build-names.mjs +0 -43
- package/scripts/generate-manuals-bundle.mjs +0 -49
- package/scripts/postinstall.js +0 -42
- package/scripts/publish-package.sh +0 -70
- package/src/Honeypot/index.ts +0 -457
- package/src/SSHClient/index.ts +0 -270
- package/src/SSHMimic/exec.ts +0 -49
- package/src/SSHMimic/executor.ts +0 -251
- package/src/SSHMimic/hostKey.ts +0 -21
- package/src/SSHMimic/index.ts +0 -337
- package/src/SSHMimic/loginBanner.ts +0 -36
- package/src/SSHMimic/loginFormat.ts +0 -10
- package/src/SSHMimic/prompt.ts +0 -14
- package/src/SSHMimic/sftp.ts +0 -883
- package/src/VirtualFileSystem/binaryPack.ts +0 -258
- package/src/VirtualFileSystem/index.ts +0 -1193
- package/src/VirtualFileSystem/internalTypes.ts +0 -43
- package/src/VirtualFileSystem/journal.ts +0 -171
- package/src/VirtualFileSystem/path.ts +0 -74
- package/src/VirtualPackageManager/index.ts +0 -1006
- package/src/VirtualShell/idleManager.ts +0 -137
- package/src/VirtualShell/index.ts +0 -475
- package/src/VirtualShell/shell.ts +0 -700
- package/src/VirtualShell/shellParser.ts +0 -285
- package/src/VirtualUserManager/index.ts +0 -758
- package/src/bun.d.ts +0 -1
- package/src/commands/adduser.ts +0 -103
- package/src/commands/alias.ts +0 -69
- package/src/commands/apt.ts +0 -233
- package/src/commands/awk.ts +0 -168
- package/src/commands/base64.ts +0 -29
- package/src/commands/cat.ts +0 -52
- package/src/commands/cd.ts +0 -25
- package/src/commands/chmod.ts +0 -85
- package/src/commands/clear.ts +0 -15
- package/src/commands/command-helpers.ts +0 -286
- package/src/commands/cp.ts +0 -83
- package/src/commands/curl.ts +0 -147
- package/src/commands/cut.ts +0 -36
- package/src/commands/date.ts +0 -30
- package/src/commands/declare.ts +0 -49
- package/src/commands/deluser.ts +0 -98
- package/src/commands/df.ts +0 -23
- package/src/commands/diff.ts +0 -43
- package/src/commands/dpkg.ts +0 -180
- package/src/commands/du.ts +0 -56
- package/src/commands/echo.ts +0 -58
- package/src/commands/env.ts +0 -23
- package/src/commands/exit.ts +0 -18
- package/src/commands/export.ts +0 -34
- package/src/commands/find.ts +0 -68
- package/src/commands/free.ts +0 -47
- package/src/commands/grep.ts +0 -116
- package/src/commands/groups.ts +0 -19
- package/src/commands/gzip.ts +0 -88
- package/src/commands/head.ts +0 -52
- package/src/commands/help.ts +0 -152
- package/src/commands/helpers.ts +0 -234
- package/src/commands/history.ts +0 -34
- package/src/commands/hostname.ts +0 -14
- package/src/commands/htop.ts +0 -20
- package/src/commands/id.ts +0 -19
- package/src/commands/index.ts +0 -9
- package/src/commands/kill.ts +0 -19
- package/src/commands/ln.ts +0 -71
- package/src/commands/ls.ts +0 -243
- package/src/commands/lsb-release.ts +0 -63
- package/src/commands/man.ts +0 -31
- package/src/commands/manuals/adduser.txt +0 -11
- package/src/commands/manuals/apt-cache.txt +0 -12
- package/src/commands/manuals/apt.txt +0 -20
- package/src/commands/manuals/awk.txt +0 -13
- package/src/commands/manuals/cat.txt +0 -14
- package/src/commands/manuals/cd.txt +0 -16
- package/src/commands/manuals/chmod.txt +0 -16
- package/src/commands/manuals/clear.txt +0 -10
- package/src/commands/manuals/cp.txt +0 -10
- package/src/commands/manuals/curl.txt +0 -20
- package/src/commands/manuals/date.txt +0 -14
- package/src/commands/manuals/declare.txt +0 -12
- package/src/commands/manuals/deluser.txt +0 -10
- package/src/commands/manuals/df.txt +0 -10
- package/src/commands/manuals/dpkg-query.txt +0 -11
- package/src/commands/manuals/dpkg.txt +0 -14
- package/src/commands/manuals/du.txt +0 -11
- package/src/commands/manuals/echo.txt +0 -11
- package/src/commands/manuals/false.txt +0 -10
- package/src/commands/manuals/find.txt +0 -11
- package/src/commands/manuals/free.txt +0 -12
- package/src/commands/manuals/grep.txt +0 -13
- package/src/commands/manuals/groups.txt +0 -10
- package/src/commands/manuals/gzip.txt +0 -11
- package/src/commands/manuals/head.txt +0 -10
- package/src/commands/manuals/help.txt +0 -11
- package/src/commands/manuals/history.txt +0 -11
- package/src/commands/manuals/hostname.txt +0 -10
- package/src/commands/manuals/id.txt +0 -10
- package/src/commands/manuals/kill.txt +0 -13
- package/src/commands/manuals/ls.txt +0 -20
- package/src/commands/manuals/lsb_release.txt +0 -14
- package/src/commands/manuals/mkdir.txt +0 -10
- package/src/commands/manuals/mv.txt +0 -10
- package/src/commands/manuals/nano.txt +0 -11
- package/src/commands/manuals/neofetch.txt +0 -10
- package/src/commands/manuals/node.txt +0 -13
- package/src/commands/manuals/npm.txt +0 -13
- package/src/commands/manuals/npx.txt +0 -13
- package/src/commands/manuals/passwd.txt +0 -11
- package/src/commands/manuals/ping.txt +0 -10
- package/src/commands/manuals/printf.txt +0 -11
- package/src/commands/manuals/ps.txt +0 -10
- package/src/commands/manuals/pwd.txt +0 -10
- package/src/commands/manuals/python3.txt +0 -13
- package/src/commands/manuals/readlink.txt +0 -10
- package/src/commands/manuals/return.txt +0 -10
- package/src/commands/manuals/rm.txt +0 -10
- package/src/commands/manuals/sed.txt +0 -11
- package/src/commands/manuals/set.txt +0 -11
- package/src/commands/manuals/shift.txt +0 -10
- package/src/commands/manuals/sleep.txt +0 -10
- package/src/commands/manuals/sort.txt +0 -12
- package/src/commands/manuals/source.txt +0 -11
- package/src/commands/manuals/ssh.txt +0 -11
- package/src/commands/manuals/stat.txt +0 -10
- package/src/commands/manuals/su.txt +0 -13
- package/src/commands/manuals/sudo.txt +0 -11
- package/src/commands/manuals/tail.txt +0 -10
- package/src/commands/manuals/tar.txt +0 -19
- package/src/commands/manuals/tee.txt +0 -10
- package/src/commands/manuals/test.txt +0 -11
- package/src/commands/manuals/touch.txt +0 -11
- package/src/commands/manuals/tr.txt +0 -10
- package/src/commands/manuals/trap.txt +0 -10
- package/src/commands/manuals/true.txt +0 -10
- package/src/commands/manuals/type.txt +0 -10
- package/src/commands/manuals/uname.txt +0 -12
- package/src/commands/manuals/uniq.txt +0 -12
- package/src/commands/manuals/unset.txt +0 -10
- package/src/commands/manuals/uptime.txt +0 -11
- package/src/commands/manuals/wc.txt +0 -12
- package/src/commands/manuals/wget.txt +0 -12
- package/src/commands/manuals/which.txt +0 -10
- package/src/commands/manuals/whoami.txt +0 -10
- package/src/commands/manuals/xargs.txt +0 -10
- package/src/commands/manuals-bundle.ts +0 -898
- package/src/commands/mkdir.ts +0 -31
- package/src/commands/mv.ts +0 -50
- package/src/commands/nano.ts +0 -38
- package/src/commands/neofetch.ts +0 -53
- package/src/commands/node.ts +0 -341
- package/src/commands/npm.ts +0 -132
- package/src/commands/passwd.ts +0 -50
- package/src/commands/ping.ts +0 -32
- package/src/commands/printf.ts +0 -129
- package/src/commands/ps.ts +0 -58
- package/src/commands/pwd.ts +0 -9
- package/src/commands/python.ts +0 -2229
- package/src/commands/read.ts +0 -46
- package/src/commands/registry.ts +0 -249
- package/src/commands/rm.ts +0 -42
- package/src/commands/runtime.ts +0 -378
- package/src/commands/sed.ts +0 -68
- package/src/commands/seq.ts +0 -43
- package/src/commands/set.ts +0 -29
- package/src/commands/sh.ts +0 -467
- package/src/commands/shift.ts +0 -63
- package/src/commands/sleep.ts +0 -20
- package/src/commands/sort.ts +0 -46
- package/src/commands/source.ts +0 -52
- package/src/commands/stat.ts +0 -61
- package/src/commands/su.ts +0 -72
- package/src/commands/sudo.ts +0 -76
- package/src/commands/tail.ts +0 -53
- package/src/commands/tar.ts +0 -102
- package/src/commands/tee.ts +0 -36
- package/src/commands/test.ts +0 -137
- package/src/commands/touch.ts +0 -28
- package/src/commands/tr.ts +0 -70
- package/src/commands/tree.ts +0 -20
- package/src/commands/true.ts +0 -27
- package/src/commands/type.ts +0 -48
- package/src/commands/uname.ts +0 -29
- package/src/commands/uniq.ts +0 -39
- package/src/commands/unset.ts +0 -17
- package/src/commands/uptime.ts +0 -54
- package/src/commands/wc.ts +0 -55
- package/src/commands/wget.ts +0 -148
- package/src/commands/which.ts +0 -37
- package/src/commands/who.ts +0 -25
- package/src/commands/whoami.ts +0 -14
- package/src/commands/xargs.ts +0 -31
- package/src/index.ts +0 -67
- package/src/modules/linuxRootfs.ts +0 -1961
- package/src/modules/neofetch.ts +0 -358
- package/src/modules/shellInteractive.ts +0 -57
- package/src/modules/shellRuntime.ts +0 -76
- package/src/self-standalone.ts +0 -542
- package/src/standalone-wo-sftp.ts +0 -38
- package/src/standalone.ts +0 -72
- package/src/types/commands.ts +0 -146
- package/src/types/pipeline.ts +0 -52
- package/src/types/streams.ts +0 -32
- package/src/types/tar-stream.d.ts +0 -38
- package/src/types/vfs.ts +0 -98
- package/src/utils/expand.ts +0 -491
- package/src/utils/perfLogger.ts +0 -72
- package/src/utils/tokenize.ts +0 -98
- package/src/utils/vfsDiff.ts +0 -275
- package/tests/command-helpers.test.ts +0 -116
- package/tests/commands-admin-net.test.ts +0 -441
- package/tests/commands-advanced.test.ts +0 -456
- package/tests/commands-core.test.ts +0 -562
- package/tests/commands-missing.test.ts +0 -570
- package/tests/commands-specific-units.test.ts +0 -327
- package/tests/commands-text-sys.test.ts +0 -445
- package/tests/expand.test.ts +0 -170
- package/tests/helpers.test.ts +0 -97
- package/tests/new-features.test.ts +0 -1036
- package/tests/parser-executor.test.ts +0 -37
- package/tests/sftp.test.ts +0 -323
- package/tests/ssh-exec.test.ts +0 -45
- package/tests/test-helper.ts +0 -79
- package/tests/users.test.ts +0 -86
- package/tsconfig.json +0 -49
- package/typedoc.json +0 -47
|
@@ -3,6 +3,7 @@ import { aliasCommand, unaliasCommand } from "./alias";
|
|
|
3
3
|
import { aptCacheCommand, aptCommand } from "./apt";
|
|
4
4
|
import { awkCommand } from "./awk";
|
|
5
5
|
import { base64Command } from "./base64";
|
|
6
|
+
import { basenameCommand, dirnameCommand } from "./basename";
|
|
6
7
|
import { catCommand } from "./cat";
|
|
7
8
|
import { cdCommand } from "./cd";
|
|
8
9
|
import { chmodCommand } from "./chmod";
|
|
@@ -21,8 +22,10 @@ import { echoCommand } from "./echo";
|
|
|
21
22
|
import { envCommand } from "./env";
|
|
22
23
|
import { exitCommand } from "./exit";
|
|
23
24
|
import { exportCommand } from "./export";
|
|
25
|
+
import { fileCommand } from "./file";
|
|
24
26
|
import { findCommand } from "./find";
|
|
25
27
|
import { freeCommand } from "./free";
|
|
28
|
+
import { cmatrixCommand, cowsayCommand, cowthinkCommand, fortuneCommand, slCommand, yesCommand } from "./fun";
|
|
26
29
|
import { grepCommand } from "./grep";
|
|
27
30
|
import { groupsCommand } from "./groups";
|
|
28
31
|
import { gunzipCommand, gzipCommand } from "./gzip";
|
|
@@ -32,10 +35,10 @@ import { historyCommand } from "./history";
|
|
|
32
35
|
import { hostnameCommand } from "./hostname";
|
|
33
36
|
import { htopCommand } from "./htop";
|
|
34
37
|
import { idCommand } from "./id";
|
|
38
|
+
import { ifconfigCommand } from "./ifconfig";
|
|
35
39
|
import { killCommand } from "./kill";
|
|
40
|
+
import { dmesgCommand, lastCommand } from "./last";
|
|
36
41
|
import { lnCommand, readlinkCommand } from "./ln";
|
|
37
|
-
import { seqCommand } from "./seq";
|
|
38
|
-
import { statCommand } from "./stat";
|
|
39
42
|
import { lsCommand } from "./ls";
|
|
40
43
|
import { lsbReleaseCommand } from "./lsb-release";
|
|
41
44
|
import { manCommand } from "./man";
|
|
@@ -54,12 +57,14 @@ import { python3Command } from "./python";
|
|
|
54
57
|
import { readCommand } from "./read";
|
|
55
58
|
import { rmCommand } from "./rm";
|
|
56
59
|
import { sedCommand } from "./sed";
|
|
60
|
+
import { seqCommand } from "./seq";
|
|
57
61
|
import { setCommand } from "./set";
|
|
58
62
|
import { shCommand } from "./sh";
|
|
59
63
|
import { returnCommand, shiftCommand, trapCommand } from "./shift";
|
|
60
64
|
import { sleepCommand } from "./sleep";
|
|
61
65
|
import { sortCommand } from "./sort";
|
|
62
66
|
import { sourceCommand } from "./source";
|
|
67
|
+
import { statCommand } from "./stat";
|
|
63
68
|
import { suCommand } from "./su";
|
|
64
69
|
import { sudoCommand } from "./sudo";
|
|
65
70
|
import { tailCommand } from "./tail";
|
|
@@ -67,6 +72,7 @@ import { tarCommand } from "./tar";
|
|
|
67
72
|
import { teeCommand } from "./tee";
|
|
68
73
|
import { testCommand } from "./test";
|
|
69
74
|
import { touchCommand } from "./touch";
|
|
75
|
+
import { sttyCommand, tputCommand } from "./tput";
|
|
70
76
|
import { trCommand } from "./tr";
|
|
71
77
|
import { treeCommand } from "./tree";
|
|
72
78
|
import { falseCommand, trueCommand } from "./true";
|
|
@@ -81,6 +87,7 @@ import { whichCommand } from "./which";
|
|
|
81
87
|
import { whoCommand } from "./who";
|
|
82
88
|
import { whoamiCommand } from "./whoami";
|
|
83
89
|
import { xargsCommand } from "./xargs";
|
|
90
|
+
import { wCommand } from "./w";
|
|
84
91
|
const BASE_COMMANDS = [
|
|
85
92
|
// Navigation
|
|
86
93
|
pwdCommand,
|
|
@@ -144,6 +151,21 @@ const BASE_COMMANDS = [
|
|
|
144
151
|
exitCommand,
|
|
145
152
|
// Editors
|
|
146
153
|
nanoCommand,
|
|
154
|
+
wCommand,
|
|
155
|
+
basenameCommand,
|
|
156
|
+
dirnameCommand,
|
|
157
|
+
fileCommand,
|
|
158
|
+
tputCommand,
|
|
159
|
+
sttyCommand,
|
|
160
|
+
lastCommand,
|
|
161
|
+
dmesgCommand,
|
|
162
|
+
ifconfigCommand,
|
|
163
|
+
yesCommand,
|
|
164
|
+
fortuneCommand,
|
|
165
|
+
cowsayCommand,
|
|
166
|
+
cowthinkCommand,
|
|
167
|
+
cmatrixCommand,
|
|
168
|
+
slCommand,
|
|
147
169
|
htopCommand,
|
|
148
170
|
// Network
|
|
149
171
|
curlCommand,
|
package/dist/commands/runtime.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** biome-ignore-all lint/style/useNamingConvention: ENV VARIABLES */
|
|
2
2
|
import { executeStatements } from "../SSHMimic/executor";
|
|
3
3
|
import { parseScript } from "../VirtualShell/shellParser";
|
|
4
|
-
import { expandAsync, expandBraces } from "../utils/expand";
|
|
4
|
+
import { expandAsync, expandBraces, expandGlob } from "../utils/expand";
|
|
5
5
|
import { tokenizeCommand } from "../utils/tokenize";
|
|
6
6
|
import { resolveModule } from "./registry";
|
|
7
7
|
/** Returns the home directory path for a given user. Root lives at /root. */
|
|
@@ -61,7 +61,25 @@ function resolveVfsBinary(name, env, shell, authUser) {
|
|
|
61
61
|
}
|
|
62
62
|
return null;
|
|
63
63
|
}
|
|
64
|
+
const MAX_CALL_DEPTH = 8;
|
|
65
|
+
let _callDepth = 0;
|
|
64
66
|
export async function runCommandDirect(name, args, authUser, hostname, mode, cwd, shell, stdin, env) {
|
|
67
|
+
// Anti-loop guard: track call depth via env to avoid infinite recursion
|
|
68
|
+
_callDepth++;
|
|
69
|
+
// console.debug(`[depth=${_callDepth}] runCommandDirect: ${name}`);
|
|
70
|
+
if (_callDepth > MAX_CALL_DEPTH) {
|
|
71
|
+
_callDepth--;
|
|
72
|
+
// console.debug(`[LOOP DETECTED] runCommandDirect blocked: ${name}`);
|
|
73
|
+
return { stderr: `${name}: maximum call depth (${MAX_CALL_DEPTH}) exceeded`, exitCode: 126 };
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
return await _runCommandDirectInner(name, args, authUser, hostname, mode, cwd, shell, stdin, env);
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
_callDepth--;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function _runCommandDirectInner(name, args, authUser, hostname, mode, cwd, shell, stdin, env) {
|
|
65
83
|
const assignRe = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/;
|
|
66
84
|
const invocation = [name, ...args];
|
|
67
85
|
let assignCount = 0;
|
|
@@ -118,6 +136,8 @@ export async function runCommandDirect(name, args, authUser, hostname, mode, cwd
|
|
|
118
136
|
env,
|
|
119
137
|
});
|
|
120
138
|
}
|
|
139
|
+
// builtin not found — stop here, don't fall through to sh -c (avoids infinite loop)
|
|
140
|
+
return { stderr: `${name}: exec builtin '${builtinMatch[1]}' not found`, exitCode: 127 };
|
|
121
141
|
}
|
|
122
142
|
const shMod = resolveModule("sh");
|
|
123
143
|
if (shMod) {
|
|
@@ -163,84 +183,129 @@ export async function runCommand(rawInput, authUser, hostname, mode, cwd, shell,
|
|
|
163
183
|
if (trimmed.length === 0)
|
|
164
184
|
return { exitCode: 0 };
|
|
165
185
|
const shellEnv = env ?? makeDefaultEnv(authUser, hostname);
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
: trimmed;
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
activeSessions: shell.users.listActiveSessions(),
|
|
193
|
-
rawInput: aliasExpanded,
|
|
194
|
-
mode,
|
|
195
|
-
args: ["-c", aliasExpanded],
|
|
196
|
-
stdin: undefined,
|
|
197
|
-
cwd,
|
|
198
|
-
shell,
|
|
199
|
-
env: shellEnv,
|
|
200
|
-
});
|
|
186
|
+
// Anti-loop guard: check depth here too — catches sh -c recursive calls
|
|
187
|
+
_callDepth++;
|
|
188
|
+
// console.debug(`[depth=${_callDepth}] runCommand: ${trimmed.slice(0, 60)}`);
|
|
189
|
+
if (_callDepth > MAX_CALL_DEPTH) {
|
|
190
|
+
_callDepth--;
|
|
191
|
+
// console.debug(`[LOOP DETECTED] runCommand blocked: ${trimmed.slice(0, 60)}`);
|
|
192
|
+
return { stderr: `${trimmed.split(" ")[0]}: maximum call depth (${MAX_CALL_DEPTH}) exceeded`, exitCode: 126 };
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
// History expansion: !! and !n
|
|
196
|
+
if (trimmed === '!!' || /^!-?\d+$/.test(trimmed) || trimmed.startsWith('!! ')) {
|
|
197
|
+
const histPath = `${shellEnv.vars.HOME ?? `/home/${authUser}`}/.bash_history`;
|
|
198
|
+
if (shell.vfs.exists(histPath)) {
|
|
199
|
+
const lines = shell.vfs.readFile(histPath).split('\n').filter(Boolean);
|
|
200
|
+
let cmd;
|
|
201
|
+
if (trimmed === '!!' || trimmed.startsWith('!! ')) {
|
|
202
|
+
cmd = lines[lines.length - 1];
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
const n = parseInt(trimmed.slice(1), 10);
|
|
206
|
+
cmd = n > 0 ? lines[n - 1] : lines[lines.length + n];
|
|
207
|
+
}
|
|
208
|
+
if (cmd) {
|
|
209
|
+
const suffix = trimmed.startsWith('!! ') ? trimmed.slice(3) : '';
|
|
210
|
+
return runCommand(`${cmd}${suffix ? ` ${suffix}` : ''}`, authUser, hostname, mode, cwd, shell, stdin, shellEnv);
|
|
211
|
+
}
|
|
201
212
|
}
|
|
213
|
+
return { stderr: `${trimmed}: event not found`, exitCode: 1 };
|
|
202
214
|
}
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
215
|
+
const rawTokens = tokenizeCommand(trimmed);
|
|
216
|
+
const rawFirstWord = rawTokens[0]?.toLowerCase() ?? "";
|
|
217
|
+
const aliasVal = shellEnv.vars[`__alias_${rawFirstWord}`];
|
|
218
|
+
const aliasExpanded = aliasVal
|
|
219
|
+
? trimmed.replace(rawFirstWord, aliasVal)
|
|
220
|
+
: trimmed;
|
|
221
|
+
// Detect sh-syntax constructs that must be handled by the sh interpreter
|
|
222
|
+
const isShScript = /\bfor\s+\w+\s+in\b/.test(aliasExpanded) ||
|
|
223
|
+
/\bwhile\s+/.test(aliasExpanded) ||
|
|
224
|
+
/\bif\s+/.test(aliasExpanded) ||
|
|
225
|
+
/\w+\s*\(\s*\)\s*\{/.test(aliasExpanded) ||
|
|
226
|
+
/\bfunction\s+\w+/.test(aliasExpanded) ||
|
|
227
|
+
/\(\(\s*.+\s*\)\)/.test(aliasExpanded);
|
|
228
|
+
const hasOperators = /(?<![|&])[|](?![|])/.test(aliasExpanded) ||
|
|
229
|
+
aliasExpanded.includes(">") ||
|
|
230
|
+
aliasExpanded.includes("<") ||
|
|
231
|
+
aliasExpanded.includes("&&") ||
|
|
232
|
+
aliasExpanded.includes("||") ||
|
|
233
|
+
aliasExpanded.includes(";");
|
|
234
|
+
if ((isShScript && rawFirstWord !== "sh" && rawFirstWord !== "bash") || hasOperators) {
|
|
235
|
+
// sh-syntax: route through sh interpreter to handle for/while/functions
|
|
236
|
+
if (isShScript && rawFirstWord !== "sh" && rawFirstWord !== "bash") {
|
|
237
|
+
const shMod = resolveModule("sh");
|
|
238
|
+
if (shMod) {
|
|
239
|
+
return await shMod.run({
|
|
240
|
+
authUser, hostname,
|
|
241
|
+
activeSessions: shell.users.listActiveSessions(),
|
|
242
|
+
rawInput: aliasExpanded,
|
|
243
|
+
mode,
|
|
244
|
+
args: ["-c", aliasExpanded],
|
|
245
|
+
stdin: undefined,
|
|
246
|
+
cwd,
|
|
247
|
+
shell,
|
|
248
|
+
env: shellEnv,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const script = parseScript(aliasExpanded);
|
|
253
|
+
if (!script.isValid)
|
|
254
|
+
return { stderr: script.error || "Syntax error", exitCode: 1 };
|
|
255
|
+
try {
|
|
256
|
+
return await executeStatements(script.statements, authUser, hostname, mode, cwd, shell, shellEnv);
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
return {
|
|
260
|
+
stderr: error instanceof Error ? error.message : "Execution failed",
|
|
261
|
+
exitCode: 1,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
208
264
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
265
|
+
const expanded = await expandAsync(aliasExpanded, shellEnv.vars, shellEnv.lastExitCode, (sub) => runCommand(sub, authUser, hostname, mode, cwd, shell, undefined, shellEnv).then((r) => r.stdout ?? ""));
|
|
266
|
+
const parts = tokenizeCommand(expanded.trim());
|
|
267
|
+
if (parts.length === 0)
|
|
268
|
+
return { exitCode: 0 };
|
|
269
|
+
const assignRe = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/;
|
|
270
|
+
if (assignRe.test(parts[0])) {
|
|
271
|
+
return runCommandDirect(parts[0], parts.slice(1), authUser, hostname, mode, cwd, shell, stdin, shellEnv);
|
|
214
272
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
273
|
+
const commandName = parts[0]?.toLowerCase() ?? "";
|
|
274
|
+
// Apply brace expansion to each arg token
|
|
275
|
+
const args = parts.slice(1).flatMap(expandBraces).flatMap(token => expandGlob(token, cwd, shell.vfs));
|
|
276
|
+
const mod = resolveModule(commandName);
|
|
277
|
+
if (!mod) {
|
|
278
|
+
const vfsBinary = resolveVfsBinary(commandName, shellEnv, shell, authUser);
|
|
279
|
+
if (vfsBinary) {
|
|
280
|
+
const stubContent = shell.vfs.readFile(vfsBinary);
|
|
281
|
+
const builtinMatch = stubContent.match(/exec\s+builtin\s+(\S+)/);
|
|
282
|
+
if (builtinMatch) {
|
|
283
|
+
const builtinName = builtinMatch[1];
|
|
284
|
+
const builtinMod = resolveModule(builtinName);
|
|
285
|
+
if (builtinMod) {
|
|
286
|
+
return await builtinMod.run({
|
|
287
|
+
authUser,
|
|
288
|
+
hostname,
|
|
289
|
+
activeSessions: shell.users.listActiveSessions(),
|
|
290
|
+
rawInput: [commandName, ...args].join(" "),
|
|
291
|
+
mode,
|
|
292
|
+
args,
|
|
293
|
+
stdin,
|
|
294
|
+
cwd,
|
|
295
|
+
shell,
|
|
296
|
+
env: shellEnv,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
const shMod = resolveModule("sh");
|
|
301
|
+
if (shMod) {
|
|
302
|
+
return await shMod.run({
|
|
238
303
|
authUser,
|
|
239
304
|
hostname,
|
|
240
305
|
activeSessions: shell.users.listActiveSessions(),
|
|
241
|
-
rawInput:
|
|
306
|
+
rawInput: `sh -c ${JSON.stringify(stubContent)}`,
|
|
242
307
|
mode,
|
|
243
|
-
args,
|
|
308
|
+
args: ["-c", stubContent, "--", ...args],
|
|
244
309
|
stdin,
|
|
245
310
|
cwd,
|
|
246
311
|
shell,
|
|
@@ -248,42 +313,30 @@ export async function runCommand(rawInput, authUser, hostname, mode, cwd, shell,
|
|
|
248
313
|
});
|
|
249
314
|
}
|
|
250
315
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}
|
|
316
|
+
return { stderr: `${commandName}: command not found`, exitCode: 127 };
|
|
317
|
+
}
|
|
318
|
+
try {
|
|
319
|
+
return await mod.run({
|
|
320
|
+
authUser,
|
|
321
|
+
hostname,
|
|
322
|
+
activeSessions: shell.users.listActiveSessions(),
|
|
323
|
+
rawInput: expanded,
|
|
324
|
+
mode,
|
|
325
|
+
args,
|
|
326
|
+
stdin,
|
|
327
|
+
cwd,
|
|
328
|
+
shell,
|
|
329
|
+
env: shellEnv,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
return {
|
|
334
|
+
stderr: error instanceof Error ? error.message : "Command failed",
|
|
335
|
+
exitCode: 1,
|
|
336
|
+
};
|
|
266
337
|
}
|
|
267
|
-
return { stderr: `${commandName}: command not found`, exitCode: 127 };
|
|
268
|
-
}
|
|
269
|
-
try {
|
|
270
|
-
return await mod.run({
|
|
271
|
-
authUser,
|
|
272
|
-
hostname,
|
|
273
|
-
activeSessions: shell.users.listActiveSessions(),
|
|
274
|
-
rawInput: expanded,
|
|
275
|
-
mode,
|
|
276
|
-
args,
|
|
277
|
-
stdin,
|
|
278
|
-
cwd,
|
|
279
|
-
shell,
|
|
280
|
-
env: shellEnv,
|
|
281
|
-
});
|
|
282
338
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
stderr: error instanceof Error ? error.message : "Command failed",
|
|
286
|
-
exitCode: 1,
|
|
287
|
-
};
|
|
339
|
+
finally {
|
|
340
|
+
_callDepth--;
|
|
288
341
|
}
|
|
289
342
|
}
|
package/dist/commands/sh.js
CHANGED
|
@@ -369,6 +369,11 @@ function splitShScript(script) {
|
|
|
369
369
|
}
|
|
370
370
|
continue;
|
|
371
371
|
}
|
|
372
|
+
// Backslash-newline continuation: join lines
|
|
373
|
+
if (!inSingleQ && ch === '\\' && i + 1 < script.length && script[i + 1] === '\n') {
|
|
374
|
+
i += 2; // skip \ and \n
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
372
377
|
if (depth === 0 && (ch === ";" || ch === "\n")) {
|
|
373
378
|
const t = current.trim();
|
|
374
379
|
if (t && !t.startsWith("#"))
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ShellModule } from "../types/commands";
|
|
2
|
+
/**
|
|
3
|
+
* Terminal control.
|
|
4
|
+
* @category shell
|
|
5
|
+
* @params ["<cap> [args...]"]
|
|
6
|
+
*/
|
|
7
|
+
export declare const tputCommand: ShellModule;
|
|
8
|
+
/**
|
|
9
|
+
* Print or set terminal line settings.
|
|
10
|
+
* @category shell
|
|
11
|
+
* @params ["[args...]"]
|
|
12
|
+
*/
|
|
13
|
+
export declare const sttyCommand: ShellModule;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const CAPS = {
|
|
2
|
+
cols: 220, lines: 50, colors: 256,
|
|
3
|
+
bold: "\x1b[1m", dim: "\x1b[2m", smul: "\x1b[4m", rmul: "\x1b[24m",
|
|
4
|
+
rev: "\x1b[7m", smso: "\x1b[7m", rmso: "\x1b[27m",
|
|
5
|
+
sgr0: "\x1b[0m", el: "\x1b[K", ed: "\x1b[J",
|
|
6
|
+
clear: "\x1b[2J\x1b[H", cup: "", setaf: "", setab: "",
|
|
7
|
+
};
|
|
8
|
+
const ANSI_COLORS = [
|
|
9
|
+
"30", "31", "32", "33", "34", "35", "36", "37",
|
|
10
|
+
"90", "91", "92", "93", "94", "95", "96", "97",
|
|
11
|
+
];
|
|
12
|
+
/**
|
|
13
|
+
* Terminal control.
|
|
14
|
+
* @category shell
|
|
15
|
+
* @params ["<cap> [args...]"]
|
|
16
|
+
*/
|
|
17
|
+
export const tputCommand = {
|
|
18
|
+
name: "tput",
|
|
19
|
+
description: "Query terminfo database",
|
|
20
|
+
category: "shell",
|
|
21
|
+
params: ["<cap> [args...]"],
|
|
22
|
+
run: ({ args }) => {
|
|
23
|
+
const cap = args[0];
|
|
24
|
+
if (!cap)
|
|
25
|
+
return { stderr: "tput: missing capability", exitCode: 1 };
|
|
26
|
+
if (cap === "setaf" && args[1] !== undefined) {
|
|
27
|
+
const n = parseInt(args[1], 10);
|
|
28
|
+
const code = ANSI_COLORS[n] ?? "39";
|
|
29
|
+
return { stdout: `\x1b[${code}m`, exitCode: 0 };
|
|
30
|
+
}
|
|
31
|
+
if (cap === "setab" && args[1] !== undefined) {
|
|
32
|
+
const n = parseInt(args[1], 10);
|
|
33
|
+
const code = ANSI_COLORS[n]?.replace(/3/, "4").replace(/9/, "10") ?? "49";
|
|
34
|
+
return { stdout: `\x1b[${code}m`, exitCode: 0 };
|
|
35
|
+
}
|
|
36
|
+
if (cap === "cup" && args[1] !== undefined && args[2] !== undefined) {
|
|
37
|
+
return { stdout: `\x1b[${parseInt(args[1], 10) + 1};${parseInt(args[2], 10) + 1}H`, exitCode: 0 };
|
|
38
|
+
}
|
|
39
|
+
const val = CAPS[cap];
|
|
40
|
+
if (val === undefined)
|
|
41
|
+
return { stderr: `tput: unknown terminal capability '${cap}'`, exitCode: 1 };
|
|
42
|
+
return { stdout: String(val), exitCode: 0 };
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Print or set terminal line settings.
|
|
47
|
+
* @category shell
|
|
48
|
+
* @params ["[args...]"]
|
|
49
|
+
*/
|
|
50
|
+
export const sttyCommand = {
|
|
51
|
+
name: "stty",
|
|
52
|
+
description: "Change and print terminal line settings",
|
|
53
|
+
category: "shell",
|
|
54
|
+
params: ["[args...]"],
|
|
55
|
+
run: ({ args }) => {
|
|
56
|
+
if (args.includes("-a") || args.includes("--all")) {
|
|
57
|
+
return {
|
|
58
|
+
stdout: [
|
|
59
|
+
"speed 38400 baud; rows 50; columns 220; line = 0;",
|
|
60
|
+
"intr = ^C; quit = ^\\; erase = ^?; kill = ^U; eof = ^D;",
|
|
61
|
+
"eol = M-^?; eol2 = M-^?; swtch = <undef>; start = ^Q; stop = ^S;",
|
|
62
|
+
"-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts",
|
|
63
|
+
"brkint -icrnl ixon -ixoff -iuclc ixany imaxbel -iutf8",
|
|
64
|
+
"opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0",
|
|
65
|
+
"isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke",
|
|
66
|
+
].join("\n"),
|
|
67
|
+
exitCode: 0,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (args.includes("size")) {
|
|
71
|
+
return { stdout: "50 220", exitCode: 0 };
|
|
72
|
+
}
|
|
73
|
+
// silently accept set operations
|
|
74
|
+
return { exitCode: 0 };
|
|
75
|
+
},
|
|
76
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { userHome } from "./runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Show who is logged on and what they are doing.
|
|
4
|
+
* @category system
|
|
5
|
+
* @params ["[user]"]
|
|
6
|
+
*/
|
|
7
|
+
export const wCommand = {
|
|
8
|
+
name: "w",
|
|
9
|
+
description: "Show who is logged on and what they are doing",
|
|
10
|
+
category: "system",
|
|
11
|
+
params: ["[user]"],
|
|
12
|
+
run: ({ shell, authUser }) => {
|
|
13
|
+
const now = new Date();
|
|
14
|
+
const upSecs = Math.floor(performance.now() / 1000);
|
|
15
|
+
const upMins = Math.floor(upSecs / 60);
|
|
16
|
+
const upHours = Math.floor(upMins / 60);
|
|
17
|
+
const uptimeStr = upHours > 0
|
|
18
|
+
? `${upHours}:${String(upMins % 60).padStart(2, "0")}`
|
|
19
|
+
: `${upMins} min`;
|
|
20
|
+
const timeStr = now.toTimeString().slice(0, 5);
|
|
21
|
+
// Read active sessions
|
|
22
|
+
shell.users.listActiveSessions?.();
|
|
23
|
+
const logPath = `${userHome(authUser)}/.lastlog`;
|
|
24
|
+
let loginTime = timeStr;
|
|
25
|
+
if (shell.vfs.exists(logPath)) {
|
|
26
|
+
try {
|
|
27
|
+
const log = JSON.parse(shell.vfs.readFile(logPath));
|
|
28
|
+
loginTime = new Date(log.at).toTimeString().slice(0, 5);
|
|
29
|
+
}
|
|
30
|
+
catch { }
|
|
31
|
+
}
|
|
32
|
+
const header = ` ${timeStr} up ${uptimeStr}, 1 user, load average: 0.${Math.floor(Math.random() * 30).toString().padStart(2, "0")}, 0.${Math.floor(Math.random() * 15).toString().padStart(2, "0")}, 0.${Math.floor(Math.random() * 10).toString().padStart(2, "0")}`;
|
|
33
|
+
const colHeader = "USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT";
|
|
34
|
+
const idle = "0.00s";
|
|
35
|
+
const row = `${authUser.padEnd(8)} pts/0 browser ${loginTime} ${idle} 0.01s 0.00s -bash`;
|
|
36
|
+
return { stdout: [header, colHeader, row].join("\n"), exitCode: 0 };
|
|
37
|
+
},
|
|
38
|
+
};
|
package/dist/utils/expand.d.ts
CHANGED
|
@@ -56,3 +56,15 @@ export declare function expandSync(input: string, env: Record<string, string>, l
|
|
|
56
56
|
* @param runCmd Async callback to execute a command and return its stdout.
|
|
57
57
|
*/
|
|
58
58
|
export declare function expandAsync(input: string, env: Record<string, string>, lastExit: number, runCmd: (cmd: string) => Promise<string>): Promise<string>;
|
|
59
|
+
/**
|
|
60
|
+
* Expand a glob pattern against a VirtualShell VFS.
|
|
61
|
+
* Supports * (any chars in segment) and ** (any path).
|
|
62
|
+
* Returns the original pattern if no matches found (bash behavior).
|
|
63
|
+
*/
|
|
64
|
+
export declare function expandGlob(pattern: string, cwd: string, vfs: {
|
|
65
|
+
list: (p: string) => string[];
|
|
66
|
+
exists: (p: string) => boolean;
|
|
67
|
+
stat: (p: string) => {
|
|
68
|
+
type: string;
|
|
69
|
+
};
|
|
70
|
+
}): string[];
|
package/dist/utils/expand.js
CHANGED
|
@@ -474,3 +474,87 @@ export async function expandAsync(input, env, lastExit, runCmd) {
|
|
|
474
474
|
env[depthKey] = String(currentDepth);
|
|
475
475
|
}
|
|
476
476
|
}
|
|
477
|
+
// ─── Glob expansion ──────────────────────────────────────────────────────────
|
|
478
|
+
/**
|
|
479
|
+
* Expand a glob pattern against a VirtualShell VFS.
|
|
480
|
+
* Supports * (any chars in segment) and ** (any path).
|
|
481
|
+
* Returns the original pattern if no matches found (bash behavior).
|
|
482
|
+
*/
|
|
483
|
+
export function expandGlob(pattern, cwd, vfs) {
|
|
484
|
+
// No glob chars → return as-is
|
|
485
|
+
if (!pattern.includes('*') && !pattern.includes('?'))
|
|
486
|
+
return [pattern];
|
|
487
|
+
const isAbsolute = pattern.startsWith('/');
|
|
488
|
+
const base = isAbsolute ? '/' : cwd;
|
|
489
|
+
const relPattern = isAbsolute ? pattern.slice(1) : pattern;
|
|
490
|
+
const results = matchGlob(base, relPattern.split('/'), vfs);
|
|
491
|
+
if (results.length === 0)
|
|
492
|
+
return [pattern]; // no match → literal
|
|
493
|
+
return results.sort();
|
|
494
|
+
}
|
|
495
|
+
function matchGlob(dir, segments, vfs) {
|
|
496
|
+
if (segments.length === 0)
|
|
497
|
+
return [dir];
|
|
498
|
+
const [seg, ...rest] = segments;
|
|
499
|
+
if (!seg)
|
|
500
|
+
return [dir];
|
|
501
|
+
// ** matches zero or more path segments
|
|
502
|
+
if (seg === '**') {
|
|
503
|
+
const all = walkAll(dir, vfs);
|
|
504
|
+
return rest.length === 0 ? all : all.flatMap(d => {
|
|
505
|
+
try {
|
|
506
|
+
if (vfs.stat(d).type === 'directory')
|
|
507
|
+
return matchGlob(d, rest, vfs);
|
|
508
|
+
}
|
|
509
|
+
catch { }
|
|
510
|
+
return [];
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
let entries = [];
|
|
514
|
+
try {
|
|
515
|
+
entries = vfs.list(dir);
|
|
516
|
+
}
|
|
517
|
+
catch {
|
|
518
|
+
return [];
|
|
519
|
+
}
|
|
520
|
+
const re = globToRegex(seg);
|
|
521
|
+
return entries
|
|
522
|
+
.filter(e => !e.startsWith('.') || seg.startsWith('.'))
|
|
523
|
+
.filter(e => re.test(e))
|
|
524
|
+
.flatMap(e => {
|
|
525
|
+
const full = dir === '/' ? `/${e}` : `${dir}/${e}`;
|
|
526
|
+
if (rest.length === 0)
|
|
527
|
+
return [full];
|
|
528
|
+
try {
|
|
529
|
+
if (vfs.stat(full).type === 'directory')
|
|
530
|
+
return matchGlob(full, rest, vfs);
|
|
531
|
+
}
|
|
532
|
+
catch { }
|
|
533
|
+
return [];
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
function walkAll(dir, vfs) {
|
|
537
|
+
const results = [dir];
|
|
538
|
+
let entries = [];
|
|
539
|
+
try {
|
|
540
|
+
entries = vfs.list(dir);
|
|
541
|
+
}
|
|
542
|
+
catch {
|
|
543
|
+
return results;
|
|
544
|
+
}
|
|
545
|
+
for (const e of entries) {
|
|
546
|
+
const full = dir === '/' ? `/${e}` : `${dir}/${e}`;
|
|
547
|
+
try {
|
|
548
|
+
if (vfs.stat(full).type === 'directory')
|
|
549
|
+
results.push(...walkAll(full, vfs));
|
|
550
|
+
}
|
|
551
|
+
catch { }
|
|
552
|
+
}
|
|
553
|
+
return results;
|
|
554
|
+
}
|
|
555
|
+
function globToRegex(pattern) {
|
|
556
|
+
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
557
|
+
.replace(/\*/g, '.*')
|
|
558
|
+
.replace(/\?/g, '.');
|
|
559
|
+
return new RegExp(`^${escaped}$`);
|
|
560
|
+
}
|