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,296 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ARBITRATION_LOG = exports.WS_MAX_HOLD_MS = void 0;
4
+ exports.canonicalDir = canonicalDir;
5
+ exports.scopeKey = scopeKey;
6
+ exports.isInsideLockRoot = isInsideLockRoot;
7
+ exports.readWsHolders = readWsHolders;
8
+ exports.readWsHoldersByKey = readWsHoldersByKey;
9
+ exports.tryAcquireWsLock = tryAcquireWsLock;
10
+ exports.acquireWsLock = acquireWsLock;
11
+ exports.releaseWsLock = releaseWsLock;
12
+ exports.releaseAllWsLocks = releaseAllWsLocks;
13
+ exports.withWsLock = withWsLock;
14
+ exports.logArbitration = logArbitration;
15
+ exports.__writeForeignHolder = __writeForeignHolder;
16
+ exports.__setLockRootForTests = __setLockRootForTests;
17
+ /**
18
+ * Workspace arbitration: repo-scoped file locks (IMP-950).
19
+ *
20
+ * The per-agent session lock (../lock.ts) guarantees one SESSION per agent,
21
+ * but says nothing about WHERE it works. Two agents on two tasks of the same
22
+ * project legally share one linked folder - and their intrusive commands
23
+ * (`npm run build` deleting dist, `npm install` churning node_modules,
24
+ * branch checkouts, prisma generate's engine DLL) trample each other.
25
+ *
26
+ * This module is the second axis: locks keyed by WORKING DIRECTORY (plus a
27
+ * machine-global scope for engine DLLs), with reader/writer semantics:
28
+ * - shared readers coexist (tsc/eslint/jest/git status)
29
+ * - exclusive excludes every other holder (builds, installs, checkout)
30
+ *
31
+ * State lives on the FILESYSTEM so plain CLI runs arbitrate without the
32
+ * daemon; the localhost arbiter endpoints (bridge/local-server.ts) expose
33
+ * the same functions to PATH shims and external tools. Every holder is a
34
+ * separate `<mode>-<pid>-<ts>.json` file inside the scope directory, so
35
+ * shared holders never overwrite each other. Staleness = dead pid or age >
36
+ * MAX_HOLD_MS, mirroring lock.ts.
37
+ *
38
+ * Deadlock rules (documented, enforced by convention):
39
+ * - acquire at most ONE dir scope at a time;
40
+ * - when a global resource is also needed (prisma generate), take the dir
41
+ * lock FIRST, then the global one, always in that order;
42
+ * - re-entrancy: same pid + same scope + same mode returns the existing
43
+ * handle; exclusive-then-shared in-process upgrades are rejected.
44
+ */
45
+ const node_crypto_1 = require("node:crypto");
46
+ const node_fs_1 = require("node:fs");
47
+ const node_os_1 = require("node:os");
48
+ const node_path_1 = require("node:path");
49
+ const DEFAULT_LOCK_ROOT = (0, node_path_1.join)((0, node_os_1.homedir)(), '.teamshare', 'locks', 'ws');
50
+ /** Overridable for tests only - production code never touches this. */
51
+ let lockRoot = process.env.TS_WS_LOCK_ROOT && process.env.TS_WS_LOCK_ROOT.trim()
52
+ ? (0, node_path_1.resolve)(process.env.TS_WS_LOCK_ROOT)
53
+ : DEFAULT_LOCK_ROOT;
54
+ /** Comfortably above any real build/install; crashed holders die by pid check anyway. */
55
+ exports.WS_MAX_HOLD_MS = 8 * 60 * 60 * 1000;
56
+ exports.ARBITRATION_LOG = (0, node_path_1.join)((0, node_os_1.homedir)(), '.teamshare', 'arbitration.log');
57
+ // ── scope keys ──────────────────────────────────────────────────────────────
58
+ /** Canonical, case-folded identity of a working directory (win32-safe). */
59
+ function canonicalDir(path) {
60
+ const resolved = path ? path.trim() : '';
61
+ // realpathSync would fail on not-yet-existing dirs; resolve() is enough -
62
+ // collisions only matter when two spellings reach the same folder.
63
+ return resolved.replace(/[\\/]+$/, '').toLowerCase();
64
+ }
65
+ function scopeKey(scope) {
66
+ if (scope.kind === 'global')
67
+ return `g-${slug(scope.name)}`;
68
+ return `d-${(0, node_crypto_1.createHash)('sha256').update(canonicalDir(scope.path)).digest('hex').slice(0, 20)}`;
69
+ }
70
+ function slug(name) {
71
+ return name.replace(/[^a-zA-Z0-9_-]+/g, '-').slice(0, 40) || 'x';
72
+ }
73
+ function scopeDir(scope) {
74
+ return (0, node_path_1.join)(lockRoot, scopeKey(scope));
75
+ }
76
+ /** True when `file` lives inside the managed lock tree (release guard). */
77
+ function isInsideLockRoot(file) {
78
+ const resolved = (0, node_path_1.resolve)(file).toLowerCase();
79
+ const root = (0, node_path_1.resolve)(lockRoot).toLowerCase();
80
+ return resolved.startsWith(root);
81
+ }
82
+ // ── holder files ────────────────────────────────────────────────────────────
83
+ function pidAlive(pid) {
84
+ if (!Number.isInteger(pid) || pid <= 0)
85
+ return false;
86
+ try {
87
+ process.kill(pid, 0);
88
+ return true;
89
+ }
90
+ catch (err) {
91
+ return err.code === 'EPERM';
92
+ }
93
+ }
94
+ function readHolderFile(file) {
95
+ try {
96
+ const body = JSON.parse((0, node_fs_1.readFileSync)(file, 'utf8'));
97
+ if (!body || typeof body.pid !== 'number' || (body.mode !== 'shared' && body.mode !== 'exclusive')) {
98
+ return null;
99
+ }
100
+ return { ...body, file };
101
+ }
102
+ catch {
103
+ return null;
104
+ }
105
+ }
106
+ /** Live + stale holders for a scope (stale ones still listed for pings). */
107
+ function readWsHolders(scope) {
108
+ return readWsHoldersByKey(scopeKey(scope));
109
+ }
110
+ /** Holders by precomputed scope key (status endpoints scan the tree). */
111
+ function readWsHoldersByKey(key) {
112
+ const safe = key.replace(/[^a-zA-Z0-9_-]/g, '');
113
+ if (!safe)
114
+ return [];
115
+ const dir = (0, node_path_1.join)(lockRoot, safe);
116
+ if (!(0, node_fs_1.existsSync)(dir))
117
+ return [];
118
+ const now = Date.now();
119
+ const out = [];
120
+ for (const entry of (0, node_fs_1.readdirSync)(dir)) {
121
+ if (!entry.endsWith('.json'))
122
+ continue;
123
+ const h = readHolderFile((0, node_path_1.join)(dir, entry));
124
+ if (!h)
125
+ continue;
126
+ const age = now - new Date(h.startedAt).getTime();
127
+ out.push({
128
+ pid: h.pid,
129
+ mode: h.mode,
130
+ startedAt: h.startedAt,
131
+ holder: h.holder ?? {},
132
+ file: h.file,
133
+ stale: !pidAlive(h.pid) || !Number.isFinite(age) || age > exports.WS_MAX_HOLD_MS,
134
+ });
135
+ }
136
+ return out.sort((a, b) => a.startedAt.localeCompare(b.startedAt));
137
+ }
138
+ function liveHolders(scope) {
139
+ return readWsHolders(scope).filter((h) => !h.stale);
140
+ }
141
+ function writeHolderFile(scope, mode, info) {
142
+ const dir = scopeDir(scope);
143
+ (0, node_fs_1.mkdirSync)(dir, { recursive: true });
144
+ const startedAt = new Date().toISOString();
145
+ const body = { pid: process.pid, mode, startedAt, holder: info };
146
+ // Uniqueness: pid + ms timestamp is enough even for rapid re-acquire loops.
147
+ const file = (0, node_path_1.join)(dir, `${mode}-${process.pid}-${Date.now()}.json`);
148
+ (0, node_fs_1.writeFileSync)(file, JSON.stringify(body, null, 2), 'utf8');
149
+ return { scope, mode, file, pid: process.pid, startedAt };
150
+ }
151
+ // ── in-process registry (re-entrancy + reliable release) ───────────────────
152
+ const held = new Map();
153
+ function handleId(handle) {
154
+ return `${scopeKey(handle.scope)}|${handle.mode}`;
155
+ }
156
+ /**
157
+ * Re-entrancy lookup with self-healing: an externally released handle
158
+ * (HTTP /arbitrate/release removes the FILE without touching this
159
+ * process's map) must not keep shadowing fresh acquisitions - verify the
160
+ * backing file still exists and forget the entry when it does not.
161
+ */
162
+ function validHeldHandle(mapKey) {
163
+ const h = held.get(mapKey);
164
+ if (!h)
165
+ return null;
166
+ if (!(0, node_fs_1.existsSync)(h.file)) {
167
+ held.delete(mapKey);
168
+ return null;
169
+ }
170
+ return h;
171
+ }
172
+ /**
173
+ * One-shot acquire. Exclusive requires an EMPTY live-holder set; shared
174
+ * requires no live EXCLUSIVE holder. Same-pid same-mode is re-entrant
175
+ * (returns the registered handle); same-pid cross-mode refuses rather than
176
+ * corrupting state.
177
+ */
178
+ function tryAcquireWsLock(scope, mode, info = {}) {
179
+ const key = scopeKey(scope);
180
+ const existingSameMode = validHeldHandle(`${key}|${mode}`);
181
+ if (existingSameMode)
182
+ return { ok: true, handle: existingSameMode };
183
+ const otherMode = mode === 'exclusive' ? 'shared' : 'exclusive';
184
+ if (validHeldHandle(`${key}|${otherMode}`)) {
185
+ return { ok: false, reason: 'reentrant-conflict', holders: liveHolders(scope) };
186
+ }
187
+ // Purge stale holder files first - a dead build must never block anyone.
188
+ for (const h of readWsHolders(scope)) {
189
+ if (h.stale) {
190
+ try {
191
+ (0, node_fs_1.rmSync)(h.file, { force: true });
192
+ }
193
+ catch {
194
+ /* best-effort */
195
+ }
196
+ }
197
+ }
198
+ const live = liveHolders(scope);
199
+ if (mode === 'exclusive') {
200
+ if (live.length > 0 && !(live.length === 1 && live[0].pid === process.pid)) {
201
+ return { ok: false, reason: 'busy', holders: live };
202
+ }
203
+ }
204
+ else if (live.some((h) => h.mode === 'exclusive')) {
205
+ return { ok: false, reason: 'busy', holders: live };
206
+ }
207
+ const handle = writeHolderFile(scope, mode, info);
208
+ held.set(handleId(handle), handle);
209
+ return { ok: true, handle };
210
+ }
211
+ /**
212
+ * Wait until the scope admits `mode`, then hold it. Returns null on
213
+ * deadline. `onWaiting` fires once per poll cycle while blocked (used to
214
+ * emit bus pings + console notes exactly like ../lock.ts).
215
+ */
216
+ async function acquireWsLock(scope, mode, info = {}, opts = {}) {
217
+ const waitMs = opts.waitMs ?? 10 * 60_000;
218
+ const pollMs = opts.pollMs ?? 500;
219
+ const deadline = Date.now() + waitMs;
220
+ for (;;) {
221
+ const attempt = tryAcquireWsLock(scope, mode, info);
222
+ if (attempt.ok)
223
+ return attempt.handle;
224
+ if (attempt.reason === 'busy')
225
+ opts.onWaiting?.(attempt.holders);
226
+ if (Date.now() >= deadline)
227
+ return null;
228
+ await sleep(Math.min(pollMs, Math.max(50, deadline - Date.now())));
229
+ }
230
+ }
231
+ /** Release a handle this process owns (no-op otherwise). */
232
+ function releaseWsLock(handle) {
233
+ if (!handle)
234
+ return;
235
+ held.delete(handleId(handle));
236
+ try {
237
+ (0, node_fs_1.rmSync)(handle.file, { force: true });
238
+ }
239
+ catch {
240
+ /* best-effort */
241
+ }
242
+ }
243
+ /** Release every handle this process still holds (crash/exit safety net). */
244
+ function releaseAllWsLocks() {
245
+ for (const [, handle] of held)
246
+ releaseWsLock(handle);
247
+ }
248
+ /** Run `fn` under the lock; always releases, even on throw. */
249
+ async function withWsLock(scope, mode, info, fn, opts = {}) {
250
+ const handle = await acquireWsLock(scope, mode, info, opts);
251
+ if (!handle)
252
+ return null;
253
+ try {
254
+ return await fn();
255
+ }
256
+ finally {
257
+ releaseWsLock(handle);
258
+ }
259
+ }
260
+ function sleep(ms) {
261
+ return new Promise((resolve) => setTimeout(resolve, ms));
262
+ }
263
+ /** Append-only human-readable trail (best-effort, size-capped). */
264
+ function logArbitration(line) {
265
+ try {
266
+ (0, node_fs_1.mkdirSync)((0, node_path_1.join)(exports.ARBITRATION_LOG, '..'), { recursive: true });
267
+ const stamp = new Date().toISOString();
268
+ if ((0, node_fs_1.existsSync)(exports.ARBITRATION_LOG) && (0, node_fs_1.statSync)(exports.ARBITRATION_LOG).size > 2 * 1024 * 1024) {
269
+ // Rotate once past 2 MB - keep the newest half.
270
+ const old = (0, node_fs_1.readFileSync)(exports.ARBITRATION_LOG, 'utf8');
271
+ (0, node_fs_1.writeFileSync)(exports.ARBITRATION_LOG, old.slice(old.length - 1024 * 1024), 'utf8');
272
+ }
273
+ (0, node_fs_1.appendFileSync)(exports.ARBITRATION_LOG, `[${stamp}] ${line}\n`, 'utf8');
274
+ }
275
+ catch {
276
+ /* best-effort */
277
+ }
278
+ }
279
+ /** Temp helper used by tests/E2E to fabricate a stale holder. */
280
+ function __writeForeignHolder(scope, mode, pid, ageMs = 0) {
281
+ const dir = scopeDir(scope);
282
+ (0, node_fs_1.mkdirSync)(dir, { recursive: true });
283
+ const file = (0, node_path_1.join)(dir, `${mode}-${pid}-0.json`);
284
+ (0, node_fs_1.writeFileSync)(file, JSON.stringify({
285
+ pid,
286
+ mode,
287
+ startedAt: new Date(Date.now() - ageMs).toISOString(),
288
+ holder: { label: 'foreign' },
289
+ }), 'utf8');
290
+ return file;
291
+ }
292
+ /** Isolated root override for tests (never called in production paths). */
293
+ function __setLockRootForTests(dir) {
294
+ lockRoot = dir ?? DEFAULT_LOCK_ROOT;
295
+ }
296
+ //# sourceMappingURL=repo-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo-lock.js","sourceRoot":"","sources":["../../../src/lib/workspace/repo-lock.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function runShim(argv: string[]): Promise<number>;
@@ -0,0 +1,233 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runShim = runShim;
4
+ /**
5
+ * Shim runner (IMP-950): the payload behind ~/.teamshare/shims/*.cmd.
6
+ *
7
+ * A shim invocation looks like: node shim-runner.js <bin> <args...>
8
+ *
9
+ * Flow: classify -> maybe acquire workspace locks (FS-backed, same code as
10
+ * the bridge uses in-process) -> exec the REAL binary -> release. Designed
11
+ * to fail OPEN on every error path: arbitration must never make a machine
12
+ * unusable. Timeouts are fail-CLOSED (exit 124 with a clear message) so the
13
+ * calling agent sees "workspace busy" instead of trampling another agent.
14
+ *
15
+ * Nested calls are handled via the inherited TS_WS_HELD environment list:
16
+ * a shim whose scope is already held by an ANCESTOR skips acquisition -
17
+ * the ancestor already serialized this subtree.
18
+ */
19
+ const node_child_process_1 = require("node:child_process");
20
+ const node_fs_1 = require("node:fs");
21
+ const node_os_1 = require("node:os");
22
+ const node_path_1 = require("node:path");
23
+ const repo_lock_1 = require("./repo-lock");
24
+ const classify_1 = require("./classify");
25
+ const bus_1 = require("./bus");
26
+ const DEFAULT_WAIT_MS = 10 * 60_000;
27
+ function parseEnvInt(name, fallback) {
28
+ const raw = Number(process.env[name]);
29
+ return Number.isFinite(raw) && raw > 0 ? raw : fallback;
30
+ }
31
+ function heldScopesFromEnv() {
32
+ return new Set((process.env.TS_WS_HELD ?? '').split(',').filter(Boolean));
33
+ }
34
+ function addToHeldEnv(keys) {
35
+ const set = heldScopesFromEnv();
36
+ for (const k of keys)
37
+ set.add(k);
38
+ process.env.TS_WS_HELD = [...set].join(',');
39
+ }
40
+ function note(text) {
41
+ process.stderr.write(`[teamshare] ${text}\n`);
42
+ }
43
+ // ── real-binary resolution ─────────────────────────────────────────────────
44
+ function shimDir() {
45
+ return (0, node_path_1.join)((0, node_os_1.homedir)(), '.teamshare', 'shims');
46
+ }
47
+ /** Absolute path of the real executable, skipping our shim directory. */
48
+ function resolveRealBinary(bin) {
49
+ const isWin = process.platform === 'win32';
50
+ const finder = isWin ? 'where' : 'which';
51
+ try {
52
+ const res = (0, node_child_process_1.spawnSync)(finder, isWin ? [bin] : ['-a', bin], { encoding: 'utf8' });
53
+ const candidates = (res.stdout ?? '')
54
+ .split(/\r?\n/)
55
+ .map((l) => l.trim())
56
+ .filter(Boolean);
57
+ const shims = shimDir().toLowerCase();
58
+ const filtered = candidates.filter((c) => !c.toLowerCase().startsWith(shims));
59
+ if (!isWin)
60
+ return filtered[0] ?? null;
61
+ // Windows: `where` lists extensionless POSIX wrappers first (e.g.
62
+ // nodejs/npm is a sh script) which cannot be spawned directly. Prefer
63
+ // real executables in PATHEXT order: .cmd/.bat > .exe.
64
+ const byExt = (exts) => filtered.find((c) => exts.some((e) => c.toLowerCase().endsWith(e)));
65
+ return byExt(['.cmd', '.bat']) ?? byExt(['.exe']) ?? filtered[0] ?? null;
66
+ }
67
+ catch {
68
+ return null;
69
+ }
70
+ }
71
+ function quote(arg) {
72
+ return /[\s"^&|<>]/.test(arg) ? `"${arg.replace(/"/g, '""')}"` : arg;
73
+ }
74
+ /** Execute the real binary with inherited stdio; returns its exit code. */
75
+ function execReal(realPath, args) {
76
+ const isWin = process.platform === 'win32';
77
+ const lower = realPath.toLowerCase();
78
+ if (isWin && (lower.endsWith('.cmd') || lower.endsWith('.bat'))) {
79
+ // Newer Node refuses to spawn .cmd directly; drive it through cmd /c.
80
+ const line = [`"${realPath}"`, ...args.map(quote)].join(' ');
81
+ const res = (0, node_child_process_1.spawnSync)(line, {
82
+ shell: true,
83
+ stdio: 'inherit',
84
+ windowsVerbatimArguments: true,
85
+ env: process.env,
86
+ });
87
+ return res.status ?? (res.error ? 1 : 0);
88
+ }
89
+ const res = (0, node_child_process_1.spawnSync)(realPath, args, { stdio: 'inherit', env: process.env });
90
+ if (res.error) {
91
+ note(`failed to execute ${realPath}: ${res.error.message}`);
92
+ return 127;
93
+ }
94
+ return res.status ?? 0;
95
+ }
96
+ // ── locking ────────────────────────────────────────────────────────────────
97
+ async function arbitrateAndExec(ctx, cls) {
98
+ const dirScope = { kind: 'dir', path: process.cwd() };
99
+ const dirKeyStr = (0, repo_lock_1.scopeKey)(dirScope);
100
+ const globalScopes = (cls.global ?? []).map((name) => ({ kind: 'global', name }));
101
+ const allScopeKeys = [dirKeyStr, ...globalScopes.map(repo_lock_1.scopeKey)];
102
+ // Ancestor already holds this workspace - stay inside its serialization.
103
+ const held = heldScopesFromEnv();
104
+ if (allScopeKeys.every((k) => held.has(k))) {
105
+ return execReal(resolveReal(ctx.bin), ctx.args);
106
+ }
107
+ const waitMs = parseEnvInt('TS_SHIM_WAIT_MS', DEFAULT_WAIT_MS);
108
+ const handles = [];
109
+ try {
110
+ // Fixed order: dir first, then globals (documented deadlock rule).
111
+ const dirHandle = await (0, repo_lock_1.acquireWsLock)(dirScope, 'exclusive', ctx.info, {
112
+ waitMs,
113
+ pollMs: 1000,
114
+ onWaiting: (holders) => {
115
+ const holder = holders.find((h) => h.mode === 'exclusive') ?? holders[0];
116
+ note(`workspace busy (${holder?.holder?.label ?? 'another session'} pid=${holder?.pid}) - ` +
117
+ `${cls.label} waiting (max ${Math.round(waitMs / 1000)}s)...`);
118
+ (0, bus_1.publishEvent)({
119
+ type: 'lock.waiting',
120
+ dir: (0, repo_lock_1.canonicalDir)(process.cwd()),
121
+ agentId: ctx.info.agentId,
122
+ taskId: ctx.info.taskId,
123
+ note: `${cls.label} waits on ${holder?.holder?.label ?? 'unknown'} (pid ${holder?.pid})`,
124
+ });
125
+ },
126
+ });
127
+ if (!dirHandle) {
128
+ note(`TIMEOUT: workspace busy after ${Math.round(waitMs / 1000)}s - NOT running ${cls.label}. ` +
129
+ `Retry when the other session finishes.`);
130
+ (0, repo_lock_1.logArbitration)(`shim timeout dir=${(0, repo_lock_1.canonicalDir)(process.cwd())} cmd=${cls.label}`);
131
+ return 124;
132
+ }
133
+ handles.push(dirHandle);
134
+ addToHeldEnv(allScopeKeys);
135
+ for (const g of globalScopes) {
136
+ const gh = await (0, repo_lock_1.acquireWsLock)(g, 'exclusive', ctx.info, { waitMs, pollMs: 1000 });
137
+ if (!gh) {
138
+ note(`global resource ${g.name} busy - skipping ${cls.label}`);
139
+ return 124;
140
+ }
141
+ handles.push(gh);
142
+ }
143
+ (0, bus_1.publishEvent)({
144
+ type: 'lock.granted',
145
+ dir: (0, repo_lock_1.canonicalDir)(process.cwd()),
146
+ agentId: ctx.info.agentId,
147
+ taskId: ctx.info.taskId,
148
+ note: cls.label,
149
+ });
150
+ return execReal(resolveReal(ctx.bin), ctx.args);
151
+ }
152
+ catch (err) {
153
+ // Arbitration itself broke - fail OPEN, the command must still run.
154
+ note(`arbitration skipped (${err.message})`);
155
+ (0, repo_lock_1.logArbitration)(`shim fail-open dir=${(0, repo_lock_1.canonicalDir)(process.cwd())} cmd=${cls.label}`);
156
+ return execReal(resolveReal(ctx.bin), ctx.args);
157
+ }
158
+ finally {
159
+ for (const h of handles.reverse())
160
+ (0, repo_lock_1.releaseWsLock)(h);
161
+ if (handles.length > 0) {
162
+ (0, bus_1.publishEvent)({ type: 'lock.released', dir: (0, repo_lock_1.canonicalDir)(process.cwd()), agentId: ctx.info.agentId });
163
+ }
164
+ }
165
+ }
166
+ function resolveReal(bin) {
167
+ const found = resolveRealBinary(bin);
168
+ if (found)
169
+ return found;
170
+ // Fall back to the bare name and let the OS fail visibly.
171
+ return bin;
172
+ }
173
+ // ── entry ──────────────────────────────────────────────────────────────────
174
+ async function runShim(argv) {
175
+ const [bin, ...args] = argv;
176
+ if (!bin)
177
+ return 2;
178
+ const cls = (0, classify_1.classifyCommand)([bin, ...args].join(' '));
179
+ // Passthrough commands never touch the lock files at all.
180
+ if (cls.cls === 'passthrough') {
181
+ return execReal(resolveReal(bin), args);
182
+ }
183
+ const mode = cls.cls === 'exclusive' ? 'exclusive' : 'shared';
184
+ const info = {
185
+ agentId: process.env.TS_AGENT_ID || undefined,
186
+ kind: process.env.TS_SESSION_KIND || 'shim',
187
+ taskId: process.env.TS_TASK_ID || undefined,
188
+ projectId: process.env.TS_PROJECT_ID || undefined,
189
+ label: cls.label,
190
+ };
191
+ if (!(0, node_fs_1.existsSync)(process.cwd())) {
192
+ return execReal(resolveReal(bin), args);
193
+ }
194
+ // Long-lived dev servers take a SHARED hold for their lifetime: visible
195
+ // to exclusive seekers (who ping + wait), but never blocking peers.
196
+ if (cls.cls === 'long-shared') {
197
+ const attempt = (0, repo_lock_1.tryAcquireWsLock)({ kind: 'dir', path: process.cwd() }, 'shared', info);
198
+ try {
199
+ return execReal(resolveReal(bin), args);
200
+ }
201
+ finally {
202
+ if (attempt.ok)
203
+ (0, repo_lock_1.releaseWsLock)(attempt.handle);
204
+ }
205
+ }
206
+ if (mode === 'shared') {
207
+ // Readers prefer a stable tree (short wait) but never hard-fail:
208
+ // after the wait elapses they run anyway - linting a moving tree
209
+ // beats not linting at all.
210
+ const sharedWaitMs = parseEnvInt('TS_SHIM_SHARED_WAIT_MS', 30_000);
211
+ let handle = null;
212
+ try {
213
+ handle = await (0, repo_lock_1.acquireWsLock)({ kind: 'dir', path: process.cwd() }, 'shared', info, {
214
+ waitMs: sharedWaitMs,
215
+ pollMs: 500,
216
+ });
217
+ if (!handle)
218
+ note(`workspace busy - running ${cls.label} without arbitration`);
219
+ return execReal(resolveReal(bin), args);
220
+ }
221
+ finally {
222
+ (0, repo_lock_1.releaseWsLock)(handle);
223
+ }
224
+ }
225
+ return arbitrateAndExec({ bin, args, info }, cls);
226
+ }
227
+ // Allow running as: node shim-runner.js <bin> <args...>
228
+ if (require.main === module) {
229
+ void runShim(process.argv.slice(2)).then((code) => {
230
+ process.exit(code);
231
+ });
232
+ }
233
+ //# sourceMappingURL=shim-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shim-runner.js","sourceRoot":"","sources":["../../../src/lib/workspace/shim-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export declare const SHIMS_DIR: string;
2
+ /**
3
+ * Idempotent install. Returns the list of files written this call.
4
+ * Foreign files in the directory are never touched.
5
+ */
6
+ export declare function ensureShims(): {
7
+ written: string[];
8
+ dir: string;
9
+ };
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SHIMS_DIR = void 0;
4
+ exports.ensureShims = ensureShims;
5
+ /**
6
+ * Shim installer (IMP-950): writes PATH-injectable command shims into
7
+ * ~/.teamshare/shims. Each shim forwards to the compiled shim-runner.js,
8
+ * which classifies the invocation and arbitrates intrusive commands before
9
+ * executing the real binary.
10
+ *
11
+ * The bridge NEVER puts this directory on its own PATH - only agent-spawned
12
+ * session terminals get it prepended (see bridge/spawn.ts), so manual
13
+ * shells stay completely untouched. Files are stamped; upgrades rewrite.
14
+ */
15
+ const node_fs_1 = require("node:fs");
16
+ const node_os_1 = require("node:os");
17
+ const node_path_1 = require("node:path");
18
+ exports.SHIMS_DIR = (0, node_path_1.join)((0, node_os_1.homedir)(), '.teamshare', 'shims');
19
+ const STAMP = 'teamshare-arbitration-shim v1';
20
+ /** Managed binaries. git is included: cmd.exe resolves git.CMD before a
21
+ * later PATH dir's git.EXE (per-directory PATHEXT scan). CreateProcess-based
22
+ * callers bypass the shim by design - fail-open philosophy. */
23
+ const SHIMMED_BINS = ['npm', 'npx', 'pnpm', 'yarn', 'git'];
24
+ function runnerPath() {
25
+ // dist/lib/workspace/shims.js -> dist/lib/workspace/shim-runner.js
26
+ return (0, node_path_1.join)(__dirname, 'shim-runner.js');
27
+ }
28
+ function windowsShim(bin) {
29
+ return [
30
+ '@echo off',
31
+ `rem ${STAMP}`,
32
+ `rem Arbitrated passthrough for "${bin}" - remove this file to unmanage.`,
33
+ 'setlocal',
34
+ `set "TS_SHIM_DIR=%~dp0"`,
35
+ `node "${runnerPath().replace(/"/g, '')}" ${bin} %*`,
36
+ 'endlocal & exit /b %ERRORLEVEL%',
37
+ '',
38
+ ].join('\r\n');
39
+ }
40
+ function posixShim(bin) {
41
+ return [
42
+ '#!/usr/bin/env bash',
43
+ `# ${STAMP}`,
44
+ `# Arbitrated passthrough for "${bin}" - remove this file to unmanage.`,
45
+ `exec node "${runnerPath()}" ${bin} "$@"`,
46
+ '',
47
+ ].join('\n');
48
+ }
49
+ function currentStamp(file) {
50
+ try {
51
+ const head = (0, node_fs_1.readFileSync)(file, 'utf8').split(/\r?\n/).slice(0, 3).join('\n');
52
+ if (head.includes(STAMP))
53
+ return STAMP;
54
+ if (head.includes('teamshare-arbitration-shim'))
55
+ return 'stale';
56
+ return null;
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ }
62
+ /**
63
+ * Idempotent install. Returns the list of files written this call.
64
+ * Foreign files in the directory are never touched.
65
+ */
66
+ function ensureShims() {
67
+ const written = [];
68
+ try {
69
+ (0, node_fs_1.mkdirSync)(exports.SHIMS_DIR, { recursive: true });
70
+ for (const bin of SHIMMED_BINS) {
71
+ const entries = process.platform === 'win32'
72
+ ? [[`${bin}.cmd`, () => windowsShim(bin)]]
73
+ : [[bin, () => posixShim(bin)]];
74
+ for (const [name, render] of entries) {
75
+ const file = (0, node_path_1.join)(exports.SHIMS_DIR, name);
76
+ if (currentStamp(file) === STAMP)
77
+ continue;
78
+ (0, node_fs_1.writeFileSync)(file, render(), 'utf8');
79
+ written.push(file);
80
+ }
81
+ }
82
+ }
83
+ catch {
84
+ /* best-effort: no shims simply means unmanaged harness commands */
85
+ }
86
+ return { written, dir: exports.SHIMS_DIR };
87
+ }
88
+ //# sourceMappingURL=shims.js.map