@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.
package/dist/workit-hook.js
CHANGED
|
@@ -7548,6 +7548,8 @@ var text = string2().check((ctx) => {
|
|
|
7548
7548
|
ctx.issues.push({ code: "custom", input: ctx.value, message: "invalid Unicode", path: [] });
|
|
7549
7549
|
});
|
|
7550
7550
|
var id = text.regex(uuidPattern, "expected lowercase UUID");
|
|
7551
|
+
var idRefPattern = /^[0-9a-f][0-9a-f-]{7,35}$/;
|
|
7552
|
+
var idRef = text.regex(idRefPattern, "expected a UUID or an id prefix of 8+ characters");
|
|
7551
7553
|
var digest = text.regex(digestPattern, "expected lowercase SHA-256 digest");
|
|
7552
7554
|
var revision = text.regex(uuidPattern, "expected UUID revision");
|
|
7553
7555
|
var validUtc = (value) => {
|
|
@@ -7925,7 +7927,7 @@ var workerReportSchema = object({
|
|
|
7925
7927
|
outcome: _enum(["completed", "failed", "cancelled"]),
|
|
7926
7928
|
summary: text,
|
|
7927
7929
|
evidenceIds: array(id),
|
|
7928
|
-
findingIds: array(
|
|
7930
|
+
findingIds: array(idRef)
|
|
7929
7931
|
}).strict();
|
|
7930
7932
|
var workerSchema = object({
|
|
7931
7933
|
assignment: assignmentSchema,
|
|
@@ -8171,7 +8173,7 @@ var findingOperations = {
|
|
|
8171
8173
|
action: literal("resolve"),
|
|
8172
8174
|
...taskId,
|
|
8173
8175
|
expectedRevision: revision.optional(),
|
|
8174
|
-
findingId:
|
|
8176
|
+
findingId: idRef,
|
|
8175
8177
|
disposition: _enum(["open", "fixed", "dismissed", "deferred"]),
|
|
8176
8178
|
reason: text,
|
|
8177
8179
|
evidenceIds: array(id),
|
|
@@ -8212,7 +8214,7 @@ var workerOperations = {
|
|
|
8212
8214
|
...taskId,
|
|
8213
8215
|
expectedRevision: revision.optional(),
|
|
8214
8216
|
expectedWorkspaceRevision: revision.optional(),
|
|
8215
|
-
workerId:
|
|
8217
|
+
workerId: idRef,
|
|
8216
8218
|
report: workerReportSchema
|
|
8217
8219
|
}),
|
|
8218
8220
|
cancel: operation({
|
|
@@ -8220,7 +8222,7 @@ var workerOperations = {
|
|
|
8220
8222
|
...taskId,
|
|
8221
8223
|
...revisions,
|
|
8222
8224
|
expectedWorkspaceRevision: revision.optional(),
|
|
8223
|
-
workerId:
|
|
8225
|
+
workerId: idRef,
|
|
8224
8226
|
reason: text
|
|
8225
8227
|
})
|
|
8226
8228
|
};
|
|
@@ -8270,6 +8272,16 @@ var success = (revisionValue, workspaceRevisionValue, data) => ({
|
|
|
8270
8272
|
data
|
|
8271
8273
|
});
|
|
8272
8274
|
var failure2 = (code, error, details = {}) => ({ ok: false, schemaVersion: 1, code, error, details });
|
|
8275
|
+
function resolveIdRef(kind, ref, ids) {
|
|
8276
|
+
if (ids.includes(ref))
|
|
8277
|
+
return success(null, null, ref);
|
|
8278
|
+
const matches = ids.filter((candidate) => candidate.startsWith(ref));
|
|
8279
|
+
if (matches.length === 1)
|
|
8280
|
+
return success(null, null, matches[0]);
|
|
8281
|
+
if (matches.length === 0)
|
|
8282
|
+
return failure2("not_found", `${kind} not found`);
|
|
8283
|
+
return failure2("invalid_input", `${kind} prefix ${ref} is ambiguous; candidates: ${matches.join(", ")}`);
|
|
8284
|
+
}
|
|
8273
8285
|
function parseOperation(family, input) {
|
|
8274
8286
|
if (typeof input === "object" && input !== null && "schemaVersion" in input && input.schemaVersion !== SCHEMA_VERSION)
|
|
8275
8287
|
return failure2("unsupported_version", "unsupported schema version", { operation: family });
|
|
@@ -9355,6 +9367,8 @@ var workspaceConfigSchema = object({
|
|
|
9355
9367
|
defaultEndpoint: unknown().optional(),
|
|
9356
9368
|
verification: _enum(["self", "independent"]).optional(),
|
|
9357
9369
|
trailers: boolean2().optional(),
|
|
9370
|
+
liveSessionGuard: boolean2().optional(),
|
|
9371
|
+
fanout: object({ tierModels: unknown().optional() }).passthrough().optional(),
|
|
9358
9372
|
autonomy: object(Object.fromEntries(["push", "pr", "merge", "release", "rerun"].map((kind) => [
|
|
9359
9373
|
kind,
|
|
9360
9374
|
union([boolean2(), literal("verified")]).optional()
|
|
@@ -9672,24 +9686,39 @@ var resolveRuntimeReleaseTracks = (cwd) => {
|
|
|
9672
9686
|
const matched = matchRuntimeWorkspace(cwd);
|
|
9673
9687
|
return matched ? { workspace: matched.selected.name, releaseTracks: matched.selected.releaseTracks } : null;
|
|
9674
9688
|
};
|
|
9675
|
-
var
|
|
9689
|
+
var inheritedSetting = (cwd, dir, has) => {
|
|
9676
9690
|
let matched;
|
|
9677
9691
|
try {
|
|
9678
9692
|
matched = matchRuntimeWorkspace(cwd, dir);
|
|
9679
9693
|
} catch {
|
|
9680
|
-
return
|
|
9694
|
+
return null;
|
|
9681
9695
|
}
|
|
9682
9696
|
if (!matched)
|
|
9683
|
-
return
|
|
9697
|
+
return null;
|
|
9684
9698
|
const { selected, matches } = matched;
|
|
9685
9699
|
const enclosing = matches.filter((entry) => entry !== selected).toSorted((a, b) => globSpecificity(b.glob) - globSpecificity(a.glob));
|
|
9686
|
-
|
|
9700
|
+
return { workspace: selected.name, source: [selected, ...enclosing].find(has) ?? null };
|
|
9701
|
+
};
|
|
9702
|
+
var workspaceTrailers = (cwd, dir) => {
|
|
9703
|
+
const found = inheritedSetting(cwd, dir, (entry) => typeof entry.trailers === "boolean");
|
|
9687
9704
|
return {
|
|
9688
|
-
enabled: source?.trailers !== false,
|
|
9689
|
-
workspace:
|
|
9690
|
-
from: source?.name ?? null
|
|
9705
|
+
enabled: found?.source?.trailers !== false,
|
|
9706
|
+
workspace: found?.workspace ?? null,
|
|
9707
|
+
from: found?.source?.name ?? null
|
|
9691
9708
|
};
|
|
9692
9709
|
};
|
|
9710
|
+
var workspaceLiveSessionGuard = (cwd, dir) => inheritedSetting(cwd, dir, (entry) => typeof entry.liveSessionGuard === "boolean")?.source?.liveSessionGuard !== false;
|
|
9711
|
+
var TIER_KEYS = new Set(["mundane", "standard", "hard"]);
|
|
9712
|
+
var TIER_HOSTS = new Set([
|
|
9713
|
+
"claude_code",
|
|
9714
|
+
"opencode",
|
|
9715
|
+
"codex",
|
|
9716
|
+
"codex_cli",
|
|
9717
|
+
"codex_desktop",
|
|
9718
|
+
"cursor",
|
|
9719
|
+
"pi",
|
|
9720
|
+
"default"
|
|
9721
|
+
]);
|
|
9693
9722
|
var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
9694
9723
|
const matched = matchRuntimeWorkspace(cwd);
|
|
9695
9724
|
if (!matched)
|
|
@@ -13614,20 +13643,26 @@ class TaskStore {
|
|
|
13614
13643
|
return success(null, null, { key: key.data, bound: bound.data });
|
|
13615
13644
|
}
|
|
13616
13645
|
taskForWorker(workerId) {
|
|
13617
|
-
if (typeof workerId !== "string" || !
|
|
13646
|
+
if (typeof workerId !== "string" || !idRefPattern.test(workerId))
|
|
13618
13647
|
return failure2("invalid_input", "needs a worker id: `workit worker cancel|report <workerId>`");
|
|
13619
13648
|
const listed = this.listTaskIndex();
|
|
13620
13649
|
if (!listed.ok)
|
|
13621
13650
|
return listed;
|
|
13651
|
+
const owners = new Map;
|
|
13622
13652
|
for (const entry of listed.data) {
|
|
13623
13653
|
if (entry.status === "closed")
|
|
13624
13654
|
continue;
|
|
13625
13655
|
const loaded = this.load(entry.id);
|
|
13626
13656
|
if (!loaded.ok)
|
|
13627
13657
|
return loaded;
|
|
13628
|
-
|
|
13629
|
-
|
|
13658
|
+
for (const worker of loaded.data?.record.workers ?? [])
|
|
13659
|
+
owners.set(worker.id, entry.id);
|
|
13630
13660
|
}
|
|
13661
|
+
const resolved = resolveIdRef("worker", workerId, [...owners.keys()]);
|
|
13662
|
+
if (resolved.ok)
|
|
13663
|
+
return success(null, null, owners.get(resolved.data) ?? null);
|
|
13664
|
+
if (resolved.code !== "not_found")
|
|
13665
|
+
return resolved;
|
|
13631
13666
|
return success(null, null, null);
|
|
13632
13667
|
}
|
|
13633
13668
|
originTaskEntry() {
|
|
@@ -16253,6 +16288,8 @@ var TASK_SCOPED_ACTIONS = new Set([
|
|
|
16253
16288
|
"worker.cancel",
|
|
16254
16289
|
"state.export"
|
|
16255
16290
|
]);
|
|
16291
|
+
var ONLY_OWN_REPORT = "workers can submit only their own report";
|
|
16292
|
+
var NO_HELPER_CONTROL = "helpers cannot cancel or control workers";
|
|
16256
16293
|
var WORKER_ADDRESSED_ACTIONS = new Set(["worker.cancel", "worker.report"]);
|
|
16257
16294
|
var workerCommand = (taskId, request) => {
|
|
16258
16295
|
const { action, workerId, reason, report } = request;
|
|
@@ -17150,6 +17187,10 @@ class WorkitCore {
|
|
|
17150
17187
|
}
|
|
17151
17188
|
if (helper?.ok)
|
|
17152
17189
|
return failure2("permission_denied", "helpers cannot resolve findings");
|
|
17190
|
+
const findingRef = resolveIdRef("finding", input.findingId, task.data.findings.map((entry) => entry.id));
|
|
17191
|
+
if (!findingRef.ok)
|
|
17192
|
+
return findingRef;
|
|
17193
|
+
input.findingId = findingRef.data;
|
|
17153
17194
|
const finding = task.data.findings.find((entry) => entry.id === input.findingId);
|
|
17154
17195
|
if (!finding)
|
|
17155
17196
|
return failure2("not_found", "finding not found");
|
|
@@ -17301,15 +17342,31 @@ class WorkitCore {
|
|
|
17301
17342
|
return changed;
|
|
17302
17343
|
return success(changed.data.task.revision, changed.data.workspace.revision, changed.data.task.workers.at(-1));
|
|
17303
17344
|
}
|
|
17345
|
+
const workerRef = resolveIdRef("worker", input.workerId, helperId === null ? task.data.workers.map((candidate) => candidate.id) : [helperId]);
|
|
17346
|
+
if (!workerRef.ok)
|
|
17347
|
+
return helperId === null ? workerRef : failure2("permission_denied", input.action === "report" ? ONLY_OWN_REPORT : NO_HELPER_CONTROL);
|
|
17348
|
+
input.workerId = workerRef.data;
|
|
17304
17349
|
const entry = task.data.workers.find((candidate) => candidate.id === input.workerId);
|
|
17305
17350
|
if (!entry)
|
|
17306
17351
|
return failure2("not_found", "worker not found");
|
|
17307
17352
|
if (input.action === "report") {
|
|
17308
17353
|
if (helperId === null || helperId !== input.workerId)
|
|
17309
|
-
return failure2("permission_denied",
|
|
17354
|
+
return failure2("permission_denied", ONLY_OWN_REPORT);
|
|
17310
17355
|
const helper = this.helperEntry(task.data, true, true);
|
|
17311
17356
|
if (!helper.ok)
|
|
17312
17357
|
return helper;
|
|
17358
|
+
const ownFindings = task.data.findings.filter((candidate) => candidate.provenance.workerId === helperId).map((candidate) => candidate.id);
|
|
17359
|
+
const reported = [];
|
|
17360
|
+
for (const findingRef of input.report.findingIds) {
|
|
17361
|
+
const found = resolveIdRef("finding", findingRef, ownFindings);
|
|
17362
|
+
if (found.ok)
|
|
17363
|
+
reported.push(found.data);
|
|
17364
|
+
else if (found.code === "not_found")
|
|
17365
|
+
reported.push(findingRef);
|
|
17366
|
+
else
|
|
17367
|
+
return found;
|
|
17368
|
+
}
|
|
17369
|
+
input.report.findingIds = reported;
|
|
17313
17370
|
const evidence = task.data.evidence.filter((candidate) => input.report.evidenceIds.includes(candidate.id));
|
|
17314
17371
|
const findings = task.data.findings.filter((candidate) => input.report.findingIds.includes(candidate.id));
|
|
17315
17372
|
if (evidence.length !== input.report.evidenceIds.length || findings.length !== input.report.findingIds.length)
|
|
@@ -17337,7 +17394,7 @@ class WorkitCore {
|
|
|
17337
17394
|
return updated ? success(changed.data.task.revision, changed.data.workspace.revision, updated) : failure2("recovery_required", "worker disappeared during report");
|
|
17338
17395
|
}
|
|
17339
17396
|
if (helperId !== null)
|
|
17340
|
-
return failure2("permission_denied",
|
|
17397
|
+
return failure2("permission_denied", NO_HELPER_CONTROL);
|
|
17341
17398
|
if (task.data.status === "closed")
|
|
17342
17399
|
return failure2("invalid_transition", "closed task cannot cancel workers");
|
|
17343
17400
|
if (entry.data.state === "stopped") {
|
|
@@ -20220,27 +20277,231 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
|
|
|
20220
20277
|
};
|
|
20221
20278
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
20222
20279
|
import { realpathSync as realpathSync5 } from "node:fs";
|
|
20280
|
+
import os7 from "node:os";
|
|
20281
|
+
import path23 from "node:path";
|
|
20282
|
+
|
|
20283
|
+
// packages/workit-core/src/core/live-session.ts
|
|
20284
|
+
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
20285
|
+
import { createHash as createHash15 } from "node:crypto";
|
|
20286
|
+
import fs17 from "node:fs";
|
|
20287
|
+
import os6 from "node:os";
|
|
20223
20288
|
import path22 from "node:path";
|
|
20289
|
+
var LIVE_SESSION_TTL_MS = 15 * 60000;
|
|
20290
|
+
var HEARTBEAT_EVERY_MS = 30000;
|
|
20291
|
+
var liveProbe = {
|
|
20292
|
+
platform: process.platform,
|
|
20293
|
+
run: (args) => spawnSync13("ps", args, { encoding: "utf8", timeout: 2000 }).stdout ?? "",
|
|
20294
|
+
start: (pid) => processStartOf(pid)
|
|
20295
|
+
};
|
|
20296
|
+
var leadOf = (session) => session.split(":")[0].replace(/-w-.*$/su, "") || session;
|
|
20297
|
+
var familyOf = (host) => host.replace(/_(?:cli|desktop)$/u, "");
|
|
20298
|
+
var processAlive = (pid) => {
|
|
20299
|
+
try {
|
|
20300
|
+
process.kill(pid, 0);
|
|
20301
|
+
return true;
|
|
20302
|
+
} catch (error) {
|
|
20303
|
+
return error.code === "EPERM";
|
|
20304
|
+
}
|
|
20305
|
+
};
|
|
20306
|
+
var processInfo = (pid) => {
|
|
20307
|
+
try {
|
|
20308
|
+
if (liveProbe.platform === "linux") {
|
|
20309
|
+
const stat = fs17.readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
20310
|
+
const ppid = Number(stat.slice(stat.lastIndexOf(")") + 2).split(" ")[1]);
|
|
20311
|
+
const command = fs17.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ");
|
|
20312
|
+
return Number.isInteger(ppid) ? { ppid, command: command.trim() } : null;
|
|
20313
|
+
}
|
|
20314
|
+
if (liveProbe.platform === "win32")
|
|
20315
|
+
return null;
|
|
20316
|
+
const match = /^\s*(\d+)\s+(.*)$/su.exec(liveProbe.run(["-o", "ppid=,command=", "-p", String(pid)]));
|
|
20317
|
+
return match ? { ppid: Number(match[1]), command: match[2].trim() } : null;
|
|
20318
|
+
} catch {
|
|
20319
|
+
return null;
|
|
20320
|
+
}
|
|
20321
|
+
};
|
|
20322
|
+
var 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;
|
|
20323
|
+
var PIDS = new Map;
|
|
20324
|
+
var hostPid = (transport) => {
|
|
20325
|
+
if (transport !== "hook-process")
|
|
20326
|
+
return process.pid;
|
|
20327
|
+
const key = `${liveProbe.platform}\x00${process.ppid}`;
|
|
20328
|
+
if (PIDS.has(key))
|
|
20329
|
+
return PIDS.get(key) ?? null;
|
|
20330
|
+
if (liveProbe.platform === "win32") {
|
|
20331
|
+
PIDS.set(key, null);
|
|
20332
|
+
return null;
|
|
20333
|
+
}
|
|
20334
|
+
let pid = process.ppid;
|
|
20335
|
+
for (let hop = 0;hop < 4; hop++) {
|
|
20336
|
+
const info = processInfo(pid);
|
|
20337
|
+
if (!info || !WRAPPER.test(info.command) || info.ppid <= 1)
|
|
20338
|
+
break;
|
|
20339
|
+
pid = info.ppid;
|
|
20340
|
+
}
|
|
20341
|
+
PIDS.set(key, pid);
|
|
20342
|
+
return pid;
|
|
20343
|
+
};
|
|
20344
|
+
var sessionSafe = (value) => value.replace(/[^A-Za-z0-9_.:@/+-]/gu, "-");
|
|
20345
|
+
var liveOwner = (input, transport, env = process.env) => {
|
|
20346
|
+
const session = input.session.id?.trim();
|
|
20347
|
+
if (!session)
|
|
20348
|
+
return null;
|
|
20349
|
+
const leads = new Set([leadOf(sessionSafe(session))]);
|
|
20350
|
+
if (input.session.parentId)
|
|
20351
|
+
leads.add(leadOf(sessionSafe(input.session.parentId)));
|
|
20352
|
+
if (env.WORKIT_PI_WORKER_SESSION && env.WORKIT_SESSION_ID)
|
|
20353
|
+
leads.add(leadOf(sessionSafe(env.WORKIT_SESSION_ID)));
|
|
20354
|
+
return {
|
|
20355
|
+
host: input.host,
|
|
20356
|
+
session: sessionSafe(session),
|
|
20357
|
+
leads: [...leads],
|
|
20358
|
+
pid: () => hostPid(transport)
|
|
20359
|
+
};
|
|
20360
|
+
};
|
|
20361
|
+
var checkoutOf = (target) => {
|
|
20362
|
+
let dir = path22.resolve(target);
|
|
20363
|
+
while (!fs17.existsSync(dir) && path22.dirname(dir) !== dir)
|
|
20364
|
+
dir = path22.dirname(dir);
|
|
20365
|
+
if (fs17.existsSync(dir) && !fs17.statSync(dir).isDirectory())
|
|
20366
|
+
dir = path22.dirname(dir);
|
|
20367
|
+
const parts = dir.split(path22.sep);
|
|
20368
|
+
const dotGit = parts.lastIndexOf(".git");
|
|
20369
|
+
if (dotGit > 0)
|
|
20370
|
+
dir = parts.slice(0, dotGit).join(path22.sep) || path22.sep;
|
|
20371
|
+
const location = resolveStore(dir);
|
|
20372
|
+
if (location instanceof Error || !location.shared || !location.top)
|
|
20373
|
+
return null;
|
|
20374
|
+
let top = location.top;
|
|
20375
|
+
try {
|
|
20376
|
+
top = fs17.realpathSync.native(top);
|
|
20377
|
+
} catch {}
|
|
20378
|
+
return top;
|
|
20379
|
+
};
|
|
20380
|
+
var markerFile2 = (top) => path22.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
|
|
20381
|
+
var checkoutKey = (top, platform = process.platform) => {
|
|
20382
|
+
let key = path22.resolve(top);
|
|
20383
|
+
try {
|
|
20384
|
+
key = fs17.realpathSync.native(key);
|
|
20385
|
+
} catch {}
|
|
20386
|
+
return platform === "win32" ? key.replaceAll("/", "\\").toLowerCase() : key;
|
|
20387
|
+
};
|
|
20388
|
+
var readMarker = (file) => {
|
|
20389
|
+
try {
|
|
20390
|
+
const value = JSON.parse(fs17.readFileSync(file, "utf8"));
|
|
20391
|
+
if (value?.version !== 1 || typeof value.session !== "string" || typeof value.host !== "string" || typeof value.heartbeatAt !== "string" || Number.isNaN(Date.parse(value.heartbeatAt)))
|
|
20392
|
+
return null;
|
|
20393
|
+
return {
|
|
20394
|
+
version: 1,
|
|
20395
|
+
session: value.session,
|
|
20396
|
+
leads: Array.isArray(value.leads) ? value.leads.filter((item) => typeof item === "string") : [],
|
|
20397
|
+
host: value.host,
|
|
20398
|
+
pid: typeof value.pid === "number" ? value.pid : null,
|
|
20399
|
+
pidStart: typeof value.pidStart === "string" ? value.pidStart : null,
|
|
20400
|
+
machine: typeof value.machine === "string" ? value.machine : "",
|
|
20401
|
+
checkout: typeof value.checkout === "string" ? value.checkout : "",
|
|
20402
|
+
heartbeatAt: value.heartbeatAt
|
|
20403
|
+
};
|
|
20404
|
+
} catch {
|
|
20405
|
+
return null;
|
|
20406
|
+
}
|
|
20407
|
+
};
|
|
20408
|
+
var ageOf2 = (marker, now) => now.getTime() - Date.parse(marker.heartbeatAt);
|
|
20409
|
+
var isLive = (marker, deps) => {
|
|
20410
|
+
const age = ageOf2(marker, deps.now ?? new Date);
|
|
20411
|
+
if (age > (deps.ttlMs ?? LIVE_SESSION_TTL_MS))
|
|
20412
|
+
return false;
|
|
20413
|
+
if (marker.machine !== (deps.machine ?? os6.hostname()) || marker.pid === null)
|
|
20414
|
+
return true;
|
|
20415
|
+
if (!(deps.alive ?? processAlive)(marker.pid))
|
|
20416
|
+
return false;
|
|
20417
|
+
if (marker.pidStart === null)
|
|
20418
|
+
return true;
|
|
20419
|
+
const start = liveProbe.start(marker.pid);
|
|
20420
|
+
return start === null || start === marker.pidStart;
|
|
20421
|
+
};
|
|
20422
|
+
var sameLead = (marker, owner) => marker.session === owner.session || owner.leads.some((lead) => lead === marker.session || marker.leads.includes(lead));
|
|
20423
|
+
var sameOwner = (marker, owner, machine) => {
|
|
20424
|
+
if (sameLead(marker, owner))
|
|
20425
|
+
return true;
|
|
20426
|
+
if (marker.machine !== machine)
|
|
20427
|
+
return false;
|
|
20428
|
+
const pid = owner.pid();
|
|
20429
|
+
if (pid === null || marker.pid === null || marker.pidStart === null)
|
|
20430
|
+
return familyOf(marker.host) === familyOf(owner.host);
|
|
20431
|
+
return marker.pid === pid;
|
|
20432
|
+
};
|
|
20433
|
+
var liveConflict = (target, owner, deps = {}) => {
|
|
20434
|
+
try {
|
|
20435
|
+
const checkout = checkoutOf(target);
|
|
20436
|
+
if (!checkout)
|
|
20437
|
+
return null;
|
|
20438
|
+
const file = markerFile2(checkout);
|
|
20439
|
+
const marker = readMarker(file);
|
|
20440
|
+
if (!marker || sameLead(marker, owner) || !isLive(marker, deps))
|
|
20441
|
+
return null;
|
|
20442
|
+
if (sameOwner(marker, owner, deps.machine ?? os6.hostname()))
|
|
20443
|
+
return null;
|
|
20444
|
+
return { marker, file, ageMs: ageOf2(marker, deps.now ?? new Date), checkout };
|
|
20445
|
+
} catch {
|
|
20446
|
+
return null;
|
|
20447
|
+
}
|
|
20448
|
+
};
|
|
20449
|
+
var claimLiveSession = (target, owner, deps = {}) => {
|
|
20450
|
+
try {
|
|
20451
|
+
const checkout = checkoutOf(target);
|
|
20452
|
+
if (!checkout)
|
|
20453
|
+
return;
|
|
20454
|
+
const file = markerFile2(checkout);
|
|
20455
|
+
const now = deps.now ?? new Date;
|
|
20456
|
+
const machine = deps.machine ?? os6.hostname();
|
|
20457
|
+
const current = readMarker(file);
|
|
20458
|
+
if (current) {
|
|
20459
|
+
const age = ageOf2(current, now);
|
|
20460
|
+
if (sameLead(current, owner) && age >= 0 && age < HEARTBEAT_EVERY_MS)
|
|
20461
|
+
return;
|
|
20462
|
+
if (!sameLead(current, owner) && isLive(current, deps) && !sameOwner(current, owner, machine))
|
|
20463
|
+
return;
|
|
20464
|
+
}
|
|
20465
|
+
const pid = owner.pid();
|
|
20466
|
+
const marker = {
|
|
20467
|
+
version: 1,
|
|
20468
|
+
session: owner.session,
|
|
20469
|
+
leads: owner.leads,
|
|
20470
|
+
host: owner.host,
|
|
20471
|
+
pid,
|
|
20472
|
+
pidStart: pid === null ? null : liveProbe.start(pid),
|
|
20473
|
+
machine,
|
|
20474
|
+
checkout,
|
|
20475
|
+
heartbeatAt: now.toISOString()
|
|
20476
|
+
};
|
|
20477
|
+
fs17.mkdirSync(path22.dirname(file), { recursive: true });
|
|
20478
|
+
const temp = `${file}.${process.pid}.tmp`;
|
|
20479
|
+
fs17.writeFileSync(temp, JSON.stringify(marker));
|
|
20480
|
+
fs17.renameSync(temp, file);
|
|
20481
|
+
} catch {}
|
|
20482
|
+
};
|
|
20483
|
+
|
|
20484
|
+
// packages/workit-core/src/hooks/write-gate.ts
|
|
20224
20485
|
var NONE3 = { kind: "none" };
|
|
20225
20486
|
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
20226
20487
|
var canonical2 = (file) => {
|
|
20227
20488
|
let rest = "";
|
|
20228
|
-
for (let dir =
|
|
20489
|
+
for (let dir = path23.resolve(file);; dir = path23.dirname(dir)) {
|
|
20229
20490
|
try {
|
|
20230
|
-
return
|
|
20491
|
+
return path23.join(realpathSync5(dir), rest);
|
|
20231
20492
|
} catch {
|
|
20232
|
-
if (
|
|
20233
|
-
return
|
|
20234
|
-
rest = rest ?
|
|
20493
|
+
if (path23.dirname(dir) === dir)
|
|
20494
|
+
return path23.resolve(file);
|
|
20495
|
+
rest = rest ? path23.join(path23.basename(dir), rest) : path23.basename(dir);
|
|
20235
20496
|
}
|
|
20236
20497
|
}
|
|
20237
20498
|
};
|
|
20238
20499
|
var exempt = (cwd, root, file, plans) => {
|
|
20239
20500
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
20240
|
-
const relative =
|
|
20241
|
-
if (relative.startsWith("..") ||
|
|
20501
|
+
const relative = path23.relative(fold(canonical2(root)), fold(canonical2(path23.resolve(cwd, file))));
|
|
20502
|
+
if (relative.startsWith("..") || path23.isAbsolute(relative))
|
|
20242
20503
|
return true;
|
|
20243
|
-
const posix = relative.split(
|
|
20504
|
+
const posix = relative.split(path23.sep).join("/");
|
|
20244
20505
|
const segments = posix.split("/");
|
|
20245
20506
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
20246
20507
|
};
|
|
@@ -20338,7 +20599,7 @@ var shellWrites = (command) => {
|
|
|
20338
20599
|
return { writes: true, targets: unknown ? null : targets };
|
|
20339
20600
|
};
|
|
20340
20601
|
var cache2 = new Map;
|
|
20341
|
-
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
20602
|
+
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path23.resolve(root, file)) ?? "-").join("\x00");
|
|
20342
20603
|
var gateFor = (cwd) => {
|
|
20343
20604
|
const store = new TaskStore(cwd);
|
|
20344
20605
|
const listed = store.listTaskIndex();
|
|
@@ -20376,7 +20637,218 @@ var gateFor = (cwd) => {
|
|
|
20376
20637
|
cache2.delete(cache2.keys().next().value);
|
|
20377
20638
|
return gate;
|
|
20378
20639
|
};
|
|
20379
|
-
|
|
20640
|
+
var 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
|
+
var DRY_N = new Set(["add", "clean", "rm", "mv"]);
|
|
20660
|
+
var MUTATING_WORKIT_GIT = new Set(["commit", "branch"]);
|
|
20661
|
+
var DIR_TARGET = ".workit-live-target";
|
|
20662
|
+
var OVERRIDE = "WORKIT_LIVE_SESSION=override";
|
|
20663
|
+
var 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("--"));
|
|
20664
|
+
var gitMutationDirs = (cwd, command, dialect) => {
|
|
20665
|
+
let invocations;
|
|
20666
|
+
try {
|
|
20667
|
+
invocations = rawInvocations(command, dialect);
|
|
20668
|
+
} catch {
|
|
20669
|
+
return [];
|
|
20670
|
+
}
|
|
20671
|
+
return invocations.flatMap((invocation) => {
|
|
20672
|
+
if (invocation.tool !== "git" || invocation.dir === undefined)
|
|
20673
|
+
return [];
|
|
20674
|
+
const at = invocation.args.findIndex((arg) => !arg.startsWith("-"));
|
|
20675
|
+
const sub = invocation.args[at] ?? "";
|
|
20676
|
+
const rest = invocation.args.slice(at + 1);
|
|
20677
|
+
if (!MUTATING_GIT.has(sub) || inert(rest, sub))
|
|
20678
|
+
return [];
|
|
20679
|
+
if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
|
|
20680
|
+
return [];
|
|
20681
|
+
return [path23.resolve(cwd, invocation.dir ?? ".")];
|
|
20682
|
+
});
|
|
20683
|
+
};
|
|
20684
|
+
var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
20685
|
+
var PUSHD2 = new Set(["pushd", "push-location"]);
|
|
20686
|
+
var POPD2 = new Set(["popd", "pop-location"]);
|
|
20687
|
+
var cdInto = (dir, words) => {
|
|
20688
|
+
const target = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
|
|
20689
|
+
if (dir === undefined || target === "-" || target && /^~|[$`*?]/.test(target))
|
|
20690
|
+
return;
|
|
20691
|
+
return target === undefined ? os7.homedir() : path23.resolve(dir, target);
|
|
20692
|
+
};
|
|
20693
|
+
var quoteWord = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
|
|
20694
|
+
var segmentTargets = (cwd, command, dialect) => {
|
|
20695
|
+
const targets = [];
|
|
20696
|
+
let dir = cwd;
|
|
20697
|
+
let stack = [];
|
|
20698
|
+
const groups = [];
|
|
20699
|
+
const at = (file) => path23.isAbsolute(file) ? file : dir === undefined ? null : path23.resolve(dir, file);
|
|
20700
|
+
for (const segment of segmentsOf(command, dialect)) {
|
|
20701
|
+
if (segment.group === "open") {
|
|
20702
|
+
groups.push({ dir, stack: [...stack] });
|
|
20703
|
+
continue;
|
|
20704
|
+
}
|
|
20705
|
+
if (segment.group === "close") {
|
|
20706
|
+
const saved = groups.pop();
|
|
20707
|
+
if (saved)
|
|
20708
|
+
({ dir, stack } = saved);
|
|
20709
|
+
continue;
|
|
20710
|
+
}
|
|
20711
|
+
const lead = segment.words.findIndex((word) => !/^\w+=/.test(word));
|
|
20712
|
+
const words = lead < 0 ? [] : segment.words.slice(lead);
|
|
20713
|
+
const name = (words[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
|
|
20714
|
+
if (CD2.has(name)) {
|
|
20715
|
+
dir = cdInto(dir, words);
|
|
20716
|
+
continue;
|
|
20717
|
+
}
|
|
20718
|
+
if (PUSHD2.has(name)) {
|
|
20719
|
+
stack.push(dir);
|
|
20720
|
+
dir = cdInto(dir, words);
|
|
20721
|
+
continue;
|
|
20722
|
+
}
|
|
20723
|
+
if (POPD2.has(name)) {
|
|
20724
|
+
dir = stack.length ? stack.pop() : undefined;
|
|
20725
|
+
continue;
|
|
20726
|
+
}
|
|
20727
|
+
for (const file of segment.redirects)
|
|
20728
|
+
if (!/^\/dev\/(?:null|stdout|stderr|tty)$/.test(file)) {
|
|
20729
|
+
const resolved = at(file);
|
|
20730
|
+
if (resolved)
|
|
20731
|
+
targets.push(resolved);
|
|
20732
|
+
}
|
|
20733
|
+
const edits = editsOf(segment.words);
|
|
20734
|
+
if (edits === null) {
|
|
20735
|
+
if (dir !== undefined)
|
|
20736
|
+
targets.push(path23.join(dir, DIR_TARGET));
|
|
20737
|
+
} else
|
|
20738
|
+
for (const file of edits) {
|
|
20739
|
+
const resolved = at(file);
|
|
20740
|
+
if (resolved)
|
|
20741
|
+
targets.push(resolved);
|
|
20742
|
+
}
|
|
20743
|
+
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), "")))
|
|
20744
|
+
targets.push(path23.join(dir, DIR_TARGET));
|
|
20745
|
+
}
|
|
20746
|
+
return targets;
|
|
20747
|
+
};
|
|
20748
|
+
var overridden = (command, dialect) => {
|
|
20749
|
+
const segments = segmentsOf(command, dialect);
|
|
20750
|
+
if (segments.length !== 1 || segments[0].group || segments[0].subs?.length)
|
|
20751
|
+
return false;
|
|
20752
|
+
const words = segments[0].words;
|
|
20753
|
+
const lead = words.findIndex((word) => !/^\w+=/.test(word));
|
|
20754
|
+
return lead > 0 && words.slice(0, lead).includes(OVERRIDE);
|
|
20755
|
+
};
|
|
20756
|
+
var HOST_LABELS = {
|
|
20757
|
+
claude_code: "Claude Code",
|
|
20758
|
+
opencode: "OpenCode",
|
|
20759
|
+
codex_cli: "Codex CLI",
|
|
20760
|
+
codex_desktop: "Codex Desktop",
|
|
20761
|
+
cursor: "Cursor",
|
|
20762
|
+
pi: "Pi"
|
|
20763
|
+
};
|
|
20764
|
+
var ageText = (ms) => {
|
|
20765
|
+
const seconds = Math.max(0, Math.round(ms / 1000));
|
|
20766
|
+
return seconds < 120 ? `${seconds}s` : `${Math.round(seconds / 60)} min`;
|
|
20767
|
+
};
|
|
20768
|
+
var liveDeny = ({ marker, checkout, ageMs, file }) => {
|
|
20769
|
+
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}.`;
|
|
20770
|
+
return {
|
|
20771
|
+
kind: "deny",
|
|
20772
|
+
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}`,
|
|
20773
|
+
unblock
|
|
20774
|
+
};
|
|
20775
|
+
};
|
|
20776
|
+
var guarded = (targets) => {
|
|
20777
|
+
const seen = new Set;
|
|
20778
|
+
return targets.filter((target) => {
|
|
20779
|
+
const dir = path23.dirname(target);
|
|
20780
|
+
if (seen.has(dir))
|
|
20781
|
+
return false;
|
|
20782
|
+
seen.add(dir);
|
|
20783
|
+
return true;
|
|
20784
|
+
});
|
|
20785
|
+
};
|
|
20786
|
+
var liveGate = (owner, targets, deps) => {
|
|
20787
|
+
try {
|
|
20788
|
+
for (const target of guarded(targets)) {
|
|
20789
|
+
const conflict = liveConflict(target, owner, deps);
|
|
20790
|
+
if (conflict && workspaceLiveSessionGuard(conflict.checkout))
|
|
20791
|
+
return liveDeny(conflict);
|
|
20792
|
+
}
|
|
20793
|
+
} catch {}
|
|
20794
|
+
return NONE3;
|
|
20795
|
+
};
|
|
20796
|
+
var claimAll = (owner, targets, deps) => {
|
|
20797
|
+
for (const target of guarded(targets))
|
|
20798
|
+
claimLiveSession(target, owner, deps);
|
|
20799
|
+
};
|
|
20800
|
+
var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path23.resolve(cwd, file)) : [path23.join(cwd, DIR_TARGET)];
|
|
20801
|
+
function writeGate(cwd, paths, owner, deps) {
|
|
20802
|
+
if (!owner)
|
|
20803
|
+
return beforeWriteGate(cwd, paths);
|
|
20804
|
+
const targets = liveTargets(cwd, paths);
|
|
20805
|
+
const live = liveGate(owner, targets, deps);
|
|
20806
|
+
if (live.kind !== "none")
|
|
20807
|
+
return live;
|
|
20808
|
+
const decision = beforeWriteGate(cwd, paths);
|
|
20809
|
+
if (decision.kind === "none")
|
|
20810
|
+
claimAll(owner, targets, deps);
|
|
20811
|
+
return decision;
|
|
20812
|
+
}
|
|
20813
|
+
function shellGate(cwd, command, owner, dialect = "posix", deps) {
|
|
20814
|
+
const writes = shellWrites(command);
|
|
20815
|
+
let targets = [];
|
|
20816
|
+
if (owner) {
|
|
20817
|
+
try {
|
|
20818
|
+
targets = [
|
|
20819
|
+
...segmentTargets(cwd, command, dialect),
|
|
20820
|
+
...gitMutationDirs(cwd, command, dialect).map((dir) => path23.join(dir, DIR_TARGET))
|
|
20821
|
+
];
|
|
20822
|
+
} catch {
|
|
20823
|
+
targets = [];
|
|
20824
|
+
}
|
|
20825
|
+
}
|
|
20826
|
+
if (owner && targets.length) {
|
|
20827
|
+
const live = liveGate(owner, targets, deps);
|
|
20828
|
+
if (live.kind !== "none") {
|
|
20829
|
+
if (!overridden(command, dialect))
|
|
20830
|
+
return live;
|
|
20831
|
+
noteOverride(cwd, owner, command);
|
|
20832
|
+
}
|
|
20833
|
+
}
|
|
20834
|
+
const decision = writes.writes ? beforeWriteGate(cwd, writes.targets) : NONE3;
|
|
20835
|
+
if (owner && targets.length && decision.kind === "none")
|
|
20836
|
+
claimAll(owner, targets, deps);
|
|
20837
|
+
return decision;
|
|
20838
|
+
}
|
|
20839
|
+
var noteOverride = (cwd, owner, command) => {
|
|
20840
|
+
try {
|
|
20841
|
+
if (!inWorkitWorkspace(cwd))
|
|
20842
|
+
return;
|
|
20843
|
+
appendHookObserved(cwd, {
|
|
20844
|
+
type: "live_session.override",
|
|
20845
|
+
actor: { host: owner.host, session: owner.session, agentId: null },
|
|
20846
|
+
session: owner.session,
|
|
20847
|
+
command: command.slice(0, 200)
|
|
20848
|
+
});
|
|
20849
|
+
} catch {}
|
|
20850
|
+
};
|
|
20851
|
+
function beforeWriteGate(cwd, paths) {
|
|
20380
20852
|
try {
|
|
20381
20853
|
const gate = gateFor(cwd);
|
|
20382
20854
|
if (!gate || gate.blockers.length === 0)
|
|
@@ -20394,7 +20866,7 @@ function writeGate(cwd, paths) {
|
|
|
20394
20866
|
}
|
|
20395
20867
|
}
|
|
20396
20868
|
// packages/workit-core/src/hooks/stop.ts
|
|
20397
|
-
import { spawnSync as
|
|
20869
|
+
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
20398
20870
|
var NONE4 = { kind: "none" };
|
|
20399
20871
|
var QUESTION = /[??؟](?=[\s*_`"')\]>]*(?:\s|$))|¿/;
|
|
20400
20872
|
var OFFER = /\b(?:let me know|want me to|should I|shall I|would you like|do you want|if you(?:'d| would) like|may I|can I go ahead)\b/i;
|
|
@@ -20415,7 +20887,7 @@ var asksUser = (message) => {
|
|
|
20415
20887
|
};
|
|
20416
20888
|
var short = (sha) => (sha ?? "?").slice(0, 12);
|
|
20417
20889
|
var gitList = (cwd, args) => {
|
|
20418
|
-
const run =
|
|
20890
|
+
const run = spawnSync14("git", args, {
|
|
20419
20891
|
cwd,
|
|
20420
20892
|
encoding: "utf8",
|
|
20421
20893
|
timeout: 3000,
|
|
@@ -20779,12 +21251,12 @@ ${line}` };
|
|
|
20779
21251
|
};
|
|
20780
21252
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
20781
21253
|
import { existsSync as existsSync7, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
20782
|
-
import
|
|
21254
|
+
import path24 from "node:path";
|
|
20783
21255
|
var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
20784
21256
|
var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
|
|
20785
21257
|
var optionalText = (value) => typeof value === "string" ? value : null;
|
|
20786
21258
|
var existingDirectory = (value) => {
|
|
20787
|
-
if (!nonEmpty2(value) || !
|
|
21259
|
+
if (!nonEmpty2(value) || !path24.isAbsolute(value) || !existsSync7(value))
|
|
20788
21260
|
return null;
|
|
20789
21261
|
try {
|
|
20790
21262
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
@@ -20929,8 +21401,7 @@ function handleHook(input, deps) {
|
|
|
20929
21401
|
const raw = canDeny ? rawGitPre(input, event.command, { nudge: false }) : NONE5;
|
|
20930
21402
|
if (raw.kind === "deny")
|
|
20931
21403
|
return raw;
|
|
20932
|
-
const
|
|
20933
|
-
const gate = writes.writes ? writeGate(input.cwd, writes.targets) : NONE5;
|
|
21404
|
+
const gate = usable2(descriptor.events["write.pre"].support) ? shellGate(input.cwd, event.command, liveOwner(input, descriptor.transport), event.dialect) : NONE5;
|
|
20934
21405
|
if (gate.kind === "deny")
|
|
20935
21406
|
return gate;
|
|
20936
21407
|
noteRawCommit(input, event.command, postTool ? callKey(event.toolUseId, event.command) : NEXT_COMMAND);
|
|
@@ -20943,7 +21414,7 @@ function handleHook(input, deps) {
|
|
|
20943
21414
|
rawGitPost(input, event.command, callKey(event.toolUseId, event.command));
|
|
20944
21415
|
return NONE5;
|
|
20945
21416
|
case "write.pre":
|
|
20946
|
-
return usable2(descriptor.events["write.pre"].support) ? writeGate(input.cwd, event.paths) : NONE5;
|
|
21417
|
+
return usable2(descriptor.events["write.pre"].support) ? writeGate(input.cwd, event.paths, liveOwner(input, descriptor.transport)) : NONE5;
|
|
20947
21418
|
case "subagent.start":
|
|
20948
21419
|
return {
|
|
20949
21420
|
kind: "context",
|
|
@@ -21522,8 +21993,8 @@ var codexAdapter = {
|
|
|
21522
21993
|
};
|
|
21523
21994
|
|
|
21524
21995
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
21525
|
-
import
|
|
21526
|
-
import
|
|
21996
|
+
import fs18 from "node:fs";
|
|
21997
|
+
import path25 from "node:path";
|
|
21527
21998
|
var undocumented = { support: "undocumented", native: null };
|
|
21528
21999
|
var none = { support: "none", native: null };
|
|
21529
22000
|
var CURSOR_DESCRIPTOR = {
|
|
@@ -21720,17 +22191,17 @@ var protocolEvent2 = (input) => {
|
|
|
21720
22191
|
};
|
|
21721
22192
|
var TRANSCRIPT_TAIL_BYTES = 256 * 1024;
|
|
21722
22193
|
var lastAssistantText = (file) => {
|
|
21723
|
-
if (!file || !
|
|
22194
|
+
if (!file || !path25.isAbsolute(file))
|
|
21724
22195
|
return null;
|
|
21725
22196
|
try {
|
|
21726
|
-
const size =
|
|
22197
|
+
const size = fs18.statSync(file).size;
|
|
21727
22198
|
const start = Math.max(0, size - TRANSCRIPT_TAIL_BYTES);
|
|
21728
22199
|
const buffer = Buffer.alloc(size - start);
|
|
21729
|
-
const fd =
|
|
22200
|
+
const fd = fs18.openSync(file, "r");
|
|
21730
22201
|
try {
|
|
21731
|
-
|
|
22202
|
+
fs18.readSync(fd, buffer, 0, buffer.length, start);
|
|
21732
22203
|
} finally {
|
|
21733
|
-
|
|
22204
|
+
fs18.closeSync(fd);
|
|
21734
22205
|
}
|
|
21735
22206
|
const lines = buffer.toString("utf8").split(`
|
|
21736
22207
|
`);
|
|
@@ -21757,7 +22228,7 @@ var lastAssistantText = (file) => {
|
|
|
21757
22228
|
return null;
|
|
21758
22229
|
}
|
|
21759
22230
|
};
|
|
21760
|
-
var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
22231
|
+
var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path25.sep) ? root : `${root}${path25.sep}`);
|
|
21761
22232
|
var hookCwd = (input) => {
|
|
21762
22233
|
const cwd = existingDirectory(input.cwd);
|
|
21763
22234
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|