@k2b/cloud 0.6.0-rc.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 +98 -0
- package/package.json +128 -0
- package/public/logo.svg +1 -0
- package/scripts/app-favicon.test.ts +49 -0
- package/scripts/app-favicon.ts +51 -0
- package/scripts/build.ts +205 -0
- package/scripts/preload.ts +164 -0
- package/scripts/sync-recovery-smoke.ts +200 -0
- package/src/_internal/app-favicon.ts +2 -0
- package/src/_internal/app-presentation.ts +61 -0
- package/src/_internal/app-workspace-state.ts +24 -0
- package/src/_internal/bounded-json.ts +47 -0
- package/src/_internal/build-metadata.ts +9 -0
- package/src/_internal/capabilities.ts +1118 -0
- package/src/_internal/define-app.ts +801 -0
- package/src/_internal/heartbeat.ts +167 -0
- package/src/_internal/help-catalog.ts +241 -0
- package/src/_internal/help.ts +95 -0
- package/src/_internal/nats-connection.ts +34 -0
- package/src/_internal/page-responses.ts +40 -0
- package/src/_internal/process-sync.ts +117 -0
- package/src/_internal/readiness.ts +3 -0
- package/src/_internal/registry-validation.ts +136 -0
- package/src/_internal/registry-watch.ts +52 -0
- package/src/_internal/registry.ts +338 -0
- package/src/_internal/runtime-compatibility.ts +64 -0
- package/src/_internal/runtime-context.ts +84 -0
- package/src/_internal/runtime-watcher.ts +78 -0
- package/src/_internal/static-assets.ts +86 -0
- package/src/_internal/status-preserving-ssr.ts +49 -0
- package/src/access/PermissionEditor.tsx +486 -0
- package/src/access/ResourceApiKeys.tsx +271 -0
- package/src/access/messages.ts +121 -0
- package/src/access/ui.ts +16 -0
- package/src/account/Avatar.tsx +28 -0
- package/src/account/EntitySearch.tsx +363 -0
- package/src/account/avatar-upload-dialog.tsx +197 -0
- package/src/account/avatar-upload.ts +149 -0
- package/src/account/ui.ts +3 -0
- package/src/ai/admin.ts +47 -0
- package/src/ai/approval-routes.ts +130 -0
- package/src/ai/approvals.ts +139 -0
- package/src/ai/assistant-models.ts +61 -0
- package/src/ai/attachments.ts +40 -0
- package/src/ai/browser.ts +92 -0
- package/src/ai/capabilities.ts +714 -0
- package/src/ai/capability-execution.ts +151 -0
- package/src/ai/chat/blocks.render.test.tsx +1108 -0
- package/src/ai/chat/blocks.tsx +730 -0
- package/src/ai/chat/builtin-tools.tsx +428 -0
- package/src/ai/chat/capability-table.tsx +87 -0
- package/src/ai/chat/composer-adapter.ts +169 -0
- package/src/ai/chat/file-tools.tsx +81 -0
- package/src/ai/chat/message-actions.tsx +370 -0
- package/src/ai/chat/message-utils.ts +479 -0
- package/src/ai/chat/messages.ts +43 -0
- package/src/ai/chat/presentation.tsx +395 -0
- package/src/ai/chat/primitives.tsx +3 -0
- package/src/ai/chat/tool-disclosure.tsx +41 -0
- package/src/ai/chat/turn-navigator-utils.ts +28 -0
- package/src/ai/chat/turn-navigator.tsx +164 -0
- package/src/ai/chat/user-message.tsx +149 -0
- package/src/ai/chat/visual-tools.tsx +435 -0
- package/src/ai/chat/web-tools.tsx +202 -0
- package/src/ai/chat-task-contracts.ts +113 -0
- package/src/ai/chat-tasks.ts +952 -0
- package/src/ai/client/controller.ts +1211 -0
- package/src/ai/client/file-source.ts +95 -0
- package/src/ai/client/live-connection.ts +140 -0
- package/src/ai/client/projection.ts +178 -0
- package/src/ai/client/transport.ts +111 -0
- package/src/ai/compaction.ts +182 -0
- package/src/ai/credentials.ts +124 -0
- package/src/ai/default-tool-contracts.ts +108 -0
- package/src/ai/default-tools.ts +127 -0
- package/src/ai/enrich.ts +280 -0
- package/src/ai/executor.ts +1624 -0
- package/src/ai/fetch-file-tool.ts +313 -0
- package/src/ai/file-context.ts +92 -0
- package/src/ai/file-media-type.ts +28 -0
- package/src/ai/file-mount.ts +16 -0
- package/src/ai/file-tools.ts +566 -0
- package/src/ai/files-store.ts +390 -0
- package/src/ai/firecrawl-tools.ts +305 -0
- package/src/ai/grids-skill.ts +126 -0
- package/src/ai/http.ts +265 -0
- package/src/ai/index.ts +337 -0
- package/src/ai/kit-skill.ts +22 -0
- package/src/ai/limits.ts +5 -0
- package/src/ai/live-events.ts +289 -0
- package/src/ai/live-messages.ts +45 -0
- package/src/ai/live-outbox.ts +102 -0
- package/src/ai/live-routes.ts +412 -0
- package/src/ai/live.ts +2 -0
- package/src/ai/maintenance.ts +302 -0
- package/src/ai/markdown-pdf-tool.ts +95 -0
- package/src/ai/memories.ts +605 -0
- package/src/ai/memory-learning-runs.ts +222 -0
- package/src/ai/memory-learning.ts +619 -0
- package/src/ai/memory-tool.ts +120 -0
- package/src/ai/memory-workflow-evidence.ts +158 -0
- package/src/ai/migrate.ts +2082 -0
- package/src/ai/model-access-migrate.ts +30 -0
- package/src/ai/model-access.ts +191 -0
- package/src/ai/personal-agent.ts +6 -0
- package/src/ai/prefs.ts +72 -0
- package/src/ai/project-tool.ts +94 -0
- package/src/ai/projects-routes.ts +277 -0
- package/src/ai/projects.ts +902 -0
- package/src/ai/protocol.ts +279 -0
- package/src/ai/provider.ts +73 -0
- package/src/ai/public-projection.ts +25 -0
- package/src/ai/resource-markers.ts +31 -0
- package/src/ai/resource-refs.ts +70 -0
- package/src/ai/routes.ts +1131 -0
- package/src/ai/runtime-tools.ts +39 -0
- package/src/ai/runtime.ts +544 -0
- package/src/ai/settings.ts +529 -0
- package/src/ai/short-id.ts +45 -0
- package/src/ai/skill-catalog.ts +73 -0
- package/src/ai/skill-format.ts +145 -0
- package/src/ai/skill-seeds.ts +430 -0
- package/src/ai/skill-tool.ts +81 -0
- package/src/ai/skills-routes.ts +143 -0
- package/src/ai/skills.ts +934 -0
- package/src/ai/solid.ts +27 -0
- package/src/ai/store.ts +3113 -0
- package/src/ai/stream.ts +308 -0
- package/src/ai/structured-runs.ts +66 -0
- package/src/ai/structured.ts +186 -0
- package/src/ai/system-prompt.ts +170 -0
- package/src/ai/task-prompt.ts +22 -0
- package/src/ai/timeline.ts +148 -0
- package/src/ai/tool-audit.ts +177 -0
- package/src/ai/tool-result-budget.ts +16 -0
- package/src/ai/tools.ts +155 -0
- package/src/ai/types.ts +967 -0
- package/src/ai/ui.tsx +39 -0
- package/src/ai/usage-migrate.ts +81 -0
- package/src/ai/usage.ts +315 -0
- package/src/ai/validate.ts +54 -0
- package/src/ai/vision-tool.ts +79 -0
- package/src/api/accounts-entities.ts +174 -0
- package/src/api/admin-ai-projects.ts +74 -0
- package/src/api/admin-ai-skills.ts +76 -0
- package/src/api/admin-ai-usage.ts +42 -0
- package/src/api/admin-core-settings.ts +508 -0
- package/src/api/admin-identity.ts +148 -0
- package/src/api/admin-lifecycle.ts +206 -0
- package/src/api/admin-linux-identities.ts +66 -0
- package/src/api/announcements.ts +131 -0
- package/src/api/app-approval.ts +129 -0
- package/src/api/apps.ts +51 -0
- package/src/api/auth/schemas.ts +54 -0
- package/src/api/auth.ts +370 -0
- package/src/api/capabilities.ts +658 -0
- package/src/api/help-messages.ts +19 -0
- package/src/api/help.ts +78 -0
- package/src/api/index.ts +108 -0
- package/src/api/mcp.ts +744 -0
- package/src/api/me-rail.ts +45 -0
- package/src/api/me.ts +753 -0
- package/src/api/search/schemas.ts +79 -0
- package/src/api/search.bench.ts +185 -0
- package/src/api/search.ts +407 -0
- package/src/api/sync-ops.ts +79 -0
- package/src/api/widgets.ts +143 -0
- package/src/browser/CloudResourceSearch.behavior.test.tsx +74 -0
- package/src/browser/CloudResourceSearch.tsx +791 -0
- package/src/browser/app-approval-vault.ts +305 -0
- package/src/browser/app-approval.ts +234 -0
- package/src/browser/live-websocket.ts +268 -0
- package/src/browser/locale-preference.ts +25 -0
- package/src/browser/notification-target.ts +27 -0
- package/src/browser/notification-timeout.ts +14 -0
- package/src/browser/notifications.ts +181 -0
- package/src/browser/resource-clipboard.ts +70 -0
- package/src/browser/resource-picker.tsx +39 -0
- package/src/browser/resource-search-messages.ts +55 -0
- package/src/browser/resource-search.ts +27 -0
- package/src/capabilities/client.ts +149 -0
- package/src/capabilities/response.ts +39 -0
- package/src/capabilities/server.ts +215 -0
- package/src/capabilities/signals.ts +4 -0
- package/src/capabilities/testing.ts +12 -0
- package/src/capabilities/types.ts +32 -0
- package/src/cli/access.ts +499 -0
- package/src/cli/account.ts +382 -0
- package/src/cli/admin/account-administration.ts +45 -0
- package/src/cli/admin/account-categories.ts +49 -0
- package/src/cli/admin/ai-usage.ts +118 -0
- package/src/cli/admin/app-sign-in.ts +49 -0
- package/src/cli/admin/data.ts +343 -0
- package/src/cli/admin/documentation.ts +34 -0
- package/src/cli/admin/gateway.ts +155 -0
- package/src/cli/admin/index.ts +61 -0
- package/src/cli/admin/instance.ts +250 -0
- package/src/cli/admin/jobs.ts +223 -0
- package/src/cli/admin/legal.ts +122 -0
- package/src/cli/admin/linux.ts +53 -0
- package/src/cli/admin/logs.ts +324 -0
- package/src/cli/admin/metrics.ts +142 -0
- package/src/cli/admin/nats.ts +167 -0
- package/src/cli/admin/notifications.ts +516 -0
- package/src/cli/admin/shared.ts +113 -0
- package/src/cli/admin/sync.ts +188 -0
- package/src/cli/admin/telemetry.ts +314 -0
- package/src/cli/admin/webhooks.ts +222 -0
- package/src/cli/admin/workflows.ts +367 -0
- package/src/cli/apps.ts +40 -0
- package/src/cli/capabilities.ts +239 -0
- package/src/cli/commands.ts +604 -0
- package/src/cli/index.ts +61 -0
- package/src/cli/locale.ts +31 -0
- package/src/cli/output.ts +58 -0
- package/src/clients/core.ts +20 -0
- package/src/config/env.ts +54 -0
- package/src/config/index.ts +6 -0
- package/src/contracts/account-categories.ts +62 -0
- package/src/contracts/announcements.ts +121 -0
- package/src/contracts/app-approval.ts +197 -0
- package/src/contracts/app.ts +162 -0
- package/src/contracts/capabilities.ts +484 -0
- package/src/contracts/index.ts +14 -0
- package/src/contracts/notification-live.ts +66 -0
- package/src/contracts/notification-types.ts +235 -0
- package/src/contracts/posix.ts +69 -0
- package/src/contracts/profile.ts +85 -0
- package/src/contracts/rail-preferences.ts +53 -0
- package/src/contracts/registry.ts +110 -0
- package/src/contracts/resource-clipboard.ts +33 -0
- package/src/contracts/settings-types.ts +98 -0
- package/src/contracts/shared.ts +408 -0
- package/src/contracts/user-notifications.ts +101 -0
- package/src/contracts/widgets.ts +183 -0
- package/src/desktop/index.ts +704 -0
- package/src/desktop/solid.tsx +938 -0
- package/src/index.ts +58 -0
- package/src/server/actor.ts +41 -0
- package/src/server/api/index.ts +2 -0
- package/src/server/api/respond.ts +96 -0
- package/src/server/api-client.ts +54 -0
- package/src/server/app-context.ts +39 -0
- package/src/server/help.ts +187 -0
- package/src/server/index.ts +89 -0
- package/src/server/locale.ts +55 -0
- package/src/server/middleware/auth.ts +386 -0
- package/src/server/middleware/index.ts +28 -0
- package/src/server/middleware/invocation.ts +55 -0
- package/src/server/middleware/middleware.ts +65 -0
- package/src/server/middleware/openapi.ts +126 -0
- package/src/server/middleware/rate-limit.ts +125 -0
- package/src/server/middleware/request-logger.ts +41 -0
- package/src/server/middleware/route-template.ts +46 -0
- package/src/server/middleware/runtime.ts +22 -0
- package/src/server/middleware/settings.ts +55 -0
- package/src/server/middleware/validator.ts +49 -0
- package/src/server/middleware/workload.ts +22 -0
- package/src/server/ratelimit.ts +151 -0
- package/src/server/request-cookies.ts +18 -0
- package/src/server/services/access.ts +644 -0
- package/src/server/services/freeipa/client.ts +137 -0
- package/src/server/services/freeipa/index.ts +9 -0
- package/src/server/services/freeipa/session.ts +123 -0
- package/src/server/services/freeipa/test-certificates.ts +44 -0
- package/src/server/services/freeipa/tls.ts +44 -0
- package/src/server/services/freeipa/transport.ts +116 -0
- package/src/server/services/freeipa/util.ts +59 -0
- package/src/server/services/geo.ts +198 -0
- package/src/server/services/index.ts +48 -0
- package/src/server/services/pagination.ts +22 -0
- package/src/server/services/services.ts +13 -0
- package/src/server/time.ts +33 -0
- package/src/services/account-category-policy.ts +51 -0
- package/src/services/account-lifecycle/audit.ts +41 -0
- package/src/services/account-lifecycle/index.ts +940 -0
- package/src/services/account-lifecycle/ipa-backfill.ts +152 -0
- package/src/services/account-lifecycle/notification-sender.ts +13 -0
- package/src/services/account-lifecycle/scheduler.ts +506 -0
- package/src/services/account-model.ts +22 -0
- package/src/services/accounts/app.ts +1671 -0
- package/src/services/accounts/authz.ts +44 -0
- package/src/services/accounts/avatar.ts +120 -0
- package/src/services/accounts/base-group.ts +11 -0
- package/src/services/accounts/base-user.ts +46 -0
- package/src/services/accounts/duplicate-emails.ts +82 -0
- package/src/services/accounts/entities.ts +622 -0
- package/src/services/accounts/group-sql.ts +106 -0
- package/src/services/accounts/groups.ts +272 -0
- package/src/services/accounts/index.ts +14 -0
- package/src/services/accounts/ipa-data.ts +66 -0
- package/src/services/accounts/lifecycle.ts +2 -0
- package/src/services/accounts/local-groups.ts +499 -0
- package/src/services/accounts/model.ts +129 -0
- package/src/services/accounts/notification-sender.ts +31 -0
- package/src/services/accounts/posix.ts +386 -0
- package/src/services/accounts/request-policy.ts +11 -0
- package/src/services/accounts/switching.ts +126 -0
- package/src/services/accounts/users.ts +745 -0
- package/src/services/announcements/index.ts +224 -0
- package/src/services/app-approval.ts +498 -0
- package/src/services/audit/index.ts +438 -0
- package/src/services/auth-flows/index.ts +15 -0
- package/src/services/auth-flows/ipa.ts +197 -0
- package/src/services/auth-flows/magic-link.ts +185 -0
- package/src/services/auth-flows/notification-sender.ts +10 -0
- package/src/services/auth-flows/password-reset.ts +268 -0
- package/src/services/auth-flows/proxy-return.ts +49 -0
- package/src/services/document-extraction.ts +148 -0
- package/src/services/freeipa-config.ts +179 -0
- package/src/services/gateway.ts +186 -0
- package/src/services/identity/constants.ts +18 -0
- package/src/services/identity/index.ts +52 -0
- package/src/services/identity/invocation-actor.ts +120 -0
- package/src/services/identity/invocation-authority.ts +53 -0
- package/src/services/identity/invocation-operations.ts +15 -0
- package/src/services/identity/invocation-token.ts +276 -0
- package/src/services/identity/key-config.ts +41 -0
- package/src/services/identity/key-ring.ts +530 -0
- package/src/services/identity/metrics.ts +29 -0
- package/src/services/identity/routes.ts +18 -0
- package/src/services/identity/runtime-config.ts +88 -0
- package/src/services/identity/session-token.ts +152 -0
- package/src/services/identity/signer-refresh.ts +44 -0
- package/src/services/identity/workload-auth.ts +64 -0
- package/src/services/index.ts +187 -0
- package/src/services/ipa/auth.ts +216 -0
- package/src/services/ipa/connection.ts +57 -0
- package/src/services/ipa/effective-groups.ts +70 -0
- package/src/services/ipa/groups.ts +722 -0
- package/src/services/ipa/guard.ts +17 -0
- package/src/services/ipa/index.ts +17 -0
- package/src/services/ipa/posix.ts +19 -0
- package/src/services/ipa/profile.ts +132 -0
- package/src/services/ipa/runtime.ts +28 -0
- package/src/services/ipa/search.ts +154 -0
- package/src/services/ipa/service-account.ts +15 -0
- package/src/services/ipa/sync-planning.ts +100 -0
- package/src/services/ipa/sync.ts +862 -0
- package/src/services/ipa/users.ts +806 -0
- package/src/services/legal-consent.ts +76 -0
- package/src/services/logging/index.ts +477 -0
- package/src/services/logging/redaction.ts +20 -0
- package/src/services/logging/trace.ts +1231 -0
- package/src/services/mandates/index.ts +855 -0
- package/src/services/mandates/policy.ts +85 -0
- package/src/services/network-security.ts +31 -0
- package/src/services/notifications/batches.ts +722 -0
- package/src/services/notifications/browser.ts +252 -0
- package/src/services/notifications/catalog.ts +113 -0
- package/src/services/notifications/channels.ts +104 -0
- package/src/services/notifications/dispatcher.ts +139 -0
- package/src/services/notifications/email.ts +118 -0
- package/src/services/notifications/index.ts +633 -0
- package/src/services/notifications/live.ts +50 -0
- package/src/services/notifications/observability.ts +407 -0
- package/src/services/notifications/platform.ts +421 -0
- package/src/services/notifications/runtime.ts +120 -0
- package/src/services/notifications/user.ts +248 -0
- package/src/services/notifications/web-push-transport.ts +135 -0
- package/src/services/oauth-tokens.ts +209 -0
- package/src/services/outbox.ts +187 -0
- package/src/services/pdf/gotenberg.ts +290 -0
- package/src/services/pdf/index.ts +43 -0
- package/src/services/pdf/markdown.ts +209 -0
- package/src/services/pdf/template-preview.ts +122 -0
- package/src/services/postgres.ts +76 -0
- package/src/services/providers/index.ts +27 -0
- package/src/services/providers/local/auth.ts +51 -0
- package/src/services/providers/local/index.ts +4 -0
- package/src/services/providers/local/users.ts +263 -0
- package/src/services/rail-preferences.ts +27 -0
- package/src/services/runtime-lifecycle.ts +191 -0
- package/src/services/secrets.ts +10 -0
- package/src/services/service-account-credentials.ts +891 -0
- package/src/services/service-accounts.ts +184 -0
- package/src/services/session/index.ts +236 -0
- package/src/services/session/test-fixture.ts +18 -0
- package/src/services/session/user.ts +144 -0
- package/src/services/settings/api.ts +61 -0
- package/src/services/settings/app.ts +86 -0
- package/src/services/settings/core-settings.ts +796 -0
- package/src/services/settings/crypto.ts +69 -0
- package/src/services/settings/defaults.ts +374 -0
- package/src/services/settings/index.ts +215 -0
- package/src/services/settings/namespace.ts +9 -0
- package/src/services/settings/snapshot.ts +49 -0
- package/src/services/settings/store.ts +241 -0
- package/src/services/settings/templates.ts +4 -0
- package/src/services/sync-ops.ts +403 -0
- package/src/services/topic-cursor.ts +22 -0
- package/src/services/weather/forecast.ts +323 -0
- package/src/services/weather/geo.ts +119 -0
- package/src/services/weather/index.ts +67 -0
- package/src/services/weather/location.ts +24 -0
- package/src/services/weather/locations.ts +133 -0
- package/src/services/weather/migrate.ts +45 -0
- package/src/services/weather/settings.ts +66 -0
- package/src/services/weather/short-id.ts +57 -0
- package/src/services/weather/types.ts +61 -0
- package/src/services/weather/ui.ts +50 -0
- package/src/services/webauthn.ts +385 -0
- package/src/shared/account-action-notice.ts +70 -0
- package/src/shared/account-display.ts +15 -0
- package/src/shared/account-session.ts +15 -0
- package/src/shared/ai-platform-prompt.ts +134 -0
- package/src/shared/ai-usage.ts +49 -0
- package/src/shared/app-presentation.ts +48 -0
- package/src/shared/app-url.ts +12 -0
- package/src/shared/branding.ts +14 -0
- package/src/shared/capability-messages.ts +70 -0
- package/src/shared/email-html.ts +54 -0
- package/src/shared/format.ts +91 -0
- package/src/shared/help.ts +68 -0
- package/src/shared/icons.ts +400 -0
- package/src/shared/index.ts +54 -0
- package/src/shared/locale.ts +48 -0
- package/src/shared/login-method.ts +44 -0
- package/src/shared/markdown/client.ts +130 -0
- package/src/shared/markdown/extensions/code.ts +105 -0
- package/src/shared/markdown/extensions/guided-help.ts +100 -0
- package/src/shared/markdown/extensions/images.ts +79 -0
- package/src/shared/markdown/extensions/info-blocks.ts +97 -0
- package/src/shared/markdown/extensions/katex.ts +120 -0
- package/src/shared/markdown/extensions/links.ts +43 -0
- package/src/shared/markdown/extensions/mark.ts +48 -0
- package/src/shared/markdown/extensions/sub-sup.ts +60 -0
- package/src/shared/markdown/extensions/tables.ts +109 -0
- package/src/shared/markdown/extensions/task-list.ts +53 -0
- package/src/shared/markdown/formula.ts +1187 -0
- package/src/shared/markdown/index.ts +193 -0
- package/src/shared/markdown/shared.ts +36 -0
- package/src/shared/mock-cover.ts +130 -0
- package/src/shared/network-address.ts +106 -0
- package/src/shared/redirect.ts +56 -0
- package/src/shared/template-rendering.ts +140 -0
- package/src/shared/theme.ts +68 -0
- package/src/shared/time.ts +13 -0
- package/src/ssr/AdminLayout.tsx +53 -0
- package/src/ssr/AdminSidebar.tsx +50 -0
- package/src/ssr/AppLaunchpad.island.tsx +236 -0
- package/src/ssr/AppWorkspaceController.island.tsx +21 -0
- package/src/ssr/BrowserNotifications.island.tsx +135 -0
- package/src/ssr/GlobalAnnouncements.island.tsx +142 -0
- package/src/ssr/GlobalSearchDialog.behavior.test.tsx +96 -0
- package/src/ssr/GlobalSearchDialog.tsx +35 -0
- package/src/ssr/GlobalSearchHelpDialog.tsx +104 -0
- package/src/ssr/GlobalSearchTrigger.island.tsx +55 -0
- package/src/ssr/HotkeysHelpRail.island.tsx +55 -0
- package/src/ssr/Layout.render.test.tsx +148 -0
- package/src/ssr/Layout.tsx +292 -0
- package/src/ssr/LayoutBreadcrumbs.island.tsx +44 -0
- package/src/ssr/LayoutFooter.tsx +22 -0
- package/src/ssr/LayoutHeader.tsx +81 -0
- package/src/ssr/LayoutHelp.tsx +869 -0
- package/src/ssr/LayoutPreferences.island.tsx +36 -0
- package/src/ssr/LayoutRail.tsx +58 -0
- package/src/ssr/MinimalLayout.tsx +46 -0
- package/src/ssr/PageError.tsx +29 -0
- package/src/ssr/ProfilePreferences.island.tsx +92 -0
- package/src/ssr/RailApps.island.tsx +61 -0
- package/src/ssr/RailEditor.tsx +293 -0
- package/src/ssr/RegisteredHelpDocuments.island.tsx +3 -0
- package/src/ssr/TimezoneCookie.island.tsx +23 -0
- package/src/ssr/admin-active-link.ts +47 -0
- package/src/ssr/admin-navigation.ts +78 -0
- package/src/ssr/app-appearance.ts +23 -0
- package/src/ssr/app-navigation.ts +41 -0
- package/src/ssr/help-messages.ts +104 -0
- package/src/ssr/index.ts +14 -0
- package/src/ssr/islands/SearchBar.island.tsx +80 -0
- package/src/ssr/islands/index.ts +16 -0
- package/src/ssr/layout-context.ts +17 -0
- package/src/ssr/layout-help-markdown.ts +9 -0
- package/src/ssr/layout-help-navigation.ts +24 -0
- package/src/ssr/layout-help-url.ts +5 -0
- package/src/ssr/layout-runtime.ts +22 -0
- package/src/ssr/page-error-messages.ts +25 -0
- package/src/ssr/platform-messages.ts +135 -0
- package/src/ssr/preference-controller.ts +44 -0
- package/src/ssr/profile-preferences-messages.ts +29 -0
- package/src/ssr/rail-context.ts +21 -0
- package/src/ssr/rail-messages.ts +66 -0
- package/src/ssr/rail-navigation.ts +60 -0
- package/src/ssr/runtime.ts +31 -0
- package/src/ssr/url-filter.ts +127 -0
- package/src/styles/base-popover.css +36 -0
- package/src/styles/effects.css +851 -0
- package/src/styles/global.css +268 -0
- package/src/styles/input.css +58 -0
- package/src/styles/tokens.css +230 -0
- package/src/styles/utilities-buttons.css +405 -0
- package/src/styles/utilities-data.css +33 -0
- package/src/styles/utilities-feedback.css +232 -0
- package/src/styles/utilities-layout.css +603 -0
- package/src/styles/utilities-markdown-editor.css +525 -0
- package/src/styles/utilities-markdown-table.css +234 -0
- package/src/styles/utilities-navigation.css +1054 -0
- package/src/types/ambient.d.ts +24 -0
- package/src/workflows/ai/index.ts +17 -0
- package/src/workflows/ai/runtime.ts +286 -0
- package/src/workflows/ai/store.ts +314 -0
- package/src/workflows/ai/types.ts +111 -0
- package/src/workflows/ai-actions.ts +269 -0
- package/src/workflows/authoring.ts +88 -0
- package/src/workflows/builtins.ts +193 -0
- package/src/workflows/contracts.ts +280 -0
- package/src/workflows/definition.ts +334 -0
- package/src/workflows/editor.ts +113 -0
- package/src/workflows/index.ts +71 -0
- package/src/workflows/language/binder.ts +45 -0
- package/src/workflows/language/canonical.ts +29 -0
- package/src/workflows/language/compiler.ts +396 -0
- package/src/workflows/language/expressions.ts +34 -0
- package/src/workflows/language/index.ts +20 -0
- package/src/workflows/language/references.ts +55 -0
- package/src/workflows/language/schema.ts +134 -0
- package/src/workflows/language/strict-yaml.ts +131 -0
- package/src/workflows/manifest.ts +24 -0
- package/src/workflows/module.ts +12 -0
- package/src/workflows/runtime/coordinator.ts +115 -0
- package/src/workflows/runtime/executor.ts +1003 -0
- package/src/workflows/runtime/index.ts +60 -0
- package/src/workflows/runtime/instant.ts +8 -0
- package/src/workflows/runtime/ports.ts +213 -0
- package/src/workflows/runtime/schedule.ts +203 -0
- package/src/workflows/runtime/trigger.ts +37 -0
- package/src/workflows/runtime/wake.ts +13 -0
- package/src/workflows/store/actions.ts +438 -0
- package/src/workflows/store/budget.ts +145 -0
- package/src/workflows/store/definitions.ts +345 -0
- package/src/workflows/store/events.ts +383 -0
- package/src/workflows/store/index.ts +107 -0
- package/src/workflows/store/observability.ts +594 -0
- package/src/workflows/store/runs.ts +975 -0
- package/src/workflows/store/transaction.ts +23 -0
- package/src/workflows/store/worker.ts +439 -0
- package/src/workflows/testing/conformance.ts +72 -0
- package/src/workflows/testing/index.ts +11 -0
- package/src/workflows/testing/process-fixtures.ts +275 -0
|
@@ -0,0 +1,604 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import type { CloudCliContext, CloudCliFlagValue, CloudCliModule } from "./index";
|
|
3
|
+
|
|
4
|
+
type FlagKind = "string" | "boolean" | "int" | "enum" | "stringList" | "input";
|
|
5
|
+
type ArgKind = "required" | "optional" | "rest";
|
|
6
|
+
|
|
7
|
+
type BaseFlagOptions = {
|
|
8
|
+
name?: string;
|
|
9
|
+
aliases?: readonly string[];
|
|
10
|
+
description?: string;
|
|
11
|
+
valueLabel?: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type StringFlagSpec = BaseFlagOptions & {
|
|
15
|
+
kind: "string";
|
|
16
|
+
default?: string;
|
|
17
|
+
required?: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type BooleanFlagSpec = BaseFlagOptions & {
|
|
21
|
+
kind: "boolean";
|
|
22
|
+
default?: boolean;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type IntFlagSpec = BaseFlagOptions & {
|
|
26
|
+
kind: "int";
|
|
27
|
+
default?: number;
|
|
28
|
+
min?: number;
|
|
29
|
+
max?: number;
|
|
30
|
+
required?: boolean;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
type EnumFlagSpec<TValue extends string = string> = BaseFlagOptions & {
|
|
34
|
+
kind: "enum";
|
|
35
|
+
values: readonly TValue[];
|
|
36
|
+
default?: TValue;
|
|
37
|
+
required?: boolean;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
type StringListFlagSpec = BaseFlagOptions & {
|
|
41
|
+
kind: "stringList";
|
|
42
|
+
default?: readonly string[];
|
|
43
|
+
separator?: string;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
type InputFlagSpec = BaseFlagOptions & {
|
|
47
|
+
kind: "input";
|
|
48
|
+
fileName?: string;
|
|
49
|
+
fileAliases?: readonly string[];
|
|
50
|
+
stdinName?: string | false;
|
|
51
|
+
required?: boolean;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type CliInputFlagValue = {
|
|
55
|
+
source: "value" | "file" | "stdin" | null;
|
|
56
|
+
value?: string;
|
|
57
|
+
file?: string;
|
|
58
|
+
provided: boolean;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type CliFlagSpec = StringFlagSpec | BooleanFlagSpec | IntFlagSpec | EnumFlagSpec | StringListFlagSpec | InputFlagSpec;
|
|
62
|
+
|
|
63
|
+
type CliFlagSpecs = Record<string, CliFlagSpec>;
|
|
64
|
+
|
|
65
|
+
type RequiredArgSpec = {
|
|
66
|
+
kind: "required";
|
|
67
|
+
description?: string;
|
|
68
|
+
valueLabel?: string;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
type OptionalArgSpec = {
|
|
72
|
+
kind: "optional";
|
|
73
|
+
description?: string;
|
|
74
|
+
valueLabel?: string;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
type RestArgSpec = {
|
|
78
|
+
kind: "rest";
|
|
79
|
+
description?: string;
|
|
80
|
+
valueLabel?: string;
|
|
81
|
+
required?: boolean;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export type CliArgSpec = RequiredArgSpec | OptionalArgSpec | RestArgSpec;
|
|
85
|
+
type CliArgSpecs = Record<string, CliArgSpec>;
|
|
86
|
+
|
|
87
|
+
type InferFlag<TSpec> = TSpec extends StringFlagSpec
|
|
88
|
+
? string | undefined
|
|
89
|
+
: TSpec extends BooleanFlagSpec
|
|
90
|
+
? boolean
|
|
91
|
+
: TSpec extends IntFlagSpec
|
|
92
|
+
? number | undefined
|
|
93
|
+
: TSpec extends EnumFlagSpec<infer TValue>
|
|
94
|
+
? TValue | undefined
|
|
95
|
+
: TSpec extends StringListFlagSpec
|
|
96
|
+
? string[]
|
|
97
|
+
: TSpec extends InputFlagSpec
|
|
98
|
+
? CliInputFlagValue
|
|
99
|
+
: never;
|
|
100
|
+
|
|
101
|
+
type InferFlags<TSpecs extends CliFlagSpecs | undefined> = TSpecs extends CliFlagSpecs
|
|
102
|
+
? { [K in keyof TSpecs]: InferFlag<TSpecs[K]> }
|
|
103
|
+
: Record<string, never>;
|
|
104
|
+
|
|
105
|
+
type InferArg<TSpec> = TSpec extends RequiredArgSpec
|
|
106
|
+
? string
|
|
107
|
+
: TSpec extends OptionalArgSpec
|
|
108
|
+
? string | undefined
|
|
109
|
+
: TSpec extends RestArgSpec
|
|
110
|
+
? string[]
|
|
111
|
+
: never;
|
|
112
|
+
|
|
113
|
+
type InferArgs<TSpecs extends CliArgSpecs | undefined> = TSpecs extends CliArgSpecs
|
|
114
|
+
? { [K in keyof TSpecs]: InferArg<TSpecs[K]> }
|
|
115
|
+
: Record<string, never>;
|
|
116
|
+
|
|
117
|
+
export type CliCommandRunContext<TFlags extends CliFlagSpecs | undefined, TArgs extends CliArgSpecs | undefined> = {
|
|
118
|
+
ctx: CloudCliContext;
|
|
119
|
+
flags: InferFlags<TFlags>;
|
|
120
|
+
args: InferArgs<TArgs>;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export type CliCommandConfig<TFlags extends CliFlagSpecs | undefined = undefined, TArgs extends CliArgSpecs | undefined = undefined> = {
|
|
124
|
+
summary: string;
|
|
125
|
+
description?: string;
|
|
126
|
+
requiresCloud?: boolean;
|
|
127
|
+
args?: TArgs;
|
|
128
|
+
flags?: TFlags;
|
|
129
|
+
examples?: readonly string[];
|
|
130
|
+
run: (context: CliCommandRunContext<TFlags, TArgs>) => Promise<number | void> | number | void;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
type CliCommandDefinition = {
|
|
134
|
+
path: readonly string[];
|
|
135
|
+
summary: string;
|
|
136
|
+
description?: string;
|
|
137
|
+
requiresCloud?: boolean;
|
|
138
|
+
args?: CliArgSpecs;
|
|
139
|
+
flags?: CliFlagSpecs;
|
|
140
|
+
examples?: readonly string[];
|
|
141
|
+
run: (context: {
|
|
142
|
+
ctx: CloudCliContext;
|
|
143
|
+
flags: Record<string, unknown>;
|
|
144
|
+
args: Record<string, unknown>;
|
|
145
|
+
}) => Promise<number | void> | number | void;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
type CommandNode = {
|
|
149
|
+
segment: string;
|
|
150
|
+
children: Map<string, CommandNode>;
|
|
151
|
+
command?: CliCommandDefinition;
|
|
152
|
+
groupSummary?: string;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
type CliCommandsConfig = {
|
|
156
|
+
name: string;
|
|
157
|
+
summary: string;
|
|
158
|
+
requiresCloud?: boolean;
|
|
159
|
+
groupSummaries?: Readonly<Record<string, string>>;
|
|
160
|
+
commands: readonly CliCommandDefinition[];
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export const flag = {
|
|
164
|
+
string: (options: BaseFlagOptions & { default?: string; required?: boolean } = {}): StringFlagSpec => ({ ...options, kind: "string" }),
|
|
165
|
+
boolean: (options: BaseFlagOptions & { default?: boolean } = {}): BooleanFlagSpec => ({ ...options, kind: "boolean" }),
|
|
166
|
+
int: (options: BaseFlagOptions & { default?: number; min?: number; max?: number; required?: boolean } = {}): IntFlagSpec => ({
|
|
167
|
+
...options,
|
|
168
|
+
kind: "int",
|
|
169
|
+
}),
|
|
170
|
+
enum: <const TValue extends readonly string[]>(
|
|
171
|
+
values: TValue,
|
|
172
|
+
options: BaseFlagOptions & { default?: TValue[number]; required?: boolean } = {},
|
|
173
|
+
): EnumFlagSpec<TValue[number]> => ({ ...options, kind: "enum", values }),
|
|
174
|
+
stringList: (options: BaseFlagOptions & { default?: readonly string[]; separator?: string } = {}): StringListFlagSpec => ({
|
|
175
|
+
...options,
|
|
176
|
+
kind: "stringList",
|
|
177
|
+
}),
|
|
178
|
+
input: (
|
|
179
|
+
options: BaseFlagOptions & {
|
|
180
|
+
fileName?: string;
|
|
181
|
+
fileAliases?: readonly string[];
|
|
182
|
+
stdinName?: string | false;
|
|
183
|
+
required?: boolean;
|
|
184
|
+
} = {},
|
|
185
|
+
): InputFlagSpec => ({ ...options, kind: "input" }),
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
export const arg = {
|
|
189
|
+
required: (options: Omit<RequiredArgSpec, "kind"> = {}): RequiredArgSpec => ({ ...options, kind: "required" }),
|
|
190
|
+
optional: (options: Omit<OptionalArgSpec, "kind"> = {}): OptionalArgSpec => ({ ...options, kind: "optional" }),
|
|
191
|
+
rest: (options: Omit<RestArgSpec, "kind"> = {}): RestArgSpec => ({ ...options, kind: "rest" }),
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
export const paginationFlags = (options: { defaultPerPage?: number; maxPerPage?: number } = {}) => ({
|
|
195
|
+
page: flag.int({ default: 1, min: 1, description: "Page number" }),
|
|
196
|
+
perPage: flag.int({
|
|
197
|
+
name: "per-page",
|
|
198
|
+
aliases: ["per_page"],
|
|
199
|
+
default: options.defaultPerPage ?? 50,
|
|
200
|
+
min: 1,
|
|
201
|
+
max: options.maxPerPage ?? 200,
|
|
202
|
+
description: "Items per page",
|
|
203
|
+
}),
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
export const confirmFlag = (description = "Confirm this operation"): BooleanFlagSpec => flag.boolean({ name: "yes", description });
|
|
207
|
+
|
|
208
|
+
export const command = <TFlags extends CliFlagSpecs | undefined = undefined, TArgs extends CliArgSpecs | undefined = undefined>(
|
|
209
|
+
path: string,
|
|
210
|
+
config: CliCommandConfig<TFlags, TArgs>,
|
|
211
|
+
): CliCommandDefinition => ({
|
|
212
|
+
...config,
|
|
213
|
+
path: path.split(/\s+/).filter(Boolean),
|
|
214
|
+
run: config.run as CliCommandDefinition["run"],
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
export type ReadCliInputOptions = {
|
|
218
|
+
label?: string;
|
|
219
|
+
required?: boolean;
|
|
220
|
+
trimFinalNewline?: boolean;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export const readCliInput = async (input: CliInputFlagValue, options: ReadCliInputOptions = {}): Promise<string | undefined> => {
|
|
224
|
+
const label = options.label ?? "input";
|
|
225
|
+
if (!input.provided) {
|
|
226
|
+
if (options.required) throw new Error(`Missing ${label}.`);
|
|
227
|
+
return undefined;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const raw =
|
|
231
|
+
input.source === "value"
|
|
232
|
+
? (input.value ?? "")
|
|
233
|
+
: input.source === "file"
|
|
234
|
+
? await readFile(input.file ?? "", "utf8")
|
|
235
|
+
: input.source === "stdin"
|
|
236
|
+
? await Bun.stdin.text()
|
|
237
|
+
: "";
|
|
238
|
+
|
|
239
|
+
return options.trimFinalNewline ? raw.replace(/\r?\n$/, "") : raw;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
export const defineCliCommands = (config: CliCommandsConfig): CloudCliModule => {
|
|
243
|
+
const root = buildCommandTree(config.commands, config.groupSummaries);
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
name: config.name,
|
|
247
|
+
summary: config.summary,
|
|
248
|
+
requiresCloud: config.requiresCloud,
|
|
249
|
+
requiresCloudFor: (args) => {
|
|
250
|
+
const helpRequest = normalizeHelpRequest(args, {});
|
|
251
|
+
if (helpRequest) return false;
|
|
252
|
+
const match = findCommand(root, args);
|
|
253
|
+
return match?.command.requiresCloud ?? config.requiresCloud ?? true;
|
|
254
|
+
},
|
|
255
|
+
booleanFlags: collectBooleanFlags(config.commands),
|
|
256
|
+
help: () => renderHelp(config, root, []),
|
|
257
|
+
async run(ctx) {
|
|
258
|
+
const helpRequest = normalizeHelpRequest(ctx.args, ctx.flags);
|
|
259
|
+
if (helpRequest) {
|
|
260
|
+
ctx.print(renderHelp(config, root, helpRequest));
|
|
261
|
+
return 0;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const match = findCommand(root, ctx.args);
|
|
265
|
+
if (!match) {
|
|
266
|
+
const hintPath = ctx.args.length > 0 ? ctx.args.join(" ") : config.name;
|
|
267
|
+
throw new Error(`Unknown ${config.name} command "${hintPath}". Run \`cld ${config.name} help\`.`);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const flags = parseCommandFlags(ctx.flags, match.command.flags);
|
|
271
|
+
const args = parseCommandArgs(match.rest, match.command.args);
|
|
272
|
+
return match.command.run({ ctx, flags, args });
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
const toFlagName = (key: string): string => key.replace(/[A-Z]/g, (value) => `-${value.toLowerCase()}`);
|
|
278
|
+
|
|
279
|
+
const buildCommandTree = (
|
|
280
|
+
commands: readonly CliCommandDefinition[],
|
|
281
|
+
groupSummaries: Readonly<Record<string, string>> | undefined,
|
|
282
|
+
): CommandNode => {
|
|
283
|
+
const root: CommandNode = { segment: "", children: new Map() };
|
|
284
|
+
for (const item of commands) {
|
|
285
|
+
let node = root;
|
|
286
|
+
for (const segment of item.path) {
|
|
287
|
+
const current = node.children.get(segment) ?? { segment, children: new Map() };
|
|
288
|
+
node.children.set(segment, current);
|
|
289
|
+
node = current;
|
|
290
|
+
}
|
|
291
|
+
if (node.command) throw new Error(`Duplicate CLI command path: ${item.path.join(" ")}`);
|
|
292
|
+
node.command = item;
|
|
293
|
+
}
|
|
294
|
+
for (const [path, summary] of Object.entries(groupSummaries ?? {})) {
|
|
295
|
+
const segments = path.split(/\s+/).filter(Boolean);
|
|
296
|
+
let node: CommandNode | undefined = root;
|
|
297
|
+
for (const segment of segments) node = node?.children.get(segment);
|
|
298
|
+
if (segments.length === 0 || !node || node.children.size === 0 || node.command) {
|
|
299
|
+
throw new Error(`Unknown generated CLI command group: ${path || "<root>"}`);
|
|
300
|
+
}
|
|
301
|
+
if (!summary.trim()) throw new Error(`CLI command group "${path}" requires a summary.`);
|
|
302
|
+
node.groupSummary = summary.trim();
|
|
303
|
+
}
|
|
304
|
+
return root;
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const findNode = (root: CommandNode, path: readonly string[]): CommandNode | null => {
|
|
308
|
+
let node = root;
|
|
309
|
+
for (const segment of path) {
|
|
310
|
+
const next = node.children.get(segment);
|
|
311
|
+
if (!next) return null;
|
|
312
|
+
node = next;
|
|
313
|
+
}
|
|
314
|
+
return node;
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const findCommand = (root: CommandNode, args: readonly string[]) => {
|
|
318
|
+
let node = root;
|
|
319
|
+
let command = root.command;
|
|
320
|
+
let consumed = 0;
|
|
321
|
+
|
|
322
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
323
|
+
const next = node.children.get(args[index]!);
|
|
324
|
+
if (!next) break;
|
|
325
|
+
if (index === 0) command = undefined;
|
|
326
|
+
node = next;
|
|
327
|
+
if (node.command) {
|
|
328
|
+
command = node.command;
|
|
329
|
+
consumed = index + 1;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return command ? { command, rest: args.slice(consumed) } : null;
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
const normalizeHelpRequest = (args: readonly string[], flags: Record<string, CloudCliFlagValue>): string[] | null => {
|
|
337
|
+
if (flags.help === true || flags.h === true) return [...args];
|
|
338
|
+
const last = args.at(-1);
|
|
339
|
+
if (last === "help" || last === "--help" || last === "-h") return args.slice(0, -1);
|
|
340
|
+
return null;
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
const flagNames = (key: string, spec: CliFlagSpec): string[] => {
|
|
344
|
+
if (spec.kind === "input") {
|
|
345
|
+
return [
|
|
346
|
+
spec.name ?? toFlagName(key),
|
|
347
|
+
spec.fileName ?? `${spec.name ?? toFlagName(key)}-file`,
|
|
348
|
+
...(spec.aliases ?? []),
|
|
349
|
+
...(spec.fileAliases ?? []),
|
|
350
|
+
];
|
|
351
|
+
}
|
|
352
|
+
return [spec.name ?? toFlagName(key), ...(spec.aliases ?? [])];
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
const collectBooleanFlags = (commands: readonly CliCommandDefinition[]): readonly string[] => {
|
|
356
|
+
const booleanNames = new Set<string>(["help", "h"]);
|
|
357
|
+
const valueNames = new Set<string>();
|
|
358
|
+
for (const item of commands) {
|
|
359
|
+
for (const [key, spec] of Object.entries(item.flags ?? {})) {
|
|
360
|
+
if (spec.kind === "boolean") {
|
|
361
|
+
for (const name of flagNames(key, spec)) booleanNames.add(name);
|
|
362
|
+
} else {
|
|
363
|
+
for (const name of flagNames(key, spec)) valueNames.add(name);
|
|
364
|
+
}
|
|
365
|
+
if (spec.kind === "input" && spec.stdinName !== false) booleanNames.add(spec.stdinName ?? "stdin");
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
for (const name of valueNames) booleanNames.delete(name);
|
|
369
|
+
return [...booleanNames].sort();
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const getRawFlagValues = (raw: Record<string, CloudCliFlagValue>, names: readonly string[]): CloudCliFlagValue[] => {
|
|
373
|
+
const values: CloudCliFlagValue[] = [];
|
|
374
|
+
for (const name of names) {
|
|
375
|
+
const value = raw[name];
|
|
376
|
+
if (Array.isArray(value)) values.push(...value);
|
|
377
|
+
else if (value !== undefined) values.push(value);
|
|
378
|
+
}
|
|
379
|
+
return values;
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
const requireStringValue = (value: CloudCliFlagValue | undefined, label: string): string | undefined => {
|
|
383
|
+
if (value === undefined) return undefined;
|
|
384
|
+
if (typeof value === "string") return value;
|
|
385
|
+
throw new Error(`${label} expects a value.`);
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
const parseCommandFlags = (raw: Record<string, CloudCliFlagValue>, specs: CliFlagSpecs | undefined): Record<string, unknown> => {
|
|
389
|
+
const parsed: Record<string, unknown> = {};
|
|
390
|
+
// Global flags survive into command flags (the runner reads them without
|
|
391
|
+
// consuming them), so every global boolean must be tolerated here.
|
|
392
|
+
const allowed = new Set<string>(["json", "jsonl", "help", "h"]);
|
|
393
|
+
|
|
394
|
+
for (const [key, spec] of Object.entries(specs ?? {})) {
|
|
395
|
+
for (const name of flagNames(key, spec)) allowed.add(name);
|
|
396
|
+
if (spec.kind === "input" && spec.stdinName !== false) allowed.add(spec.stdinName ?? "stdin");
|
|
397
|
+
parsed[key] = parseFlagValue(key, spec, raw);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const unknown = Object.keys(raw).filter((name) => !allowed.has(name));
|
|
401
|
+
if (unknown.length > 0)
|
|
402
|
+
throw new Error(`Unknown flag${unknown.length === 1 ? "" : "s"}: ${unknown.map((name) => `--${name}`).join(", ")}`);
|
|
403
|
+
|
|
404
|
+
return parsed;
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
const parseFlagValue = (key: string, spec: CliFlagSpec, raw: Record<string, CloudCliFlagValue>): unknown => {
|
|
408
|
+
const label = `--${spec.name ?? toFlagName(key)}`;
|
|
409
|
+
const values = getRawFlagValues(raw, flagNames(key, spec));
|
|
410
|
+
const last = values.at(-1);
|
|
411
|
+
|
|
412
|
+
if (spec.kind === "boolean")
|
|
413
|
+
return values.length > 0 ? values.some((value) => value === true || value === "true") : (spec.default ?? false);
|
|
414
|
+
|
|
415
|
+
if (spec.kind === "string") {
|
|
416
|
+
const value = requireStringValue(last, label) ?? spec.default;
|
|
417
|
+
if (value === undefined && spec.required) throw new Error(`Missing required flag ${label}.`);
|
|
418
|
+
return value;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (spec.kind === "int") {
|
|
422
|
+
const rawValue = requireStringValue(last, label);
|
|
423
|
+
const value = rawValue === undefined ? spec.default : Number.parseInt(rawValue, 10);
|
|
424
|
+
if (value === undefined) {
|
|
425
|
+
if (spec.required) throw new Error(`Missing required flag ${label}.`);
|
|
426
|
+
return undefined;
|
|
427
|
+
}
|
|
428
|
+
if (!Number.isInteger(value)) throw new Error(`${label} must be an integer.`);
|
|
429
|
+
if (spec.min !== undefined && value < spec.min) throw new Error(`${label} must be at least ${spec.min}.`);
|
|
430
|
+
if (spec.max !== undefined && value > spec.max) throw new Error(`${label} must be at most ${spec.max}.`);
|
|
431
|
+
return value;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (spec.kind === "enum") {
|
|
435
|
+
const value = requireStringValue(last, label) ?? spec.default;
|
|
436
|
+
if (value === undefined) {
|
|
437
|
+
if (spec.required) throw new Error(`Missing required flag ${label}.`);
|
|
438
|
+
return undefined;
|
|
439
|
+
}
|
|
440
|
+
if (!spec.values.includes(value)) throw new Error(`${label} must be one of: ${spec.values.join(", ")}.`);
|
|
441
|
+
return value;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (spec.kind === "stringList") {
|
|
445
|
+
const separator = spec.separator ?? ",";
|
|
446
|
+
const items = values.flatMap((value) => {
|
|
447
|
+
if (typeof value !== "string") throw new Error(`${label} expects a value.`);
|
|
448
|
+
return value
|
|
449
|
+
.split(separator)
|
|
450
|
+
.map((item) => item.trim())
|
|
451
|
+
.filter(Boolean);
|
|
452
|
+
});
|
|
453
|
+
return items.length > 0 ? items : [...(spec.default ?? [])];
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const valueFlagNames = [spec.name ?? toFlagName(key), ...(spec.aliases ?? [])];
|
|
457
|
+
const fileFlagNames = [spec.fileName ?? `${spec.name ?? toFlagName(key)}-file`, ...(spec.fileAliases ?? [])];
|
|
458
|
+
const value = requireStringValue(getRawFlagValues(raw, valueFlagNames).at(-1), `--${valueFlagNames[0]}`);
|
|
459
|
+
const file = requireStringValue(getRawFlagValues(raw, fileFlagNames).at(-1), `--${fileFlagNames[0]}`);
|
|
460
|
+
const stdinName = spec.stdinName === false ? null : (spec.stdinName ?? "stdin");
|
|
461
|
+
const stdin = stdinName ? raw[stdinName] === true : false;
|
|
462
|
+
const sourceCount = [value !== undefined, file !== undefined, stdin].filter(Boolean).length;
|
|
463
|
+
if (sourceCount > 1)
|
|
464
|
+
throw new Error(`Pass only one of --${valueFlagNames[0]}, --${fileFlagNames[0]}${stdinName ? `, or --${stdinName}` : ""}.`);
|
|
465
|
+
if (sourceCount === 0 && spec.required) throw new Error(`Missing required input ${label}.`);
|
|
466
|
+
return {
|
|
467
|
+
source: value !== undefined ? "value" : file !== undefined ? "file" : stdin ? "stdin" : null,
|
|
468
|
+
value,
|
|
469
|
+
file,
|
|
470
|
+
provided: sourceCount > 0,
|
|
471
|
+
} satisfies CliInputFlagValue;
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
const parseCommandArgs = (rawArgs: readonly string[], specs: CliArgSpecs | undefined): Record<string, unknown> => {
|
|
475
|
+
const parsed: Record<string, unknown> = {};
|
|
476
|
+
let index = 0;
|
|
477
|
+
|
|
478
|
+
for (const [key, spec] of Object.entries(specs ?? {})) {
|
|
479
|
+
if (spec.kind === "rest") {
|
|
480
|
+
const rest = rawArgs.slice(index);
|
|
481
|
+
if (spec.required && rest.length === 0) throw new Error(`Missing ${spec.valueLabel ?? key}.`);
|
|
482
|
+
parsed[key] = rest;
|
|
483
|
+
index = rawArgs.length;
|
|
484
|
+
break;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const value = rawArgs[index];
|
|
488
|
+
if (value === undefined) {
|
|
489
|
+
if (spec.kind === "required") throw new Error(`Missing ${spec.valueLabel ?? key}.`);
|
|
490
|
+
parsed[key] = undefined;
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
parsed[key] = value;
|
|
494
|
+
index += 1;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
if (index < rawArgs.length) throw new Error(`Unexpected argument: ${rawArgs[index]}.`);
|
|
498
|
+
return parsed;
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
const renderHelp = (config: CliCommandsConfig, root: CommandNode, path: readonly string[]): string => {
|
|
502
|
+
const node = findNode(root, path);
|
|
503
|
+
if (!node) return `Unknown ${config.name} command "${path.join(" ")}".`;
|
|
504
|
+
if (node.command && node.children.size === 0) return renderCommandHelp(config, node.command);
|
|
505
|
+
return renderSubtreeHelp(config, path, node);
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
const renderSubtreeHelp = (config: CliCommandsConfig, path: readonly string[], node: CommandNode): string => {
|
|
509
|
+
const prefix = ["cld", config.name, ...path].join(" ");
|
|
510
|
+
const commands = [...node.children.values()]
|
|
511
|
+
.sort((a, b) => a.segment.localeCompare(b.segment))
|
|
512
|
+
.map((child) => {
|
|
513
|
+
const summary = child.command?.summary ?? child.groupSummary ?? "Commands";
|
|
514
|
+
return ` ${child.segment.padEnd(14)} ${summary}`.trimEnd();
|
|
515
|
+
})
|
|
516
|
+
.join("\n");
|
|
517
|
+
|
|
518
|
+
const sections = [
|
|
519
|
+
prefix,
|
|
520
|
+
"",
|
|
521
|
+
path.length === 0 ? config.summary : (node.command?.summary ?? node.groupSummary ?? "Commands"),
|
|
522
|
+
"",
|
|
523
|
+
"Usage:",
|
|
524
|
+
...(node.command ? [` ${renderUsage(config, node.command)}`] : []),
|
|
525
|
+
` ${prefix} <command> [options]`,
|
|
526
|
+
` ${prefix} help`,
|
|
527
|
+
"",
|
|
528
|
+
"Commands:",
|
|
529
|
+
commands || " (none)",
|
|
530
|
+
];
|
|
531
|
+
if (node.command) {
|
|
532
|
+
const argsHelp = renderArgsHelp(node.command.args);
|
|
533
|
+
if (argsHelp) sections.push("", "Arguments:", argsHelp);
|
|
534
|
+
const flagsHelp = renderFlagsHelp(node.command.flags);
|
|
535
|
+
if (flagsHelp) sections.push("", "Flags:", flagsHelp);
|
|
536
|
+
if (node.command.examples?.length) sections.push("", "Examples:", ...node.command.examples.map((example) => ` ${example}`));
|
|
537
|
+
if (node.command.description) sections.push("", node.command.description);
|
|
538
|
+
}
|
|
539
|
+
return sections.join("\n");
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
const firstCommand = (node: CommandNode): CliCommandDefinition | undefined => {
|
|
543
|
+
if (node.command) return node.command;
|
|
544
|
+
for (const child of node.children.values()) {
|
|
545
|
+
const found = firstCommand(child);
|
|
546
|
+
if (found) return found;
|
|
547
|
+
}
|
|
548
|
+
return undefined;
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
const renderCommandHelp = (config: CliCommandsConfig, item: CliCommandDefinition): string => {
|
|
552
|
+
const usage = renderUsage(config, item);
|
|
553
|
+
const heading = ["cld", config.name, ...item.path].join(" ");
|
|
554
|
+
const sections = [heading, "", item.summary, "", "Usage:", ` ${usage}`];
|
|
555
|
+
const argsHelp = renderArgsHelp(item.args);
|
|
556
|
+
if (argsHelp) sections.push("", "Arguments:", argsHelp);
|
|
557
|
+
const flagsHelp = renderFlagsHelp(item.flags);
|
|
558
|
+
if (flagsHelp) sections.push("", "Flags:", flagsHelp);
|
|
559
|
+
if (item.examples?.length) sections.push("", "Examples:", ...item.examples.map((example) => ` ${example}`));
|
|
560
|
+
if (item.description) sections.push("", item.description);
|
|
561
|
+
return sections.join("\n");
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
const renderUsage = (config: CliCommandsConfig, item: CliCommandDefinition): string => {
|
|
565
|
+
const args = Object.entries(item.args ?? {}).map(([key, spec]) => {
|
|
566
|
+
const label = spec.valueLabel ?? key;
|
|
567
|
+
if (spec.kind === "optional") return `[<${label}>]`;
|
|
568
|
+
if (spec.kind === "rest") return spec.required ? `<${label}...>` : `[<${label}...>]`;
|
|
569
|
+
return `<${label}>`;
|
|
570
|
+
});
|
|
571
|
+
return ["cld", config.name, ...item.path, ...args, "[options]"].join(" ");
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
const renderArgsHelp = (specs: CliArgSpecs | undefined): string => {
|
|
575
|
+
const rows = Object.entries(specs ?? {});
|
|
576
|
+
if (rows.length === 0) return "";
|
|
577
|
+
return rows.map(([key, spec]) => ` <${spec.valueLabel ?? key}>${spec.description ? ` ${spec.description}` : ""}`).join("\n");
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
const renderFlagsHelp = (specs: CliFlagSpecs | undefined): string => {
|
|
581
|
+
const rows = Object.entries(specs ?? {});
|
|
582
|
+
if (rows.length === 0) return "";
|
|
583
|
+
return rows.map(([key, spec]) => ` ${renderFlagUsage(key, spec).padEnd(32)} ${renderFlagDescription(spec)}`.trimEnd()).join("\n");
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
const renderFlagUsage = (key: string, spec: CliFlagSpec): string => {
|
|
587
|
+
const name = spec.name ?? toFlagName(key);
|
|
588
|
+
const aliases = spec.aliases?.map((alias) => `${alias.length === 1 ? "-" : "--"}${alias}`).join(", ");
|
|
589
|
+
const suffix =
|
|
590
|
+
spec.kind === "boolean"
|
|
591
|
+
? ""
|
|
592
|
+
: spec.kind === "input"
|
|
593
|
+
? ` <${spec.valueLabel ?? "value"}>|--${spec.fileName ?? `${name}-file`} <path>${spec.stdinName === false ? "" : `|--${spec.stdinName ?? "stdin"}`}`
|
|
594
|
+
: ` <${spec.valueLabel ?? "value"}>`;
|
|
595
|
+
return [`--${name}${suffix}`, aliases].filter(Boolean).join(", ");
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
const renderFlagDescription = (spec: CliFlagSpec): string => {
|
|
599
|
+
const parts = [spec.description];
|
|
600
|
+
if ("default" in spec && spec.default !== undefined)
|
|
601
|
+
parts.push(`Default: ${Array.isArray(spec.default) ? spec.default.join(",") : spec.default}.`);
|
|
602
|
+
if (spec.kind === "enum") parts.push(`Values: ${spec.values.join(", ")}.`);
|
|
603
|
+
return parts.filter(Boolean).join(" ");
|
|
604
|
+
};
|
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Hono } from "hono";
|
|
2
|
+
import type { hc } from "hono/client";
|
|
3
|
+
|
|
4
|
+
export type CloudCliOutputMode = "text" | "json" | "jsonl";
|
|
5
|
+
|
|
6
|
+
export type CloudCliFlagValue = string | boolean | string[];
|
|
7
|
+
|
|
8
|
+
export type CloudCliFlags = Record<string, CloudCliFlagValue>;
|
|
9
|
+
|
|
10
|
+
export type CloudCliOptions = {
|
|
11
|
+
profile: string;
|
|
12
|
+
server: string;
|
|
13
|
+
token: string;
|
|
14
|
+
output: CloudCliOutputMode;
|
|
15
|
+
/** Explicit request locale selected by the CLI entry point. */
|
|
16
|
+
locale?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type CloudApiClient<TApi extends Hono<any, any, any>> = ReturnType<typeof hc<TApi>>;
|
|
20
|
+
|
|
21
|
+
export type CloudCliTableColumn<TRow> = {
|
|
22
|
+
key: keyof TRow | string;
|
|
23
|
+
label?: string;
|
|
24
|
+
value?: (row: TRow) => string | number | boolean | null | undefined;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type CloudCliContext = {
|
|
28
|
+
args: string[];
|
|
29
|
+
flags: CloudCliFlags;
|
|
30
|
+
options: CloudCliOptions;
|
|
31
|
+
getDefault: (key: string) => Promise<string | undefined>;
|
|
32
|
+
setDefault: (key: string, value: string | undefined) => Promise<void>;
|
|
33
|
+
createApiClient: <TApi extends Hono<any, any, any>>(basePath: string) => CloudApiClient<TApi>;
|
|
34
|
+
fetch: (path: string, init?: RequestInit) => Promise<Response>;
|
|
35
|
+
readJson: <T>(response: Response) => Promise<T>;
|
|
36
|
+
print: (value?: string) => void;
|
|
37
|
+
/** Write a stdout chunk without adding a newline. */
|
|
38
|
+
/** Resolves once the value is flushed; await it when writing large output. */
|
|
39
|
+
write: (value: string) => Promise<void>;
|
|
40
|
+
/** Print one informational or progress line to stderr. */
|
|
41
|
+
error: (value: string) => void;
|
|
42
|
+
json: (value: unknown) => void;
|
|
43
|
+
/** Print one compact JSON value followed by a newline. */
|
|
44
|
+
jsonLine: (value: unknown) => void;
|
|
45
|
+
table: <TRow extends Record<string, unknown>>(rows: TRow[], columns: CloudCliTableColumn<TRow>[]) => void;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export type CloudCliModule = {
|
|
49
|
+
name: string;
|
|
50
|
+
summary: string;
|
|
51
|
+
booleanFlags?: readonly string[];
|
|
52
|
+
requiresCloud?: boolean;
|
|
53
|
+
requiresCloudFor?: (args: string[], flags: CloudCliFlags) => boolean;
|
|
54
|
+
help?: () => string;
|
|
55
|
+
run: (context: CloudCliContext) => Promise<number | void> | number | void;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export * from "./access";
|
|
59
|
+
export * from "./commands";
|
|
60
|
+
export * from "./locale";
|
|
61
|
+
export * from "./output";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { i18n } from "@k2b/stdlib";
|
|
2
|
+
import type { CloudCliContext } from "./index";
|
|
3
|
+
|
|
4
|
+
export type CloudCliText = { en: string; de: string };
|
|
5
|
+
|
|
6
|
+
const languageCatalog = i18n.define({
|
|
7
|
+
baseLocale: "en",
|
|
8
|
+
messages: {
|
|
9
|
+
en: { language: "en" as string },
|
|
10
|
+
de: { language: "de" },
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export const resolveCloudCliLocale = (requested?: string | null): string => {
|
|
15
|
+
const value = requested?.trim();
|
|
16
|
+
if (!value) return "en";
|
|
17
|
+
try {
|
|
18
|
+
const [canonical] = Intl.getCanonicalLocales(value);
|
|
19
|
+
return canonical ?? "en";
|
|
20
|
+
} catch {
|
|
21
|
+
throw new TypeError(`Invalid locale: ${value}`);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const cloudCliLanguage = (locale?: string | null): "en" | "de" =>
|
|
26
|
+
languageCatalog.resolve(locale ? [locale] : []).t.language === "de" ? "de" : "en";
|
|
27
|
+
|
|
28
|
+
export const localizeCloudCliText = (locale: string | null | undefined, text: CloudCliText): string => text[cloudCliLanguage(locale)];
|
|
29
|
+
|
|
30
|
+
export const cliText = (ctx: Pick<CloudCliContext, "options">, text: CloudCliText): string =>
|
|
31
|
+
localizeCloudCliText(ctx.options.locale, text);
|