@k2b/cloud 0.7.0 → 0.9.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 +5 -4
- package/scripts/README.md +10 -0
- package/scripts/build.ts +14 -1
- package/scripts/preload.ts +7 -0
- package/scripts/sync-recovery-smoke.ts +21 -6
- package/src/_internal/build-metadata.ts +8 -1
- package/src/_internal/capabilities.ts +35 -12
- package/src/_internal/define-app.ts +28 -11
- package/src/_internal/page-responses.ts +1 -1
- package/src/_internal/readiness.ts +4 -1
- package/src/_internal/runtime-context.ts +1 -1
- package/src/access/PermissionEditor.tsx +15 -9
- package/src/access/PrincipalPicker.tsx +9 -3
- package/src/access/ResourceApiKeys.tsx +1 -1
- package/src/access/ui.ts +1 -2
- package/src/ai/admin.ts +2 -2
- package/src/ai/assistant-models.ts +1 -1
- package/src/ai/audio-tool.ts +1 -1
- package/src/ai/browser-code-contracts.ts +174 -52
- package/src/ai/browser.ts +4 -2
- package/src/ai/capabilities.ts +3 -3
- package/src/ai/chat/blocks.tsx +127 -50
- package/src/ai/chat/builtin-tools.tsx +2 -4
- package/src/ai/chat/capability-table.tsx +1 -1
- package/src/ai/chat/composer-adapter.ts +48 -13
- package/src/ai/chat/message-utils.ts +18 -5
- package/src/ai/chat/presentation.tsx +24 -11
- package/src/ai/chat/tool-disclosure.tsx +6 -2
- package/src/ai/chat/tool-groups.ts +8 -2
- package/src/ai/chat-quotas.ts +2 -2
- package/src/ai/chat-task-contracts.ts +9 -13
- package/src/ai/chat-tasks.ts +39 -18
- package/src/ai/client/controller.ts +87 -38
- package/src/ai/client/projection.ts +13 -6
- package/src/ai/client/transport.ts +4 -2
- package/src/ai/code-capability-routes.ts +22 -27
- package/src/ai/code-capability-transport.ts +2 -1
- package/src/ai/code-execution.ts +53 -0
- package/src/ai/code-mode-skill.ts +9 -5
- package/src/ai/code-runtime-tools.ts +16 -3
- package/src/ai/code-source-contracts.ts +100 -32
- package/src/ai/code-source-tools.ts +49 -6
- package/src/ai/compaction.ts +3 -3
- package/src/ai/default-tools.ts +87 -14
- package/src/ai/dictation-runtime.ts +1 -1
- package/src/ai/draft-content.ts +27 -12
- package/src/ai/enrich.ts +1 -1
- package/src/ai/executor.ts +8 -12
- package/src/ai/fetch-file-tool.ts +2 -2
- package/src/ai/file-content-version.ts +6 -1
- package/src/ai/file-tools.ts +8 -2
- package/src/ai/files-store.ts +55 -17
- package/src/ai/grids-skill.ts +2 -0
- package/src/ai/http.ts +18 -7
- package/src/ai/index.ts +24 -25
- package/src/ai/inference-calls.ts +3 -4
- 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 +5 -5
- package/src/ai/memory-tool.ts +1 -5
- package/src/ai/memory-workflow-evidence.ts +9 -4
- package/src/ai/message-queue.ts +23 -12
- package/src/ai/migrate.ts +6 -4
- package/src/ai/model-pricing.ts +4 -4
- package/src/ai/pdf-render-worker.ts +3 -2
- package/src/ai/projects-routes.ts +15 -40
- package/src/ai/projects.ts +18 -13
- package/src/ai/quota-provider.ts +6 -5
- package/src/ai/quota-report.ts +5 -5
- package/src/ai/quotas.ts +3 -3
- package/src/ai/routes.ts +60 -34
- package/src/ai/run-timeout.ts +3 -1
- package/src/ai/runtime-tools.ts +6 -7
- package/src/ai/runtime.ts +38 -20
- package/src/ai/settings.ts +1 -1
- package/src/ai/short-id.ts +4 -5
- package/src/ai/skill-search.ts +21 -10
- package/src/ai/skill-seeds.ts +16 -7
- package/src/ai/skill-tool.ts +6 -1
- package/src/ai/skills-routes.ts +32 -15
- package/src/ai/skills.ts +54 -28
- package/src/ai/store.ts +80 -47
- package/src/ai/structured.ts +6 -2
- package/src/ai/system-prompt.ts +7 -5
- package/src/ai/task-contracts.ts +84 -0
- package/src/ai/task-execution.ts +92 -0
- package/src/ai/todo-contracts.ts +24 -12
- package/src/ai/todo-tool.ts +16 -13
- package/src/ai/tools.ts +1 -1
- package/src/ai/transcription.ts +4 -4
- package/src/ai/turn-timing.ts +1 -1
- package/src/ai/ui.tsx +2 -1
- package/src/ai/usage.ts +1 -1
- package/src/ai/vision-tool.ts +38 -17
- package/src/api/admin-ai-quotas.ts +14 -8
- package/src/api/admin-ai-skills.ts +3 -3
- package/src/api/admin-linux-identities.ts +10 -4
- package/src/api/admin-rail.ts +2 -2
- package/src/api/app-approval.ts +2 -2
- package/src/api/auth/schemas.ts +7 -1
- package/src/api/auth.ts +4 -3
- package/src/api/capabilities.ts +45 -13
- package/src/api/index.ts +5 -5
- package/src/api/mcp.ts +5 -12
- package/src/browser/CloudResourceSearch.tsx +1 -1
- package/src/browser/command-bridge.ts +1 -1
- package/src/browser/command-shortcuts.ts +1 -1
- package/src/browser/commands.ts +8 -7
- package/src/browser/testing.ts +2 -1
- package/src/capabilities/client.ts +1 -1
- package/src/capabilities/command-link.ts +1 -1
- package/src/capabilities/server.ts +2 -1
- package/src/cli/admin/ai-quotas.ts +5 -5
- package/src/cli/admin/app-credentials.ts +7 -9
- package/src/cli/admin/index.ts +3 -3
- 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 -29
- package/src/config/define-env.ts +97 -0
- package/src/config/env.ts +147 -54
- package/src/config/index.ts +3 -1
- package/src/contracts/capabilities.ts +8 -1
- package/src/contracts/index.ts +5 -7
- package/src/contracts/registry.ts +2 -0
- package/src/contracts/shared.ts +12 -3
- package/src/desktop/index.ts +1 -0
- package/src/desktop/solid.tsx +1 -1
- package/src/index.ts +8 -2
- package/src/server/app-assets.ts +27 -0
- package/src/server/index.ts +3 -2
- package/src/server/ratelimit.ts +1 -1
- package/src/server/services/access-revision.ts +3 -3
- package/src/server/services/access.ts +3 -1
- package/src/services/accounts/notification-sender.ts +12 -2
- package/src/services/announcements/index.ts +4 -4
- package/src/services/app-approval.ts +11 -10
- package/src/services/auth-flows/magic-link.ts +13 -1
- package/src/services/identity/key-config.ts +6 -4
- package/src/services/identity/runtime-config.ts +6 -5
- package/src/services/ipa/posix.ts +1 -1
- package/src/services/ipa/sync.ts +1 -1
- package/src/services/logging/index.ts +1 -1
- package/src/services/logging/trace.ts +1 -1
- package/src/services/mandates/index.ts +5 -2
- package/src/services/mandates/policy.ts +106 -6
- package/src/services/notifications/batches.ts +1 -0
- package/src/services/pdf/gotenberg.ts +50 -14
- package/src/services/pdf/index.ts +11 -11
- package/src/services/public-http.ts +1 -1
- package/src/services/rail-shortcuts.ts +1 -1
- package/src/services/rail-snapshot.ts +1 -1
- package/src/services/request-cache-redis.ts +2 -1
- package/src/services/session/user.ts +1 -1
- package/src/services/settings/app.ts +5 -1
- package/src/services/settings/core-settings.ts +6 -8
- package/src/services/settings/index.ts +3 -3
- package/src/services/settings/store.ts +3 -3
- package/src/services/webauthn.ts +1 -1
- package/src/shared/index.ts +3 -6
- package/src/shared/markdown/extensions/info-blocks.ts +1 -1
- package/src/ssr/AdminSidebar.tsx +2 -2
- package/src/ssr/AppLaunchpad.island.tsx +12 -2
- package/src/ssr/AppLaunchpadPanel.tsx +4 -4
- package/src/ssr/GlobalSearchDialog.tsx +6 -6
- package/src/ssr/GlobalSearchTrigger.island.tsx +2 -2
- package/src/ssr/Layout.tsx +4 -3
- package/src/ssr/LayoutHeader.tsx +1 -1
- package/src/ssr/LayoutHelp.tsx +4 -6
- package/src/ssr/LayoutHelpTrigger.island.tsx +2 -2
- package/src/ssr/LayoutRail.tsx +3 -2
- package/src/ssr/MinimalLayout.tsx +1 -1
- package/src/ssr/MobileNavigation.tsx +2 -2
- package/src/ssr/ProfilePreferences.island.tsx +1 -1
- package/src/ssr/RailApps.island.tsx +7 -7
- package/src/ssr/admin-navigation.ts +6 -2
- package/src/ssr/index.ts +1 -0
- package/src/ssr/islands/index.ts +2 -2
- package/src/ssr/mobile-menu-history.ts +1 -1
- package/src/ssr/rail-navigation.ts +3 -2
- package/src/ssr/request-path.ts +12 -0
- package/src/workflows/ai/index.ts +2 -2
- package/src/workflows/ai/runtime.ts +16 -91
- package/src/workflows/ai/store.ts +1 -1
- package/src/workflows/ai/types.ts +6 -82
- package/src/workflows/store/index.ts +2 -2
- package/src/server/services/freeipa/test-certificates.ts +0 -44
- package/src/services/session/test-fixture.ts +0 -18
package/src/api/capabilities.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { sealCapabilityStream, dispatchCapabilityStream } from "./capability-streams";
|
|
2
|
-
import { CapabilityStreamSchema } from "../contracts/capability-streams";
|
|
3
1
|
import { Hono, type MiddlewareHandler } from "hono";
|
|
4
2
|
import { describeRoute } from "hono-openapi";
|
|
5
3
|
import { z } from "zod";
|
|
6
4
|
import { readBoundedJson } from "../_internal/bounded-json";
|
|
7
5
|
import { resolveCapabilityManifestPresentation } from "../_internal/capabilities";
|
|
8
6
|
import { getCapability, listApps } from "../_internal/registry";
|
|
7
|
+
import { resolveCommandLink } from "../capabilities/command-link";
|
|
9
8
|
import {
|
|
10
9
|
type CapabilityClaimScope,
|
|
11
10
|
type CapabilityExecutionStatus,
|
|
@@ -34,6 +33,8 @@ import {
|
|
|
34
33
|
type CapabilityOrigin,
|
|
35
34
|
capabilityResultJsonSchema,
|
|
36
35
|
} from "../contracts/capabilities";
|
|
36
|
+
import { CapabilityStreamSchema } from "../contracts/capability-streams";
|
|
37
|
+
import { CommandLinkSchema, CommandRequestSchema } from "../contracts/commands";
|
|
37
38
|
import type { AppRegistryEntry, CapabilityRegistryEntry } from "../contracts/registry";
|
|
38
39
|
import {
|
|
39
40
|
type AuthContext,
|
|
@@ -48,8 +49,6 @@ import {
|
|
|
48
49
|
v,
|
|
49
50
|
} from "../server";
|
|
50
51
|
import { logger, settings } from "../services";
|
|
51
|
-
import { resolveCommandLink } from "../capabilities/command-link";
|
|
52
|
-
import { CommandRequestSchema, CommandLinkSchema } from "../contracts/commands";
|
|
53
52
|
import { invocationAuthorityFromRequest } from "../services/identity/invocation-authority";
|
|
54
53
|
import { capabilityInvocationOperation } from "../services/identity/invocation-operations";
|
|
55
54
|
import type { InvocationAuthority } from "../services/identity/invocation-token";
|
|
@@ -58,6 +57,7 @@ import { withActiveIdentitySigner } from "../services/identity/key-ring";
|
|
|
58
57
|
import { withMandateIssueAuthority } from "../services/mandates";
|
|
59
58
|
import { resolveAppPresentation } from "../shared/app-presentation";
|
|
60
59
|
import { capabilityMessages } from "../shared/capability-messages";
|
|
60
|
+
import { dispatchCapabilityStream, sealCapabilityStream } from "./capability-streams";
|
|
61
61
|
|
|
62
62
|
const log = logger("capabilities");
|
|
63
63
|
const QUERY_TIMEOUT_MS = 15_000;
|
|
@@ -340,7 +340,8 @@ const runCapabilityDispatch = async (
|
|
|
340
340
|
return invalid;
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
-
if (params.mandate && operation.stream)
|
|
343
|
+
if (params.mandate && operation.stream)
|
|
344
|
+
return errorResponse("STREAM_UNSUPPORTED_AUTHORITY", "Streams require an interactive user or service-account request", 403);
|
|
344
345
|
|
|
345
346
|
let requestBody: string;
|
|
346
347
|
try {
|
|
@@ -626,12 +627,31 @@ const runCapabilityDispatch = async (
|
|
|
626
627
|
const body = upstreamBody.data as Record<string, unknown>;
|
|
627
628
|
if (!params.review && body.stream !== undefined) {
|
|
628
629
|
const stream = CapabilityStreamSchema.safeParse(body.stream);
|
|
629
|
-
if (
|
|
630
|
+
if (
|
|
631
|
+
!stream.success ||
|
|
632
|
+
!operation.stream ||
|
|
633
|
+
stream.data.direction !== operation.stream.direction ||
|
|
634
|
+
stream.data.size > operation.stream.maxBytes ||
|
|
635
|
+
Date.parse(stream.data.expiresAt) <= Date.now() ||
|
|
636
|
+
Date.parse(stream.data.expiresAt) > Date.now() + 86_400_000 ||
|
|
637
|
+
!params.authority
|
|
638
|
+
)
|
|
630
639
|
return settle(errorResponse("INVALID_APP_RESPONSE", "Invalid stream offer", 502), "uncertain");
|
|
631
640
|
try {
|
|
632
|
-
body.stream = await sealCapabilityStream(stream.data, {
|
|
641
|
+
body.stream = await sealCapabilityStream(stream.data, {
|
|
642
|
+
appId: params.appId,
|
|
643
|
+
kind: params.kind,
|
|
644
|
+
capabilityId: params.capabilityId,
|
|
645
|
+
schemaHash: operation.schemaHash,
|
|
646
|
+
authority: params.authority,
|
|
647
|
+
requestId,
|
|
648
|
+
origin: params.origin,
|
|
649
|
+
continuation: params.continuation,
|
|
650
|
+
});
|
|
633
651
|
if (Buffer.byteLength(JSON.stringify(body)) > CAPABILITY_MAX_RESULT_BYTES) throw new Error("Stream result too large");
|
|
634
|
-
} catch {
|
|
652
|
+
} catch {
|
|
653
|
+
return settle(errorResponse("INVALID_APP_RESPONSE", "Invalid stream offer", 502), "uncertain");
|
|
654
|
+
}
|
|
635
655
|
}
|
|
636
656
|
return settle({ body, status: 200 }, "succeeded");
|
|
637
657
|
};
|
|
@@ -722,11 +742,23 @@ export const createCapabilityRoutes = (dependencies: CapabilityRouteDependencies
|
|
|
722
742
|
return new Hono<AuthContext>()
|
|
723
743
|
.use("/capabilities/v1/*", dependencies.authenticate ?? auth.requireRole("authenticated"))
|
|
724
744
|
.use("/capabilities/v1/*", rejectReservedWorkloadCredential)
|
|
725
|
-
.post(
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
745
|
+
.post(
|
|
746
|
+
"/capabilities/v1/streams/:verb",
|
|
747
|
+
describeRoute({
|
|
748
|
+
tags: ["Capabilities"],
|
|
749
|
+
summary: "Transfer or inspect one authorized binary stream",
|
|
750
|
+
...requiresAuth,
|
|
751
|
+
description:
|
|
752
|
+
"POST read/write/status/abort with x-cloud-stream-id. Write sends raw bytes; read returns raw bytes. Status and abort return the upload state. No automatic retries.",
|
|
753
|
+
responses: {
|
|
754
|
+
200: { description: "Binary content, write receipt, or transfer state" },
|
|
755
|
+
403: { description: "Stream does not belong to this caller" },
|
|
756
|
+
409: { description: "Transfer or contract changed" },
|
|
757
|
+
410: { description: "Stream expired" },
|
|
758
|
+
},
|
|
759
|
+
}),
|
|
760
|
+
async (c) => dispatchCapabilityStream(c.req.raw, auth.getAuthority(c), c.req.param("verb"), dependencies),
|
|
761
|
+
)
|
|
730
762
|
.get(
|
|
731
763
|
"/capabilities/v1/catalog",
|
|
732
764
|
describeRoute({
|
package/src/api/index.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import adminAiQuotaRoutes from "./admin-ai-quotas";
|
|
2
1
|
/**
|
|
3
2
|
* Core platform API surface — owned by cloud-lib, mounted by the core-app.
|
|
4
3
|
*
|
|
@@ -17,23 +16,24 @@ import adminAiQuotaRoutes from "./admin-ai-quotas";
|
|
|
17
16
|
import { Hono } from "hono";
|
|
18
17
|
import { prettyJSON } from "hono/pretty-json";
|
|
19
18
|
import { createAiApprovalPreferenceRoutes } from "../ai/approval-routes";
|
|
20
|
-
import { aiProjectsRoutes } from "../ai/projects-routes";
|
|
21
19
|
import { createCodeCapabilityRoutes } from "../ai/code-capability-routes";
|
|
20
|
+
import { aiProjectsRoutes } from "../ai/projects-routes";
|
|
22
21
|
import { aiRoutes } from "../ai/routes";
|
|
23
22
|
import { aiSkillsRoutes } from "../ai/skills-routes";
|
|
24
23
|
import accountsEntitiesRoutes from "./accounts-entities";
|
|
25
|
-
import adminAiUsageRoutes from "./admin-ai-usage";
|
|
26
24
|
import adminAiProjectsRoutes from "./admin-ai-projects";
|
|
25
|
+
import adminAiQuotaRoutes from "./admin-ai-quotas";
|
|
27
26
|
import adminAiSkillsRoutes from "./admin-ai-skills";
|
|
28
|
-
import
|
|
27
|
+
import adminAiUsageRoutes from "./admin-ai-usage";
|
|
29
28
|
import adminCoreSettingsRoutes from "./admin-core-settings";
|
|
30
29
|
import adminIdentityRoutes from "./admin-identity";
|
|
31
30
|
import adminLifecycleRoutes from "./admin-lifecycle";
|
|
32
31
|
import adminLinuxIdentityRoutes from "./admin-linux-identities";
|
|
32
|
+
import adminRailRoutes from "./admin-rail";
|
|
33
33
|
import { adminAnnouncementRoutes, announcementRoutes } from "./announcements";
|
|
34
|
+
import { createAppApprovalRoutes } from "./app-approval";
|
|
34
35
|
import { appDiscoveryRoutes } from "./apps";
|
|
35
36
|
import { createAuthRoutes } from "./auth";
|
|
36
|
-
import { createAppApprovalRoutes } from "./app-approval";
|
|
37
37
|
import { createCapabilityRoutes } from "./capabilities";
|
|
38
38
|
import { createHelpRoutes } from "./help";
|
|
39
39
|
import { createMcpRoutes } from "./mcp";
|
package/src/api/mcp.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createHelpReader, type HelpReader, type HelpReaderFactory, type HelpMetadata, type HelpArticle } from "../services/help";
|
|
2
1
|
import { createHash } from "node:crypto";
|
|
3
2
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
3
|
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
@@ -16,12 +15,7 @@ import {
|
|
|
16
15
|
import { Hono, type MiddlewareHandler } from "hono";
|
|
17
16
|
import { z } from "zod";
|
|
18
17
|
import { readBoundedJson } from "../_internal/bounded-json";
|
|
19
|
-
import {
|
|
20
|
-
HELP_SEARCH_MAX_LIMIT,
|
|
21
|
-
helpResourceUri,
|
|
22
|
-
parseHelpResourceUri,
|
|
23
|
-
readHelpArticle,
|
|
24
|
-
} from "../_internal/help-catalog";
|
|
18
|
+
import { HELP_SEARCH_MAX_LIMIT, helpResourceUri, parseHelpResourceUri, readHelpArticle } from "../_internal/help-catalog";
|
|
25
19
|
import { getCapability, listApps } from "../_internal/registry";
|
|
26
20
|
import { capabilityValueMeta, recordCapabilityExecution } from "../capabilities/executions";
|
|
27
21
|
import {
|
|
@@ -39,6 +33,7 @@ import {
|
|
|
39
33
|
} from "../contracts/capabilities";
|
|
40
34
|
import type { AppRegistryEntry, CapabilityRegistryEntry } from "../contracts/registry";
|
|
41
35
|
import { type AuthContext, auth, type RequestAuthority, rateLimit, rejectReservedWorkloadCredential, resolveLocale } from "../server";
|
|
36
|
+
import { createHelpReader, type HelpArticle, type HelpMetadata, type HelpReader, type HelpReaderFactory } from "../services/help";
|
|
42
37
|
import { normalizeInvocationRequestId } from "../services/identity/invocation-token";
|
|
43
38
|
import { logger } from "../services/logging";
|
|
44
39
|
import { get } from "../services/settings";
|
|
@@ -485,7 +480,8 @@ const createMcpServer = (
|
|
|
485
480
|
(helpLocale ??= (dependencies.getOperatorLocale ?? (() => get<string>("app.locale")))().then((operatorDefault) =>
|
|
486
481
|
resolveLocale(request.headers, operatorDefault),
|
|
487
482
|
));
|
|
488
|
-
const helpReader = async () =>
|
|
483
|
+
const helpReader = async () =>
|
|
484
|
+
(dependencies.help ?? ((locale) => createHelpReader(locale, { listApps: registry })))(await resolveHelpLocale());
|
|
489
485
|
const hasScope = (scope: "read" | "write"): boolean =>
|
|
490
486
|
oauthScopes === null || oauthScopes.includes(scope) || oauthScopes.includes("admin");
|
|
491
487
|
const requireScope = (scope: "read" | "write"): void => {
|
|
@@ -567,10 +563,7 @@ const createMcpServer = (
|
|
|
567
563
|
return (await callHelpTool(message.params.name, message.params.arguments, await helpReader()))!;
|
|
568
564
|
} catch (error) {
|
|
569
565
|
log.error("Failed to call MCP Help tool", { error: error instanceof Error ? error.message : String(error) });
|
|
570
|
-
return boundedToolResult(
|
|
571
|
-
{ code: CAPABILITY_FRAMEWORK_ERROR_CODES.appUnavailable, message: "Help is currently unavailable" },
|
|
572
|
-
true,
|
|
573
|
-
);
|
|
566
|
+
return boundedToolResult({ code: CAPABILITY_FRAMEWORK_ERROR_CODES.appUnavailable, message: "Help is currently unavailable" }, true);
|
|
574
567
|
}
|
|
575
568
|
}
|
|
576
569
|
let selected: CapabilityTool | null;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { shortcutLabel } from "./command-shortcuts";
|
|
2
1
|
import { query, timed } from "@k2b/stdlib/solid";
|
|
3
2
|
import { Button, IconButton, ScrollArea, useLocale } from "@k2b/ui";
|
|
4
3
|
import { createEffect, createMemo, createSignal, createUniqueId, For, on, onCleanup, onMount, Show } from "solid-js";
|
|
5
4
|
import type { SearchItem, SearchResponse } from "../api/search/schemas";
|
|
6
5
|
import type { CloudResourceRef } from "../contracts";
|
|
7
6
|
import { LOCALE_HEADER } from "../shared/locale";
|
|
7
|
+
import { shortcutLabel } from "./command-shortcuts";
|
|
8
8
|
import { matchNavigationSearchItems, type NavigationSearchItem } from "./navigation-search";
|
|
9
9
|
import { cloudResourceSearchUrl, filterCloudResourceSearchItems } from "./resource-search";
|
|
10
10
|
import { commitTypedTags, matchingSearchTags, searchTags, tagAtCursor } from "./resource-search-input";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { GlobalSearchOptions } from "./search-bridge";
|
|
2
1
|
import type { z } from "zod";
|
|
3
2
|
import type { CommandOptions } from "../contracts/commands";
|
|
3
|
+
import type { GlobalSearchOptions } from "./search-bridge";
|
|
4
4
|
|
|
5
5
|
export type CommandTarget = { command: string; input: unknown; options?: CommandOptions };
|
|
6
6
|
export type ContextAwareCommand = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { COMMANDS_CHANGED,
|
|
1
|
+
import { COMMANDS_CHANGED, type ContextAwareCommand, collectContextAwareCommands } from "./command-bridge";
|
|
2
2
|
|
|
3
3
|
const isMac = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform);
|
|
4
4
|
const modifiers = ["ctrl", "alt", "shift", "meta"] as const;
|
package/src/browser/commands.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import { requestGlobalSearch } from "./search-bridge";
|
|
2
1
|
import { toast } from "@k2b/ui";
|
|
3
2
|
import { resolveCommand } from "../capabilities/client";
|
|
4
|
-
import {
|
|
5
|
-
import { resourceSearchMessages } from "./resource-search-messages";
|
|
3
|
+
import { type CommandOptions, CommandOptionsSchema, clearCommand, readCommand } from "../contracts/commands";
|
|
6
4
|
import {
|
|
7
|
-
collectContextAwareCommands,
|
|
8
|
-
requestContextCommandExecution,
|
|
9
5
|
type ContextAwareCommand,
|
|
6
|
+
collectContextAwareCommands,
|
|
10
7
|
requestCommandHandling,
|
|
8
|
+
requestContextCommandExecution,
|
|
11
9
|
} from "./command-bridge";
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
import { resourceSearchMessages } from "./resource-search-messages";
|
|
11
|
+
import { requestGlobalSearch } from "./search-bridge";
|
|
12
|
+
|
|
14
13
|
export type { CommandOptions } from "../contracts/commands";
|
|
14
|
+
export type { CommandTarget, ContextAwareCommand } from "./command-bridge";
|
|
15
|
+
export { registerCommandHandler, registerContextAwareCommand } from "./command-bridge";
|
|
15
16
|
|
|
16
17
|
/** Opens locally only when a mounted owner explicitly accepts; otherwise follows the app's URL. */
|
|
17
18
|
export const openCommand = async (command: string, input: unknown = {}, options: CommandOptions = {}): Promise<void> => {
|
package/src/browser/testing.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Browser-only test probes. Mount and dispose application islands through their public APIs. */
|
|
2
|
+
|
|
3
|
+
export { readWorkspaceNavigation } from "../ssr/workspace-navigation";
|
|
2
4
|
export { collectContextAwareCommands } from "./command-bridge";
|
|
3
5
|
export { contextCommandsWithShortcuts } from "./command-shortcuts";
|
|
4
6
|
export { registerGlobalSearchHost } from "./search-bridge";
|
|
5
|
-
export { readWorkspaceNavigation } from "../ssr/workspace-navigation";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import type { CapabilityCommandManifest } from "../contracts/capabilities";
|
|
3
|
-
import {
|
|
3
|
+
import { CommandLinkSchema, type CommandOptions, commandPath } from "../contracts/commands";
|
|
4
4
|
import { publicCloudOrigin } from "../shared/app-url";
|
|
5
5
|
|
|
6
6
|
export const resolveCommandLink = (
|
|
@@ -2,6 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { resolveCapabilityManifestPresentation } from "../_internal/capabilities";
|
|
3
3
|
import { getApp, getCapability } from "../_internal/registry";
|
|
4
4
|
import { dispatchCapability, loadCapabilityCatalogPage } from "../api/capabilities";
|
|
5
|
+
import { env } from "../config/env";
|
|
5
6
|
import {
|
|
6
7
|
CAPABILITY_FRAMEWORK_ERROR_CODES,
|
|
7
8
|
CapabilityActionReviewSchema,
|
|
@@ -54,7 +55,7 @@ export type CapabilityCaller = {
|
|
|
54
55
|
};
|
|
55
56
|
|
|
56
57
|
const coreOrigin = async (internal = false): Promise<string> => {
|
|
57
|
-
const configured =
|
|
58
|
+
const configured = env.CLOUD_CORE_INTERNAL_ORIGIN;
|
|
58
59
|
if (internal && !configured) throw new Error("CLOUD_CORE_INTERNAL_ORIGIN is required for mandate-backed invocation");
|
|
59
60
|
const origin = configured || publicCloudOrigin(await get<string>("app.url"));
|
|
60
61
|
const url = new URL(origin);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type AiModelPricing, AiModelPricingSchema } from "../../shared/ai-costs";
|
|
2
2
|
import {
|
|
3
|
+
type AiQuotaConfig,
|
|
3
4
|
AiQuotaConfigSchema,
|
|
4
|
-
|
|
5
|
+
type AiQuotaIdentity,
|
|
5
6
|
AiQuotaIdentitySchema,
|
|
7
|
+
AiQuotaReportQuerySchema,
|
|
6
8
|
AiQuotaResetSchema,
|
|
7
|
-
type AiQuotaConfig,
|
|
8
|
-
type AiQuotaIdentity,
|
|
9
9
|
} from "../../shared/ai-quotas";
|
|
10
|
-
import { cliText, command, confirmFlag, flag, printRows, printStructured
|
|
10
|
+
import { type CloudCliContext, cliText, command, confirmFlag, flag, printRows, printStructured } from "../index";
|
|
11
11
|
import { apiGet, apiJson, queryString, readJsonInput } from "./shared";
|
|
12
12
|
|
|
13
13
|
const path = "/api/admin/core/ai-quotas";
|
|
@@ -52,15 +52,13 @@ export const appCredentialCommands = [
|
|
|
52
52
|
async run({ ctx, args, flags }) {
|
|
53
53
|
if (!flags.yes)
|
|
54
54
|
throw new Error(cliText(ctx, { en: "Creating an app credential requires --yes.", de: "App-Zugang erstellen erfordert --yes." }));
|
|
55
|
-
const result = z
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}),
|
|
63
|
-
);
|
|
55
|
+
const result = z.object({ credential: metadata, token: z.string() }).parse(
|
|
56
|
+
await apiJson(ctx, "POST", path(args.app), {
|
|
57
|
+
name: flags.name,
|
|
58
|
+
scopes: ["identity:invoke"],
|
|
59
|
+
...(flags.expiresAt ? { expiresAt: flags.expiresAt } : {}),
|
|
60
|
+
}),
|
|
61
|
+
);
|
|
64
62
|
if (!printStructured(ctx, result)) ctx.print(result.token);
|
|
65
63
|
},
|
|
66
64
|
}),
|
package/src/cli/admin/index.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { appCredentialCommands } from "./app-credentials";
|
|
2
1
|
/**
|
|
3
2
|
* `cld admin` — Cloud administration surfaces.
|
|
4
3
|
*
|
|
@@ -9,10 +8,11 @@ import { appCredentialCommands } from "./app-credentials";
|
|
|
9
8
|
import { defineCliCommands } from "../commands";
|
|
10
9
|
import { accountAdministrationCommands } from "./account-administration";
|
|
11
10
|
import { accountCategoryCommands } from "./account-categories";
|
|
12
|
-
import { appSignInCommands } from "./app-sign-in";
|
|
13
|
-
import { aiSkillCommands } from "./ai-skills";
|
|
14
11
|
import { aiQuotaCommands } from "./ai-quotas";
|
|
12
|
+
import { aiSkillCommands } from "./ai-skills";
|
|
15
13
|
import { aiUsageCommands } from "./ai-usage";
|
|
14
|
+
import { appCredentialCommands } from "./app-credentials";
|
|
15
|
+
import { appSignInCommands } from "./app-sign-in";
|
|
16
16
|
import { dataCommands } from "./data";
|
|
17
17
|
import { documentationCommands } from "./documentation";
|
|
18
18
|
import { gatewayCommands } from "./gateway";
|
|
@@ -7,10 +7,10 @@ import type { PostgresDiagnostics, RedisDiagnostics } from "./data";
|
|
|
7
7
|
import type { GatewayHealth } from "./gateway";
|
|
8
8
|
import type { BackgroundJobRow } from "./jobs";
|
|
9
9
|
import { getLogs, type LogSummary, trimLogMessages } from "./logs";
|
|
10
|
-
import type { NatsSnapshot } from "./nats";
|
|
11
|
-
import type { SyncOverview } from "./sync";
|
|
12
10
|
import type { MetricsCollector } from "./metrics";
|
|
11
|
+
import type { NatsSnapshot } from "./nats";
|
|
13
12
|
import { apiGet, formatBytes, parseLookbackHours, printJsonOrTable, queryString, safeCollect, skippedCollect, truncate } from "./shared";
|
|
13
|
+
import type { SyncOverview } from "./sync";
|
|
14
14
|
import type { TelemetryEvent, TelemetryRouteRow } from "./telemetry";
|
|
15
15
|
import { diagnoseRange } from "./telemetry";
|
|
16
16
|
|
package/src/cli/admin/linux.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type LinuxIdentityConfiguration, LinuxIdentityConfigurationSchema } from "../../contracts/posix";
|
|
2
2
|
import { cliText, command, confirmFlag, flag, printStructured, readCliInput } from "../index";
|
|
3
3
|
import { apiGet, apiJson, queryString } from "./shared";
|
|
4
4
|
|
|
@@ -10,7 +10,9 @@ export const linuxCommands = [
|
|
|
10
10
|
flags: {
|
|
11
11
|
after: flag.string({ description: "Continue after the previous page's nextCursor" }),
|
|
12
12
|
search: flag.string({ description: "Filter by username substring" }),
|
|
13
|
-
scope: flag.enum(["ready", "all"] as const, {
|
|
13
|
+
scope: flag.enum(["ready", "all"] as const, {
|
|
14
|
+
description: "Show eligible accounts missing Linux attributes or all accounts (default)",
|
|
15
|
+
}),
|
|
14
16
|
},
|
|
15
17
|
async run({ ctx, flags }) {
|
|
16
18
|
const result = await apiGet<unknown>(ctx, `${path}${queryString({ after: flags.after, search: flags.search, scope: flags.scope })}`);
|
package/src/cli/admin/sync.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Admin access to application-owned Sync diagnostics and recovery. */
|
|
2
2
|
import type { SyncDeadLetterEntry, SyncDeadLetterKind, SyncDeadLetterStoreView, SyncScheduleView } from "../../services/sync-ops";
|
|
3
|
-
import { arg, command, confirmFlag, flag
|
|
3
|
+
import { arg, type CloudCliContext, command, confirmFlag, flag } from "../index";
|
|
4
4
|
import { apiGet, apiJson, printJsonOrTable, queryString } from "./shared";
|
|
5
5
|
|
|
6
6
|
export type SyncOverview = {
|
package/src/cli/capabilities.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { saveCapabilityStream } from "./capability-streams";
|
|
2
|
-
import { CapabilityStreamSchema } from "../contracts/capabilities";
|
|
3
1
|
import { z } from "zod";
|
|
4
2
|
import { readBoundedJson } from "../_internal/bounded-json";
|
|
5
3
|
import {
|
|
@@ -8,11 +6,13 @@ import {
|
|
|
8
6
|
CAPABILITY_MAX_RESULT_BYTES,
|
|
9
7
|
CapabilityCatalogSchema,
|
|
10
8
|
CapabilityErrorSchema,
|
|
9
|
+
CapabilityStreamSchema,
|
|
11
10
|
CloudResourceRefSchema,
|
|
12
11
|
capabilityResultSchema,
|
|
13
12
|
cloudResourceRefAppId,
|
|
14
13
|
resolveCapabilityResourceReader,
|
|
15
14
|
} from "../contracts/capabilities";
|
|
15
|
+
import { saveCapabilityStream } from "./capability-streams";
|
|
16
16
|
import { arg, type CliInputFlagValue, type CloudCliContext, command, defineCliCommands, flag, readCliInput } from "./index";
|
|
17
17
|
|
|
18
18
|
const parseInput = async (input: CliInputFlagValue): Promise<unknown> => {
|
|
@@ -120,33 +120,46 @@ export default defineCliCommands({
|
|
|
120
120
|
name: "capabilities",
|
|
121
121
|
summary: "Discover and invoke versioned Cloud app capabilities.",
|
|
122
122
|
commands: [
|
|
123
|
-
...(["read", "write", "status", "abort"] as const).map(verb =>
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
123
|
+
...(["read", "write", "status", "abort"] as const).map((verb) =>
|
|
124
|
+
command(`stream-${verb}`, {
|
|
125
|
+
summary: `${verb} an authenticated binary capability stream`,
|
|
126
|
+
flags: {
|
|
127
|
+
input: flag.input({ required: true, description: "Stream descriptor JSON; use --input-file or --stdin" }),
|
|
128
|
+
...(verb === "read" ? { out: flag.string({ required: true, description: "New output file; never overwritten" }) } : {}),
|
|
129
|
+
...(verb === "write" ? { file: flag.string({ required: true, description: "Local binary file to upload" }) } : {}),
|
|
130
|
+
},
|
|
131
|
+
run: async ({ ctx, flags }) => {
|
|
132
|
+
const stream = CapabilityStreamSchema.parse(await parseInput(flags.input));
|
|
133
|
+
if ((verb === "read") !== (stream.direction === "read")) throw new Error("Wrong stream direction.");
|
|
134
|
+
const body = verb === "write" ? Bun.file(String(flags.file)) : undefined;
|
|
135
|
+
if (body && (!(await body.exists()) || body.size !== stream.size))
|
|
136
|
+
throw new Error("Input file must match the authorized byte size.");
|
|
137
|
+
const controller = new AbortController();
|
|
138
|
+
const abort = () => controller.abort();
|
|
139
|
+
process.once("SIGINT", abort);
|
|
140
|
+
process.once("SIGTERM", abort);
|
|
141
|
+
try {
|
|
142
|
+
const signal = AbortSignal.any([controller.signal, AbortSignal.timeout(300_000)]);
|
|
143
|
+
const response = await ctx.fetch(`/api/capabilities/v1/streams/${verb}`, {
|
|
144
|
+
method: "POST",
|
|
145
|
+
headers: { "x-cloud-stream-id": stream.id, "content-type": "application/octet-stream" },
|
|
146
|
+
body,
|
|
147
|
+
signal,
|
|
148
|
+
redirect: "error",
|
|
149
|
+
});
|
|
150
|
+
printGenericResult(
|
|
151
|
+
ctx,
|
|
152
|
+
verb === "read"
|
|
153
|
+
? await saveCapabilityStream(response, stream, String(flags.out), signal)
|
|
154
|
+
: await readCapabilityJson(response, CAPABILITY_MAX_RESULT_BYTES),
|
|
155
|
+
);
|
|
156
|
+
} finally {
|
|
157
|
+
process.off("SIGINT", abort);
|
|
158
|
+
process.off("SIGTERM", abort);
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
}),
|
|
162
|
+
),
|
|
150
163
|
command("catalog", {
|
|
151
164
|
summary: "List live capability manifests",
|
|
152
165
|
flags: {
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment registry factory.
|
|
3
|
+
*
|
|
4
|
+
* Every runtime environment variable is declared once with a schema, a
|
|
5
|
+
* default, a scope and a one-sentence doc. Registries read `process.env`
|
|
6
|
+
* lazily: each key is parsed on access, so tests can change variables and
|
|
7
|
+
* consumers never depend on an import-time snapshot. `.env.example`,
|
|
8
|
+
* `.env.prod.example` and the configuration reference are generated from the
|
|
9
|
+
* `$specs` of every registry by `scripts/generate-config-docs.ts`.
|
|
10
|
+
*/
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* `runtime` is read by deployed processes, `secret` is a runtime credential,
|
|
15
|
+
* `development` only matters for host-run development, builds or tooling.
|
|
16
|
+
*/
|
|
17
|
+
export type EnvScope = "runtime" | "secret" | "development";
|
|
18
|
+
|
|
19
|
+
export type EnvSpec<T> = {
|
|
20
|
+
/** Parses the trimmed raw value. Empty and unset values never reach the schema. */
|
|
21
|
+
schema: z.ZodType<T, unknown>;
|
|
22
|
+
/** Used when the variable is unset or empty; a function is evaluated on every access. */
|
|
23
|
+
default?: T | (() => T);
|
|
24
|
+
/** One sentence for operators; rendered into the generated files. */
|
|
25
|
+
doc: string;
|
|
26
|
+
/** Default `runtime`. */
|
|
27
|
+
scope?: EnvScope;
|
|
28
|
+
/** Host-run development value written to `.env.example`. */
|
|
29
|
+
example?: string;
|
|
30
|
+
/** Throw on access when unset and no default exists. */
|
|
31
|
+
required?: boolean;
|
|
32
|
+
/** Alternative variable names, consulted after the canonical key. */
|
|
33
|
+
aliases?: string[];
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type EnvSpecs = Record<string, EnvSpec<unknown>>;
|
|
37
|
+
|
|
38
|
+
type EnvValue<S extends EnvSpec<unknown>> = S extends { default: unknown } | { required: true }
|
|
39
|
+
? z.output<S["schema"]>
|
|
40
|
+
: z.output<S["schema"]> | undefined;
|
|
41
|
+
|
|
42
|
+
export type EnvRegistry<S extends EnvSpecs> = { readonly [K in keyof S]: EnvValue<S[K]> } & {
|
|
43
|
+
readonly $specs: S;
|
|
44
|
+
readonly $keys: readonly string[];
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const rawValue = (key: string, aliases: string[] = []): string | undefined => {
|
|
48
|
+
for (const name of [key, ...aliases]) {
|
|
49
|
+
const value = process.env[name]?.trim();
|
|
50
|
+
if (value) return value;
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const isFactory = <T>(value: T | (() => T)): value is () => T => typeof value === "function";
|
|
56
|
+
|
|
57
|
+
export const readEnv = <T>(key: string, spec: EnvSpec<T>): T | undefined => {
|
|
58
|
+
const raw = rawValue(key, spec.aliases);
|
|
59
|
+
if (raw === undefined) {
|
|
60
|
+
if (spec.default !== undefined) return isFactory(spec.default) ? spec.default() : spec.default;
|
|
61
|
+
if (spec.required) throw new Error(`${key} is required`);
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
const parsed = spec.schema.safeParse(raw);
|
|
65
|
+
if (!parsed.success) {
|
|
66
|
+
const reason = parsed.error.issues.map((issue) => issue.message).join("; ") || "is invalid";
|
|
67
|
+
throw new Error(`${key} ${reason}`);
|
|
68
|
+
}
|
|
69
|
+
return parsed.data;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const defineEnv = <S extends EnvSpecs>(specs: S): EnvRegistry<S> => {
|
|
73
|
+
const keys = Object.freeze(Object.keys(specs));
|
|
74
|
+
const registry: Record<string, unknown> = { $specs: specs, $keys: keys };
|
|
75
|
+
for (const key of keys) {
|
|
76
|
+
Object.defineProperty(registry, key, { enumerable: true, configurable: true, get: () => readEnv(key, specs[key]!) });
|
|
77
|
+
}
|
|
78
|
+
// The getters above are the only way to build a typed lazy object.
|
|
79
|
+
return registry as EnvRegistry<S>;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/** Any non-empty string; empty values are treated as unset before parsing. */
|
|
83
|
+
export const envString = z.string();
|
|
84
|
+
|
|
85
|
+
/** `true` or `1` (case-insensitive) is true; everything else is false. */
|
|
86
|
+
export const envBoolean = z.string().transform((value) => {
|
|
87
|
+
const normalized = value.toLowerCase();
|
|
88
|
+
return normalized === "true" || normalized === "1";
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
/** Comma-separated entries; whitespace and empty entries are dropped. */
|
|
92
|
+
export const envList = z.string().transform((value) =>
|
|
93
|
+
value
|
|
94
|
+
.split(",")
|
|
95
|
+
.map((entry) => entry.trim())
|
|
96
|
+
.filter(Boolean),
|
|
97
|
+
);
|