@sleep2agi/dsh-anet 0.1.0-preview.26 → 0.1.0-preview.28
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 +3 -3
- package/README.zh.md +2 -2
- package/package.json +1 -1
- package/src/client.js +54 -10
- package/src/plugin.mjs +25 -3
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ A [DSH (DeepSeek Harness)](https://www.npmjs.com/package/@deepseek-ai/dsh) plugi
|
|
|
13
13
|
## Install
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
dsh plugin --profile web add /
|
|
16
|
+
dsh plugin --profile web add @sleep2agi/dsh-anet
|
|
17
17
|
# restart DSH
|
|
18
18
|
```
|
|
19
19
|
|
|
@@ -29,8 +29,8 @@ DSH `0.1.0-rc.6` / `0.2.0-rc.2` are supported. On the desktop app, the same pack
|
|
|
29
29
|
is minted through the logged-in account and the node connects immediately.
|
|
30
30
|
4. Others dispatch tasks to that alias from the hub/dashboard. Each task runs in the node's DSH
|
|
31
31
|
session and the answer is sent back to the hub.
|
|
32
|
-
5. Multiple nodes can run together.
|
|
33
|
-
|
|
32
|
+
5. Multiple nodes can run together. On a node card you can switch **project** (a new session is created
|
|
33
|
+
for that project) and switch **session** at any time; **Delete** stops the node.
|
|
34
34
|
|
|
35
35
|
Config lives in `~/.anet/dsh/` (`nodes.json`, `account.json`, both 0600).
|
|
36
36
|
|
package/README.zh.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
## 安装
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
dsh plugin --profile web add /
|
|
16
|
+
dsh plugin --profile web add @sleep2agi/dsh-anet
|
|
17
17
|
# 重启 DSH
|
|
18
18
|
```
|
|
19
19
|
|
|
@@ -28,7 +28,7 @@ dsh plugin --profile web add /path/to/agent-network/integrations/dsh-commhub
|
|
|
28
28
|
3. 创建节点:节点名称(alias)、项目目录、Session(新建或选已有)。token 由当前登录账号
|
|
29
29
|
自动签发,节点立即上线。
|
|
30
30
|
4. 其他人在 hub/看板给该 alias 派任务;任务在节点的 DSH session 里执行,结果自动回 hub。
|
|
31
|
-
5.
|
|
31
|
+
5. 可同时运行多个节点;节点卡片里可随时切换 **项目**(会为该节点新建 session)和 **Session**,**删除** 即下线。
|
|
32
32
|
|
|
33
33
|
配置在 `~/.anet/dsh/`(`nodes.json`、`account.json`,均 0600)。
|
|
34
34
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sleep2agi/dsh-anet",
|
|
3
|
-
"version": "0.1.0-preview.
|
|
3
|
+
"version": "0.1.0-preview.28",
|
|
4
4
|
"description": "DSH (DeepSeek Harness) plugin: expose one or more DSH sessions as Agent Network (anet.sh / CommHub) nodes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/plugin.mjs",
|
package/src/client.js
CHANGED
|
@@ -22,10 +22,11 @@ window.__ModuleLoader__.load({
|
|
|
22
22
|
loggedIn: "已登录", createTitle: "创建节点", createHint: "节点 token 会由当前账号自动创建",
|
|
23
23
|
create: "创建节点", creating: "创建中…", created: "节点已创建并接入",
|
|
24
24
|
delete: "删除", confirmDelete: "删除这个节点?", switchSession: "切换会话", switched: "已切换会话",
|
|
25
|
+
switchProject: "切换项目", switchProjectHint: "切换项目会为该节点新建 session", projectSwitched: "已切换项目",
|
|
25
26
|
loading: "正在加载…", offline: "本地服务不可用(127.0.0.1:9460)", failed: "操作失败",
|
|
26
27
|
note: "配置保存在 ~/.anet/dsh/nodes.json(0600)",
|
|
27
28
|
running: "已连接", starting: "连接中", stopped: "未连接", error: "异常",
|
|
28
|
-
network: "Network", projectLabel: "项目", noNetwork: "默认网络",
|
|
29
|
+
network: "Network", projectLabel: "项目", noNetwork: "默认网络", unnamedSession: "未命名会话",
|
|
29
30
|
};
|
|
30
31
|
const en = {
|
|
31
32
|
title: "ANet", subtitle: "Turn a project/session into an Agent Network node",
|
|
@@ -38,10 +39,11 @@ window.__ModuleLoader__.load({
|
|
|
38
39
|
loggedIn: "Logged in", createTitle: "Create node", createHint: "The node token is minted with the current account",
|
|
39
40
|
create: "Create node", creating: "Creating…", created: "Node created and connected",
|
|
40
41
|
delete: "Delete", confirmDelete: "Delete this node?", switchSession: "Switch session", switched: "Session switched",
|
|
42
|
+
switchProject: "Switch project", switchProjectHint: "Switching project creates a new session for this node", projectSwitched: "Project switched",
|
|
41
43
|
loading: "Loading…", offline: "Local service unavailable (127.0.0.1:9460)", failed: "Failed",
|
|
42
44
|
note: "Stored in ~/.anet/dsh/nodes.json (0600)",
|
|
43
45
|
running: "Connected", starting: "Connecting", stopped: "Disconnected", error: "Error",
|
|
44
|
-
network: "Network", projectLabel: "Project", noNetwork: "default network",
|
|
46
|
+
network: "Network", projectLabel: "Project", noNetwork: "default network", unnamedSession: "Untitled session",
|
|
45
47
|
};
|
|
46
48
|
|
|
47
49
|
const T = {
|
|
@@ -70,6 +72,22 @@ window.__ModuleLoader__.load({
|
|
|
70
72
|
const sectionHint = { margin: "4px 0 12px", fontSize: 12, color: T.muted, lineHeight: 1.5 };
|
|
71
73
|
const grid2 = { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 };
|
|
72
74
|
|
|
75
|
+
function baseName(path) {
|
|
76
|
+
if (!path) return "";
|
|
77
|
+
const parts = String(path).split("/").filter(Boolean);
|
|
78
|
+
return parts.length ? parts[parts.length - 1] : path;
|
|
79
|
+
}
|
|
80
|
+
function sessionWhen(ms) {
|
|
81
|
+
if (!ms) return "";
|
|
82
|
+
const date = new Date(ms);
|
|
83
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
84
|
+
return `${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
|
85
|
+
}
|
|
86
|
+
function sessionLabel(t, session) {
|
|
87
|
+
const title = session.title || t("unnamedSession");
|
|
88
|
+
return `${title} · ${sessionWhen(session.createdAt)} · ${baseName(session.cwd)}`;
|
|
89
|
+
}
|
|
90
|
+
|
|
73
91
|
function Field(props) {
|
|
74
92
|
return h("div", { style: props.style ?? null },
|
|
75
93
|
h("label", { style: labelStyle, htmlFor: props.id }, props.label),
|
|
@@ -97,20 +115,30 @@ window.__ModuleLoader__.load({
|
|
|
97
115
|
}
|
|
98
116
|
|
|
99
117
|
function NodeRow(props) {
|
|
100
|
-
const { node, t, onDelete, sessions, onSwitch } = props;
|
|
118
|
+
const { node, t, onDelete, sessions, onSwitch, projects, onSwitchProject } = props;
|
|
101
119
|
return h("div", { style: { ...cardStyle, padding: "14px 16px", marginBottom: 10 } },
|
|
102
120
|
h("div", { style: { display: "flex", alignItems: "center", gap: 10 } },
|
|
103
121
|
h("div", { style: { fontSize: 14, fontWeight: 650, color: T.text, flex: 1, minWidth: 0, overflowWrap: "anywhere" } }, node.name),
|
|
104
122
|
h(StatusPill, { status: node.status, t }),
|
|
105
123
|
h("button", { type: "button", style: ghostButton(false), onClick: () => onDelete(node) }, t("delete")),
|
|
106
124
|
),
|
|
107
|
-
h("div", { style: { display: "grid", gridTemplateColumns: "1.2fr 1fr
|
|
125
|
+
h("div", { style: { display: "grid", gridTemplateColumns: "1.2fr 1fr", gap: 12, marginTop: 10 } },
|
|
108
126
|
h(Meta, { label: "Hub", value: node.hub }),
|
|
109
127
|
h(Meta, { label: "NETWORK", value: node.networkId || t("noNetwork") }),
|
|
110
|
-
h(Meta, { label: "PROJECT", value: node.projectDir }),
|
|
111
128
|
),
|
|
112
129
|
h("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 10, paddingTop: 10, borderTop: `1px solid ${T.border}` } },
|
|
113
|
-
h("span", { style: { fontSize: 11.5, color: T.muted, flex: "none" } }, t("
|
|
130
|
+
h("span", { style: { fontSize: 11.5, color: T.muted, flex: "none", width: 44 } }, t("projectLabel")),
|
|
131
|
+
h("select", {
|
|
132
|
+
value: node.projectDir,
|
|
133
|
+
onChange: (event) => onSwitchProject(node, event.target.value),
|
|
134
|
+
style: { ...inputStyle, height: 30, padding: "0 8px", fontSize: 12, flex: 1, minWidth: 0 },
|
|
135
|
+
},
|
|
136
|
+
(projects ?? []).some((project) => project.path === node.projectDir) ? null : h("option", { value: node.projectDir }, node.projectDir),
|
|
137
|
+
(projects ?? []).map((project) => h("option", { key: project.path, value: project.path }, `${project.label} — ${project.path}`)),
|
|
138
|
+
),
|
|
139
|
+
),
|
|
140
|
+
h("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 8 } },
|
|
141
|
+
h("span", { style: { fontSize: 11.5, color: T.muted, flex: "none", width: 44 } }, t("sessionPick")),
|
|
114
142
|
h("select", {
|
|
115
143
|
value: node.sessionId,
|
|
116
144
|
disabled: !sessions || sessions.length === 0,
|
|
@@ -118,8 +146,8 @@ window.__ModuleLoader__.load({
|
|
|
118
146
|
style: { ...inputStyle, height: 30, padding: "0 8px", fontSize: 12, flex: 1, minWidth: 0 },
|
|
119
147
|
},
|
|
120
148
|
(sessions ?? []).some((session) => session.id === node.sessionId) ? null : h("option", { value: node.sessionId }, node.sessionId),
|
|
121
|
-
(sessions ?? []).map((session) => h("option", { key: session.id, value: session.id },
|
|
122
|
-
|
|
149
|
+
(sessions ?? []).map((session) => h("option", { key: session.id, value: session.id, title: session.id },
|
|
150
|
+
sessionLabel(t, session))),
|
|
123
151
|
),
|
|
124
152
|
),
|
|
125
153
|
node.lastError ? h("div", { style: { fontSize: 11.5, color: T.err, marginTop: 6 } }, node.lastError) : null,
|
|
@@ -296,6 +324,22 @@ window.__ModuleLoader__.load({
|
|
|
296
324
|
}
|
|
297
325
|
};
|
|
298
326
|
|
|
327
|
+
const onSwitchProject = async (node, projectDir) => {
|
|
328
|
+
if (!projectDir || projectDir === node.projectDir) return;
|
|
329
|
+
if (!window.confirm(`${t("switchProjectHint")}?`)) return;
|
|
330
|
+
try {
|
|
331
|
+
const res = await fetch(`${ENDPOINT}/nodes/${encodeURIComponent(node.id)}`, {
|
|
332
|
+
method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ projectDir }),
|
|
333
|
+
});
|
|
334
|
+
const body = await res.json();
|
|
335
|
+
if (!res.ok || body.ok === false) throw new Error(body.error ?? `HTTP ${res.status}`);
|
|
336
|
+
setMessage(t("projectSwitched"));
|
|
337
|
+
await refresh();
|
|
338
|
+
} catch (error) {
|
|
339
|
+
setFailed(true); setMessage(`${t("failed")}: ${error?.message ?? error}`);
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
|
|
299
343
|
const onSwitch = async (node, sessionId) => {
|
|
300
344
|
if (!sessionId || sessionId === node.sessionId) return;
|
|
301
345
|
if (!window.confirm(t("confirmDelete").replace(/.*/, t("switchSession") + "?"))) return;
|
|
@@ -333,7 +377,7 @@ window.__ModuleLoader__.load({
|
|
|
333
377
|
state.nodes.length === 0
|
|
334
378
|
? h("p", { style: { fontSize: 12, color: T.muted, margin: "10px 0 2px" } }, t("noNodes"))
|
|
335
379
|
: h("div", { style: { marginTop: 12 } }, state.nodes.map((node) =>
|
|
336
|
-
h(NodeRow, { key: node.id, node, t, onDelete, sessions, onSwitch }))),
|
|
380
|
+
h(NodeRow, { key: node.id, node, t, onDelete, sessions, onSwitch, projects, onSwitchProject }))),
|
|
337
381
|
),
|
|
338
382
|
|
|
339
383
|
session
|
|
@@ -376,7 +420,7 @@ window.__ModuleLoader__.load({
|
|
|
376
420
|
onChange: (event) => setSessionChoice(event.target.value),
|
|
377
421
|
},
|
|
378
422
|
h("option", { value: "__new__" }, t("newSession")),
|
|
379
|
-
sessions.map((item) => h("option", { key: item.id, value: item.id
|
|
423
|
+
sessions.map((item) => h("option", { key: item.id, value: item.id, title: item.id }, sessionLabel(t, item))),
|
|
380
424
|
),
|
|
381
425
|
h("p", { style: hintStyle }, t("sessionHint")),
|
|
382
426
|
),
|
package/src/plugin.mjs
CHANGED
|
@@ -13,7 +13,7 @@ import { createLedger } from "./ledger.mjs";
|
|
|
13
13
|
import { createCommhubNode } from "./node-loop.mjs";
|
|
14
14
|
import { commhubToolSpecs } from "./tools.mjs";
|
|
15
15
|
import { summarizeTurn, turnFailure } from "./turn.mjs";
|
|
16
|
-
import { findNodeBySession, loadNodes, normalizeNode, registryPath, saveNodes, validateNode } from "./nodes.mjs";
|
|
16
|
+
import { findNodeBySession, loadNodes, newSessionId, normalizeNode, registryPath, saveNodes, validateNode } from "./nodes.mjs";
|
|
17
17
|
import { startConfigServer } from "./ui-server.mjs";
|
|
18
18
|
|
|
19
19
|
export { summarizeTurn } from "./turn.mjs";
|
|
@@ -21,7 +21,7 @@ export { summarizeTurn } from "./turn.mjs";
|
|
|
21
21
|
export const name = "anet";
|
|
22
22
|
export const inject = ["agents", "agentDefaultModel", "sessions", "tools"];
|
|
23
23
|
|
|
24
|
-
const VERSION = "dsh-anet@0.1.0-preview.
|
|
24
|
+
const VERSION = "dsh-anet@0.1.0-preview.28";
|
|
25
25
|
|
|
26
26
|
function agentSetup(ctx) {
|
|
27
27
|
const selection = ctx.agentDefaultModel.currentSelection();
|
|
@@ -196,6 +196,22 @@ export function apply(ctx, config) {
|
|
|
196
196
|
return { projects, defaultProject: projects[0]?.path ?? process.cwd() };
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
+
async function sessionTitle(persistence, id) {
|
|
200
|
+
try {
|
|
201
|
+
const { events } = await persistence.inspect(id);
|
|
202
|
+
const titled = events.find((event) => event.type === "session/title");
|
|
203
|
+
const title = titled?.data?.title ?? titled?.data?.text;
|
|
204
|
+
if (typeof title === "string" && title.trim()) return title.trim();
|
|
205
|
+
const first = events.find((event) => event.type === "user/message");
|
|
206
|
+
const content = first?.data?.message?.content ?? first?.data?.content;
|
|
207
|
+
if (Array.isArray(content)) {
|
|
208
|
+
const text = content.filter((block) => block?.type === "text").map((block) => block.text).join(" ").trim();
|
|
209
|
+
if (text) return text.slice(0, 48);
|
|
210
|
+
}
|
|
211
|
+
} catch { /* unreadable session */ }
|
|
212
|
+
return "";
|
|
213
|
+
}
|
|
214
|
+
|
|
199
215
|
async function listSessions() {
|
|
200
216
|
const persistence = ctx.get("sessionPersistence");
|
|
201
217
|
const sessions = [];
|
|
@@ -207,7 +223,9 @@ export function apply(ctx, config) {
|
|
|
207
223
|
} catch { /* no persisted sessions */ }
|
|
208
224
|
}
|
|
209
225
|
sessions.sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0));
|
|
210
|
-
|
|
226
|
+
const recent = sessions.slice(0, 40);
|
|
227
|
+
for (const session of recent) session.title = await sessionTitle(persistence, session.id);
|
|
228
|
+
return { sessions: [...recent, ...sessions.slice(40, 200)] };
|
|
211
229
|
}
|
|
212
230
|
|
|
213
231
|
async function sessionById(id) {
|
|
@@ -242,6 +260,10 @@ export function apply(ctx, config) {
|
|
|
242
260
|
const node = requireNode(id);
|
|
243
261
|
let sessionId = node.sessionId;
|
|
244
262
|
let projectDir = String(draft.projectDir ?? node.projectDir);
|
|
263
|
+
if (draft.projectDir !== undefined && String(draft.projectDir) !== node.projectDir && draft.sessionId === undefined) {
|
|
264
|
+
projectDir = String(draft.projectDir);
|
|
265
|
+
sessionId = newSessionId(node.name);
|
|
266
|
+
}
|
|
245
267
|
if (draft.sessionId !== undefined && String(draft.sessionId) !== node.sessionId) {
|
|
246
268
|
const session = await sessionById(String(draft.sessionId));
|
|
247
269
|
if (!session) throw new Error(`session 不存在: ${draft.sessionId}`);
|