dsh-plugin-deck-studio 0.0.0-stage → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +47 -2
  3. package/client.js +500 -0
  4. package/cordis.example.yml +163 -0
  5. package/cordis.patch.yml +147 -0
  6. package/index.js +2340 -0
  7. package/lib/config.js +216 -0
  8. package/lib/content-density.js +98 -0
  9. package/lib/deck-brief.js +87 -0
  10. package/lib/deck-docs.js +1095 -0
  11. package/lib/deck-rules.js +124 -0
  12. package/lib/deck-templates.js +149 -0
  13. package/lib/deck-workspace.js +480 -0
  14. package/lib/design-style.js +157 -0
  15. package/lib/designs.js +175 -0
  16. package/lib/diff.js +88 -0
  17. package/lib/exporter.js +336 -0
  18. package/lib/faq.js +300 -0
  19. package/lib/git-commit.js +87 -0
  20. package/lib/home-catalog.js +249 -0
  21. package/lib/png.js +275 -0
  22. package/lib/proposal-decision.js +204 -0
  23. package/lib/render-nodes.js +171 -0
  24. package/lib/render-settings.js +213 -0
  25. package/lib/render.js +287 -0
  26. package/lib/review-personas.js +258 -0
  27. package/lib/script-style.js +55 -0
  28. package/lib/script-writer.js +165 -0
  29. package/lib/source-adapter.js +327 -0
  30. package/lib/store.js +212 -0
  31. package/lib/tools.js +2594 -0
  32. package/lib/watermark.js +137 -0
  33. package/package.json +67 -4
  34. package/seeds/docs/assets/screenshots/00-hero.png +0 -0
  35. package/seeds/docs/assets/screenshots/01-home.png +0 -0
  36. package/seeds/docs/assets/screenshots/02-deck-pages.png +0 -0
  37. package/seeds/docs/assets/screenshots/03-script.png +0 -0
  38. package/seeds/docs/assets/screenshots/04-design.png +0 -0
  39. package/seeds/docs/assets/screenshots/05-governance.png +0 -0
  40. package/seeds/docs/assets/screenshots/06-settings.png +0 -0
  41. package/seeds/docs/assets/screenshots/07-new-deck.png +0 -0
  42. package/seeds/docs/assets/screenshots/manual/01-home.png +0 -0
  43. package/seeds/docs/assets/screenshots/manual/02-deck-pages.png +0 -0
  44. package/seeds/docs/assets/screenshots/manual/03-script.png +0 -0
  45. package/seeds/docs/assets/screenshots/manual/04-design.png +0 -0
  46. package/seeds/docs/assets/screenshots/manual/05-governance.png +0 -0
  47. package/seeds/docs/assets/screenshots/manual/06-settings.png +0 -0
  48. package/seeds/docs/assets/screenshots/manual/07-new-deck.png +0 -0
  49. package/seeds/docs/assets/screenshots/manual/legend.json +12 -0
  50. package/seeds/docs/compat.md +134 -0
  51. package/seeds/docs/install.en.md +252 -0
  52. package/seeds/docs/install.md +304 -0
  53. package/seeds/docs/manual.en.md +294 -0
  54. package/seeds/docs/manual.md +258 -0
  55. package/seeds/docs/productization.en.md +212 -0
  56. package/seeds/docs/productization.md +193 -0
  57. package/seeds/docs/sample-deck.en.md +114 -0
  58. package/seeds/docs/sample-deck.md +104 -0
  59. package/seeds/docs/styles.en.md +112 -0
  60. package/seeds/docs/styles.md +100 -0
  61. package/seeds/resources/deckline-logo.png +0 -0
  62. package/seeds/resources/deckline-logo.svg +12 -0
  63. package/seeds/resources/deckline-mark.png +0 -0
  64. package/seeds/resources/index.json +19 -0
  65. package/seeds/samples/BIZ-01.jpg +0 -0
  66. package/seeds/samples/BIZ-02.jpg +0 -0
  67. package/seeds/samples/BIZ-03.jpg +0 -0
  68. package/seeds/samples/EDT-01.jpg +0 -0
  69. package/seeds/samples/EDT-02.jpg +0 -0
  70. package/seeds/samples/EDT-03.jpg +0 -0
  71. package/seeds/samples/FYI-01.jpg +0 -0
  72. package/seeds/samples/FYI-02.jpg +0 -0
  73. package/seeds/samples/FYI-03.jpg +0 -0
  74. package/seeds/samples/GRD-01.jpg +0 -0
  75. package/seeds/samples/GRD-02.jpg +0 -0
  76. package/seeds/samples/GRD-03.jpg +0 -0
  77. package/seeds/samples/JPN-01.jpg +0 -0
  78. package/seeds/samples/JPN-02.jpg +0 -0
  79. package/seeds/samples/JPN-03.jpg +0 -0
  80. package/seeds/samples/SKT-01.jpg +0 -0
  81. package/seeds/samples/SKT-02.jpg +0 -0
  82. package/seeds/samples/SKT-03.jpg +0 -0
  83. package/seeds/samples/SWS-01.jpg +0 -0
  84. package/seeds/samples/SWS-02.jpg +0 -0
  85. package/seeds/samples/SWS-03.jpg +0 -0
  86. package/seeds/samples/TEC-01.jpg +0 -0
  87. package/seeds/samples/TEC-02.jpg +0 -0
  88. package/seeds/samples/TEC-03.jpg +0 -0
  89. package/seeds/samples/deckline-intro/README.md +41 -0
  90. package/seeds/samples/deckline-intro/assets/deckline-logo.png +0 -0
  91. package/seeds/samples/deckline-intro/assets/deckline-logo.svg +12 -0
  92. package/seeds/samples/deckline-intro/assets/deckline-mark.png +0 -0
  93. package/seeds/samples/deckline-intro/deckline-intro.pptx +0 -0
  94. package/seeds/samples/deckline-intro/intent.md +48 -0
  95. package/seeds/samples/deckline-intro/materials/project-summary.md +90 -0
  96. package/seeds/samples/deckline-intro/materials/screenshots/01-workbench-deck.png +0 -0
  97. package/seeds/samples/deckline-intro/materials/screenshots/02-home-decks.png +0 -0
  98. package/seeds/samples/deckline-intro/materials/screenshots/03-home-designs.png +0 -0
  99. package/seeds/samples/deckline-intro/materials/screenshots/04-new-deck-popup.png +0 -0
  100. package/seeds/samples/deckline-intro/materials.md +110 -0
  101. package/seeds/samples/deckline-intro/pages/p01.png +0 -0
  102. package/seeds/samples/deckline-intro/pages/p02.png +0 -0
  103. package/seeds/samples/deckline-intro/pages/p03.png +0 -0
  104. package/seeds/samples/deckline-intro/pages/p04.png +0 -0
  105. package/seeds/samples/deckline-intro/pages/p05.png +0 -0
  106. package/seeds/samples/deckline-intro/pages/p06.png +0 -0
  107. package/seeds/samples/deckline-intro/pages/p07.png +0 -0
  108. package/seeds/samples/deckline-intro/pages/p08.png +0 -0
  109. package/seeds/samples/deckline-intro/pages/p09.png +0 -0
  110. package/seeds/samples/deckline-intro/pages/p10.png +0 -0
  111. package/seeds/samples/deckline-intro/popup.md +87 -0
  112. package/seeds/samples/deckline-intro/prompts/p01.txt +4 -0
  113. package/seeds/samples/deckline-intro/prompts/p02.txt +6 -0
  114. package/seeds/samples/deckline-intro/prompts/p03.txt +6 -0
  115. package/seeds/samples/deckline-intro/prompts/p04.txt +6 -0
  116. package/seeds/samples/deckline-intro/prompts/p05.txt +6 -0
  117. package/seeds/samples/deckline-intro/prompts/p06.txt +6 -0
  118. package/seeds/samples/deckline-intro/prompts/p07.txt +6 -0
  119. package/seeds/samples/deckline-intro/prompts/p08.txt +6 -0
  120. package/seeds/samples/deckline-intro/prompts/p09.txt +6 -0
  121. package/seeds/samples/deckline-intro/prompts/p10.txt +6 -0
  122. package/seeds/samples/deckline-intro/script.md +88 -0
  123. package/seeds/styles/BIZ.json +22 -0
  124. package/seeds/styles/EDT.json +22 -0
  125. package/seeds/styles/FYI.json +22 -0
  126. package/seeds/styles/GRD.json +22 -0
  127. package/seeds/styles/JPN.json +22 -0
  128. package/seeds/styles/SKT.json +22 -0
  129. package/seeds/styles/SWS.json +22 -0
  130. package/seeds/styles/TEC.json +22 -0
  131. package/ui/logo.svg +20 -0
  132. package/ui/workbench.css +687 -0
  133. package/ui/workbench.html +4554 -0
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Logo 水印(F-125):assets 分类 → 列举可选水印 → 给整份 deck 的页面盖 logo。
3
+ *
4
+ * 负责人口径(2026-10-23):
5
+ * - assets 里按分类放素材,至少有一类 `logo`,可以放多个;
6
+ * - 用户跟 Agent 说「我要加水印」,能选哪个 logo;默认盖 **右上角**;
7
+ * - 每个页面都盖;原图不覆盖——水印产物写新的 revision(vNNN),旧图保留。
8
+ *
9
+ * 实现:纯 node(zlib)自己解 / 编 PNG(见 lib/png.js),不引 sharp/canvas。
10
+ * 页面图与 logo 都必须是 PNG;SVG logo 会明确提示先转 PNG,而不是静默跳过。
11
+ *
12
+ * @module dsh-plugin-deck-studio/watermark
13
+ */
14
+
15
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
16
+ import { basename, extname, join, relative } from "node:path";
17
+ import { compositeLogo, decodePng, encodePng } from "./png.js";
18
+
19
+ /** 水印位置(默认右上角)。 */
20
+ export const WATERMARK_POSITIONS = ["top-right", "top-left", "bottom-right", "bottom-left", "center"];
21
+
22
+ export const WATERMARK_POSITION_LABELS = {
23
+ "top-right": "右上角(默认)",
24
+ "top-left": "左上角",
25
+ "bottom-right": "右下角",
26
+ "bottom-left": "左下角",
27
+ center: "居中",
28
+ };
29
+
30
+ const IMAGE_EXTS = new Set([".png", ".jpg", ".jpeg", ".svg"]);
31
+
32
+ /** 资产分类根目录:仓库 `assets/`(按子目录分类,如 logo/)。 */
33
+ export function assetsRootOf(repoRoot) {
34
+ return join(repoRoot, "assets");
35
+ }
36
+
37
+ /**
38
+ * 扫描 assets/ 下的分类与素材。
39
+ * @returns {{categories: Array<{category: string, items: Array<{id: string, name: string, category: string, format: string, bytes: number, path: string, watermarkable: boolean}>}>}}
40
+ */
41
+ export function listAssets(repoRoot) {
42
+ const root = assetsRootOf(repoRoot);
43
+ if (!existsSync(root)) return { categories: [], root };
44
+ const categories = [];
45
+ for (const entry of readdirSync(root, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
46
+ if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
47
+ const dir = join(root, entry.name);
48
+ const items = [];
49
+ for (const file of readdirSync(dir).sort()) {
50
+ const ext = extname(file).toLowerCase();
51
+ if (!IMAGE_EXTS.has(ext) || file.startsWith(".")) continue;
52
+ const path = join(dir, file);
53
+ const stat = statSync(path);
54
+ if (!stat.isFile()) continue;
55
+ items.push({
56
+ id: `${entry.name}/${file}`,
57
+ name: file,
58
+ category: entry.name,
59
+ format: ext.replace(".", ""),
60
+ bytes: stat.size,
61
+ path,
62
+ // 只有 PNG 能直接合成(zlib 自己解);SVG/JPG 明确提示先转 PNG。
63
+ watermarkable: ext === ".png",
64
+ });
65
+ }
66
+ if (items.length) categories.push({ category: entry.name, items });
67
+ }
68
+ return { categories, root };
69
+ }
70
+
71
+ /** 支持做水印的素材(PNG),供 Agent 选择。 */
72
+ export function listWatermarkAssets(repoRoot) {
73
+ const { categories, root } = listAssets(repoRoot);
74
+ const items = categories.flatMap((category) => category.items).filter((item) => item.watermarkable);
75
+ return { root, categories, items };
76
+ }
77
+
78
+ /**
79
+ * 按 Agent 给的名字找素材:id(logo/x.png)/ 文件名(x.png)/ 基名(x)都可以;
80
+ * 找不到或有歧义都返回候选,让 Agent 能自己纠正而不是瞎猜。
81
+ */
82
+ export function findAsset(repoRoot, query) {
83
+ // 找素材时把 SVG/JPG 也算进来:用户报了 SVG 名字时要能明确说「需要先转 PNG」,
84
+ // 而不是回一句「没找到」。
85
+ const { categories } = listAssets(repoRoot);
86
+ const items = categories.flatMap((category) => category.items);
87
+ const raw = String(query ?? "").trim();
88
+ if (!raw) return { ok: false, reason: "empty", candidates: items };
89
+ const needle = raw.toLowerCase().replace(/\\/g, "/");
90
+ const qualified = needle.includes("/") ? needle : null;
91
+ const matches = items.filter((item) => {
92
+ const id = item.id.toLowerCase();
93
+ const name = item.name.toLowerCase();
94
+ const stem = name.replace(/\.[^.]+$/, "");
95
+ if (qualified) return id === qualified || id.endsWith(`/${qualified}`);
96
+ return name === needle || stem === needle || id === needle;
97
+ });
98
+ if (matches.length === 1) return { ok: true, asset: matches[0] };
99
+ if (matches.length > 1) return { ok: false, reason: "ambiguous", candidates: matches };
100
+ // 宽松兜底:唯一子串命中
101
+ const loose = items.filter((item) => item.id.toLowerCase().includes(needle) || item.name.toLowerCase().includes(needle));
102
+ if (loose.length === 1) return { ok: true, asset: loose[0] };
103
+ if (loose.length > 1) return { ok: false, reason: "ambiguous", candidates: loose };
104
+ return { ok: false, reason: "not-found", candidates: items };
105
+ }
106
+
107
+ /** 把一张页面 PNG + logo PNG 合成水印后的 PNG Buffer。 */
108
+ export function watermarkPng({ pageBuffer, logoBuffer, position = "top-right", opacity = 0.85, scale = 0.14, margin = 0.04 }) {
109
+ const page = decodePng(pageBuffer);
110
+ const logo = decodePng(logoBuffer);
111
+ const { image, placement } = compositeLogo({ page, logo, position, opacity, scale, margin });
112
+ return { buffer: encodePng(image), size: `${image.width}x${image.height}`, placement };
113
+ }
114
+
115
+ /** 给 Agent 看的一行位置说明。 */
116
+ export function positionLine() {
117
+ return WATERMARK_POSITIONS.map((key) => `${key}(${WATERMARK_POSITION_LABELS[key]})`).join(" / ");
118
+ }
119
+
120
+ /** 报告里用的相对路径(不把本机绝对路径泄给用户/Agent)。 */
121
+ export function displayPath(repoRoot, path) {
122
+ try {
123
+ const rel = relative(repoRoot, path);
124
+ return rel.startsWith("..") ? basename(path) : rel;
125
+ } catch {
126
+ return basename(path);
127
+ }
128
+ }
129
+
130
+ /** 读 PNG 文件(水印流程用,统一错误文案)。 */
131
+ export function readPngFile(path) {
132
+ const buffer = readFileSync(path);
133
+ if (!buffer.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))) {
134
+ throw new Error(`不是 PNG:${basename(path)}`);
135
+ }
136
+ return buffer;
137
+ }
package/package.json CHANGED
@@ -1,6 +1,69 @@
1
1
  {
2
2
  "name": "dsh-plugin-deck-studio",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.3",
4
+ "private": false,
5
+ "description": "Deckline:DSH image deck 工作台插件 —— 原生 Chat + 整页图片 deck 工作区 + 设计风格(design)库;出图按页留痕、可点名批量连出,产物本地可追踪。",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "./index.js",
9
+ "exports": {
10
+ ".": "./index.js",
11
+ "./client": "./client.js",
12
+ "./package.json": "./package.json"
13
+ },
14
+ "keywords": [
15
+ "dsh",
16
+ "dsh-plugin",
17
+ "deepseek-harness",
18
+ "deck",
19
+ "image-deck",
20
+ "presentation",
21
+ "slides",
22
+ "deckline"
23
+ ],
24
+ "engines": {
25
+ "node": "^22.19.0 || >=24.0.0",
26
+ "dsh": ">=0.1.7-0 <0.3.0"
27
+ },
28
+ "scripts": {
29
+ "verify": "node verify/smoke.mjs",
30
+ "prepublishOnly": "node verify/smoke.mjs",
31
+ "prepack": "node scripts/prepack-assets.mjs"
32
+ },
33
+ "dsh": {
34
+ "bundle": {
35
+ "patch": "./cordis.patch.yml"
36
+ },
37
+ "client": {
38
+ "platform": "web",
39
+ "immediately": true,
40
+ "inject": [
41
+ "renderer",
42
+ "layout",
43
+ "sidebar"
44
+ ]
45
+ }
46
+ },
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "git+https://github.com/yuna78/dsh-deck-studio.git",
50
+ "directory": "plugin/deck-studio"
51
+ },
52
+ "homepage": "https://github.com/yuna78/dsh-deck-studio#readme",
53
+ "bugs": {
54
+ "url": "https://github.com/yuna78/dsh-deck-studio/issues"
55
+ },
56
+ "peerDependencies": {
57
+ "@deepseek-ai/dsh": ">=0.1.7-0 <0.2.0 || >=0.2.0-0 <0.3.0 || >=0.2.1-0 <0.3.0"
58
+ },
59
+ "author": "Yuna",
60
+ "files": [
61
+ "index.js",
62
+ "client.js",
63
+ "lib/",
64
+ "ui/",
65
+ "seeds/",
66
+ "cordis.patch.yml",
67
+ "cordis.example.yml"
68
+ ]
69
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "02-deck-pages": {
3
+ "1": "脚本 tab",
4
+ "2": "出图节点 / 模型",
5
+ "3": "内容密度",
6
+ "4": "添加页面",
7
+ "5": "全屏演示",
8
+ "6": "导出",
9
+ "7": "微调 script",
10
+ "8": "重新出图"
11
+ }
12
+ }
@@ -0,0 +1,134 @@
1
+ # DSH 兼容矩阵(定期回归记录)
2
+
3
+ > 口径:DSH 处于 prerelease 阶段,没有 stable 线;本项目**定期批次**回归,不逐 rc 跟。
4
+ > 每个批次记录:证实的版本、验证范围、断点、复现方式。已知限制与路线见 [productization.md](productization.md) 与 [install.md](install.md)。
5
+
6
+ ## 当前支持声明
7
+
8
+ | 项 | 值 | 说明 |
9
+ |---|---|---|
10
+ | `peerDependencies["@deepseek-ai/dsh"]` | `>=0.1.7-0 <0.2.0 \|\| >=0.2.0-0 <0.3.0 \|\| >=0.2.1-0 <0.3.0` | 装到 0.3+ 会被 DSH 的 peer 检查拦下 |
11
+ | `engines.dsh` | `>=0.1.7-0 <0.3.0` | 元数据口径,同 peer |
12
+ | `engines.node` | `^22.19.0 \|\| >=24.0.0` | 与 DSH 运行时要求一致;**Node 23 不在范围内** |
13
+ | 已验证版本 | `0.1.7-rc.2`(旧基线)· `0.2.1-rc.2`(2026-10-21:隔离环境 + 本地全局升级后真机回归) | 见下表 |
14
+
15
+ ## 回归记录
16
+
17
+ ### 批次 1 · 2026-10-21 · DSH `0.2.1-rc.2`
18
+
19
+ 隔离环境,不动本机 `0.1.7-rc.2` 与运行中的 deck-studio profile:
20
+
21
+ ```bash
22
+ # 临时安装(Node 必须 ^22.19 || >=24;本机默认 Node 23.11 不满足,用 nvm v24.21.0)
23
+ npm i --prefix /tmp/dsh02 @deepseek-ai/dsh@0.2.1-rc.2
24
+ export DSH_HOME=/tmp/dsh02-home PATH="$HOME/.nvm/versions/node/v24.21.0/bin:$PATH"
25
+ dsh --profile deckline-02 --from-default-profile web --dump-config
26
+ dsh plugin --profile deckline-02 add <repo>/plugin/deck-studio
27
+ dsh --profile deckline-02 --port 13290
28
+ ```
29
+
30
+ | 检查项 | 结果 | 证据 |
31
+ |---|---|---|
32
+ | `dsh -V` / CLI 启动 | ✅ `0.2.1-rc.2` | 需 Node 24(0.2.x 的 `bin.js` 用 `import.meta.main`,Node 23 会静默不执行) |
33
+ | 插件安装(本地 link) | ✅ 无 peer 冲突,进入 `dsh.profile.bundles` | `dsh plugin add` 输出 `+ dsh-plugin-deck-studio link:…` |
34
+ | 组合配置(根 mount / preset / tools) | ✅ `deck-studio`、`preset-deck-studio`、`deck-studio-tools` 均在 `--dump-config` 中 | 无组合报错 |
35
+ | Web 启动 + 插件装载 | ✅ 侧栏出现「Deckline」,`/api/deck-studio.app` iframe 加载 | 截图 `/tmp/f86-02-home.png` |
36
+ | Deckline 首页 | ✅ Decks / Designs / 资源库 / Conversations + 数据(designs 11) | `/tmp/f86-02-app-home.png` |
37
+ | Designs 列表 + 预设风格 | ✅ 11 个风格(含 8 个预设),「导入预设风格」入口在 | `/tmp/f86-02-designs.png` |
38
+ | Design 详情(BIZ / PCL) | ✅ 3/4 张样张、风格定义、样张墙 | `/tmp/f86-02-design-biz.png` |
39
+ | 样张媒体 GET | ✅ 200 / 1,383,788 bytes | `/api/deck-studio.media?…designs%2FBIZ%2Fsample-v001.png` |
40
+ | 设置面板(节点自配口径) | ✅ 显示「生图配置未就绪 / 未配置 endpoint / key 未配置」+ docs 指引 | `/tmp/f86-02-settings.png` |
41
+ | 控制台 / 页面异常 | ✅ 0 console error、0 pageerror | 同上脚本输出 |
42
+ | Agent 工具链路 | ⏭ 未验证(隔离 home 无 LLM key) | 下批用真 key 跑 `S01/S02` + `--only` |
43
+ | 真出图(real render) | ⏭ 未验证 | 需本机节点/key;fake 模式已验证 |
44
+
45
+ **观察项(不阻塞)**:iframe 内导航时 `/plugins/events`(DSH 的插件事件 SSE)报 `ERR_ABORTED`,
46
+ 页面功能与数据均正常,判断为导航时主动中断连接,记录待后续批次复核。
47
+
48
+ **结论**:`0.2.1-rc.2` 在「插件安装 → 组合 → Web 加载 → Deckline 应用/样张/设置」链路上可用;
49
+ agent 工具链路与真出图待下批补验。支持声明暂不收紧到 0.2.x-only,保持 `0.1.7–0.2.x` 区间。
50
+
51
+ ## 下批要做(每月 / 或 DSH 0.3 发布前)
52
+
53
+ 1. 真出图探活 1 页(0.2.1 上,确认 image adapter 无破坏);
54
+ 2. 合并评估脚本修复:S11「读到的原文含报错字样」误报、S13 台账表解析口径;
55
+ 3. 复核 `/plugins/events` ERR_ABORTED;
56
+ 4. 若 DSH 发布 0.3.x:先按本表跑一遍,再更新 `peerDependencies` / 本文件 / README 支持声明。
57
+
58
+ ### 批次 2 · 2026-10-21 · DSH `0.2.1-rc.2` 真机 + Agent 链路(本地全局升级后)
59
+
60
+ 批次 1 的隔离环境没有 LLM key,Agent 链路未验证;本机升级到 `0.2.1-rc.2` 后用真 key 补验,同时发现一个
61
+ 0.2.x 的破坏性差异并已修复。
62
+
63
+ | 检查项 | 结果 | 证据 |
64
+ |---|---|---|
65
+ | 真机数据迁移 | ✅ decks 5→7 / designs 11 / conversations 20 均保留 | 升级回归(见下节「本地环境升级记录」) |
66
+ | persona 提示词变量 | ❌→✅ 0.2.x 只注册 `provider` / `model` 两个变量;插件 persona suffix 里的 `{{cwd}}` 使每个 Agent 回合直接 `turn/end` 报 `unknown prompt variable "{{cwd}}"`,Chat 完全不回话 | 会话 `session.v4.jsonl.zstd` 的 `turn/end` error;修复 commit `1dd8695`(改成静态「工作目录是本 deck 仓库根目录,以工具返回路径为准」)+ smoke 增加禁 `{{cwd}}` 断言 |
67
+ | Agent 全场景评估 | ✅ 16 场景中 15 通过(S01–S16) | `.deck/eval/run-20261021-000149/report.md` |
68
+ | S11 假失败 | ✅ 复跑 PASS(eval 误报:`deck_file_read` 读到 `smoke.mjs` 原文里含「未知 deck」字样被当成工具报错) | 临时补丁复跑 `.deck/eval/run-20261021-001453/report.md`;正式修复待评估脚本合并 |
69
+ | S13 新增场景 | ⏭ 非 0.2.1 回归:检查器按标准台账表解析,agent 用自定义台账表 → 解析 0 条 | 同 report.md;属 WIP 场景口径问题 |
70
+ | 工具链路 | ✅ 真实调用 overview / 读文件 / 读文档 / 文档更新 / script 提案 / note_change / style_update | eval 每回合工具序列 |
71
+ | real 出图探活(adapter) | ✅ 2026-10-21 在 DSH 0.2.1 的 Node 24 上跑 `createRealRenderer`:POST 200 / 18.5s,1024×1024 PNG 1.3MB,图片内容与 prompt 一致 | `/tmp/f94-probe/out5/image.png`;探针脚本一次性放 /tmp,不入库 |
72
+ | CDN 下载抖动 | ⚠️→✅ 中转返回 `data[0].url` 走 Cloudflare CDN,本网络下连续 4 次 `ETIMEDOUT` 后第 5 次成功;插件已加**5 次指数退避重试**(`lib/render.js`) | probe 日志 `fetch#2–#6`;smoke 新增重试测试 |
73
+ | smoke / 公开包 | ✅ 47/47;96 文件 / 5.87 MB(含 4 份英文文档) | `smoke.mjs` / `build-public-bundle.mjs` |
74
+
75
+ **结论**:`0.2.1-rc.2` 上插件与 Agent 链路可用,但 **persona 提示词变量只能用 `provider` / `model`**;
76
+ 本仓已修复并加 smoke 防回归;real 出图 adapter 已在 0.2.1 的 Node 24 上以一页 1024×1024 探活通过。
77
+
78
+ **操作观察(不阻塞)**:13090 重启后约 1 分钟内,`/api/deck-studio.app` 可能还没把 `STATE.data.active`
79
+ 挂好(脚本直接 `goto` 会误判「工作台没加载」);等插件 patch 应用完成或先 warm-up 一次再跑自动化。
80
+ `/plugins/events` 仍是导航时 `ERR_ABORTED`,功能正常。
81
+
82
+ ## 本地环境升级记录(2026-10-21 已执行)
83
+
84
+ **升级前**:全局 `@deepseek-ai/dsh@0.1.7-rc.2`(2026-09-27 安装),本机默认 Node `v23.11.0`,
85
+ 运行中的服务靠 `~/.local/bin/dsh` 启动壳固定 Node `v24.21.0`。
86
+ **升级后**:`0.2.1-rc.2`(npm `latest`),启动壳不变(仍固定 Node 24)。
87
+
88
+ ```bash
89
+ # 备份(仓库外)
90
+ ~/dsh-upgrade-backup-20261020/{profiles.tar.gz, dsh-0.1.7-rc.2/}
91
+ # 用 Node 24 的 npm 升级全局包(保持 /opt/homebrew 前缀,启动壳路径不变)
92
+ ~/.nvm/versions/node/v24.21.0/bin/npm i -g --prefix /opt/homebrew @deepseek-ai/dsh@0.2.1-rc.2
93
+ # 重启 deck-studio(进程环境带 relay keys,见下)
94
+ cd <repo> && dsh --profile deck-studio --no-open
95
+ ```
96
+
97
+ **回归结果**:
98
+
99
+ | 检查项 | 结果 |
100
+ |---|---|
101
+ | `dsh --version`(经启动壳) | ✅ `0.2.1-rc.2` |
102
+ | `--dump-config --profile deck-studio` | ✅ 0 stderr,插件/预设/tools 条目不丢 |
103
+ | deck-studio 启动 13090 | ✅ 正常打印 token,无迁移报错 |
104
+ | 既有数据 | ✅ 迁移成功:decks 5 / designs 11 / 资源库 2 / Conversations 20,会话与治理数据都在 |
105
+ | Deckline 应用 | ✅ 首页 / Design 列表 / Deck 工作台(意图/脚本/Design/日志… + image storyboard) |
106
+ | 出图节点 | ✅ 工作台显示「默认节点 / gpt-image-2.5-sunburst 等 3 个模型」;节点 `/v1/models` 探活 200 |
107
+ | 页面异常 | ✅ 0 pageerror;首次加载有 2 条资源 404,二次加载 0 条(判断为瞬时,记录观察) |
108
+ | `/plugins/events` SSE | ⚠️ 导航时 `ERR_ABORTED`(与隔离测试一致,功能正常,继续观察) |
109
+
110
+ **升级后必须知道的运行要求**:
111
+ - **启动进程要带 relay keys**:`WOCHENG_API_KEY`(LLM,来自本机 `relays.secret.env` 的 `RELAY_B_WOCHENG_KEY`)
112
+ 与 出图节点 key(profile 里 `apiKeyEnv` 指定的环境变量)。不带 key 时服务能起,但 chat / 出图会失败。
113
+ 本机以 `set -a; . ~/.agents/skills/gpt-image/relays.secret.env; set +a; export WOCHENG_API_KEY="$RELAY_B_WOCHENG_KEY"` 后再启动。
114
+ - 开发用 `web`(3080)进程仍是升级前启动的旧版,下次重启自动用 0.2.1。
115
+
116
+ **回滚**:`~/.nvm/versions/node/v24.21.0/bin/npm i -g --prefix /opt/homebrew @deepseek-ai/dsh@0.1.7-rc.2`
117
+ (或从备份目录恢复包)→ 解压 `profiles.tar.gz` 覆盖 `~/.dsh/profiles` → 重启。
118
+
119
+ ## 已知安装失败:缺 DSH 内置包
120
+
121
+ - 症状:`dsh --profile <your> --port 13090` 启动时报
122
+ `failed to import loader entry preset-deck-studio (@deepseek-ai/dsh-agent-preset): Cannot find package '@deepseek-ai/dsh-agent-preset'`。
123
+ - 原因:当前 `dsh` 不是完整 npm 发行版(常见于从源码 checkout / 自编译,或 PATH 里指向了旧安装)。
124
+ - 处理:`which -a dsh` 确认实际路径 → 用 `npm i -g @deepseek-ai/dsh@0.2.1-rc.2` 的发行版启动;
125
+ 已在本机验证 0.2.1-rc.2 正常。详见 [install.md](install.md) §4.3。
126
+
127
+ ## 最低版本与 0.1.5
128
+
129
+ - Deckline 要求 **DSH ≥ 0.1.7 < 0.3**;**0.1.5 及更早不支持**(没有 preset 需要的
130
+ `@deepseek-ai/dsh-agent-preset` 等内置包,启动会直接报 `Cannot find package`)。
131
+ - 用 `dsh --version` 确认;是 0.1.x 旧安装(常见于源码 checkout / 旧 PATH)时,改用
132
+ `npm i -g @deepseek-ai/dsh@0.2.1-rc.2` 的发行版,并确认 `which -a dsh` 第一个是它。
133
+ - 升级后建议重建 deckline profile(`dsh --profile deckline --from-default-profile web --dump-config` 后再
134
+ `dsh plugin --profile deckline add ...`),避免沿用旧版本的 profile 模板。
@@ -0,0 +1,252 @@
1
+ # Install & configure (Deckline · DSH plugin)
2
+
3
+ Deckline (`dsh-plugin-deck-studio`) is an **image-deck workbench plugin** for DeepSeek Harness (DSH):
4
+ native DSH chat on the left, a full-page image-deck workspace in the middle, and library / Designs /
5
+ assets / conversations on the right. It is not a standalone app — it runs on a DSH profile.
6
+
7
+ > Target users: people who build content and presentations with DSH. After setup you can edit a deck
8
+ > from its script, render slide images on request (single page or a named batch), manage governance documents, register your own
9
+ > design styles (with sample images) and start from 8 preset styles.
10
+
11
+ ## 0. Prerequisites
12
+
13
+ | Dependency | Version | Notes |
14
+ |---|---|---|
15
+ | Node.js | `^22.19` or `≥ 24` | Same as the DSH runtime; Node 23 is not supported |
16
+ | pnpm | 10+ | DSH uses pnpm to manage profile dependencies |
17
+ | DSH | `≥ 0.1.7 < 0.3` | `npm i -g @deepseek-ai/dsh`; verified on 0.1.7-rc.2 / 0.2.1-rc.2, see [compat.md](compat.md) |
18
+ | An LLM provider key | — | e.g. `WOCHENG_API_KEY` (configured in the profile, see DSH docs) |
19
+ | An image node key | — | For real renders; the fake mode lets you try the flow first (section 4) |
20
+
21
+ ```sh
22
+ node -v && pnpm -v && dsh --version
23
+ ```
24
+
25
+ ## 1. Installation (pick one of three)
26
+
27
+ ### A. From npm (recommended once published)
28
+
29
+ ```sh
30
+ # 1) create a deckline profile (based on the web template shipped with DSH)
31
+ dsh --profile deckline --from-default-profile web
32
+
33
+ # 2) install the plugin; dsh plugin add adds the bundle to the profile and enables it
34
+ dsh plugin --profile deckline add dsh-plugin-deck-studio
35
+ ```
36
+
37
+ ### B. From the Git repository (public repo, no npm account needed)
38
+
39
+ ```sh
40
+ dsh --profile deckline --from-default-profile web
41
+ dsh plugin --profile deckline add "github:<your-account>/dsh-deck-studio#path:/plugin/deck-studio"
42
+ ```
43
+
44
+ > pnpm's support for git subdirectories varies by version; if `#path:` fails, use C (clone + local install).
45
+
46
+ ### C. Local clone (recommended for development / debugging)
47
+
48
+ ```sh
49
+ git clone https://github.com/<your-account>/dsh-deck-studio.git
50
+ cd dsh-deck-studio
51
+ pnpm install --ignore-scripts # optional: skip if you only run the plugin
52
+
53
+ dsh --profile deckline --from-default-profile web
54
+ dsh plugin --profile deckline add "$PWD/plugin/deck-studio"
55
+ ```
56
+
57
+ Verify the installation:
58
+
59
+ ```sh
60
+ dsh --profile deckline --dump-config | grep -n "deck-studio" | head
61
+ ```
62
+
63
+ Seeing both `deck-studio` and `preset-deck-studio` means the install succeeded.
64
+
65
+ ## 2. First configuration: decks / workspace / port
66
+
67
+ All Deckline configuration lives in the **profile patch**: `~/.dsh/profiles/deckline/cordis.patch.yml`.
68
+ The repository ships a copy-paste example:
69
+ [`plugin/deck-studio/cordis.example.yml`](../plugin/deck-studio/cordis.example.yml).
70
+
71
+ The minimum (append to the profile's `cordis.patch.yml`):
72
+
73
+ ```yaml
74
+ # your deck source directory (read-only; any existing deck directory works)
75
+ - id: deck-studio
76
+ name: 'dsh-plugin-deck-studio'
77
+ config:
78
+ displayName: 'Deckline'
79
+ userName: 'Yuna'
80
+ # where new decks are created; empty = the plugin's internal .deck/projects/<id>/source
81
+ projectsRoot: '/absolute/path/to/your/sharing'
82
+ decks:
83
+ - id: my-deck
84
+ label: 'My deck'
85
+ sourceRoot: '/absolute/path/to/my-deck'
86
+ ```
87
+
88
+ > `decks[].sourceRoot` is read-only: Deckline never writes to the source directory; all edits and
89
+ > revisions live in `.deck/`. You can also start with **+ New Deck** if you have no deck yet.
90
+
91
+ ### Per-deck project rules (optional)
92
+
93
+ Drop a `deck.rules.md` at the root of a deck project to describe the creative constraints for that deck
94
+ (audience, taboos, brand, facts that must be respected). The Deck Agent reads and follows it on every
95
+ `deck_source_overview`:
96
+
97
+ ```md
98
+ # Project rules
99
+ - Audience: internal business and IT, no technical background assumed
100
+ - Taboos: no unverified efficiency numbers; never name real customers
101
+ - Visual: cream paper + colored pencil, no 3D
102
+ ```
103
+
104
+ - It only constrains **that one deck** and does not affect other decks; without the file there are no rules.
105
+ - It **cannot override** the product's hard rules (read-only source decks / QA gates / no fabrication /
106
+ user confirmation). In a conflict the hard rules win and the agent says so.
107
+ - Max 8 KB; it is never written back and never triggers any automatic action.
108
+ - Do not name it `AGENTS.md`: that belongs to DSH workspace instructions and Deckline does not depend on it
109
+ (see `docs/adr/0020` in the development repo).
110
+
111
+ ## 2.5 Where the data lives: source deck / documents / images / exports
112
+
113
+ Deckline keeps data in two places: **your deck source** (`projectsRoot`, plain markdown + source images)
114
+ and **runtime data** (`dataRoot`: working copies / renders / events). If you cannot find files after
115
+ creating a deck, check this table first.
116
+
117
+ | Content | Location | Notes |
118
+ |---|---|---|
119
+ | New deck source documents | `<projectsRoot>/<dir>/` | `intent.md` / `script.md` / `style-reference.md`; confirmed `docs/*.md` are written back here; `prompts/`, `pages/`, `outputs/` sit alongside |
120
+ | Working copies + governance docs | `<dataRoot>/decks/<deckId>/docs/` | changelog / feedback / research / materials / review / fact-check…; stay here until confirmed |
121
+ | Script revisions | `<dataRoot>/decks/<deckId>/script/revisions/vNNN.md` | copy-on-write; `current.md` is always latest |
122
+ | Rendered page images | `<dataRoot>/decks/<deckId>/slides/<pageId>/versions/<vNNN>/image.png` | one directory per revision, with `prompt.txt` / `content.json` / `qa.json`; old revisions are never overwritten |
123
+ | Style samples | `<dataRoot>/projects/<canvasDeckId>/source/designs/<ABBR>/sample-vNNN.png` (or your own `designs/<ABBR>/`) | samples imported / pasted in the UI |
124
+ | Exports | your browser's download folder | zips from "Export" (PDF / PPTX / handoff package) |
125
+
126
+ - **`dataRoot` defaults to `$DSH_HOME/deckline`** (usually `~/.dsh/deckline`) — never inside the plugin
127
+ package, so plugin upgrades do not touch it. Override with `dataRoot: '/your/path'` in the profile patch,
128
+ or the `DSH_DECK_DATA_ROOT` env var.
129
+ - **`projectsRoot`** decides where new decks are created; if unset they go to `<dataRoot>/projects/<deckId>/source`.
130
+ - **Managed source decks are read-only**: Deckline never writes to a deck registered via `decks[].sourceRoot`.
131
+ - **Backup**: archive `dataRoot` and `projectsRoot` together (documents + images + events):
132
+ ```sh
133
+ tar czf deckline-backup-$(date +%Y%m%d).tgz "$DSH_HOME/deckline" "<your projectsRoot>"
134
+ ```
135
+ - The workbench **Settings** panel shows the current data directory; in the development repo the data root
136
+ is the repo's `.deck/` (configured explicitly in the profile).
137
+
138
+ ## 3. Start
139
+
140
+ ```sh
141
+ dsh --profile deckline --port 13090
142
+ ```
143
+
144
+ Open the `http://127.0.0.1:13090/?token=…` URL printed in the terminal.
145
+ `--port` is optional; the default is 13090 to avoid colliding with DSH's web profile on 3080.
146
+
147
+ ## 4. Configure the image key (required for real renders)
148
+
149
+ Deckline supports:
150
+
151
+ - **fake mode** (default, offline): renders placeholder SVGs, enough to try the whole flow;
152
+ - **real mode**: calls an OpenAI-compatible `/v1/images/generations` node for real images.
153
+
154
+ > The plugin **ships no image node address** (provider-neutral): real mode requires your own endpoint
155
+ > and key. Otherwise the Settings panel says "no endpoint / key configured" and rendering fails loudly
156
+ > instead of silently hitting a default node.
157
+
158
+ ### 4.1 Environment variables (simplest)
159
+
160
+ ```sh
161
+ # image node (any OpenAI-compatible relay works)
162
+ export DSH_DECK_IMAGE_ENDPOINT="https://your-image-relay.example.com/v1/images/generations"
163
+ export DSH_DECK_IMAGE_MODEL="your-image-model"
164
+ export DSH_DECK_IMAGE_API_KEY="sk-…" # shell / secret manager only
165
+ ```
166
+
167
+ ### 4.2 Profile patch (recommended: multiple nodes + team-shared config)
168
+
169
+ Override the `deck-studio` render config in `~/.dsh/profiles/deckline/cordis.patch.yml`:
170
+
171
+ ```yaml
172
+ - id: deck-studio
173
+ name: 'dsh-plugin-deck-studio'
174
+ config:
175
+ # …keep the displayName / decks from above
176
+ render:
177
+ mode: real
178
+ size: '2560x1440'
179
+ default: 'main/gpt-image-2.5-sunburst'
180
+ nodes:
181
+ - id: main
182
+ name: 'Main node'
183
+ endpoint: 'https://your-image-relay.example.com/v1/images/generations'
184
+ # choose one: the environment variable name, or a local key file (KEY=VALUE, chmod 600, never in git)
185
+ apiKeyEnv: 'DSH_DECK_IMAGE_API_KEY'
186
+ apiKeyFile: '~/.config/image-relay.env'
187
+ models:
188
+ - 'gpt-image-2.5-sunburst'
189
+ - 'gpt-image-2'
190
+ ```
191
+
192
+ Key points:
193
+
194
+ - **Keys never go into the repo or into plain-text profile config**: the config only holds `apiKeyEnv`
195
+ (environment variable name) or `apiKeyFile` (local file path).
196
+ - The workbench **Settings** panel shows whether each node's key is configured (boolean + source only,
197
+ never the key itself).
198
+ - When no key is configured or the mode is still fake, the Settings panel shows a hint.
199
+ - Model names must match what your relay actually serves; for OpenAI-compatible `gpt-image-2`-series ids,
200
+ consult the node's `/v1/models`.
201
+
202
+ ## 5. Adding your own designs (two ways)
203
+
204
+ ### Way 1: in the UI (recommended, supports pasting images)
205
+
206
+ 1. Home → **Designs** → **+ Add style**;
207
+ 2. Fill in name / abbreviation / visual description (used as the prompt);
208
+ 3. In the "sample images" area **paste with ⌘V, drag and drop, or pick files** (png/jpg/webp, multiple,
209
+ ≤12 MB each);
210
+ 4. Creating it also creates the style, a 0-page canvas and a design conversation; the samples appear
211
+ immediately on the Designs card and in the preview.
212
+
213
+ To add samples to an existing style: click the **eye** icon on the style card → "+ Add sample" at the
214
+ bottom of the preview (paste / pick files work the same way).
215
+
216
+ ### Way 2: drop files on disk
217
+
218
+ Samples live under `designs/<ABBR>/` (ABBR is your style abbreviation):
219
+
220
+ - canvas directory (recommended): `.deck/projects/<canvasDeckId>/source/designs/<ABBR>/`;
221
+ - or `designs/<ABBR>/` inside any managed deck source (Deckline scans and merges them).
222
+
223
+ Refresh the page afterwards. png / jpg / webp are supported; names are free-form
224
+ (`sample-01.jpg` and similar keep them ordered).
225
+
226
+ ### Use preset styles (no prompt writing needed)
227
+
228
+ **+ Import preset styles** on the Designs page imports all 8 bundled styles (business consulting /
229
+ premium dark / Japanese minimal / magazine editorial / tech blueprint / hand-drawn whiteboard /
230
+ Swiss minimal / aurora gradient), each with samples. See [styles.md](styles.md).
231
+
232
+ ## 6. Troubleshooting
233
+
234
+ | Symptom | What to do |
235
+ |---|---|
236
+ | Port already in use | `dsh --profile deckline --port 13100`; or kill the old process with `lsof -tiTCP:13090 -sTCP:LISTEN` |
237
+ | Page 404 / blank | Make sure the plugin bundle is enabled: `dsh --profile deckline --dump-config \| grep deck-studio` |
238
+ | No "render" button / render fails | Check the key state in Settings; confirm `render.mode: real`; `curl` the node endpoint |
239
+ | New decks land in the wrong place | Set `projectsRoot`; otherwise they go to the plugin data directory `.deck/projects/` |
240
+ | Where are data / samples | See section 2.5: source decks in `projectsRoot`, working copies / renders in `dataRoot` (default `~/.dsh/deckline`) |
241
+ | Upgrading the plugin | `dsh plugin --profile deckline install <spec>`, or `add` the new version again |
242
+
243
+ ## 7. Developers: local verification
244
+
245
+ ```sh
246
+ node plugin/deck-studio/verify/smoke.mjs # 46 contract tests (no network or key needed)
247
+ node scripts/harness-check.mjs # instruction-chain / link / redaction / ledger gate
248
+ dsh --profile deck-studio --no-open # development profile (port configured locally)
249
+ ```
250
+
251
+ The plugin has no third-party runtime dependencies (Node built-ins only). Changes to
252
+ `plugin/deck-studio/index.js` require a profile restart; changes to `ui/` html/css hot-reload.