@brainervirus/workit-cursor 8.13.1 → 8.14.1
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.
|
@@ -7624,6 +7624,16 @@ var init_zod = __esm(() => {
|
|
|
7624
7624
|
|
|
7625
7625
|
// packages/workit-core/src/core/task-contract.ts
|
|
7626
7626
|
import { createHash, randomUUID } from "node:crypto";
|
|
7627
|
+
function resolveIdRef(kind, ref, ids) {
|
|
7628
|
+
if (ids.includes(ref))
|
|
7629
|
+
return success(null, null, ref);
|
|
7630
|
+
const matches = ids.filter((candidate) => candidate.startsWith(ref));
|
|
7631
|
+
if (matches.length === 1)
|
|
7632
|
+
return success(null, null, matches[0]);
|
|
7633
|
+
if (matches.length === 0)
|
|
7634
|
+
return failure2("not_found", `${kind} not found`);
|
|
7635
|
+
return failure2("invalid_input", `${kind} prefix ${ref} is ambiguous; candidates: ${matches.join(", ")}`);
|
|
7636
|
+
}
|
|
7627
7637
|
function parseOperation(family, input) {
|
|
7628
7638
|
if (typeof input === "object" && input !== null && "schemaVersion" in input && input.schemaVersion !== SCHEMA_VERSION)
|
|
7629
7639
|
return failure2("unsupported_version", "unsupported schema version", { operation: family });
|
|
@@ -7726,7 +7736,7 @@ var SCHEMA_VERSION = 1, POLICY_VERSION = "1.0.0", OPERATION_FAMILIES, uuidPatter
|
|
|
7726
7736
|
return true;
|
|
7727
7737
|
}
|
|
7728
7738
|
return false;
|
|
7729
|
-
}, text, id, digest, revision, validUtc = (value) => {
|
|
7739
|
+
}, text, id, idRefPattern, idRef, digest, revision, validUtc = (value) => {
|
|
7730
7740
|
const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,9}))?Z$/.exec(value);
|
|
7731
7741
|
if (!match)
|
|
7732
7742
|
return false;
|
|
@@ -7909,6 +7919,8 @@ var init_task_contract = __esm(() => {
|
|
|
7909
7919
|
ctx.issues.push({ code: "custom", input: ctx.value, message: "invalid Unicode", path: [] });
|
|
7910
7920
|
});
|
|
7911
7921
|
id = text.regex(uuidPattern, "expected lowercase UUID");
|
|
7922
|
+
idRefPattern = /^[0-9a-f][0-9a-f-]{7,35}$/;
|
|
7923
|
+
idRef = text.regex(idRefPattern, "expected a UUID or an id prefix of 8+ characters");
|
|
7912
7924
|
digest = text.regex(digestPattern, "expected lowercase SHA-256 digest");
|
|
7913
7925
|
revision = text.regex(uuidPattern, "expected UUID revision");
|
|
7914
7926
|
utcSchema = text.check((ctx) => {
|
|
@@ -8177,7 +8189,7 @@ var init_task_contract = __esm(() => {
|
|
|
8177
8189
|
outcome: _enum(["completed", "failed", "cancelled"]),
|
|
8178
8190
|
summary: text,
|
|
8179
8191
|
evidenceIds: array(id),
|
|
8180
|
-
findingIds: array(
|
|
8192
|
+
findingIds: array(idRef)
|
|
8181
8193
|
}).strict();
|
|
8182
8194
|
workerSchema = object({
|
|
8183
8195
|
assignment: assignmentSchema,
|
|
@@ -8369,7 +8381,7 @@ var init_task_contract = __esm(() => {
|
|
|
8369
8381
|
action: literal("resolve"),
|
|
8370
8382
|
...taskId,
|
|
8371
8383
|
expectedRevision: revision.optional(),
|
|
8372
|
-
findingId:
|
|
8384
|
+
findingId: idRef,
|
|
8373
8385
|
disposition: _enum(["open", "fixed", "dismissed", "deferred"]),
|
|
8374
8386
|
reason: text,
|
|
8375
8387
|
evidenceIds: array(id),
|
|
@@ -8410,7 +8422,7 @@ var init_task_contract = __esm(() => {
|
|
|
8410
8422
|
...taskId,
|
|
8411
8423
|
expectedRevision: revision.optional(),
|
|
8412
8424
|
expectedWorkspaceRevision: revision.optional(),
|
|
8413
|
-
workerId:
|
|
8425
|
+
workerId: idRef,
|
|
8414
8426
|
report: workerReportSchema
|
|
8415
8427
|
}),
|
|
8416
8428
|
cancel: operation({
|
|
@@ -8418,7 +8430,7 @@ var init_task_contract = __esm(() => {
|
|
|
8418
8430
|
...taskId,
|
|
8419
8431
|
...revisions,
|
|
8420
8432
|
expectedWorkspaceRevision: revision.optional(),
|
|
8421
|
-
workerId:
|
|
8433
|
+
workerId: idRef,
|
|
8422
8434
|
reason: text
|
|
8423
8435
|
})
|
|
8424
8436
|
};
|
|
@@ -9570,24 +9582,26 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
9570
9582
|
}, resolveRuntimeReleaseTracks = (cwd) => {
|
|
9571
9583
|
const matched = matchRuntimeWorkspace(cwd);
|
|
9572
9584
|
return matched ? { workspace: matched.selected.name, releaseTracks: matched.selected.releaseTracks } : null;
|
|
9573
|
-
},
|
|
9585
|
+
}, inheritedSetting = (cwd, dir, has) => {
|
|
9574
9586
|
let matched;
|
|
9575
9587
|
try {
|
|
9576
9588
|
matched = matchRuntimeWorkspace(cwd, dir);
|
|
9577
9589
|
} catch {
|
|
9578
|
-
return
|
|
9590
|
+
return null;
|
|
9579
9591
|
}
|
|
9580
9592
|
if (!matched)
|
|
9581
|
-
return
|
|
9593
|
+
return null;
|
|
9582
9594
|
const { selected, matches } = matched;
|
|
9583
9595
|
const enclosing = matches.filter((entry) => entry !== selected).toSorted((a, b) => globSpecificity(b.glob) - globSpecificity(a.glob));
|
|
9584
|
-
|
|
9596
|
+
return { workspace: selected.name, source: [selected, ...enclosing].find(has) ?? null };
|
|
9597
|
+
}, workspaceTrailers = (cwd, dir) => {
|
|
9598
|
+
const found = inheritedSetting(cwd, dir, (entry) => typeof entry.trailers === "boolean");
|
|
9585
9599
|
return {
|
|
9586
|
-
enabled: source?.trailers !== false,
|
|
9587
|
-
workspace:
|
|
9588
|
-
from: source?.name ?? null
|
|
9600
|
+
enabled: found?.source?.trailers !== false,
|
|
9601
|
+
workspace: found?.workspace ?? null,
|
|
9602
|
+
from: found?.source?.name ?? null
|
|
9589
9603
|
};
|
|
9590
|
-
}, resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
9604
|
+
}, workspaceLiveSessionGuard = (cwd, dir) => inheritedSetting(cwd, dir, (entry) => typeof entry.liveSessionGuard === "boolean")?.source?.liveSessionGuard !== false, TIER_KEYS, TIER_HOSTS, resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
9591
9605
|
const matched = matchRuntimeWorkspace(cwd);
|
|
9592
9606
|
if (!matched)
|
|
9593
9607
|
return null;
|
|
@@ -9698,6 +9712,8 @@ var init_workspaces = __esm(() => {
|
|
|
9698
9712
|
defaultEndpoint: unknown().optional(),
|
|
9699
9713
|
verification: _enum(["self", "independent"]).optional(),
|
|
9700
9714
|
trailers: boolean2().optional(),
|
|
9715
|
+
liveSessionGuard: boolean2().optional(),
|
|
9716
|
+
fanout: object({ tierModels: unknown().optional() }).passthrough().optional(),
|
|
9701
9717
|
autonomy: object(Object.fromEntries(["push", "pr", "merge", "release", "rerun"].map((kind) => [
|
|
9702
9718
|
kind,
|
|
9703
9719
|
union([boolean2(), literal("verified")]).optional()
|
|
@@ -9747,6 +9763,17 @@ var init_workspaces = __esm(() => {
|
|
|
9747
9763
|
runtimeWorkspaceIndexSchema = object({
|
|
9748
9764
|
workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
|
|
9749
9765
|
}).passthrough();
|
|
9766
|
+
TIER_KEYS = new Set(["mundane", "standard", "hard"]);
|
|
9767
|
+
TIER_HOSTS = new Set([
|
|
9768
|
+
"claude_code",
|
|
9769
|
+
"opencode",
|
|
9770
|
+
"codex",
|
|
9771
|
+
"codex_cli",
|
|
9772
|
+
"codex_desktop",
|
|
9773
|
+
"cursor",
|
|
9774
|
+
"pi",
|
|
9775
|
+
"default"
|
|
9776
|
+
]);
|
|
9750
9777
|
});
|
|
9751
9778
|
|
|
9752
9779
|
// packages/workit-core/src/core/release-tracks.ts
|
|
@@ -13447,20 +13474,26 @@ class TaskStore {
|
|
|
13447
13474
|
return success(null, null, { key: key.data, bound: bound.data });
|
|
13448
13475
|
}
|
|
13449
13476
|
taskForWorker(workerId) {
|
|
13450
|
-
if (typeof workerId !== "string" || !
|
|
13477
|
+
if (typeof workerId !== "string" || !idRefPattern.test(workerId))
|
|
13451
13478
|
return failure2("invalid_input", "needs a worker id: `workit worker cancel|report <workerId>`");
|
|
13452
13479
|
const listed = this.listTaskIndex();
|
|
13453
13480
|
if (!listed.ok)
|
|
13454
13481
|
return listed;
|
|
13482
|
+
const owners = new Map;
|
|
13455
13483
|
for (const entry of listed.data) {
|
|
13456
13484
|
if (entry.status === "closed")
|
|
13457
13485
|
continue;
|
|
13458
13486
|
const loaded = this.load(entry.id);
|
|
13459
13487
|
if (!loaded.ok)
|
|
13460
13488
|
return loaded;
|
|
13461
|
-
|
|
13462
|
-
|
|
13489
|
+
for (const worker of loaded.data?.record.workers ?? [])
|
|
13490
|
+
owners.set(worker.id, entry.id);
|
|
13463
13491
|
}
|
|
13492
|
+
const resolved = resolveIdRef("worker", workerId, [...owners.keys()]);
|
|
13493
|
+
if (resolved.ok)
|
|
13494
|
+
return success(null, null, owners.get(resolved.data) ?? null);
|
|
13495
|
+
if (resolved.code !== "not_found")
|
|
13496
|
+
return resolved;
|
|
13464
13497
|
return success(null, null, null);
|
|
13465
13498
|
}
|
|
13466
13499
|
originTaskEntry() {
|
|
@@ -16719,6 +16752,10 @@ class WorkitCore {
|
|
|
16719
16752
|
}
|
|
16720
16753
|
if (helper?.ok)
|
|
16721
16754
|
return failure2("permission_denied", "helpers cannot resolve findings");
|
|
16755
|
+
const findingRef = resolveIdRef("finding", input.findingId, task.data.findings.map((entry) => entry.id));
|
|
16756
|
+
if (!findingRef.ok)
|
|
16757
|
+
return findingRef;
|
|
16758
|
+
input.findingId = findingRef.data;
|
|
16722
16759
|
const finding = task.data.findings.find((entry) => entry.id === input.findingId);
|
|
16723
16760
|
if (!finding)
|
|
16724
16761
|
return failure2("not_found", "finding not found");
|
|
@@ -16870,15 +16907,31 @@ class WorkitCore {
|
|
|
16870
16907
|
return changed;
|
|
16871
16908
|
return success(changed.data.task.revision, changed.data.workspace.revision, changed.data.task.workers.at(-1));
|
|
16872
16909
|
}
|
|
16910
|
+
const workerRef = resolveIdRef("worker", input.workerId, helperId === null ? task.data.workers.map((candidate) => candidate.id) : [helperId]);
|
|
16911
|
+
if (!workerRef.ok)
|
|
16912
|
+
return helperId === null ? workerRef : failure2("permission_denied", input.action === "report" ? ONLY_OWN_REPORT : NO_HELPER_CONTROL);
|
|
16913
|
+
input.workerId = workerRef.data;
|
|
16873
16914
|
const entry = task.data.workers.find((candidate) => candidate.id === input.workerId);
|
|
16874
16915
|
if (!entry)
|
|
16875
16916
|
return failure2("not_found", "worker not found");
|
|
16876
16917
|
if (input.action === "report") {
|
|
16877
16918
|
if (helperId === null || helperId !== input.workerId)
|
|
16878
|
-
return failure2("permission_denied",
|
|
16919
|
+
return failure2("permission_denied", ONLY_OWN_REPORT);
|
|
16879
16920
|
const helper = this.helperEntry(task.data, true, true);
|
|
16880
16921
|
if (!helper.ok)
|
|
16881
16922
|
return helper;
|
|
16923
|
+
const ownFindings = task.data.findings.filter((candidate) => candidate.provenance.workerId === helperId).map((candidate) => candidate.id);
|
|
16924
|
+
const reported = [];
|
|
16925
|
+
for (const findingRef of input.report.findingIds) {
|
|
16926
|
+
const found = resolveIdRef("finding", findingRef, ownFindings);
|
|
16927
|
+
if (found.ok)
|
|
16928
|
+
reported.push(found.data);
|
|
16929
|
+
else if (found.code === "not_found")
|
|
16930
|
+
reported.push(findingRef);
|
|
16931
|
+
else
|
|
16932
|
+
return found;
|
|
16933
|
+
}
|
|
16934
|
+
input.report.findingIds = reported;
|
|
16882
16935
|
const evidence = task.data.evidence.filter((candidate) => input.report.evidenceIds.includes(candidate.id));
|
|
16883
16936
|
const findings = task.data.findings.filter((candidate) => input.report.findingIds.includes(candidate.id));
|
|
16884
16937
|
if (evidence.length !== input.report.evidenceIds.length || findings.length !== input.report.findingIds.length)
|
|
@@ -16906,7 +16959,7 @@ class WorkitCore {
|
|
|
16906
16959
|
return updated ? success(changed.data.task.revision, changed.data.workspace.revision, updated) : failure2("recovery_required", "worker disappeared during report");
|
|
16907
16960
|
}
|
|
16908
16961
|
if (helperId !== null)
|
|
16909
|
-
return failure2("permission_denied",
|
|
16962
|
+
return failure2("permission_denied", NO_HELPER_CONTROL);
|
|
16910
16963
|
if (task.data.status === "closed")
|
|
16911
16964
|
return failure2("invalid_transition", "closed task cannot cancel workers");
|
|
16912
16965
|
if (entry.data.state === "stopped") {
|
|
@@ -17435,7 +17488,7 @@ var MAX_JUDGED_REFS = 5, workspaceVerification = (root) => {
|
|
|
17435
17488
|
return changed;
|
|
17436
17489
|
const updated = changed.data.task.workers.find((candidate) => candidate.id === input.workerId);
|
|
17437
17490
|
return updated ? success(changed.data.task.revision, changed.data.workspace.revision, updated) : failure2("recovery_required", "worker disappeared during lifecycle observation");
|
|
17438
|
-
}, refsWithinScope = (refs, scope) => refs.every((ref) => ref.kind !== "file" || scopeCovers(scope, { description: "", paths: [ref.path], exclusions: [] })), TASK_SCOPED_ACTIONS, WORKER_ADDRESSED_ACTIONS, workerCommand = (taskId, request) => {
|
|
17491
|
+
}, refsWithinScope = (refs, scope) => refs.every((ref) => ref.kind !== "file" || scopeCovers(scope, { description: "", paths: [ref.path], exclusions: [] })), TASK_SCOPED_ACTIONS, ONLY_OWN_REPORT = "workers can submit only their own report", NO_HELPER_CONTROL = "helpers cannot cancel or control workers", WORKER_ADDRESSED_ACTIONS, workerCommand = (taskId, request) => {
|
|
17439
17492
|
const { action, workerId, reason, report } = request;
|
|
17440
17493
|
const rest = JSON.stringify({ reason, report });
|
|
17441
17494
|
const payload = rest === "{}" ? "" : ` --payload '${rest.replace(/'/gu, "'\\''")}'`;
|
|
@@ -20052,10 +20105,238 @@ var init_policy = __esm(() => {
|
|
|
20052
20105
|
init_shell_words();
|
|
20053
20106
|
});
|
|
20054
20107
|
|
|
20108
|
+
// packages/workit-core/src/core/live-session.ts
|
|
20109
|
+
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
20110
|
+
import { createHash as createHash15 } from "node:crypto";
|
|
20111
|
+
import fs17 from "node:fs";
|
|
20112
|
+
import os6 from "node:os";
|
|
20113
|
+
import path22 from "node:path";
|
|
20114
|
+
var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (session) => session.split(":")[0].replace(/-w-.*$/su, "") || session, familyOf = (host) => host.replace(/_(?:cli|desktop)$/u, ""), processAlive = (pid) => {
|
|
20115
|
+
try {
|
|
20116
|
+
process.kill(pid, 0);
|
|
20117
|
+
return true;
|
|
20118
|
+
} catch (error) {
|
|
20119
|
+
return error.code === "EPERM";
|
|
20120
|
+
}
|
|
20121
|
+
}, processInfo = (pid) => {
|
|
20122
|
+
try {
|
|
20123
|
+
if (liveProbe.platform === "linux") {
|
|
20124
|
+
const stat = fs17.readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
20125
|
+
const ppid = Number(stat.slice(stat.lastIndexOf(")") + 2).split(" ")[1]);
|
|
20126
|
+
const command = fs17.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ");
|
|
20127
|
+
return Number.isInteger(ppid) ? { ppid, command: command.trim() } : null;
|
|
20128
|
+
}
|
|
20129
|
+
if (liveProbe.platform === "win32")
|
|
20130
|
+
return null;
|
|
20131
|
+
const match = /^\s*(\d+)\s+(.*)$/su.exec(liveProbe.run(["-o", "ppid=,command=", "-p", String(pid)]));
|
|
20132
|
+
return match ? { ppid: Number(match[1]), command: match[2].trim() } : null;
|
|
20133
|
+
} catch {
|
|
20134
|
+
return null;
|
|
20135
|
+
}
|
|
20136
|
+
}, WRAPPER, PIDS, hostPid = (transport) => {
|
|
20137
|
+
if (transport !== "hook-process")
|
|
20138
|
+
return process.pid;
|
|
20139
|
+
const key = `${liveProbe.platform}\x00${process.ppid}`;
|
|
20140
|
+
if (PIDS.has(key))
|
|
20141
|
+
return PIDS.get(key) ?? null;
|
|
20142
|
+
if (liveProbe.platform === "win32") {
|
|
20143
|
+
PIDS.set(key, null);
|
|
20144
|
+
return null;
|
|
20145
|
+
}
|
|
20146
|
+
let pid = process.ppid;
|
|
20147
|
+
for (let hop = 0;hop < 4; hop++) {
|
|
20148
|
+
const info = processInfo(pid);
|
|
20149
|
+
if (!info || !WRAPPER.test(info.command) || info.ppid <= 1)
|
|
20150
|
+
break;
|
|
20151
|
+
pid = info.ppid;
|
|
20152
|
+
}
|
|
20153
|
+
PIDS.set(key, pid);
|
|
20154
|
+
return pid;
|
|
20155
|
+
}, sessionSafe = (value) => value.replace(/[^A-Za-z0-9_.:@/+-]/gu, "-"), liveOwner = (input, transport, env = process.env) => {
|
|
20156
|
+
const session = input.session.id?.trim();
|
|
20157
|
+
if (!session)
|
|
20158
|
+
return null;
|
|
20159
|
+
const leads = new Set([leadOf(sessionSafe(session))]);
|
|
20160
|
+
if (input.session.parentId)
|
|
20161
|
+
leads.add(leadOf(sessionSafe(input.session.parentId)));
|
|
20162
|
+
if (env.WORKIT_PI_WORKER_SESSION && env.WORKIT_SESSION_ID)
|
|
20163
|
+
leads.add(leadOf(sessionSafe(env.WORKIT_SESSION_ID)));
|
|
20164
|
+
return {
|
|
20165
|
+
host: input.host,
|
|
20166
|
+
session: sessionSafe(session),
|
|
20167
|
+
leads: [...leads],
|
|
20168
|
+
pid: () => hostPid(transport)
|
|
20169
|
+
};
|
|
20170
|
+
}, checkoutOf = (target) => {
|
|
20171
|
+
let dir = path22.resolve(target);
|
|
20172
|
+
while (!fs17.existsSync(dir) && path22.dirname(dir) !== dir)
|
|
20173
|
+
dir = path22.dirname(dir);
|
|
20174
|
+
if (fs17.existsSync(dir) && !fs17.statSync(dir).isDirectory())
|
|
20175
|
+
dir = path22.dirname(dir);
|
|
20176
|
+
const parts = dir.split(path22.sep);
|
|
20177
|
+
const dotGit = parts.lastIndexOf(".git");
|
|
20178
|
+
if (dotGit > 0)
|
|
20179
|
+
dir = parts.slice(0, dotGit).join(path22.sep) || path22.sep;
|
|
20180
|
+
const location = resolveStore(dir);
|
|
20181
|
+
if (location instanceof Error || !location.shared || !location.top)
|
|
20182
|
+
return null;
|
|
20183
|
+
let top = location.top;
|
|
20184
|
+
try {
|
|
20185
|
+
top = fs17.realpathSync.native(top);
|
|
20186
|
+
} catch {}
|
|
20187
|
+
return top;
|
|
20188
|
+
}, markerFile2 = (top) => path22.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`), checkoutKey = (top, platform = process.platform) => {
|
|
20189
|
+
let key = path22.resolve(top);
|
|
20190
|
+
try {
|
|
20191
|
+
key = fs17.realpathSync.native(key);
|
|
20192
|
+
} catch {}
|
|
20193
|
+
return platform === "win32" ? key.replaceAll("/", "\\").toLowerCase() : key;
|
|
20194
|
+
}, readMarker = (file) => {
|
|
20195
|
+
try {
|
|
20196
|
+
const value = JSON.parse(fs17.readFileSync(file, "utf8"));
|
|
20197
|
+
if (value?.version !== 1 || typeof value.session !== "string" || typeof value.host !== "string" || typeof value.heartbeatAt !== "string" || Number.isNaN(Date.parse(value.heartbeatAt)))
|
|
20198
|
+
return null;
|
|
20199
|
+
return {
|
|
20200
|
+
version: 1,
|
|
20201
|
+
session: value.session,
|
|
20202
|
+
leads: Array.isArray(value.leads) ? value.leads.filter((item) => typeof item === "string") : [],
|
|
20203
|
+
host: value.host,
|
|
20204
|
+
pid: typeof value.pid === "number" ? value.pid : null,
|
|
20205
|
+
pidStart: typeof value.pidStart === "string" ? value.pidStart : null,
|
|
20206
|
+
machine: typeof value.machine === "string" ? value.machine : "",
|
|
20207
|
+
checkout: typeof value.checkout === "string" ? value.checkout : "",
|
|
20208
|
+
heartbeatAt: value.heartbeatAt
|
|
20209
|
+
};
|
|
20210
|
+
} catch {
|
|
20211
|
+
return null;
|
|
20212
|
+
}
|
|
20213
|
+
}, ageOf2 = (marker, now) => now.getTime() - Date.parse(marker.heartbeatAt), isLive = (marker, deps) => {
|
|
20214
|
+
const age = ageOf2(marker, deps.now ?? new Date);
|
|
20215
|
+
if (age > (deps.ttlMs ?? LIVE_SESSION_TTL_MS))
|
|
20216
|
+
return false;
|
|
20217
|
+
if (marker.machine !== (deps.machine ?? os6.hostname()) || marker.pid === null)
|
|
20218
|
+
return true;
|
|
20219
|
+
if (!(deps.alive ?? processAlive)(marker.pid))
|
|
20220
|
+
return false;
|
|
20221
|
+
if (marker.pidStart === null)
|
|
20222
|
+
return true;
|
|
20223
|
+
const start = liveProbe.start(marker.pid);
|
|
20224
|
+
return start === null || start === marker.pidStart;
|
|
20225
|
+
}, sameLead = (marker, owner) => marker.session === owner.session || owner.leads.some((lead) => lead === marker.session || marker.leads.includes(lead)), sameOwner = (marker, owner, machine) => {
|
|
20226
|
+
if (sameLead(marker, owner))
|
|
20227
|
+
return true;
|
|
20228
|
+
if (marker.machine !== machine)
|
|
20229
|
+
return false;
|
|
20230
|
+
const pid = owner.pid();
|
|
20231
|
+
if (pid === null || marker.pid === null || marker.pidStart === null)
|
|
20232
|
+
return familyOf(marker.host) === familyOf(owner.host);
|
|
20233
|
+
return marker.pid === pid;
|
|
20234
|
+
}, liveConflict = (target, owner, deps = {}) => {
|
|
20235
|
+
try {
|
|
20236
|
+
const checkout = checkoutOf(target);
|
|
20237
|
+
if (!checkout)
|
|
20238
|
+
return null;
|
|
20239
|
+
const file = markerFile2(checkout);
|
|
20240
|
+
const marker = readMarker(file);
|
|
20241
|
+
if (!marker || sameLead(marker, owner) || !isLive(marker, deps))
|
|
20242
|
+
return null;
|
|
20243
|
+
if (sameOwner(marker, owner, deps.machine ?? os6.hostname()))
|
|
20244
|
+
return null;
|
|
20245
|
+
return { marker, file, ageMs: ageOf2(marker, deps.now ?? new Date), checkout };
|
|
20246
|
+
} catch {
|
|
20247
|
+
return null;
|
|
20248
|
+
}
|
|
20249
|
+
}, claimLiveSession = (target, owner, deps = {}) => {
|
|
20250
|
+
try {
|
|
20251
|
+
const checkout = checkoutOf(target);
|
|
20252
|
+
if (!checkout)
|
|
20253
|
+
return;
|
|
20254
|
+
const file = markerFile2(checkout);
|
|
20255
|
+
const now = deps.now ?? new Date;
|
|
20256
|
+
const machine = deps.machine ?? os6.hostname();
|
|
20257
|
+
const current = readMarker(file);
|
|
20258
|
+
if (current) {
|
|
20259
|
+
const age = ageOf2(current, now);
|
|
20260
|
+
if (sameLead(current, owner) && age >= 0 && age < HEARTBEAT_EVERY_MS)
|
|
20261
|
+
return;
|
|
20262
|
+
if (!sameLead(current, owner) && isLive(current, deps) && !sameOwner(current, owner, machine))
|
|
20263
|
+
return;
|
|
20264
|
+
}
|
|
20265
|
+
const pid = owner.pid();
|
|
20266
|
+
const marker = {
|
|
20267
|
+
version: 1,
|
|
20268
|
+
session: owner.session,
|
|
20269
|
+
leads: owner.leads,
|
|
20270
|
+
host: owner.host,
|
|
20271
|
+
pid,
|
|
20272
|
+
pidStart: pid === null ? null : liveProbe.start(pid),
|
|
20273
|
+
machine,
|
|
20274
|
+
checkout,
|
|
20275
|
+
heartbeatAt: now.toISOString()
|
|
20276
|
+
};
|
|
20277
|
+
fs17.mkdirSync(path22.dirname(file), { recursive: true });
|
|
20278
|
+
const temp = `${file}.${process.pid}.tmp`;
|
|
20279
|
+
fs17.writeFileSync(temp, JSON.stringify(marker));
|
|
20280
|
+
fs17.renameSync(temp, file);
|
|
20281
|
+
} catch {}
|
|
20282
|
+
};
|
|
20283
|
+
var init_live_session = __esm(() => {
|
|
20284
|
+
init_paths();
|
|
20285
|
+
init_logger();
|
|
20286
|
+
init_store_lock();
|
|
20287
|
+
LIVE_SESSION_TTL_MS = 15 * 60000;
|
|
20288
|
+
liveProbe = {
|
|
20289
|
+
platform: process.platform,
|
|
20290
|
+
run: (args) => spawnSync13("ps", args, { encoding: "utf8", timeout: 2000 }).stdout ?? "",
|
|
20291
|
+
start: (pid) => processStartOf(pid)
|
|
20292
|
+
};
|
|
20293
|
+
WRAPPER = /^(?:\S*\/)?(?:sh|bash|dash|zsh|ash|ksh|fish|env|timeout|npx|npm|bunx|cmd\.exe|pwsh|powershell)(?:\s|$)|launch\.mjs|npx-cli\.js/u;
|
|
20294
|
+
PIDS = new Map;
|
|
20295
|
+
});
|
|
20296
|
+
|
|
20055
20297
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
20056
20298
|
import { realpathSync as realpathSync5 } from "node:fs";
|
|
20057
|
-
import
|
|
20058
|
-
|
|
20299
|
+
import os7 from "node:os";
|
|
20300
|
+
import path23 from "node:path";
|
|
20301
|
+
function writeGate(cwd, paths, owner, deps) {
|
|
20302
|
+
if (!owner)
|
|
20303
|
+
return beforeWriteGate(cwd, paths);
|
|
20304
|
+
const targets = liveTargets(cwd, paths);
|
|
20305
|
+
const live = liveGate(owner, targets, deps);
|
|
20306
|
+
if (live.kind !== "none")
|
|
20307
|
+
return live;
|
|
20308
|
+
const decision = beforeWriteGate(cwd, paths);
|
|
20309
|
+
if (decision.kind === "none")
|
|
20310
|
+
claimAll(owner, targets, deps);
|
|
20311
|
+
return decision;
|
|
20312
|
+
}
|
|
20313
|
+
function shellGate(cwd, command, owner, dialect = "posix", deps) {
|
|
20314
|
+
const writes = shellWrites(command);
|
|
20315
|
+
let targets = [];
|
|
20316
|
+
if (owner) {
|
|
20317
|
+
try {
|
|
20318
|
+
targets = [
|
|
20319
|
+
...segmentTargets(cwd, command, dialect),
|
|
20320
|
+
...gitMutationDirs(cwd, command, dialect).map((dir) => path23.join(dir, DIR_TARGET))
|
|
20321
|
+
];
|
|
20322
|
+
} catch {
|
|
20323
|
+
targets = [];
|
|
20324
|
+
}
|
|
20325
|
+
}
|
|
20326
|
+
if (owner && targets.length) {
|
|
20327
|
+
const live = liveGate(owner, targets, deps);
|
|
20328
|
+
if (live.kind !== "none") {
|
|
20329
|
+
if (!overridden(command, dialect))
|
|
20330
|
+
return live;
|
|
20331
|
+
noteOverride(cwd, owner, command);
|
|
20332
|
+
}
|
|
20333
|
+
}
|
|
20334
|
+
const decision = writes.writes ? beforeWriteGate(cwd, writes.targets) : NONE3;
|
|
20335
|
+
if (owner && targets.length && decision.kind === "none")
|
|
20336
|
+
claimAll(owner, targets, deps);
|
|
20337
|
+
return decision;
|
|
20338
|
+
}
|
|
20339
|
+
function beforeWriteGate(cwd, paths) {
|
|
20059
20340
|
try {
|
|
20060
20341
|
const gate = gateFor(cwd);
|
|
20061
20342
|
if (!gate || gate.blockers.length === 0)
|
|
@@ -20074,21 +20355,21 @@ function writeGate(cwd, paths) {
|
|
|
20074
20355
|
}
|
|
20075
20356
|
var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
20076
20357
|
let rest = "";
|
|
20077
|
-
for (let dir =
|
|
20358
|
+
for (let dir = path23.resolve(file);; dir = path23.dirname(dir)) {
|
|
20078
20359
|
try {
|
|
20079
|
-
return
|
|
20360
|
+
return path23.join(realpathSync5(dir), rest);
|
|
20080
20361
|
} catch {
|
|
20081
|
-
if (
|
|
20082
|
-
return
|
|
20083
|
-
rest = rest ?
|
|
20362
|
+
if (path23.dirname(dir) === dir)
|
|
20363
|
+
return path23.resolve(file);
|
|
20364
|
+
rest = rest ? path23.join(path23.basename(dir), rest) : path23.basename(dir);
|
|
20084
20365
|
}
|
|
20085
20366
|
}
|
|
20086
20367
|
}, exempt = (cwd, root, file, plans) => {
|
|
20087
20368
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
20088
|
-
const relative =
|
|
20089
|
-
if (relative.startsWith("..") ||
|
|
20369
|
+
const relative = path23.relative(fold(canonical2(root)), fold(canonical2(path23.resolve(cwd, file))));
|
|
20370
|
+
if (relative.startsWith("..") || path23.isAbsolute(relative))
|
|
20090
20371
|
return true;
|
|
20091
|
-
const posix = relative.split(
|
|
20372
|
+
const posix = relative.split(path23.sep).join("/");
|
|
20092
20373
|
const segments = posix.split("/");
|
|
20093
20374
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
20094
20375
|
}, PREFIXES2, operands = (words) => words.filter((item) => !item.startsWith("-")), editsOf = (input) => {
|
|
@@ -20180,7 +20461,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20180
20461
|
if (!unknown && targets.length === 0)
|
|
20181
20462
|
return { writes: false, targets: null };
|
|
20182
20463
|
return { writes: true, targets: unknown ? null : targets };
|
|
20183
|
-
}, cache2, planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
20464
|
+
}, cache2, planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path23.resolve(root, file)) ?? "-").join("\x00"), gateFor = (cwd) => {
|
|
20184
20465
|
const store = new TaskStore(cwd);
|
|
20185
20466
|
const listed = store.listTaskIndex();
|
|
20186
20467
|
if (!listed.ok)
|
|
@@ -20216,21 +20497,182 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20216
20497
|
if (cache2.size > 32)
|
|
20217
20498
|
cache2.delete(cache2.keys().next().value);
|
|
20218
20499
|
return gate;
|
|
20500
|
+
}, MUTATING_GIT, DRY_N, MUTATING_WORKIT_GIT, DIR_TARGET = ".workit-live-target", OVERRIDE = "WORKIT_LIVE_SESSION=override", inert = (args, sub) => args.some((arg) => arg === "--help" || arg === "-h" || arg === "--dry-run" || DRY_N.has(sub) && /^-[a-zA-Z]*n[a-zA-Z]*$/.test(arg) && !arg.startsWith("--")), gitMutationDirs = (cwd, command, dialect) => {
|
|
20501
|
+
let invocations;
|
|
20502
|
+
try {
|
|
20503
|
+
invocations = rawInvocations(command, dialect);
|
|
20504
|
+
} catch {
|
|
20505
|
+
return [];
|
|
20506
|
+
}
|
|
20507
|
+
return invocations.flatMap((invocation) => {
|
|
20508
|
+
if (invocation.tool !== "git" || invocation.dir === undefined)
|
|
20509
|
+
return [];
|
|
20510
|
+
const at = invocation.args.findIndex((arg) => !arg.startsWith("-"));
|
|
20511
|
+
const sub = invocation.args[at] ?? "";
|
|
20512
|
+
const rest = invocation.args.slice(at + 1);
|
|
20513
|
+
if (!MUTATING_GIT.has(sub) || inert(rest, sub))
|
|
20514
|
+
return [];
|
|
20515
|
+
if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
|
|
20516
|
+
return [];
|
|
20517
|
+
return [path23.resolve(cwd, invocation.dir ?? ".")];
|
|
20518
|
+
});
|
|
20519
|
+
}, CD2, PUSHD2, POPD2, cdInto = (dir, words) => {
|
|
20520
|
+
const target = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
|
|
20521
|
+
if (dir === undefined || target === "-" || target && /^~|[$`*?]/.test(target))
|
|
20522
|
+
return;
|
|
20523
|
+
return target === undefined ? os7.homedir() : path23.resolve(dir, target);
|
|
20524
|
+
}, quoteWord = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`, segmentTargets = (cwd, command, dialect) => {
|
|
20525
|
+
const targets = [];
|
|
20526
|
+
let dir = cwd;
|
|
20527
|
+
let stack = [];
|
|
20528
|
+
const groups = [];
|
|
20529
|
+
const at = (file) => path23.isAbsolute(file) ? file : dir === undefined ? null : path23.resolve(dir, file);
|
|
20530
|
+
for (const segment of segmentsOf(command, dialect)) {
|
|
20531
|
+
if (segment.group === "open") {
|
|
20532
|
+
groups.push({ dir, stack: [...stack] });
|
|
20533
|
+
continue;
|
|
20534
|
+
}
|
|
20535
|
+
if (segment.group === "close") {
|
|
20536
|
+
const saved = groups.pop();
|
|
20537
|
+
if (saved)
|
|
20538
|
+
({ dir, stack } = saved);
|
|
20539
|
+
continue;
|
|
20540
|
+
}
|
|
20541
|
+
const lead = segment.words.findIndex((word) => !/^\w+=/.test(word));
|
|
20542
|
+
const words = lead < 0 ? [] : segment.words.slice(lead);
|
|
20543
|
+
const name = (words[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
|
|
20544
|
+
if (CD2.has(name)) {
|
|
20545
|
+
dir = cdInto(dir, words);
|
|
20546
|
+
continue;
|
|
20547
|
+
}
|
|
20548
|
+
if (PUSHD2.has(name)) {
|
|
20549
|
+
stack.push(dir);
|
|
20550
|
+
dir = cdInto(dir, words);
|
|
20551
|
+
continue;
|
|
20552
|
+
}
|
|
20553
|
+
if (POPD2.has(name)) {
|
|
20554
|
+
dir = stack.length ? stack.pop() : undefined;
|
|
20555
|
+
continue;
|
|
20556
|
+
}
|
|
20557
|
+
for (const file of segment.redirects)
|
|
20558
|
+
if (!/^\/dev\/(?:null|stdout|stderr|tty)$/.test(file)) {
|
|
20559
|
+
const resolved = at(file);
|
|
20560
|
+
if (resolved)
|
|
20561
|
+
targets.push(resolved);
|
|
20562
|
+
}
|
|
20563
|
+
const edits = editsOf(segment.words);
|
|
20564
|
+
if (edits === null) {
|
|
20565
|
+
if (dir !== undefined)
|
|
20566
|
+
targets.push(path23.join(dir, DIR_TARGET));
|
|
20567
|
+
} else
|
|
20568
|
+
for (const file of edits) {
|
|
20569
|
+
const resolved = at(file);
|
|
20570
|
+
if (resolved)
|
|
20571
|
+
targets.push(resolved);
|
|
20572
|
+
}
|
|
20573
|
+
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), "")))
|
|
20574
|
+
targets.push(path23.join(dir, DIR_TARGET));
|
|
20575
|
+
}
|
|
20576
|
+
return targets;
|
|
20577
|
+
}, overridden = (command, dialect) => {
|
|
20578
|
+
const segments = segmentsOf(command, dialect);
|
|
20579
|
+
if (segments.length !== 1 || segments[0].group || segments[0].subs?.length)
|
|
20580
|
+
return false;
|
|
20581
|
+
const words = segments[0].words;
|
|
20582
|
+
const lead = words.findIndex((word) => !/^\w+=/.test(word));
|
|
20583
|
+
return lead > 0 && words.slice(0, lead).includes(OVERRIDE);
|
|
20584
|
+
}, HOST_LABELS, ageText = (ms) => {
|
|
20585
|
+
const seconds = Math.max(0, Math.round(ms / 1000));
|
|
20586
|
+
return seconds < 120 ? `${seconds}s` : `${Math.round(seconds / 60)} min`;
|
|
20587
|
+
}, liveDeny = ({ marker, checkout, ageMs, file }) => {
|
|
20588
|
+
const unblock = `Work in your own worktree (\`git worktree add <path> -b <branch>\`, or \`workit fanout worktree create <slice>\`). Only if the user confirms that session is finished: release it with \`rm '${file}'\`, or prefix one command with ${OVERRIDE}.`;
|
|
20589
|
+
return {
|
|
20590
|
+
kind: "deny",
|
|
20591
|
+
reason: `workit live-session guard: ${checkout} belongs to another live agent session, ${HOST_LABELS[marker.host] ?? marker.host} session ${marker.session}${marker.pid === null ? "" : ` (pid ${marker.pid})`}, last active ${ageText(ageMs)} ago. Do not edit a checkout another session is working in; read-only commands stay allowed. ${unblock}`,
|
|
20592
|
+
unblock
|
|
20593
|
+
};
|
|
20594
|
+
}, guarded = (targets) => {
|
|
20595
|
+
const seen = new Set;
|
|
20596
|
+
return targets.filter((target) => {
|
|
20597
|
+
const dir = path23.dirname(target);
|
|
20598
|
+
if (seen.has(dir))
|
|
20599
|
+
return false;
|
|
20600
|
+
seen.add(dir);
|
|
20601
|
+
return true;
|
|
20602
|
+
});
|
|
20603
|
+
}, liveGate = (owner, targets, deps) => {
|
|
20604
|
+
try {
|
|
20605
|
+
for (const target of guarded(targets)) {
|
|
20606
|
+
const conflict = liveConflict(target, owner, deps);
|
|
20607
|
+
if (conflict && workspaceLiveSessionGuard(conflict.checkout))
|
|
20608
|
+
return liveDeny(conflict);
|
|
20609
|
+
}
|
|
20610
|
+
} catch {}
|
|
20611
|
+
return NONE3;
|
|
20612
|
+
}, claimAll = (owner, targets, deps) => {
|
|
20613
|
+
for (const target of guarded(targets))
|
|
20614
|
+
claimLiveSession(target, owner, deps);
|
|
20615
|
+
}, liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path23.resolve(cwd, file)) : [path23.join(cwd, DIR_TARGET)], noteOverride = (cwd, owner, command) => {
|
|
20616
|
+
try {
|
|
20617
|
+
if (!inWorkitWorkspace(cwd))
|
|
20618
|
+
return;
|
|
20619
|
+
appendHookObserved(cwd, {
|
|
20620
|
+
type: "live_session.override",
|
|
20621
|
+
actor: { host: owner.host, session: owner.session, agentId: null },
|
|
20622
|
+
session: owner.session,
|
|
20623
|
+
command: command.slice(0, 200)
|
|
20624
|
+
});
|
|
20625
|
+
} catch {}
|
|
20219
20626
|
};
|
|
20220
20627
|
var init_write_gate = __esm(() => {
|
|
20221
20628
|
init_task_store();
|
|
20222
20629
|
init_derive();
|
|
20223
20630
|
init_task_evaluation();
|
|
20631
|
+
init_live_session();
|
|
20632
|
+
init_workspaces();
|
|
20224
20633
|
init_ledger();
|
|
20634
|
+
init_raw_git();
|
|
20225
20635
|
init_shell_words();
|
|
20226
20636
|
NONE3 = { kind: "none" };
|
|
20227
20637
|
CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
20228
20638
|
PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
|
|
20229
20639
|
cache2 = new Map;
|
|
20640
|
+
MUTATING_GIT = new Set([
|
|
20641
|
+
"add",
|
|
20642
|
+
"am",
|
|
20643
|
+
"apply",
|
|
20644
|
+
"checkout",
|
|
20645
|
+
"cherry-pick",
|
|
20646
|
+
"clean",
|
|
20647
|
+
"commit",
|
|
20648
|
+
"merge",
|
|
20649
|
+
"mv",
|
|
20650
|
+
"pull",
|
|
20651
|
+
"rebase",
|
|
20652
|
+
"reset",
|
|
20653
|
+
"restore",
|
|
20654
|
+
"revert",
|
|
20655
|
+
"rm",
|
|
20656
|
+
"stash",
|
|
20657
|
+
"switch"
|
|
20658
|
+
]);
|
|
20659
|
+
DRY_N = new Set(["add", "clean", "rm", "mv"]);
|
|
20660
|
+
MUTATING_WORKIT_GIT = new Set(["commit", "branch"]);
|
|
20661
|
+
CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
20662
|
+
PUSHD2 = new Set(["pushd", "push-location"]);
|
|
20663
|
+
POPD2 = new Set(["popd", "pop-location"]);
|
|
20664
|
+
HOST_LABELS = {
|
|
20665
|
+
claude_code: "Claude Code",
|
|
20666
|
+
opencode: "OpenCode",
|
|
20667
|
+
codex_cli: "Codex CLI",
|
|
20668
|
+
codex_desktop: "Codex Desktop",
|
|
20669
|
+
cursor: "Cursor",
|
|
20670
|
+
pi: "Pi"
|
|
20671
|
+
};
|
|
20230
20672
|
});
|
|
20231
20673
|
|
|
20232
20674
|
// packages/workit-core/src/hooks/stop.ts
|
|
20233
|
-
import { spawnSync as
|
|
20675
|
+
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
20234
20676
|
function stopObligation(input, descriptor) {
|
|
20235
20677
|
try {
|
|
20236
20678
|
const session = input.session.id;
|
|
@@ -20294,7 +20736,7 @@ var NONE4, QUESTION, OFFER, YES_NO, LIST_ITEM, asksUser = (message) => {
|
|
|
20294
20736
|
`);
|
|
20295
20737
|
return QUESTION.test(tail) || OFFER.test(tail) || YES_NO.test(tail);
|
|
20296
20738
|
}, short = (sha) => (sha ?? "?").slice(0, 12), gitList = (cwd, args) => {
|
|
20297
|
-
const run =
|
|
20739
|
+
const run = spawnSync14("git", args, {
|
|
20298
20740
|
cwd,
|
|
20299
20741
|
encoding: "utf8",
|
|
20300
20742
|
timeout: 3000,
|
|
@@ -20606,9 +21048,9 @@ var init_skill_nudge = __esm(() => {
|
|
|
20606
21048
|
|
|
20607
21049
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
20608
21050
|
import { existsSync as existsSync7, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
20609
|
-
import
|
|
21051
|
+
import path24 from "node:path";
|
|
20610
21052
|
var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value), nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "", optionalText = (value) => typeof value === "string" ? value : null, existingDirectory = (value) => {
|
|
20611
|
-
if (!nonEmpty2(value) || !
|
|
21053
|
+
if (!nonEmpty2(value) || !path24.isAbsolute(value) || !existsSync7(value))
|
|
20612
21054
|
return null;
|
|
20613
21055
|
try {
|
|
20614
21056
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
@@ -20713,8 +21155,7 @@ function handleHook(input, deps) {
|
|
|
20713
21155
|
const raw = canDeny ? rawGitPre(input, event.command, { nudge: false }) : NONE5;
|
|
20714
21156
|
if (raw.kind === "deny")
|
|
20715
21157
|
return raw;
|
|
20716
|
-
const
|
|
20717
|
-
const gate = writes.writes ? writeGate(input.cwd, writes.targets) : NONE5;
|
|
21158
|
+
const gate = usable2(descriptor.events["write.pre"].support) ? shellGate(input.cwd, event.command, liveOwner(input, descriptor.transport), event.dialect) : NONE5;
|
|
20718
21159
|
if (gate.kind === "deny")
|
|
20719
21160
|
return gate;
|
|
20720
21161
|
noteRawCommit(input, event.command, postTool ? callKey(event.toolUseId, event.command) : NEXT_COMMAND);
|
|
@@ -20727,7 +21168,7 @@ function handleHook(input, deps) {
|
|
|
20727
21168
|
rawGitPost(input, event.command, callKey(event.toolUseId, event.command));
|
|
20728
21169
|
return NONE5;
|
|
20729
21170
|
case "write.pre":
|
|
20730
|
-
return usable2(descriptor.events["write.pre"].support) ? writeGate(input.cwd, event.paths) : NONE5;
|
|
21171
|
+
return usable2(descriptor.events["write.pre"].support) ? writeGate(input.cwd, event.paths, liveOwner(input, descriptor.transport)) : NONE5;
|
|
20731
21172
|
case "subagent.start":
|
|
20732
21173
|
return {
|
|
20733
21174
|
kind: "context",
|
|
@@ -20810,6 +21251,7 @@ var init_handle = __esm(() => {
|
|
|
20810
21251
|
init_raw_git();
|
|
20811
21252
|
init_skill_nudge();
|
|
20812
21253
|
init_stop();
|
|
21254
|
+
init_live_session();
|
|
20813
21255
|
init_write_gate();
|
|
20814
21256
|
NONE5 = { kind: "none" };
|
|
20815
21257
|
offered = new Set;
|
|
@@ -21348,8 +21790,8 @@ var init_codex = __esm(() => {
|
|
|
21348
21790
|
});
|
|
21349
21791
|
|
|
21350
21792
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
21351
|
-
import
|
|
21352
|
-
import
|
|
21793
|
+
import fs18 from "node:fs";
|
|
21794
|
+
import path25 from "node:path";
|
|
21353
21795
|
var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (value) => {
|
|
21354
21796
|
if (!isRecord4(value) || !Object.hasOwn(EVENTS3, String(value.hook_event_name)))
|
|
21355
21797
|
return { ok: false, error: "hook_event_name is required" };
|
|
@@ -21430,17 +21872,17 @@ var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (valu
|
|
|
21430
21872
|
};
|
|
21431
21873
|
}
|
|
21432
21874
|
}, TRANSCRIPT_TAIL_BYTES, lastAssistantText = (file) => {
|
|
21433
|
-
if (!file || !
|
|
21875
|
+
if (!file || !path25.isAbsolute(file))
|
|
21434
21876
|
return null;
|
|
21435
21877
|
try {
|
|
21436
|
-
const size =
|
|
21878
|
+
const size = fs18.statSync(file).size;
|
|
21437
21879
|
const start = Math.max(0, size - TRANSCRIPT_TAIL_BYTES);
|
|
21438
21880
|
const buffer = Buffer.alloc(size - start);
|
|
21439
|
-
const fd =
|
|
21881
|
+
const fd = fs18.openSync(file, "r");
|
|
21440
21882
|
try {
|
|
21441
|
-
|
|
21883
|
+
fs18.readSync(fd, buffer, 0, buffer.length, start);
|
|
21442
21884
|
} finally {
|
|
21443
|
-
|
|
21885
|
+
fs18.closeSync(fd);
|
|
21444
21886
|
}
|
|
21445
21887
|
const lines = buffer.toString("utf8").split(`
|
|
21446
21888
|
`);
|
|
@@ -21466,7 +21908,7 @@ var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (valu
|
|
|
21466
21908
|
} catch {
|
|
21467
21909
|
return null;
|
|
21468
21910
|
}
|
|
21469
|
-
}, within = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
21911
|
+
}, within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path25.sep) ? root : `${root}${path25.sep}`), hookCwd = (input) => {
|
|
21470
21912
|
const cwd = existingDirectory(input.cwd);
|
|
21471
21913
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|
|
21472
21914
|
return cwd;
|
|
@@ -21692,6 +22134,7 @@ var init_hooks = __esm(async () => {
|
|
|
21692
22134
|
init_context();
|
|
21693
22135
|
init_policy();
|
|
21694
22136
|
init_write_gate();
|
|
22137
|
+
init_live_session();
|
|
21695
22138
|
init_stop();
|
|
21696
22139
|
init_raw_git();
|
|
21697
22140
|
init_skill_nudge();
|