teamshare-bridge 0.11.0 → 0.11.2

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.
Files changed (68) hide show
  1. package/dist/bridge/daemon.js +89 -0
  2. package/dist/bridge/daemon.js.map +1 -1
  3. package/dist/bridge/local-server.d.ts +5 -0
  4. package/dist/bridge/local-server.js +195 -0
  5. package/dist/bridge/local-server.js.map +1 -1
  6. package/dist/bridge/spawn.d.ts +10 -0
  7. package/dist/bridge/spawn.js +78 -15
  8. package/dist/bridge/spawn.js.map +1 -1
  9. package/dist/cli/commands/build.js +7 -2
  10. package/dist/cli/commands/build.js.map +1 -1
  11. package/dist/cli/commands/chat.js +5 -0
  12. package/dist/cli/commands/chat.js.map +1 -1
  13. package/dist/cli/commands/cleanup.js +28 -1
  14. package/dist/cli/commands/cleanup.js.map +1 -1
  15. package/dist/cli/commands/doc.js +4 -0
  16. package/dist/cli/commands/doc.js.map +1 -1
  17. package/dist/cli/commands/link.js +30 -2
  18. package/dist/cli/commands/link.js.map +1 -1
  19. package/dist/cli/commands/run.js +13 -4
  20. package/dist/cli/commands/run.js.map +1 -1
  21. package/dist/cli/commands/worker.js +18 -1
  22. package/dist/cli/commands/worker.js.map +1 -1
  23. package/dist/lib/api.js +18 -0
  24. package/dist/lib/api.js.map +1 -1
  25. package/dist/lib/brief.js +5 -0
  26. package/dist/lib/brief.js.map +1 -1
  27. package/dist/lib/cache-cleaner.d.ts +51 -0
  28. package/dist/lib/cache-cleaner.js +403 -0
  29. package/dist/lib/cache-cleaner.js.map +1 -0
  30. package/dist/lib/chat-reply.js +1 -0
  31. package/dist/lib/chat-reply.js.map +1 -1
  32. package/dist/lib/config.d.ts +38 -1
  33. package/dist/lib/config.js +23 -2
  34. package/dist/lib/config.js.map +1 -1
  35. package/dist/lib/git-pipeline.d.ts +18 -3
  36. package/dist/lib/git-pipeline.js +82 -4
  37. package/dist/lib/git-pipeline.js.map +1 -1
  38. package/dist/lib/orchestrator/client.js +10 -4
  39. package/dist/lib/orchestrator/client.js.map +1 -1
  40. package/dist/lib/orchestrator/runner.d.ts +6 -1
  41. package/dist/lib/orchestrator/runner.js +86 -14
  42. package/dist/lib/orchestrator/runner.js.map +1 -1
  43. package/dist/lib/orchestrator/worktree.d.ts +24 -0
  44. package/dist/lib/orchestrator/worktree.js +99 -0
  45. package/dist/lib/orchestrator/worktree.js.map +1 -1
  46. package/dist/lib/rate-gate.d.ts +42 -0
  47. package/dist/lib/rate-gate.js +128 -0
  48. package/dist/lib/rate-gate.js.map +1 -0
  49. package/dist/lib/workspace/bus.d.ts +30 -0
  50. package/dist/lib/workspace/bus.js +101 -0
  51. package/dist/lib/workspace/bus.js.map +1 -0
  52. package/dist/lib/workspace/classify.d.ts +32 -0
  53. package/dist/lib/workspace/classify.js +197 -0
  54. package/dist/lib/workspace/classify.js.map +1 -0
  55. package/dist/lib/workspace/presence.d.ts +26 -0
  56. package/dist/lib/workspace/presence.js +130 -0
  57. package/dist/lib/workspace/presence.js.map +1 -0
  58. package/dist/lib/workspace/repo-lock.d.ts +90 -0
  59. package/dist/lib/workspace/repo-lock.js +296 -0
  60. package/dist/lib/workspace/repo-lock.js.map +1 -0
  61. package/dist/lib/workspace/shim-runner.d.ts +1 -0
  62. package/dist/lib/workspace/shim-runner.js +233 -0
  63. package/dist/lib/workspace/shim-runner.js.map +1 -0
  64. package/dist/lib/workspace/shims.d.ts +9 -0
  65. package/dist/lib/workspace/shims.js +88 -0
  66. package/dist/lib/workspace/shims.js.map +1 -0
  67. package/package.json +1 -1
  68. package/tools/workspace-arbitration-check.mjs +390 -0
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ /**
3
+ * Command classification (IMP-950): decides how intrusive a shell command
4
+ * is before anyone runs it. One table feeds BOTH arbitration layers:
5
+ * - bridge-owned spawns (orchestrator runner) arbitrate in-process;
6
+ * - PATH shims consult this via the arbiter's acquire endpoint.
7
+ *
8
+ * Philosophy: known-mutating commands are exclusive; known-read-only are
9
+ * shared; long-lived dev servers take a shared hold for their lifetime so
10
+ * an exclusive seeker can SEE them (and ping) without being blocked forever;
11
+ * everything else passes through untouched - fail-open beats bricking.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.classifyCommand = classifyCommand;
15
+ const BUILD_SCRIPTS = new Set([
16
+ 'build', 'compile', 'dist', 'package', 'generate', 'gen', 'prisma',
17
+ 'migrate', 'seed', 'db:', 'prepare', 'prepack', 'postinstall', 'clean',
18
+ ]);
19
+ const CHECK_SCRIPTS = new Set([
20
+ 'test', 't', 'lint', 'typecheck', 'type-check', 'check', 'spec', 'cover',
21
+ ]);
22
+ const SERVE_SCRIPTS = new Set(['dev', 'start', 'serve', 'preview', 'watch']);
23
+ /** git subcommands that rewrite working tree / branch state. */
24
+ const GIT_EXCLUSIVE = new Set([
25
+ 'checkout', 'switch', 'reset', 'clean', 'rebase', 'merge', 'cherry-pick',
26
+ 'revert', 'worktree', 'apply', 'restore', 'bisect', 'init', 'clone',
27
+ 'am', 'gc', 'filter-branch', 'submodule',
28
+ ]);
29
+ /** git subcommands safe to run concurrently (index.lock serializes writes). */
30
+ const GIT_SHARED = new Set([
31
+ 'status', 'diff', 'log', 'show', 'branch', 'add', 'commit', 'tag',
32
+ 'notes', 'fetch', 'ls-files', 'rev-parse', 'describe', 'stash', 'push',
33
+ ]);
34
+ function base(commandLine) {
35
+ const parts = commandLine.trim().split(/\s+/);
36
+ const rawBin = (parts.shift() ?? '').toLowerCase();
37
+ // Strip path prefixes and Windows extensions: C:\x\npm.CMD -> npm
38
+ const bin = rawBin.split(/[\\/]/).pop().replace(/\.(exe|cmd|bat|ps1)$/i, '');
39
+ return { bin, rest: parts.filter((p) => p !== '') };
40
+ }
41
+ function scriptName(rest) {
42
+ const flagSkip = /^(--|-)/;
43
+ for (const p of rest)
44
+ if (!flagSkip.test(p))
45
+ return p.toLowerCase();
46
+ return '';
47
+ }
48
+ /**
49
+ * Classify a full command line. Never throws - unknown input is
50
+ * passthrough.
51
+ */
52
+ function classifyCommand(commandLine) {
53
+ try {
54
+ const { bin, rest } = base(commandLine);
55
+ switch (bin) {
56
+ case 'npm':
57
+ case 'pnpm':
58
+ case 'yarn':
59
+ case 'bun':
60
+ case 'bunx':
61
+ return classifyPackageManager(bin, rest);
62
+ case 'npx':
63
+ return classifyNpx(rest);
64
+ case 'prisma':
65
+ return classifyPrisma(rest);
66
+ case 'git':
67
+ return classifyGit(rest);
68
+ case 'tsc': {
69
+ const noEmit = rest.some((a) => a === '--noEmit' || a.startsWith('--noEmit'));
70
+ return noEmit
71
+ ? { cls: 'shared', label: 'tsc --noEmit' }
72
+ : { cls: 'exclusive', label: 'tsc (emit)' };
73
+ }
74
+ case 'eslint':
75
+ case 'prettier':
76
+ case 'biome':
77
+ return { cls: 'shared', label: bin };
78
+ case 'jest':
79
+ case 'vitest':
80
+ case 'mocha':
81
+ case 'pytest':
82
+ return { cls: 'shared', label: bin };
83
+ case 'pip':
84
+ case 'pip3': {
85
+ const sub = (rest[0] ?? '').toLowerCase();
86
+ if (sub === 'install' || sub === 'uninstall') {
87
+ return { cls: 'exclusive', label: `${bin} ${sub}`, global: ['pip-install'] };
88
+ }
89
+ return { cls: 'passthrough', label: commandLine.slice(0, 60) };
90
+ }
91
+ default:
92
+ // node/python/echo/... : too broad to guard meaningfully - pass.
93
+ return { cls: 'passthrough', label: commandLine.slice(0, 60) };
94
+ }
95
+ }
96
+ catch {
97
+ return { cls: 'passthrough', label: String(commandLine).slice(0, 60) };
98
+ }
99
+ }
100
+ function classifyPackageManager(bin, rest) {
101
+ const sub = (rest[0] ?? '').toLowerCase().replace(/^:/, '');
102
+ const args = rest.slice(1);
103
+ switch (sub) {
104
+ case 'install':
105
+ case 'i':
106
+ case 'add':
107
+ case 'ci':
108
+ case 'update':
109
+ case 'upgrade':
110
+ case 'remove':
111
+ case 'uninstall':
112
+ case 'rm':
113
+ case 'dedupe':
114
+ case 'rebuild':
115
+ case 'link':
116
+ case 'unlink':
117
+ case 'pack':
118
+ case 'publish':
119
+ return { cls: 'exclusive', label: `${bin} ${rest[0]}` };
120
+ case 'run':
121
+ case 'run-script':
122
+ case 'exec':
123
+ case 'x': {
124
+ const name = scriptName(args);
125
+ if (!name)
126
+ return { cls: 'exclusive', label: `${bin} ${sub}` };
127
+ if (SERVE_SCRIPTS.has(name))
128
+ return { cls: 'long-shared', label: `${bin} run ${name}` };
129
+ if (CHECK_SCRIPTS.has(name) || name.startsWith('test')) {
130
+ return { cls: 'shared', label: `${bin} run ${name}` };
131
+ }
132
+ if (name.startsWith('db:') || name.includes('migrat') || name.includes('generat')) {
133
+ return { cls: 'exclusive', label: `${bin} run ${name}`, global: ['prisma-generate'] };
134
+ }
135
+ // Unknown/custom scripts may emit or mutate anything - be safe.
136
+ return { cls: 'exclusive', label: `${bin} run ${name}` };
137
+ }
138
+ case 'test':
139
+ case 'lint':
140
+ return { cls: 'shared', label: `${bin} ${sub}` };
141
+ case 'start':
142
+ case 'dev':
143
+ return { cls: 'long-shared', label: `${bin} ${sub}` };
144
+ case 'workspaces':
145
+ case 'ws':
146
+ return { cls: 'exclusive', label: `${bin} workspaces` };
147
+ default:
148
+ // init/cache/config/binary shortcuts etc. - assume mutation-capable.
149
+ return { cls: 'exclusive', label: `${bin} ${sub || '(default)'}`.trim() };
150
+ }
151
+ }
152
+ function classifyNpx(rest) {
153
+ const target = scriptName(rest);
154
+ if (target === 'prisma') {
155
+ const inner = classifyPrisma(rest.slice(rest.indexOf('prisma') + 1));
156
+ return { ...inner, global: inner.global ?? (inner.cls === 'exclusive' ? ['prisma-generate'] : undefined), label: `npx ${inner.label}` };
157
+ }
158
+ if (['tsc', 'eslint', 'jest', 'vitest', 'mocha'].includes(target)) {
159
+ return { cls: 'shared', label: `npx ${target}` };
160
+ }
161
+ // Unknown npx target may install + run anything.
162
+ return { cls: 'exclusive', label: `npx ${target || '(default)'}`.trim() };
163
+ }
164
+ function classifyPrisma(rest) {
165
+ const sub = (rest[0] ?? '').toLowerCase();
166
+ const mutating = sub === 'generate' ||
167
+ sub.startsWith('migrate') ||
168
+ sub === 'db' ||
169
+ sub === 'pull' ||
170
+ sub === 'push' ||
171
+ sub === 'studio' ||
172
+ sub === 'format';
173
+ if (!mutating)
174
+ return { cls: 'passthrough', label: `prisma ${sub}`.trim() };
175
+ // generate/migrate load the native query engine - serialize machine-wide
176
+ // (documented gotcha: two generates deadlock on the engine DLL).
177
+ const needsGlobal = sub === 'generate' || sub.startsWith('migrate');
178
+ return {
179
+ cls: 'exclusive',
180
+ label: `prisma ${sub}`,
181
+ ...(needsGlobal ? { global: ['prisma-generate'] } : {}),
182
+ };
183
+ }
184
+ function classifyGit(rest) {
185
+ const sub = (rest[0] ?? '').toLowerCase();
186
+ if (GIT_EXCLUSIVE.has(sub))
187
+ return { cls: 'exclusive', label: `git ${sub}` };
188
+ if (GIT_SHARED.has(sub)) {
189
+ // stash pop/apply mutates tree+index like checkout would.
190
+ if (sub === 'stash' && rest[1] && !['list', 'show'].includes((rest[1] ?? '').toLowerCase())) {
191
+ return { cls: 'exclusive', label: 'git stash pop' };
192
+ }
193
+ return { cls: 'shared', label: `git ${sub}` };
194
+ }
195
+ return { cls: 'passthrough', label: `git ${sub}`.trim() };
196
+ }
197
+ //# sourceMappingURL=classify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classify.js","sourceRoot":"","sources":["../../../src/lib/workspace/classify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /** A crashed session disappears from "who is here" after 10 minutes. */
2
+ export declare const PRESENCE_TTL_MS: number;
3
+ export interface PresenceEntry {
4
+ pid: number;
5
+ startedAt: string;
6
+ updatedAt?: string;
7
+ agentId?: string;
8
+ kind?: string;
9
+ taskId?: string;
10
+ projectId?: string;
11
+ }
12
+ /** Live occupants of `path`, oldest first. */
13
+ export declare function listPresent(path: string): PresenceEntry[];
14
+ /**
15
+ * Register (or heartbeat-refresh) THIS process in the folder. Deduped by
16
+ * pid - calling it again just refreshes timestamps.
17
+ */
18
+ export declare function registerPresence(path: string, info?: Omit<PresenceEntry, 'pid' | 'startedAt' | 'updatedAt'>): void;
19
+ /** Remove THIS process from the folder's registry. */
20
+ export declare function unregisterPresence(path: string): void;
21
+ /**
22
+ * True when at least one OTHER live process occupies the folder. Used by
23
+ * the daemon's auto-worktree escalation ("second agent showed up") and by
24
+ * status endpoints.
25
+ */
26
+ export declare function hasOtherOccupants(path: string, excludePid?: number): boolean;
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PRESENCE_TTL_MS = void 0;
4
+ exports.listPresent = listPresent;
5
+ exports.registerPresence = registerPresence;
6
+ exports.unregisterPresence = unregisterPresence;
7
+ exports.hasOtherOccupants = hasOtherOccupants;
8
+ /**
9
+ * Presence registry (IMP-950): who is working WHERE right now.
10
+ *
11
+ * Complements repo-lock: locks guard a single intrusive command; presence
12
+ * answers "is another agent occupying this folder at all" - which is what
13
+ * auto-worktree escalation and /arbitrate/status need.
14
+ *
15
+ * One JSON file per directory scope, array of entries. Each session
16
+ * registers on start, heartbeats are implicit (staleness = dead pid or
17
+ * older than PRESENCE_TTL_MS), and the entry is removed on exit via the
18
+ * same finally blocks that release the per-agent lock. Cross-process by
19
+ * design; every call is best-effort - presence must never break a session.
20
+ */
21
+ const node_fs_1 = require("node:fs");
22
+ const node_os_1 = require("node:os");
23
+ const node_path_1 = require("node:path");
24
+ const repo_lock_1 = require("./repo-lock");
25
+ const PRESENCE_ROOT = process.env.TS_PRESENCE_ROOT && process.env.TS_PRESENCE_ROOT.trim()
26
+ ? process.env.TS_PRESENCE_ROOT
27
+ : (0, node_path_1.join)((0, node_os_1.homedir)(), '.teamshare', 'presence');
28
+ /** A crashed session disappears from "who is here" after 10 minutes. */
29
+ exports.PRESENCE_TTL_MS = 10 * 60_000;
30
+ function presenceFile(path) {
31
+ return (0, node_path_1.join)(PRESENCE_ROOT, `${(0, repo_lock_1.scopeKey)({ kind: 'dir', path })}.json`);
32
+ }
33
+ function pidAlive(pid) {
34
+ if (!Number.isInteger(pid) || pid <= 0)
35
+ return false;
36
+ try {
37
+ process.kill(pid, 0);
38
+ return true;
39
+ }
40
+ catch (err) {
41
+ return err.code === 'EPERM';
42
+ }
43
+ }
44
+ function readEntries(path) {
45
+ const file = presenceFile(path);
46
+ try {
47
+ const raw = JSON.parse((0, node_fs_1.readFileSync)(file, 'utf8'));
48
+ if (!Array.isArray(raw))
49
+ return [];
50
+ return raw.filter((e) => e && typeof e.pid === 'number').map((e) => ({ ...e, file }));
51
+ }
52
+ catch {
53
+ return [];
54
+ }
55
+ }
56
+ function writeEntries(path, entries) {
57
+ (0, node_fs_1.mkdirSync)(PRESENCE_ROOT, { recursive: true });
58
+ if (entries.length === 0) {
59
+ try {
60
+ (0, node_fs_1.rmSync)(presenceFile(path), { force: true });
61
+ }
62
+ catch {
63
+ /* best-effort */
64
+ }
65
+ return;
66
+ }
67
+ try {
68
+ (0, node_fs_1.writeFileSync)(presenceFile(path), JSON.stringify(entries, null, 2), 'utf8');
69
+ }
70
+ catch {
71
+ /* best-effort */
72
+ }
73
+ }
74
+ function isLive(entry) {
75
+ if (!pidAlive(entry.pid))
76
+ return false;
77
+ const ref = entry.updatedAt ?? entry.startedAt;
78
+ const age = Date.now() - new Date(ref).getTime();
79
+ return Number.isFinite(age) && age <= exports.PRESENCE_TTL_MS;
80
+ }
81
+ /** Live occupants of `path`, oldest first. */
82
+ function listPresent(path) {
83
+ return readEntries(path)
84
+ .filter(isLive)
85
+ .map(({ file: _file, ...entry }) => entry);
86
+ }
87
+ /**
88
+ * Register (or heartbeat-refresh) THIS process in the folder. Deduped by
89
+ * pid - calling it again just refreshes timestamps.
90
+ */
91
+ function registerPresence(path, info = {}) {
92
+ try {
93
+ const canonical = (0, repo_lock_1.canonicalDir)(path);
94
+ const others = readEntries(canonical).filter((e) => e.pid !== process.pid);
95
+ const mine = readEntries(canonical).find((e) => e.pid === process.pid);
96
+ const now = new Date().toISOString();
97
+ const entry = {
98
+ pid: process.pid,
99
+ startedAt: mine?.startedAt ?? now,
100
+ updatedAt: now,
101
+ ...info,
102
+ };
103
+ // Write against the canonical spelling so lookups agree regardless of
104
+ // how the caller spelled the path.
105
+ writeEntries(canonical, [...others, entry]);
106
+ }
107
+ catch {
108
+ /* best-effort */
109
+ }
110
+ }
111
+ /** Remove THIS process from the folder's registry. */
112
+ function unregisterPresence(path) {
113
+ try {
114
+ const canonical = (0, repo_lock_1.canonicalDir)(path);
115
+ const remaining = readEntries(canonical).filter((e) => e.pid !== process.pid);
116
+ writeEntries(canonical, remaining.map(({ file: _f, ...e }) => e));
117
+ }
118
+ catch {
119
+ /* best-effort */
120
+ }
121
+ }
122
+ /**
123
+ * True when at least one OTHER live process occupies the folder. Used by
124
+ * the daemon's auto-worktree escalation ("second agent showed up") and by
125
+ * status endpoints.
126
+ */
127
+ function hasOtherOccupants(path, excludePid = process.pid) {
128
+ return readEntries(path).some((e) => e.pid !== excludePid && isLive(e));
129
+ }
130
+ //# sourceMappingURL=presence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presence.js","sourceRoot":"","sources":["../../../src/lib/workspace/presence.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ export type WsMode = 'shared' | 'exclusive';
2
+ export type WsScope = {
3
+ kind: 'dir';
4
+ path: string;
5
+ } | {
6
+ kind: 'global';
7
+ name: string;
8
+ };
9
+ export interface WsHolderInfo {
10
+ /** Agent id when known ("manual" for bare CLI use). */
11
+ agentId?: string;
12
+ kind?: string;
13
+ taskId?: string;
14
+ projectId?: string;
15
+ /** Free-text reason shown in pings ("npm install", "git finalize"). */
16
+ label?: string;
17
+ }
18
+ export interface WsHandle {
19
+ scope: WsScope;
20
+ mode: WsMode;
21
+ file: string;
22
+ pid: number;
23
+ startedAt: string;
24
+ }
25
+ export interface WsHolder {
26
+ pid: number;
27
+ mode: WsMode;
28
+ startedAt: string;
29
+ holder: WsHolderInfo;
30
+ /** Absolute path of the holder file (purge/release needs it). */
31
+ file: string;
32
+ stale: boolean;
33
+ }
34
+ /** Comfortably above any real build/install; crashed holders die by pid check anyway. */
35
+ export declare const WS_MAX_HOLD_MS: number;
36
+ export declare const ARBITRATION_LOG: string;
37
+ /** Canonical, case-folded identity of a working directory (win32-safe). */
38
+ export declare function canonicalDir(path: string): string;
39
+ export declare function scopeKey(scope: WsScope): string;
40
+ /** True when `file` lives inside the managed lock tree (release guard). */
41
+ export declare function isInsideLockRoot(file: string): boolean;
42
+ /** Live + stale holders for a scope (stale ones still listed for pings). */
43
+ export declare function readWsHolders(scope: WsScope): WsHolder[];
44
+ /** Holders by precomputed scope key (status endpoints scan the tree). */
45
+ export declare function readWsHoldersByKey(key: string): WsHolder[];
46
+ export type AcquireResult = {
47
+ ok: true;
48
+ handle: WsHandle;
49
+ } | {
50
+ ok: false;
51
+ reason: 'busy';
52
+ holders: WsHolder[];
53
+ } | {
54
+ ok: false;
55
+ reason: 'reentrant-conflict';
56
+ holders: WsHolder[];
57
+ };
58
+ /**
59
+ * One-shot acquire. Exclusive requires an EMPTY live-holder set; shared
60
+ * requires no live EXCLUSIVE holder. Same-pid same-mode is re-entrant
61
+ * (returns the registered handle); same-pid cross-mode refuses rather than
62
+ * corrupting state.
63
+ */
64
+ export declare function tryAcquireWsLock(scope: WsScope, mode: WsMode, info?: WsHolderInfo): AcquireResult;
65
+ /**
66
+ * Wait until the scope admits `mode`, then hold it. Returns null on
67
+ * deadline. `onWaiting` fires once per poll cycle while blocked (used to
68
+ * emit bus pings + console notes exactly like ../lock.ts).
69
+ */
70
+ export declare function acquireWsLock(scope: WsScope, mode: WsMode, info?: WsHolderInfo, opts?: {
71
+ waitMs?: number;
72
+ pollMs?: number;
73
+ onWaiting?: (holders: WsHolder[]) => void;
74
+ }): Promise<WsHandle | null>;
75
+ /** Release a handle this process owns (no-op otherwise). */
76
+ export declare function releaseWsLock(handle: WsHandle | null | undefined): void;
77
+ /** Release every handle this process still holds (crash/exit safety net). */
78
+ export declare function releaseAllWsLocks(): void;
79
+ /** Run `fn` under the lock; always releases, even on throw. */
80
+ export declare function withWsLock<T>(scope: WsScope, mode: WsMode, info: WsHolderInfo, fn: () => Promise<T>, opts?: {
81
+ waitMs?: number;
82
+ pollMs?: number;
83
+ onWaiting?: (h: WsHolder[]) => void;
84
+ }): Promise<T | null>;
85
+ /** Append-only human-readable trail (best-effort, size-capped). */
86
+ export declare function logArbitration(line: string): void;
87
+ /** Temp helper used by tests/E2E to fabricate a stale holder. */
88
+ export declare function __writeForeignHolder(scope: WsScope, mode: WsMode, pid: number, ageMs?: number): string;
89
+ /** Isolated root override for tests (never called in production paths). */
90
+ export declare function __setLockRootForTests(dir?: string): void;