@dailephd/my-dev-kit-lab 0.4.5 → 0.4.7

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 (263) hide show
  1. package/CHANGELOG.md +26 -1
  2. package/README.md +43 -3
  3. package/dist/scripts/cli.js +9 -0
  4. package/dist/src/audits/core/typescriptJavaScriptAnalyzer.js +30 -12
  5. package/dist/src/browser/index.js +2 -0
  6. package/dist/src/browser/playwrightRuntime.js +74 -0
  7. package/dist/src/browser/types.js +10 -0
  8. package/dist/src/cli/help.js +376 -0
  9. package/dist/src/cli/index.js +2 -0
  10. package/dist/src/cli/runLabCli.js +257 -0
  11. package/dist/src/commands/runAuditCommand.js +132 -0
  12. package/dist/src/commands/runControlledExperimentCommand.js +7 -3
  13. package/dist/{scripts/experiments/describeExperiment.js → src/commands/runExperimentDescribeCommand.js} +14 -10
  14. package/dist/{scripts/experiments/listExperiments.js → src/commands/runExperimentListCommand.js} +3 -6
  15. package/dist/{scripts/experiments/runExperiment.js → src/commands/runExperimentRunCommand.js} +80 -22
  16. package/dist/src/commands/runFinalDemoCommand.js +2 -3
  17. package/dist/src/commands/runSecurityValidationCommand.js +271 -0
  18. package/dist/src/commands/runTutorialRunCommand.js +170 -0
  19. package/dist/src/commands/runTutorialValidateCommand.js +138 -0
  20. package/dist/src/core/processTree.js +52 -0
  21. package/dist/src/core/resolveCommand.js +34 -0
  22. package/dist/src/core/runMeasuredCommand.js +39 -31
  23. package/dist/src/runtime/index.js +4 -0
  24. package/dist/src/runtime/labExecutionContext.js +22 -0
  25. package/dist/src/runtime/managedProcess.js +380 -0
  26. package/dist/src/runtime/packageResource.js +22 -0
  27. package/dist/src/runtime/packageRoot.js +37 -0
  28. package/dist/src/screenshot/captureReportScreenshot.js +30 -27
  29. package/dist/src/securityValidation/packageChecks/requiredPackageContents.js +14 -1
  30. package/dist/src/tutorial/index.js +16 -0
  31. package/dist/src/tutorial/loadTutorialContracts.js +60 -0
  32. package/dist/src/tutorial/markdownTutorialWriter.js +53 -0
  33. package/dist/src/tutorial/runTutorial.js +465 -0
  34. package/dist/src/tutorial/scenarioValidation.js +383 -0
  35. package/dist/src/tutorial/subtitleWriter.js +88 -0
  36. package/dist/src/tutorial/targetContractValidation.js +212 -0
  37. package/dist/src/tutorial/tutorialActions.js +110 -0
  38. package/dist/src/tutorial/tutorialArtifacts.js +176 -0
  39. package/dist/src/tutorial/tutorialAssertions.js +249 -0
  40. package/dist/src/tutorial/tutorialCursor.js +149 -0
  41. package/dist/src/tutorial/tutorialLocators.js +37 -0
  42. package/dist/src/tutorial/tutorialManifest.js +74 -0
  43. package/dist/src/tutorial/tutorialOverlay.js +180 -0
  44. package/dist/src/tutorial/tutorialPaths.js +86 -0
  45. package/dist/src/tutorial/tutorialSession.js +398 -0
  46. package/dist/src/tutorial/types.js +48 -0
  47. package/dist/src/tutorial/validationPrimitives.js +141 -0
  48. package/examples/tutorial-browser/index.html +47 -0
  49. package/examples/tutorial-browser/prepare.mjs +14 -0
  50. package/examples/tutorial-browser/scenario.json +17 -0
  51. package/examples/tutorial-browser/server.mjs +14 -0
  52. package/package.json +13 -9
  53. package/dist/scripts/audits/runAudit.js +0 -135
  54. package/dist/scripts/build-gallery.js +0 -3
  55. package/dist/scripts/capture-demo-report.js +0 -3
  56. package/dist/scripts/evaluate-token-savings.js +0 -2
  57. package/dist/scripts/generate-experiment-plots.js +0 -3
  58. package/dist/scripts/generate-prompt-variants.js +0 -2
  59. package/dist/scripts/render-experiment-report.js +0 -2
  60. package/dist/scripts/report-context-integrity-smoke.js +0 -70
  61. package/dist/scripts/run-agent-prompt.js +0 -2
  62. package/dist/scripts/run-controlled-experiment.js +0 -2
  63. package/dist/scripts/run-lab-demo.js +0 -5
  64. package/dist/scripts/run-visualization-demos.js +0 -3
  65. package/dist/scripts/security/resolveToolRoot.js +0 -16
  66. package/dist/scripts/security/runCodeql.js +0 -58
  67. package/dist/scripts/security/runDependencyChecks.js +0 -58
  68. package/dist/scripts/security/runFuzzSmoke.js +0 -29
  69. package/dist/scripts/security/runPackageChecks.js +0 -57
  70. package/dist/scripts/security/runSemgrep.js +0 -64
  71. package/dist/scripts/security/validate.js +0 -266
  72. package/dist/scripts/verify-benchmarks.js +0 -202
  73. package/examples/demo-report-input.json +0 -78
  74. package/examples/lab-demo-cases.json +0 -35
  75. package/examples/real-agent-campaign-cases.json +0 -118
  76. package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-a/src/debug/res/xml/ambiguous_backup_rules.xml +0 -4
  77. package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-a/src/main/res/xml/ambiguous_backup_rules.xml +0 -4
  78. package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-a/src/main/res/xml/backup_rules.xml +0 -5
  79. package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-a/src/main/res/xml/broad_backup_rules.xml +0 -4
  80. package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-a/src/main/res/xml/data_extraction_rules.xml +0 -10
  81. package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-a/src/main/res/xml/unsupported_domain_rules.xml +0 -4
  82. package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-b/src/main/res/xml/malformed_backup_rules.xml +0 -4
  83. package/tests/fixtures/android/advanced-security-fixtures/backup-configuration/module-b/src/main/res/xml/unsupported_root.xml +0 -4
  84. package/tests/fixtures/android/advanced-security-fixtures/network-security/module-a/src/debug/res/xml/ambiguous_config.xml +0 -4
  85. package/tests/fixtures/android/advanced-security-fixtures/network-security/module-a/src/main/res/xml/ambiguous_config.xml +0 -4
  86. package/tests/fixtures/android/advanced-security-fixtures/network-security/module-a/src/main/res/xml/base_cleartext_false.xml +0 -8
  87. package/tests/fixtures/android/advanced-security-fixtures/network-security/module-a/src/main/res/xml/broad_and_duplicate_domains.xml +0 -8
  88. package/tests/fixtures/android/advanced-security-fixtures/network-security/module-a/src/main/res/xml/empty_domain.xml +0 -6
  89. package/tests/fixtures/android/advanced-security-fixtures/network-security/module-a/src/main/res/xml/malformed_pin.xml +0 -11
  90. package/tests/fixtures/android/advanced-security-fixtures/network-security/module-a/src/main/res/xml/network_security_config.xml +0 -26
  91. package/tests/fixtures/android/advanced-security-fixtures/network-security/module-b/src/main/res/xml/malformed_network_security_config.xml +0 -6
  92. package/tests/fixtures/android/advanced-security-fixtures/network-security/module-b/src/main/res/xml/unsupported_root.xml +0 -4
  93. package/tests/fixtures/android/advanced-security-fixtures/release-configuration/groovy-release-debuggable-dynamic.gradle +0 -7
  94. package/tests/fixtures/android/advanced-security-fixtures/release-configuration/groovy-release-debuggable-false.gradle +0 -9
  95. package/tests/fixtures/android/advanced-security-fixtures/release-configuration/groovy-release-debuggable-true.gradle +0 -13
  96. package/tests/fixtures/android/advanced-security-fixtures/release-configuration/kotlin-release-debuggable-false.gradle.kts +0 -7
  97. package/tests/fixtures/android/advanced-security-fixtures/release-configuration/kotlin-release-debuggable-true.gradle.kts +0 -10
  98. package/tests/fixtures/android/advanced-security-fixtures/release-configuration/missing-release-build-type.gradle.kts +0 -7
  99. package/tests/fixtures/android/advanced-security-fixtures/resource-resolution/module-a/src/debug/res/xml/network_security_config.xml +0 -8
  100. package/tests/fixtures/android/advanced-security-fixtures/resource-resolution/module-a/src/main/res/xml/backup_rules.xml +0 -4
  101. package/tests/fixtures/android/advanced-security-fixtures/resource-resolution/module-a/src/main/res/xml/network_security_config.xml +0 -8
  102. package/tests/fixtures/android/advanced-security-fixtures/resource-resolution/module-b/src/main/res/xml/malformed_network_security_config.xml +0 -7
  103. package/tests/fixtures/android/advanced-security-fixtures/secret-candidates/SampleActivity.java +0 -8
  104. package/tests/fixtures/android/advanced-security-fixtures/secret-candidates/SampleActivity.kt +0 -9
  105. package/tests/fixtures/android/advanced-security-fixtures/secret-candidates/build.gradle +0 -11
  106. package/tests/fixtures/android/advanced-security-fixtures/secret-candidates/build.gradle.kts +0 -11
  107. package/tests/fixtures/android/advanced-security-fixtures/secret-candidates/google-services.json +0 -16
  108. package/tests/fixtures/android/advanced-security-fixtures/secret-candidates/local.properties +0 -3
  109. package/tests/fixtures/android/advanced-security-fixtures/secret-candidates-bounds/module/binary.properties +0 -0
  110. package/tests/fixtures/android/advanced-security-fixtures/secret-candidates-bounds/module/oversized.json +0 -1
  111. package/tests/fixtures/android/advanced-security-fixtures/signing-configuration/app/fake-release-keystore.jks +0 -1
  112. package/tests/fixtures/android/advanced-security-fixtures/signing-configuration/build/generated-fake.keystore +0 -1
  113. package/tests/fixtures/android/compose-app/app/build.gradle.kts +0 -21
  114. package/tests/fixtures/android/compose-app/app/src/main/AndroidManifest.xml +0 -22
  115. package/tests/fixtures/android/compose-app/app/src/main/java/com/example/composeapp/MainActivity.kt +0 -11
  116. package/tests/fixtures/android/compose-app/build.gradle.kts +0 -4
  117. package/tests/fixtures/android/compose-app/gradle/wrapper/gradle-wrapper.properties +0 -4
  118. package/tests/fixtures/android/compose-app/settings.gradle.kts +0 -2
  119. package/tests/fixtures/android/gradle-metadata-fixtures/catalog-only-plugin/build.gradle.kts +0 -3
  120. package/tests/fixtures/android/gradle-metadata-fixtures/catalog-only-plugin/gradle/libs.versions.toml +0 -5
  121. package/tests/fixtures/android/gradle-metadata-fixtures/comment-safety.gradle.kts +0 -21
  122. package/tests/fixtures/android/gradle-metadata-fixtures/dynamic-values.gradle.kts +0 -17
  123. package/tests/fixtures/android/gradle-metadata-fixtures/groovy-literal.gradle +0 -23
  124. package/tests/fixtures/android/gradle-metadata-fixtures/wrapper-malformed.properties +0 -4
  125. package/tests/fixtures/android/gradle-metadata-fixtures/wrapper-missing-url.properties +0 -3
  126. package/tests/fixtures/android/gradle-metadata-fixtures/wrapper-with-checksum.properties +0 -5
  127. package/tests/fixtures/android/java-app/app/build.gradle +0 -16
  128. package/tests/fixtures/android/java-app/app/src/main/AndroidManifest.xml +0 -19
  129. package/tests/fixtures/android/java-app/app/src/main/java/com/example/javaapp/MainActivity.java +0 -4
  130. package/tests/fixtures/android/java-app/build.gradle +0 -3
  131. package/tests/fixtures/android/java-app/gradle/wrapper/gradle-wrapper.properties +0 -4
  132. package/tests/fixtures/android/java-app/settings.gradle +0 -2
  133. package/tests/fixtures/android/library/build.gradle.kts +0 -3
  134. package/tests/fixtures/android/library/gradle/wrapper/gradle-wrapper.properties +0 -4
  135. package/tests/fixtures/android/library/mylibrary/build.gradle.kts +0 -13
  136. package/tests/fixtures/android/library/mylibrary/src/main/AndroidManifest.xml +0 -2
  137. package/tests/fixtures/android/library/settings.gradle.kts +0 -2
  138. package/tests/fixtures/android/manifest-audit-fixtures/incomplete-intent-filter.xml +0 -14
  139. package/tests/fixtures/android/manifest-audit-fixtures/malformed-exported.xml +0 -10
  140. package/tests/fixtures/android/manifest-audit-fixtures/malformed.xml +0 -5
  141. package/tests/fixtures/android/manifest-audit-fixtures/missing-application.xml +0 -7
  142. package/tests/fixtures/android/manifest-audit-fixtures/missing-component-name.xml +0 -11
  143. package/tests/fixtures/android/manifest-audit-fixtures/permission-coverage.xml +0 -29
  144. package/tests/fixtures/android/manifest-audit-fixtures/risky-components.xml +0 -62
  145. package/tests/fixtures/android/manifest-audit-fixtures/risky-deep-links.xml +0 -45
  146. package/tests/fixtures/android/manifest-audit-fixtures/unresolved-placeholder.xml +0 -14
  147. package/tests/fixtures/android/manifest-audit-fixtures/unusual-namespace-prefix.xml +0 -11
  148. package/tests/fixtures/android/multi-module/app/build.gradle.kts +0 -22
  149. package/tests/fixtures/android/multi-module/app/src/androidTest/java/com/example/multimodule/app/ExampleInstrumentedTest.kt +0 -3
  150. package/tests/fixtures/android/multi-module/app/src/main/AndroidManifest.xml +0 -11
  151. package/tests/fixtures/android/multi-module/app/src/test/java/com/example/multimodule/app/ExampleUnitTest.kt +0 -3
  152. package/tests/fixtures/android/multi-module/build.gradle.kts +0 -5
  153. package/tests/fixtures/android/multi-module/core/build.gradle.kts +0 -14
  154. package/tests/fixtures/android/multi-module/core/src/main/AndroidManifest.xml +0 -2
  155. package/tests/fixtures/android/multi-module/feature-login/build.gradle.kts +0 -18
  156. package/tests/fixtures/android/multi-module/feature-login/src/main/AndroidManifest.xml +0 -2
  157. package/tests/fixtures/android/multi-module/gradle/libs.versions.toml +0 -8
  158. package/tests/fixtures/android/multi-module/gradle/wrapper/gradle-wrapper.properties +0 -4
  159. package/tests/fixtures/android/multi-module/settings.gradle.kts +0 -4
  160. package/tests/fixtures/android/non-android-gradle/build.gradle.kts +0 -3
  161. package/tests/fixtures/android/non-android-gradle/core/build.gradle.kts +0 -3
  162. package/tests/fixtures/android/non-android-gradle/core/src/main/java/com/example/core/Core.java +0 -4
  163. package/tests/fixtures/android/non-android-gradle/gradle/wrapper/gradle-wrapper.properties +0 -4
  164. package/tests/fixtures/android/non-android-gradle/settings.gradle.kts +0 -2
  165. package/tests/fixtures/android/partial/app/build.gradle.kts +0 -7
  166. package/tests/fixtures/android/partial/settings.gradle.kts +0 -2
  167. package/tests/fixtures/android/xml-view-app/app/build.gradle.kts +0 -17
  168. package/tests/fixtures/android/xml-view-app/app/src/main/AndroidManifest.xml +0 -31
  169. package/tests/fixtures/android/xml-view-app/app/src/main/kotlin/com/example/xmlviewapp/MainActivity.kt +0 -7
  170. package/tests/fixtures/android/xml-view-app/app/src/main/res/layout/activity_main.xml +0 -4
  171. package/tests/fixtures/android/xml-view-app/build.gradle.kts +0 -4
  172. package/tests/fixtures/android/xml-view-app/gradle/wrapper/gradle-wrapper.properties +0 -4
  173. package/tests/fixtures/android/xml-view-app/settings.gradle.kts +0 -2
  174. package/tests/fixtures/audits/mixed-language/mixed-docs-claims/README.md +0 -16
  175. package/tests/fixtures/audits/mixed-language/mixed-docs-claims/build.gradle.kts +0 -4
  176. package/tests/fixtures/audits/mixed-language/mixed-docs-claims/src/main/java/com/example/Widget.java +0 -7
  177. package/tests/fixtures/audits/mixed-language/mixed-docs-claims/src/main/kotlin/com/example/Gadget.kt +0 -7
  178. package/tests/fixtures/audits/mixed-language/mixed-generated-vendor/README.md +0 -10
  179. package/tests/fixtures/audits/mixed-language/mixed-generated-vendor/build/Main.class +0 -1
  180. package/tests/fixtures/audits/mixed-language/mixed-generated-vendor/dist/index.js +0 -1
  181. package/tests/fixtures/audits/mixed-language/mixed-generated-vendor/generated/schema.ts +0 -1
  182. package/tests/fixtures/audits/mixed-language/mixed-generated-vendor/src/index.ts +0 -3
  183. package/tests/fixtures/audits/mixed-language/mixed-generated-vendor/vendor/lib.py +0 -2
  184. package/tests/fixtures/audits/mixed-language/mixed-java-kotlin/README.md +0 -10
  185. package/tests/fixtures/audits/mixed-language/mixed-java-kotlin/build.gradle.kts +0 -8
  186. package/tests/fixtures/audits/mixed-language/mixed-java-kotlin/settings.gradle.kts +0 -1
  187. package/tests/fixtures/audits/mixed-language/mixed-java-kotlin/src/main/java/com/example/FooService.java +0 -7
  188. package/tests/fixtures/audits/mixed-language/mixed-java-kotlin/src/main/kotlin/com/example/BarService.kt +0 -7
  189. package/tests/fixtures/audits/mixed-language/mixed-java-kotlin/src/test/java/com/example/FooServiceTest.java +0 -8
  190. package/tests/fixtures/audits/mixed-language/mixed-java-kotlin/src/test/kotlin/com/example/BarServiceTest.kt +0 -8
  191. package/tests/fixtures/audits/mixed-language/mixed-python-ts/README.md +0 -8
  192. package/tests/fixtures/audits/mixed-language/mixed-python-ts/src/app.py +0 -7
  193. package/tests/fixtures/audits/mixed-language/mixed-python-ts/src/index.ts +0 -3
  194. package/tests/fixtures/audits/mixed-language/mixed-python-ts/tests/test_app.py +0 -5
  195. package/tests/fixtures/audits/mixed-language/mixed-ts-js/README.md +0 -7
  196. package/tests/fixtures/audits/mixed-language/mixed-ts-js/src/helper.js +0 -5
  197. package/tests/fixtures/audits/mixed-language/mixed-ts-js/src/index.ts +0 -3
  198. package/tests/fixtures/audits/mixed-language/mixed-ts-js/tests/index.sample.ts +0 -5
  199. package/tests/fixtures/audits/mixed-language/mixed-ts-python-jvm/README.md +0 -10
  200. package/tests/fixtures/audits/mixed-language/mixed-ts-python-jvm/build.gradle.kts +0 -4
  201. package/tests/fixtures/audits/mixed-language/mixed-ts-python-jvm/pytests/test_app.py +0 -5
  202. package/tests/fixtures/audits/mixed-language/mixed-ts-python-jvm/src/app.py +0 -2
  203. package/tests/fixtures/audits/mixed-language/mixed-ts-python-jvm/src/helper.js +0 -5
  204. package/tests/fixtures/audits/mixed-language/mixed-ts-python-jvm/src/index.ts +0 -3
  205. package/tests/fixtures/audits/mixed-language/mixed-ts-python-jvm/src/main/java/com/example/Multiplier.java +0 -7
  206. package/tests/fixtures/audits/mixed-language/mixed-ts-python-jvm/src/main/kotlin/com/example/Divider.kt +0 -7
  207. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/corrected-replay/derived/run-integrity-evidence.json +0 -99
  208. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/corrected-replay/producer/implementation-audit.json +0 -1585
  209. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/corrected-replay/producer/implementation-capsule.json +0 -11455
  210. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/00-request.txt +0 -1
  211. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/architecture-context-packet.txt +0 -82
  212. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/behavior-model.txt +0 -104
  213. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/final-report.txt +0 -41
  214. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/implementation-context-packet.txt +0 -114
  215. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/implementation-report.txt +0 -92
  216. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/judge-report.txt +0 -34
  217. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/pseudocode-packet.txt +0 -244
  218. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/request-brief.txt +0 -40
  219. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/test-context-packet.txt +0 -93
  220. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/test-implementation-report.txt +0 -69
  221. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/test-strategy-packet.txt +0 -354
  222. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/artifacts/verification-report.txt +0 -85
  223. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/derived/run-integrity-evidence.json +0 -98
  224. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/producer/architecture-audit.json +0 -1169
  225. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/producer/architecture-request.json +0 -29
  226. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/producer/implementation-audit.json +0 -1494
  227. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/producer/implementation-capsule.json +0 -11364
  228. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/producer/implementation-request.json +0 -33
  229. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/producer/test-request.json +0 -74
  230. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/reports/architecture-context-retrieval-report.txt +0 -58
  231. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/reports/implementation-context-retrieval-report.txt +0 -75
  232. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/reports/test-context-retrieval-report.txt +0 -84
  233. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/run.json +0 -62
  234. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/tool-versions/my-dev-kit-version.txt +0 -1
  235. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/failed-run/tool-versions/orchestrator-version.txt +0 -1
  236. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/manifests/corrected-replay-manifest.json +0 -19
  237. package/tests/fixtures/ecosystem/context-integrity/v0.4.5/manifests/failed-run-manifest.json +0 -56
  238. package/tests/fixtures/fake-adversarial-cli.js +0 -152
  239. package/tests/fixtures/fake-my-dev-kit-cli.js +0 -83
  240. package/tests/fixtures/full-workflow-library/complete-v1.0.0.json +0 -24
  241. package/tests/fixtures/full-workflow-library/future-minor-v1.1.0-additive.json +0 -25
  242. package/tests/fixtures/stage-context-expectations/complete-v1.0.0.json +0 -230
  243. package/tests/fixtures/stage-context-expectations/future-minor-v1.1.0-additive.json +0 -232
  244. package/tests/fixtures/stage-context-expectations/invalid/malformed-json.txt +0 -1
  245. package/tests/fixtures/stage-context-expectations/invalid/non-object-root.json +0 -1
  246. package/tests/fixtures/stage-context-expectations/minimal-v1.0.0.json +0 -18
  247. package/tests/fixtures/upstream-artifacts/invalid/malformed-json.txt +0 -1
  248. package/tests/fixtures/upstream-artifacts/invalid/non-object-root.json +0 -1
  249. package/tests/fixtures/upstream-artifacts/my-dev-kit/1.10.2/context-capsule/complete-v1.0.0.json +0 -504
  250. package/tests/fixtures/upstream-artifacts/my-dev-kit/1.10.2/context-capsule/future-minor-v1.1.0-additive.json +0 -955
  251. package/tests/fixtures/upstream-artifacts/my-dev-kit/1.10.2/retrieval-audit-record/complete-v1.0.0.json +0 -190
  252. package/tests/fixtures/upstream-artifacts/my-dev-kit/1.10.2/retrieval-audit-record/future-minor-v1.1.0-additive.json +0 -327
  253. package/tests/fixtures/upstream-artifacts/my-dev-kit/1.10.4/PROVENANCE.txt +0 -18
  254. package/tests/fixtures/upstream-artifacts/my-dev-kit/1.10.4/context-capsule/complete-v1.0.0.json +0 -556
  255. package/tests/fixtures/upstream-artifacts/my-dev-kit/1.10.4/retrieval-audit-record/complete-v1.0.0.json +0 -238
  256. package/tests/fixtures/upstream-artifacts/my-dev-kit-1.10.2-identity/PROVENANCE.txt +0 -13
  257. package/tests/fixtures/upstream-artifacts/my-dev-kit-1.10.2-identity/context-capsule.json +0 -7874
  258. package/tests/fixtures/upstream-artifacts/my-dev-kit-1.10.2-identity/retrieval-audit-record.json +0 -1076
  259. package/tests/fixtures/upstream-artifacts/my-dev-kit-orchestrator/1.2.1/workflow-instruction-packet/complete-v1.0.0.json +0 -160
  260. package/tests/fixtures/upstream-artifacts/my-dev-kit-orchestrator/1.2.1/workflow-instruction-packet/future-minor-v1.1.0-additive.json +0 -186
  261. package/tests/fixtures/upstream-artifacts/my-dev-kit-orchestrator/1.2.3/run-integrity/PROVENANCE.txt +0 -31
  262. package/tests/fixtures/upstream-artifacts/my-dev-kit-orchestrator/1.2.3/run-integrity/ready-pass-final-pass.json +0 -132
  263. package/tests/fixtures/upstream-artifacts/my-dev-kit-orchestrator/1.2.3/run-integrity/refresh-required-need-context.json +0 -146
@@ -1,31 +1,35 @@
1
1
  import { mkdir, writeFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { spawn } from "node:child_process";
4
- import { parseCommandString } from "./commandLine.js";
5
- import { resolveCommand } from "./resolveCommand.js";
4
+ import { parseCommandString, serializeCommand } from "./commandLine.js";
5
+ import { forceTerminateProcess } from "./processTree.js";
6
+ import { buildResolvedCommandInvocation, resolveCommandInvocation } from "./resolveCommand.js";
6
7
  export { parseCommandString } from "./commandLine.js";
7
8
  export async function runMeasuredCommand(options) {
8
9
  await mkdir(options.outDir, { recursive: true });
9
- const parsed = parseCommandString(options.commandString);
10
- const resolution = options.resolveCommand === false
11
- ? {
12
- originalCommand: parsed.executable,
13
- command: parsed.executable,
10
+ const named = readCommandName(options);
11
+ const commandString = named.commandString;
12
+ const trailingArgs = [...named.args, ...(options.extraArgs ?? [])];
13
+ // Windows .cmd/.bat/.ps1 argument assembly is owned by resolveCommand.ts so
14
+ // that every spawn site (measured commands and managed long-running processes)
15
+ // shares exactly one shim rule.
16
+ const invocation = options.resolveCommand === false
17
+ ? buildResolvedCommandInvocation({
18
+ originalCommand: named.executable,
19
+ command: named.executable,
14
20
  argsPrefix: [],
15
21
  resolutionKind: "direct",
16
- resolvedPath: parsed.executable,
22
+ resolvedPath: named.executable,
17
23
  warnings: []
18
- }
19
- : resolveCommand(parsed.executable, {
24
+ }, trailingArgs)
25
+ : resolveCommandInvocation(named.executable, trailingArgs, {
20
26
  cwd: options.cwd,
21
27
  env: { ...process.env, ...options.env },
22
28
  allowPowerShellShim: options.allowPowerShellShim
23
29
  });
24
- const executable = resolution.command;
25
- const trailingArgs = [...parsed.args, ...(options.extraArgs ?? [])];
26
- const args = resolution.resolutionKind === "windows-cmd-shim" && resolution.resolvedPath
27
- ? [...resolution.argsPrefix, resolution.resolvedPath, ...trailingArgs]
28
- : [...resolution.argsPrefix, ...trailingArgs];
30
+ const resolution = invocation.resolvedCommand;
31
+ const executable = invocation.executable;
32
+ const args = invocation.args;
29
33
  const stdoutPath = path.join(options.outDir, `${options.commandId}.stdout.txt`);
30
34
  const stderrPath = path.join(options.outDir, `${options.commandId}.stderr.txt`);
31
35
  const telemetryPath = path.join(options.outDir, `${options.commandId}.telemetry.json`);
@@ -52,7 +56,7 @@ export async function runMeasuredCommand(options) {
52
56
  const message = error instanceof Error ? error.message : String(error);
53
57
  const measured = {
54
58
  commandId: options.commandId,
55
- commandString: options.commandString,
59
+ commandString,
56
60
  executable,
57
61
  args,
58
62
  cwd: options.cwd,
@@ -89,7 +93,7 @@ export async function runMeasuredCommand(options) {
89
93
  timeout = setTimeout(() => {
90
94
  timedOut = true;
91
95
  spawnError = `Command timed out after ${options.timeoutMs}ms.`;
92
- killProcessTree(child.pid);
96
+ forceTerminateProcess(child.pid);
93
97
  }, options.timeoutMs);
94
98
  }
95
99
  child.on("close", async (exitCode) => {
@@ -102,7 +106,7 @@ export async function runMeasuredCommand(options) {
102
106
  await writeArtifact(stderrPath, stderr);
103
107
  const measured = {
104
108
  commandId: options.commandId,
105
- commandString: options.commandString,
109
+ commandString,
106
110
  executable,
107
111
  args,
108
112
  cwd: options.cwd,
@@ -129,19 +133,23 @@ async function writeArtifact(filePath, value) {
129
133
  await mkdir(path.dirname(filePath), { recursive: true });
130
134
  await writeFile(filePath, value, "utf8");
131
135
  }
132
- function killProcessTree(pid) {
133
- if (!pid) {
134
- return;
135
- }
136
- if (process.platform === "win32") {
137
- const killer = spawn("taskkill", ["/pid", String(pid), "/T", "/F"], { shell: false, stdio: "ignore" });
138
- killer.on("error", () => undefined);
139
- return;
140
- }
141
- try {
142
- process.kill(pid, "SIGTERM");
136
+ /**
137
+ * Normalizes either input form into an executable, its arguments, and a
138
+ * human-readable command line for the telemetry record. The serialized string is
139
+ * for display only -- it is never parsed back into arguments.
140
+ */
141
+ function readCommandName(options) {
142
+ if (options.commandString !== undefined) {
143
+ const parsed = parseCommandString(options.commandString);
144
+ return { executable: parsed.executable, args: parsed.args, commandString: options.commandString };
143
145
  }
144
- catch {
145
- // The child may have exited between timeout scheduling and kill.
146
+ if (options.executable !== undefined) {
147
+ const args = [...(options.args ?? [])];
148
+ return {
149
+ executable: options.executable,
150
+ args,
151
+ commandString: serializeCommand([options.executable, ...args])
152
+ };
146
153
  }
154
+ throw new Error("runMeasuredCommand requires either a commandString or an executable.");
147
155
  }
@@ -0,0 +1,4 @@
1
+ export * from "./packageRoot.js";
2
+ export * from "./labExecutionContext.js";
3
+ export * from "./packageResource.js";
4
+ export * from "./managedProcess.js";
@@ -0,0 +1,22 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ import { discoverPackageRoot } from "./packageRoot.js";
4
+ const DEFAULT_WORKSPACE_DIR_NAME = ".my-dev-kit-lab";
5
+ export function createLabExecutionContext(options = {}) {
6
+ const invocationCwd = path.resolve(options.invocationCwd ?? process.cwd());
7
+ const packageRoot = path.resolve(options.packageRoot ?? discoverPackageRoot());
8
+ const workspaceRoot = resolveWorkspaceRoot(options.workspaceRoot, invocationCwd);
9
+ const resourceRoot = packageRoot;
10
+ return Object.freeze({
11
+ invocationCwd,
12
+ packageRoot,
13
+ workspaceRoot,
14
+ resourceRoot
15
+ });
16
+ }
17
+ function resolveWorkspaceRoot(explicitWorkspaceRoot, invocationCwd) {
18
+ if (!explicitWorkspaceRoot) {
19
+ return path.join(os.homedir(), DEFAULT_WORKSPACE_DIR_NAME);
20
+ }
21
+ return path.resolve(path.isAbsolute(explicitWorkspaceRoot) ? explicitWorkspaceRoot : path.resolve(invocationCwd, explicitWorkspaceRoot));
22
+ }
@@ -0,0 +1,380 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createWriteStream } from "node:fs";
3
+ import { mkdir, stat } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { forceTerminateProcess, requestProcessTermination } from "../core/processTree.js";
6
+ import { resolveCommandInvocation } from "../core/resolveCommand.js";
7
+ /**
8
+ * Long-running child process lifecycle.
9
+ *
10
+ * This owner is deliberately NOT a replacement for `runMeasuredCommand`.
11
+ * `runMeasuredCommand` owns bounded commands that are expected to terminate and
12
+ * whose full output is a measurement artifact. `startManagedProcess` owns
13
+ * commands that are expected to stay alive until explicitly stopped (local demo
14
+ * servers and viewers), so it returns a handle instead of a completed result and
15
+ * streams its logs to disk instead of accumulating them in memory.
16
+ *
17
+ * It reuses the canonical command resolver and the shared process-termination
18
+ * primitive; it does not implement a second command resolver and never uses a
19
+ * shell.
20
+ */
21
+ /** Bounded in-memory diagnostic tail retained per stream. Disk logs stay complete. */
22
+ export const MANAGED_PROCESS_CAPTURE_LIMIT_BYTES = 256 * 1024;
23
+ export const MANAGED_PROCESS_DEFAULT_GRACE_PERIOD_MS = 1500;
24
+ export const MANAGED_PROCESS_DEFAULT_READINESS_INTERVAL_MS = 100;
25
+ export const MANAGED_PROCESS_DEFAULT_REQUEST_TIMEOUT_MS = 1000;
26
+ export const MANAGED_PROCESS_DEFAULT_ACCEPTED_STATUS_MIN = 200;
27
+ export const MANAGED_PROCESS_DEFAULT_ACCEPTED_STATUS_MAX = 399;
28
+ /** Hostnames a readiness probe is permitted to contact. Loopback only, by design. */
29
+ const ALLOWED_READINESS_HOSTS = new Set(["localhost", "127.0.0.1", "::1"]);
30
+ /**
31
+ * Thrown by `startManagedProcess` when the spec cannot produce a process at all.
32
+ *
33
+ * Start-time problems throw because there is no handle to return. Runtime states
34
+ * a caller is expected to handle -- readiness timeout, the child exiting before
35
+ * readiness, an invalid probe -- are returned as structured results instead.
36
+ */
37
+ export class ManagedProcessStartError extends Error {
38
+ constructor(message) {
39
+ super(message);
40
+ this.name = "ManagedProcessStartError";
41
+ }
42
+ }
43
+ export async function startManagedProcess(spec) {
44
+ const id = validateProcessId(spec.id);
45
+ const executable = typeof spec.executable === "string" ? spec.executable.trim() : "";
46
+ if (executable.length === 0) {
47
+ throw new ManagedProcessStartError("Managed process executable must be a non-empty string.");
48
+ }
49
+ if (typeof spec.cwd !== "string" || spec.cwd.trim().length === 0) {
50
+ throw new ManagedProcessStartError("Managed process cwd must be a non-empty path.");
51
+ }
52
+ if (typeof spec.outDir !== "string" || spec.outDir.trim().length === 0) {
53
+ throw new ManagedProcessStartError("Managed process outDir must be a non-empty path.");
54
+ }
55
+ const cwd = path.resolve(spec.cwd);
56
+ try {
57
+ if (!(await stat(cwd)).isDirectory()) {
58
+ throw new Error("not a directory");
59
+ }
60
+ }
61
+ catch {
62
+ throw new ManagedProcessStartError(`Managed process cwd is not an existing directory: ${cwd}`);
63
+ }
64
+ const outDir = path.resolve(spec.outDir);
65
+ try {
66
+ await mkdir(outDir, { recursive: true });
67
+ }
68
+ catch (error) {
69
+ throw new ManagedProcessStartError(`Managed process outDir could not be created: ${outDir} (${messageOf(error)})`);
70
+ }
71
+ const env = { ...process.env, ...spec.env };
72
+ const invocation = resolveCommandInvocation(executable, spec.args ?? [], {
73
+ cwd,
74
+ env,
75
+ allowPowerShellShim: spec.allowPowerShellShim
76
+ });
77
+ if (invocation.resolvedCommand.resolutionKind === "unavailable") {
78
+ const warnings = invocation.resolvedCommand.warnings;
79
+ throw new ManagedProcessStartError(`Managed process command is unavailable: ${executable}${warnings.length > 0 ? ` (${warnings.join("; ")})` : ""}`);
80
+ }
81
+ const stdoutPath = path.join(outDir, `${id}.stdout.txt`);
82
+ const stderrPath = path.join(outDir, `${id}.stderr.txt`);
83
+ // stdio ["ignore", "pipe", "pipe"]: stdin is deliberately not held open.
84
+ let child;
85
+ try {
86
+ child = spawn(invocation.executable, invocation.args, {
87
+ cwd,
88
+ env,
89
+ shell: false,
90
+ stdio: ["ignore", "pipe", "pipe"]
91
+ });
92
+ }
93
+ catch (error) {
94
+ throw new ManagedProcessStartError(`Managed process failed to spawn: ${executable} (${messageOf(error)})`);
95
+ }
96
+ let exited = false;
97
+ let exitResult;
98
+ let spawnError;
99
+ const exitWaiters = [];
100
+ // Attached before any early return so a spawn failure can never surface as an
101
+ // unhandled 'error' event.
102
+ child.on("error", (error) => {
103
+ spawnError = error.message;
104
+ });
105
+ if (child.pid === undefined) {
106
+ throw new ManagedProcessStartError(`Managed process did not start and reported no pid: ${executable}${spawnError ? ` (${spawnError})` : ""}`);
107
+ }
108
+ const pid = child.pid;
109
+ const stdoutStream = createWriteStream(stdoutPath, { flags: "w" });
110
+ const stderrStream = createWriteStream(stderrPath, { flags: "w" });
111
+ // Log-file write errors must not crash the host process; they are recorded and
112
+ // surfaced through the exit result instead.
113
+ stdoutStream.on("error", (error) => {
114
+ spawnError = spawnError ?? `stdout log write failed: ${messageOf(error)}`;
115
+ });
116
+ stderrStream.on("error", (error) => {
117
+ spawnError = spawnError ?? `stderr log write failed: ${messageOf(error)}`;
118
+ });
119
+ const stdoutTail = createBoundedTail();
120
+ const stderrTail = createBoundedTail();
121
+ attachStream(child.stdout, stdoutStream, stdoutTail);
122
+ attachStream(child.stderr, stderrStream, stderrTail);
123
+ child.on("close", (code, signal) => {
124
+ exited = true;
125
+ exitResult = {
126
+ exitCode: code,
127
+ signal: signal ?? null,
128
+ ...(spawnError ? { error: spawnError } : {})
129
+ };
130
+ stdoutStream.end();
131
+ stderrStream.end();
132
+ const finalResult = exitResult;
133
+ for (const waiter of exitWaiters.splice(0)) {
134
+ waiter(finalResult);
135
+ }
136
+ });
137
+ function waitForExit() {
138
+ if (exited && exitResult) {
139
+ return Promise.resolve(exitResult);
140
+ }
141
+ return new Promise((resolve) => {
142
+ exitWaiters.push(resolve);
143
+ });
144
+ }
145
+ async function waitForExitWithin(timeoutMs) {
146
+ if (exited && exitResult) {
147
+ return exitResult;
148
+ }
149
+ let timer;
150
+ const timedOut = new Promise((resolve) => {
151
+ timer = setTimeout(() => resolve(undefined), timeoutMs);
152
+ });
153
+ try {
154
+ return await Promise.race([waitForExit(), timedOut]);
155
+ }
156
+ finally {
157
+ if (timer) {
158
+ clearTimeout(timer);
159
+ }
160
+ }
161
+ }
162
+ let stopInFlight;
163
+ async function stop(options) {
164
+ if (exited && exitResult) {
165
+ return { status: "already-exited", exit: exitResult };
166
+ }
167
+ if (stopInFlight) {
168
+ return stopInFlight;
169
+ }
170
+ const gracePeriodMs = options?.gracePeriodMs ?? MANAGED_PROCESS_DEFAULT_GRACE_PERIOD_MS;
171
+ const pending = (async () => {
172
+ requestProcessTermination(pid);
173
+ const gracefulExit = await waitForExitWithin(Math.max(0, gracePeriodMs));
174
+ if (gracefulExit) {
175
+ return { status: "stopped", exit: gracefulExit };
176
+ }
177
+ // Escalation only: SIGKILL here is safe because the graceful SIGTERM grace
178
+ // period has already elapsed. On Windows this is taskkill /T /F.
179
+ forceTerminateProcess(pid, { posixSignal: "SIGKILL" });
180
+ return { status: "forced", exit: await waitForExit() };
181
+ })();
182
+ stopInFlight = pending;
183
+ try {
184
+ return await pending;
185
+ }
186
+ finally {
187
+ stopInFlight = undefined;
188
+ }
189
+ }
190
+ async function waitForReadiness(probe) {
191
+ const startedAt = Date.now();
192
+ const validation = validateReadinessProbe(probe);
193
+ if (!validation.ok) {
194
+ return {
195
+ status: "probe-failed",
196
+ url: typeof probe?.url === "string" ? probe.url : "",
197
+ attempts: 0,
198
+ elapsedMs: Date.now() - startedAt,
199
+ error: validation.error
200
+ };
201
+ }
202
+ const { url, acceptedStatusMin, acceptedStatusMax, intervalMs } = validation;
203
+ let attempts = 0;
204
+ let lastStatusCode;
205
+ let lastError;
206
+ for (;;) {
207
+ if (exited) {
208
+ return {
209
+ status: "process-exited",
210
+ url: probe.url,
211
+ attempts,
212
+ elapsedMs: Date.now() - startedAt,
213
+ ...(lastStatusCode !== undefined ? { lastStatusCode } : {}),
214
+ error: `Managed process "${id}" exited before readiness was reached.`
215
+ };
216
+ }
217
+ const remainingMs = probe.timeoutMs - (Date.now() - startedAt);
218
+ if (remainingMs <= 0) {
219
+ return {
220
+ status: "timeout",
221
+ url: probe.url,
222
+ attempts,
223
+ elapsedMs: Date.now() - startedAt,
224
+ ...(lastStatusCode !== undefined ? { lastStatusCode } : {}),
225
+ ...(lastError ? { error: lastError } : {})
226
+ };
227
+ }
228
+ attempts += 1;
229
+ const requestTimeoutMs = Math.max(1, Math.min(probe.requestTimeoutMs ?? MANAGED_PROCESS_DEFAULT_REQUEST_TIMEOUT_MS, remainingMs));
230
+ try {
231
+ const response = await fetch(url, {
232
+ // "manual" keeps a 3xx observable as a 3xx: a redirect is a legitimate
233
+ // sign of a live server and is accepted by the default 200..399 range.
234
+ redirect: "manual",
235
+ signal: AbortSignal.timeout(requestTimeoutMs)
236
+ });
237
+ lastStatusCode = response.status;
238
+ await response.body?.cancel().catch(() => undefined);
239
+ if (response.status >= acceptedStatusMin && response.status <= acceptedStatusMax) {
240
+ return {
241
+ status: "ready",
242
+ url: probe.url,
243
+ attempts,
244
+ elapsedMs: Date.now() - startedAt,
245
+ lastStatusCode: response.status
246
+ };
247
+ }
248
+ lastError = `Readiness probe received status ${response.status}.`;
249
+ }
250
+ catch (error) {
251
+ // Connection refused / socket reset / per-attempt abort are all expected
252
+ // while a local server is still starting, so they are retried.
253
+ lastError = messageOf(error);
254
+ }
255
+ const remainingAfterAttempt = probe.timeoutMs - (Date.now() - startedAt);
256
+ if (remainingAfterAttempt <= 0) {
257
+ continue;
258
+ }
259
+ await delay(Math.min(intervalMs, remainingAfterAttempt));
260
+ }
261
+ }
262
+ return Object.freeze({
263
+ id,
264
+ pid,
265
+ executable: invocation.executable,
266
+ args: Object.freeze([...invocation.args]),
267
+ cwd,
268
+ stdoutPath,
269
+ stderrPath,
270
+ resolvedCommand: invocation.resolvedCommand,
271
+ waitForReadiness,
272
+ waitForExit,
273
+ stop,
274
+ readRecentOutput: () => ({
275
+ stdout: stdoutTail.read(),
276
+ stderr: stderrTail.read(),
277
+ truncated: stdoutTail.truncated() || stderrTail.truncated()
278
+ })
279
+ });
280
+ }
281
+ /**
282
+ * Readiness is a bounded local-server liveness check, not a general network
283
+ * fetch primitive: only http/https to a loopback host is permitted.
284
+ */
285
+ function validateReadinessProbe(probe) {
286
+ if (!probe || probe.kind !== "http") {
287
+ return { ok: false, error: 'Readiness probe kind must be "http".' };
288
+ }
289
+ if (typeof probe.url !== "string" || probe.url.trim().length === 0) {
290
+ return { ok: false, error: "Readiness probe url must be a non-empty string." };
291
+ }
292
+ if (!Number.isFinite(probe.timeoutMs) || probe.timeoutMs <= 0) {
293
+ return { ok: false, error: "Readiness probe timeoutMs must be a positive number." };
294
+ }
295
+ let parsed;
296
+ try {
297
+ parsed = new URL(probe.url);
298
+ }
299
+ catch {
300
+ return { ok: false, error: `Readiness probe url is not a valid URL: ${probe.url}` };
301
+ }
302
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
303
+ return {
304
+ ok: false,
305
+ error: `Readiness probe protocol must be http: or https:, received ${parsed.protocol}`
306
+ };
307
+ }
308
+ // URL parsing renders an IPv6 host as "[::1]"; compare on the unbracketed form.
309
+ const hostname = parsed.hostname.replace(/^\[/, "").replace(/\]$/, "").toLowerCase();
310
+ if (!ALLOWED_READINESS_HOSTS.has(hostname)) {
311
+ return {
312
+ ok: false,
313
+ error: `Readiness probe host must be loopback (localhost, 127.0.0.1, ::1), received ${parsed.hostname}`
314
+ };
315
+ }
316
+ const acceptedStatusMin = probe.acceptedStatusMin ?? MANAGED_PROCESS_DEFAULT_ACCEPTED_STATUS_MIN;
317
+ const acceptedStatusMax = probe.acceptedStatusMax ?? MANAGED_PROCESS_DEFAULT_ACCEPTED_STATUS_MAX;
318
+ if (!Number.isFinite(acceptedStatusMin) || !Number.isFinite(acceptedStatusMax)) {
319
+ return { ok: false, error: "Readiness probe accepted status range must be numeric." };
320
+ }
321
+ if (acceptedStatusMin > acceptedStatusMax) {
322
+ return {
323
+ ok: false,
324
+ error: `Readiness probe accepted status range is inverted: ${acceptedStatusMin}..${acceptedStatusMax}`
325
+ };
326
+ }
327
+ const intervalMs = probe.intervalMs ?? MANAGED_PROCESS_DEFAULT_READINESS_INTERVAL_MS;
328
+ if (!Number.isFinite(intervalMs) || intervalMs < 0) {
329
+ return { ok: false, error: "Readiness probe intervalMs must be a non-negative number." };
330
+ }
331
+ return { ok: true, url: parsed.href, acceptedStatusMin, acceptedStatusMax, intervalMs };
332
+ }
333
+ function validateProcessId(id) {
334
+ const trimmed = typeof id === "string" ? id.trim() : "";
335
+ if (trimmed.length === 0) {
336
+ throw new ManagedProcessStartError("Managed process id must be a non-empty string.");
337
+ }
338
+ // The id becomes a log file name, so it must stay a single path segment.
339
+ if (trimmed !== path.basename(trimmed) || trimmed === "." || trimmed === "..") {
340
+ throw new ManagedProcessStartError(`Managed process id must be a single path segment without separators: ${id}`);
341
+ }
342
+ return trimmed;
343
+ }
344
+ function createBoundedTail(limitBytes = MANAGED_PROCESS_CAPTURE_LIMIT_BYTES) {
345
+ let buffer = Buffer.alloc(0);
346
+ let dropped = false;
347
+ return {
348
+ push(chunk) {
349
+ buffer = Buffer.concat([buffer, chunk]);
350
+ if (buffer.length > limitBytes) {
351
+ buffer = buffer.subarray(buffer.length - limitBytes);
352
+ dropped = true;
353
+ }
354
+ },
355
+ read() {
356
+ return buffer.toString("utf8");
357
+ },
358
+ truncated() {
359
+ return dropped;
360
+ }
361
+ };
362
+ }
363
+ function attachStream(source, sink, tail) {
364
+ source.on("data", (chunk) => {
365
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk), "utf8");
366
+ tail.push(buffer);
367
+ // Written as it arrives: a managed process may live for minutes and its log
368
+ // must be readable while it runs.
369
+ sink.write(buffer);
370
+ });
371
+ source.on("error", () => undefined);
372
+ }
373
+ function delay(ms) {
374
+ return new Promise((resolve) => {
375
+ setTimeout(resolve, ms);
376
+ });
377
+ }
378
+ function messageOf(error) {
379
+ return error instanceof Error ? error.message : String(error);
380
+ }
@@ -0,0 +1,22 @@
1
+ import { existsSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { resolveWithinRoot } from "../core/pathSafety.js";
4
+ /**
5
+ * Safely resolves a package-relative path (e.g. "benchmarks/contracts/foo.json")
6
+ * against resourceRoot. Rejects empty/absolute/traversal inputs and missing files
7
+ * so command owners never depend on process.cwd() to find bundled resources.
8
+ */
9
+ export function resolvePackageResource(contextOrResourceRoot, resourceRelativePath) {
10
+ const resourceRoot = typeof contextOrResourceRoot === "string" ? contextOrResourceRoot : contextOrResourceRoot.resourceRoot;
11
+ if (!resourceRelativePath || resourceRelativePath.trim().length === 0) {
12
+ throw new Error("Package resource path must not be empty.");
13
+ }
14
+ if (path.isAbsolute(resourceRelativePath)) {
15
+ throw new Error(`Package resource path must be relative, not absolute: ${resourceRelativePath}`);
16
+ }
17
+ const resolvedPath = resolveWithinRoot(resourceRoot, resourceRelativePath);
18
+ if (!existsSync(resolvedPath)) {
19
+ throw new Error(`Required package resource not found: ${resourceRelativePath} (resolved: ${resolvedPath})`);
20
+ }
21
+ return resolvedPath;
22
+ }
@@ -0,0 +1,37 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ const PACKAGE_NAME = "@dailephd/my-dev-kit-lab";
5
+ /**
6
+ * Walks upward from the executing module's location (not process.cwd()) until it
7
+ * finds the package.json that declares PACKAGE_NAME, so this resolves correctly
8
+ * from a source checkout, compiled dist output, or an installed node_modules copy.
9
+ */
10
+ export function discoverPackageRoot(startDir) {
11
+ const start = path.resolve(startDir ?? path.dirname(fileURLToPath(import.meta.url)));
12
+ let current = start;
13
+ while (true) {
14
+ const candidatePackageJson = path.join(current, "package.json");
15
+ if (existsSync(candidatePackageJson)) {
16
+ const name = readPackageName(candidatePackageJson);
17
+ if (name === PACKAGE_NAME) {
18
+ return current;
19
+ }
20
+ }
21
+ const parent = path.dirname(current);
22
+ if (parent === current) {
23
+ break;
24
+ }
25
+ current = parent;
26
+ }
27
+ throw new Error(`Unable to locate the "${PACKAGE_NAME}" package root by walking up from ${start}.`);
28
+ }
29
+ function readPackageName(packageJsonPath) {
30
+ try {
31
+ const parsed = JSON.parse(readFileSync(packageJsonPath, "utf8"));
32
+ return typeof parsed.name === "string" ? parsed.name : undefined;
33
+ }
34
+ catch {
35
+ return undefined;
36
+ }
37
+ }