dsh-winrm 0.3.1 → 0.4.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/README.en.md +132 -0
- package/README.md +20 -7
- package/docs/screenshots/winrm-panel.png +0 -0
- package/lib/client.js +597 -73
- package/lib/index.js +898 -26
- package/lib/types/client/locales.d.ts +44 -0
- package/lib/types/client/tool/card-model.d.ts +71 -0
- package/lib/types/client/tool/cards.d.ts +9 -0
- package/lib/types/diff.d.ts +62 -0
- package/lib/types/engine/client.d.ts +30 -4
- package/lib/types/engine.d.ts +51 -1
- package/lib/types/index.d.ts +4 -3
- package/lib/types/powershell.d.ts +64 -0
- package/lib/types/protocol.d.ts +54 -0
- package/lib/types/tools.d.ts +7 -1
- package/package.json +8 -4
- package/scripts/verify-live.mts +116 -0
- package/src/client/index.ts +17 -0
- package/src/client/locales.ts +44 -0
- package/src/client/tool/card-model.ts +151 -0
- package/src/client/tool/cards.module.css +128 -0
- package/src/client/tool/cards.tsx +335 -0
- package/src/diff.ts +128 -0
- package/src/engine/client.ts +66 -16
- package/src/engine.ts +222 -5
- package/src/index.ts +10 -3
- package/src/powershell.ts +253 -0
- package/src/protocol.ts +59 -0
- package/src/smb.ts +4 -1
- package/src/tools.ts +303 -4
- package/README.zh.md +0 -126
|
@@ -113,6 +113,28 @@ export declare const zh: {
|
|
|
113
113
|
readonly 'transfer.mib': "MB";
|
|
114
114
|
readonly 'transfer.dir': "目录";
|
|
115
115
|
readonly 'transfer.file': "文件";
|
|
116
|
+
readonly 'tool.read': "读取远程文件";
|
|
117
|
+
readonly 'tool.write': "写入远程文件";
|
|
118
|
+
readonly 'tool.edit': "编辑远程文件";
|
|
119
|
+
readonly 'tool.running': "执行中…";
|
|
120
|
+
readonly 'tool.noDetail': "无卡片数据";
|
|
121
|
+
readonly 'tool.created': "新建 {bytes} 字节";
|
|
122
|
+
readonly 'tool.updated': "覆盖 {bytes} 字节";
|
|
123
|
+
readonly 'tool.replacements': "替换 {count} 处";
|
|
124
|
+
readonly 'tool.beyondBasis': "文件超过 diff 体积上限,仅显示路径";
|
|
125
|
+
readonly 'tool.truncatedByBytes': "已按字节上限截断";
|
|
126
|
+
readonly 'tool.truncatedLines': "{count} 行过长已截断";
|
|
127
|
+
readonly 'tool.codeLabel': "代码";
|
|
128
|
+
readonly 'tool.wrapLabel': "自动换行";
|
|
129
|
+
readonly 'tool.unwrapLabel': "取消自动换行";
|
|
130
|
+
readonly 'tool.window': "第 {shown} / {total} 行";
|
|
131
|
+
readonly 'tool.lines': "共 {count} 行";
|
|
132
|
+
readonly 'tool.copy': "复制";
|
|
133
|
+
readonly 'tool.copied': "已复制";
|
|
134
|
+
readonly 'tool.collapseAria': "收起";
|
|
135
|
+
readonly 'tool.expandAria': "展开剩余 {hidden} 行";
|
|
136
|
+
readonly 'tool.collapse': "收起";
|
|
137
|
+
readonly 'tool.expand': "展开其余 {hidden} 行";
|
|
116
138
|
};
|
|
117
139
|
export declare const en: {
|
|
118
140
|
readonly 'entry.label': "Windows";
|
|
@@ -225,6 +247,28 @@ export declare const en: {
|
|
|
225
247
|
readonly 'transfer.mib': "MB";
|
|
226
248
|
readonly 'transfer.dir': "dir";
|
|
227
249
|
readonly 'transfer.file': "file";
|
|
250
|
+
readonly 'tool.read': "Read remote file";
|
|
251
|
+
readonly 'tool.write': "Write remote file";
|
|
252
|
+
readonly 'tool.edit': "Edit remote file";
|
|
253
|
+
readonly 'tool.running': "Working…";
|
|
254
|
+
readonly 'tool.noDetail': "No card data";
|
|
255
|
+
readonly 'tool.created': "Created {bytes} bytes";
|
|
256
|
+
readonly 'tool.updated': "Overwrote {bytes} bytes";
|
|
257
|
+
readonly 'tool.replacements': "{count} replacements";
|
|
258
|
+
readonly 'tool.beyondBasis': "File is past the diff size cap; showing the path only";
|
|
259
|
+
readonly 'tool.truncatedByBytes': "Cut at the byte budget";
|
|
260
|
+
readonly 'tool.truncatedLines': "{count} long lines truncated";
|
|
261
|
+
readonly 'tool.codeLabel': "Code";
|
|
262
|
+
readonly 'tool.wrapLabel': "Wrap lines";
|
|
263
|
+
readonly 'tool.unwrapLabel': "Do not wrap";
|
|
264
|
+
readonly 'tool.window': "{shown} of {total} lines";
|
|
265
|
+
readonly 'tool.lines': "{count} lines";
|
|
266
|
+
readonly 'tool.copy': "Copy";
|
|
267
|
+
readonly 'tool.copied': "Copied";
|
|
268
|
+
readonly 'tool.collapseAria': "Collapse";
|
|
269
|
+
readonly 'tool.expandAria': "Expand the remaining {hidden} lines";
|
|
270
|
+
readonly 'tool.collapse': "Collapse";
|
|
271
|
+
readonly 'tool.expand': "Show the remaining {hidden} lines";
|
|
228
272
|
};
|
|
229
273
|
export type WinKey = keyof typeof zh;
|
|
230
274
|
/** Translate a key in one dictionary with optional {name} params. */
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Card models for the three remote file tools.
|
|
3
|
+
*
|
|
4
|
+
* The host projects a small JSON payload through `output.presentationMeta` and
|
|
5
|
+
* the session log persists it; these functions narrow that unknown payload into
|
|
6
|
+
* something the card components can hand to the harness's `ReadBlock` and
|
|
7
|
+
* `DiffBlock` primitives. Kept apart from the components so the narrowing can be
|
|
8
|
+
* unit tested without a DOM.
|
|
9
|
+
* @module dsh-winrm/src/client/tool/card-model
|
|
10
|
+
*/
|
|
11
|
+
/** One line of a read window. */
|
|
12
|
+
export interface ReadCardLine {
|
|
13
|
+
number: number;
|
|
14
|
+
text: string;
|
|
15
|
+
}
|
|
16
|
+
/** A completed `winrm_read`: a line-numbered window of a remote file. */
|
|
17
|
+
export interface ReadCard {
|
|
18
|
+
kind: 'read';
|
|
19
|
+
path: string;
|
|
20
|
+
offset: number;
|
|
21
|
+
lines: ReadCardLine[];
|
|
22
|
+
totalLines: number;
|
|
23
|
+
encoding: string;
|
|
24
|
+
truncatedByBytes: boolean;
|
|
25
|
+
truncatedLines: number;
|
|
26
|
+
}
|
|
27
|
+
/** One hunk of a remote write or edit, in the primitive's own shape. */
|
|
28
|
+
export interface DiffHunk {
|
|
29
|
+
/** Prior fragment including shared context; `''` when the hunk had no prior content. */
|
|
30
|
+
oldText: string;
|
|
31
|
+
/** Resulting fragment including the same shared context. */
|
|
32
|
+
newText: string;
|
|
33
|
+
}
|
|
34
|
+
/** A completed `winrm_write` or `winrm_edit`: the changes it applied. */
|
|
35
|
+
export interface DiffCard {
|
|
36
|
+
kind: 'diff';
|
|
37
|
+
/** Which tool produced it, so the header can name the operation. */
|
|
38
|
+
tool: 'write' | 'edit';
|
|
39
|
+
path: string;
|
|
40
|
+
operation: 'create' | 'update';
|
|
41
|
+
bytes: number;
|
|
42
|
+
size: number;
|
|
43
|
+
replacements: number;
|
|
44
|
+
encoding: string;
|
|
45
|
+
hunks: DiffHunk[];
|
|
46
|
+
/** True when the previous text was past the basis cap, so no hunks exist. */
|
|
47
|
+
beyondBasis: boolean;
|
|
48
|
+
}
|
|
49
|
+
/** A failed call: keeps the path and the message, drops card-specific detail. */
|
|
50
|
+
export interface ErrorCard {
|
|
51
|
+
kind: 'error';
|
|
52
|
+
path: string;
|
|
53
|
+
message: string;
|
|
54
|
+
}
|
|
55
|
+
/** Any card these tools render. */
|
|
56
|
+
export type FileCard = ReadCard | DiffCard | ErrorCard;
|
|
57
|
+
/**
|
|
58
|
+
* Narrow a `winrm_read` metadata payload into a card.
|
|
59
|
+
* @param meta - the persisted payload, of unknown shape.
|
|
60
|
+
* @param fallbackPath - the authored path, used when the payload omits one.
|
|
61
|
+
* @returns the card, or `undefined` when the payload is not a read card.
|
|
62
|
+
*/
|
|
63
|
+
export declare function readCard(meta: unknown, fallbackPath: string): FileCard | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* Narrow a `winrm_write` / `winrm_edit` metadata payload into a card.
|
|
66
|
+
* @param meta - the persisted payload, of unknown shape.
|
|
67
|
+
* @param fallbackPath - the authored path, used when the payload omits one.
|
|
68
|
+
* @param tool - which tool produced the payload.
|
|
69
|
+
* @returns the card, or `undefined` when the payload belongs to another card.
|
|
70
|
+
*/
|
|
71
|
+
export declare function diffCard(meta: unknown, fallbackPath: string, tool: 'write' | 'edit'): FileCard | undefined;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ToolCallViewProps } from '@deepseek-ai/dsh-client-ui-tool/client';
|
|
2
|
+
/** The Tool view props these cards read. */
|
|
3
|
+
export type WinrmFileCardProps = ToolCallViewProps;
|
|
4
|
+
/** Card for `winrm_read`. */
|
|
5
|
+
export declare function WinrmReadCard(props: WinrmFileCardProps): import("react").JSX.Element;
|
|
6
|
+
/** Card for `winrm_write`. */
|
|
7
|
+
export declare function WinrmWriteCard(props: WinrmFileCardProps): import("react").JSX.Element;
|
|
8
|
+
/** Card for `winrm_edit`. */
|
|
9
|
+
export declare function WinrmEditCard(props: WinrmFileCardProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contextual diff derivation for the write/edit presentation metadata.
|
|
3
|
+
*
|
|
4
|
+
* The GUI renders a diff card from `tool/result` metadata rather than from the
|
|
5
|
+
* model-facing text, so the hunks are projected here, at result time, and
|
|
6
|
+
* persisted with the session log. This mirrors the harness's own file tools:
|
|
7
|
+
* one entry per applied change with a few surrounding context lines.
|
|
8
|
+
*
|
|
9
|
+
* The alignment is deliberately cheap rather than a full LCS: common leading
|
|
10
|
+
* and trailing lines are trimmed, and when the remaining halves have the same
|
|
11
|
+
* length each differing run becomes its own hunk. That covers the cases these
|
|
12
|
+
* two tools actually produce — an exact-string edit, and a rewrite that keeps
|
|
13
|
+
* most lines — without a diff dependency in a plugin whose point is a small
|
|
14
|
+
* footprint. A rewrite that changes the line count collapses to one hunk over
|
|
15
|
+
* the changed span, which is still a truthful summary.
|
|
16
|
+
* @module dsh-winrm/src/diff
|
|
17
|
+
*/
|
|
18
|
+
/** Context lines shown on each side of an applied hunk. */
|
|
19
|
+
export declare const DIFF_CONTEXT = 3;
|
|
20
|
+
/**
|
|
21
|
+
* Largest file whose text may serve as a diff basis. Above this the card shows
|
|
22
|
+
* the path and operation without hunks: fetching and projecting a bigger basis
|
|
23
|
+
* would cost another transfer and a large metadata payload for a summary the
|
|
24
|
+
* user reads at a glance.
|
|
25
|
+
*/
|
|
26
|
+
export declare const DIFF_BASIS_MAX_BYTES: number;
|
|
27
|
+
/**
|
|
28
|
+
* Largest serialized hunk list kept on one result. A pathological edit (a
|
|
29
|
+
* whole-file rewrite of a large file) can produce thousands of hunks; past
|
|
30
|
+
* this bound the card degrades to path-only instead of bloating the log.
|
|
31
|
+
*/
|
|
32
|
+
export declare const DIFF_META_MAX_BYTES: number;
|
|
33
|
+
/**
|
|
34
|
+
* One applied hunk: the fragment before the change and after it.
|
|
35
|
+
*
|
|
36
|
+
* Declared as a type alias rather than an interface so it satisfies the
|
|
37
|
+
* `JsonValue` index signature the tool-output metadata is typed against.
|
|
38
|
+
*/
|
|
39
|
+
export type RemoteFileDiff = {
|
|
40
|
+
/** Remote path the hunk belongs to (the authored argument). */
|
|
41
|
+
path: string;
|
|
42
|
+
/** Prior content including context lines; empty for a pure insertion. */
|
|
43
|
+
oldText: string;
|
|
44
|
+
/** Content after the change, including the shared context lines. */
|
|
45
|
+
newText: string;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Compute the hunks between two texts.
|
|
49
|
+
* @param path - remote path stamped on every hunk.
|
|
50
|
+
* @param before - file text before the change.
|
|
51
|
+
* @param after - file text after the change.
|
|
52
|
+
* @returns one hunk per contiguous change in file order; empty when equal.
|
|
53
|
+
*/
|
|
54
|
+
export declare function computeHunkDiffs(path: string, before: string, after: string): RemoteFileDiff[];
|
|
55
|
+
/**
|
|
56
|
+
* Compute hunks and drop them when the serialized payload would exceed the cap.
|
|
57
|
+
* @param path - remote path stamped on every hunk.
|
|
58
|
+
* @param before - prior text.
|
|
59
|
+
* @param after - text after the change.
|
|
60
|
+
* @returns the kept hunks; empty when nothing changed or the payload is too big.
|
|
61
|
+
*/
|
|
62
|
+
export declare function boundedHunkDiffs(path: string, before: string, after: string): RemoteFileDiff[];
|
|
@@ -53,6 +53,32 @@ export declare const TRANSFER_CHUNK: number;
|
|
|
53
53
|
export declare function remoteFileSize(conn: WinRMParams, remotePath: string, timeoutMs?: number): Promise<number | null>;
|
|
54
54
|
export declare function downloadChunks(conn: WinRMParams, remotePath: string, onChunk: (b64: string, bytes: number) => void, onProgress?: (transferred: number, total: number) => void, timeoutMs?: number): Promise<number>;
|
|
55
55
|
export declare function uploadBuffer(conn: WinRMParams, remotePath: string, data: Buffer, onProgress?: (transferred: number, total: number) => void, timeoutMs?: number): Promise<number>;
|
|
56
|
+
/**
|
|
57
|
+
* How long a streaming transfer may deliver nothing before it is treated as
|
|
58
|
+
* stalled.
|
|
59
|
+
*
|
|
60
|
+
* A wall-clock cap on the whole transfer cannot work here: this transport is
|
|
61
|
+
* latency-bound and measured at ~0.73 MiB/s against a real target, so a 700 MiB
|
|
62
|
+
* file legitimately needs about sixteen minutes and any smaller fixed cap fails
|
|
63
|
+
* a transfer that is making steady progress. What a transfer actually needs
|
|
64
|
+
* bounding against is a *stall* — a range that has stopped producing bytes —
|
|
65
|
+
* which is what this idle window measures.
|
|
66
|
+
*/
|
|
67
|
+
export declare const STREAM_IDLE_TIMEOUT_MS = 180000;
|
|
68
|
+
/**
|
|
69
|
+
* Budget for one streaming transfer of `bytes` bytes.
|
|
70
|
+
*
|
|
71
|
+
* The idle window is the effective limit; the hard window exists so a wedged
|
|
72
|
+
* transfer cannot stay alive forever, and it scales with the payload because
|
|
73
|
+
* the payload is what sets the legitimate duration.
|
|
74
|
+
* @param bytes - total bytes the transfer must move.
|
|
75
|
+
* @param idleMs - how long the transfer may go without delivering anything.
|
|
76
|
+
* @returns the idle window and the absolute backstop, both in milliseconds.
|
|
77
|
+
*/
|
|
78
|
+
export declare function transferBudget(bytes: number, idleMs?: number): {
|
|
79
|
+
idleMs: number;
|
|
80
|
+
hardMs: number;
|
|
81
|
+
};
|
|
56
82
|
/**
|
|
57
83
|
* Upload a buffer over a single WinRS stdin stream.
|
|
58
84
|
*
|
|
@@ -68,10 +94,10 @@ export declare function uploadBuffer(conn: WinRMParams, remotePath: string, data
|
|
|
68
94
|
* @param remotePath - destination path on the target.
|
|
69
95
|
* @param data - the file contents to send.
|
|
70
96
|
* @param onProgress - called with cumulative bytes after each frame.
|
|
71
|
-
* @param
|
|
97
|
+
* @param idleMs - how long the transfer may go without moving a slice.
|
|
72
98
|
* @returns bytes written, or null when the streaming path is unavailable.
|
|
73
99
|
*/
|
|
74
|
-
export declare function uploadStream(conn: WinRMParams, remotePath: string, data: Buffer, onProgress?: (transferred: number, total: number) => void,
|
|
100
|
+
export declare function uploadStream(conn: WinRMParams, remotePath: string, data: Buffer, onProgress?: (transferred: number, total: number) => void, idleMs?: number): Promise<number | null>;
|
|
75
101
|
/**
|
|
76
102
|
* Stream a remote file to a consumer, in parallel byte ranges.
|
|
77
103
|
*
|
|
@@ -90,7 +116,7 @@ export declare function uploadStream(conn: WinRMParams, remotePath: string, data
|
|
|
90
116
|
* @param remotePath - file to read.
|
|
91
117
|
* @param onData - receives each decompressed piece with its absolute file offset.
|
|
92
118
|
* @param onProgress - receives cumulative bytes delivered and the total size.
|
|
93
|
-
* @param
|
|
119
|
+
* @param idleMs - how long a worker may go without delivering bytes.
|
|
94
120
|
* @returns the number of bytes delivered, or null when streaming is unavailable.
|
|
95
121
|
*/
|
|
96
|
-
export declare function downloadStream(conn: WinRMParams, remotePath: string, onData: (offset: number, data: Buffer) => void, onProgress?: (transferred: number, total: number) => void,
|
|
122
|
+
export declare function downloadStream(conn: WinRMParams, remotePath: string, onData: (offset: number, data: Buffer) => void, onProgress?: (transferred: number, total: number) => void, idleMs?: number): Promise<number | null>;
|
package/lib/types/engine.d.ts
CHANGED
|
@@ -5,10 +5,18 @@
|
|
|
5
5
|
* (client, console); this class composes them behind one WinRmEngine instance
|
|
6
6
|
* per plugin apply.
|
|
7
7
|
*/
|
|
8
|
-
import type { ClusterResult, ExecResult, ProcessInfo, RemoteDirEntry, ServiceInfo, TestResult, TransferProgress, WinHostSummary } from './protocol.ts';
|
|
8
|
+
import type { ClusterResult, ExecResult, ProcessInfo, RemoteDirEntry, RemoteEditOutcome, RemoteTextEncoding, RemoteTextWindow, RemoteWriteOutcome, ServiceInfo, TestResult, TransferProgress, WinHostSummary } from './protocol.ts';
|
|
9
9
|
import type { HostStore } from './store.ts';
|
|
10
10
|
import { type TransferChannel } from './smb.ts';
|
|
11
11
|
import { ConsoleSession } from './engine/console.ts';
|
|
12
|
+
/** Longest single line the reader returns before cutting it (characters). */
|
|
13
|
+
export declare const READ_MAX_LINE_LENGTH = 2000;
|
|
14
|
+
/** Lines the reader returns when the caller does not choose a page size. */
|
|
15
|
+
export declare const READ_DEFAULT_LIMIT = 2000;
|
|
16
|
+
/** Most lines the reader will return in one call. */
|
|
17
|
+
export declare const READ_MAX_LIMIT = 5000;
|
|
18
|
+
/** Byte budget for one read window; keeps the envelope well under the transport limit. */
|
|
19
|
+
export declare const READ_MAX_BYTES: number;
|
|
12
20
|
/** The engine. One instance per plugin apply; dispose() clears nothing pooled (WinRM is per-call HTTP). */
|
|
13
21
|
export declare class WinRmEngine {
|
|
14
22
|
readonly store: HostStore;
|
|
@@ -39,6 +47,48 @@ export declare class WinRmEngine {
|
|
|
39
47
|
killProcess(alias: string, id: number): Promise<void>;
|
|
40
48
|
/** List a remote directory. */
|
|
41
49
|
ls(alias: string, dir: string): Promise<RemoteDirEntry[]>;
|
|
50
|
+
/**
|
|
51
|
+
* Read a bounded, line-numbered window of a remote text file.
|
|
52
|
+
*
|
|
53
|
+
* One PowerShell round trip: the remote side decodes the file, counts every
|
|
54
|
+
* line, and returns only the requested window, so a large log costs one
|
|
55
|
+
* transfer of the window rather than of the file.
|
|
56
|
+
* @param alias - configured host.
|
|
57
|
+
* @param remotePath - path on the target.
|
|
58
|
+
* @param offset - 1-based first line to return.
|
|
59
|
+
* @param limit - maximum number of lines to return.
|
|
60
|
+
* @returns the window plus the file's total line count and encoding.
|
|
61
|
+
*/
|
|
62
|
+
readFile(alias: string, remotePath: string, offset: number, limit: number): Promise<RemoteTextWindow>;
|
|
63
|
+
/**
|
|
64
|
+
* Write a whole remote text file, creating parent directories as needed.
|
|
65
|
+
*
|
|
66
|
+
* Reuses the transfer transport (single streamed stdin when available, the
|
|
67
|
+
* 48 KiB chunk path otherwise) so a large write does not pay one shell cycle
|
|
68
|
+
* per chunk.
|
|
69
|
+
* @param alias - configured host.
|
|
70
|
+
* @param remotePath - path on the target; overwritten when it exists.
|
|
71
|
+
* @param content - UTF-8 text to write.
|
|
72
|
+
* @param encoding - byte-order mark to emit (`utf8`, `utf8bom`, `utf16le`).
|
|
73
|
+
* @returns the operation kind, byte count, encoding written, and both texts
|
|
74
|
+
* the diff card compares.
|
|
75
|
+
*/
|
|
76
|
+
writeFile(alias: string, remotePath: string, content: string, encoding: RemoteTextEncoding): Promise<RemoteWriteOutcome>;
|
|
77
|
+
/**
|
|
78
|
+
* Replace `oldString` with `newString` inside a remote text file.
|
|
79
|
+
*
|
|
80
|
+
* The replacement is literal, never a pattern, and the file's existing
|
|
81
|
+
* encoding is preserved. Without `replaceAll` the edit is refused unless the
|
|
82
|
+
* needle is unique, so a stale needle cannot rewrite the wrong region.
|
|
83
|
+
* @param alias - configured host.
|
|
84
|
+
* @param remotePath - path on the target.
|
|
85
|
+
* @param oldString - exact text to find.
|
|
86
|
+
* @param newString - text to substitute.
|
|
87
|
+
* @param replaceAll - replace every occurrence instead of requiring one.
|
|
88
|
+
* @returns the replacement count, the file's new size, and both texts the
|
|
89
|
+
* diff card compares.
|
|
90
|
+
*/
|
|
91
|
+
editFile(alias: string, remotePath: string, oldString: string, newString: string, replaceAll: boolean): Promise<RemoteEditOutcome>;
|
|
42
92
|
/** Upload one local file to a remote path. Defaults to SMB with WinRM fallback. */
|
|
43
93
|
upload(alias: string, localPath: string, remotePath: string, onProgress?: (progress: TransferProgress) => void, channel?: TransferChannel): Promise<{
|
|
44
94
|
bytes: number;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
* execution with a UTF-8 envelope, streaming console, service/process
|
|
4
4
|
* management, streamed file transfer, cluster), the /api/dsh-winrm
|
|
5
5
|
* route family plus the console WebSocket upgrade, the agent tools
|
|
6
|
-
* (winrm_list, winrm_exec, winrm_service, winrm_process,
|
|
7
|
-
* winrm_download, winrm_cluster), and
|
|
6
|
+
* (winrm_list, winrm_exec, winrm_service, winrm_process, winrm_read,
|
|
7
|
+
* winrm_write, winrm_edit, winrm_upload, winrm_download, winrm_cluster), and
|
|
8
|
+
* a system-prompt announcement. The
|
|
8
9
|
* browser half (./client) renders the host manager and operations panel.
|
|
9
10
|
* Everything rides official NPM SDK packages — no dsh source changes.
|
|
10
11
|
*
|
|
@@ -34,7 +35,7 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
|
34
35
|
enabled: z<boolean, boolean, "volatile-defined">;
|
|
35
36
|
}>>, "plain">;
|
|
36
37
|
/** Model-facing announcement: plugin presence, capabilities, and limits. */
|
|
37
|
-
export declare const WINRM_GUIDANCE = "\u672C\u673A\u5DF2\u5B89\u88C5 dsh-winrm \u63D2\u4EF6\uFF08DSH \u8FDC\u7A0B Windows \u8FD0\u7EF4\uFF0CWinRM/PowerShell Remoting\uFF09\uFF1A\u4FA7\u8FB9\u680F\u300CWindows\u300D\u5165\u53E3\uFF1B\u4EFF\u7167 dsh-ssh \u63D2\u4EF6\u5F00\u53D1\u3002\u80FD\u529B\uFF1A\u4E3B\u673A\u914D\u7F6E\u5B58 ~/.dsh/dsh-winrm.json\uFF08GUI \u914D\u7F6E\u540E agent \u65B9\u53EF\u4F7F\u7528\uFF09\uFF1Bwinrm_list \u5217\u51FA\u4E3B\u673A\u3001winrm_exec \u6267\u884C PowerShell \u547D\u4EE4\uFF08UTF-8 \u4FE1\u5C01\uFF0C\u4E2D\u6587\u4E0D\u4E71\u7801\uFF09\u3001winrm_service \u670D\u52A1\u7BA1\u7406\uFF08start/stop/restart/\u542F\u52A8\u7C7B\u578B\uFF09\u3001winrm_process \u8FDB\u7A0B\u7BA1\u7406\uFF08list/kill\uFF09\u3001winrm_upload/winrm_download \u6587\u4EF6\u4F20\u8F93\
|
|
38
|
+
export declare const WINRM_GUIDANCE = "\u672C\u673A\u5DF2\u5B89\u88C5 dsh-winrm \u63D2\u4EF6\uFF08DSH \u8FDC\u7A0B Windows \u8FD0\u7EF4\uFF0CWinRM/PowerShell Remoting\uFF09\uFF1A\u4FA7\u8FB9\u680F\u300CWindows\u300D\u5165\u53E3\uFF1B\u4EFF\u7167 dsh-ssh \u63D2\u4EF6\u5F00\u53D1\u3002\u80FD\u529B\uFF1A\u4E3B\u673A\u914D\u7F6E\u5B58 ~/.dsh/dsh-winrm.json\uFF08GUI \u914D\u7F6E\u540E agent \u65B9\u53EF\u4F7F\u7528\uFF09\uFF1Bwinrm_list \u5217\u51FA\u4E3B\u673A\u3001winrm_exec \u6267\u884C PowerShell \u547D\u4EE4\uFF08UTF-8 \u4FE1\u5C01\uFF0C\u4E2D\u6587\u4E0D\u4E71\u7801\uFF09\u3001winrm_service \u670D\u52A1\u7BA1\u7406\uFF08start/stop/restart/\u542F\u52A8\u7C7B\u578B\uFF09\u3001winrm_process \u8FDB\u7A0B\u7BA1\u7406\uFF08list/kill\uFF09\u3001winrm_read/winrm_write/winrm_edit \u8FDC\u7A0B\u6587\u672C\u6587\u4EF6\u8BFB\u5199\uFF08\u6309\u884C\u5206\u9875\u3001\u7F16\u7801\u611F\u77E5\u3001\u7CBE\u786E\u4E32\u66FF\u6362\uFF0C\u4F5C\u7528\u4E8E\u8FDC\u7AEF\u4E3B\u673A\u800C\u975E\u672C\u673A\uFF0C\u672C\u673A\u6587\u4EF6\u4ECD\u7528 read/write/edit\uFF09\u3001winrm_upload/winrm_download \u6587\u4EF6\u4F20\u8F93\uFF08channel=auto \u65F6\u82E5\u76EE\u6807 445 \u53EF\u8FBE\u8D70 SMB \u7BA1\u7406\u5171\u4EAB\uFF0C\u5426\u5219\u8D70 WinRM \u6D41\u5F0F\u4F20\u8F93\uFF1Bchannel=smb \u5728 445 \u5173\u95ED\u65F6\u76F4\u63A5\u62A5\u9519\u3001\u9700\u6539\u7528 winrm\uFF1B\u5927\u6587\u4EF6\u53EF\u4E0B\u8F7D\uFF0C\u6D41\u5F0F\u901A\u9053\u6309\u5B57\u8282\u6301\u7EED\u63A8\u8FDB\u3001\u4EC5\u5728\u5B57\u8282\u65AD\u6D41\u65F6\u624D\u5224\u8D85\u65F6\u3001\u5B8C\u6210\u540E\u7ECF SHA256 \u6821\u9A8C\uFF09\u3001winrm_cluster \u96C6\u7FA4\u5E76\u53D1\u6267\u884C\uFF1BWeb \u63A7\u5236\u53F0\u8D70 WebSocket\u3002\u8BA4\u8BC1\uFF1A\u901A\u8FC7\u539F\u751F Node.js WinRM \u5BA2\u6237\u7AEF\uFF08winrm-client\uFF09\u7684 NTLM/Negotiate \u4E0E Basic \u5B9E\u73B0\uFF0C\u517C\u5BB9\u672C\u5730\u8D26\u6237\u4E0E\u57DF\u8D26\u6237\uFF1B\u4F20\u8F93\u652F\u6301 http(5985)/https(5986)\u3002\u524D\u63D0\uFF1A\u76EE\u6807\u673A\u9700\u542F\u7528 WinRM\uFF08scripts/enable-winrm.ps1\uFF09\uFF0C\u672C\u673A\u65E0\u9700 Python \u2014\u2014 WinRM \u8D70\u7EAF Node \u4F9D\u8D56\u3002HTTP \u4EC5\u5EFA\u8BAE\u53D7\u4FE1\u5185\u7F51\u4F7F\u7528\uFF1B\u5BC6\u7801\u4EE5\u660E\u6587\u5B58\u5728\u7528\u6237\u4E3B\u76EE\u5F55\u79C1\u6709\u6587\u4EF6\uFF08\u6743\u9650 0600\uFF09\uFF1B\u547D\u4EE4\u8F93\u51FA\u53EF\u80FD\u542B\u654F\u611F\u4FE1\u606F\uFF1B\u4F20\u8F93/\u6267\u884C\u6D88\u8017\u771F\u5B9E\u8FDC\u7A0B\u8D44\u6E90\uFF0C\u5148\u786E\u8BA4\u518D\u64CD\u4F5C\u3002\u7528\u6237\u63D0\u5230\u300CWindows \u670D\u52A1\u5668 / WinRM / PowerShell \u8FDC\u7A0B / \u670D\u52A1\u7BA1\u7406 / \u8FDB\u7A0B\u7BA1\u7406\u300D\u65F6\u5373\u6307\u672C\u63D2\u4EF6\uFF0C\u8BF7\u636E\u6B64\u534F\u4F5C\u3002";
|
|
38
39
|
/**
|
|
39
40
|
* Mount the WinRM engine, routes, tools, and announcement.
|
|
40
41
|
* @param ctx - host plugin context carrying webServer/tools/systemPrompt.
|
|
@@ -108,6 +108,70 @@ export declare function psReadChunk(p: string, offset: number, count: number): s
|
|
|
108
108
|
* prints the number of bytes written.
|
|
109
109
|
*/
|
|
110
110
|
export declare function psWriteChunk(p: string, b64: string, append: boolean): string;
|
|
111
|
+
/**
|
|
112
|
+
* JSON: one bounded, line-numbered window of a remote TEXT file.
|
|
113
|
+
*
|
|
114
|
+
* Decodes UTF-8 (with or without BOM), UTF-16LE and UTF-16BE by BOM, and
|
|
115
|
+
* rejects invalid UTF-8 instead of returning mojibake — a binary file fails
|
|
116
|
+
* loudly rather than as unreadable text. Lines are numbered from 1 over the
|
|
117
|
+
* whole file, `offset` selects the first returned line, and the window stops
|
|
118
|
+
* early once the joined text would exceed `maxBytes` (reported as
|
|
119
|
+
* `truncated`, so the caller knows more lines remain). A line longer than
|
|
120
|
+
* `maxLineLength` is cut to `maxLineLength + 1` code units here; the Node half
|
|
121
|
+
* appends the truncation note.
|
|
122
|
+
*
|
|
123
|
+
* A missing path or a directory emits `{"exists":false}`; anything the reader
|
|
124
|
+
* rejects emits `{"error":…}`. Both keep the exit code at 0 so the caller
|
|
125
|
+
* always receives parseable JSON rather than a PowerShell error record.
|
|
126
|
+
* @param p - remote path.
|
|
127
|
+
* @param offset - 1-based first line to return.
|
|
128
|
+
* @param limit - maximum number of lines to return.
|
|
129
|
+
* @param maxLineLength - maximum characters kept per line.
|
|
130
|
+
* @param maxBytes - maximum UTF-8 bytes of joined line text.
|
|
131
|
+
* @returns the PowerShell fragment.
|
|
132
|
+
*/
|
|
133
|
+
export declare function psReadTextWindow(p: string, offset: number, limit: number, maxLineLength: number, maxBytes: number): string;
|
|
134
|
+
/**
|
|
135
|
+
* JSON: replace `oldB64` with `newB64` (both UTF-8, base64-carried) in a remote
|
|
136
|
+
* text file, preserving its encoding and writing through the same file.
|
|
137
|
+
*
|
|
138
|
+
* The search is ordinal and never pattern-based, so a replacement cannot be
|
|
139
|
+
* reinterpreted as a regular expression. Without `replaceAll` the edit is
|
|
140
|
+
* refused unless the needle appears exactly once, which is what keeps a stale
|
|
141
|
+
* or too-short needle from rewriting the wrong region. The file is read whole,
|
|
142
|
+
* so this is meant for source files and configuration, not for multi-hundred-
|
|
143
|
+
* megabyte logs.
|
|
144
|
+
*
|
|
145
|
+
* Failures (missing file, absent needle, ambiguous needle, undecodable bytes)
|
|
146
|
+
* are reported as `{"error":…}` with exit code 0 so the caller always receives
|
|
147
|
+
* parseable JSON.
|
|
148
|
+
* @param p - remote path.
|
|
149
|
+
* @param oldB64 - base64 of the UTF-8 text to find.
|
|
150
|
+
* @param newB64 - base64 of the UTF-8 text to substitute.
|
|
151
|
+
* @param replaceAll - replace every occurrence instead of requiring exactly one.
|
|
152
|
+
* @param basisMaxBytes - include the before/after text when the original file is
|
|
153
|
+
* at most this large; `0` omits the basis entirely.
|
|
154
|
+
* @returns the PowerShell fragment.
|
|
155
|
+
*/
|
|
156
|
+
export declare function psEditText(p: string, oldB64: string, newB64: string, replaceAll: boolean, basisMaxBytes?: number): string;
|
|
157
|
+
/**
|
|
158
|
+
* JSON: the whole remote text file as base64, or a refusal when it is missing
|
|
159
|
+
* or larger than `maxBytes`.
|
|
160
|
+
*
|
|
161
|
+
* The write tool needs the previous text to project a diff card, and the line
|
|
162
|
+
* window reader cannot serve that: it numbers lines and caps each one, so a
|
|
163
|
+
* long line would silently truncate inside the diff basis. The BOM detection
|
|
164
|
+
* repeats {@link psEditText}'s on purpose — both must decode identically, and a
|
|
165
|
+
* shared emitter would hide that requirement behind a helper neither could be
|
|
166
|
+
* reviewed against.
|
|
167
|
+
*
|
|
168
|
+
* Failures are reported as `{"error":…}` with exit code 0, so the caller always
|
|
169
|
+
* receives parseable JSON.
|
|
170
|
+
* @param p - remote path.
|
|
171
|
+
* @param maxBytes - largest file whose text is returned.
|
|
172
|
+
* @returns the PowerShell fragment.
|
|
173
|
+
*/
|
|
174
|
+
export declare function psReadTextBasis(p: string, maxBytes: number): string;
|
|
111
175
|
/** Magic stamped into the frame header that precedes every stream chunk. */
|
|
112
176
|
export declare const STREAM_FRAME_MAGIC = "DSHZ";
|
|
113
177
|
/** Frame header size: 4 magic bytes + 4 little-endian length bytes + 1 mode byte + 1 reserved. */
|
package/lib/types/protocol.d.ts
CHANGED
|
@@ -88,6 +88,60 @@ export interface RemoteDirEntry {
|
|
|
88
88
|
size: number;
|
|
89
89
|
mtimeMs: number;
|
|
90
90
|
}
|
|
91
|
+
/** One numbered line of a remote text file. */
|
|
92
|
+
export interface RemoteTextLine {
|
|
93
|
+
/** 1-based line number within the remote file. */
|
|
94
|
+
number: number;
|
|
95
|
+
/** Line text without its terminator; over-long lines are cut and annotated. */
|
|
96
|
+
text: string;
|
|
97
|
+
}
|
|
98
|
+
/** Result of reading a bounded window of a remote text file. */
|
|
99
|
+
export interface RemoteTextWindow {
|
|
100
|
+
path: string;
|
|
101
|
+
/** 1-based line number of the first returned line. */
|
|
102
|
+
offset: number;
|
|
103
|
+
lines: RemoteTextLine[];
|
|
104
|
+
/** Lines in the whole file, so the caller can page. */
|
|
105
|
+
totalLines: number;
|
|
106
|
+
/** Detected encoding: `utf8`, `utf8bom`, `utf16le` or `utf16be`. */
|
|
107
|
+
encoding: string;
|
|
108
|
+
/** True when the window stopped at the byte budget instead of at `limit`. */
|
|
109
|
+
truncatedByBytes: boolean;
|
|
110
|
+
/** Lines whose text was cut at the per-line cap and annotated in `lines`. */
|
|
111
|
+
truncatedLines: number;
|
|
112
|
+
}
|
|
113
|
+
/** Text encodings the writer can emit. */
|
|
114
|
+
export type RemoteTextEncoding = 'utf8' | 'utf8bom' | 'utf16le';
|
|
115
|
+
/** Result of writing a remote text file. */
|
|
116
|
+
export interface RemoteWriteOutcome {
|
|
117
|
+
path: string;
|
|
118
|
+
/** `create` when the path was absent, `update` when it was overwritten. */
|
|
119
|
+
operation: 'create' | 'update';
|
|
120
|
+
bytes: number;
|
|
121
|
+
encoding: RemoteTextEncoding;
|
|
122
|
+
/**
|
|
123
|
+
* Previous file text, or `null` for a create and for a file too large to
|
|
124
|
+
* serve as a diff basis. Only the presentation layer reads it.
|
|
125
|
+
*/
|
|
126
|
+
before: string | null;
|
|
127
|
+
/** Text after the write, on the same basis as {@link RemoteWriteOutcome.before}. */
|
|
128
|
+
after: string;
|
|
129
|
+
}
|
|
130
|
+
/** Result of an in-place remote text edit. */
|
|
131
|
+
export interface RemoteEditOutcome {
|
|
132
|
+
path: string;
|
|
133
|
+
/** Encoding the file already had, preserved across the edit. */
|
|
134
|
+
encoding: string;
|
|
135
|
+
replacements: number;
|
|
136
|
+
size: number;
|
|
137
|
+
/**
|
|
138
|
+
* File text before the edit, or `null` when the file was too large to serve
|
|
139
|
+
* as a diff basis. Only the presentation layer reads it.
|
|
140
|
+
*/
|
|
141
|
+
before: string | null;
|
|
142
|
+
/** Text after the edit, on the same basis as {@link RemoteEditOutcome.before}. */
|
|
143
|
+
after: string | null;
|
|
144
|
+
}
|
|
91
145
|
/** One server entry in a cluster run. */
|
|
92
146
|
export interface ClusterResult {
|
|
93
147
|
alias: string;
|
package/lib/types/tools.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* the same engine the web UI uses, so a host configured in the GUI is
|
|
4
4
|
* immediately operable by any agent, and vice versa.
|
|
5
5
|
*/
|
|
6
|
-
import type
|
|
6
|
+
import { type WinRmEngine } from './engine.ts';
|
|
7
7
|
/** The host-list tool. */
|
|
8
8
|
export declare function winrmListTool(engine: WinRmEngine): import("@deepseek-ai/dsh-tools").ToolDefinition;
|
|
9
9
|
/** The command-execution tool (PowerShell). */
|
|
@@ -18,3 +18,9 @@ export declare function winrmUploadTool(engine: WinRmEngine): import("@deepseek-
|
|
|
18
18
|
export declare function winrmDownloadTool(engine: WinRmEngine): import("@deepseek-ai/dsh-tools").ToolDefinition;
|
|
19
19
|
/** The cluster tool. */
|
|
20
20
|
export declare function winrmClusterTool(engine: WinRmEngine): import("@deepseek-ai/dsh-tools").ToolDefinition;
|
|
21
|
+
/** The remote read tool: a line-numbered window of a text file on the target. */
|
|
22
|
+
export declare function winrmReadTool(engine: WinRmEngine): import("@deepseek-ai/dsh-tools").ToolDefinition;
|
|
23
|
+
/** The remote write tool: replace a whole text file on the target. */
|
|
24
|
+
export declare function winrmWriteTool(engine: WinRmEngine): import("@deepseek-ai/dsh-tools").ToolDefinition;
|
|
25
|
+
/** The remote edit tool: replace an exact string inside a file on the target. */
|
|
26
|
+
export declare function winrmEditTool(engine: WinRmEngine): import("@deepseek-ai/dsh-tools").ToolDefinition;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
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 / streamed 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.
|
|
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 / remote text read-write-edit / streamed file transfer / cluster execution, plus agent tools (winrm_list, winrm_exec, winrm_service, winrm_process, winrm_read, winrm_write, winrm_edit, 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.4.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@11.7.0",
|
|
7
7
|
"engines": {
|
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
"@deepseek-ai/dsh-client-ui-renderer",
|
|
35
35
|
"@deepseek-ai/dsh-client-locale",
|
|
36
36
|
"@deepseek-ai/dsh-client-ui-layout",
|
|
37
|
-
"@deepseek-ai/dsh-client-ui-sidebar"
|
|
37
|
+
"@deepseek-ai/dsh-client-ui-sidebar",
|
|
38
|
+
"@deepseek-ai/dsh-client-ui-tool"
|
|
38
39
|
],
|
|
39
40
|
"platform": "web"
|
|
40
41
|
}
|
|
@@ -56,9 +57,11 @@
|
|
|
56
57
|
"@deepseek-ai/cordis-plugin-loader": "^1.0.5",
|
|
57
58
|
"@deepseek-ai/dsh-client-locale": "^0.2.0-rc.2",
|
|
58
59
|
"@deepseek-ai/dsh-client-ui-layout": "^0.2.0-rc.2",
|
|
60
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.2.1-rc.2",
|
|
59
61
|
"@deepseek-ai/dsh-client-ui-renderer": "^0.2.0-rc.2",
|
|
60
62
|
"@deepseek-ai/dsh-client-ui-sidebar": "^0.2.0-rc.2",
|
|
61
63
|
"@deepseek-ai/dsh-client-ui-slots": "^0.2.0-rc.2",
|
|
64
|
+
"@deepseek-ai/dsh-client-ui-tool": "^0.2.0-rc.2",
|
|
62
65
|
"@deepseek-ai/dsh-host-webserver": "^0.2.0-rc.2",
|
|
63
66
|
"@deepseek-ai/dsh-llm": "^0.2.0-rc.2",
|
|
64
67
|
"@deepseek-ai/dsh-system-prompt": "^0.2.0-rc.2",
|
|
@@ -79,8 +82,9 @@
|
|
|
79
82
|
"lib/**/*.d.ts",
|
|
80
83
|
"src",
|
|
81
84
|
"cordis.patch.yml",
|
|
85
|
+
"docs/screenshots/*.png",
|
|
82
86
|
"README.md",
|
|
83
|
-
"README.
|
|
87
|
+
"README.en.md",
|
|
84
88
|
"RELEASING.md",
|
|
85
89
|
"scripts"
|
|
86
90
|
],
|