@sleep2agi/dsh-anet 0.1.0-preview.27 → 0.1.0-preview.29

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
@@ -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. Use the session dropdown on a node card to switch sessions,
33
- or **Delete** to stop it.
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
@@ -28,7 +28,7 @@ dsh plugin --profile web add @sleep2agi/dsh-anet
28
28
  3. 创建节点:节点名称(alias)、项目目录、Session(新建或选已有)。token 由当前登录账号
29
29
  自动签发,节点立即上线。
30
30
  4. 其他人在 hub/看板给该 alias 派任务;任务在节点的 DSH session 里执行,结果自动回 hub。
31
- 5. 可同时运行多个节点;节点卡片里的 Session 下拉可随时切换,**删除** 即下线。
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.27",
3
+ "version": "0.1.0-preview.29",
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
@@ -26,7 +26,7 @@ window.__ModuleLoader__.load({
26
26
  loading: "正在加载…", offline: "本地服务不可用(127.0.0.1:9460)", failed: "操作失败",
27
27
  note: "配置保存在 ~/.anet/dsh/nodes.json(0600)",
28
28
  running: "已连接", starting: "连接中", stopped: "未连接", error: "异常",
29
- network: "Network", projectLabel: "项目", noNetwork: "默认网络",
29
+ network: "Network", projectLabel: "项目", noNetwork: "默认网络", unnamedSession: "未命名会话",
30
30
  };
31
31
  const en = {
32
32
  title: "ANet", subtitle: "Turn a project/session into an Agent Network node",
@@ -43,7 +43,7 @@ window.__ModuleLoader__.load({
43
43
  loading: "Loading…", offline: "Local service unavailable (127.0.0.1:9460)", failed: "Failed",
44
44
  note: "Stored in ~/.anet/dsh/nodes.json (0600)",
45
45
  running: "Connected", starting: "Connecting", stopped: "Disconnected", error: "Error",
46
- network: "Network", projectLabel: "Project", noNetwork: "default network",
46
+ network: "Network", projectLabel: "Project", noNetwork: "default network", unnamedSession: "Untitled session",
47
47
  };
48
48
 
49
49
  const T = {
@@ -72,6 +72,22 @@ window.__ModuleLoader__.load({
72
72
  const sectionHint = { margin: "4px 0 12px", fontSize: 12, color: T.muted, lineHeight: 1.5 };
73
73
  const grid2 = { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 };
74
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
+
75
91
  function Field(props) {
76
92
  return h("div", { style: props.style ?? null },
77
93
  h("label", { style: labelStyle, htmlFor: props.id }, props.label),
@@ -98,8 +114,13 @@ window.__ModuleLoader__.load({
98
114
  );
99
115
  }
100
116
 
117
+ function sessionsUnder(sessions, projectDir, currentId) {
118
+ return (sessions ?? []).filter((session) => session.id === currentId || session.cwd === projectDir);
119
+ }
120
+
101
121
  function NodeRow(props) {
102
122
  const { node, t, onDelete, sessions, onSwitch, projects, onSwitchProject } = props;
123
+ const nodeSessions = sessionsUnder(sessions, node.projectDir, node.sessionId);
103
124
  return h("div", { style: { ...cardStyle, padding: "14px 16px", marginBottom: 10 } },
104
125
  h("div", { style: { display: "flex", alignItems: "center", gap: 10 } },
105
126
  h("div", { style: { fontSize: 14, fontWeight: 650, color: T.text, flex: 1, minWidth: 0, overflowWrap: "anywhere" } }, node.name),
@@ -125,13 +146,13 @@ window.__ModuleLoader__.load({
125
146
  h("span", { style: { fontSize: 11.5, color: T.muted, flex: "none", width: 44 } }, t("sessionPick")),
126
147
  h("select", {
127
148
  value: node.sessionId,
128
- disabled: !sessions || sessions.length === 0,
149
+ disabled: nodeSessions.length === 0,
129
150
  onChange: (event) => onSwitch(node, event.target.value),
130
151
  style: { ...inputStyle, height: 30, padding: "0 8px", fontSize: 12, flex: 1, minWidth: 0 },
131
152
  },
132
- (sessions ?? []).some((session) => session.id === node.sessionId) ? null : h("option", { value: node.sessionId }, node.sessionId),
133
- (sessions ?? []).map((session) => h("option", { key: session.id, value: session.id },
134
- `${session.id}${session.cwd ? ` — ${session.cwd}` : ""}`)),
153
+ nodeSessions.some((session) => session.id === node.sessionId) ? null : h("option", { value: node.sessionId }, node.sessionId),
154
+ nodeSessions.map((session) => h("option", { key: session.id, value: session.id, title: session.id },
155
+ sessionLabel(t, session))),
135
156
  ),
136
157
  ),
137
158
  node.lastError ? h("div", { style: { fontSize: 11.5, color: T.err, marginTop: 6 } }, node.lastError) : null,
@@ -255,6 +276,13 @@ window.__ModuleLoader__.load({
255
276
  .catch(() => {});
256
277
  }, []);
257
278
 
279
+ const createSessions = sessionsUnder(sessions, draft.projectDir, sessionChoice === "__new__" ? null : sessionChoice);
280
+ React.useEffect(() => {
281
+ if (sessionChoice !== "__new__" && !sessions.some((item) => item.id === sessionChoice && item.cwd === draft.projectDir)) {
282
+ setSessionChoice("__new__");
283
+ }
284
+ }, [draft.projectDir, sessions]);
285
+
258
286
  const editLogin = (key) => (value) => setLoginDraft((current) => ({ ...current, [key]: value }));
259
287
  const editDraft = (key) => (value) => setDraft((current) => ({ ...current, [key]: value }));
260
288
 
@@ -404,7 +432,7 @@ window.__ModuleLoader__.load({
404
432
  onChange: (event) => setSessionChoice(event.target.value),
405
433
  },
406
434
  h("option", { value: "__new__" }, t("newSession")),
407
- sessions.map((item) => h("option", { key: item.id, value: item.id }, `${item.id}${item.cwd ? ` — ${item.cwd}` : ""}`)),
435
+ createSessions.map((item) => h("option", { key: item.id, value: item.id, title: item.id }, sessionLabel(t, item))),
408
436
  ),
409
437
  h("p", { style: hintStyle }, t("sessionHint")),
410
438
  ),
package/src/plugin.mjs CHANGED
@@ -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.27";
24
+ const VERSION = "dsh-anet@0.1.0-preview.29";
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
- return { sessions: sessions.slice(0, 200) };
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) {