@brainervirus/workit-pi 8.15.1 → 8.17.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/workit.js CHANGED
@@ -12430,6 +12430,17 @@ import fs9 from "node:fs";
12430
12430
  import path9 from "node:path";
12431
12431
  var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
12432
12432
  var CHECKS_FILE = "workit.checks.json";
12433
+ var checksConfigFile = (root) => path9.join(root, CHECKS_FILE);
12434
+ function repoHeavyCommands(root) {
12435
+ try {
12436
+ const value = JSON.parse(fs9.readFileSync(checksConfigFile(root), "utf8"));
12437
+ const resources = isRecord(value) ? value.resources : undefined;
12438
+ const list = isRecord(resources) ? resources.heavyCommands : undefined;
12439
+ return Array.isArray(list) ? list.filter((item) => typeof item === "string") : [];
12440
+ } catch {
12441
+ return [];
12442
+ }
12443
+ }
12433
12444
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
12434
12445
  var SHELL_META = /[|&;<>()$`\\*?[\]{}~!#]/u;
12435
12446
  function splitCommand(command) {
@@ -12542,7 +12553,7 @@ function detectedChecks(root) {
12542
12553
  var GATES = ["testing", "verification"];
12543
12554
  function loadCheckConfig(cwd) {
12544
12555
  const root = checkRoot(cwd);
12545
- const file = path9.join(root, CHECKS_FILE);
12556
+ const file = checksConfigFile(root);
12546
12557
  const none = (error = null) => ({
12547
12558
  source: error ? CHECKS_FILE : "none",
12548
12559
  root,
@@ -13184,6 +13195,7 @@ var workspaceConfigSchema = object({
13184
13195
  autoApprove: union([boolean2(), array(nonBlank)]).optional(),
13185
13196
  defaultEndpoint: unknown().optional(),
13186
13197
  verification: _enum(["self", "independent"]).optional(),
13198
+ delegation: unknown().optional(),
13187
13199
  trailers: boolean2().optional(),
13188
13200
  liveSessionGuard: boolean2().optional(),
13189
13201
  fanout: object({ tierModels: unknown().optional() }).passthrough().optional(),
@@ -16023,6 +16035,58 @@ function evaluateClosure(requestedOutcome, view) {
16023
16035
  requirementIds: evaluations.map((item) => item.requirementId)
16024
16036
  });
16025
16037
  }
16038
+ var BRIEF_TEXT = 250;
16039
+ var BRIEF_BLOCKERS = 3;
16040
+ var BRIEF_OPEN = 8;
16041
+ var clip = (value) => {
16042
+ const fits = (text) => Buffer.byteLength(JSON.stringify(text)) <= BRIEF_TEXT;
16043
+ if (fits(value))
16044
+ return value;
16045
+ let length = Math.min(value.length, BRIEF_TEXT);
16046
+ while (length > 0 && !fits(`${value.slice(0, length)}…`))
16047
+ length -= 1;
16048
+ return `${value.slice(0, length)}…`;
16049
+ };
16050
+ function boundTaskSummary(summary) {
16051
+ const rules = new Map((summary.policy?.requirements ?? []).map((item) => [item.id, item]));
16052
+ const unmet = summary.requirements.filter((item) => item.status === "unsatisfied" || item.status === "unavailable");
16053
+ const { progress, closure } = summary;
16054
+ const next = progress.nextAction;
16055
+ const open = unmet.slice(0, BRIEF_OPEN).map((item) => ({
16056
+ dimension: rules.get(item.requirementId)?.dimension ?? "verification",
16057
+ before: rules.get(item.requirementId)?.before ?? "close",
16058
+ status: item.status
16059
+ }));
16060
+ const brief = {
16061
+ id: summary.id,
16062
+ revision: summary.revision,
16063
+ workspaceRevision: summary.workspaceRevision,
16064
+ updatedAt: summary.updatedAt,
16065
+ objective: clip(summary.objective),
16066
+ status: summary.status,
16067
+ closure: closure && {
16068
+ outcome: closure.outcome,
16069
+ at: closure.at,
16070
+ summary: clip(closure.summary)
16071
+ },
16072
+ progress: {
16073
+ summary: clip(progress.summary),
16074
+ nextAction: next && clip(next),
16075
+ blockers: progress.blockers.slice(0, BRIEF_BLOCKERS).map((item) => ({
16076
+ reason: clip(item.reason),
16077
+ dependentAction: clip(item.dependentAction)
16078
+ }))
16079
+ },
16080
+ requirements: { total: summary.requirements.length, unmet: unmet.length, open },
16081
+ truncated: false
16082
+ };
16083
+ brief.truncated = JSON.stringify(brief).includes("…") || progress.blockers.length > BRIEF_BLOCKERS || unmet.length > BRIEF_OPEN;
16084
+ return brief;
16085
+ }
16086
+ function briefInspectResult(request, result) {
16087
+ const { action, view } = request ?? {};
16088
+ return result.ok && action === "inspect" && (view ?? "summary") === "summary" ? { ...result, data: boundTaskSummary(result.data) } : result;
16089
+ }
16026
16090
 
16027
16091
  // packages/workit-core/src/core/methods.ts
16028
16092
  var METHODS = {
@@ -17416,7 +17480,16 @@ function rawInvocations(command, dialect = "posix", depth = 0) {
17416
17480
  return found;
17417
17481
  }
17418
17482
  var RECENT = new Map;
17419
- function walk2(command, dialect, depth) {
17483
+ function commandLines(command, dialect = "posix") {
17484
+ const lines = [];
17485
+ try {
17486
+ walk2(command, dialect, 0, lines);
17487
+ } catch (error) {
17488
+ throw error instanceof ShellAnalysisError ? error : new ShellAnalysisError(error instanceof Error ? error.message : String(error));
17489
+ }
17490
+ return lines;
17491
+ }
17492
+ function walk2(command, dialect, depth, seen) {
17420
17493
  const found = [];
17421
17494
  let dir = null;
17422
17495
  const groups = [];
@@ -17427,7 +17500,8 @@ function walk2(command, dialect, depth) {
17427
17500
  const nested = (script, inner) => {
17428
17501
  if (!script || depth >= MAX_DEPTH)
17429
17502
  return;
17430
- for (const invocation of rawInvocations(script, inner, depth + 1))
17503
+ const inside = seen ? walk2(script, inner, depth + 1, seen) : rawInvocations(script, inner, depth + 1);
17504
+ for (const invocation of inside)
17431
17505
  found.push({ ...invocation, dir: rebase(dir, invocation.dir) });
17432
17506
  };
17433
17507
  const visit = (input, stdin, hops) => {
@@ -17439,11 +17513,17 @@ function walk2(command, dialect, depth) {
17439
17513
  const name = commandName(words[0]);
17440
17514
  if (!name)
17441
17515
  return;
17516
+ seen?.push(words);
17517
+ if (name === "-c" || name === "--command")
17518
+ return nested(words[1], "posix");
17442
17519
  const runs = (lines) => {
17443
17520
  if (hops < MAX_DEPTH)
17444
17521
  for (const next of lines)
17445
17522
  visit(next, undefined, hops + 1);
17446
17523
  };
17524
+ const wrapped = seen ? null : workitCheckTail(words);
17525
+ if (wrapped)
17526
+ return "script" in wrapped ? nested(wrapped.script, "posix") : runs([wrapped.argv]);
17447
17527
  if (CD.has(name)) {
17448
17528
  const target = cdTarget(words);
17449
17529
  dir = target === null ? os4.homedir() : joinDir(dir, target);
@@ -17534,6 +17614,51 @@ var LAUNCHERS = new Set([
17534
17614
  "command"
17535
17615
  ]);
17536
17616
  var WORKIT_BIN = /(?:^|[\\/])workit(?:\.exe)?$|^@brainervirus\/workit-cli$/;
17617
+ var WORKIT_PACKAGE = /^@brainervirus\/workit-cli(?:@\S*)?$/;
17618
+ var PACKAGE_RUNNERS = new Set(["npx", "bunx", "pnpx"]);
17619
+ function workitCheckTail(words) {
17620
+ let at = 0;
17621
+ if (PACKAGE_RUNNERS.has(commandName(words[0]))) {
17622
+ at = words.findIndex((word, index) => index > 0 && !word.startsWith("-"));
17623
+ if (at < 0 || !WORKIT_PACKAGE.test(words[at]))
17624
+ return null;
17625
+ } else if (!/^workit(?:\.exe|\.cmd)?$/.test(commandName(words[0])))
17626
+ return null;
17627
+ const args = words.slice(at + 1);
17628
+ if (args[0] !== "check" && !(args[0] === "lock" && args[1] === "run"))
17629
+ return null;
17630
+ const split = args.indexOf("--");
17631
+ const tail = split < 0 ? [] : args.slice(split + 1);
17632
+ if (!tail.length)
17633
+ return null;
17634
+ return args.slice(0, split).includes("--shell") ? { script: tail.join(" ") } : { argv: tail };
17635
+ }
17636
+ function simpleCommand(command, dialect = "posix", depth = 0) {
17637
+ let segments;
17638
+ try {
17639
+ segments = segmentsOf(command, dialect);
17640
+ } catch {
17641
+ return null;
17642
+ }
17643
+ if (segments.length !== 1)
17644
+ return null;
17645
+ const [segment] = segments;
17646
+ if (segment.group || segment.pipe || segment.unanalyzable || segment.redirects.length || segment.subs?.length || segment.stdin?.length)
17647
+ return null;
17648
+ const { words } = stripPrefixes(segment.words);
17649
+ const name = commandName(words[0]);
17650
+ if (!name || depth >= MAX_DEPTH)
17651
+ return null;
17652
+ if (SHELLS.has(name)) {
17653
+ const script = shellScript(words, undefined);
17654
+ return script ? simpleCommand(script, "posix", depth + 1) : null;
17655
+ }
17656
+ if (name === "-c" || name === "--command")
17657
+ return words[1] ? simpleCommand(words[1], "posix", depth + 1) : null;
17658
+ if (EVALS.has(name) || POWERSHELLS.has(name) || ["xargs", "find", "parallel", "script", "cmd", "source", "."].includes(name))
17659
+ return null;
17660
+ return words;
17661
+ }
17537
17662
  function workitInvocations(command, dialect = "posix", depth = 0) {
17538
17663
  const found = [];
17539
17664
  for (const { words } of segmentsOf(command, dialect)) {
@@ -19460,6 +19585,15 @@ class WorkitCore {
19460
19585
  if (!parsed.ok)
19461
19586
  return parsed;
19462
19587
  const input = parsed.data;
19588
+ if (this.context.readOnlyChild) {
19589
+ if (input.action !== "inspect")
19590
+ return failure2("permission_denied", "child sessions can only inspect the task summary");
19591
+ const task = this.store.readTask(input.taskId);
19592
+ if (!task.ok)
19593
+ return task;
19594
+ const summary = this.summary(task.data);
19595
+ return summary.ok ? { ...summary, data: boundTaskSummary(summary.data) } : summary;
19596
+ }
19463
19597
  if ((this.context.workerId ?? null) !== null) {
19464
19598
  if (input.action !== "inspect" && input.action !== "list")
19465
19599
  return failure2("permission_denied", "helpers cannot control task lifecycle or scope");
@@ -21903,17 +22037,827 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
21903
22037
  }
21904
22038
  return policy && !policy.ok ? { kind: "deny", reason: `branch_policy_denied: ${policy.error}`, unblock: null } : { kind: "none" };
21905
22039
  };
21906
- // packages/workit-core/src/hooks/write-gate.ts
21907
- import { realpathSync as realpathSync6 } from "node:fs";
22040
+ // packages/workit-core/src/core/heavy-rules.ts
22041
+ import fs19 from "node:fs";
22042
+ import os6 from "node:os";
22043
+ import path26 from "node:path";
22044
+ var EMPTY = () => ({ deny: [], allow: [] });
22045
+ var readText2 = (file) => {
22046
+ try {
22047
+ return fs19.readFileSync(file, "utf8");
22048
+ } catch (error) {
22049
+ if (error.code === "ENOENT")
22050
+ return null;
22051
+ throw error;
22052
+ }
22053
+ };
22054
+ var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
22055
+ function parseJsonc(text) {
22056
+ let out = "";
22057
+ for (let index = 0;index < text.length; index += 1) {
22058
+ const char = text[index];
22059
+ if (char === '"') {
22060
+ const end = /"(?:[^"\\]|\\.)*"/y;
22061
+ end.lastIndex = index;
22062
+ const match = end.exec(text);
22063
+ if (!match)
22064
+ throw new Error("unterminated string");
22065
+ out += match[0];
22066
+ index += match[0].length - 1;
22067
+ } else if (char === "/" && text[index + 1] === "/") {
22068
+ while (index < text.length && text[index] !== `
22069
+ `)
22070
+ index += 1;
22071
+ out += `
22072
+ `;
22073
+ } else if (char === "/" && text[index + 1] === "*") {
22074
+ const close = text.indexOf("*/", index + 2);
22075
+ if (close < 0)
22076
+ throw new Error("unterminated comment");
22077
+ index = close + 1;
22078
+ } else
22079
+ out += char;
22080
+ }
22081
+ return JSON.parse(out.replace(/,(\s*[}\]])/gu, "$1"));
22082
+ }
22083
+ function gitTop(cwd) {
22084
+ for (let dir = path26.resolve(cwd);; dir = path26.dirname(dir)) {
22085
+ if (fs19.existsSync(path26.join(dir, ".git")))
22086
+ return dir;
22087
+ if (path26.dirname(dir) === dir)
22088
+ return null;
22089
+ }
22090
+ }
22091
+ var projectDirs = (cwd) => {
22092
+ const top = gitTop(cwd);
22093
+ const dirs = [];
22094
+ for (let dir = path26.resolve(cwd);; dir = path26.dirname(dir)) {
22095
+ dirs.push(dir);
22096
+ if (!top || dir === top || path26.dirname(dir) === dir)
22097
+ return dirs;
22098
+ }
22099
+ };
22100
+ var homeOf = (env) => env.HOME || os6.homedir();
22101
+ var CLAUDE_MANAGED = {
22102
+ linux: "/etc/claude-code/managed-settings.json",
22103
+ darwin: "/Library/Application Support/ClaudeCode/managed-settings.json",
22104
+ win32: "C:\\Program Files\\ClaudeCode\\managed-settings.json"
22105
+ };
22106
+ function claudeSettingsFiles(cwd, env, platform = process.platform) {
22107
+ const project = env.CLAUDE_PROJECT_DIR || gitTop(cwd) || cwd;
22108
+ const user = env.CLAUDE_CONFIG_DIR || path26.join(homeOf(env), ".claude");
22109
+ return [
22110
+ ...CLAUDE_MANAGED[platform] ? [CLAUDE_MANAGED[platform]] : [],
22111
+ path26.join(user, "settings.json"),
22112
+ path26.join(project, ".claude", "settings.json"),
22113
+ path26.join(project, ".claude", "settings.local.json")
22114
+ ];
22115
+ }
22116
+ var bashPattern = (entry) => {
22117
+ const match = /^Bash(?:\((.*)\))?$/su.exec(entry.trim());
22118
+ return match ? match[1] ?? "" : null;
22119
+ };
22120
+ function claudeRules(cwd, env = process.env) {
22121
+ const rules = EMPTY();
22122
+ try {
22123
+ for (const file of claudeSettingsFiles(cwd, env)) {
22124
+ const text = readText2(file);
22125
+ if (text === null)
22126
+ continue;
22127
+ const value = JSON.parse(text);
22128
+ if (!isRecord4(value))
22129
+ return "unreadable";
22130
+ const permissions = value.permissions;
22131
+ if (permissions === undefined)
22132
+ continue;
22133
+ if (!isRecord4(permissions))
22134
+ return "unreadable";
22135
+ for (const [key, bucket] of [
22136
+ ["deny", rules.deny],
22137
+ ["ask", rules.deny],
22138
+ ["allow", rules.allow]
22139
+ ]) {
22140
+ const list = permissions[key];
22141
+ if (list === undefined)
22142
+ continue;
22143
+ if (!Array.isArray(list))
22144
+ return "unreadable";
22145
+ for (const entry of list) {
22146
+ if (typeof entry !== "string")
22147
+ return "unreadable";
22148
+ const pattern = bashPattern(entry);
22149
+ if (pattern !== null)
22150
+ bucket.push(pattern);
22151
+ }
22152
+ }
22153
+ }
22154
+ } catch {
22155
+ return "unreadable";
22156
+ }
22157
+ return rules;
22158
+ }
22159
+ var OPENCODE_NAMES = ["opencode.json", "opencode.jsonc"];
22160
+ var opencodePermission = (permission, rules) => {
22161
+ if (permission === undefined)
22162
+ return true;
22163
+ if (typeof permission === "string") {
22164
+ (permission === "allow" ? rules.allow : rules.deny).push("");
22165
+ return ["allow", "ask", "deny"].includes(permission);
22166
+ }
22167
+ if (!isRecord4(permission))
22168
+ return false;
22169
+ for (const key of ["bash", "shell"]) {
22170
+ const value = permission[key];
22171
+ if (value === undefined)
22172
+ continue;
22173
+ if (typeof value === "string") {
22174
+ if (!["allow", "ask", "deny"].includes(value))
22175
+ return false;
22176
+ (value === "allow" ? rules.allow : rules.deny).push("");
22177
+ continue;
22178
+ }
22179
+ if (!isRecord4(value))
22180
+ return false;
22181
+ for (const [pattern, action] of Object.entries(value)) {
22182
+ if (action !== "allow" && action !== "ask" && action !== "deny")
22183
+ return false;
22184
+ (action === "allow" ? rules.allow : rules.deny).push(pattern);
22185
+ }
22186
+ }
22187
+ return true;
22188
+ };
22189
+ var opencodeConfig = (value, rules) => {
22190
+ if (!isRecord4(value))
22191
+ return false;
22192
+ if (!opencodePermission(value.permission, rules))
22193
+ return false;
22194
+ if (value.agent === undefined)
22195
+ return true;
22196
+ if (!isRecord4(value.agent))
22197
+ return false;
22198
+ return Object.values(value.agent).every((agent) => isRecord4(agent) && opencodePermission(agent.permission, rules));
22199
+ };
22200
+ function opencodeRules(cwd, env = process.env) {
22201
+ const rules = EMPTY();
22202
+ try {
22203
+ const config = env.XDG_CONFIG_HOME || path26.join(homeOf(env), ".config");
22204
+ const files = [
22205
+ ...OPENCODE_NAMES.map((name) => path26.join(config, "opencode", name)),
22206
+ ...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path26.join(dir, name), path26.join(dir, ".opencode", name)])),
22207
+ ...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : []
22208
+ ];
22209
+ for (const file of files) {
22210
+ const text = readText2(file);
22211
+ if (text !== null && !opencodeConfig(parseJsonc(text), rules))
22212
+ return "unreadable";
22213
+ }
22214
+ if (env.OPENCODE_CONFIG_CONTENT && !opencodeConfig(parseJsonc(env.OPENCODE_CONFIG_CONTENT), rules))
22215
+ return "unreadable";
22216
+ } catch {
22217
+ return "unreadable";
22218
+ }
22219
+ return rules;
22220
+ }
22221
+ var codexRule = (body) => {
22222
+ const open = /\bpattern\s*=\s*\[/u.exec(body);
22223
+ if (!open)
22224
+ return null;
22225
+ const words = [];
22226
+ let index = open.index + open[0].length;
22227
+ for (;; ) {
22228
+ while (/[\s,]/u.test(body[index] ?? ""))
22229
+ index += 1;
22230
+ if (body[index] === "]")
22231
+ break;
22232
+ if (body[index] !== '"')
22233
+ return null;
22234
+ const literal = /"((?:[^"\\]|\\.)*)"/y;
22235
+ literal.lastIndex = index;
22236
+ const match = literal.exec(body);
22237
+ if (!match)
22238
+ return null;
22239
+ words.push(JSON.parse(`"${match[1]}"`));
22240
+ index = literal.lastIndex;
22241
+ }
22242
+ const decision = /\bdecision\s*=\s*"(\w+)"/u.exec(body)?.[1];
22243
+ if (!words.length || !decision || !["allow", "prompt", "forbidden"].includes(decision))
22244
+ return null;
22245
+ return { pattern: words.join(" "), decision };
22246
+ };
22247
+ function codexRules(cwd, env = process.env) {
22248
+ const rules = EMPTY();
22249
+ try {
22250
+ const home = env.CODEX_HOME || path26.join(homeOf(env), ".codex");
22251
+ const top = gitTop(cwd) ?? cwd;
22252
+ for (const dir of [path26.join(home, "rules"), path26.join(top, ".codex", "rules")]) {
22253
+ let files;
22254
+ try {
22255
+ files = fs19.readdirSync(dir).filter((name) => name.endsWith(".rules"));
22256
+ } catch (error) {
22257
+ if (error.code === "ENOENT")
22258
+ continue;
22259
+ return "unreadable";
22260
+ }
22261
+ for (const name of files) {
22262
+ const text = readText2(path26.join(dir, name)) ?? "";
22263
+ for (const part of text.split(/\bprefix_rule\s*\(/u).slice(1)) {
22264
+ const rule = codexRule(part);
22265
+ if (!rule)
22266
+ return "unreadable";
22267
+ (rule.decision === "allow" ? rules.allow : rules.deny).push(rule.pattern);
22268
+ }
22269
+ }
22270
+ }
22271
+ } catch {
22272
+ return "unreadable";
22273
+ }
22274
+ return rules;
22275
+ }
22276
+ function cursorRules(cwd, env = process.env) {
22277
+ const rules = EMPTY();
22278
+ try {
22279
+ const top = gitTop(cwd) ?? cwd;
22280
+ for (const file of [
22281
+ path26.join(homeOf(env), ".cursor", "cli-config.json"),
22282
+ path26.join(top, ".cursor", "cli.json")
22283
+ ]) {
22284
+ const text = readText2(file);
22285
+ if (text === null)
22286
+ continue;
22287
+ const value = JSON.parse(text);
22288
+ if (!isRecord4(value))
22289
+ return "unreadable";
22290
+ if (value.permissions === undefined)
22291
+ continue;
22292
+ if (!isRecord4(value.permissions))
22293
+ return "unreadable";
22294
+ for (const [key, bucket] of [
22295
+ ["deny", rules.deny],
22296
+ ["allow", rules.allow]
22297
+ ]) {
22298
+ const list = value.permissions[key];
22299
+ if (list === undefined)
22300
+ continue;
22301
+ if (!Array.isArray(list))
22302
+ return "unreadable";
22303
+ for (const entry of list) {
22304
+ if (typeof entry !== "string")
22305
+ return "unreadable";
22306
+ const match = /^Shell(?:\((.*)\))?$/su.exec(entry.trim());
22307
+ if (match)
22308
+ bucket.push(match[1] ?? "");
22309
+ }
22310
+ }
22311
+ }
22312
+ } catch {
22313
+ return "unreadable";
22314
+ }
22315
+ return rules;
22316
+ }
22317
+ var baseName = (word) => (word.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat)$/u, "");
22318
+ var firstToken = (pattern) => pattern.trim().split(/[:\s]/u)[0] ?? "";
22319
+ function names(pattern, words) {
22320
+ const token = firstToken(pattern);
22321
+ if (!token || token === "*" || token.includes("*"))
22322
+ return true;
22323
+ const wanted = baseName(token);
22324
+ return words.some((word) => baseName(word) === wanted);
22325
+ }
22326
+ var literalPrefix = (pattern) => pattern.trim().replace(/(?::\*| \*|\*)$/u, "");
22327
+ var coversWrapper = (pattern) => {
22328
+ const token = firstToken(pattern);
22329
+ if (!token || token.includes("*"))
22330
+ return true;
22331
+ const words = literalPrefix(pattern).split(/\s+/u);
22332
+ if (baseName(words[0]) !== "workit")
22333
+ return false;
22334
+ return ["check", "--heavy"].slice(0, words.length - 1).every((word, at) => {
22335
+ const given = words[at + 1];
22336
+ return given === word || word.startsWith(given);
22337
+ });
22338
+ };
22339
+ var coversCommand = (pattern, command) => {
22340
+ const prefix = literalPrefix(pattern);
22341
+ if (!prefix)
22342
+ return true;
22343
+ return !prefix.includes("*") && command.trim().startsWith(prefix);
22344
+ };
22345
+ function wrapAllowed(read, words, command, options = {}) {
22346
+ if (read === "unreadable")
22347
+ return { ok: false, why: "named" };
22348
+ if (read.deny.some((pattern) => names(pattern, words)))
22349
+ return { ok: false, why: "named" };
22350
+ if (!options.bypass && read.allow.some(coversWrapper) && !read.allow.some((pattern) => coversCommand(pattern, command)))
22351
+ return { ok: false, why: "allowlisted" };
22352
+ return { ok: true };
22353
+ }
22354
+
22355
+ // packages/workit-core/src/core/heavy.ts
22356
+ import fs20 from "node:fs";
21908
22357
  import os7 from "node:os";
21909
22358
  import path27 from "node:path";
22359
+ function stateDir(env, platform = process.platform) {
22360
+ if (env.WORKFLOW_TOOLKIT_STATE)
22361
+ return env.WORKFLOW_TOOLKIT_STATE;
22362
+ if (env.XDG_STATE_HOME)
22363
+ return path27.join(env.XDG_STATE_HOME, "workit");
22364
+ const home = env.HOME || os7.homedir();
22365
+ if (platform === "darwin")
22366
+ return path27.join(home, "Library", "Application Support", "workit");
22367
+ if (platform === "win32")
22368
+ return path27.join(env.LOCALAPPDATA ?? path27.join(home, "AppData", "Local"), "workit");
22369
+ return path27.join(home, ".local", "state", "workit");
22370
+ }
22371
+ var HEAVY_COMMANDS_FILE = "heavy-commands.json";
22372
+ var heavyCommandsFile = (env) => path27.join(stateDir(env), HEAVY_COMMANDS_FILE);
22373
+ var HEAVY_SCRIPT = /^(?:test|tests|t|tst|unit|integration|e2e|coverage|cov|lint|eslint|typecheck|type-check|types|tsc|check|verify|validate|build|compile|bundle|ci|sonar|cypress|playwright|jest|vitest|mocha|bench|benchmark|all)(?:$|[:_.-])/iu;
22374
+ var INFO_FLAGS = new Set(["--version", "-v", "-V", "--help", "-h", "help", "version"]);
22375
+ var WATCH_FLAG = /^(?:--watch(?:All)?(?:=.*)?|-w|--serve)$/u;
22376
+ var WATCH_SCRIPT = /(?:^|[:_.-])(?:watch|dev|serve|start|storybook)(?:$|[:_.-])/iu;
22377
+ var sub = (...names) => new Set(names);
22378
+ var TOOLS = {
22379
+ jest: "any",
22380
+ vitest: "any",
22381
+ mocha: "any",
22382
+ ava: "any",
22383
+ tap: "any",
22384
+ karma: "any",
22385
+ jasmine: "any",
22386
+ nyc: "any",
22387
+ c8: "any",
22388
+ tsc: "any",
22389
+ "vue-tsc": "any",
22390
+ "svelte-check": "any",
22391
+ eslint: "any",
22392
+ stylelint: "any",
22393
+ tslint: "any",
22394
+ webpack: "any",
22395
+ "webpack-cli": "any",
22396
+ rollup: "any",
22397
+ parcel: "any",
22398
+ tsup: "any",
22399
+ vite: sub("build", "optimize"),
22400
+ next: sub("build", "lint"),
22401
+ nuxt: sub("build", "generate"),
22402
+ nuxi: sub("build", "generate"),
22403
+ astro: sub("build", "check"),
22404
+ ng: sub("build", "b", "test", "t", "e2e", "lint"),
22405
+ "react-scripts": sub("build", "test"),
22406
+ playwright: sub("test", "install"),
22407
+ cypress: sub("run"),
22408
+ wdio: "any",
22409
+ testcafe: "any",
22410
+ turbo: "any",
22411
+ lerna: sub("run", "exec", "bootstrap"),
22412
+ "sonar-scanner": "any",
22413
+ sonarless: sub("scan"),
22414
+ "sonarless-mcp": sub("scan"),
22415
+ go: sub("test", "build", "vet", "install", "generate"),
22416
+ "golangci-lint": sub("run"),
22417
+ gotestsum: "any",
22418
+ staticcheck: "any",
22419
+ cargo: sub("build", "b", "test", "t", "check", "c", "clippy", "bench", "nextest", "doc", "install", "tarpaulin", "llvm-cov"),
22420
+ mvn: "any",
22421
+ mvnw: "any",
22422
+ gradle: "any",
22423
+ gradlew: "any",
22424
+ ant: "any",
22425
+ sbt: "any",
22426
+ bazel: sub("build", "test", "run", "coverage"),
22427
+ bazelisk: sub("build", "test", "run", "coverage"),
22428
+ buck2: sub("build", "test", "run"),
22429
+ dotnet: sub("build", "test", "publish", "restore", "pack", "msbuild"),
22430
+ msbuild: "any",
22431
+ pytest: "any",
22432
+ "py.test": "any",
22433
+ tox: "any",
22434
+ nox: "any",
22435
+ mypy: "any",
22436
+ pyright: "any",
22437
+ pylint: "any",
22438
+ coverage: sub("run"),
22439
+ maturin: sub("build", "develop"),
22440
+ pip: sub("install", "wheel"),
22441
+ pip3: sub("install", "wheel"),
22442
+ rspec: "any",
22443
+ rails: sub("test", "assets:precompile"),
22444
+ phpunit: "any",
22445
+ pest: "any",
22446
+ phpstan: "any",
22447
+ psalm: "any",
22448
+ composer: sub("install", "update", "require", "test"),
22449
+ mix: sub("test", "compile", "deps.get", "dialyzer", "credo"),
22450
+ swift: sub("build", "test"),
22451
+ xcodebuild: "any",
22452
+ flutter: sub("build", "test"),
22453
+ dart: sub("test", "compile"),
22454
+ pod: sub("install"),
22455
+ cmake: sub("--build"),
22456
+ ninja: "any",
22457
+ meson: sub("compile", "test"),
22458
+ ctest: "any",
22459
+ scons: "any",
22460
+ "docker-compose": sub("build"),
22461
+ buildah: sub("bud", "build"),
22462
+ earthly: "any"
22463
+ };
22464
+ var PM_VALUES = new Set([
22465
+ "--prefix",
22466
+ "-w",
22467
+ "--workspace",
22468
+ "-C",
22469
+ "--dir",
22470
+ "--filter",
22471
+ "-F",
22472
+ "--cwd",
22473
+ "-p",
22474
+ "--package",
22475
+ "--cache",
22476
+ "--registry"
22477
+ ]);
22478
+ function positional2(words, from, values = PM_VALUES) {
22479
+ let index = from;
22480
+ while (index < words.length && words[index].startsWith("-")) {
22481
+ if (words[index] === "--")
22482
+ return index + 1;
22483
+ index += values.has(words[index]) ? 2 : 1;
22484
+ }
22485
+ return index;
22486
+ }
22487
+ var baseName2 = (word) => (word?.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat|ps1)$/u, "");
22488
+ var WORKIT = /^(?:workit|@brainervirus\/workit-cli(?:@.*)?)$/u;
22489
+ var PYTHONS = /^(?:python[\d.]*|py)$/u;
22490
+ var NPM_INSTALL = new Set([
22491
+ "ci",
22492
+ "clean-install",
22493
+ "cit",
22494
+ "install-test",
22495
+ "it",
22496
+ "install",
22497
+ "i",
22498
+ "in",
22499
+ "add",
22500
+ "rebuild",
22501
+ "update",
22502
+ "up"
22503
+ ]);
22504
+ var NPM_RUN = new Set(["run", "run-script", "rum", "urn"]);
22505
+ var NPM_TEST = new Set(["test", "t", "tst"]);
22506
+ var EXEC = new Set(["exec", "x", "dlx"]);
22507
+ function builtIn(words, depth = 0) {
22508
+ if (depth > 6 || !words.length)
22509
+ return null;
22510
+ const name = baseName2(words[0]);
22511
+ const label = () => words.slice(0, 3).join(" ");
22512
+ const inner = (from) => builtIn(words.slice(from), depth + 1);
22513
+ if (WORKIT.test(name))
22514
+ return null;
22515
+ if (INFO_FLAGS.has(words[1] ?? "") && words.length === 2)
22516
+ return null;
22517
+ if (words.some((word) => word === "--watch" || word.startsWith("--watch=") || word === "--watchAll"))
22518
+ return null;
22519
+ if (name === "npx" || name === "bunx" || name === "pnpx")
22520
+ return inner(positional2(words, 1));
22521
+ if (name === "corepack")
22522
+ return inner(1);
22523
+ if (name === "npm" || name === "pnpm" || name === "yarn" || name === "bun" || name === "deno") {
22524
+ const at = positional2(words, 1);
22525
+ const command = words[at];
22526
+ if (command === undefined)
22527
+ return name === "yarn" ? "yarn (install)" : null;
22528
+ if (name === "deno") {
22529
+ if (["test", "bench", "check", "compile", "lint"].includes(command))
22530
+ return label();
22531
+ if (command === "task" && HEAVY_SCRIPT.test(words[at + 1] ?? ""))
22532
+ return label();
22533
+ return null;
22534
+ }
22535
+ if (NPM_INSTALL.has(command) || NPM_TEST.has(command))
22536
+ return label();
22537
+ if (name === "bun" && command === "build")
22538
+ return label();
22539
+ if (EXEC.has(command))
22540
+ return inner(positional2(words, at + 1));
22541
+ if (name === "yarn" && command === "workspace")
22542
+ return inner(at + 2);
22543
+ if (name === "yarn" && command === "workspaces")
22544
+ return words.slice(at + 1).some((word) => HEAVY_SCRIPT.test(word)) ? label() : null;
22545
+ if (NPM_RUN.has(command)) {
22546
+ const script = words[positional2(words, at + 1)];
22547
+ if (script === undefined || WATCH_SCRIPT.test(script))
22548
+ return null;
22549
+ if (HEAVY_SCRIPT.test(script))
22550
+ return label();
22551
+ return name === "bun" ? inner(positional2(words, at + 1)) : null;
22552
+ }
22553
+ if (name === "npm")
22554
+ return null;
22555
+ if (WATCH_SCRIPT.test(command))
22556
+ return null;
22557
+ if (HEAVY_SCRIPT.test(command) && !/\.[cm]?[jt]sx?$/u.test(command))
22558
+ return label();
22559
+ return inner(at);
22560
+ }
22561
+ if (PYTHONS.test(name)) {
22562
+ const at = words.indexOf("-m");
22563
+ return at > 0 && words[at + 1] ? inner(at + 1) : null;
22564
+ }
22565
+ if (["uv", "poetry", "pdm", "pipenv", "hatch", "rye"].includes(name)) {
22566
+ const at = positional2(words, 1);
22567
+ const command = words[at];
22568
+ if (command === "run")
22569
+ return inner(positional2(words, at + 1));
22570
+ if (command === "pip")
22571
+ return inner(at);
22572
+ return ["sync", "install", "build", "test", "add"].includes(command ?? "") ? label() : null;
22573
+ }
22574
+ if (name === "bundle") {
22575
+ const command = words[positional2(words, 1)];
22576
+ if (command === "exec")
22577
+ return inner(positional2(words, 2));
22578
+ return command === "install" || command === undefined ? label() : null;
22579
+ }
22580
+ if (name === "make" || name === "gmake") {
22581
+ const targets = words.slice(1).filter((word) => !word.startsWith("-") && !word.includes("="));
22582
+ if (targets.some((target) => WATCH_SCRIPT.test(target)))
22583
+ return null;
22584
+ return !targets.length || targets.some((target) => HEAVY_SCRIPT.test(target)) ? label() : null;
22585
+ }
22586
+ if (name === "rake") {
22587
+ const targets = words.slice(1).filter((word) => !word.startsWith("-") && !word.includes("="));
22588
+ return targets.some((target) => HEAVY_SCRIPT.test(target) || /^spec(?:$|:)/u.test(target)) ? label() : null;
22589
+ }
22590
+ if (name === "nx") {
22591
+ const at = positional2(words, 1);
22592
+ const command = words[at] ?? "";
22593
+ if (["run-many", "affected"].includes(command))
22594
+ return words.some((word) => /^(?:-t|--targets?)(?:=|$)/u.test(word)) && !words.some((word) => WATCH_SCRIPT.test(word.replace(/^--?targets?=/u, ""))) ? label() : null;
22595
+ if (command === "run") {
22596
+ const target = (words[at + 1] ?? "").split(":")[1] ?? "";
22597
+ return HEAVY_SCRIPT.test(target) && !WATCH_SCRIPT.test(target) ? label() : null;
22598
+ }
22599
+ return HEAVY_SCRIPT.test(command) && !WATCH_SCRIPT.test(command) ? label() : null;
22600
+ }
22601
+ if (name === "docker" || name === "podman" || name === "nerdctl") {
22602
+ const at = positional2(words, 1, new Set(["-H", "--host", "--context", "-c", "--config"]));
22603
+ const command = words[at];
22604
+ if (command === "build")
22605
+ return label();
22606
+ if (command === "buildx" || command === "image" || command === "builder")
22607
+ return words[positional2(words, at + 1)] === "build" ? label() : null;
22608
+ if (command === "compose") {
22609
+ const at2 = positional2(words, at + 1, new Set(["-f", "--file", "-p"]));
22610
+ const action = words[at2];
22611
+ if (action === "build")
22612
+ return label();
22613
+ return action === "up" && words.slice(at2 + 1).includes("--build") ? label() : null;
22614
+ }
22615
+ return null;
22616
+ }
22617
+ const rule = TOOLS[name];
22618
+ if (!rule)
22619
+ return null;
22620
+ if (words.slice(1).some((word) => WATCH_FLAG.test(word) || word === "watch" || word === "dev"))
22621
+ return null;
22622
+ if (rule === "any")
22623
+ return label();
22624
+ const command = words.slice(1).find((word) => !word.startsWith("-") && !word.startsWith("+") || rule.has(word));
22625
+ return command !== undefined && rule.has(command) ? label() : null;
22626
+ }
22627
+ var globWord = (word) => new RegExp(`^${word.replace(/[.+?^${}()|[\]\\]/gu, "\\$&").replaceAll("*", ".*")}$`, "u");
22628
+ function parsePattern(pattern) {
22629
+ const words = pattern.trim().split(/\s+/u).filter(Boolean);
22630
+ return words.length ? words.map(globWord) : null;
22631
+ }
22632
+ var matches = (matchers, words) => matchers.length <= words.length && matchers.every((matcher, index) => index === 0 ? matcher.test(words[0]) || matcher.test(baseName2(words[0])) : matcher.test(words[index]));
22633
+ var readJson = (file) => {
22634
+ try {
22635
+ return JSON.parse(fs20.readFileSync(file, "utf8"));
22636
+ } catch {
22637
+ return;
22638
+ }
22639
+ };
22640
+ var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
22641
+ var stringList = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
22642
+ function machinePatterns(env) {
22643
+ const value = readJson(heavyCommandsFile(env));
22644
+ return isRecord5(value) ? stringList(value.patterns) : [];
22645
+ }
22646
+ function repoPatterns(cwd) {
22647
+ return repoHeavyCommands(gitTop(cwd) ?? path27.resolve(cwd));
22648
+ }
22649
+ function heavyLines(lines, options) {
22650
+ const found = [];
22651
+ let patterns = null;
22652
+ for (const line of lines) {
22653
+ const reason = builtIn(line);
22654
+ if (reason) {
22655
+ found.push({ line, source: "catalog", reason });
22656
+ continue;
22657
+ }
22658
+ patterns ??= (options.patterns ?? [
22659
+ ...machinePatterns(options.env ?? process.env).map((pattern) => ({
22660
+ pattern,
22661
+ from: "machine"
22662
+ })),
22663
+ ...repoPatterns(options.cwd).map((pattern) => ({ pattern, from: "repo" }))
22664
+ ]).flatMap((entry) => {
22665
+ const matchers = parsePattern(entry.pattern);
22666
+ return matchers ? [{ ...entry, matchers }] : [];
22667
+ });
22668
+ const hit = patterns.find((entry) => matches(entry.matchers, line));
22669
+ if (hit)
22670
+ found.push({
22671
+ line,
22672
+ source: { pattern: hit.pattern, from: hit.from },
22673
+ reason: `${hit.from} pattern "${hit.pattern}"`
22674
+ });
22675
+ }
22676
+ return found;
22677
+ }
22678
+ var linesOf = (command, dialect) => {
22679
+ try {
22680
+ return commandLines(command, dialect);
22681
+ } catch {
22682
+ return null;
22683
+ }
22684
+ };
22685
+ var quoteWord = (word) => /^[\w@%+=:,./-]+$/u.test(word) ? word : `'${word.replaceAll("'", `'\\''`)}'`;
22686
+ var leaseCommand = (words) => `workit check --heavy -- ${words.map(quoteWord).join(" ")}`;
22687
+ function heavyPlan(command, options) {
22688
+ const dialect = options.dialect ?? "posix";
22689
+ if (dialect !== "posix" || !gitTop(options.cwd))
22690
+ return { kind: "none" };
22691
+ const lines = linesOf(command, dialect);
22692
+ if (!lines)
22693
+ return { kind: "none" };
22694
+ const heavy = heavyLines(lines, options);
22695
+ if (!heavy.length)
22696
+ return { kind: "none" };
22697
+ const simple = simpleCommand(command, dialect);
22698
+ const single = simple && !WORKIT.test(baseName2(simple[0])) && heavyLines([simple], options)[0];
22699
+ const judged = single ? [{ words: lines.flat(), text: command }] : heavy.map((match) => ({
22700
+ words: match.line,
22701
+ text: match.line.join(" ")
22702
+ }));
22703
+ if (options.rules !== null)
22704
+ for (const { words, text } of judged) {
22705
+ const verdict = wrapAllowed(options.rules, words, text, { bypass: options.bypass });
22706
+ if (!verdict.ok)
22707
+ return {
22708
+ kind: "passthrough",
22709
+ note: `workit: \`${command.trim()}\` is heavy but runs outside the heavy-check lease because a host permission rule names it (or \`workit\` is allowlisted); the host decides.`
22710
+ };
22711
+ }
22712
+ if (single) {
22713
+ const rewrite = leaseRewrite(command);
22714
+ return rewrite ? { kind: "rewrite", command: rewrite, reason: single.reason } : { kind: "none" };
22715
+ }
22716
+ const parts = heavy.map((match) => leaseCommand(match.line));
22717
+ return {
22718
+ kind: "split",
22719
+ reason: `heavy_command: \`${command.trim()}\` chains heavy work (${heavy.map((match) => match.reason).join("; ")}) with other commands, and a chain cannot run under the machine-wide heavy-check lease. Run each heavy part alone as \`workit check --heavy -- <segment>\` (here: ${parts.map((part) => `\`${part}\``).join("; ")}); run the other parts as their own commands.`,
22720
+ unblock: parts[0] ?? null
22721
+ };
22722
+ }
22723
+ function workitOnPath2(env = process.env) {
22724
+ const names = process.platform === "win32" ? ["workit.exe", "workit.cmd", "workit"] : ["workit"];
22725
+ for (const dir of (env.PATH ?? "").split(path27.delimiter))
22726
+ for (const name of names) {
22727
+ if (!dir)
22728
+ continue;
22729
+ try {
22730
+ fs20.accessSync(path27.join(dir, name), fs20.constants.X_OK);
22731
+ return true;
22732
+ } catch {}
22733
+ }
22734
+ return false;
22735
+ }
22736
+ var ASSIGNMENTS = /^\s*((?:[A-Za-z_]\w*=(?:'[^']*'|"(?:[^"\\]|\\.)*"|[^\s'"])*\s+)*)/u;
22737
+ function leaseRewrite(command) {
22738
+ const text = command.trim();
22739
+ const prefix = ASSIGNMENTS.exec(text)?.[1] ?? "";
22740
+ const rest = text.slice(prefix.length).trim();
22741
+ if (!rest)
22742
+ return null;
22743
+ return `${prefix}workit check --heavy -- ${rest}`;
22744
+ }
22745
+
22746
+ // packages/workit-core/src/core/heavy-words.ts
22747
+ import fs21 from "node:fs";
22748
+ import os8 from "node:os";
22749
+ import path28 from "node:path";
22750
+ var HEAVY_WORDS = new Set(("npm npx pnpm pnpx yarn bun bunx deno corepack make gmake docker podman nerdctl " + "python python3 py uv poetry pdm pipenv hatch rye bundle rake nx " + "jest vitest mocha ava tap karma jasmine nyc c8 tsc vue-tsc svelte-check eslint stylelint " + "tslint webpack webpack-cli rollup parcel tsup vite next nuxt nuxi astro ng react-scripts " + "playwright cypress wdio testcafe turbo lerna sonar-scanner sonarless sonarless-mcp go " + "golangci-lint gotestsum staticcheck cargo mvn mvnw gradle gradlew ant sbt bazel bazelisk " + "buck2 dotnet msbuild pytest py.test tox nox mypy pyright pylint coverage maturin pip pip3 " + "rspec rails phpunit pest phpstan psalm composer mix swift xcodebuild flutter dart pod cmake " + "ninja meson ctest scons docker-compose buildah earthly").split(" "));
22751
+ var wordsOf = (command) => command.split(/[\s;&|()<>`'"]+/u).map((word) => (word.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat)$/u, "")).filter(Boolean);
22752
+ var stateDir2 = (env) => {
22753
+ if (env.WORKFLOW_TOOLKIT_STATE)
22754
+ return env.WORKFLOW_TOOLKIT_STATE;
22755
+ if (env.XDG_STATE_HOME)
22756
+ return path28.join(env.XDG_STATE_HOME, "workit");
22757
+ const home = env.HOME || os8.homedir();
22758
+ if (process.platform === "darwin")
22759
+ return path28.join(home, "Library", "Application Support", "workit");
22760
+ if (process.platform === "win32")
22761
+ return path28.join(env.LOCALAPPDATA ?? path28.join(home, "AppData", "Local"), "workit");
22762
+ return path28.join(home, ".local", "state", "workit");
22763
+ };
22764
+ var readJson2 = (file) => {
22765
+ try {
22766
+ return JSON.parse(fs21.readFileSync(file, "utf8"));
22767
+ } catch {
22768
+ return;
22769
+ }
22770
+ };
22771
+ var patternWords = (cwd, env) => {
22772
+ const patterns = [];
22773
+ const machine = readJson2(path28.join(stateDir2(env), "heavy-commands.json"));
22774
+ if (Array.isArray(machine?.patterns))
22775
+ patterns.push(...machine.patterns);
22776
+ for (let dir = path28.resolve(cwd);; dir = path28.dirname(dir)) {
22777
+ if (fs21.existsSync(path28.join(dir, ".git"))) {
22778
+ patterns.push(...repoHeavyCommands(dir));
22779
+ break;
22780
+ }
22781
+ if (path28.dirname(dir) === dir)
22782
+ break;
22783
+ }
22784
+ const words = new Set;
22785
+ for (const pattern of patterns) {
22786
+ if (typeof pattern !== "string")
22787
+ continue;
22788
+ const first = wordsOf(pattern)[0];
22789
+ if (!first || first.includes("*"))
22790
+ return null;
22791
+ words.add(first);
22792
+ }
22793
+ return words;
22794
+ };
22795
+ function maybeHeavy(command, cwd, env = process.env) {
22796
+ try {
22797
+ const words = wordsOf(command);
22798
+ if (words.some((word) => HEAVY_WORDS.has(word) || /^python[\d.]*$/u.test(word)))
22799
+ return true;
22800
+ const extra = patternWords(cwd, env);
22801
+ return extra === null || words.some((word) => extra.has(word));
22802
+ } catch {
22803
+ return true;
22804
+ }
22805
+ }
22806
+
22807
+ // packages/workit-core/src/hooks/heavy-gate.ts
22808
+ var rulesFor = (host, cwd, env) => {
22809
+ switch (host) {
22810
+ case "claude_code":
22811
+ return claudeRules(cwd, env);
22812
+ case "opencode":
22813
+ return opencodeRules(cwd, env);
22814
+ case "codex_cli":
22815
+ case "codex_desktop":
22816
+ return codexRules(cwd, env);
22817
+ case "cursor":
22818
+ return cursorRules(cwd, env);
22819
+ case "pi":
22820
+ return null;
22821
+ }
22822
+ };
22823
+ function heavyGate(cwd, command, options) {
22824
+ const env = options.env ?? process.env;
22825
+ try {
22826
+ if (env.WORKIT_HEAVY_GATE === "0" || !maybeHeavy(command, cwd, env) || !workitOnPath2(env))
22827
+ return { kind: "none" };
22828
+ return heavyPlan(command, {
22829
+ cwd,
22830
+ env,
22831
+ dialect: options.dialect ?? "posix",
22832
+ rules: rulesFor(options.host, cwd, env),
22833
+ bypass: options.permissionMode === "bypassPermissions"
22834
+ });
22835
+ } catch {
22836
+ return { kind: "none" };
22837
+ }
22838
+ }
22839
+ var rewriteNote = (plan) => `workit: this heavy command (${plan.reason}) runs under the machine-wide heavy-check lease as \`${plan.command}\`: it waits while another heavy check holds the lease (\`workit lock status\` shows who). Run heavy commands that way yourself; \`workit help lock\` explains the lease.`;
22840
+ var heavyDenial = (original, plan) => ({
22841
+ kind: "deny",
22842
+ reason: `heavy_command: \`${original.trim()}\` is heavy (${plan.reason}) and must run under the machine-wide heavy-check lease, so parallel agents cannot exhaust this machine's memory and CPU. Run exactly: ${plan.command}`,
22843
+ unblock: plan.command
22844
+ });
22845
+ var splitDenial = (plan) => ({
22846
+ kind: "deny",
22847
+ reason: plan.reason,
22848
+ unblock: plan.unblock
22849
+ });
22850
+ // packages/workit-core/src/hooks/write-gate.ts
22851
+ import { realpathSync as realpathSync6 } from "node:fs";
22852
+ import os10 from "node:os";
22853
+ import path30 from "node:path";
21910
22854
 
21911
22855
  // packages/workit-core/src/core/live-session.ts
21912
22856
  import { spawnSync as spawnSync17 } from "node:child_process";
21913
22857
  import { createHash as createHash15 } from "node:crypto";
21914
- import fs19 from "node:fs";
21915
- import os6 from "node:os";
21916
- import path26 from "node:path";
22858
+ import fs22 from "node:fs";
22859
+ import os9 from "node:os";
22860
+ import path29 from "node:path";
21917
22861
  var LIVE_SESSION_TTL_MS = 15 * 60000;
21918
22862
  var HEARTBEAT_EVERY_MS = 30000;
21919
22863
  var liveProbe = {
@@ -21934,9 +22878,9 @@ var processAlive = (pid) => {
21934
22878
  var processInfo = (pid) => {
21935
22879
  try {
21936
22880
  if (liveProbe.platform === "linux") {
21937
- const stat = fs19.readFileSync(`/proc/${pid}/stat`, "utf8");
22881
+ const stat = fs22.readFileSync(`/proc/${pid}/stat`, "utf8");
21938
22882
  const ppid = Number(stat.slice(stat.lastIndexOf(")") + 2).split(" ")[1]);
21939
- const command = fs19.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ");
22883
+ const command = fs22.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ");
21940
22884
  return Number.isInteger(ppid) ? { ppid, command: command.trim() } : null;
21941
22885
  }
21942
22886
  if (liveProbe.platform === "win32")
@@ -21987,35 +22931,35 @@ var liveOwner = (input, transport, env = process.env) => {
21987
22931
  };
21988
22932
  };
21989
22933
  var checkoutOf = (target) => {
21990
- let dir = path26.resolve(target);
21991
- while (!fs19.existsSync(dir) && path26.dirname(dir) !== dir)
21992
- dir = path26.dirname(dir);
21993
- if (fs19.existsSync(dir) && !fs19.statSync(dir).isDirectory())
21994
- dir = path26.dirname(dir);
21995
- const parts = dir.split(path26.sep);
22934
+ let dir = path29.resolve(target);
22935
+ while (!fs22.existsSync(dir) && path29.dirname(dir) !== dir)
22936
+ dir = path29.dirname(dir);
22937
+ if (fs22.existsSync(dir) && !fs22.statSync(dir).isDirectory())
22938
+ dir = path29.dirname(dir);
22939
+ const parts = dir.split(path29.sep);
21996
22940
  const dotGit = parts.lastIndexOf(".git");
21997
22941
  if (dotGit > 0)
21998
- dir = parts.slice(0, dotGit).join(path26.sep) || path26.sep;
22942
+ dir = parts.slice(0, dotGit).join(path29.sep) || path29.sep;
21999
22943
  const location = resolveStore(dir);
22000
22944
  if (location instanceof Error || !location.shared || !location.top)
22001
22945
  return null;
22002
22946
  let top = location.top;
22003
22947
  try {
22004
- top = fs19.realpathSync.native(top);
22948
+ top = fs22.realpathSync.native(top);
22005
22949
  } catch {}
22006
22950
  return top;
22007
22951
  };
22008
- var markerFile2 = (top) => path26.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
22952
+ var markerFile2 = (top) => path29.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
22009
22953
  var checkoutKey = (top, platform = process.platform) => {
22010
- let key = path26.resolve(top);
22954
+ let key = path29.resolve(top);
22011
22955
  try {
22012
- key = fs19.realpathSync.native(key);
22956
+ key = fs22.realpathSync.native(key);
22013
22957
  } catch {}
22014
22958
  return platform === "win32" ? key.replaceAll("/", "\\").toLowerCase() : key;
22015
22959
  };
22016
22960
  var readMarker = (file) => {
22017
22961
  try {
22018
- const value = JSON.parse(fs19.readFileSync(file, "utf8"));
22962
+ const value = JSON.parse(fs22.readFileSync(file, "utf8"));
22019
22963
  if (value?.version !== 1 || typeof value.session !== "string" || typeof value.host !== "string" || typeof value.heartbeatAt !== "string" || Number.isNaN(Date.parse(value.heartbeatAt)))
22020
22964
  return null;
22021
22965
  return {
@@ -22038,7 +22982,7 @@ var isLive = (marker, deps) => {
22038
22982
  const age = ageOf2(marker, deps.now ?? new Date);
22039
22983
  if (age > (deps.ttlMs ?? LIVE_SESSION_TTL_MS))
22040
22984
  return false;
22041
- if (marker.machine !== (deps.machine ?? os6.hostname()) || marker.pid === null)
22985
+ if (marker.machine !== (deps.machine ?? os9.hostname()) || marker.pid === null)
22042
22986
  return true;
22043
22987
  if (!(deps.alive ?? processAlive)(marker.pid))
22044
22988
  return false;
@@ -22067,7 +23011,7 @@ var liveConflict = (target, owner, deps = {}) => {
22067
23011
  const marker = readMarker(file);
22068
23012
  if (!marker || sameLead(marker, owner) || !isLive(marker, deps))
22069
23013
  return null;
22070
- if (sameOwner(marker, owner, deps.machine ?? os6.hostname()))
23014
+ if (sameOwner(marker, owner, deps.machine ?? os9.hostname()))
22071
23015
  return null;
22072
23016
  return { marker, file, ageMs: ageOf2(marker, deps.now ?? new Date), checkout };
22073
23017
  } catch {
@@ -22081,7 +23025,7 @@ var claimLiveSession = (target, owner, deps = {}) => {
22081
23025
  return;
22082
23026
  const file = markerFile2(checkout);
22083
23027
  const now = deps.now ?? new Date;
22084
- const machine = deps.machine ?? os6.hostname();
23028
+ const machine = deps.machine ?? os9.hostname();
22085
23029
  const current = readMarker(file);
22086
23030
  if (current) {
22087
23031
  const age = ageOf2(current, now);
@@ -22102,10 +23046,10 @@ var claimLiveSession = (target, owner, deps = {}) => {
22102
23046
  checkout,
22103
23047
  heartbeatAt: now.toISOString()
22104
23048
  };
22105
- fs19.mkdirSync(path26.dirname(file), { recursive: true });
23049
+ fs22.mkdirSync(path29.dirname(file), { recursive: true });
22106
23050
  const temp = `${file}.${process.pid}.tmp`;
22107
- fs19.writeFileSync(temp, JSON.stringify(marker));
22108
- fs19.renameSync(temp, file);
23051
+ fs22.writeFileSync(temp, JSON.stringify(marker));
23052
+ fs22.renameSync(temp, file);
22109
23053
  } catch {}
22110
23054
  };
22111
23055
 
@@ -22114,27 +23058,42 @@ var NONE3 = { kind: "none" };
22114
23058
  var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
22115
23059
  var canonical2 = (file) => {
22116
23060
  let rest = "";
22117
- for (let dir = path27.resolve(file);; dir = path27.dirname(dir)) {
23061
+ for (let dir = path30.resolve(file);; dir = path30.dirname(dir)) {
22118
23062
  try {
22119
- return path27.join(realpathSync6(dir), rest);
23063
+ return path30.join(realpathSync6(dir), rest);
22120
23064
  } catch {
22121
- if (path27.dirname(dir) === dir)
22122
- return path27.resolve(file);
22123
- rest = rest ? path27.join(path27.basename(dir), rest) : path27.basename(dir);
23065
+ if (path30.dirname(dir) === dir)
23066
+ return path30.resolve(file);
23067
+ rest = rest ? path30.join(path30.basename(dir), rest) : path30.basename(dir);
22124
23068
  }
22125
23069
  }
22126
23070
  };
22127
23071
  var exempt = (cwd, root, file, plans) => {
22128
23072
  const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
22129
- const relative = path27.relative(fold(canonical2(root)), fold(canonical2(path27.resolve(cwd, file))));
22130
- if (relative.startsWith("..") || path27.isAbsolute(relative))
23073
+ const relative = path30.relative(fold(canonical2(root)), fold(canonical2(path30.resolve(cwd, file))));
23074
+ if (relative.startsWith("..") || path30.isAbsolute(relative))
22131
23075
  return true;
22132
- const posix = relative.split(path27.sep).join("/");
23076
+ const posix = relative.split(path30.sep).join("/");
22133
23077
  const segments = posix.split("/");
22134
23078
  return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
22135
23079
  };
22136
23080
  var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
22137
23081
  var operands = (words) => words.filter((item) => !item.startsWith("-"));
23082
+ var SCRIPT_SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
23083
+ var wrappedScript = (input) => {
23084
+ let words = input;
23085
+ while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0])))
23086
+ words = words.slice(1);
23087
+ const tail = workitCheckTail(words);
23088
+ if (!tail)
23089
+ return null;
23090
+ if ("script" in tail)
23091
+ return tail.script;
23092
+ const [shell, flag, script] = tail.argv;
23093
+ if (SCRIPT_SHELLS.has((shell.split(/[\\/]/).at(-1) ?? "").toLowerCase()) && /^-[a-z]*c$/.test(flag ?? ""))
23094
+ return script ?? null;
23095
+ return tail.argv.map(quoteWord2).join(" ");
23096
+ };
22138
23097
  var editsOf = (input) => {
22139
23098
  let words = input;
22140
23099
  while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0])))
@@ -22211,10 +23170,18 @@ var editsOf = (input) => {
22211
23170
  return [];
22212
23171
  }
22213
23172
  };
22214
- var shellWrites = (command) => {
23173
+ var shellWrites = (command, depth = 0) => {
22215
23174
  const targets = [];
22216
23175
  let unknown = false;
22217
23176
  for (const segment of segmentsOf(command)) {
23177
+ const inner = depth < 4 ? wrappedScript(segment.words) : null;
23178
+ if (inner !== null) {
23179
+ const nested = shellWrites(inner, depth + 1);
23180
+ if (nested.writes && nested.targets === null)
23181
+ unknown = true;
23182
+ else if (nested.targets)
23183
+ targets.push(...nested.targets);
23184
+ }
22218
23185
  targets.push(...segment.redirects.filter((item) => !/^\/dev\/(?:null|stdout|stderr|tty)$/.test(item)));
22219
23186
  const edits = editsOf(segment.words);
22220
23187
  if (edits === null)
@@ -22227,7 +23194,7 @@ var shellWrites = (command) => {
22227
23194
  return { writes: true, targets: unknown ? null : targets };
22228
23195
  };
22229
23196
  var cache2 = new Map;
22230
- var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path27.resolve(root, file)) ?? "-").join("\x00");
23197
+ var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path30.resolve(root, file)) ?? "-").join("\x00");
22231
23198
  var gateFor = (cwd) => {
22232
23199
  const store = new TaskStore(cwd);
22233
23200
  const listed = store.listTaskIndex();
@@ -22306,7 +23273,7 @@ var gitMutationDirs = (cwd, command, dialect) => {
22306
23273
  return [];
22307
23274
  if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
22308
23275
  return [];
22309
- return [path27.resolve(cwd, invocation.dir ?? ".")];
23276
+ return [path30.resolve(cwd, invocation.dir ?? ".")];
22310
23277
  });
22311
23278
  };
22312
23279
  var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
@@ -22316,15 +23283,15 @@ var cdInto = (dir, words) => {
22316
23283
  const target = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
22317
23284
  if (dir === undefined || target === "-" || target && /^~|[$`*?]/.test(target))
22318
23285
  return;
22319
- return target === undefined ? os7.homedir() : path27.resolve(dir, target);
23286
+ return target === undefined ? os10.homedir() : path30.resolve(dir, target);
22320
23287
  };
22321
- var quoteWord = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
22322
- var segmentTargets = (cwd, command, dialect) => {
23288
+ var quoteWord2 = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
23289
+ var segmentTargets = (cwd, command, dialect, depth = 0) => {
22323
23290
  const targets = [];
22324
23291
  let dir = cwd;
22325
23292
  let stack = [];
22326
23293
  const groups = [];
22327
- const at = (file) => path27.isAbsolute(file) ? file : dir === undefined ? null : path27.resolve(dir, file);
23294
+ const at = (file) => path30.isAbsolute(file) ? file : dir === undefined ? null : path30.resolve(dir, file);
22328
23295
  for (const segment of segmentsOf(command, dialect)) {
22329
23296
  if (segment.group === "open") {
22330
23297
  groups.push({ dir, stack: [...stack] });
@@ -22352,6 +23319,9 @@ var segmentTargets = (cwd, command, dialect) => {
22352
23319
  dir = stack.length ? stack.pop() : undefined;
22353
23320
  continue;
22354
23321
  }
23322
+ const inner = depth < 4 ? wrappedScript(words) : null;
23323
+ if (inner !== null && dir !== undefined)
23324
+ targets.push(...segmentTargets(dir, inner, "posix", depth + 1));
22355
23325
  for (const file of segment.redirects)
22356
23326
  if (!/^\/dev\/(?:null|stdout|stderr|tty)$/.test(file)) {
22357
23327
  const resolved = at(file);
@@ -22361,15 +23331,15 @@ var segmentTargets = (cwd, command, dialect) => {
22361
23331
  const edits = editsOf(segment.words);
22362
23332
  if (edits === null) {
22363
23333
  if (dir !== undefined)
22364
- targets.push(path27.join(dir, DIR_TARGET));
23334
+ targets.push(path30.join(dir, DIR_TARGET));
22365
23335
  } else
22366
23336
  for (const file of edits) {
22367
23337
  const resolved = at(file);
22368
23338
  if (resolved)
22369
23339
  targets.push(resolved);
22370
23340
  }
22371
- if (dir !== undefined && workitInvocations(segment.words.map(quoteWord).join(" "), "posix").some((args) => args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), "")))
22372
- targets.push(path27.join(dir, DIR_TARGET));
23341
+ if (dir !== undefined && workitInvocations(segment.words.map(quoteWord2).join(" "), "posix").some((args) => args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), "")))
23342
+ targets.push(path30.join(dir, DIR_TARGET));
22373
23343
  }
22374
23344
  return targets;
22375
23345
  };
@@ -22404,7 +23374,7 @@ var liveDeny = ({ marker, checkout, ageMs, file }) => {
22404
23374
  var guarded = (targets) => {
22405
23375
  const seen = new Set;
22406
23376
  return targets.filter((target) => {
22407
- const dir = path27.dirname(target);
23377
+ const dir = path30.dirname(target);
22408
23378
  if (seen.has(dir))
22409
23379
  return false;
22410
23380
  seen.add(dir);
@@ -22425,7 +23395,7 @@ var claimAll = (owner, targets, deps) => {
22425
23395
  for (const target of guarded(targets))
22426
23396
  claimLiveSession(target, owner, deps);
22427
23397
  };
22428
- var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path27.resolve(cwd, file)) : [path27.join(cwd, DIR_TARGET)];
23398
+ var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path30.resolve(cwd, file)) : [path30.join(cwd, DIR_TARGET)];
22429
23399
  function writeGate(cwd, paths, owner, deps) {
22430
23400
  if (!owner)
22431
23401
  return beforeWriteGate(cwd, paths);
@@ -22445,7 +23415,7 @@ function shellGate(cwd, command, owner, dialect = "posix", deps) {
22445
23415
  try {
22446
23416
  targets = [
22447
23417
  ...segmentTargets(cwd, command, dialect),
22448
- ...gitMutationDirs(cwd, command, dialect).map((dir) => path27.join(dir, DIR_TARGET))
23418
+ ...gitMutationDirs(cwd, command, dialect).map((dir) => path30.join(dir, DIR_TARGET))
22449
23419
  ];
22450
23420
  } catch {
22451
23421
  targets = [];
@@ -22879,7 +23849,7 @@ ${line}` };
22879
23849
  };
22880
23850
  var shellNudge = (input, command) => withContextLine(rawGitPre(input, command), shipNudge(input, command));
22881
23851
  // packages/workit-core/src/hooks/hosts/fields.ts
22882
- var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
23852
+ var isRecord6 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
22883
23853
  var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
22884
23854
  var SHELLS2 = new Set(["sh", "bash", "zsh", "dash", "pwsh", "powershell", "cmd"]);
22885
23855
  var WRITE_TOOLS = new Set([
@@ -22904,7 +23874,7 @@ var WRITE_TOOLS = new Set([
22904
23874
  var PATH_KEYS = ["file_path", "filePath", "notebook_path", "path", "target_file", "file"];
22905
23875
  var PATCH_HEADER = /^[ \t]*\*\*\* (?:(?:Add|Update|Delete) File|Move to): (.+)$/gm;
22906
23876
  var writePaths = (input) => {
22907
- if (!isRecord4(input))
23877
+ if (!isRecord6(input))
22908
23878
  return [];
22909
23879
  const paths = PATH_KEYS.map((key) => input[key]).filter(nonEmpty2);
22910
23880
  for (const value of Object.values(input))
@@ -22995,10 +23965,32 @@ function handleHook(input, deps) {
22995
23965
  const gate = usable2(descriptor.events["write.pre"].support) ? shellGate(input.cwd, event.command, liveOwner(input, descriptor.transport), event.dialect) : NONE5;
22996
23966
  if (gate.kind === "deny")
22997
23967
  return gate;
23968
+ const plan = heavyGate(input.cwd, event.command, {
23969
+ host: input.host,
23970
+ dialect: event.dialect,
23971
+ permissionMode: input.permissionMode
23972
+ });
23973
+ if (plan.kind === "split" && canDeny)
23974
+ return splitDenial(plan);
23975
+ const rewrites = usable2(descriptor.shellPolicy.rewrite);
23976
+ if (plan.kind === "rewrite" && !rewrites && canDeny)
23977
+ return heavyDenial(event.command, plan);
23978
+ const lease = plan.kind === "rewrite" && rewrites ? plan : null;
23979
+ const aside = plan.kind === "passthrough" ? plan.note : null;
22998
23980
  noteRawCommit(input, event.command, postTool ? callKey(event.toolUseId, event.command) : NEXT_COMMAND);
23981
+ const rewritten = (text) => lease ? {
23982
+ kind: "rewrite",
23983
+ command: lease.command,
23984
+ input: event.toolInput ? { ...event.toolInput, command: lease.command } : null,
23985
+ text
23986
+ } : NONE5;
22999
23987
  if (descriptor.transport !== "hook-process")
23000
- return NONE5;
23001
- return withContextLine(canDeny ? rawGitPre(input, event.command) : NONE5, shipNudge(input, event.command));
23988
+ return rewritten(null);
23989
+ const nudged = withContextLine(withContextLine(canDeny ? rawGitPre(input, event.command) : NONE5, aside), shipNudge(input, event.command));
23990
+ if (!lease)
23991
+ return nudged;
23992
+ return rewritten([rewriteNote(lease), nudged.kind === "context" ? nudged.text : null].filter(Boolean).join(`
23993
+ `));
23002
23994
  }
23003
23995
  case "shell.post":
23004
23996
  if (usable2(descriptor.events["shell.post"].support))
@@ -23068,7 +24060,7 @@ var PI_DESCRIPTOR = {
23068
24060
  "compact.pre": { support: "native", native: "session_before_compact" },
23069
24061
  stop: { support: "native", native: "agent_end" }
23070
24062
  },
23071
- shellPolicy: { deny: "native", channel: "block", failClosed: false },
24063
+ shellPolicy: { deny: "native", channel: "block", failClosed: false, rewrite: "native" },
23072
24064
  context: {
23073
24065
  sessionStart: "native",
23074
24066
  perTurn: "native",
@@ -23211,9 +24203,11 @@ var executeFamily = async (family, input, ctx) => {
23211
24203
  const trust = trustedForMutation(ctx, parsed.data.action);
23212
24204
  if (!trust.ok)
23213
24205
  return output(trust);
23214
- const core = new WorkitCore(new TaskStore(ctx.cwd), piContext(ctx));
24206
+ const context = piContext(ctx);
24207
+ const core = new WorkitCore(new TaskStore(ctx.cwd), context);
23215
24208
  const run = core[family];
23216
- return output(run.call(core, parsed.data));
24209
+ const result = run.call(core, parsed.data);
24210
+ return output(family === "task" && (context.workerId ?? null) !== null ? briefInspectResult(parsed.data, result) : result);
23217
24211
  };
23218
24212
  var registerWorkitTools = (pi, options = {}) => {
23219
24213
  for (const family of OPERATION_FAMILIES)
@@ -23260,6 +24254,8 @@ var enforceToolPolicy = (event, ctx) => {
23260
24254
  descriptor: PI_DESCRIPTOR,
23261
24255
  addendum: null
23262
24256
  });
24257
+ if (decision.kind === "rewrite" && event.toolName === "bash")
24258
+ event.input.command = decision.command;
23263
24259
  return decision.kind === "deny" ? { block: true, reason: decision.reason } : undefined;
23264
24260
  };
23265
24261
  if (event.toolName === "bash") {
@@ -23309,13 +24305,13 @@ var skillPromptNudge = (prompt, ctx) => process.env.WORKIT_PI_WORKER_SESSION ? n
23309
24305
 
23310
24306
  // packages/workit-pi/src/launches.ts
23311
24307
  import { mkdirSync as mkdirSync3, readFileSync as readFileSync6, readdirSync as readdirSync5, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
23312
- import os8 from "node:os";
23313
- import path28 from "node:path";
24308
+ import os11 from "node:os";
24309
+ import path31 from "node:path";
23314
24310
  var launchDir = (root) => {
23315
24311
  const location = resolveStore(root);
23316
- return location instanceof Error ? null : path28.join(location.dir, "pi-launches");
24312
+ return location instanceof Error ? null : path31.join(location.dir, "pi-launches");
23317
24313
  };
23318
- var fileFor = (dir, workerId) => path28.join(dir, `${workerId.replace(/[^A-Za-z0-9-]/g, "_")}.json`);
24314
+ var fileFor = (dir, workerId) => path31.join(dir, `${workerId.replace(/[^A-Za-z0-9-]/g, "_")}.json`);
23319
24315
  var recordLaunch = (root, attempt) => {
23320
24316
  const dir = launchDir(root);
23321
24317
  if (!dir)
@@ -23325,7 +24321,7 @@ var recordLaunch = (root, attempt) => {
23325
24321
  writeFileSync3(fileFor(dir, attempt.workerId), JSON.stringify({
23326
24322
  ...attempt,
23327
24323
  pid: process.pid,
23328
- host: os8.hostname(),
24324
+ host: os11.hostname(),
23329
24325
  at: new Date().toISOString()
23330
24326
  }), { flag: "wx" });
23331
24327
  return true;
@@ -23339,7 +24335,7 @@ var clearLaunch = (root, workerId) => {
23339
24335
  rmSync2(fileFor(dir, workerId), { force: true });
23340
24336
  };
23341
24337
  var alive = (attempt) => {
23342
- if (attempt.host !== os8.hostname())
24338
+ if (attempt.host !== os11.hostname())
23343
24339
  return true;
23344
24340
  try {
23345
24341
  process.kill(attempt.pid, 0);
@@ -23361,7 +24357,7 @@ var abandonedLaunches = (root) => {
23361
24357
  const found = [];
23362
24358
  for (const name of names) {
23363
24359
  try {
23364
- const attempt = JSON.parse(readFileSync6(path28.join(dir, name), "utf8"));
24360
+ const attempt = JSON.parse(readFileSync6(path31.join(dir, name), "utf8"));
23365
24361
  if (typeof attempt.workerId === "string" && typeof attempt.session === "string") {
23366
24362
  if (!alive(attempt))
23367
24363
  found.push(attempt);
@@ -23379,9 +24375,9 @@ import { randomUUID as randomUUID4 } from "node:crypto";
23379
24375
  // packages/workit-pi/src/worker-protocol.ts
23380
24376
  var MAX_WORKER_LINE_BYTES = 64 * 1024;
23381
24377
  var MAX_WORKER_STDERR_BYTES = 64 * 1024;
23382
- var isRecord5 = (value) => typeof value === "object" && value !== null;
24378
+ var isRecord7 = (value) => typeof value === "object" && value !== null;
23383
24379
  var parseEvent2 = (value) => {
23384
- if (!isRecord5(value) || typeof value.type !== "string")
24380
+ if (!isRecord7(value) || typeof value.type !== "string")
23385
24381
  return null;
23386
24382
  if (value.type === "workit_worker_ready") {
23387
24383
  if (typeof value.workerId !== "string" || typeof value.sessionId !== "string")