@k2b/cloud 0.42.0 → 0.44.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 +13 -5
- package/scripts/app-favicon.ts +7 -6
- package/scripts/build.ts +3 -0
- package/scripts/installable-assets.ts +61 -0
- package/scripts/preload.ts +3 -0
- package/src/_internal/app-access-path.ts +12 -0
- 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 +185 -12
- package/src/_internal/capability-origin.ts +4 -0
- package/src/_internal/capability-streams.ts +10 -5
- package/src/_internal/define-app.ts +159 -45
- package/src/_internal/installable-assets.ts +90 -0
- package/src/_internal/installable-head.ts +22 -0
- 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/page-responses.ts +34 -3
- package/src/_internal/process-identity.ts +7 -2
- package/src/_internal/registry-validation.ts +15 -1
- package/src/_internal/registry.ts +44 -1
- package/src/_internal/runtime-context.ts +9 -1
- package/src/_internal/runtime-watcher.ts +8 -0
- package/src/_internal/status-preserving-ssr.ts +12 -1
- package/src/access/AppAccessEditor.browser-fixture.ts +429 -0
- package/src/access/AppAccessEditor.browser-frame.tsx +68 -0
- package/src/access/AppAccessEditor.browser-harness.tsx +65 -0
- package/src/access/AppAccessEditor.tsx +380 -0
- package/src/access/PermissionEditor.browser-harness.tsx +22 -2
- package/src/access/PermissionEditor.tsx +386 -77
- package/src/access/PrincipalAvatar.tsx +63 -0
- package/src/access/PrincipalPicker.tsx +29 -8
- package/src/access/ResourceApiKeys.tsx +3 -4
- package/src/access/app-access-adapter.ts +51 -0
- package/src/access/app-access-messages.ts +172 -0
- package/src/access/messages.ts +14 -0
- package/src/access/ui.ts +4 -1
- package/src/account/Avatar.tsx +21 -3
- package/src/account/ProfilePictureDialog.tsx +699 -0
- package/src/account/profile-picture-messages.ts +269 -0
- package/src/account/ui.ts +8 -1
- 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/capability-execution.ts +5 -2
- 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/code-capability-routes.ts +1 -0
- 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 +4 -4
- package/src/ai/enrich.ts +2 -2
- package/src/ai/executor.ts +38 -27
- package/src/ai/http.ts +17 -29
- package/src/ai/index.ts +11 -5
- package/src/ai/inference-calls.ts +6 -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-slots.ts +10 -2
- package/src/ai/openai-api.ts +83 -16
- package/src/ai/personal-agent.ts +1 -1
- package/src/ai/prefs.ts +12 -10
- package/src/ai/projects-routes.ts +2 -1
- package/src/ai/projects.ts +13 -12
- package/src/ai/protocol.ts +2 -2
- package/src/ai/provider-connections.ts +161 -0
- package/src/ai/provider-errors.ts +138 -0
- package/src/ai/provider-fetch.ts +4 -0
- package/src/ai/provider-retry.ts +1 -1
- package/src/ai/provider.ts +2 -1
- package/src/ai/quota-limits.ts +36 -0
- package/src/ai/quota-provider.ts +11 -2
- package/src/ai/quota-report.ts +7 -4
- package/src/ai/quotas-migrate.ts +1 -0
- package/src/ai/quotas.ts +19 -8
- package/src/ai/recent-resources.ts +6 -0
- package/src/ai/request-headers.ts +14 -87
- package/src/ai/resource-refs.ts +6 -2
- package/src/ai/routes.ts +17 -18
- package/src/ai/runtime.ts +50 -22
- package/src/ai/settings.ts +93 -580
- 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 +29 -46
- 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 +37 -21
- package/src/ai/turn-failure.ts +24 -2
- 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 +40 -16
- package/src/api/admin-ai-quotas.ts +34 -56
- 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/admin-directory-shares.ts +55 -0
- package/src/api/admin-identity.ts +81 -2
- package/src/api/app-access.ts +85 -0
- 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 +26 -3
- package/src/api/capabilities.ts +127 -15
- package/src/api/capability-streams.ts +2 -5
- package/src/api/identity-child-invocation.ts +145 -0
- package/src/api/index.ts +11 -2
- package/src/api/mcp.ts +180 -5
- package/src/api/me.ts +38 -17
- package/src/api/pwa-phone.ts +87 -48
- 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/api/widgets.ts +1 -0
- package/src/app-pwa/authority.ts +75 -0
- package/src/app-pwa/contracts.ts +177 -0
- package/src/app-pwa/core-query-error.ts +20 -0
- package/src/app-pwa/index.ts +23 -0
- package/src/app-pwa/protocol.ts +27 -0
- package/src/app-pwa/schema.ts +59 -0
- package/src/app-pwa/service.ts +861 -0
- package/src/app-pwa/transaction.ts +338 -0
- package/src/app-pwa/transport-input.ts +57 -0
- package/src/app-pwa/transport.ts +235 -0
- package/src/approvals/messages.ts +2 -2
- package/src/browser/app-access-recovery.ts +44 -0
- package/src/browser/app-session.ts +14 -3
- package/src/browser/cloud-id.ts +86 -0
- package/src/browser/live-websocket.ts +5 -0
- package/src/browser/pwa-drafts.ts +57 -0
- package/src/browser/resource-clipboard.ts +4 -0
- package/src/browser/resource-links.ts +52 -0
- package/src/browser/resource-picker.tsx +3 -2
- package/src/capabilities/calendar-sources.ts +302 -0
- package/src/capabilities/executions.ts +10 -3
- package/src/capabilities/migrate.ts +4 -0
- package/src/capabilities/nested.ts +43 -0
- package/src/capabilities/server-invoke.ts +202 -0
- package/src/capabilities/server.ts +26 -187
- package/src/capabilities/testing.ts +5 -2
- package/src/cli/access.ts +6 -5
- package/src/cli/account.ts +57 -0
- package/src/cli/admin/agents.ts +66 -0
- 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 +7 -3
- package/src/cli/app-access.ts +60 -0
- package/src/cli/apps.ts +89 -26
- package/src/cli/avatar.ts +64 -0
- package/src/cli/capabilities.ts +22 -1
- package/src/cli/events.ts +320 -0
- package/src/cli/index.ts +1 -1
- package/src/config/env.ts +40 -0
- package/src/contracts/app-access.ts +52 -5
- package/src/contracts/app-approval.ts +8 -1
- package/src/contracts/app-details.ts +89 -0
- package/src/contracts/app-events.ts +51 -0
- package/src/contracts/app.ts +24 -5
- package/src/contracts/calendar-source.ts +274 -0
- package/src/contracts/calendar-view.ts +35 -0
- package/src/contracts/capabilities.ts +164 -3
- package/src/contracts/geo-admin.ts +2 -0
- package/src/contracts/geo-route.ts +0 -11
- package/src/contracts/geo.ts +14 -2
- package/src/contracts/index.ts +6 -0
- package/src/contracts/installable.ts +40 -0
- package/src/contracts/notification-types.ts +36 -9
- package/src/contracts/profile.ts +32 -0
- package/src/contracts/pwa-builds.ts +14 -0
- package/src/contracts/pwa-location.ts +38 -0
- package/src/contracts/pwa.ts +24 -3
- package/src/contracts/registry.ts +8 -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 +4 -0
- 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 +2 -1
- package/src/server/api/respond.ts +8 -3
- package/src/server/index.ts +1 -0
- package/src/server/middleware/app-access.ts +7 -6
- package/src/server/middleware/auth.ts +168 -66
- package/src/server/middleware/index.ts +1 -0
- package/src/server/middleware/invocation.ts +4 -2
- package/src/server/middleware/workload.ts +3 -3
- package/src/server/oauth-scope.ts +10 -0
- package/src/server/public-storage.ts +106 -0
- package/src/server/services/access.ts +8 -0
- package/src/services/accounts/app.ts +35 -3
- package/src/services/accounts/avatar.ts +342 -35
- package/src/services/accounts/base-group.ts +4 -5
- package/src/services/accounts/directory-shares.ts +157 -0
- package/src/services/accounts/entities.ts +50 -6
- package/src/services/accounts/groups.ts +3 -1
- package/src/services/accounts/identities.ts +64 -12
- package/src/services/accounts/local-groups.ts +1 -1
- package/src/services/accounts/users.ts +23 -15
- package/src/services/app-access-management.ts +519 -0
- package/src/services/app-access.ts +25 -7
- package/src/services/app-approval.ts +15 -4
- 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/admin.ts +4 -1
- package/src/services/geo/ledger.ts +47 -14
- package/src/services/geo/route-chain.ts +40 -18
- package/src/services/geo/route-estimate.ts +19 -0
- package/src/services/geo/route-plan.ts +0 -2
- package/src/services/geo/route-service.ts +57 -39
- package/src/services/geo/service.ts +51 -25
- package/src/services/identity/core-origin.ts +12 -0
- package/src/services/identity/index.ts +2 -1
- package/src/services/identity/invocation-actor.ts +50 -2
- package/src/services/identity/invocation-authority.ts +38 -1
- package/src/services/identity/invocation-token.ts +255 -50
- package/src/services/identity/issue-invocation.ts +9 -1
- package/src/services/identity/models-only-auth.ts +13 -4
- package/src/services/identity/nested-invocation.ts +128 -0
- package/src/services/image-metadata.ts +65 -1
- package/src/services/index.ts +24 -1
- package/src/services/ipa/sync.ts +6 -2
- package/src/services/ipa/users.ts +4 -1
- package/src/services/mandates/policy.ts +19 -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/service-account-credentials.ts +5 -2
- package/src/services/session/index.ts +14 -0
- package/src/services/settings/core-settings.ts +91 -47
- 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/index.ts +2 -0
- package/src/shared-text/documents.ts +25 -8
- package/src/shared-text/log.ts +1 -1
- package/src/shared-text/text-diff.ts +75 -0
- package/src/ssr/AdminArea.tsx +131 -0
- package/src/ssr/AdminLayout.tsx +11 -3
- package/src/ssr/AdminSidebar.tsx +7 -7
- package/src/ssr/AppAccessDenied.tsx +114 -0
- package/src/ssr/AppBadge.tsx +14 -0
- package/src/ssr/AppLaunchpadPanel.tsx +1 -1
- package/src/ssr/AppPwaRuntime.island.tsx +82 -0
- package/src/ssr/CloudShell.island.tsx +25 -4
- package/src/ssr/Layout.tsx +15 -7
- package/src/ssr/PwaLayout.tsx +73 -8
- package/src/ssr/PwaPartCount.island.tsx +23 -0
- package/src/ssr/PwaRuntime.island.tsx +85 -6
- package/src/ssr/SettingsSaveOutcome.tsx +58 -0
- package/src/ssr/SettingsSaveStatus.island.tsx +16 -0
- package/src/ssr/ShellRuntime.island.tsx +5 -0
- package/src/ssr/__snapshots__/Layout.unchanged.render.test.tsx.snap +3 -3
- package/src/ssr/__snapshots__/LayoutHelp.ssr.render.test.tsx.snap +1 -1
- package/src/ssr/admin-active-link.ts +16 -0
- package/src/ssr/admin-navigation.ts +26 -20
- package/src/ssr/app-badges.ts +31 -7
- package/src/ssr/app-pwa-document.tsx +16 -0
- package/src/ssr/app-pwa-messages.ts +17 -0
- package/src/ssr/index.ts +8 -0
- package/src/ssr/layout-context.ts +1 -1
- package/src/ssr/page-trail-runtime.ts +3 -1
- package/src/ssr/platform-messages.ts +9 -1
- package/src/ssr/pwa-messages.ts +10 -2
- package/src/ssr/rail-navigation.ts +5 -1
- package/src/ssr/settings-draft.ts +153 -0
- package/src/ssr/shell-data.tsx +2 -0
- 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/runtime/executor.ts +132 -33
- 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
|
@@ -10,12 +10,15 @@ import {
|
|
|
10
10
|
PaginationQuerySchema,
|
|
11
11
|
PaginationResponseSchema,
|
|
12
12
|
parsePagination,
|
|
13
|
+
ServiceAccountKindSchema,
|
|
14
|
+
ServiceAccountStatusSchema,
|
|
13
15
|
UserProfileSchema,
|
|
14
16
|
UserProviderSchema,
|
|
15
17
|
} from "../contracts";
|
|
16
|
-
import { type AuthContext, auth,
|
|
18
|
+
import { type AuthContext, auth, jsonResponse, requiresAuth, respond, userFromActor, v } from "../server";
|
|
17
19
|
import { noTransform } from "../server/middleware/no-transform";
|
|
18
20
|
import { accountsAppService as accountsService } from "../services";
|
|
21
|
+
import { directoryShares } from "../services/accounts/directory-shares";
|
|
19
22
|
|
|
20
23
|
const EntitiesListResponseSchema = z.object({
|
|
21
24
|
items: z.array(EntityListItemSchema),
|
|
@@ -33,6 +36,8 @@ const QuerySchema = z
|
|
|
33
36
|
profile: UserProfileSchema.optional(),
|
|
34
37
|
user_ids: z.string().optional(),
|
|
35
38
|
group_ids: z.string().optional(),
|
|
39
|
+
service_account_kinds: z.string().optional(),
|
|
40
|
+
service_account_status: ServiceAccountStatusSchema.optional(),
|
|
36
41
|
exclude_user_ids: z.string().optional(),
|
|
37
42
|
exclude_group_ids: z.string().optional(),
|
|
38
43
|
exclude_service_account_ids: z.string().optional(),
|
|
@@ -72,6 +77,14 @@ const parseKinds = (value?: string) => {
|
|
|
72
77
|
return { ok: false as const, message: "Invalid kinds query parameter." };
|
|
73
78
|
};
|
|
74
79
|
|
|
80
|
+
const parseServiceAccountKinds = (value?: string) => {
|
|
81
|
+
const kinds = parseCsv(value);
|
|
82
|
+
if (kinds.length === 0) return { ok: true as const, value: undefined };
|
|
83
|
+
const parsed = z.array(ServiceAccountKindSchema).safeParse([...new Set(kinds)]);
|
|
84
|
+
if (parsed.success) return { ok: true as const, value: parsed.data };
|
|
85
|
+
return { ok: false as const, message: "Invalid service_account_kinds query parameter." };
|
|
86
|
+
};
|
|
87
|
+
|
|
75
88
|
const parseUuidList = (value: string | undefined, label: string) => {
|
|
76
89
|
const ids = parseCsv(value);
|
|
77
90
|
if (ids.length === 0) return { ok: true as const, value: undefined };
|
|
@@ -87,30 +100,30 @@ const parseUuidList = (value: string | undefined, label: string) => {
|
|
|
87
100
|
|
|
88
101
|
type AccountsEntitiesRouteDependencies = {
|
|
89
102
|
authenticate?: MiddlewareHandler<AuthContext>;
|
|
90
|
-
requireUser?: MiddlewareHandler<AuthContext>;
|
|
91
103
|
listEntities?: typeof accountsService.entity.list;
|
|
104
|
+
searchShares?: typeof directoryShares.search;
|
|
92
105
|
};
|
|
93
106
|
|
|
94
107
|
export const createAccountsEntitiesRoutes = (dependencies: AccountsEntitiesRouteDependencies = {}) =>
|
|
95
108
|
new Hono<AuthContext>().use("/entities/*", noTransform()).get(
|
|
96
109
|
"/entities",
|
|
97
110
|
dependencies.authenticate ?? auth.requireRole("authenticated"),
|
|
98
|
-
dependencies.requireUser ?? auth.requireUser(),
|
|
99
111
|
describeRoute({
|
|
100
112
|
tags: ["Accounts"],
|
|
101
|
-
summary: "List
|
|
113
|
+
summary: "List visible directory entries",
|
|
102
114
|
description:
|
|
103
|
-
"List visible users and groups with SQL-backed filtering and pagination. Guest accounts see only themselves and their effective groups; relation filters require a full user account. Personal Linux groups (a user's private primary group, flagged with `personalOwner`) are left out unless `include_personal=true`; exact `group_ids` lookups and relation filters always return them.",
|
|
115
|
+
"List visible users and groups with SQL-backed filtering and pagination. Guest accounts see only themselves and their effective groups; relation filters require a full user account. Standalone and agent accounts with read scope see only explicitly shared directory entries and cannot query relationships. Personal Linux groups (a user's private primary group, flagged with `personalOwner`) are left out unless `include_personal=true`; exact `group_ids` lookups and relation filters always return them.",
|
|
104
116
|
...requiresAuth,
|
|
105
117
|
responses: {
|
|
106
118
|
200: jsonResponse(EntitiesListResponseSchema, "Paginated mixed entity list"),
|
|
107
119
|
401: jsonResponse(ErrorResponseSchema, "Authentication required"),
|
|
108
|
-
403: jsonResponse(ErrorResponseSchema, "
|
|
120
|
+
403: jsonResponse(ErrorResponseSchema, "Directory read access required; relation filters require a full user account"),
|
|
109
121
|
},
|
|
110
122
|
}),
|
|
111
123
|
v("query", QuerySchema),
|
|
112
124
|
async (c) => {
|
|
113
|
-
const
|
|
125
|
+
const actor = c.get("actor");
|
|
126
|
+
const user = userFromActor(actor);
|
|
114
127
|
const query = c.req.valid("query");
|
|
115
128
|
const usesRelationFilter = Boolean(
|
|
116
129
|
query.user_member_of_group_ids ||
|
|
@@ -119,12 +132,14 @@ export const createAccountsEntitiesRoutes = (dependencies: AccountsEntitiesRoute
|
|
|
119
132
|
query.parent_group_id ||
|
|
120
133
|
query.managed_by_user_id,
|
|
121
134
|
);
|
|
122
|
-
if (!user.roles.includes("user") && usesRelationFilter) {
|
|
135
|
+
if (user && !user.roles.includes("user") && usesRelationFilter) {
|
|
123
136
|
return respond(c, fail(err.forbidden("Guest accounts cannot use entity relation filters")));
|
|
124
137
|
}
|
|
125
138
|
const params = parsePagination(query);
|
|
126
139
|
const kinds = parseKinds(query.kinds);
|
|
127
140
|
if (!kinds.ok) return respond(c, fail(err.badInput(kinds.message)));
|
|
141
|
+
const serviceAccountKinds = parseServiceAccountKinds(query.service_account_kinds);
|
|
142
|
+
if (!serviceAccountKinds.ok) return respond(c, fail(err.badInput(serviceAccountKinds.message)));
|
|
128
143
|
const excludeUserIds = parseUuidList(query.exclude_user_ids, "exclude_user_ids");
|
|
129
144
|
if (!excludeUserIds.ok) return respond(c, fail(err.badInput(excludeUserIds.message)));
|
|
130
145
|
const excludeGroupIds = parseUuidList(query.exclude_group_ids, "exclude_group_ids");
|
|
@@ -138,20 +153,15 @@ export const createAccountsEntitiesRoutes = (dependencies: AccountsEntitiesRoute
|
|
|
138
153
|
const groupIds = parseUuidList(query.group_ids, "group_ids");
|
|
139
154
|
if (!groupIds.ok) return respond(c, fail(err.badInput(groupIds.message)));
|
|
140
155
|
|
|
141
|
-
const
|
|
142
|
-
actor: {
|
|
143
|
-
userId: user.id,
|
|
144
|
-
uid: user.uid,
|
|
145
|
-
roles: user.roles,
|
|
146
|
-
provider: user.provider,
|
|
147
|
-
},
|
|
148
|
-
pagination: { page: params.page, perPage: params.perPage },
|
|
156
|
+
const filters = {
|
|
149
157
|
search: query.search,
|
|
150
158
|
kinds: kinds.value,
|
|
151
159
|
provider: query.provider,
|
|
152
160
|
profile: query.profile,
|
|
153
161
|
userIds: userIds.value,
|
|
154
162
|
groupIds: groupIds.value,
|
|
163
|
+
serviceAccountKinds: serviceAccountKinds.value,
|
|
164
|
+
serviceAccountStatus: query.service_account_status,
|
|
155
165
|
excludeUserIds: excludeUserIds.value,
|
|
156
166
|
excludeGroupIds: excludeGroupIds.value,
|
|
157
167
|
excludeServiceAccountIds: excludeServiceAccountIds.value,
|
|
@@ -162,6 +172,20 @@ export const createAccountsEntitiesRoutes = (dependencies: AccountsEntitiesRoute
|
|
|
162
172
|
managedByUserId: query.managed_by_user_id,
|
|
163
173
|
recursive: query.recursive === "true",
|
|
164
174
|
includePersonal: query.include_personal === "true",
|
|
175
|
+
};
|
|
176
|
+
if (!user) {
|
|
177
|
+
const result = await (dependencies.searchShares ?? directoryShares.search)({
|
|
178
|
+
actor,
|
|
179
|
+
requestId: c.req.header("x-request-id"),
|
|
180
|
+
query: { ...filters, page: params.page, perPage: params.perPage },
|
|
181
|
+
});
|
|
182
|
+
if (!result.ok) return respond(c, result);
|
|
183
|
+
return c.json({ items: result.data.items, pagination: createPagination(params, result.data.total) });
|
|
184
|
+
}
|
|
185
|
+
const result = await (dependencies.listEntities ?? accountsService.entity.list)({
|
|
186
|
+
actor: { userId: user.id, uid: user.uid, roles: user.roles, provider: user.provider },
|
|
187
|
+
pagination: { page: params.page, perPage: params.perPage },
|
|
188
|
+
...filters,
|
|
165
189
|
});
|
|
166
190
|
|
|
167
191
|
return respond(c, {
|
|
@@ -1,15 +1,11 @@
|
|
|
1
|
+
import { sql } from "bun";
|
|
1
2
|
import { Hono, type MiddlewareHandler } from "hono";
|
|
2
|
-
import { describeRoute } from "hono-openapi";
|
|
3
|
-
import { z } from "zod";
|
|
4
3
|
import { AiBackgroundCostError, backgroundCostState, releaseBackgroundCostStop } from "../ai/inference-calls";
|
|
5
|
-
import { setAiModelPricing } from "../ai/model-pricing";
|
|
6
|
-
import { AiModelRequestSettingsInvalid, getAiModelRequestSettings, setAiModelRequestSettings } from "../ai/model-request-settings";
|
|
7
4
|
import { quotaAdminConfig, quotaReport } from "../ai/quota-report";
|
|
8
5
|
import { AiQuotaError, aiQuotas } from "../ai/quotas";
|
|
9
6
|
import { readAiSettingsState } from "../ai/settings";
|
|
10
|
-
import { type AuthContext, auth,
|
|
11
|
-
import {
|
|
12
|
-
import { AiModelRequestSettingsSchema, AiModelRequestSettingsUpdateSchema } from "../shared/ai-model-request-settings";
|
|
7
|
+
import { type AuthContext, auth, v } from "../server";
|
|
8
|
+
import { hasBillableAiPricing } from "../shared/ai-costs";
|
|
13
9
|
import {
|
|
14
10
|
AiQuotaConfigSchema,
|
|
15
11
|
AiQuotaIdentitySchema,
|
|
@@ -23,8 +19,22 @@ const subject = (p: { type: "user" | "service_account"; id: string }) =>
|
|
|
23
19
|
export const createAdminAiQuotaRoutes = (authenticate: MiddlewareHandler<AuthContext> = auth.requireRole("admin")) =>
|
|
24
20
|
new Hono<AuthContext>()
|
|
25
21
|
.use("*", authenticate)
|
|
26
|
-
.onError((error, c) => {
|
|
27
|
-
if (error instanceof
|
|
22
|
+
.onError(async (error, c) => {
|
|
23
|
+
if (error instanceof AiQuotaError && error.code === "quota_conflict") {
|
|
24
|
+
const current = await quotaAdminConfig();
|
|
25
|
+
const [change] = await sql<
|
|
26
|
+
{ id: string | null; name: string | null; changedAt: string }[]
|
|
27
|
+
>`SELECT u.id,COALESCE(NULLIF(u.display_name,''),u.uid) AS name,to_char(c.created_at AT TIME ZONE 'UTC','YYYY-MM-DD"T"HH24:MI:SS.MS"Z"') AS "changedAt" FROM ai.cost_changes c LEFT JOIN auth.users u ON u.id=c.actor_id WHERE c.revision=${current.revision} ORDER BY c.created_at DESC LIMIT 1`;
|
|
28
|
+
return c.json(
|
|
29
|
+
{
|
|
30
|
+
code: "conflict",
|
|
31
|
+
current,
|
|
32
|
+
changedBy: change?.id ? { id: change.id, name: change.name ?? change.id } : null,
|
|
33
|
+
changedAt: change?.changedAt ?? new Date(0).toISOString(),
|
|
34
|
+
},
|
|
35
|
+
409,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
28
38
|
if (error instanceof AiQuotaError || error instanceof AiBackgroundCostError)
|
|
29
39
|
return c.json({ error: error.code, message: error.message }, 409);
|
|
30
40
|
throw error;
|
|
@@ -33,54 +43,22 @@ export const createAdminAiQuotaRoutes = (authenticate: MiddlewareHandler<AuthCon
|
|
|
33
43
|
.get("/models", async (c) =>
|
|
34
44
|
c.json({
|
|
35
45
|
unit: (await aiQuotas.config()).unit ?? "EUR",
|
|
36
|
-
models: (await readAiSettingsState()).profiles
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
models: (await readAiSettingsState()).profiles
|
|
47
|
+
.filter((m, index, profiles) => {
|
|
48
|
+
const representative =
|
|
49
|
+
profiles.find((candidate) => candidate.modelId === m.modelId && hasBillableAiPricing(candidate.pricing)) ??
|
|
50
|
+
profiles.find((candidate) => candidate.modelId === m.modelId);
|
|
51
|
+
return profiles.indexOf(representative!) === index;
|
|
52
|
+
})
|
|
53
|
+
.map((m) => ({
|
|
54
|
+
id: m.modelId,
|
|
55
|
+
label: m.label,
|
|
56
|
+
pricing: m.pricing,
|
|
57
|
+
enabled: m.enabled,
|
|
58
|
+
capabilities: m.capabilities,
|
|
59
|
+
})),
|
|
43
60
|
}),
|
|
44
61
|
)
|
|
45
|
-
.put(
|
|
46
|
-
"/models/:id/pricing",
|
|
47
|
-
v("json", z.object({ pricing: AiModelPricingSchema.nullable(), expected: AiModelPricingSchema.nullable() }).strict()),
|
|
48
|
-
async (c) => {
|
|
49
|
-
const data = c.req.valid("json");
|
|
50
|
-
return c.json(await setAiModelPricing(c.req.param("id")!, data.pricing, data.expected, c.get("user")!.id));
|
|
51
|
-
},
|
|
52
|
-
)
|
|
53
|
-
.get(
|
|
54
|
-
"/models/:id/settings",
|
|
55
|
-
describeRoute({
|
|
56
|
-
tags: ["Administration"],
|
|
57
|
-
summary: "Read masked model request settings",
|
|
58
|
-
...requiresAdmin,
|
|
59
|
-
responses: { 200: jsonResponse(AiModelRequestSettingsSchema, "Model request settings (header names only)") },
|
|
60
|
-
}),
|
|
61
|
-
async (c) => {
|
|
62
|
-
c.header("Cache-Control", "no-store");
|
|
63
|
-
return c.json(await getAiModelRequestSettings(c.req.param("id")!));
|
|
64
|
-
},
|
|
65
|
-
)
|
|
66
|
-
.put(
|
|
67
|
-
"/models/:id/settings",
|
|
68
|
-
describeRoute({
|
|
69
|
-
tags: ["Administration"],
|
|
70
|
-
summary: "Patch model request settings with a revision guard",
|
|
71
|
-
...requiresAdmin,
|
|
72
|
-
responses: {
|
|
73
|
-
200: jsonResponse(AiModelRequestSettingsSchema, "Updated model request settings (header names only)"),
|
|
74
|
-
400: jsonResponse(z.object({ message: z.string() }), "Invalid model request settings"),
|
|
75
|
-
409: jsonResponse(z.object({ message: z.string(), error: z.string() }), "Model request settings changed"),
|
|
76
|
-
},
|
|
77
|
-
}),
|
|
78
|
-
v("json", AiModelRequestSettingsUpdateSchema),
|
|
79
|
-
async (c) => {
|
|
80
|
-
c.header("Cache-Control", "no-store");
|
|
81
|
-
return c.json(await setAiModelRequestSettings(c.req.param("id")!, c.req.valid("json"), c.get("user")!.id));
|
|
82
|
-
},
|
|
83
|
-
)
|
|
84
62
|
.get("/background", async (c) => c.json(await backgroundCostState()))
|
|
85
63
|
.post("/background/release", async (c) => c.json(await releaseBackgroundCostStop(c.get("user")!.id)))
|
|
86
64
|
.get("/report", v("query", AiQuotaReportQuerySchema), async (c) => c.json(await quotaReport(c.req.valid("query"))))
|
|
@@ -92,7 +70,7 @@ export const createAdminAiQuotaRoutes = (authenticate: MiddlewareHandler<AuthCon
|
|
|
92
70
|
previous = await aiQuotas.config();
|
|
93
71
|
if (
|
|
94
72
|
config.rules.some(
|
|
95
|
-
(r) => r.scope !== "*" && !models.some((m) => m.
|
|
73
|
+
(r) => r.scope !== "*" && !models.some((m) => m.modelId === r.scope) && !previous.rules.some((old) => old.scope === r.scope),
|
|
96
74
|
)
|
|
97
75
|
)
|
|
98
76
|
return c.json({ message: "Select an enabled chat model with nonzero prices or all chat models." }, 400);
|
|
@@ -25,7 +25,7 @@ export const createAdminAiUsageRoutes = (
|
|
|
25
25
|
.get(
|
|
26
26
|
"/facets",
|
|
27
27
|
describeRoute({ summary: "Find AI usage filter values", tags: ["Admin AI usage"] }),
|
|
28
|
-
v("query", AiUsageQuerySchema.extend({ field: z.enum(["userId", "
|
|
28
|
+
v("query", AiUsageQuerySchema.extend({ field: z.enum(["userId", "modelId", "providerModel", "appId", "task"]) }).strict()),
|
|
29
29
|
async (c) => {
|
|
30
30
|
const q = c.req.valid("query");
|
|
31
31
|
return respond(c, ok({ items: await service.facets(q.field, q.search ?? "", q) }));
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { sql } from "bun";
|
|
2
|
+
import { Hono, type MiddlewareHandler } from "hono";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { setAiCredential } from "../ai/credentials";
|
|
5
|
+
import { AiModelAccessDraftSchema } from "../ai/model-access";
|
|
6
|
+
import { AiConfigError, aiModelCatalog, assertAiRevision, lockAiModelCatalog } from "../ai/model-catalog";
|
|
7
|
+
import { deleteAiCatalogEntry } from "../ai/model-delete";
|
|
8
|
+
import { readAiAdminOverview } from "../ai/model-overview";
|
|
9
|
+
import { aiModelUsage } from "../ai/model-usage";
|
|
10
|
+
import {
|
|
11
|
+
AiProviderListError,
|
|
12
|
+
listProviderModels,
|
|
13
|
+
recordAiProviderStatus,
|
|
14
|
+
storedProviderConnection,
|
|
15
|
+
testAiProvider,
|
|
16
|
+
} from "../ai/provider-connections";
|
|
17
|
+
import { getAiRequestHeaders, setAiRequestHeaders } from "../ai/request-headers";
|
|
18
|
+
import { type AuthContext, auth, v } from "../server";
|
|
19
|
+
import { ConfigConflict } from "../services/config-conflict";
|
|
20
|
+
import { ImageMetadataError } from "../services/image-metadata";
|
|
21
|
+
import {
|
|
22
|
+
AI_MODEL_SLOTS,
|
|
23
|
+
AiDeleteModelSchema,
|
|
24
|
+
AiModelInputSchema,
|
|
25
|
+
AiModelPatchSchema,
|
|
26
|
+
AiProviderInputSchema,
|
|
27
|
+
AiProviderPatchSchema,
|
|
28
|
+
AiVariantInputSchema,
|
|
29
|
+
AiVariantPatchSchema,
|
|
30
|
+
} from "../shared/ai-models";
|
|
31
|
+
import { AiRequestHeadersSchema, patchAiRequestHeaders, providerSupportsRequestHeaders } from "../shared/ai-request-options";
|
|
32
|
+
|
|
33
|
+
const revision = { revision: z.number().int().positive() };
|
|
34
|
+
const providerPatch = AiProviderPatchSchema.extend(revision).strict();
|
|
35
|
+
const modelPatch = AiModelPatchSchema.extend({ ...revision, access: AiModelAccessDraftSchema.optional() }).strict();
|
|
36
|
+
const variantPatch = AiVariantPatchSchema.extend(revision).strict();
|
|
37
|
+
const draftConnection = AiProviderInputSchema.omit({ id: true, label: true, dataBoundary: true, enabled: true, logo: true })
|
|
38
|
+
.extend({ apiKey: z.string().optional(), headers: AiRequestHeadersSchema.optional() })
|
|
39
|
+
.strict();
|
|
40
|
+
export const createAdminAiRoutes = (authenticate: MiddlewareHandler<AuthContext> = auth.requireRole("admin")) =>
|
|
41
|
+
new Hono<AuthContext>()
|
|
42
|
+
.use("*", authenticate)
|
|
43
|
+
.onError((error, c) => {
|
|
44
|
+
if (error instanceof ImageMetadataError) return c.json({ code: error.code, message: error.message }, 422);
|
|
45
|
+
if (error instanceof ConfigConflict)
|
|
46
|
+
return c.json({ code: error.code, current: error.current, changedBy: error.changedBy, changedAt: error.changedAt }, 409);
|
|
47
|
+
if (error instanceof AiConfigError)
|
|
48
|
+
return c.json({ code: error.code, message: error.message }, error.code === "model_not_found" ? 404 : 400);
|
|
49
|
+
if (error instanceof AiProviderListError) return c.json({ code: error.code, message: error.message }, 502);
|
|
50
|
+
if (error instanceof z.ZodError) return c.json({ code: "invalid_configuration", message: z.prettifyError(error) }, 400);
|
|
51
|
+
throw error;
|
|
52
|
+
})
|
|
53
|
+
.get("/overview", async (c) => c.json(await readAiAdminOverview()))
|
|
54
|
+
.get("/providers", async (c) => c.json({ providers: await aiModelCatalog.providers() }))
|
|
55
|
+
.post("/providers", v("json", AiProviderInputSchema), async (c) =>
|
|
56
|
+
c.json(await aiModelCatalog.createProvider(c.req.valid("json"), c.get("user").id), 201),
|
|
57
|
+
)
|
|
58
|
+
.post("/providers/test", v("json", draftConnection), async (c) => {
|
|
59
|
+
const data = c.req.valid("json");
|
|
60
|
+
return c.json(await testAiProvider({ ...data, headers: patchAiRequestHeaders({}, data.headers ?? {}) }, c.req.raw.signal));
|
|
61
|
+
})
|
|
62
|
+
.get("/providers/:id", async (c) => {
|
|
63
|
+
return c.json(await aiModelCatalog.providerDetail(c.req.param("id")!));
|
|
64
|
+
})
|
|
65
|
+
.patch("/providers/:id", v("json", providerPatch), async (c) => {
|
|
66
|
+
const { revision, ...patch } = c.req.valid("json");
|
|
67
|
+
return c.json(await aiModelCatalog.updateProvider(c.req.param("id")!, patch, revision, c.get("user").id));
|
|
68
|
+
})
|
|
69
|
+
.put("/providers/:id/credential", v("json", z.object({ ...revision, apiKey: z.string() }).strict()), async (c) => {
|
|
70
|
+
const id = c.req.param("id")!,
|
|
71
|
+
data = c.req.valid("json");
|
|
72
|
+
const saved = await sql.begin(async (db) => {
|
|
73
|
+
await lockAiModelCatalog(db);
|
|
74
|
+
const current = await aiModelCatalog.providerDetail(id, db);
|
|
75
|
+
await assertAiRevision(current, data.revision, db);
|
|
76
|
+
await setAiCredential(id, data.apiKey, db);
|
|
77
|
+
await db`UPDATE ai.providers SET revision=revision+1,updated_by=${c.get("user").id}::uuid,updated_at=clock_timestamp(),status='unknown',status_code=NULL,status_message=NULL,status_checked_at=NULL WHERE id=${id}`;
|
|
78
|
+
return aiModelCatalog.provider(id, db);
|
|
79
|
+
});
|
|
80
|
+
return c.json(saved);
|
|
81
|
+
})
|
|
82
|
+
.put("/providers/:id/headers", v("json", z.object({ ...revision, headers: AiRequestHeadersSchema }).strict()), async (c) => {
|
|
83
|
+
const id = c.req.param("id")!,
|
|
84
|
+
data = c.req.valid("json");
|
|
85
|
+
return c.json(
|
|
86
|
+
await sql.begin(async (db) => {
|
|
87
|
+
await lockAiModelCatalog(db);
|
|
88
|
+
const current = await aiModelCatalog.providerDetail(id, db);
|
|
89
|
+
await assertAiRevision(current, data.revision, db);
|
|
90
|
+
if (!providerSupportsRequestHeaders(current.type))
|
|
91
|
+
throw new AiConfigError("invalid_configuration", "Extra headers require vLLM or an OpenAI-compatible provider.");
|
|
92
|
+
await setAiRequestHeaders(id, patchAiRequestHeaders(await getAiRequestHeaders(id, db), data.headers), db);
|
|
93
|
+
await db`UPDATE ai.providers SET revision=revision+1,updated_by=${c.get("user").id}::uuid,updated_at=clock_timestamp(),status='unknown',status_code=NULL,status_message=NULL,status_checked_at=NULL WHERE id=${id}`;
|
|
94
|
+
return aiModelCatalog.provider(id, db);
|
|
95
|
+
}),
|
|
96
|
+
);
|
|
97
|
+
})
|
|
98
|
+
.post("/providers/:id/test", async (c) => {
|
|
99
|
+
const provider = await aiModelCatalog.provider(c.req.param("id")!);
|
|
100
|
+
const result = await testAiProvider(await storedProviderConnection(provider), c.req.raw.signal);
|
|
101
|
+
await recordAiProviderStatus(provider.id, result, true);
|
|
102
|
+
return c.json(result);
|
|
103
|
+
})
|
|
104
|
+
.get("/providers/:id/available-models", async (c) =>
|
|
105
|
+
c.json({
|
|
106
|
+
models: await listProviderModels(
|
|
107
|
+
await storedProviderConnection(await aiModelCatalog.provider(c.req.param("id")!)),
|
|
108
|
+
c.req.raw.signal,
|
|
109
|
+
),
|
|
110
|
+
}),
|
|
111
|
+
)
|
|
112
|
+
.post("/providers/:id/models", v("json", z.union([AiModelInputSchema, z.array(AiModelInputSchema).min(1)])), async (c) => {
|
|
113
|
+
const data = c.req.valid("json");
|
|
114
|
+
return c.json(
|
|
115
|
+
{ models: await aiModelCatalog.createModels(c.req.param("id")!, Array.isArray(data) ? data : [data], c.get("user").id) },
|
|
116
|
+
201,
|
|
117
|
+
);
|
|
118
|
+
})
|
|
119
|
+
.get("/models", async (c) => c.json({ models: await aiModelCatalog.models() }))
|
|
120
|
+
.get("/models/:id", async (c) => {
|
|
121
|
+
return c.json(await aiModelCatalog.modelDetail(c.req.param("id")!));
|
|
122
|
+
})
|
|
123
|
+
.patch("/models/:id", v("json", modelPatch), async (c) => {
|
|
124
|
+
const { revision, access, ...patch } = c.req.valid("json");
|
|
125
|
+
const model = await aiModelCatalog.updateModel(c.req.param("id")!, patch, revision, c.get("user").id, access);
|
|
126
|
+
return c.json(model);
|
|
127
|
+
})
|
|
128
|
+
.get("/models/:id/usage", async (c) => {
|
|
129
|
+
const model = await aiModelCatalog.model(c.req.param("id")!);
|
|
130
|
+
return c.json(
|
|
131
|
+
await aiModelUsage(
|
|
132
|
+
(await aiModelCatalog.variants()).filter((v) => v.modelId === model.id).map((v) => v.id),
|
|
133
|
+
[model.id],
|
|
134
|
+
),
|
|
135
|
+
);
|
|
136
|
+
})
|
|
137
|
+
.post("/models/:id/variants", v("json", AiVariantInputSchema), async (c) =>
|
|
138
|
+
c.json(await aiModelCatalog.createVariant(c.req.param("id")!, c.req.valid("json"), c.get("user").id), 201),
|
|
139
|
+
)
|
|
140
|
+
.patch("/variants/:id", v("json", variantPatch), async (c) => {
|
|
141
|
+
const { revision, ...patch } = c.req.valid("json");
|
|
142
|
+
return c.json(await aiModelCatalog.updateVariant(c.req.param("id")!, patch, revision, c.get("user").id));
|
|
143
|
+
})
|
|
144
|
+
.get("/variants/:id/usage", async (c) => {
|
|
145
|
+
const variant = await aiModelCatalog.variant(c.req.param("id")!);
|
|
146
|
+
return c.json(await aiModelUsage([variant.id], [variant.modelId]));
|
|
147
|
+
})
|
|
148
|
+
.delete("/providers/:id", v("json", AiDeleteModelSchema), async (c) =>
|
|
149
|
+
c.json(await deleteAiCatalogEntry("provider", c.req.param("id")!, c.req.valid("json"), c.get("user").id)),
|
|
150
|
+
)
|
|
151
|
+
.delete("/models/:id", v("json", AiDeleteModelSchema), async (c) =>
|
|
152
|
+
c.json(await deleteAiCatalogEntry("model", c.req.param("id")!, c.req.valid("json"), c.get("user").id)),
|
|
153
|
+
)
|
|
154
|
+
.delete("/variants/:id", v("json", AiDeleteModelSchema), async (c) =>
|
|
155
|
+
c.json(await deleteAiCatalogEntry("variant", c.req.param("id")!, c.req.valid("json"), c.get("user").id)),
|
|
156
|
+
)
|
|
157
|
+
.get("/assignments", async (c) => c.json({ assignments: await aiModelCatalog.assignmentsForAdmin() }))
|
|
158
|
+
.put(
|
|
159
|
+
"/assignments/:slot",
|
|
160
|
+
v("param", z.object({ slot: z.enum(AI_MODEL_SLOTS) })),
|
|
161
|
+
v("json", z.object({ ...revision, variantId: z.string().nullable() }).strict()),
|
|
162
|
+
async (c) =>
|
|
163
|
+
c.json(
|
|
164
|
+
await aiModelCatalog.assign(
|
|
165
|
+
c.req.valid("param").slot,
|
|
166
|
+
c.req.valid("json").variantId,
|
|
167
|
+
c.req.valid("json").revision,
|
|
168
|
+
c.get("user").id,
|
|
169
|
+
),
|
|
170
|
+
),
|
|
171
|
+
);
|
|
172
|
+
export default createAdminAiRoutes();
|
|
173
|
+
export type AdminAiApi = ReturnType<typeof createAdminAiRoutes>;
|