@minonl/dia 0.0.9 → 0.0.11
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/README.md +12 -0
- package/dist/cli.js +339 -66
- package/docs/index.md +1 -1
- package/docs/msg-collab-m1.md +24 -0
- package/docs/overview.md +6 -0
- package/docs/service-noun.md +38 -4
- package/package.json +1 -1
- package/packages/wt/bin/dia-wt +157 -67
- package/packages/wt/tests/adopt.test.sh +46 -44
package/dist/cli.js
CHANGED
|
@@ -4392,7 +4392,7 @@ function migrateLegacy(opts = {}) {
|
|
|
4392
4392
|
|
|
4393
4393
|
// src/claims.ts
|
|
4394
4394
|
init_paths();
|
|
4395
|
-
import { appendFileSync as appendFileSync5, existsSync as existsSync14, mkdirSync as mkdirSync13, readFileSync as readFileSync13 } from "node:fs";
|
|
4395
|
+
import { appendFileSync as appendFileSync5, existsSync as existsSync14, mkdirSync as mkdirSync13, readFileSync as readFileSync13, renameSync as renameSync7, writeFileSync as writeFileSync11 } from "node:fs";
|
|
4396
4396
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
4397
4397
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
4398
4398
|
import { basename as basename7, join as join16, relative as relative5, resolve as resolve9, sep as sep7 } from "node:path";
|
|
@@ -4510,6 +4510,58 @@ function unclaim(target, opts = {}) {
|
|
|
4510
4510
|
return { ...hit, released_at: new Date().toISOString() };
|
|
4511
4511
|
});
|
|
4512
4512
|
}
|
|
4513
|
+
function compact(opts = {}) {
|
|
4514
|
+
const days = Math.max(0, Number(opts.days ?? 7));
|
|
4515
|
+
const now = opts.now || new Date;
|
|
4516
|
+
const cutoff = now.getTime() - days * 86400000;
|
|
4517
|
+
return withLock("claims", () => {
|
|
4518
|
+
const f = file();
|
|
4519
|
+
if (!existsSync14(f))
|
|
4520
|
+
return { before: 0, after: 0, dropped: 0 };
|
|
4521
|
+
const raw = readFileSync13(f, "utf8");
|
|
4522
|
+
const lines = raw.split(`
|
|
4523
|
+
`).filter((l) => l.trim());
|
|
4524
|
+
const keepIds = new Set;
|
|
4525
|
+
for (const line of lines) {
|
|
4526
|
+
let e;
|
|
4527
|
+
try {
|
|
4528
|
+
e = JSON.parse(line);
|
|
4529
|
+
} catch {
|
|
4530
|
+
continue;
|
|
4531
|
+
}
|
|
4532
|
+
if (e.event !== "claim" || !e.id)
|
|
4533
|
+
continue;
|
|
4534
|
+
const created = new Date(e.created_at || e.ts).getTime();
|
|
4535
|
+
if (isActive({ id: e.id, path: e.path || "", scope: e.scope || "", holder: e.holder || "", created_at: e.created_at || e.ts, expires_at: e.expires_at || e.ts }, now.getTime()) || created > cutoff) {
|
|
4536
|
+
keepIds.add(e.id);
|
|
4537
|
+
}
|
|
4538
|
+
}
|
|
4539
|
+
const kept = [];
|
|
4540
|
+
let dropped = 0;
|
|
4541
|
+
for (const line of lines) {
|
|
4542
|
+
let e;
|
|
4543
|
+
try {
|
|
4544
|
+
e = JSON.parse(line);
|
|
4545
|
+
} catch {
|
|
4546
|
+
dropped++;
|
|
4547
|
+
continue;
|
|
4548
|
+
}
|
|
4549
|
+
if (e.id && keepIds.has(e.id))
|
|
4550
|
+
kept.push(line);
|
|
4551
|
+
else
|
|
4552
|
+
dropped++;
|
|
4553
|
+
}
|
|
4554
|
+
if (dropped) {
|
|
4555
|
+
const tmp = `${f}.tmp.${process.pid}`;
|
|
4556
|
+
writeFileSync11(tmp, kept.join(`
|
|
4557
|
+
`) + (kept.length ? `
|
|
4558
|
+
` : ""), { mode: 384 });
|
|
4559
|
+
renameSync7(tmp, f);
|
|
4560
|
+
}
|
|
4561
|
+
const after = existsSync14(f) ? readFileSync13(f, "utf8").length : 0;
|
|
4562
|
+
return { before: raw.length, after, dropped };
|
|
4563
|
+
});
|
|
4564
|
+
}
|
|
4513
4565
|
|
|
4514
4566
|
// src/task.ts
|
|
4515
4567
|
init_paths();
|
|
@@ -4786,7 +4838,7 @@ init_paths();
|
|
|
4786
4838
|
|
|
4787
4839
|
// src/agents.ts
|
|
4788
4840
|
init_paths();
|
|
4789
|
-
import { existsSync as existsSync16, mkdirSync as mkdirSync15, readdirSync as readdirSync9, readFileSync as readFileSync15, renameSync as
|
|
4841
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync15, readdirSync as readdirSync9, readFileSync as readFileSync15, renameSync as renameSync8, rmSync as rmSync6, writeFileSync as writeFileSync12 } from "node:fs";
|
|
4790
4842
|
import { basename as basename8, join as join18, resolve as resolve10 } from "node:path";
|
|
4791
4843
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
4792
4844
|
var agentsRoot2 = () => agentsRoot();
|
|
@@ -4794,8 +4846,8 @@ var fileOf = (agent) => join18(agentsRoot2(), `${agent}.json`);
|
|
|
4794
4846
|
function atomicWrite2(path, data) {
|
|
4795
4847
|
mkdirSync15(join18(path, ".."), { recursive: true });
|
|
4796
4848
|
const tmp = `${path}.tmp.${process.pid}`;
|
|
4797
|
-
|
|
4798
|
-
|
|
4849
|
+
writeFileSync12(tmp, data);
|
|
4850
|
+
renameSync8(tmp, path);
|
|
4799
4851
|
}
|
|
4800
4852
|
function sh(cmd, args) {
|
|
4801
4853
|
try {
|
|
@@ -4893,7 +4945,7 @@ function discoverTmux() {
|
|
|
4893
4945
|
|
|
4894
4946
|
// src/tmux.ts
|
|
4895
4947
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
4896
|
-
import { mkdtempSync, writeFileSync as
|
|
4948
|
+
import { mkdtempSync, writeFileSync as writeFileSync13 } from "node:fs";
|
|
4897
4949
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
4898
4950
|
import { join as join19 } from "node:path";
|
|
4899
4951
|
function tmux(args) {
|
|
@@ -4939,7 +4991,7 @@ function deliver(agent, id, opts = {}) {
|
|
|
4939
4991
|
`);
|
|
4940
4992
|
const dir = mkdtempSync(join19(tmpdir2(), "dia-msg-"));
|
|
4941
4993
|
const buf = join19(dir, "body.txt");
|
|
4942
|
-
|
|
4994
|
+
writeFileSync13(buf, text);
|
|
4943
4995
|
try {
|
|
4944
4996
|
tmux(["load-buffer", "-b", `dia-${m.id}`, buf]);
|
|
4945
4997
|
tmux(["paste-buffer", "-p", "-b", `dia-${m.id}`, "-t", pane]);
|
|
@@ -5404,7 +5456,7 @@ function checks(reg) {
|
|
|
5404
5456
|
|
|
5405
5457
|
// src/deps.ts
|
|
5406
5458
|
init_paths();
|
|
5407
|
-
import { existsSync as existsSync20, readFileSync as readFileSync18, mkdirSync as mkdirSync16, writeFileSync as
|
|
5459
|
+
import { existsSync as existsSync20, readFileSync as readFileSync18, mkdirSync as mkdirSync16, writeFileSync as writeFileSync14, chmodSync as chmodSync3 } from "node:fs";
|
|
5408
5460
|
import { homedir as homedir10 } from "node:os";
|
|
5409
5461
|
import { dirname as dirname11, join as join23 } from "node:path";
|
|
5410
5462
|
import { execFileSync as execFileSync9 } from "node:child_process";
|
|
@@ -5511,7 +5563,7 @@ function append4(entry, p) {
|
|
|
5511
5563
|
const line = JSON.stringify(entry) + `
|
|
5512
5564
|
`;
|
|
5513
5565
|
const prev = existsSync20(path) ? readFileSync18(path, "utf8") : "";
|
|
5514
|
-
|
|
5566
|
+
writeFileSync14(path, prev.endsWith(`
|
|
5515
5567
|
`) || !prev ? prev + line : prev + `
|
|
5516
5568
|
` + line);
|
|
5517
5569
|
try {
|
|
@@ -5847,7 +5899,7 @@ function run2(cfg, cmd, opts = {}) {
|
|
|
5847
5899
|
}
|
|
5848
5900
|
|
|
5849
5901
|
// src/bw.ts
|
|
5850
|
-
import { chmodSync as chmodSync4, existsSync as existsSync24, mkdirSync as mkdirSync17, readFileSync as readFileSync21, rmSync as rmSync7, writeFileSync as
|
|
5902
|
+
import { chmodSync as chmodSync4, existsSync as existsSync24, mkdirSync as mkdirSync17, readFileSync as readFileSync21, rmSync as rmSync7, writeFileSync as writeFileSync15 } from "node:fs";
|
|
5851
5903
|
init_paths();
|
|
5852
5904
|
import { join as join27 } from "node:path";
|
|
5853
5905
|
import { execFileSync as execFileSync10 } from "node:child_process";
|
|
@@ -5869,7 +5921,7 @@ function readSession2() {
|
|
|
5869
5921
|
function writeSession(key) {
|
|
5870
5922
|
const f = sessionFile2();
|
|
5871
5923
|
mkdirSync17(join27(f, ".."), { recursive: true });
|
|
5872
|
-
|
|
5924
|
+
writeFileSync15(f, key + `
|
|
5873
5925
|
`, { mode: 384 });
|
|
5874
5926
|
chmodSync4(f, 384);
|
|
5875
5927
|
}
|
|
@@ -6100,7 +6152,7 @@ function askBroker(req, timeoutMs = 5000) {
|
|
|
6100
6152
|
}
|
|
6101
6153
|
|
|
6102
6154
|
// packages/cli/src/index.ts
|
|
6103
|
-
import { execFileSync as execFileSync12, spawn as
|
|
6155
|
+
import { execFileSync as execFileSync12, spawn as spawn3, spawnSync as spawnSync5 } from "node:child_process";
|
|
6104
6156
|
import { mkdirSync as mkdirSync20, openSync as openSync3 } from "node:fs";
|
|
6105
6157
|
import { basename as basename10, join as join32 } from "node:path";
|
|
6106
6158
|
import { existsSync as existsSync29, readFileSync as readFileSync26 } from "node:fs";
|
|
@@ -6108,7 +6160,7 @@ import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
|
6108
6160
|
import { resolve as resolvePath, sep as pathSep } from "node:path";
|
|
6109
6161
|
|
|
6110
6162
|
// src/team.ts
|
|
6111
|
-
import { appendFileSync as appendFileSync8, cpSync as cpSync3, existsSync as existsSync26, mkdirSync as mkdirSync18, readFileSync as readFileSync23, readdirSync as readdirSync11, rmSync as rmSync8, writeFileSync as
|
|
6163
|
+
import { appendFileSync as appendFileSync8, cpSync as cpSync3, existsSync as existsSync26, mkdirSync as mkdirSync18, readFileSync as readFileSync23, readdirSync as readdirSync11, rmSync as rmSync8, writeFileSync as writeFileSync16 } from "node:fs";
|
|
6112
6164
|
import { homedir as homedir15, tmpdir as tmpdir3 } from "node:os";
|
|
6113
6165
|
import { join as join29, resolve as resolve14 } from "node:path";
|
|
6114
6166
|
|
|
@@ -6161,23 +6213,23 @@ function runMock(id) {
|
|
|
6161
6213
|
const t = get2(id);
|
|
6162
6214
|
const dir = runDir(id);
|
|
6163
6215
|
mkdirSync18(dir, { recursive: true, mode: 448 });
|
|
6164
|
-
|
|
6216
|
+
writeFileSync16(join29(dir, "plan.md"), `# Plan: ${t.title}
|
|
6165
6217
|
|
|
6166
6218
|
- judge: ${t.judge || "(missing)"}
|
|
6167
6219
|
- roles: planner → builder → verifier → redteam
|
|
6168
6220
|
`);
|
|
6169
|
-
|
|
6221
|
+
writeFileSync16(join29(dir, "diff.patch"), `# mock diff
|
|
6170
6222
|
`);
|
|
6171
|
-
|
|
6223
|
+
writeFileSync16(join29(dir, "test.log"), `mock tests: 0 failures
|
|
6172
6224
|
`);
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6225
|
+
writeFileSync16(join29(dir, "verify.json"), JSON.stringify({ conclusion: "通过", commit: "mock", evidence: ["mock test.log"] }, null, 1));
|
|
6226
|
+
writeFileSync16(join29(dir, "redteam.json"), JSON.stringify({ findings: [] }, null, 1));
|
|
6227
|
+
writeFileSync16(join29(dir, "packet.md"), `# 验收包 ${id}
|
|
6176
6228
|
|
|
6177
6229
|
- 结论:通过(mock)
|
|
6178
6230
|
- 风险:无
|
|
6179
6231
|
`);
|
|
6180
|
-
|
|
6232
|
+
writeFileSync16(join29(dir, "run.json"), JSON.stringify({ mock: true, judge: t.judge || null, ts: new Date().toISOString() }, null, 1));
|
|
6181
6233
|
status3(id, "awaiting_acceptance", "mock run");
|
|
6182
6234
|
return dir;
|
|
6183
6235
|
}
|
|
@@ -6197,7 +6249,7 @@ function runJudgeSafe(workspace, judge) {
|
|
|
6197
6249
|
}
|
|
6198
6250
|
}
|
|
6199
6251
|
function writeUsage(dir, id, acc) {
|
|
6200
|
-
|
|
6252
|
+
writeFileSync16(join29(dir, "usage.json"), JSON.stringify({ task_id: id, calls: acc.rows.length, input_tokens: acc.input, output_tokens: acc.output, total_tokens: acc.total, cost_usd: acc.cost, unpriced_calls: acc.unpriced, by_role: acc.rows }, null, 1));
|
|
6201
6253
|
}
|
|
6202
6254
|
function judgeWorkspace(dir, fallback) {
|
|
6203
6255
|
try {
|
|
@@ -6269,8 +6321,8 @@ function gate(id) {
|
|
|
6269
6321
|
exit = typeof e?.status === "number" ? e.status : 1;
|
|
6270
6322
|
out = String(e?.stdout || e?.message || "");
|
|
6271
6323
|
}
|
|
6272
|
-
|
|
6273
|
-
|
|
6324
|
+
writeFileSync16(join29(dir, "judge.json"), JSON.stringify({ source: "gate", command: judgeCmd, exit, ms: Date.now() - started, ts: new Date().toISOString() }, null, 1));
|
|
6325
|
+
writeFileSync16(join29(dir, "judge.log"), out.slice(0, 20000));
|
|
6274
6326
|
if (exit !== 0)
|
|
6275
6327
|
return { ok: false, reason: `judge exit: ${exit}(见 judge.log)` };
|
|
6276
6328
|
return { ok: true };
|
|
@@ -6377,7 +6429,7 @@ function snapPath(id, dir, tag) {
|
|
|
6377
6429
|
list = [];
|
|
6378
6430
|
}
|
|
6379
6431
|
list.push(p);
|
|
6380
|
-
|
|
6432
|
+
writeFileSync16(f, JSON.stringify(list, null, 1));
|
|
6381
6433
|
return p;
|
|
6382
6434
|
}
|
|
6383
6435
|
function snapshotWorkspace(src, dest) {
|
|
@@ -6415,17 +6467,17 @@ async function repairRedteamHighs(o) {
|
|
|
6415
6467
|
await o.builderFix(high);
|
|
6416
6468
|
const workspace = judgeWorkspace(dir, o.workspace);
|
|
6417
6469
|
const jr = runJudgeSafe(workspace, judge);
|
|
6418
|
-
|
|
6470
|
+
writeFileSync16(join29(dir, "test.log"), `judge: ${judge}
|
|
6419
6471
|
exit=${jr.code}
|
|
6420
6472
|
${jr.out}`.slice(0, 20000));
|
|
6421
6473
|
const diff2 = gitDiff(workspace);
|
|
6422
|
-
|
|
6474
|
+
writeFileSync16(join29(dir, "diff.patch"), diff2);
|
|
6423
6475
|
const ws2 = snapshotWorkspace(workspace, snapPath(id, dir, "redteam-ws-2"));
|
|
6424
6476
|
const p2 = await o.round2(ws2, diff2);
|
|
6425
6477
|
redteam = { ...normalizeRedteam(p2.value), repaired: p2.repaired, round: 2 };
|
|
6426
|
-
|
|
6478
|
+
writeFileSync16(join29(dir, "redteam.json"), JSON.stringify(redteam, null, 1));
|
|
6427
6479
|
if (jr.code !== 0 || highFindings(redteam).length) {
|
|
6428
|
-
|
|
6480
|
+
writeFileSync16(join29(dir, "packet.md"), `# 验收包 ${id}
|
|
6429
6481
|
|
|
6430
6482
|
- 任务:${t.title}
|
|
6431
6483
|
- 结论:**已升级**(redteam 修复后仍不达标)
|
|
@@ -6523,7 +6575,7 @@ Keep exploration bounded (at most 3 reads); return only the JSON object now.`);
|
|
|
6523
6575
|
});
|
|
6524
6576
|
plan = planned.value;
|
|
6525
6577
|
} catch (e) {
|
|
6526
|
-
|
|
6578
|
+
writeFileSync16(join29(dir, "packet.md"), `# 验收包 ${id}
|
|
6527
6579
|
|
|
6528
6580
|
- 任务:${t.title}
|
|
6529
6581
|
- 结论:**已升级**(planner 输出无法解析)
|
|
@@ -6533,7 +6585,7 @@ Keep exploration bounded (at most 3 reads); return only the JSON object now.`);
|
|
|
6533
6585
|
status3(id, "escalated", "planner: " + e.message);
|
|
6534
6586
|
return { dir, escalated: true, error: e.message };
|
|
6535
6587
|
}
|
|
6536
|
-
|
|
6588
|
+
writeFileSync16(join29(dir, "plan.md"), `# Plan
|
|
6537
6589
|
|
|
6538
6590
|
${JSON.stringify(plan, null, 1)}
|
|
6539
6591
|
`);
|
|
@@ -6551,7 +6603,7 @@ Plan: ${JSON.stringify(plan)}
|
|
|
6551
6603
|
Judge: ${judge}${fix}
|
|
6552
6604
|
Report briefly what you changed and test results.`);
|
|
6553
6605
|
recordModelCall(id, `builder-${round}`, modelOf(buildNs), buildOut.usage, usageAcc, prices);
|
|
6554
|
-
|
|
6606
|
+
writeFileSync16(join29(dir, `builder-${round}.md`), buildOut.text || "(no output)");
|
|
6555
6607
|
const vws = snapshotWorkspace(workspace, snapPath(id, dir, `verify-ws-${round}`));
|
|
6556
6608
|
const verifyNs = await acp.newSession(vws);
|
|
6557
6609
|
const sVerify = verifyNs.sessionId;
|
|
@@ -6566,8 +6618,8 @@ Return only the JSON object.`);
|
|
|
6566
6618
|
return r.text;
|
|
6567
6619
|
});
|
|
6568
6620
|
verify = { ...normalizeVerify(fixed.value), repaired: fixed.repaired };
|
|
6569
|
-
|
|
6570
|
-
|
|
6621
|
+
writeFileSync16(join29(dir, "verify.json"), JSON.stringify(verify, null, 1));
|
|
6622
|
+
writeFileSync16(join29(dir, "test.log"), (verify.evidence || []).join(`
|
|
6571
6623
|
`) || "(no evidence)");
|
|
6572
6624
|
if (verify.conclusion === "通过")
|
|
6573
6625
|
break;
|
|
@@ -6577,9 +6629,9 @@ Return only the JSON object.`);
|
|
|
6577
6629
|
lastVerifyKey = key;
|
|
6578
6630
|
}
|
|
6579
6631
|
const diff = gitDiff(workspace);
|
|
6580
|
-
|
|
6632
|
+
writeFileSync16(join29(dir, "diff.patch"), diff);
|
|
6581
6633
|
if (verify?.conclusion !== "通过") {
|
|
6582
|
-
|
|
6634
|
+
writeFileSync16(join29(dir, "packet.md"), `# 验收包 ${id}
|
|
6583
6635
|
|
|
6584
6636
|
- 任务:${t.title}
|
|
6585
6637
|
- 结论:**已升级**(验证未通过)
|
|
@@ -6594,7 +6646,7 @@ ${(verify?.evidence || []).map((e) => "- " + e).join(`
|
|
|
6594
6646
|
status3(id, "escalated", "verify: " + verify?.conclusion);
|
|
6595
6647
|
return { dir, verify, escalated: true };
|
|
6596
6648
|
}
|
|
6597
|
-
|
|
6649
|
+
writeFileSync16(join29(dir, "run.json"), JSON.stringify({ mock: false, mode: "acp", workspace, judge, ts: new Date().toISOString() }, null, 1));
|
|
6598
6650
|
const rws = snapshotWorkspace(workspace, snapPath(id, dir, "redteam-ws"));
|
|
6599
6651
|
const redNs = await acp.newSession(rws);
|
|
6600
6652
|
const sRed = redNs.sessionId;
|
|
@@ -6610,7 +6662,7 @@ Return only the JSON object.`);
|
|
|
6610
6662
|
return r.text;
|
|
6611
6663
|
});
|
|
6612
6664
|
let redteam = { ...normalizeRedteam(fixed.value), repaired: fixed.repaired, round: 1 };
|
|
6613
|
-
|
|
6665
|
+
writeFileSync16(join29(dir, "redteam.json"), JSON.stringify(redteam, null, 1));
|
|
6614
6666
|
const repaired = await repairRedteamHighs({
|
|
6615
6667
|
id,
|
|
6616
6668
|
dir,
|
|
@@ -6647,7 +6699,7 @@ Return only the JSON object.`);
|
|
|
6647
6699
|
if (repaired.escalated)
|
|
6648
6700
|
return { dir, verify, redteam, escalated: true };
|
|
6649
6701
|
writeUsage(dir, id, usageAcc);
|
|
6650
|
-
|
|
6702
|
+
writeFileSync16(join29(dir, "packet.md"), `# 验收包 ${id}
|
|
6651
6703
|
|
|
6652
6704
|
- 任务:${t.title}
|
|
6653
6705
|
- 判据:${judge}
|
|
@@ -6768,7 +6820,7 @@ Judge: ${t.judge}
|
|
|
6768
6820
|
});
|
|
6769
6821
|
plan = planned.value;
|
|
6770
6822
|
} catch (e) {
|
|
6771
|
-
|
|
6823
|
+
writeFileSync16(join29(dir, "packet.md"), `# 验收包 ${id}
|
|
6772
6824
|
|
|
6773
6825
|
- 任务:${t.title}
|
|
6774
6826
|
- 结论:**已升级**(planner 输出无法解析)
|
|
@@ -6778,7 +6830,7 @@ Judge: ${t.judge}
|
|
|
6778
6830
|
status3(id, "escalated", "planner: " + e.message);
|
|
6779
6831
|
return { dir, escalated: true, error: e.message };
|
|
6780
6832
|
}
|
|
6781
|
-
|
|
6833
|
+
writeFileSync16(join29(dir, "plan.md"), `# Plan
|
|
6782
6834
|
|
|
6783
6835
|
${JSON.stringify(plan, null, 1)}
|
|
6784
6836
|
`);
|
|
@@ -6793,7 +6845,7 @@ Plan: ${JSON.stringify(plan)}
|
|
|
6793
6845
|
Judge: ${judge}${fix}
|
|
6794
6846
|
Report briefly what you changed and test results.`, tracker);
|
|
6795
6847
|
recordModelCall(id, `builder-${round}`, buildOut.model, buildOut.usage, usageAcc, prices);
|
|
6796
|
-
|
|
6848
|
+
writeFileSync16(join29(dir, `builder-${round}.md`), buildOut.text || "(no output)");
|
|
6797
6849
|
const vws = snapshotWorkspace(workspace, snapPath(id, dir, `verify-ws-${round}`));
|
|
6798
6850
|
const vOut = await callDsh("verifier", vws, `Repo: ${vws}
|
|
6799
6851
|
Run this judge yourself: ${judge}
|
|
@@ -6807,8 +6859,8 @@ ${msg}`, tracker);
|
|
|
6807
6859
|
return r.text;
|
|
6808
6860
|
});
|
|
6809
6861
|
verify = { ...normalizeVerify(fixed.value), repaired: fixed.repaired };
|
|
6810
|
-
|
|
6811
|
-
|
|
6862
|
+
writeFileSync16(join29(dir, "verify.json"), JSON.stringify(verify, null, 1));
|
|
6863
|
+
writeFileSync16(join29(dir, "test.log"), (verify.evidence || []).join(`
|
|
6812
6864
|
`) || "(no evidence)");
|
|
6813
6865
|
if (verify.conclusion === "通过")
|
|
6814
6866
|
break;
|
|
@@ -6818,9 +6870,9 @@ ${msg}`, tracker);
|
|
|
6818
6870
|
lastKey = key;
|
|
6819
6871
|
}
|
|
6820
6872
|
const diff = gitDiff(workspace);
|
|
6821
|
-
|
|
6873
|
+
writeFileSync16(join29(dir, "diff.patch"), diff);
|
|
6822
6874
|
if (verify?.conclusion !== "通过") {
|
|
6823
|
-
|
|
6875
|
+
writeFileSync16(join29(dir, "packet.md"), `# 验收包 ${id}
|
|
6824
6876
|
|
|
6825
6877
|
- 任务:${t.title}
|
|
6826
6878
|
- 结论:**已升级**(验证未通过)
|
|
@@ -6835,7 +6887,7 @@ ${(verify?.evidence || []).map((e) => "- " + e).join(`
|
|
|
6835
6887
|
status3(id, "escalated", "verify: " + verify?.conclusion);
|
|
6836
6888
|
return { dir, verify, escalated: true };
|
|
6837
6889
|
}
|
|
6838
|
-
|
|
6890
|
+
writeFileSync16(join29(dir, "run.json"), JSON.stringify({ mock: false, mode: "dsh", workspace, judge, ts: new Date().toISOString() }, null, 1));
|
|
6839
6891
|
const rws = snapshotWorkspace(workspace, snapPath(id, dir, "redteam-ws"));
|
|
6840
6892
|
const rOut = await callDsh("redteam", rws, `Repo: ${rws}
|
|
6841
6893
|
Diff:
|
|
@@ -6851,7 +6903,7 @@ ${msg}`, tracker);
|
|
|
6851
6903
|
return r.text;
|
|
6852
6904
|
});
|
|
6853
6905
|
let redteam = { ...normalizeRedteam(fixed.value), repaired: fixed.repaired, round: 1 };
|
|
6854
|
-
|
|
6906
|
+
writeFileSync16(join29(dir, "redteam.json"), JSON.stringify(redteam, null, 1));
|
|
6855
6907
|
const repaired = await repairRedteamHighs({
|
|
6856
6908
|
id,
|
|
6857
6909
|
dir,
|
|
@@ -6888,7 +6940,7 @@ ${msg}`, tracker);
|
|
|
6888
6940
|
if (repaired.escalated)
|
|
6889
6941
|
return { dir, verify, redteam, escalated: true };
|
|
6890
6942
|
writeUsage(dir, id, usageAcc);
|
|
6891
|
-
|
|
6943
|
+
writeFileSync16(join29(dir, "packet.md"), `# 验收包 ${id}
|
|
6892
6944
|
|
|
6893
6945
|
- 任务:${t.title}
|
|
6894
6946
|
- 判据:${judge}
|
|
@@ -6897,7 +6949,7 @@ ${msg}`, tracker);
|
|
|
6897
6949
|
- 反驳:${(redteam.findings || []).length} 条
|
|
6898
6950
|
- 模型调用:${usageAcc.rows.length} 次 / ${usageAcc.total} tokens
|
|
6899
6951
|
`);
|
|
6900
|
-
|
|
6952
|
+
writeFileSync16(join29(dir, "run.json"), JSON.stringify({ mock: false, mode: "dsh", workspace, judge, ts: new Date().toISOString() }, null, 1));
|
|
6901
6953
|
status3(id, "awaiting_acceptance", "dsh run");
|
|
6902
6954
|
return { dir, verify, redteam };
|
|
6903
6955
|
} finally {
|
|
@@ -7096,7 +7148,7 @@ function propose(opts = {}) {
|
|
|
7096
7148
|
}
|
|
7097
7149
|
|
|
7098
7150
|
// src/service.ts
|
|
7099
|
-
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
7151
|
+
import { spawn as spawn2, spawnSync as spawnSync4 } from "node:child_process";
|
|
7100
7152
|
import { createHash as createHash11, randomBytes as randomBytes4 } from "node:crypto";
|
|
7101
7153
|
import {
|
|
7102
7154
|
appendFileSync as appendFileSync9,
|
|
@@ -7111,7 +7163,7 @@ import {
|
|
|
7111
7163
|
statSync as statSync12,
|
|
7112
7164
|
symlinkSync as symlinkSync3,
|
|
7113
7165
|
unlinkSync as unlinkSync3,
|
|
7114
|
-
writeFileSync as
|
|
7166
|
+
writeFileSync as writeFileSync17
|
|
7115
7167
|
} from "node:fs";
|
|
7116
7168
|
import { hostname as osHostname } from "node:os";
|
|
7117
7169
|
import { basename as basename9, dirname as dirname13, isAbsolute as isAbsolute4, join as join31, relative as relative6, resolve as resolve15, sep as sep8 } from "node:path";
|
|
@@ -7121,6 +7173,52 @@ var ID_RE2 = /^[a-z0-9][a-z0-9-]*$/;
|
|
|
7121
7173
|
var VERSION_RE = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/;
|
|
7122
7174
|
var LIFECYCLE_TYPES = new Set(["periodic", "bounded", "service"]);
|
|
7123
7175
|
var STDIN_TYPES = new Set(["json", "text", "none"]);
|
|
7176
|
+
var ACP_HOSTS = ["dsh", "opencode"];
|
|
7177
|
+
var HEALTHCHECK_KINDS = new Set(["script", "acp"]);
|
|
7178
|
+
var isPlainObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
7179
|
+
var isNonEmptyString = (v) => typeof v === "string" && v.trim() !== "";
|
|
7180
|
+
function acpAgentCommand(agent) {
|
|
7181
|
+
if (!ACP_HOSTS.includes(agent?.host))
|
|
7182
|
+
throw new Error(`E_MANIFEST: entry.agent.host must be ${ACP_HOSTS.join("|")}`);
|
|
7183
|
+
const profile = agent.profile?.trim();
|
|
7184
|
+
if (agent.host === "dsh")
|
|
7185
|
+
return ["dsh", ...profile ? ["--profile", profile] : [], "acp"];
|
|
7186
|
+
return ["opencode", "acp"];
|
|
7187
|
+
}
|
|
7188
|
+
var isAgentEntry = (e) => !!e && e.agent !== undefined && e.agent !== null;
|
|
7189
|
+
var healthcheckKind = (hc) => hc?.kind ?? "script";
|
|
7190
|
+
var entryCmdOf = (svc) => svc.entry.cmd ?? "";
|
|
7191
|
+
function checkAgentEntry(a) {
|
|
7192
|
+
if (!isPlainObject(a))
|
|
7193
|
+
throw new Error("E_MANIFEST: entry.agent must be an object");
|
|
7194
|
+
if (!ACP_HOSTS.includes(a.host))
|
|
7195
|
+
throw new Error(`E_MANIFEST: entry.agent.host must be ${ACP_HOSTS.join("|")}`);
|
|
7196
|
+
for (const k of ["profile", "persona"]) {
|
|
7197
|
+
if (a[k] !== undefined && !isNonEmptyString(a[k]))
|
|
7198
|
+
throw new Error(`E_MANIFEST: entry.agent.${k} must be a non-empty string`);
|
|
7199
|
+
}
|
|
7200
|
+
if (a.host !== "dsh" && a.profile !== undefined) {
|
|
7201
|
+
throw new Error(`E_MANIFEST: entry.agent.profile 只适用于 host=dsh(${a.host} 没有 profile)`);
|
|
7202
|
+
}
|
|
7203
|
+
return a;
|
|
7204
|
+
}
|
|
7205
|
+
function checkEntry(entry) {
|
|
7206
|
+
if (!isPlainObject(entry))
|
|
7207
|
+
throw new Error("E_MANIFEST: entry required");
|
|
7208
|
+
const hasCmd = entry.cmd !== undefined && entry.cmd !== null;
|
|
7209
|
+
const hasAgent = entry.agent !== undefined && entry.agent !== null;
|
|
7210
|
+
if (hasCmd && hasAgent)
|
|
7211
|
+
throw new Error("E_MANIFEST: entry.cmd 与 entry.agent 互斥,只能声明其一");
|
|
7212
|
+
if (!hasCmd && !hasAgent)
|
|
7213
|
+
throw new Error("E_MANIFEST: entry.cmd required(脚本型)或 entry.agent(agent 型)");
|
|
7214
|
+
if (hasCmd) {
|
|
7215
|
+
if (typeof entry.cmd !== "string" || !entry.cmd.trim())
|
|
7216
|
+
throw new Error("E_MANIFEST: entry.cmd required");
|
|
7217
|
+
} else {
|
|
7218
|
+
checkAgentEntry(entry.agent);
|
|
7219
|
+
}
|
|
7220
|
+
return entry;
|
|
7221
|
+
}
|
|
7124
7222
|
function entryDir() {
|
|
7125
7223
|
return process.env.DIA_JOB_ENTRY_DIR || join31(jobRoot2(), "bin");
|
|
7126
7224
|
}
|
|
@@ -7163,8 +7261,14 @@ var LEVELS = ["ok", "degraded", "fail"];
|
|
|
7163
7261
|
function checkHealthcheck(hc) {
|
|
7164
7262
|
if (hc === null || typeof hc !== "object" || Array.isArray(hc))
|
|
7165
7263
|
throw new Error("E_MANIFEST: healthcheck must be an object");
|
|
7166
|
-
if (
|
|
7167
|
-
throw new Error("E_MANIFEST: healthcheck.
|
|
7264
|
+
if (hc.kind !== undefined && !HEALTHCHECK_KINDS.has(hc.kind))
|
|
7265
|
+
throw new Error("E_MANIFEST: healthcheck.kind must be script|acp");
|
|
7266
|
+
if (healthcheckKind(hc) === "script") {
|
|
7267
|
+
if (typeof hc.cmd !== "string" || !hc.cmd.trim())
|
|
7268
|
+
throw new Error("E_MANIFEST: healthcheck.cmd required");
|
|
7269
|
+
} else if (hc.cmd !== undefined) {
|
|
7270
|
+
throw new Error("E_MANIFEST: healthcheck.kind=acp 不得再声明 cmd(判据是 initialize + session/new,不是外部脚本)");
|
|
7271
|
+
}
|
|
7168
7272
|
if (hc.interval !== undefined) {
|
|
7169
7273
|
const why = checkCronInterval(hc.interval);
|
|
7170
7274
|
if (why)
|
|
@@ -7199,10 +7303,14 @@ function validateServiceManifest(m) {
|
|
|
7199
7303
|
throw new Error("E_MANIFEST: bad id");
|
|
7200
7304
|
if (!m.version || !VERSION_RE.test(m.version))
|
|
7201
7305
|
throw new Error("E_MANIFEST: bad version");
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
if (
|
|
7205
|
-
throw new Error(
|
|
7306
|
+
checkEntry(m.entry);
|
|
7307
|
+
const agentEntry = isAgentEntry(m.entry);
|
|
7308
|
+
if (agentEntry && healthcheckKind(m.healthcheck) !== "acp") {
|
|
7309
|
+
throw new Error('E_MANIFEST: entry.agent 必须配 healthcheck.kind="acp"(脚本型 healthcheck 无 cmd 可执行)');
|
|
7310
|
+
}
|
|
7311
|
+
if (!agentEntry && m.healthcheck && healthcheckKind(m.healthcheck) === "acp") {
|
|
7312
|
+
throw new Error('E_MANIFEST: healthcheck.kind="acp" 只适用于 entry.agent 型服务');
|
|
7313
|
+
}
|
|
7206
7314
|
if (m.lifecycle === null || typeof m.lifecycle !== "object" || Array.isArray(m.lifecycle))
|
|
7207
7315
|
throw new Error("E_MANIFEST: lifecycle required");
|
|
7208
7316
|
if (!LIFECYCLE_TYPES.has(m.lifecycle.type))
|
|
@@ -7804,6 +7912,134 @@ function runHealth(svc, opts) {
|
|
|
7804
7912
|
}
|
|
7805
7913
|
return { judgement, report, stdout, stderr, ms };
|
|
7806
7914
|
}
|
|
7915
|
+
async function runAcpHealth(svc, opts) {
|
|
7916
|
+
const hc = svc.healthcheck;
|
|
7917
|
+
if (!hc || healthcheckKind(hc) !== "acp")
|
|
7918
|
+
throw new Error("E_MANIFEST: runAcpHealth 只处理 healthcheck.kind=acp");
|
|
7919
|
+
const agent = svc.entry.agent;
|
|
7920
|
+
if (!agent)
|
|
7921
|
+
throw new Error("E_MANIFEST: healthcheck.kind=acp 需要 entry.agent");
|
|
7922
|
+
const timeoutMs = (hc.timeout ?? DEFAULT_HEALTH_TIMEOUT) * 1000;
|
|
7923
|
+
const now = opts.now ?? Date.now;
|
|
7924
|
+
const t0 = now();
|
|
7925
|
+
const argv = acpAgentCommand(agent);
|
|
7926
|
+
const env = { ...process.env, ...opts.env ?? {} };
|
|
7927
|
+
const steps = [];
|
|
7928
|
+
const finish = (status, exit) => {
|
|
7929
|
+
const judgement = {
|
|
7930
|
+
...judgeHealth(exit, {}),
|
|
7931
|
+
state: status,
|
|
7932
|
+
exit,
|
|
7933
|
+
timeout: false,
|
|
7934
|
+
detail: steps.filter((x) => x.state !== "ok").map((x) => `${x.step}: ${x.detail}`).join("; ") || `initialize + session/new ok(${agent.host}${agent.profile ? " --profile " + agent.profile : ""})`
|
|
7935
|
+
};
|
|
7936
|
+
return { judgement, report: { status, checks: steps.map((x) => ({ name: x.step, status: x.state, detail: x.detail })) }, ms: now() - t0, steps };
|
|
7937
|
+
};
|
|
7938
|
+
const child = spawn2(argv[0], argv.slice(1), { cwd: opts.dir, env, stdio: ["pipe", "pipe", "pipe"] });
|
|
7939
|
+
const pending = new Map;
|
|
7940
|
+
let buf = "";
|
|
7941
|
+
child.stdout.setEncoding("utf8");
|
|
7942
|
+
child.stdout.on("data", (chunk) => {
|
|
7943
|
+
buf += chunk;
|
|
7944
|
+
for (let i = buf.indexOf(`
|
|
7945
|
+
`);i >= 0; i = buf.indexOf(`
|
|
7946
|
+
`)) {
|
|
7947
|
+
const line = buf.slice(0, i).trim();
|
|
7948
|
+
buf = buf.slice(i + 1);
|
|
7949
|
+
if (!line)
|
|
7950
|
+
continue;
|
|
7951
|
+
let msg;
|
|
7952
|
+
try {
|
|
7953
|
+
msg = JSON.parse(line);
|
|
7954
|
+
} catch {
|
|
7955
|
+
continue;
|
|
7956
|
+
}
|
|
7957
|
+
const waiter = msg && msg.id !== undefined ? pending.get(msg.id) : undefined;
|
|
7958
|
+
if (waiter) {
|
|
7959
|
+
pending.delete(msg.id);
|
|
7960
|
+
waiter(msg);
|
|
7961
|
+
}
|
|
7962
|
+
}
|
|
7963
|
+
});
|
|
7964
|
+
const spawnError = await new Promise((resolve) => {
|
|
7965
|
+
let settled = false;
|
|
7966
|
+
const done = (v) => {
|
|
7967
|
+
if (!settled) {
|
|
7968
|
+
settled = true;
|
|
7969
|
+
resolve(v);
|
|
7970
|
+
}
|
|
7971
|
+
};
|
|
7972
|
+
child.once("error", (e) => done(String(e?.message || e)));
|
|
7973
|
+
child.once("spawn", () => done(null));
|
|
7974
|
+
setTimeout(() => done(null), 50);
|
|
7975
|
+
});
|
|
7976
|
+
if (spawnError)
|
|
7977
|
+
return steps.push({ step: "initialize", state: "degraded", detail: `端点起不来:${spawnError}` }), finish("degraded", 20);
|
|
7978
|
+
const call = (id, method, params) => new Promise((resolve) => {
|
|
7979
|
+
const timer = setTimeout(() => {
|
|
7980
|
+
pending.delete(id);
|
|
7981
|
+
resolve({ timeout: true });
|
|
7982
|
+
}, timeoutMs);
|
|
7983
|
+
pending.set(id, (m) => {
|
|
7984
|
+
clearTimeout(timer);
|
|
7985
|
+
resolve(m);
|
|
7986
|
+
});
|
|
7987
|
+
try {
|
|
7988
|
+
child.stdin.write(JSON.stringify({ jsonrpc: "2.0", id, method, params }) + `
|
|
7989
|
+
`);
|
|
7990
|
+
} catch (e) {
|
|
7991
|
+
clearTimeout(timer);
|
|
7992
|
+
pending.delete(id);
|
|
7993
|
+
resolve({ died: String(e?.message || e) });
|
|
7994
|
+
}
|
|
7995
|
+
});
|
|
7996
|
+
try {
|
|
7997
|
+
const init = await call(1, "initialize", { protocolVersion: 1, clientCapabilities: {} });
|
|
7998
|
+
if (init.timeout)
|
|
7999
|
+
steps.push({ step: "initialize", state: "degraded", detail: "initialize 超时" });
|
|
8000
|
+
else if (init.died)
|
|
8001
|
+
steps.push({ step: "initialize", state: "degraded", detail: `端点已退出:${init.died}` });
|
|
8002
|
+
else if (init.error)
|
|
8003
|
+
steps.push({ step: "initialize", state: "degraded", detail: `initialize 报错:${init.error?.message ?? JSON.stringify(init.error)}` });
|
|
8004
|
+
else if (!init.result)
|
|
8005
|
+
steps.push({ step: "initialize", state: "degraded", detail: "initialize 无 result" });
|
|
8006
|
+
else
|
|
8007
|
+
steps.push({ step: "initialize", state: "ok", detail: "initialize ok" });
|
|
8008
|
+
if (steps[0].state !== "ok")
|
|
8009
|
+
return finish("degraded", 20);
|
|
8010
|
+
const ns = await call(2, "session/new", { cwd: opts.dir, mcpServers: [] });
|
|
8011
|
+
if (ns.timeout)
|
|
8012
|
+
steps.push({ step: "session/new", state: "fail", detail: "session/new 超时" });
|
|
8013
|
+
else if (ns.error)
|
|
8014
|
+
steps.push({ step: "session/new", state: "fail", detail: `session/new 报错:${ns.error?.message ?? JSON.stringify(ns.error)}` });
|
|
8015
|
+
else if (!ns.result?.sessionId)
|
|
8016
|
+
steps.push({ step: "session/new", state: "fail", detail: "session/new 未返回 sessionId" });
|
|
8017
|
+
else
|
|
8018
|
+
steps.push({ step: "session/new", state: "ok", detail: `session ${ns.result.sessionId}` });
|
|
8019
|
+
return steps[1].state === "ok" ? finish("ok", 0) : finish("fail", 21);
|
|
8020
|
+
} finally {
|
|
8021
|
+
try {
|
|
8022
|
+
child.kill("SIGKILL");
|
|
8023
|
+
} catch {}
|
|
8024
|
+
}
|
|
8025
|
+
}
|
|
8026
|
+
function recordHealth(svc, judgement, opts = {}) {
|
|
8027
|
+
const id = svc.id;
|
|
8028
|
+
const gen = opts.gen || generationOf(id);
|
|
8029
|
+
const now = opts.now ?? Math.floor(Date.now() / 1000);
|
|
8030
|
+
const file = opts.stateFile || process.env.DIA_SERVICE_HEALTH_FILE || healthStatePath(id);
|
|
8031
|
+
const record = transition(findLastHealth(readHealthLines(file), id, gen), {
|
|
8032
|
+
service: id,
|
|
8033
|
+
gen,
|
|
8034
|
+
source: opts.source ?? "manual",
|
|
8035
|
+
ts: now,
|
|
8036
|
+
judgement,
|
|
8037
|
+
debounce: svc.healthcheck?.debounce,
|
|
8038
|
+
checks: opts.checks
|
|
8039
|
+
});
|
|
8040
|
+
appendHealthRecord(file, record);
|
|
8041
|
+
return { record, statePath: file };
|
|
8042
|
+
}
|
|
7807
8043
|
function whichTimeout1() {
|
|
7808
8044
|
const probe = spawnSync4("bash", ["-c", "command -v timeout || command -v gtimeout"], { encoding: "utf8" });
|
|
7809
8045
|
const p = String(probe.stdout ?? "").trim().split(`
|
|
@@ -7862,7 +8098,7 @@ function collectManagedFiles(base, hc) {
|
|
|
7862
8098
|
}
|
|
7863
8099
|
function verify4(dir, opts = {}) {
|
|
7864
8100
|
const svc = readServiceManifest(dir);
|
|
7865
|
-
const entry = resolveEntry(svc
|
|
8101
|
+
const entry = resolveEntry(entryCmdOf(svc), dir);
|
|
7866
8102
|
const drift = [];
|
|
7867
8103
|
if (!existsSync28(entry.path))
|
|
7868
8104
|
drift.push({ code: "entry_missing", detail: `entry 不存在:${entry.path}` });
|
|
@@ -7965,7 +8201,7 @@ function add2(opts) {
|
|
|
7965
8201
|
}
|
|
7966
8202
|
if (svc.healthcheck)
|
|
7967
8203
|
healthJobInterval(svc);
|
|
7968
|
-
const entry = resolveEntry(svc
|
|
8204
|
+
const entry = resolveEntry(entryCmdOf(svc), dir);
|
|
7969
8205
|
if (!existsSync28(entry.path))
|
|
7970
8206
|
throw new Error(`E_UNSTABLE_ENTRY: entry 不存在:${entry.path}`);
|
|
7971
8207
|
if (!isExecutableFile(entry.path))
|
|
@@ -7979,7 +8215,7 @@ function add2(opts) {
|
|
|
7979
8215
|
gen,
|
|
7980
8216
|
dryRun: !!opts.dryRun,
|
|
7981
8217
|
backend,
|
|
7982
|
-
entry: { declared: svc
|
|
8218
|
+
entry: { declared: entryCmdOf(svc), kind: entry.kind, path: entry.path, link: null },
|
|
7983
8219
|
jobs: { service: serviceJobName(svc.id), health: svc.healthcheck ? healthJobName(svc.id) : null },
|
|
7984
8220
|
target_mismatch: mismatch
|
|
7985
8221
|
};
|
|
@@ -7988,7 +8224,7 @@ function add2(opts) {
|
|
|
7988
8224
|
rmSync9(dir, { recursive: true, force: true });
|
|
7989
8225
|
return result;
|
|
7990
8226
|
}
|
|
7991
|
-
const backfill = ["--allow-unstable", "--desc", `dia service ${svc.id}: healthcheck 只读判定,稳定入口 ${entryPathOf(svc
|
|
8227
|
+
const backfill = ["--allow-unstable", "--desc", `dia service ${svc.id}: healthcheck 只读判定,稳定入口 ${entryPathOf(entryCmdOf(svc), dir)}`];
|
|
7992
8228
|
const runner = opts.runner ?? defaultRunner({ backfill });
|
|
7993
8229
|
const installed = [];
|
|
7994
8230
|
const fail = (code, msg, rc) => {
|
|
@@ -8005,7 +8241,7 @@ function add2(opts) {
|
|
|
8005
8241
|
entryPath = linkEntry(entry.path);
|
|
8006
8242
|
result.entry.link = entryPath;
|
|
8007
8243
|
result.entry.path = entryPath;
|
|
8008
|
-
} else if (svc.
|
|
8244
|
+
} else if (entryCmdOf(svc).startsWith(entryDir())) {
|
|
8009
8245
|
result.entry.link = entry.path;
|
|
8010
8246
|
}
|
|
8011
8247
|
const jobArgs = (name, cmd) => {
|
|
@@ -8047,7 +8283,7 @@ function add2(opts) {
|
|
|
8047
8283
|
rmSync9(copy, { recursive: true, force: true });
|
|
8048
8284
|
copyTree(dir, copy);
|
|
8049
8285
|
}
|
|
8050
|
-
|
|
8286
|
+
writeFileSync17(serviceLockPath(svc.id), JSON.stringify(lock, null, 1) + `
|
|
8051
8287
|
`);
|
|
8052
8288
|
return result;
|
|
8053
8289
|
}
|
|
@@ -8322,14 +8558,14 @@ usage:
|
|
|
8322
8558
|
dia collab status|enable|disable [--persona ID] # dia-msg 默认能力开关(默认开)
|
|
8323
8559
|
dia whoami [--json] [--name NAME] # 当前会话身份(session id)
|
|
8324
8560
|
dia in [--unread] [--all] [--legacy] [--json] # 会话收件箱(正文内联)
|
|
8325
|
-
dia out --to <session_id> --subject S (--body B|--body-file F) [--kind K] [--reply-to ID]
|
|
8561
|
+
dia out --to <session_id> --subject S (--body B|--body-file F) [--kind K] [--touch p1,p2] [--reply-to ID]
|
|
8326
8562
|
dia ack <id> | dia reply <id> --body B | dia close <id> --reason R | dia show <id>
|
|
8327
8563
|
dia msg send|show|inbox|ack|delivered|deliver [--wait]|reply|needs-info|status|stuck|thread|escalate|agents|migrate
|
|
8328
8564
|
dia agents list|register|unregister|discover
|
|
8329
8565
|
dia msg deliver <agent> <id> [--transport tmux|acp]
|
|
8330
8566
|
dia task add|list|link|status|done [--owner S] [--depends a,b] [--touches p1,p2]
|
|
8331
8567
|
dia graph [--json] [--task t-...] [--tui [--watch] [--no-color]]
|
|
8332
|
-
dia claim <path> [--reason R] [--ttl 4h] [--session S] | dia claims [--all] [--mine] | dia unclaim <path|id> [--force]
|
|
8568
|
+
dia claim <path> [--reason R] [--ttl 4h] [--session S] | dia claims [--all] [--mine] [--gc [--days 7]] | dia unclaim <path|id> [--force]
|
|
8333
8569
|
dia team run <id> --mock | --acp|--dsh --workspace DIR
|
|
8334
8570
|
dia team gate|accept|reject|next
|
|
8335
8571
|
dia team accept <id> [--note ...] [--pr N [--repo R] [--merge]] # --pr:先验 CI,绿才接受/合并
|
|
@@ -8820,7 +9056,7 @@ function spawnDetachedDelivery(args) {
|
|
|
8820
9056
|
const log = join32(dir, `deliver-${id}.log`);
|
|
8821
9057
|
const fd = openSync3(log, "a");
|
|
8822
9058
|
const bin = process.argv[1];
|
|
8823
|
-
const child =
|
|
9059
|
+
const child = spawn3(process.execPath, [bin, "msg", "deliver", ...args, "--wait"], {
|
|
8824
9060
|
detached: true,
|
|
8825
9061
|
stdio: ["ignore", fd, fd],
|
|
8826
9062
|
env: process.env
|
|
@@ -9067,6 +9303,11 @@ async function claimsCli(argv) {
|
|
|
9067
9303
|
return 0;
|
|
9068
9304
|
}
|
|
9069
9305
|
if (sub === "claims") {
|
|
9306
|
+
if (has(args, "--gc")) {
|
|
9307
|
+
const r = compact({ days: Number(flag(args, "--days") || 7) });
|
|
9308
|
+
out(asJson ? JSON.stringify(r, null, 1) : `claims gc: before=${r.before} after=${r.after} dropped=${r.dropped}`);
|
|
9309
|
+
return 0;
|
|
9310
|
+
}
|
|
9070
9311
|
const mine = has(args, "--mine") ? resolveSessionId(session) : undefined;
|
|
9071
9312
|
const rows = list3({ all: has(args, "--all"), mine });
|
|
9072
9313
|
out(asJson ? JSON.stringify(rows, null, 1) : rows.map((c) => `${c.path} ${c.holder} ${c.reason || ""} ${c.expires_at}${isActive(c) ? "" : "\t(expired)"}`).join(`
|
|
@@ -9110,6 +9351,13 @@ async function collabCli(argv) {
|
|
|
9110
9351
|
i++;
|
|
9111
9352
|
}
|
|
9112
9353
|
}
|
|
9354
|
+
const touchPaths = [...flags(args, "--touch"), ...(flag(args, "--touches") || "").split(",")].map((x) => x.trim()).filter(Boolean);
|
|
9355
|
+
if (touchPaths.length)
|
|
9356
|
+
refs.touches = touchPaths.join(",");
|
|
9357
|
+
const refPaths = (m) => [
|
|
9358
|
+
...(m.refs?.touches || "").split(","),
|
|
9359
|
+
...m.refs?.path ? [m.refs.path] : []
|
|
9360
|
+
].map((x) => x.trim()).filter(Boolean);
|
|
9113
9361
|
const view = (m) => ({ ...m, body: bodyOf(m) });
|
|
9114
9362
|
const print = (m) => {
|
|
9115
9363
|
out(`${m.id} [${m.state}] ${m.subject}`);
|
|
@@ -9118,6 +9366,12 @@ async function collabCli(argv) {
|
|
|
9118
9366
|
out(` kind: ${m.kind}${m.requires_reply ? " 需回复" : ""}${m.deadline ? ` deadline=${m.deadline}` : ""} thread=${m.thread_id}#${m.seq}`);
|
|
9119
9367
|
if (m.refs && Object.keys(m.refs).length)
|
|
9120
9368
|
out(` refs: ${Object.entries(m.refs).map(([k, v]) => `${k}=${v}`).join(" ")}`);
|
|
9369
|
+
const touches = refPaths(m);
|
|
9370
|
+
if (touches.length) {
|
|
9371
|
+
const held = list3({}).filter((c) => touches.includes(c.path) && c.holder !== m.to.session_id);
|
|
9372
|
+
for (const c of held)
|
|
9373
|
+
out(` ⚠ claim: ${c.path} 由 ${c.holder} 持有(${c.reason || "no reason"})`);
|
|
9374
|
+
}
|
|
9121
9375
|
const body = bodyOf(m);
|
|
9122
9376
|
if (body) {
|
|
9123
9377
|
const max = 4000;
|
|
@@ -9220,7 +9474,14 @@ async function collabCli(argv) {
|
|
|
9220
9474
|
idempotencyKey: flag(args, "--idempotency-key"),
|
|
9221
9475
|
hops: flag(args, "--hops") ? Number(flag(args, "--hops")) : undefined
|
|
9222
9476
|
});
|
|
9223
|
-
|
|
9477
|
+
const overlaps = list3({}).filter((c) => touchPaths.includes(c.path) && c.holder !== m.from.session_id);
|
|
9478
|
+
if (asJson)
|
|
9479
|
+
out(JSON.stringify(overlaps.length ? { ...m, claim_overlaps: overlaps } : m, null, 1));
|
|
9480
|
+
else {
|
|
9481
|
+
out(`${m.id} ${m.thread_id}#${m.seq} ${m.from.session_id} → ${m.to.session_id} ${m.kind} ${m.subject}`);
|
|
9482
|
+
for (const c of overlaps)
|
|
9483
|
+
out(` ⚠ claim overlap: ${c.path} 由 ${c.holder} 持有(${c.reason || "no reason"})`);
|
|
9484
|
+
}
|
|
9224
9485
|
return 0;
|
|
9225
9486
|
}
|
|
9226
9487
|
if (sub === "ack") {
|
|
@@ -9814,6 +10075,18 @@ async function serviceCli(argv) {
|
|
|
9814
10075
|
}
|
|
9815
10076
|
const fromWrapper = has(args, "--from-wrapper");
|
|
9816
10077
|
const ex = flag(args, "--exit");
|
|
10078
|
+
const dir = serviceDir(id);
|
|
10079
|
+
const svc = readServiceManifest(dir);
|
|
10080
|
+
if (healthcheckKind(svc.healthcheck) === "acp" && !fromWrapper) {
|
|
10081
|
+
const run = await runAcpHealth(svc, { dir });
|
|
10082
|
+
const rec = recordHealth(svc, run.judgement, { gen: flag(args, "--gen"), stateFile: flag(args, "--state-file") });
|
|
10083
|
+
const code = run.judgement.state === "ok" ? 0 : run.judgement.state === "degraded" ? 30 : 1;
|
|
10084
|
+
if (asJson)
|
|
10085
|
+
out(JSON.stringify({ id, verdict: run.judgement.state, shown: rec.record.state, exit: run.judgement.exit, detail: run.judgement.detail, steps: run.steps }, null, 1));
|
|
10086
|
+
else
|
|
10087
|
+
out(`${id}: ${run.judgement.state}(shown=${rec.record.state}${run.judgement.detail ? ", " + run.judgement.detail : ""})`);
|
|
10088
|
+
return code;
|
|
10089
|
+
}
|
|
9817
10090
|
const r = runServiceHealth(id, {
|
|
9818
10091
|
wrapperExit: fromWrapper ? Number(ex ?? "1") : undefined,
|
|
9819
10092
|
gen: flag(args, "--gen"),
|