@typecad/cuttlefish 1.0.0-alpha.13 → 1.0.0-alpha.15

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 (343) hide show
  1. package/README.md +82 -79
  2. package/assets/editor-extensions/typecad-debug/README.md +141 -0
  3. package/assets/editor-extensions/typecad-debug/out/extension.js +347 -0
  4. package/assets/editor-extensions/typecad-debug/out/extension.js.map +1 -0
  5. package/assets/editor-extensions/typecad-debug/package.json +76 -0
  6. package/assets/editor-extensions/typecad-ui/LICENSE +27 -0
  7. package/assets/editor-extensions/typecad-ui/README.md +52 -0
  8. package/assets/editor-extensions/typecad-ui/file-icon-dark.png +0 -0
  9. package/assets/editor-extensions/typecad-ui/file-icon-light.png +0 -0
  10. package/assets/editor-extensions/typecad-ui/icon.png +0 -0
  11. package/assets/editor-extensions/typecad-ui/language-configuration.json +43 -0
  12. package/assets/editor-extensions/typecad-ui/package.json +54 -0
  13. package/assets/editor-extensions/typecad-ui/snippets/typecad-ui.json +80 -0
  14. package/assets/editor-extensions/typecad-ui/syntaxes/markdown-ui.json +45 -0
  15. package/assets/editor-extensions/typecad-ui/syntaxes/typecad-ui.tmLanguage.json +1269 -0
  16. package/dist/api/config.d.ts +30 -27
  17. package/dist/api/schema/types.d.ts +20 -3
  18. package/dist/api/shared/async-runtime-static.d.ts +1 -1
  19. package/dist/api/shared/async-runtime-static.js +5 -5
  20. package/dist/api/shared/async-types.d.ts +3 -2
  21. package/dist/api/shared/board-catalog.d.ts +2 -0
  22. package/dist/api/shared/board-catalog.js +6 -0
  23. package/dist/api/shared/board-resolver.d.ts +4 -7
  24. package/dist/api/shared/board-resolver.js +2 -1
  25. package/dist/api/shared/coop-scheduler.d.ts +1 -1
  26. package/dist/api/shared/coop-scheduler.js +1 -1
  27. package/dist/api/shared/cpp-type-ir.d.ts +1 -1
  28. package/dist/api/shared/cpp-type-ir.js +1 -1
  29. package/dist/api/shared/display-profile.d.ts +1 -1
  30. package/dist/api/shared/display-profile.js +3 -5
  31. package/dist/api/shared/framework-manifest-registry.d.ts +1 -1
  32. package/dist/api/shared/framework-manifest-registry.js +0 -1
  33. package/dist/api/shared/framework-manifest.d.ts +65 -507
  34. package/dist/api/shared/framework-manifest.js +6 -11
  35. package/dist/api/shared/hal-op-ir.d.ts +294 -685
  36. package/dist/api/shared/hal-op-ir.js +31 -65
  37. package/dist/api/shared/index.d.ts +0 -4
  38. package/dist/api/shared/index.js +1 -7
  39. package/dist/api/shared/ir-core.d.ts +6 -0
  40. package/dist/api/shared/ir-declarations.d.ts +4 -0
  41. package/dist/api/shared/ir.d.ts +1 -3
  42. package/dist/api/shared/platform-strategy.d.ts +14 -37
  43. package/dist/api/shared/polyfill-helper-registry.js +0 -5
  44. package/dist/api/shared/polyfill-types.js +0 -27
  45. package/dist/api/shared/promise-runtime.js +3 -3
  46. package/dist/api/shared/spdx-licenses.d.ts +12 -10
  47. package/dist/api/shared/spdx-licenses.js +1 -1
  48. package/dist/api/shared/toolchain-types.d.ts +1 -1
  49. package/dist/api/shared/toolchain-types.js +3 -3
  50. package/dist/api/shared/types.d.ts +0 -3
  51. package/dist/api/shared/validate-framework-manifest.js +13 -33
  52. package/dist/board-catalog/dts-reader.d.ts +220 -0
  53. package/dist/board-catalog/dts-reader.js +1052 -0
  54. package/dist/board-catalog/index.d.ts +11 -0
  55. package/dist/board-catalog/index.js +12 -0
  56. package/dist/board-catalog/pinconfig.d.ts +48 -0
  57. package/dist/board-catalog/pinconfig.js +263 -0
  58. package/dist/board-catalog/sdk.d.ts +82 -0
  59. package/dist/board-catalog/sdk.js +180 -0
  60. package/dist/board-catalog/store.d.ts +196 -0
  61. package/dist/board-catalog/store.js +363 -0
  62. package/dist/board-catalog/types.d.ts +233 -0
  63. package/dist/board-catalog/types.js +7 -0
  64. package/dist/board-catalog/walker.d.ts +112 -0
  65. package/dist/board-catalog/walker.js +1101 -0
  66. package/dist/cli-utils.d.ts +2 -2
  67. package/dist/cli-utils.js +5 -5
  68. package/dist/cli.js +227 -244
  69. package/dist/config-loader.d.ts +14 -19
  70. package/dist/config-loader.js +242 -78
  71. package/dist/config-schema.d.ts +19 -227
  72. package/dist/config-schema.js +9 -8
  73. package/dist/contract/board-generator.d.ts +4 -4
  74. package/dist/contract/board-generator.js +16 -5
  75. package/dist/contract/contract-parser.d.ts +36 -127
  76. package/dist/contract/contract-parser.js +76 -0
  77. package/dist/contract/index.d.ts +1 -1
  78. package/dist/contract/index.js +70 -21
  79. package/dist/create/active-board-catalog.d.ts +9 -0
  80. package/dist/create/active-board-catalog.js +94 -0
  81. package/dist/create/board-catalog.generated.d.ts +96 -0
  82. package/dist/create/board-catalog.generated.js +128737 -0
  83. package/dist/create/board-search.d.ts +23 -0
  84. package/dist/create/board-search.js +88 -0
  85. package/dist/create/editor-integration.d.ts +32 -0
  86. package/dist/create/editor-integration.js +257 -0
  87. package/dist/create/eslint-rules-template.d.ts +2 -2
  88. package/dist/create/framework-catalog.d.ts +48 -8
  89. package/dist/create/framework-catalog.js +58 -44
  90. package/dist/create/index.d.ts +8 -13
  91. package/dist/create/index.js +5 -9
  92. package/dist/create/mcu-target.d.ts +39 -0
  93. package/dist/create/mcu-target.js +80 -0
  94. package/dist/create/pack-targets.d.ts +19 -0
  95. package/dist/create/pack-targets.js +56 -0
  96. package/dist/create/{init-scaffold.d.ts → scaffold.d.ts} +8 -11
  97. package/dist/create/{init-scaffold.js → scaffold.js} +28 -87
  98. package/dist/create/templates.d.ts +51 -0
  99. package/dist/create/{init-templates.js → templates.js} +135 -116
  100. package/dist/create/wizard.d.ts +19 -0
  101. package/dist/create/wizard.js +314 -0
  102. package/dist/debug/preprocessor.d.ts +5 -0
  103. package/dist/debug/preprocessor.js +31 -7
  104. package/dist/diagnostics/diagnostics-report.d.ts +1 -1
  105. package/dist/diagnostics/diagnostics-report.js +3 -11
  106. package/dist/diagnostics/json-schema.d.ts +1 -1
  107. package/dist/diagnostics/md-writer.js +2 -2
  108. package/dist/diagnostics/mermaid-builder.d.ts +1 -1
  109. package/dist/diagnostics/mermaid-builder.js +1 -1
  110. package/dist/emit/cpp-emitter.js +4 -1
  111. package/dist/emit/emitters/class-emitter.js +32 -4
  112. package/dist/emit/emitters/emitter-context.d.ts +11 -0
  113. package/dist/emit/emitters/entrypoint-synthesizer.js +1 -1
  114. package/dist/emit/emitters/function-emitter-impl.js +33 -17
  115. package/dist/emit/emitters/output-finalizer.d.ts +7 -0
  116. package/dist/emit/emitters/output-finalizer.js +31 -23
  117. package/dist/emit/emitters/setup.js +147 -63
  118. package/dist/emit/emitters/top-level-prep.js +1 -1
  119. package/dist/emit/emitters/type-decl-emitter.js +13 -8
  120. package/dist/emit/emitters/ui-emitter.js +1 -1
  121. package/dist/emit/expression-renderer.d.ts +3 -3
  122. package/dist/emit/expression-renderer.js +17 -7
  123. package/dist/emit/route-hal-op.js +17 -48
  124. package/dist/emit/snprintf-helpers.d.ts +1 -3
  125. package/dist/emit/snprintf-helpers.js +1 -39
  126. package/dist/emit/statement-renderer.d.ts +0 -4
  127. package/dist/emit/statement-renderer.js +54 -45
  128. package/dist/emit/utils/async-state-machine.d.ts +16 -9
  129. package/dist/emit/utils/async-state-machine.js +56 -54
  130. package/dist/emit/utils/comment-helpers.d.ts +0 -8
  131. package/dist/emit/utils/comment-helpers.js +1 -1
  132. package/dist/emit/utils/cpp-helpers.d.ts +2 -2
  133. package/dist/emit/utils/cpp-helpers.js +2 -2
  134. package/dist/emit/utils/hal-op-cpp-type.js +4 -6
  135. package/dist/emit/utils/index.d.ts +3 -3
  136. package/dist/emit/utils/index.js +3 -3
  137. package/dist/emit/utils/type-inference.d.ts +1 -29
  138. package/dist/emit/utils/type-inference.js +0 -75
  139. package/dist/framework-package.d.ts +3 -0
  140. package/dist/framework-package.js +23 -6
  141. package/dist/framework-registry.d.ts +5 -5
  142. package/dist/frameworks/native/graphics/terminal-preview.d.ts +5 -0
  143. package/dist/frameworks/native/graphics/terminal-preview.js +33 -0
  144. package/dist/frameworks/native/index.d.ts +3 -0
  145. package/dist/frameworks/native/index.js +15 -0
  146. package/dist/frameworks/native/native-compile.d.ts +17 -0
  147. package/dist/frameworks/native/native-compile.js +170 -0
  148. package/dist/frameworks/native/native-config.d.ts +41 -0
  149. package/dist/frameworks/native/native-config.js +7 -0
  150. package/dist/frameworks/native/strategy.d.ts +97 -0
  151. package/dist/frameworks/native/strategy.js +664 -0
  152. package/dist/ir/board-resolver.d.ts +18 -23
  153. package/dist/ir/board-resolver.js +43 -401
  154. package/dist/ir/build-ir-state.d.ts +3 -0
  155. package/dist/ir/build-ir-state.js +42 -0
  156. package/dist/ir/build-ir.d.ts +1 -1
  157. package/dist/ir/build-ir.js +64 -45
  158. package/dist/ir/declaration-builders.js +1 -0
  159. package/dist/ir/expression-to-ir.js +17 -10
  160. package/dist/ir/hal/hal-emitter.d.ts +0 -2
  161. package/dist/ir/hal/hal-emitter.js +92 -41
  162. package/dist/ir/hal/hal-parser.d.ts +5 -5
  163. package/dist/ir/hal/hal-parser.js +128 -36
  164. package/dist/ir/hal/hal-plugins.d.ts +0 -1
  165. package/dist/ir/hal/hal-plugins.js +425 -716
  166. package/dist/ir/heap-analysis.js +2 -41
  167. package/dist/ir/identifier-collector.js +32 -20
  168. package/dist/ir/network-validation.js +7 -11
  169. package/dist/ir/ownership-analysis.js +75 -13
  170. package/dist/ir/peripheral-usage.d.ts +8 -2
  171. package/dist/ir/peripheral-usage.js +78 -149
  172. package/dist/ir/peripheral-validation.js +23 -3
  173. package/dist/ir/pin-capability-validation.d.ts +12 -0
  174. package/dist/ir/pin-capability-validation.js +61 -97
  175. package/dist/ir/pin-mode-validation.js +5 -5
  176. package/dist/ir/program-analysis.d.ts +8 -15
  177. package/dist/ir/program-analysis.js +140 -323
  178. package/dist/ir/pulldown-validation.js +11 -4
  179. package/dist/ir/resource-analysis.js +15 -3
  180. package/dist/ir/statement-to-ir.js +0 -6
  181. package/dist/ir/timing-validation.js +1 -1
  182. package/dist/ir/transformers/array-methods.js +11 -5
  183. package/dist/ir/transformers/call-statement.js +20 -87
  184. package/dist/ir/transformers/callback-context-registry.d.ts +1 -1
  185. package/dist/ir/transformers/callback-context-registry.js +8 -16
  186. package/dist/ir/transformers/control-flow.js +0 -13
  187. package/dist/ir/transformers/expressions.js +10 -9
  188. package/dist/ir/transformers/hal-call-resolver.d.ts +2 -2
  189. package/dist/ir/transformers/hal-call-resolver.js +24 -10
  190. package/dist/ir/transformers/hal-emit-helpers.d.ts +2 -2
  191. package/dist/ir/transformers/hal-emit-helpers.js +2 -2
  192. package/dist/ir/transformers/namespace-methods.js +1 -1
  193. package/dist/ir/transformers/ui-callback-lowering.d.ts +0 -2
  194. package/dist/ir/transformers/ui-callback-lowering.js +0 -24
  195. package/dist/ir/transformers/variables.js +338 -42
  196. package/dist/ir/try-catch-validation.js +4 -3
  197. package/dist/ir/type-resolution.js +15 -4
  198. package/dist/ir/validation-orchestrator.js +0 -9
  199. package/dist/libdef/cpp-to-decl.d.ts +0 -9
  200. package/dist/libdef/cpp-to-decl.js +0 -72
  201. package/dist/libdef/registry.d.ts +0 -1
  202. package/dist/libdef/registry.js +1 -24
  203. package/dist/library/catalog.d.ts +35 -0
  204. package/dist/library/catalog.js +67 -0
  205. package/dist/library/cli.d.ts +2 -0
  206. package/dist/library/cli.js +168 -0
  207. package/dist/library/init.d.ts +25 -0
  208. package/dist/library/init.js +397 -0
  209. package/dist/library/install.d.ts +9 -0
  210. package/dist/library/install.js +80 -0
  211. package/dist/library/registry-search.d.ts +34 -0
  212. package/dist/library/registry-search.js +50 -0
  213. package/dist/library/validate.d.ts +17 -0
  214. package/dist/library/validate.js +175 -0
  215. package/dist/library-packages.d.ts +95 -0
  216. package/dist/library-packages.js +275 -0
  217. package/dist/mapping/peripheral-names.js +9 -4
  218. package/dist/mapping/source-map.d.ts +3 -4
  219. package/dist/mapping/source-map.js +12 -18
  220. package/dist/orchestrator/graph-builder.d.ts +3 -3
  221. package/dist/orchestrator/graph-builder.js +29 -12
  222. package/dist/orchestrator/type-checker.d.ts +3 -3
  223. package/dist/orchestrator/type-checker.js +3 -3
  224. package/dist/platform/async-runtime.js +1 -1
  225. package/dist/platform/coop-scheduler-runtime.js +2 -2
  226. package/dist/platform/generic-debug-codegen.d.ts +36 -0
  227. package/dist/platform/generic-debug-codegen.js +185 -0
  228. package/dist/platform/generic-strategy.d.ts +3 -4
  229. package/dist/platform/generic-strategy.js +5 -80
  230. package/dist/platform/index.js +1 -1
  231. package/dist/platform/registry.d.ts +2 -2
  232. package/dist/platform/registry.js +2 -2
  233. package/dist/preview/client.js +17 -3
  234. package/dist/safety/asil-decorators.d.ts +32 -0
  235. package/dist/safety/asil-decorators.js +32 -0
  236. package/dist/safety/authoring.d.ts +85 -0
  237. package/dist/safety/authoring.js +61 -0
  238. package/dist/safety/engine.d.ts +3 -0
  239. package/dist/safety/engine.js +83 -0
  240. package/dist/safety/hal/ops.d.ts +48 -0
  241. package/dist/safety/hal/ops.js +46 -0
  242. package/dist/safety/iso26262/analyze.d.ts +13 -0
  243. package/dist/safety/iso26262/analyze.js +29 -0
  244. package/dist/safety/iso26262/asil.d.ts +10 -0
  245. package/dist/safety/iso26262/asil.js +22 -0
  246. package/dist/safety/iso26262/collect.d.ts +14 -0
  247. package/dist/safety/iso26262/collect.js +137 -0
  248. package/dist/safety/iso26262/goto-checker.d.ts +10 -0
  249. package/dist/safety/iso26262/goto-checker.js +72 -0
  250. package/dist/safety/iso26262/heap-checker.d.ts +7 -0
  251. package/dist/safety/iso26262/heap-checker.js +57 -0
  252. package/dist/safety/iso26262/init-checker.d.ts +13 -0
  253. package/dist/safety/iso26262/init-checker.js +127 -0
  254. package/dist/safety/iso26262/loop-checker.d.ts +7 -0
  255. package/dist/safety/iso26262/loop-checker.js +86 -0
  256. package/dist/safety/iso26262/recursion-checker.d.ts +9 -0
  257. package/dist/safety/iso26262/recursion-checker.js +134 -0
  258. package/dist/safety/iso26262/sidecar-writer.d.ts +25 -0
  259. package/dist/safety/iso26262/sidecar-writer.js +13 -0
  260. package/dist/safety/passes/pinMode-intercept.d.ts +3 -0
  261. package/dist/safety/passes/pinMode-intercept.js +60 -0
  262. package/dist/safety/runtime/mode-table.d.ts +5 -0
  263. package/dist/safety/runtime/mode-table.js +55 -0
  264. package/dist/safety/runtime/polyfills.d.ts +6 -0
  265. package/dist/safety/runtime/polyfills.js +12 -0
  266. package/dist/safety/runtime/result-struct.d.ts +11 -0
  267. package/dist/safety/runtime/result-struct.js +98 -0
  268. package/dist/safety/runtime/safe-int.d.ts +33 -0
  269. package/dist/safety/runtime/safe-int.js +170 -0
  270. package/dist/safety/runtime/safe-traits.d.ts +4 -0
  271. package/dist/safety/runtime/safe-traits.js +95 -0
  272. package/dist/safety/runtime/safe-variable.d.ts +39 -0
  273. package/dist/safety/runtime/safe-variable.js +226 -0
  274. package/dist/safety/runtime/vote.d.ts +8 -0
  275. package/dist/safety/runtime/vote.js +92 -0
  276. package/dist/safety/runtime/write-verify.d.ts +8 -0
  277. package/dist/safety/runtime/write-verify.js +71 -0
  278. package/dist/safety/safe-int-types.d.ts +45 -0
  279. package/dist/safety/safe-int-types.js +25 -0
  280. package/dist/safety/safe-variable-types.d.ts +29 -0
  281. package/dist/safety/safe-variable-types.js +19 -0
  282. package/dist/safety/safety-bridge.d.ts +5 -6
  283. package/dist/safety/safety-bridge.js +15 -28
  284. package/dist/safety/sidecar-bridge.d.ts +1 -3
  285. package/dist/safety/sidecar-bridge.js +3 -20
  286. package/dist/safety/specifiers.d.ts +6 -0
  287. package/dist/safety/specifiers.js +14 -0
  288. package/dist/safety-hook.d.ts +8 -7
  289. package/dist/safety-hook.js +12 -13
  290. package/dist/testing.d.ts +5 -6
  291. package/dist/testing.js +5 -5
  292. package/dist/transpile/resolution.d.ts +1 -1
  293. package/dist/transpile/resolution.js +18 -9
  294. package/dist/transpile/test-pins.d.ts +18 -0
  295. package/dist/transpile/test-pins.js +142 -0
  296. package/dist/transpile.d.ts +1 -2
  297. package/dist/transpile.js +90 -29
  298. package/dist/types.d.ts +45 -26
  299. package/dist/ui-hook.d.ts +1 -1
  300. package/dist/utils/cli.d.ts +2 -2
  301. package/dist/utils/cli.js +160 -157
  302. package/dist/utils/fs.d.ts +3 -4
  303. package/dist/utils/fs.js +4 -5
  304. package/dist/utils/strings.d.ts +0 -1
  305. package/dist/utils/strings.js +0 -8
  306. package/dist/utils/ui.d.ts +5 -2
  307. package/dist/utils/ui.js +13 -3
  308. package/package.json +158 -149
  309. package/dist/add-preset.d.ts +0 -4
  310. package/dist/add-preset.js +0 -74
  311. package/dist/api/shared/async-symbol-detector.d.ts +0 -11
  312. package/dist/api/shared/async-symbol-detector.js +0 -140
  313. package/dist/api/shared/native-display-op-resolver.d.ts +0 -10
  314. package/dist/api/shared/native-display-op-resolver.js +0 -64
  315. package/dist/create/board-checklist.d.ts +0 -2
  316. package/dist/create/board-checklist.js +0 -52
  317. package/dist/create/board-codegen.d.ts +0 -9
  318. package/dist/create/board-codegen.js +0 -238
  319. package/dist/create/board-generators.d.ts +0 -12
  320. package/dist/create/board-generators.js +0 -651
  321. package/dist/create/board-spec.d.ts +0 -637
  322. package/dist/create/board-spec.js +0 -214
  323. package/dist/create/init-templates.d.ts +0 -28
  324. package/dist/create/init-wizard.d.ts +0 -8
  325. package/dist/create/init-wizard.js +0 -182
  326. package/dist/install/framework-catalog.d.ts +0 -53
  327. package/dist/install/framework-catalog.js +0 -107
  328. package/dist/install/handle-install.d.ts +0 -35
  329. package/dist/install/handle-install.js +0 -177
  330. package/dist/install/index.d.ts +0 -4
  331. package/dist/install/index.js +0 -3
  332. package/dist/ir/pin-state-tracking.d.ts +0 -58
  333. package/dist/ir/pin-state-tracking.js +0 -182
  334. package/dist/ir/pwm-timer-sharing.d.ts +0 -4
  335. package/dist/ir/pwm-timer-sharing.js +0 -94
  336. package/dist/ir/timer0-pwm-timing-conflict.d.ts +0 -4
  337. package/dist/ir/timer0-pwm-timing-conflict.js +0 -72
  338. package/dist/libdef/component-decls.d.ts +0 -2
  339. package/dist/libdef/component-decls.js +0 -6
  340. package/dist/libdef/component-discovery.d.ts +0 -43
  341. package/dist/libdef/component-discovery.js +0 -83
  342. package/dist/theme-tokens.d.ts +0 -22
  343. package/dist/theme-tokens.js +0 -172
@@ -0,0 +1,11 @@
1
+ export type { BoardDataEntry } from './types.js';
2
+ export { readBoardDts } from './dts-reader.js';
3
+ export { harvestPinconfig } from './pinconfig.js';
4
+ export type { PinconfigFacts, PinconfigAdcRoute, PinconfigDacRoute, PinconfigPwmRoute } from './pinconfig.js';
5
+ export type { DtsBoardFacts, DtsGpioRef, DtsGpioNode, DtsConnector, DtsPwmLed, } from './dts-reader.js';
6
+ export { walkBoardCatalog, boardYmlSocIndex, boardYmlTargets, boardProbeMethods, socBusLabelsFromTree, zephyrVersionOf, gitHeadOf, } from './walker.js';
7
+ export type { BoardYmlSocIndex, BoardYmlTarget, BoardCatalogProvenance, BoardCatalogWalkResult, VariantPin, } from './walker.js';
8
+ export { GENERATOR_REV, isZephyrBase, locateZephyrBaseCheap, micromambaZephyrBase, overlayPathFor, readBoardCatalogOverlayFile, loadBoardCatalogOverlay, resetBoardCatalogOverlayCache, isOverlayStale, ensureFreshBoardCatalog, syncBoardCatalog, diffBoardCatalogs, findBoardInCatalog, boardRecordFingerprint, factsFingerprint, activeBoardCatalog, resetActiveBoardCatalog, } from './store.js';
9
+ export type { BoardCatalogOverlay, BoardCatalogSyncReport, BoardCatalogSnapshot, BoardCatalogEnsureResult, } from './store.js';
10
+ export { PINNED_ZEPHYR_MANIFEST_REV, PINNED_ZEPHYR_SDK_VERSION, ZEPHYR_INSTALL_CMD, sdkFingerprint, findToolchainSdk, checkZephyrSdk, assertZephyrSdkForCreate, formatZephyrSdkFound, } from './sdk.js';
11
+ export type { SdkCheck, SdkDiscoverySource } from './sdk.js';
@@ -0,0 +1,12 @@
1
+ // ---------------------------------------------------------------------------
2
+ // board-catalog/index.ts — public surface of the board catalog tooling.
3
+ //
4
+ // The catalog is generated from the user's own Zephyr tree (walker over the
5
+ // tolerant devicetree reader) and lives as a machine-local overlay. There
6
+ // is no compiled-in board database.
7
+ // ---------------------------------------------------------------------------
8
+ export { readBoardDts } from './dts-reader.js';
9
+ export { harvestPinconfig } from './pinconfig.js';
10
+ export { walkBoardCatalog, boardYmlSocIndex, boardYmlTargets, boardProbeMethods, socBusLabelsFromTree, zephyrVersionOf, gitHeadOf, } from './walker.js';
11
+ export { GENERATOR_REV, isZephyrBase, locateZephyrBaseCheap, micromambaZephyrBase, overlayPathFor, readBoardCatalogOverlayFile, loadBoardCatalogOverlay, resetBoardCatalogOverlayCache, isOverlayStale, ensureFreshBoardCatalog, syncBoardCatalog, diffBoardCatalogs, findBoardInCatalog, boardRecordFingerprint, factsFingerprint, activeBoardCatalog, resetActiveBoardCatalog, } from './store.js';
12
+ export { PINNED_ZEPHYR_MANIFEST_REV, PINNED_ZEPHYR_SDK_VERSION, ZEPHYR_INSTALL_CMD, sdkFingerprint, findToolchainSdk, checkZephyrSdk, assertZephyrSdkForCreate, formatZephyrSdkFound, } from './sdk.js';
@@ -0,0 +1,48 @@
1
+ /** A normalized ADC route from a pinconfig table (raw harvest form). */
2
+ export interface PinconfigAdcRoute {
3
+ /** ADC controller nodelabel the route maps to ('adc0', 'adc1', 'adc'). */
4
+ readonly source: string;
5
+ /** Input channel number. */
6
+ readonly channel: number;
7
+ /** GPIO port letter ('A', …) or digit ('0' for a flat gpio0 controller). */
8
+ readonly port: string;
9
+ readonly bit: number;
10
+ /** Pinmux token when the family's ADC needs a pad mux. Absent for families
11
+ * whose analog inputs need none (Atmel SAM, Bouffalolab). */
12
+ readonly pinctrl?: string;
13
+ }
14
+ export interface PinconfigFacts {
15
+ readonly adc: PinconfigAdcRoute[];
16
+ /** DAC output routes (Atmel SAM — no pinmux group; the driver selects the
17
+ * output pad). */
18
+ readonly dac?: PinconfigDacRoute[];
19
+ /** PWM output routes (Atmel SAM tc/tcc waveform outputs — the WO pinmux
20
+ * macro is synthesized from the position+peripheral+signal triple). */
21
+ readonly pwm?: PinconfigPwmRoute[];
22
+ /** The pinconfig file that satisfied the board (provenance). */
23
+ readonly file?: string;
24
+ }
25
+ /** A normalized DAC output route from a pinconfig table (raw harvest form). */
26
+ export interface PinconfigDacRoute {
27
+ readonly source: string;
28
+ readonly channel: number;
29
+ readonly port: string;
30
+ readonly bit: number;
31
+ }
32
+ /** A normalized PWM output route from a pinconfig table. `pinctrl` is the
33
+ * synthesized SAM pinmux macro token (`PA17E_TCC2_WO1`), which the board's
34
+ * pinctrl include chain defines via SAM_PINMUX. */
35
+ export interface PinconfigPwmRoute {
36
+ readonly source: string;
37
+ readonly channel: number;
38
+ readonly port: string;
39
+ readonly bit: number;
40
+ readonly pinctrl: string;
41
+ }
42
+ /**
43
+ * Harvest ADC routes from the vendor pinconfigs for the board's SoC. The
44
+ * source is chosen by the soc segment — a data convention (like the
45
+ * letter-port families), never a board name. Undefined for SoCs without a
46
+ * pinconfig source.
47
+ */
48
+ export declare function harvestPinconfig(identifier: string, westRoot: string): PinconfigFacts | undefined;
@@ -0,0 +1,263 @@
1
+ // ---------------------------------------------------------------------------
2
+ // board-catalog/pinconfig.ts — parse the vendor `pinconfigs/*.yml` datasheet
3
+ // pin tables into normalized silicon routes.
4
+ //
5
+ // Atmel (SAM/SAM0) and Bouffalolab publish datasheet-complete pin-mux tables
6
+ // as YAML under their HAL module's `pinconfigs/` directory — the SAME files
7
+ // the vendors' own pinctrl generators consume. Each declares, per pin and per
8
+ // bonded package ("pincode"), the full list of alternate functions (ADC input
9
+ // channel, DAC output channel, timer/PWM channel, and the raw signal
10
+ // vocabulary). That is the authoritative "every pin + every function" source
11
+ // — no macro-name regexes, no generated-header churn.
12
+ //
13
+ // GigaDevice is deliberately NOT here: its board soc segment is the SERIES
14
+ // (gd32f450), not the part, so the package key this parser needs is absent
15
+ // from the soc name — the package-specific generated header (read by the
16
+ // dts-reader's dialect table) is the authoritative source for GD32.
17
+ //
18
+ // This module unifies the two grammars (they share a `series`/`pincode`/
19
+ // `pins` skeleton) and returns ADC routes in the raw harvest form the rest of
20
+ // the catalog already uses (source/channel/port/bit). DAC/PWM routes are
21
+ // deliberately NOT lowered yet — their overlay pinmux synthesis is per-family
22
+ // and still pending (tracked by the walker's coverage ledger, not shipped as
23
+ // false capability flags).
24
+ // ----------------------------------------------------------------------------
25
+ import fs from 'node:fs';
26
+ import path from 'node:path';
27
+ /** List the *.yml files in a pinconfigs dir (undefined when absent). */
28
+ function listYml(dir) {
29
+ try {
30
+ return fs.readdirSync(dir).filter((f) => f.endsWith('.yml'));
31
+ }
32
+ catch {
33
+ return undefined;
34
+ }
35
+ }
36
+ // ── Atmel SAM / SAM0 ────────────────────────────────────────────────────────
37
+ // `periph:` is a list of `[position, peripheral, signal]` triples per pin.
38
+ // Analog inputs are `[b, adc0, ain15]` — no pad mux token (the SAM ADC driver
39
+ // samples by channel; the pad is analog by function selection).
40
+ // ----------------------------------------------------------------------------
41
+ function harvestAtmel(soc, westRoot) {
42
+ const dir = path.join(westRoot, 'modules', 'hal', 'atmel', 'pinconfigs');
43
+ const files = listYml(dir);
44
+ if (!files)
45
+ return undefined;
46
+ const socLower = soc.toLowerCase();
47
+ for (const f of files) {
48
+ let text;
49
+ try {
50
+ text = fs.readFileSync(path.join(dir, f), 'utf8');
51
+ }
52
+ catch {
53
+ continue;
54
+ }
55
+ const seriesMatch = text.match(/^series:\s*\[([^\]]*)\]/m);
56
+ if (!seriesMatch)
57
+ continue;
58
+ const series = seriesMatch[1].split(',').map((s) => s.trim()).filter(Boolean);
59
+ // Series tokens encode the soc prefix either directly ('d51') or as a
60
+ // placeholder with a trailing 'X' ('4eX', '3XX', '4lsX' — the OLD SAM
61
+ // parts). A placeholder matches the bare prefix and its package letter is
62
+ // the LAST character of the soc name (sam4e16e → 'e'), unlike the modern
63
+ // parts whose package follows the series directly (samd51j19a → 'j').
64
+ const prefixes = [];
65
+ for (const s of series) {
66
+ const lower = s.toLowerCase();
67
+ if (lower.includes('-')) {
68
+ prefixes.push({ prefix: lower, placeholder: false });
69
+ prefixes.push({ prefix: lower.split('-')[0], placeholder: false });
70
+ continue;
71
+ }
72
+ if (lower.endsWith('x')) {
73
+ const stripped = lower.replace(/x+$/, '');
74
+ if (stripped)
75
+ prefixes.push({ prefix: stripped, placeholder: true });
76
+ prefixes.push({ prefix: lower, placeholder: true });
77
+ }
78
+ else {
79
+ prefixes.push({ prefix: lower, placeholder: false });
80
+ }
81
+ }
82
+ const matched = prefixes.find((p) => socLower.startsWith('sam' + p.prefix));
83
+ if (!matched)
84
+ continue;
85
+ const pincode = matched.placeholder
86
+ ? socLower.slice(-1)
87
+ : socLower.slice(('sam' + matched.prefix).length).charAt(0);
88
+ if (!/^[a-z]$/.test(pincode))
89
+ break;
90
+ const routes = [];
91
+ const dacRoutes = [];
92
+ const pwmRoutes = [];
93
+ let pin;
94
+ for (const line of text.split('\n')) {
95
+ // Atmel pin names are p<port><bits> (pa02, pa0 — the PIO prefix; the
96
+ // old SAM parts use single-digit pads).
97
+ const pinM = line.match(/^ p([a-z])(\d+):/);
98
+ if (pinM) {
99
+ pin = { name: pinM[1] + pinM[2], codes: new Set() };
100
+ continue;
101
+ }
102
+ if (!pin)
103
+ continue;
104
+ const codesM = line.match(/^ {4}pincodes:\s*\[([^\]]*)\]/);
105
+ if (codesM) {
106
+ for (const c of codesM[1].split(',').map((s) => s.trim()).filter(Boolean))
107
+ pin.codes.add(c);
108
+ continue;
109
+ }
110
+ const perM = line.match(/^ {6}- \[([a-z]),\s*(\w+),\s*(\w+)(?:,\s*\[[^\]]*\])?\]/);
111
+ // 'adc' (d2x/3x single controller), 'adc0'/'adc1' (d5x). The signal is
112
+ // `ain<N>` on the SAM0/SAMD parts and `ad<N>` on the older SAM parts.
113
+ // `adc_dac` (reference outputs) and the AFEC/ADCIFE front-ends
114
+ // (`afec0`/`adcife`) are NOT input routes on the standard adc driver.
115
+ if (perM && pin.codes.has(pincode) && /^adc\d*$/.test(perM[2])) {
116
+ const ain = perM[3].match(/^ain(\d+)$/) ?? perM[3].match(/^ad(\d+)$/);
117
+ if (ain) {
118
+ routes.push({
119
+ source: perM[2],
120
+ channel: Number(ain[1]),
121
+ port: pin.name[0].toUpperCase(),
122
+ bit: Number(pin.name.slice(1)),
123
+ });
124
+ }
125
+ }
126
+ // DAC output: `[b, dac, vout<N>]` (and the digitless `vout` on the
127
+ // single-channel parts). The YAML peripheral is `dac` but the DT
128
+ // nodelabel is `dac0` (samd2x/samd5x/… declare dac0: dac@…), so the
129
+ // source is normalized to dac0 to survive the analogDevices cross-check.
130
+ // No pinmux group — the sam0-dac driver selects the output pad.
131
+ if (perM && pin.codes.has(pincode) && perM[2] === 'dac') {
132
+ const vout = perM[3].match(/^vout(\d+)$/) ?? (perM[3] === 'vout' ? [null, '0'] : null);
133
+ if (vout) {
134
+ dacRoutes.push({
135
+ source: 'dac0',
136
+ channel: Number(vout[1] ?? 0),
137
+ port: pin.name[0].toUpperCase(),
138
+ bit: Number(pin.name.slice(1)),
139
+ });
140
+ }
141
+ }
142
+ // PWM waveform output: `[e, tcc2, wo1]` — the position letter +
143
+ // peripheral + signal synthesize the SAM pinmux macro token the board's
144
+ // pinctrl chain defines (`P<port><bit><pos>_<PERIPH>_<SIG>` →
145
+ // `PA17E_TCC2_WO1`). The source is the DT nodelabel verbatim (`tcc2`).
146
+ if (perM && pin.codes.has(pincode) && /^(tcc|tc)\d+$/.test(perM[2])) {
147
+ const wo = perM[3].match(/^wo(\d+)$/);
148
+ if (wo) {
149
+ const port = pin.name[0].toUpperCase();
150
+ // The macro token uses the NUMERIC pad (no leading zero — pa02 →
151
+ // PA2E_TCC2_WO1, pa17 → PA17E_TCC2_WO1).
152
+ const bit = Number(pin.name.slice(1));
153
+ pwmRoutes.push({
154
+ source: perM[2],
155
+ channel: Number(wo[1]),
156
+ port,
157
+ bit,
158
+ pinctrl: `P${port}${bit}${perM[1].toUpperCase()}_${perM[2].toUpperCase()}_${perM[3].toUpperCase()}`,
159
+ });
160
+ }
161
+ }
162
+ }
163
+ if (routes.length > 0 || dacRoutes.length > 0 || pwmRoutes.length > 0) {
164
+ routes.sort((a, b) => a.source.localeCompare(b.source, undefined, { numeric: true }) || a.channel - b.channel);
165
+ dacRoutes.sort((a, b) => a.channel - b.channel);
166
+ pwmRoutes.sort((a, b) => a.source.localeCompare(b.source, undefined, { numeric: true }) || a.channel - b.channel);
167
+ return {
168
+ adc: routes,
169
+ ...(dacRoutes.length > 0 ? { dac: dacRoutes } : {}),
170
+ ...(pwmRoutes.length > 0 ? { pwm: pwmRoutes } : {}),
171
+ file: f,
172
+ };
173
+ }
174
+ break;
175
+ }
176
+ return undefined;
177
+ }
178
+ // ── Bouffalolab ─────────────────────────────────────────────────────────────
179
+ // Pins are global pads (`gpio0`…), `analog:` lists `[adc, [ch<N>]]` and the
180
+ // family/series fields name the SoC variants (`family: bl60x`, `series:
181
+ // [602, 604]` → bl602/bl604). Analog pads need no pinmux group (silicon-
182
+ // selected, like the ESP32 SARADC). Indentation varies between files (bl60x
183
+ // indents pins 2 spaces, bl61x/bl70x 4) — matched tolerant of depth.
184
+ // ----------------------------------------------------------------------------
185
+ function harvestBflb(soc, westRoot) {
186
+ const dir = path.join(westRoot, 'modules', 'hal', 'bouffalolab', 'pinconfigs');
187
+ const files = listYml(dir);
188
+ if (!files)
189
+ return undefined;
190
+ const socLower = soc.toLowerCase();
191
+ for (const f of files) {
192
+ let text;
193
+ try {
194
+ text = fs.readFileSync(path.join(dir, f), 'utf8');
195
+ }
196
+ catch {
197
+ continue;
198
+ }
199
+ const series = [...text.matchAll(/^series:\s*\[([^\]]*)\]/gm)]
200
+ .flatMap((m) => m[1].split(',').map((s) => s.trim()).filter(Boolean));
201
+ if (series.length === 0)
202
+ continue;
203
+ // The soc name starts with `bl` + a series entry (bl602c00q2i → bl602,
204
+ // bl704l10q2i → bl704l, …) — the tail is package/flash detail.
205
+ const suffix = series.find((s) => socLower.startsWith(('bl' + s).toLowerCase()));
206
+ if (!suffix)
207
+ continue;
208
+ const routes = [];
209
+ let pin;
210
+ let inAnalog = false;
211
+ for (const rawLine of text.split('\n')) {
212
+ const line = rawLine.replace(/\r$/, '');
213
+ const pinM = line.match(/^\s+gpio(\d+):/);
214
+ if (pinM) {
215
+ pin = { bit: Number(pinM[1]), bonded: true };
216
+ inAnalog = false;
217
+ continue;
218
+ }
219
+ if (!pin)
220
+ continue;
221
+ // A new key at pin depth ends the analog block.
222
+ if (inAnalog && /^\s+\w[\w-]*:/.test(line)) {
223
+ inAnalog = false;
224
+ }
225
+ if (inAnalog) {
226
+ const adc = line.match(/^\s*-\s*\[adc,\s*\[ch(\d+)\]\]/);
227
+ if (adc && pin.bonded) {
228
+ routes.push({ source: 'adc0', channel: Number(adc[1]), port: '0', bit: pin.bit });
229
+ }
230
+ continue;
231
+ }
232
+ const seriesM = line.match(/^\s+series:\s*\[([^\]]*)\]/);
233
+ if (seriesM) {
234
+ pin.bonded = seriesM[1].split(',').some((s) => s.trim() === suffix);
235
+ continue;
236
+ }
237
+ if (/^\s+analog:/.test(line))
238
+ inAnalog = true;
239
+ }
240
+ if (routes.length > 0) {
241
+ routes.sort((a, b) => a.channel - b.channel);
242
+ return { adc: routes, file: f };
243
+ }
244
+ }
245
+ return undefined;
246
+ }
247
+ /**
248
+ * Harvest ADC routes from the vendor pinconfigs for the board's SoC. The
249
+ * source is chosen by the soc segment — a data convention (like the
250
+ * letter-port families), never a board name. Undefined for SoCs without a
251
+ * pinconfig source.
252
+ */
253
+ export function harvestPinconfig(identifier, westRoot) {
254
+ const soc = identifier.split('/')[1];
255
+ if (!soc)
256
+ return undefined;
257
+ const s = soc.toLowerCase();
258
+ if (s.startsWith('sam'))
259
+ return harvestAtmel(soc, westRoot);
260
+ if (s.startsWith('bl'))
261
+ return harvestBflb(soc, westRoot);
262
+ return undefined;
263
+ }
@@ -0,0 +1,82 @@
1
+ /** The Zephyr manifest revision the workspace pins (installer versions.env). */
2
+ export declare const PINNED_ZEPHYR_MANIFEST_REV = "v4.4.2";
3
+ /** The Zephyr SDK (toolchain bundle) version the workspace pins. */
4
+ export declare const PINNED_ZEPHYR_SDK_VERSION = "1.0.1";
5
+ /** The canonical install command (the installer's own usage strings). */
6
+ export declare const ZEPHYR_INSTALL_CMD = "npx --package @typecad/framework-zephyr zephyr-installer";
7
+ /** Env that disables the create-time SDK gate (tests / expert override). */
8
+ export declare const SDK_CHECK_ENV = "CUTTLEFISH_SDK_CHECK";
9
+ /**
10
+ * Fingerprint of an installed Zephyr tree: version + git HEAD, hashed. This
11
+ * is the identity the board catalog's provenance already records per-walk;
12
+ * expose it for humans (`board sync`, `doctor`) and any consumer that wants
13
+ * to detect "the SDK moved" without comparing fields.
14
+ */
15
+ export declare function sdkFingerprint(tree: string): string | undefined;
16
+ /** Where the tree was found — printed by create so the user knows which
17
+ * SDK they are building against (and why, when two machines differ). */
18
+ export type SdkDiscoverySource = 'ZEPHYR_BASE' | 'well-known workspace' | 'installer env' | 'explicit';
19
+ /** Everything the create-time gate (and doctor) needs to know about the SDK:
20
+ * the Zephyr tree (source of truth for boards) and the toolchain SDK dir
21
+ * (the compiler bundle the installer lays down beside it). */
22
+ export type SdkCheck = {
23
+ status: 'ok';
24
+ tree: string;
25
+ version: string;
26
+ pinnedVersion: string;
27
+ fingerprint: string;
28
+ discoveredVia: SdkDiscoverySource;
29
+ toolchainDir?: string;
30
+ toolchainVersion?: string;
31
+ } | {
32
+ status: 'missing';
33
+ pinnedVersion: string;
34
+ } | {
35
+ status: 'version-mismatch';
36
+ tree: string;
37
+ version: string;
38
+ pinnedVersion: string;
39
+ fingerprint: string;
40
+ discoveredVia: SdkDiscoverySource;
41
+ toolchainDir?: string;
42
+ toolchainVersion?: string;
43
+ } | {
44
+ status: 'toolchain-mismatch';
45
+ tree: string;
46
+ version: string;
47
+ pinnedVersion: string;
48
+ fingerprint: string;
49
+ discoveredVia: SdkDiscoverySource;
50
+ toolchainDir: string;
51
+ toolchainVersion: string;
52
+ pinnedToolchainVersion: string;
53
+ };
54
+ /**
55
+ * Locate the toolchain SDK dir (the compiler bundle the installer lays
56
+ * down — distinct from the Zephyr tree): $ZEPHYR_SDK_INSTALL_DIR, the
57
+ * installer env-vars file, or a `zephyr-sdk-*` sibling of the tree.
58
+ * Verifies the dir carries an sdk_version file and returns the pair.
59
+ */
60
+ export declare function findToolchainSdk(tree: string): {
61
+ toolchainDir: string;
62
+ toolchainVersion: string;
63
+ } | undefined;
64
+ /**
65
+ * Check the installed Zephyr SDK against the workspace pin. `tree` is
66
+ * optional injection for tests; without it, discovery runs (fs-only,
67
+ * ZEPHYR_BASE authoritative when set). Pure — no throwing, callers decide.
68
+ */
69
+ export declare function checkZephyrSdk(tree?: string): SdkCheck;
70
+ /**
71
+ * The create-time gate: throw with actionable guidance unless a matching SDK
72
+ * is installed. Suppressed by CUTTLEFISH_SDK_CHECK=off (tests / experts who
73
+ * track their own tree against the looser build-time compat range).
74
+ */
75
+ export declare function assertZephyrSdkForCreate(): Extract<SdkCheck, {
76
+ status: 'ok';
77
+ }> | undefined;
78
+ /** Multi-line summary of a successful check — the CLI's indented
79
+ * key/value list style (match the `board sync` block's format). */
80
+ export declare function formatZephyrSdkFound(check: Extract<SdkCheck, {
81
+ status: 'ok';
82
+ }>): string[];
@@ -0,0 +1,180 @@
1
+ // ---------------------------------------------------------------------------
2
+ // board-catalog/sdk.ts — the installed Zephyr SDK is the source of truth.
3
+ //
4
+ // `cuttlefish create` gates on this: no SDK, no project. The check is
5
+ // fs-only (the same discovery the catalog store uses) and compares the
6
+ // installed tree against the workspace pin so a project is always created
7
+ // against the SDK the frameworks were validated with.
8
+ //
9
+ // The pin mirrors framework-zephyr's installer/versions.env
10
+ // (ZEPHYR_MANIFEST_REV) — the installer's canonical source. A drift-guard
11
+ // test keeps the two in lockstep; change them together.
12
+ // ----------------------------------------------------------------------------
13
+ import fs from 'node:fs';
14
+ import os from 'node:os';
15
+ import path from 'node:path';
16
+ import { createHash } from 'node:crypto';
17
+ import { locateZephyrBaseCheap, isZephyrBase, micromambaZephyrBase } from './store.js';
18
+ import { zephyrVersionOf, gitHeadOf } from './walker.js';
19
+ /** The Zephyr manifest revision the workspace pins (installer versions.env). */
20
+ export const PINNED_ZEPHYR_MANIFEST_REV = 'v4.4.2';
21
+ /** The Zephyr SDK (toolchain bundle) version the workspace pins. */
22
+ export const PINNED_ZEPHYR_SDK_VERSION = '1.0.1';
23
+ /** The canonical install command (the installer's own usage strings). */
24
+ export const ZEPHYR_INSTALL_CMD = 'npx --package @typecad/framework-zephyr zephyr-installer';
25
+ /** Env that disables the create-time SDK gate (tests / expert override). */
26
+ export const SDK_CHECK_ENV = 'CUTTLEFISH_SDK_CHECK';
27
+ /** 'v4.4.2' → '4.4.2' (the tree's VERSION file form). */
28
+ function revToVersion(rev) {
29
+ return rev.replace(/^v/, '');
30
+ }
31
+ /**
32
+ * Fingerprint of an installed Zephyr tree: version + git HEAD, hashed. This
33
+ * is the identity the board catalog's provenance already records per-walk;
34
+ * expose it for humans (`board sync`, `doctor`) and any consumer that wants
35
+ * to detect "the SDK moved" without comparing fields.
36
+ */
37
+ export function sdkFingerprint(tree) {
38
+ if (!isZephyrBase(tree))
39
+ return undefined;
40
+ const version = zephyrVersionOf(tree);
41
+ const head = gitHeadOf(tree) ?? 'no-git';
42
+ if (!version)
43
+ return undefined;
44
+ return createHash('sha1').update(`${version}\0${head}`).digest('hex').slice(0, 16);
45
+ }
46
+ /**
47
+ * Locate the toolchain SDK dir (the compiler bundle the installer lays
48
+ * down — distinct from the Zephyr tree): $ZEPHYR_SDK_INSTALL_DIR, the
49
+ * installer env-vars file, or a `zephyr-sdk-*` sibling of the tree.
50
+ * Verifies the dir carries an sdk_version file and returns the pair.
51
+ */
52
+ export function findToolchainSdk(tree) {
53
+ const candidates = [];
54
+ if (process.env.ZEPHYR_SDK_INSTALL_DIR)
55
+ candidates.push(process.env.ZEPHYR_SDK_INSTALL_DIR);
56
+ const root = process.env.MAMBA_ROOT_PREFIX || path.join(os.homedir(), 'micromamba');
57
+ const envDir = path.join(root, 'envs', process.env.TYPECAD_ZEPHYR_ENV || 'zephyr');
58
+ const envVars = process.platform === 'win32'
59
+ ? [path.join(envDir, 'etc', 'conda', 'env-vars.ps1'), path.join(envDir, 'etc', 'conda', 'env-vars.bat')]
60
+ : [path.join(envDir, 'etc', 'conda', 'env-vars.sh')];
61
+ for (const f of envVars) {
62
+ if (!fs.existsSync(f))
63
+ continue;
64
+ try {
65
+ const m = fs.readFileSync(f, 'utf8').match(/TYPECAD_ZEPHYR_SDK_INSTALL_DIR\s*=\s*"([^"]+)"/);
66
+ if (m)
67
+ candidates.push(m[1]);
68
+ }
69
+ catch {
70
+ // unreadable — keep going
71
+ }
72
+ }
73
+ const workspace = path.dirname(path.resolve(tree));
74
+ for (const entry of fs.existsSync(workspace) ? fs.readdirSync(workspace) : []) {
75
+ if (/^zephyr-sdk-/.test(entry))
76
+ candidates.push(path.join(workspace, entry));
77
+ }
78
+ for (const dir of candidates) {
79
+ const versionFile = path.join(dir, 'sdk_version');
80
+ try {
81
+ const version = fs.readFileSync(versionFile, 'utf8').trim();
82
+ if (version)
83
+ return { toolchainDir: path.resolve(dir), toolchainVersion: version };
84
+ }
85
+ catch {
86
+ // not an SDK dir — try the next candidate
87
+ }
88
+ }
89
+ return undefined;
90
+ }
91
+ /** How the tree at `base` was discovered (for the create printout). */
92
+ function discoverySource(base) {
93
+ if (process.env.ZEPHYR_BASE && path.resolve(process.env.ZEPHYR_BASE) === base)
94
+ return 'ZEPHYR_BASE';
95
+ const installer = micromambaZephyrBase();
96
+ if (installer && path.resolve(installer) === base)
97
+ return 'installer env';
98
+ return 'well-known workspace';
99
+ }
100
+ /**
101
+ * Check the installed Zephyr SDK against the workspace pin. `tree` is
102
+ * optional injection for tests; without it, discovery runs (fs-only,
103
+ * ZEPHYR_BASE authoritative when set). Pure — no throwing, callers decide.
104
+ */
105
+ export function checkZephyrSdk(tree) {
106
+ const pinnedVersion = revToVersion(PINNED_ZEPHYR_MANIFEST_REV);
107
+ let discoveredVia = 'explicit';
108
+ let base;
109
+ if (tree !== undefined) {
110
+ base = isZephyrBase(tree) ? path.resolve(tree) : undefined;
111
+ }
112
+ else {
113
+ base = locateZephyrBaseCheap();
114
+ if (base)
115
+ discoveredVia = discoverySource(base);
116
+ }
117
+ if (!base)
118
+ return { status: 'missing', pinnedVersion };
119
+ const version = zephyrVersionOf(base);
120
+ const fingerprint = sdkFingerprint(base);
121
+ if (!version || !fingerprint)
122
+ return { status: 'missing', pinnedVersion };
123
+ const toolchain = findToolchainSdk(base);
124
+ if (version !== pinnedVersion) {
125
+ return {
126
+ status: 'version-mismatch', tree: base, version, pinnedVersion, fingerprint,
127
+ discoveredVia, ...(toolchain ?? {}),
128
+ };
129
+ }
130
+ // The toolchain bundle is the SDK proper — the installer pins it in
131
+ // lockstep with the manifest revision, so a mismatch means a partial
132
+ // upgrade (tree re-pinned, toolchains not re-fetched, or vice versa).
133
+ if (toolchain && toolchain.toolchainVersion !== PINNED_ZEPHYR_SDK_VERSION) {
134
+ return {
135
+ status: 'toolchain-mismatch', tree: base, version, pinnedVersion, fingerprint,
136
+ discoveredVia,
137
+ toolchainDir: toolchain.toolchainDir,
138
+ toolchainVersion: toolchain.toolchainVersion,
139
+ pinnedToolchainVersion: PINNED_ZEPHYR_SDK_VERSION,
140
+ };
141
+ }
142
+ return {
143
+ status: 'ok', tree: base, version, pinnedVersion, fingerprint,
144
+ discoveredVia, ...(toolchain ?? {}),
145
+ };
146
+ }
147
+ /**
148
+ * The create-time gate: throw with actionable guidance unless a matching SDK
149
+ * is installed. Suppressed by CUTTLEFISH_SDK_CHECK=off (tests / experts who
150
+ * track their own tree against the looser build-time compat range).
151
+ */
152
+ export function assertZephyrSdkForCreate() {
153
+ if (process.env[SDK_CHECK_ENV] === 'off')
154
+ return undefined;
155
+ const check = checkZephyrSdk();
156
+ if (check.status === 'ok')
157
+ return check;
158
+ if (check.status === 'missing') {
159
+ throw new Error(`No Zephyr SDK found on this machine — a project is only useful with one.\n` +
160
+ `Install the pinned SDK (Zephyr ${check.pinnedVersion}, toolchain SDK ${PINNED_ZEPHYR_SDK_VERSION}):\n` +
161
+ ` ${ZEPHYR_INSTALL_CMD}\n` +
162
+ `Then re-run 'cuttlefish create'. (Set ZEPHYR_BASE if your tree lives elsewhere.)`);
163
+ }
164
+ throw new Error(`The installed Zephyr SDK (${check.version} at ${check.tree}) does not match the\n` +
165
+ `workspace pin (${check.pinnedVersion}). Re-pin your tree to the validated revision:\n` +
166
+ ` ${ZEPHYR_INSTALL_CMD} # interactive — re-checks out the pinned revision\n` +
167
+ `or track your own tree by setting CUTTLEFISH_SDK_CHECK=off (the build-time\n` +
168
+ `compat check still applies).`);
169
+ }
170
+ /** Multi-line summary of a successful check — the CLI's indented
171
+ * key/value list style (match the `board sync` block's format). */
172
+ export function formatZephyrSdkFound(check) {
173
+ const lines = [
174
+ ` zephyr: ${check.version} @ ${check.tree}`,
175
+ ];
176
+ lines.push(check.toolchainDir
177
+ ? ` toolchain: SDK ${check.toolchainVersion ?? ''} @ ${check.toolchainDir}`
178
+ : ` toolchain: not found (west builds still work; some runners may not)`);
179
+ return lines;
180
+ }