zdashboard 2.8.3 → 2.8.5
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 +80 -304
- package/dist/cli.js.map +1 -1
- package/dist/web/assets/CodeViewer-BrquqtDE.js +307 -0
- package/dist/web/assets/{FileIcon-a612-H-j.js → FileIcon-FyROY2Qe.js} +1 -1
- package/dist/web/assets/{FilterPills-BVAToHeu.js → FilterPills-DwCH0Z96.js} +1 -1
- package/dist/web/assets/{Sidebar-CmJ0RfM6.js → Sidebar-BUi2jDuu.js} +1 -1
- package/dist/web/assets/{Sidebar-CYYR-the.js → Sidebar-Cz3W5MP6.js} +1 -1
- package/dist/web/assets/Workspace-CafALYiK.js +1 -0
- package/dist/web/assets/Workspace-D-n83gau.js +1 -0
- package/dist/web/assets/{Workspace-DT0JWcsX.js → Workspace-Db4r4oQO.js} +1 -1
- package/dist/web/assets/Workspace-ICT0FTaI.js +1 -0
- package/dist/web/assets/index--t_ijYil.css +1 -0
- package/dist/web/assets/{index-C_16tmIU.js → index-BAFFLjYP.js} +12 -12
- package/dist/web/assets/{usePluginData-pzvMTyoN.js → usePluginData-DEpAv1Yl.js} +1 -1
- package/dist/web/assets/web-BP-QGcd3.js +2 -0
- package/dist/web/assets/web-CE86ahqk.js +2 -0
- package/dist/web/assets/{web-DwYp5Vde.js → web-D0z_1kb8.js} +2 -2
- package/dist/web/assets/web-S85uEYzf.js +2 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/BatchView-D9TMypRX.js +0 -2
- package/dist/web/assets/CheckpointViewer-DJ9yKpMh.js +0 -1
- package/dist/web/assets/CodeViewer-C_3Ms2Ri.js +0 -279
- package/dist/web/assets/DependencyGraph-hsA8ZmBD.js +0 -1
- package/dist/web/assets/ProgressBar-YEvCsTOC.js +0 -1
- package/dist/web/assets/SingleChangeView-CCpmUD7z.js +0 -1
- package/dist/web/assets/ViewHeader-C_euf5UJ.js +0 -1
- package/dist/web/assets/Workspace-BLaqqsJU.js +0 -1
- package/dist/web/assets/Workspace-C70CR7yx.js +0 -1
- package/dist/web/assets/Workspace-Coooyssr.js +0 -1
- package/dist/web/assets/Workspace-NqbDbmhC.js +0 -2
- package/dist/web/assets/index-CwS-KyOV.css +0 -1
- package/dist/web/assets/index-DvE_prho.js +0 -29
- package/dist/web/assets/web-BXIb25pa.js +0 -2
- package/dist/web/assets/web-CF8JCQ-n.js +0 -2
- package/dist/web/assets/web-D-wmMJ1D.js +0 -2
- package/dist/web/assets/web-qAmlbo_B.js +0 -2
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import
|
|
5
|
-
import
|
|
4
|
+
import path9 from "path";
|
|
5
|
+
import fs9 from "fs";
|
|
6
6
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7
7
|
import { access, readdir } from "fs/promises";
|
|
8
8
|
import { parseArgs as utilParseArgs } from "util";
|
|
@@ -43,7 +43,7 @@ import { fileURLToPath } from "url";
|
|
|
43
43
|
// package.json
|
|
44
44
|
var package_default = {
|
|
45
45
|
name: "zdashboard",
|
|
46
|
-
version: "2.8.
|
|
46
|
+
version: "2.8.5",
|
|
47
47
|
description: "ZCode skill dashboard platform \u2014 pluggable viewers for zdesign/zview/zreview/zgoal",
|
|
48
48
|
type: "module",
|
|
49
49
|
bin: {
|
|
@@ -452,13 +452,13 @@ var ServerService = class extends Service {
|
|
|
452
452
|
});
|
|
453
453
|
this.start(config.port);
|
|
454
454
|
}
|
|
455
|
-
route(
|
|
456
|
-
this.routes.set(
|
|
457
|
-
this.ctx.effect(() => () => this.routes.delete(
|
|
455
|
+
route(path10, handler) {
|
|
456
|
+
this.routes.set(path10, handler);
|
|
457
|
+
this.ctx.effect(() => () => this.routes.delete(path10));
|
|
458
458
|
}
|
|
459
|
-
sse(
|
|
460
|
-
this.sses.set(
|
|
461
|
-
this.ctx.effect(() => () => this.sses.delete(
|
|
459
|
+
sse(path10, onConnect) {
|
|
460
|
+
this.sses.set(path10, onConnect);
|
|
461
|
+
this.ctx.effect(() => () => this.sses.delete(path10));
|
|
462
462
|
}
|
|
463
463
|
static(prefix, dir) {
|
|
464
464
|
this.prefixStatic.set(prefix, dir);
|
|
@@ -757,7 +757,7 @@ import path5 from "path";
|
|
|
757
757
|
import fs5 from "fs";
|
|
758
758
|
import path4 from "path";
|
|
759
759
|
function walkDir(root, opts = {}) {
|
|
760
|
-
const { skip = /* @__PURE__ */ new Set(), maxDepth, onFile, onDir } = opts;
|
|
760
|
+
const { skip = /* @__PURE__ */ new Set(), maxDepth, allowDotDirs = false, onFile, onDir } = opts;
|
|
761
761
|
function walk(dir, rel, depth) {
|
|
762
762
|
if (typeof maxDepth === "number" && depth > maxDepth) return;
|
|
763
763
|
let ents;
|
|
@@ -767,7 +767,7 @@ function walkDir(root, opts = {}) {
|
|
|
767
767
|
return;
|
|
768
768
|
}
|
|
769
769
|
for (const ent of ents) {
|
|
770
|
-
if (ent.name.startsWith(".")) continue;
|
|
770
|
+
if (ent.name.startsWith(".") && !(allowDotDirs && ent.isDirectory())) continue;
|
|
771
771
|
if (skip.has(ent.name)) continue;
|
|
772
772
|
const abs = path4.join(dir, ent.name);
|
|
773
773
|
const r = rel ? `${rel}/${ent.name}` : ent.name;
|
|
@@ -835,12 +835,14 @@ function prefixPaths(nodes, prefix) {
|
|
|
835
835
|
function scanTree(root, scanDirs, opts) {
|
|
836
836
|
const tree = [];
|
|
837
837
|
const defaultExpandDepth = typeof opts?.defaultExpandDepth === "number" ? opts.defaultExpandDepth : 2;
|
|
838
|
+
const dotDeclared = new Set(Array.isArray(opts?.dotDirs) ? opts.dotDirs : []);
|
|
838
839
|
const dirs = Array.isArray(scanDirs) ? scanDirs.filter((d) => typeof d === "string" && d) : [];
|
|
839
840
|
for (const dir of dirs) {
|
|
841
|
+
if (dir.startsWith(".") && !dotDeclared.has(dir)) continue;
|
|
840
842
|
const dirPath = path5.join(root, dir);
|
|
841
843
|
if (!fs6.existsSync(dirPath)) continue;
|
|
842
844
|
const paths = [];
|
|
843
|
-
walkDir(dirPath, { maxDepth: 4, onFile: (_, rel) => paths.push(rel) });
|
|
845
|
+
walkDir(dirPath, { maxDepth: 4, allowDotDirs: dotDeclared.has(dir), onFile: (_, rel) => paths.push(rel) });
|
|
844
846
|
const prefix = dir;
|
|
845
847
|
const children = prefixPaths(buildTree(paths, defaultExpandDepth), prefix);
|
|
846
848
|
if (children.length) {
|
|
@@ -851,7 +853,8 @@ function scanTree(root, scanDirs, opts) {
|
|
|
851
853
|
}
|
|
852
854
|
|
|
853
855
|
// src/core/tree.ts
|
|
854
|
-
var CONVENTION_SCAN_DIRS = ["openspec", "docs"];
|
|
856
|
+
var CONVENTION_SCAN_DIRS = ["openspec", "docs", ".zdev/apply"];
|
|
857
|
+
var CONVENTION_DOT_DIRS = CONVENTION_SCAN_DIRS.filter((d) => d.startsWith("."));
|
|
855
858
|
var apply = {
|
|
856
859
|
inject: ["server"],
|
|
857
860
|
apply(ctx, config) {
|
|
@@ -862,7 +865,7 @@ var apply = {
|
|
|
862
865
|
const url = new URL(req.url || "/", "http://localhost");
|
|
863
866
|
const wt = url.searchParams.get("wt");
|
|
864
867
|
const scanRoot = wt ? decodeURIComponent(wt) : config.root;
|
|
865
|
-
const tree = scanTree(scanRoot, [...CONVENTION_SCAN_DIRS]);
|
|
868
|
+
const tree = scanTree(scanRoot, [...CONVENTION_SCAN_DIRS], { dotDirs: [...CONVENTION_DOT_DIRS] });
|
|
866
869
|
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8", "Cache-Control": "no-cache" });
|
|
867
870
|
res.end(JSON.stringify({ tree }));
|
|
868
871
|
} catch {
|
|
@@ -1028,9 +1031,9 @@ var DashboardService = class extends Service3 {
|
|
|
1028
1031
|
res.end('{"error":"method not allowed"}');
|
|
1029
1032
|
});
|
|
1030
1033
|
}
|
|
1031
|
-
register(
|
|
1032
|
-
this.plugins.set(
|
|
1033
|
-
this.ctx.effect(() => () => this.plugins.delete(
|
|
1034
|
+
register(manifest5) {
|
|
1035
|
+
this.plugins.set(manifest5.mode, manifest5);
|
|
1036
|
+
this.ctx.effect(() => () => this.plugins.delete(manifest5.mode));
|
|
1034
1037
|
}
|
|
1035
1038
|
list() {
|
|
1036
1039
|
return Array.from(this.plugins.values());
|
|
@@ -1193,7 +1196,7 @@ var JustRunner = class {
|
|
|
1193
1196
|
|
|
1194
1197
|
// src/sdk/server.ts
|
|
1195
1198
|
function definePlugin(def) {
|
|
1196
|
-
const { manifest:
|
|
1199
|
+
const { manifest: manifest5, setup } = def;
|
|
1197
1200
|
return {
|
|
1198
1201
|
inject: ["server", "dashboard", "reload"],
|
|
1199
1202
|
apply(ctx) {
|
|
@@ -1203,49 +1206,49 @@ function definePlugin(def) {
|
|
|
1203
1206
|
try {
|
|
1204
1207
|
fn();
|
|
1205
1208
|
} catch (e) {
|
|
1206
|
-
console.error(`[zdashboard] plugin ${
|
|
1209
|
+
console.error(`[zdashboard] plugin ${manifest5.mode} dispose error:`, e);
|
|
1207
1210
|
}
|
|
1208
1211
|
}
|
|
1209
1212
|
});
|
|
1210
|
-
ctx.dashboard.register(
|
|
1213
|
+
ctx.dashboard.register(manifest5);
|
|
1211
1214
|
const respondWith = (handler) => async (req, res) => {
|
|
1212
1215
|
if (res.headersSent) return;
|
|
1213
1216
|
try {
|
|
1214
1217
|
const data = await handler(req, res);
|
|
1215
1218
|
if (data !== void 0 && !res.writableEnded) json(res, data);
|
|
1216
1219
|
} catch (e) {
|
|
1217
|
-
console.error(`[zdashboard] plugin ${
|
|
1220
|
+
console.error(`[zdashboard] plugin ${manifest5.mode} route error:`, e);
|
|
1218
1221
|
if (!res.headersSent) json(res, { error: "internal" }, 500);
|
|
1219
1222
|
}
|
|
1220
1223
|
};
|
|
1221
1224
|
const pctx = {
|
|
1222
|
-
mode:
|
|
1225
|
+
mode: manifest5.mode,
|
|
1223
1226
|
config() {
|
|
1224
|
-
const schema =
|
|
1225
|
-
const stored = ctx.server.getPluginConfig(
|
|
1227
|
+
const schema = manifest5.config ?? {};
|
|
1228
|
+
const stored = ctx.server.getPluginConfig(manifest5.mode) ?? {};
|
|
1226
1229
|
const defaults = Object.fromEntries(
|
|
1227
1230
|
Object.entries(schema).map(([key, field]) => [key, field.default])
|
|
1228
1231
|
);
|
|
1229
1232
|
return { ...defaults, ...stored };
|
|
1230
1233
|
},
|
|
1231
|
-
route(
|
|
1232
|
-
ctx.server.route(
|
|
1234
|
+
route(path10, handler) {
|
|
1235
|
+
ctx.server.route(path10, respondWith(handler));
|
|
1233
1236
|
},
|
|
1234
|
-
guardedRoute(
|
|
1237
|
+
guardedRoute(path10, handler) {
|
|
1235
1238
|
const token = () => ctx.server.stopToken;
|
|
1236
|
-
ctx.server.route(
|
|
1239
|
+
ctx.server.route(path10, (req, res) => {
|
|
1237
1240
|
if (!guarded(req, res, token())) return;
|
|
1238
1241
|
void respondWith(handler)(req, res);
|
|
1239
1242
|
});
|
|
1240
1243
|
},
|
|
1241
|
-
sse(
|
|
1242
|
-
ctx.server.sse(
|
|
1244
|
+
sse(path10, handler) {
|
|
1245
|
+
ctx.server.sse(path10, handler);
|
|
1243
1246
|
},
|
|
1244
1247
|
static(prefix, dir) {
|
|
1245
1248
|
ctx.server.static(prefix, dir);
|
|
1246
1249
|
},
|
|
1247
1250
|
broadcast(event, data) {
|
|
1248
|
-
ctx.reload?.broadcast(`plugin:${
|
|
1251
|
+
ctx.reload?.broadcast(`plugin:${manifest5.mode}:${event}`, data ?? "");
|
|
1249
1252
|
},
|
|
1250
1253
|
onDispose(fn) {
|
|
1251
1254
|
disposers.push(fn);
|
|
@@ -1350,239 +1353,12 @@ var apply3 = defineBuiltin({
|
|
|
1350
1353
|
}
|
|
1351
1354
|
});
|
|
1352
1355
|
|
|
1353
|
-
// src/plugins/apply/scan.ts
|
|
1354
|
-
import fs7 from "fs";
|
|
1355
|
-
import path6 from "path";
|
|
1356
|
-
|
|
1357
|
-
// src/plugins/apply/parse-tasks.ts
|
|
1358
|
-
var TASK_RE = /^\s*-\s*\[([ xX])\]\s*(.*)$/;
|
|
1359
|
-
var MANUAL_PREFIX = "\u{1F527}[\u4EBA\u5DE5]";
|
|
1360
|
-
function parseTasks(md) {
|
|
1361
|
-
return md.split(/\r?\n/).filter((l) => TASK_RE.test(l)).map((l) => {
|
|
1362
|
-
const m = l.match(TASK_RE);
|
|
1363
|
-
const text = (m?.[2] ?? l).trim();
|
|
1364
|
-
return { text, checked: m?.[1] !== " ", manual: text.startsWith(MANUAL_PREFIX) };
|
|
1365
|
-
});
|
|
1366
|
-
}
|
|
1367
|
-
function countTasks(md) {
|
|
1368
|
-
const tasks = parseTasks(md);
|
|
1369
|
-
const normal = tasks.filter((t) => !t.manual);
|
|
1370
|
-
return {
|
|
1371
|
-
total: normal.length,
|
|
1372
|
-
done: normal.filter((t) => t.checked).length,
|
|
1373
|
-
manual: tasks.length - normal.length
|
|
1374
|
-
};
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
|
-
// src/plugins/apply/scan.ts
|
|
1378
|
-
function readText(p) {
|
|
1379
|
-
try {
|
|
1380
|
-
return fs7.readFileSync(p, "utf8");
|
|
1381
|
-
} catch {
|
|
1382
|
-
return "";
|
|
1383
|
-
}
|
|
1384
|
-
}
|
|
1385
|
-
function worktreeDir(root, name) {
|
|
1386
|
-
return path6.join(root, ".zworktree", name);
|
|
1387
|
-
}
|
|
1388
|
-
function changeDir(root, name) {
|
|
1389
|
-
return path6.join(root, "openspec", "changes", name);
|
|
1390
|
-
}
|
|
1391
|
-
function parseDependsOn(proposal) {
|
|
1392
|
-
const deps = [];
|
|
1393
|
-
const m = proposal.match(/^##\s+依赖\s*\n([\s\S]*?)(?:\n#{1,6}\s|\n---\s*\n|$)/i);
|
|
1394
|
-
if (!m) return deps;
|
|
1395
|
-
for (const line of m[1].split("\n")) {
|
|
1396
|
-
if (!/^-\s+/.test(line)) continue;
|
|
1397
|
-
const name = line.replace(/^-\s+/, "").trim();
|
|
1398
|
-
if (name) deps.push(name);
|
|
1399
|
-
}
|
|
1400
|
-
return deps;
|
|
1401
|
-
}
|
|
1402
|
-
function scanApplyChanges(root) {
|
|
1403
|
-
const changesDir = path6.join(root, "openspec", "changes");
|
|
1404
|
-
if (!fs7.existsSync(changesDir)) return [];
|
|
1405
|
-
const out = [];
|
|
1406
|
-
for (const ent of fs7.readdirSync(changesDir, { withFileTypes: true })) {
|
|
1407
|
-
if (!ent.isDirectory() || ent.name.startsWith(".") || ent.name === "archive") continue;
|
|
1408
|
-
const wt = worktreeDir(root, ent.name);
|
|
1409
|
-
const tasks = readText(path6.join(wt, "openspec", "changes", ent.name, "tasks.md"));
|
|
1410
|
-
if (!tasks) {
|
|
1411
|
-
const fallback = readText(path6.join(changesDir, ent.name, "tasks.md"));
|
|
1412
|
-
if (fallback) {
|
|
1413
|
-
const { total, done } = countTasks(fallback);
|
|
1414
|
-
const wtChange = path6.join(wt, "openspec", "changes", ent.name);
|
|
1415
|
-
const mainChange = path6.join(changesDir, ent.name);
|
|
1416
|
-
out.push({
|
|
1417
|
-
name: ent.name,
|
|
1418
|
-
path: `openspec/changes/${ent.name}`,
|
|
1419
|
-
total,
|
|
1420
|
-
done,
|
|
1421
|
-
hasProposal: fs7.existsSync(path6.join(wtChange, "proposal.md")) || fs7.existsSync(path6.join(mainChange, "proposal.md")),
|
|
1422
|
-
hasDesign: fs7.existsSync(path6.join(wtChange, "design.md")) || fs7.existsSync(path6.join(mainChange, "design.md")),
|
|
1423
|
-
// worktree 目录存在即算执行中(tasks.md 可能尚未落盘)
|
|
1424
|
-
inWorktree: fs7.existsSync(wtChange)
|
|
1425
|
-
});
|
|
1426
|
-
}
|
|
1427
|
-
} else {
|
|
1428
|
-
const { total, done } = countTasks(tasks);
|
|
1429
|
-
const wtChange = path6.join(wt, "openspec", "changes", ent.name);
|
|
1430
|
-
const mainChange = path6.join(changesDir, ent.name);
|
|
1431
|
-
out.push({
|
|
1432
|
-
name: ent.name,
|
|
1433
|
-
path: `openspec/changes/${ent.name}`,
|
|
1434
|
-
total,
|
|
1435
|
-
done,
|
|
1436
|
-
// 任一侧存在即算(详情读取是 worktree 优先、主目录兜底,列表标记保持一致)
|
|
1437
|
-
hasProposal: fs7.existsSync(path6.join(wtChange, "proposal.md")) || fs7.existsSync(path6.join(mainChange, "proposal.md")),
|
|
1438
|
-
hasDesign: fs7.existsSync(path6.join(wtChange, "design.md")) || fs7.existsSync(path6.join(mainChange, "design.md")),
|
|
1439
|
-
inWorktree: true
|
|
1440
|
-
});
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1443
|
-
out.sort((a, b) => a.name.localeCompare(b.name));
|
|
1444
|
-
return out;
|
|
1445
|
-
}
|
|
1446
|
-
function readApplyChange(root, name) {
|
|
1447
|
-
const wt = worktreeDir(root, name);
|
|
1448
|
-
const mainChangeDir = changeDir(root, name);
|
|
1449
|
-
const proposalFrom = fs7.existsSync(path6.join(wt, "openspec", "changes", name, "proposal.md")) ? path6.join(wt, "openspec", "changes", name, "proposal.md") : path6.join(mainChangeDir, "proposal.md");
|
|
1450
|
-
const designFrom = fs7.existsSync(path6.join(wt, "openspec", "changes", name, "design.md")) ? path6.join(wt, "openspec", "changes", name, "design.md") : path6.join(mainChangeDir, "design.md");
|
|
1451
|
-
const tasksFrom = fs7.existsSync(path6.join(wt, "openspec", "changes", name, "tasks.md")) ? path6.join(wt, "openspec", "changes", name, "tasks.md") : path6.join(mainChangeDir, "tasks.md");
|
|
1452
|
-
const proposal = readText(proposalFrom);
|
|
1453
|
-
const design = readText(designFrom);
|
|
1454
|
-
const tasks = readText(tasksFrom);
|
|
1455
|
-
const { total, done } = countTasks(tasks);
|
|
1456
|
-
const inWorktree = fs7.existsSync(path6.join(wt, "openspec", "changes", name));
|
|
1457
|
-
const dependsOn = parseDependsOn(proposal);
|
|
1458
|
-
const hasTestStrategy = /^##\s+测试策略\s*$/m.test(design);
|
|
1459
|
-
return {
|
|
1460
|
-
name,
|
|
1461
|
-
path: `openspec/changes/${name}`,
|
|
1462
|
-
total,
|
|
1463
|
-
done,
|
|
1464
|
-
hasProposal: !!proposal,
|
|
1465
|
-
hasDesign: !!design,
|
|
1466
|
-
inWorktree,
|
|
1467
|
-
proposal,
|
|
1468
|
-
design,
|
|
1469
|
-
tasks,
|
|
1470
|
-
dependsOn,
|
|
1471
|
-
hasTestStrategy
|
|
1472
|
-
};
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
|
-
// src/plugins/apply/batch.ts
|
|
1476
|
-
import fs8 from "fs";
|
|
1477
|
-
import path7 from "path";
|
|
1478
|
-
var RUN_ID_PATTERN = /^[A-Za-z0-9-]+$/;
|
|
1479
|
-
function isPlainObject(v) {
|
|
1480
|
-
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1481
|
-
}
|
|
1482
|
-
function readText2(p) {
|
|
1483
|
-
try {
|
|
1484
|
-
return fs8.readFileSync(p, "utf8");
|
|
1485
|
-
} catch {
|
|
1486
|
-
return null;
|
|
1487
|
-
}
|
|
1488
|
-
}
|
|
1489
|
-
function readBatchState(root, explicitRun) {
|
|
1490
|
-
const applyDir = path7.join(root, ".zdev", "apply");
|
|
1491
|
-
const requested = explicitRun?.trim() ?? "";
|
|
1492
|
-
const runId = requested && RUN_ID_PATTERN.test(requested) ? requested : readText2(path7.join(applyDir, "CURRENT"))?.trim() ?? "";
|
|
1493
|
-
if (!runId || !RUN_ID_PATTERN.test(runId)) {
|
|
1494
|
-
return { run: null, state: null };
|
|
1495
|
-
}
|
|
1496
|
-
const parsed = (() => {
|
|
1497
|
-
const text = readText2(path7.join(applyDir, "runs", runId, "state.json"));
|
|
1498
|
-
if (text == null) return null;
|
|
1499
|
-
try {
|
|
1500
|
-
return JSON.parse(text);
|
|
1501
|
-
} catch {
|
|
1502
|
-
return null;
|
|
1503
|
-
}
|
|
1504
|
-
})();
|
|
1505
|
-
if (!isPlainObject(parsed)) {
|
|
1506
|
-
return { run: { id: runId }, state: null };
|
|
1507
|
-
}
|
|
1508
|
-
return { run: { id: runId }, state: parsed };
|
|
1509
|
-
}
|
|
1510
|
-
function projectGraph(state) {
|
|
1511
|
-
if (!state) {
|
|
1512
|
-
return { changes: [], batches: [], conflicts: [] };
|
|
1513
|
-
}
|
|
1514
|
-
const rawChanges = state.changes;
|
|
1515
|
-
const rawBatches = state.batches;
|
|
1516
|
-
const rawConflicts = state.conflicts;
|
|
1517
|
-
return {
|
|
1518
|
-
changes: (Array.isArray(rawChanges) ? rawChanges : []).map((c) => ({
|
|
1519
|
-
name: c.name,
|
|
1520
|
-
status: c.status,
|
|
1521
|
-
dependencies: Array.isArray(c?.dependencies) ? c.dependencies : [],
|
|
1522
|
-
batchIndex: c.batchIndex
|
|
1523
|
-
})),
|
|
1524
|
-
batches: Array.isArray(rawBatches) ? rawBatches : [],
|
|
1525
|
-
conflicts: Array.isArray(rawConflicts) ? rawConflicts : []
|
|
1526
|
-
};
|
|
1527
|
-
}
|
|
1528
|
-
var BATCH_LOGS_TAIL = 100;
|
|
1529
|
-
function tailLogs(state) {
|
|
1530
|
-
return (state?.logs ?? []).slice(-BATCH_LOGS_TAIL);
|
|
1531
|
-
}
|
|
1532
|
-
function readBatchPlan(root, runId) {
|
|
1533
|
-
return readText2(path7.join(root, ".zdev", "apply", "runs", runId, "plan.md"));
|
|
1534
|
-
}
|
|
1535
|
-
|
|
1536
|
-
// src/plugins/apply/manifest.ts
|
|
1537
|
-
var manifest2 = {
|
|
1538
|
-
mode: "apply",
|
|
1539
|
-
label: "\u6267\u884C\u8FDB\u5EA6",
|
|
1540
|
-
icon: "\u2699\uFE0F",
|
|
1541
|
-
description: "OpenSpec \u6267\u884C\u8FDB\u5EA6 \xB7 zapply \u5355 change \u4E0E\u6279\u91CF",
|
|
1542
|
-
order: 40
|
|
1543
|
-
};
|
|
1544
|
-
|
|
1545
|
-
// src/plugins/apply/index.ts
|
|
1546
|
-
function errorResponse(res, status, msg) {
|
|
1547
|
-
res.writeHead(status, { "Content-Type": "application/json; charset=utf-8" });
|
|
1548
|
-
res.end(JSON.stringify({ error: msg }));
|
|
1549
|
-
return void 0;
|
|
1550
|
-
}
|
|
1551
|
-
var apply4 = defineBuiltin({
|
|
1552
|
-
manifest: manifest2,
|
|
1553
|
-
setup(ctx, root) {
|
|
1554
|
-
ctx.route("/__apply", async () => scanApplyChanges(root));
|
|
1555
|
-
ctx.route("/__apply/change", async (req, res) => {
|
|
1556
|
-
const url = new URL(req.url || "", "http://x");
|
|
1557
|
-
const name = url.searchParams.get("name");
|
|
1558
|
-
const bad = (msg) => errorResponse(res, 400, msg);
|
|
1559
|
-
if (!name) return bad("missing name");
|
|
1560
|
-
if (name.includes("..") || name.includes("/") || name.includes("\\")) return bad("invalid name");
|
|
1561
|
-
try {
|
|
1562
|
-
return readApplyChange(root, name);
|
|
1563
|
-
} catch (e) {
|
|
1564
|
-
return bad(e instanceof Error ? e.message : "unknown error");
|
|
1565
|
-
}
|
|
1566
|
-
});
|
|
1567
|
-
const queryRun = (req) => new URL(req.url || "", "http://x").searchParams.get("run") ?? void 0;
|
|
1568
|
-
ctx.route("/__apply/batch", async (req) => readBatchState(root, queryRun(req)));
|
|
1569
|
-
ctx.route("/__apply/batch/graph", async (req) => projectGraph(readBatchState(root, queryRun(req)).state));
|
|
1570
|
-
ctx.route("/__apply/batch/logs", async (req) => tailLogs(readBatchState(root, queryRun(req)).state));
|
|
1571
|
-
ctx.route("/__apply/batch/plan", async (req, res) => {
|
|
1572
|
-
const snap = readBatchState(root, queryRun(req));
|
|
1573
|
-
const plan = snap.run ? readBatchPlan(root, snap.run.id) : null;
|
|
1574
|
-
if (plan == null) return errorResponse(res, 404, "plan not found");
|
|
1575
|
-
return { plan };
|
|
1576
|
-
});
|
|
1577
|
-
}
|
|
1578
|
-
});
|
|
1579
|
-
|
|
1580
1356
|
// src/plugins/design/index.ts
|
|
1581
|
-
import
|
|
1582
|
-
import
|
|
1357
|
+
import path7 from "path";
|
|
1358
|
+
import fs7 from "fs";
|
|
1583
1359
|
|
|
1584
1360
|
// src/server/design-assets.ts
|
|
1585
|
-
import
|
|
1361
|
+
import path6 from "path";
|
|
1586
1362
|
var PAGE_EXTS = [".html", ".htm"];
|
|
1587
1363
|
var ICON_EXTS = [".svg", ".png", ".ico", ".jpg", ".jpeg", ".gif", ".webp"];
|
|
1588
1364
|
var VIDEO_EXTS = [".mp4", ".webm", ".mov", ".ogg", ".ogv"];
|
|
@@ -1610,17 +1386,17 @@ function scanAssets(root, subDir = "") {
|
|
|
1610
1386
|
const keys = ["page", "component", "icon", "token", "md", "video", "audio", "pdf", "font"];
|
|
1611
1387
|
for (const k of keys) out[k] = [];
|
|
1612
1388
|
const SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", "coverage", ".next", ".cache"]);
|
|
1613
|
-
const scanRoot = subDir ?
|
|
1389
|
+
const scanRoot = subDir ? path6.join(root, subDir) : root;
|
|
1614
1390
|
walkDir(scanRoot, { skip: SKIP, onFile: (abs, rel) => {
|
|
1615
|
-
const ext =
|
|
1391
|
+
const ext = path6.extname(abs).toLowerCase();
|
|
1616
1392
|
const t = categorize(rel, ext);
|
|
1617
|
-
if (t) out[t].push({ path: rel, name:
|
|
1393
|
+
if (t) out[t].push({ path: rel, name: path6.basename(abs), ext, type: t });
|
|
1618
1394
|
} });
|
|
1619
1395
|
return out;
|
|
1620
1396
|
}
|
|
1621
1397
|
|
|
1622
1398
|
// src/plugins/design/manifest.ts
|
|
1623
|
-
var
|
|
1399
|
+
var manifest2 = {
|
|
1624
1400
|
mode: "design",
|
|
1625
1401
|
label: "\u8BBE\u8BA1\u8D44\u4EA7",
|
|
1626
1402
|
icon: "\u{1F3A8}",
|
|
@@ -1635,35 +1411,35 @@ function emptyScan() {
|
|
|
1635
1411
|
for (const k of ASSET_KEYS) out[k] = [];
|
|
1636
1412
|
return out;
|
|
1637
1413
|
}
|
|
1638
|
-
var
|
|
1639
|
-
manifest:
|
|
1414
|
+
var apply4 = defineBuiltin({
|
|
1415
|
+
manifest: manifest2,
|
|
1640
1416
|
setup(ctx, root) {
|
|
1641
1417
|
ctx.route("/__design/assets", async () => {
|
|
1642
|
-
const designRoot =
|
|
1643
|
-
return
|
|
1418
|
+
const designRoot = path7.join(root, ".zdev", "design");
|
|
1419
|
+
return fs7.existsSync(designRoot) ? scanAssets(designRoot) : emptyScan();
|
|
1644
1420
|
});
|
|
1645
1421
|
ctx.route("/__design/asset", async (req, res) => {
|
|
1646
1422
|
const rel = new URL(req.url || "/", "http://localhost").searchParams.get("path") ?? "";
|
|
1647
|
-
if (!rel || rel.includes("..") || rel.includes("\\") ||
|
|
1423
|
+
if (!rel || rel.includes("..") || rel.includes("\\") || path7.isAbsolute(rel)) {
|
|
1648
1424
|
res.writeHead(400);
|
|
1649
1425
|
res.end("bad path");
|
|
1650
1426
|
return;
|
|
1651
1427
|
}
|
|
1652
|
-
const designRoot =
|
|
1653
|
-
const filePath =
|
|
1654
|
-
if (!filePath.startsWith(designRoot +
|
|
1428
|
+
const designRoot = path7.join(root, ".zdev", "design");
|
|
1429
|
+
const filePath = path7.join(designRoot, rel);
|
|
1430
|
+
if (!filePath.startsWith(designRoot + path7.sep)) {
|
|
1655
1431
|
res.writeHead(400);
|
|
1656
1432
|
res.end("bad path");
|
|
1657
1433
|
return;
|
|
1658
1434
|
}
|
|
1659
|
-
|
|
1435
|
+
fs7.stat(filePath, (err) => {
|
|
1660
1436
|
if (err) {
|
|
1661
1437
|
res.writeHead(404);
|
|
1662
1438
|
return res.end("Not found");
|
|
1663
1439
|
}
|
|
1664
|
-
const ct = MIME[
|
|
1440
|
+
const ct = MIME[path7.extname(filePath).toLowerCase()] ?? "application/octet-stream";
|
|
1665
1441
|
res.writeHead(200, { "Content-Type": ct, "Cache-Control": "no-cache" });
|
|
1666
|
-
const stream =
|
|
1442
|
+
const stream = fs7.createReadStream(filePath);
|
|
1667
1443
|
stream.on("error", () => {
|
|
1668
1444
|
try {
|
|
1669
1445
|
res.destroy();
|
|
@@ -1677,7 +1453,7 @@ var apply5 = defineBuiltin({
|
|
|
1677
1453
|
});
|
|
1678
1454
|
|
|
1679
1455
|
// src/plugins/view/manifest.ts
|
|
1680
|
-
var
|
|
1456
|
+
var manifest3 = {
|
|
1681
1457
|
mode: "view",
|
|
1682
1458
|
label: "\u9879\u76EE\u6D4F\u89C8",
|
|
1683
1459
|
icon: "\u{1F441}\uFE0F",
|
|
@@ -1686,19 +1462,19 @@ var manifest4 = {
|
|
|
1686
1462
|
};
|
|
1687
1463
|
|
|
1688
1464
|
// src/plugins/view/index.ts
|
|
1689
|
-
var
|
|
1690
|
-
manifest:
|
|
1465
|
+
var apply5 = defineBuiltin({
|
|
1466
|
+
manifest: manifest3,
|
|
1691
1467
|
setup() {
|
|
1692
1468
|
}
|
|
1693
1469
|
});
|
|
1694
1470
|
|
|
1695
1471
|
// src/plugins/stats/index.ts
|
|
1696
|
-
import
|
|
1697
|
-
import
|
|
1472
|
+
import fs8 from "fs";
|
|
1473
|
+
import path8 from "path";
|
|
1698
1474
|
import { execFile as execFile6 } from "child_process";
|
|
1699
1475
|
|
|
1700
1476
|
// src/plugins/stats/manifest.ts
|
|
1701
|
-
var
|
|
1477
|
+
var manifest4 = {
|
|
1702
1478
|
mode: "stats",
|
|
1703
1479
|
label: "\u9879\u76EE\u7EDF\u8BA1",
|
|
1704
1480
|
icon: "\u{1F4CA}",
|
|
@@ -1715,14 +1491,14 @@ function execFilePromise(file, args, opts) {
|
|
|
1715
1491
|
}
|
|
1716
1492
|
function scan(root) {
|
|
1717
1493
|
const stats = {
|
|
1718
|
-
root:
|
|
1494
|
+
root: path8.basename(root),
|
|
1719
1495
|
files: 0,
|
|
1720
1496
|
dirs: 0,
|
|
1721
1497
|
totalSize: 0,
|
|
1722
1498
|
byExt: [],
|
|
1723
1499
|
markdown: 0,
|
|
1724
1500
|
openspec: { active: 0, archived: 0 },
|
|
1725
|
-
hasJust:
|
|
1501
|
+
hasJust: fs8.existsSync(path8.join(root, "justfile")),
|
|
1726
1502
|
worktrees: 0
|
|
1727
1503
|
};
|
|
1728
1504
|
const extMap = /* @__PURE__ */ new Map();
|
|
@@ -1732,22 +1508,22 @@ function scan(root) {
|
|
|
1732
1508
|
}, onFile: (abs) => {
|
|
1733
1509
|
stats.files++;
|
|
1734
1510
|
try {
|
|
1735
|
-
stats.totalSize +=
|
|
1511
|
+
stats.totalSize += fs8.statSync(abs).size;
|
|
1736
1512
|
} catch {
|
|
1737
1513
|
}
|
|
1738
|
-
const ext =
|
|
1514
|
+
const ext = path8.extname(abs).toLowerCase() || "(\u65E0\u6269\u5C55\u540D)";
|
|
1739
1515
|
extMap.set(ext, (extMap.get(ext) ?? 0) + 1);
|
|
1740
1516
|
if (ext === ".md" || ext === ".markdown") stats.markdown++;
|
|
1741
1517
|
} });
|
|
1742
1518
|
};
|
|
1743
1519
|
walk(root);
|
|
1744
|
-
const changesDir =
|
|
1745
|
-
if (
|
|
1746
|
-
for (const ent of
|
|
1520
|
+
const changesDir = path8.join(root, "openspec", "changes");
|
|
1521
|
+
if (fs8.existsSync(changesDir)) {
|
|
1522
|
+
for (const ent of fs8.readdirSync(changesDir, { withFileTypes: true })) {
|
|
1747
1523
|
if (!ent.isDirectory() || ent.name.startsWith(".")) continue;
|
|
1748
1524
|
if (ent.name === "archive") {
|
|
1749
1525
|
try {
|
|
1750
|
-
stats.openspec.archived =
|
|
1526
|
+
stats.openspec.archived = fs8.readdirSync(path8.join(changesDir, "archive"), { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".")).length;
|
|
1751
1527
|
} catch {
|
|
1752
1528
|
}
|
|
1753
1529
|
} else stats.openspec.active++;
|
|
@@ -1756,8 +1532,8 @@ function scan(root) {
|
|
|
1756
1532
|
stats.byExt = Array.from(extMap.entries()).map(([ext, count]) => ({ ext, count })).sort((a, b) => b.count - a.count).slice(0, 10);
|
|
1757
1533
|
return stats;
|
|
1758
1534
|
}
|
|
1759
|
-
var
|
|
1760
|
-
manifest:
|
|
1535
|
+
var apply6 = defineBuiltin({
|
|
1536
|
+
manifest: manifest4,
|
|
1761
1537
|
setup(ctx, root) {
|
|
1762
1538
|
ctx.route("/__stats/data", async () => {
|
|
1763
1539
|
const stats = scan(root);
|
|
@@ -1783,7 +1559,7 @@ var apply7 = defineBuiltin({
|
|
|
1783
1559
|
});
|
|
1784
1560
|
|
|
1785
1561
|
// src/cli.ts
|
|
1786
|
-
var __dirname2 =
|
|
1562
|
+
var __dirname2 = path9.dirname(fileURLToPath2(import.meta.url));
|
|
1787
1563
|
var DEFAULT_PORT = 4190;
|
|
1788
1564
|
function parseArgs() {
|
|
1789
1565
|
const args = process.argv.slice(2);
|
|
@@ -1833,7 +1609,7 @@ async function loadExternal(ctx, dir, root) {
|
|
|
1833
1609
|
if (!ent.isDirectory()) continue;
|
|
1834
1610
|
const candidates = tsxLoaded ? ["index.ts", "index.js", "index.mjs"] : ["index.js", "index.mjs"];
|
|
1835
1611
|
for (const name of candidates) {
|
|
1836
|
-
const p =
|
|
1612
|
+
const p = path9.join(dir, ent.name, name);
|
|
1837
1613
|
if (await pathExists(p)) {
|
|
1838
1614
|
try {
|
|
1839
1615
|
const mod = await import(p);
|
|
@@ -1848,8 +1624,8 @@ async function loadExternal(ctx, dir, root) {
|
|
|
1848
1624
|
const m = ctx.dashboard.get(ent.name);
|
|
1849
1625
|
if (m && m.mode === ent.name) {
|
|
1850
1626
|
const patch = { external: true };
|
|
1851
|
-
const webDir =
|
|
1852
|
-
if (await pathExists(
|
|
1627
|
+
const webDir = path9.join(dir, ent.name, "web");
|
|
1628
|
+
if (await pathExists(path9.join(webDir, "index.html"))) {
|
|
1853
1629
|
ctx.server.static(`${PLUGIN_STATIC_PREFIX}${ent.name}/`, webDir);
|
|
1854
1630
|
if (!m.viewerUrl) patch.viewerUrl = `${PLUGIN_STATIC_PREFIX}${ent.name}/`;
|
|
1855
1631
|
}
|
|
@@ -1868,7 +1644,7 @@ async function loadExternal(ctx, dir, root) {
|
|
|
1868
1644
|
}
|
|
1869
1645
|
async function main() {
|
|
1870
1646
|
const args = parseArgs();
|
|
1871
|
-
const root =
|
|
1647
|
+
const root = path9.resolve(args.dir);
|
|
1872
1648
|
const existing = await findReusable(root);
|
|
1873
1649
|
if (existing && !args.restart) {
|
|
1874
1650
|
const u = `http://localhost:${existing.port}` + (args.page ? `#${args.page}` : "");
|
|
@@ -1882,14 +1658,14 @@ async function main() {
|
|
|
1882
1658
|
console.log(`[zdashboard] --restart\uFF1A\u505C\u6B62\u65E7\u5B9E\u4F8B pid=${oldRecord.pid}`);
|
|
1883
1659
|
await stopInstance(oldRecord);
|
|
1884
1660
|
}
|
|
1885
|
-
const appDir =
|
|
1886
|
-
const BUILTIN_PLUGINS = [
|
|
1661
|
+
const appDir = path9.resolve(__dirname2, "web");
|
|
1662
|
+
const BUILTIN_PLUGINS = [apply6, apply3, apply4, apply5];
|
|
1887
1663
|
if (stripDeadPluginConfig(root, BUILTIN_PLUGINS.map((p) => p.manifest))) {
|
|
1888
1664
|
console.log("[zdashboard] \u5DF2\u6E05\u7406 dashboard.json \u4E2D\u5185\u7F6E\u63D2\u4EF6\u7684\u8FC7\u65F6\u914D\u7F6E\u952E");
|
|
1889
1665
|
}
|
|
1890
1666
|
const startPort = oldRecord && !args.portExplicit ? oldRecord.port : args.port;
|
|
1891
|
-
const zdevDir =
|
|
1892
|
-
const dataDir =
|
|
1667
|
+
const zdevDir = path9.join(root, ".zdev");
|
|
1668
|
+
const dataDir = fs9.existsSync(zdevDir) ? ".zdev/" : "";
|
|
1893
1669
|
const det = await detect(root);
|
|
1894
1670
|
const ctx = new Context();
|
|
1895
1671
|
ctx.plugin(ServerService, {
|
|
@@ -1906,15 +1682,15 @@ async function main() {
|
|
|
1906
1682
|
ctx.plugin(apply, { root });
|
|
1907
1683
|
ctx.plugin(apply2, { root });
|
|
1908
1684
|
ctx.plugin(DashboardService);
|
|
1909
|
-
for (const
|
|
1685
|
+
for (const apply7 of BUILTIN_PLUGINS) {
|
|
1910
1686
|
try {
|
|
1911
|
-
ctx.plugin(
|
|
1687
|
+
ctx.plugin(apply7, { root });
|
|
1912
1688
|
} catch (e) {
|
|
1913
1689
|
console.error("[zdashboard] builtin plugin failed:", e);
|
|
1914
1690
|
}
|
|
1915
1691
|
}
|
|
1916
1692
|
if (args.plugins) {
|
|
1917
|
-
await loadExternal(ctx,
|
|
1693
|
+
await loadExternal(ctx, path9.resolve(args.plugins), root);
|
|
1918
1694
|
}
|
|
1919
1695
|
const shutdown = () => {
|
|
1920
1696
|
try {
|