dsh-output-styles 0.4.2 → 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/CHANGELOG.md +44 -0
- package/README.es.md +9 -6
- package/README.hi.md +9 -6
- package/README.md +10 -6
- package/README.pt.md +9 -6
- package/README.zh.md +9 -6
- package/cordis.patch.yml +5 -0
- package/docs/renderer-protocol.md +10 -0
- package/docs/renderer-protocol.zh.md +8 -0
- package/lib/index.js +110 -16
- package/lib/{invariant-CEWlfnrw.js → invariant-ExQ574yb.js} +14 -1
- package/lib/invariant.js +1 -1
- package/lib/types/index.d.ts +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/runtime.d.ts +47 -1
- package/lib/types/runtime.d.ts.map +1 -1
- package/lib/types/types.d.ts +21 -0
- package/lib/types/types.d.ts.map +1 -1
- package/package.json +39 -29
- package/src/index.ts +1 -0
- package/src/runtime.ts +165 -18
- package/src/types.ts +21 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,50 @@ All notable changes to this project are documented in this file. The format
|
|
|
4
4
|
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the
|
|
5
5
|
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.5.0] - 2026-08-23
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`/export --save <path>`**: the export command now writes the rendered
|
|
12
|
+
document to a workspace path, gated by the user-approval service and the fs
|
|
13
|
+
service. `/export [md|markdown|html] [--renderer=<id>] [--save <path>]` — the
|
|
14
|
+
no-argument behavior is unchanged (the document is returned as output text);
|
|
15
|
+
`--save` writes only after `ctx.get('approval')` grants `allowed-once`
|
|
16
|
+
(fail-closed when the service is absent, rejects, cancels, or throws), uses
|
|
17
|
+
`ctx.get('fs')` for the actual write (fail-loud with a structured error when
|
|
18
|
+
absent), and passes the document through `sanitizeText` before writing.
|
|
19
|
+
`md` is accepted as a Markdown alias. `@deepseek-ai/dsh-fs` and
|
|
20
|
+
`@deepseek-ai/dsh-user-approval` are declared as optional peer dependencies.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **Standards alignment**: `package.json` declares `packageManager` (`pnpm@11.7.0`,
|
|
25
|
+
matching CI and the lockfile), and the `cordis.patch.yml` reference comment
|
|
26
|
+
now lists the two renderer-protocol config keys (`rules`, `enableExport`)
|
|
27
|
+
added in 0.4.0. No runtime behavior changes.
|
|
28
|
+
- Five READMEs: `/export --save` reference, the `fs:write` workshop permission,
|
|
29
|
+
and the test count refreshed to 127.
|
|
30
|
+
|
|
31
|
+
## [0.4.3] - 2026-08-22
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- **rc2 compatibility**: every `@deepseek-ai/dsh-*` dependency moves to
|
|
36
|
+
`0.1.1-rc.2` (devDependencies and the runtime storage dependencies pinned);
|
|
37
|
+
the `@deepseek-ai/dsh-session-projection` peer range raises to
|
|
38
|
+
`>=0.1.1-rc.2 <0.2.0` because the projection unit now uses the rc2-only
|
|
39
|
+
`stateSchema` + `wire` definition. The five README compatibility rows, the
|
|
40
|
+
`dshWorkshop` compatibility claim, the compat workflow's rc pins, and the
|
|
41
|
+
workspace `minimumReleaseAgeExclude` follow.
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- **rc2 session-projection definition**: the `style` projection registers a
|
|
46
|
+
`stateSchema` (persisted fold-state validator) and a client `wire`
|
|
47
|
+
(`viewSchema` + `view`) instead of the rc8 top-level `schema`/`view`, and
|
|
48
|
+
declares its `SessionProjectionStateMap` merge so the `register` overload
|
|
49
|
+
resolves the client-visible unit.
|
|
50
|
+
|
|
7
51
|
## [0.4.2] - 2026-08-21
|
|
8
52
|
|
|
9
53
|
### Changed
|
package/README.es.md
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
| Surface | Status |
|
|
26
26
|
|---|---|
|
|
27
|
-
| Harness | DeepSeek Harness `0.1.
|
|
27
|
+
| Harness | DeepSeek Harness `0.1.1-rc.2` |
|
|
28
28
|
| Node | `^22.19.0 || >=24.0.0` |
|
|
29
29
|
| Platforms | Todas (host + cliente web) |
|
|
30
30
|
| Model | Cualquiera (inyección en el prompt del sistema) |
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
- **Paridad Claude Code** — `keep-coding-instructions`, `force-for-plugin` (alias `force`), compatibilidad JSON `outputStyles`, directorios `stylesDir` por capas, recarga en caliente y fallback del proyecto sobre la costura de settings de DSH.
|
|
41
41
|
- **Registro de renderers (`output.render.*`)** — `ctx.outputRenderers` permite a cualquier plugin registrar un presenter puro, aplicado a través de la cascada `output.render/before`; renderers integrados `concise` y `step-by-step`.
|
|
42
42
|
- **Reglas por sesión/por herramienta** — `rules: [{ match: { tool: 'bash' }, style: 'concise' }]` nombran el renderer para solicitudes coincidentes; editables mediante la sección de settings `output-style-rules`.
|
|
43
|
-
- **`/export`** — renderiza la sesión actual a Markdown o HTML saneado a través de la tubería de render;
|
|
43
|
+
- **`/export`** — renderiza la sesión actual a Markdown o HTML saneado a través de la tubería de render; `--save <path>` escribe el documento saneado en esa ruta de workspace tras la aprobación del usuario. Cada render conserva el texto original junto al renderizado.
|
|
44
44
|
|
|
45
45
|
## Quick start
|
|
46
46
|
|
|
@@ -111,14 +111,14 @@ Todos los parámetros son campos Schemastery `Config` (modificables desde cordis
|
|
|
111
111
|
| `includeBuiltins` | `true` | Incluir los `styles/` del paquete como capa de menor prioridad |
|
|
112
112
|
| `watchStyles` | `true` | Recargar la librería cuando un archivo de estilo cambia en disco |
|
|
113
113
|
| `rules` | `[]` | Reglas de render por sesión/herramienta: `[{ match: { tool?, contentType?, session? }, style, priority? }]` |
|
|
114
|
-
| `enableExport` | `true` | Registrar el comando `/export` (exportación de sesión Markdown/HTML, consciente del renderer) |
|
|
114
|
+
| `enableExport` | `true` | Registrar el comando `/export` (exportación de sesión Markdown/HTML, consciente del renderer; `--save` escribe con aprobación) |
|
|
115
115
|
|
|
116
116
|
## Tools & surfaces
|
|
117
117
|
|
|
118
118
|
| Surface | Kind | Notes |
|
|
119
119
|
|---|---|---|
|
|
120
120
|
| `/style` | command | Lista estilos, cambia o restaura el valor por defecto del proyecto |
|
|
121
|
-
| `/export` | command | Renderiza la sesión actual a Markdown o HTML saneado |
|
|
121
|
+
| `/export` | command | Renderiza la sesión actual a Markdown o HTML saneado; `--save` escribe con aprobación |
|
|
122
122
|
| `output_style` | storage domain | Elección de estilo por sesión, indexada por sessionId |
|
|
123
123
|
| `systemPrompt.section()` | contribution | Inyecta el cuerpo del estilo actual en cada ensamblado |
|
|
124
124
|
| `output.render.*` | renderer registry | `ctx.outputRenderers` + la cascada `output.render/before` |
|
|
@@ -135,8 +135,10 @@ Todos los parámetros son campos Schemastery `Config` (modificables desde cordis
|
|
|
135
135
|
| `/style off` | Restaura el valor por defecto del proyecto (default de settings, luego `defaultStyle`) |
|
|
136
136
|
| `/style nope` | `error: unknown output style "nope" (available: …)` |
|
|
137
137
|
| `/export` | Renderiza la sesión actual a Markdown a través de la pipeline de render |
|
|
138
|
+
| `/export md` | Renderiza a Markdown (`md` es la forma abreviada de `markdown`) |
|
|
138
139
|
| `/export html` | Renderiza a HTML saneado |
|
|
139
140
|
| `/export --renderer=concise` | Renderiza forzando un renderer (reglas omitidas) |
|
|
141
|
+
| `/export md --save report.md` | Renderiza y luego escribe el documento saneado en `report.md` tras la aprobación |
|
|
140
142
|
|
|
141
143
|
## Style library
|
|
142
144
|
|
|
@@ -182,7 +184,7 @@ Filtrado contra el ecosistema DSH antes del desarrollo (instantánea 2026-08): n
|
|
|
182
184
|
|
|
183
185
|
## Permissions & data
|
|
184
186
|
|
|
185
|
-
- **Permissions**: el manifiesto de workshop declara `fs:read`, `fs:watch`, `storage:read`, `storage:write` y `settings:read`.
|
|
187
|
+
- **Permissions**: el manifiesto de workshop declara `fs:read`, `fs:write`, `fs:watch`, `storage:read`, `storage:write` y `settings:read`.
|
|
186
188
|
- **Data**: la elección de estilo vive en el dominio de almacenamiento `output_style` (indexada por sessionId); no se persiste otro estado, sin solicitudes de red.
|
|
187
189
|
- **Session log**: el nombre del estilo viene de `command/run`, el texto exacto inyectado de `request/header`; el marcador de procedencia `{ kind: 'plugin', plugin: 'dsh-output-styles' }` viaja en el registro del dominio.
|
|
188
190
|
|
|
@@ -191,6 +193,7 @@ Filtrado contra el ecosistema DSH antes del desarrollo (instantánea 2026-08): n
|
|
|
191
193
|
- **Solo servicios públicos.** Contribuye `systemPrompt`, comandos, almacenamiento y settings; sin cambios en engine / agent-loop / apiproxy / UI oficial.
|
|
192
194
|
- **Visible para el modelo ⟺ registrado.** Todo lo que el modelo ve es reconstruible desde el registro de sesión — sin un nuevo tipo de evento de sesión, sin cambios en el agent-loop.
|
|
193
195
|
- **Original siempre conservado.** Cada render (y `/export`) conserva el texto original junto al renderizado; para la exportación HTML se usa HTML saneado.
|
|
196
|
+
- **Escrituras en disco controladas.** `/export --save` escribe solo después de que el servicio de aprobación lo conceda, y el contenido escrito pasa primero por la función pura `sanitizeText`; sin un servicio de aprobación o fs no escribe nada (fail-closed).
|
|
194
197
|
|
|
195
198
|
## Known limitations
|
|
196
199
|
|
|
@@ -203,7 +206,7 @@ Filtrado contra el ecosistema DSH antes del desarrollo (instantánea 2026-08): n
|
|
|
203
206
|
```sh
|
|
204
207
|
pnpm install
|
|
205
208
|
pnpm run typecheck # ambos proyectos tsc
|
|
206
|
-
pnpm test # vitest —
|
|
209
|
+
pnpm test # vitest — 127 tests
|
|
207
210
|
pnpm run verify # typecheck + tests + self-contained (la puerta de prepublishOnly)
|
|
208
211
|
pnpm run build # artefactos lib/ (bundles host + client)
|
|
209
212
|
pnpm pack # tarball para dsh plugin add
|
package/README.hi.md
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
| Surface | Status |
|
|
26
26
|
|---|---|
|
|
27
|
-
| Harness | DeepSeek Harness `0.1.
|
|
27
|
+
| Harness | DeepSeek Harness `0.1.1-rc.2` |
|
|
28
28
|
| Node | `^22.19.0 || >=24.0.0` |
|
|
29
29
|
| Platforms | सभी (host + वेब क्लाइंट) |
|
|
30
30
|
| Model | कोई भी (सिस्टम-प्रॉम्प्ट इंजेक्शन) |
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
- **Claude Code समानता** — `keep-coding-instructions`, `force-for-plugin` (उपनाम `force`), `outputStyles` JSON संगतता, स्तरित `stylesDir` निर्देशिकाएँ, हॉट रीलोड और DSH settings सीम पर परियोजना-डिफ़ॉल्ट फ़ॉलबैक।
|
|
41
41
|
- **रेंडरर रजिस्ट्री (`output.render.*`)** — `ctx.outputRenderers` किसी भी प्लगइन को एक शुद्ध presenter पंजीकृत करने देता है, जो `output.render/before` वॉटरफ़ॉल से लागू होता है; अंतर्निहित रेंडरर `concise` और `step-by-step`।
|
|
42
42
|
- **प्रति-सत्र/प्रति-टूल नियम** — `rules: [{ match: { tool: 'bash' }, style: 'concise' }]` मिलान वाले अनुरोधों के लिए रेंडरर नामित करते हैं; `output-style-rules` settings अनुभाग से संपादन-योग्य।
|
|
43
|
-
- **`/export`** — रेंडर पाइपलाइन से वर्तमान सत्र को Markdown या सैनिटाइज़्ड HTML में प्रस्तुत करता है; हर रेंडर मूल पाठ को रेंडर किए गए के साथ रखता है।
|
|
43
|
+
- **`/export`** — रेंडर पाइपलाइन से वर्तमान सत्र को Markdown या सैनिटाइज़्ड HTML में प्रस्तुत करता है; `--save <path>` उपयोगकर्ता की स्वीकृति के बाद सैनिटाइज़्ड दस्तावेज़ को उस workspace पथ पर लिखता है। हर रेंडर मूल पाठ को रेंडर किए गए के साथ रखता है।
|
|
44
44
|
|
|
45
45
|
## Quick start
|
|
46
46
|
|
|
@@ -111,14 +111,14 @@ flowchart LR
|
|
|
111
111
|
| `includeBuiltins` | `true` | पैकेज के अंतर्निहित `styles/` को निम्नतम-प्राथमिकता परत के रूप में शामिल करें |
|
|
112
112
|
| `watchStyles` | `true` | डिस्क पर शैली फ़ाइल बदलने पर पुस्तकालय फिर से लोड करें |
|
|
113
113
|
| `rules` | `[]` | प्रति-सत्र/प्रति-टूल रेंडर नियम: `[{ match: { tool?, contentType?, session? }, style, priority? }]` |
|
|
114
|
-
| `enableExport` | `true` | `/export` कमांड पंजीकृत करें (Markdown/HTML सत्र निर्यात,
|
|
114
|
+
| `enableExport` | `true` | `/export` कमांड पंजीकृत करें (Markdown/HTML सत्र निर्यात, रेंडरर-जागरूक; `--save` स्वीकृति से लिखता है) |
|
|
115
115
|
|
|
116
116
|
## Tools & surfaces
|
|
117
117
|
|
|
118
118
|
| Surface | Kind | Notes |
|
|
119
119
|
|---|---|---|
|
|
120
120
|
| `/style` | command | शैलियाँ सूचीबद्ध करें, बदलें या परियोजना डिफ़ॉल्ट बहाल करें |
|
|
121
|
-
| `/export` | command | वर्तमान सत्र को Markdown या सैनिटाइज़्ड HTML में प्रस्तुत
|
|
121
|
+
| `/export` | command | वर्तमान सत्र को Markdown या सैनिटाइज़्ड HTML में प्रस्तुत करें; `--save` स्वीकृति से लिखता है |
|
|
122
122
|
| `output_style` | storage domain | sessionId से अनुक्रमित सत्र-स्कोप्ड शैली चयन |
|
|
123
123
|
| `systemPrompt.section()` | contribution | हर संयोजन पर वर्तमान शैली का मुख्य भाग इंजेक्ट करता है |
|
|
124
124
|
| `output.render.*` | renderer registry | `ctx.outputRenderers` + `output.render/before` वॉटरफ़ॉल |
|
|
@@ -135,8 +135,10 @@ flowchart LR
|
|
|
135
135
|
| `/style off` | परियोजना डिफ़ॉल्ट बहाल करें (settings डिफ़ॉल्ट, फिर `defaultStyle`) |
|
|
136
136
|
| `/style nope` | `error: unknown output style "nope" (available: …)` |
|
|
137
137
|
| `/export` | रेंडर पाइपलाइन से वर्तमान सत्र को Markdown में प्रस्तुत करें |
|
|
138
|
+
| `/export md` | Markdown में प्रस्तुत करें (`md`, `markdown` का संक्षिप्त रूप है) |
|
|
138
139
|
| `/export html` | सैनिटाइज़्ड HTML में प्रस्तुत करें |
|
|
139
140
|
| `/export --renderer=concise` | एक रेंडरर बाध्य करके प्रस्तुत करें (नियम छोड़े गए) |
|
|
141
|
+
| `/export md --save report.md` | प्रस्तुत करें, फिर स्वीकृति के बाद सैनिटाइज़्ड दस्तावेज़ को `report.md` में लिखें |
|
|
140
142
|
|
|
141
143
|
## Style library
|
|
142
144
|
|
|
@@ -182,7 +184,7 @@ flowchart LR
|
|
|
182
184
|
|
|
183
185
|
## Permissions & data
|
|
184
186
|
|
|
185
|
-
- **Permissions**: workshop मैनिफ़ेस्ट `fs:read`, `fs:watch`, `storage:read`, `storage:write` और `settings:read` घोषित करता है।
|
|
187
|
+
- **Permissions**: workshop मैनिफ़ेस्ट `fs:read`, `fs:write`, `fs:watch`, `storage:read`, `storage:write` और `settings:read` घोषित करता है।
|
|
186
188
|
- **Data**: शैली चयन `output_style` स्टोरेज डोमेन में रहता है (sessionId से अनुक्रमित); कोई अन्य स्थिति स्थायी नहीं, कोई नेटवर्क अनुरोध नहीं।
|
|
187
189
|
- **Session log**: शैली नाम `command/run` से आता है, सटीक इंजेक्ट किया गया पाठ `request/header` से; स्रोत मार्कर `{ kind: 'plugin', plugin: 'dsh-output-styles' }` डोमेन रिकॉर्ड में चलता है।
|
|
188
190
|
|
|
@@ -191,6 +193,7 @@ flowchart LR
|
|
|
191
193
|
- **केवल सार्वजनिक सेवाएँ।** `systemPrompt`, कमांड, स्टोरेज और settings योगदान करता है; engine / agent-loop / apiproxy / आधिकारिक UI में कोई बदलाव नहीं।
|
|
192
194
|
- **मॉडल-दृश्य ⟺ लॉग किया गया।** मॉडल जो देखता है वह सब सत्र लॉग से पुनर्निर्माण-योग्य है — कोई नया सत्र घटना प्रकार नहीं, कोई agent-loop बदलाव नहीं।
|
|
193
195
|
- **मूल हमेशा रखा गया।** हर रेंडर (और `/export`) मूल पाठ को रेंडर किए गए के साथ रखता है; HTML निर्यात के लिए सैनिटाइज़्ड HTML उपयोग होता है।
|
|
196
|
+
- **डिस्क लेखन गेटेड।** `/export --save` केवल स्वीकृति सेवा की अनुमति के बाद लिखता है, और लिखा गया कंटेंट पहले `sanitizeText` शुद्ध फ़ंक्शन से गुज़रता है; स्वीकृति या fs सेवा के बिना कुछ भी नहीं लिखा जाता (fail-closed)।
|
|
194
197
|
|
|
195
198
|
## Known limitations
|
|
196
199
|
|
|
@@ -203,7 +206,7 @@ flowchart LR
|
|
|
203
206
|
```sh
|
|
204
207
|
pnpm install
|
|
205
208
|
pnpm run typecheck # दोनों tsc परियोजनाएँ
|
|
206
|
-
pnpm test # vitest —
|
|
209
|
+
pnpm test # vitest — 127 tests
|
|
207
210
|
pnpm run verify # typecheck + tests + self-contained (prepublishOnly द्वार)
|
|
208
211
|
pnpm run build # lib/ कलाकृतियाँ (host + client बंडल)
|
|
209
212
|
pnpm pack # dsh plugin add के लिए tarball
|
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
3
|
# 🎨 dsh-output-styles
|
|
4
|
+
[](https://gitee.com/perrylink/dsh-output-styles)
|
|
4
5
|
|
|
5
6
|
**Claude Code `outputStyles` for DeepSeek Harness** — switch the model's output style at runtime, per session, durably.
|
|
6
7
|
|
|
@@ -24,7 +25,7 @@
|
|
|
24
25
|
|
|
25
26
|
| Surface | Status |
|
|
26
27
|
|---|---|
|
|
27
|
-
| Harness | DeepSeek Harness `0.1.
|
|
28
|
+
| Harness | DeepSeek Harness `0.1.1-rc.2` |
|
|
28
29
|
| Node | `^22.19.0 || >=24.0.0` |
|
|
29
30
|
| Platforms | All (host + web client) |
|
|
30
31
|
| Model | Any (system-prompt injection) |
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
- **Claude Code parity** — `keep-coding-instructions`, `force-for-plugin` (`force` alias), `outputStyles` JSON compatibility, layered `stylesDir` directories, hot reload, and project-default fallback over the DSH settings seam.
|
|
41
42
|
- **Renderer registry (`output.render.*`)** — `ctx.outputRenderers` lets any plugin register a pure presenter, applied through the `output.render/before` waterfall; built-in renderers `concise` and `step-by-step`.
|
|
42
43
|
- **Per-session/per-tool rules** — `rules: [{ match: { tool: 'bash' }, style: 'concise' }]` name the renderer for matching requests; editable through the `output-style-rules` settings section.
|
|
43
|
-
- **`/export`** — render the current session to Markdown or sanitized HTML through the render pipeline;
|
|
44
|
+
- **`/export`** — render the current session to Markdown or sanitized HTML through the render pipeline; `--save <path>` writes the sanitized document to that workspace path after user approval. Every render keeps the original text beside the rendered one.
|
|
44
45
|
|
|
45
46
|
## Quick start
|
|
46
47
|
|
|
@@ -111,14 +112,14 @@ All tunables are Schemastery `Config` fields (changeable from cordis.yml). Inval
|
|
|
111
112
|
| `includeBuiltins` | `true` | Include the package's bundled `styles/` as the lowest-priority layer |
|
|
112
113
|
| `watchStyles` | `true` | Reload the library when a style file changes on disk |
|
|
113
114
|
| `rules` | `[]` | Per-session/per-tool render rules: `[{ match: { tool?, contentType?, session? }, style, priority? }]` |
|
|
114
|
-
| `enableExport` | `true` | Register the `/export` command (Markdown/HTML session export, renderer-aware) |
|
|
115
|
+
| `enableExport` | `true` | Register the `/export` command (Markdown/HTML session export, renderer-aware; `--save` writes with approval) |
|
|
115
116
|
|
|
116
117
|
## Tools & surfaces
|
|
117
118
|
|
|
118
119
|
| Surface | Kind | Notes |
|
|
119
120
|
|---|---|---|
|
|
120
121
|
| `/style` | command | List styles, switch, or restore the project default |
|
|
121
|
-
| `/export` | command | Render the current session to Markdown or sanitized HTML |
|
|
122
|
+
| `/export` | command | Render the current session to Markdown or sanitized HTML; `--save` writes with approval |
|
|
122
123
|
| `output_style` | storage domain | Session-scoped style choice, keyed by sessionId |
|
|
123
124
|
| `systemPrompt.section()` | contribution | Injects the current style body at every assembly |
|
|
124
125
|
| `output.render.*` | renderer registry | `ctx.outputRenderers` + the `output.render/before` waterfall |
|
|
@@ -135,8 +136,10 @@ All tunables are Schemastery `Config` fields (changeable from cordis.yml). Inval
|
|
|
135
136
|
| `/style off` | Restore the project default (settings default, then `defaultStyle`) |
|
|
136
137
|
| `/style nope` | `error: unknown output style "nope" (available: …)` |
|
|
137
138
|
| `/export` | Render the current session to Markdown through the renderer pipeline |
|
|
139
|
+
| `/export md` | Render to Markdown (`md` is the shorthand for `markdown`) |
|
|
138
140
|
| `/export html` | Render to sanitized HTML |
|
|
139
141
|
| `/export --renderer=concise` | Render with one renderer forced (rules bypassed) |
|
|
142
|
+
| `/export md --save report.md` | Render, then write the sanitized document to `report.md` after approval |
|
|
140
143
|
|
|
141
144
|
## Style library
|
|
142
145
|
|
|
@@ -182,7 +185,7 @@ Screened against the DSH ecosystem before development (2026-08 snapshot): no `st
|
|
|
182
185
|
|
|
183
186
|
## Permissions & data
|
|
184
187
|
|
|
185
|
-
- **Permissions**: declares `fs:read`, `fs:watch`, `storage:read`, `storage:write`, and `settings:read` in its workshop manifest.
|
|
188
|
+
- **Permissions**: declares `fs:read`, `fs:write`, `fs:watch`, `storage:read`, `storage:write`, and `settings:read` in its workshop manifest.
|
|
186
189
|
- **Data**: the style choice lives in the `output_style` storage domain (keyed by sessionId); no other state is persisted, no network requests.
|
|
187
190
|
- **Session log**: the style name comes from `command/run`, the exact injected text from `request/header`; the provenance marker `{ kind: 'plugin', plugin: 'dsh-output-styles' }` rides in the domain record.
|
|
188
191
|
|
|
@@ -191,6 +194,7 @@ Screened against the DSH ecosystem before development (2026-08 snapshot): no `st
|
|
|
191
194
|
- **Public services only.** Contributes `systemPrompt`, commands, storage, and settings; no engine / agent-loop / apiproxy / official-UI changes.
|
|
192
195
|
- **Model-visible ⟺ logged.** Everything the model sees is reconstructable from the session log — no new session event type, no agent-loop changes.
|
|
193
196
|
- **Original always kept.** Every render (and `/export`) keeps the original text beside the rendered one; sanitized HTML is used for HTML export.
|
|
197
|
+
- **Disk writes gated.** `/export --save` writes only after the approval service grants it, and the written content passes through the `sanitizeText` pure function first; without an approval or fs service it writes nothing (fail-closed).
|
|
194
198
|
|
|
195
199
|
## Known limitations
|
|
196
200
|
|
|
@@ -203,7 +207,7 @@ Screened against the DSH ecosystem before development (2026-08 snapshot): no `st
|
|
|
203
207
|
```sh
|
|
204
208
|
pnpm install
|
|
205
209
|
pnpm run typecheck # both tsc projects
|
|
206
|
-
pnpm test # vitest —
|
|
210
|
+
pnpm test # vitest — 127 tests
|
|
207
211
|
pnpm run verify # typecheck + tests + self-contained (the prepublishOnly gate)
|
|
208
212
|
pnpm run build # lib/ artifacts (host + client bundles)
|
|
209
213
|
pnpm pack # tarball for dsh plugin add
|
package/README.pt.md
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
| Surface | Status |
|
|
26
26
|
|---|---|
|
|
27
|
-
| Harness | DeepSeek Harness `0.1.
|
|
27
|
+
| Harness | DeepSeek Harness `0.1.1-rc.2` |
|
|
28
28
|
| Node | `^22.19.0 || >=24.0.0` |
|
|
29
29
|
| Platforms | Todas (host + cliente web) |
|
|
30
30
|
| Model | Qualquer (injeção no prompt do sistema) |
|
|
@@ -40,7 +40,7 @@ O `dsh-output-styles` é o equivalente do `outputStyles` do Claude Code para o D
|
|
|
40
40
|
- **Paridade Claude Code** — `keep-coding-instructions`, `force-for-plugin` (alias `force`), compatibilidade JSON `outputStyles`, diretórios `stylesDir` em camadas, recarga a quente e fallback do projeto sobre a costura de settings do DSH.
|
|
41
41
|
- **Registro de renderers (`output.render.*`)** — `ctx.outputRenderers` permite a qualquer plugin registrar um presenter puro, aplicado pela cascata `output.render/before`; renderers integrados `concise` e `step-by-step`.
|
|
42
42
|
- **Regras por sessão/por ferramenta** — `rules: [{ match: { tool: 'bash' }, style: 'concise' }]` nomeiam o renderer para solicitações coincidentes; editáveis pela seção de settings `output-style-rules`.
|
|
43
|
-
- **`/export`** — renderiza a sessão atual para Markdown ou HTML saneado pela pipeline de render;
|
|
43
|
+
- **`/export`** — renderiza a sessão atual para Markdown ou HTML saneado pela pipeline de render; `--save <path>` escreve o documento saneado nessa rota de workspace após aprovação do usuário. Cada render mantém o texto original ao lado do renderizado.
|
|
44
44
|
|
|
45
45
|
## Quick start
|
|
46
46
|
|
|
@@ -111,14 +111,14 @@ Todos os parâmetros são campos Schemastery `Config` (alteráveis pelo cordis.y
|
|
|
111
111
|
| `includeBuiltins` | `true` | Incluir os `styles/` do pacote como camada de menor prioridade |
|
|
112
112
|
| `watchStyles` | `true` | Recarregar a biblioteca quando um arquivo de estilo muda em disco |
|
|
113
113
|
| `rules` | `[]` | Regras de render por sessão/ferramenta: `[{ match: { tool?, contentType?, session? }, style, priority? }]` |
|
|
114
|
-
| `enableExport` | `true` | Registrar o comando `/export` (exportação de sessão Markdown/HTML, ciente do renderer) |
|
|
114
|
+
| `enableExport` | `true` | Registrar o comando `/export` (exportação de sessão Markdown/HTML, ciente do renderer; `--save` escreve com aprovação) |
|
|
115
115
|
|
|
116
116
|
## Tools & surfaces
|
|
117
117
|
|
|
118
118
|
| Surface | Kind | Notes |
|
|
119
119
|
|---|---|---|
|
|
120
120
|
| `/style` | command | Lista estilos, alterna ou restaura o padrão do projeto |
|
|
121
|
-
| `/export` | command | Renderiza a sessão atual para Markdown ou HTML saneado |
|
|
121
|
+
| `/export` | command | Renderiza a sessão atual para Markdown ou HTML saneado; `--save` escreve com aprovação |
|
|
122
122
|
| `output_style` | storage domain | Escolha de estilo por sessão, indexada por sessionId |
|
|
123
123
|
| `systemPrompt.section()` | contribution | Injeta o corpo do estilo atual a cada montagem |
|
|
124
124
|
| `output.render.*` | renderer registry | `ctx.outputRenderers` + a cascata `output.render/before` |
|
|
@@ -135,8 +135,10 @@ Todos os parâmetros são campos Schemastery `Config` (alteráveis pelo cordis.y
|
|
|
135
135
|
| `/style off` | Restaura o padrão do projeto (default de settings, depois `defaultStyle`) |
|
|
136
136
|
| `/style nope` | `error: unknown output style "nope" (available: …)` |
|
|
137
137
|
| `/export` | Renderiza a sessão atual para Markdown pela pipeline de render |
|
|
138
|
+
| `/export md` | Renderiza para Markdown (`md` é a forma abreviada de `markdown`) |
|
|
138
139
|
| `/export html` | Renderiza para HTML saneado |
|
|
139
140
|
| `/export --renderer=concise` | Renderiza forçando um renderer (regras ignoradas) |
|
|
141
|
+
| `/export md --save report.md` | Renderiza e então escreve o documento saneado em `report.md` após aprovação |
|
|
140
142
|
|
|
141
143
|
## Style library
|
|
142
144
|
|
|
@@ -182,7 +184,7 @@ Filtrado contra o ecossistema DSH antes do desenvolvimento (instantânea 2026-08
|
|
|
182
184
|
|
|
183
185
|
## Permissions & data
|
|
184
186
|
|
|
185
|
-
- **Permissions**: o manifesto de workshop declara `fs:read`, `fs:watch`, `storage:read`, `storage:write` e `settings:read`.
|
|
187
|
+
- **Permissions**: o manifesto de workshop declara `fs:read`, `fs:write`, `fs:watch`, `storage:read`, `storage:write` e `settings:read`.
|
|
186
188
|
- **Data**: a escolha de estilo vive no domínio de armazenamento `output_style` (indexada por sessionId); nenhum outro estado é persistido, sem solicitações de rede.
|
|
187
189
|
- **Session log**: o nome do estilo vem de `command/run`, o texto exato injetado de `request/header`; o marcador de procedência `{ kind: 'plugin', plugin: 'dsh-output-styles' }` viaja no registro do domínio.
|
|
188
190
|
|
|
@@ -191,6 +193,7 @@ Filtrado contra o ecossistema DSH antes do desenvolvimento (instantânea 2026-08
|
|
|
191
193
|
- **Somente serviços públicos.** Contribui `systemPrompt`, comandos, armazenamento e settings; sem alterações em engine / agent-loop / apiproxy / UI oficial.
|
|
192
194
|
- **Visível para o modelo ⟺ registrado.** Tudo o que o modelo vê é reconstruível a partir do log de sessão — sem novo tipo de evento de sessão, sem alterações no agent-loop.
|
|
193
195
|
- **Original sempre conservado.** Cada render (e `/export`) mantém o texto original ao lado do renderizado; a exportação HTML usa HTML saneado.
|
|
196
|
+
- **Escritas em disco controladas.** `/export --save` escreve somente após o serviço de aprovação conceder, e o conteúdo escrito passa primeiro pela função pura `sanitizeText`; sem um serviço de aprovação ou fs, nada é escrito (fail-closed).
|
|
194
197
|
|
|
195
198
|
## Known limitations
|
|
196
199
|
|
|
@@ -203,7 +206,7 @@ Filtrado contra o ecossistema DSH antes do desenvolvimento (instantânea 2026-08
|
|
|
203
206
|
```sh
|
|
204
207
|
pnpm install
|
|
205
208
|
pnpm run typecheck # ambos os projetos tsc
|
|
206
|
-
pnpm test # vitest —
|
|
209
|
+
pnpm test # vitest — 127 tests
|
|
207
210
|
pnpm run verify # typecheck + tests + self-contained (a porta de prepublishOnly)
|
|
208
211
|
pnpm run build # artefatos lib/ (bundles host + client)
|
|
209
212
|
pnpm pack # tarball para dsh plugin add
|
package/README.zh.md
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
| Surface | Status |
|
|
26
26
|
|---|---|
|
|
27
|
-
| Harness | DeepSeek Harness `0.1.
|
|
27
|
+
| Harness | DeepSeek Harness `0.1.1-rc.2` |
|
|
28
28
|
| Node | `^22.19.0 || >=24.0.0` |
|
|
29
29
|
| Platforms | 全部(host + Web 客户端) |
|
|
30
30
|
| Model | 任意(系统提示注入) |
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
- **Claude Code 对齐** —— `keep-coding-instructions`、`force-for-plugin`(别名 `force`)、`outputStyles` JSON 兼容、分层 `stylesDir` 目录、热重载,以及通过 DSH settings 接缝的项目默认回退。
|
|
41
41
|
- **渲染器注册表(`output.render.*`)** —— `ctx.outputRenderers` 允许任意插件注册纯 presenter,经 `output.render/before` waterfall 应用;内置渲染器 `concise` 与 `step-by-step`。
|
|
42
42
|
- **按会话/按工具规则** —— `rules: [{ match: { tool: 'bash' }, style: 'concise' }]` 为匹配请求指定渲染器;可通过 `output-style-rules` 设置区编辑。
|
|
43
|
-
- **`/export`** —— 经渲染管线把当前会话导出为 Markdown 或净化 HTML
|
|
43
|
+
- **`/export`** —— 经渲染管线把当前会话导出为 Markdown 或净化 HTML;`--save <path>` 经用户审批后把净化文档写入该工作区路径。每次渲染都保留原文与渲染结果并列。
|
|
44
44
|
|
|
45
45
|
## Quick start
|
|
46
46
|
|
|
@@ -111,14 +111,14 @@ flowchart LR
|
|
|
111
111
|
| `includeBuiltins` | `true` | 将包内置 `styles/` 作为最低优先级层 |
|
|
112
112
|
| `watchStyles` | `true` | 风格文件在磁盘上变化时重载库 |
|
|
113
113
|
| `rules` | `[]` | 按会话/按工具渲染规则:`[{ match: { tool?, contentType?, session? }, style, priority? }]` |
|
|
114
|
-
| `enableExport` | `true` | 注册 `/export` 命令(Markdown/HTML
|
|
114
|
+
| `enableExport` | `true` | 注册 `/export` 命令(Markdown/HTML 会话导出,感知渲染器;`--save` 经审批写入) |
|
|
115
115
|
|
|
116
116
|
## Tools & surfaces
|
|
117
117
|
|
|
118
118
|
| Surface | Kind | Notes |
|
|
119
119
|
|---|---|---|
|
|
120
120
|
| `/style` | command | 列出风格、切换或恢复项目默认 |
|
|
121
|
-
| `/export` | command | 把当前会话渲染为 Markdown 或净化 HTML |
|
|
121
|
+
| `/export` | command | 把当前会话渲染为 Markdown 或净化 HTML;`--save` 经审批写入 |
|
|
122
122
|
| `output_style` | storage domain | 按 sessionId 隔离的会话级风格选择 |
|
|
123
123
|
| `systemPrompt.section()` | contribution | 在每次组装时注入当前风格正文 |
|
|
124
124
|
| `output.render.*` | renderer registry | `ctx.outputRenderers` + `output.render/before` waterfall |
|
|
@@ -135,8 +135,10 @@ flowchart LR
|
|
|
135
135
|
| `/style off` | 恢复项目默认(settings 默认,其次 `defaultStyle`) |
|
|
136
136
|
| `/style nope` | `error: unknown output style "nope" (available: …)` |
|
|
137
137
|
| `/export` | 经渲染管线把当前会话渲染为 Markdown |
|
|
138
|
+
| `/export md` | 渲染为 Markdown(`md` 是 `markdown` 的简写) |
|
|
138
139
|
| `/export html` | 渲染为净化 HTML |
|
|
139
140
|
| `/export --renderer=concise` | 强制指定一个渲染器渲染(跳过规则) |
|
|
141
|
+
| `/export md --save report.md` | 渲染后经审批把净化文档写入 `report.md` |
|
|
140
142
|
|
|
141
143
|
## Style library
|
|
142
144
|
|
|
@@ -182,7 +184,7 @@ flowchart LR
|
|
|
182
184
|
|
|
183
185
|
## Permissions & data
|
|
184
186
|
|
|
185
|
-
- **Permissions**:workshop 清单声明 `fs:read`、`fs:watch`、`storage:read`、`storage:write` 与 `settings:read`。
|
|
187
|
+
- **Permissions**:workshop 清单声明 `fs:read`、`fs:write`、`fs:watch`、`storage:read`、`storage:write` 与 `settings:read`。
|
|
186
188
|
- **Data**:风格选择存于 `output_style` 存储域(按 sessionId 隔离);不持久化其他状态,无网络请求。
|
|
187
189
|
- **Session log**:风格名来自 `command/run`,精确注入文本来自 `request/header`;来源标记 `{ kind: 'plugin', plugin: 'dsh-output-styles' }` 随域记录携带。
|
|
188
190
|
|
|
@@ -191,6 +193,7 @@ flowchart LR
|
|
|
191
193
|
- **仅公开服务。** 贡献 `systemPrompt`、命令、存储与 settings;不改 engine / agent-loop / apiproxy / 官方 UI。
|
|
192
194
|
- **模型可见 ⟺ 已记录。** 模型所见的一切都能从会话日志重建 —— 无新增会话事件类型、无 agent-loop 改动。
|
|
193
195
|
- **始终保留原文。** 每次渲染(含 `/export`)都保留原文与渲染结果并列;HTML 导出使用净化 HTML。
|
|
196
|
+
- **写盘有门禁。** `/export --save` 仅在审批服务放行后写入,且写入内容先经 `sanitizeText` 纯函数净化;缺少审批或 fs 服务时一律不写入(fail-closed)。
|
|
194
197
|
|
|
195
198
|
## Known limitations
|
|
196
199
|
|
|
@@ -203,7 +206,7 @@ flowchart LR
|
|
|
203
206
|
```sh
|
|
204
207
|
pnpm install
|
|
205
208
|
pnpm run typecheck # 两个 tsc 项目
|
|
206
|
-
pnpm test # vitest ——
|
|
209
|
+
pnpm test # vitest —— 127 个测试
|
|
207
210
|
pnpm run verify # typecheck + tests + self-contained(prepublishOnly 门禁)
|
|
208
211
|
pnpm run build # lib/ 产物(host + client 包)
|
|
209
212
|
pnpm pack # 供 dsh plugin add 的 tarball
|
package/cordis.patch.yml
CHANGED
|
@@ -31,6 +31,11 @@
|
|
|
31
31
|
# truncationMarker: "\n\n[style truncated]"
|
|
32
32
|
# includeBuiltins: true
|
|
33
33
|
# watchStyles: true
|
|
34
|
+
# rules: [] # per-session/per-tool render rules
|
|
35
|
+
# # [{ match: { tool?, contentType?, session? }, style, priority? }]
|
|
36
|
+
# enableExport: true # register the /export command
|
|
37
|
+
# # (md|html; --save <path> writes the
|
|
38
|
+
# # sanitized document after approval)
|
|
34
39
|
|
|
35
40
|
# Optional invariant companion (envelope-level checks). Only for profiles
|
|
36
41
|
# that DISABLE the main row above: the main plugin already registers
|
|
@@ -132,3 +132,13 @@ export function apply(ctx: Context): void {
|
|
|
132
132
|
const result = await ctx.outputRenderers.renderText(rawText, { tool: 'sql', contentType: 'text' })
|
|
133
133
|
// { original, rendered, rendererId, changed } — log both halves wherever you surface it.
|
|
134
134
|
```
|
|
135
|
+
|
|
136
|
+
## Export to disk
|
|
137
|
+
|
|
138
|
+
`/export` returns the rendered document as command output text. `/export
|
|
139
|
+
[md|markdown|html] [--renderer=<id>] --save <path>` additionally writes the
|
|
140
|
+
document to a workspace path: the document passes through the `sanitizeText`
|
|
141
|
+
pure function first, then the write is gated by the approval service
|
|
142
|
+
(`ctx.get('approval')`, fail-closed when absent) and performed by the fs
|
|
143
|
+
service (`ctx.get('fs')`, fail-loud when absent). The render pipeline itself is
|
|
144
|
+
unchanged — the same presenters and rule table apply before either output.
|
|
@@ -123,3 +123,11 @@ export function apply(ctx: Context): void {
|
|
|
123
123
|
const result = await ctx.outputRenderers.renderText(rawText, { tool: 'sql', contentType: 'text' })
|
|
124
124
|
// { original, rendered, rendererId, changed } —— 在任何展示它的地方把两半都记下来。
|
|
125
125
|
```
|
|
126
|
+
|
|
127
|
+
## 导出到磁盘
|
|
128
|
+
|
|
129
|
+
`/export` 把渲染后的文档作为命令输出文本返回。`/export [md|markdown|html]
|
|
130
|
+
[--renderer=<id>] --save <path>` 另外把文档写入工作区路径:文档先经 `sanitizeText`
|
|
131
|
+
纯函数净化,随后写入由审批服务(`ctx.get('approval')`,缺失则 fail-closed)把关、由
|
|
132
|
+
fs 服务(`ctx.get('fs')`,缺失则大声失败)执行。渲染流水线本身不变——两种输出之前都应用
|
|
133
|
+
同样的 presenter 与规则表。
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as
|
|
1
|
+
import { _ as styleFoldStateSchema, c as loadStyleLibrary, d as STYLE_COMMAND, f as applyStyleEvent, g as STYLE_SOURCE, h as OUTPUT_STYLE_DOMAIN, i as installInvariant, l as truncateStyle, m as OFF, o as STYLE_NAME_RE, p as parseStyleInput, s as isValidStyleName, t as PACKAGE_NAME, u as EMPTY_STYLE_STATE, v as styleSelectionSchema, y as styleSelectionViewSchema } from "./invariant-ExQ574yb.js";
|
|
2
2
|
import z from "@deepseek-ai/schemastery";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
@@ -732,10 +732,13 @@ async function apply(ctx, config) {
|
|
|
732
732
|
ctx.inject(["sessionProjections"], (projectionCtx) => {
|
|
733
733
|
projectionCtx.sessionProjections.register({
|
|
734
734
|
key: "style",
|
|
735
|
-
|
|
735
|
+
stateSchema: styleFoldStateSchema,
|
|
736
736
|
init: () => EMPTY_STYLE_STATE,
|
|
737
737
|
apply: applyStyleEvent,
|
|
738
|
-
|
|
738
|
+
wire: {
|
|
739
|
+
viewSchema: styleSelectionViewSchema,
|
|
740
|
+
view: (state) => viewStyleSelection(runtime, state)
|
|
741
|
+
},
|
|
739
742
|
stateVersion: 2
|
|
740
743
|
});
|
|
741
744
|
});
|
|
@@ -791,12 +794,12 @@ async function apply(ctx, config) {
|
|
|
791
794
|
commandCtx.commands.register({
|
|
792
795
|
name: "export",
|
|
793
796
|
description: "Export this session as Markdown or HTML (renderer-aware)",
|
|
794
|
-
input: { hint: "[markdown|html] [--renderer=<id>]" },
|
|
795
|
-
handler: async ({ agent, rawInput }) => {
|
|
797
|
+
input: { hint: "[markdown|html] [--renderer=<id>] [--save <path>]" },
|
|
798
|
+
handler: async ({ agent, rawInput, signal }) => {
|
|
796
799
|
const input = parseExportInput(rawInput);
|
|
797
800
|
if (input.kind === "error") return {
|
|
798
801
|
kind: "error",
|
|
799
|
-
text: "usage: /export [markdown|html] [--renderer=<id>]"
|
|
802
|
+
text: "usage: /export [markdown|html] [--renderer=<id>] [--save <path>]"
|
|
800
803
|
};
|
|
801
804
|
const lines = conversationLines(agent.session.events);
|
|
802
805
|
const rules = input.renderer === void 0 ? [...effectiveRules] : [{
|
|
@@ -804,28 +807,57 @@ async function apply(ctx, config) {
|
|
|
804
807
|
style: input.renderer,
|
|
805
808
|
priority: 0
|
|
806
809
|
}];
|
|
810
|
+
const document = renderExport(renderers, lines, input.format, rules);
|
|
811
|
+
if (input.save === void 0) return {
|
|
812
|
+
kind: "success",
|
|
813
|
+
text: document.text
|
|
814
|
+
};
|
|
815
|
+
const saved = await saveExportFile(ctx.get("fs"), ctx.get("approval"), agent, input.save, sanitizeText(document.text), signal);
|
|
816
|
+
if (saved.kind === "error") return {
|
|
817
|
+
kind: "error",
|
|
818
|
+
text: saved.text
|
|
819
|
+
};
|
|
807
820
|
return {
|
|
808
821
|
kind: "success",
|
|
809
|
-
text:
|
|
822
|
+
text: `saved ${input.format} export to ${saved.path}`
|
|
810
823
|
};
|
|
811
824
|
}
|
|
812
825
|
});
|
|
813
826
|
});
|
|
814
827
|
}
|
|
815
|
-
/** Parse `/export [markdown|html] [--renderer=<id>]` from the raw command input. */
|
|
828
|
+
/** Parse `/export [markdown|html] [--renderer=<id>] [--save <path>]` from the raw command input. */
|
|
816
829
|
function parseExportInput(rawInput) {
|
|
817
830
|
const raw = String(rawInput ?? "").trim();
|
|
818
831
|
const parts = raw === "" ? [] : raw.split(/\s+/);
|
|
819
832
|
let format = "markdown";
|
|
820
833
|
let renderer;
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
834
|
+
let save;
|
|
835
|
+
for (let index = 0; index < parts.length; index += 1) {
|
|
836
|
+
const part = parts[index];
|
|
837
|
+
if (part === void 0) continue;
|
|
838
|
+
if (part === "markdown" || part === "md") {
|
|
839
|
+
format = "markdown";
|
|
840
|
+
continue;
|
|
841
|
+
}
|
|
842
|
+
if (part === "html") {
|
|
843
|
+
format = "html";
|
|
844
|
+
continue;
|
|
845
|
+
}
|
|
846
|
+
const rendererMatch = /^--renderer=([a-z0-9][a-z0-9-]*)$/.exec(part);
|
|
847
|
+
if (rendererMatch !== null) {
|
|
848
|
+
renderer = rendererMatch[1];
|
|
824
849
|
continue;
|
|
825
850
|
}
|
|
826
|
-
const
|
|
827
|
-
if (
|
|
828
|
-
|
|
851
|
+
const saveInline = /^--save=(.+)$/.exec(part);
|
|
852
|
+
if (saveInline !== null) {
|
|
853
|
+
save = saveInline[1];
|
|
854
|
+
continue;
|
|
855
|
+
}
|
|
856
|
+
if (part === "--save") {
|
|
857
|
+
const next = parts[index + 1];
|
|
858
|
+
if (next === void 0 || next === "") return { kind: "error" };
|
|
859
|
+
save = next;
|
|
860
|
+
index += 1;
|
|
829
861
|
continue;
|
|
830
862
|
}
|
|
831
863
|
return { kind: "error" };
|
|
@@ -833,7 +865,69 @@ function parseExportInput(rawInput) {
|
|
|
833
865
|
return {
|
|
834
866
|
kind: "ok",
|
|
835
867
|
format,
|
|
836
|
-
...renderer === void 0 ? {} : { renderer }
|
|
868
|
+
...renderer === void 0 ? {} : { renderer },
|
|
869
|
+
...save === void 0 ? {} : { save }
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* Write one rendered export document to a workspace path after user approval.
|
|
874
|
+
* Fail-closed: a missing approval service, a rejected/cancelled/unavailable
|
|
875
|
+
* decision, or a throwing approval channel all deny the write; a missing fs
|
|
876
|
+
* service fails loudly with a structured error. The caller already sanitized
|
|
877
|
+
* `content` before this write.
|
|
878
|
+
* @param fs - the fs service (`ctx.get('fs')`), or undefined when none is composed.
|
|
879
|
+
* @param approval - the approval service (`ctx.get('approval')`), or undefined when none is composed.
|
|
880
|
+
* @param agent - the agent whose session the export belongs to (routes the approval).
|
|
881
|
+
* @param path - the workspace path to write.
|
|
882
|
+
* @param content - the sanitized document text.
|
|
883
|
+
* @param signal - the command's abort signal, forwarded to approval and resolve.
|
|
884
|
+
* @returns the written path, or a structured failure.
|
|
885
|
+
*/
|
|
886
|
+
async function saveExportFile(fs, approval, agent, path, content, signal) {
|
|
887
|
+
if (approval === void 0) return {
|
|
888
|
+
kind: "error",
|
|
889
|
+
code: "approval-unavailable",
|
|
890
|
+
text: "dsh-output-styles: /export --save requires an approval service (compose @deepseek-ai/dsh-user-approval); nothing was written"
|
|
891
|
+
};
|
|
892
|
+
let outcome;
|
|
893
|
+
try {
|
|
894
|
+
outcome = await approval.request({
|
|
895
|
+
agent,
|
|
896
|
+
toolName: "export",
|
|
897
|
+
reason: `write the exported document to ${path}`,
|
|
898
|
+
...signal === void 0 ? {} : { signal }
|
|
899
|
+
});
|
|
900
|
+
} catch {
|
|
901
|
+
outcome = "unavailable";
|
|
902
|
+
}
|
|
903
|
+
switch (outcome) {
|
|
904
|
+
case "allowed-once": break;
|
|
905
|
+
case "rejected": return {
|
|
906
|
+
kind: "error",
|
|
907
|
+
code: "approval-denied",
|
|
908
|
+
text: "dsh-output-styles: /export --save was rejected; nothing was written"
|
|
909
|
+
};
|
|
910
|
+
case "cancelled": return {
|
|
911
|
+
kind: "error",
|
|
912
|
+
code: "approval-cancelled",
|
|
913
|
+
text: "dsh-output-styles: /export --save was cancelled; nothing was written"
|
|
914
|
+
};
|
|
915
|
+
default: return {
|
|
916
|
+
kind: "error",
|
|
917
|
+
code: "approval-unavailable",
|
|
918
|
+
text: "dsh-output-styles: /export --save approval is unavailable; nothing was written"
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
if (fs === void 0) return {
|
|
922
|
+
kind: "error",
|
|
923
|
+
code: "fs-unavailable",
|
|
924
|
+
text: "dsh-output-styles: /export --save requires an fs service (compose @deepseek-ai/dsh-fs); nothing was written"
|
|
925
|
+
};
|
|
926
|
+
const target = await fs.resolve(path, signal === void 0 ? void 0 : { signal });
|
|
927
|
+
await fs.writeText(target, content);
|
|
928
|
+
return {
|
|
929
|
+
kind: "written",
|
|
930
|
+
path
|
|
837
931
|
};
|
|
838
932
|
}
|
|
839
933
|
//#endregion
|
|
@@ -857,4 +951,4 @@ const name = "dsh-output-styles";
|
|
|
857
951
|
*/
|
|
858
952
|
const inject = ["systemPrompt", "storageDomain"];
|
|
859
953
|
//#endregion
|
|
860
|
-
export { Config, DEFAULT_STYLES_DIR, EMPTY_STYLE_STATE, OFF, OUTPUT_STYLE_DOMAIN, OutputStyleRuntime, PACKAGE_NAME, STYLE_COMMAND, STYLE_NAME_RE, STYLE_SECTION_NAME, STYLE_SOURCE, apply, applyStyleEvent, inject, installInvariant, isValidStyleName, loadStyleLibrary, name, parseStyleInput, resolveConfig, styleSelectionSchema, styleSelectionViewSchema, truncateStyle };
|
|
954
|
+
export { Config, DEFAULT_STYLES_DIR, EMPTY_STYLE_STATE, OFF, OUTPUT_STYLE_DOMAIN, OutputStyleRuntime, PACKAGE_NAME, STYLE_COMMAND, STYLE_NAME_RE, STYLE_SECTION_NAME, STYLE_SOURCE, apply, applyStyleEvent, inject, installInvariant, isValidStyleName, loadStyleLibrary, name, parseStyleInput, resolveConfig, styleFoldStateSchema, styleSelectionSchema, styleSelectionViewSchema, truncateStyle };
|
|
@@ -53,6 +53,19 @@ const styleSelectionViewSchema = z.object({
|
|
|
53
53
|
})),
|
|
54
54
|
currentValue: z.string().min(1).nullable()
|
|
55
55
|
});
|
|
56
|
+
/**
|
|
57
|
+
* Validates the `style` projection's persisted fold state before it seeds a
|
|
58
|
+
* fold (the `stateSchema` of the registered unit). The state is plain JSON:
|
|
59
|
+
* the settled selection (`current`, null when off) plus the in-flight switch
|
|
60
|
+
* parked by `command/run` and resolved by its paired `command/done`.
|
|
61
|
+
*/
|
|
62
|
+
const styleFoldStateSchema = z.object({
|
|
63
|
+
current: z.string().min(1).nullable(),
|
|
64
|
+
pending: z.object({
|
|
65
|
+
commandId: z.string().min(1),
|
|
66
|
+
target: z.union([z.object({ name: z.string().min(1) }), z.object({ off: z.literal(true) })])
|
|
67
|
+
}).nullable()
|
|
68
|
+
});
|
|
56
69
|
//#endregion
|
|
57
70
|
//#region lib/types/style-command.js
|
|
58
71
|
/**
|
|
@@ -495,4 +508,4 @@ function getInvariantRegistry(ctx) {
|
|
|
495
508
|
*/
|
|
496
509
|
const apply = (ctx) => Promise.resolve(getInvariantRegistry(ctx).register(PACKAGE_NAME, installInvariant(COMPANION_FACTS)));
|
|
497
510
|
//#endregion
|
|
498
|
-
export {
|
|
511
|
+
export { styleFoldStateSchema as _, name as a, loadStyleLibrary as c, STYLE_COMMAND as d, applyStyleEvent as f, STYLE_SOURCE as g, OUTPUT_STYLE_DOMAIN as h, installInvariant as i, truncateStyle as l, OFF as m, apply as n, STYLE_NAME_RE as o, parseStyleInput as p, inject as r, isValidStyleName as s, PACKAGE_NAME as t, EMPTY_STYLE_STATE as u, styleSelectionSchema as v, styleSelectionViewSchema as y };
|
package/lib/invariant.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as name, i as installInvariant, n as apply, r as inject, t as PACKAGE_NAME } from "./invariant-
|
|
1
|
+
import { a as name, i as installInvariant, n as apply, r as inject, t as PACKAGE_NAME } from "./invariant-ExQ574yb.js";
|
|
2
2
|
export { PACKAGE_NAME, apply, inject, installInvariant, name };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export { applyStyleEvent, EMPTY_STYLE_STATE, parseStyleInput, STYLE_COMMAND, } f
|
|
|
23
23
|
export type { StyleFoldState, StyleInput } from './style-command.js';
|
|
24
24
|
export { isValidStyleName, loadStyleLibrary, STYLE_NAME_RE, truncateStyle, } from './style-library.js';
|
|
25
25
|
export type { OutputStyle } from './style-library.js';
|
|
26
|
-
export { OFF, OUTPUT_STYLE_DOMAIN, STYLE_SOURCE, styleSelectionSchema, styleSelectionViewSchema, } from './types.js';
|
|
26
|
+
export { OFF, OUTPUT_STYLE_DOMAIN, STYLE_SOURCE, styleFoldStateSchema, styleSelectionSchema, styleSelectionViewSchema, } from './types.js';
|
|
27
27
|
export type { StyleOption, StyleSelection, StyleSelectionView } from './types.js';
|
|
28
28
|
export { installInvariant, PACKAGE_NAME } from './invariant.js';
|
|
29
29
|
export type { InvariantFacts, InvariantInstaller, InvariantRegistry } from './invariant.js';
|
package/lib/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,6DAA6D;AAC7D,eAAO,MAAM,IAAI,sBAAsB,CAAA;AAEvC;;;;;;GAMG;AACH,eAAO,MAAM,MAAM,UAAoC,CAAA;AAEvD,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AACnD,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAChG,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,aAAa,GACd,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AACpE,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACb,aAAa,GACd,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,EACL,GAAG,EACH,mBAAmB,EACnB,YAAY,EACZ,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,YAAY,CAAA;AACnB,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AACjF,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC/D,YAAY,EAAE,cAAc,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAG3F,mBAAmB,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,6DAA6D;AAC7D,eAAO,MAAM,IAAI,sBAAsB,CAAA;AAEvC;;;;;;GAMG;AACH,eAAO,MAAM,MAAM,UAAoC,CAAA;AAEvD,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AACnD,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAChG,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,aAAa,GACd,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AACpE,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACb,aAAa,GACd,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,EACL,GAAG,EACH,mBAAmB,EACnB,YAAY,EACZ,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,YAAY,CAAA;AACnB,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AACjF,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC/D,YAAY,EAAE,cAAc,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAG3F,mBAAmB,YAAY,CAAA"}
|
package/lib/types/runtime.d.ts
CHANGED
|
@@ -146,10 +146,56 @@ type ExportInput = {
|
|
|
146
146
|
kind: 'ok';
|
|
147
147
|
format: 'markdown' | 'html';
|
|
148
148
|
renderer?: string;
|
|
149
|
+
save?: string;
|
|
149
150
|
} | {
|
|
150
151
|
kind: 'error';
|
|
151
152
|
};
|
|
152
|
-
/** Parse `/export [markdown|html] [--renderer=<id>]` from the raw command input. */
|
|
153
|
+
/** Parse `/export [markdown|html] [--renderer=<id>] [--save <path>]` from the raw command input. */
|
|
153
154
|
export declare function parseExportInput(rawInput: unknown): ExportInput;
|
|
155
|
+
/** Approval outcome vocabulary, structural (mirrors the approval seam without importing it). */
|
|
156
|
+
type ApprovalOutcome = 'allowed-once' | 'rejected' | 'cancelled' | 'unavailable';
|
|
157
|
+
/** Structural slice of the DSH filesystem service the save path uses. */
|
|
158
|
+
export interface ExportFileSystem {
|
|
159
|
+
resolve(path: string, opts?: {
|
|
160
|
+
cwd?: string;
|
|
161
|
+
signal?: AbortSignal;
|
|
162
|
+
}): Promise<unknown>;
|
|
163
|
+
writeText(target: unknown, content: string): Promise<unknown>;
|
|
164
|
+
}
|
|
165
|
+
/** Structural slice of the DSH approval service the save path uses. */
|
|
166
|
+
export interface ExportApproval {
|
|
167
|
+
request(request: {
|
|
168
|
+
agent: unknown;
|
|
169
|
+
toolName: string;
|
|
170
|
+
reason: string;
|
|
171
|
+
signal?: AbortSignal;
|
|
172
|
+
}): Promise<ApprovalOutcome>;
|
|
173
|
+
}
|
|
174
|
+
/** Error codes of the `/export --save` path; each is a stable machine-readable label. */
|
|
175
|
+
export type ExportSaveErrorCode = 'fs-unavailable' | 'approval-unavailable' | 'approval-denied' | 'approval-cancelled';
|
|
176
|
+
/** Result of a `/export --save` attempt: the written path or a structured failure. */
|
|
177
|
+
export type ExportSaveResult = {
|
|
178
|
+
readonly kind: 'written';
|
|
179
|
+
readonly path: string;
|
|
180
|
+
} | {
|
|
181
|
+
readonly kind: 'error';
|
|
182
|
+
readonly code: ExportSaveErrorCode;
|
|
183
|
+
readonly text: string;
|
|
184
|
+
};
|
|
185
|
+
/**
|
|
186
|
+
* Write one rendered export document to a workspace path after user approval.
|
|
187
|
+
* Fail-closed: a missing approval service, a rejected/cancelled/unavailable
|
|
188
|
+
* decision, or a throwing approval channel all deny the write; a missing fs
|
|
189
|
+
* service fails loudly with a structured error. The caller already sanitized
|
|
190
|
+
* `content` before this write.
|
|
191
|
+
* @param fs - the fs service (`ctx.get('fs')`), or undefined when none is composed.
|
|
192
|
+
* @param approval - the approval service (`ctx.get('approval')`), or undefined when none is composed.
|
|
193
|
+
* @param agent - the agent whose session the export belongs to (routes the approval).
|
|
194
|
+
* @param path - the workspace path to write.
|
|
195
|
+
* @param content - the sanitized document text.
|
|
196
|
+
* @param signal - the command's abort signal, forwarded to approval and resolve.
|
|
197
|
+
* @returns the written path, or a structured failure.
|
|
198
|
+
*/
|
|
199
|
+
export declare function saveExportFile(fs: ExportFileSystem | undefined, approval: ExportApproval | undefined, agent: unknown, path: string, content: string, signal?: AbortSignal): Promise<ExportSaveResult>;
|
|
154
200
|
export {};
|
|
155
201
|
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AAKlE,OAAO,KAAK,EAAE,MAAM,EAA2B,MAAM,iCAAiC,CAAA;AAGtF,OAAO,EAAiB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAA;AAExD,OAAO,EAAmC,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAEtF,OAAO,EAAE,mBAAmB,
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/runtime.ts"],"names":[],"mappings":"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"}
|
package/lib/types/types.d.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import type { SessionId } from '@deepseek-ai/dsh-session';
|
|
11
11
|
import { z as zod } from 'zod';
|
|
12
12
|
import type { OutputRenderer, RenderContext, RenderedText } from './renderers.js';
|
|
13
|
+
import type { StyleFoldState } from './style-command.js';
|
|
13
14
|
/** The reserved switch target that removes a session's selection. */
|
|
14
15
|
export declare const OFF = "off";
|
|
15
16
|
/**
|
|
@@ -71,10 +72,30 @@ export declare const styleSelectionViewSchema: zod.ZodObject<{
|
|
|
71
72
|
}, zod.core.$strip>>;
|
|
72
73
|
currentValue: zod.ZodNullable<zod.ZodString>;
|
|
73
74
|
}, zod.core.$strip>;
|
|
75
|
+
/**
|
|
76
|
+
* Validates the `style` projection's persisted fold state before it seeds a
|
|
77
|
+
* fold (the `stateSchema` of the registered unit). The state is plain JSON:
|
|
78
|
+
* the settled selection (`current`, null when off) plus the in-flight switch
|
|
79
|
+
* parked by `command/run` and resolved by its paired `command/done`.
|
|
80
|
+
*/
|
|
81
|
+
export declare const styleFoldStateSchema: zod.ZodObject<{
|
|
82
|
+
current: zod.ZodNullable<zod.ZodString>;
|
|
83
|
+
pending: zod.ZodNullable<zod.ZodObject<{
|
|
84
|
+
commandId: zod.ZodString;
|
|
85
|
+
target: zod.ZodUnion<readonly [zod.ZodObject<{
|
|
86
|
+
name: zod.ZodString;
|
|
87
|
+
}, zod.core.$strip>, zod.ZodObject<{
|
|
88
|
+
off: zod.ZodLiteral<true>;
|
|
89
|
+
}, zod.core.$strip>]>;
|
|
90
|
+
}, zod.core.$strip>>;
|
|
91
|
+
}, zod.core.$strip>;
|
|
74
92
|
declare module '@deepseek-ai/dsh-session-projection/types' {
|
|
75
93
|
interface SessionProjectionMap {
|
|
76
94
|
style: StyleSelectionView;
|
|
77
95
|
}
|
|
96
|
+
interface SessionProjectionStateMap {
|
|
97
|
+
style: StyleFoldState;
|
|
98
|
+
}
|
|
78
99
|
}
|
|
79
100
|
/** The `ctx.outputRenderers` service: the output.render.* renderer registry. */
|
|
80
101
|
export interface OutputRenderersService {
|
package/lib/types/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AACzD,OAAO,EAAE,CAAC,IAAI,GAAG,EAAE,MAAM,KAAK,CAAA;AAE9B,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AACzD,OAAO,EAAE,CAAC,IAAI,GAAG,EAAE,MAAM,KAAK,CAAA;AAE9B,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAExD,qEAAqE;AACrE,eAAO,MAAM,GAAG,QAAQ,CAAA;AAExB;;;;GAIG;AACH,eAAO,MAAM,YAAY;;;CAA2D,CAAA;AAEpF,gDAAgD;AAChD,eAAO,MAAM,oBAAoB;;;;;;mBAQ/B,CAAA;AAEF,kDAAkD;AAClD,MAAM,WAAW,cAAe,SAAQ,GAAG,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC;CAAG;AAEjF;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;;;;;CAM9B,CAAA;AAEF,8DAA8D;AAC9D,MAAM,WAAW,WAAW;IAC1B,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAA;IACb,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAA;IACZ,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAA;IACnB,oFAAoF;IACpF,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC/B;AAED,0DAA0D;AAC1D,MAAM,WAAW,kBAAkB;IACjC,gDAAgD;IAChD,OAAO,EAAE,WAAW,EAAE,CAAA;IACtB,4DAA4D;IAC5D,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B;AAED,iFAAiF;AACjF,eAAO,MAAM,wBAAwB;;;;;;;;mBAQnC,CAAA;AAEF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;mBAS/B,CAAA;AAEF,OAAO,QAAQ,2CAA2C,CAAC;IACzD,UAAU,oBAAoB;QAC5B,KAAK,EAAE,kBAAkB,CAAA;KAC1B;IACD,UAAU,yBAAyB;QACjC,KAAK,EAAE,cAAc,CAAA;KACtB;CACF;AAED,gFAAgF;AAChF,MAAM,WAAW,sBAAsB;IACrC,iFAAiF;IACjF,QAAQ,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,IAAI,CAAA;IAC9C,wFAAwF;IACxF,IAAI,IAAI,cAAc,EAAE,CAAA;IACxB,4DAA4D;IAC5D,OAAO,CAAC,OAAO,EAAE,aAAa,GAAG,cAAc,EAAE,CAAA;IACjD,gGAAgG;IAChG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;CACxE;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf,2EAA2E;QAC3E,eAAe,EAAE,sBAAsB,CAAA;KACxC;IACD,UAAU,MAAM;QACd;;;;WAIG;QACH,sBAAsB,CAAC,OAAO,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,aAAa,CAAA;SAAE,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,aAAa,CAAA;SAAE,KAAK,OAAO,CAAC,YAAY,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;KACrL;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-output-styles",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Claude Code outputStyles-equivalent runtime output-style switching for DeepSeek Harness",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"publishConfig": {
|
|
18
18
|
"access": "public"
|
|
19
19
|
},
|
|
20
|
+
"packageManager": "pnpm@11.7.0",
|
|
20
21
|
"engines": {
|
|
21
22
|
"node": "^22.19.0 || >=24.0.0"
|
|
22
23
|
},
|
|
@@ -99,6 +100,7 @@
|
|
|
99
100
|
},
|
|
100
101
|
"permissions": [
|
|
101
102
|
"fs:read",
|
|
103
|
+
"fs:write",
|
|
102
104
|
"fs:watch",
|
|
103
105
|
"storage:read",
|
|
104
106
|
"storage:write",
|
|
@@ -106,7 +108,7 @@
|
|
|
106
108
|
],
|
|
107
109
|
"compatibility": {
|
|
108
110
|
"dshVersions": [
|
|
109
|
-
"0.1.
|
|
111
|
+
"0.1.1-rc.2"
|
|
110
112
|
]
|
|
111
113
|
},
|
|
112
114
|
"capability": {
|
|
@@ -123,21 +125,29 @@
|
|
|
123
125
|
}
|
|
124
126
|
},
|
|
125
127
|
"dependencies": {
|
|
126
|
-
"@deepseek-ai/dsh-storage": "0.1.
|
|
127
|
-
"@deepseek-ai/dsh-storage-domain": "0.1.
|
|
128
|
-
"@deepseek-ai/dsh-storage-json": "0.1.
|
|
128
|
+
"@deepseek-ai/dsh-storage": "0.1.1-rc.2",
|
|
129
|
+
"@deepseek-ai/dsh-storage-domain": "0.1.1-rc.2",
|
|
130
|
+
"@deepseek-ai/dsh-storage-json": "0.1.1-rc.2",
|
|
129
131
|
"yaml": "^2.8.0",
|
|
130
132
|
"zod": "^4.4.3"
|
|
131
133
|
},
|
|
132
134
|
"peerDependencies": {
|
|
133
135
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
136
|
+
"@deepseek-ai/dsh-fs": ">=0.1.0-rc.8 <0.2.0",
|
|
134
137
|
"@deepseek-ai/dsh-session": ">=0.1.0-rc.8 <0.2.0",
|
|
135
|
-
"@deepseek-ai/dsh-session-projection": ">=0.1.
|
|
138
|
+
"@deepseek-ai/dsh-session-projection": ">=0.1.1-rc.2 <0.2.0",
|
|
136
139
|
"@deepseek-ai/dsh-settings": ">=0.1.0-rc.8 <0.2.0",
|
|
137
140
|
"@deepseek-ai/dsh-storage-domain": ">=0.1.0-rc.8 <0.2.0",
|
|
141
|
+
"@deepseek-ai/dsh-user-approval": ">=0.1.0-rc.8 <0.2.0",
|
|
138
142
|
"@deepseek-ai/schemastery": "^3.18.1"
|
|
139
143
|
},
|
|
140
144
|
"peerDependenciesMeta": {
|
|
145
|
+
"@deepseek-ai/dsh-fs": {
|
|
146
|
+
"optional": true
|
|
147
|
+
},
|
|
148
|
+
"@deepseek-ai/dsh-user-approval": {
|
|
149
|
+
"optional": true
|
|
150
|
+
},
|
|
141
151
|
"@deepseek-ai/dsh-settings": {
|
|
142
152
|
"optional": true
|
|
143
153
|
},
|
|
@@ -161,29 +171,29 @@
|
|
|
161
171
|
"@deepseek-ai/cordis": "4.0.1",
|
|
162
172
|
"@deepseek-ai/cordis-plugin-include": "1.0.6",
|
|
163
173
|
"@deepseek-ai/cordis-plugin-loader": "1.0.2",
|
|
164
|
-
"@deepseek-ai/dsh-agent": "0.1.
|
|
165
|
-
"@deepseek-ai/dsh-api-remotes": "0.1.
|
|
166
|
-
"@deepseek-ai/dsh-attachment": "0.1.
|
|
167
|
-
"@deepseek-ai/dsh-brand": "0.1.
|
|
168
|
-
"@deepseek-ai/dsh-client-locale": "0.1.
|
|
169
|
-
"@deepseek-ai/dsh-client-runtime": "0.1.
|
|
170
|
-
"@deepseek-ai/dsh-client-ui-commands": "0.1.
|
|
171
|
-
"@deepseek-ai/dsh-client-ui-input-trigger": "0.1.
|
|
172
|
-
"@deepseek-ai/dsh-client-ui-slots": "0.1.
|
|
173
|
-
"@deepseek-ai/dsh-commands": "0.1.
|
|
174
|
-
"@deepseek-ai/dsh-llm": "0.1.
|
|
175
|
-
"@deepseek-ai/dsh-scope": "0.1.
|
|
176
|
-
"@deepseek-ai/dsh-session": "0.1.
|
|
177
|
-
"@deepseek-ai/dsh-session-persistence": "0.1.
|
|
178
|
-
"@deepseek-ai/dsh-session-persistence-jsonl": "0.1.
|
|
179
|
-
"@deepseek-ai/dsh-session-projection": "0.1.
|
|
180
|
-
"@deepseek-ai/dsh-settings": "0.1.
|
|
181
|
-
"@deepseek-ai/dsh-storage": "0.1.
|
|
182
|
-
"@deepseek-ai/dsh-storage-domain": "0.1.
|
|
183
|
-
"@deepseek-ai/dsh-storage-json": "0.1.
|
|
184
|
-
"@deepseek-ai/dsh-system-prompt": "0.1.
|
|
185
|
-
"@deepseek-ai/dsh-timeout": "0.1.
|
|
186
|
-
"@deepseek-ai/dsh-typert-protocol": "0.1.
|
|
174
|
+
"@deepseek-ai/dsh-agent": "0.1.1-rc.2",
|
|
175
|
+
"@deepseek-ai/dsh-api-remotes": "0.1.1-rc.2",
|
|
176
|
+
"@deepseek-ai/dsh-attachment": "0.1.1-rc.2",
|
|
177
|
+
"@deepseek-ai/dsh-brand": "0.1.1-rc.2",
|
|
178
|
+
"@deepseek-ai/dsh-client-locale": "0.1.1-rc.2",
|
|
179
|
+
"@deepseek-ai/dsh-client-runtime": "0.1.1-rc.2",
|
|
180
|
+
"@deepseek-ai/dsh-client-ui-commands": "0.1.1-rc.2",
|
|
181
|
+
"@deepseek-ai/dsh-client-ui-input-trigger": "0.1.1-rc.2",
|
|
182
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.1.1-rc.2",
|
|
183
|
+
"@deepseek-ai/dsh-commands": "0.1.1-rc.2",
|
|
184
|
+
"@deepseek-ai/dsh-llm": "0.1.1-rc.2",
|
|
185
|
+
"@deepseek-ai/dsh-scope": "0.1.1-rc.2",
|
|
186
|
+
"@deepseek-ai/dsh-session": "0.1.1-rc.2",
|
|
187
|
+
"@deepseek-ai/dsh-session-persistence": "0.1.1-rc.2",
|
|
188
|
+
"@deepseek-ai/dsh-session-persistence-jsonl": "0.1.1-rc.2",
|
|
189
|
+
"@deepseek-ai/dsh-session-projection": "0.1.1-rc.2",
|
|
190
|
+
"@deepseek-ai/dsh-settings": "0.1.1-rc.2",
|
|
191
|
+
"@deepseek-ai/dsh-storage": "0.1.1-rc.2",
|
|
192
|
+
"@deepseek-ai/dsh-storage-domain": "0.1.1-rc.2",
|
|
193
|
+
"@deepseek-ai/dsh-storage-json": "0.1.1-rc.2",
|
|
194
|
+
"@deepseek-ai/dsh-system-prompt": "0.1.1-rc.2",
|
|
195
|
+
"@deepseek-ai/dsh-timeout": "0.1.1-rc.2",
|
|
196
|
+
"@deepseek-ai/dsh-typert-protocol": "0.1.1-rc.2",
|
|
187
197
|
"@deepseek-ai/schemastery": "3.18.1",
|
|
188
198
|
"@types/node": "^22.20.0",
|
|
189
199
|
"@vitest/coverage-v8": "4.1.10",
|
package/src/index.ts
CHANGED
package/src/runtime.ts
CHANGED
|
@@ -23,9 +23,9 @@ import { resolveConfig, type Config } from './config.ts'
|
|
|
23
23
|
import { installInvariant, PACKAGE_NAME, type InvariantFacts, type InvariantRegistry } from './invariant.ts'
|
|
24
24
|
import { loadStyleLibrary, truncateStyle, type OutputStyle } from './style-library.ts'
|
|
25
25
|
import { applyStyleEvent, EMPTY_STYLE_STATE, parseStyleInput, STYLE_COMMAND, type StyleFoldState } from './style-command.ts'
|
|
26
|
-
import { OUTPUT_STYLE_DOMAIN, STYLE_SOURCE, styleSelectionViewSchema, type StyleSelection, type StyleSelectionView } from './types.ts'
|
|
26
|
+
import { OUTPUT_STYLE_DOMAIN, STYLE_SOURCE, styleFoldStateSchema, styleSelectionViewSchema, type StyleSelection, type StyleSelectionView } from './types.ts'
|
|
27
27
|
import { BUILTIN_RENDERERS, RendererRegistry, type OutputRenderer, type RenderContext, type RenderedText, type StyleRule } from './renderers.ts'
|
|
28
|
-
import { conversationLines, renderExport } from './export.ts'
|
|
28
|
+
import { conversationLines, renderExport, sanitizeText } from './export.ts'
|
|
29
29
|
|
|
30
30
|
/** Bundled style-library directory (package `styles/`), the lowest-priority `stylesDir` entry. */
|
|
31
31
|
export const DEFAULT_STYLES_DIR = fileURLToPath(new URL('../styles/', import.meta.url))
|
|
@@ -418,10 +418,13 @@ export async function apply(ctx: Context, config: Config): Promise<void> {
|
|
|
418
418
|
ctx.inject(['sessionProjections'], (projectionCtx) => {
|
|
419
419
|
projectionCtx.sessionProjections.register<'style', StyleFoldState>({
|
|
420
420
|
key: 'style',
|
|
421
|
-
|
|
421
|
+
stateSchema: styleFoldStateSchema,
|
|
422
422
|
init: () => EMPTY_STYLE_STATE,
|
|
423
423
|
apply: applyStyleEvent,
|
|
424
|
-
|
|
424
|
+
wire: {
|
|
425
|
+
viewSchema: styleSelectionViewSchema,
|
|
426
|
+
view: state => viewStyleSelection(runtime, state),
|
|
427
|
+
},
|
|
425
428
|
stateVersion: 2,
|
|
426
429
|
})
|
|
427
430
|
})
|
|
@@ -504,23 +507,41 @@ export async function apply(ctx: Context, config: Config): Promise<void> {
|
|
|
504
507
|
// Markdown or sanitized HTML through the renderer pipeline. The document
|
|
505
508
|
// itself is the visible artifact; the original lines are the session log
|
|
506
509
|
// the export was projected from — rendered and original stay reconstructable.
|
|
510
|
+
// `--save <path>` additionally writes the sanitized document to that
|
|
511
|
+
// workspace path, gated by the approval service and the fs service (both
|
|
512
|
+
// optional; a missing approval service denies the write, a missing fs
|
|
513
|
+
// service fails loudly).
|
|
507
514
|
if (resolved.enableExport) {
|
|
508
515
|
ctx.inject(['commands'], (commandCtx) => {
|
|
509
516
|
commandCtx.commands.register({
|
|
510
517
|
name: 'export',
|
|
511
518
|
description: 'Export this session as Markdown or HTML (renderer-aware)',
|
|
512
|
-
input: { hint: '[markdown|html] [--renderer=<id>]' },
|
|
513
|
-
handler: async ({ agent, rawInput }) => {
|
|
519
|
+
input: { hint: '[markdown|html] [--renderer=<id>] [--save <path>]' },
|
|
520
|
+
handler: async ({ agent, rawInput, signal }) => {
|
|
514
521
|
const input = parseExportInput(rawInput)
|
|
515
522
|
if (input.kind === 'error') {
|
|
516
|
-
return { kind: 'error', text: 'usage: /export [markdown|html] [--renderer=<id>]' }
|
|
523
|
+
return { kind: 'error', text: 'usage: /export [markdown|html] [--renderer=<id>] [--save <path>]' }
|
|
517
524
|
}
|
|
518
525
|
const lines = conversationLines(agent.session.events)
|
|
519
526
|
const rules: StyleRule[] = input.renderer === undefined
|
|
520
527
|
? [...effectiveRules]
|
|
521
528
|
: [{ match: {}, style: input.renderer, priority: 0 }]
|
|
522
529
|
const document = renderExport(renderers, lines, input.format, rules)
|
|
523
|
-
|
|
530
|
+
if (input.save === undefined) {
|
|
531
|
+
return { kind: 'success', text: document.text }
|
|
532
|
+
}
|
|
533
|
+
// The disk path sanitizes the rendered document before writing and
|
|
534
|
+
// writes only after the approval service grants it.
|
|
535
|
+
const saved = await saveExportFile(
|
|
536
|
+
ctx.get('fs') as ExportFileSystem | undefined,
|
|
537
|
+
ctx.get('approval') as ExportApproval | undefined,
|
|
538
|
+
agent,
|
|
539
|
+
input.save,
|
|
540
|
+
sanitizeText(document.text),
|
|
541
|
+
signal,
|
|
542
|
+
)
|
|
543
|
+
if (saved.kind === 'error') return { kind: 'error', text: saved.text }
|
|
544
|
+
return { kind: 'success', text: `saved ${input.format} export to ${saved.path}` }
|
|
524
545
|
},
|
|
525
546
|
})
|
|
526
547
|
})
|
|
@@ -528,25 +549,151 @@ export async function apply(ctx: Context, config: Config): Promise<void> {
|
|
|
528
549
|
}
|
|
529
550
|
|
|
530
551
|
/** Parsed `/export` invocation. */
|
|
531
|
-
type ExportInput = {
|
|
532
|
-
|
|
533
|
-
|
|
552
|
+
type ExportInput = {
|
|
553
|
+
kind: 'ok'
|
|
554
|
+
format: 'markdown' | 'html'
|
|
555
|
+
renderer?: string
|
|
556
|
+
save?: string
|
|
557
|
+
} | { kind: 'error' }
|
|
558
|
+
|
|
559
|
+
/** Parse `/export [markdown|html] [--renderer=<id>] [--save <path>]` from the raw command input. */
|
|
534
560
|
export function parseExportInput(rawInput: unknown): ExportInput {
|
|
535
561
|
const raw = String(rawInput ?? '').trim()
|
|
536
562
|
const parts = raw === '' ? [] : raw.split(/\s+/)
|
|
537
563
|
let format: 'markdown' | 'html' = 'markdown'
|
|
538
564
|
let renderer: string | undefined
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
565
|
+
let save: string | undefined
|
|
566
|
+
for (let index = 0; index < parts.length; index += 1) {
|
|
567
|
+
const part = parts[index]
|
|
568
|
+
if (part === undefined) continue
|
|
569
|
+
if (part === 'markdown' || part === 'md') {
|
|
570
|
+
format = 'markdown'
|
|
571
|
+
continue
|
|
572
|
+
}
|
|
573
|
+
if (part === 'html') {
|
|
574
|
+
format = 'html'
|
|
575
|
+
continue
|
|
576
|
+
}
|
|
577
|
+
const rendererMatch = /^--renderer=([a-z0-9][a-z0-9-]*)$/.exec(part)
|
|
578
|
+
if (rendererMatch !== null) {
|
|
579
|
+
renderer = rendererMatch[1]
|
|
580
|
+
continue
|
|
581
|
+
}
|
|
582
|
+
const saveInline = /^--save=(.+)$/.exec(part)
|
|
583
|
+
if (saveInline !== null) {
|
|
584
|
+
save = saveInline[1]
|
|
542
585
|
continue
|
|
543
586
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
587
|
+
if (part === '--save') {
|
|
588
|
+
const next = parts[index + 1]
|
|
589
|
+
if (next === undefined || next === '') return { kind: 'error' }
|
|
590
|
+
save = next
|
|
591
|
+
index += 1
|
|
547
592
|
continue
|
|
548
593
|
}
|
|
549
594
|
return { kind: 'error' }
|
|
550
595
|
}
|
|
551
|
-
return {
|
|
596
|
+
return {
|
|
597
|
+
kind: 'ok',
|
|
598
|
+
format,
|
|
599
|
+
...renderer === undefined ? {} : { renderer },
|
|
600
|
+
...save === undefined ? {} : { save },
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/** Approval outcome vocabulary, structural (mirrors the approval seam without importing it). */
|
|
605
|
+
type ApprovalOutcome = 'allowed-once' | 'rejected' | 'cancelled' | 'unavailable'
|
|
606
|
+
|
|
607
|
+
/** Structural slice of the DSH filesystem service the save path uses. */
|
|
608
|
+
export interface ExportFileSystem {
|
|
609
|
+
resolve(path: string, opts?: { cwd?: string; signal?: AbortSignal }): Promise<unknown>
|
|
610
|
+
writeText(target: unknown, content: string): Promise<unknown>
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/** Structural slice of the DSH approval service the save path uses. */
|
|
614
|
+
export interface ExportApproval {
|
|
615
|
+
request(request: { agent: unknown; toolName: string; reason: string; signal?: AbortSignal }): Promise<ApprovalOutcome>
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/** Error codes of the `/export --save` path; each is a stable machine-readable label. */
|
|
619
|
+
export type ExportSaveErrorCode = 'fs-unavailable' | 'approval-unavailable' | 'approval-denied' | 'approval-cancelled'
|
|
620
|
+
|
|
621
|
+
/** Result of a `/export --save` attempt: the written path or a structured failure. */
|
|
622
|
+
export type ExportSaveResult =
|
|
623
|
+
| { readonly kind: 'written'; readonly path: string }
|
|
624
|
+
| { readonly kind: 'error'; readonly code: ExportSaveErrorCode; readonly text: string }
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* Write one rendered export document to a workspace path after user approval.
|
|
628
|
+
* Fail-closed: a missing approval service, a rejected/cancelled/unavailable
|
|
629
|
+
* decision, or a throwing approval channel all deny the write; a missing fs
|
|
630
|
+
* service fails loudly with a structured error. The caller already sanitized
|
|
631
|
+
* `content` before this write.
|
|
632
|
+
* @param fs - the fs service (`ctx.get('fs')`), or undefined when none is composed.
|
|
633
|
+
* @param approval - the approval service (`ctx.get('approval')`), or undefined when none is composed.
|
|
634
|
+
* @param agent - the agent whose session the export belongs to (routes the approval).
|
|
635
|
+
* @param path - the workspace path to write.
|
|
636
|
+
* @param content - the sanitized document text.
|
|
637
|
+
* @param signal - the command's abort signal, forwarded to approval and resolve.
|
|
638
|
+
* @returns the written path, or a structured failure.
|
|
639
|
+
*/
|
|
640
|
+
export async function saveExportFile(
|
|
641
|
+
fs: ExportFileSystem | undefined,
|
|
642
|
+
approval: ExportApproval | undefined,
|
|
643
|
+
agent: unknown,
|
|
644
|
+
path: string,
|
|
645
|
+
content: string,
|
|
646
|
+
signal?: AbortSignal,
|
|
647
|
+
): Promise<ExportSaveResult> {
|
|
648
|
+
if (approval === undefined) {
|
|
649
|
+
return {
|
|
650
|
+
kind: 'error',
|
|
651
|
+
code: 'approval-unavailable',
|
|
652
|
+
text: 'dsh-output-styles: /export --save requires an approval service (compose @deepseek-ai/dsh-user-approval); nothing was written',
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
let outcome: ApprovalOutcome
|
|
656
|
+
try {
|
|
657
|
+
outcome = await approval.request({
|
|
658
|
+
agent,
|
|
659
|
+
toolName: 'export',
|
|
660
|
+
reason: `write the exported document to ${path}`,
|
|
661
|
+
...signal === undefined ? {} : { signal },
|
|
662
|
+
})
|
|
663
|
+
} catch {
|
|
664
|
+
// An approval channel that cannot answer is an unanswerable ask: fail closed.
|
|
665
|
+
outcome = 'unavailable'
|
|
666
|
+
}
|
|
667
|
+
switch (outcome) {
|
|
668
|
+
case 'allowed-once': break
|
|
669
|
+
case 'rejected':
|
|
670
|
+
return {
|
|
671
|
+
kind: 'error',
|
|
672
|
+
code: 'approval-denied',
|
|
673
|
+
text: 'dsh-output-styles: /export --save was rejected; nothing was written',
|
|
674
|
+
}
|
|
675
|
+
case 'cancelled':
|
|
676
|
+
return {
|
|
677
|
+
kind: 'error',
|
|
678
|
+
code: 'approval-cancelled',
|
|
679
|
+
text: 'dsh-output-styles: /export --save was cancelled; nothing was written',
|
|
680
|
+
}
|
|
681
|
+
default:
|
|
682
|
+
// 'unavailable' plus any out-of-vocabulary answer fail closed (never write).
|
|
683
|
+
return {
|
|
684
|
+
kind: 'error',
|
|
685
|
+
code: 'approval-unavailable',
|
|
686
|
+
text: 'dsh-output-styles: /export --save approval is unavailable; nothing was written',
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
if (fs === undefined) {
|
|
690
|
+
return {
|
|
691
|
+
kind: 'error',
|
|
692
|
+
code: 'fs-unavailable',
|
|
693
|
+
text: 'dsh-output-styles: /export --save requires an fs service (compose @deepseek-ai/dsh-fs); nothing was written',
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
const target = await fs.resolve(path, signal === undefined ? undefined : { signal })
|
|
697
|
+
await fs.writeText(target, content)
|
|
698
|
+
return { kind: 'written', path }
|
|
552
699
|
}
|
package/src/types.ts
CHANGED
|
@@ -12,6 +12,7 @@ import type { SessionId } from '@deepseek-ai/dsh-session'
|
|
|
12
12
|
import { z as zod } from 'zod'
|
|
13
13
|
import { defineDomain, domainTable } from '@deepseek-ai/dsh-storage-domain'
|
|
14
14
|
import type { OutputRenderer, RenderContext, RenderedText } from './renderers.ts'
|
|
15
|
+
import type { StyleFoldState } from './style-command.ts'
|
|
15
16
|
|
|
16
17
|
/** The reserved switch target that removes a session's selection. */
|
|
17
18
|
export const OFF = 'off'
|
|
@@ -80,10 +81,30 @@ export const styleSelectionViewSchema = zod.object({
|
|
|
80
81
|
currentValue: zod.string().min(1).nullable(),
|
|
81
82
|
})
|
|
82
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Validates the `style` projection's persisted fold state before it seeds a
|
|
86
|
+
* fold (the `stateSchema` of the registered unit). The state is plain JSON:
|
|
87
|
+
* the settled selection (`current`, null when off) plus the in-flight switch
|
|
88
|
+
* parked by `command/run` and resolved by its paired `command/done`.
|
|
89
|
+
*/
|
|
90
|
+
export const styleFoldStateSchema = zod.object({
|
|
91
|
+
current: zod.string().min(1).nullable(),
|
|
92
|
+
pending: zod.object({
|
|
93
|
+
commandId: zod.string().min(1),
|
|
94
|
+
target: zod.union([
|
|
95
|
+
zod.object({ name: zod.string().min(1) }),
|
|
96
|
+
zod.object({ off: zod.literal(true) }),
|
|
97
|
+
]),
|
|
98
|
+
}).nullable(),
|
|
99
|
+
})
|
|
100
|
+
|
|
83
101
|
declare module '@deepseek-ai/dsh-session-projection/types' {
|
|
84
102
|
interface SessionProjectionMap {
|
|
85
103
|
style: StyleSelectionView
|
|
86
104
|
}
|
|
105
|
+
interface SessionProjectionStateMap {
|
|
106
|
+
style: StyleFoldState
|
|
107
|
+
}
|
|
87
108
|
}
|
|
88
109
|
|
|
89
110
|
/** The `ctx.outputRenderers` service: the output.render.* renderer registry. */
|