@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.
- package/dist/index.js +216 -10
- 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
|
-
|
|
520
|
-
if (e.
|
|
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 ??
|
|
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.
|
|
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.
|
|
19
|
-
"@deepseek-ai/dsh-
|
|
20
|
-
"@deepseek-ai/dsh-
|
|
21
|
-
"@deepseek-ai/dsh-
|
|
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.
|
|
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",
|