@sparkelf/dsh-task-bus 0.1.1 → 0.1.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/lib/client.js CHANGED
@@ -22,483 +22,6 @@ window.__ModuleLoader__.load({
22
22
  danger: "var(--dsw-alias-state-danger, #b91c1c)"
23
23
  };
24
24
  //#endregion
25
- //#region src/client/connector-state.ts
26
- /**
27
- * Client state adapter for the Task Center's one connector.
28
- *
29
- * The Host owns the authoritative enabled/capability state and the tool
30
- * disclosure that follows it. This module only reads that state, sends the two
31
- * writes the panel offers, and projects the task rows a capability filter
32
- * selects. It never decides permissions: a capability absent from the Host's
33
- * answer is one this account may not use, so it is never rendered.
34
- *
35
- * @module @sparkelf/dsh-task-bus/client/connector-state
36
- */
37
- /** Route the Host serves the connector state from. */
38
- const CONNECTOR_ROUTE = "/dsh-task-bus/api/connector";
39
- /** Connector id carried by DataOps approval tasks. */
40
- const CONNECTOR_ID = "wanxiang-ai";
41
- /** Display name shared by the card and its empty state. */
42
- const CONNECTOR_NAME = "万相AI中台连接器";
43
- /** Copy for the capability ids the Host currently ships. */
44
- const CAPABILITY_COPY = {
45
- "approval.push": {
46
- label: "接收审批任务",
47
- description: "从万相AI中台接收待办理的审批任务"
48
- },
49
- "approval.writeback": {
50
- label: "回写审批结果",
51
- description: "把办理结果回写到万相AI中台"
52
- },
53
- "task.push": {
54
- label: "接收通用任务",
55
- description: "接收万相AI中台下发的通用任务"
56
- },
57
- "task.writeback": {
58
- label: "回写任务状态",
59
- description: "把任务处理状态回写到万相AI中台"
60
- },
61
- "ai.accept": {
62
- label: "AI 受理",
63
- description: "由助手创建受理会话处理任务"
64
- }
65
- };
66
- /** Humanize an id the client has no display copy for. */
67
- function humanizeCapability(id) {
68
- return id.replace(/[._-]+/gu, " ").trim();
69
- }
70
- /** Parse one unknown wire value into connector state. */
71
- function parseConnectorState(value) {
72
- if (typeof value !== "object" || value === null) return {
73
- enabled: false,
74
- capabilities: {}
75
- };
76
- const row = value;
77
- const capabilities = {};
78
- const reasons = {};
79
- const raw = typeof row.capabilities === "object" && row.capabilities !== null ? row.capabilities : {};
80
- for (const [id, entry] of Object.entries(raw)) {
81
- if (typeof entry === "boolean") {
82
- capabilities[id] = entry;
83
- continue;
84
- }
85
- if (typeof entry !== "object" || entry === null) continue;
86
- const detail = entry;
87
- if (detail.permitted === false) continue;
88
- capabilities[id] = detail.enabled === true;
89
- if (typeof detail.reason === "string" && detail.reason !== "") reasons[id] = detail.reason;
90
- }
91
- const rawReasons = typeof row.reasons === "object" && row.reasons !== null ? row.reasons : {};
92
- for (const [id, reason] of Object.entries(rawReasons)) if (typeof reason === "string" && reason !== "") reasons[id] = reason;
93
- return {
94
- enabled: row.enabled === true,
95
- capabilities,
96
- ...Object.keys(reasons).length === 0 ? {} : { reasons }
97
- };
98
- }
99
- /**
100
- * Read the Host's connector state.
101
- * @param route - connector route; overridable for tests.
102
- * @returns the parsed state.
103
- * @throws when the Host rejects the request.
104
- */
105
- async function fetchConnectorState(route = CONNECTOR_ROUTE) {
106
- const response = await fetch(route, { headers: { accept: "application/json" } });
107
- if (!response.ok) throw new Error("连接器状态读取失败(" + String(response.status) + ")");
108
- return parseConnectorState(await response.json());
109
- }
110
- /**
111
- * Write connector state through the Host.
112
- * @param patch - the enabled flag and optional capability changes to merge.
113
- * @param route - connector route; overridable for tests.
114
- * @returns the state the Host persisted.
115
- * @throws when the Host rejects the request.
116
- */
117
- async function saveConnectorState(patch, route = CONNECTOR_ROUTE) {
118
- const response = await fetch(route, {
119
- method: "POST",
120
- headers: {
121
- accept: "application/json",
122
- "content-type": "application/json"
123
- },
124
- body: JSON.stringify(patch)
125
- });
126
- if (!response.ok) throw new Error("连接器状态保存失败(" + String(response.status) + ")");
127
- return parseConnectorState(await response.json());
128
- }
129
- /**
130
- * Project the capability rows the card renders.
131
- * @param state - Host state.
132
- * @returns one row per capability the account may use, in the Host's order.
133
- */
134
- function visibleCapabilities(state) {
135
- if (state === null) return [];
136
- return Object.entries(state.capabilities).map(([id, enabled]) => {
137
- const copy = CAPABILITY_COPY[id];
138
- return {
139
- id,
140
- label: copy?.label ?? humanizeCapability(id),
141
- ...copy === void 0 ? {} : { description: copy.description },
142
- enabled,
143
- ...state.reasons?.[id] === void 0 ? {} : { unavailableReason: state.reasons[id] }
144
- };
145
- });
146
- }
147
- /**
148
- * The connector that produced a task row. MVP DataOps approval rows predate
149
- * explicit ids, so their source type still identifies the built-in connector.
150
- * @param task - one task row.
151
- * @returns the connector id, or undefined for a generic Task Bus row.
152
- */
153
- function taskConnectorId(task) {
154
- if (task.connectorId !== void 0 && task.connectorId !== "") return task.connectorId;
155
- return task.sourceType === "dataops_approval" ? CONNECTOR_ID : void 0;
156
- }
157
- /**
158
- * The capability that produced a task row; an id-less DataOps approval row
159
- * belongs to `approval.push`.
160
- * @param task - one task row.
161
- * @returns the capability id, or undefined for a generic Task Bus row.
162
- */
163
- function taskCapabilityId(task) {
164
- if (task.capabilityId !== void 0 && task.capabilityId !== "") return task.capabilityId;
165
- return task.sourceType === "dataops_approval" ? "approval.push" : void 0;
166
- }
167
- /**
168
- * Filter rows for the connector tab.
169
- * @param tasks - every loaded row.
170
- * @param capabilityId - `all`, or the capability whose rows should remain.
171
- * @returns rows produced by the built-in connector, narrowed to the capability.
172
- */
173
- function filterConnectorTasks(tasks, capabilityId) {
174
- return tasks.filter((task) => taskConnectorId(task) === "wanxiang-ai" && (capabilityId === "all" || taskCapabilityId(task) === capabilityId));
175
- }
176
- //#endregion
177
- //#region src/client/ConnectorCard.tsx
178
- /** Status label and tone for the card header. */
179
- function statusOf(state, loading, error) {
180
- if (loading && state === null) return {
181
- label: "同步中",
182
- tone: "neutral"
183
- };
184
- if (error !== null) return {
185
- label: "连接异常",
186
- tone: "danger"
187
- };
188
- if (state?.enabled !== true) return {
189
- label: "未启用",
190
- tone: "neutral"
191
- };
192
- return {
193
- label: "已连接",
194
- tone: "success"
195
- };
196
- }
197
- /** Draw the connector glyph the card header carries. */
198
- function ConnectorGlyph() {
199
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
200
- width: "18",
201
- height: "18",
202
- viewBox: "0 0 24 24",
203
- fill: "none",
204
- stroke: "currentColor",
205
- strokeWidth: "1.8",
206
- strokeLinecap: "round",
207
- strokeLinejoin: "round",
208
- "aria-hidden": "true",
209
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M10 13a5 5 0 0 0 7.54.54l2-2a5 5 0 0 0-7.07-7.07l-1.15 1.15" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-2 2a5 5 0 0 0 7.07 7.07l1.15-1.15" })]
210
- });
211
- }
212
- /**
213
- * Render the connector card.
214
- * @param props - server state, write handlers, and the current task count.
215
- * @returns the card.
216
- */
217
- function ConnectorCard({ state, loading, error, busy, taskCount, onSetEnabled, onToggleCapability, onReload }) {
218
- const status = statusOf(state, loading, error);
219
- const enabled = state?.enabled === true;
220
- const capabilities = visibleCapabilities(state);
221
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
222
- "data-connector-card": CONNECTOR_NAME,
223
- "data-connector-enabled": enabled,
224
- style: {
225
- width: "100%",
226
- minWidth: 0,
227
- border: "1px solid " + colors.line,
228
- borderRadius: 12,
229
- background: colors.panelAlt,
230
- padding: 14,
231
- display: "flex",
232
- flexDirection: "column",
233
- gap: 10
234
- },
235
- children: [
236
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
237
- style: {
238
- display: "flex",
239
- alignItems: "center",
240
- gap: 9
241
- },
242
- children: [
243
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
244
- "aria-hidden": "true",
245
- style: {
246
- width: 32,
247
- height: 32,
248
- flex: "none",
249
- borderRadius: 9,
250
- background: "#2563eb",
251
- color: "#fff",
252
- display: "inline-flex",
253
- alignItems: "center",
254
- justifyContent: "center"
255
- },
256
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConnectorGlyph, {})
257
- }),
258
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
259
- style: {
260
- flex: 1,
261
- minWidth: 0
262
- },
263
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
264
- style: {
265
- fontSize: 13,
266
- fontWeight: 700,
267
- color: colors.text
268
- },
269
- children: CONNECTOR_NAME
270
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
271
- style: {
272
- marginTop: 2,
273
- fontSize: 10,
274
- color: colors.muted
275
- },
276
- children: "DataOps"
277
- })]
278
- }),
279
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
280
- tone: status.tone,
281
- children: status.label
282
- })
283
- ]
284
- }),
285
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
286
- style: {
287
- fontSize: 11,
288
- lineHeight: 1.55,
289
- color: colors.muted
290
- },
291
- children: "接收万相AI中台的审批任务"
292
- }),
293
- loading && state === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
294
- style: {
295
- fontSize: 11,
296
- color: colors.muted
297
- },
298
- children: "正在读取连接器状态…"
299
- }) : null,
300
- error === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
301
- role: "alert",
302
- style: {
303
- display: "flex",
304
- alignItems: "center",
305
- gap: 8,
306
- fontSize: 11,
307
- color: colors.danger
308
- },
309
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
310
- style: {
311
- flex: 1,
312
- minWidth: 0
313
- },
314
- children: error
315
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
316
- size: "sm",
317
- variant: "ghost",
318
- disabled: loading,
319
- onClick: onReload,
320
- children: "重试"
321
- })]
322
- }),
323
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
324
- style: {
325
- display: "flex",
326
- alignItems: "center",
327
- justifyContent: "space-between",
328
- gap: 10,
329
- minHeight: 28
330
- },
331
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
332
- style: {
333
- fontSize: 12,
334
- color: colors.muted
335
- },
336
- children: enabled ? "已启用,任务中心只读取该连接器的任务" : "未启用,任务中心不读取外部任务"
337
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
338
- checked: enabled,
339
- disabled: busy || loading,
340
- label: (enabled ? "停用 " : "启用 ") + CONNECTOR_NAME,
341
- onChange: (next) => {
342
- onSetEnabled(next);
343
- }
344
- })]
345
- }),
346
- enabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
347
- "data-connector-capabilities": true,
348
- style: {
349
- borderTop: "1px solid " + colors.line,
350
- paddingTop: 8,
351
- display: "grid",
352
- gap: 2
353
- },
354
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
355
- style: {
356
- fontSize: 10,
357
- color: colors.muted
358
- },
359
- children: "能力"
360
- }), capabilities.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
361
- style: {
362
- fontSize: 11,
363
- color: colors.muted,
364
- padding: "6px 0"
365
- },
366
- children: "当前账号没有可用的连接器能力,请联系管理员开通"
367
- }) : capabilities.map((capability) => {
368
- const unavailable = capability.unavailableReason !== void 0;
369
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
370
- "data-capability-id": capability.id,
371
- "data-capability-enabled": capability.enabled,
372
- style: {
373
- display: "flex",
374
- alignItems: "center",
375
- gap: 10,
376
- minHeight: 40,
377
- padding: "4px 0",
378
- borderTop: "1px solid " + colors.line
379
- },
380
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
381
- style: {
382
- flex: 1,
383
- minWidth: 0
384
- },
385
- children: [
386
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
387
- style: {
388
- fontSize: 12,
389
- color: colors.text
390
- },
391
- children: capability.label
392
- }),
393
- capability.description === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
394
- style: {
395
- marginTop: 1,
396
- fontSize: 10,
397
- color: colors.muted
398
- },
399
- children: capability.description
400
- }),
401
- capability.unavailableReason === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
402
- style: {
403
- marginTop: 2,
404
- fontSize: 10,
405
- color: colors.warning
406
- },
407
- children: ["暂不可用 · ", capability.unavailableReason]
408
- })
409
- ]
410
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
411
- checked: capability.enabled,
412
- disabled: busy || unavailable,
413
- label: (capability.enabled ? "停用 " : "启用 ") + capability.label,
414
- onChange: (next) => {
415
- onToggleCapability(capability.id, next);
416
- }
417
- })]
418
- }, capability.id);
419
- })]
420
- }) : null,
421
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
422
- style: {
423
- display: "flex",
424
- alignItems: "center",
425
- justifyContent: "space-between",
426
- gap: 8,
427
- marginTop: "auto",
428
- fontSize: 10,
429
- color: colors.muted
430
- },
431
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: enabled ? "该连接器任务 " + String(taskCount) + " 条" : "启用后在此查看连接器任务" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
432
- size: "sm",
433
- variant: "ghost",
434
- disabled: loading,
435
- onClick: onReload,
436
- children: "刷新状态"
437
- })]
438
- })
439
- ]
440
- });
441
- }
442
- /**
443
- * The disabled connector's empty state, with the one path that can enable it.
444
- * @param props.onOpenSkillCenter - select the skill center's connector tab.
445
- * @returns the empty state.
446
- */
447
- function ConnectorDisabledState({ onOpenSkillCenter }) {
448
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
449
- "data-connector-disabled-state": true,
450
- style: {
451
- display: "grid",
452
- justifyItems: "center",
453
- gap: 8,
454
- padding: "36px 20px 40px",
455
- color: colors.muted,
456
- textAlign: "center"
457
- },
458
- children: [
459
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
460
- style: {
461
- display: "inline-flex",
462
- width: 36,
463
- height: 36,
464
- alignItems: "center",
465
- justifyContent: "center",
466
- borderRadius: 18,
467
- background: colors.panelAlt,
468
- color: colors.muted
469
- },
470
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConnectorGlyph, {})
471
- }),
472
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
473
- style: {
474
- fontSize: 13,
475
- fontWeight: 600,
476
- color: colors.text
477
- },
478
- children: "连接器未启用"
479
- }),
480
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
481
- style: {
482
- maxWidth: 380,
483
- fontSize: 12,
484
- lineHeight: 1.6
485
- },
486
- children: [
487
- "在技能中心的连接器页面启用「",
488
- CONNECTOR_NAME,
489
- "」后,这里会显示它接收的审批任务。"
490
- ]
491
- }),
492
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
493
- size: "sm",
494
- variant: "outline",
495
- onClick: onOpenSkillCenter,
496
- children: "前往技能中心启用"
497
- })
498
- ]
499
- });
500
- }
501
- //#endregion
502
25
  //#region src/client/index.tsx
503
26
  /**
504
27
  * The Task Center surface in the frame-wide overlay.
@@ -512,8 +35,7 @@ window.__ModuleLoader__.load({
512
35
  "locale",
513
36
  "uiWorkspace",
514
37
  "sessions",
515
- "sidebarRight",
516
- "layout"
38
+ "sidebarRight"
517
39
  ];
518
40
  const ROUTE = "/dsh-task-bus/api";
519
41
  const TASK_BUS_MODAL_CLASS = "task-bus-center-modal";
@@ -585,26 +107,7 @@ window.__ModuleLoader__.load({
585
107
  dateLocale: "en-US"
586
108
  }
587
109
  };
588
- const buttonStyle = {
589
- border: "1px solid " + colors.line,
590
- background: colors.panel,
591
- color: colors.text,
592
- borderRadius: 8,
593
- padding: "7px 11px",
594
- fontSize: 12,
595
- cursor: "pointer"
596
- };
597
- /** Capability filter chip; the active chip keeps the accent fill and stroke. */
598
- const filterChipStyle = (active) => ({
599
- padding: "4px 10px",
600
- borderRadius: 999,
601
- border: "1px solid " + (active ? colors.accent : colors.line),
602
- background: active ? colors.accentSoft : "transparent",
603
- color: active ? colors.accent : colors.muted,
604
- fontSize: 11,
605
- lineHeight: 1.6,
606
- cursor: "pointer"
607
- });
110
+ "" + colors.line, colors.panel, colors.text;
608
111
  const statusTagTone = (status) => {
609
112
  if (status === "completed") return "success";
610
113
  if (status === "cancelled") return "neutral";
@@ -1196,7 +699,7 @@ window.__ModuleLoader__.load({
1196
699
  ]
1197
700
  });
1198
701
  };
1199
- const TaskCenterButton = ({ openSession, openSkillCenter }) => {
702
+ const TaskCenterButton = ({ openSession }) => {
1200
703
  const [open, setOpen] = (0, react.useState)(false);
1201
704
  const [tasks, setTasks] = (0, react.useState)([]);
1202
705
  const [loading, setLoading] = (0, react.useState)(false);
@@ -1204,20 +707,9 @@ window.__ModuleLoader__.load({
1204
707
  const [tab, setTab] = (0, react.useState)("all");
1205
708
  const [busyId, setBusyId] = (0, react.useState)(null);
1206
709
  const [aiBusyId, setAiBusyId] = (0, react.useState)(null);
1207
- const [creating, setCreating] = (0, react.useState)(false);
1208
- const [draftTitle, setDraftTitle] = (0, react.useState)("");
1209
- const [draftSource, setDraftSource] = (0, react.useState)("manual");
1210
- const [createBusy, setCreateBusy] = (0, react.useState)(false);
1211
710
  const [selected, setSelected] = (0, react.useState)(null);
1212
711
  const [anchor, setAnchor] = (0, react.useState)(null);
1213
- const [connector, setConnector] = (0, react.useState)(null);
1214
- const [connectorLoading, setConnectorLoading] = (0, react.useState)(true);
1215
- const [connectorError, setConnectorError] = (0, react.useState)(null);
1216
- const [connectorBusy, setConnectorBusy] = (0, react.useState)(false);
1217
- const [capabilityFilter, setCapabilityFilter] = (0, react.useState)("all");
1218
- const [skillCenterError, setSkillCenterError] = (0, react.useState)(null);
1219
712
  const [pendingCount, setPendingCount] = (0, react.useState)(0);
1220
- const connectorEnabled = connector?.enabled === true;
1221
713
  (0, react.useEffect)(() => {
1222
714
  if (document.getElementById(TASK_BUS_STYLE_ID) !== null) return;
1223
715
  const style = document.createElement("style");
@@ -1260,39 +752,7 @@ window.__ModuleLoader__.load({
1260
752
  window.removeEventListener("resize", update);
1261
753
  };
1262
754
  }, []);
1263
- const readConnector = async () => {
1264
- setConnectorLoading(true);
1265
- setConnectorError(null);
1266
- try {
1267
- setConnector(await fetchConnectorState());
1268
- } catch (cause) {
1269
- setConnectorError(cause instanceof Error ? cause.message : String(cause));
1270
- } finally {
1271
- setConnectorLoading(false);
1272
- }
1273
- };
1274
- (0, react.useEffect)(() => {
1275
- let alive = true;
1276
- setConnectorLoading(true);
1277
- setConnectorError(null);
1278
- fetchConnectorState().then((next) => {
1279
- if (alive) setConnector(next);
1280
- }).catch((cause) => {
1281
- if (alive) setConnectorError(cause instanceof Error ? cause.message : String(cause));
1282
- }).finally(() => {
1283
- if (alive) setConnectorLoading(false);
1284
- });
1285
- return () => {
1286
- alive = false;
1287
- };
1288
- }, []);
1289
755
  const load = async (silent = false) => {
1290
- if (!connectorEnabled) {
1291
- setTasks([]);
1292
- setLoading(false);
1293
- setError(null);
1294
- return;
1295
- }
1296
756
  if (!silent) setLoading(true);
1297
757
  setError(null);
1298
758
  try {
@@ -1304,53 +764,8 @@ window.__ModuleLoader__.load({
1304
764
  if (!silent) setLoading(false);
1305
765
  }
1306
766
  };
1307
- const setConnectorEnabled = async (enabled) => {
1308
- setConnectorBusy(true);
1309
- setConnectorError(null);
1310
- try {
1311
- setConnector(await saveConnectorState({ enabled }));
1312
- if (!enabled) {
1313
- setTasks([]);
1314
- setCapabilityFilter("all");
1315
- }
1316
- } catch (cause) {
1317
- setConnectorError(cause instanceof Error ? cause.message : String(cause));
1318
- } finally {
1319
- setConnectorBusy(false);
1320
- }
1321
- };
1322
- const toggleCapability = async (capabilityId, enabled) => {
1323
- if (connector === null) return;
1324
- setConnectorBusy(true);
1325
- setConnectorError(null);
1326
- try {
1327
- setConnector(await saveConnectorState({
1328
- enabled: connector.enabled,
1329
- capabilities: { [capabilityId]: enabled }
1330
- }));
1331
- } catch (cause) {
1332
- setConnectorError(cause instanceof Error ? cause.message : String(cause));
1333
- } finally {
1334
- setConnectorBusy(false);
1335
- }
1336
- };
1337
- const openSkillCenterFromPanel = () => {
1338
- if (openSkillCenter()) {
1339
- setSkillCenterError(null);
1340
- setOpen(false);
1341
- return;
1342
- }
1343
- setSkillCenterError("技能中心未安装,无法打开连接器页面");
1344
- };
1345
767
  (0, react.useEffect)(() => {
1346
768
  if (!open) return;
1347
- if (!connectorEnabled) {
1348
- setTab("connectors");
1349
- setTasks([]);
1350
- setError(null);
1351
- setLoading(false);
1352
- return;
1353
- }
1354
769
  load();
1355
770
  const timer = window.setInterval(() => {
1356
771
  load(true);
@@ -1358,12 +773,8 @@ window.__ModuleLoader__.load({
1358
773
  return () => {
1359
774
  window.clearInterval(timer);
1360
775
  };
1361
- }, [open, connectorEnabled]);
776
+ }, [open]);
1362
777
  (0, react.useEffect)(() => {
1363
- if (!connectorEnabled) {
1364
- setPendingCount(0);
1365
- return;
1366
- }
1367
778
  let alive = true;
1368
779
  const refresh = async () => {
1369
780
  try {
@@ -1379,19 +790,13 @@ window.__ModuleLoader__.load({
1379
790
  alive = false;
1380
791
  window.clearInterval(timer);
1381
792
  };
1382
- }, [connectorEnabled]);
793
+ }, []);
1383
794
  const filtered = (0, react.useMemo)(() => {
1384
- if (tab === "connectors") return connectorEnabled ? filterConnectorTasks(tasks, capabilityFilter) : [];
1385
795
  if (tab === "group") return tasks.filter((task) => task.sourceType === "task_bus" && task.source.type.includes("group"));
1386
796
  if (tab === "mine") return tasks.filter((task) => task.status !== "completed" && task.status !== "cancelled");
1387
797
  if (tab === "personal") return tasks.filter((task) => task.sourceType === "task_bus" && !task.source.type.includes("group"));
1388
798
  return tasks;
1389
- }, [
1390
- tasks,
1391
- tab,
1392
- connectorEnabled,
1393
- capabilityFilter
1394
- ]);
799
+ }, [tasks, tab]);
1395
800
  const actionable = tasks.filter((task) => task.status !== "completed" && task.status !== "cancelled").length;
1396
801
  const tabItems = [
1397
802
  {
@@ -1429,15 +834,6 @@ window.__ModuleLoader__.load({
1429
834
  style: { opacity: .55 },
1430
835
  children: tasks.filter((task) => task.sourceType === "task_bus" && task.source.type.includes("group")).length
1431
836
  })] })
1432
- },
1433
- {
1434
- value: "connectors",
1435
- id: "task-bus-tab-connectors",
1436
- panelId: "task-bus-panel-connectors",
1437
- label: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["连接器 ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1438
- style: { opacity: .55 },
1439
- children: connector === null ? 0 : 1
1440
- })] })
1441
837
  }
1442
838
  ];
1443
839
  const transition = async (task, action) => {
@@ -1461,31 +857,6 @@ window.__ModuleLoader__.load({
1461
857
  setBusyId(null);
1462
858
  }
1463
859
  };
1464
- const createTask = async () => {
1465
- const title = draftTitle.trim();
1466
- if (!title) return;
1467
- setCreateBusy(true);
1468
- setError(null);
1469
- try {
1470
- const response = await fetch(ROUTE + "/generic", {
1471
- method: "POST",
1472
- headers: { "content-type": "application/json" },
1473
- body: JSON.stringify({
1474
- title,
1475
- sourceType: draftSource
1476
- })
1477
- });
1478
- const payload = await response.json();
1479
- if (!response.ok) throw new Error(payload.error ?? "创建任务失败");
1480
- setDraftTitle("");
1481
- setCreating(false);
1482
- await load();
1483
- } catch (cause) {
1484
- setError(cause instanceof Error ? cause.message : String(cause));
1485
- } finally {
1486
- setCreateBusy(false);
1487
- }
1488
- };
1489
860
  const aiAccept = async (task) => {
1490
861
  setAiBusyId(task.id);
1491
862
  setError(null);
@@ -1596,13 +967,6 @@ window.__ModuleLoader__.load({
1596
967
  children: "任务中心"
1597
968
  })
1598
969
  }),
1599
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1600
- size: "sm",
1601
- variant: "outline",
1602
- disabled: !connectorEnabled,
1603
- onClick: () => setCreating((value) => !value),
1604
- children: "新建"
1605
- }),
1606
970
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1607
971
  size: "sm",
1608
972
  variant: "ghost",
@@ -1635,69 +999,6 @@ window.__ModuleLoader__.load({
1635
999
  label: "任务中心分类"
1636
1000
  })
1637
1001
  }),
1638
- creating ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1639
- style: {
1640
- margin: "0 24px 12px",
1641
- padding: 12,
1642
- border: "1px solid " + colors.line,
1643
- borderRadius: 9,
1644
- display: "grid",
1645
- gap: 7
1646
- },
1647
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1648
- value: draftTitle,
1649
- onChange: (event) => setDraftTitle(event.target.value),
1650
- placeholder: "任务标题,例如:整理演示材料",
1651
- autoFocus: true,
1652
- style: {
1653
- ...buttonStyle,
1654
- cursor: "text",
1655
- padding: "7px 9px"
1656
- }
1657
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1658
- style: {
1659
- display: "flex",
1660
- gap: 6
1661
- },
1662
- children: [
1663
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1664
- value: draftSource,
1665
- onChange: (event) => setDraftSource(event.target.value),
1666
- style: {
1667
- ...buttonStyle,
1668
- padding: "6px 8px"
1669
- },
1670
- children: [
1671
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1672
- value: "manual",
1673
- children: "手工任务"
1674
- }),
1675
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1676
- value: "session",
1677
- children: "会话任务"
1678
- }),
1679
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1680
- value: "group",
1681
- children: "群聊任务"
1682
- })
1683
- ]
1684
- }),
1685
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1686
- size: "sm",
1687
- variant: "primary",
1688
- disabled: createBusy || draftTitle.trim() === "",
1689
- onClick: () => void createTask(),
1690
- children: createBusy ? "创建中…" : "创建"
1691
- }),
1692
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1693
- size: "sm",
1694
- variant: "ghost",
1695
- onClick: () => setCreating(false),
1696
- children: "取消"
1697
- })
1698
- ]
1699
- })]
1700
- }) : null,
1701
1002
  error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1702
1003
  style: {
1703
1004
  padding: "8px 10px",
@@ -1725,59 +1026,6 @@ window.__ModuleLoader__.load({
1725
1026
  task: selected,
1726
1027
  onBack: () => setSelected(null)
1727
1028
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1728
- tab === "connectors" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1729
- style: {
1730
- display: "grid",
1731
- gridTemplateColumns: "repeat(auto-fill, minmax(320px, 1fr))",
1732
- gap: 10
1733
- },
1734
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConnectorCard, {
1735
- state: connector,
1736
- loading: connectorLoading,
1737
- error: connectorError,
1738
- busy: connectorBusy,
1739
- taskCount: filtered.length,
1740
- onSetEnabled: (enabled) => {
1741
- setConnectorEnabled(enabled);
1742
- },
1743
- onToggleCapability: (capabilityId, enabled) => {
1744
- toggleCapability(capabilityId, enabled);
1745
- },
1746
- onReload: () => {
1747
- readConnector();
1748
- }
1749
- })
1750
- }), connectorEnabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1751
- role: "group",
1752
- "aria-label": "连接器能力筛选",
1753
- style: {
1754
- display: "flex",
1755
- flexWrap: "wrap",
1756
- gap: 6
1757
- },
1758
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1759
- type: "button",
1760
- "aria-pressed": capabilityFilter === "all",
1761
- onClick: () => setCapabilityFilter("all"),
1762
- style: filterChipStyle(capabilityFilter === "all"),
1763
- children: "全部"
1764
- }), visibleCapabilities(connector).map((capability) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1765
- type: "button",
1766
- "data-capability-filter": capability.id,
1767
- "aria-pressed": capabilityFilter === capability.id,
1768
- onClick: () => setCapabilityFilter(capability.id),
1769
- style: filterChipStyle(capabilityFilter === capability.id),
1770
- children: capability.label
1771
- }, capability.id))]
1772
- }) : connectorLoading ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConnectorDisabledState, { onOpenSkillCenter: openSkillCenterFromPanel }), skillCenterError === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1773
- role: "alert",
1774
- style: {
1775
- textAlign: "center",
1776
- color: colors.danger,
1777
- fontSize: 11
1778
- },
1779
- children: skillCenterError
1780
- })] })] }) : null,
1781
1029
  loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1782
1030
  style: {
1783
1031
  padding: 18,
@@ -1787,7 +1035,7 @@ window.__ModuleLoader__.load({
1787
1035
  },
1788
1036
  children: "正在加载…"
1789
1037
  }) : null,
1790
- !loading && tab !== "connectors" && filtered.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1038
+ !loading && filtered.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1791
1039
  style: {
1792
1040
  padding: 18,
1793
1041
  textAlign: "center",
@@ -1796,22 +1044,6 @@ window.__ModuleLoader__.load({
1796
1044
  },
1797
1045
  children: "暂无任务"
1798
1046
  }) : null,
1799
- !loading && tab === "connectors" && connectorEnabled && filtered.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1800
- style: {
1801
- padding: 18,
1802
- textAlign: "center",
1803
- color: colors.muted,
1804
- fontSize: 12
1805
- },
1806
- children: capabilityFilter === "all" ? "暂无来自该连接器的任务" : "该能力下暂无任务"
1807
- }) : null,
1808
- !loading && tab === "connectors" && filtered.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1809
- style: {
1810
- fontSize: 10,
1811
- color: colors.muted
1812
- },
1813
- children: "来自该连接器的任务"
1814
- }) : null,
1815
1047
  !loading && filtered.map((task) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TaskCard, {
1816
1048
  task,
1817
1049
  busy: busyId === task.id,
@@ -1899,34 +1131,6 @@ window.__ModuleLoader__.load({
1899
1131
  reference.release?.();
1900
1132
  }
1901
1133
  };
1902
- const HistoryGlyph = () => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
1903
- width: "16",
1904
- height: "16",
1905
- viewBox: "0 0 16 16",
1906
- fill: "none",
1907
- xmlns: "http://www.w3.org/2000/svg",
1908
- "aria-hidden": "true",
1909
- strokeWidth: "1.5",
1910
- children: [
1911
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1912
- d: "M2.4 6.8A5.6 5.6 0 1 1 2.4 9.2",
1913
- stroke: "currentColor",
1914
- strokeLinecap: "round"
1915
- }),
1916
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1917
- d: "M2.2 3.4v3.4h3.4",
1918
- stroke: "currentColor",
1919
- strokeLinecap: "round",
1920
- strokeLinejoin: "round"
1921
- }),
1922
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1923
- d: "M8 5.4V8l1.9 1.2",
1924
- stroke: "currentColor",
1925
- strokeLinecap: "round",
1926
- strokeLinejoin: "round"
1927
- })
1928
- ]
1929
- });
1930
1134
  const fileAttachmentIn = (content, name) => {
1931
1135
  if (!Array.isArray(content)) return void 0;
1932
1136
  for (const block of content) {
@@ -1969,6 +1173,35 @@ window.__ModuleLoader__.load({
1969
1173
  reference.release?.();
1970
1174
  }
1971
1175
  };
1176
+ /**
1177
+ * The design system's standard 16-unit clock glyph, drawn locally.
1178
+ *
1179
+ * The client primitives package this runtime serves (0.2.2-alpha.2) exports
1180
+ * `IconClockOutline`, `IconClockOutlineRegular`, and `IconClockOutlineMedium`,
1181
+ * not the newer `IconClockOutline16` name. A missing named export resolves to
1182
+ * `undefined`, so importing it crashed this header slot with React error #130
1183
+ * and removed the button entirely; the standard geometry is inlined instead.
1184
+ */
1185
+ const MessageHistoryIcon = ({ size = 16 }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
1186
+ width: size,
1187
+ height: size,
1188
+ viewBox: "0 0 16 16",
1189
+ fill: "none",
1190
+ xmlns: "http://www.w3.org/2000/svg",
1191
+ "aria-hidden": "true",
1192
+ strokeWidth: "1.5",
1193
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
1194
+ cx: "8",
1195
+ cy: "8",
1196
+ r: "6.375",
1197
+ stroke: "currentColor"
1198
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1199
+ d: "M8 4.4V8.3L10.7 9.85",
1200
+ stroke: "currentColor",
1201
+ strokeLinecap: "round",
1202
+ strokeLinejoin: "round"
1203
+ })]
1204
+ });
1972
1205
  const HistoryButton = ({ sessionId, sessions, t }) => {
1973
1206
  const [open, setOpen] = (0, react.useState)(false);
1974
1207
  const [entries, setEntries] = (0, react.useState)([]);
@@ -2061,7 +1294,7 @@ window.__ModuleLoader__.load({
2061
1294
  color: "inherit",
2062
1295
  cursor: "pointer"
2063
1296
  },
2064
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HistoryGlyph, {})
1297
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageHistoryIcon, { size: 16 })
2065
1298
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
2066
1299
  open,
2067
1300
  onClose: () => setOpen(false),
@@ -2159,7 +1392,7 @@ window.__ModuleLoader__.load({
2159
1392
  background: colors.panelAlt,
2160
1393
  color: colors.muted
2161
1394
  },
2162
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HistoryGlyph, {})
1395
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageHistoryIcon, { size: 16 })
2163
1396
  }),
2164
1397
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2165
1398
  style: {
@@ -2286,18 +1519,7 @@ window.__ModuleLoader__.load({
2286
1519
  document.removeEventListener("click", onFileClick, true);
2287
1520
  };
2288
1521
  }, "task-bus: native file card preview");
2289
- const openSkillCenter = () => {
2290
- try {
2291
- surface.layout.selectPanel("skill-center");
2292
- return true;
2293
- } catch {
2294
- return false;
2295
- }
2296
- };
2297
- const Surface = () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TaskCenterButton, {
2298
- openSession: (sessionId) => surface.uiWorkspace.openSession(sessionId),
2299
- openSkillCenter
2300
- });
1522
+ const Surface = () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TaskCenterButton, { openSession: (sessionId) => surface.uiWorkspace.openSession(sessionId) });
2301
1523
  ctx.effect(() => surface.slots.inject("shell.overlay", () => surface.slots.register({
2302
1524
  name: "shell.overlay",
2303
1525
  id: "task-bus.center"
@@ -2305,7 +1527,7 @@ window.__ModuleLoader__.load({
2305
1527
  ctx.effect(() => {
2306
1528
  const style = document.createElement("style");
2307
1529
  style.id = "task-bus-history-replaces-file-manager";
2308
- style.textContent = "button[aria-label=\"用 文件管理器 打开\"] { display: none !important; }\n[data-message-attachments] > * { cursor: pointer; }";
1530
+ style.textContent = ["[data-slot=\"conversation.session.header.utilities\"] > *:has([data-open-target]) { display: none !important; }", "[data-message-attachments] > * { cursor: pointer; }"].join("\n");
2309
1531
  document.head.appendChild(style);
2310
1532
  return () => {
2311
1533
  style.remove();