@agent-native/dispatch 0.40.6 → 0.40.7
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/CHANGELOG.md +13 -15
- package/dist/actions/create-workspace-resource.d.ts +13 -13
- package/dist/components/ui/tooltip.d.ts +1 -7
- package/dist/components/ui/tooltip.d.ts.map +1 -1
- package/dist/components/ui/tooltip.js +1 -10
- package/dist/components/ui/tooltip.js.map +1 -1
- package/dist/lib/overview-chat.d.ts +1 -0
- package/dist/lib/overview-chat.d.ts.map +1 -1
- package/dist/lib/overview-chat.js +1 -0
- package/dist/lib/overview-chat.js.map +1 -1
- package/dist/routes/pages/chat.d.ts.map +1 -1
- package/dist/routes/pages/chat.js +1 -0
- package/dist/routes/pages/chat.js.map +1 -1
- package/dist/server/lib/admin-config.d.ts +3 -0
- package/dist/server/lib/admin-config.d.ts.map +1 -0
- package/dist/server/lib/admin-config.js +16 -0
- package/dist/server/lib/admin-config.js.map +1 -0
- 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.map +1 -1
- package/dist/server/lib/dispatch-integrations.js +2 -1
- package/dist/server/lib/dispatch-integrations.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 -15
- package/dist/server/lib/thread-debug-store.js.map +1 -1
- package/dist/server/lib/usage-metrics-store.d.ts.map +1 -1
- package/dist/server/lib/usage-metrics-store.js +5 -16
- package/dist/server/lib/usage-metrics-store.js.map +1 -1
- package/package.json +4 -4
- package/src/components/ui/tooltip.tsx +1 -28
- package/src/lib/overview-chat.spec.ts +2 -0
- package/src/lib/overview-chat.ts +2 -0
- package/src/routes/pages/chat.tsx +1 -0
- package/src/server/lib/admin-config.ts +16 -0
- package/src/server/lib/app-creation-store.ts +3 -2
- package/src/server/lib/dispatch-integrations.ts +2 -1
- package/src/server/lib/thread-debug-store.spec.ts +14 -2
- package/src/server/lib/thread-debug-store.ts +2 -17
- package/src/server/lib/usage-metrics-store.spec.ts +26 -0
- package/src/server/lib/usage-metrics-store.ts +5 -18
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
import { createDbExec, getDbExec, type DbExec } from "@agent-native/core/db";
|
|
6
6
|
import { ForbiddenError } from "@agent-native/core/sharing";
|
|
7
7
|
|
|
8
|
+
import { isDispatchEnvironmentAdmin } from "./admin-config.js";
|
|
8
9
|
import { currentOrgId, currentOwnerEmail } from "./dispatch-store.js";
|
|
9
10
|
|
|
10
11
|
const CONFIG_ENV_KEY = "AGENT_NATIVE_THREAD_DEBUG_DATABASES";
|
|
@@ -108,26 +109,10 @@ class UnsupportedThreadDebugSchemaError extends Error {
|
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
111
|
|
|
111
|
-
function envEmails(name: string): string[] {
|
|
112
|
-
return (process.env[name] ?? "")
|
|
113
|
-
.split(",")
|
|
114
|
-
.map((value) => value.trim().toLowerCase())
|
|
115
|
-
.filter(Boolean);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
112
|
function escapeLike(value: string): string {
|
|
119
113
|
return value.replace(/([\\%_])/g, "\\$1");
|
|
120
114
|
}
|
|
121
115
|
|
|
122
|
-
function isEnvAdmin(email: string): boolean {
|
|
123
|
-
const normalized = email.trim().toLowerCase();
|
|
124
|
-
return [
|
|
125
|
-
...envEmails("DISPATCH_ADMIN_EMAILS"),
|
|
126
|
-
...envEmails("WORKSPACE_OWNER_EMAIL"),
|
|
127
|
-
...envEmails("DISPATCH_DEFAULT_OWNER_EMAIL"),
|
|
128
|
-
].includes(normalized);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
116
|
function missingTableName(error: unknown): string | null {
|
|
132
117
|
const message = String((error as Error)?.message ?? error);
|
|
133
118
|
const patterns = [
|
|
@@ -457,7 +442,7 @@ async function resolveDebugAccess(): Promise<DebugAccess> {
|
|
|
457
442
|
const viewerEmail = currentOwnerEmail();
|
|
458
443
|
const orgId = currentOrgId();
|
|
459
444
|
const role = await viewerOrgRole(orgId, viewerEmail);
|
|
460
|
-
const envAdmin =
|
|
445
|
+
const envAdmin = isDispatchEnvironmentAdmin(viewerEmail);
|
|
461
446
|
const canInspectAll = envAdmin || role === "owner" || role === "admin";
|
|
462
447
|
const memberEmails = canInspectAll
|
|
463
448
|
? await currentOrgMembers(orgId)
|
|
@@ -10,6 +10,11 @@ const mocks = vi.hoisted(() => ({
|
|
|
10
10
|
currentOrgId: vi.fn((): string | null => null),
|
|
11
11
|
currentOwnerEmail: vi.fn(() => "owner@example.test"),
|
|
12
12
|
registerBuiltinEngines: vi.fn(),
|
|
13
|
+
dispatchConfig: {
|
|
14
|
+
adminEmails: [] as string[],
|
|
15
|
+
workspaceOwnerEmails: [] as string[],
|
|
16
|
+
defaultOwnerEmail: undefined as string | undefined,
|
|
17
|
+
},
|
|
13
18
|
}));
|
|
14
19
|
|
|
15
20
|
vi.mock("@agent-native/core/agent/engine", () => ({
|
|
@@ -33,6 +38,10 @@ vi.mock("@agent-native/core/settings", () => ({
|
|
|
33
38
|
getSetting: (...args: any[]) => mocks.getSetting(...args),
|
|
34
39
|
}));
|
|
35
40
|
|
|
41
|
+
vi.mock("@agent-native/core/server", () => ({
|
|
42
|
+
getAppConfig: () => ({ dispatch: mocks.dispatchConfig }),
|
|
43
|
+
}));
|
|
44
|
+
|
|
36
45
|
vi.mock("@agent-native/core/usage", () => ({
|
|
37
46
|
builderCreditsFromCostCents: (cents: number) => cents / 4,
|
|
38
47
|
getUsageSummary: (...args: any[]) => mocks.getUsageSummary(...args),
|
|
@@ -75,6 +84,9 @@ afterEach(() => {
|
|
|
75
84
|
vi.clearAllMocks();
|
|
76
85
|
mocks.currentOrgId.mockReturnValue(null);
|
|
77
86
|
mocks.currentOwnerEmail.mockReturnValue("owner@example.test");
|
|
87
|
+
mocks.dispatchConfig.adminEmails = [];
|
|
88
|
+
mocks.dispatchConfig.workspaceOwnerEmails = [];
|
|
89
|
+
mocks.dispatchConfig.defaultOwnerEmail = undefined;
|
|
78
90
|
});
|
|
79
91
|
|
|
80
92
|
describe("listDispatchUsageMetrics", () => {
|
|
@@ -95,6 +107,20 @@ describe("listDispatchUsageMetrics", () => {
|
|
|
95
107
|
expect(mocks.listWorkspaceApps).not.toHaveBeenCalled();
|
|
96
108
|
});
|
|
97
109
|
|
|
110
|
+
it("allows a configured deployment admin to view workspace metrics", async () => {
|
|
111
|
+
mocks.currentOrgId.mockReturnValue("org-a");
|
|
112
|
+
mocks.currentOwnerEmail.mockReturnValue("owner@example.test");
|
|
113
|
+
mocks.dispatchConfig.workspaceOwnerEmails = ["Owner@Example.Test"];
|
|
114
|
+
mocks.execute.mockResolvedValue({ rows: [{ role: "member" }] });
|
|
115
|
+
mocks.getUsageSummary.mockResolvedValue(null);
|
|
116
|
+
mocks.listWorkspaceApps.mockResolvedValue([]);
|
|
117
|
+
|
|
118
|
+
const metrics = await listDispatchUsageMetrics({ sinceDays: 30 });
|
|
119
|
+
|
|
120
|
+
expect(metrics.access.viewerEmail).toBe("owner@example.test");
|
|
121
|
+
expect(metrics.access.role).toBe("member");
|
|
122
|
+
});
|
|
123
|
+
|
|
98
124
|
it("returns empty metrics when usage storage bootstrap and reads fail", async () => {
|
|
99
125
|
mocks.getUsageSummary.mockRejectedValue(
|
|
100
126
|
new Error("could not serialize access due to concurrent update"),
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
type UsageBillingMode,
|
|
19
19
|
} from "@agent-native/core/usage";
|
|
20
20
|
|
|
21
|
+
import { isDispatchEnvironmentAdmin } from "./admin-config.js";
|
|
21
22
|
import {
|
|
22
23
|
listWorkspaceApps,
|
|
23
24
|
type WorkspaceAppSummary,
|
|
@@ -373,22 +374,6 @@ function appOwner(app: WorkspaceAppSummary): string | null {
|
|
|
373
374
|
return owner || null;
|
|
374
375
|
}
|
|
375
376
|
|
|
376
|
-
function envEmails(name: string): string[] {
|
|
377
|
-
return (process.env[name] ?? "")
|
|
378
|
-
.split(",")
|
|
379
|
-
.map((value) => value.trim().toLowerCase())
|
|
380
|
-
.filter(Boolean);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
function isEnvAdmin(email: string): boolean {
|
|
384
|
-
const normalized = email.trim().toLowerCase();
|
|
385
|
-
return [
|
|
386
|
-
...envEmails("DISPATCH_ADMIN_EMAILS"),
|
|
387
|
-
...envEmails("WORKSPACE_OWNER_EMAIL"),
|
|
388
|
-
...envEmails("DISPATCH_DEFAULT_OWNER_EMAIL"),
|
|
389
|
-
].includes(normalized);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
377
|
async function detectUsageEngineName(): Promise<string | null> {
|
|
393
378
|
try {
|
|
394
379
|
const stored = (await readDefaultAgentEngineSetting()) as {
|
|
@@ -939,7 +924,7 @@ async function assertCanViewMetrics(viewScope: UsageMetricsScope): Promise<{
|
|
|
939
924
|
const role = await getViewerOrgRole(orgId, viewerEmail);
|
|
940
925
|
if (
|
|
941
926
|
viewScope === "me" ||
|
|
942
|
-
|
|
927
|
+
isDispatchEnvironmentAdmin(viewerEmail) ||
|
|
943
928
|
role === "owner" ||
|
|
944
929
|
role === "admin"
|
|
945
930
|
) {
|
|
@@ -985,7 +970,9 @@ export async function listDispatchUsageMetrics(input: {
|
|
|
985
970
|
}
|
|
986
971
|
const selectedAppOwner = selectedApp ? appOwner(selectedApp) : null;
|
|
987
972
|
const isMetricsAdmin = Boolean(
|
|
988
|
-
|
|
973
|
+
isDispatchEnvironmentAdmin(viewerEmail) ||
|
|
974
|
+
role === "owner" ||
|
|
975
|
+
role === "admin",
|
|
989
976
|
);
|
|
990
977
|
if (
|
|
991
978
|
viewScope === "app" &&
|