@k2b/cloud 0.6.0 → 0.8.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 +22 -11
- package/scripts/README.md +10 -0
- package/scripts/browser-performance.ts +14 -0
- package/scripts/build-pdf-renderer.ts +39 -0
- package/scripts/build.ts +20 -35
- package/scripts/preload.ts +16 -12
- package/scripts/sync-recovery-smoke.ts +22 -6
- package/src/_internal/app-presentation.ts +14 -2
- package/src/_internal/build-metadata.ts +8 -1
- package/src/_internal/capabilities.ts +131 -7
- package/src/_internal/capability-streams.ts +81 -0
- package/src/_internal/define-app.ts +80 -37
- 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/readiness.ts +4 -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 +18 -151
- package/src/access/PrincipalPicker.tsx +110 -0
- package/src/access/ResourceApiKeys.tsx +1 -1
- package/src/access/ui.ts +1 -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 +201 -0
- package/src/ai/browser.ts +4 -0
- package/src/ai/capabilities.ts +55 -36
- package/src/ai/capability-execution.ts +47 -12
- package/src/ai/chat/blocks.tsx +229 -90
- package/src/ai/chat/builtin-tools.tsx +2 -4
- 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 +83 -3
- package/src/ai/chat/message-actions.tsx +8 -7
- package/src/ai/chat/message-utils.ts +25 -1
- package/src/ai/chat/messages.ts +2 -0
- package/src/ai/chat/presentation.tsx +31 -47
- package/src/ai/chat/primitives.tsx +43 -1
- package/src/ai/chat/tool-disclosure.tsx +21 -2
- package/src/ai/chat/tool-groups.ts +58 -0
- package/src/ai/chat-quotas.ts +25 -0
- package/src/ai/chat-task-contracts.ts +14 -10
- package/src/ai/chat-tasks.ts +297 -43
- package/src/ai/client/controller.ts +165 -39
- package/src/ai/client/file-source.ts +12 -6
- package/src/ai/client/live-connection.ts +1 -0
- package/src/ai/client/projection.ts +29 -11
- package/src/ai/client/transport.ts +6 -0
- package/src/ai/code-capability-routes.ts +105 -0
- package/src/ai/code-capability-transport.ts +39 -0
- package/src/ai/code-execution.ts +53 -0
- package/src/ai/code-mode-skill.ts +118 -0
- package/src/ai/code-runtime-tools.ts +179 -0
- package/src/ai/code-source-contracts.ts +270 -0
- package/src/ai/code-source-tools.ts +188 -0
- package/src/ai/compaction.ts +27 -6
- package/src/ai/data-analysis-skill.ts +10 -0
- package/src/ai/default-tools.ts +99 -3
- package/src/ai/dictation-runtime.ts +171 -0
- package/src/ai/dictations.ts +188 -0
- package/src/ai/draft-content.ts +49 -0
- package/src/ai/enrich.ts +6 -0
- package/src/ai/executor.ts +189 -33
- package/src/ai/fetch-file-tool.ts +2 -2
- package/src/ai/file-content-version.ts +27 -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 +8 -1
- package/src/ai/files-store.ts +212 -71
- package/src/ai/fixtures/vision-encrypted.pdf +0 -0
- package/src/ai/grids-skill.ts +42 -10
- package/src/ai/http.ts +26 -8
- package/src/ai/index.ts +26 -3
- package/src/ai/inference-calls.ts +197 -0
- package/src/ai/live-events.ts +5 -1
- package/src/ai/live.ts +1 -1
- package/src/ai/markdown-pdf-tool.ts +2 -2
- package/src/ai/memories.ts +30 -27
- package/src/ai/memory-learning-runs.ts +1 -1
- package/src/ai/memory-learning.ts +64 -32
- package/src/ai/memory-tool.ts +1 -5
- package/src/ai/memory-workflow-evidence.ts +9 -4
- package/src/ai/message-queue.ts +178 -0
- package/src/ai/migrate.ts +189 -11
- package/src/ai/model-pricing.ts +43 -0
- package/src/ai/pdf-render-worker.ts +75 -0
- package/src/ai/pdf-render.fixture.ts +28 -0
- package/src/ai/pdf-render.ts +77 -0
- package/src/ai/projects-routes.ts +18 -43
- package/src/ai/projects.ts +33 -14
- package/src/ai/protocol.ts +3 -0
- package/src/ai/provider.ts +1 -8
- package/src/ai/quota-provider.ts +192 -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 +241 -11
- package/src/ai/run-timeout.ts +12 -0
- package/src/ai/runtime-tools.ts +7 -5
- package/src/ai/runtime.ts +74 -17
- package/src/ai/settings.ts +36 -8
- package/src/ai/short-id.ts +4 -5
- package/src/ai/skill-catalog.ts +0 -10
- package/src/ai/skill-search.ts +58 -0
- package/src/ai/skill-seeds.ts +114 -20
- package/src/ai/skill-tool.ts +21 -5
- package/src/ai/skills-routes.ts +49 -4
- package/src/ai/skills.ts +358 -163
- package/src/ai/store.ts +416 -201
- package/src/ai/stream.ts +5 -0
- package/src/ai/structured-runs.ts +4 -3
- package/src/ai/structured.ts +12 -1
- package/src/ai/system-prompt.ts +14 -2
- package/src/ai/task-contracts.ts +84 -0
- package/src/ai/task-execution.ts +92 -0
- package/src/ai/timeline.ts +2 -5
- package/src/ai/todo-contracts.ts +32 -0
- package/src/ai/todo-tool.ts +19 -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 +3 -0
- package/src/ai/usage.ts +118 -118
- package/src/ai/vision-tool.ts +60 -10
- package/src/api/admin-ai-projects.ts +2 -2
- package/src/api/admin-ai-quotas.ts +80 -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-linux-identities.ts +10 -4
- package/src/api/admin-rail.ts +58 -0
- package/src/api/announcements.ts +14 -0
- package/src/api/app-approval.ts +2 -2
- package/src/api/auth.ts +1 -1
- package/src/api/capabilities.ts +324 -43
- package/src/api/capability-streams.ts +262 -0
- package/src/api/help.ts +8 -44
- package/src/api/index.ts +8 -2
- package/src/api/mcp.ts +45 -34
- 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 +67 -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 +6 -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 +116 -35
- 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 +77 -0
- package/src/cli/admin/data.ts +17 -0
- package/src/cli/admin/index.ts +10 -1
- package/src/cli/admin/instance.ts +2 -2
- package/src/cli/admin/linux.ts +4 -2
- package/src/cli/admin/sync.ts +1 -1
- package/src/cli/capabilities.ts +42 -0
- package/src/cli/capability-streams.ts +25 -0
- package/src/cli/index.ts +3 -2
- package/src/config/define-env.ts +97 -0
- package/src/config/env.ts +147 -51
- package/src/config/index.ts +3 -1
- package/src/contracts/app.ts +23 -3
- package/src/contracts/capabilities.ts +75 -5
- package/src/contracts/capability-streams.ts +46 -0
- package/src/contracts/commands.ts +49 -0
- package/src/contracts/index.ts +5 -2
- package/src/contracts/rail-admin.ts +38 -0
- package/src/contracts/rail-preferences.ts +16 -0
- package/src/contracts/registry.ts +5 -27
- package/src/contracts/settings-types.ts +1 -1
- package/src/contracts/shared.ts +18 -5
- package/src/contracts/web-vitals.ts +27 -0
- package/src/desktop/index.ts +1 -0
- package/src/desktop/solid.tsx +1 -1
- package/src/index.ts +8 -6
- 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/ratelimit.ts +1 -1
- package/src/server/services/access-revision.ts +10 -0
- package/src/server/services/access.ts +3 -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/notification-sender.ts +12 -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/app-approval.ts +11 -10
- 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 +8 -5
- package/src/services/identity/key-ring.ts +1 -1
- package/src/services/identity/runtime-config.ts +6 -5
- package/src/services/index.ts +15 -0
- package/src/services/ipa/posix.ts +1 -1
- package/src/services/ipa/sync.ts +1 -1
- 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 +21 -4
- package/src/services/mandates/policy.ts +165 -2
- package/src/services/notifications/batches.ts +125 -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 +131 -72
- package/src/services/pdf/index.ts +11 -8
- 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 +29 -0
- package/src/services/session/index.ts +15 -2
- package/src/services/session/user.ts +62 -37
- package/src/services/settings/app.ts +5 -1
- package/src/services/settings/core-settings.ts +26 -35
- package/src/services/settings/defaults.ts +5 -0
- package/src/services/settings/index.ts +3 -3
- package/src/services/settings/store.ts +58 -84
- package/src/services/webauthn.ts +1 -1
- 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 +3 -0
- package/src/shared/markdown/extensions/info-blocks.ts +1 -1
- package/src/ssr/AdminLayout.tsx +7 -5
- package/src/ssr/AdminSidebar.tsx +30 -16
- package/src/ssr/AppLaunchpad.island.tsx +33 -136
- 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 +22 -23
- package/src/ssr/{HotkeysHelpRail.island.tsx → LayoutHelpTrigger.island.tsx} +18 -12
- package/src/ssr/LayoutRail.tsx +17 -9
- package/src/ssr/MinimalLayout.tsx +1 -1
- 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 +11 -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/index.ts +2 -2
- package/src/workflows/ai/runtime.ts +19 -90
- package/src/workflows/ai/store.ts +2 -0
- package/src/workflows/ai/types.ts +6 -82
- 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 +4 -2
- 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/src/ai/kit-skill.ts +0 -22
- package/src/ai/usage-migrate.ts +0 -81
- package/src/browser/notification-target.ts +0 -27
- package/src/contracts/notification-live.ts +0 -66
- package/src/server/services/freeipa/test-certificates.ts +0 -44
- package/src/services/notifications/live.ts +0 -50
- package/src/services/session/test-fixture.ts +0 -18
- package/src/ssr/BrowserNotifications.island.tsx +0 -135
- package/src/ssr/islands/SearchBar.island.tsx +0 -80
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { CloudResourceRefSchema } from "../../contracts";
|
|
3
|
+
import { CapabilitySearchTagManifestSchema } from "../../contracts/capabilities";
|
|
3
4
|
|
|
4
5
|
const TAG_PATTERN = /^[^\s#]+$/;
|
|
5
6
|
const SEARCH_BASE_URL = "https://cloud.invalid";
|
|
@@ -29,26 +30,35 @@ const TagArraySchema = z.preprocess(
|
|
|
29
30
|
.transform((tags) => [...new Set(tags.map((tag) => tag.toLowerCase()))]),
|
|
30
31
|
);
|
|
31
32
|
|
|
32
|
-
export const SearchQuerySchema = z
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
.
|
|
43
|
-
.optional()
|
|
44
|
-
.
|
|
45
|
-
|
|
46
|
-
|
|
33
|
+
export const SearchQuerySchema = z
|
|
34
|
+
.object({
|
|
35
|
+
q: z
|
|
36
|
+
.string()
|
|
37
|
+
.max(500)
|
|
38
|
+
.optional()
|
|
39
|
+
.default("")
|
|
40
|
+
.transform((query) => query.trim()),
|
|
41
|
+
tag: TagArraySchema.optional().default([]),
|
|
42
|
+
app: z.string().trim().min(1).max(120).optional(),
|
|
43
|
+
scope_tag: z.string().trim().min(1).max(64).regex(TAG_PATTERN).toLowerCase().optional(),
|
|
44
|
+
scope_type: CloudResourceRefSchema.shape.type.optional(),
|
|
45
|
+
scope_id: CloudResourceRefSchema.shape.id.optional(),
|
|
46
|
+
require_reader: z
|
|
47
|
+
.enum(["true"])
|
|
48
|
+
.optional()
|
|
49
|
+
.transform((value) => value === "true"),
|
|
50
|
+
provider_limit: z.coerce.number().int().min(1).max(30).optional().default(10),
|
|
51
|
+
})
|
|
52
|
+
.refine((value) => Boolean(value.scope_type) === Boolean(value.scope_id), { message: "Both scope_type and scope_id are required" })
|
|
53
|
+
.refine((value) => !value.scope_tag || (Boolean(value.app) && !value.scope_type), {
|
|
54
|
+
message: "A tag context requires an app and cannot include a resource scope",
|
|
55
|
+
});
|
|
47
56
|
|
|
48
57
|
export const SearchAppSchema = z.object({
|
|
49
58
|
id: z.string(),
|
|
50
59
|
name: z.string(),
|
|
51
60
|
icon: z.string(),
|
|
61
|
+
tags: z.array(CapabilitySearchTagManifestSchema).optional(),
|
|
52
62
|
});
|
|
53
63
|
|
|
54
64
|
export const SearchItemSchema = z.object({
|
|
@@ -72,6 +82,7 @@ export const SearchResponseSchema = z.object({
|
|
|
72
82
|
items: z.array(SearchItemSchema),
|
|
73
83
|
apps: z.array(SearchAppSchema),
|
|
74
84
|
unsupportedTags: z.array(z.string()).optional(),
|
|
85
|
+
failedApps: z.array(z.string()).optional(),
|
|
75
86
|
});
|
|
76
87
|
|
|
77
88
|
export type SearchApp = z.infer<typeof SearchAppSchema>;
|
package/src/api/search.bench.ts
CHANGED
|
@@ -16,7 +16,7 @@ const PROVIDER_LATENCY_MS = 5;
|
|
|
16
16
|
const ISSUER = "https://cloud.test";
|
|
17
17
|
|
|
18
18
|
const capabilities = defineCapabilities({
|
|
19
|
-
protocolVersion:
|
|
19
|
+
protocolVersion: 2,
|
|
20
20
|
types: { item: { title: "Item", description: "A benchmark item." } },
|
|
21
21
|
queries: {
|
|
22
22
|
search: {
|
package/src/api/search.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Hono, type MiddlewareHandler } from "hono";
|
|
2
2
|
import { describeRoute } from "hono-openapi";
|
|
3
3
|
import { readBoundedJson } from "../_internal/bounded-json";
|
|
4
|
+
import { resolveCapabilityManifestPresentation } from "../_internal/capabilities";
|
|
4
5
|
import { listCapabilities } from "../_internal/registry";
|
|
5
6
|
import type { CapabilityRegistryEntry } from "../contracts";
|
|
6
7
|
import {
|
|
@@ -20,6 +21,7 @@ import { LOCALE_HEADER } from "../shared/locale";
|
|
|
20
21
|
import { type SearchItem, SearchItemSchema, SearchQuerySchema, SearchResponseSchema } from "./search/schemas";
|
|
21
22
|
|
|
22
23
|
const log = logger("search");
|
|
24
|
+
const SearchProviderResultSchema = capabilityResultSchema(UniversalSearchDataSchema);
|
|
23
25
|
|
|
24
26
|
/**
|
|
25
27
|
* Maximum items returned to the client after merging across providers.
|
|
@@ -28,7 +30,6 @@ const log = logger("search");
|
|
|
28
30
|
const GLOBAL_RESULT_LIMIT = 30;
|
|
29
31
|
const PROVIDER_CONCURRENCY = 8;
|
|
30
32
|
const PROVIDER_TIMEOUT_MS = 8_000;
|
|
31
|
-
const SIGNING_TIMEOUT_MS = 500;
|
|
32
33
|
|
|
33
34
|
type HttpSearchProvider = {
|
|
34
35
|
appId: string;
|
|
@@ -36,6 +37,7 @@ type HttpSearchProvider = {
|
|
|
36
37
|
appIcon: string;
|
|
37
38
|
endpoint: string;
|
|
38
39
|
tags: string[];
|
|
40
|
+
scopeTypes: string[];
|
|
39
41
|
typeIds: Set<string>;
|
|
40
42
|
schemaHash: string;
|
|
41
43
|
};
|
|
@@ -55,6 +57,7 @@ const getSearchProviders = (entries: CapabilityRegistryEntry[]): HttpSearchProvi
|
|
|
55
57
|
appIcon: entry.appIcon,
|
|
56
58
|
endpoint: `${entry.endpoint}/queries/${encodeURIComponent(query.localId)}`,
|
|
57
59
|
tags: query.universalSearch.tags.flatMap((tag) => [tag.tag, ...(tag.aliases ?? [])]),
|
|
60
|
+
scopeTypes: (query.universalSearch.scopeTypes ?? []).map((type) => `${entry.appId}.${type}`),
|
|
58
61
|
typeIds: new Set(entry.manifest.types.map((type) => `${entry.appId}.${type.localId}`)),
|
|
59
62
|
schemaHash: query.schemaHash,
|
|
60
63
|
},
|
|
@@ -125,8 +128,8 @@ const settleBounded = async <T, R>(items: readonly T[], concurrency: number, run
|
|
|
125
128
|
const waitWithin = <T>(value: Promise<T>, signal: AbortSignal): Promise<T> =>
|
|
126
129
|
new Promise<T>((resolve, reject) => {
|
|
127
130
|
const aborted = () => reject(signal.reason);
|
|
128
|
-
|
|
129
|
-
|
|
131
|
+
// The work has already started; observe its rejection even if the client
|
|
132
|
+
// disconnected before we began waiting.
|
|
130
133
|
value.then(
|
|
131
134
|
(result) => {
|
|
132
135
|
signal.removeEventListener("abort", aborted);
|
|
@@ -137,6 +140,8 @@ const waitWithin = <T>(value: Promise<T>, signal: AbortSignal): Promise<T> =>
|
|
|
137
140
|
reject(error);
|
|
138
141
|
},
|
|
139
142
|
);
|
|
143
|
+
if (signal.aborted) return aborted();
|
|
144
|
+
signal.addEventListener("abort", aborted, { once: true });
|
|
140
145
|
});
|
|
141
146
|
|
|
142
147
|
/**
|
|
@@ -183,11 +188,26 @@ export const createSearchRoutes = (dependencies: SearchRouteDependencies = {}) =
|
|
|
183
188
|
);
|
|
184
189
|
}
|
|
185
190
|
const providers = getSearchProviders(entries);
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
+
const requestLocale = preferredLocale(c.req.raw.headers) ?? "en";
|
|
192
|
+
const apps = entries
|
|
193
|
+
.flatMap((entry) => {
|
|
194
|
+
const manifest = resolveCapabilityManifestPresentation(entry.manifest, entry.presentation, requestLocale);
|
|
195
|
+
const queries = manifest.queries.filter((operation) => operation.universalSearch);
|
|
196
|
+
if (!queries.length) return [];
|
|
197
|
+
return [
|
|
198
|
+
{
|
|
199
|
+
id: entry.appId,
|
|
200
|
+
name: entry.appName,
|
|
201
|
+
icon: entry.appIcon,
|
|
202
|
+
tags: [
|
|
203
|
+
...new Map(
|
|
204
|
+
queries.flatMap((operation) => (operation.universalSearch?.tags ?? []).map((tag) => [tag.tag, tag] as const)),
|
|
205
|
+
).values(),
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
];
|
|
209
|
+
})
|
|
210
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
191
211
|
const readableTypes = new Set(
|
|
192
212
|
entries.flatMap((entry) => entry.manifest.types.filter((type) => type.reader).map((type) => `${entry.appId}.${type.localId}`)),
|
|
193
213
|
);
|
|
@@ -199,11 +219,19 @@ export const createSearchRoutes = (dependencies: SearchRouteDependencies = {}) =
|
|
|
199
219
|
// returned to the client so it can render a helpful empty state.
|
|
200
220
|
const knownTags = new Set(providers.flatMap((p) => p.tags));
|
|
201
221
|
const unsupportedTags = query.tag.filter((t) => !knownTags.has(t));
|
|
202
|
-
const
|
|
222
|
+
const scope = query.scope_type && query.scope_id ? { type: query.scope_type, id: query.scope_id } : undefined;
|
|
223
|
+
const appProviders = providers.filter(
|
|
224
|
+
(provider) =>
|
|
225
|
+
(!query.app || provider.appId === query.app) &&
|
|
226
|
+
(!scope || provider.scopeTypes.includes(scope.type)) &&
|
|
227
|
+
(!query.scope_tag || provider.tags.includes(query.scope_tag)),
|
|
228
|
+
);
|
|
229
|
+
if ((scope || query.scope_tag) && !appProviders.length)
|
|
230
|
+
return c.json({ code: "INVALID_SEARCH_SCOPE", message: "Search scope is unavailable" }, 400);
|
|
203
231
|
const active =
|
|
204
232
|
query.tag.length === 0 ? appProviders : appProviders.filter((provider) => provider.tags.some((tag) => query.tag.includes(tag)));
|
|
205
233
|
|
|
206
|
-
if (query.q.length === 0 && query.tag.length === 0 && !query.app) {
|
|
234
|
+
if (query.q.length === 0 && query.tag.length === 0 && !query.app && !scope) {
|
|
207
235
|
return c.json({ query: "", count: 0, items: [], apps });
|
|
208
236
|
}
|
|
209
237
|
|
|
@@ -225,7 +253,6 @@ export const createSearchRoutes = (dependencies: SearchRouteDependencies = {}) =
|
|
|
225
253
|
// The same request-wide budget covers signing and provider I/O.
|
|
226
254
|
const providerDeadline = AbortSignal.timeout(PROVIDER_TIMEOUT_MS);
|
|
227
255
|
const fanoutSignal = AbortSignal.any([c.req.raw.signal, providerDeadline]);
|
|
228
|
-
const signingSignal = AbortSignal.any([fanoutSignal, AbortSignal.timeout(SIGNING_TIMEOUT_MS)]);
|
|
229
256
|
// Guard the whole issuance batch once, then reuse the prepared signer for
|
|
230
257
|
// every target. This is one Postgres check per outer search request, not
|
|
231
258
|
// one check per provider.
|
|
@@ -251,17 +278,17 @@ export const createSearchRoutes = (dependencies: SearchRouteDependencies = {}) =
|
|
|
251
278
|
signer,
|
|
252
279
|
issuer: signer.issuer,
|
|
253
280
|
}),
|
|
254
|
-
|
|
281
|
+
fanoutSignal,
|
|
255
282
|
),
|
|
256
283
|
),
|
|
257
|
-
{ signal:
|
|
284
|
+
{ signal: fanoutSignal, timeoutMs: PROVIDER_TIMEOUT_MS },
|
|
258
285
|
),
|
|
259
|
-
|
|
286
|
+
fanoutSignal,
|
|
260
287
|
);
|
|
261
288
|
}
|
|
262
289
|
} catch {
|
|
263
290
|
log.warn("Search invocation authority unavailable", {
|
|
264
|
-
reason:
|
|
291
|
+
reason: fanoutSignal.aborted ? "signing_deadline" : "signing_guard_failed",
|
|
265
292
|
});
|
|
266
293
|
return c.json(
|
|
267
294
|
{ code: CAPABILITY_FRAMEWORK_ERROR_CODES.appUnavailable, message: "Search invocation authority is currently unavailable" },
|
|
@@ -275,7 +302,9 @@ export const createSearchRoutes = (dependencies: SearchRouteDependencies = {}) =
|
|
|
275
302
|
// Scope tags to those this provider declared. Apps no longer need
|
|
276
303
|
// their own gate — the framework guarantees they only see tags
|
|
277
304
|
// they understand.
|
|
278
|
-
const scopedTags = query.tag.filter((
|
|
305
|
+
const scopedTags = [...new Set([...(query.scope_tag ? [query.scope_tag] : []), ...query.tag])].filter((tag) =>
|
|
306
|
+
provider.tags.includes(tag),
|
|
307
|
+
);
|
|
279
308
|
|
|
280
309
|
const headers = await (async () => {
|
|
281
310
|
const signed = await signedInvocations?.[index];
|
|
@@ -305,6 +334,7 @@ export const createSearchRoutes = (dependencies: SearchRouteDependencies = {}) =
|
|
|
305
334
|
input: {
|
|
306
335
|
query: query.q,
|
|
307
336
|
tags: scopedTags,
|
|
337
|
+
...(scope ? { scope } : {}),
|
|
308
338
|
limit: effectiveProviderLimit,
|
|
309
339
|
},
|
|
310
340
|
}),
|
|
@@ -317,7 +347,7 @@ export const createSearchRoutes = (dependencies: SearchRouteDependencies = {}) =
|
|
|
317
347
|
|
|
318
348
|
const parsedBody = await readBoundedJson(res, CAPABILITY_MAX_RESULT_BYTES);
|
|
319
349
|
if (!parsedBody.ok) throw new Error(`Search provider ${provider.appId} returned invalid or oversized JSON`);
|
|
320
|
-
const envelope =
|
|
350
|
+
const envelope = SearchProviderResultSchema.safeParse(parsedBody.data);
|
|
321
351
|
if (!envelope.success) throw new Error(`Search provider ${provider.appId} returned an invalid capability result`);
|
|
322
352
|
const results = envelope.data.data;
|
|
323
353
|
const validItems: SearchItem[] = [];
|
|
@@ -399,6 +429,13 @@ export const createSearchRoutes = (dependencies: SearchRouteDependencies = {}) =
|
|
|
399
429
|
items: sliced,
|
|
400
430
|
apps,
|
|
401
431
|
...(unsupportedTags.length > 0 ? { unsupportedTags } : {}),
|
|
432
|
+
...(settled.some((result) => result.status === "rejected")
|
|
433
|
+
? {
|
|
434
|
+
failedApps: [
|
|
435
|
+
...new Set(active.filter((_, index) => settled[index]?.status === "rejected").map((provider) => provider.appId)),
|
|
436
|
+
],
|
|
437
|
+
}
|
|
438
|
+
: {}),
|
|
402
439
|
});
|
|
403
440
|
},
|
|
404
441
|
);
|