canopycms 0.0.67-int.90 → 0.0.67

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 (539) hide show
  1. package/README.md +30 -3
  2. package/dist/ai/generate.js +1 -27
  3. package/dist/ai/handler.d.ts +0 -4
  4. package/dist/ai/handler.js +0 -11
  5. package/dist/ai/index.d.ts +1 -8
  6. package/dist/ai/index.js +1 -8
  7. package/dist/ai/json-to-markdown.js +15 -58
  8. package/dist/ai/resolve-branch.d.ts +2 -7
  9. package/dist/ai/resolve-branch.js +2 -8
  10. package/dist/ai/strip-mdx.d.ts +5 -13
  11. package/dist/ai/strip-mdx.js +5 -17
  12. package/dist/ai/to-plain-text.d.ts +10 -28
  13. package/dist/ai/to-plain-text.js +10 -28
  14. package/dist/ai/transform-components.d.ts +2 -4
  15. package/dist/ai/transform-components.js +8 -19
  16. package/dist/ai/types.d.ts +5 -10
  17. package/dist/api/admin-branch-health.d.ts +4 -12
  18. package/dist/api/admin-branch-health.js +26 -66
  19. package/dist/api/admin.d.ts +4 -12
  20. package/dist/api/admin.js +10 -26
  21. package/dist/api/assets.d.ts +0 -25
  22. package/dist/api/assets.js +50 -91
  23. package/dist/api/branch-merge.d.ts +1 -1
  24. package/dist/api/branch-merge.js +0 -2
  25. package/dist/api/branch-review.d.ts +0 -8
  26. package/dist/api/branch-review.js +0 -13
  27. package/dist/api/branch-status.d.ts +0 -3
  28. package/dist/api/branch-status.js +8 -33
  29. package/dist/api/branch-withdraw.d.ts +0 -1
  30. package/dist/api/branch-withdraw.js +6 -9
  31. package/dist/api/branch.d.ts +7 -12
  32. package/dist/api/branch.js +7 -44
  33. package/dist/api/client.d.ts +71 -224
  34. package/dist/api/client.js +82 -233
  35. package/dist/api/comments.d.ts +2 -4
  36. package/dist/api/comments.js +0 -17
  37. package/dist/api/content.d.ts +0 -6
  38. package/dist/api/content.js +66 -142
  39. package/dist/api/entries-constants.d.ts +5 -9
  40. package/dist/api/entries-constants.js +5 -9
  41. package/dist/api/entries.d.ts +8 -13
  42. package/dist/api/entries.js +20 -51
  43. package/dist/api/github-sync.d.ts +2 -4
  44. package/dist/api/github-sync.js +4 -8
  45. package/dist/api/groups.d.ts +3 -0
  46. package/dist/api/groups.js +8 -21
  47. package/dist/api/guards.d.ts +4 -16
  48. package/dist/api/guards.js +3 -22
  49. package/dist/api/index.d.ts +4 -4
  50. package/dist/api/permissions.d.ts +2 -0
  51. package/dist/api/permissions.js +18 -40
  52. package/dist/api/reference-options.d.ts +0 -1
  53. package/dist/api/reference-options.js +3 -11
  54. package/dist/api/request-body-hash.d.ts +12 -23
  55. package/dist/api/request-body-hash.js +12 -23
  56. package/dist/api/resolve-references.d.ts +0 -1
  57. package/dist/api/resolve-references.js +13 -28
  58. package/dist/api/route-builder.d.ts +3 -7
  59. package/dist/api/route-builder.js +1 -1
  60. package/dist/api/routes.d.ts +12 -0
  61. package/dist/api/routes.js +47 -0
  62. package/dist/api/schema.d.ts +23 -33
  63. package/dist/api/schema.js +20 -74
  64. package/dist/api/settings-helpers.js +4 -5
  65. package/dist/api/types.d.ts +0 -5
  66. package/dist/api/user.js +0 -5
  67. package/dist/api/validators.d.ts +14 -52
  68. package/dist/api/validators.js +14 -52
  69. package/dist/assets/asset-prefixes.d.ts +4 -8
  70. package/dist/assets/asset-prefixes.js +4 -8
  71. package/dist/assets/asset-src.d.ts +5 -22
  72. package/dist/assets/asset-src.js +5 -21
  73. package/dist/assets/factory.d.ts +1 -2
  74. package/dist/assets/factory.js +1 -2
  75. package/dist/assets/finalize.d.ts +3 -1
  76. package/dist/assets/finalize.js +3 -1
  77. package/dist/assets/index.d.ts +3 -3
  78. package/dist/assets/index.js +1 -1
  79. package/dist/assets/keys.d.ts +5 -3
  80. package/dist/assets/keys.js +6 -3
  81. package/dist/assets/pipeline.d.ts +3 -2
  82. package/dist/assets/pipeline.js +22 -56
  83. package/dist/assets/sharp-loader.d.ts +2 -10
  84. package/dist/assets/sharp-loader.js +2 -10
  85. package/dist/assets/store-local.d.ts +1 -2
  86. package/dist/assets/store-local.js +1 -2
  87. package/dist/assets/store-s3.d.ts +3 -4
  88. package/dist/assets/svg-sanitizer.d.ts +6 -14
  89. package/dist/assets/svg-sanitizer.js +6 -14
  90. package/dist/assets/transform-directives.d.ts +3 -4
  91. package/dist/assets/transform-directives.js +1 -1
  92. package/dist/assets/transform.d.ts +19 -30
  93. package/dist/assets/transform.js +18 -31
  94. package/dist/auth/cache.d.ts +2 -3
  95. package/dist/auth/cache.js +2 -3
  96. package/dist/auth/caching-auth-plugin.d.ts +11 -22
  97. package/dist/auth/caching-auth-plugin.js +12 -14
  98. package/dist/auth/context-helpers.d.ts +6 -20
  99. package/dist/auth/context-helpers.js +5 -17
  100. package/dist/auth/file-based-auth-cache.d.ts +10 -21
  101. package/dist/auth/file-based-auth-cache.js +23 -51
  102. package/dist/auth/plugin.d.ts +29 -61
  103. package/dist/auth/plugin.js +9 -13
  104. package/dist/auth/types.d.ts +11 -20
  105. package/dist/authorization/branch.d.ts +30 -49
  106. package/dist/authorization/branch.js +16 -42
  107. package/dist/authorization/content.d.ts +11 -40
  108. package/dist/authorization/content.js +13 -45
  109. package/dist/authorization/groups/index.d.ts +0 -3
  110. package/dist/authorization/groups/index.js +0 -3
  111. package/dist/authorization/groups/loader.d.ts +14 -28
  112. package/dist/authorization/groups/loader.js +14 -38
  113. package/dist/authorization/groups/schema.d.ts +0 -6
  114. package/dist/authorization/groups/schema.js +4 -7
  115. package/dist/authorization/helpers.d.ts +9 -28
  116. package/dist/authorization/helpers.js +9 -28
  117. package/dist/authorization/index.d.ts +3 -25
  118. package/dist/authorization/index.js +3 -35
  119. package/dist/authorization/path.d.ts +6 -13
  120. package/dist/authorization/path.js +8 -25
  121. package/dist/authorization/permissions/index.d.ts +0 -3
  122. package/dist/authorization/permissions/index.js +0 -3
  123. package/dist/authorization/permissions/loader.d.ts +14 -27
  124. package/dist/authorization/permissions/loader.js +14 -32
  125. package/dist/authorization/permissions/schema.d.ts +1 -6
  126. package/dist/authorization/permissions/schema.js +6 -13
  127. package/dist/authorization/protected-branch.d.ts +36 -65
  128. package/dist/authorization/protected-branch.js +29 -41
  129. package/dist/authorization/settings-file-store.d.ts +38 -81
  130. package/dist/authorization/settings-file-store.js +38 -79
  131. package/dist/authorization/types.d.ts +4 -26
  132. package/dist/authorization/types.js +0 -6
  133. package/dist/authorization/validation.d.ts +1 -23
  134. package/dist/authorization/validation.js +1 -24
  135. package/dist/branch-health.d.ts +39 -67
  136. package/dist/branch-health.js +27 -43
  137. package/dist/branch-metadata-file.d.ts +16 -32
  138. package/dist/branch-metadata-file.js +13 -26
  139. package/dist/branch-metadata.d.ts +49 -80
  140. package/dist/branch-metadata.js +57 -87
  141. package/dist/branch-registry.d.ts +26 -64
  142. package/dist/branch-registry.js +44 -93
  143. package/dist/branch-schema-cache.d.ts +34 -124
  144. package/dist/branch-schema-cache.js +49 -155
  145. package/dist/branch-workspace.d.ts +4 -4
  146. package/dist/branch-workspace.js +15 -19
  147. package/dist/build/generate-ai-content.d.ts +1 -0
  148. package/dist/build/generate-ai-content.js +12 -25
  149. package/dist/build-canopy.d.ts +17 -27
  150. package/dist/build-canopy.js +16 -23
  151. package/dist/build-mode.d.ts +25 -27
  152. package/dist/build-mode.js +25 -29
  153. package/dist/cli/cli.d.ts +15 -26
  154. package/dist/cli/cli.js +601 -978
  155. package/dist/cli/generate-ai-content.js +351 -628
  156. package/dist/cli/github-app-manifest.d.ts +84 -0
  157. package/dist/cli/github-app-manifest.js +161 -0
  158. package/dist/cli/init-github-app.d.ts +110 -444
  159. package/dist/cli/init-github-app.js +236 -773
  160. package/dist/cli/init.js +39 -80
  161. package/dist/cli/migrate.d.ts +13 -10
  162. package/dist/cli/migrate.js +22 -30
  163. package/dist/cli/project-detect.d.ts +17 -31
  164. package/dist/cli/project-detect.js +31 -42
  165. package/dist/cli/project-root.d.ts +4 -11
  166. package/dist/cli/project-root.js +4 -11
  167. package/dist/cli/prompt.d.ts +10 -0
  168. package/dist/cli/prompt.js +67 -0
  169. package/dist/cli/sync.d.ts +0 -8
  170. package/dist/cli/sync.js +8 -28
  171. package/dist/cli/template-files/middleware.ts.template +15 -29
  172. package/dist/cli/templates.d.ts +2 -12
  173. package/dist/cli/templates.js +4 -4
  174. package/dist/client.d.ts +1 -1
  175. package/dist/comment-store.d.ts +32 -53
  176. package/dist/comment-store.js +32 -52
  177. package/dist/config/flatten.d.ts +0 -3
  178. package/dist/config/flatten.js +2 -12
  179. package/dist/config/helpers.js +2 -2
  180. package/dist/config/index.d.ts +2 -2
  181. package/dist/config/index.js +1 -4
  182. package/dist/config/schemas/collection.d.ts +0 -8
  183. package/dist/config/schemas/collection.js +0 -10
  184. package/dist/config/schemas/config.d.ts +6 -78
  185. package/dist/config/schemas/config.js +38 -54
  186. package/dist/config/schemas/field.d.ts +2 -242
  187. package/dist/config/schemas/field.js +9 -20
  188. package/dist/config/schemas/media.js +1 -1
  189. package/dist/config/schemas/permissions.js +0 -2
  190. package/dist/config/schemas/url.d.ts +5 -6
  191. package/dist/config/schemas/url.js +5 -6
  192. package/dist/config/types.d.ts +45 -33
  193. package/dist/config/types.js +0 -1
  194. package/dist/config.d.ts +1 -8
  195. package/dist/config.js +1 -9
  196. package/dist/content-id-index.d.ts +76 -188
  197. package/dist/content-id-index.js +79 -230
  198. package/dist/content-index-generation.d.ts +27 -48
  199. package/dist/content-index-generation.js +35 -84
  200. package/dist/content-index-registry.d.ts +15 -24
  201. package/dist/content-index-registry.js +5 -9
  202. package/dist/content-listing.d.ts +64 -107
  203. package/dist/content-listing.js +68 -118
  204. package/dist/content-reader.d.ts +20 -29
  205. package/dist/content-reader.js +10 -18
  206. package/dist/content-store.d.ts +210 -336
  207. package/dist/content-store.js +413 -657
  208. package/dist/content-tree.d.ts +44 -86
  209. package/dist/content-tree.js +26 -54
  210. package/dist/context.d.ts +67 -94
  211. package/dist/context.js +53 -72
  212. package/dist/dev-content-watcher.d.ts +18 -18
  213. package/dist/dev-content-watcher.js +41 -44
  214. package/dist/editor/BranchManager.d.ts +3 -6
  215. package/dist/editor/BranchManager.js +24 -44
  216. package/dist/editor/CanopyEditor.js +5 -2
  217. package/dist/editor/CommentsPanel.js +0 -2
  218. package/dist/editor/Editor.js +19 -76
  219. package/dist/editor/EditorAuthGate.d.ts +27 -0
  220. package/dist/editor/EditorAuthGate.js +140 -0
  221. package/dist/editor/EntryNavigator.js +0 -20
  222. package/dist/editor/FormRenderer.d.ts +1 -2
  223. package/dist/editor/FormRenderer.js +8 -23
  224. package/dist/editor/admin/SystemHealthPanel.js +20 -37
  225. package/dist/editor/admin/useSystemHealth.d.ts +2 -3
  226. package/dist/editor/admin/useSystemHealth.js +10 -12
  227. package/dist/editor/client-reference-resolver.d.ts +2 -5
  228. package/dist/editor/client-reference-resolver.js +2 -19
  229. package/dist/editor/comments/BranchComments.js +0 -4
  230. package/dist/editor/comments/EntryComments.js +0 -4
  231. package/dist/editor/comments/FieldWrapper.js +0 -4
  232. package/dist/editor/comments/InlineCommentThread.js +0 -2
  233. package/dist/editor/comments/ThreadCarousel.d.ts +0 -58
  234. package/dist/editor/comments/ThreadCarousel.js +0 -12
  235. package/dist/editor/components/EditorFooter.d.ts +0 -8
  236. package/dist/editor/components/EditorFooter.js +0 -5
  237. package/dist/editor/components/EditorHeader.d.ts +0 -31
  238. package/dist/editor/components/EditorHeader.js +21 -71
  239. package/dist/editor/components/EditorSidebar.d.ts +0 -19
  240. package/dist/editor/components/EditorSidebar.js +0 -16
  241. package/dist/editor/components/EntryCreateModal.d.ts +4 -4
  242. package/dist/editor/components/EntryCreateModal.js +7 -22
  243. package/dist/editor/components/RenameEntryModal.js +0 -4
  244. package/dist/editor/components/UserBadge.js +0 -10
  245. package/dist/editor/components/index.js +0 -2
  246. package/dist/editor/context/ApiClientContext.d.ts +8 -7
  247. package/dist/editor/context/ApiClientContext.js +39 -11
  248. package/dist/editor/context/AssetContext.d.ts +7 -17
  249. package/dist/editor/context/AssetContext.js +7 -17
  250. package/dist/editor/context/EditorIdentityContext.d.ts +13 -0
  251. package/dist/editor/context/EditorIdentityContext.js +7 -0
  252. package/dist/editor/context/EditorStateContext.d.ts +10 -41
  253. package/dist/editor/context/EditorStateContext.js +7 -23
  254. package/dist/editor/context/SWRProvider.d.ts +3 -6
  255. package/dist/editor/context/index.d.ts +1 -20
  256. package/dist/editor/context/index.js +1 -20
  257. package/dist/editor/editor-utils.d.ts +8 -47
  258. package/dist/editor/editor-utils.js +19 -72
  259. package/dist/editor/fields/BlockField.d.ts +2 -2
  260. package/dist/editor/fields/BlockField.js +1 -2
  261. package/dist/editor/fields/CodeField.d.ts +0 -1
  262. package/dist/editor/fields/CodeField.js +0 -1
  263. package/dist/editor/fields/DateTimeField.d.ts +15 -22
  264. package/dist/editor/fields/DateTimeField.js +17 -26
  265. package/dist/editor/fields/ImageField.d.ts +2 -2
  266. package/dist/editor/fields/ImageField.js +8 -14
  267. package/dist/editor/fields/InlineGroupField.d.ts +0 -1
  268. package/dist/editor/fields/InlineGroupField.js +0 -1
  269. package/dist/editor/fields/MarkdownField.d.ts +0 -1
  270. package/dist/editor/fields/MarkdownField.js +6 -11
  271. package/dist/editor/fields/MdxImageDialog.d.ts +1 -1
  272. package/dist/editor/fields/MdxImageDialog.js +0 -1
  273. package/dist/editor/fields/NumberField.d.ts +9 -19
  274. package/dist/editor/fields/NumberField.js +11 -22
  275. package/dist/editor/fields/NumberListField.d.ts +7 -10
  276. package/dist/editor/fields/NumberListField.js +7 -10
  277. package/dist/editor/fields/ObjectField.d.ts +5 -8
  278. package/dist/editor/fields/ObjectField.js +0 -1
  279. package/dist/editor/fields/ReferenceField.d.ts +1 -1
  280. package/dist/editor/fields/ReferenceField.js +11 -16
  281. package/dist/editor/fields/SelectField.d.ts +2 -2
  282. package/dist/editor/fields/SelectField.js +0 -1
  283. package/dist/editor/fields/StringListField.d.ts +0 -4
  284. package/dist/editor/fields/StringListField.js +4 -9
  285. package/dist/editor/fields/TextField.d.ts +0 -1
  286. package/dist/editor/fields/TextField.js +0 -1
  287. package/dist/editor/fields/ToggleField.d.ts +0 -1
  288. package/dist/editor/fields/ToggleField.js +0 -1
  289. package/dist/editor/fields/entry-link/EntryLinkContext.js +2 -4
  290. package/dist/editor/fields/entry-link/InsertEntryLink.d.ts +0 -3
  291. package/dist/editor/fields/entry-link/InsertEntryLink.js +0 -4
  292. package/dist/editor/group-manager/ExternalGroupsTab.d.ts +0 -3
  293. package/dist/editor/group-manager/ExternalGroupsTab.js +0 -1
  294. package/dist/editor/group-manager/GroupCard.d.ts +0 -3
  295. package/dist/editor/group-manager/GroupForm.d.ts +0 -3
  296. package/dist/editor/group-manager/InternalGroupsTab.d.ts +0 -3
  297. package/dist/editor/group-manager/InternalGroupsTab.js +0 -2
  298. package/dist/editor/group-manager/MemberList.d.ts +0 -3
  299. package/dist/editor/group-manager/hooks/useExternalGroupSearch.d.ts +0 -3
  300. package/dist/editor/group-manager/hooks/useExternalGroupSearch.js +0 -3
  301. package/dist/editor/group-manager/hooks/useGroupState.d.ts +0 -3
  302. package/dist/editor/group-manager/hooks/useGroupState.js +0 -5
  303. package/dist/editor/group-manager/hooks/useUserSearch.d.ts +0 -3
  304. package/dist/editor/group-manager/hooks/useUserSearch.js +0 -3
  305. package/dist/editor/group-manager/index.d.ts +0 -6
  306. package/dist/editor/group-manager/index.js +0 -10
  307. package/dist/editor/group-manager/types.d.ts +2 -5
  308. package/dist/editor/group-manager/types.js +0 -3
  309. package/dist/editor/hooks/index.d.ts +6 -11
  310. package/dist/editor/hooks/index.js +6 -12
  311. package/dist/editor/hooks/useBranchActions.d.ts +0 -14
  312. package/dist/editor/hooks/useBranchActions.js +0 -22
  313. package/dist/editor/hooks/useBranchManager.d.ts +2 -48
  314. package/dist/editor/hooks/useBranchManager.js +4 -64
  315. package/dist/editor/hooks/useBranchesData.d.ts +7 -6
  316. package/dist/editor/hooks/useBranchesData.js +7 -6
  317. package/dist/editor/hooks/useCommentSystem.d.ts +0 -49
  318. package/dist/editor/hooks/useCommentSystem.js +1 -36
  319. package/dist/editor/hooks/useDraftManager.d.ts +2 -35
  320. package/dist/editor/hooks/useDraftManager.js +23 -64
  321. package/dist/editor/hooks/useEditorLayout.d.ts +0 -20
  322. package/dist/editor/hooks/useEditorLayout.js +0 -23
  323. package/dist/editor/hooks/useEntriesData.d.ts +1 -0
  324. package/dist/editor/hooks/useEntriesData.js +2 -3
  325. package/dist/editor/hooks/useEntryLinkResolution.d.ts +2 -2
  326. package/dist/editor/hooks/useEntryLinkResolution.js +2 -2
  327. package/dist/editor/hooks/useEntryManager.d.ts +2 -27
  328. package/dist/editor/hooks/useEntryManager.js +29 -78
  329. package/dist/editor/hooks/useGroupManager.d.ts +0 -21
  330. package/dist/editor/hooks/useGroupManager.js +0 -21
  331. package/dist/editor/hooks/usePermissionManager.d.ts +0 -20
  332. package/dist/editor/hooks/usePermissionManager.js +0 -20
  333. package/dist/editor/hooks/useReferenceResolution.d.ts +2 -33
  334. package/dist/editor/hooks/useReferenceResolution.js +1 -48
  335. package/dist/editor/hooks/useSchemaManager.d.ts +8 -27
  336. package/dist/editor/hooks/useSchemaManager.js +0 -19
  337. package/dist/editor/hooks/useUserContext.d.ts +2 -9
  338. package/dist/editor/hooks/useUserContext.js +10 -10
  339. package/dist/editor/hooks/useUserMetadata.d.ts +0 -2
  340. package/dist/editor/hooks/useUserMetadata.js +0 -3
  341. package/dist/editor/media/AssetCard.d.ts +0 -1
  342. package/dist/editor/media/AssetCard.js +1 -2
  343. package/dist/editor/media/CropStep.d.ts +0 -1
  344. package/dist/editor/media/CropStep.js +0 -1
  345. package/dist/editor/media/MediaLibrary.d.ts +1 -2
  346. package/dist/editor/media/MediaLibrary.js +1 -2
  347. package/dist/editor/media/MediaLibraryBody.d.ts +0 -1
  348. package/dist/editor/media/MediaLibraryBody.js +0 -1
  349. package/dist/editor/media/crop-math.d.ts +1 -1
  350. package/dist/editor/media/crop-math.js +1 -1
  351. package/dist/editor/permission-manager/GroupSelector.d.ts +0 -3
  352. package/dist/editor/permission-manager/PermissionEditor.d.ts +0 -4
  353. package/dist/editor/permission-manager/PermissionEditor.js +0 -1
  354. package/dist/editor/permission-manager/PermissionLevelBadge.d.ts +0 -7
  355. package/dist/editor/permission-manager/PermissionLevelBadge.js +0 -4
  356. package/dist/editor/permission-manager/PermissionTree.d.ts +0 -4
  357. package/dist/editor/permission-manager/UserSelector.d.ts +0 -3
  358. package/dist/editor/permission-manager/constants.d.ts +0 -3
  359. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.d.ts +0 -3
  360. package/dist/editor/permission-manager/hooks/useGroupsAndUsers.js +0 -10
  361. package/dist/editor/permission-manager/hooks/usePermissionTree.d.ts +0 -14
  362. package/dist/editor/permission-manager/hooks/usePermissionTree.js +0 -11
  363. package/dist/editor/permission-manager/index.d.ts +0 -7
  364. package/dist/editor/permission-manager/index.js +0 -16
  365. package/dist/editor/permission-manager/types.d.ts +2 -12
  366. package/dist/editor/permission-manager/types.js +0 -3
  367. package/dist/editor/permission-manager/utils.d.ts +0 -28
  368. package/dist/editor/permission-manager/utils.js +9 -23
  369. package/dist/editor/preview-bridge.d.ts +5 -1
  370. package/dist/editor/preview-bridge.js +4 -4
  371. package/dist/editor/schema-editor/CollectionEditor.d.ts +0 -5
  372. package/dist/editor/schema-editor/CollectionEditor.js +9 -31
  373. package/dist/editor/schema-editor/EntryTypeEditor.d.ts +0 -6
  374. package/dist/editor/schema-editor/EntryTypeEditor.js +3 -24
  375. package/dist/editor/schema-editor/index.d.ts +0 -7
  376. package/dist/editor/schema-editor/index.js +0 -7
  377. package/dist/editor/utils/env.d.ts +3 -18
  378. package/dist/editor/utils/env.js +6 -25
  379. package/dist/entry-link-resolver.d.ts +13 -35
  380. package/dist/entry-link-resolver.js +20 -53
  381. package/dist/entry-schema-registry.d.ts +20 -60
  382. package/dist/entry-schema-registry.js +20 -69
  383. package/dist/entry-schema.d.ts +118 -223
  384. package/dist/entry-schema.js +46 -91
  385. package/dist/git-manager.d.ts +101 -200
  386. package/dist/git-manager.js +211 -384
  387. package/dist/github-service.d.ts +34 -75
  388. package/dist/github-service.js +37 -91
  389. package/dist/http/handler.d.ts +6 -37
  390. package/dist/http/handler.js +65 -120
  391. package/dist/http/router.d.ts +15 -32
  392. package/dist/http/router.js +34 -99
  393. package/dist/http/types.d.ts +15 -40
  394. package/dist/http/types.js +3 -7
  395. package/dist/id.d.ts +4 -10
  396. package/dist/id.js +4 -10
  397. package/dist/index.js +6 -9
  398. package/dist/operating-mode/client-safe-strategy.d.ts +7 -14
  399. package/dist/operating-mode/client-safe-strategy.js +8 -28
  400. package/dist/operating-mode/client-unsafe-strategy.d.ts +6 -16
  401. package/dist/operating-mode/client-unsafe-strategy.js +9 -34
  402. package/dist/operating-mode/client.d.ts +3 -10
  403. package/dist/operating-mode/client.js +3 -8
  404. package/dist/operating-mode/deployment-name-fixtures.d.ts +14 -18
  405. package/dist/operating-mode/deployment-name-fixtures.js +14 -18
  406. package/dist/operating-mode/deployment-name.d.ts +19 -29
  407. package/dist/operating-mode/deployment-name.js +29 -47
  408. package/dist/operating-mode/index.d.ts +4 -25
  409. package/dist/operating-mode/index.js +9 -31
  410. package/dist/operating-mode/mode-env.d.ts +32 -54
  411. package/dist/operating-mode/mode-env.js +36 -58
  412. package/dist/operating-mode/types.d.ts +24 -72
  413. package/dist/operating-mode/types.js +1 -7
  414. package/dist/paths/branch-name.d.ts +30 -40
  415. package/dist/paths/branch-name.js +30 -40
  416. package/dist/paths/branch.d.ts +4 -18
  417. package/dist/paths/branch.js +6 -22
  418. package/dist/paths/index.d.ts +2 -13
  419. package/dist/paths/index.js +3 -23
  420. package/dist/paths/normalize-server.d.ts +3 -15
  421. package/dist/paths/normalize-server.js +3 -15
  422. package/dist/paths/normalize.d.ts +13 -33
  423. package/dist/paths/normalize.js +13 -33
  424. package/dist/paths/resolve.d.ts +4 -17
  425. package/dist/paths/resolve.js +4 -22
  426. package/dist/paths/types.d.ts +12 -34
  427. package/dist/paths/types.js +5 -7
  428. package/dist/paths/validation.d.ts +22 -119
  429. package/dist/paths/validation.js +30 -148
  430. package/dist/reference-resolver.d.ts +0 -13
  431. package/dist/reference-resolver.js +0 -18
  432. package/dist/resolve-canopy-user.d.ts +16 -18
  433. package/dist/resolve-canopy-user.js +17 -26
  434. package/dist/resource-generation.d.ts +16 -25
  435. package/dist/resource-generation.js +34 -97
  436. package/dist/schema/meta-loader.d.ts +2 -14
  437. package/dist/schema/meta-loader.js +15 -64
  438. package/dist/schema/resolver.d.ts +1 -4
  439. package/dist/schema/resolver.js +1 -6
  440. package/dist/schema/schema-store-types.d.ts +0 -12
  441. package/dist/schema/schema-store.d.ts +61 -138
  442. package/dist/schema/schema-store.js +85 -282
  443. package/dist/schema/types.d.ts +0 -4
  444. package/dist/server.d.ts +62 -93
  445. package/dist/server.js +63 -94
  446. package/dist/services.d.ts +23 -47
  447. package/dist/services.js +55 -98
  448. package/dist/settings-workspace.d.ts +17 -30
  449. package/dist/settings-workspace.js +51 -81
  450. package/dist/static/index.d.ts +26 -52
  451. package/dist/static/index.js +33 -67
  452. package/dist/{worker/task-queue.d.ts → task-queue/cms-task-queue.d.ts} +7 -9
  453. package/dist/task-queue/cms-task-queue.js +13 -0
  454. package/dist/task-queue/task-queue-config.d.ts +3 -0
  455. package/dist/{worker → task-queue}/task-queue-config.js +1 -6
  456. package/dist/task-queue/task-queue.d.ts +16 -22
  457. package/dist/task-queue/task-queue.js +20 -44
  458. package/dist/task-queue/types.d.ts +0 -1
  459. package/dist/task-queue/worker-status.d.ts +35 -0
  460. package/dist/task-queue/worker-status.js +40 -0
  461. package/dist/types.d.ts +40 -51
  462. package/dist/url-collision.d.ts +26 -40
  463. package/dist/url-collision.js +47 -63
  464. package/dist/url-exclusivity-fixtures.d.ts +15 -20
  465. package/dist/url-exclusivity-fixtures.js +14 -19
  466. package/dist/url-path-resolver.d.ts +8 -16
  467. package/dist/url-path-resolver.js +19 -29
  468. package/dist/user.d.ts +8 -21
  469. package/dist/user.js +10 -19
  470. package/dist/utils/async-mutex.js +4 -6
  471. package/dist/utils/atomic-write.d.ts +2 -11
  472. package/dist/utils/atomic-write.js +2 -11
  473. package/dist/utils/body-field.d.ts +8 -15
  474. package/dist/utils/body-field.js +8 -15
  475. package/dist/utils/content-serialize.d.ts +18 -24
  476. package/dist/utils/content-serialize.js +109 -160
  477. package/dist/utils/content-write-lock.d.ts +51 -106
  478. package/dist/utils/content-write-lock.js +66 -111
  479. package/dist/utils/debug.d.ts +7 -23
  480. package/dist/utils/debug.js +7 -23
  481. package/dist/utils/entry-url.d.ts +11 -23
  482. package/dist/utils/entry-url.js +11 -26
  483. package/dist/utils/error.d.ts +25 -90
  484. package/dist/utils/error.js +60 -147
  485. package/dist/utils/flatten-group-fields.d.ts +5 -7
  486. package/dist/utils/flatten-group-fields.js +5 -7
  487. package/dist/utils/format.d.ts +1 -5
  488. package/dist/utils/format.js +1 -5
  489. package/dist/utils/fs.d.ts +3 -4
  490. package/dist/utils/fs.js +3 -4
  491. package/dist/utils/git.d.ts +52 -83
  492. package/dist/utils/git.js +79 -125
  493. package/dist/utils/logger.d.ts +35 -50
  494. package/dist/utils/logger.js +35 -52
  495. package/dist/utils/provisioning-lock.d.ts +23 -35
  496. package/dist/utils/provisioning-lock.js +49 -69
  497. package/dist/utils/sanitize-href.d.ts +5 -10
  498. package/dist/utils/sanitize-href.js +8 -18
  499. package/dist/utils/title-field.d.ts +17 -20
  500. package/dist/utils/title-field.js +18 -27
  501. package/dist/utils/typed-filename.d.ts +20 -48
  502. package/dist/utils/typed-filename.js +25 -61
  503. package/dist/utils/url-prefix.d.ts +3 -3
  504. package/dist/utils/url-prefix.js +3 -3
  505. package/dist/validation/block-structural-keys.d.ts +16 -31
  506. package/dist/validation/block-structural-keys.js +19 -29
  507. package/dist/validation/deletion-checker.d.ts +0 -34
  508. package/dist/validation/deletion-checker.js +1 -43
  509. package/dist/validation/entry-link-validator.d.ts +6 -13
  510. package/dist/validation/entry-link-validator.js +4 -13
  511. package/dist/validation/entry-type-reference-validator.d.ts +13 -18
  512. package/dist/validation/entry-type-reference-validator.js +13 -18
  513. package/dist/validation/entry-validator.d.ts +13 -25
  514. package/dist/validation/entry-validator.js +15 -32
  515. package/dist/validation/field-traversal.d.ts +3 -30
  516. package/dist/validation/field-traversal.js +1 -32
  517. package/dist/validation/reference-validator.d.ts +5 -24
  518. package/dist/validation/reference-validator.js +5 -30
  519. package/dist/worker/cms-worker.d.ts +172 -232
  520. package/dist/worker/cms-worker.js +241 -333
  521. package/dist/worker/git-sync.d.ts +32 -133
  522. package/dist/worker/git-sync.js +129 -191
  523. package/dist/worker/github-auth.d.ts +13 -19
  524. package/dist/worker/github-auth.js +13 -19
  525. package/dist/worker/history-rewrite.d.ts +53 -66
  526. package/dist/worker/history-rewrite.js +54 -60
  527. package/dist/worker/log.d.ts +24 -41
  528. package/dist/worker/log.js +25 -42
  529. package/dist/worker/rebase.d.ts +20 -110
  530. package/dist/worker/rebase.js +279 -354
  531. package/dist/worker/task-runner.d.ts +32 -66
  532. package/dist/worker/task-runner.js +126 -162
  533. package/dist/worker/worker-context.d.ts +41 -81
  534. package/package.json +4 -4
  535. package/dist/cli/template-files/middleware-clerk.ts.template +0 -37
  536. package/dist/worker/task-queue-config.d.ts +0 -8
  537. package/dist/worker/task-queue.js +0 -19
  538. package/dist/worker/worker-status.d.ts +0 -47
  539. package/dist/worker/worker-status.js +0 -52
@@ -1,183 +1,42 @@
1
1
  /**
2
- * `canopycms init-github-app <create|verify>` — registers the GitHub App the CMS
3
- * worker authenticates as, and reads its grant back.
4
- *
5
- * WHY THIS EXISTS: THE PERMISSIONS ARE CODE HERE
6
- *
7
- * The worker can authenticate as a GitHub App instead of a personal access token
8
- * (adopter request #45). What did not ship with it was any machine-checkable
9
- * statement of which permissions that App needs — the answer lived only as prose
10
- * in `docs/deploying-to-aws.md`, which nothing derives and nothing verifies.
11
- *
12
- * A PAT's scope only ever exists as checkboxes somebody ticked, so "is this
13
- * credential wider than intended?" is unanswerable from the repository. The
14
- * manifest flow takes the permission set as JSON, so `CANOPY_APP_PERMISSIONS`
15
- * below is reviewable, diffable, and a desired state a program can check —
16
- * against the call sites that force each entry (see the constant) and against
17
- * what an installation actually holds (see `verify`).
18
- *
19
- * And guessing too narrow does not fail loudly. `task-runner.ts`'s
20
- * `convert-to-draft` runs a GraphQL mutation, and a GraphQL failure answers HTTP
21
- * 200 with a body-level error carrying NO numeric status — so
22
- * `isPermanentTaskFailure` reads a permission denial as transient, retries to the
23
- * cap, and wedges the branch in `sync-failed` with nothing naming a permission.
24
- * `createOrUpdatePullRequest` swallows a failed `markPullRequestReadyForReview`
25
- * into a warning by design (`github-service.ts`), leaving the PR stuck as a
26
- * draft. Both surface hours after setup. `verify` moves that discovery to setup
27
- * time, which is the only cheap place to find it.
28
- *
29
- * ONE APP PER SITE. NOT ONE APP PER ORGANISATION.
30
- *
31
- * Worth stating at length because the obvious economy is to register one App and
32
- * install it on every repository that needs it, and for a READ-ONLY App whose key
33
- * never leaves GitHub that is right. It is wrong here, and the reason is
34
- * structural rather than a matter of taste:
35
- *
36
- * **A GitHub App's private key is App-level. Installation scoping is not a
37
- * boundary GitHub enforces against whoever holds the key — it is a choice the
38
- * key-holder makes at mint time.** Anyone with the key can sign a JWT, call
39
- * `GET /app/installations` to enumerate every installation, and mint an access
40
- * token for any of them.
41
- *
42
- * Our key does not stay in one place: each site's worker reads it at runtime,
43
- * from that site's own secret store, on that site's own host. So one shared App
44
- * with `contents: write` would mean that reading the marketing site's secret
45
- * store hands you write access to the documentation site's repository. One App
46
- * per site, one installation each, one key each — blast radius contained to the
47
- * site that leaked.
48
- *
49
- * The cost is real and is not pretended away: N sites means N Apps, N keys and N
50
- * things to rotate. That is affordable only because creating one is cheap, which
51
- * is most of why this command exists.
52
- *
53
- * WHAT THE MANIFEST FLOW BUYS, WHICH IS MORE THAN IT LOOKS
54
- *
55
- * `redirect_url` is the point of it. GitHub redirects there with a one-hour code,
56
- * and `POST /app-manifests/{code}/conversions` exchanges that code for the App's
57
- * id, client id and **the private key itself**. So `create` runs a one-shot
58
- * loopback server, captures the key in memory, and hands it straight to a
59
- * destination the operator named. Sent to a command's stdin — the recommended
60
- * route — **the key never touches disk**, and nobody has to find a downloaded
61
- * `.pem`. `--key-out` deliberately does write it to a file, for an operator with
62
- * no such command; that is the trade, not an oversight.
63
- *
64
- * MEASURED (2026-09-05, in a sibling project) and it contradicts the
65
- * documentation: GitHub's REST docs list `redirect_url` as OPTIONAL, and the
66
- * organisation App-creation form refuses a manifest without it — "Invalid GitHub
67
- * App configuration ... Error 'redirect_url' wasn't supplied."
68
- *
69
- * THIS FILE TALKS TO GITHUB WITH `fetch`, NOT OCTOKIT, DELIBERATELY
70
- *
71
- * Two reasons. An App JWT requires `Authorization: Bearer <jwt>`, and a client
72
- * that sends `token <value>` is answered with `401 A JSON web token could not be
73
- * decoded` — measured in the sibling project, where it presented as "no
74
- * installation found" and sent the operator to check a page that was correct.
75
- * And Octokit here would add `octokit.apps.*` call sites to this package, which
76
- * the guard in `github-app-permission-drift.test.ts` would then have to carve an
77
- * exception for — an exception being exactly how a guard goes blind.
78
- *
79
- * The JWT is hand-rolled over `node:crypto` for a third reason that is not
80
- * optional: `.dependency-cruiser.mjs`'s `core-no-github-app-auth` rule scopes to
81
- * every package's own `src/`, so importing `@octokit/auth-app` here is a lint
82
- * error, same as anywhere else in this package.
83
- *
84
- * THE KEY'S DESTINATION IS NOT THIS TOOL'S BUSINESS
85
- *
86
- * `create` takes either a command to pipe the PEM into (`-- <cmd>`) or a file to
87
- * write (`--key-out`), and asks for a file path if that first one fails. It
88
- * knows nothing about AWS, or any other secret store: an adopter may not deploy
89
- * to AWS at all, and a setup tool that hardcodes one cloud is a setup tool for
90
- * one adopter. `docs/deploying-to-aws.md` carries the worked invocation.
2
+ * `canopycms init-github-app <create|verify>` — registers the GitHub App the
3
+ * CMS worker authenticates as, and reads its grant back. A PAT's scope is
4
+ * unauditable — checkboxes somebody ticked, nowhere recorded.
5
+ * `CANOPY_APP_PERMISSIONS` in github-app-manifest.ts is the machine-checkable statement of what
6
+ * this App needs instead, checked against the call sites that force each
7
+ * entry (see the constant) and what an installation holds (see `verify`). A
8
+ * missing permission fails silently — a GraphQL denial answers HTTP 200 with
9
+ * no numeric status — so `verify` exists to catch that at setup time.
10
+ * One App per site, never shared across an organisation: a GitHub App's
11
+ * private key is App-level — GitHub does not enforce per-installation
12
+ * scoping against the key-holder — so anyone holding it can mint a token for
13
+ * every installation. Each site's worker reads its own key from its own
14
+ * secret store, so sharing one App would let one leaked store reach every
15
+ * other site's repository.
16
+ * `create` runs a one-shot loopback server, exchanges GitHub's redirect code
17
+ * for the App's credentials including the private key, then hands it to the
18
+ * destination the operator named — over a command's stdin so it never
19
+ * touches disk, or to a file with `--key-out`. That destination is not this
20
+ * tool's business: it knows nothing about AWS or any other secret store.
21
+ * This file talks to GitHub with `fetch`, not Octokit: an App JWT needs
22
+ * `Authorization: Bearer <jwt>` (sent as `token <value>` it gets a misleading
23
+ * 401), and `.dependency-cruiser.mjs` forbids importing `@octokit/auth-app`
24
+ * from this package's `src/` anyway.
91
25
  */
92
26
  import type { ChildProcess } from 'node:child_process';
93
27
  import { createServer } from 'node:http';
94
- /** Permission levels GitHub uses for App repository permissions, weakest first. */
95
- export declare const PERMISSION_LEVELS: readonly ["read", "write", "admin"];
96
- export type PermissionLevel = (typeof PERMISSION_LEVELS)[number];
97
- export type PermissionSet = Readonly<Record<string, PermissionLevel>>;
98
28
  /**
99
- * The App's entire security surface, and the derivation behind it.
100
- *
101
- * Every entry names the call site that forces it, because a permission whose
102
- * justification lives only in a commit message cannot be re-checked. Enumerated
103
- * from the only two modules in this package that issue GitHub calls —
104
- * `github-service.ts` and `worker/{task-runner,rebase}.ts` — plus every git
105
- * operation that reaches github.com. `canopycms-cdk` makes no REST calls at all.
106
- *
107
- * - `contents: write`
108
- * - git over HTTPS: the first-boot bare clone (`worker/cms-worker.ts`), the
109
- * per-cycle fetch of all branches (`worker/git-sync.ts`), and the branch
110
- * pushes including `--force-with-lease` (`worker/task-runner.ts`).
111
- * - `octokit.git.deleteRef` (`github-service.ts`, `worker/task-runner.ts`).
112
- * GitHub's permissions reference puts `DELETE /repos/{o}/{r}/git/refs/{ref}`
113
- * under Contents/write — NOT `administration`, which is the plausible wrong
114
- * guess. It is also dead code today: nothing enqueues `delete-remote-branch`
115
- * and `GitHubService.deleteBranch`'s only call site is commented out, so it
116
- * forces nothing that pushing does not already force. Declared anyway
117
- * because the handler is live the moment a producer appears.
118
- *
119
- * - `pull_requests: write`
120
- * - `pulls.create` and `pulls.update` (`github-service.ts`,
121
- * `worker/task-runner.ts`). GitHub's reference: Create and Update a pull
122
- * request are Pull requests/write. `pulls.list` and `pulls.get` are
123
- * Pull requests/read, subsumed by this.
124
- * - the GraphQL mutations `markPullRequestReadyForReview` and
125
- * `convertPullRequestToDraft`. **This is the one entry with no
126
- * documentation line behind it** — GitHub's permissions reference
127
- * enumerates REST endpoints only — so it is derived by analogy with the
128
- * REST PR mutations. A live run is the oracle, and until one has happened
129
- * this comment is the honest state of it.
130
- *
131
- * - `metadata: read` — implied: GitHub grants it alongside any repository
132
- * permission. Declared explicitly so this object states the whole surface
133
- * rather than the part that is not automatic.
134
- *
135
- * Nothing else. No `issues` (there are no `issues.*` calls — nothing sets labels
136
- * or assignees, which is the usual reason a PR bot needs it), no `administration`,
137
- * no `actions`, and no organisation permissions. No `workflows`: nothing in the
138
- * worker edits `.github/workflows/`, but GitHub may still refuse to push rebased
139
- * history that carries a base-branch change to it — see
140
- * .claude/future-tasks/worker-push-refused-when-base-changes-workflows.md, an
141
- * open P1.
29
+ * The repository an App is being registered for.
30
+ * @internal Exported for tests.
142
31
  */
143
- export declare const CANOPY_APP_PERMISSIONS: PermissionSet;
144
- /**
145
- * GitHub's limit on an App's display name.
146
- *
147
- * MEASURED 2026-09-06 in a sibling project, by having a 39-character name
148
- * refused while a 33-character one was accepted; the documentation states no
149
- * limit at all. So the true bound is somewhere in 33..38 and 34 is the safe
150
- * direction: a wrongly-refused name costs the operator one `--name` flag, while a
151
- * wrongly-accepted one costs a browser round trip to find out. Re-measure when a
152
- * real App is created from this file.
153
- */
154
- export declare const APP_NAME_MAX_LENGTH = 34;
155
- /**
156
- * How much of a description the account's App LIST renders before truncating —
157
- * mid-word, with an ellipsis.
158
- *
159
- * MEASURED 2026-09-06 in the same project: a description opening "Read-only,
160
- * organisation-wide. Lets th…" was cut at 37 characters. The list appears to
161
- * truncate by CHARACTER rather than by line, so a newline does not rescue a long
162
- * opening sentence. Hence the shape of `appDescription`: a standalone summary
163
- * inside this budget, a blank line, then the detail only the App's own page
164
- * shows.
165
- */
166
- export declare const APP_SUMMARY_MAX_LENGTH = 37;
167
- /** How long `create` waits for the browser round trip before giving up. */
168
- export declare const CALLBACK_TIMEOUT_MS: number;
169
- /** The repository an App is being registered for. */
170
32
  export type AppTarget = {
171
33
  owner: string;
172
34
  repo: string;
173
35
  /** Organisation accounts and user accounts post the manifest to different URLs. */
174
36
  isOrganization: boolean;
175
37
  };
176
- /**
177
- * Where `create` should send the captured private key. Exactly one, chosen by
178
- * the operator BEFORE the App exists — see `requireDestination`.
179
- */
180
- export type KeyDestination = {
38
+ /** Where `create` should send the captured private key, chosen by the operator BEFORE the App exists. */
39
+ type KeyDestination = {
181
40
  kind: 'command';
182
41
  argv: string[];
183
42
  } | {
@@ -185,246 +44,85 @@ export type KeyDestination = {
185
44
  filePath: string;
186
45
  };
187
46
  /**
188
- * The App's display name, and the slug GitHub derives from it.
189
- *
190
- * Named after the REPOSITORY, because the App is per-site (see the file header):
191
- * an App named after the organisation would suggest it is shared, which is the
192
- * arrangement this file exists to avoid.
193
- *
194
- * GitHub App names are unique across the WHOLE of GitHub, not merely within an
195
- * account, so a short generic name is plausibly already taken by somebody else.
196
- * `create` pre-checks what it can and `--name` overrides; see
197
- * `checkNameAvailable`.
198
- */
199
- export declare function appName(repo: string): string;
200
- /**
201
- * GitHub's slug derivation, as far as this needs it: lowercase, and runs of
202
- * anything that is not alphanumeric collapse to single hyphens.
203
- *
204
- * Pinned by a test. If GitHub ever derives differently the name pre-check goes
205
- * blind rather than loud, so this is not something to assume.
206
- */
207
- export declare function appSlug(name: string): string;
208
- /**
209
- * A short summary that survives the App list's truncation, then the detail.
210
- *
211
- * Two audiences, two parts. Deliberately generic in both: this text is stored ON
212
- * GITHUB rather than here, so anything in it that can go stale goes stale
213
- * silently, with nothing in this repository able to detect or fix it. It
214
- * therefore says only what stays true of the App itself — what it does, that it
215
- * is installed on one repository, and that it is registered per site.
216
- */
217
- export declare function appDescription(): string;
218
- /**
219
- * Where the manifest form posts, which differs by account type.
220
- *
221
- * A solo adopter who owns the content repository personally needs the user-level
222
- * URL; posting an organisation manifest to it (or the reverse) fails at the form.
47
+ * Where the manifest form posts: user-owned and organisation repositories use
48
+ * different URLs, and posting the wrong one fails at the form.
49
+ * @internal Exported for tests.
223
50
  */
224
51
  export declare function manifestPostUrl(target: AppTarget, state: string): string;
225
52
  /**
226
- * The App as GitHub's manifest flow takes it.
227
- *
228
- * `default_permissions` is the entire security surface and lives in
229
- * `CANOPY_APP_PERMISSIONS`, where each entry carries the call site that forces
230
- * it. `default_events` is empty and the webhook is inactive: nothing is ever
231
- * delivered to this App — it is only ever assumed outward by the worker.
53
+ * The App as GitHub's manifest flow takes it. `default_permissions` is the
54
+ * entire security surface, defined in `CANOPY_APP_PERMISSIONS`. The webhook is
55
+ * declared but inactive: nothing is ever delivered to this App.
56
+ * @internal Exported for tests.
232
57
  */
233
58
  export declare function appManifest(target: AppTarget, redirectUrl: string, name?: string): Record<string, unknown>;
234
59
  /**
235
- * The auto-submitting form that carries the manifest to GitHub.
236
- *
237
- * A manifest can only be delivered as a form POST from a browser, which is why
238
- * this exists at all. Both interpolations are escaped: the manifest embeds
239
- * adopter-supplied owner and repository names, and this string is written to a
240
- * file that a browser then executes.
60
+ * The auto-submitting form that carries the manifest to GitHub — a manifest
61
+ * can only be delivered as a browser form POST. Both interpolations are
62
+ * escaped: this embeds adopter-supplied names into a file a browser executes.
63
+ * @internal Exported for tests.
241
64
  */
242
65
  export declare function creationForm(target: string, manifest: Record<string, unknown>): string;
243
66
  /**
244
- * A short-lived App JWT, signed RS256 with `node:crypto`.
245
- *
246
- * `iat` is backdated 60s because GitHub rejects a JWT whose `iat` is in its own
247
- * future, and a second of clock skew between here and GitHub is entirely
248
- * ordinary. `exp` leaves headroom rather than sitting on GitHub's stated
249
- * ten-minute maximum for no benefit.
67
+ * A short-lived App JWT, signed RS256 with `node:crypto`. `iat` is backdated
68
+ * 60s because GitHub rejects a JWT whose `iat` is in its own future, and a
69
+ * second of clock skew is ordinary; `exp` leaves headroom rather than sitting
70
+ * on GitHub's stated ten-minute maximum for no benefit.
71
+ * @internal Exported for tests.
250
72
  */
251
73
  export declare function appJwt(issuer: string, privateKey: string, now?: number): string;
252
- export type GitHubResponse<T> = {
253
- ok: boolean;
254
- status: number;
255
- body: T | null;
256
- /**
257
- * On a FAILURE: GitHub's own `message`, a redacted prefix of the response text,
258
- * or — when `status` is 0, meaning the request or the body read never completed
259
- * — the redacted client-side error, since there is no response to quote. Empty
260
- * string on success: a success body here is App credentials, and a prefix of one
261
- * is not worth handing to a caller that might print it.
262
- */
263
- message: string;
264
- };
265
- /**
266
- * One call to api.github.com.
267
- *
268
- * Returns the status and body rather than collapsing failure to `null`, because
269
- * the two failures a caller has to tell apart here — "this credential cannot
270
- * authenticate" and "this App is not installed on that repository" — send an
271
- * operator to entirely different pages. Collapsing them is measured to have sent
272
- * a real run's operator to check a page that was correct.
273
- */
274
- export declare function githubRequest<T>(apiPath: string, init?: {
275
- method?: string;
276
- body?: unknown;
277
- token?: string;
278
- jwt?: string;
279
- }): Promise<GitHubResponse<T>>;
280
- /** The parts of GitHub's installation object this tool reads back. */
281
- export type InstallationSummary = {
282
- id: number;
283
- permissions?: Record<string, string>;
284
- repository_selection?: string;
285
- suspended_at?: string | null;
286
- app_slug?: string;
287
- };
288
- export type ReadbackFinding = {
289
- severity: 'error' | 'warn';
290
- message: string;
291
- };
292
- /**
293
- * Compare what an installation actually holds against what this package needs.
294
- *
295
- * Pure, so the whole judgement is testable without a live App — which matters,
296
- * because everything around it can only ever be exercised by hand.
297
- *
298
- * Reports in BOTH directions. A credential does not usually fail by being
299
- * unusable; that gets noticed immediately. It fails by being one permission too
300
- * wide, which works perfectly and is never noticed. So an extra permission, or
301
- * an installation scoped to every repository in the account, is an error here
302
- * and not a shrug.
303
- */
304
- export declare function readbackVerdict(installation: InstallationSummary, desired?: PermissionSet): ReadbackFinding[];
305
- export type HandOffResult = {
74
+ type HandOffResult = {
306
75
  stored: boolean;
307
76
  detail: string;
308
77
  };
309
78
  /**
310
- * Injectable so `handOffKey` is testable without spawning anything real.
311
- *
312
- * Everywhere but Windows the child must carry the bridge's status channel at
313
- * `stdio[3]`, as `spawnKeyDestination` does: `handOffKey` reads `cat`'s exit
314
- * status from it, and without a `0` there nothing is reported as stored.
315
- */
316
- export type SpawnFn = (command: string, args: string[]) => ChildProcess;
317
- /**
318
- * Send the captured private key to the destination the operator named.
319
- *
320
- * For a command: no shell parses the operator's argv (see `KEY_INPUT_BRIDGE`
321
- * for how `sh` is used without doing so). The PEM goes over **stdin**, never
322
- * argv — an argument is visible in `ps` and lands in shell history. The
323
- * child's stdout and stderr are INHERITED rather than captured, and that is
324
- * load-bearing rather than lazy: `aws secretsmanager create-secret` prints the
325
- * ARN, and CDK's `Secret.fromSecretCompleteArn` needs the full ARN including its
326
- * six-character suffix. Swallowing the child's output would leave the operator
327
- * holding a stored secret with no way to learn the one string that wires it up.
328
- *
329
- * The `'error'` listener on `child.stdin` is not optional. A child that exits or
330
- * closes stdin early can make the write fail with EPIPE, and an unhandled error
331
- * on a stream takes the process down — carrying with it the only copy of a
332
- * private key for an App that already exists.
333
- *
334
- * WHAT `stored: true` DOES AND DOES NOT MEAN. Everywhere but Windows it means
335
- * three things: the command exited 0, the `cat` feeding it exited 0, and nothing
336
- * went wrong writing to it. `cat` exiting 0 means every byte of the key went into
337
- * the pipe. It still does NOT prove the command READ them, and cannot: a pipe
338
- * write lands in the kernel's buffer, and a key this size fits in one write, so a
339
- * command that exits without reading races `cat`'s write and can lose or win.
340
- * MEASURED on macOS with a real 1.7KB key, 100 runs each through this function,
341
- * with the same result before the bridge and after it: `true`, `head -c 10`,
342
- * `sh -c 'exec 0<&-; exit 0'` and `sh -c 'head -c 5 >/dev/null; exit 0'` were
343
- * each reported stored 100 times of 100. What IS caught, whatever the timing,
344
- * is a command that leaves more unread than a pipe buffer holds: ~100KB into
345
- * `true` was reported not stored 20 times of 20, by `cat` exiting 141 — the
346
- * same case the direct spawn caught as a write EPIPE.
347
- *
348
- * So the command's EXIT CODE is still the contract, and the operator is still
349
- * responsible for naming a command that fails loudly. That is stated here rather
350
- * than papered over, because the tempting fix — waiting for the stream to flush
351
- * — measures the kernel buffer rather than the command, and would look like a
352
- * check while being one.
79
+ * Injectable so `handOffKey` is testable without spawning anything real. Everywhere but Windows
80
+ * the child carries the bridge's status channel at `stdio[3]`, without which nothing is stored.
81
+ */
82
+ type SpawnFn = (command: string, args: string[]) => ChildProcess;
83
+ /**
84
+ * Send the captured private key to the destination the operator named. For a
85
+ * command, the PEM goes over **stdin**, never argv — an argument is visible
86
+ * in `ps` and lands in shell history (see `KEY_INPUT_BRIDGE` for how `sh`
87
+ * runs it without parsing that argv itself). Stdout/stderr are INHERITED,
88
+ * not captured: `aws secretsmanager create-secret` prints the ARN the
89
+ * operator needs, which this function must not swallow. The `'error'`
90
+ * listener on `child.stdin` is not optional: a child that exits or closes
91
+ * stdin early can fail the write with EPIPE, an unhandled stream error that
92
+ * would take the process — and the key — down with it.
93
+ * Everywhere but Windows, `stored: true` means the command exited 0, `cat`
94
+ * exited 0, and nothing went wrong writing — every byte reached the pipe. It
95
+ * does NOT prove the command read them: a write lands in the kernel buffer,
96
+ * and a key this size fits in one write, so a command that exits without
97
+ * reading races `cat`'s write and can win or lose silently. What IS caught is
98
+ * a command leaving more unread than the buffer holds, which fails loudly as
99
+ * `cat` exiting on SIGPIPE — so the exit code is still the contract, and
100
+ * naming a command that fails loudly is the operator's job.
101
+ * @internal Exported for tests.
353
102
  */
354
103
  export declare function handOffKey(pem: string, destination: KeyDestination, spawnFn?: SpawnFn): Promise<HandOffResult>;
355
- export type CallbackServer = {
104
+ type CallbackServer = {
356
105
  port: number;
357
106
  code: Promise<string>;
358
107
  close: () => void;
359
108
  };
360
109
  /**
361
- * One request, then closed — closed by the handler itself, as soon as it has the
362
- * code. Bound to loopback only.
363
- *
364
- * The standard CLI pattern (`gh auth login` does the same): it exists solely so
365
- * the private key can arrive over the redirect rather than through a downloads
366
- * folder.
367
- *
368
- * A REJECTED REQUEST IS ANSWERED AND IGNORED, never fatal. The `state` check is
369
- * the CSRF guard the manifest flow provides, but rejecting the promise on a
370
- * mismatch ends the run on the first stray loopback request — a browser
371
- * prefetch, an extension, a retried tab — possibly seconds before GitHub's real
372
- * redirect arrives, and AFTER the App has been created. Aborting there leaves an
373
- * App nobody holds a key for, over a request that was never GitHub's.
110
+ * One request, then closed by the handler itself, bound to loopback only —
111
+ * the standard CLI pattern (`gh auth login` does the same) so the key
112
+ * arrives over the redirect, not a downloads folder. A rejected request is
113
+ * answered and ignored, never fatal: the `state` check
114
+ * is the CSRF guard the manifest flow provides, but rejecting the promise on
115
+ * a mismatch would end the run on a stray loopback request, possibly before
116
+ * GitHub's real redirect, leaving an App nobody holds a key for.
117
+ * @internal Exported for tests.
374
118
  */
375
119
  export declare function startCallbackServer(state: string, timeoutMs?: number, createServerFn?: typeof createServer): Promise<CallbackServer>;
376
- type CreatedApp = {
377
- id: number;
378
- slug: string;
379
- clientId: string;
380
- pem: string;
381
- };
382
- /**
383
- * The exchange. Unauthenticated by design: the one-hour code IS the credential.
384
- *
385
- * Only the four fields this tool needs are lifted out. The response also carries
386
- * `client_secret` and `webhook_secret`, which this App never uses — so the body
387
- * is never logged, never returned whole, and never reaches an error message.
388
- * Failures collapse to `null` and nothing from the response is surfaced, for
389
- * the same reason: the code itself is exchangeable for the private key for a
390
- * full hour. That also drops the HTTP status, which the caller does not report
391
- * (.claude/future-tasks/init-github-app-review-round-1-lows.md, item 2).
392
- */
393
- export declare function convertManifest(code: string): Promise<CreatedApp | null>;
394
- /**
395
- * Whether the account is an organisation, which decides where the manifest posts.
396
- *
397
- * Unauthenticated, so it needs no credential at a point in the flow where we have
398
- * none. `null` means "could not tell" and the caller says so rather than
399
- * guessing — guessing wrong sends the operator to a form that refuses the
400
- * manifest with no useful explanation.
401
- */
402
- export declare function detectAccountType(owner: string): Promise<boolean | null>;
403
- /**
404
- * Best-effort check that the App name is not already taken.
405
- *
406
- * `true` = available as far as we can tell, `false` = definitely taken, `null` =
407
- * could not check. The three are kept apart deliberately: a PRIVATE App with
408
- * this slug also answers 404, so a 404 is not proof of availability, and
409
- * reporting "could not check" as "checked, it is free" is how a guard goes
410
- * blind. This exists because name collisions can otherwise only be discovered by
411
- * the creation form — with the CLI already sitting on a loopback server waiting
412
- * for a redirect that will never arrive.
413
- */
414
- export declare function checkNameAvailable(slug: string): Promise<boolean | null>;
415
- /** Wait for the operator to continue. Returns at once if stdin has ended. */
416
- export declare function pressEnter(prompt: string): Promise<void>;
417
- /** Read one line from the operator. `null` when stdin ended instead. */
418
- export declare function askLine(prompt: string): Promise<string | null>;
419
- /** Reset between tests. Not used by the command itself. */
420
- export declare function resetStdinStateForTesting(): void;
421
120
  /**
422
- * What to do with one answer to the retry prompt: write the key to a file, ask
423
- * again, or stop asking. There is deliberately no command here — see
424
- * `parseKeyRetryAnswer`.
425
- *
426
- * `reprompt` carries WHY, so `handOffWithRetry` can tell the operator what was
427
- * wrong with what they typed instead of silently asking again.
121
+ * What to do with one answer to the retry prompt: write to a file, ask again,
122
+ * or stop. No command here — see `parseKeyRetryAnswer`. `reprompt` carries
123
+ * WHY, so `handOffWithRetry` can tell the operator what was wrong instead of
124
+ * silently asking again.
125
+ * @internal Exported for tests.
428
126
  */
429
127
  export type KeyRetryChoice = {
430
128
  kind: 'give-up';
@@ -436,72 +134,40 @@ export type KeyRetryChoice = {
436
134
  filePath: string;
437
135
  };
438
136
  /**
439
- * Route one line of operator input at the retry prompt, which accepts a FILE
440
- * PATH and nothing else. Exported and pure so every rule below is a table test
441
- * rather than something only reachable by driving a live prompt end to end.
442
- *
443
- * WHY NO COMMANDS. The first destination can be a command because the
444
- * operator's own shell parsed `-- <command…>` into argv. A typed line here has
445
- * no such parser, and three consecutive review rounds each found splitting it on
446
- * whitespace sending the key somewhere unintended: a one-word answer written as
447
- * a file in the working directory; a `|` inside the argv becoming a file name;
448
- * then `>`, `;`, `&&` and quotes each becoming literal argv words, so
449
- * `tee key.pem > /dev/null` wrote 0644 copies of the key into the repository
450
- * root and reported it stored. Each fix closed one spelling. Accepting a path
451
- * only closes the class: an operator who wants a command writes the file, runs
452
- * the command against it, and deletes the file.
453
- *
454
- * The rules, in order:
455
- *
456
- * - `null` (stdin has already ended — see `stdinEnded` above) gives up: there
457
- * is nobody left who could answer.
458
- * - A blank or whitespace-only line MUST NOT give up. Nothing reads stdin
459
- * until a readline interface exists, so an Enter pressed during the long
460
- * wait for GitHub's redirect — or while the FIRST destination's command was
461
- * running — sits in the terminal's line buffer. When that first hand-off
462
- * then fails, `askLine` reads the buffered blank line immediately, before
463
- * the operator has even seen this prompt. Treating that as "give up"
464
- * discarded the only copy of the key without anyone actually answering.
465
- * Re-prompting instead costs nothing when the blank line really was
466
- * intentional, because the very next prompt still offers "give up" as
467
- * something the operator has to type.
468
- * - The words "give up", case-insensitively and with whitespace normalised, is
469
- * the one deliberate way to give up once stdin is live.
470
- * - Any whitespace inside the answer, or a leading `|`, is re-prompted with
471
- * "commands are not accepted here" and the write-run-delete route. Every
472
- * command-shaped answer above was spelled with whitespace, and nothing about
473
- * a typed line tells a command apart from a path that contains spaces — so
474
- * the cost, stated in the reason, is that such a path cannot be entered here.
475
- * - A leading `~` is re-prompted: nothing here expands it, so `~/key.pem`
476
- * would name a directory literally called `~` under the working directory.
477
- * - A single token with NO `/` or `\` — `pbcopy`, `wl-copy`, any script on
478
- * PATH — is re-prompted, suggesting `./<token>`. A "one word is a path" rule
479
- * once let `writeFile('pbcopy', pem, { flag: 'wx' })` succeed in
480
- * `process.cwd()` — normally the git repository root — leaving a
481
- * `contents: write` private key sitting untracked on disk, while the
482
- * operator read "written to pbcopy (mode 0600)" and believed it had gone
483
- * into the command they meant.
484
- * - Anything else is a path, which `handOffKey` creates with mode 0600 and
137
+ * Route one line of operator input at the retry prompt, accepting a FILE
138
+ * PATH and nothing else (exported and pure, so every rule is table-tested
139
+ * directly). The first destination can be a command because the operator's
140
+ * shell parses `-- <command…>` into argv; a typed line has no such parser,
141
+ * and splitting on whitespace sends the key somewhere unintended (a stray
142
+ * `|`, `>`, `;`, `&&` or quote becomes a literal argv word). Accepting only a
143
+ * path closes that class — a command means write, run, then delete. Rules,
144
+ * in order:
145
+ * - `null` (stdin has already ended — see prompt.ts) gives up.
146
+ * - A blank or whitespace-only line MUST NOT give up — a stray Enter pressed
147
+ * earlier sits buffered and is read back before the operator has seen this
148
+ * prompt — but the words "give up", case-insensitively and
149
+ * whitespace-normalised, deliberately do once stdin is live.
150
+ * - Whitespace inside the answer, or a leading `|`, is re-prompted: nothing
151
+ * tells a command apart from a path containing spaces, so such paths
152
+ * aren't accepted either.
153
+ * - A leading `~` is re-prompted (unexpanded, `~/key.pem` would name a
154
+ * directory literally called `~`), as is a single token with no `/` or `\`
155
+ * (`pbcopy`, any PATH script): as a bare word it would write the key into
156
+ * the current directory while the operator believes it reached the command.
157
+ * - Anything else is a path: `handOffKey` creates it with mode 0600 and
485
158
  * refuses to write if something already exists there.
159
+ * @internal Exported for tests.
486
160
  */
487
161
  export declare function parseKeyRetryAnswer(answer: string | null): KeyRetryChoice;
488
162
  /**
489
163
  * Hand the key over, and keep asking while the operator still has a chance.
490
- *
491
- * The pre-flight can only refuse a MISSING destination — it cannot know whether
492
- * a command exists, whether a path is writable, or whether a secret store will
493
- * accept the call. Measured: `--key-out` at a directory, `--key-out` under a
494
- * parent that does not exist, and a command not on PATH all pass the pre-flight
495
- * and fail here. Exiting at that point destroys the only copy of a private key
496
- * for an App that already exists, over a typo.
497
- *
498
- * So a failure asks for a file path instead, while the key is still in memory —
499
- * a path only, never a command, for the reasons on `parseKeyRetryAnswer`.
500
- * `create` already requires a TTY, so there is someone there to answer. Only a
501
- * closed stdin (nobody left to answer) or the operator typing "give up" ends the
502
- * loop without a destination — a blank line re-prompts instead, since that is
503
- * exactly what a stray Enter pressed before this prompt existed leaves buffered.
504
- * See `parseKeyRetryAnswer` for the exact rules.
164
+ * The pre-flight can only refuse a MISSING destination — a bad path or a
165
+ * command not on PATH fails only here, and exiting on that would destroy the
166
+ * only copy of a private key for an App that already exists, over a typo. So
167
+ * a failure asks for a file path instead, while the key is still in memory —
168
+ * a path only, never a command, per `parseKeyRetryAnswer`'s rules. Only a
169
+ * closed stdin or "give up" ends the loop without a destination.
170
+ * @internal Exported for tests.
505
171
  */
506
172
  export declare function handOffWithRetry(pem: string, destination: KeyDestination): Promise<boolean>;
507
173
  export type InitGitHubAppOptions = {