zdashboard 2.10.0 → 2.11.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 +153 -22
- package/dist/cli.js.map +1 -1
- package/dist/web/assets/{CodeViewer-yUtAi1hT.js → CodeViewer-B7R0ZVR6.js} +51 -51
- package/dist/web/assets/FileIcon-gTgtllQ_.js +1 -0
- package/dist/web/assets/FilterPills-BG7PY8Ts.js +1 -0
- package/dist/web/assets/Sidebar-BFf7prry.js +1 -0
- package/dist/web/assets/Sidebar-Bv_jdwYG.js +1 -0
- package/dist/web/assets/Workspace-B7_x75Fc.js +1 -0
- package/dist/web/assets/Workspace-BimPBKII.js +1 -0
- package/dist/web/assets/Workspace-D42j59Cc.js +5 -0
- package/dist/web/assets/Workspace-DXhpVGbA.js +1 -0
- package/dist/web/assets/fetchJson-D-dWeu1j.js +1 -0
- package/dist/web/assets/index-B4hHecQs.js +316 -0
- package/dist/web/assets/index-Bfe1AHR3.css +1 -0
- package/dist/web/assets/usePluginData-Dhhi3YQs.js +1 -0
- package/dist/web/assets/vendor-react-CDaMBvJU.js +40 -0
- package/dist/web/assets/vendor-ui-dpI0b3Ao.js +42 -0
- package/dist/web/assets/web-B3RuriWk.js +2 -0
- package/dist/web/assets/web-BJPSRqva.js +2 -0
- package/dist/web/assets/web-BugMZG5x.js +2 -0
- package/dist/web/assets/web-D-lPF0OX.js +2 -0
- package/dist/web/index.html +25 -2
- package/package.json +1 -1
- package/dist/web/assets/FileIcon-DCL9OD6I.js +0 -1
- package/dist/web/assets/FilterPills-BD2vsIx2.js +0 -1
- package/dist/web/assets/Sidebar-CsmNxVjL.js +0 -1
- package/dist/web/assets/Sidebar-DxeR1Shk.js +0 -1
- package/dist/web/assets/Workspace-BGj3n613.js +0 -1
- package/dist/web/assets/Workspace-CUepnOlM.js +0 -1
- package/dist/web/assets/Workspace-D0rSzljF.js +0 -1
- package/dist/web/assets/Workspace-DUK4Lm-v.js +0 -5
- package/dist/web/assets/index--t_ijYil.css +0 -1
- package/dist/web/assets/index-veTBv7Qv.js +0 -7816
- package/dist/web/assets/usePluginData-mjHueOjf.js +0 -1
- package/dist/web/assets/web-BODcUPmi.js +0 -2
- package/dist/web/assets/web-CsCyTuDw.js +0 -2
- package/dist/web/assets/web-D6VGQFbH.js +0 -2
- package/dist/web/assets/web-DIZjnUb4.js +0 -2
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ import { access, readdir } from "fs/promises";
|
|
|
9
9
|
// package.json
|
|
10
10
|
var package_default = {
|
|
11
11
|
name: "zdashboard",
|
|
12
|
-
version: "2.
|
|
12
|
+
version: "2.11.0",
|
|
13
13
|
description: "ZCode skill dashboard platform \u2014 pluggable viewers for zdesign/zview/zreview/zgoal",
|
|
14
14
|
type: "module",
|
|
15
15
|
bin: {
|
|
@@ -386,6 +386,25 @@ function json(res, data, status = 200) {
|
|
|
386
386
|
res.writeHead(status, { "Content-Type": "application/json; charset=utf-8", "Cache-Control": "no-cache" });
|
|
387
387
|
res.end(JSON.stringify(data));
|
|
388
388
|
}
|
|
389
|
+
var ERROR_PAGE_TEXT = {
|
|
390
|
+
400: "\u8BF7\u6C42\u65E0\u6548",
|
|
391
|
+
403: "\u6CA1\u6709\u8BBF\u95EE\u6743\u9650",
|
|
392
|
+
404: "\u9875\u9762\u4E0D\u5B58\u5728"
|
|
393
|
+
};
|
|
394
|
+
function sendErrorPage(res, code) {
|
|
395
|
+
if (res.headersSent) {
|
|
396
|
+
try {
|
|
397
|
+
res.destroy();
|
|
398
|
+
} catch {
|
|
399
|
+
}
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
const text = ERROR_PAGE_TEXT[code] ?? "\u51FA\u9519\u4E86";
|
|
403
|
+
res.writeHead(code, { "Content-Type": "text/html; charset=utf-8" });
|
|
404
|
+
res.end(
|
|
405
|
+
`<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><title>${code} ${text} \xB7 zdashboard</title></head><body><h1>${code} \xB7 ${text}</h1><p>\u8BF7\u6C42\u7684\u8D44\u6E90\u4E0D\u53EF\u7528\u6216\u5DF2\u88AB\u79FB\u52A8\u3002</p><p><a href="/">\u8FD4\u56DE\u9996\u9875</a></p></body></html>`
|
|
406
|
+
);
|
|
407
|
+
}
|
|
389
408
|
function guarded(req, res, token) {
|
|
390
409
|
if (req.headers["x-stop-token"] !== token) {
|
|
391
410
|
res.writeHead(403);
|
|
@@ -485,10 +504,14 @@ var ServerService = class extends Service {
|
|
|
485
504
|
writePluginsConfig(this.root, merged);
|
|
486
505
|
}
|
|
487
506
|
serveFile(filePath, req, res, injectHtml) {
|
|
507
|
+
const apiPath = (req.url || "").startsWith("/__");
|
|
488
508
|
fs3.stat(filePath, (err, stat) => {
|
|
489
509
|
if (err) {
|
|
490
|
-
|
|
491
|
-
|
|
510
|
+
if (apiPath) {
|
|
511
|
+
res.writeHead(404);
|
|
512
|
+
return res.end("Not found");
|
|
513
|
+
}
|
|
514
|
+
return sendErrorPage(res, 404);
|
|
492
515
|
}
|
|
493
516
|
const ext = path3.extname(filePath).toLowerCase();
|
|
494
517
|
const ct = MIME[ext] ?? "application/octet-stream";
|
|
@@ -526,8 +549,11 @@ var ServerService = class extends Service {
|
|
|
526
549
|
}
|
|
527
550
|
fs3.readFile(filePath, (err2, data) => {
|
|
528
551
|
if (err2) {
|
|
529
|
-
|
|
530
|
-
|
|
552
|
+
if (apiPath) {
|
|
553
|
+
res.writeHead(404);
|
|
554
|
+
return res.end("Not found");
|
|
555
|
+
}
|
|
556
|
+
return sendErrorPage(res, 404);
|
|
531
557
|
}
|
|
532
558
|
let body = data;
|
|
533
559
|
if (injectHtml && ext === ".html") {
|
|
@@ -594,22 +620,26 @@ var ServerService = class extends Service {
|
|
|
594
620
|
if (url !== "/") fp2 = path3.join(this.appDir, this.safeDecode(url));
|
|
595
621
|
if (url.indexOf("/__app/") === 0) fp2 = path3.join(this.appDir, url.slice(7));
|
|
596
622
|
if (fp2 !== this.appDir && fp2.indexOf(this.appDir + path3.sep) !== 0) {
|
|
597
|
-
res
|
|
598
|
-
return res.end("Forbidden");
|
|
623
|
+
return sendErrorPage(res, 403);
|
|
599
624
|
}
|
|
600
625
|
if (url === "/") fp2 = path3.join(this.appDir, "index.html");
|
|
601
626
|
return this.serveFile(fp2, req, res, false);
|
|
602
627
|
}
|
|
603
628
|
const fp = path3.join(this.root, this.safeDecode(url));
|
|
604
629
|
if (fp !== this.root && fp.indexOf(this.root + path3.sep) !== 0) {
|
|
605
|
-
|
|
606
|
-
|
|
630
|
+
if ((req.url || "").startsWith("/__")) {
|
|
631
|
+
res.writeHead(403);
|
|
632
|
+
return res.end("Forbidden");
|
|
633
|
+
}
|
|
634
|
+
return sendErrorPage(res, 403);
|
|
607
635
|
}
|
|
608
636
|
return this.serveFile(fp, req, res, true);
|
|
609
637
|
} catch (e) {
|
|
610
638
|
try {
|
|
611
|
-
|
|
612
|
-
|
|
639
|
+
if ((req.url || "").startsWith("/__")) {
|
|
640
|
+
res.writeHead(400);
|
|
641
|
+
res.end("bad request");
|
|
642
|
+
} else sendErrorPage(res, 400);
|
|
613
643
|
} catch {
|
|
614
644
|
}
|
|
615
645
|
console.error("[zdashboard] handler error", e);
|
|
@@ -1055,9 +1085,66 @@ var DashboardService = class extends Service3 {
|
|
|
1055
1085
|
}
|
|
1056
1086
|
};
|
|
1057
1087
|
|
|
1088
|
+
// src/plugins/just/index.ts
|
|
1089
|
+
import { execFile as execFile6 } from "child_process";
|
|
1090
|
+
|
|
1058
1091
|
// src/server/just-runner.ts
|
|
1059
1092
|
import { spawn, execFile as execFile5 } from "child_process";
|
|
1060
1093
|
import iconv from "iconv-lite";
|
|
1094
|
+
|
|
1095
|
+
// src/server/just-recipe-params.ts
|
|
1096
|
+
var NAME_RE = /^[A-Za-z_][A-Za-z0-9_-]*/;
|
|
1097
|
+
function parseRecipeSignature(line) {
|
|
1098
|
+
let s = line.trim();
|
|
1099
|
+
if (s.startsWith("@")) s = s.slice(1).trimStart();
|
|
1100
|
+
const nameMatch = NAME_RE.exec(s);
|
|
1101
|
+
if (!nameMatch) return null;
|
|
1102
|
+
const name = nameMatch[0];
|
|
1103
|
+
const params = [];
|
|
1104
|
+
let i = name.length;
|
|
1105
|
+
while (true) {
|
|
1106
|
+
while (i < s.length && /\s/.test(s[i])) i++;
|
|
1107
|
+
if (i >= s.length) return null;
|
|
1108
|
+
if (s[i] === ":") {
|
|
1109
|
+
const rest = s.slice(i + 1).trim();
|
|
1110
|
+
if (rest === "" || rest.startsWith("#")) return { name, params };
|
|
1111
|
+
return null;
|
|
1112
|
+
}
|
|
1113
|
+
let j = i;
|
|
1114
|
+
if (s[j] === "+" || s[j] === "$") j++;
|
|
1115
|
+
const pm = NAME_RE.exec(s.slice(j));
|
|
1116
|
+
if (!pm) return null;
|
|
1117
|
+
j += pm[0].length;
|
|
1118
|
+
if (s[j] === "=") {
|
|
1119
|
+
j++;
|
|
1120
|
+
if (s[j] === '"' || s[j] === "'") {
|
|
1121
|
+
const quote = s[j];
|
|
1122
|
+
j++;
|
|
1123
|
+
while (j < s.length && s[j] !== quote) j++;
|
|
1124
|
+
if (j >= s.length) return null;
|
|
1125
|
+
j++;
|
|
1126
|
+
} else {
|
|
1127
|
+
while (j < s.length && !/\s/.test(s[j]) && s[j] !== ":") j++;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
params.push(pm[0]);
|
|
1131
|
+
i = j;
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
function buildJustArgv(recipe, args) {
|
|
1135
|
+
if (!args) return [recipe];
|
|
1136
|
+
return [recipe, ...Object.entries(args).map(([k, v]) => `${k}=${v}`)];
|
|
1137
|
+
}
|
|
1138
|
+
function pickStringArgs(args) {
|
|
1139
|
+
if (typeof args !== "object" || args === null) return void 0;
|
|
1140
|
+
const out = {};
|
|
1141
|
+
for (const [k, v] of Object.entries(args)) {
|
|
1142
|
+
if (typeof v === "string") out[k] = v;
|
|
1143
|
+
}
|
|
1144
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
// src/server/just-runner.ts
|
|
1061
1148
|
var MAX_BUFFER = 1e3;
|
|
1062
1149
|
var utf8Strict = new TextDecoder("utf-8", { fatal: true });
|
|
1063
1150
|
function decodeLine(buf) {
|
|
@@ -1072,6 +1159,8 @@ var JustRunner = class {
|
|
|
1072
1159
|
tasks = /* @__PURE__ */ new Map();
|
|
1073
1160
|
clients = /* @__PURE__ */ new Set();
|
|
1074
1161
|
recipesCache = null;
|
|
1162
|
+
/** recipe 参数清单进程内缓存(含失败空数组),避免反复 just --show 探测 */
|
|
1163
|
+
paramsCache = /* @__PURE__ */ new Map();
|
|
1075
1164
|
constructor(cwd) {
|
|
1076
1165
|
this.cwd = cwd;
|
|
1077
1166
|
}
|
|
@@ -1101,6 +1190,24 @@ var JustRunner = class {
|
|
|
1101
1190
|
});
|
|
1102
1191
|
});
|
|
1103
1192
|
}
|
|
1193
|
+
/** 懒解析单 recipe 参数:just --show 输出中首个可解析签名行(跳过 recipe 上方注释/空行)→ 参数名;失败记空数组并缓存 */
|
|
1194
|
+
recipeParams(name) {
|
|
1195
|
+
const hit = this.paramsCache.get(name);
|
|
1196
|
+
if (hit) return Promise.resolve(hit);
|
|
1197
|
+
return new Promise((resolve) => {
|
|
1198
|
+
execFile5("just", ["--show", name], { cwd: this.cwd, maxBuffer: 1 << 20, timeout: 8e3 }, (err, stdout) => {
|
|
1199
|
+
const sig = err ? null : stdout.split(/\r?\n/).map((l) => parseRecipeSignature(l)).find(Boolean) ?? null;
|
|
1200
|
+
const params = sig?.params ?? [];
|
|
1201
|
+
this.paramsCache.set(name, params);
|
|
1202
|
+
resolve(params);
|
|
1203
|
+
});
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
/** recipes 列表附参数清单(参数探测逐 recipe 并发,进程内缓存后零开销) */
|
|
1207
|
+
async recipesWithParams() {
|
|
1208
|
+
const list = await this.recipes();
|
|
1209
|
+
return Promise.all(list.map(async (r) => ({ ...r, params: await this.recipeParams(r.name) })));
|
|
1210
|
+
}
|
|
1104
1211
|
subscribe(fn) {
|
|
1105
1212
|
this.clients.add(fn);
|
|
1106
1213
|
for (const t of this.tasks.values()) {
|
|
@@ -1115,16 +1222,16 @@ var JustRunner = class {
|
|
|
1115
1222
|
list() {
|
|
1116
1223
|
return Array.from(this.tasks.values(), (t) => ({ recipe: t.recipe, state: t.state, code: t.code, startedAt: t.startedAt }));
|
|
1117
1224
|
}
|
|
1118
|
-
/** 启动 recipe:同名先停旧进程(重启语义),不影响其他任务;调用方须先用 recipes()
|
|
1119
|
-
|
|
1225
|
+
/** 启动 recipe:同名先停旧进程(重启语义),不影响其他任务;调用方须先用 recipes() 校验名字;
|
|
1226
|
+
* args 为可选参数表,spawn 数组 argv 追加 k=v(不经 shell,特殊字符安全) */
|
|
1227
|
+
start(recipe, args) {
|
|
1120
1228
|
this.killOne(recipe);
|
|
1121
1229
|
const task = { recipe, child: null, state: "running", code: null, startedAt: Date.now(), buffer: [], pending: Buffer.alloc(0) };
|
|
1122
1230
|
this.tasks.set(recipe, task);
|
|
1123
1231
|
this.emit({ type: "clear", recipe });
|
|
1124
1232
|
this.emit({ type: "state", recipe, state: "running", code: null, startedAt: task.startedAt });
|
|
1125
|
-
const child = spawn("just",
|
|
1233
|
+
const child = spawn("just", buildJustArgv(recipe, args), {
|
|
1126
1234
|
cwd: this.cwd,
|
|
1127
|
-
shell: true,
|
|
1128
1235
|
env: {
|
|
1129
1236
|
...process.env,
|
|
1130
1237
|
FORCE_COLOR: "1",
|
|
@@ -1165,10 +1272,22 @@ var JustRunner = class {
|
|
|
1165
1272
|
this.emit({ type: "state", recipe, state: "exited", code: task.code, startedAt: task.startedAt, signal: task.signal });
|
|
1166
1273
|
});
|
|
1167
1274
|
}
|
|
1275
|
+
/** 行推送:行内 \r 再切分为多段独立行(进度条输出降级为分段渲染);
|
|
1276
|
+
* \r 空段丢弃,但整行全空仍推送一条空行(空行是日志内容,不静默丢弃) */
|
|
1168
1277
|
pushLine(task, line) {
|
|
1169
|
-
|
|
1278
|
+
const body = line.endsWith("\n") ? line.slice(0, -1) : line;
|
|
1279
|
+
const segs = body.split("\r").filter(Boolean);
|
|
1280
|
+
if (segs.length === 0) {
|
|
1281
|
+
this.pushSegment(task, "");
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
for (const seg of segs) this.pushSegment(task, seg);
|
|
1285
|
+
}
|
|
1286
|
+
pushSegment(task, seg) {
|
|
1287
|
+
const text = seg + "\n";
|
|
1288
|
+
task.buffer.push(text);
|
|
1170
1289
|
if (task.buffer.length > MAX_BUFFER) task.buffer.shift();
|
|
1171
|
-
this.emit({ type: "log", recipe: task.recipe, text
|
|
1290
|
+
this.emit({ type: "log", recipe: task.recipe, text });
|
|
1172
1291
|
}
|
|
1173
1292
|
/** 停单个任务;不传 recipe 停全部 */
|
|
1174
1293
|
stop(recipe) {
|
|
@@ -1305,12 +1424,21 @@ var apply3 = defineBuiltin({
|
|
|
1305
1424
|
ctx.onDispose(() => runner.stop());
|
|
1306
1425
|
ctx.route("/__just/recipes", async () => {
|
|
1307
1426
|
try {
|
|
1308
|
-
return await runner.
|
|
1427
|
+
return await runner.recipesWithParams();
|
|
1309
1428
|
} catch {
|
|
1310
1429
|
return [];
|
|
1311
1430
|
}
|
|
1312
1431
|
});
|
|
1313
1432
|
ctx.route("/__just/tasks", async () => runner.list());
|
|
1433
|
+
ctx.route("/__just/status", async () => {
|
|
1434
|
+
return await new Promise((resolve) => {
|
|
1435
|
+
execFile6("just", ["--list", "--unsorted"], { cwd: root, maxBuffer: 1 << 20, timeout: 8e3 }, (err) => {
|
|
1436
|
+
if (!err) return resolve({ installed: true, hasJustfile: true });
|
|
1437
|
+
if (err.code === "ENOENT") return resolve({ installed: false, hasJustfile: false });
|
|
1438
|
+
resolve({ installed: true, hasJustfile: false });
|
|
1439
|
+
});
|
|
1440
|
+
});
|
|
1441
|
+
});
|
|
1314
1442
|
ctx.sse("/__just/logs", (res) => {
|
|
1315
1443
|
const unsub = runner.subscribe((ev) => {
|
|
1316
1444
|
try {
|
|
@@ -1326,8 +1454,11 @@ var apply3 = defineBuiltin({
|
|
|
1326
1454
|
const handleAction = async (req, res, act) => {
|
|
1327
1455
|
const body = await readBody(req);
|
|
1328
1456
|
let recipe;
|
|
1457
|
+
let rawArgs;
|
|
1329
1458
|
try {
|
|
1330
|
-
|
|
1459
|
+
const parsed = JSON.parse(body || "{}");
|
|
1460
|
+
recipe = parsed.recipe;
|
|
1461
|
+
rawArgs = parsed.args;
|
|
1331
1462
|
} catch {
|
|
1332
1463
|
}
|
|
1333
1464
|
if (act === "clear") {
|
|
@@ -1346,7 +1477,7 @@ var apply3 = defineBuiltin({
|
|
|
1346
1477
|
res.end('{"error":"unknown recipe"}');
|
|
1347
1478
|
return void 0;
|
|
1348
1479
|
}
|
|
1349
|
-
runner.start(recipe);
|
|
1480
|
+
runner.start(recipe, pickStringArgs(rawArgs));
|
|
1350
1481
|
} else {
|
|
1351
1482
|
runner.stop(recipe || void 0);
|
|
1352
1483
|
}
|
|
@@ -1480,7 +1611,7 @@ var apply5 = defineBuiltin({
|
|
|
1480
1611
|
// src/plugins/stats/index.ts
|
|
1481
1612
|
import fs8 from "fs";
|
|
1482
1613
|
import path8 from "path";
|
|
1483
|
-
import { execFile as
|
|
1614
|
+
import { execFile as execFile7 } from "child_process";
|
|
1484
1615
|
|
|
1485
1616
|
// src/plugins/stats/manifest.ts
|
|
1486
1617
|
var manifest4 = {
|
|
@@ -1495,7 +1626,7 @@ var manifest4 = {
|
|
|
1495
1626
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", ".cache"]);
|
|
1496
1627
|
function execFilePromise(file, args, opts) {
|
|
1497
1628
|
return new Promise((resolve) => {
|
|
1498
|
-
|
|
1629
|
+
execFile7(file, args, opts, (err, stdout) => resolve(err ? "" : stdout));
|
|
1499
1630
|
});
|
|
1500
1631
|
}
|
|
1501
1632
|
function scan(root) {
|