dineway 0.2.0 → 0.2.2
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.
- package/README.md +17 -17
- package/dist/activity-BDMlkpYY.mjs +79 -0
- package/dist/{activity-events-y6eT8UW6.mjs → activity-events-DEYmJhWd.mjs} +1 -77
- package/dist/{actor-CrObAPV0.mjs → actor-DA7yL9Yp.mjs} +1 -1
- package/dist/{allowed-origins-_oeyP6x-.mjs → allowed-origins-VLgv2PL1.mjs} +1 -1
- package/dist/api/route-utils.d.mts +9 -10
- package/dist/api/route-utils.mjs +14 -14
- package/dist/api/schemas/index.d.mts +2 -2
- package/dist/api/schemas/index.mjs +5 -5
- package/dist/{api-ByrU2q-Z.mjs → api-DsO87Qak.mjs} +51 -23
- package/dist/{api-tokens-CjVMQT-X.mjs → api-tokens-C4K9eocv.mjs} +1 -1
- package/dist/{apply-bZswmZPm.mjs → apply-DHT0l398.mjs} +19 -19
- package/dist/astro/index.d.mts +10 -11
- package/dist/astro/index.mjs +18 -5
- package/dist/astro/middleware/auth.d.mts +9 -19
- package/dist/astro/middleware/auth.mjs +6 -6
- package/dist/astro/middleware/redirect.mjs +5 -5
- package/dist/astro/middleware/request-context.mjs +2 -2
- package/dist/astro/middleware/seed.d.mts +1 -1
- package/dist/astro/middleware/seed.mjs +1 -1
- package/dist/astro/middleware/setup.mjs +1 -1
- package/dist/astro/middleware.mjs +142 -98
- package/dist/astro/routes/api/admin/allowed-domains/_domain_.mjs +6 -6
- package/dist/astro/routes/api/admin/allowed-domains/index.mjs +6 -6
- package/dist/astro/routes/api/admin/api-tokens/_id_.mjs +4 -4
- package/dist/astro/routes/api/admin/api-tokens/index.mjs +6 -6
- package/dist/astro/routes/api/admin/briefing.mjs +18 -18
- package/dist/astro/routes/api/admin/byline-fields/_slug_/usage.mjs +4 -4
- package/dist/astro/routes/api/admin/byline-fields/_slug_.mjs +9 -9
- package/dist/astro/routes/api/admin/byline-fields/index.mjs +9 -9
- package/dist/astro/routes/api/admin/byline-fields/reorder.mjs +9 -9
- package/dist/astro/routes/api/admin/bylines/_id_/index.mjs +16 -16
- package/dist/astro/routes/api/admin/bylines/_id_/translations.mjs +16 -16
- package/dist/astro/routes/api/admin/bylines/index.mjs +16 -16
- package/dist/astro/routes/api/admin/comments/_id_/status.mjs +12 -12
- package/dist/astro/routes/api/admin/comments/_id_.mjs +5 -5
- package/dist/astro/routes/api/admin/comments/bulk.mjs +10 -10
- package/dist/astro/routes/api/admin/comments/counts.mjs +5 -5
- package/dist/astro/routes/api/admin/comments/index.mjs +10 -10
- package/dist/astro/routes/api/admin/context/_id_/history.mjs +25 -24
- package/dist/astro/routes/api/admin/context/_id_/index.mjs +25 -24
- package/dist/astro/routes/api/admin/context/_id_/review.mjs +30 -29
- package/dist/astro/routes/api/admin/context/_id_/supersede.mjs +30 -29
- package/dist/astro/routes/api/admin/context/diff.mjs +29 -28
- package/dist/astro/routes/api/admin/context/index.mjs +30 -29
- package/dist/astro/routes/api/admin/context/stale.mjs +29 -28
- package/dist/astro/routes/api/admin/hitl-requests/_id_/index.mjs +25 -24
- package/dist/astro/routes/api/admin/hitl-requests/_id_/resolve.mjs +29 -28
- package/dist/astro/routes/api/admin/hitl-requests/index.mjs +29 -28
- package/dist/astro/routes/api/admin/hooks/exclusive/_hookName_.mjs +26 -25
- package/dist/astro/routes/api/admin/hooks/exclusive/index.mjs +2 -2
- package/dist/astro/routes/api/admin/oauth-clients/_id_.mjs +6 -6
- package/dist/astro/routes/api/admin/oauth-clients/index.mjs +6 -6
- package/dist/astro/routes/api/admin/plugins/_id_/disable.mjs +55 -51
- package/dist/astro/routes/api/admin/plugins/_id_/enable.mjs +55 -51
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +47 -44
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +54 -50
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +54 -50
- package/dist/astro/routes/api/admin/plugins/index.mjs +47 -44
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/icon.mjs +2 -2
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +47 -44
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +54 -50
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +47 -44
- package/dist/astro/routes/api/admin/plugins/updates.mjs +47 -44
- package/dist/astro/routes/api/admin/review-requests/_id_/index.mjs +5 -5
- package/dist/astro/routes/api/admin/review-requests/_id_/resolve.mjs +30 -65
- package/dist/astro/routes/api/admin/review-requests/index.d.mts +2 -1
- package/dist/astro/routes/api/admin/review-requests/index.mjs +58 -11
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +47 -44
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/thumbnail.mjs +2 -2
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +47 -44
- package/dist/astro/routes/api/admin/users/_id_/disable.mjs +2 -2
- package/dist/astro/routes/api/admin/users/_id_/enable.mjs +1 -1
- package/dist/astro/routes/api/admin/users/_id_/index.mjs +7 -7
- package/dist/astro/routes/api/admin/users/_id_/send-recovery.mjs +2 -2
- package/dist/astro/routes/api/admin/users/index.mjs +6 -6
- package/dist/astro/routes/api/auth/dev-bypass.mjs +4 -4
- package/dist/astro/routes/api/auth/invite/accept.mjs +1 -1
- package/dist/astro/routes/api/auth/invite/complete.mjs +10 -10
- package/dist/astro/routes/api/auth/invite/index.mjs +7 -7
- package/dist/astro/routes/api/auth/invite/register-options.mjs +9 -9
- package/dist/astro/routes/api/auth/logout.mjs +2 -2
- package/dist/astro/routes/api/auth/magic-link/send.mjs +9 -9
- package/dist/astro/routes/api/auth/magic-link/verify.mjs +2 -2
- package/dist/astro/routes/api/auth/me.mjs +8 -8
- package/dist/astro/routes/api/auth/mode.mjs +1 -1
- package/dist/astro/routes/api/auth/oauth/_provider_/callback.mjs +2 -2
- package/dist/astro/routes/api/auth/oauth/_provider_.mjs +2 -2
- package/dist/astro/routes/api/auth/passkey/_id_.mjs +6 -6
- package/dist/astro/routes/api/auth/passkey/index.mjs +1 -1
- package/dist/astro/routes/api/auth/passkey/options.mjs +11 -11
- package/dist/astro/routes/api/auth/passkey/register/options.mjs +9 -9
- package/dist/astro/routes/api/auth/passkey/register/verify.mjs +10 -10
- package/dist/astro/routes/api/auth/passkey/verify.mjs +10 -10
- package/dist/astro/routes/api/auth/setup-token/index.mjs +5 -5
- package/dist/astro/routes/api/auth/setup-token/verify.mjs +2 -2
- package/dist/astro/routes/api/auth/signup/complete.mjs +10 -10
- package/dist/astro/routes/api/auth/signup/request.mjs +9 -9
- package/dist/astro/routes/api/auth/signup/verify.mjs +1 -1
- package/dist/astro/routes/api/comments/_collection_/_contentId_/index.mjs +13 -13
- package/dist/astro/routes/api/comments/_collection_/_contentId_/reactions.mjs +12 -12
- package/dist/astro/routes/api/content/_collection_/_id_/compare.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/discard-draft.mjs +6 -5
- package/dist/astro/routes/api/content/_collection_/_id_/duplicate.mjs +6 -5
- package/dist/astro/routes/api/content/_collection_/_id_/permanent.mjs +6 -5
- package/dist/astro/routes/api/content/_collection_/_id_/preview-url.mjs +10 -10
- package/dist/astro/routes/api/content/_collection_/_id_/publish.mjs +49 -21
- package/dist/astro/routes/api/content/_collection_/_id_/restore.mjs +6 -5
- package/dist/astro/routes/api/content/_collection_/_id_/revisions.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/schedule.mjs +67 -33
- package/dist/astro/routes/api/content/_collection_/_id_/terms/_taxonomy_.mjs +14 -14
- package/dist/astro/routes/api/content/_collection_/_id_/translations.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/_id_/unpublish.mjs +6 -5
- package/dist/astro/routes/api/content/_collection_/_id_.mjs +11 -10
- package/dist/astro/routes/api/content/_collection_/authors.mjs +2 -2
- package/dist/astro/routes/api/content/_collection_/index.mjs +11 -10
- package/dist/astro/routes/api/content/_collection_/trash.mjs +7 -7
- package/dist/astro/routes/api/dashboard.mjs +8 -8
- package/dist/astro/routes/api/dev/emails.mjs +2 -2
- package/dist/astro/routes/api/health.mjs +1 -1
- package/dist/astro/routes/api/import/probe.d.mts +3 -3
- package/dist/astro/routes/api/import/probe.mjs +19 -19
- package/dist/astro/routes/api/import/wordpress/analyze.mjs +8 -8
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +8 -9
- package/dist/astro/routes/api/import/wordpress/execute.mjs +35 -34
- package/dist/astro/routes/api/import/wordpress/media.d.mts +8 -9
- package/dist/astro/routes/api/import/wordpress/media.mjs +12 -12
- package/dist/astro/routes/api/import/wordpress/prepare.mjs +34 -33
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +9 -10
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.mjs +31 -30
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.d.mts +1 -1
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +22 -22
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +8 -9
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +40 -39
- package/dist/astro/routes/api/integrations/_provider_.d.mts +8 -0
- package/dist/astro/routes/api/integrations/_provider_.mjs +51 -0
- package/dist/astro/routes/api/integrations/index.d.mts +7 -0
- package/dist/astro/routes/api/integrations/index.mjs +24 -0
- package/dist/astro/routes/api/manifest.mjs +4 -4
- package/dist/astro/routes/api/mcp.mjs +474 -167
- package/dist/astro/routes/api/media/_id_/confirm.mjs +10 -10
- package/dist/astro/routes/api/media/_id_.mjs +8 -8
- package/dist/astro/routes/api/media/file/_...key_.mjs +1 -1
- package/dist/astro/routes/api/media/providers/_providerId_/_itemId_.mjs +2 -2
- package/dist/astro/routes/api/media/providers/_providerId_/index.mjs +2 -2
- package/dist/astro/routes/api/media/providers/index.mjs +2 -2
- package/dist/astro/routes/api/media/upload-url.mjs +11 -11
- package/dist/astro/routes/api/media.mjs +14 -14
- package/dist/astro/routes/api/menus/_name_/items/_id_.mjs +30 -29
- package/dist/astro/routes/api/menus/_name_/items.mjs +30 -29
- package/dist/astro/routes/api/menus/_name_/reorder.mjs +30 -29
- package/dist/astro/routes/api/menus/_name_/translations.mjs +30 -29
- package/dist/astro/routes/api/menus/_name_.mjs +30 -29
- package/dist/astro/routes/api/menus/index.mjs +30 -29
- package/dist/astro/routes/api/oauth/authorize.mjs +7 -7
- package/dist/astro/routes/api/oauth/device/authorize.mjs +6 -6
- package/dist/astro/routes/api/oauth/device/code.mjs +9 -9
- package/dist/astro/routes/api/oauth/device/token.mjs +8 -8
- package/dist/astro/routes/api/oauth/register.mjs +4 -4
- package/dist/astro/routes/api/oauth/token/refresh.mjs +6 -6
- package/dist/astro/routes/api/oauth/token/revoke.mjs +6 -6
- package/dist/astro/routes/api/oauth/token.mjs +6 -6
- package/dist/astro/routes/api/openapi.json.mjs +4 -4
- package/dist/astro/routes/api/plugins/_pluginId_/_...path_.mjs +20 -5
- package/dist/astro/routes/api/redirects/404s/index.mjs +10 -10
- package/dist/astro/routes/api/redirects/404s/summary.mjs +10 -10
- package/dist/astro/routes/api/redirects/_id_.mjs +31 -30
- package/dist/astro/routes/api/redirects/index.mjs +31 -30
- package/dist/astro/routes/api/revisions/_revisionId_/index.mjs +2 -2
- package/dist/astro/routes/api/revisions/_revisionId_/restore.mjs +2 -2
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +49 -45
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +49 -45
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +49 -45
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +49 -45
- package/dist/astro/routes/api/schema/collections/index.mjs +49 -45
- package/dist/astro/routes/api/schema/index.mjs +8 -8
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +47 -44
- package/dist/astro/routes/api/schema/orphans/index.mjs +47 -44
- package/dist/astro/routes/api/search/enable.mjs +11 -11
- package/dist/astro/routes/api/search/index.mjs +10 -10
- package/dist/astro/routes/api/search/rebuild.mjs +11 -11
- package/dist/astro/routes/api/search/stats.mjs +6 -6
- package/dist/astro/routes/api/search/suggest.mjs +10 -10
- package/dist/astro/routes/api/sections/_slug_.mjs +30 -29
- package/dist/astro/routes/api/sections/index.mjs +30 -29
- package/dist/astro/routes/api/settings/email.mjs +3 -3
- package/dist/astro/routes/api/settings.mjs +30 -29
- package/dist/astro/routes/api/setup/admin-verify.mjs +12 -12
- package/dist/astro/routes/api/setup/admin.mjs +10 -10
- package/dist/astro/routes/api/setup/dev-bypass.mjs +25 -25
- package/dist/astro/routes/api/setup/dev-reset.mjs +1 -1
- package/dist/astro/routes/api/setup/index.mjs +27 -27
- package/dist/astro/routes/api/setup/status.mjs +3 -3
- package/dist/astro/routes/api/snapshot.mjs +6 -6
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_/translations.mjs +33 -32
- package/dist/astro/routes/api/taxonomies/_name_/terms/_slug_.mjs +33 -32
- package/dist/astro/routes/api/taxonomies/_name_/terms/index.mjs +33 -32
- package/dist/astro/routes/api/taxonomies/index.mjs +33 -32
- package/dist/astro/routes/api/themes/preview.mjs +4 -4
- package/dist/astro/routes/api/typegen.mjs +4 -4
- package/dist/astro/routes/api/well-known/auth.mjs +2 -2
- package/dist/astro/routes/api/well-known/oauth-authorization-server.mjs +3 -3
- package/dist/astro/routes/api/well-known/oauth-protected-resource.mjs +3 -3
- package/dist/astro/routes/api/widget-areas/_name_/reorder.mjs +29 -28
- package/dist/astro/routes/api/widget-areas/_name_/widgets/_id_.mjs +30 -29
- package/dist/astro/routes/api/widget-areas/_name_/widgets.mjs +30 -29
- package/dist/astro/routes/api/widget-areas/_name_.mjs +27 -26
- package/dist/astro/routes/api/widget-areas/index.mjs +30 -29
- package/dist/astro/routes/api/widget-components.mjs +2 -2
- package/dist/astro/routes/robots.txt.mjs +6 -6
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +9 -9
- package/dist/astro/routes/sitemap.xml.mjs +7 -7
- package/dist/astro/session-driver.d.mts +1 -1
- package/dist/astro/session-driver.mjs +2 -2
- package/dist/astro/types.d.mts +13 -12
- package/dist/{audit-ByVKIwWF.mjs → audit-BzhwRVym.mjs} +1 -1
- package/dist/auth/providers/github.d.mts +1 -1
- package/dist/auth/providers/google.d.mts +1 -1
- package/dist/{auth-control-guard-CqeUmQsv.mjs → auth-control-guard-B_Dx-epW.mjs} +1 -1
- package/dist/{authorize-CcBxcHtz.mjs → authorize-D6MkRtPw.mjs} +1 -1
- package/dist/{briefing-BJaJI8dm.mjs → briefing-Cl70iEH5.mjs} +1 -1
- package/dist/{briefing-CawZ59CY.mjs → briefing-GFhLTLJo.mjs} +7 -7
- package/dist/{bundle-7C_DpkEq.mjs → bundle-CIrUlrkQ.mjs} +2 -2
- package/dist/{byline-e51-fWRs.mjs → byline-BJa1MyKB.mjs} +6 -6
- package/dist/{byline-fields-D-N5qVIG.mjs → byline-fields-BKzA0fJU.mjs} +1 -1
- package/dist/{byline-fields-CUvbslGc.d.mts → byline-fields-DxMpdtsZ.d.mts} +57 -47
- package/dist/{byline-fields-DOJO16Gm.mjs → byline-fields-yT4herdN.mjs} +10 -4
- package/dist/{byline-registry-87nNnYl4.mjs → byline-registry-Ba54M74c.mjs} +2 -2
- package/dist/{bylines-CMQNCNFh.mjs → bylines-B53Saxdb.mjs} +3 -3
- package/dist/{bylines-y4LO-dTj.mjs → bylines-kixFoaRV.mjs} +5 -5
- package/dist/{cache-C-FNTFYK.mjs → cache-fpisdt5Q.mjs} +2 -2
- package/dist/{chunks-CxBt-GxR.mjs → chunks-DX_b8_4Z.mjs} +1 -1
- package/dist/cli/index.d.mts +29 -1
- package/dist/cli/index.mjs +1527 -1222
- package/dist/cli/plugin-kit.d.mts +75 -0
- package/dist/cli/plugin-kit.mjs +4 -0
- package/dist/client/external-auth-headers.d.mts +1 -1
- package/dist/client/index.d.mts +27 -52
- package/dist/client/index.mjs +34 -7
- package/dist/client-factory-DEtHApCc.mjs +323 -0
- package/dist/{comment-DlA4iChg.mjs → comment-B88XcvU1.mjs} +1 -1
- package/dist/{comment-reaction-DfFao1ST.mjs → comment-reaction-Ckn6vCmB.mjs} +1 -1
- package/dist/{comments-DApFGMsP.mjs → comments-BDXsbL7z.mjs} +1 -1
- package/dist/{content-Bh4BDjv8.mjs → content-Cbl8HbIR.mjs} +2 -2
- package/dist/{content-refresh-CgutSUfO.mjs → content-refresh-BmkilU5M.mjs} +5 -5
- package/dist/{context-CK4ym2Sf.mjs → context-AJNd0tcG.mjs} +2 -2
- package/dist/{context-C5W3G18y.mjs → context-DT0tIsfw.mjs} +24 -13
- package/dist/{context-route-helpers-MASyGgWf.mjs → context-route-helpers-DhP--tTq.mjs} +2 -2
- package/dist/{dashboard-DQkVT7Ei.mjs → dashboard-YvDEYbS3.mjs} +4 -4
- package/dist/db/index.d.mts +3 -3
- package/dist/db/index.mjs +1 -1
- package/dist/db/libsql.d.mts +1 -1
- package/dist/db/postgres.d.mts +1 -1
- package/dist/db/sqlite.d.mts +1 -1
- package/dist/{device-flow-BnqRH-T9.mjs → device-flow-BKKeobcV.mjs} +5 -5
- package/dist/{enrich-DIFIFA8O.mjs → enrich-Ck9tfMqe.mjs} +1 -1
- package/dist/{error-CytFlgcd.mjs → error-BW9f_ynq.mjs} +16 -2
- package/dist/{experimental-workflows-BMsUnIAw.mjs → experimental-workflows-CgO73K4e.mjs} +1 -1
- package/dist/{field-defs-cache-DRNdhBJv.mjs → field-defs-cache-CiXP3eO3.mjs} +4 -4
- package/dist/{fts-manager-Crzypjqu.mjs → fts-manager-Zo9uhGel.mjs} +1 -1
- package/dist/{hitl-requests-B4Vbcmaq.mjs → hitl-requests-Bee8Cjj8.mjs} +2 -2
- package/dist/{hitl-route-helpers-hEVzvP8l.mjs → hitl-route-helpers-DtHaKGgm.mjs} +5 -5
- package/dist/{import-Bh9biJfx.mjs → import-C-zxnT89.mjs} +7 -7
- package/dist/{index-D4y6NGiy.d.mts → index-DybFmYtZ.d.mts} +79 -3
- package/dist/index.d.mts +15 -17
- package/dist/index.mjs +83 -78
- package/dist/integration-credentials-D3ZneN0L.mjs +49 -0
- package/dist/{jsonld-Dq0gsnOH.d.mts → jsonld-CoTa8I1a.d.mts} +2 -2
- package/dist/{load-DmYeniXl.mjs → load-Cuiyh8LT.mjs} +1 -1
- package/dist/{loader-CjWqjweO.mjs → loader-WHf22FwU.mjs} +2 -2
- package/dist/{manifest-schema-BxVPYMJR.mjs → manifest-schema-DKa3ZaJL.mjs} +6 -2
- package/dist/{mcp-reviewed-executor-TwVbMzG6.mjs → mcp-reviewed-executor-DnlF7mIL.mjs} +6 -3
- package/dist/media/image-endpoint.mjs +1 -1
- package/dist/media/index.d.mts +1 -1
- package/dist/media/index.mjs +2 -2
- package/dist/media/local-runtime.d.mts +10 -11
- package/dist/media/local-runtime.mjs +2 -2
- package/dist/{media-DMj0grzC.mjs → media-B4m-cnmj.mjs} +1 -1
- package/dist/{media-allowlist-BlVCSzHx.mjs → media-allowlist-BQyIBPzd.mjs} +1 -1
- package/dist/{menus-TaW1gKnk.mjs → menus-LYALU2Mf.mjs} +2 -2
- package/dist/{menus-xvsnVZXw.mjs → menus-oYfViqfE.mjs} +6 -6
- package/dist/{oauth-authorization-DiaGvRVz.mjs → oauth-authorization-CX7aa8Ph.mjs} +5 -5
- package/dist/{oauth-clients-Dl9E8dEM.mjs → oauth-clients-D9Gg0WjF.mjs} +2 -2
- package/dist/object-cache/memory.d.mts +1 -1
- package/dist/output-13u4dvu-.mjs +68 -0
- package/dist/page/index.d.mts +9 -3
- package/dist/page/index.mjs +1 -1
- package/dist/{parse-CbymENaR.mjs → parse-B-Y84oJl.mjs} +1 -1
- package/dist/plugin-utils.d.mts +8 -9
- package/dist/plugins/adapt-sandbox-entry.d.mts +8 -9
- package/dist/plugins/adapt-sandbox-entry.mjs +3 -1
- package/dist/{plugins-BolVa-tl.mjs → plugins-B7-4Okii.mjs} +95 -10
- package/dist/portable-text-Ehqj3I_y.d.mts +45 -0
- package/dist/{preview-vSVg6fwG.mjs → preview-BMIReQGc.mjs} +2 -2
- package/dist/{provider-loader-MD8zAbnH.d.mts → provider-loader-BIbIiz0P.d.mts} +1 -1
- package/dist/{public-url-gsp3oUbu.d.mts → public-url-DYISdY_R.d.mts} +1 -1
- package/dist/publication-authorization-BVQ5rCAF.mjs +985 -0
- package/dist/{query-CfRCdaL3.mjs → query-C_m8sn-S.mjs} +13 -13
- package/dist/{query-jaAmw8E1.mjs → query-DNczixZr.mjs} +3 -3
- package/dist/{rate-limit-ZLdyWoZF.mjs → rate-limit-oUGexQO1.mjs} +2 -2
- package/dist/{redirect-BAjt4Z6b.mjs → redirect-DoDgOWIA.mjs} +2 -2
- package/dist/{redirects-BTdwpQtE.mjs → redirects-CrP6U4hZ.mjs} +4 -4
- package/dist/{redirects-CA-xylPG.mjs → redirects-E_B6hvuz.mjs} +18 -10
- package/dist/{registry-CtyVedcN.mjs → registry-SEJqFliS.mjs} +24 -10
- package/dist/{request-meta-CUypToDJ.mjs → request-meta-DHFIzWcf.mjs} +1 -1
- package/dist/review-payload-BysRWpxS.mjs +111 -0
- package/dist/{review-requests-DUyEmtmG.mjs → review-requests-CObSuTYU.mjs} +1 -1
- package/dist/review-requests-Cu9051_B.mjs +142 -0
- package/dist/review-route-helpers-Dp_7PH8r.mjs +41 -0
- package/dist/{runner-1tC2tIRV.d.mts → runner-Bcy5w0vj.d.mts} +1 -1
- package/dist/{runner-DB7CRfOF.mjs → runner-BfWbi9YY.mjs} +1166 -447
- package/dist/runtime.d.mts +9 -10
- package/dist/runtime.mjs +3 -3
- package/dist/schema-Dk_N31V4.mjs +8 -0
- package/dist/{search-DSaLgEFI.mjs → search-BXEQepxL.mjs} +3 -3
- package/dist/{secrets-CDZo2k8L.mjs → secrets-CpGpyGcO.mjs} +1 -1
- package/dist/{sections-PVVXHGfp.mjs → sections-DxvEF6Bj.mjs} +2 -2
- package/dist/seed/index.d.mts +2 -2
- package/dist/seed/index.mjs +19 -19
- package/dist/seo/index.d.mts +1 -1
- package/dist/seo/index.mjs +1 -1
- package/dist/{seo-CQG3GCHQ.mjs → seo-D1RiaooQ.mjs} +1 -1
- package/dist/service-CY0aH6eI.mjs +347 -0
- package/dist/{service-DiN4ZuPp.mjs → service-QdG0YHws.mjs} +2 -2
- package/dist/{settings-i4VPP8bT.mjs → settings-Dje-PHXo.mjs} +2 -2
- package/dist/{settings-BbqgHWpU.mjs → settings-SVDAB7_W.mjs} +5 -5
- package/dist/{site-activity-Dy3FROs0.mjs → site-activity-BVK40wCy.mjs} +1 -1
- package/dist/{site-context-DQiL8Fl5.mjs → site-context-CzaEGizi.mjs} +565 -129
- package/dist/stable-hash-DshcXvRE.mjs +19 -0
- package/dist/storage/local.d.mts +1 -1
- package/dist/storage/local.mjs +1 -1
- package/dist/storage/s3.d.mts +1 -1
- package/dist/storage/s3.mjs +1 -1
- package/dist/store-CCfMD6nw.mjs +262 -0
- package/dist/{taxonomies-9s-v03H-.mjs → taxonomies-DuhGjBa8.mjs} +3 -3
- package/dist/{taxonomies-DRiixp_E.mjs → taxonomies-WVnFki0o.mjs} +6 -6
- package/dist/{taxonomy-Dvsw6BOs.mjs → taxonomy-CCo2plmV.mjs} +2 -2
- package/dist/{types-DU0bU2Wu.d.mts → types-BFeoAIHl.d.mts} +596 -2
- package/dist/{types-Dq1qT19x.mjs → types-Bshd_6wQ.mjs} +1 -1
- package/dist/{runtime-BOeiQsCv.d.mts → types-CJezJeNE.d.mts} +3561 -1355
- package/dist/{types-4garmdWa.mjs → types-CK0r-Xq4.mjs} +5 -2
- package/dist/{types-C_ly5eTp.d.mts → types-GtwhHAg8.d.mts} +1 -1
- package/dist/{types-DW_-sFyz.d.mts → types-ywmuDETA.d.mts} +2 -0
- package/dist/ui/server-runtime.d.mts +12 -14
- package/dist/ui/server-runtime.mjs +21 -21
- package/dist/{user-BIZPzCb9.mjs → user-Bcvr5ihC.mjs} +2 -2
- package/dist/{utils-CMDhr5Xd.mjs → utils-B7gemXz5.mjs} +1 -1
- package/dist/{validate-CZvb-BKB.d.mts → validate-YiX3rly5.d.mts} +59 -5
- package/dist/{validate-DNs0pTSc.mjs → validate-qKzI3BCv.mjs} +2 -2
- package/dist/version-DEAV9k58.mjs +6 -0
- package/dist/{widgets-f0ibZUIq.mjs → widgets-DyGcotPM.mjs} +3 -3
- package/dist/{wordpress-slugs-BhHJX8XE.mjs → wordpress-slugs-D_LfQCan.mjs} +1 -1
- package/dist/{zod-generator-B7TMjyFJ.mjs → zod-generator-Cr3SiHsc.mjs} +2 -2
- package/package.json +16 -6
- package/dist/hitl-Hw1RmWOq.mjs +0 -677
- package/dist/review-requests-C9Ff11Vq.mjs +0 -79
- package/dist/schema-DXdYj4R3.mjs +0 -8
- package/dist/types-C46EvNdc.d.mts +0 -56
- package/dist/types-C8XW6L19.d.mts +0 -1468
- package/dist/version-BTvfRGc6.mjs +0 -6
- /package/dist/{adapters-NVSs4UH7.d.mts → adapters-DnMDRQ4e.d.mts} +0 -0
- /package/dist/{api-tokens-2r7OWi_I.mjs → api-tokens-D4tcY8R6.mjs} +0 -0
- /package/dist/{challenge-store-Bjlepa2E.mjs → challenge-store-BMkL2kmt.mjs} +0 -0
- /package/dist/{components-Bclf98Be.mjs → components-D0w9MzQe.mjs} +0 -0
- /package/dist/{context-types-DqTo6C79.mjs → context-types-eBuMDAg-.mjs} +0 -0
- /package/dist/{cron-cKcNTix_.mjs → cron-D_c3WUPN.mjs} +0 -0
- /package/dist/{default--K40jqm0.mjs → default-BxoI3SoK.mjs} +0 -0
- /package/dist/{email-console-ChgKFO8-.mjs → email-console-BEMwuxhV.mjs} +0 -0
- /package/dist/{escape-Bd4P_xKg.mjs → escape-smSUOb7u.mjs} +0 -0
- /package/dist/{hash-BPmX6P8I.mjs → hash-mxxx2oPW.mjs} +0 -0
- /package/dist/{media-url-lEKSrpr9.mjs → media-url-B6d-yVI6.mjs} +0 -0
- /package/dist/{mime-CA7Lgovc.mjs → mime-C0vOHFm7.mjs} +0 -0
- /package/dist/{mode-C4CIyjtR.mjs → mode-DPC7segX.mjs} +0 -0
- /package/dist/{normalize-5ek_z7de.mjs → normalize-Cyt7JdF7.mjs} +0 -0
- /package/dist/{oauth-state-store-CNoXafF7.mjs → oauth-state-store-DPVzeoyY.mjs} +0 -0
- /package/dist/{oauth-user-lookup-Ygw4bNyt.mjs → oauth-user-lookup-3E5jlXrr.mjs} +0 -0
- /package/dist/{parser-C1nYd-81.mjs → parser-DX_c5iG3.mjs} +0 -0
- /package/dist/{passkey-config-BUwu8BA3.mjs → passkey-config-DE_0EGEP.mjs} +0 -0
- /package/dist/{patterns-BhAvqO0S.mjs → patterns-CoqlXUvk.mjs} +0 -0
- /package/dist/{placeholder-uvNRZBX9.d.mts → placeholder-C6ZjZcmJ.d.mts} +0 -0
- /package/dist/{portable-text-dA_UlJwW.mjs → portable-text-BASHceim.mjs} +0 -0
- /package/dist/{process-cache-7xgAO9JY.mjs → process-cache-CCDgti7j.mjs} +0 -0
- /package/dist/{provider-loader-D0w9dqjm.mjs → provider-loader-BeQcnPN1.mjs} +0 -0
- /package/dist/{public-url-BEz_sM9n.mjs → public-url-BA-7LKvQ.mjs} +0 -0
- /package/dist/{redirect-wphRBv4y.mjs → redirect-CZFAqHLr.mjs} +0 -0
- /package/dist/{request-cache-D9GQBxMP.mjs → request-cache-CBFjCwSt.mjs} +0 -0
- /package/dist/{resolve-Cb6Dbz-C.mjs → resolve-B6BpqwAR.mjs} +0 -0
- /package/dist/{seo-qa7Dn0jK.mjs → seo-BLszyJxd.mjs} +0 -0
- /package/dist/{seo-contributions-C1EnikHB.mjs → seo-contributions-Ci8_12_r.mjs} +0 -0
- /package/dist/{session-user-CAKImna3.mjs → session-user-BwliEFp0.mjs} +0 -0
- /package/dist/{setup-complete-B8fl-mjV.mjs → setup-complete-PvF8I7Oh.mjs} +0 -0
- /package/dist/{setup-nonce-CoyjILvW.mjs → setup-nonce-DkpYROqg.mjs} +0 -0
- /package/dist/{sidecar-client-C3mqXg4C.mjs → sidecar-client-dt5fv23q.mjs} +0 -0
- /package/dist/{site-url-BML3GQYm.mjs → site-url-DfYDZyWl.mjs} +0 -0
- /package/dist/{ssrf-DWe8jwAp.mjs → ssrf-BZHvtFEv.mjs} +0 -0
- /package/dist/{status-DANd8ERo.mjs → status-BN40hMpr.mjs} +0 -0
- /package/dist/{tokens-1RK1n6ED.mjs → tokens-DIzhPT80.mjs} +0 -0
- /package/dist/{transaction-C2EOMTJA.mjs → transaction-0LEAAy8R.mjs} +0 -0
- /package/dist/{transport-CcJu3Co8.d.mts → transport-BZ3aqdu7.d.mts} +0 -0
- /package/dist/{transport-BNmCqLu7.mjs → transport-C46KHLMP.mjs} +0 -0
- /package/dist/{trusted-proxy-DTXQ2IMv.mjs → trusted-proxy-czZuby6G.mjs} +0 -0
- /package/dist/{types-B3t5cxhU.mjs → types-B7TDDFMG.mjs} +0 -0
- /package/dist/{types-CDpH6MA4.d.mts → types-BsTXSvKG.d.mts} +0 -0
- /package/dist/{types-DF7ZuGGk.d.mts → types-CDcuRd4z.d.mts} +0 -0
- /package/dist/{types-Rc3gQqqw.d.mts → types-S9VIopdY.d.mts} +0 -0
- /package/dist/{types-POH8PQZh.d.mts → types-YxFz4lDr.d.mts} +0 -0
- /package/dist/{types-C5CVgwsV.mjs → types-gvNBqijt.mjs} +0 -0
- /package/dist/{url-CpyFDFpO.mjs → url-D282S1Zw.mjs} +0 -0
- /package/dist/{url-Cac7a9QZ.mjs → url-guoXQQcV.mjs} +0 -0
package/dist/cli/index.mjs
CHANGED
|
@@ -1,322 +1,57 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { i as runMigrations, n as getMigrationStatus, t as getMigrationFingerprint } from "../runner-BfWbi9YY.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-
|
|
9
|
-
import { o as isMissingTableError } from "../loader-
|
|
8
|
+
import "../types-Bshd_6wQ.mjs";
|
|
9
|
+
import { o as isMissingTableError } from "../loader-WHf22FwU.mjs";
|
|
10
10
|
import "../object-cache-D51qOseS.mjs";
|
|
11
11
|
import { n as slugify } from "../slugify-KSsVD_D3.mjs";
|
|
12
|
-
import { t as ContentRepository } from "../content-
|
|
13
|
-
import { t as MediaRepository } from "../media-
|
|
14
|
-
import { t as TaxonomyRepository } from "../taxonomy-
|
|
12
|
+
import { t as ContentRepository } from "../content-Cbl8HbIR.mjs";
|
|
13
|
+
import { t as MediaRepository } from "../media-B4m-cnmj.mjs";
|
|
14
|
+
import { t as TaxonomyRepository } from "../taxonomy-CCo2plmV.mjs";
|
|
15
15
|
import { t as OptionsRepository } from "../options-DQPTH5n0.mjs";
|
|
16
|
-
import "../redirect-
|
|
17
|
-
import "../request-cache-
|
|
18
|
-
import "../byline-registry-
|
|
19
|
-
import "../field-defs-cache-
|
|
20
|
-
import { t as BylineRepository } from "../byline-
|
|
21
|
-
import "../content-refresh-
|
|
22
|
-
import "../fts-manager-
|
|
23
|
-
import { n as SchemaRegistry } from "../registry-
|
|
24
|
-
import "../settings-
|
|
25
|
-
import { n as VERSION } from "../version-
|
|
26
|
-
import {
|
|
27
|
-
import "../
|
|
28
|
-
import {
|
|
29
|
-
import { t as
|
|
30
|
-
import { t as
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
16
|
+
import "../redirect-DoDgOWIA.mjs";
|
|
17
|
+
import "../request-cache-CBFjCwSt.mjs";
|
|
18
|
+
import "../byline-registry-Ba54M74c.mjs";
|
|
19
|
+
import "../field-defs-cache-CiXP3eO3.mjs";
|
|
20
|
+
import { t as BylineRepository } from "../byline-BJa1MyKB.mjs";
|
|
21
|
+
import "../content-refresh-BmkilU5M.mjs";
|
|
22
|
+
import "../fts-manager-Zo9uhGel.mjs";
|
|
23
|
+
import { n as SchemaRegistry } from "../registry-SEJqFliS.mjs";
|
|
24
|
+
import "../settings-SVDAB7_W.mjs";
|
|
25
|
+
import { n as VERSION } from "../version-DEAV9k58.mjs";
|
|
26
|
+
import { i as parseEncryptionKeys, n as fingerprintKey, r as generateEncryptionKey, t as DinewaySecretsError } from "../secrets-CpGpyGcO.mjs";
|
|
27
|
+
import { t as IntegrationCredentialStore } from "../store-CCfMD6nw.mjs";
|
|
28
|
+
import { a as reconcileManifestAccess, i as pluginManifestSchema } from "../manifest-schema-DKa3ZaJL.mjs";
|
|
29
|
+
import { n as resolveAndValidateExternalUrl, t as SsrfError } from "../ssrf-BZHvtFEv.mjs";
|
|
30
|
+
import { t as validateSeed } from "../validate-qKzI3BCv.mjs";
|
|
31
|
+
import { t as applySeed } from "../apply-DHT0l398.mjs";
|
|
32
|
+
import { createHeaderAwareFetch, isRedirectResponse, resolveCustomHeaders } from "../client/external-auth-headers.mjs";
|
|
33
|
+
import { t as convertDataForRead } from "../portable-text-BASHceim.mjs";
|
|
34
|
+
import { _ as readForgewayCredentials, a as InvalidForgewayAccountCredentialsError, c as getForgewayDinewayAccount, d as normalizeForgewayEmail, f as readStoredForgewayAccount, g as listForgewayCredentials, h as sendForgewayAccountLoginLink, i as DEFAULT_FORGEWAY_API_BASE_URL, l as normalizeDinewayWebUrl, m as removeStoredForgewayAccount, n as DinewayToolsService, o as completeForgewayAccountLogin, p as refreshForgewayAccountCredentials, r as DEFAULT_DINEWAY_WEB_URL, s as createForgewayAccountClient, t as DinewayToolsError, u as normalizeForgewayApiUrl, v as writeForgewayCredentials } from "../service-CY0aH6eI.mjs";
|
|
35
|
+
import { n as output, t as configureOutputMode } from "../output-13u4dvu-.mjs";
|
|
36
|
+
import { _ as saveMarketplaceCredential, a as ensureProjectGitignoreDinewayLocalState, c as resolveDinewayBaseUrl, d as getCredentials, f as getMarketplaceCredential, g as saveCredentials, h as resolveCredentialKey, i as DEFAULT_DINEWAY_URL, l as resolveDinewayToken, m as removeMarketplaceCredential, n as connectionArgs, o as ensureProjectGitignoreEntry, p as removeCredentials, r as createClientFromArgs, s as readProjectEnv, u as upsertProjectEnv } from "../client-factory-DEtHApCc.mjs";
|
|
34
37
|
import { LocalStorage } from "../storage/local.mjs";
|
|
35
|
-
import { r as fileExists$11, t as bundleCommand } from "../bundle-
|
|
36
|
-
import {
|
|
38
|
+
import { r as fileExists$11, t as bundleCommand } from "../bundle-CIrUlrkQ.mjs";
|
|
39
|
+
import { createRequire } from "node:module";
|
|
40
|
+
import { createHash } from "node:crypto";
|
|
37
41
|
import { sql } from "kysely";
|
|
38
42
|
import { createGzipDecoder, unpackTar } from "modern-tar";
|
|
39
|
-
import { createReadStream, existsSync,
|
|
43
|
+
import { createReadStream, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
40
44
|
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
45
|
+
import sax from "sax";
|
|
46
|
+
import { pathToFileURL } from "node:url";
|
|
41
47
|
import { defineCommand, runCommand, runMain } from "citty";
|
|
42
48
|
import { createInterface } from "node:readline/promises";
|
|
43
49
|
import consola, { consola as consola$1 } from "consola";
|
|
44
50
|
import pc from "picocolors";
|
|
45
|
-
import {
|
|
46
|
-
import { access, chmod, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
47
|
-
import { homedir } from "node:os";
|
|
51
|
+
import { access, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
48
52
|
import { spawn } from "node:child_process";
|
|
53
|
+
import { parse } from "parse5";
|
|
49
54
|
|
|
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
|
-
//#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;
|
|
135
|
-
const DEFAULT_FORGEWAY_API_BASE_URL = "https://api.dineway.ai";
|
|
136
|
-
const DEFAULT_DINEWAY_WEB_URL = "https://dineway.ai";
|
|
137
|
-
function normalizeForgewayApiUrl(value) {
|
|
138
|
-
return (value || DEFAULT_FORGEWAY_API_BASE_URL).replace(TRAILING_SLASH_PATTERN$2, "");
|
|
139
|
-
}
|
|
140
|
-
function normalizeDinewayWebUrl(value) {
|
|
141
|
-
return (value || DEFAULT_DINEWAY_WEB_URL).replace(TRAILING_SLASH_PATTERN$2, "");
|
|
142
|
-
}
|
|
143
|
-
function normalizeForgewayEmail(value) {
|
|
144
|
-
return value?.trim().toLowerCase() ?? "";
|
|
145
|
-
}
|
|
146
|
-
function isShadowForgewayEmail(email) {
|
|
147
|
-
return Boolean(email && SHADOW_EMAIL_PATTERN$2.test(email));
|
|
148
|
-
}
|
|
149
|
-
function createForgewayAccountClient(platformApiUrl, accessToken) {
|
|
150
|
-
return new ForgewayClient({
|
|
151
|
-
baseUrl: normalizeForgewayApiUrl(platformApiUrl),
|
|
152
|
-
accessToken,
|
|
153
|
-
isServerMode: true,
|
|
154
|
-
auth: { detectOAuthCallback: false }
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
function normalizeForgewayUser$2(user) {
|
|
158
|
-
if (!user?.id || !user.email) throw new Error("Forgeway session response did not include a user email.");
|
|
159
|
-
return {
|
|
160
|
-
id: user.id,
|
|
161
|
-
name: user.profile?.name ?? user.name ?? user.email,
|
|
162
|
-
email: user.email,
|
|
163
|
-
avatarUrl: user.profile?.avatar_url ?? user.avatarUrl ?? user.avatar_url ?? null,
|
|
164
|
-
emailVerified: user.emailVerified ?? user.email_verified ?? false
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
function assertFormalForgewayUser(user) {
|
|
168
|
-
if (!user || user.emailVerified !== true || isShadowForgewayEmail(user.email)) throw new Error("Dineway account commands require a verified Forgeway account.");
|
|
169
|
-
return user;
|
|
170
|
-
}
|
|
171
|
-
async function readStoredForgewayAccount(lookup = {}) {
|
|
172
|
-
const platformApiUrl = normalizeForgewayApiUrl(lookup.platformApiUrl);
|
|
173
|
-
const email = normalizeForgewayEmail(lookup.email);
|
|
174
|
-
if (email) return readForgewayCredentials({
|
|
175
|
-
platformApiUrl,
|
|
176
|
-
email
|
|
177
|
-
});
|
|
178
|
-
const stored = await readForgewayCredentials({ platformApiUrl });
|
|
179
|
-
if (stored) return stored;
|
|
180
|
-
const accounts = (await listForgewayCredentials(platformApiUrl)).filter((credentials) => {
|
|
181
|
-
try {
|
|
182
|
-
assertFormalForgewayUser(credentials.user);
|
|
183
|
-
return true;
|
|
184
|
-
} catch {
|
|
185
|
-
return false;
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
return accounts.length === 1 ? accounts[0] ?? null : null;
|
|
189
|
-
}
|
|
190
|
-
async function refreshForgewayAccountCredentials(credentials) {
|
|
191
|
-
const user = assertFormalForgewayUser(credentials.user);
|
|
192
|
-
if (!credentials.refreshToken) throw new Error("Stored Forgeway account is missing a refresh token. Run `dineway account login`.");
|
|
193
|
-
const { data, error } = await createForgewayAccountClient(credentials.platformApiUrl).auth.refreshSession({ refreshToken: credentials.refreshToken });
|
|
194
|
-
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);
|
|
196
|
-
if (normalizeForgewayEmail(refreshedUser.email) !== normalizeForgewayEmail(user.email)) throw new Error("Forgeway refresh returned credentials for a different account.");
|
|
197
|
-
const updated = {
|
|
198
|
-
...credentials,
|
|
199
|
-
platformApiUrl: normalizeForgewayApiUrl(credentials.platformApiUrl),
|
|
200
|
-
accessToken: data.accessToken,
|
|
201
|
-
refreshToken: data.refreshToken ?? credentials.refreshToken,
|
|
202
|
-
user: {
|
|
203
|
-
...refreshedUser,
|
|
204
|
-
emailVerified: true
|
|
205
|
-
}
|
|
206
|
-
};
|
|
207
|
-
await writeForgewayCredentials(updated, {
|
|
208
|
-
platformApiUrl: updated.platformApiUrl,
|
|
209
|
-
email: updated.user?.email
|
|
210
|
-
});
|
|
211
|
-
return updated;
|
|
212
|
-
}
|
|
213
|
-
async function sendForgewayAccountLoginLink(input) {
|
|
214
|
-
const platformApiUrl = normalizeForgewayApiUrl(input.platformApiUrl);
|
|
215
|
-
const webUrl = normalizeDinewayWebUrl(input.webUrl);
|
|
216
|
-
const redirectTo = new URL("/cli-auth", webUrl).toString();
|
|
217
|
-
const { error } = await createForgewayAccountClient(platformApiUrl).auth.sendPasswordlessSignInEmail({
|
|
218
|
-
email: input.email,
|
|
219
|
-
redirectTo
|
|
220
|
-
});
|
|
221
|
-
if (error) throw new Error(error.message || "Failed to send Forgeway account sign-in link.");
|
|
222
|
-
}
|
|
223
|
-
async function completeForgewayAccountLogin(input) {
|
|
224
|
-
const platformApiUrl = normalizeForgewayApiUrl(input.platformApiUrl);
|
|
225
|
-
const { data, error } = await createForgewayAccountClient(platformApiUrl).auth.signInWithPasswordlessToken({ token: input.token });
|
|
226
|
-
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);
|
|
228
|
-
if (input.email && normalizeForgewayEmail(user.email) !== normalizeForgewayEmail(input.email)) throw new Error("Forgeway sign-in returned credentials for a different email.");
|
|
229
|
-
const credentials = {
|
|
230
|
-
platformApiUrl,
|
|
231
|
-
accessToken: data.accessToken,
|
|
232
|
-
refreshToken: data.refreshToken,
|
|
233
|
-
user: {
|
|
234
|
-
...user,
|
|
235
|
-
emailVerified: true
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
await writeForgewayCredentials(credentials, {
|
|
239
|
-
platformApiUrl,
|
|
240
|
-
email: credentials.user?.email
|
|
241
|
-
});
|
|
242
|
-
return credentials;
|
|
243
|
-
}
|
|
244
|
-
async function getForgewayDinewayAccount(credentials) {
|
|
245
|
-
const refreshed = await refreshForgewayAccountCredentials(credentials);
|
|
246
|
-
const { data, error } = await createForgewayAccountClient(refreshed.platformApiUrl, refreshed.accessToken).dineway.getAccount();
|
|
247
|
-
if (error || !data) throw new Error(error?.message || "Failed to fetch Dineway account status from Forgeway.");
|
|
248
|
-
return {
|
|
249
|
-
credentials: refreshed,
|
|
250
|
-
account: data
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
|
|
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
|
-
//#endregion
|
|
320
55
|
//#region src/cli/commands/account.ts
|
|
321
56
|
async function promptLine(question) {
|
|
322
57
|
const readline = createInterface({
|
|
@@ -378,12 +113,14 @@ const accountLoginCommand = defineCommand({
|
|
|
378
113
|
consola$1.error("Email is required.");
|
|
379
114
|
process.exit(2);
|
|
380
115
|
}
|
|
116
|
+
let validatingStoredAccount = false;
|
|
381
117
|
try {
|
|
382
118
|
const existing = await readStoredForgewayAccount({
|
|
383
119
|
platformApiUrl,
|
|
384
120
|
email
|
|
385
121
|
});
|
|
386
122
|
if (existing && !args.token && !args["send-link"]) {
|
|
123
|
+
validatingStoredAccount = true;
|
|
387
124
|
const { account } = await getForgewayDinewayAccount(existing);
|
|
388
125
|
if (args.json) outputAccountJson({
|
|
389
126
|
loggedIn: true,
|
|
@@ -431,6 +168,15 @@ const accountLoginCommand = defineCommand({
|
|
|
431
168
|
consola$1.info(`Hosted sites: ${account.sites.usedHostedSites.toLocaleString()} / ${account.sites.hostedSiteLimitLabel}`);
|
|
432
169
|
}
|
|
433
170
|
} catch (error) {
|
|
171
|
+
if (validatingStoredAccount && error instanceof InvalidForgewayAccountCredentialsError) {
|
|
172
|
+
await removeStoredForgewayAccount({
|
|
173
|
+
platformApiUrl,
|
|
174
|
+
email
|
|
175
|
+
});
|
|
176
|
+
consola$1.error("Stored Dineway account credentials were invalid and have been cleared.");
|
|
177
|
+
consola$1.info(`Run ${pc.cyan(`dineway account login --email ${email}`)} again to sign in.`);
|
|
178
|
+
process.exit(2);
|
|
179
|
+
}
|
|
434
180
|
consola$1.error(error instanceof Error ? error.message : "Dineway account login failed");
|
|
435
181
|
process.exit(2);
|
|
436
182
|
}
|
|
@@ -504,320 +250,6 @@ const accountCommand = defineCommand({
|
|
|
504
250
|
}
|
|
505
251
|
});
|
|
506
252
|
|
|
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
253
|
//#endregion
|
|
822
254
|
//#region src/cli/commands/auth.ts
|
|
823
255
|
/**
|
|
@@ -1064,7 +496,7 @@ const createCommand$1 = defineCommand({
|
|
|
1064
496
|
},
|
|
1065
497
|
draft: {
|
|
1066
498
|
type: "boolean",
|
|
1067
|
-
description: "
|
|
499
|
+
description: "Explicitly create as Draft (the only supported creation mode)"
|
|
1068
500
|
},
|
|
1069
501
|
...connectionArgs
|
|
1070
502
|
},
|
|
@@ -1079,7 +511,6 @@ const createCommand$1 = defineCommand({
|
|
|
1079
511
|
locale: args.locale,
|
|
1080
512
|
translationOf: args["translation-of"]
|
|
1081
513
|
});
|
|
1082
|
-
if (!args.draft) await client.publish(args.collection, item.id);
|
|
1083
514
|
output(await client.get(args.collection, item.id), args);
|
|
1084
515
|
} catch (error) {
|
|
1085
516
|
consola$1.error(error instanceof Error ? error.message : "Unknown error");
|
|
@@ -1122,7 +553,7 @@ const updateCommand = defineCommand({
|
|
|
1122
553
|
},
|
|
1123
554
|
draft: {
|
|
1124
555
|
type: "boolean",
|
|
1125
|
-
description: "
|
|
556
|
+
description: "Explicitly update the Draft (the only supported update mode)"
|
|
1126
557
|
},
|
|
1127
558
|
...connectionArgs
|
|
1128
559
|
},
|
|
@@ -1136,7 +567,6 @@ const updateCommand = defineCommand({
|
|
|
1136
567
|
_rev: args.rev,
|
|
1137
568
|
locale: args.locale
|
|
1138
569
|
});
|
|
1139
|
-
if (!args.draft && updated.draftRevisionId) await client.publish(args.collection, updated.id);
|
|
1140
570
|
output(await client.get(args.collection, updated.id), args);
|
|
1141
571
|
} catch (error) {
|
|
1142
572
|
consola$1.error(error instanceof Error ? error.message : "Unknown error");
|
|
@@ -1190,12 +620,25 @@ const publishCommand$1 = defineCommand({
|
|
|
1190
620
|
description: "Content item ID or slug",
|
|
1191
621
|
required: true
|
|
1192
622
|
},
|
|
623
|
+
"review-request": {
|
|
624
|
+
type: "string",
|
|
625
|
+
description: "Approved editorial Review Request ID for the exact Draft",
|
|
626
|
+
required: true
|
|
627
|
+
},
|
|
628
|
+
"release-authorization": {
|
|
629
|
+
type: "string",
|
|
630
|
+
description: "Approved release authorization ID for this token action",
|
|
631
|
+
required: true
|
|
632
|
+
},
|
|
1193
633
|
...connectionArgs
|
|
1194
634
|
},
|
|
1195
635
|
async run({ args }) {
|
|
1196
636
|
configureOutputMode(args);
|
|
1197
637
|
try {
|
|
1198
|
-
await createClientFromArgs(args).publish(args.collection, args.id
|
|
638
|
+
await createClientFromArgs(args).publish(args.collection, args.id, {
|
|
639
|
+
reviewRequestId: args["review-request"],
|
|
640
|
+
releaseAuthorizationId: args["release-authorization"]
|
|
641
|
+
});
|
|
1199
642
|
output({ success: true }, args);
|
|
1200
643
|
consola$1.success(`Published ${args.collection}/${args.id}`);
|
|
1201
644
|
} catch (error) {
|
|
@@ -1255,12 +698,26 @@ const scheduleCommand = defineCommand({
|
|
|
1255
698
|
description: "ISO 8601 datetime to publish at",
|
|
1256
699
|
required: true
|
|
1257
700
|
},
|
|
701
|
+
"review-request": {
|
|
702
|
+
type: "string",
|
|
703
|
+
description: "Approved editorial Review Request ID for the exact Draft",
|
|
704
|
+
required: true
|
|
705
|
+
},
|
|
706
|
+
"release-authorization": {
|
|
707
|
+
type: "string",
|
|
708
|
+
description: "Approved release authorization ID for this token action",
|
|
709
|
+
required: true
|
|
710
|
+
},
|
|
1258
711
|
...connectionArgs
|
|
1259
712
|
},
|
|
1260
713
|
async run({ args }) {
|
|
1261
714
|
configureOutputMode(args);
|
|
1262
715
|
try {
|
|
1263
|
-
await createClientFromArgs(args).schedule(args.collection, args.id, {
|
|
716
|
+
await createClientFromArgs(args).schedule(args.collection, args.id, {
|
|
717
|
+
at: args.at,
|
|
718
|
+
reviewRequestId: args["review-request"],
|
|
719
|
+
releaseAuthorizationId: args["release-authorization"]
|
|
720
|
+
});
|
|
1264
721
|
output({ success: true }, args);
|
|
1265
722
|
consola$1.success(`Scheduled ${args.collection}/${args.id} for ${args.at}`);
|
|
1266
723
|
} catch (error) {
|
|
@@ -1349,7 +806,7 @@ const contentCommand = defineCommand({
|
|
|
1349
806
|
//#endregion
|
|
1350
807
|
//#region src/cli/paywall.ts
|
|
1351
808
|
const PAYWALL_MESSAGE_PATTERN = /\b(quota|credit|credits|upgrade|plan|billing|subscription|slot)\b/i;
|
|
1352
|
-
function isRecord$
|
|
809
|
+
function isRecord$2(value) {
|
|
1353
810
|
return typeof value === "object" && value !== null;
|
|
1354
811
|
}
|
|
1355
812
|
function stringField(record, key) {
|
|
@@ -1357,9 +814,9 @@ function stringField(record, key) {
|
|
|
1357
814
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
1358
815
|
}
|
|
1359
816
|
function getNestedError(value) {
|
|
1360
|
-
if (!isRecord$
|
|
817
|
+
if (!isRecord$2(value)) return null;
|
|
1361
818
|
const nested = value.error;
|
|
1362
|
-
return isRecord$
|
|
819
|
+
return isRecord$2(nested) ? nested : value;
|
|
1363
820
|
}
|
|
1364
821
|
function isHostedSitesResource(resource) {
|
|
1365
822
|
const normalized = resource.toLowerCase();
|
|
@@ -1927,7 +1384,7 @@ const FLY_HOST_RE = /\b[a-z0-9][a-z0-9-]*\.fly\.dev\b/;
|
|
|
1927
1384
|
const INVALID_FLY_APP_CHARS_RE = /[^a-z0-9-]+/g;
|
|
1928
1385
|
const REPEATED_HYPHENS_RE$2 = /-+/g;
|
|
1929
1386
|
const EDGE_HYPHENS_RE$2 = /^-|-$/g;
|
|
1930
|
-
const LINE_SPLIT_RE = /\r?\n/;
|
|
1387
|
+
const LINE_SPLIT_RE$1 = /\r?\n/;
|
|
1931
1388
|
const DEFAULT_REGION$1 = "iad";
|
|
1932
1389
|
const FLY_VOLUME_NAME = "dineway_data";
|
|
1933
1390
|
async function fileExists$8(path) {
|
|
@@ -2019,7 +1476,7 @@ primary_region = "${region}"
|
|
|
2019
1476
|
function parseFlyUrl(stdout) {
|
|
2020
1477
|
const direct = stdout.match(FLY_URL_RE);
|
|
2021
1478
|
if (direct) return direct[0];
|
|
2022
|
-
for (const line of stdout.split(LINE_SPLIT_RE).filter(Boolean)) try {
|
|
1479
|
+
for (const line of stdout.split(LINE_SPLIT_RE$1).filter(Boolean)) try {
|
|
2023
1480
|
const url = findFlyUrl(JSON.parse(line));
|
|
2024
1481
|
if (url) return url;
|
|
2025
1482
|
} catch {
|
|
@@ -2191,7 +1648,7 @@ async function writeForgewayDeployMetadata(cwd, metadata) {
|
|
|
2191
1648
|
|
|
2192
1649
|
//#endregion
|
|
2193
1650
|
//#region src/cli/commands/deploy/targets/forgeway.ts
|
|
2194
|
-
const DEFAULT_DINEWAY_API_BASE_URL
|
|
1651
|
+
const DEFAULT_DINEWAY_API_BASE_URL = "https://api.dineway.ai";
|
|
2195
1652
|
const DATABASE_ENV_VAR_NAMES = ["DINEWAY_DATABASE_URL", "DINEWAY_DATABASE_AUTH_TOKEN"];
|
|
2196
1653
|
const POLL_INTERVAL_MS$1 = 5e3;
|
|
2197
1654
|
const POLL_TIMEOUT_MS$1 = 3e5;
|
|
@@ -2206,12 +1663,12 @@ const NON_COMPARE_PATTERN = /[^\p{Letter}\p{Number}]+/gu;
|
|
|
2206
1663
|
const LOCATION_PARTS_PATTERN = /[,;|/]+|\s+-\s+/u;
|
|
2207
1664
|
const JWT_PATTERN = /[A-Za-z0-9_-]{32,}\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}/g;
|
|
2208
1665
|
const TOKEN_ASSIGNMENT_PATTERN = /(DINEWAY_DATABASE_AUTH_TOKEN|DATABASE_AUTH_TOKEN|AUTH_TOKEN)=\S+/gi;
|
|
2209
|
-
const TRAILING_SLASH_PATTERN
|
|
1666
|
+
const TRAILING_SLASH_PATTERN = /\/$/;
|
|
2210
1667
|
const BACKSLASH_PATTERN = /\\/g;
|
|
2211
1668
|
const DINEWAY_PACKAGE_NAME = "dineway";
|
|
2212
1669
|
const SEMVER_VERSION_PATTERN = /^\d+\.\d+\.\d+(?:[-+].*)?$/;
|
|
2213
1670
|
const LOCAL_PACKAGE_SPEC_PATTERN = /^(?:workspace|file|link|portal):/;
|
|
2214
|
-
const SHADOW_EMAIL_PATTERN
|
|
1671
|
+
const SHADOW_EMAIL_PATTERN = /^shadow_[a-f0-9]{16}@dineway\.ai$/i;
|
|
2215
1672
|
const FOOD_TYPES = new Set([
|
|
2216
1673
|
"restaurant",
|
|
2217
1674
|
"cafe",
|
|
@@ -2259,7 +1716,7 @@ var ForgewayApiError = class extends Error {
|
|
|
2259
1716
|
this.name = "ForgewayApiError";
|
|
2260
1717
|
}
|
|
2261
1718
|
};
|
|
2262
|
-
function normalizeForgewayUser
|
|
1719
|
+
function normalizeForgewayUser(user) {
|
|
2263
1720
|
return {
|
|
2264
1721
|
id: user.id,
|
|
2265
1722
|
name: user.profile?.name ?? user.name ?? user.email,
|
|
@@ -2280,7 +1737,7 @@ async function fileExists$7(path) {
|
|
|
2280
1737
|
}
|
|
2281
1738
|
}
|
|
2282
1739
|
function normalizePlatformApiUrl(value) {
|
|
2283
|
-
return (value || DEFAULT_DINEWAY_API_BASE_URL
|
|
1740
|
+
return (value || DEFAULT_DINEWAY_API_BASE_URL).replace(TRAILING_SLASH_PATTERN, "");
|
|
2284
1741
|
}
|
|
2285
1742
|
function resolvePlatformApiUrl(savedPlatformApiUrl) {
|
|
2286
1743
|
return normalizePlatformApiUrl(getEnv("DINEWAY_API_BASE_URL") ?? (typeof savedPlatformApiUrl === "string" ? savedPlatformApiUrl : void 0));
|
|
@@ -2343,11 +1800,11 @@ async function parseErrorResponse(response) {
|
|
|
2343
1800
|
}
|
|
2344
1801
|
return `Forgeway request failed: ${response.status}`;
|
|
2345
1802
|
}
|
|
2346
|
-
function normalizeEmail
|
|
1803
|
+
function normalizeEmail(email) {
|
|
2347
1804
|
return email.trim().toLowerCase();
|
|
2348
1805
|
}
|
|
2349
|
-
function isShadowEmail
|
|
2350
|
-
return Boolean(email && SHADOW_EMAIL_PATTERN
|
|
1806
|
+
function isShadowEmail(email) {
|
|
1807
|
+
return Boolean(email && SHADOW_EMAIL_PATTERN.test(email));
|
|
2351
1808
|
}
|
|
2352
1809
|
async function refreshFormalAccessToken(context, deps) {
|
|
2353
1810
|
if (!context.refreshToken) throw new Error("Forgeway formal account refresh token is missing. Run dineway deploy again.");
|
|
@@ -2359,7 +1816,7 @@ async function refreshFormalAccessToken(context, deps) {
|
|
|
2359
1816
|
if (!response.ok) throw new Error("Failed to refresh Forgeway formal account credentials. Run dineway deploy again.");
|
|
2360
1817
|
const payload = await response.json();
|
|
2361
1818
|
if (!payload.accessToken) throw new Error("Forgeway refresh response was missing accessToken.");
|
|
2362
|
-
const user = assertVerifiedFormalAccountUser(payload.user ? normalizeForgewayUser
|
|
1819
|
+
const user = assertVerifiedFormalAccountUser(payload.user ? normalizeForgewayUser(payload.user) : context.user, context.user?.email);
|
|
2363
1820
|
const updated = {
|
|
2364
1821
|
platformApiUrl: context.platformApiUrl,
|
|
2365
1822
|
accessToken: payload.accessToken,
|
|
@@ -2406,7 +1863,7 @@ function getDependencySpec(pkg, name) {
|
|
|
2406
1863
|
if (typeof value === "string" && value.trim()) return value.trim();
|
|
2407
1864
|
}
|
|
2408
1865
|
}
|
|
2409
|
-
async function readProjectPackage(cwd) {
|
|
1866
|
+
async function readProjectPackage$1(cwd) {
|
|
2410
1867
|
try {
|
|
2411
1868
|
return await readDeployPackageJson(cwd);
|
|
2412
1869
|
} catch {
|
|
@@ -2420,7 +1877,7 @@ function isDinewayDependencySpecCompatible(dependencySpec, cliVersion) {
|
|
|
2420
1877
|
}
|
|
2421
1878
|
async function warnProjectDinewayRuntimeMismatch(cwd, cliVersion = VERSION) {
|
|
2422
1879
|
if (!SEMVER_VERSION_PATTERN.test(cliVersion)) return;
|
|
2423
|
-
const pkg = await readProjectPackage(cwd);
|
|
1880
|
+
const pkg = await readProjectPackage$1(cwd);
|
|
2424
1881
|
if (!pkg) return;
|
|
2425
1882
|
const dependencySpec = getDependencySpec(pkg, DINEWAY_PACKAGE_NAME);
|
|
2426
1883
|
if (!dependencySpec) return;
|
|
@@ -2434,20 +1891,20 @@ function savedPublisherEmail(saved) {
|
|
|
2434
1891
|
return typeof saved.publisherEmail === "string" && saved.publisherEmail.trim() ? saved.publisherEmail : void 0;
|
|
2435
1892
|
}
|
|
2436
1893
|
function validatePublisherEmail(rawEmail) {
|
|
2437
|
-
const email = normalizeEmail
|
|
2438
|
-
if (!email || isShadowEmail
|
|
1894
|
+
const email = normalizeEmail(rawEmail);
|
|
1895
|
+
if (!email || isShadowEmail(email)) throw new Error("Dineway account email must be a real owner email, not a shadow user email.");
|
|
2439
1896
|
return email;
|
|
2440
1897
|
}
|
|
2441
1898
|
function isStoredFormalCredentialForEmail(stored, email) {
|
|
2442
1899
|
if (!stored?.accessToken || !stored.user) return false;
|
|
2443
|
-
if (email && normalizeEmail
|
|
1900
|
+
if (email && normalizeEmail(stored.user.email) !== email) return false;
|
|
2444
1901
|
if (stored.user.emailVerified !== true) return false;
|
|
2445
|
-
if (isShadowEmail
|
|
1902
|
+
if (isShadowEmail(stored.user.email)) return false;
|
|
2446
1903
|
return true;
|
|
2447
1904
|
}
|
|
2448
1905
|
function assertVerifiedFormalAccountUser(user, expectedEmail) {
|
|
2449
|
-
if (!user || user.emailVerified !== true || isShadowEmail
|
|
2450
|
-
if (expectedEmail && normalizeEmail
|
|
1906
|
+
if (!user || user.emailVerified !== true || isShadowEmail(user.email)) throw new Error("Forgeway formal account credentials require a verified formal account.");
|
|
1907
|
+
if (expectedEmail && normalizeEmail(user.email) !== normalizeEmail(expectedEmail)) throw new Error("Forgeway refresh returned credentials for a different email.");
|
|
2451
1908
|
return user;
|
|
2452
1909
|
}
|
|
2453
1910
|
async function readCachedPublisherCredentials(cwd, platformApiUrl, email, deps) {
|
|
@@ -2480,14 +1937,14 @@ async function resolveCachedPublisherAccount(cwd, options, saved, platformApiUrl
|
|
|
2480
1937
|
platformApiUrl
|
|
2481
1938
|
});
|
|
2482
1939
|
if (isStoredFormalCredentialForEmail(singleCredentials)) return {
|
|
2483
|
-
email: normalizeEmail
|
|
1940
|
+
email: normalizeEmail(singleCredentials.user.email),
|
|
2484
1941
|
credentials: singleCredentials
|
|
2485
1942
|
};
|
|
2486
1943
|
const accounts = (await (deps.listCredentials ?? listForgewayCredentials)(platformApiUrl)).filter((credentials) => isStoredFormalCredentialForEmail(credentials));
|
|
2487
1944
|
if (accounts.length === 1) {
|
|
2488
1945
|
const credentials = accounts[0];
|
|
2489
1946
|
return {
|
|
2490
|
-
email: normalizeEmail
|
|
1947
|
+
email: normalizeEmail(credentials.user.email),
|
|
2491
1948
|
credentials
|
|
2492
1949
|
};
|
|
2493
1950
|
}
|
|
@@ -2549,7 +2006,7 @@ async function resolveProjectContext(cwd, options, deps) {
|
|
|
2549
2006
|
if (formal.email) throw new Error(`No verified Dineway account credentials were found for ${formal.email}. Run: dineway account login --email ${formal.email}`);
|
|
2550
2007
|
throw new Error("No verified Dineway account credentials were found. Run: dineway account login");
|
|
2551
2008
|
}
|
|
2552
|
-
function isRecord(value) {
|
|
2009
|
+
function isRecord$1(value) {
|
|
2553
2010
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2554
2011
|
}
|
|
2555
2012
|
function scalarText(value) {
|
|
@@ -2562,7 +2019,7 @@ function uniqueStrings(values) {
|
|
|
2562
2019
|
function displayText(value) {
|
|
2563
2020
|
if (typeof value === "string" || typeof value === "number") return String(value);
|
|
2564
2021
|
if (Array.isArray(value)) return value.map(displayText).filter(Boolean).join(" ");
|
|
2565
|
-
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);
|
|
2022
|
+
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);
|
|
2566
2023
|
return "";
|
|
2567
2024
|
}
|
|
2568
2025
|
function normalizeCompare(value) {
|
|
@@ -2580,7 +2037,7 @@ function candidateNameTexts(candidate) {
|
|
|
2580
2037
|
]);
|
|
2581
2038
|
}
|
|
2582
2039
|
function candidateAddressTexts(candidate) {
|
|
2583
|
-
const addressComponents = Array.isArray(candidate.addressComponents) ? candidate.addressComponents.flatMap((component) => isRecord(component) ? [
|
|
2040
|
+
const addressComponents = Array.isArray(candidate.addressComponents) ? candidate.addressComponents.flatMap((component) => isRecord$1(component) ? [
|
|
2584
2041
|
displayText(component.longText),
|
|
2585
2042
|
displayText(component.shortText),
|
|
2586
2043
|
displayText(component.long_name),
|
|
@@ -2722,11 +2179,11 @@ function selectPlaceCandidate(candidates, context) {
|
|
|
2722
2179
|
} : null;
|
|
2723
2180
|
}
|
|
2724
2181
|
function extractPlaceCandidates(payload) {
|
|
2725
|
-
const root = isRecord(payload) ? payload : {};
|
|
2726
|
-
const nestedData = isRecord(root.data) ? root.data : {};
|
|
2182
|
+
const root = isRecord$1(payload) ? payload : {};
|
|
2183
|
+
const nestedData = isRecord$1(root.data) ? root.data : {};
|
|
2727
2184
|
const places = root.places ?? root.results ?? nestedData.places ?? nestedData.results;
|
|
2728
2185
|
if (!Array.isArray(places)) throw new Error("Dineway place search response did not include places/results.");
|
|
2729
|
-
return places.filter(isRecord);
|
|
2186
|
+
return places.filter(isRecord$1);
|
|
2730
2187
|
}
|
|
2731
2188
|
async function resolvePlaceIdWithFormalSearch(options) {
|
|
2732
2189
|
const candidates = extractPlaceCandidates(await ossFetch(options.context, "/api/places/search", options.deps, {
|
|
@@ -2743,7 +2200,7 @@ async function resolvePlaceIdWithFormalSearch(options) {
|
|
|
2743
2200
|
return finalPlaceId;
|
|
2744
2201
|
}
|
|
2745
2202
|
async function ossFetch(context, path, deps, options = {}) {
|
|
2746
|
-
const request = async (token) => await forgewayRequest(`${context.ossHost.replace(TRAILING_SLASH_PATTERN
|
|
2203
|
+
const request = async (token) => await forgewayRequest(`${context.ossHost.replace(TRAILING_SLASH_PATTERN, "")}${path}`, {
|
|
2747
2204
|
...options,
|
|
2748
2205
|
headers: {
|
|
2749
2206
|
"Content-Type": "application/json",
|
|
@@ -3258,7 +2715,7 @@ async function deployForgeway(cwd, options, deps = {}) {
|
|
|
3258
2715
|
deps,
|
|
3259
2716
|
verbose
|
|
3260
2717
|
});
|
|
3261
|
-
if (context.user?.email) await writeForgewayDeployMetadata(cwd, { publisherEmail: normalizeEmail
|
|
2718
|
+
if (context.user?.email) await writeForgewayDeployMetadata(cwd, { publisherEmail: normalizeEmail(context.user.email) });
|
|
3262
2719
|
const url = deployment.liveUrl ?? `https://${site.domain}`;
|
|
3263
2720
|
await (deps.writeProjectEnv ?? persistForgewayCliEnv)(cwd, {
|
|
3264
2721
|
siteUrl: url,
|
|
@@ -4040,7 +3497,7 @@ const devCommand = defineCommand({
|
|
|
4040
3497
|
const remoteUrl = pkg.dineway?.url || process.env.DINEWAY_URL;
|
|
4041
3498
|
if (!remoteUrl) consola.warn("No remote URL configured. Set DINEWAY_URL or dineway.url in package.json");
|
|
4042
3499
|
else try {
|
|
4043
|
-
const { createClientFromArgs } = await
|
|
3500
|
+
const { createClientFromArgs } = await import("../client-factory-DEtHApCc.mjs").then((n) => n.t);
|
|
4044
3501
|
const client = createClientFromArgs({ url: remoteUrl });
|
|
4045
3502
|
const schema = await client.schemaExport();
|
|
4046
3503
|
const types = await client.schemaTypes();
|
|
@@ -4746,6 +4203,76 @@ async function getTaxonomyAssignments(taxonomyRepo, collection, entryId) {
|
|
|
4746
4203
|
return result;
|
|
4747
4204
|
}
|
|
4748
4205
|
|
|
4206
|
+
//#endregion
|
|
4207
|
+
//#region src/integration-credentials/environment-import.ts
|
|
4208
|
+
const INTEGRATION_CREDENTIAL_IMPORT_MARKER = "dineway:integration_credentials_environment_import_v1";
|
|
4209
|
+
const INTEGRATION_TOKEN_ENV_SOURCES = [
|
|
4210
|
+
["akta", "AKTA_API_KEY"],
|
|
4211
|
+
["apify", "APIFY_API_TOKEN"],
|
|
4212
|
+
["apollo", "APOLLO_API_KEY"],
|
|
4213
|
+
["bundle-social", "BUNDLE_SOCIAL_API_KEY"],
|
|
4214
|
+
["context.dev", "CONTEXT_DEV_API_KEY"],
|
|
4215
|
+
["elevenlabs", "ELEVENLABS_API_KEY"],
|
|
4216
|
+
["exa", "EXA_API_KEY"],
|
|
4217
|
+
["firecrawl", "FIRECRAWL_API_KEY"],
|
|
4218
|
+
["google-places", "GOOGLE_PLACES_API_KEY"],
|
|
4219
|
+
["minimax", "MINIMAX_API_KEY"],
|
|
4220
|
+
["pdl", "PDL_API_KEY"],
|
|
4221
|
+
["semrush", "SEMRUSH_V4_API_KEY"],
|
|
4222
|
+
["tabapi", "TABAPI_API_KEY"],
|
|
4223
|
+
["tikhub", "TIKHUB_API_KEY"]
|
|
4224
|
+
];
|
|
4225
|
+
async function importIntegrationCredentialsFromEnvironment(db, env) {
|
|
4226
|
+
if (await new OptionsRepository(db).exists(INTEGRATION_CREDENTIAL_IMPORT_MARKER)) return {
|
|
4227
|
+
status: "already_completed",
|
|
4228
|
+
providers: []
|
|
4229
|
+
};
|
|
4230
|
+
const credentials = collectEnvironmentCredentials(env);
|
|
4231
|
+
const keys = await parseEncryptionKeys(env.DINEWAY_ENCRYPTION_KEY);
|
|
4232
|
+
if (credentials.length > 0 && (!keys || keys.length === 0)) throw new DinewaySecretsError("DINEWAY_ENCRYPTION_KEY is required to import Integration credentials.", "ENCRYPTION_KEY_REQUIRED");
|
|
4233
|
+
await db.transaction().execute(async (trx) => {
|
|
4234
|
+
const store = new IntegrationCredentialStore(trx, { keys: keys ?? [] });
|
|
4235
|
+
for (const entry of credentials) await store.set(entry.provider, entry.credential, "environment");
|
|
4236
|
+
await new OptionsRepository(trx).set(INTEGRATION_CREDENTIAL_IMPORT_MARKER, {
|
|
4237
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4238
|
+
providerCount: credentials.length
|
|
4239
|
+
});
|
|
4240
|
+
});
|
|
4241
|
+
return {
|
|
4242
|
+
status: credentials.length > 0 ? "imported" : "completed_empty",
|
|
4243
|
+
providers: credentials.map((entry) => entry.provider)
|
|
4244
|
+
};
|
|
4245
|
+
}
|
|
4246
|
+
function collectEnvironmentCredentials(env) {
|
|
4247
|
+
const credentials = [];
|
|
4248
|
+
for (const [provider, variable] of INTEGRATION_TOKEN_ENV_SOURCES) {
|
|
4249
|
+
const value = nonEmpty(env[variable]);
|
|
4250
|
+
if (value) credentials.push({
|
|
4251
|
+
provider,
|
|
4252
|
+
credential: {
|
|
4253
|
+
type: "token",
|
|
4254
|
+
value
|
|
4255
|
+
}
|
|
4256
|
+
});
|
|
4257
|
+
}
|
|
4258
|
+
const username = nonEmpty(env.DATAFORSEO_LOGIN);
|
|
4259
|
+
const password = nonEmpty(env.DATAFORSEO_PASSWORD);
|
|
4260
|
+
if (username && !password || !username && password) throw new DinewaySecretsError("DATAFORSEO_LOGIN and DATAFORSEO_PASSWORD must both be set for the initial Integration import.", "INCOMPLETE_INTEGRATION_CREDENTIAL");
|
|
4261
|
+
if (username && password) credentials.push({
|
|
4262
|
+
provider: "dataforseo",
|
|
4263
|
+
credential: {
|
|
4264
|
+
type: "basic",
|
|
4265
|
+
username,
|
|
4266
|
+
password
|
|
4267
|
+
}
|
|
4268
|
+
});
|
|
4269
|
+
return credentials;
|
|
4270
|
+
}
|
|
4271
|
+
function nonEmpty(value) {
|
|
4272
|
+
const trimmed = value?.trim();
|
|
4273
|
+
return trimmed ? trimmed : null;
|
|
4274
|
+
}
|
|
4275
|
+
|
|
4749
4276
|
//#endregion
|
|
4750
4277
|
//#region src/cli/commands/init.ts
|
|
4751
4278
|
/**
|
|
@@ -4844,6 +4371,17 @@ const initCommand = defineCommand({
|
|
|
4844
4371
|
await db.destroy();
|
|
4845
4372
|
process.exit(1);
|
|
4846
4373
|
}
|
|
4374
|
+
try {
|
|
4375
|
+
const imported = await importIntegrationCredentialsFromEnvironment(db, {
|
|
4376
|
+
...readProjectEnv(cwd),
|
|
4377
|
+
...process.env
|
|
4378
|
+
});
|
|
4379
|
+
if (imported.status === "imported") consola.success(`Imported Integration configuration for ${imported.providers.length} providers`);
|
|
4380
|
+
} catch (error) {
|
|
4381
|
+
consola.error("Integration configuration import failed. No provider credentials were stored.", error);
|
|
4382
|
+
await db.destroy();
|
|
4383
|
+
process.exit(1);
|
|
4384
|
+
}
|
|
4847
4385
|
const alreadyInitialized = await isAlreadyInitialized(db);
|
|
4848
4386
|
if (alreadyInitialized && !args.force) {
|
|
4849
4387
|
await db.destroy();
|
|
@@ -5953,7 +5491,7 @@ const publishCommand = defineCommand({
|
|
|
5953
5491
|
const pluginDir = resolve(args.dir);
|
|
5954
5492
|
try {
|
|
5955
5493
|
const { runCommand } = await import("citty");
|
|
5956
|
-
const { bundleCommand } = await import("../bundle-
|
|
5494
|
+
const { bundleCommand } = await import("../bundle-CIrUlrkQ.mjs").then((n) => n.n);
|
|
5957
5495
|
await runCommand(bundleCommand, { rawArgs: ["--dir", pluginDir] });
|
|
5958
5496
|
} catch {
|
|
5959
5497
|
consola.error("Build failed");
|
|
@@ -6723,164 +6261,712 @@ const seedCommand = defineCommand({
|
|
|
6723
6261
|
});
|
|
6724
6262
|
|
|
6725
6263
|
//#endregion
|
|
6726
|
-
//#region src/
|
|
6727
|
-
const
|
|
6728
|
-
const
|
|
6729
|
-
const
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
|
|
6738
|
-
|
|
6739
|
-
|
|
6264
|
+
//#region src/seo/local-crawl.ts
|
|
6265
|
+
const CONTRACT_VERSION = 1;
|
|
6266
|
+
const USER_AGENT = "DinewayContentCrawler/1.0";
|
|
6267
|
+
const DEFAULT_PAGE_LIMIT = 100;
|
|
6268
|
+
const MAX_PAGE_LIMIT = 500;
|
|
6269
|
+
const DEFAULT_MAX_DEPTH = 3;
|
|
6270
|
+
const MAX_DEPTH = 10;
|
|
6271
|
+
const DEFAULT_CONCURRENCY = 4;
|
|
6272
|
+
const MAX_CONCURRENCY = 10;
|
|
6273
|
+
const DEFAULT_TIMEOUT_MS = 1e4;
|
|
6274
|
+
const MAX_TIMEOUT_MS = 6e4;
|
|
6275
|
+
const DEFAULT_MAX_BYTES = 1e6;
|
|
6276
|
+
const MAX_RESOURCE_BYTES = 1e7;
|
|
6277
|
+
const MAX_SITEMAPS = 20;
|
|
6278
|
+
const MAX_REDIRECTS = 5;
|
|
6279
|
+
const MAX_TEXT_EXCERPT = 2e4;
|
|
6280
|
+
const LINE_SPLIT_RE = /\r?\n/;
|
|
6281
|
+
const COMMENT_RE = /#.*$/;
|
|
6282
|
+
const WHITESPACE_RE = /\s+/g;
|
|
6283
|
+
const WORD_RE = /[\p{L}\p{N}]+(?:['’-][\p{L}\p{N}]+)*/gu;
|
|
6284
|
+
const REGEXP_SPECIAL_RE = /[.*+?^${}()|[\]\\]/g;
|
|
6285
|
+
async function crawlLocalSite(options) {
|
|
6286
|
+
const normalized = normalizeOptions(options);
|
|
6287
|
+
if (!normalized.allowPrivateNetwork) await resolveAndValidateExternalUrl(normalized.startUrl.href, { resolver: normalized.dnsResolver });
|
|
6288
|
+
const origin = normalized.startUrl.origin;
|
|
6289
|
+
const skipped = /* @__PURE__ */ new Map();
|
|
6290
|
+
const robots = await loadRobots(normalized);
|
|
6291
|
+
const sitemapResult = normalized.sitemap === "skip" ? {
|
|
6292
|
+
sources: [],
|
|
6293
|
+
urls: []
|
|
6294
|
+
} : await loadSitemaps(normalized, robots.sitemaps);
|
|
6295
|
+
const queue = [];
|
|
6296
|
+
const queued = /* @__PURE__ */ new Set();
|
|
6297
|
+
const enqueue = (candidate, depth, discoveredFrom) => {
|
|
6298
|
+
const url = normalizeUrl(candidate, normalized.startUrl, normalized.keepQuery);
|
|
6299
|
+
if (!url || url.origin !== origin || queued.has(url.href)) return;
|
|
6300
|
+
const pathDecision = pathEligibility(url, normalized);
|
|
6301
|
+
if (pathDecision) {
|
|
6302
|
+
skipped.set(url.href, {
|
|
6303
|
+
url: url.href,
|
|
6304
|
+
reason: pathDecision
|
|
6305
|
+
});
|
|
6306
|
+
return;
|
|
6307
|
+
}
|
|
6308
|
+
if (!robotsAllows(url, robots.rules)) {
|
|
6309
|
+
skipped.set(url.href, {
|
|
6310
|
+
url: url.href,
|
|
6311
|
+
reason: "robots_disallowed"
|
|
6312
|
+
});
|
|
6313
|
+
return;
|
|
6314
|
+
}
|
|
6315
|
+
queued.add(url.href);
|
|
6316
|
+
queue.push({
|
|
6317
|
+
url: url.href,
|
|
6318
|
+
depth,
|
|
6319
|
+
discoveredFrom
|
|
6320
|
+
});
|
|
6321
|
+
};
|
|
6322
|
+
if (normalized.sitemap !== "only") enqueue(normalized.startUrl.href, 0, "start");
|
|
6323
|
+
for (const url of sitemapResult.urls) enqueue(url, 0, "sitemap");
|
|
6324
|
+
const pages = [];
|
|
6325
|
+
let cursor = 0;
|
|
6326
|
+
while (cursor < queue.length && pages.length < normalized.pageLimit) {
|
|
6327
|
+
const remaining = normalized.pageLimit - pages.length;
|
|
6328
|
+
const batch = queue.slice(cursor, cursor + Math.min(normalized.concurrency, remaining));
|
|
6329
|
+
cursor += batch.length;
|
|
6330
|
+
const resources = await Promise.all(batch.map(async (item) => ({
|
|
6331
|
+
item,
|
|
6332
|
+
resource: await fetchResource(item.url, normalized)
|
|
6333
|
+
})));
|
|
6334
|
+
for (const { item, resource } of resources) {
|
|
6335
|
+
const page = pageFromResource(item, resource, normalized);
|
|
6336
|
+
pages.push(page);
|
|
6337
|
+
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);
|
|
6338
|
+
}
|
|
6339
|
+
}
|
|
6340
|
+
const edges = pages.flatMap((page) => page.internalLinks.map((link) => ({
|
|
6341
|
+
from: page.url,
|
|
6342
|
+
...link
|
|
6343
|
+
})));
|
|
6344
|
+
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));
|
|
6345
|
+
const incoming = new Set(edges.map((edge) => edge.url));
|
|
6346
|
+
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));
|
|
6347
|
+
const successfulHtml = pages.filter((page) => page.httpStatus !== null && page.httpStatus < 400 && page.contentType?.includes("text/html"));
|
|
6348
|
+
return {
|
|
6349
|
+
contractVersion: CONTRACT_VERSION,
|
|
6350
|
+
startUrl: normalized.startUrl.href,
|
|
6351
|
+
origin,
|
|
6352
|
+
crawledAt: normalized.now().toISOString(),
|
|
6353
|
+
options: {
|
|
6354
|
+
pageLimit: normalized.pageLimit,
|
|
6355
|
+
maxDepth: normalized.maxDepth,
|
|
6356
|
+
concurrency: normalized.concurrency,
|
|
6357
|
+
include: normalized.includeValues,
|
|
6358
|
+
exclude: normalized.excludeValues,
|
|
6359
|
+
timeoutMs: normalized.timeoutMs,
|
|
6360
|
+
maxBytesPerResource: normalized.maxBytesPerResource,
|
|
6361
|
+
sitemap: normalized.sitemap,
|
|
6362
|
+
keepQuery: normalized.keepQuery,
|
|
6363
|
+
allowPrivateNetwork: normalized.allowPrivateNetwork
|
|
6364
|
+
},
|
|
6365
|
+
sources: {
|
|
6366
|
+
robots: robots.source,
|
|
6367
|
+
sitemaps: sitemapResult.sources
|
|
6368
|
+
},
|
|
6369
|
+
pages,
|
|
6370
|
+
skipped: [...skipped.values()].toSorted((left, right) => left.url.localeCompare(right.url)),
|
|
6371
|
+
linkGraph: {
|
|
6372
|
+
edges,
|
|
6373
|
+
orphanUrls,
|
|
6374
|
+
brokenInternalUrls,
|
|
6375
|
+
duplicateTitles: duplicateGroups(successfulHtml, "title"),
|
|
6376
|
+
duplicateDescriptions: duplicateGroups(successfulHtml, "description"),
|
|
6377
|
+
duplicateContent: duplicateGroups(successfulHtml, "contentSha256")
|
|
6378
|
+
},
|
|
6379
|
+
summary: {
|
|
6380
|
+
fetchedPages: pages.length,
|
|
6381
|
+
htmlPages: pages.filter((page) => page.contentType?.includes("text/html")).length,
|
|
6382
|
+
brokenPages: pages.filter((page) => page.httpStatus !== null && page.httpStatus >= 400).length,
|
|
6383
|
+
orphanPages: orphanUrls.length,
|
|
6384
|
+
skippedUrls: skipped.size,
|
|
6385
|
+
errorPages: pages.filter((page) => page.error).length,
|
|
6386
|
+
pageLimitReached: pages.length === normalized.pageLimit && cursor < queue.length
|
|
6387
|
+
}
|
|
6388
|
+
};
|
|
6740
6389
|
}
|
|
6741
|
-
function
|
|
6742
|
-
|
|
6390
|
+
function normalizeOptions(options) {
|
|
6391
|
+
const startUrl = normalizeUrl(options.startUrl, void 0, options.keepQuery === true);
|
|
6392
|
+
if (!startUrl) throw new Error("startUrl must be an absolute HTTP or HTTPS URL without credentials");
|
|
6393
|
+
const pageLimit = boundedInteger(options.pageLimit, DEFAULT_PAGE_LIMIT, 1, MAX_PAGE_LIMIT, "pageLimit");
|
|
6394
|
+
const maxDepth = boundedInteger(options.maxDepth, DEFAULT_MAX_DEPTH, 0, MAX_DEPTH, "maxDepth");
|
|
6395
|
+
const concurrency = boundedInteger(options.concurrency, DEFAULT_CONCURRENCY, 1, MAX_CONCURRENCY, "concurrency");
|
|
6396
|
+
const timeoutMs = boundedInteger(options.timeoutMs, DEFAULT_TIMEOUT_MS, 100, MAX_TIMEOUT_MS, "timeoutMs");
|
|
6397
|
+
const maxBytesPerResource = boundedInteger(options.maxBytesPerResource, DEFAULT_MAX_BYTES, 1024, MAX_RESOURCE_BYTES, "maxBytesPerResource");
|
|
6398
|
+
const sitemap = options.sitemap ?? "include";
|
|
6399
|
+
if (![
|
|
6400
|
+
"include",
|
|
6401
|
+
"skip",
|
|
6402
|
+
"only"
|
|
6403
|
+
].includes(sitemap)) throw new Error("sitemap must be include, skip, or only");
|
|
6404
|
+
const includeValues = normalizePatterns(options.include ?? []);
|
|
6405
|
+
const excludeValues = normalizePatterns(options.exclude ?? []);
|
|
6406
|
+
return {
|
|
6407
|
+
startUrl,
|
|
6408
|
+
pageLimit,
|
|
6409
|
+
maxDepth,
|
|
6410
|
+
concurrency,
|
|
6411
|
+
include: includeValues.map(pathMatcher),
|
|
6412
|
+
exclude: excludeValues.map(pathMatcher),
|
|
6413
|
+
includeValues,
|
|
6414
|
+
excludeValues,
|
|
6415
|
+
timeoutMs,
|
|
6416
|
+
maxBytesPerResource,
|
|
6417
|
+
sitemap,
|
|
6418
|
+
keepQuery: options.keepQuery === true,
|
|
6419
|
+
allowPrivateNetwork: options.allowPrivateNetwork === true,
|
|
6420
|
+
dnsResolver: options.dnsResolver,
|
|
6421
|
+
fetch: options.fetch ?? globalThis.fetch,
|
|
6422
|
+
now: options.now ?? (() => /* @__PURE__ */ new Date())
|
|
6423
|
+
};
|
|
6743
6424
|
}
|
|
6744
|
-
function
|
|
6745
|
-
|
|
6425
|
+
function boundedInteger(value, fallback, minimum, maximum, name) {
|
|
6426
|
+
const normalized = value ?? fallback;
|
|
6427
|
+
if (!Number.isInteger(normalized) || normalized < minimum || normalized > maximum) throw new Error(`${name} must be an integer from ${minimum} to ${maximum}`);
|
|
6428
|
+
return normalized;
|
|
6746
6429
|
}
|
|
6747
|
-
function
|
|
6748
|
-
return
|
|
6430
|
+
function normalizePatterns(values) {
|
|
6431
|
+
return uniqueSorted(values.map((value) => value.trim()).filter((value) => {
|
|
6432
|
+
if (!value.startsWith("/") || value.length > 255) throw new Error("include and exclude patterns must be path globs starting with /");
|
|
6433
|
+
return true;
|
|
6434
|
+
}));
|
|
6749
6435
|
}
|
|
6750
|
-
function
|
|
6751
|
-
|
|
6752
|
-
|
|
6436
|
+
function pathMatcher(value) {
|
|
6437
|
+
let source = "^";
|
|
6438
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
6439
|
+
const character = value[index];
|
|
6440
|
+
if (character === "*" && value[index + 1] === "*") {
|
|
6441
|
+
source += ".*";
|
|
6442
|
+
index += 1;
|
|
6443
|
+
} else if (character === "*") source += "[^/]*";
|
|
6444
|
+
else source += escapeRegExp(character ?? "");
|
|
6445
|
+
}
|
|
6446
|
+
return {
|
|
6447
|
+
value,
|
|
6448
|
+
pattern: new RegExp(`${source}$`)
|
|
6449
|
+
};
|
|
6753
6450
|
}
|
|
6754
|
-
function
|
|
6755
|
-
|
|
6451
|
+
function pathEligibility(url, options) {
|
|
6452
|
+
const path = url.pathname;
|
|
6453
|
+
if (options.include.length > 0 && !options.include.some((item) => item.pattern.test(path))) return "not_included";
|
|
6454
|
+
if (options.exclude.some((item) => item.pattern.test(path))) return "excluded";
|
|
6455
|
+
return null;
|
|
6456
|
+
}
|
|
6457
|
+
async function loadRobots(options) {
|
|
6458
|
+
const url = new URL("/robots.txt", options.startUrl.origin).href;
|
|
6459
|
+
const resource = await fetchResource(url, options);
|
|
6460
|
+
if (!resource.response) return {
|
|
6461
|
+
source: {
|
|
6462
|
+
url,
|
|
6463
|
+
status: "temporarily_unavailable",
|
|
6464
|
+
httpStatus: null,
|
|
6465
|
+
error: resource.error ?? "fetch_failed"
|
|
6466
|
+
},
|
|
6467
|
+
rules: [],
|
|
6468
|
+
sitemaps: []
|
|
6469
|
+
};
|
|
6470
|
+
if (resource.response.status === 404) return {
|
|
6471
|
+
source: {
|
|
6472
|
+
url,
|
|
6473
|
+
status: "not_found",
|
|
6474
|
+
httpStatus: 404
|
|
6475
|
+
},
|
|
6476
|
+
rules: [],
|
|
6477
|
+
sitemaps: []
|
|
6478
|
+
};
|
|
6479
|
+
if (!resource.response.ok || resource.error) return {
|
|
6480
|
+
source: {
|
|
6481
|
+
url,
|
|
6482
|
+
status: "temporarily_unavailable",
|
|
6483
|
+
httpStatus: resource.response.status,
|
|
6484
|
+
error: resource.error ?? `http_${resource.response.status}`
|
|
6485
|
+
},
|
|
6486
|
+
rules: [],
|
|
6487
|
+
sitemaps: []
|
|
6488
|
+
};
|
|
6489
|
+
const parsed = parseRobots(resource.body);
|
|
6756
6490
|
return {
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6491
|
+
source: {
|
|
6492
|
+
url,
|
|
6493
|
+
status: "available",
|
|
6494
|
+
httpStatus: resource.response.status
|
|
6495
|
+
},
|
|
6496
|
+
rules: parsed.rules,
|
|
6497
|
+
sitemaps: parsed.sitemaps
|
|
6762
6498
|
};
|
|
6763
6499
|
}
|
|
6764
|
-
|
|
6765
|
-
const
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
|
|
6787
|
-
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
async loadCredentials() {
|
|
6797
|
-
const metadata = await readForgewayDeployMetadata(this.cwd).catch(() => ({}));
|
|
6798
|
-
const platformApiUrl = resolveForgewaySeoApiUrl(metadata.platformApiUrl, this.cwd);
|
|
6799
|
-
const credentials = await this.readCredentials({
|
|
6800
|
-
cwd: this.cwd,
|
|
6801
|
-
platformApiUrl,
|
|
6802
|
-
email: typeof metadata.publisherEmail === "string" ? metadata.publisherEmail : void 0
|
|
6500
|
+
function parseRobots(body) {
|
|
6501
|
+
const groups = [];
|
|
6502
|
+
const sitemaps = [];
|
|
6503
|
+
let current = {
|
|
6504
|
+
agents: [],
|
|
6505
|
+
rules: []
|
|
6506
|
+
};
|
|
6507
|
+
for (const rawLine of body.split(LINE_SPLIT_RE)) {
|
|
6508
|
+
const line = rawLine.replace(COMMENT_RE, "").trim();
|
|
6509
|
+
if (!line) continue;
|
|
6510
|
+
const separator = line.indexOf(":");
|
|
6511
|
+
if (separator < 0) continue;
|
|
6512
|
+
const field = line.slice(0, separator).trim().toLowerCase();
|
|
6513
|
+
const value = line.slice(separator + 1).trim();
|
|
6514
|
+
if (field === "sitemap" && value) {
|
|
6515
|
+
sitemaps.push(value);
|
|
6516
|
+
continue;
|
|
6517
|
+
}
|
|
6518
|
+
if (field === "user-agent") {
|
|
6519
|
+
if (current.rules.length > 0) {
|
|
6520
|
+
groups.push(current);
|
|
6521
|
+
current = {
|
|
6522
|
+
agents: [],
|
|
6523
|
+
rules: []
|
|
6524
|
+
};
|
|
6525
|
+
}
|
|
6526
|
+
current.agents.push(value.toLowerCase());
|
|
6527
|
+
continue;
|
|
6528
|
+
}
|
|
6529
|
+
if ((field === "allow" || field === "disallow") && current.agents.length > 0 && value) current.rules.push({
|
|
6530
|
+
type: field,
|
|
6531
|
+
path: value
|
|
6803
6532
|
});
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
|
|
6533
|
+
}
|
|
6534
|
+
if (current.agents.length > 0 || current.rules.length > 0) groups.push(current);
|
|
6535
|
+
const crawlerName = USER_AGENT.split("/")[0]?.toLowerCase() ?? "dinewaycontentcrawler";
|
|
6536
|
+
const exact = groups.filter((group) => group.agents.includes(crawlerName));
|
|
6537
|
+
return {
|
|
6538
|
+
rules: (exact.length > 0 ? exact : groups.filter((group) => group.agents.includes("*"))).flatMap((group) => group.rules),
|
|
6539
|
+
sitemaps: uniqueSorted(sitemaps)
|
|
6540
|
+
};
|
|
6541
|
+
}
|
|
6542
|
+
function robotsAllows(url, rules) {
|
|
6543
|
+
const path = `${url.pathname}${url.search}`;
|
|
6544
|
+
const matching = rules.filter((rule) => robotsRuleMatches(path, rule.path)).toSorted((left, right) => right.path.length - left.path.length);
|
|
6545
|
+
if (matching.length === 0) return true;
|
|
6546
|
+
const longest = matching[0]?.path.length ?? 0;
|
|
6547
|
+
return matching.some((rule) => rule.path.length === longest && rule.type === "allow");
|
|
6548
|
+
}
|
|
6549
|
+
function robotsRuleMatches(path, rulePath) {
|
|
6550
|
+
const anchored = rulePath.endsWith("$");
|
|
6551
|
+
const pattern = escapeRegExp(anchored ? rulePath.slice(0, -1) : rulePath).replaceAll("\\*", ".*");
|
|
6552
|
+
return new RegExp(`^${pattern}${anchored ? "$" : ""}`).test(path);
|
|
6553
|
+
}
|
|
6554
|
+
async function loadSitemaps(options, robotsSitemaps) {
|
|
6555
|
+
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);
|
|
6556
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6557
|
+
const sources = [];
|
|
6558
|
+
const urls = [];
|
|
6559
|
+
while (queue.length > 0 && seen.size < MAX_SITEMAPS) {
|
|
6560
|
+
const url = queue.shift();
|
|
6561
|
+
if (!url || seen.has(url)) continue;
|
|
6562
|
+
seen.add(url);
|
|
6563
|
+
const resource = await fetchResource(url, options);
|
|
6564
|
+
if (!resource.response) {
|
|
6565
|
+
sources.push({
|
|
6566
|
+
url,
|
|
6567
|
+
status: "temporarily_unavailable",
|
|
6568
|
+
httpStatus: null,
|
|
6569
|
+
error: resource.error ?? "fetch_failed"
|
|
6570
|
+
});
|
|
6571
|
+
continue;
|
|
6572
|
+
}
|
|
6573
|
+
if (resource.response.status === 404) {
|
|
6574
|
+
sources.push({
|
|
6575
|
+
url,
|
|
6576
|
+
status: "not_found",
|
|
6577
|
+
httpStatus: 404
|
|
6578
|
+
});
|
|
6579
|
+
continue;
|
|
6580
|
+
}
|
|
6581
|
+
if (!resource.response.ok || resource.error) {
|
|
6582
|
+
sources.push({
|
|
6583
|
+
url,
|
|
6584
|
+
status: "temporarily_unavailable",
|
|
6585
|
+
httpStatus: resource.response.status,
|
|
6586
|
+
error: resource.error ?? `http_${resource.response.status}`
|
|
6587
|
+
});
|
|
6588
|
+
continue;
|
|
6589
|
+
}
|
|
6590
|
+
try {
|
|
6591
|
+
const parsed = parseSitemap(resource.body);
|
|
6592
|
+
sources.push({
|
|
6593
|
+
url,
|
|
6594
|
+
status: "available",
|
|
6595
|
+
httpStatus: resource.response.status
|
|
6596
|
+
});
|
|
6597
|
+
for (const location of parsed.locations) {
|
|
6598
|
+
const normalized = normalizeUrl(location, options.startUrl, options.keepQuery);
|
|
6599
|
+
if (!normalized || normalized.origin !== options.startUrl.origin) continue;
|
|
6600
|
+
if (parsed.kind === "index") queue.push(normalized.href);
|
|
6601
|
+
else urls.push(normalized.href);
|
|
6602
|
+
}
|
|
6603
|
+
} catch {
|
|
6604
|
+
sources.push({
|
|
6605
|
+
url,
|
|
6606
|
+
status: "invalid",
|
|
6607
|
+
httpStatus: resource.response.status,
|
|
6608
|
+
error: "invalid_sitemap_xml"
|
|
6609
|
+
});
|
|
6610
|
+
}
|
|
6611
|
+
}
|
|
6612
|
+
return {
|
|
6613
|
+
sources,
|
|
6614
|
+
urls: uniqueSorted(urls)
|
|
6615
|
+
};
|
|
6616
|
+
}
|
|
6617
|
+
function parseSitemap(body) {
|
|
6618
|
+
const parser = sax.parser(true, {
|
|
6619
|
+
trim: true,
|
|
6620
|
+
normalize: true
|
|
6621
|
+
});
|
|
6622
|
+
let root = null;
|
|
6623
|
+
let inLocation = false;
|
|
6624
|
+
let location = "";
|
|
6625
|
+
const locations = [];
|
|
6626
|
+
parser.onopentag = (node) => {
|
|
6627
|
+
const name = node.name.toLowerCase();
|
|
6628
|
+
root ??= name;
|
|
6629
|
+
if (name === "loc") {
|
|
6630
|
+
inLocation = true;
|
|
6631
|
+
location = "";
|
|
6632
|
+
}
|
|
6633
|
+
};
|
|
6634
|
+
parser.ontext = (text) => {
|
|
6635
|
+
if (inLocation) location += text;
|
|
6636
|
+
};
|
|
6637
|
+
parser.oncdata = (text) => {
|
|
6638
|
+
if (inLocation) location += text;
|
|
6639
|
+
};
|
|
6640
|
+
parser.onclosetag = (name) => {
|
|
6641
|
+
if (name.toLowerCase() !== "loc") return;
|
|
6642
|
+
inLocation = false;
|
|
6643
|
+
if (location.trim()) locations.push(location.trim());
|
|
6644
|
+
};
|
|
6645
|
+
parser.write(body).close();
|
|
6646
|
+
if (root !== "urlset" && root !== "sitemapindex") throw new Error("Unsupported sitemap root");
|
|
6647
|
+
return {
|
|
6648
|
+
kind: root === "sitemapindex" ? "index" : "urls",
|
|
6649
|
+
locations
|
|
6650
|
+
};
|
|
6651
|
+
}
|
|
6652
|
+
async function fetchResource(url, options) {
|
|
6653
|
+
const controller = new AbortController();
|
|
6654
|
+
const timeout = setTimeout(() => controller.abort(), options.timeoutMs);
|
|
6655
|
+
try {
|
|
6656
|
+
let currentUrl = url;
|
|
6657
|
+
let response = null;
|
|
6658
|
+
for (let redirects = 0; redirects <= MAX_REDIRECTS; redirects += 1) {
|
|
6659
|
+
if (!options.allowPrivateNetwork) await resolveAndValidateExternalUrl(currentUrl, { resolver: options.dnsResolver });
|
|
6660
|
+
response = await options.fetch(currentUrl, {
|
|
6661
|
+
headers: {
|
|
6662
|
+
accept: "text/html,application/xml,text/xml,text/plain;q=0.8",
|
|
6663
|
+
"user-agent": USER_AGENT
|
|
6664
|
+
},
|
|
6665
|
+
redirect: "manual",
|
|
6666
|
+
signal: controller.signal
|
|
6667
|
+
});
|
|
6668
|
+
const location = response.headers.get("location");
|
|
6669
|
+
if (response.status < 300 || response.status >= 400 || !location) break;
|
|
6670
|
+
const next = normalizeUrl(location, new URL(currentUrl), options.keepQuery);
|
|
6671
|
+
if (!next) return {
|
|
6672
|
+
response,
|
|
6673
|
+
finalUrl: currentUrl,
|
|
6674
|
+
contentType: response.headers.get("content-type"),
|
|
6675
|
+
body: "",
|
|
6676
|
+
bytes: 0,
|
|
6677
|
+
error: "invalid_redirect"
|
|
6678
|
+
};
|
|
6679
|
+
if (next.origin !== options.startUrl.origin) return {
|
|
6680
|
+
response,
|
|
6681
|
+
finalUrl: next.href,
|
|
6682
|
+
contentType: response.headers.get("content-type"),
|
|
6683
|
+
body: "",
|
|
6684
|
+
bytes: 0,
|
|
6685
|
+
error: "cross_origin_redirect"
|
|
6686
|
+
};
|
|
6687
|
+
if (redirects === MAX_REDIRECTS) return {
|
|
6688
|
+
response,
|
|
6689
|
+
finalUrl: next.href,
|
|
6690
|
+
contentType: response.headers.get("content-type"),
|
|
6691
|
+
body: "",
|
|
6692
|
+
bytes: 0,
|
|
6693
|
+
error: "too_many_redirects"
|
|
6694
|
+
};
|
|
6695
|
+
currentUrl = next.href;
|
|
6696
|
+
}
|
|
6697
|
+
if (!response) throw new Error("No response");
|
|
6698
|
+
const finalUrl = response.url || currentUrl;
|
|
6699
|
+
const contentType = response.headers.get("content-type");
|
|
6700
|
+
const declaredLength = Number(response.headers.get("content-length"));
|
|
6701
|
+
if (Number.isFinite(declaredLength) && declaredLength > options.maxBytesPerResource) return {
|
|
6702
|
+
response,
|
|
6703
|
+
finalUrl,
|
|
6704
|
+
contentType,
|
|
6705
|
+
body: "",
|
|
6706
|
+
bytes: 0,
|
|
6707
|
+
error: "resource_too_large"
|
|
6708
|
+
};
|
|
6709
|
+
const body = await readBoundedBody(response, options.maxBytesPerResource);
|
|
6807
6710
|
return {
|
|
6808
|
-
|
|
6809
|
-
|
|
6711
|
+
response,
|
|
6712
|
+
finalUrl,
|
|
6713
|
+
contentType,
|
|
6714
|
+
body: body.text,
|
|
6715
|
+
bytes: body.bytes
|
|
6810
6716
|
};
|
|
6717
|
+
} catch (error) {
|
|
6718
|
+
return {
|
|
6719
|
+
response: null,
|
|
6720
|
+
finalUrl: url,
|
|
6721
|
+
contentType: null,
|
|
6722
|
+
body: "",
|
|
6723
|
+
bytes: 0,
|
|
6724
|
+
error: error instanceof ResourceTooLargeError ? "resource_too_large" : fetchError(error)
|
|
6725
|
+
};
|
|
6726
|
+
} finally {
|
|
6727
|
+
clearTimeout(timeout);
|
|
6811
6728
|
}
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6729
|
+
}
|
|
6730
|
+
async function readBoundedBody(response, limit) {
|
|
6731
|
+
if (!response.body) return {
|
|
6732
|
+
text: "",
|
|
6733
|
+
bytes: 0
|
|
6734
|
+
};
|
|
6735
|
+
const reader = response.body.getReader();
|
|
6736
|
+
const chunks = [];
|
|
6737
|
+
let bytes = 0;
|
|
6738
|
+
while (true) {
|
|
6739
|
+
const result = await reader.read();
|
|
6740
|
+
if (result.done) break;
|
|
6741
|
+
bytes += result.value.byteLength;
|
|
6742
|
+
if (bytes > limit) {
|
|
6743
|
+
await reader.cancel();
|
|
6744
|
+
throw new ResourceTooLargeError();
|
|
6745
|
+
}
|
|
6746
|
+
chunks.push(result.value);
|
|
6747
|
+
}
|
|
6748
|
+
const combined = new Uint8Array(bytes);
|
|
6749
|
+
let offset = 0;
|
|
6750
|
+
for (const chunk of chunks) {
|
|
6751
|
+
combined.set(chunk, offset);
|
|
6752
|
+
offset += chunk.byteLength;
|
|
6753
|
+
}
|
|
6754
|
+
return {
|
|
6755
|
+
text: new TextDecoder().decode(combined),
|
|
6756
|
+
bytes
|
|
6757
|
+
};
|
|
6758
|
+
}
|
|
6759
|
+
var ResourceTooLargeError = class extends Error {};
|
|
6760
|
+
function fetchError(error) {
|
|
6761
|
+
if (error instanceof SsrfError) return "private_network_blocked";
|
|
6762
|
+
if (error instanceof Error && error.name === "AbortError") return "timeout";
|
|
6763
|
+
return "fetch_failed";
|
|
6764
|
+
}
|
|
6765
|
+
function pageFromResource(item, resource, options) {
|
|
6766
|
+
const base = {
|
|
6767
|
+
url: item.url,
|
|
6768
|
+
finalUrl: resource.finalUrl,
|
|
6769
|
+
depth: item.depth,
|
|
6770
|
+
discoveredFrom: item.discoveredFrom,
|
|
6771
|
+
httpStatus: resource.response?.status ?? null,
|
|
6772
|
+
contentType: resource.contentType,
|
|
6773
|
+
bytes: resource.bytes
|
|
6774
|
+
};
|
|
6775
|
+
const empty = {
|
|
6776
|
+
title: null,
|
|
6777
|
+
description: null,
|
|
6778
|
+
canonicalUrl: null,
|
|
6779
|
+
robots: null,
|
|
6780
|
+
language: null,
|
|
6781
|
+
headings: {
|
|
6782
|
+
h1: [],
|
|
6783
|
+
h2: [],
|
|
6784
|
+
h3: []
|
|
6785
|
+
},
|
|
6786
|
+
wordCount: 0,
|
|
6787
|
+
contentSha256: null,
|
|
6788
|
+
textExcerpt: "",
|
|
6789
|
+
textTruncated: false,
|
|
6790
|
+
internalLinks: [],
|
|
6791
|
+
externalLinks: [],
|
|
6792
|
+
structuredDataBlocks: 0,
|
|
6793
|
+
renderingRecommended: false
|
|
6794
|
+
};
|
|
6795
|
+
if (resource.error) return {
|
|
6796
|
+
...base,
|
|
6797
|
+
...empty,
|
|
6798
|
+
error: resource.error
|
|
6799
|
+
};
|
|
6800
|
+
const final = normalizeUrl(resource.finalUrl, void 0, options.keepQuery);
|
|
6801
|
+
if (!final || final.origin !== options.startUrl.origin) return {
|
|
6802
|
+
...base,
|
|
6803
|
+
...empty,
|
|
6804
|
+
error: "cross_origin_redirect"
|
|
6805
|
+
};
|
|
6806
|
+
if (!resource.contentType?.includes("text/html")) return {
|
|
6807
|
+
...base,
|
|
6808
|
+
...empty
|
|
6809
|
+
};
|
|
6810
|
+
try {
|
|
6811
|
+
return {
|
|
6812
|
+
...base,
|
|
6813
|
+
...parseHtml(resource.body, final, options.keepQuery)
|
|
6814
|
+
};
|
|
6815
|
+
} catch {
|
|
6816
|
+
return {
|
|
6817
|
+
...base,
|
|
6818
|
+
...empty,
|
|
6819
|
+
error: "invalid_html"
|
|
6828
6820
|
};
|
|
6829
|
-
await this.writeCredentials(updated, {
|
|
6830
|
-
cwd: this.cwd,
|
|
6831
|
-
platformApiUrl: updated.platformApiUrl,
|
|
6832
|
-
email: user.email
|
|
6833
|
-
});
|
|
6834
|
-
return updated;
|
|
6835
6821
|
}
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6822
|
+
}
|
|
6823
|
+
function parseHtml(body, pageUrl, keepQuery) {
|
|
6824
|
+
const document = parse(body);
|
|
6825
|
+
let title = null;
|
|
6826
|
+
let description = null;
|
|
6827
|
+
let canonicalUrl = null;
|
|
6828
|
+
let robots = null;
|
|
6829
|
+
let language = null;
|
|
6830
|
+
let structuredDataBlocks = 0;
|
|
6831
|
+
let scriptBlocks = 0;
|
|
6832
|
+
const headings = {
|
|
6833
|
+
h1: [],
|
|
6834
|
+
h2: [],
|
|
6835
|
+
h3: []
|
|
6836
|
+
};
|
|
6837
|
+
const internalLinks = [];
|
|
6838
|
+
const externalLinks = [];
|
|
6839
|
+
walk(document, (element) => {
|
|
6840
|
+
const attributes = new Map(element.attrs.map((attribute) => [attribute.name, attribute.value]));
|
|
6841
|
+
if (element.tagName === "html") language = cleanText(attributes.get("lang")) || language;
|
|
6842
|
+
if (element.tagName === "title" && title === null) title = cleanText(textContent(element));
|
|
6843
|
+
if (element.tagName === "meta") {
|
|
6844
|
+
const name = attributes.get("name")?.toLowerCase();
|
|
6845
|
+
if (name === "description" && description === null) description = cleanText(attributes.get("content"));
|
|
6846
|
+
if ((name === "robots" || name === "googlebot") && robots === null) robots = cleanText(attributes.get("content"));
|
|
6847
|
+
}
|
|
6848
|
+
if (element.tagName === "link") {
|
|
6849
|
+
if ((attributes.get("rel")?.toLowerCase().split(WHITESPACE_RE) ?? []).includes("canonical") && canonicalUrl === null) canonicalUrl = normalizeUrl(attributes.get("href") ?? "", pageUrl, keepQuery)?.href ?? null;
|
|
6850
|
+
}
|
|
6851
|
+
if (element.tagName === "h1" || element.tagName === "h2" || element.tagName === "h3") {
|
|
6852
|
+
const value = cleanText(textContent(element));
|
|
6853
|
+
if (value) headings[element.tagName].push(value);
|
|
6854
|
+
}
|
|
6855
|
+
if (element.tagName === "script") {
|
|
6856
|
+
scriptBlocks += 1;
|
|
6857
|
+
if (attributes.get("type")?.toLowerCase() === "application/ld+json") structuredDataBlocks += 1;
|
|
6858
|
+
}
|
|
6859
|
+
if (element.tagName === "a") {
|
|
6860
|
+
const link = normalizeUrl(attributes.get("href") ?? "", pageUrl, keepQuery);
|
|
6861
|
+
if (!link) return;
|
|
6862
|
+
const item = {
|
|
6863
|
+
url: link.href,
|
|
6864
|
+
anchor: cleanText(textContent(element)) ?? ""
|
|
6865
|
+
};
|
|
6866
|
+
if (link.origin === pageUrl.origin) internalLinks.push(item);
|
|
6867
|
+
else externalLinks.push(item);
|
|
6868
|
+
}
|
|
6869
|
+
});
|
|
6870
|
+
const visibleText = cleanText(textContent(document)) ?? "";
|
|
6871
|
+
const wordCount = countWords(visibleText);
|
|
6872
|
+
const textExcerpt = visibleText.slice(0, MAX_TEXT_EXCERPT);
|
|
6873
|
+
return {
|
|
6874
|
+
title,
|
|
6875
|
+
description,
|
|
6876
|
+
canonicalUrl,
|
|
6877
|
+
robots,
|
|
6878
|
+
language,
|
|
6879
|
+
headings,
|
|
6880
|
+
wordCount,
|
|
6881
|
+
contentSha256: createHash("sha256").update(visibleText).digest("hex"),
|
|
6882
|
+
textExcerpt,
|
|
6883
|
+
textTruncated: textExcerpt.length < visibleText.length,
|
|
6884
|
+
internalLinks: uniqueLinks(internalLinks),
|
|
6885
|
+
externalLinks: uniqueLinks(externalLinks),
|
|
6886
|
+
structuredDataBlocks,
|
|
6887
|
+
renderingRecommended: wordCount < 20 && scriptBlocks > 0
|
|
6888
|
+
};
|
|
6889
|
+
}
|
|
6890
|
+
function walk(node, visit) {
|
|
6891
|
+
if (isElement(node)) visit(node);
|
|
6892
|
+
if (!("childNodes" in node)) return;
|
|
6893
|
+
for (const child of node.childNodes) walk(child, visit);
|
|
6894
|
+
}
|
|
6895
|
+
function textContent(node, hidden = false) {
|
|
6896
|
+
if ("value" in node) return hidden ? "" : node.value;
|
|
6897
|
+
const nextHidden = hidden || isElement(node) && [
|
|
6898
|
+
"script",
|
|
6899
|
+
"style",
|
|
6900
|
+
"noscript",
|
|
6901
|
+
"template",
|
|
6902
|
+
"svg",
|
|
6903
|
+
"head"
|
|
6904
|
+
].includes(node.tagName);
|
|
6905
|
+
if (!("childNodes" in node)) return "";
|
|
6906
|
+
return node.childNodes.map((child) => textContent(child, nextHidden)).join(" ");
|
|
6907
|
+
}
|
|
6908
|
+
function isElement(node) {
|
|
6909
|
+
return "tagName" in node;
|
|
6910
|
+
}
|
|
6911
|
+
function normalizeUrl(value, base, keepQuery = false) {
|
|
6912
|
+
try {
|
|
6913
|
+
const url = base ? new URL(value, base) : new URL(value);
|
|
6914
|
+
if (url.protocol !== "http:" && url.protocol !== "https:" || url.username || url.password) return null;
|
|
6915
|
+
url.hash = "";
|
|
6916
|
+
if (!keepQuery) url.search = "";
|
|
6917
|
+
if (!url.pathname) url.pathname = "/";
|
|
6918
|
+
return url;
|
|
6919
|
+
} catch {
|
|
6920
|
+
return null;
|
|
6851
6921
|
}
|
|
6852
|
-
}
|
|
6922
|
+
}
|
|
6923
|
+
function cleanText(value) {
|
|
6924
|
+
if (!value) return null;
|
|
6925
|
+
return value.replaceAll(WHITESPACE_RE, " ").trim() || null;
|
|
6926
|
+
}
|
|
6927
|
+
function countWords(value) {
|
|
6928
|
+
return value.match(WORD_RE)?.length ?? 0;
|
|
6929
|
+
}
|
|
6930
|
+
function uniqueLinks(links) {
|
|
6931
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6932
|
+
return links.filter((link) => {
|
|
6933
|
+
const key = `${link.url}\u0000${link.anchor}`;
|
|
6934
|
+
if (seen.has(key)) return false;
|
|
6935
|
+
seen.add(key);
|
|
6936
|
+
return true;
|
|
6937
|
+
});
|
|
6938
|
+
}
|
|
6939
|
+
function duplicateGroups(pages, field) {
|
|
6940
|
+
const groups = /* @__PURE__ */ new Map();
|
|
6941
|
+
for (const page of pages) {
|
|
6942
|
+
const value = page[field];
|
|
6943
|
+
if (!value) continue;
|
|
6944
|
+
const key = field === "contentSha256" ? value : value.toLocaleLowerCase();
|
|
6945
|
+
const group = groups.get(key) ?? {
|
|
6946
|
+
value,
|
|
6947
|
+
urls: []
|
|
6948
|
+
};
|
|
6949
|
+
group.urls.push(page.url);
|
|
6950
|
+
groups.set(key, group);
|
|
6951
|
+
}
|
|
6952
|
+
return [...groups.values()].filter((group) => group.urls.length > 1).map((group) => ({
|
|
6953
|
+
value: group.value,
|
|
6954
|
+
urls: uniqueSorted(group.urls)
|
|
6955
|
+
})).toSorted((left, right) => left.value.localeCompare(right.value));
|
|
6956
|
+
}
|
|
6957
|
+
function uniqueSorted(values) {
|
|
6958
|
+
return [...new Set(values)].toSorted((left, right) => left.localeCompare(right));
|
|
6959
|
+
}
|
|
6960
|
+
function escapeRegExp(value) {
|
|
6961
|
+
return value.replace(REGEXP_SPECIAL_RE, "\\$&");
|
|
6962
|
+
}
|
|
6853
6963
|
|
|
6854
6964
|
//#endregion
|
|
6855
6965
|
//#region src/cli/commands/seo.ts
|
|
6856
|
-
const
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
},
|
|
6861
|
-
"force-refresh": {
|
|
6862
|
-
type: "boolean",
|
|
6863
|
-
description: "Bypass Forgeway SEO provider cache"
|
|
6864
|
-
}
|
|
6865
|
-
};
|
|
6866
|
-
const dataForSeoCommonArgs = {
|
|
6867
|
-
...hostedProviderArgs,
|
|
6868
|
-
"location-code": {
|
|
6869
|
-
type: "string",
|
|
6870
|
-
description: "DataForSEO location code",
|
|
6871
|
-
default: "2840"
|
|
6872
|
-
},
|
|
6873
|
-
"language-code": {
|
|
6874
|
-
type: "string",
|
|
6875
|
-
description: "DataForSEO language code",
|
|
6876
|
-
default: "en"
|
|
6877
|
-
},
|
|
6878
|
-
device: {
|
|
6879
|
-
type: "string",
|
|
6880
|
-
description: "Device type: desktop or mobile",
|
|
6881
|
-
default: "desktop"
|
|
6882
|
-
}
|
|
6883
|
-
};
|
|
6966
|
+
const jsonArgs = { json: {
|
|
6967
|
+
type: "boolean",
|
|
6968
|
+
description: "Output as JSON"
|
|
6969
|
+
} };
|
|
6884
6970
|
function toStringValue(value) {
|
|
6885
6971
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
6886
6972
|
}
|
|
@@ -6890,9 +6976,6 @@ function toNumberValue(value, fallback) {
|
|
|
6890
6976
|
const parsed = Number(raw);
|
|
6891
6977
|
return Number.isFinite(parsed) ? parsed : fallback;
|
|
6892
6978
|
}
|
|
6893
|
-
function forceRefresh(args) {
|
|
6894
|
-
return args["force-refresh"] === true;
|
|
6895
|
-
}
|
|
6896
6979
|
function formats(value) {
|
|
6897
6980
|
const raw = toStringValue(value);
|
|
6898
6981
|
if (!raw) return void 0;
|
|
@@ -6903,416 +6986,108 @@ function requireArg(value, name) {
|
|
|
6903
6986
|
if (!normalized) throw new Error(`${name} is required.`);
|
|
6904
6987
|
return normalized;
|
|
6905
6988
|
}
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
} catch (error) {
|
|
6911
|
-
if (await handleCliPaywall({
|
|
6912
|
-
error,
|
|
6913
|
-
resource: path,
|
|
6914
|
-
context: { providerPath: path }
|
|
6915
|
-
})) process.exit(2);
|
|
6916
|
-
consola$1.error(error instanceof Error ? error.message : "Unknown error");
|
|
6917
|
-
process.exit(1);
|
|
6918
|
-
}
|
|
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.");
|
|
6919
6993
|
}
|
|
6920
|
-
const
|
|
6994
|
+
const crawlCommand = defineCommand({
|
|
6921
6995
|
meta: {
|
|
6922
|
-
name: "
|
|
6923
|
-
description: "
|
|
6996
|
+
name: "crawl",
|
|
6997
|
+
description: "Deterministic site crawl controls"
|
|
6924
6998
|
},
|
|
6925
|
-
subCommands: {
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
|
|
6929
|
-
|
|
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
|
|
6930
7009
|
},
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
},
|
|
6937
|
-
...hostedProviderArgs,
|
|
6938
|
-
formats: {
|
|
6939
|
-
type: "string",
|
|
6940
|
-
description: "Comma-separated Firecrawl formats",
|
|
6941
|
-
default: "markdown"
|
|
6942
|
-
},
|
|
6943
|
-
"only-main-content": {
|
|
6944
|
-
type: "boolean",
|
|
6945
|
-
description: "Return main content only",
|
|
6946
|
-
default: true
|
|
6947
|
-
},
|
|
6948
|
-
"timeout-ms": {
|
|
6949
|
-
type: "string",
|
|
6950
|
-
description: "Provider timeout in milliseconds",
|
|
6951
|
-
default: "60000"
|
|
6952
|
-
}
|
|
7010
|
+
...jsonArgs,
|
|
7011
|
+
limit: {
|
|
7012
|
+
type: "string",
|
|
7013
|
+
description: "Maximum pages",
|
|
7014
|
+
default: "100"
|
|
6953
7015
|
},
|
|
6954
|
-
|
|
6955
|
-
|
|
6956
|
-
|
|
6957
|
-
|
|
6958
|
-
onlyMainContent: args["only-main-content"] !== false,
|
|
6959
|
-
timeoutMs: toNumberValue(args["timeout-ms"], 6e4),
|
|
6960
|
-
forceRefresh: forceRefresh(args)
|
|
6961
|
-
});
|
|
6962
|
-
}
|
|
6963
|
-
}),
|
|
6964
|
-
map: defineCommand({
|
|
6965
|
-
meta: {
|
|
6966
|
-
name: "map",
|
|
6967
|
-
description: "Map URLs through Forgeway-hosted Firecrawl"
|
|
7016
|
+
"max-depth": {
|
|
7017
|
+
type: "string",
|
|
7018
|
+
description: "Maximum link discovery depth",
|
|
7019
|
+
default: "3"
|
|
6968
7020
|
},
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
|
|
6972
|
-
|
|
6973
|
-
required: true
|
|
6974
|
-
},
|
|
6975
|
-
...hostedProviderArgs,
|
|
6976
|
-
search: {
|
|
6977
|
-
type: "string",
|
|
6978
|
-
description: "Optional relevance search term"
|
|
6979
|
-
},
|
|
6980
|
-
sitemap: {
|
|
6981
|
-
type: "string",
|
|
6982
|
-
description: "Sitemap mode: skip, include, or only",
|
|
6983
|
-
default: "include"
|
|
6984
|
-
},
|
|
6985
|
-
limit: {
|
|
6986
|
-
type: "string",
|
|
6987
|
-
description: "Maximum URLs",
|
|
6988
|
-
default: "100"
|
|
6989
|
-
},
|
|
6990
|
-
"include-subdomains": {
|
|
6991
|
-
type: "boolean",
|
|
6992
|
-
description: "Include subdomains"
|
|
6993
|
-
},
|
|
6994
|
-
"ignore-query-parameters": {
|
|
6995
|
-
type: "boolean",
|
|
6996
|
-
description: "Ignore query parameters",
|
|
6997
|
-
default: true
|
|
6998
|
-
}
|
|
7021
|
+
concurrency: {
|
|
7022
|
+
type: "string",
|
|
7023
|
+
description: "Maximum concurrent page requests",
|
|
7024
|
+
default: "4"
|
|
6999
7025
|
},
|
|
7000
|
-
|
|
7001
|
-
|
|
7002
|
-
|
|
7003
|
-
search: toStringValue(args.search),
|
|
7004
|
-
sitemap: toStringValue(args.sitemap) ?? "include",
|
|
7005
|
-
limit: toNumberValue(args.limit, 100),
|
|
7006
|
-
includeSubdomains: args["include-subdomains"] === true,
|
|
7007
|
-
ignoreQueryParameters: args["ignore-query-parameters"] !== false,
|
|
7008
|
-
forceRefresh: forceRefresh(args)
|
|
7009
|
-
});
|
|
7010
|
-
}
|
|
7011
|
-
}),
|
|
7012
|
-
crawl: defineCommand({
|
|
7013
|
-
meta: {
|
|
7014
|
-
name: "crawl",
|
|
7015
|
-
description: "Start a bounded Firecrawl crawl, or check status with --id"
|
|
7026
|
+
include: {
|
|
7027
|
+
type: "string",
|
|
7028
|
+
description: "Comma-separated path globs to include"
|
|
7016
7029
|
},
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
description: "Base URL to crawl"
|
|
7021
|
-
},
|
|
7022
|
-
...hostedProviderArgs,
|
|
7023
|
-
id: {
|
|
7024
|
-
type: "string",
|
|
7025
|
-
description: "Existing Firecrawl crawl id to check"
|
|
7026
|
-
},
|
|
7027
|
-
limit: {
|
|
7028
|
-
type: "string",
|
|
7029
|
-
description: "Maximum pages",
|
|
7030
|
-
default: "25"
|
|
7031
|
-
},
|
|
7032
|
-
"max-depth": {
|
|
7033
|
-
type: "string",
|
|
7034
|
-
description: "Maximum discovery depth",
|
|
7035
|
-
default: "3"
|
|
7036
|
-
},
|
|
7037
|
-
"allow-subdomains": {
|
|
7038
|
-
type: "boolean",
|
|
7039
|
-
description: "Allow subdomains"
|
|
7040
|
-
},
|
|
7041
|
-
"allow-external-links": {
|
|
7042
|
-
type: "boolean",
|
|
7043
|
-
description: "Allow external links"
|
|
7044
|
-
},
|
|
7045
|
-
formats: {
|
|
7046
|
-
type: "string",
|
|
7047
|
-
description: "Comma-separated scrape formats",
|
|
7048
|
-
default: "markdown"
|
|
7049
|
-
}
|
|
7030
|
+
exclude: {
|
|
7031
|
+
type: "string",
|
|
7032
|
+
description: "Comma-separated path globs to exclude"
|
|
7050
7033
|
},
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
try {
|
|
7056
|
-
output(await new ForgewaySeoGatewayClient().request(`/api/firecrawl/crawl/${encodeURIComponent(crawlId)}`, void 0, { method: "GET" }), args);
|
|
7057
|
-
} catch (error) {
|
|
7058
|
-
if (await handleCliPaywall({
|
|
7059
|
-
error,
|
|
7060
|
-
resource: `/api/firecrawl/crawl/${crawlId}`,
|
|
7061
|
-
context: { providerPath: `/api/firecrawl/crawl/${crawlId}` }
|
|
7062
|
-
})) process.exit(2);
|
|
7063
|
-
consola$1.error(error instanceof Error ? error.message : "Unknown error");
|
|
7064
|
-
process.exit(1);
|
|
7065
|
-
}
|
|
7066
|
-
return;
|
|
7067
|
-
}
|
|
7068
|
-
await runSeoRequest(args, "/api/firecrawl/crawl", {
|
|
7069
|
-
url: requireArg(args.url, "url"),
|
|
7070
|
-
limit: toNumberValue(args.limit, 25),
|
|
7071
|
-
maxDiscoveryDepth: toNumberValue(args["max-depth"], 3),
|
|
7072
|
-
allowExternalLinks: args["allow-external-links"] === true,
|
|
7073
|
-
allowSubdomains: args["allow-subdomains"] === true,
|
|
7074
|
-
scrapeFormats: formats(args.formats),
|
|
7075
|
-
forceRefresh: forceRefresh(args)
|
|
7076
|
-
});
|
|
7077
|
-
}
|
|
7078
|
-
}),
|
|
7079
|
-
search: defineCommand({
|
|
7080
|
-
meta: {
|
|
7081
|
-
name: "search",
|
|
7082
|
-
description: "Search through Forgeway-hosted Firecrawl"
|
|
7034
|
+
"timeout-ms": {
|
|
7035
|
+
type: "string",
|
|
7036
|
+
description: "Per-resource timeout in milliseconds",
|
|
7037
|
+
default: "10000"
|
|
7083
7038
|
},
|
|
7084
|
-
|
|
7085
|
-
|
|
7086
|
-
|
|
7087
|
-
|
|
7088
|
-
required: true
|
|
7089
|
-
},
|
|
7090
|
-
...hostedProviderArgs,
|
|
7091
|
-
limit: {
|
|
7092
|
-
type: "string",
|
|
7093
|
-
description: "Maximum results",
|
|
7094
|
-
default: "10"
|
|
7095
|
-
},
|
|
7096
|
-
scrape: {
|
|
7097
|
-
type: "boolean",
|
|
7098
|
-
description: "Scrape result markdown"
|
|
7099
|
-
}
|
|
7039
|
+
"max-bytes": {
|
|
7040
|
+
type: "string",
|
|
7041
|
+
description: "Maximum bytes per fetched resource",
|
|
7042
|
+
default: "1000000"
|
|
7100
7043
|
},
|
|
7101
|
-
|
|
7102
|
-
|
|
7103
|
-
|
|
7104
|
-
|
|
7105
|
-
|
|
7106
|
-
|
|
7107
|
-
|
|
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"
|
|
7108
7056
|
}
|
|
7109
|
-
})
|
|
7110
|
-
}
|
|
7111
|
-
});
|
|
7112
|
-
function dataForSeoBase(args) {
|
|
7113
|
-
return {
|
|
7114
|
-
locationCode: toNumberValue(args["location-code"], 2840),
|
|
7115
|
-
languageCode: toStringValue(args["language-code"]) ?? "en",
|
|
7116
|
-
device: toStringValue(args.device) ?? "desktop",
|
|
7117
|
-
forceRefresh: forceRefresh(args)
|
|
7118
|
-
};
|
|
7119
|
-
}
|
|
7120
|
-
const dataForSeoSerpCommand = defineCommand({
|
|
7121
|
-
meta: {
|
|
7122
|
-
name: "serp",
|
|
7123
|
-
description: "Fetch Google organic SERP data"
|
|
7124
|
-
},
|
|
7125
|
-
args: {
|
|
7126
|
-
keyword: {
|
|
7127
|
-
type: "positional",
|
|
7128
|
-
description: "Keyword",
|
|
7129
|
-
required: true
|
|
7130
|
-
},
|
|
7131
|
-
...dataForSeoCommonArgs,
|
|
7132
|
-
depth: {
|
|
7133
|
-
type: "string",
|
|
7134
|
-
description: "SERP depth",
|
|
7135
|
-
default: "10"
|
|
7136
|
-
}
|
|
7137
|
-
},
|
|
7138
|
-
async run({ args }) {
|
|
7139
|
-
await runSeoRequest(args, "/api/dataforseo/serp", {
|
|
7140
|
-
keyword: requireArg(args.keyword, "keyword"),
|
|
7141
|
-
depth: toNumberValue(args.depth, 10),
|
|
7142
|
-
...dataForSeoBase(args)
|
|
7143
|
-
});
|
|
7144
|
-
}
|
|
7145
|
-
});
|
|
7146
|
-
const dataForSeoMapsCommand = defineCommand({
|
|
7147
|
-
meta: {
|
|
7148
|
-
name: "maps",
|
|
7149
|
-
description: "Fetch Google Maps SERP data"
|
|
7150
|
-
},
|
|
7151
|
-
args: {
|
|
7152
|
-
keyword: {
|
|
7153
|
-
type: "positional",
|
|
7154
|
-
description: "Keyword",
|
|
7155
|
-
required: true
|
|
7156
7057
|
},
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
|
|
7160
|
-
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
args: {
|
|
7178
|
-
keyword: {
|
|
7179
|
-
type: "positional",
|
|
7180
|
-
description: "Seed keyword",
|
|
7181
|
-
required: true
|
|
7182
|
-
},
|
|
7183
|
-
...dataForSeoCommonArgs,
|
|
7184
|
-
limit: {
|
|
7185
|
-
type: "string",
|
|
7186
|
-
description: "Maximum suggestions",
|
|
7187
|
-
default: "20"
|
|
7188
|
-
}
|
|
7189
|
-
},
|
|
7190
|
-
async run({ args }) {
|
|
7191
|
-
await runSeoRequest(args, "/api/dataforseo/keywords", {
|
|
7192
|
-
keyword: requireArg(args.keyword, "keyword"),
|
|
7193
|
-
limit: toNumberValue(args.limit, 20),
|
|
7194
|
-
includeSerpInfo: true,
|
|
7195
|
-
includeSeedKeyword: true,
|
|
7196
|
-
...dataForSeoBase(args)
|
|
7197
|
-
});
|
|
7198
|
-
}
|
|
7199
|
-
});
|
|
7200
|
-
const dataForSeoCompetitorsCommand = defineCommand({
|
|
7201
|
-
meta: {
|
|
7202
|
-
name: "competitors",
|
|
7203
|
-
description: "Fetch competitor-domain data"
|
|
7204
|
-
},
|
|
7205
|
-
args: {
|
|
7206
|
-
domain: {
|
|
7207
|
-
type: "positional",
|
|
7208
|
-
description: "Target domain",
|
|
7209
|
-
required: true
|
|
7210
|
-
},
|
|
7211
|
-
...dataForSeoCommonArgs,
|
|
7212
|
-
limit: {
|
|
7213
|
-
type: "string",
|
|
7214
|
-
description: "Maximum competitors",
|
|
7215
|
-
default: "20"
|
|
7216
|
-
}
|
|
7217
|
-
},
|
|
7218
|
-
async run({ args }) {
|
|
7219
|
-
await runSeoRequest(args, "/api/dataforseo/competitors", {
|
|
7220
|
-
targetDomain: requireArg(args.domain, "domain"),
|
|
7221
|
-
limit: toNumberValue(args.limit, 20),
|
|
7222
|
-
...dataForSeoBase(args)
|
|
7223
|
-
});
|
|
7224
|
-
}
|
|
7225
|
-
});
|
|
7226
|
-
const dataForSeoAiScrapeCommand = defineCommand({
|
|
7227
|
-
meta: {
|
|
7228
|
-
name: "ai-scrape",
|
|
7229
|
-
description: "Fetch DataForSEO AI scraper response data"
|
|
7230
|
-
},
|
|
7231
|
-
args: {
|
|
7232
|
-
keyword: {
|
|
7233
|
-
type: "positional",
|
|
7234
|
-
description: "AI search keyword",
|
|
7235
|
-
required: true
|
|
7236
|
-
},
|
|
7237
|
-
...dataForSeoCommonArgs,
|
|
7238
|
-
platform: {
|
|
7239
|
-
type: "string",
|
|
7240
|
-
description: "AI platform: chat_gpt, gemini, or perplexity",
|
|
7241
|
-
default: "chat_gpt"
|
|
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
|
+
}
|
|
7242
7078
|
}
|
|
7243
|
-
}
|
|
7244
|
-
async run({ args }) {
|
|
7245
|
-
await runSeoRequest(args, "/api/dataforseo/ai-scrape", {
|
|
7246
|
-
keyword: requireArg(args.keyword, "keyword"),
|
|
7247
|
-
platform: toStringValue(args.platform) ?? "chat_gpt",
|
|
7248
|
-
...dataForSeoBase(args)
|
|
7249
|
-
});
|
|
7250
|
-
}
|
|
7079
|
+
}) }
|
|
7251
7080
|
});
|
|
7252
|
-
|
|
7253
|
-
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
args: {
|
|
7258
|
-
...dataForSeoCommonArgs,
|
|
7259
|
-
domain: {
|
|
7260
|
-
type: "string",
|
|
7261
|
-
description: "Target domain"
|
|
7262
|
-
},
|
|
7263
|
-
keyword: {
|
|
7264
|
-
type: "string",
|
|
7265
|
-
description: "Target keyword"
|
|
7266
|
-
},
|
|
7267
|
-
platform: {
|
|
7268
|
-
type: "string",
|
|
7269
|
-
description: "Mention platform: google or chat_gpt",
|
|
7270
|
-
default: "google"
|
|
7081
|
+
function createSeoCommand() {
|
|
7082
|
+
return defineCommand({
|
|
7083
|
+
meta: {
|
|
7084
|
+
name: "seo",
|
|
7085
|
+
description: "Deterministic local SEO evidence"
|
|
7271
7086
|
},
|
|
7272
|
-
|
|
7273
|
-
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
}
|
|
7277
|
-
},
|
|
7278
|
-
async run({ args }) {
|
|
7279
|
-
if (!toStringValue(args.domain) && !toStringValue(args.keyword)) {
|
|
7280
|
-
consola$1.error("domain or keyword is required.");
|
|
7281
|
-
process.exit(1);
|
|
7282
|
-
}
|
|
7283
|
-
await runSeoRequest(args, "/api/dataforseo/ai-mentions", {
|
|
7284
|
-
targetDomain: toStringValue(args.domain),
|
|
7285
|
-
keyword: toStringValue(args.keyword),
|
|
7286
|
-
platform: toStringValue(args.platform) ?? "google",
|
|
7287
|
-
limit: toNumberValue(args.limit, 20),
|
|
7288
|
-
...dataForSeoBase(args)
|
|
7289
|
-
});
|
|
7290
|
-
}
|
|
7291
|
-
});
|
|
7292
|
-
const dataForSeoCommand = defineCommand({
|
|
7293
|
-
meta: {
|
|
7294
|
-
name: "dataforseo",
|
|
7295
|
-
description: "Forgeway-hosted DataForSEO workflows"
|
|
7296
|
-
},
|
|
7297
|
-
subCommands: {
|
|
7298
|
-
serp: dataForSeoSerpCommand,
|
|
7299
|
-
maps: dataForSeoMapsCommand,
|
|
7300
|
-
keywords: dataForSeoKeywordsCommand,
|
|
7301
|
-
competitors: dataForSeoCompetitorsCommand,
|
|
7302
|
-
"ai-scrape": dataForSeoAiScrapeCommand,
|
|
7303
|
-
"ai-mentions": dataForSeoAiMentionsCommand
|
|
7304
|
-
}
|
|
7305
|
-
});
|
|
7306
|
-
const seoCommand = defineCommand({
|
|
7307
|
-
meta: {
|
|
7308
|
-
name: "seo",
|
|
7309
|
-
description: "Hosted SEO provider workflows through Forgeway"
|
|
7310
|
-
},
|
|
7311
|
-
subCommands: {
|
|
7312
|
-
firecrawl: firecrawlCommand,
|
|
7313
|
-
dataforseo: dataForSeoCommand
|
|
7314
|
-
}
|
|
7315
|
-
});
|
|
7087
|
+
subCommands: { crawl: crawlCommand }
|
|
7088
|
+
});
|
|
7089
|
+
}
|
|
7090
|
+
const seoCommand = createSeoCommand();
|
|
7316
7091
|
|
|
7317
7092
|
//#endregion
|
|
7318
7093
|
//#region src/cli/commands/taxonomy.ts
|
|
@@ -7431,6 +7206,449 @@ const taxonomyCommand = defineCommand({
|
|
|
7431
7206
|
}
|
|
7432
7207
|
});
|
|
7433
7208
|
|
|
7209
|
+
//#endregion
|
|
7210
|
+
//#region src/cli/commands/tools.ts
|
|
7211
|
+
const ENV_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
7212
|
+
const CREDENTIAL_HEADER_PATTERN = /^(authorization|proxy-authorization|x-api-key|api-key|apikey|token|access-token|private-key)$/i;
|
|
7213
|
+
const accountArgs = {
|
|
7214
|
+
email: {
|
|
7215
|
+
type: "string",
|
|
7216
|
+
alias: "e",
|
|
7217
|
+
description: "Forgeway account email"
|
|
7218
|
+
},
|
|
7219
|
+
"api-url": {
|
|
7220
|
+
type: "string",
|
|
7221
|
+
description: "Forgeway API URL",
|
|
7222
|
+
default: DEFAULT_FORGEWAY_API_BASE_URL
|
|
7223
|
+
},
|
|
7224
|
+
json: {
|
|
7225
|
+
type: "boolean",
|
|
7226
|
+
description: "Output the complete JSON response"
|
|
7227
|
+
}
|
|
7228
|
+
};
|
|
7229
|
+
const endpointArgs = {
|
|
7230
|
+
provider: {
|
|
7231
|
+
type: "string",
|
|
7232
|
+
alias: "p",
|
|
7233
|
+
description: "Provider slug",
|
|
7234
|
+
required: true
|
|
7235
|
+
},
|
|
7236
|
+
endpoint: {
|
|
7237
|
+
type: "string",
|
|
7238
|
+
alias: "e",
|
|
7239
|
+
description: "Provider endpoint path",
|
|
7240
|
+
required: true
|
|
7241
|
+
}
|
|
7242
|
+
};
|
|
7243
|
+
const credentialReferenceArgs = {
|
|
7244
|
+
"credential-token-env": {
|
|
7245
|
+
type: "string",
|
|
7246
|
+
description: "Environment variable containing a request-scoped provider token"
|
|
7247
|
+
},
|
|
7248
|
+
"credential-basic-username-env": {
|
|
7249
|
+
type: "string",
|
|
7250
|
+
description: "Environment variable containing a request-scoped basic-auth username"
|
|
7251
|
+
},
|
|
7252
|
+
"credential-basic-password-env": {
|
|
7253
|
+
type: "string",
|
|
7254
|
+
description: "Environment variable containing a request-scoped basic-auth password"
|
|
7255
|
+
}
|
|
7256
|
+
};
|
|
7257
|
+
function createToolsCommand(options = {}) {
|
|
7258
|
+
const createService = options.createService ?? createCliToolsService;
|
|
7259
|
+
return defineCommand({
|
|
7260
|
+
meta: {
|
|
7261
|
+
name: "tools",
|
|
7262
|
+
description: "Discover, inspect, and run Forgeway Integration endpoints"
|
|
7263
|
+
},
|
|
7264
|
+
subCommands: {
|
|
7265
|
+
discover: defineCommand({
|
|
7266
|
+
meta: {
|
|
7267
|
+
name: "discover",
|
|
7268
|
+
description: "Find Forgeway Integration endpoints from a natural-language need"
|
|
7269
|
+
},
|
|
7270
|
+
args: {
|
|
7271
|
+
...accountArgs,
|
|
7272
|
+
query: {
|
|
7273
|
+
type: "string",
|
|
7274
|
+
alias: "q",
|
|
7275
|
+
description: "Natural-language endpoint need",
|
|
7276
|
+
required: true
|
|
7277
|
+
},
|
|
7278
|
+
limit: {
|
|
7279
|
+
type: "string",
|
|
7280
|
+
alias: "l",
|
|
7281
|
+
description: "Maximum results (1-100)"
|
|
7282
|
+
},
|
|
7283
|
+
"min-score": {
|
|
7284
|
+
type: "string",
|
|
7285
|
+
description: "Minimum relevance score (0-1)"
|
|
7286
|
+
}
|
|
7287
|
+
},
|
|
7288
|
+
async run({ args }) {
|
|
7289
|
+
await executeToolsCommand(args, async (resolvedArgs) => {
|
|
7290
|
+
const result = await (await createService(resolvedArgs)).discover({
|
|
7291
|
+
query: requiredString(resolvedArgs.query, "query"),
|
|
7292
|
+
limit: optionalNumber(resolvedArgs.limit, "limit", {
|
|
7293
|
+
integer: true,
|
|
7294
|
+
min: 1,
|
|
7295
|
+
max: 100
|
|
7296
|
+
}) ?? 10,
|
|
7297
|
+
minScore: optionalNumber(resolvedArgs["min-score"], "min-score", {
|
|
7298
|
+
min: 0,
|
|
7299
|
+
max: 1
|
|
7300
|
+
})
|
|
7301
|
+
});
|
|
7302
|
+
emitResult(result, discoverSummary(result), resolvedArgs);
|
|
7303
|
+
});
|
|
7304
|
+
}
|
|
7305
|
+
}),
|
|
7306
|
+
inspect: defineCommand({
|
|
7307
|
+
meta: {
|
|
7308
|
+
name: "inspect",
|
|
7309
|
+
description: "Inspect an endpoint schema, output, credits, cache, and risk contract"
|
|
7310
|
+
},
|
|
7311
|
+
args: {
|
|
7312
|
+
...accountArgs,
|
|
7313
|
+
...endpointArgs
|
|
7314
|
+
},
|
|
7315
|
+
async run({ args }) {
|
|
7316
|
+
await executeToolsCommand(args, async (resolvedArgs) => {
|
|
7317
|
+
const result = await (await createService(resolvedArgs)).inspect(endpointRequest(resolvedArgs));
|
|
7318
|
+
emitResult(result, inspectSummary(result), resolvedArgs);
|
|
7319
|
+
});
|
|
7320
|
+
}
|
|
7321
|
+
}),
|
|
7322
|
+
run: defineCommand({
|
|
7323
|
+
meta: {
|
|
7324
|
+
name: "run",
|
|
7325
|
+
description: "Run one inspected Forgeway Integration endpoint"
|
|
7326
|
+
},
|
|
7327
|
+
args: {
|
|
7328
|
+
...accountArgs,
|
|
7329
|
+
...endpointArgs,
|
|
7330
|
+
...credentialReferenceArgs,
|
|
7331
|
+
input: {
|
|
7332
|
+
type: "string",
|
|
7333
|
+
alias: "i",
|
|
7334
|
+
description: "Body JSON"
|
|
7335
|
+
},
|
|
7336
|
+
"input-file": {
|
|
7337
|
+
type: "string",
|
|
7338
|
+
alias: "f",
|
|
7339
|
+
description: "Read body JSON from a file"
|
|
7340
|
+
},
|
|
7341
|
+
query: {
|
|
7342
|
+
type: "string",
|
|
7343
|
+
description: "Query parameters JSON object"
|
|
7344
|
+
},
|
|
7345
|
+
path: {
|
|
7346
|
+
type: "string",
|
|
7347
|
+
description: "Path parameters JSON object"
|
|
7348
|
+
},
|
|
7349
|
+
header: {
|
|
7350
|
+
type: "string",
|
|
7351
|
+
description: "Non-credential header parameters JSON object"
|
|
7352
|
+
},
|
|
7353
|
+
wait: {
|
|
7354
|
+
type: "boolean",
|
|
7355
|
+
alias: "w",
|
|
7356
|
+
description: "Wait for a terminal run state"
|
|
7357
|
+
},
|
|
7358
|
+
"wait-timeout": {
|
|
7359
|
+
type: "string",
|
|
7360
|
+
description: "Maximum wait in seconds",
|
|
7361
|
+
default: "300"
|
|
7362
|
+
},
|
|
7363
|
+
output: {
|
|
7364
|
+
type: "string",
|
|
7365
|
+
alias: "o",
|
|
7366
|
+
description: "Write provider output as JSON"
|
|
7367
|
+
},
|
|
7368
|
+
"force-refresh": {
|
|
7369
|
+
type: "boolean",
|
|
7370
|
+
description: "Bypass eligible Forgeway cache"
|
|
7371
|
+
},
|
|
7372
|
+
"confirm-side-effect": {
|
|
7373
|
+
type: "boolean",
|
|
7374
|
+
description: "Confirm the inspected endpoint's external side effect"
|
|
7375
|
+
},
|
|
7376
|
+
"idempotency-key": {
|
|
7377
|
+
type: "string",
|
|
7378
|
+
description: "Stable idempotency key required for side-effecting endpoints"
|
|
7379
|
+
}
|
|
7380
|
+
},
|
|
7381
|
+
async run({ args }) {
|
|
7382
|
+
await executeToolsCommand(args, async (resolvedArgs) => {
|
|
7383
|
+
if (resolvedArgs["confirm-side-effect"] && !resolvedArgs["idempotency-key"]) throw new DinewayToolsError("--confirm-side-effect requires --idempotency-key.", "IDEMPOTENCY_KEY_REQUIRED");
|
|
7384
|
+
const service = await createService(resolvedArgs);
|
|
7385
|
+
const result = await service.run({
|
|
7386
|
+
...endpointRequest(resolvedArgs),
|
|
7387
|
+
input: await parseEndpointInput(resolvedArgs),
|
|
7388
|
+
credential: resolveInlineCredential(resolvedArgs),
|
|
7389
|
+
forceRefresh: resolvedArgs["force-refresh"] === true,
|
|
7390
|
+
confirmSideEffect: resolvedArgs["confirm-side-effect"] === true
|
|
7391
|
+
}, { idempotencyKey: optionalString(resolvedArgs["idempotency-key"]) });
|
|
7392
|
+
const finalResult = resolvedArgs.wait ? await service.waitForRun(result.runId, { timeoutMs: requiredNumber(resolvedArgs["wait-timeout"], "wait-timeout", { min: 1 }) * 1e3 }) : result;
|
|
7393
|
+
if (resolvedArgs.output) await writeFile(resolvedArgs.output, JSON.stringify(finalResult.output ?? finalResult.artifact ?? finalResult, null, 2) + "\n", "utf-8");
|
|
7394
|
+
emitResult(finalResult, runSummary(finalResult), resolvedArgs);
|
|
7395
|
+
});
|
|
7396
|
+
}
|
|
7397
|
+
}),
|
|
7398
|
+
runs: defineCommand({
|
|
7399
|
+
meta: {
|
|
7400
|
+
name: "runs",
|
|
7401
|
+
description: "List, get, poll, and stop Integration runs"
|
|
7402
|
+
},
|
|
7403
|
+
subCommands: {
|
|
7404
|
+
list: defineCommand({
|
|
7405
|
+
meta: {
|
|
7406
|
+
name: "list",
|
|
7407
|
+
description: "List recent Forgeway Integration runs"
|
|
7408
|
+
},
|
|
7409
|
+
args: {
|
|
7410
|
+
...accountArgs,
|
|
7411
|
+
limit: {
|
|
7412
|
+
type: "string",
|
|
7413
|
+
alias: "l",
|
|
7414
|
+
description: "Maximum runs (1-100)"
|
|
7415
|
+
},
|
|
7416
|
+
cursor: {
|
|
7417
|
+
type: "string",
|
|
7418
|
+
description: "Pagination cursor"
|
|
7419
|
+
}
|
|
7420
|
+
},
|
|
7421
|
+
async run({ args }) {
|
|
7422
|
+
await executeToolsCommand(args, async (resolvedArgs) => {
|
|
7423
|
+
const result = await (await createService(resolvedArgs)).listRuns({
|
|
7424
|
+
limit: optionalNumber(resolvedArgs.limit, "limit", {
|
|
7425
|
+
integer: true,
|
|
7426
|
+
min: 1,
|
|
7427
|
+
max: 100
|
|
7428
|
+
}),
|
|
7429
|
+
cursor: optionalString(resolvedArgs.cursor)
|
|
7430
|
+
});
|
|
7431
|
+
emitResult(result, runsSummary(result.items, result.nextCursor), resolvedArgs);
|
|
7432
|
+
});
|
|
7433
|
+
}
|
|
7434
|
+
}),
|
|
7435
|
+
get: defineCommand({
|
|
7436
|
+
meta: {
|
|
7437
|
+
name: "get",
|
|
7438
|
+
description: "Get or poll one Forgeway Integration run"
|
|
7439
|
+
},
|
|
7440
|
+
args: {
|
|
7441
|
+
...accountArgs,
|
|
7442
|
+
"run-id": {
|
|
7443
|
+
type: "string",
|
|
7444
|
+
alias: "r",
|
|
7445
|
+
description: "Run ID",
|
|
7446
|
+
required: true
|
|
7447
|
+
},
|
|
7448
|
+
wait: {
|
|
7449
|
+
type: "boolean",
|
|
7450
|
+
alias: "w",
|
|
7451
|
+
description: "Wait for a terminal run state"
|
|
7452
|
+
},
|
|
7453
|
+
"wait-timeout": {
|
|
7454
|
+
type: "string",
|
|
7455
|
+
description: "Maximum wait in seconds",
|
|
7456
|
+
default: "300"
|
|
7457
|
+
}
|
|
7458
|
+
},
|
|
7459
|
+
async run({ args }) {
|
|
7460
|
+
await executeToolsCommand(args, async (resolvedArgs) => {
|
|
7461
|
+
const service = await createService(resolvedArgs);
|
|
7462
|
+
const runId = requiredString(resolvedArgs["run-id"], "run-id");
|
|
7463
|
+
const result = resolvedArgs.wait ? await service.waitForRun(runId, { timeoutMs: requiredNumber(resolvedArgs["wait-timeout"], "wait-timeout", { min: 1 }) * 1e3 }) : await service.getRun(runId);
|
|
7464
|
+
emitResult(result, runSummary(result), resolvedArgs);
|
|
7465
|
+
});
|
|
7466
|
+
}
|
|
7467
|
+
}),
|
|
7468
|
+
stop: defineCommand({
|
|
7469
|
+
meta: {
|
|
7470
|
+
name: "stop",
|
|
7471
|
+
description: "Request cancellation for a stoppable Integration run"
|
|
7472
|
+
},
|
|
7473
|
+
args: {
|
|
7474
|
+
...accountArgs,
|
|
7475
|
+
...credentialReferenceArgs,
|
|
7476
|
+
"run-id": {
|
|
7477
|
+
type: "string",
|
|
7478
|
+
alias: "r",
|
|
7479
|
+
description: "Run ID",
|
|
7480
|
+
required: true
|
|
7481
|
+
}
|
|
7482
|
+
},
|
|
7483
|
+
async run({ args }) {
|
|
7484
|
+
await executeToolsCommand(args, async (resolvedArgs) => {
|
|
7485
|
+
const result = await (await createService(resolvedArgs)).stopRun(requiredString(resolvedArgs["run-id"], "run-id"), { credential: resolveInlineCredential(resolvedArgs) });
|
|
7486
|
+
emitResult(result, result, resolvedArgs);
|
|
7487
|
+
});
|
|
7488
|
+
}
|
|
7489
|
+
})
|
|
7490
|
+
}
|
|
7491
|
+
})
|
|
7492
|
+
}
|
|
7493
|
+
});
|
|
7494
|
+
}
|
|
7495
|
+
const toolsCommand = createToolsCommand();
|
|
7496
|
+
async function createCliToolsService(args) {
|
|
7497
|
+
const credentials = await readStoredForgewayAccount({
|
|
7498
|
+
platformApiUrl: normalizeForgewayApiUrl(optionalString(args["api-url"]) ?? process.env["DINEWAY_API_BASE_URL"] ?? process.env["FORGEWAY_API_URL"]),
|
|
7499
|
+
email: normalizeForgewayEmail(optionalString(args.email))
|
|
7500
|
+
});
|
|
7501
|
+
if (!credentials) throw new DinewayToolsError("Dineway Tools requires a verified Forgeway account. Run `dineway account login`.", "FORGEWAY_ACCOUNT_REQUIRED");
|
|
7502
|
+
const refreshed = await refreshForgewayAccountCredentials(credentials);
|
|
7503
|
+
return new DinewayToolsService(createForgewayAccountClient(refreshed.platformApiUrl, refreshed.accessToken).integration);
|
|
7504
|
+
}
|
|
7505
|
+
async function executeToolsCommand(args, operation) {
|
|
7506
|
+
configureOutputMode(args);
|
|
7507
|
+
try {
|
|
7508
|
+
await operation(args);
|
|
7509
|
+
} catch (error) {
|
|
7510
|
+
const message = error instanceof Error ? error.message : "Dineway Tools request failed.";
|
|
7511
|
+
consola$1.error(message);
|
|
7512
|
+
process.exitCode = 2;
|
|
7513
|
+
}
|
|
7514
|
+
}
|
|
7515
|
+
function endpointRequest(args) {
|
|
7516
|
+
const endpoint = requiredString(args.endpoint, "endpoint");
|
|
7517
|
+
if (!endpoint.startsWith("/")) throw new DinewayToolsError("Endpoint must start with '/'.", "INVALID_ENDPOINT");
|
|
7518
|
+
return {
|
|
7519
|
+
provider: requiredString(args.provider, "provider"),
|
|
7520
|
+
endpoint
|
|
7521
|
+
};
|
|
7522
|
+
}
|
|
7523
|
+
async function parseEndpointInput(args) {
|
|
7524
|
+
if (args.input && args["input-file"]) throw new DinewayToolsError("Choose either --input or --input-file, not both.", "DUPLICATE_BODY_INPUT");
|
|
7525
|
+
const body = args["input-file"] ? parseJson(await readFile(args["input-file"], "utf-8"), "input-file") : args.input ? parseJson(args.input, "input") : void 0;
|
|
7526
|
+
const queryParams = parseJsonObject(args.query, "query");
|
|
7527
|
+
const pathParams = parseJsonObject(args.path, "path");
|
|
7528
|
+
const headerParams = parseHeaderParams(args.header);
|
|
7529
|
+
return {
|
|
7530
|
+
...body === void 0 ? {} : { body },
|
|
7531
|
+
...queryParams ? { queryParams } : {},
|
|
7532
|
+
...pathParams ? { pathParams } : {},
|
|
7533
|
+
...headerParams ? { headerParams } : {}
|
|
7534
|
+
};
|
|
7535
|
+
}
|
|
7536
|
+
function resolveInlineCredential(args) {
|
|
7537
|
+
const tokenEnv = optionalString(args["credential-token-env"]);
|
|
7538
|
+
const usernameEnv = optionalString(args["credential-basic-username-env"]);
|
|
7539
|
+
const passwordEnv = optionalString(args["credential-basic-password-env"]);
|
|
7540
|
+
if (tokenEnv && (usernameEnv || passwordEnv)) throw new DinewayToolsError("Choose either token or basic credential environment references, not both.", "CREDENTIAL_MODE_CONFLICT");
|
|
7541
|
+
if (usernameEnv && !passwordEnv || !usernameEnv && passwordEnv) throw new DinewayToolsError("Basic credential username and password environment references must be provided together.", "INCOMPLETE_BASIC_CREDENTIAL");
|
|
7542
|
+
if (tokenEnv) return {
|
|
7543
|
+
type: "token",
|
|
7544
|
+
value: readCredentialEnvironmentValue(tokenEnv)
|
|
7545
|
+
};
|
|
7546
|
+
if (usernameEnv && passwordEnv) return {
|
|
7547
|
+
type: "basic",
|
|
7548
|
+
username: readCredentialEnvironmentValue(usernameEnv),
|
|
7549
|
+
password: readCredentialEnvironmentValue(passwordEnv)
|
|
7550
|
+
};
|
|
7551
|
+
}
|
|
7552
|
+
function readCredentialEnvironmentValue(name) {
|
|
7553
|
+
if (!ENV_NAME_PATTERN.test(name)) throw new DinewayToolsError(`Invalid credential environment variable name: ${name}`, "INVALID_CREDENTIAL_ENV_NAME");
|
|
7554
|
+
const value = process.env[name];
|
|
7555
|
+
if (!value) throw new DinewayToolsError(`Credential environment variable ${name} is not set.`, "CREDENTIAL_ENV_MISSING");
|
|
7556
|
+
return value;
|
|
7557
|
+
}
|
|
7558
|
+
function parseHeaderParams(raw) {
|
|
7559
|
+
const value = parseJsonObject(raw, "header");
|
|
7560
|
+
if (!value) return void 0;
|
|
7561
|
+
for (const name of Object.keys(value)) if (CREDENTIAL_HEADER_PATTERN.test(name)) throw new DinewayToolsError("Credential headers are not allowed. Use environment credential references.", "CREDENTIAL_HEADER_FORBIDDEN");
|
|
7562
|
+
return value;
|
|
7563
|
+
}
|
|
7564
|
+
function parseJsonObject(raw, label) {
|
|
7565
|
+
if (raw === void 0) return void 0;
|
|
7566
|
+
const value = parseJson(requiredString(raw, label), label);
|
|
7567
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new DinewayToolsError(`--${label} must be a JSON object.`, "INVALID_JSON_OBJECT");
|
|
7568
|
+
return value;
|
|
7569
|
+
}
|
|
7570
|
+
function parseJson(raw, label) {
|
|
7571
|
+
try {
|
|
7572
|
+
const value = JSON.parse(raw);
|
|
7573
|
+
if (!isIntegrationJsonValue(value)) throw new Error("Unsupported JSON value");
|
|
7574
|
+
return value;
|
|
7575
|
+
} catch {
|
|
7576
|
+
throw new DinewayToolsError(`--${label} contains invalid JSON.`, "INVALID_JSON");
|
|
7577
|
+
}
|
|
7578
|
+
}
|
|
7579
|
+
function isIntegrationJsonValue(value) {
|
|
7580
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
7581
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
7582
|
+
if (Array.isArray(value)) return value.every(isIntegrationJsonValue);
|
|
7583
|
+
if (typeof value !== "object") return false;
|
|
7584
|
+
return Object.values(value).every(isIntegrationJsonValue);
|
|
7585
|
+
}
|
|
7586
|
+
function requiredString(value, label) {
|
|
7587
|
+
const resolved = optionalString(value);
|
|
7588
|
+
if (!resolved) throw new DinewayToolsError(`--${label} is required.`, "MISSING_ARGUMENT");
|
|
7589
|
+
return resolved;
|
|
7590
|
+
}
|
|
7591
|
+
function optionalString(value) {
|
|
7592
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
7593
|
+
}
|
|
7594
|
+
function optionalNumber(value, label, options) {
|
|
7595
|
+
return value === void 0 ? void 0 : requiredNumber(value, label, options);
|
|
7596
|
+
}
|
|
7597
|
+
function requiredNumber(value, label, options) {
|
|
7598
|
+
const parsed = Number(value);
|
|
7599
|
+
if (!Number.isFinite(parsed) || options.integer && !Number.isInteger(parsed) || options.min !== void 0 && parsed < options.min || options.max !== void 0 && parsed > options.max) throw new DinewayToolsError(`--${label} has an invalid numeric value.`, "INVALID_NUMBER");
|
|
7600
|
+
return parsed;
|
|
7601
|
+
}
|
|
7602
|
+
function emitResult(full, human, args) {
|
|
7603
|
+
output(args.json || !process.stdout.isTTY ? full : human, args);
|
|
7604
|
+
}
|
|
7605
|
+
function discoverSummary(result) {
|
|
7606
|
+
return {
|
|
7607
|
+
query: result.query,
|
|
7608
|
+
count: result.count,
|
|
7609
|
+
items: result.results.map(({ endpoint, score }) => ({
|
|
7610
|
+
provider: endpoint.provider,
|
|
7611
|
+
endpoint: endpoint.endpoint,
|
|
7612
|
+
name: endpoint.displayName,
|
|
7613
|
+
score: Number(score.toFixed(3)),
|
|
7614
|
+
estimatedCredits: endpoint.pricing.estimatedCredits,
|
|
7615
|
+
risk: endpoint.risk.level
|
|
7616
|
+
}))
|
|
7617
|
+
};
|
|
7618
|
+
}
|
|
7619
|
+
function inspectSummary(result) {
|
|
7620
|
+
return {
|
|
7621
|
+
provider: result.provider,
|
|
7622
|
+
endpoint: result.endpoint,
|
|
7623
|
+
method: result.method,
|
|
7624
|
+
name: result.displayName,
|
|
7625
|
+
pricing: result.pricing,
|
|
7626
|
+
cache: result.cache,
|
|
7627
|
+
risk: result.risk,
|
|
7628
|
+
async: result.async,
|
|
7629
|
+
input: result.input
|
|
7630
|
+
};
|
|
7631
|
+
}
|
|
7632
|
+
function runSummary(result) {
|
|
7633
|
+
return {
|
|
7634
|
+
runId: result.runId,
|
|
7635
|
+
provider: result.provider,
|
|
7636
|
+
endpoint: result.endpoint,
|
|
7637
|
+
status: result.status,
|
|
7638
|
+
stoppable: result.stoppable,
|
|
7639
|
+
cache: result.cache,
|
|
7640
|
+
billing: result.billing,
|
|
7641
|
+
artifact: result.artifact,
|
|
7642
|
+
error: result.error
|
|
7643
|
+
};
|
|
7644
|
+
}
|
|
7645
|
+
function runsSummary(items, nextCursor) {
|
|
7646
|
+
return {
|
|
7647
|
+
items: items.map(runSummary),
|
|
7648
|
+
...nextCursor ? { nextCursor } : {}
|
|
7649
|
+
};
|
|
7650
|
+
}
|
|
7651
|
+
|
|
7434
7652
|
//#endregion
|
|
7435
7653
|
//#region src/cli/commands/types.ts
|
|
7436
7654
|
/**
|
|
@@ -7482,6 +7700,88 @@ const typesCommand = defineCommand({
|
|
|
7482
7700
|
}
|
|
7483
7701
|
});
|
|
7484
7702
|
|
|
7703
|
+
//#endregion
|
|
7704
|
+
//#region src/cli/contributions.ts
|
|
7705
|
+
const DINEWAY_PLUGIN_PACKAGE = /^(?:@dineway-ai\/plugin-|dineway-plugin-)/;
|
|
7706
|
+
const CLI_COMMAND_NAME = /^[a-z][a-z0-9-]*$/;
|
|
7707
|
+
async function discoverDinewayCliContributions(cwd = process.cwd()) {
|
|
7708
|
+
const projectPackage = await findProjectPackage(cwd);
|
|
7709
|
+
if (!projectPackage) return [];
|
|
7710
|
+
const { packagePath, project } = projectPackage;
|
|
7711
|
+
const requireFromProject = createRequire(packagePath);
|
|
7712
|
+
const packageNames = new Set([
|
|
7713
|
+
...Object.keys(project.dependencies ?? {}),
|
|
7714
|
+
...Object.keys(project.devDependencies ?? {}),
|
|
7715
|
+
...Object.keys(project.optionalDependencies ?? {})
|
|
7716
|
+
]);
|
|
7717
|
+
const contributions = [];
|
|
7718
|
+
for (const packageName of [...packageNames].filter(isDinewayPluginPackage).toSorted()) {
|
|
7719
|
+
const specifier = `${packageName}/cli`;
|
|
7720
|
+
let modulePath;
|
|
7721
|
+
try {
|
|
7722
|
+
modulePath = requireFromProject.resolve(specifier);
|
|
7723
|
+
} catch (error) {
|
|
7724
|
+
if (isMissingContribution(error)) continue;
|
|
7725
|
+
throw error;
|
|
7726
|
+
}
|
|
7727
|
+
const loaded = await import(pathToFileURL(modulePath).href);
|
|
7728
|
+
const value = loaded.dinewayCliContribution ?? loaded.default;
|
|
7729
|
+
if (!isDinewayCliContribution(value)) throw new Error(`${specifier} does not export a valid Dineway CLI contribution`);
|
|
7730
|
+
contributions.push({
|
|
7731
|
+
packageName,
|
|
7732
|
+
commands: value.commands
|
|
7733
|
+
});
|
|
7734
|
+
}
|
|
7735
|
+
return contributions;
|
|
7736
|
+
}
|
|
7737
|
+
async function findProjectPackage(from) {
|
|
7738
|
+
let directory = resolve(from);
|
|
7739
|
+
while (true) {
|
|
7740
|
+
const packagePath = join(directory, "package.json");
|
|
7741
|
+
const project = await readProjectPackage(packagePath);
|
|
7742
|
+
if (project) return {
|
|
7743
|
+
packagePath,
|
|
7744
|
+
project
|
|
7745
|
+
};
|
|
7746
|
+
const parent = dirname(directory);
|
|
7747
|
+
if (parent === directory) return null;
|
|
7748
|
+
directory = parent;
|
|
7749
|
+
}
|
|
7750
|
+
}
|
|
7751
|
+
function mergeDinewayCliContributions(builtIns, contributions) {
|
|
7752
|
+
const commands = { ...builtIns };
|
|
7753
|
+
for (const contribution of contributions) for (const [name, command] of Object.entries(contribution.commands).toSorted(([left], [right]) => left.localeCompare(right))) {
|
|
7754
|
+
if (commands[name]) throw new Error(`Dineway CLI command "${name}" from ${contribution.packageName} conflicts with an existing command`);
|
|
7755
|
+
commands[name] = command;
|
|
7756
|
+
}
|
|
7757
|
+
return commands;
|
|
7758
|
+
}
|
|
7759
|
+
async function readProjectPackage(path) {
|
|
7760
|
+
try {
|
|
7761
|
+
const parsed = JSON.parse(await readFile(path, "utf8"));
|
|
7762
|
+
return isRecord(parsed) ? parsed : null;
|
|
7763
|
+
} catch (error) {
|
|
7764
|
+
if (isFileNotFound(error)) return null;
|
|
7765
|
+
throw error;
|
|
7766
|
+
}
|
|
7767
|
+
}
|
|
7768
|
+
function isDinewayPluginPackage(packageName) {
|
|
7769
|
+
return DINEWAY_PLUGIN_PACKAGE.test(packageName);
|
|
7770
|
+
}
|
|
7771
|
+
function isDinewayCliContribution(value) {
|
|
7772
|
+
if (!isRecord(value) || !isRecord(value.commands)) return false;
|
|
7773
|
+
return Object.entries(value.commands).every(([name, command]) => CLI_COMMAND_NAME.test(name) && isRecord(command));
|
|
7774
|
+
}
|
|
7775
|
+
function isRecord(value) {
|
|
7776
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7777
|
+
}
|
|
7778
|
+
function isMissingContribution(error) {
|
|
7779
|
+
return error instanceof Error && "code" in error && (error.code === "MODULE_NOT_FOUND" || error.code === "ERR_PACKAGE_PATH_NOT_EXPORTED");
|
|
7780
|
+
}
|
|
7781
|
+
function isFileNotFound(error) {
|
|
7782
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
7783
|
+
}
|
|
7784
|
+
|
|
7485
7785
|
//#endregion
|
|
7486
7786
|
//#region src/cli/index.ts
|
|
7487
7787
|
/**
|
|
@@ -7507,36 +7807,41 @@ const typesCommand = defineCommand({
|
|
|
7507
7807
|
* - deploy: Deploy a standard Dineway Node server through a provider CLI
|
|
7508
7808
|
* - plugin: Plugin management (init, bundle, validate, publish, login, logout)
|
|
7509
7809
|
*/
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
7810
|
+
const builtInCommands = {
|
|
7811
|
+
init: initCommand,
|
|
7812
|
+
account: accountCommand,
|
|
7813
|
+
types: typesCommand,
|
|
7814
|
+
dev: devCommand,
|
|
7815
|
+
deploy: deployCommand,
|
|
7816
|
+
doctor: doctorCommand,
|
|
7817
|
+
seed: seedCommand,
|
|
7818
|
+
"export-seed": exportSeedCommand,
|
|
7819
|
+
secrets: secretsCommand,
|
|
7820
|
+
auth: authCommand,
|
|
7821
|
+
login: loginCommand,
|
|
7822
|
+
logout: logoutCommand,
|
|
7823
|
+
whoami: whoamiCommand,
|
|
7824
|
+
content: contentCommand,
|
|
7825
|
+
schema: schemaCommand,
|
|
7826
|
+
media: mediaCommand,
|
|
7827
|
+
search: searchCommand,
|
|
7828
|
+
tools: toolsCommand,
|
|
7829
|
+
seo: seoCommand,
|
|
7830
|
+
taxonomy: taxonomyCommand,
|
|
7831
|
+
menu: menuCommand,
|
|
7832
|
+
plugin: pluginCommand
|
|
7833
|
+
};
|
|
7834
|
+
async function createDinewayCliCommand(cwd = process.cwd()) {
|
|
7835
|
+
return defineCommand({
|
|
7836
|
+
meta: {
|
|
7837
|
+
name: "dineway",
|
|
7838
|
+
version: "0.0.0",
|
|
7839
|
+
description: "CLI for Dineway Agentic Web builder"
|
|
7840
|
+
},
|
|
7841
|
+
subCommands: mergeDinewayCliContributions(builtInCommands, await discoverDinewayCliContributions(cwd))
|
|
7842
|
+
});
|
|
7843
|
+
}
|
|
7844
|
+
createDinewayCliCommand().then((main) => runMain(main));
|
|
7540
7845
|
|
|
7541
7846
|
//#endregion
|
|
7542
|
-
export {
|
|
7847
|
+
export { createDinewayCliCommand };
|