dsh-winrm 0.1.0

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 (64) hide show
  1. package/LICENSE +73 -0
  2. package/README.md +112 -0
  3. package/README.zh.md +119 -0
  4. package/RELEASING.md +75 -0
  5. package/cordis.patch.yml +8 -0
  6. package/lib/client.js +2076 -0
  7. package/lib/index.js +2458 -0
  8. package/lib/invariant.js +6 -0
  9. package/lib/types/client/api.d.ts +59 -0
  10. package/lib/types/client/index.d.ts +38 -0
  11. package/lib/types/client/locales.d.ts +231 -0
  12. package/lib/types/client/panel/ConsoleTab.d.ts +11 -0
  13. package/lib/types/client/panel/HostFormDialog.d.ts +12 -0
  14. package/lib/types/client/panel/HostsTab.d.ts +9 -0
  15. package/lib/types/client/panel/ProcessesTab.d.ts +7 -0
  16. package/lib/types/client/panel/ServicesTab.d.ts +7 -0
  17. package/lib/types/client/panel/TransferTab.d.ts +7 -0
  18. package/lib/types/client/panel/WinrmPanel.d.ts +12 -0
  19. package/lib/types/client/panel/WinrmPanelIcon.d.ts +8 -0
  20. package/lib/types/client/panel/helpers.d.ts +16 -0
  21. package/lib/types/engine/client.d.ts +49 -0
  22. package/lib/types/engine/console.d.ts +23 -0
  23. package/lib/types/engine.d.ts +58 -0
  24. package/lib/types/index.d.ts +44 -0
  25. package/lib/types/invariant.d.ts +3 -0
  26. package/lib/types/loopback.d.ts +24 -0
  27. package/lib/types/mount-once.d.ts +24 -0
  28. package/lib/types/powershell.d.ts +73 -0
  29. package/lib/types/protocol.d.ts +176 -0
  30. package/lib/types/routes.d.ts +26 -0
  31. package/lib/types/smb.d.ts +31 -0
  32. package/lib/types/store.d.ts +44 -0
  33. package/lib/types/tools.d.ts +20 -0
  34. package/package.json +111 -0
  35. package/scripts/enable-winrm.ps1 +0 -0
  36. package/scripts/postbuild.mjs +40 -0
  37. package/scripts/validate-manifest.mjs +18 -0
  38. package/src/client/api.ts +345 -0
  39. package/src/client/css-modules.d.ts +5 -0
  40. package/src/client/index.ts +82 -0
  41. package/src/client/locales.ts +243 -0
  42. package/src/client/panel/ConsoleTab.tsx +142 -0
  43. package/src/client/panel/HostFormDialog.tsx +142 -0
  44. package/src/client/panel/HostsTab.tsx +153 -0
  45. package/src/client/panel/ProcessesTab.tsx +119 -0
  46. package/src/client/panel/ServicesTab.tsx +123 -0
  47. package/src/client/panel/TransferTab.tsx +161 -0
  48. package/src/client/panel/WinrmPanel.tsx +85 -0
  49. package/src/client/panel/WinrmPanelIcon.tsx +27 -0
  50. package/src/client/panel/helpers.ts +33 -0
  51. package/src/client/panel/panel.module.css +1000 -0
  52. package/src/engine/client.ts +291 -0
  53. package/src/engine/console.ts +62 -0
  54. package/src/engine.ts +331 -0
  55. package/src/index.ts +142 -0
  56. package/src/invariant.ts +4 -0
  57. package/src/loopback.ts +63 -0
  58. package/src/mount-once.ts +48 -0
  59. package/src/powershell.ts +208 -0
  60. package/src/protocol.ts +178 -0
  61. package/src/routes.ts +490 -0
  62. package/src/smb.ts +129 -0
  63. package/src/store.ts +232 -0
  64. package/src/tools.ts +387 -0
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Loopback trust fence shared by the host route families: socket address,
3
+ * Host header, and browser same-origin markers. Packages receive this file as
4
+ * a generated copy via scripts/sync-shared.mjs; edit the shared source and
5
+ * re-run the sync instead of editing a copy.
6
+ *
7
+ * Semantics: RFC 5735 IPv4 127/8, ::1, IPv4-mapped ::ffff:127/8 (matching the
8
+ * remote-web-ui gate), localhost hostnames, plus the browser same-origin
9
+ * markers (sec-fetch-site and Origin) for the request-level fence.
10
+ * @module dsh-web-ui-shared/host/loopback
11
+ */
12
+ import type { IncomingMessage } from 'node:http';
13
+ /** IPv4 127/8 predicate (four decimal octets, first == 127). */
14
+ export declare function isIPv4Loopback(v4: string): boolean;
15
+ /** Whether a socket remote address names the loopback range (127/8, ::1, IPv4-mapped). */
16
+ export declare function isLoopbackAddress(address: string | undefined): boolean;
17
+ /** Whether a normalized URL hostname names the loopback authority (localhost, [::1], 127/8). */
18
+ export declare function isLoopbackHostname(hostname: string): boolean;
19
+ /**
20
+ * Request-level trust fence: a loopback socket address AND a loopback Host
21
+ * header, plus browser same-origin markers. The socket address is
22
+ * authoritative; X-Forwarded-For is never trusted.
23
+ */
24
+ export declare function isLoopbackRequest(request: IncomingMessage): boolean;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Host single-instance guard shared by the plugin family. The family bundle
3
+ * (dsh-web-ui-all / dsh-skins) namespaces every child row id (web-ui-*), so
4
+ * the loader accepts a standalone install of the same package side by side;
5
+ * without this guard the second instance would still re-register the same
6
+ * webserver routes, tools, settings namespaces, and system-prompt sections
7
+ * and fail the boot. mountOnce makes the second host apply a no-op for the
8
+ * lifetime of the first instance (the browser half is already deduped by
9
+ * package name in the client module host).
10
+ *
11
+ * The registry rides a global symbol so two module instances of the same
12
+ * package (npm copy vs repository link) still share one verdict. cordis
13
+ * `ctx.effect` runs its callback immediately and treats the callback's
14
+ * return value as the fiber disposer, so the unmarker is returned, not run.
15
+ */
16
+ /**
17
+ * Wrap a cordis plugin apply so the package runs at most once per process.
18
+ * The first mount registers normally and unmarks when its fiber disposes;
19
+ * any later mount of the same package name is a no-op.
20
+ * @param packageName - npm package identity shared by every install source.
21
+ * @param fn - the original plugin apply.
22
+ * @returns an apply of the same shape.
23
+ */
24
+ export declare function mountOnce<T extends (...args: any[]) => unknown>(packageName: string, fn: T): T;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * PowerShell snippet builders. Every payload rides the UTF-8 base64
3
+ * envelope: the WinRM host prints only ASCII (marker + base64 of UTF-8 text)
4
+ * so Chinese / any codepage output survives the transport losslessly. One-shot
5
+ * commands execute directly in the PowerShell session created by WinRM.
6
+ */
7
+ /** Marker line prefix carrying the exit code: '__DSH_WINRM__<code>'. */
8
+ export declare const ENVELOPE_MARKER = "__DSH_WINRM__";
9
+ /** PS single-quoted literal (doubles embedded quotes). */
10
+ export declare function sq(value: string): string;
11
+ /** Base64 of a UTF-16LE PowerShell script, for -EncodedCommand. */
12
+ export declare function encodeCommand(script: string): string;
13
+ /**
14
+ * The UTF-8 WinRM session script: runs `userScript` inside a script block,
15
+ * merges stderr, prints the exit-code marker and base64 of UTF-8 output.
16
+ */
17
+ export declare function wrapEnvelope(userScript: string): string;
18
+ /** One-shot script body for the PowerShell session already created by WinRM. */
19
+ export declare function winrmPowerShellScript(userScript: string): string;
20
+ /** The full powershell.exe command line (ASCII-safe) for a script body. */
21
+ export declare function powershellCommandLine(script: string): string;
22
+ /** One-shot non-interactive PowerShell command line (envelope-wrapped). */
23
+ export declare function execCommandLine(userScript: string): string;
24
+ /** Interactive bootstrap for the streaming console (keeps the shell alive). */
25
+ export declare function consoleCommandLine(): string;
26
+ /** JSON: full service table (CIM). */
27
+ export declare function psListServices(): string;
28
+ /** JSON: one service after an action. */
29
+ export declare function psServiceAction(name: string, action: 'start' | 'stop' | 'restart' | 'set-auto' | 'set-manual' | 'set-disabled'): string;
30
+ /** JSON: process table. */
31
+ export declare function psListProcesses(): string;
32
+ /** Kill one process by id. */
33
+ export declare function psKillProcess(id: number): string;
34
+ /** JSON: directory listing. */
35
+ export declare function psListDir(dir: string): string;
36
+ /** Plain: remote file size in bytes (0 when missing). */
37
+ export declare function psFileSize(p: string): string;
38
+ /** Plain: base64 of up to `count` bytes at `offset` (download chunk). */
39
+ export declare function psReadChunk(p: string, offset: number, count: number): string;
40
+ /**
41
+ * Plain: append `b64` bytes to `p` (Create on first chunk, Append after),
42
+ * prints the number of bytes written.
43
+ */
44
+ export declare function psWriteChunk(p: string, b64: string, append: boolean): string;
45
+ /**
46
+ * Parse the envelope output: { exitCode, text } or null when absent.
47
+ *
48
+ * The base64 body is optional and the separating newline may be missing: a
49
+ * command that printed nothing emits the marker alone (an empty string has
50
+ * no base64 body), and a transport that trims each response drops the
51
+ * trailing newline. The exit code is signed because `$LASTEXITCODE` can be
52
+ * negative.
53
+ */
54
+ export declare function parseEnvelope(output: string): {
55
+ exitCode: number;
56
+ text: string;
57
+ } | null;
58
+ /**
59
+ * Remove PowerShell's CLIXML host records from the WinRS stderr stream.
60
+ *
61
+ * PowerShell marks its stderr stream with a `#< CLIXML` header line and
62
+ * serializes the remote host's own progress/verbose records into `<Objs>`
63
+ * blocks. Both are removed here. Anything else on the stream is genuine
64
+ * process-level stderr and is preserved verbatim — text written between the
65
+ * header and a record block (for example `[Console]::Error`) is real output,
66
+ * not part of the CLIXML payload.
67
+ *
68
+ * The script's own error output is already merged into stdout by the
69
+ * envelope, so this only removes transport-level noise.
70
+ * @param stderr - raw stderr text captured from the WinRS stream.
71
+ * @returns the stderr text without CLIXML host records.
72
+ */
73
+ export declare function stripClixml(stderr: string): string;
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Wire contract between the host half (routes.ts) and the browser half
3
+ * (client/api.ts). Pure types only — imported by both halves, bundled into
4
+ * each, no runtime identity to share.
5
+ */
6
+ /** WinRM transport flavors. */
7
+ export type WinTransport = 'http' | 'https';
8
+ /** One stored host entry (the ~/.dsh/dsh-winrm.json store shape). */
9
+ export interface WinHostEntry {
10
+ /** Stable, user-chosen identifier used by every operation. */
11
+ alias: string;
12
+ /** Hostname or IP of the target Windows machine. */
13
+ host: string;
14
+ /** WinRM port (5985 HTTP, 5986 HTTPS). */
15
+ port: number;
16
+ /** Login user. The transport auto-selects NTLM/Negotiate or Basic for local and domain accounts. */
17
+ user: string;
18
+ /** Authentication (WinRM password; transport handles NTLM/Negotiate/Basic in-process). */
19
+ auth: {
20
+ kind: 'password';
21
+ password?: string;
22
+ };
23
+ /** http (5985) or https (5986). */
24
+ transport: WinTransport;
25
+ /** Whether self-signed HTTPS certificates are accepted. */
26
+ rejectUnauthorized?: boolean;
27
+ /** Free-form note. */
28
+ description?: string;
29
+ /** Deployment environment label (development / production / ...). */
30
+ environment?: string;
31
+ /** Free-form tags. */
32
+ tags: string[];
33
+ /** Physical location note. */
34
+ location?: string;
35
+ createdAt: number;
36
+ updatedAt: number;
37
+ }
38
+ /** Public (secret-free) projection of an entry, safe for the browser/agent. */
39
+ export interface WinHostSummary {
40
+ alias: string;
41
+ host: string;
42
+ port: number;
43
+ user: string;
44
+ auth: 'password';
45
+ transport: WinTransport;
46
+ rejectUnauthorized?: boolean;
47
+ description?: string;
48
+ environment?: string;
49
+ tags: string[];
50
+ location?: string;
51
+ createdAt: number;
52
+ updatedAt: number;
53
+ }
54
+ /** Result of one PowerShell command execution. */
55
+ export interface ExecResult {
56
+ success: boolean;
57
+ /** Remote exit code, or null when the channel died without one. */
58
+ exitCode: number | null;
59
+ timedOut: boolean;
60
+ stdout: string;
61
+ stderr: string;
62
+ /** Wall-clock duration of the round trip in ms. */
63
+ durationMs: number;
64
+ /** Connection error message when the command never ran. */
65
+ error?: string;
66
+ }
67
+ /** One Windows service entry. */
68
+ export interface ServiceInfo {
69
+ name: string;
70
+ displayName: string;
71
+ status: string;
72
+ startMode: string;
73
+ startName?: string;
74
+ }
75
+ /** One Windows process entry. */
76
+ export interface ProcessInfo {
77
+ id: number;
78
+ name: string;
79
+ cpu?: number;
80
+ memMB?: number;
81
+ startTime?: string;
82
+ path?: string;
83
+ }
84
+ /** One directory listing entry (remote file browser). */
85
+ export interface RemoteDirEntry {
86
+ name: string;
87
+ type: 'dir' | 'file' | 'other';
88
+ size: number;
89
+ mtimeMs: number;
90
+ }
91
+ /** One server entry in a cluster run. */
92
+ export interface ClusterResult {
93
+ alias: string;
94
+ ok: boolean;
95
+ exitCode?: number | null;
96
+ timedOut?: boolean;
97
+ stdout?: string;
98
+ stderr?: string;
99
+ durationMs?: number;
100
+ error?: string;
101
+ }
102
+ /** Transfer progress frame (upload/download). */
103
+ export interface TransferProgress {
104
+ phase: 'connecting' | 'transferring' | 'done' | 'error';
105
+ file: string;
106
+ transferred: number;
107
+ total: number;
108
+ percent: number;
109
+ speedBps?: number;
110
+ error?: string;
111
+ }
112
+ /** Test-connection outcome. */
113
+ export interface TestResult {
114
+ ok: boolean;
115
+ latencyMs?: number;
116
+ error?: string;
117
+ }
118
+ /** Host edit payload (create/update); 'alias' comes from the URL for updates. */
119
+ export interface WinHostPayload {
120
+ alias?: string;
121
+ host: string;
122
+ port?: number;
123
+ user: string;
124
+ /** Authentication. Required on create; on update an omitted auth keeps the stored password. */
125
+ auth?: WinHostEntry['auth'];
126
+ transport?: WinTransport;
127
+ rejectUnauthorized?: boolean;
128
+ description?: string;
129
+ environment?: string;
130
+ tags?: string[];
131
+ location?: string;
132
+ }
133
+ /** JSON error body used by every route. */
134
+ export interface ApiErrorBody {
135
+ error: string;
136
+ }
137
+ /** WebSocket console protocol frames (host -> client and client -> host). */
138
+ export type ConsoleServerFrame = {
139
+ type: 'ready';
140
+ alias: string;
141
+ } | {
142
+ type: 'output';
143
+ data: string;
144
+ } | {
145
+ type: 'exit';
146
+ code: number | null;
147
+ error?: string;
148
+ };
149
+ export type ConsoleClientFrame = {
150
+ type: 'input';
151
+ data: string;
152
+ };
153
+ /** Route paths the client calls (shared literals). */
154
+ export declare const WINRM_API_BASE: "/api/dsh-winrm";
155
+ export declare const WINRM_API: {
156
+ readonly hosts: string;
157
+ readonly test: string;
158
+ readonly exec: string;
159
+ readonly cluster: string;
160
+ readonly services: string;
161
+ readonly processes: string;
162
+ readonly ls: string;
163
+ readonly upload: string;
164
+ readonly download: string;
165
+ readonly console: string;
166
+ };
167
+ /** NDJSON transfer stream line shapes (upload). */
168
+ export type TransferStreamLine = {
169
+ type: 'progress';
170
+ progress: TransferProgress;
171
+ } | {
172
+ type: 'result';
173
+ ok: boolean;
174
+ transferredBytes?: number;
175
+ error?: string;
176
+ };
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The /api/dsh-winrm route family: host CRUD, exec, cluster, services,
3
+ * processes, remote listing, NDJSON upload progress, binary download, and
4
+ * the WebSocket PowerShell console upgrade. Every route carries a
5
+ * loopback-only trust fence (plus browser same-origin markers) — these
6
+ * endpoints execute commands on remote machines, so LAN-exposed dsh web
7
+ * deployments must not serve them.
8
+ */
9
+ import type { WebRoute, WebUpgradeRoute } from '@deepseek-ai/dsh-host-webserver';
10
+ import type { WinRmEngine } from './engine.ts';
11
+ import type { HostStore } from './store.ts';
12
+ /** Route family dependencies. */
13
+ export interface WinrmRoutesDeps {
14
+ store: HostStore;
15
+ engine: WinRmEngine;
16
+ stagingDir?: string;
17
+ maxUploadBytes?: number;
18
+ }
19
+ /**
20
+ * Build every /api/dsh-winrm route plus the console upgrade.
21
+ * @param deps - store, engine, staging dir.
22
+ */
23
+ export declare function makeRoutes(deps: WinrmRoutesDeps): {
24
+ routes: WebRoute[];
25
+ upgrade: WebUpgradeRoute;
26
+ };
@@ -0,0 +1,31 @@
1
+ /**
2
+ * SMB transfer bridge.
3
+ *
4
+ * The WinRM base64 chunk bridge is reliable for small files but breaks for
5
+ * large artifacts (the WinRM/HTTP envelope rejects large payloads), so whole
6
+ * files ride the remote admin share. This module mounts the remote host's
7
+ * admin share with `net use` (password passed via stdin, never argv), copies
8
+ * whole files with node:fs, then verifies the SHA-256 on both ends.
9
+ */
10
+ import type { WinHostEntry } from './protocol.ts';
11
+ /** Per-transfer channel selector. */
12
+ export type TransferChannel = 'auto' | 'smb' | 'winrm';
13
+ export interface SmbTransferResult {
14
+ bytes: number;
15
+ channel: 'smb';
16
+ }
17
+ /**
18
+ * Convert a Windows drive path to its UNC admin-share form.
19
+ * D:\\dir\\file -> \\\\host\\D$\\dir\\file
20
+ */
21
+ export declare function adminSharePath(host: string, remotePath: string): string;
22
+ /**
23
+ * Copy one local file to a remote Windows host over SMB, verifying the
24
+ * remote SHA-256 matches the local source.
25
+ */
26
+ export declare function uploadSmb(entry: WinHostEntry, localPath: string, remotePath: string, timeoutMs?: number): Promise<SmbTransferResult>;
27
+ /**
28
+ * Copy one remote Windows file to a local path over SMB and verify the local
29
+ * SHA-256 matches the remote source.
30
+ */
31
+ export declare function downloadSmb(entry: WinHostEntry, remotePath: string, localPath: string, timeoutMs?: number): Promise<SmbTransferResult>;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Host config store: one JSON file (~/.dsh/dsh-winrm.json) holding every
3
+ * Windows host entry, written atomically (tmp + rename). Secrets (passwords)
4
+ * live in this user-owned file in plaintext — the same trust model as
5
+ * dsh-ssh; document it, never log it.
6
+ */
7
+ import type { WinHostEntry, WinHostPayload, WinHostSummary } from './protocol.ts';
8
+ /** Store file location: <home>/.dsh/dsh-winrm.json. */
9
+ export declare function storePath(): string;
10
+ /** Validate the wire shape of a host payload; returns a message or undefined. */
11
+ export declare function validateHostPayload(payload: unknown): string | undefined;
12
+ /** Validate an alias for creation. */
13
+ export declare function validateAlias(alias: string): string | undefined;
14
+ /**
15
+ * The host store. Pure file I/O — no cordis dependency, unit-testable.
16
+ */
17
+ export declare class HostStore {
18
+ /** The JSON file path. */
19
+ readonly path: string;
20
+ /**
21
+ * @param path - store file path (defaults to the standard location).
22
+ */
23
+ constructor(path?: string);
24
+ /** Load all entries (empty store when the file is absent). */
25
+ list(): WinHostEntry[];
26
+ /** Find one entry by alias. */
27
+ find(alias: string): WinHostEntry | undefined;
28
+ /** Secret-free projection for the browser and agent surfaces. */
29
+ summarize(entry: WinHostEntry): WinHostSummary;
30
+ /** Create one entry. Throws on alias collision or invalid payload. */
31
+ create(payload: WinHostPayload): WinHostEntry;
32
+ /** Update the fields present in `patch`; unknown aliases throw. */
33
+ update(alias: string, patch: Partial<WinHostPayload>): WinHostEntry;
34
+ /** Remove one entry. */
35
+ delete(alias: string): void;
36
+ /**
37
+ * Last parsed store keyed by file identity. list/find ride every acquire
38
+ * and GUI refresh; re-reading and re-parsing the whole file each call is
39
+ * wasted work when the file has not changed. Any save invalidates.
40
+ */
41
+ private cache;
42
+ private load;
43
+ private save;
44
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Agent tools: the DSH-native counterpart of the GUI — every tool talks to
3
+ * the same engine the web UI uses, so a host configured in the GUI is
4
+ * immediately operable by any agent, and vice versa.
5
+ */
6
+ import type { WinRmEngine } from './engine.ts';
7
+ /** The host-list tool. */
8
+ export declare function winrmListTool(engine: WinRmEngine): import("@deepseek-ai/dsh-tools").ToolDefinition;
9
+ /** The command-execution tool (PowerShell). */
10
+ export declare function winrmExecTool(engine: WinRmEngine): import("@deepseek-ai/dsh-tools").ToolDefinition;
11
+ /** The service-management tool. */
12
+ export declare function winrmServiceTool(engine: WinRmEngine): import("@deepseek-ai/dsh-tools").ToolDefinition;
13
+ /** The process-management tool. */
14
+ export declare function winrmProcessTool(engine: WinRmEngine): import("@deepseek-ai/dsh-tools").ToolDefinition;
15
+ /** The upload tool. */
16
+ export declare function winrmUploadTool(engine: WinRmEngine): import("@deepseek-ai/dsh-tools").ToolDefinition;
17
+ /** The download tool. */
18
+ export declare function winrmDownloadTool(engine: WinRmEngine): import("@deepseek-ai/dsh-tools").ToolDefinition;
19
+ /** The cluster tool. */
20
+ export declare function winrmClusterTool(engine: WinRmEngine): import("@deepseek-ai/dsh-tools").ToolDefinition;
package/package.json ADDED
@@ -0,0 +1,111 @@
1
+ {
2
+ "name": "dsh-winrm",
3
+ "description": "Remote Windows administration for the dsh web GUI: WinRM/PowerShell Remoting host config store (~/.dsh/dsh-winrm.json), PowerShell exec / streaming console / service & process management / base64-chunked file transfer / cluster execution, plus agent tools (winrm_list, winrm_exec, winrm_service, winrm_process, winrm_upload, winrm_download, winrm_cluster). Runs entirely on a native Node.js WinRM client (winrm-client) — no Python/pywinrm required. Standalone Cordis plugin — mounted via cordis.patch.yml + a profile node_modules link, no dsh source changes.",
4
+ "version": "0.1.0",
5
+ "type": "module",
6
+ "packageManager": "pnpm@11.7.0",
7
+ "engines": {
8
+ "node": "^22.19.0 || >=24.0.0"
9
+ },
10
+ "main": "lib/index.js",
11
+ "types": "lib/types/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./lib/types/index.d.ts",
15
+ "default": "./lib/index.js"
16
+ },
17
+ "./invariant": {
18
+ "types": "./lib/types/invariant.d.ts",
19
+ "default": "./lib/invariant.js"
20
+ },
21
+ "./client": {
22
+ "types": "./lib/types/client/index.d.ts",
23
+ "default": "./lib/client.js"
24
+ },
25
+ "./src/*": "./src/*",
26
+ "./package.json": "./package.json"
27
+ },
28
+ "dsh": {
29
+ "bundle": {
30
+ "patch": "./cordis.patch.yml"
31
+ },
32
+ "client": {
33
+ "inject": [
34
+ "@deepseek-ai/dsh-client-ui-renderer",
35
+ "@deepseek-ai/dsh-client-locale",
36
+ "@deepseek-ai/dsh-client-ui-layout",
37
+ "@deepseek-ai/dsh-client-ui-sidebar"
38
+ ],
39
+ "platform": "web"
40
+ }
41
+ },
42
+ "dependencies": {
43
+ "winrm-client": "^0.0.12",
44
+ "ws": "^8.18.0"
45
+ },
46
+ "peerDependencies": {
47
+ "@deepseek-ai/dsh-host-webserver": "^0.1.7-alpha.2",
48
+ "@deepseek-ai/dsh-llm": "^0.1.7-alpha.2",
49
+ "@deepseek-ai/dsh-system-prompt": "^0.1.7-alpha.2",
50
+ "@deepseek-ai/dsh-tools": "^0.1.7-alpha.2",
51
+ "react": "^18.2.0",
52
+ "react-dom": "^18.2.0"
53
+ },
54
+ "devDependencies": {
55
+ "@deepseek-ai/cordis": "^4.0.4",
56
+ "@deepseek-ai/cordis-plugin-loader": "^1.0.5",
57
+ "@deepseek-ai/dsh-client-locale": "^0.1.7-rc.1",
58
+ "@deepseek-ai/dsh-client-ui-layout": "^0.1.7-rc.1",
59
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.7-rc.1",
60
+ "@deepseek-ai/dsh-client-ui-sidebar": "^0.1.7-rc.1",
61
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.7-rc.1",
62
+ "@deepseek-ai/dsh-host-webserver": "^0.1.7-rc.1",
63
+ "@deepseek-ai/dsh-llm": "^0.1.7-rc.1",
64
+ "@deepseek-ai/dsh-system-prompt": "^0.1.7-rc.1",
65
+ "@deepseek-ai/dsh-tools": "^0.1.7-rc.1",
66
+ "@deepseek-ai/schemastery": "^3.18.4",
67
+ "@types/node": "^22.20.0",
68
+ "@types/react": "~18.3.1",
69
+ "@types/react-dom": "^18.3.5",
70
+ "@types/ws": "^8.5.13",
71
+ "lightningcss": "^1.29.0",
72
+ "react": "^18.3.1",
73
+ "react-dom": "^18.3.1",
74
+ "tsdown": "0.22.2",
75
+ "typescript": "~5.7.2"
76
+ },
77
+ "files": [
78
+ "lib/**/*.js",
79
+ "lib/**/*.d.ts",
80
+ "src",
81
+ "cordis.patch.yml",
82
+ "README.md",
83
+ "README.zh.md",
84
+ "RELEASING.md",
85
+ "scripts"
86
+ ],
87
+ "license": "Apache-2.0",
88
+ "scripts": {
89
+ "build": "tsc -p tsconfig.build.json && tsdown && node scripts/postbuild.mjs",
90
+ "bundle": "tsdown",
91
+ "watch": "tsdown --watch",
92
+ "test": "node --experimental-strip-types --test tests/*.test.ts tests/*.test.mjs",
93
+ "check": "npm run typecheck && npm run build && npm test",
94
+ "pack:check": "npm pack --dry-run --json",
95
+ "prepack": "npm run check",
96
+ "typecheck": "tsc --noEmit"
97
+ },
98
+ "repository": {
99
+ "type": "git",
100
+ "url": "git+https://github.com/MoRanYue/dsh-winrm.git"
101
+ },
102
+ "keywords": [
103
+ "dsh",
104
+ "deepseek-harness",
105
+ "windows",
106
+ "winrm",
107
+ "remote",
108
+ "powershell",
109
+ "bundle-plugin"
110
+ ]
111
+ }
Binary file
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Post-build step: wrap the rolldown CJS client bundle in the GUI module
3
+ * loader handoff (window.__ModuleLoader__.load({id, factory})) and restore
4
+ * the CJS preamble (var module/exports) that this rolldown generation omits,
5
+ * exactly matching the official dsh-web-ui artifact shape. Emits
6
+ * lib/client.js that the loader serves at /plugins/winrm/client.js.
7
+ *
8
+ * The source map stays a local debugging aid and is not part of the published
9
+ * payload (`files` omits `*.map`), so the wrapper does not reference it — a
10
+ * published bundle must not point at a file that is not shipped.
11
+ */
12
+ import { readFileSync, renameSync, writeFileSync } from 'node:fs'
13
+ import { fileURLToPath } from 'node:url'
14
+ import { join } from 'node:path'
15
+
16
+ const lib = fileURLToPath(new URL('../lib/', import.meta.url))
17
+ const id = 'dsh-winrm'
18
+
19
+ const cjs = readFileSync(join(lib, 'client.cjs'), 'utf8')
20
+ const map = join(lib, 'client.cjs.map')
21
+ const mapJs = join(lib, 'client.js.map')
22
+ try { renameSync(map, mapJs) } catch { /* optional */ }
23
+
24
+ const body = cjs.replace(/\/\/# sourceMappingURL=client\.cjs\.map\s*$/, '')
25
+ const wrapped = [
26
+ 'window.__ModuleLoader__.load({',
27
+ '\tid: ' + JSON.stringify(id) + ',',
28
+ '\tfactory: (require) => {',
29
+ '\t\tvar module = { exports: {} };',
30
+ '\t\tvar exports = module.exports;',
31
+ body,
32
+ '\t\treturn module.exports;',
33
+ '\t}',
34
+ '});',
35
+ '',
36
+ ].join('\n')
37
+
38
+ writeFileSync(join(lib, 'client.js'), wrapped)
39
+ try { renameSync(join(lib, 'client.cjs'), join(lib, 'client.cjs.bak')) } catch { /* keep both is fine */ }
40
+ console.log('postbuild: wrapped lib/client.js (' + wrapped.length + 'B)')
@@ -0,0 +1,18 @@
1
+ import fs from 'node:fs'
2
+
3
+ const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'))
4
+ if (!pkg.dsh || !pkg.dsh.bundle) {
5
+ console.error('package.json does not declare dsh.bundle')
6
+ process.exit(1)
7
+ }
8
+ const patch = pkg.dsh.bundle.patch
9
+ if (!patch || !fs.existsSync(patch)) {
10
+ console.error('bundle patch not found: ' + String(patch))
11
+ process.exit(1)
12
+ }
13
+ const patchText = fs.readFileSync(patch, 'utf8')
14
+ if (patchText.trim().length === 0) {
15
+ console.error(patch + ' is empty')
16
+ process.exit(1)
17
+ }
18
+ console.log('OK: dsh.bundle -> ' + patch)