@k2b/cloud 0.43.0 → 0.45.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/package.json +11 -2
- package/src/_internal/app-access-request.ts +13 -0
- package/src/_internal/app-access-resources.ts +141 -0
- package/src/_internal/app-event-catalog.ts +40 -0
- package/src/_internal/app-event-schemas.ts +127 -0
- package/src/_internal/application-package.ts +40 -0
- package/src/_internal/built-in-app-ids.json +27 -0
- package/src/_internal/capabilities.ts +158 -12
- package/src/_internal/capability-origin.ts +4 -0
- package/src/_internal/capability-streams.ts +7 -5
- package/src/_internal/define-app.ts +57 -6
- package/src/_internal/object-store-area.ts +9 -0
- package/src/_internal/object-store-config.ts +101 -0
- package/src/_internal/object-store-key.ts +14 -0
- package/src/_internal/process-identity.ts +7 -2
- package/src/_internal/registry-validation.ts +11 -1
- package/src/_internal/registry.ts +8 -1
- package/src/_internal/runtime-context.ts +8 -1
- package/src/access/AppAccessEditor.tsx +2 -0
- package/src/access/GroupCoverage.tsx +20 -5
- package/src/access/PermissionEditor.tsx +20 -5
- package/src/access/PrincipalAvatar.tsx +30 -21
- package/src/access/PrincipalPicker.tsx +14 -4
- package/src/access/ResourceApiKeys.tsx +3 -4
- package/src/access/app-access-messages.ts +6 -0
- package/src/access/messages.ts +4 -4
- package/src/account/EntitySearch.tsx +12 -0
- package/src/account/ProfilePictureDialog.tsx +699 -0
- package/src/account/profile-picture-messages.ts +269 -0
- package/src/account/ui.ts +7 -0
- package/src/ai/admin.ts +3 -0
- package/src/ai/app-frame.ts +12 -0
- package/src/ai/application-tasks.ts +101 -0
- package/src/ai/assistant-models.ts +126 -64
- package/src/ai/capabilities.ts +19 -4
- package/src/ai/chart-block.ts +63 -30
- package/src/ai/chat/chart-block.tsx +12 -3
- package/src/ai/chat/composer-adapter.ts +8 -7
- package/src/ai/chat/message-actions.tsx +1 -1
- package/src/ai/chat/message-utils.ts +2 -2
- package/src/ai/chat/presentation.tsx +22 -1
- package/src/ai/chat-quotas.ts +5 -4
- package/src/ai/chat-tasks.ts +2 -2
- package/src/ai/client/controller.ts +11 -10
- package/src/ai/client/projection.ts +3 -3
- package/src/ai/compaction.ts +3 -3
- package/src/ai/credentials.ts +13 -114
- package/src/ai/default-tool-contracts.ts +4 -3
- package/src/ai/dictation-runtime.ts +10 -6
- package/src/ai/enrich.ts +2 -2
- package/src/ai/executor.ts +40 -25
- package/src/ai/http.ts +17 -29
- package/src/ai/index.ts +12 -5
- package/src/ai/inference-calls.ts +5 -4
- package/src/ai/maintenance.ts +1 -1
- package/src/ai/memory-learning-runs.ts +5 -5
- package/src/ai/memory-learning.ts +3 -3
- package/src/ai/message-queue.ts +1 -2
- package/src/ai/migrate.ts +75 -42
- package/src/ai/model-access-migrate.ts +3 -17
- package/src/ai/model-access.ts +44 -85
- package/src/ai/model-card.tsx +134 -0
- package/src/ai/model-catalog.ts +359 -0
- package/src/ai/model-chooser-messages.ts +88 -0
- package/src/ai/model-chooser.tsx +577 -0
- package/src/ai/model-delete.ts +234 -0
- package/src/ai/model-fixture.ts +39 -0
- package/src/ai/model-icon.tsx +36 -0
- package/src/ai/model-overview.ts +53 -0
- package/src/ai/model-usage.ts +45 -0
- package/src/ai/openai-api-messages.ts +0 -4
- package/src/ai/openai-api.ts +26 -9
- package/src/ai/personal-agent.ts +1 -1
- package/src/ai/prefs.ts +12 -10
- package/src/ai/project-tool.ts +4 -4
- package/src/ai/projects-authorization.ts +53 -0
- package/src/ai/projects-routes.ts +58 -47
- package/src/ai/projects.ts +110 -88
- package/src/ai/protocol.ts +2 -2
- package/src/ai/provider-connections.ts +161 -0
- package/src/ai/provider-errors.ts +55 -6
- package/src/ai/provider-fetch.ts +4 -0
- package/src/ai/provider.ts +1 -1
- package/src/ai/quota-limits.ts +36 -0
- package/src/ai/quota-provider.ts +4 -2
- package/src/ai/quota-report.ts +7 -4
- package/src/ai/quotas.ts +19 -8
- package/src/ai/recent-resources.ts +9 -3
- package/src/ai/request-headers.ts +14 -87
- package/src/ai/resource-refs.ts +6 -2
- package/src/ai/routes.ts +47 -48
- package/src/ai/runtime.ts +50 -22
- package/src/ai/settings.ts +93 -580
- package/src/ai/skill-tool.ts +4 -4
- package/src/ai/skills-routes.ts +27 -26
- package/src/ai/skills.ts +120 -81
- package/src/ai/store.ts +45 -23
- package/src/ai/stream.ts +1 -1
- package/src/ai/structured-runs.ts +2 -2
- package/src/ai/structured.ts +21 -44
- package/src/ai/task-contracts.ts +25 -5
- package/src/ai/task-execution.ts +15 -6
- package/src/ai/task-model.ts +9 -0
- package/src/ai/transcription.ts +27 -22
- package/src/ai/types.ts +50 -62
- package/src/ai/ui.tsx +13 -0
- package/src/ai/usage.ts +8 -8
- package/src/ai/validate.ts +8 -7
- package/src/ai/view-block.ts +2 -2
- package/src/api/accounts-entities.ts +22 -3
- package/src/api/admin-ai-quotas.ts +34 -56
- package/src/api/admin-ai-skills.ts +4 -1
- package/src/api/admin-ai-usage.ts +1 -1
- package/src/api/admin-ai.ts +173 -0
- package/src/api/admin-core-settings.ts +68 -244
- package/src/api/app-approval.ts +3 -1
- package/src/api/app-event-delivery.ts +51 -0
- package/src/api/app-events.ts +123 -0
- package/src/api/app-resource-access.ts +48 -0
- package/src/api/auth/account-mismatch.ts +24 -0
- package/src/api/auth.ts +35 -5
- package/src/api/capabilities.ts +28 -4
- package/src/api/index.ts +7 -1
- package/src/api/mcp.ts +175 -2
- package/src/api/me.ts +6 -4
- package/src/api/pwa-phone.ts +75 -26
- package/src/api/pwa.ts +34 -8
- package/src/api/resource-links.ts +83 -6
- package/src/api/resource-resolve.ts +50 -0
- package/src/api/search/schemas.ts +3 -1
- package/src/api/search.ts +9 -7
- package/src/app-pwa/contracts.ts +4 -0
- package/src/app-pwa/core-query-error.ts +20 -0
- package/src/app-pwa/index.ts +1 -0
- package/src/app-pwa/service.ts +91 -0
- package/src/app-pwa/transport.ts +22 -0
- package/src/approvals/messages.ts +2 -2
- package/src/browser/cloud-id.ts +86 -0
- package/src/browser/resource-clipboard.ts +4 -0
- package/src/browser/resource-links.ts +52 -0
- package/src/browser/session.ts +39 -0
- package/src/browser/shared-text-history-validation.ts +151 -0
- package/src/browser/shared-text-history.ts +464 -0
- package/src/browser/shared-text-offline-authorized.ts +29 -0
- package/src/browser/shared-text-offline.ts +351 -0
- package/src/browser/shared-text-provider.ts +714 -0
- package/src/browser/shared-text-table-contract.ts +150 -0
- package/src/browser/shared-text.ts +19 -0
- package/src/capabilities/server-invoke.ts +2 -11
- package/src/capabilities/testing.ts +5 -2
- package/src/cli/access.ts +6 -5
- package/src/cli/admin/ai-models.ts +199 -0
- package/src/cli/admin/ai-quotas.ts +6 -102
- package/src/cli/admin/ai-usage.ts +4 -4
- package/src/cli/admin/app-credentials.ts +5 -3
- package/src/cli/admin/index.ts +5 -2
- package/src/cli/apps.ts +89 -26
- package/src/cli/capabilities.ts +22 -1
- package/src/cli/events.ts +320 -0
- package/src/config/env.ts +40 -0
- package/src/contracts/app-access.ts +26 -2
- package/src/contracts/app-details.ts +89 -0
- package/src/contracts/app-events.ts +51 -0
- package/src/contracts/app.ts +21 -5
- package/src/contracts/capabilities.ts +143 -3
- package/src/contracts/index.ts +2 -0
- package/src/contracts/notification-types.ts +36 -9
- package/src/contracts/pwa.ts +17 -1
- package/src/contracts/registry.ts +6 -0
- package/src/contracts/resource-clipboard.ts +35 -0
- package/src/contracts/resource-pages.ts +140 -0
- package/src/contracts/resource-resolution.ts +13 -0
- package/src/contracts/shared.ts +6 -1
- package/src/contracts/user-notifications.ts +2 -0
- package/src/events/app-event-delivery.ts +100 -0
- package/src/events/app-events.testing.ts +65 -0
- package/src/events/app-events.ts +134 -0
- package/src/events/core-user-created.ts +24 -0
- package/src/events/index.ts +9 -0
- package/src/events/testing.ts +2 -0
- package/src/index.ts +1 -1
- package/src/server/api/respond.ts +17 -4
- package/src/server/index.ts +2 -0
- package/src/server/middleware/app-access.ts +1 -1
- package/src/server/middleware/auth.ts +21 -7
- package/src/server/middleware/index.ts +1 -0
- package/src/server/middleware/invocation.ts +2 -0
- package/src/server/public-storage.ts +106 -0
- package/src/server/services/access.ts +31 -10
- package/src/server/services/index.ts +1 -0
- package/src/services/accounts/app.ts +36 -13
- package/src/services/accounts/entities.ts +43 -19
- package/src/services/accounts/group-sql.ts +23 -0
- package/src/services/accounts/groups.ts +19 -14
- package/src/services/accounts/identities.ts +70 -11
- package/src/services/accounts/identity-reconciliation.ts +3 -1
- package/src/services/accounts/local-groups.ts +69 -4
- package/src/services/accounts/users.ts +45 -14
- package/src/services/app-access.ts +25 -7
- package/src/services/app-details.ts +224 -0
- package/src/services/app-discovery.ts +2 -1
- package/src/services/app-event-shapes.ts +139 -0
- package/src/services/app-events/accept.ts +116 -0
- package/src/services/app-events/admin.ts +250 -0
- package/src/services/app-events/declarations.ts +187 -0
- package/src/services/app-events/deliver.ts +218 -0
- package/src/services/app-events.ts +92 -0
- package/src/services/config-conflict.ts +11 -0
- package/src/services/core-user-events.ts +1 -0
- package/src/services/freeipa-config.ts +9 -0
- package/src/services/geo/ledger.ts +28 -10
- package/src/services/geo/service.ts +43 -24
- package/src/services/identity/core-origin.ts +12 -0
- package/src/services/identity/invocation-actor.ts +20 -2
- package/src/services/identity/invocation-authority.ts +4 -0
- package/src/services/identity/invocation-token.ts +238 -56
- package/src/services/identity/issue-invocation.ts +6 -1
- package/src/services/image-metadata.ts +65 -1
- package/src/services/index.ts +13 -1
- package/src/services/ipa/sync.ts +6 -2
- package/src/services/ipa/users.ts +4 -1
- package/src/services/mandates/index.ts +2 -2
- package/src/services/mandates/policy.ts +20 -12
- package/src/services/notifications/browser.ts +4 -0
- package/src/services/notifications/catalog.ts +5 -2
- package/src/services/notifications/platform.ts +7 -2
- package/src/services/notifications/user.ts +18 -7
- package/src/services/oauth-tokens.ts +28 -2
- package/src/services/object-store.ts +217 -0
- package/src/services/providers/local/users.ts +5 -0
- package/src/services/pwa-devices.ts +147 -37
- package/src/services/session/index.ts +26 -3
- package/src/services/settings/core-settings.ts +71 -43
- package/src/services/settings/index.ts +8 -0
- package/src/services/settings/versions.ts +26 -0
- package/src/shared/ai-models.ts +274 -0
- package/src/shared/ai-quotas.ts +2 -2
- package/src/shared/ai-usage.ts +2 -2
- package/src/shared/email-html.ts +1 -0
- package/src/shared/index.ts +2 -0
- package/src/shared/markdown/extensions/katex.ts +8 -1
- package/src/shared/markdown/extensions/tables.ts +14 -1
- package/src/shared/markdown/index.ts +22 -11
- package/src/shared-text/anchors.ts +29 -0
- package/src/shared-text/awareness.ts +87 -0
- package/src/shared-text/changes.ts +27 -0
- package/src/shared-text/documents.ts +433 -65
- package/src/shared-text/events.ts +12 -2
- package/src/shared-text/index.ts +16 -1
- package/src/shared-text/log.ts +24 -5
- package/src/shared-text/snapshots.ts +69 -23
- package/src/shared-text/structural-history.ts +377 -0
- package/src/shared-text/table-actions.ts +432 -0
- package/src/shared-text/text-diff.ts +75 -0
- package/src/shared-text/types.ts +24 -5
- package/src/ssr/AdminArea.tsx +131 -0
- package/src/ssr/AdminLayout.tsx +9 -3
- package/src/ssr/AdminSidebar.tsx +7 -7
- package/src/ssr/AppAccessDenied.tsx +2 -0
- package/src/ssr/AppBadge.tsx +14 -0
- package/src/ssr/AppLaunchpadPanel.tsx +2 -2
- package/src/ssr/CloudShell.island.tsx +44 -7
- package/src/ssr/Layout.tsx +9 -7
- package/src/ssr/MobileProfileActions.tsx +2 -2
- package/src/ssr/ProfilePreferences.tsx +2 -1
- package/src/ssr/PwaLayout.tsx +16 -1
- package/src/ssr/PwaPartCount.island.tsx +23 -0
- package/src/ssr/SettingsSaveOutcome.tsx +58 -0
- package/src/ssr/SettingsSaveStatus.island.tsx +16 -0
- package/src/ssr/__snapshots__/Layout.unchanged.render.test.tsx.snap +3 -3
- package/src/ssr/admin-active-link.ts +16 -0
- package/src/ssr/admin-navigation.ts +26 -31
- package/src/ssr/app-badges.ts +31 -7
- package/src/ssr/app-launchpad.tsx +6 -1
- package/src/ssr/index.ts +8 -0
- package/src/ssr/page-trail-runtime.ts +3 -1
- package/src/ssr/platform-messages.ts +11 -1
- package/src/ssr/profile-actions.ts +3 -6
- package/src/ssr/pwa-messages.ts +2 -2
- package/src/ssr/rail-navigation.ts +5 -1
- package/src/ssr/settings-draft.ts +153 -0
- package/src/ssr/shell-data.tsx +3 -1
- package/src/styles/global.css +1 -0
- package/src/styles/profile-picture.css +270 -0
- package/src/styles/utilities-navigation.css +6 -0
- package/src/workflows/ai/policy.ts +49 -0
- package/src/workflows/ai/runtime.ts +131 -21
- package/src/workflows/ai/store.ts +283 -35
- package/src/workflows/ai/types.ts +8 -2
- package/src/workflows/ai-actions.ts +148 -120
- package/src/workflows/authoring.ts +31 -0
- package/src/workflows/contracts.ts +1 -0
- package/src/workflows/definition.ts +2 -0
- package/src/workflows/editor.ts +15 -0
- package/src/workflows/index.ts +2 -1
- package/src/workflows/language/compiler.ts +34 -3
- package/src/workflows/language/expressions.ts +3 -2
- package/src/workflows/runtime/executor.ts +134 -34
- package/src/workflows/runtime/index.ts +2 -0
- package/src/workflows/runtime/ports.ts +5 -1
- package/src/workflows/store/actions.ts +1 -1
- package/src/workflows/store/definitions.ts +5 -0
- package/src/workflows/store/runs.ts +5 -3
- package/src/workflows/store/worker.ts +7 -2
- package/src/ai/model-pricing.ts +0 -43
- package/src/ai/model-request-settings.ts +0 -98
- package/src/ai/settings-messages.ts +0 -129
- package/src/shared/ai-model-request-settings.ts +0 -21
|
@@ -42,6 +42,10 @@ export const platformMessages = i18n.define({
|
|
|
42
42
|
announcements: "Announcements",
|
|
43
43
|
appCredentials: "App credentials",
|
|
44
44
|
ai: "AI",
|
|
45
|
+
observability: "Observability",
|
|
46
|
+
baseSettings: "Base settings",
|
|
47
|
+
signinSecurity: "Sign-in & security",
|
|
48
|
+
cloudAreas: "Cloud",
|
|
45
49
|
settings: "Settings",
|
|
46
50
|
appAdmin: "App administration",
|
|
47
51
|
userManagement: "Accounts & sign-in",
|
|
@@ -49,6 +53,7 @@ export const platformMessages = i18n.define({
|
|
|
49
53
|
accountRegistration: "Registration & requests",
|
|
50
54
|
accountOperations: "Operations",
|
|
51
55
|
linuxAccess: "Linux identities",
|
|
56
|
+
storage: "Storage",
|
|
52
57
|
pdfRendering: "PDF rendering",
|
|
53
58
|
placesAndMaps: "Places and maps",
|
|
54
59
|
emailTemplates: "Email templates",
|
|
@@ -142,8 +147,12 @@ export const platformMessages = i18n.define({
|
|
|
142
147
|
general: "Allgemein",
|
|
143
148
|
rail: "App-Leiste",
|
|
144
149
|
announcements: "Ankündigungen",
|
|
145
|
-
appCredentials: "App-
|
|
150
|
+
appCredentials: "App-Schlüssel",
|
|
146
151
|
ai: "KI",
|
|
152
|
+
observability: "Observability",
|
|
153
|
+
baseSettings: "Grundeinstellungen",
|
|
154
|
+
signinSecurity: "Anmeldung & Sicherheit",
|
|
155
|
+
cloudAreas: "Cloud",
|
|
147
156
|
settings: "Einstellungen",
|
|
148
157
|
appAdmin: "App-Administration",
|
|
149
158
|
userManagement: "Accounts & Anmeldung",
|
|
@@ -151,6 +160,7 @@ export const platformMessages = i18n.define({
|
|
|
151
160
|
accountRegistration: "Registrierung & Anfragen",
|
|
152
161
|
accountOperations: "Betrieb",
|
|
153
162
|
linuxAccess: "Linux-Identitäten",
|
|
163
|
+
storage: "Speicher",
|
|
154
164
|
pdfRendering: "PDF-Erstellung",
|
|
155
165
|
placesAndMaps: "Orte und Karten",
|
|
156
166
|
emailTemplates: "E-Mail-Vorlagen",
|
|
@@ -5,7 +5,7 @@ import type { CloudThemePreference } from "../shared/theme";
|
|
|
5
5
|
import { createPreferenceController } from "./preference-controller";
|
|
6
6
|
|
|
7
7
|
/** `appHref` is set while the installation offers the mobile app; the item then leads to `/me/app`. */
|
|
8
|
-
export function createProfileActions(initialTheme: CloudThemePreference, appHref?: string) {
|
|
8
|
+
export function createProfileActions(initialTheme: CloudThemePreference, appHref?: string, userId?: string) {
|
|
9
9
|
const locale = useLocale();
|
|
10
10
|
const preferences = createPreferenceController(initialTheme, locale);
|
|
11
11
|
const [signingOut, setSigningOut] = createSignal(false);
|
|
@@ -13,16 +13,13 @@ export function createProfileActions(initialTheme: CloudThemePreference, appHref
|
|
|
13
13
|
if (signingOut()) return;
|
|
14
14
|
setSigningOut(true);
|
|
15
15
|
// The API client and the push client load with the first sign-out, not with every page.
|
|
16
|
-
const loaded = await importOnDemand(() =>
|
|
16
|
+
const loaded = await importOnDemand(() => import("../browser/session"));
|
|
17
17
|
if (!loaded) {
|
|
18
18
|
setSigningOut(false);
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
|
-
const [{ browserNotificationClient }, { apiClient }] = loaded;
|
|
22
21
|
try {
|
|
23
|
-
await
|
|
24
|
-
const response = await apiClient.auth.logout.$post();
|
|
25
|
-
if (!response.ok) throw new Error(preferences.messages().signOutFailed);
|
|
22
|
+
await loaded.signOutBrowserSession(userId, preferences.messages().signOutFailed);
|
|
26
23
|
window.location.href = "/auth/login";
|
|
27
24
|
} catch {
|
|
28
25
|
toast.error(preferences.messages().signOutFailed);
|
package/src/ssr/pwa-messages.ts
CHANGED
|
@@ -13,7 +13,7 @@ export const pwaMessages = i18n.define({
|
|
|
13
13
|
newVersion: ({ cloud }: { cloud: string }) => `New version of ${cloud}`,
|
|
14
14
|
reload: "Reload",
|
|
15
15
|
otherAccount: ({ cloud, name }: { cloud: string; name: string }) =>
|
|
16
|
-
`Chrome on this
|
|
16
|
+
`Chrome on this device is signed in to ${cloud} as ${name}. Changes in the app may be saved to that account. Sign out of ${cloud} in Chrome.`,
|
|
17
17
|
errorAction: "Back to Start",
|
|
18
18
|
},
|
|
19
19
|
de: {
|
|
@@ -25,7 +25,7 @@ export const pwaMessages = i18n.define({
|
|
|
25
25
|
newVersion: ({ cloud }: { cloud: string }) => `Neue Version von ${cloud}`,
|
|
26
26
|
reload: "Neu laden",
|
|
27
27
|
otherAccount: ({ cloud, name }: { cloud: string; name: string }) =>
|
|
28
|
-
`Chrome auf diesem
|
|
28
|
+
`Chrome auf diesem Gerät ist bei ${cloud} als ${name} angemeldet. Änderungen in der App werden womöglich in diesem Konto gespeichert. Melde dich in Chrome von ${cloud} ab.`,
|
|
29
29
|
errorAction: "Zurück zum Start",
|
|
30
30
|
},
|
|
31
31
|
},
|
|
@@ -79,6 +79,9 @@ export const phoneBarLinks = (navigation: { shortcuts: RailLink[]; apps: RailLin
|
|
|
79
79
|
return [...links.slice(0, limit - 1), links[current]!];
|
|
80
80
|
};
|
|
81
81
|
|
|
82
|
+
/** A phone bar item, and where its app's count comes from, like the rail icon's. */
|
|
83
|
+
export type PhoneBarItem = TabBarItem & { counted?: { endpoint?: string; appId?: string } };
|
|
84
|
+
|
|
82
85
|
/** Start and four apps fill the phone bar's five places. */
|
|
83
86
|
const PHONE_BAR_APPS = 4;
|
|
84
87
|
|
|
@@ -93,7 +96,7 @@ export const phoneBarItems = (
|
|
|
93
96
|
navigation: { shortcuts: RailLink[]; apps: RailLink[] },
|
|
94
97
|
currentPath: string,
|
|
95
98
|
home: { label: string; path: string },
|
|
96
|
-
):
|
|
99
|
+
): PhoneBarItem[] => {
|
|
97
100
|
const links = phoneBarLinks(navigation, currentPath, PHONE_BAR_APPS);
|
|
98
101
|
const items = links.map((link) => ({
|
|
99
102
|
id: link.id,
|
|
@@ -101,6 +104,7 @@ export const phoneBarItems = (
|
|
|
101
104
|
icon: link.iconClass,
|
|
102
105
|
href: link.href,
|
|
103
106
|
current: railLinkActive(link, currentPath),
|
|
107
|
+
...(link.badge || link.appId ? { counted: { endpoint: link.badge, appId: link.appId } } : {}),
|
|
104
108
|
}));
|
|
105
109
|
const pathname = new URL(currentPath, "https://cloud.invalid").pathname;
|
|
106
110
|
const start = new URL(home.path, "https://cloud.invalid").pathname;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { SaveStatusState } from "@k2b/ui";
|
|
2
|
+
import { createEffect, createMemo, createSignal, onCleanup } from "solid-js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A settings form saves with the button in its level head, outside the form island. Both islands read this store: the
|
|
6
|
+
* form registers its state and actions, `SettingsSaveStatus` renders them. One form per page registers at a time.
|
|
7
|
+
*/
|
|
8
|
+
export type SettingsSaveControls = {
|
|
9
|
+
state: SaveStatusState;
|
|
10
|
+
savedAt?: string;
|
|
11
|
+
invalidCount?: number;
|
|
12
|
+
save: () => void;
|
|
13
|
+
discard: () => void;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const [controls, setControls] = createSignal<SettingsSaveControls | null>(null);
|
|
17
|
+
export const settingsSaveControls = controls;
|
|
18
|
+
|
|
19
|
+
/** A save that did not succeed: refused with a reason, or with an unclear outcome. */
|
|
20
|
+
export type SettingsDraftOutcome = { kind: "failed" | "unknown"; message: string };
|
|
21
|
+
|
|
22
|
+
/** Thrown by `save` when the server refused the write; anything else counts as an unclear outcome. */
|
|
23
|
+
export class SaveRejected extends Error {}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The error for a non-OK save response. A 4xx refused the write. A 5xx can come after the value was stored (for
|
|
27
|
+
* example when a follow-up step failed), so its outcome stays unclear and the person checks the current state.
|
|
28
|
+
*/
|
|
29
|
+
export const saveFailure = (status: number, message: string): Error =>
|
|
30
|
+
status >= 400 && status < 500 ? new SaveRejected(message) : new Error(message);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* For endpoints that write in one step and answer afterwards: an error body with a message comes from the handler,
|
|
34
|
+
* which did not store the value, so the save is refused and can be tried again. A response without one (a proxy
|
|
35
|
+
* page, a crash) leaves the outcome unclear.
|
|
36
|
+
*/
|
|
37
|
+
export const oneStepFailure = (status: number, body: { message?: string } | null, fallback: string): Error =>
|
|
38
|
+
body?.message ? new SaveRejected(body.message) : saveFailure(status, fallback);
|
|
39
|
+
|
|
40
|
+
type Options<T> = {
|
|
41
|
+
initial: T;
|
|
42
|
+
/** Field errors of a draft; Save stays disabled while there are any. */
|
|
43
|
+
validate?: (value: T) => Record<string, string>;
|
|
44
|
+
/** True when saving is needed although the values did not change, such as a confirmed replacement. */
|
|
45
|
+
pending?: () => boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Writes the draft and returns what is stored now. A save that writes several values one after another reports
|
|
48
|
+
* each stored step with `commit`, so a later failure keeps what already reached the server as the saved state.
|
|
49
|
+
*/
|
|
50
|
+
save: (value: T, commit: (stored: T) => void) => Promise<T>;
|
|
51
|
+
locale: () => string;
|
|
52
|
+
/** Runs with Discard, for state the form keeps beside the draft, such as staged resets. */
|
|
53
|
+
onDiscard?: () => void;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* A settings form that saves with a button: the draft, its field errors and the outcome of the last save. It registers
|
|
58
|
+
* with the level head, which shows the status with Discard and Save. A failed save keeps the draft; when the
|
|
59
|
+
* connection breaks, the outcome stays unclear and the person checks the current state instead of saving twice.
|
|
60
|
+
*/
|
|
61
|
+
export const createSettingsDraft = <T>(options: Options<T>) => {
|
|
62
|
+
const [stored, setStored] = createSignal<T>(options.initial);
|
|
63
|
+
const [draft, setDraft] = createSignal<T>(options.initial);
|
|
64
|
+
const [saving, setSaving] = createSignal(false);
|
|
65
|
+
const [outcome, setOutcome] = createSignal<SettingsDraftOutcome | null>(null);
|
|
66
|
+
const [savedAt, setSavedAt] = createSignal<string | undefined>(undefined);
|
|
67
|
+
const errors = createMemo(() => options.validate?.(draft()) ?? {});
|
|
68
|
+
const dirty = createMemo(() => JSON.stringify(draft()) !== JSON.stringify(stored()) || options.pending?.() === true);
|
|
69
|
+
const state = createMemo<SaveStatusState>(() => {
|
|
70
|
+
if (saving()) return "saving";
|
|
71
|
+
if (outcome()?.kind === "unknown") return "unknown";
|
|
72
|
+
if (Object.keys(errors()).length > 0) return "invalid";
|
|
73
|
+
if (outcome()?.kind === "failed") return "failed";
|
|
74
|
+
return dirty() ? "draft" : "saved";
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const save = async () => {
|
|
78
|
+
if (saving() || !dirty() || Object.keys(errors()).length > 0 || outcome()?.kind === "unknown") return;
|
|
79
|
+
setSaving(true);
|
|
80
|
+
setOutcome(null);
|
|
81
|
+
const submitted = draft();
|
|
82
|
+
// Edits made while the request runs stay; only an untouched draft takes the stored values.
|
|
83
|
+
const settle = (next: T) => {
|
|
84
|
+
setStored(() => next);
|
|
85
|
+
if (draft() === submitted) setDraft(() => next);
|
|
86
|
+
};
|
|
87
|
+
try {
|
|
88
|
+
// A step only moves the saved state; the draft keeps the values still on their way.
|
|
89
|
+
settle(await options.save(submitted, (step) => setStored(() => step)));
|
|
90
|
+
setSavedAt(new Intl.DateTimeFormat(options.locale(), { hour: "2-digit", minute: "2-digit" }).format(new Date()));
|
|
91
|
+
} catch (error) {
|
|
92
|
+
setOutcome({
|
|
93
|
+
kind: error instanceof SaveRejected ? "failed" : "unknown",
|
|
94
|
+
message: error instanceof Error ? error.message : String(error),
|
|
95
|
+
});
|
|
96
|
+
} finally {
|
|
97
|
+
setSaving(false);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
const discard = () => {
|
|
101
|
+
setDraft(() => stored());
|
|
102
|
+
setOutcome((current) => (current?.kind === "unknown" ? current : null));
|
|
103
|
+
options.onDiscard?.();
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Takes newer stored values without losing edits: a field still at its old saved value moves to the new one, an
|
|
107
|
+
* edited field keeps the person's value. `adopt` names fields that take the new value regardless.
|
|
108
|
+
*/
|
|
109
|
+
const rebase = (next: T, adopt: readonly string[] = []) => {
|
|
110
|
+
const previous = stored() as Record<string, unknown>;
|
|
111
|
+
const current = draft() as Record<string, unknown>;
|
|
112
|
+
const merged: Record<string, unknown> = { ...current };
|
|
113
|
+
for (const [key, value] of Object.entries(next as Record<string, unknown>)) {
|
|
114
|
+
if (adopt.includes(key) || JSON.stringify(current[key]) === JSON.stringify(previous[key])) merged[key] = value;
|
|
115
|
+
}
|
|
116
|
+
setStored(() => next);
|
|
117
|
+
setDraft(() => merged as T);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// Leaving the page with unsaved changes asks first, as the settings pages did before.
|
|
121
|
+
if (typeof window !== "undefined") {
|
|
122
|
+
const guard = (event: BeforeUnloadEvent) => {
|
|
123
|
+
if (state() === "saved") return;
|
|
124
|
+
event.preventDefault();
|
|
125
|
+
};
|
|
126
|
+
window.addEventListener("beforeunload", guard);
|
|
127
|
+
onCleanup(() => window.removeEventListener("beforeunload", guard));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
createEffect(() =>
|
|
131
|
+
setControls({
|
|
132
|
+
state: state(),
|
|
133
|
+
savedAt: savedAt(),
|
|
134
|
+
invalidCount: Object.keys(errors()).length,
|
|
135
|
+
save: () => void save(),
|
|
136
|
+
discard,
|
|
137
|
+
}),
|
|
138
|
+
);
|
|
139
|
+
onCleanup(() => setControls(null));
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
draft,
|
|
143
|
+
update: (change: Partial<T>) => setDraft((current) => ({ ...current, ...change })),
|
|
144
|
+
errors,
|
|
145
|
+
outcome,
|
|
146
|
+
saving,
|
|
147
|
+
save,
|
|
148
|
+
discard,
|
|
149
|
+
rebase,
|
|
150
|
+
/** The values last read from the server. */
|
|
151
|
+
stored,
|
|
152
|
+
};
|
|
153
|
+
};
|
package/src/ssr/shell-data.tsx
CHANGED
|
@@ -9,6 +9,8 @@ import type { PanelProvider } from "./app-navigation";
|
|
|
9
9
|
import type { RailApp } from "./rail-navigation";
|
|
10
10
|
|
|
11
11
|
export type ShellData = {
|
|
12
|
+
/** Canonical public Cloud origin; omitted until app.url is configured. */
|
|
13
|
+
cloudOrigin?: string;
|
|
12
14
|
panel?: { providers: PanelProvider[]; provider?: string; context: PanelPageContext | null; consent?: PanelConsentSnapshot };
|
|
13
15
|
/** The authorized navigation catalog, sorted for the request locale: the rail, the app grid and the phone menu show it. */
|
|
14
16
|
apps: RailApp[];
|
|
@@ -18,7 +20,7 @@ export type ShellData = {
|
|
|
18
20
|
home?: string;
|
|
19
21
|
legalLinks: AppLaunchpadLegalLink[];
|
|
20
22
|
/** Signed-in pages: the person, for the profile menu and the phone menu's profile actions. */
|
|
21
|
-
profile?: { name: string; avatarSrc?: string; appHref?: string };
|
|
23
|
+
profile?: { userId?: string; name: string; avatarSrc?: string; appHref?: string };
|
|
22
24
|
theme: CloudThemePreference;
|
|
23
25
|
/** The current app's accent, for the Help dialog and the phone menu, which open outside the app canvas. */
|
|
24
26
|
accent?: string;
|
package/src/styles/global.css
CHANGED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/* "Change profile picture" (@k2b/cloud/account/ui). It opens in the workspace
|
|
2
|
+
frame, which takes the whole screen on phones. On wider screens it keeps one
|
|
3
|
+
fixed size, so switching between photo, avatar, and initials or between parts
|
|
4
|
+
never resizes or moves the frame. Unlayered, like the shared frame rules. */
|
|
5
|
+
@media (min-width: 48rem) {
|
|
6
|
+
.k2b-ui .k2b-dialog.k2b-panel-dialog-frame.cloud-profile-picture-dialog {
|
|
7
|
+
width: min(var(--k2b-dialog-available-width), 67.5rem);
|
|
8
|
+
height: min(43.75rem, 92dvh, var(--k2b-dialog-available-height));
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.cloud-profile-picture {
|
|
13
|
+
display: flex;
|
|
14
|
+
min-height: 100%;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
gap: 0.875rem;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.cloud-profile-picture__body {
|
|
20
|
+
min-height: 0;
|
|
21
|
+
flex: 1;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.cloud-profile-picture__builder {
|
|
25
|
+
display: grid;
|
|
26
|
+
grid-template-columns: 20.625rem minmax(0, 1fr);
|
|
27
|
+
align-items: start;
|
|
28
|
+
gap: 1.5rem;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.cloud-profile-picture__preview {
|
|
32
|
+
position: sticky;
|
|
33
|
+
top: 0;
|
|
34
|
+
display: flex;
|
|
35
|
+
flex-direction: column;
|
|
36
|
+
align-items: center;
|
|
37
|
+
gap: 1rem;
|
|
38
|
+
padding-top: 0.25rem;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.cloud-profile-picture__stack {
|
|
42
|
+
display: contents;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.cloud-profile-picture__big {
|
|
46
|
+
display: inline-block;
|
|
47
|
+
width: 10rem;
|
|
48
|
+
height: 10rem;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.cloud-profile-picture__drawing,
|
|
52
|
+
.cloud-profile-picture__photo {
|
|
53
|
+
display: inline-block;
|
|
54
|
+
flex: none;
|
|
55
|
+
overflow: hidden;
|
|
56
|
+
border-radius: 9999px;
|
|
57
|
+
line-height: 0;
|
|
58
|
+
object-fit: cover;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.cloud-profile-picture__drawing[data-shape="square"],
|
|
62
|
+
.cloud-profile-picture__photo[data-shape="square"] {
|
|
63
|
+
border-radius: 28%;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.cloud-profile-picture__drawing > svg {
|
|
67
|
+
display: block;
|
|
68
|
+
width: 100%;
|
|
69
|
+
height: 100%;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.cloud-profile-picture__empty {
|
|
73
|
+
display: inline-grid;
|
|
74
|
+
width: 2.25rem;
|
|
75
|
+
height: 2.25rem;
|
|
76
|
+
place-items: center;
|
|
77
|
+
border-radius: 9999px;
|
|
78
|
+
color: var(--k2b-text-muted);
|
|
79
|
+
background: var(--k2b-surface-muted);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.cloud-profile-picture__empty[data-shape="square"] {
|
|
83
|
+
border-radius: 28%;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.cloud-profile-picture__sizes {
|
|
87
|
+
display: flex;
|
|
88
|
+
align-items: flex-end;
|
|
89
|
+
justify-content: center;
|
|
90
|
+
gap: 0.75rem;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.cloud-profile-picture__sizes figure {
|
|
94
|
+
display: flex;
|
|
95
|
+
margin: 0;
|
|
96
|
+
flex-direction: column;
|
|
97
|
+
align-items: center;
|
|
98
|
+
gap: 0.375rem;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.cloud-profile-picture__sizes figcaption {
|
|
102
|
+
color: var(--k2b-text-muted);
|
|
103
|
+
font-size: 0.65625rem;
|
|
104
|
+
font-variant-numeric: tabular-nums;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* The dark-theme preview carries the dark tokens of its own `data-theme`. */
|
|
108
|
+
.cloud-profile-picture__sizes--dark {
|
|
109
|
+
padding: 0.625rem 0.875rem;
|
|
110
|
+
border-radius: 0.75rem;
|
|
111
|
+
background: var(--k2b-surface);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.cloud-profile-picture__tools {
|
|
115
|
+
display: flex;
|
|
116
|
+
justify-content: center;
|
|
117
|
+
gap: 0.125rem;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.cloud-profile-picture__panel {
|
|
121
|
+
display: flex;
|
|
122
|
+
min-height: 22rem;
|
|
123
|
+
flex-direction: column;
|
|
124
|
+
gap: 1.25rem;
|
|
125
|
+
padding-top: 0.75rem;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.cloud-profile-picture__pane {
|
|
129
|
+
display: flex;
|
|
130
|
+
min-height: 26rem;
|
|
131
|
+
flex-direction: column;
|
|
132
|
+
align-items: center;
|
|
133
|
+
justify-content: center;
|
|
134
|
+
gap: 0.875rem;
|
|
135
|
+
text-align: center;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.cloud-profile-picture__pane > p {
|
|
139
|
+
max-width: 26rem;
|
|
140
|
+
margin: 0;
|
|
141
|
+
color: var(--k2b-text-secondary);
|
|
142
|
+
font-size: 0.875rem;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.cloud-profile-picture__pane > .k2b-field {
|
|
146
|
+
width: min(32.5rem, 100%);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.cloud-profile-picture__cropper {
|
|
150
|
+
width: min(28rem, 100%);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.cloud-profile-picture__row {
|
|
154
|
+
display: flex;
|
|
155
|
+
flex-wrap: wrap;
|
|
156
|
+
justify-content: center;
|
|
157
|
+
gap: 0.5rem;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.cloud-profile-picture__placeholder {
|
|
161
|
+
width: 10rem;
|
|
162
|
+
height: 10rem;
|
|
163
|
+
border-radius: 9999px;
|
|
164
|
+
background: var(--k2b-surface-muted);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* One status line that always keeps its height: who sees the picture, unsaved, or why saving failed. */
|
|
168
|
+
.cloud-profile-picture__status {
|
|
169
|
+
display: flex;
|
|
170
|
+
min-height: 1.25rem;
|
|
171
|
+
flex: 1 1 15rem;
|
|
172
|
+
align-items: center;
|
|
173
|
+
gap: 0.5rem;
|
|
174
|
+
color: var(--k2b-text-muted);
|
|
175
|
+
font-size: 0.8125rem;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.cloud-profile-picture__status[data-error="true"] {
|
|
179
|
+
color: var(--k2b-danger-text);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.cloud-profile-picture__retry {
|
|
183
|
+
padding: 0;
|
|
184
|
+
border: 0;
|
|
185
|
+
color: var(--k2b-action);
|
|
186
|
+
background: none;
|
|
187
|
+
font: inherit;
|
|
188
|
+
font-weight: 550;
|
|
189
|
+
text-decoration: underline;
|
|
190
|
+
text-underline-offset: 3px;
|
|
191
|
+
white-space: nowrap;
|
|
192
|
+
cursor: pointer;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.cloud-profile-picture__retry:focus-visible {
|
|
196
|
+
outline: var(--k2b-focus-width) solid var(--k2b-focus-ring);
|
|
197
|
+
outline-offset: var(--k2b-focus-offset);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.cloud-profile-picture__actions {
|
|
201
|
+
display: flex;
|
|
202
|
+
flex-shrink: 0;
|
|
203
|
+
gap: 0.5rem;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/* The save button keeps its width while it says "Saving", so the footer never moves. */
|
|
207
|
+
.cloud-profile-picture__save {
|
|
208
|
+
min-width: 10.75rem;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
@media (max-width: 47.999rem) {
|
|
212
|
+
.cloud-profile-picture__builder {
|
|
213
|
+
grid-template-columns: minmax(0, 1fr);
|
|
214
|
+
gap: 0.625rem;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.cloud-profile-picture__preview {
|
|
218
|
+
z-index: 1;
|
|
219
|
+
flex-direction: row;
|
|
220
|
+
justify-content: flex-start;
|
|
221
|
+
gap: 0.875rem;
|
|
222
|
+
padding: 0.25rem 0 0.625rem;
|
|
223
|
+
background: var(--k2b-surface);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.cloud-profile-picture__stack {
|
|
227
|
+
display: flex;
|
|
228
|
+
flex-direction: column;
|
|
229
|
+
align-items: flex-start;
|
|
230
|
+
gap: 0.5rem;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.cloud-profile-picture__big {
|
|
234
|
+
width: 5.25rem;
|
|
235
|
+
height: 5.25rem;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.cloud-profile-picture__sizes {
|
|
239
|
+
gap: 0.5rem;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.cloud-profile-picture__sizes figure:first-child,
|
|
243
|
+
.cloud-profile-picture__sizes--dark,
|
|
244
|
+
.cloud-profile-picture__tool-label {
|
|
245
|
+
display: none;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.cloud-profile-picture__tools {
|
|
249
|
+
justify-content: flex-start;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.cloud-profile-picture__panel {
|
|
253
|
+
min-height: 0;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/* Two lines held for the failure message and Retry, so Cancel and Save never move when saving fails. */
|
|
257
|
+
.cloud-profile-picture__status {
|
|
258
|
+
min-height: 2.5rem;
|
|
259
|
+
flex-basis: 100%;
|
|
260
|
+
align-items: flex-start;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.cloud-profile-picture__actions {
|
|
264
|
+
flex: 1;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.cloud-profile-picture__actions > .k2b-button {
|
|
268
|
+
flex: 1;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
@@ -785,6 +785,12 @@
|
|
|
785
785
|
pointer-events: none;
|
|
786
786
|
}
|
|
787
787
|
|
|
788
|
+
/* On a phone bar tab, the tab's badge slot places the count at the icon's corner. */
|
|
789
|
+
.k2b-tab-bar__badge > .cloud-app-badge {
|
|
790
|
+
position: static;
|
|
791
|
+
display: block;
|
|
792
|
+
}
|
|
793
|
+
|
|
788
794
|
/* On the corner of the rail icon, not of its 44 px target. */
|
|
789
795
|
.rail-item > .cloud-app-badge {
|
|
790
796
|
top: 0.25rem;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { AiModelProfile } from "../../ai/types";
|
|
2
|
+
import { canonicalWorkflowJson } from "../language/canonical";
|
|
3
|
+
|
|
4
|
+
export type WorkflowAiBudget = {
|
|
5
|
+
maxStepsPerSource: number;
|
|
6
|
+
tasksPerMinute: number;
|
|
7
|
+
burst: number;
|
|
8
|
+
sourcesPer24Hours: number;
|
|
9
|
+
scopeConcurrent: number;
|
|
10
|
+
globalConcurrent: number;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/** Application-owned source identity/revision, within the run's durable scope. */
|
|
14
|
+
export type WorkflowAiScope = {
|
|
15
|
+
sourceId: string;
|
|
16
|
+
sourceRevision: string;
|
|
17
|
+
budget: WorkflowAiBudget;
|
|
18
|
+
recompute?: boolean;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export class WorkflowAiAdmissionError extends Error {
|
|
22
|
+
readonly retryable: boolean;
|
|
23
|
+
constructor(readonly code: string) {
|
|
24
|
+
super(
|
|
25
|
+
code === "WORKFLOW_AI_MODEL_CHANGED"
|
|
26
|
+
? "Workflow AI model configuration changed. Retry the workflow to use the current model configuration."
|
|
27
|
+
: `${code}: Workflow AI admission is unavailable. Retry after capacity or the rolling budget recovers, or inspect the source manually.`,
|
|
28
|
+
);
|
|
29
|
+
this.retryable = code === "WORKFLOW_AI_CAPACITY" || code === "WORKFLOW_AI_RATE_LIMIT";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const workflowAiHash = (value: unknown): string => new Bun.CryptoHasher("sha256").update(canonicalWorkflowJson(value)).digest("hex");
|
|
34
|
+
|
|
35
|
+
/** v1 identifies the executor's prompt suffixes, schemas, temperature and token policy. */
|
|
36
|
+
export const workflowAiModelHash = (profile: AiModelProfile): string =>
|
|
37
|
+
workflowAiHash({
|
|
38
|
+
executor: 1,
|
|
39
|
+
profile: {
|
|
40
|
+
provider: profile.provider,
|
|
41
|
+
model: profile.model,
|
|
42
|
+
baseURL: profile.baseURL,
|
|
43
|
+
contextWindow: profile.contextWindow,
|
|
44
|
+
temperature: profile.temperature,
|
|
45
|
+
maxOutputTokens: profile.maxOutputTokens,
|
|
46
|
+
reasoningEffort: profile.reasoningEffort,
|
|
47
|
+
extraBody: profile.extraBody,
|
|
48
|
+
},
|
|
49
|
+
});
|