@aipanel/dsh-plugin 1.2.7 → 1.2.8

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.
Files changed (2) hide show
  1. package/dist/index.js +155 -0
  2. package/package.json +7 -6
package/dist/index.js CHANGED
@@ -502,6 +502,156 @@ async function runProjectDiagnostics(workspace) {
502
502
  return { eslintOutput, tscOutput: mergedTsc };
503
503
  }
504
504
 
505
+ // dsh-plugin/src/events-relay.ts
506
+ var log3 = createNodeLogger("DshEventRelay");
507
+ var DEFAULT_EVENTS_API_PATH = "/__aipanel_host_events__";
508
+ var FLUSH_DELAY_MS = 120;
509
+ var IDLE_DEBOUNCE_MS = 1200;
510
+ function transitionOf(type) {
511
+ switch (type) {
512
+ case "turn/start":
513
+ return { running: true, thinking: true };
514
+ case "step/start":
515
+ case "assistant/chunk":
516
+ return { thinking: true };
517
+ case "assistant/message":
518
+ return { thinking: false };
519
+ case "turn/end":
520
+ return { thinking: false };
521
+ default:
522
+ return null;
523
+ }
524
+ }
525
+ function setupEventRelay(ctx, config) {
526
+ const vitePort = config.vitePort ?? 0;
527
+ const token = config.eventsToken;
528
+ if (!token || vitePort <= 0) return;
529
+ const eventsPath = config.eventsPath ?? DEFAULT_EVENTS_API_PATH;
530
+ const eventsUrl = `http://127.0.0.1:${vitePort}${eventsPath}`;
531
+ const states = /* @__PURE__ */ new Map();
532
+ const lastSent = /* @__PURE__ */ new Map();
533
+ const lastTitles = /* @__PURE__ */ new Map();
534
+ const dirty = /* @__PURE__ */ new Set();
535
+ const idleTimers = /* @__PURE__ */ new Map();
536
+ let flushTimer = null;
537
+ let lastPostErrorAt = 0;
538
+ const scheduleFlush = () => {
539
+ if (flushTimer !== null) return;
540
+ flushTimer = setTimeout(() => {
541
+ flushTimer = null;
542
+ flush();
543
+ }, FLUSH_DELAY_MS);
544
+ flushTimer.unref?.();
545
+ };
546
+ const markDirty = (sessionId) => {
547
+ dirty.add(sessionId);
548
+ scheduleFlush();
549
+ };
550
+ const scheduleIdleCheck = (sessionId) => {
551
+ const existing = idleTimers.get(sessionId);
552
+ if (existing !== void 0) clearTimeout(existing);
553
+ const timer = setTimeout(() => {
554
+ idleTimers.delete(sessionId);
555
+ const s = states.get(sessionId);
556
+ if (s !== void 0 && s.running) {
557
+ s.running = false;
558
+ markDirty(sessionId);
559
+ }
560
+ }, IDLE_DEBOUNCE_MS);
561
+ timer.unref?.();
562
+ idleTimers.set(sessionId, timer);
563
+ };
564
+ const handleSessionEvent = (session, event) => {
565
+ const sessionId = session?.id;
566
+ if (!sessionId || !event || typeof event.type !== "string") return;
567
+ if (event.type === "session/title") {
568
+ const raw = event.data?.title;
569
+ const title = typeof raw === "string" ? raw.trim() : "";
570
+ if (title.length > 0 && title !== lastTitles.get(sessionId)) {
571
+ lastTitles.set(sessionId, title);
572
+ post({
573
+ type: "session.updated",
574
+ session: {
575
+ id: sessionId,
576
+ title,
577
+ // dsh session/event 自带提交时间戳,客户端据此刷新列表 meta(更新时刻)
578
+ updatedAt: typeof event.time === "number" ? event.time : Date.now()
579
+ }
580
+ });
581
+ }
582
+ return;
583
+ }
584
+ const idleTimer = idleTimers.get(sessionId);
585
+ if (idleTimer !== void 0) {
586
+ clearTimeout(idleTimer);
587
+ idleTimers.delete(sessionId);
588
+ }
589
+ const tr = transitionOf(event.type);
590
+ if (tr === null) return;
591
+ let s = states.get(sessionId);
592
+ if (s === void 0) {
593
+ s = { running: false, thinking: false };
594
+ states.set(sessionId, s);
595
+ }
596
+ const next = {
597
+ running: tr.running !== void 0 ? tr.running : s.running,
598
+ thinking: tr.thinking !== void 0 ? tr.thinking : s.thinking
599
+ };
600
+ const changed = next.running !== s.running || next.thinking !== s.thinking;
601
+ s.running = next.running;
602
+ s.thinking = next.thinking;
603
+ if (event.type === "turn/end") scheduleIdleCheck(sessionId);
604
+ if (changed) markDirty(sessionId);
605
+ };
606
+ const flush = () => {
607
+ const pending = [...dirty];
608
+ dirty.clear();
609
+ for (const sessionId of pending) {
610
+ const s = states.get(sessionId);
611
+ if (s === void 0) continue;
612
+ const last = lastSent.get(sessionId);
613
+ const events = [];
614
+ if (last === void 0 || last.running !== s.running) {
615
+ events.push({
616
+ type: "session.status",
617
+ sessionId,
618
+ status: s.running ? "running" : "idle"
619
+ });
620
+ }
621
+ if (last === void 0 || last.thinking !== s.thinking) {
622
+ events.push({ type: "thinking", sessionId, thinking: s.thinking });
623
+ }
624
+ if (events.length === 0) continue;
625
+ lastSent.set(sessionId, { running: s.running, thinking: s.thinking });
626
+ for (const event of events) post(event);
627
+ }
628
+ };
629
+ let inflight = Promise.resolve();
630
+ const post = (event) => {
631
+ const payload = JSON.stringify({ token, event });
632
+ inflight = inflight.then(async () => {
633
+ try {
634
+ const controller = new AbortController();
635
+ const timeout = setTimeout(() => controller.abort(), 1500);
636
+ const res = await fetch(eventsUrl, {
637
+ method: "POST",
638
+ headers: { "content-type": "application/json" },
639
+ body: payload,
640
+ signal: controller.signal
641
+ });
642
+ clearTimeout(timeout);
643
+ if (!res.ok && Date.now() - lastPostErrorAt > 5e3) {
644
+ lastPostErrorAt = Date.now();
645
+ log3.warn("host event relay push failed", { status: res.status });
646
+ }
647
+ } catch {
648
+ }
649
+ });
650
+ };
651
+ const bus = ctx;
652
+ bus.on("session/event", handleSessionEvent, { global: true });
653
+ }
654
+
505
655
  // dsh-plugin/src/index.ts
506
656
  var name = "aipanel";
507
657
  var inject = ["tools"];
@@ -728,6 +878,11 @@ ${contextText}`
728
878
  { prepend: true }
729
879
  );
730
880
  }
881
+ setupEventRelay(ctx, {
882
+ vitePort,
883
+ eventsPath: config.eventsPath,
884
+ eventsToken: config.eventsToken
885
+ });
731
886
  }
732
887
  export {
733
888
  apply,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aipanel/dsh-plugin",
3
- "version": "1.2.7",
3
+ "version": "1.2.8",
4
4
  "type": "module",
5
5
  "description": "AIPanel for DeepSeek Harness (dsh):注入审查工具 run_diagnostics、编辑后自动诊断。",
6
6
  "main": "./dist/index.js",
@@ -15,12 +15,13 @@
15
15
  "vue-tsc": "^3.3.9"
16
16
  },
17
17
  "devDependencies": {
18
- "@deepseek-ai/cordis": "^4.0.1",
19
- "@deepseek-ai/dsh-agent": "^0.1.1-rc.2",
20
- "@deepseek-ai/dsh-llm": "^0.1.1-rc.2",
21
- "@deepseek-ai/dsh-tools": "^0.1.1-rc.2",
18
+ "@deepseek-ai/cordis": "^4.0.2",
19
+ "@deepseek-ai/dsh-agent": "^0.1.2-rc.1",
20
+ "@deepseek-ai/dsh-llm": "^0.1.2-rc.1",
21
+ "@deepseek-ai/dsh-tools": "^0.1.2-rc.1",
22
+ "@deepseek-ai/dsh-util-values": "^0.1.2-rc.1",
22
23
  "esbuild": "^0.25.0",
23
- "@aipanel/core": "1.2.7"
24
+ "@aipanel/core": "1.2.8"
24
25
  },
25
26
  "scripts": {
26
27
  "build": "esbuild src/index.ts --bundle --outfile=dist/index.js --platform=node --format=esm --target=node18 --external:@deepseek-ai/* --external:node:* --external:vue-tsc",