dsh-plugin-pwsh7 0.1.0 → 0.1.1
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 +33 -0
- package/LICENSE +21 -21
- package/README.md +219 -186
- package/README.zh.md +208 -176
- package/cordis.patch.yml +16 -16
- package/icon.svg +28 -28
- package/index.js +126 -126
- package/lib/executors/pwsh.mjs +20 -20
- package/lib/install.mjs +978 -978
- package/lib/paths.mjs +327 -327
- package/lib/shipped.mjs +115 -115
- package/lib/state.mjs +129 -129
- package/lib/status.mjs +187 -187
- package/locale/en.json +6 -6
- package/locale/zh.json +6 -6
- package/package.json +62 -60
package/index.js
CHANGED
|
@@ -1,126 +1,126 @@
|
|
|
1
|
-
// dsh-plugin-pwsh7 — host plugin entry.
|
|
2
|
-
//
|
|
3
|
-
// Routes the model-facing `pwsh` tool to PowerShell 7 and exposes a `shell_router` tool for
|
|
4
|
-
// status, diagnosis, and per-user installation. The executor class lives in
|
|
5
|
-
// ./lib/executors/pwsh.mjs and is mounted by ./cordis.patch.yml.
|
|
6
|
-
|
|
7
|
-
import { defineTool } from './lib/shipped.mjs';
|
|
8
|
-
import { configPath, getRouterState, refreshRouterState } from './lib/state.mjs';
|
|
9
|
-
import { ensurePwsh } from './lib/install.mjs';
|
|
10
|
-
import { describeStatus, runDiagnosis } from './lib/status.mjs';
|
|
11
|
-
|
|
12
|
-
export const name = 'pwsh7-router';
|
|
13
|
-
export const inject = ['tools', 'systemPrompt'];
|
|
14
|
-
|
|
15
|
-
function formatBytes(bytes) {
|
|
16
|
-
if (!Number.isFinite(bytes) || bytes <= 0) return '0 B';
|
|
17
|
-
if (bytes >= 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GiB`;
|
|
18
|
-
if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MiB`;
|
|
19
|
-
if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KiB`;
|
|
20
|
-
return `${bytes} B`;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Build the `shell_router` tool definition. Exported so tests/smoke harnesses can compile and
|
|
25
|
-
* inspect it without registering a second copy in the live tool registry.
|
|
26
|
-
*/
|
|
27
|
-
export function createToolDefinition(ctx) {
|
|
28
|
-
return defineTool({
|
|
29
|
-
name: 'shell_router',
|
|
30
|
-
description: [
|
|
31
|
-
'Inspect or repair how this Windows deployment routes the `pwsh` tool.',
|
|
32
|
-
'`status` (default) reports the resolved PowerShell 7 executable and version, the config file, and the active sandbox policy.',
|
|
33
|
-
'`diagnose` runs real PowerShell 7 commands under the current sandbox policy and reports failures with concrete hints.',
|
|
34
|
-
'`install` downloads and installs PowerShell 7 per-user under %LOCALAPPDATA%\\dsh\\pwsh7 (no admin), or falls back to winget.',
|
|
35
|
-
'Use it when `pwsh` reports that PowerShell 7 is missing, or when the user asks to set up PowerShell 7.',
|
|
36
|
-
].join(' '),
|
|
37
|
-
parameters: {
|
|
38
|
-
action: {
|
|
39
|
-
type: 'string',
|
|
40
|
-
enum: ['status', 'diagnose', 'install'],
|
|
41
|
-
description: 'Operation to perform; omit for `status`.',
|
|
42
|
-
},
|
|
43
|
-
method: {
|
|
44
|
-
type: 'string',
|
|
45
|
-
enum: ['auto', 'portable', 'winget'],
|
|
46
|
-
description: 'For `install`: installation strategy. `auto` prefers the per-user portable zip.',
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
output: {
|
|
50
|
-
schema: {
|
|
51
|
-
type: 'object',
|
|
52
|
-
additionalProperties: false,
|
|
53
|
-
properties: {
|
|
54
|
-
ok: { type: 'boolean', required: true, description: 'Whether the requested operation succeeded.' },
|
|
55
|
-
summary: { type: 'string', required: true, description: 'Human-readable status or diagnosis.' },
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
render: (_args, value) => [{ type: 'text', text: value.summary }],
|
|
59
|
-
},
|
|
60
|
-
async execute(args, exec) {
|
|
61
|
-
const action = args.action ?? 'status';
|
|
62
|
-
if (action === 'status') {
|
|
63
|
-
const state = await refreshRouterState();
|
|
64
|
-
return { ok: true, summary: await describeStatus(ctx, state, exec) };
|
|
65
|
-
}
|
|
66
|
-
if (action === 'diagnose') {
|
|
67
|
-
const state = await refreshRouterState();
|
|
68
|
-
const result = await runDiagnosis(ctx, state, exec);
|
|
69
|
-
return { ok: result.ok, summary: result.summary };
|
|
70
|
-
}
|
|
71
|
-
const progress = [];
|
|
72
|
-
let lastProgressAt = 0;
|
|
73
|
-
const installed = await ensurePwsh({
|
|
74
|
-
method: args.method ?? 'auto',
|
|
75
|
-
onProgress: (bytes, totalBytes) => {
|
|
76
|
-
const at = Date.now();
|
|
77
|
-
if (at - lastProgressAt < 4000) return;
|
|
78
|
-
lastProgressAt = at;
|
|
79
|
-
if (Number.isFinite(totalBytes) && totalBytes > 0) {
|
|
80
|
-
progress.push(`download progress: ${formatBytes(bytes)} / ${formatBytes(totalBytes)} (${Math.round((bytes / totalBytes) * 100)}%)`);
|
|
81
|
-
} else if (Number.isFinite(bytes) && bytes > 0) {
|
|
82
|
-
progress.push(`downloaded ${formatBytes(bytes)}`);
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
signal: exec.signal,
|
|
86
|
-
});
|
|
87
|
-
const state = await refreshRouterState();
|
|
88
|
-
const result = installed.result ?? {};
|
|
89
|
-
const resultLine = result.ok
|
|
90
|
-
? `pwsh: ok (${result.method ?? 'unknown'}${result.version === undefined ? '' : `, ${result.version}`}${result.path === undefined ? '' : ` at ${result.path}`})`
|
|
91
|
-
: `pwsh: failed — ${result.message ?? installed.message ?? 'unknown error'}`;
|
|
92
|
-
const summary = [
|
|
93
|
-
installed.ok ? 'PowerShell 7 install completed.' : 'PowerShell 7 install finished with problems.',
|
|
94
|
-
resultLine,
|
|
95
|
-
...(Array.isArray(installed.notes) ? installed.notes : []),
|
|
96
|
-
...progress.slice(-8),
|
|
97
|
-
'',
|
|
98
|
-
await describeStatus(ctx, state, exec),
|
|
99
|
-
].join('\n');
|
|
100
|
-
return { ok: installed.ok === true, summary };
|
|
101
|
-
},
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export function apply(ctx) {
|
|
106
|
-
ctx.systemPrompt.section({
|
|
107
|
-
name: 'tool:pwsh7-router',
|
|
108
|
-
order: ctx.systemPrompt.getSectionOrder('TOOL_PWSH') + 1,
|
|
109
|
-
text: [
|
|
110
|
-
'On Windows this deployment routes the `pwsh` tool to PowerShell 7.',
|
|
111
|
-
'If PowerShell 7 is missing or misconfigured, call `shell_router`: action `status` reports the resolved executable,',
|
|
112
|
-
'`diagnose` runs real commands under the current sandbox policy, and `install` downloads a per-user copy into',
|
|
113
|
-
'%LOCALAPPDATA%\\dsh\\pwsh7 (no administrator rights) with a winget fallback.',
|
|
114
|
-
'A shell installed this way becomes usable without restarting the app.',
|
|
115
|
-
].join(' '),
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
ctx.tools.register(createToolDefinition(ctx));
|
|
119
|
-
|
|
120
|
-
ctx.on('app-boot/config-reload', () => {
|
|
121
|
-
// Detection is cheap and must never break the host reload path.
|
|
122
|
-
refreshRouterState().catch(() => {});
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export { configPath, getRouterState };
|
|
1
|
+
// dsh-plugin-pwsh7 — host plugin entry.
|
|
2
|
+
//
|
|
3
|
+
// Routes the model-facing `pwsh` tool to PowerShell 7 and exposes a `shell_router` tool for
|
|
4
|
+
// status, diagnosis, and per-user installation. The executor class lives in
|
|
5
|
+
// ./lib/executors/pwsh.mjs and is mounted by ./cordis.patch.yml.
|
|
6
|
+
|
|
7
|
+
import { defineTool } from './lib/shipped.mjs';
|
|
8
|
+
import { configPath, getRouterState, refreshRouterState } from './lib/state.mjs';
|
|
9
|
+
import { ensurePwsh } from './lib/install.mjs';
|
|
10
|
+
import { describeStatus, runDiagnosis } from './lib/status.mjs';
|
|
11
|
+
|
|
12
|
+
export const name = 'pwsh7-router';
|
|
13
|
+
export const inject = ['tools', 'systemPrompt'];
|
|
14
|
+
|
|
15
|
+
function formatBytes(bytes) {
|
|
16
|
+
if (!Number.isFinite(bytes) || bytes <= 0) return '0 B';
|
|
17
|
+
if (bytes >= 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GiB`;
|
|
18
|
+
if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MiB`;
|
|
19
|
+
if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KiB`;
|
|
20
|
+
return `${bytes} B`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Build the `shell_router` tool definition. Exported so tests/smoke harnesses can compile and
|
|
25
|
+
* inspect it without registering a second copy in the live tool registry.
|
|
26
|
+
*/
|
|
27
|
+
export function createToolDefinition(ctx) {
|
|
28
|
+
return defineTool({
|
|
29
|
+
name: 'shell_router',
|
|
30
|
+
description: [
|
|
31
|
+
'Inspect or repair how this Windows deployment routes the `pwsh` tool.',
|
|
32
|
+
'`status` (default) reports the resolved PowerShell 7 executable and version, the config file, and the active sandbox policy.',
|
|
33
|
+
'`diagnose` runs real PowerShell 7 commands under the current sandbox policy and reports failures with concrete hints.',
|
|
34
|
+
'`install` downloads and installs PowerShell 7 per-user under %LOCALAPPDATA%\\dsh\\pwsh7 (no admin), or falls back to winget.',
|
|
35
|
+
'Use it when `pwsh` reports that PowerShell 7 is missing, or when the user asks to set up PowerShell 7.',
|
|
36
|
+
].join(' '),
|
|
37
|
+
parameters: {
|
|
38
|
+
action: {
|
|
39
|
+
type: 'string',
|
|
40
|
+
enum: ['status', 'diagnose', 'install'],
|
|
41
|
+
description: 'Operation to perform; omit for `status`.',
|
|
42
|
+
},
|
|
43
|
+
method: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
enum: ['auto', 'portable', 'winget'],
|
|
46
|
+
description: 'For `install`: installation strategy. `auto` prefers the per-user portable zip.',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
output: {
|
|
50
|
+
schema: {
|
|
51
|
+
type: 'object',
|
|
52
|
+
additionalProperties: false,
|
|
53
|
+
properties: {
|
|
54
|
+
ok: { type: 'boolean', required: true, description: 'Whether the requested operation succeeded.' },
|
|
55
|
+
summary: { type: 'string', required: true, description: 'Human-readable status or diagnosis.' },
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
render: (_args, value) => [{ type: 'text', text: value.summary }],
|
|
59
|
+
},
|
|
60
|
+
async execute(args, exec) {
|
|
61
|
+
const action = args.action ?? 'status';
|
|
62
|
+
if (action === 'status') {
|
|
63
|
+
const state = await refreshRouterState();
|
|
64
|
+
return { ok: true, summary: await describeStatus(ctx, state, exec) };
|
|
65
|
+
}
|
|
66
|
+
if (action === 'diagnose') {
|
|
67
|
+
const state = await refreshRouterState();
|
|
68
|
+
const result = await runDiagnosis(ctx, state, exec);
|
|
69
|
+
return { ok: result.ok, summary: result.summary };
|
|
70
|
+
}
|
|
71
|
+
const progress = [];
|
|
72
|
+
let lastProgressAt = 0;
|
|
73
|
+
const installed = await ensurePwsh({
|
|
74
|
+
method: args.method ?? 'auto',
|
|
75
|
+
onProgress: (bytes, totalBytes) => {
|
|
76
|
+
const at = Date.now();
|
|
77
|
+
if (at - lastProgressAt < 4000) return;
|
|
78
|
+
lastProgressAt = at;
|
|
79
|
+
if (Number.isFinite(totalBytes) && totalBytes > 0) {
|
|
80
|
+
progress.push(`download progress: ${formatBytes(bytes)} / ${formatBytes(totalBytes)} (${Math.round((bytes / totalBytes) * 100)}%)`);
|
|
81
|
+
} else if (Number.isFinite(bytes) && bytes > 0) {
|
|
82
|
+
progress.push(`downloaded ${formatBytes(bytes)}`);
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
signal: exec.signal,
|
|
86
|
+
});
|
|
87
|
+
const state = await refreshRouterState();
|
|
88
|
+
const result = installed.result ?? {};
|
|
89
|
+
const resultLine = result.ok
|
|
90
|
+
? `pwsh: ok (${result.method ?? 'unknown'}${result.version === undefined ? '' : `, ${result.version}`}${result.path === undefined ? '' : ` at ${result.path}`})`
|
|
91
|
+
: `pwsh: failed — ${result.message ?? installed.message ?? 'unknown error'}`;
|
|
92
|
+
const summary = [
|
|
93
|
+
installed.ok ? 'PowerShell 7 install completed.' : 'PowerShell 7 install finished with problems.',
|
|
94
|
+
resultLine,
|
|
95
|
+
...(Array.isArray(installed.notes) ? installed.notes : []),
|
|
96
|
+
...progress.slice(-8),
|
|
97
|
+
'',
|
|
98
|
+
await describeStatus(ctx, state, exec),
|
|
99
|
+
].join('\n');
|
|
100
|
+
return { ok: installed.ok === true, summary };
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function apply(ctx) {
|
|
106
|
+
ctx.systemPrompt.section({
|
|
107
|
+
name: 'tool:pwsh7-router',
|
|
108
|
+
order: ctx.systemPrompt.getSectionOrder('TOOL_PWSH') + 1,
|
|
109
|
+
text: [
|
|
110
|
+
'On Windows this deployment routes the `pwsh` tool to PowerShell 7.',
|
|
111
|
+
'If PowerShell 7 is missing or misconfigured, call `shell_router`: action `status` reports the resolved executable,',
|
|
112
|
+
'`diagnose` runs real commands under the current sandbox policy, and `install` downloads a per-user copy into',
|
|
113
|
+
'%LOCALAPPDATA%\\dsh\\pwsh7 (no administrator rights) with a winget fallback.',
|
|
114
|
+
'A shell installed this way becomes usable without restarting the app.',
|
|
115
|
+
].join(' '),
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
ctx.tools.register(createToolDefinition(ctx));
|
|
119
|
+
|
|
120
|
+
ctx.on('app-boot/config-reload', () => {
|
|
121
|
+
// Detection is cheap and must never break the host reload path.
|
|
122
|
+
refreshRouterState().catch(() => {});
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export { configPath, getRouterState };
|
package/lib/executors/pwsh.mjs
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
// PowerShell 7 executor row for Windows.
|
|
2
|
-
//
|
|
3
|
-
// The shipped SandboxPwshExecutor keeps every sandbox behavior (escalation fields, denial
|
|
4
|
-
// facts, jobs, output caps) but resolves its executable once from config/well-known locations.
|
|
5
|
-
// This subclass replaces only that resolution: an explicit row `config.pwshPath` still wins,
|
|
6
|
-
// otherwise the shared router state resolves PowerShell 7 (and raises a setup hint when absent).
|
|
7
|
-
|
|
8
|
-
import { shipped } from '../shipped.mjs';
|
|
9
|
-
import { requirePwshPath } from '../state.mjs';
|
|
10
|
-
|
|
11
|
-
const { SandboxPwshExecutor } = shipped('@deepseek-ai/dsh-pwsh-sandbox');
|
|
12
|
-
|
|
13
|
-
export default class WindowsRouterPwshExecutor extends SandboxPwshExecutor {
|
|
14
|
-
/** @returns {string} resolved PowerShell 7 executable. */
|
|
15
|
-
get pwshPath() {
|
|
16
|
-
const declared = this.config?.pwshPath?.get?.();
|
|
17
|
-
if (typeof declared === 'string' && declared.trim().length > 0) return declared;
|
|
18
|
-
return requirePwshPath();
|
|
19
|
-
}
|
|
20
|
-
}
|
|
1
|
+
// PowerShell 7 executor row for Windows.
|
|
2
|
+
//
|
|
3
|
+
// The shipped SandboxPwshExecutor keeps every sandbox behavior (escalation fields, denial
|
|
4
|
+
// facts, jobs, output caps) but resolves its executable once from config/well-known locations.
|
|
5
|
+
// This subclass replaces only that resolution: an explicit row `config.pwshPath` still wins,
|
|
6
|
+
// otherwise the shared router state resolves PowerShell 7 (and raises a setup hint when absent).
|
|
7
|
+
|
|
8
|
+
import { shipped } from '../shipped.mjs';
|
|
9
|
+
import { requirePwshPath } from '../state.mjs';
|
|
10
|
+
|
|
11
|
+
const { SandboxPwshExecutor } = shipped('@deepseek-ai/dsh-pwsh-sandbox');
|
|
12
|
+
|
|
13
|
+
export default class WindowsRouterPwshExecutor extends SandboxPwshExecutor {
|
|
14
|
+
/** @returns {string} resolved PowerShell 7 executable. */
|
|
15
|
+
get pwshPath() {
|
|
16
|
+
const declared = this.config?.pwshPath?.get?.();
|
|
17
|
+
if (typeof declared === 'string' && declared.trim().length > 0) return declared;
|
|
18
|
+
return requirePwshPath();
|
|
19
|
+
}
|
|
20
|
+
}
|