@msn-control/liftoff 0.10.4 → 0.11.1

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 (643) hide show
  1. package/DEVELOPER.md +220 -77
  2. package/README.md +28 -28
  3. package/assets/supported-stack.json +3 -136
  4. package/dist/adapters/azure/governance-assessment.d.ts +2 -0
  5. package/dist/adapters/azure/governance-assessment.js +21 -0
  6. package/dist/adapters/azure/governance-assessment.js.map +1 -0
  7. package/dist/adapters/filesystem/atomic-write.d.ts +2 -0
  8. package/dist/adapters/filesystem/atomic-write.js +74 -0
  9. package/dist/adapters/filesystem/atomic-write.js.map +1 -0
  10. package/dist/adapters/filesystem/errors.d.ts +2 -0
  11. package/dist/adapters/filesystem/errors.js +8 -0
  12. package/dist/adapters/filesystem/errors.js.map +1 -0
  13. package/dist/adapters/filesystem/manifest-file.d.ts +1 -0
  14. package/dist/adapters/filesystem/manifest-file.js +21 -0
  15. package/dist/adapters/filesystem/manifest-file.js.map +1 -0
  16. package/dist/adapters/filesystem/project-config.d.ts +4 -0
  17. package/dist/adapters/filesystem/project-config.js +157 -0
  18. package/dist/adapters/filesystem/project-config.js.map +1 -0
  19. package/dist/adapters/filesystem/project-discovery.d.ts +2 -0
  20. package/dist/adapters/filesystem/project-discovery.js +30 -0
  21. package/dist/adapters/filesystem/project-discovery.js.map +1 -0
  22. package/dist/adapters/filesystem/project-files.d.ts +6 -0
  23. package/dist/adapters/filesystem/project-files.js +68 -0
  24. package/dist/adapters/filesystem/project-files.js.map +1 -0
  25. package/dist/adapters/filesystem/project-lock.d.ts +8 -0
  26. package/dist/adapters/filesystem/project-lock.js +164 -0
  27. package/dist/adapters/filesystem/project-lock.js.map +1 -0
  28. package/dist/adapters/filesystem/project-paths.d.ts +2 -0
  29. package/dist/adapters/filesystem/project-paths.js +60 -0
  30. package/dist/adapters/filesystem/project-paths.js.map +1 -0
  31. package/dist/adapters/filesystem/project-transaction.d.ts +23 -0
  32. package/dist/adapters/filesystem/project-transaction.js +158 -0
  33. package/dist/adapters/filesystem/project-transaction.js.map +1 -0
  34. package/dist/adapters/git/governance-assessment.d.ts +22 -0
  35. package/dist/adapters/git/governance-assessment.js +161 -0
  36. package/dist/adapters/git/governance-assessment.js.map +1 -0
  37. package/dist/adapters/github/governance-assessment.d.ts +2 -0
  38. package/dist/adapters/github/governance-assessment.js +18 -0
  39. package/dist/adapters/github/governance-assessment.js.map +1 -0
  40. package/dist/adapters/packaged-assets/package-root.d.ts +3 -0
  41. package/dist/adapters/packaged-assets/package-root.js +21 -0
  42. package/dist/adapters/packaged-assets/package-root.js.map +1 -0
  43. package/dist/adapters/packaged-assets/supported-stack.d.ts +4 -0
  44. package/dist/adapters/packaged-assets/supported-stack.js +18 -0
  45. package/dist/adapters/packaged-assets/supported-stack.js.map +1 -0
  46. package/dist/adapters/packaged-assets/template-assets.d.ts +23 -0
  47. package/dist/adapters/packaged-assets/template-assets.js +36 -0
  48. package/dist/adapters/packaged-assets/template-assets.js.map +1 -0
  49. package/dist/adapters/process/shell-command.d.ts +5 -0
  50. package/dist/adapters/process/shell-command.js +21 -0
  51. package/dist/adapters/process/shell-command.js.map +1 -0
  52. package/dist/application/context.d.ts +20 -0
  53. package/dist/application/context.js +2 -0
  54. package/dist/application/context.js.map +1 -0
  55. package/dist/application/diagnose/doctor.d.ts +21 -0
  56. package/dist/application/diagnose/doctor.js +586 -0
  57. package/dist/application/diagnose/doctor.js.map +1 -0
  58. package/dist/application/diagnose/generated-project.d.ts +1 -0
  59. package/dist/application/diagnose/generated-project.js +105 -0
  60. package/dist/application/diagnose/generated-project.js.map +1 -0
  61. package/dist/application/diagnose/validate.d.ts +6 -0
  62. package/dist/application/diagnose/validate.js +27 -0
  63. package/dist/application/diagnose/validate.js.map +1 -0
  64. package/dist/application/initialize/fixture.d.ts +2 -0
  65. package/dist/application/initialize/fixture.js +39 -0
  66. package/dist/application/initialize/fixture.js.map +1 -0
  67. package/dist/application/initialize/inputs.d.ts +3 -0
  68. package/dist/application/initialize/inputs.js +35 -0
  69. package/dist/application/initialize/inputs.js.map +1 -0
  70. package/dist/application/initialize/use-case.d.ts +23 -0
  71. package/dist/application/initialize/use-case.js +360 -0
  72. package/dist/application/initialize/use-case.js.map +1 -0
  73. package/dist/application/migrate/use-case.d.ts +7 -0
  74. package/dist/application/migrate/use-case.js +378 -0
  75. package/dist/application/migrate/use-case.js.map +1 -0
  76. package/dist/application/project/catalog.d.ts +46 -0
  77. package/dist/application/project/catalog.js +12 -0
  78. package/dist/application/project/catalog.js.map +1 -0
  79. package/dist/application/project/manifest.d.ts +2 -0
  80. package/dist/application/project/manifest.js +29 -0
  81. package/dist/application/project/manifest.js.map +1 -0
  82. package/dist/application/project/planning.d.ts +6 -0
  83. package/dist/application/project/planning.js +12 -0
  84. package/dist/application/project/planning.js.map +1 -0
  85. package/dist/application/update/planning.d.ts +40 -0
  86. package/dist/application/update/planning.js +293 -0
  87. package/dist/application/update/planning.js.map +1 -0
  88. package/dist/application/update/reporting.d.ts +42 -0
  89. package/dist/application/update/reporting.js +325 -0
  90. package/dist/application/update/reporting.js.map +1 -0
  91. package/dist/application/update/use-case.d.ts +8 -0
  92. package/dist/application/update/use-case.js +600 -0
  93. package/dist/application/update/use-case.js.map +1 -0
  94. package/dist/application/upgrade/use-case.d.ts +6 -0
  95. package/dist/application/upgrade/use-case.js +82 -0
  96. package/dist/application/upgrade/use-case.js.map +1 -0
  97. package/dist/args.d.ts +5 -65
  98. package/dist/args.js +4 -472
  99. package/dist/args.js.map +1 -1
  100. package/dist/artifact-lifecycle.d.ts +1 -21
  101. package/dist/artifact-lifecycle.js +1 -75
  102. package/dist/artifact-lifecycle.js.map +1 -1
  103. package/dist/catalogs.d.ts +1 -50
  104. package/dist/catalogs.js +1 -472
  105. package/dist/catalogs.js.map +1 -1
  106. package/dist/cli/args/contracts.d.ts +51 -0
  107. package/dist/cli/args/contracts.js +2 -0
  108. package/dist/cli/args/contracts.js.map +1 -0
  109. package/dist/cli/args/definitions.d.ts +6 -0
  110. package/dist/cli/args/definitions.js +187 -0
  111. package/dist/cli/args/definitions.js.map +1 -0
  112. package/dist/cli/args/help.d.ts +5 -0
  113. package/dist/cli/args/help.js +130 -0
  114. package/dist/cli/args/help.js.map +1 -0
  115. package/dist/cli/args/parser.d.ts +5 -0
  116. package/dist/cli/args/parser.js +145 -0
  117. package/dist/cli/args/parser.js.map +1 -0
  118. package/dist/cli/args/readers.d.ts +4 -0
  119. package/dist/cli/args/readers.js +19 -0
  120. package/dist/cli/args/readers.js.map +1 -0
  121. package/dist/cli/commands/diagnose.d.ts +4 -0
  122. package/dist/cli/commands/diagnose.js +12 -0
  123. package/dist/cli/commands/diagnose.js.map +1 -0
  124. package/dist/cli/commands/dispatch.d.ts +3 -0
  125. package/dist/cli/commands/dispatch.js +94 -0
  126. package/dist/cli/commands/dispatch.js.map +1 -0
  127. package/dist/cli/commands/help.d.ts +3 -0
  128. package/dist/cli/commands/help.js +32 -0
  129. package/dist/cli/commands/help.js.map +1 -0
  130. package/dist/cli/commands/helpers.d.ts +8 -0
  131. package/dist/cli/commands/helpers.js +97 -0
  132. package/dist/cli/commands/helpers.js.map +1 -0
  133. package/dist/cli/commands/initialize.d.ts +3 -0
  134. package/dist/cli/commands/initialize.js +7 -0
  135. package/dist/cli/commands/initialize.js.map +1 -0
  136. package/dist/cli/commands/migrate.d.ts +3 -0
  137. package/dist/cli/commands/migrate.js +8 -0
  138. package/dist/cli/commands/migrate.js.map +1 -0
  139. package/dist/cli/commands/plan.d.ts +3 -0
  140. package/dist/cli/commands/plan.js +34 -0
  141. package/dist/cli/commands/plan.js.map +1 -0
  142. package/dist/cli/commands/reference.d.ts +5 -0
  143. package/dist/cli/commands/reference.js +43 -0
  144. package/dist/cli/commands/reference.js.map +1 -0
  145. package/dist/cli/commands/update.d.ts +3 -0
  146. package/dist/cli/commands/update.js +9 -0
  147. package/dist/cli/commands/update.js.map +1 -0
  148. package/dist/cli/commands/upgrade.d.ts +3 -0
  149. package/dist/cli/commands/upgrade.js +7 -0
  150. package/dist/cli/commands/upgrade.js.map +1 -0
  151. package/dist/cli/context.d.ts +1 -0
  152. package/dist/cli/context.js +2 -0
  153. package/dist/cli/context.js.map +1 -0
  154. package/dist/cli/project-options.d.ts +3 -0
  155. package/dist/cli/project-options.js +41 -0
  156. package/dist/cli/project-options.js.map +1 -0
  157. package/dist/cli.d.ts +3 -3
  158. package/dist/cli.js +2 -2
  159. package/dist/cli.js.map +1 -1
  160. package/dist/commands.d.ts +4 -35
  161. package/dist/commands.js +3 -2969
  162. package/dist/commands.js.map +1 -1
  163. package/dist/container-validation.d.ts +1 -1
  164. package/dist/domain/governance/activation/approvals.d.ts +351 -0
  165. package/dist/domain/governance/activation/approvals.js +528 -0
  166. package/dist/domain/governance/activation/approvals.js.map +1 -0
  167. package/dist/domain/governance/activation/canonical-json.d.ts +4 -0
  168. package/dist/domain/governance/activation/canonical-json.js +39 -0
  169. package/dist/domain/governance/activation/canonical-json.js.map +1 -0
  170. package/dist/domain/governance/activation/capabilities.d.ts +7 -0
  171. package/dist/domain/governance/activation/capabilities.js +33 -0
  172. package/dist/domain/governance/activation/capabilities.js.map +1 -0
  173. package/dist/domain/governance/activation/evidence.d.ts +63 -0
  174. package/dist/domain/governance/activation/evidence.js +444 -0
  175. package/dist/domain/governance/activation/evidence.js.map +1 -0
  176. package/dist/domain/governance/activation/graph.d.ts +397 -0
  177. package/dist/domain/governance/activation/graph.js +441 -0
  178. package/dist/domain/governance/activation/graph.js.map +1 -0
  179. package/dist/domain/governance/activation/inputs.d.ts +25 -0
  180. package/dist/domain/governance/activation/inputs.js +63 -0
  181. package/dist/domain/governance/activation/inputs.js.map +1 -0
  182. package/dist/domain/governance/activation/operations.d.ts +19 -0
  183. package/dist/domain/governance/activation/operations.js +181 -0
  184. package/dist/domain/governance/activation/operations.js.map +1 -0
  185. package/dist/domain/governance/activation/readiness.d.ts +27 -0
  186. package/dist/domain/governance/activation/readiness.js +269 -0
  187. package/dist/domain/governance/activation/readiness.js.map +1 -0
  188. package/dist/domain/governance/activation/types.d.ts +399 -0
  189. package/dist/domain/governance/activation/types.js +109 -0
  190. package/dist/domain/governance/activation/types.js.map +1 -0
  191. package/dist/domain/governance/activation/validators.d.ts +22 -0
  192. package/dist/domain/governance/activation/validators.js +1347 -0
  193. package/dist/domain/governance/activation/validators.js.map +1 -0
  194. package/dist/domain/governance/assessment/catalog.d.ts +14 -0
  195. package/dist/domain/governance/assessment/catalog.js +105 -0
  196. package/dist/domain/governance/assessment/catalog.js.map +1 -0
  197. package/dist/domain/governance/assessment/errors.d.ts +10 -0
  198. package/dist/domain/governance/assessment/errors.js +22 -0
  199. package/dist/domain/governance/assessment/errors.js.map +1 -0
  200. package/dist/domain/governance/assessment/index.d.ts +8 -0
  201. package/dist/domain/governance/assessment/index.js +9 -0
  202. package/dist/domain/governance/assessment/index.js.map +1 -0
  203. package/dist/domain/governance/assessment/live-normalize.d.ts +19 -0
  204. package/dist/domain/governance/assessment/live-normalize.js +403 -0
  205. package/dist/domain/governance/assessment/live-normalize.js.map +1 -0
  206. package/dist/domain/governance/assessment/predicates.d.ts +36 -0
  207. package/dist/domain/governance/assessment/predicates.js +545 -0
  208. package/dist/domain/governance/assessment/predicates.js.map +1 -0
  209. package/dist/domain/governance/assessment/report.d.ts +23 -0
  210. package/dist/domain/governance/assessment/report.js +266 -0
  211. package/dist/domain/governance/assessment/report.js.map +1 -0
  212. package/dist/domain/governance/assessment/sanitize.d.ts +8 -0
  213. package/dist/domain/governance/assessment/sanitize.js +60 -0
  214. package/dist/domain/governance/assessment/sanitize.js.map +1 -0
  215. package/dist/domain/governance/assessment/types.d.ts +174 -0
  216. package/dist/domain/governance/assessment/types.js +13 -0
  217. package/dist/domain/governance/assessment/types.js.map +1 -0
  218. package/dist/domain/governance/assessment/yaml.d.ts +6 -0
  219. package/dist/domain/governance/assessment/yaml.js +30 -0
  220. package/dist/domain/governance/assessment/yaml.js.map +1 -0
  221. package/dist/domain/governance/policy/identity.d.ts +64 -0
  222. package/dist/domain/governance/policy/identity.js +112 -0
  223. package/dist/domain/governance/policy/identity.js.map +1 -0
  224. package/dist/domain/migration/dependencies.d.ts +7 -0
  225. package/dist/domain/migration/dependencies.js +221 -0
  226. package/dist/domain/migration/dependencies.js.map +1 -0
  227. package/dist/domain/migration/inventory.d.ts +3 -0
  228. package/dist/domain/migration/inventory.js +15 -0
  229. package/dist/domain/migration/inventory.js.map +1 -0
  230. package/dist/domain/project/artifact-lifecycle.d.ts +69 -0
  231. package/dist/domain/project/artifact-lifecycle.js +137 -0
  232. package/dist/domain/project/artifact-lifecycle.js.map +1 -0
  233. package/dist/domain/project/catalog.d.ts +48 -0
  234. package/dist/domain/project/catalog.js +507 -0
  235. package/dist/domain/project/catalog.js.map +1 -0
  236. package/dist/domain/project/contracts.d.ts +247 -0
  237. package/dist/domain/project/contracts.js +2 -0
  238. package/dist/domain/project/contracts.js.map +1 -0
  239. package/dist/domain/project/errors.d.ts +3 -0
  240. package/dist/domain/project/errors.js +7 -0
  241. package/dist/domain/project/errors.js.map +1 -0
  242. package/dist/domain/project/infrastructure-layout.d.ts +26 -0
  243. package/dist/domain/project/infrastructure-layout.js +124 -0
  244. package/dist/domain/project/infrastructure-layout.js.map +1 -0
  245. package/dist/domain/project/inputs.d.ts +21 -0
  246. package/dist/domain/project/inputs.js +41 -0
  247. package/dist/domain/project/inputs.js.map +1 -0
  248. package/dist/domain/project/manifest/artifacts.d.ts +8 -0
  249. package/dist/domain/project/manifest/artifacts.js +150 -0
  250. package/dist/domain/project/manifest/artifacts.js.map +1 -0
  251. package/dist/domain/project/manifest/context.d.ts +13 -0
  252. package/dist/domain/project/manifest/context.js +2 -0
  253. package/dist/domain/project/manifest/context.js.map +1 -0
  254. package/dist/domain/project/manifest/fields.d.ts +6 -0
  255. package/dist/domain/project/manifest/fields.js +36 -0
  256. package/dist/domain/project/manifest/fields.js.map +1 -0
  257. package/dist/domain/project/manifest/governance.d.ts +8 -0
  258. package/dist/domain/project/manifest/governance.js +161 -0
  259. package/dist/domain/project/manifest/governance.js.map +1 -0
  260. package/dist/domain/project/manifest/project-identity.d.ts +6 -0
  261. package/dist/domain/project/manifest/project-identity.js +273 -0
  262. package/dist/domain/project/manifest/project-identity.js.map +1 -0
  263. package/dist/domain/project/manifest/reader.d.ts +9 -0
  264. package/dist/domain/project/manifest/reader.js +141 -0
  265. package/dist/domain/project/manifest/reader.js.map +1 -0
  266. package/dist/domain/project/paths.d.ts +2 -0
  267. package/dist/domain/project/paths.js +29 -0
  268. package/dist/domain/project/paths.js.map +1 -0
  269. package/dist/domain/project/planning.d.ts +19 -0
  270. package/dist/domain/project/planning.js +329 -0
  271. package/dist/domain/project/planning.js.map +1 -0
  272. package/dist/domain/project/retired-workload.d.ts +4 -0
  273. package/dist/domain/project/retired-workload.js +18 -0
  274. package/dist/domain/project/retired-workload.js.map +1 -0
  275. package/dist/domain/project/supported-stack.d.ts +113 -0
  276. package/dist/domain/project/supported-stack.js +426 -0
  277. package/dist/domain/project/supported-stack.js.map +1 -0
  278. package/dist/domain/workstation/constraints.d.ts +6 -0
  279. package/dist/domain/workstation/constraints.js +8 -0
  280. package/dist/domain/workstation/constraints.js.map +1 -0
  281. package/dist/file-system.d.ts +10 -44
  282. package/dist/file-system.js +9 -1245
  283. package/dist/file-system.js.map +1 -1
  284. package/dist/framework-adapters.d.ts +1 -1
  285. package/dist/framework-validation.d.ts +1 -1
  286. package/dist/framework-validation.js +1 -1
  287. package/dist/framework-validation.js.map +1 -1
  288. package/dist/genai-templates.d.ts +2 -9
  289. package/dist/genai-templates.js +4 -5
  290. package/dist/genai-templates.js.map +1 -1
  291. package/dist/generators/common/artifacts.d.ts +6 -0
  292. package/dist/generators/common/artifacts.js +30 -0
  293. package/dist/generators/common/artifacts.js.map +1 -0
  294. package/dist/generators/common/base.d.ts +17 -0
  295. package/dist/generators/common/base.js +421 -0
  296. package/dist/generators/common/base.js.map +1 -0
  297. package/dist/generators/common/dotenv-validation.d.ts +2 -0
  298. package/dist/generators/common/dotenv-validation.js +80 -0
  299. package/dist/generators/common/dotenv-validation.js.map +1 -0
  300. package/dist/generators/common/environments.d.ts +3 -0
  301. package/dist/generators/common/environments.js +15 -0
  302. package/dist/generators/common/environments.js.map +1 -0
  303. package/dist/generators/common/frontend.d.ts +15 -0
  304. package/dist/generators/common/frontend.js +166 -0
  305. package/dist/generators/common/frontend.js.map +1 -0
  306. package/dist/generators/common/python-settings.d.ts +2 -0
  307. package/dist/generators/common/python-settings.js +70 -0
  308. package/dist/generators/common/python-settings.js.map +1 -0
  309. package/dist/generators/common/spec-workflow.d.ts +28 -0
  310. package/dist/generators/common/spec-workflow.js +567 -0
  311. package/dist/generators/common/spec-workflow.js.map +1 -0
  312. package/dist/generators/common/values.d.ts +13 -0
  313. package/dist/generators/common/values.js +16 -0
  314. package/dist/generators/common/values.js.map +1 -0
  315. package/dist/generators/containers/compose.d.ts +7 -0
  316. package/dist/generators/containers/compose.js +196 -0
  317. package/dist/generators/containers/compose.js.map +1 -0
  318. package/dist/generators/containers/context.d.ts +1 -0
  319. package/dist/generators/containers/context.js +43 -0
  320. package/dist/generators/containers/context.js.map +1 -0
  321. package/dist/generators/containers/images.d.ts +7 -0
  322. package/dist/generators/containers/images.js +119 -0
  323. package/dist/generators/containers/images.js.map +1 -0
  324. package/dist/generators/context.d.ts +42 -0
  325. package/dist/generators/context.js +38 -0
  326. package/dist/generators/context.js.map +1 -0
  327. package/dist/generators/genai/backend.d.ts +20 -0
  328. package/dist/generators/genai/backend.js +667 -0
  329. package/dist/generators/genai/backend.js.map +1 -0
  330. package/dist/generators/genai/configuration.d.ts +3 -0
  331. package/dist/generators/genai/configuration.js +43 -0
  332. package/dist/generators/genai/configuration.js.map +1 -0
  333. package/dist/generators/genai/database.d.ts +7 -0
  334. package/dist/generators/genai/database.js +71 -0
  335. package/dist/generators/genai/database.js.map +1 -0
  336. package/dist/generators/genai/functions.d.ts +15 -0
  337. package/dist/generators/genai/functions.js +156 -0
  338. package/dist/generators/genai/functions.js.map +1 -0
  339. package/dist/generators/genai/index.d.ts +8 -0
  340. package/dist/generators/genai/index.js +11 -0
  341. package/dist/generators/genai/index.js.map +1 -0
  342. package/dist/generators/genai/patterns.d.ts +9 -0
  343. package/dist/generators/genai/patterns.js +309 -0
  344. package/dist/generators/genai/patterns.js.map +1 -0
  345. package/dist/generators/infrastructure/azure.d.ts +18 -0
  346. package/dist/generators/infrastructure/azure.js +655 -0
  347. package/dist/generators/infrastructure/azure.js.map +1 -0
  348. package/dist/generators/infrastructure/names.d.ts +23 -0
  349. package/dist/generators/infrastructure/names.js +51 -0
  350. package/dist/generators/infrastructure/names.js.map +1 -0
  351. package/dist/generators/standard/configuration.d.ts +3 -0
  352. package/dist/generators/standard/configuration.js +26 -0
  353. package/dist/generators/standard/configuration.js.map +1 -0
  354. package/dist/generators/standard/go.d.ts +17 -0
  355. package/dist/generators/standard/go.js +338 -0
  356. package/dist/generators/standard/go.js.map +1 -0
  357. package/dist/generators/standard/index.d.ts +12 -0
  358. package/dist/generators/standard/index.js +12 -0
  359. package/dist/generators/standard/index.js.map +1 -0
  360. package/dist/generators/standard/node.d.ts +20 -0
  361. package/dist/generators/standard/node.js +314 -0
  362. package/dist/generators/standard/node.js.map +1 -0
  363. package/dist/generators/standard/python.d.ts +18 -0
  364. package/dist/generators/standard/python.js +191 -0
  365. package/dist/generators/standard/python.js.map +1 -0
  366. package/dist/go-template-assets.js +3 -3
  367. package/dist/go-template-assets.js.map +1 -1
  368. package/dist/governance-activation/activation-state.d.ts +1 -1
  369. package/dist/governance-activation/activation-state.js +46 -9
  370. package/dist/governance-activation/activation-state.js.map +1 -1
  371. package/dist/governance-activation/approvals.d.ts +1 -188
  372. package/dist/governance-activation/approvals.js +1 -507
  373. package/dist/governance-activation/approvals.js.map +1 -1
  374. package/dist/governance-activation/canonical-json.d.ts +1 -3
  375. package/dist/governance-activation/canonical-json.js +1 -35
  376. package/dist/governance-activation/canonical-json.js.map +1 -1
  377. package/dist/governance-activation/commands.d.ts +1 -1
  378. package/dist/governance-activation/commands.js +72 -106
  379. package/dist/governance-activation/commands.js.map +1 -1
  380. package/dist/governance-activation/compatibility.d.ts +11 -3
  381. package/dist/governance-activation/compatibility.js +31 -4
  382. package/dist/governance-activation/compatibility.js.map +1 -1
  383. package/dist/governance-activation/credentials.d.ts +3 -3
  384. package/dist/governance-activation/credentials.js +5 -5
  385. package/dist/governance-activation/credentials.js.map +1 -1
  386. package/dist/governance-activation/doctor.d.ts +1 -1
  387. package/dist/governance-activation/doctor.js +28 -96
  388. package/dist/governance-activation/doctor.js.map +1 -1
  389. package/dist/governance-activation/evidence.d.ts +1 -49
  390. package/dist/governance-activation/evidence.js +1 -245
  391. package/dist/governance-activation/evidence.js.map +1 -1
  392. package/dist/governance-activation/git-ignore.d.ts +9 -0
  393. package/dist/governance-activation/git-ignore.js +76 -0
  394. package/dist/governance-activation/git-ignore.js.map +1 -0
  395. package/dist/governance-activation/graph.d.ts +1 -397
  396. package/dist/governance-activation/graph.js +1 -440
  397. package/dist/governance-activation/graph.js.map +1 -1
  398. package/dist/governance-activation/identity.d.ts +1 -35
  399. package/dist/governance-activation/identity.js +1 -88
  400. package/dist/governance-activation/identity.js.map +1 -1
  401. package/dist/governance-activation/index.d.ts +3 -0
  402. package/dist/governance-activation/index.js +3 -0
  403. package/dist/governance-activation/index.js.map +1 -1
  404. package/dist/governance-activation/inputs.d.ts +8 -0
  405. package/dist/governance-activation/inputs.js +146 -0
  406. package/dist/governance-activation/inputs.js.map +1 -0
  407. package/dist/governance-activation/migration.d.ts +4 -3
  408. package/dist/governance-activation/migration.js +14 -6
  409. package/dist/governance-activation/migration.js.map +1 -1
  410. package/dist/governance-activation/phase-bootstrap-state.d.ts +3 -0
  411. package/dist/governance-activation/phase-bootstrap-state.js +128 -0
  412. package/dist/governance-activation/phase-bootstrap-state.js.map +1 -0
  413. package/dist/governance-activation/phase-discovery.d.ts +2 -0
  414. package/dist/governance-activation/phase-discovery.js +88 -0
  415. package/dist/governance-activation/phase-discovery.js.map +1 -0
  416. package/dist/governance-activation/phase-governance.d.ts +4 -0
  417. package/dist/governance-activation/phase-governance.js +141 -0
  418. package/dist/governance-activation/phase-governance.js.map +1 -0
  419. package/dist/governance-activation/phase-publication.d.ts +8 -0
  420. package/dist/governance-activation/phase-publication.js +318 -0
  421. package/dist/governance-activation/phase-publication.js.map +1 -0
  422. package/dist/governance-activation/read-only.d.ts +30 -0
  423. package/dist/governance-activation/read-only.js +93 -0
  424. package/dist/governance-activation/read-only.js.map +1 -0
  425. package/dist/governance-activation/readiness.d.ts +1 -25
  426. package/dist/governance-activation/readiness.js +1 -222
  427. package/dist/governance-activation/readiness.js.map +1 -1
  428. package/dist/governance-activation/reconciliation.d.ts +1 -1
  429. package/dist/governance-activation/reconciliation.js +3 -3
  430. package/dist/governance-activation/reconciliation.js.map +1 -1
  431. package/dist/governance-activation/release-integrity.d.ts +1 -1
  432. package/dist/governance-activation/release-integrity.js +2 -2
  433. package/dist/governance-activation/release-integrity.js.map +1 -1
  434. package/dist/governance-activation/seed-lifecycle.d.ts +9 -1
  435. package/dist/governance-activation/seed-lifecycle.js +197 -63
  436. package/dist/governance-activation/seed-lifecycle.js.map +1 -1
  437. package/dist/governance-activation/source-of-truth.d.ts +5 -3
  438. package/dist/governance-activation/source-of-truth.js +32 -52
  439. package/dist/governance-activation/source-of-truth.js.map +1 -1
  440. package/dist/governance-activation/spec-kit-seed.d.ts +9 -0
  441. package/dist/governance-activation/spec-kit-seed.js +43 -0
  442. package/dist/governance-activation/spec-kit-seed.js.map +1 -0
  443. package/dist/governance-activation/task-projection.d.ts +1 -1
  444. package/dist/governance-activation/transition-planning.d.ts +15 -0
  445. package/dist/governance-activation/transition-planning.js +250 -0
  446. package/dist/governance-activation/transition-planning.js.map +1 -0
  447. package/dist/governance-activation/transition-ports.d.ts +150 -0
  448. package/dist/governance-activation/transition-ports.js +2 -0
  449. package/dist/governance-activation/transition-ports.js.map +1 -0
  450. package/dist/governance-activation/transition-process.d.ts +7 -0
  451. package/dist/governance-activation/transition-process.js +21 -0
  452. package/dist/governance-activation/transition-process.js.map +1 -0
  453. package/dist/governance-activation/transition-records.d.ts +56 -0
  454. package/dist/governance-activation/transition-records.js +208 -0
  455. package/dist/governance-activation/transition-records.js.map +1 -0
  456. package/dist/governance-activation/transitions.d.ts +6 -165
  457. package/dist/governance-activation/transitions.js +142 -2055
  458. package/dist/governance-activation/transitions.js.map +1 -1
  459. package/dist/governance-activation/types.d.ts +1 -390
  460. package/dist/governance-activation/types.js +1 -107
  461. package/dist/governance-activation/types.js.map +1 -1
  462. package/dist/governance-activation/validators.d.ts +1 -17
  463. package/dist/governance-activation/validators.js +1 -1304
  464. package/dist/governance-activation/validators.js.map +1 -1
  465. package/dist/governance-assessment/catalog.d.ts +1 -2
  466. package/dist/governance-assessment/catalog.js +18 -85
  467. package/dist/governance-assessment/catalog.js.map +1 -1
  468. package/dist/governance-assessment/command.d.ts +1 -1
  469. package/dist/governance-assessment/command.js +8 -12
  470. package/dist/governance-assessment/command.js.map +1 -1
  471. package/dist/governance-assessment/engine.d.ts +6 -11
  472. package/dist/governance-assessment/engine.js +463 -114
  473. package/dist/governance-assessment/engine.js.map +1 -1
  474. package/dist/governance-assessment/live-normalize.d.ts +1 -19
  475. package/dist/governance-assessment/live-normalize.js +1 -402
  476. package/dist/governance-assessment/live-normalize.js.map +1 -1
  477. package/dist/governance-assessment/live-transport.d.ts +2 -6
  478. package/dist/governance-assessment/live-transport.js +10 -22
  479. package/dist/governance-assessment/live-transport.js.map +1 -1
  480. package/dist/governance-assessment/live.js +55 -5
  481. package/dist/governance-assessment/live.js.map +1 -1
  482. package/dist/governance-assessment/predicates.d.ts +1 -27
  483. package/dist/governance-assessment/predicates.js +1 -329
  484. package/dist/governance-assessment/predicates.js.map +1 -1
  485. package/dist/governance-assessment/project.d.ts +13 -17
  486. package/dist/governance-assessment/project.js +59 -149
  487. package/dist/governance-assessment/project.js.map +1 -1
  488. package/dist/governance-assessment/readers.d.ts +1 -5
  489. package/dist/governance-assessment/readers.js +5 -13
  490. package/dist/governance-assessment/readers.js.map +1 -1
  491. package/dist/governance-assessment/report.d.ts +1 -23
  492. package/dist/governance-assessment/report.js +1 -265
  493. package/dist/governance-assessment/report.js.map +1 -1
  494. package/dist/governance-assessment/sanitize.d.ts +1 -8
  495. package/dist/governance-assessment/sanitize.js +1 -56
  496. package/dist/governance-assessment/sanitize.js.map +1 -1
  497. package/dist/governance-assessment/types.d.ts +1 -173
  498. package/dist/governance-assessment/types.js +1 -12
  499. package/dist/governance-assessment/types.js.map +1 -1
  500. package/dist/governance-assessment/yaml.d.ts +1 -6
  501. package/dist/governance-assessment/yaml.js +1 -29
  502. package/dist/governance-assessment/yaml.js.map +1 -1
  503. package/dist/init-filesystem.d.ts +1 -1
  504. package/dist/init-filesystem.js +126 -174
  505. package/dist/init-filesystem.js.map +1 -1
  506. package/dist/interactive.d.ts +2 -2
  507. package/dist/interactive.js +42 -41
  508. package/dist/interactive.js.map +1 -1
  509. package/dist/migrate-plan.d.ts +1 -1
  510. package/dist/migrate-plan.js +14 -8
  511. package/dist/migrate-plan.js.map +1 -1
  512. package/dist/npm-template-assets.js +5 -4
  513. package/dist/npm-template-assets.js.map +1 -1
  514. package/dist/openspec-profile.d.ts +1 -1
  515. package/dist/opentofu-template-assets.js +3 -3
  516. package/dist/opentofu-template-assets.js.map +1 -1
  517. package/dist/package-identity.d.ts +3 -0
  518. package/dist/package-identity.js +13 -2
  519. package/dist/package-identity.js.map +1 -1
  520. package/dist/planner.d.ts +1 -19
  521. package/dist/planner.js +1 -565
  522. package/dist/planner.js.map +1 -1
  523. package/dist/process-runner.d.ts +1 -1
  524. package/dist/project-dependencies.d.ts +5 -3
  525. package/dist/project-dependencies.js +74 -63
  526. package/dist/project-dependencies.js.map +1 -1
  527. package/dist/published-verifier.js +61 -30
  528. package/dist/published-verifier.js.map +1 -1
  529. package/dist/python-template-assets.js +6 -9
  530. package/dist/python-template-assets.js.map +1 -1
  531. package/dist/reconcile.d.ts +1 -1
  532. package/dist/reconcile.js +2 -2
  533. package/dist/reconcile.js.map +1 -1
  534. package/dist/repository-governance.d.ts +5 -2
  535. package/dist/repository-governance.js +126 -112
  536. package/dist/repository-governance.js.map +1 -1
  537. package/dist/runtime.js +1 -1
  538. package/dist/runtime.js.map +1 -1
  539. package/dist/scan.d.ts +6 -2
  540. package/dist/scan.js +78 -30
  541. package/dist/scan.js.map +1 -1
  542. package/dist/self-upgrade.d.ts +1 -1
  543. package/dist/self-upgrade.js +20 -7
  544. package/dist/self-upgrade.js.map +1 -1
  545. package/dist/stable-release.js +7 -1
  546. package/dist/stable-release.js.map +1 -1
  547. package/dist/standard-templates.d.ts +2 -2
  548. package/dist/standard-templates.js +6 -841
  549. package/dist/standard-templates.js.map +1 -1
  550. package/dist/supported-stack.d.ts +3 -115
  551. package/dist/supported-stack.js +4 -438
  552. package/dist/supported-stack.js.map +1 -1
  553. package/dist/telemetry/index.d.ts +1 -1
  554. package/dist/templates.d.ts +10 -20
  555. package/dist/templates.js +52 -3261
  556. package/dist/templates.js.map +1 -1
  557. package/dist/types.d.ts +1 -263
  558. package/dist/types.js +1 -1
  559. package/dist/types.js.map +1 -1
  560. package/dist/version.js +2 -6
  561. package/dist/version.js.map +1 -1
  562. package/dist/workstation-catalog.d.ts +5 -2
  563. package/dist/workstation-catalog.js +33 -2
  564. package/dist/workstation-catalog.js.map +1 -1
  565. package/dist/workstation.d.ts +4 -3
  566. package/dist/workstation.js +86 -32
  567. package/dist/workstation.js.map +1 -1
  568. package/docs/assets/liftoff-terminal.svg +3 -3
  569. package/docs/azure-deployment.md +107 -10
  570. package/docs/cli-reference.md +49 -13
  571. package/docs/configuration-and-manifests.md +139 -34
  572. package/docs/existing-repositories.md +33 -6
  573. package/docs/getting-started.md +44 -31
  574. package/docs/prerequisites.md +34 -35
  575. package/docs/project-structure.md +39 -46
  576. package/docs/repository-governance.md +90 -24
  577. package/docs/safety-and-consent.md +65 -12
  578. package/docs/spec-workflows-and-agents.md +31 -28
  579. package/docs/supported-stack.md +11 -9
  580. package/docs/troubleshooting.md +107 -49
  581. package/docs/workloads.md +46 -62
  582. package/package.json +4 -7
  583. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/UPSTREAM_LICENSE.txt +0 -21
  584. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/catalog.json +0 -520
  585. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/packaged/gitignore +0 -27
  586. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/README.md +0 -25
  587. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/components.json +0 -22
  588. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/eslint.config.js +0 -23
  589. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/index.html +0 -13
  590. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/package-lock.json +0 -6156
  591. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/package.json +0 -60
  592. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/public/power-apps.svg +0 -55
  593. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/App.tsx +0 -17
  594. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/assets/react.svg +0 -1
  595. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/mode-toggle.tsx +0 -37
  596. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/badge.tsx +0 -46
  597. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/button.tsx +0 -60
  598. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/calendar.tsx +0 -211
  599. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/card.tsx +0 -92
  600. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/chart.tsx +0 -355
  601. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/checkbox.tsx +0 -32
  602. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/command.tsx +0 -184
  603. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/dialog.tsx +0 -141
  604. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/dropdown-menu.tsx +0 -257
  605. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/input.tsx +0 -21
  606. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/label.tsx +0 -22
  607. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/popover.tsx +0 -46
  608. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/progress.tsx +0 -29
  609. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/select.tsx +0 -185
  610. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/separator.tsx +0 -26
  611. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/skeleton.tsx +0 -13
  612. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/table.tsx +0 -114
  613. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/tabs.tsx +0 -66
  614. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/textarea.tsx +0 -18
  615. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/components/ui/tooltip.tsx +0 -61
  616. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/hooks/use-theme.ts +0 -11
  617. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/index.css +0 -131
  618. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/lib/utils.ts +0 -6
  619. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/main.tsx +0 -10
  620. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/pages/_layout.tsx +0 -31
  621. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/pages/home.tsx +0 -59
  622. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/pages/not-found.tsx +0 -14
  623. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/providers/query-provider.tsx +0 -26
  624. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/providers/sonner-provider.tsx +0 -23
  625. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/providers/theme-provider.tsx +0 -64
  626. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/src/router.tsx +0 -24
  627. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/tsconfig.app.json +0 -34
  628. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/tsconfig.json +0 -13
  629. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/tsconfig.node.json +0 -26
  630. package/assets/power-apps-code-app/3438c352483e40982f6c5c0fc36fd71f8e7adbbb/starter/vite.config.ts +0 -19
  631. package/assets/power-apps-code-app/README.md +0 -27
  632. package/dist/code-apps-plugin.d.ts +0 -13
  633. package/dist/code-apps-plugin.js +0 -69
  634. package/dist/code-apps-plugin.js.map +0 -1
  635. package/dist/power-apps-assets.d.ts +0 -30
  636. package/dist/power-apps-assets.js +0 -131
  637. package/dist/power-apps-assets.js.map +0 -1
  638. package/dist/power-apps-templates.d.ts +0 -3
  639. package/dist/power-apps-templates.js +0 -177
  640. package/dist/power-apps-templates.js.map +0 -1
  641. package/dist/power-apps-validation.d.ts +0 -1
  642. package/dist/power-apps-validation.js +0 -79
  643. package/dist/power-apps-validation.js.map +0 -1
@@ -1,1246 +1,10 @@
1
- import { createHash, randomUUID } from 'node:crypto';
2
- import { access, chmod, lstat, mkdir, readdir, readFile, realpath, rename, rmdir, stat, unlink, writeFile } from 'node:fs/promises';
3
- import path from 'node:path';
4
- import { getApiStack, canonicalizeCodingAgents, getEnvironment, getGovernanceProfile, getCodingAgent, getPattern, getProvider, getProjectType, getSpecWorkflow, listRegions } from './catalogs.js';
5
- import { validateFrameworkInstallation } from './framework-validation.js';
6
- import { isManagedCoreLogicalName, isRetiredManagedCoreArtifactIdentity, isRetiredManagedCoreLogicalName, isUnknownRetiredManagedCoreAliasLogicalName, managedCoreLogicalNames, legacyProvisioningGroup } from './artifact-lifecycle.js';
7
- import { governanceArtifactPaths, governancePolicyVersion } from './repository-governance.js';
8
- import { currentActivationIdentity } from './governance-activation/graph.js';
9
- import { validateActivationIdentity } from './governance-activation/validators.js';
10
- import { validateGovernanceCompatibilityMetadata } from './governance-activation/compatibility.js';
11
- export class FileSystemError extends Error {
12
- constructor(message) {
13
- super(message);
14
- this.name = 'FileSystemError';
15
- }
16
- }
17
- export class ProjectFileTransactionError extends FileSystemError {
18
- rollbackFailures;
19
- constructor(message, rollbackFailures) {
20
- super(message);
21
- this.rollbackFailures = rollbackFailures;
22
- this.name = 'ProjectFileTransactionError';
23
- }
24
- }
25
- export function resolveTargetRoot(cwd, projectName) {
26
- return path.resolve(cwd, projectName);
27
- }
28
- const SEMVER_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
29
- const CONTENT_HASH_PATTERN = /^sha256:[0-9a-f]{64}$/;
30
- const WINDOWS_DRIVE_PATTERN = /^[A-Za-z]:/;
31
- const WINDOWS_RESERVED_NAME_PATTERN = /^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(?:\.|$)/i;
32
- function errorCode(error) {
33
- if (typeof error !== 'object' || error === null || !('code' in error)) {
34
- return undefined;
35
- }
36
- const code = error.code;
37
- return typeof code === 'string' ? code : undefined;
38
- }
39
- function errorMessage(error) {
40
- return error instanceof Error ? error.message : String(error);
41
- }
42
- function isRecord(value) {
43
- return typeof value === 'object' && value !== null && !Array.isArray(value);
44
- }
45
- function isPathWithin(root, candidate) {
46
- const relative = path.relative(root, candidate);
47
- return relative === '' || (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative));
48
- }
49
- export function validateArtifactPathParts(value, label = 'Artifact path') {
50
- if (!Array.isArray(value) || value.length === 0) {
51
- throw new FileSystemError(`${label} must be a non-empty path-part array.`);
52
- }
53
- return value.map((part, index) => {
54
- if (typeof part !== 'string' || part.length === 0 || part.trim().length === 0) {
55
- throw new FileSystemError(`${label} part ${index + 1} must be a non-empty string.`);
56
- }
57
- if (part === '.' ||
58
- part === '..' ||
59
- part.includes('/') ||
60
- part.includes('\\') ||
61
- part.includes('\0') ||
62
- path.posix.isAbsolute(part) ||
63
- path.win32.isAbsolute(part) ||
64
- WINDOWS_DRIVE_PATTERN.test(part)) {
65
- throw new FileSystemError(`${label} contains unsafe path part ${JSON.stringify(part)}.`);
66
- }
67
- if (part.endsWith('.') || part.endsWith(' ') || WINDOWS_RESERVED_NAME_PATTERN.test(part)) {
68
- throw new FileSystemError(`${label} contains non-portable path part ${JSON.stringify(part)}.`);
69
- }
70
- return part;
71
- });
72
- }
73
- export function artifactPath(root, pathParts) {
74
- const validated = validateArtifactPathParts(pathParts);
75
- const joinedPath = path.join(root, ...validated);
76
- const resolvedRoot = path.resolve(root);
77
- const resolvedPath = path.resolve(joinedPath);
78
- if (!isPathWithin(resolvedRoot, resolvedPath) || resolvedPath === resolvedRoot) {
79
- throw new FileSystemError(`Artifact path escapes project root: ${validated.join('/')}`);
80
- }
81
- return joinedPath;
82
- }
83
- export async function resolveProjectPath(projectRoot, pathParts) {
84
- const validated = validateArtifactPathParts(pathParts);
85
- const resolvedRoot = path.resolve(projectRoot);
86
- const targetPath = artifactPath(resolvedRoot, validated);
87
- let realRoot;
88
- try {
89
- realRoot = await realpath(resolvedRoot);
90
- }
91
- catch (error) {
92
- throw new FileSystemError(`Unable to resolve project root ${resolvedRoot}: ${errorMessage(error)}`);
93
- }
94
- let current = resolvedRoot;
95
- for (const [index, part] of validated.entries()) {
96
- current = path.join(current, part);
97
- let details;
98
- try {
99
- details = await lstat(current);
100
- }
101
- catch (error) {
102
- if (errorCode(error) === 'ENOENT') {
103
- return targetPath;
104
- }
105
- throw new FileSystemError(`Unable to inspect artifact path ${validated.join('/')}: ${errorMessage(error)}`);
106
- }
107
- let resolvedExistingPath;
108
- try {
109
- resolvedExistingPath = await realpath(current);
110
- }
111
- catch (error) {
112
- throw new FileSystemError(`Unable to resolve artifact path ${validated.join('/')}: ${errorMessage(error)}`);
113
- }
114
- if (!isPathWithin(realRoot, resolvedExistingPath)) {
115
- throw new FileSystemError(`Artifact path escapes project root through a symlink: ${validated.join('/')}`);
116
- }
117
- if (index < validated.length - 1 && !details.isDirectory() && !details.isSymbolicLink()) {
118
- throw new FileSystemError(`Artifact path parent is not a directory: ${validated.slice(0, index + 1).join('/')}`);
119
- }
120
- }
121
- return targetPath;
122
- }
123
- export async function assertNewOrEmptyDirectory(targetRoot) {
124
- try {
125
- const details = await stat(targetRoot);
126
- if (!details.isDirectory()) {
127
- throw new FileSystemError(`Target path exists and is not a directory: ${targetRoot}`);
128
- }
129
- const entries = await readdir(targetRoot);
130
- if (entries.length > 0) {
131
- throw new FileSystemError(`Target directory must be new or empty: ${targetRoot}`);
132
- }
133
- }
134
- catch (error) {
135
- if (errorCode(error) === 'ENOENT') {
136
- return;
137
- }
138
- throw error;
139
- }
140
- }
141
- export async function writeArtifacts(targetRoot, artifacts) {
142
- await assertNewOrEmptyDirectory(targetRoot);
143
- await mkdir(targetRoot, { recursive: true });
144
- for (const artifact of artifacts) {
145
- await writeProjectFile(targetRoot, artifact.pathParts, artifact.content);
146
- }
147
- }
148
- export const SUPPORTED_MANIFEST_VERSIONS = [2, 3, 4, 5, 6, 7];
149
- // seed entries recorded by 0.2.0 manifests; dropped on read so archiving the
150
- // seeded change is a non-event for validate, update, and doctor
151
- const LEGACY_SEED_LOGICAL_NAMES = new Set([
152
- 'openspec-config',
153
- 'openspec-seed-change-metadata',
154
- 'openspec-seed-proposal',
155
- 'openspec-seed-design',
156
- 'openspec-seed-tasks',
157
- 'openspec-seed-spec',
158
- 'openspec-spec-placeholder',
159
- 'spec-kit-constitution',
160
- 'specs-placeholder'
161
- ]);
162
- const LEGACY_NON_PROVENANCE_LOGICAL_NAMES = new Set([
163
- ...LEGACY_SEED_LOGICAL_NAMES,
164
- 'liftoff-config',
165
- 'spec-kit-spec-template',
166
- 'spec-kit-plan-template'
167
- ]);
168
- const manifestsWithFilteredLegacySeedOwnership = new WeakSet();
169
- export function manifestHadFilteredLegacyNonDurableOwnership(manifest) {
170
- return manifestsWithFilteredLegacySeedOwnership.has(manifest);
171
- }
172
- export async function loadManifest(projectRoot) {
173
- let raw;
174
- try {
175
- const manifestBytes = await readProjectFile(projectRoot, ['liftoff.manifest.json']);
176
- if (manifestBytes === undefined) {
177
- throw new Error('file does not exist');
178
- }
179
- raw = JSON.parse(manifestBytes.toString('utf8'));
180
- }
181
- catch (error) {
182
- throw new FileSystemError(`Unable to read liftoff.manifest.json: ${errorMessage(error)}`);
183
- }
184
- return parseManifest(raw);
185
- }
186
- export function parseManifest(raw) {
187
- if (!isRecord(raw)) {
188
- throw new FileSystemError('Manifest root must be a JSON object.');
189
- }
190
- const artifactVersion = raw.artifactVersion;
191
- if (typeof artifactVersion !== 'number' || !Number.isInteger(artifactVersion)) {
192
- throw new FileSystemError('Manifest artifactVersion must be an integer.');
193
- }
194
- if (!SUPPORTED_MANIFEST_VERSIONS.includes(artifactVersion)) {
195
- throw new FileSystemError(`Unsupported manifest artifactVersion ${JSON.stringify(artifactVersion)}: found ${JSON.stringify(artifactVersion)}; ` +
196
- `supported values are ${SUPPORTED_MANIFEST_VERSIONS.join(', ')}; write version is 7. ` +
197
- `Minimum Liftoff ${currentActivationIdentity.liftoffVersion} is required for manifest v7. ` +
198
- 'Regenerate the project with this CLI, upgrade the CLI for future manifests, or use the Liftoff version that generated this project; no downgrade or write was performed.');
199
- }
200
- if (artifactVersion === 6 || artifactVersion === 7) {
201
- assertOnlyFields(raw, [
202
- 'artifactVersion',
203
- 'generatedBy',
204
- 'liftoffVersion',
205
- 'project',
206
- 'framework',
207
- 'governance',
208
- 'managedArtifacts',
209
- 'projectArtifacts'
210
- ], 'Manifest');
211
- }
212
- else if (artifactVersion === 5) {
213
- assertOnlyFields(raw, [
214
- 'artifactVersion',
215
- 'generatedBy',
216
- 'liftoffVersion',
217
- 'project',
218
- 'framework',
219
- 'governance',
220
- 'artifacts'
221
- ], 'Manifest');
222
- }
223
- if (raw.generatedBy !== 'Mission Control Liftoff') {
224
- throw new FileSystemError('Manifest generatedBy must be "Mission Control Liftoff".');
225
- }
226
- const liftoffVersion = requiredString(raw, 'liftoffVersion', 'Manifest');
227
- if (!SEMVER_PATTERN.test(liftoffVersion)) {
228
- throw new FileSystemError('Manifest liftoffVersion must be a valid semantic version.');
229
- }
230
- const project = normalizeManifestProject(raw.project, artifactVersion);
231
- const framework = normalizeManifestFramework(raw.framework, artifactVersion, project);
232
- const governance = normalizeManifestGovernance(raw.governance, artifactVersion);
233
- let managedArtifacts;
234
- let projectArtifacts;
235
- let filteredLegacySeedOwnership = false;
236
- if (artifactVersion === 6 || artifactVersion === 7) {
237
- managedArtifacts = normalizeManifestManagedArtifacts(raw.managedArtifacts, 'Manifest.managedArtifacts');
238
- projectArtifacts = normalizeManifestProjectArtifacts(raw.projectArtifacts);
239
- validateV6AndV7ArtifactAuthority(managedArtifacts, projectArtifacts);
240
- }
241
- else {
242
- const normalizedArtifacts = normalizeManifestManagedArtifacts(raw.artifacts, 'Manifest.artifacts');
243
- const artifacts = normalizedArtifacts.filter((artifact) => !LEGACY_NON_PROVENANCE_LOGICAL_NAMES.has(artifact.logicalName));
244
- for (const artifact of artifacts) {
245
- if (isUnknownRetiredManagedCoreAliasLogicalName(artifact.logicalName)) {
246
- throw new FileSystemError(`Manifest artifact ${artifact.logicalName} is an unknown retired managed-core alias logical name.`);
247
- }
248
- }
249
- filteredLegacySeedOwnership = artifacts.length !== normalizedArtifacts.length;
250
- managedArtifacts = artifacts.filter((artifact) => isManagedCoreLogicalName(artifact.logicalName) ||
251
- isRetiredManagedCoreLogicalName(artifact.logicalName));
252
- projectArtifacts = artifacts
253
- .filter((artifact) => !isManagedCoreLogicalName(artifact.logicalName) &&
254
- !isRetiredManagedCoreLogicalName(artifact.logicalName))
255
- .map((artifact) => ({
256
- logicalName: artifact.logicalName,
257
- category: artifact.category,
258
- pathParts: artifact.pathParts,
259
- generatedBy: liftoffVersion,
260
- generationHash: artifact.contentHash,
261
- provisioningGroup: project.workload.kind === 'power-apps-code-app'
262
- ? 'power-apps-starter'
263
- : legacyProvisioningGroup(artifact.logicalName)
264
- }));
265
- }
266
- validateManifestArtifactUniqueness(managedArtifacts, projectArtifacts);
267
- const manifest = {
268
- artifactVersion: artifactVersion,
269
- generatedBy: 'Mission Control Liftoff',
270
- liftoffVersion,
271
- project,
272
- framework,
273
- governance,
274
- managedArtifacts,
275
- projectArtifacts
276
- };
277
- validateGovernanceArtifactIdentity(manifest);
278
- if (filteredLegacySeedOwnership) {
279
- manifestsWithFilteredLegacySeedOwnership.add(manifest);
280
- }
281
- return manifest;
282
- }
283
- function requiredString(record, key, scope) {
284
- const value = record[key];
285
- if (typeof value !== 'string' || value.trim().length === 0) {
286
- throw new FileSystemError(`${scope}.${key} must be a non-empty string.`);
287
- }
288
- return value;
289
- }
290
- function optionalString(record, key, scope) {
291
- const value = record[key];
292
- if (value === undefined) {
293
- return undefined;
294
- }
295
- if (typeof value !== 'string' || value.trim().length === 0) {
296
- throw new FileSystemError(`${scope}.${key} must be a non-empty string when present.`);
297
- }
298
- return value;
299
- }
300
- export function normalizeManifestProject(project, artifactVersion) {
301
- if (!isRecord(project)) {
302
- throw new FileSystemError('Manifest.project must be a JSON object.');
303
- }
304
- if (artifactVersion >= 4) {
305
- return normalizeV4ManifestProject(project);
306
- }
307
- const name = requiredString(project, 'name', 'Manifest.project');
308
- const patternValue = optionalString(project, 'pattern', 'Manifest.project');
309
- const projectTypeValue = optionalString(project, 'projectType', 'Manifest.project');
310
- const projectType = getProjectType(projectTypeValue ?? (patternValue ? 'genai' : ''));
311
- if (!projectType || projectTypeValue !== undefined && projectType.id !== projectTypeValue) {
312
- throw new FileSystemError('Manifest project identity is missing a valid projectType.');
313
- }
314
- const apiStackValue = optionalString(project, 'apiStack', 'Manifest.project');
315
- const apiStack = getApiStack(apiStackValue ?? (projectType.id === 'genai' ? 'python-fastapi' : ''));
316
- if (!apiStack || apiStackValue !== undefined && apiStack.id !== apiStackValue) {
317
- throw new FileSystemError(`Manifest project type ${projectType.id} is missing a valid apiStack.`);
318
- }
319
- const pattern = patternValue ? getPattern(patternValue) : undefined;
320
- if (patternValue && (!pattern || pattern.id !== patternValue)) {
321
- throw new FileSystemError(`Manifest project pattern ${JSON.stringify(patternValue)} is invalid.`);
322
- }
323
- if (projectType.id === 'genai' && (!pattern || apiStack.id !== 'python-fastapi')) {
324
- throw new FileSystemError('GenAI manifests require a valid pattern and the python-fastapi API stack.');
325
- }
326
- if (projectType.id === 'standard' && pattern) {
327
- throw new FileSystemError('Standard manifests cannot record a GenAI pattern.');
328
- }
329
- const cloudValue = requiredString(project, 'cloud', 'Manifest.project');
330
- const provider = getProvider(cloudValue);
331
- if (!provider || provider.id !== cloudValue || provider.status !== 'available') {
332
- throw new FileSystemError(`Manifest project cloud ${JSON.stringify(cloudValue)} is invalid or unavailable.`);
333
- }
334
- const regionValue = requiredString(project, 'region', 'Manifest.project');
335
- if (!listRegions(provider.id).some((region) => region.slug === regionValue)) {
336
- throw new FileSystemError(`Manifest project region ${JSON.stringify(regionValue)} is invalid for ${provider.id}.`);
337
- }
338
- const frontend = project.frontend;
339
- if (typeof frontend !== 'boolean') {
340
- throw new FileSystemError('Manifest.project.frontend must be a boolean.');
341
- }
342
- const specWorkflowValue = requiredString(project, 'specWorkflow', 'Manifest.project');
343
- const specWorkflow = getSpecWorkflow(specWorkflowValue);
344
- if (!specWorkflow || specWorkflow.id !== specWorkflowValue) {
345
- throw new FileSystemError(`Manifest project specWorkflow ${JSON.stringify(specWorkflowValue)} is invalid.`);
346
- }
347
- let agents = [];
348
- let defaultAgent;
349
- if (artifactVersion >= 3) {
350
- if (!Array.isArray(project.agents)) {
351
- throw new FileSystemError('Manifest.project.agents must be an array.');
352
- }
353
- const rawAgents = project.agents.map((value, index) => {
354
- if (typeof value !== 'string') {
355
- throw new FileSystemError(`Manifest.project.agents[${index}] must be a string.`);
356
- }
357
- const agent = getCodingAgent(value);
358
- if (!agent || agent.id !== value) {
359
- throw new FileSystemError(`Manifest project agent ${JSON.stringify(value)} is invalid.`);
360
- }
361
- return agent.id;
362
- });
363
- const canonical = canonicalizeCodingAgents(rawAgents).agents.map((agent) => agent.id);
364
- if (canonical.length !== rawAgents.length || canonical.some((agent, index) => agent !== rawAgents[index])) {
365
- throw new FileSystemError('Manifest.project.agents must be unique and in canonical order.');
366
- }
367
- agents = canonical;
368
- const defaultAgentValue = optionalString(project, 'defaultAgent', 'Manifest.project');
369
- if (defaultAgentValue) {
370
- const resolved = getCodingAgent(defaultAgentValue);
371
- if (!resolved || resolved.id !== defaultAgentValue) {
372
- throw new FileSystemError(`Manifest project defaultAgent ${JSON.stringify(defaultAgentValue)} is invalid.`);
373
- }
374
- defaultAgent = resolved.id;
375
- }
376
- }
377
- if (!Array.isArray(project.environments) || project.environments.length === 0) {
378
- throw new FileSystemError('Manifest.project.environments must be a non-empty string array.');
379
- }
380
- const environments = project.environments.map((value, index) => {
381
- if (typeof value !== 'string') {
382
- throw new FileSystemError(`Manifest.project.environments[${index}] must be a string.`);
383
- }
384
- const environment = getEnvironment(value);
385
- if (!environment || environment.id !== value) {
386
- throw new FileSystemError(`Manifest project environment ${JSON.stringify(value)} is invalid.`);
387
- }
388
- return environment.id;
389
- });
390
- if (new Set(environments).size !== environments.length) {
391
- throw new FileSystemError('Manifest.project.environments must not contain duplicates.');
392
- }
393
- return {
394
- name,
395
- workload: projectType.id === 'genai'
396
- ? {
397
- kind: 'genai',
398
- apiStack: apiStack.id,
399
- pattern: pattern.id,
400
- cloud: provider.id,
401
- region: regionValue,
402
- frontend,
403
- environments
404
- }
405
- : {
406
- kind: 'standard',
407
- apiStack: apiStack.id,
408
- cloud: provider.id,
409
- region: regionValue,
410
- frontend,
411
- environments
412
- },
413
- specWorkflow: specWorkflow.id,
414
- agents,
415
- ...(defaultAgent ? { defaultAgent } : {})
416
- };
417
- }
418
- function assertOnlyFields(record, allowed, scope) {
419
- const unknown = Object.keys(record).filter((field) => !allowed.includes(field));
420
- if (unknown.length > 0) {
421
- throw new FileSystemError(`${scope} contains inapplicable or unknown field${unknown.length === 1 ? '' : 's'}: ${unknown.join(', ')}.`);
422
- }
423
- }
424
- function requiredBoolean(record, key, scope) {
425
- const value = record[key];
426
- if (typeof value !== 'boolean') {
427
- throw new FileSystemError(`${scope}.${key} must be a boolean.`);
428
- }
429
- return value;
430
- }
431
- function normalizeV4ManifestProject(project) {
432
- assertOnlyFields(project, ['name', 'workload', 'specWorkflow', 'agents', 'defaultAgent'], 'Manifest.project');
433
- const name = requiredString(project, 'name', 'Manifest.project');
434
- if (!isRecord(project.workload)) {
435
- throw new FileSystemError('Manifest.project.workload must be a JSON object.');
436
- }
437
- const workload = normalizeV4ManifestWorkload(project.workload);
438
- const specWorkflowValue = requiredString(project, 'specWorkflow', 'Manifest.project');
439
- const specWorkflow = getSpecWorkflow(specWorkflowValue);
440
- if (!specWorkflow || specWorkflow.id !== specWorkflowValue) {
441
- throw new FileSystemError(`Manifest project specWorkflow ${JSON.stringify(specWorkflowValue)} is invalid.`);
442
- }
443
- if (!Array.isArray(project.agents)) {
444
- throw new FileSystemError('Manifest.project.agents must be an array.');
445
- }
446
- const rawAgents = project.agents.map((value, index) => {
447
- if (typeof value !== 'string') {
448
- throw new FileSystemError(`Manifest.project.agents[${index}] must be a string.`);
449
- }
450
- const agent = getCodingAgent(value);
451
- if (!agent || agent.id !== value) {
452
- throw new FileSystemError(`Manifest project agent ${JSON.stringify(value)} is invalid.`);
453
- }
454
- return agent.id;
455
- });
456
- const agents = canonicalizeCodingAgents(rawAgents).agents.map((agent) => agent.id);
457
- if (agents.length !== rawAgents.length || agents.some((agent, index) => agent !== rawAgents[index])) {
458
- throw new FileSystemError('Manifest.project.agents must be unique and in canonical order.');
459
- }
460
- const defaultAgentValue = optionalString(project, 'defaultAgent', 'Manifest.project');
461
- let defaultAgent;
462
- if (defaultAgentValue) {
463
- const resolved = getCodingAgent(defaultAgentValue);
464
- if (!resolved || resolved.id !== defaultAgentValue) {
465
- throw new FileSystemError(`Manifest project defaultAgent ${JSON.stringify(defaultAgentValue)} is invalid.`);
466
- }
467
- defaultAgent = resolved.id;
468
- }
469
- return {
470
- name,
471
- workload,
472
- specWorkflow: specWorkflow.id,
473
- agents,
474
- ...(defaultAgent ? { defaultAgent } : {})
475
- };
476
- }
477
- function normalizeV4ManifestWorkload(workload) {
478
- const kind = requiredString(workload, 'kind', 'Manifest.project.workload');
479
- if (kind === 'power-apps-code-app') {
480
- assertOnlyFields(workload, ['kind', 'starter', 'codeAppsPlugin'], 'Manifest.project.workload');
481
- if (!isRecord(workload.starter)) {
482
- throw new FileSystemError('Manifest.project.workload.starter must be a JSON object.');
483
- }
484
- assertOnlyFields(workload.starter, ['repository', 'path', 'commit'], 'Manifest.project.workload.starter');
485
- const repository = requiredString(workload.starter, 'repository', 'Manifest.project.workload.starter');
486
- const templatePath = requiredString(workload.starter, 'path', 'Manifest.project.workload.starter');
487
- const commit = requiredString(workload.starter, 'commit', 'Manifest.project.workload.starter');
488
- if (!/^https:\/\/github\.com\/[^/]+\/[^/]+$/.test(repository)) {
489
- throw new FileSystemError('Manifest Power Apps starter repository must be a canonical GitHub repository URL.');
490
- }
491
- if (templatePath.startsWith('/') ||
492
- templatePath.endsWith('/') ||
493
- templatePath.split('/').some((part) => part === '' || part === '.' || part === '..')) {
494
- throw new FileSystemError('Manifest Power Apps starter path must be a canonical repository-relative path.');
495
- }
496
- if (!/^[0-9a-f]{40}$/.test(commit)) {
497
- throw new FileSystemError('Manifest Power Apps starter commit must be a 40-character lowercase Git commit.');
498
- }
499
- return {
500
- kind,
501
- starter: { repository, path: templatePath, commit },
502
- codeAppsPlugin: requiredBoolean(workload, 'codeAppsPlugin', 'Manifest.project.workload')
503
- };
504
- }
505
- if (kind !== 'genai' && kind !== 'standard') {
506
- throw new FileSystemError(`Manifest project workload kind ${JSON.stringify(kind)} is invalid.`);
507
- }
508
- const allowed = kind === 'genai'
509
- ? ['kind', 'apiStack', 'pattern', 'cloud', 'region', 'frontend', 'environments']
510
- : ['kind', 'apiStack', 'cloud', 'region', 'frontend', 'environments'];
511
- assertOnlyFields(workload, allowed, 'Manifest.project.workload');
512
- const apiStackValue = requiredString(workload, 'apiStack', 'Manifest.project.workload');
513
- const apiStack = getApiStack(apiStackValue);
514
- if (!apiStack || apiStack.id !== apiStackValue) {
515
- throw new FileSystemError(`Manifest project workload apiStack ${JSON.stringify(apiStackValue)} is invalid.`);
516
- }
517
- const patternValue = kind === 'genai'
518
- ? requiredString(workload, 'pattern', 'Manifest.project.workload')
519
- : undefined;
520
- const pattern = patternValue ? getPattern(patternValue) : undefined;
521
- if (kind === 'genai' && (!pattern || pattern.id !== patternValue || apiStack.id !== 'python-fastapi')) {
522
- throw new FileSystemError('GenAI manifest workloads require a valid pattern and python-fastapi API stack.');
523
- }
524
- const cloudValue = requiredString(workload, 'cloud', 'Manifest.project.workload');
525
- const provider = getProvider(cloudValue);
526
- if (!provider || provider.id !== cloudValue || provider.status !== 'available') {
527
- throw new FileSystemError(`Manifest project workload cloud ${JSON.stringify(cloudValue)} is invalid or unavailable.`);
528
- }
529
- const region = requiredString(workload, 'region', 'Manifest.project.workload');
530
- if (!listRegions(provider.id).some((candidate) => candidate.slug === region)) {
531
- throw new FileSystemError(`Manifest project workload region ${JSON.stringify(region)} is invalid for ${provider.id}.`);
532
- }
533
- if (!Array.isArray(workload.environments) || workload.environments.length === 0) {
534
- throw new FileSystemError('Manifest.project.workload.environments must be a non-empty string array.');
535
- }
536
- const environments = workload.environments.map((value, index) => {
537
- if (typeof value !== 'string') {
538
- throw new FileSystemError(`Manifest.project.workload.environments[${index}] must be a string.`);
539
- }
540
- const environment = getEnvironment(value);
541
- if (!environment || environment.id !== value) {
542
- throw new FileSystemError(`Manifest project workload environment ${JSON.stringify(value)} is invalid.`);
543
- }
544
- return environment.id;
545
- });
546
- if (new Set(environments).size !== environments.length) {
547
- throw new FileSystemError('Manifest.project.workload.environments must not contain duplicates.');
548
- }
549
- const common = {
550
- apiStack: apiStack.id,
551
- cloud: provider.id,
552
- region,
553
- frontend: requiredBoolean(workload, 'frontend', 'Manifest.project.workload'),
554
- environments
555
- };
556
- return kind === 'genai'
557
- ? { kind, ...common, pattern: pattern.id }
558
- : { kind, ...common };
559
- }
560
- export function normalizeManifestFramework(value, artifactVersion, project) {
561
- if (artifactVersion === 2) {
562
- return { state: 'legacy', adapter: project.specWorkflow };
563
- }
564
- if (!isRecord(value)) {
565
- throw new FileSystemError('Manifest.framework must be a JSON object.');
566
- }
567
- if (artifactVersion >= 5) {
568
- assertOnlyFields(value, ['state', 'adapter', 'contractVersion'], 'Manifest.framework');
569
- }
570
- const state = requiredString(value, 'state', 'Manifest.framework');
571
- if (state !== 'initialized' && state !== 'legacy') {
572
- throw new FileSystemError('Manifest.framework.state must be "initialized" or "legacy".');
573
- }
574
- const adapterValue = requiredString(value, 'adapter', 'Manifest.framework');
575
- const adapter = getSpecWorkflow(adapterValue);
576
- if (!adapter || adapter.id !== adapterValue || adapter.id !== project.specWorkflow) {
577
- throw new FileSystemError('Manifest.framework.adapter must match Manifest.project.specWorkflow.');
578
- }
579
- const contractVersion = optionalString(value, 'contractVersion', 'Manifest.framework');
580
- if (contractVersion && !SEMVER_PATTERN.test(contractVersion)) {
581
- throw new FileSystemError('Manifest.framework.contractVersion must be a valid semantic version.');
582
- }
583
- if (state === 'legacy') {
584
- if (contractVersion || project.agents.length > 0 || project.defaultAgent) {
585
- throw new FileSystemError('Legacy framework state cannot claim a contract version or configured agents.');
586
- }
587
- return { state, adapter: adapter.id };
588
- }
589
- if (!contractVersion) {
590
- throw new FileSystemError('Initialized framework state requires Manifest.framework.contractVersion.');
591
- }
592
- if (project.agents.length === 0) {
593
- throw new FileSystemError('Initialized framework state requires at least one configured agent.');
594
- }
595
- if (adapter.id === 'spec-kit') {
596
- if (!project.defaultAgent || !project.agents.includes(project.defaultAgent)) {
597
- throw new FileSystemError('Spec Kit manifests require a selected defaultAgent.');
598
- }
599
- }
600
- else if (project.defaultAgent) {
601
- throw new FileSystemError('OpenSpec manifests cannot record a defaultAgent.');
602
- }
603
- return { state, adapter: adapter.id, contractVersion };
604
- }
605
- function normalizeManifestGovernance(value, artifactVersion) {
606
- if (artifactVersion < 5) {
607
- return { profile: 'unspecified', state: 'unspecified' };
608
- }
609
- if (!isRecord(value)) {
610
- throw new FileSystemError('Manifest.governance must be a JSON object.');
611
- }
612
- const profileValue = requiredString(value, 'profile', 'Manifest.governance');
613
- const profile = getGovernanceProfile(profileValue);
614
- if (!profile || profile.id !== profileValue) {
615
- throw new FileSystemError(`Manifest governance profile ${JSON.stringify(profileValue)} is invalid.`);
616
- }
617
- const state = requiredString(value, 'state', 'Manifest.governance');
618
- if (profile.id === 'none') {
619
- assertOnlyFields(value, ['profile', 'state'], 'Manifest.governance');
620
- if (state !== 'disabled') {
621
- throw new FileSystemError('Manifest governance profile none requires disabled state.');
622
- }
623
- return { profile: profile.id, state };
624
- }
625
- assertOnlyFields(value, artifactVersion === 7
626
- ? ['profile', 'policyVersion', 'state', 'activationIdentity']
627
- : ['profile', 'policyVersion', 'state'], 'Manifest.governance');
628
- const policyVersion = requiredString(value, 'policyVersion', 'Manifest.governance');
629
- if (!/^[1-9]\d*$/.test(policyVersion)) {
630
- throw new FileSystemError('Manifest governance policyVersion must be a positive integer.');
631
- }
632
- const supportedPolicyVersions = artifactVersion === 7
633
- ? [governancePolicyVersion]
634
- : ['1', '2', '3', '4', '5', governancePolicyVersion];
635
- if (!supportedPolicyVersions.includes(policyVersion)) {
636
- throw new FileSystemError(`Manifest governance policyVersion cannot be newer than ${governancePolicyVersion}. ` +
637
- `Unsupported Manifest.governance.policyVersion: found ${JSON.stringify(policyVersion)}; ` +
638
- `supported values for artifactVersion ${artifactVersion} are ${supportedPolicyVersions.map((value) => JSON.stringify(value)).join(', ')}. ` +
639
- `Minimum Liftoff ${currentActivationIdentity.liftoffVersion} is required for policy ${governancePolicyVersion}; ` +
640
- 'upgrade the CLI for future policy identities or restore a supported manifest without writing.');
641
- }
642
- if (artifactVersion === 7 && policyVersion !== governancePolicyVersion) {
643
- throw new FileSystemError(`Manifest governance policyVersion must be ${governancePolicyVersion} for artifactVersion 7.`);
644
- }
645
- if (state !== 'handoff-generated' && state !== 'handoff-partial') {
646
- throw new FileSystemError('Enabled manifest governance requires handoff-generated or handoff-partial state.');
647
- }
648
- if (artifactVersion === 7) {
649
- let activationIdentity;
650
- try {
651
- activationIdentity = validateActivationIdentity(value.activationIdentity);
652
- }
653
- catch (error) {
654
- throw new FileSystemError(`Manifest governance activationIdentity is invalid: ${errorMessage(error)}`);
655
- }
656
- if (activationIdentity.policyVersion !== policyVersion) {
657
- throw new FileSystemError('Manifest governance activationIdentity.policyVersion must match policyVersion.');
658
- }
659
- return {
660
- profile: profile.id,
661
- policyVersion,
662
- activationIdentity,
663
- state
664
- };
665
- }
666
- return {
667
- profile: profile.id,
668
- policyVersion,
669
- state
670
- };
671
- }
672
- function normalizeManifestManagedArtifacts(value, scopeRoot) {
673
- if (!Array.isArray(value)) {
674
- throw new FileSystemError(`${scopeRoot} must be an array.`);
675
- }
676
- const logicalNames = new Set();
677
- const paths = new Set();
678
- return value.map((entry, index) => {
679
- const scope = `${scopeRoot}[${index}]`;
680
- if (!isRecord(entry)) {
681
- throw new FileSystemError(`${scope} must be a JSON object.`);
682
- }
683
- assertOnlyFields(entry, ['logicalName', 'category', 'pathParts', 'contentHash'], scope);
684
- const logicalName = requiredString(entry, 'logicalName', scope);
685
- const category = requiredString(entry, 'category', scope);
686
- const pathParts = validateArtifactPathParts(entry.pathParts, `${scope}.pathParts`);
687
- const contentHash = requiredString(entry, 'contentHash', scope);
688
- if (!CONTENT_HASH_PATTERN.test(contentHash)) {
689
- throw new FileSystemError(`${scope}.contentHash must be a sha256-prefixed lowercase hexadecimal digest.`);
690
- }
691
- if (logicalNames.has(logicalName)) {
692
- throw new FileSystemError(`Manifest contains duplicate logicalName ${JSON.stringify(logicalName)}.`);
693
- }
694
- logicalNames.add(logicalName);
695
- const pathKey = pathParts.join('\0');
696
- if (paths.has(pathKey)) {
697
- throw new FileSystemError(`Manifest contains duplicate artifact path ${pathParts.join('/')}.`);
698
- }
699
- paths.add(pathKey);
700
- return { logicalName, category, pathParts, contentHash };
701
- });
702
- }
703
- function normalizeManifestProjectArtifacts(value) {
704
- if (!Array.isArray(value)) {
705
- throw new FileSystemError('Manifest.projectArtifacts must be an array.');
706
- }
707
- const logicalNames = new Set();
708
- const paths = new Set();
709
- return value.map((entry, index) => {
710
- const scope = `Manifest.projectArtifacts[${index}]`;
711
- if (!isRecord(entry)) {
712
- throw new FileSystemError(`${scope} must be a JSON object.`);
713
- }
714
- assertOnlyFields(entry, [
715
- 'logicalName',
716
- 'category',
717
- 'pathParts',
718
- 'generatedBy',
719
- 'generationHash',
720
- 'provisioningGroup'
721
- ], scope);
722
- const logicalName = requiredString(entry, 'logicalName', scope);
723
- const category = requiredString(entry, 'category', scope);
724
- const pathParts = validateArtifactPathParts(entry.pathParts, `${scope}.pathParts`);
725
- const generatedBy = requiredString(entry, 'generatedBy', scope);
726
- if (!SEMVER_PATTERN.test(generatedBy)) {
727
- throw new FileSystemError(`${scope}.generatedBy must be a valid semantic version.`);
728
- }
729
- const generationHash = requiredString(entry, 'generationHash', scope);
730
- if (!CONTENT_HASH_PATTERN.test(generationHash)) {
731
- throw new FileSystemError(`${scope}.generationHash must be a sha256-prefixed lowercase hexadecimal digest.`);
732
- }
733
- const provisioningGroup = normalizeProjectProvisioningGroup(requiredString(entry, 'provisioningGroup', scope), scope);
734
- if (logicalNames.has(logicalName)) {
735
- throw new FileSystemError(`Manifest contains duplicate logicalName ${JSON.stringify(logicalName)}.`);
736
- }
737
- logicalNames.add(logicalName);
738
- const pathKey = pathParts.join('\0');
739
- if (paths.has(pathKey)) {
740
- throw new FileSystemError(`Manifest contains duplicate artifact path ${pathParts.join('/')}.`);
741
- }
742
- paths.add(pathKey);
743
- return {
744
- logicalName,
745
- category,
746
- pathParts,
747
- generatedBy,
748
- generationHash,
749
- provisioningGroup
750
- };
751
- });
752
- }
753
- function normalizeProjectProvisioningGroup(value, scope) {
754
- if (value === 'base' ||
755
- value === 'frontend' ||
756
- value === 'power-apps-starter') {
757
- return value;
758
- }
759
- const prefix = 'environment:';
760
- if (value.startsWith(prefix)) {
761
- const environmentValue = value.slice(prefix.length);
762
- const environment = getEnvironment(environmentValue);
763
- if (environment?.id === environmentValue) {
764
- return `environment:${environment.id}`;
765
- }
766
- }
767
- throw new FileSystemError(`${scope}.provisioningGroup is invalid.`);
768
- }
769
- function validateV6AndV7ArtifactAuthority(managedArtifacts, projectArtifacts) {
770
- for (const artifact of managedArtifacts) {
771
- if (isManagedCoreLogicalName(artifact.logicalName)) {
772
- continue;
773
- }
774
- if (isRetiredManagedCoreLogicalName(artifact.logicalName)) {
775
- if (!isRetiredManagedCoreArtifactIdentity(artifact.logicalName, artifact.category, artifact.pathParts)) {
776
- throw new FileSystemError(`Retired managed-core artifact ${artifact.logicalName} has invalid identity.`);
777
- }
778
- continue;
779
- }
780
- if (isUnknownRetiredManagedCoreAliasLogicalName(artifact.logicalName)) {
781
- throw new FileSystemError(`Manifest managed artifact ${artifact.logicalName} is an unknown retired managed-core alias logical name.`);
782
- }
783
- throw new FileSystemError(`Manifest managed artifact ${artifact.logicalName} is not an explicit managed-core logical name.`);
784
- }
785
- for (const artifact of projectArtifacts) {
786
- if (isManagedCoreLogicalName(artifact.logicalName)) {
787
- throw new FileSystemError(`Manifest project artifact ${artifact.logicalName} cannot contain a managed-core logical name.`);
788
- }
789
- if (isRetiredManagedCoreLogicalName(artifact.logicalName)) {
790
- throw new FileSystemError(`Manifest project artifact ${artifact.logicalName} cannot contain a retired managed-core logical name.`);
791
- }
792
- if (isUnknownRetiredManagedCoreAliasLogicalName(artifact.logicalName)) {
793
- throw new FileSystemError(`Manifest project artifact ${artifact.logicalName} cannot contain an unknown retired managed-core alias logical name.`);
794
- }
795
- }
796
- }
797
- function validateManifestArtifactUniqueness(managedArtifacts, projectArtifacts) {
798
- const logicalNames = new Set();
799
- const paths = new Set();
800
- for (const artifact of [...managedArtifacts, ...projectArtifacts]) {
801
- if (logicalNames.has(artifact.logicalName)) {
802
- throw new FileSystemError(`Manifest contains duplicate logicalName ${JSON.stringify(artifact.logicalName)}.`);
803
- }
804
- logicalNames.add(artifact.logicalName);
805
- const pathKey = artifact.pathParts.join('\0');
806
- if (paths.has(pathKey)) {
807
- throw new FileSystemError(`Manifest contains duplicate artifact path ${artifact.pathParts.join('/')}.`);
808
- }
809
- paths.add(pathKey);
810
- }
811
- }
812
- const governanceLogicalPaths = new Map([
813
- ['repository-governance-policy', governanceArtifactPaths.policy],
814
- ['repository-governance-context', governanceArtifactPaths.context],
815
- ['repository-governance-guide', governanceArtifactPaths.guide],
816
- ['repository-governance-phase-graph', governanceArtifactPaths.phaseGraph],
817
- ['repository-governance-compatibility', governanceArtifactPaths.compatibility],
818
- [
819
- 'repository-governance-credential-policy-schema',
820
- governanceArtifactPaths.credentialPolicySchema
821
- ],
822
- [
823
- 'liftoff-setup-copilot',
824
- governanceArtifactPaths.setup['github-copilot']
825
- ],
826
- [
827
- 'liftoff-setup-claude',
828
- governanceArtifactPaths.setup.claude
829
- ],
830
- [
831
- 'liftoff-governance-assess-copilot',
832
- governanceArtifactPaths.assessment['github-copilot']
833
- ],
834
- [
835
- 'liftoff-governance-assess-claude',
836
- governanceArtifactPaths.assessment.claude
837
- ]
838
- ]);
839
- const assessmentLogicalNames = [
840
- 'liftoff-governance-assess-copilot',
841
- 'liftoff-governance-assess-claude'
842
- ];
843
- const preAssessmentManagedCoreLogicalNames = managedCoreLogicalNames.filter((logicalName) => !assessmentLogicalNames.some((assessment) => assessment === logicalName));
844
- function validateGovernanceArtifactIdentity(manifest) {
845
- if ([...manifest.managedArtifacts, ...manifest.projectArtifacts].some((artifact) => artifact.pathParts.join('/') === 'governance/activation-baseline.json')) {
846
- throw new FileSystemError('governance/activation-baseline.json is user-owned and cannot be a Liftoff manifest artifact.');
847
- }
848
- const governanceArtifacts = manifest.managedArtifacts.filter((artifact) => governanceLogicalPaths.has(artifact.logicalName));
849
- const retiredGovernanceArtifacts = manifest.managedArtifacts.filter((artifact) => isRetiredManagedCoreLogicalName(artifact.logicalName));
850
- for (const artifact of retiredGovernanceArtifacts) {
851
- if (!isRetiredManagedCoreArtifactIdentity(artifact.logicalName, artifact.category, artifact.pathParts)) {
852
- throw new FileSystemError(`Retired managed-core artifact ${artifact.logicalName} has invalid identity.`);
853
- }
854
- }
855
- const applicableAssessment = manifest.project.agents.map((agent) => agent === 'github-copilot'
856
- ? 'liftoff-governance-assess-copilot'
857
- : 'liftoff-governance-assess-claude');
858
- for (const artifact of governanceArtifacts.filter((entry) => assessmentLogicalNames.some((logicalName) => entry.logicalName === logicalName))) {
859
- if (!applicableAssessment.includes(artifact.logicalName)) {
860
- throw new FileSystemError(`Manifest governance contains inapplicable assessment integration ${artifact.logicalName}.`);
861
- }
862
- if (artifact.category !== 'governance' ||
863
- artifact.pathParts.join('\0') !== governanceLogicalPaths.get(artifact.logicalName).join('\0')) {
864
- throw new FileSystemError(`Manifest governance artifact ${artifact.logicalName} has invalid identity.`);
865
- }
866
- }
867
- if (manifest.governance.profile === 'unspecified') {
868
- return;
869
- }
870
- if (manifest.governance.profile === 'none') {
871
- if (governanceArtifacts.length > 0 || retiredGovernanceArtifacts.length > 0) {
872
- throw new FileSystemError('Disabled manifest governance cannot own governance handoff artifacts.');
873
- }
874
- return;
875
- }
876
- const required = [
877
- 'repository-governance-policy',
878
- 'repository-governance-context',
879
- 'repository-governance-guide',
880
- 'repository-governance-phase-graph',
881
- 'repository-governance-compatibility',
882
- 'repository-governance-credential-policy-schema',
883
- ...manifest.project.agents.map((agent) => agent === 'github-copilot'
884
- ? 'liftoff-setup-copilot'
885
- : 'liftoff-setup-claude')
886
- ];
887
- const applicable = [...required, ...applicableAssessment];
888
- const hasAssessmentInventory = governanceArtifacts.some((artifact) => applicableAssessment.includes(artifact.logicalName));
889
- const missing = [];
890
- for (const logicalName of applicable) {
891
- const artifact = manifest.managedArtifacts.find((entry) => entry.logicalName === logicalName);
892
- const expectedPath = governanceLogicalPaths.get(logicalName);
893
- if (!artifact) {
894
- missing.push(logicalName);
895
- continue;
896
- }
897
- if (!expectedPath) {
898
- throw new FileSystemError(`Unknown manifest governance artifact ${logicalName}.`);
899
- }
900
- if (artifact.category !== 'governance' ||
901
- artifact.pathParts.join('\0') !== expectedPath.join('\0')) {
902
- throw new FileSystemError(`Manifest governance artifact ${logicalName} has invalid identity.`);
903
- }
904
- }
905
- // Supported older complete inventories predate assessment integrations.
906
- const missingRequired = missing.filter((logicalName) => hasAssessmentInventory || required.includes(logicalName));
907
- if (manifest.governance.state === 'handoff-generated' && missingRequired.length > 0) {
908
- throw new FileSystemError(`Enabled manifest governance is missing artifact ${missingRequired[0]}.`);
909
- }
910
- if (manifest.governance.state === 'handoff-partial' &&
911
- missing.length === 0 &&
912
- retiredGovernanceArtifacts.length === 0) {
913
- throw new FileSystemError('Manifest governance state handoff-partial requires at least one applicable artifact to remain outside Liftoff ownership or one protected retired alias to remain tracked.');
914
- }
915
- for (const artifact of governanceArtifacts) {
916
- if (!applicable.includes(artifact.logicalName)) {
917
- const integration = assessmentLogicalNames.some((logicalName) => artifact.logicalName === logicalName)
918
- ? 'assessment'
919
- : 'setup';
920
- throw new FileSystemError(`Manifest governance contains inapplicable ${integration} integration ${artifact.logicalName}.`);
921
- }
922
- }
923
- }
924
- export async function validateGeneratedProject(projectRoot) {
925
- let manifest;
926
- try {
927
- manifest = await loadManifest(projectRoot);
928
- }
929
- catch (error) {
930
- return [error.message];
931
- }
932
- const issues = [];
933
- for (const artifact of manifest.managedArtifacts) {
934
- if (isRetiredManagedCoreLogicalName(artifact.logicalName)) {
935
- try {
936
- await resolveProjectPath(projectRoot, artifact.pathParts);
937
- }
938
- catch (error) {
939
- issues.push(`Unsafe retired managed-core path for ${artifact.logicalName} at ${artifact.pathParts.join('/')}: ${errorMessage(error)}`);
940
- }
941
- continue;
942
- }
943
- try {
944
- const targetPath = await resolveProjectPath(projectRoot, artifact.pathParts);
945
- const bytes = await readFile(targetPath);
946
- const actualHash = `sha256:${createHash('sha256').update(bytes).digest('hex')}`;
947
- if (actualHash !== artifact.contentHash) {
948
- issues.push(`Artifact hash mismatch for ${artifact.logicalName} at ${artifact.pathParts.join('/')}`);
949
- }
950
- }
951
- catch (error) {
952
- if (errorCode(error) === 'ENOENT') {
953
- issues.push(`Missing artifact ${artifact.logicalName} at ${artifact.pathParts.join('/')}`);
954
- }
955
- else {
956
- issues.push(`Unable to access artifact ${artifact.logicalName} at ${artifact.pathParts.join('/')}: ${errorMessage(error)}`);
957
- }
958
- }
959
- }
960
- const compatibilityArtifact = manifest.managedArtifacts.find((artifact) => artifact.logicalName === 'repository-governance-compatibility');
961
- if (compatibilityArtifact &&
962
- manifest.governance.profile !== 'none' &&
963
- manifest.governance.profile !== 'unspecified') {
964
- try {
965
- const bytes = await readProjectFile(projectRoot, compatibilityArtifact.pathParts);
966
- if (bytes === undefined) {
967
- issues.push(`Missing artifact ${compatibilityArtifact.logicalName} at ${compatibilityArtifact.pathParts.join('/')}`);
968
- }
969
- else {
970
- const hasRetiredManagedArtifacts = manifest.managedArtifacts.some((artifact) => isRetiredManagedCoreLogicalName(artifact.logicalName));
971
- const currentManagedArtifacts = manifest.managedArtifacts.filter((artifact) => !isRetiredManagedCoreLogicalName(artifact.logicalName));
972
- const expectedInventory = currentManagedArtifacts.map((artifact) => ({
973
- logicalName: artifact.logicalName,
974
- pathParts: artifact.pathParts,
975
- lifecycle: 'managed-core',
976
- contentHashAuthority: 'liftoff.manifest.json managedArtifacts[].contentHash'
977
- }));
978
- const compatibility = validateGovernanceCompatibilityMetadata(JSON.parse(bytes.toString('utf8')));
979
- const predatesAssessment = !currentManagedArtifacts.some((artifact) => assessmentLogicalNames.some((logicalName) => artifact.logicalName === logicalName)) && compatibility.managedCore.logicalNameAllowlist.join('\0') ===
980
- preAssessmentManagedCoreLogicalNames.join('\0');
981
- const logicalNameAllowlist = predatesAssessment
982
- ? preAssessmentManagedCoreLogicalNames
983
- : managedCoreLogicalNames;
984
- validateGovernanceCompatibilityMetadata(compatibility, hasRetiredManagedArtifacts
985
- ? undefined
986
- : manifest.governance.state === 'handoff-generated'
987
- ? {
988
- logicalNameAllowlist,
989
- pathAllowlist: currentManagedArtifacts.map((artifact) => artifact.pathParts),
990
- inventory: expectedInventory
991
- }
992
- : {
993
- logicalNameAllowlist
994
- });
995
- }
996
- }
997
- catch (error) {
998
- issues.push(`Invalid repository-governance-compatibility at ${compatibilityArtifact.pathParts.join('/')}: ${errorMessage(error)}`);
999
- }
1000
- }
1001
- for (const artifact of manifest.projectArtifacts) {
1002
- try {
1003
- await resolveProjectPath(projectRoot, artifact.pathParts);
1004
- }
1005
- catch (error) {
1006
- issues.push(`Unsafe project provenance path for ${artifact.logicalName} at ${artifact.pathParts.join('/')}: ${errorMessage(error)}`);
1007
- }
1008
- }
1009
- if (manifest.framework.state === 'initialized') {
1010
- issues.push(...await validateFrameworkInstallation(projectRoot, {
1011
- workflow: manifest.framework.adapter,
1012
- agents: manifest.project.agents,
1013
- ...(manifest.project.defaultAgent ? { defaultAgent: manifest.project.defaultAgent } : {})
1014
- }));
1015
- }
1016
- if (manifest.project.workload.kind === 'power-apps-code-app') {
1017
- const requiredArtifacts = [
1018
- { logicalName: 'power-apps-package', pathParts: ['package.json'] },
1019
- { logicalName: 'power-apps-lock', pathParts: ['package-lock.json'] },
1020
- { logicalName: 'power-apps-readme', pathParts: ['README.md'] },
1021
- { logicalName: 'power-apps-attribution', pathParts: ['THIRD_PARTY_NOTICES.md'] }
1022
- ];
1023
- for (const required of requiredArtifacts) {
1024
- const artifact = manifest.projectArtifacts.find((entry) => entry.logicalName === required.logicalName);
1025
- if (!artifact) {
1026
- issues.push(`Missing required Power Apps manifest artifact ${required.logicalName} at ${required.pathParts.join('/')}`);
1027
- }
1028
- else if (artifact.pathParts.join('/') !== required.pathParts.join('/')) {
1029
- issues.push(`Power Apps manifest artifact ${required.logicalName} must target ${required.pathParts.join('/')}`);
1030
- }
1031
- }
1032
- }
1033
- return issues;
1034
- }
1035
- export function manifestDisplayPath(pathParts) {
1036
- return pathParts.join('/');
1037
- }
1038
- export async function findProjectRoot(startDir) {
1039
- let current = path.resolve(startDir);
1040
- while (true) {
1041
- try {
1042
- await access(path.join(current, 'liftoff.manifest.json'));
1043
- return current;
1044
- }
1045
- catch (error) {
1046
- if (errorCode(error) !== 'ENOENT' && errorCode(error) !== 'ENOTDIR') {
1047
- throw new FileSystemError(`Unable to inspect ${current} for a Liftoff manifest: ${errorMessage(error)}`);
1048
- }
1049
- const parent = path.dirname(current);
1050
- if (parent === current) {
1051
- return undefined;
1052
- }
1053
- current = parent;
1054
- }
1055
- }
1056
- }
1057
- export async function readProjectFile(projectRoot, pathParts) {
1058
- const targetPath = await resolveProjectPath(projectRoot, pathParts);
1059
- try {
1060
- return await readFile(targetPath);
1061
- }
1062
- catch (error) {
1063
- if (errorCode(error) === 'ENOENT') {
1064
- return undefined;
1065
- }
1066
- throw new FileSystemError(`Unable to read ${pathParts.join('/')}: ${errorMessage(error)}`);
1067
- }
1068
- }
1069
- export async function writeProjectFile(projectRoot, pathParts, content) {
1070
- const targetPath = await resolveProjectPath(projectRoot, pathParts);
1071
- const temporaryPath = path.join(path.dirname(targetPath), `.${path.basename(targetPath)}.liftoff-${process.pid}-${randomUUID()}.tmp`);
1072
- let temporaryFileWritten = false;
1073
- try {
1074
- await mkdir(path.dirname(targetPath), { recursive: true });
1075
- await writeFile(temporaryPath, content, { flag: 'wx' });
1076
- temporaryFileWritten = true;
1077
- await rename(temporaryPath, targetPath);
1078
- }
1079
- catch (error) {
1080
- let cleanupFailure;
1081
- if (temporaryFileWritten) {
1082
- try {
1083
- await unlink(temporaryPath);
1084
- }
1085
- catch (cleanupError) {
1086
- if (errorCode(cleanupError) !== 'ENOENT') {
1087
- cleanupFailure = errorMessage(cleanupError);
1088
- }
1089
- }
1090
- }
1091
- const cleanupDetail = cleanupFailure ? ` Temporary-file cleanup also failed: ${cleanupFailure}` : '';
1092
- throw new FileSystemError(`Unable to write ${pathParts.join('/')}: ${errorMessage(error)}${cleanupDetail}`);
1093
- }
1094
- }
1095
- export async function deleteProjectFile(projectRoot, pathParts) {
1096
- const targetPath = await resolveProjectPath(projectRoot, pathParts);
1097
- try {
1098
- await unlink(targetPath);
1099
- }
1100
- catch (error) {
1101
- if (errorCode(error) === 'ENOENT') {
1102
- return;
1103
- }
1104
- throw new FileSystemError(`Unable to delete ${pathParts.join('/')}: ${errorMessage(error)}`);
1105
- }
1106
- }
1107
- export async function captureProjectFileSnapshot(projectRoot, pathParts) {
1108
- const targetPath = await resolveProjectPath(projectRoot, pathParts);
1109
- try {
1110
- const details = await lstat(targetPath);
1111
- if (!details.isFile()) {
1112
- throw new FileSystemError(`Project update target must be a regular file: ${pathParts.join('/')}`);
1113
- }
1114
- return {
1115
- pathParts,
1116
- content: await readFile(targetPath),
1117
- mode: details.mode & 0o7777
1118
- };
1119
- }
1120
- catch (error) {
1121
- if (errorCode(error) === 'ENOENT') {
1122
- return { pathParts };
1123
- }
1124
- throw error;
1125
- }
1126
- }
1127
- async function assertProjectFileSnapshot(projectRoot, snapshot) {
1128
- const current = await captureProjectFileSnapshot(projectRoot, snapshot.pathParts);
1129
- if ((snapshot.content === undefined) !== (current.content === undefined) ||
1130
- snapshot.mode !== current.mode ||
1131
- snapshot.content?.equals(current.content) === false) {
1132
- throw new FileSystemError(`Project update target changed after review: ${snapshot.pathParts.join('/')}`);
1133
- }
1134
- }
1135
- async function assertAppliedMutationCurrent(projectRoot, mutation) {
1136
- const current = await captureProjectFileSnapshot(projectRoot, mutation.pathParts);
1137
- if (mutation.type === 'delete') {
1138
- if (current.content !== undefined) {
1139
- throw new FileSystemError(`Project update target changed before rollback: ${mutation.pathParts.join('/')}`);
1140
- }
1141
- return;
1142
- }
1143
- if (current.content === undefined ||
1144
- !current.content.equals(Buffer.from(mutation.content, 'utf8'))) {
1145
- throw new FileSystemError(`Project update target changed before rollback: ${mutation.pathParts.join('/')}`);
1146
- }
1147
- }
1148
- async function missingMutationParents(projectRoot, pathParts) {
1149
- const missing = [];
1150
- for (let index = 1; index < pathParts.length; index += 1) {
1151
- const parentParts = pathParts.slice(0, index);
1152
- const parentPath = await resolveProjectPath(projectRoot, parentParts);
1153
- try {
1154
- const details = await lstat(parentPath);
1155
- if (!details.isDirectory() && !details.isSymbolicLink()) {
1156
- throw new FileSystemError(`Artifact path parent is not a directory: ${parentParts.join('/')}`);
1157
- }
1158
- }
1159
- catch (error) {
1160
- if (errorCode(error) === 'ENOENT') {
1161
- missing.push(parentParts);
1162
- continue;
1163
- }
1164
- throw error;
1165
- }
1166
- }
1167
- return missing;
1168
- }
1169
- export async function applyProjectFileTransaction(projectRoot, mutations, options = {}) {
1170
- const snapshots = new Map();
1171
- const preconditions = new Map();
1172
- const missingParents = new Map();
1173
- for (const snapshot of options.preconditions ?? []) {
1174
- const key = snapshot.pathParts.join('\0');
1175
- if (preconditions.has(key)) {
1176
- throw new FileSystemError(`Project update contains duplicate preconditions for ${snapshot.pathParts.join('/')}.`);
1177
- }
1178
- preconditions.set(key, snapshot);
1179
- }
1180
- for (const mutation of mutations) {
1181
- const key = mutation.pathParts.join('\0');
1182
- if (snapshots.has(key)) {
1183
- throw new FileSystemError(`Project update contains duplicate mutations for ${mutation.pathParts.join('/')}.`);
1184
- }
1185
- snapshots.set(key, preconditions.get(key) ??
1186
- await captureProjectFileSnapshot(projectRoot, mutation.pathParts));
1187
- if (mutation.type === 'write') {
1188
- for (const parentParts of await missingMutationParents(projectRoot, mutation.pathParts)) {
1189
- missingParents.set(parentParts.join('\0'), parentParts);
1190
- }
1191
- }
1192
- }
1193
- for (const snapshot of preconditions.values()) {
1194
- await assertProjectFileSnapshot(projectRoot, snapshot);
1195
- }
1196
- const applied = [];
1197
- try {
1198
- for (const [index, mutation] of mutations.entries()) {
1199
- await options.onBeforeMutation?.(mutation, index);
1200
- await assertProjectFileSnapshot(projectRoot, snapshots.get(mutation.pathParts.join('\0')));
1201
- if (mutation.type === 'write') {
1202
- await writeProjectFile(projectRoot, mutation.pathParts, mutation.content);
1203
- }
1204
- else {
1205
- await deleteProjectFile(projectRoot, mutation.pathParts);
1206
- }
1207
- applied.push(mutation);
1208
- }
1209
- }
1210
- catch (error) {
1211
- const rollbackFailures = [];
1212
- for (const mutation of [...applied].reverse()) {
1213
- const snapshot = snapshots.get(mutation.pathParts.join('\0'));
1214
- try {
1215
- await assertAppliedMutationCurrent(projectRoot, mutation);
1216
- if (snapshot.content === undefined) {
1217
- await deleteProjectFile(projectRoot, mutation.pathParts);
1218
- }
1219
- else {
1220
- await writeProjectFile(projectRoot, mutation.pathParts, snapshot.content);
1221
- await chmod(await resolveProjectPath(projectRoot, mutation.pathParts), snapshot.mode);
1222
- }
1223
- }
1224
- catch (rollbackError) {
1225
- rollbackFailures.push(`${mutation.pathParts.join('/')}: ${errorMessage(rollbackError)}`);
1226
- }
1227
- }
1228
- const parents = [...missingParents.values()]
1229
- .sort((left, right) => right.length - left.length);
1230
- for (const parentParts of parents) {
1231
- try {
1232
- await rmdir(await resolveProjectPath(projectRoot, parentParts));
1233
- }
1234
- catch (rollbackError) {
1235
- if (errorCode(rollbackError) !== 'ENOENT') {
1236
- rollbackFailures.push(`${parentParts.join('/')}: ${errorMessage(rollbackError)}`);
1237
- }
1238
- }
1239
- }
1240
- const rollbackDetail = rollbackFailures.length === 0
1241
- ? 'All applied changes were rolled back.'
1242
- : `Rollback was incomplete:\n${rollbackFailures.map((failure) => `- ${failure}`).join('\n')}`;
1243
- throw new ProjectFileTransactionError(`Project update failed: ${errorMessage(error)} ${rollbackDetail}`, rollbackFailures);
1244
- }
1245
- }
1
+ export { FileSystemError } from './domain/project/errors.js';
2
+ export { validateArtifactPathParts, manifestDisplayPath } from './domain/project/paths.js';
3
+ export { SUPPORTED_MANIFEST_VERSIONS, manifestHadFilteredLegacyNonDurableOwnership } from './domain/project/manifest/reader.js';
4
+ export { parseManifest, normalizeManifestProject, normalizeManifestFramework, loadManifest } from './application/project/manifest.js';
5
+ export { validateGeneratedProject } from './application/diagnose/generated-project.js';
6
+ export { resolveTargetRoot, findProjectRoot } from './adapters/filesystem/project-discovery.js';
7
+ export { artifactPath, resolveProjectPath } from './adapters/filesystem/project-paths.js';
8
+ export { assertNewOrEmptyDirectory, writeArtifacts, readProjectFile, writeProjectFile, deleteProjectFile } from './adapters/filesystem/project-files.js';
9
+ export { applyProjectFileTransaction, captureProjectFileSnapshot, ProjectFileTransactionError } from './adapters/filesystem/project-transaction.js';
1246
10
  //# sourceMappingURL=file-system.js.map