@cotal-ai/workspace 0.11.2 → 0.11.4

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.
@@ -0,0 +1,76 @@
1
+ /**
2
+ * A machine-local, crash-safe advisory file lock, shared by every serialized workstation mutation
3
+ * (the `cotal ext` add/remove path, the built-in-connector seeding reconcile, and the materialize
4
+ * read-side probe). One primitive so ownership, liveness, and reclaim are decided identically
5
+ * everywhere.
6
+ *
7
+ * Correctness rests on two invariants that together defeat the empty-lock and reclaim-ABA races:
8
+ *
9
+ * 1. **No canonical lock is ever visible before its complete owner record.** The owner JSON is
10
+ * written to a unique temp file and `link()`ed onto the canonical path — an atomic create-if-absent
11
+ * that either publishes the fully-written record or fails `EEXIST`. There is no `mkdir`-then-write
12
+ * window a racer could misread as stale, and the canonical file is immutable once created (nobody
13
+ * rewrites it; it is only removed by a reclaimer).
14
+ * 2. **Reclaim is serialized by a sentinel.** A stale lock is removed only by the process that first
15
+ * wins a `.reclaim` sentinel (same atomic `link` claim). While it holds the sentinel it re-checks
16
+ * the exact owner+inode and unlinks ONLY that generation, then releases. Because the canonical file
17
+ * blocks any new publish while it exists, no `inspect→unlink` can ever target a later generation,
18
+ * and two reclaimers can never both remove. A dead sentinel holder is fail-loud (manual cleanup),
19
+ * never a recursive unsafe reclaim.
20
+ *
21
+ * Liveness is the owner PID being alive AND, where the OS makes it cheap (Linux `/proc`, macOS/BSD
22
+ * `ps`), its process-start token still matching. When the token is unobtainable the lock is treated as
23
+ * active/unknown (bounded-wait → fail loud), NEVER reclaimed on the missing token alone; only a
24
+ * PID-dead owner is reclaimable.
25
+ */
26
+ /** The fully-written owner record published inside the canonical lock file. */
27
+ export interface LockOwner {
28
+ readonly pid: number;
29
+ /** Best-effort process-start token (PID-reuse guard); absent where the OS makes it unobtainable. */
30
+ readonly start?: string;
31
+ /** Per-acquire nonce: release only removes a lock still carrying it (defeats reclaim-then-release). */
32
+ readonly nonce: string;
33
+ /** Acquire time (epoch ms) — diagnostic. */
34
+ readonly ts: number;
35
+ /** Human label naming the holder in contention errors (e.g. "connector reconcile"). */
36
+ readonly label?: string;
37
+ }
38
+ export type LockInspection = {
39
+ readonly state: "absent";
40
+ } | {
41
+ readonly state: "stale";
42
+ } | {
43
+ readonly state: "active";
44
+ readonly owner: LockOwner;
45
+ };
46
+ export interface HeldLock {
47
+ readonly nonce: string;
48
+ release(): void;
49
+ }
50
+ export interface AcquireOptions {
51
+ /** Naming the holder in a contention error. */
52
+ readonly label?: string;
53
+ /** Max ms to wait on a live holder before failing loud (default 300000). */
54
+ readonly waitMs?: number;
55
+ /** Poll interval while waiting (default 200). */
56
+ readonly pollMs?: number;
57
+ /** The loud error thrown when the wait bound elapses with the lock still live-held. */
58
+ readonly onTimeout?: (owner: LockOwner) => Error;
59
+ }
60
+ /** A best-effort, OS-cheap process-start token used ONLY to detect PID reuse. Undefined ⇒ the check
61
+ * is skipped for that platform and the bounded wait is the sole backstop (never a hard dependency). */
62
+ export declare function processStartToken(pid: number): string | undefined;
63
+ /** Inspect a lock file: absent, stale (dead/aborted owner or torn record), or active with the owner. */
64
+ export declare function inspectLock(path: string): LockInspection;
65
+ /**
66
+ * Acquire the lock at `path`. Returns a handle whose {@link HeldLock.release} removes it (idempotent,
67
+ * nonce-guarded). Reclaims a dead owner (serialized); waits up to `waitMs` on a live one, then throws.
68
+ */
69
+ export declare function acquireLock(path: string, opts?: AcquireOptions): HeldLock;
70
+ /** True when a live process currently holds the lock at `path` (used by read-side probes). */
71
+ export declare function lockIsActive(path: string): boolean;
72
+ /** The owning PID of a live lock at `path`, else undefined (used for diagnostics). */
73
+ export declare function liveLockOwnerPid(path: string): number | undefined;
74
+ /** Force-remove a lock path and any reclaim sentinel (operator recovery for a wedged lock). */
75
+ export declare function breakLock(path: string): void;
76
+ //# sourceMappingURL=advisory-lock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"advisory-lock.d.ts","sourceRoot":"","sources":["../src/advisory-lock.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,+EAA+E;AAC/E,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,oGAAoG;IACpG,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,uGAAuG;IACvG,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,uFAAuF;IACvF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,GAC5B;IAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAC3B;IAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;CAAE,CAAC;AAE5D,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,+CAA+C;IAC/C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,iDAAiD;IACjD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,uFAAuF;IACvF,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,KAAK,CAAC;CAClD;AAED;wGACwG;AACxG,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAyBjE;AA6CD,wGAAwG;AACxG,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAsBxD;AA0DD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB,GAAG,QAAQ,CAmD7E;AAED,8FAA8F;AAC9F,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAED,sFAAsF;AACtF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGjE;AAED,+FAA+F;AAC/F,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAG5C"}
@@ -0,0 +1,242 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { linkSync, mkdirSync, readFileSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
3
+ import { randomBytes } from "node:crypto";
4
+ import { dirname } from "node:path";
5
+ /** A best-effort, OS-cheap process-start token used ONLY to detect PID reuse. Undefined ⇒ the check
6
+ * is skipped for that platform and the bounded wait is the sole backstop (never a hard dependency). */
7
+ export function processStartToken(pid) {
8
+ // Windows has no cheap, STABLE start token here: `/proc` is absent and a `ps` on PATH (MSYS/Git)
9
+ // returns inconsistent output between calls, which would make the SAME live lock read differently
10
+ // from the acquirer vs a reader and be misjudged stale. Per the no-token rule, return undefined so a
11
+ // live PID keeps the lock active/unknown (bounded-wait is the backstop) rather than a false reclaim.
12
+ if (process.platform === "win32")
13
+ return undefined;
14
+ try {
15
+ // Linux: /proc/<pid>/stat field 22 (starttime). comm (field 2) may hold spaces/parens, so parse
16
+ // after the final ')': the remainder begins at field 3 (state), so starttime is index 19.
17
+ const stat = readFileSync(`/proc/${pid}/stat`, "utf8");
18
+ const after = stat.lastIndexOf(") ");
19
+ if (after >= 0) {
20
+ const token = stat.slice(after + 2).split(" ")[19];
21
+ if (token)
22
+ return token;
23
+ }
24
+ }
25
+ catch {
26
+ /* not Linux, or the process is gone — fall through */
27
+ }
28
+ try {
29
+ // macOS/BSD: the process start date is stable for the life of the PID.
30
+ const out = execFileSync("ps", ["-o", "lstart=", "-p", String(pid)], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
31
+ return out || undefined;
32
+ }
33
+ catch {
34
+ return undefined;
35
+ }
36
+ }
37
+ /** Sleep synchronously without spawning a process (safe inside a sync acquire loop). */
38
+ function sleepSync(ms) {
39
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
40
+ }
41
+ function pidAlive(pid) {
42
+ try {
43
+ process.kill(pid, 0);
44
+ return true;
45
+ }
46
+ catch (e) {
47
+ const code = e.code;
48
+ if (code === "ESRCH")
49
+ return false;
50
+ if (code === "EPERM")
51
+ return true; // another user's live process
52
+ throw e;
53
+ }
54
+ }
55
+ function readOwner(path) {
56
+ let raw;
57
+ try {
58
+ raw = readFileSync(path, "utf8");
59
+ }
60
+ catch (e) {
61
+ const code = e.code;
62
+ if (code === "ENOENT")
63
+ return undefined;
64
+ // A directory (EISDIR) or an unreadable file (EACCES/EPERM) where a lock file belongs is not a
65
+ // holder we can reason about — fail loud fast, never busy-spin trying to reclaim it.
66
+ if (code === "EISDIR" || code === "EACCES" || code === "EPERM")
67
+ throw new Error(`advisory lock path ${path} is unreadable (${code}) - remove the stray file or directory there and retry`);
68
+ throw e;
69
+ }
70
+ let ino;
71
+ try {
72
+ ino = statSync(path).ino;
73
+ }
74
+ catch {
75
+ /* raced removal */
76
+ }
77
+ try {
78
+ return { owner: JSON.parse(raw), ino };
79
+ }
80
+ catch {
81
+ return { ino }; // torn/legacy content
82
+ }
83
+ }
84
+ /** Inspect a lock file: absent, stale (dead/aborted owner or torn record), or active with the owner. */
85
+ export function inspectLock(path) {
86
+ const found = readOwner(path);
87
+ if (found === undefined)
88
+ return { state: "absent" };
89
+ const owner = found.owner;
90
+ if (!owner ||
91
+ typeof owner.pid !== "number" ||
92
+ !Number.isInteger(owner.pid) ||
93
+ owner.pid <= 0 ||
94
+ typeof owner.nonce !== "string") {
95
+ return { state: "stale" };
96
+ }
97
+ if (!pidAlive(owner.pid))
98
+ return { state: "stale" };
99
+ // PID alive — but is it still the SAME process? A recorded start token that no longer matches means
100
+ // the PID was recycled: the original owner is gone (stale). A token we simply cannot obtain is NOT
101
+ // grounds to reclaim — the live PID keeps the lock active/unknown.
102
+ if (owner.start !== undefined) {
103
+ const now = processStartToken(owner.pid);
104
+ if (now !== undefined && now !== owner.start)
105
+ return { state: "stale" };
106
+ }
107
+ return { state: "active", owner };
108
+ }
109
+ /** Publish `content` at `path` iff nothing is there — atomic (temp write + hard link). Returns true on
110
+ * success, false if the path already exists. */
111
+ function publishAtomic(path, content) {
112
+ const tmp = `${path}.${randomBytes(8).toString("hex")}.tmp`;
113
+ writeFileSync(tmp, content, { mode: 0o600, flag: "wx" });
114
+ try {
115
+ linkSync(tmp, path);
116
+ return true;
117
+ }
118
+ catch (e) {
119
+ if (e.code === "EEXIST")
120
+ return false;
121
+ throw e;
122
+ }
123
+ finally {
124
+ try {
125
+ unlinkSync(tmp);
126
+ }
127
+ catch {
128
+ /* best-effort temp cleanup */
129
+ }
130
+ }
131
+ }
132
+ /** Serialized reclaim of a stale canonical lock at `path`, via a `.reclaim` sentinel that only one
133
+ * reclaimer can hold. Under the sentinel the exact stale generation is re-confirmed and removed. A
134
+ * live sentinel is waited on; a dead sentinel holder fails loud (never a recursive unsafe reclaim). */
135
+ function reclaimStale(path, inspectedIno) {
136
+ const sentinel = `${path}.reclaim`;
137
+ const mine = JSON.stringify({ pid: process.pid, start: processStartToken(process.pid), nonce: randomBytes(8).toString("hex"), ts: Date.now() });
138
+ if (!publishAtomic(sentinel, mine)) {
139
+ // Lost the sentinel race. Only a STALE (dead-owner) sentinel is fail-loud — a live one is a
140
+ // reclaim in progress and an absent one is a reclaim that just finished; both mean "let the acquire
141
+ // loop retry", never "proceed to unlink" (an EEXIST loser must not fall through to remove G1).
142
+ if (inspectLock(sentinel).state === "stale")
143
+ throw new Error(`a lock reclaim was interrupted and its sentinel remains (${sentinel}) - if no cotal process is running, remove it and retry`);
144
+ return; // active or absent — retry acquisition
145
+ }
146
+ try {
147
+ // Under the sentinel the canonical file is immutable (publish is blocked while it exists), so a
148
+ // re-inspect that is still stale AND the same inode is exactly the generation we set out to reclaim.
149
+ const found = readOwner(path);
150
+ if (found === undefined)
151
+ return; // already gone
152
+ const state = inspectLock(path).state;
153
+ if (state === "active")
154
+ return; // reacquired under us (should not happen while it exists) — leave it
155
+ if (inspectedIno !== undefined && found.ino !== undefined && found.ino !== inspectedIno)
156
+ return; // a different generation
157
+ unlinkSync(path);
158
+ }
159
+ catch (e) {
160
+ if (e.code !== "ENOENT")
161
+ throw e;
162
+ }
163
+ finally {
164
+ try {
165
+ unlinkSync(sentinel);
166
+ }
167
+ catch {
168
+ /* released */
169
+ }
170
+ }
171
+ }
172
+ /**
173
+ * Acquire the lock at `path`. Returns a handle whose {@link HeldLock.release} removes it (idempotent,
174
+ * nonce-guarded). Reclaims a dead owner (serialized); waits up to `waitMs` on a live one, then throws.
175
+ */
176
+ export function acquireLock(path, opts = {}) {
177
+ const waitMs = opts.waitMs ?? 300000;
178
+ const pollMs = opts.pollMs ?? 200;
179
+ mkdirSync(dirname(path), { recursive: true });
180
+ const nonce = randomBytes(12).toString("hex");
181
+ const owner = {
182
+ pid: process.pid,
183
+ start: processStartToken(process.pid),
184
+ nonce,
185
+ ts: Date.now(),
186
+ ...(opts.label ? { label: opts.label } : {}),
187
+ };
188
+ const content = `${JSON.stringify(owner)}\n`;
189
+ const deadline = Date.now() + waitMs;
190
+ for (;;) {
191
+ if (publishAtomic(path, content)) {
192
+ let released = false;
193
+ return {
194
+ nonce,
195
+ release() {
196
+ if (released)
197
+ return;
198
+ released = true;
199
+ const found = readOwner(path);
200
+ if (found?.owner?.nonce === nonce) {
201
+ try {
202
+ unlinkSync(path);
203
+ }
204
+ catch {
205
+ /* already reclaimed */
206
+ }
207
+ }
208
+ },
209
+ };
210
+ }
211
+ // Held — inspect and either wait (live) or reclaim (dead).
212
+ const found = readOwner(path);
213
+ const inspection = inspectLock(path);
214
+ if (inspection.state === "absent")
215
+ continue; // raced with a release
216
+ if (inspection.state === "stale") {
217
+ reclaimStale(path, found?.ino);
218
+ sleepSync(Math.min(pollMs, 50));
219
+ continue;
220
+ }
221
+ if (Date.now() >= deadline) {
222
+ throw (opts.onTimeout?.(inspection.owner)) ??
223
+ new Error(`${opts.label ?? "a lock"} is held by a live process (pid ${inspection.owner.pid}) and did not release within ${Math.round(waitMs / 1000)}s - retry, or if it is wedged, remove ${path}`);
224
+ }
225
+ sleepSync(pollMs);
226
+ }
227
+ }
228
+ /** True when a live process currently holds the lock at `path` (used by read-side probes). */
229
+ export function lockIsActive(path) {
230
+ return inspectLock(path).state === "active";
231
+ }
232
+ /** The owning PID of a live lock at `path`, else undefined (used for diagnostics). */
233
+ export function liveLockOwnerPid(path) {
234
+ const found = inspectLock(path);
235
+ return found.state === "active" ? found.owner.pid : undefined;
236
+ }
237
+ /** Force-remove a lock path and any reclaim sentinel (operator recovery for a wedged lock). */
238
+ export function breakLock(path) {
239
+ rmSync(path, { force: true });
240
+ rmSync(`${path}.reclaim`, { force: true });
241
+ }
242
+ //# sourceMappingURL=advisory-lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"advisory-lock.js","sourceRoot":"","sources":["../src/advisory-lock.ts"],"names":[],"mappings":"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"}
@@ -33,6 +33,9 @@ export interface InstalledExtension {
33
33
  readonly version: string;
34
34
  /** The spec the operator passed to `ext add` (registry range, file:, tarball…) — for re-adds. */
35
35
  readonly spec: string;
36
+ /** `"seeded"` iff installed by the built-in-connector reconcile (not an operator `ext add`). Keys
37
+ * refresh-gating and import-failure hints on the marker rather than trusting the spec path. */
38
+ readonly source?: "seeded";
36
39
  /** Every registry contribution made by the package. Older command-only manifests omit this; the
37
40
  * loader derives `command:<name>` entries from `commands` for compatibility. */
38
41
  readonly provides?: readonly ExtensionRef[];
@@ -46,6 +49,8 @@ export interface ExtensionsManifest {
46
49
  /** The extensions prefix: `<config>/cotal/extensions` — its own npm installation root. */
47
50
  export declare function extensionsDir(): string;
48
51
  export declare function extensionsManifestPath(): string;
52
+ /** The extension-prefix writer lock — a lock DIRECTORY (see {@link inspectLock}: `mkdir` is the
53
+ * portable atomic create-exclusive, with no empty-file window a racer could misread as stale). */
49
54
  export declare function extensionMutationLockPath(): string;
50
55
  export type ExtensionMutationLockState = {
51
56
  readonly state: "absent" | "stale";
@@ -53,13 +58,18 @@ export type ExtensionMutationLockState = {
53
58
  readonly state: "active";
54
59
  readonly owner: number;
55
60
  };
56
- /** Inspect the extension-prefix writer lock. Permission denial means the owner may still be live;
57
- * only ESRCH proves that a recorded owner is gone. */
61
+ /** Inspect the extension-prefix writer lock via the shared advisory-lock primitive (PID liveness +
62
+ * process-start identity + torn-record handling all decided in one place). */
58
63
  export declare function extensionMutationLockState(): ExtensionMutationLockState;
59
64
  /** Load the manifest. Missing file → no extensions. A CORRUPT file is a loud error (never treat
60
65
  * installed extensions as absent — commands would silently vanish from help). */
61
66
  export declare function loadExtensionsManifest(): ExtensionsManifest;
67
+ /** Persist the manifest atomically (temp-then-rename, exclusive temp): a SIGKILL mid-write can never
68
+ * truncate the live manifest into corrupt JSON that would then vanish every installed extension. */
62
69
  export declare function saveExtensionsManifest(m: ExtensionsManifest): void;
70
+ /** Move a corrupt/unreadable manifest aside so a `--reset`/`--repair` can rebuild a clean one instead
71
+ * of wedging on the same unreadable read. Returns the quarantine path, or undefined if none existed. */
72
+ export declare function quarantineExtensionsManifest(): string | undefined;
63
73
  /** Strip a live {@link Command} down to its serializable display surface for the cache. */
64
74
  export declare function cacheCommand(cmd: Command): CachedCommand;
65
75
  /** Stable, serializable registry keys contributed by one imported package. */
@@ -1 +1 @@
1
- {"version":3,"file":"extensions.d.ts","sourceRoot":"","sources":["../src/extensions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAEjF,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEzE;;;;;;;;;;;;;GAaG;AAEH;;qBAEqB;AACrB,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;IACrC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,kBAAkB;IACjC,8DAA8D;IAC9D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,iGAAiG;IACjG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;qFACiF;IACjF,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IAC5C,QAAQ,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;CACnD;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,UAAU,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACpD;AAED,0FAA0F;AAC1F,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAED,MAAM,MAAM,0BAA0B,GAClC;IAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAA;CAAE,GACtC;IAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD;sDACsD;AACtD,wBAAgB,0BAA0B,IAAI,0BAA0B,CAqBvE;AAED;kFACkF;AAClF,wBAAgB,sBAAsB,IAAI,kBAAkB,CAY3D;AAED,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAGlE;AAED,2FAA2F;AAC3F,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,CAUxD;AAED,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,GAAG,EAAE,SAAS,GAAG,YAAY,CAE3D;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,YAAY,GAAG,YAAY,CAkBvE;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,kBAAkB,GAAG,SAAS,YAAY,EAAE,CAExF;AAED,oGAAoG;AACpG,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,kBAAkB,GAAG,SAAS,YAAY,EAAE,CAElF;AAED,8DAA8D;AAC9D,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,8FAA8F;AAC9F,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKzE"}
1
+ {"version":3,"file":"extensions.d.ts","sourceRoot":"","sources":["../src/extensions.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAGjF,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEzE;;;;;;;;;;;;;GAaG;AAEH;;qBAEqB;AACrB,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;IACrC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,kBAAkB;IACjC,8DAA8D;IAC9D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,iGAAiG;IACjG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;oGACgG;IAChG,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC;IAC3B;qFACiF;IACjF,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;IAC5C,QAAQ,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;IAC5C,wEAAwE;IACxE,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,YAAY,EAAE,CAAC;CACnD;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,UAAU,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACpD;AAED,0FAA0F;AAC1F,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED;mGACmG;AACnG,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAED,MAAM,MAAM,0BAA0B,GAClC;IAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAA;CAAE,GACtC;IAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD;+EAC+E;AAC/E,wBAAgB,0BAA0B,IAAI,0BAA0B,CAGvE;AAED;kFACkF;AAClF,wBAAgB,sBAAsB,IAAI,kBAAkB,CAY3D;AAED;qGACqG;AACrG,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAOlE;AAED;yGACyG;AACzG,wBAAgB,4BAA4B,IAAI,MAAM,GAAG,SAAS,CAMjE;AAED,2FAA2F;AAC3F,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,CAUxD;AAED,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,GAAG,EAAE,SAAS,GAAG,YAAY,CAE3D;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,YAAY,GAAG,YAAY,CAkBvE;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,kBAAkB,GAAG,SAAS,YAAY,EAAE,CAExF;AAED,oGAAoG;AACpG,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,kBAAkB,GAAG,SAAS,YAAY,EAAE,CAElF;AAED,8DAA8D;AAC9D,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,8FAA8F;AAC9F,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKzE"}
@@ -1,6 +1,8 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
- import { dirname, join } from "node:path";
1
+ import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
2
+ import { randomBytes } from "node:crypto";
3
+ import { basename, dirname, join } from "node:path";
3
4
  import { globalConfigDir } from "@cotal-ai/core";
5
+ import { inspectLock } from "./advisory-lock.js";
4
6
  import { localProcessPath } from "./local-process.js";
5
7
  /** The extensions prefix: `<config>/cotal/extensions` — its own npm installation root. */
6
8
  export function extensionsDir() {
@@ -9,37 +11,16 @@ export function extensionsDir() {
9
11
  export function extensionsManifestPath() {
10
12
  return join(extensionsDir(), "extensions.json");
11
13
  }
14
+ /** The extension-prefix writer lock — a lock DIRECTORY (see {@link inspectLock}: `mkdir` is the
15
+ * portable atomic create-exclusive, with no empty-file window a racer could misread as stale). */
12
16
  export function extensionMutationLockPath() {
13
17
  return join(dirname(extensionsDir()), ".extensions.lock");
14
18
  }
15
- /** Inspect the extension-prefix writer lock. Permission denial means the owner may still be live;
16
- * only ESRCH proves that a recorded owner is gone. */
19
+ /** Inspect the extension-prefix writer lock via the shared advisory-lock primitive (PID liveness +
20
+ * process-start identity + torn-record handling all decided in one place). */
17
21
  export function extensionMutationLockState() {
18
- const lock = extensionMutationLockPath();
19
- let raw;
20
- try {
21
- raw = readFileSync(lock, "utf8");
22
- }
23
- catch (e) {
24
- if (e.code === "ENOENT")
25
- return { state: "absent" };
26
- throw new Error(`can't read extension mutation lock ${lock}: ${e.message}`);
27
- }
28
- const owner = Number(raw.trim());
29
- if (!Number.isInteger(owner) || owner <= 0)
30
- return { state: "stale" };
31
- try {
32
- process.kill(owner, 0);
33
- return { state: "active", owner };
34
- }
35
- catch (e) {
36
- const code = e.code;
37
- if (code === "ESRCH")
38
- return { state: "stale" };
39
- if (code === "EPERM")
40
- return { state: "active", owner };
41
- throw new Error(`can't check extension mutation lock owner ${owner}: ${e.message}`);
42
- }
22
+ const found = inspectLock(extensionMutationLockPath());
23
+ return found.state === "active" ? { state: "active", owner: found.owner.pid } : { state: found.state };
43
24
  }
44
25
  /** Load the manifest. Missing file → no extensions. A CORRUPT file is a loud error (never treat
45
26
  * installed extensions as absent — commands would silently vanish from help). */
@@ -59,9 +40,25 @@ export function loadExtensionsManifest() {
59
40
  throw new Error(`corrupt extensions manifest ${p}: no "extensions" array`);
60
41
  return m;
61
42
  }
43
+ /** Persist the manifest atomically (temp-then-rename, exclusive temp): a SIGKILL mid-write can never
44
+ * truncate the live manifest into corrupt JSON that would then vanish every installed extension. */
62
45
  export function saveExtensionsManifest(m) {
63
- mkdirSync(extensionsDir(), { recursive: true });
64
- writeFileSync(extensionsManifestPath(), `${JSON.stringify(m, null, 2)}\n`);
46
+ const dir = extensionsDir();
47
+ mkdirSync(dir, { recursive: true });
48
+ const path = extensionsManifestPath();
49
+ const tmp = join(dir, `.${basename(path)}.${randomBytes(6).toString("hex")}.tmp`);
50
+ writeFileSync(tmp, `${JSON.stringify(m, null, 2)}\n`, { flag: "wx" });
51
+ renameSync(tmp, path);
52
+ }
53
+ /** Move a corrupt/unreadable manifest aside so a `--reset`/`--repair` can rebuild a clean one instead
54
+ * of wedging on the same unreadable read. Returns the quarantine path, or undefined if none existed. */
55
+ export function quarantineExtensionsManifest() {
56
+ const path = extensionsManifestPath();
57
+ if (!existsSync(path))
58
+ return undefined;
59
+ const aside = `${path}.corrupt.${randomBytes(4).toString("hex")}`;
60
+ renameSync(path, aside);
61
+ return aside;
65
62
  }
66
63
  /** Strip a live {@link Command} down to its serializable display surface for the cache. */
67
64
  export function cacheCommand(cmd) {
@@ -1 +1 @@
1
- {"version":3,"file":"extensions.js","sourceRoot":"","sources":["../src/extensions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"extensions.js","sourceRoot":"","sources":["../src/extensions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACjG,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEpD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAqB,MAAM,oBAAoB,CAAC;AAoDzE,0FAA0F;AAC1F,MAAM,UAAU,aAAa;IAC3B,OAAO,IAAI,CAAC,eAAe,EAAE,EAAE,YAAY,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,OAAO,IAAI,CAAC,aAAa,EAAE,EAAE,iBAAiB,CAAC,CAAC;AAClD,CAAC;AAED;mGACmG;AACnG,MAAM,UAAU,yBAAyB;IACvC,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,EAAE,kBAAkB,CAAC,CAAC;AAC5D,CAAC;AAMD;+EAC+E;AAC/E,MAAM,UAAU,0BAA0B;IACxC,MAAM,KAAK,GAAG,WAAW,CAAC,yBAAyB,EAAE,CAAC,CAAC;IACvD,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;AACzG,CAAC;AAED;kFACkF;AAClF,MAAM,UAAU,sBAAsB;IACpC,MAAM,CAAC,GAAG,sBAAsB,EAAE,CAAC;IACnC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;IAC9C,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,KAAM,CAAW,CAAC,OAAO,mDAAmD,CAAC,CAAC;IAChI,CAAC;IACD,MAAM,CAAC,GAAG,MAA4B,CAAC;IACvC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,yBAAyB,CAAC,CAAC;IAC7G,OAAO,CAAC,CAAC;AACX,CAAC;AAED;qGACqG;AACrG,MAAM,UAAU,sBAAsB,CAAC,CAAqB;IAC1D,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;IAC5B,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,sBAAsB,EAAE,CAAC;IACtC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAClF,aAAa,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACtE,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AACxB,CAAC;AAED;yGACyG;AACzG,MAAM,UAAU,4BAA4B;IAC1C,MAAM,IAAI,GAAG,sBAAsB,EAAE,CAAC;IACtC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACxC,MAAM,KAAK,GAAG,GAAG,IAAI,YAAY,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;IAClE,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACxB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,WAAW,EAAE,GAAG,CAAC,WAAW;KAC7B,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,cAAc,CAAC,GAAc;IAC3C,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,SAAuB;IACvD,IAAI,OAAO,SAAS,CAAC,OAAO,KAAK,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,SAAS,CAAC,IAAI,yCAAyC,CAAC,CAAC;IACrI,gBAAgB,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;IAClF,KAAK,MAAM,QAAQ,IAAI,SAAS,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC;QACjD,IAAI,OAAO,QAAQ,KAAK,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,SAAS,CAAC,IAAI,qCAAqC,CAAC,CAAC;QACxH,gBAAgB,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO;QACL,IAAI,EAAE,eAAe;QACrB,IAAI,EAAE,SAAS,CAAC,IAAI;QACpB,KAAK,EAAE,SAAS,CAAC,KAAK;QACtB,KAAK,EAAE,SAAS,CAAC,KAAK;QACtB,OAAO,EAAE,SAAS,CAAC,OAAO;QAC1B,SAAS,EAAE,SAAS,CAAC,SAAS;QAC9B,QAAQ,EAAE,SAAS,CAAC,QAAQ;QAC5B,UAAU,EAAE,SAAS,CAAC,UAAU;QAChC,WAAW,EAAE,SAAS,CAAC,WAAW;KACnC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,GAAuB;IAC7D,OAAO,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC;AAClC,CAAC;AAED,oGAAoG;AACpG,MAAM,UAAU,iBAAiB,CAAC,GAAuB;IACvD,OAAO,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;AAClG,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC7C,OAAO,IAAI,CAAC,aAAa,EAAE,EAAE,cAAc,EAAE,GAAG,CAAC,CAAC;AACpD,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,yBAAyB,CAAC,GAAW;IACnD,MAAM,CAAC,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,EAAE,cAAc,CAAC,CAAC;IACzD,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACrC,MAAM,CAAC,GAAI,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAA0B,CAAC,OAAO,CAAC;IAChF,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/C,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,10 +1,13 @@
1
+ export * from "./advisory-lock.js";
1
2
  export * from "./auth-paths.js";
2
3
  export * from "./agent-health.js";
3
4
  export * from "./bin-path.js";
4
5
  export * from "./colors.js";
5
6
  export * from "./connect.js";
6
7
  export * from "./default-agent.js";
8
+ export * from "./official-connectors.js";
7
9
  export * from "./extensions.js";
10
+ export * from "./materialize.js";
8
11
  export * from "./local-process.js";
9
12
  export * from "./flags.js";
10
13
  export * from "./provenance.js";
@@ -14,4 +17,5 @@ export * from "./preflight.js";
14
17
  export * from "./render.js";
15
18
  export * from "./renewal.js";
16
19
  export * from "./space.js";
20
+ export * from "./win-cmd.js";
17
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -1,10 +1,13 @@
1
+ export * from "./advisory-lock.js";
1
2
  export * from "./auth-paths.js";
2
3
  export * from "./agent-health.js";
3
4
  export * from "./bin-path.js";
4
5
  export * from "./colors.js";
5
6
  export * from "./connect.js";
6
7
  export * from "./default-agent.js";
8
+ export * from "./official-connectors.js";
7
9
  export * from "./extensions.js";
10
+ export * from "./materialize.js";
8
11
  export * from "./local-process.js";
9
12
  export * from "./flags.js";
10
13
  export * from "./provenance.js";
@@ -14,4 +17,5 @@ export * from "./preflight.js";
14
17
  export * from "./render.js";
15
18
  export * from "./renewal.js";
16
19
  export * from "./space.js";
20
+ export * from "./win-cmd.js";
17
21
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { type Extension, type ExtensionRef } from "@cotal-ai/core";
2
+ import { type InstalledExtension } from "./extensions.js";
3
+ /**
4
+ * Verify the pin, resolve the entry, and import the package TRANSACTIONALLY and INVISIBLY. The
5
+ * import's self-registrations are STAGED (never resolvable while it runs); they publish atomically
6
+ * only after `advertised` is confirmed present among them. A package that registers a key then throws
7
+ * (a rejected top-level await) commits NOTHING; a package that imports cleanly but never advertises
8
+ * the requested provider commits NONE of its registrations. Serialized so concurrent loads never
9
+ * share a validation window.
10
+ */
11
+ export declare function importInstalledExtension(ext: InstalledExtension, advertised: ExtensionRef): Promise<void>;
12
+ /**
13
+ * Names an extension of `kind` is available under from the installed MANIFEST alone — no import, no
14
+ * CLI process state. The manager and the seeding engine read this directly (the CLI's own
15
+ * `extensionNames` additionally folds in registered names behind its `installedEnabled` gate). A
16
+ * spawn/models path gating on this honors a live `cotal ext remove`, since a removed connector
17
+ * leaves the manifest even while an earlier import keeps it in the registry.
18
+ */
19
+ export declare function manifestExtensionNames(kind: string): string[];
20
+ /**
21
+ * Resolve a provider from the installed manifest, importing the package that advertised it. The
22
+ * "already registered" short-circuit and any composition-root gating stay with the caller — this
23
+ * ALWAYS consults the machine manifest. `hint(ref)` phrases the not-found error (the CLI names the
24
+ * exact `cotal ext add <pkg>` for an official name; a bare caller gets the generic form).
25
+ */
26
+ export declare function materializeFromManifest<T extends Extension = Extension>(ref: ExtensionRef, opts?: {
27
+ hint?: (ref: ExtensionRef) => string;
28
+ }): Promise<T>;
29
+ //# sourceMappingURL=materialize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"materialize.d.ts","sourceRoot":"","sources":["../src/materialize.ts"],"names":[],"mappings":"AAGA,OAAO,EAAY,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC7E,OAAO,EAML,KAAK,kBAAkB,EACxB,MAAM,iBAAiB,CAAC;AAuBzB;;;;;;;GAOG;AACH,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,kBAAkB,EAAE,UAAU,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAO/G;AAiDD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAK7D;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAAC,CAAC,SAAS,SAAS,GAAG,SAAS,EAC3E,GAAG,EAAE,YAAY,EACjB,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,MAAM,CAAA;CAAO,GAClD,OAAO,CAAC,CAAC,CAAC,CAWZ"}
@@ -0,0 +1,113 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+ import { registry } from "@cotal-ai/core";
5
+ import { extensionPackageDir, extensionMutationLockState, extensionProvides, installedExtensionVersion, loadExtensionsManifest, } from "./extensions.js";
6
+ /**
7
+ * The generic manifest-materialize primitive: verify an installed package's version pin, resolve
8
+ * its entry, and dynamic-`import()` it (which self-registers its extensions into the shared core
9
+ * {@link registry}). It lives here — below `@cotal-ai/cli` — because both the CLI dispatcher and the
10
+ * manager supervisor must materialize manifest extensions, and neither the manager nor this package
11
+ * may import the CLI. It knows nothing about connectors or any other kind; callers supply the
12
+ * name→package hint used to phrase a not-found error.
13
+ */
14
+ function importFailure(pkg, ext, e) {
15
+ const message = e instanceof Error ? e.message : String(e);
16
+ const compatibility = /does not provide an export named/.test(message) && /@cotal-ai\/(core|workspace)/.test(message)
17
+ ? " (the extension is not compatible with this cotal binary's linked @cotal-ai/* packages; update the binary and extension together)"
18
+ : "";
19
+ return new Error(`extension ${pkg}@${ext.version} failed to import: ${message}${compatibility} - reinstall it: \`cotal ext add ${ext.spec}\``);
20
+ }
21
+ /** Process-wide single-flight serializer: two `import()`s must not share a staging/validation window,
22
+ * so their advertised-key checks can't interleave. */
23
+ let loadChain = Promise.resolve();
24
+ /**
25
+ * Verify the pin, resolve the entry, and import the package TRANSACTIONALLY and INVISIBLY. The
26
+ * import's self-registrations are STAGED (never resolvable while it runs); they publish atomically
27
+ * only after `advertised` is confirmed present among them. A package that registers a key then throws
28
+ * (a rejected top-level await) commits NOTHING; a package that imports cleanly but never advertises
29
+ * the requested provider commits NONE of its registrations. Serialized so concurrent loads never
30
+ * share a validation window.
31
+ */
32
+ export async function importInstalledExtension(ext, advertised) {
33
+ const run = loadChain.then(() => loadOne(ext, advertised));
34
+ loadChain = run.then(() => undefined, () => undefined);
35
+ return run;
36
+ }
37
+ async function loadOne(ext, advertised) {
38
+ // Idempotent: if a prior load (a concurrent first call now serialized ahead of us on loadChain, or an
39
+ // earlier one) already published the advertised provider, we're done. Re-import()ing an
40
+ // already-evaluated module is a cache hit with NO registration side effects, so its stage would be
41
+ // empty and wrongly read as "did not register"; the live check short-circuits that. commitStaged
42
+ // publishes ALL of a package's keys, so a sibling ref requested concurrently is live here too.
43
+ if (registry.has(advertised.kind, advertised.name))
44
+ return;
45
+ const mutation = extensionMutationLockState();
46
+ if (mutation.state === "active")
47
+ throw new Error(`extension install/remove is in progress (pid ${mutation.owner}) - retry after the active \`cotal ext\` command finishes`);
48
+ const pkg = ext.pkg;
49
+ const onDisk = installedExtensionVersion(pkg);
50
+ if (!onDisk) {
51
+ throw new Error(`extension ${pkg} is in the manifest but not installed at ${extensionPackageDir(pkg)} - \`cotal ext add ${ext.spec}\` again`);
52
+ }
53
+ if (onDisk !== ext.version) {
54
+ throw new Error(`extension ${pkg} is ${onDisk} on disk but the manifest pinned ${ext.version} (its cached command surface may be stale) - re-add it: \`cotal ext add ${ext.spec}\``);
55
+ }
56
+ const dir = extensionPackageDir(pkg);
57
+ const meta = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
58
+ let entry = meta.main ?? "index.js";
59
+ const dot = meta.exports?.["."];
60
+ if (typeof dot === "string")
61
+ entry = dot;
62
+ else if (dot && typeof dot === "object") {
63
+ const d = dot;
64
+ entry = d.import ?? d.default ?? entry;
65
+ }
66
+ else if (typeof meta.exports === "string")
67
+ entry = meta.exports;
68
+ let staged;
69
+ try {
70
+ // The import self-registers into OUR registry (core is linked); runStaged keeps those
71
+ // registrations invisible until we've validated them, so a throw discards the stage untouched.
72
+ ({ staged } = await registry.runStaged(() => import(pathToFileURL(join(dir, entry)).href)));
73
+ }
74
+ catch (e) {
75
+ throw importFailure(pkg, ext, e); // stage discarded; nothing reached the live registry
76
+ }
77
+ // Publish ONLY if the package advertised the requested provider. Otherwise commit NONE of its
78
+ // registrations (never leave a package's other keys live when its advertised one is absent).
79
+ if (!staged.some((r) => r.kind === advertised.kind && r.name === advertised.name)) {
80
+ throw new Error(`extension ${pkg}@${ext.version} imported but did not register ${advertised.kind} "${advertised.name}" - re-add it: \`cotal ext add ${ext.spec}\``);
81
+ }
82
+ registry.commitStaged(staged);
83
+ }
84
+ /**
85
+ * Names an extension of `kind` is available under from the installed MANIFEST alone — no import, no
86
+ * CLI process state. The manager and the seeding engine read this directly (the CLI's own
87
+ * `extensionNames` additionally folds in registered names behind its `installedEnabled` gate). A
88
+ * spawn/models path gating on this honors a live `cotal ext remove`, since a removed connector
89
+ * leaves the manifest even while an earlier import keeps it in the registry.
90
+ */
91
+ export function manifestExtensionNames(kind) {
92
+ const names = new Set();
93
+ for (const ext of loadExtensionsManifest().extensions)
94
+ for (const ref of extensionProvides(ext))
95
+ if (ref.kind === kind)
96
+ names.add(ref.name);
97
+ return [...names].sort();
98
+ }
99
+ /**
100
+ * Resolve a provider from the installed manifest, importing the package that advertised it. The
101
+ * "already registered" short-circuit and any composition-root gating stay with the caller — this
102
+ * ALWAYS consults the machine manifest. `hint(ref)` phrases the not-found error (the CLI names the
103
+ * exact `cotal ext add <pkg>` for an official name; a bare caller gets the generic form).
104
+ */
105
+ export async function materializeFromManifest(ref, opts = {}) {
106
+ const ext = loadExtensionsManifest().extensions.find((candidate) => extensionProvides(candidate).some((provided) => provided.kind === ref.kind && provided.name === ref.name));
107
+ if (!ext) {
108
+ throw new Error(opts.hint?.(ref) ?? `no installed extension provides ${ref.kind} "${ref.name}" - install it with \`cotal ext add <npm-package>\``);
109
+ }
110
+ await importInstalledExtension(ext, ref); // throws if the package doesn't advertise ref; else ref is now committed
111
+ return registry.resolve(ref.kind, ref.name);
112
+ }
113
+ //# sourceMappingURL=materialize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"materialize.js","sourceRoot":"","sources":["../src/materialize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The four first-party agent connectors, name → npm package. This is NOT authority — a connector is
3
+ * usable only once installed (seeded on first run, or `cotal ext add`ed) and imported, exactly like
4
+ * a third-party one. It is the operator-UX source of truth so an absent official connector names its
5
+ * REAL package in the install hint, and the seeder knows which built-ins to seed. A third-party
6
+ * connector installs under its own package name and resolves the same way without appearing here.
7
+ *
8
+ * It lives in workspace (not cli) because both the CLI and the manager consume it and the manager
9
+ * must not import the CLI — it is pure data supplied to the generic materialize/hint path.
10
+ */
11
+ export declare const OFFICIAL_CONNECTORS: Readonly<Record<string, string>>;
12
+ /** The default connector/agent type when `COTAL_DEFAULT_AGENT` is unset (David's locked decision). */
13
+ export declare const DEFAULT_CONNECTOR = "claude";
14
+ /** `cotal` is reserved: it is the binary's own name, so a connector must never claim it (there is no
15
+ * runtime alias anymore). Rejected at `cotal ext add` and at materialization. */
16
+ export declare const RESERVED_CONNECTOR_NAMES: readonly string[];
17
+ /** The install-hint for an absent connector: name the exact package for an official one, else the
18
+ * generic `cotal ext add` form. Shared by the manager and the CLI so the message never drifts. */
19
+ export declare function connectorInstallHint(name: string): string;
20
+ //# sourceMappingURL=official-connectors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"official-connectors.d.ts","sourceRoot":"","sources":["../src/official-connectors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAKhE,CAAC;AAEF,sGAAsG;AACtG,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAE1C;kFACkF;AAClF,eAAO,MAAM,wBAAwB,EAAE,SAAS,MAAM,EAAc,CAAC;AAErE;mGACmG;AACnG,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKzD"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The four first-party agent connectors, name → npm package. This is NOT authority — a connector is
3
+ * usable only once installed (seeded on first run, or `cotal ext add`ed) and imported, exactly like
4
+ * a third-party one. It is the operator-UX source of truth so an absent official connector names its
5
+ * REAL package in the install hint, and the seeder knows which built-ins to seed. A third-party
6
+ * connector installs under its own package name and resolves the same way without appearing here.
7
+ *
8
+ * It lives in workspace (not cli) because both the CLI and the manager consume it and the manager
9
+ * must not import the CLI — it is pure data supplied to the generic materialize/hint path.
10
+ */
11
+ export const OFFICIAL_CONNECTORS = {
12
+ claude: "@cotal-ai/connector-claude-code",
13
+ opencode: "@cotal-ai/connector-opencode",
14
+ hermes: "@cotal-ai/connector-hermes",
15
+ pi: "@cotal-ai/pi",
16
+ };
17
+ /** The default connector/agent type when `COTAL_DEFAULT_AGENT` is unset (David's locked decision). */
18
+ export const DEFAULT_CONNECTOR = "claude";
19
+ /** `cotal` is reserved: it is the binary's own name, so a connector must never claim it (there is no
20
+ * runtime alias anymore). Rejected at `cotal ext add` and at materialization. */
21
+ export const RESERVED_CONNECTOR_NAMES = ["cotal"];
22
+ /** The install-hint for an absent connector: name the exact package for an official one, else the
23
+ * generic `cotal ext add` form. Shared by the manager and the CLI so the message never drifts. */
24
+ export function connectorInstallHint(name) {
25
+ const pkg = OFFICIAL_CONNECTORS[name];
26
+ return pkg
27
+ ? `no connector "${name}" installed - add it with \`cotal ext add ${pkg}\``
28
+ : `no connector "${name}" - install its integration with \`cotal ext add <npm-package>\`, or check the name`;
29
+ }
30
+ //# sourceMappingURL=official-connectors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"official-connectors.js","sourceRoot":"","sources":["../src/official-connectors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAqC;IACnE,MAAM,EAAE,iCAAiC;IACzC,QAAQ,EAAE,8BAA8B;IACxC,MAAM,EAAE,4BAA4B;IACpC,EAAE,EAAE,cAAc;CACnB,CAAC;AAEF,sGAAsG;AACtG,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AAE1C;kFACkF;AAClF,MAAM,CAAC,MAAM,wBAAwB,GAAsB,CAAC,OAAO,CAAC,CAAC;AAErE;mGACmG;AACnG,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,MAAM,GAAG,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IACtC,OAAO,GAAG;QACR,CAAC,CAAC,iBAAiB,IAAI,6CAA6C,GAAG,IAAI;QAC3E,CAAC,CAAC,iBAAiB,IAAI,qFAAqF,CAAC;AACjH,CAAC"}
@@ -0,0 +1,35 @@
1
+ /** The cmd.exe that runs a batch shim — ALWAYS the system interpreter built from a TRUSTED env, NEVER
2
+ * a child env's `%ComSpec%`: absolute `%SystemRoot%\System32\cmd.exe` (`win32.join` so it's
3
+ * backslash-correct even in off-Windows tests). */
4
+ export declare function resolveComspec(operatorEnv?: NodeJS.ProcessEnv): string;
5
+ /**
6
+ * Escape one argument for a cmd.exe `/c` command line so the launched program receives it
7
+ * byte-for-byte. PORTS Rust std `append_bat_arg`'s quote/backslash mechanics; throws (fail closed) for
8
+ * an argument cmd cannot preserve: a newline (`\r`/`\n`) or NUL, or a `%NAME%` cmd would expand. A lone
9
+ * `%` / undefined `%NAME%` pass.
10
+ */
11
+ export declare function quoteCmdArg(arg: string, env: NodeJS.ProcessEnv): string;
12
+ /**
13
+ * The cmd.exe argument string that launches `scriptPath` (a `.cmd`/`.bat`) with `args`. Shape:
14
+ * `/e:ON /v:OFF /d /s /c "<invocation>"` where `<invocation>` is `"<script>" <arg>…`. `/v:OFF` makes
15
+ * `!x!` LITERAL; `/e:ON` keeps command extensions on so a shim's `%~dp0` resolves; `/d` skips AutoRun.
16
+ * The script path is fail-closed against cmd `%VAR%` expansion too.
17
+ */
18
+ export declare function buildCmdCommandLine(scriptPath: string, args: readonly string[], env: NodeJS.ProcessEnv): string;
19
+ /** A `spawnSync`-shaped launch spec. On non-win32 (or a real `.exe`) it is a passthrough; a `.cmd`/
20
+ * `.bat` resolves to the system cmd.exe with a pre-escaped verbatim command line. */
21
+ export interface CmdSpawnSpec {
22
+ readonly file: string;
23
+ readonly args: string[];
24
+ /** Pass to `spawnSync`/`spawn` opts so Node appends the pre-escaped command line verbatim. */
25
+ readonly windowsVerbatimArguments: boolean;
26
+ }
27
+ /**
28
+ * Resolve how to `spawnSync` `command` (a bare name like `npm`) with `args` on the current platform.
29
+ * POSIX: passthrough. Windows: resolve the exact file on PATH; a `.cmd`/`.bat` runs THROUGH the system
30
+ * cmd.exe with a byte-for-byte command line (so `spawnSync("npm.cmd", …)` no longer trips the
31
+ * `.cmd`-without-shell refusal, and args survive cmd's metachar parse). Fails loud if not on PATH or
32
+ * cmd.exe is missing — no silent fallback.
33
+ */
34
+ export declare function cmdSpawnSpec(command: string, args: readonly string[], operatorEnv?: NodeJS.ProcessEnv): CmdSpawnSpec;
35
+ //# sourceMappingURL=win-cmd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"win-cmd.d.ts","sourceRoot":"","sources":["../src/win-cmd.ts"],"names":[],"mappings":"AAoDA;;oDAEoD;AACpD,wBAAgB,cAAc,CAAC,WAAW,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAGnF;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAmCvE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAO/G;AAED;sFACsF;AACtF,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IACxB,8FAA8F;IAC9F,QAAQ,CAAC,wBAAwB,EAAE,OAAO,CAAC;CAC5C;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,WAAW,GAAE,MAAM,CAAC,UAAwB,GAAG,YAAY,CAWjI"}
@@ -0,0 +1,133 @@
1
+ import { existsSync } from "node:fs";
2
+ import { extname, win32 } from "node:path";
3
+ import { resolveOnPath } from "./bin-path.js";
4
+ /**
5
+ * Windows `.cmd`/`.bat` spawn adapter — pure OS plumbing, no Cotal protocol types. Node refuses to
6
+ * spawn a `.cmd`/`.bat` without a shell (CVE-2024-27980), and a naive `shell: true` re-parses the
7
+ * command line through cmd.exe's metachar grammar (`& | < > ^ …`), so a batch shim must be run THROUGH
8
+ * cmd.exe with a byte-for-byte pre-escaped command line. `quoteCmdArg` PORTS Rust std `append_bat_arg`
9
+ * (the CVE fix). Used by the manager's PtyRuntime launch AND the CLI's `npm` invocation (`npm` is
10
+ * `npm.cmd` on Windows), so the seeding + `cotal ext add` paths spawn npm correctly on Windows.
11
+ *
12
+ * The three primitives are exported so their byte-for-byte contract is unit-testable off-Windows (they
13
+ * do not gate on `process.platform`); {@link cmdSpawnSpec} is the `spawnSync`-shaped high-level adapter.
14
+ */
15
+ /** cmd.exe dynamic pseudo-variables — always "defined" with command extensions on, even though they
16
+ * are absent from the process env. A `%NAME%` for one of these (or a real env var) would be expanded
17
+ * on the command line, so it cannot be preserved byte-for-byte. */
18
+ const CMD_DYNAMIC_VARS = new Set([
19
+ "CD",
20
+ "DATE",
21
+ "TIME",
22
+ "RANDOM",
23
+ "ERRORLEVEL",
24
+ "CMDEXTVERSION",
25
+ "CMDCMDLINE",
26
+ "HIGHESTNUMANODENUMBER",
27
+ ]);
28
+ /** Case-insensitive env lookup (Windows env is case-insensitive; a plain object may hold either case). */
29
+ function envGet(env, name) {
30
+ if (env[name] !== undefined)
31
+ return env[name];
32
+ const lower = name.toLowerCase();
33
+ for (const key of Object.keys(env))
34
+ if (key.toLowerCase() === lower)
35
+ return env[key];
36
+ return undefined;
37
+ }
38
+ /** Throw (fail closed) if `s` carries a `%NAME%` cmd.exe would expand before exec — a defined env var
39
+ * or a cmd dynamic pseudo-variable — since there's no lossless escape. A lone `%` and an UNdefined
40
+ * `%NAME%` are left untouched by cmd and pass. Guards BOTH argv values and the resolved script path. */
41
+ function rejectCmdExpansion(s, env, what) {
42
+ for (const m of s.matchAll(/%([^%]+)%/g)) {
43
+ const base = m[1].split(":")[0]; // %VAR:~s,l% / %VAR:a=b% substring/replace forms expand VAR too
44
+ if (CMD_DYNAMIC_VARS.has(base.toUpperCase()) || envGet(env, base) !== undefined) {
45
+ throw new Error(`cannot pass ${what} through cmd.exe - %${m[1]}% would be expanded (unsupported on Windows): ${JSON.stringify(s)}`);
46
+ }
47
+ }
48
+ }
49
+ /** The cmd.exe that runs a batch shim — ALWAYS the system interpreter built from a TRUSTED env, NEVER
50
+ * a child env's `%ComSpec%`: absolute `%SystemRoot%\System32\cmd.exe` (`win32.join` so it's
51
+ * backslash-correct even in off-Windows tests). */
52
+ export function resolveComspec(operatorEnv = process.env) {
53
+ const sysRoot = envGet(operatorEnv, "SystemRoot") ?? envGet(operatorEnv, "windir") ?? "C:\\Windows";
54
+ return win32.join(sysRoot, "System32", "cmd.exe");
55
+ }
56
+ /**
57
+ * Escape one argument for a cmd.exe `/c` command line so the launched program receives it
58
+ * byte-for-byte. PORTS Rust std `append_bat_arg`'s quote/backslash mechanics; throws (fail closed) for
59
+ * an argument cmd cannot preserve: a newline (`\r`/`\n`) or NUL, or a `%NAME%` cmd would expand. A lone
60
+ * `%` / undefined `%NAME%` pass.
61
+ */
62
+ export function quoteCmdArg(arg, env) {
63
+ if (/[\r\n\0]/.test(arg)) {
64
+ throw new Error(`cannot pass argument through cmd.exe - contains a newline or NUL (unsupported on Windows): ${JSON.stringify(arg)}`);
65
+ }
66
+ rejectCmdExpansion(arg, env, "argument");
67
+ const UNQUOTED = "#$*+-./:?@\\_";
68
+ let quote = arg.length === 0 || arg.endsWith("\\");
69
+ for (const ch of arg) {
70
+ const code = ch.codePointAt(0);
71
+ if (code <= 0x1f || (code >= 0x7f && code <= 0x9f))
72
+ quote = true;
73
+ else if (code < 0x80 && !(/[A-Za-z0-9]/.test(ch) || UNQUOTED.includes(ch)))
74
+ quote = true;
75
+ }
76
+ let out = quote ? '"' : "";
77
+ let backslashes = 0;
78
+ for (const ch of arg) {
79
+ if (ch === "\\") {
80
+ backslashes++;
81
+ }
82
+ else {
83
+ if (ch === '"') {
84
+ out += "\\".repeat(backslashes);
85
+ out += '"';
86
+ }
87
+ backslashes = 0;
88
+ }
89
+ out += ch;
90
+ }
91
+ if (quote) {
92
+ out += "\\".repeat(backslashes);
93
+ out += '"';
94
+ }
95
+ return out;
96
+ }
97
+ /**
98
+ * The cmd.exe argument string that launches `scriptPath` (a `.cmd`/`.bat`) with `args`. Shape:
99
+ * `/e:ON /v:OFF /d /s /c "<invocation>"` where `<invocation>` is `"<script>" <arg>…`. `/v:OFF` makes
100
+ * `!x!` LITERAL; `/e:ON` keeps command extensions on so a shim's `%~dp0` resolves; `/d` skips AutoRun.
101
+ * The script path is fail-closed against cmd `%VAR%` expansion too.
102
+ */
103
+ export function buildCmdCommandLine(scriptPath, args, env) {
104
+ if (scriptPath.includes('"')) {
105
+ throw new Error(`cannot launch script with a quote in its path: ${JSON.stringify(scriptPath)}`);
106
+ }
107
+ rejectCmdExpansion(scriptPath, env, "script path");
108
+ const invocation = [`"${scriptPath}"`, ...args.map((a) => quoteCmdArg(a, env))].join(" ");
109
+ return `/e:ON /v:OFF /d /s /c "${invocation}"`;
110
+ }
111
+ /**
112
+ * Resolve how to `spawnSync` `command` (a bare name like `npm`) with `args` on the current platform.
113
+ * POSIX: passthrough. Windows: resolve the exact file on PATH; a `.cmd`/`.bat` runs THROUGH the system
114
+ * cmd.exe with a byte-for-byte command line (so `spawnSync("npm.cmd", …)` no longer trips the
115
+ * `.cmd`-without-shell refusal, and args survive cmd's metachar parse). Fails loud if not on PATH or
116
+ * cmd.exe is missing — no silent fallback.
117
+ */
118
+ export function cmdSpawnSpec(command, args, operatorEnv = process.env) {
119
+ if (process.platform !== "win32")
120
+ return { file: command, args: [...args], windowsVerbatimArguments: false };
121
+ const resolved = resolveOnPath(command, operatorEnv);
122
+ if (resolved === undefined)
123
+ throw new Error(`cannot run "${command}": not found on PATH`);
124
+ const ext = extname(resolved).toLowerCase();
125
+ if (ext === ".cmd" || ext === ".bat") {
126
+ const comspec = resolveComspec(operatorEnv);
127
+ if (!existsSync(comspec))
128
+ throw new Error(`cannot run "${command}": system cmd.exe not found at ${comspec}`);
129
+ return { file: comspec, args: [buildCmdCommandLine(resolved, args, operatorEnv)], windowsVerbatimArguments: true };
130
+ }
131
+ return { file: resolved, args: [...args], windowsVerbatimArguments: false };
132
+ }
133
+ //# sourceMappingURL=win-cmd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"win-cmd.js","sourceRoot":"","sources":["../src/win-cmd.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cotal-ai/workspace",
3
3
  "description": "Cotal machine-local workstation layer: the ~/.cotal mesh registry, target resolution, preflight, on-disk auth-path I/O, and the command-copy renderer — operator concerns over a local checkout, kept separate from the wire protocol in @cotal-ai/core.",
4
- "version": "0.11.2",
4
+ "version": "0.11.4",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
@@ -18,7 +18,7 @@
18
18
  }
19
19
  },
20
20
  "dependencies": {
21
- "@cotal-ai/core": "0.11.2"
21
+ "@cotal-ai/core": "0.11.4"
22
22
  },
23
23
  "files": [
24
24
  "dist"