@k2b/cloud 0.41.0 → 0.42.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 +6 -5
- package/src/_internal/bounded-json.ts +14 -2
- package/src/_internal/capabilities.ts +87 -8
- package/src/_internal/capability-reviews.ts +6 -0
- package/src/_internal/capability-timeout.ts +71 -0
- package/src/_internal/define-app.ts +145 -89
- package/src/_internal/http-drain.ts +56 -0
- package/src/_internal/registry-validation.ts +3 -0
- package/src/_internal/runtime-context.ts +1 -0
- package/src/_internal/status-preserving-ssr.ts +2 -0
- package/src/access/PermissionEditor.tsx +16 -17
- package/src/access/PrincipalPicker.tsx +45 -10
- package/src/access/messages.ts +8 -0
- package/src/access/principal-label.ts +18 -0
- package/src/access/ui.ts +1 -0
- package/src/account/EntitySearch.tsx +5 -4
- package/src/ai/action-review.ts +2 -1
- package/src/ai/app-skill-store.ts +1 -1
- package/src/ai/capabilities.ts +21 -3
- package/src/ai/capability-execution.ts +14 -12
- package/src/ai/chat/action-card.ts +7 -1
- package/src/ai/chat/action-messages.ts +14 -0
- package/src/ai/chat/action-question.browser-harness.tsx +12 -0
- package/src/ai/chat/action-question.tsx +202 -22
- package/src/ai/chat/action-text.ts +1 -1
- package/src/ai/chat-tasks.ts +2 -1
- package/src/ai/client/controller.ts +150 -32
- package/src/ai/client/transport.ts +14 -2
- package/src/ai/code-capability-routes.ts +27 -23
- package/src/ai/code-runtime-tools.ts +14 -16
- package/src/ai/code-source-tools.ts +16 -18
- package/src/ai/executor.ts +56 -21
- package/src/ai/fetch-file-tool.ts +67 -76
- package/src/ai/http.ts +1 -0
- package/src/ai/migrate.ts +13 -0
- package/src/ai/open-questions.ts +4 -8
- package/src/ai/projects.ts +1 -2
- package/src/ai/protocol.ts +2 -2
- package/src/ai/recent-resources.ts +12 -5
- package/src/ai/review-content.ts +77 -0
- package/src/ai/routes.ts +69 -31
- package/src/ai/runtime.ts +1 -0
- package/src/ai/skill-tool.ts +17 -6
- package/src/ai/skills-routes.ts +72 -27
- package/src/ai/skills.ts +139 -60
- package/src/ai/store.ts +168 -33
- package/src/ai/types.ts +10 -3
- package/src/ai/usage.ts +2 -2
- package/src/api/admin-core-settings.ts +15 -2
- package/src/api/admin-geo.ts +54 -0
- package/src/api/admin-linux-identities.ts +6 -3
- package/src/api/admin-outgoing-mail.ts +29 -15
- package/src/api/approvals-loader.ts +3 -3
- package/src/api/approvals.ts +51 -0
- package/src/api/apps.ts +4 -2
- package/src/api/capabilities.ts +258 -181
- package/src/api/capability-streams.ts +21 -13
- package/src/api/cli-plugins.ts +4 -1
- package/src/api/geo-glyphs.ts +11 -0
- package/src/api/geo.ts +128 -0
- package/src/api/help.ts +12 -4
- package/src/api/index.ts +5 -0
- package/src/api/mcp.ts +46 -38
- package/src/api/me-panel-consent.ts +8 -5
- package/src/api/search.ts +21 -14
- package/src/api/sync-ops.ts +19 -13
- package/src/api/widgets.ts +42 -18
- package/src/approvals/Approvals.tsx +5 -3
- package/src/browser/CloudResourceSearch.browser-harness.tsx +53 -1
- package/src/browser/CloudResourceSearch.tsx +3 -13
- package/src/browser/PanelLeaveNotice.tsx +18 -0
- package/src/browser/navigation-search.ts +78 -25
- package/src/browser/panel-leave.ts +69 -0
- package/src/browser/panel-messages.ts +12 -0
- package/src/browser/panel-slot.ts +173 -24
- package/src/browser/panel.ts +3 -0
- package/src/browser/resource-search-messages.ts +2 -0
- package/src/capabilities/client.ts +28 -1
- package/src/capabilities/executions.ts +22 -3
- package/src/capabilities/geo-client.ts +57 -0
- package/src/capabilities/response.ts +2 -1
- package/src/capabilities/review-storage.ts +24 -0
- package/src/capabilities/reviews.ts +17 -3
- package/src/capabilities/server.ts +36 -16
- package/src/cli/access.ts +15 -23
- package/src/cli/admin/linux.ts +13 -2
- package/src/cli/admin/outgoing-mail.ts +26 -4
- package/src/cli/capabilities.ts +19 -0
- package/src/config/env.ts +0 -6
- package/src/contracts/app-access.ts +48 -0
- package/src/contracts/app.ts +4 -1
- package/src/contracts/approvals.ts +2 -2
- package/src/contracts/avatar.ts +186 -0
- package/src/contracts/capabilities.ts +148 -54
- package/src/contracts/geo-admin.ts +127 -0
- package/src/contracts/geo-map.ts +19 -0
- package/src/contracts/geo-route.ts +168 -0
- package/src/contracts/geo.ts +248 -0
- package/src/contracts/index.ts +4 -0
- package/src/contracts/outgoing-mail.ts +17 -4
- package/src/contracts/registry.ts +2 -0
- package/src/events/app-access.ts +68 -0
- package/src/events/live-delivery.ts +128 -0
- package/src/events/live.ts +14 -130
- package/src/geo/CloudMap.tsx +95 -0
- package/src/geo/map-messages.ts +17 -0
- package/src/geo/ui.ts +1 -0
- package/src/index.ts +2 -0
- package/src/server/client-ip.ts +36 -0
- package/src/server/index.ts +3 -4
- package/src/server/middleware/app-access.ts +312 -0
- package/src/server/middleware/auth.ts +1 -1
- package/src/server/middleware/invocation.ts +11 -0
- package/src/server/middleware/rate-limit.ts +4 -4
- package/src/server/services/access.ts +13 -9
- package/src/server/services/index.ts +0 -2
- package/src/server/services/services.ts +0 -2
- package/src/services/accounts/posix.ts +21 -7
- package/src/services/allowed-network-fetch.ts +16 -10
- package/src/services/app-access-metrics.ts +24 -0
- package/src/services/app-access.ts +123 -0
- package/src/services/app-discovery.ts +27 -0
- package/src/services/approvals/content.ts +40 -0
- package/src/services/approvals/index.ts +14 -6
- package/src/services/approvals/list.ts +6 -11
- package/src/services/approvals/question.ts +11 -7
- package/src/services/downstream-budget.ts +87 -0
- package/src/services/geo/admin.ts +262 -0
- package/src/services/geo/bucket.ts +67 -0
- package/src/services/geo/cache.ts +57 -0
- package/src/services/geo/costs.ts +5 -0
- package/src/services/geo/errors.ts +41 -0
- package/src/services/geo/fixtures/README.md +17 -0
- package/src/services/geo/fixtures/along.json +355 -0
- package/src/services/geo/fixtures/autocomplete.json +218 -0
- package/src/services/geo/fixtures/geocode.json +129 -0
- package/src/services/geo/fixtures/places.json +355 -0
- package/src/services/geo/fixtures/reverse.json +61 -0
- package/src/services/geo/fixtures/routing.json +57 -0
- package/src/services/geo/geoapify-route.ts +93 -0
- package/src/services/geo/geoapify.ts +281 -0
- package/src/services/geo/ledger.ts +122 -0
- package/src/services/geo/map.ts +160 -0
- package/src/services/geo/migrate.ts +25 -0
- package/src/services/geo/polyline.ts +184 -0
- package/src/services/geo/route-chain.ts +145 -0
- package/src/services/geo/route-costs.ts +9 -0
- package/src/services/geo/route-geometry.ts +39 -0
- package/src/services/geo/route-plan.ts +172 -0
- package/src/services/geo/route-service.ts +433 -0
- package/src/services/geo/service.ts +300 -0
- package/src/services/geo/settings.ts +49 -0
- package/src/services/help/index.ts +16 -7
- package/src/services/help/types.ts +6 -1
- package/src/services/identity/invocation-actor.ts +20 -2
- package/src/services/identity/issue-invocation.ts +67 -0
- package/src/services/identity/key-ring.ts +5 -3
- package/src/services/identity/runtime-config.ts +23 -0
- package/src/services/index.ts +9 -0
- package/src/services/mandates/index.ts +40 -3
- package/src/services/network-security.ts +6 -3
- package/src/services/network-transport.ts +99 -0
- package/src/services/notifications/catalog.ts +3 -2
- package/src/services/notifications/runtime.ts +1 -0
- package/src/services/notifications/web-push-transport.ts +45 -38
- package/src/services/outgoing-mail/dispatcher.ts +6 -1
- package/src/services/outgoing-mail/index.ts +55 -1
- package/src/services/outgoing-mail/messages.ts +82 -3
- package/src/services/outgoing-mail/resolve-host.ts +2 -2
- package/src/services/outgoing-mail/retention.ts +13 -1
- package/src/services/outgoing-mail/store.ts +36 -6
- package/src/services/outgoing-mail/test-send.ts +34 -4
- package/src/services/outgoing-mail/transport.ts +1 -1
- package/src/services/pdf/gotenberg.ts +11 -38
- package/src/services/pdf/template-preview.ts +1 -1
- package/src/services/public-http.ts +28 -40
- package/src/services/settings/app.ts +4 -1
- package/src/services/settings/core-settings.ts +88 -0
- package/src/services/settings/defaults.ts +8 -2
- package/src/services/settings/errors.ts +11 -0
- package/src/services/settings/geo-budget.ts +40 -0
- package/src/services/settings/index.ts +4 -3
- package/src/services/settings/store.ts +25 -17
- package/src/services/weather/geo.ts +29 -111
- package/src/services/weather/index.ts +4 -3
- package/src/services/weather/locations.ts +8 -5
- package/src/services/weather/migrate.ts +1 -0
- package/src/services/weather/settings.ts +0 -16
- package/src/shared/__snapshots__/avatar-art.test.ts.snap +5 -0
- package/src/shared/avatar-art.ts +400 -0
- package/src/shared/index.ts +2 -0
- package/src/shared/network-address.ts +22 -3
- package/src/shared/template-rendering.ts +36 -4
- package/src/shared-text/documents.ts +1 -1
- package/src/shared-text/index.ts +2 -0
- package/src/shared-text/log.ts +51 -3
- package/src/shared-text/snapshots.ts +340 -0
- package/src/ssr/AdminLayout.tsx +4 -2
- package/src/ssr/CloudShell.island.tsx +6 -0
- package/src/ssr/Layout.tsx +65 -15
- package/src/ssr/LayoutHelp.browser-harness.tsx +1 -1
- package/src/ssr/LayoutHelp.tsx +3 -2
- package/src/ssr/PanelRuntime.island.tsx +11 -7
- package/src/ssr/PwaLayout.tsx +2 -2
- package/src/ssr/RailApps.tsx +6 -0
- package/src/ssr/ShellRuntime.island.tsx +23 -7
- package/src/ssr/__snapshots__/Layout.unchanged.render.test.tsx.snap +151 -0
- package/src/ssr/__snapshots__/LayoutHelp.ssr.render.test.tsx.snap +6 -6
- package/src/ssr/admin-navigation.ts +1 -0
- package/src/ssr/app-badges.ts +6 -1
- package/src/ssr/compact-shell.browser-harness.ts +44 -5
- package/src/ssr/help.ts +3 -2
- package/src/ssr/index.ts +1 -1
- package/src/ssr/layout-context.ts +2 -0
- package/src/ssr/mobile-menu-history.ts +4 -0
- package/src/ssr/page-swap-fixture/Column.island.tsx +15 -0
- package/src/ssr/page-swap-fixture/ProviderProbe.island.tsx +31 -0
- package/src/ssr/page-swap.browser-harness.tsx +177 -0
- package/src/ssr/page-swap.ts +28 -0
- package/src/ssr/page-trail-runtime.ts +32 -13
- package/src/ssr/panel-consent.ts +2 -2
- package/src/ssr/panel-slot.browser-harness.tsx +3 -0
- package/src/ssr/platform-messages.ts +8 -0
- package/src/ssr/runtime.ts +17 -0
- package/src/ssr/shell-data.tsx +10 -2
- package/src/styles/global.css +70 -6
- package/src/server/services/geo.ts +0 -215
package/src/api/capabilities.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
capabilityActionReviewIssues,
|
|
9
9
|
capabilityQueryChecksActionOutcome,
|
|
10
10
|
} from "../_internal/capability-reviews";
|
|
11
|
+
import { capabilityTimeoutMs, createCapabilityDeadline, withCapabilityRequestTimeout } from "../_internal/capability-timeout";
|
|
11
12
|
import { getCapability, listApps } from "../_internal/registry";
|
|
12
13
|
import { resolveCommandLink } from "../capabilities/command-link";
|
|
13
14
|
import {
|
|
@@ -28,6 +29,7 @@ import {
|
|
|
28
29
|
CAPABILITY_MAX_CATALOG_BYTES,
|
|
29
30
|
CAPABILITY_MAX_REQUEST_BYTES,
|
|
30
31
|
CAPABILITY_MAX_RESULT_BYTES,
|
|
32
|
+
CAPABILITY_MAX_REVIEW_RESPONSE_BYTES,
|
|
31
33
|
CAPABILITY_ORIGIN_HEADER,
|
|
32
34
|
CAPABILITY_PROTOCOL_VERSION,
|
|
33
35
|
CapabilityActionReviewDataSchema,
|
|
@@ -42,33 +44,34 @@ import {
|
|
|
42
44
|
import { CapabilityStreamSchema } from "../contracts/capability-streams";
|
|
43
45
|
import { CommandLinkSchema, CommandRequestSchema } from "../contracts/commands";
|
|
44
46
|
import type { AppRegistryEntry, CapabilityRegistryEntry } from "../contracts/registry";
|
|
47
|
+
import { getLocale, LOCALE_HEADER, preferredLocale } from "../server/locale";
|
|
45
48
|
import {
|
|
46
49
|
type AuthContext,
|
|
47
50
|
auth,
|
|
48
|
-
getLocale,
|
|
49
51
|
jsonResponse,
|
|
50
|
-
LOCALE_HEADER,
|
|
51
|
-
preferredLocale,
|
|
52
52
|
type RequestAuthority,
|
|
53
53
|
rejectReservedWorkloadCredential,
|
|
54
54
|
requiresAuth,
|
|
55
55
|
v,
|
|
56
|
-
} from "../server";
|
|
56
|
+
} from "../server/middleware";
|
|
57
|
+
import { AppAccessDeniedError, appAccessDeniedBody } from "../server/middleware/app-access";
|
|
57
58
|
import { noTransform } from "../server/middleware/no-transform";
|
|
58
|
-
import {
|
|
59
|
+
import { appAccessAuthority, canUseApp } from "../services/app-access";
|
|
60
|
+
import { discoverableApps } from "../services/app-discovery";
|
|
59
61
|
import { invocationAuthorityFromRequest } from "../services/identity/invocation-authority";
|
|
60
62
|
import { capabilityInvocationOperation } from "../services/identity/invocation-operations";
|
|
61
63
|
import type { InvocationAuthority } from "../services/identity/invocation-token";
|
|
62
|
-
import { normalizeInvocationRequestId
|
|
64
|
+
import { normalizeInvocationRequestId } from "../services/identity/invocation-token";
|
|
65
|
+
import { issueInvocationToken as signInvocationToken } from "../services/identity/issue-invocation";
|
|
63
66
|
import { withActiveIdentitySigner } from "../services/identity/key-ring";
|
|
67
|
+
import { logger } from "../services/logging";
|
|
64
68
|
import { withMandateIssueAuthority } from "../services/mandates";
|
|
69
|
+
import * as settings from "../services/settings";
|
|
65
70
|
import { resolveAppPresentation } from "../shared/app-presentation";
|
|
66
71
|
import { capabilityMessages } from "../shared/capability-messages";
|
|
67
72
|
import { dispatchCapabilityStream, sealCapabilityStream } from "./capability-streams";
|
|
68
73
|
|
|
69
74
|
const log = logger("capabilities");
|
|
70
|
-
export const QUERY_TIMEOUT_MS = 15_000;
|
|
71
|
-
const ACTION_TIMEOUT_MS = 30_000;
|
|
72
75
|
const DEFAULT_CATALOG_LIMIT = 10;
|
|
73
76
|
const MAX_CATALOG_LIMIT = 25;
|
|
74
77
|
const MAX_SCHEMA_VALIDATORS = 512;
|
|
@@ -109,12 +112,20 @@ export type CapabilityRouteDependencies = {
|
|
|
109
112
|
withMandateIssueAuthority?: typeof withMandateIssueAuthority;
|
|
110
113
|
queryTimeoutMs?: number;
|
|
111
114
|
actionTimeoutMs?: number;
|
|
115
|
+
reviewTimeoutMs?: number;
|
|
112
116
|
appUrl?: () => Promise<string>;
|
|
113
117
|
};
|
|
114
118
|
|
|
115
119
|
export type CapabilityDispatchDependencies = Pick<
|
|
116
120
|
CapabilityRouteDependencies,
|
|
117
|
-
|
|
121
|
+
| "getCapability"
|
|
122
|
+
| "fetch"
|
|
123
|
+
| "queryTimeoutMs"
|
|
124
|
+
| "actionTimeoutMs"
|
|
125
|
+
| "reviewTimeoutMs"
|
|
126
|
+
| "signInvocation"
|
|
127
|
+
| "withActiveSigner"
|
|
128
|
+
| "withMandateIssueAuthority"
|
|
118
129
|
>;
|
|
119
130
|
|
|
120
131
|
type MandatedCapabilityAuthority = {
|
|
@@ -148,13 +159,14 @@ export const loadCapabilityCatalogPage = async (
|
|
|
148
159
|
query: { cursor?: string; limit: number },
|
|
149
160
|
dependencies: Pick<CapabilityRouteDependencies, "listApps" | "getCapability"> = {},
|
|
150
161
|
locale?: string,
|
|
162
|
+
authority: RequestAuthority | null = null,
|
|
151
163
|
): Promise<CapabilityCatalog> => {
|
|
152
164
|
if (!Number.isInteger(query.limit) || query.limit < 1 || query.limit > MAX_CATALOG_LIMIT) {
|
|
153
165
|
throw new RangeError(`Capability catalog limit must be between 1 and ${MAX_CATALOG_LIMIT}`);
|
|
154
166
|
}
|
|
155
167
|
const registry = dependencies.listApps ?? listApps;
|
|
156
168
|
const capabilityLookup = dependencies.getCapability ?? getCapability;
|
|
157
|
-
const entries = await registry();
|
|
169
|
+
const entries = await discoverableApps(authority, await registry());
|
|
158
170
|
const liveApps = entries
|
|
159
171
|
.filter((entry) => entry.capabilities?.protocolVersion === CAPABILITY_PROTOCOL_VERSION)
|
|
160
172
|
.sort((left, right) => left.id.localeCompare(right.id));
|
|
@@ -213,23 +225,6 @@ const capabilityJsonResponse = (body: unknown, status: number): Response =>
|
|
|
213
225
|
headers: { "content-type": "application/json" },
|
|
214
226
|
});
|
|
215
227
|
|
|
216
|
-
const waitWithin = <T>(value: Promise<T>, signal: AbortSignal): Promise<T> =>
|
|
217
|
-
new Promise<T>((resolve, reject) => {
|
|
218
|
-
const aborted = () => reject(signal.reason);
|
|
219
|
-
if (signal.aborted) return aborted();
|
|
220
|
-
signal.addEventListener("abort", aborted, { once: true });
|
|
221
|
-
value.then(
|
|
222
|
-
(result) => {
|
|
223
|
-
signal.removeEventListener("abort", aborted);
|
|
224
|
-
resolve(result);
|
|
225
|
-
},
|
|
226
|
-
(error) => {
|
|
227
|
-
signal.removeEventListener("abort", aborted);
|
|
228
|
-
reject(error);
|
|
229
|
-
},
|
|
230
|
-
);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
228
|
export type CapabilityDispatchParams = {
|
|
234
229
|
/** Trusted Core continuation binding; never read from client headers. */
|
|
235
230
|
continuation?: string;
|
|
@@ -272,14 +267,17 @@ const runCapabilityDispatch = async (
|
|
|
272
267
|
params: CapabilityDispatchParams,
|
|
273
268
|
requestId: string,
|
|
274
269
|
observed: CapabilityDispatchObservation,
|
|
270
|
+
deadline: ReturnType<typeof createCapabilityDeadline>,
|
|
275
271
|
): Promise<CapabilityDispatchOutcome> => {
|
|
272
|
+
const { signal } = deadline;
|
|
276
273
|
const messages = capabilityMessages(params.locale ?? preferredLocale(params.request.headers));
|
|
277
274
|
const registryEntry = params.dependencies?.getCapability ?? getCapability;
|
|
278
275
|
const fetchUpstream = params.dependencies?.fetch ?? globalThis.fetch;
|
|
279
276
|
let entry: CapabilityRegistryEntry | null;
|
|
280
277
|
try {
|
|
281
|
-
entry = await registryEntry(params.appId);
|
|
278
|
+
entry = await deadline.wait(() => registryEntry(params.appId));
|
|
282
279
|
} catch (error) {
|
|
280
|
+
deadline.check();
|
|
283
281
|
log.warn("Capability registry unavailable", {
|
|
284
282
|
appId: params.appId,
|
|
285
283
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -366,21 +364,16 @@ const runCapabilityDispatch = async (
|
|
|
366
364
|
|
|
367
365
|
const invocationOperation = capabilityInvocationOperation(params.kind, params.capabilityId, params.review);
|
|
368
366
|
const mandate = params.mandate;
|
|
369
|
-
const timeoutMs =
|
|
370
|
-
params.kind === "queries" || params.review
|
|
371
|
-
? (params.dependencies?.queryTimeoutMs ?? QUERY_TIMEOUT_MS)
|
|
372
|
-
: (params.dependencies?.actionTimeoutMs ?? ACTION_TIMEOUT_MS);
|
|
373
|
-
const timeout = AbortSignal.timeout(timeoutMs);
|
|
374
|
-
const signal = AbortSignal.any([params.request.signal, timeout]);
|
|
375
367
|
let headers: Headers;
|
|
376
368
|
try {
|
|
377
369
|
headers = mandate
|
|
378
370
|
? await (async () => {
|
|
379
|
-
const issued = await
|
|
371
|
+
const issued = await deadline.wait(() =>
|
|
380
372
|
(params.dependencies?.withActiveSigner ?? withActiveIdentitySigner)(
|
|
381
373
|
"invocation",
|
|
382
|
-
(signer, db) =>
|
|
383
|
-
(
|
|
374
|
+
(signer, db) => {
|
|
375
|
+
deadline.check();
|
|
376
|
+
return (params.dependencies?.withMandateIssueAuthority ?? withMandateIssueAuthority)(
|
|
384
377
|
{
|
|
385
378
|
mandateId: mandate.mandateId,
|
|
386
379
|
expectedRevision: mandate.mandateRevision,
|
|
@@ -394,24 +387,27 @@ const runCapabilityDispatch = async (
|
|
|
394
387
|
requestId,
|
|
395
388
|
},
|
|
396
389
|
async (mandateAuthority) => {
|
|
397
|
-
|
|
390
|
+
deadline.check();
|
|
398
391
|
observed.subject = mandateAuthority.subject;
|
|
399
|
-
return (params.dependencies?.signInvocation ?? signInvocationToken)(
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
392
|
+
return (params.dependencies?.signInvocation ?? signInvocationToken)(
|
|
393
|
+
{
|
|
394
|
+
targetAppId: params.appId,
|
|
395
|
+
callingAppId: mandate.ownerAppId,
|
|
396
|
+
operation: invocationOperation,
|
|
397
|
+
schemaHash: operation.schemaHash,
|
|
398
|
+
authority: invocationAuthorityFromMandate(mandateAuthority),
|
|
399
|
+
requestId,
|
|
400
|
+
signer,
|
|
401
|
+
issuer: signer.issuer,
|
|
402
|
+
},
|
|
403
|
+
{ db, groupsAdmin: signer.groupsAdmin },
|
|
404
|
+
);
|
|
409
405
|
},
|
|
410
|
-
{ db },
|
|
411
|
-
)
|
|
412
|
-
|
|
406
|
+
{ db, groupsAdmin: signer.groupsAdmin },
|
|
407
|
+
);
|
|
408
|
+
},
|
|
409
|
+
{ signal, timeoutMs: deadline.remainingMs() },
|
|
413
410
|
),
|
|
414
|
-
signal,
|
|
415
411
|
);
|
|
416
412
|
if (!issued.ok) {
|
|
417
413
|
if (issued.error.code === "INTERNAL") throw new Error("Mandate invocation issuance failed");
|
|
@@ -426,23 +422,27 @@ const runCapabilityDispatch = async (
|
|
|
426
422
|
: await (async () => {
|
|
427
423
|
if (!params.authority) throw new Error("Resolved request authority is required for Cloud invocation issuance");
|
|
428
424
|
const authority = params.authority;
|
|
429
|
-
const signed = await
|
|
425
|
+
const signed = await deadline.wait(() =>
|
|
430
426
|
(params.dependencies?.withActiveSigner ?? withActiveIdentitySigner)(
|
|
431
427
|
"invocation",
|
|
432
|
-
(signer) =>
|
|
433
|
-
(
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
428
|
+
(signer, db) => {
|
|
429
|
+
deadline.check();
|
|
430
|
+
return (params.dependencies?.signInvocation ?? signInvocationToken)(
|
|
431
|
+
{
|
|
432
|
+
targetAppId: params.appId,
|
|
433
|
+
callingAppId: params.callingAppId ?? "core",
|
|
434
|
+
operation: invocationOperation,
|
|
435
|
+
schemaHash: operation.schemaHash,
|
|
436
|
+
authority: invocationAuthorityFromRequest(authority),
|
|
437
|
+
requestId,
|
|
438
|
+
signer,
|
|
439
|
+
issuer: signer.issuer,
|
|
440
|
+
},
|
|
441
|
+
{ db, groupsAdmin: signer.groupsAdmin },
|
|
442
|
+
);
|
|
443
|
+
},
|
|
444
|
+
{ signal, timeoutMs: deadline.remainingMs() },
|
|
444
445
|
),
|
|
445
|
-
signal,
|
|
446
446
|
);
|
|
447
447
|
return new Headers({
|
|
448
448
|
"content-type": "application/json",
|
|
@@ -451,6 +451,7 @@ const runCapabilityDispatch = async (
|
|
|
451
451
|
});
|
|
452
452
|
})();
|
|
453
453
|
} catch (error) {
|
|
454
|
+
if (error instanceof AppAccessDeniedError) return { body: appAccessDeniedBody(error.app), status: 403 };
|
|
454
455
|
if (error instanceof MandateDispatchError) {
|
|
455
456
|
const denied = errorResponse(
|
|
456
457
|
error.status === 409 ? "MANDATE_REVISION_CHANGED" : "MANDATE_FORBIDDEN",
|
|
@@ -459,7 +460,7 @@ const runCapabilityDispatch = async (
|
|
|
459
460
|
);
|
|
460
461
|
return denied;
|
|
461
462
|
}
|
|
462
|
-
if (
|
|
463
|
+
if (signal.aborted) {
|
|
463
464
|
const cancelled = params.request.signal.aborted;
|
|
464
465
|
const failure = errorResponse(
|
|
465
466
|
cancelled ? CAPABILITY_FRAMEWORK_ERROR_CODES.requestCancelled : CAPABILITY_FRAMEWORK_ERROR_CODES.deadlineExceeded,
|
|
@@ -473,6 +474,7 @@ const runCapabilityDispatch = async (
|
|
|
473
474
|
const failure = errorResponse(CAPABILITY_FRAMEWORK_ERROR_CODES.internal, messages.issuanceFailed, 500, { retrySafe: true });
|
|
474
475
|
return failure;
|
|
475
476
|
}
|
|
477
|
+
deadline.check();
|
|
476
478
|
headers.set("x-request-id", requestId);
|
|
477
479
|
headers.set(CAPABILITY_ORIGIN_HEADER, params.origin);
|
|
478
480
|
for (const name of ["traceparent", "tracestate"] as const) {
|
|
@@ -555,18 +557,34 @@ const runCapabilityDispatch = async (
|
|
|
555
557
|
return outcome;
|
|
556
558
|
};
|
|
557
559
|
|
|
560
|
+
// Claims are mutations: await their settlement, then refuse any late upstream start.
|
|
561
|
+
try {
|
|
562
|
+
deadline.check();
|
|
563
|
+
} catch {
|
|
564
|
+
const cancelled = params.request.signal.aborted;
|
|
565
|
+
return settle(
|
|
566
|
+
errorResponse(
|
|
567
|
+
cancelled ? CAPABILITY_FRAMEWORK_ERROR_CODES.requestCancelled : CAPABILITY_FRAMEWORK_ERROR_CODES.deadlineExceeded,
|
|
568
|
+
cancelled ? messages.requestCancelled : messages.deadlineExceeded,
|
|
569
|
+
cancelled ? 499 : 504,
|
|
570
|
+
{ retrySafe: true },
|
|
571
|
+
),
|
|
572
|
+
"nothing_happened",
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
|
|
558
576
|
let response: Response;
|
|
559
577
|
try {
|
|
560
578
|
params.onUpstreamAttempt?.();
|
|
561
|
-
|
|
562
|
-
`${entry.endpoint}/${params.kind}/${encodeURIComponent(params.capabilityId)}${params.review ? "/review" : ""}`,
|
|
563
|
-
{
|
|
579
|
+
const fetchResponse = () =>
|
|
580
|
+
fetchUpstream(`${entry.endpoint}/${params.kind}/${encodeURIComponent(params.capabilityId)}${params.review ? "/review" : ""}`, {
|
|
564
581
|
method: "POST",
|
|
565
582
|
headers,
|
|
566
583
|
body: requestBody,
|
|
567
584
|
signal,
|
|
568
|
-
}
|
|
569
|
-
|
|
585
|
+
});
|
|
586
|
+
// A read may be released at its deadline; Action effects retain their existing uncertainty handling.
|
|
587
|
+
response = params.kind === "queries" || params.review ? await deadline.wait(fetchResponse) : await fetchResponse();
|
|
570
588
|
} catch (error) {
|
|
571
589
|
log.warn("Capability app unavailable", {
|
|
572
590
|
appId: params.appId,
|
|
@@ -581,7 +599,7 @@ const runCapabilityDispatch = async (
|
|
|
581
599
|
});
|
|
582
600
|
return settle(cancelled, "uncertain");
|
|
583
601
|
}
|
|
584
|
-
if (
|
|
602
|
+
if (signal.aborted) {
|
|
585
603
|
const deadline = errorResponse(CAPABILITY_FRAMEWORK_ERROR_CODES.deadlineExceeded, messages.deadlineExceeded, 504, {
|
|
586
604
|
retrySafe: params.kind === "queries" || ("idempotency" in operation && operation.idempotency === "required"),
|
|
587
605
|
});
|
|
@@ -596,7 +614,24 @@ const runCapabilityDispatch = async (
|
|
|
596
614
|
return settle(unavailable, "uncertain");
|
|
597
615
|
}
|
|
598
616
|
|
|
599
|
-
|
|
617
|
+
let upstreamBody: Awaited<ReturnType<typeof readBoundedJson>>;
|
|
618
|
+
try {
|
|
619
|
+
upstreamBody = await deadline.wait(() =>
|
|
620
|
+
readBoundedJson(response, params.review ? CAPABILITY_MAX_REVIEW_RESPONSE_BYTES : CAPABILITY_MAX_RESULT_BYTES, signal),
|
|
621
|
+
);
|
|
622
|
+
} catch {
|
|
623
|
+
if (actionWithoutRetrySafety) return settle(outcomeUnknown(), "uncertain");
|
|
624
|
+
const cancelled = params.request.signal.aborted;
|
|
625
|
+
return settle(
|
|
626
|
+
errorResponse(
|
|
627
|
+
cancelled ? CAPABILITY_FRAMEWORK_ERROR_CODES.requestCancelled : CAPABILITY_FRAMEWORK_ERROR_CODES.deadlineExceeded,
|
|
628
|
+
cancelled ? messages.requestCancelled : messages.deadlineExceeded,
|
|
629
|
+
cancelled ? 499 : 504,
|
|
630
|
+
{ retrySafe: true },
|
|
631
|
+
),
|
|
632
|
+
"uncertain",
|
|
633
|
+
);
|
|
634
|
+
}
|
|
600
635
|
if (!upstreamBody.ok) {
|
|
601
636
|
if (actionWithoutRetrySafety) return settle(outcomeUnknown(), "uncertain");
|
|
602
637
|
const code =
|
|
@@ -660,6 +695,7 @@ const runCapabilityDispatch = async (
|
|
|
660
695
|
const body = upstreamBody.data as Record<string, unknown>;
|
|
661
696
|
if (!params.review && body.stream !== undefined) {
|
|
662
697
|
const stream = CapabilityStreamSchema.safeParse(body.stream);
|
|
698
|
+
const authority = params.authority;
|
|
663
699
|
if (
|
|
664
700
|
!stream.success ||
|
|
665
701
|
!operation.stream ||
|
|
@@ -667,25 +703,29 @@ const runCapabilityDispatch = async (
|
|
|
667
703
|
stream.data.size > operation.stream.maxBytes ||
|
|
668
704
|
Date.parse(stream.data.expiresAt) <= Date.now() ||
|
|
669
705
|
Date.parse(stream.data.expiresAt) > Date.now() + 86_400_000 ||
|
|
670
|
-
!
|
|
706
|
+
!authority
|
|
671
707
|
)
|
|
672
708
|
return settle(errorResponse("INVALID_APP_RESPONSE", "Invalid stream offer", 502), "uncertain");
|
|
673
709
|
try {
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
710
|
+
const seal = () =>
|
|
711
|
+
sealCapabilityStream(stream.data, {
|
|
712
|
+
appId: params.appId,
|
|
713
|
+
kind: params.kind,
|
|
714
|
+
capabilityId: params.capabilityId,
|
|
715
|
+
schemaHash: operation.schemaHash,
|
|
716
|
+
authority,
|
|
717
|
+
requestId,
|
|
718
|
+
origin: params.origin,
|
|
719
|
+
continuation: params.continuation,
|
|
720
|
+
});
|
|
721
|
+
body.stream = params.kind === "queries" ? await deadline.wait(seal) : await seal();
|
|
684
722
|
if (Buffer.byteLength(JSON.stringify(body)) > CAPABILITY_MAX_RESULT_BYTES) throw new Error("Stream result too large");
|
|
685
723
|
} catch {
|
|
724
|
+
if (params.kind === "queries") deadline.check();
|
|
686
725
|
return settle(errorResponse("INVALID_APP_RESPONSE", "Invalid stream offer", 502), "uncertain");
|
|
687
726
|
}
|
|
688
727
|
}
|
|
728
|
+
if (params.kind === "queries" || params.review) deadline.check();
|
|
689
729
|
return settle({ body, status: 200 }, "succeeded");
|
|
690
730
|
};
|
|
691
731
|
|
|
@@ -714,50 +754,82 @@ const resultDataMeta = (body: unknown) =>
|
|
|
714
754
|
* read-only preview and are not executions, so they are not recorded.
|
|
715
755
|
*/
|
|
716
756
|
export const dispatchCapability = async (params: CapabilityDispatchParams): Promise<Response> => {
|
|
717
|
-
const
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
const
|
|
723
|
-
const
|
|
724
|
-
const
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
accessSubject
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
log.error("Capability execution could not be recorded", {
|
|
757
|
+
const deadline = createCapabilityDeadline(
|
|
758
|
+
capabilityTimeoutMs(params.review ? "review" : params.kind === "queries" ? "query" : "action", params.dependencies),
|
|
759
|
+
params.request.signal,
|
|
760
|
+
);
|
|
761
|
+
try {
|
|
762
|
+
const requestId = normalizeInvocationRequestId(params.request.headers.get("x-request-id")) ?? crypto.randomUUID();
|
|
763
|
+
const observed: CapabilityDispatchObservation = { subject: null, destructive: false };
|
|
764
|
+
const startedAt = new Date();
|
|
765
|
+
let outcome: CapabilityDispatchOutcome;
|
|
766
|
+
try {
|
|
767
|
+
outcome = await runCapabilityDispatch(params, requestId, observed, deadline);
|
|
768
|
+
} catch (error) {
|
|
769
|
+
if (!deadline.signal.aborted) throw error;
|
|
770
|
+
const cancelled = params.request.signal.aborted;
|
|
771
|
+
const messages = capabilityMessages(params.locale ?? preferredLocale(params.request.headers));
|
|
772
|
+
outcome = errorResponse(
|
|
773
|
+
cancelled ? CAPABILITY_FRAMEWORK_ERROR_CODES.requestCancelled : CAPABILITY_FRAMEWORK_ERROR_CODES.deadlineExceeded,
|
|
774
|
+
cancelled ? messages.requestCancelled : messages.deadlineExceeded,
|
|
775
|
+
cancelled ? 499 : 504,
|
|
776
|
+
{ retrySafe: true },
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
if (!params.review) {
|
|
780
|
+
const actor = params.authority?.actor;
|
|
781
|
+
const principal = dispatchPrincipal(params, observed);
|
|
782
|
+
const accessSubject = params.authority?.accessSubject;
|
|
783
|
+
const subject =
|
|
784
|
+
accessSubject?.type === "user"
|
|
785
|
+
? ({ type: "user", id: accessSubject.userId } as const)
|
|
786
|
+
: accessSubject?.type === "service_account"
|
|
787
|
+
? ({ type: "service_account", id: accessSubject.serviceAccountId } as const)
|
|
788
|
+
: null;
|
|
789
|
+
const errorCode = outcome.status >= 400 ? bodyErrorCode(outcome.body) : null;
|
|
790
|
+
const recording = recordCapabilityExecution({
|
|
791
|
+
requestId,
|
|
792
|
+
origin: params.origin,
|
|
754
793
|
appId: params.appId,
|
|
755
|
-
|
|
756
|
-
|
|
794
|
+
capability: `${params.appId}.${params.capabilityId}`,
|
|
795
|
+
kind: params.kind === "queries" ? "query" : "action",
|
|
796
|
+
destructive: observed.destructive,
|
|
797
|
+
actorKind: principal?.type ?? null,
|
|
798
|
+
actorId: principal?.id ?? null,
|
|
799
|
+
userId: actor?.kind === "user" ? actor.user.id : (actor?.delegatedUser?.id ?? (principal?.type === "user" ? principal.id : null)),
|
|
800
|
+
accessSubject: subject && subject.id !== principal?.id ? subject : null,
|
|
801
|
+
status: executionStatus(outcome.status, errorCode),
|
|
802
|
+
errorCode,
|
|
803
|
+
inputMeta: capabilityValueMeta(params.input),
|
|
804
|
+
outputMeta: outcome.status < 400 ? resultDataMeta(outcome.body) : null,
|
|
805
|
+
idempotencyKey: CapabilityIdempotencyKeySchema.safeParse(params.request.headers.get("idempotency-key")).data ?? null,
|
|
806
|
+
replayed: outcome.replayed === true,
|
|
807
|
+
startedAt,
|
|
808
|
+
completedAt: new Date(),
|
|
809
|
+
}).catch((error) => {
|
|
810
|
+
// History is operational evidence; losing a row must not fail the call.
|
|
811
|
+
log.error("Capability execution could not be recorded", {
|
|
812
|
+
appId: params.appId,
|
|
813
|
+
capabilityId: params.capabilityId,
|
|
814
|
+
error: error instanceof Error ? error.message : String(error),
|
|
815
|
+
});
|
|
757
816
|
});
|
|
758
|
-
|
|
817
|
+
// Operational history must not extend a Query's response budget. The store
|
|
818
|
+
// bounds this independent, best-effort write even for already-expired calls.
|
|
819
|
+
if (params.kind === "queries") {
|
|
820
|
+
try {
|
|
821
|
+
await deadline.wait(() => recording);
|
|
822
|
+
} catch {
|
|
823
|
+
// The outcome is already known; return it while the bounded record finishes.
|
|
824
|
+
}
|
|
825
|
+
} else {
|
|
826
|
+
await recording;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
return capabilityJsonResponse(outcome.body, outcome.status);
|
|
830
|
+
} finally {
|
|
831
|
+
deadline.clear();
|
|
759
832
|
}
|
|
760
|
-
return capabilityJsonResponse(outcome.body, outcome.status);
|
|
761
833
|
};
|
|
762
834
|
|
|
763
835
|
class MandateDispatchError extends Error {
|
|
@@ -813,7 +885,7 @@ export const createCapabilityRoutes = (dependencies: CapabilityRouteDependencies
|
|
|
813
885
|
const query = c.req.valid("query");
|
|
814
886
|
const messages = capabilityMessages(getLocale(c));
|
|
815
887
|
try {
|
|
816
|
-
return c.json(await loadCapabilityCatalogPage(query, dependencies, getLocale(c)));
|
|
888
|
+
return c.json(await loadCapabilityCatalogPage(query, dependencies, getLocale(c), appAccessAuthority(c)));
|
|
817
889
|
} catch (error) {
|
|
818
890
|
log.warn("Capability catalog unavailable", { error: error instanceof Error ? error.message : String(error) });
|
|
819
891
|
const unavailable = errorResponse(CAPABILITY_FRAMEWORK_ERROR_CODES.appUnavailable, messages.registryUnavailable, 503);
|
|
@@ -830,6 +902,7 @@ export const createCapabilityRoutes = (dependencies: CapabilityRouteDependencies
|
|
|
830
902
|
responses: {
|
|
831
903
|
200: jsonResponse(CommandLinkSchema, "Command entry URL"),
|
|
832
904
|
400: jsonResponse(CapabilityErrorSchema, "Invalid command input"),
|
|
905
|
+
403: jsonResponse(CapabilityErrorSchema, "Application access denied"),
|
|
833
906
|
404: jsonResponse(CapabilityErrorSchema, "Command unavailable"),
|
|
834
907
|
503: jsonResponse(CapabilityErrorSchema, "Route owner unavailable"),
|
|
835
908
|
},
|
|
@@ -840,6 +913,9 @@ export const createCapabilityRoutes = (dependencies: CapabilityRouteDependencies
|
|
|
840
913
|
const request = body.ok ? CommandRequestSchema.safeParse(body.data) : null;
|
|
841
914
|
if (!request?.success) return c.json({ code: "VALIDATION_FAILED", message: capabilityMessages(getLocale(c)).requestBodyJson }, 400);
|
|
842
915
|
const appId = c.req.param("appId");
|
|
916
|
+
const apps = await (dependencies.listApps ?? listApps)();
|
|
917
|
+
const target = apps.find((app) => app.id === appId);
|
|
918
|
+
if (target && !(await canUseApp(appAccessAuthority(c), target))) return c.json(appAccessDeniedBody(target), 403);
|
|
843
919
|
const entry = await (dependencies.getCapability ?? getCapability)(appId);
|
|
844
920
|
const command = entry?.manifest.commands.find((command) => command.localId === c.req.param("capabilityId"));
|
|
845
921
|
if (!command)
|
|
@@ -850,7 +926,6 @@ export const createCapabilityRoutes = (dependencies: CapabilityRouteDependencies
|
|
|
850
926
|
},
|
|
851
927
|
404,
|
|
852
928
|
);
|
|
853
|
-
const apps = await (dependencies.listApps ?? listApps)();
|
|
854
929
|
const pathname = new URL(command.path, "https://cloud.invalid").pathname;
|
|
855
930
|
const owner = apps
|
|
856
931
|
.flatMap((app) => app.routes.map((prefix) => ({ app, prefix })))
|
|
@@ -889,33 +964,34 @@ export const createCapabilityRoutes = (dependencies: CapabilityRouteDependencies
|
|
|
889
964
|
},
|
|
890
965
|
}),
|
|
891
966
|
requireReadScope,
|
|
892
|
-
async (c) =>
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
967
|
+
async (c) =>
|
|
968
|
+
withCapabilityRequestTimeout(c, "review", async () => {
|
|
969
|
+
const locale = getLocale(c);
|
|
970
|
+
const messages = capabilityMessages(locale);
|
|
971
|
+
const body = await readBoundedJson(c.req.raw, CAPABILITY_MAX_REQUEST_BYTES);
|
|
972
|
+
if (!body.ok) {
|
|
973
|
+
const message = body.reason === "too_large" ? messages.requestTooLarge : messages.requestBodyJson;
|
|
974
|
+
const error = errorResponse(CAPABILITY_FRAMEWORK_ERROR_CODES.validationFailed, message, 400);
|
|
975
|
+
return capabilityJsonResponse(error.body, error.status);
|
|
976
|
+
}
|
|
977
|
+
const request = CapabilityInvocationRequestSchema.safeParse(body.data);
|
|
978
|
+
if (!request.success) {
|
|
979
|
+
const error = errorResponse(CAPABILITY_FRAMEWORK_ERROR_CODES.validationFailed, messages.requestInputOnly, 400);
|
|
980
|
+
return capabilityJsonResponse(error.body, error.status);
|
|
981
|
+
}
|
|
982
|
+
return dispatchCapability({
|
|
983
|
+
request: c.req.raw,
|
|
984
|
+
kind: "actions",
|
|
985
|
+
review: true,
|
|
986
|
+
origin: "http",
|
|
987
|
+
appId: c.req.param("appId") ?? "",
|
|
988
|
+
capabilityId: c.req.param("capabilityId") ?? "",
|
|
989
|
+
input: request.data.input,
|
|
990
|
+
authority: auth.getAuthority(c),
|
|
991
|
+
locale,
|
|
992
|
+
dependencies,
|
|
993
|
+
});
|
|
994
|
+
}),
|
|
919
995
|
)
|
|
920
996
|
.post(
|
|
921
997
|
"/capabilities/v1/:kind{queries|actions}/:appId/:capabilityId",
|
|
@@ -940,33 +1016,34 @@ export const createCapabilityRoutes = (dependencies: CapabilityRouteDependencies
|
|
|
940
1016
|
},
|
|
941
1017
|
}),
|
|
942
1018
|
requireInvocationScope,
|
|
943
|
-
async (c) =>
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
1019
|
+
async (c) =>
|
|
1020
|
+
withCapabilityRequestTimeout(c, c.req.param("kind") === "queries" ? "query" : "action", async () => {
|
|
1021
|
+
const locale = getLocale(c);
|
|
1022
|
+
const messages = capabilityMessages(locale);
|
|
1023
|
+
const body = await readBoundedJson(c.req.raw, CAPABILITY_MAX_REQUEST_BYTES);
|
|
1024
|
+
if (!body.ok) {
|
|
1025
|
+
const message = body.reason === "too_large" ? messages.requestTooLarge : messages.requestBodyJson;
|
|
1026
|
+
const error = errorResponse(CAPABILITY_FRAMEWORK_ERROR_CODES.validationFailed, message, 400);
|
|
1027
|
+
return capabilityJsonResponse(error.body, error.status);
|
|
1028
|
+
}
|
|
1029
|
+
const request = CapabilityInvocationRequestSchema.safeParse(body.data);
|
|
1030
|
+
if (!request.success) {
|
|
1031
|
+
const error = errorResponse(CAPABILITY_FRAMEWORK_ERROR_CODES.validationFailed, messages.requestInputOnly, 400);
|
|
1032
|
+
return capabilityJsonResponse(error.body, error.status);
|
|
1033
|
+
}
|
|
957
1034
|
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1035
|
+
return dispatchCapability({
|
|
1036
|
+
request: c.req.raw,
|
|
1037
|
+
kind: c.req.param("kind") as "queries" | "actions",
|
|
1038
|
+
origin: "http",
|
|
1039
|
+
appId: c.req.param("appId") ?? "",
|
|
1040
|
+
capabilityId: c.req.param("capabilityId") ?? "",
|
|
1041
|
+
input: request.data.input,
|
|
1042
|
+
authority: auth.getAuthority(c),
|
|
1043
|
+
locale,
|
|
1044
|
+
dependencies,
|
|
1045
|
+
});
|
|
1046
|
+
}),
|
|
970
1047
|
);
|
|
971
1048
|
};
|
|
972
1049
|
|