dineway 0.1.49 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (434) hide show
  1. package/dist/activity-DUaz_w7p.mjs +79 -0
  2. package/dist/{activity-events-CwM7yxPW.mjs → activity-events-CJDvE2uc.mjs} +5 -113
  3. package/dist/actor-0QJ1Gbtf.mjs +37 -0
  4. package/dist/after-BfniwgIf.mjs +9 -0
  5. package/dist/{allowed-origins-CyGnbgBd.mjs → allowed-origins--OwHHiY8.mjs} +1 -1
  6. package/dist/api/route-utils.d.mts +9 -10
  7. package/dist/api/route-utils.mjs +16 -13
  8. package/dist/api/schemas/index.d.mts +2 -2
  9. package/dist/api/schemas/index.mjs +5 -4
  10. package/dist/{api-BMIxRIEN.mjs → api-B4PHIF4V.mjs} +93 -41
  11. package/dist/{api-tokens-DtvdtIkm.mjs → api-tokens-tbK_sORH.mjs} +6 -2
  12. package/dist/{apply-D_RlTYtC.mjs → apply-DjTqq6Ae.mjs} +117 -77
  13. package/dist/astro/image-endpoint.d.mts +7 -0
  14. package/dist/astro/image-endpoint.mjs +54 -0
  15. package/dist/astro/index.d.mts +34 -11
  16. package/dist/astro/index.mjs +163 -24
  17. package/dist/astro/integration/shims/use-sync-external-store-with-selector.js +71 -0
  18. package/dist/astro/integration/shims/use-sync-external-store.js +1 -0
  19. package/dist/astro/middleware/auth.d.mts +8 -18
  20. package/dist/astro/middleware/auth.mjs +42 -24
  21. package/dist/astro/middleware/redirect.mjs +13 -4
  22. package/dist/astro/middleware/request-context.mjs +8 -5
  23. package/dist/astro/middleware/seed.d.mts +1 -1
  24. package/dist/astro/middleware/seed.mjs +2 -2
  25. package/dist/astro/middleware/setup.mjs +1 -1
  26. package/dist/astro/middleware.mjs +389 -206
  27. package/dist/astro/routes/admin.astro +6 -1
  28. package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +7 -6
  29. package/dist/astro/routes/api/admin/allowed-domains/index.mjs +7 -6
  30. package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +4 -4
  31. package/dist/astro/routes/api/admin/api-tokens/index.mjs +6 -6
  32. package/dist/astro/routes/api/admin/briefing.mjs +21 -17
  33. package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +4 -4
  34. package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +9 -8
  35. package/dist/astro/routes/api/admin/byline-fields/index.mjs +9 -8
  36. package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +9 -8
  37. package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +17 -14
  38. package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +17 -14
  39. package/dist/astro/routes/api/admin/bylines/index.mjs +17 -14
  40. package/dist/astro/routes/api/admin/comments/_id_/status.mjs +14 -12
  41. package/dist/astro/routes/api/admin/comments/_id_.mjs +6 -5
  42. package/dist/astro/routes/api/admin/comments/bulk.mjs +11 -9
  43. package/dist/astro/routes/api/admin/comments/counts.mjs +6 -5
  44. package/dist/astro/routes/api/admin/comments/index.mjs +11 -9
  45. package/dist/astro/routes/api/admin/context/_id_/history.mjs +27 -24
  46. package/dist/astro/routes/api/admin/context/_id_/index.mjs +27 -24
  47. package/dist/astro/routes/api/admin/context/_id_/review.mjs +32 -28
  48. package/dist/astro/routes/api/admin/context/_id_/supersede.mjs +32 -28
  49. package/dist/astro/routes/api/admin/context/diff.mjs +31 -27
  50. package/dist/astro/routes/api/admin/context/index.mjs +32 -28
  51. package/dist/astro/routes/api/admin/context/stale.mjs +31 -27
  52. package/dist/astro/routes/api/admin/hitl-requests/_id_/index.mjs +27 -24
  53. package/dist/astro/routes/api/admin/hitl-requests/_id_/resolve.mjs +31 -27
  54. package/dist/astro/routes/api/admin/hitl-requests/index.mjs +31 -27
  55. package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +28 -25
  56. package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +3 -3
  57. package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +6 -6
  58. package/dist/astro/routes/api/admin/oauth-clients/index.mjs +6 -6
  59. package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +56 -51
  60. package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +56 -51
  61. package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +47 -43
  62. package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +54 -49
  63. package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +53 -48
  64. package/dist/astro/routes/api/admin/plugins/index.mjs +47 -43
  65. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +2 -2
  66. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +46 -42
  67. package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +53 -48
  68. package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +46 -42
  69. package/dist/astro/routes/api/admin/plugins/updates.mjs +46 -42
  70. package/dist/astro/routes/api/admin/review-requests/_id_/index.mjs +5 -5
  71. package/dist/astro/routes/api/admin/review-requests/_id_/resolve.mjs +32 -63
  72. package/dist/astro/routes/api/admin/review-requests/index.d.mts +2 -1
  73. package/dist/astro/routes/api/admin/review-requests/index.mjs +58 -10
  74. package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +46 -42
  75. package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +2 -2
  76. package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +46 -42
  77. package/dist/astro/routes/api/admin/users/_id_/disable.mjs +3 -3
  78. package/dist/astro/routes/api/admin/users/_id_/enable.mjs +2 -2
  79. package/dist/astro/routes/api/admin/users/_id_/index.mjs +8 -7
  80. package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +4 -4
  81. package/dist/astro/routes/api/admin/users/index.mjs +7 -6
  82. package/dist/astro/routes/api/auth/dev-bypass.mjs +4 -4
  83. package/dist/astro/routes/api/auth/invite/accept.mjs +2 -2
  84. package/dist/astro/routes/api/auth/invite/complete.mjs +12 -11
  85. package/dist/astro/routes/api/auth/invite/index.mjs +9 -8
  86. package/dist/astro/routes/api/auth/invite/register-options.mjs +11 -10
  87. package/dist/astro/routes/api/auth/logout.mjs +2 -2
  88. package/dist/astro/routes/api/auth/magic-link/send.mjs +11 -10
  89. package/dist/astro/routes/api/auth/magic-link/verify.mjs +3 -3
  90. package/dist/astro/routes/api/auth/me.mjs +9 -8
  91. package/dist/astro/routes/api/auth/mode.mjs +1 -1
  92. package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +3 -3
  93. package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
  94. package/dist/astro/routes/api/auth/passkey/_id_.mjs +6 -5
  95. package/dist/astro/routes/api/auth/passkey/index.mjs +1 -1
  96. package/dist/astro/routes/api/auth/passkey/options.mjs +12 -11
  97. package/dist/astro/routes/api/auth/passkey/register/options.mjs +10 -9
  98. package/dist/astro/routes/api/auth/passkey/register/verify.mjs +11 -10
  99. package/dist/astro/routes/api/auth/passkey/verify.mjs +11 -10
  100. package/dist/astro/routes/api/auth/setup-token/index.mjs +5 -5
  101. package/dist/astro/routes/api/auth/setup-token/verify.mjs +2 -2
  102. package/dist/astro/routes/api/auth/signup/complete.mjs +12 -11
  103. package/dist/astro/routes/api/auth/signup/request.mjs +11 -10
  104. package/dist/astro/routes/api/auth/signup/verify.mjs +2 -2
  105. package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +15 -13
  106. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.d.mts +8 -0
  107. package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +160 -0
  108. package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +2 -2
  109. package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +6 -5
  110. package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +6 -5
  111. package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +6 -5
  112. package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +11 -10
  113. package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +49 -20
  114. package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +6 -5
  115. package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +2 -2
  116. package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +67 -32
  117. package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +16 -14
  118. package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +2 -2
  119. package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +6 -5
  120. package/dist/astro/routes/api/content/_collection_/_id_.mjs +11 -9
  121. package/dist/astro/routes/api/content/_collection_/authors.mjs +2 -2
  122. package/dist/astro/routes/api/content/_collection_/index.mjs +11 -9
  123. package/dist/astro/routes/api/content/_collection_/trash.mjs +7 -6
  124. package/dist/astro/routes/api/dashboard.mjs +9 -8
  125. package/dist/astro/routes/api/dev/emails.mjs +2 -2
  126. package/dist/astro/routes/api/health.mjs +1 -1
  127. package/dist/astro/routes/api/import/probe.d.mts +3 -3
  128. package/dist/astro/routes/api/import/probe.mjs +22 -10
  129. package/dist/astro/routes/api/import/wordpress/analyze.mjs +9 -6
  130. package/dist/astro/routes/api/import/wordpress/execute.d.mts +18 -1
  131. package/dist/astro/routes/api/import/wordpress/execute.mjs +85 -37
  132. package/dist/astro/routes/api/import/wordpress/media.d.mts +12 -1
  133. package/dist/astro/routes/api/import/wordpress/media.mjs +20 -14
  134. package/dist/astro/routes/api/import/wordpress/prepare.mjs +36 -32
  135. package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +17 -1
  136. package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +121 -100
  137. package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
  138. package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +24 -12
  139. package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +55 -2
  140. package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +284 -55
  141. package/dist/astro/routes/api/manifest.mjs +14 -5
  142. package/dist/astro/routes/api/mcp.mjs +753 -170
  143. package/dist/astro/routes/api/media/_id_/confirm.mjs +38 -10
  144. package/dist/astro/routes/api/media/_id_.mjs +11 -7
  145. package/dist/astro/routes/api/media/file/_...key_.mjs +1 -1
  146. package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +2 -2
  147. package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +2 -2
  148. package/dist/astro/routes/api/media/providers/index.mjs +2 -2
  149. package/dist/astro/routes/api/media/upload-url.mjs +11 -10
  150. package/dist/astro/routes/api/media.mjs +28 -35
  151. package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +32 -28
  152. package/dist/astro/routes/api/menus/_name_/items.mjs +32 -28
  153. package/dist/astro/routes/api/menus/_name_/reorder.mjs +32 -28
  154. package/dist/astro/routes/api/menus/_name_/translations.mjs +32 -28
  155. package/dist/astro/routes/api/menus/_name_.mjs +32 -28
  156. package/dist/astro/routes/api/menus/index.mjs +32 -28
  157. package/dist/astro/routes/api/oauth/authorize.mjs +7 -7
  158. package/dist/astro/routes/api/oauth/device/authorize.mjs +6 -6
  159. package/dist/astro/routes/api/oauth/device/code.mjs +9 -9
  160. package/dist/astro/routes/api/oauth/device/token.mjs +8 -8
  161. package/dist/astro/routes/api/oauth/register.mjs +4 -4
  162. package/dist/astro/routes/api/oauth/token/refresh.mjs +6 -6
  163. package/dist/astro/routes/api/oauth/token/revoke.mjs +6 -6
  164. package/dist/astro/routes/api/oauth/token.mjs +6 -6
  165. package/dist/astro/routes/api/openapi.json.mjs +4 -3
  166. package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +46 -31
  167. package/dist/astro/routes/api/redirects/404s/index.mjs +11 -10
  168. package/dist/astro/routes/api/redirects/404s/summary.mjs +11 -10
  169. package/dist/astro/routes/api/redirects/_id_.mjs +33 -29
  170. package/dist/astro/routes/api/redirects/index.mjs +33 -29
  171. package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +2 -2
  172. package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +2 -2
  173. package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +48 -43
  174. package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +48 -43
  175. package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +48 -43
  176. package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +48 -43
  177. package/dist/astro/routes/api/schema/collections/index.mjs +48 -43
  178. package/dist/astro/routes/api/schema/index.mjs +14 -15
  179. package/dist/astro/routes/api/schema/orphans/_slug_.mjs +46 -42
  180. package/dist/astro/routes/api/schema/orphans/index.mjs +46 -42
  181. package/dist/astro/routes/api/search/enable.mjs +11 -10
  182. package/dist/astro/routes/api/search/index.mjs +10 -9
  183. package/dist/astro/routes/api/search/rebuild.mjs +11 -10
  184. package/dist/astro/routes/api/search/stats.mjs +6 -6
  185. package/dist/astro/routes/api/search/suggest.mjs +10 -9
  186. package/dist/astro/routes/api/sections/_slug_.mjs +32 -28
  187. package/dist/astro/routes/api/sections/index.mjs +32 -28
  188. package/dist/astro/routes/api/settings/email.mjs +4 -4
  189. package/dist/astro/routes/api/settings.mjs +32 -28
  190. package/dist/astro/routes/api/setup/admin-verify.mjs +14 -13
  191. package/dist/astro/routes/api/setup/admin.mjs +12 -11
  192. package/dist/astro/routes/api/setup/dev-bypass.mjs +31 -25
  193. package/dist/astro/routes/api/setup/dev-reset.mjs +2 -2
  194. package/dist/astro/routes/api/setup/index.mjs +29 -25
  195. package/dist/astro/routes/api/setup/status.mjs +3 -3
  196. package/dist/astro/routes/api/snapshot.mjs +8 -7
  197. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +35 -31
  198. package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +35 -31
  199. package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +35 -31
  200. package/dist/astro/routes/api/taxonomies/index.mjs +35 -31
  201. package/dist/astro/routes/api/themes/preview.mjs +5 -5
  202. package/dist/astro/routes/api/typegen.mjs +4 -4
  203. package/dist/astro/routes/api/well-known/auth.mjs +3 -3
  204. package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +3 -3
  205. package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +3 -3
  206. package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +31 -27
  207. package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +32 -28
  208. package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +32 -28
  209. package/dist/astro/routes/api/widget-areas/_name_.mjs +29 -26
  210. package/dist/astro/routes/api/widget-areas/index.mjs +32 -28
  211. package/dist/astro/routes/api/widget-components.mjs +2 -2
  212. package/dist/astro/routes/robots.txt.mjs +8 -7
  213. package/dist/astro/routes/sitemap-_collection_.xml.mjs +23 -14
  214. package/dist/astro/routes/sitemap.xml.mjs +9 -8
  215. package/dist/astro/session-driver.d.mts +1 -1
  216. package/dist/astro/session-driver.mjs +2 -2
  217. package/dist/astro/types.d.mts +22 -13
  218. package/dist/{audit-BeNsSyfU.mjs → audit-B2pRxZ20.mjs} +1 -1
  219. package/dist/auth/providers/github.d.mts +1 -1
  220. package/dist/auth/providers/google.d.mts +1 -1
  221. package/dist/{auth-control-guard-Dr14I98m.mjs → auth-control-guard-D71jourk.mjs} +1 -1
  222. package/dist/{authorize-BLO0m7fW.mjs → authorize-ZXPGPwKn.mjs} +1 -1
  223. package/dist/{briefing-CkTee1ex.mjs → briefing-0rf-su3-.mjs} +90 -28
  224. package/dist/{briefing-DJX6irto.mjs → briefing-kRhegBoF.mjs} +2 -1
  225. package/dist/bundle-CEcdUa1s.mjs +602 -0
  226. package/dist/{byline-BVC1lV6G.mjs → byline-7xR185Ly.mjs} +30 -62
  227. package/dist/{byline-fields-Cj39WhXC.mjs → byline-fields-BKzA0fJU.mjs} +1 -1
  228. package/dist/{byline-fields-uJlygdzM.d.mts → byline-fields-Ckeqd1OY.d.mts} +88 -35
  229. package/dist/{byline-fields-cNrLJVRs.mjs → byline-fields-WN7RQT4q.mjs} +10 -4
  230. package/dist/{byline-registry-4v6tXMNX.mjs → byline-registry-Ba54M74c.mjs} +2 -2
  231. package/dist/{bylines-LN9XISRo.mjs → bylines-C-EL6dh3.mjs} +9 -3
  232. package/dist/{bylines-DwokOFj6.mjs → bylines-CmZFCMr0.mjs} +5 -5
  233. package/dist/{cache-Bac05gBd.mjs → cache-DkVCKGio.mjs} +2 -2
  234. package/dist/{chunks-CuUt7K50.mjs → chunks-ChGNDY8r.mjs} +1 -1
  235. package/dist/cli/index.d.mts +29 -1
  236. package/dist/cli/index.mjs +1466 -1763
  237. package/dist/cli/plugin-kit.d.mts +75 -0
  238. package/dist/cli/plugin-kit.mjs +4 -0
  239. package/dist/client/external-auth-headers.d.mts +1 -1
  240. package/dist/client/index.d.mts +27 -52
  241. package/dist/client/index.mjs +34 -6
  242. package/dist/client-factory-BDD9azPJ.mjs +233 -0
  243. package/dist/{comment-Beue8-15.mjs → comment-DFALgsJD.mjs} +13 -3
  244. package/dist/comment-reaction-C37bf99u.mjs +58 -0
  245. package/dist/{comments-DcjFIfqX.mjs → comments-B3NwDBxl.mjs} +1 -1
  246. package/dist/{content-CBxYGWfL.mjs → content-DJcAYFhz.mjs} +51 -34
  247. package/dist/content-refresh-B4ZCmxgd.mjs +1576 -0
  248. package/dist/{context-Df1xVYyr.mjs → context-DnpXcp6Y.mjs} +2 -2
  249. package/dist/{context-CQVNZaR8.mjs → context-fSCYLL3c.mjs} +125 -53
  250. package/dist/{context-route-helpers-B9wj8Rmh.mjs → context-route-helpers-CI8XzcsK.mjs} +2 -1
  251. package/dist/{cron-DxCAU_Sn.mjs → cron-B1SL_lsb.mjs} +7 -3
  252. package/dist/{dashboard-B7ZUNuyF.mjs → dashboard-D0nj5oWg.mjs} +4 -4
  253. package/dist/db/index.d.mts +3 -3
  254. package/dist/db/index.mjs +1 -1
  255. package/dist/db/libsql.d.mts +1 -1
  256. package/dist/db/postgres.d.mts +1 -1
  257. package/dist/db/sqlite.d.mts +1 -1
  258. package/dist/{device-flow-BZVGftqi.mjs → device-flow-BJe6VEhP.mjs} +5 -5
  259. package/dist/{placeholder-d54Qr_7n.mjs → enrich-Ck9tfMqe.mjs} +36 -9
  260. package/dist/{error-DCSuUAZW.mjs → error-CSVaQNZy.mjs} +5 -0
  261. package/dist/{experimental-workflows-CSMffP6q.mjs → experimental-workflows-GVhUgzEs.mjs} +1 -1
  262. package/dist/field-defs-cache-DL-T2XVS.mjs +49 -0
  263. package/dist/forgeway-seo-client-G5ddhVHd.mjs +456 -0
  264. package/dist/{fts-manager-B5l_qwrA.mjs → fts-manager-ewTGohf4.mjs} +1 -1
  265. package/dist/{hitl-requests-7NlzVeUA.mjs → hitl-requests-DEFj8e6u.mjs} +2 -2
  266. package/dist/{hitl-route-helpers-k---_H3-.mjs → hitl-route-helpers-C23JHaWz.mjs} +5 -4
  267. package/dist/import-CXXL5pip.mjs +1354 -0
  268. package/dist/index-DcnOCD1e.d.mts +375 -0
  269. package/dist/index.d.mts +15 -16
  270. package/dist/index.mjs +85 -70
  271. package/dist/{jsonld-DZ67souc.d.mts → jsonld-DzKbhRZ4.d.mts} +8 -4
  272. package/dist/{load-D9OiGr2m.mjs → load-CFtJRT3c.mjs} +1 -1
  273. package/dist/{loader--GOYux8M.mjs → loader-aWkkc1_o.mjs} +117 -31
  274. package/dist/{manifest-schema-qh2P7RDX.mjs → manifest-schema-uA6pRFXi.mjs} +38 -1
  275. package/dist/mcp-reviewed-executor-Dn52L_yY.mjs +558 -0
  276. package/dist/media/image-endpoint.d.mts +32 -0
  277. package/dist/media/image-endpoint.mjs +111 -0
  278. package/dist/media/index.d.mts +27 -2
  279. package/dist/media/index.mjs +5 -3
  280. package/dist/media/local-runtime.d.mts +15 -11
  281. package/dist/media/local-runtime.mjs +9 -7
  282. package/dist/{media-CbKyYukG.mjs → media-CVMOoywq.mjs} +3 -1
  283. package/dist/{media-allowlist-BYxJIcTO.mjs → media-allowlist-zDNJmXCo.mjs} +1 -1
  284. package/dist/media-url-BdoLPvg8.mjs +20 -0
  285. package/dist/{menus-C7F0sCrC.mjs → menus-CCG2z5um.mjs} +80 -15
  286. package/dist/{menus-BZbXf-ej.mjs → menus-CmyIkRG5.mjs} +19 -5
  287. package/dist/{normalize-ByemhhfW.mjs → normalize-Cyt7JdF7.mjs} +8 -1
  288. package/dist/{oauth-authorization-DF-LCsIA.mjs → oauth-authorization-B9H7X85L.mjs} +5 -5
  289. package/dist/{oauth-clients-Du-V0fEm.mjs → oauth-clients-Dwl32-Uj.mjs} +2 -2
  290. package/dist/object-cache/memory.d.mts +14 -0
  291. package/dist/object-cache/memory.mjs +55 -0
  292. package/dist/object-cache-D51qOseS.mjs +419 -0
  293. package/dist/output-DhBe5_Pi.mjs +68 -0
  294. package/dist/page/index.d.mts +10 -3
  295. package/dist/page/index.mjs +2 -2
  296. package/dist/{parse-CneSgl7-.mjs → parse-i15EFSAE.mjs} +1 -1
  297. package/dist/{parser-DxfsARXL.mjs → parser-06LLLL4E.mjs} +151 -9
  298. package/dist/{placeholder-CXfoCtCd.d.mts → placeholder-DkWkXfDI.d.mts} +12 -4
  299. package/dist/plugin-utils.d.mts +8 -9
  300. package/dist/plugins/adapt-sandbox-entry.d.mts +8 -9
  301. package/dist/plugins/adapt-sandbox-entry.mjs +13 -2
  302. package/dist/{plugins-DzfqRREq.mjs → plugins-CPA2PtfY.mjs} +919 -66
  303. package/dist/{transport-LSdZVXHF.mjs → portable-text-BASHceim.mjs} +1 -134
  304. package/dist/portable-text-E79LyjGo.d.mts +45 -0
  305. package/dist/{preview-C-3YTX2U.mjs → preview-BjedZm8G.mjs} +4 -3
  306. package/dist/process-cache-CCDgti7j.mjs +106 -0
  307. package/dist/project-env-DKibmuP8.mjs +95 -0
  308. package/dist/{provider-loader-9it4vd6L.d.mts → provider-loader-DC2JdWeO.d.mts} +1 -1
  309. package/dist/{public-url-B4xV4rGa.d.mts → public-url-DQJCnYi9.d.mts} +1 -1
  310. package/dist/publication-authorization-RKiHXKtq.mjs +985 -0
  311. package/dist/{query-CZ3TH_Uu.mjs → query-C2U5u5_p.mjs} +11 -6
  312. package/dist/{query-Ds0EnMFX.mjs → query-DEv5x6yB.mjs} +281 -76
  313. package/dist/{rate-limit-i0G3IAOX.mjs → rate-limit-DglsAJSw.mjs} +2 -2
  314. package/dist/{redirect-CEACczxo.mjs → redirect-ubhEqQCQ.mjs} +2 -2
  315. package/dist/{redirects-ocRJmQHP.mjs → redirects-Bhpm8bRe.mjs} +35 -19
  316. package/dist/{redirects-DYj1yPxs.mjs → redirects-xs2yahLq.mjs} +47 -24
  317. package/dist/{registry-D7HACema.mjs → registry-BYYyb2tF.mjs} +148 -81
  318. package/dist/request-context.d.mts +2 -0
  319. package/dist/{request-meta-DAb6xaP9.mjs → request-meta-CFe1tW2M.mjs} +1 -1
  320. package/dist/review-payload-BysRWpxS.mjs +111 -0
  321. package/dist/{review-requests-BW2NxY-l.mjs → review-requests-CTWGnWUt.mjs} +1 -1
  322. package/dist/review-requests-D42VEP1l.mjs +142 -0
  323. package/dist/review-route-helpers-C31vO6SI.mjs +41 -0
  324. package/dist/runner-BgmttQmD.mjs +7444 -0
  325. package/dist/{runner-09sTqtv0.d.mts → runner-D6hzs8L9.d.mts} +1 -1
  326. package/dist/runtime.d.mts +9 -10
  327. package/dist/runtime.mjs +3 -3
  328. package/dist/schema-DUtnZxjZ.mjs +8 -0
  329. package/dist/{search-Gdrqj582.mjs → search-DUKgZUlm.mjs} +5 -3
  330. package/dist/{secrets-CeGpBFV2.mjs → secrets-ZV5rd8yV.mjs} +14 -11
  331. package/dist/{sections-BOjU13Y-.mjs → sections-DvFgsnda.mjs} +2 -2
  332. package/dist/seed/index.d.mts +2 -2
  333. package/dist/seed/index.mjs +21 -18
  334. package/dist/seo/index.d.mts +1 -1
  335. package/dist/seo/index.mjs +3 -12
  336. package/dist/{seo-5lJOtB5J.mjs → seo-BLszyJxd.mjs} +22 -4
  337. package/dist/{seo-CLj8lS7Q.mjs → seo-BU8MLCS9.mjs} +5 -2
  338. package/dist/{seo-contributions-CqVvReEj.mjs → seo-contributions-GnCuL1ML.mjs} +11 -3
  339. package/dist/{service-jGlhrICs.mjs → service-FL3fB0Rx.mjs} +2 -2
  340. package/dist/session-user-Ct6GBLvn.mjs +22 -0
  341. package/dist/{settings-CUXOxzsT.mjs → settings-BECcAIRH.mjs} +28 -55
  342. package/dist/{settings-DlBsT2VS.mjs → settings-EzADu-dd.mjs} +2 -2
  343. package/dist/{setup-complete-BRrdPYjZ.mjs → setup-complete-Da0axZEE.mjs} +1 -1
  344. package/dist/{site-activity-DONFrMBJ.mjs → site-activity-h8smOlYI.mjs} +1 -1
  345. package/dist/{site-context-C8Az_P_I.mjs → site-context-B8T1kpUP.mjs} +337 -530
  346. package/dist/{site-url-DIMD68go.mjs → site-url-DJEid0ml.mjs} +1 -1
  347. package/dist/stable-hash-DshcXvRE.mjs +19 -0
  348. package/dist/status-NqWOSFAD.mjs +21 -0
  349. package/dist/storage/local.d.mts +1 -1
  350. package/dist/storage/local.mjs +1 -1
  351. package/dist/storage/s3.d.mts +1 -1
  352. package/dist/storage/s3.mjs +1 -1
  353. package/dist/{taxonomies-D9Sw4wWM.mjs → taxonomies-BiM9NtgW.mjs} +36 -29
  354. package/dist/taxonomies-DSnY5nOa.mjs +443 -0
  355. package/dist/{taxonomy-B169Nm6B.mjs → taxonomy-0k_vRVsD.mjs} +44 -9
  356. package/dist/transport-C46KHLMP.mjs +134 -0
  357. package/dist/types-A8fPOJnl.mjs +69 -0
  358. package/dist/types-BLQCz46p.d.mts +118 -0
  359. package/dist/{types-CwawBUTa.d.mts → types-B_FlxIIl.d.mts} +4 -1
  360. package/dist/{types-CSgR9nQB.mjs → types-ChxYDRvx.mjs} +1 -1
  361. package/dist/{types-GaCtZ1k9.d.mts → types-Cw0PeCT_.d.mts} +18 -0
  362. package/dist/types-DuQn1G7-.d.mts +2973 -0
  363. package/dist/{runtime-C4-2lSFg.d.mts → types-DwoUifGE.d.mts} +5668 -3000
  364. package/dist/{types-DRIKLmJI.d.mts → types-opjX_kz6.d.mts} +17 -2
  365. package/dist/ui/server-runtime.d.mts +13 -15
  366. package/dist/ui/server-runtime.mjs +24 -20
  367. package/dist/url-guoXQQcV.mjs +33 -0
  368. package/dist/{user-DTLoZs16.mjs → user-BH-n01y3.mjs} +2 -2
  369. package/dist/{utils-B8hBJ9Oz.mjs → utils-D1SrKGtP.mjs} +129 -7
  370. package/dist/{validate-DzjCdy6v.d.mts → validate-CbVFoJgc.d.mts} +65 -8
  371. package/dist/{validate-DWmPO6oj.mjs → validate-D1HL-oLR.mjs} +11 -6
  372. package/dist/version-C3iVRuaF.mjs +6 -0
  373. package/dist/{widgets-BpJ8EpPd.mjs → widgets-BZrLrNgS.mjs} +46 -43
  374. package/dist/{wordpress-slugs-DHNR9LpG.mjs → wordpress-slugs-C3PWcowq.mjs} +1 -1
  375. package/dist/{zod-generator-K91-g665.mjs → zod-generator-C7AsLpSB.mjs} +42 -14
  376. package/package.json +38 -8
  377. package/src/components/Block.astro +50 -0
  378. package/src/components/Comments.astro +143 -1
  379. package/src/components/DinewayHead.astro +19 -2
  380. package/src/components/DinewayImage.astro +66 -33
  381. package/src/components/DinewayMedia.astro +1 -2
  382. package/src/components/Image.astro +85 -40
  383. package/src/components/InlinePortableTextEditor.tsx +12 -2
  384. package/src/components/JsonLdScript.astro +9 -0
  385. package/src/components/LiveSearch.astro +40 -4
  386. package/src/components/index.ts +4 -0
  387. package/src/components/live-search-routing.ts +30 -0
  388. package/src/components/portable-text-text-align.ts +15 -0
  389. package/src/ui.ts +2 -0
  390. package/dist/entity-aliases-4y93Yjuc.mjs +0 -51
  391. package/dist/import-4YBXbDkI.mjs +0 -684
  392. package/dist/index-CZiWCQDp.d.mts +0 -227
  393. package/dist/review-requests-Qum4713s.mjs +0 -79
  394. package/dist/runner-DzIpHGND.mjs +0 -2836
  395. package/dist/schema-Bsi-VpYv.mjs +0 -8
  396. package/dist/taxonomies-CZ-_DitR.mjs +0 -327
  397. package/dist/types-CuJOqHfM.d.mts +0 -758
  398. package/dist/types-DSPGWjxO.d.mts +0 -1247
  399. package/dist/types-nP9vGUFq.d.mts +0 -56
  400. package/dist/version-C4gfn0PJ.mjs +0 -6
  401. /package/dist/{adapters-OQCImOL7.d.mts → adapters-BiRymx02.d.mts} +0 -0
  402. /package/dist/{api-tokens-CYUoFGo7.mjs → api-tokens-Ypaly90d.mjs} +0 -0
  403. /package/dist/{challenge-store-DFxX0_15.mjs → challenge-store-D5pSY2S1.mjs} +0 -0
  404. /package/dist/{components--pg6zBvu.mjs → components-DImzlLhb.mjs} +0 -0
  405. /package/dist/{context-types-r8oFvyOq.mjs → context-types-eBuMDAg-.mjs} +0 -0
  406. /package/dist/{default-B6iaLvP7.mjs → default-D4Vygcy_.mjs} +0 -0
  407. /package/dist/{email-console-BOVqIYpB.mjs → email-console-Bp0r68iF.mjs} +0 -0
  408. /package/dist/{escape-BDgouFFY.mjs → escape-DN4Qmjsl.mjs} +0 -0
  409. /package/dist/{hash-BPmX6P8I.mjs → hash-mxxx2oPW.mjs} +0 -0
  410. /package/dist/{mime-CbKsKDCB.mjs → mime-C0vOHFm7.mjs} +0 -0
  411. /package/dist/{mode-CZqCF9dZ.mjs → mode-C8yZnIlR.mjs} +0 -0
  412. /package/dist/{oauth-state-store-qncPB_IU.mjs → oauth-state-store-CndT35iw.mjs} +0 -0
  413. /package/dist/{oauth-user-lookup-BFjWF8wK.mjs → oauth-user-lookup-CMehAaKs.mjs} +0 -0
  414. /package/dist/{options-LsPjZlqD.mjs → options-DQPTH5n0.mjs} +0 -0
  415. /package/dist/{passkey-config-DzHQK90v.mjs → passkey-config-NYukYGed.mjs} +0 -0
  416. /package/dist/{patterns-DKAX6oqN.mjs → patterns-CoqlXUvk.mjs} +0 -0
  417. /package/dist/{provider-loader-DzUxejk7.mjs → provider-loader-BtfrrzfD.mjs} +0 -0
  418. /package/dist/{public-url-B_m9IwdV.mjs → public-url-Cp6hyjVR.mjs} +0 -0
  419. /package/dist/{redirect-BjIBtDWY.mjs → redirect-DrbEmU9a.mjs} +0 -0
  420. /package/dist/{request-cache-ho99schz.mjs → request-cache-CBFjCwSt.mjs} +0 -0
  421. /package/dist/{resolve-DTuDyYKs.mjs → resolve-DE71WcL9.mjs} +0 -0
  422. /package/dist/{setup-nonce-pyGfc9hV.mjs → setup-nonce-BtkKnvxQ.mjs} +0 -0
  423. /package/dist/{sidecar-client-mJxwayYN.mjs → sidecar-client-Ctzfyyp-.mjs} +0 -0
  424. /package/dist/{slugify-BqebcqBg.mjs → slugify-KSsVD_D3.mjs} +0 -0
  425. /package/dist/{ssrf-DiKqWd3N.mjs → ssrf-FJqwQzj4.mjs} +0 -0
  426. /package/dist/{tokens-SLmgowfd.mjs → tokens-BDQU29Vd.mjs} +0 -0
  427. /package/dist/{transaction-CFLhEOuH.mjs → transaction-0LEAAy8R.mjs} +0 -0
  428. /package/dist/{transport-BnsNxh2l.d.mts → transport-D9pKAzFP.d.mts} +0 -0
  429. /package/dist/{trusted-proxy-BgIIcbsN.mjs → trusted-proxy-Dm7Mxvt6.mjs} +0 -0
  430. /package/dist/{types-CJ8DkL0q.mjs → types-B7TDDFMG.mjs} +0 -0
  431. /package/dist/{types-Dryvv7LH.d.mts → types-g0zMtraY.d.mts} +0 -0
  432. /package/dist/{types-Cg276-3z.mjs → types-mVVgRPaU.mjs} +0 -0
  433. /package/dist/{types-DZpT-PnG.d.mts → types-wFu80FrO.d.mts} +0 -0
  434. /package/dist/{url-w7Mp5qAD.mjs → url-EMAguGVk.mjs} +0 -0
@@ -1,150 +1,74 @@
1
1
  #!/usr/bin/env node
2
- import { a as __exportAll, i as runMigrations, n as getMigrationStatus, t as getMigrationFingerprint } from "../runner-DzIpHGND.mjs";
2
+ import { i as runMigrations, n as getMigrationStatus, t as getMigrationFingerprint } from "../runner-BgmttQmD.mjs";
3
3
  import { t as createDatabase } from "../connection-CiJWiWya.mjs";
4
4
  import { t as validateIdentifier } from "../validate-VPnKoIzW.mjs";
5
5
  import { l as listTablesLike } from "../dialect-helpers-fk43GbDb.mjs";
6
6
  import { n as getI18nConfig, r as isI18nEnabled } from "../config-CVssduLe.mjs";
7
7
  import { i as encodeBase64url } from "../base64-F8-DUraK.mjs";
8
- import "../types-CSgR9nQB.mjs";
9
- import { a as isMissingTableError } from "../loader--GOYux8M.mjs";
10
- import { n as slugify } from "../slugify-BqebcqBg.mjs";
11
- import { t as ContentRepository } from "../content-CBxYGWfL.mjs";
12
- import { t as MediaRepository } from "../media-CbKyYukG.mjs";
13
- import { t as TaxonomyRepository } from "../taxonomy-B169Nm6B.mjs";
14
- import { t as OptionsRepository } from "../options-LsPjZlqD.mjs";
15
- import "../redirect-CEACczxo.mjs";
16
- import "../request-cache-ho99schz.mjs";
17
- import "../byline-registry-4v6tXMNX.mjs";
18
- import { t as BylineRepository } from "../byline-BVC1lV6G.mjs";
19
- import "../fts-manager-B5l_qwrA.mjs";
20
- import { n as SchemaRegistry } from "../registry-D7HACema.mjs";
21
- import "../settings-CUXOxzsT.mjs";
22
- import { n as VERSION } from "../version-C4gfn0PJ.mjs";
23
- import { i as pluginManifestSchema } from "../manifest-schema-qh2P7RDX.mjs";
24
- import "../ssrf-DiKqWd3N.mjs";
25
- import { t as validateSeed } from "../validate-DWmPO6oj.mjs";
26
- import { t as applySeed } from "../apply-D_RlTYtC.mjs";
27
- import { n as fingerprintKey, r as generateEncryptionKey, t as DinewaySecretsError } from "../secrets-CeGpBFV2.mjs";
28
- import { createHeaderAwareFetch, customHeadersInterceptor, isRedirectResponse, resolveCustomHeaders } from "../client/external-auth-headers.mjs";
29
- import { o as convertDataForRead } from "../transport-LSdZVXHF.mjs";
30
- import { DinewayClient } from "../client/index.mjs";
8
+ import "../types-ChxYDRvx.mjs";
9
+ import { o as isMissingTableError } from "../loader-aWkkc1_o.mjs";
10
+ import "../object-cache-D51qOseS.mjs";
11
+ import { n as slugify } from "../slugify-KSsVD_D3.mjs";
12
+ import { t as ContentRepository } from "../content-DJcAYFhz.mjs";
13
+ import { t as MediaRepository } from "../media-CVMOoywq.mjs";
14
+ import { t as TaxonomyRepository } from "../taxonomy-0k_vRVsD.mjs";
15
+ import { t as OptionsRepository } from "../options-DQPTH5n0.mjs";
16
+ import "../redirect-ubhEqQCQ.mjs";
17
+ import "../request-cache-CBFjCwSt.mjs";
18
+ import "../byline-registry-Ba54M74c.mjs";
19
+ import "../field-defs-cache-DL-T2XVS.mjs";
20
+ import { t as BylineRepository } from "../byline-7xR185Ly.mjs";
21
+ import "../content-refresh-B4ZCmxgd.mjs";
22
+ import "../fts-manager-ewTGohf4.mjs";
23
+ import { n as SchemaRegistry } from "../registry-BYYyb2tF.mjs";
24
+ import "../settings-BECcAIRH.mjs";
25
+ import { n as VERSION } from "../version-C3iVRuaF.mjs";
26
+ import { a as reconcileManifestAccess, i as pluginManifestSchema } from "../manifest-schema-uA6pRFXi.mjs";
27
+ import { n as resolveAndValidateExternalUrl, t as SsrfError } from "../ssrf-FJqwQzj4.mjs";
28
+ import { n as fingerprintKey, r as generateEncryptionKey, t as DinewaySecretsError } from "../secrets-ZV5rd8yV.mjs";
29
+ import { t as validateSeed } from "../validate-D1HL-oLR.mjs";
30
+ import { t as applySeed } from "../apply-DjTqq6Ae.mjs";
31
+ import { a as readSavedDeployTarget, c as listForgewayCredentials, i as readForgewayDeployMetadata, l as readForgewayCredentials, n as SeoProviderEvidenceService, o as writeDeployTarget, r as readDeployPackageJson, s as writeForgewayDeployMetadata, t as ForgewaySeoGatewayClient, u as writeForgewayCredentials } from "../forgeway-seo-client-G5ddhVHd.mjs";
32
+ import { n as output, t as configureOutputMode } from "../output-DhBe5_Pi.mjs";
33
+ import { createHeaderAwareFetch, isRedirectResponse, resolveCustomHeaders } from "../client/external-auth-headers.mjs";
34
+ import { t as convertDataForRead } from "../portable-text-BASHceim.mjs";
35
+ import { a as getMarketplaceCredential, c as resolveCredentialKey, i as getCredentials, l as saveCredentials, n as connectionArgs, o as removeCredentials, r as createClientFromArgs, s as removeMarketplaceCredential, u as saveMarketplaceCredential } from "../client-factory-BDD9azPJ.mjs";
36
+ import { a as resolveDinewayBaseUrl, n as ensureProjectGitignoreDinewayLocalState, o as resolveDinewayToken, r as ensureProjectGitignoreEntry, s as upsertProjectEnv, t as DEFAULT_DINEWAY_URL } from "../project-env-DKibmuP8.mjs";
31
37
  import { LocalStorage } from "../storage/local.mjs";
32
- import { createHash, randomUUID } from "node:crypto";
38
+ import { r as fileExists$11, t as bundleCommand } from "../bundle-CEcdUa1s.mjs";
39
+ import { createRequire } from "node:module";
40
+ import { createHash } from "node:crypto";
33
41
  import { sql } from "kysely";
34
- import { imageSize } from "image-size";
35
42
  import { createGzipDecoder, unpackTar } from "modern-tar";
36
- import { createReadStream, createWriteStream, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
37
- import { basename, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
43
+ import { createReadStream, existsSync, readFileSync, writeFileSync } from "node:fs";
44
+ import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
45
+ import sax from "sax";
38
46
  import { pathToFileURL } from "node:url";
39
47
  import { defineCommand, runCommand, runMain } from "citty";
40
48
  import { createInterface } from "node:readline/promises";
41
49
  import consola, { consola as consola$1 } from "consola";
42
50
  import pc from "picocolors";
43
51
  import { ForgewayClient } from "@forgeway/sdk";
44
- import { access, chmod, copyFile, mkdir, readFile, readdir, rm, stat, symlink, writeFile } from "node:fs/promises";
45
- import { homedir } from "node:os";
52
+ import { access, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
46
53
  import { spawn } from "node:child_process";
47
- import { pipeline } from "node:stream/promises";
48
- import { packTar } from "modern-tar/fs";
54
+ import { parse } from "parse5";
49
55
 
50
- //#region src/cli/commands/deploy/utils/forgeway-secrets.ts
51
- const DINEWAY_USER_CONFIG_DIR_NAME = "dineway";
52
- const TRAILING_SLASH_PATTERN$3 = /\/$/;
53
- function getDinewayUserConfigDir() {
54
- return join(process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), DINEWAY_USER_CONFIG_DIR_NAME);
55
- }
56
- function getForgewayAccountSecretPath() {
57
- return join(getDinewayUserConfigDir(), "forgeway.json");
58
- }
59
- function normalizePlatformApiUrl$1(platformApiUrl) {
60
- return platformApiUrl.replace(TRAILING_SLASH_PATTERN$3, "");
61
- }
62
- function normalizeEmail$2(email) {
63
- return email.trim().toLowerCase();
64
- }
65
- function accountKey(platformApiUrl, email) {
66
- return `${normalizePlatformApiUrl$1(platformApiUrl)}#${normalizeEmail$2(email)}`;
67
- }
68
- function normalizeLookup(lookup) {
69
- if (typeof lookup === "string") return { cwd: lookup };
70
- return lookup ?? {};
71
- }
72
- async function readAccountStore() {
73
- try {
74
- const raw = await readFile(getForgewayAccountSecretPath(), "utf-8");
75
- return JSON.parse(raw);
76
- } catch {
77
- return { version: 1 };
78
- }
79
- }
80
- async function writeAccountStore(store) {
81
- await mkdir(getDinewayUserConfigDir(), {
82
- recursive: true,
83
- mode: 448
84
- });
85
- const file = getForgewayAccountSecretPath();
86
- await writeFile(file, JSON.stringify({
87
- ...store,
88
- version: 1
89
- }, null, " ") + "\n", {
90
- encoding: "utf-8",
91
- mode: 384
92
- });
93
- await chmod(file, 384);
94
- }
95
- function accountMatches(credentials, lookup) {
96
- if (lookup.platformApiUrl && normalizePlatformApiUrl$1(credentials.platformApiUrl) !== normalizePlatformApiUrl$1(lookup.platformApiUrl)) return false;
97
- if (lookup.email && normalizeEmail$2(credentials.user?.email ?? "") !== normalizeEmail$2(lookup.email)) return false;
98
- return true;
99
- }
100
- async function readForgewayCredentials(lookup) {
101
- const normalizedLookup = normalizeLookup(lookup);
102
- const store = await readAccountStore();
103
- const accounts = Object.values(store.accounts ?? {}).filter((credentials) => accountMatches(credentials, normalizedLookup));
104
- if (normalizedLookup.platformApiUrl && normalizedLookup.email) return store.accounts?.[accountKey(normalizedLookup.platformApiUrl, normalizedLookup.email)] ?? null;
105
- if (accounts.length === 1) return accounts[0] ?? null;
106
- return null;
107
- }
108
- async function writeForgewayCredentials(credentials, lookup) {
109
- const normalizedLookup = normalizeLookup(lookup);
110
- const platformApiUrl = normalizePlatformApiUrl$1(normalizedLookup.platformApiUrl ?? credentials.platformApiUrl);
111
- const email = normalizeEmail$2(normalizedLookup.email ?? credentials.user?.email ?? "");
112
- if (!email) throw new Error("Forgeway credentials require a user email before they can be stored.");
113
- const key = accountKey(platformApiUrl, email);
114
- const store = await readAccountStore();
115
- store.accounts = {
116
- ...store.accounts,
117
- [key]: {
118
- ...credentials,
119
- platformApiUrl
120
- }
121
- };
122
- store.lastAccountKey = key;
123
- await writeAccountStore(store);
124
- }
125
- async function listForgewayCredentials(platformApiUrl) {
126
- const lookup = platformApiUrl ? { platformApiUrl } : {};
127
- const store = await readAccountStore();
128
- return Object.values(store.accounts ?? {}).filter((credentials) => accountMatches(credentials, lookup));
129
- }
130
-
131
- //#endregion
132
56
  //#region src/cli/forgeway-account.ts
133
- const TRAILING_SLASH_PATTERN$2 = /\/$/;
134
- const SHADOW_EMAIL_PATTERN$2 = /^shadow_[a-f0-9]{16}@dineway\.ai$/i;
57
+ const TRAILING_SLASH_PATTERN$1 = /\/$/;
58
+ const SHADOW_EMAIL_PATTERN$1 = /^shadow_[a-f0-9]{16}@dineway\.ai$/i;
135
59
  const DEFAULT_FORGEWAY_API_BASE_URL = "https://api.dineway.ai";
136
60
  const DEFAULT_DINEWAY_WEB_URL = "https://dineway.ai";
137
61
  function normalizeForgewayApiUrl(value) {
138
- return (value || DEFAULT_FORGEWAY_API_BASE_URL).replace(TRAILING_SLASH_PATTERN$2, "");
62
+ return (value || DEFAULT_FORGEWAY_API_BASE_URL).replace(TRAILING_SLASH_PATTERN$1, "");
139
63
  }
140
64
  function normalizeDinewayWebUrl(value) {
141
- return (value || DEFAULT_DINEWAY_WEB_URL).replace(TRAILING_SLASH_PATTERN$2, "");
65
+ return (value || DEFAULT_DINEWAY_WEB_URL).replace(TRAILING_SLASH_PATTERN$1, "");
142
66
  }
143
67
  function normalizeForgewayEmail(value) {
144
68
  return value?.trim().toLowerCase() ?? "";
145
69
  }
146
70
  function isShadowForgewayEmail(email) {
147
- return Boolean(email && SHADOW_EMAIL_PATTERN$2.test(email));
71
+ return Boolean(email && SHADOW_EMAIL_PATTERN$1.test(email));
148
72
  }
149
73
  function createForgewayAccountClient(platformApiUrl, accessToken) {
150
74
  return new ForgewayClient({
@@ -154,7 +78,7 @@ function createForgewayAccountClient(platformApiUrl, accessToken) {
154
78
  auth: { detectOAuthCallback: false }
155
79
  });
156
80
  }
157
- function normalizeForgewayUser$2(user) {
81
+ function normalizeForgewayUser$1(user) {
158
82
  if (!user?.id || !user.email) throw new Error("Forgeway session response did not include a user email.");
159
83
  return {
160
84
  id: user.id,
@@ -192,7 +116,7 @@ async function refreshForgewayAccountCredentials(credentials) {
192
116
  if (!credentials.refreshToken) throw new Error("Stored Forgeway account is missing a refresh token. Run `dineway account login`.");
193
117
  const { data, error } = await createForgewayAccountClient(credentials.platformApiUrl).auth.refreshSession({ refreshToken: credentials.refreshToken });
194
118
  if (error || !data?.accessToken) throw new Error(error?.message || "Failed to refresh Forgeway account. Run `dineway account login`.");
195
- const refreshedUser = normalizeForgewayUser$2(data.user);
119
+ const refreshedUser = normalizeForgewayUser$1(data.user);
196
120
  if (normalizeForgewayEmail(refreshedUser.email) !== normalizeForgewayEmail(user.email)) throw new Error("Forgeway refresh returned credentials for a different account.");
197
121
  const updated = {
198
122
  ...credentials,
@@ -224,7 +148,7 @@ async function completeForgewayAccountLogin(input) {
224
148
  const platformApiUrl = normalizeForgewayApiUrl(input.platformApiUrl);
225
149
  const { data, error } = await createForgewayAccountClient(platformApiUrl).auth.signInWithPasswordlessToken({ token: input.token });
226
150
  if (error || !data?.accessToken || !data.refreshToken) throw new Error(error?.message || "Forgeway sign-in token is invalid or expired.");
227
- const user = normalizeForgewayUser$2(data.user);
151
+ const user = normalizeForgewayUser$1(data.user);
228
152
  if (input.email && normalizeForgewayEmail(user.email) !== normalizeForgewayEmail(input.email)) throw new Error("Forgeway sign-in returned credentials for a different email.");
229
153
  const credentials = {
230
154
  platformApiUrl,
@@ -251,71 +175,6 @@ async function getForgewayDinewayAccount(credentials) {
251
175
  };
252
176
  }
253
177
 
254
- //#endregion
255
- //#region src/cli/output.ts
256
- /**
257
- * Redirect consola output to stderr so it doesn't pollute JSON on stdout.
258
- *
259
- * Call this early in any command that uses `output()` with `--json`.
260
- * Safe to call multiple times — only applies the redirect once.
261
- */
262
- function configureOutputMode(args) {
263
- if (args.json || !process.stdout.isTTY) {
264
- consola$1.options.stdout = process.stderr;
265
- consola$1.options.stderr = process.stderr;
266
- }
267
- }
268
- /**
269
- * Output data as JSON or pretty-printed.
270
- *
271
- * If stdout is not a TTY or --json is set, outputs JSON.
272
- * Otherwise, outputs a formatted representation.
273
- */
274
- function output(data, args) {
275
- if (args.json || !process.stdout.isTTY) process.stdout.write(JSON.stringify(data, null, 2) + "\n");
276
- else prettyPrint(data);
277
- }
278
- function prettyPrint(data, indent = 0) {
279
- if (data === null || data === void 0) {
280
- consola$1.log("(empty)");
281
- return;
282
- }
283
- if (Array.isArray(data)) {
284
- if (data.length === 0) {
285
- consola$1.log("(no items)");
286
- return;
287
- }
288
- for (const item of data) {
289
- prettyPrint(item, indent);
290
- if (indent === 0) consola$1.log("---");
291
- }
292
- return;
293
- }
294
- if (typeof data === "object") {
295
- const obj = Object(data);
296
- if ("items" in obj && Array.isArray(obj.items)) {
297
- prettyPrint(obj.items, indent);
298
- if (typeof obj.nextCursor === "string") consola$1.log(`\nNext cursor: ${obj.nextCursor}`);
299
- return;
300
- }
301
- const prefix = " ".repeat(indent);
302
- for (const [key, value] of Object.entries(obj)) {
303
- if (value === null || value === void 0) continue;
304
- if (typeof value === "object" && !Array.isArray(value)) {
305
- consola$1.log(`${prefix}${key}:`);
306
- prettyPrint(value, indent + 1);
307
- } else if (Array.isArray(value)) consola$1.log(`${prefix}${key}: [${value.length} items]`);
308
- else {
309
- const str = typeof value === "string" ? value : JSON.stringify(value);
310
- const display = str.length > 80 ? str.slice(0, 77) + "..." : str;
311
- consola$1.log(`${prefix}${key}: ${display}`);
312
- }
313
- }
314
- return;
315
- }
316
- consola$1.log(typeof data === "string" ? data : JSON.stringify(data));
317
- }
318
-
319
178
  //#endregion
320
179
  //#region src/cli/commands/account.ts
321
180
  async function promptLine(question) {
@@ -504,320 +363,6 @@ const accountCommand = defineCommand({
504
363
  }
505
364
  });
506
365
 
507
- //#endregion
508
- //#region src/cli/credentials.ts
509
- /**
510
- * Credential storage for CLI auth tokens.
511
- *
512
- * Stores OAuth tokens in ~/.config/dineway/auth.json.
513
- * Remote URLs are keyed by origin, local dev by project path.
514
- */
515
- function getConfigDir() {
516
- const xdg = process.env["XDG_CONFIG_HOME"];
517
- if (xdg) return join(xdg, "dineway");
518
- return join(homedir(), ".config", "dineway");
519
- }
520
- function getCredentialPath() {
521
- return join(getConfigDir(), "auth.json");
522
- }
523
- /**
524
- * Resolve the credential key for a given URL.
525
- *
526
- * Remote URLs are keyed by origin (e.g. "https://my-site.pages.dev").
527
- * Local dev instances are keyed by project path (e.g. "path:/Users/matt/sites/blog").
528
- */
529
- function resolveCredentialKey(baseUrl) {
530
- try {
531
- const url = new URL(baseUrl);
532
- if (url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]") {
533
- const projectPath = findProjectRoot(process.cwd());
534
- if (projectPath) return `path:${projectPath}`;
535
- return url.origin;
536
- }
537
- return url.origin;
538
- } catch {
539
- return baseUrl;
540
- }
541
- }
542
- /**
543
- * Walk up from cwd to find the project root (directory containing astro.config.*).
544
- */
545
- function findProjectRoot(from) {
546
- let dir = resolve(from);
547
- const root = resolve("/");
548
- while (dir !== root) {
549
- for (const name of [
550
- "astro.config.ts",
551
- "astro.config.mts",
552
- "astro.config.js",
553
- "astro.config.mjs"
554
- ]) if (existsSync(join(dir, name))) return dir;
555
- const parent = resolve(dir, "..");
556
- if (parent === dir) break;
557
- dir = parent;
558
- }
559
- return null;
560
- }
561
- function readStore() {
562
- const path = getCredentialPath();
563
- try {
564
- if (existsSync(path)) {
565
- const content = readFileSync(path, "utf-8");
566
- return JSON.parse(content);
567
- }
568
- } catch {}
569
- return {};
570
- }
571
- function writeStore(store) {
572
- mkdirSync(getConfigDir(), {
573
- recursive: true,
574
- mode: 448
575
- });
576
- writeFileSync(getCredentialPath(), JSON.stringify(store, null, " "), {
577
- encoding: "utf-8",
578
- mode: 384
579
- });
580
- }
581
- /**
582
- * Get stored credentials for a URL.
583
- */
584
- function getCredentials(baseUrl) {
585
- const key = resolveCredentialKey(baseUrl);
586
- const cred = readStore()[key];
587
- if (!cred || !("accessToken" in cred)) return null;
588
- return cred;
589
- }
590
- /**
591
- * Save credentials for a URL.
592
- */
593
- function saveCredentials(baseUrl, cred) {
594
- const key = resolveCredentialKey(baseUrl);
595
- const store = readStore();
596
- store[key] = cred;
597
- writeStore(store);
598
- }
599
- /**
600
- * Remove credentials for a URL.
601
- */
602
- function removeCredentials(baseUrl) {
603
- const key = resolveCredentialKey(baseUrl);
604
- const store = readStore();
605
- if (key in store) {
606
- delete store[key];
607
- writeStore(store);
608
- return true;
609
- }
610
- return false;
611
- }
612
- function marketplaceKey(registryUrl) {
613
- try {
614
- return `marketplace:${new URL(registryUrl).origin}`;
615
- } catch {
616
- return `marketplace:${registryUrl}`;
617
- }
618
- }
619
- /**
620
- * Get stored marketplace credential for a registry URL.
621
- */
622
- function getMarketplaceCredential(registryUrl) {
623
- const key = marketplaceKey(registryUrl);
624
- const cred = readStore()[key];
625
- if (!cred || !("token" in cred)) return null;
626
- if (new Date(cred.expiresAt) < /* @__PURE__ */ new Date()) return null;
627
- return cred;
628
- }
629
- /**
630
- * Save marketplace credential for a registry URL.
631
- */
632
- function saveMarketplaceCredential(registryUrl, cred) {
633
- const key = marketplaceKey(registryUrl);
634
- const store = readStore();
635
- store[key] = cred;
636
- writeStore(store);
637
- }
638
- /**
639
- * Remove marketplace credential for a registry URL.
640
- */
641
- function removeMarketplaceCredential(registryUrl) {
642
- const key = marketplaceKey(registryUrl);
643
- const store = readStore();
644
- if (key in store) {
645
- delete store[key];
646
- writeStore(store);
647
- return true;
648
- }
649
- return false;
650
- }
651
-
652
- //#endregion
653
- //#region src/cli/project-env.ts
654
- const DEFAULT_DINEWAY_URL = "http://localhost:4321";
655
- const ENV_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
656
- const LINE_SPLIT_PATTERN = /\r?\n/;
657
- const DINEWAY_LOCAL_STATE_GITIGNORE_COMMENT = "# Dineway local state";
658
- const DINEWAY_LOCAL_STATE_GITIGNORE_ENTRIES = [
659
- ".dineway/deploy.json",
660
- ".dineway/forgeway.json",
661
- ".dineway/types.ts",
662
- ".dineway/schema.json",
663
- ".dineway/uploads/"
664
- ];
665
- const NEWLINE_PATTERN = /\r?\n/g;
666
- function parseDotenvValue(value) {
667
- const trimmed = value.trim();
668
- if (trimmed.startsWith("\"") && trimmed.endsWith("\"") || trimmed.startsWith("'") && trimmed.endsWith("'")) return trimmed.slice(1, -1);
669
- return trimmed;
670
- }
671
- function parseDotenv(text) {
672
- const env = {};
673
- for (const line of text.split(LINE_SPLIT_PATTERN)) {
674
- const trimmed = line.trim();
675
- if (!trimmed || trimmed.startsWith("#")) continue;
676
- const normalized = trimmed.startsWith("export ") ? trimmed.slice(7).trim() : trimmed;
677
- const equalsIndex = normalized.indexOf("=");
678
- if (equalsIndex <= 0) continue;
679
- const key = normalized.slice(0, equalsIndex).trim();
680
- if (!ENV_KEY_PATTERN.test(key)) continue;
681
- env[key] = parseDotenvValue(normalized.slice(equalsIndex + 1));
682
- }
683
- return env;
684
- }
685
- function readProjectEnv(cwd = process.cwd()) {
686
- const envPath = join(cwd, ".env");
687
- if (!existsSync(envPath)) return {};
688
- return parseDotenv(readFileSync(envPath, "utf-8"));
689
- }
690
- function getProjectEnvValue(name, cwd = process.cwd()) {
691
- return process.env[name] ?? readProjectEnv(cwd)[name];
692
- }
693
- function resolveDinewayBaseUrl(url, cwd = process.cwd()) {
694
- if (url && url !== DEFAULT_DINEWAY_URL) return url;
695
- const projectEnv = readProjectEnv(cwd);
696
- return process.env["DINEWAY_URL"] ?? process.env["DINEWAY_SITE_URL"] ?? projectEnv["DINEWAY_SITE_URL"] ?? projectEnv["DINEWAY_URL"] ?? url ?? DEFAULT_DINEWAY_URL;
697
- }
698
- function resolveDinewayToken(token, cwd = process.cwd()) {
699
- return token ?? getProjectEnvValue("DINEWAY_TOKEN", cwd);
700
- }
701
- function formatEnvValue(value) {
702
- return value.replace(NEWLINE_PATTERN, "");
703
- }
704
- async function upsertProjectEnv(cwd, values) {
705
- const envPath = join(cwd, ".env");
706
- const existing = await readFile(envPath, "utf-8").catch(() => "");
707
- const lines = existing ? existing.split(LINE_SPLIT_PATTERN) : [];
708
- const seen = /* @__PURE__ */ new Set();
709
- const nextLines = lines.map((line) => {
710
- const trimmed = line.trim();
711
- const normalized = trimmed.startsWith("export ") ? trimmed.slice(7).trim() : trimmed;
712
- const equalsIndex = normalized.indexOf("=");
713
- if (equalsIndex <= 0) return line;
714
- const key = normalized.slice(0, equalsIndex).trim();
715
- const value = values[key];
716
- if (!(key in values) || value === void 0) return line;
717
- seen.add(key);
718
- return `${key}=${formatEnvValue(value)}`;
719
- });
720
- for (const [key, value] of Object.entries(values)) {
721
- if (value === void 0 || seen.has(key)) continue;
722
- nextLines.push(`${key}=${formatEnvValue(value)}`);
723
- }
724
- await writeFile(envPath, `${nextLines.filter((line, index) => line || index < nextLines.length - 1).join("\n")}\n`, "utf-8");
725
- }
726
- async function ensureProjectGitignoreEntry(cwd, entry) {
727
- const gitignorePath = join(cwd, ".gitignore");
728
- const existing = await readFile(gitignorePath, "utf-8").catch(() => "");
729
- if (existing.split(LINE_SPLIT_PATTERN).map((line) => line.trim()).includes(entry)) return;
730
- await writeFile(gitignorePath, `${existing}${existing && !existing.endsWith("\n") ? "\n" : ""}${entry}\n`, "utf-8");
731
- }
732
- async function ensureProjectGitignoreDinewayLocalState(cwd) {
733
- const gitignorePath = join(cwd, ".gitignore");
734
- const existing = await readFile(gitignorePath, "utf-8").catch(() => "");
735
- const lines = new Set(existing.split(LINE_SPLIT_PATTERN).map((line) => line.trim()));
736
- const missingEntries = DINEWAY_LOCAL_STATE_GITIGNORE_ENTRIES.filter((entry) => !lines.has(entry));
737
- if (missingEntries.length === 0) return;
738
- const block = [DINEWAY_LOCAL_STATE_GITIGNORE_COMMENT, ...missingEntries].join("\n");
739
- await writeFile(gitignorePath, `${existing}${existing.trim() ? existing.endsWith("\n") ? "\n" : "\n\n" : ""}${block}\n`, "utf-8");
740
- }
741
-
742
- //#endregion
743
- //#region src/cli/client-factory.ts
744
- var client_factory_exports = /* @__PURE__ */ __exportAll({
745
- connectionArgs: () => connectionArgs,
746
- createClientFromArgs: () => createClientFromArgs
747
- });
748
- /**
749
- * Shared connection args for all CLI commands that talk to a Dineway instance.
750
- * Spread into each command's `args` definition.
751
- */
752
- const connectionArgs = {
753
- url: {
754
- type: "string",
755
- alias: "u",
756
- description: "Dineway instance URL",
757
- default: DEFAULT_DINEWAY_URL
758
- },
759
- token: {
760
- type: "string",
761
- alias: "t",
762
- description: "Auth token"
763
- },
764
- header: {
765
- type: "string",
766
- alias: "H",
767
- description: "Custom header \"Name: Value\" (repeatable, or use DINEWAY_HEADERS env)"
768
- },
769
- json: {
770
- type: "boolean",
771
- description: "Output as JSON"
772
- }
773
- };
774
- /**
775
- * Create a DinewayClient from CLI args, env vars, and stored credentials.
776
- *
777
- * Auth resolution order:
778
- * 1. --token flag
779
- * 2. DINEWAY_TOKEN env var
780
- * 3. Stored credentials (~/.config/dineway/auth.json)
781
- * 4. Dev bypass (if URL is localhost)
782
- *
783
- * Custom headers are merged from (in priority order):
784
- * 1. Stored credentials (persisted during `dineway login --header`)
785
- * 2. DINEWAY_HEADERS env var
786
- * 3. --header CLI flags
787
- */
788
- function createClientFromArgs(args) {
789
- const baseUrl = resolveDinewayBaseUrl(args.url);
790
- let token = resolveDinewayToken(args.token);
791
- const isLocal = baseUrl.includes("localhost") || baseUrl.includes("127.0.0.1");
792
- const cred = !token ? getCredentials(baseUrl) : null;
793
- const customHeaders = {
794
- ...cred?.customHeaders,
795
- ...resolveCustomHeaders()
796
- };
797
- const extraInterceptors = [];
798
- if (Object.keys(customHeaders).length > 0) extraInterceptors.push(customHeadersInterceptor(customHeaders));
799
- if (!token && cred) if (new Date(cred.expiresAt) > /* @__PURE__ */ new Date()) token = cred.accessToken;
800
- else return new DinewayClient({
801
- baseUrl,
802
- token: cred.accessToken,
803
- refreshToken: cred.refreshToken,
804
- onTokenRefresh: (newAccessToken, expiresIn) => {
805
- saveCredentials(baseUrl, {
806
- ...cred,
807
- accessToken: newAccessToken,
808
- expiresAt: new Date(Date.now() + expiresIn * 1e3).toISOString()
809
- });
810
- },
811
- interceptors: extraInterceptors
812
- });
813
- return new DinewayClient({
814
- baseUrl,
815
- token,
816
- devBypass: !token && isLocal,
817
- interceptors: extraInterceptors
818
- });
819
- }
820
-
821
366
  //#endregion
822
367
  //#region src/cli/commands/auth.ts
823
368
  /**
@@ -1064,7 +609,7 @@ const createCommand$1 = defineCommand({
1064
609
  },
1065
610
  draft: {
1066
611
  type: "boolean",
1067
- description: "Keep as draft instead of auto-publishing"
612
+ description: "Explicitly create as Draft (the only supported creation mode)"
1068
613
  },
1069
614
  ...connectionArgs
1070
615
  },
@@ -1079,7 +624,6 @@ const createCommand$1 = defineCommand({
1079
624
  locale: args.locale,
1080
625
  translationOf: args["translation-of"]
1081
626
  });
1082
- if (!args.draft) await client.publish(args.collection, item.id);
1083
627
  output(await client.get(args.collection, item.id), args);
1084
628
  } catch (error) {
1085
629
  consola$1.error(error instanceof Error ? error.message : "Unknown error");
@@ -1122,7 +666,7 @@ const updateCommand = defineCommand({
1122
666
  },
1123
667
  draft: {
1124
668
  type: "boolean",
1125
- description: "Keep as draft instead of auto-publishing"
669
+ description: "Explicitly update the Draft (the only supported update mode)"
1126
670
  },
1127
671
  ...connectionArgs
1128
672
  },
@@ -1136,7 +680,6 @@ const updateCommand = defineCommand({
1136
680
  _rev: args.rev,
1137
681
  locale: args.locale
1138
682
  });
1139
- if (!args.draft && updated.draftRevisionId) await client.publish(args.collection, updated.id);
1140
683
  output(await client.get(args.collection, updated.id), args);
1141
684
  } catch (error) {
1142
685
  consola$1.error(error instanceof Error ? error.message : "Unknown error");
@@ -1190,12 +733,25 @@ const publishCommand$1 = defineCommand({
1190
733
  description: "Content item ID or slug",
1191
734
  required: true
1192
735
  },
736
+ "review-request": {
737
+ type: "string",
738
+ description: "Approved editorial Review Request ID for the exact Draft",
739
+ required: true
740
+ },
741
+ "release-authorization": {
742
+ type: "string",
743
+ description: "Approved release authorization ID for this token action",
744
+ required: true
745
+ },
1193
746
  ...connectionArgs
1194
747
  },
1195
748
  async run({ args }) {
1196
749
  configureOutputMode(args);
1197
750
  try {
1198
- await createClientFromArgs(args).publish(args.collection, args.id);
751
+ await createClientFromArgs(args).publish(args.collection, args.id, {
752
+ reviewRequestId: args["review-request"],
753
+ releaseAuthorizationId: args["release-authorization"]
754
+ });
1199
755
  output({ success: true }, args);
1200
756
  consola$1.success(`Published ${args.collection}/${args.id}`);
1201
757
  } catch (error) {
@@ -1255,12 +811,26 @@ const scheduleCommand = defineCommand({
1255
811
  description: "ISO 8601 datetime to publish at",
1256
812
  required: true
1257
813
  },
814
+ "review-request": {
815
+ type: "string",
816
+ description: "Approved editorial Review Request ID for the exact Draft",
817
+ required: true
818
+ },
819
+ "release-authorization": {
820
+ type: "string",
821
+ description: "Approved release authorization ID for this token action",
822
+ required: true
823
+ },
1258
824
  ...connectionArgs
1259
825
  },
1260
826
  async run({ args }) {
1261
827
  configureOutputMode(args);
1262
828
  try {
1263
- await createClientFromArgs(args).schedule(args.collection, args.id, { at: args.at });
829
+ await createClientFromArgs(args).schedule(args.collection, args.id, {
830
+ at: args.at,
831
+ reviewRequestId: args["review-request"],
832
+ releaseAuthorizationId: args["release-authorization"]
833
+ });
1264
834
  output({ success: true }, args);
1265
835
  consola$1.success(`Scheduled ${args.collection}/${args.id} for ${args.at}`);
1266
836
  } catch (error) {
@@ -1349,7 +919,7 @@ const contentCommand = defineCommand({
1349
919
  //#endregion
1350
920
  //#region src/cli/paywall.ts
1351
921
  const PAYWALL_MESSAGE_PATTERN = /\b(quota|credit|credits|upgrade|plan|billing|subscription|slot)\b/i;
1352
- function isRecord$1(value) {
922
+ function isRecord$2(value) {
1353
923
  return typeof value === "object" && value !== null;
1354
924
  }
1355
925
  function stringField(record, key) {
@@ -1357,9 +927,9 @@ function stringField(record, key) {
1357
927
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
1358
928
  }
1359
929
  function getNestedError(value) {
1360
- if (!isRecord$1(value)) return null;
930
+ if (!isRecord$2(value)) return null;
1361
931
  const nested = value.error;
1362
- return isRecord$1(nested) ? nested : value;
932
+ return isRecord$2(nested) ? nested : value;
1363
933
  }
1364
934
  function isHostedSitesResource(resource) {
1365
935
  const normalized = resource.toLowerCase();
@@ -1367,7 +937,11 @@ function isHostedSitesResource(resource) {
1367
937
  }
1368
938
  function isCreditResource(resource) {
1369
939
  const normalized = resource.toLowerCase();
1370
- return normalized.includes("credit") || normalized.includes("firecrawl") || normalized.includes("dataforseo") || normalized.startsWith("/api/");
940
+ return normalized.includes("credit") || normalized.includes("firecrawl") || normalized.includes("dataforseo");
941
+ }
942
+ function isQuotaResource(resource) {
943
+ const normalized = resource.toLowerCase();
944
+ return normalized.includes("quota") || normalized.includes("place_search") || normalized.includes("place_get") || normalized.includes("/api/places");
1371
945
  }
1372
946
  function getCliPaywallRefreshStatus(paywall, account) {
1373
947
  if (isHostedSitesResource(paywall.resource)) {
@@ -1391,6 +965,10 @@ function getCliPaywallRefreshStatus(paywall, account) {
1391
965
  message: "There are still no provider credits. Complete checkout or top up credits in Dineway Web."
1392
966
  };
1393
967
  }
968
+ if (isQuotaResource(paywall.resource)) return {
969
+ blocked: false,
970
+ message: "Account quota status refreshed. Re-run the blocked command when ready."
971
+ };
1394
972
  return {
1395
973
  blocked: false,
1396
974
  message: "Account status refreshed. Re-run the blocked command when ready."
@@ -1533,7 +1111,7 @@ function collectUploadIncludeFiles(directory = dinewayUploadsJoin(dinewayForgewa
1533
1111
  \t});
1534
1112
  }
1535
1113
  `;
1536
- async function fileExists$11(path) {
1114
+ async function fileExists$10(path) {
1537
1115
  try {
1538
1116
  await access(path);
1539
1117
  return true;
@@ -1694,7 +1272,7 @@ function configurePackageJson(pkg, adapter) {
1694
1272
  }
1695
1273
  async function configureAstroConfig(cwd, adapter, dryRun) {
1696
1274
  const configPath = join(cwd, "astro.config.mjs");
1697
- if (!await fileExists$11(configPath)) return [];
1275
+ if (!await fileExists$10(configPath)) return [];
1698
1276
  const current = await readFile(configPath, "utf-8");
1699
1277
  const next = configureAstroConfigSource(current, adapter);
1700
1278
  if (next === current) return [{
@@ -1713,7 +1291,7 @@ async function configureAstroConfig(cwd, adapter, dryRun) {
1713
1291
  }
1714
1292
  async function configurePackage(cwd, adapter, dryRun) {
1715
1293
  const packagePath = join(cwd, "package.json");
1716
- if (!await fileExists$11(packagePath)) return [];
1294
+ if (!await fileExists$10(packagePath)) return [];
1717
1295
  const current = await readFile(packagePath, "utf-8");
1718
1296
  const nextPkg = configurePackageJson(JSON.parse(current), adapter);
1719
1297
  const next = JSON.stringify(nextPkg, null, " ") + "\n";
@@ -1732,7 +1310,7 @@ async function configurePackage(cwd, adapter, dryRun) {
1732
1310
  }];
1733
1311
  }
1734
1312
  async function configureAstroDeployAdapter(cwd, adapter, dryRun = false) {
1735
- if (!await fileExists$11(join(cwd, "astro.config.mjs"))) return [];
1313
+ if (!await fileExists$10(join(cwd, "astro.config.mjs"))) return [];
1736
1314
  return [...await configureAstroConfig(cwd, adapter, dryRun), ...await configurePackage(cwd, adapter, dryRun)];
1737
1315
  }
1738
1316
 
@@ -1822,7 +1400,7 @@ volumes:
1822
1400
 
1823
1401
  //#endregion
1824
1402
  //#region src/cli/commands/deploy/targets/docker.ts
1825
- async function fileExists$10(path) {
1403
+ async function fileExists$9(path) {
1826
1404
  try {
1827
1405
  await access(path);
1828
1406
  return true;
@@ -1832,7 +1410,7 @@ async function fileExists$10(path) {
1832
1410
  }
1833
1411
  async function writeGeneratedFile$2(cwd, relativePath, content, dryRun) {
1834
1412
  const path = join(cwd, relativePath);
1835
- if (await fileExists$10(path)) return {
1413
+ if (await fileExists$9(path)) return {
1836
1414
  path: relativePath,
1837
1415
  action: "exists"
1838
1416
  };
@@ -1919,10 +1497,10 @@ const FLY_HOST_RE = /\b[a-z0-9][a-z0-9-]*\.fly\.dev\b/;
1919
1497
  const INVALID_FLY_APP_CHARS_RE = /[^a-z0-9-]+/g;
1920
1498
  const REPEATED_HYPHENS_RE$2 = /-+/g;
1921
1499
  const EDGE_HYPHENS_RE$2 = /^-|-$/g;
1922
- const LINE_SPLIT_RE = /\r?\n/;
1500
+ const LINE_SPLIT_RE$1 = /\r?\n/;
1923
1501
  const DEFAULT_REGION$1 = "iad";
1924
1502
  const FLY_VOLUME_NAME = "dineway_data";
1925
- async function fileExists$9(path) {
1503
+ async function fileExists$8(path) {
1926
1504
  try {
1927
1505
  await access(path);
1928
1506
  return true;
@@ -1932,7 +1510,7 @@ async function fileExists$9(path) {
1932
1510
  }
1933
1511
  async function writeGeneratedFile$1(cwd, relativePath, content, dryRun) {
1934
1512
  const path = join(cwd, relativePath);
1935
- if (await fileExists$9(path)) return {
1513
+ if (await fileExists$8(path)) return {
1936
1514
  path: relativePath,
1937
1515
  action: "exists"
1938
1516
  };
@@ -2011,7 +1589,7 @@ primary_region = "${region}"
2011
1589
  function parseFlyUrl(stdout) {
2012
1590
  const direct = stdout.match(FLY_URL_RE);
2013
1591
  if (direct) return direct[0];
2014
- for (const line of stdout.split(LINE_SPLIT_RE).filter(Boolean)) try {
1592
+ for (const line of stdout.split(LINE_SPLIT_RE$1).filter(Boolean)) try {
2015
1593
  const url = findFlyUrl(JSON.parse(line));
2016
1594
  if (url) return url;
2017
1595
  } catch {
@@ -2121,69 +1699,9 @@ const flyTarget = {
2121
1699
  }
2122
1700
  };
2123
1701
 
2124
- //#endregion
2125
- //#region src/cli/commands/deploy/utils/config.ts
2126
- const DINEWAY_DIR_NAME = ".dineway";
2127
- const DEPLOY_STATE_FILE_NAME = "deploy.json";
2128
- function deployStatePath(cwd) {
2129
- return join(cwd, DINEWAY_DIR_NAME, DEPLOY_STATE_FILE_NAME);
2130
- }
2131
- function emptyDeployState() {
2132
- return { version: 1 };
2133
- }
2134
- async function readDeployPackageJson(cwd) {
2135
- const content = await readFile(join(cwd, "package.json"), "utf-8");
2136
- return JSON.parse(content);
2137
- }
2138
- async function readDeployState(cwd) {
2139
- try {
2140
- const content = await readFile(deployStatePath(cwd), "utf-8");
2141
- return {
2142
- ...emptyDeployState(),
2143
- ...JSON.parse(content),
2144
- version: 1
2145
- };
2146
- } catch (error) {
2147
- if (error && typeof error === "object" && error.code === "ENOENT") return emptyDeployState();
2148
- throw error;
2149
- }
2150
- }
2151
- async function writeDeployState(cwd, state) {
2152
- await mkdir(join(cwd, DINEWAY_DIR_NAME), { recursive: true });
2153
- await writeFile(deployStatePath(cwd), JSON.stringify({
2154
- ...state,
2155
- version: 1
2156
- }, null, " ") + "\n", "utf-8");
2157
- }
2158
- async function readSavedDeployTarget(cwd) {
2159
- return (await readDeployState(cwd)).target;
2160
- }
2161
- async function writeDeployTarget(cwd, target) {
2162
- await writeDeployState(cwd, {
2163
- ...await readDeployState(cwd),
2164
- target
2165
- });
2166
- }
2167
- async function readForgewayDeployMetadata(cwd) {
2168
- return (await readDeployState(cwd)).targets?.forgeway ?? {};
2169
- }
2170
- async function writeForgewayDeployMetadata(cwd, metadata) {
2171
- const state = await readDeployState(cwd);
2172
- await writeDeployState(cwd, {
2173
- ...state,
2174
- targets: {
2175
- ...state.targets,
2176
- forgeway: {
2177
- ...state.targets?.forgeway,
2178
- ...metadata
2179
- }
2180
- }
2181
- });
2182
- }
2183
-
2184
1702
  //#endregion
2185
1703
  //#region src/cli/commands/deploy/targets/forgeway.ts
2186
- const DEFAULT_DINEWAY_API_BASE_URL$1 = "https://api.dineway.ai";
1704
+ const DEFAULT_DINEWAY_API_BASE_URL = "https://api.dineway.ai";
2187
1705
  const DATABASE_ENV_VAR_NAMES = ["DINEWAY_DATABASE_URL", "DINEWAY_DATABASE_AUTH_TOKEN"];
2188
1706
  const POLL_INTERVAL_MS$1 = 5e3;
2189
1707
  const POLL_TIMEOUT_MS$1 = 3e5;
@@ -2198,12 +1716,12 @@ const NON_COMPARE_PATTERN = /[^\p{Letter}\p{Number}]+/gu;
2198
1716
  const LOCATION_PARTS_PATTERN = /[,;|/]+|\s+-\s+/u;
2199
1717
  const JWT_PATTERN = /[A-Za-z0-9_-]{32,}\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}/g;
2200
1718
  const TOKEN_ASSIGNMENT_PATTERN = /(DINEWAY_DATABASE_AUTH_TOKEN|DATABASE_AUTH_TOKEN|AUTH_TOKEN)=\S+/gi;
2201
- const TRAILING_SLASH_PATTERN$1 = /\/$/;
1719
+ const TRAILING_SLASH_PATTERN = /\/$/;
2202
1720
  const BACKSLASH_PATTERN = /\\/g;
2203
1721
  const DINEWAY_PACKAGE_NAME = "dineway";
2204
1722
  const SEMVER_VERSION_PATTERN = /^\d+\.\d+\.\d+(?:[-+].*)?$/;
2205
1723
  const LOCAL_PACKAGE_SPEC_PATTERN = /^(?:workspace|file|link|portal):/;
2206
- const SHADOW_EMAIL_PATTERN$1 = /^shadow_[a-f0-9]{16}@dineway\.ai$/i;
1724
+ const SHADOW_EMAIL_PATTERN = /^shadow_[a-f0-9]{16}@dineway\.ai$/i;
2207
1725
  const FOOD_TYPES = new Set([
2208
1726
  "restaurant",
2209
1727
  "cafe",
@@ -2251,7 +1769,7 @@ var ForgewayApiError = class extends Error {
2251
1769
  this.name = "ForgewayApiError";
2252
1770
  }
2253
1771
  };
2254
- function normalizeForgewayUser$1(user) {
1772
+ function normalizeForgewayUser(user) {
2255
1773
  return {
2256
1774
  id: user.id,
2257
1775
  name: user.profile?.name ?? user.name ?? user.email,
@@ -2263,7 +1781,7 @@ function normalizeForgewayUser$1(user) {
2263
1781
  function getFetch(deps) {
2264
1782
  return deps.fetch ?? fetch;
2265
1783
  }
2266
- async function fileExists$8(path) {
1784
+ async function fileExists$7(path) {
2267
1785
  try {
2268
1786
  await access(path);
2269
1787
  return true;
@@ -2272,7 +1790,7 @@ async function fileExists$8(path) {
2272
1790
  }
2273
1791
  }
2274
1792
  function normalizePlatformApiUrl(value) {
2275
- return (value || DEFAULT_DINEWAY_API_BASE_URL$1).replace(TRAILING_SLASH_PATTERN$1, "");
1793
+ return (value || DEFAULT_DINEWAY_API_BASE_URL).replace(TRAILING_SLASH_PATTERN, "");
2276
1794
  }
2277
1795
  function resolvePlatformApiUrl(savedPlatformApiUrl) {
2278
1796
  return normalizePlatformApiUrl(getEnv("DINEWAY_API_BASE_URL") ?? (typeof savedPlatformApiUrl === "string" ? savedPlatformApiUrl : void 0));
@@ -2288,7 +1806,7 @@ function formatDuration(ms) {
2288
1806
  if (ms < 1e3) return `${ms}ms`;
2289
1807
  return `${Math.round(ms / 100) / 10}s`;
2290
1808
  }
2291
- function formatBytes$1(bytes) {
1809
+ function formatBytes(bytes) {
2292
1810
  if (bytes < 1024) return `${bytes} B`;
2293
1811
  const units = [
2294
1812
  "KB",
@@ -2335,11 +1853,11 @@ async function parseErrorResponse(response) {
2335
1853
  }
2336
1854
  return `Forgeway request failed: ${response.status}`;
2337
1855
  }
2338
- function normalizeEmail$1(email) {
1856
+ function normalizeEmail(email) {
2339
1857
  return email.trim().toLowerCase();
2340
1858
  }
2341
- function isShadowEmail$1(email) {
2342
- return Boolean(email && SHADOW_EMAIL_PATTERN$1.test(email));
1859
+ function isShadowEmail(email) {
1860
+ return Boolean(email && SHADOW_EMAIL_PATTERN.test(email));
2343
1861
  }
2344
1862
  async function refreshFormalAccessToken(context, deps) {
2345
1863
  if (!context.refreshToken) throw new Error("Forgeway formal account refresh token is missing. Run dineway deploy again.");
@@ -2351,7 +1869,7 @@ async function refreshFormalAccessToken(context, deps) {
2351
1869
  if (!response.ok) throw new Error("Failed to refresh Forgeway formal account credentials. Run dineway deploy again.");
2352
1870
  const payload = await response.json();
2353
1871
  if (!payload.accessToken) throw new Error("Forgeway refresh response was missing accessToken.");
2354
- const user = assertVerifiedFormalAccountUser(payload.user ? normalizeForgewayUser$1(payload.user) : context.user, context.user?.email);
1872
+ const user = assertVerifiedFormalAccountUser(payload.user ? normalizeForgewayUser(payload.user) : context.user, context.user?.email);
2355
1873
  const updated = {
2356
1874
  platformApiUrl: context.platformApiUrl,
2357
1875
  accessToken: payload.accessToken,
@@ -2398,7 +1916,7 @@ function getDependencySpec(pkg, name) {
2398
1916
  if (typeof value === "string" && value.trim()) return value.trim();
2399
1917
  }
2400
1918
  }
2401
- async function readProjectPackage(cwd) {
1919
+ async function readProjectPackage$1(cwd) {
2402
1920
  try {
2403
1921
  return await readDeployPackageJson(cwd);
2404
1922
  } catch {
@@ -2412,7 +1930,7 @@ function isDinewayDependencySpecCompatible(dependencySpec, cliVersion) {
2412
1930
  }
2413
1931
  async function warnProjectDinewayRuntimeMismatch(cwd, cliVersion = VERSION) {
2414
1932
  if (!SEMVER_VERSION_PATTERN.test(cliVersion)) return;
2415
- const pkg = await readProjectPackage(cwd);
1933
+ const pkg = await readProjectPackage$1(cwd);
2416
1934
  if (!pkg) return;
2417
1935
  const dependencySpec = getDependencySpec(pkg, DINEWAY_PACKAGE_NAME);
2418
1936
  if (!dependencySpec) return;
@@ -2426,20 +1944,20 @@ function savedPublisherEmail(saved) {
2426
1944
  return typeof saved.publisherEmail === "string" && saved.publisherEmail.trim() ? saved.publisherEmail : void 0;
2427
1945
  }
2428
1946
  function validatePublisherEmail(rawEmail) {
2429
- const email = normalizeEmail$1(rawEmail);
2430
- if (!email || isShadowEmail$1(email)) throw new Error("Dineway account email must be a real owner email, not a shadow user email.");
1947
+ const email = normalizeEmail(rawEmail);
1948
+ if (!email || isShadowEmail(email)) throw new Error("Dineway account email must be a real owner email, not a shadow user email.");
2431
1949
  return email;
2432
1950
  }
2433
1951
  function isStoredFormalCredentialForEmail(stored, email) {
2434
1952
  if (!stored?.accessToken || !stored.user) return false;
2435
- if (email && normalizeEmail$1(stored.user.email) !== email) return false;
1953
+ if (email && normalizeEmail(stored.user.email) !== email) return false;
2436
1954
  if (stored.user.emailVerified !== true) return false;
2437
- if (isShadowEmail$1(stored.user.email)) return false;
1955
+ if (isShadowEmail(stored.user.email)) return false;
2438
1956
  return true;
2439
1957
  }
2440
1958
  function assertVerifiedFormalAccountUser(user, expectedEmail) {
2441
- if (!user || user.emailVerified !== true || isShadowEmail$1(user.email)) throw new Error("Forgeway formal account credentials require a verified formal account.");
2442
- if (expectedEmail && normalizeEmail$1(user.email) !== normalizeEmail$1(expectedEmail)) throw new Error("Forgeway refresh returned credentials for a different email.");
1959
+ if (!user || user.emailVerified !== true || isShadowEmail(user.email)) throw new Error("Forgeway formal account credentials require a verified formal account.");
1960
+ if (expectedEmail && normalizeEmail(user.email) !== normalizeEmail(expectedEmail)) throw new Error("Forgeway refresh returned credentials for a different email.");
2443
1961
  return user;
2444
1962
  }
2445
1963
  async function readCachedPublisherCredentials(cwd, platformApiUrl, email, deps) {
@@ -2472,14 +1990,14 @@ async function resolveCachedPublisherAccount(cwd, options, saved, platformApiUrl
2472
1990
  platformApiUrl
2473
1991
  });
2474
1992
  if (isStoredFormalCredentialForEmail(singleCredentials)) return {
2475
- email: normalizeEmail$1(singleCredentials.user.email),
1993
+ email: normalizeEmail(singleCredentials.user.email),
2476
1994
  credentials: singleCredentials
2477
1995
  };
2478
1996
  const accounts = (await (deps.listCredentials ?? listForgewayCredentials)(platformApiUrl)).filter((credentials) => isStoredFormalCredentialForEmail(credentials));
2479
1997
  if (accounts.length === 1) {
2480
1998
  const credentials = accounts[0];
2481
1999
  return {
2482
- email: normalizeEmail$1(credentials.user.email),
2000
+ email: normalizeEmail(credentials.user.email),
2483
2001
  credentials
2484
2002
  };
2485
2003
  }
@@ -2541,7 +2059,7 @@ async function resolveProjectContext(cwd, options, deps) {
2541
2059
  if (formal.email) throw new Error(`No verified Dineway account credentials were found for ${formal.email}. Run: dineway account login --email ${formal.email}`);
2542
2060
  throw new Error("No verified Dineway account credentials were found. Run: dineway account login");
2543
2061
  }
2544
- function isRecord(value) {
2062
+ function isRecord$1(value) {
2545
2063
  return typeof value === "object" && value !== null && !Array.isArray(value);
2546
2064
  }
2547
2065
  function scalarText(value) {
@@ -2554,7 +2072,7 @@ function uniqueStrings(values) {
2554
2072
  function displayText(value) {
2555
2073
  if (typeof value === "string" || typeof value === "number") return String(value);
2556
2074
  if (Array.isArray(value)) return value.map(displayText).filter(Boolean).join(" ");
2557
- if (isRecord(value)) return displayText(value.text) || displayText(value.name) || displayText(value.displayName) || displayText(value.longText) || displayText(value.shortText) || displayText(value.long_name) || displayText(value.short_name) || displayText(value.value);
2075
+ if (isRecord$1(value)) return displayText(value.text) || displayText(value.name) || displayText(value.displayName) || displayText(value.longText) || displayText(value.shortText) || displayText(value.long_name) || displayText(value.short_name) || displayText(value.value);
2558
2076
  return "";
2559
2077
  }
2560
2078
  function normalizeCompare(value) {
@@ -2572,7 +2090,7 @@ function candidateNameTexts(candidate) {
2572
2090
  ]);
2573
2091
  }
2574
2092
  function candidateAddressTexts(candidate) {
2575
- const addressComponents = Array.isArray(candidate.addressComponents) ? candidate.addressComponents.flatMap((component) => isRecord(component) ? [
2093
+ const addressComponents = Array.isArray(candidate.addressComponents) ? candidate.addressComponents.flatMap((component) => isRecord$1(component) ? [
2576
2094
  displayText(component.longText),
2577
2095
  displayText(component.shortText),
2578
2096
  displayText(component.long_name),
@@ -2714,11 +2232,11 @@ function selectPlaceCandidate(candidates, context) {
2714
2232
  } : null;
2715
2233
  }
2716
2234
  function extractPlaceCandidates(payload) {
2717
- const root = isRecord(payload) ? payload : {};
2718
- const nestedData = isRecord(root.data) ? root.data : {};
2235
+ const root = isRecord$1(payload) ? payload : {};
2236
+ const nestedData = isRecord$1(root.data) ? root.data : {};
2719
2237
  const places = root.places ?? root.results ?? nestedData.places ?? nestedData.results;
2720
2238
  if (!Array.isArray(places)) throw new Error("Dineway place search response did not include places/results.");
2721
- return places.filter(isRecord);
2239
+ return places.filter(isRecord$1);
2722
2240
  }
2723
2241
  async function resolvePlaceIdWithFormalSearch(options) {
2724
2242
  const candidates = extractPlaceCandidates(await ossFetch(options.context, "/api/places/search", options.deps, {
@@ -2735,7 +2253,7 @@ async function resolvePlaceIdWithFormalSearch(options) {
2735
2253
  return finalPlaceId;
2736
2254
  }
2737
2255
  async function ossFetch(context, path, deps, options = {}) {
2738
- const request = async (token) => await forgewayRequest(`${context.ossHost.replace(TRAILING_SLASH_PATTERN$1, "")}${path}`, {
2256
+ const request = async (token) => await forgewayRequest(`${context.ossHost.replace(TRAILING_SLASH_PATTERN, "")}${path}`, {
2739
2257
  ...options,
2740
2258
  headers: {
2741
2259
  "Content-Type": "application/json",
@@ -2761,18 +2279,18 @@ function deriveForgewaySiteSlug(restaurantName, city) {
2761
2279
  async function resolveSeedPath$1(cwd, explicitSeedPath) {
2762
2280
  if (explicitSeedPath) {
2763
2281
  const resolved = resolve(cwd, explicitSeedPath);
2764
- if (!await fileExists$8(resolved)) throw new Error(`Seed file not found: ${explicitSeedPath}`);
2282
+ if (!await fileExists$7(resolved)) throw new Error(`Seed file not found: ${explicitSeedPath}`);
2765
2283
  return resolved;
2766
2284
  }
2767
2285
  const convention = resolve(cwd, ".dineway", "seed.json");
2768
- if (await fileExists$8(convention)) return convention;
2286
+ if (await fileExists$7(convention)) return convention;
2769
2287
  const pkg = await readDeployPackageJson(cwd).catch(() => null);
2770
2288
  if (typeof pkg?.dineway?.seed === "string") {
2771
2289
  const configured = resolve(cwd, pkg.dineway.seed);
2772
- if (await fileExists$8(configured)) return configured;
2290
+ if (await fileExists$7(configured)) return configured;
2773
2291
  }
2774
2292
  const legacyConvention = resolve(cwd, "seed", "seed.json");
2775
- if (await fileExists$8(legacyConvention)) return legacyConvention;
2293
+ if (await fileExists$7(legacyConvention)) return legacyConvention;
2776
2294
  return null;
2777
2295
  }
2778
2296
  async function readSeedTitle(seedPath) {
@@ -3140,7 +2658,7 @@ async function deploySiteProject(options) {
3140
2658
  consola.start("Scanning deploy files");
3141
2659
  const localFiles = await (options.deps.collectDeploymentFiles ?? collectDeploymentFiles)(options.sourceDir);
3142
2660
  if (localFiles.length === 0) throw new Error("No deployable files found in the source directory.");
3143
- consola.success(`Scanned ${localFiles.length} files (${formatBytes$1(deploymentFileSize(localFiles))}) in ${formatDuration(Date.now() - scanStart)}`);
2661
+ consola.success(`Scanned ${localFiles.length} files (${formatBytes(deploymentFileSize(localFiles))}) in ${formatDuration(Date.now() - scanStart)}`);
3144
2662
  const createStart = Date.now();
3145
2663
  consola.start("Creating Forgeway deployment");
3146
2664
  const createResult = await ossFetch(options.context, `/api/deployments/sites/${encodeURIComponent(options.siteId)}/deploy`, options.deps, {
@@ -3159,7 +2677,7 @@ async function deploySiteProject(options) {
3159
2677
  const pendingFiles = createResult.files.filter((file) => !file.uploadedAt);
3160
2678
  const pendingSize = pendingFiles.reduce((total, file) => total + file.size, 0);
3161
2679
  if (pendingFiles.length === 0) consola.success("No file uploads needed");
3162
- else consola.start(`Uploading ${pendingFiles.length} changed files (${formatBytes$1(pendingSize)})`);
2680
+ else consola.start(`Uploading ${pendingFiles.length} changed files (${formatBytes(pendingSize)})`);
3163
2681
  let uploadedCount = 0;
3164
2682
  await runWithConcurrency(pendingFiles, DIRECT_UPLOAD_CONCURRENCY, async (manifestFile) => {
3165
2683
  const localFile = localFileByPath.get(manifestFile.path);
@@ -3250,7 +2768,7 @@ async function deployForgeway(cwd, options, deps = {}) {
3250
2768
  deps,
3251
2769
  verbose
3252
2770
  });
3253
- if (context.user?.email) await writeForgewayDeployMetadata(cwd, { publisherEmail: normalizeEmail$1(context.user.email) });
2771
+ if (context.user?.email) await writeForgewayDeployMetadata(cwd, { publisherEmail: normalizeEmail(context.user.email) });
3254
2772
  const url = deployment.liveUrl ?? `https://${site.domain}`;
3255
2773
  await (deps.writeProjectEnv ?? persistForgewayCliEnv)(cwd, {
3256
2774
  siteUrl: url,
@@ -3303,7 +2821,7 @@ pnpm-debug.log*
3303
2821
  yarn-debug.log*
3304
2822
  yarn-error.log*
3305
2823
  `;
3306
- async function fileExists$7(path) {
2824
+ async function fileExists$6(path) {
3307
2825
  try {
3308
2826
  await access(path);
3309
2827
  return true;
@@ -3313,7 +2831,7 @@ async function fileExists$7(path) {
3313
2831
  }
3314
2832
  async function writeGeneratedFile(cwd, relativePath, content, dryRun) {
3315
2833
  const path = join(cwd, relativePath);
3316
- if (await fileExists$7(path)) return {
2834
+ if (await fileExists$6(path)) return {
3317
2835
  path: relativePath,
3318
2836
  action: "exists"
3319
2837
  };
@@ -3446,7 +2964,7 @@ const gcpTarget = {
3446
2964
  env: options.env,
3447
2965
  stdio: "inherit"
3448
2966
  });
3449
- if (result.code !== 0 && !await fileExists$7(join(cwd, "Dockerfile"))) {
2967
+ if (result.code !== 0 && !await fileExists$6(join(cwd, "Dockerfile"))) {
3450
2968
  await writeFile(join(cwd, "Dockerfile"), generateDockerfile(), "utf-8");
3451
2969
  result = await runCommand$1("gcloud", deployArgs, {
3452
2970
  cwd,
@@ -3465,7 +2983,7 @@ const gcpTarget = {
3465
2983
 
3466
2984
  //#endregion
3467
2985
  //#region src/cli/commands/deploy/utils/build.ts
3468
- async function fileExists$6(path) {
2986
+ async function fileExists$5(path) {
3469
2987
  try {
3470
2988
  await access(path);
3471
2989
  return true;
@@ -3474,19 +2992,19 @@ async function fileExists$6(path) {
3474
2992
  }
3475
2993
  }
3476
2994
  async function detectBuildCommand(cwd) {
3477
- if (await fileExists$6(join(cwd, "pnpm-lock.yaml"))) return {
2995
+ if (await fileExists$5(join(cwd, "pnpm-lock.yaml"))) return {
3478
2996
  packageManager: "pnpm",
3479
2997
  command: "pnpm",
3480
2998
  args: ["build"],
3481
2999
  display: "pnpm build"
3482
3000
  };
3483
- if (await fileExists$6(join(cwd, "yarn.lock"))) return {
3001
+ if (await fileExists$5(join(cwd, "yarn.lock"))) return {
3484
3002
  packageManager: "yarn",
3485
3003
  command: "yarn",
3486
3004
  args: ["build"],
3487
3005
  display: "yarn build"
3488
3006
  };
3489
- if (await fileExists$6(join(cwd, "bun.lock")) || await fileExists$6(join(cwd, "bun.lockb"))) return {
3007
+ if (await fileExists$5(join(cwd, "bun.lock")) || await fileExists$5(join(cwd, "bun.lockb"))) return {
3490
3008
  packageManager: "bun",
3491
3009
  command: "bun",
3492
3010
  args: ["run", "build"],
@@ -3517,7 +3035,7 @@ const INVALID_RAILWAY_PROJECT_CHARS_RE = /[^a-z0-9-]+/g;
3517
3035
  const REPEATED_HYPHENS_RE = /-+/g;
3518
3036
  const EDGE_HYPHENS_RE = /^-|-$/g;
3519
3037
  const RAILWAY_BUILDER = "RAILPACK";
3520
- async function fileExists$5(path) {
3038
+ async function fileExists$4(path) {
3521
3039
  try {
3522
3040
  await access(path);
3523
3041
  return true;
@@ -3611,7 +3129,7 @@ const railwayTarget = {
3611
3129
  async generateConfig(cwd, options) {
3612
3130
  const generated = await configureAstroDeployAdapter(cwd, "node", options.dryRun);
3613
3131
  const railwayPath = join(cwd, "railway.json");
3614
- if (await fileExists$5(railwayPath)) return [...generated, {
3132
+ if (await fileExists$4(railwayPath)) return [...generated, {
3615
3133
  path: "railway.json",
3616
3134
  action: "exists"
3617
3135
  }];
@@ -3968,7 +3486,7 @@ async function readPackageJson$2(cwd) {
3968
3486
  return null;
3969
3487
  }
3970
3488
  }
3971
- async function fileExists$4(path) {
3489
+ async function fileExists$3(path) {
3972
3490
  try {
3973
3491
  await access(path);
3974
3492
  return true;
@@ -4015,7 +3533,7 @@ const devCommand = defineCommand({
4015
3533
  }
4016
3534
  const database = resolveCliDatabaseTarget(cwd, args.database);
4017
3535
  consola.info(`Database: ${database.display}`);
4018
- if (database.localPath && !await fileExists$4(database.localPath)) consola.start("Database not found, initializing...");
3536
+ if (database.localPath && !await fileExists$3(database.localPath)) consola.start("Database not found, initializing...");
4019
3537
  const db = createDatabase({ url: database.url });
4020
3538
  try {
4021
3539
  consola.start("Checking database migrations...");
@@ -4032,7 +3550,7 @@ const devCommand = defineCommand({
4032
3550
  const remoteUrl = pkg.dineway?.url || process.env.DINEWAY_URL;
4033
3551
  if (!remoteUrl) consola.warn("No remote URL configured. Set DINEWAY_URL or dineway.url in package.json");
4034
3552
  else try {
4035
- const { createClientFromArgs } = await Promise.resolve().then(() => client_factory_exports);
3553
+ const { createClientFromArgs } = await import("../client-factory-BDD9azPJ.mjs").then((n) => n.t);
4036
3554
  const client = createClientFromArgs({ url: remoteUrl });
4037
3555
  const schema = await client.schemaExport();
4038
3556
  const types = await client.schemaTypes();
@@ -4054,8 +3572,8 @@ const devCommand = defineCommand({
4054
3572
  "--port",
4055
3573
  args.port
4056
3574
  ];
4057
- const pnpmLockExists = await fileExists$4(resolve(cwd, "pnpm-lock.yaml"));
4058
- const yarnLockExists = await fileExists$4(resolve(cwd, "yarn.lock"));
3575
+ const pnpmLockExists = await fileExists$3(resolve(cwd, "pnpm-lock.yaml"));
3576
+ const yarnLockExists = await fileExists$3(resolve(cwd, "yarn.lock"));
4059
3577
  let cmd;
4060
3578
  let cmdArgs;
4061
3579
  if (pnpmLockExists) {
@@ -4099,7 +3617,7 @@ const devCommand = defineCommand({
4099
3617
  *
4100
3618
  * Diagnose database health: connection, migrations, schema integrity.
4101
3619
  */
4102
- async function fileExists$3(path) {
3620
+ async function fileExists$2(path) {
4103
3621
  try {
4104
3622
  await access(path);
4105
3623
  return true;
@@ -4112,7 +3630,7 @@ function printResult(result) {
4112
3630
  }
4113
3631
  async function checkDatabase(database) {
4114
3632
  const results = [];
4115
- if (database.localPath && !await fileExists$3(database.localPath)) {
3633
+ if (database.localPath && !await fileExists$2(database.localPath)) {
4116
3634
  results.push({
4117
3635
  name: "database",
4118
3636
  status: "fail",
@@ -4166,7 +3684,7 @@ async function checkDatabase(database) {
4166
3684
  });
4167
3685
  } catch {}
4168
3686
  try {
4169
- const count = (await sql`SELECT COUNT(id) as count FROM _dineway_users`.execute(db)).rows[0]?.count ?? 0;
3687
+ const count = (await sql`SELECT COUNT(id) as count FROM users`.execute(db)).rows[0]?.count ?? 0;
4170
3688
  results.push({
4171
3689
  name: "users",
4172
3690
  status: count > 0 ? "pass" : "warn",
@@ -4314,7 +3832,7 @@ async function exportSeed(db, withContent) {
4314
3832
  const { bylines, idToSeedId: bylineIdToSeedId } = await exportBylines(db);
4315
3833
  if (bylines.length > 0) seed.bylines = bylines;
4316
3834
  if (withContent !== void 0) {
4317
- const collections = withContent === "" || withContent === "true" ? null : withContent.split(",").map((s) => s.trim()).filter(Boolean);
3835
+ const collections = withContent === "" || withContent === "true" || withContent === "all" ? null : withContent.split(",").map((s) => s.trim()).filter(Boolean);
4318
3836
  seed.content = await exportContent(db, seed.collections || [], collections, bylineIdToSeedId, i18nEnabled);
4319
3837
  }
4320
3838
  return seed;
@@ -4444,8 +3962,8 @@ async function exportTaxonomies(db, i18nEnabled) {
4444
3962
  for (const def of defs) {
4445
3963
  const defSeedId = i18nEnabled && def.locale ? `tax:${def.name}:${def.locale}` : `tax:${def.name}`;
4446
3964
  const terms = await termRepo.findByName(def.name, { locale: def.locale });
4447
- const idToSlug = /* @__PURE__ */ new Map();
4448
- for (const term of terms) idToSlug.set(term.id, term.slug);
3965
+ const groupToSlug = /* @__PURE__ */ new Map();
3966
+ for (const term of terms) groupToSlug.set(term.translationGroup ?? term.id, term.slug);
4449
3967
  const termGroupToSeedId = /* @__PURE__ */ new Map();
4450
3968
  const seedTerms = [];
4451
3969
  for (const term of terms) {
@@ -4456,7 +3974,7 @@ async function exportTaxonomies(db, i18nEnabled) {
4456
3974
  label: term.label,
4457
3975
  description: typeof term.data?.description === "string" ? term.data.description : void 0
4458
3976
  };
4459
- if (term.parentId) seedTerm.parent = idToSlug.get(term.parentId);
3977
+ if (term.parentId) seedTerm.parent = groupToSlug.get(term.parentId);
4460
3978
  if (i18nEnabled && term.locale) {
4461
3979
  seedTerm.locale = term.locale;
4462
3980
  if (term.translationGroup) {
@@ -4745,7 +4263,7 @@ async function getTaxonomyAssignments(taxonomyRepo, collection, entryId) {
4745
4263
  *
4746
4264
  * Initialize database from template config in package.json
4747
4265
  */
4748
- async function fileExists$2(path) {
4266
+ async function fileExists$1(path) {
4749
4267
  try {
4750
4268
  await access(path);
4751
4269
  return true;
@@ -4845,7 +4363,7 @@ const initCommand = defineCommand({
4845
4363
  if (alreadyInitialized && args.force) consola.warn("Re-initializing (--force)...");
4846
4364
  if (config?.schema) {
4847
4365
  const schemaPath = resolve(cwd, config.schema);
4848
- if (await fileExists$2(schemaPath)) {
4366
+ if (await fileExists$1(schemaPath)) {
4849
4367
  consola.start(`Running schema: ${config.schema}`);
4850
4368
  try {
4851
4369
  await runSqlFile(db, schemaPath);
@@ -5389,606 +4907,19 @@ const menuCommand = defineCommand({
5389
4907
  });
5390
4908
 
5391
4909
  //#endregion
5392
- //#region src/cli/commands/bundle-utils.ts
4910
+ //#region src/cli/commands/plugin-init.ts
5393
4911
  /**
5394
- * Bundle utility functions
4912
+ * dineway plugin init
5395
4913
  *
5396
- * Shared logic extracted from the bundle command so it can be tested
5397
- * without the CLI harness and tsdown dependency.
5398
- */
5399
- const MAX_BUNDLE_SIZE = 256 * 1024;
5400
- const MAX_FILE_SIZE = 128 * 1024;
5401
- const MAX_FILE_COUNT = 20;
5402
- const MAX_SCREENSHOTS = 5;
5403
- const MAX_SCREENSHOT_WIDTH = 1920;
5404
- const MAX_SCREENSHOT_HEIGHT = 1080;
5405
- const ICON_SIZE = 256;
5406
- function toFileImportSpecifier(filePath) {
5407
- return pathToFileURL(filePath).href;
5408
- }
5409
- /** Matches Node.js built-in imports in bundled output. */
5410
- const NODE_BUILTIN_IMPORT_RE = /(?:import|export|require)\s*(?:\(|[^(]*?\bfrom\s+)["'](?:node:)?([a-z_]+)(?:\/[^"']*)?\s*["']\)?/g;
5411
- const LEADING_DOT_SLASH_RE = /^\.\//;
5412
- const DIST_PREFIX_RE = /^dist\//;
5413
- const MJS_EXT_RE = /\.m?js$/;
5414
- const BUILD_OUTPUT_EXT_RE = /\.(?:mjs|js|cjs)$/;
5415
- const TS_TO_TSX_RE = /\.ts$/;
5416
- /** Node.js built-in modules that shouldn't appear in sandbox code */
5417
- const NODE_BUILTINS = new Set([
5418
- "assert",
5419
- "buffer",
5420
- "child_process",
5421
- "cluster",
5422
- "crypto",
5423
- "dgram",
5424
- "dns",
5425
- "domain",
5426
- "events",
5427
- "fs",
5428
- "http",
5429
- "http2",
5430
- "https",
5431
- "inspector",
5432
- "module",
5433
- "net",
5434
- "os",
5435
- "path",
5436
- "perf_hooks",
5437
- "process",
5438
- "punycode",
5439
- "querystring",
5440
- "readline",
5441
- "repl",
5442
- "stream",
5443
- "string_decoder",
5444
- "sys",
5445
- "timers",
5446
- "tls",
5447
- "trace_events",
5448
- "tty",
5449
- "url",
5450
- "util",
5451
- "v8",
5452
- "vm",
5453
- "wasi",
5454
- "worker_threads",
5455
- "zlib"
5456
- ]);
5457
- async function fileExists$1(path) {
5458
- try {
5459
- await access(path);
5460
- return true;
5461
- } catch {
5462
- return false;
5463
- }
5464
- }
5465
- /**
5466
- * Read image dimensions from a buffer.
5467
- * Returns [width, height] or null if the format is unrecognized.
5468
- */
5469
- function readImageDimensions(buf) {
5470
- try {
5471
- const result = imageSize(buf);
5472
- if (result.width != null && result.height != null) return [result.width, result.height];
5473
- return null;
5474
- } catch {
5475
- return null;
5476
- }
5477
- }
5478
- /**
5479
- * Extract manifest metadata from a ResolvedPlugin.
5480
- * Strips functions (hooks, route handlers) and keeps only serializable metadata.
5481
- */
5482
- function extractManifest(plugin) {
5483
- const hooks = [];
5484
- for (const [name, resolved] of Object.entries(plugin.hooks)) {
5485
- if (!resolved) continue;
5486
- if (resolved.exclusive || resolved.priority !== 100 || resolved.timeout !== 5e3) {
5487
- const entry = { name };
5488
- if (resolved.exclusive) entry.exclusive = true;
5489
- if (resolved.priority !== 100) entry.priority = resolved.priority;
5490
- if (resolved.timeout !== 5e3) entry.timeout = resolved.timeout;
5491
- hooks.push(entry);
5492
- } else hooks.push(name);
5493
- }
5494
- return {
5495
- id: plugin.id,
5496
- version: plugin.version,
5497
- capabilities: plugin.capabilities,
5498
- allowedHosts: plugin.allowedHosts,
5499
- storage: plugin.storage,
5500
- hooks,
5501
- routes: Object.keys(plugin.routes),
5502
- admin: {
5503
- settingsSchema: plugin.admin.settingsSchema,
5504
- pages: plugin.admin.pages,
5505
- widgets: plugin.admin.widgets
5506
- }
5507
- };
5508
- }
5509
- /**
5510
- * Scan bundled code for Node.js built-in imports.
5511
- * Matches require("node:xxx"), require("xxx"), import("node:xxx") — the patterns
5512
- * that appear in bundled ESM/CJS output (not source-level named imports).
5513
- * Returns deduplicated array of built-in module names found.
5514
- */
5515
- function findNodeBuiltinImports(code) {
5516
- const found = [];
5517
- NODE_BUILTIN_IMPORT_RE.lastIndex = 0;
5518
- let match;
5519
- while ((match = NODE_BUILTIN_IMPORT_RE.exec(code)) !== null) {
5520
- const mod = match[1];
5521
- if (NODE_BUILTINS.has(mod)) found.push(mod);
5522
- }
5523
- return [...new Set(found)];
5524
- }
5525
- /**
5526
- * Find a build output file by base name, checking common extensions.
5527
- * tsdown may output .mjs, .js, or .cjs depending on format and config.
5528
- */
5529
- async function findBuildOutput(dir, baseName) {
5530
- const normalizedBaseName = baseName.replace(BUILD_OUTPUT_EXT_RE, "");
5531
- for (const ext of [
5532
- ".mjs",
5533
- ".js",
5534
- ".cjs"
5535
- ]) {
5536
- const candidate = join(dir, `${normalizedBaseName}${ext}`);
5537
- if (await fileExists$1(candidate)) return candidate;
5538
- }
5539
- }
5540
- /**
5541
- * Resolve a dist/built path back to its source .ts/.tsx equivalent.
5542
- * E.g., "./dist/index.mjs" → "src/index.ts"
5543
- */
5544
- async function resolveSourceEntry(pluginDir, distPath) {
5545
- const cleaned = distPath.replace(LEADING_DOT_SLASH_RE, "");
5546
- const srcPath = cleaned.replace(DIST_PREFIX_RE, "src/").replace(MJS_EXT_RE, ".ts");
5547
- const srcFull = resolve(pluginDir, srcPath);
5548
- if (await fileExists$1(srcFull)) return srcFull;
5549
- const tsxFull = resolve(pluginDir, srcPath.replace(TS_TO_TSX_RE, ".tsx"));
5550
- if (await fileExists$1(tsxFull)) return tsxFull;
5551
- const direct = resolve(pluginDir, cleaned);
5552
- if (await fileExists$1(direct)) return direct;
5553
- }
5554
- const TS_SOURCE_EXPORT_RE = /\.(?:ts|tsx|mts|cts|jsx)$/;
5555
- /**
5556
- * Find package.json exports that point to source files instead of built output.
5557
- * Returns an array of `{ exportPath, resolvedPath }` for each offending export.
5558
- */
5559
- function findSourceExports(exports) {
5560
- const issues = [];
5561
- for (const [exportPath, exportValue] of Object.entries(exports)) {
5562
- const resolved = typeof exportValue === "string" ? exportValue : exportValue && typeof exportValue === "object" && "import" in exportValue ? exportValue.import : null;
5563
- if (resolved && TS_SOURCE_EXPORT_RE.test(resolved)) issues.push({
5564
- exportPath,
5565
- resolvedPath: resolved
5566
- });
5567
- }
5568
- return issues;
5569
- }
5570
- /**
5571
- * Recursively walk a staging directory and return all regular files with
5572
- * tarball-relative names.
5573
- */
5574
- async function collectBundleEntries(dir) {
5575
- const entries = [];
5576
- await walkBundle(dir, "", entries);
5577
- return entries;
5578
- }
5579
- async function walkBundle(dir, prefix, into) {
5580
- const items = await readdir(dir, { withFileTypes: true });
5581
- for (const item of items) {
5582
- const fullPath = join(dir, item.name);
5583
- const relPath = prefix ? `${prefix}/${item.name}` : item.name;
5584
- if (item.isFile()) {
5585
- const s = await stat(fullPath);
5586
- into.push({
5587
- name: relPath,
5588
- bytes: s.size
5589
- });
5590
- } else if (item.isDirectory()) await walkBundle(fullPath, relPath, into);
5591
- }
5592
- }
5593
- function totalBundleBytes(entries) {
5594
- let total = 0;
5595
- for (const entry of entries) total += entry.bytes;
5596
- return total;
5597
- }
5598
- /**
5599
- * Enforce sandboxed plugin caps against decompressed bundle files:
5600
- * total size, per-file size, and file count.
5601
- */
5602
- function validateBundleSize(entries) {
5603
- const violations = [];
5604
- const total = totalBundleBytes(entries);
5605
- if (total > MAX_BUNDLE_SIZE) violations.push(`Bundle size ${formatBytes(total)} exceeds maximum of ${formatBytes(MAX_BUNDLE_SIZE)}.`);
5606
- if (entries.length > MAX_FILE_COUNT) violations.push(`Bundle contains ${entries.length} files, exceeds maximum of ${MAX_FILE_COUNT}.`);
5607
- const oversized = entries.filter((entry) => entry.bytes > MAX_FILE_SIZE).toSorted((a, b) => a.name.localeCompare(b.name));
5608
- for (const entry of oversized) violations.push(`File ${entry.name} is ${formatBytes(entry.bytes)}, exceeds per-file maximum of ${formatBytes(MAX_FILE_SIZE)}.`);
5609
- return violations;
5610
- }
5611
- function formatBytes(bytes) {
5612
- if (bytes < 1024) return `${bytes} B`;
5613
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
5614
- return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
5615
- }
5616
- /**
5617
- * Create a gzipped tarball from a directory.
5618
- */
5619
- async function createTarball(sourceDir, outputPath) {
5620
- const { createGzip } = await import("node:zlib");
5621
- await pipeline(packTar(sourceDir), createGzip({ level: 9 }), createWriteStream(outputPath));
5622
- }
5623
-
5624
- //#endregion
5625
- //#region src/cli/commands/bundle.ts
5626
- /**
5627
- * dineway plugin bundle
5628
- *
5629
- * Produces a publishable plugin tarball from a plugin source directory.
5630
- *
5631
- * Steps:
5632
- * 1. Resolve plugin entrypoint (finds definePlugin() export)
5633
- * 2. Bundle backend code with tsdown → backend.js (single ES module, tree-shaken)
5634
- * 3. Bundle admin code if present → admin.js
5635
- * 4. Extract manifest from definePlugin() → manifest.json
5636
- * 5. Collect assets (README.md, icon.png, screenshots/)
5637
- * 6. Validate bundle (manifest schema, size limits, no Node.js builtins)
5638
- * 7. Create tarball ({id}-{version}.tar.gz)
5639
- */
5640
- var bundle_exports = /* @__PURE__ */ __exportAll({ bundleCommand: () => bundleCommand });
5641
- const TS_EXT_RE = /\.tsx?$/;
5642
- const SLASH_RE = /\//g;
5643
- const LEADING_AT_RE = /^@/;
5644
- const DINEWAY_SCOPE_RE = /^@dineway-ai\//;
5645
- const bundleCommand = defineCommand({
5646
- meta: {
5647
- name: "bundle",
5648
- description: "Bundle a plugin for marketplace distribution"
5649
- },
5650
- args: {
5651
- dir: {
5652
- type: "string",
5653
- description: "Plugin directory (default: current directory)",
5654
- default: process.cwd()
5655
- },
5656
- outDir: {
5657
- type: "string",
5658
- alias: "o",
5659
- description: "Output directory for the tarball (default: ./dist)",
5660
- default: "dist"
5661
- },
5662
- validateOnly: {
5663
- type: "boolean",
5664
- description: "Run validation only, skip tarball creation",
5665
- default: false
5666
- }
5667
- },
5668
- async run({ args }) {
5669
- const pluginDir = resolve(args.dir);
5670
- const outDir = resolve(pluginDir, args.outDir);
5671
- const validateOnly = args.validateOnly;
5672
- consola.start(validateOnly ? "Validating plugin..." : "Bundling plugin...");
5673
- const pkgPath = join(pluginDir, "package.json");
5674
- if (!await fileExists$1(pkgPath)) {
5675
- consola.error("No package.json found in", pluginDir);
5676
- process.exit(1);
5677
- }
5678
- const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
5679
- let backendEntry;
5680
- let adminEntry;
5681
- if (pkg.exports) {
5682
- const sandboxExport = pkg.exports["./sandbox"];
5683
- if (typeof sandboxExport === "string") backendEntry = await resolveSourceEntry(pluginDir, sandboxExport);
5684
- else if (sandboxExport && typeof sandboxExport === "object" && "import" in sandboxExport) backendEntry = await resolveSourceEntry(pluginDir, sandboxExport.import);
5685
- const adminExport = pkg.exports["./admin"];
5686
- if (typeof adminExport === "string") adminEntry = await resolveSourceEntry(pluginDir, adminExport);
5687
- else if (adminExport && typeof adminExport === "object" && "import" in adminExport) adminEntry = await resolveSourceEntry(pluginDir, adminExport.import);
5688
- }
5689
- if (!backendEntry) {
5690
- const defaultSandbox = join(pluginDir, "src/sandbox-entry.ts");
5691
- if (await fileExists$1(defaultSandbox)) backendEntry = defaultSandbox;
5692
- }
5693
- let mainEntry;
5694
- if (pkg.exports?.["."] !== void 0) {
5695
- const mainExport = pkg.exports["."];
5696
- if (typeof mainExport === "string") mainEntry = await resolveSourceEntry(pluginDir, mainExport);
5697
- else if (mainExport && typeof mainExport === "object" && "import" in mainExport) mainEntry = await resolveSourceEntry(pluginDir, mainExport.import);
5698
- }
5699
- if (!mainEntry && pkg.main) mainEntry = await resolveSourceEntry(pluginDir, pkg.main);
5700
- if (!mainEntry) {
5701
- const defaultMain = join(pluginDir, "src/index.ts");
5702
- if (await fileExists$1(defaultMain)) mainEntry = defaultMain;
5703
- }
5704
- if (!mainEntry) {
5705
- consola.error("Cannot find plugin entrypoint. Expected src/index.ts or main/exports in package.json");
5706
- process.exit(1);
5707
- }
5708
- consola.info(`Main entry: ${mainEntry}`);
5709
- if (backendEntry) consola.info(`Backend entry: ${backendEntry}`);
5710
- if (adminEntry) consola.info(`Admin entry: ${adminEntry}`);
5711
- consola.start("Extracting plugin manifest...");
5712
- const { build } = await import("tsdown");
5713
- const tmpDir = join(pluginDir, ".dineway-bundle-tmp");
5714
- try {
5715
- await mkdir(tmpDir, { recursive: true });
5716
- await copyFile(pkgPath, join(tmpDir, "package.json"));
5717
- const mainOutDir = join(tmpDir, "main");
5718
- await build({
5719
- config: false,
5720
- entry: [mainEntry],
5721
- format: "esm",
5722
- outDir: mainOutDir,
5723
- dts: false,
5724
- platform: "node",
5725
- external: ["dineway", DINEWAY_SCOPE_RE]
5726
- });
5727
- const pluginNodeModules = join(pluginDir, "node_modules");
5728
- const tmpNodeModules = join(mainOutDir, "node_modules");
5729
- if (await fileExists$1(pluginNodeModules)) await symlink(pluginNodeModules, tmpNodeModules, "junction");
5730
- const mainOutputPath = await findBuildOutput(mainOutDir, basename(mainEntry).replace(TS_EXT_RE, ""));
5731
- if (!mainOutputPath) {
5732
- consola.error("Failed to build main entry — no output found in", mainOutDir);
5733
- process.exit(1);
5734
- }
5735
- const pluginModule = await import(toFileImportSpecifier(mainOutputPath));
5736
- let resolvedPlugin;
5737
- if (typeof pluginModule.createPlugin === "function") resolvedPlugin = pluginModule.createPlugin();
5738
- else if (typeof pluginModule.default === "function") resolvedPlugin = pluginModule.default();
5739
- else if (typeof pluginModule.default === "object" && pluginModule.default !== null) {
5740
- const defaultExport = pluginModule.default;
5741
- if ("id" in defaultExport && "version" in defaultExport) resolvedPlugin = defaultExport;
5742
- }
5743
- if (!resolvedPlugin) for (const [key, value] of Object.entries(pluginModule)) {
5744
- if (key === "default" || typeof value !== "function") continue;
5745
- try {
5746
- const result = value();
5747
- if (result && typeof result === "object" && "id" in result && "version" in result) {
5748
- resolvedPlugin = {
5749
- id: result.id,
5750
- version: result.version,
5751
- capabilities: result.capabilities ?? [],
5752
- allowedHosts: result.allowedHosts ?? [],
5753
- storage: result.storage ?? {},
5754
- hooks: {},
5755
- routes: {},
5756
- admin: {
5757
- pages: result.adminPages,
5758
- widgets: result.adminWidgets
5759
- }
5760
- };
5761
- if (backendEntry) {
5762
- const backendProbeDir = join(tmpDir, "backend-probe");
5763
- const probeShimDir = join(tmpDir, "probe-shims");
5764
- await mkdir(probeShimDir, { recursive: true });
5765
- await writeFile(join(probeShimDir, "dineway.mjs"), "export const definePlugin = (d) => d;\n");
5766
- await build({
5767
- config: false,
5768
- entry: [backendEntry],
5769
- format: "esm",
5770
- outDir: backendProbeDir,
5771
- dts: false,
5772
- platform: "neutral",
5773
- external: [],
5774
- alias: { dineway: join(probeShimDir, "dineway.mjs") },
5775
- treeshake: true
5776
- });
5777
- const backendProbePath = await findBuildOutput(backendProbeDir, basename(backendEntry).replace(TS_EXT_RE, ""));
5778
- if (backendProbePath) {
5779
- const standardDef = (await import(toFileImportSpecifier(backendProbePath))).default ?? {};
5780
- const hooks = standardDef.hooks;
5781
- const routes = standardDef.routes;
5782
- if (hooks) for (const hookName of Object.keys(hooks)) {
5783
- const hookEntry = hooks[hookName];
5784
- const isConfig = typeof hookEntry === "object" && hookEntry !== null && "handler" in hookEntry;
5785
- const config = isConfig ? hookEntry : {};
5786
- resolvedPlugin.hooks[hookName] = {
5787
- handler: isConfig ? hookEntry.handler : hookEntry,
5788
- priority: config.priority ?? 100,
5789
- timeout: config.timeout ?? 5e3,
5790
- dependencies: config.dependencies ?? [],
5791
- errorPolicy: config.errorPolicy ?? "abort",
5792
- exclusive: config.exclusive ?? false,
5793
- pluginId: result.id
5794
- };
5795
- }
5796
- if (routes) for (const [name, route] of Object.entries(routes)) {
5797
- const routeObj = route;
5798
- resolvedPlugin.routes[name] = {
5799
- handler: routeObj.handler,
5800
- public: routeObj.public
5801
- };
5802
- }
5803
- }
5804
- }
5805
- break;
5806
- }
5807
- } catch {}
5808
- }
5809
- if (!resolvedPlugin?.id || !resolvedPlugin?.version) {
5810
- consola.error("Could not extract plugin definition. Expected one of:\n - createPlugin() export (native format)\n - Descriptor factory function returning { id, version, ... } (standard format)");
5811
- process.exit(1);
5812
- }
5813
- const manifest = extractManifest(resolvedPlugin);
5814
- if (resolvedPlugin.admin?.entry) {
5815
- consola.error("Plugin declares adminEntry — React admin components require native/trusted mode. Use Block Kit for sandboxed admin pages, or remove adminEntry.");
5816
- process.exit(1);
5817
- }
5818
- if (resolvedPlugin.admin?.portableTextBlocks && resolvedPlugin.admin.portableTextBlocks.length > 0) {
5819
- consola.error("Plugin declares portableTextBlocks — these require native/trusted mode and cannot be bundled for the marketplace.");
5820
- process.exit(1);
5821
- }
5822
- consola.success(`Plugin: ${manifest.id}@${manifest.version}`);
5823
- consola.info(` Capabilities: ${manifest.capabilities.length > 0 ? manifest.capabilities.join(", ") : "(none)"}`);
5824
- consola.info(` Hooks: ${manifest.hooks.length > 0 ? manifest.hooks.map((h) => typeof h === "string" ? h : h.name).join(", ") : "(none)"}`);
5825
- consola.info(` Routes: ${manifest.routes.length > 0 ? manifest.routes.map((r) => typeof r === "string" ? r : r.name).join(", ") : "(none)"}`);
5826
- const bundleDir = join(tmpDir, "bundle");
5827
- await mkdir(bundleDir, { recursive: true });
5828
- if (backendEntry) {
5829
- consola.start("Bundling backend...");
5830
- const shimDir = join(tmpDir, "shims");
5831
- await mkdir(shimDir, { recursive: true });
5832
- await writeFile(join(shimDir, "dineway.mjs"), "export const definePlugin = (d) => d;\n");
5833
- await build({
5834
- config: false,
5835
- entry: [backendEntry],
5836
- format: "esm",
5837
- outDir: join(tmpDir, "backend"),
5838
- dts: false,
5839
- platform: "neutral",
5840
- external: [],
5841
- alias: { dineway: join(shimDir, "dineway.mjs") },
5842
- minify: true,
5843
- treeshake: true
5844
- });
5845
- const backendBaseName = basename(backendEntry).replace(TS_EXT_RE, "");
5846
- const backendOutputPath = await findBuildOutput(join(tmpDir, "backend"), backendBaseName);
5847
- if (backendOutputPath) {
5848
- await copyFile(backendOutputPath, join(bundleDir, "backend.js"));
5849
- consola.success("Built backend.js");
5850
- } else {
5851
- consola.error("Backend build produced no output");
5852
- process.exit(1);
5853
- }
5854
- } else {
5855
- consola.warn("No sandbox entry found — bundle will have no backend.js");
5856
- consola.warn(" Add a \"sandbox-entry.ts\" in src/ or a \"./sandbox\" export in package.json");
5857
- }
5858
- if (adminEntry) {
5859
- consola.start("Bundling admin...");
5860
- await build({
5861
- config: false,
5862
- entry: [adminEntry],
5863
- format: "esm",
5864
- outDir: join(tmpDir, "admin"),
5865
- dts: false,
5866
- platform: "neutral",
5867
- external: [],
5868
- minify: true,
5869
- treeshake: true
5870
- });
5871
- const adminBaseName = basename(adminEntry).replace(TS_EXT_RE, "");
5872
- const adminOutputPath = await findBuildOutput(join(tmpDir, "admin"), adminBaseName);
5873
- if (adminOutputPath) {
5874
- await copyFile(adminOutputPath, join(bundleDir, "admin.js"));
5875
- consola.success("Built admin.js");
5876
- }
5877
- }
5878
- await writeFile(join(bundleDir, "manifest.json"), JSON.stringify(manifest, null, 2));
5879
- consola.start("Collecting assets...");
5880
- const readmePath = join(pluginDir, "README.md");
5881
- if (await fileExists$1(readmePath)) {
5882
- await copyFile(readmePath, join(bundleDir, "README.md"));
5883
- consola.success("Included README.md");
5884
- }
5885
- const iconPath = join(pluginDir, "icon.png");
5886
- if (await fileExists$1(iconPath)) {
5887
- const dims = readImageDimensions(await readFile(iconPath));
5888
- if (!dims) consola.warn("icon.png is not a valid PNG — skipping");
5889
- else if (dims[0] !== ICON_SIZE || dims[1] !== ICON_SIZE) {
5890
- consola.warn(`icon.png is ${dims[0]}x${dims[1]}, expected ${ICON_SIZE}x${ICON_SIZE} — including anyway`);
5891
- await copyFile(iconPath, join(bundleDir, "icon.png"));
5892
- } else {
5893
- await copyFile(iconPath, join(bundleDir, "icon.png"));
5894
- consola.success("Included icon.png");
5895
- }
5896
- }
5897
- const screenshotsDir = join(pluginDir, "screenshots");
5898
- if (await fileExists$1(screenshotsDir)) {
5899
- const screenshotFiles = (await readdir(screenshotsDir)).filter((f) => {
5900
- const ext = extname(f).toLowerCase();
5901
- return ext === ".png" || ext === ".jpg" || ext === ".jpeg";
5902
- }).toSorted().slice(0, MAX_SCREENSHOTS);
5903
- if (screenshotFiles.length > 0) {
5904
- await mkdir(join(bundleDir, "screenshots"), { recursive: true });
5905
- for (const file of screenshotFiles) {
5906
- const filePath = join(screenshotsDir, file);
5907
- const dims = readImageDimensions(await readFile(filePath));
5908
- if (!dims) {
5909
- consola.warn(`screenshots/${file} — cannot read dimensions, skipping`);
5910
- continue;
5911
- }
5912
- if (dims[0] > MAX_SCREENSHOT_WIDTH || dims[1] > MAX_SCREENSHOT_HEIGHT) consola.warn(`screenshots/${file} is ${dims[0]}x${dims[1]}, max ${MAX_SCREENSHOT_WIDTH}x${MAX_SCREENSHOT_HEIGHT} — including anyway`);
5913
- await copyFile(filePath, join(bundleDir, "screenshots", file));
5914
- }
5915
- consola.success(`Included ${screenshotFiles.length} screenshot(s)`);
5916
- }
5917
- }
5918
- consola.start("Validating bundle...");
5919
- let hasErrors = false;
5920
- if (pkg.exports) for (const issue of findSourceExports(pkg.exports)) {
5921
- consola.error(`Export "${issue.exportPath}" points to source (${issue.resolvedPath}). Package exports must point to built files (e.g. dist/*.mjs). Add a build step and update the exports map.`);
5922
- hasErrors = true;
5923
- }
5924
- const backendPath = join(bundleDir, "backend.js");
5925
- if (await fileExists$1(backendPath)) {
5926
- const builtins = findNodeBuiltinImports(await readFile(backendPath, "utf-8"));
5927
- if (builtins.length > 0) {
5928
- consola.error(`backend.js imports Node.js built-in modules: ${builtins.join(", ")}`);
5929
- consola.error("Sandboxed plugins cannot use Node.js APIs");
5930
- hasErrors = true;
5931
- }
5932
- }
5933
- if (manifest.capabilities.includes("network:request:unrestricted")) consola.warn("Plugin declares unrestricted network access (network:request:unrestricted) — it can make requests to any host");
5934
- else if (manifest.capabilities.includes("network:request") && manifest.allowedHosts.length === 0) consola.warn("Plugin declares network:request capability but no allowedHosts — all fetch requests will be blocked");
5935
- if (resolvedPlugin.admin?.portableTextBlocks && resolvedPlugin.admin.portableTextBlocks.length > 0) consola.warn("Plugin declares portableTextBlocks — these require trusted mode and will be ignored in sandboxed plugins");
5936
- if (resolvedPlugin.admin?.entry) consola.warn("Plugin declares admin.entry — custom React components require trusted mode. Use Block Kit for sandboxed admin pages");
5937
- if (resolvedPlugin.hooks["page:fragments"]) consola.warn("Plugin declares page:fragments hook — this is trusted-only and will not work in sandboxed mode");
5938
- const hasAdminPages = (manifest.admin?.pages?.length ?? 0) > 0;
5939
- const hasAdminWidgets = (manifest.admin?.widgets?.length ?? 0) > 0;
5940
- if (hasAdminPages || hasAdminWidgets) {
5941
- if (!manifest.routes.map((r) => typeof r === "string" ? r : r.name).includes("admin")) {
5942
- consola.error(`Plugin declares ${hasAdminPages ? "adminPages" : ""}${hasAdminPages && hasAdminWidgets ? " and " : ""}${hasAdminWidgets ? "adminWidgets" : ""} but the sandbox entry has no "admin" route. Add an admin route handler to serve Block Kit pages.`);
5943
- hasErrors = true;
5944
- }
5945
- }
5946
- const bundleEntries = await collectBundleEntries(bundleDir);
5947
- const sizeViolations = validateBundleSize(bundleEntries);
5948
- if (sizeViolations.length > 0) {
5949
- for (const violation of sizeViolations) consola.error(violation);
5950
- hasErrors = true;
5951
- } else consola.info(`Bundle size: ${formatBytes(totalBundleBytes(bundleEntries))} across ${bundleEntries.length} file${bundleEntries.length === 1 ? "" : "s"}`);
5952
- if (hasErrors) {
5953
- consola.error("Bundle validation failed");
5954
- process.exit(1);
5955
- }
5956
- consola.success("Validation passed");
5957
- if (validateOnly) return;
5958
- await mkdir(outDir, { recursive: true });
5959
- const tarballName = `${manifest.id.replace(SLASH_RE, "-").replace(LEADING_AT_RE, "")}-${manifest.version}.tar.gz`;
5960
- const tarballPath = join(outDir, tarballName);
5961
- consola.start("Creating tarball...");
5962
- await createTarball(bundleDir, tarballPath);
5963
- const tarballSizeKB = ((await stat(tarballPath)).size / 1024).toFixed(1);
5964
- const tarballBuf = await readFile(tarballPath);
5965
- const checksum = createHash("sha256").update(tarballBuf).digest("hex");
5966
- consola.success(`Created ${tarballName} (${tarballSizeKB}KB)`);
5967
- consola.info(` SHA-256: ${checksum}`);
5968
- consola.info(` Path: ${tarballPath}`);
5969
- } finally {
5970
- if (tmpDir.endsWith(".dineway-bundle-tmp")) await rm(tmpDir, {
5971
- recursive: true,
5972
- force: true
5973
- });
5974
- }
5975
- }
5976
- });
5977
-
5978
- //#endregion
5979
- //#region src/cli/commands/plugin-init.ts
5980
- /**
5981
- * dineway plugin init
5982
- *
5983
- * Scaffold a new Dineway plugin. Generates the sandboxed-format boilerplate:
5984
- * src/index.ts -- descriptor factory
5985
- * src/sandbox-entry.ts -- definePlugin({ hooks, routes })
5986
- * package.json
5987
- * tsconfig.json
5988
- *
5989
- * Use --format=native (or --native) to generate native-format boilerplate
5990
- * instead (createPlugin + React admin). When neither is passed and stdout
5991
- * is a TTY, the user is prompted to choose.
4914
+ * Scaffold a new Dineway plugin. Generates the sandboxed-format boilerplate:
4915
+ * src/index.ts -- descriptor factory
4916
+ * src/sandbox-entry.ts -- definePlugin({ hooks, routes })
4917
+ * package.json
4918
+ * tsconfig.json
4919
+ *
4920
+ * Use --format=native (or --native) to generate native-format boilerplate
4921
+ * instead (createPlugin + React admin). When neither is passed and stdout
4922
+ * is a TTY, the user is prompted to choose.
5992
4923
  */
5993
4924
  const SLUG_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
5994
4925
  const SCOPE_RE = /^@[^/]+\//;
@@ -6050,7 +4981,7 @@ const pluginInitCommand = defineCommand({
6050
4981
  process.exit(1);
6051
4982
  }
6052
4983
  const srcDir = join(targetDir, "src");
6053
- if (await fileExists$1(join(targetDir, "package.json"))) {
4984
+ if (await fileExists$11(join(targetDir, "package.json"))) {
6054
4985
  consola.error(`package.json already exists in ${targetDir}`);
6055
4986
  process.exit(1);
6056
4987
  }
@@ -6416,6 +5347,7 @@ async function pollGitHubDeviceFlow(tokenEndpoint, clientId, deviceCode, interva
6416
5347
  const manifestSummarySchema = pluginManifestSchema.pick({
6417
5348
  id: true,
6418
5349
  version: true,
5350
+ declaredAccess: true,
6419
5351
  capabilities: true,
6420
5352
  allowedHosts: true
6421
5353
  });
@@ -6433,7 +5365,20 @@ async function readManifestFromTarball(tarballPath) {
6433
5365
  const parsed = JSON.parse(content);
6434
5366
  const result = manifestSummarySchema.safeParse(parsed);
6435
5367
  if (!result.success) throw new Error(`Invalid manifest.json: ${result.error.message}`);
6436
- return result.data;
5368
+ const reconciled = reconcileManifestAccess({
5369
+ ...result.data,
5370
+ storage: {},
5371
+ hooks: [],
5372
+ routes: [],
5373
+ admin: {}
5374
+ });
5375
+ return {
5376
+ id: reconciled.id,
5377
+ version: reconciled.version,
5378
+ declaredAccess: reconciled.declaredAccess,
5379
+ capabilities: reconciled.capabilities,
5380
+ allowedHosts: reconciled.allowedHosts
5381
+ };
6437
5382
  }
6438
5383
  const POLL_INTERVAL_MS = 3e3;
6439
5384
  const POLL_TIMEOUT_MS = 12e4;
@@ -6518,7 +5463,7 @@ const publishCommand = defineCommand({
6518
5463
  const pluginDir = resolve(args.dir);
6519
5464
  try {
6520
5465
  const { runCommand } = await import("citty");
6521
- const { bundleCommand } = await Promise.resolve().then(() => bundle_exports);
5466
+ const { bundleCommand } = await import("../bundle-CEcdUa1s.mjs").then((n) => n.n);
6522
5467
  await runCommand(bundleCommand, { rawArgs: ["--dir", pluginDir] });
6523
5468
  } catch {
6524
5469
  consola.error("Build failed");
@@ -7182,7 +6127,7 @@ const seedCommand = defineCommand({
7182
6127
  },
7183
6128
  "no-content": {
7184
6129
  type: "boolean",
7185
- description: "Skip sample content",
6130
+ description: "Skip sample data (content entries, bylines, taxonomy terms)",
7186
6131
  default: false
7187
6132
  },
7188
6133
  "on-conflict": {
@@ -7288,141 +6233,714 @@ const seedCommand = defineCommand({
7288
6233
  });
7289
6234
 
7290
6235
  //#endregion
7291
- //#region src/cli/forgeway-seo-client.ts
7292
- const DEFAULT_DINEWAY_API_BASE_URL = "https://api.dineway.ai";
7293
- const TRAILING_SLASH_PATTERN = /\/$/;
7294
- const SHADOW_EMAIL_PATTERN = /^shadow_[a-f0-9]{16}@dineway\.ai$/i;
7295
- var ForgewaySeoApiError = class extends Error {
7296
- constructor(message, status, body) {
7297
- super(message);
7298
- this.status = status;
7299
- this.body = body;
7300
- this.name = "ForgewaySeoApiError";
6236
+ //#region src/seo/local-crawl.ts
6237
+ const CONTRACT_VERSION = 1;
6238
+ const USER_AGENT = "DinewayContentCrawler/1.0";
6239
+ const DEFAULT_PAGE_LIMIT = 100;
6240
+ const MAX_PAGE_LIMIT = 500;
6241
+ const DEFAULT_MAX_DEPTH = 3;
6242
+ const MAX_DEPTH = 10;
6243
+ const DEFAULT_CONCURRENCY = 4;
6244
+ const MAX_CONCURRENCY = 10;
6245
+ const DEFAULT_TIMEOUT_MS = 1e4;
6246
+ const MAX_TIMEOUT_MS = 6e4;
6247
+ const DEFAULT_MAX_BYTES = 1e6;
6248
+ const MAX_RESOURCE_BYTES = 1e7;
6249
+ const MAX_SITEMAPS = 20;
6250
+ const MAX_REDIRECTS = 5;
6251
+ const MAX_TEXT_EXCERPT = 2e4;
6252
+ const LINE_SPLIT_RE = /\r?\n/;
6253
+ const COMMENT_RE = /#.*$/;
6254
+ const WHITESPACE_RE = /\s+/g;
6255
+ const WORD_RE = /[\p{L}\p{N}]+(?:['’-][\p{L}\p{N}]+)*/gu;
6256
+ const REGEXP_SPECIAL_RE = /[.*+?^${}()|[\]\\]/g;
6257
+ async function crawlLocalSite(options) {
6258
+ const normalized = normalizeOptions(options);
6259
+ if (!normalized.allowPrivateNetwork) await resolveAndValidateExternalUrl(normalized.startUrl.href, { resolver: normalized.dnsResolver });
6260
+ const origin = normalized.startUrl.origin;
6261
+ const skipped = /* @__PURE__ */ new Map();
6262
+ const robots = await loadRobots(normalized);
6263
+ const sitemapResult = normalized.sitemap === "skip" ? {
6264
+ sources: [],
6265
+ urls: []
6266
+ } : await loadSitemaps(normalized, robots.sitemaps);
6267
+ const queue = [];
6268
+ const queued = /* @__PURE__ */ new Set();
6269
+ const enqueue = (candidate, depth, discoveredFrom) => {
6270
+ const url = normalizeUrl(candidate, normalized.startUrl, normalized.keepQuery);
6271
+ if (!url || url.origin !== origin || queued.has(url.href)) return;
6272
+ const pathDecision = pathEligibility(url, normalized);
6273
+ if (pathDecision) {
6274
+ skipped.set(url.href, {
6275
+ url: url.href,
6276
+ reason: pathDecision
6277
+ });
6278
+ return;
6279
+ }
6280
+ if (!robotsAllows(url, robots.rules)) {
6281
+ skipped.set(url.href, {
6282
+ url: url.href,
6283
+ reason: "robots_disallowed"
6284
+ });
6285
+ return;
6286
+ }
6287
+ queued.add(url.href);
6288
+ queue.push({
6289
+ url: url.href,
6290
+ depth,
6291
+ discoveredFrom
6292
+ });
6293
+ };
6294
+ if (normalized.sitemap !== "only") enqueue(normalized.startUrl.href, 0, "start");
6295
+ for (const url of sitemapResult.urls) enqueue(url, 0, "sitemap");
6296
+ const pages = [];
6297
+ let cursor = 0;
6298
+ while (cursor < queue.length && pages.length < normalized.pageLimit) {
6299
+ const remaining = normalized.pageLimit - pages.length;
6300
+ const batch = queue.slice(cursor, cursor + Math.min(normalized.concurrency, remaining));
6301
+ cursor += batch.length;
6302
+ const resources = await Promise.all(batch.map(async (item) => ({
6303
+ item,
6304
+ resource: await fetchResource(item.url, normalized)
6305
+ })));
6306
+ for (const { item, resource } of resources) {
6307
+ const page = pageFromResource(item, resource, normalized);
6308
+ pages.push(page);
6309
+ if (normalized.sitemap !== "only" && !page.error && page.contentType?.includes("text/html") && item.depth < normalized.maxDepth) for (const link of page.internalLinks) enqueue(link.url, item.depth + 1, item.url);
6310
+ }
6311
+ }
6312
+ const edges = pages.flatMap((page) => page.internalLinks.map((link) => ({
6313
+ from: page.url,
6314
+ ...link
6315
+ })));
6316
+ const brokenInternalUrls = uniqueSorted(pages.filter((page) => page.httpStatus !== null && page.httpStatus >= 400).filter((page) => edges.some((edge) => edge.url === page.url)).map((page) => page.url));
6317
+ const incoming = new Set(edges.map((edge) => edge.url));
6318
+ const orphanUrls = uniqueSorted(pages.filter((page) => page.url !== normalized.startUrl.href && page.httpStatus !== null && page.httpStatus < 400 && page.contentType?.includes("text/html") && !incoming.has(page.url)).map((page) => page.url));
6319
+ const successfulHtml = pages.filter((page) => page.httpStatus !== null && page.httpStatus < 400 && page.contentType?.includes("text/html"));
6320
+ return {
6321
+ contractVersion: CONTRACT_VERSION,
6322
+ startUrl: normalized.startUrl.href,
6323
+ origin,
6324
+ crawledAt: normalized.now().toISOString(),
6325
+ options: {
6326
+ pageLimit: normalized.pageLimit,
6327
+ maxDepth: normalized.maxDepth,
6328
+ concurrency: normalized.concurrency,
6329
+ include: normalized.includeValues,
6330
+ exclude: normalized.excludeValues,
6331
+ timeoutMs: normalized.timeoutMs,
6332
+ maxBytesPerResource: normalized.maxBytesPerResource,
6333
+ sitemap: normalized.sitemap,
6334
+ keepQuery: normalized.keepQuery,
6335
+ allowPrivateNetwork: normalized.allowPrivateNetwork
6336
+ },
6337
+ sources: {
6338
+ robots: robots.source,
6339
+ sitemaps: sitemapResult.sources
6340
+ },
6341
+ pages,
6342
+ skipped: [...skipped.values()].toSorted((left, right) => left.url.localeCompare(right.url)),
6343
+ linkGraph: {
6344
+ edges,
6345
+ orphanUrls,
6346
+ brokenInternalUrls,
6347
+ duplicateTitles: duplicateGroups(successfulHtml, "title"),
6348
+ duplicateDescriptions: duplicateGroups(successfulHtml, "description"),
6349
+ duplicateContent: duplicateGroups(successfulHtml, "contentSha256")
6350
+ },
6351
+ summary: {
6352
+ fetchedPages: pages.length,
6353
+ htmlPages: pages.filter((page) => page.contentType?.includes("text/html")).length,
6354
+ brokenPages: pages.filter((page) => page.httpStatus !== null && page.httpStatus >= 400).length,
6355
+ orphanPages: orphanUrls.length,
6356
+ skippedUrls: skipped.size,
6357
+ errorPages: pages.filter((page) => page.error).length,
6358
+ pageLimitReached: pages.length === normalized.pageLimit && cursor < queue.length
6359
+ }
6360
+ };
6361
+ }
6362
+ function normalizeOptions(options) {
6363
+ const startUrl = normalizeUrl(options.startUrl, void 0, options.keepQuery === true);
6364
+ if (!startUrl) throw new Error("startUrl must be an absolute HTTP or HTTPS URL without credentials");
6365
+ const pageLimit = boundedInteger(options.pageLimit, DEFAULT_PAGE_LIMIT, 1, MAX_PAGE_LIMIT, "pageLimit");
6366
+ const maxDepth = boundedInteger(options.maxDepth, DEFAULT_MAX_DEPTH, 0, MAX_DEPTH, "maxDepth");
6367
+ const concurrency = boundedInteger(options.concurrency, DEFAULT_CONCURRENCY, 1, MAX_CONCURRENCY, "concurrency");
6368
+ const timeoutMs = boundedInteger(options.timeoutMs, DEFAULT_TIMEOUT_MS, 100, MAX_TIMEOUT_MS, "timeoutMs");
6369
+ const maxBytesPerResource = boundedInteger(options.maxBytesPerResource, DEFAULT_MAX_BYTES, 1024, MAX_RESOURCE_BYTES, "maxBytesPerResource");
6370
+ const sitemap = options.sitemap ?? "include";
6371
+ if (![
6372
+ "include",
6373
+ "skip",
6374
+ "only"
6375
+ ].includes(sitemap)) throw new Error("sitemap must be include, skip, or only");
6376
+ const includeValues = normalizePatterns(options.include ?? []);
6377
+ const excludeValues = normalizePatterns(options.exclude ?? []);
6378
+ return {
6379
+ startUrl,
6380
+ pageLimit,
6381
+ maxDepth,
6382
+ concurrency,
6383
+ include: includeValues.map(pathMatcher),
6384
+ exclude: excludeValues.map(pathMatcher),
6385
+ includeValues,
6386
+ excludeValues,
6387
+ timeoutMs,
6388
+ maxBytesPerResource,
6389
+ sitemap,
6390
+ keepQuery: options.keepQuery === true,
6391
+ allowPrivateNetwork: options.allowPrivateNetwork === true,
6392
+ dnsResolver: options.dnsResolver,
6393
+ fetch: options.fetch ?? globalThis.fetch,
6394
+ now: options.now ?? (() => /* @__PURE__ */ new Date())
6395
+ };
6396
+ }
6397
+ function boundedInteger(value, fallback, minimum, maximum, name) {
6398
+ const normalized = value ?? fallback;
6399
+ if (!Number.isInteger(normalized) || normalized < minimum || normalized > maximum) throw new Error(`${name} must be an integer from ${minimum} to ${maximum}`);
6400
+ return normalized;
6401
+ }
6402
+ function normalizePatterns(values) {
6403
+ return uniqueSorted(values.map((value) => value.trim()).filter((value) => {
6404
+ if (!value.startsWith("/") || value.length > 255) throw new Error("include and exclude patterns must be path globs starting with /");
6405
+ return true;
6406
+ }));
6407
+ }
6408
+ function pathMatcher(value) {
6409
+ let source = "^";
6410
+ for (let index = 0; index < value.length; index += 1) {
6411
+ const character = value[index];
6412
+ if (character === "*" && value[index + 1] === "*") {
6413
+ source += ".*";
6414
+ index += 1;
6415
+ } else if (character === "*") source += "[^/]*";
6416
+ else source += escapeRegExp(character ?? "");
7301
6417
  }
7302
- };
7303
- function normalizeApiUrl(value) {
7304
- return (value || DEFAULT_DINEWAY_API_BASE_URL).replace(TRAILING_SLASH_PATTERN, "");
6418
+ return {
6419
+ value,
6420
+ pattern: new RegExp(`${source}$`)
6421
+ };
7305
6422
  }
7306
- function resolveForgewaySeoApiUrl(savedPlatformApiUrl, cwd = process.cwd()) {
7307
- return normalizeApiUrl(getProjectEnvValue("DINEWAY_API_BASE_URL", cwd) ?? (typeof savedPlatformApiUrl === "string" ? savedPlatformApiUrl : void 0));
6423
+ function pathEligibility(url, options) {
6424
+ const path = url.pathname;
6425
+ if (options.include.length > 0 && !options.include.some((item) => item.pattern.test(path))) return "not_included";
6426
+ if (options.exclude.some((item) => item.pattern.test(path))) return "excluded";
6427
+ return null;
7308
6428
  }
7309
- function normalizeEmail(value) {
7310
- return value?.trim().toLowerCase() ?? "";
6429
+ async function loadRobots(options) {
6430
+ const url = new URL("/robots.txt", options.startUrl.origin).href;
6431
+ const resource = await fetchResource(url, options);
6432
+ if (!resource.response) return {
6433
+ source: {
6434
+ url,
6435
+ status: "temporarily_unavailable",
6436
+ httpStatus: null,
6437
+ error: resource.error ?? "fetch_failed"
6438
+ },
6439
+ rules: [],
6440
+ sitemaps: []
6441
+ };
6442
+ if (resource.response.status === 404) return {
6443
+ source: {
6444
+ url,
6445
+ status: "not_found",
6446
+ httpStatus: 404
6447
+ },
6448
+ rules: [],
6449
+ sitemaps: []
6450
+ };
6451
+ if (!resource.response.ok || resource.error) return {
6452
+ source: {
6453
+ url,
6454
+ status: "temporarily_unavailable",
6455
+ httpStatus: resource.response.status,
6456
+ error: resource.error ?? `http_${resource.response.status}`
6457
+ },
6458
+ rules: [],
6459
+ sitemaps: []
6460
+ };
6461
+ const parsed = parseRobots(resource.body);
6462
+ return {
6463
+ source: {
6464
+ url,
6465
+ status: "available",
6466
+ httpStatus: resource.response.status
6467
+ },
6468
+ rules: parsed.rules,
6469
+ sitemaps: parsed.sitemaps
6470
+ };
7311
6471
  }
7312
- function isShadowEmail(email) {
7313
- return Boolean(email && SHADOW_EMAIL_PATTERN.test(email));
6472
+ function parseRobots(body) {
6473
+ const groups = [];
6474
+ const sitemaps = [];
6475
+ let current = {
6476
+ agents: [],
6477
+ rules: []
6478
+ };
6479
+ for (const rawLine of body.split(LINE_SPLIT_RE)) {
6480
+ const line = rawLine.replace(COMMENT_RE, "").trim();
6481
+ if (!line) continue;
6482
+ const separator = line.indexOf(":");
6483
+ if (separator < 0) continue;
6484
+ const field = line.slice(0, separator).trim().toLowerCase();
6485
+ const value = line.slice(separator + 1).trim();
6486
+ if (field === "sitemap" && value) {
6487
+ sitemaps.push(value);
6488
+ continue;
6489
+ }
6490
+ if (field === "user-agent") {
6491
+ if (current.rules.length > 0) {
6492
+ groups.push(current);
6493
+ current = {
6494
+ agents: [],
6495
+ rules: []
6496
+ };
6497
+ }
6498
+ current.agents.push(value.toLowerCase());
6499
+ continue;
6500
+ }
6501
+ if ((field === "allow" || field === "disallow") && current.agents.length > 0 && value) current.rules.push({
6502
+ type: field,
6503
+ path: value
6504
+ });
6505
+ }
6506
+ if (current.agents.length > 0 || current.rules.length > 0) groups.push(current);
6507
+ const crawlerName = USER_AGENT.split("/")[0]?.toLowerCase() ?? "dinewaycontentcrawler";
6508
+ const exact = groups.filter((group) => group.agents.includes(crawlerName));
6509
+ return {
6510
+ rules: (exact.length > 0 ? exact : groups.filter((group) => group.agents.includes("*"))).flatMap((group) => group.rules),
6511
+ sitemaps: uniqueSorted(sitemaps)
6512
+ };
7314
6513
  }
7315
- function assertFormalUser(user) {
7316
- if (!user || user.emailVerified !== true || isShadowEmail(user.email)) throw new Error("Forgeway SEO provider access requires verified formal Forgeway credentials. Run `dineway deploy forgeway` and complete email verification first.");
7317
- return user;
6514
+ function robotsAllows(url, rules) {
6515
+ const path = `${url.pathname}${url.search}`;
6516
+ const matching = rules.filter((rule) => robotsRuleMatches(path, rule.path)).toSorted((left, right) => right.path.length - left.path.length);
6517
+ if (matching.length === 0) return true;
6518
+ const longest = matching[0]?.path.length ?? 0;
6519
+ return matching.some((rule) => rule.path.length === longest && rule.type === "allow");
7318
6520
  }
7319
- function normalizeForgewayUser(user, fallback) {
7320
- if (!user?.id || !user.email) return fallback;
6521
+ function robotsRuleMatches(path, rulePath) {
6522
+ const anchored = rulePath.endsWith("$");
6523
+ const pattern = escapeRegExp(anchored ? rulePath.slice(0, -1) : rulePath).replaceAll("\\*", ".*");
6524
+ return new RegExp(`^${pattern}${anchored ? "$" : ""}`).test(path);
6525
+ }
6526
+ async function loadSitemaps(options, robotsSitemaps) {
6527
+ const queue = (robotsSitemaps.length > 0 ? robotsSitemaps : [new URL("/sitemap.xml", options.startUrl.origin).href]).map((value) => normalizeUrl(value, options.startUrl, options.keepQuery)).filter((value) => value !== null && value.origin === options.startUrl.origin).map((value) => value.href);
6528
+ const seen = /* @__PURE__ */ new Set();
6529
+ const sources = [];
6530
+ const urls = [];
6531
+ while (queue.length > 0 && seen.size < MAX_SITEMAPS) {
6532
+ const url = queue.shift();
6533
+ if (!url || seen.has(url)) continue;
6534
+ seen.add(url);
6535
+ const resource = await fetchResource(url, options);
6536
+ if (!resource.response) {
6537
+ sources.push({
6538
+ url,
6539
+ status: "temporarily_unavailable",
6540
+ httpStatus: null,
6541
+ error: resource.error ?? "fetch_failed"
6542
+ });
6543
+ continue;
6544
+ }
6545
+ if (resource.response.status === 404) {
6546
+ sources.push({
6547
+ url,
6548
+ status: "not_found",
6549
+ httpStatus: 404
6550
+ });
6551
+ continue;
6552
+ }
6553
+ if (!resource.response.ok || resource.error) {
6554
+ sources.push({
6555
+ url,
6556
+ status: "temporarily_unavailable",
6557
+ httpStatus: resource.response.status,
6558
+ error: resource.error ?? `http_${resource.response.status}`
6559
+ });
6560
+ continue;
6561
+ }
6562
+ try {
6563
+ const parsed = parseSitemap(resource.body);
6564
+ sources.push({
6565
+ url,
6566
+ status: "available",
6567
+ httpStatus: resource.response.status
6568
+ });
6569
+ for (const location of parsed.locations) {
6570
+ const normalized = normalizeUrl(location, options.startUrl, options.keepQuery);
6571
+ if (!normalized || normalized.origin !== options.startUrl.origin) continue;
6572
+ if (parsed.kind === "index") queue.push(normalized.href);
6573
+ else urls.push(normalized.href);
6574
+ }
6575
+ } catch {
6576
+ sources.push({
6577
+ url,
6578
+ status: "invalid",
6579
+ httpStatus: resource.response.status,
6580
+ error: "invalid_sitemap_xml"
6581
+ });
6582
+ }
6583
+ }
7321
6584
  return {
7322
- id: user.id,
7323
- name: user.profile?.name ?? user.name ?? user.email,
7324
- email: user.email,
7325
- avatarUrl: user.profile?.avatar_url ?? user.avatarUrl ?? user.avatar_url ?? null,
7326
- emailVerified: user.emailVerified ?? user.email_verified ?? false
6585
+ sources,
6586
+ urls: uniqueSorted(urls)
6587
+ };
6588
+ }
6589
+ function parseSitemap(body) {
6590
+ const parser = sax.parser(true, {
6591
+ trim: true,
6592
+ normalize: true
6593
+ });
6594
+ let root = null;
6595
+ let inLocation = false;
6596
+ let location = "";
6597
+ const locations = [];
6598
+ parser.onopentag = (node) => {
6599
+ const name = node.name.toLowerCase();
6600
+ root ??= name;
6601
+ if (name === "loc") {
6602
+ inLocation = true;
6603
+ location = "";
6604
+ }
6605
+ };
6606
+ parser.ontext = (text) => {
6607
+ if (inLocation) location += text;
6608
+ };
6609
+ parser.oncdata = (text) => {
6610
+ if (inLocation) location += text;
6611
+ };
6612
+ parser.onclosetag = (name) => {
6613
+ if (name.toLowerCase() !== "loc") return;
6614
+ inLocation = false;
6615
+ if (location.trim()) locations.push(location.trim());
6616
+ };
6617
+ parser.write(body).close();
6618
+ if (root !== "urlset" && root !== "sitemapindex") throw new Error("Unsupported sitemap root");
6619
+ return {
6620
+ kind: root === "sitemapindex" ? "index" : "urls",
6621
+ locations
7327
6622
  };
7328
6623
  }
7329
- async function parseJsonResponse(response) {
7330
- const text = await response.text();
7331
- if (!text) return {};
6624
+ async function fetchResource(url, options) {
6625
+ const controller = new AbortController();
6626
+ const timeout = setTimeout(() => controller.abort(), options.timeoutMs);
7332
6627
  try {
7333
- return JSON.parse(text);
7334
- } catch {
7335
- return { message: text };
7336
- }
7337
- }
7338
- function errorMessage(body, fallback) {
7339
- if (typeof body !== "object" || body === null) return fallback;
7340
- const record = body;
7341
- if (typeof record.message === "string") return record.message;
7342
- if (typeof record.error === "string") return record.error;
7343
- const nestedError = record.error;
7344
- if (typeof nestedError === "object" && nestedError !== null) {
7345
- const message = nestedError.message;
7346
- if (typeof message === "string") return message;
7347
- }
7348
- return fallback;
7349
- }
7350
- var ForgewaySeoGatewayClient = class {
7351
- fetchImpl;
7352
- readCredentials;
7353
- writeCredentials;
7354
- cwd;
7355
- constructor(deps = {}) {
7356
- this.fetchImpl = deps.fetch ?? fetch;
7357
- this.readCredentials = deps.readCredentials ?? readForgewayCredentials;
7358
- this.writeCredentials = deps.writeCredentials ?? writeForgewayCredentials;
7359
- this.cwd = deps.cwd ?? process.cwd();
7360
- }
7361
- async loadCredentials() {
7362
- const metadata = await readForgewayDeployMetadata(this.cwd).catch(() => ({}));
7363
- const platformApiUrl = resolveForgewaySeoApiUrl(metadata.platformApiUrl, this.cwd);
7364
- const credentials = await this.readCredentials({
7365
- cwd: this.cwd,
7366
- platformApiUrl,
7367
- email: typeof metadata.publisherEmail === "string" ? metadata.publisherEmail : void 0
7368
- });
7369
- if (!credentials?.accessToken) throw new Error("Forgeway SEO provider access requires stored Forgeway credentials. Run `dineway deploy forgeway` first.");
7370
- assertFormalUser(credentials.user);
7371
- if (!credentials.refreshToken) throw new Error("Forgeway SEO provider access requires a stored formal-account refresh token. Run `dineway deploy forgeway` again.");
6628
+ let currentUrl = url;
6629
+ let response = null;
6630
+ for (let redirects = 0; redirects <= MAX_REDIRECTS; redirects += 1) {
6631
+ if (!options.allowPrivateNetwork) await resolveAndValidateExternalUrl(currentUrl, { resolver: options.dnsResolver });
6632
+ response = await options.fetch(currentUrl, {
6633
+ headers: {
6634
+ accept: "text/html,application/xml,text/xml,text/plain;q=0.8",
6635
+ "user-agent": USER_AGENT
6636
+ },
6637
+ redirect: "manual",
6638
+ signal: controller.signal
6639
+ });
6640
+ const location = response.headers.get("location");
6641
+ if (response.status < 300 || response.status >= 400 || !location) break;
6642
+ const next = normalizeUrl(location, new URL(currentUrl), options.keepQuery);
6643
+ if (!next) return {
6644
+ response,
6645
+ finalUrl: currentUrl,
6646
+ contentType: response.headers.get("content-type"),
6647
+ body: "",
6648
+ bytes: 0,
6649
+ error: "invalid_redirect"
6650
+ };
6651
+ if (next.origin !== options.startUrl.origin) return {
6652
+ response,
6653
+ finalUrl: next.href,
6654
+ contentType: response.headers.get("content-type"),
6655
+ body: "",
6656
+ bytes: 0,
6657
+ error: "cross_origin_redirect"
6658
+ };
6659
+ if (redirects === MAX_REDIRECTS) return {
6660
+ response,
6661
+ finalUrl: next.href,
6662
+ contentType: response.headers.get("content-type"),
6663
+ body: "",
6664
+ bytes: 0,
6665
+ error: "too_many_redirects"
6666
+ };
6667
+ currentUrl = next.href;
6668
+ }
6669
+ if (!response) throw new Error("No response");
6670
+ const finalUrl = response.url || currentUrl;
6671
+ const contentType = response.headers.get("content-type");
6672
+ const declaredLength = Number(response.headers.get("content-length"));
6673
+ if (Number.isFinite(declaredLength) && declaredLength > options.maxBytesPerResource) return {
6674
+ response,
6675
+ finalUrl,
6676
+ contentType,
6677
+ body: "",
6678
+ bytes: 0,
6679
+ error: "resource_too_large"
6680
+ };
6681
+ const body = await readBoundedBody(response, options.maxBytesPerResource);
6682
+ return {
6683
+ response,
6684
+ finalUrl,
6685
+ contentType,
6686
+ body: body.text,
6687
+ bytes: body.bytes
6688
+ };
6689
+ } catch (error) {
7372
6690
  return {
7373
- ...credentials,
7374
- platformApiUrl: resolveForgewaySeoApiUrl(credentials.platformApiUrl, this.cwd)
6691
+ response: null,
6692
+ finalUrl: url,
6693
+ contentType: null,
6694
+ body: "",
6695
+ bytes: 0,
6696
+ error: error instanceof ResourceTooLargeError ? "resource_too_large" : fetchError(error)
7375
6697
  };
6698
+ } finally {
6699
+ clearTimeout(timeout);
7376
6700
  }
7377
- async refreshAccessToken(credentials) {
7378
- const response = await this.fetchImpl(`${credentials.platformApiUrl}/api/auth/refresh?client_type=server`, {
7379
- method: "POST",
7380
- headers: { "Content-Type": "application/json" },
7381
- body: JSON.stringify({ refreshToken: credentials.refreshToken })
7382
- });
7383
- const body = await parseJsonResponse(response);
7384
- if (!response.ok) throw new ForgewaySeoApiError(errorMessage(body, "Failed to refresh Forgeway formal account credentials. Run `dineway deploy forgeway` again."), response.status, body);
7385
- if (!body.accessToken) throw new Error("Forgeway refresh response was missing accessToken.");
7386
- const user = assertFormalUser(normalizeForgewayUser(body.user, credentials.user));
7387
- if (credentials.user?.email && normalizeEmail(user.email) !== normalizeEmail(credentials.user.email)) throw new Error("Forgeway refresh returned credentials for a different email.");
7388
- const updated = {
7389
- ...credentials,
7390
- accessToken: body.accessToken,
7391
- refreshToken: body.refreshToken ?? credentials.refreshToken,
7392
- user
6701
+ }
6702
+ async function readBoundedBody(response, limit) {
6703
+ if (!response.body) return {
6704
+ text: "",
6705
+ bytes: 0
6706
+ };
6707
+ const reader = response.body.getReader();
6708
+ const chunks = [];
6709
+ let bytes = 0;
6710
+ while (true) {
6711
+ const result = await reader.read();
6712
+ if (result.done) break;
6713
+ bytes += result.value.byteLength;
6714
+ if (bytes > limit) {
6715
+ await reader.cancel();
6716
+ throw new ResourceTooLargeError();
6717
+ }
6718
+ chunks.push(result.value);
6719
+ }
6720
+ const combined = new Uint8Array(bytes);
6721
+ let offset = 0;
6722
+ for (const chunk of chunks) {
6723
+ combined.set(chunk, offset);
6724
+ offset += chunk.byteLength;
6725
+ }
6726
+ return {
6727
+ text: new TextDecoder().decode(combined),
6728
+ bytes
6729
+ };
6730
+ }
6731
+ var ResourceTooLargeError = class extends Error {};
6732
+ function fetchError(error) {
6733
+ if (error instanceof SsrfError) return "private_network_blocked";
6734
+ if (error instanceof Error && error.name === "AbortError") return "timeout";
6735
+ return "fetch_failed";
6736
+ }
6737
+ function pageFromResource(item, resource, options) {
6738
+ const base = {
6739
+ url: item.url,
6740
+ finalUrl: resource.finalUrl,
6741
+ depth: item.depth,
6742
+ discoveredFrom: item.discoveredFrom,
6743
+ httpStatus: resource.response?.status ?? null,
6744
+ contentType: resource.contentType,
6745
+ bytes: resource.bytes
6746
+ };
6747
+ const empty = {
6748
+ title: null,
6749
+ description: null,
6750
+ canonicalUrl: null,
6751
+ robots: null,
6752
+ language: null,
6753
+ headings: {
6754
+ h1: [],
6755
+ h2: [],
6756
+ h3: []
6757
+ },
6758
+ wordCount: 0,
6759
+ contentSha256: null,
6760
+ textExcerpt: "",
6761
+ textTruncated: false,
6762
+ internalLinks: [],
6763
+ externalLinks: [],
6764
+ structuredDataBlocks: 0,
6765
+ renderingRecommended: false
6766
+ };
6767
+ if (resource.error) return {
6768
+ ...base,
6769
+ ...empty,
6770
+ error: resource.error
6771
+ };
6772
+ const final = normalizeUrl(resource.finalUrl, void 0, options.keepQuery);
6773
+ if (!final || final.origin !== options.startUrl.origin) return {
6774
+ ...base,
6775
+ ...empty,
6776
+ error: "cross_origin_redirect"
6777
+ };
6778
+ if (!resource.contentType?.includes("text/html")) return {
6779
+ ...base,
6780
+ ...empty
6781
+ };
6782
+ try {
6783
+ return {
6784
+ ...base,
6785
+ ...parseHtml(resource.body, final, options.keepQuery)
6786
+ };
6787
+ } catch {
6788
+ return {
6789
+ ...base,
6790
+ ...empty,
6791
+ error: "invalid_html"
7393
6792
  };
7394
- await this.writeCredentials(updated, {
7395
- cwd: this.cwd,
7396
- platformApiUrl: updated.platformApiUrl,
7397
- email: user.email
7398
- });
7399
- return updated;
7400
6793
  }
7401
- async request(path, body, init) {
7402
- const credentials = await this.refreshAccessToken(await this.loadCredentials());
7403
- const method = init?.method ?? "POST";
7404
- const response = await this.fetchImpl(`${credentials.platformApiUrl}${path}`, {
7405
- method,
7406
- headers: {
7407
- Authorization: `Bearer ${credentials.accessToken}`,
7408
- "Content-Type": "application/json",
7409
- "Idempotency-Key": init?.idempotencyKey ?? randomUUID()
7410
- },
7411
- body: method === "GET" ? void 0 : JSON.stringify(body ?? {})
7412
- });
7413
- const payload = await parseJsonResponse(response);
7414
- if (!response.ok) throw new ForgewaySeoApiError(errorMessage(payload, `Forgeway SEO request failed with status ${response.status}.`), response.status, payload);
7415
- return payload;
6794
+ }
6795
+ function parseHtml(body, pageUrl, keepQuery) {
6796
+ const document = parse(body);
6797
+ let title = null;
6798
+ let description = null;
6799
+ let canonicalUrl = null;
6800
+ let robots = null;
6801
+ let language = null;
6802
+ let structuredDataBlocks = 0;
6803
+ let scriptBlocks = 0;
6804
+ const headings = {
6805
+ h1: [],
6806
+ h2: [],
6807
+ h3: []
6808
+ };
6809
+ const internalLinks = [];
6810
+ const externalLinks = [];
6811
+ walk(document, (element) => {
6812
+ const attributes = new Map(element.attrs.map((attribute) => [attribute.name, attribute.value]));
6813
+ if (element.tagName === "html") language = cleanText(attributes.get("lang")) || language;
6814
+ if (element.tagName === "title" && title === null) title = cleanText(textContent(element));
6815
+ if (element.tagName === "meta") {
6816
+ const name = attributes.get("name")?.toLowerCase();
6817
+ if (name === "description" && description === null) description = cleanText(attributes.get("content"));
6818
+ if ((name === "robots" || name === "googlebot") && robots === null) robots = cleanText(attributes.get("content"));
6819
+ }
6820
+ if (element.tagName === "link") {
6821
+ if ((attributes.get("rel")?.toLowerCase().split(WHITESPACE_RE) ?? []).includes("canonical") && canonicalUrl === null) canonicalUrl = normalizeUrl(attributes.get("href") ?? "", pageUrl, keepQuery)?.href ?? null;
6822
+ }
6823
+ if (element.tagName === "h1" || element.tagName === "h2" || element.tagName === "h3") {
6824
+ const value = cleanText(textContent(element));
6825
+ if (value) headings[element.tagName].push(value);
6826
+ }
6827
+ if (element.tagName === "script") {
6828
+ scriptBlocks += 1;
6829
+ if (attributes.get("type")?.toLowerCase() === "application/ld+json") structuredDataBlocks += 1;
6830
+ }
6831
+ if (element.tagName === "a") {
6832
+ const link = normalizeUrl(attributes.get("href") ?? "", pageUrl, keepQuery);
6833
+ if (!link) return;
6834
+ const item = {
6835
+ url: link.href,
6836
+ anchor: cleanText(textContent(element)) ?? ""
6837
+ };
6838
+ if (link.origin === pageUrl.origin) internalLinks.push(item);
6839
+ else externalLinks.push(item);
6840
+ }
6841
+ });
6842
+ const visibleText = cleanText(textContent(document)) ?? "";
6843
+ const wordCount = countWords(visibleText);
6844
+ const textExcerpt = visibleText.slice(0, MAX_TEXT_EXCERPT);
6845
+ return {
6846
+ title,
6847
+ description,
6848
+ canonicalUrl,
6849
+ robots,
6850
+ language,
6851
+ headings,
6852
+ wordCount,
6853
+ contentSha256: createHash("sha256").update(visibleText).digest("hex"),
6854
+ textExcerpt,
6855
+ textTruncated: textExcerpt.length < visibleText.length,
6856
+ internalLinks: uniqueLinks(internalLinks),
6857
+ externalLinks: uniqueLinks(externalLinks),
6858
+ structuredDataBlocks,
6859
+ renderingRecommended: wordCount < 20 && scriptBlocks > 0
6860
+ };
6861
+ }
6862
+ function walk(node, visit) {
6863
+ if (isElement(node)) visit(node);
6864
+ if (!("childNodes" in node)) return;
6865
+ for (const child of node.childNodes) walk(child, visit);
6866
+ }
6867
+ function textContent(node, hidden = false) {
6868
+ if ("value" in node) return hidden ? "" : node.value;
6869
+ const nextHidden = hidden || isElement(node) && [
6870
+ "script",
6871
+ "style",
6872
+ "noscript",
6873
+ "template",
6874
+ "svg",
6875
+ "head"
6876
+ ].includes(node.tagName);
6877
+ if (!("childNodes" in node)) return "";
6878
+ return node.childNodes.map((child) => textContent(child, nextHidden)).join(" ");
6879
+ }
6880
+ function isElement(node) {
6881
+ return "tagName" in node;
6882
+ }
6883
+ function normalizeUrl(value, base, keepQuery = false) {
6884
+ try {
6885
+ const url = base ? new URL(value, base) : new URL(value);
6886
+ if (url.protocol !== "http:" && url.protocol !== "https:" || url.username || url.password) return null;
6887
+ url.hash = "";
6888
+ if (!keepQuery) url.search = "";
6889
+ if (!url.pathname) url.pathname = "/";
6890
+ return url;
6891
+ } catch {
6892
+ return null;
7416
6893
  }
7417
- };
6894
+ }
6895
+ function cleanText(value) {
6896
+ if (!value) return null;
6897
+ return value.replaceAll(WHITESPACE_RE, " ").trim() || null;
6898
+ }
6899
+ function countWords(value) {
6900
+ return value.match(WORD_RE)?.length ?? 0;
6901
+ }
6902
+ function uniqueLinks(links) {
6903
+ const seen = /* @__PURE__ */ new Set();
6904
+ return links.filter((link) => {
6905
+ const key = `${link.url}\u0000${link.anchor}`;
6906
+ if (seen.has(key)) return false;
6907
+ seen.add(key);
6908
+ return true;
6909
+ });
6910
+ }
6911
+ function duplicateGroups(pages, field) {
6912
+ const groups = /* @__PURE__ */ new Map();
6913
+ for (const page of pages) {
6914
+ const value = page[field];
6915
+ if (!value) continue;
6916
+ const key = field === "contentSha256" ? value : value.toLocaleLowerCase();
6917
+ const group = groups.get(key) ?? {
6918
+ value,
6919
+ urls: []
6920
+ };
6921
+ group.urls.push(page.url);
6922
+ groups.set(key, group);
6923
+ }
6924
+ return [...groups.values()].filter((group) => group.urls.length > 1).map((group) => ({
6925
+ value: group.value,
6926
+ urls: uniqueSorted(group.urls)
6927
+ })).toSorted((left, right) => left.value.localeCompare(right.value));
6928
+ }
6929
+ function uniqueSorted(values) {
6930
+ return [...new Set(values)].toSorted((left, right) => left.localeCompare(right));
6931
+ }
6932
+ function escapeRegExp(value) {
6933
+ return value.replace(REGEXP_SPECIAL_RE, "\\$&");
6934
+ }
7418
6935
 
7419
6936
  //#endregion
7420
6937
  //#region src/cli/commands/seo.ts
6938
+ const jsonArgs = { json: {
6939
+ type: "boolean",
6940
+ description: "Output as JSON"
6941
+ } };
7421
6942
  const hostedProviderArgs = {
7422
- json: {
7423
- type: "boolean",
7424
- description: "Output as JSON"
7425
- },
6943
+ ...jsonArgs,
7426
6944
  "force-refresh": {
7427
6945
  type: "boolean",
7428
6946
  description: "Bypass Forgeway SEO provider cache"
@@ -7468,416 +6986,515 @@ function requireArg(value, name) {
7468
6986
  if (!normalized) throw new Error(`${name} is required.`);
7469
6987
  return normalized;
7470
6988
  }
7471
- async function runSeoRequest(args, path, body) {
7472
- configureOutputMode(args);
7473
- try {
7474
- output(await new ForgewaySeoGatewayClient().request(path, body), args);
7475
- } catch (error) {
7476
- if (await handleCliPaywall({
7477
- error,
7478
- resource: path,
7479
- context: { providerPath: path }
7480
- })) process.exit(2);
7481
- consola$1.error(error instanceof Error ? error.message : "Unknown error");
7482
- process.exit(1);
7483
- }
6989
+ function sitemapMode(value) {
6990
+ const mode = toStringValue(value) ?? "include";
6991
+ if (mode === "include" || mode === "skip" || mode === "only") return mode;
6992
+ throw new Error("sitemap must be include, skip, or only.");
7484
6993
  }
7485
- const firecrawlCommand = defineCommand({
6994
+ const crawlCommand = defineCommand({
7486
6995
  meta: {
7487
- name: "firecrawl",
7488
- description: "Forgeway-hosted Firecrawl workflows"
6996
+ name: "crawl",
6997
+ description: "Deterministic site crawl controls"
7489
6998
  },
7490
- subCommands: {
7491
- scrape: defineCommand({
7492
- meta: {
7493
- name: "scrape",
7494
- description: "Scrape one URL through Forgeway-hosted Firecrawl"
6999
+ subCommands: { local: defineCommand({
7000
+ meta: {
7001
+ name: "local",
7002
+ description: "Crawl one site locally and return bounded structural evidence"
7003
+ },
7004
+ args: {
7005
+ url: {
7006
+ type: "positional",
7007
+ description: "First-party site URL",
7008
+ required: true
7495
7009
  },
7496
- args: {
7497
- url: {
7498
- type: "positional",
7499
- description: "URL to scrape",
7500
- required: true
7501
- },
7502
- ...hostedProviderArgs,
7503
- formats: {
7504
- type: "string",
7505
- description: "Comma-separated Firecrawl formats",
7506
- default: "markdown"
7507
- },
7508
- "only-main-content": {
7509
- type: "boolean",
7510
- description: "Return main content only",
7511
- default: true
7512
- },
7513
- "timeout-ms": {
7514
- type: "string",
7515
- description: "Provider timeout in milliseconds",
7516
- default: "60000"
7517
- }
7010
+ ...jsonArgs,
7011
+ limit: {
7012
+ type: "string",
7013
+ description: "Maximum pages",
7014
+ default: "100"
7518
7015
  },
7519
- async run({ args }) {
7520
- await runSeoRequest(args, "/api/firecrawl/scrape", {
7521
- url: requireArg(args.url, "url"),
7522
- formats: formats(args.formats),
7523
- onlyMainContent: args["only-main-content"] !== false,
7524
- timeoutMs: toNumberValue(args["timeout-ms"], 6e4),
7525
- forceRefresh: forceRefresh(args)
7526
- });
7527
- }
7528
- }),
7529
- map: defineCommand({
7530
- meta: {
7531
- name: "map",
7532
- description: "Map URLs through Forgeway-hosted Firecrawl"
7016
+ "max-depth": {
7017
+ type: "string",
7018
+ description: "Maximum link discovery depth",
7019
+ default: "3"
7533
7020
  },
7534
- args: {
7535
- url: {
7536
- type: "positional",
7537
- description: "Base URL to map",
7538
- required: true
7539
- },
7540
- ...hostedProviderArgs,
7541
- search: {
7542
- type: "string",
7543
- description: "Optional relevance search term"
7544
- },
7545
- sitemap: {
7546
- type: "string",
7547
- description: "Sitemap mode: skip, include, or only",
7548
- default: "include"
7549
- },
7550
- limit: {
7551
- type: "string",
7552
- description: "Maximum URLs",
7553
- default: "100"
7554
- },
7555
- "include-subdomains": {
7556
- type: "boolean",
7557
- description: "Include subdomains"
7558
- },
7559
- "ignore-query-parameters": {
7560
- type: "boolean",
7561
- description: "Ignore query parameters",
7562
- default: true
7563
- }
7021
+ concurrency: {
7022
+ type: "string",
7023
+ description: "Maximum concurrent page requests",
7024
+ default: "4"
7564
7025
  },
7565
- async run({ args }) {
7566
- await runSeoRequest(args, "/api/firecrawl/map", {
7567
- url: requireArg(args.url, "url"),
7568
- search: toStringValue(args.search),
7569
- sitemap: toStringValue(args.sitemap) ?? "include",
7570
- limit: toNumberValue(args.limit, 100),
7571
- includeSubdomains: args["include-subdomains"] === true,
7572
- ignoreQueryParameters: args["ignore-query-parameters"] !== false,
7573
- forceRefresh: forceRefresh(args)
7574
- });
7575
- }
7576
- }),
7577
- crawl: defineCommand({
7578
- meta: {
7579
- name: "crawl",
7580
- description: "Start a bounded Firecrawl crawl, or check status with --id"
7026
+ include: {
7027
+ type: "string",
7028
+ description: "Comma-separated path globs to include"
7581
7029
  },
7582
- args: {
7583
- url: {
7584
- type: "positional",
7585
- description: "Base URL to crawl"
7586
- },
7587
- ...hostedProviderArgs,
7588
- id: {
7589
- type: "string",
7590
- description: "Existing Firecrawl crawl id to check"
7591
- },
7592
- limit: {
7593
- type: "string",
7594
- description: "Maximum pages",
7595
- default: "25"
7596
- },
7597
- "max-depth": {
7598
- type: "string",
7599
- description: "Maximum discovery depth",
7600
- default: "3"
7601
- },
7602
- "allow-subdomains": {
7603
- type: "boolean",
7604
- description: "Allow subdomains"
7605
- },
7606
- "allow-external-links": {
7607
- type: "boolean",
7608
- description: "Allow external links"
7609
- },
7610
- formats: {
7611
- type: "string",
7612
- description: "Comma-separated scrape formats",
7613
- default: "markdown"
7614
- }
7030
+ exclude: {
7031
+ type: "string",
7032
+ description: "Comma-separated path globs to exclude"
7615
7033
  },
7616
- async run({ args }) {
7617
- const crawlId = toStringValue(args.id);
7618
- if (crawlId) {
7619
- configureOutputMode(args);
7620
- try {
7621
- output(await new ForgewaySeoGatewayClient().request(`/api/firecrawl/crawl/${encodeURIComponent(crawlId)}`, void 0, { method: "GET" }), args);
7622
- } catch (error) {
7623
- if (await handleCliPaywall({
7624
- error,
7625
- resource: `/api/firecrawl/crawl/${crawlId}`,
7626
- context: { providerPath: `/api/firecrawl/crawl/${crawlId}` }
7627
- })) process.exit(2);
7628
- consola$1.error(error instanceof Error ? error.message : "Unknown error");
7629
- process.exit(1);
7630
- }
7631
- return;
7632
- }
7633
- await runSeoRequest(args, "/api/firecrawl/crawl", {
7634
- url: requireArg(args.url, "url"),
7635
- limit: toNumberValue(args.limit, 25),
7636
- maxDiscoveryDepth: toNumberValue(args["max-depth"], 3),
7637
- allowExternalLinks: args["allow-external-links"] === true,
7638
- allowSubdomains: args["allow-subdomains"] === true,
7639
- scrapeFormats: formats(args.formats),
7640
- forceRefresh: forceRefresh(args)
7641
- });
7642
- }
7643
- }),
7644
- search: defineCommand({
7645
- meta: {
7646
- name: "search",
7647
- description: "Search through Forgeway-hosted Firecrawl"
7034
+ "timeout-ms": {
7035
+ type: "string",
7036
+ description: "Per-resource timeout in milliseconds",
7037
+ default: "10000"
7648
7038
  },
7649
- args: {
7650
- query: {
7651
- type: "positional",
7652
- description: "Search query",
7653
- required: true
7654
- },
7655
- ...hostedProviderArgs,
7656
- limit: {
7657
- type: "string",
7658
- description: "Maximum results",
7659
- default: "10"
7660
- },
7661
- scrape: {
7662
- type: "boolean",
7663
- description: "Scrape result markdown"
7664
- }
7039
+ "max-bytes": {
7040
+ type: "string",
7041
+ description: "Maximum bytes per fetched resource",
7042
+ default: "1000000"
7665
7043
  },
7666
- async run({ args }) {
7667
- await runSeoRequest(args, "/api/firecrawl/search", {
7668
- query: requireArg(args.query, "query"),
7669
- limit: toNumberValue(args.limit, 10),
7670
- scrape: args.scrape === true,
7671
- forceRefresh: forceRefresh(args)
7672
- });
7044
+ sitemap: {
7045
+ type: "string",
7046
+ description: "Sitemap mode: include, skip, or only",
7047
+ default: "include"
7048
+ },
7049
+ "keep-query": {
7050
+ type: "boolean",
7051
+ description: "Keep query parameters in page identity"
7052
+ },
7053
+ "allow-private-network": {
7054
+ type: "boolean",
7055
+ description: "Allow an explicitly selected localhost or private origin"
7673
7056
  }
7674
- })
7675
- }
7057
+ },
7058
+ async run({ args }) {
7059
+ configureOutputMode(args);
7060
+ try {
7061
+ output(await crawlLocalSite({
7062
+ startUrl: requireArg(args.url, "url"),
7063
+ pageLimit: toNumberValue(args.limit, 100),
7064
+ maxDepth: toNumberValue(args["max-depth"], 3),
7065
+ concurrency: toNumberValue(args.concurrency, 4),
7066
+ include: formats(args.include) ?? [],
7067
+ exclude: formats(args.exclude) ?? [],
7068
+ timeoutMs: toNumberValue(args["timeout-ms"], 1e4),
7069
+ maxBytesPerResource: toNumberValue(args["max-bytes"], 1e6),
7070
+ sitemap: sitemapMode(args.sitemap),
7071
+ keepQuery: args["keep-query"] === true,
7072
+ allowPrivateNetwork: args["allow-private-network"] === true
7073
+ }), args);
7074
+ } catch (error) {
7075
+ consola$1.error(error instanceof Error ? error.message : "Local site crawl failed");
7076
+ process.exit(1);
7077
+ }
7078
+ }
7079
+ }) }
7676
7080
  });
7081
+ function dataForSeoDevice(value) {
7082
+ const normalized = toStringValue(value) ?? "desktop";
7083
+ if (normalized === "desktop" || normalized === "mobile") return normalized;
7084
+ throw new Error("device must be desktop or mobile.");
7085
+ }
7086
+ function aiScrapePlatform(value) {
7087
+ const normalized = toStringValue(value) ?? "chat_gpt";
7088
+ if (normalized === "chat_gpt" || normalized === "gemini" || normalized === "perplexity") return normalized;
7089
+ throw new Error("platform must be chat_gpt, gemini, or perplexity.");
7090
+ }
7091
+ function aiMentionPlatform(value) {
7092
+ const normalized = toStringValue(value) ?? "google";
7093
+ if (normalized === "google" || normalized === "chat_gpt") return normalized;
7094
+ throw new Error("platform must be google or chat_gpt.");
7095
+ }
7677
7096
  function dataForSeoBase(args) {
7678
7097
  return {
7679
7098
  locationCode: toNumberValue(args["location-code"], 2840),
7680
7099
  languageCode: toStringValue(args["language-code"]) ?? "en",
7681
- device: toStringValue(args.device) ?? "desktop",
7100
+ device: dataForSeoDevice(args.device),
7682
7101
  forceRefresh: forceRefresh(args)
7683
7102
  };
7684
7103
  }
7685
- const dataForSeoSerpCommand = defineCommand({
7686
- meta: {
7687
- name: "serp",
7688
- description: "Fetch Google organic SERP data"
7689
- },
7690
- args: {
7691
- keyword: {
7692
- type: "positional",
7693
- description: "Keyword",
7694
- required: true
7695
- },
7696
- ...dataForSeoCommonArgs,
7697
- depth: {
7698
- type: "string",
7699
- description: "SERP depth",
7700
- default: "10"
7104
+ function createSeoCommand(options = {}) {
7105
+ const providerService = options.providerService ?? new SeoProviderEvidenceService(new ForgewaySeoGatewayClient());
7106
+ async function runSeoRequest(args, resource, request) {
7107
+ configureOutputMode(args);
7108
+ try {
7109
+ output(await request(), args);
7110
+ } catch (error) {
7111
+ if (await handleCliPaywall({
7112
+ error,
7113
+ resource,
7114
+ context: { providerPath: resource }
7115
+ })) process.exit(2);
7116
+ consola$1.error(error instanceof Error ? error.message : "Unknown error");
7117
+ process.exit(1);
7701
7118
  }
7702
- },
7703
- async run({ args }) {
7704
- await runSeoRequest(args, "/api/dataforseo/serp", {
7705
- keyword: requireArg(args.keyword, "keyword"),
7706
- depth: toNumberValue(args.depth, 10),
7707
- ...dataForSeoBase(args)
7708
- });
7709
7119
  }
7710
- });
7711
- const dataForSeoMapsCommand = defineCommand({
7712
- meta: {
7713
- name: "maps",
7714
- description: "Fetch Google Maps SERP data"
7715
- },
7716
- args: {
7717
- keyword: {
7718
- type: "positional",
7719
- description: "Keyword",
7720
- required: true
7721
- },
7722
- ...dataForSeoCommonArgs,
7723
- depth: {
7724
- type: "string",
7725
- description: "Maps depth",
7726
- default: "10"
7120
+ const firecrawlCommand = defineCommand({
7121
+ meta: {
7122
+ name: "firecrawl",
7123
+ description: "Forgeway-hosted Firecrawl workflows"
7124
+ },
7125
+ subCommands: {
7126
+ scrape: defineCommand({
7127
+ meta: {
7128
+ name: "scrape",
7129
+ description: "Scrape one URL through Forgeway-hosted Firecrawl"
7130
+ },
7131
+ args: {
7132
+ url: {
7133
+ type: "positional",
7134
+ description: "URL to scrape",
7135
+ required: true
7136
+ },
7137
+ ...hostedProviderArgs,
7138
+ formats: {
7139
+ type: "string",
7140
+ description: "Comma-separated Firecrawl formats",
7141
+ default: "markdown"
7142
+ },
7143
+ "only-main-content": {
7144
+ type: "boolean",
7145
+ description: "Return main content only",
7146
+ default: true
7147
+ },
7148
+ "timeout-ms": {
7149
+ type: "string",
7150
+ description: "Provider timeout in milliseconds",
7151
+ default: "60000"
7152
+ }
7153
+ },
7154
+ async run({ args }) {
7155
+ await runSeoRequest(args, "/api/firecrawl/scrape", () => providerService.firecrawlScrape({
7156
+ url: requireArg(args.url, "url"),
7157
+ formats: formats(args.formats) ?? ["markdown"],
7158
+ onlyMainContent: args["only-main-content"] !== false,
7159
+ timeoutMs: toNumberValue(args["timeout-ms"], 6e4),
7160
+ forceRefresh: forceRefresh(args)
7161
+ }));
7162
+ }
7163
+ }),
7164
+ map: defineCommand({
7165
+ meta: {
7166
+ name: "map",
7167
+ description: "Map URLs through Forgeway-hosted Firecrawl"
7168
+ },
7169
+ args: {
7170
+ url: {
7171
+ type: "positional",
7172
+ description: "Base URL to map",
7173
+ required: true
7174
+ },
7175
+ ...hostedProviderArgs,
7176
+ search: {
7177
+ type: "string",
7178
+ description: "Optional relevance search term"
7179
+ },
7180
+ sitemap: {
7181
+ type: "string",
7182
+ description: "Sitemap mode: skip, include, or only",
7183
+ default: "include"
7184
+ },
7185
+ limit: {
7186
+ type: "string",
7187
+ description: "Maximum URLs",
7188
+ default: "100"
7189
+ },
7190
+ "include-subdomains": {
7191
+ type: "boolean",
7192
+ description: "Include subdomains"
7193
+ },
7194
+ "ignore-query-parameters": {
7195
+ type: "boolean",
7196
+ description: "Ignore query parameters",
7197
+ default: true
7198
+ }
7199
+ },
7200
+ async run({ args }) {
7201
+ await runSeoRequest(args, "/api/firecrawl/map", () => providerService.firecrawlMap({
7202
+ url: requireArg(args.url, "url"),
7203
+ search: toStringValue(args.search),
7204
+ sitemap: sitemapMode(args.sitemap),
7205
+ limit: toNumberValue(args.limit, 100),
7206
+ includeSubdomains: args["include-subdomains"] === true,
7207
+ ignoreQueryParameters: args["ignore-query-parameters"] !== false,
7208
+ forceRefresh: forceRefresh(args)
7209
+ }));
7210
+ }
7211
+ }),
7212
+ crawl: defineCommand({
7213
+ meta: {
7214
+ name: "crawl",
7215
+ description: "Start a bounded Firecrawl crawl, or check status with --id"
7216
+ },
7217
+ args: {
7218
+ url: {
7219
+ type: "positional",
7220
+ description: "Base URL to crawl",
7221
+ required: false
7222
+ },
7223
+ ...hostedProviderArgs,
7224
+ id: {
7225
+ type: "string",
7226
+ description: "Existing Firecrawl crawl id to check"
7227
+ },
7228
+ limit: {
7229
+ type: "string",
7230
+ description: "Maximum pages",
7231
+ default: "25"
7232
+ },
7233
+ "max-depth": {
7234
+ type: "string",
7235
+ description: "Maximum discovery depth",
7236
+ default: "3"
7237
+ },
7238
+ "allow-subdomains": {
7239
+ type: "boolean",
7240
+ description: "Allow subdomains"
7241
+ },
7242
+ "allow-external-links": {
7243
+ type: "boolean",
7244
+ description: "Allow external links"
7245
+ },
7246
+ formats: {
7247
+ type: "string",
7248
+ description: "Comma-separated scrape formats",
7249
+ default: "markdown"
7250
+ }
7251
+ },
7252
+ async run({ args }) {
7253
+ const crawlId = toStringValue(args.id);
7254
+ if (crawlId) {
7255
+ await runSeoRequest(args, `/api/firecrawl/crawl/${crawlId}`, () => providerService.firecrawlCrawlStatus({ id: crawlId }));
7256
+ return;
7257
+ }
7258
+ await runSeoRequest(args, "/api/firecrawl/crawl", () => providerService.firecrawlCrawlStart({
7259
+ url: requireArg(args.url, "url"),
7260
+ limit: toNumberValue(args.limit, 25),
7261
+ maxDiscoveryDepth: toNumberValue(args["max-depth"], 3),
7262
+ allowExternalLinks: args["allow-external-links"] === true,
7263
+ allowSubdomains: args["allow-subdomains"] === true,
7264
+ scrapeFormats: formats(args.formats) ?? ["markdown"],
7265
+ forceRefresh: forceRefresh(args)
7266
+ }));
7267
+ }
7268
+ }),
7269
+ search: defineCommand({
7270
+ meta: {
7271
+ name: "search",
7272
+ description: "Search through Forgeway-hosted Firecrawl"
7273
+ },
7274
+ args: {
7275
+ query: {
7276
+ type: "positional",
7277
+ description: "Search query",
7278
+ required: true
7279
+ },
7280
+ ...hostedProviderArgs,
7281
+ limit: {
7282
+ type: "string",
7283
+ description: "Maximum results",
7284
+ default: "10"
7285
+ },
7286
+ scrape: {
7287
+ type: "boolean",
7288
+ description: "Scrape result markdown"
7289
+ }
7290
+ },
7291
+ async run({ args }) {
7292
+ await runSeoRequest(args, "/api/firecrawl/search", () => providerService.firecrawlSearch({
7293
+ query: requireArg(args.query, "query"),
7294
+ limit: toNumberValue(args.limit, 10),
7295
+ scrape: args.scrape === true,
7296
+ forceRefresh: forceRefresh(args)
7297
+ }));
7298
+ }
7299
+ })
7727
7300
  }
7728
- },
7729
- async run({ args }) {
7730
- await runSeoRequest(args, "/api/dataforseo/maps", {
7731
- keyword: requireArg(args.keyword, "keyword"),
7732
- depth: toNumberValue(args.depth, 10),
7733
- ...dataForSeoBase(args)
7734
- });
7735
- }
7736
- });
7737
- const dataForSeoKeywordsCommand = defineCommand({
7738
- meta: {
7739
- name: "keywords",
7740
- description: "Fetch keyword suggestions"
7741
- },
7742
- args: {
7743
- keyword: {
7744
- type: "positional",
7745
- description: "Seed keyword",
7746
- required: true
7301
+ });
7302
+ const dataForSeoSerpCommand = defineCommand({
7303
+ meta: {
7304
+ name: "serp",
7305
+ description: "Fetch Google organic SERP data"
7306
+ },
7307
+ args: {
7308
+ keyword: {
7309
+ type: "positional",
7310
+ description: "Keyword",
7311
+ required: true
7312
+ },
7313
+ ...dataForSeoCommonArgs,
7314
+ depth: {
7315
+ type: "string",
7316
+ description: "SERP depth",
7317
+ default: "10"
7318
+ }
7747
7319
  },
7748
- ...dataForSeoCommonArgs,
7749
- limit: {
7750
- type: "string",
7751
- description: "Maximum suggestions",
7752
- default: "20"
7320
+ async run({ args }) {
7321
+ await runSeoRequest(args, "/api/dataforseo/serp", () => providerService.dataForSeoSerp({
7322
+ keyword: requireArg(args.keyword, "keyword"),
7323
+ depth: toNumberValue(args.depth, 10),
7324
+ ...dataForSeoBase(args)
7325
+ }));
7753
7326
  }
7754
- },
7755
- async run({ args }) {
7756
- await runSeoRequest(args, "/api/dataforseo/keywords", {
7757
- keyword: requireArg(args.keyword, "keyword"),
7758
- limit: toNumberValue(args.limit, 20),
7759
- includeSerpInfo: true,
7760
- includeSeedKeyword: true,
7761
- ...dataForSeoBase(args)
7762
- });
7763
- }
7764
- });
7765
- const dataForSeoCompetitorsCommand = defineCommand({
7766
- meta: {
7767
- name: "competitors",
7768
- description: "Fetch competitor-domain data"
7769
- },
7770
- args: {
7771
- domain: {
7772
- type: "positional",
7773
- description: "Target domain",
7774
- required: true
7327
+ });
7328
+ const dataForSeoMapsCommand = defineCommand({
7329
+ meta: {
7330
+ name: "maps",
7331
+ description: "Fetch Google Maps SERP data"
7332
+ },
7333
+ args: {
7334
+ keyword: {
7335
+ type: "positional",
7336
+ description: "Keyword",
7337
+ required: true
7338
+ },
7339
+ ...dataForSeoCommonArgs,
7340
+ depth: {
7341
+ type: "string",
7342
+ description: "Maps depth",
7343
+ default: "10"
7344
+ }
7775
7345
  },
7776
- ...dataForSeoCommonArgs,
7777
- limit: {
7778
- type: "string",
7779
- description: "Maximum competitors",
7780
- default: "20"
7346
+ async run({ args }) {
7347
+ await runSeoRequest(args, "/api/dataforseo/maps", () => providerService.dataForSeoMaps({
7348
+ keyword: requireArg(args.keyword, "keyword"),
7349
+ depth: toNumberValue(args.depth, 10),
7350
+ ...dataForSeoBase(args)
7351
+ }));
7781
7352
  }
7782
- },
7783
- async run({ args }) {
7784
- await runSeoRequest(args, "/api/dataforseo/competitors", {
7785
- targetDomain: requireArg(args.domain, "domain"),
7786
- limit: toNumberValue(args.limit, 20),
7787
- ...dataForSeoBase(args)
7788
- });
7789
- }
7790
- });
7791
- const dataForSeoAiScrapeCommand = defineCommand({
7792
- meta: {
7793
- name: "ai-scrape",
7794
- description: "Fetch DataForSEO AI scraper response data"
7795
- },
7796
- args: {
7797
- keyword: {
7798
- type: "positional",
7799
- description: "AI search keyword",
7800
- required: true
7353
+ });
7354
+ const dataForSeoKeywordsCommand = defineCommand({
7355
+ meta: {
7356
+ name: "keywords",
7357
+ description: "Fetch keyword suggestions"
7358
+ },
7359
+ args: {
7360
+ keyword: {
7361
+ type: "positional",
7362
+ description: "Seed keyword",
7363
+ required: true
7364
+ },
7365
+ ...dataForSeoCommonArgs,
7366
+ limit: {
7367
+ type: "string",
7368
+ description: "Maximum suggestions",
7369
+ default: "20"
7370
+ }
7801
7371
  },
7802
- ...dataForSeoCommonArgs,
7803
- platform: {
7804
- type: "string",
7805
- description: "AI platform: chat_gpt, gemini, or perplexity",
7806
- default: "chat_gpt"
7372
+ async run({ args }) {
7373
+ await runSeoRequest(args, "/api/dataforseo/keywords", () => providerService.dataForSeoKeywords({
7374
+ keyword: requireArg(args.keyword, "keyword"),
7375
+ limit: toNumberValue(args.limit, 20),
7376
+ ...dataForSeoBase(args)
7377
+ }));
7807
7378
  }
7808
- },
7809
- async run({ args }) {
7810
- await runSeoRequest(args, "/api/dataforseo/ai-scrape", {
7811
- keyword: requireArg(args.keyword, "keyword"),
7812
- platform: toStringValue(args.platform) ?? "chat_gpt",
7813
- ...dataForSeoBase(args)
7814
- });
7815
- }
7816
- });
7817
- const dataForSeoAiMentionsCommand = defineCommand({
7818
- meta: {
7819
- name: "ai-mentions",
7820
- description: "Fetch DataForSEO LLM mention data"
7821
- },
7822
- args: {
7823
- ...dataForSeoCommonArgs,
7824
- domain: {
7825
- type: "string",
7826
- description: "Target domain"
7379
+ });
7380
+ const dataForSeoCompetitorsCommand = defineCommand({
7381
+ meta: {
7382
+ name: "competitors",
7383
+ description: "Fetch competitor-domain data"
7384
+ },
7385
+ args: {
7386
+ domain: {
7387
+ type: "positional",
7388
+ description: "Target domain",
7389
+ required: true
7390
+ },
7391
+ ...dataForSeoCommonArgs,
7392
+ limit: {
7393
+ type: "string",
7394
+ description: "Maximum competitors",
7395
+ default: "20"
7396
+ }
7827
7397
  },
7828
- keyword: {
7829
- type: "string",
7830
- description: "Target keyword"
7398
+ async run({ args }) {
7399
+ await runSeoRequest(args, "/api/dataforseo/competitors", () => providerService.dataForSeoCompetitors({
7400
+ domain: requireArg(args.domain, "domain"),
7401
+ limit: toNumberValue(args.limit, 20),
7402
+ ...dataForSeoBase(args)
7403
+ }));
7404
+ }
7405
+ });
7406
+ const dataForSeoAiScrapeCommand = defineCommand({
7407
+ meta: {
7408
+ name: "ai-scrape",
7409
+ description: "Fetch DataForSEO AI scraper response data"
7410
+ },
7411
+ args: {
7412
+ keyword: {
7413
+ type: "positional",
7414
+ description: "AI search keyword",
7415
+ required: true
7416
+ },
7417
+ ...dataForSeoCommonArgs,
7418
+ platform: {
7419
+ type: "string",
7420
+ description: "AI platform: chat_gpt, gemini, or perplexity",
7421
+ default: "chat_gpt"
7422
+ }
7831
7423
  },
7832
- platform: {
7833
- type: "string",
7834
- description: "Mention platform: google or chat_gpt",
7835
- default: "google"
7424
+ async run({ args }) {
7425
+ await runSeoRequest(args, "/api/dataforseo/ai-scrape", () => providerService.dataForSeoAiScrape({
7426
+ keyword: requireArg(args.keyword, "keyword"),
7427
+ platform: aiScrapePlatform(args.platform),
7428
+ ...dataForSeoBase(args)
7429
+ }));
7430
+ }
7431
+ });
7432
+ const dataForSeoAiMentionsCommand = defineCommand({
7433
+ meta: {
7434
+ name: "ai-mentions",
7435
+ description: "Fetch DataForSEO LLM mention data"
7436
+ },
7437
+ args: {
7438
+ ...dataForSeoCommonArgs,
7439
+ domain: {
7440
+ type: "string",
7441
+ description: "Target domain"
7442
+ },
7443
+ keyword: {
7444
+ type: "string",
7445
+ description: "Target keyword"
7446
+ },
7447
+ platform: {
7448
+ type: "string",
7449
+ description: "Mention platform: google or chat_gpt",
7450
+ default: "google"
7451
+ },
7452
+ limit: {
7453
+ type: "string",
7454
+ description: "Maximum mentions",
7455
+ default: "20"
7456
+ }
7836
7457
  },
7837
- limit: {
7838
- type: "string",
7839
- description: "Maximum mentions",
7840
- default: "20"
7458
+ async run({ args }) {
7459
+ if (!toStringValue(args.domain) && !toStringValue(args.keyword)) {
7460
+ consola$1.error("domain or keyword is required.");
7461
+ process.exit(1);
7462
+ }
7463
+ await runSeoRequest(args, "/api/dataforseo/ai-mentions", () => providerService.dataForSeoAiMentions({
7464
+ domain: toStringValue(args.domain),
7465
+ keyword: toStringValue(args.keyword),
7466
+ platform: aiMentionPlatform(args.platform),
7467
+ limit: toNumberValue(args.limit, 20),
7468
+ ...dataForSeoBase(args)
7469
+ }));
7841
7470
  }
7842
- },
7843
- async run({ args }) {
7844
- if (!toStringValue(args.domain) && !toStringValue(args.keyword)) {
7845
- consola$1.error("domain or keyword is required.");
7846
- process.exit(1);
7471
+ });
7472
+ return defineCommand({
7473
+ meta: {
7474
+ name: "seo",
7475
+ description: "Local site evidence and hosted SEO provider workflows"
7476
+ },
7477
+ subCommands: {
7478
+ crawl: crawlCommand,
7479
+ firecrawl: firecrawlCommand,
7480
+ dataforseo: defineCommand({
7481
+ meta: {
7482
+ name: "dataforseo",
7483
+ description: "Forgeway-hosted DataForSEO workflows"
7484
+ },
7485
+ subCommands: {
7486
+ serp: dataForSeoSerpCommand,
7487
+ maps: dataForSeoMapsCommand,
7488
+ keywords: dataForSeoKeywordsCommand,
7489
+ competitors: dataForSeoCompetitorsCommand,
7490
+ "ai-scrape": dataForSeoAiScrapeCommand,
7491
+ "ai-mentions": dataForSeoAiMentionsCommand
7492
+ }
7493
+ })
7847
7494
  }
7848
- await runSeoRequest(args, "/api/dataforseo/ai-mentions", {
7849
- targetDomain: toStringValue(args.domain),
7850
- keyword: toStringValue(args.keyword),
7851
- platform: toStringValue(args.platform) ?? "google",
7852
- limit: toNumberValue(args.limit, 20),
7853
- ...dataForSeoBase(args)
7854
- });
7855
- }
7856
- });
7857
- const dataForSeoCommand = defineCommand({
7858
- meta: {
7859
- name: "dataforseo",
7860
- description: "Forgeway-hosted DataForSEO workflows"
7861
- },
7862
- subCommands: {
7863
- serp: dataForSeoSerpCommand,
7864
- maps: dataForSeoMapsCommand,
7865
- keywords: dataForSeoKeywordsCommand,
7866
- competitors: dataForSeoCompetitorsCommand,
7867
- "ai-scrape": dataForSeoAiScrapeCommand,
7868
- "ai-mentions": dataForSeoAiMentionsCommand
7869
- }
7870
- });
7871
- const seoCommand = defineCommand({
7872
- meta: {
7873
- name: "seo",
7874
- description: "Hosted SEO provider workflows through Forgeway"
7875
- },
7876
- subCommands: {
7877
- firecrawl: firecrawlCommand,
7878
- dataforseo: dataForSeoCommand
7879
- }
7880
- });
7495
+ });
7496
+ }
7497
+ const seoCommand = createSeoCommand();
7881
7498
 
7882
7499
  //#endregion
7883
7500
  //#region src/cli/commands/taxonomy.ts
@@ -8047,6 +7664,88 @@ const typesCommand = defineCommand({
8047
7664
  }
8048
7665
  });
8049
7666
 
7667
+ //#endregion
7668
+ //#region src/cli/contributions.ts
7669
+ const DINEWAY_PLUGIN_PACKAGE = /^(?:@dineway-ai\/plugin-|dineway-plugin-)/;
7670
+ const CLI_COMMAND_NAME = /^[a-z][a-z0-9-]*$/;
7671
+ async function discoverDinewayCliContributions(cwd = process.cwd()) {
7672
+ const projectPackage = await findProjectPackage(cwd);
7673
+ if (!projectPackage) return [];
7674
+ const { packagePath, project } = projectPackage;
7675
+ const requireFromProject = createRequire(packagePath);
7676
+ const packageNames = new Set([
7677
+ ...Object.keys(project.dependencies ?? {}),
7678
+ ...Object.keys(project.devDependencies ?? {}),
7679
+ ...Object.keys(project.optionalDependencies ?? {})
7680
+ ]);
7681
+ const contributions = [];
7682
+ for (const packageName of [...packageNames].filter(isDinewayPluginPackage).toSorted()) {
7683
+ const specifier = `${packageName}/cli`;
7684
+ let modulePath;
7685
+ try {
7686
+ modulePath = requireFromProject.resolve(specifier);
7687
+ } catch (error) {
7688
+ if (isMissingContribution(error)) continue;
7689
+ throw error;
7690
+ }
7691
+ const loaded = await import(pathToFileURL(modulePath).href);
7692
+ const value = loaded.dinewayCliContribution ?? loaded.default;
7693
+ if (!isDinewayCliContribution(value)) throw new Error(`${specifier} does not export a valid Dineway CLI contribution`);
7694
+ contributions.push({
7695
+ packageName,
7696
+ commands: value.commands
7697
+ });
7698
+ }
7699
+ return contributions;
7700
+ }
7701
+ async function findProjectPackage(from) {
7702
+ let directory = resolve(from);
7703
+ while (true) {
7704
+ const packagePath = join(directory, "package.json");
7705
+ const project = await readProjectPackage(packagePath);
7706
+ if (project) return {
7707
+ packagePath,
7708
+ project
7709
+ };
7710
+ const parent = dirname(directory);
7711
+ if (parent === directory) return null;
7712
+ directory = parent;
7713
+ }
7714
+ }
7715
+ function mergeDinewayCliContributions(builtIns, contributions) {
7716
+ const commands = { ...builtIns };
7717
+ for (const contribution of contributions) for (const [name, command] of Object.entries(contribution.commands).toSorted(([left], [right]) => left.localeCompare(right))) {
7718
+ if (commands[name]) throw new Error(`Dineway CLI command "${name}" from ${contribution.packageName} conflicts with an existing command`);
7719
+ commands[name] = command;
7720
+ }
7721
+ return commands;
7722
+ }
7723
+ async function readProjectPackage(path) {
7724
+ try {
7725
+ const parsed = JSON.parse(await readFile(path, "utf8"));
7726
+ return isRecord(parsed) ? parsed : null;
7727
+ } catch (error) {
7728
+ if (isFileNotFound(error)) return null;
7729
+ throw error;
7730
+ }
7731
+ }
7732
+ function isDinewayPluginPackage(packageName) {
7733
+ return DINEWAY_PLUGIN_PACKAGE.test(packageName);
7734
+ }
7735
+ function isDinewayCliContribution(value) {
7736
+ if (!isRecord(value) || !isRecord(value.commands)) return false;
7737
+ return Object.entries(value.commands).every(([name, command]) => CLI_COMMAND_NAME.test(name) && isRecord(command));
7738
+ }
7739
+ function isRecord(value) {
7740
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7741
+ }
7742
+ function isMissingContribution(error) {
7743
+ return error instanceof Error && "code" in error && (error.code === "MODULE_NOT_FOUND" || error.code === "ERR_PACKAGE_PATH_NOT_EXPORTED");
7744
+ }
7745
+ function isFileNotFound(error) {
7746
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
7747
+ }
7748
+
8050
7749
  //#endregion
8051
7750
  //#region src/cli/index.ts
8052
7751
  /**
@@ -8072,36 +7771,40 @@ const typesCommand = defineCommand({
8072
7771
  * - deploy: Deploy a standard Dineway Node server through a provider CLI
8073
7772
  * - plugin: Plugin management (init, bundle, validate, publish, login, logout)
8074
7773
  */
8075
- runMain(defineCommand({
8076
- meta: {
8077
- name: "dineway",
8078
- version: "0.0.0",
8079
- description: "CLI for Dineway Agentic Web builder"
8080
- },
8081
- subCommands: {
8082
- init: initCommand,
8083
- account: accountCommand,
8084
- types: typesCommand,
8085
- dev: devCommand,
8086
- deploy: deployCommand,
8087
- doctor: doctorCommand,
8088
- seed: seedCommand,
8089
- "export-seed": exportSeedCommand,
8090
- secrets: secretsCommand,
8091
- auth: authCommand,
8092
- login: loginCommand,
8093
- logout: logoutCommand,
8094
- whoami: whoamiCommand,
8095
- content: contentCommand,
8096
- schema: schemaCommand,
8097
- media: mediaCommand,
8098
- search: searchCommand,
8099
- seo: seoCommand,
8100
- taxonomy: taxonomyCommand,
8101
- menu: menuCommand,
8102
- plugin: pluginCommand
8103
- }
8104
- }));
7774
+ const builtInCommands = {
7775
+ init: initCommand,
7776
+ account: accountCommand,
7777
+ types: typesCommand,
7778
+ dev: devCommand,
7779
+ deploy: deployCommand,
7780
+ doctor: doctorCommand,
7781
+ seed: seedCommand,
7782
+ "export-seed": exportSeedCommand,
7783
+ secrets: secretsCommand,
7784
+ auth: authCommand,
7785
+ login: loginCommand,
7786
+ logout: logoutCommand,
7787
+ whoami: whoamiCommand,
7788
+ content: contentCommand,
7789
+ schema: schemaCommand,
7790
+ media: mediaCommand,
7791
+ search: searchCommand,
7792
+ seo: seoCommand,
7793
+ taxonomy: taxonomyCommand,
7794
+ menu: menuCommand,
7795
+ plugin: pluginCommand
7796
+ };
7797
+ async function createDinewayCliCommand(cwd = process.cwd()) {
7798
+ return defineCommand({
7799
+ meta: {
7800
+ name: "dineway",
7801
+ version: "0.0.0",
7802
+ description: "CLI for Dineway Agentic Web builder"
7803
+ },
7804
+ subCommands: mergeDinewayCliContributions(builtInCommands, await discoverDinewayCliContributions(cwd))
7805
+ });
7806
+ }
7807
+ createDinewayCliCommand().then((main) => runMain(main));
8105
7808
 
8106
7809
  //#endregion
8107
- export { };
7810
+ export { createDinewayCliCommand };