@brainervirus/workit-claude-code 8.1.0 → 8.2.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/.claude-plugin/plugin.json +1 -1
- package/dist/workit-hook.js +37 -37
- package/dist/workit.js +429 -164
- package/package.json +3 -3
package/dist/workit.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.2.0",
|
|
69
69
|
private: false,
|
|
70
70
|
description: "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
|
|
71
71
|
keywords: [
|
|
@@ -103,9 +103,9 @@ var init_package = __esm(() => {
|
|
|
103
103
|
},
|
|
104
104
|
dependencies: {
|
|
105
105
|
"@babel/parser": "7.29.9",
|
|
106
|
-
"@brainervirus/workit-core": "^8.
|
|
107
|
-
"@brainervirus/workit-cursor": "^8.
|
|
108
|
-
"@brainervirus/workit-opencode": "^8.
|
|
106
|
+
"@brainervirus/workit-core": "^8.2.0",
|
|
107
|
+
"@brainervirus/workit-cursor": "^8.2.0",
|
|
108
|
+
"@brainervirus/workit-opencode": "^8.2.0",
|
|
109
109
|
"@inkjs/ui": "2.0.0",
|
|
110
110
|
ink: "7.1.1",
|
|
111
111
|
react: "19.2.8"
|
|
@@ -12831,12 +12831,13 @@ function pushForge(cwd, options = {}) {
|
|
|
12831
12831
|
};
|
|
12832
12832
|
const forge = deriveForge(url, options);
|
|
12833
12833
|
if (!forge) {
|
|
12834
|
-
const
|
|
12834
|
+
const parsed = parseRemoteUrl(url);
|
|
12835
|
+
const host = parsed?.host || null;
|
|
12835
12836
|
return {
|
|
12836
12837
|
ok: false,
|
|
12837
12838
|
code: "unavailable",
|
|
12838
|
-
error: host ? `cannot tell whether push host "${host}" is GitHub or GitLab` : `push URL ${redactRemote(url)} of remote "${remote}" is not a GitHub/GitLab URL`,
|
|
12839
|
-
unblock: host ? `
|
|
12839
|
+
error: host ? parsed?.protocol === "ssh" && !host.includes(".") ? `unsupported_forge: could not resolve ssh alias "${host}" to GitHub or GitLab; PR and CI verbs need one (workit git push still pushes with git)` : parsed?.protocol === "ssh" ? `unsupported_forge: ssh host "${host}" is not a GitHub/GitLab host or an alias workit can resolve; PR and CI verbs need one (workit git push still pushes with git)` : `unsupported_forge: cannot tell whether push host "${host}" is GitHub or GitLab; PR and CI verbs need one (workit git push still pushes with git)` : `push URL ${redactRemote(url)} of remote "${remote}" is not a GitHub/GitLab URL`,
|
|
12840
|
+
unblock: host ? `if ${host} is an ssh alias of GitHub/GitLab, add "Host ${host}" with "HostName github.com" (or gitlab.com) to ~/.ssh/config; if it is a self-hosted GitHub/GitLab, set github.host or gitlab.host in ~/.config/workit/vcs.json; other forges (Bitbucket, Gitea…) have no PR/CI verbs` : `git remote set-url --push ${remote} <ssh-or-https-url>`
|
|
12840
12841
|
};
|
|
12841
12842
|
}
|
|
12842
12843
|
return { ok: true, remote, url: redactRemote(url), forge };
|
|
@@ -12847,8 +12848,8 @@ function forgeConflict(derived, configuredProvider) {
|
|
|
12847
12848
|
return null;
|
|
12848
12849
|
return {
|
|
12849
12850
|
code: "blocked",
|
|
12850
|
-
error: `forge_mismatch: push remote ${derived.host} is ${derived.kind} but the workspace vcs.provider is ${configured}`,
|
|
12851
|
-
unblock: `
|
|
12851
|
+
error: `forge_mismatch: push remote ${derived.host} is ${derived.kind} but the workspace vcs.provider is ${configured}, so its account and grants do not apply; PR and CI verbs stop here (workit git push still pushes with git)`,
|
|
12852
|
+
unblock: `add a workspace whose glob matches only this repo, with "vcs": {"provider": "${derived.kind}"} (and its account), to ~/.config/workit/workspaces.json; the most specific glob wins`
|
|
12852
12853
|
};
|
|
12853
12854
|
}
|
|
12854
12855
|
var MAX_BUFFER, GIT_TIMEOUTS, git2 = (cwd, args, options = {}) => {
|
|
@@ -15235,6 +15236,7 @@ var init_commit_flavors = __esm(() => {
|
|
|
15235
15236
|
var init_git = () => {};
|
|
15236
15237
|
|
|
15237
15238
|
// packages/workit-core/src/core/workspaces.ts
|
|
15239
|
+
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
15238
15240
|
import { createHash as createHash8 } from "node:crypto";
|
|
15239
15241
|
import { readFileSync as readFileSync4, realpathSync as realpathSync3 } from "node:fs";
|
|
15240
15242
|
import path14 from "node:path";
|
|
@@ -15362,6 +15364,46 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
15362
15364
|
};
|
|
15363
15365
|
}
|
|
15364
15366
|
return { ok: true };
|
|
15367
|
+
}, WorkspaceConfigError, mainCheckouts, mainCheckoutOf = (cwd) => {
|
|
15368
|
+
const cached = mainCheckouts.get(cwd);
|
|
15369
|
+
if (cached !== undefined)
|
|
15370
|
+
return cached;
|
|
15371
|
+
const env = { ...process.env };
|
|
15372
|
+
for (const name of ["GIT_DIR", "GIT_WORK_TREE", "GIT_COMMON_DIR", "GIT_INDEX_FILE"])
|
|
15373
|
+
delete env[name];
|
|
15374
|
+
const git = (args) => {
|
|
15375
|
+
const run = spawnSync8("git", args, {
|
|
15376
|
+
cwd,
|
|
15377
|
+
env,
|
|
15378
|
+
encoding: "utf8",
|
|
15379
|
+
timeout: 5000,
|
|
15380
|
+
windowsHide: true
|
|
15381
|
+
});
|
|
15382
|
+
return run.status === 0 ? run.stdout : null;
|
|
15383
|
+
};
|
|
15384
|
+
let main = null;
|
|
15385
|
+
const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
|
|
15386
|
+
const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
|
|
15387
|
+
if (gitDir && common && path14.resolve(gitDir) !== path14.resolve(common)) {
|
|
15388
|
+
const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
|
|
15389
|
+
main = listed?.trim() || (path14.basename(common) === ".git" ? path14.dirname(common) : common);
|
|
15390
|
+
}
|
|
15391
|
+
mainCheckouts.set(cwd, main);
|
|
15392
|
+
return main;
|
|
15393
|
+
}, matchingEntries = (cwd, entries) => {
|
|
15394
|
+
const match = (where) => {
|
|
15395
|
+
const targets = [where, realpathOf(where)].map((value) => value.replaceAll("\\", "/"));
|
|
15396
|
+
return entries.filter((entry) => {
|
|
15397
|
+
const glob = entry.glob.replaceAll("\\", "/");
|
|
15398
|
+
const canonical = canonicalGlob(glob);
|
|
15399
|
+
return targets.some((target) => matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target));
|
|
15400
|
+
});
|
|
15401
|
+
};
|
|
15402
|
+
const direct = match(cwd);
|
|
15403
|
+
if (direct.length)
|
|
15404
|
+
return direct;
|
|
15405
|
+
const main = mainCheckoutOf(cwd);
|
|
15406
|
+
return main ? match(main) : direct;
|
|
15365
15407
|
}, realpathOf = (p) => {
|
|
15366
15408
|
const real = realpathSync3.native ?? realpathSync3;
|
|
15367
15409
|
try {
|
|
@@ -15383,41 +15425,27 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
15383
15425
|
if (named.length === 1)
|
|
15384
15426
|
return named[0];
|
|
15385
15427
|
if (named.length > 1)
|
|
15386
|
-
throw new
|
|
15428
|
+
throw new WorkspaceConfigError(`ambiguous workspace ${JSON.stringify(workspaceName)} for ${cwd}`);
|
|
15387
15429
|
if (matches.length > 0) {
|
|
15388
|
-
throw new
|
|
15430
|
+
throw new WorkspaceConfigError(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}; matching choices: ${matches.map((entry) => entry.name).join(", ")}`);
|
|
15389
15431
|
}
|
|
15390
|
-
throw new
|
|
15432
|
+
throw new WorkspaceConfigError(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}`);
|
|
15391
15433
|
}
|
|
15392
15434
|
if (matches.length < 2)
|
|
15393
15435
|
return matches[0];
|
|
15394
15436
|
const highestSpecificity = Math.max(...matches.map((entry) => globSpecificity(entry.glob)));
|
|
15395
15437
|
const mostSpecific = matches.filter((entry) => globSpecificity(entry.glob) === highestSpecificity);
|
|
15396
15438
|
if (mostSpecific.length > 1) {
|
|
15397
|
-
throw new
|
|
15439
|
+
throw new WorkspaceConfigError(`ambiguous workspace for ${cwd}; choose one of: ${mostSpecific.map((entry) => entry.name).join(", ")}`);
|
|
15398
15440
|
}
|
|
15399
15441
|
return mostSpecific[0];
|
|
15400
15442
|
}, resolveWorkspaceFromEntries = (cwd, entries, workspaceName) => {
|
|
15401
|
-
const
|
|
15402
|
-
const matches = [];
|
|
15403
|
-
for (const ws of entries) {
|
|
15404
|
-
const glob = ws.glob.replaceAll("\\", "/");
|
|
15405
|
-
const canonical = canonicalGlob(glob);
|
|
15406
|
-
let matched = false;
|
|
15407
|
-
for (const target of targets) {
|
|
15408
|
-
if (matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target)) {
|
|
15409
|
-
matched = true;
|
|
15410
|
-
break;
|
|
15411
|
-
}
|
|
15412
|
-
}
|
|
15413
|
-
if (matched)
|
|
15414
|
-
matches.push(ws);
|
|
15415
|
-
}
|
|
15443
|
+
const matches = matchingEntries(cwd, entries);
|
|
15416
15444
|
return selectWorkspaceMatch(matches, cwd, workspaceName) ?? null;
|
|
15417
15445
|
}, resolveWorkspaceFrom = (cwd, dir, workspaceName) => {
|
|
15418
15446
|
const result = readWorkspacesResult(dir);
|
|
15419
15447
|
if (result.status === "malformed" || result.status === "invalid")
|
|
15420
|
-
throw new
|
|
15448
|
+
throw new WorkspaceConfigError(result.error);
|
|
15421
15449
|
return resolveWorkspaceFromEntries(cwd, result.entries, workspaceName);
|
|
15422
15450
|
}, runtimeWorkspacePolicySchema = (kind) => {
|
|
15423
15451
|
const key = kind === "branch" ? "branchPolicy" : kind === "commit" ? "commitPolicy" : "vcs";
|
|
@@ -15443,31 +15471,26 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
15443
15471
|
} catch (error) {
|
|
15444
15472
|
if (error.code === "ENOENT")
|
|
15445
15473
|
return null;
|
|
15446
|
-
throw new
|
|
15474
|
+
throw new WorkspaceConfigError(`${file} could not be read: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
15447
15475
|
}
|
|
15448
15476
|
let parsed;
|
|
15449
15477
|
try {
|
|
15450
15478
|
parsed = JSON.parse(raw);
|
|
15451
15479
|
} catch {
|
|
15452
|
-
throw new
|
|
15480
|
+
throw new WorkspaceConfigError(`${file} is not valid JSON`);
|
|
15453
15481
|
}
|
|
15454
15482
|
const validated = runtimeWorkspaceIndexSchema.safeParse(parsed);
|
|
15455
15483
|
if (!validated.success) {
|
|
15456
15484
|
const detail = validated.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspaces"}: ${issue.message}`).join("; ");
|
|
15457
|
-
throw new
|
|
15485
|
+
throw new WorkspaceConfigError(`${file} has invalid workspace matching data: ${detail}`);
|
|
15458
15486
|
}
|
|
15459
15487
|
const entries = validated.data.workspaces;
|
|
15460
15488
|
for (const entry of entries ?? []) {
|
|
15461
15489
|
const glob = validateWorkspaceGlob(entry.glob);
|
|
15462
15490
|
if (!glob.ok)
|
|
15463
|
-
throw new
|
|
15491
|
+
throw new WorkspaceConfigError(`${file} workspace ${entry.name}: ${glob.error}`);
|
|
15464
15492
|
}
|
|
15465
|
-
const
|
|
15466
|
-
const matches = (entries ?? []).filter((entry) => {
|
|
15467
|
-
const glob = entry.glob.replaceAll("\\", "/");
|
|
15468
|
-
const canonical = canonicalGlob(glob);
|
|
15469
|
-
return targets.some((target) => matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target));
|
|
15470
|
-
});
|
|
15493
|
+
const matches = matchingEntries(cwd, entries ?? []);
|
|
15471
15494
|
const workspaceName = process.env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined;
|
|
15472
15495
|
const selected = selectWorkspaceMatch(matches, cwd, workspaceName);
|
|
15473
15496
|
return selected ? { file, selected } : null;
|
|
@@ -15482,7 +15505,7 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
15482
15505
|
const policy = runtimeWorkspacePolicySchema(kind).safeParse(selected);
|
|
15483
15506
|
if (!policy.success) {
|
|
15484
15507
|
const detail = policy.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspace"}: ${issue.message}`).join("; ");
|
|
15485
|
-
throw new
|
|
15508
|
+
throw new WorkspaceConfigError(`${file} has invalid ${kind} policy configuration: ${detail}`);
|
|
15486
15509
|
}
|
|
15487
15510
|
return policy.data;
|
|
15488
15511
|
}, resolveRuntimeWorkspacePolicy = (cwd, kind, requestedProfile) => {
|
|
@@ -15492,10 +15515,10 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
15492
15515
|
const key = kind === "branch" ? "branchPolicy" : "commitPolicy";
|
|
15493
15516
|
const profileName = requestedProfile?.trim() || candidate.defaultProfile;
|
|
15494
15517
|
if (candidate.defaultProfile && !Object.hasOwn(candidate.profiles ?? {}, candidate.defaultProfile))
|
|
15495
|
-
throw new
|
|
15518
|
+
throw new WorkspaceConfigError(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(candidate.defaultProfile)}`);
|
|
15496
15519
|
const profile = profileName ? candidate.profiles?.[profileName] : undefined;
|
|
15497
15520
|
if (profileName && !profile)
|
|
15498
|
-
throw new
|
|
15521
|
+
throw new WorkspaceConfigError(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(profileName)}`);
|
|
15499
15522
|
const profilePolicy = profile?.[key];
|
|
15500
15523
|
const workspacePolicy = candidate[key];
|
|
15501
15524
|
return {
|
|
@@ -15666,6 +15689,10 @@ var init_workspaces = __esm(() => {
|
|
|
15666
15689
|
});
|
|
15667
15690
|
UNSUPPORTED_GLOB = /[?[\]{}]/;
|
|
15668
15691
|
UNSUPPORTED_EXTGLOB = /[@+*]\(|!/;
|
|
15692
|
+
WorkspaceConfigError = class WorkspaceConfigError extends Error {
|
|
15693
|
+
name = "WorkspaceConfigError";
|
|
15694
|
+
};
|
|
15695
|
+
mainCheckouts = new Map;
|
|
15669
15696
|
runtimeWorkspaceIndexSchema = object({
|
|
15670
15697
|
workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
|
|
15671
15698
|
}).passthrough();
|
|
@@ -15673,7 +15700,7 @@ var init_workspaces = __esm(() => {
|
|
|
15673
15700
|
|
|
15674
15701
|
// packages/workit-core/src/core/release-tracks.ts
|
|
15675
15702
|
import { createHash as createHash9 } from "node:crypto";
|
|
15676
|
-
import { spawnSync as
|
|
15703
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
15677
15704
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
15678
15705
|
function readReleaseTracks(raw) {
|
|
15679
15706
|
const out = { tracks: [], issues: [], error: null, protectedBranches: [] };
|
|
@@ -16075,7 +16102,7 @@ var ReleaseTrackError, KNOWN_FIELDS, BRANCH_FIELDS, PLACEHOLDER, HAS_PLACEHOLDER
|
|
|
16075
16102
|
const remote = remotes.find((name) => bare.startsWith(`${name}/`));
|
|
16076
16103
|
return remote ? bare.slice(remote.length + 1) : bare;
|
|
16077
16104
|
}, MAX_RECORDED_HOPS = 10, git4 = (cwd, args) => {
|
|
16078
|
-
const run =
|
|
16105
|
+
const run = spawnSync9("git", args, {
|
|
16079
16106
|
cwd,
|
|
16080
16107
|
encoding: "utf8",
|
|
16081
16108
|
timeout: 20000,
|
|
@@ -16136,7 +16163,7 @@ var init_release_tracks = __esm(() => {
|
|
|
16136
16163
|
// packages/workit-core/src/core/vcs-config.ts
|
|
16137
16164
|
import fs12 from "node:fs";
|
|
16138
16165
|
import path15 from "node:path";
|
|
16139
|
-
import { spawnSync as
|
|
16166
|
+
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
16140
16167
|
function vcsConfig(mode, cwd, options = {}) {
|
|
16141
16168
|
const ws = resolveRuntimeWorkspaceVcs(vcsCwd(cwd));
|
|
16142
16169
|
const wsVcs = ws?.vcs ?? {};
|
|
@@ -16219,7 +16246,7 @@ function vcsConfig(mode, cwd, options = {}) {
|
|
|
16219
16246
|
return out;
|
|
16220
16247
|
}
|
|
16221
16248
|
var workspacesPath = () => path15.join(configDir(), "workspaces.json"), vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd(), remoteProvider = (cwd) => {
|
|
16222
|
-
const r =
|
|
16249
|
+
const r = spawnSync10("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
|
|
16223
16250
|
if (r.status !== 0)
|
|
16224
16251
|
return null;
|
|
16225
16252
|
const url = (r.stdout ?? "").trim();
|
|
@@ -18535,7 +18562,7 @@ var init_pr_body = __esm(() => {
|
|
|
18535
18562
|
|
|
18536
18563
|
// packages/workit-core/src/core/pr-create.ts
|
|
18537
18564
|
import path19 from "node:path";
|
|
18538
|
-
import { spawnSync as
|
|
18565
|
+
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
18539
18566
|
import { pathToFileURL } from "node:url";
|
|
18540
18567
|
var safePushUrl = (remote) => {
|
|
18541
18568
|
const raw = remote.trim();
|
|
@@ -18563,7 +18590,7 @@ var safePushUrl = (remote) => {
|
|
|
18563
18590
|
const target = safePushUrl(remote);
|
|
18564
18591
|
if (!target)
|
|
18565
18592
|
return false;
|
|
18566
|
-
const result =
|
|
18593
|
+
const result = spawnSync11("git", ["config", "--get-regexp", "^url\\..*\\.(insteadof|pushinsteadof)$"], { cwd, encoding: "utf8" });
|
|
18567
18594
|
if (result.status !== 0 && result.status !== 1)
|
|
18568
18595
|
return false;
|
|
18569
18596
|
const prefixes = (result.stdout ?? "").split(/\r?\n/u).map((line) => line.trim().slice(line.trim().indexOf(" ") + 1)).filter(Boolean);
|
|
@@ -62851,7 +62878,7 @@ var init_support_matrix = __esm(() => {
|
|
|
62851
62878
|
});
|
|
62852
62879
|
|
|
62853
62880
|
// packages/workit-cli/src/admin/host-install.ts
|
|
62854
|
-
import { spawnSync as
|
|
62881
|
+
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
62855
62882
|
import {
|
|
62856
62883
|
accessSync,
|
|
62857
62884
|
constants as constants2,
|
|
@@ -63122,7 +63149,7 @@ function runHostCommand(step, context = {}) {
|
|
|
63122
63149
|
stderr: `Cannot safely execute batch shim ${step.command}; resolve its Node.js CLI entrypoint and retry`
|
|
63123
63150
|
};
|
|
63124
63151
|
}
|
|
63125
|
-
const result =
|
|
63152
|
+
const result = spawnSync12(step.command, step.args, {
|
|
63126
63153
|
cwd: step.cwd,
|
|
63127
63154
|
env: (() => {
|
|
63128
63155
|
const home = context.home ?? context.env?.HOME ?? os6.homedir();
|
|
@@ -63327,7 +63354,7 @@ var init_skill_manifests = __esm(() => {
|
|
|
63327
63354
|
});
|
|
63328
63355
|
|
|
63329
63356
|
// packages/workit-cli/src/admin/doctor.ts
|
|
63330
|
-
import { spawnSync as
|
|
63357
|
+
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
63331
63358
|
import {
|
|
63332
63359
|
existsSync as existsSync13,
|
|
63333
63360
|
lstatSync as lstatSync4,
|
|
@@ -63393,9 +63420,8 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
63393
63420
|
return [
|
|
63394
63421
|
...new Set(entries.filter((entry) => typeof entry === "string" && isWorkitPlugin(entry)))
|
|
63395
63422
|
];
|
|
63396
|
-
}, commandOnPath = (name, env) => {
|
|
63423
|
+
}, commandOnPath = (name, env) => findOnPath(process.platform === "win32" ? [name, `${name}.exe`] : [name], env) !== null, pathextNames = (name, env) => process.platform === "win32" ? (env.PATHEXT ?? process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean).map((ext) => `${name}${ext.toLowerCase()}`) : [name], findOnPath = (names, env) => {
|
|
63397
63424
|
const dirs = (env.PATH ?? process.env.PATH ?? "").split(path29.delimiter);
|
|
63398
|
-
const names = process.platform === "win32" ? [name, `${name}.exe`] : [name];
|
|
63399
63425
|
for (const dir of dirs) {
|
|
63400
63426
|
if (!dir)
|
|
63401
63427
|
continue;
|
|
@@ -63405,13 +63431,13 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
63405
63431
|
const st = statSync4(candidate);
|
|
63406
63432
|
if (process.platform !== "win32" && (st.mode & 73) === 0)
|
|
63407
63433
|
continue;
|
|
63408
|
-
return
|
|
63434
|
+
return candidate;
|
|
63409
63435
|
} catch {}
|
|
63410
63436
|
}
|
|
63411
63437
|
}
|
|
63412
|
-
return
|
|
63438
|
+
return null;
|
|
63413
63439
|
}, versionOf = (bin, env, timeout) => {
|
|
63414
|
-
const r =
|
|
63440
|
+
const r = spawnSync13(bin, ["--version"], { encoding: "utf8", env, timeout });
|
|
63415
63441
|
if (r.error)
|
|
63416
63442
|
return null;
|
|
63417
63443
|
return (r.stdout ?? "").trim();
|
|
@@ -63660,7 +63686,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
63660
63686
|
`)) {
|
|
63661
63687
|
return false;
|
|
63662
63688
|
}
|
|
63663
|
-
return
|
|
63689
|
+
return spawnSync13(runtime, ["--check", entry], { encoding: "utf8", env }).status === 0;
|
|
63664
63690
|
} catch {
|
|
63665
63691
|
return false;
|
|
63666
63692
|
}
|
|
@@ -63874,7 +63900,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
63874
63900
|
if (!cmd)
|
|
63875
63901
|
return null;
|
|
63876
63902
|
try {
|
|
63877
|
-
const r =
|
|
63903
|
+
const r = spawnSync13(cmd, ["view", pkgName, "version"], {
|
|
63878
63904
|
encoding: "utf8",
|
|
63879
63905
|
timeout: 20000,
|
|
63880
63906
|
env: res.env
|
|
@@ -64313,7 +64339,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
64313
64339
|
}, defaultIdentityProbes = (env) => ({
|
|
64314
64340
|
origin: (cwd) => {
|
|
64315
64341
|
try {
|
|
64316
|
-
const out =
|
|
64342
|
+
const out = spawnSync13("git", ["remote", "get-url", "origin"], {
|
|
64317
64343
|
cwd,
|
|
64318
64344
|
encoding: "utf8",
|
|
64319
64345
|
env
|
|
@@ -64325,7 +64351,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
64325
64351
|
},
|
|
64326
64352
|
ghLogin: (e) => {
|
|
64327
64353
|
try {
|
|
64328
|
-
const out =
|
|
64354
|
+
const out = spawnSync13("gh", ["api", "user", "--jq", ".login"], { encoding: "utf8", env: e });
|
|
64329
64355
|
const login = (out.stdout ?? "").trim();
|
|
64330
64356
|
return out.status === 0 && login ? login : null;
|
|
64331
64357
|
} catch {
|
|
@@ -64334,7 +64360,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
64334
64360
|
},
|
|
64335
64361
|
glabLogin: (e) => {
|
|
64336
64362
|
try {
|
|
64337
|
-
const out =
|
|
64363
|
+
const out = spawnSync13("glab", ["api", "user", "--jq", ".username"], {
|
|
64338
64364
|
encoding: "utf8",
|
|
64339
64365
|
env: e
|
|
64340
64366
|
});
|
|
@@ -64346,7 +64372,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
64346
64372
|
},
|
|
64347
64373
|
sshLogin: (host) => {
|
|
64348
64374
|
try {
|
|
64349
|
-
const out =
|
|
64375
|
+
const out = spawnSync13("ssh", ["-o", "BatchMode=yes", "-o", "ConnectTimeout=5", "-T", `git@${host}`], {
|
|
64350
64376
|
encoding: "utf8",
|
|
64351
64377
|
env
|
|
64352
64378
|
});
|
|
@@ -64503,6 +64529,67 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
64503
64529
|
fix: repair,
|
|
64504
64530
|
registryProbed
|
|
64505
64531
|
};
|
|
64532
|
+
}, CLI_PACKAGE = "@brainervirus/workit-cli", SEMVER, WORKIT_PROBE_TIMEOUT_MS = 1e4, workitVersions, workitVersionAt = (bin, env) => {
|
|
64533
|
+
let key;
|
|
64534
|
+
try {
|
|
64535
|
+
const st = statSync4(bin);
|
|
64536
|
+
key = `${bin}:${st.ino}:${st.size}:${st.mtimeMs}`;
|
|
64537
|
+
} catch {
|
|
64538
|
+
return { version: null, timedOut: false };
|
|
64539
|
+
}
|
|
64540
|
+
const cached = workitVersions.get(key);
|
|
64541
|
+
if (cached)
|
|
64542
|
+
return cached;
|
|
64543
|
+
const viaShell = process.platform === "win32" && /\.(?:cmd|bat)$/i.test(bin);
|
|
64544
|
+
const r = viaShell ? spawnSync13(`"${bin}" --version`, {
|
|
64545
|
+
encoding: "utf8",
|
|
64546
|
+
env,
|
|
64547
|
+
timeout: WORKIT_PROBE_TIMEOUT_MS,
|
|
64548
|
+
shell: true
|
|
64549
|
+
}) : spawnSync13(bin, ["--version"], { encoding: "utf8", env, timeout: WORKIT_PROBE_TIMEOUT_MS });
|
|
64550
|
+
const timedOut = r.error !== undefined && "code" in r.error && r.error.code === "ETIMEDOUT";
|
|
64551
|
+
const version = r.status === 0 ? (r.stdout ?? "").match(SEMVER)?.[0] ?? null : null;
|
|
64552
|
+
const probe = { version: timedOut ? null : version, timedOut };
|
|
64553
|
+
workitVersions.set(key, probe);
|
|
64554
|
+
return probe;
|
|
64555
|
+
}, checkWorkitOnPath = (res) => {
|
|
64556
|
+
const plugins = [
|
|
64557
|
+
...claudeWorkitInstalls(res.home, res.env, res.cwd).map((i) => ({
|
|
64558
|
+
label: `Claude Code plugin ${i.id}`,
|
|
64559
|
+
version: i.version
|
|
64560
|
+
})),
|
|
64561
|
+
{ label: "Cursor plugin", version: installedPluginVersion(res) },
|
|
64562
|
+
{ label: "OpenCode plugin", version: installedOpenCodeCacheVersion(res) }
|
|
64563
|
+
].filter((p) => p.version !== null);
|
|
64564
|
+
const newest = plugins.reduce((best, p) => best && semverAtLeast(best.version, p.version) ? best : p, null);
|
|
64565
|
+
const target = newest?.version ?? "latest";
|
|
64566
|
+
const fix = `npm i -g ${CLI_PACKAGE}@${target} (or run it without a global install: npx -y ${CLI_PACKAGE}@${target} <command>)`;
|
|
64567
|
+
const bin = findOnPath(pathextNames("workit", res.env), res.env);
|
|
64568
|
+
if (!bin) {
|
|
64569
|
+
return { id: "workit_on_path", status: "warn", detail: "no workit on PATH", fix };
|
|
64570
|
+
}
|
|
64571
|
+
const { version, timedOut } = workitVersionAt(bin, res.env);
|
|
64572
|
+
if (!version) {
|
|
64573
|
+
return {
|
|
64574
|
+
id: "workit_on_path",
|
|
64575
|
+
status: "warn",
|
|
64576
|
+
detail: timedOut ? `${bin} does not run (\`workit --version\` timed out after ${WORKIT_PROBE_TIMEOUT_MS / 1000}s)` : `${bin} does not run (\`workit --version\` failed)`,
|
|
64577
|
+
fix
|
|
64578
|
+
};
|
|
64579
|
+
}
|
|
64580
|
+
if (newest && versionBehind(version, newest.version)) {
|
|
64581
|
+
return {
|
|
64582
|
+
id: "workit_on_path",
|
|
64583
|
+
status: "warn",
|
|
64584
|
+
detail: `workit ${version} at ${bin} is older than the ${newest.label} ${newest.version}`,
|
|
64585
|
+
fix
|
|
64586
|
+
};
|
|
64587
|
+
}
|
|
64588
|
+
return {
|
|
64589
|
+
id: "workit_on_path",
|
|
64590
|
+
status: "pass",
|
|
64591
|
+
detail: `workit ${version} at ${bin}${newest ? ` (newest host plugin ${newest.version})` : ""}`
|
|
64592
|
+
};
|
|
64506
64593
|
}, RUN_CHECKS, INSTALLER_REQUIRED, runDoctor = (options = {}) => {
|
|
64507
64594
|
const res = resolve(options);
|
|
64508
64595
|
const raw = RUN_CHECKS.map((fn) => fn(res));
|
|
@@ -64550,12 +64637,15 @@ var init_doctor = __esm(() => {
|
|
|
64550
64637
|
init_skill_manifests();
|
|
64551
64638
|
OPENCODE_V1_FIX = `OpenCode < ${SUPPORT_MATRIX.opencode.minimum} cannot load Workit 3 (V2 plugin API only): upgrade OpenCode to ${SUPPORT_MATRIX.opencode.minimum}+, or stay on Workit 2.x by pinning "@brainervirus/workit-opencode@2" in the opencode.json "plugin" array`;
|
|
64552
64639
|
canonicalCursorHook = cursorHooksEntry("").command;
|
|
64640
|
+
SEMVER = /\d+\.\d+\.\d+/;
|
|
64641
|
+
workitVersions = new Map;
|
|
64553
64642
|
RUN_CHECKS = [
|
|
64554
64643
|
checkRuntime,
|
|
64555
64644
|
checkVersions,
|
|
64556
64645
|
checkCodexPin,
|
|
64557
64646
|
checkOpencodeVersion,
|
|
64558
64647
|
checkClaudePlugin,
|
|
64648
|
+
checkWorkitOnPath,
|
|
64559
64649
|
checkAssets,
|
|
64560
64650
|
checkLauncher,
|
|
64561
64651
|
checkUtility,
|
|
@@ -69504,7 +69594,7 @@ var init_init = __esm(() => {
|
|
|
69504
69594
|
});
|
|
69505
69595
|
|
|
69506
69596
|
// packages/workit-cli/src/upgrade.ts
|
|
69507
|
-
import { spawnSync as
|
|
69597
|
+
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
69508
69598
|
import {
|
|
69509
69599
|
existsSync as existsSync17,
|
|
69510
69600
|
mkdtempSync as mkdtempSync3,
|
|
@@ -69860,7 +69950,7 @@ async function runLaunchCommand(argv, deps = {}) {
|
|
|
69860
69950
|
}
|
|
69861
69951
|
if (deps.run)
|
|
69862
69952
|
return deps.run(host, hostArgs).status ?? 1;
|
|
69863
|
-
return
|
|
69953
|
+
return spawnSync14(launch.command, launch.args, {
|
|
69864
69954
|
stdio: "inherit",
|
|
69865
69955
|
env
|
|
69866
69956
|
}).status ?? 1;
|
|
@@ -70042,7 +70132,7 @@ var init_launch = __esm(() => {
|
|
|
70042
70132
|
});
|
|
70043
70133
|
|
|
70044
70134
|
// packages/workit-core/src/knowledge.ts
|
|
70045
|
-
import { spawnSync as
|
|
70135
|
+
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
70046
70136
|
import fs17 from "node:fs";
|
|
70047
70137
|
import path36 from "node:path";
|
|
70048
70138
|
function lintKnowledge(root) {
|
|
@@ -70163,7 +70253,7 @@ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODI
|
|
|
70163
70253
|
return "unreadable";
|
|
70164
70254
|
}
|
|
70165
70255
|
}, trackedTopLevel = (root) => {
|
|
70166
|
-
const run =
|
|
70256
|
+
const run = spawnSync15("git", ["ls-files", "-z", "--cached"], {
|
|
70167
70257
|
cwd: root,
|
|
70168
70258
|
encoding: "utf8",
|
|
70169
70259
|
maxBuffer: 256 * 1024 * 1024,
|
|
@@ -70542,7 +70632,7 @@ var init_redact = __esm(() => {
|
|
|
70542
70632
|
});
|
|
70543
70633
|
|
|
70544
70634
|
// packages/workit-core/src/checks.ts
|
|
70545
|
-
import { spawn, spawnSync as
|
|
70635
|
+
import { spawn, spawnSync as spawnSync16 } from "node:child_process";
|
|
70546
70636
|
import { createHash as createHash14 } from "node:crypto";
|
|
70547
70637
|
import fs18 from "node:fs";
|
|
70548
70638
|
import os13 from "node:os";
|
|
@@ -70721,7 +70811,7 @@ function runCheckCommand(argv, options) {
|
|
|
70721
70811
|
if (!child?.pid)
|
|
70722
70812
|
return;
|
|
70723
70813
|
if (windows)
|
|
70724
|
-
|
|
70814
|
+
spawnSync16("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
|
|
70725
70815
|
stdio: "ignore",
|
|
70726
70816
|
windowsHide: true
|
|
70727
70817
|
});
|
|
@@ -71443,7 +71533,7 @@ var init_check = __esm(() => {
|
|
|
71443
71533
|
var success2 = (data) => ({ ok: true, data }), failure3 = (code, error, unblock) => ({ ok: false, code, error, ...unblock ? { unblock } : {} });
|
|
71444
71534
|
|
|
71445
71535
|
// packages/workit-core/src/git/ops.ts
|
|
71446
|
-
import { spawnSync as
|
|
71536
|
+
import { spawnSync as spawnSync17 } from "node:child_process";
|
|
71447
71537
|
import { existsSync as existsSync18, readFileSync as readFileSync18 } from "node:fs";
|
|
71448
71538
|
import path39 from "node:path";
|
|
71449
71539
|
function operationInProgress(cwd) {
|
|
@@ -71931,7 +72021,8 @@ ${gitOutput(run)}`);
|
|
|
71931
72021
|
url: plan.url,
|
|
71932
72022
|
previous: before.sha,
|
|
71933
72023
|
pushed,
|
|
71934
|
-
forced
|
|
72024
|
+
forced,
|
|
72025
|
+
...input.identity ? { identity: input.identity } : {}
|
|
71935
72026
|
});
|
|
71936
72027
|
return success2({
|
|
71937
72028
|
remote: plan.remote,
|
|
@@ -71975,7 +72066,7 @@ ${run.stdout}`;
|
|
|
71975
72066
|
} : { ok: false, lease: false, error: gitError(run) };
|
|
71976
72067
|
}
|
|
71977
72068
|
var COMMIT_TIMEOUT_MS, PUSH_TIMEOUT_MS = 120000, git5 = (cwd, args, options = {}) => {
|
|
71978
|
-
const result =
|
|
72069
|
+
const result = spawnSync17("git", args, {
|
|
71979
72070
|
cwd,
|
|
71980
72071
|
encoding: "utf8",
|
|
71981
72072
|
env: { ...options.env ?? process.env, GIT_TERMINAL_PROMPT: "0" },
|
|
@@ -72355,16 +72446,16 @@ function waitVerdict(doc, options) {
|
|
|
72355
72446
|
const checks = doc.checks.state;
|
|
72356
72447
|
if (checks === "failing")
|
|
72357
72448
|
return { state: "failed", reason: "checks_failing" };
|
|
72358
|
-
if (checks
|
|
72359
|
-
return { state: "waiting", reason: "checks_pending" };
|
|
72360
|
-
if (checks === "passing")
|
|
72361
|
-
return { state: "ready", reason: "checks_passing" };
|
|
72362
|
-
if (doc.conflicts)
|
|
72449
|
+
if (doc.conflicts && checks !== "passing")
|
|
72363
72450
|
return {
|
|
72364
72451
|
state: "blocked",
|
|
72365
72452
|
reason: "conflicts",
|
|
72366
72453
|
unblock: `rebase onto ${doc.base} and push; CI does not run on a conflicting PR`
|
|
72367
72454
|
};
|
|
72455
|
+
if (checks === "pending")
|
|
72456
|
+
return { state: "waiting", reason: "checks_pending" };
|
|
72457
|
+
if (checks === "passing")
|
|
72458
|
+
return { state: "ready", reason: "checks_passing" };
|
|
72368
72459
|
const ciRequired = doc.checks.missingRequired === null || doc.mergeState === "ci_must_pass" || doc.mergeState === "ci_still_running";
|
|
72369
72460
|
if (ciRequired)
|
|
72370
72461
|
return { state: "waiting", reason: "no_checks_yet" };
|
|
@@ -72472,7 +72563,7 @@ var init_report = __esm(() => {
|
|
|
72472
72563
|
});
|
|
72473
72564
|
|
|
72474
72565
|
// packages/workit-core/src/forge/pr-ops.ts
|
|
72475
|
-
import { spawnSync as
|
|
72566
|
+
import { spawnSync as spawnSync18 } from "node:child_process";
|
|
72476
72567
|
function fillFromCommits(cwd, resolved, base) {
|
|
72477
72568
|
const tracking = `refs/remotes/${resolved.baseRemote}/${base}`;
|
|
72478
72569
|
if (!base.startsWith("-"))
|
|
@@ -72480,7 +72571,7 @@ function fillFromCommits(cwd, resolved, base) {
|
|
|
72480
72571
|
const from = resolveRef(cwd, tracking) ?? resolveRef(cwd, `refs/heads/${base}`);
|
|
72481
72572
|
if (!from)
|
|
72482
72573
|
return failure3("not_found", `base ${base} is not available locally`, `git fetch ${resolved.baseRemote} ${base}`);
|
|
72483
|
-
const log =
|
|
72574
|
+
const log = spawnSync18("git", ["log", "--reverse", "--no-merges", "--format=%s%x1f%b%x1e", `${from}..HEAD`], {
|
|
72484
72575
|
cwd,
|
|
72485
72576
|
encoding: "utf8",
|
|
72486
72577
|
timeout: GIT_TIMEOUTS.local,
|
|
@@ -72785,7 +72876,7 @@ var init_pr_ops = __esm(() => {
|
|
|
72785
72876
|
});
|
|
72786
72877
|
|
|
72787
72878
|
// packages/workit-core/src/forge/exec.ts
|
|
72788
|
-
import { spawnSync as
|
|
72879
|
+
import { spawnSync as spawnSync19 } from "node:child_process";
|
|
72789
72880
|
import fs20 from "node:fs";
|
|
72790
72881
|
import path41 from "node:path";
|
|
72791
72882
|
function findExecutable(bin, env) {
|
|
@@ -72820,7 +72911,7 @@ function systemRunner(kind, apiHost, env = process.env) {
|
|
|
72820
72911
|
if (!exe)
|
|
72821
72912
|
return { status: null, stdout: "", stderr: "", timedOut: false, missing: true };
|
|
72822
72913
|
const token = options.token ? kind === "github" ? { GH_TOKEN: options.token, GH_ENTERPRISE_TOKEN: options.token } : { GITLAB_TOKEN: options.token } : {};
|
|
72823
|
-
const result =
|
|
72914
|
+
const result = spawnSync19(exe, [...args], {
|
|
72824
72915
|
encoding: "utf8",
|
|
72825
72916
|
env: { ...childEnv, ...token },
|
|
72826
72917
|
maxBuffer: MAX_BUFFER2,
|
|
@@ -72839,17 +72930,45 @@ function systemRunner(kind, apiHost, env = process.env) {
|
|
|
72839
72930
|
};
|
|
72840
72931
|
};
|
|
72841
72932
|
}
|
|
72933
|
+
function forgeReason(run) {
|
|
72934
|
+
const parts = [];
|
|
72935
|
+
try {
|
|
72936
|
+
const body = JSON.parse(run.stdout);
|
|
72937
|
+
const add = (value) => {
|
|
72938
|
+
if (typeof value === "string" && value.trim())
|
|
72939
|
+
parts.push(value.trim());
|
|
72940
|
+
else if (Array.isArray(value))
|
|
72941
|
+
value.forEach(add);
|
|
72942
|
+
else if (value && typeof value === "object") {
|
|
72943
|
+
const record = value;
|
|
72944
|
+
if (typeof record.message === "string")
|
|
72945
|
+
add(record.message);
|
|
72946
|
+
else if (!("code" in record))
|
|
72947
|
+
Object.values(record).forEach(add);
|
|
72948
|
+
}
|
|
72949
|
+
};
|
|
72950
|
+
add(body.message);
|
|
72951
|
+
add(body.error);
|
|
72952
|
+
add(body.errors);
|
|
72953
|
+
} catch {}
|
|
72954
|
+
const stderr = (run.stderr.split(/\r?\n/u).find((value) => value.trim()) ?? "").trim();
|
|
72955
|
+
const unique = [...new Set(parts)];
|
|
72956
|
+
const text = unique.length ? `${unique.join("; ")}${stderr ? ` (${stderr.replace(/^gh: |^glab: /u, "")})` : ""}` : stderr || (run.stdout.split(/\r?\n/u).find((value) => value.trim()) ?? "");
|
|
72957
|
+
return redactText(text).slice(0, 300);
|
|
72958
|
+
}
|
|
72842
72959
|
function cliFailure(bin, apiHost, run, what, timeoutMs) {
|
|
72843
72960
|
if (run.missing)
|
|
72844
72961
|
return failure3("unavailable", `${bin} is not installed`, installHint(bin));
|
|
72845
72962
|
if (run.timedOut)
|
|
72846
|
-
return failure3("unavailable", `${bin} api ${what} timed out after ${timeoutMs} ms`);
|
|
72963
|
+
return failure3("unavailable", run.timeoutMs === 0 ? `${bin} api ${what} not attempted: the command's time budget ran out` : `${bin} api ${what} timed out after ${run.timeoutMs ?? timeoutMs} ms`, `check the network to ${apiHost}, then retry`);
|
|
72847
72964
|
const stderr = redactText(run.stderr).trim();
|
|
72848
72965
|
if (/auth login|not logged in|no token|authentication required|bad credentials|\b401\b|unauthorized/iu.test(stderr))
|
|
72849
72966
|
return failure3("unavailable", `${bin} is not authenticated for ${apiHost}`, loginHint(bin, apiHost));
|
|
72850
72967
|
if (/\b404\b|not found/iu.test(stderr))
|
|
72851
72968
|
return failure3("not_found", `${what} was not found`);
|
|
72852
72969
|
const first = stderr.split(/\r?\n/u).find((value) => value.trim()) ?? `exit ${run.status}`;
|
|
72970
|
+
if (isNetworkError(stderr))
|
|
72971
|
+
return failure3("unavailable", `${bin} api ${what} failed (network): ${first.slice(0, 200)}`, `check the network to ${apiHost}, then retry`);
|
|
72853
72972
|
return failure3("failed", `${bin} api ${what} failed: ${first.slice(0, 200)}`);
|
|
72854
72973
|
}
|
|
72855
72974
|
function apiJson(runner, bin, apiHost, args, what, timeoutMs = FORGE_TIMEOUTS.api, options = {}) {
|
|
@@ -72880,12 +72999,15 @@ function apiWrite(runner, bin, apiHost, args, what, timeoutMs = FORGE_TIMEOUTS.a
|
|
|
72880
72999
|
if (run.status !== 0) {
|
|
72881
73000
|
const text = redactText(`${run.stderr}
|
|
72882
73001
|
${run.stdout}`).trim();
|
|
72883
|
-
const reason = (
|
|
73002
|
+
const reason = forgeReason(run);
|
|
72884
73003
|
if (!run.missing && !run.timedOut && /\b409\b|head branch was modified|sha does not match|sha.*mismatch/iu.test(text))
|
|
72885
73004
|
return failure3("blocked", `head_moved: ${what} refused because the head changed (${reason})`, "workit pr status # re-check the new head, then verify it again");
|
|
72886
73005
|
if (!run.missing && !run.timedOut && /\b(405|406|422)\b/u.test(text))
|
|
72887
73006
|
return failure3("blocked", `${what} refused by the forge: ${reason}`);
|
|
72888
|
-
|
|
73007
|
+
const failed = cliFailure(bin, apiHost, run, what, timeoutMs);
|
|
73008
|
+
if (!failed.ok && failed.code === "unavailable" && !run.missing && !/ is not authenticated for /u.test(failed.error))
|
|
73009
|
+
return failure3("unavailable", failed.error, "workit pr status # the write may have gone through; check it first, then retry");
|
|
73010
|
+
return failed;
|
|
72889
73011
|
}
|
|
72890
73012
|
if (!run.stdout.trim())
|
|
72891
73013
|
return { ok: true, data: {} };
|
|
@@ -72895,11 +73017,12 @@ ${run.stdout}`).trim();
|
|
|
72895
73017
|
return failure3("failed", `${bin} api ${what} returned invalid JSON`);
|
|
72896
73018
|
}
|
|
72897
73019
|
}
|
|
72898
|
-
var FORGE_TIMEOUTS, MAX_BUFFER2, installHint = (bin) => bin === "gh" ? "install the GitHub CLI (https://cli.github.com) and run: gh auth login" : "install glab (https://gitlab.com/gitlab-org/cli) and run: glab auth login", loginHint = (bin, apiHost) => `${bin} auth login --hostname ${apiHost}
|
|
73020
|
+
var FORGE_TIMEOUTS, MAX_BUFFER2, installHint = (bin) => bin === "gh" ? "install the GitHub CLI (https://cli.github.com) and run: gh auth login" : "install glab (https://gitlab.com/gitlab-org/cli) and run: glab auth login", loginHint = (bin, apiHost) => `${bin} auth login --hostname ${apiHost}`, NETWORK, isNetworkError = (text) => NETWORK.test(text);
|
|
72899
73021
|
var init_exec = __esm(() => {
|
|
72900
73022
|
init_redact();
|
|
72901
73023
|
FORGE_TIMEOUTS = { api: 20000, log: 30000 };
|
|
72902
73024
|
MAX_BUFFER2 = 64 * 1024 * 1024;
|
|
73025
|
+
NETWORK = /error connecting|could not resolve|no such host|connection (refused|reset|timed out)|network is unreachable|i\/o timeout|tls handshake|unexpected eof|\bHTTP 5\d\d\b|bad gateway|service unavailable|gateway time-?out/iu;
|
|
72903
73026
|
});
|
|
72904
73027
|
|
|
72905
73028
|
// packages/workit-core/src/forge/github.ts
|
|
@@ -73491,6 +73614,82 @@ var init_gitlab = __esm(() => {
|
|
|
73491
73614
|
|
|
73492
73615
|
// packages/workit-core/src/forge/resolve.ts
|
|
73493
73616
|
import fs21 from "node:fs";
|
|
73617
|
+
function memo4(scope, key, compute, now = Date.now) {
|
|
73618
|
+
let entries = memoScopes.get(scope.owner);
|
|
73619
|
+
if (!entries)
|
|
73620
|
+
memoScopes.set(scope.owner, entries = new Map);
|
|
73621
|
+
const full = `${scope.key} ${key}`;
|
|
73622
|
+
const hit = entries.get(full);
|
|
73623
|
+
if (hit && now() - hit.at < MEMO_TTL_MS)
|
|
73624
|
+
return hit.value;
|
|
73625
|
+
const { keep, value } = compute();
|
|
73626
|
+
if (keep)
|
|
73627
|
+
entries.set(full, { at: now(), value });
|
|
73628
|
+
return value;
|
|
73629
|
+
}
|
|
73630
|
+
function clampedRunner(base, limits, now, token) {
|
|
73631
|
+
return (bin, args, callOptions) => {
|
|
73632
|
+
let timeoutMs = callOptions.timeoutMs;
|
|
73633
|
+
if (limits.deadline !== null) {
|
|
73634
|
+
const remaining = limits.deadline - now();
|
|
73635
|
+
if (remaining <= 0)
|
|
73636
|
+
return {
|
|
73637
|
+
status: null,
|
|
73638
|
+
stdout: "",
|
|
73639
|
+
stderr: "",
|
|
73640
|
+
timedOut: true,
|
|
73641
|
+
missing: false,
|
|
73642
|
+
timeoutMs: 0
|
|
73643
|
+
};
|
|
73644
|
+
timeoutMs = Math.min(timeoutMs, remaining);
|
|
73645
|
+
}
|
|
73646
|
+
const run = base(bin, args, { ...callOptions, timeoutMs, ...token ? { token } : {} });
|
|
73647
|
+
return run.timedOut && run.timeoutMs === undefined ? { ...run, timeoutMs } : run;
|
|
73648
|
+
};
|
|
73649
|
+
}
|
|
73650
|
+
function resolveCredential(workspace, derived, run, scope) {
|
|
73651
|
+
const { kind, apiHost } = derived;
|
|
73652
|
+
const expectedAccount = workspace?.vcs?.account ?? null;
|
|
73653
|
+
const tokenFile = workspace?.vcs?.tokenFile;
|
|
73654
|
+
if (typeof tokenFile === "string" && tokenFile.trim()) {
|
|
73655
|
+
const workspaceHost = workspace?.vcs?.host;
|
|
73656
|
+
const explicit = typeof workspaceHost === "string" && workspaceHost.trim().toLowerCase().replace(/^https?:\/\//u, "").replace(/\/.*$/u, "") === apiHost;
|
|
73657
|
+
if (derived.via === "host_name" && !explicit)
|
|
73658
|
+
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`);
|
|
73659
|
+
let token = "";
|
|
73660
|
+
try {
|
|
73661
|
+
token = fs21.readFileSync(tokenFile.trim(), "utf8").trim();
|
|
73662
|
+
} catch {
|
|
73663
|
+
return failure3("blocked", `workspace ${workspace?.name ?? "?"} vcs.tokenFile cannot be read`, "fix vcs.tokenFile in ~/.config/workit/workspaces.json");
|
|
73664
|
+
}
|
|
73665
|
+
if (!token)
|
|
73666
|
+
return failure3("blocked", `workspace ${workspace?.name ?? "?"} vcs.tokenFile is empty`, "fix vcs.tokenFile in ~/.config/workit/workspaces.json");
|
|
73667
|
+
return success2({ token, credential: "workspace_token_file" });
|
|
73668
|
+
}
|
|
73669
|
+
if (!expectedAccount || kind !== "github")
|
|
73670
|
+
return success2({ token: undefined, credential: "cli_login" });
|
|
73671
|
+
return memo4(scope, `token ${apiHost} ${expectedAccount}`, () => {
|
|
73672
|
+
const issued = run("gh", ["auth", "token", "--hostname", apiHost, "--user", expectedAccount], {
|
|
73673
|
+
timeoutMs: FORGE_TIMEOUTS.api
|
|
73674
|
+
});
|
|
73675
|
+
const value = authTokenResult(issued, apiHost, expectedAccount, workspace?.name ?? "?");
|
|
73676
|
+
return { keep: value.ok, value };
|
|
73677
|
+
});
|
|
73678
|
+
}
|
|
73679
|
+
function authTokenResult(issued, apiHost, account, workspace) {
|
|
73680
|
+
if (issued.missing)
|
|
73681
|
+
return failure3("unavailable", "gh is not installed", "install the GitHub CLI (https://cli.github.com) and run: gh auth login");
|
|
73682
|
+
if (issued.timedOut)
|
|
73683
|
+
return failure3("unavailable", `gh auth token ${issued.timeoutMs === 0 ? "not attempted: the time budget ran out" : `timed out after ${issued.timeoutMs ?? FORGE_TIMEOUTS.api} ms`} (looking up the ${account} login on ${apiHost})`, "gh auth status # check gh and the network, then retry");
|
|
73684
|
+
const token = issued.status === 0 ? issued.stdout.trim() : "";
|
|
73685
|
+
if (token)
|
|
73686
|
+
return success2({ token, credential: "gh_account_token" });
|
|
73687
|
+
const stderr = redactText(issued.stderr).trim();
|
|
73688
|
+
const first = stderr.split(/\r?\n/u).find((line) => line.trim()) ?? "";
|
|
73689
|
+
if (issued.status !== 0 && first && !/no oauth token|not logged in|no account/iu.test(stderr))
|
|
73690
|
+
return failure3("unavailable", `gh auth token failed${isNetworkError(stderr) ? " (network)" : ""}: ${first.slice(0, 200)}`, "gh auth status # check gh and the network, then retry");
|
|
73691
|
+
return failure3("blocked", `identity_unavailable: gh has no login for ${account} on ${apiHost} (workspace ${workspace})`, `gh auth login --hostname ${apiHost} # sign in as ${account}; workit passes that account's token per call and never switches the active account`);
|
|
73692
|
+
}
|
|
73494
73693
|
function resolveForge(cwd, options = {}) {
|
|
73495
73694
|
let workspace;
|
|
73496
73695
|
try {
|
|
@@ -73508,48 +73707,16 @@ function resolveForge(cwd, options = {}) {
|
|
|
73508
73707
|
const derived = pushed.forge;
|
|
73509
73708
|
const { kind, apiHost } = derived;
|
|
73510
73709
|
const base = options.runner ?? systemRunner(kind, apiHost, options.env ?? process.env);
|
|
73710
|
+
const scope = memoScope(options.runner, options.env ?? process.env);
|
|
73511
73711
|
const now = options.now ?? Date.now;
|
|
73512
73712
|
const expectedAccount = workspace?.vcs?.account ?? null;
|
|
73513
|
-
|
|
73514
|
-
|
|
73515
|
-
|
|
73516
|
-
|
|
73517
|
-
|
|
73518
|
-
const explicit = typeof workspaceHost === "string" && workspaceHost.trim().toLowerCase().replace(/^https?:\/\//u, "").replace(/\/.*$/u, "") === apiHost;
|
|
73519
|
-
if (derived.via === "host_name" && !explicit)
|
|
73520
|
-
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`);
|
|
73521
|
-
try {
|
|
73522
|
-
token = fs21.readFileSync(tokenFile.trim(), "utf8").trim();
|
|
73523
|
-
} catch {
|
|
73524
|
-
return failure3("blocked", `workspace ${workspace?.name ?? "?"} vcs.tokenFile cannot be read`, "fix vcs.tokenFile in ~/.config/workit/workspaces.json");
|
|
73525
|
-
}
|
|
73526
|
-
if (!token)
|
|
73527
|
-
return failure3("blocked", `workspace ${workspace?.name ?? "?"} vcs.tokenFile is empty`, "fix vcs.tokenFile in ~/.config/workit/workspaces.json");
|
|
73528
|
-
credential = "workspace_token_file";
|
|
73529
|
-
} else if (expectedAccount && kind === "github") {
|
|
73530
|
-
const issued = base("gh", ["auth", "token", "--hostname", apiHost, "--user", expectedAccount], {
|
|
73531
|
-
timeoutMs: FORGE_TIMEOUTS.api
|
|
73532
|
-
});
|
|
73533
|
-
if (issued.missing)
|
|
73534
|
-
return failure3("unavailable", "gh is not installed", "install the GitHub CLI (https://cli.github.com) and run: gh auth login");
|
|
73535
|
-
token = issued.status === 0 ? issued.stdout.trim() : "";
|
|
73536
|
-
if (!token)
|
|
73537
|
-
return failure3("blocked", `identity_unavailable: gh has no login for ${expectedAccount} on ${apiHost} (workspace ${workspace?.name ?? "?"})`, `gh auth login --hostname ${apiHost} # sign in as ${expectedAccount}; workit passes that account's token per call and never switches the active account`);
|
|
73538
|
-
credential = "gh_account_token";
|
|
73539
|
-
}
|
|
73540
|
-
const limits = { deadline: null };
|
|
73541
|
-
const runner = (bin, args, callOptions) => {
|
|
73542
|
-
let timeoutMs = callOptions.timeoutMs;
|
|
73543
|
-
if (limits.deadline !== null) {
|
|
73544
|
-
const remaining = limits.deadline - now();
|
|
73545
|
-
if (remaining <= 0)
|
|
73546
|
-
return { status: null, stdout: "", stderr: "", timedOut: true, missing: false };
|
|
73547
|
-
timeoutMs = Math.min(timeoutMs, remaining);
|
|
73548
|
-
}
|
|
73549
|
-
return base(bin, args, { ...callOptions, timeoutMs, ...token ? { token } : {} });
|
|
73550
|
-
};
|
|
73713
|
+
const limits = { deadline: options.deadline ?? null };
|
|
73714
|
+
const credential = resolveCredential(workspace, derived, clampedRunner(base, limits, now), scope);
|
|
73715
|
+
if (!credential.ok)
|
|
73716
|
+
return credential;
|
|
73717
|
+
const runner = clampedRunner(base, limits, now, credential.data.token);
|
|
73551
73718
|
const headRepo = derived.repo;
|
|
73552
|
-
const probe = createForge(derived, headRepo, runner);
|
|
73719
|
+
const probe = memoizedIdentity(createForge(derived, headRepo, runner), scope, credential.data);
|
|
73553
73720
|
const info = probe.repoInfo(headRepo);
|
|
73554
73721
|
if (!info.ok && info.code !== "not_found")
|
|
73555
73722
|
return info;
|
|
@@ -73564,13 +73731,13 @@ function resolveForge(cwd, options = {}) {
|
|
|
73564
73731
|
const baseRepo = upstream && upstream !== headRepo ? upstream : info.ok && info.data.parent ? info.data.parent : headRepo;
|
|
73565
73732
|
const baseRemote = baseRepo === headRepo ? pushed.remote : remotes.find((name) => repoOfRemote(name) === baseRepo) ?? pushed.remote;
|
|
73566
73733
|
return success2({
|
|
73567
|
-
forge: baseRepo === headRepo ? probe : createForge(derived, baseRepo, runner),
|
|
73734
|
+
forge: baseRepo === headRepo ? probe : memoizedIdentity(createForge(derived, baseRepo, runner), scope, credential.data),
|
|
73568
73735
|
remote: pushed.remote,
|
|
73569
73736
|
url: pushed.url,
|
|
73570
73737
|
derived,
|
|
73571
73738
|
workspace: workspace?.name ?? null,
|
|
73572
73739
|
expectedAccount,
|
|
73573
|
-
credential,
|
|
73740
|
+
credential: credential.data.credential,
|
|
73574
73741
|
headRepo,
|
|
73575
73742
|
headProjectId,
|
|
73576
73743
|
fork: baseRepo !== headRepo,
|
|
@@ -73583,14 +73750,74 @@ function checkIdentity(resolved) {
|
|
|
73583
73750
|
const identity = forge.identity(expectedAccount);
|
|
73584
73751
|
if (!identity.ok)
|
|
73585
73752
|
return identity;
|
|
73586
|
-
if (identity.data.matches === false)
|
|
73587
|
-
|
|
73588
|
-
const where = `workspace ${resolved.workspace ?? "?"}`;
|
|
73589
|
-
return failure3("blocked", `identity_mismatch: the ${resolved.credential === "workspace_token_file" ? "vcs.tokenFile token" : `${bin} login`} is ${identity.data.login} but ${where} expects ${expectedAccount}`, resolved.credential === "workspace_token_file" ? `put a token for ${expectedAccount} in the ${where} vcs.tokenFile` : `${bin} auth login --hostname ${forge.apiHost} # sign in as ${expectedAccount}`);
|
|
73590
|
-
}
|
|
73753
|
+
if (identity.data.matches === false)
|
|
73754
|
+
return mismatch(resolved, identity.data.login ?? "?", resolved.workspace);
|
|
73591
73755
|
return identity;
|
|
73592
73756
|
}
|
|
73593
|
-
|
|
73757
|
+
function checkPushIdentity(cwd, options = {}) {
|
|
73758
|
+
let workspace;
|
|
73759
|
+
try {
|
|
73760
|
+
workspace = resolveRuntimeWorkspaceVcs(cwd);
|
|
73761
|
+
} catch (error) {
|
|
73762
|
+
return failure3("blocked", error instanceof Error ? error.message : String(error), "fix ~/.config/workit/workspaces.json (workit init → Workspaces)");
|
|
73763
|
+
}
|
|
73764
|
+
const account = workspace?.vcs?.account ?? null;
|
|
73765
|
+
const skipped = (forge, reason, unblock) => success2({
|
|
73766
|
+
status: "skipped",
|
|
73767
|
+
forge,
|
|
73768
|
+
account,
|
|
73769
|
+
login: null,
|
|
73770
|
+
reason,
|
|
73771
|
+
...unblock ? { unblock } : {}
|
|
73772
|
+
});
|
|
73773
|
+
const pushed = pushForge(cwd, {
|
|
73774
|
+
branch: options.branch,
|
|
73775
|
+
hosts: configuredHosts(),
|
|
73776
|
+
sshConfig: options.sshConfig
|
|
73777
|
+
});
|
|
73778
|
+
if (!pushed.ok) {
|
|
73779
|
+
const host = /^unsupported_forge: .*?"([^"]+)"/u.exec(pushed.error)?.[1];
|
|
73780
|
+
const what = /could not resolve ssh alias/u.test(pushed.error) ? `could not resolve ssh alias ${host} to GitHub or GitLab` : pushed.error.startsWith("unsupported_forge: ssh host") ? `ssh host ${host} is not a GitHub/GitLab host or an alias workit can resolve` : `${host} is not a GitHub or GitLab host workit knows`;
|
|
73781
|
+
return skipped(null, host ? `unsupported_forge: ${what}; account checks skipped, pushed with git only (no PR or CI verbs)` : pushed.error, pushed.unblock);
|
|
73782
|
+
}
|
|
73783
|
+
const derived = pushed.forge;
|
|
73784
|
+
const conflict = forgeConflict(derived, workspace?.vcs?.provider);
|
|
73785
|
+
if (conflict)
|
|
73786
|
+
return skipped(derived.kind, conflict.error, conflict.unblock);
|
|
73787
|
+
if (!account)
|
|
73788
|
+
return success2({ status: "no_account", forge: derived.kind, account, login: null });
|
|
73789
|
+
const base = options.runner ?? systemRunner(derived.kind, derived.apiHost, options.env ?? process.env);
|
|
73790
|
+
const scope = memoScope(options.runner, options.env ?? process.env);
|
|
73791
|
+
const now = options.now ?? Date.now;
|
|
73792
|
+
const limits = { deadline: now() + (options.budgetMs ?? PUSH_IDENTITY_BUDGET_MS) };
|
|
73793
|
+
const credential = resolveCredential(workspace, derived, clampedRunner(base, limits, now), scope);
|
|
73794
|
+
if (!credential.ok)
|
|
73795
|
+
return unreachable(credential) ? skipped(derived.kind, credential.error, credential.unblock) : credential;
|
|
73796
|
+
const forge = memoizedIdentity(createForge(derived, derived.repo, clampedRunner(base, limits, now, credential.data.token)), scope, credential.data);
|
|
73797
|
+
const identity = forge.identity(account);
|
|
73798
|
+
if (!identity.ok)
|
|
73799
|
+
return unreachable(identity) ? skipped(derived.kind, identity.error, identity.unblock) : identity;
|
|
73800
|
+
if (identity.data.matches === false)
|
|
73801
|
+
return mismatch({ forge, credential: credential.data.credential, expectedAccount: account }, identity.data.login ?? "?", workspace?.name ?? null);
|
|
73802
|
+
if (identity.data.matches === null)
|
|
73803
|
+
return skipped(derived.kind, identity.data.note ?? "the credential cannot read /user");
|
|
73804
|
+
return success2({ status: "verified", forge: derived.kind, account, login: identity.data.login });
|
|
73805
|
+
}
|
|
73806
|
+
var MEMO_TTL_MS, SYSTEM_SCOPE, memoScopes, memoScope = (runner, env) => ({
|
|
73807
|
+
owner: runner ?? SYSTEM_SCOPE,
|
|
73808
|
+
key: [env.GH_CONFIG_DIR, env.GLAB_CONFIG_DIR, env.XDG_CONFIG_HOME, env.HOME].map((value) => value ?? "").join("|")
|
|
73809
|
+
}), memoizedIdentity = (forge, scope, credential) => {
|
|
73810
|
+
if (credential.credential === "cli_login")
|
|
73811
|
+
return forge;
|
|
73812
|
+
const identity = (expected) => forge.identity(expected);
|
|
73813
|
+
return {
|
|
73814
|
+
...forge,
|
|
73815
|
+
identity: (expected) => memo4(scope, `identity ${forge.kind} ${forge.apiHost} ${credential.credential} ${credential.token ?? ""} ${expected ?? ""}`, () => {
|
|
73816
|
+
const value = identity(expected);
|
|
73817
|
+
return { keep: value.ok, value };
|
|
73818
|
+
})
|
|
73819
|
+
};
|
|
73820
|
+
}, configuredHosts = () => {
|
|
73594
73821
|
const vcs = readVcsConfig();
|
|
73595
73822
|
if (vcs.status !== "valid")
|
|
73596
73823
|
return {};
|
|
@@ -73599,7 +73826,13 @@ var configuredHosts = () => {
|
|
|
73599
73826
|
return typeof value === "string" && value.trim() ? [value.trim()] : [];
|
|
73600
73827
|
};
|
|
73601
73828
|
return { github: host("github"), gitlab: host("gitlab") };
|
|
73602
|
-
}, createForge = (derived, repo, runner) => derived.kind === "github" ? createGitHubForge({ apiHost: derived.apiHost, repo, runner }) : createGitLabForge({ apiHost: derived.apiHost, repo, runner })
|
|
73829
|
+
}, createForge = (derived, repo, runner) => derived.kind === "github" ? createGitHubForge({ apiHost: derived.apiHost, repo, runner }) : createGitLabForge({ apiHost: derived.apiHost, repo, runner }), mismatch = (resolved, login, workspace) => {
|
|
73830
|
+
const { forge, expectedAccount } = resolved;
|
|
73831
|
+
const bin = forge.kind === "github" ? "gh" : "glab";
|
|
73832
|
+
const where = `workspace ${workspace ?? "?"}`;
|
|
73833
|
+
const fileFix = `put a token for ${expectedAccount} in the ${where} vcs.tokenFile`;
|
|
73834
|
+
return failure3("blocked", `identity_mismatch: the ${resolved.credential === "workspace_token_file" ? "vcs.tokenFile token" : `${bin} login`} is ${login} but ${where} expects ${expectedAccount}`, resolved.credential === "workspace_token_file" ? fileFix : forge.kind === "github" ? `gh auth login --hostname ${forge.apiHost} # sign in as ${expectedAccount}` : `${fileFix} (glab keeps one login per host, so workit never switches it)`);
|
|
73835
|
+
}, unreachable = (result) => result.code === "unavailable" && !/ is not authenticated for /u.test(result.error), PUSH_IDENTITY_BUDGET_MS = 5000;
|
|
73603
73836
|
var init_resolve = __esm(() => {
|
|
73604
73837
|
init_vcs_config();
|
|
73605
73838
|
init_workspaces();
|
|
@@ -73607,6 +73840,10 @@ var init_resolve = __esm(() => {
|
|
|
73607
73840
|
init_exec();
|
|
73608
73841
|
init_github();
|
|
73609
73842
|
init_gitlab();
|
|
73843
|
+
init_redact();
|
|
73844
|
+
MEMO_TTL_MS = 5 * 60000;
|
|
73845
|
+
SYSTEM_SCOPE = {};
|
|
73846
|
+
memoScopes = new WeakMap;
|
|
73610
73847
|
});
|
|
73611
73848
|
|
|
73612
73849
|
// packages/workit-core/src/duration.ts
|
|
@@ -73699,16 +73936,19 @@ function releaseTrunk(io, track, branch) {
|
|
|
73699
73936
|
tracked: Boolean(resolved.releaseTrack)
|
|
73700
73937
|
};
|
|
73701
73938
|
}
|
|
73702
|
-
function connect(io, branch) {
|
|
73939
|
+
function connect(io, branch, options = {}) {
|
|
73940
|
+
const verbDeadline = options.deadline ?? null;
|
|
73703
73941
|
const resolved = resolveForge(io.cwd, {
|
|
73704
73942
|
branch,
|
|
73705
73943
|
env: io.env,
|
|
73706
73944
|
runner: forgeDeps.runner,
|
|
73707
|
-
now: forgeDeps.now
|
|
73945
|
+
now: forgeDeps.now,
|
|
73946
|
+
deadline: verbDeadline ?? forgeDeps.now() + CONNECT_BUDGET_MS
|
|
73708
73947
|
});
|
|
73709
73948
|
if (!resolved.ok)
|
|
73710
73949
|
return resolved;
|
|
73711
73950
|
const identity = checkIdentity(resolved.data);
|
|
73951
|
+
resolved.data.limits.deadline = verbDeadline;
|
|
73712
73952
|
if (!identity.ok)
|
|
73713
73953
|
return identity;
|
|
73714
73954
|
return {
|
|
@@ -73763,7 +74003,12 @@ function renderStatus(doc) {
|
|
|
73763
74003
|
lines.push(`next: ${doc.next}`);
|
|
73764
74004
|
return lines;
|
|
73765
74005
|
}
|
|
73766
|
-
var forgeDeps, MAX_REASON = 500, usage3 = (io, message, line) => emit(io, fail("invalid_input", message, { unblock: line })), forgeFail = (io, result, data) => emit(io, fail(result.code, result.error, { data, unblock: result.unblock })),
|
|
74006
|
+
var forgeDeps, MAX_REASON = 500, usage3 = (io, message, line) => emit(io, fail("invalid_input", message, { unblock: line })), forgeFail = (io, result, data) => emit(io, fail(result.code, result.error, { data, unblock: result.unblock })), CONNECT_BUDGET_MS = 15000, pushIdentity = (io, branch) => checkPushIdentity(io.cwd, {
|
|
74007
|
+
branch,
|
|
74008
|
+
env: io.env,
|
|
74009
|
+
runner: forgeDeps.runner,
|
|
74010
|
+
now: forgeDeps.now
|
|
74011
|
+
}), short = (sha) => sha ? sha.slice(0, 7) : "?";
|
|
73767
74012
|
var init_forge_common = __esm(() => {
|
|
73768
74013
|
init_resolve();
|
|
73769
74014
|
init_vcs_config();
|
|
@@ -74013,16 +74258,17 @@ async function wait(argv, io) {
|
|
|
74013
74258
|
const head = flags.values.head ?? null;
|
|
74014
74259
|
if (head !== null && !/^[0-9a-f]{7,64}$/u.test(head))
|
|
74015
74260
|
return usage3(io, "--head must be a commit sha (7-64 hex chars)", WAIT_USAGE);
|
|
74016
|
-
const
|
|
74261
|
+
const started = forgeDeps.now();
|
|
74262
|
+
const deadline = started + timeoutMs;
|
|
74263
|
+
const connected = connect(io, flags.values.branch, {
|
|
74264
|
+
deadline: started + Math.max(timeoutMs, ONE_POLL_MS)
|
|
74265
|
+
});
|
|
74017
74266
|
if (!connected.ok)
|
|
74018
74267
|
return forgeFail(io, connected);
|
|
74019
74268
|
const resolved = connected.data;
|
|
74020
74269
|
const number = selectPr(io.cwd, resolved, { pr, branch: flags.values.branch ?? null });
|
|
74021
74270
|
if (!number.ok)
|
|
74022
74271
|
return forgeFail(io, number);
|
|
74023
|
-
const started = forgeDeps.now();
|
|
74024
|
-
const deadline = started + timeoutMs;
|
|
74025
|
-
resolved.limits.deadline = started + Math.max(timeoutMs, ONE_POLL_MS);
|
|
74026
74272
|
let polls = 0;
|
|
74027
74273
|
let errors = 0;
|
|
74028
74274
|
let status = null;
|
|
@@ -74359,12 +74605,11 @@ async function push(argv, io) {
|
|
|
74359
74605
|
const plan = pushPreflight(io.cwd);
|
|
74360
74606
|
if (!plan.ok)
|
|
74361
74607
|
return forgeFail(io, plan);
|
|
74362
|
-
|
|
74363
|
-
|
|
74364
|
-
|
|
74365
|
-
|
|
74366
|
-
}
|
|
74367
|
-
const grant = requireGrant(io.cwd, "push", { forge: !plan.data.local });
|
|
74608
|
+
const identity = plan.data.local ? null : pushIdentity(io, plan.data.branch);
|
|
74609
|
+
if (identity && !identity.ok)
|
|
74610
|
+
return forgeFail(io, identity);
|
|
74611
|
+
const checked = identity?.data ?? null;
|
|
74612
|
+
const grant = requireGrant(io.cwd, "push", { forge: checked?.forge != null });
|
|
74368
74613
|
if (!grant.allowed)
|
|
74369
74614
|
return emit(io, fail("blocked", grant.error, {
|
|
74370
74615
|
unblock: grant.unblock,
|
|
@@ -74375,14 +74620,22 @@ async function push(argv, io) {
|
|
|
74375
74620
|
overwriteUnintegrated: flags.booleans.has("overwrite-unintegrated"),
|
|
74376
74621
|
expect,
|
|
74377
74622
|
setUpstream: flags.booleans.has("set-upstream"),
|
|
74378
|
-
actor: actorFromEnv(io.env)
|
|
74623
|
+
actor: actorFromEnv(io.env),
|
|
74624
|
+
...checked ? { identity: { ...checked } } : {}
|
|
74379
74625
|
});
|
|
74380
74626
|
if (!result.ok)
|
|
74381
74627
|
return forgeFail(io, result, {
|
|
74382
74628
|
branch: plan.data.branch,
|
|
74383
74629
|
sha: plan.data.sha
|
|
74384
74630
|
});
|
|
74385
|
-
|
|
74631
|
+
const warnings = checked?.status === "skipped" ? [
|
|
74632
|
+
`identity check skipped: ${checked.reason}${checked.unblock ? ` (to restore it: ${checked.unblock})` : ""}`
|
|
74633
|
+
] : [];
|
|
74634
|
+
for (const warning of warnings)
|
|
74635
|
+
io.stderr(`warning: ${warning}
|
|
74636
|
+
`);
|
|
74637
|
+
const envelope = ok({ ...result.data, identity: checked });
|
|
74638
|
+
return emit(io, warnings.length ? { ...envelope, warnings } : envelope, (data) => [
|
|
74386
74639
|
data.pushed ? `pushed ${data.branch} ${short2(data.previous)} -> ${short2(data.sha)} to ${data.remote}${data.forced ? " (force-with-lease)" : ""}; remote tip verified` : `${data.remote}/${data.branch} is already at ${short2(data.sha)}; remote tip verified`,
|
|
74387
74640
|
...data.upstream ? [`upstream: ${data.remote}/${data.branch}`] : []
|
|
74388
74641
|
]);
|
|
@@ -74415,7 +74668,7 @@ var init_git2 = __esm(() => {
|
|
|
74415
74668
|
});
|
|
74416
74669
|
|
|
74417
74670
|
// packages/workit-core/src/forge/verify.ts
|
|
74418
|
-
import { spawnSync as
|
|
74671
|
+
import { spawnSync as spawnSync20 } from "node:child_process";
|
|
74419
74672
|
function finish(cwd, report, actor) {
|
|
74420
74673
|
const delivered = report.observations.length > 0 && report.observations.every((entry) => entry.ok);
|
|
74421
74674
|
const out = { ...report, delivered };
|
|
@@ -74546,7 +74799,7 @@ function parsePackageSpec(spec) {
|
|
|
74546
74799
|
const version = at > 0 ? value.slice(at + 1) : null;
|
|
74547
74800
|
if (!name || name.length > 214 || !NPM_NAME.test(name))
|
|
74548
74801
|
return null;
|
|
74549
|
-
if (version !== null && !
|
|
74802
|
+
if (version !== null && !SEMVER2.test(version))
|
|
74550
74803
|
return null;
|
|
74551
74804
|
return { name, version };
|
|
74552
74805
|
}
|
|
@@ -74581,7 +74834,7 @@ function verifyReleased(cwd, input, npm = systemNpm) {
|
|
|
74581
74834
|
const version = spec.version ?? (input.tag ? input.tag.replace(/^v(?=\d)/u, "") : null);
|
|
74582
74835
|
if (!version)
|
|
74583
74836
|
return failure3("invalid_input", "--package needs a version (<name>@<version>) or --tag");
|
|
74584
|
-
if (!
|
|
74837
|
+
if (!SEMVER2.test(version))
|
|
74585
74838
|
return failure3("invalid_input", `${version} (from --tag) is not a semver version; pass <name>@<version>`);
|
|
74586
74839
|
const viewed = npm(["view", `${spec.name}@${version}`, "version", "gitHead", "--json"]);
|
|
74587
74840
|
if (viewed.status !== 0 && !/E404|404 Not Found|is not in this registry|No match found/iu.test(viewed.stderr))
|
|
@@ -74615,7 +74868,7 @@ function verifyReleased(cwd, input, npm = systemNpm) {
|
|
|
74615
74868
|
observations
|
|
74616
74869
|
}, input.actor));
|
|
74617
74870
|
}
|
|
74618
|
-
var NPM_NAME,
|
|
74871
|
+
var NPM_NAME, SEMVER2, SAFE_ARG, systemNpm = (args) => {
|
|
74619
74872
|
if (!args.every((arg) => SAFE_ARG.test(arg)))
|
|
74620
74873
|
return { status: 2, stdout: "", stderr: "refused: unsafe npm argument" };
|
|
74621
74874
|
const options = {
|
|
@@ -74628,7 +74881,7 @@ var NPM_NAME, SEMVER, SAFE_ARG, systemNpm = (args) => {
|
|
|
74628
74881
|
};
|
|
74629
74882
|
const found = process.platform === "win32" ? findExecutable("npm.cmd", process.env) : null;
|
|
74630
74883
|
const npmCmd = found && !/["%!^]/u.test(found) ? `"${found}"` : "npm.cmd";
|
|
74631
|
-
const result = process.platform === "win32" ?
|
|
74884
|
+
const result = process.platform === "win32" ? spawnSync20(`${npmCmd} ${args.join(" ")}`, { ...options, shell: true }) : spawnSync20("npm", [...args], options);
|
|
74632
74885
|
return { status: result.status, stdout: result.stdout ?? "", stderr: result.stderr ?? "" };
|
|
74633
74886
|
}, short3 = (sha) => sha ? sha.slice(0, 12) : "(absent)", npmView = (stdout) => {
|
|
74634
74887
|
try {
|
|
@@ -74646,7 +74899,7 @@ var init_verify = __esm(() => {
|
|
|
74646
74899
|
init_report();
|
|
74647
74900
|
init_redact();
|
|
74648
74901
|
NPM_NAME = /^(?:@[A-Za-z0-9~][A-Za-z0-9-._~]*\/)?[A-Za-z0-9~][A-Za-z0-9-._~]*$/u;
|
|
74649
|
-
|
|
74902
|
+
SEMVER2 = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/u;
|
|
74650
74903
|
SAFE_ARG = /^[0-9A-Za-z@/._~+-]+$/u;
|
|
74651
74904
|
});
|
|
74652
74905
|
|
|
@@ -74735,7 +74988,7 @@ var init_verify_delivery = __esm(() => {
|
|
|
74735
74988
|
});
|
|
74736
74989
|
|
|
74737
74990
|
// packages/workit-core/src/stack.ts
|
|
74738
|
-
import { spawnSync as
|
|
74991
|
+
import { spawnSync as spawnSync21 } from "node:child_process";
|
|
74739
74992
|
import { createHash as createHash15, randomBytes as randomBytes3 } from "node:crypto";
|
|
74740
74993
|
import fs22 from "node:fs";
|
|
74741
74994
|
import path44 from "node:path";
|
|
@@ -75248,7 +75501,7 @@ function commitKeys(cwd, range) {
|
|
|
75248
75501
|
const patches = new Map;
|
|
75249
75502
|
const diff = gitRun3(cwd, ["log", "--no-merges", "-p", "--format=commit %H", range]);
|
|
75250
75503
|
if (diff.ok && diff.stdout.trim()) {
|
|
75251
|
-
const ids =
|
|
75504
|
+
const ids = spawnSync21("git", ["patch-id", "--stable"], {
|
|
75252
75505
|
cwd,
|
|
75253
75506
|
input: diff.stdout,
|
|
75254
75507
|
encoding: "utf8",
|
|
@@ -75872,7 +76125,7 @@ var STACK_VERSION = 1, stackFail = (code, error, unblock, data) => ({
|
|
|
75872
76125
|
...unblock ? { unblock } : {},
|
|
75873
76126
|
...data ? { data } : {}
|
|
75874
76127
|
}), fromForge = (result) => stackFail(result.code, result.error, result.unblock), stackDeps, gitRun3 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
|
|
75875
|
-
const run =
|
|
76128
|
+
const run = spawnSync21("git", args, {
|
|
75876
76129
|
cwd,
|
|
75877
76130
|
encoding: "utf8",
|
|
75878
76131
|
timeout: timeoutMs,
|
|
@@ -76167,7 +76420,7 @@ var init_stack2 = __esm(() => {
|
|
|
76167
76420
|
});
|
|
76168
76421
|
|
|
76169
76422
|
// packages/workit-core/src/fanout.ts
|
|
76170
|
-
import { spawnSync as
|
|
76423
|
+
import { spawnSync as spawnSync22 } from "node:child_process";
|
|
76171
76424
|
import { createHash as createHash16 } from "node:crypto";
|
|
76172
76425
|
import fs23 from "node:fs";
|
|
76173
76426
|
import path45 from "node:path";
|
|
@@ -76940,7 +77193,7 @@ var FANOUT_VERSION = 1, TIERS, FAN_IN_MODES, fanoutFail = (code, error, unblock,
|
|
|
76940
77193
|
text: edges.map((edge) => `add "${edge.dependsOn}" to "dependsOn" of slice ${edge.slice}`).join("; ")
|
|
76941
77194
|
};
|
|
76942
77195
|
}, gitRun4 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
|
|
76943
|
-
const run =
|
|
77196
|
+
const run = spawnSync22("git", args, {
|
|
76944
77197
|
cwd,
|
|
76945
77198
|
encoding: "utf8",
|
|
76946
77199
|
timeout: timeoutMs,
|
|
@@ -77460,7 +77713,7 @@ var init_fanout_brief = __esm(() => {
|
|
|
77460
77713
|
});
|
|
77461
77714
|
|
|
77462
77715
|
// packages/workit-core/src/fanout-landed.ts
|
|
77463
|
-
import { spawnSync as
|
|
77716
|
+
import { spawnSync as spawnSync23 } from "node:child_process";
|
|
77464
77717
|
function forgeBreaker(resolved) {
|
|
77465
77718
|
if (!resolved)
|
|
77466
77719
|
return { forge: null, down: () => null };
|
|
@@ -77551,7 +77804,7 @@ function trunkPatchIds(ctx, fork) {
|
|
|
77551
77804
|
"--"
|
|
77552
77805
|
]);
|
|
77553
77806
|
if (log.ok && log.stdout.trim()) {
|
|
77554
|
-
const run =
|
|
77807
|
+
const run = spawnSync23("git", ["patch-id", "--stable"], {
|
|
77555
77808
|
cwd: ctx.cwd,
|
|
77556
77809
|
input: log.stdout,
|
|
77557
77810
|
encoding: "utf8",
|
|
@@ -94494,11 +94747,11 @@ var init_rules = __esm(() => {
|
|
|
94494
94747
|
});
|
|
94495
94748
|
|
|
94496
94749
|
// packages/workit-cli/src/test-audit/audit.ts
|
|
94497
|
-
import { spawnSync as
|
|
94750
|
+
import { spawnSync as spawnSync24 } from "node:child_process";
|
|
94498
94751
|
import { existsSync as existsSync19, readdirSync as readdirSync8, readFileSync as readFileSync21, statSync as statSync7 } from "node:fs";
|
|
94499
94752
|
import path49 from "node:path";
|
|
94500
94753
|
function git6(cwd, args) {
|
|
94501
|
-
const run =
|
|
94754
|
+
const run = spawnSync24("git", ["-c", "core.quotepath=off", ...args], {
|
|
94502
94755
|
cwd,
|
|
94503
94756
|
encoding: "utf8",
|
|
94504
94757
|
maxBuffer: 64 * 1024 * 1024
|
|
@@ -96391,9 +96644,21 @@ async function main(argv, overrides = {}, options = {}) {
|
|
|
96391
96644
|
globalThis[Symbol.for("workit.migrationReporter")] = (report) => io.stderr(`workit: migrated ${report.tasks} task${report.tasks === 1 ? "" : "s"} from ${report.from} to ${report.to} (backup: ${report.backup})
|
|
96392
96645
|
`);
|
|
96393
96646
|
const verb = await entry.load();
|
|
96647
|
+
const runVerb = (target) => guarded(target, () => verb.run(ended, target));
|
|
96394
96648
|
if (!io.json)
|
|
96395
|
-
return
|
|
96396
|
-
return runJsonPure(io, command,
|
|
96649
|
+
return runVerb(io);
|
|
96650
|
+
return runJsonPure(io, command, runVerb);
|
|
96651
|
+
}
|
|
96652
|
+
async function guarded(io, run) {
|
|
96653
|
+
try {
|
|
96654
|
+
return await run();
|
|
96655
|
+
} catch (error) {
|
|
96656
|
+
if (!(error instanceof Error) || error.name !== "WorkspaceConfigError")
|
|
96657
|
+
throw error;
|
|
96658
|
+
return emit(io, fail("blocked", error.message, {
|
|
96659
|
+
unblock: "fix ~/.config/workit/workspaces.json (workit init → Workspaces)"
|
|
96660
|
+
}));
|
|
96661
|
+
}
|
|
96397
96662
|
}
|
|
96398
96663
|
var CODE_FOR_EXIT = {
|
|
96399
96664
|
1: "failed",
|