@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
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
const loadWorkspaceAppsManifestMock = vi.hoisted(() => vi.fn());
|
|
4
|
+
const getBuiltinAgentsMock = vi.hoisted(() => vi.fn());
|
|
5
|
+
|
|
6
|
+
vi.mock("@agent-native/core/server/agent-discovery", () => ({
|
|
7
|
+
loadWorkspaceAppsManifest: loadWorkspaceAppsManifestMock,
|
|
8
|
+
getBuiltinAgents: getBuiltinAgentsMock,
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
import { resolveCatchAllTarget } from "./catch-all-target.js";
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
vi.clearAllMocks();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
vi.clearAllMocks();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe("resolveCatchAllTarget", () => {
|
|
22
|
+
it("prefers the workspace manifest entry when one matches", () => {
|
|
23
|
+
loadWorkspaceAppsManifestMock.mockReturnValue([
|
|
24
|
+
{ id: "todo", name: "Todo", path: "/todo" },
|
|
25
|
+
]);
|
|
26
|
+
getBuiltinAgentsMock.mockReturnValue([
|
|
27
|
+
{
|
|
28
|
+
id: "todo",
|
|
29
|
+
name: "Todo",
|
|
30
|
+
description: "",
|
|
31
|
+
url: "https://todo.example.com",
|
|
32
|
+
color: "#000",
|
|
33
|
+
},
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
expect(resolveCatchAllTarget("todo")).toBe("/todo");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("falls back to the built-in template URL when no workspace manifest exists", () => {
|
|
40
|
+
loadWorkspaceAppsManifestMock.mockReturnValue(null);
|
|
41
|
+
getBuiltinAgentsMock.mockReturnValue([
|
|
42
|
+
{
|
|
43
|
+
id: "forms",
|
|
44
|
+
name: "Forms",
|
|
45
|
+
description: "",
|
|
46
|
+
url: "http://localhost:8084",
|
|
47
|
+
color: "#06B6D4",
|
|
48
|
+
},
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
expect(resolveCatchAllTarget("forms")).toBe("http://localhost:8084");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("falls back to the built-in template URL when the workspace manifest does not include the app", () => {
|
|
55
|
+
loadWorkspaceAppsManifestMock.mockReturnValue([
|
|
56
|
+
{ id: "dispatch", name: "Dispatch", path: "/dispatch" },
|
|
57
|
+
]);
|
|
58
|
+
getBuiltinAgentsMock.mockReturnValue([
|
|
59
|
+
{
|
|
60
|
+
id: "forms",
|
|
61
|
+
name: "Forms",
|
|
62
|
+
description: "",
|
|
63
|
+
url: "http://localhost:8084",
|
|
64
|
+
color: "#06B6D4",
|
|
65
|
+
},
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
expect(resolveCatchAllTarget("forms")).toBe("http://localhost:8084");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("normalizes a manifest entry without a leading slash", () => {
|
|
72
|
+
loadWorkspaceAppsManifestMock.mockReturnValue([
|
|
73
|
+
{ id: "todo", name: "Todo", path: "todo" },
|
|
74
|
+
]);
|
|
75
|
+
getBuiltinAgentsMock.mockReturnValue([]);
|
|
76
|
+
|
|
77
|
+
expect(resolveCatchAllTarget("todo")).toBe("/todo");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("uses app.path when id !== path (not /${appId})", () => {
|
|
81
|
+
// Before the fix, an entry whose mounted path differs from its id —
|
|
82
|
+
// e.g. id: "forms", path: "my-forms" without a leading slash — was
|
|
83
|
+
// silently rewritten to `/forms` (the appId) and routed to the wrong
|
|
84
|
+
// app. The normalizer now keeps the manifest path and only prepends
|
|
85
|
+
// the missing slash.
|
|
86
|
+
loadWorkspaceAppsManifestMock.mockReturnValue([
|
|
87
|
+
{ id: "forms", name: "Forms", path: "my-forms" },
|
|
88
|
+
]);
|
|
89
|
+
getBuiltinAgentsMock.mockReturnValue([]);
|
|
90
|
+
|
|
91
|
+
expect(resolveCatchAllTarget("forms")).toBe("/my-forms");
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("prefers app.url when the manifest entry has an externally-hosted URL", () => {
|
|
95
|
+
// Workspaces can point at remote deploys. The catch-all should bounce
|
|
96
|
+
// to the absolute URL instead of mounting a local path that doesn't
|
|
97
|
+
// exist inside the gateway.
|
|
98
|
+
loadWorkspaceAppsManifestMock.mockReturnValue([
|
|
99
|
+
{
|
|
100
|
+
id: "forms",
|
|
101
|
+
name: "Forms",
|
|
102
|
+
path: "/forms",
|
|
103
|
+
url: "https://forms.example.com",
|
|
104
|
+
},
|
|
105
|
+
]);
|
|
106
|
+
getBuiltinAgentsMock.mockReturnValue([]);
|
|
107
|
+
|
|
108
|
+
expect(resolveCatchAllTarget("forms")).toBe("https://forms.example.com");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("ignores app.url that isn't an absolute http(s) URL and falls back to path", () => {
|
|
112
|
+
// Bare hostname — `new URL("forms.example.com")` throws, so the value
|
|
113
|
+
// is rejected and we fall through to the (validated) path. Without
|
|
114
|
+
// this, the catch-all would `throw redirect("forms.example.com")`
|
|
115
|
+
// and the browser would treat the value as a relative path inside the
|
|
116
|
+
// gateway, producing a broken redirect.
|
|
117
|
+
loadWorkspaceAppsManifestMock.mockReturnValue([
|
|
118
|
+
{
|
|
119
|
+
id: "forms",
|
|
120
|
+
name: "Forms",
|
|
121
|
+
path: "/forms",
|
|
122
|
+
url: "forms.example.com",
|
|
123
|
+
},
|
|
124
|
+
]);
|
|
125
|
+
getBuiltinAgentsMock.mockReturnValue([]);
|
|
126
|
+
|
|
127
|
+
expect(resolveCatchAllTarget("forms")).toBe("/forms");
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("rejects non-http(s) URL schemes (e.g. javascript:) and falls back to path", () => {
|
|
131
|
+
// Defense in depth — a hostile manifest entry can't produce a
|
|
132
|
+
// `javascript:` redirect target. Validation enforces http(s) only.
|
|
133
|
+
loadWorkspaceAppsManifestMock.mockReturnValue([
|
|
134
|
+
{
|
|
135
|
+
id: "forms",
|
|
136
|
+
name: "Forms",
|
|
137
|
+
path: "/forms",
|
|
138
|
+
url: "javascript:alert(1)",
|
|
139
|
+
},
|
|
140
|
+
]);
|
|
141
|
+
getBuiltinAgentsMock.mockReturnValue([]);
|
|
142
|
+
|
|
143
|
+
expect(resolveCatchAllTarget("forms")).toBe("/forms");
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("strips a trailing slash from app.url", () => {
|
|
147
|
+
loadWorkspaceAppsManifestMock.mockReturnValue([
|
|
148
|
+
{
|
|
149
|
+
id: "forms",
|
|
150
|
+
name: "Forms",
|
|
151
|
+
path: "/forms",
|
|
152
|
+
url: "https://forms.example.com/",
|
|
153
|
+
},
|
|
154
|
+
]);
|
|
155
|
+
getBuiltinAgentsMock.mockReturnValue([]);
|
|
156
|
+
|
|
157
|
+
expect(resolveCatchAllTarget("forms")).toBe("https://forms.example.com");
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("ignores an empty/whitespace app.url and falls back to path", () => {
|
|
161
|
+
loadWorkspaceAppsManifestMock.mockReturnValue([
|
|
162
|
+
{
|
|
163
|
+
id: "forms",
|
|
164
|
+
name: "Forms",
|
|
165
|
+
path: "/forms",
|
|
166
|
+
url: " ",
|
|
167
|
+
},
|
|
168
|
+
]);
|
|
169
|
+
getBuiltinAgentsMock.mockReturnValue([]);
|
|
170
|
+
|
|
171
|
+
expect(resolveCatchAllTarget("forms")).toBe("/forms");
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("collapses leading slashes/backslashes in app.path so `/\\evil.example` can't redirect off-origin", () => {
|
|
175
|
+
// Browsers normalize backslashes to forward slashes during URL
|
|
176
|
+
// parsing, so `throw redirect("/\\evil.example")` would resolve to
|
|
177
|
+
// `https://evil.example`. The regex covers both slash types.
|
|
178
|
+
loadWorkspaceAppsManifestMock.mockReturnValue([
|
|
179
|
+
{ id: "forms", name: "Forms", path: "/\\evil.example" },
|
|
180
|
+
]);
|
|
181
|
+
getBuiltinAgentsMock.mockReturnValue([]);
|
|
182
|
+
|
|
183
|
+
expect(resolveCatchAllTarget("forms")).toBe("/evil.example");
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("collapses leading double slashes in app.path so `//evil.example` can't redirect off-origin", () => {
|
|
187
|
+
// The manifest parser only checks `startsWith("/")`, so a path of
|
|
188
|
+
// `//evil.example` slips through. Browsers treat that as a network-
|
|
189
|
+
// path reference and `throw redirect("//evil.example")` would redirect
|
|
190
|
+
// to `https://evil.example` — the same phishing vector the `app.url`
|
|
191
|
+
// validator closes. Collapse the leading slashes so the redirect
|
|
192
|
+
// stays on the gateway.
|
|
193
|
+
loadWorkspaceAppsManifestMock.mockReturnValue([
|
|
194
|
+
{ id: "forms", name: "Forms", path: "//evil.example" },
|
|
195
|
+
]);
|
|
196
|
+
getBuiltinAgentsMock.mockReturnValue([]);
|
|
197
|
+
|
|
198
|
+
expect(resolveCatchAllTarget("forms")).toBe("/evil.example");
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("falls back to /${appId} when the manifest entry has neither path nor url", () => {
|
|
202
|
+
loadWorkspaceAppsManifestMock.mockReturnValue([
|
|
203
|
+
{ id: "forms", name: "Forms", path: "" },
|
|
204
|
+
]);
|
|
205
|
+
getBuiltinAgentsMock.mockReturnValue([]);
|
|
206
|
+
|
|
207
|
+
expect(resolveCatchAllTarget("forms")).toBe("/forms");
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("returns null when nothing matches", () => {
|
|
211
|
+
loadWorkspaceAppsManifestMock.mockReturnValue([
|
|
212
|
+
{ id: "dispatch", name: "Dispatch", path: "/dispatch" },
|
|
213
|
+
]);
|
|
214
|
+
getBuiltinAgentsMock.mockReturnValue([]);
|
|
215
|
+
|
|
216
|
+
expect(resolveCatchAllTarget("unknown-app")).toBeNull();
|
|
217
|
+
});
|
|
218
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getBuiltinAgents,
|
|
3
|
+
loadWorkspaceAppsManifest,
|
|
4
|
+
} from "@agent-native/core/server/agent-discovery";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Resolve where `/dispatch/<appId>` should bounce to when it doesn't match
|
|
8
|
+
* an explicit dispatch route. Used by the `$appId` catch-all route loader.
|
|
9
|
+
*
|
|
10
|
+
* Resolution order:
|
|
11
|
+
*
|
|
12
|
+
* 1. Workspace apps manifest (env, .agent-native/workspace-apps.json, or a
|
|
13
|
+
* filesystem scan of `apps/`).
|
|
14
|
+
* - `app.url` (absolute URL — externally hosted workspace app) wins if
|
|
15
|
+
* present.
|
|
16
|
+
* - Otherwise the `app.path` mounted under the workspace gateway is
|
|
17
|
+
* used. Path is normalized to a leading slash if missing
|
|
18
|
+
* (e.g. manifest entry `path: "my-forms"` → `/my-forms`), so an app
|
|
19
|
+
* whose mounted path differs from its id ends up at the right place
|
|
20
|
+
* instead of being silently rewritten to `/${appId}`.
|
|
21
|
+
* - Bare entry with no path / url falls back to `/${appId}`.
|
|
22
|
+
* 2. First-party template registry. When no workspace manifest matches
|
|
23
|
+
* (framework dev with each template on its own port, hosted dispatch
|
|
24
|
+
* with no sibling apps), return the matching template's deploy URL —
|
|
25
|
+
* dev URL in development (e.g. http://localhost:8084 for forms), prod
|
|
26
|
+
* URL in production (e.g. https://forms.agent-native.com).
|
|
27
|
+
*
|
|
28
|
+
* Returns `null` if neither lookup matches, letting the route render its
|
|
29
|
+
* "Page not found" pane.
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* Validate `app.url` is an absolute http(s) URL before we trust it as a
|
|
33
|
+
* redirect target. A bare hostname (`"forms.example.com"`) or a
|
|
34
|
+
* `javascript:` scheme would otherwise get returned verbatim from
|
|
35
|
+
* `resolveCatchAllTarget` and produce a broken redirect (or a phishing
|
|
36
|
+
* vector). Mirrors `normalizeWorkspaceAppUrl` in
|
|
37
|
+
* `packages/core/src/deploy/workspace-deploy.ts` — but inlined to avoid
|
|
38
|
+
* pulling the deploy CLI module into a runtime path.
|
|
39
|
+
*/
|
|
40
|
+
function validatedAbsoluteUrl(value: unknown): string | undefined {
|
|
41
|
+
if (typeof value !== "string" || !value.trim()) return undefined;
|
|
42
|
+
try {
|
|
43
|
+
const parsed = new URL(value.trim());
|
|
44
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
return parsed.toString().replace(/\/$/, "");
|
|
48
|
+
} catch {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function resolveCatchAllTarget(appId: string): string | null {
|
|
54
|
+
const apps = loadWorkspaceAppsManifest();
|
|
55
|
+
if (apps) {
|
|
56
|
+
const app = apps.find((entry) => entry?.id === appId);
|
|
57
|
+
if (app) {
|
|
58
|
+
// Explicit externally-hosted URL wins. Workspaces that point at a
|
|
59
|
+
// remote deploy (e.g. a sibling app on Netlify) set `url` and we
|
|
60
|
+
// should bounce the user there rather than mounting a local path
|
|
61
|
+
// that doesn't exist inside the gateway. Validate the URL first —
|
|
62
|
+
// a bare hostname or non-http(s) scheme would produce a broken
|
|
63
|
+
// redirect (and a `javascript:` value would be a phishing vector).
|
|
64
|
+
const url = validatedAbsoluteUrl(app.url);
|
|
65
|
+
if (url) {
|
|
66
|
+
return url;
|
|
67
|
+
}
|
|
68
|
+
// Fall back to the mounted path. Normalize to leading slash so an
|
|
69
|
+
// entry whose path differs from its id (e.g. `id: "forms"`,
|
|
70
|
+
// `path: "my-forms"`) still lands on the correct gateway mount —
|
|
71
|
+
// not on `/${appId}`, which would silently route to the wrong app.
|
|
72
|
+
//
|
|
73
|
+
// Reject scheme-relative paths. Three variants reach this point —
|
|
74
|
+
// all of them get collapsed to a single leading slash so the
|
|
75
|
+
// redirect stays on the gateway:
|
|
76
|
+
//
|
|
77
|
+
// `//evil.example` — network-path reference, browser treats as
|
|
78
|
+
// absolute (https://evil.example).
|
|
79
|
+
// `/\evil.example` — browsers normalize backslashes to forward
|
|
80
|
+
// slashes during URL parsing, same result.
|
|
81
|
+
// `\/evil.example` — same idea, leading-backslash variant.
|
|
82
|
+
//
|
|
83
|
+
// The manifest parser only checks `startsWith("/")` for the first
|
|
84
|
+
// case, and even that allows `//evil…`. Defend in depth here by
|
|
85
|
+
// collapsing any run of leading slashes-or-backslashes to one
|
|
86
|
+
// forward slash. Same phishing vector that `validatedAbsoluteUrl`
|
|
87
|
+
// closes for `app.url`.
|
|
88
|
+
if (typeof app.path === "string" && app.path.trim()) {
|
|
89
|
+
const normalized = app.path.trim().replace(/^[/\\]+/, "/");
|
|
90
|
+
return normalized.startsWith("/") ? normalized : `/${normalized}`;
|
|
91
|
+
}
|
|
92
|
+
return `/${appId}`;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const builtin = getBuiltinAgents("dispatch").find(
|
|
96
|
+
(agent) => agent.id === appId,
|
|
97
|
+
);
|
|
98
|
+
return builtin?.url ?? null;
|
|
99
|
+
}
|
package/src/lib/utils.ts
CHANGED
|
@@ -5,10 +5,19 @@ export interface WorkspaceAppSummary {
|
|
|
5
5
|
path: string;
|
|
6
6
|
url?: string | null;
|
|
7
7
|
isDispatch?: boolean;
|
|
8
|
+
audience?: "internal" | "public";
|
|
9
|
+
publicPaths?: string[];
|
|
10
|
+
protectedPaths?: string[];
|
|
8
11
|
status?: "ready" | "pending";
|
|
9
12
|
statusLabel?: string;
|
|
10
13
|
builderUrl?: string | null;
|
|
11
14
|
branchName?: string | null;
|
|
15
|
+
createdAt?: string | null;
|
|
16
|
+
agentCardUrl?: string | null;
|
|
17
|
+
agentCardReachable?: boolean;
|
|
18
|
+
a2aEndpointUrl?: string | null;
|
|
19
|
+
agentName?: string | null;
|
|
20
|
+
agentSkillsCount?: number | null;
|
|
12
21
|
archived?: boolean;
|
|
13
22
|
}
|
|
14
23
|
|
package/src/routes/index.ts
CHANGED
|
@@ -46,6 +46,7 @@ export const dispatchRoutes: RouteConfig = [
|
|
|
46
46
|
route("approval", "./pages/approval.js"),
|
|
47
47
|
route("approvals", "./pages/approvals.js"),
|
|
48
48
|
route("audit", "./pages/audit.js"),
|
|
49
|
+
route("dreams", "./pages/dreams.js"),
|
|
49
50
|
route("thread-debug", "./pages/thread-debug.js"),
|
|
50
51
|
route("team", "./pages/team.js"),
|
|
51
52
|
route("extensions", "./pages/extensions._index.js"),
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
Navigate,
|
|
5
5
|
redirect,
|
|
6
6
|
useParams,
|
|
7
|
+
type ClientLoaderFunctionArgs,
|
|
7
8
|
type LoaderFunctionArgs,
|
|
8
9
|
} from "react-router";
|
|
9
10
|
import { useActionQuery, appPath } from "@agent-native/core/client";
|
|
10
|
-
import { loadWorkspaceAppsManifest } from "@agent-native/core/server/agent-discovery";
|
|
11
11
|
import {
|
|
12
12
|
IconArrowLeft,
|
|
13
13
|
IconArrowUpRight,
|
|
@@ -17,6 +17,7 @@ import { DispatchShell } from "@/components/dispatch-shell";
|
|
|
17
17
|
import { Spinner } from "@/components/ui/spinner";
|
|
18
18
|
import { Badge } from "@/components/ui/badge";
|
|
19
19
|
import { Button } from "@/components/ui/button";
|
|
20
|
+
import { resolveCatchAllTarget } from "@/lib/catch-all-target";
|
|
20
21
|
import {
|
|
21
22
|
workspaceAppHref,
|
|
22
23
|
type WorkspaceAppSummary,
|
|
@@ -49,6 +50,14 @@ export function meta() {
|
|
|
49
50
|
* and looks broken. This route fixes both the post-creation navigation
|
|
50
51
|
* and the OAuth round-trip from a single place.
|
|
51
52
|
*
|
|
53
|
+
* Built-in template fallback: when no workspace manifest is available
|
|
54
|
+
* (framework dev with each template on its own port, hosted dispatch with
|
|
55
|
+
* no sibling apps), redirect to the matching first-party template's deploy
|
|
56
|
+
* URL — `http://localhost:<devPort>` in dev, `https://<id>.agent-native.com`
|
|
57
|
+
* in production. Without this, a user visiting `/forms` on dispatch is
|
|
58
|
+
* forced to sign in (auth guard) and then lands on this route's "Page not
|
|
59
|
+
* found" pane after the post-login reload.
|
|
60
|
+
*
|
|
52
61
|
* `appId === "dispatch"` short-circuit: when the segment matches Dispatch
|
|
53
62
|
* itself (e.g. `/dispatch/dispatch`), we go straight to the overview rather
|
|
54
63
|
* than chaining through `/dispatch` (which polled `useActionQuery` re-fired
|
|
@@ -64,19 +73,23 @@ export function loader({ params }: LoaderFunctionArgs) {
|
|
|
64
73
|
if (!appId) return null;
|
|
65
74
|
const selfTarget = dispatchSelfRedirect(appId);
|
|
66
75
|
if (selfTarget) throw redirect(selfTarget);
|
|
67
|
-
const
|
|
68
|
-
if (!apps) return null;
|
|
69
|
-
const app = apps.find((entry) => entry?.id === appId);
|
|
70
|
-
const target =
|
|
71
|
-
app?.path && app.path.startsWith("/") ? app.path : app ? `/${appId}` : null;
|
|
76
|
+
const target = resolveCatchAllTarget(appId);
|
|
72
77
|
if (target) throw redirect(target);
|
|
73
78
|
return null;
|
|
74
79
|
}
|
|
75
80
|
|
|
76
|
-
export function clientLoader({
|
|
81
|
+
export async function clientLoader({
|
|
82
|
+
params,
|
|
83
|
+
serverLoader,
|
|
84
|
+
}: ClientLoaderFunctionArgs) {
|
|
77
85
|
const selfTarget = dispatchSelfRedirect(params.appId);
|
|
78
86
|
if (selfTarget) throw redirect(selfTarget);
|
|
79
|
-
|
|
87
|
+
// Defer to the server loader so the built-in template fallback runs on
|
|
88
|
+
// SPA navigations too (e.g. clicking a `/<template-id>` link inside
|
|
89
|
+
// dispatch). Without this the client side would only check the workspace
|
|
90
|
+
// apps query, which never lists the static first-party templates and so
|
|
91
|
+
// the user would land on the "Page not found" pane.
|
|
92
|
+
return serverLoader();
|
|
80
93
|
}
|
|
81
94
|
|
|
82
95
|
export default function WorkspaceAppCatchAllRoute() {
|
|
@@ -7,6 +7,10 @@ import {
|
|
|
7
7
|
appPath,
|
|
8
8
|
} from "@agent-native/core/client";
|
|
9
9
|
import { toast } from "sonner";
|
|
10
|
+
import {
|
|
11
|
+
ApprovalValueBlock,
|
|
12
|
+
parseApprovalValue,
|
|
13
|
+
} from "@/components/approval-value-block";
|
|
10
14
|
import { Button } from "@/components/ui/button";
|
|
11
15
|
import { Badge } from "@/components/ui/badge";
|
|
12
16
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
@@ -168,10 +172,12 @@ export default function ApprovalPreviewRoute() {
|
|
|
168
172
|
}
|
|
169
173
|
|
|
170
174
|
const isPending = approval.status === "pending";
|
|
175
|
+
const beforeValue = parseApprovalValue(approval.beforeValue);
|
|
176
|
+
const afterValue = parseApprovalValue(approval.afterValue);
|
|
171
177
|
|
|
172
178
|
return (
|
|
173
179
|
<div className="flex min-h-screen items-start justify-center bg-background p-6">
|
|
174
|
-
<div className="w-full max-w-
|
|
180
|
+
<div className="w-full max-w-2xl space-y-4">
|
|
175
181
|
<div className="rounded-2xl border bg-card p-5">
|
|
176
182
|
<div className="flex items-start gap-3">
|
|
177
183
|
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl border bg-muted text-foreground">
|
|
@@ -224,6 +230,13 @@ export default function ApprovalPreviewRoute() {
|
|
|
224
230
|
)}
|
|
225
231
|
</div>
|
|
226
232
|
|
|
233
|
+
{(beforeValue !== null || afterValue !== null) && (
|
|
234
|
+
<div className="mt-4 grid gap-3 md:grid-cols-2">
|
|
235
|
+
<ApprovalValueBlock label="Before" value={beforeValue} />
|
|
236
|
+
<ApprovalValueBlock label="After" value={afterValue} />
|
|
237
|
+
</div>
|
|
238
|
+
)}
|
|
239
|
+
|
|
227
240
|
{isPending && (
|
|
228
241
|
<div className="mt-4 flex gap-2">
|
|
229
242
|
<Button
|
|
@@ -58,7 +58,7 @@ export default function ApprovalsRoute() {
|
|
|
58
58
|
</div>
|
|
59
59
|
<div className="mt-1 text-xs text-muted-foreground">
|
|
60
60
|
{hasOrg
|
|
61
|
-
? "Applies to saved destinations and dispatch settings
|
|
61
|
+
? "Applies to saved destinations, shared dream proposals, All-app workspace resources, and dispatch settings."
|
|
62
62
|
: "Requires a team workspace. Set one up on the Team page."}
|
|
63
63
|
</div>
|
|
64
64
|
</div>
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
dreamSettingsToDraft,
|
|
4
|
+
dreamSettingsUpdateFromDraft,
|
|
5
|
+
splitSourceIds,
|
|
6
|
+
type DreamSettingsDraft,
|
|
7
|
+
} from "./dream-settings.js";
|
|
8
|
+
|
|
9
|
+
function draft(
|
|
10
|
+
overrides: Partial<DreamSettingsDraft> = {},
|
|
11
|
+
): DreamSettingsDraft {
|
|
12
|
+
return {
|
|
13
|
+
enabled: true,
|
|
14
|
+
schedule: "0 9 * * 1",
|
|
15
|
+
sourceId: "all",
|
|
16
|
+
sourceIdsText: "",
|
|
17
|
+
allSources: true,
|
|
18
|
+
query: "",
|
|
19
|
+
limit: "8",
|
|
20
|
+
sourceTimeoutMs: "30000",
|
|
21
|
+
sourceConcurrency: "2",
|
|
22
|
+
sourceStartStaggerMs: "250",
|
|
23
|
+
threadConcurrency: "3",
|
|
24
|
+
threadTimeoutMs: "8000",
|
|
25
|
+
minCandidateCount: "1",
|
|
26
|
+
...overrides,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe("dream settings payload helpers", () => {
|
|
31
|
+
it("normalizes persisted settings into editable draft text", () => {
|
|
32
|
+
expect(
|
|
33
|
+
dreamSettingsToDraft({
|
|
34
|
+
enabled: true,
|
|
35
|
+
schedule: "0 8 * * 2",
|
|
36
|
+
sourceId: "selected",
|
|
37
|
+
sourceIds: ["mail", "calendar"],
|
|
38
|
+
allSources: false,
|
|
39
|
+
query: "memory",
|
|
40
|
+
limit: 12,
|
|
41
|
+
sourceTimeoutMs: 45000,
|
|
42
|
+
sourceConcurrency: 4,
|
|
43
|
+
sourceStartStaggerMs: 500,
|
|
44
|
+
threadConcurrency: 5,
|
|
45
|
+
threadTimeoutMs: 9000,
|
|
46
|
+
minCandidateCount: 2,
|
|
47
|
+
}),
|
|
48
|
+
).toMatchObject({
|
|
49
|
+
enabled: true,
|
|
50
|
+
schedule: "0 8 * * 2",
|
|
51
|
+
sourceId: "selected",
|
|
52
|
+
sourceIdsText: "mail\ncalendar",
|
|
53
|
+
allSources: false,
|
|
54
|
+
query: "memory",
|
|
55
|
+
limit: "12",
|
|
56
|
+
sourceTimeoutMs: "45000",
|
|
57
|
+
sourceConcurrency: "4",
|
|
58
|
+
sourceStartStaggerMs: "500",
|
|
59
|
+
threadConcurrency: "5",
|
|
60
|
+
threadTimeoutMs: "9000",
|
|
61
|
+
minCandidateCount: "2",
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("splits explicit source ids from commas and newlines", () => {
|
|
66
|
+
expect(splitSourceIds(" mail\ncalendar, analytics ,, \nbrain ")).toEqual([
|
|
67
|
+
"mail",
|
|
68
|
+
"calendar",
|
|
69
|
+
"analytics",
|
|
70
|
+
"brain",
|
|
71
|
+
]);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("builds a save payload that clears source ids and query", () => {
|
|
75
|
+
expect(
|
|
76
|
+
dreamSettingsUpdateFromDraft(
|
|
77
|
+
draft({
|
|
78
|
+
enabled: false,
|
|
79
|
+
schedule: " 0 10 * * 3 ",
|
|
80
|
+
sourceId: " current ",
|
|
81
|
+
sourceIdsText: "",
|
|
82
|
+
allSources: false,
|
|
83
|
+
query: " ",
|
|
84
|
+
minCandidateCount: "0",
|
|
85
|
+
}),
|
|
86
|
+
),
|
|
87
|
+
).toMatchObject({
|
|
88
|
+
enabled: false,
|
|
89
|
+
schedule: "0 10 * * 3",
|
|
90
|
+
sourceId: "current",
|
|
91
|
+
sourceIds: [],
|
|
92
|
+
allSources: false,
|
|
93
|
+
query: "",
|
|
94
|
+
limit: 8,
|
|
95
|
+
sourceTimeoutMs: 30000,
|
|
96
|
+
sourceConcurrency: 2,
|
|
97
|
+
sourceStartStaggerMs: 250,
|
|
98
|
+
threadConcurrency: 3,
|
|
99
|
+
threadTimeoutMs: 8000,
|
|
100
|
+
minCandidateCount: 0,
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("falls back to all source scope and omits invalid numeric edits", () => {
|
|
105
|
+
expect(
|
|
106
|
+
dreamSettingsUpdateFromDraft(
|
|
107
|
+
draft({
|
|
108
|
+
sourceId: " ",
|
|
109
|
+
sourceIdsText: "mail\ncalendar",
|
|
110
|
+
limit: "not-a-number",
|
|
111
|
+
threadTimeoutMs: "",
|
|
112
|
+
}),
|
|
113
|
+
),
|
|
114
|
+
).toMatchObject({
|
|
115
|
+
sourceId: "all",
|
|
116
|
+
sourceIds: ["mail", "calendar"],
|
|
117
|
+
allSources: true,
|
|
118
|
+
});
|
|
119
|
+
expect(
|
|
120
|
+
dreamSettingsUpdateFromDraft(
|
|
121
|
+
draft({ limit: "not-a-number", threadTimeoutMs: "" }),
|
|
122
|
+
),
|
|
123
|
+
).not.toHaveProperty("limit");
|
|
124
|
+
expect(
|
|
125
|
+
dreamSettingsUpdateFromDraft(
|
|
126
|
+
draft({ limit: "not-a-number", threadTimeoutMs: "" }),
|
|
127
|
+
),
|
|
128
|
+
).not.toHaveProperty("threadTimeoutMs");
|
|
129
|
+
});
|
|
130
|
+
});
|