@aipanel/dsh-plugin 1.2.7 → 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 +216 -10
  2. package/package.json +9 -6
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);
@@ -502,11 +506,146 @@ async function runProjectDiagnostics(workspace) {
502
506
  return { eslintOutput, tscOutput: mergedTsc };
503
507
  }
504
508
 
509
+ // dsh-plugin/src/events-relay.ts
510
+ var log3 = createNodeLogger("DshEventRelay");
511
+ var FLUSH_DELAY_MS = 120;
512
+ function thinkingOf(type) {
513
+ switch (type) {
514
+ case "turn/start":
515
+ case "step/start":
516
+ case "assistant/chunk":
517
+ return true;
518
+ case "assistant/message":
519
+ case "turn/end":
520
+ return 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 ?? HOST_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
+ let flushTimer = null;
536
+ let lastPostErrorAt = 0;
537
+ const scheduleFlush = () => {
538
+ if (flushTimer !== null) return;
539
+ flushTimer = setTimeout(() => {
540
+ flushTimer = null;
541
+ flush();
542
+ }, FLUSH_DELAY_MS);
543
+ flushTimer.unref?.();
544
+ };
545
+ const markDirty = (sessionId) => {
546
+ dirty.add(sessionId);
547
+ scheduleFlush();
548
+ };
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);
566
+ };
567
+ const handleSessionEvent = (session, event) => {
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() : "";
574
+ if (title.length > 0 && title !== lastTitles.get(sessionId)) {
575
+ lastTitles.set(sessionId, title);
576
+ const ts = event.time;
577
+ post({
578
+ type: "session.updated",
579
+ session: {
580
+ id: sessionId,
581
+ title,
582
+ updatedAt: typeof ts === "number" ? ts : Date.now()
583
+ }
584
+ });
585
+ }
586
+ return;
587
+ }
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);
594
+ };
595
+ const flush = () => {
596
+ const pending = [...dirty];
597
+ dirty.clear();
598
+ for (const sessionId of pending) {
599
+ const s = states.get(sessionId);
600
+ if (s === void 0) continue;
601
+ const last = lastSent.get(sessionId);
602
+ const events = [];
603
+ if (last === void 0 || last.running !== s.running) {
604
+ events.push({
605
+ type: "session.status",
606
+ sessionId,
607
+ status: s.running ? "running" : "idle"
608
+ });
609
+ }
610
+ if (last === void 0 || last.thinking !== s.thinking) {
611
+ events.push({ type: "thinking", sessionId, thinking: s.thinking });
612
+ }
613
+ if (events.length === 0) continue;
614
+ lastSent.set(sessionId, { running: s.running, thinking: s.thinking });
615
+ for (const event of events) post(event);
616
+ }
617
+ };
618
+ let inflight = Promise.resolve();
619
+ const post = (event) => {
620
+ const payload = JSON.stringify({ token, event });
621
+ inflight = inflight.then(async () => {
622
+ try {
623
+ const controller = new AbortController();
624
+ const timeout = setTimeout(() => controller.abort(), 1500);
625
+ const res = await fetch(eventsUrl, {
626
+ method: "POST",
627
+ headers: { "content-type": "application/json" },
628
+ body: payload,
629
+ signal: controller.signal
630
+ });
631
+ clearTimeout(timeout);
632
+ if (!res.ok && Date.now() - lastPostErrorAt > 5e3) {
633
+ lastPostErrorAt = Date.now();
634
+ log3.warn("host event relay push failed", { status: res.status });
635
+ }
636
+ } catch {
637
+ }
638
+ });
639
+ };
640
+ ctx.on("agent/status", handleAgentStatus);
641
+ ctx.on("session/event", handleSessionEvent, { global: true });
642
+ }
643
+
505
644
  // dsh-plugin/src/index.ts
506
645
  var name = "aipanel";
507
646
  var inject = ["tools"];
647
+ var log4 = createNodeLogger("DshPlugin");
508
648
  var MUTATING_TOOLS = /* @__PURE__ */ new Set(["write", "edit", "apply_patch"]);
509
- var DEFAULT_CONTEXT_API_PATH = "/__aipanel_context__";
510
649
  function collectNodeIds(text) {
511
650
  const ids = [];
512
651
  const re = new RegExp("@\u8282\u70B9\\[(n[0-9a-z]+)\\]", "g");
@@ -516,13 +655,11 @@ function collectNodeIds(text) {
516
655
  }
517
656
  function buildNodeContext(e) {
518
657
  const lines = [`\u8282\u70B9 ID\uFF1A${e.id ?? ""}`];
519
- if (e.filePath) lines.push(`\u6E90\u7801\u6587\u4EF6\u8DEF\u5F84\uFF1A${e.filePath}${e.line ? `:${e.line}` : ""}`);
520
- if (e.line) lines.push(`\u4EE3\u7801\u6240\u5728\u884C\u53F7\uFF1A${e.line}`);
521
- 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}`);
522
660
  if (e.description) lines.push(`DOM \u5143\u7D20\u9009\u62E9\u5668\uFF1A${e.description}`);
523
661
  if (e.innerText) lines.push(`DOM \u5143\u7D20\u5185\u90E8\u6587\u672C\uFF1A${e.innerText.slice(0, 200)}`);
524
662
  if (e.previewPageUrl) lines.push(`\u7528\u6237\u9009\u4E2D\u8282\u70B9\u65F6\u7684\u9875\u9762 URL\uFF1A${e.previewPageUrl}`);
525
- if (e.previewPageTitle) lines.push(`\u9875\u9762\u6807\u9898\uFF1A${e.previewPageTitle}`);
526
663
  return lines.join("\n");
527
664
  }
528
665
  function toDiagnosticEntries(items) {
@@ -555,12 +692,74 @@ ${s.text}`).join("\n\n");
555
692
 
556
693
  ${body}` : value.title;
557
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
+ }
558
757
  function apply(ctx, config = {}) {
559
758
  const cwd = config.cwd ?? process.cwd();
560
759
  const enableDiagnostics = config.enableDiagnostics ?? false;
561
760
  const autoDiagnose = config.autoDiagnose ?? process.env.OPENCODE_ENABLE_LINT === "1";
562
761
  const vitePort = config.vitePort ?? 0;
563
- const contextApiPath = config.contextApiPath ?? DEFAULT_CONTEXT_API_PATH;
762
+ const contextApiPath = config.contextApiPath ?? CONTEXT_API_PATH;
564
763
  const tools = ctx.tools;
565
764
  if (enableDiagnostics) {
566
765
  const diagnosticsTool = {
@@ -728,9 +927,16 @@ ${contextText}`
728
927
  { prepend: true }
729
928
  );
730
929
  }
930
+ setupEventRelay(ctx, {
931
+ vitePort,
932
+ eventsPath: config.eventsPath,
933
+ eventsToken: config.eventsToken
934
+ });
935
+ applyProviderSettings(ctx, config);
731
936
  }
732
937
  export {
733
938
  apply,
939
+ applyProviderSettings,
734
940
  inject,
735
941
  name
736
942
  };
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.9",
4
4
  "type": "module",
5
5
  "description": "AIPanel for DeepSeek Harness (dsh):注入审查工具 run_diagnostics、编辑后自动诊断。",
6
6
  "main": "./dist/index.js",
@@ -15,12 +15,15 @@
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-settings": "^0.1.2-rc.1",
20
+ "@deepseek-ai/dsh-agent": "^0.1.2-rc.1",
21
+ "@deepseek-ai/dsh-llm": "^0.1.2-rc.1",
22
+ "@deepseek-ai/dsh-tools": "^0.1.2-rc.1",
23
+ "@deepseek-ai/dsh-util-values": "^0.1.2-rc.1",
24
+ "@deepseek-ai/dsh-session": "^0.1.2-rc.1",
22
25
  "esbuild": "^0.25.0",
23
- "@aipanel/core": "1.2.7"
26
+ "@aipanel/core": "1.2.9"
24
27
  },
25
28
  "scripts": {
26
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",