dsh-ssh-tunnel 1.0.0 → 1.0.2
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 +68 -0
- package/README.md +80 -38
- package/README_en.md +80 -38
- package/cordis.patch.yml +4 -4
- package/icon.svg +26 -0
- package/lib/client.js +1643 -1472
- package/lib/index.js +1739 -1611
- package/lib/session.js +616 -525
- package/lib/shared/args.js +23 -0
- package/lib/shared/host-key.js +32 -32
- package/lib/shared/host-summary.js +75 -75
- package/lib/shared/http-trust.js +59 -59
- package/lib/shared/path.js +21 -22
- package/lib/shared/persist.js +41 -41
- package/lib/shared/session-auth.js +47 -39
- package/lib/shared/session-policy.js +151 -125
- package/lib/shared/shell-buffer.js +10 -10
- package/lib/shared/vendor.js +49 -49
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +91 -84
- package/scripts/install.ps1 +191 -93
- package/scripts/install.sh +278 -226
- package/scripts/lib/bundle-check.cjs +108 -0
- package/scripts/lib/strip-mount.cjs +114 -0
- package/scripts/lib/ws-exclude.cjs +143 -0
- package/scripts/smoke-test.mjs +899 -511
- package/scripts/sync-to-dsh.sh +87 -5
|
@@ -1,125 +1,151 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure SSH session policy: timeouts, status invariant, reuse vs tombstone.
|
|
3
|
-
* No ssh2, no I/O.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export const SSH_KEEPALIVE_INTERVAL_MS = 30_000
|
|
7
|
-
export const SSH_KEEPALIVE_COUNT_MAX = 3
|
|
8
|
-
export const SSH_READY_TIMEOUT_MS = 20_000
|
|
9
|
-
export const SSH_RECONNECT_MAX_ATTEMPTS = 3
|
|
10
|
-
export const SSH_RECONNECT_DELAYS_MS = [5_000, 10_000, 10_000]
|
|
11
|
-
export const SSH_RECONNECT_ATTEMPT_TIMEOUT_MS = 20_000
|
|
12
|
-
export const SSH_TOOL_TIMEOUT_MS = 300_000
|
|
13
|
-
export const SSH_TIMEOUT_MIN_MS = 1_000
|
|
14
|
-
export const SSH_TIMEOUT_MAX_MS = 300_000
|
|
15
|
-
export const SSH_EXEC_DEFAULT_TIMEOUT_MS = 30_000
|
|
16
|
-
export const SSH_SFTP_META_TIMEOUT_MS = 30_000
|
|
17
|
-
export const SSH_SFTP_TRANSFER_TIMEOUT_MS = 120_000
|
|
18
|
-
export const SSH_SHELL_OPEN_TIMEOUT_MS = 10_000
|
|
19
|
-
|
|
20
|
-
export const SSH_STATUS_CONNECTED = 'connected'
|
|
21
|
-
export const SSH_STATUS_RECONNECTING = 'reconnecting'
|
|
22
|
-
export const SSH_STATUS_DISCONNECTED = 'disconnected'
|
|
23
|
-
|
|
24
|
-
export function clampTimeoutMs(value, fallback) {
|
|
25
|
-
const n = Number(value)
|
|
26
|
-
const base = Number.isFinite(n) && n > 0 ? n : fallback
|
|
27
|
-
return Math.min(SSH_TIMEOUT_MAX_MS, Math.max(SSH_TIMEOUT_MIN_MS, Math.floor(base)))
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function defaultTimeoutMsForAction(action) {
|
|
31
|
-
const a = String(action || '')
|
|
32
|
-
if (a === 'exec') return SSH_EXEC_DEFAULT_TIMEOUT_MS
|
|
33
|
-
if (a === 'sftp_read_text' || a === 'sftp_write_text' || a === 'sftp_upload' || a === 'sftp_download') {
|
|
34
|
-
return SSH_SFTP_TRANSFER_TIMEOUT_MS
|
|
35
|
-
}
|
|
36
|
-
if (a.startsWith('sftp_')) return SSH_SFTP_META_TIMEOUT_MS
|
|
37
|
-
if (a === 'read_session' || a === 'send_input' || a === 'resize_session') return SSH_SHELL_OPEN_TIMEOUT_MS
|
|
38
|
-
return SSH_EXEC_DEFAULT_TIMEOUT_MS
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function isLiveStatus(status, running) {
|
|
42
|
-
return status === SSH_STATUS_CONNECTED && running === true
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Pure SSH session policy: timeouts, status invariant, reuse vs tombstone.
|
|
3
|
+
* No ssh2, no I/O.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const SSH_KEEPALIVE_INTERVAL_MS = 30_000
|
|
7
|
+
export const SSH_KEEPALIVE_COUNT_MAX = 3
|
|
8
|
+
export const SSH_READY_TIMEOUT_MS = 20_000
|
|
9
|
+
export const SSH_RECONNECT_MAX_ATTEMPTS = 3
|
|
10
|
+
export const SSH_RECONNECT_DELAYS_MS = [5_000, 10_000, 10_000]
|
|
11
|
+
export const SSH_RECONNECT_ATTEMPT_TIMEOUT_MS = 20_000
|
|
12
|
+
export const SSH_TOOL_TIMEOUT_MS = 300_000
|
|
13
|
+
export const SSH_TIMEOUT_MIN_MS = 1_000
|
|
14
|
+
export const SSH_TIMEOUT_MAX_MS = 300_000
|
|
15
|
+
export const SSH_EXEC_DEFAULT_TIMEOUT_MS = 30_000
|
|
16
|
+
export const SSH_SFTP_META_TIMEOUT_MS = 30_000
|
|
17
|
+
export const SSH_SFTP_TRANSFER_TIMEOUT_MS = 120_000
|
|
18
|
+
export const SSH_SHELL_OPEN_TIMEOUT_MS = 10_000
|
|
19
|
+
|
|
20
|
+
export const SSH_STATUS_CONNECTED = 'connected'
|
|
21
|
+
export const SSH_STATUS_RECONNECTING = 'reconnecting'
|
|
22
|
+
export const SSH_STATUS_DISCONNECTED = 'disconnected'
|
|
23
|
+
|
|
24
|
+
export function clampTimeoutMs(value, fallback) {
|
|
25
|
+
const n = Number(value)
|
|
26
|
+
const base = Number.isFinite(n) && n > 0 ? n : fallback
|
|
27
|
+
return Math.min(SSH_TIMEOUT_MAX_MS, Math.max(SSH_TIMEOUT_MIN_MS, Math.floor(base)))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function defaultTimeoutMsForAction(action) {
|
|
31
|
+
const a = String(action || '')
|
|
32
|
+
if (a === 'exec') return SSH_EXEC_DEFAULT_TIMEOUT_MS
|
|
33
|
+
if (a === 'sftp_read_text' || a === 'sftp_write_text' || a === 'sftp_upload' || a === 'sftp_download') {
|
|
34
|
+
return SSH_SFTP_TRANSFER_TIMEOUT_MS
|
|
35
|
+
}
|
|
36
|
+
if (a.startsWith('sftp_')) return SSH_SFTP_META_TIMEOUT_MS
|
|
37
|
+
if (a === 'read_session' || a === 'send_input' || a === 'resize_session') return SSH_SHELL_OPEN_TIMEOUT_MS
|
|
38
|
+
return SSH_EXEC_DEFAULT_TIMEOUT_MS
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function isLiveStatus(status, running) {
|
|
42
|
+
return status === SSH_STATUS_CONNECTED && running === true
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function findReusableLive(sessions, { projectPathKey, hostId, needsSftp }) {
|
|
46
|
+
return sessions
|
|
47
|
+
.filter((s) => {
|
|
48
|
+
if (s.projectPathKey !== projectPathKey || s.hostId !== hostId) return false
|
|
49
|
+
if (!isLiveStatus(s.status, s.running)) return false
|
|
50
|
+
if (needsSftp && !s.sftpEnabled) return false
|
|
51
|
+
return true
|
|
52
|
+
})
|
|
53
|
+
.sort((a, b) => a.createdAt - b.createdAt || String(a.id).localeCompare(String(b.id)))[0]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function pickOldestTombstone(sessions, { projectPathKey, hostId, needsSftp }) {
|
|
57
|
+
return sessions
|
|
58
|
+
.filter((s) => {
|
|
59
|
+
if (s.projectPathKey !== projectPathKey || s.hostId !== hostId) return false
|
|
60
|
+
if (s.status !== SSH_STATUS_DISCONNECTED || s.running) return false
|
|
61
|
+
if (needsSftp && !s.sftpEnabled) return false
|
|
62
|
+
return true
|
|
63
|
+
})
|
|
64
|
+
.sort((a, b) => a.createdAt - b.createdAt || String(a.id).localeCompare(String(b.id)))[0]
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function disconnectedSessionError() {
|
|
68
|
+
return 'SSH session is disconnected. Reconnect in the SSH Tunnel tab, or omit session_id to reuse_or_create.'
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function kbiReconnectError() {
|
|
72
|
+
return 'keyboard-interactive auth is not supported; edit the host to password or private key.'
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function kbiUnsupportedError() {
|
|
76
|
+
return 'keyboard-interactive auth is not supported; edit the host to password or private key.'
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function withTimeout(work, ms, { signal, onTimeout } = {}) {
|
|
80
|
+
const timeoutMs = clampTimeoutMs(ms, SSH_EXEC_DEFAULT_TIMEOUT_MS)
|
|
81
|
+
const run = typeof work === 'function' ? work() : work
|
|
82
|
+
return new Promise((resolve, reject) => {
|
|
83
|
+
let settled = false
|
|
84
|
+
const finish = (kind, value) => {
|
|
85
|
+
if (settled) return
|
|
86
|
+
settled = true
|
|
87
|
+
clearTimeout(timer)
|
|
88
|
+
if (signal) signal.removeEventListener('abort', onAbort)
|
|
89
|
+
if (kind === 'ok') resolve(value)
|
|
90
|
+
else reject(value)
|
|
91
|
+
}
|
|
92
|
+
const onAbort = () => {
|
|
93
|
+
try {
|
|
94
|
+
onTimeout?.()
|
|
95
|
+
} catch {}
|
|
96
|
+
const err = new Error('Cancelled')
|
|
97
|
+
err.aborted = true
|
|
98
|
+
finish('err', err)
|
|
99
|
+
}
|
|
100
|
+
const timer = setTimeout(() => {
|
|
101
|
+
try {
|
|
102
|
+
onTimeout?.()
|
|
103
|
+
} catch {}
|
|
104
|
+
const err = new Error('timeout')
|
|
105
|
+
err.timedOut = true
|
|
106
|
+
finish('err', err)
|
|
107
|
+
}, timeoutMs)
|
|
108
|
+
if (signal) {
|
|
109
|
+
if (signal.aborted) {
|
|
110
|
+
onAbort()
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
signal.addEventListener('abort', onAbort, { once: true })
|
|
114
|
+
}
|
|
115
|
+
Promise.resolve(run).then(
|
|
116
|
+
(v) => finish('ok', v),
|
|
117
|
+
(e) => finish('err', e),
|
|
118
|
+
)
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export const MAX_BYTES_MIN = 1_024
|
|
123
|
+
export const MAX_BYTES_MAX = 1_048_576
|
|
124
|
+
export const MAX_BYTES_EXEC_DEFAULT = 262_144
|
|
125
|
+
export const MAX_BYTES_SFTP_READ_DEFAULT = 524_288
|
|
126
|
+
|
|
127
|
+
export function clampMaxBytes(value, fallback) {
|
|
128
|
+
const n = Number(value)
|
|
129
|
+
const base = Number.isFinite(n) && n > 0 ? n : fallback
|
|
130
|
+
return Math.min(MAX_BYTES_MAX, Math.max(MAX_BYTES_MIN, Math.floor(base)))
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** HTTP status for an API error code; anything unmapped stays a server fault. */
|
|
134
|
+
export const API_ERROR_STATUS = {
|
|
135
|
+
bad_request: 400,
|
|
136
|
+
forbidden: 403,
|
|
137
|
+
not_found: 404,
|
|
138
|
+
conflict: 409,
|
|
139
|
+
gone: 410,
|
|
140
|
+
payload_too_large: 413,
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function sshError(code, message) {
|
|
144
|
+
const err = new Error(message)
|
|
145
|
+
err.code = code
|
|
146
|
+
return err
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function hostKeyPromptMessage() {
|
|
150
|
+
return 'Connection needs your confirmation: complete the trust/MFA prompt in the SSH Tunnel tab, then retry.'
|
|
151
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
/** Incremental shell output slice for polling clients. */
|
|
2
|
-
export function chunkFromBuffer(fullText, since) {
|
|
3
|
-
const full = fullText == null ? '' : String(fullText)
|
|
4
|
-
const start = Math.max(0, Math.min(Number(since) || 0, full.length))
|
|
5
|
-
return {
|
|
6
|
-
chunk: full.slice(start),
|
|
7
|
-
length: full.length,
|
|
8
|
-
since: start,
|
|
9
|
-
}
|
|
10
|
-
}
|
|
1
|
+
/** Incremental shell output slice for polling clients. */
|
|
2
|
+
export function chunkFromBuffer(fullText, since) {
|
|
3
|
+
const full = fullText == null ? '' : String(fullText)
|
|
4
|
+
const start = Math.max(0, Math.min(Number(since) || 0, full.length))
|
|
5
|
+
return {
|
|
6
|
+
chunk: full.slice(start),
|
|
7
|
+
length: full.length,
|
|
8
|
+
since: start,
|
|
9
|
+
}
|
|
10
|
+
}
|
package/lib/shared/vendor.js
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Same-origin static assets for the browser client.
|
|
3
|
-
*
|
|
4
|
-
* `dsh-ssh-tunnel/lib/client.js` is a raw browser bundle executed by the DSH
|
|
5
|
-
* client module system; its `require()` only answers platform seeds and
|
|
6
|
-
* registered plugin rows, so `@xterm/xterm` is NOT resolvable there. Instead
|
|
7
|
-
* of the removed jsDelivr fallback we serve the UMD builds from our own web
|
|
8
|
-
* route (`/dsh-ssh-tunnel/vendor/*`) and load them with plain <script>/<link>
|
|
9
|
-
* tags. This module is the host-side allowlist + resolver (Node only).
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { createRequire } from 'node:module'
|
|
13
|
-
|
|
14
|
-
const requireHost = createRequire(import.meta.url)
|
|
15
|
-
|
|
16
|
-
/** name -> { spec (resolved from this plugin's deps), content-type } */
|
|
17
|
-
export const VENDOR_ASSETS = {
|
|
18
|
-
'xterm.js': {
|
|
19
|
-
spec: '@xterm/xterm/lib/xterm.js',
|
|
20
|
-
type: 'text/javascript; charset=utf-8',
|
|
21
|
-
},
|
|
22
|
-
'xterm.css': {
|
|
23
|
-
spec: '@xterm/xterm/css/xterm.css',
|
|
24
|
-
type: 'text/css; charset=utf-8',
|
|
25
|
-
},
|
|
26
|
-
'addon-fit.js': {
|
|
27
|
-
spec: '@xterm/addon-fit/lib/addon-fit.js',
|
|
28
|
-
type: 'text/javascript; charset=utf-8',
|
|
29
|
-
},
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Resolve an allowlisted vendor asset to an absolute file path.
|
|
34
|
-
* Anything not in the allowlist throws — callers must treat that as 404 and
|
|
35
|
-
* must not interpolate user input into the file lookup.
|
|
36
|
-
* @param {string} name exact basename from the allowlist
|
|
37
|
-
* @returns {{ path: string, type: string }}
|
|
38
|
-
*/
|
|
39
|
-
export function resolveVendorAsset(name) {
|
|
40
|
-
const entry = VENDOR_ASSETS[name]
|
|
41
|
-
if (!entry) {
|
|
42
|
-
throw new Error(`dsh-ssh-tunnel: vendor asset not allowed: ${String(name)}`)
|
|
43
|
-
}
|
|
44
|
-
return { path: requireHost.resolve(entry.spec), type: entry.type }
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/** @param {string} name checked against the allowlist only */
|
|
48
|
-
export function isVendorAssetAllowed(name) {
|
|
49
|
-
return Object.prototype.hasOwnProperty.call(VENDOR_ASSETS, name)
|
|
1
|
+
/**
|
|
2
|
+
* Same-origin static assets for the browser client.
|
|
3
|
+
*
|
|
4
|
+
* `dsh-ssh-tunnel/lib/client.js` is a raw browser bundle executed by the DSH
|
|
5
|
+
* client module system; its `require()` only answers platform seeds and
|
|
6
|
+
* registered plugin rows, so `@xterm/xterm` is NOT resolvable there. Instead
|
|
7
|
+
* of the removed jsDelivr fallback we serve the UMD builds from our own web
|
|
8
|
+
* route (`/dsh-ssh-tunnel/vendor/*`) and load them with plain <script>/<link>
|
|
9
|
+
* tags. This module is the host-side allowlist + resolver (Node only).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { createRequire } from 'node:module'
|
|
13
|
+
|
|
14
|
+
const requireHost = createRequire(import.meta.url)
|
|
15
|
+
|
|
16
|
+
/** name -> { spec (resolved from this plugin's deps), content-type } */
|
|
17
|
+
export const VENDOR_ASSETS = {
|
|
18
|
+
'xterm.js': {
|
|
19
|
+
spec: '@xterm/xterm/lib/xterm.js',
|
|
20
|
+
type: 'text/javascript; charset=utf-8',
|
|
21
|
+
},
|
|
22
|
+
'xterm.css': {
|
|
23
|
+
spec: '@xterm/xterm/css/xterm.css',
|
|
24
|
+
type: 'text/css; charset=utf-8',
|
|
25
|
+
},
|
|
26
|
+
'addon-fit.js': {
|
|
27
|
+
spec: '@xterm/addon-fit/lib/addon-fit.js',
|
|
28
|
+
type: 'text/javascript; charset=utf-8',
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Resolve an allowlisted vendor asset to an absolute file path.
|
|
34
|
+
* Anything not in the allowlist throws — callers must treat that as 404 and
|
|
35
|
+
* must not interpolate user input into the file lookup.
|
|
36
|
+
* @param {string} name exact basename from the allowlist
|
|
37
|
+
* @returns {{ path: string, type: string }}
|
|
38
|
+
*/
|
|
39
|
+
export function resolveVendorAsset(name) {
|
|
40
|
+
const entry = VENDOR_ASSETS[name]
|
|
41
|
+
if (!entry) {
|
|
42
|
+
throw new Error(`dsh-ssh-tunnel: vendor asset not allowed: ${String(name)}`)
|
|
43
|
+
}
|
|
44
|
+
return { path: requireHost.resolve(entry.spec), type: entry.type }
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** @param {string} name checked against the allowlist only */
|
|
48
|
+
export function isVendorAssetAllowed(name) {
|
|
49
|
+
return Object.prototype.hasOwnProperty.call(VENDOR_ASSETS, name)
|
|
50
50
|
}
|
package/locale/en.json
ADDED
package/locale/zh.json
ADDED
package/package.json
CHANGED
|
@@ -1,84 +1,91 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "dsh-ssh-tunnel",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Multi-host SSH tunnel and SSHManager tool for DeepSeek Harness (project-scoped grants)",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"author": "thirsty5034",
|
|
8
|
-
"repository": {
|
|
9
|
-
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/OMSociety/dsh-ssh-tunnel.git"
|
|
11
|
-
},
|
|
12
|
-
"homepage": "https://github.com/OMSociety/dsh-ssh-tunnel#readme",
|
|
13
|
-
"bugs": {
|
|
14
|
-
"url": "https://github.com/OMSociety/dsh-ssh-tunnel/issues"
|
|
15
|
-
},
|
|
16
|
-
"contributors": [
|
|
17
|
-
{
|
|
18
|
-
"name": "OMSociety",
|
|
19
|
-
"url": "https://github.com/OMSociety"
|
|
20
|
-
}
|
|
21
|
-
],
|
|
22
|
-
"publishConfig": {
|
|
23
|
-
"access": "public"
|
|
24
|
-
},
|
|
25
|
-
"keywords": [
|
|
26
|
-
"dsh",
|
|
27
|
-
"dsh-plugin",
|
|
28
|
-
"deepseek-harness",
|
|
29
|
-
"ssh",
|
|
30
|
-
"sftp",
|
|
31
|
-
"cordis",
|
|
32
|
-
"better-sidebar",
|
|
33
|
-
"dsh-better-sidebar"
|
|
34
|
-
],
|
|
35
|
-
"engines": {
|
|
36
|
-
"node": ">=20",
|
|
37
|
-
"dsh": ">=0.1.7-rc.2 <0.3.0-0"
|
|
38
|
-
},
|
|
39
|
-
"main": "lib/index.js",
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
"./
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
"@
|
|
68
|
-
"
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"scripts/
|
|
83
|
-
|
|
84
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-ssh-tunnel",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "Multi-host SSH tunnel and SSHManager tool for DeepSeek Harness (project-scoped grants)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "thirsty5034",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/OMSociety/dsh-ssh-tunnel.git"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/OMSociety/dsh-ssh-tunnel#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/OMSociety/dsh-ssh-tunnel/issues"
|
|
15
|
+
},
|
|
16
|
+
"contributors": [
|
|
17
|
+
{
|
|
18
|
+
"name": "OMSociety",
|
|
19
|
+
"url": "https://github.com/OMSociety"
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"dsh",
|
|
27
|
+
"dsh-plugin",
|
|
28
|
+
"deepseek-harness",
|
|
29
|
+
"ssh",
|
|
30
|
+
"sftp",
|
|
31
|
+
"cordis",
|
|
32
|
+
"better-sidebar",
|
|
33
|
+
"dsh-better-sidebar"
|
|
34
|
+
],
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=20",
|
|
37
|
+
"dsh": ">=0.1.7-rc.2 <0.2.0-0 || >=0.2.0-rc.1 <0.3.0-0"
|
|
38
|
+
},
|
|
39
|
+
"main": "lib/index.js",
|
|
40
|
+
"icon": "./icon.svg",
|
|
41
|
+
"exports": {
|
|
42
|
+
".": "./lib/index.js",
|
|
43
|
+
"./locale/*.json": "./locale/*.json",
|
|
44
|
+
"./client": "./lib/client.js",
|
|
45
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
46
|
+
"./package.json": "./package.json"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"test": "node scripts/smoke-test.mjs",
|
|
50
|
+
"check": "node --check lib/index.js && node scripts/smoke-test.mjs"
|
|
51
|
+
},
|
|
52
|
+
"dsh": {
|
|
53
|
+
"manifestVersion": 1,
|
|
54
|
+
"bundle": {
|
|
55
|
+
"patch": "./cordis.patch.yml"
|
|
56
|
+
},
|
|
57
|
+
"client": {
|
|
58
|
+
"inject": [
|
|
59
|
+
"dsh-better-sidebar",
|
|
60
|
+
"@deepseek-ai/dsh-client-locale"
|
|
61
|
+
],
|
|
62
|
+
"platform": "web"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"@xterm/addon-fit": "0.11.0",
|
|
67
|
+
"@xterm/xterm": "5.5.0",
|
|
68
|
+
"ssh2": "^1.17.0"
|
|
69
|
+
},
|
|
70
|
+
"peerDependencies": {
|
|
71
|
+
"@deepseek-ai/cordis": "^4.0.4",
|
|
72
|
+
"@deepseek-ai/dsh-client-locale": ">=0.1.7-rc.2 <0.2.0-0 || >=0.2.0-rc.1 <0.3.0-0",
|
|
73
|
+
"dsh-better-sidebar": ">=0.12.0"
|
|
74
|
+
},
|
|
75
|
+
"files": [
|
|
76
|
+
"CHANGELOG.md",
|
|
77
|
+
"LICENSE",
|
|
78
|
+
"README.md",
|
|
79
|
+
"README_en.md",
|
|
80
|
+
"cordis.patch.yml",
|
|
81
|
+
"lib",
|
|
82
|
+
"scripts/install.sh",
|
|
83
|
+
"scripts/install.ps1",
|
|
84
|
+
"scripts/lib/*.cjs",
|
|
85
|
+
"scripts/portal-probe.mjs",
|
|
86
|
+
"scripts/smoke-test.mjs",
|
|
87
|
+
"scripts/sync-to-dsh.sh",
|
|
88
|
+
"icon.svg",
|
|
89
|
+
"locale/*.json"
|
|
90
|
+
]
|
|
91
|
+
}
|