@brainervirus/workit-cli 8.13.0 → 8.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +710 -216
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -65,7 +65,7 @@ var package_default;
|
|
|
65
65
|
var init_package = __esm(() => {
|
|
66
66
|
package_default = {
|
|
67
67
|
name: "@brainervirus/workit-cli",
|
|
68
|
-
version: "8.
|
|
68
|
+
version: "8.14.0",
|
|
69
69
|
private: false,
|
|
70
70
|
description: "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
|
|
71
71
|
keywords: [
|
|
@@ -103,13 +103,14 @@ var init_package = __esm(() => {
|
|
|
103
103
|
},
|
|
104
104
|
dependencies: {
|
|
105
105
|
"@babel/parser": "7.29.9",
|
|
106
|
-
"@brainervirus/workit-
|
|
107
|
-
"@brainervirus/workit-cursor": "^8.13.0",
|
|
108
|
-
"@brainervirus/workit-opencode": "^8.13.0",
|
|
106
|
+
"@brainervirus/workit-cursor": "^8.14.0",
|
|
109
107
|
"@inkjs/ui": "2.0.0",
|
|
110
108
|
ink: "7.1.1",
|
|
111
109
|
react: "19.2.8"
|
|
112
110
|
},
|
|
111
|
+
devDependencies: {
|
|
112
|
+
"@brainervirus/workit-core": "^8.14.0"
|
|
113
|
+
},
|
|
113
114
|
engines: {
|
|
114
115
|
node: ">=24"
|
|
115
116
|
}
|
|
@@ -10429,7 +10430,7 @@ var init_paths = __esm(() => {
|
|
|
10429
10430
|
});
|
|
10430
10431
|
|
|
10431
10432
|
// packages/workit-core/src/host-session.ts
|
|
10432
|
-
import { createHash as createHash3 } from "node:crypto";
|
|
10433
|
+
import { createHash as createHash3, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
|
|
10433
10434
|
import fs6 from "node:fs";
|
|
10434
10435
|
import path6 from "node:path";
|
|
10435
10436
|
function hostSessionFromEnv(env) {
|
|
@@ -10482,7 +10483,12 @@ function migrateRepoIssued(cwd) {
|
|
|
10482
10483
|
const to = path6.join(userIssuedRoot(), lead);
|
|
10483
10484
|
fs6.mkdirSync(to, { recursive: true, mode: 448 });
|
|
10484
10485
|
for (const id of fs6.readdirSync(from)) {
|
|
10485
|
-
|
|
10486
|
+
try {
|
|
10487
|
+
fs6.copyFileSync(path6.join(from, id), path6.join(to, id), fs6.constants.COPYFILE_EXCL);
|
|
10488
|
+
} catch (error) {
|
|
10489
|
+
if (error.code !== "EEXIST")
|
|
10490
|
+
throw error;
|
|
10491
|
+
}
|
|
10486
10492
|
fs6.rmSync(path6.join(from, id), { force: true });
|
|
10487
10493
|
}
|
|
10488
10494
|
removeIfEmpty(from);
|
|
@@ -10490,7 +10496,25 @@ function migrateRepoIssued(cwd) {
|
|
|
10490
10496
|
}
|
|
10491
10497
|
removeIfEmpty(old);
|
|
10492
10498
|
}
|
|
10493
|
-
function
|
|
10499
|
+
function issuedBinding(cwd, claimed) {
|
|
10500
|
+
const record = issuedRecord(cwd, claimed);
|
|
10501
|
+
const { stored, leadBound } = issuedToken(claimed);
|
|
10502
|
+
if (stored)
|
|
10503
|
+
return "token";
|
|
10504
|
+
if (record === null)
|
|
10505
|
+
return null;
|
|
10506
|
+
return leadBound ? "token" : "legacy";
|
|
10507
|
+
}
|
|
10508
|
+
function issuedTokenMatches(cwd, claimed, token) {
|
|
10509
|
+
migrateRepoIssued(cwd);
|
|
10510
|
+
const { stored } = issuedToken(claimed);
|
|
10511
|
+
if (!stored || !token)
|
|
10512
|
+
return false;
|
|
10513
|
+
const given = Buffer.from(tokenHash(token.trim()));
|
|
10514
|
+
const want = Buffer.from(stored);
|
|
10515
|
+
return given.length === want.length && timingSafeEqual(given, want);
|
|
10516
|
+
}
|
|
10517
|
+
function sessionClaimRefusal(env, claimed, cwd, options = {}) {
|
|
10494
10518
|
const real = realHostSession(env);
|
|
10495
10519
|
const host = family(real.host);
|
|
10496
10520
|
if (real.kind !== "derived" && host !== null && HOST_SET_VARIABLE[host])
|
|
@@ -10500,18 +10524,22 @@ function sessionClaimRefusal(env, claimed, cwd) {
|
|
|
10500
10524
|
};
|
|
10501
10525
|
if (real.kind === "none" || !real.session)
|
|
10502
10526
|
return null;
|
|
10503
|
-
|
|
10504
|
-
if (
|
|
10505
|
-
|
|
10506
|
-
|
|
10507
|
-
|
|
10508
|
-
|
|
10509
|
-
|
|
10510
|
-
|
|
10511
|
-
|
|
10512
|
-
if (
|
|
10527
|
+
const ownShell = claimed === real.session && !(real.kind === "declared" && claimed.includes(":")) || real.kind === "derived" && claimed.endsWith(`:${real.session}`);
|
|
10528
|
+
if (ownShell)
|
|
10529
|
+
return options.flag ? {
|
|
10530
|
+
error: `session_flag: --session ${claimed} names this shell's own ${real.host} session; on a host only an issued subagent id is passed as --session`,
|
|
10531
|
+
unblock: "drop --session: the shell's own session is already the acting one"
|
|
10532
|
+
} : null;
|
|
10533
|
+
const issuedClaim = claimed === real.session || claimed.startsWith(`${real.session}:`);
|
|
10534
|
+
if (issuedClaim) {
|
|
10535
|
+
const binding = issuedBinding(cwd, claimed);
|
|
10536
|
+
if (binding === "legacy")
|
|
10513
10537
|
return null;
|
|
10514
|
-
|
|
10538
|
+
if (binding === "token")
|
|
10539
|
+
return issuedTokenMatches(cwd, claimed, options.token) ? null : {
|
|
10540
|
+
error: `unbound_session: ${claimed} was issued to a subagent, and this call did not present the token its SubagentStart hook gave it`,
|
|
10541
|
+
unblock: `only that subagent acts as ${claimed}: it passes --token exactly as its SubagentStart context names it (or WORKIT_SESSION_TOKEN); anyone else spawns their own verifier`
|
|
10542
|
+
};
|
|
10515
10543
|
}
|
|
10516
10544
|
const error = `forged_session: ${claimed} is neither this shell's session (${real.session}) nor an id its host issued to a child of it`;
|
|
10517
10545
|
if (issuedClaim && noIssuedRecord(cwd, leadOf(claimed)) && host !== null && PLUGIN_UPDATE[host])
|
|
@@ -10522,10 +10550,10 @@ function sessionClaimRefusal(env, claimed, cwd) {
|
|
|
10522
10550
|
const ownChildShell = host !== null && OWN_CHILD_SHELL.has(host);
|
|
10523
10551
|
return {
|
|
10524
10552
|
error,
|
|
10525
|
-
unblock: ownChildShell ? `record the verdict from the verifier's own ${host === "pi" ? "Pi worker" : "OpenCode child session"} shell, where WORKIT_SESSION_ID is already set; never set WORKIT_SESSION_ID or --session by hand` : "never name a session yourself: spawn a verifier or reviewer subagent and pass exactly the --session its SubagentStart context names, or pass --as verifier"
|
|
10553
|
+
unblock: ownChildShell ? `record the verdict from the verifier's own ${host === "pi" ? "Pi worker" : "OpenCode child session"} shell, where WORKIT_SESSION_ID is already set; never set WORKIT_SESSION_ID or --session by hand` : "never name a session yourself: spawn a verifier or reviewer subagent and pass exactly the --session and --token its SubagentStart context names, or pass --as verifier"
|
|
10526
10554
|
};
|
|
10527
10555
|
}
|
|
10528
|
-
var HOST_SESSION_VARS, family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null, hash = (value) => createHash3("sha256").update(value).digest("hex").slice(0, 32), userIssuedRoot = () => path6.join(resolveStateDir(), "issued"), safeList = (dir) => {
|
|
10556
|
+
var HOST_SESSION_VARS, family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null, hash = (value) => createHash3("sha256").update(value).digest("hex").slice(0, 32), userIssuedRoot = () => path6.join(resolveStateDir(), "issued"), userTokenRoot = () => path6.join(resolveStateDir(), "issued-tokens"), safeList = (dir) => {
|
|
10529
10557
|
try {
|
|
10530
10558
|
return fs6.readdirSync(dir);
|
|
10531
10559
|
} catch {
|
|
@@ -10544,12 +10572,26 @@ var HOST_SESSION_VARS, family = (host) => host?.replace(/_(?:cli|desktop)$/, "")
|
|
|
10544
10572
|
} catch {
|
|
10545
10573
|
return false;
|
|
10546
10574
|
}
|
|
10547
|
-
}, PLUGIN_UPDATE,
|
|
10575
|
+
}, PLUGIN_UPDATE, tokenHash = (token) => createHash3("sha256").update(token).digest("hex"), issuedRecord = (cwd, claimed) => {
|
|
10576
|
+
const cut = claimed.lastIndexOf(":");
|
|
10577
|
+
if (cut <= 0)
|
|
10578
|
+
return null;
|
|
10548
10579
|
migrateRepoIssued(cwd);
|
|
10549
10580
|
try {
|
|
10550
|
-
return fs6.
|
|
10581
|
+
return fs6.readFileSync(path6.join(userIssuedRoot(), hash(claimed.slice(0, cut)), hash(claimed)), "utf8");
|
|
10551
10582
|
} catch {
|
|
10552
|
-
return
|
|
10583
|
+
return null;
|
|
10584
|
+
}
|
|
10585
|
+
}, TOKEN_HASH, issuedToken = (claimed) => {
|
|
10586
|
+
const cut = claimed.lastIndexOf(":");
|
|
10587
|
+
if (cut <= 0)
|
|
10588
|
+
return { stored: null, leadBound: false };
|
|
10589
|
+
const dir = path6.join(userTokenRoot(), hash(claimed.slice(0, cut)));
|
|
10590
|
+
try {
|
|
10591
|
+
const text = fs6.readFileSync(path6.join(dir, hash(claimed)), "utf8");
|
|
10592
|
+
return { stored: TOKEN_HASH.exec(text)?.[1] ?? null, leadBound: true };
|
|
10593
|
+
} catch {
|
|
10594
|
+
return { stored: null, leadBound: safeList(dir).length > 0 };
|
|
10553
10595
|
}
|
|
10554
10596
|
}, leadOf = (session) => session.slice(0, session.lastIndexOf(":")), OWN_CHILD_SHELL, HOST_SET_VARIABLE;
|
|
10555
10597
|
var init_host_session = __esm(() => {
|
|
@@ -10567,6 +10609,7 @@ var init_host_session = __esm(() => {
|
|
|
10567
10609
|
claude_code: "claude plugin marketplace update workit && claude plugin update workit@workit",
|
|
10568
10610
|
codex: "codex plugin remove workit@workit && codex plugin add workit@workit"
|
|
10569
10611
|
};
|
|
10612
|
+
TOKEN_HASH = /^([0-9a-f]{64})$/mu;
|
|
10570
10613
|
OWN_CHILD_SHELL = new Set(["opencode", "pi"]);
|
|
10571
10614
|
HOST_SET_VARIABLE = {
|
|
10572
10615
|
opencode: "OPENCODE_SESSION_ID",
|
|
@@ -10599,7 +10642,7 @@ import { spawnSync as spawnSync3 } from "node:child_process";
|
|
|
10599
10642
|
import fs7 from "node:fs";
|
|
10600
10643
|
import { hostname } from "node:os";
|
|
10601
10644
|
import path8 from "node:path";
|
|
10602
|
-
var metadataLockSchema, FOREIGN_LOCK_TTL_MS, UNREADABLE_LOCK_TTL_MS = 30000, RECLAIM_GUARD_TTL_MS = 30000,
|
|
10645
|
+
var metadataLockSchema, FOREIGN_LOCK_TTL_MS, UNREADABLE_LOCK_TTL_MS = 30000, RECLAIM_GUARD_TTL_MS = 30000, IN_PROCESS_LOCK_TIMEOUT_MS = 250, defaultLockTimeoutMs, defaultLockTimeout = () => defaultLockTimeoutMs, hostAwareLockTimeout = (cliMs) => defaultLockTimeoutMs > IN_PROCESS_LOCK_TIMEOUT_MS ? cliMs : IN_PROCESS_LOCK_TIMEOUT_MS, setDefaultLockTimeout = (ms) => {
|
|
10603
10646
|
defaultLockTimeoutMs = ms;
|
|
10604
10647
|
}, parseMetadataLock = (raw) => {
|
|
10605
10648
|
let value;
|
|
@@ -10789,6 +10832,7 @@ var init_store_lock = __esm(() => {
|
|
|
10789
10832
|
externalAction: literal(true).optional()
|
|
10790
10833
|
}).strict();
|
|
10791
10834
|
FOREIGN_LOCK_TTL_MS = 10 * 60000;
|
|
10835
|
+
defaultLockTimeoutMs = IN_PROCESS_LOCK_TIMEOUT_MS;
|
|
10792
10836
|
TASK_STORE_LOCK = { reclaimUnverifiedAfterTtl: true };
|
|
10793
10837
|
});
|
|
10794
10838
|
|
|
@@ -13978,23 +14022,79 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
13978
14022
|
}, resolveRuntimeReleaseTracks = (cwd) => {
|
|
13979
14023
|
const matched = matchRuntimeWorkspace(cwd);
|
|
13980
14024
|
return matched ? { workspace: matched.selected.name, releaseTracks: matched.selected.releaseTracks } : null;
|
|
13981
|
-
},
|
|
14025
|
+
}, inheritedSetting = (cwd, dir, has) => {
|
|
13982
14026
|
let matched;
|
|
13983
14027
|
try {
|
|
13984
14028
|
matched = matchRuntimeWorkspace(cwd, dir);
|
|
13985
14029
|
} catch {
|
|
13986
|
-
return
|
|
14030
|
+
return null;
|
|
13987
14031
|
}
|
|
13988
14032
|
if (!matched)
|
|
13989
|
-
return
|
|
14033
|
+
return null;
|
|
13990
14034
|
const { selected, matches } = matched;
|
|
13991
14035
|
const enclosing = matches.filter((entry) => entry !== selected).toSorted((a, b) => globSpecificity(b.glob) - globSpecificity(a.glob));
|
|
13992
|
-
|
|
14036
|
+
return { workspace: selected.name, source: [selected, ...enclosing].find(has) ?? null };
|
|
14037
|
+
}, workspaceTrailers = (cwd, dir) => {
|
|
14038
|
+
const found = inheritedSetting(cwd, dir, (entry) => typeof entry.trailers === "boolean");
|
|
13993
14039
|
return {
|
|
13994
|
-
enabled: source?.trailers !== false,
|
|
13995
|
-
workspace:
|
|
13996
|
-
from: source?.name ?? null
|
|
14040
|
+
enabled: found?.source?.trailers !== false,
|
|
14041
|
+
workspace: found?.workspace ?? null,
|
|
14042
|
+
from: found?.source?.name ?? null
|
|
13997
14043
|
};
|
|
14044
|
+
}, hostFamily = (host) => host.replace(/_(?:cli|desktop)$/u, ""), TIER_KEYS, TIER_HOSTS, workspaceTierModelIssues = (cwd, dir) => {
|
|
14045
|
+
let matched;
|
|
14046
|
+
try {
|
|
14047
|
+
matched = matchRuntimeWorkspace(cwd, dir);
|
|
14048
|
+
} catch {
|
|
14049
|
+
return [];
|
|
14050
|
+
}
|
|
14051
|
+
const issues = [];
|
|
14052
|
+
for (const entry of matched?.matches ?? []) {
|
|
14053
|
+
const models = entry.fanout?.tierModels;
|
|
14054
|
+
if (models === undefined)
|
|
14055
|
+
continue;
|
|
14056
|
+
const where = `workspace ${entry.name} fanout.tierModels`;
|
|
14057
|
+
if (!models || typeof models !== "object" || Array.isArray(models)) {
|
|
14058
|
+
issues.push(`${where} is not an object; ignored`);
|
|
14059
|
+
continue;
|
|
14060
|
+
}
|
|
14061
|
+
for (const [tier, value] of Object.entries(models)) {
|
|
14062
|
+
if (!TIER_KEYS.has(tier))
|
|
14063
|
+
issues.push(`${where}.${tier}: unknown tier; ignored`);
|
|
14064
|
+
else if (typeof value === "string") {
|
|
14065
|
+
if (!value.trim())
|
|
14066
|
+
issues.push(`${where}.${tier}: empty model name; ignored`);
|
|
14067
|
+
} else if (!value || typeof value !== "object" || Array.isArray(value))
|
|
14068
|
+
issues.push(`${where}.${tier}: not a model name or a per-host object; ignored`);
|
|
14069
|
+
else
|
|
14070
|
+
for (const [host, model] of Object.entries(value)) {
|
|
14071
|
+
if (!TIER_HOSTS.has(host))
|
|
14072
|
+
issues.push(`${where}.${tier}.${host}: unknown host; ignored`);
|
|
14073
|
+
else if (typeof model !== "string" || !model.trim())
|
|
14074
|
+
issues.push(`${where}.${tier}.${host}: not a model name; ignored`);
|
|
14075
|
+
}
|
|
14076
|
+
}
|
|
14077
|
+
}
|
|
14078
|
+
return issues;
|
|
14079
|
+
}, workspaceTierModel = (cwd, tier, host, dir) => {
|
|
14080
|
+
const pick = (entry) => {
|
|
14081
|
+
const fanout = entry.fanout;
|
|
14082
|
+
const models = fanout?.tierModels;
|
|
14083
|
+
if (!models || typeof models !== "object" || !Object.hasOwn(models, tier))
|
|
14084
|
+
return null;
|
|
14085
|
+
const value = models[tier];
|
|
14086
|
+
if (typeof value === "string")
|
|
14087
|
+
return value.trim() || null;
|
|
14088
|
+
if (!value || typeof value !== "object")
|
|
14089
|
+
return null;
|
|
14090
|
+
const byHost = value;
|
|
14091
|
+
const family = host ? hostFamily(host) : null;
|
|
14092
|
+
const named = (family && Object.hasOwn(byHost, family) ? byHost[family] : undefined) ?? (host && Object.hasOwn(byHost, host) ? byHost[host] : undefined) ?? (Object.hasOwn(byHost, "default") ? byHost.default : undefined);
|
|
14093
|
+
return typeof named === "string" && named.trim() ? named.trim() : null;
|
|
14094
|
+
};
|
|
14095
|
+
const found = inheritedSetting(cwd, dir, (entry) => pick(entry) !== null);
|
|
14096
|
+
const model = found?.source ? pick(found.source) : null;
|
|
14097
|
+
return model && found?.source ? { model, workspace: found.source.name } : null;
|
|
13998
14098
|
}, resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
13999
14099
|
const matched = matchRuntimeWorkspace(cwd);
|
|
14000
14100
|
if (!matched)
|
|
@@ -14146,6 +14246,8 @@ var init_workspaces = __esm(() => {
|
|
|
14146
14246
|
defaultEndpoint: unknown().optional(),
|
|
14147
14247
|
verification: _enum(["self", "independent"]).optional(),
|
|
14148
14248
|
trailers: boolean2().optional(),
|
|
14249
|
+
liveSessionGuard: boolean2().optional(),
|
|
14250
|
+
fanout: object({ tierModels: unknown().optional() }).passthrough().optional(),
|
|
14149
14251
|
autonomy: object(Object.fromEntries(["push", "pr", "merge", "release", "rerun"].map((kind) => [
|
|
14150
14252
|
kind,
|
|
14151
14253
|
union([boolean2(), literal("verified")]).optional()
|
|
@@ -14195,6 +14297,17 @@ var init_workspaces = __esm(() => {
|
|
|
14195
14297
|
runtimeWorkspaceIndexSchema = object({
|
|
14196
14298
|
workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
|
|
14197
14299
|
}).passthrough();
|
|
14300
|
+
TIER_KEYS = new Set(["mundane", "standard", "hard"]);
|
|
14301
|
+
TIER_HOSTS = new Set([
|
|
14302
|
+
"claude_code",
|
|
14303
|
+
"opencode",
|
|
14304
|
+
"codex",
|
|
14305
|
+
"codex_cli",
|
|
14306
|
+
"codex_desktop",
|
|
14307
|
+
"cursor",
|
|
14308
|
+
"pi",
|
|
14309
|
+
"default"
|
|
14310
|
+
]);
|
|
14198
14311
|
});
|
|
14199
14312
|
|
|
14200
14313
|
// packages/workit-core/src/core/release-tracks.ts
|
|
@@ -14789,7 +14902,7 @@ var init_vcs_config = __esm(() => {
|
|
|
14789
14902
|
|
|
14790
14903
|
// packages/workit-core/src/ledger.ts
|
|
14791
14904
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
14792
|
-
import { createHash as createHash9, randomBytes as
|
|
14905
|
+
import { createHash as createHash9, randomBytes as randomBytes3 } from "node:crypto";
|
|
14793
14906
|
import fs12 from "node:fs";
|
|
14794
14907
|
import path15 from "node:path";
|
|
14795
14908
|
function resolveCommit(cwd, ref) {
|
|
@@ -14878,7 +14991,7 @@ function actorFromEnv(env) {
|
|
|
14878
14991
|
agentId: value("WORKIT_AGENT_ID")
|
|
14879
14992
|
};
|
|
14880
14993
|
}
|
|
14881
|
-
function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
14994
|
+
function withLedgerLock(file, fn, waitMs = hostAwareLockTimeout(LOCK_WAIT_MS)) {
|
|
14882
14995
|
const lock = `${file}.lock`;
|
|
14883
14996
|
const deadline = Date.now() + waitMs;
|
|
14884
14997
|
let token = null;
|
|
@@ -14902,8 +15015,10 @@ function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
|
|
|
14902
15015
|
}
|
|
14903
15016
|
function appendLine(file, build, options) {
|
|
14904
15017
|
const write = () => {
|
|
14905
|
-
const fd = fs12.openSync(file, "a+");
|
|
15018
|
+
const fd = fs12.openSync(file, "a+", 384);
|
|
14906
15019
|
try {
|
|
15020
|
+
if (process.platform !== "win32")
|
|
15021
|
+
tighten(fd);
|
|
14907
15022
|
const size = fs12.fstatSync(fd).size;
|
|
14908
15023
|
let fence = "";
|
|
14909
15024
|
if (size > 0) {
|
|
@@ -14982,7 +15097,9 @@ function readLedger(cwd, options = {}) {
|
|
|
14982
15097
|
if (!file.ok)
|
|
14983
15098
|
return file;
|
|
14984
15099
|
let text = "";
|
|
15100
|
+
let stat = null;
|
|
14985
15101
|
try {
|
|
15102
|
+
stat = fs12.statSync(file.value);
|
|
14986
15103
|
text = fs12.readFileSync(file.value, "utf8");
|
|
14987
15104
|
} catch (error) {
|
|
14988
15105
|
if (error.code === "ENOENT")
|
|
@@ -15002,6 +15119,7 @@ function readLedger(cwd, options = {}) {
|
|
|
15002
15119
|
const integrity = { chained: 0, legacy: 0, unverified: [] };
|
|
15003
15120
|
const earlier = new Set([null]);
|
|
15004
15121
|
let chainStarted = false;
|
|
15122
|
+
const ids = new Set;
|
|
15005
15123
|
for (const raw of text.split(`
|
|
15006
15124
|
`)) {
|
|
15007
15125
|
const trimmed = raw.trim();
|
|
@@ -15035,6 +15153,10 @@ function readLedger(cwd, options = {}) {
|
|
|
15035
15153
|
reason = "unsigned";
|
|
15036
15154
|
else
|
|
15037
15155
|
rowIntegrity = "legacy";
|
|
15156
|
+
if (options.integrity && !reason && ids.has(parsed.id))
|
|
15157
|
+
reason = "duplicate_id";
|
|
15158
|
+
if (options.integrity)
|
|
15159
|
+
ids.add(parsed.id);
|
|
15038
15160
|
if (reason)
|
|
15039
15161
|
rowIntegrity = "unverified";
|
|
15040
15162
|
const pr = typeof parsed.pr === "number" && Number.isInteger(parsed.pr) ? parsed.pr : undefined;
|
|
@@ -15058,7 +15180,8 @@ function readLedger(cwd, options = {}) {
|
|
|
15058
15180
|
seq: rows.length + 1,
|
|
15059
15181
|
superseded: false,
|
|
15060
15182
|
supersedeIgnored: false,
|
|
15061
|
-
...rowIntegrity ? { integrity: rowIntegrity } : {}
|
|
15183
|
+
...rowIntegrity ? { integrity: rowIntegrity } : {},
|
|
15184
|
+
...reason ? { integrityReason: reason } : {}
|
|
15062
15185
|
};
|
|
15063
15186
|
rows.push(row);
|
|
15064
15187
|
if (!options.integrity)
|
|
@@ -15086,18 +15209,44 @@ function readLedger(cwd, options = {}) {
|
|
|
15086
15209
|
const byId = new Map;
|
|
15087
15210
|
for (const row of rows) {
|
|
15088
15211
|
const target = row.supersedes ? byId.get(row.supersedes) : undefined;
|
|
15089
|
-
if (target && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
|
|
15212
|
+
if (target && row.integrity !== "unverified" && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
|
|
15090
15213
|
target.superseded = true;
|
|
15091
15214
|
else if (row.supersedes)
|
|
15092
15215
|
row.supersedeIgnored = true;
|
|
15093
15216
|
if (!byId.has(row.id))
|
|
15094
15217
|
byId.set(row.id, row);
|
|
15095
15218
|
}
|
|
15219
|
+
if (options.integrity && stat && Buffer.byteLength(text) === stat.size)
|
|
15220
|
+
tamperCache.set(file.value, {
|
|
15221
|
+
size: stat.size,
|
|
15222
|
+
stamp: fileStamp(stat),
|
|
15223
|
+
breaks: integrity.unverified.filter((row) => TAMPER_REASONS.has(row.reason))
|
|
15224
|
+
});
|
|
15096
15225
|
return {
|
|
15097
15226
|
ok: true,
|
|
15098
15227
|
value: { path: file.value, rows, skipped, integrity: options.integrity ? integrity : null }
|
|
15099
15228
|
};
|
|
15100
15229
|
}
|
|
15230
|
+
function ledgerTampering(cwd) {
|
|
15231
|
+
const file = ledgerPath(cwd);
|
|
15232
|
+
if (!file.ok)
|
|
15233
|
+
return [];
|
|
15234
|
+
try {
|
|
15235
|
+
const stat = fs12.statSync(file.value);
|
|
15236
|
+
const cached = tamperCache.get(file.value);
|
|
15237
|
+
if (cached && cached.size === stat.size && cached.stamp === fileStamp(stat))
|
|
15238
|
+
return cached.breaks;
|
|
15239
|
+
} catch {
|
|
15240
|
+
return [];
|
|
15241
|
+
}
|
|
15242
|
+
const read = readLedger(cwd, { integrity: true });
|
|
15243
|
+
return read.ok ? (read.value.integrity?.unverified ?? []).filter((row) => TAMPER_REASONS.has(row.reason)) : [];
|
|
15244
|
+
}
|
|
15245
|
+
function tamperedUnblock(cwd) {
|
|
15246
|
+
const file = ledgerPath(cwd);
|
|
15247
|
+
const where = file.ok ? file.value : "the ledger file";
|
|
15248
|
+
return `workit ledger verify-integrity # lists the edited or missing rows; if the edit is understood, move ${where} aside (mv ${where} ${where}.tampered-<date>) and record the verdicts again`;
|
|
15249
|
+
}
|
|
15101
15250
|
function filterRows(rows, filter) {
|
|
15102
15251
|
const matched = rows.filter((row) => (filter.branch === undefined || row.branch === filter.branch) && (filter.pr === undefined || row.pr === filter.pr) && (filter.type === undefined || row.type === filter.type));
|
|
15103
15252
|
return filter.last === undefined ? matched : matched.slice(Math.max(0, matched.length - filter.last));
|
|
@@ -15213,15 +15362,51 @@ function authorSessions(cwd, rows, branch, range = { base: null, head: null }, s
|
|
|
15213
15362
|
sessions.add(session);
|
|
15214
15363
|
return sessions;
|
|
15215
15364
|
}
|
|
15216
|
-
function
|
|
15365
|
+
function sessionTrust(cwd, row) {
|
|
15366
|
+
const session = row.actor.session;
|
|
15367
|
+
if (!session || row.self === true)
|
|
15368
|
+
return null;
|
|
15369
|
+
const source = typeof row.sessionSource === "string" ? row.sessionSource : "env";
|
|
15370
|
+
const host = row.actor.host;
|
|
15371
|
+
const issuedOnly = source === "flag" ? host !== "cli" : source === "env" && session.includes(":") && !SHELL_VOUCHED_HOSTS.has(host);
|
|
15372
|
+
if (!issuedOnly)
|
|
15373
|
+
return null;
|
|
15374
|
+
const binding = issuedBinding(cwd, session);
|
|
15375
|
+
if (binding === null)
|
|
15376
|
+
return "unissued_session";
|
|
15377
|
+
return binding === "token" && row.sessionBound !== true ? "unbound_session" : null;
|
|
15378
|
+
}
|
|
15379
|
+
function trustReasons(cwd, row) {
|
|
15380
|
+
if (row.integrity === "unverified")
|
|
15381
|
+
return [row.integrityReason ?? "unsigned"];
|
|
15382
|
+
if (row.integrity !== "verified")
|
|
15383
|
+
return ["unsigned"];
|
|
15384
|
+
const session = sessionTrust(cwd, row);
|
|
15385
|
+
return session ? [session] : [];
|
|
15386
|
+
}
|
|
15387
|
+
function withIntegrity(cwd, rows) {
|
|
15388
|
+
if (rows.every((row) => row.integrity !== undefined))
|
|
15389
|
+
return rows;
|
|
15390
|
+
const read = readLedger(cwd, { integrity: true });
|
|
15391
|
+
const bySeq = new Map(read.ok ? read.value.rows.map((row) => [row.seq, row]) : []);
|
|
15392
|
+
return rows.map((row) => {
|
|
15393
|
+
if (row.integrity !== undefined)
|
|
15394
|
+
return row;
|
|
15395
|
+
const fresh = bySeq.get(row.seq);
|
|
15396
|
+
return fresh && fresh.id === row.id && fresh.at === row.at ? fresh : row;
|
|
15397
|
+
});
|
|
15398
|
+
}
|
|
15399
|
+
function effectiveOf(rows, reasonsOf, sameCode, isCurrent) {
|
|
15217
15400
|
let effective = null;
|
|
15218
15401
|
for (const row of rows) {
|
|
15219
15402
|
if (!effective) {
|
|
15220
15403
|
effective = row;
|
|
15221
15404
|
continue;
|
|
15222
15405
|
}
|
|
15223
|
-
|
|
15224
|
-
|
|
15406
|
+
if (untrusted(reasonsOf(row)) && !untrusted(reasonsOf(effective)))
|
|
15407
|
+
continue;
|
|
15408
|
+
const rowIndependent = reasonsOf(row).length === 0;
|
|
15409
|
+
const effIndependent = reasonsOf(effective).length === 0;
|
|
15225
15410
|
if (effIndependent && !rowIndependent && isCurrent(effective))
|
|
15226
15411
|
continue;
|
|
15227
15412
|
if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session && sameCode(effective, row))
|
|
@@ -15235,7 +15420,7 @@ function restackCarries(rows, branch, from, to) {
|
|
|
15235
15420
|
return false;
|
|
15236
15421
|
const edges = new Map;
|
|
15237
15422
|
for (const row of rows)
|
|
15238
|
-
if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
|
|
15423
|
+
if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.integrity === "verified" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
|
|
15239
15424
|
edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
|
|
15240
15425
|
const seen = new Set([from]);
|
|
15241
15426
|
const queue = [from];
|
|
@@ -15251,7 +15436,8 @@ function restackCarries(rows, branch, from, to) {
|
|
|
15251
15436
|
}
|
|
15252
15437
|
return false;
|
|
15253
15438
|
}
|
|
15254
|
-
function checkVerdicts(cwd, branch,
|
|
15439
|
+
function checkVerdicts(cwd, branch, ledgerRows) {
|
|
15440
|
+
const rows = withIntegrity(cwd, ledgerRows);
|
|
15255
15441
|
const head = branchHead(cwd, branch);
|
|
15256
15442
|
const fallbackBase = defaultBase(cwd);
|
|
15257
15443
|
const scope = branchScope(cwd, branch);
|
|
@@ -15281,21 +15467,47 @@ function checkVerdicts(cwd, branch, rows) {
|
|
|
15281
15467
|
return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(rows, branch, failed.head, row.head);
|
|
15282
15468
|
};
|
|
15283
15469
|
const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head }, scope);
|
|
15470
|
+
const failureAuthorCache = new Map;
|
|
15471
|
+
const failureAuthors = (row) => {
|
|
15472
|
+
let set = failureAuthorCache.get(row.seq);
|
|
15473
|
+
if (!set) {
|
|
15474
|
+
set = authorSessions(cwd, rows.filter((other) => other.integrity === "verified" && other.seq < row.seq), branch, { base: null, head: null }, scope);
|
|
15475
|
+
failureAuthorCache.set(row.seq, set);
|
|
15476
|
+
}
|
|
15477
|
+
return set;
|
|
15478
|
+
};
|
|
15479
|
+
const reasonCache = new Map;
|
|
15480
|
+
const reasonsOf = (row) => {
|
|
15481
|
+
let reasons = reasonCache.get(row);
|
|
15482
|
+
if (!reasons) {
|
|
15483
|
+
const failing = FAILING.has(String(row.result));
|
|
15484
|
+
reasons = [
|
|
15485
|
+
...trustReasons(cwd, row),
|
|
15486
|
+
...independenceReasons(row, failing ? failureAuthors(row) : authors)
|
|
15487
|
+
];
|
|
15488
|
+
reasonCache.set(row, reasons);
|
|
15489
|
+
}
|
|
15490
|
+
return reasons;
|
|
15491
|
+
};
|
|
15492
|
+
const retired = new Set;
|
|
15493
|
+
for (const row of rows)
|
|
15494
|
+
if (row.type === "verdict" && row.supersedes && !row.supersedeIgnored && trustReasons(cwd, row).length === 0)
|
|
15495
|
+
retired.add(row.supersedes);
|
|
15284
15496
|
const byKind = new Map;
|
|
15285
15497
|
for (const row of rows)
|
|
15286
|
-
if (isLiveVerdict(row) && scope(row)) {
|
|
15498
|
+
if (isLiveVerdict(row, retired) && scope(row)) {
|
|
15287
15499
|
const kind = typeof row.kind === "string" ? row.kind : "review";
|
|
15288
15500
|
byKind.set(kind, [...byKind.get(kind) ?? [], row]);
|
|
15289
15501
|
}
|
|
15290
15502
|
const verdicts = [];
|
|
15291
15503
|
const warnings = [];
|
|
15292
15504
|
for (const [kind, list] of byKind) {
|
|
15293
|
-
const row = effectiveOf(list,
|
|
15505
|
+
const row = effectiveOf(list, reasonsOf, sameCode, (candidate) => basisOf(candidate) !== "stale");
|
|
15294
15506
|
if (!row)
|
|
15295
15507
|
continue;
|
|
15296
15508
|
const basis = basisOf(row);
|
|
15297
15509
|
const current = basis !== "stale";
|
|
15298
|
-
const independence =
|
|
15510
|
+
const independence = reasonsOf(row);
|
|
15299
15511
|
const reasons = [
|
|
15300
15512
|
...current ? [] : ["stale"],
|
|
15301
15513
|
...PASSING.has(String(row.result)) ? [] : ["not_passing"],
|
|
@@ -15312,23 +15524,31 @@ function checkVerdicts(cwd, branch, rows) {
|
|
|
15312
15524
|
});
|
|
15313
15525
|
if (reasons.length === 0) {
|
|
15314
15526
|
for (const later of list)
|
|
15315
|
-
if (later.seq > row.seq &&
|
|
15527
|
+
if (later.seq > row.seq && reasonsOf(later).length > 0 && !untrusted(reasonsOf(later)) && FAILING.has(String(later.result)) && basisOf(later) !== "stale")
|
|
15316
15528
|
warnings.push(`the author's own (self) ${String(later.result)} ${kind} verdict ${later.id} by ${later.actor.session ?? "no session"} on the current code is newer than the accepted pass ${row.id}: read it before merging`);
|
|
15317
15529
|
}
|
|
15318
15530
|
}
|
|
15319
15531
|
for (const row of rows)
|
|
15320
15532
|
if (row.integrity === "unverified" && scope(row))
|
|
15321
15533
|
warnings.push(`ledger row ${row.id} (${row.type}) on ${row.branch ?? branch} is unverified: the workit CLI did not write it or it changed afterwards (workit ledger verify-integrity)`);
|
|
15534
|
+
const tampered = ledgerTampering(cwd);
|
|
15535
|
+
if (tampered.length) {
|
|
15536
|
+
warnings.push(tamperedWarning(cwd, tampered));
|
|
15537
|
+
for (const entry of verdicts) {
|
|
15538
|
+
entry.accepted = false;
|
|
15539
|
+
entry.reasons = [...entry.reasons, "ledger_tampered"];
|
|
15540
|
+
}
|
|
15541
|
+
}
|
|
15322
15542
|
verdicts.sort((a, b) => a.verdict.seq - b.verdict.seq);
|
|
15323
15543
|
const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
|
|
15324
15544
|
const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
|
|
15325
15545
|
const newestAccepted = verdicts.findLast((entry) => entry.accepted && STRONG_RESULTS.has(String(entry.verdict.result))) ?? null;
|
|
15326
|
-
const accepted = newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
|
|
15546
|
+
const accepted = tampered.length ? { accepted: false, verdict: null, reasons: ["ledger_tampered"] } : newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
|
|
15327
15547
|
accepted: false,
|
|
15328
15548
|
verdict: failing?.verdict ?? null,
|
|
15329
15549
|
reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
|
|
15330
15550
|
};
|
|
15331
|
-
const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
15551
|
+
const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
15332
15552
|
return {
|
|
15333
15553
|
branch,
|
|
15334
15554
|
head,
|
|
@@ -15523,6 +15743,7 @@ function recordVerdict(context, input) {
|
|
|
15523
15743
|
self: self2,
|
|
15524
15744
|
selfReason,
|
|
15525
15745
|
sessionSource: context.sessionSource ?? "env",
|
|
15746
|
+
...context.sessionBound === true ? { sessionBound: true } : {},
|
|
15526
15747
|
evidenceRefs: input.evidenceRefs ?? []
|
|
15527
15748
|
}, { now: context.now });
|
|
15528
15749
|
}
|
|
@@ -15751,7 +15972,7 @@ var LEDGER_VERSION = 1, MAX_LINE_BYTES = 4096, VERDICT_RESULTS, PASSING, FAILING
|
|
|
15751
15972
|
} catch {
|
|
15752
15973
|
return null;
|
|
15753
15974
|
}
|
|
15754
|
-
}, newId2 = () => `${Date.now().toString(36)}-${
|
|
15975
|
+
}, newId2 = () => `${Date.now().toString(36)}-${randomBytes3(6).toString("hex")}`, NETWORK_FS_TYPES, isNetworkFsType = (type) => NETWORK_FS_TYPES.has(type >>> 0), onNetworkFs = (dir) => {
|
|
15755
15976
|
if (process.platform !== "linux")
|
|
15756
15977
|
return false;
|
|
15757
15978
|
try {
|
|
@@ -15804,6 +16025,11 @@ ${body}`), TAIL_BYTES, lastLine = (fd, size) => {
|
|
|
15804
16025
|
return text || null;
|
|
15805
16026
|
}
|
|
15806
16027
|
return null;
|
|
16028
|
+
}, tighten = (fd) => {
|
|
16029
|
+
try {
|
|
16030
|
+
if ((fs12.fstatSync(fd).mode & 63) !== 0)
|
|
16031
|
+
fs12.fchmodSync(fd, 384);
|
|
16032
|
+
} catch {}
|
|
15807
16033
|
}, sealed = (row, id, at, previous) => {
|
|
15808
16034
|
const { prevHash: _prev, rowHash: _row, ...fields } = row;
|
|
15809
16035
|
const prevHash = previous === null ? null : sha2562(previous);
|
|
@@ -15818,7 +16044,7 @@ ${body}`), TAIL_BYTES, lastLine = (fd, size) => {
|
|
|
15818
16044
|
session: str2(actor.session),
|
|
15819
16045
|
agentId: str2(actor.agentId)
|
|
15820
16046
|
};
|
|
15821
|
-
}, isLiveVerdict = (row) => row.type === "verdict" && typeof row.result === "string" && !row.
|
|
16047
|
+
}, TAMPER_REASONS, tamperCache, fileStamp = (stat) => `${stat.ino}:${stat.mtimeMs}:${stat.ctimeMs}`, tamperedWarning = (cwd, breaks) => `the ledger was edited, or a row was removed or moved (${breaks.slice(0, 3).map((row) => `${row.reason} at row ${row.seq}`).join(", ")}${breaks.length > 3 ? ", …" : ""}): no verdict is accepted until it is repaired. ${tamperedUnblock(cwd)}`, isLiveVerdict = (row, retired) => row.type === "verdict" && typeof row.result === "string" && !retired.has(row.id), RENAMED, renameAwareBranch = (scopeOf, branch) => {
|
|
15822
16048
|
return (target) => {
|
|
15823
16049
|
const own = typeof branch === "string" ? branch : null;
|
|
15824
16050
|
const theirs = typeof target.branch === "string" ? target.branch : null;
|
|
@@ -15835,7 +16061,7 @@ ${body}`), TAIL_BYTES, lastLine = (fd, size) => {
|
|
|
15835
16061
|
if (derivesFrom(author, session) || derivesFrom(session, author))
|
|
15836
16062
|
return true;
|
|
15837
16063
|
return false;
|
|
15838
|
-
}, independenceReasons = (row, authors) => {
|
|
16064
|
+
}, TRUST_REASONS, SHELL_VOUCHED_HOSTS, untrusted = (reasons) => reasons.some((reason) => TRUST_REASONS.has(reason)), independenceReasons = (row, authors) => {
|
|
15839
16065
|
const reasons = [];
|
|
15840
16066
|
if (!row.actor.session)
|
|
15841
16067
|
reasons.push("no_session");
|
|
@@ -15897,6 +16123,7 @@ ${body}`), TAIL_BYTES, lastLine = (fd, size) => {
|
|
|
15897
16123
|
return brief.pr === null ? "workit pr create" : `workit pr status --pr ${brief.pr}`;
|
|
15898
16124
|
};
|
|
15899
16125
|
var init_ledger = __esm(() => {
|
|
16126
|
+
init_store_lock();
|
|
15900
16127
|
init_vcs_config();
|
|
15901
16128
|
init_rev();
|
|
15902
16129
|
init_host_session();
|
|
@@ -15935,14 +16162,14 @@ var init_ledger = __esm(() => {
|
|
|
15935
16162
|
return null;
|
|
15936
16163
|
throw error;
|
|
15937
16164
|
}
|
|
15938
|
-
const token = `${process.pid}-${
|
|
16165
|
+
const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
|
|
15939
16166
|
fs12.writeFileSync(path15.join(lock, LOCK_OWNER), token);
|
|
15940
16167
|
return token;
|
|
15941
16168
|
},
|
|
15942
16169
|
release(lock, token) {
|
|
15943
16170
|
if (lockToken(lock) !== token)
|
|
15944
16171
|
return false;
|
|
15945
|
-
const tomb = `${lock}.release-${
|
|
16172
|
+
const tomb = `${lock}.release-${randomBytes3(6).toString("hex")}`;
|
|
15946
16173
|
try {
|
|
15947
16174
|
fs12.renameSync(lock, tomb);
|
|
15948
16175
|
} catch {
|
|
@@ -15959,7 +16186,7 @@ var init_ledger = __esm(() => {
|
|
|
15959
16186
|
},
|
|
15960
16187
|
token: lockToken,
|
|
15961
16188
|
reap(lock, observed) {
|
|
15962
|
-
const tomb = `${lock}.reap-${
|
|
16189
|
+
const tomb = `${lock}.reap-${randomBytes3(6).toString("hex")}`;
|
|
15963
16190
|
try {
|
|
15964
16191
|
fs12.renameSync(lock, tomb);
|
|
15965
16192
|
} catch {
|
|
@@ -15978,7 +16205,24 @@ var init_ledger = __esm(() => {
|
|
|
15978
16205
|
}
|
|
15979
16206
|
};
|
|
15980
16207
|
TAIL_BYTES = 64 * 1024;
|
|
16208
|
+
TAMPER_REASONS = new Set(["hash_mismatch", "broken_link"]);
|
|
16209
|
+
tamperCache = new Map;
|
|
15981
16210
|
RENAMED = /^branch: renamed refs\/heads\/(.+) to refs\/heads\/(.+)$/iu;
|
|
16211
|
+
TRUST_REASONS = new Set([
|
|
16212
|
+
"unsigned",
|
|
16213
|
+
"hash_mismatch",
|
|
16214
|
+
"broken_link",
|
|
16215
|
+
"duplicate_id",
|
|
16216
|
+
"unissued_session",
|
|
16217
|
+
"unbound_session"
|
|
16218
|
+
]);
|
|
16219
|
+
SHELL_VOUCHED_HOSTS = new Set([
|
|
16220
|
+
"cli",
|
|
16221
|
+
"opencode",
|
|
16222
|
+
"pi",
|
|
16223
|
+
"codex_cli",
|
|
16224
|
+
"codex_desktop"
|
|
16225
|
+
]);
|
|
15982
16226
|
OBSERVED_ONLY = new Set([
|
|
15983
16227
|
"check",
|
|
15984
16228
|
"merge.unverified",
|
|
@@ -63713,7 +63957,8 @@ var init_changelog = __esm(() => {
|
|
|
63713
63957
|
});
|
|
63714
63958
|
|
|
63715
63959
|
// packages/workit-core/src/core/hygiene.ts
|
|
63716
|
-
import {
|
|
63960
|
+
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
63961
|
+
import { existsSync as existsSync9, readFileSync as readFileSync9, readdirSync as readdirSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
63717
63962
|
import path26 from "node:path";
|
|
63718
63963
|
var repoRoot2, templatesDir = () => path26.join(repoRoot2, "templates", "hygiene"), packageJson = (root) => {
|
|
63719
63964
|
if (!existsSync9(path26.join(root, "package.json")))
|
|
@@ -63743,6 +63988,23 @@ var repoRoot2, templatesDir = () => path26.join(repoRoot2, "templates", "hygiene
|
|
|
63743
63988
|
}
|
|
63744
63989
|
}
|
|
63745
63990
|
return "";
|
|
63991
|
+
}, projectTarget = (dir) => {
|
|
63992
|
+
const start = path26.resolve(dir);
|
|
63993
|
+
const top = spawnSync12("git", ["rev-parse", "--show-toplevel"], { cwd: start, encoding: "utf8" });
|
|
63994
|
+
const root = top.status === 0 ? (top.stdout ?? "").trim() : "";
|
|
63995
|
+
if (root)
|
|
63996
|
+
return { ok: true, root: path26.resolve(root) };
|
|
63997
|
+
let children = [];
|
|
63998
|
+
try {
|
|
63999
|
+
children = readdirSync5(start, { withFileTypes: true }).filter((entry) => entry.isDirectory() && existsSync9(path26.join(start, entry.name, ".git"))).map((entry) => path26.join(start, entry.name)).toSorted();
|
|
64000
|
+
} catch {
|
|
64001
|
+
children = [];
|
|
64002
|
+
}
|
|
64003
|
+
const where = children.length > 0 ? `run it inside one of its repositories: ${children.slice(0, 5).map((child) => `cd ${child}`).join(", ")}${children.length > 5 ? ` (+${children.length - 5} more)` : ""}` : "cd <repo> (or git init here) and run workit init again";
|
|
64004
|
+
return {
|
|
64005
|
+
ok: false,
|
|
64006
|
+
refused: `${start} is not a git repository, so no project files were added there; ${where}`
|
|
64007
|
+
};
|
|
63746
64008
|
}, planHygieneFiles = (root, opts = {}) => {
|
|
63747
64009
|
const openSource = opts.includeOpenSource ?? isOpenSource(root);
|
|
63748
64010
|
const files = ["CHANGELOG.md", "README.md", ".editorconfig", ".gitattributes"];
|
|
@@ -63967,13 +64229,13 @@ var init_support_matrix = __esm(() => {
|
|
|
63967
64229
|
});
|
|
63968
64230
|
|
|
63969
64231
|
// packages/workit-cli/src/admin/host-install.ts
|
|
63970
|
-
import { spawnSync as
|
|
64232
|
+
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
63971
64233
|
import {
|
|
63972
64234
|
accessSync,
|
|
63973
64235
|
constants as constants2,
|
|
63974
64236
|
existsSync as existsSync11,
|
|
63975
64237
|
readFileSync as readFileSync11,
|
|
63976
|
-
readdirSync as
|
|
64238
|
+
readdirSync as readdirSync6,
|
|
63977
64239
|
realpathSync as realpathSync6,
|
|
63978
64240
|
statSync as statSync2
|
|
63979
64241
|
} from "node:fs";
|
|
@@ -64016,7 +64278,7 @@ function findHostExecutable(name, options = {}) {
|
|
|
64016
64278
|
[path29.join(home, ".nvm", "versions", "node"), "bin"]
|
|
64017
64279
|
]) {
|
|
64018
64280
|
try {
|
|
64019
|
-
for (const entry of
|
|
64281
|
+
for (const entry of readdirSync6(base, { withFileTypes: true })) {
|
|
64020
64282
|
if (entry.isDirectory())
|
|
64021
64283
|
dirs.push(path29.join(base, entry.name, tail));
|
|
64022
64284
|
}
|
|
@@ -64238,7 +64500,7 @@ function runHostCommand(step, context = {}) {
|
|
|
64238
64500
|
stderr: `Cannot safely execute batch shim ${step.command}; resolve its Node.js CLI entrypoint and retry`
|
|
64239
64501
|
};
|
|
64240
64502
|
}
|
|
64241
|
-
const result =
|
|
64503
|
+
const result = spawnSync13(step.command, step.args, {
|
|
64242
64504
|
cwd: step.cwd,
|
|
64243
64505
|
env: (() => {
|
|
64244
64506
|
const home = context.home ?? context.env?.HOME ?? os7.homedir();
|
|
@@ -64292,6 +64554,40 @@ var resolveWindowsShimScript = (shim) => {
|
|
|
64292
64554
|
} catch {
|
|
64293
64555
|
return false;
|
|
64294
64556
|
}
|
|
64557
|
+
}, OPENCODE_PLUGIN_SCOPE = "@brainervirus", OPENCODE_PLUGIN_NAME = "workit-opencode", packageVersionAt = (root, name) => {
|
|
64558
|
+
try {
|
|
64559
|
+
const pkg = JSON.parse(readFileSync11(path29.join(root, "package.json"), "utf8"));
|
|
64560
|
+
return pkg.name === name && typeof pkg.version === "string" ? pkg.version : null;
|
|
64561
|
+
} catch {
|
|
64562
|
+
return null;
|
|
64563
|
+
}
|
|
64564
|
+
}, compareVersions = (a, b) => {
|
|
64565
|
+
const pa = a.split(/[.+-]/).map((n) => Number(n) || 0);
|
|
64566
|
+
const pb = b.split(/[.+-]/).map((n) => Number(n) || 0);
|
|
64567
|
+
for (let i = 0;i < Math.max(pa.length, pb.length); i++) {
|
|
64568
|
+
const d = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
64569
|
+
if (d !== 0)
|
|
64570
|
+
return d;
|
|
64571
|
+
}
|
|
64572
|
+
return 0;
|
|
64573
|
+
}, openCodeCacheDir = (home, env = process.env) => path29.join(env.XDG_CACHE_HOME || path29.join(home, ".cache"), "opencode"), subdirs = (dir) => {
|
|
64574
|
+
try {
|
|
64575
|
+
return readdirSync6(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path29.join(dir, entry.name));
|
|
64576
|
+
} catch {
|
|
64577
|
+
return [];
|
|
64578
|
+
}
|
|
64579
|
+
}, openCodeInstalledPlugin = (home, env = process.env) => {
|
|
64580
|
+
const name = `${OPENCODE_PLUGIN_SCOPE}/${OPENCODE_PLUGIN_NAME}`;
|
|
64581
|
+
const cache = openCodeCacheDir(home, env);
|
|
64582
|
+
const specDirs = (layout) => subdirs(path29.join(cache, layout, OPENCODE_PLUGIN_SCOPE)).filter((dir) => path29.basename(dir) === `${OPENCODE_PLUGIN_NAME}@latest`);
|
|
64583
|
+
const installs = [...specDirs("npm").flatMap(subdirs), ...specDirs("packages")].map((dir) => path29.join(dir, "node_modules", OPENCODE_PLUGIN_SCOPE, OPENCODE_PLUGIN_NAME));
|
|
64584
|
+
let best = null;
|
|
64585
|
+
for (const root of installs) {
|
|
64586
|
+
const version = packageVersionAt(root, name);
|
|
64587
|
+
if (version && (!best || compareVersions(version, best.version) > 0))
|
|
64588
|
+
best = { root, version };
|
|
64589
|
+
}
|
|
64590
|
+
return best;
|
|
64295
64591
|
}, isCanonicalCodexPackage = (root) => {
|
|
64296
64592
|
for (const file of [
|
|
64297
64593
|
path29.join(root, ".codex-plugin", "plugin.json"),
|
|
@@ -64311,7 +64607,7 @@ var resolveWindowsShimScript = (shim) => {
|
|
|
64311
64607
|
if (isCanonicalCodexPackage(root))
|
|
64312
64608
|
return true;
|
|
64313
64609
|
try {
|
|
64314
|
-
return
|
|
64610
|
+
return readdirSync6(root, { withFileTypes: true }).some((entry) => entry.isDirectory() && isCanonicalCodexPackage(path29.join(root, entry.name)));
|
|
64315
64611
|
} catch {
|
|
64316
64612
|
return false;
|
|
64317
64613
|
}
|
|
@@ -64320,7 +64616,7 @@ var resolveWindowsShimScript = (shim) => {
|
|
|
64320
64616
|
return true;
|
|
64321
64617
|
const root = path29.join(codexHome, "plugins", "cache");
|
|
64322
64618
|
try {
|
|
64323
|
-
for (const marketplace of
|
|
64619
|
+
for (const marketplace of readdirSync6(root, { withFileTypes: true })) {
|
|
64324
64620
|
if (!marketplace.isDirectory())
|
|
64325
64621
|
continue;
|
|
64326
64622
|
if (isPackageOrVersionDirectory(path29.join(root, marketplace.name, "workit")))
|
|
@@ -64398,9 +64694,9 @@ var init_host_install = __esm(() => {
|
|
|
64398
64694
|
});
|
|
64399
64695
|
|
|
64400
64696
|
// packages/workit-core/src/core/skill-manifests.ts
|
|
64401
|
-
import { cpSync as cpSync3, existsSync as existsSync12, readFileSync as readFileSync12, readdirSync as
|
|
64697
|
+
import { cpSync as cpSync3, existsSync as existsSync12, readFileSync as readFileSync12, readdirSync as readdirSync7, statSync as statSync3, writeFileSync as writeFileSync5 } from "node:fs";
|
|
64402
64698
|
import path30 from "node:path";
|
|
64403
|
-
var WORKIT_METHOD_SKILLS, skillManifestNames = (root) => existsSync12(root) ?
|
|
64699
|
+
var WORKIT_METHOD_SKILLS, skillManifestNames = (root) => existsSync12(root) ? readdirSync7(root).filter((name) => existsSync12(path30.join(root, name, "SKILL.md"))).toSorted() : [], validateSkillManifests = (root, expected, label) => {
|
|
64404
64700
|
const actual = skillManifestNames(root);
|
|
64405
64701
|
const missing = expected.filter((name) => !actual.includes(name));
|
|
64406
64702
|
const extra = actual.filter((name) => !expected.includes(name));
|
|
@@ -64412,7 +64708,7 @@ var WORKIT_METHOD_SKILLS, skillManifestNames = (root) => existsSync12(root) ? re
|
|
|
64412
64708
|
const pending = [path30.join(pluginDir, "skills")];
|
|
64413
64709
|
while (pending.length > 0) {
|
|
64414
64710
|
const dir = pending.pop();
|
|
64415
|
-
for (const entry of
|
|
64711
|
+
for (const entry of readdirSync7(dir, { withFileTypes: true })) {
|
|
64416
64712
|
const file = path30.join(dir, entry.name);
|
|
64417
64713
|
if (entry.isDirectory())
|
|
64418
64714
|
pending.push(file);
|
|
@@ -64443,7 +64739,7 @@ var init_skill_manifests = __esm(() => {
|
|
|
64443
64739
|
});
|
|
64444
64740
|
|
|
64445
64741
|
// packages/workit-core/src/git/session-hook.ts
|
|
64446
|
-
import { spawnSync as
|
|
64742
|
+
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
64447
64743
|
import {
|
|
64448
64744
|
chmodSync,
|
|
64449
64745
|
existsSync as existsSync13,
|
|
@@ -64638,7 +64934,7 @@ add_trailer "$(git rev-parse --git-path COMMIT_EDITMSG)"
|
|
|
64638
64934
|
[ "$2" != merge ] || add_trailer "$(git rev-parse --git-path MERGE_MSG)"
|
|
64639
64935
|
exit 0
|
|
64640
64936
|
`, manualTrailerLine = () => `h="$(git rev-parse --git-common-dir 2>/dev/null)/${HELPER.join("/")}"; [ ! -f "$h" ] || sh "$h" "$@" || true`, lefthookTrailerRun = () => `set -f; IFS=; h=$(git rev-parse --git-common-dir 2>/dev/null)/${HELPER.join("/")}; [ ! -f $h ] || sh $h '' {2} {3} || true`, HELPER_REF, SESSION_HOOK_SCRIPT, run2 = (cwd, args, env) => {
|
|
64641
|
-
const r =
|
|
64937
|
+
const r = spawnSync14("git", args, {
|
|
64642
64938
|
cwd,
|
|
64643
64939
|
encoding: "utf8",
|
|
64644
64940
|
timeout: GIT_TIMEOUTS.local,
|
|
@@ -64799,14 +65095,14 @@ ${manualTrailerLine()}
|
|
|
64799
65095
|
});
|
|
64800
65096
|
|
|
64801
65097
|
// packages/workit-cli/src/admin/doctor.ts
|
|
64802
|
-
import { spawnSync as
|
|
65098
|
+
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
64803
65099
|
import { createHash as createHash14 } from "node:crypto";
|
|
64804
65100
|
import {
|
|
64805
65101
|
existsSync as existsSync14,
|
|
64806
65102
|
lstatSync as lstatSync5,
|
|
64807
65103
|
mkdirSync as mkdirSync6,
|
|
64808
65104
|
mkdtempSync,
|
|
64809
|
-
readdirSync as
|
|
65105
|
+
readdirSync as readdirSync8,
|
|
64810
65106
|
readFileSync as readFileSync14,
|
|
64811
65107
|
readlinkSync as readlinkSync2,
|
|
64812
65108
|
rmSync as rmSync3,
|
|
@@ -64832,7 +65128,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
64832
65128
|
const configDir = options.configDir ?? env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path32.join(home, ".config", "workit");
|
|
64833
65129
|
const stateDir = options.stateDir ?? env.WORKFLOW_TOOLKIT_STATE ?? path32.join(home, ".local", "state", "workit");
|
|
64834
65130
|
const cwd = options.cwd ?? process.cwd();
|
|
64835
|
-
const dev = options.dev
|
|
65131
|
+
const dev = options.dev?.trim() || env.WORKFLOW_TOOLKIT_DEV?.trim() || findDevFromCwd(cwd);
|
|
64836
65132
|
return {
|
|
64837
65133
|
host: options.host ?? "cli",
|
|
64838
65134
|
home,
|
|
@@ -64842,7 +65138,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
64842
65138
|
workspaceRoot: options.workspaceRoot ?? env.WORKFLOW_WORKSPACE_ROOT ?? cwd,
|
|
64843
65139
|
dev,
|
|
64844
65140
|
opencodeConfig: options.opencodeConfig ?? path32.join(home, ".config", "opencode", "opencode.json"),
|
|
64845
|
-
opencodePackageCacheDir: options.opencodePackageCacheDir ??
|
|
65141
|
+
opencodePackageCacheDir: options.opencodePackageCacheDir ?? null,
|
|
64846
65142
|
cursorSettings: options.cursorSettings ?? path32.join(home, ".cursor", "settings.json"),
|
|
64847
65143
|
cursorMcp: options.cursorMcp ?? path32.join(home, ".cursor", "mcp.json"),
|
|
64848
65144
|
cursorPluginDir: options.cursorPluginDir ?? path32.join(home, ".cursor", "plugins", "local", "workit"),
|
|
@@ -64888,7 +65184,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
64888
65184
|
}
|
|
64889
65185
|
return null;
|
|
64890
65186
|
}, versionOf = (bin, env, timeout) => {
|
|
64891
|
-
const r =
|
|
65187
|
+
const r = spawnSync15(bin, ["--version"], { encoding: "utf8", env, timeout });
|
|
64892
65188
|
if (r.error)
|
|
64893
65189
|
return null;
|
|
64894
65190
|
return (r.stdout ?? "").trim();
|
|
@@ -64906,7 +65202,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
64906
65202
|
if (env.BUN && existsSync14(env.BUN))
|
|
64907
65203
|
return env.BUN;
|
|
64908
65204
|
for (const candidate of [
|
|
64909
|
-
path32.join(os8.homedir(), ".bun/bin/bun"),
|
|
65205
|
+
path32.join(env.HOME || os8.homedir(), ".bun/bin/bun"),
|
|
64910
65206
|
"/usr/local/bin/bun",
|
|
64911
65207
|
"/usr/bin/bun"
|
|
64912
65208
|
]) {
|
|
@@ -64915,47 +65211,50 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
64915
65211
|
}
|
|
64916
65212
|
return commandOnPath("bun", env) ? "bun" : null;
|
|
64917
65213
|
}, checkRuntime = (res) => {
|
|
64918
|
-
const
|
|
64919
|
-
const
|
|
65214
|
+
const running = `v${process.versions.node}`;
|
|
65215
|
+
const runningOk = semverAtLeast(running, SUPPORT_MATRIX.node.minimum);
|
|
65216
|
+
const pathNode = versionOf("node", res.env);
|
|
64920
65217
|
const bun = resolveBun(res.env);
|
|
64921
65218
|
const bunVersion = bun ? versionOf(bun, res.env) : null;
|
|
64922
65219
|
const bunOk = bunVersion !== null;
|
|
64923
65220
|
const needsBun = res.dev !== null;
|
|
64924
|
-
if (!
|
|
65221
|
+
if (!runningOk) {
|
|
64925
65222
|
return {
|
|
64926
65223
|
id: "runtime",
|
|
64927
65224
|
status: "fail",
|
|
64928
|
-
detail: `node ${SUPPORT_MATRIX.node.minimum}+ required (
|
|
65225
|
+
detail: `node ${SUPPORT_MATRIX.node.minimum}+ required (workit runs under ${running} at ${process.execPath})`,
|
|
64929
65226
|
fix: `Install Node ${SUPPORT_MATRIX.node.minimum}+ (declared support: ${SUPPORT_MATRIX.node.minimum}/current)`
|
|
64930
65227
|
};
|
|
64931
65228
|
}
|
|
64932
|
-
|
|
65229
|
+
const pathNote = pathNode !== null && pathNode !== running ? `; node on PATH is ${pathNode}${semverAtLeast(pathNode, SUPPORT_MATRIX.node.minimum) ? "" : ` (below ${SUPPORT_MATRIX.node.minimum}; it does not run this CLI)`}` : "";
|
|
65230
|
+
if (pathNode === null) {
|
|
64933
65231
|
return {
|
|
64934
65232
|
id: "runtime",
|
|
64935
|
-
status: "
|
|
64936
|
-
detail: `
|
|
64937
|
-
fix: `
|
|
65233
|
+
status: "warn",
|
|
65234
|
+
detail: `workit runs under node ${running}, but no node is on PATH for host hooks`,
|
|
65235
|
+
fix: `Put Node ${SUPPORT_MATRIX.node.minimum}+ on PATH (host hooks launch \`node\`)`
|
|
64938
65236
|
};
|
|
64939
65237
|
}
|
|
64940
|
-
if (!bunOk) {
|
|
65238
|
+
if (!bunOk && needsBun) {
|
|
64941
65239
|
return {
|
|
64942
65240
|
id: "runtime",
|
|
64943
|
-
status: "
|
|
64944
|
-
detail:
|
|
64945
|
-
fix:
|
|
65241
|
+
status: "fail",
|
|
65242
|
+
detail: `bun not found on PATH (dev checkout requires the pinned toolchain ${SUPPORT_MATRIX.bun})`,
|
|
65243
|
+
fix: `Install bun ${SUPPORT_MATRIX.bun} (curl -fsSL https://bun.sh/install | bash)`
|
|
64946
65244
|
};
|
|
64947
65245
|
}
|
|
65246
|
+
const pathOld = !semverAtLeast(pathNode, SUPPORT_MATRIX.node.minimum);
|
|
64948
65247
|
return {
|
|
64949
65248
|
id: "runtime",
|
|
64950
|
-
status: "pass",
|
|
64951
|
-
detail: `node ${
|
|
65249
|
+
status: bunOk && !pathOld ? "pass" : "info",
|
|
65250
|
+
detail: `node ${running} (>=${SUPPORT_MATRIX.node.minimum})${pathNote}, ${bunOk ? `bun ${bunVersion}` : "no bun (needed only for a dev checkout)"}`
|
|
64952
65251
|
};
|
|
64953
65252
|
}, checkVersions = (res) => {
|
|
64954
65253
|
if (!res.dev) {
|
|
64955
65254
|
return {
|
|
64956
65255
|
id: "versions",
|
|
64957
|
-
status: "
|
|
64958
|
-
detail: "
|
|
65256
|
+
status: "info",
|
|
65257
|
+
detail: "installed package, not a dev checkout — version parity applies to checkouts only"
|
|
64959
65258
|
};
|
|
64960
65259
|
}
|
|
64961
65260
|
const corePkg = readJson(path32.join(res.dev, "packages/workit-core/package.json"));
|
|
@@ -64969,7 +65268,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
64969
65268
|
const refs = new Set;
|
|
64970
65269
|
for (const name of ["workit-opencode", "workit-cursor", "workit-cli"]) {
|
|
64971
65270
|
const pkg = readJson(path32.join(res.dev, "packages", name, "package.json"));
|
|
64972
|
-
const dep = pkg?.dependencies?.["@brainervirus/workit-core"];
|
|
65271
|
+
const dep = pkg?.dependencies?.["@brainervirus/workit-core"] ?? pkg?.devDependencies?.["@brainervirus/workit-core"];
|
|
64973
65272
|
if (typeof dep === "string")
|
|
64974
65273
|
refs.add(dep);
|
|
64975
65274
|
}
|
|
@@ -65066,10 +65365,16 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
65066
65365
|
status: "pass",
|
|
65067
65366
|
detail: `codex CLI ${installed} matches the qualified pin`
|
|
65068
65367
|
};
|
|
65368
|
+
if (semverAtLeast(installed, qualified))
|
|
65369
|
+
return {
|
|
65370
|
+
id: "codex_pin",
|
|
65371
|
+
status: "info",
|
|
65372
|
+
detail: `codex CLI ${installed} is newer than the qualified ${qualified}`
|
|
65373
|
+
};
|
|
65069
65374
|
return {
|
|
65070
65375
|
id: "codex_pin",
|
|
65071
65376
|
status: "warn",
|
|
65072
|
-
detail: `codex CLI ${installed}
|
|
65377
|
+
detail: `codex CLI ${installed} is older than the qualified ${qualified}`,
|
|
65073
65378
|
fix: `Reinstall the qualified Codex CLI ${qualified} or record fresh qualification evidence`
|
|
65074
65379
|
};
|
|
65075
65380
|
}, readTomlTables = (file) => {
|
|
@@ -65126,7 +65431,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
65126
65431
|
const dir = path32.join(codexHome, "plugins", "cache", marketplace, "workit");
|
|
65127
65432
|
let versions = [];
|
|
65128
65433
|
try {
|
|
65129
|
-
versions =
|
|
65434
|
+
versions = readdirSync8(dir).filter((v) => existsSync14(path32.join(dir, v, ".codex-plugin")));
|
|
65130
65435
|
} catch {
|
|
65131
65436
|
versions = [];
|
|
65132
65437
|
}
|
|
@@ -65235,7 +65540,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
65235
65540
|
const installed = [];
|
|
65236
65541
|
for (const install of installs) {
|
|
65237
65542
|
const source = install.root ? path32.join(install.root, "agents") : null;
|
|
65238
|
-
const files = source && existsSync14(source) ?
|
|
65543
|
+
const files = source && existsSync14(source) ? readdirSync8(source).filter((f) => f.endsWith(".toml")).toSorted() : [];
|
|
65239
65544
|
if (files.length === 0) {
|
|
65240
65545
|
problems.push(`${install.id} ${install.version ?? "?"} bundles no agents`);
|
|
65241
65546
|
fixes.push(`codex plugin remove ${install.id} && codex plugin add ${install.id}`);
|
|
@@ -65463,8 +65768,8 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
65463
65768
|
if (!dev && res.host === "opencode") {
|
|
65464
65769
|
return {
|
|
65465
65770
|
id: "assets",
|
|
65466
|
-
status: "
|
|
65467
|
-
detail: "
|
|
65771
|
+
status: "info",
|
|
65772
|
+
detail: "installed package — OpenCode loads the published assets, no checkout to compare"
|
|
65468
65773
|
};
|
|
65469
65774
|
}
|
|
65470
65775
|
return {
|
|
@@ -65499,7 +65804,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
65499
65804
|
`)) {
|
|
65500
65805
|
return false;
|
|
65501
65806
|
}
|
|
65502
|
-
return
|
|
65807
|
+
return spawnSync15(runtime, ["--check", entry], { encoding: "utf8", env }).status === 0;
|
|
65503
65808
|
} catch {
|
|
65504
65809
|
return false;
|
|
65505
65810
|
}
|
|
@@ -65589,8 +65894,8 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
65589
65894
|
if (!dev && res.host !== "cursor") {
|
|
65590
65895
|
return {
|
|
65591
65896
|
id: "launcher",
|
|
65592
|
-
status: "
|
|
65593
|
-
detail: "
|
|
65897
|
+
status: "info",
|
|
65898
|
+
detail: "installed package — non-Cursor launchers ship inside each host plugin, no checkout to check"
|
|
65594
65899
|
};
|
|
65595
65900
|
}
|
|
65596
65901
|
return {
|
|
@@ -65630,7 +65935,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
65630
65935
|
fix: "Re-run install-cursor-plugin.sh (or `workit init` with Cursor) to rewrite the hook entries"
|
|
65631
65936
|
};
|
|
65632
65937
|
const started = performance.now();
|
|
65633
|
-
const probe =
|
|
65938
|
+
const probe = spawnSync15("node", [launcher, "--probe", "workit-cursor-hook"], {
|
|
65634
65939
|
encoding: "utf8",
|
|
65635
65940
|
env: { ...res.env, WORKIT_CURSOR_HOOK_NPX_OFFLINE: "1" },
|
|
65636
65941
|
timeout: 40000,
|
|
@@ -65699,18 +66004,18 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
65699
66004
|
fix: "Install git and ensure it is on PATH"
|
|
65700
66005
|
};
|
|
65701
66006
|
}
|
|
65702
|
-
if (process.platform !== "win32" && !commandOnPath("flock", res.env)) {
|
|
66007
|
+
if (res.dev && process.platform !== "win32" && !commandOnPath("flock", res.env)) {
|
|
65703
66008
|
return {
|
|
65704
66009
|
id: "utility",
|
|
65705
66010
|
status: "warn",
|
|
65706
|
-
detail: "flock
|
|
65707
|
-
fix: "Install util-linux (flock)"
|
|
66011
|
+
detail: "flock not found — the dev checkout's sync-runtime requires it for locking",
|
|
66012
|
+
fix: process.platform === "darwin" ? "brew install flock (Homebrew's util-linux is keg-only and leaves flock off PATH)" : "Install util-linux (flock)"
|
|
65708
66013
|
};
|
|
65709
66014
|
}
|
|
65710
66015
|
return {
|
|
65711
66016
|
id: "utility",
|
|
65712
66017
|
status: "pass",
|
|
65713
|
-
detail: "git
|
|
66018
|
+
detail: res.dev ? "git and flock on PATH" : "git on PATH"
|
|
65714
66019
|
};
|
|
65715
66020
|
}, isFragileCachePin = (entry) => {
|
|
65716
66021
|
const n = entry.replaceAll("\\", "/").toLowerCase();
|
|
@@ -65804,7 +66109,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
65804
66109
|
if (!cmd)
|
|
65805
66110
|
return null;
|
|
65806
66111
|
try {
|
|
65807
|
-
const r =
|
|
66112
|
+
const r = spawnSync15(cmd, ["view", pkgName, "version"], {
|
|
65808
66113
|
encoding: "utf8",
|
|
65809
66114
|
timeout: 20000,
|
|
65810
66115
|
env: res.env
|
|
@@ -65817,9 +66122,18 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
65817
66122
|
}, installedPluginVersion = (res) => {
|
|
65818
66123
|
const pkg = readJson(path32.join(res.cursorPluginDir, "package.json"));
|
|
65819
66124
|
return typeof pkg?.version === "string" && pkg.version ? pkg.version : null;
|
|
65820
|
-
},
|
|
65821
|
-
|
|
65822
|
-
|
|
66125
|
+
}, installedOpenCodeCache = (res) => {
|
|
66126
|
+
if (res.opencodePackageCacheDir !== null) {
|
|
66127
|
+
const pkg = readJson(path32.join(res.opencodePackageCacheDir, "node_modules", "@brainervirus", "workit-opencode", "package.json"));
|
|
66128
|
+
return typeof pkg?.version === "string" && pkg.version ? { version: pkg.version, dir: res.opencodePackageCacheDir } : null;
|
|
66129
|
+
}
|
|
66130
|
+
const found = openCodeInstalledPlugin(res.home, res.env);
|
|
66131
|
+
if (!found)
|
|
66132
|
+
return null;
|
|
66133
|
+
let dir = path32.dirname(path32.dirname(path32.dirname(found.root)));
|
|
66134
|
+
if (path32.basename(dir) !== "workit-opencode@latest")
|
|
66135
|
+
dir = path32.dirname(dir);
|
|
66136
|
+
return { version: found.version, dir };
|
|
65823
66137
|
}, checkOpenCodePackageCache = (res) => {
|
|
65824
66138
|
if (!existsSync14(res.opencodeConfig)) {
|
|
65825
66139
|
return {
|
|
@@ -65851,8 +66165,9 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
65851
66165
|
detail: `opencode pin ${pin} is an exact version — not compared to registry latest`
|
|
65852
66166
|
};
|
|
65853
66167
|
}
|
|
65854
|
-
const
|
|
65855
|
-
|
|
66168
|
+
const cache = installedOpenCodeCache(res);
|
|
66169
|
+
const installed = cache?.version ?? null;
|
|
66170
|
+
if (cache === null || installed === null) {
|
|
65856
66171
|
return {
|
|
65857
66172
|
id: "stale_install",
|
|
65858
66173
|
status: "pass",
|
|
@@ -65866,7 +66181,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
65866
66181
|
status: "warn",
|
|
65867
66182
|
registryProbed: true,
|
|
65868
66183
|
detail: "registry_unreachable: cannot compare OpenCode @latest cache against published workit-opencode",
|
|
65869
|
-
fix: `Retry when npm is reachable, or remove ${
|
|
66184
|
+
fix: `Retry when npm is reachable, or remove ${cache.dir} and restart OpenCode to re-resolve`
|
|
65870
66185
|
};
|
|
65871
66186
|
}
|
|
65872
66187
|
if (!semverAtLeast(installed, expected)) {
|
|
@@ -65875,7 +66190,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
65875
66190
|
status: "fail",
|
|
65876
66191
|
registryProbed: true,
|
|
65877
66192
|
detail: `stale_install: OpenCode @latest cache has workit-opencode ${installed} behind published ${expected}`,
|
|
65878
|
-
fix: `Remove ${
|
|
66193
|
+
fix: `Remove ${cache.dir} and restart OpenCode — it re-resolves @latest on the next launch`
|
|
65879
66194
|
};
|
|
65880
66195
|
}
|
|
65881
66196
|
return {
|
|
@@ -66344,7 +66659,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
66344
66659
|
}, defaultIdentityProbes = (env) => ({
|
|
66345
66660
|
origin: (cwd) => {
|
|
66346
66661
|
try {
|
|
66347
|
-
const out =
|
|
66662
|
+
const out = spawnSync15("git", ["remote", "get-url", "origin"], {
|
|
66348
66663
|
cwd,
|
|
66349
66664
|
encoding: "utf8",
|
|
66350
66665
|
env
|
|
@@ -66356,7 +66671,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
66356
66671
|
},
|
|
66357
66672
|
ghLogin: (e) => {
|
|
66358
66673
|
try {
|
|
66359
|
-
const out =
|
|
66674
|
+
const out = spawnSync15("gh", ["api", "user", "--jq", ".login"], { encoding: "utf8", env: e });
|
|
66360
66675
|
const login = (out.stdout ?? "").trim();
|
|
66361
66676
|
return out.status === 0 && login ? login : null;
|
|
66362
66677
|
} catch {
|
|
@@ -66365,7 +66680,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
66365
66680
|
},
|
|
66366
66681
|
glabLogin: (e) => {
|
|
66367
66682
|
try {
|
|
66368
|
-
const out =
|
|
66683
|
+
const out = spawnSync15("glab", ["api", "user", "--jq", ".username"], {
|
|
66369
66684
|
encoding: "utf8",
|
|
66370
66685
|
env: e
|
|
66371
66686
|
});
|
|
@@ -66377,7 +66692,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
66377
66692
|
},
|
|
66378
66693
|
sshLogin: (host) => {
|
|
66379
66694
|
try {
|
|
66380
|
-
const out =
|
|
66695
|
+
const out = spawnSync15("ssh", ["-o", "BatchMode=yes", "-o", "ConnectTimeout=5", "-T", `git@${host}`], {
|
|
66381
66696
|
encoding: "utf8",
|
|
66382
66697
|
env
|
|
66383
66698
|
});
|
|
@@ -66534,10 +66849,10 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
66534
66849
|
fix: repair,
|
|
66535
66850
|
registryProbed
|
|
66536
66851
|
};
|
|
66537
|
-
}, HOOK_ISSUED_MARKER = "workit-issued-sessions/
|
|
66852
|
+
}, HOOK_ISSUED_MARKER = "workit-issued-sessions/3", hookRecordsIssued = (root) => {
|
|
66538
66853
|
let files;
|
|
66539
66854
|
try {
|
|
66540
|
-
files =
|
|
66855
|
+
files = readdirSync8(path32.join(root, "dist")).filter((f) => f.endsWith(".js"));
|
|
66541
66856
|
} catch {
|
|
66542
66857
|
return null;
|
|
66543
66858
|
}
|
|
@@ -66596,12 +66911,12 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
66596
66911
|
if (cached)
|
|
66597
66912
|
return cached;
|
|
66598
66913
|
const viaShell = process.platform === "win32" && /\.(?:cmd|bat)$/i.test(bin);
|
|
66599
|
-
const r = viaShell ?
|
|
66914
|
+
const r = viaShell ? spawnSync15(`"${bin}" --version`, {
|
|
66600
66915
|
encoding: "utf8",
|
|
66601
66916
|
env,
|
|
66602
66917
|
timeout: WORKIT_PROBE_TIMEOUT_MS,
|
|
66603
66918
|
shell: true
|
|
66604
|
-
}) :
|
|
66919
|
+
}) : spawnSync15(bin, ["--version"], { encoding: "utf8", env, timeout: WORKIT_PROBE_TIMEOUT_MS });
|
|
66605
66920
|
const timedOut = r.error !== undefined && "code" in r.error && r.error.code === "ETIMEDOUT";
|
|
66606
66921
|
const version = r.status === 0 ? (r.stdout ?? "").match(SEMVER)?.[0] ?? null : null;
|
|
66607
66922
|
const probe = { version: timedOut ? null : version, timedOut };
|
|
@@ -66614,7 +66929,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
66614
66929
|
version: i.version
|
|
66615
66930
|
})),
|
|
66616
66931
|
{ label: "Cursor plugin", version: installedPluginVersion(res) },
|
|
66617
|
-
{ label: "OpenCode plugin", version:
|
|
66932
|
+
{ label: "OpenCode plugin", version: installedOpenCodeCache(res)?.version ?? null }
|
|
66618
66933
|
].filter((p) => p.version !== null);
|
|
66619
66934
|
const newest = plugins.reduce((best, p) => best && semverAtLeast(best.version, p.version) ? best : p, null);
|
|
66620
66935
|
const target = newest?.version ?? "latest";
|
|
@@ -66655,7 +66970,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
66655
66970
|
} : c) : raw;
|
|
66656
66971
|
const failed = checks.filter((c) => c.status === "fail").length;
|
|
66657
66972
|
const warned = checks.filter((c) => c.status === "warn").length;
|
|
66658
|
-
const passed = checks.filter((c) => c.status === "pass").length;
|
|
66973
|
+
const passed = checks.filter((c) => c.status === "pass" || c.status === "info").length;
|
|
66659
66974
|
const exitCode = failed > 0 ? 1 : 0;
|
|
66660
66975
|
const offline = !raw.some((c) => ("registryProbed" in c) && c.registryProbed);
|
|
66661
66976
|
const report = {
|
|
@@ -66746,13 +67061,14 @@ import {
|
|
|
66746
67061
|
mkdirSync as mkdirSync7,
|
|
66747
67062
|
mkdtempSync as mkdtempSync2,
|
|
66748
67063
|
readFileSync as readFileSync15,
|
|
66749
|
-
readdirSync as
|
|
67064
|
+
readdirSync as readdirSync9,
|
|
66750
67065
|
realpathSync as realpathSync7,
|
|
66751
67066
|
renameSync as renameSync4,
|
|
66752
67067
|
rmSync as rmSync4,
|
|
66753
67068
|
statSync as statSync6,
|
|
66754
67069
|
writeFileSync as writeFileSync8
|
|
66755
67070
|
} from "node:fs";
|
|
67071
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
66756
67072
|
import os9 from "node:os";
|
|
66757
67073
|
import path33 from "node:path";
|
|
66758
67074
|
import { isDeepStrictEqual as isDeepStrictEqual2 } from "node:util";
|
|
@@ -66789,6 +67105,7 @@ function buildSetupPreview(values, opts = {}) {
|
|
|
66789
67105
|
const blocked = [];
|
|
66790
67106
|
const mutations = [];
|
|
66791
67107
|
const preserved = [];
|
|
67108
|
+
let projectRefused;
|
|
66792
67109
|
const overrides = activeSetupOverrides(env);
|
|
66793
67110
|
for (const entry of [state.config, state.youtrack, state.vcs, state.workspaces]) {
|
|
66794
67111
|
if (entry.status === "malformed")
|
|
@@ -66911,8 +67228,11 @@ function buildSetupPreview(values, opts = {}) {
|
|
|
66911
67228
|
}
|
|
66912
67229
|
}
|
|
66913
67230
|
}
|
|
66914
|
-
|
|
66915
|
-
|
|
67231
|
+
const target = values.applyProject ? projectTarget(opts.cwd ?? process.cwd()) : null;
|
|
67232
|
+
if (target && !target.ok)
|
|
67233
|
+
projectRefused = target.refused;
|
|
67234
|
+
if (target?.ok) {
|
|
67235
|
+
const root = target.root;
|
|
66916
67236
|
const gitignorePath = path33.join(root, ".gitignore");
|
|
66917
67237
|
const existing = existsSync15(gitignorePath) ? readFileSync15(gitignorePath, "utf8") : "";
|
|
66918
67238
|
const existingLines = new Set(existing.split(`
|
|
@@ -66931,7 +67251,8 @@ function buildSetupPreview(values, opts = {}) {
|
|
|
66931
67251
|
overrides,
|
|
66932
67252
|
preserved,
|
|
66933
67253
|
platforms: values.platforms ?? [],
|
|
66934
|
-
state
|
|
67254
|
+
state,
|
|
67255
|
+
...projectRefused === undefined ? {} : { projectRefused }
|
|
66935
67256
|
};
|
|
66936
67257
|
}
|
|
66937
67258
|
function resolveApply(preview, options) {
|
|
@@ -67114,7 +67435,14 @@ function adapterRoot(platform, res, preferInstalledAdapter = false) {
|
|
|
67114
67435
|
if (res.dev) {
|
|
67115
67436
|
candidates.push(path33.join(res.dev, "packages", `workit-${platform}`));
|
|
67116
67437
|
} else {
|
|
67117
|
-
candidates.push(
|
|
67438
|
+
candidates.push(...nodeResolvedAdapters(platform));
|
|
67439
|
+
if (platform === "cursor")
|
|
67440
|
+
candidates.push(stagedCursor);
|
|
67441
|
+
if (platform === "opencode") {
|
|
67442
|
+
const cached = openCodeInstalledPlugin(res.home, res.env);
|
|
67443
|
+
if (cached)
|
|
67444
|
+
candidates.push(cached.root);
|
|
67445
|
+
}
|
|
67118
67446
|
let dir = path33.resolve(res.cwd);
|
|
67119
67447
|
while (true) {
|
|
67120
67448
|
candidates.push(path33.join(dir, "packages", `workit-${platform}`));
|
|
@@ -67124,17 +67452,14 @@ function adapterRoot(platform, res, preferInstalledAdapter = false) {
|
|
|
67124
67452
|
dir = parent;
|
|
67125
67453
|
}
|
|
67126
67454
|
}
|
|
67127
|
-
candidates.push(path33.join(res.home, ".local", "share", "workit", "packages", `workit-${platform}`));
|
|
67128
|
-
if (platform === "cursor")
|
|
67129
|
-
candidates.push(stagedCursor);
|
|
67130
67455
|
for (const candidate of candidates) {
|
|
67131
67456
|
if (isAdapter(candidate, platform))
|
|
67132
|
-
return candidate;
|
|
67457
|
+
return realRoot(candidate);
|
|
67133
67458
|
}
|
|
67134
67459
|
return null;
|
|
67135
67460
|
}
|
|
67136
67461
|
function applyOpenCode(root, res) {
|
|
67137
|
-
const resolvedPin = opencodePin(root, res);
|
|
67462
|
+
const resolvedPin = root === null ? OPENCODE_NPM_PIN : opencodePin(root, res);
|
|
67138
67463
|
if (!resolvedPin) {
|
|
67139
67464
|
return {
|
|
67140
67465
|
platform: "opencode",
|
|
@@ -67433,6 +67758,10 @@ function applySetupPreview(preview, options = {}) {
|
|
|
67433
67758
|
root = adapterRoot(platform, res, options.preferInstalledAdapter);
|
|
67434
67759
|
rootFor.set(platform, root);
|
|
67435
67760
|
}
|
|
67761
|
+
if (root === null && registersNpmPin(platform, res)) {
|
|
67762
|
+
entries.push(applyOpenCode(null, res));
|
|
67763
|
+
continue;
|
|
67764
|
+
}
|
|
67436
67765
|
if (root === null) {
|
|
67437
67766
|
entries.push({
|
|
67438
67767
|
platform,
|
|
@@ -67474,12 +67803,20 @@ function applySetupPreview(preview, options = {}) {
|
|
|
67474
67803
|
detail: "existing credential preserved"
|
|
67475
67804
|
});
|
|
67476
67805
|
}
|
|
67806
|
+
if (preview.projectRefused) {
|
|
67807
|
+
entries.push({
|
|
67808
|
+
platform: "core",
|
|
67809
|
+
file: path33.resolve(res.cwd),
|
|
67810
|
+
status: "Skipped",
|
|
67811
|
+
detail: preview.projectRefused
|
|
67812
|
+
});
|
|
67813
|
+
}
|
|
67477
67814
|
const doctor = [];
|
|
67478
67815
|
for (const platform of preview.platforms) {
|
|
67479
67816
|
if (platform !== "opencode" && platform !== "cursor")
|
|
67480
67817
|
continue;
|
|
67481
67818
|
const root = rootFor.get(platform);
|
|
67482
|
-
if (root ===
|
|
67819
|
+
if (root === undefined || root === null && !registersNpmPin(platform, res))
|
|
67483
67820
|
continue;
|
|
67484
67821
|
if (!entries.some((e) => e.platform === platform && e.status !== "Failed"))
|
|
67485
67822
|
continue;
|
|
@@ -67608,6 +67945,16 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
|
|
|
67608
67945
|
} catch {
|
|
67609
67946
|
return false;
|
|
67610
67947
|
}
|
|
67948
|
+
}, nodeResolvedAdapters = (platform) => {
|
|
67949
|
+
const name = `@brainervirus/workit-${platform}`;
|
|
67950
|
+
const require2 = createRequire2(path33.join(packageRoot(), "package.json"));
|
|
67951
|
+
return (require2.resolve.paths(name) ?? []).map((dir) => path33.join(dir, name));
|
|
67952
|
+
}, realRoot = (root) => {
|
|
67953
|
+
try {
|
|
67954
|
+
return realpathSync7(root);
|
|
67955
|
+
} catch {
|
|
67956
|
+
return root;
|
|
67957
|
+
}
|
|
67611
67958
|
}, isDevOpenCodeAdapterRoot = (root, dev) => {
|
|
67612
67959
|
if (dev)
|
|
67613
67960
|
return true;
|
|
@@ -67622,7 +67969,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
|
|
|
67622
67969
|
return `file://${entry}`;
|
|
67623
67970
|
}
|
|
67624
67971
|
return null;
|
|
67625
|
-
}, opencodePin = (root, res) => resolveOpenCodePin(root, { dev: res.dev }), CURSOR_HOOKS_FILE, localizedCursorHooks = (shipped, pluginDir) => {
|
|
67972
|
+
}, opencodePin = (root, res) => resolveOpenCodePin(root, { dev: res.dev }), registersNpmPin = (platform, res) => platform === "opencode" && !res.dev, CURSOR_HOOKS_FILE, localizedCursorHooks = (shipped, pluginDir) => {
|
|
67626
67973
|
try {
|
|
67627
67974
|
const merged = mergeCursorHooks(JSON.parse(shipped.toString("utf8")), { command: cursorHooksEntry(pluginDir).command }, pluginDir);
|
|
67628
67975
|
return Buffer.from(`${JSON.stringify(merged.config, null, 2)}
|
|
@@ -67632,7 +67979,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
|
|
|
67632
67979
|
}
|
|
67633
67980
|
}, samePluginContent = (src, dest, pluginDir, relative = "") => {
|
|
67634
67981
|
try {
|
|
67635
|
-
for (const entry of
|
|
67982
|
+
for (const entry of readdirSync9(src, { withFileTypes: true })) {
|
|
67636
67983
|
if (entry.name === "node_modules")
|
|
67637
67984
|
continue;
|
|
67638
67985
|
const source = path33.join(src, entry.name);
|
|
@@ -67647,7 +67994,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
|
|
|
67647
67994
|
return false;
|
|
67648
67995
|
}
|
|
67649
67996
|
}
|
|
67650
|
-
for (const entry of
|
|
67997
|
+
for (const entry of readdirSync9(dest, { withFileTypes: true })) {
|
|
67651
67998
|
if (existsSync15(path33.join(src, entry.name)))
|
|
67652
67999
|
continue;
|
|
67653
68000
|
const rel = path33.join(relative, entry.name);
|
|
@@ -67666,7 +68013,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
|
|
|
67666
68013
|
const sourceRules = path33.join(src, "rules");
|
|
67667
68014
|
const installedRules = path33.join(dest, "rules");
|
|
67668
68015
|
try {
|
|
67669
|
-
for (const entry of
|
|
68016
|
+
for (const entry of readdirSync9(installedRules, { withFileTypes: true })) {
|
|
67670
68017
|
if (!entry.isFile() || !entry.name.endsWith(".mdc"))
|
|
67671
68018
|
continue;
|
|
67672
68019
|
if (existsSync15(path33.join(sourceRules, entry.name)))
|
|
@@ -67683,7 +68030,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
|
|
|
67683
68030
|
if (existsSync15(legacyRules)) {
|
|
67684
68031
|
try {
|
|
67685
68032
|
mkdirSync7(path33.join(res.cursorPluginDir, "rules"), { recursive: true });
|
|
67686
|
-
for (const entry of
|
|
68033
|
+
for (const entry of readdirSync9(legacyRules, { withFileTypes: true })) {
|
|
67687
68034
|
if (!entry.isFile() || !entry.name.endsWith(".mdc"))
|
|
67688
68035
|
continue;
|
|
67689
68036
|
const target = path33.join(res.cursorPluginDir, "rules", entry.name);
|
|
@@ -67693,7 +68040,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
|
|
|
67693
68040
|
} catch {}
|
|
67694
68041
|
}
|
|
67695
68042
|
rmSync4(legacy, { recursive: true, force: true });
|
|
67696
|
-
}, failedDetail = (report) => report.checks.filter((c) => c.status === "fail").map((c) => `${c.id}: ${c.detail}`).join("; "), setupCompletionGuidance = () => [
|
|
68043
|
+
}, failedDetail = (report) => report.checks.filter((c) => c.status === "fail").map((c) => c.detail.startsWith(`${c.id}:`) ? c.detail : `${c.id}: ${c.detail}`).join("; "), setupCompletionGuidance = () => [
|
|
67697
68044
|
"Run `workit doctor` to verify your installation.",
|
|
67698
68045
|
"Run /wk-status in OpenCode to review token setup steps."
|
|
67699
68046
|
];
|
|
@@ -70762,7 +71109,43 @@ function Screen({
|
|
|
70762
71109
|
}, undefined, false, undefined, this)
|
|
70763
71110
|
]
|
|
70764
71111
|
}, undefined, true, undefined, this);
|
|
70765
|
-
case "project":
|
|
71112
|
+
case "project": {
|
|
71113
|
+
const target = projectTargetFor(resolveBasePath(draft.values));
|
|
71114
|
+
if (!target.ok) {
|
|
71115
|
+
return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
|
|
71116
|
+
flexDirection: "column",
|
|
71117
|
+
gap: 1,
|
|
71118
|
+
children: [
|
|
71119
|
+
/* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
|
|
71120
|
+
bold: true,
|
|
71121
|
+
children: "Step 6 — Project setup"
|
|
71122
|
+
}, undefined, false, undefined, this),
|
|
71123
|
+
/* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
|
|
71124
|
+
color: "yellow",
|
|
71125
|
+
children: [
|
|
71126
|
+
"Skipped: ",
|
|
71127
|
+
target.refused
|
|
71128
|
+
]
|
|
71129
|
+
}, undefined, true, undefined, this),
|
|
71130
|
+
/* @__PURE__ */ jsx_dev_runtime2.jsxDEV(ConfirmInput, {
|
|
71131
|
+
defaultChoice: "confirm",
|
|
71132
|
+
submitOnEnter: false,
|
|
71133
|
+
onConfirm: () => {
|
|
71134
|
+
dispatch({ type: "set", field: "applyProject", value: true });
|
|
71135
|
+
dispatch({ type: "next" });
|
|
71136
|
+
},
|
|
71137
|
+
onCancel: () => {
|
|
71138
|
+
dispatch({ type: "set", field: "applyProject", value: false });
|
|
71139
|
+
dispatch({ type: "next" });
|
|
71140
|
+
}
|
|
71141
|
+
}, undefined, false, undefined, this),
|
|
71142
|
+
/* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
|
|
71143
|
+
dimColor: true,
|
|
71144
|
+
children: "y to keep this note in the summary · n to skip it · either adds no project files · b Back · Esc Cancel"
|
|
71145
|
+
}, undefined, false, undefined, this)
|
|
71146
|
+
]
|
|
71147
|
+
}, undefined, true, undefined, this);
|
|
71148
|
+
}
|
|
70766
71149
|
return /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Box_default, {
|
|
70767
71150
|
flexDirection: "column",
|
|
70768
71151
|
gap: 1,
|
|
@@ -70775,7 +71158,7 @@ function Screen({
|
|
|
70775
71158
|
dimColor: true,
|
|
70776
71159
|
children: [
|
|
70777
71160
|
"Apply gitignore + hygiene in ",
|
|
70778
|
-
|
|
71161
|
+
target.root,
|
|
70779
71162
|
"? (Existing files are never overwritten.)"
|
|
70780
71163
|
]
|
|
70781
71164
|
}, undefined, true, undefined, this),
|
|
@@ -70797,6 +71180,7 @@ function Screen({
|
|
|
70797
71180
|
}, undefined, false, undefined, this)
|
|
70798
71181
|
]
|
|
70799
71182
|
}, undefined, true, undefined, this);
|
|
71183
|
+
}
|
|
70800
71184
|
case "summary": {
|
|
70801
71185
|
const policy = effectivePolicy(draft.values);
|
|
70802
71186
|
const preview = buildSetupPreview(draft.values, { cwd: resolveBasePath(draft.values) });
|
|
@@ -70938,8 +71322,15 @@ function Screen({
|
|
|
70938
71322
|
}, undefined, true, undefined, this),
|
|
70939
71323
|
/* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
|
|
70940
71324
|
children: [
|
|
70941
|
-
"Project hygiene:
|
|
70942
|
-
|
|
71325
|
+
"Project hygiene:",
|
|
71326
|
+
" ",
|
|
71327
|
+
preview.projectRefused ? /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
|
|
71328
|
+
color: "yellow",
|
|
71329
|
+
children: [
|
|
71330
|
+
"skipped — ",
|
|
71331
|
+
preview.projectRefused
|
|
71332
|
+
]
|
|
71333
|
+
}, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime2.jsxDEV(Text, {
|
|
70943
71334
|
color: "green",
|
|
70944
71335
|
children: draft.values.applyProject ? "yes" : "no"
|
|
70945
71336
|
}, undefined, false, undefined, this)
|
|
@@ -71028,7 +71419,11 @@ function Screen({
|
|
|
71028
71419
|
}, undefined, false, undefined, this);
|
|
71029
71420
|
}
|
|
71030
71421
|
}
|
|
71031
|
-
var import_react66, jsx_dev_runtime2,
|
|
71422
|
+
var import_react66, jsx_dev_runtime2, lastProjectTarget = null, projectTargetFor = (base) => {
|
|
71423
|
+
if (lastProjectTarget?.base !== base)
|
|
71424
|
+
lastProjectTarget = { base, target: projectTarget(base) };
|
|
71425
|
+
return lastProjectTarget.target;
|
|
71426
|
+
}, PLATFORM_LABELS, BRANCH_PRESETS, BRANCH_PRESET_DESCRIPTIONS, SCREEN_PLACEHOLDERS, VCS_PROVIDERS, LOCALE_PICKER_OPTIONS, ISSUE_TRACKERS, WORKSPACE_ADVANCED_FIELDS, PROFILE_FIELDS, TRACK_FIELDS, enumFieldOptions = (field) => {
|
|
71032
71427
|
if (field === "commitPolicy.preset")
|
|
71033
71428
|
return [{ label: "Inherit global", value: "inherit" }, ...commitPresetOptions];
|
|
71034
71429
|
if (field === "branchPolicy.preset")
|
|
@@ -71070,6 +71465,7 @@ var init_steps = __esm(async () => {
|
|
|
71070
71465
|
init_logic();
|
|
71071
71466
|
init_workspaces();
|
|
71072
71467
|
init_branch_policy();
|
|
71468
|
+
init_hygiene();
|
|
71073
71469
|
init_wizard_state();
|
|
71074
71470
|
init_detect_hosts();
|
|
71075
71471
|
await __promiseAll([
|
|
@@ -71461,7 +71857,7 @@ var init_init = __esm(() => {
|
|
|
71461
71857
|
});
|
|
71462
71858
|
|
|
71463
71859
|
// packages/workit-cli/src/upgrade.ts
|
|
71464
|
-
import { spawnSync as
|
|
71860
|
+
import { spawnSync as spawnSync16 } from "node:child_process";
|
|
71465
71861
|
import {
|
|
71466
71862
|
existsSync as existsSync18,
|
|
71467
71863
|
mkdtempSync as mkdtempSync3,
|
|
@@ -71817,7 +72213,7 @@ async function runLaunchCommand(argv, deps = {}) {
|
|
|
71817
72213
|
}
|
|
71818
72214
|
if (deps.run)
|
|
71819
72215
|
return deps.run(host, hostArgs).status ?? 1;
|
|
71820
|
-
return
|
|
72216
|
+
return spawnSync16(launch.command, launch.args, {
|
|
71821
72217
|
stdio: "inherit",
|
|
71822
72218
|
env
|
|
71823
72219
|
}).status ?? 1;
|
|
@@ -71999,7 +72395,7 @@ var init_launch = __esm(() => {
|
|
|
71999
72395
|
});
|
|
72000
72396
|
|
|
72001
72397
|
// packages/workit-core/src/knowledge.ts
|
|
72002
|
-
import { spawnSync as
|
|
72398
|
+
import { spawnSync as spawnSync17 } from "node:child_process";
|
|
72003
72399
|
import fs18 from "node:fs";
|
|
72004
72400
|
import path38 from "node:path";
|
|
72005
72401
|
function lintKnowledge(root) {
|
|
@@ -72120,7 +72516,7 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
|
|
|
72120
72516
|
return "unreadable";
|
|
72121
72517
|
}
|
|
72122
72518
|
}, trackedTopLevel = (root) => {
|
|
72123
|
-
const run =
|
|
72519
|
+
const run = spawnSync17("git", ["ls-files", "-z", "--cached"], {
|
|
72124
72520
|
cwd: root,
|
|
72125
72521
|
encoding: "utf8",
|
|
72126
72522
|
maxBuffer: 256 * 1024 * 1024,
|
|
@@ -72488,7 +72884,8 @@ var R = "[REDACTED]", TOKEN_PATTERNS, WEBHOOK, URL_USERINFO, CURL_USER, KEY, ASS
|
|
|
72488
72884
|
var init_redact = __esm(() => {
|
|
72489
72885
|
TOKEN_PATTERNS = [
|
|
72490
72886
|
/\b(?:gh[pousr]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,})/gu,
|
|
72491
|
-
/\
|
|
72887
|
+
/\b(?:glpat|gloas|gldt|glrtr|glrt|glcbt|glptt|glft|glimt|glagent|glwt|glsoat|glffct)-[A-Za-z0-9_-]{20,}/gu,
|
|
72888
|
+
/\b_gitlab_session=[A-Za-z0-9%_-]{16,}/gu,
|
|
72492
72889
|
/\bnpm_[A-Za-z0-9]{30,}/gu,
|
|
72493
72890
|
/\bxox[abposr]-[A-Za-z0-9-]{10,}/gu,
|
|
72494
72891
|
/\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/gu,
|
|
@@ -72522,7 +72919,7 @@ var init_redact = __esm(() => {
|
|
|
72522
72919
|
});
|
|
72523
72920
|
|
|
72524
72921
|
// packages/workit-core/src/checks.ts
|
|
72525
|
-
import { spawn, spawnSync as
|
|
72922
|
+
import { spawn, spawnSync as spawnSync18 } from "node:child_process";
|
|
72526
72923
|
import { createHash as createHash16 } from "node:crypto";
|
|
72527
72924
|
import fs19 from "node:fs";
|
|
72528
72925
|
import os13 from "node:os";
|
|
@@ -72704,7 +73101,7 @@ function runCheckCommand(argv, options) {
|
|
|
72704
73101
|
if (!child?.pid)
|
|
72705
73102
|
return;
|
|
72706
73103
|
if (windows)
|
|
72707
|
-
|
|
73104
|
+
spawnSync18("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
|
|
72708
73105
|
stdio: "ignore",
|
|
72709
73106
|
windowsHide: true
|
|
72710
73107
|
});
|
|
@@ -72975,9 +73372,15 @@ function show(argv, io) {
|
|
|
72975
73372
|
return emit(io, fail("invalid_input", error instanceof Error ? error.message : String(error)));
|
|
72976
73373
|
}
|
|
72977
73374
|
const tracks = releaseTracksReport(io.cwd);
|
|
72978
|
-
const
|
|
73375
|
+
const tierModelIssues = workspaceTierModelIssues(io.cwd);
|
|
73376
|
+
const shown = {
|
|
73377
|
+
...autonomy,
|
|
73378
|
+
...tracks.tracks.length || tracks.error ? { releaseTracks: tracks } : {},
|
|
73379
|
+
...tierModelIssues.length ? { tierModelIssues } : {}
|
|
73380
|
+
};
|
|
72979
73381
|
return emit(io, ok(shown), (data) => [
|
|
72980
73382
|
...data.note ? [`note: ${data.note}`] : [],
|
|
73383
|
+
...tierModelIssues.map((issue) => `note: ${issue}`),
|
|
72981
73384
|
data.workspace ? `workspace ${data.workspace} (${data.source === "default" ? "D4 defaults" : `from ${data.source}`})` : "no workspace matches this checkout: D4 defaults apply",
|
|
72982
73385
|
...describe2(data.grants, data.configured, data, data.verification),
|
|
72983
73386
|
...describeReleaseTracks(tracks).map((line) => ` ${line}`),
|
|
@@ -73073,6 +73476,7 @@ usage: ${USAGE2}`)), run9 = (argv, io) => runGrant(argv, io);
|
|
|
73073
73476
|
var init_grant = __esm(() => {
|
|
73074
73477
|
init_autonomy();
|
|
73075
73478
|
init_release_tracks();
|
|
73479
|
+
init_workspaces();
|
|
73076
73480
|
init_output();
|
|
73077
73481
|
init_prompt();
|
|
73078
73482
|
AGENT_ENV = [
|
|
@@ -74040,7 +74444,7 @@ var init_check = __esm(() => {
|
|
|
74040
74444
|
var success2 = (data) => ({ ok: true, data }), failure3 = (code, error, unblock) => ({ ok: false, code, error, ...unblock ? { unblock } : {} });
|
|
74041
74445
|
|
|
74042
74446
|
// packages/workit-core/src/git/ops.ts
|
|
74043
|
-
import { spawnSync as
|
|
74447
|
+
import { spawnSync as spawnSync19 } from "node:child_process";
|
|
74044
74448
|
import { existsSync as existsSync19, readFileSync as readFileSync19 } from "node:fs";
|
|
74045
74449
|
import path42 from "node:path";
|
|
74046
74450
|
function operationInProgress(cwd) {
|
|
@@ -74355,6 +74759,14 @@ function gitCommit(cwd, input) {
|
|
|
74355
74759
|
const session = input.actor.session;
|
|
74356
74760
|
if (session && !SESSION_SAFE.test(session))
|
|
74357
74761
|
return failure3("invalid_input", "WORKIT_SESSION_ID must be 1-128 characters of [A-Za-z0-9_.:@/+-]");
|
|
74762
|
+
if (session) {
|
|
74763
|
+
const env = input.env ?? process.env;
|
|
74764
|
+
if (issuedBinding(cwd, session) !== null)
|
|
74765
|
+
return failure3("blocked", `judge_session: ${session} was issued to a verifier or reviewer subagent; it records verdicts and never authors commits`, "commit from the implementer's or the lead's own session (unset WORKIT_SESSION_ID back to the shell's)");
|
|
74766
|
+
const refusal = sessionClaimRefusal(env, session, cwd);
|
|
74767
|
+
if (refusal)
|
|
74768
|
+
return failure3("blocked", refusal.error, "commit from the session this shell really is: never set WORKIT_SESSION_ID by hand for a commit (unset it, or use the id the host put in this shell)");
|
|
74769
|
+
}
|
|
74358
74770
|
const changeRequired = !input.amend && !input.allowEmpty;
|
|
74359
74771
|
const emptyAmend = previous !== null && git5(cwd, ["rev-parse", "--verify", "-q", `${previous}^{tree}`]).stdout.trim() === git5(cwd, ["rev-parse", "--verify", "-q", `${previous}~1^{tree}`]).stdout.trim();
|
|
74360
74772
|
const args = ["commit", "--no-edit"];
|
|
@@ -74853,7 +75265,7 @@ ${run.stdout}`;
|
|
|
74853
75265
|
} : { ok: false, lease: false, error: gitError(run) };
|
|
74854
75266
|
}
|
|
74855
75267
|
var COMMIT_TIMEOUT_MS, PUSH_TIMEOUT_MS = 120000, git5 = (cwd, args, options = {}) => {
|
|
74856
|
-
const result =
|
|
75268
|
+
const result = spawnSync19("git", args, {
|
|
74857
75269
|
cwd,
|
|
74858
75270
|
encoding: "utf8",
|
|
74859
75271
|
env: { ...options.env ?? process.env, GIT_TERMINAL_PROMPT: "0" },
|
|
@@ -74898,6 +75310,7 @@ var init_ops = __esm(() => {
|
|
|
74898
75310
|
init_vcs_config();
|
|
74899
75311
|
init_workspaces();
|
|
74900
75312
|
init_redact();
|
|
75313
|
+
init_host_session();
|
|
74901
75314
|
init_ledger();
|
|
74902
75315
|
init_rev();
|
|
74903
75316
|
COMMIT_TIMEOUT_MS = 10 * 60000;
|
|
@@ -75386,7 +75799,7 @@ var init_report = __esm(() => {
|
|
|
75386
75799
|
});
|
|
75387
75800
|
|
|
75388
75801
|
// packages/workit-core/src/forge/pr-ops.ts
|
|
75389
|
-
import { spawnSync as
|
|
75802
|
+
import { spawnSync as spawnSync20 } from "node:child_process";
|
|
75390
75803
|
function fillFromCommits(cwd, resolved, base) {
|
|
75391
75804
|
const tracking = `refs/remotes/${resolved.baseRemote}/${base}`;
|
|
75392
75805
|
if (!base.startsWith("-"))
|
|
@@ -75394,7 +75807,7 @@ function fillFromCommits(cwd, resolved, base) {
|
|
|
75394
75807
|
const from = resolveRef(cwd, tracking) ?? resolveRef(cwd, `refs/heads/${base}`);
|
|
75395
75808
|
if (!from)
|
|
75396
75809
|
return failure3("not_found", `base ${base} is not available locally`, `git fetch ${resolved.baseRemote} ${base}`);
|
|
75397
|
-
const log =
|
|
75810
|
+
const log = spawnSync20("git", ["log", "--reverse", "--no-merges", "--format=%s%x1f%b%x1e", `${from}..HEAD`], {
|
|
75398
75811
|
cwd,
|
|
75399
75812
|
encoding: "utf8",
|
|
75400
75813
|
timeout: GIT_TIMEOUTS.local,
|
|
@@ -75591,7 +76004,7 @@ function squashCommitList(cwd, resolved, base, head) {
|
|
|
75591
76004
|
const from = resolveRef(cwd, `refs/remotes/${resolved.baseRemote}/${base}`) ?? resolveRef(cwd, `refs/heads/${base}`);
|
|
75592
76005
|
if (!from)
|
|
75593
76006
|
return "";
|
|
75594
|
-
const log =
|
|
76007
|
+
const log = spawnSync20("git", ["log", "--reverse", "--no-merges", "--format=%B%x1e", `${from}..${head}`], {
|
|
75595
76008
|
cwd,
|
|
75596
76009
|
encoding: "utf8",
|
|
75597
76010
|
timeout: GIT_TIMEOUTS.local,
|
|
@@ -75664,7 +76077,7 @@ async function mergePullRequest(cwd, resolved, input, sleep) {
|
|
|
75664
76077
|
if (reason)
|
|
75665
76078
|
return refuse(`protected_branch: --delete-branch would delete ${branch}, which ${reason}; workit never deletes it`, `workit pr merge${input.pr ? ` --pr ${doc.number}` : ""} --method ${input.method} # without --delete-branch`, { reason: "protected_branch" });
|
|
75666
76079
|
}
|
|
75667
|
-
const ledger = readLedger(cwd);
|
|
76080
|
+
const ledger = readLedger(cwd, { integrity: true });
|
|
75668
76081
|
if (!ledger.ok)
|
|
75669
76082
|
return ledger;
|
|
75670
76083
|
const check = checkVerdicts(cwd, branch, ledger.value.rows);
|
|
@@ -75696,18 +76109,54 @@ async function mergePullRequest(cwd, resolved, input, sleep) {
|
|
|
75696
76109
|
unverified = { reason, recorded: String(recorded.value.id) };
|
|
75697
76110
|
} else if (check.head !== head)
|
|
75698
76111
|
return refuse(`head_mismatch: the verdict check reads ${branch} at ${check.head?.slice(0, 12) ?? "(missing)"} but ${label} is at ${head.slice(0, 12)}`, `git fetch && git switch ${branch} && git merge --ff-only @{u} # then re-verify that head`, { reason: "head_mismatch", verdict: summary });
|
|
76112
|
+
else if (!covered && check.accepted.reasons.includes("ledger_tampered"))
|
|
76113
|
+
return refuse(`ledger_tampered: a ledger row was edited, removed or moved, so no verdict for ${label} is accepted until it is repaired`, tamperedUnblock(cwd), { reason: "ledger_tampered", verdict: summary });
|
|
75699
76114
|
else if (!covered)
|
|
75700
76115
|
return refuse(`NEEDS_VERDICT: ${label} has no accepted independent verdict for ${head.slice(0, 12)} (${check.accepted.reasons.join(", ")})`, `an independent session verifies the head and runs: workit ledger verdict verified --how "<what was exercised>" --branch ${branch}${grant.allowUnverified ? `; or, only if the user asks to merge unverified: workit pr merge --unverified --reason "<why>"` : ""}`, { reason: "needs_verdict", verdict: summary });
|
|
75701
76116
|
const squashMessage = squashMessageFor(cwd, resolved, input, doc);
|
|
75702
76117
|
if (squashMessage && !squashMessage.ok)
|
|
75703
76118
|
return squashMessage;
|
|
76119
|
+
const intent = appendObserved(cwd, {
|
|
76120
|
+
type: "pr.merge.intent",
|
|
76121
|
+
actor: input.actor,
|
|
76122
|
+
branch,
|
|
76123
|
+
head,
|
|
76124
|
+
pr: doc.number,
|
|
76125
|
+
base: doc.base,
|
|
76126
|
+
method: input.method,
|
|
76127
|
+
verdictId: verdict.verdictId,
|
|
76128
|
+
...unverified ? { unverified: unverified.recorded } : {},
|
|
76129
|
+
grant: grant.source
|
|
76130
|
+
});
|
|
76131
|
+
if (!intent.ok)
|
|
76132
|
+
return refuse(`intent_unrecorded: the merge intent for ${label} could not be recorded (${intent.error}); nothing was merged`, "fix the ledger (workit ledger check), then merge again", { reason: "intent_unrecorded" });
|
|
76133
|
+
const intentId = String(intent.value.id);
|
|
75704
76134
|
const merged = resolved.forge.merge(doc.number, {
|
|
75705
76135
|
sha: head,
|
|
75706
76136
|
method: input.method,
|
|
75707
76137
|
...squashMessage ? { squashMessage: squashMessage.data } : {}
|
|
75708
76138
|
});
|
|
75709
76139
|
if (!merged.ok)
|
|
75710
|
-
return
|
|
76140
|
+
return {
|
|
76141
|
+
...merged,
|
|
76142
|
+
error: `${merged.error} (merge intent ${intentId} is recorded: if the forge merged anyway, workit pr status reads it)`
|
|
76143
|
+
};
|
|
76144
|
+
const row = appendObserved(cwd, {
|
|
76145
|
+
type: "pr.merged",
|
|
76146
|
+
actor: input.actor,
|
|
76147
|
+
branch,
|
|
76148
|
+
head,
|
|
76149
|
+
pr: doc.number,
|
|
76150
|
+
base: doc.base,
|
|
76151
|
+
mergeSha: merged.data.mergeSha,
|
|
76152
|
+
method: input.method,
|
|
76153
|
+
verdictId: verdict.verdictId,
|
|
76154
|
+
...unverified ? { unverified: unverified.recorded } : {},
|
|
76155
|
+
grant: grant.source,
|
|
76156
|
+
intent: intentId,
|
|
76157
|
+
verified: false
|
|
76158
|
+
});
|
|
76159
|
+
const recorded = row.ok ? { id: String(row.value.id) } : { error: row.error };
|
|
75711
76160
|
let state = "open";
|
|
75712
76161
|
for (const delay of [0, ...VERIFY_DELAYS_MS]) {
|
|
75713
76162
|
if (delay)
|
|
@@ -75722,9 +76171,21 @@ async function mergePullRequest(cwd, resolved, input, sleep) {
|
|
|
75722
76171
|
return {
|
|
75723
76172
|
ok: false,
|
|
75724
76173
|
code: "failed",
|
|
75725
|
-
error: `merge_unverified: the forge accepted the merge of ${label} but still reports it ${state}`,
|
|
75726
|
-
unblock: `workit verify-delivery merged --pr ${doc.number}
|
|
76174
|
+
error: `merge_unverified: the forge accepted the merge of ${label} but still reports it ${state}${"id" in recorded ? `; recorded as pr.merged ${recorded.id} (verified: false)` : `; the pr.merged row could not be written (${recorded.error})`}`,
|
|
76175
|
+
unblock: `workit verify-delivery merged --pr ${doc.number}`,
|
|
76176
|
+
recorded
|
|
75727
76177
|
};
|
|
76178
|
+
const confirmation = "id" in recorded ? appendObserved(cwd, {
|
|
76179
|
+
type: "pr.merge.verified",
|
|
76180
|
+
actor: input.actor,
|
|
76181
|
+
branch,
|
|
76182
|
+
head,
|
|
76183
|
+
pr: doc.number,
|
|
76184
|
+
base: doc.base,
|
|
76185
|
+
merged: recorded.id,
|
|
76186
|
+
mergeSha: merged.data.mergeSha
|
|
76187
|
+
}) : null;
|
|
76188
|
+
const confirmed = confirmation === null ? { error: "no pr.merged row to confirm" } : confirmation.ok ? { id: String(confirmation.value.id) } : { error: confirmation.error };
|
|
75728
76189
|
let deletedBranch = false;
|
|
75729
76190
|
if (input.deleteBranch) {
|
|
75730
76191
|
const deleted = deleteRemoteBranch(cwd, resolved.remote, branch, head);
|
|
@@ -75732,19 +76193,6 @@ async function mergePullRequest(cwd, resolved, input, sleep) {
|
|
|
75732
76193
|
deletedBranch = deleted.ok || gone && gone.ok && gone.sha === null ? true : { error: deleted.error };
|
|
75733
76194
|
}
|
|
75734
76195
|
const mergeBack = mergeBackFor(cwd, doc.base, branch);
|
|
75735
|
-
const row = appendObserved(cwd, {
|
|
75736
|
-
type: "pr.merged",
|
|
75737
|
-
actor: input.actor,
|
|
75738
|
-
branch,
|
|
75739
|
-
head,
|
|
75740
|
-
pr: doc.number,
|
|
75741
|
-
base: doc.base,
|
|
75742
|
-
mergeSha: merged.data.mergeSha,
|
|
75743
|
-
method: input.method,
|
|
75744
|
-
verdictId: verdict.verdictId,
|
|
75745
|
-
...unverified ? { unverified: unverified.recorded } : {},
|
|
75746
|
-
grant: grant.source
|
|
75747
|
-
});
|
|
75748
76196
|
return {
|
|
75749
76197
|
ok: true,
|
|
75750
76198
|
data: {
|
|
@@ -75759,7 +76207,8 @@ async function mergePullRequest(cwd, resolved, input, sleep) {
|
|
|
75759
76207
|
unverified,
|
|
75760
76208
|
grant: { source: grant.source },
|
|
75761
76209
|
deletedBranch,
|
|
75762
|
-
recorded
|
|
76210
|
+
recorded,
|
|
76211
|
+
confirmed,
|
|
75763
76212
|
mergeBack: mergeBack.branches,
|
|
75764
76213
|
mergeBackTrack: mergeBack.track
|
|
75765
76214
|
}
|
|
@@ -75817,8 +76266,8 @@ var init_pr_ops = __esm(() => {
|
|
|
75817
76266
|
});
|
|
75818
76267
|
|
|
75819
76268
|
// packages/workit-core/src/stack.ts
|
|
75820
|
-
import { spawnSync as
|
|
75821
|
-
import { createHash as createHash17, randomBytes as
|
|
76269
|
+
import { spawnSync as spawnSync21 } from "node:child_process";
|
|
76270
|
+
import { createHash as createHash17, randomBytes as randomBytes4 } from "node:crypto";
|
|
75822
76271
|
import fs22 from "node:fs";
|
|
75823
76272
|
import path44 from "node:path";
|
|
75824
76273
|
function worktreeOf(cwd, branch) {
|
|
@@ -75864,7 +76313,7 @@ function gitRestack(cwd, input) {
|
|
|
75864
76313
|
if (!scratch)
|
|
75865
76314
|
return { ok: false, conflict: false, worktree: cwd, error: "no store root" };
|
|
75866
76315
|
fs22.mkdirSync(scratch, { recursive: true });
|
|
75867
|
-
dir = path44.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${
|
|
76316
|
+
dir = path44.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes4(4).toString("hex")}`);
|
|
75868
76317
|
const added = gitRun3(cwd, ["worktree", "add", "-q", dir, input.branch], GIT_TIMEOUTS.worktree);
|
|
75869
76318
|
if (!added.ok)
|
|
75870
76319
|
return { ok: false, conflict: false, worktree: cwd, error: firstLine(added) };
|
|
@@ -76073,13 +76522,13 @@ function takeLock(lock) {
|
|
|
76073
76522
|
pid: process.pid,
|
|
76074
76523
|
processStart: processStartOf(process.pid),
|
|
76075
76524
|
host: localLockHost(),
|
|
76076
|
-
nonce:
|
|
76525
|
+
nonce: randomBytes4(8).toString("hex")
|
|
76077
76526
|
};
|
|
76078
76527
|
fs22.writeFileSync(path44.join(lock, OWNER), JSON.stringify(owner));
|
|
76079
76528
|
return owner;
|
|
76080
76529
|
}
|
|
76081
76530
|
function reapLock(lock, observed) {
|
|
76082
|
-
const tomb = `${lock}.reap-${
|
|
76531
|
+
const tomb = `${lock}.reap-${randomBytes4(6).toString("hex")}`;
|
|
76083
76532
|
try {
|
|
76084
76533
|
fs22.renameSync(lock, tomb);
|
|
76085
76534
|
} catch {
|
|
@@ -76100,7 +76549,7 @@ function releaseLock(lock, owner) {
|
|
|
76100
76549
|
const { raw } = readOwner(lock);
|
|
76101
76550
|
if (raw !== JSON.stringify(owner))
|
|
76102
76551
|
return;
|
|
76103
|
-
const tomb = `${lock}.release-${
|
|
76552
|
+
const tomb = `${lock}.release-${randomBytes4(6).toString("hex")}`;
|
|
76104
76553
|
try {
|
|
76105
76554
|
fs22.renameSync(lock, tomb);
|
|
76106
76555
|
fs22.rmSync(tomb, { recursive: true, force: true });
|
|
@@ -76330,7 +76779,7 @@ function commitKeys(cwd, range) {
|
|
|
76330
76779
|
const patches = new Map;
|
|
76331
76780
|
const diff = gitRun3(cwd, ["log", "--no-merges", "-p", "--format=commit %H", range]);
|
|
76332
76781
|
if (diff.ok && diff.stdout.trim()) {
|
|
76333
|
-
const ids =
|
|
76782
|
+
const ids = spawnSync21("git", ["patch-id", "--stable"], {
|
|
76334
76783
|
cwd,
|
|
76335
76784
|
input: diff.stdout,
|
|
76336
76785
|
encoding: "utf8",
|
|
@@ -76423,7 +76872,7 @@ function stackStatus(ctx, stack) {
|
|
|
76423
76872
|
const mismatch = repoMismatch(ctx, stack);
|
|
76424
76873
|
if (mismatch)
|
|
76425
76874
|
return mismatch;
|
|
76426
|
-
const ledger = readLedger(ctx.cwd);
|
|
76875
|
+
const ledger = readLedger(ctx.cwd, { integrity: true });
|
|
76427
76876
|
if (!ledger.ok)
|
|
76428
76877
|
return stackFail(ledger.code, ledger.error, ledger.unblock);
|
|
76429
76878
|
const remote = remoteFor(ctx.cwd, ctx.resolved, stack.trunk);
|
|
@@ -76847,7 +77296,7 @@ async function landStack(ctx, stack, options) {
|
|
|
76847
77296
|
absorb(synced.data);
|
|
76848
77297
|
}
|
|
76849
77298
|
const ledgerRows = () => {
|
|
76850
|
-
const ledger = readLedger(ctx.cwd);
|
|
77299
|
+
const ledger = readLedger(ctx.cwd, { integrity: true });
|
|
76851
77300
|
return ledger.ok ? ledger.value.rows : [];
|
|
76852
77301
|
};
|
|
76853
77302
|
const simulated = new Set;
|
|
@@ -76954,7 +77403,7 @@ var STACK_VERSION = 1, stackFail = (code, error, unblock, data) => ({
|
|
|
76954
77403
|
...unblock ? { unblock } : {},
|
|
76955
77404
|
...data ? { data } : {}
|
|
76956
77405
|
}), fromForge = (result) => stackFail(result.code, result.error, result.unblock), stackDeps, gitRun3 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
|
|
76957
|
-
const run =
|
|
77406
|
+
const run = spawnSync21("git", args, {
|
|
76958
77407
|
cwd,
|
|
76959
77408
|
encoding: "utf8",
|
|
76960
77409
|
timeout: timeoutMs,
|
|
@@ -77035,7 +77484,7 @@ var init_stack = __esm(() => {
|
|
|
77035
77484
|
});
|
|
77036
77485
|
|
|
77037
77486
|
// packages/workit-core/src/forge/pr-edit.ts
|
|
77038
|
-
import { spawnSync as
|
|
77487
|
+
import { spawnSync as spawnSync22 } from "node:child_process";
|
|
77039
77488
|
function openPr(cwd, resolved, pr) {
|
|
77040
77489
|
const grant = requireGrant(cwd, "pr");
|
|
77041
77490
|
if (!grant.allowed)
|
|
@@ -77200,7 +77649,7 @@ function replyToThread(cwd, resolved, input) {
|
|
|
77200
77649
|
});
|
|
77201
77650
|
}
|
|
77202
77651
|
var label = (resolved, number) => `${resolved.forge.kind === "github" ? "PR #" : "MR !"}${number}`, git6 = (cwd, args) => {
|
|
77203
|
-
const run =
|
|
77652
|
+
const run = spawnSync22("git", args, {
|
|
77204
77653
|
cwd,
|
|
77205
77654
|
encoding: "utf8",
|
|
77206
77655
|
timeout: GIT_TIMEOUTS.local,
|
|
@@ -77220,7 +77669,7 @@ var init_pr_edit = __esm(() => {
|
|
|
77220
77669
|
});
|
|
77221
77670
|
|
|
77222
77671
|
// packages/workit-core/src/forge/exec.ts
|
|
77223
|
-
import { spawnSync as
|
|
77672
|
+
import { spawnSync as spawnSync23 } from "node:child_process";
|
|
77224
77673
|
import fs23 from "node:fs";
|
|
77225
77674
|
import path45 from "node:path";
|
|
77226
77675
|
function findExecutable(bin, env) {
|
|
@@ -77255,7 +77704,7 @@ function systemRunner(kind, apiHost, env = process.env) {
|
|
|
77255
77704
|
if (!exe)
|
|
77256
77705
|
return { status: null, stdout: "", stderr: "", timedOut: false, missing: true };
|
|
77257
77706
|
const token = options.token ? kind === "github" ? { GH_TOKEN: options.token, GH_ENTERPRISE_TOKEN: options.token } : { GITLAB_TOKEN: options.token } : {};
|
|
77258
|
-
const result =
|
|
77707
|
+
const result = spawnSync23(exe, [...args], {
|
|
77259
77708
|
encoding: "utf8",
|
|
77260
77709
|
env: { ...childEnv, ...token },
|
|
77261
77710
|
maxBuffer: MAX_BUFFER2,
|
|
@@ -78255,6 +78704,14 @@ function resolveCredential(workspace, derived, run, scope) {
|
|
|
78255
78704
|
const explicit = typeof workspaceHost === "string" && workspaceHost.trim().toLowerCase().replace(/^https?:\/\//u, "").replace(/\/.*$/u, "") === apiHost;
|
|
78256
78705
|
if (derived.via === "host_name" && !explicit)
|
|
78257
78706
|
return failure3("blocked", `token_host_unverified: push host ${derived.host} only looks like ${kind}; workspace ${workspace?.name ?? "?"} vcs.tokenFile is not sent to it`, `if ${apiHost} is your ${kind} server, set ${kind}.host in ~/.config/workit/vcs.json (or vcs.host for the workspace); otherwise fix the push remote`);
|
|
78707
|
+
if (process.platform !== "win32") {
|
|
78708
|
+
let mode = 0;
|
|
78709
|
+
try {
|
|
78710
|
+
mode = fs24.statSync(tokenFile.trim()).mode;
|
|
78711
|
+
} catch {}
|
|
78712
|
+
if ((mode & 63) !== 0)
|
|
78713
|
+
return failure3("blocked", `workspace ${workspace?.name ?? "?"} vcs.tokenFile is readable by group or other (mode ${(mode & 511).toString(8).padStart(4, "0")})`, `chmod 600 ${tokenFile.trim()}`);
|
|
78714
|
+
}
|
|
78258
78715
|
let token = "";
|
|
78259
78716
|
try {
|
|
78260
78717
|
token = fs24.readFileSync(tokenFile.trim(), "utf8").trim();
|
|
@@ -78678,17 +79135,20 @@ async function status(argv, io) {
|
|
|
78678
79135
|
});
|
|
78679
79136
|
}
|
|
78680
79137
|
function verdictBlock(cwd, branch, doc) {
|
|
78681
|
-
const ledger = readLedger(cwd);
|
|
79138
|
+
const ledger = readLedger(cwd, { integrity: true });
|
|
78682
79139
|
if (!ledger.ok)
|
|
78683
79140
|
return null;
|
|
78684
79141
|
const check = checkVerdicts(cwd, branch, ledger.value.rows);
|
|
78685
79142
|
let landed = null;
|
|
78686
79143
|
if (doc.state === "merged") {
|
|
78687
|
-
const
|
|
79144
|
+
const mine = (type) => ledger.value.rows.findLast((candidate) => candidate.type === type && candidate.observer === "workit_cli" && candidate.integrity !== "unverified" && candidate.pr === doc.number && candidate.base === doc.base);
|
|
79145
|
+
const merged = mine("pr.merged");
|
|
79146
|
+
const row = merged ?? mine("pr.merge.intent");
|
|
78688
79147
|
landed = row ? {
|
|
78689
79148
|
head: typeof row.head === "string" ? row.head : null,
|
|
78690
79149
|
verdictId: typeof row.verdictId === "string" ? row.verdictId : null,
|
|
78691
|
-
unverified: row.unverified !== undefined && row.unverified !== null
|
|
79150
|
+
unverified: row.unverified !== undefined && row.unverified !== null,
|
|
79151
|
+
...merged ? {} : { intentOnly: true }
|
|
78692
79152
|
} : "untracked";
|
|
78693
79153
|
}
|
|
78694
79154
|
return {
|
|
@@ -78859,7 +79319,7 @@ async function merge3(argv, io) {
|
|
|
78859
79319
|
if (!result.ok)
|
|
78860
79320
|
return emit(io, fail(result.code, result.error, {
|
|
78861
79321
|
unblock: result.unblock,
|
|
78862
|
-
data: "refusal" in result && result.refusal ? result.refusal : {}
|
|
79322
|
+
data: "refusal" in result && result.refusal ? result.refusal : ("recorded" in result) && result.recorded ? { recorded: result.recorded } : {}
|
|
78863
79323
|
}));
|
|
78864
79324
|
return emit(io, ok(result.data), (data) => [
|
|
78865
79325
|
`merged ${connected.data.forge.kind === "github" ? "PR #" : "MR !"}${data.number} (${data.method}) at head ${data.head.slice(0, 12)}${data.mergeSha ? ` -> ${data.mergeSha.slice(0, 12)}` : ""}`,
|
|
@@ -78868,7 +79328,8 @@ async function merge3(argv, io) {
|
|
|
78868
79328
|
`merge back: ${data.base} is the ${data.mergeBackTrack} production branch; bring it into ${data.mergeBack.join(", ")}`
|
|
78869
79329
|
] : [],
|
|
78870
79330
|
...data.deletedBranch === true ? [`deleted ${data.branch}`] : typeof data.deletedBranch === "object" ? [`branch not deleted: ${data.deletedBranch.error}`] : [],
|
|
78871
|
-
..."error" in data.recorded ? [`ledger: not recorded (${data.recorded.error})`] : []
|
|
79331
|
+
..."error" in data.recorded ? [`ledger: not recorded (${data.recorded.error})`] : [],
|
|
79332
|
+
..."error" in data.confirmed && !("error" in data.recorded) ? [`ledger: merge not confirmed in the ledger (${data.confirmed.error})`] : []
|
|
78872
79333
|
]);
|
|
78873
79334
|
}
|
|
78874
79335
|
async function ready(argv, io) {
|
|
@@ -79018,7 +79479,7 @@ async function run11(argv, io) {
|
|
|
79018
79479
|
return reply(rest, io);
|
|
79019
79480
|
return usage3(io, sub && !sub.startsWith("-") ? `unknown pr subcommand "${sub}"` : "missing pr subcommand", USAGE4);
|
|
79020
79481
|
}
|
|
79021
|
-
var STATUS_USAGE = "workit pr status [--pr <n> | --branch <b>] [--log-lines 60] [--json]", CREATE_USAGE = "workit pr create [--base <b> | --track <t>] (--title <t> [--body <text> | --body-file <f|->] | --fill) [--label <l>]… [--reviewer <login>]… [--draft] [--json]", MERGE_USAGE = "workit pr merge [--pr <n>] [--method squash|merge|rebase] [--subject <s>] [--body <b> | --body-file <f|->] [--delete-branch] [--unverified --reason <why>] [--json]", READY_USAGE = "workit pr ready [--pr <n>] [--undo] [--json]", EDIT_USAGE = "workit pr edit [--pr <n>] [--title <t>] [--body-file <f|->] [--add-label <l>]… [--remove-label <l>]… [--add-reviewer <login>]… [--base <b>] [--json]", THREADS_USAGE = "workit pr threads [--pr <n>] [--json]", REPLY_USAGE = "workit pr reply [--pr <n>] --thread <id> [--body-file <f|->] [--resolve] [--json]", USAGE4 = "workit pr status|create|merge|ready|edit|threads|reply ... (workit help pr)", prLabel = (kind, number) => `${kind === "github" ? "PR #" : "MR !"}${number}`, mergedVerdictLine = (landed) => landed === "untracked" ? "merged outside workit (no merge record)" : landed.unverified ? `merged unverified at ${(landed.head ?? "?").slice(0, 12)} (recorded bypass)` : `merged with accepted verdict ${landed.verdictId ?? "?"} at ${(landed.head ?? "?").slice(0, 12)}`, METHODS2;
|
|
79482
|
+
var STATUS_USAGE = "workit pr status [--pr <n> | --branch <b>] [--log-lines 60] [--json]", CREATE_USAGE = "workit pr create [--base <b> | --track <t>] (--title <t> [--body <text> | --body-file <f|->] | --fill) [--label <l>]… [--reviewer <login>]… [--draft] [--json]", MERGE_USAGE = "workit pr merge [--pr <n>] [--method squash|merge|rebase] [--subject <s>] [--body <b> | --body-file <f|->] [--delete-branch] [--unverified --reason <why>] [--json]", READY_USAGE = "workit pr ready [--pr <n>] [--undo] [--json]", EDIT_USAGE = "workit pr edit [--pr <n>] [--title <t>] [--body-file <f|->] [--add-label <l>]… [--remove-label <l>]… [--add-reviewer <login>]… [--base <b>] [--json]", THREADS_USAGE = "workit pr threads [--pr <n>] [--json]", REPLY_USAGE = "workit pr reply [--pr <n>] --thread <id> [--body-file <f|->] [--resolve] [--json]", USAGE4 = "workit pr status|create|merge|ready|edit|threads|reply ... (workit help pr)", prLabel = (kind, number) => `${kind === "github" ? "PR #" : "MR !"}${number}`, mergedVerdictLine = (landed) => landed === "untracked" ? "merged outside workit (no merge record)" : landed.unverified ? `merged unverified at ${(landed.head ?? "?").slice(0, 12)} (recorded bypass)` : `merged with accepted verdict ${landed.verdictId ?? "?"} at ${(landed.head ?? "?").slice(0, 12)}${landed.intentOnly ? " (merge intent recorded; the merge call itself failed)" : ""}`, METHODS2;
|
|
79022
79483
|
var init_pr = __esm(() => {
|
|
79023
79484
|
init_pr_body();
|
|
79024
79485
|
init_pr_edit();
|
|
@@ -79543,7 +80004,7 @@ var init_git2 = __esm(() => {
|
|
|
79543
80004
|
});
|
|
79544
80005
|
|
|
79545
80006
|
// packages/workit-core/src/forge/verify.ts
|
|
79546
|
-
import { spawnSync as
|
|
80007
|
+
import { spawnSync as spawnSync24 } from "node:child_process";
|
|
79547
80008
|
function finish(cwd, report, actor) {
|
|
79548
80009
|
const delivered = report.observations.length > 0 && report.observations.every((entry) => entry.ok);
|
|
79549
80010
|
const out = { ...report, delivered };
|
|
@@ -79756,7 +80217,7 @@ var NPM_NAME, SEMVER2, SAFE_ARG, systemNpm = (args) => {
|
|
|
79756
80217
|
};
|
|
79757
80218
|
const found = process.platform === "win32" ? findExecutable("npm.cmd", process.env) : null;
|
|
79758
80219
|
const npmCmd = found && !/["%!^]/u.test(found) ? `"${found}"` : "npm.cmd";
|
|
79759
|
-
const result = process.platform === "win32" ?
|
|
80220
|
+
const result = process.platform === "win32" ? spawnSync24(`${npmCmd} ${args.join(" ")}`, { ...options, shell: true }) : spawnSync24("npm", [...args], options);
|
|
79760
80221
|
return { status: result.status, stdout: result.stdout ?? "", stderr: result.stderr ?? "" };
|
|
79761
80222
|
}, short3 = (sha) => sha ? sha.slice(0, 12) : "(absent)", npmView = (stdout) => {
|
|
79762
80223
|
try {
|
|
@@ -80765,7 +81226,7 @@ var init_stack2 = __esm(() => {
|
|
|
80765
81226
|
});
|
|
80766
81227
|
|
|
80767
81228
|
// packages/workit-core/src/fanout.ts
|
|
80768
|
-
import { spawnSync as
|
|
81229
|
+
import { spawnSync as spawnSync25 } from "node:child_process";
|
|
80769
81230
|
import { createHash as createHash18 } from "node:crypto";
|
|
80770
81231
|
import fs25 from "node:fs";
|
|
80771
81232
|
import path48 from "node:path";
|
|
@@ -81538,7 +81999,7 @@ var FANOUT_VERSION = 1, TIERS, FAN_IN_MODES, fanoutFail = (code, error, unblock,
|
|
|
81538
81999
|
text: edges.map((edge) => `add "${edge.dependsOn}" to "dependsOn" of slice ${edge.slice}`).join("; ")
|
|
81539
82000
|
};
|
|
81540
82001
|
}, gitRun4 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
|
|
81541
|
-
const run =
|
|
82002
|
+
const run = spawnSync25("git", args, {
|
|
81542
82003
|
cwd,
|
|
81543
82004
|
encoding: "utf8",
|
|
81544
82005
|
timeout: timeoutMs,
|
|
@@ -81576,7 +82037,7 @@ var init_fanout = __esm(() => {
|
|
|
81576
82037
|
});
|
|
81577
82038
|
|
|
81578
82039
|
// packages/workit-core/src/fanout-worktree.ts
|
|
81579
|
-
import { randomBytes as
|
|
82040
|
+
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
81580
82041
|
import fs26 from "node:fs";
|
|
81581
82042
|
import path49 from "node:path";
|
|
81582
82043
|
function rowSample(lines) {
|
|
@@ -81660,7 +82121,7 @@ function markAdminDir(worktree) {
|
|
|
81660
82121
|
const admin = adminDir(worktree);
|
|
81661
82122
|
if (!admin)
|
|
81662
82123
|
return null;
|
|
81663
|
-
const token =
|
|
82124
|
+
const token = randomBytes5(16).toString("hex");
|
|
81664
82125
|
try {
|
|
81665
82126
|
fs26.writeFileSync(path49.join(admin.path, TOKEN_FILE), `${token}
|
|
81666
82127
|
`);
|
|
@@ -81989,6 +82450,7 @@ function renderBrief(cwd, plan, input) {
|
|
|
81989
82450
|
what: String(row.what)
|
|
81990
82451
|
}));
|
|
81991
82452
|
const identity = identityOf(input.actor.host);
|
|
82453
|
+
const model = workspaceTierModel(cwd, slice.tier, input.actor.host);
|
|
81992
82454
|
const minted = workerSession(sessionSafe(input.actor.session), slice.id, input.attempt);
|
|
81993
82455
|
const session = identity === "declared" ? minted : null;
|
|
81994
82456
|
const fanIn = plan.fanIn === "integration" ? [
|
|
@@ -82012,6 +82474,7 @@ function renderBrief(cwd, plan, input) {
|
|
|
82012
82474
|
...slice.acceptance.map((line) => ` - ${line}`),
|
|
82013
82475
|
`VERIFY: ${slice.verify.join("; ")}`,
|
|
82014
82476
|
`TIER: ${slice.tier}`,
|
|
82477
|
+
...model ? [` model: ${model.model} (fanout.tierModels in workspace ${model.workspace})`] : [],
|
|
82015
82478
|
`TIMEBOX: ${slice.timebox ?? DEFAULT_TIMEBOX}; past it without a new commit you will be replaced`,
|
|
82016
82479
|
scratchLine,
|
|
82017
82480
|
`FORBIDDEN: ${slice.forbidden.join("; ")}`,
|
|
@@ -82032,6 +82495,7 @@ function renderBrief(cwd, plan, input) {
|
|
|
82032
82495
|
branch,
|
|
82033
82496
|
base: slice.base,
|
|
82034
82497
|
tier: slice.tier,
|
|
82498
|
+
model: model?.model ?? null,
|
|
82035
82499
|
scratch,
|
|
82036
82500
|
session,
|
|
82037
82501
|
standing,
|
|
@@ -82074,6 +82538,7 @@ var DEFAULT_TIMEBOX = "30 minutes", REPORT, identityOf = (host) => {
|
|
|
82074
82538
|
];
|
|
82075
82539
|
}, sessionSafe = (text) => text.replace(/[^A-Za-z0-9_.:@/+-]/gu, "-"), SESSION_MAX = 128;
|
|
82076
82540
|
var init_fanout_brief = __esm(() => {
|
|
82541
|
+
init_workspaces();
|
|
82077
82542
|
init_ledger();
|
|
82078
82543
|
init_fanout();
|
|
82079
82544
|
init_fanout_worktree();
|
|
@@ -82085,7 +82550,7 @@ var init_fanout_brief = __esm(() => {
|
|
|
82085
82550
|
});
|
|
82086
82551
|
|
|
82087
82552
|
// packages/workit-core/src/fanout-landed.ts
|
|
82088
|
-
import { spawnSync as
|
|
82553
|
+
import { spawnSync as spawnSync26 } from "node:child_process";
|
|
82089
82554
|
function forgeBreaker(resolved) {
|
|
82090
82555
|
if (!resolved)
|
|
82091
82556
|
return { forge: null, down: () => null };
|
|
@@ -82176,7 +82641,7 @@ function trunkPatchIds(ctx, fork) {
|
|
|
82176
82641
|
"--"
|
|
82177
82642
|
]);
|
|
82178
82643
|
if (log.ok && log.stdout.trim()) {
|
|
82179
|
-
const run =
|
|
82644
|
+
const run = spawnSync26("git", ["patch-id", "--stable"], {
|
|
82180
82645
|
cwd: ctx.cwd,
|
|
82181
82646
|
input: log.stdout,
|
|
82182
82647
|
encoding: "utf8",
|
|
@@ -82691,6 +83156,15 @@ function fanoutStatus(cwd, plan, options) {
|
|
|
82691
83156
|
if (row.waitsFor.length && !suggested.includes(id))
|
|
82692
83157
|
row.reasons.push(`waits for ${row.waitsFor.join(", ")}`);
|
|
82693
83158
|
}
|
|
83159
|
+
const host = options.host === undefined ? actorFromEnv(process.env).host : options.host;
|
|
83160
|
+
const spawnModels = {};
|
|
83161
|
+
for (const id of spawnable) {
|
|
83162
|
+
const tier = plan.slices.find((slice) => slice.id === id)?.tier;
|
|
83163
|
+
const model = tier ? workspaceTierModel(cwd, tier, host) : null;
|
|
83164
|
+
if (model)
|
|
83165
|
+
spawnModels[id] = model.model;
|
|
83166
|
+
}
|
|
83167
|
+
const spawnList = spawnable.map((id) => spawnModels[id] ? `${id} (model ${spawnModels[id]})` : id).join(", ");
|
|
82694
83168
|
const stuck = order.filter((id) => out.get(id)?.stuck);
|
|
82695
83169
|
const down = breaker.down();
|
|
82696
83170
|
if (down)
|
|
@@ -82700,7 +83174,7 @@ function fanoutStatus(cwd, plan, options) {
|
|
|
82700
83174
|
const row = out.get(id);
|
|
82701
83175
|
return !row.landed && row.reasons.length === 1 && row.reasons[0] === "no PR";
|
|
82702
83176
|
});
|
|
82703
|
-
const next = stuck.length ? `replace ${stuck.join(", ")}: stop the worker, then workit fanout worktree release ${stuck[0]} (records git status first) and respawn it in MODE: resume` : suggested.length ? online ? `land in this order: ${suggested.join(", ")} (workit fanout check first; workit pr merge each)` : `land in this order: ${suggested.join(", ")} once each has a PR (offline: PRs unchecked; open a missing one with workit git push && workit pr create --fill, then workit fanout status online)` : noPr.length ? `open PRs for ${noPr.join(", ")}: workit git push && workit pr create --fill on each branch` : spawnable.length ? `spawn ${
|
|
83177
|
+
const next = stuck.length ? `replace ${stuck.join(", ")}: stop the worker, then workit fanout worktree release ${stuck[0]} (records git status first) and respawn it in MODE: resume` : suggested.length ? online ? `land in this order: ${suggested.join(", ")} (workit fanout check first; workit pr merge each)` : `land in this order: ${suggested.join(", ")} once each has a PR (offline: PRs unchecked; open a missing one with workit git push && workit pr create --fill, then workit fanout status online)` : noPr.length ? `open PRs for ${noPr.join(", ")}: workit git push && workit pr create --fill on each branch` : spawnable.length ? `spawn ${spawnList} (workit fanout worktree create <slice> on hosts without native worktrees)` : order.every((id) => out.get(id)?.landed) ? "every slice has landed" : "wait for workers, then workit fanout status again";
|
|
82704
83178
|
return {
|
|
82705
83179
|
ok: true,
|
|
82706
83180
|
data: {
|
|
@@ -82714,6 +83188,7 @@ function fanoutStatus(cwd, plan, options) {
|
|
|
82714
83188
|
slices: order.map((id) => out.get(id)),
|
|
82715
83189
|
landingOrder: suggested,
|
|
82716
83190
|
spawnable,
|
|
83191
|
+
spawnModels,
|
|
82717
83192
|
stuck,
|
|
82718
83193
|
next,
|
|
82719
83194
|
notes
|
|
@@ -82727,6 +83202,7 @@ var DEFAULT_STUCK_MS, isoOf = (seconds) => {
|
|
|
82727
83202
|
var init_fanout_status = __esm(() => {
|
|
82728
83203
|
init_duration();
|
|
82729
83204
|
init_report();
|
|
83205
|
+
init_workspaces();
|
|
82730
83206
|
init_ledger();
|
|
82731
83207
|
init_fanout_check();
|
|
82732
83208
|
init_fanout_landed();
|
|
@@ -83025,7 +83501,7 @@ var exports_ledger = {};
|
|
|
83025
83501
|
__export(exports_ledger, {
|
|
83026
83502
|
run: () => run18
|
|
83027
83503
|
});
|
|
83028
|
-
import { randomBytes as
|
|
83504
|
+
import { randomBytes as randomBytes6 } from "node:crypto";
|
|
83029
83505
|
import { parseArgs } from "node:util";
|
|
83030
83506
|
function targetBranch(io, rows, branch, pr) {
|
|
83031
83507
|
if (pr !== undefined) {
|
|
@@ -83163,10 +83639,12 @@ async function run18(argv, io) {
|
|
|
83163
83639
|
if (rest.length !== 1)
|
|
83164
83640
|
return usage4(io, `ledger verdict takes one result: ${VERDICT_RESULTS.join("|")}`);
|
|
83165
83641
|
const claimed = values.self === true ? null : claimedSession(acting);
|
|
83166
|
-
const
|
|
83642
|
+
const token = values.token ?? io.env.WORKIT_SESSION_TOKEN ?? null;
|
|
83643
|
+
const refusal = claimed === null ? null : sessionClaimRefusal(io.env, claimed, io.cwd, { token, flag: acting.source === "flag" });
|
|
83167
83644
|
if (refusal)
|
|
83168
83645
|
return emit(io, fail("blocked", refusal.error, { unblock: refusal.unblock }));
|
|
83169
|
-
const
|
|
83646
|
+
const bound = claimed !== null && issuedTokenMatches(io.cwd, claimed, token);
|
|
83647
|
+
const verdict = await recorded(recordVerdict({ ...context, ...bound ? { sessionBound: true } : {} }, {
|
|
83170
83648
|
result: rest[0],
|
|
83171
83649
|
kind: values.kind,
|
|
83172
83650
|
how: values.how,
|
|
@@ -83182,7 +83660,12 @@ async function run18(argv, io) {
|
|
|
83182
83660
|
if (shadowed)
|
|
83183
83661
|
io.stderr(`warning: ${shadowed}
|
|
83184
83662
|
`);
|
|
83185
|
-
|
|
83663
|
+
const withGate = verdict.ok ? { ok: true, value: { ...verdict.value, gate: gateReading(io.cwd, verdict.value) } } : verdict;
|
|
83664
|
+
return fromResult(io, withGate, (row) => [
|
|
83665
|
+
`recorded verdict ${row.id}: ${row.result} [${row.kind}] for ${row.branch} @ ${(row.head ?? "").slice(0, 12)} as ${actor.session ?? "no session"}${row.self ? ` (self${row.selfReason === "no_session" ? ": WORKIT_SESSION_ID unset" : ""}; never accepted)` : ""}`,
|
|
83666
|
+
row.gate ? `gate: ${row.branch} ${row.gate.accepted ? `accepted (verdict ${row.gate.verdictId ?? "?"})` : `not accepted (${row.gate.reasons.join(", ")})`}` : "gate: unknown (ledger unreadable)"
|
|
83667
|
+
].join(`
|
|
83668
|
+
`));
|
|
83186
83669
|
}
|
|
83187
83670
|
function standingFanout(io, values) {
|
|
83188
83671
|
if (values.fanout !== undefined) {
|
|
@@ -83253,7 +83736,7 @@ var USAGE10 = "workit ledger decision|ruling|verdict|standing|list|check ... (wo
|
|
|
83253
83736
|
return new Error("--as takes a lowercase role, e.g. verifier");
|
|
83254
83737
|
const prefix = (actor.session ?? actor.host).slice(0, 96);
|
|
83255
83738
|
return {
|
|
83256
|
-
actor: { ...actor, session: `${prefix}:${values.as}:${
|
|
83739
|
+
actor: { ...actor, session: `${prefix}:${values.as}:${randomBytes6(4).toString("hex")}` },
|
|
83257
83740
|
source: "as",
|
|
83258
83741
|
derivedFrom: actor.session
|
|
83259
83742
|
};
|
|
@@ -83296,7 +83779,17 @@ var USAGE10 = "workit ledger decision|ruling|verdict|standing|list|check ... (wo
|
|
|
83296
83779
|
] : [
|
|
83297
83780
|
`${data.unverified_rows.length} unverified row(s) (not written by the workit CLI, or changed afterwards):`,
|
|
83298
83781
|
...data.unverified_rows.map((row) => ` #${row.seq} ${row.id} ${row.type}${row.branch ? ` [${row.branch}]` : ""}: ${row.reason}`)
|
|
83299
|
-
]
|
|
83782
|
+
], gateReading = (cwd, row) => {
|
|
83783
|
+
const ledger = readLedger(cwd, { integrity: true });
|
|
83784
|
+
if (!ledger.ok || !row.branch)
|
|
83785
|
+
return null;
|
|
83786
|
+
const { accepted } = checkVerdicts(cwd, row.branch, ledger.value.rows);
|
|
83787
|
+
return {
|
|
83788
|
+
accepted: accepted.accepted,
|
|
83789
|
+
reasons: accepted.reasons,
|
|
83790
|
+
verdictId: typeof accepted.verdict?.id === "string" ? accepted.verdict.id : null
|
|
83791
|
+
};
|
|
83792
|
+
};
|
|
83300
83793
|
var init_ledger2 = __esm(() => {
|
|
83301
83794
|
init_ledger();
|
|
83302
83795
|
init_fanout();
|
|
@@ -83320,6 +83813,7 @@ var init_ledger2 = __esm(() => {
|
|
|
83320
83813
|
last: { type: "string" },
|
|
83321
83814
|
supersedes: { type: "string" },
|
|
83322
83815
|
session: { type: "string" },
|
|
83816
|
+
token: { type: "string" },
|
|
83323
83817
|
as: { type: "string" },
|
|
83324
83818
|
fanout: { type: "string" },
|
|
83325
83819
|
json: { type: "boolean" }
|
|
@@ -99175,11 +99669,11 @@ var init_rules = __esm(() => {
|
|
|
99175
99669
|
});
|
|
99176
99670
|
|
|
99177
99671
|
// packages/workit-cli/src/test-audit/audit.ts
|
|
99178
|
-
import { spawnSync as
|
|
99179
|
-
import { existsSync as existsSync20, readdirSync as
|
|
99672
|
+
import { spawnSync as spawnSync27 } from "node:child_process";
|
|
99673
|
+
import { existsSync as existsSync20, readdirSync as readdirSync10, readFileSync as readFileSync23, statSync as statSync8 } from "node:fs";
|
|
99180
99674
|
import path52 from "node:path";
|
|
99181
99675
|
function git7(cwd, args) {
|
|
99182
|
-
const run =
|
|
99676
|
+
const run = spawnSync27("git", ["-c", "core.quotepath=off", ...args], {
|
|
99183
99677
|
cwd,
|
|
99184
99678
|
encoding: "utf8",
|
|
99185
99679
|
maxBuffer: 64 * 1024 * 1024
|
|
@@ -99187,7 +99681,7 @@ function git7(cwd, args) {
|
|
|
99187
99681
|
return { ok: run.status === 0, stdout: run.stdout ?? "" };
|
|
99188
99682
|
}
|
|
99189
99683
|
function walkTests(dir, out) {
|
|
99190
|
-
for (const entry of
|
|
99684
|
+
for (const entry of readdirSync10(dir, { withFileTypes: true })) {
|
|
99191
99685
|
if (entry.name.startsWith(".") && entry.name !== "." && entry.isDirectory())
|
|
99192
99686
|
continue;
|
|
99193
99687
|
const full = path52.join(dir, entry.name);
|
|
@@ -99323,7 +99817,7 @@ import {
|
|
|
99323
99817
|
lstatSync as lstatSync8,
|
|
99324
99818
|
mkdirSync as mkdirSync10,
|
|
99325
99819
|
mkdtempSync as mkdtempSync4,
|
|
99326
|
-
readdirSync as
|
|
99820
|
+
readdirSync as readdirSync11,
|
|
99327
99821
|
readFileSync as readFileSync24,
|
|
99328
99822
|
readlinkSync as readlinkSync3,
|
|
99329
99823
|
realpathSync as realpathSync9,
|
|
@@ -99420,7 +99914,7 @@ function removeStaleCopies(tmp = os16.tmpdir()) {
|
|
|
99420
99914
|
let removed = 0;
|
|
99421
99915
|
let names = [];
|
|
99422
99916
|
try {
|
|
99423
|
-
names =
|
|
99917
|
+
names = readdirSync11(tmp);
|
|
99424
99918
|
} catch {
|
|
99425
99919
|
return 0;
|
|
99426
99920
|
}
|
|
@@ -99449,7 +99943,7 @@ function removeStaleCopies(tmp = os16.tmpdir()) {
|
|
|
99449
99943
|
}
|
|
99450
99944
|
function linkNodeModules(origin, target, repo, copy) {
|
|
99451
99945
|
mkdirSync10(target, { recursive: true });
|
|
99452
|
-
for (const entry of
|
|
99946
|
+
for (const entry of readdirSync11(origin, { withFileTypes: true })) {
|
|
99453
99947
|
const from = path53.join(origin, entry.name);
|
|
99454
99948
|
const to = path53.join(target, entry.name);
|
|
99455
99949
|
if (entry.name.startsWith("@") && entry.isDirectory()) {
|
|
@@ -100586,7 +101080,7 @@ function withSubcommands(entry, options = {}) {
|
|
|
100586
101080
|
};
|
|
100587
101081
|
}
|
|
100588
101082
|
var VERIFY_OPTIONS = "[--expect pushed|pr|merged|released] [--pr <n> | --branch <b>] [--sha <sha>] [--tag <t>] [--package <name[@version]>]";
|
|
100589
|
-
var LEDGER_ACTOR = "[--branch <b> | --pr <n>] [--supersedes <id>] [--session <id> | --as <role>]";
|
|
101083
|
+
var LEDGER_ACTOR = "[--branch <b> | --pr <n>] [--supersedes <id>] [--session <id> [--token <t>] | --as <role>]";
|
|
100590
101084
|
var VERBS = [
|
|
100591
101085
|
{
|
|
100592
101086
|
name: "init",
|
|
@@ -100915,7 +101409,7 @@ var VERBS = [
|
|
|
100915
101409
|
},
|
|
100916
101410
|
{
|
|
100917
101411
|
name: "standing",
|
|
100918
|
-
usage: `workit ledger standing add "<order>" | standing list | standing clear [<id>] [--fanout <name>] [--supersedes <id>] [--session <id> | --as <role>]`,
|
|
101412
|
+
usage: `workit ledger standing add "<order>" | standing list | standing clear [<id>] [--fanout <name>] [--supersedes <id>] [--session <id> [--token <t>] | --as <role>]`,
|
|
100919
101413
|
summary: "The lead's standing orders for a fanout's workers"
|
|
100920
101414
|
},
|
|
100921
101415
|
{
|