@aipanel/dsh-plugin 1.2.6 → 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.
- package/dist/index.js +155 -0
- 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.
|
|
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.
|
|
19
|
-
"@deepseek-ai/dsh-agent": "^0.1.
|
|
20
|
-
"@deepseek-ai/dsh-llm": "^0.1.
|
|
21
|
-
"@deepseek-ai/dsh-tools": "^0.1.
|
|
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.
|
|
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",
|