@agent-native/dispatch 0.7.0 → 0.8.1
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 +56 -3
- 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-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-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/index.d.ts.map +1 -1
- package/dist/actions/index.js +30 -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-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/start-workspace-app-creation.js +1 -1
- package/dist/actions/start-workspace-app-creation.js.map +1 -1
- 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 +73 -2
- package/dist/actions/view-screen.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 +6 -5
- package/dist/components/create-app-popover.js.map +1 -1
- package/dist/components/layout/Layout.d.ts.map +1 -1
- package/dist/components/layout/Layout.js +8 -1
- package/dist/components/layout/Layout.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 +25 -4
- 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 +23 -3
- package/dist/hooks/use-navigation-state.js.map +1 -1
- 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/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/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/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.map +1 -1
- package/dist/server/lib/app-creation-store.js +3 -2
- 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/thread-debug-store.d.ts +2 -2
- package/dist/server/lib/vault-store.d.ts +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.js +1 -1
- package/dist/server/plugins/agent-chat.js.map +1 -1
- package/dist/server/plugins/integrations.js +2 -2
- package/dist/server/plugins/integrations.js.map +1 -1
- package/package.json +4 -2
- package/src/actions/apply-dream-proposal.ts +12 -0
- package/src/actions/create-dream-report.ts +76 -0
- 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-workspace-resource-effective-context.ts +34 -0
- package/src/actions/index.spec.ts +26 -0
- package/src/actions/index.ts +31 -4
- package/src/actions/list-dream-candidates.ts +77 -0
- package/src/actions/list-dreams.ts +17 -0
- 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/start-workspace-app-creation.ts +1 -1
- package/src/actions/update-workspace-resource.ts +1 -1
- package/src/actions/view-screen.ts +90 -2
- 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 +6 -5
- package/src/components/layout/Layout.tsx +8 -0
- package/src/components/workspace-app-card.tsx +166 -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.spec.ts +79 -0
- package/src/db/migrations.ts +59 -0
- package/src/db/schema.ts +46 -2
- package/src/hooks/use-navigation-state.ts +24 -5
- package/src/lib/utils.ts +6 -1
- package/src/routes/index.ts +1 -0
- 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/workspace.tsx +577 -97
- package/src/server/lib/app-creation-store.ts +3 -2
- 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/workspace-resource-approval-lifecycle.spec.ts +226 -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 +1 -1
- 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,141 @@
|
|
|
1
|
+
import { useActionQuery } from "@agent-native/core/client";
|
|
2
|
+
import { Badge } from "@/components/ui/badge";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
export function appAvailabilityLabel(value?: string) {
|
|
6
|
+
switch (value) {
|
|
7
|
+
case "all-apps":
|
|
8
|
+
return "Inherited by all apps";
|
|
9
|
+
case "selected-granted":
|
|
10
|
+
return "Granted to this app";
|
|
11
|
+
case "selected-not-granted":
|
|
12
|
+
return "Not granted";
|
|
13
|
+
case "selected-no-app":
|
|
14
|
+
return "Select app";
|
|
15
|
+
case "path-not-managed":
|
|
16
|
+
return "Not managed";
|
|
17
|
+
default:
|
|
18
|
+
return "Checking";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function appLayerState(layer: any): {
|
|
23
|
+
label: string;
|
|
24
|
+
className: string;
|
|
25
|
+
} {
|
|
26
|
+
if (layer.effective) {
|
|
27
|
+
return {
|
|
28
|
+
label: "Wins",
|
|
29
|
+
className: "border-green-500/30 bg-green-500/10 text-green-700",
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (layer.overridden) {
|
|
33
|
+
return {
|
|
34
|
+
label: "Overridden",
|
|
35
|
+
className: "border-amber-500/30 bg-amber-500/10 text-amber-700",
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
label: "Missing",
|
|
40
|
+
className: "text-muted-foreground",
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function formatResourceTimestamp(value?: number | null): string {
|
|
45
|
+
if (!value) return "not present";
|
|
46
|
+
return new Date(value).toLocaleString();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function AppResourceEffectiveStack({
|
|
50
|
+
appId,
|
|
51
|
+
resource,
|
|
52
|
+
}: {
|
|
53
|
+
appId: string;
|
|
54
|
+
resource: any;
|
|
55
|
+
}) {
|
|
56
|
+
const { data: context, isLoading } = useActionQuery(
|
|
57
|
+
"get-workspace-resource-effective-context",
|
|
58
|
+
{ resourceId: resource.id, appId },
|
|
59
|
+
{ enabled: !!resource.id },
|
|
60
|
+
);
|
|
61
|
+
const layers = ((context as any)?.layers ?? []) as any[];
|
|
62
|
+
const active = (context as any)?.effectiveResource;
|
|
63
|
+
const availability = (context as any)?.availability;
|
|
64
|
+
|
|
65
|
+
if (isLoading && !context) {
|
|
66
|
+
return (
|
|
67
|
+
<div className="mt-3 rounded-lg border bg-muted/20 p-3">
|
|
68
|
+
<div className="h-3 w-44 animate-pulse rounded bg-muted-foreground/10" />
|
|
69
|
+
<div className="mt-3 grid gap-2 sm:grid-cols-3">
|
|
70
|
+
<div className="h-20 animate-pulse rounded-md bg-muted-foreground/10" />
|
|
71
|
+
<div className="h-20 animate-pulse rounded-md bg-muted-foreground/10" />
|
|
72
|
+
<div className="h-20 animate-pulse rounded-md bg-muted-foreground/10" />
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<div className="mt-3 rounded-lg border bg-muted/20 p-3">
|
|
80
|
+
<div className="flex flex-wrap items-start justify-between gap-2">
|
|
81
|
+
<div className="min-w-0">
|
|
82
|
+
<div className="text-xs font-semibold uppercase text-muted-foreground">
|
|
83
|
+
Effective context stack
|
|
84
|
+
</div>
|
|
85
|
+
<div className="mt-1 truncate font-mono text-[11px] text-muted-foreground">
|
|
86
|
+
{resource.path}
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
<Badge variant="outline">{appAvailabilityLabel(availability)}</Badge>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<div className="mt-3 grid gap-2 sm:grid-cols-3">
|
|
93
|
+
{layers.map((layer) => {
|
|
94
|
+
const state = appLayerState(layer);
|
|
95
|
+
return (
|
|
96
|
+
<div
|
|
97
|
+
key={layer.scope}
|
|
98
|
+
className={cn("rounded-md border bg-background/70 p-2", {
|
|
99
|
+
"border-green-500/30 bg-green-500/10": layer.effective,
|
|
100
|
+
})}
|
|
101
|
+
>
|
|
102
|
+
<div className="flex items-start justify-between gap-2">
|
|
103
|
+
<span className="text-xs font-medium text-foreground">
|
|
104
|
+
{layer.label}
|
|
105
|
+
</span>
|
|
106
|
+
<Badge variant="outline" className={state.className}>
|
|
107
|
+
{state.label}
|
|
108
|
+
</Badge>
|
|
109
|
+
</div>
|
|
110
|
+
<div className="mt-1 truncate font-mono text-[10px] text-muted-foreground">
|
|
111
|
+
{layer.owner}
|
|
112
|
+
</div>
|
|
113
|
+
{layer.resource ? (
|
|
114
|
+
<div className="mt-2 text-[11px] text-muted-foreground">
|
|
115
|
+
Updated {formatResourceTimestamp(layer.resource.updatedAt)}
|
|
116
|
+
</div>
|
|
117
|
+
) : (
|
|
118
|
+
<div className="mt-2 text-[11px] text-muted-foreground">
|
|
119
|
+
No file at this layer
|
|
120
|
+
</div>
|
|
121
|
+
)}
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
})}
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
<div className="mt-3 rounded-md bg-background/70 px-3 py-2 text-xs text-muted-foreground">
|
|
128
|
+
{active ? (
|
|
129
|
+
<>
|
|
130
|
+
Winning layer:{" "}
|
|
131
|
+
<span className="font-mono text-foreground">
|
|
132
|
+
{active.owner}/{active.path}
|
|
133
|
+
</span>
|
|
134
|
+
</>
|
|
135
|
+
) : (
|
|
136
|
+
"No active resource exists for this path yet."
|
|
137
|
+
)}
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
import React, { act } from "react";
|
|
3
|
+
import { createRoot, type Root } from "react-dom/client";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import {
|
|
6
|
+
ImpactPreview,
|
|
7
|
+
workspaceResourceMutationMessage,
|
|
8
|
+
} from "./workspace-resource-impact-preview";
|
|
9
|
+
|
|
10
|
+
const queryState = vi.hoisted(() => ({
|
|
11
|
+
result: { data: null as any, isLoading: false },
|
|
12
|
+
calls: [] as any[],
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
vi.mock("@agent-native/core/client", () => ({
|
|
16
|
+
useActionQuery: (...args: any[]) => {
|
|
17
|
+
queryState.calls.push(args);
|
|
18
|
+
return queryState.result;
|
|
19
|
+
},
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
describe("ImpactPreview", () => {
|
|
23
|
+
let container: HTMLDivElement;
|
|
24
|
+
let root: Root;
|
|
25
|
+
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
|
|
28
|
+
queryState.calls = [];
|
|
29
|
+
queryState.result = { data: null, isLoading: false };
|
|
30
|
+
container = document.createElement("div");
|
|
31
|
+
document.body.appendChild(container);
|
|
32
|
+
root = createRoot(container);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
act(() => root.unmount());
|
|
37
|
+
container.remove();
|
|
38
|
+
vi.unstubAllGlobals();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("renders All-app approval impact and override details", () => {
|
|
42
|
+
queryState.result = {
|
|
43
|
+
isLoading: false,
|
|
44
|
+
data: {
|
|
45
|
+
affectsAllApps: true,
|
|
46
|
+
affectedApps: { count: 4 },
|
|
47
|
+
approval: { willRequestApproval: true },
|
|
48
|
+
overrides: {
|
|
49
|
+
count: 2,
|
|
50
|
+
items: [
|
|
51
|
+
{
|
|
52
|
+
scope: "shared",
|
|
53
|
+
owner: "__shared__",
|
|
54
|
+
label: "Organization/app override",
|
|
55
|
+
updatedAt: 1,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
scope: "personal",
|
|
59
|
+
owner: "person@example.test",
|
|
60
|
+
label: "Personal override (person@example.test)",
|
|
61
|
+
updatedAt: 2,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
act(() => {
|
|
69
|
+
root.render(
|
|
70
|
+
<ImpactPreview
|
|
71
|
+
operation="update"
|
|
72
|
+
resourceId="resource_1"
|
|
73
|
+
scope="all"
|
|
74
|
+
/>,
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
expect(queryState.calls[0]).toEqual([
|
|
79
|
+
"preview-workspace-resource-change",
|
|
80
|
+
{
|
|
81
|
+
operation: "update",
|
|
82
|
+
resourceId: "resource_1",
|
|
83
|
+
path: undefined,
|
|
84
|
+
scope: "all",
|
|
85
|
+
},
|
|
86
|
+
{ enabled: true },
|
|
87
|
+
]);
|
|
88
|
+
expect(container.textContent).toContain("All apps impact");
|
|
89
|
+
expect(container.textContent).toContain("Approval required");
|
|
90
|
+
expect(container.textContent).toContain("2 overrides");
|
|
91
|
+
expect(container.textContent).toContain(
|
|
92
|
+
"This change applies to every workspace app (4 discovered).",
|
|
93
|
+
);
|
|
94
|
+
expect(container.textContent).toContain(
|
|
95
|
+
"It will be queued for approval before it takes effect.",
|
|
96
|
+
);
|
|
97
|
+
expect(container.textContent).toContain("Organization/app override");
|
|
98
|
+
expect(container.textContent).toContain(
|
|
99
|
+
"Personal override (person@example.test)",
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("renders selected-only changes as immediate", () => {
|
|
104
|
+
queryState.result = {
|
|
105
|
+
isLoading: false,
|
|
106
|
+
data: {
|
|
107
|
+
affectsAllApps: false,
|
|
108
|
+
affectedApps: { count: null },
|
|
109
|
+
approval: { willRequestApproval: false },
|
|
110
|
+
overrides: { count: 0, items: [] },
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
act(() => {
|
|
115
|
+
root.render(
|
|
116
|
+
<ImpactPreview
|
|
117
|
+
operation="create"
|
|
118
|
+
path="context/private-launch.md"
|
|
119
|
+
scope="selected"
|
|
120
|
+
/>,
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
expect(container.textContent).toContain("Selected only");
|
|
125
|
+
expect(container.textContent).toContain(
|
|
126
|
+
"This change only applies to explicitly granted apps.",
|
|
127
|
+
);
|
|
128
|
+
expect(container.textContent).toContain(
|
|
129
|
+
"It will take effect immediately when saved.",
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("uses approval-request mutation copy only for workspace resource approvals", () => {
|
|
134
|
+
expect(
|
|
135
|
+
workspaceResourceMutationMessage(
|
|
136
|
+
{ status: "pending", changeType: "workspace-resource.update" },
|
|
137
|
+
"Resource updated",
|
|
138
|
+
),
|
|
139
|
+
).toBe("Approval requested");
|
|
140
|
+
expect(
|
|
141
|
+
workspaceResourceMutationMessage(
|
|
142
|
+
{ status: "pending", changeType: "dream-proposal.apply" },
|
|
143
|
+
"Resource updated",
|
|
144
|
+
),
|
|
145
|
+
).toBe("Resource updated");
|
|
146
|
+
});
|
|
147
|
+
});
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { useActionQuery } from "@agent-native/core/client";
|
|
2
|
+
import { Badge } from "@/components/ui/badge";
|
|
3
|
+
import { Skeleton } from "@/components/ui/skeleton";
|
|
4
|
+
import { formatResourceTimestamp } from "./workspace-resource-effective-stack";
|
|
5
|
+
|
|
6
|
+
function isApprovalRequest(result: any): boolean {
|
|
7
|
+
return (
|
|
8
|
+
result?.status === "pending" &&
|
|
9
|
+
typeof result?.changeType === "string" &&
|
|
10
|
+
result.changeType.startsWith("workspace-resource.")
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function workspaceResourceMutationMessage(
|
|
15
|
+
result: any,
|
|
16
|
+
fallback: string,
|
|
17
|
+
): string {
|
|
18
|
+
return isApprovalRequest(result) ? "Approval requested" : fallback;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function ImpactPreview({
|
|
22
|
+
operation,
|
|
23
|
+
resourceId,
|
|
24
|
+
path,
|
|
25
|
+
scope,
|
|
26
|
+
enabled = true,
|
|
27
|
+
}: {
|
|
28
|
+
operation: "create" | "update" | "delete";
|
|
29
|
+
resourceId?: string;
|
|
30
|
+
path?: string;
|
|
31
|
+
scope?: "all" | "selected";
|
|
32
|
+
enabled?: boolean;
|
|
33
|
+
}) {
|
|
34
|
+
const { data: impact, isLoading } = useActionQuery(
|
|
35
|
+
"preview-workspace-resource-change",
|
|
36
|
+
{
|
|
37
|
+
operation,
|
|
38
|
+
resourceId,
|
|
39
|
+
path,
|
|
40
|
+
scope,
|
|
41
|
+
},
|
|
42
|
+
{ enabled: enabled && Boolean(resourceId || path) },
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
if (!enabled || (!resourceId && !path)) return null;
|
|
46
|
+
|
|
47
|
+
if (isLoading) {
|
|
48
|
+
return (
|
|
49
|
+
<div className="rounded-lg border bg-muted/30 p-3">
|
|
50
|
+
<Skeleton className="h-4 w-40" />
|
|
51
|
+
<Skeleton className="mt-2 h-3 w-72" />
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const data = impact as any;
|
|
57
|
+
if (!data) return null;
|
|
58
|
+
const affectsAllApps = data.affectsAllApps === true;
|
|
59
|
+
const appCount = data.affectedApps?.count;
|
|
60
|
+
const overrides = data.overrides ?? { count: 0, items: [] };
|
|
61
|
+
const willRequestApproval = data.approval?.willRequestApproval === true;
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div className="rounded-lg border bg-muted/30 p-3 text-xs">
|
|
65
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
66
|
+
<Badge variant={affectsAllApps ? "secondary" : "outline"}>
|
|
67
|
+
{affectsAllApps ? "All apps impact" : "Selected only"}
|
|
68
|
+
</Badge>
|
|
69
|
+
{willRequestApproval ? (
|
|
70
|
+
<Badge
|
|
71
|
+
variant="outline"
|
|
72
|
+
className="border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300"
|
|
73
|
+
>
|
|
74
|
+
Approval required
|
|
75
|
+
</Badge>
|
|
76
|
+
) : null}
|
|
77
|
+
{overrides.count > 0 ? (
|
|
78
|
+
<Badge variant="outline">
|
|
79
|
+
{overrides.count} override{overrides.count === 1 ? "" : "s"}
|
|
80
|
+
</Badge>
|
|
81
|
+
) : null}
|
|
82
|
+
</div>
|
|
83
|
+
<p className="mt-2 leading-relaxed text-muted-foreground">
|
|
84
|
+
{affectsAllApps
|
|
85
|
+
? `This change applies to every workspace app${typeof appCount === "number" ? ` (${appCount} discovered)` : ""}.`
|
|
86
|
+
: "This change only applies to explicitly granted apps."}{" "}
|
|
87
|
+
{willRequestApproval
|
|
88
|
+
? "It will be queued for approval before it takes effect."
|
|
89
|
+
: "It will take effect immediately when saved."}
|
|
90
|
+
</p>
|
|
91
|
+
{overrides.count > 0 ? (
|
|
92
|
+
<div className="mt-2 space-y-1">
|
|
93
|
+
{overrides.items.slice(0, 4).map((override: any) => (
|
|
94
|
+
<div
|
|
95
|
+
key={`${override.scope}:${override.owner}`}
|
|
96
|
+
className="flex items-center justify-between gap-3 rounded-md border bg-background px-2 py-1.5"
|
|
97
|
+
>
|
|
98
|
+
<span className="min-w-0 truncate text-muted-foreground">
|
|
99
|
+
{override.label}
|
|
100
|
+
</span>
|
|
101
|
+
<span className="shrink-0 font-mono text-[11px] text-muted-foreground">
|
|
102
|
+
{formatResourceTimestamp(override.updatedAt)}
|
|
103
|
+
</span>
|
|
104
|
+
</div>
|
|
105
|
+
))}
|
|
106
|
+
{overrides.count > 4 ? (
|
|
107
|
+
<div className="text-muted-foreground">
|
|
108
|
+
+{overrides.count - 4} more override
|
|
109
|
+
{overrides.count - 4 === 1 ? "" : "s"}
|
|
110
|
+
</div>
|
|
111
|
+
) : null}
|
|
112
|
+
</div>
|
|
113
|
+
) : null}
|
|
114
|
+
</div>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
|
|
6
|
+
const originalEnv = {
|
|
7
|
+
DATABASE_URL: process.env.DATABASE_URL,
|
|
8
|
+
DATABASE_AUTH_TOKEN: process.env.DATABASE_AUTH_TOKEN,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
let tempDir: string | null = null;
|
|
12
|
+
|
|
13
|
+
function restoreEnv() {
|
|
14
|
+
for (const [key, value] of Object.entries(originalEnv)) {
|
|
15
|
+
if (value === undefined) delete process.env[key];
|
|
16
|
+
else process.env[key] = value;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function setupTempDb() {
|
|
21
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "dispatch-migrations-"));
|
|
22
|
+
process.env.DATABASE_URL = `file:${path.join(tempDir, "app.db")}`;
|
|
23
|
+
delete process.env.DATABASE_AUTH_TOKEN;
|
|
24
|
+
vi.resetModules();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
beforeEach(async () => {
|
|
28
|
+
await setupTempDb();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
afterEach(async () => {
|
|
32
|
+
try {
|
|
33
|
+
const { closeDbExec } = await import("@agent-native/core/db");
|
|
34
|
+
await closeDbExec();
|
|
35
|
+
} catch {}
|
|
36
|
+
restoreEnv();
|
|
37
|
+
if (tempDir) {
|
|
38
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
39
|
+
tempDir = null;
|
|
40
|
+
}
|
|
41
|
+
vi.restoreAllMocks();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe("dispatch migrations", () => {
|
|
45
|
+
it("quietly records source_health when the column already exists", async () => {
|
|
46
|
+
const [{ getDbExec, runMigrations }, { dispatchMigrations }] =
|
|
47
|
+
await Promise.all([
|
|
48
|
+
import("@agent-native/core/db"),
|
|
49
|
+
import("./migrations.js"),
|
|
50
|
+
]);
|
|
51
|
+
const exec = getDbExec();
|
|
52
|
+
await exec.execute(`
|
|
53
|
+
CREATE TABLE dispatch_dreams (
|
|
54
|
+
id TEXT PRIMARY KEY,
|
|
55
|
+
source_health TEXT
|
|
56
|
+
)
|
|
57
|
+
`);
|
|
58
|
+
await exec.execute(
|
|
59
|
+
"CREATE TABLE dispatch_migrations (version INTEGER PRIMARY KEY)",
|
|
60
|
+
);
|
|
61
|
+
await exec.execute({
|
|
62
|
+
sql: "INSERT INTO dispatch_migrations VALUES (?)",
|
|
63
|
+
args: [3],
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const consoleError = vi
|
|
67
|
+
.spyOn(console, "error")
|
|
68
|
+
.mockImplementation(() => {});
|
|
69
|
+
await runMigrations(dispatchMigrations, {
|
|
70
|
+
table: "dispatch_migrations",
|
|
71
|
+
})({});
|
|
72
|
+
|
|
73
|
+
expect(consoleError).not.toHaveBeenCalled();
|
|
74
|
+
const { rows } = await exec.execute(
|
|
75
|
+
"SELECT MAX(version) as version FROM dispatch_migrations",
|
|
76
|
+
);
|
|
77
|
+
expect(rows[0]?.version).toBe(4);
|
|
78
|
+
});
|
|
79
|
+
});
|
package/src/db/migrations.ts
CHANGED
|
@@ -162,4 +162,63 @@ export const dispatchMigrations: Array<{ version: number; sql: string }> = [
|
|
|
162
162
|
);
|
|
163
163
|
`,
|
|
164
164
|
},
|
|
165
|
+
{
|
|
166
|
+
version: 3,
|
|
167
|
+
sql: `
|
|
168
|
+
CREATE TABLE IF NOT EXISTS dispatch_dreams (
|
|
169
|
+
id TEXT PRIMARY KEY,
|
|
170
|
+
owner_email TEXT NOT NULL,
|
|
171
|
+
org_id TEXT,
|
|
172
|
+
source_id TEXT NOT NULL,
|
|
173
|
+
title TEXT NOT NULL,
|
|
174
|
+
status TEXT NOT NULL,
|
|
175
|
+
query TEXT,
|
|
176
|
+
report TEXT,
|
|
177
|
+
summary TEXT,
|
|
178
|
+
candidate_count INTEGER NOT NULL,
|
|
179
|
+
inspected_thread_count INTEGER NOT NULL,
|
|
180
|
+
created_by TEXT NOT NULL,
|
|
181
|
+
error TEXT,
|
|
182
|
+
started_at INTEGER NOT NULL,
|
|
183
|
+
completed_at INTEGER,
|
|
184
|
+
created_at INTEGER NOT NULL,
|
|
185
|
+
updated_at INTEGER NOT NULL
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
CREATE TABLE IF NOT EXISTS dispatch_dream_proposals (
|
|
189
|
+
id TEXT PRIMARY KEY,
|
|
190
|
+
dream_id TEXT NOT NULL,
|
|
191
|
+
owner_email TEXT NOT NULL,
|
|
192
|
+
org_id TEXT,
|
|
193
|
+
target_type TEXT NOT NULL,
|
|
194
|
+
target_path TEXT NOT NULL,
|
|
195
|
+
title TEXT NOT NULL,
|
|
196
|
+
summary TEXT NOT NULL,
|
|
197
|
+
rationale TEXT NOT NULL,
|
|
198
|
+
content TEXT NOT NULL,
|
|
199
|
+
evidence TEXT NOT NULL,
|
|
200
|
+
confidence INTEGER NOT NULL,
|
|
201
|
+
risk TEXT NOT NULL,
|
|
202
|
+
status TEXT NOT NULL,
|
|
203
|
+
applied_by TEXT,
|
|
204
|
+
applied_at INTEGER,
|
|
205
|
+
rejected_by TEXT,
|
|
206
|
+
rejected_at INTEGER,
|
|
207
|
+
created_at INTEGER NOT NULL,
|
|
208
|
+
updated_at INTEGER NOT NULL
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
CREATE INDEX IF NOT EXISTS dispatch_dreams_owner_updated_idx
|
|
212
|
+
ON dispatch_dreams (owner_email, org_id, updated_at);
|
|
213
|
+
|
|
214
|
+
CREATE INDEX IF NOT EXISTS dispatch_dream_proposals_dream_status_idx
|
|
215
|
+
ON dispatch_dream_proposals (dream_id, status);
|
|
216
|
+
`,
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
version: 4,
|
|
220
|
+
sql: `
|
|
221
|
+
ALTER TABLE dispatch_dreams ADD COLUMN IF NOT EXISTS source_health TEXT;
|
|
222
|
+
`,
|
|
223
|
+
},
|
|
165
224
|
];
|
package/src/db/schema.ts
CHANGED
|
@@ -73,6 +73,50 @@ export const dispatchAuditEvents = table("dispatch_audit_events", {
|
|
|
73
73
|
createdAt: integer("created_at").notNull(),
|
|
74
74
|
});
|
|
75
75
|
|
|
76
|
+
export const dispatchDreams = table("dispatch_dreams", {
|
|
77
|
+
id: text("id").primaryKey(),
|
|
78
|
+
ownerEmail: text("owner_email").notNull(),
|
|
79
|
+
orgId: text("org_id"),
|
|
80
|
+
sourceId: text("source_id").notNull(),
|
|
81
|
+
title: text("title").notNull(),
|
|
82
|
+
status: text("status").notNull(),
|
|
83
|
+
query: text("query"),
|
|
84
|
+
report: text("report"),
|
|
85
|
+
summary: text("summary"),
|
|
86
|
+
sourceHealth: text("source_health"),
|
|
87
|
+
candidateCount: integer("candidate_count").notNull(),
|
|
88
|
+
inspectedThreadCount: integer("inspected_thread_count").notNull(),
|
|
89
|
+
createdBy: text("created_by").notNull(),
|
|
90
|
+
error: text("error"),
|
|
91
|
+
startedAt: integer("started_at").notNull(),
|
|
92
|
+
completedAt: integer("completed_at"),
|
|
93
|
+
createdAt: integer("created_at").notNull(),
|
|
94
|
+
updatedAt: integer("updated_at").notNull(),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
export const dispatchDreamProposals = table("dispatch_dream_proposals", {
|
|
98
|
+
id: text("id").primaryKey(),
|
|
99
|
+
dreamId: text("dream_id").notNull(),
|
|
100
|
+
ownerEmail: text("owner_email").notNull(),
|
|
101
|
+
orgId: text("org_id"),
|
|
102
|
+
targetType: text("target_type").notNull(),
|
|
103
|
+
targetPath: text("target_path").notNull(),
|
|
104
|
+
title: text("title").notNull(),
|
|
105
|
+
summary: text("summary").notNull(),
|
|
106
|
+
rationale: text("rationale").notNull(),
|
|
107
|
+
content: text("content").notNull(),
|
|
108
|
+
evidence: text("evidence").notNull(),
|
|
109
|
+
confidence: integer("confidence").notNull(),
|
|
110
|
+
risk: text("risk").notNull(),
|
|
111
|
+
status: text("status").notNull(),
|
|
112
|
+
appliedBy: text("applied_by"),
|
|
113
|
+
appliedAt: integer("applied_at"),
|
|
114
|
+
rejectedBy: text("rejected_by"),
|
|
115
|
+
rejectedAt: integer("rejected_at"),
|
|
116
|
+
createdAt: integer("created_at").notNull(),
|
|
117
|
+
updatedAt: integer("updated_at").notNull(),
|
|
118
|
+
});
|
|
119
|
+
|
|
76
120
|
// ─── Vault: workspace-wide secret management ───────────────────────
|
|
77
121
|
|
|
78
122
|
export const vaultSecrets = table("vault_secrets", {
|
|
@@ -141,7 +185,7 @@ export const workspaceResources = table("workspace_resources", {
|
|
|
141
185
|
description: text("description"),
|
|
142
186
|
path: text("path").notNull(), // resource path, e.g. "skills/designer.md"
|
|
143
187
|
content: text("content").notNull(),
|
|
144
|
-
scope: text("scope").notNull(), // "all" (
|
|
188
|
+
scope: text("scope").notNull(), // "all" (runtime inherited) | "selected" (grant per-app)
|
|
145
189
|
createdBy: text("created_by").notNull(),
|
|
146
190
|
createdAt: integer("created_at").notNull(),
|
|
147
191
|
updatedAt: integer("updated_at").notNull(),
|
|
@@ -154,7 +198,7 @@ export const workspaceResourceGrants = table("workspace_resource_grants", {
|
|
|
154
198
|
resourceId: text("resource_id").notNull(),
|
|
155
199
|
appId: text("app_id").notNull(),
|
|
156
200
|
status: text("status").notNull(), // "active" | "revoked"
|
|
157
|
-
syncedAt: integer("synced_at"),
|
|
201
|
+
syncedAt: integer("synced_at"), // legacy column retained for older rows
|
|
158
202
|
createdAt: integer("created_at").notNull(),
|
|
159
203
|
updatedAt: integer("updated_at").notNull(),
|
|
160
204
|
});
|
|
@@ -14,6 +14,9 @@ import type {
|
|
|
14
14
|
export interface NavigationState {
|
|
15
15
|
view: string;
|
|
16
16
|
path?: string;
|
|
17
|
+
dreamId?: string;
|
|
18
|
+
sourceId?: string;
|
|
19
|
+
query?: string;
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
export function useNavigationState(extensions?: DispatchExtensionConfig) {
|
|
@@ -24,10 +27,19 @@ export function useNavigationState(extensions?: DispatchExtensionConfig) {
|
|
|
24
27
|
// Sync current route to application state
|
|
25
28
|
useEffect(() => {
|
|
26
29
|
const localPathname = routerPath(location.pathname);
|
|
30
|
+
const params = new URLSearchParams(location.search);
|
|
27
31
|
const state: NavigationState = {
|
|
28
32
|
view: resolveView(localPathname, extensions),
|
|
29
33
|
path: appPath(localPathname),
|
|
30
34
|
};
|
|
35
|
+
if (state.view === "dreams") {
|
|
36
|
+
const dreamId = params.get("dreamId");
|
|
37
|
+
const sourceId = params.get("sourceId");
|
|
38
|
+
const query = params.get("query");
|
|
39
|
+
if (dreamId) state.dreamId = dreamId;
|
|
40
|
+
if (sourceId) state.sourceId = sourceId;
|
|
41
|
+
if (query) state.query = query;
|
|
42
|
+
}
|
|
31
43
|
|
|
32
44
|
fetch(agentNativePath("/_agent-native/application-state/navigation"), {
|
|
33
45
|
method: "PUT",
|
|
@@ -35,7 +47,7 @@ export function useNavigationState(extensions?: DispatchExtensionConfig) {
|
|
|
35
47
|
headers: { "Content-Type": "application/json" },
|
|
36
48
|
body: JSON.stringify(state),
|
|
37
49
|
}).catch(() => {});
|
|
38
|
-
}, [extensions, location.pathname]);
|
|
50
|
+
}, [extensions, location.pathname, location.search]);
|
|
39
51
|
|
|
40
52
|
// Listen for navigate commands from agent
|
|
41
53
|
const { data: navCommand } = useQuery({
|
|
@@ -66,10 +78,14 @@ export function useNavigationState(extensions?: DispatchExtensionConfig) {
|
|
|
66
78
|
const cmd = navCommand as NavigationState;
|
|
67
79
|
|
|
68
80
|
// Navigate to a specific path or resolve view name to path
|
|
69
|
-
const
|
|
70
|
-
cmd.path || resolvePath(cmd.view, extensions) || "/overview"
|
|
71
|
-
|
|
72
|
-
|
|
81
|
+
const resolvedPath =
|
|
82
|
+
cmd.path || resolvePath(cmd.view, extensions) || "/overview";
|
|
83
|
+
const path =
|
|
84
|
+
cmd.view === "dreams" && cmd.dreamId && !resolvedPath.includes("?")
|
|
85
|
+
? `${resolvedPath}?dreamId=${encodeURIComponent(cmd.dreamId)}`
|
|
86
|
+
: resolvedPath;
|
|
87
|
+
const nextPath = routerPath(path);
|
|
88
|
+
navigate(nextPath);
|
|
73
89
|
qc.setQueryData(["navigate-command"], null);
|
|
74
90
|
}, [extensions, navCommand, navigate, qc]);
|
|
75
91
|
}
|
|
@@ -139,6 +155,7 @@ function resolveView(
|
|
|
139
155
|
if (pathname.startsWith("/identities")) return "identities";
|
|
140
156
|
if (pathname.startsWith("/approvals")) return "approvals";
|
|
141
157
|
if (pathname.startsWith("/audit")) return "audit";
|
|
158
|
+
if (pathname.startsWith("/dreams")) return "dreams";
|
|
142
159
|
if (pathname.startsWith("/thread-debug")) return "thread-debug";
|
|
143
160
|
if (pathname.startsWith("/team")) return "team";
|
|
144
161
|
return "overview";
|
|
@@ -180,6 +197,8 @@ function resolvePath(
|
|
|
180
197
|
return "/approvals";
|
|
181
198
|
case "audit":
|
|
182
199
|
return "/audit";
|
|
200
|
+
case "dreams":
|
|
201
|
+
return "/dreams";
|
|
183
202
|
case "thread-debug":
|
|
184
203
|
case "threads":
|
|
185
204
|
return "/thread-debug";
|