tapline-host 1.6.2 → 1.7.0

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 (108) hide show
  1. package/dist/accounts.d.ts +129 -12
  2. package/dist/accounts.js +844 -75
  3. package/dist/accounts.js.map +1 -1
  4. package/dist/actionlog.d.ts +11 -4
  5. package/dist/actionlog.js +22 -1
  6. package/dist/actionlog.js.map +1 -1
  7. package/dist/animations.d.ts +4 -2
  8. package/dist/animations.js +7 -7
  9. package/dist/animations.js.map +1 -1
  10. package/dist/api.d.ts +7 -1
  11. package/dist/api.js +12 -1
  12. package/dist/api.js.map +1 -1
  13. package/dist/audit.d.ts +1 -0
  14. package/dist/auditlog.d.ts +40 -0
  15. package/dist/auditlog.js +150 -0
  16. package/dist/auditlog.js.map +1 -0
  17. package/dist/cli.d.ts +8 -0
  18. package/dist/cli.js +143 -7
  19. package/dist/cli.js.map +1 -1
  20. package/dist/control.d.ts +51 -0
  21. package/dist/control.js +110 -0
  22. package/dist/control.js.map +1 -1
  23. package/dist/doctor.d.ts +6 -0
  24. package/dist/doctor.js +61 -4
  25. package/dist/doctor.js.map +1 -1
  26. package/dist/drive.d.ts +5 -2
  27. package/dist/drive.js +50 -13
  28. package/dist/drive.js.map +1 -1
  29. package/dist/elicit.d.ts +47 -0
  30. package/dist/elicit.js +87 -0
  31. package/dist/elicit.js.map +1 -1
  32. package/dist/format.d.ts +6 -0
  33. package/dist/format.js +25 -4
  34. package/dist/format.js.map +1 -1
  35. package/dist/grants.d.ts +297 -0
  36. package/dist/grants.js +938 -0
  37. package/dist/grants.js.map +1 -0
  38. package/dist/grantscmd.d.ts +34 -0
  39. package/dist/grantscmd.js +100 -0
  40. package/dist/grantscmd.js.map +1 -0
  41. package/dist/hostfacts.d.ts +23 -0
  42. package/dist/hostfacts.js +65 -0
  43. package/dist/hostfacts.js.map +1 -0
  44. package/dist/inspect.d.ts +3 -2
  45. package/dist/inspect.js +20 -8
  46. package/dist/inspect.js.map +1 -1
  47. package/dist/mapcmd.d.ts +2 -2
  48. package/dist/mcp.js +1 -1
  49. package/dist/nochange.d.ts +1 -0
  50. package/dist/otpclient.d.ts +79 -0
  51. package/dist/otpclient.js +362 -0
  52. package/dist/otpclient.js.map +1 -0
  53. package/dist/pidlock.d.ts +99 -0
  54. package/dist/pidlock.js +382 -0
  55. package/dist/pidlock.js.map +1 -0
  56. package/dist/places.d.ts +11 -0
  57. package/dist/places.js +28 -2
  58. package/dist/places.js.map +1 -1
  59. package/dist/policy.d.ts +2 -1
  60. package/dist/policy.js +5 -2
  61. package/dist/policy.js.map +1 -1
  62. package/dist/protocol.d.ts +4 -0
  63. package/dist/protocol.js +2 -0
  64. package/dist/protocol.js.map +1 -1
  65. package/dist/report.d.ts +8 -0
  66. package/dist/report.js +34 -1
  67. package/dist/report.js.map +1 -1
  68. package/dist/routes.d.ts +1 -1
  69. package/dist/routes.js +27 -11
  70. package/dist/routes.js.map +1 -1
  71. package/dist/runschedule.d.ts +119 -0
  72. package/dist/runschedule.js +281 -0
  73. package/dist/runschedule.js.map +1 -0
  74. package/dist/runtime.d.ts +48 -1
  75. package/dist/runtime.js +214 -18
  76. package/dist/runtime.js.map +1 -1
  77. package/dist/schedule.d.ts +145 -0
  78. package/dist/schedule.js +401 -0
  79. package/dist/schedule.js.map +1 -0
  80. package/dist/schedulehost.d.ts +73 -0
  81. package/dist/schedulehost.js +391 -0
  82. package/dist/schedulehost.js.map +1 -0
  83. package/dist/script.d.ts +2 -0
  84. package/dist/script.js +2 -0
  85. package/dist/script.js.map +1 -1
  86. package/dist/server.d.ts +6 -0
  87. package/dist/server.js +3 -1
  88. package/dist/server.js.map +1 -1
  89. package/dist/thincall.d.ts +13 -0
  90. package/dist/thincall.js +139 -59
  91. package/dist/thincall.js.map +1 -1
  92. package/dist/tool-text-data.d.ts +1 -1
  93. package/dist/tool-text-data.js +1 -1
  94. package/dist/tool-text-data.js.map +1 -1
  95. package/dist/tool-text.d.ts +1 -1
  96. package/dist/tools.d.ts +116 -6
  97. package/dist/tools.js +758 -134
  98. package/dist/tools.js.map +1 -1
  99. package/dist/unlockcmd.d.ts +44 -0
  100. package/dist/unlockcmd.js +80 -0
  101. package/dist/unlockcmd.js.map +1 -0
  102. package/dist/uxcheck.d.ts +1 -0
  103. package/dist/vault.d.ts +75 -0
  104. package/dist/vault.js +395 -0
  105. package/dist/vault.js.map +1 -0
  106. package/package.json +1 -1
  107. package/skills/tapline/SKILL.md +34 -6
  108. package/skills/tapline/references/setup.md +11 -1
@@ -0,0 +1,362 @@
1
+ import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
2
+ import { readFileSync } from "node:fs";
3
+ import { connect } from "node:net";
4
+ import { homedir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { looseScrubber } from "./accounts.js";
7
+ /**
8
+ * AGENT-TOOLS 1.14 §18.11 and §20.4: the one-time code client. A sign-in code arrives where only Tapwright can read it, so
9
+ * Tapline's host asks Tapwright's daemon for it on loopback:
10
+ *
11
+ * 1. read `serve.json` (`port`, `token`) afresh, and keep the token only for this request;
12
+ * 2. open ONE connection to the literal `127.0.0.1:<port>` and have the daemon prove it holds the token (`POST /api/prove`,
13
+ * no token sent), checking the proof for the address and port actually dialled, in constant time;
14
+ * 3. only then, on that same connection, `POST /otp` with the token. A connection the daemon closed gets nothing, and the
15
+ * token never goes to a second connection.
16
+ *
17
+ * This is a raw `net.Socket` with a small HTTP/1.1 reader, so that "one socket" is a fact of the code and not a setting of an
18
+ * agent pool. Nothing here prints a token or a code; a failure says why in words.
19
+ */
20
+ /** The address that is dialled, always as this literal (no name, no fallback). */
21
+ export const LOOPBACK = "127.0.0.1";
22
+ const PROOF_LABEL = "tapwright-proof-v2";
23
+ /** How a code is shown wherever it could be seen (§20.3). */
24
+ export const OTP_PLACEHOLDER = "<one-time code>";
25
+ const MAX_RESPONSE = 64 * 1024;
26
+ /** How many codes the redaction keeps for the session. */
27
+ /** A session never forgets a code; past this many distinct codes a new one is refused rather than an old one dropped. */
28
+ const MAX_CODES = 1000;
29
+ /** `${otp.<app>}` as the whole text of a step: the app's id (reverse-domain names, so letters, digits, '.', '_' and '-'). */
30
+ const OTP_WHOLE = /^\$\{otp\.([A-Za-z0-9._-]+)\}$/;
31
+ /** The app id a text names when it is exactly `${otp.<app>}`; null for any other text. */
32
+ export function otpReference(text) {
33
+ return OTP_WHOLE.exec(text)?.[1] ?? null;
34
+ }
35
+ const fail = (reason, message) => ({ ok: false, reason, message });
36
+ /** The words for a step that has no code. They name no token and no code. */
37
+ export function noCodeMessage(app) {
38
+ return `no one-time code for ${app}: Tapwright has none waiting. It needs Tapwright 1.7's daemon running, with this app's code setting turned on, and the code must come within 2 minutes after the app showed a code field.`;
39
+ }
40
+ function tapwrightHome(deps) {
41
+ if (deps.home !== undefined)
42
+ return deps.home;
43
+ const override = (deps.env ?? process.env)["TAPWRIGHT_HOME"];
44
+ return override !== undefined && override.length > 0 ? override : join((deps.homeDir ?? homedir)(), ".tapwright");
45
+ }
46
+ /** `serve.json`, read now. The token is returned to the one request that asked and kept nowhere else. */
47
+ function readServe(deps) {
48
+ let text;
49
+ try {
50
+ text = readFileSync(join(tapwrightHome(deps), "serve.json"), "utf8");
51
+ }
52
+ catch {
53
+ return fail("no_file", "Tapwright's daemon file (serve.json) isn't there; start Tapwright's daemon (Tapwright 1.7) and try again.");
54
+ }
55
+ let parsed;
56
+ try {
57
+ parsed = JSON.parse(text);
58
+ }
59
+ catch {
60
+ // Node's own message would quote part of the text, which holds a token.
61
+ return fail("bad_file", "Tapwright's daemon file (serve.json) isn't valid JSON.");
62
+ }
63
+ const top = typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : null;
64
+ const port = top?.port;
65
+ const token = top?.token;
66
+ if (typeof port !== "number" || !Number.isInteger(port) || port < 1 || port > 65535 || typeof token !== "string" || token === "") {
67
+ return fail("bad_file", "Tapwright's daemon file (serve.json) has no usable port and token.");
68
+ }
69
+ return { port, token };
70
+ }
71
+ /** HMAC-SHA256(token, "tapwright-proof-v2" NUL challenge-bytes NUL "127.0.0.1" NUL port), as hex. */
72
+ export function proofFor(token, challenge, address, port) {
73
+ const nul = Buffer.from([0]);
74
+ const message = Buffer.concat([Buffer.from(PROOF_LABEL, "utf8"), nul, challenge, nul, Buffer.from(address, "utf8"), nul, Buffer.from(String(port), "ascii")]);
75
+ return createHmac("sha256", token).update(message).digest("hex");
76
+ }
77
+ function sameProof(given, wanted) {
78
+ if (typeof given !== "string")
79
+ return false;
80
+ const a = Buffer.from(given.toLowerCase(), "utf8");
81
+ const b = Buffer.from(wanted, "utf8");
82
+ return a.length === b.length && timingSafeEqual(a, b);
83
+ }
84
+ class Closed extends Error {
85
+ }
86
+ /** A parsed response, or null while more bytes are needed. Throws on anything that isn't a plain HTTP/1.1 answer. */
87
+ function parseReply(buffer) {
88
+ const end = buffer.indexOf("\r\n\r\n");
89
+ if (end < 0) {
90
+ if (buffer.length > MAX_RESPONSE)
91
+ throw new Error("too long");
92
+ return null;
93
+ }
94
+ const lines = buffer.subarray(0, end).toString("latin1").split("\r\n");
95
+ const match = /^HTTP\/1\.1 (\d{3})(?: |$)/.exec(lines[0] ?? "");
96
+ if (match === null)
97
+ throw new Error("not HTTP/1.1");
98
+ const headers = {};
99
+ for (const line of lines.slice(1)) {
100
+ const colon = line.indexOf(":");
101
+ if (colon > 0)
102
+ headers[line.slice(0, colon).trim().toLowerCase()] = line.slice(colon + 1).trim();
103
+ }
104
+ const status = Number(match[1]);
105
+ const rest = buffer.subarray(end + 4);
106
+ if ((headers["transfer-encoding"] ?? "").toLowerCase() === "chunked") {
107
+ const parts = [];
108
+ let at = 0;
109
+ for (;;) {
110
+ const lineEnd = rest.indexOf("\r\n", at);
111
+ if (lineEnd < 0)
112
+ return null;
113
+ const size = Number.parseInt(rest.subarray(at, lineEnd).toString("latin1").split(";")[0] ?? "", 16);
114
+ if (!Number.isInteger(size) || size < 0)
115
+ throw new Error("bad chunk");
116
+ if (size === 0) {
117
+ // The trailer's end: an empty line.
118
+ return rest.length >= lineEnd + 4 ? { status, headers, body: Buffer.concat(parts).toString("utf8") } : null;
119
+ }
120
+ if (rest.length < lineEnd + 2 + size + 2)
121
+ return null;
122
+ parts.push(rest.subarray(lineEnd + 2, lineEnd + 2 + size));
123
+ at = lineEnd + 2 + size + 2;
124
+ }
125
+ }
126
+ const lengthText = headers["content-length"];
127
+ if (lengthText === undefined) {
128
+ if (status === 204 || status === 304)
129
+ return { status, headers, body: "" };
130
+ throw new Error("no length");
131
+ }
132
+ const length = Number(lengthText);
133
+ if (!Number.isInteger(length) || length < 0 || length > MAX_RESPONSE)
134
+ throw new Error("bad length");
135
+ return rest.length >= length ? { status, headers, body: rest.subarray(0, length).toString("utf8") } : null;
136
+ }
137
+ class Wire {
138
+ socket;
139
+ buffer = Buffer.alloc(0);
140
+ wake;
141
+ /** The peer ended the connection, or it failed. */
142
+ gone = false;
143
+ constructor(socket) {
144
+ this.socket = socket;
145
+ socket.on("data", (chunk) => {
146
+ this.buffer = Buffer.concat([this.buffer, chunk]);
147
+ this.wake?.();
148
+ });
149
+ const end = () => {
150
+ this.gone = true;
151
+ this.wake?.();
152
+ };
153
+ socket.on("end", end);
154
+ socket.on("close", end);
155
+ socket.on("error", end);
156
+ }
157
+ /** Whether another request may still go on this connection. */
158
+ usable() {
159
+ return !this.gone && !this.socket.destroyed && this.socket.writable;
160
+ }
161
+ async request(path, port, body, authorization) {
162
+ const head = [
163
+ `POST ${path} HTTP/1.1`,
164
+ `Host: ${LOOPBACK}:${port}`,
165
+ "Content-Type: application/json",
166
+ `Content-Length: ${Buffer.byteLength(body)}`,
167
+ "Connection: keep-alive",
168
+ ...(authorization !== undefined ? [`Authorization: ${authorization}`] : []),
169
+ "",
170
+ "",
171
+ ].join("\r\n");
172
+ this.buffer = Buffer.alloc(0);
173
+ await new Promise((resolve, reject) => {
174
+ this.socket.write(head + body, (error) => (error ? reject(new Closed("write failed")) : resolve()));
175
+ });
176
+ for (;;) {
177
+ let reply;
178
+ try {
179
+ reply = parseReply(this.buffer);
180
+ }
181
+ catch {
182
+ throw new Error("bad reply");
183
+ }
184
+ if (reply !== null)
185
+ return reply;
186
+ if (this.gone)
187
+ throw new Closed("closed before an answer");
188
+ await new Promise((resolve) => {
189
+ this.wake = () => {
190
+ this.wake = undefined;
191
+ resolve();
192
+ };
193
+ });
194
+ }
195
+ }
196
+ }
197
+ function dial(port) {
198
+ return new Promise((resolve, reject) => {
199
+ // The literal address, IPv4 only: no name lookup, no fallback to localhost.
200
+ const socket = connect({ host: LOOPBACK, port, family: 4 });
201
+ socket.setNoDelay(true);
202
+ socket.once("connect", () => {
203
+ socket.removeListener("error", reject);
204
+ resolve(socket);
205
+ });
206
+ socket.once("error", reject);
207
+ });
208
+ }
209
+ function parseJson(text) {
210
+ try {
211
+ const parsed = JSON.parse(text);
212
+ return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : null;
213
+ }
214
+ catch {
215
+ return null;
216
+ }
217
+ }
218
+ /**
219
+ * Asks Tapwright's daemon for the code of an app (§18.11). The caller has already checked that a step is typing
220
+ * `${otp.<that app>}` for the connected app; this is the only way a code comes in.
221
+ */
222
+ export async function fetchOtp(app, deps = {}) {
223
+ const serve = readServe(deps);
224
+ if ("ok" in serve)
225
+ return serve;
226
+ const { port, token } = serve;
227
+ const random = deps.random ?? randomBytes;
228
+ const now = deps.now ?? Date.now;
229
+ const challenge = random(32);
230
+ let wire;
231
+ let timer;
232
+ // Set when the time is up: after that nothing more is sent, and a connection that completes late is closed.
233
+ let aborted = false;
234
+ const timedOut = () => fail("timeout", "Tapwright's daemon didn't answer in time.");
235
+ const run = async () => {
236
+ let socket;
237
+ try {
238
+ socket = await (deps.dial ?? dial)(port);
239
+ }
240
+ catch {
241
+ return fail("unreachable", "Tapwright's daemon didn't answer on this computer; start it (Tapwright 1.7) and try again.");
242
+ }
243
+ if (aborted) {
244
+ socket.destroy();
245
+ return timedOut();
246
+ }
247
+ const opened = new Wire(socket);
248
+ wire = opened;
249
+ // Only the socket's own answer about where it is connected counts: the address and port the proof is made for.
250
+ if (socket.remoteAddress !== LOOPBACK || socket.remotePort !== port)
251
+ return fail("unreachable", "Tapwright's daemon didn't answer on this computer.");
252
+ let proved;
253
+ try {
254
+ proved = await opened.request("/api/prove", port, JSON.stringify({ challenge: challenge.toString("hex") }), undefined);
255
+ }
256
+ catch (error) {
257
+ return error instanceof Closed ? fail("no_proof", "Tapwright's daemon closed the connection before it proved itself; nothing was sent to it.") : fail("bad_reply", "Tapwright's daemon answered in a form this Tapline doesn't know.");
258
+ }
259
+ const proof = proved.status === 200 ? parseJson(proved.body)?.["proof"] : undefined;
260
+ if (!sameProof(proof, proofFor(token, challenge, LOOPBACK, port))) {
261
+ return fail("no_proof", "Whatever answers on Tapwright's daemon port didn't prove it holds Tapwright's token, so nothing was sent to it.");
262
+ }
263
+ // A daemon that closes its connection after the proof gets nothing: the token never goes to a second connection.
264
+ const settle = deps.settleMs ?? 15;
265
+ if (settle > 0)
266
+ await new Promise((done) => setTimeout(done, settle));
267
+ if (aborted)
268
+ return timedOut();
269
+ if (!opened.usable() || (proved.headers["connection"] ?? "").toLowerCase().includes("close")) {
270
+ return fail("closed", "Tapwright's daemon closed the connection after it proved itself; nothing was sent, and the token is never sent on a new connection.");
271
+ }
272
+ if (aborted)
273
+ return timedOut();
274
+ let answer;
275
+ try {
276
+ answer = await opened.request("/otp", port, JSON.stringify({ app }), `Bearer ${token}`);
277
+ }
278
+ catch (error) {
279
+ return error instanceof Closed ? fail("closed", "Tapwright's daemon closed the connection before it answered.") : fail("bad_reply", "Tapwright's daemon answered in a form this Tapline doesn't know.");
280
+ }
281
+ if (answer.status === 404)
282
+ return fail("none", noCodeMessage(app));
283
+ if (answer.status !== 200)
284
+ return fail("refused", `Tapwright's daemon refused the request for a one-time code for ${app} (status ${answer.status}).`);
285
+ const body = parseJson(answer.body);
286
+ const code = body?.["code"];
287
+ const expires = body?.["expires"];
288
+ const from = body?.["source"];
289
+ if (typeof code !== "string" || !/^[0-9]{4,12}$/.test(code) || typeof expires !== "number" || !Number.isFinite(expires)) {
290
+ return fail("bad_reply", "Tapwright's daemon sent a one-time code in a form this Tapline doesn't know.");
291
+ }
292
+ if (expires <= now())
293
+ return fail("expired", `the one-time code for ${app} had already expired when it arrived; ask the app for a new one.`);
294
+ // The source is Tapwright's own word for where the code came from; it is a name, never the code.
295
+ const source = typeof from === "string" && /^[A-Za-z0-9._-]{1,100}$/.test(from) && from !== code ? from : undefined;
296
+ return { ok: true, code, expires, source };
297
+ };
298
+ try {
299
+ const limit = new Promise((resolve) => {
300
+ timer = setTimeout(() => {
301
+ aborted = true;
302
+ wire?.socket.destroy();
303
+ resolve(timedOut());
304
+ }, deps.timeoutMs ?? 5000);
305
+ });
306
+ return await Promise.race([run(), limit]);
307
+ }
308
+ catch {
309
+ return fail("unreachable", "Tapwright's daemon couldn't be asked.");
310
+ }
311
+ finally {
312
+ if (timer !== undefined)
313
+ clearTimeout(timer);
314
+ wire?.socket.destroy();
315
+ }
316
+ }
317
+ // -- what the codes of a session are scrubbed from
318
+ /** The codes this session typed. Each one is scrubbed from everything shown or kept, for the rest of the session (§20.3). */
319
+ export class OtpCodes {
320
+ codes = [];
321
+ /** Built on first use after a code is added, so a burst of codes compiles once. */
322
+ scrub = null;
323
+ /**
324
+ * Keeps scrubbing a code for the rest of the session: none is ever dropped (§20.3). Codes are short-lived and few; past a
325
+ * large cap a new code is refused (false) instead of an old one being forgotten.
326
+ */
327
+ register(code) {
328
+ if (this.codes.includes(code))
329
+ return true;
330
+ if (this.codes.length >= MAX_CODES)
331
+ return false;
332
+ this.codes = [...this.codes, code];
333
+ this.scrub = null;
334
+ return true;
335
+ }
336
+ token = OTP_PLACEHOLDER;
337
+ /** The codes so far: the same array until a code is added. */
338
+ values() {
339
+ return this.codes;
340
+ }
341
+ /** Arrow, so it can be handed on as a plain function. */
342
+ redact = (text) => {
343
+ if (this.codes.length === 0)
344
+ return text;
345
+ this.scrub ??= looseScrubber(this.codes, OTP_PLACEHOLDER);
346
+ return this.scrub(text);
347
+ };
348
+ }
349
+ export function createOtpService(deps = {}, codes = new OtpCodes()) {
350
+ return {
351
+ codes,
352
+ async fetch(app) {
353
+ const result = await fetchOtp(app, deps);
354
+ if (result.ok && !codes.register(result.code)) {
355
+ // Never hand on a code that couldn't be registered for scrubbing.
356
+ return { ok: false, reason: "refused", message: "this session has used too many one-time codes to scrub them all; restart Tapline to type another." };
357
+ }
358
+ return result;
359
+ },
360
+ };
361
+ }
362
+ //# sourceMappingURL=otpclient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otpclient.js","sourceRoot":"","sources":["../src/otpclient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * AGENT-TOOLS 1.14.1 §22.3 and §19.5: the pid-liveness lock shared by the run lock (`<schedule>.lock`) and the policy file's edit lock
3
+ * (`<policy file>.edit.lock`). A lock is a file that holds its holder's owner token, process id and the process's start time. It
4
+ * is published whole and atomically: the full JSON goes to a unique temp file, which is hard-linked to the lock's name (`link` is an
5
+ * exclusive create on NTFS, APFS and ext4: EEXIST when the lock exists), then the temp file is unlinked. A lock is therefore never
6
+ * seen empty or half written. Where hard links aren't available the lock is created with `wx` and written in place; a lock that can't
7
+ * be read as a holder is then a run that is still writing it.
8
+ *
9
+ * A lock is a leftover only on PROOF of its holder's death: its process doesn't run, or the process with that id started at another
10
+ * time. An empty, cut-short or garbage lock is never a leftover (there is no age rule): it is waited out, which fails closed.
11
+ * A leftover is taken over under `<lock>.takeover`, published the same way.
12
+ */
13
+ /** What the lock needs from the process and the system. Everything is injectable so a test can pause at any point. */
14
+ export interface PidLockDeps {
15
+ pid: number;
16
+ alive: (pid: number) => boolean;
17
+ /** When a process started, as text that is the same each time for one process (null: unknown). A lock keeps it so a reused process id is not taken for the holder. */
18
+ startOf?: (pid: number) => string | null;
19
+ /** For tests: runs after a run found a leftover lock and before it takes the lock over (another run may get in between). */
20
+ beforeReclaim?: () => void;
21
+ /** For tests: runs after the temp file is written and before it is linked to the lock's name. */
22
+ beforePublish?: (path: string) => void;
23
+ /** For tests: the hard link (default `fs.linkSync`); a throw of EPERM stands for a file system without hard links. */
24
+ link?: (existing: string, path: string) => void;
25
+ /** For tests: runs in the no-hard-link fallback, after the exclusive create and before the content is written. */
26
+ afterCreate?: (path: string) => void;
27
+ /** For tests: runs in an unlock after a file was judged stuck and before it is removed (a new holder may get in between). */
28
+ beforeRemove?: (path: string) => void;
29
+ }
30
+ /** What runs a program and answers its output; throws when it fails. Injectable, so the start time of a process can be tested. */
31
+ export type RunProgram = (file: string, args: string[]) => string;
32
+ /**
33
+ * When a process started, as text that is the same each time it is asked for the same process; null when it can't be told. A lock
34
+ * keeps it beside the process id, so a process id that was reused by another program is not mistaken for the holder. Windows asks
35
+ * PowerShell (`Get-Process -Id N`, about half a second, asked only for a lock's own holder and when a lock is found), elsewhere `ps`.
36
+ */
37
+ export declare function processStartTime(pid: number, platform?: string, run?: RunProgram): string | null;
38
+ /** Whether a process runs (signal 0); a process we may not signal still runs. */
39
+ export declare function processAlive(pid: number): boolean;
40
+ /** The deps of this process for a lock of its own (this process's start time is asked once and kept). */
41
+ export declare function ownLockDeps(): PidLockDeps;
42
+ /**
43
+ * Creates `path` with `text`, whole, only if it doesn't exist: "published", or "exists" (someone's file stands there), or "error".
44
+ * The text is written to a unique temp file first and hard-linked into place, so `path` is never seen empty.
45
+ */
46
+ export declare function publishNew(deps: PidLockDeps, path: string, text: string): "published" | "exists" | "error";
47
+ /**
48
+ * Takes the lock at `path` (its folder must exist): the owner token, or null when a live holder has it. A live holder is never
49
+ * displaced, however long it takes. A leftover (holder provably dead) is taken over under `<path>.takeover`, published the same way:
50
+ * under it the lock is read again, and only if it is unchanged and its holder is still dead is it deleted and replaced by the taker's.
51
+ * A `.takeover` whose own process is dead is cleared the same way.
52
+ */
53
+ export declare function takePidLock(deps: PidLockDeps, path: string): string | null;
54
+ /** Releases the lock only while its content is still the holder's own (`token`); another's lock is never removed. */
55
+ export declare function dropPidLock(path: string, token: string): void;
56
+ /** Whether the lock's holder is `token` right now (for a holder that wants to know it still has it). */
57
+ export declare function holdsPidLock(path: string, token: string): boolean;
58
+ /**
59
+ * What a lock file is right now. `stuck` is a lock no run will ever clear by itself and a person has to see: empty, unreadable, or
60
+ * held by a process that is provably dead. `live` is a holder that is proven alive (never displaced).
61
+ */
62
+ export type StuckState = {
63
+ state: "stuck";
64
+ why: "empty" | "unreadable" | "dead";
65
+ pid?: number;
66
+ };
67
+ export type PidLockState = {
68
+ state: "absent";
69
+ } | {
70
+ state: "live";
71
+ pid: number;
72
+ } | StuckState;
73
+ /** Looks at the lock at `path` without changing it. */
74
+ export declare function inspectPidLock(deps: PidLockDeps, path: string): PidLockState;
75
+ /** The words for a stuck lock's cause. */
76
+ export declare function stuckWords(state: StuckState): string;
77
+ export interface UnlockResult {
78
+ /** `none`: no lock file; `live`: a live holder is proven, nothing removed; `removed`: stuck files removed; `failed`: a stuck file couldn't be removed. */
79
+ status: "none" | "live" | "removed" | "failed";
80
+ /** The live holder's process id when `status` is `live`. */
81
+ pid?: number;
82
+ /** Every file removed, with why. */
83
+ removed: Array<{
84
+ path: string;
85
+ why: string;
86
+ }>;
87
+ /** The file of a `live` or `failed` answer. */
88
+ path?: string;
89
+ }
90
+ /**
91
+ * Removes the lock at `path` and its `.takeover`, but only when no holder is proven alive: an empty or unreadable file, or one whose
92
+ * holder is dead (its process is gone, or has another start time). A live holder in either file refuses and removes nothing.
93
+ *
94
+ * It runs under the lock's own `.takeover` (published whole, as a takeover is), so it never overlaps a takeover, and acquisition
95
+ * honours `.takeover` too. Under it the exact bytes of the lock are read once, THAT snapshot's holder is judged, and the file is
96
+ * removed only if it still holds exactly those bytes (moved to a tombstone and compared; a different file is linked back).
97
+ * A `.takeover` that is itself stuck is cleared the same way first.
98
+ */
99
+ export declare function unlockPidLock(deps: PidLockDeps, path: string): UnlockResult;