zdashboard 1.1.0 → 1.2.0
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/cli.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/server/index.ts
|
|
4
4
|
import http from "http";
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import fs6 from "fs";
|
|
6
|
+
import path6 from "path";
|
|
7
7
|
import crypto from "crypto";
|
|
8
8
|
import { exec } from "child_process";
|
|
9
9
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -331,10 +331,117 @@ function allBuiltins() {
|
|
|
331
331
|
return Array.from(builtinPlugins.values());
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
+
// src/server/review-store.ts
|
|
335
|
+
import fs4 from "fs";
|
|
336
|
+
import path4 from "path";
|
|
337
|
+
import YAML from "yaml";
|
|
338
|
+
var REVIEW_FILE = "review.yaml";
|
|
339
|
+
var ReviewStore = class {
|
|
340
|
+
root;
|
|
341
|
+
file;
|
|
342
|
+
onChange;
|
|
343
|
+
constructor(root2, onChange) {
|
|
344
|
+
this.root = root2;
|
|
345
|
+
this.file = path4.join(root2, REVIEW_FILE);
|
|
346
|
+
this.onChange = onChange;
|
|
347
|
+
}
|
|
348
|
+
exists() {
|
|
349
|
+
return fs4.existsSync(this.file);
|
|
350
|
+
}
|
|
351
|
+
read() {
|
|
352
|
+
try {
|
|
353
|
+
const parsed = YAML.parse(fs4.readFileSync(this.file, "utf8"));
|
|
354
|
+
if (!parsed || !Array.isArray(parsed.items)) return { status: "draft", items: [] };
|
|
355
|
+
return parsed;
|
|
356
|
+
} catch {
|
|
357
|
+
return { status: "draft", items: [] };
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
write(data) {
|
|
361
|
+
fs4.writeFileSync(this.file, YAML.stringify(data), "utf8");
|
|
362
|
+
this.onChange?.();
|
|
363
|
+
}
|
|
364
|
+
updateItem(id, patch) {
|
|
365
|
+
const data = this.read();
|
|
366
|
+
const item = data.items.find((i) => i.id === id);
|
|
367
|
+
if (!item) throw new Error(`item ${id} not found`);
|
|
368
|
+
if (patch.answer !== void 0) item.answer = patch.answer;
|
|
369
|
+
if (patch.state !== void 0) item.state = patch.state;
|
|
370
|
+
if (patch.state === "answered" && !patch.answer && !item.answer) item.answer = "";
|
|
371
|
+
this.write(data);
|
|
372
|
+
return data;
|
|
373
|
+
}
|
|
374
|
+
setStatus(status) {
|
|
375
|
+
const data = this.read();
|
|
376
|
+
if (status === "passed" && data.items.some((i) => i.state === "open")) {
|
|
377
|
+
throw new Error("\u5B58\u5728\u672A\u5904\u7406\u7684\u8BC4\u5BA1\u9879(open),\u4E0D\u80FD\u901A\u8FC7");
|
|
378
|
+
}
|
|
379
|
+
data.status = status;
|
|
380
|
+
this.write(data);
|
|
381
|
+
return data;
|
|
382
|
+
}
|
|
383
|
+
docs() {
|
|
384
|
+
try {
|
|
385
|
+
return fs4.readdirSync(this.root).filter((f) => /\.(md|markdown)$/i.test(f) && fs4.statSync(path4.join(this.root, f)).isFile()).sort();
|
|
386
|
+
} catch {
|
|
387
|
+
return [];
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
// src/server/design-assets.ts
|
|
393
|
+
import fs5 from "fs";
|
|
394
|
+
import path5 from "path";
|
|
395
|
+
var PAGE_EXTS = [".html", ".htm"];
|
|
396
|
+
var ICON_EXTS = [".svg", ".png", ".ico", ".jpg", ".jpeg", ".gif", ".webp"];
|
|
397
|
+
var VIDEO_EXTS = [".mp4", ".webm", ".mov", ".ogg", ".ogv"];
|
|
398
|
+
var AUDIO_EXTS = [".mp3", ".wav", ".flac", ".aac", ".m4a"];
|
|
399
|
+
var CODE_EXTS = [".js", ".mjs", ".ts", ".tsx", ".jsx", ".css", ".json", ".txt", ".xml", ".yml", ".yaml", ".sh", ".md"];
|
|
400
|
+
var FONT_EXTS = [".woff", ".woff2", ".ttf", ".otf"];
|
|
401
|
+
var TOKEN_RE = /token|theme|design|color|palette|typograph/i;
|
|
402
|
+
function categorize(rel, ext) {
|
|
403
|
+
if (rel.indexOf("components/") === 0) return "component";
|
|
404
|
+
if (VIDEO_EXTS.includes(ext)) return "video";
|
|
405
|
+
if (AUDIO_EXTS.includes(ext)) return "audio";
|
|
406
|
+
if (ext === ".pdf") return "pdf";
|
|
407
|
+
if (ext === ".md") return "md";
|
|
408
|
+
if (FONT_EXTS.includes(ext)) return "font";
|
|
409
|
+
if (ICON_EXTS.includes(ext)) return "icon";
|
|
410
|
+
if (PAGE_EXTS.includes(ext)) return "page";
|
|
411
|
+
if (CODE_EXTS.includes(ext)) return TOKEN_RE.test(rel) && (ext === ".css" || ext === ".json") ? "token" : "code";
|
|
412
|
+
return "other";
|
|
413
|
+
}
|
|
414
|
+
function scanAssets(root2) {
|
|
415
|
+
const out = {};
|
|
416
|
+
const keys = ["page", "component", "icon", "token", "md", "video", "audio", "pdf", "code", "font", "other"];
|
|
417
|
+
for (const k of keys) out[k] = [];
|
|
418
|
+
function walk(dir, rel) {
|
|
419
|
+
let ents;
|
|
420
|
+
try {
|
|
421
|
+
ents = fs5.readdirSync(dir, { withFileTypes: true });
|
|
422
|
+
} catch {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
for (const ent of ents) {
|
|
426
|
+
if (ent.name.charAt(0) === ".") continue;
|
|
427
|
+
const r = rel ? `${rel}/${ent.name}` : ent.name;
|
|
428
|
+
if (ent.isDirectory()) {
|
|
429
|
+
walk(path5.join(dir, ent.name), r);
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
const ext = path5.extname(ent.name).toLowerCase();
|
|
433
|
+
const t = categorize(r, ext);
|
|
434
|
+
out[t].push({ path: r, name: ent.name, ext, type: t });
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
walk(root2, "");
|
|
438
|
+
return out;
|
|
439
|
+
}
|
|
440
|
+
|
|
334
441
|
// package.json
|
|
335
442
|
var package_default = {
|
|
336
443
|
name: "zdashboard",
|
|
337
|
-
version: "1.
|
|
444
|
+
version: "1.2.0",
|
|
338
445
|
description: "ZCode skill dashboard platform \u2014 pluggable viewers for zdesign/zview/zreview/zgoal",
|
|
339
446
|
type: "module",
|
|
340
447
|
bin: {
|
|
@@ -384,12 +491,14 @@ var package_default = {
|
|
|
384
491
|
"remark-math": "^6.0.0",
|
|
385
492
|
sonner: "^1.5.0",
|
|
386
493
|
"tailwind-merge": "^2.5.4",
|
|
387
|
-
"use-debounce": "^10.0.0"
|
|
494
|
+
"use-debounce": "^10.0.0",
|
|
495
|
+
yaml: "^2.9.0"
|
|
388
496
|
},
|
|
389
497
|
devDependencies: {
|
|
390
498
|
"@tailwindcss/typography": "^0.5.20",
|
|
391
499
|
"@testing-library/jest-dom": "^7.0.1",
|
|
392
500
|
"@testing-library/react": "^16.3.2",
|
|
501
|
+
"@types/lodash-es": "^4.17.12",
|
|
393
502
|
"@types/node": "^22.9.0",
|
|
394
503
|
"@types/react": "^18.3.12",
|
|
395
504
|
"@types/react-dom": "^18.3.1",
|
|
@@ -413,7 +522,7 @@ var package_default = {
|
|
|
413
522
|
|
|
414
523
|
// src/server/index.ts
|
|
415
524
|
var VERSION = package_default.version;
|
|
416
|
-
var __dirname2 =
|
|
525
|
+
var __dirname2 = path6.dirname(fileURLToPath2(import.meta.url));
|
|
417
526
|
var STOP_TOKEN = crypto.randomBytes(12).toString("hex");
|
|
418
527
|
var INJECT = `<script>(function(){try{var es=new EventSource('/__reload');es.addEventListener('reload',function(){location.reload();});es.onerror=function(){es.close();};}catch(e){}document.addEventListener('click',function(e){var t=e.target;if(t&&t.closest){var a=t.closest('a[target]');if(a&&a.target!=='_self'){a.target='_self';}}},true);})();</script>`;
|
|
419
528
|
var MIME = {
|
|
@@ -447,11 +556,11 @@ function readBody(req) {
|
|
|
447
556
|
});
|
|
448
557
|
}
|
|
449
558
|
function createServer(opts) {
|
|
450
|
-
const ROOT =
|
|
559
|
+
const ROOT = path6.resolve(opts.root);
|
|
451
560
|
const PORT0 = opts.port ?? 4190;
|
|
452
561
|
const OPEN = !!opts.open;
|
|
453
|
-
const APP_DIR = opts.dashboardDir ??
|
|
454
|
-
if (!
|
|
562
|
+
const APP_DIR = opts.dashboardDir ?? path6.resolve(__dirname2, "web");
|
|
563
|
+
if (!fs6.existsSync(ROOT)) fs6.mkdirSync(ROOT, { recursive: true });
|
|
455
564
|
const det2 = opts.detect;
|
|
456
565
|
const runner = new JustRunner(ROOT);
|
|
457
566
|
const MODE = opts.mode;
|
|
@@ -465,7 +574,61 @@ function createServer(opts) {
|
|
|
465
574
|
} }
|
|
466
575
|
});
|
|
467
576
|
registerBuiltin({ mode: "view", label: "\u9879\u76EE\u6D4F\u89C8", icon: "\u{1F441}\uFE0F" });
|
|
468
|
-
|
|
577
|
+
const reviewStore = new ReviewStore(ROOT);
|
|
578
|
+
registerBuiltin({
|
|
579
|
+
mode: "review",
|
|
580
|
+
label: "\u6587\u6863\u8BC4\u5BA1",
|
|
581
|
+
icon: "\u2705",
|
|
582
|
+
apiRoutes: {
|
|
583
|
+
"/__review": async (_, res) => {
|
|
584
|
+
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8", "Cache-Control": "no-cache" });
|
|
585
|
+
res.end(JSON.stringify(reviewStore.read()));
|
|
586
|
+
},
|
|
587
|
+
"/__review/item": async (req, res) => {
|
|
588
|
+
if (req.headers["x-stop-token"] !== STOP_TOKEN) {
|
|
589
|
+
res.writeHead(403);
|
|
590
|
+
res.end("forbidden");
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
(async () => {
|
|
594
|
+
try {
|
|
595
|
+
const body = JSON.parse(await readBody(req) || "{}");
|
|
596
|
+
const data = reviewStore.updateItem(body.id, { answer: body.answer, state: body.state });
|
|
597
|
+
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
598
|
+
res.end(JSON.stringify(data));
|
|
599
|
+
} catch (e) {
|
|
600
|
+
res.writeHead(400, { "Content-Type": "application/json; charset=utf-8" });
|
|
601
|
+
res.end(JSON.stringify({ error: e.message }));
|
|
602
|
+
}
|
|
603
|
+
})();
|
|
604
|
+
return;
|
|
605
|
+
},
|
|
606
|
+
"/__review/status": async (req, res) => {
|
|
607
|
+
if (req.headers["x-stop-token"] !== STOP_TOKEN) {
|
|
608
|
+
res.writeHead(403);
|
|
609
|
+
res.end("forbidden");
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
(async () => {
|
|
613
|
+
try {
|
|
614
|
+
const body = JSON.parse(await readBody(req) || "{}");
|
|
615
|
+
const data = reviewStore.setStatus(body.status);
|
|
616
|
+
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
617
|
+
res.end(JSON.stringify(data));
|
|
618
|
+
} catch (e) {
|
|
619
|
+
res.writeHead(400, { "Content-Type": "application/json; charset=utf-8" });
|
|
620
|
+
res.end(JSON.stringify({ error: e.message }));
|
|
621
|
+
}
|
|
622
|
+
})();
|
|
623
|
+
return;
|
|
624
|
+
},
|
|
625
|
+
"/__docs": async (_, res) => {
|
|
626
|
+
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8", "Cache-Control": "no-cache" });
|
|
627
|
+
res.end(JSON.stringify(reviewStore.docs()));
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
registerBuiltin({ mode: "design", label: "\u8BBE\u8BA1\u8D44\u4EA7", icon: "\u{1F3A8}" });
|
|
469
632
|
const clients = /* @__PURE__ */ new Set();
|
|
470
633
|
const broadcast = (ev, data = "") => {
|
|
471
634
|
const payload = `event: ${ev}
|
|
@@ -475,12 +638,12 @@ data: ${JSON.stringify(data == null ? "" : data)}
|
|
|
475
638
|
for (const c of clients) c.write(payload);
|
|
476
639
|
};
|
|
477
640
|
function serveFile(filePath, res, injectHtml) {
|
|
478
|
-
|
|
641
|
+
fs6.readFile(filePath, (err, data) => {
|
|
479
642
|
if (err) {
|
|
480
643
|
res.writeHead(404);
|
|
481
644
|
return res.end("Not found");
|
|
482
645
|
}
|
|
483
|
-
const ext =
|
|
646
|
+
const ext = path6.extname(filePath).toLowerCase();
|
|
484
647
|
const ct = MIME[ext] ?? "application/octet-stream";
|
|
485
648
|
let body = data;
|
|
486
649
|
if (injectHtml && ext === ".html") {
|
|
@@ -523,6 +686,10 @@ data: ${JSON.stringify(data == null ? "" : data)}
|
|
|
523
686
|
return;
|
|
524
687
|
}
|
|
525
688
|
if (url === "/__files") {
|
|
689
|
+
if (MODE === "design") {
|
|
690
|
+
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8", "Cache-Control": "no-cache" });
|
|
691
|
+
return res.end(JSON.stringify(scanAssets(ROOT)));
|
|
692
|
+
}
|
|
526
693
|
const tree = scanTree(ROOT, det2.hasOpenspec, det2.hasDocs);
|
|
527
694
|
const payload = { tree, ...det2 };
|
|
528
695
|
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8", "Cache-Control": "no-cache" });
|
|
@@ -590,25 +757,25 @@ data: ${JSON.stringify(data == null ? "" : data)}
|
|
|
590
757
|
}
|
|
591
758
|
}
|
|
592
759
|
}
|
|
593
|
-
if (url === "/") return serveFile(
|
|
760
|
+
if (url === "/") return serveFile(path6.join(APP_DIR, "index.html"), res, false);
|
|
594
761
|
if (url.indexOf("/__app/") === 0) {
|
|
595
|
-
const fp2 =
|
|
596
|
-
if (fp2 !== APP_DIR && fp2.indexOf(APP_DIR +
|
|
762
|
+
const fp2 = path6.join(APP_DIR, url.slice(7));
|
|
763
|
+
if (fp2 !== APP_DIR && fp2.indexOf(APP_DIR + path6.sep) !== 0) {
|
|
597
764
|
res.writeHead(403);
|
|
598
765
|
return res.end("Forbidden");
|
|
599
766
|
}
|
|
600
767
|
return serveFile(fp2, res, false);
|
|
601
768
|
}
|
|
602
769
|
if (url.indexOf("/assets/") === 0) {
|
|
603
|
-
const fp2 =
|
|
604
|
-
if (fp2.indexOf(APP_DIR +
|
|
770
|
+
const fp2 = path6.join(APP_DIR, decodeURIComponent(url));
|
|
771
|
+
if (fp2.indexOf(APP_DIR + path6.sep) !== 0) {
|
|
605
772
|
res.writeHead(403);
|
|
606
773
|
return res.end("Forbidden");
|
|
607
774
|
}
|
|
608
775
|
return serveFile(fp2, res, false);
|
|
609
776
|
}
|
|
610
|
-
const fp =
|
|
611
|
-
if (fp !== ROOT && fp.indexOf(ROOT +
|
|
777
|
+
const fp = path6.join(ROOT, decodeURIComponent(url));
|
|
778
|
+
if (fp !== ROOT && fp.indexOf(ROOT + path6.sep) !== 0) {
|
|
612
779
|
res.writeHead(403);
|
|
613
780
|
return res.end("Forbidden");
|
|
614
781
|
}
|
|
@@ -634,7 +801,7 @@ data: ${JSON.stringify(data == null ? "" : data)}
|
|
|
634
801
|
}
|
|
635
802
|
let debounce;
|
|
636
803
|
try {
|
|
637
|
-
|
|
804
|
+
fs6.watch(ROOT, { recursive: true }, () => {
|
|
638
805
|
clearTimeout(debounce);
|
|
639
806
|
debounce = setTimeout(() => {
|
|
640
807
|
broadcast("reload");
|
|
@@ -649,8 +816,8 @@ data: ${JSON.stringify(data == null ? "" : data)}
|
|
|
649
816
|
}
|
|
650
817
|
|
|
651
818
|
// src/server/detect.ts
|
|
652
|
-
import
|
|
653
|
-
import
|
|
819
|
+
import fs7 from "fs";
|
|
820
|
+
import path7 from "path";
|
|
654
821
|
import { execFile as execFile2 } from "child_process";
|
|
655
822
|
function justAvailable(cwd) {
|
|
656
823
|
return new Promise((resolve) => {
|
|
@@ -661,10 +828,10 @@ function justAvailable(cwd) {
|
|
|
661
828
|
});
|
|
662
829
|
}
|
|
663
830
|
async function detect(root2) {
|
|
664
|
-
const hasOpenspec =
|
|
665
|
-
const hasDocs =
|
|
831
|
+
const hasOpenspec = fs7.existsSync(path7.join(root2, "openspec"));
|
|
832
|
+
const hasDocs = fs7.existsSync(path7.join(root2, "docs"));
|
|
666
833
|
const hasJust = await justAvailable(root2);
|
|
667
|
-
const hasBugs =
|
|
834
|
+
const hasBugs = fs7.existsSync(path7.join(root2, ".zgoal", "config.yaml"));
|
|
668
835
|
return { hasOpenspec, hasDocs, hasJust, hasBugs };
|
|
669
836
|
}
|
|
670
837
|
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/index.ts","../src/server/spec-scan.ts","../src/server/just-runner.ts","../src/server/bugs.ts","../src/server/api/fetch.ts","../src/server/errors.ts","../src/server/plugins.ts","../package.json","../src/server/detect.ts","../src/cli.ts"],"sourcesContent":["import http from 'node:http';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport crypto from 'node:crypto';\nimport { exec } from 'node:child_process';\nimport { fileURLToPath } from 'node:url';\nimport { scanTree } from './spec-scan.js';\nimport { JustRunner } from './just-runner.js';\nimport { fetchBugs } from './bugs.js';\nimport { registerBuiltin, allBuiltins, type DashboardPlugin } from './plugins.js';\nimport type { DetectResult } from './detect.js';\nimport pkg from '../../package.json' with { type: 'json' };\n\nconst VERSION = pkg.version;\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\nconst STOP_TOKEN = crypto.randomBytes(12).toString('hex');\nconst INJECT = `<script>(function(){try{var es=new EventSource('/__reload');es.addEventListener('reload',function(){location.reload();});es.onerror=function(){es.close();};}catch(e){}document.addEventListener('click',function(e){var t=e.target;if(t&&t.closest){var a=t.closest('a[target]');if(a&&a.target!=='_self'){a.target='_self';}}},true);})();</script>`;\n\nconst MIME: Record<string, string> = {\n '.html': 'text/html; charset=utf-8', '.htm': 'text/html; charset=utf-8',\n '.css': 'text/css; charset=utf-8', '.js': 'application/javascript; charset=utf-8',\n '.mjs': 'application/javascript; charset=utf-8', '.json': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml', '.png': 'image/png', '.ico': 'image/x-icon',\n '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.webp': 'image/webp',\n '.md': 'text/markdown; charset=utf-8', '.txt': 'text/plain; charset=utf-8',\n '.yml': 'text/yaml; charset=utf-8', '.yaml': 'text/yaml; charset=utf-8',\n '.woff': 'font/woff', '.woff2': 'font/woff2', '.ttf': 'font/ttf',\n '.map': 'application/json; charset=utf-8',\n};\n\nexport interface ServerOptions {\n root: string;\n port?: number;\n open?: boolean;\n detect: DetectResult;\n dashboardDir?: string;\n mode?: string;\n}\n\nfunction readBody(req: http.IncomingMessage): Promise<string> {\n return new Promise((resolve) => {\n let data = '';\n req.on('data', (c) => (data += c));\n req.on('end', () => resolve(data));\n });\n}\n\nexport function createServer(opts: ServerOptions) {\n const ROOT = path.resolve(opts.root);\n const PORT0 = opts.port ?? 4190;\n const OPEN = !!opts.open;\n const APP_DIR = opts.dashboardDir ?? path.resolve(__dirname, 'web');\n if (!fs.existsSync(ROOT)) fs.mkdirSync(ROOT, { recursive: true });\n const det = opts.detect;\n const runner = new JustRunner(ROOT);\n const MODE = opts.mode;\n\n // register builtin plugins (server-side: apiRoutes only; viewer loaded by frontend)\n registerBuiltin({\n mode: 'bugs', label: '禅道 Bugs', icon: '🎯',\n apiRoutes: { '/__bugs': async (_, res) => { res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8', 'Cache-Control': 'no-cache' }); fetchBugs(ROOT).then((r) => res.end(JSON.stringify(r))); } }\n });\n registerBuiltin({ mode: 'view', label: '项目浏览', icon: '👁️' });\n registerBuiltin({ mode: 'review', label: '文档评审', icon: '✅' });\n\n const clients = new Set<http.ServerResponse>();\n const broadcast = (ev: string, data: unknown = '') => {\n const payload = `event: ${ev}\\ndata: ${JSON.stringify(data == null ? '' : data)}\\n\\n`;\n for (const c of clients) c.write(payload);\n };\n\n function serveFile(filePath: string, res: http.ServerResponse, injectHtml: boolean) {\n fs.readFile(filePath, (err, data) => {\n if (err) { res.writeHead(404); return res.end('Not found'); }\n const ext = path.extname(filePath).toLowerCase();\n const ct = MIME[ext] ?? 'application/octet-stream';\n let body = data;\n if (injectHtml && ext === '.html') {\n const s = data.toString('utf8');\n body = Buffer.from(s.indexOf('</body>') >= 0 ? s.replace('</body>', INJECT + '</body>') : s + INJECT);\n }\n res.writeHead(200, { 'Content-Type': ct, 'Cache-Control': 'no-cache' });\n res.end(body);\n });\n }\n\n function handler(req: http.IncomingMessage, res: http.ServerResponse) {\n const url = req.url!.split('?')[0];\n\n // ── SSE:文件变更 ──\n if (url === '/__reload') {\n res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', Connection: 'keep-alive' });\n res.write(': connected\\n\\n');\n clients.add(res);\n req.on('close', () => clients.delete(res));\n return;\n }\n if (url === '/__config') {\n res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8', 'Cache-Control': 'no-cache' });\n return res.end(JSON.stringify({ stopToken: STOP_TOKEN }));\n }\n if (url === '/__stop' && req.method === 'POST') {\n if (req.headers['x-stop-token'] === STOP_TOKEN) {\n res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });\n res.end('{\"ok\":true}');\n runner.stop();\n setTimeout(() => { try { server.close(); } catch {} process.exit(0); }, 50);\n } else { res.writeHead(403); res.end('forbidden'); }\n return;\n }\n\n // ── 方案模式:树形文件清单(+探测结果) ──\n if (url === '/__files') {\n const tree = scanTree(ROOT, det.hasOpenspec, det.hasDocs);\n const payload: TreeNodePayload = { tree, ...det };\n res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8', 'Cache-Control': 'no-cache' });\n return res.end(JSON.stringify(payload));\n }\n\n // ── 日志能力 ──\n if (url === '/__just/recipes') {\n res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8', 'Cache-Control': 'no-cache' });\n runner.recipes().then((r) => res.end(JSON.stringify(r)));\n return;\n }\n if (url === '/__just/logs') {\n res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', Connection: 'keep-alive' });\n res.write(': connected\\n\\n');\n const unsub = runner.subscribe((ev) => res.write(`data: ${JSON.stringify(ev)}\\n\\n`));\n req.on('close', unsub);\n return;\n }\n const justAction = url.match(/^\\/__just\\/(start|stop|restart)$/);\n if (justAction && req.method === 'POST') {\n (async () => {\n if (req.headers['x-stop-token'] !== STOP_TOKEN) { res.writeHead(403); res.end('forbidden'); return; }\n const body = await readBody(req);\n let recipe: string | undefined;\n try { recipe = JSON.parse(body || '{}').recipe; } catch { /* ignore */ }\n const act = justAction[1];\n if (act === 'start' || act === 'restart') {\n const target = recipe ?? runner.info().recipe;\n if (!target) { res.writeHead(400); res.end('{\"error\":\"no recipe\"}'); return; }\n const recipes = await runner.recipes();\n if (!recipes.some((r) => r.name === target)) { res.writeHead(403); res.end('{\"error\":\"unknown recipe\"}'); return; }\n runner.start(target);\n } else {\n runner.stop();\n }\n res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });\n res.end(JSON.stringify(runner.info()));\n })();\n return;\n }\n\n // ── plugin API routes ──\n let handled = false;\n for (const plugin of allBuiltins()) {\n if (!plugin.apiRoutes) continue;\n for (const [route, handler] of Object.entries(plugin.apiRoutes)) {\n if (url === route) {\n handled = true;\n handler(req, res, ROOT);\n return;\n }\n }\n }\n\n // ── dashboard 前端 ──\n if (url === '/') return serveFile(path.join(APP_DIR, 'index.html'), res, false);\n if (url.indexOf('/__app/') === 0) {\n const fp = path.join(APP_DIR, url.slice(7));\n if (fp !== APP_DIR && fp.indexOf(APP_DIR + path.sep) !== 0) { res.writeHead(403); return res.end('Forbidden'); }\n return serveFile(fp, res, false);\n }\n if (url.indexOf('/assets/') === 0) {\n const fp = path.join(APP_DIR, decodeURIComponent(url));\n if (fp.indexOf(APP_DIR + path.sep) !== 0) { res.writeHead(403); return res.end('Forbidden'); }\n return serveFile(fp, res, false);\n }\n\n // ── 用户资产 ──\n const fp = path.join(ROOT, decodeURIComponent(url));\n if (fp !== ROOT && fp.indexOf(ROOT + path.sep) !== 0) { res.writeHead(403); return res.end('Forbidden'); }\n return serveFile(fp, res, true);\n }\n\n let server: http.Server;\n function start(port: number) {\n server = http.createServer(handler);\n server.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') { console.log(`[zdashboard] port ${port} busy, trying ${port + 1}`); start(port + 1); }\n else throw err;\n });\n server.listen(port, () => {\n const u = `http://localhost:${port}`;\n console.log(`[zdashboard] v${VERSION} dashboard -> ${u}`);\n console.log(`[zdashboard] project -> ${ROOT}`);\n console.log(`[zdashboard] mode -> ${MODE ?? '(auto)'}`);\n console.log(`[zdashboard] detect -> openspec:${det.hasOpenspec} docs:${det.hasDocs} just:${det.hasJust} bugs:${det.hasBugs}`);\n if (OPEN) exec(process.platform === 'darwin' ? `open ${u}` : `start ${u}`);\n });\n }\n\n let debounce: NodeJS.Timeout;\n try {\n fs.watch(ROOT, { recursive: true }, () => {\n clearTimeout(debounce);\n debounce = setTimeout(() => {\n broadcast('reload');\n broadcast('files');\n console.log(`[zdashboard] change -> reload + refresh tree (${clients.size} client${clients.size === 1 ? '' : 's'})`);\n }, 150);\n });\n } catch { console.log('[zdashboard] watch unavailable - static only.'); }\n\n start(PORT0);\n}\n\ninterface TreeNodePayload { tree: unknown; hasOpenspec: boolean; hasDocs: boolean; hasJust: boolean; hasBugs: boolean; }\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nexport type NodeKind = 'file' | 'dir' | 'log';\nexport interface TreeNode {\n name: string;\n kind: NodeKind;\n path?: string; // file: 相对 root 的路径(点击预览用)\n defaultCollapsed?: boolean;\n children?: TreeNode[];\n}\n\nfunction walkFiles(absDir: string, relDir: string, depth = 0): TreeNode[] {\n if (depth > 4) return [];\n let ents: fs.Dirent[];\n try { ents = fs.readdirSync(absDir, { withFileTypes: true }); } catch { return []; }\n const nodes: TreeNode[] = [];\n for (const ent of ents) {\n if (ent.name.startsWith('.') || ent.name === 'node_modules') continue;\n const rel = relDir ? `${relDir}/${ent.name}` : ent.name;\n if (ent.isDirectory()) {\n nodes.push({ name: ent.name, kind: 'dir', children: walkFiles(path.join(absDir, ent.name), rel, depth + 1) });\n } else {\n nodes.push({ name: ent.name, kind: 'file', path: rel });\n }\n }\n nodes.sort((a, b) => (a.kind === b.kind ? a.name.localeCompare(b.name) : a.kind === 'dir' ? -1 : 1));\n return nodes;\n}\n\n/** 方案模式树形扫描:openspec 感知 + docs 聚合 + 其他兜底 */\nexport function scanTree(root: string, hasOpenspec: boolean, hasDocs: boolean): TreeNode[] {\n const tree: TreeNode[] = [];\n if (hasOpenspec && fs.existsSync(path.join(root, 'openspec', 'changes'))) {\n const changesDir = path.join(root, 'openspec', 'changes');\n const active: TreeNode[] = [];\n const archived: TreeNode[] = [];\n for (const ent of fs.readdirSync(changesDir, { withFileTypes: true })) {\n if (!ent.isDirectory() || ent.name.startsWith('.') || ent.name === 'archive') continue;\n active.push({ name: ent.name, kind: 'dir', children: walkFiles(path.join(changesDir, ent.name), `openspec/changes/${ent.name}`) });\n }\n active.sort((a, b) => a.name.localeCompare(b.name));\n const archiveDir = path.join(changesDir, 'archive');\n if (fs.existsSync(archiveDir)) {\n for (const ent of fs.readdirSync(archiveDir, { withFileTypes: true })) {\n if (!ent.isDirectory() || ent.name.startsWith('.')) continue;\n archived.push({ name: ent.name, kind: 'dir', children: walkFiles(path.join(archiveDir, ent.name), `openspec/changes/archive/${ent.name}`) });\n }\n archived.sort((a, b) => b.name.localeCompare(a.name)); // 日期前缀倒序\n }\n if (active.length) tree.push({ name: `进行中 (${active.length})`, kind: 'dir', children: active });\n if (archived.length) tree.push({ name: `归档 (${archived.length})`, kind: 'dir', defaultCollapsed: true, children: archived });\n const specsDir = path.join(root, 'openspec', 'specs');\n if (fs.existsSync(specsDir)) {\n const specs = walkFiles(specsDir, 'openspec/specs');\n if (specs.length) tree.push({ name: '能力 Specs', kind: 'dir', children: specs });\n }\n }\n if (hasDocs && fs.existsSync(path.join(root, 'docs'))) {\n const docs = walkFiles(path.join(root, 'docs'), 'docs');\n if (docs.length) tree.push({ name: 'docs', kind: 'dir', children: docs });\n }\n const skip = new Set(['openspec', 'docs', 'node_modules', '.git', 'dist', 'test-server']);\n // \"其他\"只收根目录的 md 文档(README/CLAUDE 等);构建配置(pom.xml/justfile 等)不收——对\"方案+日志\"定位是噪音\n const etc: TreeNode[] = [];\n try {\n for (const ent of fs.readdirSync(root, { withFileTypes: true })) {\n if (ent.name.startsWith('.') || skip.has(ent.name)) continue;\n const ext = path.extname(ent.name).toLowerCase();\n if (ent.isFile() && (ext === '.md' || ext === '.markdown')) etc.push({ name: ent.name, kind: 'file', path: ent.name });\n }\n } catch {}\n etc.sort((a, b) => a.name.localeCompare(b.name));\n if (etc.length) tree.push({ name: `其他 (${etc.length})`, kind: 'dir', children: etc });\n return tree;\n}\n","import { spawn, execFile, type ChildProcess } from 'node:child_process';\n\nexport interface Recipe { name: string; description: string; }\nexport type JustState = 'idle' | 'running' | 'exited';\nexport type JustEvent =\n | { type: 'log'; text: string }\n | { type: 'clear' }\n | { type: 'state'; state: JustState; recipe: string | null; code: number | null };\n\nconst MAX_BUFFER = 1000;\n\nexport class JustRunner {\n private cwd: string;\n private child: ChildProcess | null = null;\n private recipe: string | null = null;\n private state: JustState = 'idle';\n private code: number | null = null;\n private buffer: string[] = [];\n private pending = ''; // 行缓冲:块缓冲输出(如 maven)的 chunk 会在行中间断开,攒到 \\n 才切行\n private clients = new Set<(ev: JustEvent) => void>();\n private recipesCache: Recipe[] | null = null;\n\n constructor(cwd: string) { this.cwd = cwd; }\n\n recipes(): Promise<Recipe[]> {\n if (this.recipesCache) return Promise.resolve(this.recipesCache);\n return new Promise((resolve) => {\n execFile('just', ['--list', '--unsorted'], { cwd: this.cwd, maxBuffer: 1 << 20, timeout: 8000 }, (err, stdout) => {\n if (err) { resolve([]); return; }\n const out: Recipe[] = [];\n const seen = new Set<string>();\n for (const line of stdout.split(/\\r?\\n/).slice(1)) { // 跳过 \"Available recipes:\"\n const trimmed = line.trim();\n if (!trimmed) continue;\n const hashIdx = trimmed.indexOf('#');\n const sig = (hashIdx >= 0 ? trimmed.slice(0, hashIdx) : trimmed).trim();\n if (!sig) continue;\n const name = sig.split(/\\s+/)[0]; // \"hello msg=...\" -> \"hello\"\n if (seen.has(name)) continue;\n seen.add(name);\n out.push({ name, description: hashIdx >= 0 ? trimmed.slice(hashIdx + 1).trim() : '' });\n }\n this.recipesCache = out;\n resolve(out);\n });\n });\n }\n\n subscribe(fn: (ev: JustEvent) => void): () => void {\n this.clients.add(fn);\n // 连上即重放:历史日志 + 当前状态\n for (const text of this.buffer) fn({ type: 'log', text });\n fn({ type: 'state', state: this.state, recipe: this.recipe, code: this.code });\n return () => this.clients.delete(fn);\n }\n\n private emit(ev: JustEvent) { for (const fn of this.clients) fn(ev); }\n\n info() { return { state: this.state, recipe: this.recipe, code: this.code }; }\n\n /** 启动 recipe(调用方须先用 recipes() 校验名字);自动停旧进程 */\n start(recipe: string) {\n this.killChild();\n this.recipe = recipe;\n this.code = null;\n this.state = 'running';\n this.buffer = [];\n this.pending = '';\n this.emit({ type: 'clear' }); // 广播清屏:已连接的订阅者同步清掉上一个任务的残留日志\n this.emit({ type: 'state', state: 'running', recipe, code: null });\n const child = spawn('just', [recipe], {\n cwd: this.cwd,\n shell: true,\n env: {\n ...process.env,\n FORCE_COLOR: '1', // node 生态(chalk 等)\n // maven 检测非 tty 会关颜色;经 MAVEN_OPTS 强制开(保留用户已有值)\n MAVEN_OPTS: `${process.env.MAVEN_OPTS ?? ''} -Dstyle.color=always`.trim(),\n CI: '',\n },\n });\n this.child = child;\n const push = (d: Buffer) => {\n this.pending += d.toString();\n let idx: number;\n while ((idx = this.pending.indexOf('\\n')) >= 0) {\n const line = this.pending.slice(0, idx + 1);\n this.pending = this.pending.slice(idx + 1);\n this.pushLine(line);\n }\n // 无 \\n 的尾巴留在 pending,等下个 chunk(块缓冲输出会在行中断开,不能当独立行)\n };\n child.stdout?.on('data', push);\n child.stderr?.on('data', push);\n child.on('error', (err) => { this.pushLine(`[zview] spawn error: ${err.message}\\n`); });\n child.on('exit', (code) => {\n if (this.pending) { this.pushLine(this.pending + '\\n'); this.pending = ''; } // flush 末尾无换行的残留\n this.child = null;\n this.state = 'exited';\n this.code = code ?? 0;\n this.emit({ type: 'state', state: 'exited', recipe: this.recipe, code: this.code });\n });\n }\n\n private pushLine(line: string) {\n this.buffer.push(line);\n if (this.buffer.length > MAX_BUFFER) this.buffer.shift();\n this.emit({ type: 'log', text: line });\n }\n\n stop() {\n this.killChild();\n }\n\n restart(recipe?: string) {\n const target = recipe ?? this.recipe;\n if (target) this.start(target);\n }\n\n private killChild() {\n const child = this.child;\n if (child?.pid) {\n try {\n if (process.platform === 'win32') spawn('taskkill', ['/PID', String(child.pid), '/T', '/F']);\n else child.kill('SIGTERM');\n } catch { /* 已退出 */ }\n }\n this.child = null;\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { fetchJson } from './api/fetch.js';\n\n/** .zgoal/config.yaml(zgoal skill 的禅道凭据配置,扁平 key: value) */\nexport interface ZgoalConfig {\n url: string;\n account: string;\n password?: string;\n token?: string;\n product: number;\n}\n\nexport interface ZenBug {\n id: number;\n title: string;\n severity: number | string;\n pri: number | string;\n status: string;\n assignedTo: string;\n openedBy?: string;\n /** 指派给 config.account 的本人 */\n mine: boolean;\n}\n\nexport type BugsResult =\n | { ok: true; url: string; total: number; bugs: ZenBug[] }\n | { ok: false; error: string };\n\n/** 极简扁平 yaml 解析(仅 key: value 行,够 .zgoal/config.yaml 用) */\nfunction loadZgoalConfig(root: string): ZgoalConfig | null {\n const file = path.join(root, '.zgoal', 'config.yaml');\n if (!fs.existsSync(file)) return null;\n const kv: Record<string, string> = {};\n for (const line of fs.readFileSync(file, 'utf8').split('\\n')) {\n const m = line.match(/^\\s*([A-Za-z_]\\w*)\\s*:\\s*(.+?)\\s*$/);\n if (m && !m[2].startsWith('#')) kv[m[1]] = m[2].replace(/^[\"']|[\"']$/g, '');\n }\n const product = Number(kv.product);\n if (!kv.url || !product) return null;\n return {\n url: kv.url.replace(/\\/+$/, ''),\n account: kv.account ?? '',\n password: kv.password,\n token: kv.token,\n product,\n };\n}\n\nlet tokenCache: { key: string; token: string; at: number } | null = null;\n\nasync function getToken(cfg: ZgoalConfig): Promise<string> {\n if (cfg.token) return cfg.token;\n const key = `${cfg.url}|${cfg.account}|${cfg.password ?? ''}`;\n if (tokenCache && tokenCache.key === key && Date.now() - tokenCache.at < 10 * 60_000) return tokenCache.token;\n const json = await fetchJson(`${cfg.url}/api.php/v1/tokens`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ account: cfg.account, password: cfg.password }),\n });\n const token = typeof json.token === 'string' ? json.token : '';\n if (!token) throw new Error('token 获取失败:检查 account / password');\n tokenCache = { key, token, at: Date.now() };\n return token;\n}\n\nfunction normBug(b: Record<string, unknown>, account: string): ZenBug {\n const assigned = b.assignedTo;\n const assignedTo =\n typeof assigned === 'string'\n ? assigned\n : assigned && typeof assigned === 'object' && 'realname' in (assigned as Record<string, unknown>)\n ? String((assigned as Record<string, unknown>).realname ?? '')\n : '';\n const assignedAccount =\n typeof assigned === 'string'\n ? assigned\n : assigned && typeof assigned === 'object'\n ? String((assigned as Record<string, unknown>).account ?? '')\n : '';\n const mine = !!account && (assignedAccount === account || assignedTo === account);\n return {\n id: Number(b.id),\n title: String(b.title ?? ''),\n severity: (b.severity as number | string) ?? 4,\n pri: (b.pri as number | string) ?? 3,\n status: String(b.status ?? ''),\n assignedTo,\n openedBy: typeof b.openedBy === 'string' ? b.openedBy : undefined,\n mine,\n };\n}\n\n/** 只读拉取禅道 bug 列表(GET,绝不写)。失败返回 ok:false,不抛。 */\nexport async function fetchBugs(root: string): Promise<BugsResult> {\n const cfg = loadZgoalConfig(root);\n if (!cfg) return { ok: false, error: '.zgoal/config.yaml 缺失或 url/product 未配置(由 zgoal skill 创建)' };\n try {\n const token = await getToken(cfg);\n const json = await fetchJson(\n `${cfg.url}/api.php/v1/products/${cfg.product}/bugs?page=1&limit=100`,\n { headers: { Token: token } },\n );\n const raw = Array.isArray(json.bugs) ? (json.bugs as Record<string, unknown>[]) : [];\n return { ok: true, url: cfg.url, total: Number(json.total ?? raw.length), bugs: raw.map((b) => normBug(b, cfg.account)) };\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return { ok: false, error: `禅道请求失败(${msg})——检查 url / 凭据 / 是否开启 RESTful API v1` };\n }\n}\n","import ky from 'ky';\nimport { HttpError, NetworkError } from '../errors.js';\n\nexport async function fetchJson(url: string, init?: RequestInit): Promise<Record<string, unknown>> {\n try {\n const res = await ky(url, { ...init, timeout: 8000, retry: 2 });\n return await res.json() as Record<string, unknown>;\n } catch (e) {\n if (e instanceof HttpError) throw e;\n if (e instanceof Error && e.name === 'TimeoutError') {\n throw new NetworkError(`请求超时: ${url}`);\n }\n if (e instanceof Error && e.name === 'HTTPError') {\n throw new HttpError((e as Error & { status?: number }).status ?? 500, e.message);\n }\n throw new NetworkError(`请求失败: ${url}`, e);\n }\n}\n","export class HttpError extends Error {\n constructor(\n public status: number,\n message: string,\n public body?: unknown,\n ) {\n super(message);\n this.name = 'HttpError';\n }\n}\n\nexport class NetworkError extends Error {\n constructor(message: string, public cause?: unknown) {\n super(message);\n this.name = 'NetworkError';\n }\n}\n\nexport function isHttpError(e: unknown): e is HttpError {\n return e instanceof HttpError;\n}\n","/**\n * zdashboard plugin system\n *\n * Plugin contract:\n * {\n * mode: string; // unique mode identifier, e.g. 'bugs'\n * label: string; // human label, e.g. '禅道'\n * icon?: string; // optional emoji or icon name\n * viewer: () => Promise<{ default: React.ComponentType }>;\n * sidebar?: () => Promise<{ default: React.ComponentType }>;\n * apiRoutes?: Record<string, (req: http.IncomingMessage, res: http.ServerResponse, root: string) => void>;\n * }\n */\n\nimport http from 'node:http';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nexport interface DashboardPlugin {\n mode: string;\n label: string;\n icon?: string;\n viewer: () => Promise<{ default: React.ComponentType }>;\n sidebar?: () => Promise<{ default: React.ComponentType }>;\n apiRoutes?: Record<string, (req: http.IncomingMessage, res: http.ServerResponse, root: string) => void>;\n}\n\nexport interface PluginContext {\n root: string;\n appDir: string;\n}\n\nconst builtinPlugins = new Map<string, DashboardPlugin>();\n\nexport function registerBuiltin(plugin: DashboardPlugin) {\n builtinPlugins.set(plugin.mode, plugin);\n}\n\nexport function getBuiltin(mode: string): DashboardPlugin | undefined {\n return builtinPlugins.get(mode);\n}\n\nexport function allBuiltins(): DashboardPlugin[] {\n return Array.from(builtinPlugins.values());\n}\n\nexport async function loadExternalPlugins(pluginDirs: string[]): Promise<DashboardPlugin[]> {\n const plugins: DashboardPlugin[] = [];\n for (const dir of pluginDirs) {\n if (!fs.existsSync(dir)) continue;\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const indexPath = path.join(dir, entry.name, 'index.ts');\n if (!fs.existsSync(indexPath)) continue;\n try {\n const mod = await import(path.join(dir, entry.name, 'index.ts'));\n const plugin = mod.default as DashboardPlugin;\n if (plugin?.mode) {\n plugins.push(plugin);\n }\n } catch (e) {\n console.error(`[zdashboard] failed to load plugin ${entry.name}:`, e);\n }\n }\n }\n return plugins;\n}\n","{\n \"name\": \"zdashboard\",\n \"version\": \"1.1.0\",\n \"description\": \"ZCode skill dashboard platform — pluggable viewers for zdesign/zview/zreview/zgoal\",\n \"type\": \"module\",\n \"bin\": {\n \"zdashboard\": \"./dist/cli.js\"\n },\n \"files\": [\n \"dist\"\n ],\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"scripts\": {\n \"dev\": \"vite\",\n \"build\": \"tsup && vite build\",\n \"build:web\": \"vite build\",\n \"build:node\": \"tsup\",\n \"start\": \"node dist/cli.js\",\n \"preview\": \"vite preview\"\n },\n \"dependencies\": {\n \"@radix-ui/react-scroll-area\": \"^1.2.0\",\n \"@radix-ui/react-separator\": \"^1.1.0\",\n \"@radix-ui/react-slot\": \"^1.1.0\",\n \"@radix-ui/react-tooltip\": \"^1.1.2\",\n \"@uidotdev/usehooks\": \"^2.4.0\",\n \"ansi-to-react\": \"^6.1.6\",\n \"class-variance-authority\": \"^0.7.0\",\n \"clsx\": \"^2.1.1\",\n \"date-fns\": \"^3.6.0\",\n \"filesize\": \"^11.0.0\",\n \"highlight.js\": \"^11.11.1\",\n \"katex\": \"^0.16.11\",\n \"ky\": \"^1.7.0\",\n \"lodash-es\": \"^4.17.21\",\n \"lucide-react\": \"^0.460.0\",\n \"react\": \"^18.3.1\",\n \"react-dom\": \"^18.3.1\",\n \"react-error-boundary\": \"^5.0.0\",\n \"react-markdown\": \"^9.0.1\",\n \"rehype-autolink-headings\": \"^7.1.0\",\n \"rehype-highlight\": \"^7.0.1\",\n \"rehype-katex\": \"^7.0.1\",\n \"rehype-raw\": \"^7.0.0\",\n \"rehype-slug\": \"^6.0.0\",\n \"remark-frontmatter\": \"^5.0.0\",\n \"remark-gfm\": \"^4.0.0\",\n \"remark-math\": \"^6.0.0\",\n \"sonner\": \"^1.5.0\",\n \"tailwind-merge\": \"^2.5.4\",\n \"use-debounce\": \"^10.0.0\"\n },\n \"devDependencies\": {\n \"@tailwindcss/typography\": \"^0.5.20\",\n \"@testing-library/jest-dom\": \"^7.0.1\",\n \"@testing-library/react\": \"^16.3.2\",\n \"@types/node\": \"^22.9.0\",\n \"@types/react\": \"^18.3.12\",\n \"@types/react-dom\": \"^18.3.1\",\n \"@vitejs/plugin-react\": \"^4.7.0\",\n \"autoprefixer\": \"^10.4.20\",\n \"jsdom\": \"^30.0.1\",\n \"postcss\": \"^8.4.49\",\n \"tailwindcss\": \"^3.4.14\",\n \"tailwindcss-animate\": \"^1.0.7\",\n \"tsup\": \"^8.3.5\",\n \"typescript\": \"^5.6.3\",\n \"vite\": \"^5.4.10\",\n \"vitest\": \"^1.6.0\"\n },\n \"pnpm\": {\n \"onlyBuiltDependencies\": [\n \"esbuild\"\n ]\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { execFile } from 'node:child_process';\n\nexport interface DetectResult {\n hasOpenspec: boolean;\n hasDocs: boolean;\n hasJust: boolean;\n hasBugs: boolean;\n}\n\nfunction justAvailable(cwd: string): Promise<boolean> {\n return new Promise((resolve) => {\n const child = execFile('just', ['--list', '--unsorted'], { cwd, timeout: 5000 }, (err) => {\n resolve(!err);\n });\n if (child.killed) resolve(false);\n });\n}\n\nexport async function detect(root: string): Promise<DetectResult> {\n const hasOpenspec = fs.existsSync(path.join(root, 'openspec'));\n const hasDocs = fs.existsSync(path.join(root, 'docs'));\n const hasJust = await justAvailable(root);\n const hasBugs = fs.existsSync(path.join(root, '.zgoal', 'config.yaml'));\n return { hasOpenspec, hasDocs, hasJust, hasBugs };\n}\n","import { createServer } from './server/index.js';\nimport { detect } from './server/detect.js';\n\nfunction parseArgs(a: string[]): Record<string, string | true> {\n const o: Record<string, string | true> = {};\n for (let i = 0; i < a.length; i++) {\n if (a[i].indexOf('--') === 0) {\n const n = a[i + 1];\n o[a[i].slice(2)] = n && n.indexOf('--') !== 0 ? a[++i] : true;\n }\n }\n return o;\n}\n\nconst args = parseArgs(process.argv.slice(2));\nconst root = (args.dir as string) ?? '.';\n\nconst det = await detect(root);\ncreateServer({\n root,\n port: args.port ? parseInt(args.port as string, 10) : undefined,\n open: !!args.open,\n detect: det,\n mode: args.mode as string | undefined,\n});\n"],"mappings":"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","names":["fs","path","fileURLToPath","root","fs","path","root","path","fs","fs","path","__dirname","path","fileURLToPath","fs","det","handler","fp","fs","path","execFile","root"]}
|
|
1
|
+
{"version":3,"sources":["../src/server/index.ts","../src/server/spec-scan.ts","../src/server/just-runner.ts","../src/server/bugs.ts","../src/server/api/fetch.ts","../src/server/errors.ts","../src/server/plugins.ts","../src/server/review-store.ts","../src/server/design-assets.ts","../package.json","../src/server/detect.ts","../src/cli.ts"],"sourcesContent":["import http from 'node:http';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport crypto from 'node:crypto';\nimport { exec } from 'node:child_process';\nimport { fileURLToPath } from 'node:url';\nimport { scanTree } from './spec-scan.js';\nimport { JustRunner } from './just-runner.js';\nimport { fetchBugs } from './bugs.js';\nimport { registerBuiltin, allBuiltins, type DashboardPlugin } from './plugins.js';\nimport { ReviewStore, type ItemState, type ReviewStatus } from './review-store.js';\nimport { scanAssets } from './design-assets.js';\nimport type { DetectResult } from './detect.js';\nimport pkg from '../../package.json' with { type: 'json' };\n\nconst VERSION = pkg.version;\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\nconst STOP_TOKEN = crypto.randomBytes(12).toString('hex');\nconst INJECT = `<script>(function(){try{var es=new EventSource('/__reload');es.addEventListener('reload',function(){location.reload();});es.onerror=function(){es.close();};}catch(e){}document.addEventListener('click',function(e){var t=e.target;if(t&&t.closest){var a=t.closest('a[target]');if(a&&a.target!=='_self'){a.target='_self';}}},true);})();</script>`;\n\nconst MIME: Record<string, string> = {\n '.html': 'text/html; charset=utf-8', '.htm': 'text/html; charset=utf-8',\n '.css': 'text/css; charset=utf-8', '.js': 'application/javascript; charset=utf-8',\n '.mjs': 'application/javascript; charset=utf-8', '.json': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml', '.png': 'image/png', '.ico': 'image/x-icon',\n '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.webp': 'image/webp',\n '.md': 'text/markdown; charset=utf-8', '.txt': 'text/plain; charset=utf-8',\n '.yml': 'text/yaml; charset=utf-8', '.yaml': 'text/yaml; charset=utf-8',\n '.woff': 'font/woff', '.woff2': 'font/woff2', '.ttf': 'font/ttf',\n '.map': 'application/json; charset=utf-8',\n};\n\nexport interface ServerOptions {\n root: string;\n port?: number;\n open?: boolean;\n detect: DetectResult;\n dashboardDir?: string;\n mode?: string;\n}\n\nfunction readBody(req: http.IncomingMessage): Promise<string> {\n return new Promise((resolve) => {\n let data = '';\n req.on('data', (c) => (data += c));\n req.on('end', () => resolve(data));\n });\n}\n\nexport function createServer(opts: ServerOptions) {\n const ROOT = path.resolve(opts.root);\n const PORT0 = opts.port ?? 4190;\n const OPEN = !!opts.open;\n const APP_DIR = opts.dashboardDir ?? path.resolve(__dirname, 'web');\n if (!fs.existsSync(ROOT)) fs.mkdirSync(ROOT, { recursive: true });\n const det = opts.detect;\n const runner = new JustRunner(ROOT);\n const MODE = opts.mode;\n\n // register builtin plugins (server-side: apiRoutes only; viewer loaded by frontend)\n registerBuiltin({\n mode: 'bugs', label: '禅道 Bugs', icon: '🎯',\n apiRoutes: { '/__bugs': async (_, res) => { res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8', 'Cache-Control': 'no-cache' }); fetchBugs(ROOT).then((r) => res.end(JSON.stringify(r))); } }\n });\n registerBuiltin({ mode: 'view', label: '项目浏览', icon: '👁️' });\n\n const reviewStore = new ReviewStore(ROOT);\n registerBuiltin({\n mode: 'review', label: '文档评审', icon: '✅',\n apiRoutes: {\n '/__review': async (_, res) => {\n res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8', 'Cache-Control': 'no-cache' });\n res.end(JSON.stringify(reviewStore.read()));\n },\n '/__review/item': async (req, res) => {\n if (req.headers['x-stop-token'] !== STOP_TOKEN) { res.writeHead(403); res.end('forbidden'); return; }\n (async () => {\n try {\n const body = JSON.parse(await readBody(req) || '{}');\n const data = reviewStore.updateItem(body.id, { answer: body.answer, state: body.state as ItemState | undefined });\n res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });\n res.end(JSON.stringify(data));\n } catch (e) {\n res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });\n res.end(JSON.stringify({ error: (e as Error).message }));\n }\n })();\n return;\n },\n '/__review/status': async (req, res) => {\n if (req.headers['x-stop-token'] !== STOP_TOKEN) { res.writeHead(403); res.end('forbidden'); return; }\n (async () => {\n try {\n const body = JSON.parse(await readBody(req) || '{}');\n const data = reviewStore.setStatus(body.status as ReviewStatus);\n res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });\n res.end(JSON.stringify(data));\n } catch (e) {\n res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });\n res.end(JSON.stringify({ error: (e as Error).message }));\n }\n })();\n return;\n },\n '/__docs': async (_, res) => {\n res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8', 'Cache-Control': 'no-cache' });\n res.end(JSON.stringify(reviewStore.docs()));\n },\n }\n });\n\n registerBuiltin({ mode: 'design', label: '设计资产', icon: '🎨' });\n\n const clients = new Set<http.ServerResponse>();\n const broadcast = (ev: string, data: unknown = '') => {\n const payload = `event: ${ev}\\ndata: ${JSON.stringify(data == null ? '' : data)}\\n\\n`;\n for (const c of clients) c.write(payload);\n };\n\n function serveFile(filePath: string, res: http.ServerResponse, injectHtml: boolean) {\n fs.readFile(filePath, (err, data) => {\n if (err) { res.writeHead(404); return res.end('Not found'); }\n const ext = path.extname(filePath).toLowerCase();\n const ct = MIME[ext] ?? 'application/octet-stream';\n let body = data;\n if (injectHtml && ext === '.html') {\n const s = data.toString('utf8');\n body = Buffer.from(s.indexOf('</body>') >= 0 ? s.replace('</body>', INJECT + '</body>') : s + INJECT);\n }\n res.writeHead(200, { 'Content-Type': ct, 'Cache-Control': 'no-cache' });\n res.end(body);\n });\n }\n\n function handler(req: http.IncomingMessage, res: http.ServerResponse) {\n const url = req.url!.split('?')[0];\n\n // ── SSE:文件变更 ──\n if (url === '/__reload') {\n res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', Connection: 'keep-alive' });\n res.write(': connected\\n\\n');\n clients.add(res);\n req.on('close', () => clients.delete(res));\n return;\n }\n if (url === '/__config') {\n res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8', 'Cache-Control': 'no-cache' });\n return res.end(JSON.stringify({ stopToken: STOP_TOKEN }));\n }\n if (url === '/__stop' && req.method === 'POST') {\n if (req.headers['x-stop-token'] === STOP_TOKEN) {\n res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });\n res.end('{\"ok\":true}');\n runner.stop();\n setTimeout(() => { try { server.close(); } catch {} process.exit(0); }, 50);\n } else { res.writeHead(403); res.end('forbidden'); }\n return;\n }\n\n // ── 方案模式:树形文件清单(+探测结果) / 设计模式:资产分类清单 ──\n if (url === '/__files') {\n if (MODE === 'design') {\n res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8', 'Cache-Control': 'no-cache' });\n return res.end(JSON.stringify(scanAssets(ROOT)));\n }\n const tree = scanTree(ROOT, det.hasOpenspec, det.hasDocs);\n const payload: TreeNodePayload = { tree, ...det };\n res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8', 'Cache-Control': 'no-cache' });\n return res.end(JSON.stringify(payload));\n }\n\n // ── 日志能力 ──\n if (url === '/__just/recipes') {\n res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8', 'Cache-Control': 'no-cache' });\n runner.recipes().then((r) => res.end(JSON.stringify(r)));\n return;\n }\n if (url === '/__just/logs') {\n res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', Connection: 'keep-alive' });\n res.write(': connected\\n\\n');\n const unsub = runner.subscribe((ev) => res.write(`data: ${JSON.stringify(ev)}\\n\\n`));\n req.on('close', unsub);\n return;\n }\n const justAction = url.match(/^\\/__just\\/(start|stop|restart)$/);\n if (justAction && req.method === 'POST') {\n (async () => {\n if (req.headers['x-stop-token'] !== STOP_TOKEN) { res.writeHead(403); res.end('forbidden'); return; }\n const body = await readBody(req);\n let recipe: string | undefined;\n try { recipe = JSON.parse(body || '{}').recipe; } catch { /* ignore */ }\n const act = justAction[1];\n if (act === 'start' || act === 'restart') {\n const target = recipe ?? runner.info().recipe;\n if (!target) { res.writeHead(400); res.end('{\"error\":\"no recipe\"}'); return; }\n const recipes = await runner.recipes();\n if (!recipes.some((r) => r.name === target)) { res.writeHead(403); res.end('{\"error\":\"unknown recipe\"}'); return; }\n runner.start(target);\n } else {\n runner.stop();\n }\n res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });\n res.end(JSON.stringify(runner.info()));\n })();\n return;\n }\n\n // ── plugin API routes ──\n let handled = false;\n for (const plugin of allBuiltins()) {\n if (!plugin.apiRoutes) continue;\n for (const [route, handler] of Object.entries(plugin.apiRoutes)) {\n if (url === route) {\n handled = true;\n handler(req, res, ROOT);\n return;\n }\n }\n }\n\n // ── dashboard 前端 ──\n if (url === '/') return serveFile(path.join(APP_DIR, 'index.html'), res, false);\n if (url.indexOf('/__app/') === 0) {\n const fp = path.join(APP_DIR, url.slice(7));\n if (fp !== APP_DIR && fp.indexOf(APP_DIR + path.sep) !== 0) { res.writeHead(403); return res.end('Forbidden'); }\n return serveFile(fp, res, false);\n }\n if (url.indexOf('/assets/') === 0) {\n const fp = path.join(APP_DIR, decodeURIComponent(url));\n if (fp.indexOf(APP_DIR + path.sep) !== 0) { res.writeHead(403); return res.end('Forbidden'); }\n return serveFile(fp, res, false);\n }\n\n // ── 用户资产 ──\n const fp = path.join(ROOT, decodeURIComponent(url));\n if (fp !== ROOT && fp.indexOf(ROOT + path.sep) !== 0) { res.writeHead(403); return res.end('Forbidden'); }\n return serveFile(fp, res, true);\n }\n\n let server: http.Server;\n function start(port: number) {\n server = http.createServer(handler);\n server.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') { console.log(`[zdashboard] port ${port} busy, trying ${port + 1}`); start(port + 1); }\n else throw err;\n });\n server.listen(port, () => {\n const u = `http://localhost:${port}`;\n console.log(`[zdashboard] v${VERSION} dashboard -> ${u}`);\n console.log(`[zdashboard] project -> ${ROOT}`);\n console.log(`[zdashboard] mode -> ${MODE ?? '(auto)'}`);\n console.log(`[zdashboard] detect -> openspec:${det.hasOpenspec} docs:${det.hasDocs} just:${det.hasJust} bugs:${det.hasBugs}`);\n if (OPEN) exec(process.platform === 'darwin' ? `open ${u}` : `start ${u}`);\n });\n }\n\n let debounce: NodeJS.Timeout;\n try {\n fs.watch(ROOT, { recursive: true }, () => {\n clearTimeout(debounce);\n debounce = setTimeout(() => {\n broadcast('reload');\n broadcast('files');\n console.log(`[zdashboard] change -> reload + refresh tree (${clients.size} client${clients.size === 1 ? '' : 's'})`);\n }, 150);\n });\n } catch { console.log('[zdashboard] watch unavailable - static only.'); }\n\n start(PORT0);\n}\n\ninterface TreeNodePayload { tree: unknown; hasOpenspec: boolean; hasDocs: boolean; hasJust: boolean; hasBugs: boolean; }\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nexport type NodeKind = 'file' | 'dir' | 'log';\nexport interface TreeNode {\n name: string;\n kind: NodeKind;\n path?: string; // file: 相对 root 的路径(点击预览用)\n defaultCollapsed?: boolean;\n children?: TreeNode[];\n}\n\nfunction walkFiles(absDir: string, relDir: string, depth = 0): TreeNode[] {\n if (depth > 4) return [];\n let ents: fs.Dirent[];\n try { ents = fs.readdirSync(absDir, { withFileTypes: true }); } catch { return []; }\n const nodes: TreeNode[] = [];\n for (const ent of ents) {\n if (ent.name.startsWith('.') || ent.name === 'node_modules') continue;\n const rel = relDir ? `${relDir}/${ent.name}` : ent.name;\n if (ent.isDirectory()) {\n nodes.push({ name: ent.name, kind: 'dir', children: walkFiles(path.join(absDir, ent.name), rel, depth + 1) });\n } else {\n nodes.push({ name: ent.name, kind: 'file', path: rel });\n }\n }\n nodes.sort((a, b) => (a.kind === b.kind ? a.name.localeCompare(b.name) : a.kind === 'dir' ? -1 : 1));\n return nodes;\n}\n\n/** 方案模式树形扫描:openspec 感知 + docs 聚合 + 其他兜底 */\nexport function scanTree(root: string, hasOpenspec: boolean, hasDocs: boolean): TreeNode[] {\n const tree: TreeNode[] = [];\n if (hasOpenspec && fs.existsSync(path.join(root, 'openspec', 'changes'))) {\n const changesDir = path.join(root, 'openspec', 'changes');\n const active: TreeNode[] = [];\n const archived: TreeNode[] = [];\n for (const ent of fs.readdirSync(changesDir, { withFileTypes: true })) {\n if (!ent.isDirectory() || ent.name.startsWith('.') || ent.name === 'archive') continue;\n active.push({ name: ent.name, kind: 'dir', children: walkFiles(path.join(changesDir, ent.name), `openspec/changes/${ent.name}`) });\n }\n active.sort((a, b) => a.name.localeCompare(b.name));\n const archiveDir = path.join(changesDir, 'archive');\n if (fs.existsSync(archiveDir)) {\n for (const ent of fs.readdirSync(archiveDir, { withFileTypes: true })) {\n if (!ent.isDirectory() || ent.name.startsWith('.')) continue;\n archived.push({ name: ent.name, kind: 'dir', children: walkFiles(path.join(archiveDir, ent.name), `openspec/changes/archive/${ent.name}`) });\n }\n archived.sort((a, b) => b.name.localeCompare(a.name)); // 日期前缀倒序\n }\n if (active.length) tree.push({ name: `进行中 (${active.length})`, kind: 'dir', children: active });\n if (archived.length) tree.push({ name: `归档 (${archived.length})`, kind: 'dir', defaultCollapsed: true, children: archived });\n const specsDir = path.join(root, 'openspec', 'specs');\n if (fs.existsSync(specsDir)) {\n const specs = walkFiles(specsDir, 'openspec/specs');\n if (specs.length) tree.push({ name: '能力 Specs', kind: 'dir', children: specs });\n }\n }\n if (hasDocs && fs.existsSync(path.join(root, 'docs'))) {\n const docs = walkFiles(path.join(root, 'docs'), 'docs');\n if (docs.length) tree.push({ name: 'docs', kind: 'dir', children: docs });\n }\n const skip = new Set(['openspec', 'docs', 'node_modules', '.git', 'dist', 'test-server']);\n // \"其他\"只收根目录的 md 文档(README/CLAUDE 等);构建配置(pom.xml/justfile 等)不收——对\"方案+日志\"定位是噪音\n const etc: TreeNode[] = [];\n try {\n for (const ent of fs.readdirSync(root, { withFileTypes: true })) {\n if (ent.name.startsWith('.') || skip.has(ent.name)) continue;\n const ext = path.extname(ent.name).toLowerCase();\n if (ent.isFile() && (ext === '.md' || ext === '.markdown')) etc.push({ name: ent.name, kind: 'file', path: ent.name });\n }\n } catch {}\n etc.sort((a, b) => a.name.localeCompare(b.name));\n if (etc.length) tree.push({ name: `其他 (${etc.length})`, kind: 'dir', children: etc });\n return tree;\n}\n","import { spawn, execFile, type ChildProcess } from 'node:child_process';\n\nexport interface Recipe { name: string; description: string; }\nexport type JustState = 'idle' | 'running' | 'exited';\nexport type JustEvent =\n | { type: 'log'; text: string }\n | { type: 'clear' }\n | { type: 'state'; state: JustState; recipe: string | null; code: number | null };\n\nconst MAX_BUFFER = 1000;\n\nexport class JustRunner {\n private cwd: string;\n private child: ChildProcess | null = null;\n private recipe: string | null = null;\n private state: JustState = 'idle';\n private code: number | null = null;\n private buffer: string[] = [];\n private pending = ''; // 行缓冲:块缓冲输出(如 maven)的 chunk 会在行中间断开,攒到 \\n 才切行\n private clients = new Set<(ev: JustEvent) => void>();\n private recipesCache: Recipe[] | null = null;\n\n constructor(cwd: string) { this.cwd = cwd; }\n\n recipes(): Promise<Recipe[]> {\n if (this.recipesCache) return Promise.resolve(this.recipesCache);\n return new Promise((resolve) => {\n execFile('just', ['--list', '--unsorted'], { cwd: this.cwd, maxBuffer: 1 << 20, timeout: 8000 }, (err, stdout) => {\n if (err) { resolve([]); return; }\n const out: Recipe[] = [];\n const seen = new Set<string>();\n for (const line of stdout.split(/\\r?\\n/).slice(1)) { // 跳过 \"Available recipes:\"\n const trimmed = line.trim();\n if (!trimmed) continue;\n const hashIdx = trimmed.indexOf('#');\n const sig = (hashIdx >= 0 ? trimmed.slice(0, hashIdx) : trimmed).trim();\n if (!sig) continue;\n const name = sig.split(/\\s+/)[0]; // \"hello msg=...\" -> \"hello\"\n if (seen.has(name)) continue;\n seen.add(name);\n out.push({ name, description: hashIdx >= 0 ? trimmed.slice(hashIdx + 1).trim() : '' });\n }\n this.recipesCache = out;\n resolve(out);\n });\n });\n }\n\n subscribe(fn: (ev: JustEvent) => void): () => void {\n this.clients.add(fn);\n // 连上即重放:历史日志 + 当前状态\n for (const text of this.buffer) fn({ type: 'log', text });\n fn({ type: 'state', state: this.state, recipe: this.recipe, code: this.code });\n return () => this.clients.delete(fn);\n }\n\n private emit(ev: JustEvent) { for (const fn of this.clients) fn(ev); }\n\n info() { return { state: this.state, recipe: this.recipe, code: this.code }; }\n\n /** 启动 recipe(调用方须先用 recipes() 校验名字);自动停旧进程 */\n start(recipe: string) {\n this.killChild();\n this.recipe = recipe;\n this.code = null;\n this.state = 'running';\n this.buffer = [];\n this.pending = '';\n this.emit({ type: 'clear' }); // 广播清屏:已连接的订阅者同步清掉上一个任务的残留日志\n this.emit({ type: 'state', state: 'running', recipe, code: null });\n const child = spawn('just', [recipe], {\n cwd: this.cwd,\n shell: true,\n env: {\n ...process.env,\n FORCE_COLOR: '1', // node 生态(chalk 等)\n // maven 检测非 tty 会关颜色;经 MAVEN_OPTS 强制开(保留用户已有值)\n MAVEN_OPTS: `${process.env.MAVEN_OPTS ?? ''} -Dstyle.color=always`.trim(),\n CI: '',\n },\n });\n this.child = child;\n const push = (d: Buffer) => {\n this.pending += d.toString();\n let idx: number;\n while ((idx = this.pending.indexOf('\\n')) >= 0) {\n const line = this.pending.slice(0, idx + 1);\n this.pending = this.pending.slice(idx + 1);\n this.pushLine(line);\n }\n // 无 \\n 的尾巴留在 pending,等下个 chunk(块缓冲输出会在行中断开,不能当独立行)\n };\n child.stdout?.on('data', push);\n child.stderr?.on('data', push);\n child.on('error', (err) => { this.pushLine(`[zview] spawn error: ${err.message}\\n`); });\n child.on('exit', (code) => {\n if (this.pending) { this.pushLine(this.pending + '\\n'); this.pending = ''; } // flush 末尾无换行的残留\n this.child = null;\n this.state = 'exited';\n this.code = code ?? 0;\n this.emit({ type: 'state', state: 'exited', recipe: this.recipe, code: this.code });\n });\n }\n\n private pushLine(line: string) {\n this.buffer.push(line);\n if (this.buffer.length > MAX_BUFFER) this.buffer.shift();\n this.emit({ type: 'log', text: line });\n }\n\n stop() {\n this.killChild();\n }\n\n restart(recipe?: string) {\n const target = recipe ?? this.recipe;\n if (target) this.start(target);\n }\n\n private killChild() {\n const child = this.child;\n if (child?.pid) {\n try {\n if (process.platform === 'win32') spawn('taskkill', ['/PID', String(child.pid), '/T', '/F']);\n else child.kill('SIGTERM');\n } catch { /* 已退出 */ }\n }\n this.child = null;\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { fetchJson } from './api/fetch.js';\n\n/** .zgoal/config.yaml(zgoal skill 的禅道凭据配置,扁平 key: value) */\nexport interface ZgoalConfig {\n url: string;\n account: string;\n password?: string;\n token?: string;\n product: number;\n}\n\nexport interface ZenBug {\n id: number;\n title: string;\n severity: number | string;\n pri: number | string;\n status: string;\n assignedTo: string;\n openedBy?: string;\n /** 指派给 config.account 的本人 */\n mine: boolean;\n}\n\nexport type BugsResult =\n | { ok: true; url: string; total: number; bugs: ZenBug[] }\n | { ok: false; error: string };\n\n/** 极简扁平 yaml 解析(仅 key: value 行,够 .zgoal/config.yaml 用) */\nfunction loadZgoalConfig(root: string): ZgoalConfig | null {\n const file = path.join(root, '.zgoal', 'config.yaml');\n if (!fs.existsSync(file)) return null;\n const kv: Record<string, string> = {};\n for (const line of fs.readFileSync(file, 'utf8').split('\\n')) {\n const m = line.match(/^\\s*([A-Za-z_]\\w*)\\s*:\\s*(.+?)\\s*$/);\n if (m && !m[2].startsWith('#')) kv[m[1]] = m[2].replace(/^[\"']|[\"']$/g, '');\n }\n const product = Number(kv.product);\n if (!kv.url || !product) return null;\n return {\n url: kv.url.replace(/\\/+$/, ''),\n account: kv.account ?? '',\n password: kv.password,\n token: kv.token,\n product,\n };\n}\n\nlet tokenCache: { key: string; token: string; at: number } | null = null;\n\nasync function getToken(cfg: ZgoalConfig): Promise<string> {\n if (cfg.token) return cfg.token;\n const key = `${cfg.url}|${cfg.account}|${cfg.password ?? ''}`;\n if (tokenCache && tokenCache.key === key && Date.now() - tokenCache.at < 10 * 60_000) return tokenCache.token;\n const json = await fetchJson(`${cfg.url}/api.php/v1/tokens`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ account: cfg.account, password: cfg.password }),\n });\n const token = typeof json.token === 'string' ? json.token : '';\n if (!token) throw new Error('token 获取失败:检查 account / password');\n tokenCache = { key, token, at: Date.now() };\n return token;\n}\n\nfunction normBug(b: Record<string, unknown>, account: string): ZenBug {\n const assigned = b.assignedTo;\n const assignedTo =\n typeof assigned === 'string'\n ? assigned\n : assigned && typeof assigned === 'object' && 'realname' in (assigned as Record<string, unknown>)\n ? String((assigned as Record<string, unknown>).realname ?? '')\n : '';\n const assignedAccount =\n typeof assigned === 'string'\n ? assigned\n : assigned && typeof assigned === 'object'\n ? String((assigned as Record<string, unknown>).account ?? '')\n : '';\n const mine = !!account && (assignedAccount === account || assignedTo === account);\n return {\n id: Number(b.id),\n title: String(b.title ?? ''),\n severity: (b.severity as number | string) ?? 4,\n pri: (b.pri as number | string) ?? 3,\n status: String(b.status ?? ''),\n assignedTo,\n openedBy: typeof b.openedBy === 'string' ? b.openedBy : undefined,\n mine,\n };\n}\n\n/** 只读拉取禅道 bug 列表(GET,绝不写)。失败返回 ok:false,不抛。 */\nexport async function fetchBugs(root: string): Promise<BugsResult> {\n const cfg = loadZgoalConfig(root);\n if (!cfg) return { ok: false, error: '.zgoal/config.yaml 缺失或 url/product 未配置(由 zgoal skill 创建)' };\n try {\n const token = await getToken(cfg);\n const json = await fetchJson(\n `${cfg.url}/api.php/v1/products/${cfg.product}/bugs?page=1&limit=100`,\n { headers: { Token: token } },\n );\n const raw = Array.isArray(json.bugs) ? (json.bugs as Record<string, unknown>[]) : [];\n return { ok: true, url: cfg.url, total: Number(json.total ?? raw.length), bugs: raw.map((b) => normBug(b, cfg.account)) };\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n return { ok: false, error: `禅道请求失败(${msg})——检查 url / 凭据 / 是否开启 RESTful API v1` };\n }\n}\n","import ky from 'ky';\nimport { HttpError, NetworkError } from '../errors.js';\n\nexport async function fetchJson(url: string, init?: RequestInit): Promise<Record<string, unknown>> {\n try {\n const res = await ky(url, { ...init, timeout: 8000, retry: 2 });\n return await res.json() as Record<string, unknown>;\n } catch (e) {\n if (e instanceof HttpError) throw e;\n if (e instanceof Error && e.name === 'TimeoutError') {\n throw new NetworkError(`请求超时: ${url}`);\n }\n if (e instanceof Error && e.name === 'HTTPError') {\n throw new HttpError((e as Error & { status?: number }).status ?? 500, e.message);\n }\n throw new NetworkError(`请求失败: ${url}`, e);\n }\n}\n","export class HttpError extends Error {\n constructor(\n public status: number,\n message: string,\n public body?: unknown,\n ) {\n super(message);\n this.name = 'HttpError';\n }\n}\n\nexport class NetworkError extends Error {\n constructor(message: string, public cause?: unknown) {\n super(message);\n this.name = 'NetworkError';\n }\n}\n\nexport function isHttpError(e: unknown): e is HttpError {\n return e instanceof HttpError;\n}\n","/**\n * zdashboard plugin system\n *\n * Plugin contract:\n * {\n * mode: string; // unique mode identifier, e.g. 'bugs'\n * label: string; // human label, e.g. '禅道'\n * icon?: string; // optional emoji or icon name\n * viewer: () => Promise<{ default: React.ComponentType }>;\n * sidebar?: () => Promise<{ default: React.ComponentType }>;\n * apiRoutes?: Record<string, (req: http.IncomingMessage, res: http.ServerResponse, root: string) => void>;\n * }\n */\n\nimport http from 'node:http';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nexport interface DashboardPlugin {\n mode: string;\n label: string;\n icon?: string;\n viewer?: () => Promise<{ default: React.ComponentType }>;\n sidebar?: () => Promise<{ default: React.ComponentType }>;\n apiRoutes?: Record<string, (req: http.IncomingMessage, res: http.ServerResponse, root: string) => void>;\n}\n\nexport interface PluginContext {\n root: string;\n appDir: string;\n}\n\nconst builtinPlugins = new Map<string, DashboardPlugin>();\n\nexport function registerBuiltin(plugin: DashboardPlugin) {\n builtinPlugins.set(plugin.mode, plugin);\n}\n\nexport function getBuiltin(mode: string): DashboardPlugin | undefined {\n return builtinPlugins.get(mode);\n}\n\nexport function allBuiltins(): DashboardPlugin[] {\n return Array.from(builtinPlugins.values());\n}\n\nexport async function loadExternalPlugins(pluginDirs: string[]): Promise<DashboardPlugin[]> {\n const plugins: DashboardPlugin[] = [];\n for (const dir of pluginDirs) {\n if (!fs.existsSync(dir)) continue;\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const indexPath = path.join(dir, entry.name, 'index.ts');\n if (!fs.existsSync(indexPath)) continue;\n try {\n const mod = await import(path.join(dir, entry.name, 'index.ts'));\n const plugin = mod.default as DashboardPlugin;\n if (plugin?.mode) {\n plugins.push(plugin);\n }\n } catch (e) {\n console.error(`[zdashboard] failed to load plugin ${entry.name}:`, e);\n }\n }\n }\n return plugins;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport YAML from 'yaml';\n\nexport type ItemState = 'open' | 'answered' | 'accepted' | 'dismissed';\nexport type ReviewStatus = 'draft' | 'reviewing' | 'passed' | 'rejected';\n\nexport interface ReviewItem {\n id: string;\n doc?: string;\n category?: string;\n severity?: 'high' | 'medium' | 'low';\n state: ItemState;\n question: string;\n answer?: string;\n}\n\nexport interface ReviewData {\n status: ReviewStatus;\n items: ReviewItem[];\n}\n\nconst REVIEW_FILE = 'review.yaml';\n\nexport class ReviewStore {\n private root: string;\n private file: string;\n private onChange?: () => void;\n\n constructor(root: string, onChange?: () => void) {\n this.root = root;\n this.file = path.join(root, REVIEW_FILE);\n this.onChange = onChange;\n }\n\n exists(): boolean { return fs.existsSync(this.file); }\n\n read(): ReviewData {\n try {\n const parsed = YAML.parse(fs.readFileSync(this.file, 'utf8')) as ReviewData;\n if (!parsed || !Array.isArray(parsed.items)) return { status: 'draft', items: [] };\n return parsed;\n } catch {\n return { status: 'draft', items: [] };\n }\n }\n\n private write(data: ReviewData) {\n fs.writeFileSync(this.file, YAML.stringify(data), 'utf8');\n this.onChange?.();\n }\n\n updateItem(id: string, patch: { answer?: string; state?: ItemState }): ReviewData {\n const data = this.read();\n const item = data.items.find((i) => i.id === id);\n if (!item) throw new Error(`item ${id} not found`);\n if (patch.answer !== undefined) item.answer = patch.answer;\n if (patch.state !== undefined) item.state = patch.state;\n if (patch.state === 'answered' && !patch.answer && !item.answer) item.answer = '';\n this.write(data);\n return data;\n }\n\n setStatus(status: ReviewStatus): ReviewData {\n const data = this.read();\n if (status === 'passed' && data.items.some((i) => i.state === 'open')) {\n throw new Error('存在未处理的评审项(open),不能通过');\n }\n data.status = status;\n this.write(data);\n return data;\n }\n\n docs(): string[] {\n try {\n return fs.readdirSync(this.root)\n .filter((f) => /\\.(md|markdown)$/i.test(f) && fs.statSync(path.join(this.root, f)).isFile())\n .sort();\n } catch { return []; }\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nexport type AssetType = 'page' | 'component' | 'icon' | 'token' | 'md' | 'video' | 'audio' | 'pdf' | 'code' | 'font' | 'other';\n\nconst PAGE_EXTS = ['.html', '.htm'];\nconst ICON_EXTS = ['.svg', '.png', '.ico', '.jpg', '.jpeg', '.gif', '.webp'];\nconst VIDEO_EXTS = ['.mp4', '.webm', '.mov', '.ogg', '.ogv'];\nconst AUDIO_EXTS = ['.mp3', '.wav', '.flac', '.aac', '.m4a'];\nconst CODE_EXTS = ['.js', '.mjs', '.ts', '.tsx', '.jsx', '.css', '.json', '.txt', '.xml', '.yml', '.yaml', '.sh', '.md'];\nconst FONT_EXTS = ['.woff', '.woff2', '.ttf', '.otf'];\nconst TOKEN_RE = /token|theme|design|color|palette|typograph/i;\n\nexport function categorize(rel: string, ext: string): AssetType {\n if (rel.indexOf('components/') === 0) return 'component';\n if (VIDEO_EXTS.includes(ext)) return 'video';\n if (AUDIO_EXTS.includes(ext)) return 'audio';\n if (ext === '.pdf') return 'pdf';\n if (ext === '.md') return 'md';\n if (FONT_EXTS.includes(ext)) return 'font';\n if (ICON_EXTS.includes(ext)) return 'icon';\n if (PAGE_EXTS.includes(ext)) return 'page';\n if (CODE_EXTS.includes(ext)) return TOKEN_RE.test(rel) && (ext === '.css' || ext === '.json') ? 'token' : 'code';\n return 'other';\n}\n\nexport interface AssetFile { path: string; name: string; ext: string; type: AssetType; }\nexport type ScanResult = Record<AssetType, AssetFile[]>;\n\nexport function scanAssets(root: string): ScanResult {\n const out: ScanResult = {} as ScanResult;\n const keys: AssetType[] = ['page','component','icon','token','md','video','audio','pdf','code','font','other'];\n for (const k of keys) out[k] = [];\n function walk(dir: string, rel: string) {\n let ents: fs.Dirent[];\n try { ents = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }\n for (const ent of ents) {\n if (ent.name.charAt(0) === '.') continue;\n const r = rel ? `${rel}/${ent.name}` : ent.name;\n if (ent.isDirectory()) { walk(path.join(dir, ent.name), r); continue; }\n const ext = path.extname(ent.name).toLowerCase();\n const t = categorize(r, ext);\n out[t].push({ path: r, name: ent.name, ext, type: t });\n }\n }\n walk(root, '');\n return out;\n}\n","{\n \"name\": \"zdashboard\",\n \"version\": \"1.2.0\",\n \"description\": \"ZCode skill dashboard platform — pluggable viewers for zdesign/zview/zreview/zgoal\",\n \"type\": \"module\",\n \"bin\": {\n \"zdashboard\": \"./dist/cli.js\"\n },\n \"files\": [\n \"dist\"\n ],\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"scripts\": {\n \"dev\": \"vite\",\n \"build\": \"tsup && vite build\",\n \"build:web\": \"vite build\",\n \"build:node\": \"tsup\",\n \"start\": \"node dist/cli.js\",\n \"preview\": \"vite preview\"\n },\n \"dependencies\": {\n \"@radix-ui/react-scroll-area\": \"^1.2.0\",\n \"@radix-ui/react-separator\": \"^1.1.0\",\n \"@radix-ui/react-slot\": \"^1.1.0\",\n \"@radix-ui/react-tooltip\": \"^1.1.2\",\n \"@uidotdev/usehooks\": \"^2.4.0\",\n \"ansi-to-react\": \"^6.1.6\",\n \"class-variance-authority\": \"^0.7.0\",\n \"clsx\": \"^2.1.1\",\n \"date-fns\": \"^3.6.0\",\n \"filesize\": \"^11.0.0\",\n \"highlight.js\": \"^11.11.1\",\n \"katex\": \"^0.16.11\",\n \"ky\": \"^1.7.0\",\n \"lodash-es\": \"^4.17.21\",\n \"lucide-react\": \"^0.460.0\",\n \"react\": \"^18.3.1\",\n \"react-dom\": \"^18.3.1\",\n \"react-error-boundary\": \"^5.0.0\",\n \"react-markdown\": \"^9.0.1\",\n \"rehype-autolink-headings\": \"^7.1.0\",\n \"rehype-highlight\": \"^7.0.1\",\n \"rehype-katex\": \"^7.0.1\",\n \"rehype-raw\": \"^7.0.0\",\n \"rehype-slug\": \"^6.0.0\",\n \"remark-frontmatter\": \"^5.0.0\",\n \"remark-gfm\": \"^4.0.0\",\n \"remark-math\": \"^6.0.0\",\n \"sonner\": \"^1.5.0\",\n \"tailwind-merge\": \"^2.5.4\",\n \"use-debounce\": \"^10.0.0\",\n \"yaml\": \"^2.9.0\"\n },\n \"devDependencies\": {\n \"@tailwindcss/typography\": \"^0.5.20\",\n \"@testing-library/jest-dom\": \"^7.0.1\",\n \"@testing-library/react\": \"^16.3.2\",\n \"@types/lodash-es\": \"^4.17.12\",\n \"@types/node\": \"^22.9.0\",\n \"@types/react\": \"^18.3.12\",\n \"@types/react-dom\": \"^18.3.1\",\n \"@vitejs/plugin-react\": \"^4.7.0\",\n \"autoprefixer\": \"^10.4.20\",\n \"jsdom\": \"^30.0.1\",\n \"postcss\": \"^8.4.49\",\n \"tailwindcss\": \"^3.4.14\",\n \"tailwindcss-animate\": \"^1.0.7\",\n \"tsup\": \"^8.3.5\",\n \"typescript\": \"^5.6.3\",\n \"vite\": \"^5.4.10\",\n \"vitest\": \"^1.6.0\"\n },\n \"pnpm\": {\n \"onlyBuiltDependencies\": [\n \"esbuild\"\n ]\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { execFile } from 'node:child_process';\n\nexport interface DetectResult {\n hasOpenspec: boolean;\n hasDocs: boolean;\n hasJust: boolean;\n hasBugs: boolean;\n}\n\nfunction justAvailable(cwd: string): Promise<boolean> {\n return new Promise((resolve) => {\n const child = execFile('just', ['--list', '--unsorted'], { cwd, timeout: 5000 }, (err) => {\n resolve(!err);\n });\n if (child.killed) resolve(false);\n });\n}\n\nexport async function detect(root: string): Promise<DetectResult> {\n const hasOpenspec = fs.existsSync(path.join(root, 'openspec'));\n const hasDocs = fs.existsSync(path.join(root, 'docs'));\n const hasJust = await justAvailable(root);\n const hasBugs = fs.existsSync(path.join(root, '.zgoal', 'config.yaml'));\n return { hasOpenspec, hasDocs, hasJust, hasBugs };\n}\n","import { createServer } from './server/index.js';\nimport { detect } from './server/detect.js';\n\nfunction parseArgs(a: string[]): Record<string, string | true> {\n const o: Record<string, string | true> = {};\n for (let i = 0; i < a.length; i++) {\n if (a[i].indexOf('--') === 0) {\n const n = a[i + 1];\n o[a[i].slice(2)] = n && n.indexOf('--') !== 0 ? a[++i] : true;\n }\n }\n return o;\n}\n\nconst args = parseArgs(process.argv.slice(2));\nconst root = (args.dir as string) ?? '.';\n\nconst det = await detect(root);\ncreateServer({\n root,\n port: args.port ? parseInt(args.port as string, 10) : undefined,\n open: !!args.open,\n detect: det,\n mode: args.mode as string | undefined,\n});\n"],"mappings":"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","names":["fs","path","fileURLToPath","root","fs","path","root","path","fs","fs","path","fs","path","root","fs","path","root","__dirname","path","fileURLToPath","fs","det","handler","fp","fs","path","execFile","root"]}
|