@linxin666/dsh-ssh 0.3.6 → 0.3.9
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/lib/client.js +474 -471
- package/lib/client.js.map +1 -1
- package/lib/index.js +19 -14
- package/lib/types/client/index.d.ts +2 -1
- package/lib/types/client/locales.d.ts +1 -0
- package/lib/types/index.d.ts +2 -1
- package/package.json +17 -17
- package/src/client/api.ts +5 -1
- package/src/client/index.ts +4 -1
- package/src/client/locales.ts +2 -0
- package/src/dsh-home.ts +1 -1
- package/src/index.ts +27 -14
package/lib/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
2
1
|
import { closeSync, createReadStream, createWriteStream, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
3
2
|
import { Client } from "ssh2";
|
|
4
3
|
import { homedir, tmpdir } from "node:os";
|
|
@@ -3560,7 +3559,7 @@ const inject = [
|
|
|
3560
3559
|
* surface edits. Spelled here rather than imported: the browser half spells
|
|
3561
3560
|
* the same value and must not depend on a Host package.
|
|
3562
3561
|
*/
|
|
3563
|
-
const SSH_SETTINGS_NAMESPACE =
|
|
3562
|
+
const SSH_SETTINGS_NAMESPACE = "dsh-ssh";
|
|
3564
3563
|
const Config = import_lib.default.object({
|
|
3565
3564
|
announceToAgent: import_lib.default.boolean().default(false),
|
|
3566
3565
|
enabled: import_lib.default.boolean().default(true),
|
|
@@ -3580,18 +3579,21 @@ const SSH_GUIDANCE = "本机已安装 dsh-ssh 插件(DSH 远程 SSH 运维)
|
|
|
3580
3579
|
const apply = mountOnce("@linxin666/dsh-ssh", applyImpl);
|
|
3581
3580
|
function applyImpl(ctx, config) {
|
|
3582
3581
|
let current = () => config ?? {};
|
|
3582
|
+
let isSettingsBound = false;
|
|
3583
|
+
const store = new HostStore();
|
|
3584
|
+
const engine = new SshEngine(store);
|
|
3585
|
+
ctx.effect(() => () => {
|
|
3586
|
+
engine.dispose();
|
|
3587
|
+
}, "dsh-ssh: engine");
|
|
3583
3588
|
const resolve = () => {
|
|
3584
3589
|
const value = current();
|
|
3590
|
+
let enabled = value.enabled ?? true;
|
|
3591
|
+
if (enabled === false && !isSettingsBound && store.list().length > 0) enabled = true;
|
|
3585
3592
|
return {
|
|
3586
3593
|
announceToAgent: value.announceToAgent ?? DEFAULT_ANNOUNCE,
|
|
3587
|
-
enabled
|
|
3594
|
+
enabled
|
|
3588
3595
|
};
|
|
3589
3596
|
};
|
|
3590
|
-
const store = new HostStore();
|
|
3591
|
-
const engine = new SshEngine(store);
|
|
3592
|
-
ctx.effect(() => () => {
|
|
3593
|
-
engine.dispose();
|
|
3594
|
-
}, "dsh-ssh: engine");
|
|
3595
3597
|
const { routes, upgrade } = makeRoutes({
|
|
3596
3598
|
store,
|
|
3597
3599
|
engine
|
|
@@ -3642,12 +3644,15 @@ function applyImpl(ctx, config) {
|
|
|
3642
3644
|
};
|
|
3643
3645
|
}, "dsh-ssh: tools");
|
|
3644
3646
|
};
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3647
|
+
ctx.inject(["settings"], (settingsCtx) => {
|
|
3648
|
+
settingsCtx.settings.installSection(ctx, SSH_SETTINGS_NAMESPACE, Config, config ?? {}, {
|
|
3649
|
+
setSource: (source) => {
|
|
3650
|
+
isSettingsBound = true;
|
|
3651
|
+
current = source;
|
|
3652
|
+
sync();
|
|
3653
|
+
},
|
|
3654
|
+
onChange: sync
|
|
3655
|
+
});
|
|
3651
3656
|
});
|
|
3652
3657
|
sync();
|
|
3653
3658
|
}
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
* Export discipline (packages/client rule): the /client surface carries what
|
|
11
11
|
* cordis loading needs plus types only — all value exports stay internal.
|
|
12
12
|
*/
|
|
13
|
-
import type {
|
|
13
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
14
|
+
import type { SettingsScope, SettingsScopeSpec } from '@deepseek-ai/dsh-client-ui-settings/client';
|
|
14
15
|
import { type SshKey } from './locales.ts';
|
|
15
16
|
declare module '@deepseek-ai/cordis' {
|
|
16
17
|
interface Context {
|
|
@@ -146,6 +146,7 @@ export declare const zh: {
|
|
|
146
146
|
readonly 'common.error': "出错:{error}";
|
|
147
147
|
readonly 'common.refresh': "刷新";
|
|
148
148
|
readonly 'common.confirm': "确定";
|
|
149
|
+
readonly 'error.disabled': "SSH 插件在宿主端未启用。请前往「设置 → Web 插件 → SSH」打开 enabled 开关。";
|
|
149
150
|
};
|
|
150
151
|
export declare const en: Record<keyof typeof zh, string>;
|
|
151
152
|
/** Locale key union. */
|
package/lib/types/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* no dsh source changes.
|
|
9
9
|
*/
|
|
10
10
|
import type { Context } from '@deepseek-ai/cordis';
|
|
11
|
+
import type { SettingsNamespace } from '@deepseek-ai/dsh-settings';
|
|
11
12
|
import z from 'schemastery';
|
|
12
13
|
/** Stable cordis plugin name. */
|
|
13
14
|
export declare const name = "ssh";
|
|
@@ -18,7 +19,7 @@ export declare const inject: string[];
|
|
|
18
19
|
* surface edits. Spelled here rather than imported: the browser half spells
|
|
19
20
|
* the same value and must not depend on a Host package.
|
|
20
21
|
*/
|
|
21
|
-
export declare const SSH_SETTINGS_NAMESPACE:
|
|
22
|
+
export declare const SSH_SETTINGS_NAMESPACE: SettingsNamespace;
|
|
22
23
|
/** Plugin config, validated by the same-named schemastery schema. */
|
|
23
24
|
export interface Config {
|
|
24
25
|
/**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linxin666/dsh-ssh",
|
|
3
3
|
"description": "Remote SSH operations for the dsh web GUI: host config store (~/.dsh/dsh-ssh.json, import from ~/.ssh/config), a persistent ssh2 connection pool with jump-host support, exec / PTY web terminal / SFTP transfer / local port-forward tunnels / cluster execution, plus agent tools (ssh_list, ssh_exec, ssh_upload, ssh_download, ssh_tunnel, ssh_cluster). Hot-pluggable — mounted via ~/.dsh/cordis.patch.yml + a profile node_modules symlink, no dsh source changes.",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.9",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": "^22.19.0 || >=24.0.0"
|
|
@@ -26,15 +26,15 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dsh": {
|
|
28
28
|
"engines": {
|
|
29
|
-
"dsh": ">=0.1.
|
|
29
|
+
"dsh": ">=0.1.2-alpha.1"
|
|
30
30
|
},
|
|
31
31
|
"bundle": {
|
|
32
32
|
"patch": "./cordis.patch.yml"
|
|
33
33
|
},
|
|
34
34
|
"client": {
|
|
35
35
|
"inject": [
|
|
36
|
-
"@deepseek-ai/dsh-client-
|
|
37
|
-
"@deepseek-ai/dsh-client-
|
|
36
|
+
"@deepseek-ai/dsh-client-locale",
|
|
37
|
+
"@deepseek-ai/dsh-client-ui-renderer",
|
|
38
38
|
"@deepseek-ai/dsh-client-ui-settings"
|
|
39
39
|
],
|
|
40
40
|
"platform": "web"
|
|
@@ -51,19 +51,19 @@
|
|
|
51
51
|
"react-dom": "^18.2.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
55
|
-
"@deepseek-ai/dsh-client-connection": "^0.1.
|
|
56
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.
|
|
57
|
-
"@deepseek-ai/dsh-client-
|
|
58
|
-
"@deepseek-ai/dsh-client-ui-settings": "^0.1.
|
|
59
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.
|
|
60
|
-
"@deepseek-ai/dsh-host-webserver": "^0.1.
|
|
61
|
-
"@deepseek-ai/dsh-llm": "^0.1.
|
|
62
|
-
"@deepseek-ai/dsh-scope": "^0.1.
|
|
63
|
-
"@deepseek-ai/dsh-settings": "^0.1.
|
|
64
|
-
"@deepseek-ai/dsh-system-prompt": "^0.1.
|
|
65
|
-
"@deepseek-ai/dsh-timeout": "^0.1.
|
|
66
|
-
"@deepseek-ai/dsh-tools": "^0.1.
|
|
54
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
55
|
+
"@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.2",
|
|
56
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.2",
|
|
57
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.2",
|
|
58
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.2",
|
|
59
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.2-alpha.2",
|
|
60
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.2",
|
|
61
|
+
"@deepseek-ai/dsh-llm": "^0.1.2-alpha.2",
|
|
62
|
+
"@deepseek-ai/dsh-scope": "^0.1.2-alpha.2",
|
|
63
|
+
"@deepseek-ai/dsh-settings": "^0.1.2-alpha.2",
|
|
64
|
+
"@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.2",
|
|
65
|
+
"@deepseek-ai/dsh-timeout": "^0.1.2-alpha.2",
|
|
66
|
+
"@deepseek-ai/dsh-tools": "^0.1.2-alpha.2",
|
|
67
67
|
"@types/node": "^22.20.0",
|
|
68
68
|
"@types/react": "~18.3.1",
|
|
69
69
|
"@types/react-dom": "^18.3.5",
|
package/src/client/api.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
type TransferStreamLine,
|
|
19
19
|
type TunnelInfo,
|
|
20
20
|
} from '../protocol.ts'
|
|
21
|
+
import { tt } from './panel/helpers.ts'
|
|
21
22
|
|
|
22
23
|
/** Minimal File System Access API surface (not in all lib.dom versions). */
|
|
23
24
|
interface WindowWithFileSystemAccess {
|
|
@@ -51,12 +52,15 @@ async function readJson<T>(response: Response): Promise<T> {
|
|
|
51
52
|
try {
|
|
52
53
|
body = await response.json()
|
|
53
54
|
} catch {
|
|
55
|
+
if (response.status === 404) {
|
|
56
|
+
throw new SshApiError(tt('error.disabled'))
|
|
57
|
+
}
|
|
54
58
|
throw new SshApiError(`HTTP ${response.status}: invalid JSON response`)
|
|
55
59
|
}
|
|
56
60
|
if (!response.ok) {
|
|
57
61
|
const message = typeof body === 'object' && body !== null && typeof (body as { error?: unknown }).error === 'string'
|
|
58
62
|
? (body as { error: string }).error
|
|
59
|
-
: `HTTP ${response.status}`
|
|
63
|
+
: (response.status === 404 ? tt('error.disabled') : `HTTP ${response.status}`)
|
|
60
64
|
throw new SshApiError(message)
|
|
61
65
|
}
|
|
62
66
|
return body as T
|
package/src/client/index.ts
CHANGED
|
@@ -10,11 +10,14 @@
|
|
|
10
10
|
* Export discipline (packages/client rule): the /client surface carries what
|
|
11
11
|
* cordis loading needs plus types only — all value exports stay internal.
|
|
12
12
|
*/
|
|
13
|
-
import type {
|
|
13
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis'
|
|
14
|
+
import type { SettingsScope, SettingsScopeSpec } from '@deepseek-ai/dsh-client-ui-settings/client'
|
|
14
15
|
// Type-only: pulls the locale plugin's Context merge (ctx.locale).
|
|
15
16
|
import type {} from '@deepseek-ai/dsh-client-locale/client'
|
|
16
17
|
// Type-only: pulls the settings-surface Context merge (ctx.settingsScope).
|
|
17
18
|
import type {} from '@deepseek-ai/dsh-client-ui-settings/client'
|
|
19
|
+
// Type-only: pulls the ctx.slots merge (the renderer owns the slot registry since 0.1.2).
|
|
20
|
+
import type {} from '@deepseek-ai/dsh-client-ui-renderer/client'
|
|
18
21
|
// Type-only: pulls the LocaleNamespaceMap merge table.
|
|
19
22
|
import type {} from '@deepseek-ai/dsh-client-ui-slots'
|
|
20
23
|
import { SshApi } from './api.ts'
|
package/src/client/locales.ts
CHANGED
|
@@ -154,6 +154,7 @@ export const zh = {
|
|
|
154
154
|
'common.error': '出错:{error}',
|
|
155
155
|
'common.refresh': '刷新',
|
|
156
156
|
'common.confirm': '确定',
|
|
157
|
+
'error.disabled': 'SSH 插件在宿主端未启用。请前往「设置 → Web 插件 → SSH」打开 enabled 开关。',
|
|
157
158
|
} as const
|
|
158
159
|
|
|
159
160
|
export const en: Record<keyof typeof zh, string> = {
|
|
@@ -301,6 +302,7 @@ export const en: Record<keyof typeof zh, string> = {
|
|
|
301
302
|
'common.error': 'Error: {error}',
|
|
302
303
|
'common.refresh': 'Refresh',
|
|
303
304
|
'common.confirm': 'Confirm',
|
|
305
|
+
'error.disabled': 'The SSH plugin is disabled on the host. Please enable it in Settings → Web Plugins → SSH.',
|
|
304
306
|
}
|
|
305
307
|
|
|
306
308
|
/** Locale key union. */
|
package/src/dsh-home.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { Context } from '@deepseek-ai/cordis'
|
|
12
|
-
import {
|
|
12
|
+
import type { SettingsNamespace } from '@deepseek-ai/dsh-settings'
|
|
13
13
|
import z from 'schemastery'
|
|
14
14
|
import type {} from '@deepseek-ai/dsh-host-webserver'
|
|
15
15
|
import type {} from '@deepseek-ai/dsh-system-prompt'
|
|
@@ -31,7 +31,7 @@ export const inject = ['webServer', 'tools', 'systemPrompt']
|
|
|
31
31
|
* surface edits. Spelled here rather than imported: the browser half spells
|
|
32
32
|
* the same value and must not depend on a Host package.
|
|
33
33
|
*/
|
|
34
|
-
export const SSH_SETTINGS_NAMESPACE =
|
|
34
|
+
export const SSH_SETTINGS_NAMESPACE = 'dsh-ssh' as SettingsNamespace
|
|
35
35
|
|
|
36
36
|
/** Plugin config, validated by the same-named schemastery schema. */
|
|
37
37
|
export interface Config {
|
|
@@ -77,18 +77,28 @@ function applyImpl(ctx: Context, config?: Config): void {
|
|
|
77
77
|
// The live source the surfaces read: the settings section once the web
|
|
78
78
|
// settings surface is served, the composition entry otherwise.
|
|
79
79
|
let current: () => Config = () => config ?? {}
|
|
80
|
+
let isSettingsBound = false
|
|
81
|
+
|
|
82
|
+
const store = new HostStore()
|
|
83
|
+
const engine = new SshEngine(store)
|
|
84
|
+
ctx.effect(() => () => { engine.dispose() }, 'dsh-ssh: engine')
|
|
85
|
+
|
|
80
86
|
const resolve = (): Config => {
|
|
81
87
|
const value = current()
|
|
88
|
+
let enabled = value.enabled ?? true
|
|
89
|
+
// When dsh-ssh was seeded with enabled: false (e.g. from an aggregate profile line),
|
|
90
|
+
// but the user has never explicitly configured settings (not bound yet)
|
|
91
|
+
// AND already has active host records in dsh-ssh.json, keep the plugin enabled so
|
|
92
|
+
// existing users are not broken upon upgrade (#1250).
|
|
93
|
+
if (enabled === false && !isSettingsBound && store.list().length > 0) {
|
|
94
|
+
enabled = true
|
|
95
|
+
}
|
|
82
96
|
return {
|
|
83
97
|
announceToAgent: value.announceToAgent ?? DEFAULT_ANNOUNCE,
|
|
84
|
-
enabled
|
|
98
|
+
enabled,
|
|
85
99
|
}
|
|
86
100
|
}
|
|
87
101
|
|
|
88
|
-
const store = new HostStore()
|
|
89
|
-
const engine = new SshEngine(store)
|
|
90
|
-
ctx.effect(() => () => { engine.dispose() }, 'dsh-ssh: engine')
|
|
91
|
-
|
|
92
102
|
// The /api/dsh-ssh route family + terminal upgrade.
|
|
93
103
|
const { routes, upgrade } = makeRoutes({ store, engine })
|
|
94
104
|
let disposeRoutes: (() => void) | undefined
|
|
@@ -152,15 +162,18 @@ function applyImpl(ctx: Context, config?: Config): void {
|
|
|
152
162
|
)
|
|
153
163
|
}
|
|
154
164
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
165
|
+
ctx.inject(['settings'], (settingsCtx) => {
|
|
166
|
+
settingsCtx.settings.installSection(ctx, SSH_SETTINGS_NAMESPACE, Config, config ?? {}, {
|
|
167
|
+
setSource: (source) => {
|
|
168
|
+
isSettingsBound = true
|
|
169
|
+
current = source
|
|
170
|
+
sync()
|
|
171
|
+
},
|
|
172
|
+
onChange: sync,
|
|
173
|
+
})
|
|
161
174
|
})
|
|
162
175
|
|
|
163
176
|
// Initial registration from the composition entry (covers deployments with
|
|
164
|
-
// no settings service, whose
|
|
177
|
+
// no settings service, whose installSection never fires its hooks).
|
|
165
178
|
sync()
|
|
166
179
|
}
|