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.
- package/CHANGELOG.md +504 -0
- package/CONTRIBUTING.md +62 -0
- package/LICENSE +21 -0
- package/README.md +159 -2
- package/SECURITY.md +25 -0
- package/cordis.patch.yml +5 -0
- package/docs/architecture.md +124 -0
- package/docs/quickstart.md +94 -0
- package/lib/client.js +4977 -0
- package/lib/core/activity.js +83 -0
- package/lib/core/background.js +69 -0
- package/lib/core/broker.js +799 -0
- package/lib/core/dialect.js +28 -0
- package/lib/core/errors.js +138 -0
- package/lib/core/gate-pwsh.js +88 -0
- package/lib/core/gate.js +572 -0
- package/lib/core/guard.js +71 -0
- package/lib/core/known-hosts.js +74 -0
- package/lib/core/local-file-api.js +174 -0
- package/lib/core/localfs.js +381 -0
- package/lib/core/paths.js +104 -0
- package/lib/core/posix-quote.js +4 -0
- package/lib/core/prefs.js +38 -0
- package/lib/core/remote-bash-tool.js +321 -0
- package/lib/core/remote-bash.js +337 -0
- package/lib/core/secrets.js +96 -0
- package/lib/core/server-reference.js +299 -0
- package/lib/core/shell-adapters.js +48 -0
- package/lib/core/ssh.js +761 -0
- package/lib/core/store.js +83 -0
- package/lib/core/targets.js +250 -0
- package/lib/core/terminals.js +299 -0
- package/lib/core/win32-adapter.js +141 -0
- package/lib/index.js +450 -0
- package/lib/routes.js +733 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +88 -4
- package/vendor/NOTICE.md +50 -0
- package/vendor/XTERM-LICENSE +21 -0
- package/vendor/node_modules/asn1/LICENSE +19 -0
- package/vendor/node_modules/asn1/README.md +50 -0
- package/vendor/node_modules/asn1/lib/ber/errors.js +13 -0
- package/vendor/node_modules/asn1/lib/ber/index.js +27 -0
- package/vendor/node_modules/asn1/lib/ber/reader.js +262 -0
- package/vendor/node_modules/asn1/lib/ber/types.js +36 -0
- package/vendor/node_modules/asn1/lib/ber/writer.js +317 -0
- package/vendor/node_modules/asn1/lib/index.js +20 -0
- package/vendor/node_modules/asn1/package.json +31 -0
- package/vendor/node_modules/bcrypt-pbkdf/CONTRIBUTING.md +13 -0
- package/vendor/node_modules/bcrypt-pbkdf/LICENSE +66 -0
- package/vendor/node_modules/bcrypt-pbkdf/README.md +45 -0
- package/vendor/node_modules/bcrypt-pbkdf/index.js +556 -0
- package/vendor/node_modules/bcrypt-pbkdf/package.json +15 -0
- package/vendor/node_modules/safer-buffer/LICENSE +21 -0
- package/vendor/node_modules/safer-buffer/Readme.md +156 -0
- package/vendor/node_modules/safer-buffer/dangerous.js +58 -0
- package/vendor/node_modules/safer-buffer/package.json +34 -0
- package/vendor/node_modules/safer-buffer/safer.js +77 -0
- package/vendor/node_modules/tweetnacl/AUTHORS.md +28 -0
- package/vendor/node_modules/tweetnacl/LICENSE +24 -0
- package/vendor/node_modules/tweetnacl/README.md +459 -0
- package/vendor/node_modules/tweetnacl/nacl-fast.js +2388 -0
- package/vendor/node_modules/tweetnacl/nacl.js +1175 -0
- package/vendor/node_modules/tweetnacl/package.json +58 -0
- package/vendor/ssh2/LICENSE +19 -0
- package/vendor/ssh2/lib/Channel.js +295 -0
- package/vendor/ssh2/lib/agent.js +1123 -0
- package/vendor/ssh2/lib/client.js +2176 -0
- package/vendor/ssh2/lib/http-agents.js +84 -0
- package/vendor/ssh2/lib/index.js +44 -0
- package/vendor/ssh2/lib/keygen.js +582 -0
- package/vendor/ssh2/lib/protocol/Protocol.js +2136 -0
- package/vendor/ssh2/lib/protocol/SFTP.js +4052 -0
- package/vendor/ssh2/lib/protocol/constants.js +356 -0
- package/vendor/ssh2/lib/protocol/crypto/poly1305.js +43 -0
- package/vendor/ssh2/lib/protocol/crypto.js +1602 -0
- package/vendor/ssh2/lib/protocol/handlers.js +16 -0
- package/vendor/ssh2/lib/protocol/handlers.misc.js +1285 -0
- package/vendor/ssh2/lib/protocol/kex.js +1908 -0
- package/vendor/ssh2/lib/protocol/keyParser.js +1484 -0
- package/vendor/ssh2/lib/protocol/node-fs-compat.js +115 -0
- package/vendor/ssh2/lib/protocol/utils.js +356 -0
- package/vendor/ssh2/lib/protocol/zlib.js +255 -0
- package/vendor/ssh2/lib/server.js +1380 -0
- package/vendor/ssh2/lib/utils.js +336 -0
- package/vendor/ssh2/package.json +34 -0
- 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
|
+
}
|