@agent-native/dispatch 0.40.5 → 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.
Files changed (41) hide show
  1. package/CHANGELOG.md +21 -28
  2. package/dist/actions/create-workspace-resource.d.ts +13 -13
  3. package/dist/components/ui/tooltip.d.ts +1 -7
  4. package/dist/components/ui/tooltip.d.ts.map +1 -1
  5. package/dist/components/ui/tooltip.js +1 -10
  6. package/dist/components/ui/tooltip.js.map +1 -1
  7. package/dist/lib/overview-chat.d.ts +1 -0
  8. package/dist/lib/overview-chat.d.ts.map +1 -1
  9. package/dist/lib/overview-chat.js +1 -0
  10. package/dist/lib/overview-chat.js.map +1 -1
  11. package/dist/routes/pages/chat.d.ts.map +1 -1
  12. package/dist/routes/pages/chat.js +1 -0
  13. package/dist/routes/pages/chat.js.map +1 -1
  14. package/dist/server/lib/admin-config.d.ts +3 -0
  15. package/dist/server/lib/admin-config.d.ts.map +1 -0
  16. package/dist/server/lib/admin-config.js +16 -0
  17. package/dist/server/lib/admin-config.js.map +1 -0
  18. package/dist/server/lib/app-creation-store.d.ts.map +1 -1
  19. package/dist/server/lib/app-creation-store.js +3 -2
  20. package/dist/server/lib/app-creation-store.js.map +1 -1
  21. package/dist/server/lib/dispatch-integrations.d.ts.map +1 -1
  22. package/dist/server/lib/dispatch-integrations.js +2 -1
  23. package/dist/server/lib/dispatch-integrations.js.map +1 -1
  24. package/dist/server/lib/thread-debug-store.d.ts.map +1 -1
  25. package/dist/server/lib/thread-debug-store.js +2 -15
  26. package/dist/server/lib/thread-debug-store.js.map +1 -1
  27. package/dist/server/lib/usage-metrics-store.d.ts.map +1 -1
  28. package/dist/server/lib/usage-metrics-store.js +5 -16
  29. package/dist/server/lib/usage-metrics-store.js.map +1 -1
  30. package/package.json +4 -4
  31. package/src/components/ui/tooltip.tsx +1 -28
  32. package/src/lib/overview-chat.spec.ts +2 -0
  33. package/src/lib/overview-chat.ts +2 -0
  34. package/src/routes/pages/chat.tsx +1 -0
  35. package/src/server/lib/admin-config.ts +16 -0
  36. package/src/server/lib/app-creation-store.ts +3 -2
  37. package/src/server/lib/dispatch-integrations.ts +2 -1
  38. package/src/server/lib/thread-debug-store.spec.ts +14 -2
  39. package/src/server/lib/thread-debug-store.ts +2 -17
  40. package/src/server/lib/usage-metrics-store.spec.ts +26 -0
  41. 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 = isEnvAdmin(viewerEmail);
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
- isEnvAdmin(viewerEmail) ||
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
- isEnvAdmin(viewerEmail) || role === "owner" || role === "admin",
973
+ isDispatchEnvironmentAdmin(viewerEmail) ||
974
+ role === "owner" ||
975
+ role === "admin",
989
976
  );
990
977
  if (
991
978
  viewScope === "app" &&