harness-wingman 0.4.1 → 0.4.3
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/apps/cli/dist/main.js +721 -195
- package/apps/panel/dist/assets/index-AwMHEwTe.css +1 -0
- package/apps/panel/dist/assets/index-CccFfpXM.js +212 -0
- package/apps/panel/dist/index.html +2 -2
- package/package.json +2 -2
- package/apps/panel/dist/assets/index-Bi0Z36tF.js +0 -209
- package/apps/panel/dist/assets/index-DCNNABGS.css +0 -1
package/apps/cli/dist/main.js
CHANGED
|
@@ -267,13 +267,16 @@ import { join as join5, parse, resolve } from "node:path";
|
|
|
267
267
|
function newWalkStats(entryBudget = MAX_WALK_ENTRIES) {
|
|
268
268
|
return { unknownErrors: 0, truncated: false, entriesLeft: entryBudget };
|
|
269
269
|
}
|
|
270
|
+
function recordWalkError(error, stats) {
|
|
271
|
+
const code = error.code;
|
|
272
|
+
if (stats && (code === void 0 || !TOLERATED_CODES.has(code)))
|
|
273
|
+
stats.unknownErrors += 1;
|
|
274
|
+
}
|
|
270
275
|
function safeReaddir(dir, stats) {
|
|
271
276
|
try {
|
|
272
277
|
return readdirSync(dir, { withFileTypes: true });
|
|
273
278
|
} catch (e) {
|
|
274
|
-
|
|
275
|
-
if (stats && (code === void 0 || !TOLERATED_CODES.has(code)))
|
|
276
|
-
stats.unknownErrors += 1;
|
|
279
|
+
recordWalkError(e, stats);
|
|
277
280
|
return [];
|
|
278
281
|
}
|
|
279
282
|
}
|
|
@@ -325,26 +328,38 @@ var init_walk = __esm({
|
|
|
325
328
|
import { existsSync as existsSync2, readFileSync as readFileSync3, statSync } from "node:fs";
|
|
326
329
|
import { homedir as homedir3 } from "node:os";
|
|
327
330
|
import { join as join6 } from "node:path";
|
|
328
|
-
function probeVersion(homeDir) {
|
|
331
|
+
function probeVersion(homeDir, stats = newWalkStats()) {
|
|
329
332
|
const root = join6(homeDir, ".claude", "projects");
|
|
330
333
|
let latest;
|
|
331
|
-
for (const entry of safeReaddir(root)) {
|
|
334
|
+
for (const entry of safeReaddir(root, stats)) {
|
|
332
335
|
if (!entry.isDirectory())
|
|
333
336
|
continue;
|
|
334
337
|
const dir = join6(root, entry.name);
|
|
335
|
-
for (const sub of safeReaddir(dir)) {
|
|
338
|
+
for (const sub of safeReaddir(dir, stats)) {
|
|
336
339
|
const name = sub.name;
|
|
337
340
|
if (!name.endsWith(".jsonl"))
|
|
338
341
|
continue;
|
|
339
342
|
const file = join6(dir, name);
|
|
340
|
-
|
|
343
|
+
let mtimeMs;
|
|
344
|
+
try {
|
|
345
|
+
mtimeMs = statSync(file).mtimeMs;
|
|
346
|
+
} catch (error) {
|
|
347
|
+
recordWalkError(error, stats);
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
341
350
|
if (!latest || mtimeMs > latest.mtimeMs)
|
|
342
351
|
latest = { file, mtimeMs };
|
|
343
352
|
}
|
|
344
353
|
}
|
|
345
354
|
if (!latest)
|
|
346
355
|
return void 0;
|
|
347
|
-
|
|
356
|
+
let lines;
|
|
357
|
+
try {
|
|
358
|
+
lines = readFileSync3(latest.file, "utf8").split("\n");
|
|
359
|
+
} catch (error) {
|
|
360
|
+
recordWalkError(error, stats);
|
|
361
|
+
return void 0;
|
|
362
|
+
}
|
|
348
363
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
349
364
|
const line = lines[i];
|
|
350
365
|
if (!line)
|
|
@@ -439,36 +454,46 @@ function buildBase() {
|
|
|
439
454
|
}
|
|
440
455
|
];
|
|
441
456
|
}
|
|
442
|
-
function ruleItem(file, source) {
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
457
|
+
function ruleItem(file, source, stats) {
|
|
458
|
+
try {
|
|
459
|
+
return {
|
|
460
|
+
id: "",
|
|
461
|
+
slot: "rules",
|
|
462
|
+
nativeName: basename(file),
|
|
463
|
+
nativePath: file,
|
|
464
|
+
tokens: estimateTokens(statSync2(file).size),
|
|
465
|
+
residency: "resident",
|
|
466
|
+
toggleable: false,
|
|
467
|
+
enabled: true,
|
|
468
|
+
source
|
|
469
|
+
};
|
|
470
|
+
} catch (error) {
|
|
471
|
+
recordWalkError(error, stats);
|
|
472
|
+
return void 0;
|
|
473
|
+
}
|
|
454
474
|
}
|
|
455
|
-
function buildRules(homeDir, projectDir) {
|
|
475
|
+
function buildRules(homeDir, projectDir, stats) {
|
|
456
476
|
const items = [];
|
|
477
|
+
const append = (file, source) => {
|
|
478
|
+
const item = ruleItem(file, source, stats);
|
|
479
|
+
if (item !== void 0)
|
|
480
|
+
items.push(item);
|
|
481
|
+
};
|
|
457
482
|
const userClaudeMd = join7(homeDir, ".claude", "CLAUDE.md");
|
|
458
483
|
if (existsSync3(userClaudeMd))
|
|
459
|
-
|
|
460
|
-
for (const f of listMdFiles(join7(homeDir, ".claude", "rules")))
|
|
461
|
-
|
|
484
|
+
append(userClaudeMd, "user");
|
|
485
|
+
for (const f of listMdFiles(join7(homeDir, ".claude", "rules"), stats))
|
|
486
|
+
append(f, "user");
|
|
462
487
|
if (isBareProjectDir(projectDir, homeDir)) {
|
|
463
488
|
const direct = join7(projectDir, "CLAUDE.md");
|
|
464
489
|
if (existsSync3(direct))
|
|
465
|
-
|
|
490
|
+
append(direct, "project");
|
|
466
491
|
} else {
|
|
467
|
-
for (const f of walkFiles(projectDir, "CLAUDE.md"))
|
|
468
|
-
|
|
492
|
+
for (const f of walkFiles(projectDir, "CLAUDE.md", stats))
|
|
493
|
+
append(f, "project");
|
|
469
494
|
}
|
|
470
|
-
for (const f of listMdFiles(join7(projectDir, ".claude", "rules"))) {
|
|
471
|
-
|
|
495
|
+
for (const f of listMdFiles(join7(projectDir, ".claude", "rules"), stats)) {
|
|
496
|
+
append(f, "project");
|
|
472
497
|
}
|
|
473
498
|
return items;
|
|
474
499
|
}
|
|
@@ -489,7 +514,7 @@ function parseSkillFrontmatter(content) {
|
|
|
489
514
|
}
|
|
490
515
|
return out;
|
|
491
516
|
}
|
|
492
|
-
function buildSkills(homeDir, projectDir, overrides) {
|
|
517
|
+
function buildSkills(homeDir, projectDir, overrides, stats) {
|
|
493
518
|
const userRoot = join7(homeDir, ".claude", "skills");
|
|
494
519
|
const syncedRoot = join7(userRoot, "synced");
|
|
495
520
|
const roots = [
|
|
@@ -502,24 +527,29 @@ function buildSkills(homeDir, projectDir, overrides) {
|
|
|
502
527
|
];
|
|
503
528
|
const items = [];
|
|
504
529
|
for (const { root, source, exclude } of roots) {
|
|
505
|
-
for (const skillFile of walkFiles(root, "SKILL.md")) {
|
|
530
|
+
for (const skillFile of walkFiles(root, "SKILL.md", stats)) {
|
|
506
531
|
if (exclude && (skillFile === exclude || skillFile.startsWith(exclude + sep)))
|
|
507
532
|
continue;
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
533
|
+
try {
|
|
534
|
+
const fm = parseSkillFrontmatter(readFileSync4(skillFile, "utf8"));
|
|
535
|
+
const name = fm.name ?? basename(dirname3(skillFile));
|
|
536
|
+
const size = statSync2(skillFile).size;
|
|
537
|
+
items.push({
|
|
538
|
+
id: "",
|
|
539
|
+
slot: "skills",
|
|
540
|
+
nativeName: name,
|
|
541
|
+
nativePath: skillFile,
|
|
542
|
+
// 两段式成本(PLAN §4a):常驻 = frontmatter description 字节;触发后 = SKILL.md 全文字节
|
|
543
|
+
tokens: estimateTokens(Buffer2.byteLength(fm.description ?? "", "utf8")),
|
|
544
|
+
onTriggerTokens: estimateTokens(size),
|
|
545
|
+
residency: "on_trigger",
|
|
546
|
+
toggleable: true,
|
|
547
|
+
enabled: overrides[name] !== "off",
|
|
548
|
+
source
|
|
549
|
+
});
|
|
550
|
+
} catch (error) {
|
|
551
|
+
recordWalkError(error, stats);
|
|
552
|
+
}
|
|
523
553
|
}
|
|
524
554
|
}
|
|
525
555
|
return items;
|
|
@@ -595,9 +625,16 @@ function buildGates(chain) {
|
|
|
595
625
|
function projectSlug(projectDir) {
|
|
596
626
|
return resolve2(projectDir).replace(/[^a-zA-Z0-9]/g, "-");
|
|
597
627
|
}
|
|
598
|
-
function lastContextTotal(file) {
|
|
628
|
+
function lastContextTotal(file, stats) {
|
|
599
629
|
let total;
|
|
600
|
-
|
|
630
|
+
let text;
|
|
631
|
+
try {
|
|
632
|
+
text = readFileSync4(file, "utf8");
|
|
633
|
+
} catch (error) {
|
|
634
|
+
recordWalkError(error, stats);
|
|
635
|
+
return void 0;
|
|
636
|
+
}
|
|
637
|
+
for (const line of text.split("\n")) {
|
|
601
638
|
if (!line)
|
|
602
639
|
continue;
|
|
603
640
|
let row;
|
|
@@ -619,12 +656,12 @@ function lastContextTotal(file) {
|
|
|
619
656
|
}
|
|
620
657
|
return total;
|
|
621
658
|
}
|
|
622
|
-
function buildHistory(homeDir, projectDir) {
|
|
659
|
+
function buildHistory(homeDir, projectDir, stats) {
|
|
623
660
|
const dir = join7(homeDir, ".claude", "projects", projectSlug(projectDir));
|
|
624
661
|
const items = [];
|
|
625
|
-
for (const name of safeReaddir(dir).map((e) => e.name).filter((n) => n.endsWith(".jsonl")).sort()) {
|
|
662
|
+
for (const name of safeReaddir(dir, stats).map((e) => e.name).filter((n) => n.endsWith(".jsonl")).sort()) {
|
|
626
663
|
const file = join7(dir, name);
|
|
627
|
-
const total = lastContextTotal(file);
|
|
664
|
+
const total = lastContextTotal(file, stats);
|
|
628
665
|
if (total === void 0)
|
|
629
666
|
continue;
|
|
630
667
|
items.push({
|
|
@@ -641,11 +678,12 @@ function buildHistory(homeDir, projectDir) {
|
|
|
641
678
|
return items;
|
|
642
679
|
}
|
|
643
680
|
function detectLiveWiring(chain) {
|
|
681
|
+
const wired = /* @__PURE__ */ new Set();
|
|
644
682
|
for (const s of chain) {
|
|
645
683
|
const hooks = asRecord(s.data.hooks);
|
|
646
684
|
if (!hooks)
|
|
647
685
|
continue;
|
|
648
|
-
for (const entries of Object.
|
|
686
|
+
for (const [event, entries] of Object.entries(hooks)) {
|
|
649
687
|
if (!Array.isArray(entries))
|
|
650
688
|
continue;
|
|
651
689
|
for (const entry of entries) {
|
|
@@ -655,13 +693,22 @@ function detectLiveWiring(chain) {
|
|
|
655
693
|
for (const h of handlers) {
|
|
656
694
|
const handler = asRecord(h);
|
|
657
695
|
if (handler?.type === "http" && typeof handler.url === "string" && handler.url.includes("/hook?harness=cc")) {
|
|
658
|
-
|
|
696
|
+
wired.add(event);
|
|
659
697
|
}
|
|
660
698
|
}
|
|
661
699
|
}
|
|
662
700
|
}
|
|
663
701
|
}
|
|
664
|
-
return
|
|
702
|
+
return [...wired];
|
|
703
|
+
}
|
|
704
|
+
function feedbackSignalsCc(options) {
|
|
705
|
+
const homeDir = options?.homeDir ?? homedir4();
|
|
706
|
+
const projectDir = options?.projectDir ?? process.cwd();
|
|
707
|
+
const wiredEvents = detectLiveWiring(readSettingsChain(homeDir, projectDir));
|
|
708
|
+
return {
|
|
709
|
+
wiredEvents,
|
|
710
|
+
feedback: wiredEvents.includes("PermissionRequest") || wiredEvents.includes("Notification") ? "available" : "not_wired"
|
|
711
|
+
};
|
|
665
712
|
}
|
|
666
713
|
function assignIds(items) {
|
|
667
714
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -689,17 +736,18 @@ function assignIds(items) {
|
|
|
689
736
|
async function scanCc(options) {
|
|
690
737
|
const homeDir = options?.homeDir ?? homedir4();
|
|
691
738
|
const projectDir = options?.projectDir ?? process.cwd();
|
|
739
|
+
const stats = newWalkStats();
|
|
692
740
|
const chain = readSettingsChain(homeDir, projectDir);
|
|
693
741
|
const slots = {
|
|
694
742
|
base: buildBase(),
|
|
695
|
-
rules: buildRules(homeDir, projectDir),
|
|
696
|
-
skills: buildSkills(homeDir, projectDir, mergedSkillOverrides(chain)),
|
|
743
|
+
rules: buildRules(homeDir, projectDir, stats),
|
|
744
|
+
skills: buildSkills(homeDir, projectDir, mergedSkillOverrides(chain), stats),
|
|
697
745
|
tools: buildTools(homeDir, projectDir),
|
|
698
746
|
gates: buildGates(chain),
|
|
699
|
-
history: buildHistory(homeDir, projectDir)
|
|
747
|
+
history: buildHistory(homeDir, projectDir, stats)
|
|
700
748
|
};
|
|
701
749
|
assignIds(Object.values(slots).flat());
|
|
702
|
-
const version = probeVersion(homeDir);
|
|
750
|
+
const version = probeVersion(homeDir, stats);
|
|
703
751
|
return {
|
|
704
752
|
harness: "cc",
|
|
705
753
|
...version !== void 0 ? { harnessVersion: version } : {},
|
|
@@ -708,7 +756,7 @@ async function scanCc(options) {
|
|
|
708
756
|
capabilities: {
|
|
709
757
|
// M3:可逆开关已落地(planAction/applyAction,actions.ts);open_editor /
|
|
710
758
|
// scan_references 为即时只读(合同注释),分别由 nativePath 与 plan.references 承载
|
|
711
|
-
live: detectLiveWiring(chain),
|
|
759
|
+
live: detectLiveWiring(chain).length > 0 ? "push" : "none",
|
|
712
760
|
actions: ["toggle", "open_editor", "scan_references"]
|
|
713
761
|
}
|
|
714
762
|
};
|
|
@@ -755,6 +803,13 @@ function backupDirPath(homeDir) {
|
|
|
755
803
|
dir = join8(root, `${ts}-${n}`);
|
|
756
804
|
return dir;
|
|
757
805
|
}
|
|
806
|
+
function decodeUtf8Strict(buffer, file) {
|
|
807
|
+
const text = buffer.toString("utf8");
|
|
808
|
+
if (!buffer.equals(Buffer.from(text, "utf8"))) {
|
|
809
|
+
throw new Error(`\u62D2\u5199:${file} \u4E0D\u662F UTF-8 \u6587\u672C,\u672A\u77E5\u683C\u5F0F\u4E0D\u731C`);
|
|
810
|
+
}
|
|
811
|
+
return text;
|
|
812
|
+
}
|
|
758
813
|
function readSettingsSnapshots(homeDir, projectDir) {
|
|
759
814
|
const files = [
|
|
760
815
|
join8(homeDir, ".claude", "settings.json"),
|
|
@@ -765,7 +820,7 @@ function readSettingsSnapshots(homeDir, projectDir) {
|
|
|
765
820
|
for (const file of files) {
|
|
766
821
|
if (!existsSync4(file))
|
|
767
822
|
continue;
|
|
768
|
-
const raw = readFileSync5(file,
|
|
823
|
+
const raw = decodeUtf8Strict(readFileSync5(file), file);
|
|
769
824
|
let parsed;
|
|
770
825
|
try {
|
|
771
826
|
parsed = JSON.parse(raw);
|
|
@@ -1062,6 +1117,7 @@ async function applyActionCc(plan, _options) {
|
|
|
1062
1117
|
const shapeError = validatePlanShape(plan);
|
|
1063
1118
|
if (shapeError !== void 0)
|
|
1064
1119
|
return { ok: false, error: shapeError };
|
|
1120
|
+
const currentByFile = /* @__PURE__ */ new Map();
|
|
1065
1121
|
for (const w of plan.writes) {
|
|
1066
1122
|
if (w.kind === "create") {
|
|
1067
1123
|
if (existsSync4(w.file)) {
|
|
@@ -1071,9 +1127,11 @@ async function applyActionCc(plan, _options) {
|
|
|
1071
1127
|
if (!existsSync4(w.file)) {
|
|
1072
1128
|
return { ok: false, error: `\u62D2\u7EDD\u6267\u884C:${w.file} \u5DF2\u4E0D\u5B58\u5728(plan \u540E\u88AB\u6539\u52A8),\u8BF7\u91CD\u65B0 plan` };
|
|
1073
1129
|
}
|
|
1074
|
-
|
|
1130
|
+
const currentRaw = readFileSync5(w.file);
|
|
1131
|
+
if (!currentRaw.equals(Buffer.from(w.before, "utf8"))) {
|
|
1075
1132
|
return { ok: false, error: `\u62D2\u7EDD\u6267\u884C:${w.file} \u5728 plan \u540E\u88AB\u6539\u8FC7,\u8BF7\u91CD\u65B0 plan` };
|
|
1076
1133
|
}
|
|
1134
|
+
currentByFile.set(w.file, currentRaw);
|
|
1077
1135
|
}
|
|
1078
1136
|
}
|
|
1079
1137
|
const toBackup = plan.writes.filter((w) => w.kind !== "create");
|
|
@@ -1083,7 +1141,7 @@ async function applyActionCc(plan, _options) {
|
|
|
1083
1141
|
target: w.file
|
|
1084
1142
|
}));
|
|
1085
1143
|
toBackup.forEach((w, i) => {
|
|
1086
|
-
writeFileSync3(join8(plan.backupDir, `${i}-${basename2(w.file)}`), w.
|
|
1144
|
+
writeFileSync3(join8(plan.backupDir, `${i}-${basename2(w.file)}`), currentByFile.get(w.file));
|
|
1087
1145
|
});
|
|
1088
1146
|
writeFileSync3(join8(plan.backupDir, "manifest.json"), serialize({
|
|
1089
1147
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -1391,7 +1449,16 @@ var WIRED_EVENTS, SIGNATURE;
|
|
|
1391
1449
|
var init_hooks = __esm({
|
|
1392
1450
|
"packages/adapter-cc/dist/hooks.js"() {
|
|
1393
1451
|
"use strict";
|
|
1394
|
-
WIRED_EVENTS = [
|
|
1452
|
+
WIRED_EVENTS = [
|
|
1453
|
+
"SessionStart",
|
|
1454
|
+
"UserPromptSubmit",
|
|
1455
|
+
"PreToolUse",
|
|
1456
|
+
"PostToolUse",
|
|
1457
|
+
"Stop",
|
|
1458
|
+
"Notification",
|
|
1459
|
+
"PermissionRequest",
|
|
1460
|
+
"SessionEnd"
|
|
1461
|
+
];
|
|
1395
1462
|
SIGNATURE = "/hook?harness=cc";
|
|
1396
1463
|
}
|
|
1397
1464
|
});
|
|
@@ -1399,7 +1466,9 @@ var init_hooks = __esm({
|
|
|
1399
1466
|
// packages/adapter-cc/dist/index.js
|
|
1400
1467
|
var dist_exports = {};
|
|
1401
1468
|
__export(dist_exports, {
|
|
1402
|
-
|
|
1469
|
+
WIRED_EVENTS: () => WIRED_EVENTS,
|
|
1470
|
+
createAdapter: () => createAdapter,
|
|
1471
|
+
feedbackSignals: () => feedbackSignalsCc
|
|
1403
1472
|
});
|
|
1404
1473
|
function createAdapter(_options) {
|
|
1405
1474
|
return {
|
|
@@ -1419,6 +1488,8 @@ var init_dist2 = __esm({
|
|
|
1419
1488
|
init_detect();
|
|
1420
1489
|
init_hooks();
|
|
1421
1490
|
init_scan();
|
|
1491
|
+
init_hooks();
|
|
1492
|
+
init_scan();
|
|
1422
1493
|
}
|
|
1423
1494
|
});
|
|
1424
1495
|
|
|
@@ -2853,16 +2924,18 @@ async function installHooks(port, options) {
|
|
|
2853
2924
|
const configPath = join12(paths.codexHome, "config.toml");
|
|
2854
2925
|
const raw = existsSync7(configPath) ? readFileSync8(configPath) : void 0;
|
|
2855
2926
|
const text = raw?.toString("utf8") ?? "";
|
|
2856
|
-
const { remainder, block } = splitManagedBlock(text, configPath);
|
|
2857
|
-
const
|
|
2858
|
-
|
|
2927
|
+
const { before, after, remainder, block } = splitManagedBlock(text, configPath);
|
|
2928
|
+
const lineEnding = preferredLineEnding(text);
|
|
2929
|
+
const newBlock = buildManagedBlock(scriptPath).replaceAll("\n", lineEnding);
|
|
2930
|
+
const knownBlock = block === void 0 ? void 0 : inspectManagedBlock(block, scriptPath);
|
|
2931
|
+
if (block !== void 0 && knownBlock === void 0) {
|
|
2859
2932
|
throw new Error(`Wingman Codex hook \u53D7\u7BA1\u5757\u5DF2\u88AB\u4FEE\u6539\u6216\u6B8B\u7F3A(${configPath}),\u62D2\u7EDD\u8C0E\u62A5\u63A5\u7EBF\u6210\u529F;\u8BF7\u5148\u4ECE <wingman\u6839>/backups/ \u6062\u590D\u5B8C\u6574\u5757,\u6216\u4EBA\u5DE5\u6838\u5BF9\u540E\u79FB\u9664\u8BE5\u53D7\u7BA1\u5757\u518D\u91CD\u65B0\u5B89\u88C5\u5E76\u4FE1\u4EFB`);
|
|
2860
2933
|
}
|
|
2861
2934
|
mkdirSync5(root, { recursive: true });
|
|
2862
2935
|
writeFileSync6(scriptPath, FORWARD_SCRIPT);
|
|
2863
2936
|
const statePath2 = hooksStatePath(root);
|
|
2864
2937
|
const prior = readState2(statePath2);
|
|
2865
|
-
if (
|
|
2938
|
+
if (knownBlock !== void 0 && knownBlock.state === void 0) {
|
|
2866
2939
|
if (prior?.port !== port || prior.configPath !== configPath || prior.forwardScriptPath !== scriptPath) {
|
|
2867
2940
|
writeState2(statePath2, {
|
|
2868
2941
|
...prior,
|
|
@@ -2875,8 +2948,22 @@ async function installHooks(port, options) {
|
|
|
2875
2948
|
}
|
|
2876
2949
|
return;
|
|
2877
2950
|
}
|
|
2878
|
-
assertWritable(remainder, configPath);
|
|
2951
|
+
const hasStateOutsideBlock = assertWritable(remainder, configPath);
|
|
2879
2952
|
const backupDir = raw === void 0 ? void 0 : backupFile(root, configPath, raw);
|
|
2953
|
+
if (knownBlock !== void 0) {
|
|
2954
|
+
writeState2(statePath2, {
|
|
2955
|
+
...prior,
|
|
2956
|
+
version: 1,
|
|
2957
|
+
port,
|
|
2958
|
+
configPath,
|
|
2959
|
+
forwardScriptPath: scriptPath,
|
|
2960
|
+
installedAt: prior?.installedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
2961
|
+
...backupDir !== void 0 ? { backupDir } : {}
|
|
2962
|
+
});
|
|
2963
|
+
const state = hasStateOutsideBlock ? "" : knownBlock.state;
|
|
2964
|
+
writeFileSync6(configPath, before + knownBlock.managedBlock + state + after);
|
|
2965
|
+
return;
|
|
2966
|
+
}
|
|
2880
2967
|
const original = prior?.original ?? (raw === void 0 ? { existed: false } : { existed: true, base64: raw.toString("base64") });
|
|
2881
2968
|
writeState2(statePath2, {
|
|
2882
2969
|
version: 1,
|
|
@@ -2889,7 +2976,7 @@ async function installHooks(port, options) {
|
|
|
2889
2976
|
});
|
|
2890
2977
|
let base = remainder;
|
|
2891
2978
|
if (base !== "" && !base.endsWith("\n"))
|
|
2892
|
-
base +=
|
|
2979
|
+
base += lineEnding;
|
|
2893
2980
|
mkdirSync5(paths.codexHome, { recursive: true });
|
|
2894
2981
|
writeFileSync6(configPath, base + newBlock);
|
|
2895
2982
|
}
|
|
@@ -2903,13 +2990,15 @@ async function uninstallHooks(options) {
|
|
|
2903
2990
|
const raw = existsSync7(configPath) ? readFileSync8(configPath) : void 0;
|
|
2904
2991
|
if (raw !== void 0) {
|
|
2905
2992
|
const text = raw.toString("utf8");
|
|
2906
|
-
const { remainder, block } = splitManagedBlock(text, configPath);
|
|
2993
|
+
const { before, after, remainder, block } = splitManagedBlock(text, configPath);
|
|
2907
2994
|
if (block !== void 0) {
|
|
2908
|
-
|
|
2995
|
+
const knownBlock = inspectManagedBlock(block, scriptPath);
|
|
2996
|
+
if (knownBlock === void 0) {
|
|
2909
2997
|
throw new Error(`Wingman Codex hook \u53D7\u7BA1\u5757\u5DF2\u88AB\u4FEE\u6539\u6216\u6B8B\u7F3A(${configPath}),\u62D2\u7EDD\u81EA\u52A8\u5220\u9664;\u8BF7\u5148\u4ECE <wingman\u6839>/backups/ \u6062\u590D\u5B8C\u6574\u5757,\u6216\u4EBA\u5DE5\u6838\u5BF9\u540E\u79FB\u9664\u8BE5\u53D7\u7BA1\u5757`);
|
|
2910
2998
|
}
|
|
2911
2999
|
backupFile(root, configPath, raw);
|
|
2912
|
-
const
|
|
3000
|
+
const preservedState = hasHooksState(remainder) ? "" : knownBlock.state ?? "";
|
|
3001
|
+
const restored = restoreTarget(before + preservedState + after, state);
|
|
2913
3002
|
if (restored === null)
|
|
2914
3003
|
rmSync3(configPath);
|
|
2915
3004
|
else
|
|
@@ -2925,11 +3014,36 @@ function hasManagedHooks(paths) {
|
|
|
2925
3014
|
return false;
|
|
2926
3015
|
try {
|
|
2927
3016
|
const { block } = splitManagedBlock(readFileSync8(configPath, "utf8"), configPath);
|
|
2928
|
-
return block !== void 0 &&
|
|
3017
|
+
return block !== void 0 && inspectManagedBlock(block, join12(wingmanStateRoot(paths), FORWARD_SCRIPT_NAME)) !== void 0;
|
|
2929
3018
|
} catch {
|
|
2930
3019
|
return false;
|
|
2931
3020
|
}
|
|
2932
3021
|
}
|
|
3022
|
+
function feedbackSignalsCodex(options) {
|
|
3023
|
+
const paths = resolvePaths(options);
|
|
3024
|
+
const configPath = join12(paths.codexHome, "config.toml");
|
|
3025
|
+
const wiredEvents = [];
|
|
3026
|
+
if (existsSync7(configPath)) {
|
|
3027
|
+
try {
|
|
3028
|
+
const { block } = splitManagedBlock(readFileSync8(configPath, "utf8"), configPath);
|
|
3029
|
+
if (block !== void 0) {
|
|
3030
|
+
const knownBlock = inspectManagedBlock(block, join12(wingmanStateRoot(paths), FORWARD_SCRIPT_NAME));
|
|
3031
|
+
const hooks = knownBlock === void 0 ? void 0 : parse2(normalizeLineEndings(knownBlock.managedBlock)).hooks;
|
|
3032
|
+
if (isTable(hooks)) {
|
|
3033
|
+
for (const [event, groups] of Object.entries(hooks)) {
|
|
3034
|
+
if (Array.isArray(groups))
|
|
3035
|
+
wiredEvents.push(event);
|
|
3036
|
+
}
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
} catch {
|
|
3040
|
+
}
|
|
3041
|
+
}
|
|
3042
|
+
return {
|
|
3043
|
+
wiredEvents,
|
|
3044
|
+
feedback: wiredEvents.includes("PermissionRequest") ? "available" : "not_wired"
|
|
3045
|
+
};
|
|
3046
|
+
}
|
|
2933
3047
|
function restoreTarget(remainder, state) {
|
|
2934
3048
|
const original = state?.original;
|
|
2935
3049
|
if (original === void 0)
|
|
@@ -2938,9 +3052,16 @@ function restoreTarget(remainder, state) {
|
|
|
2938
3052
|
return remainder.trim() === "" ? null : remainder;
|
|
2939
3053
|
}
|
|
2940
3054
|
const originalBytes = Buffer.from(original.base64 ?? "", "base64");
|
|
3055
|
+
const originalText = originalBytes.toString("utf8");
|
|
2941
3056
|
try {
|
|
2942
|
-
if (deepEqual3(parse2(remainder), parse2(
|
|
2943
|
-
|
|
3057
|
+
if (deepEqual3(parse2(remainder), parse2(originalText))) {
|
|
3058
|
+
const separatorOnly = remainder === `${originalText}
|
|
3059
|
+
` || remainder === `${originalText}\r
|
|
3060
|
+
`;
|
|
3061
|
+
if (separatorOnly || lineEndingStyle(remainder) === lineEndingStyle(originalText)) {
|
|
3062
|
+
return originalBytes;
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
2944
3065
|
} catch {
|
|
2945
3066
|
}
|
|
2946
3067
|
return remainder;
|
|
@@ -2958,43 +3079,78 @@ function buildManagedBlockForCommand(rawCommand) {
|
|
|
2958
3079
|
return `${lines.join("\n")}
|
|
2959
3080
|
`;
|
|
2960
3081
|
}
|
|
2961
|
-
function
|
|
2962
|
-
|
|
2963
|
-
|
|
3082
|
+
function inspectManagedBlock(block, scriptPath) {
|
|
3083
|
+
const inspected = separateCodexHooksState(block);
|
|
3084
|
+
const normalized = normalizeLineEndings(inspected.managedBlock);
|
|
3085
|
+
if (normalized === buildManagedBlock(scriptPath))
|
|
3086
|
+
return inspected;
|
|
2964
3087
|
let table;
|
|
2965
3088
|
try {
|
|
2966
|
-
table = parse2(
|
|
3089
|
+
table = parse2(normalized);
|
|
2967
3090
|
} catch {
|
|
2968
|
-
return
|
|
3091
|
+
return void 0;
|
|
2969
3092
|
}
|
|
2970
3093
|
const hooks = table.hooks;
|
|
2971
3094
|
if (!isTable(hooks))
|
|
2972
|
-
return
|
|
3095
|
+
return void 0;
|
|
2973
3096
|
const first = hooks[HOOK_EVENTS[0]];
|
|
2974
3097
|
if (!Array.isArray(first) || first.length !== 1 || !isTable(first[0]))
|
|
2975
|
-
return
|
|
3098
|
+
return void 0;
|
|
2976
3099
|
const handlers = first[0].hooks;
|
|
2977
3100
|
if (!Array.isArray(handlers) || handlers.length !== 1 || !isTable(handlers[0]))
|
|
2978
|
-
return
|
|
3101
|
+
return void 0;
|
|
2979
3102
|
const command = handlers[0].command;
|
|
2980
3103
|
if (typeof command !== "string")
|
|
2981
|
-
return
|
|
3104
|
+
return void 0;
|
|
2982
3105
|
const prefix = `node "${scriptPath}" `;
|
|
2983
3106
|
if (!command.startsWith(prefix))
|
|
2984
|
-
return
|
|
3107
|
+
return void 0;
|
|
2985
3108
|
const legacyPortText = command.slice(prefix.length);
|
|
2986
3109
|
if (!/^[1-9]\d{0,4}$/.test(legacyPortText))
|
|
2987
|
-
return
|
|
3110
|
+
return void 0;
|
|
2988
3111
|
const legacyPort = Number(legacyPortText);
|
|
2989
3112
|
if (legacyPort > 65535 || String(legacyPort) !== legacyPortText)
|
|
2990
|
-
return
|
|
2991
|
-
return
|
|
3113
|
+
return void 0;
|
|
3114
|
+
return normalized === buildManagedBlockForCommand(command) ? inspected : void 0;
|
|
3115
|
+
}
|
|
3116
|
+
function separateCodexHooksState(block) {
|
|
3117
|
+
const stateHeader = /^[ \t]*\[[ \t]*hooks[ \t]*\.[ \t]*state(?:[ \t]*|[ \t]*\..+)\][ \t]*(?:#.*)?\r?$/m;
|
|
3118
|
+
const match = stateHeader.exec(block);
|
|
3119
|
+
if (match === null)
|
|
3120
|
+
return { managedBlock: block };
|
|
3121
|
+
let stateStart = match.index;
|
|
3122
|
+
while (stateStart > 0 && block[stateStart - 1] === "\n") {
|
|
3123
|
+
const precedingNewline = block.lastIndexOf("\n", stateStart - 2);
|
|
3124
|
+
const lineStart = precedingNewline + 1;
|
|
3125
|
+
const lineEnd = block[stateStart - 2] === "\r" ? stateStart - 2 : stateStart - 1;
|
|
3126
|
+
if (!/^[ \t]*$/.test(block.slice(lineStart, lineEnd)))
|
|
3127
|
+
break;
|
|
3128
|
+
stateStart = lineStart;
|
|
3129
|
+
}
|
|
3130
|
+
const stateEnd = block.indexOf(MANAGED_BLOCK_END, match.index);
|
|
3131
|
+
if (stateEnd === -1)
|
|
3132
|
+
return { managedBlock: block };
|
|
3133
|
+
const state = block.slice(stateStart, stateEnd);
|
|
3134
|
+
try {
|
|
3135
|
+
const table = parse2(normalizeLineEndings(state));
|
|
3136
|
+
const hooks = table.hooks;
|
|
3137
|
+
if (Object.keys(table).length !== 1 || !isTable(hooks) || Object.keys(hooks).length !== 1 || !isTable(hooks.state)) {
|
|
3138
|
+
return { managedBlock: block };
|
|
3139
|
+
}
|
|
3140
|
+
} catch {
|
|
3141
|
+
return { managedBlock: block };
|
|
3142
|
+
}
|
|
3143
|
+
return {
|
|
3144
|
+
managedBlock: block.slice(0, stateStart) + block.slice(stateEnd),
|
|
3145
|
+
state
|
|
3146
|
+
};
|
|
2992
3147
|
}
|
|
2993
3148
|
function splitManagedBlock(text, configPath) {
|
|
2994
3149
|
const start = text.indexOf(MANAGED_BLOCK_START);
|
|
2995
3150
|
const endMark = text.indexOf(MANAGED_BLOCK_END);
|
|
2996
|
-
if (start === -1 && endMark === -1)
|
|
2997
|
-
return { remainder: text };
|
|
3151
|
+
if (start === -1 && endMark === -1) {
|
|
3152
|
+
return { before: text, after: "", remainder: text };
|
|
3153
|
+
}
|
|
2998
3154
|
if (start === -1 || endMark === -1 || endMark < start) {
|
|
2999
3155
|
throw new Error(`wingman \u6807\u8BB0\u5757\u6B8B\u7F3A(${configPath}):\u8BF7\u4ECE <wingman\u6839>/backups/ \u6062\u590D\u6216\u624B\u52A8\u5220\u9664\u6807\u8BB0\u540E\u91CD\u8BD5`);
|
|
3000
3156
|
}
|
|
@@ -3002,9 +3158,28 @@ function splitManagedBlock(text, configPath) {
|
|
|
3002
3158
|
throw new Error(`wingman \u6807\u8BB0\u5757\u91CD\u590D(${configPath}):\u8BF7\u4ECE <wingman\u6839>/backups/ \u6062\u590D\u6216\u624B\u52A8\u5220\u9664\u591A\u4F59\u6807\u8BB0\u540E\u91CD\u8BD5`);
|
|
3003
3159
|
}
|
|
3004
3160
|
let end = endMark + MANAGED_BLOCK_END.length;
|
|
3005
|
-
if (text
|
|
3161
|
+
if (text.startsWith("\r\n", end))
|
|
3162
|
+
end += 2;
|
|
3163
|
+
else if (text[end] === "\n")
|
|
3006
3164
|
end += 1;
|
|
3007
|
-
|
|
3165
|
+
const before = text.slice(0, start);
|
|
3166
|
+
const after = text.slice(end);
|
|
3167
|
+
return { before, after, remainder: before + after, block: text.slice(start, end) };
|
|
3168
|
+
}
|
|
3169
|
+
function normalizeLineEndings(text) {
|
|
3170
|
+
return text.replaceAll("\r\n", "\n");
|
|
3171
|
+
}
|
|
3172
|
+
function preferredLineEnding(text) {
|
|
3173
|
+
return lineEndingStyle(text) === "crlf" ? "\r\n" : "\n";
|
|
3174
|
+
}
|
|
3175
|
+
function lineEndingStyle(text) {
|
|
3176
|
+
const hasCrlf = text.includes("\r\n");
|
|
3177
|
+
if (!text.includes("\n"))
|
|
3178
|
+
return "none";
|
|
3179
|
+
const hasBareLf = text.replaceAll("\r\n", "").includes("\n");
|
|
3180
|
+
if (hasCrlf && hasBareLf)
|
|
3181
|
+
return "mixed";
|
|
3182
|
+
return hasCrlf ? "crlf" : "lf";
|
|
3008
3183
|
}
|
|
3009
3184
|
function assertWritable(remainder, configPath) {
|
|
3010
3185
|
let table;
|
|
@@ -3015,7 +3190,7 @@ function assertWritable(remainder, configPath) {
|
|
|
3015
3190
|
}
|
|
3016
3191
|
const hooks = table.hooks;
|
|
3017
3192
|
if (hooks === void 0)
|
|
3018
|
-
return;
|
|
3193
|
+
return false;
|
|
3019
3194
|
if (!isTable(hooks)) {
|
|
3020
3195
|
throw new Error(`Codex config.toml \u7684 hooks \u952E\u4E0D\u662F\u8868,\u672A\u77E5\u683C\u5F0F\u62D2\u7EDD\u5199\u5165(${configPath})`);
|
|
3021
3196
|
}
|
|
@@ -3023,6 +3198,15 @@ function assertWritable(remainder, configPath) {
|
|
|
3023
3198
|
if (userKeys.length > 0) {
|
|
3024
3199
|
throw new Error(`\u5DF2\u5B58\u5728\u7528\u6237\u81EA\u5DF1\u7684 [hooks] \u914D\u7F6E(${userKeys.join(", ")}),wingman \u4E0D\u5408\u5E76\u4E0D\u8986\u76D6;\u8BF7\u5148\u624B\u52A8\u79FB\u9664\u6216\u81EA\u884C\u628A\u8F6C\u53D1\u547D\u4EE4\u52A0\u5165\u73B0\u6709 hooks(${configPath})`);
|
|
3025
3200
|
}
|
|
3201
|
+
return hooks.state !== void 0;
|
|
3202
|
+
}
|
|
3203
|
+
function hasHooksState(text) {
|
|
3204
|
+
try {
|
|
3205
|
+
const hooks = parse2(text).hooks;
|
|
3206
|
+
return isTable(hooks) && hooks.state !== void 0;
|
|
3207
|
+
} catch {
|
|
3208
|
+
return /^[ \t]*\[[ \t]*hooks[ \t]*\.[ \t]*state(?:[ \t]*|[ \t]*\..+)\]/m.test(text);
|
|
3209
|
+
}
|
|
3026
3210
|
}
|
|
3027
3211
|
function backupFile(root, filePath, contents) {
|
|
3028
3212
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-");
|
|
@@ -3364,27 +3548,18 @@ function collectTools(out, layer) {
|
|
|
3364
3548
|
enabled: server.enabled !== false,
|
|
3365
3549
|
source: layer.source
|
|
3366
3550
|
});
|
|
3367
|
-
const
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
id: `codex:tools:${layer.source}:mcp_servers.${serverId}/${tool}`,
|
|
3380
|
-
slot: "tools",
|
|
3381
|
-
nativeName: `mcp_servers.${serverId}/${tool}`,
|
|
3382
|
-
nativePath: layer.path,
|
|
3383
|
-
toggleable: true,
|
|
3384
|
-
enabled,
|
|
3385
|
-
source: layer.source
|
|
3386
|
-
});
|
|
3387
|
-
}
|
|
3551
|
+
const enabledTools = new Set(Array.isArray(server.enabled_tools) ? server.enabled_tools.filter((tool) => typeof tool === "string") : []);
|
|
3552
|
+
const disabledTools = new Set(Array.isArray(server.disabled_tools) ? server.disabled_tools.filter((tool) => typeof tool === "string") : []);
|
|
3553
|
+
for (const tool of /* @__PURE__ */ new Set([...enabledTools, ...disabledTools])) {
|
|
3554
|
+
out.push({
|
|
3555
|
+
id: `codex:tools:${layer.source}:mcp_servers.${serverId}/${tool}`,
|
|
3556
|
+
slot: "tools",
|
|
3557
|
+
nativeName: `mcp_servers.${serverId}/${tool}`,
|
|
3558
|
+
nativePath: layer.path,
|
|
3559
|
+
toggleable: true,
|
|
3560
|
+
enabled: enabledTools.has(tool) && !disabledTools.has(tool),
|
|
3561
|
+
source: layer.source
|
|
3562
|
+
});
|
|
3388
3563
|
}
|
|
3389
3564
|
}
|
|
3390
3565
|
}
|
|
@@ -3494,7 +3669,9 @@ var init_scan2 = __esm({
|
|
|
3494
3669
|
// packages/adapter-codex/dist/index.js
|
|
3495
3670
|
var dist_exports2 = {};
|
|
3496
3671
|
__export(dist_exports2, {
|
|
3497
|
-
|
|
3672
|
+
HOOK_EVENTS: () => HOOK_EVENTS,
|
|
3673
|
+
createAdapter: () => createAdapter2,
|
|
3674
|
+
feedbackSignals: () => feedbackSignalsCodex
|
|
3498
3675
|
});
|
|
3499
3676
|
function createAdapter2(_options) {
|
|
3500
3677
|
return {
|
|
@@ -3513,6 +3690,7 @@ var init_dist4 = __esm({
|
|
|
3513
3690
|
init_actions2();
|
|
3514
3691
|
init_hooks2();
|
|
3515
3692
|
init_scan2();
|
|
3693
|
+
init_hooks2();
|
|
3516
3694
|
}
|
|
3517
3695
|
});
|
|
3518
3696
|
|
|
@@ -11054,6 +11232,9 @@ import { Buffer as Buffer3 } from "node:buffer";
|
|
|
11054
11232
|
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
11055
11233
|
import { homedir as homedir8 } from "node:os";
|
|
11056
11234
|
import { dirname as dirname9, join as join14 } from "node:path";
|
|
11235
|
+
function feedbackSignalsDsh(_options) {
|
|
11236
|
+
return { wiredEvents: [], feedback: "unavailable" };
|
|
11237
|
+
}
|
|
11057
11238
|
function resolveDshHome(options) {
|
|
11058
11239
|
if (options?.homeDir !== void 0)
|
|
11059
11240
|
return join14(options.homeDir, ".dsh");
|
|
@@ -11114,7 +11295,7 @@ async function readIfExists(file) {
|
|
|
11114
11295
|
throw error;
|
|
11115
11296
|
}
|
|
11116
11297
|
}
|
|
11117
|
-
function
|
|
11298
|
+
function decodeUtf8Strict2(buffer, file) {
|
|
11118
11299
|
const text = buffer.toString("utf8");
|
|
11119
11300
|
if (!buffer.equals(Buffer3.from(text, "utf8"))) {
|
|
11120
11301
|
throw new Error(`\u62D2\u5199:${file} \u4E0D\u662F UTF-8 \u6587\u672C,\u672A\u77E5\u683C\u5F0F\u4E0D\u731C`);
|
|
@@ -11226,7 +11407,7 @@ async function installDshHooks(port, options) {
|
|
|
11226
11407
|
const url = `http://127.0.0.1:${port}/hook?harness=dsh`;
|
|
11227
11408
|
const block = buildPatchBlock(paths.hooksConfigFile);
|
|
11228
11409
|
const currentBuffer = await readIfExists(paths.patchFile);
|
|
11229
|
-
const currentText = currentBuffer === void 0 ? void 0 :
|
|
11410
|
+
const currentText = currentBuffer === void 0 ? void 0 : decodeUtf8Strict2(currentBuffer, paths.patchFile);
|
|
11230
11411
|
const surgicalText = currentText === void 0 ? void 0 : stripLiveHooksBlocks(currentText, paths.patchFile);
|
|
11231
11412
|
const entriesBefore = surgicalText === void 0 ? [] : parsePatchEntries(surgicalText, paths.patchFile);
|
|
11232
11413
|
if (hasWingmanRow(entriesBefore)) {
|
|
@@ -11273,7 +11454,7 @@ async function uninstallDshHooks(options) {
|
|
|
11273
11454
|
const state = await readState3(paths);
|
|
11274
11455
|
const currentBuffer = await readIfExists(paths.patchFile);
|
|
11275
11456
|
if (currentBuffer !== void 0) {
|
|
11276
|
-
const currentText =
|
|
11457
|
+
const currentText = decodeUtf8Strict2(currentBuffer, paths.patchFile);
|
|
11277
11458
|
if (currentText.includes(HOOKS_MARKER_BEGIN_PREFIX)) {
|
|
11278
11459
|
const surgical = stripLiveHooksBlocks(currentText, paths.patchFile);
|
|
11279
11460
|
const restored = resolveRestoredText(surgical, state, paths);
|
|
@@ -11527,8 +11708,11 @@ function composeToggleOn(current, block, file) {
|
|
|
11527
11708
|
if (block.newlineAdded && block.end === current.length && removed.endsWith("\n")) {
|
|
11528
11709
|
removed = removed.slice(0, -1);
|
|
11529
11710
|
}
|
|
11530
|
-
if (block.fileCreated
|
|
11531
|
-
|
|
11711
|
+
if (block.fileCreated) {
|
|
11712
|
+
if (removed === "")
|
|
11713
|
+
return { kind: "delete" };
|
|
11714
|
+
removed = transferFileCreatedFlag(removed, file);
|
|
11715
|
+
}
|
|
11532
11716
|
const entriesAfter = parsePatchEntries(removed, file);
|
|
11533
11717
|
if (block.emptyRootLine !== void 0) {
|
|
11534
11718
|
if (entriesAfter.length === 0) {
|
|
@@ -11541,6 +11725,18 @@ function composeToggleOn(current, block, file) {
|
|
|
11541
11725
|
}
|
|
11542
11726
|
return { kind: "modify", after: removed };
|
|
11543
11727
|
}
|
|
11728
|
+
function transferFileCreatedFlag(text, file) {
|
|
11729
|
+
const blocks = findToggleBlocks(text, file);
|
|
11730
|
+
const target = blocks.find((b) => !b.fileCreated);
|
|
11731
|
+
if (target === void 0)
|
|
11732
|
+
return text;
|
|
11733
|
+
const beginLineEnd = text.indexOf("\n", target.start);
|
|
11734
|
+
if (beginLineEnd === -1)
|
|
11735
|
+
return text;
|
|
11736
|
+
const flagLine = `${FLAG_PREFIX}${FLAG_FILE_CREATED}
|
|
11737
|
+
`;
|
|
11738
|
+
return text.slice(0, beginLineEnd + 1) + flagLine + text.slice(beginLineEnd + 1);
|
|
11739
|
+
}
|
|
11544
11740
|
function transferEmptyRootFlag(text, lineIndex, file) {
|
|
11545
11741
|
const blocks = findToggleBlocks(text, file);
|
|
11546
11742
|
const target = blocks.find((b) => b.emptyRootLine === void 0);
|
|
@@ -11607,7 +11803,7 @@ async function planDshToggle(action, dump, options) {
|
|
|
11607
11803
|
}
|
|
11608
11804
|
const paths = resolveDshHookPaths(options);
|
|
11609
11805
|
const buffer = await readIfExists(paths.patchFile);
|
|
11610
|
-
const text = buffer === void 0 ? void 0 :
|
|
11806
|
+
const text = buffer === void 0 ? void 0 : decodeUtf8Strict2(buffer, paths.patchFile);
|
|
11611
11807
|
const blocks = text === void 0 ? [] : findToggleBlocks(text, paths.patchFile);
|
|
11612
11808
|
const myBlock = blocks.find((b) => b.instanceId === row.id);
|
|
11613
11809
|
const entriesAll = text === void 0 ? [] : parsePatchEntries(text, paths.patchFile);
|
|
@@ -11749,8 +11945,10 @@ var init_toggle = __esm({
|
|
|
11749
11945
|
// packages/adapter-dsh/dist/index.js
|
|
11750
11946
|
var dist_exports3 = {};
|
|
11751
11947
|
__export(dist_exports3, {
|
|
11948
|
+
BRIDGE_EVENTS: () => BRIDGE_EVENTS,
|
|
11752
11949
|
createAdapter: () => createAdapter3,
|
|
11753
11950
|
detectDshLiveWiring: () => detectDshLiveWiring,
|
|
11951
|
+
feedbackSignals: () => feedbackSignalsDsh,
|
|
11754
11952
|
parseDumpConfig: () => parseDumpConfig,
|
|
11755
11953
|
resolveDshHome: () => resolveDshHome,
|
|
11756
11954
|
resolveDshHookPaths: () => resolveDshHookPaths
|
|
@@ -11902,12 +12100,14 @@ function createReceiver(options = {}) {
|
|
|
11902
12100
|
const heartbeatMs = options.heartbeatMs ?? 15e3;
|
|
11903
12101
|
const buffer = [];
|
|
11904
12102
|
const subscribers = /* @__PURE__ */ new Set();
|
|
12103
|
+
let nextEventId = 1;
|
|
11905
12104
|
function accept(event) {
|
|
11906
|
-
|
|
12105
|
+
const buffered = { id: nextEventId++, event };
|
|
12106
|
+
buffer.push(buffered);
|
|
11907
12107
|
if (buffer.length > bufferSize)
|
|
11908
12108
|
buffer.shift();
|
|
11909
12109
|
options.onEvent?.(event);
|
|
11910
|
-
const data = frame(
|
|
12110
|
+
const data = frame(buffered);
|
|
11911
12111
|
for (const subscriber of subscribers)
|
|
11912
12112
|
subscriber.res.write(data);
|
|
11913
12113
|
}
|
|
@@ -11935,7 +12135,7 @@ function createReceiver(options = {}) {
|
|
|
11935
12135
|
return;
|
|
11936
12136
|
}
|
|
11937
12137
|
accept({ harness, received_at: (/* @__PURE__ */ new Date()).toISOString(), payload: parsed });
|
|
11938
|
-
|
|
12138
|
+
sendJson(res, 200, {});
|
|
11939
12139
|
}, (error) => sendText(res, 400, error instanceof Error ? error.message : "\u8BFB\u53D6\u8BF7\u6C42\u4F53\u5931\u8D25"));
|
|
11940
12140
|
}
|
|
11941
12141
|
function handleEvents(req, res) {
|
|
@@ -11951,8 +12151,12 @@ function createReceiver(options = {}) {
|
|
|
11951
12151
|
res.on("error", () => {
|
|
11952
12152
|
});
|
|
11953
12153
|
res.write(": connected\n\n");
|
|
11954
|
-
|
|
11955
|
-
|
|
12154
|
+
const latestId = buffer[buffer.length - 1]?.id ?? 0;
|
|
12155
|
+
const lastEventId = parseLastEventId(req, latestId);
|
|
12156
|
+
for (const event of buffer) {
|
|
12157
|
+
if (lastEventId === void 0 || event.id > lastEventId)
|
|
12158
|
+
res.write(frame(event));
|
|
12159
|
+
}
|
|
11956
12160
|
const subscriber = {
|
|
11957
12161
|
res,
|
|
11958
12162
|
heartbeat: setInterval(() => res.write(": heartbeat\n\n"), heartbeatMs)
|
|
@@ -11979,11 +12183,22 @@ function createReceiver(options = {}) {
|
|
|
11979
12183
|
close
|
|
11980
12184
|
};
|
|
11981
12185
|
}
|
|
11982
|
-
function frame(event) {
|
|
11983
|
-
return `
|
|
12186
|
+
function frame({ id, event }) {
|
|
12187
|
+
return `id: ${id}
|
|
12188
|
+
data: ${JSON.stringify(event)}
|
|
11984
12189
|
|
|
11985
12190
|
`;
|
|
11986
12191
|
}
|
|
12192
|
+
function parseLastEventId(req, latestId) {
|
|
12193
|
+
const header = req.headers["last-event-id"];
|
|
12194
|
+
const value = Array.isArray(header) ? header[0] : header;
|
|
12195
|
+
if (value === void 0 || !/^\d+$/.test(value))
|
|
12196
|
+
return void 0;
|
|
12197
|
+
const id = Number(value);
|
|
12198
|
+
if (!Number.isSafeInteger(id) || id > latestId)
|
|
12199
|
+
return void 0;
|
|
12200
|
+
return id;
|
|
12201
|
+
}
|
|
11987
12202
|
function isHarnessId2(value) {
|
|
11988
12203
|
return typeof value === "string" && HARNESS_IDS.includes(value);
|
|
11989
12204
|
}
|
|
@@ -12011,6 +12226,10 @@ function readBody(req) {
|
|
|
12011
12226
|
req.on("error", reject);
|
|
12012
12227
|
});
|
|
12013
12228
|
}
|
|
12229
|
+
function sendJson(res, status, body) {
|
|
12230
|
+
res.writeHead(status, { "content-type": "application/json; charset=utf-8" });
|
|
12231
|
+
res.end(JSON.stringify(body));
|
|
12232
|
+
}
|
|
12014
12233
|
function sendText(res, status, body) {
|
|
12015
12234
|
res.writeHead(status, { "content-type": "text/plain; charset=utf-8" });
|
|
12016
12235
|
res.end(body);
|
|
@@ -12098,6 +12317,13 @@ function verifyEntitlement(raw, now, publicKeyPem) {
|
|
|
12098
12317
|
}
|
|
12099
12318
|
return { state: "expired", detail: `\u8BA2\u9605\u5DF2\u4E8E ${claims.exp} \u5230\u671F\u4E14\u8D85\u51FA\u5BBD\u9650\u671F,\u5199\u52A8\u4F5C\u5DF2\u9501`, claims };
|
|
12100
12319
|
}
|
|
12320
|
+
function nextLicenseBackupPath(licensePath, now) {
|
|
12321
|
+
const base = `${licensePath}.bak-${now.toISOString().replaceAll(":", "-")}`;
|
|
12322
|
+
let candidate = base;
|
|
12323
|
+
for (let index = 2; existsSync(candidate); index++)
|
|
12324
|
+
candidate = `${base}-${index}`;
|
|
12325
|
+
return candidate;
|
|
12326
|
+
}
|
|
12101
12327
|
function installLicense(raw, options = {}) {
|
|
12102
12328
|
let parsed;
|
|
12103
12329
|
try {
|
|
@@ -12121,8 +12347,9 @@ function installLicense(raw, options = {}) {
|
|
|
12121
12347
|
try {
|
|
12122
12348
|
mkdirSync(dir, { recursive: true });
|
|
12123
12349
|
writeFileSync(tmpPath, raw);
|
|
12124
|
-
if (existsSync(licensePath))
|
|
12125
|
-
renameSync(licensePath,
|
|
12350
|
+
if (existsSync(licensePath)) {
|
|
12351
|
+
renameSync(licensePath, nextLicenseBackupPath(licensePath, options.now ?? /* @__PURE__ */ new Date()));
|
|
12352
|
+
}
|
|
12126
12353
|
renameSync(tmpPath, licensePath);
|
|
12127
12354
|
} catch (error) {
|
|
12128
12355
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -12132,7 +12359,7 @@ function installLicense(raw, options = {}) {
|
|
|
12132
12359
|
}
|
|
12133
12360
|
|
|
12134
12361
|
// apps/cli/dist/license-refresh.js
|
|
12135
|
-
import { readFileSync as readFileSync2, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
12362
|
+
import { copyFileSync, readFileSync as readFileSync2, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
12136
12363
|
import { homedir as homedir2 } from "node:os";
|
|
12137
12364
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
12138
12365
|
var DEFAULT_LICENSE_SERVER = "https://api.getwingman.dev";
|
|
@@ -12229,6 +12456,7 @@ async function refreshLicense(options = {}) {
|
|
|
12229
12456
|
const tmpPath = join3(dirname2(licensePath), `.license.json.tmp-${process.pid}`);
|
|
12230
12457
|
try {
|
|
12231
12458
|
writeFileSync2(tmpPath, next);
|
|
12459
|
+
copyFileSync(licensePath, nextLicenseBackupPath(licensePath, options.now ?? /* @__PURE__ */ new Date()));
|
|
12232
12460
|
renameSync2(tmpPath, licensePath);
|
|
12233
12461
|
} catch (error) {
|
|
12234
12462
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -12901,10 +13129,16 @@ function applyTodoWrite(input, tasks) {
|
|
|
12901
13129
|
if (!Array.isArray(todos))
|
|
12902
13130
|
return false;
|
|
12903
13131
|
tasks.clear();
|
|
13132
|
+
const occurrences = /* @__PURE__ */ new Map();
|
|
12904
13133
|
for (const raw of todos) {
|
|
12905
13134
|
if (!isRecord3(raw) || typeof raw.content !== "string" || !isTodoStatus(raw.status))
|
|
12906
13135
|
continue;
|
|
12907
|
-
const
|
|
13136
|
+
const occurrence = (occurrences.get(raw.content) ?? 0) + 1;
|
|
13137
|
+
occurrences.set(raw.content, occurrence);
|
|
13138
|
+
const id = occurrence === 1 ? raw.content : `${raw.content}#${occurrence}`;
|
|
13139
|
+
const item = { id, content: raw.content, status: raw.status };
|
|
13140
|
+
if (typeof raw.description === "string")
|
|
13141
|
+
item.description = raw.description;
|
|
12908
13142
|
if (typeof raw.activeForm === "string")
|
|
12909
13143
|
item.activeForm = raw.activeForm;
|
|
12910
13144
|
tasks.set(item.id, item);
|
|
@@ -12916,6 +13150,8 @@ function applyTaskCreate(input, toolUseId, createResultIds, tasks) {
|
|
|
12916
13150
|
return false;
|
|
12917
13151
|
const id = createResultIds.get(toolUseId) ?? toolUseId;
|
|
12918
13152
|
const item = { id, content: input.subject, status: "pending" };
|
|
13153
|
+
if (typeof input.description === "string")
|
|
13154
|
+
item.description = input.description;
|
|
12919
13155
|
if (typeof input.activeForm === "string")
|
|
12920
13156
|
item.activeForm = input.activeForm;
|
|
12921
13157
|
tasks.set(id, item);
|
|
@@ -12927,11 +13163,28 @@ function applyTaskUpdate(input, tasks) {
|
|
|
12927
13163
|
const existing = tasks.get(input.taskId);
|
|
12928
13164
|
if (existing === void 0)
|
|
12929
13165
|
return false;
|
|
13166
|
+
if (input.status === "deleted") {
|
|
13167
|
+
tasks.delete(input.taskId);
|
|
13168
|
+
return true;
|
|
13169
|
+
}
|
|
13170
|
+
let changed = false;
|
|
12930
13171
|
if (isTodoStatus(input.status) && input.status !== existing.status) {
|
|
12931
13172
|
existing.status = input.status;
|
|
12932
|
-
|
|
13173
|
+
changed = true;
|
|
12933
13174
|
}
|
|
12934
|
-
|
|
13175
|
+
if (typeof input.subject === "string" && input.subject !== existing.content) {
|
|
13176
|
+
existing.content = input.subject;
|
|
13177
|
+
changed = true;
|
|
13178
|
+
}
|
|
13179
|
+
if (typeof input.description === "string" && input.description !== existing.description) {
|
|
13180
|
+
existing.description = input.description;
|
|
13181
|
+
changed = true;
|
|
13182
|
+
}
|
|
13183
|
+
if (typeof input.activeForm === "string" && input.activeForm !== existing.activeForm) {
|
|
13184
|
+
existing.activeForm = input.activeForm;
|
|
13185
|
+
changed = true;
|
|
13186
|
+
}
|
|
13187
|
+
return changed;
|
|
12935
13188
|
}
|
|
12936
13189
|
function snapshotOf(tasks, ts) {
|
|
12937
13190
|
const snapshot = { todos: [...tasks.values()].map((item) => ({ ...item })) };
|
|
@@ -12961,6 +13214,10 @@ function deriveTodoTimeline(snapshots) {
|
|
|
12961
13214
|
if (todo.activeForm !== void 0)
|
|
12962
13215
|
existing.activeForm = todo.activeForm;
|
|
12963
13216
|
const last = existing.spans[existing.spans.length - 1];
|
|
13217
|
+
if (last !== void 0 && last.to !== void 0) {
|
|
13218
|
+
existing.spans.push(newSpan(todo.status, snapshot.ts));
|
|
13219
|
+
continue;
|
|
13220
|
+
}
|
|
12964
13221
|
if (last !== void 0 && last.to === void 0 && last.status !== todo.status) {
|
|
12965
13222
|
if (snapshot.ts !== void 0)
|
|
12966
13223
|
last.to = snapshot.ts;
|
|
@@ -13045,10 +13302,155 @@ function costUsd(usage, model) {
|
|
|
13045
13302
|
return (usage.inputTokens * price.inputPerMTok + usage.cacheReadInputTokens * price.inputPerMTok * CACHE_READ_MULTIPLIER + cacheWriteTokensPriced * price.inputPerMTok + usage.outputTokens * price.outputPerMTok) / 1e6;
|
|
13046
13303
|
}
|
|
13047
13304
|
|
|
13305
|
+
// packages/narrative/dist/workflow.js
|
|
13306
|
+
function deriveTaskWorkflowView(input) {
|
|
13307
|
+
return {
|
|
13308
|
+
...input.goal === void 0 ? {} : { goal: input.goal },
|
|
13309
|
+
...input.latestInstruction === void 0 ? {} : { latestInstruction: input.latestInstruction },
|
|
13310
|
+
plan: deriveTaskPlan(input.harness, input.lines, input.codexLines),
|
|
13311
|
+
execution: input.execution,
|
|
13312
|
+
feedback: input.feedback
|
|
13313
|
+
};
|
|
13314
|
+
}
|
|
13315
|
+
function derivePlanRevisions(observations) {
|
|
13316
|
+
const revisions = [];
|
|
13317
|
+
let revision = 0;
|
|
13318
|
+
let previousKey;
|
|
13319
|
+
for (const observation of observations) {
|
|
13320
|
+
const structureKey = JSON.stringify(observation.steps.map((step) => [step.text, step.description ?? null]));
|
|
13321
|
+
if (structureKey !== previousKey) {
|
|
13322
|
+
revision += 1;
|
|
13323
|
+
previousKey = structureKey;
|
|
13324
|
+
}
|
|
13325
|
+
const snapshot = {
|
|
13326
|
+
revision,
|
|
13327
|
+
steps: observation.steps.map((step, index) => ({
|
|
13328
|
+
id: `${revision}:${index + 1}`,
|
|
13329
|
+
text: step.text,
|
|
13330
|
+
...step.description === void 0 ? {} : { description: step.description },
|
|
13331
|
+
status: step.status
|
|
13332
|
+
}))
|
|
13333
|
+
};
|
|
13334
|
+
if (observation.observedAt !== void 0)
|
|
13335
|
+
snapshot.observedAt = observation.observedAt;
|
|
13336
|
+
revisions.push(snapshot);
|
|
13337
|
+
}
|
|
13338
|
+
return revisions;
|
|
13339
|
+
}
|
|
13340
|
+
function deriveTaskPlan(harness, lines = [], codexLines = []) {
|
|
13341
|
+
if (harness === "cc")
|
|
13342
|
+
return deriveClaudeTaskPlan(lines);
|
|
13343
|
+
if (harness === "codex")
|
|
13344
|
+
return deriveCodexTaskPlan(codexLines);
|
|
13345
|
+
return {
|
|
13346
|
+
availability: "unavailable",
|
|
13347
|
+
source: "dsh 0.1.1-rc.2",
|
|
13348
|
+
reason: "dsh \u6CA1\u6709\u5411 Wingman hooks \u66B4\u9732\u7A33\u5B9A\u7684\u672C\u5730 todo projection"
|
|
13349
|
+
};
|
|
13350
|
+
}
|
|
13351
|
+
function deriveClaudeTaskPlan(lines) {
|
|
13352
|
+
const source = "Claude Code TaskCreate/TaskUpdate/TodoWrite";
|
|
13353
|
+
if (latestClaudePlanPayloadValid(lines) === false) {
|
|
13354
|
+
return {
|
|
13355
|
+
availability: "unavailable",
|
|
13356
|
+
source,
|
|
13357
|
+
reason: "Claude Code \u8BA1\u5212\u5DE5\u5177\u53C2\u6570\u65E0\u6CD5\u89E3\u6790"
|
|
13358
|
+
};
|
|
13359
|
+
}
|
|
13360
|
+
const observations = extractTodoSnapshots(lines).map((snapshot) => ({
|
|
13361
|
+
...snapshot.ts === void 0 ? {} : { observedAt: snapshot.ts },
|
|
13362
|
+
steps: snapshot.todos.map((todo) => ({
|
|
13363
|
+
text: todo.content,
|
|
13364
|
+
...todo.description === void 0 ? {} : { description: todo.description },
|
|
13365
|
+
status: todo.status
|
|
13366
|
+
}))
|
|
13367
|
+
}));
|
|
13368
|
+
if (observations.length === 0)
|
|
13369
|
+
return { availability: "missing", source };
|
|
13370
|
+
return availablePlan(source, derivePlanRevisions(observations));
|
|
13371
|
+
}
|
|
13372
|
+
function latestClaudePlanPayloadValid(lines) {
|
|
13373
|
+
let latest;
|
|
13374
|
+
for (const line of lines) {
|
|
13375
|
+
if (line.kind !== "assistant")
|
|
13376
|
+
continue;
|
|
13377
|
+
for (const block of line.blocks) {
|
|
13378
|
+
if (block.kind !== "tool_use")
|
|
13379
|
+
continue;
|
|
13380
|
+
if (block.name === "TodoWrite") {
|
|
13381
|
+
latest = Array.isArray(block.input.todos) && block.input.todos.every((todo) => isRecord4(todo) && typeof todo.content === "string" && isTodoStatus2(todo.status));
|
|
13382
|
+
} else if (block.name === "TaskCreate") {
|
|
13383
|
+
latest = typeof block.input.subject === "string";
|
|
13384
|
+
} else if (block.name === "TaskUpdate") {
|
|
13385
|
+
latest = typeof block.input.taskId === "string" && (block.input.status === void 0 || block.input.status === "deleted" || isTodoStatus2(block.input.status)) && (block.input.subject === void 0 || typeof block.input.subject === "string") && (block.input.description === void 0 || typeof block.input.description === "string") && (block.input.activeForm === void 0 || typeof block.input.activeForm === "string");
|
|
13386
|
+
}
|
|
13387
|
+
}
|
|
13388
|
+
}
|
|
13389
|
+
return latest;
|
|
13390
|
+
}
|
|
13391
|
+
function deriveCodexTaskPlan(lines) {
|
|
13392
|
+
const source = "Codex update_plan";
|
|
13393
|
+
const calls = lines.filter((line) => line.kind === "tool_call" && line.name === "update_plan");
|
|
13394
|
+
if (calls.length === 0)
|
|
13395
|
+
return { availability: "missing", source };
|
|
13396
|
+
const observations = [];
|
|
13397
|
+
let latestValid = false;
|
|
13398
|
+
for (const call of calls) {
|
|
13399
|
+
const observation = parseCodexPlan(call.input, call.timestamp);
|
|
13400
|
+
latestValid = observation !== null;
|
|
13401
|
+
if (observation !== null)
|
|
13402
|
+
observations.push(observation);
|
|
13403
|
+
}
|
|
13404
|
+
if (!latestValid) {
|
|
13405
|
+
return {
|
|
13406
|
+
availability: "unavailable",
|
|
13407
|
+
source,
|
|
13408
|
+
reason: "Codex update_plan \u53C2\u6570\u65E0\u6CD5\u89E3\u6790"
|
|
13409
|
+
};
|
|
13410
|
+
}
|
|
13411
|
+
return availablePlan(source, derivePlanRevisions(observations));
|
|
13412
|
+
}
|
|
13413
|
+
function parseCodexPlan(input, observedAt) {
|
|
13414
|
+
if (!isRecord4(input) || !Array.isArray(input.plan))
|
|
13415
|
+
return null;
|
|
13416
|
+
const steps = [];
|
|
13417
|
+
for (const item of input.plan) {
|
|
13418
|
+
if (!isRecord4(item) || typeof item.step !== "string" || !isTodoStatus2(item.status))
|
|
13419
|
+
return null;
|
|
13420
|
+
const text = item.step.trim();
|
|
13421
|
+
if (text === "")
|
|
13422
|
+
return null;
|
|
13423
|
+
steps.push({ text, status: item.status });
|
|
13424
|
+
}
|
|
13425
|
+
return {
|
|
13426
|
+
...observedAt === void 0 ? {} : { observedAt },
|
|
13427
|
+
steps
|
|
13428
|
+
};
|
|
13429
|
+
}
|
|
13430
|
+
function availablePlan(source, revisions) {
|
|
13431
|
+
const latest = revisions[revisions.length - 1];
|
|
13432
|
+
if (latest === void 0)
|
|
13433
|
+
return { availability: "missing", source };
|
|
13434
|
+
return {
|
|
13435
|
+
availability: "available",
|
|
13436
|
+
source,
|
|
13437
|
+
revision: latest.revision,
|
|
13438
|
+
...latest.observedAt === void 0 ? {} : { observedAt: latest.observedAt },
|
|
13439
|
+
semantics: "current_snapshot",
|
|
13440
|
+
steps: latest.steps
|
|
13441
|
+
};
|
|
13442
|
+
}
|
|
13443
|
+
function isTodoStatus2(value) {
|
|
13444
|
+
return value === "pending" || value === "in_progress" || value === "completed";
|
|
13445
|
+
}
|
|
13446
|
+
function isRecord4(value) {
|
|
13447
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13448
|
+
}
|
|
13449
|
+
|
|
13048
13450
|
// apps/cli/dist/narrative-service.js
|
|
13049
13451
|
var MAX_SESSIONS = 8;
|
|
13050
13452
|
var ENDED_AFTER_STOP_MS = 10 * 6e4;
|
|
13051
|
-
var TODO_TOOLS = /* @__PURE__ */ new Set(["TaskCreate", "TaskUpdate", "TodoWrite"]);
|
|
13453
|
+
var TODO_TOOLS = /* @__PURE__ */ new Set(["TaskCreate", "TaskUpdate", "TodoWrite", "update_plan"]);
|
|
13052
13454
|
var DETAIL_LIMIT = 80;
|
|
13053
13455
|
var QUOTE_LIMIT = 160;
|
|
13054
13456
|
var ERROR_BRIEF_LIMIT = 60;
|
|
@@ -13095,6 +13497,7 @@ function createNarrativeService(options = {}) {
|
|
|
13095
13497
|
function newSession(id) {
|
|
13096
13498
|
return {
|
|
13097
13499
|
id,
|
|
13500
|
+
generation: 0,
|
|
13098
13501
|
lines: [],
|
|
13099
13502
|
codexLines: [],
|
|
13100
13503
|
follows: /* @__PURE__ */ new Map(),
|
|
@@ -13105,6 +13508,7 @@ function createNarrativeService(options = {}) {
|
|
|
13105
13508
|
};
|
|
13106
13509
|
}
|
|
13107
13510
|
function closeSession(session) {
|
|
13511
|
+
session.generation += 1;
|
|
13108
13512
|
for (const handle2 of session.follows.values())
|
|
13109
13513
|
handle2.close();
|
|
13110
13514
|
session.follows.clear();
|
|
@@ -13155,10 +13559,12 @@ function createNarrativeService(options = {}) {
|
|
|
13155
13559
|
session.transcriptPath = path;
|
|
13156
13560
|
session.dirty = true;
|
|
13157
13561
|
}
|
|
13158
|
-
function startFollowing(harness, session, path) {
|
|
13159
|
-
if (closed || session.follows.has(path))
|
|
13562
|
+
function startFollowing(harness, session, path, generation = session.generation) {
|
|
13563
|
+
if (closed || generation !== session.generation || session.follows.has(path))
|
|
13160
13564
|
return;
|
|
13161
13565
|
session.follows.set(path, followTranscript(path, (line) => {
|
|
13566
|
+
if (closed || generation !== session.generation)
|
|
13567
|
+
return;
|
|
13162
13568
|
if (harness === "codex")
|
|
13163
13569
|
session.codexLines.push(parseCodexRolloutLine(line));
|
|
13164
13570
|
else
|
|
@@ -13172,12 +13578,16 @@ function createNarrativeService(options = {}) {
|
|
|
13172
13578
|
if (sessionDir === transcriptPath)
|
|
13173
13579
|
return;
|
|
13174
13580
|
const subagentsDir = join4(sessionDir, "subagents");
|
|
13581
|
+
const generation = session.generation;
|
|
13175
13582
|
const poll = async () => {
|
|
13176
13583
|
try {
|
|
13177
13584
|
const entries = await readdir(subagentsDir);
|
|
13585
|
+
if (closed || generation !== session.generation || session.transcriptPath !== transcriptPath) {
|
|
13586
|
+
return;
|
|
13587
|
+
}
|
|
13178
13588
|
for (const entry of entries) {
|
|
13179
13589
|
if (/^agent-.*\.jsonl$/.test(entry)) {
|
|
13180
|
-
startFollowing(harness, session, join4(subagentsDir, entry));
|
|
13590
|
+
startFollowing(harness, session, join4(subagentsDir, entry), generation);
|
|
13181
13591
|
}
|
|
13182
13592
|
}
|
|
13183
13593
|
} catch {
|
|
@@ -13240,14 +13650,20 @@ function createNarrativeService(options = {}) {
|
|
|
13240
13650
|
} else if (name === "PostToolUse" && typeof payload.tool_name === "string" || name === "Stop" || name === "SessionEnd") {
|
|
13241
13651
|
session.pendingHookTool = void 0;
|
|
13242
13652
|
}
|
|
13243
|
-
|
|
13244
|
-
|
|
13245
|
-
const
|
|
13653
|
+
const signal = waitingSignal(payload);
|
|
13654
|
+
if (signal !== null) {
|
|
13655
|
+
const { kind, message } = signal;
|
|
13246
13656
|
if (session.waiting === null) {
|
|
13247
13657
|
session.waiting = { kind, message, since: event.received_at };
|
|
13248
13658
|
} else if (kind === "permission" || session.waiting.kind === kind) {
|
|
13249
13659
|
session.waiting = { kind, message, since: session.waiting.since };
|
|
13250
13660
|
}
|
|
13661
|
+
session.feedbackState = {
|
|
13662
|
+
state: "needs_user",
|
|
13663
|
+
source: `${event.harness} ${String(name)} signal`,
|
|
13664
|
+
since: session.waiting?.since ?? event.received_at,
|
|
13665
|
+
...message === "" ? {} : { message }
|
|
13666
|
+
};
|
|
13251
13667
|
} else {
|
|
13252
13668
|
if (session.waiting !== null) {
|
|
13253
13669
|
session.waitEpisodes.push({
|
|
@@ -13257,6 +13673,18 @@ function createNarrativeService(options = {}) {
|
|
|
13257
13673
|
});
|
|
13258
13674
|
session.waiting = null;
|
|
13259
13675
|
}
|
|
13676
|
+
const resetFeedback = name === "UserPromptSubmit" || name === "Stop" || name === "SessionEnd";
|
|
13677
|
+
if (resetFeedback) {
|
|
13678
|
+
session.feedbackState = void 0;
|
|
13679
|
+
} else if (session.feedbackState?.state === "needs_user") {
|
|
13680
|
+
session.feedbackState = {
|
|
13681
|
+
state: "activity_resumed_unknown",
|
|
13682
|
+
source: session.feedbackState.source,
|
|
13683
|
+
requestSince: session.feedbackState.since,
|
|
13684
|
+
resumedAt: event.received_at,
|
|
13685
|
+
...session.feedbackState.message === void 0 ? {} : { message: session.feedbackState.message }
|
|
13686
|
+
};
|
|
13687
|
+
}
|
|
13260
13688
|
}
|
|
13261
13689
|
if (name === "SessionEnd") {
|
|
13262
13690
|
session.endedAt = event.received_at;
|
|
@@ -13320,16 +13748,31 @@ function createNarrativeService(options = {}) {
|
|
|
13320
13748
|
function getOverview(harness) {
|
|
13321
13749
|
const nowIso = now().toISOString();
|
|
13322
13750
|
const sessions = sessionsOf(harness);
|
|
13751
|
+
let feedback;
|
|
13752
|
+
try {
|
|
13753
|
+
feedback = options.feedbackSignals?.(harness);
|
|
13754
|
+
} catch {
|
|
13755
|
+
feedback = void 0;
|
|
13756
|
+
}
|
|
13323
13757
|
const infos = [...sessions.values()].map((session) => ({
|
|
13324
13758
|
id: session.id,
|
|
13325
13759
|
label: session.label ?? session.id.slice(0, 8),
|
|
13326
13760
|
lastActivity: lastActivityOf(session),
|
|
13327
13761
|
waiting: session.waiting !== null,
|
|
13328
|
-
ended: isEnded(session, nowIso)
|
|
13762
|
+
ended: isEnded(session, nowIso),
|
|
13763
|
+
...feedback === void 0 ? {} : { feedback }
|
|
13329
13764
|
})).sort((a, b) => a.lastActivity < b.lastActivity ? 1 : a.lastActivity > b.lastActivity ? -1 : 0);
|
|
13330
13765
|
const states = {};
|
|
13331
|
-
for (const [id, session] of sessions)
|
|
13332
|
-
|
|
13766
|
+
for (const [id, session] of sessions) {
|
|
13767
|
+
const state = deriveCached(harness, session);
|
|
13768
|
+
states[id] = state.taskWorkflow === void 0 ? state : {
|
|
13769
|
+
...state,
|
|
13770
|
+
taskWorkflow: {
|
|
13771
|
+
...state.taskWorkflow,
|
|
13772
|
+
feedback: feedbackView(harness, feedback, session.feedbackState)
|
|
13773
|
+
}
|
|
13774
|
+
};
|
|
13775
|
+
}
|
|
13333
13776
|
return { harness, sessions: infos, states };
|
|
13334
13777
|
}
|
|
13335
13778
|
function getState(harness) {
|
|
@@ -13422,6 +13865,44 @@ function waitingMessage(payload, fallback) {
|
|
|
13422
13865
|
}
|
|
13423
13866
|
return fallback;
|
|
13424
13867
|
}
|
|
13868
|
+
function waitingSignal(payload) {
|
|
13869
|
+
const name = payload.hook_event_name;
|
|
13870
|
+
if (name === "PermissionRequest") {
|
|
13871
|
+
const toolName = typeof payload.tool_name === "string" && payload.tool_name !== "" ? payload.tool_name : "";
|
|
13872
|
+
const detail = hookToolDetail(payload.tool_input);
|
|
13873
|
+
const reason = [toolName, detail].filter((part) => part !== "").join(" \xB7 ");
|
|
13874
|
+
return {
|
|
13875
|
+
kind: "permission",
|
|
13876
|
+
message: reason === "" ? waitingMessage(payload, "\u7B49\u5F85\u6388\u6743") : `\u7B49\u5F85\u6388\u6743:${reason}`
|
|
13877
|
+
};
|
|
13878
|
+
}
|
|
13879
|
+
if (name !== "Notification")
|
|
13880
|
+
return null;
|
|
13881
|
+
switch (payload.notification_type) {
|
|
13882
|
+
case "permission_prompt":
|
|
13883
|
+
return { kind: "permission", message: waitingMessage(payload, "\u7B49\u5F85\u6388\u6743") };
|
|
13884
|
+
case "idle_prompt":
|
|
13885
|
+
case "elicitation_dialog":
|
|
13886
|
+
return { kind: "notification", message: waitingMessage(payload, "\u7B49\u5F85\u56DE\u5E94") };
|
|
13887
|
+
default:
|
|
13888
|
+
return null;
|
|
13889
|
+
}
|
|
13890
|
+
}
|
|
13891
|
+
function feedbackView(harness, capability, state) {
|
|
13892
|
+
if (capability === "not_wired") {
|
|
13893
|
+
return {
|
|
13894
|
+
state: "unavailable",
|
|
13895
|
+
reason: `\u53CD\u9988\u4FE1\u53F7\u672A\u63A5\u7EBF\uFF1B\u8BF7\u8FD0\u884C npx harness-wingman hook install ${harness}`
|
|
13896
|
+
};
|
|
13897
|
+
}
|
|
13898
|
+
if (capability === "unavailable") {
|
|
13899
|
+
return { state: "unavailable", reason: "\u53CD\u9988\u4FE1\u53F7\u4E0D\u53EF\u7528" };
|
|
13900
|
+
}
|
|
13901
|
+
if (capability !== "available") {
|
|
13902
|
+
return { state: "unavailable", reason: "\u53CD\u9988\u80FD\u529B\u672A\u62A5\u544A" };
|
|
13903
|
+
}
|
|
13904
|
+
return state ?? { state: "no_open_request", source: `${harness} explicit feedback signals` };
|
|
13905
|
+
}
|
|
13425
13906
|
function derive(harness, session, nowIso) {
|
|
13426
13907
|
const codexLines = sortedCodexByTimestamp(session.codexLines);
|
|
13427
13908
|
const lines = harness === "codex" ? adaptCodexNarrativeLines(codexLines) : sortedByTimestamp(session.lines);
|
|
@@ -13509,6 +13990,30 @@ function derive(harness, session, nowIso) {
|
|
|
13509
13990
|
};
|
|
13510
13991
|
});
|
|
13511
13992
|
const textTodos = todos.length === 0 ? deriveTextTodos(lines) : void 0;
|
|
13993
|
+
const currentAction = deriveCurrentAction(steps, session.lastEvent, session.pendingHookTool);
|
|
13994
|
+
const lastPrompt = deriveLastPrompt(lines, session.hookPrompt, session.hookPromptAt);
|
|
13995
|
+
const workflowAction = currentAction === null ? void 0 : currentAction.kind === "tool" ? {
|
|
13996
|
+
text: [currentAction.toolName, currentAction.detail].filter((part) => part !== "").join(": "),
|
|
13997
|
+
source: "transcript / hook tool event",
|
|
13998
|
+
...currentAction.since === void 0 ? {} : { observedAt: currentAction.since }
|
|
13999
|
+
} : { text: currentAction.name, source: "hook event", observedAt: currentAction.at };
|
|
14000
|
+
const taskWorkflow = deriveTaskWorkflowView({
|
|
14001
|
+
harness,
|
|
14002
|
+
lines,
|
|
14003
|
+
codexLines,
|
|
14004
|
+
...lastPrompt === null ? {} : {
|
|
14005
|
+
latestInstruction: {
|
|
14006
|
+
text: lastPrompt,
|
|
14007
|
+
source: "transcript user message / UserPromptSubmit"
|
|
14008
|
+
}
|
|
14009
|
+
},
|
|
14010
|
+
execution: {
|
|
14011
|
+
...workflowAction === void 0 ? {} : { currentAction: workflowAction },
|
|
14012
|
+
evidenceCount: steps.length + session.waitEpisodes.length + (session.waiting === null ? 0 : 1),
|
|
14013
|
+
failureCount: steps.filter((step) => step.isError).length
|
|
14014
|
+
},
|
|
14015
|
+
feedback: { state: "unavailable", reason: "\u53CD\u9988\u80FD\u529B\u5C1A\u672A\u6620\u5C04" }
|
|
14016
|
+
});
|
|
13512
14017
|
return {
|
|
13513
14018
|
harness,
|
|
13514
14019
|
sessionId: session.id,
|
|
@@ -13518,11 +14023,12 @@ function derive(harness, session, nowIso) {
|
|
|
13518
14023
|
...textTodos !== void 0 && textTodos.length > 0 ? { textTodos } : {},
|
|
13519
14024
|
traces,
|
|
13520
14025
|
turnUsages: harness === "codex" ? deriveCodexTurnUsages(codexLines) : deriveTurnUsages(lines),
|
|
13521
|
-
currentAction
|
|
14026
|
+
currentAction,
|
|
13522
14027
|
waiting: session.waiting,
|
|
13523
14028
|
agentQuote: deriveAgentQuote(lines),
|
|
13524
14029
|
// transcript 是会话事实源;hook prompt 只在 tail 尚未读到/格式暂不可读时兜底。
|
|
13525
|
-
lastPrompt
|
|
14030
|
+
lastPrompt,
|
|
14031
|
+
taskWorkflow,
|
|
13526
14032
|
updatedAt: nowIso
|
|
13527
14033
|
};
|
|
13528
14034
|
}
|
|
@@ -13561,22 +14067,26 @@ function deriveTextTodos(lines) {
|
|
|
13561
14067
|
return void 0;
|
|
13562
14068
|
}
|
|
13563
14069
|
function sortedByTimestamp(lines) {
|
|
13564
|
-
return
|
|
13565
|
-
const ta = a.kind === "unknown" ? void 0 : a.timestamp;
|
|
13566
|
-
const tb = b.kind === "unknown" ? void 0 : b.timestamp;
|
|
13567
|
-
if (ta === void 0 || tb === void 0)
|
|
13568
|
-
return 0;
|
|
13569
|
-
return ta < tb ? -1 : ta > tb ? 1 : 0;
|
|
13570
|
-
});
|
|
14070
|
+
return sortByInheritedTimestamp(lines, (line) => line.kind === "unknown" ? void 0 : line.timestamp, (line) => line.kind === "unknown" ? "" : line.agentId ?? "");
|
|
13571
14071
|
}
|
|
13572
14072
|
function sortedCodexByTimestamp(lines) {
|
|
13573
|
-
return
|
|
13574
|
-
|
|
13575
|
-
|
|
13576
|
-
|
|
13577
|
-
|
|
13578
|
-
|
|
13579
|
-
|
|
14073
|
+
return sortByInheritedTimestamp(lines, (line) => line.kind === "unknown" ? void 0 : line.timestamp);
|
|
14074
|
+
}
|
|
14075
|
+
function sortByInheritedTimestamp(lines, timestampOf, sourceOf = () => "") {
|
|
14076
|
+
const previousBySource = /* @__PURE__ */ new Map();
|
|
14077
|
+
return lines.map((line, index) => {
|
|
14078
|
+
const source = sourceOf(line);
|
|
14079
|
+
const timestamp = timestampOf(line) ?? previousBySource.get(source);
|
|
14080
|
+
if (timestamp !== void 0)
|
|
14081
|
+
previousBySource.set(source, timestamp);
|
|
14082
|
+
return { line, index, timestamp };
|
|
14083
|
+
}).sort((a, b) => {
|
|
14084
|
+
if (a.timestamp === void 0)
|
|
14085
|
+
return b.timestamp === void 0 ? a.index - b.index : -1;
|
|
14086
|
+
if (b.timestamp === void 0)
|
|
14087
|
+
return 1;
|
|
14088
|
+
return a.timestamp < b.timestamp ? -1 : a.timestamp > b.timestamp ? 1 : a.index - b.index;
|
|
14089
|
+
}).map(({ line }) => line);
|
|
13580
14090
|
}
|
|
13581
14091
|
function adaptCodexNarrativeLines(lines) {
|
|
13582
14092
|
const responseTexts = new Set(lines.flatMap((line) => line.kind === "assistant" && line.source === "response_item" && line.text !== void 0 ? [line.text] : []));
|
|
@@ -13990,12 +14500,16 @@ function errorBrief(content) {
|
|
|
13990
14500
|
|
|
13991
14501
|
// apps/cli/dist/registry.js
|
|
13992
14502
|
import { existsSync as existsSync10 } from "node:fs";
|
|
13993
|
-
|
|
14503
|
+
import { homedir as homedir9 } from "node:os";
|
|
14504
|
+
async function importHarnessModule(harness) {
|
|
13994
14505
|
const mod = await importAdapterModule(harness);
|
|
13995
14506
|
if (typeof mod.createAdapter !== "function") {
|
|
13996
14507
|
throw new Error(`adapter-${harness} \u5C1A\u672A\u5BFC\u51FA createAdapter(M1 \u5E76\u884C\u7EBF\u672A\u5408\u5E76?)`);
|
|
13997
14508
|
}
|
|
13998
|
-
|
|
14509
|
+
if (typeof mod.feedbackSignals !== "function") {
|
|
14510
|
+
throw new Error(`adapter-${harness} \u5C1A\u672A\u5BFC\u51FA feedbackSignals`);
|
|
14511
|
+
}
|
|
14512
|
+
return { createAdapter: mod.createAdapter, feedbackSignals: mod.feedbackSignals };
|
|
13999
14513
|
}
|
|
14000
14514
|
function importAdapterModule(harness) {
|
|
14001
14515
|
switch (harness) {
|
|
@@ -14021,19 +14535,20 @@ function fixtureScanOptions(harness) {
|
|
|
14021
14535
|
}
|
|
14022
14536
|
}
|
|
14023
14537
|
async function loadHarness(harness, mode) {
|
|
14024
|
-
const createAdapter4 = await
|
|
14538
|
+
const { createAdapter: createAdapter4, feedbackSignals } = await importHarnessModule(harness);
|
|
14025
14539
|
const adapter = harness === "dsh" && mode.fixtures ? createAdapter4({ dumpConfigPath: fixturePath("dsh", "dump-config.yaml") }) : createAdapter4();
|
|
14026
14540
|
return {
|
|
14027
14541
|
harness,
|
|
14028
14542
|
adapter,
|
|
14029
|
-
scanOptions: mode.fixtures ? fixtureScanOptions(harness) : {}
|
|
14543
|
+
scanOptions: mode.fixtures ? fixtureScanOptions(harness) : { homeDir: homedir9() },
|
|
14544
|
+
feedbackSignals
|
|
14030
14545
|
};
|
|
14031
14546
|
}
|
|
14032
14547
|
|
|
14033
14548
|
// apps/cli/dist/runtime-state.js
|
|
14034
14549
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
14035
14550
|
import { closeSync, mkdirSync as mkdirSync6, openSync, readFileSync as readFileSync11, renameSync as renameSync3, rmSync as rmSync4, writeFileSync as writeFileSync7 } from "node:fs";
|
|
14036
|
-
import { homedir as
|
|
14551
|
+
import { homedir as homedir10 } from "node:os";
|
|
14037
14552
|
import { dirname as dirname11, join as join17 } from "node:path";
|
|
14038
14553
|
function createHookToken() {
|
|
14039
14554
|
return randomBytes2(32).toString("base64url");
|
|
@@ -14045,7 +14560,7 @@ function writeServeRuntimeState(port, token, options = {}) {
|
|
|
14045
14560
|
const pid = options.pid ?? process.pid;
|
|
14046
14561
|
const suffix = randomBytes2(8).toString("base64url");
|
|
14047
14562
|
const owner = `serve-${startedAtMs}-${pid}-${suffix}`;
|
|
14048
|
-
const path = join17(options.homeDir ??
|
|
14563
|
+
const path = join17(options.homeDir ?? homedir10(), ".wingman", "state", "serve", `${owner}.json`);
|
|
14049
14564
|
const tempPath = join17(dirname11(path), `.${owner}.tmp`);
|
|
14050
14565
|
const contents = Buffer.from(`${JSON.stringify({ version: 2, owner, pid, port, startedAtMs, token })}
|
|
14051
14566
|
`, "utf8");
|
|
@@ -14267,10 +14782,11 @@ var HOOK_IDENTITY_PATH = "/api/hook-target";
|
|
|
14267
14782
|
var HOOK_TOKEN_HEADER = "x-wingman-hook-token";
|
|
14268
14783
|
var HOOK_PROOF_HEADER = "x-wingman-hook-proof";
|
|
14269
14784
|
var PANEL_ACTION_HEADER = "x-wingman-panel-action";
|
|
14785
|
+
var LOCAL_HOST = /^(?:127\.0\.0\.1|localhost)(?::\d+)?$|^\[::1\](?::\d+)?$/i;
|
|
14270
14786
|
function createWingmanServer(options) {
|
|
14271
14787
|
const warn = options.warn ?? ((message) => console.error(message));
|
|
14272
14788
|
const runtime = { anatomy: null };
|
|
14273
|
-
return createServer((req, res) => {
|
|
14789
|
+
return createServer({ requireHostHeader: false }, (req, res) => {
|
|
14274
14790
|
handle(req, res, options, warn, runtime).catch((error) => {
|
|
14275
14791
|
warn(`[wingman] \u8BF7\u6C42\u5904\u7406\u5931\u8D25:${errorMessage2(error)}`);
|
|
14276
14792
|
if (!res.headersSent)
|
|
@@ -14290,11 +14806,15 @@ function listen(server, port) {
|
|
|
14290
14806
|
});
|
|
14291
14807
|
}
|
|
14292
14808
|
async function handle(req, res, options, warn, runtime) {
|
|
14809
|
+
if (typeof req.headers.host !== "string" || !LOCAL_HOST.test(req.headers.host)) {
|
|
14810
|
+
sendJson2(res, 421, { error: "Host \u53EA\u63A5\u53D7\u672C\u673A\u5730\u5740" });
|
|
14811
|
+
return;
|
|
14812
|
+
}
|
|
14293
14813
|
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
14294
14814
|
if (req.method !== "GET") {
|
|
14295
14815
|
const origin = req.headers.origin;
|
|
14296
14816
|
if (typeof origin === "string" && origin !== "" && !/^https?:\/\/(127\.0\.0\.1|localhost)(:\d+)?$/.test(origin)) {
|
|
14297
|
-
|
|
14817
|
+
sendJson2(res, 403, { error: `\u62D2\u7EDD\u5F02\u6E90\u5199\u8BF7\u6C42(Origin: ${origin})` });
|
|
14298
14818
|
return;
|
|
14299
14819
|
}
|
|
14300
14820
|
}
|
|
@@ -14353,7 +14873,7 @@ async function handle(req, res, options, warn, runtime) {
|
|
|
14353
14873
|
return;
|
|
14354
14874
|
}
|
|
14355
14875
|
if (req.headers[PANEL_ACTION_HEADER] !== "refresh-license") {
|
|
14356
|
-
|
|
14876
|
+
sendJson2(res, 403, { error: "\u5237\u65B0\u6388\u6743\u5FC5\u987B\u7531 Wingman \u9762\u677F\u4E2D\u7684\u6309\u94AE\u89E6\u53D1" });
|
|
14357
14877
|
return;
|
|
14358
14878
|
}
|
|
14359
14879
|
await handleLicenseRefresh(res, options);
|
|
@@ -14364,7 +14884,7 @@ async function handle(req, res, options, warn, runtime) {
|
|
|
14364
14884
|
return;
|
|
14365
14885
|
}
|
|
14366
14886
|
if (url.pathname === "/api/license") {
|
|
14367
|
-
|
|
14887
|
+
sendJson2(res, 200, publicLicenseStatus(licenseStatus(options.licenseOptions)));
|
|
14368
14888
|
return;
|
|
14369
14889
|
}
|
|
14370
14890
|
if (url.pathname === "/api/narrative") {
|
|
@@ -14374,7 +14894,7 @@ async function handle(req, res, options, warn, runtime) {
|
|
|
14374
14894
|
}
|
|
14375
14895
|
const harness = url.searchParams.get("harness");
|
|
14376
14896
|
if (typeof harness !== "string" || !HARNESS_IDS.includes(harness)) {
|
|
14377
|
-
|
|
14897
|
+
sendJson2(res, 400, {
|
|
14378
14898
|
error: `harness \u9700\u8981 ${HARNESS_IDS.join("|")}(?harness=<id>),\u5F97\u5230 ${String(harness)}`
|
|
14379
14899
|
});
|
|
14380
14900
|
return;
|
|
@@ -14390,7 +14910,7 @@ async function handle(req, res, options, warn, runtime) {
|
|
|
14390
14910
|
}
|
|
14391
14911
|
overview.states = states;
|
|
14392
14912
|
}
|
|
14393
|
-
|
|
14913
|
+
sendJson2(res, 200, overview);
|
|
14394
14914
|
return;
|
|
14395
14915
|
}
|
|
14396
14916
|
if (url.pathname === "/api/anatomy") {
|
|
@@ -14508,48 +15028,48 @@ function errorSummary(error) {
|
|
|
14508
15028
|
async function handleActionPlan(req, res, options) {
|
|
14509
15029
|
const body = await readJsonBody(req);
|
|
14510
15030
|
if (body === void 0) {
|
|
14511
|
-
|
|
15031
|
+
sendJson2(res, 400, { error: "\u8BF7\u6C42\u4F53\u5FC5\u987B\u662F\u5408\u6CD5 JSON" });
|
|
14512
15032
|
return;
|
|
14513
15033
|
}
|
|
14514
15034
|
const { harness, action } = body;
|
|
14515
15035
|
const loaded = pickHarness(options, harness);
|
|
14516
15036
|
if (!loaded) {
|
|
14517
|
-
|
|
15037
|
+
sendJson2(res, 400, { error: `harness \u9700\u8981 ${HARNESS_IDS.join("|")},\u5F97\u5230 ${String(harness)}` });
|
|
14518
15038
|
return;
|
|
14519
15039
|
}
|
|
14520
15040
|
if (!isToggleRequest(action)) {
|
|
14521
|
-
|
|
15041
|
+
sendJson2(res, 400, { error: 'action \u9700\u8981 {kind:"toggle", itemId, enable} \u5F62\u72B6' });
|
|
14522
15042
|
return;
|
|
14523
15043
|
}
|
|
14524
15044
|
try {
|
|
14525
15045
|
const plan = await loaded.adapter.planAction(action, loaded.scanOptions);
|
|
14526
|
-
|
|
15046
|
+
sendJson2(res, 200, plan);
|
|
14527
15047
|
} catch (error) {
|
|
14528
|
-
|
|
15048
|
+
sendJson2(res, 400, { error: errorMessage2(error) });
|
|
14529
15049
|
}
|
|
14530
15050
|
}
|
|
14531
15051
|
async function handleActionApply(req, res, options, runtime) {
|
|
14532
15052
|
if (options.readonlyActions) {
|
|
14533
|
-
|
|
15053
|
+
sendJson2(res, 403, { error: "fixtures \u6A21\u5F0F\u53EA\u8BFB:\u52A8\u4F5C\u53EA\u5230 plan \u4E3A\u6B62,\u4E0D\u5199\u4EFB\u4F55\u6587\u4EF6" });
|
|
14534
15054
|
return;
|
|
14535
15055
|
}
|
|
14536
15056
|
if (!checkLicense(options.licenseOptions)) {
|
|
14537
|
-
|
|
15057
|
+
sendJson2(res, 403, { error: "\u9700\u8981\u6709\u6548\u8BA2\u9605\u624D\u80FD\u6267\u884C\u5199\u52A8\u4F5C(anatomy \u4E0E\u5B9E\u51B5\u6C38\u4E45\u514D\u8D39)" });
|
|
14538
15058
|
return;
|
|
14539
15059
|
}
|
|
14540
15060
|
const body = await readJsonBody(req);
|
|
14541
15061
|
if (body === void 0) {
|
|
14542
|
-
|
|
15062
|
+
sendJson2(res, 400, { error: "\u8BF7\u6C42\u4F53\u5FC5\u987B\u662F\u5408\u6CD5 JSON" });
|
|
14543
15063
|
return;
|
|
14544
15064
|
}
|
|
14545
15065
|
const { harness, plan } = body;
|
|
14546
15066
|
const loaded = pickHarness(options, harness);
|
|
14547
15067
|
if (!loaded) {
|
|
14548
|
-
|
|
15068
|
+
sendJson2(res, 400, { error: `harness \u9700\u8981 ${HARNESS_IDS.join("|")},\u5F97\u5230 ${String(harness)}` });
|
|
14549
15069
|
return;
|
|
14550
15070
|
}
|
|
14551
15071
|
if (typeof plan !== "object" || plan === null) {
|
|
14552
|
-
|
|
15072
|
+
sendJson2(res, 400, { error: "plan \u9700\u8981 ActionPlan \u5BF9\u8C61(\u7531 /api/action/plan \u4EA7\u51FA)" });
|
|
14553
15073
|
return;
|
|
14554
15074
|
}
|
|
14555
15075
|
try {
|
|
@@ -14557,24 +15077,24 @@ async function handleActionApply(req, res, options, runtime) {
|
|
|
14557
15077
|
if (result.ok) {
|
|
14558
15078
|
runtime.anatomy = null;
|
|
14559
15079
|
}
|
|
14560
|
-
|
|
15080
|
+
sendJson2(res, 200, result);
|
|
14561
15081
|
} catch (error) {
|
|
14562
|
-
|
|
15082
|
+
sendJson2(res, 400, { error: errorMessage2(error) });
|
|
14563
15083
|
}
|
|
14564
15084
|
}
|
|
14565
15085
|
var LICENSE_BODY_LIMIT = 64 * 1024;
|
|
14566
15086
|
async function handleLicenseInstall(req, res, options) {
|
|
14567
15087
|
const raw = await readTextBody(req, LICENSE_BODY_LIMIT);
|
|
14568
15088
|
if (raw === void 0) {
|
|
14569
|
-
|
|
15089
|
+
sendJson2(res, 400, { error: "license \u6587\u4EF6\u8D85\u8FC7 64KB \u4E0A\u9650\u6216\u8BFB\u53D6\u5931\u8D25" });
|
|
14570
15090
|
return;
|
|
14571
15091
|
}
|
|
14572
15092
|
const result = installLicense(raw, options.licenseOptions);
|
|
14573
15093
|
if (!result.ok) {
|
|
14574
|
-
|
|
15094
|
+
sendJson2(res, 400, { error: result.error });
|
|
14575
15095
|
return;
|
|
14576
15096
|
}
|
|
14577
|
-
|
|
15097
|
+
sendJson2(res, 200, publicLicenseStatus(result.status));
|
|
14578
15098
|
}
|
|
14579
15099
|
async function handleLicenseRefresh(res, options) {
|
|
14580
15100
|
const homeDir = options.licenseRefreshOptions?.homeDir ?? options.licenseOptions?.homeDir;
|
|
@@ -14588,7 +15108,7 @@ async function handleLicenseRefresh(res, options) {
|
|
|
14588
15108
|
};
|
|
14589
15109
|
const result = await refreshLicense(refreshOptions);
|
|
14590
15110
|
if (!result.ok) {
|
|
14591
|
-
|
|
15111
|
+
sendJson2(res, 400, {
|
|
14592
15112
|
error: result.message,
|
|
14593
15113
|
canSubscribe: result.reason === "inactive"
|
|
14594
15114
|
});
|
|
@@ -14600,7 +15120,7 @@ async function handleLicenseRefresh(res, options) {
|
|
|
14600
15120
|
...publicKeyPem === void 0 ? {} : { publicKeyPem },
|
|
14601
15121
|
...now === void 0 ? {} : { now }
|
|
14602
15122
|
};
|
|
14603
|
-
|
|
15123
|
+
sendJson2(res, 200, {
|
|
14604
15124
|
...publicLicenseStatus(licenseStatus(statusOptions)),
|
|
14605
15125
|
message: result.message
|
|
14606
15126
|
});
|
|
@@ -14665,7 +15185,7 @@ function readJsonBody(req) {
|
|
|
14665
15185
|
req.on("error", () => resolveBody(void 0));
|
|
14666
15186
|
});
|
|
14667
15187
|
}
|
|
14668
|
-
function
|
|
15188
|
+
function sendJson2(res, status, body) {
|
|
14669
15189
|
res.writeHead(status, { "content-type": "application/json; charset=utf-8" });
|
|
14670
15190
|
res.end(JSON.stringify(body));
|
|
14671
15191
|
}
|
|
@@ -14798,7 +15318,13 @@ async function runServe(args) {
|
|
|
14798
15318
|
}
|
|
14799
15319
|
}
|
|
14800
15320
|
const panelDistDir = panelDist === void 0 ? resolve6(dirname12(fileURLToPath2(import.meta.url)), "../../panel/dist") : resolve6(panelDist);
|
|
14801
|
-
const narrative = createNarrativeService({
|
|
15321
|
+
const narrative = createNarrativeService({
|
|
15322
|
+
followTranscripts: !fixtures,
|
|
15323
|
+
feedbackSignals: (harness) => {
|
|
15324
|
+
const loaded = harnesses.find((candidate) => candidate.harness === harness);
|
|
15325
|
+
return loaded?.feedbackSignals(loaded.scanOptions).feedback;
|
|
15326
|
+
}
|
|
15327
|
+
});
|
|
14802
15328
|
if (fixtures) {
|
|
14803
15329
|
try {
|
|
14804
15330
|
const raw = readFileSync12(fixturePath("cc", "sessions", "session-todos.jsonl"), "utf8");
|
|
@@ -14860,9 +15386,9 @@ async function runHook(args) {
|
|
|
14860
15386
|
throw new UsageError(`hook \u4E0D\u8BA4\u8BC6\u53C2\u6570:${arg}`);
|
|
14861
15387
|
}
|
|
14862
15388
|
}
|
|
14863
|
-
const { adapter } = await loadHarness(harness, { fixtures: false });
|
|
15389
|
+
const { adapter, scanOptions } = await loadHarness(harness, { fixtures: false });
|
|
14864
15390
|
if (action === "install") {
|
|
14865
|
-
await adapter.installHooks(port);
|
|
15391
|
+
await adapter.installHooks(port, scanOptions);
|
|
14866
15392
|
if (harness === "codex") {
|
|
14867
15393
|
process.stdout.write("wingman:codex \u914D\u7F6E\u5B8C\u6210\uFF1B\u9996\u6B21\u63A5\u7EBF\u8BF7\u5728 Codex \u8F93\u5165 /hooks\uFF0C\u5C06 Wingman hooks \u8BBE\u4E3A Trusted + Enabled\u3002\u6388\u6743\u540E\u65B0\u4E8B\u4EF6\u5E94\u8FDB\u5165 Wingman\uFF1B\u82E5\u5F53\u524D\u4EFB\u52A1\u4ECD\u65E0\u4E8B\u4EF6\uFF0C\u8BF7\u5B8C\u6574\u9000\u51FA\u91CD\u5F00\u540E\u7528\u65B0\u4EFB\u52A1\u9A8C\u8BC1\u3002\n\u5347\u7EA7\u65F6\u53EA\u8981\u53D7\u7BA1 command \u4FDD\u6301\u4E0D\u53D8\uFF0C\u65E0\u9700\u91CD\u590D\u4FE1\u4EFB\uFF1Bcommand \u53D8\u5316\u6216\u53D7\u7BA1\u5757\u6B8B\u7F3A/\u88AB\u4FEE\u6539\u65F6\u4F8B\u5916\u3002\n\u5199\u524D\u5DF2\u5907\u4EFD;\u91CD\u590D\u6267\u884C\u5E42\u7B49;\u89E3\u7EBF:wingman hook uninstall codex\n");
|
|
14868
15394
|
} else {
|
|
@@ -14871,7 +15397,7 @@ async function runHook(args) {
|
|
|
14871
15397
|
`);
|
|
14872
15398
|
}
|
|
14873
15399
|
} else {
|
|
14874
|
-
await adapter.uninstallHooks();
|
|
15400
|
+
await adapter.uninstallHooks(scanOptions);
|
|
14875
15401
|
process.stdout.write(`wingman:${harness} \u5DF2\u89E3\u7EBF,\u914D\u7F6E\u5DF2\u6062\u590D\u3002
|
|
14876
15402
|
`);
|
|
14877
15403
|
}
|