@agent-native/dispatch 0.6.1 → 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 +38 -1
- package/dist/actions/apply-dream-proposal.d.ts +3 -0
- package/dist/actions/apply-dream-proposal.d.ts.map +1 -0
- package/dist/actions/apply-dream-proposal.js +11 -0
- package/dist/actions/apply-dream-proposal.js.map +1 -0
- package/dist/actions/create-dream-report.d.ts +3 -0
- package/dist/actions/create-dream-report.d.ts.map +1 -0
- package/dist/actions/create-dream-report.js +67 -0
- package/dist/actions/create-dream-report.js.map +1 -0
- package/dist/actions/create-pylon-ticket.d.ts +3 -0
- package/dist/actions/create-pylon-ticket.d.ts.map +1 -0
- package/dist/actions/create-pylon-ticket.js +94 -0
- package/dist/actions/create-pylon-ticket.js.map +1 -0
- package/dist/actions/create-vault-grant.js +1 -1
- package/dist/actions/create-vault-grant.js.map +1 -1
- package/dist/actions/create-vault-secret.d.ts.map +1 -1
- package/dist/actions/create-vault-secret.js +4 -3
- package/dist/actions/create-vault-secret.js.map +1 -1
- package/dist/actions/create-workspace-resource.js +3 -3
- package/dist/actions/create-workspace-resource.js.map +1 -1
- package/dist/actions/delete-workspace-resource.js +1 -1
- package/dist/actions/delete-workspace-resource.js.map +1 -1
- package/dist/actions/ensure-dream-job.d.ts +3 -0
- package/dist/actions/ensure-dream-job.d.ts.map +1 -0
- package/dist/actions/ensure-dream-job.js +73 -0
- package/dist/actions/ensure-dream-job.js.map +1 -0
- package/dist/actions/get-dream-settings.d.ts +3 -0
- package/dist/actions/get-dream-settings.d.ts.map +1 -0
- package/dist/actions/get-dream-settings.js +11 -0
- package/dist/actions/get-dream-settings.js.map +1 -0
- package/dist/actions/get-dream.d.ts +3 -0
- package/dist/actions/get-dream.d.ts.map +1 -0
- package/dist/actions/get-dream.js +13 -0
- package/dist/actions/get-dream.js.map +1 -0
- package/dist/actions/get-vault-access-settings.d.ts +3 -0
- package/dist/actions/get-vault-access-settings.d.ts.map +1 -0
- package/dist/actions/get-vault-access-settings.js +10 -0
- package/dist/actions/get-vault-access-settings.js.map +1 -0
- package/dist/actions/get-workspace-resource-effective-context.d.ts +3 -0
- package/dist/actions/get-workspace-resource-effective-context.d.ts.map +1 -0
- package/dist/actions/get-workspace-resource-effective-context.js +27 -0
- package/dist/actions/get-workspace-resource-effective-context.js.map +1 -0
- package/dist/actions/grant-vault-secrets-to-app.js +1 -1
- package/dist/actions/grant-vault-secrets-to-app.js.map +1 -1
- package/dist/actions/index.d.ts.map +1 -1
- package/dist/actions/index.js +38 -4
- package/dist/actions/index.js.map +1 -1
- package/dist/actions/list-dream-candidates.d.ts +3 -0
- package/dist/actions/list-dream-candidates.d.ts.map +1 -0
- package/dist/actions/list-dream-candidates.js +68 -0
- package/dist/actions/list-dream-candidates.js.map +1 -0
- package/dist/actions/list-dreams.d.ts +3 -0
- package/dist/actions/list-dreams.d.ts.map +1 -0
- package/dist/actions/list-dreams.js +17 -0
- package/dist/actions/list-dreams.js.map +1 -0
- package/dist/actions/list-integrations-catalog.js +1 -1
- package/dist/actions/list-integrations-catalog.js.map +1 -1
- package/dist/actions/list-vault-grants.js +1 -1
- package/dist/actions/list-vault-grants.js.map +1 -1
- package/dist/actions/list-workspace-apps.d.ts.map +1 -1
- package/dist/actions/list-workspace-apps.js +5 -1
- package/dist/actions/list-workspace-apps.js.map +1 -1
- package/dist/actions/list-workspace-resources-for-app.d.ts +3 -0
- package/dist/actions/list-workspace-resources-for-app.d.ts.map +1 -0
- package/dist/actions/list-workspace-resources-for-app.js +12 -0
- package/dist/actions/list-workspace-resources-for-app.js.map +1 -0
- package/dist/actions/list-workspace-resources.js +1 -1
- package/dist/actions/list-workspace-resources.js.map +1 -1
- package/dist/actions/navigate.d.ts +1 -0
- package/dist/actions/navigate.d.ts.map +1 -1
- package/dist/actions/navigate.js +2 -1
- package/dist/actions/navigate.js.map +1 -1
- package/dist/actions/preview-dream-proposal.d.ts +3 -0
- package/dist/actions/preview-dream-proposal.d.ts.map +1 -0
- package/dist/actions/preview-dream-proposal.js +13 -0
- package/dist/actions/preview-dream-proposal.js.map +1 -0
- package/dist/actions/preview-workspace-resource-change.d.ts +3 -0
- package/dist/actions/preview-workspace-resource-change.d.ts.map +1 -0
- package/dist/actions/preview-workspace-resource-change.js +24 -0
- package/dist/actions/preview-workspace-resource-change.js.map +1 -0
- package/dist/actions/reject-dream-proposal.d.ts +3 -0
- package/dist/actions/reject-dream-proposal.d.ts.map +1 -0
- package/dist/actions/reject-dream-proposal.js +12 -0
- package/dist/actions/reject-dream-proposal.js.map +1 -0
- package/dist/actions/restore-starter-workspace-resources.d.ts +3 -0
- package/dist/actions/restore-starter-workspace-resources.d.ts.map +1 -0
- package/dist/actions/restore-starter-workspace-resources.js +14 -0
- package/dist/actions/restore-starter-workspace-resources.js.map +1 -0
- package/dist/actions/send-code-agent-remote-command.d.ts +3 -0
- package/dist/actions/send-code-agent-remote-command.d.ts.map +1 -0
- package/dist/actions/send-code-agent-remote-command.js +53 -0
- package/dist/actions/send-code-agent-remote-command.js.map +1 -0
- package/dist/actions/set-dream-settings.d.ts +3 -0
- package/dist/actions/set-dream-settings.d.ts.map +1 -0
- package/dist/actions/set-dream-settings.js +41 -0
- package/dist/actions/set-dream-settings.js.map +1 -0
- package/dist/actions/set-vault-access-settings.d.ts +3 -0
- package/dist/actions/set-vault-access-settings.d.ts.map +1 -0
- package/dist/actions/set-vault-access-settings.js +13 -0
- package/dist/actions/set-vault-access-settings.js.map +1 -0
- package/dist/actions/start-workspace-app-creation.d.ts.map +1 -1
- package/dist/actions/start-workspace-app-creation.js +6 -0
- package/dist/actions/start-workspace-app-creation.js.map +1 -1
- package/dist/actions/sync-vault-to-app.js +1 -1
- package/dist/actions/sync-vault-to-app.js.map +1 -1
- package/dist/actions/update-workspace-app-metadata.d.ts +3 -0
- package/dist/actions/update-workspace-app-metadata.d.ts.map +1 -0
- package/dist/actions/update-workspace-app-metadata.js +30 -0
- package/dist/actions/update-workspace-app-metadata.js.map +1 -0
- package/dist/actions/update-workspace-resource.js +1 -1
- package/dist/actions/update-workspace-resource.js.map +1 -1
- package/dist/actions/view-screen.d.ts.map +1 -1
- package/dist/actions/view-screen.js +77 -4
- package/dist/actions/view-screen.js.map +1 -1
- package/dist/components/app-keys-popover.js +16 -5
- package/dist/components/app-keys-popover.js.map +1 -1
- package/dist/components/approval-value-block.d.ts +7 -0
- package/dist/components/approval-value-block.d.ts.map +1 -0
- package/dist/components/approval-value-block.js +22 -0
- package/dist/components/approval-value-block.js.map +1 -0
- package/dist/components/create-app-popover.d.ts.map +1 -1
- package/dist/components/create-app-popover.js +41 -16
- package/dist/components/create-app-popover.js.map +1 -1
- package/dist/components/dispatch-shell.d.ts +4 -4
- package/dist/components/dispatch-shell.d.ts.map +1 -1
- package/dist/components/dispatch-shell.js +6 -6
- package/dist/components/dispatch-shell.js.map +1 -1
- package/dist/components/layout/Layout.d.ts.map +1 -1
- package/dist/components/layout/Layout.js +18 -4
- package/dist/components/layout/Layout.js.map +1 -1
- package/dist/components/messaging-setup-panel.d.ts.map +1 -1
- package/dist/components/messaging-setup-panel.js +2 -2
- package/dist/components/messaging-setup-panel.js.map +1 -1
- package/dist/components/ui/chart.d.ts +1 -1
- package/dist/components/workspace-app-card.d.ts.map +1 -1
- package/dist/components/workspace-app-card.js +63 -3
- package/dist/components/workspace-app-card.js.map +1 -1
- package/dist/components/workspace-resource-effective-stack.d.ts +11 -0
- package/dist/components/workspace-resource-effective-stack.d.ts.map +1 -0
- package/dist/components/workspace-resource-effective-stack.js +59 -0
- package/dist/components/workspace-resource-effective-stack.js.map +1 -0
- package/dist/components/workspace-resource-impact-preview.d.ts +9 -0
- package/dist/components/workspace-resource-impact-preview.d.ts.map +1 -0
- package/dist/components/workspace-resource-impact-preview.js +39 -0
- package/dist/components/workspace-resource-impact-preview.js.map +1 -0
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +59 -0
- package/dist/db/migrations.js.map +1 -1
- package/dist/db/schema.d.ts +714 -0
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +44 -2
- package/dist/db/schema.js.map +1 -1
- package/dist/hooks/use-navigation-state.d.ts +3 -0
- package/dist/hooks/use-navigation-state.d.ts.map +1 -1
- package/dist/hooks/use-navigation-state.js +35 -8
- package/dist/hooks/use-navigation-state.js.map +1 -1
- package/dist/lib/catch-all-target.d.ts +2 -0
- package/dist/lib/catch-all-target.d.ts.map +1 -0
- package/dist/lib/catch-all-target.js +95 -0
- package/dist/lib/catch-all-target.js.map +1 -0
- package/dist/lib/utils.d.ts +2 -1
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +5 -1
- package/dist/lib/utils.js.map +1 -1
- package/dist/lib/workspace-apps.d.ts +9 -0
- package/dist/lib/workspace-apps.d.ts.map +1 -1
- package/dist/lib/workspace-apps.js.map +1 -1
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +1 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/pages/$appId.d.ts +2 -2
- package/dist/routes/pages/$appId.d.ts.map +1 -1
- package/dist/routes/pages/$appId.js +17 -8
- package/dist/routes/pages/$appId.js.map +1 -1
- package/dist/routes/pages/approval.d.ts.map +1 -1
- package/dist/routes/pages/approval.js +4 -1
- package/dist/routes/pages/approval.js.map +1 -1
- package/dist/routes/pages/approvals.js +1 -1
- package/dist/routes/pages/approvals.js.map +1 -1
- package/dist/routes/pages/dream-settings.d.ts +34 -0
- package/dist/routes/pages/dream-settings.d.ts.map +1 -0
- package/dist/routes/pages/dream-settings.js +68 -0
- package/dist/routes/pages/dream-settings.js.map +1 -0
- package/dist/routes/pages/dreams.d.ts +5 -0
- package/dist/routes/pages/dreams.d.ts.map +1 -0
- package/dist/routes/pages/dreams.js +435 -0
- package/dist/routes/pages/dreams.js.map +1 -0
- package/dist/routes/pages/integrations.d.ts.map +1 -1
- package/dist/routes/pages/integrations.js +20 -15
- package/dist/routes/pages/integrations.js.map +1 -1
- package/dist/routes/pages/new-app.js +1 -1
- package/dist/routes/pages/new-app.js.map +1 -1
- package/dist/routes/pages/overview.d.ts.map +1 -1
- package/dist/routes/pages/overview.js +5 -1
- package/dist/routes/pages/overview.js.map +1 -1
- package/dist/routes/pages/vault.d.ts.map +1 -1
- package/dist/routes/pages/vault.js +23 -5
- package/dist/routes/pages/vault.js.map +1 -1
- package/dist/routes/pages/workspace.d.ts.map +1 -1
- package/dist/routes/pages/workspace.js +187 -35
- package/dist/routes/pages/workspace.js.map +1 -1
- package/dist/server/lib/app-creation-store.d.ts +13 -0
- package/dist/server/lib/app-creation-store.d.ts.map +1 -1
- package/dist/server/lib/app-creation-store.js +298 -11
- package/dist/server/lib/app-creation-store.js.map +1 -1
- package/dist/server/lib/dispatch-integrations.d.ts +1 -1
- package/dist/server/lib/dispatch-integrations.d.ts.map +1 -1
- package/dist/server/lib/dispatch-integrations.js +9 -4
- package/dist/server/lib/dispatch-integrations.js.map +1 -1
- package/dist/server/lib/dispatch-remote-commands.d.ts +83 -0
- package/dist/server/lib/dispatch-remote-commands.d.ts.map +1 -0
- package/dist/server/lib/dispatch-remote-commands.js +256 -0
- package/dist/server/lib/dispatch-remote-commands.js.map +1 -0
- package/dist/server/lib/dispatch-store.d.ts +26 -0
- package/dist/server/lib/dispatch-store.d.ts.map +1 -1
- package/dist/server/lib/dispatch-store.js +17 -1
- package/dist/server/lib/dispatch-store.js.map +1 -1
- package/dist/server/lib/dreams-store.d.ts +398 -0
- package/dist/server/lib/dreams-store.d.ts.map +1 -0
- package/dist/server/lib/dreams-store.js +2330 -0
- package/dist/server/lib/dreams-store.js.map +1 -0
- package/dist/server/lib/env-config.d.ts.map +1 -1
- package/dist/server/lib/env-config.js +5 -0
- package/dist/server/lib/env-config.js.map +1 -1
- package/dist/server/lib/onboarding-steps.d.ts +12 -0
- package/dist/server/lib/onboarding-steps.d.ts.map +1 -0
- package/dist/server/lib/onboarding-steps.js +47 -0
- package/dist/server/lib/onboarding-steps.js.map +1 -0
- package/dist/server/lib/thread-debug-store.d.ts +2 -2
- package/dist/server/lib/vault-store.d.ts +55 -0
- package/dist/server/lib/vault-store.d.ts.map +1 -1
- package/dist/server/lib/vault-store.js +210 -41
- package/dist/server/lib/vault-store.js.map +1 -1
- package/dist/server/lib/workspace-resources-store.d.ts +181 -17
- package/dist/server/lib/workspace-resources-store.d.ts.map +1 -1
- package/dist/server/lib/workspace-resources-store.js +737 -108
- package/dist/server/lib/workspace-resources-store.js.map +1 -1
- package/dist/server/plugins/agent-chat.d.ts.map +1 -1
- package/dist/server/plugins/agent-chat.js +2 -1
- package/dist/server/plugins/agent-chat.js.map +1 -1
- package/dist/server/plugins/core-routes.d.ts.map +1 -1
- package/dist/server/plugins/core-routes.js +4 -0
- package/dist/server/plugins/core-routes.js.map +1 -1
- package/dist/server/plugins/integrations.js +2 -2
- package/dist/server/plugins/integrations.js.map +1 -1
- package/package.json +15 -11
- package/src/actions/apply-dream-proposal.ts +12 -0
- package/src/actions/create-dream-report.ts +76 -0
- package/src/actions/create-pylon-ticket.ts +109 -0
- package/src/actions/create-vault-grant.ts +1 -1
- package/src/actions/create-vault-secret.ts +4 -3
- package/src/actions/create-workspace-resource.ts +3 -3
- package/src/actions/delete-workspace-resource.ts +1 -1
- package/src/actions/ensure-dream-job.ts +76 -0
- package/src/actions/get-dream-settings.ts +12 -0
- package/src/actions/get-dream.ts +14 -0
- package/src/actions/get-vault-access-settings.ts +11 -0
- package/src/actions/get-workspace-resource-effective-context.ts +34 -0
- package/src/actions/grant-vault-secrets-to-app.ts +1 -1
- package/src/actions/index.spec.ts +26 -0
- package/src/actions/index.ts +39 -4
- package/src/actions/list-dream-candidates.ts +77 -0
- package/src/actions/list-dreams.ts +17 -0
- package/src/actions/list-integrations-catalog.ts +1 -1
- package/src/actions/list-vault-grants.ts +1 -1
- package/src/actions/list-workspace-apps.ts +5 -1
- package/src/actions/list-workspace-resources-for-app.ts +13 -0
- package/src/actions/list-workspace-resources.ts +1 -1
- package/src/actions/navigate.ts +2 -1
- package/src/actions/preview-dream-proposal.ts +14 -0
- package/src/actions/preview-workspace-resource-change.ts +25 -0
- package/src/actions/reject-dream-proposal.ts +12 -0
- package/src/actions/restore-starter-workspace-resources.ts +17 -0
- package/src/actions/send-code-agent-remote-command.ts +59 -0
- package/src/actions/set-dream-settings.spec.ts +81 -0
- package/src/actions/set-dream-settings.ts +44 -0
- package/src/actions/set-vault-access-settings.ts +16 -0
- package/src/actions/start-workspace-app-creation.ts +8 -0
- package/src/actions/sync-vault-to-app.ts +1 -1
- package/src/actions/update-workspace-app-metadata.ts +32 -0
- package/src/actions/update-workspace-resource.ts +1 -1
- package/src/actions/view-screen.ts +94 -3
- package/src/components/app-keys-popover.tsx +23 -7
- package/src/components/approval-value-block.spec.tsx +59 -0
- package/src/components/approval-value-block.tsx +33 -0
- package/src/components/create-app-popover.tsx +50 -16
- package/src/components/dispatch-shell.tsx +16 -15
- package/src/components/layout/Layout.tsx +19 -5
- package/src/components/messaging-setup-panel.tsx +54 -39
- package/src/components/workspace-app-card.tsx +268 -1
- package/src/components/workspace-resource-effective-stack.spec.tsx +125 -0
- package/src/components/workspace-resource-effective-stack.tsx +141 -0
- package/src/components/workspace-resource-impact-preview.spec.tsx +147 -0
- package/src/components/workspace-resource-impact-preview.tsx +116 -0
- package/src/db/migrations.ts +59 -0
- package/src/db/schema.ts +46 -2
- package/src/hooks/use-navigation-state.ts +34 -9
- package/src/lib/catch-all-target.spec.ts +218 -0
- package/src/lib/catch-all-target.ts +99 -0
- package/src/lib/utils.ts +6 -1
- package/src/lib/workspace-apps.ts +9 -0
- package/src/routes/index.ts +1 -0
- package/src/routes/pages/$appId.tsx +21 -8
- package/src/routes/pages/approval.tsx +14 -1
- package/src/routes/pages/approvals.tsx +1 -1
- package/src/routes/pages/dream-settings.spec.ts +130 -0
- package/src/routes/pages/dream-settings.ts +103 -0
- package/src/routes/pages/dreams.tsx +1828 -0
- package/src/routes/pages/integrations.tsx +57 -18
- package/src/routes/pages/new-app.tsx +1 -1
- package/src/routes/pages/overview.tsx +11 -3
- package/src/routes/pages/vault.tsx +76 -9
- package/src/routes/pages/workspace.tsx +577 -97
- package/src/server/lib/app-creation-store.spec.ts +61 -2
- package/src/server/lib/app-creation-store.ts +389 -13
- package/src/server/lib/dispatch-integrations.ts +10 -3
- package/src/server/lib/dispatch-remote-commands.spec.ts +167 -0
- package/src/server/lib/dispatch-remote-commands.ts +375 -0
- package/src/server/lib/dispatch-store.ts +37 -1
- package/src/server/lib/dreams-store.spec.ts +1492 -0
- package/src/server/lib/dreams-store.ts +3168 -0
- package/src/server/lib/env-config.ts +5 -0
- package/src/server/lib/onboarding-steps.ts +49 -0
- package/src/server/lib/vault-store.spec.ts +69 -0
- package/src/server/lib/vault-store.ts +266 -49
- package/src/server/lib/workspace-resource-approval-lifecycle.spec.ts +236 -0
- package/src/server/lib/workspace-resources-store.spec.ts +1106 -0
- package/src/server/lib/workspace-resources-store.ts +1001 -134
- package/src/server/plugins/agent-chat.ts +2 -1
- package/src/server/plugins/core-routes.ts +5 -0
- package/src/server/plugins/integrations.ts +2 -2
- package/dist/actions/sync-workspace-resources-to-all.d.ts +0 -3
- package/dist/actions/sync-workspace-resources-to-all.d.ts.map +0 -1
- package/dist/actions/sync-workspace-resources-to-all.js +0 -9
- package/dist/actions/sync-workspace-resources-to-all.js.map +0 -1
- package/dist/actions/sync-workspace-resources-to-app.d.ts +0 -3
- package/dist/actions/sync-workspace-resources-to-app.d.ts.map +0 -1
- package/dist/actions/sync-workspace-resources-to-app.js +0 -11
- package/dist/actions/sync-workspace-resources-to-app.js.map +0 -1
- package/src/actions/sync-workspace-resources-to-all.ts +0 -10
- package/src/actions/sync-workspace-resources-to-app.ts +0 -12
|
@@ -3,6 +3,7 @@ import fs from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { getSetting, putSetting } from "@agent-native/core/settings";
|
|
6
|
+
import { assertValidWorkspaceAppId } from "@agent-native/core/shared";
|
|
6
7
|
import {
|
|
7
8
|
getBuilderBranchProjectId,
|
|
8
9
|
getRequestContext,
|
|
@@ -12,7 +13,6 @@ import {
|
|
|
12
13
|
runBuilderAgent,
|
|
13
14
|
} from "@agent-native/core/server";
|
|
14
15
|
import { getDbExec } from "@agent-native/core/db";
|
|
15
|
-
import { assertValidWorkspaceAppId } from "@agent-native/core/shared";
|
|
16
16
|
import {
|
|
17
17
|
currentOrgId,
|
|
18
18
|
currentOwnerEmail,
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
} from "./workspace-resources-store.js";
|
|
29
29
|
|
|
30
30
|
const SETTINGS_KEY = "dispatch-app-creation-settings";
|
|
31
|
+
const WORKSPACE_APP_METADATA_SETTINGS_KEY = "workspace-app-metadata";
|
|
31
32
|
const WORKSPACE_APPS_ENV_KEY = "AGENT_NATIVE_WORKSPACE_APPS_JSON";
|
|
32
33
|
const WORKSPACE_APPS_MANIFEST_FILE = "workspace-apps.json";
|
|
33
34
|
const WORKSPACE_APPS_GATEWAY_PATH = "/_workspace/apps";
|
|
@@ -35,6 +36,9 @@ const WORKSPACE_APPS_GATEWAY_TIMEOUT_MS = 1_000;
|
|
|
35
36
|
const MAX_PENDING_APPS = 50;
|
|
36
37
|
const AGENT_CARD_PATH = "/.well-known/agent-card.json";
|
|
37
38
|
const AGENT_CARD_FETCH_TIMEOUT_MS = 1_500;
|
|
39
|
+
const DEFAULT_WORKSPACE_APP_AUDIENCE = "internal";
|
|
40
|
+
|
|
41
|
+
type WorkspaceAppAudience = "internal" | "public";
|
|
38
42
|
|
|
39
43
|
export interface WorkspaceAppSummary {
|
|
40
44
|
id: string;
|
|
@@ -43,6 +47,9 @@ export interface WorkspaceAppSummary {
|
|
|
43
47
|
path: string;
|
|
44
48
|
url: string | null;
|
|
45
49
|
isDispatch: boolean;
|
|
50
|
+
audience: WorkspaceAppAudience;
|
|
51
|
+
publicPaths: string[];
|
|
52
|
+
protectedPaths: string[];
|
|
46
53
|
status?: "ready" | "pending";
|
|
47
54
|
statusLabel?: string;
|
|
48
55
|
builderUrl?: string | null;
|
|
@@ -64,6 +71,7 @@ export interface ListWorkspaceAppsOptions {
|
|
|
64
71
|
* when rendering the "Hidden apps" expander.
|
|
65
72
|
*/
|
|
66
73
|
includeArchived?: boolean;
|
|
74
|
+
audience?: WorkspaceAppAudience | "all";
|
|
67
75
|
}
|
|
68
76
|
|
|
69
77
|
export interface AvailableWorkspaceTemplate {
|
|
@@ -103,10 +111,24 @@ interface PendingWorkspaceApp {
|
|
|
103
111
|
builderUrl: string | null;
|
|
104
112
|
branchName: string | null;
|
|
105
113
|
projectId: string | null;
|
|
114
|
+
audience?: WorkspaceAppAudience;
|
|
106
115
|
createdAt: string;
|
|
107
116
|
updatedAt: string;
|
|
108
117
|
}
|
|
109
118
|
|
|
119
|
+
interface WorkspaceAppMetadataOverride {
|
|
120
|
+
name?: string;
|
|
121
|
+
description?: string;
|
|
122
|
+
generated?: boolean;
|
|
123
|
+
sourcePrompt?: string;
|
|
124
|
+
updatedAt?: string;
|
|
125
|
+
updatedBy?: string;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
interface WorkspaceAppMetadataSettings {
|
|
129
|
+
apps: Record<string, WorkspaceAppMetadataOverride>;
|
|
130
|
+
}
|
|
131
|
+
|
|
110
132
|
function readJson(file: string): any {
|
|
111
133
|
try {
|
|
112
134
|
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
@@ -137,12 +159,56 @@ function titleCase(value: string): string {
|
|
|
137
159
|
.join(" ");
|
|
138
160
|
}
|
|
139
161
|
|
|
162
|
+
function normalizeWhitespace(value: string): string {
|
|
163
|
+
return value.replace(/\s+/g, " ").trim();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function ensureSentence(value: string): string {
|
|
167
|
+
if (!value) return value;
|
|
168
|
+
const capitalized = value.charAt(0).toUpperCase() + value.slice(1);
|
|
169
|
+
return /[.!?]$/.test(capitalized) ? capitalized : `${capitalized}.`;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function clipSentence(value: string, max = 180): string {
|
|
173
|
+
if (value.length <= max) return value;
|
|
174
|
+
const clipped = value
|
|
175
|
+
.slice(0, max - 1)
|
|
176
|
+
.replace(/\s+\S*$/, "")
|
|
177
|
+
.trim();
|
|
178
|
+
return `${clipped || value.slice(0, max - 1).trim()}…`;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function generateWorkspaceAppDescription(
|
|
182
|
+
prompt: string,
|
|
183
|
+
appId: string,
|
|
184
|
+
): string {
|
|
185
|
+
const cleaned = normalizeWhitespace(prompt)
|
|
186
|
+
.replace(
|
|
187
|
+
/^(please\s+)?(build|create|make|generate|scaffold)\s+(me\s+|us\s+)?/i,
|
|
188
|
+
"",
|
|
189
|
+
)
|
|
190
|
+
.replace(
|
|
191
|
+
/^(an?\s+)?(workspace\s+)?(agent-native\s+)?(app|tool)\s+(that|to|for)\s+/i,
|
|
192
|
+
"",
|
|
193
|
+
)
|
|
194
|
+
.replace(/^(an?\s+)?(dashboard|workspace|agent)\s+(that|to|for)\s+/i, "");
|
|
195
|
+
|
|
196
|
+
if (!cleaned) return `Workspace app for ${titleCase(appId)}.`;
|
|
197
|
+
return clipSentence(ensureSentence(cleaned));
|
|
198
|
+
}
|
|
199
|
+
|
|
140
200
|
function scopedSettingsKey(): string {
|
|
141
201
|
const orgId = currentOrgId();
|
|
142
202
|
if (orgId) return `${SETTINGS_KEY}:org:${orgId}`;
|
|
143
203
|
return `${SETTINGS_KEY}:user:${currentOwnerEmail()}`;
|
|
144
204
|
}
|
|
145
205
|
|
|
206
|
+
function workspaceAppMetadataSettingsKey(): string {
|
|
207
|
+
const orgId = currentOrgId();
|
|
208
|
+
if (orgId) return `${WORKSPACE_APP_METADATA_SETTINGS_KEY}:org:${orgId}`;
|
|
209
|
+
return `${WORKSPACE_APP_METADATA_SETTINGS_KEY}:user:${currentOwnerEmail()}`;
|
|
210
|
+
}
|
|
211
|
+
|
|
146
212
|
async function readSettingsRecord(): Promise<Record<string, any>> {
|
|
147
213
|
const raw = await getSetting(scopedSettingsKey()).catch(() => null);
|
|
148
214
|
return raw && typeof raw === "object" && !Array.isArray(raw)
|
|
@@ -150,6 +216,114 @@ async function readSettingsRecord(): Promise<Record<string, any>> {
|
|
|
150
216
|
: {};
|
|
151
217
|
}
|
|
152
218
|
|
|
219
|
+
function cleanOptionalText(value: unknown): string | undefined {
|
|
220
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function parseWorkspaceAppMetadataSettings(
|
|
224
|
+
raw: unknown,
|
|
225
|
+
): WorkspaceAppMetadataSettings {
|
|
226
|
+
const record =
|
|
227
|
+
raw && typeof raw === "object" && !Array.isArray(raw)
|
|
228
|
+
? (raw as Record<string, unknown>)
|
|
229
|
+
: {};
|
|
230
|
+
const rawApps =
|
|
231
|
+
record.apps &&
|
|
232
|
+
typeof record.apps === "object" &&
|
|
233
|
+
!Array.isArray(record.apps)
|
|
234
|
+
? (record.apps as Record<string, unknown>)
|
|
235
|
+
: {};
|
|
236
|
+
const apps: Record<string, WorkspaceAppMetadataOverride> = {};
|
|
237
|
+
|
|
238
|
+
for (const [id, value] of Object.entries(rawApps)) {
|
|
239
|
+
if (!id.trim() || !value || typeof value !== "object") continue;
|
|
240
|
+
const item = value as Record<string, unknown>;
|
|
241
|
+
const override: WorkspaceAppMetadataOverride = {};
|
|
242
|
+
const name = cleanOptionalText(item.name);
|
|
243
|
+
const description = cleanOptionalText(item.description);
|
|
244
|
+
const sourcePrompt = cleanOptionalText(item.sourcePrompt);
|
|
245
|
+
const updatedAt = cleanOptionalText(item.updatedAt);
|
|
246
|
+
const updatedBy = cleanOptionalText(item.updatedBy);
|
|
247
|
+
|
|
248
|
+
if (name) override.name = name;
|
|
249
|
+
if (description) override.description = description;
|
|
250
|
+
if (item.generated === true) override.generated = true;
|
|
251
|
+
if (sourcePrompt) override.sourcePrompt = sourcePrompt;
|
|
252
|
+
if (updatedAt) override.updatedAt = updatedAt;
|
|
253
|
+
if (updatedBy) override.updatedBy = updatedBy;
|
|
254
|
+
|
|
255
|
+
if (Object.keys(override).length > 0) apps[id.trim()] = override;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return { apps };
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async function readWorkspaceAppMetadataSettings(): Promise<WorkspaceAppMetadataSettings> {
|
|
262
|
+
const raw = await getSetting(workspaceAppMetadataSettingsKey()).catch(
|
|
263
|
+
() => null,
|
|
264
|
+
);
|
|
265
|
+
return parseWorkspaceAppMetadataSettings(raw);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async function writeWorkspaceAppMetadataOverride(input: {
|
|
269
|
+
appId: string;
|
|
270
|
+
name?: string | null;
|
|
271
|
+
description?: string | null;
|
|
272
|
+
generated?: boolean;
|
|
273
|
+
sourcePrompt?: string | null;
|
|
274
|
+
updatedBy?: string | null;
|
|
275
|
+
}): Promise<WorkspaceAppMetadataSettings> {
|
|
276
|
+
const key = workspaceAppMetadataSettingsKey();
|
|
277
|
+
const current = parseWorkspaceAppMetadataSettings(
|
|
278
|
+
await getSetting(key).catch(() => null),
|
|
279
|
+
);
|
|
280
|
+
const appId = input.appId.trim();
|
|
281
|
+
const existing = current.apps[appId] ?? {};
|
|
282
|
+
const next: WorkspaceAppMetadataOverride = {
|
|
283
|
+
...existing,
|
|
284
|
+
updatedAt: new Date().toISOString(),
|
|
285
|
+
};
|
|
286
|
+
const name = cleanOptionalText(input.name);
|
|
287
|
+
const description = cleanOptionalText(input.description);
|
|
288
|
+
const sourcePrompt = cleanOptionalText(input.sourcePrompt);
|
|
289
|
+
const updatedBy = cleanOptionalText(input.updatedBy);
|
|
290
|
+
|
|
291
|
+
if (name) next.name = name;
|
|
292
|
+
else delete next.name;
|
|
293
|
+
if (description) next.description = description;
|
|
294
|
+
else delete next.description;
|
|
295
|
+
if (input.generated === true) next.generated = true;
|
|
296
|
+
else if (input.generated === false) delete next.generated;
|
|
297
|
+
if (sourcePrompt) next.sourcePrompt = sourcePrompt;
|
|
298
|
+
if (updatedBy) next.updatedBy = updatedBy;
|
|
299
|
+
|
|
300
|
+
current.apps[appId] = next;
|
|
301
|
+
await putSetting(key, { apps: current.apps });
|
|
302
|
+
return current;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function applyWorkspaceAppMetadataOverride(
|
|
306
|
+
app: WorkspaceAppSummary,
|
|
307
|
+
settings: WorkspaceAppMetadataSettings,
|
|
308
|
+
): WorkspaceAppSummary {
|
|
309
|
+
const override = settings.apps[app.id];
|
|
310
|
+
if (!override) return app;
|
|
311
|
+
|
|
312
|
+
const name = cleanOptionalText(override.name);
|
|
313
|
+
const description = cleanOptionalText(override.description);
|
|
314
|
+
const generated = override.generated === true;
|
|
315
|
+
const shouldApplyName = !!name && !generated;
|
|
316
|
+
const shouldApplyDescription =
|
|
317
|
+
!!description && (!generated || !cleanOptionalText(app.description));
|
|
318
|
+
if (!shouldApplyName && !shouldApplyDescription) return app;
|
|
319
|
+
|
|
320
|
+
return {
|
|
321
|
+
...app,
|
|
322
|
+
...(shouldApplyName ? { name } : {}),
|
|
323
|
+
...(shouldApplyDescription ? { description } : {}),
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
153
327
|
function workspaceAppUrl(appPath: string): string | null {
|
|
154
328
|
const base =
|
|
155
329
|
process.env.WORKSPACE_GATEWAY_URL ||
|
|
@@ -180,6 +354,85 @@ function workspaceAppLink(
|
|
|
180
354
|
}
|
|
181
355
|
}
|
|
182
356
|
|
|
357
|
+
function normalizeWorkspaceAppAudience(value: unknown): WorkspaceAppAudience {
|
|
358
|
+
return value === "public" ? "public" : DEFAULT_WORKSPACE_APP_AUDIENCE;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function normalizeWorkspaceAppPathList(value: unknown): string[] {
|
|
362
|
+
let rawPaths: unknown[] = [];
|
|
363
|
+
if (Array.isArray(value)) {
|
|
364
|
+
rawPaths = value;
|
|
365
|
+
} else if (typeof value === "string") {
|
|
366
|
+
const trimmed = value.trim();
|
|
367
|
+
if (!trimmed) return [];
|
|
368
|
+
try {
|
|
369
|
+
const parsed = JSON.parse(trimmed);
|
|
370
|
+
rawPaths = Array.isArray(parsed) ? parsed : [trimmed];
|
|
371
|
+
} catch {
|
|
372
|
+
rawPaths = trimmed.split(",");
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const paths = rawPaths
|
|
377
|
+
.map((entry) => (typeof entry === "string" ? entry.trim() : ""))
|
|
378
|
+
.filter((entry) => entry.startsWith("/"))
|
|
379
|
+
.map((entry) =>
|
|
380
|
+
entry.length > 1 && entry.endsWith("/") ? entry.slice(0, -1) : entry,
|
|
381
|
+
);
|
|
382
|
+
return Array.from(new Set(paths));
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function workspaceAppAudienceFromPackageJson(
|
|
386
|
+
pkg: unknown,
|
|
387
|
+
): WorkspaceAppAudience | undefined {
|
|
388
|
+
if (!pkg || typeof pkg !== "object" || Array.isArray(pkg)) return undefined;
|
|
389
|
+
const record = pkg as Record<string, any>;
|
|
390
|
+
const config = record["agent-native"] ?? record.agentNative;
|
|
391
|
+
const nested =
|
|
392
|
+
config && typeof config === "object" && !Array.isArray(config)
|
|
393
|
+
? (config as Record<string, any>)
|
|
394
|
+
: {};
|
|
395
|
+
const raw =
|
|
396
|
+
nested.workspaceApp?.audience ??
|
|
397
|
+
nested.workspace?.audience ??
|
|
398
|
+
nested.audience ??
|
|
399
|
+
record.workspaceAppAudience;
|
|
400
|
+
if (raw === undefined) return undefined;
|
|
401
|
+
return normalizeWorkspaceAppAudience(raw);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function workspaceAppRouteAccessFromPackageJson(pkg: unknown): {
|
|
405
|
+
publicPaths: string[];
|
|
406
|
+
protectedPaths: string[];
|
|
407
|
+
} {
|
|
408
|
+
if (!pkg || typeof pkg !== "object" || Array.isArray(pkg)) {
|
|
409
|
+
return { publicPaths: [], protectedPaths: [] };
|
|
410
|
+
}
|
|
411
|
+
const record = pkg as Record<string, any>;
|
|
412
|
+
const config = record["agent-native"] ?? record.agentNative;
|
|
413
|
+
const nested =
|
|
414
|
+
config && typeof config === "object" && !Array.isArray(config)
|
|
415
|
+
? (config as Record<string, any>)
|
|
416
|
+
: {};
|
|
417
|
+
return {
|
|
418
|
+
publicPaths: normalizeWorkspaceAppPathList(
|
|
419
|
+
nested.workspaceApp?.publicPaths ??
|
|
420
|
+
nested.workspaceApp?.publicPagePaths ??
|
|
421
|
+
nested.workspace?.publicPaths ??
|
|
422
|
+
nested.publicPaths ??
|
|
423
|
+
record.workspaceAppPublicPaths,
|
|
424
|
+
),
|
|
425
|
+
protectedPaths: normalizeWorkspaceAppPathList(
|
|
426
|
+
nested.workspaceApp?.protectedPaths ??
|
|
427
|
+
nested.workspaceApp?.privatePaths ??
|
|
428
|
+
nested.workspaceApp?.authRequiredPaths ??
|
|
429
|
+
nested.workspace?.protectedPaths ??
|
|
430
|
+
nested.protectedPaths ??
|
|
431
|
+
record.workspaceAppProtectedPaths,
|
|
432
|
+
),
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
|
|
183
436
|
function parseWorkspaceAppsManifest(parsed: any): WorkspaceAppSummary[] | null {
|
|
184
437
|
const rawApps = Array.isArray(parsed?.apps)
|
|
185
438
|
? parsed.apps
|
|
@@ -208,6 +461,12 @@ function parseWorkspaceAppsManifest(parsed: any): WorkspaceAppSummary[] | null {
|
|
|
208
461
|
typeof entry.isDispatch === "boolean"
|
|
209
462
|
? entry.isDispatch
|
|
210
463
|
: id === "dispatch",
|
|
464
|
+
audience:
|
|
465
|
+
entry.audience === undefined
|
|
466
|
+
? DEFAULT_WORKSPACE_APP_AUDIENCE
|
|
467
|
+
: normalizeWorkspaceAppAudience(entry.audience),
|
|
468
|
+
publicPaths: normalizeWorkspaceAppPathList(entry.publicPaths),
|
|
469
|
+
protectedPaths: normalizeWorkspaceAppPathList(entry.protectedPaths),
|
|
211
470
|
status: "ready",
|
|
212
471
|
} satisfies WorkspaceAppSummary;
|
|
213
472
|
})
|
|
@@ -228,7 +487,7 @@ function sortWorkspaceApps(a: WorkspaceAppSummary, b: WorkspaceAppSummary) {
|
|
|
228
487
|
function parsePendingWorkspaceApps(value: unknown): PendingWorkspaceApp[] {
|
|
229
488
|
if (!Array.isArray(value)) return [];
|
|
230
489
|
return value
|
|
231
|
-
.map((entry) => {
|
|
490
|
+
.map((entry): PendingWorkspaceApp | null => {
|
|
232
491
|
if (!entry || typeof entry !== "object") return null;
|
|
233
492
|
const record = entry as Record<string, unknown>;
|
|
234
493
|
const id = typeof record.id === "string" ? record.id.trim() : "";
|
|
@@ -259,6 +518,9 @@ function parsePendingWorkspaceApps(value: unknown): PendingWorkspaceApp[] {
|
|
|
259
518
|
typeof record.projectId === "string" && record.projectId.trim()
|
|
260
519
|
? record.projectId.trim()
|
|
261
520
|
: null,
|
|
521
|
+
...(record.audience === undefined
|
|
522
|
+
? {}
|
|
523
|
+
: { audience: normalizeWorkspaceAppAudience(record.audience) }),
|
|
262
524
|
createdAt:
|
|
263
525
|
typeof record.createdAt === "string" && record.createdAt.trim()
|
|
264
526
|
? record.createdAt.trim()
|
|
@@ -356,6 +618,9 @@ function pendingAppToSummary(app: PendingWorkspaceApp): WorkspaceAppSummary {
|
|
|
356
618
|
path: app.path,
|
|
357
619
|
url: app.builderUrl,
|
|
358
620
|
isDispatch: false,
|
|
621
|
+
audience: app.audience ?? DEFAULT_WORKSPACE_APP_AUDIENCE,
|
|
622
|
+
publicPaths: [],
|
|
623
|
+
protectedPaths: [],
|
|
359
624
|
status: "pending",
|
|
360
625
|
statusLabel: "Building in Builder",
|
|
361
626
|
builderUrl: app.builderUrl,
|
|
@@ -494,6 +759,8 @@ async function maybeIncludeAgentCards(
|
|
|
494
759
|
async function recordPendingWorkspaceApp(input: {
|
|
495
760
|
appId: string;
|
|
496
761
|
projectId: string | null;
|
|
762
|
+
description: string;
|
|
763
|
+
sourcePrompt: string;
|
|
497
764
|
branchName?: string | null;
|
|
498
765
|
builderUrl?: string | null;
|
|
499
766
|
}) {
|
|
@@ -505,6 +772,7 @@ async function recordPendingWorkspaceApp(input: {
|
|
|
505
772
|
id: input.appId,
|
|
506
773
|
name: titleCase(input.appId),
|
|
507
774
|
description:
|
|
775
|
+
input.description ||
|
|
508
776
|
"Builder is creating this app. The workspace path becomes live after the branch is merged and deployed.",
|
|
509
777
|
path: `/${input.appId}`,
|
|
510
778
|
builderUrl: input.builderUrl?.trim() || null,
|
|
@@ -522,6 +790,14 @@ async function recordPendingWorkspaceApp(input: {
|
|
|
522
790
|
].slice(0, MAX_PENDING_APPS),
|
|
523
791
|
});
|
|
524
792
|
|
|
793
|
+
await writeWorkspaceAppMetadataOverride({
|
|
794
|
+
appId: input.appId,
|
|
795
|
+
description: input.description,
|
|
796
|
+
generated: true,
|
|
797
|
+
sourcePrompt: input.sourcePrompt,
|
|
798
|
+
updatedBy: currentOwnerEmail(),
|
|
799
|
+
});
|
|
800
|
+
|
|
525
801
|
await recordAudit({
|
|
526
802
|
action: "workspace-app.pending",
|
|
527
803
|
targetType: "workspace-app",
|
|
@@ -614,11 +890,12 @@ function readWorkspaceAppsFromFilesystem(
|
|
|
614
890
|
|
|
615
891
|
const apps = fs
|
|
616
892
|
.readdirSync(appsDir, { withFileTypes: true })
|
|
617
|
-
.filter((entry) => entry.isDirectory())
|
|
893
|
+
.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."))
|
|
618
894
|
.map((entry): WorkspaceAppSummary | null => {
|
|
619
895
|
const appDir = path.join(appsDir, entry.name);
|
|
620
896
|
const pkg = readJson(path.join(appDir, "package.json"));
|
|
621
897
|
if (!pkg) return null;
|
|
898
|
+
const routeAccess = workspaceAppRouteAccessFromPackageJson(pkg);
|
|
622
899
|
return {
|
|
623
900
|
id: entry.name,
|
|
624
901
|
name: pkg.displayName || titleCase(entry.name),
|
|
@@ -626,6 +903,11 @@ function readWorkspaceAppsFromFilesystem(
|
|
|
626
903
|
path: `/${entry.name}`,
|
|
627
904
|
url: workspaceAppUrl(`/${entry.name}`),
|
|
628
905
|
isDispatch: entry.name === "dispatch",
|
|
906
|
+
audience:
|
|
907
|
+
workspaceAppAudienceFromPackageJson(pkg) ??
|
|
908
|
+
DEFAULT_WORKSPACE_APP_AUDIENCE,
|
|
909
|
+
publicPaths: routeAccess.publicPaths,
|
|
910
|
+
protectedPaths: routeAccess.protectedPaths,
|
|
629
911
|
status: "ready",
|
|
630
912
|
} satisfies WorkspaceAppSummary;
|
|
631
913
|
})
|
|
@@ -689,17 +971,92 @@ async function applyArchivedAndPending(
|
|
|
689
971
|
apps: WorkspaceAppSummary[],
|
|
690
972
|
options: ListWorkspaceAppsOptions,
|
|
691
973
|
): Promise<WorkspaceAppSummary[]> {
|
|
692
|
-
const [withPending, archivedIds] = await Promise.all([
|
|
974
|
+
const [withPending, archivedIds, metadataSettings] = await Promise.all([
|
|
693
975
|
appendPendingWorkspaceApps(apps),
|
|
694
976
|
listArchivedAppIds(),
|
|
977
|
+
readWorkspaceAppMetadataSettings(),
|
|
695
978
|
]);
|
|
696
979
|
const archivedSet = new Set(archivedIds);
|
|
697
|
-
const annotated = withPending.map((app) =>
|
|
698
|
-
|
|
699
|
-
|
|
980
|
+
const annotated = withPending.map((app) => {
|
|
981
|
+
const withMetadata = applyWorkspaceAppMetadataOverride(
|
|
982
|
+
app,
|
|
983
|
+
metadataSettings,
|
|
984
|
+
);
|
|
985
|
+
return archivedSet.has(app.id)
|
|
986
|
+
? { ...withMetadata, archived: true }
|
|
987
|
+
: withMetadata;
|
|
988
|
+
});
|
|
700
989
|
return options.includeArchived
|
|
701
|
-
? annotated
|
|
702
|
-
:
|
|
990
|
+
? filterAppsByAudience(annotated, options.audience)
|
|
991
|
+
: filterAppsByAudience(
|
|
992
|
+
annotated.filter((app) => !app.archived),
|
|
993
|
+
options.audience,
|
|
994
|
+
);
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
function filterAppsByAudience(
|
|
998
|
+
apps: WorkspaceAppSummary[],
|
|
999
|
+
audience: ListWorkspaceAppsOptions["audience"],
|
|
1000
|
+
): WorkspaceAppSummary[] {
|
|
1001
|
+
if (!audience || audience === "all") return apps;
|
|
1002
|
+
return apps.filter(
|
|
1003
|
+
(app) =>
|
|
1004
|
+
(app.audience ?? DEFAULT_WORKSPACE_APP_AUDIENCE) ===
|
|
1005
|
+
normalizeWorkspaceAppAudience(audience),
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
export async function updateWorkspaceAppMetadata(input: {
|
|
1010
|
+
appId: string;
|
|
1011
|
+
name?: string | null;
|
|
1012
|
+
description?: string | null;
|
|
1013
|
+
}): Promise<WorkspaceAppSummary> {
|
|
1014
|
+
await assertCanManageAppCreationSettings();
|
|
1015
|
+
const appId = input.appId.trim();
|
|
1016
|
+
assertValidWorkspaceAppId(appId);
|
|
1017
|
+
|
|
1018
|
+
const apps = await listWorkspaceApps({
|
|
1019
|
+
includeAgentCards: false,
|
|
1020
|
+
includeArchived: true,
|
|
1021
|
+
});
|
|
1022
|
+
const app = apps.find((candidate) => candidate.id === appId);
|
|
1023
|
+
if (!app) throw new Error(`Workspace app "${appId}" was not found.`);
|
|
1024
|
+
|
|
1025
|
+
// Treat undefined/null as "field omitted, leave existing value alone"; an
|
|
1026
|
+
// explicit empty string clears the override (the app reverts to its
|
|
1027
|
+
// built-in name / no description). Without this, a partial update that
|
|
1028
|
+
// only touches one field silently wipes the other.
|
|
1029
|
+
const name = input.name == null ? app.name : input.name.trim();
|
|
1030
|
+
const description =
|
|
1031
|
+
input.description == null
|
|
1032
|
+
? (app.description ?? undefined)
|
|
1033
|
+
: input.description.trim();
|
|
1034
|
+
await writeWorkspaceAppMetadataOverride({
|
|
1035
|
+
appId,
|
|
1036
|
+
name,
|
|
1037
|
+
description,
|
|
1038
|
+
generated: false,
|
|
1039
|
+
updatedBy: currentOwnerEmail(),
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1042
|
+
await recordAudit({
|
|
1043
|
+
action: "workspace-app.metadata-updated",
|
|
1044
|
+
targetType: "workspace-app",
|
|
1045
|
+
targetId: appId,
|
|
1046
|
+
summary: `Updated workspace app details for ${name}`,
|
|
1047
|
+
metadata: {
|
|
1048
|
+
name,
|
|
1049
|
+
descriptionConfigured: !!description,
|
|
1050
|
+
},
|
|
1051
|
+
});
|
|
1052
|
+
|
|
1053
|
+
const updated = (
|
|
1054
|
+
await listWorkspaceApps({
|
|
1055
|
+
includeAgentCards: false,
|
|
1056
|
+
includeArchived: true,
|
|
1057
|
+
})
|
|
1058
|
+
).find((candidate) => candidate.id === appId);
|
|
1059
|
+
return updated ?? { ...app, name, description };
|
|
703
1060
|
}
|
|
704
1061
|
|
|
705
1062
|
export async function listWorkspaceApps(
|
|
@@ -745,6 +1102,9 @@ export async function listWorkspaceApps(
|
|
|
745
1102
|
path: "/dispatch",
|
|
746
1103
|
url: workspaceAppUrl("/dispatch"),
|
|
747
1104
|
isDispatch: true,
|
|
1105
|
+
audience: DEFAULT_WORKSPACE_APP_AUDIENCE,
|
|
1106
|
+
publicPaths: [],
|
|
1107
|
+
protectedPaths: [],
|
|
748
1108
|
status: "ready",
|
|
749
1109
|
},
|
|
750
1110
|
],
|
|
@@ -1166,6 +1526,7 @@ async function remoteAppCreationAuthorization(): Promise<
|
|
|
1166
1526
|
function buildWorkspaceAppPrompt(input: {
|
|
1167
1527
|
prompt: string;
|
|
1168
1528
|
appId?: string | null;
|
|
1529
|
+
description?: string | null;
|
|
1169
1530
|
template?: string | null;
|
|
1170
1531
|
selectedKeys?: string[];
|
|
1171
1532
|
selectedResources?: WorkspaceResourceOption[];
|
|
@@ -1176,6 +1537,9 @@ function buildWorkspaceAppPrompt(input: {
|
|
|
1176
1537
|
input.prompt.replace(/\b(build|create|make|an?|the|app|tool)\b/gi, " "),
|
|
1177
1538
|
) ||
|
|
1178
1539
|
"new-app";
|
|
1540
|
+
const appDescription =
|
|
1541
|
+
input.description?.trim() ||
|
|
1542
|
+
generateWorkspaceAppDescription(input.prompt, appId);
|
|
1179
1543
|
const selectedKeys = input.selectedKeys || [];
|
|
1180
1544
|
const selectedResources = input.selectedResources || [];
|
|
1181
1545
|
const resourceList = selectedResources.length
|
|
@@ -1192,6 +1556,7 @@ function buildWorkspaceAppPrompt(input: {
|
|
|
1192
1556
|
"Create a new agent-native app in this workspace.",
|
|
1193
1557
|
"",
|
|
1194
1558
|
`App name: ${appId}`,
|
|
1559
|
+
`App description: ${appDescription}`,
|
|
1195
1560
|
`Template to start from: ${input.template || "starter"}`,
|
|
1196
1561
|
`User prompt: ${input.prompt.trim()}`,
|
|
1197
1562
|
"If the user mentions a product or company such as Granola, Loom, Superhuman, Linear, or Notion, treat it as product inspiration unless they explicitly ask to connect to that service. Do not invent or require third-party API keys like GRANOLA_API_KEY just because a product is named.",
|
|
@@ -1199,6 +1564,7 @@ function buildWorkspaceAppPrompt(input: {
|
|
|
1199
1564
|
? `Dispatch vault keys selected for this app: ${selectedKeys.join(", ")}`
|
|
1200
1565
|
: "Dispatch vault keys selected for this app: none",
|
|
1201
1566
|
`Dispatch workspace resources selected for this app:\n${resourceList}`,
|
|
1567
|
+
`Dispatch workspace resources with scope=all are global. After the app exists, sync workspace resources to appId "${appId}" so global skills, guardrail instructions, and reference resources reach the new app even when no per-app resources were selected.`,
|
|
1202
1568
|
"",
|
|
1203
1569
|
`Use the workspace app layout: create it under apps/${appId}, mount it at /${appId}, keep it on the shared workspace database/hosting model, and avoid table-name collisions by namespacing any new domain tables to the app.`,
|
|
1204
1570
|
`Important routing rule: from outside the app, link to /${appId}; inside apps/${appId}, React Router routes are app-local. Use <Link to="/review"> and navigate("/review"), not "/${appId}/review"; APP_BASE_PATH supplies the mounted prefix, and hardcoding it causes doubled URLs like /${appId}/${appId}/review.`,
|
|
@@ -1209,8 +1575,8 @@ function buildWorkspaceAppPrompt(input: {
|
|
|
1209
1575
|
? `Dispatch will create pending vault requests for the selected keys for appId "${appId}" after this app creation request is accepted. Do not grant or sync vault keys directly from the app-creation branch.`
|
|
1210
1576
|
: "Do not grant or request any Dispatch vault keys unless the user asks later.",
|
|
1211
1577
|
selectedResources.length
|
|
1212
|
-
? `Dispatch will create workspace resource grants for the selected resources for appId "${appId}". After the app exists, sync workspace resources so the app receives
|
|
1213
|
-
: "Do not grant any Dispatch workspace resources unless the user asks later.",
|
|
1578
|
+
? `Dispatch will create workspace resource grants for the selected resources for appId "${appId}". After the app exists, sync workspace resources so the app receives both global and selected shared resources.`
|
|
1579
|
+
: "Do not grant any selected-only Dispatch workspace resources unless the user asks later.",
|
|
1214
1580
|
"",
|
|
1215
1581
|
"Agent-native rules (these are the framework's contract — not optional):",
|
|
1216
1582
|
`- Persist ALL data in SQL via Drizzle. Add tables to apps/${appId}/server/db/schema.ts and migrations to apps/${appId}/server/plugins/db.ts. NEVER use localStorage, sessionStorage, IndexedDB, or in-memory state for anything the user expects to persist — agent and UI must read the same source of truth.`,
|
|
@@ -1222,11 +1588,13 @@ function buildWorkspaceAppPrompt(input: {
|
|
|
1222
1588
|
"",
|
|
1223
1589
|
"Branch readiness requirements before handing off:",
|
|
1224
1590
|
"- The CLI auto-fills package.json name and displayName from the app id; only edit the description / scripts / dependencies if the app actually needs more than the template provides.",
|
|
1591
|
+
`- Save a concise, human-readable app description in apps/${appId}/package.json "description" so Dispatch, A2A discovery, and connected agents can describe what this app does. Use the description above or improve it based on the prompt.`,
|
|
1225
1592
|
"- Do not add or update workspace-apps.json or .agent-native/workspace-apps.json unless the app needs an explicit external URL override; the root deploy generates the workspace app registry from apps/* and deploy metadata.",
|
|
1226
1593
|
"- Update pnpm-lock.yaml when adding or changing dependencies so Netlify can install the branch reliably.",
|
|
1227
1594
|
"- Update the app manifest/package/deploy metadata needed by the existing workspace deployment model; do not leave the branch relying only on uncommitted local state.",
|
|
1228
|
-
"- Verify the app's agent card/A2A metadata is ready so Dispatch can discover and delegate to the app after deployment.",
|
|
1229
|
-
"-
|
|
1595
|
+
"- Verify the app's agent card/A2A metadata is ready so Dispatch can discover and delegate to the app after deployment. Every sibling workspace app should be usable over A2A by default through call-agent.",
|
|
1596
|
+
"- Give the app agent context that sibling workspace apps are available over A2A with names and descriptions from the workspace app registry; do not hardcode a stale app list.",
|
|
1597
|
+
"- Include a final verification note covering the registry entry, manifest/deploy metadata, relative same-origin routing, and agent-card readiness.",
|
|
1230
1598
|
`When it is ready, start or update the workspace dev server and navigate the user to /${appId}.`,
|
|
1231
1599
|
].join("\n"),
|
|
1232
1600
|
};
|
|
@@ -1270,6 +1638,7 @@ async function grantSelectedWorkspaceResources(input: {
|
|
|
1270
1638
|
export async function startWorkspaceAppCreation(input: {
|
|
1271
1639
|
prompt: string;
|
|
1272
1640
|
appId?: string | null;
|
|
1641
|
+
description?: string | null;
|
|
1273
1642
|
template?: string | null;
|
|
1274
1643
|
secretIds?: string[];
|
|
1275
1644
|
resourceIds?: string[];
|
|
@@ -1277,6 +1646,7 @@ export async function startWorkspaceAppCreation(input: {
|
|
|
1277
1646
|
const initial = buildWorkspaceAppPrompt({
|
|
1278
1647
|
prompt: input.prompt,
|
|
1279
1648
|
appId: input.appId,
|
|
1649
|
+
description: input.description,
|
|
1280
1650
|
template: input.template,
|
|
1281
1651
|
});
|
|
1282
1652
|
assertValidWorkspaceAppId(initial.appId);
|
|
@@ -1304,11 +1674,15 @@ export async function startWorkspaceAppCreation(input: {
|
|
|
1304
1674
|
const built = buildWorkspaceAppPrompt({
|
|
1305
1675
|
prompt: input.prompt,
|
|
1306
1676
|
appId: input.appId,
|
|
1677
|
+
description: input.description,
|
|
1307
1678
|
template: input.template,
|
|
1308
1679
|
selectedKeys,
|
|
1309
1680
|
selectedResources,
|
|
1310
1681
|
});
|
|
1311
1682
|
const prompt = built.prompt;
|
|
1683
|
+
const appDescription =
|
|
1684
|
+
input.description?.trim() ||
|
|
1685
|
+
generateWorkspaceAppDescription(input.prompt, built.appId);
|
|
1312
1686
|
|
|
1313
1687
|
if (isLocal) {
|
|
1314
1688
|
await requestSelectedVaultKeys({
|
|
@@ -1375,6 +1749,8 @@ export async function startWorkspaceAppCreation(input: {
|
|
|
1375
1749
|
await recordPendingWorkspaceApp({
|
|
1376
1750
|
appId: built.appId,
|
|
1377
1751
|
projectId: settings.builderProjectId,
|
|
1752
|
+
description: appDescription,
|
|
1753
|
+
sourcePrompt: input.prompt,
|
|
1378
1754
|
branchName: result.branchName,
|
|
1379
1755
|
builderUrl: result.url,
|
|
1380
1756
|
});
|
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
import { resolveOrgIdForEmail } from "@agent-native/core/org";
|
|
6
6
|
import crypto from "node:crypto";
|
|
7
7
|
import { consumeLinkToken, resolveLinkedOwner } from "./dispatch-store.js";
|
|
8
|
+
import { handleRemoteCodeCommand } from "./dispatch-remote-commands.js";
|
|
8
9
|
|
|
9
10
|
type SlackSenderProfile = {
|
|
10
11
|
email: string | null;
|
|
@@ -195,11 +196,17 @@ export async function resolveDispatchOwner(
|
|
|
195
196
|
|
|
196
197
|
export async function beforeDispatchProcess(
|
|
197
198
|
incoming: IncomingMessage,
|
|
198
|
-
|
|
199
|
+
adapter: PlatformAdapter,
|
|
199
200
|
): Promise<{ handled: true; responseText?: string } | { handled: false }> {
|
|
200
201
|
const trimmed = incoming.text.trim();
|
|
201
|
-
const
|
|
202
|
-
|
|
202
|
+
const commandText =
|
|
203
|
+
contextString(incoming.platformContext.rawText) || trimmed;
|
|
204
|
+
const match = commandText.match(/^\/link(?:@\w+)?\s+([a-zA-Z0-9_-]+)$/);
|
|
205
|
+
if (!match) {
|
|
206
|
+
return handleRemoteCodeCommand(incoming, adapter, {
|
|
207
|
+
resolveOwner: () => resolveDispatchOwner(incoming),
|
|
208
|
+
});
|
|
209
|
+
}
|
|
203
210
|
|
|
204
211
|
try {
|
|
205
212
|
const owner = await consumeLinkToken({
|