opencode-feature-factory 0.2.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 (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +877 -0
  3. package/assets/agent/backend-builder.md +73 -0
  4. package/assets/agent/codebase-researcher.md +56 -0
  5. package/assets/agent/design-interpreter.md +37 -0
  6. package/assets/agent/frontend-builder.md +74 -0
  7. package/assets/agent/implementation-validator.md +73 -0
  8. package/assets/agent/security-reviewer.md +60 -0
  9. package/assets/agent/spec-writer.md +94 -0
  10. package/assets/agent/story-reader.md +38 -0
  11. package/assets/agent/story-writer.md +39 -0
  12. package/assets/agent/test-verifier.md +73 -0
  13. package/assets/agent/work-decomposer.md +102 -0
  14. package/assets/agent/work-reviewer.md +77 -0
  15. package/assets/command/feature.md +68 -0
  16. package/assets/skills/feature/SCHEMA.md +990 -0
  17. package/assets/skills/feature/SKILL.md +620 -0
  18. package/dist/tui.js +3641 -0
  19. package/package.json +65 -0
  20. package/src/cleanup-sweep-command.js +75 -0
  21. package/src/cleanup-sweep-eligibility.js +581 -0
  22. package/src/cleanup-sweep-output.js +139 -0
  23. package/src/cleanup-sweep-report.js +548 -0
  24. package/src/cleanup-sweep.js +546 -0
  25. package/src/cli-output.js +251 -0
  26. package/src/cli.js +1152 -0
  27. package/src/config.js +231 -0
  28. package/src/cost-attribution.js +327 -0
  29. package/src/cost-report.js +185 -0
  30. package/src/detached-log-supervisor.js +178 -0
  31. package/src/doctor.js +598 -0
  32. package/src/env-snapshot.js +211 -0
  33. package/src/factory-diagnostics.js +429 -0
  34. package/src/factory-paths.js +40 -0
  35. package/src/factory.js +4769 -0
  36. package/src/feature-command-payload.js +378 -0
  37. package/src/git.js +110 -0
  38. package/src/github.js +252 -0
  39. package/src/hardening/atomic-write.js +954 -0
  40. package/src/hardening/line-output.js +139 -0
  41. package/src/hardening/output-policy.js +365 -0
  42. package/src/hardening/process-verification.js +542 -0
  43. package/src/hardening/sensitive-data.js +341 -0
  44. package/src/hardening/terminal-encoding.js +224 -0
  45. package/src/plugin.js +246 -0
  46. package/src/post-pr-ci.js +754 -0
  47. package/src/process-evidence.js +1139 -0
  48. package/src/refs.js +144 -0
  49. package/src/run-state.js +2411 -0
  50. package/src/steering-conflicts.js +77 -0
  51. package/src/telemetry.js +419 -0
  52. package/src/tui-data.js +499 -0
  53. package/src/tui-rendering.js +148 -0
  54. package/src/utils.js +35 -0
  55. package/src/validate.js +1655 -0
  56. package/src/worktrees.js +56 -0
@@ -0,0 +1,542 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { readFileSync, readlinkSync } from "node:fs";
3
+ import { hostname as systemHostname } from "node:os";
4
+ import { isAbsolute, posix, resolve } from "node:path";
5
+
6
+ export const PROCESS_INSPECTOR = "node-process";
7
+ export const VERIFIED_PROCESS_SIGNAL = "SIGTERM";
8
+ export const PROCESS_SIGNAL_RACE_LIMITATION =
9
+ "Portable process inspection and signaling are not atomic; exit or PID reuse after the final identity check can still cause SIGTERM to reach a replacement process.";
10
+
11
+ export function normalizeLegacyBooleanLiveness(value) {
12
+ if (value === true) return "live";
13
+ if (value === false) return "absent";
14
+ return "indeterminate";
15
+ }
16
+
17
+ export function probeLegacyBooleanLiveness(callback, ...args) {
18
+ if (typeof callback !== "function") return "indeterminate";
19
+ try {
20
+ return normalizeLegacyBooleanLiveness(callback(...args));
21
+ } catch {
22
+ return "indeterminate";
23
+ }
24
+ }
25
+
26
+ export function publicLivenessBoolean(status) {
27
+ if (status === "live") return true;
28
+ if (status === "absent") return false;
29
+ return null;
30
+ }
31
+
32
+ const DEFAULT_COMMAND_TIMEOUT_MS = 5_000;
33
+ const DEFAULT_COMMAND_MAX_BUFFER = 1024 * 1024;
34
+ const DEFAULT_POLL_INTERVAL_MS = 200;
35
+ const DARWIN_WEEKDAYS = Object.freeze(["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]);
36
+ const DARWIN_MONTHS = Object.freeze(["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]);
37
+ const DARWIN_START_PATTERN = /^(Sun|Mon|Tue|Wed|Thu|Fri|Sat) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ([1-9]|[12]\d|3[01]) ([01]\d|2[0-3]):([0-5]\d):([0-5]\d) (\d{4})$/u;
38
+
39
+ export const PROCESS_VERIFICATION_CODES = Object.freeze({
40
+ INVALID_PID: "INVALID_PID",
41
+ INVALID_OPTIONS: "INVALID_OPTIONS",
42
+ LIVENESS_LIVE: "LIVENESS_LIVE",
43
+ LIVENESS_ABSENT: "LIVENESS_ABSENT",
44
+ LIVENESS_PERMISSION_DENIED: "LIVENESS_PERMISSION_DENIED",
45
+ LIVENESS_FAILED: "LIVENESS_FAILED",
46
+ LIVENESS_RESULT_MALFORMED: "LIVENESS_RESULT_MALFORMED",
47
+ PLATFORM_UNSUPPORTED: "PLATFORM_UNSUPPORTED",
48
+ METADATA_UNAVAILABLE: "METADATA_UNAVAILABLE",
49
+ METADATA_MALFORMED: "METADATA_MALFORMED",
50
+ INSPECTION_TIMEOUT: "INSPECTION_TIMEOUT",
51
+ IDENTITY_LIVE: "IDENTITY_LIVE",
52
+ IDENTITY_INVALID: "IDENTITY_INVALID",
53
+ IDENTITY_MATCH: "IDENTITY_MATCH",
54
+ IDENTITY_MISMATCH: "IDENTITY_MISMATCH",
55
+ SIGNAL_INVALID: "SIGNAL_INVALID",
56
+ SIGNAL_NOT_AUTHORIZED: "SIGNAL_NOT_AUTHORIZED",
57
+ SIGNAL_SENT: "SIGNAL_SENT",
58
+ SIGNAL_FAILED: "SIGNAL_FAILED",
59
+ POST_SIGNAL_NOT_CHECKED: "POST_SIGNAL_NOT_CHECKED",
60
+ POST_SIGNAL_ABSENT: "POST_SIGNAL_ABSENT",
61
+ POST_SIGNAL_LIVE: "POST_SIGNAL_LIVE",
62
+ POST_SIGNAL_MISMATCH: "POST_SIGNAL_MISMATCH",
63
+ POST_SIGNAL_INDETERMINATE: "POST_SIGNAL_INDETERMINATE",
64
+ });
65
+
66
+ const REASONS = Object.freeze({
67
+ INVALID_PID: "process pid must be a positive integer",
68
+ INVALID_OPTIONS: "process verification options are invalid",
69
+ LIVENESS_LIVE: "process is live",
70
+ LIVENESS_ABSENT: "process is absent",
71
+ LIVENESS_PERMISSION_DENIED: "process liveness is indeterminate because permission was denied",
72
+ LIVENESS_FAILED: "process liveness could not be determined",
73
+ LIVENESS_RESULT_MALFORMED: "process liveness probe returned a malformed result",
74
+ PLATFORM_UNSUPPORTED: "process identity inspection is unsupported on this platform",
75
+ METADATA_UNAVAILABLE: "process identity metadata could not be inspected",
76
+ METADATA_MALFORMED: "process identity metadata is malformed",
77
+ INSPECTION_TIMEOUT: "process identity inspection timed out",
78
+ IDENTITY_LIVE: "process identity is live",
79
+ IDENTITY_INVALID: "expected process identity is invalid",
80
+ IDENTITY_MATCH: "process identity matches expected identity",
81
+ IDENTITY_MISMATCH: "process identity does not match expected identity",
82
+ SIGNAL_INVALID: "only SIGTERM may be sent by verified process signaling",
83
+ SIGNAL_NOT_AUTHORIZED: "process signal was not authorized by identity verification",
84
+ SIGNAL_SENT: "SIGTERM was sent to the verified process",
85
+ SIGNAL_FAILED: "SIGTERM could not be sent to the verified process",
86
+ POST_SIGNAL_NOT_CHECKED: "process state was not checked after SIGTERM",
87
+ POST_SIGNAL_ABSENT: "process absence was confirmed after SIGTERM",
88
+ POST_SIGNAL_LIVE: "the matching process remains live after SIGTERM",
89
+ POST_SIGNAL_MISMATCH: "a different process identity was observed after SIGTERM",
90
+ POST_SIGNAL_INDETERMINATE: "process state is indeterminate after SIGTERM",
91
+ });
92
+
93
+ class InspectionFailure extends Error {
94
+ constructor(code) {
95
+ super(REASONS[code]);
96
+ this.code = code;
97
+ }
98
+ }
99
+
100
+ export function probeProcessLiveness(pid, options = {}) {
101
+ if (!positivePid(pid)) return result("indeterminate", "INVALID_PID", pid);
102
+
103
+ const injectedProbe = firstFunction(options.livenessProbe, options.livenessProbeFn, options.processLivenessProbe);
104
+ if (injectedProbe) {
105
+ try {
106
+ return normalizeLivenessResult(injectedProbe(pid), pid);
107
+ } catch (error) {
108
+ return livenessErrorResult(error, pid);
109
+ }
110
+ }
111
+
112
+ try {
113
+ process.kill(pid, 0);
114
+ return result("live", "LIVENESS_LIVE", pid);
115
+ } catch (error) {
116
+ return livenessErrorResult(error, pid);
117
+ }
118
+ }
119
+
120
+ export function inspectProcessIdentity(pid, options = {}) {
121
+ const liveness = probeProcessLiveness(pid, options);
122
+ if (liveness.status !== "live") return liveness;
123
+
124
+ const platform = resolvePlatform(options);
125
+ if (platform !== "linux" && platform !== "darwin") {
126
+ return result("indeterminate", "PLATFORM_UNSUPPORTED", pid);
127
+ }
128
+
129
+ try {
130
+ const identity = platform === "linux"
131
+ ? inspectLinuxIdentity(pid, options)
132
+ : inspectDarwinIdentity(pid, options);
133
+ identity.hostname = resolveHostname(options);
134
+ return {
135
+ ...result("live", "IDENTITY_LIVE", pid),
136
+ identity,
137
+ };
138
+ } catch (error) {
139
+ const failure = error instanceof InspectionFailure
140
+ ? error
141
+ : new InspectionFailure(isTimeoutError(error) ? "INSPECTION_TIMEOUT" : "METADATA_UNAVAILABLE");
142
+ const refreshed = probeProcessLiveness(pid, options);
143
+ if (refreshed.status === "absent") return refreshed;
144
+ return {
145
+ ...result("indeterminate", failure.code, pid),
146
+ liveness_status: refreshed.status,
147
+ };
148
+ }
149
+ }
150
+
151
+ export function verifyProcessIdentity(expected, options = {}) {
152
+ const normalizedExpected = normalizeExpectedIdentity(expected);
153
+ if (!normalizedExpected) return result("indeterminate", "IDENTITY_INVALID", expected?.pid ?? null);
154
+
155
+ const inspected = inspectProcessIdentity(normalizedExpected.pid, options);
156
+ if (inspected.status !== "live") return inspected;
157
+
158
+ const actual = normalizeInspectedIdentity(inspected.identity);
159
+ if (!actual) return result("indeterminate", "METADATA_MALFORMED", normalizedExpected.pid);
160
+
161
+ const mismatchedFields = [];
162
+ for (const field of ["pid", "inspector", "start_marker", "command_name", "cwd"]) {
163
+ if (actual[field] !== normalizedExpected[field]) mismatchedFields.push(field);
164
+ }
165
+
166
+ if (mismatchedFields.length > 0) {
167
+ return {
168
+ ...result("mismatched", "IDENTITY_MISMATCH", normalizedExpected.pid),
169
+ mismatched_fields: mismatchedFields,
170
+ };
171
+ }
172
+
173
+ return {
174
+ ...result("live-and-matching", "IDENTITY_MATCH", normalizedExpected.pid),
175
+ identity: inspected.identity,
176
+ };
177
+ }
178
+
179
+ export async function signalVerifiedProcess(expected, options = {}) {
180
+ const signal = options.signal ?? VERIFIED_PROCESS_SIGNAL;
181
+ if (signal !== VERIFIED_PROCESS_SIGNAL) {
182
+ return signalResult("not-signaled", "SIGNAL_INVALID", expected?.pid ?? null);
183
+ }
184
+
185
+ const polling = normalizePollingOptions(options);
186
+ if (!polling) return signalResult("not-signaled", "INVALID_OPTIONS", expected?.pid ?? null);
187
+
188
+ let deadline = null;
189
+ if (polling.waitForExitMs > 0) {
190
+ try {
191
+ const started = polling.clock();
192
+ if (!Number.isFinite(started)) return signalResult("not-signaled", "INVALID_OPTIONS", expected?.pid ?? null);
193
+ deadline = started + polling.waitForExitMs;
194
+ } catch {
195
+ return signalResult("not-signaled", "INVALID_OPTIONS", expected?.pid ?? null);
196
+ }
197
+ }
198
+
199
+ const signalFn = firstFunction(options.signalFn, options.processSignalFn) || process.kill.bind(process);
200
+
201
+ // No callback, hook, wait, or fallback belongs between this final inspection
202
+ // and the single targeted signal operation.
203
+ const verification = verifyProcessIdentity(expected, options);
204
+ if (verification.status !== "live-and-matching") {
205
+ return {
206
+ ...signalResult("not-signaled", "SIGNAL_NOT_AUTHORIZED", verification.pid),
207
+ verification,
208
+ };
209
+ }
210
+
211
+ const pid = verification.pid;
212
+ if (!positivePid(pid)) {
213
+ return signalResult("not-signaled", "SIGNAL_NOT_AUTHORIZED", pid);
214
+ }
215
+
216
+ try {
217
+ signalFn(pid, VERIFIED_PROCESS_SIGNAL);
218
+ } catch {
219
+ return {
220
+ ...signalResult("signal-failed", "SIGNAL_FAILED", pid),
221
+ verification,
222
+ };
223
+ }
224
+
225
+ const postSignal = deadline === null
226
+ ? postSignalResult("not-checked", "POST_SIGNAL_NOT_CHECKED")
227
+ : await observeAfterSignal(expected, options, polling, deadline);
228
+ return {
229
+ ...signalResult("signaled", "SIGNAL_SENT", pid),
230
+ verification,
231
+ post_signal: postSignal,
232
+ };
233
+ }
234
+
235
+ function inspectLinuxIdentity(pid, options) {
236
+ const readFile = firstFunction(options.procReadFile, options.procReadFileFn, options.readProcFileFn)
237
+ || ((path) => readFileSync(path, "utf8"));
238
+ const readLink = firstFunction(options.procReadlink, options.procReadlinkFn, options.readProcLinkFn)
239
+ || ((path) => readlinkSync(path));
240
+
241
+ let firstStat;
242
+ let command;
243
+ let cwd;
244
+ let finalStat;
245
+ try {
246
+ firstStat = readFile(`/proc/${pid}/stat`, "utf8");
247
+ command = readFile(`/proc/${pid}/comm`, "utf8");
248
+ cwd = readLink(`/proc/${pid}/cwd`);
249
+ finalStat = readFile(`/proc/${pid}/stat`, "utf8");
250
+ } catch (error) {
251
+ throw new InspectionFailure(isTimeoutError(error) ? "INSPECTION_TIMEOUT" : "METADATA_UNAVAILABLE");
252
+ }
253
+
254
+ const firstStart = parseLinuxStartMarker(firstStat, pid);
255
+ const finalStart = parseLinuxStartMarker(finalStat, pid);
256
+ const commandName = normalizeCommandName(command);
257
+ const resolvedCwd = normalizeCwd(cwd);
258
+ if (!firstStart || !finalStart || !commandName || !resolvedCwd) {
259
+ throw new InspectionFailure("METADATA_MALFORMED");
260
+ }
261
+ if (firstStart !== finalStart) throw new InspectionFailure("METADATA_MALFORMED");
262
+
263
+ return {
264
+ pid,
265
+ inspector: PROCESS_INSPECTOR,
266
+ start_marker: `linux-procfs:${firstStart}`,
267
+ command_name: commandName,
268
+ cwd: resolvedCwd,
269
+ };
270
+ }
271
+
272
+ function inspectDarwinIdentity(pid, options) {
273
+ const timeout = positiveBound(options.commandTimeoutMs, DEFAULT_COMMAND_TIMEOUT_MS);
274
+ const maxBuffer = positiveBound(options.commandMaxBuffer, DEFAULT_COMMAND_MAX_BUFFER);
275
+ if (!timeout || !maxBuffer) throw new InspectionFailure("METADATA_MALFORMED");
276
+ const runCommand = firstFunction(options.commandRunner, options.commandRunnerFn)
277
+ || ((command, args, commandOptions) => execFileSync(command, args, commandOptions));
278
+ const commandOptions = {
279
+ encoding: "utf8",
280
+ stdio: ["ignore", "pipe", "pipe"],
281
+ timeout,
282
+ maxBuffer,
283
+ };
284
+
285
+ let firstStart;
286
+ let command;
287
+ let lsof;
288
+ let finalStart;
289
+ try {
290
+ firstStart = commandOutput(runCommand, "ps", ["-p", String(pid), "-o", "lstart="], commandOptions, maxBuffer);
291
+ command = commandOutput(runCommand, "ps", ["-p", String(pid), "-o", "comm="], commandOptions, maxBuffer);
292
+ lsof = commandOutput(runCommand, "lsof", ["-a", "-p", String(pid), "-d", "cwd", "-Fn"], commandOptions, maxBuffer);
293
+ finalStart = commandOutput(runCommand, "ps", ["-p", String(pid), "-o", "lstart="], commandOptions, maxBuffer);
294
+ } catch (error) {
295
+ if (error instanceof InspectionFailure) throw error;
296
+ throw new InspectionFailure(isTimeoutError(error) ? "INSPECTION_TIMEOUT" : "METADATA_UNAVAILABLE");
297
+ }
298
+
299
+ const firstMarker = normalizeDarwinStart(firstStart);
300
+ const finalMarker = normalizeDarwinStart(finalStart);
301
+ const commandName = parseDarwinCommand(command);
302
+ const cwd = parseDarwinCwd(lsof, pid);
303
+ if (!firstMarker || !finalMarker || firstMarker !== finalMarker || !commandName || !cwd) {
304
+ throw new InspectionFailure("METADATA_MALFORMED");
305
+ }
306
+
307
+ return {
308
+ pid,
309
+ inspector: PROCESS_INSPECTOR,
310
+ start_marker: `darwin-ps:${firstMarker}`,
311
+ command_name: commandName,
312
+ cwd,
313
+ };
314
+ }
315
+
316
+ async function observeAfterSignal(expected, options, polling, deadline) {
317
+ const maximumPolls = Math.ceil(polling.waitForExitMs / polling.pollIntervalMs) + 1;
318
+ for (let count = 0; count < maximumPolls; count += 1) {
319
+ let now;
320
+ try {
321
+ now = polling.clock();
322
+ if (!Number.isFinite(now) || now >= deadline) break;
323
+ await polling.sleep(Math.min(polling.pollIntervalMs, deadline - now));
324
+ } catch {
325
+ return postSignalResult("indeterminate", "POST_SIGNAL_INDETERMINATE");
326
+ }
327
+
328
+ const verification = verifyProcessIdentity(expected, options);
329
+ if (verification.status === "absent") {
330
+ return { ...postSignalResult("absent", "POST_SIGNAL_ABSENT"), verification };
331
+ }
332
+ if (verification.status === "mismatched") {
333
+ return { ...postSignalResult("mismatched", "POST_SIGNAL_MISMATCH"), verification };
334
+ }
335
+ if (verification.status === "indeterminate") {
336
+ return { ...postSignalResult("indeterminate", "POST_SIGNAL_INDETERMINATE"), verification };
337
+ }
338
+ }
339
+
340
+ const verification = verifyProcessIdentity(expected, options);
341
+ if (verification.status === "absent") return { ...postSignalResult("absent", "POST_SIGNAL_ABSENT"), verification };
342
+ if (verification.status === "mismatched") return { ...postSignalResult("mismatched", "POST_SIGNAL_MISMATCH"), verification };
343
+ if (verification.status === "indeterminate") return { ...postSignalResult("indeterminate", "POST_SIGNAL_INDETERMINATE"), verification };
344
+ return { ...postSignalResult("live-and-matching", "POST_SIGNAL_LIVE"), verification };
345
+ }
346
+
347
+ function normalizePollingOptions(options) {
348
+ const waitForExitMs = options.waitForExitMs ?? 0;
349
+ const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
350
+ if (!Number.isInteger(waitForExitMs) || waitForExitMs < 0) return null;
351
+ if (!Number.isInteger(pollIntervalMs) || pollIntervalMs <= 0) return null;
352
+ const clock = firstFunction(options.clock, options.clockFn) || Date.now;
353
+ const sleep = firstFunction(options.sleep, options.sleepFn)
354
+ || ((ms) => new Promise((resolveSleep) => setTimeout(resolveSleep, ms)));
355
+ return { waitForExitMs, pollIntervalMs, clock, sleep };
356
+ }
357
+
358
+ function normalizeExpectedIdentity(expected) {
359
+ if (!expected || typeof expected !== "object" || Array.isArray(expected)) return null;
360
+ const nested = expected.identity && typeof expected.identity === "object" && !Array.isArray(expected.identity)
361
+ ? expected.identity
362
+ : expected;
363
+ const pid = expected.pid ?? nested.pid;
364
+ const inspector = nested.inspector;
365
+ const startMarker = nested.start_marker ?? nested.startMarker;
366
+ const commandName = normalizeCommandName(nested.command_name ?? nested.commandName);
367
+ const cwd = normalizeCwd(expected.cwd ?? nested.cwd);
368
+ if (!positivePid(pid) || !nonEmptyExactString(inspector) || !nonEmptyExactString(startMarker) || !commandName || !cwd) return null;
369
+ return { pid, inspector, start_marker: startMarker, command_name: commandName, cwd };
370
+ }
371
+
372
+ function normalizeInspectedIdentity(identity) {
373
+ if (!identity || typeof identity !== "object" || Array.isArray(identity)) return null;
374
+ const commandName = normalizeCommandName(identity.command_name ?? identity.commandName);
375
+ const cwd = normalizeCwd(identity.cwd);
376
+ const startMarker = identity.start_marker ?? identity.startMarker;
377
+ if (!positivePid(identity.pid) || !nonEmptyExactString(identity.inspector) || !nonEmptyExactString(startMarker) || !commandName || !cwd) return null;
378
+ return {
379
+ pid: identity.pid,
380
+ inspector: identity.inspector,
381
+ start_marker: startMarker,
382
+ command_name: commandName,
383
+ cwd,
384
+ };
385
+ }
386
+
387
+ function normalizeLivenessResult(value, pid) {
388
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
389
+ return result("indeterminate", "LIVENESS_RESULT_MALFORMED", pid);
390
+ }
391
+ if (value.status === "live") return result("live", "LIVENESS_LIVE", pid);
392
+ if (value.status === "absent") return result("absent", "LIVENESS_ABSENT", pid);
393
+ if (value.status === "indeterminate") return result("indeterminate", "LIVENESS_FAILED", pid);
394
+ return result("indeterminate", "LIVENESS_RESULT_MALFORMED", pid);
395
+ }
396
+
397
+ function livenessErrorResult(error, pid) {
398
+ if (error?.code === "ESRCH") return result("absent", "LIVENESS_ABSENT", pid);
399
+ if (error?.code === "EPERM" || error?.code === "EACCES") {
400
+ return result("indeterminate", "LIVENESS_PERMISSION_DENIED", pid);
401
+ }
402
+ return result("indeterminate", "LIVENESS_FAILED", pid);
403
+ }
404
+
405
+ function parseLinuxStartMarker(value, pid) {
406
+ const text = toText(value);
407
+ if (!text.startsWith(`${pid} (`)) return null;
408
+ const commandEnd = text.lastIndexOf(") ");
409
+ if (commandEnd < 0) return null;
410
+ const fields = text.slice(commandEnd + 2).trim().split(/\s+/u);
411
+ const marker = fields[19];
412
+ return /^\d+$/u.test(marker || "") ? marker : null;
413
+ }
414
+
415
+ function parseDarwinCwd(value, pid) {
416
+ const lines = strictOutputLines(value);
417
+ if (!lines || lines.length !== 3) return null;
418
+ if (lines[0] !== `p${pid}` || lines[1] !== "fcwd" || !lines[2].startsWith("n")) return null;
419
+ return normalizeCwd(lines[2].slice(1));
420
+ }
421
+
422
+ function commandOutput(runCommand, command, args, commandOptions, maxBuffer) {
423
+ const output = runCommand(command, args, commandOptions);
424
+ if (!(typeof output === "string" || Buffer.isBuffer(output))) throw new InspectionFailure("METADATA_MALFORMED");
425
+ if (Buffer.byteLength(output) > maxBuffer) throw new InspectionFailure("METADATA_UNAVAILABLE");
426
+ return toText(output);
427
+ }
428
+
429
+ function normalizeCommandName(value) {
430
+ const text = toText(value).trim();
431
+ if (!text || /[\r\n\0]/u.test(text)) return null;
432
+ const name = posix.basename(text);
433
+ return name && name !== "." && name !== ".." ? name : null;
434
+ }
435
+
436
+ function normalizeCwd(value) {
437
+ const text = toText(value);
438
+ if (!text || text.includes("\0") || !isAbsolute(text)) return null;
439
+ return resolve(text);
440
+ }
441
+
442
+ function normalizeDarwinStart(value) {
443
+ const lines = strictOutputLines(value);
444
+ if (!lines || lines.length !== 1 || !/^[ -~]+$/u.test(lines[0])) return null;
445
+ const normalized = lines[0].trim().replace(/ +/gu, " ");
446
+ const match = normalized.match(DARWIN_START_PATTERN);
447
+ if (!match) return null;
448
+
449
+ const [, weekday, monthName, dayText, hourText, minuteText, secondText, yearText] = match;
450
+ const month = DARWIN_MONTHS.indexOf(monthName);
451
+ const day = Number(dayText);
452
+ const year = Number(yearText);
453
+ if (year < 1970 || month < 0) return null;
454
+ const date = new Date(Date.UTC(year, month, day, Number(hourText), Number(minuteText), Number(secondText)));
455
+ if (
456
+ date.getUTCFullYear() !== year
457
+ || date.getUTCMonth() !== month
458
+ || date.getUTCDate() !== day
459
+ || DARWIN_WEEKDAYS[date.getUTCDay()] !== weekday
460
+ ) return null;
461
+ return normalized;
462
+ }
463
+
464
+ function parseDarwinCommand(value) {
465
+ const lines = strictOutputLines(value);
466
+ return lines && lines.length === 1 ? normalizeCommandName(lines[0]) : null;
467
+ }
468
+
469
+ function strictOutputLines(value) {
470
+ const text = toText(value);
471
+ if (!text || text.includes("\0") || /\r(?!\n)/u.test(text)) return null;
472
+ const body = text.endsWith("\r\n") ? text.slice(0, -2)
473
+ : text.endsWith("\n") ? text.slice(0, -1)
474
+ : text;
475
+ if (!body || /[\r\n]$/u.test(body)) return null;
476
+ const lines = body.split(/\r?\n/u);
477
+ return lines.some((line) => line.length === 0) ? null : lines;
478
+ }
479
+
480
+ function resolvePlatform(options) {
481
+ const provider = firstFunction(options.platformFn);
482
+ try {
483
+ return provider ? provider() : (options.platform ?? process.platform);
484
+ } catch {
485
+ return null;
486
+ }
487
+ }
488
+
489
+ function resolveHostname(options) {
490
+ const provider = firstFunction(options.hostnameFn, options.hostnameProvider);
491
+ let value;
492
+ try {
493
+ value = provider ? provider() : (options.hostname ?? systemHostname());
494
+ } catch {
495
+ throw new InspectionFailure("METADATA_UNAVAILABLE");
496
+ }
497
+ if (!nonEmptyExactString(value)) throw new InspectionFailure("METADATA_MALFORMED");
498
+ return value;
499
+ }
500
+
501
+ function positiveBound(value, fallback) {
502
+ const selected = value ?? fallback;
503
+ return Number.isInteger(selected) && selected > 0 ? selected : null;
504
+ }
505
+
506
+ function isTimeoutError(error) {
507
+ return error?.code === "ETIMEDOUT" || error?.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER" || error?.timedOut === true;
508
+ }
509
+
510
+ function positivePid(value) {
511
+ return Number.isSafeInteger(value) && value > 0;
512
+ }
513
+
514
+ function nonEmptyExactString(value) {
515
+ return typeof value === "string" && value.length > 0 && value.trim().length > 0 && !value.includes("\0");
516
+ }
517
+
518
+ function firstFunction(...values) {
519
+ return values.find((value) => typeof value === "function") || null;
520
+ }
521
+
522
+ function toText(value) {
523
+ if (typeof value === "string") return value;
524
+ if (Buffer.isBuffer(value)) return value.toString("utf8");
525
+ return "";
526
+ }
527
+
528
+ function result(status, code, pid, extra = {}) {
529
+ return { status, code: PROCESS_VERIFICATION_CODES[code], reason: REASONS[code], pid, ...extra };
530
+ }
531
+
532
+ function signalResult(status, code, pid) {
533
+ return {
534
+ ...result(status, code, pid),
535
+ signal: VERIFIED_PROCESS_SIGNAL,
536
+ limitation: PROCESS_SIGNAL_RACE_LIMITATION,
537
+ };
538
+ }
539
+
540
+ function postSignalResult(status, code) {
541
+ return { status, code: PROCESS_VERIFICATION_CODES[code], reason: REASONS[code] };
542
+ }