@cassiomc1/forgeloop 1.14.0 → 2.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. package/CONTRIBUTING.md +11 -7
  2. package/DOCS_INDEX.md +2 -1
  3. package/ENG/c-development-eng.md +2 -2
  4. package/ENG/cpp-development-eng.md +2 -2
  5. package/ENG/dotnet-aspnetcore-development-eng.md +7 -1
  6. package/ENG/flutter-development-eng.md +4 -2
  7. package/ENG/go-development-eng.md +2 -2
  8. package/ENG/java-development-eng.md +3 -3
  9. package/ENG/nodejs-backend-development-eng.md +8 -4
  10. package/ENG/php-development-eng.md +3 -3
  11. package/ENG/python-development-eng.md +205 -0
  12. package/ENG/rust-development-eng.md +42 -1
  13. package/ENG/sql-development-eng.md +4 -4
  14. package/ENG/swift-development-eng.md +5 -4
  15. package/ENG/test-code-eng.md +1 -1
  16. package/ENG/typescript-development-eng.md +4 -4
  17. package/GUIDE_ROUTER.md +6 -0
  18. package/LICENSE +133 -21
  19. package/LOOP_SYSTEM_DESIGN.md +10 -10
  20. package/README.md +28 -39
  21. package/THIRD_PARTY_NOTICES.md +3 -4
  22. package/completions/_forgeloop +11 -3
  23. package/completions/forgeloop.bash +19 -3
  24. package/completions/forgeloop.fish +61 -1
  25. package/docs/AGENT_PROTOCOL_SUMMARY.md +50 -40
  26. package/docs/ARTIFACT_REFERENCE.md +50 -37
  27. package/docs/CLI_REFERENCE.md +147 -3
  28. package/docs/DOCUMENTATION_GUIDE.md +6 -4
  29. package/docs/GETTING_STARTED.md +3 -3
  30. package/docs/PACKAGE_CONTENTS.md +14 -13
  31. package/docs/RELEASE_CHECKLIST.md +81 -10
  32. package/docs/SELF_HOSTED_TESTS.md +121 -0
  33. package/docs/documentation-manifest.json +25 -12
  34. package/package.json +15 -10
  35. package/schemas/task-bundle.schema.json +7 -2
  36. package/scripts/generate-agent-protocol-summary.mjs +2 -2
  37. package/scripts/run-mcp-clean-tests.mjs +30 -0
  38. package/src/adapters/agent-browser/provider.js +14 -11
  39. package/src/adapters/emulated-services/provider.js +11 -4
  40. package/src/adapters/temporary-paths.js +18 -0
  41. package/src/cli.js +146 -210
  42. package/src/commands/action-show.js +5 -2
  43. package/src/commands/advance.js +5 -1
  44. package/src/commands/baseline.js +13 -11
  45. package/src/commands/checkpoint-revalidate.js +24 -8
  46. package/src/commands/complete.js +19 -3
  47. package/src/commands/contract-create.js +3 -1
  48. package/src/commands/doctor.js +36 -13
  49. package/src/commands/gate-record.js +20 -31
  50. package/src/commands/gate-revalidate.js +11 -13
  51. package/src/commands/handoff-list.js +6 -1
  52. package/src/commands/handoff-show.js +6 -1
  53. package/src/commands/migrate-protocol.js +2 -2
  54. package/src/commands/model-route.js +9 -1
  55. package/src/commands/next.js +7 -1
  56. package/src/commands/policy-diff.js +25 -4
  57. package/src/commands/progress.js +17 -11
  58. package/src/commands/record-diagnosis.js +3 -0
  59. package/src/commands/semantic-plan.js +26 -2
  60. package/src/commands/status.js +6 -1
  61. package/src/commands/storage-backup.js +15 -0
  62. package/src/commands/storage-migrate.js +5 -0
  63. package/src/commands/storage-migration-resume.js +5 -0
  64. package/src/commands/storage-migration-status.js +44 -0
  65. package/src/commands/storage-restore-resume.js +7 -0
  66. package/src/commands/storage-restore.js +8 -0
  67. package/src/commands/task-abandon.js +1 -1
  68. package/src/commands/task-list.js +8 -43
  69. package/src/commands/task-migrate.js +3 -3
  70. package/src/commands/task-recover.js +10 -2
  71. package/src/commands/task-scope.js +18 -2
  72. package/src/commands/task-show.js +16 -2
  73. package/src/commands/validate-protocol.js +29 -14
  74. package/src/commands/validate-receipt.js +8 -1
  75. package/src/config/guides.json +4 -0
  76. package/src/core/action-ledger-projection.js +10 -3
  77. package/src/core/actions.js +38 -75
  78. package/src/core/activation.js +11 -3
  79. package/src/core/approvals.js +17 -51
  80. package/src/core/artifact-registry.js +59 -2
  81. package/src/core/artifacts.js +81 -27
  82. package/src/core/attachment-paths.js +45 -0
  83. package/src/core/attestation-coverage.js +6 -1
  84. package/src/core/attestation-verifier.js +21 -2
  85. package/src/core/attestation.js +17 -6
  86. package/src/core/audit.js +6 -1
  87. package/src/core/bundles.js +160 -66
  88. package/src/core/checkpoint-revalidation.js +12 -10
  89. package/src/core/cli-command-definitions.js +109 -7
  90. package/src/core/code-manifest.js +9 -3
  91. package/src/core/command-executors.js +182 -126
  92. package/src/core/command-input.js +21 -1
  93. package/src/core/completion-artifacts.js +71 -29
  94. package/src/core/completion-ownership.js +19 -8
  95. package/src/core/completion-recovery-rebind.js +11 -1
  96. package/src/core/completion.js +60 -9
  97. package/src/core/conformance.js +2 -1
  98. package/src/core/continuity-lint.js +13 -2
  99. package/src/core/continuity-reconciliation.js +16 -3
  100. package/src/core/continuity.js +16 -8
  101. package/src/core/contract-bootstrap-recovery.js +42 -41
  102. package/src/core/contract-revision.js +8 -9
  103. package/src/core/decision/artifact.js +2 -1
  104. package/src/core/decision/events.js +9 -6
  105. package/src/core/decision/resolver.js +19 -11
  106. package/src/core/decision/service.js +63 -38
  107. package/src/core/decision/task-bindings.js +6 -1
  108. package/src/core/diagnosis-model.js +11 -0
  109. package/src/core/diagnosis.js +10 -1
  110. package/src/core/diagnostic-projection.js +29 -12
  111. package/src/core/diagnostic-record.js +28 -3
  112. package/src/core/efficiency.js +6 -1
  113. package/src/core/events.js +316 -97
  114. package/src/core/execution-prerequisites.js +22 -13
  115. package/src/core/execution.js +26 -40
  116. package/src/core/filesystem.js +11 -0
  117. package/src/core/gate-artifact.js +105 -19
  118. package/src/core/gate-provenance.js +6 -5
  119. package/src/core/handoff.js +26 -14
  120. package/src/core/history.js +39 -35
  121. package/src/core/hypothesis-projection.js +32 -0
  122. package/src/core/information-gain-projection.js +104 -100
  123. package/src/core/inspect.js +12 -3
  124. package/src/core/integration-invocation-policy.js +10 -0
  125. package/src/core/integration-resources.js +41 -34
  126. package/src/core/ledger-event-collection.js +189 -0
  127. package/src/core/ledger-relations.js +23 -0
  128. package/src/core/native-storage.js +12 -0
  129. package/src/core/next-action-continuity.js +31 -9
  130. package/src/core/next-action-phases.js +1 -1
  131. package/src/core/next-action-review-phase.js +2 -2
  132. package/src/core/next-action-verification-phase.js +2 -1
  133. package/src/core/next-action.js +21 -2
  134. package/src/core/phase.js +138 -93
  135. package/src/core/policy-engine.js +12 -13
  136. package/src/core/preflight-consistency.js +72 -59
  137. package/src/core/preflight-loaders.js +19 -12
  138. package/src/core/preflight-model.js +6 -1
  139. package/src/core/preflight.js +29 -12
  140. package/src/core/progress.js +21 -10
  141. package/src/core/protocol-info.js +3 -0
  142. package/src/core/protocol-migration.js +7 -6
  143. package/src/core/reconcile-closure.js +162 -24
  144. package/src/core/recovery-history.js +66 -47
  145. package/src/core/reflection.js +72 -77
  146. package/src/core/responsibility.js +25 -13
  147. package/src/core/route-artifact.js +13 -3
  148. package/src/core/runtime-context.js +5 -0
  149. package/src/core/schema-validation.js +8 -3
  150. package/src/core/settlement.js +12 -1
  151. package/src/core/signing/bundle-file.js +53 -0
  152. package/src/core/signing/bundle-reference.js +15 -0
  153. package/src/core/signing/sigstore.js +60 -33
  154. package/src/core/signing/statement-file.js +27 -0
  155. package/src/core/storage-runtime-registry.js +41 -0
  156. package/src/core/structural-quality/artifacts.js +20 -14
  157. package/src/core/structural-quality/service.js +81 -56
  158. package/src/core/target-layout.js +1 -1
  159. package/src/core/task-claim-state.js +52 -18
  160. package/src/core/task-command.js +21 -1
  161. package/src/core/task-conflict-inspection.js +112 -107
  162. package/src/core/task-descriptor.js +29 -22
  163. package/src/core/task-discovery.js +288 -90
  164. package/src/core/task-lock.js +66 -306
  165. package/src/core/task-migration-validation.js +5 -5
  166. package/src/core/task-migration.js +17 -287
  167. package/src/core/task-snapshot.js +51 -8
  168. package/src/core/task-storage-migration.js +18 -0
  169. package/src/core/templates.js +20 -8
  170. package/src/core/test-intelligence/prune.js +5 -1
  171. package/src/core/trace.js +154 -99
  172. package/src/core/trajectory-evaluation.js +15 -3
  173. package/src/core/trajectory-metrics.js +47 -32
  174. package/src/core/transaction.js +16 -288
  175. package/src/core/verification-scope.js +20 -12
  176. package/src/core/work-state.js +100 -59
  177. package/src/repository-index/binary-manager.js +7 -5
  178. package/src/storage/artifact-bytes.js +22 -0
  179. package/src/storage/attachment-binding.js +11 -0
  180. package/src/storage/attachment-files.js +82 -0
  181. package/src/storage/attachment-references.js +36 -0
  182. package/src/storage/backup.js +53 -0
  183. package/src/storage/capabilities.js +31 -0
  184. package/src/storage/connection-owner.js +119 -0
  185. package/src/storage/connection.js +376 -0
  186. package/src/storage/existing-project-scope.js +45 -0
  187. package/src/storage/export-destination.js +44 -0
  188. package/src/storage/exporter.js +284 -0
  189. package/src/storage/file-durability.js +46 -0
  190. package/src/storage/importer.js +461 -0
  191. package/src/storage/index.js +74 -0
  192. package/src/storage/leases.js +63 -0
  193. package/src/storage/ledger-event-snapshot.js +150 -0
  194. package/src/storage/ledger-relations.js +220 -0
  195. package/src/storage/legacy-maintenance-boundary.js +18 -0
  196. package/src/storage/legacy-signature.js +115 -0
  197. package/src/storage/maintenance-handoff.js +129 -0
  198. package/src/storage/maintenance-owner.js +42 -0
  199. package/src/storage/maintenance.js +310 -0
  200. package/src/storage/metadata-json.js +35 -0
  201. package/src/storage/migration-activate.js +59 -0
  202. package/src/storage/migration-archive.js +57 -0
  203. package/src/storage/migration-attachment-root.js +61 -0
  204. package/src/storage/migration-candidate.js +398 -0
  205. package/src/storage/migration-publication.js +143 -0
  206. package/src/storage/migration-rollback-admission.js +54 -0
  207. package/src/storage/migration-rollback-preparation.js +132 -0
  208. package/src/storage/migration-rollback-publication.js +183 -0
  209. package/src/storage/migration-rollback-release.js +66 -0
  210. package/src/storage/migration-rollback-source-stage.js +103 -0
  211. package/src/storage/migration-rollback.js +43 -0
  212. package/src/storage/migration-source-partition.js +56 -0
  213. package/src/storage/migration-source.js +255 -0
  214. package/src/storage/migration-terminal.js +39 -0
  215. package/src/storage/migration-validation.js +75 -0
  216. package/src/storage/migration.js +43 -0
  217. package/src/storage/operational-attachments.js +49 -0
  218. package/src/storage/operational-context.js +50 -0
  219. package/src/storage/project-backup.js +98 -0
  220. package/src/storage/project-bootstrap.js +24 -0
  221. package/src/storage/project-boundary.js +79 -0
  222. package/src/storage/project-read-snapshot.js +18 -0
  223. package/src/storage/project-replacement.js +137 -0
  224. package/src/storage/project-restore.js +203 -0
  225. package/src/storage/record-definitions.js +45 -0
  226. package/src/storage/repository.js +507 -0
  227. package/src/storage/restore-journal.js +62 -0
  228. package/src/storage/restore-layout.js +30 -0
  229. package/src/storage/restore-preparation.js +84 -0
  230. package/src/storage/restore-replacement-archive.js +162 -0
  231. package/src/storage/restore-replacement-owner.js +31 -0
  232. package/src/storage/restore-replacement-preparation.js +128 -0
  233. package/src/storage/restore-replacement.js +142 -0
  234. package/src/storage/restore-terminal.js +32 -0
  235. package/src/storage/runtime.js +31 -0
  236. package/src/storage/schema.js +214 -0
  237. package/src/storage/singleton-import.js +84 -0
  238. package/src/storage/snapshot-continuity.js +16 -0
  239. package/src/storage/snapshot.js +36 -0
  240. package/src/storage/storage-marker.js +79 -0
  241. package/src/storage/task-guards.js +107 -0
  242. package/src/storage/topology.js +42 -0
  243. package/src/storage/transaction.js +179 -0
  244. package/src/storage/unit-of-work.js +663 -0
  245. package/src/storage/windows-process-incarnation.js +206 -0
  246. package/.forgeloop/forgeloop.gitignore +0 -4
  247. package/LICENSE-DOCS.md +0 -13
  248. package/src/core/transaction-maintenance.js +0 -70
@@ -0,0 +1,206 @@
1
+ import { execFile as nodeExecFile } from "node:child_process";
2
+ import path from "node:path";
3
+
4
+ export const WINDOWS_PROCESS_INCARNATION_SCHEMA_VERSION = 1;
5
+ export const WINDOWS_PROCESS_INCARNATION_KIND = "windows-process-start-time-ticks-v1";
6
+ export const WINDOWS_PROCESS_INCARNATION_DEFAULTS = Object.freeze({
7
+ timeoutMs: 5_000,
8
+ maxOutputBytes: 16 * 1024,
9
+ });
10
+
11
+ const MAX_TIMEOUT_MS = WINDOWS_PROCESS_INCARNATION_DEFAULTS.timeoutMs;
12
+ const MAX_OUTPUT_BYTES = WINDOWS_PROCESS_INCARNATION_DEFAULTS.maxOutputBytes;
13
+ const MAX_PROCESS_ID = 2_147_483_647;
14
+ const MAX_DATETIME_TICKS = 3_155_378_975_999_999_999n;
15
+ const PROCESS_STATUSES = new Set(["ALIVE", "EXITED", "NOT_FOUND"]);
16
+
17
+ function boundedPositiveInteger(value, fallback, maximum) {
18
+ return Number.isInteger(value) && value > 0 ? Math.min(value, maximum) : fallback;
19
+ }
20
+
21
+ function assertProcessId(pid) {
22
+ if (!Number.isSafeInteger(pid) || pid < 1 || pid > MAX_PROCESS_ID) {
23
+ throw new TypeError("Windows process identity requires a positive 32-bit process ID");
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Resolve only the conventional absolute system PowerShell path. This is a
29
+ * path constraint, not a signer or host attestation; unsupported/untrusted
30
+ * host configuration must remain unavailable to the caller.
31
+ */
32
+ export function resolveWindowsPowerShellPath({ platform = process.platform, systemRoot = process.env.SystemRoot } = {}) {
33
+ if (platform !== "win32" || typeof systemRoot !== "string" || systemRoot.length === 0 || systemRoot.includes("\0")) return null;
34
+ // A SystemRoot value must name a fully qualified local drive path. Windows
35
+ // also treats `\\Windows` as absolute, but that form is current-drive
36
+ // relative and must not select an arbitrary host path. Device prefixes,
37
+ // UNC paths, and drive-relative values are rejected by this check.
38
+ if (!/^[A-Za-z]:[\\/]/u.test(systemRoot)) return null;
39
+ if (/(^|[\\/])\.\.([\\/]|$)/u.test(systemRoot)) return null;
40
+ const normalizedRoot = path.win32.normalize(systemRoot);
41
+ if (!/^[A-Za-z]:\\/u.test(normalizedRoot) || !path.win32.isAbsolute(normalizedRoot) || normalizedRoot.startsWith("\\\\")) return null;
42
+ return path.win32.join(normalizedRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
43
+ }
44
+
45
+ /**
46
+ * The script has one authority-bearing operation: StartTime, read while the
47
+ * Process object retains its kernel handle. Names and command lines are never
48
+ * queried. The PID is validated before it is embedded as a numeric literal.
49
+ */
50
+ export function buildWindowsProcessIncarnationScript(pid) {
51
+ assertProcessId(pid);
52
+ return [
53
+ "$ErrorActionPreference = 'Stop'",
54
+ "$WarningPreference = 'Stop'",
55
+ "$InformationPreference = 'SilentlyContinue'",
56
+ "$ProgressPreference = 'SilentlyContinue'",
57
+ "$VerbosePreference = 'SilentlyContinue'",
58
+ "$DebugPreference = 'SilentlyContinue'",
59
+ `$targetPid = ${pid}`,
60
+ "$result = $null",
61
+ "try {",
62
+ " $candidate = $null",
63
+ " try { $candidate = [System.Diagnostics.Process]::GetProcessById($targetPid) } catch {",
64
+ " $lookupException = $_.Exception",
65
+ " while ($lookupException -is [System.Management.Automation.MethodInvocationException] -and $null -ne $lookupException.InnerException) { $lookupException = $lookupException.InnerException }",
66
+ " if ($lookupException -is [System.ArgumentException]) { $result = [ordered]@{ schemaVersion = 1; pid = $targetPid; status = 'NOT_FOUND' } } else { $result = [ordered]@{ schemaVersion = 1; pid = $targetPid; status = 'UNAVAILABLE' } }",
67
+ " }",
68
+ " if ($null -ne $candidate) {",
69
+ " try {",
70
+ " $retainedHandle = $candidate.Handle",
71
+ " if ($retainedHandle -eq [IntPtr]::Zero) { throw [System.InvalidOperationException]::new('Process handle unavailable') }",
72
+ " $startTimeTicks = $candidate.StartTime.ToUniversalTime().Ticks.ToString('D', [Globalization.CultureInfo]::InvariantCulture)",
73
+ " $hasExited = $candidate.HasExited",
74
+ " $status = 'ALIVE'",
75
+ " if ($hasExited) { $status = 'EXITED' }",
76
+ " $result = [ordered]@{ schemaVersion = 1; pid = $targetPid; status = $status; hasExited = $hasExited; startTimeTicks = $startTimeTicks }",
77
+ " } catch {",
78
+ " $result = [ordered]@{ schemaVersion = 1; pid = $targetPid; status = 'UNAVAILABLE' }",
79
+ " } finally {",
80
+ " $candidate.Dispose()",
81
+ " }",
82
+ " }",
83
+ "} catch { $result = [ordered]@{ schemaVersion = 1; pid = $targetPid; status = 'UNAVAILABLE' } }",
84
+ "[Console]::Out.Write(($result | ConvertTo-Json -Compress -Depth 2))",
85
+ ].join("\n");
86
+ }
87
+
88
+ function encodeWindowsProcessIncarnationScript(pid) {
89
+ return Buffer.from(buildWindowsProcessIncarnationScript(pid), "utf16le").toString("base64");
90
+ }
91
+
92
+ function windowsProcessIncarnationArgs(pid) {
93
+ return [
94
+ "-NoLogo",
95
+ "-NoProfile",
96
+ "-NonInteractive",
97
+ "-EncodedCommand",
98
+ encodeWindowsProcessIncarnationScript(pid),
99
+ ];
100
+ }
101
+
102
+ function exactKeys(value, keys) {
103
+ const actual = Object.keys(value).sort();
104
+ const expected = [...keys].sort();
105
+ return actual.length === expected.length && actual.every((key, index) => key === expected[index]);
106
+ }
107
+
108
+ function validTicks(value) {
109
+ if (typeof value !== "string" || !/^[1-9]\d{0,18}$/u.test(value)) return false;
110
+ try { return BigInt(value) <= MAX_DATETIME_TICKS; } catch { return false; }
111
+ }
112
+
113
+ /** Validate the persisted owner token without accepting observation-only fields. */
114
+ export function isWindowsProcessIncarnationToken(value, { pid = value?.pid } = {}) {
115
+ try { assertProcessId(pid); } catch { return false; }
116
+ return !!value
117
+ && exactKeys(value, ["kind", "pid", "schemaVersion", "startTimeTicks"])
118
+ && value.kind === WINDOWS_PROCESS_INCARNATION_KIND
119
+ && value.schemaVersion === WINDOWS_PROCESS_INCARNATION_SCHEMA_VERSION
120
+ && value.pid === pid
121
+ && validTicks(value.startTimeTicks);
122
+ }
123
+
124
+ /** Parse only the small, authority-bearing JSON envelope emitted by the fixed script. */
125
+ export function parseWindowsProcessIncarnationOutput(stdout, { pid, maxOutputBytes = MAX_OUTPUT_BYTES } = {}) {
126
+ try { assertProcessId(pid); } catch { return null; }
127
+ const outputLimit = boundedPositiveInteger(maxOutputBytes, MAX_OUTPUT_BYTES, MAX_OUTPUT_BYTES);
128
+ if (typeof stdout !== "string" || Buffer.byteLength(stdout, "utf8") > outputLimit) return null;
129
+ const text = stdout.trim();
130
+ if (text.length === 0) return null;
131
+ let value;
132
+ try { value = JSON.parse(text); } catch { return null; }
133
+ if (!value || typeof value !== "object" || Array.isArray(value)
134
+ || value.schemaVersion !== WINDOWS_PROCESS_INCARNATION_SCHEMA_VERSION
135
+ || value.pid !== pid || typeof value.status !== "string" || !PROCESS_STATUSES.has(value.status)) return null;
136
+ if (value.status === "NOT_FOUND") {
137
+ return exactKeys(value, ["pid", "schemaVersion", "status"]) ? Object.freeze({
138
+ schemaVersion: value.schemaVersion,
139
+ pid: value.pid,
140
+ status: value.status,
141
+ }) : null;
142
+ }
143
+ if (!exactKeys(value, ["hasExited", "pid", "schemaVersion", "startTimeTicks", "status"])
144
+ || typeof value.hasExited !== "boolean"
145
+ || value.hasExited !== (value.status === "EXITED")
146
+ || !validTicks(value.startTimeTicks)) return null;
147
+ return Object.freeze({
148
+ schemaVersion: value.schemaVersion,
149
+ pid: value.pid,
150
+ status: value.status,
151
+ hasExited: value.hasExited,
152
+ startTimeTicks: value.startTimeTicks,
153
+ });
154
+ }
155
+
156
+ function invokeExecFile(execFileImpl, file, args, options, timeoutMs) {
157
+ return new Promise((resolve) => {
158
+ let child;
159
+ let timedOut = false;
160
+ const finish = (result) => {
161
+ clearTimeout(timer);
162
+ resolve(timedOut
163
+ ? { error: Object.assign(new Error("Windows process identity query timed out"), { code: "ETIMEDOUT" }), stdout: "", stderr: "" }
164
+ : result);
165
+ };
166
+ const timer = setTimeout(() => {
167
+ timedOut = true;
168
+ try { child?.kill?.(); } catch { /* process may already be gone */ }
169
+ // The child_process implementation invokes the callback after the
170
+ // killed child closes. Do not resolve before that close is observed.
171
+ }, timeoutMs);
172
+ try {
173
+ child = execFileImpl(file, args, options, (error, stdout, stderr) => finish({ error, stdout: stdout ?? "", stderr: stderr ?? "" }));
174
+ } catch (error) {
175
+ finish({ error, stdout: "", stderr: "" });
176
+ }
177
+ });
178
+ }
179
+
180
+ /**
181
+ * Return a kernel-backed Windows process observation, or null when the host
182
+ * cannot provide one. This helper is intentionally not wired into admission;
183
+ * callers must keep legacy schema-v1 ESRCH handling separate.
184
+ */
185
+ export async function readWindowsProcessIncarnation(pid, {
186
+ platform = process.platform,
187
+ systemRoot = process.env.SystemRoot,
188
+ execFileImpl = nodeExecFile,
189
+ timeoutMs = WINDOWS_PROCESS_INCARNATION_DEFAULTS.timeoutMs,
190
+ maxOutputBytes = WINDOWS_PROCESS_INCARNATION_DEFAULTS.maxOutputBytes,
191
+ } = {}) {
192
+ try { assertProcessId(pid); } catch { return null; }
193
+ const executable = resolveWindowsPowerShellPath({ platform, systemRoot });
194
+ if (!executable) return null;
195
+ const boundedTimeoutMs = boundedPositiveInteger(timeoutMs, WINDOWS_PROCESS_INCARNATION_DEFAULTS.timeoutMs, MAX_TIMEOUT_MS);
196
+ const boundedOutputBytes = boundedPositiveInteger(maxOutputBytes, WINDOWS_PROCESS_INCARNATION_DEFAULTS.maxOutputBytes, MAX_OUTPUT_BYTES);
197
+ const result = await invokeExecFile(execFileImpl, executable, windowsProcessIncarnationArgs(pid), {
198
+ shell: false,
199
+ windowsHide: true,
200
+ timeout: boundedTimeoutMs,
201
+ maxBuffer: boundedOutputBytes,
202
+ encoding: "utf8",
203
+ }, boundedTimeoutMs);
204
+ if (result.error || typeof result.stderr !== "string" || result.stderr.length !== 0) return null;
205
+ return parseWindowsProcessIncarnationOutput(result.stdout, { pid, maxOutputBytes: boundedOutputBytes });
206
+ }
@@ -1,4 +0,0 @@
1
- # Local resumable task state is untrusted, target-specific data.
2
- work-state.json
3
- executions/
4
- repository-index/
package/LICENSE-DOCS.md DELETED
@@ -1,13 +0,0 @@
1
- # Documentation license
2
-
3
- Original documentation in this repository is offered under the Creative
4
- Commons Attribution 4.0 International license (CC BY 4.0):
5
-
6
- <https://creativecommons.org/licenses/by/4.0/>
7
-
8
- That license permits sharing, adaptation, and commercial use when appropriate
9
- credit, a link to the license, and an indication of changes are provided.
10
-
11
- This notice applies only to documentation for which the repository owner has
12
- the necessary rights. Adapted or externally sourced material remains subject
13
- to the terms and attribution recorded in `THIRD_PARTY_NOTICES.md`.
@@ -1,70 +0,0 @@
1
- import { lstat, readdir, readFile, rm } from "node:fs/promises";
2
- import path from "node:path";
3
- import { assertSafePath, ensureWithin, fileExists, writeFileAtomic } from "./filesystem.js";
4
- import { withTaskLock } from "./task-lock.js";
5
-
6
- const ROOT = ".forgeloop/.txn";
7
- const TERMINAL = new Set(["COMMITTED", "ROLLED_BACK", "ABORTED"]);
8
- const DAY_MS = 86_400_000;
9
-
10
- async function payloadBytes(directory) {
11
- let bytes = 0;
12
- for (const entry of await readdir(directory, { withFileTypes: true })) {
13
- const child = path.join(directory, entry.name);
14
- const metadata = await lstat(child);
15
- if (metadata.isSymbolicLink()) throw new Error("Transaction payload contains a symlink");
16
- if (metadata.isDirectory()) bytes += await payloadBytes(child);
17
- else if (metadata.isFile()) bytes += metadata.size;
18
- else throw new Error("Transaction payload contains a special file");
19
- }
20
- return bytes;
21
- }
22
-
23
- /** Compact terminal payloads only; preserve manifests and all event ledgers. */
24
- export async function compactTransactions({ target, retainDays = 7, apply = false, now = Date.now() } = {}) {
25
- if (!Number.isFinite(retainDays) || retainDays < 1) throw new Error("retainDays must be at least one day");
26
- await assertSafePath(target, ROOT);
27
- const root = ensureWithin(target, ROOT);
28
- const report = { apply, scanned: 0, eligible: 0, bytes: 0, compacted: 0, skipped: [] };
29
- if (!await fileExists(root)) return report;
30
- for (const entry of await readdir(root, { withFileTypes: true })) {
31
- if (!entry.isDirectory()) continue;
32
- report.scanned += 1;
33
- const relative = `${ROOT}/${entry.name}`;
34
- const manifestPath = `${relative}/manifest.json`;
35
- try {
36
- await assertSafePath(target, manifestPath);
37
- const original = JSON.parse(await readFile(ensureWithin(target, manifestPath), "utf8"));
38
- const lockTaskId = original.lockTaskId ?? original.taskId;
39
- if (typeof lockTaskId !== "string" || !lockTaskId) throw new Error("Missing lock identity");
40
- await withTaskLock(target, lockTaskId, "compact-transactions", async () => {
41
- const manifest = JSON.parse(await readFile(ensureWithin(target, manifestPath), "utf8"));
42
- if ((manifest.lockTaskId ?? manifest.taskId) !== lockTaskId || manifest.transactionId !== entry.name) throw new Error("Changed transaction identity");
43
- const timestamp = Date.parse(manifest.committedAt ?? manifest.recoveredAt ?? manifest.failedAt ?? manifest.startedAt);
44
- if (!TERMINAL.has(manifest.status) || !Number.isFinite(timestamp) || now - timestamp < retainDays * DAY_MS) return;
45
- const payloads = [];
46
- let bytes = 0;
47
- for (const name of ["stage", "backup"]) {
48
- const payload = `${relative}/${name}`;
49
- await assertSafePath(target, payload);
50
- const fullPath = ensureWithin(target, payload);
51
- if (await fileExists(fullPath)) {
52
- bytes += await payloadBytes(fullPath);
53
- payloads.push(fullPath);
54
- }
55
- }
56
- if (!payloads.length) return;
57
- report.eligible += 1;
58
- report.bytes += bytes;
59
- if (!apply) return;
60
- for (const payload of payloads) await rm(payload, { recursive: true, force: true, maxRetries: 3 });
61
- manifest.compactedAt = new Date(now).toISOString();
62
- await writeFileAtomic(ensureWithin(target, manifestPath), `${JSON.stringify(manifest, null, 2)}\n`);
63
- report.compacted += 1;
64
- });
65
- } catch (error) {
66
- report.skipped.push({ transactionId: entry.name, reason: error.message });
67
- }
68
- }
69
- return report;
70
- }