dsh-tool-docx 0.5.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.
- package/LICENSE +53 -0
- package/README.i18n.yaml +7 -0
- package/README.md +202 -0
- package/README.ru.md +202 -0
- package/README.zh.md +202 -0
- package/cordis.patch.yml +18 -0
- package/lib/fs-binary-D0seEN6R.js +63 -0
- package/lib/fs-binary-local-n8Ls-bn_.js +155 -0
- package/lib/fs-binary-local-plugin.js +24 -0
- package/lib/fs-binary-local.js +2 -0
- package/lib/fs-binary-sandbox-BobaE5Hr.js +135 -0
- package/lib/fs-binary-sandbox-plugin.js +31 -0
- package/lib/fs-binary-sandbox.js +2 -0
- package/lib/index.js +1507 -0
- package/lib/invariant.js +23 -0
- package/lib/types/caps.d.ts +14 -0
- package/lib/types/docx/extract.d.ts +18 -0
- package/lib/types/docx/generate.d.ts +17 -0
- package/lib/types/docx/zip.d.ts +17 -0
- package/lib/types/error.d.ts +20 -0
- package/lib/types/fs-binary-local-plugin.d.ts +19 -0
- package/lib/types/fs-binary-local.d.ts +45 -0
- package/lib/types/fs-binary-sandbox-plugin.d.ts +22 -0
- package/lib/types/fs-binary-sandbox.d.ts +51 -0
- package/lib/types/fs-binary.d.ts +52 -0
- package/lib/types/fsio-bytes.d.ts +27 -0
- package/lib/types/index.d.ts +32 -0
- package/lib/types/invariant.d.ts +16 -0
- package/lib/types/markdown.d.ts +35 -0
- package/lib/types/path-contains.d.ts +21 -0
- package/lib/types/sandbox.d.ts +63 -0
- package/lib/types/tool-utils.d.ts +56 -0
- package/lib/types/tools/create.d.ts +19 -0
- package/lib/types/tools/edit.d.ts +19 -0
- package/lib/types/tools/read.d.ts +16 -0
- package/lib/types/types.d.ts +77 -0
- package/package.json +114 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BroBFG
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
Attribution for derived code
|
|
26
|
+
|
|
27
|
+
Portions of `src/sandbox.ts`, the atomic-write pattern in `src/fsio-bytes.ts`,
|
|
28
|
+
and the containment logic in `src/path-contains.ts` are derived from the
|
|
29
|
+
deepseek-harness project
|
|
30
|
+
(https://github.com/deepseek-ai/deepseek-harness), distributed under the MIT
|
|
31
|
+
License:
|
|
32
|
+
|
|
33
|
+
MIT License
|
|
34
|
+
|
|
35
|
+
Copyright (c) 2026 DeepSeek
|
|
36
|
+
|
|
37
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
38
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
39
|
+
in the Software without restriction, including without limitation the rights
|
|
40
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
41
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
42
|
+
furnished to do so, subject to the following conditions:
|
|
43
|
+
|
|
44
|
+
The above copyright notice and this permission notice shall be included in all
|
|
45
|
+
copies or substantial portions of the Software.
|
|
46
|
+
|
|
47
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
48
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
49
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
50
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
51
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
52
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
53
|
+
SOFTWARE.
|
package/README.i18n.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Trilingual README consistency record: the git blob hash of each side as of the
|
|
2
|
+
# last confirmed-consistent state. All three languages carry equal authority; after
|
|
3
|
+
# editing any side, bring the others along and re-record the hashes
|
|
4
|
+
# (git hash-object <file>).
|
|
5
|
+
README.md: dcfd3f0a66b1965ed198bb57dff32e5a0b4b3ee4
|
|
6
|
+
README.zh.md: fbb4039011e581f9ef1a58ca0854e5f9f9b4ca39
|
|
7
|
+
README.ru.md: 24ba8e1dd45e8b084a0b46332a27570d59f6175f
|
package/README.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# dsh-tool-docx
|
|
2
|
+
|
|
3
|
+
English | [中文](README.zh.md) | [Русский](README.ru.md)
|
|
4
|
+
|
|
5
|
+
Model-facing Microsoft Word (`.docx`) tools for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): `docx_read` extracts a document as Markdown or structured JSON blocks, `docx_create` generates a new `.docx` from Markdown, and `docx_edit` replaces a document's content from Markdown while preserving its title/author/created properties. `.docx` is a ZIP of XML parts, so every tool reads the whole package through the bounded `ctx.fs.readBytes` primitive and writes packages through the plugin's `fsBinary` binary write service (or a host `ctx.fs` that provides `writeBytes` natively) — the same atomic, sandbox-fenced mutations the text tools use, without ever replacing the host's own filesystem.
|
|
6
|
+
|
|
7
|
+
This repository is the **standalone distribution** of the plugin. The plugin is **original work written for DeepSeek Harness** — an independent plugin developed by this project, initially in a local `deepseek-harness` checkout (the harness is its runtime target), not a copy of a plugin from the shared repository. It plugs into the harness's `tools`, `fs`, and `systemPrompt` services, and it builds and tests on its own against the published `@deepseek-ai/*` packages, so it can be installed into any harness checkout.
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- A DeepSeek Harness host (the `0.1.0-rc.7` line) whose filesystem seam provides the **read** primitive `fs.readBytes` — published in `@deepseek-ai/dsh-fs` since `0.1.0-rc.7`. The **write** side (`writeBytes`) is not in any published `dsh-fs` release, so the bundle mounts the plugin's [binary fs providers](#binary-fs-providers): a separate `fsBinary` service that implements `writeBytes` (fenced for sandboxed hosts) without touching `ctx.fs`. Without any binary writer — no `fsBinary` service and no native `ctx.fs.writeBytes` — `docx_read` still works, while `docx_create`/`docx_edit` fail with a typed `DOCX_HOST_FS_UNSUPPORTED` error naming the fix.
|
|
12
|
+
- The harness provides the peer services (the `0.1.0-rc.7` line): `cordis`, `dsh-tools`, `dsh-fs`, `dsh-llm`, `dsh-sandbox`, `dsh-sandbox-policy`, `dsh-system-prompt`, `dsh-invariants`, `dsh-user-approval`, `dsh-session`.
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
The official install path is the harness's own plugin manager — one command installs the package into the profile, and the profile launcher activates the bundle's [`cordis.patch.yml`](cordis.patch.yml) layer automatically (the package declares `dsh.bundle.patch`):
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
dsh plugin --profile web add github:BroBFG/dsh-tool-docx#v0.5.0
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`dsh plugin` runs pnpm inside the profile directory and reconciles `dsh.profile.bundles` against the installed state, so nothing else is needed — no `allowBuilds` entries (the package ships its built `lib/` and has no build scripts), no manual `cordis.patch.yml` editing, no `--patch` overlays. Restart the harness afterwards.
|
|
23
|
+
|
|
24
|
+
- **Update:** `dsh plugin --profile web update dsh-tool-docx`, or re-run `add` with the new tag.
|
|
25
|
+
- **Remove:** `dsh plugin --profile web remove dsh-tool-docx`.
|
|
26
|
+
- **Local development:** `dsh plugin --profile web add ../dsh-tool-docx` (relative specs are anchored to the directory you invoke from) or `dsh plugin --profile web add link:../dsh-tool-docx`.
|
|
27
|
+
|
|
28
|
+
> npm publication is planned but not yet available; the package ships under the standalone name `dsh-tool-docx` (the `dsh-tool-*` ecosystem convention), independent of the `@deepseek-ai` scope.
|
|
29
|
+
|
|
30
|
+
## Tools
|
|
31
|
+
|
|
32
|
+
| Tool | Purpose |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `docx_read(file_path, format?, max_chars?)` | Extract document body as Markdown (default) or structured JSON blocks plus `docProps`. Emits `fs/observed`. |
|
|
35
|
+
| `docx_create(file_path, markdown, title?, author?)` | Generate a new `.docx` from Markdown. Guarded `createIfAbsent`: an existing file is never blindly overwritten. |
|
|
36
|
+
| `docx_edit(file_path, markdown)` | Read the current document (validating it is a docx), preserve `docProps`, regenerate the body from the full Markdown, and write back with a version guard (`DOCX_STALE` on a concurrent change). |
|
|
37
|
+
|
|
38
|
+
All three resolve relative paths against the calling agent's session cwd, dispatch the `fs/write-intent` waterfall before mutating (the observation-policy plugin may supply its own intent), and record `fs/observed` on completion — so the sandbox fence, escalation fields, and read-before-write policy apply to docx mutations exactly as they do to `write`/`edit`.
|
|
39
|
+
|
|
40
|
+
## Config
|
|
41
|
+
|
|
42
|
+
| Field | Default | Meaning |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| `maxDocxBytes` | 64 MiB | Inclusive byte cap on a whole `.docx` file (read + ZIP expansion). |
|
|
45
|
+
| `maxMarkdownChars` | 1 000 000 | Inclusive character cap on the Markdown input to create/edit. |
|
|
46
|
+
| `maxReadChars` | 200 000 | Inclusive character cap on the Markdown returned by `docx_read`. |
|
|
47
|
+
|
|
48
|
+
## Binary fs providers
|
|
49
|
+
|
|
50
|
+
The base bundle's `fs-sandbox` row keeps providing `ctx.fs` **unchanged**. The bundle patch adds the docx tools plus one of the binary providers, which register the binary `writeBytes` primitive as a **separate `fsBinary` service** — the host filesystem is never replaced, so this plugin cannot break the harness boot; at worst, without the provider, the mutating tools report `DOCX_HOST_FS_UNSUPPORTED`:
|
|
51
|
+
|
|
52
|
+
- **`dsh-tool-docx/fs-binary-sandbox-plugin`** (mounted by the bundle; recommended for sandboxed hosts) — registers `fsBinary.writeBytes` fenced by the **same per-call policy** as every sandbox mutation: `workspace-write` containment, `read-only` denial, `danger-full-access` passthrough, `FS_SANDBOX_DENIED` on refusal (mapped to `DOCX_SANDBOX_DENIED` at the tool layer).
|
|
53
|
+
- **`dsh-tool-docx/fs-binary-local-plugin`** — registers `fsBinary.writeBytes` **unfenced**, for minimal contexts (tests, headless scripts) or hosts that fence above the provider. To mount it instead of the sandboxed one, override the bundle row in your profile `cordis.patch.yml`:
|
|
54
|
+
|
|
55
|
+
```yaml
|
|
56
|
+
- id: fs-binary-sandbox
|
|
57
|
+
disabled: true
|
|
58
|
+
- insert:
|
|
59
|
+
- id: fs-binary-local
|
|
60
|
+
name: dsh-tool-docx/fs-binary-local-plugin
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Both use the same probe → intent-guard (`createIfAbsent` / `replaceIfVersion`) → atomic-publish flow as the harness seam: a private owner-only staging directory, fsync, then atomic publication (a hard-link no-replace primitive for `createIfAbsent`), with per-target serialization. The first version omits the harness's Win32 DACL-preservation ceremony — a replacement inherits the owner-only ACL of the staged temp file.
|
|
64
|
+
|
|
65
|
+
For hosts that deliberately want the full backend mounted **as `ctx.fs`** (replacing `fs-sandbox`), the package also ships the provider classes `dsh-tool-docx/fs-binary-sandbox` and `dsh-tool-docx/fs-binary-local`; the replace-the-row recipe still applies:
|
|
66
|
+
|
|
67
|
+
```yaml
|
|
68
|
+
- id: fs-sandbox
|
|
69
|
+
disabled: true
|
|
70
|
+
- insert:
|
|
71
|
+
- id: fs-binary-sandbox
|
|
72
|
+
name: dsh-tool-docx/fs-binary-sandbox
|
|
73
|
+
- id: tool-docx
|
|
74
|
+
name: dsh-tool-docx
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Design notes
|
|
78
|
+
|
|
79
|
+
- **Extraction** (`src/docx/extract.ts`) walks `word/document.xml` with `fast-xml-parser`: headings (`Heading1`–`Heading6`, `Title`), bold/italic/strike runs, nested lists through `word/numbering.xml` (bullet vs decimal), pipe tables (merged cells approximate), external hyperlinks through `word/_rels/document.xml.rels`, and embedded images as counted placeholders. Unsupported constructs degrade to warnings, never failures.
|
|
80
|
+
- **Generation** (`src/docx/generate.ts`) renders the block model with the `docx` library: ATX headings, styled inline runs, 9-level bullet/numbered numbering, pipe tables, and `[text](url)` external hyperlinks. `parseMarkdown` (src/markdown.ts) accepts the subset the extractor emits, so read → edit → write round trips are stable.
|
|
81
|
+
- **Caps are enforced at the seam, not in the tool** — the whole-file byte cap flows into `ctx.fs.readBytes` (`FS_TOO_LARGE` maps to `DOCX_TOO_LARGE`), and the ZIP reader applies the same cap to the uncompressed total, so a compressed bomb cannot expand without limit.
|
|
82
|
+
- **Sandbox parity** — `src/sandbox.ts` mirrors `dsh-tool-fs`'s escalation API (`sandbox_permissions`/`justification` advertised only under a confining backend, denial marker mapping); extracting a shared controller is deferred (see below).
|
|
83
|
+
- **Host filesystem contract** — `src/fs-binary.ts` declares the binary contract the tools need and resolves the writer at call time: the plugin's `fsBinary` service when mounted, else a host `ctx.fs` that natively provides `writeBytes`. Without any binary writer it raises `DOCX_HOST_FS_UNSUPPORTED` with a pointer to the fix instead of a cryptic `fs.writeBytes is not a function`; `docx_read` needs only the published `ctx.fs.readBytes`.
|
|
84
|
+
|
|
85
|
+
## Model Experience
|
|
86
|
+
|
|
87
|
+
### System prompt
|
|
88
|
+
|
|
89
|
+
#### What the model sees
|
|
90
|
+
|
|
91
|
+
The `tool:docx-read` section below is registered once at plugin apply:
|
|
92
|
+
|
|
93
|
+
##### The docx guidance section
|
|
94
|
+
|
|
95
|
+
```markdown
|
|
96
|
+
MS Word .docx files are binary (ZIP+XML) and the read tool cannot read them. Use docx_read to extract a document as Markdown (default) or structured JSON blocks, docx_create to generate a new .docx from Markdown, and docx_edit to replace a document's content from Markdown while preserving its title/author/created properties. Legacy .doc is not supported — convert it to .docx first.
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
#### Token effect
|
|
100
|
+
|
|
101
|
+
Fixed guidance cost per request while the plugin is mounted; the section is unaffected by scoped tool restrictions.
|
|
102
|
+
|
|
103
|
+
#### KV Cache effect
|
|
104
|
+
|
|
105
|
+
Prefix-stable while the guidance text is unchanged. Plugin lifecycle or a text change may invalidate reuse from the first changed prompt section.
|
|
106
|
+
|
|
107
|
+
### Tool schemas
|
|
108
|
+
|
|
109
|
+
#### What the model sees
|
|
110
|
+
|
|
111
|
+
The generated `docx_read`, `docx_create`, and `docx_edit` schemas — parameters and canonical outputs as summarized in the [Tools](#tools) table. The byte/character caps are deployment settings, not model arguments; the escalation fields appear only under a confining filesystem backend.
|
|
112
|
+
|
|
113
|
+
#### Token effect
|
|
114
|
+
|
|
115
|
+
Fixed schema cost per request for each mounted tool; config disablement removes schemas and guidance together, while a scoped restriction removes only the schema.
|
|
116
|
+
|
|
117
|
+
#### KV Cache effect
|
|
118
|
+
|
|
119
|
+
Prefix-stable while definitions and visibility are unchanged. Config enablement, plugin lifecycle, or scoped restrictions may invalidate reuse from the first changed schema token.
|
|
120
|
+
|
|
121
|
+
### Read result
|
|
122
|
+
|
|
123
|
+
#### What the model sees
|
|
124
|
+
|
|
125
|
+
A successful `docx_read` renders the extracted Markdown (or pretty-printed JSON blocks). Truncation appends `\n… (truncated)`; failures are typed messages such as `file not found: <path>`, `the document is encrypted (password-protected); decryption is not supported`, or the legacy hint `legacy .doc format is not supported — convert the document to .docx first`.
|
|
126
|
+
|
|
127
|
+
#### Token effect
|
|
128
|
+
|
|
129
|
+
Data-dependent results are capped by `maxReadChars` (or the call's `max_chars`) and resent until compaction.
|
|
130
|
+
|
|
131
|
+
#### KV Cache effect
|
|
132
|
+
|
|
133
|
+
Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
|
|
134
|
+
|
|
135
|
+
### Create/Edit result
|
|
136
|
+
|
|
137
|
+
#### What the model sees
|
|
138
|
+
|
|
139
|
+
A successful create/edit renders a short `<path>`/`<type>docx</type>` envelope with the byte size — never the document body. Warnings about approximations (images, merged cells, code blocks) are carried in the canonical `warnings` array and rendered as plain text.
|
|
140
|
+
|
|
141
|
+
#### Token effect
|
|
142
|
+
|
|
143
|
+
Only the retained call arguments (including the full Markdown input) and the short result add tokens; the generated package bytes never enter the session log.
|
|
144
|
+
|
|
145
|
+
#### KV Cache effect
|
|
146
|
+
|
|
147
|
+
Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
|
|
148
|
+
|
|
149
|
+
### Argument errors
|
|
150
|
+
|
|
151
|
+
#### What the model sees
|
|
152
|
+
|
|
153
|
+
Blank `file_path` becomes `Error: file_path must be a non-empty string`; markdown over the input cap becomes `Error: markdown exceeds the <n>-character limit`.
|
|
154
|
+
|
|
155
|
+
#### Token effect
|
|
156
|
+
|
|
157
|
+
Only the failing call adds these retained tokens.
|
|
158
|
+
|
|
159
|
+
#### KV Cache effect
|
|
160
|
+
|
|
161
|
+
Append-only; newly visible content follows the reusable request prefix and does not invalidate existing KV-cache entries.
|
|
162
|
+
|
|
163
|
+
## Known Limitations and Deferred Work
|
|
164
|
+
|
|
165
|
+
- **Legacy `.doc` (OLE) is unsupported** — binary OLE needs LibreOffice or Word COM conversion; the tools fail with `DOCX_LEGACY_DOC` and a hint to convert to `.docx` first.
|
|
166
|
+
- **Images are not extracted or embedded** — `docx_read` counts images and emits placeholders; `docx_create`/`docx_edit` drop image syntax with a warning. Extracting image bytes and embedding them on generate is deferred.
|
|
167
|
+
- **Round-trip regenerates the document** — styles, page setup, headers/footers, and section breaks are not preserved; an edit rebuilds the body with default styles, keeping only title/author/created. Layout fidelity is not a goal of the round trip.
|
|
168
|
+
- **Merged table cells approximate** — `gridSpan`/`vMerge` degrade to plain pipe-table cells with a warning; footnotes, endnotes, text boxes, and page breaks are dropped (warnings included).
|
|
169
|
+
- **The sandbox controller duplicates `dsh-tool-fs`** — extracting a shared `FsSandboxController` is deferred; the two copies must be kept in sync until then.
|
|
170
|
+
- **Markdown input subset** — blockquotes, horizontal rules, nested fences, and images are not represented; they degrade to paragraphs with a warning (fenced code becomes code-styled paragraphs).
|
|
171
|
+
|
|
172
|
+
## Development
|
|
173
|
+
|
|
174
|
+
```sh
|
|
175
|
+
pnpm install
|
|
176
|
+
pnpm typecheck # tsc over src/
|
|
177
|
+
pnpm build # tsc → lib/types + tsdown → lib/index.js, lib/invariant.js
|
|
178
|
+
pnpm test # vitest: unit conversion tests + consumer tests over a fake fs
|
|
179
|
+
pnpm pack # produce the npm tarball (files: lib/index.js, lib/invariant.js, lib/types/**/*.d.ts)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Layout:
|
|
183
|
+
|
|
184
|
+
- `src/docx/` — ZIP/XML extraction and `docx`-library generation;
|
|
185
|
+
- `src/tools/` — the three tool registrations;
|
|
186
|
+
- `src/fs-binary.ts` — the binary filesystem contract and the call-time writer resolver (`fsBinary` service or native `ctx.fs.writeBytes`);
|
|
187
|
+
- `src/fs-binary-local.ts`, `src/fs-binary-sandbox.ts`, `src/fsio-bytes.ts`, `src/path-contains.ts` — the binary fs provider classes and the atomic writer + containment helpers;
|
|
188
|
+
- `src/fs-binary-sandbox-plugin.ts`, `src/fs-binary-local-plugin.ts` — the namespace plugins that register the `fsBinary` service (the bundle's default mount);
|
|
189
|
+
- `tests/` — conversion round-trip tests, provider tests, and consumer tests against the published `ToolRuntime` service (exported since `dsh-tools@0.1.0-rc.7`).
|
|
190
|
+
|
|
191
|
+
## Relationship to deepseek-harness
|
|
192
|
+
|
|
193
|
+
This plugin is an **original, independent project written for DeepSeek Harness** — it plugs into the harness's public services (`tools`, `fs`, `systemPrompt`) and is developed in a local `deepseek-harness` checkout for testing against the harness. It is not a copy of a plugin from the shared `deepseek-harness` repository and is not part of it; this repository is the canonical distribution. Two implementation notes:
|
|
194
|
+
|
|
195
|
+
1. `src/fs-binary.ts` (host contract + writer resolver) — the binary `readBytes`/`writeBytes` contract is part of this plugin's design. `readBytes` is published in the harness's `dsh-fs` release line since `0.1.0-rc.7`; `writeBytes` is not, so the [binary fs providers](#binary-fs-providers) ship it as a separate `fsBinary` service instead of patching the host;
|
|
196
|
+
2. `tests/` runs against the published `ToolRuntime` service (exported since `dsh-tools@0.1.0-rc.7`), so the consumer tests exercise the real registry pipeline rather than a local double.
|
|
197
|
+
|
|
198
|
+
The plugin is developed and maintained entirely in this repository against the published `@deepseek-ai/*` packages; the harness checkout is only a runtime target used for integration verification, and carries no copy of this plugin.
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
MIT © 2026 BroBFG. Portions of `src/sandbox.ts`, the atomic-write pattern in `src/fsio-bytes.ts`, and the containment logic in `src/path-contains.ts` are derived from [deepseek-harness](https://github.com/deepseek-ai/deepseek-harness) (MIT, Copyright (c) 2026 DeepSeek) — see [LICENSE](LICENSE).
|
package/README.ru.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# dsh-tool-docx
|
|
2
|
+
|
|
3
|
+
[English](README.md) | [中文](README.zh.md) | Русский
|
|
4
|
+
|
|
5
|
+
Ориентированные на модель инструменты Microsoft Word (`.docx`) для [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): `docx_read` извлекает документ как Markdown или структурированные JSON-блоки, `docx_create` генерирует новый `.docx` из Markdown, а `docx_edit` заменяет содержимое документа из Markdown, сохраняя его свойства title/author/created. `.docx` — это ZIP-архив XML-частей, поэтому каждый инструмент читает весь пакет через ограниченный примитив `ctx.fs.readBytes` и записывает пакеты через бинарный сервис записи плагина `fsBinary` (или через `ctx.fs` хоста, если он нативно предоставляет `writeBytes`) — те же атомарные, ограждённые песочницей мутации, которые используют текстовые инструменты, при этом собственная файловая система хоста никогда не заменяется.
|
|
6
|
+
|
|
7
|
+
Этот репозиторий — **автономное распространение** плагина. Плагин — **оригинальная работа, написанная для DeepSeek Harness**: независимый плагин, разработанный этим проектом, изначально в локальном checkout `deepseek-harness` (harness — его среда выполнения), а не копия плагина из общего репозитория. Он подключается к сервисам harness `tools`, `fs` и `systemPrompt` и самостоятельно собирается и тестируется против опубликованных пакетов `@deepseek-ai/*`, поэтому его можно установить в любой checkout harness.
|
|
8
|
+
|
|
9
|
+
## Требования
|
|
10
|
+
|
|
11
|
+
- Хост DeepSeek Harness (линия `0.1.0-rc.7`), чей слой файловой системы предоставляет **примитив чтения** `fs.readBytes` — опубликован в `@deepseek-ai/dsh-fs` начиная с `0.1.0-rc.7`. **Сторона записи** (`writeBytes`) отсутствует во всех опубликованных релизах `dsh-fs`, поэтому бандл монтирует [бинарные fs-провайдеры](#бинарные-fs-провайдеры) плагина: отдельный сервис `fsBinary`, реализующий `writeBytes` (с ограждением для песочных хостов), не затрагивая `ctx.fs`. Без какого-либо бинарного writer'а — ни сервиса `fsBinary`, ни нативного `ctx.fs.writeBytes` — `docx_read` по-прежнему работает, а `docx_create`/`docx_edit` завершаются типизированной ошибкой `DOCX_HOST_FS_UNSUPPORTED` с указанием исправления.
|
|
12
|
+
- Harness предоставляет peer-сервисы (линия `0.1.0-rc.7`): `cordis`, `dsh-tools`, `dsh-fs`, `dsh-llm`, `dsh-sandbox`, `dsh-sandbox-policy`, `dsh-system-prompt`, `dsh-invariants`, `dsh-user-approval`, `dsh-session`.
|
|
13
|
+
|
|
14
|
+
## Установка
|
|
15
|
+
|
|
16
|
+
Официальный путь установки — собственный менеджер плагинов harness: одна команда устанавливает пакет в профиль, а лаунчер профиля автоматически активирует слой [`cordis.patch.yml`](cordis.patch.yml) бандла (пакет объявляет `dsh.bundle.patch`):
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
dsh plugin --profile web add github:BroBFG/dsh-tool-docx#v0.5.0
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`dsh plugin` запускает pnpm внутри каталога профиля и сверяет `dsh.profile.bundles` с установленным состоянием, поэтому больше ничего не нужно — ни записей `allowBuilds` (пакет поставляется с готовым `lib/` и без build-скриптов), ни ручного редактирования `cordis.patch.yml`, ни overlay через `--patch`. После этого перезапустите harness.
|
|
23
|
+
|
|
24
|
+
- **Обновление:** `dsh plugin --profile web update dsh-tool-docx`, либо повторный `add` с новым тегом.
|
|
25
|
+
- **Удаление:** `dsh plugin --profile web remove dsh-tool-docx`.
|
|
26
|
+
- **Локальная разработка:** `dsh plugin --profile web add ../dsh-tool-docx` (относительные spec привязываются к каталогу вызова) или `dsh plugin --profile web add link:../dsh-tool-docx`.
|
|
27
|
+
|
|
28
|
+
> Публикация в npm запланирована, но пока недоступна; пакет поставляется под самостоятельным именем `dsh-tool-docx` (конвенция экосистемы `dsh-tool-*`), независимо от scope `@deepseek-ai`.
|
|
29
|
+
|
|
30
|
+
## Инструменты
|
|
31
|
+
|
|
32
|
+
| Инструмент | Назначение |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `docx_read(file_path, format?, max_chars?)` | Извлекает тело документа как Markdown (по умолчанию) или структурированные JSON-блоки плюс `docProps`. Испускает `fs/observed`. |
|
|
35
|
+
| `docx_create(file_path, markdown, title?, author?)` | Генерирует новый `.docx` из Markdown. Защищён `createIfAbsent`: существующий файл никогда не перезаписывается вслепую. |
|
|
36
|
+
| `docx_edit(file_path, markdown)` | Читает текущий документ (проверяя, что это docx), сохраняет `docProps`, перегенерирует тело из полного Markdown и записывает обратно с защитой версии (`DOCX_STALE` при конкурентном изменении). |
|
|
37
|
+
|
|
38
|
+
Все три инструмента разрешают относительные пути относительно рабочей директории сессии вызывающего агента, перед изменением запускают водопад `fs/write-intent` (плагин политики наблюдения может предоставить свой intent) и фиксируют `fs/observed` по завершении — поэтому ограждение песочницы, поля эскалации и политика «читать перед записью» применяются к мутациям docx точно так же, как к `write`/`edit`.
|
|
39
|
+
|
|
40
|
+
## Конфигурация
|
|
41
|
+
|
|
42
|
+
| Поле | По умолчанию | Значение |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| `maxDocxBytes` | 64 MiB | Включительный байтовый лимит на весь файл `.docx` (чтение + распаковка ZIP). |
|
|
45
|
+
| `maxMarkdownChars` | 1 000 000 | Включительный лимит символов на входной Markdown для create/edit. |
|
|
46
|
+
| `maxReadChars` | 200 000 | Включительный лимит символов на Markdown, возвращаемый `docx_read`. |
|
|
47
|
+
|
|
48
|
+
## Бинарные fs-провайдеры
|
|
49
|
+
|
|
50
|
+
Строка `fs-sandbox` базового бандла продолжает предоставлять `ctx.fs` **без изменений**. Бандл добавляет рядом docx-инструменты и один из бинарных провайдеров, который регистрирует бинарный примитив `writeBytes` как **отдельный сервис `fsBinary`** — файловая система хоста никогда не заменяется, поэтому этот плагин не может сломать запуск harness; в худшем случае без провайдера изменяющие инструменты сообщают `DOCX_HOST_FS_UNSUPPORTED`:
|
|
51
|
+
|
|
52
|
+
- **`dsh-tool-docx/fs-binary-sandbox-plugin`** (монтируется бандлом; рекомендуется для песочных хостов) — регистрирует `fsBinary.writeBytes`, ограждённый **тем же по-вызовным политическим забором**, что и каждая мутация песочницы: containment `workspace-write`, отказ в `read-only`, пропуск в `danger-full-access`, `FS_SANDBOX_DENIED` при отказе (на уровне инструмента отображается в `DOCX_SANDBOX_DENIED`).
|
|
53
|
+
- **`dsh-tool-docx/fs-binary-local-plugin`** — регистрирует `fsBinary.writeBytes` **без забора**, для минимальных окружений (тесты, headless-скрипты) или там, где хост уже ограждает поверх провайдера. Чтобы смонтировать его вместо песочного, переопределите строку бандла в своём `cordis.patch.yml` профиля:
|
|
54
|
+
|
|
55
|
+
```yaml
|
|
56
|
+
- id: fs-binary-sandbox
|
|
57
|
+
disabled: true
|
|
58
|
+
- insert:
|
|
59
|
+
- id: fs-binary-local
|
|
60
|
+
name: dsh-tool-docx/fs-binary-local-plugin
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Оба используют тот же поток probe → intent-охрана (`createIfAbsent` / `replaceIfVersion`) → атомарная публикация, что и в seam harness: приватный owner-only staging-каталог, fsync, затем атомарная публикация (для `createIfAbsent` — hard-link без замены), с сериализацией по таргету. Первая версия опускает Win32 DACL-церемонию harness — заменённый файл наследует owner-only ACL временного файла.
|
|
64
|
+
|
|
65
|
+
Для хостов, которые намеренно хотят смонтировать полный бэкенд **как `ctx.fs`** (заменив `fs-sandbox`), пакет также поставляет классы-провайдеры `dsh-tool-docx/fs-binary-sandbox` и `dsh-tool-docx/fs-binary-local`; рецепт замены строки по-прежнему применим:
|
|
66
|
+
|
|
67
|
+
```yaml
|
|
68
|
+
- id: fs-sandbox
|
|
69
|
+
disabled: true
|
|
70
|
+
- insert:
|
|
71
|
+
- id: fs-binary-sandbox
|
|
72
|
+
name: dsh-tool-docx/fs-binary-sandbox
|
|
73
|
+
- id: tool-docx
|
|
74
|
+
name: dsh-tool-docx
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Заметки по дизайну
|
|
78
|
+
|
|
79
|
+
- **Извлечение** (`src/docx/extract.ts`) обходит `word/document.xml` с помощью `fast-xml-parser`: заголовки (`Heading1`–`Heading6`, `Title`), run-ы жирный/курсив/зачёркнутый, вложенные списки через `word/numbering.xml` (маркированный и нумерованный), таблицы с разделителями-пайпами (объединённые ячейки — приблизительно), внешние гиперссылки через `word/_rels/document.xml.rels` и встроенные изображения как считаемые плейсхолдеры. Неподдерживаемые конструкции деградируют до предупреждений, но никогда до ошибок.
|
|
80
|
+
- **Генерация** (`src/docx/generate.ts`) рендерит блочную модель библиотекой `docx`: ATX-заголовки, стилизованные строчные run-ы, маркированная/нумерованная нумерация 9 уровней, таблицы с пайпами и внешние гиперссылки `[text](url)`. `parseMarkdown` (src/markdown.ts) принимает то подмножество, которое выдаёт экстрактор, поэтому циклы «прочитать → изменить → записать» стабильны.
|
|
81
|
+
- **Лимиты применяются на стыке (seam), а не в инструменте** — байтовый лимит всего файла уходит в `ctx.fs.readBytes` (`FS_TOO_LARGE` отображается в `DOCX_TOO_LARGE`), а ZIP-ридер применяет тот же лимит к общему несжатому объёму, поэтому сжатая бомба не может распаковаться без ограничений.
|
|
82
|
+
- **Паритет песочницы** — `src/sandbox.ts` повторяет API эскалации `dsh-tool-fs` (`sandbox_permissions`/`justification` анонсируются только под ограничивающим бэкендом, отображение маркеров отказа); извлечение общего контроллера — отложенная работа (см. ниже).
|
|
83
|
+
- **Контракт файловой системы хоста** — `src/fs-binary.ts` объявляет бинарный контракт, нужный инструментам, и разрешает writer во время вызова: сервис `fsBinary`, когда он смонтирован, иначе `ctx.fs` хоста, нативно предоставляющий `writeBytes`. Без какого-либо бинарного writer'а он выбрасывает `DOCX_HOST_FS_UNSUPPORTED` с указанием исправления вместо загадочного `fs.writeBytes is not a function`; для `docx_read` нужен только опубликованный `ctx.fs.readBytes`.
|
|
84
|
+
|
|
85
|
+
## Опыт модели
|
|
86
|
+
|
|
87
|
+
### Системный промпт
|
|
88
|
+
|
|
89
|
+
#### Что видит модель
|
|
90
|
+
|
|
91
|
+
Раздел `tool:docx-read` ниже регистрируется один раз при применении плагина:
|
|
92
|
+
|
|
93
|
+
##### Раздел-руководство по docx
|
|
94
|
+
|
|
95
|
+
```markdown
|
|
96
|
+
MS Word .docx files are binary (ZIP+XML) and the read tool cannot read them. Use docx_read to extract a document as Markdown (default) or structured JSON blocks, docx_create to generate a new .docx from Markdown, and docx_edit to replace a document's content from Markdown while preserving its title/author/created properties. Legacy .doc is not supported — convert it to .docx first.
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
#### Влияние на токены
|
|
100
|
+
|
|
101
|
+
Фиксированная стоимость руководства на каждый запрос, пока плагин подключён; раздел не зависит от ограничений инструментов по scope.
|
|
102
|
+
|
|
103
|
+
#### Влияние на KV-кэш
|
|
104
|
+
|
|
105
|
+
Префиксно-стабилен, пока текст руководства не меняется. Жизненный цикл плагина или изменение текста могут инвалидировать переиспользование с первого изменённого раздела промпта.
|
|
106
|
+
|
|
107
|
+
### Схемы инструментов
|
|
108
|
+
|
|
109
|
+
#### Что видит модель
|
|
110
|
+
|
|
111
|
+
Сгенерированные схемы `docx_read`, `docx_create` и `docx_edit` — параметры и канонические результаты в сводке таблицы [Инструменты](#инструменты). Байтовые/символьные лимиты — это настройки развёртывания, а не аргументы модели; поля эскалации появляются только под ограничивающим файловым бэкендом.
|
|
112
|
+
|
|
113
|
+
#### Влияние на токены
|
|
114
|
+
|
|
115
|
+
Фиксированная стоимость схем на каждый запрос для каждого подключённого инструмента; отключение в конфигурации удаляет схемы и руководство вместе, а ограничение по scope удаляет только схему.
|
|
116
|
+
|
|
117
|
+
#### Влияние на KV-кэш
|
|
118
|
+
|
|
119
|
+
Префиксно-стабилен, пока определения и видимость не меняются. Включение в конфигурации, жизненный цикл плагина или ограничения по scope могут инвалидировать переиспользование с первого изменённого токена схемы.
|
|
120
|
+
|
|
121
|
+
### Результат чтения
|
|
122
|
+
|
|
123
|
+
#### Что видит модель
|
|
124
|
+
|
|
125
|
+
Успешный `docx_read` рендерит извлечённый Markdown (или форматированные JSON-блоки). При усечении добавляется `\n… (truncated)`; ошибки — типизированные сообщения, например `file not found: <path>`, `the document is encrypted (password-protected); decryption is not supported` или подсказка для legacy `legacy .doc format is not supported — convert the document to .docx first`.
|
|
126
|
+
|
|
127
|
+
#### Влияние на токены
|
|
128
|
+
|
|
129
|
+
Зависящие от данных результаты ограничены `maxReadChars` (или `max_chars` из вызова) и пересылаются до компактификации.
|
|
130
|
+
|
|
131
|
+
#### Влияние на KV-кэш
|
|
132
|
+
|
|
133
|
+
Только добавление; вновь видимое содержимое следует за переиспользуемым префиксом запроса и не инвалидирует существующие записи KV-кэша.
|
|
134
|
+
|
|
135
|
+
### Результат создания/редактирования
|
|
136
|
+
|
|
137
|
+
#### Что видит модель
|
|
138
|
+
|
|
139
|
+
Успешное создание/редактирование рендерит короткий конверт `<path>`/`<type>docx</type>` с размером в байтах — никогда тело документа. Предупреждения об приближениях (изображения, объединённые ячейки, блоки кода) переносятся в канонический массив `warnings` и рендерятся как обычный текст.
|
|
140
|
+
|
|
141
|
+
#### Влияние на токены
|
|
142
|
+
|
|
143
|
+
Только сохранённые аргументы вызова (включая полный Markdown-ввод) и короткий результат добавляют токены; байты сгенерированного пакета никогда не попадают в журнал сессии.
|
|
144
|
+
|
|
145
|
+
#### Влияние на KV-кэш
|
|
146
|
+
|
|
147
|
+
Только добавление; вновь видимое содержимое следует за переиспользуемым префиксом запроса и не инвалидирует существующие записи KV-кэша.
|
|
148
|
+
|
|
149
|
+
### Ошибки аргументов
|
|
150
|
+
|
|
151
|
+
#### Что видит модель
|
|
152
|
+
|
|
153
|
+
Пустой `file_path` становится `Error: file_path must be a non-empty string`; markdown сверх лимита ввода становится `Error: markdown exceeds the <n>-character limit`.
|
|
154
|
+
|
|
155
|
+
#### Влияние на токены
|
|
156
|
+
|
|
157
|
+
Только неудачный вызов добавляет эти сохранённые токены.
|
|
158
|
+
|
|
159
|
+
#### Влияние на KV-кэш
|
|
160
|
+
|
|
161
|
+
Только добавление; вновь видимое содержимое следует за переиспользуемым префиксом запроса и не инвалидирует существующие записи KV-кэша.
|
|
162
|
+
|
|
163
|
+
## Известные ограничения и отложенная работа
|
|
164
|
+
|
|
165
|
+
- **Legacy `.doc` (OLE) не поддерживается** — бинарный OLE требует конвертации через LibreOffice или Word COM; инструменты завершаются с `DOCX_LEGACY_DOC` и подсказкой сначала сконвертировать в `.docx`.
|
|
166
|
+
- **Изображения не извлекаются и не встраиваются** — `docx_read` подсчитывает изображения и выводит плейсхолдеры; `docx_create`/`docx_edit` отбрасывают синтаксис изображений с предупреждением. Извлечение байтов изображений и их встраивание при генерации — отложенная работа.
|
|
167
|
+
- **Цикл «прочитать → изменить» перегенерирует документ** — стили, настройки страницы, колонтитулы и разрывы разделов не сохраняются; редактирование пересобирает тело со стилями по умолчанию, сохраняя только title/author/created. Точность вёрстки не является целью цикла.
|
|
168
|
+
- **Объединённые ячейки таблиц — приблизительно** — `gridSpan`/`vMerge` деградируют до обычных ячеек пайп-таблиц с предупреждением; сноски, концевые сноски, текстовые поля и разрывы страниц отбрасываются (с предупреждениями).
|
|
169
|
+
- **Контроллер песочницы дублирует `dsh-tool-fs`** — извлечение общего `FsSandboxController` — отложенная работа; до тех пор две копии должны поддерживаться в синхронизации.
|
|
170
|
+
- **Подмножество Markdown-ввода** — цитаты, горизонтальные линии, вложенные ограждения и изображения не представлены; они деградируют до абзацев с предупреждением (ограждённый код становится абзацами в стиле кода).
|
|
171
|
+
|
|
172
|
+
## Разработка
|
|
173
|
+
|
|
174
|
+
```sh
|
|
175
|
+
pnpm install
|
|
176
|
+
pnpm typecheck # tsc по src/
|
|
177
|
+
pnpm build # tsc → lib/types + tsdown → lib/index.js, lib/invariant.js
|
|
178
|
+
pnpm test # vitest: юнит-тесты конвертации + потребительские тесты на фейковой fs
|
|
179
|
+
pnpm pack # собрать npm-тарбол (files: lib/index.js, lib/invariant.js, lib/types/**/*.d.ts)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Структура:
|
|
183
|
+
|
|
184
|
+
- `src/docx/` — извлечение ZIP/XML и генерация библиотекой `docx`;
|
|
185
|
+
- `src/tools/` — три регистрации инструментов;
|
|
186
|
+
- `src/fs-binary.ts` — контракт бинарной файловой системы и разрешение writer'а во время вызова (`fsBinary`-сервис или нативный `ctx.fs.writeBytes`);
|
|
187
|
+
- `src/fs-binary-local.ts`, `src/fs-binary-sandbox.ts`, `src/fsio-bytes.ts`, `src/path-contains.ts` — классы бинарных fs-провайдеров, атомарный writer и хелперы containment;
|
|
188
|
+
- `src/fs-binary-sandbox-plugin.ts`, `src/fs-binary-local-plugin.ts` — namespace-плагины, регистрирующие сервис `fsBinary` (монтирование по умолчанию из бандла);
|
|
189
|
+
- `tests/` — тесты циклов конвертации, тесты провайдера и потребительские тесты против опубликованного сервиса `ToolRuntime` (экспортируется с `dsh-tools@0.1.0-rc.7`).
|
|
190
|
+
|
|
191
|
+
## Отношение к deepseek-harness
|
|
192
|
+
|
|
193
|
+
Этот плагин — **оригинальный, независимый проект, написанный для DeepSeek Harness**: он подключается к публичным сервисам harness (`tools`, `fs`, `systemPrompt`) и разрабатывается в локальном checkout `deepseek-harness` для тестирования против harness. Он не является копией плагина из общего репозитория `deepseek-harness` и не является его частью; этот репозиторий — канонический канал распространения. Две заметки по реализации:
|
|
194
|
+
|
|
195
|
+
1. `src/fs-binary.ts` (контракт хоста + разрешение writer'а) — бинарный контракт `readBytes`/`writeBytes` — часть дизайна этого плагина. `readBytes` опубликован в линии релизов `dsh-fs` harness начиная с `0.1.0-rc.7`; `writeBytes` — нет, поэтому [бинарные fs-провайдеры](#бинарные-fs-провайдеры) поставляют его как отдельный сервис `fsBinary`, а не патчат хост;
|
|
196
|
+
2. `tests/` работает против опубликованного сервиса `ToolRuntime` (экспортируется с `dsh-tools@0.1.0-rc.7`), поэтому потребительские тесты проходят через реальный конвейер реестра, а не локальный дублёр.
|
|
197
|
+
|
|
198
|
+
Плагин полностью разрабатывается и поддерживается в этом репозитории против опубликованных пакетов `@deepseek-ai/*`; checkout harness — лишь среда выполнения для интеграционной проверки и не содержит копии этого плагина.
|
|
199
|
+
|
|
200
|
+
## Лицензия
|
|
201
|
+
|
|
202
|
+
MIT © 2026 BroBFG. Части `src/sandbox.ts`, паттерн атомарной записи в `src/fsio-bytes.ts` и логика containment в `src/path-contains.ts` являются производными от [deepseek-harness](https://github.com/deepseek-ai/deepseek-harness) (MIT, Copyright (c) 2026 DeepSeek) — см. [LICENSE](LICENSE).
|