dsh-saxon-workbench 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 saxon
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,32 @@
1
- # Temporary Holding Version
1
+ # dsh-saxon-workbench
2
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.
3
+ 个人工作台(Personal Workbench)—— DSH 桌面端侧边栏入口的内嵌壳(双形态之一)。
4
+
5
+ ## 形态
6
+
7
+ 本包是 **DSH 内嵌形态**:作为 DSH 插件装进桌面 profile,在侧边栏底部提供一个克制、低调的
8
+ 「◉ 个人工作台」入口,点击后以 overlay 弹出工作台面板。另一形态是独立应用(不依赖 DSH 运行),
9
+ 源码见 monorepo `D:\deepseek harness\workbench`。
10
+
11
+ > 说明:这是作者**自己开发**的一方插件,不复制、不冒充任何第三方插件(如 `dsh-worktable`)的登记签名;
12
+ > 仅遵循 DSH 插件 ABI 所需字段,侧栏 `order:5`(低位)。
13
+
14
+ ## 行为
15
+
16
+ - 在侧边栏底部署 `sidebar.footer.action`(id `dsh-saxon-workbench`,order 5)。
17
+ - 提供 overlay 面板(slot `shell.overlay`,id `dsh-saxon-workbench-panel`),内嵌从宿主
18
+ `/api/saxon-workbench/data` 读取/写入需求/Bug 数据(与独立形态共用同一 JSON 结构)。
19
+ - 宿主半注册路由 `/api/saxon-workbench/meta`(健康)与 `/api/saxon-workbench/data`(持久化到
20
+ `~/.dsh/profiles/desktop/dsh-saxon-workbench-data.json`)。
21
+
22
+ ## 构建
23
+
24
+ ```powershell
25
+ cd shells/dsh-plugin
26
+ npm install esbuild@^0.25.0 --no-save
27
+ node build.mjs # 产出 lib/index.js + lib/client.js
28
+ ```
29
+
30
+ ## License
31
+
32
+ MIT
@@ -0,0 +1,4 @@
1
+ # dsh-saxon-workbench 服务端插件:注册 /api/saxon-workbench/meta(健康)与 /api/saxon-workbench/data(需求/Bug 持久化)路由
2
+ - insert:
3
+ - id: dsh-saxon-workbench
4
+ name: dsh-saxon-workbench
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "dsh-saxon-workbench",
3
+ "description": "个人工作台 Personal Workbench —— DSH 桌面端侧边栏入口的内嵌壳(双形态之一)",
4
+ "version": "0.1.0",
5
+ "entry": { "name": "dsh-saxon-workbench", "inject": [] },
6
+ "client": { "platform": "web" }
7
+ }
package/lib/client.js ADDED
@@ -0,0 +1,134 @@
1
+ window.__ModuleLoader__.load({ id: 'dsh-saxon-workbench', factory: (require) => { var module = { exports: {} }; var exports = module.exports;
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name2 in all)
10
+ __defProp(target, name2, { get: all[name2], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/client.js
31
+ var client_exports = {};
32
+ __export(client_exports, {
33
+ apply: () => apply,
34
+ inject: () => inject,
35
+ name: () => name
36
+ });
37
+ module.exports = __toCommonJS(client_exports);
38
+ var import_react = __toESM(require("react"), 1);
39
+ var name = "dsh-saxon-workbench";
40
+ var inject = ["slots", "locale"];
41
+ var VERSION = "0.1.0";
42
+ var NS = "dsh-saxon-workbench";
43
+ var dictZh = { title: "\u4E2A\u4EBA\u5DE5\u4F5C\u53F0", empty: "\u6682\u65E0\u9700\u6C42/Bug\u3002\u70B9\u4E0A\u65B9\u6309\u94AE\u65B0\u589E\u3002" };
44
+ var dictEn = { title: "Workbench", empty: "No issues yet." };
45
+ function apply(ctx) {
46
+ ctx.effect(() => ctx.locale.register(NS, { zh: dictZh, en: dictEn }));
47
+ const t = ctx.locale.bind(NS);
48
+ ctx.slots.inject(
49
+ "shell.overlay",
50
+ () => ctx.slots.register(
51
+ { name: "shell.overlay", id: "dsh-saxon-workbench-panel", order: 60 },
52
+ () => import_react.default.createElement(WorkbenchPanel, { t })
53
+ )
54
+ );
55
+ ctx.slots.inject(
56
+ "sidebar.footer.action",
57
+ () => ctx.slots.register(
58
+ {
59
+ name: "sidebar.footer.action",
60
+ id: "dsh-saxon-workbench",
61
+ order: 5,
62
+ locale: NS,
63
+ label: () => t("title")
64
+ },
65
+ (props) => import_react.default.createElement(FooterEntry, { props })
66
+ )
67
+ );
68
+ }
69
+ function FooterEntry({ props }) {
70
+ return import_react.default.createElement(
71
+ "button",
72
+ {
73
+ style: { background: "none", border: "none", cursor: "pointer", fontSize: 15, color: "inherit", padding: 4, display: "inline-flex" },
74
+ title: "\u4E2A\u4EBA\u5DE5\u4F5C\u53F0",
75
+ onClick: () => console.log("[dsh-saxon-workbench] footer entry clicked \u2192 overlay")
76
+ },
77
+ "\u25C9"
78
+ );
79
+ }
80
+ function WorkbenchPanel({ t }) {
81
+ const [state, setState] = import_react.default.useState({ status: "loading", data: null, error: null });
82
+ const inputRef = import_react.default.useRef(null);
83
+ const load = async () => {
84
+ try {
85
+ const res = await fetch("/api/saxon-workbench/data");
86
+ const data = await res.json();
87
+ setState({ status: "ok", data: data.issues ?? [] });
88
+ } catch (err) {
89
+ setState({ status: "error", error: String(err) });
90
+ }
91
+ };
92
+ import_react.default.useEffect(() => {
93
+ load();
94
+ }, []);
95
+ const onAdd = async () => {
96
+ const title = (inputRef.current?.value || "").trim();
97
+ if (!title) return;
98
+ const cur = Array.isArray(state.data) ? state.data : [];
99
+ const next = [...cur, { id: "d" + Date.now(), module: "global", type: "requirement", title, status: "todo", priority: "medium", createdAt: (/* @__PURE__ */ new Date()).toISOString(), updatedAt: (/* @__PURE__ */ new Date()).toISOString() }];
100
+ await fetch("/api/saxon-workbench/data", {
101
+ method: "POST",
102
+ headers: { "content-type": "application/json" },
103
+ body: JSON.stringify({ issues: next, groups: [], version: 1 })
104
+ });
105
+ setState({ status: "ok", data: next });
106
+ if (inputRef.current) inputRef.current.value = "";
107
+ };
108
+ const styles = {
109
+ wrap: { padding: 20, fontFamily: "system-ui", height: "100%", boxSizing: "border-box" },
110
+ muted: { color: "#9aa4b2", margin: "0 0 12px" },
111
+ row: { display: "flex", gap: 8, marginBottom: 12 },
112
+ input: { flex: 1, padding: 6, border: "1px solid #2a2f3a", borderRadius: 6, background: "#12161e", color: "#e6e8eb" },
113
+ item: { padding: "8px 10px", border: "1px solid #2a2f3a", borderRadius: 8, marginBottom: 6 }
114
+ };
115
+ return import_react.default.createElement(
116
+ "div",
117
+ { style: styles.wrap },
118
+ import_react.default.createElement("h2", { style: { margin: "0 0 8px" } }, "\u25C9 \u4E2A\u4EBA\u5DE5\u4F5C\u53F0 \xB7 v" + VERSION),
119
+ import_react.default.createElement("p", { style: styles.muted }, "DSH \u5185\u5D4C\u58F3\uFF08M0 \u9AA8\u67B6\uFF09\u2014\u2014 \u6570\u636E\u6D41\u9A8C\u8BC1\u3002"),
120
+ import_react.default.createElement(
121
+ "div",
122
+ { style: styles.row },
123
+ import_react.default.createElement("input", { ref: inputRef, type: "text", placeholder: "\u65B0\u589E\u4E00\u6761\u9700\u6C42/Bug\u2026", style: styles.input }),
124
+ import_react.default.createElement("button", { onClick: onAdd }, "\uFF0B \u65B0\u589E")
125
+ ),
126
+ state.status === "loading" ? import_react.default.createElement("p", {}, "\u52A0\u8F7D\u2026") : state.status === "error" ? import_react.default.createElement("p", { style: { color: "#e5484d" } }, "\u9519\u8BEF\uFF1A" + state.error) : Array.isArray(state.data) && state.data.length ? import_react.default.createElement("ul", { style: { listStyle: "none", padding: 0, margin: 0 } }, state.data.map((it) => import_react.default.createElement("li", { key: it.id, style: styles.item }, it.title))) : import_react.default.createElement("p", { style: { color: "#888" } }, t("empty")),
127
+ import_react.default.createElement(
128
+ "p",
129
+ { style: { color: "#666", marginTop: 16 } },
130
+ "[host] /api/saxon-workbench/meta\uFF08\u5065\u5EB7\uFF09\xB7 /api/saxon-workbench/data\uFF08\u9700\u6C42/Bug \u6301\u4E45\u5316\uFF09"
131
+ )
132
+ );
133
+ }
134
+ return module.exports; } });
package/lib/index.js ADDED
@@ -0,0 +1,65 @@
1
+ // src/index.js
2
+ import { dirname, resolve } from "node:path";
3
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
4
+ import { homedir } from "node:os";
5
+ var name = "dsh-saxon-workbench";
6
+ var inject = ["webServer"];
7
+ var VERSION = "0.1.0";
8
+ var HEALTH = "/api/saxon-workbench/meta";
9
+ var DATA = "/api/saxon-workbench/data";
10
+ function baseDshHome() {
11
+ const env = process.env.DSH_HOME;
12
+ return env && env.trim() ? resolve(env) : resolve(homedir(), ".dsh");
13
+ }
14
+ function dataFile() {
15
+ return resolve(baseDshHome(), "profiles", "desktop", "dsh-saxon-workbench-data.json");
16
+ }
17
+ function json(res, status, body) {
18
+ res.writeHead(status, { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" });
19
+ res.end(JSON.stringify(body));
20
+ }
21
+ async function readBody(req) {
22
+ const chunks = [];
23
+ for await (const chunk of req) chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
24
+ return Buffer.concat(chunks).toString("utf8");
25
+ }
26
+ function apply(ctx) {
27
+ const webServer = ctx.webServer;
28
+ if (!webServer) {
29
+ ctx.logger?.warn?.("[dsh-saxon-workbench] ctx.webServer \u4E0D\u53EF\u7528\uFF0C\u8DF3\u8FC7\u670D\u52A1\u7AEF\u8DEF\u7531");
30
+ return;
31
+ }
32
+ webServer.register({ kind: "exact", path: HEALTH, handler: (_req, res) => {
33
+ json(res, 200, { plugin: "dsh-saxon-workbench", version: VERSION, ok: true, file: dataFile() });
34
+ } });
35
+ webServer.register({ kind: "exact", path: DATA, handler: async (req, res) => {
36
+ try {
37
+ if (req.method === "GET") {
38
+ try {
39
+ const raw = await readFile(dataFile(), "utf8");
40
+ json(res, 200, JSON.parse(raw));
41
+ } catch {
42
+ json(res, 200, { issues: [], groups: [], version: 1 });
43
+ }
44
+ return;
45
+ }
46
+ if (req.method === "POST") {
47
+ const body = await readBody(req);
48
+ const parsed = JSON.parse(body || "{}");
49
+ await mkdir(dirname(dataFile()), { recursive: true });
50
+ await writeFile(dataFile(), JSON.stringify(parsed, null, 2), "utf8");
51
+ json(res, 200, { ok: true });
52
+ return;
53
+ }
54
+ res.writeHead(405);
55
+ res.end();
56
+ } catch (err) {
57
+ json(res, 500, { error: String(err) });
58
+ }
59
+ } });
60
+ }
61
+ export {
62
+ apply,
63
+ inject,
64
+ name
65
+ };
package/package.json CHANGED
@@ -1,6 +1,48 @@
1
1
  {
2
2
  "name": "dsh-saxon-workbench",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "description": "个人工作台 Personal Workbench —— DSH 桌面端侧边栏入口的内嵌壳(双形态之一)",
4
+ "version": "0.1.0",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "exports": {
8
+ ".": "./lib/index.js",
9
+ "./client": "./lib/client.js",
10
+ "./package.json": "./package.json"
11
+ },
12
+ "files": [
13
+ "lib/index.js",
14
+ "lib/client.js",
15
+ "cordis.patch.yml",
16
+ "dsh.plugin.json",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "dsh": {
21
+ "bundle": { "patch": "./cordis.patch.yml" },
22
+ "client": {
23
+ "platform": "web",
24
+ "inject": [
25
+ "@deepseek-ai/dsh-client-ui-slots",
26
+ "@deepseek-ai/dsh-client-locale"
27
+ ]
28
+ },
29
+ "compatibility": { "dsh": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.2.0-rc.2" }
30
+ },
31
+ "scripts": {
32
+ "build": "node build.mjs"
33
+ },
34
+ "peerDependencies": {
35
+ "@deepseek-ai/cordis": "*",
36
+ "@deepseek-ai/dsh-host-webserver": "*",
37
+ "@deepseek-ai/dsh-client-locale": "*"
38
+ },
39
+ "peerDependenciesMeta": {
40
+ "@deepseek-ai/cordis": { "optional": true },
41
+ "@deepseek-ai/dsh-host-webserver": { "optional": true },
42
+ "@deepseek-ai/dsh-client-locale": { "optional": true }
43
+ },
44
+ "devDependencies": {
45
+ "esbuild": "^0.25.0"
46
+ },
47
+ "license": "MIT"
48
+ }