twentythree-cli 1.0.0
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 +25 -0
- package/bin/dev.cmd +2 -0
- package/bin/dev.js +28 -0
- package/bin/run.cmd +2 -0
- package/bin/run.js +23 -0
- package/dist/_virtual/_rolldown/runtime.cjs +23 -0
- package/dist/api/client.cjs +24 -0
- package/dist/api/types.cjs +0 -0
- package/dist/auth/credential-store.cjs +22 -0
- package/dist/auth/token-refresh.cjs +70 -0
- package/dist/auth/workspace-config.cjs +61 -0
- package/dist/commands/action/add.cjs +75 -0
- package/dist/commands/action/delete.cjs +64 -0
- package/dist/commands/action/exclude.cjs +70 -0
- package/dist/commands/action/get.cjs +112 -0
- package/dist/commands/action/include.cjs +70 -0
- package/dist/commands/action/list.cjs +119 -0
- package/dist/commands/action/types.cjs +63 -0
- package/dist/commands/action/update.cjs +86 -0
- package/dist/commands/action/upload.cjs +89 -0
- package/dist/commands/analytics/conversions/index.cjs +83 -0
- package/dist/commands/analytics/conversions/timeseries.cjs +83 -0
- package/dist/commands/analytics/conversions/totals.cjs +83 -0
- package/dist/commands/analytics/live/event-timeseries.cjs +86 -0
- package/dist/commands/analytics/live/event-totals.cjs +86 -0
- package/dist/commands/analytics/live/event.cjs +88 -0
- package/dist/commands/analytics/live/index.cjs +89 -0
- package/dist/commands/analytics/live/timeseries.cjs +85 -0
- package/dist/commands/analytics/live/totals.cjs +85 -0
- package/dist/commands/analytics/live/weekday/timeseries.cjs +85 -0
- package/dist/commands/analytics/live/weekday/totals.cjs +85 -0
- package/dist/commands/analytics/live/weekday.cjs +88 -0
- package/dist/commands/analytics/usage/devices/timeseries.cjs +83 -0
- package/dist/commands/analytics/usage/devices/totals.cjs +86 -0
- package/dist/commands/analytics/usage/devices.cjs +89 -0
- package/dist/commands/analytics/usage/domains/totals.cjs +83 -0
- package/dist/commands/analytics/usage/domains.cjs +89 -0
- package/dist/commands/analytics/usage/locations/totals.cjs +83 -0
- package/dist/commands/analytics/usage/locations.cjs +89 -0
- package/dist/commands/analytics/usage/sourceids/totals.cjs +83 -0
- package/dist/commands/analytics/usage/sourceids.cjs +89 -0
- package/dist/commands/analytics/usage/sources/totals.cjs +83 -0
- package/dist/commands/analytics/usage/sources.cjs +89 -0
- package/dist/commands/analytics/usage/spots/timeseries.cjs +83 -0
- package/dist/commands/analytics/usage/spots/totals.cjs +83 -0
- package/dist/commands/analytics/usage/spots.cjs +89 -0
- package/dist/commands/analytics/usage/storage.cjs +52 -0
- package/dist/commands/analytics/usage/traffic/timeseries.cjs +83 -0
- package/dist/commands/analytics/usage/traffic/totals.cjs +86 -0
- package/dist/commands/analytics/usage/traffic.cjs +89 -0
- package/dist/commands/analytics/video/index.cjs +89 -0
- package/dist/commands/analytics/video/performance/timeseries.cjs +83 -0
- package/dist/commands/analytics/video/performance/totals.cjs +80 -0
- package/dist/commands/analytics/video/performance.cjs +89 -0
- package/dist/commands/analytics/video/published/timeseries.cjs +83 -0
- package/dist/commands/analytics/video/published/totals.cjs +80 -0
- package/dist/commands/analytics/video/published.cjs +89 -0
- package/dist/commands/analytics/video/timeseries.cjs +86 -0
- package/dist/commands/analytics/video/totals.cjs +86 -0
- package/dist/commands/analytics/video/weekday/timeseries.cjs +86 -0
- package/dist/commands/analytics/video/weekday/totals.cjs +83 -0
- package/dist/commands/analytics/video/weekday.cjs +89 -0
- package/dist/commands/app/add.cjs +76 -0
- package/dist/commands/app/delete.cjs +57 -0
- package/dist/commands/app/update.cjs +77 -0
- package/dist/commands/audience/companies.cjs +116 -0
- package/dist/commands/audience/field/list.cjs +83 -0
- package/dist/commands/audience/field/remove.cjs +63 -0
- package/dist/commands/audience/field/set.cjs +78 -0
- package/dist/commands/audience/field/types.cjs +57 -0
- package/dist/commands/audience/funnel.cjs +77 -0
- package/dist/commands/audience/identity-sources.cjs +64 -0
- package/dist/commands/audience/list-collectors.cjs +88 -0
- package/dist/commands/audience/list.cjs +122 -0
- package/dist/commands/audience/metrics.cjs +89 -0
- package/dist/commands/audience/register.cjs +106 -0
- package/dist/commands/audience/remove.cjs +72 -0
- package/dist/commands/audience/search.cjs +106 -0
- package/dist/commands/audience/timelines.cjs +115 -0
- package/dist/commands/audience/unregister.cjs +65 -0
- package/dist/commands/auth/credentials.cjs +89 -0
- package/dist/commands/auth/status.cjs +34 -0
- package/dist/commands/category/create.cjs +80 -0
- package/dist/commands/category/delete.cjs +70 -0
- package/dist/commands/category/index.cjs +11 -0
- package/dist/commands/category/list.cjs +98 -0
- package/dist/commands/category/update.cjs +134 -0
- package/dist/commands/collector/exclude.cjs +56 -0
- package/dist/commands/collector/include.cjs +56 -0
- package/dist/commands/collector/list.cjs +81 -0
- package/dist/commands/comment/add.cjs +102 -0
- package/dist/commands/comment/clone.cjs +59 -0
- package/dist/commands/comment/delete.cjs +55 -0
- package/dist/commands/comment/list.cjs +126 -0
- package/dist/commands/comment/promote.cjs +65 -0
- package/dist/commands/comment/reaction/add.cjs +81 -0
- package/dist/commands/comment/reaction/list.cjs +92 -0
- package/dist/commands/comment/reaction/remove.cjs +81 -0
- package/dist/commands/comment/set-order.cjs +63 -0
- package/dist/commands/comment/update.cjs +63 -0
- package/dist/commands/doctor.cjs +129 -0
- package/dist/commands/openupload/list.cjs +95 -0
- package/dist/commands/openupload/update-file.cjs +85 -0
- package/dist/commands/openupload/upload-file.cjs +130 -0
- package/dist/commands/player/delete.cjs +66 -0
- package/dist/commands/player/embed-versions.cjs +83 -0
- package/dist/commands/player/embed.cjs +134 -0
- package/dist/commands/player/list.cjs +86 -0
- package/dist/commands/player/styles.cjs +70 -0
- package/dist/commands/player/update.cjs +80 -0
- package/dist/commands/poll/add.cjs +84 -0
- package/dist/commands/poll/answer.cjs +89 -0
- package/dist/commands/poll/list.cjs +92 -0
- package/dist/commands/poll/remove.cjs +57 -0
- package/dist/commands/poll/set-options.cjs +84 -0
- package/dist/commands/poll/update.cjs +79 -0
- package/dist/commands/presentation/page/link-locations.cjs +57 -0
- package/dist/commands/presentation/setting/list.cjs +44 -0
- package/dist/commands/presentation/setting/update.cjs +67 -0
- package/dist/commands/protection/protect.cjs +68 -0
- package/dist/commands/protection/unprotect.cjs +65 -0
- package/dist/commands/protection/verify.cjs +80 -0
- package/dist/commands/session/get-token.cjs +75 -0
- package/dist/commands/session/redeem-token.cjs +56 -0
- package/dist/commands/setting/update.cjs +79 -0
- package/dist/commands/site/get.cjs +73 -0
- package/dist/commands/site/search.cjs +93 -0
- package/dist/commands/spot/check.cjs +52 -0
- package/dist/commands/spot/create.cjs +79 -0
- package/dist/commands/spot/delete.cjs +68 -0
- package/dist/commands/spot/list.cjs +120 -0
- package/dist/commands/spot/reset-version.cjs +53 -0
- package/dist/commands/spot/set-videos.cjs +64 -0
- package/dist/commands/spot/update.cjs +78 -0
- package/dist/commands/tag/list.cjs +98 -0
- package/dist/commands/tag/related.cjs +57 -0
- package/dist/commands/thumbnail/add.cjs +66 -0
- package/dist/commands/thumbnail/data.cjs +60 -0
- package/dist/commands/thumbnail/delete.cjs +61 -0
- package/dist/commands/thumbnail/duplicate.cjs +67 -0
- package/dist/commands/thumbnail/file/delete.cjs +71 -0
- package/dist/commands/thumbnail/file/list.cjs +74 -0
- package/dist/commands/thumbnail/file/upload.cjs +79 -0
- package/dist/commands/thumbnail/index.cjs +11 -0
- package/dist/commands/thumbnail/list.cjs +89 -0
- package/dist/commands/thumbnail/update.cjs +87 -0
- package/dist/commands/user/create.cjs +87 -0
- package/dist/commands/user/get-login-token.cjs +72 -0
- package/dist/commands/user/get.cjs +67 -0
- package/dist/commands/user/list.cjs +95 -0
- package/dist/commands/user/redeem-login-token.cjs +55 -0
- package/dist/commands/user/send-invitation.cjs +63 -0
- package/dist/commands/user/tokens.cjs +80 -0
- package/dist/commands/user/update.cjs +121 -0
- package/dist/commands/video/delete.cjs +67 -0
- package/dist/commands/video/frame.cjs +69 -0
- package/dist/commands/video/get.cjs +68 -0
- package/dist/commands/video/index.cjs +11 -0
- package/dist/commands/video/list.cjs +98 -0
- package/dist/commands/video/replace.cjs +126 -0
- package/dist/commands/video/section/create.cjs +73 -0
- package/dist/commands/video/section/delete.cjs +78 -0
- package/dist/commands/video/section/index.cjs +11 -0
- package/dist/commands/video/section/list.cjs +75 -0
- package/dist/commands/video/section/set-thumbnail.cjs +73 -0
- package/dist/commands/video/section/update.cjs +84 -0
- package/dist/commands/video/subtitle/archive.cjs +76 -0
- package/dist/commands/video/subtitle/create.cjs +80 -0
- package/dist/commands/video/subtitle/data.cjs +75 -0
- package/dist/commands/video/subtitle/delete.cjs +84 -0
- package/dist/commands/video/subtitle/duplicate.cjs +89 -0
- package/dist/commands/video/subtitle/index.cjs +11 -0
- package/dist/commands/video/subtitle/list.cjs +94 -0
- package/dist/commands/video/subtitle/locales.cjs +60 -0
- package/dist/commands/video/subtitle/set-primary.cjs +64 -0
- package/dist/commands/video/subtitle/types.cjs +42 -0
- package/dist/commands/video/subtitle/update.cjs +93 -0
- package/dist/commands/video/subtitle/upload.cjs +104 -0
- package/dist/commands/video/transcoding-progress.cjs +71 -0
- package/dist/commands/video/update.cjs +184 -0
- package/dist/commands/video/upload.cjs +151 -0
- package/dist/commands/webhook/events.cjs +68 -0
- package/dist/commands/webhook/list.cjs +64 -0
- package/dist/commands/webhook/sample.cjs +47 -0
- package/dist/commands/webhook/subscribe.cjs +66 -0
- package/dist/commands/webhook/unsubscribe.cjs +71 -0
- package/dist/commands/webinar/attachment/delete.cjs +79 -0
- package/dist/commands/webinar/attachment/list.cjs +94 -0
- package/dist/commands/webinar/attachment/set-hidden.cjs +85 -0
- package/dist/commands/webinar/attachment/upload.cjs +128 -0
- package/dist/commands/webinar/clips.cjs +77 -0
- package/dist/commands/webinar/create.cjs +107 -0
- package/dist/commands/webinar/delete.cjs +62 -0
- package/dist/commands/webinar/highlights.cjs +84 -0
- package/dist/commands/webinar/index.cjs +11 -0
- package/dist/commands/webinar/list-formats.cjs +50 -0
- package/dist/commands/webinar/list.cjs +134 -0
- package/dist/commands/webinar/log.cjs +69 -0
- package/dist/commands/webinar/mail/add.cjs +101 -0
- package/dist/commands/webinar/mail/list.cjs +83 -0
- package/dist/commands/webinar/mail/preview.cjs +75 -0
- package/dist/commands/webinar/mail/remove.cjs +75 -0
- package/dist/commands/webinar/mail/send.cjs +68 -0
- package/dist/commands/webinar/mail/test.cjs +86 -0
- package/dist/commands/webinar/mail/update.cjs +84 -0
- package/dist/commands/webinar/metrics.cjs +59 -0
- package/dist/commands/webinar/queued-video/add.cjs +81 -0
- package/dist/commands/webinar/queued-video/remove.cjs +81 -0
- package/dist/commands/webinar/recording/split.cjs +53 -0
- package/dist/commands/webinar/recording/start.cjs +53 -0
- package/dist/commands/webinar/recording/status.cjs +52 -0
- package/dist/commands/webinar/recording/stop.cjs +53 -0
- package/dist/commands/webinar/repeat.cjs +65 -0
- package/dist/commands/webinar/room/connect.cjs +60 -0
- package/dist/commands/webinar/room/info.cjs +60 -0
- package/dist/commands/webinar/room/send-recording.cjs +59 -0
- package/dist/commands/webinar/room/themes.cjs +63 -0
- package/dist/commands/webinar/section/add.cjs +98 -0
- package/dist/commands/webinar/section/list.cjs +86 -0
- package/dist/commands/webinar/section/remove.cjs +75 -0
- package/dist/commands/webinar/section/update.cjs +85 -0
- package/dist/commands/webinar/series/apply-recurrence.cjs +68 -0
- package/dist/commands/webinar/series/cancel.cjs +73 -0
- package/dist/commands/webinar/series/create.cjs +80 -0
- package/dist/commands/webinar/series/delete.cjs +73 -0
- package/dist/commands/webinar/series/list.cjs +79 -0
- package/dist/commands/webinar/series/mapped-objects.cjs +69 -0
- package/dist/commands/webinar/series/metrics.cjs +57 -0
- package/dist/commands/webinar/series/recurrences.cjs +72 -0
- package/dist/commands/webinar/series/set-ondemand.cjs +67 -0
- package/dist/commands/webinar/series/skip-recurrence.cjs +79 -0
- package/dist/commands/webinar/series/update.cjs +71 -0
- package/dist/commands/webinar/series/upload-thumbnail.cjs +117 -0
- package/dist/commands/webinar/speaker/add-from-speaker.cjs +78 -0
- package/dist/commands/webinar/speaker/add-from-user.cjs +78 -0
- package/dist/commands/webinar/speaker/add.cjs +112 -0
- package/dist/commands/webinar/speaker/cancel-guest-request.cjs +62 -0
- package/dist/commands/webinar/speaker/connection-types.cjs +66 -0
- package/dist/commands/webinar/speaker/library.cjs +59 -0
- package/dist/commands/webinar/speaker/list.cjs +90 -0
- package/dist/commands/webinar/speaker/remove-avatar.cjs +62 -0
- package/dist/commands/webinar/speaker/remove.cjs +69 -0
- package/dist/commands/webinar/speaker/request-guest.cjs +62 -0
- package/dist/commands/webinar/speaker/send-invitation.cjs +62 -0
- package/dist/commands/webinar/speaker/set-avatar.cjs +130 -0
- package/dist/commands/webinar/speaker/set-order.cjs +95 -0
- package/dist/commands/webinar/speaker/update.cjs +90 -0
- package/dist/commands/webinar/transcription/connect.cjs +69 -0
- package/dist/commands/webinar/transcription/list.cjs +88 -0
- package/dist/commands/webinar/transcription/locales.cjs +76 -0
- package/dist/commands/webinar/transcription/transcriptionlist.cjs +67 -0
- package/dist/commands/webinar/update.cjs +192 -0
- package/dist/commands/webinar/upload-image.cjs +133 -0
- package/dist/commands/workspace/list.cjs +47 -0
- package/dist/commands/workspace/use.cjs +52 -0
- package/dist/index.cjs +6 -0
- package/dist/lib/analytics-flags.cjs +60 -0
- package/dist/lib/audit.cjs +44 -0
- package/dist/lib/base-command.cjs +156 -0
- package/dist/lib/output.cjs +82 -0
- package/dist/lib/pagination.cjs +36 -0
- package/dist/lib/term-map.cjs +48 -0
- package/dist/upload/chunk-pool.cjs +47 -0
- package/dist/upload/chunked-upload.cjs +131 -0
- package/dist/upload/types.cjs +9 -0
- package/docs/commands/README.md +31 -0
- package/docs/commands/action/add.md +36 -0
- package/docs/commands/action/delete.md +32 -0
- package/docs/commands/action/exclude.md +38 -0
- package/docs/commands/action/get.md +47 -0
- package/docs/commands/action/include.md +38 -0
- package/docs/commands/action/list.md +43 -0
- package/docs/commands/action/types.md +34 -0
- package/docs/commands/action/update.md +42 -0
- package/docs/commands/action/upload.md +36 -0
- package/docs/commands/action.md +306 -0
- package/docs/commands/analytics/conversions.md +113 -0
- package/docs/commands/analytics/live.md +338 -0
- package/docs/commands/analytics/usage.md +674 -0
- package/docs/commands/analytics/video.md +449 -0
- package/docs/commands/analytics.md +1559 -0
- package/docs/commands/app/add.md +38 -0
- package/docs/commands/app/delete.md +32 -0
- package/docs/commands/app/update.md +41 -0
- package/docs/commands/app.md +101 -0
- package/docs/commands/audience/companies.md +41 -0
- package/docs/commands/audience/field.md +119 -0
- package/docs/commands/audience/funnel.md +38 -0
- package/docs/commands/audience/identity-sources.md +29 -0
- package/docs/commands/audience/list-collectors.md +35 -0
- package/docs/commands/audience/list.md +42 -0
- package/docs/commands/audience/metrics.md +40 -0
- package/docs/commands/audience/register.md +45 -0
- package/docs/commands/audience/remove.md +35 -0
- package/docs/commands/audience/search.md +39 -0
- package/docs/commands/audience/timelines.md +41 -0
- package/docs/commands/audience/unregister.md +36 -0
- package/docs/commands/audience.md +485 -0
- package/docs/commands/auth/credentials.md +23 -0
- package/docs/commands/auth/status.md +27 -0
- package/docs/commands/auth.md +45 -0
- package/docs/commands/category/create.md +36 -0
- package/docs/commands/category/delete.md +32 -0
- package/docs/commands/category/list.md +34 -0
- package/docs/commands/category/update.md +39 -0
- package/docs/commands/category.md +141 -0
- package/docs/commands/collector/exclude.md +33 -0
- package/docs/commands/collector/include.md +33 -0
- package/docs/commands/collector/list.md +37 -0
- package/docs/commands/collector.md +93 -0
- package/docs/commands/comment/add.md +43 -0
- package/docs/commands/comment/clone.md +35 -0
- package/docs/commands/comment/delete.md +32 -0
- package/docs/commands/comment/list.md +42 -0
- package/docs/commands/comment/promote.md +37 -0
- package/docs/commands/comment/reaction.md +106 -0
- package/docs/commands/comment/set-order.md +34 -0
- package/docs/commands/comment/update.md +36 -0
- package/docs/commands/comment.md +330 -0
- package/docs/commands/doctor.md +28 -0
- package/docs/commands/openupload/list.md +36 -0
- package/docs/commands/openupload/update-file.md +40 -0
- package/docs/commands/openupload/upload-file.md +39 -0
- package/docs/commands/openupload.md +105 -0
- package/docs/commands/player/delete.md +32 -0
- package/docs/commands/player/embed-versions.md +34 -0
- package/docs/commands/player/embed.md +51 -0
- package/docs/commands/player/list.md +32 -0
- package/docs/commands/player/styles.md +32 -0
- package/docs/commands/player/update.md +37 -0
- package/docs/commands/player.md +193 -0
- package/docs/commands/poll/add.md +33 -0
- package/docs/commands/poll/answer.md +38 -0
- package/docs/commands/poll/list.md +33 -0
- package/docs/commands/poll/remove.md +32 -0
- package/docs/commands/poll/set-options.md +35 -0
- package/docs/commands/poll/update.md +41 -0
- package/docs/commands/poll.md +187 -0
- package/docs/commands/presentation/page.md +29 -0
- package/docs/commands/presentation/setting.md +58 -0
- package/docs/commands/presentation.md +82 -0
- package/docs/commands/protection/protect.md +37 -0
- package/docs/commands/protection/unprotect.md +34 -0
- package/docs/commands/protection/verify.md +39 -0
- package/docs/commands/protection.md +100 -0
- package/docs/commands/session/get-token.md +38 -0
- package/docs/commands/session/redeem-token.md +32 -0
- package/docs/commands/session.md +65 -0
- package/docs/commands/setting/update.md +37 -0
- package/docs/commands/setting.md +37 -0
- package/docs/commands/site/get.md +35 -0
- package/docs/commands/site/search.md +38 -0
- package/docs/commands/site.md +68 -0
- package/docs/commands/spot/check.md +32 -0
- package/docs/commands/spot/create.md +38 -0
- package/docs/commands/spot/delete.md +32 -0
- package/docs/commands/spot/list.md +43 -0
- package/docs/commands/spot/reset-version.md +32 -0
- package/docs/commands/spot/set-videos.md +35 -0
- package/docs/commands/spot/update.md +40 -0
- package/docs/commands/spot.md +222 -0
- package/docs/commands/tag/list.md +44 -0
- package/docs/commands/tag/related.md +32 -0
- package/docs/commands/tag.md +71 -0
- package/docs/commands/thumbnail/add.md +33 -0
- package/docs/commands/thumbnail/data.md +35 -0
- package/docs/commands/thumbnail/delete.md +32 -0
- package/docs/commands/thumbnail/duplicate.md +37 -0
- package/docs/commands/thumbnail/file.md +90 -0
- package/docs/commands/thumbnail/list.md +37 -0
- package/docs/commands/thumbnail/update.md +44 -0
- package/docs/commands/thumbnail.md +293 -0
- package/docs/commands/user/create.md +41 -0
- package/docs/commands/user/get-login-token.md +37 -0
- package/docs/commands/user/get.md +37 -0
- package/docs/commands/user/list.md +40 -0
- package/docs/commands/user/redeem-login-token.md +32 -0
- package/docs/commands/user/send-invitation.md +37 -0
- package/docs/commands/user/tokens.md +34 -0
- package/docs/commands/user/update.md +44 -0
- package/docs/commands/user.md +267 -0
- package/docs/commands/video/delete.md +32 -0
- package/docs/commands/video/frame.md +37 -0
- package/docs/commands/video/get.md +32 -0
- package/docs/commands/video/list.md +33 -0
- package/docs/commands/video/replace.md +37 -0
- package/docs/commands/video/section.md +177 -0
- package/docs/commands/video/subtitle.md +362 -0
- package/docs/commands/video/transcoding-progress.md +32 -0
- package/docs/commands/video/update.md +45 -0
- package/docs/commands/video/upload.md +44 -0
- package/docs/commands/video.md +801 -0
- package/docs/commands/webhook/events.md +34 -0
- package/docs/commands/webhook/list.md +29 -0
- package/docs/commands/webhook/sample.md +32 -0
- package/docs/commands/webhook/subscribe.md +33 -0
- package/docs/commands/webhook/unsubscribe.md +35 -0
- package/docs/commands/webhook.md +143 -0
- package/docs/commands/webinar/attachment.md +135 -0
- package/docs/commands/webinar/clips.md +32 -0
- package/docs/commands/webinar/create.md +40 -0
- package/docs/commands/webinar/delete.md +32 -0
- package/docs/commands/webinar/highlights.md +37 -0
- package/docs/commands/webinar/list-formats.md +29 -0
- package/docs/commands/webinar/list.md +41 -0
- package/docs/commands/webinar/log.md +32 -0
- package/docs/commands/webinar/mail.md +238 -0
- package/docs/commands/webinar/metrics.md +32 -0
- package/docs/commands/webinar/queued-video.md +65 -0
- package/docs/commands/webinar/recording.md +113 -0
- package/docs/commands/webinar/repeat.md +35 -0
- package/docs/commands/webinar/room.md +110 -0
- package/docs/commands/webinar/section.md +134 -0
- package/docs/commands/webinar/series.md +365 -0
- package/docs/commands/webinar/speaker.md +430 -0
- package/docs/commands/webinar/transcription.md +125 -0
- package/docs/commands/webinar/update.md +43 -0
- package/docs/commands/webinar/upload-image.md +42 -0
- package/docs/commands/webinar.md +2030 -0
- package/docs/commands/workspace/list.md +27 -0
- package/docs/commands/workspace/use.md +32 -0
- package/docs/commands/workspace.md +54 -0
- package/docs/guides/api-spec-upgrade.md +92 -0
- package/docs/guides/getting-started.md +66 -0
- package/oclif.manifest.json +21609 -0
- package/package.json +71 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const require_runtime = require("../../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_auth_workspace_config = require("../../auth/workspace-config.cjs");
|
|
3
|
+
const require_lib_base_command = require("../../lib/base-command.cjs");
|
|
4
|
+
let _oclif_core = require("@oclif/core");
|
|
5
|
+
let chalk = require("chalk");
|
|
6
|
+
chalk = require_runtime.__toESM(chalk);
|
|
7
|
+
let _clack_prompts = require("@clack/prompts");
|
|
8
|
+
_clack_prompts = require_runtime.__toESM(_clack_prompts);
|
|
9
|
+
//#region src/commands/workspace/use.ts
|
|
10
|
+
var Use = class Use extends require_lib_base_command.BaseCommand {
|
|
11
|
+
static description = "Switch the active workspace";
|
|
12
|
+
static agentMetadata = {
|
|
13
|
+
api_endpoint: "local",
|
|
14
|
+
auth_scope: "none",
|
|
15
|
+
output_shape: { type: "none" },
|
|
16
|
+
side_effects: "updates"
|
|
17
|
+
};
|
|
18
|
+
static examples = ["<%= config.bin %> workspace use company.video23.com", "<%= config.bin %> workspace use \"Company Name\""];
|
|
19
|
+
static enableJsonFlag = true;
|
|
20
|
+
static args = { name: _oclif_core.Args.string({
|
|
21
|
+
description: "Workspace domain or display name",
|
|
22
|
+
required: true
|
|
23
|
+
}) };
|
|
24
|
+
async run() {
|
|
25
|
+
const { args } = await this.parse(Use);
|
|
26
|
+
const name = args.name;
|
|
27
|
+
const result = require_auth_workspace_config.findWorkspace(name, require_auth_workspace_config.getWorkspaces());
|
|
28
|
+
let workspace;
|
|
29
|
+
if (result === null) this.error(`No workspace matching '${name}' found. Run \`twentythree workspace list\` to see available workspaces.`, { exit: 1 });
|
|
30
|
+
else if (Array.isArray(result)) {
|
|
31
|
+
const chosen = await _clack_prompts.select({
|
|
32
|
+
message: `Multiple workspaces match '${name}'. Select one:`,
|
|
33
|
+
options: result.map((w) => ({
|
|
34
|
+
value: w.domain,
|
|
35
|
+
label: `${w.display_name} (${w.domain})`
|
|
36
|
+
}))
|
|
37
|
+
});
|
|
38
|
+
if (_clack_prompts.isCancel(chosen)) {
|
|
39
|
+
_clack_prompts.cancel("Cancelled");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const found = result.find((w) => w.domain === chosen);
|
|
43
|
+
if (!found) this.error("Selected workspace not found", { exit: 1 });
|
|
44
|
+
workspace = found;
|
|
45
|
+
} else workspace = result;
|
|
46
|
+
require_auth_workspace_config.setActiveWorkspace(workspace.domain);
|
|
47
|
+
this.log(`Active workspace set to ${chalk.default.bold(workspace.domain)} (${workspace.display_name})`);
|
|
48
|
+
if (this.jsonEnabled()) return workspace;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
//#endregion
|
|
52
|
+
module.exports = Use;
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_lib_term_map = require("./lib/term-map.cjs");
|
|
3
|
+
exports.TERM_MAP = require_lib_term_map.TERM_MAP;
|
|
4
|
+
exports.applyCliTerms = require_lib_term_map.applyCliTerms;
|
|
5
|
+
exports.toApiTerm = require_lib_term_map.toApiTerm;
|
|
6
|
+
exports.toCliTerm = require_lib_term_map.toCliTerm;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
require("../_virtual/_rolldown/runtime.cjs");
|
|
3
|
+
let _oclif_core = require("@oclif/core");
|
|
4
|
+
//#region src/lib/analytics-flags.ts
|
|
5
|
+
/**
|
|
6
|
+
* Shared date filter flags for all analytics commands.
|
|
7
|
+
* Supports explicit date ranges (date_start/date_end) or a predefined expression.
|
|
8
|
+
*/
|
|
9
|
+
const ANALYTICS_DATE_FLAGS = {
|
|
10
|
+
"date-start": _oclif_core.Flags.string({
|
|
11
|
+
description: "First date (YYYY-MM-DD)",
|
|
12
|
+
required: false
|
|
13
|
+
}),
|
|
14
|
+
"date-end": _oclif_core.Flags.string({
|
|
15
|
+
description: "Last date (YYYY-MM-DD)",
|
|
16
|
+
required: false
|
|
17
|
+
}),
|
|
18
|
+
"date-expression": _oclif_core.Flags.string({
|
|
19
|
+
description: "Predefined date range (e.g. thisweek, lastyear)",
|
|
20
|
+
required: false
|
|
21
|
+
})
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Pagination flags for analytics endpoints that support paging.
|
|
25
|
+
*/
|
|
26
|
+
const ANALYTICS_PAGINATION_FLAGS = {
|
|
27
|
+
page: _oclif_core.Flags.integer({
|
|
28
|
+
description: "Page number",
|
|
29
|
+
required: false
|
|
30
|
+
}),
|
|
31
|
+
size: _oclif_core.Flags.integer({
|
|
32
|
+
description: "Page size",
|
|
33
|
+
required: false
|
|
34
|
+
})
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Optional filter flags for analytics endpoints that support grouping and ordering.
|
|
38
|
+
*/
|
|
39
|
+
const ANALYTICS_FILTER_FLAGS = {
|
|
40
|
+
selection: _oclif_core.Flags.string({
|
|
41
|
+
description: "Scope to specific objects/types",
|
|
42
|
+
required: false
|
|
43
|
+
}),
|
|
44
|
+
groupby: _oclif_core.Flags.string({
|
|
45
|
+
description: "Group results by dimension",
|
|
46
|
+
required: false
|
|
47
|
+
}),
|
|
48
|
+
orderby: _oclif_core.Flags.string({
|
|
49
|
+
description: "Order results by field",
|
|
50
|
+
required: false
|
|
51
|
+
}),
|
|
52
|
+
order: _oclif_core.Flags.string({
|
|
53
|
+
description: "Sort direction (asc/desc)",
|
|
54
|
+
required: false
|
|
55
|
+
})
|
|
56
|
+
};
|
|
57
|
+
//#endregion
|
|
58
|
+
exports.ANALYTICS_DATE_FLAGS = ANALYTICS_DATE_FLAGS;
|
|
59
|
+
exports.ANALYTICS_FILTER_FLAGS = ANALYTICS_FILTER_FLAGS;
|
|
60
|
+
exports.ANALYTICS_PAGINATION_FLAGS = ANALYTICS_PAGINATION_FLAGS;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/lib/audit.ts
|
|
3
|
+
/**
|
|
4
|
+
* EXCLUDED_OPERATIONS — the authoritative list of spec endpoints that are intentionally
|
|
5
|
+
* not implemented as CLI commands. Any spec endpoint not covered by a command file AND
|
|
6
|
+
* not present in this list will be reported as a gap by scripts/audit-endpoints.mjs.
|
|
7
|
+
*
|
|
8
|
+
* Add entries here when an endpoint is confirmed to have no CLI use case, with a clear
|
|
9
|
+
* reason and appropriate category.
|
|
10
|
+
*/
|
|
11
|
+
const EXCLUDED_OPERATIONS = [
|
|
12
|
+
{
|
|
13
|
+
endpoint: "GET /photo/get-upload-token",
|
|
14
|
+
reason: "Server-to-server token delegation for external upload flows; no CLI use case",
|
|
15
|
+
category: "internal"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
endpoint: "GET /photo/get-replace-token",
|
|
19
|
+
reason: "Server-to-server token delegation for external replace flows; no CLI use case",
|
|
20
|
+
category: "internal"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
endpoint: "GET /photo/get-update-token",
|
|
24
|
+
reason: "Server-to-server token delegation for external update flows; no CLI use case",
|
|
25
|
+
category: "internal"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
endpoint: "POST /photo/delete-upload-token",
|
|
29
|
+
reason: "Counterpart to get-upload-token; server-to-server only",
|
|
30
|
+
category: "internal"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
endpoint: "POST /photo/update-upload-token",
|
|
34
|
+
reason: "Update video metadata via upload token; server-to-server only",
|
|
35
|
+
category: "internal"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
endpoint: "POST /photo/subtitle/archive/get-progress",
|
|
39
|
+
reason: "Covered by video subtitle archive --progress flag (dual-endpoint command)",
|
|
40
|
+
category: "non-standard"
|
|
41
|
+
}
|
|
42
|
+
];
|
|
43
|
+
//#endregion
|
|
44
|
+
exports.EXCLUDED_OPERATIONS = EXCLUDED_OPERATIONS;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
3
|
+
const require_api_client = require("../api/client.cjs");
|
|
4
|
+
const require_auth_workspace_config = require("../auth/workspace-config.cjs");
|
|
5
|
+
const require_auth_token_refresh = require("../auth/token-refresh.cjs");
|
|
6
|
+
let _oclif_core = require("@oclif/core");
|
|
7
|
+
let chalk = require("chalk");
|
|
8
|
+
chalk = require_runtime.__toESM(chalk);
|
|
9
|
+
let _clack_prompts = require("@clack/prompts");
|
|
10
|
+
//#region src/lib/base-command.ts
|
|
11
|
+
var BaseCommand = class extends _oclif_core.Command {
|
|
12
|
+
static enableJsonFlag = true;
|
|
13
|
+
static baseFlags = {
|
|
14
|
+
workspace: _oclif_core.Flags.string({
|
|
15
|
+
char: "w",
|
|
16
|
+
summary: "Workspace domain or display name to use for this invocation.",
|
|
17
|
+
helpGroup: "GLOBAL"
|
|
18
|
+
}),
|
|
19
|
+
agent: _oclif_core.Flags.boolean({
|
|
20
|
+
description: "Output machine-readable command metadata for AI agent consumption",
|
|
21
|
+
helpGroup: "GLOBAL",
|
|
22
|
+
hidden: true
|
|
23
|
+
})
|
|
24
|
+
};
|
|
25
|
+
flags;
|
|
26
|
+
activeWorkspace;
|
|
27
|
+
/** Base URL with /api/2/ appended — use this when constructing URLs outside of openapi-fetch (e.g. chunked upload) */
|
|
28
|
+
apiBaseUrl;
|
|
29
|
+
apiClient;
|
|
30
|
+
async init() {
|
|
31
|
+
await super.init();
|
|
32
|
+
if (process.argv.includes("--agent")) {
|
|
33
|
+
const ctor = this.ctor;
|
|
34
|
+
const flagDefs = ctor.flags ?? {};
|
|
35
|
+
const agentMeta = ctor.agentMetadata;
|
|
36
|
+
const flagsArr = Object.entries(flagDefs).filter(([name]) => ![
|
|
37
|
+
"workspace",
|
|
38
|
+
"agent",
|
|
39
|
+
"json"
|
|
40
|
+
].includes(name)).map(([name, def]) => ({
|
|
41
|
+
name,
|
|
42
|
+
type: def.type ?? "string",
|
|
43
|
+
required: def.required ?? false,
|
|
44
|
+
default: def.default ?? null,
|
|
45
|
+
description: def.description ?? def.summary ?? ""
|
|
46
|
+
}));
|
|
47
|
+
const output = {
|
|
48
|
+
command: this.id,
|
|
49
|
+
description: ctor.description ?? "",
|
|
50
|
+
flags: flagsArr,
|
|
51
|
+
examples: (ctor.examples ?? []).map((e) => typeof e === "string" ? e : e?.command ?? String(e)),
|
|
52
|
+
api_endpoint: agentMeta?.api_endpoint ?? null,
|
|
53
|
+
auth_scope: agentMeta?.auth_scope ?? "read",
|
|
54
|
+
output_shape: agentMeta?.output_shape ?? { type: "none" },
|
|
55
|
+
side_effects: agentMeta?.side_effects ?? "none"
|
|
56
|
+
};
|
|
57
|
+
process.stdout.write(JSON.stringify(output, null, 2) + "\n");
|
|
58
|
+
process.exit(0);
|
|
59
|
+
}
|
|
60
|
+
const { flags } = await this.parse({
|
|
61
|
+
flags: this.ctor.flags,
|
|
62
|
+
baseFlags: super.ctor.baseFlags,
|
|
63
|
+
enableJsonFlag: this.ctor.enableJsonFlag,
|
|
64
|
+
args: this.ctor.args,
|
|
65
|
+
strict: this.ctor.strict
|
|
66
|
+
});
|
|
67
|
+
this.flags = flags;
|
|
68
|
+
const workspaceFlagValue = flags.workspace;
|
|
69
|
+
let resolved = null;
|
|
70
|
+
if (workspaceFlagValue) {
|
|
71
|
+
const result = require_auth_workspace_config.findWorkspace(workspaceFlagValue, require_auth_workspace_config.getWorkspaces());
|
|
72
|
+
if (result === null) this.error(`No workspace matching '${workspaceFlagValue}' found — run \`twentythree workspace list\` to see available workspaces`, { exit: 1 });
|
|
73
|
+
else if (Array.isArray(result)) {
|
|
74
|
+
const chosen = await (0, _clack_prompts.select)({
|
|
75
|
+
message: `Multiple workspaces match '${workspaceFlagValue}'. Select one:`,
|
|
76
|
+
options: result.map((w) => ({
|
|
77
|
+
value: w.domain,
|
|
78
|
+
label: `${w.display_name} (${w.domain})`
|
|
79
|
+
}))
|
|
80
|
+
});
|
|
81
|
+
if (typeof chosen === "symbol") this.error("Workspace selection cancelled", { exit: 1 });
|
|
82
|
+
resolved = require_auth_workspace_config.getWorkspaceForDomain(chosen);
|
|
83
|
+
} else resolved = result;
|
|
84
|
+
} else {
|
|
85
|
+
const activeDomain = require_auth_workspace_config.getActiveWorkspace();
|
|
86
|
+
if (activeDomain) resolved = require_auth_workspace_config.getWorkspaceForDomain(activeDomain);
|
|
87
|
+
}
|
|
88
|
+
if (!resolved) this.error("No workspace configured — run `twentythree auth credentials` to set up", { exit: 1 });
|
|
89
|
+
this.activeWorkspace = resolved;
|
|
90
|
+
if (resolved.bearer_token) {
|
|
91
|
+
const freshToken = await require_auth_token_refresh.ensureFreshToken(resolved.domain);
|
|
92
|
+
if (freshToken) this.activeWorkspace = {
|
|
93
|
+
...resolved,
|
|
94
|
+
bearer_token: freshToken
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
this.apiBaseUrl = this.activeWorkspace.api_base_url.replace(/\/?$/, "/") + "api/2/";
|
|
98
|
+
this.apiClient = require_api_client.createApiClient({
|
|
99
|
+
baseUrl: this.apiBaseUrl,
|
|
100
|
+
token: this.activeWorkspace.bearer_token || void 0
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Print the [domain] workspace header in dim style.
|
|
105
|
+
* Call at the top of every command's run() method (AUTH-04).
|
|
106
|
+
*/
|
|
107
|
+
printWorkspaceHeader() {
|
|
108
|
+
this.log(chalk.default.dim(`[${this.activeWorkspace.domain}]`));
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* AuthenticatedCommand extends BaseCommand with an auth guard.
|
|
113
|
+
* Commands requiring a bearer token extend this class instead of BaseCommand.
|
|
114
|
+
* Commands for anonymous-scope endpoints extend BaseCommand directly.
|
|
115
|
+
*
|
|
116
|
+
* Rejects execution with exact AUTH-10 error message when no token is configured.
|
|
117
|
+
*/
|
|
118
|
+
var AuthenticatedCommand = class extends BaseCommand {
|
|
119
|
+
async init() {
|
|
120
|
+
await super.init();
|
|
121
|
+
if (!this.activeWorkspace.bearer_token) this.error("This command requires authentication — run `twentythree auth credentials` to add a bearer token", { exit: 1 });
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Fetch the API token for a video by ID.
|
|
125
|
+
* Required by endpoints (e.g. section/list, subtitle/list) that demand a real token param.
|
|
126
|
+
* Throws a user-friendly error if the video is not found.
|
|
127
|
+
*/
|
|
128
|
+
async fetchVideoToken(videoId) {
|
|
129
|
+
const { data, error } = await this.apiClient.GET("/photo/list", { params: { query: {
|
|
130
|
+
photo_id: Number(videoId),
|
|
131
|
+
include_unpublished_p: 1
|
|
132
|
+
} } });
|
|
133
|
+
if (error) this.error(`Could not look up video ${videoId}: ${error}`, { exit: 1 });
|
|
134
|
+
const resp = data;
|
|
135
|
+
const video = Array.isArray(resp?.data) ? resp.data[0] : resp?.data;
|
|
136
|
+
if (!video?.token) this.error(`Video ${videoId} not found or has no token`, { exit: 1 });
|
|
137
|
+
return video.token;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Fetch the API token for a webinar by ID.
|
|
141
|
+
* Required by Phase 5 webinar endpoints that demand a real token param.
|
|
142
|
+
* Mirrors fetchVideoToken but calls /live/list instead of /photo/list.
|
|
143
|
+
* Throws a user-friendly error if the webinar is not found.
|
|
144
|
+
*/
|
|
145
|
+
async fetchWebinarToken(webinarId) {
|
|
146
|
+
const { data, error } = await this.apiClient.GET("/live/list", { params: { query: { live_id: Number(webinarId) } } });
|
|
147
|
+
if (error) this.error(`Could not look up webinar ${webinarId}: ${error}`, { exit: 1 });
|
|
148
|
+
const resp = data;
|
|
149
|
+
const webinar = Array.isArray(resp?.data) ? resp.data[0] : resp?.data;
|
|
150
|
+
if (!webinar?.token) this.error(`Webinar ${webinarId} not found or has no token`, { exit: 1 });
|
|
151
|
+
return webinar.token;
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
//#endregion
|
|
155
|
+
exports.AuthenticatedCommand = AuthenticatedCommand;
|
|
156
|
+
exports.BaseCommand = BaseCommand;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
3
|
+
const require_lib_term_map = require("./term-map.cjs");
|
|
4
|
+
let cli_table3 = require("cli-table3");
|
|
5
|
+
cli_table3 = require_runtime.__toESM(cli_table3);
|
|
6
|
+
//#region src/lib/output.ts
|
|
7
|
+
const EXIT_SUCCESS = 0;
|
|
8
|
+
const EXIT_ERROR = 1;
|
|
9
|
+
const EXIT_CANCELLED = 2;
|
|
10
|
+
/**
|
|
11
|
+
* Format a CLI-01-compliant JSON output object.
|
|
12
|
+
*
|
|
13
|
+
* When ok=false, applies applyCliTerms() to the summary to prevent leaking
|
|
14
|
+
* internal API field names (e.g. "photo" → "video") to the user (T-03-01 mitigation).
|
|
15
|
+
*/
|
|
16
|
+
function formatJsonOutput(params) {
|
|
17
|
+
const ok = params.ok !== false;
|
|
18
|
+
const summary = ok ? params.summary : require_lib_term_map.applyCliTerms(params.summary);
|
|
19
|
+
return {
|
|
20
|
+
ok,
|
|
21
|
+
data: params.data,
|
|
22
|
+
summary,
|
|
23
|
+
breadcrumbs: params.breadcrumbs
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Create a cli-table3 Table instance with the given headers and rows.
|
|
28
|
+
* Headers are styled in cyan.
|
|
29
|
+
*/
|
|
30
|
+
function renderTable(headers, rows) {
|
|
31
|
+
const table = new cli_table3.default({
|
|
32
|
+
head: headers,
|
|
33
|
+
style: { head: ["cyan"] }
|
|
34
|
+
});
|
|
35
|
+
for (const row of rows) table.push(row);
|
|
36
|
+
return table;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Format a byte count as a human-readable string.
|
|
40
|
+
* Examples: "512 B", "45 KB", "300 MB", "1.5 GB"
|
|
41
|
+
*/
|
|
42
|
+
function formatBytes(bytes) {
|
|
43
|
+
if (bytes === 0) return "0 B";
|
|
44
|
+
const KB = 1024;
|
|
45
|
+
const MB = 1024 * KB;
|
|
46
|
+
const GB = 1024 * MB;
|
|
47
|
+
if (bytes >= GB) {
|
|
48
|
+
const value = bytes / GB;
|
|
49
|
+
return `${Number(value.toFixed(1))} GB`;
|
|
50
|
+
}
|
|
51
|
+
if (bytes >= MB) {
|
|
52
|
+
const value = bytes / MB;
|
|
53
|
+
return `${Number(value.toFixed(1))} MB`;
|
|
54
|
+
}
|
|
55
|
+
if (bytes >= KB) {
|
|
56
|
+
const value = bytes / KB;
|
|
57
|
+
return `${Number(value.toFixed(1))} KB`;
|
|
58
|
+
}
|
|
59
|
+
return `${bytes} B`;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Resolve a potentially relative URL to an absolute URL using the workspace domain.
|
|
63
|
+
*
|
|
64
|
+
* - If url is undefined or empty, return it unchanged.
|
|
65
|
+
* - If url already starts with http:// or https://, return it unchanged.
|
|
66
|
+
* - Otherwise, construct a full URL using new URL(url, baseUrl).
|
|
67
|
+
*
|
|
68
|
+
* The baseUrl is the workspace's api_base_url (e.g. "https://video.company.com/").
|
|
69
|
+
*/
|
|
70
|
+
function resolveUrl(url, baseUrl) {
|
|
71
|
+
if (!url) return url;
|
|
72
|
+
if (url.startsWith("http://") || url.startsWith("https://")) return url;
|
|
73
|
+
return new URL(url, baseUrl).toString();
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
exports.EXIT_CANCELLED = EXIT_CANCELLED;
|
|
77
|
+
exports.EXIT_ERROR = EXIT_ERROR;
|
|
78
|
+
exports.EXIT_SUCCESS = EXIT_SUCCESS;
|
|
79
|
+
exports.formatBytes = formatBytes;
|
|
80
|
+
exports.formatJsonOutput = formatJsonOutput;
|
|
81
|
+
exports.renderTable = renderTable;
|
|
82
|
+
exports.resolveUrl = resolveUrl;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/lib/pagination.ts
|
|
3
|
+
/**
|
|
4
|
+
* Generic auto-pagination helper (CLI-02).
|
|
5
|
+
*
|
|
6
|
+
* Fetches all pages from a paginated API endpoint by calling fetchPage
|
|
7
|
+
* repeatedly until all items have been retrieved or an empty page is returned.
|
|
8
|
+
*/
|
|
9
|
+
const PAGE_SIZE = 100;
|
|
10
|
+
/**
|
|
11
|
+
* Fetch all pages from a paginated API endpoint.
|
|
12
|
+
*
|
|
13
|
+
* @param fetchPage - Function that takes (page: number, size: number) and returns
|
|
14
|
+
* a promise resolving to { data?: T[], total_count?: number }
|
|
15
|
+
* @returns Concatenated array of all items across all pages
|
|
16
|
+
*/
|
|
17
|
+
async function fetchAllPages(fetchPage) {
|
|
18
|
+
const allItems = [];
|
|
19
|
+
let page = 1;
|
|
20
|
+
while (true) {
|
|
21
|
+
const response = await fetchPage(page, PAGE_SIZE);
|
|
22
|
+
const items = response.data ?? [];
|
|
23
|
+
if (items.length === 0) break;
|
|
24
|
+
allItems.push(...items);
|
|
25
|
+
const totalCount = response.total_count ?? 0;
|
|
26
|
+
if (totalCount > 0 && allItems.length >= totalCount) break;
|
|
27
|
+
if (totalCount === 0) {
|
|
28
|
+
page++;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
page++;
|
|
32
|
+
}
|
|
33
|
+
return allItems;
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
exports.fetchAllPages = fetchAllPages;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/lib/term-map.ts
|
|
3
|
+
/**
|
|
4
|
+
* Bidirectional terminology translation between API legacy terms and CLI modern terms.
|
|
5
|
+
*
|
|
6
|
+
* The TwentyThree API uses legacy terms (photo, album, live).
|
|
7
|
+
* The CLI uses modern terms (video, category, webinar).
|
|
8
|
+
* This module translates between them.
|
|
9
|
+
*/
|
|
10
|
+
const API_TO_CLI = {
|
|
11
|
+
photo: "video",
|
|
12
|
+
album: "category",
|
|
13
|
+
live: "webinar"
|
|
14
|
+
};
|
|
15
|
+
const CLI_TO_API = Object.fromEntries(Object.entries(API_TO_CLI).map(([k, v]) => [v, k]));
|
|
16
|
+
/**
|
|
17
|
+
* Convert an API legacy term to the modern CLI term.
|
|
18
|
+
* Unknown terms are returned unchanged.
|
|
19
|
+
*/
|
|
20
|
+
function toCliTerm(apiTerm) {
|
|
21
|
+
return API_TO_CLI[apiTerm.toLowerCase()] ?? apiTerm;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Convert a CLI modern term back to the API legacy term.
|
|
25
|
+
* Unknown terms are returned unchanged.
|
|
26
|
+
*/
|
|
27
|
+
function toApiTerm(cliTerm) {
|
|
28
|
+
return CLI_TO_API[cliTerm.toLowerCase()] ?? cliTerm;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Apply term mapping to an entire string, replacing all occurrences
|
|
32
|
+
* of API legacy terms with CLI modern terms.
|
|
33
|
+
* Used for mapping error message bodies and API response text.
|
|
34
|
+
*/
|
|
35
|
+
function applyCliTerms(text) {
|
|
36
|
+
let result = text;
|
|
37
|
+
for (const [apiTerm, cliTerm] of Object.entries(API_TO_CLI)) result = result.replace(new RegExp(`(?<![a-zA-Z])${apiTerm}(?![a-zA-Z])`, "gi"), cliTerm);
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
const TERM_MAP = {
|
|
41
|
+
API_TO_CLI,
|
|
42
|
+
CLI_TO_API
|
|
43
|
+
};
|
|
44
|
+
//#endregion
|
|
45
|
+
exports.TERM_MAP = TERM_MAP;
|
|
46
|
+
exports.applyCliTerms = applyCliTerms;
|
|
47
|
+
exports.toApiTerm = toApiTerm;
|
|
48
|
+
exports.toCliTerm = toCliTerm;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/upload/chunk-pool.ts
|
|
3
|
+
/**
|
|
4
|
+
* Default exponential backoff delay: min(1000 * 2^attempt, 30000) ms
|
|
5
|
+
*/
|
|
6
|
+
function defaultDelay(ms) {
|
|
7
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Upload chunks concurrently in windows of `concurrency` size.
|
|
11
|
+
* Retries each failed chunk up to maxRetries times with exponential backoff.
|
|
12
|
+
* Aborts immediately on 500. Skips already-completed chunks.
|
|
13
|
+
*/
|
|
14
|
+
async function uploadChunkPool(params) {
|
|
15
|
+
const { chunks, uploadFn, concurrency, maxRetries, onChunkComplete, delayFn = defaultDelay, completed = /* @__PURE__ */ new Set() } = params;
|
|
16
|
+
let finalResponse;
|
|
17
|
+
/**
|
|
18
|
+
* Upload a single chunk with retry and exponential backoff.
|
|
19
|
+
*/
|
|
20
|
+
async function uploadWithRetry(chunk) {
|
|
21
|
+
if (completed.has(chunk.number)) return;
|
|
22
|
+
let attempt = 0;
|
|
23
|
+
while (true) {
|
|
24
|
+
const response = await uploadFn(chunk);
|
|
25
|
+
if (response.status === 200) {
|
|
26
|
+
completed.add(chunk.number);
|
|
27
|
+
finalResponse = response.data;
|
|
28
|
+
onChunkComplete?.(chunk, response);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (response.status === 500) throw new Error("Unsupported file format — upload aborted");
|
|
32
|
+
if (attempt >= maxRetries) throw new Error(`Chunk ${chunk.number} failed after ${maxRetries} retries`);
|
|
33
|
+
await delayFn(Math.min(1e3 * Math.pow(2, attempt), 3e4));
|
|
34
|
+
attempt++;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
for (let i = 0; i < chunks.length; i += concurrency) {
|
|
38
|
+
const window = chunks.slice(i, i + concurrency);
|
|
39
|
+
await Promise.all(window.map((chunk) => uploadWithRetry(chunk)));
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
completedChunks: completed.size,
|
|
43
|
+
finalResponse
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
exports.uploadChunkPool = uploadChunkPool;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
3
|
+
const require_upload_chunk_pool = require("./chunk-pool.cjs");
|
|
4
|
+
const require_upload_types = require("./types.cjs");
|
|
5
|
+
let node_fs_promises = require("node:fs/promises");
|
|
6
|
+
let node_fs = require("node:fs");
|
|
7
|
+
let node_path = require("node:path");
|
|
8
|
+
node_path = require_runtime.__toESM(node_path);
|
|
9
|
+
//#region src/upload/chunked-upload.ts
|
|
10
|
+
/**
|
|
11
|
+
* Chunked upload engine — splits a file into chunks and uploads them via the
|
|
12
|
+
* TwentyThree resumable.js protocol.
|
|
13
|
+
*
|
|
14
|
+
* This module is display-agnostic: it reports progress via an onProgress
|
|
15
|
+
* callback and has zero imports of chalk, ora, or cli-progress.
|
|
16
|
+
*
|
|
17
|
+
* Threat mitigations implemented here:
|
|
18
|
+
* T-03-02: uploadUrl must use https:// (validated before first request)
|
|
19
|
+
* T-03-03: upload_token is never logged; onProgress reports byte counts only
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Read a file slice into a Buffer by collecting stream chunks.
|
|
23
|
+
*/
|
|
24
|
+
async function readSlice(filePath, start, end) {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
const chunks = [];
|
|
27
|
+
const stream = (0, node_fs.createReadStream)(filePath, {
|
|
28
|
+
start,
|
|
29
|
+
end: end - 1
|
|
30
|
+
});
|
|
31
|
+
stream.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
|
|
32
|
+
stream.on("end", () => resolve(Buffer.concat(chunks)));
|
|
33
|
+
stream.on("error", reject);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Main chunked upload function. Splits the file into chunks and uploads them
|
|
38
|
+
* concurrently via uploadChunkPool with retry and progress tracking.
|
|
39
|
+
*
|
|
40
|
+
* @throws Error if file is not found
|
|
41
|
+
* @throws Error if uploadUrl is not https://
|
|
42
|
+
*/
|
|
43
|
+
async function uploadChunked(params) {
|
|
44
|
+
const { filePath, uploadToken, uploadUrl, tokenFieldName = "upload_token", bearerToken, chunkSize = require_upload_types.DEFAULT_CHUNK_SIZE, concurrency = 5, maxRetries = 5, onProgress, extraFields } = params;
|
|
45
|
+
if (!uploadUrl.startsWith("https://")) throw new Error(`uploadUrl must use https:// — insecure upload URLs are not allowed: ${uploadUrl}`);
|
|
46
|
+
let totalSize;
|
|
47
|
+
try {
|
|
48
|
+
totalSize = (await (0, node_fs_promises.stat)(filePath)).size;
|
|
49
|
+
} catch (err) {
|
|
50
|
+
if (err?.code === "ENOENT") throw new Error(`File not found: ${filePath}`);
|
|
51
|
+
throw err;
|
|
52
|
+
}
|
|
53
|
+
const totalChunks = Math.max(1, Math.floor(totalSize / chunkSize));
|
|
54
|
+
const chunks = Array.from({ length: totalChunks }, (_, i) => {
|
|
55
|
+
const start = i * chunkSize;
|
|
56
|
+
const end = i < totalChunks - 1 ? start + chunkSize : totalSize;
|
|
57
|
+
return {
|
|
58
|
+
number: i + 1,
|
|
59
|
+
start,
|
|
60
|
+
end,
|
|
61
|
+
size: end - start
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
const filename = node_path.basename(filePath);
|
|
65
|
+
const resumableIdentifier = filename;
|
|
66
|
+
let bytesUploaded = 0;
|
|
67
|
+
/**
|
|
68
|
+
* Upload function for a single chunk — builds FormData and POSTs via native fetch.
|
|
69
|
+
* Returns { status, data } for the pool to handle retry/abort logic.
|
|
70
|
+
*/
|
|
71
|
+
async function uploadFn(chunk) {
|
|
72
|
+
try {
|
|
73
|
+
const sliceBuffer = await readSlice(filePath, chunk.start, chunk.end);
|
|
74
|
+
const blob = new Blob([sliceBuffer]);
|
|
75
|
+
const formData = new FormData();
|
|
76
|
+
formData.append(tokenFieldName, uploadToken);
|
|
77
|
+
formData.append("file", blob, filename);
|
|
78
|
+
formData.append("resumableChunkNumber", String(chunk.number));
|
|
79
|
+
formData.append("resumableChunkSize", String(chunkSize));
|
|
80
|
+
formData.append("resumableTotalSize", String(totalSize));
|
|
81
|
+
formData.append("resumableIdentifier", resumableIdentifier);
|
|
82
|
+
formData.append("resumableFilename", filename);
|
|
83
|
+
formData.append("resumableTotalChunks", String(totalChunks));
|
|
84
|
+
if (extraFields) for (const [key, value] of Object.entries(extraFields)) formData.append(key, value);
|
|
85
|
+
const headers = {};
|
|
86
|
+
if (bearerToken) headers["Authorization"] = `Bearer ${bearerToken}`;
|
|
87
|
+
const response = await fetch(uploadUrl, {
|
|
88
|
+
method: "POST",
|
|
89
|
+
headers,
|
|
90
|
+
body: formData
|
|
91
|
+
});
|
|
92
|
+
let data;
|
|
93
|
+
try {
|
|
94
|
+
const buf = await response.arrayBuffer();
|
|
95
|
+
const text = Buffer.from(buf).toString("utf-8");
|
|
96
|
+
try {
|
|
97
|
+
data = JSON.parse(text);
|
|
98
|
+
} catch {
|
|
99
|
+
data = text || void 0;
|
|
100
|
+
}
|
|
101
|
+
} catch {
|
|
102
|
+
data = void 0;
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
status: response.status,
|
|
106
|
+
data
|
|
107
|
+
};
|
|
108
|
+
} catch (err) {
|
|
109
|
+
return { status: 0 };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Callback fired after each chunk completes successfully.
|
|
114
|
+
* Accumulates bytesUploaded and fires onProgress.
|
|
115
|
+
*/
|
|
116
|
+
function onChunkComplete(chunk) {
|
|
117
|
+
bytesUploaded += chunk.size;
|
|
118
|
+
onProgress?.(bytesUploaded, totalSize);
|
|
119
|
+
}
|
|
120
|
+
const raw = (await require_upload_chunk_pool.uploadChunkPool({
|
|
121
|
+
chunks,
|
|
122
|
+
uploadFn,
|
|
123
|
+
concurrency,
|
|
124
|
+
maxRetries,
|
|
125
|
+
onChunkComplete: (chunk) => onChunkComplete(chunk)
|
|
126
|
+
})).finalResponse;
|
|
127
|
+
if (typeof raw === "number") return { photo_id: raw };
|
|
128
|
+
return raw ?? {};
|
|
129
|
+
}
|
|
130
|
+
//#endregion
|
|
131
|
+
exports.uploadChunked = uploadChunked;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/upload/types.ts
|
|
3
|
+
const DEFAULT_CHUNK_SIZE = 5 * 1024 * 1024;
|
|
4
|
+
const DEFAULT_CONCURRENCY = 5;
|
|
5
|
+
const DEFAULT_MAX_RETRIES = 5;
|
|
6
|
+
//#endregion
|
|
7
|
+
exports.DEFAULT_CHUNK_SIZE = DEFAULT_CHUNK_SIZE;
|
|
8
|
+
exports.DEFAULT_CONCURRENCY = DEFAULT_CONCURRENCY;
|
|
9
|
+
exports.DEFAULT_MAX_RETRIES = DEFAULT_MAX_RETRIES;
|