dsh-plugin-prompt-polish 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,143 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
1
+ # dsh-plugin-prompt-polish
2
+
3
+ [![DSH Marketplace](https://img.shields.io/badge/DSH%20Marketplace-listed-4C6FFF)](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/blob/main/data/plugins/CAI-KUN-MAX__prompt-polish--dsh-plugin-prompt-polish.yml)
4
+
5
+ DeepSeek Harness 的**原生插件版**提示词优化器:模型选择器左侧一个小图标,点一下优化当前输入框草稿。
6
+ 不弹外部窗口、不复制粘贴、不需要 API Key —— 直接使用当前会话的 DSH 模型和额度。
7
+
8
+ ```
9
+ [ + ] [完全权限] [✨] [DeepSeek-V4.1-Flash ▾] [发送]
10
+ ↑
11
+ 提示词优化按钮
12
+ ```
13
+
14
+ ![输入框中的优化按钮](assets/screenshot-1.png)
15
+
16
+ ![优化结果回填到输入框](assets/screenshot-2.png)
17
+
18
+ ## 功能
19
+
20
+ - **图标按钮**:注册在 composer 的 `conversation.input.right` 插槽,位于模型选择器左侧;只有图标,无文字。
21
+ - **使用 DSH 额度**:服务端通过 `ctx.llm.stream()` 调用当前会话的 provider/model,凭据由 DSH 的 credentials seam 解析。
22
+ - **原地回填**:通过 composer 的 `inputActions.setDraft()` 写回,不发送消息,不新增会话轮次。
23
+ - **草稿变化保护**:请求返回时如果草稿已变化,拒绝回填并在按钮上显示红色错误态。
24
+ - **引用/附件保护**:草稿含引用 chip 或图片附件时拒绝优化。
25
+ - **Shift+点击恢复**:优化成功后再按住 Shift 点图标,可恢复上一版原文。
26
+ - **错误提示**:空草稿、插件未加载、网络错误、模型失败会在按钮 `title` 与红色状态中显示。
27
+
28
+ ## 已验证(本机真实运行)
29
+
30
+ - `/prompt-polish/health` 返回 200,插件 host 半已加载。
31
+ - 模型选择器左侧出现 ✨ 图标,位置与截图一致。
32
+ - 真实鼠标点击:79 字草稿 → 15 秒后回填为 1415 字结构化提示词。
33
+ - 再次点击:1415 字 → 1511 字,确认可以对当前结果继续优化。
34
+ - 全程未按 Enter、未点发送,草稿留在输入框中。
35
+ - 客户端使用 `sessionId` 前缀 `session-`,与 host 的 `ctx.sessions.get()` 对齐。
36
+
37
+ > Shift+点击恢复原文已实现,但本次自动化仅验证了普通点击;真实 Shift+点击请在界面中试一次。
38
+
39
+ ## 安装
40
+
41
+ 需要 DSH 桌面版和 Node.js(用于安装时的导入自检)。
42
+
43
+ ### 从 DSH 插件市场
44
+
45
+ 已收录到 DSH 插件市场(PR #14055 已合并):
46
+
47
+ <https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/blob/main/data/plugins/CAI-KUN-MAX__prompt-polish--dsh-plugin-prompt-polish.yml>
48
+
49
+ 在 DSH 的「插件」面板里搜索 **PromptPolish** 或 **prompt-polish** 即可安装。
50
+
51
+ ### 从 npm(发布后)
52
+
53
+ 包名已准备为 `dsh-plugin-prompt-polish`:
54
+
55
+ ```powershell
56
+ dsh plugin --profile desktop add dsh-plugin-prompt-polish
57
+ ```
58
+
59
+ ### 从源码安装(当前可用)
60
+
61
+ ```powershell
62
+ cd <this-folder>
63
+ powershell -ExecutionPolicy Bypass -File .\install-dsh-plugin.ps1
64
+ ```
65
+
66
+ 脚本会:
67
+
68
+ 1. 把 `dsh-plugin-prompt-polish` 复制到 `%USERPROFILE%\.dsh\profiles\desktop\node_modules\`;
69
+ 2. 在 profile 的 `cordis.patch.yml` 末尾加入 loader 条目(先备份 `.bak-prompt-polish`);
70
+ 3. 做一次 Node 导入自检。
71
+
72
+ 然后**重启一次 DeepSeek Harness**。Host 插件只在应用启动时加载,重启后图标才会出现。
73
+
74
+ > 如果使用 `web` profile,运行:
75
+ > `powershell -ExecutionPolicy Bypass -File .\install-dsh-plugin.ps1 -Profile web`
76
+
77
+ ## 使用
78
+
79
+ 1. 在输入框写好提示词。
80
+ 2. 点模型选择器左边的 ✨ 图标。
81
+ 3. 等待图标从转圈恢复;草稿会被替换为结构化版本,**不会自动发送**。
82
+ 4. 不满意再点一次:对当前草稿继续优化。
83
+ 5. 想恢复上一版:Shift+点击图标(仅当草稿仍等于上次优化结果时)。
84
+
85
+ ## 架构
86
+
87
+ ```text
88
+ client.js ── POST /prompt-polish/optimize ──► lib/index.js
89
+ (composer slot) {sessionId, draft} (host route)
90
+ ▲ │
91
+ │ inputActions.setDraft(text) │ ctx.sessions.get(sessionId)
92
+ └──────────────────────────────────────────────┘ .requestHeader() → provider/model
93
+ │
94
+ └ ctx.llm.stream(...) → DSH 额度
95
+ ```
96
+
97
+ - 不新增会话消息、不触发工具、不写入会话日志。
98
+ - 路由仅监听本机 web server;与页面同源,不开放 CORS 给任意站点(OPTIONS 仅用于本地调试)。
99
+ - 插件不读取 API Key;凭据完全由 DSH 管理。
100
+
101
+ ## 文件
102
+
103
+ ```text
104
+ package.json DSH bundle/client 元数据
105
+ lib/index.js host 插件:HTTP 路由 + ctx.llm 调用
106
+ lib/client.js browser bundle:slot 按钮
107
+ install-dsh-plugin.ps1 安装脚本(仓库根目录)
108
+ ```
109
+
110
+ ## 已知限制
111
+
112
+ - Host 插件需要**重启 DSH** 才能加载;当前桌面版没有通用的热加载入口。
113
+ - 新会话尚未产生第一条消息时,`requestHeader()` 还不存在,服务端回退到 `deepseek-official` + `deepseek-flash`;已有会话会严格使用当前会话的 provider/model。
114
+ - 依赖 DSH 内部的 slot 名称、`inputActions.setDraft`、`ctx.llm.stream` 与 `ctx.webServer.register`;DSH 升级后可能需要适配。
115
+ - 输出是一次性非流式结果(服务端流式收集后回填),长文本需要几秒。
116
+ - 不提供历史/多轮对话;重新点击就是对当前草稿再优化一次。
117
+ - 如果 DSH 页面不是从 `http://127.0.0.1:<port>` 加载(例如纯 `file://`),相对路径 fetch 可能失败;当前桌面版实测为本地 HTTP。
118
+
119
+ ## 故障排查
120
+
121
+ | 现象 | 处理 |
122
+ | --- | --- |
123
+ | 图标没出现 | 确认已重启 DSH;检查 profile patch 是否有 `prompt-polish` 条目;看 devtools console |
124
+ | 点击提示“插件服务未加载” | host 半没有加载;重启 DSH,或检查 `%USERPROFILE%\.dsh\profiles\desktop\node_modules\dsh-plugin-prompt-polish` |
125
+ | 点击提示“无法连接本地插件服务” | 页面不是同源 HTTP;检查 DSH 启动方式 |
126
+ | 提示“草稿含引用或附件” | 移除 @ 引用/图片后再点 |
127
+ | 提示模型错误 | 检查 DSH 当前模型是否可用、账户额度;DSH 本身能正常对话即可 |
128
+
129
+ 健康检查:DSH 启动后访问
130
+ `http://127.0.0.1:<DSH端口>/prompt-polish/health`
131
+ 应返回 `{"ok":true,"plugin":"dsh-plugin-prompt-polish","version":"0.1.0"}`。
132
+
133
+ ## 回滚
134
+
135
+ 如果重启后 DSH 无法启动,恢复 profile 补丁并删除插件目录:
136
+
137
+ ```powershell
138
+ $p = "$env:USERPROFILE\.dsh\profiles\desktop"
139
+ Copy-Item "$p\cordis.patch.yml.bak-prompt-polish" "$p\cordis.patch.yml" -Force
140
+ Remove-Item "$p\node_modules\dsh-plugin-prompt-polish" -Recurse -Force
141
+ ```
142
+
143
+ 然后重新启动 DSH。
Binary file
Binary file
@@ -0,0 +1,5 @@
1
+ # PromptPolish composer plugin bundle.
2
+ # The host loads lib/index.js and registers /prompt-polish/optimize.
3
+ - insert:
4
+ - id: prompt-polish
5
+ name: dsh-plugin-prompt-polish
package/lib/client.js ADDED
@@ -0,0 +1,162 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-plugin-prompt-polish",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+
8
+ const React = require("react");
9
+
10
+ const STYLE_ID = "dsh-plugin-prompt-polish/style";
11
+ if (typeof document !== "undefined" && document.getElementById(STYLE_ID) === null) {
12
+ const style = document.createElement("style");
13
+ style.id = STYLE_ID;
14
+ style.textContent = `
15
+ .pp-opt-btn{box-sizing:border-box;width:28px;height:28px;border:0;border-radius:999px;background:transparent;color:var(--dsw-alias-label-tertiary);display:inline-flex;align-items:center;justify-content:center;padding:0;margin-right:2px;cursor:pointer;flex:none;transition:background .15s,color .15s}
16
+ .pp-opt-btn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}
17
+ .pp-opt-btn:active{background:var(--dsw-alias-interactive-bg-pressed,var(--dsw-alias-interactive-bg-hover))}
18
+ .pp-opt-btn:disabled{opacity:.45;cursor:default}
19
+ .pp-opt-btn[data-error="true"]{color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-state-error-background,transparent)}
20
+ .pp-opt-spin{width:14px;height:14px;border:1.5px solid currentColor;border-right-color:transparent;border-radius:50%;animation:pp-opt-spin .8s linear infinite;display:inline-block}
21
+ @keyframes pp-opt-spin{to{transform:rotate(360deg)}}
22
+ `;
23
+ document.head.appendChild(style);
24
+ }
25
+
26
+ function icon() {
27
+ return React.createElement(
28
+ "svg",
29
+ {
30
+ viewBox: "0 0 16 16",
31
+ width: 16,
32
+ height: 16,
33
+ fill: "none",
34
+ stroke: "currentColor",
35
+ strokeWidth: 1.25,
36
+ strokeLinecap: "round",
37
+ strokeLinejoin: "round",
38
+ "aria-hidden": true,
39
+ },
40
+ React.createElement("path", {
41
+ d: "M8 1.8l1.15 3.05L12.2 6 9.15 7.15 8 10.2 6.85 7.15 3.8 6l3.05-1.15L8 1.8z",
42
+ }),
43
+ React.createElement("path", {
44
+ d: "M12.4 10.4l.55 1.45 1.45.55-1.45.55-.55 1.45-.55-1.45-1.45-.55 1.45-.55.55-1.45z",
45
+ })
46
+ );
47
+ }
48
+
49
+ function Inner(props) {
50
+ const state = props.useInput((s) => s);
51
+ const draftRef = React.useRef(state.draft);
52
+ draftRef.current = state.draft;
53
+ const lastRef = React.useRef(null);
54
+ const timerRef = React.useRef(0);
55
+ const [busy, setBusy] = React.useState(false);
56
+ const [error, setError] = React.useState(null);
57
+
58
+ React.useEffect(() => () => {
59
+ if (timerRef.current) window.clearTimeout(timerRef.current);
60
+ }, []);
61
+
62
+ const showError = (message) => {
63
+ setError(message);
64
+ if (timerRef.current) window.clearTimeout(timerRef.current);
65
+ timerRef.current = window.setTimeout(() => setError(null), 4000);
66
+ };
67
+
68
+ const onClick = async (event) => {
69
+ if (busy) return;
70
+ const original = draftRef.current;
71
+ if (original.trim().length === 0) {
72
+ showError("草稿为空");
73
+ return;
74
+ }
75
+ if (event.shiftKey && lastRef.current !== null && original === lastRef.current.result) {
76
+ props.inputActions.setDraft(lastRef.current.original);
77
+ lastRef.current = null;
78
+ setError(null);
79
+ return;
80
+ }
81
+ setBusy(true);
82
+ setError(null);
83
+ try {
84
+ if ((state.occurrences?.length ?? 0) > 0 || (state.imageIds?.length ?? 0) > 0) {
85
+ throw new Error("草稿含引用或附件,无法安全替换");
86
+ }
87
+ const sid = String(props.sessionId ?? "");
88
+ const sessionId = sid.length === 0
89
+ ? ""
90
+ : (sid.startsWith("session-") ? sid : "session-" + sid);
91
+ if (sessionId.length === 0) throw new Error("缺少会话 ID,请刷新页面");
92
+ const response = await fetch("/prompt-polish/optimize", {
93
+ method: "POST",
94
+ headers: { "content-type": "application/json" },
95
+ body: JSON.stringify({ sessionId, draft: original }),
96
+ });
97
+ if (response.status === 404) throw new Error("插件服务未加载,请重启 DSH");
98
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
99
+ const data = await response.json();
100
+ if (!data || data.ok !== true || typeof data.text !== "string") {
101
+ throw new Error(data && data.error && data.error.message ? data.error.message : "优化失败");
102
+ }
103
+ if (draftRef.current !== original) {
104
+ showError("草稿已修改,未回填");
105
+ return;
106
+ }
107
+ props.inputActions.setDraft(data.text);
108
+ lastRef.current = { original, result: data.text };
109
+ } catch (failure) {
110
+ const raw = failure instanceof Error ? failure.message : String(failure);
111
+ const message = raw === "Failed to fetch"
112
+ ? "无法连接本地插件服务,请重启 DSH"
113
+ : raw;
114
+ showError(message);
115
+ console.warn("prompt-polish:", message);
116
+ } finally {
117
+ setBusy(false);
118
+ }
119
+ };
120
+
121
+ const label = "优化提示词";
122
+ const title = error !== null ? error : "优化提示词(Shift+点击恢复上一版)";
123
+ return React.createElement(
124
+ "button",
125
+ {
126
+ type: "button",
127
+ className: "pp-opt-btn",
128
+ "aria-label": label,
129
+ title,
130
+ "data-error": error !== null ? "true" : "false",
131
+ "data-busy": busy ? "true" : "false",
132
+ disabled: busy,
133
+ onMouseDown: (event) => event.preventDefault(),
134
+ onClick,
135
+ },
136
+ busy
137
+ ? React.createElement("span", { className: "pp-opt-spin", "aria-hidden": true })
138
+ : icon()
139
+ );
140
+ }
141
+
142
+ function OptimizeButton(props) {
143
+ if (typeof props.useInput !== "function" || props.inputActions === undefined) return null;
144
+ return React.createElement(Inner, props);
145
+ }
146
+
147
+ const inject = ["slots"];
148
+
149
+ function apply(ctx) {
150
+ ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
151
+ name: "conversation.input.right",
152
+ id: "prompt-polish",
153
+ order: 10,
154
+ inject: (sessionId) => ({ sessionId }),
155
+ }, OptimizeButton));
156
+ }
157
+
158
+ exports.apply = apply;
159
+ exports.inject = inject;
160
+ return module.exports;
161
+ },
162
+ });
package/lib/index.js ADDED
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Host half of dsh-plugin-prompt-polish.
3
+ *
4
+ * A single local HTTP route accepts { sessionId, draft }, resolves the session's
5
+ * current provider/model from its request header, and streams a rewrite through
6
+ * `ctx.llm`. That means the request uses the signed-in DeepSeek Harness account
7
+ * and its quota — no API key and no extra settings.
8
+ *
9
+ * The route never appends to the session, never sends a message and never runs
10
+ * tools. It is a one-shot auxiliary completion with a bounded output.
11
+ */
12
+ import { BlockAssembler, createUserMessage } from '@deepseek-ai/dsh-llm';
13
+
14
+ export const name = 'dsh-plugin-prompt-polish';
15
+ export const inject = ['webServer', 'llm', 'sessions'];
16
+
17
+ const ROUTE = '/prompt-polish/optimize';
18
+ const MAX_BODY_BYTES = 256 * 1024;
19
+ const MAX_DRAFT_CHARS = 12000;
20
+ const MAX_OUTPUT_TOKENS = 8000;
21
+ const TIMEOUT_MS = 90_000;
22
+
23
+ const SYSTEM_PROMPT = `你是资深提示词工程师。把用户草稿改写成可直接交给 AI 执行的高质量提示词,而不是同义句改写或简单排版。
24
+
25
+ 必须遵守:
26
+ 1. 信息保真:不得新增项目特定事实(字段名、路径、接口、业务规则、技术栈);不得删除硬性约束。无法从原文确定的项目特定信息用“[待确认:…]”占位;通用工程做法(如分块读取、日志、重试)可以写成要求,但不得冒充用户给定的事实。
27
+ 2. 结构:按“任务目标 / 输入 / 输出 / 约束 / 验收标准 / 交付物”组织,缺少信息的小节可以省略。
28
+ 3. 可执行性:把含糊要求改成可检查的验收标准;原文没有的数值阈值写成“[建议确认:…]”。
29
+ 4. 保留原语言、代码、路径、数字、专有名词。
30
+ 5. 只输出改写后的提示词正文;不要解释、前言、结语,不要用代码块包裹全文。
31
+ 6. 占位符控制在必要范围(尽量不超过 5 处),不要把整篇写成问卷。
32
+ 7. 即使原文已经清楚,也要做结构重排和验收标准补全,不能只改标点。`;
33
+
34
+ function json(res, status, value) {
35
+ const body = JSON.stringify(value);
36
+ res.writeHead(status, {
37
+ 'content-type': 'application/json; charset=utf-8',
38
+ 'cache-control': 'no-store',
39
+ 'content-length': Buffer.byteLength(body),
40
+ });
41
+ res.end(body);
42
+ }
43
+
44
+ function readBody(req) {
45
+ return new Promise((resolve, reject) => {
46
+ const chunks = [];
47
+ let size = 0;
48
+ req.on('data', (chunk) => {
49
+ size += chunk.length;
50
+ if (size > MAX_BODY_BYTES) {
51
+ reject(new Error('BODY_TOO_LARGE'));
52
+ req.destroy();
53
+ return;
54
+ }
55
+ chunks.push(chunk);
56
+ });
57
+ req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
58
+ req.on('error', reject);
59
+ });
60
+ }
61
+
62
+ function resolveRoute(ctx, sessionId) {
63
+ const session = ctx.sessions.get(sessionId);
64
+ if (session === undefined) throw new Error('SESSION_NOT_FOUND');
65
+ const header = session.requestHeader();
66
+ const config = header?.config;
67
+ const provider = typeof config?.provider === 'string' && config.provider.length > 0
68
+ ? config.provider
69
+ : 'deepseek-official';
70
+ const model = typeof config?.model === 'string' && config.model.length > 0
71
+ ? config.model
72
+ : 'deepseek-flash';
73
+ const reasoningEffort = typeof config?.reasoningEffort === 'string' && config.reasoningEffort.length > 0
74
+ ? config.reasoningEffort
75
+ : undefined;
76
+ return { sessionId: session.id, provider, model, reasoningEffort };
77
+ }
78
+
79
+ async function rewrite(ctx, route, draft) {
80
+ const controller = new AbortController();
81
+ const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
82
+ try {
83
+ const messages = [
84
+ createUserMessage({
85
+ content: [{ type: 'text', text: `请优化下面的提示词:\n<<<PROMPT\n${draft}\nPROMPT>>>` }],
86
+ source: { kind: 'plugin', plugin: name },
87
+ }),
88
+ ];
89
+ const assembler = new BlockAssembler();
90
+ for await (const chunk of ctx.llm.stream({
91
+ provider: route.provider,
92
+ model: route.model,
93
+ reasoningEffort: route.reasoningEffort,
94
+ messages,
95
+ system: SYSTEM_PROMPT,
96
+ temperature: 0.4,
97
+ maxTokens: MAX_OUTPUT_TOKENS,
98
+ sessionId: route.sessionId,
99
+ signal: controller.signal,
100
+ })) {
101
+ assembler.push(chunk);
102
+ }
103
+ const finish = assembler.finish;
104
+ if (finish !== undefined && (finish.kind === 'error' || finish.kind === 'aborted')) {
105
+ const failure = finish.failure;
106
+ throw Object.assign(new Error(failure?.message || 'MODEL_FAILED'), {
107
+ code: failure?.code || (finish.kind === 'aborted' ? 'ABORTED' : 'MODEL_FAILED'),
108
+ });
109
+ }
110
+ const text = assembler.blocks()
111
+ .filter((block) => block.type === 'text')
112
+ .map((block) => block.text)
113
+ .join('')
114
+ .trim();
115
+ if (text.length === 0) throw Object.assign(new Error('EMPTY_RESPONSE'), { code: 'EMPTY_RESPONSE' });
116
+ return text;
117
+ } finally {
118
+ clearTimeout(timer);
119
+ }
120
+ }
121
+
122
+ export function apply(ctx) {
123
+ ctx.effect(() => ctx.webServer.register({
124
+ kind: 'exact',
125
+ path: '/prompt-polish/health',
126
+ handler: (req, res) => {
127
+ json(res, 200, { ok: true, plugin: name, version: '0.1.0' });
128
+ },
129
+ }), 'prompt-polish: health route');
130
+
131
+ ctx.effect(() => ctx.webServer.register({
132
+ kind: 'exact',
133
+ path: ROUTE,
134
+ handler: async (req, res) => {
135
+ if (req.method === 'OPTIONS') {
136
+ res.writeHead(204, {
137
+ 'access-control-allow-origin': '*',
138
+ 'access-control-allow-methods': 'POST, OPTIONS',
139
+ 'access-control-allow-headers': 'content-type',
140
+ });
141
+ res.end();
142
+ return;
143
+ }
144
+ if (req.method !== 'POST') {
145
+ json(res, 405, { ok: false, error: { code: 'METHOD_NOT_ALLOWED', message: 'POST only' } });
146
+ return;
147
+ }
148
+ try {
149
+ const raw = await readBody(req);
150
+ let body;
151
+ try {
152
+ body = JSON.parse(raw);
153
+ } catch {
154
+ json(res, 400, { ok: false, error: { code: 'BAD_JSON', message: '请求体不是 JSON' } });
155
+ return;
156
+ }
157
+ const sessionId = typeof body?.sessionId === 'string' ? body.sessionId : '';
158
+ const draft = typeof body?.draft === 'string' ? body.draft : '';
159
+ if (sessionId.length === 0) {
160
+ json(res, 400, { ok: false, error: { code: 'NO_SESSION', message: '缺少 sessionId' } });
161
+ return;
162
+ }
163
+ if (draft.trim().length === 0) {
164
+ json(res, 400, { ok: false, error: { code: 'EMPTY_DRAFT', message: '草稿为空' } });
165
+ return;
166
+ }
167
+ if (draft.length > MAX_DRAFT_CHARS) {
168
+ json(res, 400, {
169
+ ok: false,
170
+ error: { code: 'DRAFT_TOO_LONG', message: `草稿超过 ${MAX_DRAFT_CHARS} 字符` },
171
+ });
172
+ return;
173
+ }
174
+ const route = resolveRoute(ctx, sessionId);
175
+ const text = await rewrite(ctx, route, draft);
176
+ json(res, 200, {
177
+ ok: true,
178
+ text,
179
+ model: route.model,
180
+ provider: route.provider,
181
+ });
182
+ } catch (error) {
183
+ const code = typeof error?.code === 'string' ? error.code : 'FAILED';
184
+ const message = error?.code === 'SESSION_NOT_FOUND'
185
+ ? '当前会话不存在或已关闭'
186
+ : typeof error?.message === 'string'
187
+ ? error.message
188
+ : '优化失败';
189
+ ctx.logger?.warn?.(`prompt-polish: ${code}`);
190
+ json(res, 200, { ok: false, error: { code, message } });
191
+ }
192
+ },
193
+ }), 'prompt-polish: optimize route');
194
+ }
package/package.json CHANGED
@@ -1,6 +1,75 @@
1
- {
2
- "name": "dsh-plugin-prompt-polish",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
1
+ {
2
+ "name": "dsh-plugin-prompt-polish",
3
+ "version": "0.1.0",
4
+ "description": "Icon-only prompt optimizer in the DeepSeek Harness composer, using the active session model and DSH quota.",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "exports": {
8
+ ".": "./lib/index.js",
9
+ "./client": "./lib/client.js",
10
+ "./cordis.patch.yml": "./cordis.patch.yml",
11
+ "./package.json": "./package.json"
12
+ },
13
+ "files": [
14
+ "lib/index.js",
15
+ "lib/client.js",
16
+ "cordis.patch.yml",
17
+ "screenshots.json",
18
+ "assets/*.png",
19
+ "README.md"
20
+ ],
21
+ "keywords": [
22
+ "dsh",
23
+ "dsh-plugin",
24
+ "deepseek-harness",
25
+ "deepseek",
26
+ "prompt-engineering",
27
+ "composer",
28
+ "ui"
29
+ ],
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://github.com/CAI-KUN-MAX/prompt-polish.git",
33
+ "directory": "dsh-plugin-prompt-polish"
34
+ },
35
+ "homepage": "https://github.com/CAI-KUN-MAX/prompt-polish/tree/main/dsh-plugin-prompt-polish",
36
+ "bugs": {
37
+ "url": "https://github.com/CAI-KUN-MAX/prompt-polish/issues"
38
+ },
39
+ "dsh": {
40
+ "bundle": {
41
+ "patch": "./cordis.patch.yml"
42
+ },
43
+ "client": {
44
+ "platform": "web",
45
+ "inject": [
46
+ "@deepseek-ai/dsh-client-ui-conversation"
47
+ ]
48
+ }
49
+ },
50
+ "peerDependencies": {
51
+ "@deepseek-ai/dsh-host-webserver": "*",
52
+ "@deepseek-ai/dsh-llm": "*",
53
+ "@deepseek-ai/dsh-session": "*",
54
+ "react": "^18.2.0"
55
+ },
56
+ "peerDependenciesMeta": {
57
+ "@deepseek-ai/dsh-host-webserver": {
58
+ "optional": true
59
+ },
60
+ "@deepseek-ai/dsh-llm": {
61
+ "optional": true
62
+ },
63
+ "@deepseek-ai/dsh-session": {
64
+ "optional": true
65
+ },
66
+ "react": {
67
+ "optional": true
68
+ }
69
+ },
70
+ "publishConfig": {
71
+ "access": "public",
72
+ "registry": "https://registry.npmjs.org/"
73
+ },
74
+ "license": "MIT"
75
+ }
@@ -0,0 +1,4 @@
1
+ [
2
+ "assets/screenshot-1.png",
3
+ "assets/screenshot-2.png"
4
+ ]