@aipanel/dsh-plugin 1.2.8 → 1.2.9

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 +113 -62
  2. package/package.json +4 -2
package/dist/index.js CHANGED
@@ -3,8 +3,10 @@ import fs2 from "node:fs";
3
3
  import path2 from "node:path";
4
4
  import { randomUUID } from "node:crypto";
5
5
 
6
- // ../../core/es/constants.mjs
6
+ // ../../core/es/common/constants.mjs
7
7
  var LOG_PREFIX = "[vite-plugin-aipanel]";
8
+ var CONTEXT_API_PATH = "/__aipanel_context__";
9
+ var HOST_EVENTS_API_PATH = "/__aipanel_host_events__";
8
10
  var EXT_BROADCAST = {
9
11
  PAGE_CONTEXT: "PAGE_CONTEXT",
10
12
  THEME_CHANGE: "THEME_CHANGE",
@@ -18,12 +20,14 @@ var EXT_MSG = {
18
20
  REQUEST_PAGE_CONTEXT: "REQUEST_PAGE_CONTEXT",
19
21
  SELECTION_START: "SELECTION_START",
20
22
  SELECTION_STOP: "SELECTION_STOP",
21
- CS_QUERY_WINDOW: "__CS_QUERY_WINDOW__"
23
+ CS_QUERY_WINDOW: "__CS_QUERY_WINDOW__",
24
+ /** Side Panel → Background:立即轮询一次并回传当前服务信息 */
25
+ FORCE_POLL: "FORCE_POLL"
22
26
  };
23
27
  var SEVERITY_ERROR = 1;
24
28
  var SEVERITY_WARN = 2;
25
29
 
26
- // ../../core/es/logger-core.mjs
30
+ // ../../core/es/common/logger-core.mjs
27
31
  var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
28
32
  LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
29
33
  LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
@@ -92,7 +96,7 @@ function formatContext(context) {
92
96
  return parts.join(" ");
93
97
  }
94
98
 
95
- // ../../core/es/node-logger.mjs
99
+ // ../../core/es/node/node-logger.mjs
96
100
  var __defProp = Object.defineProperty;
97
101
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
98
102
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -504,20 +508,16 @@ async function runProjectDiagnostics(workspace) {
504
508
 
505
509
  // dsh-plugin/src/events-relay.ts
506
510
  var log3 = createNodeLogger("DshEventRelay");
507
- var DEFAULT_EVENTS_API_PATH = "/__aipanel_host_events__";
508
511
  var FLUSH_DELAY_MS = 120;
509
- var IDLE_DEBOUNCE_MS = 1200;
510
- function transitionOf(type) {
512
+ function thinkingOf(type) {
511
513
  switch (type) {
512
514
  case "turn/start":
513
- return { running: true, thinking: true };
514
515
  case "step/start":
515
516
  case "assistant/chunk":
516
- return { thinking: true };
517
+ return true;
517
518
  case "assistant/message":
518
- return { thinking: false };
519
519
  case "turn/end":
520
- return { thinking: false };
520
+ return false;
521
521
  default:
522
522
  return null;
523
523
  }
@@ -526,13 +526,12 @@ function setupEventRelay(ctx, config) {
526
526
  const vitePort = config.vitePort ?? 0;
527
527
  const token = config.eventsToken;
528
528
  if (!token || vitePort <= 0) return;
529
- const eventsPath = config.eventsPath ?? DEFAULT_EVENTS_API_PATH;
529
+ const eventsPath = config.eventsPath ?? HOST_EVENTS_API_PATH;
530
530
  const eventsUrl = `http://127.0.0.1:${vitePort}${eventsPath}`;
531
531
  const states = /* @__PURE__ */ new Map();
532
532
  const lastSent = /* @__PURE__ */ new Map();
533
533
  const lastTitles = /* @__PURE__ */ new Map();
534
534
  const dirty = /* @__PURE__ */ new Set();
535
- const idleTimers = /* @__PURE__ */ new Map();
536
535
  let flushTimer = null;
537
536
  let lastPostErrorAt = 0;
538
537
  const scheduleFlush = () => {
@@ -547,61 +546,51 @@ function setupEventRelay(ctx, config) {
547
546
  dirty.add(sessionId);
548
547
  scheduleFlush();
549
548
  };
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);
549
+ const ensureState = (sessionId) => {
550
+ let s = states.get(sessionId);
551
+ if (s === void 0) {
552
+ s = { running: false, thinking: false };
553
+ states.set(sessionId, s);
554
+ }
555
+ return s;
556
+ };
557
+ const handleAgentStatus = ({ agent, status }) => {
558
+ const sessionId = String(agent.session.id);
559
+ if (!sessionId) return;
560
+ const running = status === "running";
561
+ const s = ensureState(sessionId);
562
+ if (s.running === running) return;
563
+ s.running = running;
564
+ if (!running) s.thinking = false;
565
+ markDirty(sessionId);
563
566
  };
564
567
  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() : "";
568
+ const sessionId = String(session?.id ?? "");
569
+ if (!sessionId) return;
570
+ const type = typeof event?.type === "string" ? event.type : "";
571
+ if (type === "session/title") {
572
+ const titleData = event.data;
573
+ const title = typeof titleData?.title === "string" ? titleData.title.trim() : "";
570
574
  if (title.length > 0 && title !== lastTitles.get(sessionId)) {
571
575
  lastTitles.set(sessionId, title);
576
+ const ts = event.time;
572
577
  post({
573
578
  type: "session.updated",
574
579
  session: {
575
580
  id: sessionId,
576
581
  title,
577
- // dsh session/event 自带提交时间戳,客户端据此刷新列表 meta(更新时刻)
578
- updatedAt: typeof event.time === "number" ? event.time : Date.now()
582
+ updatedAt: typeof ts === "number" ? ts : Date.now()
579
583
  }
580
584
  });
581
585
  }
582
586
  return;
583
587
  }
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);
588
+ const thinking = thinkingOf(type);
589
+ if (thinking === null) return;
590
+ const s = ensureState(sessionId);
591
+ if (s.thinking === thinking) return;
592
+ s.thinking = thinking;
593
+ markDirty(sessionId);
605
594
  };
606
595
  const flush = () => {
607
596
  const pending = [...dirty];
@@ -648,15 +637,15 @@ function setupEventRelay(ctx, config) {
648
637
  }
649
638
  });
650
639
  };
651
- const bus = ctx;
652
- bus.on("session/event", handleSessionEvent, { global: true });
640
+ ctx.on("agent/status", handleAgentStatus);
641
+ ctx.on("session/event", handleSessionEvent, { global: true });
653
642
  }
654
643
 
655
644
  // dsh-plugin/src/index.ts
656
645
  var name = "aipanel";
657
646
  var inject = ["tools"];
647
+ var log4 = createNodeLogger("DshPlugin");
658
648
  var MUTATING_TOOLS = /* @__PURE__ */ new Set(["write", "edit", "apply_patch"]);
659
- var DEFAULT_CONTEXT_API_PATH = "/__aipanel_context__";
660
649
  function collectNodeIds(text) {
661
650
  const ids = [];
662
651
  const re = new RegExp("@\u8282\u70B9\\[(n[0-9a-z]+)\\]", "g");
@@ -666,13 +655,11 @@ function collectNodeIds(text) {
666
655
  }
667
656
  function buildNodeContext(e) {
668
657
  const lines = [`\u8282\u70B9 ID\uFF1A${e.id ?? ""}`];
669
- if (e.filePath) lines.push(`\u6E90\u7801\u6587\u4EF6\u8DEF\u5F84\uFF1A${e.filePath}${e.line ? `:${e.line}` : ""}`);
670
- if (e.line) lines.push(`\u4EE3\u7801\u6240\u5728\u884C\u53F7\uFF1A${e.line}`);
671
- if (e.column) lines.push(`\u4EE3\u7801\u6240\u5728\u5217\u53F7\uFF1A${e.column}`);
658
+ const loc = e.line ? e.column ? `:${e.line}:${e.column}` : `:${e.line}` : "";
659
+ if (e.filePath) lines.push(`\u6E90\u7801\u6587\u4EF6\u8DEF\u5F84\uFF1A${e.filePath}${loc}`);
672
660
  if (e.description) lines.push(`DOM \u5143\u7D20\u9009\u62E9\u5668\uFF1A${e.description}`);
673
661
  if (e.innerText) lines.push(`DOM \u5143\u7D20\u5185\u90E8\u6587\u672C\uFF1A${e.innerText.slice(0, 200)}`);
674
662
  if (e.previewPageUrl) lines.push(`\u7528\u6237\u9009\u4E2D\u8282\u70B9\u65F6\u7684\u9875\u9762 URL\uFF1A${e.previewPageUrl}`);
675
- if (e.previewPageTitle) lines.push(`\u9875\u9762\u6807\u9898\uFF1A${e.previewPageTitle}`);
676
663
  return lines.join("\n");
677
664
  }
678
665
  function toDiagnosticEntries(items) {
@@ -705,12 +692,74 @@ ${s.text}`).join("\n\n");
705
692
 
706
693
  ${body}` : value.title;
707
694
  }
695
+ function applyProviderSettings(ctx, config) {
696
+ const pending = [];
697
+ if (typeof config.agentPreset === "string" && config.agentPreset) {
698
+ pending.push({ ns: "agent-presets", patch: { default: config.agentPreset } });
699
+ }
700
+ if (typeof config.permissionPreset === "string" && config.permissionPreset) {
701
+ pending.push({ ns: "permission", patch: { defaultPreset: config.permissionPreset } });
702
+ }
703
+ if (typeof config.busyEnter === "string" && config.busyEnter) {
704
+ pending.push({ ns: "ui-conversation", patch: { busyEnter: config.busyEnter } });
705
+ }
706
+ if (pending.length === 0) return;
707
+ const settings = ctx.get("settings");
708
+ if (!settings) {
709
+ log4.warn("settings service unavailable; provider settings not applied via plugin");
710
+ return;
711
+ }
712
+ const registered = (ns) => settings.describe()?.some((d) => String(d.ns) === ns) ?? false;
713
+ const APPLY_TIMEOUT_MS = 12e3;
714
+ const APPLY_INTERVAL_MS = 300;
715
+ const deadline = Date.now() + APPLY_TIMEOUT_MS;
716
+ let timer = null;
717
+ const applied = /* @__PURE__ */ new Set();
718
+ const tick = () => {
719
+ timer = null;
720
+ let stillPending = false;
721
+ for (let i = 0; i < pending.length; i++) {
722
+ if (applied.has(i)) continue;
723
+ const entry = pending[i];
724
+ if (!registered(entry.ns)) {
725
+ stillPending = true;
726
+ continue;
727
+ }
728
+ void settings.update(entry.ns, entry.patch).then(() => {
729
+ applied.add(i);
730
+ log4.debug("applied provider setting via plugin", { ns: entry.ns });
731
+ }).catch((err) => {
732
+ log4.warn("failed to apply provider setting via plugin", {
733
+ ns: entry.ns,
734
+ error: err instanceof Error ? err.message : String(err)
735
+ });
736
+ applied.add(i);
737
+ });
738
+ }
739
+ if (!stillPending && applied.size === pending.length) return;
740
+ if (Date.now() < deadline) {
741
+ timer = setTimeout(tick, APPLY_INTERVAL_MS);
742
+ timer.unref?.();
743
+ } else if (stillPending) {
744
+ log4.warn("provider settings not fully applied: namespace registration timed out", {
745
+ pending: pending.filter((_, i) => !applied.has(i)).map((p) => p.ns)
746
+ });
747
+ }
748
+ };
749
+ tick();
750
+ ctx.effect(
751
+ () => () => {
752
+ if (timer) clearTimeout(timer);
753
+ },
754
+ "aipanel: settings apply timer"
755
+ );
756
+ }
708
757
  function apply(ctx, config = {}) {
709
758
  const cwd = config.cwd ?? process.cwd();
710
759
  const enableDiagnostics = config.enableDiagnostics ?? false;
711
760
  const autoDiagnose = config.autoDiagnose ?? process.env.OPENCODE_ENABLE_LINT === "1";
712
761
  const vitePort = config.vitePort ?? 0;
713
- const contextApiPath = config.contextApiPath ?? DEFAULT_CONTEXT_API_PATH;
762
+ const contextApiPath = config.contextApiPath ?? CONTEXT_API_PATH;
714
763
  const tools = ctx.tools;
715
764
  if (enableDiagnostics) {
716
765
  const diagnosticsTool = {
@@ -883,9 +932,11 @@ ${contextText}`
883
932
  eventsPath: config.eventsPath,
884
933
  eventsToken: config.eventsToken
885
934
  });
935
+ applyProviderSettings(ctx, config);
886
936
  }
887
937
  export {
888
938
  apply,
939
+ applyProviderSettings,
889
940
  inject,
890
941
  name
891
942
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aipanel/dsh-plugin",
3
- "version": "1.2.8",
3
+ "version": "1.2.9",
4
4
  "type": "module",
5
5
  "description": "AIPanel for DeepSeek Harness (dsh):注入审查工具 run_diagnostics、编辑后自动诊断。",
6
6
  "main": "./dist/index.js",
@@ -16,12 +16,14 @@
16
16
  },
17
17
  "devDependencies": {
18
18
  "@deepseek-ai/cordis": "^4.0.2",
19
+ "@deepseek-ai/dsh-settings": "^0.1.2-rc.1",
19
20
  "@deepseek-ai/dsh-agent": "^0.1.2-rc.1",
20
21
  "@deepseek-ai/dsh-llm": "^0.1.2-rc.1",
21
22
  "@deepseek-ai/dsh-tools": "^0.1.2-rc.1",
22
23
  "@deepseek-ai/dsh-util-values": "^0.1.2-rc.1",
24
+ "@deepseek-ai/dsh-session": "^0.1.2-rc.1",
23
25
  "esbuild": "^0.25.0",
24
- "@aipanel/core": "1.2.8"
26
+ "@aipanel/core": "1.2.9"
25
27
  },
26
28
  "scripts": {
27
29
  "build": "esbuild src/index.ts --bundle --outfile=dist/index.js --platform=node --format=esm --target=node18 --external:@deepseek-ai/* --external:node:* --external:vue-tsc",