dsh-ssh-workbench 0.0.0-stage → 1.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/CHANGELOG.md +504 -0
  2. package/CONTRIBUTING.md +62 -0
  3. package/LICENSE +21 -0
  4. package/README.md +159 -2
  5. package/SECURITY.md +25 -0
  6. package/cordis.patch.yml +5 -0
  7. package/docs/architecture.md +124 -0
  8. package/docs/quickstart.md +94 -0
  9. package/lib/client.js +4977 -0
  10. package/lib/core/activity.js +83 -0
  11. package/lib/core/background.js +69 -0
  12. package/lib/core/broker.js +799 -0
  13. package/lib/core/dialect.js +28 -0
  14. package/lib/core/errors.js +138 -0
  15. package/lib/core/gate-pwsh.js +88 -0
  16. package/lib/core/gate.js +572 -0
  17. package/lib/core/guard.js +71 -0
  18. package/lib/core/known-hosts.js +74 -0
  19. package/lib/core/local-file-api.js +174 -0
  20. package/lib/core/localfs.js +381 -0
  21. package/lib/core/paths.js +104 -0
  22. package/lib/core/posix-quote.js +4 -0
  23. package/lib/core/prefs.js +38 -0
  24. package/lib/core/remote-bash-tool.js +321 -0
  25. package/lib/core/remote-bash.js +337 -0
  26. package/lib/core/secrets.js +96 -0
  27. package/lib/core/server-reference.js +299 -0
  28. package/lib/core/shell-adapters.js +48 -0
  29. package/lib/core/ssh.js +761 -0
  30. package/lib/core/store.js +83 -0
  31. package/lib/core/targets.js +250 -0
  32. package/lib/core/terminals.js +299 -0
  33. package/lib/core/win32-adapter.js +141 -0
  34. package/lib/index.js +450 -0
  35. package/lib/routes.js +733 -0
  36. package/locale/en.json +6 -0
  37. package/locale/zh.json +6 -0
  38. package/package.json +88 -4
  39. package/vendor/NOTICE.md +50 -0
  40. package/vendor/XTERM-LICENSE +21 -0
  41. package/vendor/node_modules/asn1/LICENSE +19 -0
  42. package/vendor/node_modules/asn1/README.md +50 -0
  43. package/vendor/node_modules/asn1/lib/ber/errors.js +13 -0
  44. package/vendor/node_modules/asn1/lib/ber/index.js +27 -0
  45. package/vendor/node_modules/asn1/lib/ber/reader.js +262 -0
  46. package/vendor/node_modules/asn1/lib/ber/types.js +36 -0
  47. package/vendor/node_modules/asn1/lib/ber/writer.js +317 -0
  48. package/vendor/node_modules/asn1/lib/index.js +20 -0
  49. package/vendor/node_modules/asn1/package.json +31 -0
  50. package/vendor/node_modules/bcrypt-pbkdf/CONTRIBUTING.md +13 -0
  51. package/vendor/node_modules/bcrypt-pbkdf/LICENSE +66 -0
  52. package/vendor/node_modules/bcrypt-pbkdf/README.md +45 -0
  53. package/vendor/node_modules/bcrypt-pbkdf/index.js +556 -0
  54. package/vendor/node_modules/bcrypt-pbkdf/package.json +15 -0
  55. package/vendor/node_modules/safer-buffer/LICENSE +21 -0
  56. package/vendor/node_modules/safer-buffer/Readme.md +156 -0
  57. package/vendor/node_modules/safer-buffer/dangerous.js +58 -0
  58. package/vendor/node_modules/safer-buffer/package.json +34 -0
  59. package/vendor/node_modules/safer-buffer/safer.js +77 -0
  60. package/vendor/node_modules/tweetnacl/AUTHORS.md +28 -0
  61. package/vendor/node_modules/tweetnacl/LICENSE +24 -0
  62. package/vendor/node_modules/tweetnacl/README.md +459 -0
  63. package/vendor/node_modules/tweetnacl/nacl-fast.js +2388 -0
  64. package/vendor/node_modules/tweetnacl/nacl.js +1175 -0
  65. package/vendor/node_modules/tweetnacl/package.json +58 -0
  66. package/vendor/ssh2/LICENSE +19 -0
  67. package/vendor/ssh2/lib/Channel.js +295 -0
  68. package/vendor/ssh2/lib/agent.js +1123 -0
  69. package/vendor/ssh2/lib/client.js +2176 -0
  70. package/vendor/ssh2/lib/http-agents.js +84 -0
  71. package/vendor/ssh2/lib/index.js +44 -0
  72. package/vendor/ssh2/lib/keygen.js +582 -0
  73. package/vendor/ssh2/lib/protocol/Protocol.js +2136 -0
  74. package/vendor/ssh2/lib/protocol/SFTP.js +4052 -0
  75. package/vendor/ssh2/lib/protocol/constants.js +356 -0
  76. package/vendor/ssh2/lib/protocol/crypto/poly1305.js +43 -0
  77. package/vendor/ssh2/lib/protocol/crypto.js +1602 -0
  78. package/vendor/ssh2/lib/protocol/handlers.js +16 -0
  79. package/vendor/ssh2/lib/protocol/handlers.misc.js +1285 -0
  80. package/vendor/ssh2/lib/protocol/kex.js +1908 -0
  81. package/vendor/ssh2/lib/protocol/keyParser.js +1484 -0
  82. package/vendor/ssh2/lib/protocol/node-fs-compat.js +115 -0
  83. package/vendor/ssh2/lib/protocol/utils.js +356 -0
  84. package/vendor/ssh2/lib/protocol/zlib.js +255 -0
  85. package/vendor/ssh2/lib/server.js +1380 -0
  86. package/vendor/ssh2/lib/utils.js +336 -0
  87. package/vendor/ssh2/package.json +34 -0
  88. package/vendor/xterm.js +53 -0
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Local, bounded activity log for the overview page (not an audit log).
3
+ *
4
+ * This is NOT an audit system: entries are best-effort, newest-first, and
5
+ * capped so the file cannot grow without bound. A failed write never fails the
6
+ * operation that produced the entry.
7
+ */
8
+ import { activityPath, readTextFile, writePrivateFile } from './paths.js'
9
+
10
+ export const MAX_ACTIVITY_ENTRIES = 200
11
+
12
+ /**
13
+ * Human-readable activity outcome for one command result.
14
+ *
15
+ * The overview must distinguish "the command ran and returned non-zero" from
16
+ * "the transport failed": `cat missing-file` is `exit 1`, `docker` on a host
17
+ * without Docker is `exit 127 (COMMAND_NOT_FOUND)`, while a dropped connection
18
+ * is `failed (CONNECT_TIMEOUT)`. Without this, benign non-zero exits look like
19
+ * remote failures.
20
+ */
21
+ export function describeResultOutcome(result) {
22
+ if (!result || typeof result !== 'object') return 'unknown'
23
+ if (result.outcome === 'success') return 'success'
24
+ const code = typeof result.errorCode === 'string' && result.errorCode ? result.errorCode : ''
25
+ const hasExit = result.exitCode !== null && result.exitCode !== undefined
26
+ if (hasExit) return code && code !== 'EXEC_FAILED' ? `exit ${result.exitCode} (${code})` : `exit ${result.exitCode}`
27
+ if (code) return `${result.outcome ?? 'failed'} (${code})`
28
+ return String(result.outcome ?? 'failed')
29
+ }
30
+
31
+ export function createActivityStore({ file = activityPath(), maxEntries = MAX_ACTIVITY_ENTRIES } = {}) {
32
+ if (!Number.isSafeInteger(maxEntries) || maxEntries <= 0) throw new Error('maxEntries must be a positive safe integer')
33
+
34
+ function load() {
35
+ const text = readTextFile(file, '[]')
36
+ try {
37
+ const parsed = JSON.parse(text)
38
+ return Array.isArray(parsed) ? parsed.filter((entry) => entry && typeof entry === 'object') : []
39
+ } catch {
40
+ return []
41
+ }
42
+ }
43
+
44
+ function save(entries) {
45
+ try {
46
+ writePrivateFile(file, `${JSON.stringify(entries, null, 2)}\n`)
47
+ return true
48
+ } catch {
49
+ return false
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Prepend one entry. `at` defaults to now; unknown fields are kept so callers
55
+ * can add outcome/latency without touching the store.
56
+ * @returns the normalized entry (even when the write failed).
57
+ */
58
+ function append(entry) {
59
+ const normalized = {
60
+ at: typeof entry?.at === 'string' ? entry.at : new Date().toISOString(),
61
+ kind: String(entry?.kind ?? 'operation'),
62
+ ...(Array.isArray(entry?.servers) ? { servers: entry.servers.map(String) } : {}),
63
+ ...(entry?.server !== undefined ? { server: String(entry.server) } : {}),
64
+ ...(entry?.summary !== undefined ? { summary: String(entry.summary) } : {}),
65
+ ...(entry?.outcome !== undefined ? { outcome: String(entry.outcome) } : {}),
66
+ }
67
+ const next = [normalized, ...load()].slice(0, maxEntries)
68
+ save(next)
69
+ return normalized
70
+ }
71
+
72
+ /** @returns newest-first entries, at most `limit`. */
73
+ function list(limit = 20) {
74
+ const bounded = Number.isSafeInteger(limit) && limit > 0 ? Math.min(limit, maxEntries) : 20
75
+ return load().slice(0, bounded)
76
+ }
77
+
78
+ function clear() {
79
+ return save([])
80
+ }
81
+
82
+ return { append, list, clear, maxEntries }
83
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Detached ("background") remote commands.
3
+ *
4
+ * A remote process cannot be registered in DSH's local job registry, so a
5
+ * background start returns the only facts that stay true across transports:
6
+ * the remote pid and a log file. The model reads output with `tail` /
7
+ * `Get-Content` and stops the process with `kill` / `Stop-Process`; that keeps
8
+ * the behaviour identical on POSIX and Windows targets without pretending the
9
+ * local `job_*` tools manage a remote process.
10
+ *
11
+ * The launcher itself must finish quickly and print machine-readable markers,
12
+ * so the caller can distinguish "started" from "never started".
13
+ */
14
+ import { quotePosix } from './posix-quote.js'
15
+
16
+ export const BG_PID_MARKER = '__SDH_BG_PID__'
17
+ export const BG_LOG_MARKER = '__SDH_BG_LOG__'
18
+ export const BG_ERR_MARKER = '__SDH_BG_ERR__'
19
+
20
+ /** Parse the marker lines out of the launcher's stdout. */
21
+ export function parseBackgroundStart(stdout) {
22
+ const text = String(stdout ?? '')
23
+ const pidMatch = /__SDH_BG_PID__(\d+)/.exec(text)
24
+ const logMatch = /__SDH_BG_LOG__([^\r\n]+)/.exec(text)
25
+ const errMatch = /__SDH_BG_ERR__([^\r\n]+)/.exec(text)
26
+ const pid = pidMatch ? Number.parseInt(pidMatch[1], 10) : undefined
27
+ const log = logMatch ? logMatch[1].trim() : undefined
28
+ const errLog = errMatch ? errMatch[1].trim() : undefined
29
+ return {
30
+ pid: Number.isInteger(pid) && pid > 0 ? pid : undefined,
31
+ log: log || undefined,
32
+ errLog: errLog || undefined,
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Build one POSIX launcher for an already workdir-prefixed command.
38
+ *
39
+ * `nohup` + redirected stdio is what keeps the child alive after the SSH
40
+ * channel closes; the whole launcher runs under `sh -c` so a fish/csh login
41
+ * shell only has to parse the simple outer command.
42
+ */
43
+ export function buildPosixBackgroundCommand(builtCommand) {
44
+ const script = [
45
+ 'LOG=$(mktemp /tmp/dsh-ssh-workbench-bg.XXXXXX 2>/dev/null || echo /tmp/dsh-ssh-workbench-bg-$$-$(date +%s).log)',
46
+ `nohup "\${SHELL:-/bin/sh}" -lc ${quotePosix(builtCommand)} >"$LOG" 2>&1 &`,
47
+ `echo "${BG_PID_MARKER}$!"`,
48
+ `echo "${BG_LOG_MARKER}$LOG"`,
49
+ ].join('\n')
50
+ return `sh -c ${quotePosix(script)}`
51
+ }
52
+
53
+ /** One model-facing description of a started background process. */
54
+ export function renderBackgroundStart({ pid, log, errLog, target, command, dialect = 'posix' }) {
55
+ const windows = dialect === 'windows'
56
+ const lines = [`background: started${target ? ` on ${target}` : ''}`, `command: ${command}`]
57
+ if (pid !== undefined) lines.push(`pid: ${pid}`)
58
+ if (log) lines.push(`log: ${log}`)
59
+ if (errLog) lines.push(`stderr log: ${errLog}`)
60
+ const logPath = log ?? '<log path from the launcher output>'
61
+ lines.push(
62
+ windows
63
+ ? `read output: Get-Content -LiteralPath '${logPath.replace(/'/g, "''")}' -Wait`
64
+ : `read output: tail -f ${quotePosix(logPath)}`,
65
+ )
66
+ if (pid !== undefined) lines.push(windows ? `stop: Stop-Process -Id ${pid} -Force` : `stop: kill ${pid}`)
67
+ lines.push('The process is detached from this tool call: timeoutMs does not apply and it keeps running until it exits or is stopped.')
68
+ return lines.join('\n')
69
+ }