@k2b/cloud 0.6.0-rc.0 → 0.7.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 +2 -2
- package/package.json +30 -17
- package/scripts/browser-performance.ts +14 -0
- package/scripts/build-pdf-renderer.ts +39 -0
- package/scripts/build.ts +15 -35
- package/scripts/preload.ts +9 -12
- package/scripts/sync-recovery-smoke.ts +2 -1
- package/src/_internal/app-presentation.ts +14 -2
- package/src/_internal/capabilities.ts +108 -7
- package/src/_internal/capability-streams.ts +81 -0
- package/src/_internal/define-app.ts +62 -36
- package/src/_internal/heartbeat.ts +4 -0
- package/src/_internal/help-catalog.ts +27 -172
- package/src/_internal/help.ts +13 -76
- package/src/_internal/page-responses.ts +2 -0
- package/src/_internal/postgres-application-name.ts +30 -0
- package/src/_internal/process-identity.ts +15 -0
- package/src/_internal/process-sync.ts +1 -1
- package/src/_internal/registry-validation.ts +29 -25
- package/src/_internal/registry.ts +2 -53
- package/src/_internal/runtime-context.ts +5 -16
- package/src/_internal/server-timing.ts +13 -0
- package/src/_internal/status-preserving-ssr.ts +18 -6
- package/src/_internal/web-vitals-asset.ts +10 -0
- package/src/access/PermissionEditor.tsx +14 -153
- package/src/access/PrincipalPicker.tsx +104 -0
- package/src/access/ui.ts +2 -0
- package/src/ai/admin.ts +14 -13
- package/src/ai/assistant-models.ts +12 -1
- package/src/ai/audio-format.ts +34 -0
- package/src/ai/audio-tool.ts +90 -0
- package/src/ai/browser-code-contracts.ts +79 -0
- package/src/ai/browser.ts +2 -0
- package/src/ai/capabilities.ts +55 -36
- package/src/ai/capability-execution.ts +47 -12
- package/src/ai/chat/blocks.tsx +157 -95
- package/src/ai/chat/capability-result.ts +15 -0
- package/src/ai/chat/capability-table.tsx +2 -10
- package/src/ai/chat/composer-adapter.ts +48 -3
- package/src/ai/chat/message-actions.tsx +8 -7
- package/src/ai/chat/message-utils.ts +12 -1
- package/src/ai/chat/messages.ts +2 -0
- package/src/ai/chat/presentation.tsx +19 -48
- package/src/ai/chat/primitives.tsx +43 -1
- package/src/ai/chat/tool-disclosure.tsx +17 -2
- package/src/ai/chat/tool-groups.ts +52 -0
- package/src/ai/chat-quotas.ts +25 -0
- package/src/ai/chat-task-contracts.ts +9 -1
- package/src/ai/chat-tasks.ts +276 -43
- package/src/ai/client/controller.ts +110 -33
- package/src/ai/client/file-source.ts +12 -6
- package/src/ai/client/live-connection.ts +1 -0
- package/src/ai/client/projection.ts +22 -11
- package/src/ai/client/transport.ts +4 -0
- package/src/ai/code-capability-routes.ts +110 -0
- package/src/ai/code-capability-transport.ts +38 -0
- package/src/ai/code-mode-skill.ts +114 -0
- package/src/ai/code-runtime-tools.ts +166 -0
- package/src/ai/code-source-contracts.ts +202 -0
- package/src/ai/code-source-tools.ts +145 -0
- package/src/ai/compaction.ts +27 -6
- package/src/ai/data-analysis-skill.ts +10 -0
- package/src/ai/default-tools.ts +25 -2
- package/src/ai/dictation-runtime.ts +171 -0
- package/src/ai/dictations.ts +188 -0
- package/src/ai/draft-content.ts +34 -0
- package/src/ai/enrich.ts +6 -0
- package/src/ai/executor.ts +191 -31
- package/src/ai/file-content-version.ts +22 -0
- package/src/ai/file-context.ts +2 -1
- package/src/ai/file-media-type.ts +12 -0
- package/src/ai/file-reference-contracts.ts +15 -0
- package/src/ai/file-tools.ts +2 -1
- package/src/ai/files-store.ts +174 -71
- package/src/ai/fixtures/vision-encrypted.pdf +0 -0
- package/src/ai/grids-skill.ts +40 -10
- package/src/ai/http.ts +15 -8
- package/src/ai/index.ts +26 -2
- package/src/ai/inference-calls.ts +198 -0
- package/src/ai/live-events.ts +5 -1
- package/src/ai/memory-learning.ts +60 -28
- package/src/ai/message-queue.ts +167 -0
- package/src/ai/migrate.ts +187 -11
- package/src/ai/model-pricing.ts +43 -0
- package/src/ai/pdf-render-worker.ts +74 -0
- package/src/ai/pdf-render.fixture.ts +28 -0
- package/src/ai/pdf-render.ts +77 -0
- package/src/ai/projects-routes.ts +3 -3
- package/src/ai/projects.ts +20 -6
- package/src/ai/protocol.ts +3 -0
- package/src/ai/provider.ts +1 -8
- package/src/ai/quota-provider.ts +191 -0
- package/src/ai/quota-report.ts +152 -0
- package/src/ai/quotas-migrate.ts +45 -0
- package/src/ai/quotas.ts +168 -0
- package/src/ai/routes.ts +215 -11
- package/src/ai/run-timeout.ts +10 -0
- package/src/ai/runtime-tools.ts +3 -0
- package/src/ai/runtime.ts +55 -16
- package/src/ai/settings.ts +36 -8
- package/src/ai/short-id.ts +2 -2
- package/src/ai/skill-catalog.ts +0 -10
- package/src/ai/skill-search.ts +47 -0
- package/src/ai/skill-seeds.ts +105 -20
- package/src/ai/skill-tool.ts +16 -5
- package/src/ai/skills-routes.ts +32 -4
- package/src/ai/skills.ts +324 -155
- package/src/ai/store.ts +373 -191
- package/src/ai/stream.ts +5 -0
- package/src/ai/structured-runs.ts +4 -3
- package/src/ai/structured.ts +8 -1
- package/src/ai/system-prompt.ts +12 -2
- package/src/ai/timeline.ts +2 -5
- package/src/ai/todo-contracts.ts +20 -0
- package/src/ai/todo-tool.ts +16 -0
- package/src/ai/tool-audit.ts +30 -40
- package/src/ai/tools.ts +6 -0
- package/src/ai/transcription.ts +202 -0
- package/src/ai/turn-timing.ts +114 -0
- package/src/ai/types.ts +58 -10
- package/src/ai/ui.tsx +2 -0
- package/src/ai/usage.ts +118 -118
- package/src/ai/vision-tool.ts +39 -10
- package/src/api/admin-ai-projects.ts +2 -2
- package/src/api/admin-ai-quotas.ts +74 -0
- package/src/api/admin-ai-skills.ts +48 -5
- package/src/api/admin-ai-usage.ts +2 -2
- package/src/api/admin-core-settings.ts +25 -0
- package/src/api/admin-identity.ts +91 -51
- package/src/api/admin-rail.ts +58 -0
- package/src/api/announcements.ts +14 -0
- package/src/api/capabilities.ts +292 -43
- package/src/api/capability-streams.ts +262 -0
- package/src/api/help.ts +8 -44
- package/src/api/index.ts +6 -0
- package/src/api/mcp.ts +44 -26
- package/src/api/me-web-vitals.ts +35 -0
- package/src/api/me.ts +2 -0
- package/src/api/search/schemas.ts +26 -15
- package/src/api/search.bench.ts +1 -1
- package/src/api/search.ts +54 -17
- package/src/browser/CloudResourceSearch.tsx +640 -702
- package/src/browser/app-approval-vault.ts +19 -7
- package/src/browser/command-bridge.ts +109 -0
- package/src/browser/command-shortcuts.ts +76 -0
- package/src/browser/commands.ts +66 -0
- package/src/browser/navigation-search.ts +48 -0
- package/src/browser/notifications.ts +1 -1
- package/src/browser/resource-picker.tsx +5 -18
- package/src/browser/resource-search-dialog.ts +9 -0
- package/src/browser/resource-search-input.ts +46 -0
- package/src/browser/resource-search-messages.ts +76 -40
- package/src/browser/resource-search.ts +7 -0
- package/src/browser/search-bridge.ts +102 -0
- package/src/browser/search-commands.ts +59 -0
- package/src/browser/search.ts +13 -0
- package/src/browser/spotlight-position.ts +208 -0
- package/src/browser/testing.ts +5 -0
- package/src/browser/web-vitals.ts +25 -0
- package/src/capabilities/claims.ts +179 -0
- package/src/capabilities/client.ts +34 -0
- package/src/capabilities/command-link.ts +16 -0
- package/src/capabilities/executions.ts +298 -0
- package/src/capabilities/migrate.ts +81 -0
- package/src/capabilities/server.ts +114 -34
- package/src/capabilities/stream-body.ts +16 -0
- package/src/capabilities/streams.ts +28 -0
- package/src/cli/admin/ai-quotas.ts +161 -0
- package/src/cli/admin/ai-skills.ts +64 -0
- package/src/cli/admin/ai-usage.ts +53 -30
- package/src/cli/admin/app-credentials.ts +79 -0
- package/src/cli/admin/data.ts +17 -0
- package/src/cli/admin/index.ts +9 -0
- package/src/cli/capabilities.ts +29 -0
- package/src/cli/capability-streams.ts +25 -0
- package/src/cli/index.ts +3 -2
- package/src/config/env.ts +11 -8
- package/src/contracts/app.ts +23 -3
- package/src/contracts/capabilities.ts +68 -5
- package/src/contracts/capability-streams.ts +46 -0
- package/src/contracts/commands.ts +49 -0
- package/src/contracts/index.ts +6 -1
- package/src/contracts/rail-admin.ts +38 -0
- package/src/contracts/rail-preferences.ts +16 -0
- package/src/contracts/registry.ts +3 -27
- package/src/contracts/settings-types.ts +1 -1
- package/src/contracts/shared.ts +9 -5
- package/src/contracts/web-vitals.ts +27 -0
- package/src/desktop/solid.tsx +3 -3
- package/src/index.ts +0 -4
- package/src/server/index.ts +3 -0
- package/src/server/middleware/auth.ts +3 -2
- package/src/server/middleware/route-template.ts +9 -3
- package/src/server/middleware/runtime.ts +2 -1
- package/src/server/middleware/settings.ts +17 -11
- package/src/server/services/access-revision.ts +10 -0
- package/src/server/services/access.ts +1 -1
- package/src/server/services/index.ts +1 -0
- package/src/services/account-category-policy.ts +9 -0
- package/src/services/account-lifecycle/audit.ts +1 -1
- package/src/services/accounts/app.ts +12 -2
- package/src/services/accounts/email-write.ts +23 -0
- package/src/services/accounts/groups.ts +15 -4
- package/src/services/accounts/identities.ts +249 -0
- package/src/services/accounts/identity-reconciliation.ts +87 -0
- package/src/services/accounts/ipa-data.ts +2 -2
- package/src/services/accounts/local-groups.ts +2 -2
- package/src/services/accounts/posix.ts +45 -29
- package/src/services/accounts/users.ts +2 -2
- package/src/services/announcements/index.ts +69 -8
- package/src/services/audit/index.ts +1 -1
- package/src/services/cache-fill.ts +35 -0
- package/src/services/help/index.ts +109 -0
- package/src/services/help/maintenance.ts +25 -0
- package/src/services/help/store.ts +180 -0
- package/src/services/help/types.ts +46 -0
- package/src/services/identity/invocation-actor.ts +2 -2
- package/src/services/identity/key-config.ts +2 -1
- package/src/services/identity/key-ring.ts +1 -1
- package/src/services/index.ts +15 -0
- package/src/services/ipa/users.ts +268 -221
- package/src/services/legal-consent.ts +1 -1
- package/src/services/logging/index.ts +15 -13
- package/src/services/logging/trace.ts +2 -1
- package/src/services/logging/web-vitals.ts +81 -0
- package/src/services/mandates/index.ts +18 -4
- package/src/services/mandates/policy.ts +65 -2
- package/src/services/notifications/batches.ts +124 -57
- package/src/services/notifications/browser.ts +12 -0
- package/src/services/notifications/index.ts +0 -2
- package/src/services/notifications/platform.ts +0 -4
- package/src/services/oauth-tokens.ts +3 -3
- package/src/services/pdf/gotenberg.ts +107 -84
- package/src/services/pdf/index.ts +3 -0
- package/src/services/providers/local/users.ts +25 -16
- package/src/services/public-http.ts +101 -0
- package/src/services/rail-shortcuts.ts +95 -0
- package/src/services/rail-snapshot.ts +53 -0
- package/src/services/request-cache-redis.ts +28 -0
- package/src/services/session/index.ts +15 -2
- package/src/services/session/user.ts +62 -37
- package/src/services/settings/core-settings.ts +21 -28
- package/src/services/settings/defaults.ts +5 -0
- package/src/services/settings/store.ts +58 -84
- package/src/shared/ai-costs.ts +42 -0
- package/src/shared/ai-platform-prompt.ts +1 -1
- package/src/shared/ai-quotas.ts +136 -0
- package/src/shared/ai-usage.ts +6 -16
- package/src/shared/app-presentation.ts +21 -15
- package/src/shared/capability-messages.ts +14 -0
- package/src/shared/help.ts +6 -42
- package/src/shared/index.ts +6 -0
- package/src/ssr/AdminLayout.tsx +7 -5
- package/src/ssr/AdminSidebar.tsx +29 -15
- package/src/ssr/AppLaunchpad.island.tsx +24 -137
- package/src/ssr/AppLaunchpadPanel.tsx +181 -0
- package/src/ssr/BrowserPushRegistration.island.tsx +12 -0
- package/src/ssr/GlobalSearchDialog.tsx +167 -20
- package/src/ssr/GlobalSearchTrigger.island.tsx +33 -22
- package/src/ssr/Layout.tsx +17 -6
- package/src/ssr/LayoutBreadcrumbs.island.tsx +3 -5
- package/src/ssr/LayoutFooter.tsx +1 -1
- package/src/ssr/LayoutHeader.tsx +46 -14
- package/src/ssr/LayoutHelp.tsx +23 -22
- package/src/ssr/{HotkeysHelpRail.island.tsx → LayoutHelpTrigger.island.tsx} +18 -12
- package/src/ssr/LayoutRail.tsx +17 -9
- package/src/ssr/MobileNavigation.tsx +71 -0
- package/src/ssr/MobileProfileActions.tsx +49 -0
- package/src/ssr/ProfilePreferences.island.tsx +3 -36
- package/src/ssr/RailApps.island.tsx +2 -2
- package/src/ssr/RailEditor.tsx +17 -2
- package/src/ssr/WorkspaceNavigation.island.tsx +7 -0
- package/src/ssr/WorkspaceNavigationProvider.tsx +16 -0
- package/src/ssr/admin-navigation.ts +7 -1
- package/src/ssr/help.ts +29 -0
- package/src/ssr/islands/SearchBar.tsx +67 -0
- package/src/ssr/islands/index.ts +3 -1
- package/src/ssr/layout-context.ts +2 -2
- package/src/ssr/layout-help-search.ts +15 -0
- package/src/ssr/mobile-menu-history.ts +70 -0
- package/src/ssr/platform-messages.ts +16 -8
- package/src/ssr/profile-actions.ts +42 -0
- package/src/ssr/rail-context.ts +8 -4
- package/src/ssr/rail-messages.ts +2 -0
- package/src/ssr/rail-navigation.ts +3 -3
- package/src/ssr/workspace-navigation.ts +75 -0
- package/src/styles/effects.css +6 -6
- package/src/styles/global.css +54 -0
- package/src/styles/input.css +43 -41
- package/src/styles/resource-search.css +505 -0
- package/src/styles/utilities-feedback.css +13 -0
- package/src/styles/utilities-layout.css +1 -5
- package/src/styles/utilities-navigation.css +3 -39
- package/src/types/ambient.d.ts +5 -0
- package/src/workflows/ai/runtime.ts +5 -1
- package/src/workflows/ai/store.ts +2 -0
- package/src/workflows/ai-actions.ts +16 -13
- package/src/workflows/definition.ts +10 -5
- package/src/workflows/store/actions.ts +44 -20
- package/src/workflows/store/index.ts +2 -0
- package/src/workflows/store/runs.ts +36 -0
- package/src/workflows/store/worker-pool.ts +55 -0
- package/src/workflows/store/worker-runtime.ts +90 -0
- package/scripts/app-favicon.test.ts +0 -49
- package/src/ai/chat/blocks.render.test.tsx +0 -1108
- package/src/ai/kit-skill.ts +0 -22
- package/src/ai/usage-migrate.ts +0 -81
- package/src/browser/CloudResourceSearch.behavior.test.tsx +0 -74
- package/src/browser/notification-target.ts +0 -27
- package/src/contracts/notification-live.ts +0 -66
- package/src/services/notifications/live.ts +0 -50
- package/src/ssr/BrowserNotifications.island.tsx +0 -135
- package/src/ssr/GlobalSearchDialog.behavior.test.tsx +0 -96
- package/src/ssr/Layout.render.test.tsx +0 -148
- package/src/ssr/islands/SearchBar.island.tsx +0 -80
package/src/config/env.ts
CHANGED
|
@@ -7,18 +7,20 @@ const str = (key: string, fallback: string = ""): string => process.env[key] ??
|
|
|
7
7
|
|
|
8
8
|
const optional = (key: string): string | undefined => process.env[key]?.trim() || undefined;
|
|
9
9
|
|
|
10
|
-
const int = (key: string, fallback: number): number => {
|
|
11
|
-
const value = process.env[key];
|
|
12
|
-
const parsed = parseInt(value ?? "", 10);
|
|
13
|
-
return isNaN(parsed) ? fallback : parsed;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
10
|
const bool = (key: string, fallback: boolean): boolean => {
|
|
17
11
|
const value = process.env[key]?.trim().toLowerCase();
|
|
18
12
|
if (!value) return fallback;
|
|
19
13
|
return value === "true" || value === "1";
|
|
20
14
|
};
|
|
21
15
|
|
|
16
|
+
const syncReplicas = (): 1 | 3 | 5 => {
|
|
17
|
+
const value = str("SYNC_REPLICAS", "3").trim();
|
|
18
|
+
if (value === "1") return 1;
|
|
19
|
+
if (value === "3") return 3;
|
|
20
|
+
if (value === "5") return 5;
|
|
21
|
+
throw new Error("SYNC_REPLICAS must be 1, 3, or 5");
|
|
22
|
+
};
|
|
23
|
+
|
|
22
24
|
const list = (key: string): string[] =>
|
|
23
25
|
(process.env[key] ?? "")
|
|
24
26
|
.split(",")
|
|
@@ -28,10 +30,9 @@ const list = (key: string): string[] =>
|
|
|
28
30
|
export const env = {
|
|
29
31
|
// Application (infrastructure — not changeable at runtime)
|
|
30
32
|
APP_SECRET: str("APP_SECRET"),
|
|
31
|
-
PORT: int("PORT", 3000),
|
|
32
33
|
IS_DEVELOPMENT: process.env.NODE_ENV === "development",
|
|
33
34
|
|
|
34
|
-
// Admin login (
|
|
35
|
+
// Admin login (temporary first access or recovery — Core only, no IPA required)
|
|
35
36
|
ADMIN_LOGIN_TOKEN: str("ADMIN_LOGIN_TOKEN"),
|
|
36
37
|
|
|
37
38
|
// NATS / @k2b/sync (Postgres and Redis are read by Bun from DATABASE_URL / REDIS_URL)
|
|
@@ -39,6 +40,8 @@ export const env = {
|
|
|
39
40
|
NATS_SERVERS: list("NATS_SERVERS"),
|
|
40
41
|
/** Sync namespace shared by every app of one Cloud installation (e.g. `dev`, `prod`). Required. */
|
|
41
42
|
SYNC_NAMESPACE: str("SYNC_NAMESPACE"),
|
|
43
|
+
/** Persistent stream replica count; consistent across the installation. */
|
|
44
|
+
SYNC_REPLICAS: syncReplicas(),
|
|
42
45
|
/** Path to a mounted NATS `.creds` file (JWT + NKey). Unset = server-side no-auth. */
|
|
43
46
|
NATS_CREDS_FILE: optional("NATS_CREDS_FILE"),
|
|
44
47
|
/** Path to a mounted CA certificate; enables TLS with that CA. Unset = plain TCP. */
|
package/src/contracts/app.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Sync } from "@k2b/sync";
|
|
2
|
-
import type {
|
|
2
|
+
import type { AppRegistryHelpSummary } from "./registry";
|
|
3
3
|
import type { Role } from "./shared";
|
|
4
4
|
import type { DashboardWidgetPresentation } from "./widgets";
|
|
5
5
|
|
|
@@ -14,6 +14,18 @@ export type LegalLink = {
|
|
|
14
14
|
icon?: string;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
/** Static page links for the global search UI, ranked after resource results.
|
|
18
|
+
* These declare navigation only; they do not create capabilities or readers.
|
|
19
|
+
*/
|
|
20
|
+
export type AppSearchLink = {
|
|
21
|
+
label: string;
|
|
22
|
+
/** Optional plain-text context shown with the result and its preview. */
|
|
23
|
+
description?: string;
|
|
24
|
+
href: string;
|
|
25
|
+
icon?: string;
|
|
26
|
+
keywords?: readonly string[];
|
|
27
|
+
};
|
|
28
|
+
|
|
17
29
|
export type AppAppearanceColor = `#${string}`;
|
|
18
30
|
|
|
19
31
|
export type AppAppearance = {
|
|
@@ -35,6 +47,8 @@ export type AppAdminNavigationLink = {
|
|
|
35
47
|
};
|
|
36
48
|
|
|
37
49
|
export type AppAdminNavigationGroup = {
|
|
50
|
+
/** Contribute links to the platform AI section instead of a separate group. */
|
|
51
|
+
section?: "ai";
|
|
38
52
|
/** Stable key used only to localize this presentation group. */
|
|
39
53
|
id?: string;
|
|
40
54
|
label: string;
|
|
@@ -50,10 +64,14 @@ export type AppPresentationTranslation = {
|
|
|
50
64
|
adminLinks?: Readonly<Record<string, string>>;
|
|
51
65
|
/** Legal-link labels keyed by their stable same-origin href. */
|
|
52
66
|
legalLinks?: Readonly<Record<string, string>>;
|
|
67
|
+
/** Search-link labels keyed by their stable same-origin href. */
|
|
68
|
+
searchLinks?: Readonly<Record<string, string>>;
|
|
69
|
+
/** Search-link descriptions keyed by their stable same-origin href. */
|
|
70
|
+
searchLinkDescriptions?: Readonly<Record<string, string>>;
|
|
53
71
|
};
|
|
54
72
|
|
|
55
73
|
export type AppPresentationCatalog = {
|
|
56
|
-
/** Locale of the complete `name`, `description`, `adminNav`, and `
|
|
74
|
+
/** Locale of the complete `name`, `description`, `adminNav`, `legalLinks`, and `searchLinks` declaration. */
|
|
57
75
|
baseLocale: string;
|
|
58
76
|
/** Partial presentation overlays with exact -> ancestor -> base fallback. */
|
|
59
77
|
translations: Readonly<Record<string, AppPresentationTranslation>>;
|
|
@@ -87,6 +105,8 @@ export type AppMeta = {
|
|
|
87
105
|
* contributes its own (e.g. settings → terms/privacy/imprint, faq → FAQ).
|
|
88
106
|
*/
|
|
89
107
|
legalLinks?: LegalLink[];
|
|
108
|
+
/** Static search destinations, visible when the app is in the user's navigation catalog. */
|
|
109
|
+
searchLinks?: readonly AppSearchLink[];
|
|
90
110
|
/**
|
|
91
111
|
* Dashboard widget endpoints this app exposes. Each entry references an
|
|
92
112
|
* HTTP endpoint that returns a `WidgetResponse` (see `contracts/widgets.ts`).
|
|
@@ -111,7 +131,7 @@ export type WidgetEndpoint = {
|
|
|
111
131
|
};
|
|
112
132
|
|
|
113
133
|
export type RuntimeAppMeta = AppMeta & {
|
|
114
|
-
help?:
|
|
134
|
+
help?: AppRegistryHelpSummary;
|
|
115
135
|
searchTags?: string[];
|
|
116
136
|
searchHelp?: string;
|
|
117
137
|
searchTagHelp?: AppSearchTagHelpEntry[];
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { CapabilityStreamSchema, CapabilityStreamPolicySchema, type CapabilityStream, type CapabilityStreamDefinition } from "./capability-streams";
|
|
2
|
+
export * from "./capability-streams";
|
|
1
3
|
import { z } from "zod";
|
|
4
|
+
import { CommandPathSchema } from "./commands";
|
|
2
5
|
import type { AccessSubject, RequestActor, User } from "./shared";
|
|
3
6
|
|
|
4
|
-
export const CAPABILITY_PROTOCOL_VERSION =
|
|
7
|
+
export const CAPABILITY_PROTOCOL_VERSION = 2 as const;
|
|
5
8
|
export const CAPABILITY_MAX_REQUEST_BYTES = 256 * 1024;
|
|
6
9
|
export const CAPABILITY_MAX_RESULT_BYTES = 256 * 1024;
|
|
7
10
|
export const CAPABILITY_MAX_CATALOG_BYTES = 2 * 1024 * 1024;
|
|
@@ -14,6 +17,8 @@ export const CAPABILITY_FRAMEWORK_ERROR_CODES = {
|
|
|
14
17
|
idempotencyKeyRequired: "IDEMPOTENCY_KEY_REQUIRED",
|
|
15
18
|
idempotencyKeyNotAllowed: "IDEMPOTENCY_KEY_NOT_ALLOWED",
|
|
16
19
|
idempotencyConflict: "IDEMPOTENCY_CONFLICT",
|
|
20
|
+
idempotencyInProgress: "IDEMPOTENCY_IN_PROGRESS",
|
|
21
|
+
idempotencyUncertain: "IDEMPOTENCY_UNCERTAIN",
|
|
17
22
|
deadlineExceeded: "DEADLINE_EXCEEDED",
|
|
18
23
|
actionOutcomeUnknown: "ACTION_OUTCOME_UNKNOWN",
|
|
19
24
|
requestCancelled: "REQUEST_CANCELLED",
|
|
@@ -32,6 +37,17 @@ export const capabilityIdempotencyConflict = (
|
|
|
32
37
|
...(details ? { details } : {}),
|
|
33
38
|
});
|
|
34
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Cloud surface a capability invocation came from. The dispatcher resolves it
|
|
42
|
+
* once, records it on the execution row, and forwards it to the owning app.
|
|
43
|
+
*/
|
|
44
|
+
export const CAPABILITY_ORIGINS = ["assistant", "mcp", "http", "app"] as const;
|
|
45
|
+
export type CapabilityOrigin = (typeof CAPABILITY_ORIGINS)[number];
|
|
46
|
+
export const CapabilityOriginSchema = z.enum(CAPABILITY_ORIGINS);
|
|
47
|
+
|
|
48
|
+
/** Transports the resolved origin from the Cloud dispatcher to the app endpoint. */
|
|
49
|
+
export const CAPABILITY_ORIGIN_HEADER = "x-cloud-capability-origin";
|
|
50
|
+
|
|
35
51
|
export const CapabilityIdempotencyKeySchema = z
|
|
36
52
|
.string()
|
|
37
53
|
.trim()
|
|
@@ -94,10 +110,13 @@ const CapabilityActionReviewDetailSchema = z
|
|
|
94
110
|
label: z.string().min(1).max(120),
|
|
95
111
|
value: z.string().max(10_000),
|
|
96
112
|
display: z.enum(["inline", "block"]).optional(),
|
|
97
|
-
format: z.enum(["date", "date-time"]).optional(),
|
|
113
|
+
format: z.enum(["date", "date-time", "markdown"]).optional(),
|
|
98
114
|
})
|
|
99
115
|
.strict()
|
|
100
116
|
.superRefine((detail, context) => {
|
|
117
|
+
if (detail.format === "markdown" && detail.display !== "block") {
|
|
118
|
+
context.addIssue({ code: "custom", path: ["display"], message: "Markdown review details require block display" });
|
|
119
|
+
}
|
|
101
120
|
const schema =
|
|
102
121
|
detail.format === "date" ? CapabilityReviewDateSchema : detail.format === "date-time" ? CapabilityReviewDateTimeSchema : null;
|
|
103
122
|
if (schema?.safeParse(detail.value).success === false) {
|
|
@@ -175,6 +194,7 @@ export const capabilityDataAtPath = (data: unknown, path: readonly string[]): un
|
|
|
175
194
|
|
|
176
195
|
export type CapabilityResult<T> = {
|
|
177
196
|
data: T;
|
|
197
|
+
stream?: CapabilityStream;
|
|
178
198
|
/** Provider-authored, user-facing summary of the successful result. Render as escaped plain text. */
|
|
179
199
|
summary?: string;
|
|
180
200
|
refs?: CloudResourceReference[];
|
|
@@ -190,6 +210,7 @@ export const capabilityResultSchema = <T extends z.ZodType>(
|
|
|
190
210
|
z
|
|
191
211
|
.object({
|
|
192
212
|
data,
|
|
213
|
+
stream: CapabilityStreamSchema.optional(),
|
|
193
214
|
summary: z.string().trim().min(1).max(500).optional(),
|
|
194
215
|
refs: z.array(CloudResourceReferenceSchema).max(100).optional(),
|
|
195
216
|
page: CapabilityPageSchema.optional(),
|
|
@@ -264,6 +285,9 @@ export const UniversalSearchInputSchema = z
|
|
|
264
285
|
.object({
|
|
265
286
|
query: z.string().max(500).describe("User-entered search text. Empty text is allowed when a facet narrows the query."),
|
|
266
287
|
tags: z.array(z.string().min(1).max(64)).max(20).describe("Canonical search facets supported by this query."),
|
|
288
|
+
scope: CloudResourceRefSchema.optional().describe(
|
|
289
|
+
"Optional resource containing the search results; must be an explicitly supported scope type.",
|
|
290
|
+
),
|
|
267
291
|
limit: z.number().int().min(1).max(100).describe("Maximum number of results to return."),
|
|
268
292
|
})
|
|
269
293
|
.strict();
|
|
@@ -285,6 +309,14 @@ export type CapabilityExecutionContext = {
|
|
|
285
309
|
* error codes and data must not depend on it.
|
|
286
310
|
*/
|
|
287
311
|
locale: string;
|
|
312
|
+
/**
|
|
313
|
+
* Correlation id of the originating Cloud request. Write it into the app's
|
|
314
|
+
* own audit rows so a capability execution record and the app's domain trail
|
|
315
|
+
* can be joined without sharing payloads.
|
|
316
|
+
*/
|
|
317
|
+
requestId: string;
|
|
318
|
+
/** Cloud surface that invoked this capability. */
|
|
319
|
+
origin: CapabilityOrigin;
|
|
288
320
|
signal: AbortSignal;
|
|
289
321
|
};
|
|
290
322
|
|
|
@@ -304,6 +336,8 @@ export type CapabilitySearchTagDefinition = {
|
|
|
304
336
|
|
|
305
337
|
export type CapabilityUniversalSearchDefinition = {
|
|
306
338
|
tags: readonly CapabilitySearchTagDefinition[];
|
|
339
|
+
/** Local resource types whose contents this Query can search. */
|
|
340
|
+
scopeTypes?: readonly string[];
|
|
307
341
|
};
|
|
308
342
|
|
|
309
343
|
export type CapabilitySchemaPresentation = Readonly<Record<string, string>>;
|
|
@@ -323,6 +357,7 @@ export type CapabilityPresentationTranslation = {
|
|
|
323
357
|
types?: Readonly<Record<string, { title?: string; description?: string }>>;
|
|
324
358
|
queries?: Readonly<Record<string, CapabilityOperationPresentationTranslation>>;
|
|
325
359
|
actions?: Readonly<Record<string, CapabilityOperationPresentationTranslation>>;
|
|
360
|
+
commands?: Readonly<Record<string, Pick<CapabilityOperationPresentationTranslation, "title" | "description" | "input">>>;
|
|
326
361
|
};
|
|
327
362
|
|
|
328
363
|
export type CapabilityPresentationCatalog = {
|
|
@@ -338,6 +373,7 @@ export type CapabilityQueryDefinition<Input extends z.ZodType = z.ZodType<any>,
|
|
|
338
373
|
input: Input;
|
|
339
374
|
data: Data;
|
|
340
375
|
openWorld: boolean;
|
|
376
|
+
stream?: CapabilityStreamDefinition;
|
|
341
377
|
universalSearch?: CapabilityUniversalSearchDefinition;
|
|
342
378
|
run: (
|
|
343
379
|
input: z.output<Input>,
|
|
@@ -346,7 +382,7 @@ export type CapabilityQueryDefinition<Input extends z.ZodType = z.ZodType<any>,
|
|
|
346
382
|
};
|
|
347
383
|
|
|
348
384
|
export type CapabilityIdempotencyPolicy = "none" | "required";
|
|
349
|
-
export type CapabilityActionApprovalPolicy = "rememberable";
|
|
385
|
+
export type CapabilityActionApprovalPolicy = "rememberable" | "none";
|
|
350
386
|
|
|
351
387
|
export type CapabilityActionDefinition<Input extends z.ZodType = z.ZodType<any>, Data extends z.ZodType = z.ZodType<any>> = {
|
|
352
388
|
title: string;
|
|
@@ -355,6 +391,7 @@ export type CapabilityActionDefinition<Input extends z.ZodType = z.ZodType<any>,
|
|
|
355
391
|
data: Data;
|
|
356
392
|
destructive: boolean;
|
|
357
393
|
openWorld: boolean;
|
|
394
|
+
stream?: CapabilityStreamDefinition;
|
|
358
395
|
idempotency: CapabilityIdempotencyPolicy;
|
|
359
396
|
approval?: CapabilityActionApprovalPolicy;
|
|
360
397
|
review?: (
|
|
@@ -367,6 +404,29 @@ export type CapabilityActionDefinition<Input extends z.ZodType = z.ZodType<any>,
|
|
|
367
404
|
) => CapabilityInvocationResult<z.output<Data>> | Promise<CapabilityInvocationResult<z.output<Data>>>;
|
|
368
405
|
};
|
|
369
406
|
|
|
407
|
+
/** A declarative UI entry. Resolution never executes application code or writes domain data. */
|
|
408
|
+
export type CapabilityCommandDefinition = {
|
|
409
|
+
title: string;
|
|
410
|
+
description: string;
|
|
411
|
+
icon?: string;
|
|
412
|
+
keywords?: readonly string[];
|
|
413
|
+
input: z.ZodType;
|
|
414
|
+
path: string;
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
export const CapabilityCommandManifestSchema = z
|
|
418
|
+
.object({
|
|
419
|
+
localId: CapabilityLocalIdSchema,
|
|
420
|
+
title: z.string().min(1).max(120),
|
|
421
|
+
description: z.string().min(1).max(1000),
|
|
422
|
+
icon: z.string().min(1).max(120).optional(),
|
|
423
|
+
keywords: z.array(z.string().min(1).max(120)).max(30),
|
|
424
|
+
inputSchema: z.record(z.string(), z.unknown()),
|
|
425
|
+
path: CommandPathSchema,
|
|
426
|
+
})
|
|
427
|
+
.strict();
|
|
428
|
+
export type CapabilityCommandManifest = z.infer<typeof CapabilityCommandManifestSchema>;
|
|
429
|
+
|
|
370
430
|
type CapabilityDefinitionCatalog<T> = Readonly<Record<string, T>>;
|
|
371
431
|
|
|
372
432
|
export type CapabilityDefinitions = {
|
|
@@ -375,6 +435,7 @@ export type CapabilityDefinitions = {
|
|
|
375
435
|
types?: CapabilityDefinitionCatalog<CapabilityResourceTypeDefinition>;
|
|
376
436
|
queries?: CapabilityDefinitionCatalog<CapabilityQueryDefinition>;
|
|
377
437
|
actions?: CapabilityDefinitionCatalog<CapabilityActionDefinition>;
|
|
438
|
+
commands?: CapabilityDefinitionCatalog<CapabilityCommandDefinition>;
|
|
378
439
|
};
|
|
379
440
|
|
|
380
441
|
/**
|
|
@@ -409,6 +470,7 @@ const CapabilityOperationManifestBaseSchema = z
|
|
|
409
470
|
description: z.string().min(1).max(1000),
|
|
410
471
|
inputSchema: z.record(z.string(), z.unknown()),
|
|
411
472
|
dataSchema: z.record(z.string(), z.unknown()),
|
|
473
|
+
stream: CapabilityStreamPolicySchema.optional(),
|
|
412
474
|
schemaHash: z.string().regex(/^[a-f0-9]{64}$/),
|
|
413
475
|
})
|
|
414
476
|
.strict();
|
|
@@ -416,7 +478,7 @@ const CapabilityOperationManifestBaseSchema = z
|
|
|
416
478
|
export const CapabilityQueryManifestSchema = CapabilityOperationManifestBaseSchema.extend({
|
|
417
479
|
openWorld: z.boolean(),
|
|
418
480
|
universalSearch: z
|
|
419
|
-
.object({ tags: z.array(CapabilitySearchTagManifestSchema).max(100) })
|
|
481
|
+
.object({ tags: z.array(CapabilitySearchTagManifestSchema).max(100), scopeTypes: z.array(CapabilityLocalIdSchema).max(100).optional() })
|
|
420
482
|
.strict()
|
|
421
483
|
.optional(),
|
|
422
484
|
}).strict();
|
|
@@ -425,7 +487,7 @@ export const CapabilityActionManifestSchema = CapabilityOperationManifestBaseSch
|
|
|
425
487
|
destructive: z.boolean(),
|
|
426
488
|
openWorld: z.boolean(),
|
|
427
489
|
idempotency: z.enum(["none", "required"]),
|
|
428
|
-
approval: z.
|
|
490
|
+
approval: z.enum(["rememberable", "none"]).optional(),
|
|
429
491
|
review: z.literal(true).optional(),
|
|
430
492
|
}).strict();
|
|
431
493
|
|
|
@@ -443,6 +505,7 @@ export const CapabilityManifestSchema = z
|
|
|
443
505
|
types: z.array(CapabilityResourceTypeManifestSchema).max(200),
|
|
444
506
|
queries: z.array(CapabilityQueryManifestSchema).max(200),
|
|
445
507
|
actions: z.array(CapabilityActionManifestSchema).max(200),
|
|
508
|
+
commands: z.array(CapabilityCommandManifestSchema).max(200),
|
|
446
509
|
})
|
|
447
510
|
.strict();
|
|
448
511
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { CapabilityExecutionContext, CapabilityResult } from "./capabilities";
|
|
3
|
+
|
|
4
|
+
/** One binary payload. Provider IDs are limited to 2 KiB UTF-8; Core seals them into opaque references, never URLs. */
|
|
5
|
+
export const CapabilityStreamSchema = z
|
|
6
|
+
.object({
|
|
7
|
+
id: z.string().min(1).max(8192),
|
|
8
|
+
direction: z.enum(["read", "write"]),
|
|
9
|
+
name: z.string().min(1).max(255).optional(),
|
|
10
|
+
mediaType: z
|
|
11
|
+
.string()
|
|
12
|
+
.min(1)
|
|
13
|
+
.max(255)
|
|
14
|
+
.regex(/^[\x20-\x7e]+$/),
|
|
15
|
+
size: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
|
|
16
|
+
expiresAt: z.iso.datetime(),
|
|
17
|
+
})
|
|
18
|
+
.strict();
|
|
19
|
+
export type CapabilityStream = z.infer<typeof CapabilityStreamSchema>;
|
|
20
|
+
export const CapabilityStreamPolicySchema = z
|
|
21
|
+
.object({
|
|
22
|
+
direction: z.enum(["read", "write"]),
|
|
23
|
+
maxBytes: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
24
|
+
})
|
|
25
|
+
.strict();
|
|
26
|
+
export const CapabilityStreamStatusSchema = z.discriminatedUnion("state", [
|
|
27
|
+
z.object({ state: z.literal("open") }).strict(),
|
|
28
|
+
z.object({ state: z.literal("aborted") }).strict(),
|
|
29
|
+
z.object({ state: z.literal("completed"), result: z.unknown() }).strict(),
|
|
30
|
+
]);
|
|
31
|
+
export type CapabilityStreamStatus = z.infer<typeof CapabilityStreamStatusSchema>;
|
|
32
|
+
export type CapabilityStreamDefinition = z.infer<typeof CapabilityStreamPolicySchema> &
|
|
33
|
+
(
|
|
34
|
+
| { direction: "read"; read: (stream: CapabilityStream, context: CapabilityExecutionContext) => Promise<Response> }
|
|
35
|
+
| {
|
|
36
|
+
direction: "write";
|
|
37
|
+
/** Consume completely, then publish. Must reconcile retries using stream.id. */
|
|
38
|
+
write: (
|
|
39
|
+
stream: CapabilityStream,
|
|
40
|
+
body: ReadableStream<Uint8Array>,
|
|
41
|
+
context: CapabilityExecutionContext,
|
|
42
|
+
) => Promise<CapabilityResult<unknown>>;
|
|
43
|
+
status: (stream: CapabilityStream, context: CapabilityExecutionContext) => Promise<CapabilityStreamStatus>;
|
|
44
|
+
abort: (stream: CapabilityStream, context: CapabilityExecutionContext) => Promise<void>;
|
|
45
|
+
}
|
|
46
|
+
);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/** Commands carry small public references/options, never document bodies or secrets. */
|
|
4
|
+
export const COMMAND_MAX_URL_LENGTH = 8192;
|
|
5
|
+
export const CommandPathSchema = z
|
|
6
|
+
.string()
|
|
7
|
+
.max(2000)
|
|
8
|
+
.refine((value) => {
|
|
9
|
+
if (!value.startsWith("/") || /[\\\u0000-\u0020]/u.test(value)) return false;
|
|
10
|
+
try {
|
|
11
|
+
return new URL(value, "https://cloud.invalid").origin === "https://cloud.invalid";
|
|
12
|
+
} catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}, "Expected a Cloud path");
|
|
16
|
+
|
|
17
|
+
export const CommandOptionsSchema = z.object({ returnTo: CommandPathSchema.optional() }).strict();
|
|
18
|
+
export type CommandOptions = z.infer<typeof CommandOptionsSchema>;
|
|
19
|
+
export const CommandRequestSchema = CommandOptionsSchema.extend({ input: z.unknown() }).strict();
|
|
20
|
+
export const CommandLinkSchema = z.object({ href: z.url().max(COMMAND_MAX_URL_LENGTH) }).strict();
|
|
21
|
+
|
|
22
|
+
export const commandPath = (path: string, command: string, input: unknown, options: CommandOptions = {}): string => {
|
|
23
|
+
const url = new URL(CommandPathSchema.parse(path), "https://cloud.invalid");
|
|
24
|
+
url.searchParams.set("command", command);
|
|
25
|
+
url.searchParams.set("commandInput", JSON.stringify(input));
|
|
26
|
+
const parsed = CommandOptionsSchema.parse(options);
|
|
27
|
+
if (parsed.returnTo) url.searchParams.set("commandReturn", parsed.returnTo);
|
|
28
|
+
else url.searchParams.delete("commandReturn");
|
|
29
|
+
const result = `${url.pathname}${url.search}${url.hash}`;
|
|
30
|
+
if (result.length > COMMAND_MAX_URL_LENGTH) throw new Error("Command link is too large");
|
|
31
|
+
return result;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const readCommand = (url: URL) => {
|
|
35
|
+
const id = url.searchParams.get("command");
|
|
36
|
+
if (!id) return null;
|
|
37
|
+
const raw = url.searchParams.get("commandInput") ?? "{}";
|
|
38
|
+
if (url.href.length > COMMAND_MAX_URL_LENGTH) throw new Error("Command link is too large");
|
|
39
|
+
return {
|
|
40
|
+
id,
|
|
41
|
+
input: JSON.parse(raw) as unknown,
|
|
42
|
+
options: CommandOptionsSchema.parse({ returnTo: url.searchParams.get("commandReturn") ?? undefined }),
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const clearCommand = (url: URL): string => {
|
|
47
|
+
for (const key of ["command", "commandInput", "commandReturn"]) url.searchParams.delete(key);
|
|
48
|
+
return `${url.pathname}${url.search}${url.hash}`;
|
|
49
|
+
};
|
package/src/contracts/index.ts
CHANGED
|
@@ -2,7 +2,6 @@ export * from "./account-categories";
|
|
|
2
2
|
export * from "./announcements";
|
|
3
3
|
export * from "./app";
|
|
4
4
|
export * from "./capabilities";
|
|
5
|
-
export * from "./notification-live";
|
|
6
5
|
export * from "./notification-types";
|
|
7
6
|
export * from "./posix";
|
|
8
7
|
export * from "./profile";
|
|
@@ -12,3 +11,9 @@ export * from "./shared";
|
|
|
12
11
|
export * from "./user-notifications";
|
|
13
12
|
export * from "./widgets";
|
|
14
13
|
export * from "./app-approval";
|
|
14
|
+
|
|
15
|
+
// Core app bar administration and shared shortcut values.
|
|
16
|
+
export { RailAccessSchema, RailAdminInputSchema, RailAdminSchema, type RailAdminEntry, type RailAdminState } from "./rail-admin";
|
|
17
|
+
export type { RailShortcut } from "./rail-preferences";
|
|
18
|
+
|
|
19
|
+
export * from "./commands";
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { RAIL_PREFERENCES_MAX_BYTES, RailShortcutSchema } from "./rail-preferences";
|
|
3
|
+
import { AccessEntrySchema } from "./shared";
|
|
4
|
+
|
|
5
|
+
export const RailAccessSchema = AccessEntrySchema.extend({
|
|
6
|
+
principal: z.discriminatedUnion("type", [
|
|
7
|
+
z.object({ type: z.literal("authenticated") }).strict(),
|
|
8
|
+
z.object({ type: z.literal("user"), userId: z.uuid() }).strict(),
|
|
9
|
+
z.object({ type: z.literal("group"), groupId: z.uuid() }).strict(),
|
|
10
|
+
]),
|
|
11
|
+
permission: z.literal("read"),
|
|
12
|
+
});
|
|
13
|
+
export const RailAdminEntrySchema = z.object({ shortcut: RailShortcutSchema, access: z.array(RailAccessSchema) }).strict();
|
|
14
|
+
const revision = z.number().int().nonnegative().max(2_147_483_647);
|
|
15
|
+
export const RailAdminSchema = z.object({ revision, entries: z.array(RailAdminEntrySchema) }).strict();
|
|
16
|
+
/** Strip presentation metadata from editor drafts before sending or persisting grants. */
|
|
17
|
+
export const RailAdminInputSchema = z
|
|
18
|
+
.object({
|
|
19
|
+
revision,
|
|
20
|
+
entries: z.array(
|
|
21
|
+
z
|
|
22
|
+
.object({
|
|
23
|
+
shortcut: RailShortcutSchema,
|
|
24
|
+
access: z.array(RailAccessSchema.pick({ principal: true, permission: true })),
|
|
25
|
+
})
|
|
26
|
+
.strict(),
|
|
27
|
+
),
|
|
28
|
+
})
|
|
29
|
+
.strict()
|
|
30
|
+
.superRefine((value, ctx) => {
|
|
31
|
+
if (new TextEncoder().encode(JSON.stringify(value)).byteLength > RAIL_PREFERENCES_MAX_BYTES)
|
|
32
|
+
ctx.addIssue({ code: "custom", message: "Global app bar configuration exceeds 16 KiB" });
|
|
33
|
+
if (new Set(value.entries.map((entry) => entry.shortcut.id)).size !== value.entries.length)
|
|
34
|
+
ctx.addIssue({ code: "custom", message: "Shortcut IDs must be unique" });
|
|
35
|
+
});
|
|
36
|
+
export type RailAdminInput = z.infer<typeof RailAdminInputSchema>;
|
|
37
|
+
export type RailAdminState = z.infer<typeof RailAdminSchema>;
|
|
38
|
+
export type RailAdminEntry = z.infer<typeof RailAdminEntrySchema>;
|
|
@@ -51,3 +51,19 @@ export const RailPreferencesSchema = z
|
|
|
51
51
|
});
|
|
52
52
|
export type RailPreferences = z.infer<typeof RailPreferencesSchema>;
|
|
53
53
|
export const defaultRailPreferences = (): RailPreferences => ({ revision: 0, visibility: {}, shortcuts: [] });
|
|
54
|
+
|
|
55
|
+
/** Managed entries are separate from the writable personal API contract. */
|
|
56
|
+
export type RailSnapshot = RailPreferences & { managedShortcuts?: RailShortcut[] };
|
|
57
|
+
export const RailSnapshotSchema = z
|
|
58
|
+
.object({
|
|
59
|
+
revision: z.number(),
|
|
60
|
+
visibility: z.record(z.string(), z.boolean()),
|
|
61
|
+
shortcuts: z.array(RailShortcutSchema),
|
|
62
|
+
managedShortcuts: z.array(RailShortcutSchema).optional(),
|
|
63
|
+
})
|
|
64
|
+
.strict()
|
|
65
|
+
.superRefine(({ managedShortcuts, ...personal }, ctx) => {
|
|
66
|
+
if (!RailPreferencesSchema.safeParse(personal).success) ctx.addIssue({ code: "custom", message: "Invalid personal app bar settings" });
|
|
67
|
+
if (new TextEncoder().encode(JSON.stringify(managedShortcuts ?? [])).byteLength > RAIL_PREFERENCES_MAX_BYTES)
|
|
68
|
+
ctx.addIssue({ code: "custom", message: "Managed shortcuts exceed the 16 KiB page budget" });
|
|
69
|
+
});
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { AppAdminNavigationGroup, AppAppearance, AppAppearanceColor, AppPresentationCatalog } from "./app";
|
|
1
|
+
import type { AppAdminNavigationGroup, AppAppearance, AppAppearanceColor, AppPresentationCatalog, AppSearchLink } from "./app";
|
|
3
2
|
import type { CapabilityManifest, CapabilityPresentationCatalog } from "./capabilities";
|
|
4
3
|
import type { Role } from "./shared";
|
|
5
4
|
import type { DashboardWidgetPresentation } from "./widgets";
|
|
@@ -36,31 +35,7 @@ export type CapabilityRegistryEntry = {
|
|
|
36
35
|
presentation?: CapabilityPresentationCatalog;
|
|
37
36
|
};
|
|
38
37
|
|
|
39
|
-
export type AppRegistryHelpSummary =
|
|
40
|
-
|
|
41
|
-
export type HelpRegistryDocument = {
|
|
42
|
-
id: string;
|
|
43
|
-
title: string;
|
|
44
|
-
icon?: string;
|
|
45
|
-
description?: string;
|
|
46
|
-
order: number;
|
|
47
|
-
markdown: string;
|
|
48
|
-
/** Precomputed by the app. Older registry entries may omit it during rolling upgrades. */
|
|
49
|
-
searchText?: string;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
/** Full Markdown lives separately so normal app discovery stays small. */
|
|
53
|
-
export type HelpRegistryEntry = {
|
|
54
|
-
appId: string;
|
|
55
|
-
appName: string;
|
|
56
|
-
appIcon: string;
|
|
57
|
-
manifestHash: string;
|
|
58
|
-
/** Base language that owns the complete logical article set. Absent on legacy registrations. */
|
|
59
|
-
baseLocale?: string;
|
|
60
|
-
/** Partial localized article variants stored in the same bounded logical registration. */
|
|
61
|
-
documentsByLocale?: Readonly<Record<string, readonly HelpRegistryDocument[]>>;
|
|
62
|
-
documents: readonly HelpRegistryDocument[];
|
|
63
|
-
};
|
|
38
|
+
export type AppRegistryHelpSummary = { manifestHash: string; pageBase: string; baseLocale: string };
|
|
64
39
|
|
|
65
40
|
export type AppRegistryLegalLink = {
|
|
66
41
|
label: string;
|
|
@@ -102,6 +77,7 @@ export type AppRegistryEntry = {
|
|
|
102
77
|
capabilities?: AppRegistryCapabilitySummary;
|
|
103
78
|
help?: AppRegistryHelpSummary;
|
|
104
79
|
legalLinks?: AppRegistryLegalLink[];
|
|
80
|
+
searchLinks?: readonly AppSearchLink[];
|
|
105
81
|
widgets?: AppRegistryWidget[];
|
|
106
82
|
/** Setting keys declared by this app. Used by admin tooling to avoid treating live app-owned settings as legacy. */
|
|
107
83
|
settingKeys?: readonly string[];
|
|
@@ -43,7 +43,7 @@ type StringLikeKind = "string" | "text" | "email" | "url" | "secret" | "image" |
|
|
|
43
43
|
export type AppSettingDef =
|
|
44
44
|
| (CommonDef & { kind: StringLikeKind; default: string; placeholder?: string; templateVars?: readonly string[] })
|
|
45
45
|
| (CommonDef & { kind: "boolean"; default: boolean })
|
|
46
|
-
| (CommonDef & { kind: "number"; default: number; min?: number; max?: number; placeholder?: string })
|
|
46
|
+
| (CommonDef & { kind: "number"; default: number; min?: number; max?: number; integer?: boolean; placeholder?: string })
|
|
47
47
|
| (CommonDef & { kind: "enum"; default: string; options: ReadonlyArray<SettingOption> })
|
|
48
48
|
| (CommonDef & { kind: "string_list"; default: readonly string[]; placeholder?: string })
|
|
49
49
|
| (CommonDef & { kind: "number_list"; default: readonly number[]; placeholder?: string });
|
package/src/contracts/shared.ts
CHANGED
|
@@ -288,12 +288,16 @@ export const AccountActivityListResponseSchema = z.object({
|
|
|
288
288
|
});
|
|
289
289
|
export type AccountActivityListResponse = z.infer<typeof AccountActivityListResponseSchema>;
|
|
290
290
|
|
|
291
|
+
export const AuthenticatedPrincipalSchema = z.discriminatedUnion("type", [
|
|
292
|
+
z.object({ type: z.literal("user").describe("One user account."), userId: z.uuid().describe("Exact user ID returned by the Accounts directory.") }),
|
|
293
|
+
z.object({ type: z.literal("group").describe("Members of one group."), groupId: z.uuid().describe("Exact group ID returned by the Accounts directory.") }),
|
|
294
|
+
z.object({ type: z.literal("service_account").describe("One service account."), serviceAccountId: z.uuid().describe("Exact service account ID returned by the Accounts directory.") }),
|
|
295
|
+
z.object({ type: z.literal("authenticated").describe("Every authenticated identity; no directory ID is needed.") }),
|
|
296
|
+
]);
|
|
297
|
+
|
|
291
298
|
export const PrincipalSchema = z.discriminatedUnion("type", [
|
|
292
|
-
|
|
293
|
-
z.object({ type: z.literal("
|
|
294
|
-
z.object({ type: z.literal("service_account"), serviceAccountId: z.uuid() }),
|
|
295
|
-
z.object({ type: z.literal("authenticated") }),
|
|
296
|
-
z.object({ type: z.literal("public") }),
|
|
299
|
+
...AuthenticatedPrincipalSchema.options,
|
|
300
|
+
z.object({ type: z.literal("public").describe("Public access where supported by the resource; no directory ID is needed.") }),
|
|
297
301
|
]);
|
|
298
302
|
export type Principal = z.infer<typeof PrincipalSchema>;
|
|
299
303
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/** One metric plus six server phases and bounded routing metadata. */
|
|
4
|
+
export const WEB_VITALS_MAX_BYTES = 4096;
|
|
5
|
+
export const WebVitalsReportSchema = z
|
|
6
|
+
.object({
|
|
7
|
+
appId: z
|
|
8
|
+
.string()
|
|
9
|
+
.min(1)
|
|
10
|
+
.max(64)
|
|
11
|
+
.regex(/^[a-z0-9-]+$/),
|
|
12
|
+
routeTemplate: z
|
|
13
|
+
.string()
|
|
14
|
+
.min(1)
|
|
15
|
+
.max(200)
|
|
16
|
+
.regex(/^\/[a-zA-Z0-9_/:.*-]*$/),
|
|
17
|
+
id: z.string().min(1).max(100),
|
|
18
|
+
name: z.enum(["LCP", "INP", "CLS"]),
|
|
19
|
+
value: z.number().nonnegative(),
|
|
20
|
+
navigationType: z.string().max(32),
|
|
21
|
+
serverTiming: z.partialRecord(
|
|
22
|
+
z.enum(["auth", "settings", "runtime", "ssr_data", "ssr_finalize", "ssr_render"]),
|
|
23
|
+
z.number().nonnegative(),
|
|
24
|
+
),
|
|
25
|
+
})
|
|
26
|
+
.strict();
|
|
27
|
+
export type WebVitalsReport = z.infer<typeof WebVitalsReportSchema>;
|
package/src/desktop/solid.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { deserialize, serialize } from "seroval";
|
|
1
|
+
import { deserialize as deserializeProps, serialize as serializeProps } from "seroval";
|
|
2
2
|
import type { Component, JSX, ParentProps } from "solid-js";
|
|
3
3
|
import { children, createContext, createEffect, createMemo, createSignal, For, onCleanup, onMount, Show, useContext } from "solid-js";
|
|
4
4
|
import { Dynamic, Portal } from "solid-js/web";
|
|
@@ -47,7 +47,7 @@ export const defineDesktopWindows = <Definitions extends Record<string, DesktopW
|
|
|
47
47
|
({
|
|
48
48
|
kind: desktopWindowDescriptorKind,
|
|
49
49
|
name,
|
|
50
|
-
props:
|
|
50
|
+
props: serializeProps(props ?? {}),
|
|
51
51
|
}) satisfies DesktopWindowDescriptor as unknown as JSX.Element;
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -73,7 +73,7 @@ export function DesktopWindowHost<Definitions extends Record<string, DesktopWind
|
|
|
73
73
|
const windowProps = createMemo(() => {
|
|
74
74
|
const current = descriptor();
|
|
75
75
|
if (!current) return {};
|
|
76
|
-
return
|
|
76
|
+
return deserializeProps<Record<string, unknown>>(current.props);
|
|
77
77
|
});
|
|
78
78
|
|
|
79
79
|
return createMemo(() => {
|
package/src/index.ts
CHANGED
|
@@ -16,15 +16,11 @@ export {
|
|
|
16
16
|
APP_REGISTRY_PREFIX,
|
|
17
17
|
APP_REGISTRY_TTL_MS,
|
|
18
18
|
CAPABILITY_REGISTRY_CONFIG,
|
|
19
|
-
HELP_REGISTRY_CONFIG,
|
|
20
19
|
appRegistry,
|
|
21
20
|
capabilityRegistry,
|
|
22
21
|
getApp,
|
|
23
|
-
getHelp,
|
|
24
|
-
helpRegistry,
|
|
25
22
|
listApps,
|
|
26
23
|
listAppsDetailed,
|
|
27
|
-
listHelp,
|
|
28
24
|
listLegalLinks,
|
|
29
25
|
listWidgets,
|
|
30
26
|
readAppRegistrySnapshot,
|
package/src/server/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ export { DEFAULT_LOCALE, getLocale, LOCALE_COOKIE, LOCALE_HEADER, locale, prefer
|
|
|
9
9
|
export type {
|
|
10
10
|
AuthContext,
|
|
11
11
|
InvocationProvenance,
|
|
12
|
+
InvocationExpectation,
|
|
12
13
|
RateLimitConfig,
|
|
13
14
|
RateLimitRouteOverride,
|
|
14
15
|
RequestActor,
|
|
@@ -25,6 +26,7 @@ export {
|
|
|
25
26
|
openApiMeta,
|
|
26
27
|
rateLimit,
|
|
27
28
|
rejectReservedWorkloadCredential,
|
|
29
|
+
requireInvocation,
|
|
28
30
|
requestLogger,
|
|
29
31
|
requiresAdmin,
|
|
30
32
|
requiresAuth,
|
|
@@ -56,6 +58,7 @@ export type {
|
|
|
56
58
|
ServiceErrorCode,
|
|
57
59
|
} from "./services";
|
|
58
60
|
export {
|
|
61
|
+
accessRevision,
|
|
59
62
|
buildAccessPrincipalCondition,
|
|
60
63
|
buildAccessPrincipalTierConditions,
|
|
61
64
|
createAccess,
|