@cotal-ai/workspace 0.40.0 → 0.41.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/pid.d.ts +148 -0
- package/dist/pid.d.ts.map +1 -1
- package/dist/pid.js +168 -1
- package/dist/pid.js.map +1 -1
- package/package.json +2 -2
package/dist/pid.d.ts
CHANGED
|
@@ -92,4 +92,152 @@ export declare function readProcessCommand(pid: number): ProcessCommand;
|
|
|
92
92
|
* direction is the safe one and is chosen deliberately.
|
|
93
93
|
*/
|
|
94
94
|
export declare function commandIsCotalSupervisor(command: string): boolean;
|
|
95
|
+
/**
|
|
96
|
+
* A process identity record, as a pidfile's CONTENT. The pid alone is not a stable identity on any
|
|
97
|
+
* OS: PIDs are recycled (aggressively on Windows, eventually on POSIX), and a teardown that signals
|
|
98
|
+
* a recorded pid therefore signals "whatever holds that number now". The record pins the pid to the
|
|
99
|
+
* process START that claimed it, which is the strongest identity a fresh process can establish
|
|
100
|
+
* without holding a kernel capability (#969 design shape (a)): the start time of a pid is fixed for
|
|
101
|
+
* the life of that pid and changes the moment the number is reused.
|
|
102
|
+
*
|
|
103
|
+
* FORMAT: two whitespace-separated fields, `pid` and `token`. The token is opaque here (see
|
|
104
|
+
* {@link ProcessStartTokenReader} for what produces it); a record carrying only a pid is a LEGACY
|
|
105
|
+
* record (pre-identity build) and every consumer must treat it as such, loud.
|
|
106
|
+
*/
|
|
107
|
+
export type ProcessIdentityRecord = {
|
|
108
|
+
pid: number;
|
|
109
|
+
token: string;
|
|
110
|
+
};
|
|
111
|
+
/** What a pidfile's content resolves to. `legacy` is a bare pid (a pre-identity build's record);
|
|
112
|
+
* `husk` is an empty pre-protocol file; `unattributable` is anything parsePid rejects — the same
|
|
113
|
+
* three-valued rule the rest of this module applies, so "cannot read the record" is never folded
|
|
114
|
+
* into "nothing recorded". */
|
|
115
|
+
export type ReadRecord = {
|
|
116
|
+
kind: "record";
|
|
117
|
+
record: ProcessIdentityRecord;
|
|
118
|
+
} | {
|
|
119
|
+
kind: "legacy";
|
|
120
|
+
pid: number;
|
|
121
|
+
} | {
|
|
122
|
+
kind: "husk";
|
|
123
|
+
} | {
|
|
124
|
+
kind: "unattributable";
|
|
125
|
+
raw: string;
|
|
126
|
+
};
|
|
127
|
+
export declare function parseRecord(raw: string): ReadRecord;
|
|
128
|
+
/** Serialize a record. Exactly the inverse of {@link parseRecord}; writing is centralized so a
|
|
129
|
+
* future format change is one edit, not a fifth copy of `String(pid)`. */
|
|
130
|
+
export declare function formatRecord(record: ProcessIdentityRecord): string;
|
|
131
|
+
/**
|
|
132
|
+
* The creation-identity token of a live pid: on Linux `/proc/<pid>/stat` field 22 (starttime —
|
|
133
|
+
* ticks since boot, fixed for the life of the pid); on macOS/BSD `ps -o lstart=`; on Windows
|
|
134
|
+
* `undefined`, because no cheap STABLE token exists there (see the Windows note in
|
|
135
|
+
* `advisory-lock.ts`'s `processStartToken` for why a `ps` on PATH is worse than none). A pid whose
|
|
136
|
+
* token is `undefined` can still be recorded — the record then carries no start pin, and teardown
|
|
137
|
+
* warns that it is proceeding without the identity check.
|
|
138
|
+
*/
|
|
139
|
+
export type ProcessStartTokenReader = (pid: number) => string | undefined;
|
|
140
|
+
/** The start token for a pid THIS PROCESS is about to record. Prefer the reader passed by the
|
|
141
|
+
* caller (tests inject divergence), fall back to the same `/proc`/`ps` read the advisory lock
|
|
142
|
+
* uses, so the token written at launch and the token compared at teardown come from ONE
|
|
143
|
+
* implementation. `undefined` (Windows, or a ps-less host) records a pid-only LEGACY record —
|
|
144
|
+
* visible as such to every reader, never silently. */
|
|
145
|
+
export declare function identityRecord(pid: number, tokenAt?: ProcessStartTokenReader): ProcessIdentityRecord | {
|
|
146
|
+
pid: number;
|
|
147
|
+
};
|
|
148
|
+
/** The start-token reader shared with the advisory lock, so launch and teardown agree on the token
|
|
149
|
+
* for the same live pid. Imported here rather than reimplemented: two token readers that drift
|
|
150
|
+
* would make the SAME process look like a stranger at teardown. */
|
|
151
|
+
import { processStartToken as defaultStartToken } from "./advisory-lock.js";
|
|
152
|
+
export { defaultStartToken };
|
|
153
|
+
/** The verdict of {@link assertRecordIdentity}: does the live process behind `pid` still carry the
|
|
154
|
+
* start this record pinned? `unreadable` is deliberately NOT a variant: the reader signature
|
|
155
|
+
* returns `undefined` for "cannot look" and that folds into `unpinned` (refuse). This differs from
|
|
156
|
+
* a legacy record with no pin at all: legacy records warn and proceed for upgrade compatibility,
|
|
157
|
+
* while a pin that exists but cannot be checked is preserved rather than silently weakened. */
|
|
158
|
+
export type IdentityVerdict = {
|
|
159
|
+
kind: "match";
|
|
160
|
+
} | {
|
|
161
|
+
kind: "mismatch";
|
|
162
|
+
liveToken: string;
|
|
163
|
+
} | {
|
|
164
|
+
kind: "gone";
|
|
165
|
+
} | {
|
|
166
|
+
kind: "unpinned";
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* THE identity check a teardown must pass before signalling (#969). This is the "open, verify,
|
|
170
|
+
* terminate" step: verify BEFORE any signal. A record pins a start token; if the pid's CURRENT
|
|
171
|
+
* start differs, the pid was REUSED and the live process is a stranger — refuse, never signal.
|
|
172
|
+
*
|
|
173
|
+
* FAILS CLOSED ON THE CHECK ITSELF: `gone` (the pid is ESRCH-dead; the record is stale and
|
|
174
|
+
* clearable) and `unpinned` (no token could be established for a live pid - a platform that
|
|
175
|
+
* cannot pin, or the process died between the token read and the liveness probe) are both
|
|
176
|
+
* distinct from `match`, and it is the CALLER's rule which of them may clear a record. Only
|
|
177
|
+
* `gone` is ever a safe clear here, because only ESRCH-equivalent evidence proves the recorded
|
|
178
|
+
* process itself gone.
|
|
179
|
+
*/
|
|
180
|
+
export declare function assertRecordIdentity(record: ProcessIdentityRecord, tokenAt?: ProcessStartTokenReader): IdentityVerdict;
|
|
181
|
+
/** One shared refusal message, so every teardown names the same rule in the same words (#969
|
|
182
|
+
* acceptance: manager, delivery, auth and broker teardown use the SAME identity rule). */
|
|
183
|
+
export declare function identityRefusal(label: string, path: string, record: ProcessIdentityRecord, live: string): Error;
|
|
184
|
+
/** Warning for the one deliberately reduced-guarantee path: a live pre-pin record. Upgrades must be
|
|
185
|
+
* able to stop the stack that was launched by the previous version. The next launch writes a pin,
|
|
186
|
+
* after which mismatch and torn-pin protection applies in full. */
|
|
187
|
+
export declare function identityLegacyWarning(label: string, pidfilePath: string): string;
|
|
188
|
+
/**
|
|
189
|
+
* The pidfile format stays a BARE PID. Every reader of `.cotal/*.pid` across the tree - status,
|
|
190
|
+
* clean, meshes, the web dashboard, smoke suites, and any operator script - keeps working, and a
|
|
191
|
+
* bare-pid pidfile remains a LEGACY record this change must handle with a loud warning, not break. The creation
|
|
192
|
+
* identity therefore lives in a SIBLING file `<pidfile>.identity` (the `manager.delivery-aware`
|
|
193
|
+
* marker pattern), holding the {@link formatRecord} two-field pin. Missing sibling = legacy record;
|
|
194
|
+
* present-but-garbled = torn write, refuse; pid mismatch inside the sibling = torn pairing, refuse.
|
|
195
|
+
*/
|
|
196
|
+
export declare function identityPinPath(pidfilePath: string): string;
|
|
197
|
+
/** Write the sibling pin for a pid this launch just spawned. Called AFTER the pidfile write, with
|
|
198
|
+
* the same pid, so a reader that sees a pidfile with no pin yet is reading either a legacy record
|
|
199
|
+
* or a launch mid-pairing - both are visible below, never silent. `undefined` token (Windows,
|
|
200
|
+
* ps-less host) writes NO pin, which is the honest legacy shape for that platform rather than a
|
|
201
|
+
* pin that cannot be checked. */
|
|
202
|
+
export declare function writeIdentityPin(pidfilePath: string, pid: number, tokenAt?: ProcessStartTokenReader): void;
|
|
203
|
+
/** What {@link verifyIdentityPin} found for one pidfile. */
|
|
204
|
+
export type PinVerdict = {
|
|
205
|
+
kind: "legacy";
|
|
206
|
+
} | {
|
|
207
|
+
kind: "match";
|
|
208
|
+
} | {
|
|
209
|
+
kind: "gone";
|
|
210
|
+
} | {
|
|
211
|
+
kind: "mismatch";
|
|
212
|
+
record: ProcessIdentityRecord;
|
|
213
|
+
liveToken: string;
|
|
214
|
+
} | {
|
|
215
|
+
kind: "torn-pin";
|
|
216
|
+
raw: string;
|
|
217
|
+
} | {
|
|
218
|
+
kind: "torn-pairing";
|
|
219
|
+
pinPid: number;
|
|
220
|
+
} | {
|
|
221
|
+
kind: "unpinned";
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* THE open-verify step every teardown runs BEFORE signalling (#969). Read the pidfile's bare pid,
|
|
225
|
+
* then the sibling pin, and adjudicate the pairing:
|
|
226
|
+
* - legacy -> the caller warns and signals for upgrade compatibility
|
|
227
|
+
* - match -> safe to signal; the process behind the pid is the recorded one
|
|
228
|
+
* - gone -> ESRCH-proven dead; the caller may clear the record
|
|
229
|
+
* - mismatch -> PID REUSE: the pid now fronts a DIFFERENT start; NEVER signal
|
|
230
|
+
* - torn-pin/torn-pairing/unpinned -> cannot establish identity on a live pid; refuse, preserve
|
|
231
|
+
*
|
|
232
|
+
* The pidfile argument is the SIBLING'S base path (the pidfile itself), and the bare pid is read
|
|
233
|
+
* from it here so the pairing check cannot drift from however the caller read the pid.
|
|
234
|
+
*/
|
|
235
|
+
export declare function verifyIdentityPin(pidfilePath: string, tokenAt?: ProcessStartTokenReader): PinVerdict;
|
|
236
|
+
/** Remove the sibling pin together with a pidfile whose process is PROVEN gone. Never called on a
|
|
237
|
+
* refused stop: the preserved record keeps its pin so the next attempt re-adjudicates. */
|
|
238
|
+
export declare function removeIdentityPin(pidfilePath: string): void;
|
|
239
|
+
/** The loud torn/unpinned refusal, in the same shape as {@link identityRefusal}: name the component,
|
|
240
|
+
* the file, what was found, and the operator's next step. Legacy records do not reach this helper:
|
|
241
|
+
* callers emit {@link identityLegacyWarning} and proceed. */
|
|
242
|
+
export declare function identityUncertaintyRefusal(label: string, pidfilePath: string, verdict: PinVerdict): Error;
|
|
95
243
|
//# sourceMappingURL=pid.d.ts.map
|
package/dist/pid.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pid.d.ts","sourceRoot":"","sources":["../src/pid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH;;;wCAGwC;AACxC,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGxD;AAED;;;;;;;;sDAQsD;AACtD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAIxF;AAED;;;;;;;2BAO2B;AAC3B;;;mGAGmG;AACnG,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAE1E,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAOvE;AAWD;;gBAEgB;AAChB,MAAM,MAAM,cAAc;AACxB,gDAAgD;AAC9C;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AACtC,iGAAiG;GAC/F;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE;AAClB,oFAAoF;GAClF;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC;;6CAE6C;AAC7C,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,cAAc,CAAC;AAE5D;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAkB9D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEjE"}
|
|
1
|
+
{"version":3,"file":"pid.d.ts","sourceRoot":"","sources":["../src/pid.ts"],"names":[],"mappings":"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"}
|
package/dist/pid.js
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* module they may depend on, never a core export.
|
|
19
19
|
*/
|
|
20
20
|
import { spawnSync } from "node:child_process";
|
|
21
|
-
import { readFileSync } from "node:fs";
|
|
21
|
+
import { readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
22
22
|
/** A Node/POSIX-signalable pid: a positive INTEGER within the signed 32-bit range `process.kill`
|
|
23
23
|
* accepts (it throws `ERR_INVALID_ARG_TYPE`/`ERR_OUT_OF_RANGE` outside it). Anything else -
|
|
24
24
|
* fractional, non-numeric, non-positive, oversized - is undefined: UNATTRIBUTABLE, never a pid to
|
|
@@ -102,4 +102,171 @@ export function readProcessCommand(pid) {
|
|
|
102
102
|
export function commandIsCotalSupervisor(command) {
|
|
103
103
|
return /(^|\s)supervise(\s|$)/.test(command);
|
|
104
104
|
}
|
|
105
|
+
export function parseRecord(raw) {
|
|
106
|
+
const trimmed = raw.trim();
|
|
107
|
+
if (trimmed === "")
|
|
108
|
+
return { kind: "husk" };
|
|
109
|
+
// FIELD SPLIT, not a substring: the token may itself contain digits (it is a timestamp), so
|
|
110
|
+
// anything other than exactly two fields cannot name both a pid and its start.
|
|
111
|
+
const fields = trimmed.split(/\s+/);
|
|
112
|
+
if (fields.length === 1) {
|
|
113
|
+
const pid = parsePid(fields[0]);
|
|
114
|
+
return pid === undefined ? { kind: "unattributable", raw: trimmed } : { kind: "legacy", pid };
|
|
115
|
+
}
|
|
116
|
+
if (fields.length === 2 && parsePid(fields[0]) !== undefined && fields[1] !== "")
|
|
117
|
+
return { kind: "record", record: { pid: parsePid(fields[0]), token: fields[1] } };
|
|
118
|
+
return { kind: "unattributable", raw: trimmed };
|
|
119
|
+
}
|
|
120
|
+
/** Serialize a record. Exactly the inverse of {@link parseRecord}; writing is centralized so a
|
|
121
|
+
* future format change is one edit, not a fifth copy of `String(pid)`. */
|
|
122
|
+
export function formatRecord(record) {
|
|
123
|
+
return `${record.pid} ${record.token}`;
|
|
124
|
+
}
|
|
125
|
+
/** The start token for a pid THIS PROCESS is about to record. Prefer the reader passed by the
|
|
126
|
+
* caller (tests inject divergence), fall back to the same `/proc`/`ps` read the advisory lock
|
|
127
|
+
* uses, so the token written at launch and the token compared at teardown come from ONE
|
|
128
|
+
* implementation. `undefined` (Windows, or a ps-less host) records a pid-only LEGACY record —
|
|
129
|
+
* visible as such to every reader, never silently. */
|
|
130
|
+
export function identityRecord(pid, tokenAt = defaultStartToken) {
|
|
131
|
+
const token = tokenAt(pid);
|
|
132
|
+
return token === undefined ? { pid } : { pid, token };
|
|
133
|
+
}
|
|
134
|
+
/** The start-token reader shared with the advisory lock, so launch and teardown agree on the token
|
|
135
|
+
* for the same live pid. Imported here rather than reimplemented: two token readers that drift
|
|
136
|
+
* would make the SAME process look like a stranger at teardown. */
|
|
137
|
+
import { processStartToken as defaultStartToken } from "./advisory-lock.js";
|
|
138
|
+
export { defaultStartToken };
|
|
139
|
+
/**
|
|
140
|
+
* THE identity check a teardown must pass before signalling (#969). This is the "open, verify,
|
|
141
|
+
* terminate" step: verify BEFORE any signal. A record pins a start token; if the pid's CURRENT
|
|
142
|
+
* start differs, the pid was REUSED and the live process is a stranger — refuse, never signal.
|
|
143
|
+
*
|
|
144
|
+
* FAILS CLOSED ON THE CHECK ITSELF: `gone` (the pid is ESRCH-dead; the record is stale and
|
|
145
|
+
* clearable) and `unpinned` (no token could be established for a live pid - a platform that
|
|
146
|
+
* cannot pin, or the process died between the token read and the liveness probe) are both
|
|
147
|
+
* distinct from `match`, and it is the CALLER's rule which of them may clear a record. Only
|
|
148
|
+
* `gone` is ever a safe clear here, because only ESRCH-equivalent evidence proves the recorded
|
|
149
|
+
* process itself gone.
|
|
150
|
+
*/
|
|
151
|
+
export function assertRecordIdentity(record, tokenAt = defaultStartToken) {
|
|
152
|
+
const live = tokenAt(record.pid);
|
|
153
|
+
if (live === undefined) {
|
|
154
|
+
// Distinguish "cannot look" from "looked and it is gone": the token readers return undefined
|
|
155
|
+
// BOTH when the platform cannot answer and when the process does not exist, and those must not
|
|
156
|
+
// share a verdict — a platform that cannot pin must refuse, a dead pid may be cleared.
|
|
157
|
+
const liveness = probeLiveness(record.pid);
|
|
158
|
+
if (liveness === "dead")
|
|
159
|
+
return { kind: "gone" };
|
|
160
|
+
return { kind: "unpinned" };
|
|
161
|
+
}
|
|
162
|
+
return live === record.token ? { kind: "match" } : { kind: "mismatch", liveToken: live };
|
|
163
|
+
}
|
|
164
|
+
/** One shared refusal message, so every teardown names the same rule in the same words (#969
|
|
165
|
+
* acceptance: manager, delivery, auth and broker teardown use the SAME identity rule). */
|
|
166
|
+
export function identityRefusal(label, path, record, live) {
|
|
167
|
+
return new Error(`refusing to stop ${label} (pid ${record.pid}) at ${path}: the pid has been reused (recorded start ${record.token}, the live process started ${live}). Signalling it would kill an unrelated process. The record is preserved.\n` +
|
|
168
|
+
`NEXT: inspect pid ${record.pid} with \`ps -p ${record.pid}\`. If that process should be stopped, stop it, then rerun this command; once the pid is dead the stale record clears automatically.`);
|
|
169
|
+
}
|
|
170
|
+
/** Warning for the one deliberately reduced-guarantee path: a live pre-pin record. Upgrades must be
|
|
171
|
+
* able to stop the stack that was launched by the previous version. The next launch writes a pin,
|
|
172
|
+
* after which mismatch and torn-pin protection applies in full. */
|
|
173
|
+
export function identityLegacyWarning(label, pidfilePath) {
|
|
174
|
+
return `! ${label} at ${pidfilePath} predates process identity pinning; signalling it without an identity check. A relaunch will pin the process identity.`;
|
|
175
|
+
}
|
|
176
|
+
// ---- THE SIBLING IDENTITY PIN: launch writes it, teardown verifies it (#969) ---------------------
|
|
177
|
+
/**
|
|
178
|
+
* The pidfile format stays a BARE PID. Every reader of `.cotal/*.pid` across the tree - status,
|
|
179
|
+
* clean, meshes, the web dashboard, smoke suites, and any operator script - keeps working, and a
|
|
180
|
+
* bare-pid pidfile remains a LEGACY record this change must handle with a loud warning, not break. The creation
|
|
181
|
+
* identity therefore lives in a SIBLING file `<pidfile>.identity` (the `manager.delivery-aware`
|
|
182
|
+
* marker pattern), holding the {@link formatRecord} two-field pin. Missing sibling = legacy record;
|
|
183
|
+
* present-but-garbled = torn write, refuse; pid mismatch inside the sibling = torn pairing, refuse.
|
|
184
|
+
*/
|
|
185
|
+
export function identityPinPath(pidfilePath) {
|
|
186
|
+
return `${pidfilePath}.identity`;
|
|
187
|
+
}
|
|
188
|
+
/** Write the sibling pin for a pid this launch just spawned. Called AFTER the pidfile write, with
|
|
189
|
+
* the same pid, so a reader that sees a pidfile with no pin yet is reading either a legacy record
|
|
190
|
+
* or a launch mid-pairing - both are visible below, never silent. `undefined` token (Windows,
|
|
191
|
+
* ps-less host) writes NO pin, which is the honest legacy shape for that platform rather than a
|
|
192
|
+
* pin that cannot be checked. */
|
|
193
|
+
export function writeIdentityPin(pidfilePath, pid, tokenAt = defaultStartToken) {
|
|
194
|
+
const rec = identityRecord(pid, tokenAt);
|
|
195
|
+
if (!("token" in rec))
|
|
196
|
+
return; // platform cannot pin: leave the legacy bare-pid shape, loud
|
|
197
|
+
writeFileSync(identityPinPath(pidfilePath), formatRecord(rec));
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* THE open-verify step every teardown runs BEFORE signalling (#969). Read the pidfile's bare pid,
|
|
201
|
+
* then the sibling pin, and adjudicate the pairing:
|
|
202
|
+
* - legacy -> the caller warns and signals for upgrade compatibility
|
|
203
|
+
* - match -> safe to signal; the process behind the pid is the recorded one
|
|
204
|
+
* - gone -> ESRCH-proven dead; the caller may clear the record
|
|
205
|
+
* - mismatch -> PID REUSE: the pid now fronts a DIFFERENT start; NEVER signal
|
|
206
|
+
* - torn-pin/torn-pairing/unpinned -> cannot establish identity on a live pid; refuse, preserve
|
|
207
|
+
*
|
|
208
|
+
* The pidfile argument is the SIBLING'S base path (the pidfile itself), and the bare pid is read
|
|
209
|
+
* from it here so the pairing check cannot drift from however the caller read the pid.
|
|
210
|
+
*/
|
|
211
|
+
export function verifyIdentityPin(pidfilePath, tokenAt = defaultStartToken) {
|
|
212
|
+
let rawPid;
|
|
213
|
+
try {
|
|
214
|
+
rawPid = readFileSync(pidfilePath, "utf8");
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
return { kind: "legacy" }; // no pidfile at all: nothing is pinned; callers treat as absent
|
|
218
|
+
}
|
|
219
|
+
const pidRead = parsePid(rawPid);
|
|
220
|
+
if (pidRead === undefined)
|
|
221
|
+
return { kind: "legacy" }; // husk/unattributable: existing callers own it
|
|
222
|
+
let rawPin;
|
|
223
|
+
try {
|
|
224
|
+
rawPin = readFileSync(identityPinPath(pidfilePath), "utf8");
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
// No pin: a LEGACY record. A live one warns and is signalable for upgrade compatibility; a pid
|
|
228
|
+
// that is ESRCH-dead needs no identity proof, so it reports `gone` and the caller clears the
|
|
229
|
+
// stale record. kill(pid, 0) only: this probe signals nothing.
|
|
230
|
+
return probeLiveness(pidRead) === "dead" ? { kind: "gone" } : { kind: "legacy" };
|
|
231
|
+
}
|
|
232
|
+
// The TORN shapes below refuse when the pid is live or unknown - identity cannot be proven.
|
|
233
|
+
// When the pid is ESRCH-dead there is nothing to signal, so the record is clearable whatever
|
|
234
|
+
// state the pin is in: a torn pin must not wedge the cleanup of a process that no longer exists.
|
|
235
|
+
const dead = probeLiveness(pidRead) === "dead";
|
|
236
|
+
const parsed = parseRecord(rawPin);
|
|
237
|
+
if (parsed.kind !== "record")
|
|
238
|
+
return dead ? { kind: "gone" } : { kind: "torn-pin", raw: parsed.kind === "unattributable" ? parsed.raw : "" };
|
|
239
|
+
if (parsed.record.pid !== pidRead)
|
|
240
|
+
return dead ? { kind: "gone" } : { kind: "torn-pairing", pinPid: parsed.record.pid };
|
|
241
|
+
const verdict = assertRecordIdentity(parsed.record, tokenAt);
|
|
242
|
+
if (verdict.kind === "match")
|
|
243
|
+
return { kind: "match" };
|
|
244
|
+
if (verdict.kind === "gone")
|
|
245
|
+
return { kind: "gone" };
|
|
246
|
+
if (verdict.kind === "mismatch")
|
|
247
|
+
return { kind: "mismatch", record: parsed.record, liveToken: verdict.liveToken };
|
|
248
|
+
return { kind: "unpinned" };
|
|
249
|
+
}
|
|
250
|
+
/** Remove the sibling pin together with a pidfile whose process is PROVEN gone. Never called on a
|
|
251
|
+
* refused stop: the preserved record keeps its pin so the next attempt re-adjudicates. */
|
|
252
|
+
export function removeIdentityPin(pidfilePath) {
|
|
253
|
+
rmSync(identityPinPath(pidfilePath), { force: true });
|
|
254
|
+
}
|
|
255
|
+
/** The loud torn/unpinned refusal, in the same shape as {@link identityRefusal}: name the component,
|
|
256
|
+
* the file, what was found, and the operator's next step. Legacy records do not reach this helper:
|
|
257
|
+
* callers emit {@link identityLegacyWarning} and proceed. */
|
|
258
|
+
export function identityUncertaintyRefusal(label, pidfilePath, verdict) {
|
|
259
|
+
const pin = identityPinPath(pidfilePath);
|
|
260
|
+
if (verdict.kind === "torn-pin")
|
|
261
|
+
return new Error(`refusing to stop ${label} at ${pidfilePath}: its identity pin ${pin} holds unattributable content ${JSON.stringify(verdict.raw)} - a torn or tampered write. The record is preserved.\n` +
|
|
262
|
+
`NEXT: inspect the recorded process with \`ps\`. If it should be stopped, stop it, then rerun this command; once the pid is dead the stale record clears automatically.`);
|
|
263
|
+
if (verdict.kind === "torn-pairing")
|
|
264
|
+
return new Error(`refusing to stop ${label} at ${pidfilePath}: its identity pin ${pin} names pid ${verdict.pinPid}, not the pidfile's pid. The record is preserved.\n` +
|
|
265
|
+
`NEXT: inspect the recorded process with \`ps\`. If it should be stopped, stop it, then rerun this command; once the pid is dead the stale record clears automatically.`);
|
|
266
|
+
if (verdict.kind === "unpinned")
|
|
267
|
+
return new Error(`refusing to stop ${label} at ${pidfilePath}: its process is live but no creation identity could be established for it, so target identity cannot be proven. The record is preserved.\n` +
|
|
268
|
+
`NEXT: stop the process, then rerun this command; once the pid is dead the stale record clears automatically.`);
|
|
269
|
+
return new Error(`refusing to stop ${label} at ${pidfilePath}: the identity state is unsupported (${verdict.kind}). The record is preserved.\n` +
|
|
270
|
+
`NEXT: stop the process, then rerun this command; once the pid is dead the stale record clears automatically.`);
|
|
271
|
+
}
|
|
105
272
|
//# sourceMappingURL=pid.js.map
|
package/dist/pid.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pid.js","sourceRoot":"","sources":["../src/pid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"pid.js","sourceRoot":"","sources":["../src/pid.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.
|
|
4
|
+
"version": "0.41.1",
|
|
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.
|
|
21
|
+
"@cotal-ai/core": "0.41.1"
|
|
22
22
|
},
|
|
23
23
|
"files": [
|
|
24
24
|
"dist"
|