harness-wingman 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/cli/dist/main.js +447 -73
- package/apps/panel/dist/assets/index-CA8Z9FKi.js +225 -0
- package/apps/panel/dist/assets/index-CPB2aToL.css +1 -0
- package/apps/panel/dist/index.html +2 -2
- package/package.json +2 -2
- package/apps/panel/dist/assets/index-B8Ew9Kx1.js +0 -216
- package/apps/panel/dist/assets/index-DWuwFRYW.css +0 -1
package/apps/cli/dist/main.js
CHANGED
|
@@ -3239,7 +3239,7 @@ var init_actions2 = __esm({
|
|
|
3239
3239
|
|
|
3240
3240
|
// packages/adapter-codex/dist/hooks.js
|
|
3241
3241
|
import { spawnSync } from "node:child_process";
|
|
3242
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync9, rmSync as rmSync4, writeFileSync as writeFileSync7 } from "node:fs";
|
|
3242
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync9, realpathSync, rmSync as rmSync4, writeFileSync as writeFileSync7 } from "node:fs";
|
|
3243
3243
|
import { basename as basename5, dirname as dirname8, join as join12 } from "node:path";
|
|
3244
3244
|
async function installHooks(port, options) {
|
|
3245
3245
|
if (!Number.isInteger(port) || port <= 0 || port > 65535) {
|
|
@@ -3395,25 +3395,51 @@ function restoreTarget(remainder, state) {
|
|
|
3395
3395
|
}
|
|
3396
3396
|
function resolveNodeExecutable(probe = {}) {
|
|
3397
3397
|
const execPath = probe.execPath ?? process.execPath;
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3398
|
+
const exists = probe.exists ?? existsSync8;
|
|
3399
|
+
let executable = isNodeExecutable(execPath) ? execPath : void 0;
|
|
3400
|
+
if (executable === void 0) {
|
|
3401
|
+
const loginShellWhich = probe.loginShellWhich ?? defaultLoginShellWhich;
|
|
3402
|
+
try {
|
|
3403
|
+
const shellNode = loginShellWhich(probe.shell ?? process.env.SHELL ?? "/bin/sh")?.trim();
|
|
3404
|
+
if (shellNode !== void 0 && shellNode !== "node" && isNodeExecutable(shellNode)) {
|
|
3405
|
+
executable = shellNode;
|
|
3406
|
+
}
|
|
3407
|
+
} catch {
|
|
3405
3408
|
}
|
|
3406
|
-
} catch {
|
|
3407
3409
|
}
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
+
if (executable === void 0) {
|
|
3411
|
+
for (const candidate of ["/opt/homebrew/bin/node", "/usr/local/bin/node"]) {
|
|
3412
|
+
try {
|
|
3413
|
+
if (exists(candidate) && isNodeExecutable(candidate)) {
|
|
3414
|
+
executable = candidate;
|
|
3415
|
+
break;
|
|
3416
|
+
}
|
|
3417
|
+
} catch {
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3421
|
+
if (executable === void 0)
|
|
3422
|
+
return "node";
|
|
3423
|
+
const realpath2 = probe.realpath ?? defaultRealpath;
|
|
3424
|
+
for (const alias of ["/opt/homebrew/bin/node", "/usr/local/bin/node"]) {
|
|
3410
3425
|
try {
|
|
3411
|
-
if (exists(
|
|
3412
|
-
|
|
3426
|
+
if (!exists(alias))
|
|
3427
|
+
continue;
|
|
3428
|
+
const executableRealpath = realpath2(executable);
|
|
3429
|
+
if (executableRealpath !== void 0 && realpath2(alias) === executableRealpath) {
|
|
3430
|
+
return alias;
|
|
3431
|
+
}
|
|
3413
3432
|
} catch {
|
|
3414
3433
|
}
|
|
3415
3434
|
}
|
|
3416
|
-
return
|
|
3435
|
+
return executable;
|
|
3436
|
+
}
|
|
3437
|
+
function defaultRealpath(path) {
|
|
3438
|
+
try {
|
|
3439
|
+
return realpathSync(path);
|
|
3440
|
+
} catch {
|
|
3441
|
+
return void 0;
|
|
3442
|
+
}
|
|
3417
3443
|
}
|
|
3418
3444
|
function defaultLoginShellWhich(shell) {
|
|
3419
3445
|
const result4 = spawnSync(shell, ["-lc", "command -v node"], {
|
|
@@ -12905,9 +12931,9 @@ var init_dist5 = __esm({
|
|
|
12905
12931
|
|
|
12906
12932
|
// apps/cli/dist/main.js
|
|
12907
12933
|
init_dist();
|
|
12908
|
-
import { readFileSync as
|
|
12934
|
+
import { readFileSync as readFileSync17 } from "node:fs";
|
|
12909
12935
|
import { homedir as homedir13 } from "node:os";
|
|
12910
|
-
import { dirname as
|
|
12936
|
+
import { dirname as dirname17, resolve as resolve11 } from "node:path";
|
|
12911
12937
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
12912
12938
|
|
|
12913
12939
|
// packages/fixtures/dist/index.js
|
|
@@ -13308,7 +13334,7 @@ init_dist4();
|
|
|
13308
13334
|
init_dist5();
|
|
13309
13335
|
init_dist();
|
|
13310
13336
|
import { readdir } from "node:fs/promises";
|
|
13311
|
-
import { isAbsolute as isAbsolute4, join as
|
|
13337
|
+
import { isAbsolute as isAbsolute4, join as join21, resolve as resolve9 } from "node:path";
|
|
13312
13338
|
|
|
13313
13339
|
// packages/narrative/dist/attribute.js
|
|
13314
13340
|
function extractSteps(lines2) {
|
|
@@ -13882,7 +13908,7 @@ function parseProjectPlan(input, facts = {}) {
|
|
|
13882
13908
|
}
|
|
13883
13909
|
let title;
|
|
13884
13910
|
let goal;
|
|
13885
|
-
|
|
13911
|
+
const ignoredLines = [];
|
|
13886
13912
|
let fence;
|
|
13887
13913
|
let stepCount = 0;
|
|
13888
13914
|
let lastStep;
|
|
@@ -13892,7 +13918,7 @@ function parseProjectPlan(input, facts = {}) {
|
|
|
13892
13918
|
const lineNumber = index + 1;
|
|
13893
13919
|
const fenceMatch = FENCE_LINE.exec(line);
|
|
13894
13920
|
if (fence !== void 0) {
|
|
13895
|
-
|
|
13921
|
+
ignoredLines.push(ignoredLine(lineNumber, line));
|
|
13896
13922
|
if (fenceMatch?.[1] === fence)
|
|
13897
13923
|
fence = void 0;
|
|
13898
13924
|
lastStep = void 0;
|
|
@@ -13900,7 +13926,7 @@ function parseProjectPlan(input, facts = {}) {
|
|
|
13900
13926
|
}
|
|
13901
13927
|
if (fenceMatch !== null) {
|
|
13902
13928
|
fence = fenceMatch[1];
|
|
13903
|
-
|
|
13929
|
+
ignoredLines.push(ignoredLine(lineNumber, line));
|
|
13904
13930
|
lastStep = void 0;
|
|
13905
13931
|
continue;
|
|
13906
13932
|
}
|
|
@@ -13910,8 +13936,9 @@ function parseProjectPlan(input, facts = {}) {
|
|
|
13910
13936
|
if (headingMatch !== null) {
|
|
13911
13937
|
if (title === void 0)
|
|
13912
13938
|
title = headingMatch[1];
|
|
13913
|
-
else
|
|
13914
|
-
|
|
13939
|
+
else {
|
|
13940
|
+
ignoredLines.push(ignoredLine(lineNumber, line));
|
|
13941
|
+
}
|
|
13915
13942
|
lastStep = void 0;
|
|
13916
13943
|
continue;
|
|
13917
13944
|
}
|
|
@@ -13951,7 +13978,7 @@ function parseProjectPlan(input, facts = {}) {
|
|
|
13951
13978
|
lastStep = { indent, line: lineNumber, node };
|
|
13952
13979
|
continue;
|
|
13953
13980
|
}
|
|
13954
|
-
|
|
13981
|
+
ignoredLines.push(ignoredLine(lineNumber, line));
|
|
13955
13982
|
lastStep = void 0;
|
|
13956
13983
|
}
|
|
13957
13984
|
assignNodeKeys(nodes);
|
|
@@ -13960,7 +13987,8 @@ function parseProjectPlan(input, facts = {}) {
|
|
|
13960
13987
|
version: 1,
|
|
13961
13988
|
...title === void 0 ? {} : { title },
|
|
13962
13989
|
...goal === void 0 ? {} : { goal },
|
|
13963
|
-
ignoredLineCount,
|
|
13990
|
+
ignoredLineCount: ignoredLines.length,
|
|
13991
|
+
ignoredLines,
|
|
13964
13992
|
nodes
|
|
13965
13993
|
};
|
|
13966
13994
|
}
|
|
@@ -13996,7 +14024,8 @@ function deriveProjectPlanSummary(nodes) {
|
|
|
13996
14024
|
};
|
|
13997
14025
|
const allLeavesDone = leaves.length > 0 && leaves.every((node) => node.status === "completed" || node.status === "cancelled");
|
|
13998
14026
|
return {
|
|
13999
|
-
currentPointLabel: currentPoints.length === 0 ? "\
|
|
14027
|
+
currentPointLabel: currentPoints.length === 0 ? "\u5F53\u524D\u70B9" : currentPoints.length === 1 ? "\u5F53\u524D\u70B9" : `\u5E76\u884C ${currentPoints.length} \u9879`,
|
|
14028
|
+
...currentPoints.length === 0 ? { currentPointEmptyText: "\u8BA1\u5212\u672A\u6807\u8BB0\u8FDB\u884C\u4E2D\u6B65\u9AA4" } : {},
|
|
14000
14029
|
currentPoints,
|
|
14001
14030
|
checkpoints,
|
|
14002
14031
|
counts,
|
|
@@ -14040,6 +14069,9 @@ function validateFileFacts(facts) {
|
|
|
14040
14069
|
function unavailable(reason, line) {
|
|
14041
14070
|
return { availability: "unavailable", reason, ...line === void 0 ? {} : { line } };
|
|
14042
14071
|
}
|
|
14072
|
+
function ignoredLine(line, text) {
|
|
14073
|
+
return { line, preview: Array.from(text).slice(0, 40).join("") };
|
|
14074
|
+
}
|
|
14043
14075
|
function indentColumns(indent) {
|
|
14044
14076
|
let columns = 0;
|
|
14045
14077
|
for (const character of indent)
|
|
@@ -14406,6 +14438,12 @@ function costUsd(usage, model) {
|
|
|
14406
14438
|
}
|
|
14407
14439
|
|
|
14408
14440
|
// packages/narrative/dist/workflow.js
|
|
14441
|
+
var PLAN_MANAGEMENT_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
14442
|
+
"TodoWrite",
|
|
14443
|
+
"TaskCreate",
|
|
14444
|
+
"TaskUpdate",
|
|
14445
|
+
"update_plan"
|
|
14446
|
+
]);
|
|
14409
14447
|
function deriveTaskWorkflowView(input) {
|
|
14410
14448
|
return {
|
|
14411
14449
|
...input.goal === void 0 ? {} : { goal: input.goal },
|
|
@@ -14611,6 +14649,51 @@ function isRecord4(value) {
|
|
|
14611
14649
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14612
14650
|
}
|
|
14613
14651
|
|
|
14652
|
+
// packages/narrative/dist/verification.js
|
|
14653
|
+
var CHECK_STATS_SOURCE = "Harness tool_result(is_error / exit_code)";
|
|
14654
|
+
function deriveCheckStats(steps, briefs) {
|
|
14655
|
+
let runs = 0;
|
|
14656
|
+
let failures = 0;
|
|
14657
|
+
let latestFailureStep;
|
|
14658
|
+
for (const step of steps) {
|
|
14659
|
+
if (step.endTs === void 0 || PLAN_MANAGEMENT_TOOL_NAMES.has(step.toolName))
|
|
14660
|
+
continue;
|
|
14661
|
+
runs += 1;
|
|
14662
|
+
if (!step.isError)
|
|
14663
|
+
continue;
|
|
14664
|
+
failures += 1;
|
|
14665
|
+
if (latestFailureStep?.endTs === void 0 || latestFailureStep.endTs < step.endTs) {
|
|
14666
|
+
latestFailureStep = step;
|
|
14667
|
+
}
|
|
14668
|
+
}
|
|
14669
|
+
if (latestFailureStep?.endTs === void 0)
|
|
14670
|
+
return { runs, failures };
|
|
14671
|
+
const brief = briefs.get(latestFailureStep.toolUseId);
|
|
14672
|
+
return {
|
|
14673
|
+
runs,
|
|
14674
|
+
failures,
|
|
14675
|
+
latestFailure: {
|
|
14676
|
+
toolName: latestFailureStep.toolName,
|
|
14677
|
+
...brief === void 0 ? {} : { brief },
|
|
14678
|
+
at: latestFailureStep.endTs
|
|
14679
|
+
}
|
|
14680
|
+
};
|
|
14681
|
+
}
|
|
14682
|
+
function deriveTodoCheckStats(attributed) {
|
|
14683
|
+
const stats = /* @__PURE__ */ new Map();
|
|
14684
|
+
for (const { step, attribution } of attributed) {
|
|
14685
|
+
if (attribution.kind !== "todo" || step.endTs === void 0 || PLAN_MANAGEMENT_TOOL_NAMES.has(step.toolName)) {
|
|
14686
|
+
continue;
|
|
14687
|
+
}
|
|
14688
|
+
const current = stats.get(attribution.todoId) ?? { runs: 0, failures: 0 };
|
|
14689
|
+
current.runs += 1;
|
|
14690
|
+
if (step.isError)
|
|
14691
|
+
current.failures += 1;
|
|
14692
|
+
stats.set(attribution.todoId, current);
|
|
14693
|
+
}
|
|
14694
|
+
return stats;
|
|
14695
|
+
}
|
|
14696
|
+
|
|
14614
14697
|
// apps/cli/dist/project-attribution.js
|
|
14615
14698
|
function extractToolObservations(harness, records) {
|
|
14616
14699
|
const observations = [];
|
|
@@ -14950,18 +15033,171 @@ function isWithin(parent, child) {
|
|
|
14950
15033
|
return pathFromParent === "" || !pathFromParent.startsWith("..") && !isAbsolute3(pathFromParent);
|
|
14951
15034
|
}
|
|
14952
15035
|
|
|
15036
|
+
// apps/cli/dist/task-overlay.js
|
|
15037
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
15038
|
+
import { closeSync, mkdirSync as mkdirSync9, openSync, readFileSync as readFileSync14, renameSync as renameSync3, rmSync as rmSync7, writeFileSync as writeFileSync10 } from "node:fs";
|
|
15039
|
+
import { dirname as dirname15, join as join20 } from "node:path";
|
|
15040
|
+
function taskOverlayPath(homeDir) {
|
|
15041
|
+
return join20(homeDir, ".wingman", "state", "task-overlay.json");
|
|
15042
|
+
}
|
|
15043
|
+
function readTaskOverlay(homeDir) {
|
|
15044
|
+
const path = taskOverlayPath(homeDir);
|
|
15045
|
+
let raw;
|
|
15046
|
+
try {
|
|
15047
|
+
raw = readFileSync14(path, "utf8");
|
|
15048
|
+
} catch (error) {
|
|
15049
|
+
if (isErrno(error, "ENOENT"))
|
|
15050
|
+
return { version: 1, entries: {} };
|
|
15051
|
+
return { unavailable: `overlay \u6587\u4EF6\u65E0\u6CD5\u8BFB\u53D6;\u8BF7\u4EBA\u5DE5\u68C0\u67E5 ${path}:${errorMessage(error)}` };
|
|
15052
|
+
}
|
|
15053
|
+
try {
|
|
15054
|
+
const parsed = JSON.parse(raw);
|
|
15055
|
+
if (!isTaskOverlay(parsed))
|
|
15056
|
+
throw new Error("\u4E0D\u662F\u5408\u6CD5 v1 \u7ED3\u6784");
|
|
15057
|
+
return parsed;
|
|
15058
|
+
} catch (error) {
|
|
15059
|
+
return { unavailable: `overlay \u6587\u4EF6\u65E0\u6CD5\u89E3\u6790;\u8BF7\u4EBA\u5DE5\u68C0\u67E5 ${path}:${errorMessage(error)}` };
|
|
15060
|
+
}
|
|
15061
|
+
}
|
|
15062
|
+
function patchTaskOverlay(homeDir, key, patch, now) {
|
|
15063
|
+
const current = readTaskOverlay(homeDir);
|
|
15064
|
+
if ("unavailable" in current) {
|
|
15065
|
+
return {
|
|
15066
|
+
unavailable: `overlay \u6587\u4EF6\u65E0\u6CD5\u89E3\u6790,\u62D2\u7EDD\u8986\u76D6;\u8BF7\u4EBA\u5DE5\u68C0\u67E5 ${taskOverlayPath(homeDir)}`
|
|
15067
|
+
};
|
|
15068
|
+
}
|
|
15069
|
+
const entry = {
|
|
15070
|
+
...current.entries[key],
|
|
15071
|
+
updatedAt: now.toISOString()
|
|
15072
|
+
};
|
|
15073
|
+
if ("title" in patch) {
|
|
15074
|
+
const title = patch.title?.trim() ?? "";
|
|
15075
|
+
if (title === "")
|
|
15076
|
+
delete entry.title;
|
|
15077
|
+
else
|
|
15078
|
+
entry.title = title;
|
|
15079
|
+
}
|
|
15080
|
+
if (patch.pinned !== void 0)
|
|
15081
|
+
entry.pinned = patch.pinned;
|
|
15082
|
+
if (patch.hidden !== void 0)
|
|
15083
|
+
entry.hidden = patch.hidden;
|
|
15084
|
+
if (patch.accepted === true)
|
|
15085
|
+
entry.accepted = { at: now.toISOString() };
|
|
15086
|
+
else if (patch.accepted === false)
|
|
15087
|
+
delete entry.accepted;
|
|
15088
|
+
const empty = entry.title === void 0 && entry.pinned !== true && entry.hidden !== true && entry.accepted === void 0;
|
|
15089
|
+
const entries = { ...current.entries };
|
|
15090
|
+
if (empty)
|
|
15091
|
+
delete entries[key];
|
|
15092
|
+
else
|
|
15093
|
+
entries[key] = entry;
|
|
15094
|
+
writeTaskOverlay(homeDir, { version: 1, entries });
|
|
15095
|
+
return { entry: empty ? null : entry };
|
|
15096
|
+
}
|
|
15097
|
+
function writeTaskOverlay(homeDir, overlay) {
|
|
15098
|
+
const path = taskOverlayPath(homeDir);
|
|
15099
|
+
const tempPath = join20(dirname15(path), `.task-overlay-${randomBytes2(8).toString("hex")}.tmp`);
|
|
15100
|
+
let fd;
|
|
15101
|
+
try {
|
|
15102
|
+
mkdirSync9(dirname15(path), { recursive: true, mode: 448 });
|
|
15103
|
+
fd = openSync(tempPath, "wx", 384);
|
|
15104
|
+
writeFileSync10(fd, `${JSON.stringify(overlay)}
|
|
15105
|
+
`, "utf8");
|
|
15106
|
+
closeSync(fd);
|
|
15107
|
+
fd = void 0;
|
|
15108
|
+
renameSync3(tempPath, path);
|
|
15109
|
+
} catch (error) {
|
|
15110
|
+
if (fd !== void 0) {
|
|
15111
|
+
try {
|
|
15112
|
+
closeSync(fd);
|
|
15113
|
+
} catch {
|
|
15114
|
+
}
|
|
15115
|
+
}
|
|
15116
|
+
try {
|
|
15117
|
+
rmSync7(tempPath, { force: true });
|
|
15118
|
+
} catch {
|
|
15119
|
+
}
|
|
15120
|
+
throw error;
|
|
15121
|
+
}
|
|
15122
|
+
}
|
|
15123
|
+
function isTaskOverlay(value) {
|
|
15124
|
+
if (!isRecord6(value) || value.version !== 1 || !isRecord6(value.entries))
|
|
15125
|
+
return false;
|
|
15126
|
+
return Object.values(value.entries).every(isTaskOverlayEntry);
|
|
15127
|
+
}
|
|
15128
|
+
function isTaskOverlayEntry(value) {
|
|
15129
|
+
if (!isRecord6(value))
|
|
15130
|
+
return false;
|
|
15131
|
+
if (Object.keys(value).some((key) => !["title", "pinned", "hidden", "accepted", "updatedAt"].includes(key))) {
|
|
15132
|
+
return false;
|
|
15133
|
+
}
|
|
15134
|
+
return (value.title === void 0 || typeof value.title === "string") && (value.pinned === void 0 || typeof value.pinned === "boolean") && (value.hidden === void 0 || typeof value.hidden === "boolean") && (value.accepted === void 0 || isTaskAcceptance(value.accepted)) && typeof value.updatedAt === "string";
|
|
15135
|
+
}
|
|
15136
|
+
function isTaskAcceptance(value) {
|
|
15137
|
+
return isRecord6(value) && Object.keys(value).every((key) => key === "at") && typeof value.at === "string";
|
|
15138
|
+
}
|
|
15139
|
+
function isRecord6(value) {
|
|
15140
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15141
|
+
}
|
|
15142
|
+
function isErrno(error, code) {
|
|
15143
|
+
return isRecord6(error) && error.code === code;
|
|
15144
|
+
}
|
|
15145
|
+
function errorMessage(error) {
|
|
15146
|
+
return error instanceof Error ? error.message : String(error);
|
|
15147
|
+
}
|
|
15148
|
+
|
|
15149
|
+
// apps/cli/dist/version.js
|
|
15150
|
+
import { readFileSync as readFileSync15 } from "node:fs";
|
|
15151
|
+
function resolveWingmanVersion({ defined, moduleUrl = import.meta.url, readPackage }) {
|
|
15152
|
+
if (typeof defined === "string" && defined.trim() !== "")
|
|
15153
|
+
return defined;
|
|
15154
|
+
for (const packageUrl of [
|
|
15155
|
+
new URL("../package.json", moduleUrl),
|
|
15156
|
+
new URL("../../../package.json", moduleUrl)
|
|
15157
|
+
]) {
|
|
15158
|
+
try {
|
|
15159
|
+
const pkg = readPackage(packageUrl);
|
|
15160
|
+
if (typeof pkg === "object" && pkg !== null && "name" in pkg && pkg.name === "harness-wingman" && "version" in pkg && typeof pkg.version === "string") {
|
|
15161
|
+
return pkg.version;
|
|
15162
|
+
}
|
|
15163
|
+
} catch {
|
|
15164
|
+
}
|
|
15165
|
+
}
|
|
15166
|
+
return void 0;
|
|
15167
|
+
}
|
|
15168
|
+
function wingmanVersion() {
|
|
15169
|
+
const defined = false ? void 0 : "0.6.0";
|
|
15170
|
+
return resolveWingmanVersion({
|
|
15171
|
+
defined,
|
|
15172
|
+
readPackage: (url) => JSON.parse(readFileSync15(url, "utf8"))
|
|
15173
|
+
});
|
|
15174
|
+
}
|
|
15175
|
+
|
|
14953
15176
|
// apps/cli/dist/narrative-service.js
|
|
14954
15177
|
var MAX_SESSIONS = 8;
|
|
14955
15178
|
var ENDED_AFTER_STOP_MS = 10 * 6e4;
|
|
14956
|
-
var TODO_TOOLS = /* @__PURE__ */ new Set(["TaskCreate", "TaskUpdate", "TodoWrite", "update_plan"]);
|
|
14957
15179
|
var DETAIL_LIMIT = 80;
|
|
14958
15180
|
var QUOTE_LIMIT = 160;
|
|
14959
15181
|
var ERROR_BRIEF_LIMIT = 60;
|
|
15182
|
+
function acceptanceView(homeDir, overlay, key) {
|
|
15183
|
+
if (homeDir === void 0)
|
|
15184
|
+
return { state: "unavailable", reason: "\u672A\u914D\u7F6E\u672C\u673A\u72B6\u6001\u76EE\u5F55" };
|
|
15185
|
+
if (overlay === void 0 || "unavailable" in overlay) {
|
|
15186
|
+
return {
|
|
15187
|
+
state: "unavailable",
|
|
15188
|
+
reason: overlay?.unavailable ?? "\u672C\u673A\u9A8C\u6536\u72B6\u6001\u4E0D\u53EF\u8BFB"
|
|
15189
|
+
};
|
|
15190
|
+
}
|
|
15191
|
+
const accepted = overlay.entries[key]?.accepted;
|
|
15192
|
+
return accepted === void 0 ? { state: "none" } : { state: "accepted", at: accepted.at };
|
|
15193
|
+
}
|
|
14960
15194
|
function createNarrativeService(options = {}) {
|
|
14961
15195
|
const follow = options.followTranscripts ?? true;
|
|
14962
15196
|
const intervalMs = options.intervalMs ?? 500;
|
|
14963
15197
|
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
15198
|
+
const startedAt = now().toISOString();
|
|
14964
15199
|
const harnesses = /* @__PURE__ */ new Map();
|
|
15200
|
+
const notices = /* @__PURE__ */ new Map();
|
|
14965
15201
|
const listeners = /* @__PURE__ */ new Set();
|
|
14966
15202
|
const pendingNotifications = /* @__PURE__ */ new Set();
|
|
14967
15203
|
const projectWatchers = /* @__PURE__ */ new Map();
|
|
@@ -15045,7 +15281,7 @@ function createNarrativeService(options = {}) {
|
|
|
15045
15281
|
function watchResolution(resolution) {
|
|
15046
15282
|
if (resolution.lookedUp.length === 0)
|
|
15047
15283
|
return;
|
|
15048
|
-
const planPath = resolution.planPath ??
|
|
15284
|
+
const planPath = resolution.planPath ?? join21(resolution.root, ".wingman", "plan.md");
|
|
15049
15285
|
const planPathIdentity = resolution.planPathIdentity;
|
|
15050
15286
|
if (projectWatchers.has(planPathIdentity))
|
|
15051
15287
|
return;
|
|
@@ -15105,7 +15341,7 @@ function createNarrativeService(options = {}) {
|
|
|
15105
15341
|
for (const session of sessions.values()) {
|
|
15106
15342
|
const resolution = session.projectResolution;
|
|
15107
15343
|
if (resolution?.planPathIdentity === planPathIdentity) {
|
|
15108
|
-
const planPath = resolution.planPath ??
|
|
15344
|
+
const planPath = resolution.planPath ?? join21(resolution.root, ".wingman", "plan.md");
|
|
15109
15345
|
void applyPendingPlanWrites(session, planPath, planPathIdentity).then(() => {
|
|
15110
15346
|
session.dirty = true;
|
|
15111
15347
|
notify(harness);
|
|
@@ -15121,8 +15357,9 @@ function createNarrativeService(options = {}) {
|
|
|
15121
15357
|
function recordToolObservations(harness, session, tools) {
|
|
15122
15358
|
for (const tool of tools) {
|
|
15123
15359
|
if (tool.planWrites.length === 0) {
|
|
15124
|
-
if (!tool.planFileAccess && !
|
|
15360
|
+
if (!tool.planFileAccess && !PLAN_MANAGEMENT_TOOL_NAMES.has(tool.method)) {
|
|
15125
15361
|
session.nonPlanToolSteps += 1;
|
|
15362
|
+
}
|
|
15126
15363
|
continue;
|
|
15127
15364
|
}
|
|
15128
15365
|
recordPlanWrites(harness, session, tool.planWrites, session.nonPlanToolSteps);
|
|
@@ -15145,7 +15382,7 @@ function createNarrativeService(options = {}) {
|
|
|
15145
15382
|
}
|
|
15146
15383
|
const resolution = session.projectResolution;
|
|
15147
15384
|
if (resolution !== void 0) {
|
|
15148
|
-
const planPath = resolution.planPath ??
|
|
15385
|
+
const planPath = resolution.planPath ?? join21(resolution.root, ".wingman", "plan.md");
|
|
15149
15386
|
void applyPendingPlanWrites(session, planPath, resolution.planPathIdentity).then(() => {
|
|
15150
15387
|
session.dirty = true;
|
|
15151
15388
|
notify(harness);
|
|
@@ -15185,7 +15422,7 @@ function createNarrativeService(options = {}) {
|
|
|
15185
15422
|
}
|
|
15186
15423
|
session.pendingPlanWrites = remaining;
|
|
15187
15424
|
}
|
|
15188
|
-
function evictOne(sessions, nowIso) {
|
|
15425
|
+
function evictOne(harness, sessions, nowIso) {
|
|
15189
15426
|
let victimId;
|
|
15190
15427
|
let victimAt = "\uFFFF";
|
|
15191
15428
|
let victimEnded = false;
|
|
@@ -15201,8 +15438,20 @@ function createNarrativeService(options = {}) {
|
|
|
15201
15438
|
}
|
|
15202
15439
|
if (victimId !== void 0) {
|
|
15203
15440
|
const victim = sessions.get(victimId);
|
|
15204
|
-
if (victim)
|
|
15441
|
+
if (victim) {
|
|
15205
15442
|
closeSession(victim);
|
|
15443
|
+
const current = notices.get(harness) ?? [];
|
|
15444
|
+
current.push({
|
|
15445
|
+
kind: "evicted",
|
|
15446
|
+
sessionId: victim.id,
|
|
15447
|
+
label: victim.label ?? victim.id.slice(0, 8),
|
|
15448
|
+
at: nowIso,
|
|
15449
|
+
reason: "\u5DF2\u8FBE 8 \u4E2A\u4F1A\u8BDD\u4E0A\u9650"
|
|
15450
|
+
});
|
|
15451
|
+
if (current.length > 5)
|
|
15452
|
+
current.splice(0, current.length - 5);
|
|
15453
|
+
notices.set(harness, current);
|
|
15454
|
+
}
|
|
15206
15455
|
sessions.delete(victimId);
|
|
15207
15456
|
}
|
|
15208
15457
|
}
|
|
@@ -15211,7 +15460,7 @@ function createNarrativeService(options = {}) {
|
|
|
15211
15460
|
let session = sessions.get(id);
|
|
15212
15461
|
if (session === void 0) {
|
|
15213
15462
|
if (sessions.size >= MAX_SESSIONS)
|
|
15214
|
-
evictOne(sessions, now().toISOString());
|
|
15463
|
+
evictOne(harness, sessions, now().toISOString());
|
|
15215
15464
|
session = newSession(id);
|
|
15216
15465
|
sessions.set(id, session);
|
|
15217
15466
|
}
|
|
@@ -15247,7 +15496,7 @@ function createNarrativeService(options = {}) {
|
|
|
15247
15496
|
const sessionDir = transcriptPath.replace(/\.jsonl$/, "");
|
|
15248
15497
|
if (sessionDir === transcriptPath)
|
|
15249
15498
|
return;
|
|
15250
|
-
const subagentsDir =
|
|
15499
|
+
const subagentsDir = join21(sessionDir, "subagents");
|
|
15251
15500
|
const generation = session.generation;
|
|
15252
15501
|
const poll = async () => {
|
|
15253
15502
|
try {
|
|
@@ -15257,7 +15506,7 @@ function createNarrativeService(options = {}) {
|
|
|
15257
15506
|
}
|
|
15258
15507
|
for (const entry of entries) {
|
|
15259
15508
|
if (/^agent-.*\.jsonl$/.test(entry)) {
|
|
15260
|
-
startFollowing(harness, session,
|
|
15509
|
+
startFollowing(harness, session, join21(subagentsDir, entry), generation);
|
|
15261
15510
|
}
|
|
15262
15511
|
}
|
|
15263
15512
|
} catch {
|
|
@@ -15433,6 +15682,7 @@ function createNarrativeService(options = {}) {
|
|
|
15433
15682
|
function getOverview(harness) {
|
|
15434
15683
|
const nowIso = now().toISOString();
|
|
15435
15684
|
const sessions = sessionsOf(harness);
|
|
15685
|
+
const overlay = options.homeDir === void 0 ? void 0 : readTaskOverlay(options.homeDir);
|
|
15436
15686
|
let feedback;
|
|
15437
15687
|
try {
|
|
15438
15688
|
feedback = options.feedbackSignals?.(harness);
|
|
@@ -15451,15 +15701,28 @@ function createNarrativeService(options = {}) {
|
|
|
15451
15701
|
const states = {};
|
|
15452
15702
|
for (const [id, session] of sessions) {
|
|
15453
15703
|
const state = deriveCached(harness, session);
|
|
15704
|
+
const verification = state.taskWorkflow?.verification;
|
|
15454
15705
|
states[id] = state.taskWorkflow === void 0 ? state : {
|
|
15455
15706
|
...state,
|
|
15456
15707
|
taskWorkflow: {
|
|
15457
15708
|
...state.taskWorkflow,
|
|
15458
|
-
feedback: feedbackView(harness, feedback, session.feedbackState)
|
|
15709
|
+
feedback: feedbackView(harness, feedback, session.feedbackState),
|
|
15710
|
+
...verification === void 0 ? {} : {
|
|
15711
|
+
verification: {
|
|
15712
|
+
...verification,
|
|
15713
|
+
acceptance: acceptanceView(options.homeDir, overlay, `${harness}:${id}`)
|
|
15714
|
+
}
|
|
15715
|
+
}
|
|
15459
15716
|
}
|
|
15460
15717
|
};
|
|
15461
15718
|
}
|
|
15462
|
-
return {
|
|
15719
|
+
return {
|
|
15720
|
+
harness,
|
|
15721
|
+
startedAt,
|
|
15722
|
+
notices: [...notices.get(harness) ?? []],
|
|
15723
|
+
sessions: infos,
|
|
15724
|
+
states
|
|
15725
|
+
};
|
|
15463
15726
|
}
|
|
15464
15727
|
function getState(harness) {
|
|
15465
15728
|
const overview = getOverview(harness);
|
|
@@ -15533,6 +15796,8 @@ function createNarrativeService(options = {}) {
|
|
|
15533
15796
|
};
|
|
15534
15797
|
}
|
|
15535
15798
|
function getProjects() {
|
|
15799
|
+
const overlay = options.homeDir === void 0 ? void 0 : readTaskOverlay(options.homeDir);
|
|
15800
|
+
const overlayEntries = overlay === void 0 || "unavailable" in overlay ? {} : overlay.entries;
|
|
15536
15801
|
const grouped = /* @__PURE__ */ new Map();
|
|
15537
15802
|
for (const [harness, sessions] of harnesses) {
|
|
15538
15803
|
for (const session of sessions.values()) {
|
|
@@ -15553,7 +15818,7 @@ function createNarrativeService(options = {}) {
|
|
|
15553
15818
|
for (const members of grouped.values()) {
|
|
15554
15819
|
const resolution = members.find((member) => member.session.projectResolution !== void 0)?.session.projectResolution;
|
|
15555
15820
|
const root = resolution?.root ?? resolve9(members[0]?.session.cwd ?? "");
|
|
15556
|
-
const planPath = resolution?.planPath ??
|
|
15821
|
+
const planPath = resolution?.planPath ?? join21(root, ".wingman", "plan.md");
|
|
15557
15822
|
const runtime = projectFiles.get(resolution?.planPathIdentity ?? planPath);
|
|
15558
15823
|
const parsed = runtime?.missing === true ? void 0 : runtime?.parsed;
|
|
15559
15824
|
const planMtime = parsed?.availability === "available" ? runtime?.current?.info.mtime : void 0;
|
|
@@ -15566,11 +15831,31 @@ function createNarrativeService(options = {}) {
|
|
|
15566
15831
|
const overview = overviews.get(harness);
|
|
15567
15832
|
const state = overview?.states[session.id];
|
|
15568
15833
|
const info = overview?.sessions.find((candidate) => candidate.id === session.id);
|
|
15834
|
+
const label = session.label ?? session.id.slice(0, 8);
|
|
15835
|
+
const userEntry = overlayEntries[`${harness}:${session.id}`];
|
|
15836
|
+
const verification = state?.taskWorkflow?.verification;
|
|
15837
|
+
const attachedTitle = session.planAttachment?.attachedTo.split(" > ").at(-1);
|
|
15838
|
+
const instructionTitle = state?.taskWorkflow?.latestInstruction?.text;
|
|
15569
15839
|
return {
|
|
15570
15840
|
harness,
|
|
15571
15841
|
id: session.id,
|
|
15572
|
-
label
|
|
15842
|
+
label,
|
|
15573
15843
|
ended: info?.ended ?? false,
|
|
15844
|
+
title: attachedTitle !== void 0 ? { text: attachedTitle, source: "plan.md \u6B65\u9AA4" } : instructionTitle !== void 0 ? { text: truncate(instructionTitle, DETAIL_LIMIT), source: "\u6700\u65B0\u6307\u4EE4" } : { text: label, source: "\u5DE5\u4F5C\u76EE\u5F55" },
|
|
15845
|
+
user: {
|
|
15846
|
+
...userEntry?.title === void 0 ? {} : { title: userEntry.title },
|
|
15847
|
+
pinned: userEntry?.pinned ?? false,
|
|
15848
|
+
hidden: userEntry?.hidden ?? false
|
|
15849
|
+
},
|
|
15850
|
+
...session.cwd === void 0 ? {} : { cwd: session.cwd },
|
|
15851
|
+
...info?.lastActivity === void 0 ? {} : { lastActivity: info.lastActivity },
|
|
15852
|
+
waiting: info?.waiting ?? false,
|
|
15853
|
+
checks: verification?.checks ?? {
|
|
15854
|
+
runs: 0,
|
|
15855
|
+
failures: 0,
|
|
15856
|
+
source: CHECK_STATS_SOURCE
|
|
15857
|
+
},
|
|
15858
|
+
acceptance: acceptanceView(options.homeDir, overlay, `${harness}:${session.id}`),
|
|
15574
15859
|
compliance: complianceFor(session, protocol[harness]),
|
|
15575
15860
|
...session.planAttachment === void 0 ? {} : {
|
|
15576
15861
|
attachedTo: session.planAttachment.attachedTo,
|
|
@@ -15614,6 +15899,7 @@ function createNarrativeService(options = {}) {
|
|
|
15614
15899
|
plan = {
|
|
15615
15900
|
availability: "available",
|
|
15616
15901
|
ignoredLineCount: parsed.ignoredLineCount,
|
|
15902
|
+
ignoredLines: parsed.ignoredLines,
|
|
15617
15903
|
nodes: parsed.nodes,
|
|
15618
15904
|
higherPlanPaths: resolution?.higherPlanPaths ?? []
|
|
15619
15905
|
};
|
|
@@ -15774,9 +16060,11 @@ function waitingSignal(payload) {
|
|
|
15774
16060
|
}
|
|
15775
16061
|
function feedbackView(harness, capability, state) {
|
|
15776
16062
|
if (capability === "not_wired") {
|
|
16063
|
+
const version = wingmanVersion();
|
|
16064
|
+
const command = `npx harness-wingman${version === void 0 ? "" : `@${version}`} hook install ${harness}`;
|
|
15777
16065
|
return {
|
|
15778
16066
|
state: "unavailable",
|
|
15779
|
-
reason: `\u53CD\u9988\u4FE1\u53F7\u672A\u63A5\u7EBF\uFF1B\u8BF7\u8FD0\u884C
|
|
16067
|
+
reason: `\u53CD\u9988\u4FE1\u53F7\u672A\u63A5\u7EBF\uFF1B\u8BF7\u8FD0\u884C ${command}${version === void 0 ? "\uFF08\u7248\u672C\u672A\u77E5,\u53EF\u80FD\u88C5\u5230\u522B\u7684\u7248\u672C\uFF09" : ""}`
|
|
15780
16068
|
};
|
|
15781
16069
|
}
|
|
15782
16070
|
if (capability === "unavailable") {
|
|
@@ -15792,10 +16080,17 @@ function derive(harness, session, nowIso) {
|
|
|
15792
16080
|
const lines2 = harness === "codex" ? adaptCodexNarrativeLines(codexLines) : sortedByTimestamp(session.lines);
|
|
15793
16081
|
const timeline = deriveTodoTimeline(extractTodoSnapshots(lines2));
|
|
15794
16082
|
const allSteps = extractSteps(lines2);
|
|
15795
|
-
const steps = allSteps.filter((s) => !
|
|
16083
|
+
const steps = allSteps.filter((s) => !PLAN_MANAGEMENT_TOOL_NAMES.has(s.toolName));
|
|
15796
16084
|
const intervals = [...deriveSubagentIntervals(lines2), ...session.hookIntervals];
|
|
15797
16085
|
const attributed = attributeSteps(steps, timeline, intervals);
|
|
15798
16086
|
const results = collectResults(lines2);
|
|
16087
|
+
const resultBriefs = /* @__PURE__ */ new Map();
|
|
16088
|
+
for (const [toolUseId, result4] of results) {
|
|
16089
|
+
if (result4.brief !== void 0)
|
|
16090
|
+
resultBriefs.set(toolUseId, result4.brief);
|
|
16091
|
+
}
|
|
16092
|
+
const checks = deriveCheckStats(allSteps, resultBriefs);
|
|
16093
|
+
const todoChecks = deriveTodoCheckStats(attributed);
|
|
15799
16094
|
const buckets = /* @__PURE__ */ new Map();
|
|
15800
16095
|
for (const { step, attribution } of attributed) {
|
|
15801
16096
|
const key = attribution.kind === "todo" ? attribution.todoId : null;
|
|
@@ -15881,7 +16176,7 @@ function derive(harness, session, nowIso) {
|
|
|
15881
16176
|
source: "transcript / hook tool event",
|
|
15882
16177
|
...currentAction.since === void 0 ? {} : { observedAt: currentAction.since }
|
|
15883
16178
|
} : { text: currentAction.name, source: "hook event", observedAt: currentAction.at };
|
|
15884
|
-
const
|
|
16179
|
+
const workflowBase = deriveTaskWorkflowView({
|
|
15885
16180
|
harness,
|
|
15886
16181
|
lines: lines2,
|
|
15887
16182
|
codexLines,
|
|
@@ -15898,6 +16193,26 @@ function derive(harness, session, nowIso) {
|
|
|
15898
16193
|
},
|
|
15899
16194
|
feedback: { state: "unavailable", reason: "\u53CD\u9988\u80FD\u529B\u5C1A\u672A\u6620\u5C04" }
|
|
15900
16195
|
});
|
|
16196
|
+
const activeTodos = todos.filter((todo) => todo.status === "in_progress");
|
|
16197
|
+
const activeTodo = activeTodos.length === 1 ? activeTodos[0] : void 0;
|
|
16198
|
+
const activeChecks = activeTodo === void 0 ? void 0 : todoChecks.get(activeTodo.id);
|
|
16199
|
+
const plan = workflowBase.plan;
|
|
16200
|
+
const taskWorkflow = {
|
|
16201
|
+
...workflowBase,
|
|
16202
|
+
verification: {
|
|
16203
|
+
agentMarkedComplete: plan.availability === "available" && plan.steps.length > 0 && plan.steps.every((step) => step.status === "completed"),
|
|
16204
|
+
checks: { ...checks, source: CHECK_STATS_SOURCE },
|
|
16205
|
+
...activeTodo === void 0 ? {} : {
|
|
16206
|
+
currentStep: {
|
|
16207
|
+
text: activeTodo.content,
|
|
16208
|
+
runs: activeChecks?.runs ?? 0,
|
|
16209
|
+
failures: activeChecks?.failures ?? 0
|
|
16210
|
+
}
|
|
16211
|
+
},
|
|
16212
|
+
// 响应组装时从 overlay 重读并覆盖;检查与计划派生仍可安全缓存。
|
|
16213
|
+
acceptance: { state: "unavailable", reason: "\u9A8C\u6536\u72B6\u6001\u5728\u54CD\u5E94\u65F6\u8BFB\u53D6" }
|
|
16214
|
+
}
|
|
16215
|
+
};
|
|
15901
16216
|
return {
|
|
15902
16217
|
harness,
|
|
15903
16218
|
sessionId: session.id,
|
|
@@ -16574,45 +16889,45 @@ async function loadHarness(harness, mode) {
|
|
|
16574
16889
|
}
|
|
16575
16890
|
|
|
16576
16891
|
// apps/cli/dist/runtime-state.js
|
|
16577
|
-
import { randomBytes as
|
|
16578
|
-
import { closeSync, mkdirSync as
|
|
16892
|
+
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
16893
|
+
import { closeSync as closeSync2, mkdirSync as mkdirSync10, openSync as openSync2, readFileSync as readFileSync16, renameSync as renameSync4, rmSync as rmSync8, writeFileSync as writeFileSync11 } from "node:fs";
|
|
16579
16894
|
import { homedir as homedir12 } from "node:os";
|
|
16580
|
-
import { dirname as
|
|
16895
|
+
import { dirname as dirname16, join as join22 } from "node:path";
|
|
16581
16896
|
function createHookToken() {
|
|
16582
|
-
return
|
|
16897
|
+
return randomBytes3(32).toString("base64url");
|
|
16583
16898
|
}
|
|
16584
16899
|
function writeServeRuntimeState(port, token, options = {}) {
|
|
16585
16900
|
const warn = options.warn ?? (() => {
|
|
16586
16901
|
});
|
|
16587
16902
|
const startedAtMs = (options.now ?? Date.now)();
|
|
16588
16903
|
const pid = options.pid ?? process.pid;
|
|
16589
|
-
const suffix =
|
|
16904
|
+
const suffix = randomBytes3(8).toString("base64url");
|
|
16590
16905
|
const owner = `serve-${startedAtMs}-${pid}-${suffix}`;
|
|
16591
|
-
const path =
|
|
16592
|
-
const tempPath =
|
|
16906
|
+
const path = join22(options.homeDir ?? homedir12(), ".wingman", "state", "serve", `${owner}.json`);
|
|
16907
|
+
const tempPath = join22(dirname16(path), `.${owner}.tmp`);
|
|
16593
16908
|
const contents = Buffer.from(`${JSON.stringify({ version: 2, owner, pid, port, startedAtMs, token })}
|
|
16594
16909
|
`, "utf8");
|
|
16595
16910
|
let fd;
|
|
16596
16911
|
try {
|
|
16597
|
-
|
|
16598
|
-
fd =
|
|
16599
|
-
|
|
16600
|
-
|
|
16912
|
+
mkdirSync10(dirname16(path), { recursive: true, mode: 448 });
|
|
16913
|
+
fd = openSync2(tempPath, "wx", 384);
|
|
16914
|
+
writeFileSync11(fd, contents);
|
|
16915
|
+
closeSync2(fd);
|
|
16601
16916
|
fd = void 0;
|
|
16602
|
-
|
|
16917
|
+
renameSync4(tempPath, path);
|
|
16603
16918
|
return { path, contents };
|
|
16604
16919
|
} catch (error) {
|
|
16605
16920
|
if (fd !== void 0) {
|
|
16606
16921
|
try {
|
|
16607
|
-
|
|
16922
|
+
closeSync2(fd);
|
|
16608
16923
|
} catch {
|
|
16609
16924
|
}
|
|
16610
16925
|
}
|
|
16611
16926
|
try {
|
|
16612
|
-
|
|
16927
|
+
rmSync8(tempPath, { force: true });
|
|
16613
16928
|
} catch {
|
|
16614
16929
|
}
|
|
16615
|
-
warn(`Codex hook \u8FD0\u884C\u6001\u5199\u5165\u5931\u8D25,\u9762\u677F\u7EE7\u7EED\u8FD0\u884C:${
|
|
16930
|
+
warn(`Codex hook \u8FD0\u884C\u6001\u5199\u5165\u5931\u8D25,\u9762\u677F\u7EE7\u7EED\u8FD0\u884C:${errorMessage2(error)}`);
|
|
16616
16931
|
return void 0;
|
|
16617
16932
|
}
|
|
16618
16933
|
}
|
|
@@ -16620,13 +16935,13 @@ function removeServeRuntimeState(registration) {
|
|
|
16620
16935
|
if (registration === void 0)
|
|
16621
16936
|
return;
|
|
16622
16937
|
try {
|
|
16623
|
-
const current =
|
|
16938
|
+
const current = readFileSync16(registration.path);
|
|
16624
16939
|
if (current.equals(registration.contents))
|
|
16625
|
-
|
|
16940
|
+
rmSync8(registration.path, { force: true });
|
|
16626
16941
|
} catch {
|
|
16627
16942
|
}
|
|
16628
16943
|
}
|
|
16629
|
-
function
|
|
16944
|
+
function errorMessage2(error) {
|
|
16630
16945
|
return error instanceof Error ? error.message : String(error);
|
|
16631
16946
|
}
|
|
16632
16947
|
|
|
@@ -16816,7 +17131,7 @@ function createWingmanServer(options) {
|
|
|
16816
17131
|
const runtime = { anatomy: null };
|
|
16817
17132
|
return createServer({ requireHostHeader: false }, (req, res) => {
|
|
16818
17133
|
handle(req, res, options, warn, runtime).catch((error) => {
|
|
16819
|
-
warn(`[wingman] \u8BF7\u6C42\u5904\u7406\u5931\u8D25:${
|
|
17134
|
+
warn(`[wingman] \u8BF7\u6C42\u5904\u7406\u5931\u8D25:${errorMessage3(error)}`);
|
|
16820
17135
|
if (!res.headersSent)
|
|
16821
17136
|
sendText2(res, 500, "internal error");
|
|
16822
17137
|
else
|
|
@@ -16891,6 +17206,10 @@ async function handle(req, res, options, warn, runtime) {
|
|
|
16891
17206
|
await handleActionApply(req, res, options, runtime);
|
|
16892
17207
|
return;
|
|
16893
17208
|
}
|
|
17209
|
+
if (url.pathname === "/api/task-overlay" && req.method === "POST") {
|
|
17210
|
+
await handleTaskOverlay(req, res, options);
|
|
17211
|
+
return;
|
|
17212
|
+
}
|
|
16894
17213
|
if (url.pathname === "/api/license/install" && req.method === "POST") {
|
|
16895
17214
|
await handleLicenseInstall(req, res, options);
|
|
16896
17215
|
return;
|
|
@@ -16955,6 +17274,10 @@ async function handle(req, res, options, warn, runtime) {
|
|
|
16955
17274
|
res.end(JSON.stringify(configs));
|
|
16956
17275
|
return;
|
|
16957
17276
|
}
|
|
17277
|
+
if (url.pathname === "/api/version") {
|
|
17278
|
+
sendJson2(res, 200, { version: wingmanVersion() ?? null });
|
|
17279
|
+
return;
|
|
17280
|
+
}
|
|
16958
17281
|
if (url.pathname.startsWith("/api/")) {
|
|
16959
17282
|
sendText2(res, 404, "not found");
|
|
16960
17283
|
return;
|
|
@@ -17012,7 +17335,7 @@ async function scanAll(harnesses, warn) {
|
|
|
17012
17335
|
const detected = await adapter.detect(scanOptions);
|
|
17013
17336
|
installed = detected.installed;
|
|
17014
17337
|
} catch (error) {
|
|
17015
|
-
warn(`[wingman] ${harness} detect \u5931\u8D25,\u5DF2\u8DF3\u8FC7:${
|
|
17338
|
+
warn(`[wingman] ${harness} detect \u5931\u8D25,\u5DF2\u8DF3\u8FC7:${errorMessage3(error)}`);
|
|
17016
17339
|
continue;
|
|
17017
17340
|
}
|
|
17018
17341
|
if (!installed)
|
|
@@ -17020,7 +17343,7 @@ async function scanAll(harnesses, warn) {
|
|
|
17020
17343
|
try {
|
|
17021
17344
|
configs.push(await scanValidated(adapter, scanOptions));
|
|
17022
17345
|
} catch (error) {
|
|
17023
|
-
warn(`[wingman] ${harness} \u626B\u63CF\u5931\u8D25,\u964D\u7EA7\u663E\u793A:${
|
|
17346
|
+
warn(`[wingman] ${harness} \u626B\u63CF\u5931\u8D25,\u964D\u7EA7\u663E\u793A:${errorMessage3(error)}`);
|
|
17024
17347
|
configs.push(degradedScanConfig(harness, error));
|
|
17025
17348
|
}
|
|
17026
17349
|
}
|
|
@@ -17057,7 +17380,7 @@ function degradedScanConfig(harness, error) {
|
|
|
17057
17380
|
};
|
|
17058
17381
|
}
|
|
17059
17382
|
function errorSummary(error) {
|
|
17060
|
-
const first =
|
|
17383
|
+
const first = errorMessage3(error).split("\n", 1)[0] ?? "";
|
|
17061
17384
|
const scrubbed = first.replace(/(?:[A-Za-z]:)?[\\/][^\s'"()|,;]+/g, "\u2026").trim();
|
|
17062
17385
|
return scrubbed.length <= 120 ? scrubbed : `${scrubbed.slice(0, 119)}\u2026`;
|
|
17063
17386
|
}
|
|
@@ -17081,7 +17404,7 @@ async function handleActionPlan(req, res, options) {
|
|
|
17081
17404
|
const plan = await loaded.adapter.planAction(action, loaded.scanOptions);
|
|
17082
17405
|
sendJson2(res, 200, plan);
|
|
17083
17406
|
} catch (error) {
|
|
17084
|
-
sendJson2(res, 400, { error:
|
|
17407
|
+
sendJson2(res, 400, { error: errorMessage3(error) });
|
|
17085
17408
|
}
|
|
17086
17409
|
}
|
|
17087
17410
|
async function handleActionApply(req, res, options, runtime) {
|
|
@@ -17115,8 +17438,32 @@ async function handleActionApply(req, res, options, runtime) {
|
|
|
17115
17438
|
}
|
|
17116
17439
|
sendJson2(res, 200, result4);
|
|
17117
17440
|
} catch (error) {
|
|
17118
|
-
sendJson2(res, 400, { error:
|
|
17441
|
+
sendJson2(res, 400, { error: errorMessage3(error) });
|
|
17442
|
+
}
|
|
17443
|
+
}
|
|
17444
|
+
async function handleTaskOverlay(req, res, options) {
|
|
17445
|
+
if (options.readonlyActions) {
|
|
17446
|
+
sendJson2(res, 403, { error: "fixtures \u6A21\u5F0F\u53EA\u8BFB:\u4E0D\u5199\u4EFB\u52A1\u5361 overlay" });
|
|
17447
|
+
return;
|
|
17448
|
+
}
|
|
17449
|
+
const body = await readJsonBody(req);
|
|
17450
|
+
if (!isTaskOverlayRequest(body)) {
|
|
17451
|
+
sendJson2(res, 400, {
|
|
17452
|
+
error: "\u8BF7\u6C42\u9700\u8981\u5408\u6CD5 harness\u3001\u975E\u7A7A sessionId \u4E0E\u81F3\u5C11\u4E00\u4E2A title/pinned/hidden/accepted \u8865\u4E01\u5B57\u6BB5"
|
|
17453
|
+
});
|
|
17454
|
+
return;
|
|
17455
|
+
}
|
|
17456
|
+
if (options.homeDir === void 0) {
|
|
17457
|
+
sendJson2(res, 500, { error: "\u4EFB\u52A1\u5361 overlay \u7F3A\u5C11 homeDir" });
|
|
17458
|
+
return;
|
|
17459
|
+
}
|
|
17460
|
+
const key = `${body.harness}:${body.sessionId}`;
|
|
17461
|
+
const result4 = patchTaskOverlay(options.homeDir, key, body.patch, /* @__PURE__ */ new Date());
|
|
17462
|
+
if ("unavailable" in result4) {
|
|
17463
|
+
sendJson2(res, 409, { error: result4.unavailable });
|
|
17464
|
+
return;
|
|
17119
17465
|
}
|
|
17466
|
+
sendJson2(res, 200, { key, entry: result4.entry });
|
|
17120
17467
|
}
|
|
17121
17468
|
var LICENSE_BODY_LIMIT = 64 * 1024;
|
|
17122
17469
|
async function handleLicenseInstall(req, res, options) {
|
|
@@ -17198,6 +17545,25 @@ function isToggleRequest(value) {
|
|
|
17198
17545
|
const o = value;
|
|
17199
17546
|
return o.kind === "toggle" && typeof o.itemId === "string" && typeof o.enable === "boolean";
|
|
17200
17547
|
}
|
|
17548
|
+
function isTaskOverlayRequest(value) {
|
|
17549
|
+
if (!isRecord7(value) || !isHarnessId3(value.harness))
|
|
17550
|
+
return false;
|
|
17551
|
+
if (typeof value.sessionId !== "string" || value.sessionId.trim() === "")
|
|
17552
|
+
return false;
|
|
17553
|
+
if (!isRecord7(value.patch))
|
|
17554
|
+
return false;
|
|
17555
|
+
const keys = Object.keys(value.patch);
|
|
17556
|
+
if (keys.length === 0 || keys.some((key) => !["title", "pinned", "hidden", "accepted"].includes(key))) {
|
|
17557
|
+
return false;
|
|
17558
|
+
}
|
|
17559
|
+
return (value.patch.title === void 0 || value.patch.title === null || typeof value.patch.title === "string") && (value.patch.pinned === void 0 || typeof value.patch.pinned === "boolean") && (value.patch.hidden === void 0 || typeof value.patch.hidden === "boolean") && (value.patch.accepted === void 0 || typeof value.patch.accepted === "boolean");
|
|
17560
|
+
}
|
|
17561
|
+
function isHarnessId3(value) {
|
|
17562
|
+
return typeof value === "string" && HARNESS_IDS.includes(value);
|
|
17563
|
+
}
|
|
17564
|
+
function isRecord7(value) {
|
|
17565
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17566
|
+
}
|
|
17201
17567
|
function readJsonBody(req) {
|
|
17202
17568
|
return new Promise((resolveBody) => {
|
|
17203
17569
|
const chunks = [];
|
|
@@ -17258,7 +17624,7 @@ function sendText2(res, status3, body) {
|
|
|
17258
17624
|
res.writeHead(status3, { "content-type": "text/plain; charset=utf-8" });
|
|
17259
17625
|
res.end(body);
|
|
17260
17626
|
}
|
|
17261
|
-
function
|
|
17627
|
+
function errorMessage3(error) {
|
|
17262
17628
|
return error instanceof Error ? error.message : String(error);
|
|
17263
17629
|
}
|
|
17264
17630
|
|
|
@@ -17275,12 +17641,18 @@ var HELP = `wingman \u2014 Not your copilot. Your wingman.
|
|
|
17275
17641
|
[--project <dir>] [--dry-run] [--yes]
|
|
17276
17642
|
wingman license status \u67E5\u770B\u8BA2\u9605\u72B6\u6001(\u5199\u52A8\u4F5C\u7684\u95F8)
|
|
17277
17643
|
wingman license refresh \u8054\u7F51\u5411\u7B7E\u53D1\u7AEF\u6362\u65B0 license(\u7EED\u671F)
|
|
17644
|
+
wingman --version | -v | version \u663E\u793A\u7248\u672C
|
|
17278
17645
|
wingman help \u663E\u793A\u672C\u5E2E\u52A9
|
|
17279
17646
|
`;
|
|
17280
17647
|
var UsageError = class extends Error {
|
|
17281
17648
|
};
|
|
17282
17649
|
async function main(argv) {
|
|
17283
17650
|
const [command, ...rest] = argv;
|
|
17651
|
+
if (command === "--version" || command === "-v" || command === "version") {
|
|
17652
|
+
process.stdout.write(`${wingmanVersion() ?? "unknown"}
|
|
17653
|
+
`);
|
|
17654
|
+
return 0;
|
|
17655
|
+
}
|
|
17284
17656
|
switch (command) {
|
|
17285
17657
|
case void 0:
|
|
17286
17658
|
case "help":
|
|
@@ -17346,6 +17718,7 @@ ${counts}
|
|
|
17346
17718
|
}
|
|
17347
17719
|
async function runServe(args) {
|
|
17348
17720
|
const { port, fixtures, panelDist } = parseServeArgs(args);
|
|
17721
|
+
const homeDir = fixtures ? fixturesRoot : homedir13();
|
|
17349
17722
|
const hookToken = createHookToken();
|
|
17350
17723
|
const harnesses = [];
|
|
17351
17724
|
for (const id of HARNESS_IDS) {
|
|
@@ -17357,10 +17730,10 @@ async function runServe(args) {
|
|
|
17357
17730
|
`);
|
|
17358
17731
|
}
|
|
17359
17732
|
}
|
|
17360
|
-
const panelDistDir = panelDist === void 0 ? resolve11(
|
|
17733
|
+
const panelDistDir = panelDist === void 0 ? resolve11(dirname17(fileURLToPath2(import.meta.url)), "../../panel/dist") : resolve11(panelDist);
|
|
17361
17734
|
const narrative = createNarrativeService({
|
|
17362
17735
|
followTranscripts: !fixtures,
|
|
17363
|
-
homeDir
|
|
17736
|
+
homeDir,
|
|
17364
17737
|
...fixtures ? { projectBoundaryDir: fixturesRoot } : {},
|
|
17365
17738
|
feedbackSignals: (harness) => {
|
|
17366
17739
|
const loaded = harnesses.find((candidate) => candidate.harness === harness);
|
|
@@ -17369,7 +17742,7 @@ async function runServe(args) {
|
|
|
17369
17742
|
});
|
|
17370
17743
|
if (fixtures) {
|
|
17371
17744
|
try {
|
|
17372
|
-
const raw =
|
|
17745
|
+
const raw = readFileSync17(fixturePath("cc", "sessions", "session-todos.jsonl"), "utf8");
|
|
17373
17746
|
narrative.seedFixture("cc", raw.split("\n"), "MOCK \u2014 fixtures \u6F14\u793A:\u53D9\u4E8B\u6570\u636E\u6765\u81EA packages/fixtures \u7684 session-todos.jsonl \u6837\u4F8B\u4F1A\u8BDD,\u975E\u672C\u673A\u771F\u5B9E transcript\u3002");
|
|
17374
17747
|
} catch (error) {
|
|
17375
17748
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -17382,6 +17755,7 @@ async function runServe(args) {
|
|
|
17382
17755
|
panelDistDir,
|
|
17383
17756
|
receiver: createReceiver({ onEvent: narrative.handleEvent }),
|
|
17384
17757
|
readonlyActions: fixtures,
|
|
17758
|
+
homeDir,
|
|
17385
17759
|
narrative,
|
|
17386
17760
|
hookToken
|
|
17387
17761
|
});
|
|
@@ -17412,7 +17786,7 @@ async function runHook(args) {
|
|
|
17412
17786
|
if (action !== "install" && action !== "uninstall" || harness === void 0) {
|
|
17413
17787
|
throw new UsageError("hook \u7528\u6CD5:wingman hook install|uninstall <cc|codex|dsh> [--port 7333]");
|
|
17414
17788
|
}
|
|
17415
|
-
if (!
|
|
17789
|
+
if (!isHarnessId4(harness)) {
|
|
17416
17790
|
throw new UsageError(`hook \u9700\u8981 harness \u2208 ${HARNESS_IDS.join("|")},\u5F97\u5230 ${harness}`);
|
|
17417
17791
|
}
|
|
17418
17792
|
let port = 7333;
|
|
@@ -17455,7 +17829,7 @@ function parseScanArgs(args) {
|
|
|
17455
17829
|
json = true;
|
|
17456
17830
|
} else if (arg === "--harness") {
|
|
17457
17831
|
const value = rest.shift();
|
|
17458
|
-
if (value === void 0 || !
|
|
17832
|
+
if (value === void 0 || !isHarnessId4(value)) {
|
|
17459
17833
|
throw new UsageError(`--harness \u9700\u8981 ${HARNESS_IDS.join("|")},\u5F97\u5230 ${value ?? "(\u7F3A\u53C2)"}`);
|
|
17460
17834
|
}
|
|
17461
17835
|
harness = value;
|
|
@@ -17493,7 +17867,7 @@ function parseServeArgs(args) {
|
|
|
17493
17867
|
}
|
|
17494
17868
|
return { port, fixtures, panelDist };
|
|
17495
17869
|
}
|
|
17496
|
-
function
|
|
17870
|
+
function isHarnessId4(value) {
|
|
17497
17871
|
return HARNESS_IDS.includes(value);
|
|
17498
17872
|
}
|
|
17499
17873
|
main(process.argv.slice(2)).then((code) => {
|