@agent-native/dispatch 0.2.11 → 0.2.13
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.js +4 -4
- package/dist/actions/create-workspace-resource.js.map +1 -1
- package/dist/actions/grant-workspace-resources-to-app.d.ts +3 -0
- package/dist/actions/grant-workspace-resources-to-app.d.ts.map +1 -0
- package/dist/actions/grant-workspace-resources-to-app.js +15 -0
- package/dist/actions/grant-workspace-resources-to-app.js.map +1 -0
- package/dist/actions/index.d.ts.map +1 -1
- package/dist/actions/index.js +4 -0
- package/dist/actions/index.js.map +1 -1
- package/dist/actions/list-workspace-resource-options.d.ts +3 -0
- package/dist/actions/list-workspace-resource-options.d.ts.map +1 -0
- package/dist/actions/list-workspace-resource-options.js +15 -0
- package/dist/actions/list-workspace-resource-options.js.map +1 -0
- package/dist/actions/list-workspace-resources.js +2 -2
- package/dist/actions/list-workspace-resources.js.map +1 -1
- package/dist/actions/start-workspace-app-creation.d.ts.map +1 -1
- package/dist/actions/start-workspace-app-creation.js +6 -1
- package/dist/actions/start-workspace-app-creation.js.map +1 -1
- package/dist/actions/view-screen.d.ts.map +1 -1
- package/dist/actions/view-screen.js +4 -0
- package/dist/actions/view-screen.js.map +1 -1
- package/dist/components/app-keys-popover.js +3 -3
- package/dist/components/app-keys-popover.js.map +1 -1
- package/dist/components/create-app-popover.d.ts +1 -1
- package/dist/components/create-app-popover.d.ts.map +1 -1
- package/dist/components/create-app-popover.js +66 -21
- package/dist/components/create-app-popover.js.map +1 -1
- package/dist/components/workspace-app-card.d.ts +6 -0
- package/dist/components/workspace-app-card.d.ts.map +1 -0
- package/dist/components/workspace-app-card.js +12 -0
- package/dist/components/workspace-app-card.js.map +1 -0
- package/dist/db/schema.js +2 -2
- package/dist/db/schema.js.map +1 -1
- package/dist/lib/workspace-apps.d.ts +15 -0
- package/dist/lib/workspace-apps.d.ts.map +1 -0
- package/dist/lib/workspace-apps.js +9 -0
- package/dist/lib/workspace-apps.js.map +1 -0
- package/dist/routes/pages/apps.$appId.d.ts.map +1 -1
- package/dist/routes/pages/apps.$appId.js +1 -5
- package/dist/routes/pages/apps.$appId.js.map +1 -1
- package/dist/routes/pages/apps.d.ts.map +1 -1
- package/dist/routes/pages/apps.js +3 -20
- package/dist/routes/pages/apps.js.map +1 -1
- package/dist/routes/pages/overview.d.ts.map +1 -1
- package/dist/routes/pages/overview.js +2 -20
- package/dist/routes/pages/overview.js.map +1 -1
- package/dist/routes/pages/workspace.d.ts.map +1 -1
- package/dist/routes/pages/workspace.js +17 -6
- package/dist/routes/pages/workspace.js.map +1 -1
- package/dist/server/lib/app-creation-store.d.ts +1 -0
- package/dist/server/lib/app-creation-store.d.ts.map +1 -1
- package/dist/server/lib/app-creation-store.js +36 -0
- package/dist/server/lib/app-creation-store.js.map +1 -1
- package/dist/server/lib/workspace-resources-store.d.ts +29 -1
- package/dist/server/lib/workspace-resources-store.d.ts.map +1 -1
- package/dist/server/lib/workspace-resources-store.js +46 -0
- package/dist/server/lib/workspace-resources-store.js.map +1 -1
- package/dist/server/plugins/agent-chat.d.ts.map +1 -1
- package/dist/server/plugins/agent-chat.js +1 -0
- package/dist/server/plugins/agent-chat.js.map +1 -1
- package/dist/server/plugins/integrations.d.ts.map +1 -1
- package/dist/server/plugins/integrations.js +2 -0
- package/dist/server/plugins/integrations.js.map +1 -1
- package/package.json +2 -2
- package/src/actions/create-workspace-resource.ts +4 -4
- package/src/actions/grant-workspace-resources-to-app.ts +16 -0
- package/src/actions/index.ts +4 -0
- package/src/actions/list-workspace-resource-options.ts +16 -0
- package/src/actions/list-workspace-resources.ts +2 -2
- package/src/actions/start-workspace-app-creation.ts +8 -1
- package/src/actions/view-screen.ts +4 -0
- package/src/components/app-keys-popover.tsx +3 -3
- package/src/components/create-app-popover.tsx +155 -22
- package/src/components/workspace-app-card.tsx +85 -0
- package/src/db/schema.ts +2 -2
- package/src/lib/workspace-apps.ts +21 -0
- package/src/routes/pages/apps.$appId.tsx +4 -17
- package/src/routes/pages/apps.tsx +6 -89
- package/src/routes/pages/overview.tsx +5 -84
- package/src/routes/pages/workspace.tsx +31 -5
- package/src/server/lib/app-creation-store.ts +52 -0
- package/src/server/lib/workspace-resources-store.ts +75 -1
- package/src/server/plugins/agent-chat.ts +1 -0
- package/src/server/plugins/integrations.ts +2 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-chat.js","sourceRoot":"","sources":["../../../src/server/plugins/agent-chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,eAAe,qBAAqB,CAAC;IACnC,KAAK,EAAE,UAAU;IACjB,0EAA0E;IAC1E,2EAA2E;IAC3E,2EAA2E;IAC3E,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAC5B,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC;QACvC,OAAO,GAAG,CAAC,KAAK,CAAC;IACnB,CAAC;IACD,2EAA2E;IAC3E,2EAA2E;IAC3E,wEAAwE;IACxE,OAAO,EAAE,eAAe;IACxB,YAAY,EAAE
|
|
1
|
+
{"version":3,"file":"agent-chat.js","sourceRoot":"","sources":["../../../src/server/plugins/agent-chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,eAAe,qBAAqB,CAAC;IACnC,KAAK,EAAE,UAAU;IACjB,0EAA0E;IAC1E,2EAA2E;IAC3E,2EAA2E;IAC3E,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAC5B,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC;QACvC,OAAO,GAAG,CAAC,KAAK,CAAC;IACnB,CAAC;IACD,2EAA2E;IAC3E,2EAA2E;IAC3E,wEAAwE;IACxE,OAAO,EAAE,eAAe;IACxB,YAAY,EAAE;;;;;;;;;;;;;;;;;;;4EAmB4D;CAC3E,CAAC,CAAC","sourcesContent":["import { createAgentChatPlugin } from \"@agent-native/core/server\";\nimport { getOrgContext } from \"@agent-native/core/org\";\nimport { dispatchActions } from \"../../actions/index.js\";\n\nexport default createAgentChatPlugin({\n appId: \"dispatch\",\n // Without this, AGENT_ORG_ID is never set on agent action calls and every\n // row written through the frontend (vault secrets, destinations, workspace\n // resources) lands with org_id=NULL — breaking data isolation across orgs.\n resolveOrgId: async (event) => {\n const ctx = await getOrgContext(event);\n return ctx.orgId;\n },\n // Read actions directly from the package's own action map rather than from\n // a build-time-generated `.generated/actions-registry.ts` (the latter is a\n // template-only construct that the Vite plugin emits next to actions/).\n actions: dispatchActions,\n systemPrompt: `You are the central dispatch for this workspace.\n\nDefault posture:\n- Treat Slack and Telegram as shared entrypoints into the workspace.\n- Heavily delegate domain work to specialized agents through A2A when another app owns the job.\n- Keep durable memory and operating instructions in resources rather than ephemeral chat.\n- Prefer replying in the current external thread unless the user explicitly asks you to send to a saved destination.\n\nUse the standard workspace primitives:\n- Read and update resources like AGENTS.md, LEARNINGS.md, jobs/*.md, agents/*.md, and remote-agents/*.json when appropriate.\n- Use recurring jobs for scheduled behavior.\n- Use custom agent profiles in agents/*.md for local spawned work and remote-agents/*.json for remote A2A apps.\n- When answering whether workspace apps expose agent cards or A2A endpoints, call list-workspace-apps with includeAgentCards=true. If you have not requested that probe, absence of agent-card fields means unchecked, not unavailable.\n- When creating a new workspace app, create a separate app under apps/<app-id> with apps/<app-id>/package.json, mount it at /<app-id>, use relative /<app-id> links, never hardcode localhost or dev ports, use shadcn/ui with @tabler/icons-react rather than lucide-react, and ensure the React Router client entry preserves APP_BASE_PATH/VITE_APP_BASE_PATH via appBasePath(). There is no separate workspace app registry to edit.\n- Treat first-party apps such as Mail, Calendar, Analytics, and Dispatch as existing hosted/connected neighbors available through links and A2A/default connected agents. Do not create wrapper apps, child apps, nested routes, or cloned template copies just to give a new app access to them; build only the genuinely new workflow and delegate cross-app work to those existing apps.\n\nWhen a user asks for something like a digest, reminder, routing rule, or saved behavior:\n- First decide whether it should be a resource, a recurring job, a destination, or a delegated task.\n- Keep responses concise and operational.\n- Avoid inventing integrations or destinations that are not configured yet.`,\n});\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integrations.d.ts","sourceRoot":"","sources":["../../../src/server/plugins/integrations.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"integrations.d.ts","sourceRoot":"","sources":["../../../src/server/plugins/integrations.ts"],"names":[],"mappings":"AA+BA;;;;GAIG;AACH,QAAA,MAAM,0BAA0B,GAAU,UAAU,GAAG,kBAuBtD,CAAC;AAEF,eAAe,0BAA0B,CAAC"}
|
|
@@ -9,6 +9,7 @@ Default posture:
|
|
|
9
9
|
- Heavily delegate domain work to specialized agents through A2A (call-agent) when another app owns the job. Apps you can delegate to include slides (decks/presentations), analytics (data/dashboards), content (docs/articles), videos (Remotion compositions), forms (form builder), clips (screen recordings), design (visual designs), and images (brand image libraries and generated raster imagery).
|
|
10
10
|
- Use list-connected-agents to see what agents are available before assuming a request must be handled locally.
|
|
11
11
|
- When asked whether workspace apps expose agent cards or A2A endpoints, call list-workspace-apps with includeAgentCards=true. Without that probe, missing agent-card fields mean unchecked, not unavailable.
|
|
12
|
+
- Treat first-party apps such as Mail, Calendar, Analytics, and Dispatch as existing hosted/connected neighbors available through links and A2A/default connected agents. Do not create wrapper apps, child apps, nested routes, or cloned template copies just to give a new app access to them; build only the genuinely new workflow and delegate cross-app work to those existing apps.
|
|
12
13
|
- Keep durable memory and operating instructions in resources rather than ephemeral chat.
|
|
13
14
|
- Reply in the originating thread unless the user explicitly asks you to send to a saved destination.
|
|
14
15
|
|
|
@@ -17,6 +18,7 @@ When a user asks for something:
|
|
|
17
18
|
- After call-agent returns an answer, RELAY IT DIRECTLY to the user with at most a one-line preface — do not rephrase, summarize, or add commentary. The downstream agent already crafted the answer; your job is delivery, not editing. This minimizes round-trips and keeps the user-visible reply fast.
|
|
18
19
|
- Exception: if the downstream agent reports a missing model/provider credential, do not name exact env vars, Vault keys, tokens, or secrets. Say the target app needs an LLM connection and recommend connecting Builder/managed LLM for that app; keep bring-your-own provider keys as a secondary option only if the user asks.
|
|
19
20
|
- If the user asks to create, build, make, scaffold, or generate an "agent" from Dispatch chat or by tagging @agent-native in Slack, email, or Telegram, first classify the ask. If it is a simple Dispatch-native behavior like a reminder, digest, monitor, routing rule, saved instruction, or recurring workflow, create or update the recurring job/resource/destination in Dispatch. If it is a robust unique product or teammate that needs its own UI, data model, actions, integrations, or domain workflow, treat it as a new workspace app and call start-workspace-app-creation.
|
|
21
|
+
- If a new-app prompt asks for access to Mail, Calendar, Analytics, or similar first-party app data/agents, keep using the existing hosted/connected app and A2A path. Do not ask Builder to scaffold those apps as children of the new app unless the user explicitly asks for a customized fork/copy.
|
|
20
22
|
- If the user explicitly asks for a new app or workspace app, call start-workspace-app-creation with their prompt. Do not satisfy a new-app request by adding a route, page, component, or file inside apps/starter or another existing app unless the user explicitly asks to modify that existing app. If the request is too vague to classify, ask one concise follow-up. If the action returns mode "builder", reply with the Builder branch URL; Builder is responsible for creating the separate workspace app under apps/<app-id>, mounting it at /<app-id>, ensuring apps/<app-id>/package.json exists so Dispatch discovers it, using relative /<app-id> links instead of hardcoded localhost/dev ports, and preserving APP_BASE_PATH/VITE_APP_BASE_PATH via appBasePath() in the React Router client entry. There is no separate workspace app registry to edit. If it returns mode "local-agent", tell the user it is ready for the local code agent and include the returned app path/prompt summary. If it returns mode "coming-soon" or "builder-unavailable", explain the missing Builder setup and ask them to connect/configure Builder.
|
|
21
23
|
- For digests, reminders, or saved behavior, prefer recurring jobs, resources, or destinations over chat replies.
|
|
22
24
|
- Keep responses concise and operational — messaging platforms have character limits.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integrations.js","sourceRoot":"","sources":["../../../src/server/plugins/integrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EACL,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,MAAM,kCAAkC,GAAG
|
|
1
|
+
{"version":3,"file":"integrations.js","sourceRoot":"","sources":["../../../src/server/plugins/integrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EACL,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,MAAM,kCAAkC,GAAG;;;;;;;;;;;;;;;;;;;;;4FAqBiD,CAAC;AAE7F;;;;GAIG;AACH,MAAM,0BAA0B,GAAG,KAAK,EAAE,QAAa,EAAE,EAAE;IACzD,MAAM,EAAE,YAAY,GAAG,EAAE,EAAE,GAAG,iBAAiB,EAAE,CAAC;IAClD,MAAM,cAAc,GAAG,YAAY,CAAC,YAAY,CAAC;IACjD,MAAM,YAAY,GAChB,OAAO,cAAc,KAAK,QAAQ;QAChC,CAAC,CAAC,cAAc;QAChB,CAAC,CAAC,OAAO,cAAc,KAAK,UAAU;YACpC,CAAC,CAAC,cAAc,CAAC,kCAAkC,CAAC;YACpD,CAAC,CAAC,kCAAkC,CAAC;IAE3C,MAAM,MAAM,GAAG,wBAAwB,CAAC;QACtC,KAAK,EAAE,UAAU;QACjB,OAAO,EAAE,eAAe;QACxB,YAAY,EAAE,oBAAoB;QAClC,aAAa,EAAE,qBAAqB;QACpC,YAAY;QACZ,wDAAwD;QACxD,yEAAyE;QACzE,+DAA+D;QAC/D,6EAA6E;KAC9E,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC,CAAC;AAEF,eAAe,0BAA0B,CAAC","sourcesContent":["import { createIntegrationsPlugin } from \"@agent-native/core/server\";\nimport {\n beforeDispatchProcess,\n resolveDispatchOwner,\n} from \"../lib/dispatch-integrations.js\";\nimport { getDispatchConfig } from \"../index.js\";\nimport { dispatchActions } from \"../../actions/index.js\";\n\nconst DISPATCH_INTEGRATION_SYSTEM_PROMPT = `You are the central dispatch for this workspace, responding via a messaging platform integration (Slack, Telegram, email, etc.).\n\nDefault posture:\n- Treat Slack, Telegram, and email as shared entrypoints into the workspace.\n- Heavily delegate domain work to specialized agents through A2A (call-agent) when another app owns the job. Apps you can delegate to include slides (decks/presentations), analytics (data/dashboards), content (docs/articles), videos (Remotion compositions), forms (form builder), clips (screen recordings), design (visual designs), and images (brand image libraries and generated raster imagery).\n- Use list-connected-agents to see what agents are available before assuming a request must be handled locally.\n- When asked whether workspace apps expose agent cards or A2A endpoints, call list-workspace-apps with includeAgentCards=true. Without that probe, missing agent-card fields mean unchecked, not unavailable.\n- Treat first-party apps such as Mail, Calendar, Analytics, and Dispatch as existing hosted/connected neighbors available through links and A2A/default connected agents. Do not create wrapper apps, child apps, nested routes, or cloned template copies just to give a new app access to them; build only the genuinely new workflow and delegate cross-app work to those existing apps.\n- Keep durable memory and operating instructions in resources rather than ephemeral chat.\n- Reply in the originating thread unless the user explicitly asks you to send to a saved destination.\n\nWhen a user asks for something:\n- If it belongs to analytics, content, slides, videos, images, etc., delegate via call-agent — do not re-implement the domain logic in dispatch.\n- After call-agent returns an answer, RELAY IT DIRECTLY to the user with at most a one-line preface — do not rephrase, summarize, or add commentary. The downstream agent already crafted the answer; your job is delivery, not editing. This minimizes round-trips and keeps the user-visible reply fast.\n- Exception: if the downstream agent reports a missing model/provider credential, do not name exact env vars, Vault keys, tokens, or secrets. Say the target app needs an LLM connection and recommend connecting Builder/managed LLM for that app; keep bring-your-own provider keys as a secondary option only if the user asks.\n- If the user asks to create, build, make, scaffold, or generate an \"agent\" from Dispatch chat or by tagging @agent-native in Slack, email, or Telegram, first classify the ask. If it is a simple Dispatch-native behavior like a reminder, digest, monitor, routing rule, saved instruction, or recurring workflow, create or update the recurring job/resource/destination in Dispatch. If it is a robust unique product or teammate that needs its own UI, data model, actions, integrations, or domain workflow, treat it as a new workspace app and call start-workspace-app-creation.\n- If a new-app prompt asks for access to Mail, Calendar, Analytics, or similar first-party app data/agents, keep using the existing hosted/connected app and A2A path. Do not ask Builder to scaffold those apps as children of the new app unless the user explicitly asks for a customized fork/copy.\n- If the user explicitly asks for a new app or workspace app, call start-workspace-app-creation with their prompt. Do not satisfy a new-app request by adding a route, page, component, or file inside apps/starter or another existing app unless the user explicitly asks to modify that existing app. If the request is too vague to classify, ask one concise follow-up. If the action returns mode \"builder\", reply with the Builder branch URL; Builder is responsible for creating the separate workspace app under apps/<app-id>, mounting it at /<app-id>, ensuring apps/<app-id>/package.json exists so Dispatch discovers it, using relative /<app-id> links instead of hardcoded localhost/dev ports, and preserving APP_BASE_PATH/VITE_APP_BASE_PATH via appBasePath() in the React Router client entry. There is no separate workspace app registry to edit. If it returns mode \"local-agent\", tell the user it is ready for the local code agent and include the returned app path/prompt summary. If it returns mode \"coming-soon\" or \"builder-unavailable\", explain the missing Builder setup and ask them to connect/configure Builder.\n- For digests, reminders, or saved behavior, prefer recurring jobs, resources, or destinations over chat replies.\n- Keep responses concise and operational — messaging platforms have character limits.\n- Use markdown sparingly (bold and lists are fine, avoid complex formatting).\n- If a task requires many steps, summarize what you did rather than streaming every detail.`;\n\n/**\n * Defer plugin construction until the Nitro plugin actually fires so the\n * config-aware system prompt resolves AFTER `setupDispatch(config)` has\n * stamped the active config (plugin module load order is not guaranteed).\n */\nconst dispatchIntegrationsPlugin = async (nitroApp: any) => {\n const { integrations = {} } = getDispatchConfig();\n const promptOverride = integrations.systemPrompt;\n const systemPrompt =\n typeof promptOverride === \"string\"\n ? promptOverride\n : typeof promptOverride === \"function\"\n ? promptOverride(DISPATCH_INTEGRATION_SYSTEM_PROMPT)\n : DISPATCH_INTEGRATION_SYSTEM_PROMPT;\n\n const plugin = createIntegrationsPlugin({\n appId: \"dispatch\",\n actions: dispatchActions,\n resolveOwner: resolveDispatchOwner,\n beforeProcess: beforeDispatchProcess,\n systemPrompt,\n // Inherit the framework default (claude-sonnet-4-6 from\n // packages/core/src/integrations/plugin.ts). Haiku was tried for latency\n // but hallucinated URLs/IDs after delegated call-agent results\n // (e.g. inventing `https://slides.workspace.com/deck/builder-io-deck-2024`).\n });\n\n return plugin(nitroApp);\n};\n\nexport default dispatchIntegrationsPlugin;\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/dispatch",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Dispatch — workspace control plane for agent-native apps. Vault, integrations, destinations, scheduled jobs, and cross-app delegation, shipped as a single drop-in package.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"typescript": "^6.0.3",
|
|
97
97
|
"vite": "8.0.3",
|
|
98
98
|
"vitest": "^4.1.5",
|
|
99
|
-
"@agent-native/core": "0.12.
|
|
99
|
+
"@agent-native/core": "0.12.15"
|
|
100
100
|
},
|
|
101
101
|
"scripts": {
|
|
102
102
|
"build": "tsc && tsc-alias --resolve-full-paths",
|
|
@@ -4,17 +4,17 @@ import { createWorkspaceResource } from "../server/lib/workspace-resources-store
|
|
|
4
4
|
|
|
5
5
|
export default defineAction({
|
|
6
6
|
description:
|
|
7
|
-
'Create a workspace-wide skill, instruction, or
|
|
7
|
+
'Create a workspace-wide skill, instruction, agent profile, or knowledge pack. Set scope to "all" to push to every app, or "selected" to grant per-app.',
|
|
8
8
|
schema: z.object({
|
|
9
9
|
kind: z
|
|
10
|
-
.enum(["skill", "instruction", "agent"])
|
|
11
|
-
.describe("Resource kind: skill, instruction, or
|
|
10
|
+
.enum(["skill", "instruction", "agent", "knowledge"])
|
|
11
|
+
.describe("Resource kind: skill, instruction, agent, or knowledge"),
|
|
12
12
|
name: z.string().describe("Human-readable name"),
|
|
13
13
|
description: z.string().optional().describe("Short description"),
|
|
14
14
|
path: z
|
|
15
15
|
.string()
|
|
16
16
|
.describe(
|
|
17
|
-
'Resource path, e.g. "skills/designer.md", "agents/researcher.md", or "remote-agents/researcher.json"',
|
|
17
|
+
'Resource path, e.g. "skills/designer.md", "agents/researcher.md", "context/gtm-messaging.md", or "remote-agents/researcher.json"',
|
|
18
18
|
),
|
|
19
19
|
content: z
|
|
20
20
|
.string()
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { grantWorkspaceResourcesToApp } from "../server/lib/workspace-resources-store.js";
|
|
4
|
+
|
|
5
|
+
export default defineAction({
|
|
6
|
+
description:
|
|
7
|
+
"Grant several selected workspace resources or knowledge packs to an app, skipping existing active grants.",
|
|
8
|
+
schema: z.object({
|
|
9
|
+
appId: z.string().describe("App ID receiving the resources"),
|
|
10
|
+
resourceIds: z
|
|
11
|
+
.array(z.string())
|
|
12
|
+
.max(100)
|
|
13
|
+
.describe("Workspace resource IDs to grant"),
|
|
14
|
+
}),
|
|
15
|
+
run: async (args) => grantWorkspaceResourcesToApp(args),
|
|
16
|
+
});
|
package/src/actions/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ import denyVaultRequest from "./deny-vault-request.js";
|
|
|
13
13
|
import getAppCreationSettings from "./get-app-creation-settings.js";
|
|
14
14
|
import getDispatchSettings from "./get-dispatch-settings.js";
|
|
15
15
|
import getWorkspaceInfo from "./get-workspace-info.js";
|
|
16
|
+
import grantWorkspaceResourcesToApp from "./grant-workspace-resources-to-app.js";
|
|
16
17
|
import grantVaultSecretsToApp from "./grant-vault-secrets-to-app.js";
|
|
17
18
|
import listConnectedAgents from "./list-connected-agents.js";
|
|
18
19
|
import listDestinations from "./list-destinations.js";
|
|
@@ -28,6 +29,7 @@ import listVaultRequests from "./list-vault-requests.js";
|
|
|
28
29
|
import listVaultSecretOptions from "./list-vault-secret-options.js";
|
|
29
30
|
import listVaultSecrets from "./list-vault-secrets.js";
|
|
30
31
|
import listWorkspaceApps from "./list-workspace-apps.js";
|
|
32
|
+
import listWorkspaceResourceOptions from "./list-workspace-resource-options.js";
|
|
31
33
|
import listWorkspaceResourceGrants from "./list-workspace-resource-grants.js";
|
|
32
34
|
import listWorkspaceResources from "./list-workspace-resources.js";
|
|
33
35
|
import navigate from "./navigate.js";
|
|
@@ -68,6 +70,7 @@ export const dispatchActions: Record<string, ActionEntry> = {
|
|
|
68
70
|
"get-app-creation-settings": getAppCreationSettings,
|
|
69
71
|
"get-dispatch-settings": getDispatchSettings,
|
|
70
72
|
"get-workspace-info": getWorkspaceInfo,
|
|
73
|
+
"grant-workspace-resources-to-app": grantWorkspaceResourcesToApp,
|
|
71
74
|
"grant-vault-secrets-to-app": grantVaultSecretsToApp,
|
|
72
75
|
"list-connected-agents": listConnectedAgents,
|
|
73
76
|
"list-destinations": listDestinations,
|
|
@@ -83,6 +86,7 @@ export const dispatchActions: Record<string, ActionEntry> = {
|
|
|
83
86
|
"list-vault-secret-options": listVaultSecretOptions,
|
|
84
87
|
"list-vault-secrets": listVaultSecrets,
|
|
85
88
|
"list-workspace-apps": listWorkspaceApps,
|
|
89
|
+
"list-workspace-resource-options": listWorkspaceResourceOptions,
|
|
86
90
|
"list-workspace-resource-grants": listWorkspaceResourceGrants,
|
|
87
91
|
"list-workspace-resources": listWorkspaceResources,
|
|
88
92
|
navigate: navigate,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { listWorkspaceResourceOptions } from "../server/lib/workspace-resources-store.js";
|
|
4
|
+
|
|
5
|
+
export default defineAction({
|
|
6
|
+
description:
|
|
7
|
+
"List lightweight workspace resource options for selectors, without returning full content.",
|
|
8
|
+
schema: z.object({
|
|
9
|
+
kind: z
|
|
10
|
+
.enum(["skill", "instruction", "agent", "knowledge"])
|
|
11
|
+
.optional()
|
|
12
|
+
.describe("Filter by resource kind"),
|
|
13
|
+
}),
|
|
14
|
+
http: { method: "GET" },
|
|
15
|
+
run: async (args) => listWorkspaceResourceOptions(args),
|
|
16
|
+
});
|
|
@@ -4,10 +4,10 @@ import { listWorkspaceResources } from "../server/lib/workspace-resources-store.
|
|
|
4
4
|
|
|
5
5
|
export default defineAction({
|
|
6
6
|
description:
|
|
7
|
-
"List all workspace-wide resources (skills, instructions, agents) that can be shared across apps.",
|
|
7
|
+
"List all workspace-wide resources (skills, instructions, agents, and knowledge packs) that can be shared across apps.",
|
|
8
8
|
schema: z.object({
|
|
9
9
|
kind: z
|
|
10
|
-
.enum(["skill", "instruction", "agent"])
|
|
10
|
+
.enum(["skill", "instruction", "agent", "knowledge"])
|
|
11
11
|
.optional()
|
|
12
12
|
.describe("Filter by resource kind"),
|
|
13
13
|
}),
|
|
@@ -5,7 +5,7 @@ import { startWorkspaceAppCreation } from "../server/lib/app-creation-store.js";
|
|
|
5
5
|
|
|
6
6
|
export default defineAction({
|
|
7
7
|
description:
|
|
8
|
-
"Start creating a new workspace app from Dispatch when the request truly needs its own app. In local dev this returns a code-agent prompt; in production it creates a Builder branch when a Builder project is configured. The result must be a separate workspace app under apps/<app-id>, not a new route or file in apps/starter.",
|
|
8
|
+
"Start creating a new workspace app from Dispatch when the request truly needs its own app. In local dev this returns a code-agent prompt; in production it creates a Builder branch when a Builder project is configured. The result must be a separate workspace app under apps/<app-id>, not a new route or file in apps/starter. If the request needs Mail, Calendar, Analytics, or another first-party app, use the existing hosted/connected app via links or A2A; do not clone, wrap, or nest those templates inside the new app unless the user explicitly asks for a customized copy.",
|
|
9
9
|
schema: z.object({
|
|
10
10
|
prompt: z.string().min(1).describe("The user's app creation request"),
|
|
11
11
|
appId: z
|
|
@@ -28,6 +28,13 @@ export default defineAction({
|
|
|
28
28
|
.max(100)
|
|
29
29
|
.optional()
|
|
30
30
|
.describe("Dispatch vault secret IDs to grant to the app"),
|
|
31
|
+
resourceIds: z
|
|
32
|
+
.array(z.string())
|
|
33
|
+
.max(100)
|
|
34
|
+
.optional()
|
|
35
|
+
.describe(
|
|
36
|
+
"Dispatch workspace resource IDs or knowledge packs to grant to the app",
|
|
37
|
+
),
|
|
31
38
|
}),
|
|
32
39
|
run: async (args) => startWorkspaceAppCreation(args),
|
|
33
40
|
});
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
} from "../server/lib/vault-store.js";
|
|
20
20
|
import { listWorkspaceApps } from "../server/lib/app-creation-store.js";
|
|
21
21
|
import { listDispatchUsageMetrics } from "../server/lib/usage-metrics-store.js";
|
|
22
|
+
import { listWorkspaceResourceOptions } from "../server/lib/workspace-resources-store.js";
|
|
22
23
|
|
|
23
24
|
export default defineAction({
|
|
24
25
|
description:
|
|
@@ -88,6 +89,9 @@ export default defineAction({
|
|
|
88
89
|
.map((g) => ({ secretId: g.secretId, appId: g.appId }));
|
|
89
90
|
screen.vaultPendingRequests = requests;
|
|
90
91
|
}
|
|
92
|
+
if (navigation?.view === "workspace" || navigation?.view === "new-app") {
|
|
93
|
+
screen.workspaceResources = await listWorkspaceResourceOptions();
|
|
94
|
+
}
|
|
91
95
|
|
|
92
96
|
if (Object.keys(screen).length === 0) {
|
|
93
97
|
return "No application state found. Is the app running?";
|
|
@@ -54,9 +54,9 @@ export function AppKeysPopover({
|
|
|
54
54
|
type="button"
|
|
55
55
|
aria-label={`Manage keys for ${appName}`}
|
|
56
56
|
onClick={(event) => {
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
|
|
57
|
+
// Keep parent card click handlers from also firing. Do not
|
|
58
|
+
// preventDefault here: Radix uses the same click to open the
|
|
59
|
+
// popover trigger.
|
|
60
60
|
event.stopPropagation();
|
|
61
61
|
}}
|
|
62
62
|
className="flex h-7 w-7 cursor-pointer items-center justify-center rounded-md border border-transparent text-muted-foreground/70 hover:border-border hover:bg-accent/40 hover:text-foreground"
|
|
@@ -11,8 +11,10 @@ import { getWorkspaceAppIdValidationError } from "@agent-native/core/shared";
|
|
|
11
11
|
import {
|
|
12
12
|
IconArrowLeft,
|
|
13
13
|
IconArrowUpRight,
|
|
14
|
+
IconBook,
|
|
14
15
|
IconCheck,
|
|
15
16
|
IconChevronDown,
|
|
17
|
+
IconFileText,
|
|
16
18
|
IconKey,
|
|
17
19
|
IconLoader2,
|
|
18
20
|
IconPlus,
|
|
@@ -32,6 +34,16 @@ interface VaultSecretOption {
|
|
|
32
34
|
description?: string | null;
|
|
33
35
|
}
|
|
34
36
|
|
|
37
|
+
interface WorkspaceResourceOption {
|
|
38
|
+
id: string;
|
|
39
|
+
kind: "skill" | "instruction" | "agent" | "knowledge";
|
|
40
|
+
name: string;
|
|
41
|
+
description?: string | null;
|
|
42
|
+
path: string;
|
|
43
|
+
scope: "all" | "selected";
|
|
44
|
+
updatedAt?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
35
47
|
interface CreateAppPopoverProps {
|
|
36
48
|
/**
|
|
37
49
|
* Custom trigger element. Defaults to a dashed-border tile that matches the
|
|
@@ -65,11 +77,20 @@ function buildAppCreationPrompt(input: {
|
|
|
65
77
|
appId: string;
|
|
66
78
|
prompt: string;
|
|
67
79
|
selectedKeys: string[];
|
|
80
|
+
selectedResources: WorkspaceResourceOption[];
|
|
68
81
|
}): string {
|
|
69
82
|
const keyList = input.selectedKeys.join(", ");
|
|
70
83
|
const grantRequest = keyList
|
|
71
84
|
? `Requested Dispatch vault key grants for this app: ${keyList}`
|
|
72
85
|
: `Requested Dispatch vault key grants for this app: none`;
|
|
86
|
+
const resourceList = input.selectedResources.length
|
|
87
|
+
? input.selectedResources
|
|
88
|
+
.map(
|
|
89
|
+
(resource) =>
|
|
90
|
+
`- ${resource.name} (${resource.kind}, ${resource.path})`,
|
|
91
|
+
)
|
|
92
|
+
.join("\n")
|
|
93
|
+
: "none";
|
|
73
94
|
|
|
74
95
|
return [
|
|
75
96
|
`Create a new agent-native app in this workspace.`,
|
|
@@ -78,16 +99,22 @@ function buildAppCreationPrompt(input: {
|
|
|
78
99
|
`Suggested app name: ${input.appId} (you may adjust the slug if it conflicts)`,
|
|
79
100
|
`User prompt: ${input.prompt.trim()}`,
|
|
80
101
|
grantRequest,
|
|
102
|
+
`Requested Dispatch workspace resources for this app:\n${resourceList}`,
|
|
81
103
|
``,
|
|
82
104
|
`Pick a starter template that fits the user's prompt — analytics, calendar, content, design, dispatch, forms, mail, slides, clips, or starter when none of the others fit.`,
|
|
83
105
|
`Use the workspace app layout: create it under apps/${input.appId}, mount it at /${input.appId}, keep it on the shared workspace database/hosting model, and avoid table-name collisions by namespacing any new domain tables to the app.`,
|
|
84
106
|
`Use relative workspace links like /${input.appId}. Do not hardcode localhost, 127.0.0.1, 8080, 8100, or any dev port; the active workspace gateway/browser origin owns the port.`,
|
|
85
107
|
`Use the framework/template UI stack: shadcn/ui components and @tabler/icons-react. Do not add lucide-react or another icon library for standard UI.`,
|
|
86
|
-
`If the user's prompt mentions
|
|
108
|
+
`Existing first-party apps are neighbors, not implementation details for this app. If the user's prompt mentions Mail, Calendar, Analytics, Dispatch, or other templates, treat them as existing hosted/connected apps that this app can link to or call through A2A/default connected agents. For example, Mail, Calendar, and Analytics already exist at https://mail.agent-native.com, https://calendar.agent-native.com, and https://analytics.agent-native.com.`,
|
|
109
|
+
`Do not clone first-party templates, create wrapper apps, or scaffold child apps/routes for Mail, Calendar, Analytics, etc. inside apps/${input.appId} just so this app can access them. If the request is a cross-app dashboard or overview, build only the new dashboard/overview app and delegate to the existing apps for domain work.`,
|
|
110
|
+
`Only create another first-party app copy when the user explicitly asks for a customized fork/copy of that app; otherwise keep using the hosted/shared app so improvements to the base template keep flowing to users.`,
|
|
87
111
|
`Do not satisfy this by adding a route, page, component, or file inside apps/starter or another existing app unless the user explicitly asks to modify that existing app.`,
|
|
88
112
|
keyList
|
|
89
113
|
? `After the app exists, grant the selected Dispatch vault keys to appId "${input.appId}" and sync them once the app server is available. Treat these as requested grants, not active grants before creation succeeds.`
|
|
90
114
|
: `Do not grant any Dispatch vault keys unless the user asks later.`,
|
|
115
|
+
input.selectedResources.length
|
|
116
|
+
? `After the app exists, grant the selected Dispatch workspace resources to appId "${input.appId}" and sync them once the app server is available. Add a short note to apps/${input.appId}/AGENTS.md telling the app agent to read relevant shared resources under context/ or the selected resource paths before doing GTM/domain work.`
|
|
117
|
+
: `Do not grant any Dispatch workspace resources unless the user asks later.`,
|
|
91
118
|
``,
|
|
92
119
|
`App readiness requirements before handing off:`,
|
|
93
120
|
`- Ensure apps/${input.appId}/package.json exists; Dispatch discovers workspace apps from apps/<app-id>/package.json, not a separate app registry.`,
|
|
@@ -123,7 +150,7 @@ function actionUrl(basePath: string | null, action: string): string {
|
|
|
123
150
|
}
|
|
124
151
|
|
|
125
152
|
/**
|
|
126
|
-
* Inline two-step app-creation flow: prompt → optional
|
|
153
|
+
* Inline two-step app-creation flow: prompt → optional access picker → submit.
|
|
127
154
|
* Used both in the popover form and in the dedicated `/new-app` page so the
|
|
128
155
|
* same UX shows up everywhere a teammate kicks off a new workspace app.
|
|
129
156
|
*/
|
|
@@ -134,11 +161,14 @@ export function CreateAppFlow({
|
|
|
134
161
|
onClose?: () => void;
|
|
135
162
|
className?: string;
|
|
136
163
|
}) {
|
|
137
|
-
const [step, setStep] = useState<"prompt" | "
|
|
164
|
+
const [step, setStep] = useState<"prompt" | "access">("prompt");
|
|
138
165
|
const [prompt, setPrompt] = useState("");
|
|
139
166
|
const [selectedSecretIds, setSelectedSecretIds] = useState<string[]>([]);
|
|
167
|
+
const [selectedResourceIds, setSelectedResourceIds] = useState<string[]>([]);
|
|
140
168
|
const [secrets, setSecrets] = useState<VaultSecretOption[]>([]);
|
|
169
|
+
const [resources, setResources] = useState<WorkspaceResourceOption[]>([]);
|
|
141
170
|
const [secretsError, setSecretsError] = useState<string | null>(null);
|
|
171
|
+
const [resourcesError, setResourcesError] = useState<string | null>(null);
|
|
142
172
|
const [statusMessage, setStatusMessage] = useState<string | null>(null);
|
|
143
173
|
const [branchUrl, setBranchUrl] = useState<string | null>(null);
|
|
144
174
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
@@ -146,8 +176,7 @@ export function CreateAppFlow({
|
|
|
146
176
|
|
|
147
177
|
const basePath = useMemo(() => defaultDispatchBasePath(), []);
|
|
148
178
|
|
|
149
|
-
// Fetch
|
|
150
|
-
// taps "Choose keys" — no spinner, no pause between steps.
|
|
179
|
+
// Fetch access options eagerly so step 2 has them ready immediately.
|
|
151
180
|
useEffect(() => {
|
|
152
181
|
let cancelled = false;
|
|
153
182
|
fetchJson(actionUrl(basePath, "list-vault-secret-options"))
|
|
@@ -161,6 +190,17 @@ export function CreateAppFlow({
|
|
|
161
190
|
setSecrets([]);
|
|
162
191
|
setSecretsError(err?.message || "Could not load Dispatch keys");
|
|
163
192
|
});
|
|
193
|
+
fetchJson(actionUrl(basePath, "list-workspace-resource-options"))
|
|
194
|
+
.then((data) => {
|
|
195
|
+
if (cancelled) return;
|
|
196
|
+
setResources(Array.isArray(data) ? data : []);
|
|
197
|
+
setResourcesError(null);
|
|
198
|
+
})
|
|
199
|
+
.catch((err) => {
|
|
200
|
+
if (cancelled) return;
|
|
201
|
+
setResources([]);
|
|
202
|
+
setResourcesError(err?.message || "Could not load Dispatch resources");
|
|
203
|
+
});
|
|
164
204
|
return () => {
|
|
165
205
|
cancelled = true;
|
|
166
206
|
};
|
|
@@ -170,10 +210,21 @@ export function CreateAppFlow({
|
|
|
170
210
|
() => secrets.filter((s) => selectedSecretIds.includes(s.id)),
|
|
171
211
|
[secrets, selectedSecretIds],
|
|
172
212
|
);
|
|
213
|
+
const selectedResources = useMemo(
|
|
214
|
+
() => resources.filter((r) => selectedResourceIds.includes(r.id)),
|
|
215
|
+
[resources, selectedResourceIds],
|
|
216
|
+
);
|
|
173
217
|
const selectedSecretLabel =
|
|
174
218
|
selectedSecretIds.length === 0
|
|
175
219
|
? "no keys"
|
|
176
220
|
: `${selectedSecretIds.length} key${selectedSecretIds.length === 1 ? "" : "s"}`;
|
|
221
|
+
const selectedResourceLabel =
|
|
222
|
+
selectedResourceIds.length === 0
|
|
223
|
+
? "no resources"
|
|
224
|
+
: `${selectedResourceIds.length} resource${selectedResourceIds.length === 1 ? "" : "s"}`;
|
|
225
|
+
const selectedAccessLabel = [selectedSecretLabel, selectedResourceLabel].join(
|
|
226
|
+
" · ",
|
|
227
|
+
);
|
|
177
228
|
|
|
178
229
|
function toggleSecret(id: string) {
|
|
179
230
|
setSelectedSecretIds((cur) =>
|
|
@@ -181,7 +232,13 @@ export function CreateAppFlow({
|
|
|
181
232
|
);
|
|
182
233
|
}
|
|
183
234
|
|
|
184
|
-
|
|
235
|
+
function toggleResource(id: string) {
|
|
236
|
+
setSelectedResourceIds((cur) =>
|
|
237
|
+
cur.includes(id) ? cur.filter((x) => x !== id) : [...cur, id],
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async function submit(rawPrompt: string) {
|
|
185
242
|
const trimmed = rawPrompt.trim();
|
|
186
243
|
if (!trimmed || isSubmitting) return;
|
|
187
244
|
const appId = titleFromPrompt(trimmed);
|
|
@@ -194,7 +251,8 @@ export function CreateAppFlow({
|
|
|
194
251
|
const message = buildAppCreationPrompt({
|
|
195
252
|
appId,
|
|
196
253
|
prompt: trimmed,
|
|
197
|
-
selectedKeys,
|
|
254
|
+
selectedKeys: selectedSecrets.map((s) => s.credentialKey),
|
|
255
|
+
selectedResources,
|
|
198
256
|
});
|
|
199
257
|
setIsSubmitting(true);
|
|
200
258
|
setStatusMessage(null);
|
|
@@ -218,7 +276,9 @@ export function CreateAppFlow({
|
|
|
218
276
|
body: JSON.stringify({
|
|
219
277
|
prompt: trimmed,
|
|
220
278
|
appId,
|
|
221
|
-
secretIds:
|
|
279
|
+
secretIds: selectedSecretIds.length > 0 ? selectedSecretIds : [],
|
|
280
|
+
resourceIds:
|
|
281
|
+
selectedResourceIds.length > 0 ? selectedResourceIds : [],
|
|
222
282
|
}),
|
|
223
283
|
},
|
|
224
284
|
);
|
|
@@ -239,11 +299,7 @@ export function CreateAppFlow({
|
|
|
239
299
|
}
|
|
240
300
|
}
|
|
241
301
|
|
|
242
|
-
const
|
|
243
|
-
submit(
|
|
244
|
-
prompt,
|
|
245
|
-
selectedSecrets.map((s) => s.credentialKey),
|
|
246
|
-
);
|
|
302
|
+
const submitWithSelectedAccess = () => submit(prompt);
|
|
247
303
|
|
|
248
304
|
return (
|
|
249
305
|
<div className={`flex flex-col gap-3 ${className}`}>
|
|
@@ -253,11 +309,11 @@ export function CreateAppFlow({
|
|
|
253
309
|
<p className="text-sm font-semibold text-foreground">Create app</p>
|
|
254
310
|
<button
|
|
255
311
|
type="button"
|
|
256
|
-
onClick={() => setStep("
|
|
312
|
+
onClick={() => setStep("access")}
|
|
257
313
|
className="inline-flex cursor-pointer items-center gap-1 rounded-md border border-border bg-background/40 px-2 py-1 text-[11px] text-muted-foreground hover:text-foreground hover:bg-accent/50"
|
|
258
314
|
>
|
|
259
315
|
<IconKey size={11} />
|
|
260
|
-
{
|
|
316
|
+
{selectedAccessLabel}
|
|
261
317
|
</button>
|
|
262
318
|
</div>
|
|
263
319
|
<PromptComposer
|
|
@@ -267,10 +323,7 @@ export function CreateAppFlow({
|
|
|
267
323
|
draftScope="dispatch:create-app"
|
|
268
324
|
onSubmit={(text) => {
|
|
269
325
|
setPrompt(text);
|
|
270
|
-
submit(
|
|
271
|
-
text,
|
|
272
|
-
selectedSecrets.map((s) => s.credentialKey),
|
|
273
|
-
);
|
|
326
|
+
submit(text);
|
|
274
327
|
}}
|
|
275
328
|
/>
|
|
276
329
|
</>
|
|
@@ -286,10 +339,14 @@ export function CreateAppFlow({
|
|
|
286
339
|
Back
|
|
287
340
|
</button>
|
|
288
341
|
<span className="text-[11px] text-muted-foreground/70">
|
|
289
|
-
{
|
|
342
|
+
{selectedAccessLabel}
|
|
290
343
|
</span>
|
|
291
344
|
</div>
|
|
292
|
-
<div className="max-h-[
|
|
345
|
+
<div className="max-h-[180px] space-y-2 overflow-y-auto rounded-md border border-border bg-card p-2">
|
|
346
|
+
<div className="flex items-center gap-1.5 px-1 pb-1 text-[11px] font-medium text-muted-foreground">
|
|
347
|
+
<IconKey size={12} />
|
|
348
|
+
Dispatch keys
|
|
349
|
+
</div>
|
|
293
350
|
{secretsError ? (
|
|
294
351
|
<p className="rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground">
|
|
295
352
|
{secretsError}
|
|
@@ -355,11 +412,87 @@ export function CreateAppFlow({
|
|
|
355
412
|
})
|
|
356
413
|
)}
|
|
357
414
|
</div>
|
|
415
|
+
<div className="max-h-[180px] space-y-2 overflow-y-auto rounded-md border border-border bg-card p-2">
|
|
416
|
+
<div className="flex items-center gap-1.5 px-1 pb-1 text-[11px] font-medium text-muted-foreground">
|
|
417
|
+
<IconBook size={12} />
|
|
418
|
+
Resource packs
|
|
419
|
+
</div>
|
|
420
|
+
{resourcesError ? (
|
|
421
|
+
<p className="rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground">
|
|
422
|
+
{resourcesError}
|
|
423
|
+
</p>
|
|
424
|
+
) : resources.length === 0 ? (
|
|
425
|
+
<p className="rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground">
|
|
426
|
+
No Dispatch resource packs found yet.
|
|
427
|
+
</p>
|
|
428
|
+
) : (
|
|
429
|
+
resources.map((resource) => {
|
|
430
|
+
const selected = selectedResourceIds.includes(resource.id);
|
|
431
|
+
return (
|
|
432
|
+
<div
|
|
433
|
+
key={resource.id}
|
|
434
|
+
className={`group rounded-md border text-sm ${
|
|
435
|
+
selected
|
|
436
|
+
? "border-primary/45 bg-primary/5"
|
|
437
|
+
: "border-border hover:border-muted-foreground/40 hover:bg-accent/35"
|
|
438
|
+
}`}
|
|
439
|
+
>
|
|
440
|
+
<button
|
|
441
|
+
type="button"
|
|
442
|
+
aria-pressed={selected}
|
|
443
|
+
onClick={() => toggleResource(resource.id)}
|
|
444
|
+
className="flex w-full cursor-pointer items-start gap-3 rounded-md px-3 py-2 text-left"
|
|
445
|
+
>
|
|
446
|
+
<span
|
|
447
|
+
className={`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border ${
|
|
448
|
+
selected
|
|
449
|
+
? "border-primary/60 bg-primary/10 text-primary"
|
|
450
|
+
: "border-muted-foreground/35 text-transparent"
|
|
451
|
+
}`}
|
|
452
|
+
>
|
|
453
|
+
{selected ? <IconCheck className="h-3 w-3" /> : null}
|
|
454
|
+
</span>
|
|
455
|
+
<span className="min-w-0 flex-1">
|
|
456
|
+
<span className="flex min-w-0 items-center gap-1.5">
|
|
457
|
+
<IconFileText className="h-3.5 w-3.5 shrink-0 text-muted-foreground/70" />
|
|
458
|
+
<span className="block truncate font-medium">
|
|
459
|
+
{resource.name}
|
|
460
|
+
</span>
|
|
461
|
+
</span>
|
|
462
|
+
<span className="block truncate text-xs text-muted-foreground/70">
|
|
463
|
+
{resource.kind} · {resource.path}
|
|
464
|
+
</span>
|
|
465
|
+
</span>
|
|
466
|
+
</button>
|
|
467
|
+
<details className="group/details border-t border-border/60 px-3 py-1.5 text-xs text-muted-foreground/75">
|
|
468
|
+
<summary className="flex cursor-pointer list-none items-center gap-1.5 text-[11px] hover:text-muted-foreground [&::-webkit-details-marker]:hidden">
|
|
469
|
+
<IconChevronDown className="h-3 w-3 transition-transform group-open/details:rotate-180" />
|
|
470
|
+
Details
|
|
471
|
+
</summary>
|
|
472
|
+
<div className="mt-1.5 space-y-1 pb-0.5 pl-4">
|
|
473
|
+
<div className="truncate">
|
|
474
|
+
Scope:{" "}
|
|
475
|
+
{resource.scope === "all"
|
|
476
|
+
? "All apps"
|
|
477
|
+
: "Selected apps"}
|
|
478
|
+
</div>
|
|
479
|
+
{resource.description ? (
|
|
480
|
+
<div className="line-clamp-2">
|
|
481
|
+
{resource.description}
|
|
482
|
+
</div>
|
|
483
|
+
) : null}
|
|
484
|
+
</div>
|
|
485
|
+
</details>
|
|
486
|
+
</div>
|
|
487
|
+
);
|
|
488
|
+
})
|
|
489
|
+
)}
|
|
490
|
+
</div>
|
|
358
491
|
<div className="flex items-center justify-end gap-2">
|
|
359
492
|
<Button
|
|
360
493
|
type="button"
|
|
361
494
|
size="sm"
|
|
362
|
-
onClick={
|
|
495
|
+
onClick={submitWithSelectedAccess}
|
|
363
496
|
disabled={!prompt.trim() || isSubmitting}
|
|
364
497
|
>
|
|
365
498
|
{isSubmitting ? (
|