dsh-pictor 0.2.2 → 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.
- package/README.md +1 -1
- package/lib/client.js +31 -13
- package/lib/index.js +29 -13
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ Replace `<profile>` with the target profile name (e.g. `desktop`, `web`) and sta
|
|
|
22
22
|
|
|
23
23
|
## Supported dsh version
|
|
24
24
|
|
|
25
|
-
Pictor is built and tested against **DSH Desktop 0.
|
|
25
|
+
Pictor is built and tested against **DSH Desktop 0.9.0** (bundled Harness **0.1.5-rc.2**). The DeepSeek Harness client API moves quickly — the workspace/session services it exposes keep changing — so this plugin intentionally tracks one harness generation. Other versions (older or newer) may work or may break without notice, and are outside our support scope. If something stops working after a DSH update, update dsh to the version above rather than debugging the mismatch.
|
|
26
26
|
|
|
27
27
|
## Usage
|
|
28
28
|
|
package/lib/client.js
CHANGED
|
@@ -398,9 +398,14 @@ function t(key, vars) {
|
|
|
398
398
|
|
|
399
399
|
// ---------- 工具 ----------
|
|
400
400
|
|
|
401
|
+
// RPC 走 webServer 的 /pictor/rpc/* prefix 路由(同源 HTTP,不再经 connection.rpc.call)。
|
|
401
402
|
function rpc(ctx, endpoint, payload) {
|
|
402
403
|
// 服务端 schema 要求 payload 字段必须存在(对象);缺省补 {},否则序列化丢键被拒。
|
|
403
|
-
return
|
|
404
|
+
return fetch(RPC + '/rpc/' + encodeURIComponent(endpoint), {
|
|
405
|
+
method: 'POST',
|
|
406
|
+
headers: { 'content-type': 'application/json' },
|
|
407
|
+
body: JSON.stringify(payload === undefined ? {} : payload),
|
|
408
|
+
}).then((r) => r.json()).then((result) => {
|
|
404
409
|
if (result && result.ok === false) {
|
|
405
410
|
const msg = result.error && result.error.message ? result.error.message : String(result.error || 'RPC 调用失败')
|
|
406
411
|
throw new Error(msg)
|
|
@@ -490,14 +495,27 @@ function sanitizePaste(html) {
|
|
|
490
495
|
// 在就调哪个,老版本前端不受影响。
|
|
491
496
|
function sessionConnectFn(ctx, ws) {
|
|
492
497
|
let uiWs = null
|
|
493
|
-
try { uiWs =
|
|
498
|
+
try { uiWs = (ctx.get && ctx.get('uiWorkspace')) || null } catch { /* ignore */ }
|
|
494
499
|
return (uiWs && typeof uiWs.connectWorkspace === 'function')
|
|
495
500
|
? uiWs.connectWorkspace.bind(uiWs)
|
|
496
501
|
: (ws && typeof ws.connectWorkspace === 'function') ? ws.connectWorkspace.bind(ws) : null
|
|
497
502
|
}
|
|
498
503
|
|
|
504
|
+
/**
|
|
505
|
+
* 从 workspaces.connectWorkspace / sessions.create 的返回里取 sessionId。
|
|
506
|
+
* 0.1.5 起 create/connectWorkspace 返回 {ok, value:{sessionId}} 信封;
|
|
507
|
+
* 早版本直接返回 id 字符串或 {id}。这里全兼容。
|
|
508
|
+
*/
|
|
509
|
+
function extractSid(created) {
|
|
510
|
+
if (!created) return null
|
|
511
|
+
if (typeof created === 'string') return created
|
|
512
|
+
return created.sessionId || created.id
|
|
513
|
+
|| (created.value && (created.value.sessionId || created.value.id))
|
|
514
|
+
|| null
|
|
515
|
+
}
|
|
516
|
+
|
|
499
517
|
async function pictorWorkspace(ctx) {
|
|
500
|
-
const ws =
|
|
518
|
+
const ws = (ctx.get && ctx.get('workspaces'))
|
|
501
519
|
const base = await rpc(ctx, 'config.get').then((c) => (c && c.dataRoot) || '')
|
|
502
520
|
// 这里只用 list/create/delete/rename,新旧 services 都有;connectWorkspace
|
|
503
521
|
// 的可用性由 driveProjectSession 检查。
|
|
@@ -540,13 +558,13 @@ async function pictorWorkspace(ctx) {
|
|
|
540
558
|
}
|
|
541
559
|
|
|
542
560
|
async function driveProjectSession(ctx, prompt) {
|
|
543
|
-
const ws =
|
|
544
|
-
const sessionsSvc =
|
|
561
|
+
const ws = (ctx.get && ctx.get('workspaces'))
|
|
562
|
+
const sessionsSvc = (ctx.get && ctx.get('sessions'))
|
|
545
563
|
const connect = sessionConnectFn(ctx, ws)
|
|
546
564
|
const canCreate = !!(sessionsSvc && typeof sessionsSvc.create === 'function')
|
|
547
565
|
if (!(connect || canCreate) || !(sessionsSvc && typeof sessionsSvc.binding === 'function')) {
|
|
548
566
|
let uiWs = null
|
|
549
|
-
try { uiWs =
|
|
567
|
+
try { uiWs = (ctx.get && ctx.get('uiWorkspace')) || null } catch { /* ignore */ }
|
|
550
568
|
const diag = { ws: !!ws, ses: !!sessionsSvc && typeof sessionsSvc.binding === 'function', uiws: !!uiWs, conn: !!connect, create: canCreate }
|
|
551
569
|
console.warn('[pictor] session services unavailable', diag)
|
|
552
570
|
try { window.__pictor_diag = diag } catch { /* ignore */ }
|
|
@@ -557,7 +575,7 @@ async function driveProjectSession(ctx, prompt) {
|
|
|
557
575
|
let sessionId
|
|
558
576
|
try {
|
|
559
577
|
const created = connect ? await connect(wid) : await sessionsSvc.create({ workspaceId: wid })
|
|
560
|
-
sessionId = created
|
|
578
|
+
sessionId = extractSid(created)
|
|
561
579
|
}
|
|
562
580
|
catch (e) { throw new Error('创建会话失败:' + String((e && e.message) || e)) }
|
|
563
581
|
const bound = sessionsSvc && typeof sessionsSvc.binding === 'function' ? sessionsSvc.binding(sessionId) : null
|
|
@@ -573,7 +591,7 @@ async function driveProjectSession(ctx, prompt) {
|
|
|
573
591
|
async function drivePrompt(ctx, id, message) {
|
|
574
592
|
const p = await rpc(ctx, 'project.prompt', { id, message })
|
|
575
593
|
if (p && p.sessionId) {
|
|
576
|
-
const sessionsSvc =
|
|
594
|
+
const sessionsSvc = (ctx.get && ctx.get('sessions'))
|
|
577
595
|
if (sessionsSvc && typeof sessionsSvc.binding === 'function') {
|
|
578
596
|
try {
|
|
579
597
|
const bound = sessionsSvc.binding(p.sessionId)
|
|
@@ -1710,9 +1728,9 @@ function selectedLabel(ids) {
|
|
|
1710
1728
|
// 调整了客户端 API),host 落盘 ~/.pictor/diag.jsonl,无需 DevTools 即可排查。
|
|
1711
1729
|
function pictorDiag(ctx, phase) {
|
|
1712
1730
|
let ws = null, ses = null, ui = null
|
|
1713
|
-
try { ws = ctx && (ctx.
|
|
1714
|
-
try { ses = ctx && (ctx.
|
|
1715
|
-
try { ui = ctx && (ctx.
|
|
1731
|
+
try { ws = ctx && (ctx.get && ctx.get('workspaces')) } catch { /* ignore */ }
|
|
1732
|
+
try { ses = ctx && (ctx.get && ctx.get('sessions')) } catch { /* ignore */ }
|
|
1733
|
+
try { ui = ctx && (ctx.get && ctx.get('uiWorkspace')) } catch { /* ignore */ }
|
|
1716
1734
|
const body = {
|
|
1717
1735
|
phase,
|
|
1718
1736
|
ws: !!ws, ses: !!ses, ui: !!ui,
|
|
@@ -1730,7 +1748,7 @@ function pictorDiag(ctx, phase) {
|
|
|
1730
1748
|
|
|
1731
1749
|
function apply(ctx) {
|
|
1732
1750
|
try { pictorDiag(ctx, 'apply') } catch { /* ignore */ }
|
|
1733
|
-
console.info('[dsh-pictor] apply 被调用', { hasSlots: Boolean(ctx && ctx.slots) })
|
|
1751
|
+
console.info('[dsh-pictor] apply 被调用', { hasSlots: Boolean(ctx.get && ctx.get('slots')) })
|
|
1734
1752
|
if (typeof document !== 'undefined') {
|
|
1735
1753
|
const styleId = 'dsh-pictor-style'
|
|
1736
1754
|
if (!document.getElementById(styleId)) {
|
|
@@ -1740,7 +1758,7 @@ function apply(ctx) {
|
|
|
1740
1758
|
document.head.appendChild(style)
|
|
1741
1759
|
}
|
|
1742
1760
|
}
|
|
1743
|
-
const slots =
|
|
1761
|
+
const slots = (ctx.get && ctx.get('slots'))
|
|
1744
1762
|
if (!slots) return
|
|
1745
1763
|
|
|
1746
1764
|
const panel = { open: false, subs: new Set() }
|
package/lib/index.js
CHANGED
|
@@ -106,7 +106,7 @@ function initialPrompt(dir) {
|
|
|
106
106
|
return prompts.join("\n");
|
|
107
107
|
}
|
|
108
108
|
function apply(ctx, config = {}) {
|
|
109
|
-
const connection = ctx.connection;
|
|
109
|
+
const connection = ctx.get?.("connection");
|
|
110
110
|
const base = config.dataDir || process.env.PICTOR_HOME || join(homedir(), ".pictor");
|
|
111
111
|
const INDEX_JSON = join(base, "index.json");
|
|
112
112
|
const CONFIG_JSON = join(base, "pictor-config.json");
|
|
@@ -171,7 +171,7 @@ function apply(ctx, config = {}) {
|
|
|
171
171
|
}
|
|
172
172
|
function credentialsService() {
|
|
173
173
|
try {
|
|
174
|
-
return ctx.
|
|
174
|
+
return ctx.get?.("credentials");
|
|
175
175
|
} catch {
|
|
176
176
|
return null;
|
|
177
177
|
}
|
|
@@ -676,20 +676,36 @@ function apply(ctx, config = {}) {
|
|
|
676
676
|
throw new Error(`dsh-pictor: \u672A\u77E5\u7AEF\u70B9 ${endpoint}`);
|
|
677
677
|
}
|
|
678
678
|
}
|
|
679
|
-
|
|
680
|
-
connection.rpc.handle("/pictor", async (endpoint, payload) => {
|
|
681
|
-
try {
|
|
682
|
-
return { ok: true, value: await handle(endpoint, payload || {}) };
|
|
683
|
-
} catch (e) {
|
|
684
|
-
logError(`rpc:${endpoint}`, e);
|
|
685
|
-
return { ok: false, error: { code: "internal", message: String(e?.message || e), details: {} } };
|
|
686
|
-
}
|
|
687
|
-
}, { authority: "loopback" });
|
|
688
|
-
}
|
|
689
|
-
const wsA = ctx.webServer || ctx.get?.("webServer");
|
|
679
|
+
const wsA = ctx.get?.("webServer");
|
|
690
680
|
if (wsA && typeof wsA.register === "function") {
|
|
691
681
|
const IMG_CACHE = { "content-type": "image/png", "cache-control": "public, max-age=3600" };
|
|
692
682
|
const WEBP_CACHE = { "content-type": "image/webp", "cache-control": "public, max-age=3600" };
|
|
683
|
+
const RPC_PREFIX = "/pictor/rpc";
|
|
684
|
+
wsA.register({ kind: "prefix", path: RPC_PREFIX, handler: async (req, res) => {
|
|
685
|
+
try {
|
|
686
|
+
const pathname = new URL(req.url || "/", "http://x").pathname;
|
|
687
|
+
const endpoint = decodeURIComponent(pathname.slice(RPC_PREFIX.length + 1));
|
|
688
|
+
let payload = {};
|
|
689
|
+
if (req.method === "POST" || req.method === "PUT") {
|
|
690
|
+
const chunks = [];
|
|
691
|
+
for await (const c of req) chunks.push(Buffer.isBuffer(c) ? c : Buffer.from(c));
|
|
692
|
+
const raw = Buffer.concat(chunks).toString("utf8");
|
|
693
|
+
if (raw) payload = JSON.parse(raw);
|
|
694
|
+
}
|
|
695
|
+
try {
|
|
696
|
+
const value = await handle(endpoint, payload || {});
|
|
697
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
698
|
+
res.end(JSON.stringify({ ok: true, value }));
|
|
699
|
+
} catch (e) {
|
|
700
|
+
logError(`rpc:${endpoint}`, e);
|
|
701
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
702
|
+
res.end(JSON.stringify({ ok: false, error: { code: "internal", message: String(e?.message || e), details: {} } }));
|
|
703
|
+
}
|
|
704
|
+
} catch {
|
|
705
|
+
res.writeHead(400);
|
|
706
|
+
res.end("bad request");
|
|
707
|
+
}
|
|
708
|
+
} });
|
|
693
709
|
wsA.register({ kind: "exact", path: "/pictor/asset/empty-state.png", handler: (_req, res) => {
|
|
694
710
|
res.writeHead(200, IMG_CACHE);
|
|
695
711
|
res.end(readFileSync(join(PLUGIN_ROOT, "assets", "empty-state.png")));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-pictor",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Pictor:文档转信息图工作台。每项目一个连贯 dsh 会话,GUI 为 shell.overlay 工作台(左栏项目、右栏三步:提取/方案/渲染),行为全部交给 DSH 智能运行时。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -30,10 +30,10 @@
|
|
|
30
30
|
},
|
|
31
31
|
"client": {
|
|
32
32
|
"inject": [
|
|
33
|
-
"@deepseek-ai/dsh-client-runtime",
|
|
34
33
|
"@deepseek-ai/dsh-client-ui-theme",
|
|
35
34
|
"@deepseek-ai/dsh-client-ui-conversation",
|
|
36
|
-
"@deepseek-ai/dsh-client-ui-settings"
|
|
35
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
36
|
+
"@deepseek-ai/dsh-client-ui-workspace"
|
|
37
37
|
],
|
|
38
38
|
"platform": "web"
|
|
39
39
|
}
|
|
@@ -65,10 +65,11 @@
|
|
|
65
65
|
"dsh-app-dock": "^0.1.2"
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
|
-
"@deepseek-ai/
|
|
68
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
69
69
|
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6",
|
|
70
70
|
"@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.6",
|
|
71
71
|
"@deepseek-ai/dsh-client-ui-theme": "^0.1.0-rc.6",
|
|
72
|
+
"@deepseek-ai/dsh-client-ui-workspace": "^0.1.0-rc.6",
|
|
72
73
|
"react": "^18.2.0",
|
|
73
74
|
"react-dom": "^18.2.0"
|
|
74
75
|
},
|
|
@@ -84,4 +85,4 @@
|
|
|
84
85
|
"@playwright/test": "^1.62.1",
|
|
85
86
|
"esbuild": "^0.28.2"
|
|
86
87
|
}
|
|
87
|
-
}
|
|
88
|
+
}
|