@nelsonlongxiang/dsh-open-a2a-net 0.5.13

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/lib/client.js ADDED
@@ -0,0 +1,756 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@nelsonlongxiang/dsh-open-a2a-net",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react_jsx_runtime = require("react/jsx-runtime");
8
+ let react = require("react");
9
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
10
+ //#region node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
11
+ function r(e) {
12
+ var t, f, n = "";
13
+ if ("string" == typeof e || "number" == typeof e) n += e;
14
+ else if ("object" == typeof e) if (Array.isArray(e)) {
15
+ var o = e.length;
16
+ for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
17
+ } else for (f in e) e[f] && (n && (n += " "), n += f);
18
+ return n;
19
+ }
20
+ function clsx() {
21
+ for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
22
+ return n;
23
+ }
24
+ //#endregion
25
+ //#region \0dsh-css:D:\workspace\dsh-open-a2a-net\src\client\A2aControl.module.css.mjs
26
+ const css = ".Q9Ky_q_wrap{max-width:100%;display:inline-flex;position:relative}.Q9Ky_q_trigger{align-items:center;gap:4px;display:inline-flex;position:relative}.Q9Ky_q_unreadBadge{text-align:center;color:#fff;background:var(--dsh-accent,#4c8bf5);border-radius:7px;min-width:14px;height:14px;padding:0 3px;font-size:9px;line-height:14px;animation:2s ease-in-out infinite Q9Ky_q_badgePulse;position:absolute;top:-6px;right:-8px}@keyframes Q9Ky_q_badgePulse{0%,to{box-shadow:0 0 #4c8bf573}50%{box-shadow:0 0 0 4px #4c8bf500}}@media (prefers-reduced-motion:reduce){.Q9Ky_q_unreadBadge{animation:none}}.Q9Ky_q_popover{z-index:1100;border:1px solid var(--dsh-border,#2a2a30);background:var(--dsh-surface-raised,#1c1c22);border-radius:10px;flex-direction:column;gap:8px;width:320px;max-width:calc(100vw - 16px);max-height:60vh;padding:12px;display:flex;position:fixed;overflow-y:auto;box-shadow:0 8px 24px #00000059}.Q9Ky_q_title{font-size:13px;font-weight:600}.Q9Ky_q_sectionTitle{border-top:1px solid var(--dsh-border,#2a2a30);text-transform:uppercase;letter-spacing:.04em;opacity:.65;margin-top:4px;padding-top:8px;font-size:11px;font-weight:600}.Q9Ky_q_empty{opacity:.7;font-size:12px}.Q9Ky_q_row{border:1px solid #0000;border-radius:8px;align-items:center;gap:10px;padding:8px;display:flex}.Q9Ky_q_joined{border-color:var(--dsh-border,#2a2a30)}.Q9Ky_q_facts{text-align:left;min-width:0;color:inherit;font:inherit;cursor:pointer;background:0 0;border:none;border-radius:6px;flex-direction:column;flex:1;gap:2px;padding:2px 4px;display:flex}.Q9Ky_q_facts:hover{background:var(--dsh-surface-hover,#ffffff0f)}.Q9Ky_q_nameRow{align-items:center;gap:6px;display:flex}.Q9Ky_q_stateDot{border-radius:50%;flex:none;width:7px;height:7px}.Q9Ky_q_stateDot.Q9Ky_q_live{background:var(--dsh-accent,#4c8bf5)}.Q9Ky_q_stateDot.Q9Ky_q_live.Q9Ky_q_joined{background:var(--dsh-success,#3fb96f)}.Q9Ky_q_stateDot.Q9Ky_q_cold{background:var(--dsh-warning,#d9a13c)}.Q9Ky_q_name{text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:500;overflow:hidden}.Q9Ky_q_description{opacity:.7;text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.Q9Ky_q_team{opacity:.55;font-size:11px;font-family:var(--dsh-mono,monospace)}.Q9Ky_q_note{opacity:.6;font-size:11px}.Q9Ky_q_searchInput{box-sizing:border-box;border:1px solid var(--dsh-border,#2a2a30);background:var(--dsh-surface,#141419);width:100%;color:inherit;border-radius:6px;padding:5px 8px;font-size:12px}.Q9Ky_q_searchInput:focus{outline:1px solid var(--dsh-accent,#4c8bf5)}.Q9Ky_q_sessionGroup{flex-direction:column;gap:2px;display:flex}.Q9Ky_q_groupHead{color:inherit;font:inherit;opacity:.75;cursor:pointer;text-align:left;background:0 0;border:none;align-items:center;gap:6px;padding:4px 2px;font-size:11px;font-weight:600;display:flex}.Q9Ky_q_groupChevron{transition:transform .12s;display:inline-block}.Q9Ky_q_groupChevronClosed{transform:rotate(-90deg)}.Q9Ky_q_groupTag{text-overflow:ellipsis;white-space:nowrap;border:1px solid var(--dsh-border,#2a2a30);opacity:.8;border-radius:999px;flex:none;max-width:90px;padding:0 6px;font-size:10px;line-height:16px;overflow:hidden}.Q9Ky_q_groupButton{width:24px;height:24px;color:inherit;opacity:.55;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:6px;flex:none;font-size:12px;line-height:1}.Q9Ky_q_groupButton:hover{border-color:var(--dsh-border,#2a2a30);opacity:1}.Q9Ky_q_groupPicker{border:1px dashed var(--dsh-border,#2a2a30);border-radius:8px;flex-wrap:wrap;gap:6px;width:100%;padding:8px;display:flex;position:relative}.Q9Ky_q_groupOption{border:1px solid var(--dsh-border,#2a2a30);color:inherit;cursor:pointer;background:0 0;border-radius:999px;padding:2px 8px;font-size:11px}.Q9Ky_q_groupOptionActive{border-color:var(--dsh-accent,#4c8bf5);color:var(--dsh-accent,#4c8bf5)}.Q9Ky_q_groupNew{align-items:center;gap:6px;width:100%;margin-top:2px;display:flex}.Q9Ky_q_groupInput{border:1px solid var(--dsh-border,#2a2a30);background:var(--dsh-surface,#141419);min-width:0;color:inherit;border-radius:6px;flex:1;padding:4px 8px;font-size:12px}.Q9Ky_q_peerGrid{flex-wrap:wrap;gap:6px;display:flex}.Q9Ky_q_peerChip{border:1px solid var(--dsh-border,#2a2a30);border-radius:999px;align-items:center;gap:6px;padding:3px 8px;font-size:11px;display:inline-flex}.Q9Ky_q_peerHost{text-overflow:ellipsis;white-space:nowrap;max-width:160px;overflow:hidden}.Q9Ky_q_peerScore{font-family:var(--dsh-mono,monospace);opacity:.6}.Q9Ky_q_activityList{flex-direction:column;gap:2px;display:flex}.Q9Ky_q_activityRow{border-radius:4px;align-items:center;gap:8px;padding:2px 4px;font-size:11px;display:flex}.Q9Ky_q_activityRow[data-ok=false]{opacity:.55}.Q9Ky_q_activityDir{flex:none;font-weight:600}.Q9Ky_q_activityDir.Q9Ky_q_in{color:var(--dsh-success,#3fb96f)}.Q9Ky_q_activityDir.Q9Ky_q_out{color:var(--dsh-accent,#4c8bf5)}.Q9Ky_q_activityTeam{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-family:var(--dsh-mono,monospace);opacity:.8;flex:1;overflow:hidden}.Q9Ky_q_activityPeer{text-overflow:ellipsis;white-space:nowrap;opacity:.55;max-width:90px;overflow:hidden}.Q9Ky_q_activityTime{opacity:.5;font-family:var(--dsh-mono,monospace);flex:none}.Q9Ky_q_activityRow[data-jump=true]{cursor:pointer}.Q9Ky_q_activityRow[data-jump=true]:hover{background:var(--dsh-surface-hover,#ffffff0f)}.Q9Ky_q_fresh{animation:1.2s ease-out 3 Q9Ky_q_freshFlash}@keyframes Q9Ky_q_freshFlash{0%{background:#4c8bf538}to{background:0 0}}@media (prefers-reduced-motion:reduce){.Q9Ky_q_fresh{background:#4c8bf51f;animation:none}}.Q9Ky_q_inFlightList{flex-direction:column;gap:2px;margin-bottom:4px;display:flex}.Q9Ky_q_inFlightRow{border:1px dashed var(--dsh-border,#2a2a30);border-radius:4px;align-items:center;gap:8px;padding:3px 4px;font-size:11px;display:flex}.Q9Ky_q_inFlightPulse{background:var(--dsh-accent,#4c8bf5);border-radius:50%;flex:none;width:7px;height:7px;animation:1.1s ease-in-out infinite Q9Ky_q_flightPulse}@keyframes Q9Ky_q_flightPulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(.55)}}@media (prefers-reduced-motion:reduce){.Q9Ky_q_inFlightPulse{animation:none}}.Q9Ky_q_versionTag{color:var(--dsh-color-text-tertiary,#888);background:var(--dsh-color-bg-secondary,#8080801f);border-radius:999px;margin-left:auto;padding:1px 6px;font-size:10px;font-weight:500}.Q9Ky_q_inFlightStale{opacity:.55}.Q9Ky_q_inFlightStale .Q9Ky_q_inFlightPulse{opacity:.4;animation:none}";
27
+ const tagId = "@nelsonlongxiang/dsh-open-a2a-net/A2aControl.module.css";
28
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
29
+ const tag = document.createElement("style");
30
+ tag.dataset.plugin = "@nelsonlongxiang/dsh-open-a2a-net";
31
+ tag.dataset.pluginCss = tagId;
32
+ tag.textContent = css;
33
+ document.head.appendChild(tag);
34
+ }
35
+ var A2aControl_module_css_default = {
36
+ "activityDir": "Q9Ky_q_activityDir",
37
+ "activityList": "Q9Ky_q_activityList",
38
+ "activityPeer": "Q9Ky_q_activityPeer",
39
+ "activityRow": "Q9Ky_q_activityRow",
40
+ "activityTeam": "Q9Ky_q_activityTeam",
41
+ "activityTime": "Q9Ky_q_activityTime",
42
+ "badgePulse": "Q9Ky_q_badgePulse",
43
+ "cold": "Q9Ky_q_cold",
44
+ "description": "Q9Ky_q_description",
45
+ "empty": "Q9Ky_q_empty",
46
+ "facts": "Q9Ky_q_facts",
47
+ "flightPulse": "Q9Ky_q_flightPulse",
48
+ "fresh": "Q9Ky_q_fresh",
49
+ "freshFlash": "Q9Ky_q_freshFlash",
50
+ "groupButton": "Q9Ky_q_groupButton",
51
+ "groupChevron": "Q9Ky_q_groupChevron",
52
+ "groupChevronClosed": "Q9Ky_q_groupChevronClosed",
53
+ "groupHead": "Q9Ky_q_groupHead",
54
+ "groupInput": "Q9Ky_q_groupInput",
55
+ "groupNew": "Q9Ky_q_groupNew",
56
+ "groupOption": "Q9Ky_q_groupOption",
57
+ "groupOptionActive": "Q9Ky_q_groupOptionActive",
58
+ "groupPicker": "Q9Ky_q_groupPicker",
59
+ "groupTag": "Q9Ky_q_groupTag",
60
+ "in": "Q9Ky_q_in",
61
+ "inFlightList": "Q9Ky_q_inFlightList",
62
+ "inFlightPulse": "Q9Ky_q_inFlightPulse",
63
+ "inFlightRow": "Q9Ky_q_inFlightRow",
64
+ "inFlightStale": "Q9Ky_q_inFlightStale",
65
+ "joined": "Q9Ky_q_joined",
66
+ "live": "Q9Ky_q_live",
67
+ "name": "Q9Ky_q_name",
68
+ "nameRow": "Q9Ky_q_nameRow",
69
+ "note": "Q9Ky_q_note",
70
+ "out": "Q9Ky_q_out",
71
+ "peerChip": "Q9Ky_q_peerChip",
72
+ "peerGrid": "Q9Ky_q_peerGrid",
73
+ "peerHost": "Q9Ky_q_peerHost",
74
+ "peerScore": "Q9Ky_q_peerScore",
75
+ "popover": "Q9Ky_q_popover",
76
+ "row": "Q9Ky_q_row",
77
+ "searchInput": "Q9Ky_q_searchInput",
78
+ "sectionTitle": "Q9Ky_q_sectionTitle",
79
+ "sessionGroup": "Q9Ky_q_sessionGroup",
80
+ "stateDot": "Q9Ky_q_stateDot",
81
+ "team": "Q9Ky_q_team",
82
+ "title": "Q9Ky_q_title",
83
+ "trigger": "Q9Ky_q_trigger",
84
+ "unreadBadge": "Q9Ky_q_unreadBadge",
85
+ "versionTag": "Q9Ky_q_versionTag",
86
+ "wrap": "Q9Ky_q_wrap"
87
+ };
88
+ //#endregion
89
+ //#region lib/client/A2aControl.js
90
+ /**
91
+ * A2A network control: a footer action opening this host's network panel.
92
+ * Probes the host's session-node state route at registration time (the seat
93
+ * renders only on a host that serves it). The panel has three sections:
94
+ * session nodes (live rows show title, recent-activity excerpt, and team with
95
+ * a join/leave toggle and open the session on row click — the wake action on
96
+ * cold rows opens the session through the standard flow), the peer fleet
97
+ * (tracked URLs with quality scores), and the recent routing activity ring
98
+ * (inbound and outbound outcomes). While open, the panel polls so listings
99
+ * visibly follow each turn.
100
+ */
101
+ /** Fetch the host's network state; undefined means the route is absent. */
102
+ async function fetchState() {
103
+ const response = await fetch("/__dsh_a2a/state", { cache: "no-store" });
104
+ if (!response.ok) return void 0;
105
+ const body = await response.json();
106
+ if (body.nodes !== true || !Array.isArray(body.sessions)) return void 0;
107
+ return {
108
+ sessions: body.sessions,
109
+ groups: Array.isArray(body.groups) ? body.groups : [],
110
+ peers: Array.isArray(body.peers) ? body.peers : [],
111
+ activity: Array.isArray(body.activity) ? body.activity : [],
112
+ inFlight: Array.isArray(body.inFlight) ? body.inFlight : [],
113
+ remote: Array.isArray(body.remote) ? body.remote : [],
114
+ ...typeof body.version === "string" ? { version: body.version } : {}
115
+ };
116
+ }
117
+ /** Compact relative time for an activity row. */
118
+ function relativeTime(ts) {
119
+ const seconds = Math.max(0, Math.round((Date.now() - ts) / 1e3));
120
+ if (seconds < 60) return `${String(seconds)}s`;
121
+ const minutes = Math.round(seconds / 60);
122
+ if (minutes < 60) return `${String(minutes)}m`;
123
+ return `${String(Math.round(minutes / 60))}h`;
124
+ }
125
+ /**
126
+ * One session row: the facts block opens the session, the toggle joins or
127
+ * leaves, and the tag button opens the group picker (assign / create /
128
+ * clear).
129
+ */
130
+ function SessionRow({ row, t, listById, openSession, busy, onToggle, pickerFor, setPickerFor, groups, newGroupName, setNewGroupName, onAssign }) {
131
+ const cold = row.live === false;
132
+ const name = cold ? listById[row.id]?.displayTitle ?? row.label : row.name ?? row.label;
133
+ const description = cold ? t("a2a.cold") : row.description ?? "";
134
+ const pickerOpen = pickerFor === row.id;
135
+ return (0, react_jsx_runtime.jsxs)("div", {
136
+ className: clsx(A2aControl_module_css_default.row, row.joined && A2aControl_module_css_default.joined),
137
+ children: [
138
+ (0, react_jsx_runtime.jsxs)("button", {
139
+ type: "button",
140
+ className: A2aControl_module_css_default.facts,
141
+ title: cold ? t("a2a.wake") : name,
142
+ onClick: () => {
143
+ openSession(row.id);
144
+ },
145
+ children: [
146
+ (0, react_jsx_runtime.jsxs)("span", {
147
+ className: A2aControl_module_css_default.nameRow,
148
+ children: [
149
+ (0, react_jsx_runtime.jsx)("span", {
150
+ className: clsx(A2aControl_module_css_default.stateDot, cold ? "cold" : "live", row.joined && "joined"),
151
+ "aria-hidden": true
152
+ }),
153
+ (0, react_jsx_runtime.jsx)("span", {
154
+ className: A2aControl_module_css_default.name,
155
+ children: name
156
+ }),
157
+ row.group !== void 0 ? (0, react_jsx_runtime.jsx)("span", {
158
+ className: A2aControl_module_css_default.groupTag,
159
+ children: row.group
160
+ }) : null
161
+ ]
162
+ }),
163
+ (0, react_jsx_runtime.jsx)("span", {
164
+ className: A2aControl_module_css_default.description,
165
+ children: description
166
+ }),
167
+ (0, react_jsx_runtime.jsx)("span", {
168
+ className: A2aControl_module_css_default.team,
169
+ children: row.team
170
+ })
171
+ ]
172
+ }),
173
+ (0, react_jsx_runtime.jsx)("button", {
174
+ type: "button",
175
+ className: A2aControl_module_css_default.groupButton,
176
+ title: t("a2a.group"),
177
+ "aria-label": t("a2a.group"),
178
+ onClick: () => {
179
+ setPickerFor(pickerOpen ? null : row.id);
180
+ },
181
+ children: "#"
182
+ }),
183
+ cold ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
184
+ variant: "outline",
185
+ size: "sm",
186
+ disabled: busy === row.id,
187
+ onClick: () => {
188
+ onToggle(row);
189
+ },
190
+ children: t("a2a.leave")
191
+ }) : (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
192
+ variant: row.joined ? "outline" : "primary",
193
+ size: "sm",
194
+ disabled: busy === row.id,
195
+ onClick: () => {
196
+ onToggle(row);
197
+ },
198
+ children: row.joined ? t("a2a.leave") : t("a2a.join")
199
+ }),
200
+ pickerOpen && (0, react_jsx_runtime.jsxs)("div", {
201
+ className: A2aControl_module_css_default.groupPicker,
202
+ children: [
203
+ (0, react_jsx_runtime.jsx)("button", {
204
+ type: "button",
205
+ className: A2aControl_module_css_default.groupOption,
206
+ onClick: () => {
207
+ onAssign(row.id, "");
208
+ },
209
+ children: t("a2a.groupClear")
210
+ }),
211
+ groups.map((group) => (0, react_jsx_runtime.jsx)("button", {
212
+ type: "button",
213
+ className: clsx(A2aControl_module_css_default.groupOption, row.group === group && A2aControl_module_css_default.groupOptionActive),
214
+ onClick: () => {
215
+ onAssign(row.id, group);
216
+ },
217
+ children: group
218
+ }, group)),
219
+ (0, react_jsx_runtime.jsxs)("div", {
220
+ className: A2aControl_module_css_default.groupNew,
221
+ children: [(0, react_jsx_runtime.jsx)("input", {
222
+ className: A2aControl_module_css_default.groupInput,
223
+ value: newGroupName,
224
+ placeholder: t("a2a.groupNew"),
225
+ maxLength: 40,
226
+ onChange: (event) => {
227
+ setNewGroupName(event.target.value);
228
+ },
229
+ onKeyDown: (event) => {
230
+ if (event.key === "Enter" && newGroupName.trim() !== "") onAssign(row.id, newGroupName.trim());
231
+ }
232
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
233
+ variant: "primary",
234
+ size: "sm",
235
+ disabled: newGroupName.trim() === "",
236
+ onClick: () => {
237
+ if (newGroupName.trim() !== "") onAssign(row.id, newGroupName.trim());
238
+ },
239
+ children: t("a2a.groupNewGo")
240
+ })]
241
+ })
242
+ ]
243
+ })
244
+ ]
245
+ });
246
+ }
247
+ /**
248
+ * Render the network toggle and its network panel.
249
+ * @param props - composed slot props.
250
+ * @returns the control element tree.
251
+ */
252
+ function A2aControl({ wide, t, useSessions, openSession }) {
253
+ const [open, setOpen] = (0, react.useState)(false);
254
+ const [state, setState] = (0, react.useState)({
255
+ sessions: [],
256
+ groups: [],
257
+ peers: [],
258
+ activity: [],
259
+ inFlight: [],
260
+ remote: []
261
+ });
262
+ const [busy, setBusy] = (0, react.useState)(null);
263
+ const [seenActivity, setSeenActivity] = (0, react.useState)(0);
264
+ const [search, setSearch] = (0, react.useState)("");
265
+ const [pickerFor, setPickerFor] = (0, react.useState)(null);
266
+ const [newGroupName, setNewGroupName] = (0, react.useState)("");
267
+ const [collapsed, setCollapsed] = (0, react.useState)(/* @__PURE__ */ new Set());
268
+ const stopped = (0, react.useRef)(false);
269
+ (0, react.useEffect)(() => () => {
270
+ stopped.current = true;
271
+ }, []);
272
+ const wrap = (0, react.useRef)(null);
273
+ const listById = useSessions((state2) => state2.byId);
274
+ const [anchor, setAnchor] = (0, react.useState)(null);
275
+ const unread = Math.max(0, state.activity.filter((entry) => entry.dir === "in").length - seenActivity);
276
+ const toggleOpen = () => {
277
+ if (open) {
278
+ setOpen(false);
279
+ return;
280
+ }
281
+ const rect = wrap.current?.getBoundingClientRect();
282
+ if (rect !== void 0) setAnchor({
283
+ left: Math.max(8, Math.min(rect.left, window.innerWidth - 336)),
284
+ bottom: window.innerHeight - rect.top + 8
285
+ });
286
+ setSeenActivity(state.activity.filter((entry) => entry.dir === "in").length);
287
+ setOpen(true);
288
+ };
289
+ const refresh = (0, react.useCallback)(() => {
290
+ fetchState().then((next) => {
291
+ if (!stopped.current && next !== void 0) setState(next);
292
+ }).catch(() => {});
293
+ }, []);
294
+ (0, react.useEffect)(() => {
295
+ if (open) refresh();
296
+ }, [open, refresh]);
297
+ (0, react.useEffect)(() => {
298
+ const slow = setInterval(refresh, 1e4);
299
+ refresh();
300
+ return () => {
301
+ clearInterval(slow);
302
+ };
303
+ }, [refresh]);
304
+ (0, react.useEffect)(() => {
305
+ if (!open) return;
306
+ const poll = setInterval(refresh, 2e3);
307
+ return () => {
308
+ clearInterval(poll);
309
+ };
310
+ }, [open, refresh]);
311
+ (0, react.useEffect)(() => {
312
+ if (!open) return;
313
+ const onPointerDown = (event) => {
314
+ if (event.target.closest(`.${A2aControl_module_css_default.wrap}`) === null) setOpen(false);
315
+ };
316
+ document.addEventListener("pointerdown", onPointerDown);
317
+ return () => {
318
+ document.removeEventListener("pointerdown", onPointerDown);
319
+ };
320
+ }, [open]);
321
+ /** Join or leave one session, then refresh the listing. */
322
+ const toggle = (row) => {
323
+ setBusy(row.id);
324
+ fetch(`/__dsh_a2a/${row.joined ? "leave" : "join"}`, {
325
+ method: "POST",
326
+ headers: { "Content-Type": "application/json" },
327
+ body: JSON.stringify({ id: row.id })
328
+ }).then(() => {
329
+ refresh();
330
+ }).catch(() => {}).finally(() => {
331
+ if (!stopped.current) setBusy(null);
332
+ });
333
+ };
334
+ /** Assign one session to a group ('' clears), then refresh. */
335
+ const assign = (id, name) => {
336
+ fetch("/__dsh_a2a/groups", {
337
+ method: "POST",
338
+ headers: { "Content-Type": "application/json" },
339
+ body: JSON.stringify({
340
+ action: "assign",
341
+ id,
342
+ name
343
+ })
344
+ }).then(() => {
345
+ setPickerFor(null);
346
+ setNewGroupName("");
347
+ refresh();
348
+ }).catch(() => {});
349
+ };
350
+ const { sessions, groups, peers, activity, inFlight, remote } = state;
351
+ const needle = search.trim().toLowerCase();
352
+ const matches = (row) => {
353
+ if (needle === "") return true;
354
+ return (row.live === false ? listById[row.id]?.displayTitle ?? row.label : row.name ?? row.label).toLowerCase().includes(needle) || row.team.toLowerCase().includes(needle) || (row.description ?? "").toLowerCase().includes(needle);
355
+ };
356
+ const visible = sessions.filter(matches);
357
+ const ungrouped = visible.filter((row) => row.group === void 0);
358
+ const grouped = groups.map((name) => ({
359
+ name,
360
+ rows: visible.filter((row) => row.group === name)
361
+ })).filter((group) => group.rows.length > 0);
362
+ return (0, react_jsx_runtime.jsxs)("div", {
363
+ ref: wrap,
364
+ className: clsx(A2aControl_module_css_default.wrap, wide ? A2aControl_module_css_default.wide : A2aControl_module_css_default.rail),
365
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
366
+ variant: "ghost",
367
+ size: "sm",
368
+ title: t("a2a.label"),
369
+ "aria-label": t("a2a.label"),
370
+ "aria-expanded": open,
371
+ onClick: () => {
372
+ toggleOpen();
373
+ },
374
+ children: (0, react_jsx_runtime.jsxs)("span", {
375
+ className: A2aControl_module_css_default.trigger,
376
+ children: [
377
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconGlobeOutline14, {}),
378
+ wide ? (0, react_jsx_runtime.jsx)("span", { children: t("a2a.label") }) : null,
379
+ unread > 0 ? (0, react_jsx_runtime.jsx)("span", {
380
+ className: A2aControl_module_css_default.unreadBadge,
381
+ "aria-label": String(unread),
382
+ children: unread > 9 ? "9+" : String(unread)
383
+ }) : null
384
+ ]
385
+ })
386
+ }), open ? (0, react_jsx_runtime.jsxs)("div", {
387
+ className: A2aControl_module_css_default.popover,
388
+ role: "dialog",
389
+ "aria-label": t("a2a.title"),
390
+ style: anchor === null ? void 0 : {
391
+ left: `${String(anchor.left)}px`,
392
+ bottom: `${String(anchor.bottom)}px`
393
+ },
394
+ children: [
395
+ (0, react_jsx_runtime.jsxs)("div", {
396
+ className: A2aControl_module_css_default.title,
397
+ children: [t("a2a.title"), state.version !== void 0 ? (0, react_jsx_runtime.jsxs)("span", {
398
+ className: A2aControl_module_css_default.versionTag,
399
+ title: state.version,
400
+ children: ["v", state.version]
401
+ }) : null]
402
+ }),
403
+ (0, react_jsx_runtime.jsx)("input", {
404
+ className: A2aControl_module_css_default.searchInput,
405
+ type: "search",
406
+ value: search,
407
+ placeholder: t("a2a.search"),
408
+ "aria-label": t("a2a.search"),
409
+ onChange: (event) => {
410
+ setSearch(event.target.value);
411
+ }
412
+ }),
413
+ sessions.length === 0 ? (0, react_jsx_runtime.jsx)("div", {
414
+ className: A2aControl_module_css_default.empty,
415
+ children: t("a2a.empty")
416
+ }) : visible.length === 0 ? (0, react_jsx_runtime.jsx)("div", {
417
+ className: A2aControl_module_css_default.empty,
418
+ children: t("a2a.searchEmpty")
419
+ }) : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [ungrouped.length > 0 && (0, react_jsx_runtime.jsxs)("div", {
420
+ className: A2aControl_module_css_default.sessionGroup,
421
+ children: [(0, react_jsx_runtime.jsxs)("button", {
422
+ type: "button",
423
+ className: A2aControl_module_css_default.groupHead,
424
+ onClick: () => {
425
+ setCollapsed((current) => {
426
+ const next = new Set(current);
427
+ if (next.has("")) next.delete("");
428
+ else next.add("");
429
+ return next;
430
+ });
431
+ },
432
+ children: [
433
+ (0, react_jsx_runtime.jsx)("span", {
434
+ className: clsx(A2aControl_module_css_default.groupChevron, collapsed.has("") && A2aControl_module_css_default.groupChevronClosed),
435
+ "aria-hidden": true,
436
+ children: "▾"
437
+ }),
438
+ t("a2a.groupDefault"),
439
+ " · ",
440
+ String(ungrouped.length)
441
+ ]
442
+ }), !collapsed.has("") && ungrouped.map((row) => (0, react_jsx_runtime.jsx)(SessionRow, {
443
+ row,
444
+ t,
445
+ listById,
446
+ openSession,
447
+ busy,
448
+ onToggle: toggle,
449
+ pickerFor,
450
+ setPickerFor,
451
+ groups,
452
+ newGroupName,
453
+ setNewGroupName,
454
+ onAssign: assign
455
+ }, row.id))]
456
+ }), grouped.map(({ name, rows }) => {
457
+ const isCollapsed = collapsed.has(name);
458
+ return (0, react_jsx_runtime.jsxs)("div", {
459
+ className: A2aControl_module_css_default.sessionGroup,
460
+ children: [(0, react_jsx_runtime.jsxs)("button", {
461
+ type: "button",
462
+ className: A2aControl_module_css_default.groupHead,
463
+ onClick: () => {
464
+ setCollapsed((current) => {
465
+ const next = new Set(current);
466
+ if (next.has(name)) next.delete(name);
467
+ else next.add(name);
468
+ return next;
469
+ });
470
+ },
471
+ children: [
472
+ (0, react_jsx_runtime.jsx)("span", {
473
+ className: clsx(A2aControl_module_css_default.groupChevron, isCollapsed && A2aControl_module_css_default.groupChevronClosed),
474
+ "aria-hidden": true,
475
+ children: "▾"
476
+ }),
477
+ name,
478
+ " · ",
479
+ String(rows.length)
480
+ ]
481
+ }), !isCollapsed && rows.map((row) => (0, react_jsx_runtime.jsx)(SessionRow, {
482
+ row,
483
+ t,
484
+ listById,
485
+ openSession,
486
+ busy,
487
+ onToggle: toggle,
488
+ pickerFor,
489
+ setPickerFor,
490
+ groups,
491
+ newGroupName,
492
+ setNewGroupName,
493
+ onAssign: assign
494
+ }, row.id))]
495
+ }, name);
496
+ })] }),
497
+ (0, react_jsx_runtime.jsx)("div", {
498
+ className: A2aControl_module_css_default.note,
499
+ children: t("a2a.note")
500
+ }),
501
+ (needle === "" ? remote : remote.filter((row) => row.team.toLowerCase().includes(needle) || row.name.toLowerCase().includes(needle) || (row.origin ?? "").toLowerCase().includes(needle) || (row.workspace ?? "").toLowerCase().includes(needle))).length > 0 && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("div", {
502
+ className: A2aControl_module_css_default.sectionTitle,
503
+ children: t("a2a.remote")
504
+ }), Object.entries((needle === "" ? remote : remote.filter((row) => row.team.toLowerCase().includes(needle) || row.name.toLowerCase().includes(needle) || (row.origin ?? "").toLowerCase().includes(needle) || (row.workspace ?? "").toLowerCase().includes(needle))).reduce((byOrigin, row) => {
505
+ const key = row.origin ?? "";
506
+ byOrigin[key] = [...byOrigin[key] ?? [], row];
507
+ return byOrigin;
508
+ }, {})).map(([origin, rows]) => (0, react_jsx_runtime.jsxs)("div", {
509
+ className: A2aControl_module_css_default.sessionGroup,
510
+ children: [(0, react_jsx_runtime.jsxs)("button", {
511
+ type: "button",
512
+ className: A2aControl_module_css_default.groupHead,
513
+ onClick: () => {
514
+ setCollapsed((current) => {
515
+ const next = new Set(current);
516
+ if (next.has("remote:" + origin)) next.delete("remote:" + origin);
517
+ else next.add("remote:" + origin);
518
+ return next;
519
+ });
520
+ },
521
+ children: [
522
+ (0, react_jsx_runtime.jsx)("span", {
523
+ className: clsx(A2aControl_module_css_default.groupChevron, collapsed.has("remote:" + origin) && A2aControl_module_css_default.groupChevronClosed),
524
+ "aria-hidden": true,
525
+ children: "▾"
526
+ }),
527
+ origin === "" ? t("a2a.remoteUnknownOrigin") : origin,
528
+ " · ",
529
+ String(rows.length)
530
+ ]
531
+ }), !collapsed.has("remote:" + origin) && rows.map((row) => (0, react_jsx_runtime.jsx)("div", {
532
+ className: A2aControl_module_css_default.row,
533
+ title: row.workspace ?? void 0,
534
+ children: (0, react_jsx_runtime.jsxs)("div", {
535
+ className: A2aControl_module_css_default.facts,
536
+ children: [(0, react_jsx_runtime.jsxs)("span", {
537
+ className: A2aControl_module_css_default.nameRow,
538
+ children: [
539
+ (0, react_jsx_runtime.jsx)("span", {
540
+ className: A2aControl_module_css_default.stateDot,
541
+ "data-peer": true,
542
+ "aria-hidden": true
543
+ }),
544
+ (0, react_jsx_runtime.jsx)("span", {
545
+ className: A2aControl_module_css_default.name,
546
+ children: row.name === "" ? row.team : row.name
547
+ }),
548
+ row.workspace !== void 0 ? (0, react_jsx_runtime.jsx)("span", {
549
+ className: A2aControl_module_css_default.groupTag,
550
+ children: row.workspace
551
+ }) : null
552
+ ]
553
+ }), (0, react_jsx_runtime.jsx)("span", {
554
+ className: A2aControl_module_css_default.team,
555
+ children: row.team
556
+ })]
557
+ })
558
+ }, row.team))]
559
+ }, origin))] }),
560
+ (0, react_jsx_runtime.jsx)("div", {
561
+ className: A2aControl_module_css_default.sectionTitle,
562
+ children: t("a2a.peers")
563
+ }),
564
+ peers.length === 0 ? (0, react_jsx_runtime.jsx)("div", {
565
+ className: A2aControl_module_css_default.empty,
566
+ children: t("a2a.peersEmpty")
567
+ }) : (0, react_jsx_runtime.jsx)("div", {
568
+ className: A2aControl_module_css_default.peerGrid,
569
+ children: peers.map((peer) => (0, react_jsx_runtime.jsxs)("span", {
570
+ className: A2aControl_module_css_default.peerChip,
571
+ title: peer.url,
572
+ children: [(0, react_jsx_runtime.jsx)("span", {
573
+ className: A2aControl_module_css_default.peerHost,
574
+ children: peer.url.replace(/^https?:\/\//, "").replace(/\/.*$/, "")
575
+ }), typeof peer.score === "number" ? (0, react_jsx_runtime.jsx)("span", {
576
+ className: A2aControl_module_css_default.peerScore,
577
+ children: String(Math.round(peer.score))
578
+ }) : null]
579
+ }, peer.url))
580
+ }),
581
+ (0, react_jsx_runtime.jsx)("div", {
582
+ className: A2aControl_module_css_default.sectionTitle,
583
+ children: t("a2a.activity")
584
+ }),
585
+ inFlight.length > 0 && (0, react_jsx_runtime.jsx)("div", {
586
+ className: A2aControl_module_css_default.inFlightList,
587
+ "aria-label": t("a2a.inFlight"),
588
+ children: inFlight.map((route) => (0, react_jsx_runtime.jsxs)("div", {
589
+ className: clsx(A2aControl_module_css_default.inFlightRow, Date.now() - route.startedAt > 12e4 && A2aControl_module_css_default.inFlightStale),
590
+ title: Date.now() - route.startedAt > 12e4 ? t("a2a.inFlightStale") : void 0,
591
+ children: [
592
+ (0, react_jsx_runtime.jsx)("span", {
593
+ className: A2aControl_module_css_default.inFlightPulse,
594
+ "aria-hidden": true
595
+ }),
596
+ (0, react_jsx_runtime.jsx)("span", {
597
+ className: clsx(A2aControl_module_css_default.activityDir, "out"),
598
+ children: "→"
599
+ }),
600
+ (0, react_jsx_runtime.jsx)("span", {
601
+ className: A2aControl_module_css_default.activityTeam,
602
+ title: route.team,
603
+ children: route.team
604
+ }),
605
+ (0, react_jsx_runtime.jsx)("span", {
606
+ className: A2aControl_module_css_default.activityPeer,
607
+ children: route.peer === "local" ? "" : route.peer.replace(/^https?:\/\//, "").replace(/\/.*$/, "")
608
+ }),
609
+ (0, react_jsx_runtime.jsx)("span", {
610
+ className: A2aControl_module_css_default.activityTime,
611
+ children: relativeTime(route.startedAt)
612
+ })
613
+ ]
614
+ }, `${route.team}-${String(route.startedAt)}`))
615
+ }),
616
+ activity.length === 0 ? (0, react_jsx_runtime.jsx)("div", {
617
+ className: A2aControl_module_css_default.empty,
618
+ children: t("a2a.activityEmpty")
619
+ }) : (0, react_jsx_runtime.jsx)("div", {
620
+ className: A2aControl_module_css_default.activityList,
621
+ children: [...activity].reverse().slice(0, 10).map((entry, index) => {
622
+ const target = sessions.find((row) => row.team === entry.team);
623
+ const fresh = Date.now() - entry.ts < 5e3;
624
+ return (0, react_jsx_runtime.jsxs)("div", {
625
+ className: clsx(A2aControl_module_css_default.activityRow, fresh && A2aControl_module_css_default.fresh),
626
+ "data-ok": entry.ok,
627
+ "data-jump": target !== void 0,
628
+ title: target !== void 0 ? t("a2a.jump") : void 0,
629
+ onClick: () => {
630
+ if (target !== void 0) openSession(target.id);
631
+ },
632
+ role: target !== void 0 ? "button" : void 0,
633
+ children: [
634
+ (0, react_jsx_runtime.jsx)("span", {
635
+ className: clsx(A2aControl_module_css_default.activityDir, entry.dir),
636
+ children: entry.dir === "in" ? "←" : "→"
637
+ }),
638
+ (0, react_jsx_runtime.jsx)("span", {
639
+ className: A2aControl_module_css_default.activityTeam,
640
+ title: entry.team,
641
+ children: entry.team
642
+ }),
643
+ (0, react_jsx_runtime.jsx)("span", {
644
+ className: A2aControl_module_css_default.activityPeer,
645
+ title: entry.peer,
646
+ children: entry.peer === "" ? "" : entry.peer.replace(/^https?:\/\//, "").replace(/\/.*$/, "")
647
+ }),
648
+ (0, react_jsx_runtime.jsx)("span", {
649
+ className: A2aControl_module_css_default.activityTime,
650
+ children: relativeTime(entry.ts)
651
+ })
652
+ ]
653
+ }, `${String(entry.ts)}-${String(index)}`);
654
+ })
655
+ })
656
+ ]
657
+ }) : null]
658
+ });
659
+ }
660
+ //#endregion
661
+ //#region lib/client/locales.js
662
+ /** Locale dictionaries for the A2A network sidebar control (namespace `a2aNet`). */
663
+ /** Simplified Chinese dictionary. */
664
+ const zh = {
665
+ "a2a.label": "A2A 网络",
666
+ "a2a.title": "A2A 网络面板",
667
+ "a2a.empty": "没有可加入的会话",
668
+ "a2a.join": "加入",
669
+ "a2a.leave": "退出",
670
+ "a2a.cold": "未加载(重启后待唤醒)",
671
+ "a2a.wake": "唤醒",
672
+ "a2a.note": "只有加入的会话才会被网络中的其他节点发现;点击会话行可跳转。",
673
+ "a2a.remote": "远程节点(按 host 分组)",
674
+ "a2a.remoteUnknownOrigin": "未知来源",
675
+ "a2a.peers": "对等节点",
676
+ "a2a.peersEmpty": "尚无已知对等节点(配 peers 种子入网)",
677
+ "a2a.activity": "路由活动",
678
+ "a2a.activityEmpty": "暂无路由记录",
679
+ "a2a.inFlight": "进行中的路由",
680
+ "a2a.inFlightStale": "等待回执超时(对端处理慢或回执丢失);180 秒后自动解除并按已送达处理",
681
+ "a2a.jump": "点击跳转到该会话",
682
+ "a2a.search": "搜索会话(名称 / team / 摘要)",
683
+ "a2a.searchEmpty": "没有匹配的会话",
684
+ "a2a.group": "设置分组",
685
+ "a2a.groupDefault": "未分组",
686
+ "a2a.groupClear": "清除分组",
687
+ "a2a.groupNew": "新建分组…",
688
+ "a2a.groupNewGo": "创建并归入"
689
+ };
690
+ /** English dictionary. */
691
+ const en = {
692
+ "a2a.label": "A2A network",
693
+ "a2a.title": "A2A network panel",
694
+ "a2a.empty": "No joinable sessions",
695
+ "a2a.join": "Join",
696
+ "a2a.leave": "Leave",
697
+ "a2a.cold": "not loaded (waiting to wake after a restart)",
698
+ "a2a.wake": "Wake",
699
+ "a2a.note": "Only joined sessions are discoverable by other nodes; click a row to open the session.",
700
+ "a2a.remote": "Remote teams (by host)",
701
+ "a2a.remoteUnknownOrigin": "Unknown origin",
702
+ "a2a.peers": "Peers",
703
+ "a2a.peersEmpty": "No known peers yet (set peers seeds to join the mesh)",
704
+ "a2a.activity": "Routing activity",
705
+ "a2a.activityEmpty": "No routes yet",
706
+ "a2a.inFlight": "Routes in flight",
707
+ "a2a.inFlightStale": "Reply wait past 120s (slow or lost receipt); auto-releases as delivered at 180s",
708
+ "a2a.jump": "Click to open this session",
709
+ "a2a.search": "Search sessions (name / team / excerpt)",
710
+ "a2a.searchEmpty": "No sessions match",
711
+ "a2a.group": "Set group",
712
+ "a2a.groupDefault": "Ungrouped",
713
+ "a2a.groupClear": "Clear group",
714
+ "a2a.groupNew": "New group…",
715
+ "a2a.groupNewGo": "Create & assign"
716
+ };
717
+ //#endregion
718
+ //#region lib/client/index.js
719
+ /** Dictionary namespace owned by this plugin (network control copy). */
720
+ const NS = "a2aNet";
721
+ /** Services required by the browser half: the slot system, the sessions
722
+ * flow used to wake cold rows, and the locale dictionaries. */
723
+ const inject = [
724
+ "slots",
725
+ "sessions",
726
+ "locale"
727
+ ];
728
+ /** Registers the network control's dictionaries and its sidebar seat.
729
+ * @param ctx - Client root context.
730
+ */
731
+ function apply(ctx) {
732
+ ctx.effect(() => ctx.locale.register(NS, {
733
+ zh,
734
+ en
735
+ }), "a2a-net: dictionaries");
736
+ fetch("/__dsh_a2a/state", { cache: "no-store" }).then((response) => response.ok ? response.json() : void 0).then((info) => {
737
+ if (info?.nodes !== true) return;
738
+ ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register({
739
+ name: "sidebar.footer.action",
740
+ id: "a2a",
741
+ order: 10,
742
+ locale: NS,
743
+ inject: () => ({ openSession: (id) => {
744
+ ctx.sessions.open(id);
745
+ } })
746
+ }, A2aControl));
747
+ }).catch(() => {});
748
+ }
749
+ //#endregion
750
+ exports.apply = apply;
751
+ exports.inject = inject;
752
+ return module.exports;
753
+ }
754
+ });
755
+
756
+ //# sourceMappingURL=client.js.map