@agent-native/dispatch 0.14.2 → 0.14.4

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 (138) hide show
  1. package/README.md +114 -122
  2. package/dist/actions/approve-dispatch-change.d.ts +10 -10
  3. package/dist/actions/list_apps.d.ts +1 -0
  4. package/dist/actions/list_apps.js +16 -8
  5. package/dist/actions/list_apps.js.map +1 -1
  6. package/dist/actions/provider-api-register.js +64 -2
  7. package/dist/actions/provider-api-register.js.map +1 -1
  8. package/dist/actions/reject-dispatch-change.d.ts +10 -10
  9. package/dist/actions/set-mcp-app-access.js +1 -4
  10. package/dist/actions/set-mcp-app-access.js.map +1 -1
  11. package/dist/components/action-query-error.d.ts +8 -0
  12. package/dist/components/action-query-error.d.ts.map +1 -0
  13. package/dist/components/action-query-error.js +10 -0
  14. package/dist/components/action-query-error.js.map +1 -0
  15. package/dist/components/app-keys-popover.d.ts +4 -0
  16. package/dist/components/app-keys-popover.d.ts.map +1 -1
  17. package/dist/components/app-keys-popover.js +26 -14
  18. package/dist/components/app-keys-popover.js.map +1 -1
  19. package/dist/components/dispatch-control-plane.d.ts.map +1 -1
  20. package/dist/components/dispatch-control-plane.js +31 -27
  21. package/dist/components/dispatch-control-plane.js.map +1 -1
  22. package/dist/components/index.d.ts +1 -0
  23. package/dist/components/index.d.ts.map +1 -1
  24. package/dist/components/index.js +1 -0
  25. package/dist/components/index.js.map +1 -1
  26. package/dist/components/layout/Layout.d.ts +1 -0
  27. package/dist/components/layout/Layout.d.ts.map +1 -1
  28. package/dist/components/layout/Layout.js +19 -21
  29. package/dist/components/layout/Layout.js.map +1 -1
  30. package/dist/components/ui/sheet.js +1 -1
  31. package/dist/components/ui/sheet.js.map +1 -1
  32. package/dist/components/ui/toast.js +1 -1
  33. package/dist/components/ui/toast.js.map +1 -1
  34. package/dist/components/workspace-app-card.d.ts.map +1 -1
  35. package/dist/components/workspace-app-card.js +8 -5
  36. package/dist/components/workspace-app-card.js.map +1 -1
  37. package/dist/components/workspace-resource-effective-stack.d.ts.map +1 -1
  38. package/dist/components/workspace-resource-effective-stack.js +6 -1
  39. package/dist/components/workspace-resource-effective-stack.js.map +1 -1
  40. package/dist/components/workspace-resource-impact-preview.d.ts.map +1 -1
  41. package/dist/components/workspace-resource-impact-preview.js +6 -1
  42. package/dist/components/workspace-resource-impact-preview.js.map +1 -1
  43. package/dist/routes/pages/$appId.d.ts.map +1 -1
  44. package/dist/routes/pages/$appId.js +6 -1
  45. package/dist/routes/pages/$appId.js.map +1 -1
  46. package/dist/routes/pages/agents.d.ts.map +1 -1
  47. package/dist/routes/pages/agents.js +5 -8
  48. package/dist/routes/pages/agents.js.map +1 -1
  49. package/dist/routes/pages/approval.d.ts.map +1 -1
  50. package/dist/routes/pages/approval.js +6 -1
  51. package/dist/routes/pages/approval.js.map +1 -1
  52. package/dist/routes/pages/approvals.d.ts.map +1 -1
  53. package/dist/routes/pages/approvals.js +8 -5
  54. package/dist/routes/pages/approvals.js.map +1 -1
  55. package/dist/routes/pages/apps.$appId.d.ts.map +1 -1
  56. package/dist/routes/pages/apps.$appId.js +4 -2
  57. package/dist/routes/pages/apps.$appId.js.map +1 -1
  58. package/dist/routes/pages/apps.d.ts.map +1 -1
  59. package/dist/routes/pages/apps.js +9 -3
  60. package/dist/routes/pages/apps.js.map +1 -1
  61. package/dist/routes/pages/audit.d.ts.map +1 -1
  62. package/dist/routes/pages/audit.js +4 -2
  63. package/dist/routes/pages/audit.js.map +1 -1
  64. package/dist/routes/pages/chat.d.ts.map +1 -1
  65. package/dist/routes/pages/chat.js +16 -2
  66. package/dist/routes/pages/chat.js.map +1 -1
  67. package/dist/routes/pages/destinations.d.ts.map +1 -1
  68. package/dist/routes/pages/destinations.js +5 -3
  69. package/dist/routes/pages/destinations.js.map +1 -1
  70. package/dist/routes/pages/identities.d.ts.map +1 -1
  71. package/dist/routes/pages/identities.js +5 -3
  72. package/dist/routes/pages/identities.js.map +1 -1
  73. package/dist/routes/pages/integrations.d.ts.map +1 -1
  74. package/dist/routes/pages/integrations.js +9 -3
  75. package/dist/routes/pages/integrations.js.map +1 -1
  76. package/dist/routes/pages/thread-debug.d.ts.map +1 -1
  77. package/dist/routes/pages/thread-debug.js +7 -6
  78. package/dist/routes/pages/thread-debug.js.map +1 -1
  79. package/dist/routes/pages/vault.d.ts.map +1 -1
  80. package/dist/routes/pages/vault.js +23 -7
  81. package/dist/routes/pages/vault.js.map +1 -1
  82. package/dist/routes/pages/workspace.d.ts.map +1 -1
  83. package/dist/routes/pages/workspace.js +9 -3
  84. package/dist/routes/pages/workspace.js.map +1 -1
  85. package/dist/server/lib/dispatch-store.d.ts +24 -21
  86. package/dist/server/lib/dispatch-store.d.ts.map +1 -1
  87. package/dist/server/lib/dispatch-store.js +59 -23
  88. package/dist/server/lib/dispatch-store.js.map +1 -1
  89. package/dist/server/lib/mcp-access-store.d.ts.map +1 -1
  90. package/dist/server/lib/mcp-access-store.js +30 -2
  91. package/dist/server/lib/mcp-access-store.js.map +1 -1
  92. package/dist/server/lib/vault-store.d.ts.map +1 -1
  93. package/dist/server/lib/vault-store.js +102 -37
  94. package/dist/server/lib/vault-store.js.map +1 -1
  95. package/dist/server/plugins/agent-chat.d.ts +1 -1
  96. package/package.json +2 -3
  97. package/src/actions/list_apps.spec.ts +52 -0
  98. package/src/actions/list_apps.ts +16 -8
  99. package/src/actions/provider-api-register.spec.ts +193 -0
  100. package/src/actions/provider-api-register.ts +66 -1
  101. package/src/actions/set-mcp-app-access.spec.ts +73 -0
  102. package/src/actions/set-mcp-app-access.ts +1 -5
  103. package/src/components/action-query-error.spec.tsx +56 -0
  104. package/src/components/action-query-error.tsx +32 -0
  105. package/src/components/app-keys-popover.spec.tsx +85 -0
  106. package/src/components/app-keys-popover.tsx +76 -50
  107. package/src/components/dispatch-control-plane.spec.tsx +106 -0
  108. package/src/components/dispatch-control-plane.tsx +71 -115
  109. package/src/components/index.ts +1 -0
  110. package/src/components/layout/Layout.spec.tsx +42 -2
  111. package/src/components/layout/Layout.tsx +22 -27
  112. package/src/components/ui/sheet.tsx +1 -1
  113. package/src/components/ui/toast.tsx +1 -1
  114. package/src/components/workspace-app-card.spec.tsx +80 -0
  115. package/src/components/workspace-app-card.tsx +36 -25
  116. package/src/components/workspace-resource-effective-stack.tsx +13 -1
  117. package/src/components/workspace-resource-impact-preview.tsx +12 -1
  118. package/src/routes/pages/$appId.tsx +17 -1
  119. package/src/routes/pages/agents.tsx +24 -11
  120. package/src/routes/pages/approval.tsx +16 -4
  121. package/src/routes/pages/approvals.tsx +108 -89
  122. package/src/routes/pages/apps.$appId.tsx +9 -2
  123. package/src/routes/pages/apps.tsx +20 -7
  124. package/src/routes/pages/audit.tsx +29 -20
  125. package/src/routes/pages/chat.spec.tsx +94 -0
  126. package/src/routes/pages/chat.tsx +30 -10
  127. package/src/routes/pages/destinations.tsx +75 -62
  128. package/src/routes/pages/identities.tsx +74 -63
  129. package/src/routes/pages/integrations.tsx +17 -10
  130. package/src/routes/pages/thread-debug.tsx +18 -12
  131. package/src/routes/pages/vault.tsx +59 -26
  132. package/src/routes/pages/workspace.tsx +69 -55
  133. package/src/server/lib/approval-fencing.spec.ts +420 -0
  134. package/src/server/lib/dispatch-store.ts +94 -21
  135. package/src/server/lib/mcp-access-store.spec.ts +116 -4
  136. package/src/server/lib/mcp-access-store.ts +37 -2
  137. package/src/server/lib/mcp-gateway.spec.ts +79 -3
  138. package/src/server/lib/vault-store.ts +151 -45
package/README.md CHANGED
@@ -1,140 +1,132 @@
1
- # Agent-Native
1
+ # @agent-native/dispatch
2
+
3
+ Workspace control plane for agent-native apps — vault, integrations,
4
+ destinations, cross-app workspace resources, recurring "dream" report jobs,
5
+ and cross-app delegation, shipped as a single drop-in package.
6
+
7
+ Powers the `dispatch` template. Provides:
8
+
9
+ - **Drizzle schemas** — destinations, identity links, link tokens, approval
10
+ requests, audit events, dream reports/proposals, vault secrets/grants/
11
+ requests/audit, workspace resources/grants
12
+ - **Server layer** — `setupDispatch(config)` plus Nitro plugins for auth,
13
+ integrations, agent chat, DB, and core routes
14
+ - **Actions** — ~90 `defineAction` modules (vault grants/requests, workspace
15
+ resource grants, destinations, dream jobs, provider-api catalog/docs/
16
+ request, connected-agent discovery, audit/approvals, platform messaging,
17
+ and more) consumed as agent tools + HTTP endpoints
18
+ - **Routes** — a full React Router 7 `RouteConfig[]` (chat, overview, apps,
19
+ vault, integrations, agents, workspace, messaging, destinations,
20
+ identities, approvals, automations, audit, settings, dreams, extensions,
21
+ ...) to splat into a consumer's `app/routes.ts`
22
+ - **React components** — `DispatchShell`, `Layout`/`NavContent`,
23
+ `CreateAppPopover`/`CreateAppFlow`, `AppKeysPopover`, plus a full
24
+ shadcn/ui-based `components/ui/*` primitive set
25
+ - **Styles** — `dispatch.css` Tailwind layer
26
+
27
+ ## Install
2
28
 
3
- ## The framework for agentic apps
4
-
5
- Agent-Native is an open-source framework for rapidly building robust applications with agents at their core.
6
-
7
- ```ts
8
- // One action powers every app surface: UI, agent, HTTP, MCP, A2A, and CLI.
9
- export default defineAction({
10
- schema: z.object({
11
- emailId: z.string(),
12
- body: z.string(),
13
- }),
14
- run: async ({ emailId, body }) => {
15
- await db.insert(replies).values({ emailId, body });
16
- },
17
- });
29
+ ```bash
30
+ pnpm add @agent-native/dispatch
18
31
  ```
19
32
 
20
- - **[Actions](https://agent-native.com/docs/actions)**: Define work once. Use it from every app surface: UI, agent, HTTP, MCP, A2A, and CLI.
21
- - **[Agent runtime](https://agent-native.com/docs/agent-surfaces)**: Chat, tools, skills, memory, jobs, observability, and handoffs ship together.
22
- - **[Backend agnostic](https://agent-native.com/docs/database)**: Plug in any Drizzle-supported SQL database and Nitro-compatible host.
23
- - **[Toolkits](https://agent-native.com/docs/agent-native-toolkit)**: Reusable building blocks for collaboration, sharing, settings, teams, and observability.
24
-
25
- ## Don't pick between apps or agents.
26
-
27
- Agent-native apps are both
28
-
29
- | | SaaS Tools | Raw AI Agents | Internal Tools | Agent-Native App |
30
- | ----------------- | ------------------ | ----------------------- | -------------------------- | ----------------------- |
31
- | **UI** | Polished but rigid | None | Mixed quality | Full UI, fork & go |
32
- | **AI** | Bolted on | Powerful | Shallowly connected | Agent-first, integrated |
33
- | **Customization** | Can't | Instructions and skills | Full, but high maintenance | Agent modifies the app |
34
- | **Ownership** | Rented | Somewhat yours | You own the code | You own the code |
35
-
36
- ## Try an Agent-Native app
37
-
38
- Fork a working app and let the agent evolve it. **You can customize everything.**
39
-
40
- <table>
41
- <tr>
42
- <td width="33%" align="center" valign="top">
43
-
44
- **Clips**
45
-
46
- <a href="https://agent-native.com/templates/clips"><img src="https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2F189ebd9b2f2b4f0ead3b33138d4e4c10?format=webp&width=800" alt="Clips app" width="100%" /></a>
47
-
48
- **Agent-Native Loom**
49
-
50
- Record your screen with auto-transcripts and captured browser debug logs, share a link, and let an agent read the transcript, see timestamped frames, and fix the bug.
51
-
52
- </td>
53
- <td width="33%" align="center" valign="top">
54
-
55
- **Plans**
56
-
57
- <a href="https://agent-native.com/templates/plan"><img src="https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2Fefc6a3ac908149fa92e2b9392c0bb372?format=webp&width=800" alt="Plans app" width="100%" /></a>
58
-
59
- **Visual plan mode for coding agents**
60
-
61
- Install `/visual-plan` and `/visual-recap` so your coding agent can plan before it builds and recap changes after they land. High-level code reviews with diagrams, wireframes, annotations, and review links.
62
-
63
- </td>
64
- <td width="33%" align="center" valign="top">
65
-
66
- **Design**
67
-
68
- <a href="https://agent-native.com/templates/design"><img src="https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2Fe2c86908c2fa4f119ee4aa90b4823944?format=webp&width=800" alt="Design app" width="100%" /></a>
33
+ Peer-depends on `@agent-native/core` (`>=0.8.0`), `react`/`react-dom`
34
+ (`>=19.2.7`), and `react-router` (`>=8`).
69
35
 
70
- **Agent-Native Figma**
36
+ ## Compose
71
37
 
72
- Generate interactive HTML prototypes, compare variants, refine controls, and export the result.
38
+ Template's `app/routes.ts`:
73
39
 
74
- </td>
75
- </tr>
76
- <tr>
77
- <td width="33%" align="center" valign="top">
78
-
79
- **Content**
80
-
81
- <a href="https://agent-native.com/templates/content"><img src="https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2F89bcfc6106304bfbaf8ec8a7ccd721eb?format=webp&width=800" alt="Content app" width="100%" /></a>
82
-
83
- **Agent-Native Notion/Obsidian**
84
-
85
- Edit local Markdown/MDX files, generate rich interactive custom blocks, and draft, rewrite, or publish with an agent.
86
-
87
- </td>
88
- <td width="33%" align="center" valign="top">
89
-
90
- **Analytics**
91
-
92
- <a href="https://agent-native.com/templates/analytics"><img src="https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2F4933a80cc3134d7e874631f688be828a?format=webp&width=800" alt="Analytics app" width="100%" /></a>
93
-
94
- **Agent-Native Amplitude, Fullstory**
95
-
96
- Connect analytics data sources, prompt for real charts, and build reusable dashboards.
97
-
98
- </td>
99
- <td width="33%" align="center" valign="top">
100
-
101
- **Chat**
40
+ ```ts
41
+ import { type RouteConfig } from "@react-router/dev/routes";
42
+ import { dispatchRoutes } from "@agent-native/dispatch/routes";
102
43
 
103
- <a href="https://agent-native.com/templates/chat"><img src="https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2F6b36dc596fca4799815fa34c31e1c406?format=webp&width=800" alt="Chat app" width="100%" /></a>
44
+ export default [
45
+ ...localRoutes, // consumer's own routes win on collision
46
+ ...dispatchRoutes, // dispatch fills in everything else
47
+ ] satisfies RouteConfig;
48
+ ```
104
49
 
105
- **A minimal ChatGPT-style app for your own agent**
50
+ Template's `server/plugins/*.ts` (each export lives in its own file so Nitro
51
+ auto-loads it):
106
52
 
107
- Chat-first app scaffold with durable threads, actions, auth, live sync, and a clean path to add screens or plug in your own agent backend.
53
+ ```ts
54
+ // server/plugins/setup-dispatch.ts
55
+ import { setupDispatch } from "@agent-native/dispatch/server";
56
+ export default setupDispatch({ auth: { googleOnly: true } });
108
57
 
109
- </td>
110
- </tr>
111
- </table>
58
+ // server/plugins/auth.ts
59
+ export { dispatchAuthPlugin as default } from "@agent-native/dispatch/server";
112
60
 
113
- View the full app gallery at **[agent-native.com/apps](https://agent-native.com/apps)**, or build from scratch with the **[framework guide](https://agent-native.com/docs/getting-started)**.
61
+ // server/plugins/integrations.ts
62
+ export { dispatchIntegrationsPlugin as default } from "@agent-native/dispatch/server";
114
63
 
115
- ## Quick Start
64
+ // server/plugins/agent-chat.ts
65
+ export { dispatchAgentChatPlugin as default } from "@agent-native/dispatch/server";
116
66
 
117
- One command to start a new app locally.
67
+ // server/plugins/db.ts
68
+ export { dispatchDbPlugin as default } from "@agent-native/dispatch/server";
118
69
 
119
- ```bash
120
- npx @agent-native/core@latest create my-app
121
- cd my-app
122
- pnpm install
123
- pnpm dev
70
+ // server/plugins/core-routes.ts
71
+ export { dispatchCoreRoutesPlugin as default } from "@agent-native/dispatch/server";
124
72
  ```
125
73
 
126
- Prefer flags? `create my-app --template mail`, `--headless`, or `--standalone` skip the prompt.
127
-
128
- See the full [getting started docs](https://agent-native.com/docs).
129
-
130
- ## Community
74
+ Template's `server/db/schema.ts`:
131
75
 
132
- Join the **[Discord](https://discord.gg/qm82StQ2NC)** to ask questions, share what you're building, and get help.
133
-
134
- ## Docs
135
-
136
- Full documentation at **[agent-native.com](https://agent-native.com)**.
137
-
138
- ## License
76
+ ```ts
77
+ export * from "@agent-native/dispatch/db";
78
+ ```
139
79
 
140
- MIT
80
+ `@agent-native/dispatch/actions` doesn't need a manual import for most
81
+ consumers: `import "@agent-native/dispatch/server"` (pulled in by the plugin
82
+ wiring above) side-effect-registers every Dispatch action via
83
+ `registerPackageActions`, and the framework's `autoDiscoverActions` merges
84
+ them in after the consumer's local `actions/` directory. Drop a same-named
85
+ file in the consumer's own `actions/` to override any single action.
86
+
87
+ ## Exports
88
+
89
+ | Subpath | What it is |
90
+ | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
91
+ | `.` | Shared config types only — `DispatchConfig`, `DispatchAuthConfig`, `DispatchIntegrationsConfig`. Import this where a type is needed without pulling in server/client code. |
92
+ | `./routes` | `dispatchRoutes` — a programmatic `RouteConfig[]` covering every Dispatch page (chat, overview, metrics, operations, apps, vault, integrations, agents, workspace, messaging, destinations, identities, approvals, automations, audit, settings, dreams, extensions, thread-debug, team, and a workspace-app catch-all route). |
93
+ | `./routes/pages/*` | Individual compiled page modules the routes above point at (e.g. `./routes/pages/vault.js`). Not normally imported directly. |
94
+ | `./server` | `setupDispatch(config)` plus the Nitro plugin set: `dispatchAuthPlugin`, `dispatchIntegrationsPlugin`, `dispatchAgentChatPlugin`, `dispatchDbPlugin`, `dispatchCoreRoutesPlugin`. Importing this module also side-effect-registers all Dispatch actions. |
95
+ | `./server/lib/thread-link-preview` | `loadThreadLinkPreview(threadId)` — server-only helper (reads request context and checks thread ownership) that builds link-preview metadata for shared thread URLs. |
96
+ | `./actions` | `dispatchActions` — the flat name → `ActionEntry` map of every Dispatch action (vault, workspace resources, destinations, dream jobs, provider-api catalog/docs/request, connected agents, audit, approvals, platform messaging, and more). |
97
+ | `./db` | `getDb()` / `db()`, the Drizzle `schema` namespace, the Dispatch table exports (`dispatchDestinations`, `dispatchIdentityLinks`, `dispatchLinkTokens`, `dispatchApprovalRequests`, `dispatchAuditEvents`, `dispatchDreams`, `dispatchDreamProposals`, `vaultSecrets`, `vaultGrants`, `vaultRequests`, `vaultAuditLog`, `workspaceResources`, `workspaceResourceGrants`), and migrations. |
98
+ | `./lib/thread-link-preview` | Isomorphic helpers behind the server one above — `extractThreadPreviewImageUrl`, `buildThreadLinkPreviewMeta`, and the `ThreadLinkPreview` type. Safe to import from client code. |
99
+ | `./components` | Escape-hatch UI pieces for custom layouts: `DispatchShell`, `Layout`/`NavContent` (plus `DispatchExtensionConfig`/`DispatchNavIcon`/`DispatchNavItem`/`DispatchNavSection` types), `CreateAppPopover`/`CreateAppFlow`, `AppKeysPopover`. Most consumers only need `dispatchRoutes` and never import from here directly. |
100
+ | `./components/ui/*` | The shadcn/ui primitive set Dispatch's own UI is built on (`button`, `dialog`, `dropdown-menu`, `card`, `command`, `calendar`, `chart`, ...), exported per-file so a consumer can reuse one primitive without pulling in the rest. |
101
+ | `./styles/dispatch.css` | Tailwind layer with Dispatch-specific styles. Import once in the consumer's global CSS entry. |
102
+
103
+ ## What it's for
104
+
105
+ Dispatch is the workspace control plane sitting above individual template
106
+ apps:
107
+
108
+ - **Vault** — shared workspace secrets with a request → grant → audit flow,
109
+ scoped per app.
110
+ - **Workspace resources** — cross-app resources (e.g. shared instructions or
111
+ data) with explicit per-app grants and effective-context resolution.
112
+ - **Integrations & destinations** — outbound messaging channel setup (Slack,
113
+ Teams, Discord, Telegram, WhatsApp, email, and more) and a destinations
114
+ queue for delivering agent output.
115
+ - **Dream jobs** — a recurring background job (`ensure-dream-job`) that scans
116
+ agent thread-debug history across connected sources and produces pending,
117
+ evidence-backed proposals (`create-dream-report`, `list-dream-candidates`,
118
+ `apply-dream-proposal` / `reject-dream-proposal`) without an extra LLM call.
119
+ - **Connected agents & provider APIs** — discovery of other workspace apps
120
+ (`list-connected-agents`, `open_app`, `ask_app`) and a generic provider-api
121
+ catalog/docs/request surface for calling third-party APIs through stored
122
+ credentials.
123
+ - **Approvals & audit** — a shared approval-request/audit-event model used
124
+ across the above surfaces.
125
+
126
+ ## Eject
127
+
128
+ There is no `agent-native.package.json` / CLI package-lifecycle wiring for
129
+ this package yet (unlike `@agent-native/scheduling`). To customize beyond the
130
+ `setupDispatch` config surface, copy the files you want to own out of
131
+ `node_modules/@agent-native/dispatch/src` into your own package and swap the
132
+ dependency manually.
@@ -1,21 +1,21 @@
1
1
  declare const _default: import("@agent-native/core").ActionDefinition<{
2
2
  id: string;
3
3
  }, {
4
+ afterValue: string | null;
5
+ beforeValue: string | null;
6
+ changeType: string;
7
+ createdAt: number;
4
8
  id: string;
5
- ownerEmail: string;
6
9
  orgId: string | null;
7
- changeType: string;
8
- targetType: string;
9
- targetId: string | null;
10
- status: string;
11
- summary: string;
10
+ ownerEmail: string;
12
11
  payload: string;
13
- beforeValue: string | null;
14
- afterValue: string | null;
15
12
  requestedBy: string;
16
- reviewedBy: string | null;
17
13
  reviewedAt: number | null;
18
- createdAt: number;
14
+ reviewedBy: string | null;
15
+ status: string;
16
+ summary: string;
17
+ targetId: string | null;
18
+ targetType: string;
19
19
  updatedAt: number;
20
20
  }>;
21
21
  export default _default;
@@ -9,6 +9,7 @@ declare const _default: import("@agent-native/core").ActionDefinition<Record<str
9
9
  running: boolean;
10
10
  source: string;
11
11
  }[];
12
+ message: string;
12
13
  }>;
13
14
  export default _default;
14
15
  //# sourceMappingURL=list_apps.d.ts.map
@@ -9,17 +9,25 @@ export default defineAction({
9
9
  parallelSafe: true,
10
10
  run: async () => {
11
11
  const apps = await listGrantedDispatchMcpApps();
12
+ const appSummaries = apps.map((app) => ({
13
+ id: app.id,
14
+ name: app.name,
15
+ description: app.description,
16
+ url: app.url,
17
+ running: true,
18
+ source: "dispatch-mcp-grant",
19
+ }));
12
20
  return {
13
21
  workspace: true,
14
22
  gateway: "dispatch",
15
- apps: apps.map((app) => ({
16
- id: app.id,
17
- name: app.name,
18
- description: app.description,
19
- url: app.url,
20
- running: true,
21
- source: "dispatch-mcp-grant",
22
- })),
23
+ apps: appSummaries,
24
+ // MCP model-visible results are deliberately text-only unless a tool is
25
+ // app-only or opens an MCP App. Supplying a compact, valid JSON message
26
+ // keeps large app catalogs machine-readable instead of letting the
27
+ // generic text limiter cut the full result into invalid JSON.
28
+ message: JSON.stringify({
29
+ apps: appSummaries.map(({ id, name }) => ({ id, name })),
30
+ }),
23
31
  };
24
32
  },
25
33
  });
@@ -1 +1 @@
1
- {"version":3,"file":"list_apps.js","sourceRoot":"","sources":["../../src/actions/list_apps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAE1E,eAAe,YAAY,CAAC;IAC1B,WAAW,EACT,6LAA6L;IAC/L,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;IACpB,IAAI,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;IACvB,QAAQ,EAAE,IAAI;IACd,YAAY,EAAE,IAAI;IAClB,GAAG,EAAE,KAAK,IAAI,EAAE;QACd,MAAM,IAAI,GAAG,MAAM,0BAA0B,EAAE,CAAC;QAChD,OAAO;YACL,SAAS,EAAE,IAAI;YACf,OAAO,EAAE,UAAU;YACnB,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;gBACvB,EAAE,EAAE,GAAG,CAAC,EAAE;gBACV,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,WAAW,EAAE,GAAG,CAAC,WAAW;gBAC5B,GAAG,EAAE,GAAG,CAAC,GAAG;gBACZ,OAAO,EAAE,IAAI;gBACb,MAAM,EAAE,oBAAoB;aAC7B,CAAC,CAAC;SACJ,CAAC;IACJ,CAAC;CACF,CAAC,CAAC","sourcesContent":["import { defineAction } from \"@agent-native/core\";\nimport { z } from \"zod\";\n\nimport { listGrantedDispatchMcpApps } from \"../server/lib/mcp-gateway.js\";\n\nexport default defineAction({\n description:\n 'List the apps this Dispatch MCP gateway can route to, including \"dispatch\" itself for Dispatch-owned pages such as extensions. The result is filtered by Dispatch\\'s MCP app access policy.',\n schema: z.object({}),\n http: { method: \"GET\" },\n readOnly: true,\n parallelSafe: true,\n run: async () => {\n const apps = await listGrantedDispatchMcpApps();\n return {\n workspace: true,\n gateway: \"dispatch\",\n apps: apps.map((app) => ({\n id: app.id,\n name: app.name,\n description: app.description,\n url: app.url,\n running: true,\n source: \"dispatch-mcp-grant\",\n })),\n };\n },\n});\n"]}
1
+ {"version":3,"file":"list_apps.js","sourceRoot":"","sources":["../../src/actions/list_apps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAE1E,eAAe,YAAY,CAAC;IAC1B,WAAW,EACT,6LAA6L;IAC/L,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;IACpB,IAAI,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;IACvB,QAAQ,EAAE,IAAI;IACd,YAAY,EAAE,IAAI;IAClB,GAAG,EAAE,KAAK,IAAI,EAAE;QACd,MAAM,IAAI,GAAG,MAAM,0BAA0B,EAAE,CAAC;QAChD,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YACtC,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,GAAG,EAAE,GAAG,CAAC,GAAG;YACZ,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,oBAAoB;SAC7B,CAAC,CAAC,CAAC;QACJ,OAAO;YACL,SAAS,EAAE,IAAI;YACf,OAAO,EAAE,UAAU;YACnB,IAAI,EAAE,YAAY;YAClB,wEAAwE;YACxE,wEAAwE;YACxE,mEAAmE;YACnE,8DAA8D;YAC9D,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC;gBACtB,IAAI,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;aACzD,CAAC;SACH,CAAC;IACJ,CAAC;CACF,CAAC,CAAC","sourcesContent":["import { defineAction } from \"@agent-native/core\";\nimport { z } from \"zod\";\n\nimport { listGrantedDispatchMcpApps } from \"../server/lib/mcp-gateway.js\";\n\nexport default defineAction({\n description:\n 'List the apps this Dispatch MCP gateway can route to, including \"dispatch\" itself for Dispatch-owned pages such as extensions. The result is filtered by Dispatch\\'s MCP app access policy.',\n schema: z.object({}),\n http: { method: \"GET\" },\n readOnly: true,\n parallelSafe: true,\n run: async () => {\n const apps = await listGrantedDispatchMcpApps();\n const appSummaries = apps.map((app) => ({\n id: app.id,\n name: app.name,\n description: app.description,\n url: app.url,\n running: true,\n source: \"dispatch-mcp-grant\",\n }));\n return {\n workspace: true,\n gateway: \"dispatch\",\n apps: appSummaries,\n // MCP model-visible results are deliberately text-only unless a tool is\n // app-only or opens an MCP App. Supplying a compact, valid JSON message\n // keeps large app catalogs machine-readable instead of letting the\n // generic text limiter cut the full result into invalid JSON.\n message: JSON.stringify({\n apps: appSummaries.map(({ id, name }) => ({ id, name })),\n }),\n };\n },\n});\n"]}
@@ -1,7 +1,42 @@
1
1
  import { defineAction } from "@agent-native/core";
2
- import { upsertCustomProvider, deleteCustomProvider, listCustomProviders, getCustomProvider, } from "@agent-native/core/provider-api";
2
+ import { getDbExec } from "@agent-native/core/db";
3
+ import { upsertCustomProvider, deleteCustomProvider, listCustomProviders, getCustomProvider, assertCanMutateCustomProviderScope, } from "@agent-native/core/provider-api";
3
4
  import { getCredentialContext } from "@agent-native/core/server";
4
5
  import { z } from "zod";
6
+ /**
7
+ * Resolve the caller's role in a specific org, straight from `org_members`.
8
+ *
9
+ * `getCredentialContext()` (from `@agent-native/core/server`) only exposes
10
+ * `{ userEmail, orgId }` — no role. `getOrgContext()` (from
11
+ * `@agent-native/core/org`) resolves role but requires an `H3Event`, which
12
+ * `defineAction` handlers are not given. This mirrors the established
13
+ * no-event role-lookup idiom already used for org-admin gating inside
14
+ * agent-callable/background code (see `isCurrentUserOrgAdmin` in
15
+ * `packages/core/src/jobs/tools.ts`, `getViewerOrgRole` in
16
+ * `packages/dispatch/src/server/lib/usage-metrics-store.ts`, and the same
17
+ * query in `packages/core/src/mcp/actions/service-token-access.ts`) — same
18
+ * SQL, same fail-closed-to-null-on-error semantics. Returns null (never an
19
+ * org role) on any lookup error or when the caller has no membership row in
20
+ * this org.
21
+ */
22
+ async function resolveCallerOrgRole(orgId, email) {
23
+ if (!orgId)
24
+ return null;
25
+ try {
26
+ const client = getDbExec();
27
+ const { rows } = await client.execute({
28
+ sql: `SELECT role FROM org_members WHERE org_id = ? AND LOWER(email) = ? LIMIT 1`,
29
+ args: [orgId, email.toLowerCase()],
30
+ });
31
+ if (rows.length === 0)
32
+ return null;
33
+ const role = rows[0].role;
34
+ return typeof role === "string" && role ? role : null;
35
+ }
36
+ catch {
37
+ return null;
38
+ }
39
+ }
5
40
  const AuthSchema = z.discriminatedUnion("type", [
6
41
  z.object({
7
42
  type: z.literal("none"),
@@ -98,6 +133,32 @@ After registration the provider appears in provider-api-catalog and can be used
98
133
  throw new Error("provider-api-register requires an authenticated request context.");
99
134
  }
100
135
  const scopeId = scope === "org" ? (ctx.orgId ?? ctx.userEmail) : ctx.userEmail;
136
+ // Only upsert/delete mutate state; list/get remain readable by any org
137
+ // member (scoping org-scoped reads is left as a follow-up — see plan
138
+ // 014). Resolve the caller's role in the *target* org (`scopeId`, which
139
+ // for scope === "org" is exactly `ctx.orgId`) and enforce owner/admin
140
+ // before allowing an org-scoped write. `assertCanMutateCustomProviderScope`
141
+ // is the single source of truth for this check and is also enforced a
142
+ // second time inside `upsertCustomProvider`/`deleteCustomProvider`
143
+ // themselves (defense in depth) — calling it here too gives a clear,
144
+ // early error before any other work happens.
145
+ //
146
+ // When the caller has no active org (`ctx.orgId` is null — a solo user,
147
+ // or an app that hasn't wired `resolveOrgId` at all), `scopeId` above
148
+ // already collapsed to `ctx.userEmail`: there is no shared org resource
149
+ // to protect, and no *other* caller can ever address that same scopeId
150
+ // (every other request's fallback is scoped to *its own* email). Treat
151
+ // that case like sole ownership of a personal bucket — consistent with
152
+ // `org/context.ts`'s auto-created personal org, which also assigns the
153
+ // user role "owner" — rather than hard-rejecting scope: "org" (the
154
+ // action's own default) for every solo user or org-less app.
155
+ let orgRole = null;
156
+ if ((operation === "upsert" || operation === "delete") && scope === "org") {
157
+ orgRole = ctx.orgId
158
+ ? await resolveCallerOrgRole(ctx.orgId, ctx.userEmail)
159
+ : "owner";
160
+ assertCanMutateCustomProviderScope(scope, scopeId, orgRole);
161
+ }
101
162
  if (operation === "list") {
102
163
  const providers = await listCustomProviders(scope, scopeId);
103
164
  return {
@@ -125,7 +186,7 @@ After registration the provider appears in provider-api-catalog and can be used
125
186
  if (operation === "delete") {
126
187
  if (!id)
127
188
  throw new Error("id is required for delete operation.");
128
- const deleted = await deleteCustomProvider(scope, scopeId, id);
189
+ const deleted = await deleteCustomProvider(scope, scopeId, id, orgRole);
129
190
  return { deleted, id };
130
191
  }
131
192
  // upsert
@@ -148,6 +209,7 @@ After registration the provider appears in provider-api-catalog and can be used
148
209
  allowedHostSuffixes,
149
210
  defaultHeaders,
150
211
  notes,
212
+ orgRole,
151
213
  });
152
214
  return {
153
215
  registered: true,
@@ -1 +1 @@
1
- {"version":3,"file":"provider-api-register.js","sourceRoot":"","sources":["../../src/actions/provider-api-register.ts"],"names":[],"mappings":"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","sourcesContent":["import { defineAction } from \"@agent-native/core\";\nimport {\n upsertCustomProvider,\n deleteCustomProvider,\n listCustomProviders,\n getCustomProvider,\n} from \"@agent-native/core/provider-api\";\nimport { getCredentialContext } from \"@agent-native/core/server\";\nimport { z } from \"zod\";\n\nconst AuthSchema = z.discriminatedUnion(\"type\", [\n z.object({\n type: z.literal(\"none\"),\n }),\n z.object({\n type: z.literal(\"bearer\"),\n credentialKey: z\n .string()\n .min(1)\n .describe(\n \"Name of the credential key (e.g. MY_API_TOKEN). Must already be saved via app secrets — this action never accepts secret values.\",\n ),\n }),\n z.object({\n type: z.literal(\"basic\"),\n usernameKey: z\n .string()\n .min(1)\n .describe(\"Credential key name for the username/login.\"),\n passwordKey: z\n .string()\n .min(1)\n .describe(\"Credential key name for the password/secret.\"),\n }),\n z.object({\n type: z.literal(\"api-key-header\"),\n credentialKey: z\n .string()\n .min(1)\n .describe(\"Credential key name for the API key value.\"),\n headerName: z\n .string()\n .min(1)\n .describe(\"HTTP header name to send the key in (e.g. X-Api-Key).\"),\n }),\n]);\n\nexport default defineAction({\n description: `Register or update a custom API provider so the agent can call it via provider-api-request and look up its docs via provider-api-docs.\n\nIMPORTANT — credentials:\n- This action stores only credential KEY NAMES, never secret values.\n- If the required API key is not yet saved, instruct the user to add it via app Settings → Keys, or use the create-vault-secret action, before calling this action.\n- Supported auth kinds: none, bearer, basic, api-key-header.\n google-service-account and oauth-bearer are NOT supported for custom providers.\n\nAfter registration the provider appears in provider-api-catalog and can be used with provider-api-request.`,\n schema: z.object({\n operation: z\n .enum([\"upsert\", \"delete\", \"list\", \"get\"])\n .default(\"upsert\")\n .describe(\n \"Operation: upsert (create or update), delete (remove), list (all custom providers), get (single provider).\",\n ),\n id: z\n .string()\n .min(1)\n .max(64)\n .optional()\n .describe(\n \"Provider slug (e.g. my-api). Lowercase letters, digits, hyphens only. Required for upsert/delete/get.\",\n ),\n label: z\n .string()\n .min(1)\n .max(200)\n .optional()\n .describe(\n \"Human-readable name (e.g. 'My Analytics API'). Required for upsert.\",\n ),\n baseUrl: z\n .string()\n .url()\n .optional()\n .describe(\n \"Base URL for the API (e.g. https://api.example.com/v1). Required for upsert. Must be a public https/http URL.\",\n ),\n auth: AuthSchema.optional().describe(\n \"Auth configuration. Required for upsert. Use type 'none' for public APIs.\",\n ),\n docsUrls: z\n .array(z.string().url())\n .optional()\n .describe(\"Optional list of documentation URLs for this provider.\"),\n allowedHostSuffixes: z\n .array(z.string())\n .optional()\n .describe(\n \"Optional list of additional host suffixes requests may target beyond the base URL origin (e.g. ['example.com']).\",\n ),\n defaultHeaders: z\n .record(z.string(), z.string())\n .optional()\n .describe(\n \"Optional headers to include on every request (e.g. { 'Accept': 'application/json' }).\",\n ),\n notes: z\n .string()\n .max(1000)\n .optional()\n .describe(\"Optional notes about this provider shown in the catalog.\"),\n scope: z\n .enum([\"user\", \"org\"])\n .default(\"org\")\n .describe(\n \"Whether to store the provider for the current user only ('user') or for the whole workspace ('org').\",\n ),\n }),\n http: false,\n run: async ({\n operation,\n id,\n label,\n baseUrl,\n auth,\n docsUrls,\n allowedHostSuffixes,\n defaultHeaders,\n notes,\n scope,\n }) => {\n const ctx = getCredentialContext();\n if (!ctx) {\n throw new Error(\n \"provider-api-register requires an authenticated request context.\",\n );\n }\n const scopeId =\n scope === \"org\" ? (ctx.orgId ?? ctx.userEmail) : ctx.userEmail;\n\n if (operation === \"list\") {\n const providers = await listCustomProviders(scope, scopeId);\n return {\n providers: providers.map((p) => ({\n id: p.id,\n label: p.label,\n baseUrl: p.baseUrl,\n authType: p.auth.type,\n docsUrls: p.docsUrls,\n notes: p.notes,\n updatedAt: p.updatedAt,\n })),\n count: providers.length,\n };\n }\n\n if (operation === \"get\") {\n if (!id) throw new Error(\"id is required for get operation.\");\n const provider = await getCustomProvider(scope, scopeId, id);\n if (!provider) {\n return { found: false, id };\n }\n return { found: true, provider };\n }\n\n if (operation === \"delete\") {\n if (!id) throw new Error(\"id is required for delete operation.\");\n const deleted = await deleteCustomProvider(scope, scopeId, id);\n return { deleted, id };\n }\n\n // upsert\n if (!id) throw new Error(\"id is required for upsert operation.\");\n if (!label) throw new Error(\"label is required for upsert operation.\");\n if (!baseUrl) throw new Error(\"baseUrl is required for upsert operation.\");\n if (!auth) throw new Error(\"auth is required for upsert operation.\");\n\n await upsertCustomProvider({\n scope,\n scopeId,\n id,\n label,\n baseUrl,\n auth,\n docsUrls,\n allowedHostSuffixes,\n defaultHeaders,\n notes,\n });\n\n return {\n registered: true,\n id,\n label,\n message: `Custom provider \"${id}\" registered. Use provider-api-catalog to inspect it and provider-api-request to call it.`,\n };\n },\n});\n"]}
1
+ {"version":3,"file":"provider-api-register.js","sourceRoot":"","sources":["../../src/actions/provider-api-register.ts"],"names":[],"mappings":"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","sourcesContent":["import { defineAction } from \"@agent-native/core\";\nimport { getDbExec } from \"@agent-native/core/db\";\nimport {\n upsertCustomProvider,\n deleteCustomProvider,\n listCustomProviders,\n getCustomProvider,\n assertCanMutateCustomProviderScope,\n} from \"@agent-native/core/provider-api\";\nimport { getCredentialContext } from \"@agent-native/core/server\";\nimport { z } from \"zod\";\n\n/**\n * Resolve the caller's role in a specific org, straight from `org_members`.\n *\n * `getCredentialContext()` (from `@agent-native/core/server`) only exposes\n * `{ userEmail, orgId }` — no role. `getOrgContext()` (from\n * `@agent-native/core/org`) resolves role but requires an `H3Event`, which\n * `defineAction` handlers are not given. This mirrors the established\n * no-event role-lookup idiom already used for org-admin gating inside\n * agent-callable/background code (see `isCurrentUserOrgAdmin` in\n * `packages/core/src/jobs/tools.ts`, `getViewerOrgRole` in\n * `packages/dispatch/src/server/lib/usage-metrics-store.ts`, and the same\n * query in `packages/core/src/mcp/actions/service-token-access.ts`) — same\n * SQL, same fail-closed-to-null-on-error semantics. Returns null (never an\n * org role) on any lookup error or when the caller has no membership row in\n * this org.\n */\nasync function resolveCallerOrgRole(\n orgId: string | null,\n email: string,\n): Promise<string | null> {\n if (!orgId) return null;\n try {\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT role FROM org_members WHERE org_id = ? AND LOWER(email) = ? LIMIT 1`,\n args: [orgId, email.toLowerCase()],\n });\n if (rows.length === 0) return null;\n const role = (rows[0] as { role?: unknown }).role;\n return typeof role === \"string\" && role ? role : null;\n } catch {\n return null;\n }\n}\n\nconst AuthSchema = z.discriminatedUnion(\"type\", [\n z.object({\n type: z.literal(\"none\"),\n }),\n z.object({\n type: z.literal(\"bearer\"),\n credentialKey: z\n .string()\n .min(1)\n .describe(\n \"Name of the credential key (e.g. MY_API_TOKEN). Must already be saved via app secrets — this action never accepts secret values.\",\n ),\n }),\n z.object({\n type: z.literal(\"basic\"),\n usernameKey: z\n .string()\n .min(1)\n .describe(\"Credential key name for the username/login.\"),\n passwordKey: z\n .string()\n .min(1)\n .describe(\"Credential key name for the password/secret.\"),\n }),\n z.object({\n type: z.literal(\"api-key-header\"),\n credentialKey: z\n .string()\n .min(1)\n .describe(\"Credential key name for the API key value.\"),\n headerName: z\n .string()\n .min(1)\n .describe(\"HTTP header name to send the key in (e.g. X-Api-Key).\"),\n }),\n]);\n\nexport default defineAction({\n description: `Register or update a custom API provider so the agent can call it via provider-api-request and look up its docs via provider-api-docs.\n\nIMPORTANT — credentials:\n- This action stores only credential KEY NAMES, never secret values.\n- If the required API key is not yet saved, instruct the user to add it via app Settings → Keys, or use the create-vault-secret action, before calling this action.\n- Supported auth kinds: none, bearer, basic, api-key-header.\n google-service-account and oauth-bearer are NOT supported for custom providers.\n\nAfter registration the provider appears in provider-api-catalog and can be used with provider-api-request.`,\n schema: z.object({\n operation: z\n .enum([\"upsert\", \"delete\", \"list\", \"get\"])\n .default(\"upsert\")\n .describe(\n \"Operation: upsert (create or update), delete (remove), list (all custom providers), get (single provider).\",\n ),\n id: z\n .string()\n .min(1)\n .max(64)\n .optional()\n .describe(\n \"Provider slug (e.g. my-api). Lowercase letters, digits, hyphens only. Required for upsert/delete/get.\",\n ),\n label: z\n .string()\n .min(1)\n .max(200)\n .optional()\n .describe(\n \"Human-readable name (e.g. 'My Analytics API'). Required for upsert.\",\n ),\n baseUrl: z\n .string()\n .url()\n .optional()\n .describe(\n \"Base URL for the API (e.g. https://api.example.com/v1). Required for upsert. Must be a public https/http URL.\",\n ),\n auth: AuthSchema.optional().describe(\n \"Auth configuration. Required for upsert. Use type 'none' for public APIs.\",\n ),\n docsUrls: z\n .array(z.string().url())\n .optional()\n .describe(\"Optional list of documentation URLs for this provider.\"),\n allowedHostSuffixes: z\n .array(z.string())\n .optional()\n .describe(\n \"Optional list of additional host suffixes requests may target beyond the base URL origin (e.g. ['example.com']).\",\n ),\n defaultHeaders: z\n .record(z.string(), z.string())\n .optional()\n .describe(\n \"Optional headers to include on every request (e.g. { 'Accept': 'application/json' }).\",\n ),\n notes: z\n .string()\n .max(1000)\n .optional()\n .describe(\"Optional notes about this provider shown in the catalog.\"),\n scope: z\n .enum([\"user\", \"org\"])\n .default(\"org\")\n .describe(\n \"Whether to store the provider for the current user only ('user') or for the whole workspace ('org').\",\n ),\n }),\n http: false,\n run: async ({\n operation,\n id,\n label,\n baseUrl,\n auth,\n docsUrls,\n allowedHostSuffixes,\n defaultHeaders,\n notes,\n scope,\n }) => {\n const ctx = getCredentialContext();\n if (!ctx) {\n throw new Error(\n \"provider-api-register requires an authenticated request context.\",\n );\n }\n const scopeId =\n scope === \"org\" ? (ctx.orgId ?? ctx.userEmail) : ctx.userEmail;\n\n // Only upsert/delete mutate state; list/get remain readable by any org\n // member (scoping org-scoped reads is left as a follow-up — see plan\n // 014). Resolve the caller's role in the *target* org (`scopeId`, which\n // for scope === \"org\" is exactly `ctx.orgId`) and enforce owner/admin\n // before allowing an org-scoped write. `assertCanMutateCustomProviderScope`\n // is the single source of truth for this check and is also enforced a\n // second time inside `upsertCustomProvider`/`deleteCustomProvider`\n // themselves (defense in depth) — calling it here too gives a clear,\n // early error before any other work happens.\n //\n // When the caller has no active org (`ctx.orgId` is null — a solo user,\n // or an app that hasn't wired `resolveOrgId` at all), `scopeId` above\n // already collapsed to `ctx.userEmail`: there is no shared org resource\n // to protect, and no *other* caller can ever address that same scopeId\n // (every other request's fallback is scoped to *its own* email). Treat\n // that case like sole ownership of a personal bucket — consistent with\n // `org/context.ts`'s auto-created personal org, which also assigns the\n // user role \"owner\" — rather than hard-rejecting scope: \"org\" (the\n // action's own default) for every solo user or org-less app.\n let orgRole: string | null = null;\n if ((operation === \"upsert\" || operation === \"delete\") && scope === \"org\") {\n orgRole = ctx.orgId\n ? await resolveCallerOrgRole(ctx.orgId, ctx.userEmail)\n : \"owner\";\n assertCanMutateCustomProviderScope(scope, scopeId, orgRole);\n }\n\n if (operation === \"list\") {\n const providers = await listCustomProviders(scope, scopeId);\n return {\n providers: providers.map((p) => ({\n id: p.id,\n label: p.label,\n baseUrl: p.baseUrl,\n authType: p.auth.type,\n docsUrls: p.docsUrls,\n notes: p.notes,\n updatedAt: p.updatedAt,\n })),\n count: providers.length,\n };\n }\n\n if (operation === \"get\") {\n if (!id) throw new Error(\"id is required for get operation.\");\n const provider = await getCustomProvider(scope, scopeId, id);\n if (!provider) {\n return { found: false, id };\n }\n return { found: true, provider };\n }\n\n if (operation === \"delete\") {\n if (!id) throw new Error(\"id is required for delete operation.\");\n const deleted = await deleteCustomProvider(scope, scopeId, id, orgRole);\n return { deleted, id };\n }\n\n // upsert\n if (!id) throw new Error(\"id is required for upsert operation.\");\n if (!label) throw new Error(\"label is required for upsert operation.\");\n if (!baseUrl) throw new Error(\"baseUrl is required for upsert operation.\");\n if (!auth) throw new Error(\"auth is required for upsert operation.\");\n\n await upsertCustomProvider({\n scope,\n scopeId,\n id,\n label,\n baseUrl,\n auth,\n docsUrls,\n allowedHostSuffixes,\n defaultHeaders,\n notes,\n orgRole,\n });\n\n return {\n registered: true,\n id,\n label,\n message: `Custom provider \"${id}\" registered. Use provider-api-catalog to inspect it and provider-api-request to call it.`,\n };\n },\n});\n"]}
@@ -2,21 +2,21 @@ declare const _default: import("@agent-native/core").ActionDefinition<{
2
2
  id: string;
3
3
  reason?: string | undefined;
4
4
  }, {
5
+ afterValue: string | null;
6
+ beforeValue: string | null;
7
+ changeType: string;
8
+ createdAt: number;
5
9
  id: string;
6
- ownerEmail: string;
7
10
  orgId: string | null;
8
- changeType: string;
9
- targetType: string;
10
- targetId: string | null;
11
- status: string;
12
- summary: string;
11
+ ownerEmail: string;
13
12
  payload: string;
14
- beforeValue: string | null;
15
- afterValue: string | null;
16
13
  requestedBy: string;
17
- reviewedBy: string | null;
18
14
  reviewedAt: number | null;
19
- createdAt: number;
15
+ reviewedBy: string | null;
16
+ status: string;
17
+ summary: string;
18
+ targetId: string | null;
19
+ targetType: string;
20
20
  updatedAt: number;
21
21
  }>;
22
22
  export default _default;
@@ -16,7 +16,7 @@ export default defineAction({
16
16
  }),
17
17
  run: async (args) => {
18
18
  const agents = await discoverAgents("dispatch");
19
- const knownIds = new Set(agents.map((agent) => agent.id));
19
+ const knownIds = new Set(["dispatch", ...agents.map((agent) => agent.id)]);
20
20
  const selectedAppIds = Array.from(new Set(args.selectedAppIds
21
21
  .map((id) => id.trim().toLowerCase())
22
22
  .filter(Boolean)));
@@ -24,9 +24,6 @@ export default defineAction({
24
24
  if (unknown.length > 0) {
25
25
  throw new Error(`Unknown app(s): ${unknown.join(", ")}. Use list-mcp-app-access to see available app IDs.`);
26
26
  }
27
- if (args.mode === "selected-apps" && selectedAppIds.length === 0) {
28
- throw new Error("selected-apps mode requires at least one app ID.");
29
- }
30
27
  await setDispatchMcpAppAccessSettings({
31
28
  mode: args.mode,
32
29
  selectedAppIds,
@@ -1 +1 @@
1
- {"version":3,"file":"set-mcp-app-access.js","sourceRoot":"","sources":["../../src/actions/set-mcp-app-access.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,2CAA2C,CAAC;AAC3E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,+BAA+B,EAAE,MAAM,mCAAmC,CAAC;AACpF,OAAO,gBAAgB,MAAM,0BAA0B,CAAC;AAExD,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC;AAEzD,eAAe,YAAY,CAAC;IAC1B,WAAW,EACT,sEAAsE;IACxE,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,IAAI,EAAE,UAAU,CAAC,QAAQ,CACvB,6FAA6F,CAC9F;QACD,cAAc,EAAE,CAAC;aACd,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;aACjB,OAAO,CAAC,EAAE,CAAC;aACX,QAAQ,CAAC,+CAA+C,CAAC;KAC7D,CAAC;IACF,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAClB,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,UAAU,CAAC,CAAC;QAChD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1D,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,CAC/B,IAAI,GAAG,CACL,IAAI,CAAC,cAAc;aAChB,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;aACpC,MAAM,CAAC,OAAO,CAAC,CACnB,CACF,CAAC;QACF,MAAM,OAAO,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACjE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CACb,mBAAmB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,qDAAqD,CAC3F,CAAC;QACJ,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,eAAe,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjE,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;QACtE,CAAC;QAED,MAAM,+BAA+B,CAAC;YACpC,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,cAAc;SACf,CAAC,CAAC;QACH,MAAM,WAAW,CAAC;YAChB,MAAM,EAAE,oBAAoB;YAC5B,UAAU,EAAE,qBAAqB;YACjC,QAAQ,EAAE,aAAa;YACvB,OAAO,EACL,IAAI,CAAC,IAAI,KAAK,UAAU;gBACtB,CAAC,CAAC,yCAAyC;gBAC3C,CAAC,CAAC,kCAAkC,cAAc,CAAC,MAAM,kBAAkB;YAC/E,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,cAAc,EAAE;SAC9C,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAEnB,OAAO,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;CACF,CAAC,CAAC","sourcesContent":["import { defineAction } from \"@agent-native/core\";\nimport { discoverAgents } from \"@agent-native/core/server/agent-discovery\";\nimport { z } from \"zod\";\n\nimport { recordAudit } from \"../server/lib/dispatch-store.js\";\nimport { setDispatchMcpAppAccessSettings } from \"../server/lib/mcp-access-store.js\";\nimport listMcpAppAccess from \"./list-mcp-app-access.js\";\n\nconst modeSchema = z.enum([\"all-apps\", \"selected-apps\"]);\n\nexport default defineAction({\n description:\n \"Set which apps are available through Dispatch's unified MCP gateway.\",\n schema: z.object({\n mode: modeSchema.describe(\n \"Use all-apps to expose every discovered app, or selected-apps to allow only selectedAppIds.\",\n ),\n selectedAppIds: z\n .array(z.string())\n .default([])\n .describe(\"App IDs to expose when mode is selected-apps.\"),\n }),\n run: async (args) => {\n const agents = await discoverAgents(\"dispatch\");\n const knownIds = new Set(agents.map((agent) => agent.id));\n const selectedAppIds = Array.from(\n new Set(\n args.selectedAppIds\n .map((id) => id.trim().toLowerCase())\n .filter(Boolean),\n ),\n );\n const unknown = selectedAppIds.filter((id) => !knownIds.has(id));\n if (unknown.length > 0) {\n throw new Error(\n `Unknown app(s): ${unknown.join(\", \")}. Use list-mcp-app-access to see available app IDs.`,\n );\n }\n if (args.mode === \"selected-apps\" && selectedAppIds.length === 0) {\n throw new Error(\"selected-apps mode requires at least one app ID.\");\n }\n\n await setDispatchMcpAppAccessSettings({\n mode: args.mode,\n selectedAppIds,\n });\n await recordAudit({\n action: \"mcp-access.updated\",\n targetType: \"dispatch-mcp-access\",\n targetId: \"unified-mcp\",\n summary:\n args.mode === \"all-apps\"\n ? \"Allowed Dispatch MCP access to all apps\"\n : `Allowed Dispatch MCP access to ${selectedAppIds.length} selected app(s)`,\n metadata: { mode: args.mode, selectedAppIds },\n }).catch(() => {});\n\n return listMcpAppAccess.run({});\n },\n});\n"]}
1
+ {"version":3,"file":"set-mcp-app-access.js","sourceRoot":"","sources":["../../src/actions/set-mcp-app-access.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,2CAA2C,CAAC;AAC3E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,+BAA+B,EAAE,MAAM,mCAAmC,CAAC;AACpF,OAAO,gBAAgB,MAAM,0BAA0B,CAAC;AAExD,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC;AAEzD,eAAe,YAAY,CAAC;IAC1B,WAAW,EACT,sEAAsE;IACxE,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,IAAI,EAAE,UAAU,CAAC,QAAQ,CACvB,6FAA6F,CAC9F;QACD,cAAc,EAAE,CAAC;aACd,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;aACjB,OAAO,CAAC,EAAE,CAAC;aACX,QAAQ,CAAC,+CAA+C,CAAC;KAC7D,CAAC;IACF,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAClB,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,UAAU,CAAC,CAAC;QAChD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3E,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,CAC/B,IAAI,GAAG,CACL,IAAI,CAAC,cAAc;aAChB,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;aACpC,MAAM,CAAC,OAAO,CAAC,CACnB,CACF,CAAC;QACF,MAAM,OAAO,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACjE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CACb,mBAAmB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,qDAAqD,CAC3F,CAAC;QACJ,CAAC;QACD,MAAM,+BAA+B,CAAC;YACpC,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,cAAc;SACf,CAAC,CAAC;QACH,MAAM,WAAW,CAAC;YAChB,MAAM,EAAE,oBAAoB;YAC5B,UAAU,EAAE,qBAAqB;YACjC,QAAQ,EAAE,aAAa;YACvB,OAAO,EACL,IAAI,CAAC,IAAI,KAAK,UAAU;gBACtB,CAAC,CAAC,yCAAyC;gBAC3C,CAAC,CAAC,kCAAkC,cAAc,CAAC,MAAM,kBAAkB;YAC/E,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,cAAc,EAAE;SAC9C,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAEnB,OAAO,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;CACF,CAAC,CAAC","sourcesContent":["import { defineAction } from \"@agent-native/core\";\nimport { discoverAgents } from \"@agent-native/core/server/agent-discovery\";\nimport { z } from \"zod\";\n\nimport { recordAudit } from \"../server/lib/dispatch-store.js\";\nimport { setDispatchMcpAppAccessSettings } from \"../server/lib/mcp-access-store.js\";\nimport listMcpAppAccess from \"./list-mcp-app-access.js\";\n\nconst modeSchema = z.enum([\"all-apps\", \"selected-apps\"]);\n\nexport default defineAction({\n description:\n \"Set which apps are available through Dispatch's unified MCP gateway.\",\n schema: z.object({\n mode: modeSchema.describe(\n \"Use all-apps to expose every discovered app, or selected-apps to allow only selectedAppIds.\",\n ),\n selectedAppIds: z\n .array(z.string())\n .default([])\n .describe(\"App IDs to expose when mode is selected-apps.\"),\n }),\n run: async (args) => {\n const agents = await discoverAgents(\"dispatch\");\n const knownIds = new Set([\"dispatch\", ...agents.map((agent) => agent.id)]);\n const selectedAppIds = Array.from(\n new Set(\n args.selectedAppIds\n .map((id) => id.trim().toLowerCase())\n .filter(Boolean),\n ),\n );\n const unknown = selectedAppIds.filter((id) => !knownIds.has(id));\n if (unknown.length > 0) {\n throw new Error(\n `Unknown app(s): ${unknown.join(\", \")}. Use list-mcp-app-access to see available app IDs.`,\n );\n }\n await setDispatchMcpAppAccessSettings({\n mode: args.mode,\n selectedAppIds,\n });\n await recordAudit({\n action: \"mcp-access.updated\",\n targetType: \"dispatch-mcp-access\",\n targetId: \"unified-mcp\",\n summary:\n args.mode === \"all-apps\"\n ? \"Allowed Dispatch MCP access to all apps\"\n : `Allowed Dispatch MCP access to ${selectedAppIds.length} selected app(s)`,\n metadata: { mode: args.mode, selectedAppIds },\n }).catch(() => {});\n\n return listMcpAppAccess.run({});\n },\n});\n"]}
@@ -0,0 +1,8 @@
1
+ interface ActionQueryErrorProps {
2
+ error?: unknown;
3
+ onRetry: () => unknown;
4
+ className?: string;
5
+ }
6
+ export declare function ActionQueryError({ onRetry, className, }: ActionQueryErrorProps): import("node_modules/@types/react").JSX.Element;
7
+ export {};
8
+ //# sourceMappingURL=action-query-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-query-error.d.ts","sourceRoot":"","sources":["../../src/components/action-query-error.tsx"],"names":[],"mappings":"AAMA,UAAU,qBAAqB;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,OAAO,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,SAAS,GACV,EAAE,qBAAqB,mDAgBvB"}
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useT } from "@agent-native/core/client";
3
+ import { IconAlertTriangle, IconRefresh } from "@tabler/icons-react";
4
+ import { Alert, AlertDescription, AlertTitle } from "./ui/alert.js";
5
+ import { Button } from "./ui/button.js";
6
+ export function ActionQueryError({ onRetry, className, }) {
7
+ const t = useT();
8
+ return (_jsxs(Alert, { variant: "destructive", className: className, children: [_jsx(IconAlertTriangle, { className: "size-4" }), _jsx(AlertTitle, { children: t("dispatch.pages.dataLoadFailed") }), _jsxs(AlertDescription, { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx("span", { children: t("dispatch.pages.dataLoadFailedDescription") }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: onRetry, children: [_jsx(IconRefresh, { className: "size-4" }), t("dispatch.pages.tryAgain")] })] })] }));
9
+ }
10
+ //# sourceMappingURL=action-query-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-query-error.js","sourceRoot":"","sources":["../../src/components/action-query-error.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAErE,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAQrC,MAAM,UAAU,gBAAgB,CAAC,EAC/B,OAAO,EACP,SAAS,GACa;IACtB,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;IAEjB,OAAO,CACL,MAAC,KAAK,IAAC,OAAO,EAAC,aAAa,EAAC,SAAS,EAAE,SAAS,aAC/C,KAAC,iBAAiB,IAAC,SAAS,EAAC,QAAQ,GAAG,EACxC,KAAC,UAAU,cAAE,CAAC,CAAC,+BAA+B,CAAC,GAAc,EAC7D,MAAC,gBAAgB,IAAC,SAAS,EAAC,mDAAmD,aAC7E,yBAAO,CAAC,CAAC,0CAA0C,CAAC,GAAQ,EAC5D,MAAC,MAAM,IAAC,IAAI,EAAC,QAAQ,EAAC,OAAO,EAAC,SAAS,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAE,OAAO,aAChE,KAAC,WAAW,IAAC,SAAS,EAAC,QAAQ,GAAG,EACjC,CAAC,CAAC,yBAAyB,CAAC,IACtB,IACQ,IACb,CACT,CAAC;AACJ,CAAC","sourcesContent":["import { useT } from \"@agent-native/core/client\";\nimport { IconAlertTriangle, IconRefresh } from \"@tabler/icons-react\";\n\nimport { Alert, AlertDescription, AlertTitle } from \"./ui/alert\";\nimport { Button } from \"./ui/button\";\n\ninterface ActionQueryErrorProps {\n error?: unknown;\n onRetry: () => unknown;\n className?: string;\n}\n\nexport function ActionQueryError({\n onRetry,\n className,\n}: ActionQueryErrorProps) {\n const t = useT();\n\n return (\n <Alert variant=\"destructive\" className={className}>\n <IconAlertTriangle className=\"size-4\" />\n <AlertTitle>{t(\"dispatch.pages.dataLoadFailed\")}</AlertTitle>\n <AlertDescription className=\"flex flex-wrap items-center justify-between gap-3\">\n <span>{t(\"dispatch.pages.dataLoadFailedDescription\")}</span>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={onRetry}>\n <IconRefresh className=\"size-4\" />\n {t(\"dispatch.pages.tryAgain\")}\n </Button>\n </AlertDescription>\n </Alert>\n );\n}\n"]}
@@ -7,5 +7,9 @@ interface AppKeysPopoverProps {
7
7
  side?: "top" | "right" | "bottom" | "left";
8
8
  }
9
9
  export declare function AppKeysPopover({ appId, appName, trigger, align, side, }: AppKeysPopoverProps): import("node_modules/@types/react").JSX.Element;
10
+ export declare function AppKeysPanel({ appId, appName, }: {
11
+ appId: string;
12
+ appName: string;
13
+ }): import("node_modules/@types/react").JSX.Element;
10
14
  export {};
11
15
  //# sourceMappingURL=app-keys-popover.d.ts.map