@agent-native/dispatch 0.18.0 → 0.19.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/dist/actions/create-workspace-resource.d.ts +13 -13
- package/dist/actions/delete-workspace-resource.d.ts +13 -13
- 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/list-vault-audit.js +1 -1
- package/dist/actions/list-vault-audit.js.map +1 -1
- package/dist/actions/list-vault-secret-options.js +2 -9
- package/dist/actions/list-vault-secret-options.js.map +1 -1
- package/dist/actions/list-vault-secrets.js +1 -1
- package/dist/actions/list-vault-secrets.js.map +1 -1
- package/dist/actions/provider-api-register.d.ts +7 -7
- package/dist/actions/set-app-creation-settings.js +1 -1
- package/dist/actions/set-app-creation-settings.js.map +1 -1
- package/dist/actions/set-vault-access-settings.js +1 -1
- package/dist/actions/set-vault-access-settings.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/view-screen.js +4 -3
- package/dist/actions/view-screen.js.map +1 -1
- package/dist/components/admin-navigation.d.ts +15 -0
- package/dist/components/admin-navigation.d.ts.map +1 -0
- package/dist/components/admin-navigation.js +174 -0
- package/dist/components/admin-navigation.js.map +1 -0
- package/dist/components/automation-run-history-dialog.js +1 -1
- package/dist/components/automation-run-history-dialog.js.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -0
- package/dist/components/index.js.map +1 -1
- package/dist/components/layout/Header.d.ts.map +1 -1
- package/dist/components/layout/Header.js +9 -0
- package/dist/components/layout/Header.js.map +1 -1
- package/dist/components/layout/Layout.d.ts +4 -1
- package/dist/components/layout/Layout.d.ts.map +1 -1
- package/dist/components/layout/Layout.js +14 -117
- package/dist/components/layout/Layout.js.map +1 -1
- package/dist/components/layout/workspace-apps-rail.d.ts +5 -0
- package/dist/components/layout/workspace-apps-rail.d.ts.map +1 -0
- package/dist/components/layout/workspace-apps-rail.js +76 -0
- package/dist/components/layout/workspace-apps-rail.js.map +1 -0
- package/dist/hooks/use-navigation-state.js +39 -16
- package/dist/hooks/use-navigation-state.js.map +1 -1
- package/dist/lib/automation-display.js +1 -1
- package/dist/lib/automation-display.js.map +1 -1
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +22 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/pages/admin._index.d.ts +5 -0
- package/dist/routes/pages/admin._index.d.ts.map +1 -0
- package/dist/routes/pages/admin._index.js +19 -0
- package/dist/routes/pages/admin._index.js.map +1 -0
- package/dist/routes/pages/admin.d.ts +5 -0
- package/dist/routes/pages/admin.d.ts.map +1 -0
- package/dist/routes/pages/admin.js +10 -0
- package/dist/routes/pages/admin.js.map +1 -0
- package/dist/routes/pages/approval.js +2 -2
- package/dist/routes/pages/approval.js.map +1 -1
- package/dist/routes/pages/apps.$appId.d.ts.map +1 -1
- package/dist/routes/pages/apps.$appId.js +3 -2
- package/dist/routes/pages/apps.$appId.js.map +1 -1
- package/dist/routes/pages/integrations.d.ts.map +1 -1
- package/dist/routes/pages/integrations.js +2 -1
- package/dist/routes/pages/integrations.js.map +1 -1
- package/dist/routes/pages/operations.js +3 -3
- package/dist/routes/pages/operations.js.map +1 -1
- package/dist/routes/pages/settings.d.ts.map +1 -1
- package/dist/routes/pages/settings.js +1 -1
- package/dist/routes/pages/settings.js.map +1 -1
- package/dist/routes/pages/team.js +1 -1
- package/dist/routes/pages/team.js.map +1 -1
- package/dist/routes/pages/transactional-email.$appId.$id.d.ts.map +1 -1
- package/dist/routes/pages/transactional-email.$appId.$id.js +5 -2
- package/dist/routes/pages/transactional-email.$appId.$id.js.map +1 -1
- package/dist/routes/pages/transactional-email.d.ts.map +1 -1
- package/dist/routes/pages/transactional-email.js +6 -2
- package/dist/routes/pages/transactional-email.js.map +1 -1
- package/dist/routes/pages/vault.d.ts.map +1 -1
- package/dist/routes/pages/vault.js +20 -16
- package/dist/routes/pages/vault.js.map +1 -1
- package/dist/server/lib/app-creation-store.js +2 -2
- package/dist/server/lib/app-creation-store.js.map +1 -1
- package/dist/server/lib/thread-debug-store.d.ts.map +1 -1
- package/dist/server/lib/thread-debug-store.js +2 -1
- package/dist/server/lib/thread-debug-store.js.map +1 -1
- package/dist/server/lib/vault-store.d.ts +15 -0
- package/dist/server/lib/vault-store.d.ts.map +1 -1
- package/dist/server/lib/vault-store.js +110 -17
- package/dist/server/lib/vault-store.js.map +1 -1
- package/package.json +3 -3
- package/src/actions/grant-vault-secrets-to-app.ts +1 -1
- package/src/actions/list-vault-audit.ts +1 -1
- package/src/actions/list-vault-secret-options.ts +2 -9
- package/src/actions/list-vault-secrets.ts +1 -1
- package/src/actions/set-app-creation-settings.ts +1 -1
- package/src/actions/set-vault-access-settings.ts +1 -1
- package/src/actions/sync-vault-to-app.ts +1 -1
- package/src/actions/view-screen.spec.ts +1 -0
- package/src/actions/view-screen.ts +5 -3
- package/src/components/admin-navigation.tsx +278 -0
- package/src/components/automation-run-history-dialog.tsx +1 -1
- package/src/components/index.ts +1 -0
- package/src/components/layout/Header.tsx +8 -0
- package/src/components/layout/Layout.spec.tsx +85 -6
- package/src/components/layout/Layout.tsx +62 -206
- package/src/components/layout/workspace-apps-rail.tsx +154 -0
- package/src/hooks/use-navigation-state.spec.ts +11 -0
- package/src/hooks/use-navigation-state.ts +37 -16
- package/src/lib/automation-display.spec.ts +1 -1
- package/src/lib/automation-display.ts +1 -1
- package/src/routes/index.spec.ts +8 -0
- package/src/routes/index.ts +25 -0
- package/src/routes/pages/admin._index.tsx +39 -0
- package/src/routes/pages/admin.tsx +15 -0
- package/src/routes/pages/approval.tsx +2 -2
- package/src/routes/pages/apps.$appId.tsx +7 -2
- package/src/routes/pages/integrations.tsx +3 -2
- package/src/routes/pages/operations.tsx +3 -3
- package/src/routes/pages/settings.tsx +7 -3
- package/src/routes/pages/team.tsx +1 -1
- package/src/routes/pages/transactional-email.$appId.$id.tsx +9 -2
- package/src/routes/pages/transactional-email.tsx +6 -2
- package/src/routes/pages/vault.tsx +152 -89
- package/src/server/lib/app-creation-store.ts +2 -2
- package/src/server/lib/approval-fencing.spec.ts +69 -0
- package/src/server/lib/thread-debug-store.spec.ts +7 -3
- package/src/server/lib/thread-debug-store.ts +2 -1
- package/src/server/lib/vault-store.spec.ts +59 -0
- package/src/server/lib/vault-store.ts +123 -17
package/src/routes/index.ts
CHANGED
|
@@ -36,6 +36,31 @@ export const dispatchRoutes: RouteConfig = [
|
|
|
36
36
|
route("chat", "./pages/chat.js"),
|
|
37
37
|
route("chat/:threadId", "./pages/chat.js"),
|
|
38
38
|
route("overview", "./pages/overview.js"),
|
|
39
|
+
route("admin", "./pages/admin.js", [
|
|
40
|
+
index("./pages/admin._index.js"),
|
|
41
|
+
route("apps", "./pages/apps.js"),
|
|
42
|
+
route("apps/:appId", "./pages/apps.$appId.js"),
|
|
43
|
+
route("new-app", "./pages/new-app.js"),
|
|
44
|
+
route("operations", "./pages/operations.js"),
|
|
45
|
+
route("metrics", "./pages/metrics.js"),
|
|
46
|
+
route("automations", "./pages/automations.js"),
|
|
47
|
+
route("approvals", "./pages/approvals.js"),
|
|
48
|
+
route("destinations", "./pages/destinations.js"),
|
|
49
|
+
route("integrations", "./pages/integrations.js"),
|
|
50
|
+
route("transactional-email", "./pages/transactional-email.js"),
|
|
51
|
+
route(
|
|
52
|
+
"transactional-email/:appId/:id",
|
|
53
|
+
"./pages/transactional-email.$appId.$id.js",
|
|
54
|
+
),
|
|
55
|
+
route("vault", "./pages/vault.js"),
|
|
56
|
+
route("agents", "./pages/agents.js"),
|
|
57
|
+
route("workspace", "./pages/workspace.js"),
|
|
58
|
+
route("messaging", "./pages/messaging.js"),
|
|
59
|
+
route("identities", "./pages/identities.js"),
|
|
60
|
+
route("audit", "./pages/audit.js"),
|
|
61
|
+
route("dreams", "./pages/dreams.js"),
|
|
62
|
+
route("thread-debug", "./pages/thread-debug.js"),
|
|
63
|
+
]),
|
|
39
64
|
route("metrics", "./pages/metrics.js"),
|
|
40
65
|
route("operations", "./pages/operations.js"),
|
|
41
66
|
route("apps", "./pages/apps.js"),
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useT } from "@agent-native/core/client/i18n";
|
|
2
|
+
|
|
3
|
+
import { DispatchShell } from "../../components/dispatch-shell";
|
|
4
|
+
|
|
5
|
+
export function meta() {
|
|
6
|
+
return [{ title: "Admin — Dispatch" }];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default function AdminOverviewRoute() {
|
|
10
|
+
const t = useT();
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<DispatchShell
|
|
14
|
+
title={t("dispatch.nav.admin", { defaultValue: "Admin" })}
|
|
15
|
+
description={t("dispatch.pages.adminDescription", {
|
|
16
|
+
defaultValue: "Workspace controls and operations",
|
|
17
|
+
})}
|
|
18
|
+
>
|
|
19
|
+
<section className="max-w-2xl space-y-2">
|
|
20
|
+
<h2 className="text-base font-semibold text-foreground">
|
|
21
|
+
{t("dispatch.pages.adminWelcome", {
|
|
22
|
+
defaultValue: "Workspace control plane",
|
|
23
|
+
})}
|
|
24
|
+
</h2>
|
|
25
|
+
<p className="text-sm leading-6 text-muted-foreground">
|
|
26
|
+
{t("dispatch.pages.adminChooseArea", {
|
|
27
|
+
defaultValue:
|
|
28
|
+
"Manage apps, connections, automations, and operational tools from one place.",
|
|
29
|
+
})}
|
|
30
|
+
</p>
|
|
31
|
+
<p className="text-sm leading-6 text-muted-foreground">
|
|
32
|
+
{t("dispatch.pages.adminChooseAreaHint", {
|
|
33
|
+
defaultValue: "Choose an area from the Admin navigation.",
|
|
34
|
+
})}
|
|
35
|
+
</p>
|
|
36
|
+
</section>
|
|
37
|
+
</DispatchShell>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Outlet } from "react-router";
|
|
2
|
+
|
|
3
|
+
import { AdminShell } from "../../components/admin-navigation";
|
|
4
|
+
|
|
5
|
+
export function meta() {
|
|
6
|
+
return [{ title: "Admin — Dispatch" }];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default function AdminRoute() {
|
|
10
|
+
return (
|
|
11
|
+
<AdminShell>
|
|
12
|
+
<Outlet />
|
|
13
|
+
</AdminShell>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -175,7 +175,7 @@ export default function ApprovalPreviewRoute() {
|
|
|
175
175
|
size="sm"
|
|
176
176
|
variant="outline"
|
|
177
177
|
className="mt-4 gap-1.5"
|
|
178
|
-
onClick={() => postNavigate(appPath("/approvals"))}
|
|
178
|
+
onClick={() => postNavigate(appPath("/admin/approvals"))}
|
|
179
179
|
>
|
|
180
180
|
<IconArrowUpRight size={14} />
|
|
181
181
|
View all approvals
|
|
@@ -288,7 +288,7 @@ export default function ApprovalPreviewRoute() {
|
|
|
288
288
|
size="sm"
|
|
289
289
|
variant="ghost"
|
|
290
290
|
className="gap-1.5 text-muted-foreground"
|
|
291
|
-
onClick={() => postNavigate(appPath("/approvals"))}
|
|
291
|
+
onClick={() => postNavigate(appPath("/admin/approvals"))}
|
|
292
292
|
>
|
|
293
293
|
<IconArrowUpRight size={14} />
|
|
294
294
|
Open in app
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
IconClockHour4,
|
|
8
8
|
} from "@tabler/icons-react";
|
|
9
9
|
import { useEffect, useMemo } from "react";
|
|
10
|
-
import { Link, useParams } from "react-router";
|
|
10
|
+
import { Link, useLocation, useParams } from "react-router";
|
|
11
11
|
|
|
12
12
|
import { ActionQueryError } from "../../components/action-query-error";
|
|
13
13
|
import { DispatchShell } from "../../components/dispatch-shell";
|
|
@@ -25,6 +25,7 @@ export function meta() {
|
|
|
25
25
|
|
|
26
26
|
export default function WorkspaceAppRoute() {
|
|
27
27
|
const t = useT();
|
|
28
|
+
const location = useLocation();
|
|
28
29
|
const { appId } = useParams();
|
|
29
30
|
const appsQuery = useActionQuery("list-workspace-apps", {
|
|
30
31
|
includeAgentCards: false,
|
|
@@ -49,7 +50,11 @@ export default function WorkspaceAppRoute() {
|
|
|
49
50
|
>
|
|
50
51
|
<div className="max-w-2xl rounded-lg bg-card p-5">
|
|
51
52
|
<Button asChild size="sm" variant="ghost" className="-ml-2 mb-4">
|
|
52
|
-
<Link
|
|
53
|
+
<Link
|
|
54
|
+
to={
|
|
55
|
+
location.pathname.startsWith("/admin/") ? "/admin/apps" : "/apps"
|
|
56
|
+
}
|
|
57
|
+
>
|
|
53
58
|
<IconArrowLeft size={15} className="mr-1.5" />
|
|
54
59
|
{t("dispatch.nav.apps")}
|
|
55
60
|
</Link>
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from "@tabler/icons-react";
|
|
13
13
|
import { useQueryClient } from "@tanstack/react-query";
|
|
14
14
|
import { useMemo, useState } from "react";
|
|
15
|
+
import { Link } from "react-router";
|
|
15
16
|
import { toast } from "sonner";
|
|
16
17
|
|
|
17
18
|
import { ActionQueryError } from "../../components/action-query-error";
|
|
@@ -449,9 +450,9 @@ export default function ConnectionsRoute() {
|
|
|
449
450
|
</div>
|
|
450
451
|
<div className="flex items-center justify-end gap-1.5 border-t px-4 py-2.5 text-xs text-muted-foreground">
|
|
451
452
|
<IconLink size={12} />
|
|
452
|
-
<
|
|
453
|
+
<Link to="/admin/vault" className="hover:underline">
|
|
453
454
|
Open vault for advanced sharing
|
|
454
|
-
</
|
|
455
|
+
</Link>
|
|
455
456
|
</div>
|
|
456
457
|
</CollapsibleContent>
|
|
457
458
|
</Collapsible>
|
|
@@ -30,17 +30,17 @@ function OperationsShortcuts() {
|
|
|
30
30
|
|
|
31
31
|
const tools = [
|
|
32
32
|
{
|
|
33
|
-
to: "/thread-debug",
|
|
33
|
+
to: "/admin/thread-debug",
|
|
34
34
|
icon: IconMessages,
|
|
35
35
|
title: t("dispatch.nav.threadDebug", { defaultValue: "Thread debug" }),
|
|
36
36
|
},
|
|
37
37
|
{
|
|
38
|
-
to: "/audit",
|
|
38
|
+
to: "/admin/audit",
|
|
39
39
|
icon: IconHistory,
|
|
40
40
|
title: t("dispatch.nav.audit"),
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
|
-
to: "/destinations",
|
|
43
|
+
to: "/admin/destinations",
|
|
44
44
|
icon: IconSend,
|
|
45
45
|
title: t("dispatch.pages.deliveryQueue"),
|
|
46
46
|
},
|
|
@@ -62,7 +62,7 @@ export default function SettingsRoute() {
|
|
|
62
62
|
</CardHeader>
|
|
63
63
|
<CardContent>
|
|
64
64
|
<Button variant="outline" asChild>
|
|
65
|
-
<Link to="/workspace">
|
|
65
|
+
<Link to="/admin/workspace">
|
|
66
66
|
{t("settings.openResourceSettings")}
|
|
67
67
|
</Link>
|
|
68
68
|
</Button>
|
|
@@ -80,7 +80,9 @@ export default function SettingsRoute() {
|
|
|
80
80
|
</CardHeader>
|
|
81
81
|
<CardContent>
|
|
82
82
|
<Button variant="outline" asChild>
|
|
83
|
-
<Link to="/automations">
|
|
83
|
+
<Link to="/admin/automations">
|
|
84
|
+
{t("settings.openAutomations")}
|
|
85
|
+
</Link>
|
|
84
86
|
</Button>
|
|
85
87
|
</CardContent>
|
|
86
88
|
</Card>
|
|
@@ -96,7 +98,9 @@ export default function SettingsRoute() {
|
|
|
96
98
|
</CardHeader>
|
|
97
99
|
<CardContent>
|
|
98
100
|
<Button variant="outline" asChild>
|
|
99
|
-
<Link to="/destinations">
|
|
101
|
+
<Link to="/admin/destinations">
|
|
102
|
+
{t("settings.openDelivery")}
|
|
103
|
+
</Link>
|
|
100
104
|
</Button>
|
|
101
105
|
</CardContent>
|
|
102
106
|
</Card>
|
|
@@ -3,7 +3,7 @@ import { useT } from "@agent-native/core/client/i18n";
|
|
|
3
3
|
import { IconAlertTriangle, IconArrowLeft } from "@tabler/icons-react";
|
|
4
4
|
import { useQueries, useQuery } from "@tanstack/react-query";
|
|
5
5
|
import { useMemo } from "react";
|
|
6
|
-
import { Link, useParams } from "react-router";
|
|
6
|
+
import { Link, useLocation, useParams } from "react-router";
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
9
|
aggregateSharedEmails,
|
|
@@ -155,6 +155,7 @@ function useEmailDetail(appId: string, id: string) {
|
|
|
155
155
|
|
|
156
156
|
export default function TransactionalEmailDetailRoute() {
|
|
157
157
|
const t = useT();
|
|
158
|
+
const location = useLocation();
|
|
158
159
|
const params = useParams<{ appId: string; id: string }>();
|
|
159
160
|
const appId = params.appId ?? "";
|
|
160
161
|
const id = params.id ?? "";
|
|
@@ -213,7 +214,13 @@ export default function TransactionalEmailDetailRoute() {
|
|
|
213
214
|
>
|
|
214
215
|
<div className="max-w-3xl space-y-4">
|
|
215
216
|
<Button asChild size="sm" variant="ghost" className="-ml-2">
|
|
216
|
-
<Link
|
|
217
|
+
<Link
|
|
218
|
+
to={
|
|
219
|
+
location.pathname.startsWith("/admin/")
|
|
220
|
+
? "/admin/transactional-email"
|
|
221
|
+
: "/transactional-email"
|
|
222
|
+
}
|
|
223
|
+
>
|
|
217
224
|
<IconArrowLeft size={15} className="mr-1.5" />
|
|
218
225
|
{t("dispatch.transactionalEmail.title")}
|
|
219
226
|
</Link>
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from "@tabler/icons-react";
|
|
11
11
|
import { useQueries, useQuery } from "@tanstack/react-query";
|
|
12
12
|
import { useMemo, useState } from "react";
|
|
13
|
-
import { Link } from "react-router";
|
|
13
|
+
import { Link, useLocation } from "react-router";
|
|
14
14
|
|
|
15
15
|
import {
|
|
16
16
|
aggregateSharedEmails,
|
|
@@ -180,14 +180,18 @@ function EmailRow({
|
|
|
180
180
|
engagementLoading: boolean;
|
|
181
181
|
}) {
|
|
182
182
|
const t = useT();
|
|
183
|
+
const location = useLocation();
|
|
183
184
|
const [previewOpen, setPreviewOpen] = useState(false);
|
|
184
185
|
const [activityOpen, setActivityOpen] = useState(false);
|
|
186
|
+
const detailBase = location.pathname.startsWith("/admin/")
|
|
187
|
+
? "/admin/transactional-email"
|
|
188
|
+
: "/transactional-email";
|
|
185
189
|
|
|
186
190
|
return (
|
|
187
191
|
<TableRow>
|
|
188
192
|
<TableCell className="align-top">
|
|
189
193
|
<Link
|
|
190
|
-
to={
|
|
194
|
+
to={`${detailBase}/${appId}/${email.id}`}
|
|
191
195
|
className="text-sm font-medium text-foreground hover:underline"
|
|
192
196
|
>
|
|
193
197
|
{email.name}
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
useActionMutation,
|
|
3
3
|
useActionQuery,
|
|
4
4
|
} from "@agent-native/core/client/hooks";
|
|
5
|
+
import { useOrgRole } from "@agent-native/core/client/org";
|
|
5
6
|
import {
|
|
6
7
|
IconChevronDown,
|
|
7
8
|
IconChevronRight,
|
|
@@ -648,7 +649,13 @@ function SecretRow({
|
|
|
648
649
|
);
|
|
649
650
|
}
|
|
650
651
|
|
|
651
|
-
function RequestRow({
|
|
652
|
+
function RequestRow({
|
|
653
|
+
request,
|
|
654
|
+
canManage,
|
|
655
|
+
}: {
|
|
656
|
+
request: any;
|
|
657
|
+
canManage: boolean;
|
|
658
|
+
}) {
|
|
652
659
|
const [secretValue, setSecretValue] = useState("");
|
|
653
660
|
|
|
654
661
|
const approve = useActionMutation("approve-vault-request", {
|
|
@@ -706,7 +713,7 @@ function RequestRow({ request }: { request: any }) {
|
|
|
706
713
|
</Badge>
|
|
707
714
|
)}
|
|
708
715
|
</div>
|
|
709
|
-
{request.status === "pending" && (
|
|
716
|
+
{request.status === "pending" && canManage ? (
|
|
710
717
|
<div className="mt-3 flex items-end gap-2 border-t pt-3">
|
|
711
718
|
<div className="flex-1 space-y-1">
|
|
712
719
|
<Label className="text-xs">Secret value to provision</Label>
|
|
@@ -734,17 +741,45 @@ function RequestRow({ request }: { request: any }) {
|
|
|
734
741
|
Deny
|
|
735
742
|
</Button>
|
|
736
743
|
</div>
|
|
737
|
-
)
|
|
744
|
+
) : request.status === "pending" ? (
|
|
745
|
+
<p className="mt-3 border-t pt-3 text-xs text-muted-foreground">
|
|
746
|
+
Waiting for a workspace owner or admin to review this request.
|
|
747
|
+
</p>
|
|
748
|
+
) : null}
|
|
738
749
|
</div>
|
|
739
750
|
);
|
|
740
751
|
}
|
|
741
752
|
|
|
742
753
|
export default function VaultRoute() {
|
|
743
|
-
const
|
|
744
|
-
const
|
|
745
|
-
const
|
|
746
|
-
|
|
747
|
-
const
|
|
754
|
+
const { org, role, isLoading: orgLoading, error: orgError } = useOrgRole();
|
|
755
|
+
const accessReady = !orgLoading && !orgError && !!org;
|
|
756
|
+
const canManageVault =
|
|
757
|
+
accessReady && (!org.orgId || role === "owner" || role === "admin");
|
|
758
|
+
const secretsQuery = useActionQuery(
|
|
759
|
+
"list-vault-secrets",
|
|
760
|
+
{},
|
|
761
|
+
{ enabled: canManageVault },
|
|
762
|
+
);
|
|
763
|
+
const grantsQuery = useActionQuery(
|
|
764
|
+
"list-vault-grants",
|
|
765
|
+
{},
|
|
766
|
+
{ enabled: canManageVault },
|
|
767
|
+
);
|
|
768
|
+
const requestsQuery = useActionQuery(
|
|
769
|
+
"list-vault-requests",
|
|
770
|
+
{},
|
|
771
|
+
{ enabled: accessReady },
|
|
772
|
+
);
|
|
773
|
+
const auditQuery = useActionQuery(
|
|
774
|
+
"list-vault-audit",
|
|
775
|
+
{ limit: 20 },
|
|
776
|
+
{ enabled: canManageVault },
|
|
777
|
+
);
|
|
778
|
+
const accessQuery = useActionQuery(
|
|
779
|
+
"get-vault-access-settings",
|
|
780
|
+
{},
|
|
781
|
+
{ enabled: accessReady },
|
|
782
|
+
);
|
|
748
783
|
const { data: secrets, isLoading: secretsLoading } = secretsQuery;
|
|
749
784
|
const { data: grants } = grantsQuery;
|
|
750
785
|
const { data: requests } = requestsQuery;
|
|
@@ -787,13 +822,31 @@ export default function VaultRoute() {
|
|
|
787
822
|
</Badge>
|
|
788
823
|
)}
|
|
789
824
|
</TabsTrigger>
|
|
790
|
-
|
|
825
|
+
{canManageVault ? (
|
|
826
|
+
<TabsTrigger value="audit">Audit</TabsTrigger>
|
|
827
|
+
) : null}
|
|
791
828
|
</TabsList>
|
|
792
829
|
|
|
793
830
|
<TabsContent value="secrets" className="mt-4 space-y-3">
|
|
794
|
-
{
|
|
795
|
-
|
|
796
|
-
|
|
831
|
+
{!accessReady ? (
|
|
832
|
+
<div className="rounded-2xl border border-dashed px-6 py-12 text-center text-sm text-muted-foreground">
|
|
833
|
+
Checking workspace access…
|
|
834
|
+
</div>
|
|
835
|
+
) : !canManageVault ? (
|
|
836
|
+
<div className="rounded-2xl border border-dashed px-6 py-12 text-center">
|
|
837
|
+
<IconKey size={32} className="mx-auto text-muted-foreground/50" />
|
|
838
|
+
<h3 className="mt-3 text-sm font-medium text-foreground">
|
|
839
|
+
Vault management is restricted
|
|
840
|
+
</h3>
|
|
841
|
+
<p className="mx-auto mt-1 max-w-md text-sm text-muted-foreground">
|
|
842
|
+
Workspace owners and admins manage shared secret values. Use a
|
|
843
|
+
request from the app that needs a key, and an admin can review
|
|
844
|
+
it from the Requests tab.
|
|
845
|
+
</p>
|
|
846
|
+
</div>
|
|
847
|
+
) : secretsQuery.isError ||
|
|
848
|
+
grantsQuery.isError ||
|
|
849
|
+
accessQuery.isError ? (
|
|
797
850
|
<ActionQueryError
|
|
798
851
|
error={
|
|
799
852
|
secretsQuery.error ?? grantsQuery.error ?? accessQuery.error
|
|
@@ -805,60 +858,64 @@ export default function VaultRoute() {
|
|
|
805
858
|
}}
|
|
806
859
|
/>
|
|
807
860
|
) : null}
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
<
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
</div>
|
|
823
|
-
|
|
824
|
-
{!secretsQuery.isError &&
|
|
825
|
-
secretsLoading &&
|
|
826
|
-
(secrets ?? []).length === 0
|
|
827
|
-
? Array.from({ length: 3 }).map((_, index) => (
|
|
828
|
-
<div
|
|
829
|
-
key={index}
|
|
830
|
-
className="rounded-2xl bg-card px-5 py-4 space-y-2"
|
|
831
|
-
>
|
|
832
|
-
<Skeleton className="h-4 w-1/3" />
|
|
833
|
-
<Skeleton className="h-3 w-2/3" />
|
|
861
|
+
{accessReady && canManageVault ? (
|
|
862
|
+
<>
|
|
863
|
+
<VaultAccessSettingsCard mode={accessMode} />
|
|
864
|
+
|
|
865
|
+
<div className="flex items-center justify-between">
|
|
866
|
+
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
867
|
+
<IconKey size={16} />
|
|
868
|
+
{secretsLoading ? (
|
|
869
|
+
<Skeleton className="h-4 w-20" />
|
|
870
|
+
) : (
|
|
871
|
+
<span>
|
|
872
|
+
{`${secrets?.length || 0} secret${(secrets?.length || 0) !== 1 ? "s" : ""}`}
|
|
873
|
+
</span>
|
|
874
|
+
)}
|
|
834
875
|
</div>
|
|
835
|
-
|
|
836
|
-
: (secrets || []).map((secret: any) => (
|
|
837
|
-
<SecretRow
|
|
838
|
-
key={secret.id}
|
|
839
|
-
secret={secret}
|
|
840
|
-
grants={grantsBySecret[secret.id] || []}
|
|
841
|
-
accessMode={accessMode}
|
|
842
|
-
/>
|
|
843
|
-
))}
|
|
844
|
-
|
|
845
|
-
{!secretsQuery.isError &&
|
|
846
|
-
!secretsLoading &&
|
|
847
|
-
(secrets?.length || 0) === 0 && (
|
|
848
|
-
<div className="rounded-2xl border border-dashed px-6 py-12 text-center">
|
|
849
|
-
<IconKey
|
|
850
|
-
size={32}
|
|
851
|
-
className="mx-auto text-muted-foreground/50"
|
|
852
|
-
/>
|
|
853
|
-
<h3 className="mt-3 text-sm font-medium text-foreground">
|
|
854
|
-
No secrets yet
|
|
855
|
-
</h3>
|
|
856
|
-
<p className="mt-1 text-sm text-muted-foreground">
|
|
857
|
-
Add your first secret to start sharing credentials across
|
|
858
|
-
workspace apps.
|
|
859
|
-
</p>
|
|
876
|
+
<AddSecretDialog />
|
|
860
877
|
</div>
|
|
861
|
-
|
|
878
|
+
|
|
879
|
+
{!secretsQuery.isError &&
|
|
880
|
+
secretsLoading &&
|
|
881
|
+
(secrets ?? []).length === 0
|
|
882
|
+
? Array.from({ length: 3 }).map((_, index) => (
|
|
883
|
+
<div
|
|
884
|
+
key={index}
|
|
885
|
+
className="rounded-2xl bg-card px-5 py-4 space-y-2"
|
|
886
|
+
>
|
|
887
|
+
<Skeleton className="h-4 w-1/3" />
|
|
888
|
+
<Skeleton className="h-3 w-2/3" />
|
|
889
|
+
</div>
|
|
890
|
+
))
|
|
891
|
+
: (secrets || []).map((secret: any) => (
|
|
892
|
+
<SecretRow
|
|
893
|
+
key={secret.id}
|
|
894
|
+
secret={secret}
|
|
895
|
+
grants={grantsBySecret[secret.id] || []}
|
|
896
|
+
accessMode={accessMode}
|
|
897
|
+
/>
|
|
898
|
+
))}
|
|
899
|
+
|
|
900
|
+
{!secretsQuery.isError &&
|
|
901
|
+
!secretsLoading &&
|
|
902
|
+
(secrets?.length || 0) === 0 && (
|
|
903
|
+
<div className="rounded-2xl border border-dashed px-6 py-12 text-center">
|
|
904
|
+
<IconKey
|
|
905
|
+
size={32}
|
|
906
|
+
className="mx-auto text-muted-foreground/50"
|
|
907
|
+
/>
|
|
908
|
+
<h3 className="mt-3 text-sm font-medium text-foreground">
|
|
909
|
+
No secrets yet
|
|
910
|
+
</h3>
|
|
911
|
+
<p className="mt-1 text-sm text-muted-foreground">
|
|
912
|
+
Add your first secret to start sharing credentials across
|
|
913
|
+
workspace apps.
|
|
914
|
+
</p>
|
|
915
|
+
</div>
|
|
916
|
+
)}
|
|
917
|
+
</>
|
|
918
|
+
) : null}
|
|
862
919
|
</TabsContent>
|
|
863
920
|
|
|
864
921
|
<TabsContent value="requests" className="mt-4 space-y-3">
|
|
@@ -869,7 +926,11 @@ export default function VaultRoute() {
|
|
|
869
926
|
/>
|
|
870
927
|
) : null}
|
|
871
928
|
{(requests || []).map((request: any) => (
|
|
872
|
-
<RequestRow
|
|
929
|
+
<RequestRow
|
|
930
|
+
key={request.id}
|
|
931
|
+
request={request}
|
|
932
|
+
canManage={canManageVault}
|
|
933
|
+
/>
|
|
873
934
|
))}
|
|
874
935
|
{!requestsQuery.isError && (requests?.length || 0) === 0 && (
|
|
875
936
|
<div className="rounded-2xl border border-dashed px-6 py-12 text-center text-sm text-muted-foreground">
|
|
@@ -878,32 +939,34 @@ export default function VaultRoute() {
|
|
|
878
939
|
)}
|
|
879
940
|
</TabsContent>
|
|
880
941
|
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
942
|
+
{canManageVault ? (
|
|
943
|
+
<TabsContent value="audit" className="mt-4 space-y-2">
|
|
944
|
+
{auditQuery.isError ? (
|
|
945
|
+
<ActionQueryError
|
|
946
|
+
error={auditQuery.error}
|
|
947
|
+
onRetry={() => void auditQuery.refetch()}
|
|
948
|
+
/>
|
|
949
|
+
) : null}
|
|
950
|
+
{(audit || []).map((event: any) => (
|
|
951
|
+
<div
|
|
952
|
+
key={event.id}
|
|
953
|
+
className="rounded-xl border bg-muted/30 px-4 py-3"
|
|
954
|
+
>
|
|
955
|
+
<div className="text-sm font-medium text-foreground">
|
|
956
|
+
{event.summary}
|
|
957
|
+
</div>
|
|
958
|
+
<div className="mt-1 text-xs text-muted-foreground">
|
|
959
|
+
{event.actor} · {new Date(event.createdAt).toLocaleString()}
|
|
960
|
+
</div>
|
|
895
961
|
</div>
|
|
896
|
-
|
|
897
|
-
|
|
962
|
+
))}
|
|
963
|
+
{!auditQuery.isError && (audit?.length || 0) === 0 && (
|
|
964
|
+
<div className="rounded-2xl border border-dashed px-6 py-12 text-center text-sm text-muted-foreground">
|
|
965
|
+
No vault activity yet.
|
|
898
966
|
</div>
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
<div className="rounded-2xl border border-dashed px-6 py-12 text-center text-sm text-muted-foreground">
|
|
903
|
-
No vault activity yet.
|
|
904
|
-
</div>
|
|
905
|
-
)}
|
|
906
|
-
</TabsContent>
|
|
967
|
+
)}
|
|
968
|
+
</TabsContent>
|
|
969
|
+
) : null}
|
|
907
970
|
</Tabs>
|
|
908
971
|
</DispatchShell>
|
|
909
972
|
);
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
recordAudit,
|
|
28
28
|
resolveLinkedOwner,
|
|
29
29
|
} from "./dispatch-store.js";
|
|
30
|
-
import { createRequest,
|
|
30
|
+
import { createRequest, listSecretOptions } from "./vault-store.js";
|
|
31
31
|
import {
|
|
32
32
|
grantWorkspaceResourcesToApp,
|
|
33
33
|
listWorkspaceResourceOptions,
|
|
@@ -2013,7 +2013,7 @@ export async function startWorkspaceAppCreation(input: {
|
|
|
2013
2013
|
}
|
|
2014
2014
|
|
|
2015
2015
|
const selectedKeys = input.secretIds?.length
|
|
2016
|
-
? (await
|
|
2016
|
+
? (await listSecretOptions())
|
|
2017
2017
|
.filter((secret) => input.secretIds?.includes(secret.id))
|
|
2018
2018
|
.map((secret) => secret.credentialKey)
|
|
2019
2019
|
: [];
|