@caupulican/pi-adaptative 0.81.41 → 0.81.42

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 (237) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/bundled-resources/runtimes/pi-shell-engine/commands/text.py +5 -1
  3. package/dist/bundled-resources/runtimes/pi-shell-engine/context.py +3 -3
  4. package/dist/bundled-resources/runtimes/pi-shell-engine/errors.py +8 -0
  5. package/dist/bundled-resources/runtimes/pi-shell-engine/exec.py +26 -3
  6. package/dist/bundled-resources/runtimes/pi-shell-engine/expand.py +3 -0
  7. package/dist/bundled-resources/runtimes/pi-shell-engine/main.py +4 -1
  8. package/dist/bundled-resources/runtimes/pi-shell-engine/state.py +2 -1
  9. package/dist/bundled-resources/runtimes/pi-shell-engine/tokens.py +2 -0
  10. package/dist/core/agent-paths.d.ts +32 -0
  11. package/dist/core/agent-paths.d.ts.map +1 -1
  12. package/dist/core/agent-paths.js +65 -0
  13. package/dist/core/agent-paths.js.map +1 -1
  14. package/dist/core/agent-session-contracts.d.ts +19 -1
  15. package/dist/core/agent-session-contracts.d.ts.map +1 -1
  16. package/dist/core/agent-session-contracts.js.map +1 -1
  17. package/dist/core/agent-session.d.ts.map +1 -1
  18. package/dist/core/agent-session.js +4 -9
  19. package/dist/core/agent-session.js.map +1 -1
  20. package/dist/core/autonomy/contracts.d.ts +2 -0
  21. package/dist/core/autonomy/contracts.d.ts.map +1 -1
  22. package/dist/core/autonomy/contracts.js.map +1 -1
  23. package/dist/core/autonomy/envelope-enforcement.d.ts.map +1 -1
  24. package/dist/core/autonomy/envelope-enforcement.js +4 -9
  25. package/dist/core/autonomy/envelope-enforcement.js.map +1 -1
  26. package/dist/core/autonomy/lane-tool-surface.d.ts +3 -1
  27. package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
  28. package/dist/core/autonomy/lane-tool-surface.js +17 -4
  29. package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
  30. package/dist/core/autonomy/path-scope.d.ts +14 -0
  31. package/dist/core/autonomy/path-scope.d.ts.map +1 -1
  32. package/dist/core/autonomy/path-scope.js +37 -6
  33. package/dist/core/autonomy/path-scope.js.map +1 -1
  34. package/dist/core/background-lane-controller.d.ts +27 -1
  35. package/dist/core/background-lane-controller.d.ts.map +1 -1
  36. package/dist/core/background-lane-controller.js +32 -1
  37. package/dist/core/background-lane-controller.js.map +1 -1
  38. package/dist/core/delegation/managed-lane-controller.d.ts +11 -1
  39. package/dist/core/delegation/managed-lane-controller.d.ts.map +1 -1
  40. package/dist/core/delegation/managed-lane-controller.js +84 -41
  41. package/dist/core/delegation/managed-lane-controller.js.map +1 -1
  42. package/dist/core/delegation/session-worker-claim.d.ts.map +1 -1
  43. package/dist/core/delegation/session-worker-claim.js +4 -3
  44. package/dist/core/delegation/session-worker-claim.js.map +1 -1
  45. package/dist/core/delegation/worker-action-journal.d.ts +73 -0
  46. package/dist/core/delegation/worker-action-journal.d.ts.map +1 -0
  47. package/dist/core/delegation/worker-action-journal.js +297 -0
  48. package/dist/core/delegation/worker-action-journal.js.map +1 -0
  49. package/dist/core/delegation/worker-actions.d.ts +36 -1
  50. package/dist/core/delegation/worker-actions.d.ts.map +1 -1
  51. package/dist/core/delegation/worker-actions.js +171 -46
  52. package/dist/core/delegation/worker-actions.js.map +1 -1
  53. package/dist/core/delegation/worker-agent-control-coordinator.d.ts +75 -0
  54. package/dist/core/delegation/worker-agent-control-coordinator.d.ts.map +1 -0
  55. package/dist/core/delegation/worker-agent-control-coordinator.js +218 -0
  56. package/dist/core/delegation/worker-agent-control-coordinator.js.map +1 -0
  57. package/dist/core/delegation/worker-agent-control.d.ts +66 -0
  58. package/dist/core/delegation/worker-agent-control.d.ts.map +1 -0
  59. package/dist/core/delegation/worker-agent-control.js +163 -0
  60. package/dist/core/delegation/worker-agent-control.js.map +1 -0
  61. package/dist/core/delegation/worker-attempt-executor.d.ts +80 -0
  62. package/dist/core/delegation/worker-attempt-executor.d.ts.map +1 -0
  63. package/dist/core/delegation/worker-attempt-executor.js +343 -0
  64. package/dist/core/delegation/worker-attempt-executor.js.map +1 -0
  65. package/dist/core/delegation/worker-claim.d.ts +30 -0
  66. package/dist/core/delegation/worker-claim.d.ts.map +1 -1
  67. package/dist/core/delegation/worker-claim.js +205 -53
  68. package/dist/core/delegation/worker-claim.js.map +1 -1
  69. package/dist/core/delegation/worker-conversation-store.d.ts +116 -0
  70. package/dist/core/delegation/worker-conversation-store.d.ts.map +1 -0
  71. package/dist/core/delegation/worker-conversation-store.js +442 -0
  72. package/dist/core/delegation/worker-conversation-store.js.map +1 -0
  73. package/dist/core/delegation/worker-delegation-controller.d.ts +21 -5
  74. package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
  75. package/dist/core/delegation/worker-delegation-controller.js +386 -251
  76. package/dist/core/delegation/worker-delegation-controller.js.map +1 -1
  77. package/dist/core/delegation/worker-delegation-request.d.ts +14 -0
  78. package/dist/core/delegation/worker-delegation-request.d.ts.map +1 -1
  79. package/dist/core/delegation/worker-delegation-request.js.map +1 -1
  80. package/dist/core/delegation/worker-dispatch-scheduler.d.ts +4 -2
  81. package/dist/core/delegation/worker-dispatch-scheduler.d.ts.map +1 -1
  82. package/dist/core/delegation/worker-dispatch-scheduler.js +16 -6
  83. package/dist/core/delegation/worker-dispatch-scheduler.js.map +1 -1
  84. package/dist/core/delegation/worker-execution-policy.d.ts +2 -1
  85. package/dist/core/delegation/worker-execution-policy.d.ts.map +1 -1
  86. package/dist/core/delegation/worker-execution-policy.js +5 -7
  87. package/dist/core/delegation/worker-execution-policy.js.map +1 -1
  88. package/dist/core/delegation/worker-lifecycle.d.ts +44 -1
  89. package/dist/core/delegation/worker-lifecycle.d.ts.map +1 -1
  90. package/dist/core/delegation/worker-lifecycle.js +120 -0
  91. package/dist/core/delegation/worker-lifecycle.js.map +1 -1
  92. package/dist/core/delegation/worker-process-owner.d.ts +18 -0
  93. package/dist/core/delegation/worker-process-owner.d.ts.map +1 -0
  94. package/dist/core/delegation/worker-process-owner.js +54 -0
  95. package/dist/core/delegation/worker-process-owner.js.map +1 -0
  96. package/dist/core/delegation/worker-profile-resolver.d.ts +5 -1
  97. package/dist/core/delegation/worker-profile-resolver.d.ts.map +1 -1
  98. package/dist/core/delegation/worker-profile-resolver.js +8 -0
  99. package/dist/core/delegation/worker-profile-resolver.js.map +1 -1
  100. package/dist/core/delegation/worker-recovery-coordinator.d.ts +54 -0
  101. package/dist/core/delegation/worker-recovery-coordinator.d.ts.map +1 -0
  102. package/dist/core/delegation/worker-recovery-coordinator.js +181 -0
  103. package/dist/core/delegation/worker-recovery-coordinator.js.map +1 -0
  104. package/dist/core/delegation/worker-resource-catalog.d.ts +32 -0
  105. package/dist/core/delegation/worker-resource-catalog.d.ts.map +1 -0
  106. package/dist/core/delegation/worker-resource-catalog.js +116 -0
  107. package/dist/core/delegation/worker-resource-catalog.js.map +1 -0
  108. package/dist/core/delegation/worker-resource-materializer.d.ts +47 -0
  109. package/dist/core/delegation/worker-resource-materializer.d.ts.map +1 -0
  110. package/dist/core/delegation/worker-resource-materializer.js +137 -0
  111. package/dist/core/delegation/worker-resource-materializer.js.map +1 -0
  112. package/dist/core/delegation/worker-runner.d.ts +3 -1
  113. package/dist/core/delegation/worker-runner.d.ts.map +1 -1
  114. package/dist/core/delegation/worker-runner.js +189 -54
  115. package/dist/core/delegation/worker-runner.js.map +1 -1
  116. package/dist/core/delegation/worker-terminal-finalizer.d.ts +23 -0
  117. package/dist/core/delegation/worker-terminal-finalizer.d.ts.map +1 -0
  118. package/dist/core/delegation/worker-terminal-finalizer.js +12 -0
  119. package/dist/core/delegation/worker-terminal-finalizer.js.map +1 -0
  120. package/dist/core/delegation/worker-write-reservation-coordinator.d.ts +38 -0
  121. package/dist/core/delegation/worker-write-reservation-coordinator.d.ts.map +1 -0
  122. package/dist/core/delegation/worker-write-reservation-coordinator.js +107 -0
  123. package/dist/core/delegation/worker-write-reservation-coordinator.js.map +1 -0
  124. package/dist/core/delegation/worker-write-reservation.d.ts +90 -0
  125. package/dist/core/delegation/worker-write-reservation.d.ts.map +1 -0
  126. package/dist/core/delegation/worker-write-reservation.js +348 -0
  127. package/dist/core/delegation/worker-write-reservation.js.map +1 -0
  128. package/dist/core/models/model-endpoint.d.ts +3 -0
  129. package/dist/core/models/model-endpoint.d.ts.map +1 -0
  130. package/dist/core/models/model-endpoint.js +11 -0
  131. package/dist/core/models/model-endpoint.js.map +1 -0
  132. package/dist/core/orchestration/agent-resume.d.ts +2 -0
  133. package/dist/core/orchestration/agent-resume.d.ts.map +1 -1
  134. package/dist/core/orchestration/agent-resume.js +7 -0
  135. package/dist/core/orchestration/agent-resume.js.map +1 -1
  136. package/dist/core/orchestration/attempt-usage.d.ts +28 -0
  137. package/dist/core/orchestration/attempt-usage.d.ts.map +1 -0
  138. package/dist/core/orchestration/attempt-usage.js +168 -0
  139. package/dist/core/orchestration/attempt-usage.js.map +1 -0
  140. package/dist/core/orchestration/capability-gateway.d.ts +19 -1
  141. package/dist/core/orchestration/capability-gateway.d.ts.map +1 -1
  142. package/dist/core/orchestration/capability-gateway.js +128 -28
  143. package/dist/core/orchestration/capability-gateway.js.map +1 -1
  144. package/dist/core/orchestration/contracts.d.ts +35 -0
  145. package/dist/core/orchestration/contracts.d.ts.map +1 -1
  146. package/dist/core/orchestration/contracts.js +17 -0
  147. package/dist/core/orchestration/contracts.js.map +1 -1
  148. package/dist/core/orchestration/delegation-ledger.d.ts +17 -2
  149. package/dist/core/orchestration/delegation-ledger.d.ts.map +1 -1
  150. package/dist/core/orchestration/delegation-ledger.js +78 -3
  151. package/dist/core/orchestration/delegation-ledger.js.map +1 -1
  152. package/dist/core/orchestration/event-store.d.ts +1 -0
  153. package/dist/core/orchestration/event-store.d.ts.map +1 -1
  154. package/dist/core/orchestration/event-store.js +78 -38
  155. package/dist/core/orchestration/event-store.js.map +1 -1
  156. package/dist/core/orchestration/profile-registry.d.ts.map +1 -1
  157. package/dist/core/orchestration/profile-registry.js +81 -41
  158. package/dist/core/orchestration/profile-registry.js.map +1 -1
  159. package/dist/core/orchestration/profile-store.d.ts.map +1 -1
  160. package/dist/core/orchestration/profile-store.js +18 -4
  161. package/dist/core/orchestration/profile-store.js.map +1 -1
  162. package/dist/core/orchestration/risk-budget.d.ts +2 -0
  163. package/dist/core/orchestration/risk-budget.d.ts.map +1 -1
  164. package/dist/core/orchestration/risk-budget.js +26 -0
  165. package/dist/core/orchestration/risk-budget.js.map +1 -1
  166. package/dist/core/orchestration/task-runtime.d.ts +13 -1
  167. package/dist/core/orchestration/task-runtime.d.ts.map +1 -1
  168. package/dist/core/orchestration/task-runtime.js +127 -16
  169. package/dist/core/orchestration/task-runtime.js.map +1 -1
  170. package/dist/core/orchestration/worker-execution-contract.d.ts +3 -1
  171. package/dist/core/orchestration/worker-execution-contract.d.ts.map +1 -1
  172. package/dist/core/orchestration/worker-execution-contract.js +75 -23
  173. package/dist/core/orchestration/worker-execution-contract.js.map +1 -1
  174. package/dist/core/orchestration/worker-result-adapter.d.ts +9 -3
  175. package/dist/core/orchestration/worker-result-adapter.d.ts.map +1 -1
  176. package/dist/core/orchestration/worker-result-adapter.js +167 -14
  177. package/dist/core/orchestration/worker-result-adapter.js.map +1 -1
  178. package/dist/core/reflection-controller.d.ts +1 -1
  179. package/dist/core/reflection-controller.d.ts.map +1 -1
  180. package/dist/core/reflection-controller.js +90 -54
  181. package/dist/core/reflection-controller.js.map +1 -1
  182. package/dist/core/research/evidence-bundle.d.ts +20 -0
  183. package/dist/core/research/evidence-bundle.d.ts.map +1 -1
  184. package/dist/core/research/evidence-bundle.js +173 -69
  185. package/dist/core/research/evidence-bundle.js.map +1 -1
  186. package/dist/core/runtime-builder.d.ts +7 -0
  187. package/dist/core/runtime-builder.d.ts.map +1 -1
  188. package/dist/core/runtime-builder.js +15 -1
  189. package/dist/core/runtime-builder.js.map +1 -1
  190. package/dist/core/session-artifact-bundle.d.ts +35 -0
  191. package/dist/core/session-artifact-bundle.d.ts.map +1 -0
  192. package/dist/core/session-artifact-bundle.js +61 -0
  193. package/dist/core/session-artifact-bundle.js.map +1 -0
  194. package/dist/core/tools/delegate-status.d.ts +6 -1
  195. package/dist/core/tools/delegate-status.d.ts.map +1 -1
  196. package/dist/core/tools/delegate-status.js +100 -18
  197. package/dist/core/tools/delegate-status.js.map +1 -1
  198. package/dist/core/tools/delegate.d.ts +9 -1
  199. package/dist/core/tools/delegate.d.ts.map +1 -1
  200. package/dist/core/tools/delegate.js +323 -61
  201. package/dist/core/tools/delegate.js.map +1 -1
  202. package/dist/core/tools/run-process.d.ts +1 -1
  203. package/dist/core/tools/run-process.d.ts.map +1 -1
  204. package/dist/core/tools/run-process.js +69 -9
  205. package/dist/core/tools/run-process.js.map +1 -1
  206. package/dist/core/tools/shell-contract-router.d.ts.map +1 -1
  207. package/dist/core/tools/shell-contract-router.js +12 -5
  208. package/dist/core/tools/shell-contract-router.js.map +1 -1
  209. package/dist/core/util/bounded-file.d.ts +15 -0
  210. package/dist/core/util/bounded-file.d.ts.map +1 -0
  211. package/dist/core/util/bounded-file.js +120 -0
  212. package/dist/core/util/bounded-file.js.map +1 -0
  213. package/dist/core/util/bounded-jsonl.d.ts.map +1 -1
  214. package/dist/core/util/bounded-jsonl.js +119 -9
  215. package/dist/core/util/bounded-jsonl.js.map +1 -1
  216. package/dist/core/util/bounded-value.d.ts +3 -0
  217. package/dist/core/util/bounded-value.d.ts.map +1 -0
  218. package/dist/core/util/bounded-value.js +12 -0
  219. package/dist/core/util/bounded-value.js.map +1 -0
  220. package/dist/core/util/value-guards.d.ts +2 -0
  221. package/dist/core/util/value-guards.d.ts.map +1 -1
  222. package/dist/core/util/value-guards.js +5 -0
  223. package/dist/core/util/value-guards.js.map +1 -1
  224. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  225. package/dist/modes/interactive/components/session-selector.js +27 -44
  226. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  227. package/docs/tool-repair.md +3 -0
  228. package/docs/windows.md +4 -2
  229. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  230. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  231. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  232. package/examples/extensions/sandbox/package-lock.json +2 -2
  233. package/examples/extensions/sandbox/package.json +1 -1
  234. package/examples/extensions/with-deps/package-lock.json +2 -2
  235. package/examples/extensions/with-deps/package.json +1 -1
  236. package/npm-shrinkwrap.json +12 -12
  237. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"shell-contract-router.js","sourceRoot":"","sources":["../../../src/core/tools/shell-contract-router.ts"],"names":[],"mappings":"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","sourcesContent":["export type ShellContractRoute =\n\t| { kind: \"passthrough\"; command: string }\n\t| { kind: \"powershell\"; command: string; argv: readonly string[] }\n\t| { kind: \"python-engine\"; command: string }\n\t| { kind: \"unsupported\"; error: string };\n\nconst STATE_MUTATOR_BUILTINS = new Set([\"cd\", \"export\", \"unset\"]);\n\ninterface TokenizeResult {\n\tok: boolean;\n\targv?: string[];\n\terror?: string;\n}\n\nconst BLOCKED_NESTED_SHELLS = new Set([\n\t\"bash\",\n\t\"sh\",\n\t\"zsh\",\n\t\"fish\",\n\t\"cmd\",\n\t\"cmd.exe\",\n\t\"powershell\",\n\t\"powershell.exe\",\n\t\"pwsh\",\n\t\"pwsh.exe\",\n\t\"wsl\",\n\t\"wsl.exe\",\n]);\nconst UNSUPPORTED_OPERATOR_MESSAGE =\n\t\"Unsupported Bash construct on Windows. Use one simple command per call; pipelines, redirection, command substitution, variable expansion, shell chaining, and nested shells are not translated.\";\n\nfunction tokenizePortableCommand(command: string): TokenizeResult {\n\tconst argv: string[] = [];\n\tlet token = \"\";\n\tlet tokenStarted = false;\n\tlet quote: \"single\" | \"double\" | undefined;\n\tlet escaping = false;\n\n\tconst finishToken = () => {\n\t\tif (!tokenStarted) return;\n\t\targv.push(token);\n\t\ttoken = \"\";\n\t\ttokenStarted = false;\n\t};\n\n\tconst source = command.trim();\n\tfor (let index = 0; index < source.length; index++) {\n\t\tconst character = source[index];\n\t\tconst nextCharacter = source[index + 1];\n\t\tif (escaping) {\n\t\t\ttoken += character;\n\t\t\ttokenStarted = true;\n\t\t\tescaping = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (quote === \"single\") {\n\t\t\tif (character === \"'\") quote = undefined;\n\t\t\telse token += character;\n\t\t\ttokenStarted = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (quote === \"double\") {\n\t\t\tif (character === '\"') {\n\t\t\t\tquote = undefined;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (character === \"\\\\\") {\n\t\t\t\tif (nextCharacter && '\"$`\\\\'.includes(nextCharacter)) {\n\t\t\t\t\ttoken += nextCharacter;\n\t\t\t\t\ttokenStarted = true;\n\t\t\t\t\tindex++;\n\t\t\t\t} else {\n\t\t\t\t\ttoken += character;\n\t\t\t\t\ttokenStarted = true;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (character === \"$\" || character === \"`\") {\n\t\t\t\treturn { ok: false, error: UNSUPPORTED_OPERATOR_MESSAGE };\n\t\t\t}\n\t\t\ttoken += character;\n\t\t\ttokenStarted = true;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (character === \"'\") {\n\t\t\tquote = \"single\";\n\t\t\ttokenStarted = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === '\"') {\n\t\t\tquote = \"double\";\n\t\t\ttokenStarted = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"\\\\\") {\n\t\t\tif (/^[A-Za-z]:/u.test(token) || token.startsWith(\"\\\\\\\\\")) {\n\t\t\t\ttoken += character;\n\t\t\t\ttokenStarted = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (token === \"\" && nextCharacter === \"\\\\\") {\n\t\t\t\ttoken = \"\\\\\\\\\";\n\t\t\t\ttokenStarted = true;\n\t\t\t\tindex++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tescaping = true;\n\t\t\ttokenStarted = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (/\\s/u.test(character)) {\n\t\t\tfinishToken();\n\t\t\tcontinue;\n\t\t}\n\t\tif (\"|><&;\\n\\r$`(){}*?[]\".includes(character) || character === \"#\" || (character === \"~\" && token === \"\")) {\n\t\t\treturn { ok: false, error: UNSUPPORTED_OPERATOR_MESSAGE };\n\t\t}\n\t\ttoken += character;\n\t\ttokenStarted = true;\n\t}\n\n\tif (escaping) return { ok: false, error: \"Unsupported trailing escape in Bash-like command.\" };\n\tif (quote) return { ok: false, error: \"Unclosed quote in Bash-like command.\" };\n\tfinishToken();\n\treturn argv.length > 0 ? { ok: true, argv } : { ok: false, error: \"Shell command is empty.\" };\n}\n\nfunction quotePowerShell(value: string): string {\n\treturn `'${value.replaceAll(\"'\", \"''\")}'`;\n}\n\nfunction powershellArray(values: readonly string[]): string {\n\treturn `@(${values.map(quotePowerShell).join(\", \")})`;\n}\n\nfunction externalCommand(argv: readonly string[]): string {\n\tconst invocation = `& ${argv.map(quotePowerShell).join(\" \")}`;\n\treturn `${invocation}\\nif ($null -ne $LASTEXITCODE) { exit $LASTEXITCODE }\\nif (-not $?) { exit 1 }`;\n}\n\nfunction parseFlags(\n\targv: readonly string[],\n\tallowedFlags: ReadonlySet<string>,\n): { flags: Set<string>; operands: string[] } | undefined {\n\tconst flags = new Set<string>();\n\tconst operands: string[] = [];\n\tlet optionsEnded = false;\n\tfor (const value of argv) {\n\t\tif (!optionsEnded && value === \"--\") {\n\t\t\toptionsEnded = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (!optionsEnded && value.startsWith(\"-\") && value !== \"-\") {\n\t\t\tif (!allowedFlags.has(value)) return undefined;\n\t\t\tflags.add(value);\n\t\t\tcontinue;\n\t\t}\n\t\toperands.push(value);\n\t}\n\treturn { flags, operands };\n}\n\nfunction routeLs(argv: readonly string[]): string | undefined {\n\tconst parsed = parseFlags(argv.slice(1), new Set([\"-a\", \"-A\", \"-l\", \"-1\", \"-la\", \"-al\"]));\n\tif (!parsed || parsed.operands.length > 1) return undefined;\n\tconst target = parsed.operands[0] ?? \".\";\n\tconst includeHidden = [...parsed.flags].some((flag) => flag.includes(\"a\") || flag.includes(\"A\"));\n\tconst hiddenFilter = includeHidden ? \"\" : \" | Where-Object { -not $_.Name.StartsWith('.') }\";\n\treturn `[string[]]$items = @(Get-ChildItem -LiteralPath ${quotePowerShell(target)}${includeHidden ? \" -Force\" : \"\"} -ErrorAction Stop${hiddenFilter} | ForEach-Object { if ($_.PSIsContainer) { $_.Name + '/' } else { $_.Name } }); [Array]::Sort($items, [StringComparer]::Ordinal); $items`;\n}\n\nfunction routeCat(argv: readonly string[]): string | undefined {\n\tconst parsed = parseFlags(argv.slice(1), new Set([\"--\"]));\n\tif (!parsed || parsed.operands.length === 0) return undefined;\n\treturn `foreach ($path in ${powershellArray(parsed.operands)}) { [Console]::Out.Write([IO.File]::ReadAllText($path)) }`;\n}\n\nfunction routeHeadOrTail(argv: readonly string[], tail: boolean): string | undefined {\n\tlet count = 10;\n\tlet operands = argv.slice(1);\n\tif (operands[0] === \"-n\") {\n\t\tconst parsedCount = Number(operands[1]);\n\t\tif (!Number.isInteger(parsedCount) || parsedCount < 0) return undefined;\n\t\tcount = parsedCount;\n\t\toperands = operands.slice(2);\n\t}\n\tif (operands.length !== 1 || operands[0].startsWith(\"-\")) return undefined;\n\treturn `Get-Content -LiteralPath ${quotePowerShell(operands[0])} ${tail ? \"-Tail\" : \"-TotalCount\"} ${count} -ErrorAction Stop`;\n}\n\nfunction routeGrep(argv: readonly string[]): string | undefined {\n\tif (argv.length !== 3 || argv[1].startsWith(\"-\") || argv[2].startsWith(\"-\")) return undefined;\n\treturn `[string[]]$matches = @(Select-String -LiteralPath ${quotePowerShell(argv[2])} -Pattern ${quotePowerShell(argv[1])} -CaseSensitive -ErrorAction Stop | ForEach-Object { $_.Line }); if ($matches.Count -eq 0) { exit 1 }; $matches`;\n}\n\nfunction routeFind(argv: readonly string[]): string | undefined {\n\tlet searchPath = \".\";\n\tlet type: \"File\" | \"Directory\" | undefined;\n\tlet filter: string | undefined;\n\tlet index = 1;\n\tif (argv[index] && !argv[index].startsWith(\"-\")) searchPath = argv[index++];\n\twhile (index < argv.length) {\n\t\tconst option = argv[index++];\n\t\tif (option === \"-type\" && (argv[index] === \"f\" || argv[index] === \"d\")) {\n\t\t\ttype = argv[index++] === \"f\" ? \"File\" : \"Directory\";\n\t\t\tcontinue;\n\t\t}\n\t\tif (option === \"-name\" && argv[index]) {\n\t\t\tfilter = argv[index++];\n\t\t\tcontinue;\n\t\t}\n\t\treturn undefined;\n\t}\n\tconst typeFlag = type ? ` -${type}` : \"\";\n\tconst filterFlag = filter ? ` -Filter ${quotePowerShell(filter)}` : \"\";\n\treturn `[string[]]$paths = @(Get-ChildItem -LiteralPath ${quotePowerShell(searchPath)} -Recurse -Force${typeFlag}${filterFlag} -ErrorAction Stop | ForEach-Object { $_.FullName.Replace('\\\\', '/') }); [Array]::Sort($paths, [StringComparer]::Ordinal); $paths`;\n}\n\nfunction routeRemove(argv: readonly string[]): string | undefined {\n\tconst parsed = parseFlags(argv.slice(1), new Set([\"-f\", \"-r\", \"-rf\", \"-fr\"]));\n\tif (!parsed || parsed.operands.length === 0) return undefined;\n\tconst recursive = [...parsed.flags].some((flag) => flag.includes(\"r\"));\n\tconst force = [...parsed.flags].some((flag) => flag.includes(\"f\"));\n\treturn `foreach ($path in ${powershellArray(parsed.operands)}) { if (Test-Path -LiteralPath $path) { Remove-Item -LiteralPath $path -Force${recursive ? \" -Recurse\" : \"\"} -ErrorAction Stop }${force ? \"\" : ' else { throw \"rm: path not found: $path\" }'} }`;\n}\n\nfunction routeCopyOrMove(argv: readonly string[], move: boolean): string | undefined {\n\tconst parsed = parseFlags(argv.slice(1), move ? new Set<string>() : new Set([\"-r\", \"-R\"]));\n\tif (!parsed || parsed.operands.length !== 2) return undefined;\n\tconst [source, destination] = parsed.operands;\n\tconst recurse = !move && parsed.flags.size > 0 ? \" -Recurse\" : \"\";\n\tconst rejectDirectoryWithoutRecursion =\n\t\t!move && parsed.flags.size === 0\n\t\t\t? `$sourceItem = Get-Item -LiteralPath ${quotePowerShell(source)} -Force -ErrorAction Stop; if ($sourceItem.PSIsContainer) { throw 'cp: source is a directory; use -r' }; `\n\t\t\t: \"\";\n\treturn `${rejectDirectoryWithoutRecursion}${move ? \"Move-Item\" : \"Copy-Item\"} -LiteralPath ${quotePowerShell(source)} -Destination ${quotePowerShell(destination)}${recurse} -ErrorAction Stop`;\n}\n\nfunction routeMkdir(argv: readonly string[]): string | undefined {\n\tconst parsed = parseFlags(argv.slice(1), new Set([\"-p\"]));\n\tif (!parsed || parsed.operands.length === 0) return undefined;\n\treturn `${powershellArray(parsed.operands)} | ForEach-Object { New-Item -ItemType Directory -Path $_${parsed.flags.has(\"-p\") ? \" -Force\" : \"\"} -ErrorAction Stop | Out-Null }`;\n}\n\nfunction routeTouch(argv: readonly string[]): string | undefined {\n\tconst parsed = parseFlags(argv.slice(1), new Set());\n\tif (!parsed || parsed.operands.length === 0) return undefined;\n\treturn `${powershellArray(parsed.operands)} | ForEach-Object { if (Test-Path -LiteralPath $_) { (Get-Item -LiteralPath $_ -ErrorAction Stop).LastWriteTime = Get-Date } else { New-Item -ItemType File -Path $_ -ErrorAction Stop | Out-Null } }`;\n}\n\nfunction routeBuiltIn(argv: readonly string[]): string | undefined {\n\tconst command = argv[0].toLowerCase();\n\tif (command === \"pwd\" && argv.length === 1) return \"(Get-Location).Path\";\n\tif (command === \"echo\") {\n\t\tlet valueIndex = 1;\n\t\twhile (/^-n+$/u.test(argv[valueIndex] ?? \"\")) valueIndex++;\n\t\tif (/^-[neE]+$/u.test(argv[valueIndex] ?? \"\") && /[eE]/u.test(argv[valueIndex] ?? \"\")) return undefined;\n\t\tconst method = valueIndex > 1 ? \"Write\" : \"WriteLine\";\n\t\treturn `[Console]::Out.${method}((${powershellArray(argv.slice(valueIndex))} -join ' '))`;\n\t}\n\tif (command === \"true\" && argv.length === 1) return \"exit 0\";\n\tif (command === \"false\" && argv.length === 1) return \"exit 1\";\n\tif (command === \"which\" && argv.length === 2) {\n\t\treturn `(Get-Command -Name ${quotePowerShell(argv[1])} -CommandType Application -ErrorAction Stop).Source`;\n\t}\n\tif (command === \"ls\") return routeLs(argv);\n\tif (command === \"cat\") return routeCat(argv);\n\tif (command === \"head\") return routeHeadOrTail(argv, false);\n\tif (command === \"tail\") return routeHeadOrTail(argv, true);\n\tif (command === \"grep\") return routeGrep(argv);\n\tif (command === \"find\") return routeFind(argv);\n\tif (command === \"rm\") return routeRemove(argv);\n\tif (command === \"cp\") return routeCopyOrMove(argv, false);\n\tif (command === \"mv\") return routeCopyOrMove(argv, true);\n\tif (command === \"mkdir\") return routeMkdir(argv);\n\tif (command === \"touch\") return routeTouch(argv);\n\treturn undefined;\n}\n\nconst ROUTED_BUILTIN_NAMES = new Set([\n\t\"pwd\",\n\t\"echo\",\n\t\"true\",\n\t\"false\",\n\t\"which\",\n\t\"ls\",\n\t\"cat\",\n\t\"head\",\n\t\"tail\",\n\t\"grep\",\n\t\"find\",\n\t\"rm\",\n\t\"cp\",\n\t\"mv\",\n\t\"mkdir\",\n\t\"touch\",\n]);\n\nexport function routeShellContract(\n\tcommand: string,\n\tplatform: NodeJS.Platform = process.platform,\n\toptions?: { pythonEngine?: boolean },\n): ShellContractRoute {\n\tif (platform !== \"win32\") return { kind: \"passthrough\", command };\n\tconst pythonEngine = options?.pythonEngine === true;\n\tconst tokenized = tokenizePortableCommand(command);\n\n\t// Complex constructs (pipelines, redirection, expansion, quoting the tokenizer refuses, …) are\n\t// exactly the grammar the Python engine owns; with the engine off this is the original\n\t// fail-closed floor verbatim.\n\tif (!tokenized.ok || !tokenized.argv) {\n\t\tif (pythonEngine) return { kind: \"python-engine\", command };\n\t\treturn { kind: \"unsupported\", error: tokenized.error ?? UNSUPPORTED_OPERATOR_MESSAGE };\n\t}\n\n\tconst argv = tokenized.argv;\n\tconst commandName = argv[0].toLowerCase();\n\n\t// Inline env assignments (`NAME=value [cmd]`) are a state-mutating/expansion form the engine\n\t// supports (§2.1); with the engine off this is the original fail-closed floor verbatim.\n\tif (/^[A-Za-z_][A-Za-z0-9_]*=/u.test(argv[0])) {\n\t\tif (pythonEngine) return { kind: \"python-engine\", command };\n\t\treturn {\n\t\t\tkind: \"unsupported\",\n\t\t\terror: \"Inline environment assignments are not supported. Configure the environment outside the shell command.\",\n\t\t};\n\t}\n\n\t// State mutators route to the engine unconditionally when it is enabled: the engine is the\n\t// sole state owner (D4), so `cd`/`export`/`unset` never execute against the PS floor.\n\tif (pythonEngine && STATE_MUTATOR_BUILTINS.has(commandName)) {\n\t\treturn { kind: \"python-engine\", command };\n\t}\n\n\tif (BLOCKED_NESTED_SHELLS.has(commandName)) {\n\t\treturn {\n\t\t\tkind: \"unsupported\",\n\t\t\terror: \"Nested shell execution is not supported by the Windows shell contract router.\",\n\t\t};\n\t}\n\tconst builtIn = routeBuiltIn(argv);\n\tif (builtIn) return { kind: \"powershell\", command: builtIn, argv };\n\tif (ROUTED_BUILTIN_NAMES.has(commandName)) {\n\t\t// The PS floor rejected the FORM (flags/argument shape); the engine supports the fuller\n\t\t// flag set for the same builtin (§2.2).\n\t\tif (pythonEngine) return { kind: \"python-engine\", command };\n\t\treturn {\n\t\t\tkind: \"unsupported\",\n\t\t\terror: `Unsupported ${argv[0]} form on Windows. Use a simpler Bash-like form or Pi's dedicated read/search/edit tools.`,\n\t\t};\n\t}\n\tif (commandName.endsWith(\".sh\") || commandName.includes(\"/bin/\")) {\n\t\treturn {\n\t\t\tkind: \"unsupported\",\n\t\t\terror: \"POSIX shell scripts are not supported by the Windows shell contract router.\",\n\t\t};\n\t}\n\treturn { kind: \"powershell\", command: externalCommand(argv), argv };\n}\n"]}
1
+ {"version":3,"file":"shell-contract-router.js","sourceRoot":"","sources":["../../../src/core/tools/shell-contract-router.ts"],"names":[],"mappings":"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","sourcesContent":["export type ShellContractRoute =\n\t| { kind: \"passthrough\"; command: string }\n\t| { kind: \"powershell\"; command: string; argv: readonly string[] }\n\t| { kind: \"python-engine\"; command: string }\n\t| { kind: \"unsupported\"; error: string };\n\nconst ENGINE_OWNED_BUILTINS = new Set([\"cd\", \"export\", \"unset\", \"exit\"]);\n\ninterface TokenizeResult {\n\tok: boolean;\n\targv?: string[];\n\terror?: string;\n}\n\nconst BLOCKED_NESTED_SHELLS = new Set([\n\t\"bash\",\n\t\"sh\",\n\t\"zsh\",\n\t\"fish\",\n\t\"cmd\",\n\t\"cmd.exe\",\n\t\"powershell\",\n\t\"powershell.exe\",\n\t\"pwsh\",\n\t\"pwsh.exe\",\n\t\"wsl\",\n\t\"wsl.exe\",\n]);\nconst UNSUPPORTED_OPERATOR_MESSAGE =\n\t\"Unsupported Bash construct on Windows. Use one simple command per call; pipelines, redirection, command substitution, variable expansion, shell chaining, and nested shells are not translated.\";\n\nfunction tokenizePortableCommand(command: string): TokenizeResult {\n\tconst argv: string[] = [];\n\tlet token = \"\";\n\tlet tokenStarted = false;\n\tlet quote: \"single\" | \"double\" | undefined;\n\tlet escaping = false;\n\n\tconst finishToken = () => {\n\t\tif (!tokenStarted) return;\n\t\targv.push(token);\n\t\ttoken = \"\";\n\t\ttokenStarted = false;\n\t};\n\n\tconst source = command.trim();\n\tfor (let index = 0; index < source.length; index++) {\n\t\tconst character = source[index];\n\t\tconst nextCharacter = source[index + 1];\n\t\tif (escaping) {\n\t\t\ttoken += character;\n\t\t\ttokenStarted = true;\n\t\t\tescaping = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (quote === \"single\") {\n\t\t\tif (character === \"'\") quote = undefined;\n\t\t\telse token += character;\n\t\t\ttokenStarted = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (quote === \"double\") {\n\t\t\tif (character === '\"') {\n\t\t\t\tquote = undefined;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (character === \"\\\\\") {\n\t\t\t\tif (nextCharacter && '\"$`\\\\'.includes(nextCharacter)) {\n\t\t\t\t\ttoken += nextCharacter;\n\t\t\t\t\ttokenStarted = true;\n\t\t\t\t\tindex++;\n\t\t\t\t} else {\n\t\t\t\t\ttoken += character;\n\t\t\t\t\ttokenStarted = true;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (character === \"$\" || character === \"`\") {\n\t\t\t\treturn { ok: false, error: UNSUPPORTED_OPERATOR_MESSAGE };\n\t\t\t}\n\t\t\ttoken += character;\n\t\t\ttokenStarted = true;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (character === \"'\") {\n\t\t\tquote = \"single\";\n\t\t\ttokenStarted = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === '\"') {\n\t\t\tquote = \"double\";\n\t\t\ttokenStarted = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (character === \"\\\\\") {\n\t\t\tif (/^[A-Za-z]:/u.test(token) || token.startsWith(\"\\\\\\\\\")) {\n\t\t\t\ttoken += character;\n\t\t\t\ttokenStarted = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (token === \"\" && nextCharacter === \"\\\\\") {\n\t\t\t\ttoken = \"\\\\\\\\\";\n\t\t\t\ttokenStarted = true;\n\t\t\t\tindex++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tescaping = true;\n\t\t\ttokenStarted = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (/\\s/u.test(character)) {\n\t\t\tfinishToken();\n\t\t\tcontinue;\n\t\t}\n\t\tif (\"|><&;\\n\\r$`(){}*?[]\".includes(character) || character === \"#\" || (character === \"~\" && token === \"\")) {\n\t\t\treturn { ok: false, error: UNSUPPORTED_OPERATOR_MESSAGE };\n\t\t}\n\t\ttoken += character;\n\t\ttokenStarted = true;\n\t}\n\n\tif (escaping) return { ok: false, error: \"Unsupported trailing escape in Bash-like command.\" };\n\tif (quote) return { ok: false, error: \"Unclosed quote in Bash-like command.\" };\n\tfinishToken();\n\treturn argv.length > 0 ? { ok: true, argv } : { ok: false, error: \"Shell command is empty.\" };\n}\n\nfunction quotePowerShell(value: string): string {\n\treturn `'${value.replaceAll(\"'\", \"''\")}'`;\n}\n\nfunction powershellArray(values: readonly string[]): string {\n\treturn `@(${values.map(quotePowerShell).join(\", \")})`;\n}\n\nfunction externalCommand(argv: readonly string[]): string {\n\tconst invocation = `& ${argv.map(quotePowerShell).join(\" \")}`;\n\treturn `${invocation}\\nif ($null -ne $LASTEXITCODE) { exit $LASTEXITCODE }\\nif (-not $?) { exit 1 }`;\n}\n\nfunction parseFlags(\n\targv: readonly string[],\n\tallowedFlags: ReadonlySet<string>,\n): { flags: Set<string>; operands: string[] } | undefined {\n\tconst flags = new Set<string>();\n\tconst operands: string[] = [];\n\tlet optionsEnded = false;\n\tfor (const value of argv) {\n\t\tif (!optionsEnded && value === \"--\") {\n\t\t\toptionsEnded = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (!optionsEnded && value.startsWith(\"-\") && value !== \"-\") {\n\t\t\tif (!allowedFlags.has(value)) return undefined;\n\t\t\tflags.add(value);\n\t\t\tcontinue;\n\t\t}\n\t\toperands.push(value);\n\t}\n\treturn { flags, operands };\n}\n\nfunction routeLs(argv: readonly string[]): string | undefined {\n\tconst parsed = parseFlags(argv.slice(1), new Set([\"-a\", \"-A\", \"-l\", \"-1\", \"-la\", \"-al\"]));\n\tif (!parsed || parsed.operands.length > 1) return undefined;\n\tconst target = parsed.operands[0] ?? \".\";\n\tconst includeHidden = [...parsed.flags].some((flag) => flag.includes(\"a\") || flag.includes(\"A\"));\n\tconst hiddenFilter = includeHidden ? \"\" : \" | Where-Object { -not $_.Name.StartsWith('.') }\";\n\treturn `[string[]]$items = @(Get-ChildItem -LiteralPath ${quotePowerShell(target)}${includeHidden ? \" -Force\" : \"\"} -ErrorAction Stop${hiddenFilter} | ForEach-Object { if ($_.PSIsContainer) { $_.Name + '/' } else { $_.Name } }); [Array]::Sort($items, [StringComparer]::Ordinal); $items`;\n}\n\nfunction routeCat(argv: readonly string[]): string | undefined {\n\tconst parsed = parseFlags(argv.slice(1), new Set([\"--\"]));\n\tif (!parsed || parsed.operands.length === 0) return undefined;\n\treturn `foreach ($path in ${powershellArray(parsed.operands)}) { [Console]::Out.Write([IO.File]::ReadAllText($path)) }`;\n}\n\nfunction routeHeadOrTail(argv: readonly string[], tail: boolean): string | undefined {\n\tlet count = 10;\n\tlet operands = argv.slice(1);\n\tif (operands[0] === \"-n\") {\n\t\tconst parsedCount = Number(operands[1]);\n\t\tif (!Number.isInteger(parsedCount) || parsedCount < 0) return undefined;\n\t\tcount = parsedCount;\n\t\toperands = operands.slice(2);\n\t}\n\tif (operands.length !== 1 || operands[0].startsWith(\"-\")) return undefined;\n\treturn `Get-Content -LiteralPath ${quotePowerShell(operands[0])} ${tail ? \"-Tail\" : \"-TotalCount\"} ${count} -ErrorAction Stop`;\n}\n\nfunction routeGrep(argv: readonly string[]): string | undefined {\n\tif (argv.length !== 3 || argv[1].startsWith(\"-\") || argv[2].startsWith(\"-\")) return undefined;\n\treturn `[string[]]$matches = @(Select-String -LiteralPath ${quotePowerShell(argv[2])} -Pattern ${quotePowerShell(argv[1])} -CaseSensitive -ErrorAction Stop | ForEach-Object { $_.Line }); if ($matches.Count -eq 0) { exit 1 }; $matches`;\n}\n\nfunction routeFind(argv: readonly string[]): string | undefined {\n\tlet searchPath = \".\";\n\tlet type: \"File\" | \"Directory\" | undefined;\n\tlet filter: string | undefined;\n\tlet index = 1;\n\tif (argv[index] && !argv[index].startsWith(\"-\")) searchPath = argv[index++];\n\twhile (index < argv.length) {\n\t\tconst option = argv[index++];\n\t\tif (option === \"-type\" && (argv[index] === \"f\" || argv[index] === \"d\")) {\n\t\t\ttype = argv[index++] === \"f\" ? \"File\" : \"Directory\";\n\t\t\tcontinue;\n\t\t}\n\t\tif (option === \"-name\" && argv[index]) {\n\t\t\tfilter = argv[index++];\n\t\t\tcontinue;\n\t\t}\n\t\treturn undefined;\n\t}\n\tconst typeFlag = type ? ` -${type}` : \"\";\n\tconst filterFlag = filter ? ` -Filter ${quotePowerShell(filter)}` : \"\";\n\treturn `[string[]]$paths = @(Get-ChildItem -LiteralPath ${quotePowerShell(searchPath)} -Recurse -Force${typeFlag}${filterFlag} -ErrorAction Stop | ForEach-Object { $_.FullName.Replace('\\\\', '/') }); [Array]::Sort($paths, [StringComparer]::Ordinal); $paths`;\n}\n\nfunction routeRemove(argv: readonly string[]): string | undefined {\n\tconst parsed = parseFlags(argv.slice(1), new Set([\"-f\", \"-r\", \"-rf\", \"-fr\"]));\n\tif (!parsed || parsed.operands.length === 0) return undefined;\n\tconst recursive = [...parsed.flags].some((flag) => flag.includes(\"r\"));\n\tconst force = [...parsed.flags].some((flag) => flag.includes(\"f\"));\n\treturn `foreach ($path in ${powershellArray(parsed.operands)}) { if (Test-Path -LiteralPath $path) { Remove-Item -LiteralPath $path -Force${recursive ? \" -Recurse\" : \"\"} -ErrorAction Stop }${force ? \"\" : ' else { throw \"rm: path not found: $path\" }'} }`;\n}\n\nfunction routeCopyOrMove(argv: readonly string[], move: boolean): string | undefined {\n\tconst parsed = parseFlags(argv.slice(1), move ? new Set<string>() : new Set([\"-r\", \"-R\"]));\n\tif (!parsed || parsed.operands.length !== 2) return undefined;\n\tconst [source, destination] = parsed.operands;\n\tconst recurse = !move && parsed.flags.size > 0 ? \" -Recurse\" : \"\";\n\tconst rejectDirectoryWithoutRecursion =\n\t\t!move && parsed.flags.size === 0\n\t\t\t? `$sourceItem = Get-Item -LiteralPath ${quotePowerShell(source)} -Force -ErrorAction Stop; if ($sourceItem.PSIsContainer) { throw 'cp: source is a directory; use -r' }; `\n\t\t\t: \"\";\n\treturn `${rejectDirectoryWithoutRecursion}${move ? \"Move-Item\" : \"Copy-Item\"} -LiteralPath ${quotePowerShell(source)} -Destination ${quotePowerShell(destination)}${recurse} -ErrorAction Stop`;\n}\n\nfunction routeMkdir(argv: readonly string[]): string | undefined {\n\tconst parsed = parseFlags(argv.slice(1), new Set([\"-p\"]));\n\tif (!parsed || parsed.operands.length === 0) return undefined;\n\treturn `${powershellArray(parsed.operands)} | ForEach-Object { New-Item -ItemType Directory -Path $_${parsed.flags.has(\"-p\") ? \" -Force\" : \"\"} -ErrorAction Stop | Out-Null }`;\n}\n\nfunction routeTouch(argv: readonly string[]): string | undefined {\n\tconst parsed = parseFlags(argv.slice(1), new Set());\n\tif (!parsed || parsed.operands.length === 0) return undefined;\n\treturn `${powershellArray(parsed.operands)} | ForEach-Object { if (Test-Path -LiteralPath $_) { (Get-Item -LiteralPath $_ -ErrorAction Stop).LastWriteTime = Get-Date } else { New-Item -ItemType File -Path $_ -ErrorAction Stop | Out-Null } }`;\n}\n\nfunction routeBuiltIn(argv: readonly string[]): string | undefined {\n\tconst command = argv[0].toLowerCase();\n\tif (command === \"pwd\" && argv.length === 1) return \"(Get-Location).Path\";\n\tif (command === \"echo\") {\n\t\tlet valueIndex = 1;\n\t\twhile (/^-n+$/u.test(argv[valueIndex] ?? \"\")) valueIndex++;\n\t\tif (/^-[neE]+$/u.test(argv[valueIndex] ?? \"\") && /[eE]/u.test(argv[valueIndex] ?? \"\")) return undefined;\n\t\tconst method = valueIndex > 1 ? \"Write\" : \"WriteLine\";\n\t\treturn `[Console]::Out.${method}((${powershellArray(argv.slice(valueIndex))} -join ' '))`;\n\t}\n\tif (command === \"true\" && argv.length === 1) return \"exit 0\";\n\tif (command === \"false\" && argv.length === 1) return \"exit 1\";\n\tif (command === \"which\" && argv.length === 2) {\n\t\treturn `(Get-Command -Name ${quotePowerShell(argv[1])} -CommandType Application -ErrorAction Stop).Source`;\n\t}\n\tif (command === \"ls\") return routeLs(argv);\n\tif (command === \"cat\") return routeCat(argv);\n\tif (command === \"head\") return routeHeadOrTail(argv, false);\n\tif (command === \"tail\") return routeHeadOrTail(argv, true);\n\tif (command === \"grep\") return routeGrep(argv);\n\tif (command === \"find\") return routeFind(argv);\n\tif (command === \"rm\") return routeRemove(argv);\n\tif (command === \"cp\") return routeCopyOrMove(argv, false);\n\tif (command === \"mv\") return routeCopyOrMove(argv, true);\n\tif (command === \"mkdir\") return routeMkdir(argv);\n\tif (command === \"touch\") return routeTouch(argv);\n\treturn undefined;\n}\n\nconst ROUTED_BUILTIN_NAMES = new Set([\n\t\"pwd\",\n\t\"echo\",\n\t\"true\",\n\t\"false\",\n\t\"which\",\n\t\"ls\",\n\t\"cat\",\n\t\"head\",\n\t\"tail\",\n\t\"grep\",\n\t\"find\",\n\t\"rm\",\n\t\"cp\",\n\t\"mv\",\n\t\"mkdir\",\n\t\"touch\",\n]);\n\nexport function routeShellContract(\n\tcommand: string,\n\tplatform: NodeJS.Platform = process.platform,\n\toptions?: { pythonEngine?: boolean },\n): ShellContractRoute {\n\tif (platform !== \"win32\") return { kind: \"passthrough\", command };\n\tconst pythonEngine = options?.pythonEngine === true;\n\tconst tokenized = tokenizePortableCommand(command);\n\n\t// Complex constructs (pipelines, redirection, expansion, quoting the tokenizer refuses, …) are\n\t// exactly the grammar the Python engine owns; with the engine off this is the original\n\t// fail-closed floor verbatim.\n\tif (!tokenized.ok || !tokenized.argv) {\n\t\tif (pythonEngine) return { kind: \"python-engine\", command };\n\t\treturn { kind: \"unsupported\", error: tokenized.error ?? UNSUPPORTED_OPERATOR_MESSAGE };\n\t}\n\n\tconst argv = tokenized.argv;\n\tconst commandName = argv[0].toLowerCase();\n\n\t// Inline env assignments (`NAME=value [cmd]`) are a state-mutating/expansion form the engine\n\t// supports (§2.1); with the engine off this is the original fail-closed floor verbatim.\n\tif (/^[A-Za-z_][A-Za-z0-9_]*=/u.test(argv[0])) {\n\t\tif (pythonEngine) return { kind: \"python-engine\", command };\n\t\treturn {\n\t\t\tkind: \"unsupported\",\n\t\t\terror: \"Inline environment assignments are not supported. Configure the environment outside the shell command.\",\n\t\t};\n\t}\n\n\t// State mutators and controlled exit route to the engine unconditionally when enabled: the\n\t// engine is the sole state owner and must frame `exit` instead of terminating the PS backend.\n\tif (ENGINE_OWNED_BUILTINS.has(commandName)) {\n\t\tif (pythonEngine) return { kind: \"python-engine\", command };\n\t\tif (commandName === \"exit\") {\n\t\t\treturn {\n\t\t\t\tkind: \"unsupported\",\n\t\t\t\terror: \"The Bash-like 'exit' builtin requires the Windows Python shell engine so Pi can preserve its terminal result protocol.\",\n\t\t\t};\n\t\t}\n\t}\n\n\tif (BLOCKED_NESTED_SHELLS.has(commandName)) {\n\t\treturn {\n\t\t\tkind: \"unsupported\",\n\t\t\terror: \"Nested shell execution is not supported by the Windows shell contract router.\",\n\t\t};\n\t}\n\tconst builtIn = routeBuiltIn(argv);\n\tif (builtIn) return { kind: \"powershell\", command: builtIn, argv };\n\tif (ROUTED_BUILTIN_NAMES.has(commandName)) {\n\t\t// The PS floor rejected the FORM (flags/argument shape); the engine supports the fuller\n\t\t// flag set for the same builtin (§2.2).\n\t\tif (pythonEngine) return { kind: \"python-engine\", command };\n\t\treturn {\n\t\t\tkind: \"unsupported\",\n\t\t\terror: `Unsupported ${argv[0]} form on Windows. Use a simpler Bash-like form or Pi's dedicated read/search/edit tools.`,\n\t\t};\n\t}\n\tif (commandName.endsWith(\".sh\") || commandName.includes(\"/bin/\")) {\n\t\treturn {\n\t\t\tkind: \"unsupported\",\n\t\t\terror: \"POSIX shell scripts are not supported by the Windows shell contract router.\",\n\t\t};\n\t}\n\treturn { kind: \"powershell\", command: externalCommand(argv), argv };\n}\n"]}
@@ -0,0 +1,15 @@
1
+ import { type Stats } from "node:fs";
2
+ /** Compare the filesystem identity and mutation-sensitive metadata captured around a bounded read. */
3
+ export declare function sameFileVersion(left: Stats, right: Stats): boolean;
4
+ /** Read at most `maxBytes`, then probe one byte so a growing file cannot escape the bound. */
5
+ export declare function readFileDescriptorBoundedSync(fileDescriptor: number, maxBytes: number): Buffer | undefined;
6
+ /** Bounded, version-stable text read for small durable state files. */
7
+ export declare function readBoundedTextFileSync(filePath: string, maxBytes: number, label: string): string;
8
+ /**
9
+ * Enumerate a managed directory incrementally. Unlike `readdirSync`, this never allocates an
10
+ * unbounded entry array before the caller can reject a corrupt or hostile directory.
11
+ */
12
+ export declare function readBoundedDirectoryNamesSync(directoryPath: string, maxEntries: number, label: string): string[];
13
+ /** Async counterpart of {@link readBoundedTextFileSync}. */
14
+ export declare function readBoundedTextFile(filePath: string, maxBytes: number, label: string): Promise<string>;
15
+ //# sourceMappingURL=bounded-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bounded-file.d.ts","sourceRoot":"","sources":["../../../src/core/util/bounded-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyD,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAW5F,sGAAsG;AACtG,wBAAgB,eAAe,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAQlE;AAkBD,8FAA8F;AAC9F,wBAAgB,6BAA6B,CAAC,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAc1G;AAED,uEAAuE;AACvE,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAiBjG;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAehH;AAED,4DAA4D;AAC5D,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB5G","sourcesContent":["import { closeSync, fstatSync, opendirSync, openSync, readSync, type Stats } from \"node:fs\";\nimport { type FileHandle, open as openAsync } from \"node:fs/promises\";\n\nconst READ_CHUNK_BYTES = 64 * 1024;\n\nfunction validateByteLimit(maxBytes: number): void {\n\tif (!Number.isSafeInteger(maxBytes) || maxBytes < 0) {\n\t\tthrow new TypeError(\"Bounded file reads require a non-negative safe-integer byte limit.\");\n\t}\n}\n\n/** Compare the filesystem identity and mutation-sensitive metadata captured around a bounded read. */\nexport function sameFileVersion(left: Stats, right: Stats): boolean {\n\treturn (\n\t\tleft.dev === right.dev &&\n\t\tleft.ino === right.ino &&\n\t\tleft.size === right.size &&\n\t\tleft.mtimeMs === right.mtimeMs &&\n\t\tleft.ctimeMs === right.ctimeMs\n\t);\n}\n\nasync function readFileHandleBounded(fileHandle: FileHandle, maxBytes: number): Promise<Buffer | undefined> {\n\tvalidateByteLimit(maxBytes);\n\tconst chunks: Buffer[] = [];\n\tlet totalBytes = 0;\n\twhile (totalBytes < maxBytes) {\n\t\tconst chunk = Buffer.allocUnsafe(Math.min(READ_CHUNK_BYTES, maxBytes - totalBytes));\n\t\tconst { bytesRead } = await fileHandle.read(chunk, 0, chunk.length, totalBytes);\n\t\tif (bytesRead === 0) return Buffer.concat(chunks, totalBytes);\n\t\tchunks.push(chunk.subarray(0, bytesRead));\n\t\ttotalBytes += bytesRead;\n\t}\n\tconst overflowProbe = Buffer.allocUnsafe(1);\n\tif ((await fileHandle.read(overflowProbe, 0, 1, totalBytes)).bytesRead > 0) return undefined;\n\treturn Buffer.concat(chunks, totalBytes);\n}\n\n/** Read at most `maxBytes`, then probe one byte so a growing file cannot escape the bound. */\nexport function readFileDescriptorBoundedSync(fileDescriptor: number, maxBytes: number): Buffer | undefined {\n\tvalidateByteLimit(maxBytes);\n\tconst chunks: Buffer[] = [];\n\tlet totalBytes = 0;\n\twhile (totalBytes < maxBytes) {\n\t\tconst chunk = Buffer.allocUnsafe(Math.min(READ_CHUNK_BYTES, maxBytes - totalBytes));\n\t\tconst bytesRead = readSync(fileDescriptor, chunk, 0, chunk.length, null);\n\t\tif (bytesRead === 0) return Buffer.concat(chunks, totalBytes);\n\t\tchunks.push(chunk.subarray(0, bytesRead));\n\t\ttotalBytes += bytesRead;\n\t}\n\tconst overflowProbe = Buffer.allocUnsafe(1);\n\tif (readSync(fileDescriptor, overflowProbe, 0, 1, null) > 0) return undefined;\n\treturn Buffer.concat(chunks, totalBytes);\n}\n\n/** Bounded, version-stable text read for small durable state files. */\nexport function readBoundedTextFileSync(filePath: string, maxBytes: number, label: string): string {\n\tvalidateByteLimit(maxBytes);\n\tconst fileDescriptor = openSync(filePath, \"r\");\n\ttry {\n\t\tconst before = fstatSync(fileDescriptor);\n\t\tif (!before.isFile()) throw new Error(`${label} is not a regular file.`);\n\t\tif (before.size > maxBytes) throw new Error(`${label} exceeds its byte limit.`);\n\t\tconst content = readFileDescriptorBoundedSync(fileDescriptor, maxBytes);\n\t\tif (!content) throw new Error(`${label} exceeds its byte limit.`);\n\t\tconst after = fstatSync(fileDescriptor);\n\t\tif (!sameFileVersion(before, after) || content.byteLength !== before.size) {\n\t\t\tthrow new Error(`${label} changed while it was being read.`);\n\t\t}\n\t\treturn content.toString(\"utf-8\");\n\t} finally {\n\t\tcloseSync(fileDescriptor);\n\t}\n}\n\n/**\n * Enumerate a managed directory incrementally. Unlike `readdirSync`, this never allocates an\n * unbounded entry array before the caller can reject a corrupt or hostile directory.\n */\nexport function readBoundedDirectoryNamesSync(directoryPath: string, maxEntries: number, label: string): string[] {\n\tif (!Number.isSafeInteger(maxEntries) || maxEntries < 1) {\n\t\tthrow new TypeError(\"Bounded directory reads require a positive safe-integer entry limit.\");\n\t}\n\tconst directory = opendirSync(directoryPath);\n\ttry {\n\t\tconst names: string[] = [];\n\t\tfor (let entry = directory.readSync(); entry; entry = directory.readSync()) {\n\t\t\tif (names.length === maxEntries) throw new Error(`${label} exceeds its entry limit.`);\n\t\t\tnames.push(entry.name);\n\t\t}\n\t\treturn names;\n\t} finally {\n\t\tdirectory.closeSync();\n\t}\n}\n\n/** Async counterpart of {@link readBoundedTextFileSync}. */\nexport async function readBoundedTextFile(filePath: string, maxBytes: number, label: string): Promise<string> {\n\tvalidateByteLimit(maxBytes);\n\tconst fileHandle = await openAsync(filePath, \"r\");\n\ttry {\n\t\tconst before = await fileHandle.stat();\n\t\tif (!before.isFile()) throw new Error(`${label} is not a regular file.`);\n\t\tif (before.size > maxBytes) throw new Error(`${label} exceeds its byte limit.`);\n\t\tconst content = await readFileHandleBounded(fileHandle, maxBytes);\n\t\tif (!content) throw new Error(`${label} exceeds its byte limit.`);\n\t\tconst after = await fileHandle.stat();\n\t\tif (!sameFileVersion(before, after) || content.byteLength !== before.size) {\n\t\t\tthrow new Error(`${label} changed while it was being read.`);\n\t\t}\n\t\treturn content.toString(\"utf-8\");\n\t} finally {\n\t\tawait fileHandle.close();\n\t}\n}\n"]}
@@ -0,0 +1,120 @@
1
+ import { closeSync, fstatSync, opendirSync, openSync, readSync } from "node:fs";
2
+ import { open as openAsync } from "node:fs/promises";
3
+ const READ_CHUNK_BYTES = 64 * 1024;
4
+ function validateByteLimit(maxBytes) {
5
+ if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) {
6
+ throw new TypeError("Bounded file reads require a non-negative safe-integer byte limit.");
7
+ }
8
+ }
9
+ /** Compare the filesystem identity and mutation-sensitive metadata captured around a bounded read. */
10
+ export function sameFileVersion(left, right) {
11
+ return (left.dev === right.dev &&
12
+ left.ino === right.ino &&
13
+ left.size === right.size &&
14
+ left.mtimeMs === right.mtimeMs &&
15
+ left.ctimeMs === right.ctimeMs);
16
+ }
17
+ async function readFileHandleBounded(fileHandle, maxBytes) {
18
+ validateByteLimit(maxBytes);
19
+ const chunks = [];
20
+ let totalBytes = 0;
21
+ while (totalBytes < maxBytes) {
22
+ const chunk = Buffer.allocUnsafe(Math.min(READ_CHUNK_BYTES, maxBytes - totalBytes));
23
+ const { bytesRead } = await fileHandle.read(chunk, 0, chunk.length, totalBytes);
24
+ if (bytesRead === 0)
25
+ return Buffer.concat(chunks, totalBytes);
26
+ chunks.push(chunk.subarray(0, bytesRead));
27
+ totalBytes += bytesRead;
28
+ }
29
+ const overflowProbe = Buffer.allocUnsafe(1);
30
+ if ((await fileHandle.read(overflowProbe, 0, 1, totalBytes)).bytesRead > 0)
31
+ return undefined;
32
+ return Buffer.concat(chunks, totalBytes);
33
+ }
34
+ /** Read at most `maxBytes`, then probe one byte so a growing file cannot escape the bound. */
35
+ export function readFileDescriptorBoundedSync(fileDescriptor, maxBytes) {
36
+ validateByteLimit(maxBytes);
37
+ const chunks = [];
38
+ let totalBytes = 0;
39
+ while (totalBytes < maxBytes) {
40
+ const chunk = Buffer.allocUnsafe(Math.min(READ_CHUNK_BYTES, maxBytes - totalBytes));
41
+ const bytesRead = readSync(fileDescriptor, chunk, 0, chunk.length, null);
42
+ if (bytesRead === 0)
43
+ return Buffer.concat(chunks, totalBytes);
44
+ chunks.push(chunk.subarray(0, bytesRead));
45
+ totalBytes += bytesRead;
46
+ }
47
+ const overflowProbe = Buffer.allocUnsafe(1);
48
+ if (readSync(fileDescriptor, overflowProbe, 0, 1, null) > 0)
49
+ return undefined;
50
+ return Buffer.concat(chunks, totalBytes);
51
+ }
52
+ /** Bounded, version-stable text read for small durable state files. */
53
+ export function readBoundedTextFileSync(filePath, maxBytes, label) {
54
+ validateByteLimit(maxBytes);
55
+ const fileDescriptor = openSync(filePath, "r");
56
+ try {
57
+ const before = fstatSync(fileDescriptor);
58
+ if (!before.isFile())
59
+ throw new Error(`${label} is not a regular file.`);
60
+ if (before.size > maxBytes)
61
+ throw new Error(`${label} exceeds its byte limit.`);
62
+ const content = readFileDescriptorBoundedSync(fileDescriptor, maxBytes);
63
+ if (!content)
64
+ throw new Error(`${label} exceeds its byte limit.`);
65
+ const after = fstatSync(fileDescriptor);
66
+ if (!sameFileVersion(before, after) || content.byteLength !== before.size) {
67
+ throw new Error(`${label} changed while it was being read.`);
68
+ }
69
+ return content.toString("utf-8");
70
+ }
71
+ finally {
72
+ closeSync(fileDescriptor);
73
+ }
74
+ }
75
+ /**
76
+ * Enumerate a managed directory incrementally. Unlike `readdirSync`, this never allocates an
77
+ * unbounded entry array before the caller can reject a corrupt or hostile directory.
78
+ */
79
+ export function readBoundedDirectoryNamesSync(directoryPath, maxEntries, label) {
80
+ if (!Number.isSafeInteger(maxEntries) || maxEntries < 1) {
81
+ throw new TypeError("Bounded directory reads require a positive safe-integer entry limit.");
82
+ }
83
+ const directory = opendirSync(directoryPath);
84
+ try {
85
+ const names = [];
86
+ for (let entry = directory.readSync(); entry; entry = directory.readSync()) {
87
+ if (names.length === maxEntries)
88
+ throw new Error(`${label} exceeds its entry limit.`);
89
+ names.push(entry.name);
90
+ }
91
+ return names;
92
+ }
93
+ finally {
94
+ directory.closeSync();
95
+ }
96
+ }
97
+ /** Async counterpart of {@link readBoundedTextFileSync}. */
98
+ export async function readBoundedTextFile(filePath, maxBytes, label) {
99
+ validateByteLimit(maxBytes);
100
+ const fileHandle = await openAsync(filePath, "r");
101
+ try {
102
+ const before = await fileHandle.stat();
103
+ if (!before.isFile())
104
+ throw new Error(`${label} is not a regular file.`);
105
+ if (before.size > maxBytes)
106
+ throw new Error(`${label} exceeds its byte limit.`);
107
+ const content = await readFileHandleBounded(fileHandle, maxBytes);
108
+ if (!content)
109
+ throw new Error(`${label} exceeds its byte limit.`);
110
+ const after = await fileHandle.stat();
111
+ if (!sameFileVersion(before, after) || content.byteLength !== before.size) {
112
+ throw new Error(`${label} changed while it was being read.`);
113
+ }
114
+ return content.toString("utf-8");
115
+ }
116
+ finally {
117
+ await fileHandle.close();
118
+ }
119
+ }
120
+ //# sourceMappingURL=bounded-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bounded-file.js","sourceRoot":"","sources":["../../../src/core/util/bounded-file.ts"],"names":[],"mappings":"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","sourcesContent":["import { closeSync, fstatSync, opendirSync, openSync, readSync, type Stats } from \"node:fs\";\nimport { type FileHandle, open as openAsync } from \"node:fs/promises\";\n\nconst READ_CHUNK_BYTES = 64 * 1024;\n\nfunction validateByteLimit(maxBytes: number): void {\n\tif (!Number.isSafeInteger(maxBytes) || maxBytes < 0) {\n\t\tthrow new TypeError(\"Bounded file reads require a non-negative safe-integer byte limit.\");\n\t}\n}\n\n/** Compare the filesystem identity and mutation-sensitive metadata captured around a bounded read. */\nexport function sameFileVersion(left: Stats, right: Stats): boolean {\n\treturn (\n\t\tleft.dev === right.dev &&\n\t\tleft.ino === right.ino &&\n\t\tleft.size === right.size &&\n\t\tleft.mtimeMs === right.mtimeMs &&\n\t\tleft.ctimeMs === right.ctimeMs\n\t);\n}\n\nasync function readFileHandleBounded(fileHandle: FileHandle, maxBytes: number): Promise<Buffer | undefined> {\n\tvalidateByteLimit(maxBytes);\n\tconst chunks: Buffer[] = [];\n\tlet totalBytes = 0;\n\twhile (totalBytes < maxBytes) {\n\t\tconst chunk = Buffer.allocUnsafe(Math.min(READ_CHUNK_BYTES, maxBytes - totalBytes));\n\t\tconst { bytesRead } = await fileHandle.read(chunk, 0, chunk.length, totalBytes);\n\t\tif (bytesRead === 0) return Buffer.concat(chunks, totalBytes);\n\t\tchunks.push(chunk.subarray(0, bytesRead));\n\t\ttotalBytes += bytesRead;\n\t}\n\tconst overflowProbe = Buffer.allocUnsafe(1);\n\tif ((await fileHandle.read(overflowProbe, 0, 1, totalBytes)).bytesRead > 0) return undefined;\n\treturn Buffer.concat(chunks, totalBytes);\n}\n\n/** Read at most `maxBytes`, then probe one byte so a growing file cannot escape the bound. */\nexport function readFileDescriptorBoundedSync(fileDescriptor: number, maxBytes: number): Buffer | undefined {\n\tvalidateByteLimit(maxBytes);\n\tconst chunks: Buffer[] = [];\n\tlet totalBytes = 0;\n\twhile (totalBytes < maxBytes) {\n\t\tconst chunk = Buffer.allocUnsafe(Math.min(READ_CHUNK_BYTES, maxBytes - totalBytes));\n\t\tconst bytesRead = readSync(fileDescriptor, chunk, 0, chunk.length, null);\n\t\tif (bytesRead === 0) return Buffer.concat(chunks, totalBytes);\n\t\tchunks.push(chunk.subarray(0, bytesRead));\n\t\ttotalBytes += bytesRead;\n\t}\n\tconst overflowProbe = Buffer.allocUnsafe(1);\n\tif (readSync(fileDescriptor, overflowProbe, 0, 1, null) > 0) return undefined;\n\treturn Buffer.concat(chunks, totalBytes);\n}\n\n/** Bounded, version-stable text read for small durable state files. */\nexport function readBoundedTextFileSync(filePath: string, maxBytes: number, label: string): string {\n\tvalidateByteLimit(maxBytes);\n\tconst fileDescriptor = openSync(filePath, \"r\");\n\ttry {\n\t\tconst before = fstatSync(fileDescriptor);\n\t\tif (!before.isFile()) throw new Error(`${label} is not a regular file.`);\n\t\tif (before.size > maxBytes) throw new Error(`${label} exceeds its byte limit.`);\n\t\tconst content = readFileDescriptorBoundedSync(fileDescriptor, maxBytes);\n\t\tif (!content) throw new Error(`${label} exceeds its byte limit.`);\n\t\tconst after = fstatSync(fileDescriptor);\n\t\tif (!sameFileVersion(before, after) || content.byteLength !== before.size) {\n\t\t\tthrow new Error(`${label} changed while it was being read.`);\n\t\t}\n\t\treturn content.toString(\"utf-8\");\n\t} finally {\n\t\tcloseSync(fileDescriptor);\n\t}\n}\n\n/**\n * Enumerate a managed directory incrementally. Unlike `readdirSync`, this never allocates an\n * unbounded entry array before the caller can reject a corrupt or hostile directory.\n */\nexport function readBoundedDirectoryNamesSync(directoryPath: string, maxEntries: number, label: string): string[] {\n\tif (!Number.isSafeInteger(maxEntries) || maxEntries < 1) {\n\t\tthrow new TypeError(\"Bounded directory reads require a positive safe-integer entry limit.\");\n\t}\n\tconst directory = opendirSync(directoryPath);\n\ttry {\n\t\tconst names: string[] = [];\n\t\tfor (let entry = directory.readSync(); entry; entry = directory.readSync()) {\n\t\t\tif (names.length === maxEntries) throw new Error(`${label} exceeds its entry limit.`);\n\t\t\tnames.push(entry.name);\n\t\t}\n\t\treturn names;\n\t} finally {\n\t\tdirectory.closeSync();\n\t}\n}\n\n/** Async counterpart of {@link readBoundedTextFileSync}. */\nexport async function readBoundedTextFile(filePath: string, maxBytes: number, label: string): Promise<string> {\n\tvalidateByteLimit(maxBytes);\n\tconst fileHandle = await openAsync(filePath, \"r\");\n\ttry {\n\t\tconst before = await fileHandle.stat();\n\t\tif (!before.isFile()) throw new Error(`${label} is not a regular file.`);\n\t\tif (before.size > maxBytes) throw new Error(`${label} exceeds its byte limit.`);\n\t\tconst content = await readFileHandleBounded(fileHandle, maxBytes);\n\t\tif (!content) throw new Error(`${label} exceeds its byte limit.`);\n\t\tconst after = await fileHandle.stat();\n\t\tif (!sameFileVersion(before, after) || content.byteLength !== before.size) {\n\t\t\tthrow new Error(`${label} changed while it was being read.`);\n\t\t}\n\t\treturn content.toString(\"utf-8\");\n\t} finally {\n\t\tawait fileHandle.close();\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"bounded-jsonl.d.ts","sourceRoot":"","sources":["../../../src/core/util/bounded-jsonl.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACnB;AAyCD,+FAA+F;AAC/F,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAQ5G;AAED,8DAA8D;AAC9D,wBAAsB,qBAAqB,CAC1C,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,kBAAkB,GACxB,OAAO,CAAC,IAAI,CAAC,CAmBf","sourcesContent":["import { appendFileSync, promises as fsPromises, readFileSync, statSync } from \"node:fs\";\nimport { withFileLock, withFileLockSync, writeFileAtomic, writeFileAtomicSync } from \"./atomic-file.ts\";\n\nexport interface BoundedJsonlLimits {\n\tmaxBytes: number;\n\ttargetBytes: number;\n\tmaxRecords: number;\n}\n\nconst asyncAppendTails = new Map<string, Promise<void>>();\n\nfunction validateLimits(limits: BoundedJsonlLimits): void {\n\tif (\n\t\t!Number.isSafeInteger(limits.maxBytes) ||\n\t\t!Number.isSafeInteger(limits.targetBytes) ||\n\t\t!Number.isSafeInteger(limits.maxRecords) ||\n\t\tlimits.maxBytes <= 0 ||\n\t\tlimits.targetBytes <= 0 ||\n\t\tlimits.targetBytes > limits.maxBytes ||\n\t\tlimits.maxRecords <= 0\n\t) {\n\t\tthrow new TypeError(\"Bounded JSONL limits require positive integers and targetBytes <= maxBytes.\");\n\t}\n}\n\nfunction retainedContent(content: string, limits: BoundedJsonlLimits): string {\n\tconst lines = content.split(\"\\n\").filter((line) => line.trim().length > 0);\n\tconst retained: string[] = [];\n\tlet retainedBytes = 0;\n\tfor (let index = lines.length - 1; index >= 0 && retained.length < limits.maxRecords; index--) {\n\t\tconst line = lines[index]!;\n\t\tconst lineBytes = Buffer.byteLength(line, \"utf-8\") + 1;\n\t\tif (lineBytes > limits.maxBytes) continue;\n\t\tif (retained.length > 0 && retainedBytes + lineBytes > limits.targetBytes) break;\n\t\tretained.push(line);\n\t\tretainedBytes += lineBytes;\n\t\tif (retainedBytes >= limits.targetBytes) break;\n\t}\n\tretained.reverse();\n\treturn retained.length > 0 ? `${retained.join(\"\\n\")}\\n` : \"\";\n}\n\nfunction serializeLine(value: unknown): string {\n\tconst encoded = JSON.stringify(value);\n\tif (encoded === undefined) throw new TypeError(\"Bounded JSONL records must be JSON-serializable values.\");\n\treturn `${encoded}\\n`;\n}\n\n/** Append one JSON record and rotate to the newest low-water tail under one exclusive lock. */\nexport function appendBoundedJsonLineSync(filePath: string, value: unknown, limits: BoundedJsonlLimits): void {\n\tvalidateLimits(limits);\n\tconst line = serializeLine(value);\n\twithFileLockSync(filePath, () => {\n\t\tappendFileSync(filePath, line, \"utf-8\");\n\t\tif (statSync(filePath).size <= limits.maxBytes) return;\n\t\twriteFileAtomicSync(filePath, retainedContent(readFileSync(filePath, \"utf-8\"), limits));\n\t});\n}\n\n/** Async counterpart of {@link appendBoundedJsonLineSync}. */\nexport async function appendBoundedJsonLine(\n\tfilePath: string,\n\tvalue: unknown,\n\tlimits: BoundedJsonlLimits,\n): Promise<void> {\n\tvalidateLimits(limits);\n\tconst line = serializeLine(value);\n\tconst previous = asyncAppendTails.get(filePath) ?? Promise.resolve();\n\tconst operation = previous\n\t\t.catch(() => undefined)\n\t\t.then(async () => {\n\t\t\tawait withFileLock(filePath, async () => {\n\t\t\t\tawait fsPromises.appendFile(filePath, line, \"utf-8\");\n\t\t\t\tif ((await fsPromises.stat(filePath)).size <= limits.maxBytes) return;\n\t\t\t\tawait writeFileAtomic(filePath, retainedContent(await fsPromises.readFile(filePath, \"utf-8\"), limits));\n\t\t\t});\n\t\t});\n\tasyncAppendTails.set(filePath, operation);\n\ttry {\n\t\tawait operation;\n\t} finally {\n\t\tif (asyncAppendTails.get(filePath) === operation) asyncAppendTails.delete(filePath);\n\t}\n}\n"]}
1
+ {"version":3,"file":"bounded-jsonl.d.ts","sourceRoot":"","sources":["../../../src/core/util/bounded-jsonl.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,kBAAkB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACnB;AA6GD,+FAA+F;AAC/F,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAgC5G;AAED,8DAA8D;AAC9D,wBAAsB,qBAAqB,CAC1C,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,kBAAkB,GACxB,OAAO,CAAC,IAAI,CAAC,CA2Cf","sourcesContent":["import { appendFileSync, promises as fsPromises, type Stats, statSync } from \"node:fs\";\nimport { withFileLock, withFileLockSync, writeFileAtomic, writeFileAtomicSync } from \"./atomic-file.ts\";\nimport { readBoundedTextFile, readBoundedTextFileSync } from \"./bounded-file.ts\";\n\nexport interface BoundedJsonlLimits {\n\tmaxBytes: number;\n\ttargetBytes: number;\n\tmaxRecords: number;\n}\n\nconst asyncAppendTails = new Map<string, Promise<void>>();\nconst MAX_CACHED_FILES = 128;\n\ninterface JsonlFileState {\n\tstats: Stats;\n\trecordCount: number;\n}\n\nconst fileStates = new Map<string, JsonlFileState>();\n\nfunction sameFileState(left: Stats, right: Stats): boolean {\n\treturn (\n\t\tleft.dev === right.dev &&\n\t\tleft.ino === right.ino &&\n\t\tleft.size === right.size &&\n\t\tleft.mtimeMs === right.mtimeMs &&\n\t\tleft.ctimeMs === right.ctimeMs\n\t);\n}\n\nfunction cachedRecordCount(filePath: string, stats: Stats): number | undefined {\n\tconst cached = fileStates.get(filePath);\n\tif (!cached || !sameFileState(cached.stats, stats)) return undefined;\n\tfileStates.delete(filePath);\n\tfileStates.set(filePath, cached);\n\treturn cached.recordCount;\n}\n\nfunction rememberFileState(filePath: string, stats: Stats, recordCount: number): void {\n\tfileStates.delete(filePath);\n\tfileStates.set(filePath, { stats, recordCount });\n\twhile (fileStates.size > MAX_CACHED_FILES) {\n\t\tconst oldest = fileStates.keys().next().value;\n\t\tif (oldest === undefined) break;\n\t\tfileStates.delete(oldest);\n\t}\n}\n\nfunction recordCount(content: string): number {\n\tlet count = 0;\n\tfor (const line of content.split(\"\\n\")) {\n\t\tif (line.trim().length > 0) count++;\n\t}\n\treturn count;\n}\n\nfunction validateLimits(limits: BoundedJsonlLimits): void {\n\tif (\n\t\t!Number.isSafeInteger(limits.maxBytes) ||\n\t\t!Number.isSafeInteger(limits.targetBytes) ||\n\t\t!Number.isSafeInteger(limits.maxRecords) ||\n\t\tlimits.maxBytes <= 0 ||\n\t\tlimits.targetBytes <= 0 ||\n\t\tlimits.targetBytes > limits.maxBytes ||\n\t\tlimits.maxRecords <= 0\n\t) {\n\t\tthrow new TypeError(\"Bounded JSONL limits require positive integers and targetBytes <= maxBytes.\");\n\t}\n}\n\nfunction retainedContent(content: string, limits: BoundedJsonlLimits): string {\n\tconst lines = content.split(\"\\n\").filter((line) => line.trim().length > 0);\n\tconst retained: string[] = [];\n\tlet retainedBytes = 0;\n\tfor (let index = lines.length - 1; index >= 0 && retained.length < limits.maxRecords; index--) {\n\t\tconst line = lines[index]!;\n\t\tconst lineBytes = Buffer.byteLength(line, \"utf-8\") + 1;\n\t\tif (lineBytes > limits.maxBytes) continue;\n\t\tif (retained.length > 0 && retainedBytes + lineBytes > limits.targetBytes) break;\n\t\tretained.push(line);\n\t\tretainedBytes += lineBytes;\n\t\tif (retainedBytes >= limits.targetBytes) break;\n\t}\n\tretained.reverse();\n\treturn retained.length > 0 ? `${retained.join(\"\\n\")}\\n` : \"\";\n}\n\nfunction serializeLine(value: unknown): string {\n\tconst encoded = JSON.stringify(value);\n\tif (encoded === undefined) throw new TypeError(\"Bounded JSONL records must be JSON-serializable values.\");\n\treturn `${encoded}\\n`;\n}\n\nfunction assertLineFits(line: string, limits: BoundedJsonlLimits): number {\n\tconst bytes = Buffer.byteLength(line, \"utf-8\");\n\tif (bytes > limits.maxBytes) throw new Error(\"Bounded JSONL record exceeds maxBytes.\");\n\treturn bytes;\n}\n\nfunction existingStatsSync(filePath: string): Stats | undefined {\n\ttry {\n\t\treturn statSync(filePath);\n\t} catch (error) {\n\t\tif (typeof error === \"object\" && error !== null && \"code\" in error && error.code === \"ENOENT\") return undefined;\n\t\tthrow error;\n\t}\n}\n\nasync function existingStats(filePath: string): Promise<Stats | undefined> {\n\ttry {\n\t\treturn await fsPromises.stat(filePath);\n\t} catch (error) {\n\t\tif (typeof error === \"object\" && error !== null && \"code\" in error && error.code === \"ENOENT\") return undefined;\n\t\tthrow error;\n\t}\n}\n\n/** Append one JSON record and rotate to the newest low-water tail under one exclusive lock. */\nexport function appendBoundedJsonLineSync(filePath: string, value: unknown, limits: BoundedJsonlLimits): void {\n\tvalidateLimits(limits);\n\tconst line = serializeLine(value);\n\tconst lineBytes = assertLineFits(line, limits);\n\twithFileLockSync(filePath, () => {\n\t\ttry {\n\t\t\tconst before = existingStatsSync(filePath);\n\t\t\tif (!before || before.size > limits.maxBytes) {\n\t\t\t\twriteFileAtomicSync(filePath, line);\n\t\t\t\trememberFileState(filePath, statSync(filePath), 1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet content: string | undefined;\n\t\t\tlet count = cachedRecordCount(filePath, before);\n\t\t\tif (count === undefined) {\n\t\t\t\tcontent = readBoundedTextFileSync(filePath, limits.maxBytes, \"Bounded JSONL file\");\n\t\t\t\tcount = recordCount(content);\n\t\t\t}\n\t\t\tif (before.size + lineBytes <= limits.maxBytes && count + 1 <= limits.maxRecords) {\n\t\t\t\tappendFileSync(filePath, line, \"utf-8\");\n\t\t\t\trememberFileState(filePath, statSync(filePath), count + 1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcontent ??= readBoundedTextFileSync(filePath, limits.maxBytes, \"Bounded JSONL file\");\n\t\t\tconst retained = retainedContent(`${content}${line}`, limits);\n\t\t\twriteFileAtomicSync(filePath, retained);\n\t\t\trememberFileState(filePath, statSync(filePath), recordCount(retained));\n\t\t} catch (error) {\n\t\t\tfileStates.delete(filePath);\n\t\t\tthrow error;\n\t\t}\n\t});\n}\n\n/** Async counterpart of {@link appendBoundedJsonLineSync}. */\nexport async function appendBoundedJsonLine(\n\tfilePath: string,\n\tvalue: unknown,\n\tlimits: BoundedJsonlLimits,\n): Promise<void> {\n\tvalidateLimits(limits);\n\tconst line = serializeLine(value);\n\tconst lineBytes = assertLineFits(line, limits);\n\tconst previous = asyncAppendTails.get(filePath) ?? Promise.resolve();\n\tconst operation = previous\n\t\t.catch(() => undefined)\n\t\t.then(async () => {\n\t\t\tawait withFileLock(filePath, async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst before = await existingStats(filePath);\n\t\t\t\t\tif (!before || before.size > limits.maxBytes) {\n\t\t\t\t\t\tawait writeFileAtomic(filePath, line);\n\t\t\t\t\t\trememberFileState(filePath, await fsPromises.stat(filePath), 1);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tlet content: string | undefined;\n\t\t\t\t\tlet count = cachedRecordCount(filePath, before);\n\t\t\t\t\tif (count === undefined) {\n\t\t\t\t\t\tcontent = await readBoundedTextFile(filePath, limits.maxBytes, \"Bounded JSONL file\");\n\t\t\t\t\t\tcount = recordCount(content);\n\t\t\t\t\t}\n\t\t\t\t\tif (before.size + lineBytes <= limits.maxBytes && count + 1 <= limits.maxRecords) {\n\t\t\t\t\t\tawait fsPromises.appendFile(filePath, line, \"utf-8\");\n\t\t\t\t\t\trememberFileState(filePath, await fsPromises.stat(filePath), count + 1);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tcontent ??= await readBoundedTextFile(filePath, limits.maxBytes, \"Bounded JSONL file\");\n\t\t\t\t\tconst retained = retainedContent(`${content}${line}`, limits);\n\t\t\t\t\tawait writeFileAtomic(filePath, retained);\n\t\t\t\t\trememberFileState(filePath, await fsPromises.stat(filePath), recordCount(retained));\n\t\t\t\t} catch (error) {\n\t\t\t\t\tfileStates.delete(filePath);\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\tasyncAppendTails.set(filePath, operation);\n\ttry {\n\t\tawait operation;\n\t} finally {\n\t\tif (asyncAppendTails.get(filePath) === operation) asyncAppendTails.delete(filePath);\n\t}\n}\n"]}
@@ -1,6 +1,42 @@
1
- import { appendFileSync, promises as fsPromises, readFileSync, statSync } from "node:fs";
1
+ import { appendFileSync, promises as fsPromises, statSync } from "node:fs";
2
2
  import { withFileLock, withFileLockSync, writeFileAtomic, writeFileAtomicSync } from "./atomic-file.js";
3
+ import { readBoundedTextFile, readBoundedTextFileSync } from "./bounded-file.js";
3
4
  const asyncAppendTails = new Map();
5
+ const MAX_CACHED_FILES = 128;
6
+ const fileStates = new Map();
7
+ function sameFileState(left, right) {
8
+ return (left.dev === right.dev &&
9
+ left.ino === right.ino &&
10
+ left.size === right.size &&
11
+ left.mtimeMs === right.mtimeMs &&
12
+ left.ctimeMs === right.ctimeMs);
13
+ }
14
+ function cachedRecordCount(filePath, stats) {
15
+ const cached = fileStates.get(filePath);
16
+ if (!cached || !sameFileState(cached.stats, stats))
17
+ return undefined;
18
+ fileStates.delete(filePath);
19
+ fileStates.set(filePath, cached);
20
+ return cached.recordCount;
21
+ }
22
+ function rememberFileState(filePath, stats, recordCount) {
23
+ fileStates.delete(filePath);
24
+ fileStates.set(filePath, { stats, recordCount });
25
+ while (fileStates.size > MAX_CACHED_FILES) {
26
+ const oldest = fileStates.keys().next().value;
27
+ if (oldest === undefined)
28
+ break;
29
+ fileStates.delete(oldest);
30
+ }
31
+ }
32
+ function recordCount(content) {
33
+ let count = 0;
34
+ for (const line of content.split("\n")) {
35
+ if (line.trim().length > 0)
36
+ count++;
37
+ }
38
+ return count;
39
+ }
4
40
  function validateLimits(limits) {
5
41
  if (!Number.isSafeInteger(limits.maxBytes) ||
6
42
  !Number.isSafeInteger(limits.targetBytes) ||
@@ -37,30 +73,104 @@ function serializeLine(value) {
37
73
  throw new TypeError("Bounded JSONL records must be JSON-serializable values.");
38
74
  return `${encoded}\n`;
39
75
  }
76
+ function assertLineFits(line, limits) {
77
+ const bytes = Buffer.byteLength(line, "utf-8");
78
+ if (bytes > limits.maxBytes)
79
+ throw new Error("Bounded JSONL record exceeds maxBytes.");
80
+ return bytes;
81
+ }
82
+ function existingStatsSync(filePath) {
83
+ try {
84
+ return statSync(filePath);
85
+ }
86
+ catch (error) {
87
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")
88
+ return undefined;
89
+ throw error;
90
+ }
91
+ }
92
+ async function existingStats(filePath) {
93
+ try {
94
+ return await fsPromises.stat(filePath);
95
+ }
96
+ catch (error) {
97
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")
98
+ return undefined;
99
+ throw error;
100
+ }
101
+ }
40
102
  /** Append one JSON record and rotate to the newest low-water tail under one exclusive lock. */
41
103
  export function appendBoundedJsonLineSync(filePath, value, limits) {
42
104
  validateLimits(limits);
43
105
  const line = serializeLine(value);
106
+ const lineBytes = assertLineFits(line, limits);
44
107
  withFileLockSync(filePath, () => {
45
- appendFileSync(filePath, line, "utf-8");
46
- if (statSync(filePath).size <= limits.maxBytes)
47
- return;
48
- writeFileAtomicSync(filePath, retainedContent(readFileSync(filePath, "utf-8"), limits));
108
+ try {
109
+ const before = existingStatsSync(filePath);
110
+ if (!before || before.size > limits.maxBytes) {
111
+ writeFileAtomicSync(filePath, line);
112
+ rememberFileState(filePath, statSync(filePath), 1);
113
+ return;
114
+ }
115
+ let content;
116
+ let count = cachedRecordCount(filePath, before);
117
+ if (count === undefined) {
118
+ content = readBoundedTextFileSync(filePath, limits.maxBytes, "Bounded JSONL file");
119
+ count = recordCount(content);
120
+ }
121
+ if (before.size + lineBytes <= limits.maxBytes && count + 1 <= limits.maxRecords) {
122
+ appendFileSync(filePath, line, "utf-8");
123
+ rememberFileState(filePath, statSync(filePath), count + 1);
124
+ return;
125
+ }
126
+ content ??= readBoundedTextFileSync(filePath, limits.maxBytes, "Bounded JSONL file");
127
+ const retained = retainedContent(`${content}${line}`, limits);
128
+ writeFileAtomicSync(filePath, retained);
129
+ rememberFileState(filePath, statSync(filePath), recordCount(retained));
130
+ }
131
+ catch (error) {
132
+ fileStates.delete(filePath);
133
+ throw error;
134
+ }
49
135
  });
50
136
  }
51
137
  /** Async counterpart of {@link appendBoundedJsonLineSync}. */
52
138
  export async function appendBoundedJsonLine(filePath, value, limits) {
53
139
  validateLimits(limits);
54
140
  const line = serializeLine(value);
141
+ const lineBytes = assertLineFits(line, limits);
55
142
  const previous = asyncAppendTails.get(filePath) ?? Promise.resolve();
56
143
  const operation = previous
57
144
  .catch(() => undefined)
58
145
  .then(async () => {
59
146
  await withFileLock(filePath, async () => {
60
- await fsPromises.appendFile(filePath, line, "utf-8");
61
- if ((await fsPromises.stat(filePath)).size <= limits.maxBytes)
62
- return;
63
- await writeFileAtomic(filePath, retainedContent(await fsPromises.readFile(filePath, "utf-8"), limits));
147
+ try {
148
+ const before = await existingStats(filePath);
149
+ if (!before || before.size > limits.maxBytes) {
150
+ await writeFileAtomic(filePath, line);
151
+ rememberFileState(filePath, await fsPromises.stat(filePath), 1);
152
+ return;
153
+ }
154
+ let content;
155
+ let count = cachedRecordCount(filePath, before);
156
+ if (count === undefined) {
157
+ content = await readBoundedTextFile(filePath, limits.maxBytes, "Bounded JSONL file");
158
+ count = recordCount(content);
159
+ }
160
+ if (before.size + lineBytes <= limits.maxBytes && count + 1 <= limits.maxRecords) {
161
+ await fsPromises.appendFile(filePath, line, "utf-8");
162
+ rememberFileState(filePath, await fsPromises.stat(filePath), count + 1);
163
+ return;
164
+ }
165
+ content ??= await readBoundedTextFile(filePath, limits.maxBytes, "Bounded JSONL file");
166
+ const retained = retainedContent(`${content}${line}`, limits);
167
+ await writeFileAtomic(filePath, retained);
168
+ rememberFileState(filePath, await fsPromises.stat(filePath), recordCount(retained));
169
+ }
170
+ catch (error) {
171
+ fileStates.delete(filePath);
172
+ throw error;
173
+ }
64
174
  });
65
175
  });
66
176
  asyncAppendTails.set(filePath, operation);
@@ -1 +1 @@
1
- {"version":3,"file":"bounded-jsonl.js","sourceRoot":"","sources":["../../../src/core/util/bounded-jsonl.ts"],"names":[],"mappings":"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","sourcesContent":["import { appendFileSync, promises as fsPromises, readFileSync, statSync } from \"node:fs\";\nimport { withFileLock, withFileLockSync, writeFileAtomic, writeFileAtomicSync } from \"./atomic-file.ts\";\n\nexport interface BoundedJsonlLimits {\n\tmaxBytes: number;\n\ttargetBytes: number;\n\tmaxRecords: number;\n}\n\nconst asyncAppendTails = new Map<string, Promise<void>>();\n\nfunction validateLimits(limits: BoundedJsonlLimits): void {\n\tif (\n\t\t!Number.isSafeInteger(limits.maxBytes) ||\n\t\t!Number.isSafeInteger(limits.targetBytes) ||\n\t\t!Number.isSafeInteger(limits.maxRecords) ||\n\t\tlimits.maxBytes <= 0 ||\n\t\tlimits.targetBytes <= 0 ||\n\t\tlimits.targetBytes > limits.maxBytes ||\n\t\tlimits.maxRecords <= 0\n\t) {\n\t\tthrow new TypeError(\"Bounded JSONL limits require positive integers and targetBytes <= maxBytes.\");\n\t}\n}\n\nfunction retainedContent(content: string, limits: BoundedJsonlLimits): string {\n\tconst lines = content.split(\"\\n\").filter((line) => line.trim().length > 0);\n\tconst retained: string[] = [];\n\tlet retainedBytes = 0;\n\tfor (let index = lines.length - 1; index >= 0 && retained.length < limits.maxRecords; index--) {\n\t\tconst line = lines[index]!;\n\t\tconst lineBytes = Buffer.byteLength(line, \"utf-8\") + 1;\n\t\tif (lineBytes > limits.maxBytes) continue;\n\t\tif (retained.length > 0 && retainedBytes + lineBytes > limits.targetBytes) break;\n\t\tretained.push(line);\n\t\tretainedBytes += lineBytes;\n\t\tif (retainedBytes >= limits.targetBytes) break;\n\t}\n\tretained.reverse();\n\treturn retained.length > 0 ? `${retained.join(\"\\n\")}\\n` : \"\";\n}\n\nfunction serializeLine(value: unknown): string {\n\tconst encoded = JSON.stringify(value);\n\tif (encoded === undefined) throw new TypeError(\"Bounded JSONL records must be JSON-serializable values.\");\n\treturn `${encoded}\\n`;\n}\n\n/** Append one JSON record and rotate to the newest low-water tail under one exclusive lock. */\nexport function appendBoundedJsonLineSync(filePath: string, value: unknown, limits: BoundedJsonlLimits): void {\n\tvalidateLimits(limits);\n\tconst line = serializeLine(value);\n\twithFileLockSync(filePath, () => {\n\t\tappendFileSync(filePath, line, \"utf-8\");\n\t\tif (statSync(filePath).size <= limits.maxBytes) return;\n\t\twriteFileAtomicSync(filePath, retainedContent(readFileSync(filePath, \"utf-8\"), limits));\n\t});\n}\n\n/** Async counterpart of {@link appendBoundedJsonLineSync}. */\nexport async function appendBoundedJsonLine(\n\tfilePath: string,\n\tvalue: unknown,\n\tlimits: BoundedJsonlLimits,\n): Promise<void> {\n\tvalidateLimits(limits);\n\tconst line = serializeLine(value);\n\tconst previous = asyncAppendTails.get(filePath) ?? Promise.resolve();\n\tconst operation = previous\n\t\t.catch(() => undefined)\n\t\t.then(async () => {\n\t\t\tawait withFileLock(filePath, async () => {\n\t\t\t\tawait fsPromises.appendFile(filePath, line, \"utf-8\");\n\t\t\t\tif ((await fsPromises.stat(filePath)).size <= limits.maxBytes) return;\n\t\t\t\tawait writeFileAtomic(filePath, retainedContent(await fsPromises.readFile(filePath, \"utf-8\"), limits));\n\t\t\t});\n\t\t});\n\tasyncAppendTails.set(filePath, operation);\n\ttry {\n\t\tawait operation;\n\t} finally {\n\t\tif (asyncAppendTails.get(filePath) === operation) asyncAppendTails.delete(filePath);\n\t}\n}\n"]}
1
+ {"version":3,"file":"bounded-jsonl.js","sourceRoot":"","sources":["../../../src/core/util/bounded-jsonl.ts"],"names":[],"mappings":"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","sourcesContent":["import { appendFileSync, promises as fsPromises, type Stats, statSync } from \"node:fs\";\nimport { withFileLock, withFileLockSync, writeFileAtomic, writeFileAtomicSync } from \"./atomic-file.ts\";\nimport { readBoundedTextFile, readBoundedTextFileSync } from \"./bounded-file.ts\";\n\nexport interface BoundedJsonlLimits {\n\tmaxBytes: number;\n\ttargetBytes: number;\n\tmaxRecords: number;\n}\n\nconst asyncAppendTails = new Map<string, Promise<void>>();\nconst MAX_CACHED_FILES = 128;\n\ninterface JsonlFileState {\n\tstats: Stats;\n\trecordCount: number;\n}\n\nconst fileStates = new Map<string, JsonlFileState>();\n\nfunction sameFileState(left: Stats, right: Stats): boolean {\n\treturn (\n\t\tleft.dev === right.dev &&\n\t\tleft.ino === right.ino &&\n\t\tleft.size === right.size &&\n\t\tleft.mtimeMs === right.mtimeMs &&\n\t\tleft.ctimeMs === right.ctimeMs\n\t);\n}\n\nfunction cachedRecordCount(filePath: string, stats: Stats): number | undefined {\n\tconst cached = fileStates.get(filePath);\n\tif (!cached || !sameFileState(cached.stats, stats)) return undefined;\n\tfileStates.delete(filePath);\n\tfileStates.set(filePath, cached);\n\treturn cached.recordCount;\n}\n\nfunction rememberFileState(filePath: string, stats: Stats, recordCount: number): void {\n\tfileStates.delete(filePath);\n\tfileStates.set(filePath, { stats, recordCount });\n\twhile (fileStates.size > MAX_CACHED_FILES) {\n\t\tconst oldest = fileStates.keys().next().value;\n\t\tif (oldest === undefined) break;\n\t\tfileStates.delete(oldest);\n\t}\n}\n\nfunction recordCount(content: string): number {\n\tlet count = 0;\n\tfor (const line of content.split(\"\\n\")) {\n\t\tif (line.trim().length > 0) count++;\n\t}\n\treturn count;\n}\n\nfunction validateLimits(limits: BoundedJsonlLimits): void {\n\tif (\n\t\t!Number.isSafeInteger(limits.maxBytes) ||\n\t\t!Number.isSafeInteger(limits.targetBytes) ||\n\t\t!Number.isSafeInteger(limits.maxRecords) ||\n\t\tlimits.maxBytes <= 0 ||\n\t\tlimits.targetBytes <= 0 ||\n\t\tlimits.targetBytes > limits.maxBytes ||\n\t\tlimits.maxRecords <= 0\n\t) {\n\t\tthrow new TypeError(\"Bounded JSONL limits require positive integers and targetBytes <= maxBytes.\");\n\t}\n}\n\nfunction retainedContent(content: string, limits: BoundedJsonlLimits): string {\n\tconst lines = content.split(\"\\n\").filter((line) => line.trim().length > 0);\n\tconst retained: string[] = [];\n\tlet retainedBytes = 0;\n\tfor (let index = lines.length - 1; index >= 0 && retained.length < limits.maxRecords; index--) {\n\t\tconst line = lines[index]!;\n\t\tconst lineBytes = Buffer.byteLength(line, \"utf-8\") + 1;\n\t\tif (lineBytes > limits.maxBytes) continue;\n\t\tif (retained.length > 0 && retainedBytes + lineBytes > limits.targetBytes) break;\n\t\tretained.push(line);\n\t\tretainedBytes += lineBytes;\n\t\tif (retainedBytes >= limits.targetBytes) break;\n\t}\n\tretained.reverse();\n\treturn retained.length > 0 ? `${retained.join(\"\\n\")}\\n` : \"\";\n}\n\nfunction serializeLine(value: unknown): string {\n\tconst encoded = JSON.stringify(value);\n\tif (encoded === undefined) throw new TypeError(\"Bounded JSONL records must be JSON-serializable values.\");\n\treturn `${encoded}\\n`;\n}\n\nfunction assertLineFits(line: string, limits: BoundedJsonlLimits): number {\n\tconst bytes = Buffer.byteLength(line, \"utf-8\");\n\tif (bytes > limits.maxBytes) throw new Error(\"Bounded JSONL record exceeds maxBytes.\");\n\treturn bytes;\n}\n\nfunction existingStatsSync(filePath: string): Stats | undefined {\n\ttry {\n\t\treturn statSync(filePath);\n\t} catch (error) {\n\t\tif (typeof error === \"object\" && error !== null && \"code\" in error && error.code === \"ENOENT\") return undefined;\n\t\tthrow error;\n\t}\n}\n\nasync function existingStats(filePath: string): Promise<Stats | undefined> {\n\ttry {\n\t\treturn await fsPromises.stat(filePath);\n\t} catch (error) {\n\t\tif (typeof error === \"object\" && error !== null && \"code\" in error && error.code === \"ENOENT\") return undefined;\n\t\tthrow error;\n\t}\n}\n\n/** Append one JSON record and rotate to the newest low-water tail under one exclusive lock. */\nexport function appendBoundedJsonLineSync(filePath: string, value: unknown, limits: BoundedJsonlLimits): void {\n\tvalidateLimits(limits);\n\tconst line = serializeLine(value);\n\tconst lineBytes = assertLineFits(line, limits);\n\twithFileLockSync(filePath, () => {\n\t\ttry {\n\t\t\tconst before = existingStatsSync(filePath);\n\t\t\tif (!before || before.size > limits.maxBytes) {\n\t\t\t\twriteFileAtomicSync(filePath, line);\n\t\t\t\trememberFileState(filePath, statSync(filePath), 1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet content: string | undefined;\n\t\t\tlet count = cachedRecordCount(filePath, before);\n\t\t\tif (count === undefined) {\n\t\t\t\tcontent = readBoundedTextFileSync(filePath, limits.maxBytes, \"Bounded JSONL file\");\n\t\t\t\tcount = recordCount(content);\n\t\t\t}\n\t\t\tif (before.size + lineBytes <= limits.maxBytes && count + 1 <= limits.maxRecords) {\n\t\t\t\tappendFileSync(filePath, line, \"utf-8\");\n\t\t\t\trememberFileState(filePath, statSync(filePath), count + 1);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcontent ??= readBoundedTextFileSync(filePath, limits.maxBytes, \"Bounded JSONL file\");\n\t\t\tconst retained = retainedContent(`${content}${line}`, limits);\n\t\t\twriteFileAtomicSync(filePath, retained);\n\t\t\trememberFileState(filePath, statSync(filePath), recordCount(retained));\n\t\t} catch (error) {\n\t\t\tfileStates.delete(filePath);\n\t\t\tthrow error;\n\t\t}\n\t});\n}\n\n/** Async counterpart of {@link appendBoundedJsonLineSync}. */\nexport async function appendBoundedJsonLine(\n\tfilePath: string,\n\tvalue: unknown,\n\tlimits: BoundedJsonlLimits,\n): Promise<void> {\n\tvalidateLimits(limits);\n\tconst line = serializeLine(value);\n\tconst lineBytes = assertLineFits(line, limits);\n\tconst previous = asyncAppendTails.get(filePath) ?? Promise.resolve();\n\tconst operation = previous\n\t\t.catch(() => undefined)\n\t\t.then(async () => {\n\t\t\tawait withFileLock(filePath, async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst before = await existingStats(filePath);\n\t\t\t\t\tif (!before || before.size > limits.maxBytes) {\n\t\t\t\t\t\tawait writeFileAtomic(filePath, line);\n\t\t\t\t\t\trememberFileState(filePath, await fsPromises.stat(filePath), 1);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tlet content: string | undefined;\n\t\t\t\t\tlet count = cachedRecordCount(filePath, before);\n\t\t\t\t\tif (count === undefined) {\n\t\t\t\t\t\tcontent = await readBoundedTextFile(filePath, limits.maxBytes, \"Bounded JSONL file\");\n\t\t\t\t\t\tcount = recordCount(content);\n\t\t\t\t\t}\n\t\t\t\t\tif (before.size + lineBytes <= limits.maxBytes && count + 1 <= limits.maxRecords) {\n\t\t\t\t\t\tawait fsPromises.appendFile(filePath, line, \"utf-8\");\n\t\t\t\t\t\trememberFileState(filePath, await fsPromises.stat(filePath), count + 1);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tcontent ??= await readBoundedTextFile(filePath, limits.maxBytes, \"Bounded JSONL file\");\n\t\t\t\t\tconst retained = retainedContent(`${content}${line}`, limits);\n\t\t\t\t\tawait writeFileAtomic(filePath, retained);\n\t\t\t\t\trememberFileState(filePath, await fsPromises.stat(filePath), recordCount(retained));\n\t\t\t\t} catch (error) {\n\t\t\t\t\tfileStates.delete(filePath);\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\tasyncAppendTails.set(filePath, operation);\n\ttry {\n\t\tawait operation;\n\t} finally {\n\t\tif (asyncAppendTails.get(filePath) === operation) asyncAppendTails.delete(filePath);\n\t}\n}\n"]}
@@ -0,0 +1,3 @@
1
+ /** One primitive for required, trimmed durable text fields shared across control-plane stores. */
2
+ export declare function requireBoundedTrimmedText(value: string, maximum: number, label: string): string;
3
+ //# sourceMappingURL=bounded-value.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bounded-value.d.ts","sourceRoot":"","sources":["../../../src/core/util/bounded-value.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAS/F","sourcesContent":["/** One primitive for required, trimmed durable text fields shared across control-plane stores. */\nexport function requireBoundedTrimmedText(value: string, maximum: number, label: string): string {\n\tif (!Number.isSafeInteger(maximum) || maximum < 1) {\n\t\tthrow new TypeError(\"A bounded text maximum must be a positive safe integer.\");\n\t}\n\tconst trimmed = value.trim();\n\tif (!trimmed || trimmed.length > maximum) {\n\t\tthrow new TypeError(`${label} must be between 1 and ${maximum} characters.`);\n\t}\n\treturn trimmed;\n}\n"]}
@@ -0,0 +1,12 @@
1
+ /** One primitive for required, trimmed durable text fields shared across control-plane stores. */
2
+ export function requireBoundedTrimmedText(value, maximum, label) {
3
+ if (!Number.isSafeInteger(maximum) || maximum < 1) {
4
+ throw new TypeError("A bounded text maximum must be a positive safe integer.");
5
+ }
6
+ const trimmed = value.trim();
7
+ if (!trimmed || trimmed.length > maximum) {
8
+ throw new TypeError(`${label} must be between 1 and ${maximum} characters.`);
9
+ }
10
+ return trimmed;
11
+ }
12
+ //# sourceMappingURL=bounded-value.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bounded-value.js","sourceRoot":"","sources":["../../../src/core/util/bounded-value.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,MAAM,UAAU,yBAAyB,CAAC,KAAa,EAAE,OAAe,EAAE,KAAa,EAAU;IAChG,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,SAAS,CAAC,yDAAyD,CAAC,CAAC;IAChF,CAAC;IACD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,OAAO,EAAE,CAAC;QAC1C,MAAM,IAAI,SAAS,CAAC,GAAG,KAAK,0BAA0B,OAAO,cAAc,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,OAAO,CAAC;AAAA,CACf","sourcesContent":["/** One primitive for required, trimmed durable text fields shared across control-plane stores. */\nexport function requireBoundedTrimmedText(value: string, maximum: number, label: string): string {\n\tif (!Number.isSafeInteger(maximum) || maximum < 1) {\n\t\tthrow new TypeError(\"A bounded text maximum must be a positive safe integer.\");\n\t}\n\tconst trimmed = value.trim();\n\tif (!trimmed || trimmed.length > maximum) {\n\t\tthrow new TypeError(`${label} must be between 1 and ${maximum} characters.`);\n\t}\n\treturn trimmed;\n}\n"]}
@@ -2,4 +2,6 @@
2
2
  export declare function isRecordObject(value: unknown): value is Record<string, unknown>;
3
3
  /** Accepts only ordinary JSON-style records, including null-prototype dictionaries. */
4
4
  export declare function isPlainRecord(value: unknown): value is Record<string, unknown>;
5
+ /** Fail closed when an untrusted record contains a field outside its typed contract. */
6
+ export declare function hasOnlyKeys(record: Record<string, unknown>, allowed: readonly string[]): boolean;
5
7
  //# sourceMappingURL=value-guards.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"value-guards.d.ts","sourceRoot":"","sources":["../../../src/core/util/value-guards.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE/E;AAED,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAI9E","sourcesContent":["/** Accepts non-null object records while excluding arrays; custom prototypes remain valid. */\nexport function isRecordObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Accepts only ordinary JSON-style records, including null-prototype dictionaries. */\nexport function isPlainRecord(value: unknown): value is Record<string, unknown> {\n\tif (!isRecordObject(value)) return false;\n\tconst prototype = Object.getPrototypeOf(value);\n\treturn prototype === Object.prototype || prototype === null;\n}\n"]}
1
+ {"version":3,"file":"value-guards.d.ts","sourceRoot":"","sources":["../../../src/core/util/value-guards.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE/E;AAED,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAI9E;AAED,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAGhG","sourcesContent":["/** Accepts non-null object records while excluding arrays; custom prototypes remain valid. */\nexport function isRecordObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Accepts only ordinary JSON-style records, including null-prototype dictionaries. */\nexport function isPlainRecord(value: unknown): value is Record<string, unknown> {\n\tif (!isRecordObject(value)) return false;\n\tconst prototype = Object.getPrototypeOf(value);\n\treturn prototype === Object.prototype || prototype === null;\n}\n\n/** Fail closed when an untrusted record contains a field outside its typed contract. */\nexport function hasOnlyKeys(record: Record<string, unknown>, allowed: readonly string[]): boolean {\n\tconst allowedSet = new Set(allowed);\n\treturn Object.keys(record).every((key) => allowedSet.has(key));\n}\n"]}
@@ -9,4 +9,9 @@ export function isPlainRecord(value) {
9
9
  const prototype = Object.getPrototypeOf(value);
10
10
  return prototype === Object.prototype || prototype === null;
11
11
  }
12
+ /** Fail closed when an untrusted record contains a field outside its typed contract. */
13
+ export function hasOnlyKeys(record, allowed) {
14
+ const allowedSet = new Set(allowed);
15
+ return Object.keys(record).every((key) => allowedSet.has(key));
16
+ }
12
17
  //# sourceMappingURL=value-guards.js.map