dsh-workflow-map 0.0.0-stage → 0.3.4
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 +184 -0
- package/LICENSE +21 -0
- package/README.en.md +458 -0
- package/README.md +436 -2
- package/client.js +9685 -0
- package/cordis.patch.yml +11 -0
- package/docs/OPEN-SOURCE-GUIDE.md +256 -0
- package/docs/host-compatibility.md +320 -0
- package/docs/screenshot-detail.png +0 -0
- package/docs/screenshot.png +0 -0
- package/icon.svg +6 -0
- package/index.js +12 -0
- package/lib/index.js +974 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +81 -4
- package/src/client/Branches.js +241 -0
- package/src/client/FlowColumn.js +572 -0
- package/src/client/FlowEffects.js +677 -0
- package/src/client/HotInject.js +628 -0
- package/src/client/MainFlow.js +359 -0
- package/src/client/NodeCard.js +545 -0
- package/src/client/NodeDetail.js +569 -0
- package/src/client/ProgressUnder.js +216 -0
- package/src/client/RightPage.js +285 -0
- package/src/client/Settings.js +212 -0
- package/src/client/StepColumn.js +479 -0
- package/src/client/StepDetail.js +250 -0
- package/src/client/export.js +280 -0
- package/src/client/panel.js +1911 -0
- package/src/client/pulse.js +126 -0
- package/src/client/store.js +1090 -0
- package/src/client/theme.js +1176 -0
- package/src/fold.mjs +2763 -0
- package/src/orchestrator.mjs +444 -0
- package/src/route.js +621 -0
package/locale/en.json
ADDED
package/locale/zh.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,83 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-workflow-map",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.3.4",
|
|
4
|
+
"description": "把当前会话渲染成一张可交互的分步工作流地图:会话标签栏、左栏菜单与主内容区共用同一份面板,逐步显示状态、工具与文件类型明细、进度条,并支持热注入(steer)与「需确认」标注。宿主半折叠会话事件、读取按步执行(PAE)计划,经本地 HTTP 视图供数据;浏览器半渲染面板。",
|
|
5
|
+
"private": false,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"deepseek-harness",
|
|
10
|
+
"deepseek-harness-plugin",
|
|
11
|
+
"dsh",
|
|
12
|
+
"dsh-0.2.1",
|
|
13
|
+
"cordis",
|
|
14
|
+
"plugin",
|
|
15
|
+
"bundle",
|
|
16
|
+
"workflow",
|
|
17
|
+
"progress",
|
|
18
|
+
"ui"
|
|
19
|
+
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/Joz739/dsh-workflow-map.git"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/Joz739/dsh-workflow-map#readme",
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/Joz739/dsh-workflow-map/issues"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=20",
|
|
30
|
+
"dsh": ">=0.2.1-rc.1 <0.3.0-0"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@deepseek-ai/dsh-agent-loop": ">=0.2.1-rc.1 <0.3.0-0",
|
|
34
|
+
"@deepseek-ai/dsh-llm": ">=0.2.1-rc.1 <0.3.0-0"
|
|
35
|
+
},
|
|
36
|
+
"peerDependenciesMeta": {
|
|
37
|
+
"@deepseek-ai/dsh-agent-loop": {
|
|
38
|
+
"optional": true
|
|
39
|
+
},
|
|
40
|
+
"@deepseek-ai/dsh-llm": {
|
|
41
|
+
"optional": true
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"exports": {
|
|
45
|
+
".": "./index.js",
|
|
46
|
+
"./client": "./client.js",
|
|
47
|
+
"./locale/*.json": "./locale/*.json",
|
|
48
|
+
"./icon": "./icon.svg"
|
|
49
|
+
},
|
|
50
|
+
"files": [
|
|
51
|
+
"index.js",
|
|
52
|
+
"lib",
|
|
53
|
+
"src",
|
|
54
|
+
"client.js",
|
|
55
|
+
"cordis.patch.yml",
|
|
56
|
+
"locale/*.json",
|
|
57
|
+
"icon.svg",
|
|
58
|
+
"README.md",
|
|
59
|
+
"README.en.md",
|
|
60
|
+
"CHANGELOG.md",
|
|
61
|
+
"LICENSE",
|
|
62
|
+
"docs/OPEN-SOURCE-GUIDE.md",
|
|
63
|
+
"docs/host-compatibility.md",
|
|
64
|
+
"docs/screenshot.png",
|
|
65
|
+
"docs/screenshot-detail.png",
|
|
66
|
+
"package.json"
|
|
67
|
+
],
|
|
68
|
+
"scripts": {
|
|
69
|
+
"build": "node tools/build-client.mjs",
|
|
70
|
+
"test": "node tools/selftest-fold.mjs && node tools/selftest-host.mjs && node tools/selftest-orchestrator.mjs && node tools/selftest-shape.mjs && node tools/selftest-panel.mjs && node tools/selftest-detail.mjs && node tools/selftest-effects.mjs && node tools/selftest-settings.mjs && node tools/probe-route.mjs && node tools/probe-steer.mjs",
|
|
71
|
+
"test:real": "node tools/check-real-injects.mjs"
|
|
72
|
+
},
|
|
73
|
+
"dsh": {
|
|
74
|
+
"bundle": {
|
|
75
|
+
"patch": "./cordis.patch.yml"
|
|
76
|
+
},
|
|
77
|
+
"client": {
|
|
78
|
+
"platform": "web",
|
|
79
|
+
"immediately": true,
|
|
80
|
+
"inject": []
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Branches.js — the parallel lanes beside a step's flow.
|
|
3
|
+
*
|
|
4
|
+
* What a lane is: a subagent, a workflow run or a background job started by this plan
|
|
5
|
+
* step. Its activity runs IN PARALLEL with the step, so it is drawn as a branch off the
|
|
6
|
+
* main flow track instead of being mixed into the node list:
|
|
7
|
+
*
|
|
8
|
+
* main track (FlowColumn) lane (this file)
|
|
9
|
+
* ─ ● read · a.js ┌ ⧉ subagent · review the fold module ●
|
|
10
|
+
* ─ ● edit · b.js ──────6px─┤ ▸ subagent started 21:03:07
|
|
11
|
+
* ─ ● command · npm test │ ▪ done 21:04:12
|
|
12
|
+
* └ (click the head to fold the lane)
|
|
13
|
+
*
|
|
14
|
+
* The lane data comes from `view.branches` (src/fold.mjs): branches NEVER enter
|
|
15
|
+
* `state.nodes`, so the main flow's node count, its same-tool groups, both caps and the
|
|
16
|
+
* "current activity" highlight are untouched by them. A lane therefore has its OWN status
|
|
17
|
+
* dot — a running lane breathes, it does not flash as the session's current node.
|
|
18
|
+
*
|
|
19
|
+
* Pure: `h` and `say` are injected, no React import, no DOM. Inlined into client.js by
|
|
20
|
+
* tools/build-client.mjs.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* One hue per lane (index into this list), so two adjacent lanes never read as one.
|
|
25
|
+
* Deliberately not kind-based: the badge text already says subagent / workflow / background job.
|
|
26
|
+
*/
|
|
27
|
+
var WF_BRANCH_HUES = [268, 196, 152, 32, 340, 224];
|
|
28
|
+
|
|
29
|
+
/** Glyph per lane row kind (the row kinds fold.mjs writes). */
|
|
30
|
+
var WF_BRANCH_ROW_GLYPH = {
|
|
31
|
+
start: "▸",
|
|
32
|
+
end: "▪",
|
|
33
|
+
phase: "◈",
|
|
34
|
+
agent: "⧉",
|
|
35
|
+
job: "⏱",
|
|
36
|
+
tool: "⚙",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/** Lane ordinal → hue. Pure and total: any index, including a negative or missing one. */
|
|
40
|
+
function wfBranchHue(index) {
|
|
41
|
+
var value = wfFinite(index);
|
|
42
|
+
var slot = value === null ? 0 : Math.abs(Math.round(value)) % WF_BRANCH_HUES.length;
|
|
43
|
+
return WF_BRANCH_HUES[slot];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Lane status → the four-state vocabulary the rest of the panel uses. */
|
|
47
|
+
function wfBranchStatus(branch) {
|
|
48
|
+
var status = branch && typeof branch.status === "string" ? branch.status : "running";
|
|
49
|
+
if (status === "failed") return "error";
|
|
50
|
+
if (status === "done") return "done";
|
|
51
|
+
return "running";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Lane kind -> the locale key of its badge (subagent / workflow / background job). */
|
|
55
|
+
function wfBranchBadgeKey(kind) {
|
|
56
|
+
if (kind === "workflow") return "panel.branch.workflow";
|
|
57
|
+
if (kind === "job") return "panel.branch.job";
|
|
58
|
+
return "panel.branch.subagent";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Lane row status → dot state. A phase heading has no dot at all (it is not work). */
|
|
62
|
+
function wfBranchRowStatus(node) {
|
|
63
|
+
var status = node && typeof node.status === "string" ? node.status : "";
|
|
64
|
+
if (status === "failed") return "error";
|
|
65
|
+
if (status === "done") return "done";
|
|
66
|
+
if (status === "running") return "running";
|
|
67
|
+
return "pending";
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Is the lane folded? The per-step UI state keeps `closedBranches` (lanes open by
|
|
72
|
+
* default — the point of a lane is to show the parallel work), so this is a pure read.
|
|
73
|
+
*/
|
|
74
|
+
function wfBranchIsCollapsed(state, branchId) {
|
|
75
|
+
if (!state || !Array.isArray(state.closedBranches) || typeof branchId !== "string") return false;
|
|
76
|
+
return state.closedBranches.indexOf(branchId) >= 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* One activity row. It is a BUTTON because mounts the same detail view for any
|
|
81
|
+
* row; until then the click only reports the id through `actions.onSelectNode`.
|
|
82
|
+
*/
|
|
83
|
+
function wfRenderBranchRow(h, node, say, actions, ctx) {
|
|
84
|
+
var fire = actions || {};
|
|
85
|
+
var context = ctx || {};
|
|
86
|
+
var status = wfBranchRowStatus(node);
|
|
87
|
+
var selected = context.selectedNodeId !== null && context.selectedNodeId === node.id;
|
|
88
|
+
var detail = node && typeof node.detail === "string" ? node.detail : "";
|
|
89
|
+
|
|
90
|
+
return h(
|
|
91
|
+
"li",
|
|
92
|
+
{
|
|
93
|
+
className: "wf-branch-row",
|
|
94
|
+
key: "row-" + node.id,
|
|
95
|
+
"data-status": status,
|
|
96
|
+
"data-node-id": node.id,
|
|
97
|
+
},
|
|
98
|
+
h(
|
|
99
|
+
"button",
|
|
100
|
+
{
|
|
101
|
+
type: "button",
|
|
102
|
+
className: "wf-branch-node",
|
|
103
|
+
"data-dsh-workflow-map": "branch-node",
|
|
104
|
+
"data-node-id": node.id,
|
|
105
|
+
"data-kind": node.kind,
|
|
106
|
+
"data-status": status,
|
|
107
|
+
"data-selected": selected ? "true" : "false",
|
|
108
|
+
"aria-pressed": selected ? "true" : "false",
|
|
109
|
+
title: detail === "" ? node.title : node.title + " — " + detail,
|
|
110
|
+
onClick: function () {
|
|
111
|
+
if (typeof fire.onSelectNode === "function") fire.onSelectNode(node.id);
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
h("span", { className: "wf-branch-glyph", key: "glyph", "aria-hidden": "true" }, WF_BRANCH_ROW_GLYPH[node.kind] || "·"),
|
|
115
|
+
h(
|
|
116
|
+
"span",
|
|
117
|
+
{ className: "wf-branch-row-main", key: "main" },
|
|
118
|
+
h("span", { className: "wf-branch-row-title", key: "title" }, node.title || "—"),
|
|
119
|
+
detail === "" ? null : h("span", { className: "wf-branch-row-detail", key: "detail" }, detail),
|
|
120
|
+
),
|
|
121
|
+
h("span", { className: "wf-branch-clock", key: "clock" }, wfFormatClock(node.at)),
|
|
122
|
+
h("span", { className: "wf-flow-status", key: "dot", "data-status": status, "aria-hidden": "true" }),
|
|
123
|
+
),
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* One lane: the coloured 6 px connector from the main track, the head (badge · label ·
|
|
129
|
+
* count · status dot · caret) and the body. Folding is per lane and remembered for the
|
|
130
|
+
* SELECTED step only (see wfFlowUiFor), exactly like a folded group.
|
|
131
|
+
*/
|
|
132
|
+
function wfRenderBranch(h, branch, index, flowState, say, actions, ctx) {
|
|
133
|
+
var fire = actions || {};
|
|
134
|
+
var hue = wfBranchHue(index);
|
|
135
|
+
var status = wfBranchStatus(branch);
|
|
136
|
+
var collapsed = wfBranchIsCollapsed(flowState, branch.id);
|
|
137
|
+
var nodes = Array.isArray(branch.nodes) ? branch.nodes : [];
|
|
138
|
+
var count = wfFinite(branch.count) === null ? nodes.length : branch.count;
|
|
139
|
+
var truncated = wfFinite(branch.truncated) === null ? Math.max(0, count - nodes.length) : branch.truncated;
|
|
140
|
+
var label = typeof branch.label === "string" && branch.label !== "" ? branch.label : "—";
|
|
141
|
+
|
|
142
|
+
var rows = [];
|
|
143
|
+
if (!collapsed) {
|
|
144
|
+
if (truncated > 0) {
|
|
145
|
+
rows.push(h("li", { className: "wf-branch-trunc", key: "trunc" }, say("panel.branch.truncated", { count: truncated })));
|
|
146
|
+
}
|
|
147
|
+
for (var i = 0; i < nodes.length; i += 1) {
|
|
148
|
+
var node = nodes[i];
|
|
149
|
+
if (node.kind === "phase") {
|
|
150
|
+
rows.push(h("li", { className: "wf-branch-phase", key: "phase-" + node.id }, node.title));
|
|
151
|
+
} else {
|
|
152
|
+
rows.push(wfRenderBranchRow(h, node, say, actions, ctx));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return h(
|
|
158
|
+
"div",
|
|
159
|
+
{
|
|
160
|
+
className: "wf-branch",
|
|
161
|
+
key: "branch-" + branch.id,
|
|
162
|
+
// Concrete colours, not a CSS colour function: the panel must not depend on the
|
|
163
|
+
// Electron build supporting the space-separated hsl syntax.
|
|
164
|
+
style: {
|
|
165
|
+
"--wf-branch-h": String(hue),
|
|
166
|
+
"--wf-branch-line": `hsl(${hue}, 78%, 62%)`,
|
|
167
|
+
"--wf-branch-dim": `hsla(${hue}, 70%, 50%, .5)`,
|
|
168
|
+
"--wf-branch-text": `hsl(${hue}, 74%, 74%)`,
|
|
169
|
+
},
|
|
170
|
+
"data-dsh-workflow-map": "branch",
|
|
171
|
+
"data-branch-id": branch.id,
|
|
172
|
+
"data-kind": branch.kind,
|
|
173
|
+
"data-status": status,
|
|
174
|
+
"data-expanded": collapsed ? "false" : "true",
|
|
175
|
+
"data-hue": hue,
|
|
176
|
+
},
|
|
177
|
+
h("span", { className: "wf-branch-link", key: "link", "aria-hidden": "true" }),
|
|
178
|
+
h(
|
|
179
|
+
"div",
|
|
180
|
+
{ className: "wf-branch-card", key: "card" },
|
|
181
|
+
h(
|
|
182
|
+
"button",
|
|
183
|
+
{
|
|
184
|
+
type: "button",
|
|
185
|
+
className: "wf-branch-head",
|
|
186
|
+
"data-dsh-workflow-map": "branch-head",
|
|
187
|
+
"data-branch-id": branch.id,
|
|
188
|
+
"aria-expanded": collapsed ? "false" : "true",
|
|
189
|
+
// Mirrored on the card (data-expanded) as well, so a test can read either.
|
|
190
|
+
title: say(collapsed ? "panel.branch.expand" : "panel.branch.collapse"),
|
|
191
|
+
onClick: function () {
|
|
192
|
+
if (typeof fire.onToggleBranch === "function") fire.onToggleBranch(branch.id);
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
h("span", { className: "wf-branch-badge", key: "badge" }, say(wfBranchBadgeKey(branch.kind))),
|
|
196
|
+
h("span", { className: "wf-branch-label", key: "label" }, label),
|
|
197
|
+
h("span", { className: "wf-branch-meta", key: "meta" }, say("panel.branch.nodes", { count: count })),
|
|
198
|
+
h("span", { className: "wf-branch-dot", key: "dot", "data-status": status, "aria-hidden": "true" }),
|
|
199
|
+
h("span", { className: "wf-branch-caret", key: "caret", "aria-hidden": "true" }, "▶"),
|
|
200
|
+
),
|
|
201
|
+
collapsed ? null : h("ul", { className: "wf-branch-body", key: "body", role: "list" }, rows),
|
|
202
|
+
),
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* The whole branch area for the selected step, or NULL when nothing runs in parallel —
|
|
208
|
+
* the acceptance asks for NO shell at all in a session without subagents, not an empty
|
|
209
|
+
* placeholder (an empty box would read as "a branch exists but is empty").
|
|
210
|
+
*/
|
|
211
|
+
function wfRenderBranches(h, model, say, actions, flowState) {
|
|
212
|
+
var flow = model && model.flow && typeof model.flow === "object" ? model.flow : null;
|
|
213
|
+
var branches = flow && Array.isArray(flow.branches) ? flow.branches : [];
|
|
214
|
+
if (branches.length === 0) return null;
|
|
215
|
+
|
|
216
|
+
var hidden = flow && wfFinite(flow.hiddenBranches) !== null ? Math.max(0, flow.hiddenBranches) : 0;
|
|
217
|
+
var dropped = flow && wfFinite(flow.branchDropped) !== null ? Math.max(0, flow.branchDropped) : 0;
|
|
218
|
+
var ctx = {
|
|
219
|
+
selectedNodeId:
|
|
220
|
+
flowState && typeof flowState.selectedNode === "string" && flowState.selectedNode !== "" ? flowState.selectedNode : null,
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
var notes = [];
|
|
224
|
+
if (hidden > 0) notes.push(say("panel.branch.more", { count: hidden }));
|
|
225
|
+
if (dropped > 0) notes.push(say("panel.branch.dropped", { count: dropped }));
|
|
226
|
+
|
|
227
|
+
return h(
|
|
228
|
+
"div",
|
|
229
|
+
{ className: "wf-branches", key: "branches", "data-dsh-workflow-map": "branches", "data-branch-count": branches.length },
|
|
230
|
+
h(
|
|
231
|
+
"div",
|
|
232
|
+
{ className: "wf-branches-h", key: "head" },
|
|
233
|
+
h("span", { key: "label" }, say("panel.branch")),
|
|
234
|
+
h("span", { className: "wf-count", key: "count" }, String(branches.length)),
|
|
235
|
+
),
|
|
236
|
+
branches.map(function (branch, index) {
|
|
237
|
+
return wfRenderBranch(h, branch, index, flowState, say, actions, ctx);
|
|
238
|
+
}),
|
|
239
|
+
notes.length === 0 ? null : h("div", { className: "wf-branch-more", key: "more" }, notes.join(" · ")),
|
|
240
|
+
);
|
|
241
|
+
}
|