@agent-native/dispatch 0.2.12 → 0.2.14

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 (45) hide show
  1. package/dist/actions/start-workspace-app-creation.js +1 -1
  2. package/dist/actions/start-workspace-app-creation.js.map +1 -1
  3. package/dist/components/app-keys-popover.js +3 -3
  4. package/dist/components/app-keys-popover.js.map +1 -1
  5. package/dist/components/create-app-popover.d.ts.map +1 -1
  6. package/dist/components/create-app-popover.js +5 -1
  7. package/dist/components/create-app-popover.js.map +1 -1
  8. package/dist/components/workspace-app-card.d.ts +6 -0
  9. package/dist/components/workspace-app-card.d.ts.map +1 -0
  10. package/dist/components/workspace-app-card.js +12 -0
  11. package/dist/components/workspace-app-card.js.map +1 -0
  12. package/dist/lib/workspace-apps.d.ts +15 -0
  13. package/dist/lib/workspace-apps.d.ts.map +1 -0
  14. package/dist/lib/workspace-apps.js +9 -0
  15. package/dist/lib/workspace-apps.js.map +1 -0
  16. package/dist/routes/pages/apps.$appId.d.ts.map +1 -1
  17. package/dist/routes/pages/apps.$appId.js +1 -5
  18. package/dist/routes/pages/apps.$appId.js.map +1 -1
  19. package/dist/routes/pages/apps.d.ts.map +1 -1
  20. package/dist/routes/pages/apps.js +3 -20
  21. package/dist/routes/pages/apps.js.map +1 -1
  22. package/dist/routes/pages/overview.d.ts.map +1 -1
  23. package/dist/routes/pages/overview.js +2 -20
  24. package/dist/routes/pages/overview.js.map +1 -1
  25. package/dist/server/lib/app-creation-store.d.ts.map +1 -1
  26. package/dist/server/lib/app-creation-store.js +5 -1
  27. package/dist/server/lib/app-creation-store.js.map +1 -1
  28. package/dist/server/plugins/agent-chat.d.ts.map +1 -1
  29. package/dist/server/plugins/agent-chat.js +1 -0
  30. package/dist/server/plugins/agent-chat.js.map +1 -1
  31. package/dist/server/plugins/integrations.d.ts.map +1 -1
  32. package/dist/server/plugins/integrations.js +2 -0
  33. package/dist/server/plugins/integrations.js.map +1 -1
  34. package/package.json +2 -2
  35. package/src/actions/start-workspace-app-creation.ts +1 -1
  36. package/src/components/app-keys-popover.tsx +3 -3
  37. package/src/components/create-app-popover.tsx +5 -1
  38. package/src/components/workspace-app-card.tsx +85 -0
  39. package/src/lib/workspace-apps.ts +21 -0
  40. package/src/routes/pages/apps.$appId.tsx +4 -17
  41. package/src/routes/pages/apps.tsx +6 -89
  42. package/src/routes/pages/overview.tsx +5 -84
  43. package/src/server/lib/app-creation-store.ts +5 -1
  44. package/src/server/plugins/agent-chat.ts +1 -0
  45. package/src/server/plugins/integrations.ts +2 -0
@@ -3,7 +3,7 @@ import { getWorkspaceAppIdValidationError } from "@agent-native/core/shared";
3
3
  import { z } from "zod";
4
4
  import { startWorkspaceAppCreation } from "../server/lib/app-creation-store.js";
5
5
  export default defineAction({
6
- description: "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.",
6
+ description: "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.",
7
7
  schema: z.object({
8
8
  prompt: z.string().min(1).describe("The user's app creation request"),
9
9
  appId: z
@@ -1 +1 @@
1
- {"version":3,"file":"start-workspace-app-creation.js","sourceRoot":"","sources":["../../src/actions/start-workspace-app-creation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,gCAAgC,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AAEhF,eAAe,YAAY,CAAC;IAC1B,WAAW,EACT,qUAAqU;IACvU,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,iCAAiC,CAAC;QACrE,KAAK,EAAE,CAAC;aACL,MAAM,EAAE;aACR,GAAG,CAAC,EAAE,CAAC;aACP,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,gCAAgC,CAAC,KAAK,CAAC,EAAE;YAC3D,OAAO,EACL,yEAAyE;SAC5E,CAAC;aACD,QAAQ,EAAE;aACV,QAAQ,EAAE;aACV,QAAQ,CAAC,+BAA+B,CAAC;QAC5C,QAAQ,EAAE,CAAC;aACR,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,EAAE;aACV,QAAQ,CAAC,wBAAwB,CAAC;QACrC,SAAS,EAAE,CAAC;aACT,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;aACjB,GAAG,CAAC,GAAG,CAAC;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,+CAA+C,CAAC;QAC5D,WAAW,EAAE,CAAC;aACX,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;aACjB,GAAG,CAAC,GAAG,CAAC;aACR,QAAQ,EAAE;aACV,QAAQ,CACP,wEAAwE,CACzE;KACJ,CAAC;IACF,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC;CACrD,CAAC,CAAC","sourcesContent":["import { defineAction } from \"@agent-native/core\";\nimport { getWorkspaceAppIdValidationError } from \"@agent-native/core/shared\";\nimport { z } from \"zod\";\nimport { startWorkspaceAppCreation } from \"../server/lib/app-creation-store.js\";\n\nexport default defineAction({\n description:\n \"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.\",\n schema: z.object({\n prompt: z.string().min(1).describe(\"The user's app creation request\"),\n appId: z\n .string()\n .max(64)\n .refine((appId) => !getWorkspaceAppIdValidationError(appId), {\n message:\n \"Use a non-reserved app id with lowercase letters, numbers, and hyphens.\",\n })\n .optional()\n .nullable()\n .describe(\"Desired workspace app id/path\"),\n template: z\n .string()\n .optional()\n .nullable()\n .describe(\"Template to start from\"),\n secretIds: z\n .array(z.string())\n .max(100)\n .optional()\n .describe(\"Dispatch vault secret IDs to grant to the app\"),\n resourceIds: z\n .array(z.string())\n .max(100)\n .optional()\n .describe(\n \"Dispatch workspace resource IDs or knowledge packs to grant to the app\",\n ),\n }),\n run: async (args) => startWorkspaceAppCreation(args),\n});\n"]}
1
+ {"version":3,"file":"start-workspace-app-creation.js","sourceRoot":"","sources":["../../src/actions/start-workspace-app-creation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,gCAAgC,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AAEhF,eAAe,YAAY,CAAC;IAC1B,WAAW,EACT,+jBAA+jB;IACjkB,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,iCAAiC,CAAC;QACrE,KAAK,EAAE,CAAC;aACL,MAAM,EAAE;aACR,GAAG,CAAC,EAAE,CAAC;aACP,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,gCAAgC,CAAC,KAAK,CAAC,EAAE;YAC3D,OAAO,EACL,yEAAyE;SAC5E,CAAC;aACD,QAAQ,EAAE;aACV,QAAQ,EAAE;aACV,QAAQ,CAAC,+BAA+B,CAAC;QAC5C,QAAQ,EAAE,CAAC;aACR,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,EAAE;aACV,QAAQ,CAAC,wBAAwB,CAAC;QACrC,SAAS,EAAE,CAAC;aACT,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;aACjB,GAAG,CAAC,GAAG,CAAC;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,+CAA+C,CAAC;QAC5D,WAAW,EAAE,CAAC;aACX,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;aACjB,GAAG,CAAC,GAAG,CAAC;aACR,QAAQ,EAAE;aACV,QAAQ,CACP,wEAAwE,CACzE;KACJ,CAAC;IACF,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC;CACrD,CAAC,CAAC","sourcesContent":["import { defineAction } from \"@agent-native/core\";\nimport { getWorkspaceAppIdValidationError } from \"@agent-native/core/shared\";\nimport { z } from \"zod\";\nimport { startWorkspaceAppCreation } from \"../server/lib/app-creation-store.js\";\n\nexport default defineAction({\n description:\n \"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.\",\n schema: z.object({\n prompt: z.string().min(1).describe(\"The user's app creation request\"),\n appId: z\n .string()\n .max(64)\n .refine((appId) => !getWorkspaceAppIdValidationError(appId), {\n message:\n \"Use a non-reserved app id with lowercase letters, numbers, and hyphens.\",\n })\n .optional()\n .nullable()\n .describe(\"Desired workspace app id/path\"),\n template: z\n .string()\n .optional()\n .nullable()\n .describe(\"Template to start from\"),\n secretIds: z\n .array(z.string())\n .max(100)\n .optional()\n .describe(\"Dispatch vault secret IDs to grant to the app\"),\n resourceIds: z\n .array(z.string())\n .max(100)\n .optional()\n .describe(\n \"Dispatch workspace resource IDs or knowledge packs to grant to the app\",\n ),\n }),\n run: async (args) => startWorkspaceAppCreation(args),\n});\n"]}
@@ -8,9 +8,9 @@ import { Button } from "../components/ui/button.js";
8
8
  export function AppKeysPopover({ appId, appName, trigger, align = "end", side = "bottom", }) {
9
9
  const [open, setOpen] = useState(false);
10
10
  return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: trigger ?? (_jsx("button", { type: "button", "aria-label": `Manage keys for ${appName}`, onClick: (event) => {
11
- // Card itself is an <a>; stop the link from navigating when the
12
- // user clicks the cog inside it.
13
- event.preventDefault();
11
+ // Keep parent card click handlers from also firing. Do not
12
+ // preventDefault here: Radix uses the same click to open the
13
+ // popover trigger.
14
14
  event.stopPropagation();
15
15
  }, 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", children: _jsx(IconSettings, { size: 14 }) })) }), _jsx(PopoverContent, { align: align, side: side, sideOffset: 6, className: "w-80 p-3", onClick: (event) => event.stopPropagation(), children: open ? _jsx(AppKeysPanel, { appId: appId, appName: appName }) : null })] }));
16
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"app-keys-popover.js","sourceRoot":"","sources":["../../src/components/app-keys-popover.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useState, type ReactNode } from \"react\";\nimport { useActionMutation, useActionQuery } from \"@agent-native/core/client\";\nimport {\n IconCheck,\n IconLoader2,\n IconRefresh,\n IconSettings,\n} from \"@tabler/icons-react\";\nimport { toast } from \"sonner\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@/components/ui/popover\";\nimport { Button } from \"@/components/ui/button\";\n\ninterface VaultSecret {\n id: string;\n name?: string | null;\n credentialKey: string;\n provider?: string | null;\n description?: string | null;\n}\n\ninterface VaultGrant {\n id: string;\n secretId: string;\n appId: string;\n status?: string | null;\n}\n\ninterface AppKeysPopoverProps {\n appId: string;\n appName: string;\n trigger?: ReactNode;\n align?: \"start\" | \"center\" | \"end\";\n side?: \"top\" | \"right\" | \"bottom\" | \"left\";\n}\n\nexport function AppKeysPopover({\n appId,\n appName,\n trigger,\n align = \"end\",\n side = \"bottom\",\n}: AppKeysPopoverProps) {\n const [open, setOpen] = useState(false);\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n {trigger ?? (\n <button\n type=\"button\"\n aria-label={`Manage keys for ${appName}`}\n onClick={(event) => {\n // Card itself is an <a>; stop the link from navigating when the\n // user clicks the cog inside it.\n event.preventDefault();\n event.stopPropagation();\n }}\n 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\"\n >\n <IconSettings size={14} />\n </button>\n )}\n </PopoverTrigger>\n <PopoverContent\n align={align}\n side={side}\n sideOffset={6}\n className=\"w-80 p-3\"\n onClick={(event) => event.stopPropagation()}\n >\n {open ? <AppKeysPanel appId={appId} appName={appName} /> : null}\n </PopoverContent>\n </Popover>\n );\n}\n\nfunction AppKeysPanel({ appId, appName }: { appId: string; appName: string }) {\n const { data: secrets = [], isLoading: secretsLoading } = useActionQuery(\n \"list-vault-secret-options\",\n {},\n );\n const {\n data: grants = [],\n isLoading: grantsLoading,\n refetch: refetchGrants,\n } = useActionQuery(\"list-vault-grants\", { appId });\n\n const grantBySecretId = useMemo(() => {\n const map = new Map<string, VaultGrant>();\n for (const grant of grants as VaultGrant[]) {\n if (grant.status && grant.status !== \"active\") continue;\n map.set(grant.secretId, grant);\n }\n return map;\n }, [grants]);\n\n // Track per-secret pending state so a fast double-click on the same row\n // can't queue two `create-vault-grant` requests (which would silently\n // create duplicate active grants — a later revoke only clears one).\n const [pendingSecretIds, setPendingSecretIds] = useState<Set<string>>(\n () => new Set(),\n );\n const markPending = (secretId: string, pending: boolean) =>\n setPendingSecretIds((prev) => {\n const next = new Set(prev);\n if (pending) next.add(secretId);\n else next.delete(secretId);\n return next;\n });\n\n const grantMutation = useActionMutation(\"create-vault-grant\", {\n onSuccess: () => refetchGrants(),\n onError: (err) => toast.error(`Could not grant: ${String(err)}`),\n });\n\n const revokeMutation = useActionMutation(\"revoke-vault-grant\", {\n onSuccess: () => refetchGrants(),\n onError: (err) => toast.error(`Could not revoke: ${String(err)}`),\n });\n\n const syncMutation = useActionMutation(\"sync-vault-to-app\", {\n onSuccess: (result: any) => {\n const synced = result?.synced ?? 0;\n toast.success(\n synced > 0\n ? `Synced ${synced} key${synced === 1 ? \"\" : \"s\"} to ${appName}`\n : `${appName} is up to date`,\n );\n },\n onError: (err) => toast.error(`Sync failed: ${String(err)}`),\n });\n\n const isLoading = secretsLoading || grantsLoading;\n const grantedCount = grantBySecretId.size;\n const typedSecrets = secrets as VaultSecret[];\n\n const toggleSecret = (secret: VaultSecret) => {\n if (pendingSecretIds.has(secret.id)) return;\n const existing = grantBySecretId.get(secret.id);\n markPending(secret.id, true);\n const onSettled = () => markPending(secret.id, false);\n if (existing) {\n revokeMutation.mutate({ grantId: existing.id }, { onSettled });\n } else {\n grantMutation.mutate({ secretId: secret.id, appId }, { onSettled });\n }\n };\n\n return (\n <div className=\"flex flex-col gap-3\">\n <div className=\"flex items-center justify-between gap-2 px-1\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-foreground\">\n Keys for {appName}\n </p>\n <p className=\"text-[11px] text-muted-foreground\">\n {grantedCount} of {typedSecrets.length} granted\n </p>\n </div>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={syncMutation.isPending || grantedCount === 0}\n onClick={() => syncMutation.mutate({ appId })}\n className=\"h-7 px-2\"\n >\n {syncMutation.isPending ? (\n <IconLoader2 className=\"h-3 w-3 animate-spin\" />\n ) : (\n <IconRefresh className=\"h-3 w-3\" />\n )}\n <span className=\"ml-1 text-xs\">Sync</span>\n </Button>\n </div>\n\n <div className=\"max-h-[320px] space-y-1.5 overflow-y-auto rounded-md border border-border bg-card p-1.5\">\n {isLoading ? (\n <p className=\"px-3 py-3 text-xs text-muted-foreground\">Loading…</p>\n ) : typedSecrets.length === 0 ? (\n <p className=\"rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground\">\n No vault keys yet. Add one from the Vault page.\n </p>\n ) : (\n typedSecrets.map((secret) => {\n const granted = grantBySecretId.has(secret.id);\n const pending = pendingSecretIds.has(secret.id);\n return (\n <button\n key={secret.id}\n type=\"button\"\n aria-pressed={granted}\n disabled={pending}\n onClick={() => toggleSecret(secret)}\n className={`flex w-full items-start gap-3 rounded-md px-2.5 py-2 text-left text-sm disabled:cursor-not-allowed disabled:opacity-60 ${\n pending ? \"\" : \"cursor-pointer\"\n } ${\n granted\n ? \"border border-primary/45 bg-primary/5\"\n : \"border border-transparent hover:border-muted-foreground/30 hover:bg-accent/35\"\n }`}\n >\n <span\n className={`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border ${\n granted\n ? \"border-primary/60 bg-primary/10 text-primary\"\n : \"border-muted-foreground/35 text-transparent\"\n }`}\n >\n {granted ? <IconCheck className=\"h-3 w-3\" /> : null}\n </span>\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate font-medium\">\n {secret.credentialKey}\n </span>\n <span className=\"block truncate text-xs text-muted-foreground/70\">\n {secret.provider || secret.name || \"Vault secret\"}\n </span>\n </span>\n </button>\n );\n })\n )}\n </div>\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"app-keys-popover.js","sourceRoot":"","sources":["../../src/components/app-keys-popover.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo, useState, type ReactNode } from \"react\";\nimport { useActionMutation, useActionQuery } from \"@agent-native/core/client\";\nimport {\n IconCheck,\n IconLoader2,\n IconRefresh,\n IconSettings,\n} from \"@tabler/icons-react\";\nimport { toast } from \"sonner\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@/components/ui/popover\";\nimport { Button } from \"@/components/ui/button\";\n\ninterface VaultSecret {\n id: string;\n name?: string | null;\n credentialKey: string;\n provider?: string | null;\n description?: string | null;\n}\n\ninterface VaultGrant {\n id: string;\n secretId: string;\n appId: string;\n status?: string | null;\n}\n\ninterface AppKeysPopoverProps {\n appId: string;\n appName: string;\n trigger?: ReactNode;\n align?: \"start\" | \"center\" | \"end\";\n side?: \"top\" | \"right\" | \"bottom\" | \"left\";\n}\n\nexport function AppKeysPopover({\n appId,\n appName,\n trigger,\n align = \"end\",\n side = \"bottom\",\n}: AppKeysPopoverProps) {\n const [open, setOpen] = useState(false);\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n {trigger ?? (\n <button\n type=\"button\"\n aria-label={`Manage keys for ${appName}`}\n onClick={(event) => {\n // Keep parent card click handlers from also firing. Do not\n // preventDefault here: Radix uses the same click to open the\n // popover trigger.\n event.stopPropagation();\n }}\n 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\"\n >\n <IconSettings size={14} />\n </button>\n )}\n </PopoverTrigger>\n <PopoverContent\n align={align}\n side={side}\n sideOffset={6}\n className=\"w-80 p-3\"\n onClick={(event) => event.stopPropagation()}\n >\n {open ? <AppKeysPanel appId={appId} appName={appName} /> : null}\n </PopoverContent>\n </Popover>\n );\n}\n\nfunction AppKeysPanel({ appId, appName }: { appId: string; appName: string }) {\n const { data: secrets = [], isLoading: secretsLoading } = useActionQuery(\n \"list-vault-secret-options\",\n {},\n );\n const {\n data: grants = [],\n isLoading: grantsLoading,\n refetch: refetchGrants,\n } = useActionQuery(\"list-vault-grants\", { appId });\n\n const grantBySecretId = useMemo(() => {\n const map = new Map<string, VaultGrant>();\n for (const grant of grants as VaultGrant[]) {\n if (grant.status && grant.status !== \"active\") continue;\n map.set(grant.secretId, grant);\n }\n return map;\n }, [grants]);\n\n // Track per-secret pending state so a fast double-click on the same row\n // can't queue two `create-vault-grant` requests (which would silently\n // create duplicate active grants — a later revoke only clears one).\n const [pendingSecretIds, setPendingSecretIds] = useState<Set<string>>(\n () => new Set(),\n );\n const markPending = (secretId: string, pending: boolean) =>\n setPendingSecretIds((prev) => {\n const next = new Set(prev);\n if (pending) next.add(secretId);\n else next.delete(secretId);\n return next;\n });\n\n const grantMutation = useActionMutation(\"create-vault-grant\", {\n onSuccess: () => refetchGrants(),\n onError: (err) => toast.error(`Could not grant: ${String(err)}`),\n });\n\n const revokeMutation = useActionMutation(\"revoke-vault-grant\", {\n onSuccess: () => refetchGrants(),\n onError: (err) => toast.error(`Could not revoke: ${String(err)}`),\n });\n\n const syncMutation = useActionMutation(\"sync-vault-to-app\", {\n onSuccess: (result: any) => {\n const synced = result?.synced ?? 0;\n toast.success(\n synced > 0\n ? `Synced ${synced} key${synced === 1 ? \"\" : \"s\"} to ${appName}`\n : `${appName} is up to date`,\n );\n },\n onError: (err) => toast.error(`Sync failed: ${String(err)}`),\n });\n\n const isLoading = secretsLoading || grantsLoading;\n const grantedCount = grantBySecretId.size;\n const typedSecrets = secrets as VaultSecret[];\n\n const toggleSecret = (secret: VaultSecret) => {\n if (pendingSecretIds.has(secret.id)) return;\n const existing = grantBySecretId.get(secret.id);\n markPending(secret.id, true);\n const onSettled = () => markPending(secret.id, false);\n if (existing) {\n revokeMutation.mutate({ grantId: existing.id }, { onSettled });\n } else {\n grantMutation.mutate({ secretId: secret.id, appId }, { onSettled });\n }\n };\n\n return (\n <div className=\"flex flex-col gap-3\">\n <div className=\"flex items-center justify-between gap-2 px-1\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-semibold text-foreground\">\n Keys for {appName}\n </p>\n <p className=\"text-[11px] text-muted-foreground\">\n {grantedCount} of {typedSecrets.length} granted\n </p>\n </div>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={syncMutation.isPending || grantedCount === 0}\n onClick={() => syncMutation.mutate({ appId })}\n className=\"h-7 px-2\"\n >\n {syncMutation.isPending ? (\n <IconLoader2 className=\"h-3 w-3 animate-spin\" />\n ) : (\n <IconRefresh className=\"h-3 w-3\" />\n )}\n <span className=\"ml-1 text-xs\">Sync</span>\n </Button>\n </div>\n\n <div className=\"max-h-[320px] space-y-1.5 overflow-y-auto rounded-md border border-border bg-card p-1.5\">\n {isLoading ? (\n <p className=\"px-3 py-3 text-xs text-muted-foreground\">Loading…</p>\n ) : typedSecrets.length === 0 ? (\n <p className=\"rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground\">\n No vault keys yet. Add one from the Vault page.\n </p>\n ) : (\n typedSecrets.map((secret) => {\n const granted = grantBySecretId.has(secret.id);\n const pending = pendingSecretIds.has(secret.id);\n return (\n <button\n key={secret.id}\n type=\"button\"\n aria-pressed={granted}\n disabled={pending}\n onClick={() => toggleSecret(secret)}\n className={`flex w-full items-start gap-3 rounded-md px-2.5 py-2 text-left text-sm disabled:cursor-not-allowed disabled:opacity-60 ${\n pending ? \"\" : \"cursor-pointer\"\n } ${\n granted\n ? \"border border-primary/45 bg-primary/5\"\n : \"border border-transparent hover:border-muted-foreground/30 hover:bg-accent/35\"\n }`}\n >\n <span\n className={`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border ${\n granted\n ? \"border-primary/60 bg-primary/10 text-primary\"\n : \"border-muted-foreground/35 text-transparent\"\n }`}\n >\n {granted ? <IconCheck className=\"h-3 w-3\" /> : null}\n </span>\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate font-medium\">\n {secret.credentialKey}\n </span>\n <span className=\"block truncate text-xs text-muted-foreground/70\">\n {secret.provider || secret.name || \"Vault secret\"}\n </span>\n </span>\n </button>\n );\n })\n )}\n </div>\n </div>\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"create-app-popover.d.ts","sourceRoot":"","sources":["../../src/components/create-app-popover.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AA8CrE,UAAU,qBAAqB;IAC7B;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;CACpC;AA6FD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,SAAc,GACf,EAAE;IACD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,2CAgXA;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,KAAgB,GACjB,EAAE,qBAAqB,2CA0BvB"}
1
+ {"version":3,"file":"create-app-popover.d.ts","sourceRoot":"","sources":["../../src/components/create-app-popover.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AA8CrE,UAAU,qBAAqB;IAC7B;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;CACpC;AAiGD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,SAAc,GACf,EAAE;IACD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,2CAgXA;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,KAAgB,GACjB,EAAE,qBAAqB,2CA0BvB"}
@@ -41,9 +41,13 @@ function buildAppCreationPrompt(input) {
41
41
  ``,
42
42
  `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.`,
43
43
  `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.`,
44
+ `Important routing rule: from outside the app, link to /${input.appId}; inside apps/${input.appId}, React Router routes are app-local. Use <Link to="/review"> and navigate("/review"), not "/${input.appId}/review"; APP_BASE_PATH supplies the mounted prefix, and hardcoding it causes doubled URLs like /${input.appId}/${input.appId}/review.`,
45
+ `Prefer useActionQuery/useActionMutation for actions. If you must raw-fetch framework endpoints, wrap them with agentNativePath("/_agent-native/actions/<name>") so mounted apps call the right URL.`,
44
46
  `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.`,
45
47
  `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.`,
46
- `If the user's prompt mentions sibling apps like Mail, Calendar, Dispatch, or other templates, treat them as existing workspace neighbors or integrations. Do not scaffold those sibling apps inside apps/${input.appId} unless the user explicitly asks to create them too.`,
48
+ `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.`,
49
+ `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.`,
50
+ `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.`,
47
51
  `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.`,
48
52
  keyList
49
53
  ? `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.`
@@ -1 +1 @@
1
- {"version":3,"file":"create-app-popover.js","sourceRoot":"","sources":["../../src/components/create-app-popover.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useMemo, useState, type ReactNode } from \"react\";\nimport {\n PromptComposer,\n agentNativePath,\n appBasePath,\n isInBuilderFrame,\n sendToAgentChat,\n useDevMode,\n} from \"@agent-native/core/client\";\nimport { getWorkspaceAppIdValidationError } from \"@agent-native/core/shared\";\nimport {\n IconArrowLeft,\n IconArrowUpRight,\n IconBook,\n IconCheck,\n IconChevronDown,\n IconFileText,\n IconKey,\n IconLoader2,\n IconPlus,\n} from \"@tabler/icons-react\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@/components/ui/popover\";\nimport { Button } from \"@/components/ui/button\";\n\ninterface VaultSecretOption {\n id: string;\n name: string;\n credentialKey: string;\n provider?: string | null;\n description?: string | null;\n}\n\ninterface WorkspaceResourceOption {\n id: string;\n kind: \"skill\" | \"instruction\" | \"agent\" | \"knowledge\";\n name: string;\n description?: string | null;\n path: string;\n scope: \"all\" | \"selected\";\n updatedAt?: number;\n}\n\ninterface CreateAppPopoverProps {\n /**\n * Custom trigger element. Defaults to a dashed-border tile that matches the\n * apps grid empty state.\n */\n trigger?: ReactNode;\n /**\n * Override the popover alignment. Defaults to \"center\" with a 10px offset.\n */\n align?: \"start\" | \"center\" | \"end\";\n}\n\nfunction slugify(value: string): string {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .replace(/^[^a-z]+/, \"\")\n .slice(0, 48);\n}\n\nfunction titleFromPrompt(prompt: string): string {\n const cleaned = prompt\n .replace(/\\b(build|create|make|an?|the|app|tool|dashboard)\\b/gi, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n return slugify(cleaned || \"new-app\") || \"new-app\";\n}\n\nfunction buildAppCreationPrompt(input: {\n appId: string;\n prompt: string;\n selectedKeys: string[];\n selectedResources: WorkspaceResourceOption[];\n}): string {\n const keyList = input.selectedKeys.join(\", \");\n const grantRequest = keyList\n ? `Requested Dispatch vault key grants for this app: ${keyList}`\n : `Requested Dispatch vault key grants for this app: none`;\n const resourceList = input.selectedResources.length\n ? input.selectedResources\n .map(\n (resource) =>\n `- ${resource.name} (${resource.kind}, ${resource.path})`,\n )\n .join(\"\\n\")\n : \"none\";\n\n return [\n `Create a new agent-native app in this workspace.`,\n `This is a new workspace app request, not a feature request for the current app.`,\n ``,\n `Suggested app name: ${input.appId} (you may adjust the slug if it conflicts)`,\n `User prompt: ${input.prompt.trim()}`,\n grantRequest,\n `Requested Dispatch workspace resources for this app:\\n${resourceList}`,\n ``,\n `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.`,\n `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.`,\n `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.`,\n `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.`,\n `If the user's prompt mentions sibling apps like Mail, Calendar, Dispatch, or other templates, treat them as existing workspace neighbors or integrations. Do not scaffold those sibling apps inside apps/${input.appId} unless the user explicitly asks to create them too.`,\n `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.`,\n keyList\n ? `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.`\n : `Do not grant any Dispatch vault keys unless the user asks later.`,\n input.selectedResources.length\n ? `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.`\n : `Do not grant any Dispatch workspace resources unless the user asks later.`,\n ``,\n `App readiness requirements before handing off:`,\n `- Ensure apps/${input.appId}/package.json exists; Dispatch discovers workspace apps from apps/<app-id>/package.json, not a separate app registry.`,\n `- Update the app manifest/package/deploy metadata needed by the existing workspace deployment model.`,\n `- Ensure the React Router client entry preserves APP_BASE_PATH/VITE_APP_BASE_PATH via appBasePath() so /${input.appId} hydrates correctly.`,\n `- Verify the app's agent card/A2A metadata is ready so Dispatch can discover and delegate to the app after deployment.`,\n `When it is ready, start or update the workspace dev server and navigate the user to /${input.appId}.`,\n ].join(\"\\n\");\n}\n\nasync function fetchJson(url: string, init?: RequestInit): Promise<any> {\n const res = await fetch(url, init);\n const data = await res.json().catch(() => null);\n if (!res.ok) {\n throw new Error(\n data?.error || data?.message || `Request failed ${res.status}`,\n );\n }\n return data;\n}\n\nfunction defaultDispatchBasePath(): string | null {\n const base = appBasePath();\n if (base === \"/dispatch\") return null;\n return null;\n}\n\nfunction actionUrl(basePath: string | null, action: string): string {\n const path = `/_agent-native/actions/${action}`;\n if (basePath === null) return agentNativePath(path);\n const normalized = basePath.replace(/\\/+$/, \"\");\n return `${normalized}${path}`;\n}\n\n/**\n * Inline two-step app-creation flow: prompt → optional access picker → submit.\n * Used both in the popover form and in the dedicated `/new-app` page so the\n * same UX shows up everywhere a teammate kicks off a new workspace app.\n */\nexport function CreateAppFlow({\n onClose,\n className = \"\",\n}: {\n onClose?: () => void;\n className?: string;\n}) {\n const [step, setStep] = useState<\"prompt\" | \"access\">(\"prompt\");\n const [prompt, setPrompt] = useState(\"\");\n const [selectedSecretIds, setSelectedSecretIds] = useState<string[]>([]);\n const [selectedResourceIds, setSelectedResourceIds] = useState<string[]>([]);\n const [secrets, setSecrets] = useState<VaultSecretOption[]>([]);\n const [resources, setResources] = useState<WorkspaceResourceOption[]>([]);\n const [secretsError, setSecretsError] = useState<string | null>(null);\n const [resourcesError, setResourcesError] = useState<string | null>(null);\n const [statusMessage, setStatusMessage] = useState<string | null>(null);\n const [branchUrl, setBranchUrl] = useState<string | null>(null);\n const [isSubmitting, setIsSubmitting] = useState(false);\n const { isDevMode } = useDevMode();\n\n const basePath = useMemo(() => defaultDispatchBasePath(), []);\n\n // Fetch access options eagerly so step 2 has them ready immediately.\n useEffect(() => {\n let cancelled = false;\n fetchJson(actionUrl(basePath, \"list-vault-secret-options\"))\n .then((data) => {\n if (cancelled) return;\n setSecrets(Array.isArray(data) ? data : []);\n setSecretsError(null);\n })\n .catch((err) => {\n if (cancelled) return;\n setSecrets([]);\n setSecretsError(err?.message || \"Could not load Dispatch keys\");\n });\n fetchJson(actionUrl(basePath, \"list-workspace-resource-options\"))\n .then((data) => {\n if (cancelled) return;\n setResources(Array.isArray(data) ? data : []);\n setResourcesError(null);\n })\n .catch((err) => {\n if (cancelled) return;\n setResources([]);\n setResourcesError(err?.message || \"Could not load Dispatch resources\");\n });\n return () => {\n cancelled = true;\n };\n }, [basePath]);\n\n const selectedSecrets = useMemo(\n () => secrets.filter((s) => selectedSecretIds.includes(s.id)),\n [secrets, selectedSecretIds],\n );\n const selectedResources = useMemo(\n () => resources.filter((r) => selectedResourceIds.includes(r.id)),\n [resources, selectedResourceIds],\n );\n const selectedSecretLabel =\n selectedSecretIds.length === 0\n ? \"no keys\"\n : `${selectedSecretIds.length} key${selectedSecretIds.length === 1 ? \"\" : \"s\"}`;\n const selectedResourceLabel =\n selectedResourceIds.length === 0\n ? \"no resources\"\n : `${selectedResourceIds.length} resource${selectedResourceIds.length === 1 ? \"\" : \"s\"}`;\n const selectedAccessLabel = [selectedSecretLabel, selectedResourceLabel].join(\n \" · \",\n );\n\n function toggleSecret(id: string) {\n setSelectedSecretIds((cur) =>\n cur.includes(id) ? cur.filter((x) => x !== id) : [...cur, id],\n );\n }\n\n function toggleResource(id: string) {\n setSelectedResourceIds((cur) =>\n cur.includes(id) ? cur.filter((x) => x !== id) : [...cur, id],\n );\n }\n\n async function submit(rawPrompt: string) {\n const trimmed = rawPrompt.trim();\n if (!trimmed || isSubmitting) return;\n const appId = titleFromPrompt(trimmed);\n const validationError = getWorkspaceAppIdValidationError(appId);\n if (validationError) {\n setStatusMessage(validationError);\n return;\n }\n\n const message = buildAppCreationPrompt({\n appId,\n prompt: trimmed,\n selectedKeys: selectedSecrets.map((s) => s.credentialKey),\n selectedResources,\n });\n setIsSubmitting(true);\n setStatusMessage(null);\n setBranchUrl(null);\n\n try {\n if (isInBuilderFrame()) {\n sendToAgentChat({ message, submit: true, type: \"code\" });\n setStatusMessage(\"Sent to Builder chat.\");\n onClose?.();\n } else if (isDevMode) {\n sendToAgentChat({ message, submit: true, type: \"code\", newTab: true });\n setStatusMessage(\"Sent to the local agent.\");\n onClose?.();\n } else {\n const result = await fetchJson(\n actionUrl(basePath, \"start-workspace-app-creation\"),\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n prompt: trimmed,\n appId,\n secretIds: selectedSecretIds.length > 0 ? selectedSecretIds : [],\n resourceIds:\n selectedResourceIds.length > 0 ? selectedResourceIds : [],\n }),\n },\n );\n if (result?.mode === \"builder\") {\n setBranchUrl(result?.url || null);\n setStatusMessage(\"Builder branch created.\");\n } else {\n setStatusMessage(\n result?.message ||\n \"Builder app creation is coming soon. Open this workspace in Builder to create an app from this prompt.\",\n );\n }\n }\n } catch (err: any) {\n setStatusMessage(err?.message || \"Could not start the new app flow.\");\n } finally {\n setIsSubmitting(false);\n }\n }\n\n const submitWithSelectedAccess = () => submit(prompt);\n\n return (\n <div className={`flex flex-col gap-3 ${className}`}>\n {step === \"prompt\" ? (\n <>\n <div className=\"flex items-center justify-between gap-2 px-1\">\n <p className=\"text-sm font-semibold text-foreground\">Create app</p>\n <button\n type=\"button\"\n onClick={() => setStep(\"access\")}\n 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\"\n >\n <IconKey size={11} />\n {selectedAccessLabel}\n </button>\n </div>\n <PromptComposer\n autoFocus\n disabled={isSubmitting}\n placeholder=\"Describe the app your teammate should be able to use...\"\n draftScope=\"dispatch:create-app\"\n onSubmit={(text) => {\n setPrompt(text);\n submit(text);\n }}\n />\n </>\n ) : (\n <>\n <div className=\"flex items-center justify-between gap-2 px-1\">\n <button\n type=\"button\"\n onClick={() => setStep(\"prompt\")}\n className=\"inline-flex cursor-pointer items-center gap-1 text-xs text-muted-foreground hover:text-foreground\"\n >\n <IconArrowLeft size={12} />\n Back\n </button>\n <span className=\"text-[11px] text-muted-foreground/70\">\n {selectedAccessLabel}\n </span>\n </div>\n <div className=\"max-h-[180px] space-y-2 overflow-y-auto rounded-md border border-border bg-card p-2\">\n <div className=\"flex items-center gap-1.5 px-1 pb-1 text-[11px] font-medium text-muted-foreground\">\n <IconKey size={12} />\n Dispatch keys\n </div>\n {secretsError ? (\n <p className=\"rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground\">\n {secretsError}\n </p>\n ) : secrets.length === 0 ? (\n <p className=\"rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground\">\n No Dispatch vault keys found yet.\n </p>\n ) : (\n secrets.map((secret) => {\n const selected = selectedSecretIds.includes(secret.id);\n return (\n <div\n key={secret.id}\n className={`group rounded-md border text-sm ${\n selected\n ? \"border-primary/45 bg-primary/5\"\n : \"border-border hover:border-muted-foreground/40 hover:bg-accent/35\"\n }`}\n >\n <button\n type=\"button\"\n aria-pressed={selected}\n onClick={() => toggleSecret(secret.id)}\n className=\"flex w-full cursor-pointer items-start gap-3 rounded-md px-3 py-2 text-left\"\n >\n <span\n className={`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border ${\n selected\n ? \"border-primary/60 bg-primary/10 text-primary\"\n : \"border-muted-foreground/35 text-transparent\"\n }`}\n >\n {selected ? <IconCheck className=\"h-3 w-3\" /> : null}\n </span>\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate font-medium\">\n {secret.credentialKey}\n </span>\n <span className=\"block truncate text-xs text-muted-foreground/70\">\n {selected\n ? \"Will be requested for this app\"\n : \"Click to request\"}\n </span>\n </span>\n </button>\n {(secret.provider || secret.name) && (\n <details className=\"group/details border-t border-border/60 px-3 py-1.5 text-xs text-muted-foreground/75\">\n <summary className=\"flex cursor-pointer list-none items-center gap-1.5 text-[11px] hover:text-muted-foreground [&::-webkit-details-marker]:hidden\">\n <IconChevronDown className=\"h-3 w-3 transition-transform group-open/details:rotate-180\" />\n Details\n </summary>\n <div className=\"mt-1.5 space-y-1 pb-0.5 pl-4\">\n <div className=\"truncate\">\n Provider: {secret.provider || \"Not specified\"}\n </div>\n <div className=\"truncate\">Name: {secret.name}</div>\n </div>\n </details>\n )}\n </div>\n );\n })\n )}\n </div>\n <div className=\"max-h-[180px] space-y-2 overflow-y-auto rounded-md border border-border bg-card p-2\">\n <div className=\"flex items-center gap-1.5 px-1 pb-1 text-[11px] font-medium text-muted-foreground\">\n <IconBook size={12} />\n Resource packs\n </div>\n {resourcesError ? (\n <p className=\"rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground\">\n {resourcesError}\n </p>\n ) : resources.length === 0 ? (\n <p className=\"rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground\">\n No Dispatch resource packs found yet.\n </p>\n ) : (\n resources.map((resource) => {\n const selected = selectedResourceIds.includes(resource.id);\n return (\n <div\n key={resource.id}\n className={`group rounded-md border text-sm ${\n selected\n ? \"border-primary/45 bg-primary/5\"\n : \"border-border hover:border-muted-foreground/40 hover:bg-accent/35\"\n }`}\n >\n <button\n type=\"button\"\n aria-pressed={selected}\n onClick={() => toggleResource(resource.id)}\n className=\"flex w-full cursor-pointer items-start gap-3 rounded-md px-3 py-2 text-left\"\n >\n <span\n className={`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border ${\n selected\n ? \"border-primary/60 bg-primary/10 text-primary\"\n : \"border-muted-foreground/35 text-transparent\"\n }`}\n >\n {selected ? <IconCheck className=\"h-3 w-3\" /> : null}\n </span>\n <span className=\"min-w-0 flex-1\">\n <span className=\"flex min-w-0 items-center gap-1.5\">\n <IconFileText className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground/70\" />\n <span className=\"block truncate font-medium\">\n {resource.name}\n </span>\n </span>\n <span className=\"block truncate text-xs text-muted-foreground/70\">\n {resource.kind} · {resource.path}\n </span>\n </span>\n </button>\n <details className=\"group/details border-t border-border/60 px-3 py-1.5 text-xs text-muted-foreground/75\">\n <summary className=\"flex cursor-pointer list-none items-center gap-1.5 text-[11px] hover:text-muted-foreground [&::-webkit-details-marker]:hidden\">\n <IconChevronDown className=\"h-3 w-3 transition-transform group-open/details:rotate-180\" />\n Details\n </summary>\n <div className=\"mt-1.5 space-y-1 pb-0.5 pl-4\">\n <div className=\"truncate\">\n Scope:{\" \"}\n {resource.scope === \"all\"\n ? \"All apps\"\n : \"Selected apps\"}\n </div>\n {resource.description ? (\n <div className=\"line-clamp-2\">\n {resource.description}\n </div>\n ) : null}\n </div>\n </details>\n </div>\n );\n })\n )}\n </div>\n <div className=\"flex items-center justify-end gap-2\">\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={submitWithSelectedAccess}\n disabled={!prompt.trim() || isSubmitting}\n >\n {isSubmitting ? (\n <IconLoader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <IconPlus className=\"h-3.5 w-3.5\" />\n )}\n Create app\n </Button>\n </div>\n {!prompt.trim() ? (\n <p className=\"px-1 text-[11px] text-muted-foreground/70\">\n Add a prompt on the previous step before creating the app.\n </p>\n ) : null}\n </>\n )}\n\n {statusMessage ? (\n <div className=\"rounded-md border border-border bg-muted/40 px-3 py-2 text-xs text-muted-foreground\">\n {statusMessage}\n {branchUrl ? (\n <a\n href={branchUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"ml-2 inline-flex items-center gap-1 font-medium text-foreground underline\"\n >\n Open branch <IconArrowUpRight className=\"h-3 w-3\" />\n </a>\n ) : null}\n </div>\n ) : null}\n </div>\n );\n}\n\nexport function CreateAppPopover({\n trigger,\n align = \"center\",\n}: CreateAppPopoverProps) {\n const [open, setOpen] = useState(false);\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n {trigger ?? (\n <button\n type=\"button\"\n className=\"flex min-h-32 cursor-pointer items-center justify-center rounded-lg border border-dashed bg-card p-4 text-sm font-medium text-muted-foreground transition hover:border-foreground/30 hover:text-foreground\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <IconPlus size={16} />\n Create app\n </span>\n </button>\n )}\n </PopoverTrigger>\n <PopoverContent\n align={align}\n sideOffset={10}\n className=\"w-[calc(100vw-2rem)] rounded-xl p-3 shadow-xl sm:w-[460px]\"\n >\n <CreateAppFlow onClose={() => setOpen(false)} />\n </PopoverContent>\n </Popover>\n );\n}\n"]}
1
+ {"version":3,"file":"create-app-popover.js","sourceRoot":"","sources":["../../src/components/create-app-popover.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useMemo, useState, type ReactNode } from \"react\";\nimport {\n PromptComposer,\n agentNativePath,\n appBasePath,\n isInBuilderFrame,\n sendToAgentChat,\n useDevMode,\n} from \"@agent-native/core/client\";\nimport { getWorkspaceAppIdValidationError } from \"@agent-native/core/shared\";\nimport {\n IconArrowLeft,\n IconArrowUpRight,\n IconBook,\n IconCheck,\n IconChevronDown,\n IconFileText,\n IconKey,\n IconLoader2,\n IconPlus,\n} from \"@tabler/icons-react\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@/components/ui/popover\";\nimport { Button } from \"@/components/ui/button\";\n\ninterface VaultSecretOption {\n id: string;\n name: string;\n credentialKey: string;\n provider?: string | null;\n description?: string | null;\n}\n\ninterface WorkspaceResourceOption {\n id: string;\n kind: \"skill\" | \"instruction\" | \"agent\" | \"knowledge\";\n name: string;\n description?: string | null;\n path: string;\n scope: \"all\" | \"selected\";\n updatedAt?: number;\n}\n\ninterface CreateAppPopoverProps {\n /**\n * Custom trigger element. Defaults to a dashed-border tile that matches the\n * apps grid empty state.\n */\n trigger?: ReactNode;\n /**\n * Override the popover alignment. Defaults to \"center\" with a 10px offset.\n */\n align?: \"start\" | \"center\" | \"end\";\n}\n\nfunction slugify(value: string): string {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .replace(/^[^a-z]+/, \"\")\n .slice(0, 48);\n}\n\nfunction titleFromPrompt(prompt: string): string {\n const cleaned = prompt\n .replace(/\\b(build|create|make|an?|the|app|tool|dashboard)\\b/gi, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n return slugify(cleaned || \"new-app\") || \"new-app\";\n}\n\nfunction buildAppCreationPrompt(input: {\n appId: string;\n prompt: string;\n selectedKeys: string[];\n selectedResources: WorkspaceResourceOption[];\n}): string {\n const keyList = input.selectedKeys.join(\", \");\n const grantRequest = keyList\n ? `Requested Dispatch vault key grants for this app: ${keyList}`\n : `Requested Dispatch vault key grants for this app: none`;\n const resourceList = input.selectedResources.length\n ? input.selectedResources\n .map(\n (resource) =>\n `- ${resource.name} (${resource.kind}, ${resource.path})`,\n )\n .join(\"\\n\")\n : \"none\";\n\n return [\n `Create a new agent-native app in this workspace.`,\n `This is a new workspace app request, not a feature request for the current app.`,\n ``,\n `Suggested app name: ${input.appId} (you may adjust the slug if it conflicts)`,\n `User prompt: ${input.prompt.trim()}`,\n grantRequest,\n `Requested Dispatch workspace resources for this app:\\n${resourceList}`,\n ``,\n `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.`,\n `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.`,\n `Important routing rule: from outside the app, link to /${input.appId}; inside apps/${input.appId}, React Router routes are app-local. Use <Link to=\"/review\"> and navigate(\"/review\"), not \"/${input.appId}/review\"; APP_BASE_PATH supplies the mounted prefix, and hardcoding it causes doubled URLs like /${input.appId}/${input.appId}/review.`,\n `Prefer useActionQuery/useActionMutation for actions. If you must raw-fetch framework endpoints, wrap them with agentNativePath(\"/_agent-native/actions/<name>\") so mounted apps call the right URL.`,\n `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.`,\n `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.`,\n `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.`,\n `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.`,\n `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.`,\n `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.`,\n keyList\n ? `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.`\n : `Do not grant any Dispatch vault keys unless the user asks later.`,\n input.selectedResources.length\n ? `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.`\n : `Do not grant any Dispatch workspace resources unless the user asks later.`,\n ``,\n `App readiness requirements before handing off:`,\n `- Ensure apps/${input.appId}/package.json exists; Dispatch discovers workspace apps from apps/<app-id>/package.json, not a separate app registry.`,\n `- Update the app manifest/package/deploy metadata needed by the existing workspace deployment model.`,\n `- Ensure the React Router client entry preserves APP_BASE_PATH/VITE_APP_BASE_PATH via appBasePath() so /${input.appId} hydrates correctly.`,\n `- Verify the app's agent card/A2A metadata is ready so Dispatch can discover and delegate to the app after deployment.`,\n `When it is ready, start or update the workspace dev server and navigate the user to /${input.appId}.`,\n ].join(\"\\n\");\n}\n\nasync function fetchJson(url: string, init?: RequestInit): Promise<any> {\n const res = await fetch(url, init);\n const data = await res.json().catch(() => null);\n if (!res.ok) {\n throw new Error(\n data?.error || data?.message || `Request failed ${res.status}`,\n );\n }\n return data;\n}\n\nfunction defaultDispatchBasePath(): string | null {\n const base = appBasePath();\n if (base === \"/dispatch\") return null;\n return null;\n}\n\nfunction actionUrl(basePath: string | null, action: string): string {\n const path = `/_agent-native/actions/${action}`;\n if (basePath === null) return agentNativePath(path);\n const normalized = basePath.replace(/\\/+$/, \"\");\n return `${normalized}${path}`;\n}\n\n/**\n * Inline two-step app-creation flow: prompt → optional access picker → submit.\n * Used both in the popover form and in the dedicated `/new-app` page so the\n * same UX shows up everywhere a teammate kicks off a new workspace app.\n */\nexport function CreateAppFlow({\n onClose,\n className = \"\",\n}: {\n onClose?: () => void;\n className?: string;\n}) {\n const [step, setStep] = useState<\"prompt\" | \"access\">(\"prompt\");\n const [prompt, setPrompt] = useState(\"\");\n const [selectedSecretIds, setSelectedSecretIds] = useState<string[]>([]);\n const [selectedResourceIds, setSelectedResourceIds] = useState<string[]>([]);\n const [secrets, setSecrets] = useState<VaultSecretOption[]>([]);\n const [resources, setResources] = useState<WorkspaceResourceOption[]>([]);\n const [secretsError, setSecretsError] = useState<string | null>(null);\n const [resourcesError, setResourcesError] = useState<string | null>(null);\n const [statusMessage, setStatusMessage] = useState<string | null>(null);\n const [branchUrl, setBranchUrl] = useState<string | null>(null);\n const [isSubmitting, setIsSubmitting] = useState(false);\n const { isDevMode } = useDevMode();\n\n const basePath = useMemo(() => defaultDispatchBasePath(), []);\n\n // Fetch access options eagerly so step 2 has them ready immediately.\n useEffect(() => {\n let cancelled = false;\n fetchJson(actionUrl(basePath, \"list-vault-secret-options\"))\n .then((data) => {\n if (cancelled) return;\n setSecrets(Array.isArray(data) ? data : []);\n setSecretsError(null);\n })\n .catch((err) => {\n if (cancelled) return;\n setSecrets([]);\n setSecretsError(err?.message || \"Could not load Dispatch keys\");\n });\n fetchJson(actionUrl(basePath, \"list-workspace-resource-options\"))\n .then((data) => {\n if (cancelled) return;\n setResources(Array.isArray(data) ? data : []);\n setResourcesError(null);\n })\n .catch((err) => {\n if (cancelled) return;\n setResources([]);\n setResourcesError(err?.message || \"Could not load Dispatch resources\");\n });\n return () => {\n cancelled = true;\n };\n }, [basePath]);\n\n const selectedSecrets = useMemo(\n () => secrets.filter((s) => selectedSecretIds.includes(s.id)),\n [secrets, selectedSecretIds],\n );\n const selectedResources = useMemo(\n () => resources.filter((r) => selectedResourceIds.includes(r.id)),\n [resources, selectedResourceIds],\n );\n const selectedSecretLabel =\n selectedSecretIds.length === 0\n ? \"no keys\"\n : `${selectedSecretIds.length} key${selectedSecretIds.length === 1 ? \"\" : \"s\"}`;\n const selectedResourceLabel =\n selectedResourceIds.length === 0\n ? \"no resources\"\n : `${selectedResourceIds.length} resource${selectedResourceIds.length === 1 ? \"\" : \"s\"}`;\n const selectedAccessLabel = [selectedSecretLabel, selectedResourceLabel].join(\n \" · \",\n );\n\n function toggleSecret(id: string) {\n setSelectedSecretIds((cur) =>\n cur.includes(id) ? cur.filter((x) => x !== id) : [...cur, id],\n );\n }\n\n function toggleResource(id: string) {\n setSelectedResourceIds((cur) =>\n cur.includes(id) ? cur.filter((x) => x !== id) : [...cur, id],\n );\n }\n\n async function submit(rawPrompt: string) {\n const trimmed = rawPrompt.trim();\n if (!trimmed || isSubmitting) return;\n const appId = titleFromPrompt(trimmed);\n const validationError = getWorkspaceAppIdValidationError(appId);\n if (validationError) {\n setStatusMessage(validationError);\n return;\n }\n\n const message = buildAppCreationPrompt({\n appId,\n prompt: trimmed,\n selectedKeys: selectedSecrets.map((s) => s.credentialKey),\n selectedResources,\n });\n setIsSubmitting(true);\n setStatusMessage(null);\n setBranchUrl(null);\n\n try {\n if (isInBuilderFrame()) {\n sendToAgentChat({ message, submit: true, type: \"code\" });\n setStatusMessage(\"Sent to Builder chat.\");\n onClose?.();\n } else if (isDevMode) {\n sendToAgentChat({ message, submit: true, type: \"code\", newTab: true });\n setStatusMessage(\"Sent to the local agent.\");\n onClose?.();\n } else {\n const result = await fetchJson(\n actionUrl(basePath, \"start-workspace-app-creation\"),\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n prompt: trimmed,\n appId,\n secretIds: selectedSecretIds.length > 0 ? selectedSecretIds : [],\n resourceIds:\n selectedResourceIds.length > 0 ? selectedResourceIds : [],\n }),\n },\n );\n if (result?.mode === \"builder\") {\n setBranchUrl(result?.url || null);\n setStatusMessage(\"Builder branch created.\");\n } else {\n setStatusMessage(\n result?.message ||\n \"Builder app creation is coming soon. Open this workspace in Builder to create an app from this prompt.\",\n );\n }\n }\n } catch (err: any) {\n setStatusMessage(err?.message || \"Could not start the new app flow.\");\n } finally {\n setIsSubmitting(false);\n }\n }\n\n const submitWithSelectedAccess = () => submit(prompt);\n\n return (\n <div className={`flex flex-col gap-3 ${className}`}>\n {step === \"prompt\" ? (\n <>\n <div className=\"flex items-center justify-between gap-2 px-1\">\n <p className=\"text-sm font-semibold text-foreground\">Create app</p>\n <button\n type=\"button\"\n onClick={() => setStep(\"access\")}\n 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\"\n >\n <IconKey size={11} />\n {selectedAccessLabel}\n </button>\n </div>\n <PromptComposer\n autoFocus\n disabled={isSubmitting}\n placeholder=\"Describe the app your teammate should be able to use...\"\n draftScope=\"dispatch:create-app\"\n onSubmit={(text) => {\n setPrompt(text);\n submit(text);\n }}\n />\n </>\n ) : (\n <>\n <div className=\"flex items-center justify-between gap-2 px-1\">\n <button\n type=\"button\"\n onClick={() => setStep(\"prompt\")}\n className=\"inline-flex cursor-pointer items-center gap-1 text-xs text-muted-foreground hover:text-foreground\"\n >\n <IconArrowLeft size={12} />\n Back\n </button>\n <span className=\"text-[11px] text-muted-foreground/70\">\n {selectedAccessLabel}\n </span>\n </div>\n <div className=\"max-h-[180px] space-y-2 overflow-y-auto rounded-md border border-border bg-card p-2\">\n <div className=\"flex items-center gap-1.5 px-1 pb-1 text-[11px] font-medium text-muted-foreground\">\n <IconKey size={12} />\n Dispatch keys\n </div>\n {secretsError ? (\n <p className=\"rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground\">\n {secretsError}\n </p>\n ) : secrets.length === 0 ? (\n <p className=\"rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground\">\n No Dispatch vault keys found yet.\n </p>\n ) : (\n secrets.map((secret) => {\n const selected = selectedSecretIds.includes(secret.id);\n return (\n <div\n key={secret.id}\n className={`group rounded-md border text-sm ${\n selected\n ? \"border-primary/45 bg-primary/5\"\n : \"border-border hover:border-muted-foreground/40 hover:bg-accent/35\"\n }`}\n >\n <button\n type=\"button\"\n aria-pressed={selected}\n onClick={() => toggleSecret(secret.id)}\n className=\"flex w-full cursor-pointer items-start gap-3 rounded-md px-3 py-2 text-left\"\n >\n <span\n className={`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border ${\n selected\n ? \"border-primary/60 bg-primary/10 text-primary\"\n : \"border-muted-foreground/35 text-transparent\"\n }`}\n >\n {selected ? <IconCheck className=\"h-3 w-3\" /> : null}\n </span>\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate font-medium\">\n {secret.credentialKey}\n </span>\n <span className=\"block truncate text-xs text-muted-foreground/70\">\n {selected\n ? \"Will be requested for this app\"\n : \"Click to request\"}\n </span>\n </span>\n </button>\n {(secret.provider || secret.name) && (\n <details className=\"group/details border-t border-border/60 px-3 py-1.5 text-xs text-muted-foreground/75\">\n <summary className=\"flex cursor-pointer list-none items-center gap-1.5 text-[11px] hover:text-muted-foreground [&::-webkit-details-marker]:hidden\">\n <IconChevronDown className=\"h-3 w-3 transition-transform group-open/details:rotate-180\" />\n Details\n </summary>\n <div className=\"mt-1.5 space-y-1 pb-0.5 pl-4\">\n <div className=\"truncate\">\n Provider: {secret.provider || \"Not specified\"}\n </div>\n <div className=\"truncate\">Name: {secret.name}</div>\n </div>\n </details>\n )}\n </div>\n );\n })\n )}\n </div>\n <div className=\"max-h-[180px] space-y-2 overflow-y-auto rounded-md border border-border bg-card p-2\">\n <div className=\"flex items-center gap-1.5 px-1 pb-1 text-[11px] font-medium text-muted-foreground\">\n <IconBook size={12} />\n Resource packs\n </div>\n {resourcesError ? (\n <p className=\"rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground\">\n {resourcesError}\n </p>\n ) : resources.length === 0 ? (\n <p className=\"rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground\">\n No Dispatch resource packs found yet.\n </p>\n ) : (\n resources.map((resource) => {\n const selected = selectedResourceIds.includes(resource.id);\n return (\n <div\n key={resource.id}\n className={`group rounded-md border text-sm ${\n selected\n ? \"border-primary/45 bg-primary/5\"\n : \"border-border hover:border-muted-foreground/40 hover:bg-accent/35\"\n }`}\n >\n <button\n type=\"button\"\n aria-pressed={selected}\n onClick={() => toggleResource(resource.id)}\n className=\"flex w-full cursor-pointer items-start gap-3 rounded-md px-3 py-2 text-left\"\n >\n <span\n className={`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border ${\n selected\n ? \"border-primary/60 bg-primary/10 text-primary\"\n : \"border-muted-foreground/35 text-transparent\"\n }`}\n >\n {selected ? <IconCheck className=\"h-3 w-3\" /> : null}\n </span>\n <span className=\"min-w-0 flex-1\">\n <span className=\"flex min-w-0 items-center gap-1.5\">\n <IconFileText className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground/70\" />\n <span className=\"block truncate font-medium\">\n {resource.name}\n </span>\n </span>\n <span className=\"block truncate text-xs text-muted-foreground/70\">\n {resource.kind} · {resource.path}\n </span>\n </span>\n </button>\n <details className=\"group/details border-t border-border/60 px-3 py-1.5 text-xs text-muted-foreground/75\">\n <summary className=\"flex cursor-pointer list-none items-center gap-1.5 text-[11px] hover:text-muted-foreground [&::-webkit-details-marker]:hidden\">\n <IconChevronDown className=\"h-3 w-3 transition-transform group-open/details:rotate-180\" />\n Details\n </summary>\n <div className=\"mt-1.5 space-y-1 pb-0.5 pl-4\">\n <div className=\"truncate\">\n Scope:{\" \"}\n {resource.scope === \"all\"\n ? \"All apps\"\n : \"Selected apps\"}\n </div>\n {resource.description ? (\n <div className=\"line-clamp-2\">\n {resource.description}\n </div>\n ) : null}\n </div>\n </details>\n </div>\n );\n })\n )}\n </div>\n <div className=\"flex items-center justify-end gap-2\">\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={submitWithSelectedAccess}\n disabled={!prompt.trim() || isSubmitting}\n >\n {isSubmitting ? (\n <IconLoader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <IconPlus className=\"h-3.5 w-3.5\" />\n )}\n Create app\n </Button>\n </div>\n {!prompt.trim() ? (\n <p className=\"px-1 text-[11px] text-muted-foreground/70\">\n Add a prompt on the previous step before creating the app.\n </p>\n ) : null}\n </>\n )}\n\n {statusMessage ? (\n <div className=\"rounded-md border border-border bg-muted/40 px-3 py-2 text-xs text-muted-foreground\">\n {statusMessage}\n {branchUrl ? (\n <a\n href={branchUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"ml-2 inline-flex items-center gap-1 font-medium text-foreground underline\"\n >\n Open branch <IconArrowUpRight className=\"h-3 w-3\" />\n </a>\n ) : null}\n </div>\n ) : null}\n </div>\n );\n}\n\nexport function CreateAppPopover({\n trigger,\n align = \"center\",\n}: CreateAppPopoverProps) {\n const [open, setOpen] = useState(false);\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n {trigger ?? (\n <button\n type=\"button\"\n className=\"flex min-h-32 cursor-pointer items-center justify-center rounded-lg border border-dashed bg-card p-4 text-sm font-medium text-muted-foreground transition hover:border-foreground/30 hover:text-foreground\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <IconPlus size={16} />\n Create app\n </span>\n </button>\n )}\n </PopoverTrigger>\n <PopoverContent\n align={align}\n sideOffset={10}\n className=\"w-[calc(100vw-2rem)] rounded-xl p-3 shadow-xl sm:w-[460px]\"\n >\n <CreateAppFlow onClose={() => setOpen(false)} />\n </PopoverContent>\n </Popover>\n );\n}\n"]}
@@ -0,0 +1,6 @@
1
+ import { type WorkspaceAppSummary } from "../lib/workspace-apps.js";
2
+ export declare function WorkspaceAppCard({ app, className, }: {
3
+ app: WorkspaceAppSummary;
4
+ className?: string;
5
+ }): import("react/jsx-runtime").JSX.Element;
6
+ //# sourceMappingURL=workspace-app-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-app-card.d.ts","sourceRoot":"","sources":["../../src/components/workspace-app-card.tsx"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,sBAAsB,CAAC;AAE9B,wBAAgB,gBAAgB,CAAC,EAC/B,GAAG,EACH,SAAS,GACV,EAAE;IACD,GAAG,EAAE,mBAAmB,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,2CAoEA"}
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { IconArrowUpRight, IconClockHour4 } from "@tabler/icons-react";
3
+ import { AppKeysPopover } from "../components/app-keys-popover.js";
4
+ import { Badge } from "../components/ui/badge.js";
5
+ import { cn } from "../lib/utils.js";
6
+ import { isPendingBuilderHref, workspaceAppHref, } from "../lib/workspace-apps.js";
7
+ export function WorkspaceAppCard({ app, className, }) {
8
+ const href = workspaceAppHref(app);
9
+ const openInNewTab = isPendingBuilderHref(app);
10
+ return (_jsxs("div", { "aria-disabled": !href, className: cn("group relative rounded-lg border bg-card p-4 transition hover:border-foreground/30 aria-disabled:opacity-60", className), children: [href ? (_jsx("a", { href: href, target: openInNewTab ? "_blank" : undefined, rel: openInNewTab ? "noreferrer" : undefined, "aria-label": `Open ${app.name}`, className: "absolute inset-0 z-0 rounded-lg" })) : null, _jsxs("div", { className: "pointer-events-none relative z-10 flex h-full items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx("h3", { className: "truncate text-sm font-semibold text-foreground", children: app.name }), app.status === "pending" ? (_jsxs(Badge, { variant: "outline", className: "shrink-0 gap-1 border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300", children: [_jsx(IconClockHour4, { size: 12 }), "Building"] })) : null] }), _jsx("p", { className: "mt-1 truncate font-mono text-xs text-muted-foreground", children: app.path }), app.status === "pending" && app.branchName ? (_jsxs("p", { className: "mt-1 truncate text-xs text-muted-foreground", children: ["Branch: ", app.branchName] })) : null, app.description ? (_jsx("p", { className: "mt-2 line-clamp-2 text-xs leading-relaxed text-muted-foreground", children: app.description })) : null] }), _jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [app.status === "ready" ? (_jsx("div", { className: "pointer-events-auto", children: _jsx(AppKeysPopover, { appId: app.id, appName: app.name }) })) : null, href ? (_jsx(IconArrowUpRight, { size: 16, className: "text-muted-foreground transition group-hover:text-foreground" })) : null] })] })] }));
11
+ }
12
+ //# sourceMappingURL=workspace-app-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-app-card.js","sourceRoot":"","sources":["../../src/components/workspace-app-card.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACjC,OAAO,EACL,oBAAoB,EACpB,gBAAgB,GAEjB,MAAM,sBAAsB,CAAC;AAE9B,MAAM,UAAU,gBAAgB,CAAC,EAC/B,GAAG,EACH,SAAS,GAIV;IACC,MAAM,IAAI,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,YAAY,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;IAE/C,OAAO,CACL,gCACiB,CAAC,IAAI,EACpB,SAAS,EAAE,EAAE,CACX,6GAA6G,EAC7G,SAAS,CACV,aAEA,IAAI,CAAC,CAAC,CAAC,CACN,YACE,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,EAC3C,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,gBAChC,QAAQ,GAAG,CAAC,IAAI,EAAE,EAC9B,SAAS,EAAC,iCAAiC,GAC3C,CACH,CAAC,CAAC,CAAC,IAAI,EAER,eAAK,SAAS,EAAC,iFAAiF,aAC9F,eAAK,SAAS,EAAC,SAAS,aACtB,eAAK,SAAS,EAAC,iCAAiC,aAC9C,aAAI,SAAS,EAAC,gDAAgD,YAC3D,GAAG,CAAC,IAAI,GACN,EACJ,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,CAC1B,MAAC,KAAK,IACJ,OAAO,EAAC,SAAS,EACjB,SAAS,EAAC,uFAAuF,aAEjG,KAAC,cAAc,IAAC,IAAI,EAAE,EAAE,GAAI,gBAEtB,CACT,CAAC,CAAC,CAAC,IAAI,IACJ,EACN,YAAG,SAAS,EAAC,uDAAuD,YACjE,GAAG,CAAC,IAAI,GACP,EACH,GAAG,CAAC,MAAM,KAAK,SAAS,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAC5C,aAAG,SAAS,EAAC,6CAA6C,yBAC/C,GAAG,CAAC,UAAU,IACrB,CACL,CAAC,CAAC,CAAC,IAAI,EACP,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CACjB,YAAG,SAAS,EAAC,iEAAiE,YAC3E,GAAG,CAAC,WAAW,GACd,CACL,CAAC,CAAC,CAAC,IAAI,IACJ,EACN,eAAK,SAAS,EAAC,kCAAkC,aAC9C,GAAG,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CACxB,cAAK,SAAS,EAAC,qBAAqB,YAClC,KAAC,cAAc,IAAC,KAAK,EAAE,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,GAAI,GAChD,CACP,CAAC,CAAC,CAAC,IAAI,EACP,IAAI,CAAC,CAAC,CAAC,CACN,KAAC,gBAAgB,IACf,IAAI,EAAE,EAAE,EACR,SAAS,EAAC,8DAA8D,GACxE,CACH,CAAC,CAAC,CAAC,IAAI,IACJ,IACF,IACF,CACP,CAAC;AACJ,CAAC","sourcesContent":["import { IconArrowUpRight, IconClockHour4 } from \"@tabler/icons-react\";\nimport { AppKeysPopover } from \"@/components/app-keys-popover\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { cn } from \"@/lib/utils\";\nimport {\n isPendingBuilderHref,\n workspaceAppHref,\n type WorkspaceAppSummary,\n} from \"@/lib/workspace-apps\";\n\nexport function WorkspaceAppCard({\n app,\n className,\n}: {\n app: WorkspaceAppSummary;\n className?: string;\n}) {\n const href = workspaceAppHref(app);\n const openInNewTab = isPendingBuilderHref(app);\n\n return (\n <div\n aria-disabled={!href}\n className={cn(\n \"group relative rounded-lg border bg-card p-4 transition hover:border-foreground/30 aria-disabled:opacity-60\",\n className,\n )}\n >\n {href ? (\n <a\n href={href}\n target={openInNewTab ? \"_blank\" : undefined}\n rel={openInNewTab ? \"noreferrer\" : undefined}\n aria-label={`Open ${app.name}`}\n className=\"absolute inset-0 z-0 rounded-lg\"\n />\n ) : null}\n\n <div className=\"pointer-events-none relative z-10 flex h-full items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <h3 className=\"truncate text-sm font-semibold text-foreground\">\n {app.name}\n </h3>\n {app.status === \"pending\" ? (\n <Badge\n variant=\"outline\"\n className=\"shrink-0 gap-1 border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300\"\n >\n <IconClockHour4 size={12} />\n Building\n </Badge>\n ) : null}\n </div>\n <p className=\"mt-1 truncate font-mono text-xs text-muted-foreground\">\n {app.path}\n </p>\n {app.status === \"pending\" && app.branchName ? (\n <p className=\"mt-1 truncate text-xs text-muted-foreground\">\n Branch: {app.branchName}\n </p>\n ) : null}\n {app.description ? (\n <p className=\"mt-2 line-clamp-2 text-xs leading-relaxed text-muted-foreground\">\n {app.description}\n </p>\n ) : null}\n </div>\n <div className=\"flex shrink-0 items-center gap-1\">\n {app.status === \"ready\" ? (\n <div className=\"pointer-events-auto\">\n <AppKeysPopover appId={app.id} appName={app.name} />\n </div>\n ) : null}\n {href ? (\n <IconArrowUpRight\n size={16}\n className=\"text-muted-foreground transition group-hover:text-foreground\"\n />\n ) : null}\n </div>\n </div>\n </div>\n );\n}\n"]}
@@ -0,0 +1,15 @@
1
+ export interface WorkspaceAppSummary {
2
+ id: string;
3
+ name: string;
4
+ description?: string;
5
+ path: string;
6
+ url?: string | null;
7
+ isDispatch?: boolean;
8
+ status?: "ready" | "pending";
9
+ statusLabel?: string;
10
+ builderUrl?: string | null;
11
+ branchName?: string | null;
12
+ }
13
+ export declare function workspaceAppHref(app: WorkspaceAppSummary): string | null;
14
+ export declare function isPendingBuilderHref(app: WorkspaceAppSummary): boolean;
15
+ //# sourceMappingURL=workspace-apps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-apps.d.ts","sourceRoot":"","sources":["../../src/lib/workspace-apps.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,mBAAmB,GAAG,MAAM,GAAG,IAAI,CAGxE;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAEtE"}
@@ -0,0 +1,9 @@
1
+ export function workspaceAppHref(app) {
2
+ if (app.status === "pending")
3
+ return app.builderUrl || null;
4
+ return app.path || app.url || null;
5
+ }
6
+ export function isPendingBuilderHref(app) {
7
+ return app.status === "pending" && !!app.builderUrl;
8
+ }
9
+ //# sourceMappingURL=workspace-apps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-apps.js","sourceRoot":"","sources":["../../src/lib/workspace-apps.ts"],"names":[],"mappings":"AAaA,MAAM,UAAU,gBAAgB,CAAC,GAAwB;IACvD,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC,UAAU,IAAI,IAAI,CAAC;IAC5D,OAAO,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,GAAG,IAAI,IAAI,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,GAAwB;IAC3D,OAAO,GAAG,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC;AACtD,CAAC","sourcesContent":["export interface WorkspaceAppSummary {\n id: string;\n name: string;\n description?: string;\n path: string;\n url?: string | null;\n isDispatch?: boolean;\n status?: \"ready\" | \"pending\";\n statusLabel?: string;\n builderUrl?: string | null;\n branchName?: string | null;\n}\n\nexport function workspaceAppHref(app: WorkspaceAppSummary): string | null {\n if (app.status === \"pending\") return app.builderUrl || null;\n return app.path || app.url || null;\n}\n\nexport function isPendingBuilderHref(app: WorkspaceAppSummary): boolean {\n return app.status === \"pending\" && !!app.builderUrl;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"apps.$appId.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/apps.$appId.tsx"],"names":[],"mappings":"AA6BA,wBAAgB,IAAI;;IAEnB;AAED,MAAM,CAAC,OAAO,UAAU,iBAAiB,4CAoGxC"}
1
+ {"version":3,"file":"apps.$appId.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/apps.$appId.tsx"],"names":[],"mappings":"AAgBA,wBAAgB,IAAI;;IAEnB;AAED,MAAM,CAAC,OAAO,UAAU,iBAAiB,4CAoGxC"}
@@ -6,11 +6,7 @@ import { IconArrowLeft, IconArrowUpRight, IconClockHour4, } from "@tabler/icons-
6
6
  import { DispatchShell } from "../../components/dispatch-shell.js";
7
7
  import { Badge } from "../../components/ui/badge.js";
8
8
  import { Button } from "../../components/ui/button.js";
9
- function workspaceAppHref(app) {
10
- if (app.status === "pending")
11
- return app.builderUrl || null;
12
- return app.path || app.url || null;
13
- }
9
+ import { workspaceAppHref, } from "../../lib/workspace-apps.js";
14
10
  export function meta() {
15
11
  return [{ title: "Workspace app - Dispatch" }];
16
12
  }
@@ -1 +1 @@
1
- {"version":3,"file":"apps.$appId.js","sourceRoot":"","sources":["../../../src/routes/pages/apps.$appId.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useMemo } from \"react\";\nimport { Link, useParams } from \"react-router\";\nimport { useActionQuery } from \"@agent-native/core/client\";\nimport {\n IconArrowLeft,\n IconArrowUpRight,\n IconClockHour4,\n} from \"@tabler/icons-react\";\nimport { DispatchShell } from \"@/components/dispatch-shell\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\n\ninterface WorkspaceAppSummary {\n id: string;\n name: string;\n description?: string;\n path: string;\n url?: string | null;\n status?: \"ready\" | \"pending\";\n statusLabel?: string;\n builderUrl?: string | null;\n branchName?: string | null;\n}\n\nfunction workspaceAppHref(app: WorkspaceAppSummary): string | null {\n if (app.status === \"pending\") return app.builderUrl || null;\n return app.path || app.url || null;\n}\n\nexport function meta() {\n return [{ title: \"Workspace app - Dispatch\" }];\n}\n\nexport default function WorkspaceAppRoute() {\n const { appId } = useParams();\n const { data: apps = [], isLoading } = useActionQuery(\n \"list-workspace-apps\",\n { includeAgentCards: false },\n {\n refetchInterval: 2_000,\n },\n );\n const app = useMemo(\n () =>\n (apps as WorkspaceAppSummary[]).find((item) => item.id === appId) ?? null,\n [appId, apps],\n );\n const href = app ? workspaceAppHref(app) : null;\n\n useEffect(() => {\n if (!app || app.status === \"pending\" || !href) return;\n window.location.assign(href);\n }, [app, href]);\n\n return (\n <DispatchShell\n title={app?.name || \"Workspace App\"}\n description=\"Open a deployed app or check the status of an app being created.\"\n >\n <div className=\"max-w-2xl rounded-lg border bg-card p-5\">\n <Button asChild size=\"sm\" variant=\"ghost\" className=\"-ml-2 mb-4\">\n <Link to=\"/apps\">\n <IconArrowLeft size={15} className=\"mr-1.5\" />\n Apps\n </Link>\n </Button>\n\n {isLoading && !app ? (\n <p className=\"text-sm text-muted-foreground\">Loading app status...</p>\n ) : !app ? (\n <div className=\"space-y-3\">\n <h2 className=\"text-base font-semibold text-foreground\">\n App not found\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n This route is not in the workspace app list yet.\n </p>\n </div>\n ) : app.status === \"pending\" ? (\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {app.name}\n </h2>\n <Badge\n variant=\"outline\"\n className=\"gap-1 border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300\"\n >\n <IconClockHour4 size={12} />\n Building\n </Badge>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n This app is being created. It will be available at{\" \"}\n <span className=\"font-mono text-foreground\">{app.path}</span>{\" \"}\n after its branch is merged and the workspace deploy finishes.\n </p>\n {app.branchName ? (\n <p className=\"text-xs text-muted-foreground\">\n Branch: {app.branchName}\n </p>\n ) : null}\n {app.builderUrl ? (\n <Button asChild>\n <a href={app.builderUrl} target=\"_blank\" rel=\"noreferrer\">\n Open Builder branch\n <IconArrowUpRight size={15} className=\"ml-1.5\" />\n </a>\n </Button>\n ) : null}\n </div>\n ) : (\n <div className=\"space-y-3\">\n <h2 className=\"text-base font-semibold text-foreground\">\n Opening {app.name}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n Redirecting to{\" \"}\n <span className=\"font-mono text-foreground\">{app.path}</span>.\n </p>\n {href ? (\n <Button asChild>\n <a href={href}>\n Open app\n <IconArrowUpRight size={15} className=\"ml-1.5\" />\n </a>\n </Button>\n ) : null}\n </div>\n )}\n </div>\n </DispatchShell>\n );\n}\n"]}
1
+ {"version":3,"file":"apps.$appId.js","sourceRoot":"","sources":["../../../src/routes/pages/apps.$appId.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useMemo } from \"react\";\nimport { Link, useParams } from \"react-router\";\nimport { useActionQuery } from \"@agent-native/core/client\";\nimport {\n IconArrowLeft,\n IconArrowUpRight,\n IconClockHour4,\n} from \"@tabler/icons-react\";\nimport { DispatchShell } from \"@/components/dispatch-shell\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n workspaceAppHref,\n type WorkspaceAppSummary,\n} from \"@/lib/workspace-apps\";\n\nexport function meta() {\n return [{ title: \"Workspace app - Dispatch\" }];\n}\n\nexport default function WorkspaceAppRoute() {\n const { appId } = useParams();\n const { data: apps = [], isLoading } = useActionQuery(\n \"list-workspace-apps\",\n { includeAgentCards: false },\n {\n refetchInterval: 2_000,\n },\n );\n const app = useMemo(\n () =>\n (apps as WorkspaceAppSummary[]).find((item) => item.id === appId) ?? null,\n [appId, apps],\n );\n const href = app ? workspaceAppHref(app) : null;\n\n useEffect(() => {\n if (!app || app.status === \"pending\" || !href) return;\n window.location.assign(href);\n }, [app, href]);\n\n return (\n <DispatchShell\n title={app?.name || \"Workspace App\"}\n description=\"Open a deployed app or check the status of an app being created.\"\n >\n <div className=\"max-w-2xl rounded-lg border bg-card p-5\">\n <Button asChild size=\"sm\" variant=\"ghost\" className=\"-ml-2 mb-4\">\n <Link to=\"/apps\">\n <IconArrowLeft size={15} className=\"mr-1.5\" />\n Apps\n </Link>\n </Button>\n\n {isLoading && !app ? (\n <p className=\"text-sm text-muted-foreground\">Loading app status...</p>\n ) : !app ? (\n <div className=\"space-y-3\">\n <h2 className=\"text-base font-semibold text-foreground\">\n App not found\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n This route is not in the workspace app list yet.\n </p>\n </div>\n ) : app.status === \"pending\" ? (\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {app.name}\n </h2>\n <Badge\n variant=\"outline\"\n className=\"gap-1 border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300\"\n >\n <IconClockHour4 size={12} />\n Building\n </Badge>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n This app is being created. It will be available at{\" \"}\n <span className=\"font-mono text-foreground\">{app.path}</span>{\" \"}\n after its branch is merged and the workspace deploy finishes.\n </p>\n {app.branchName ? (\n <p className=\"text-xs text-muted-foreground\">\n Branch: {app.branchName}\n </p>\n ) : null}\n {app.builderUrl ? (\n <Button asChild>\n <a href={app.builderUrl} target=\"_blank\" rel=\"noreferrer\">\n Open Builder branch\n <IconArrowUpRight size={15} className=\"ml-1.5\" />\n </a>\n </Button>\n ) : null}\n </div>\n ) : (\n <div className=\"space-y-3\">\n <h2 className=\"text-base font-semibold text-foreground\">\n Opening {app.name}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n Redirecting to{\" \"}\n <span className=\"font-mono text-foreground\">{app.path}</span>.\n </p>\n {href ? (\n <Button asChild>\n <a href={href}>\n Open app\n <IconArrowUpRight size={15} className=\"ml-1.5\" />\n </a>\n </Button>\n ) : null}\n </div>\n )}\n </div>\n </DispatchShell>\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/apps.tsx"],"names":[],"mappings":"AAmCA,wBAAgB,IAAI;;IAEnB;AAQD,MAAM,CAAC,OAAO,UAAU,SAAS,4CAqHhC"}
1
+ {"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/apps.tsx"],"names":[],"mappings":"AAQA,wBAAgB,IAAI;;IAEnB;AAQD,MAAM,CAAC,OAAO,UAAU,SAAS,4CA6DhC"}
@@ -1,19 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useActionQuery } from "@agent-native/core/client";
3
- import { IconArrowUpRight, IconApps, IconClockHour4, IconPlus, } from "@tabler/icons-react";
4
- import { AppKeysPopover } from "../../components/app-keys-popover.js";
3
+ import { IconApps, IconPlus } from "@tabler/icons-react";
5
4
  import { CreateAppPopover } from "../../components/create-app-popover.js";
6
5
  import { DispatchShell } from "../../components/dispatch-shell.js";
7
- import { Badge } from "../../components/ui/badge.js";
6
+ import { WorkspaceAppCard } from "../../components/workspace-app-card.js";
8
7
  import { Button } from "../../components/ui/button.js";
9
- function workspaceAppHref(app) {
10
- if (app.status === "pending")
11
- return app.builderUrl || null;
12
- return app.path || app.url || null;
13
- }
14
- function isPendingBuilderHref(app) {
15
- return app.status === "pending" && !!app.builderUrl;
16
- }
17
8
  export function meta() {
18
9
  return [{ title: "Apps — Dispatch" }];
19
10
  }
@@ -29,14 +20,6 @@ export default function AppsRoute() {
29
20
  ? `Apps in the "${workspaceLabel}" workspace. Each app gets its own route under this workspace and shares its database, auth, and agent chat.`
30
21
  : "Open workspace apps and start new app creation from Dispatch.", children: _jsx("div", { className: "space-y-4", children: _jsxs("section", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(IconApps, { size: 16, className: "text-muted-foreground" }), _jsx("h2", { className: "text-sm font-semibold text-foreground", children: workspaceLabel
31
22
  ? `Apps in ${workspaceLabel}`
32
- : "Workspace apps" })] }), _jsx(CreateAppPopover, { align: "end", trigger: _jsxs(Button, { size: "sm", variant: "outline", children: [_jsx(IconPlus, { size: 15, className: "mr-1.5" }), "App"] }) })] }), _jsxs("div", { className: "grid gap-3 md:grid-cols-2 xl:grid-cols-3", children: [typedApps.map((app) => {
33
- const href = workspaceAppHref(app);
34
- // Pending Builder branches live on a different host; open
35
- // those in a new tab. Ready workspace apps stay in-window
36
- // so the cards work inside the Builder webview, where new
37
- // tabs would escape to the host browser.
38
- const openInNewTab = isPendingBuilderHref(app);
39
- return (_jsx("a", { href: href ?? undefined, target: openInNewTab ? "_blank" : undefined, rel: openInNewTab ? "noreferrer" : undefined, "aria-disabled": !href, className: "group rounded-lg border bg-card p-4 transition hover:border-foreground/30 aria-disabled:pointer-events-none aria-disabled:opacity-60", children: _jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx("h3", { className: "truncate text-sm font-semibold text-foreground", children: app.name }), app.status === "pending" ? (_jsxs(Badge, { variant: "outline", className: "shrink-0 gap-1 border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300", children: [_jsx(IconClockHour4, { size: 12 }), "Building"] })) : null] }), _jsx("p", { className: "mt-1 truncate font-mono text-xs text-muted-foreground", children: app.path }), app.status === "pending" && app.branchName ? (_jsxs("p", { className: "mt-1 truncate text-xs text-muted-foreground", children: ["Branch: ", app.branchName] })) : null, app.description ? (_jsx("p", { className: "mt-2 line-clamp-2 text-xs leading-relaxed text-muted-foreground", children: app.description })) : null] }), _jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [app.status === "ready" ? (_jsx(AppKeysPopover, { appId: app.id, appName: app.name })) : null, _jsx(IconArrowUpRight, { size: 16, className: "text-muted-foreground transition group-hover:text-foreground" })] })] }) }, app.id));
40
- }), _jsx(CreateAppPopover, {})] })] }) }) }));
23
+ : "Workspace apps" })] }), _jsx(CreateAppPopover, { align: "end", trigger: _jsxs(Button, { size: "sm", variant: "outline", children: [_jsx(IconPlus, { size: 15, className: "mr-1.5" }), "App"] }) })] }), _jsxs("div", { className: "grid gap-3 md:grid-cols-2 xl:grid-cols-3", children: [typedApps.map((app) => (_jsx(WorkspaceAppCard, { app: app }, app.id))), _jsx(CreateAppPopover, {})] })] }) }) }));
41
24
  }
42
25
  //# sourceMappingURL=apps.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"apps.js","sourceRoot":"","sources":["../../../src/routes/pages/apps.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useActionQuery } from \"@agent-native/core/client\";\nimport {\n IconArrowUpRight,\n IconApps,\n IconClockHour4,\n IconPlus,\n} from \"@tabler/icons-react\";\nimport { AppKeysPopover } from \"@/components/app-keys-popover\";\nimport { CreateAppPopover } from \"@/components/create-app-popover\";\nimport { DispatchShell } from \"@/components/dispatch-shell\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\n\ninterface WorkspaceAppSummary {\n id: string;\n name: string;\n description?: string;\n path: string;\n url?: string | null;\n isDispatch: boolean;\n status?: \"ready\" | \"pending\";\n statusLabel?: string;\n builderUrl?: string | null;\n branchName?: string | null;\n}\n\nfunction workspaceAppHref(app: WorkspaceAppSummary): string | null {\n if (app.status === \"pending\") return app.builderUrl || null;\n return app.path || app.url || null;\n}\n\nfunction isPendingBuilderHref(app: WorkspaceAppSummary): boolean {\n return app.status === \"pending\" && !!app.builderUrl;\n}\n\nexport function meta() {\n return [{ title: \"Apps — Dispatch\" }];\n}\n\ninterface WorkspaceInfo {\n name: string | null;\n displayName: string | null;\n appCount: number;\n}\n\nexport default function AppsRoute() {\n const { data: apps = [] } = useActionQuery(\n \"list-workspace-apps\",\n { includeAgentCards: false },\n {\n refetchInterval: 2_000,\n },\n );\n const { data: workspace } = useActionQuery(\n \"get-workspace-info\",\n {},\n { staleTime: 60_000 },\n );\n const ws = workspace as WorkspaceInfo | undefined;\n const workspaceLabel = ws?.displayName ?? ws?.name ?? null;\n const typedApps = (apps as WorkspaceAppSummary[]).filter(\n (app) => !app.isDispatch,\n );\n\n return (\n <DispatchShell\n title=\"Apps\"\n description={\n workspaceLabel\n ? `Apps in the \"${workspaceLabel}\" workspace. Each app gets its own route under this workspace and shares its database, auth, and agent chat.`\n : \"Open workspace apps and start new app creation from Dispatch.\"\n }\n >\n <div className=\"space-y-4\">\n <section className=\"space-y-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex items-center gap-2\">\n <IconApps size={16} className=\"text-muted-foreground\" />\n <h2 className=\"text-sm font-semibold text-foreground\">\n {workspaceLabel\n ? `Apps in ${workspaceLabel}`\n : \"Workspace apps\"}\n </h2>\n </div>\n <CreateAppPopover\n align=\"end\"\n trigger={\n <Button size=\"sm\" variant=\"outline\">\n <IconPlus size={15} className=\"mr-1.5\" />\n App\n </Button>\n }\n />\n </div>\n\n <div className=\"grid gap-3 md:grid-cols-2 xl:grid-cols-3\">\n {typedApps.map((app) => {\n const href = workspaceAppHref(app);\n // Pending Builder branches live on a different host; open\n // those in a new tab. Ready workspace apps stay in-window\n // so the cards work inside the Builder webview, where new\n // tabs would escape to the host browser.\n const openInNewTab = isPendingBuilderHref(app);\n return (\n <a\n key={app.id}\n href={href ?? undefined}\n target={openInNewTab ? \"_blank\" : undefined}\n rel={openInNewTab ? \"noreferrer\" : undefined}\n aria-disabled={!href}\n className=\"group rounded-lg border bg-card p-4 transition hover:border-foreground/30 aria-disabled:pointer-events-none aria-disabled:opacity-60\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <h3 className=\"truncate text-sm font-semibold text-foreground\">\n {app.name}\n </h3>\n {app.status === \"pending\" ? (\n <Badge\n variant=\"outline\"\n className=\"shrink-0 gap-1 border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300\"\n >\n <IconClockHour4 size={12} />\n Building\n </Badge>\n ) : null}\n </div>\n <p className=\"mt-1 truncate font-mono text-xs text-muted-foreground\">\n {app.path}\n </p>\n {app.status === \"pending\" && app.branchName ? (\n <p className=\"mt-1 truncate text-xs text-muted-foreground\">\n Branch: {app.branchName}\n </p>\n ) : null}\n {app.description ? (\n <p className=\"mt-2 line-clamp-2 text-xs leading-relaxed text-muted-foreground\">\n {app.description}\n </p>\n ) : null}\n </div>\n <div className=\"flex shrink-0 items-center gap-1\">\n {app.status === \"ready\" ? (\n <AppKeysPopover appId={app.id} appName={app.name} />\n ) : null}\n <IconArrowUpRight\n size={16}\n className=\"text-muted-foreground transition group-hover:text-foreground\"\n />\n </div>\n </div>\n </a>\n );\n })}\n\n <CreateAppPopover />\n </div>\n </section>\n </div>\n </DispatchShell>\n );\n}\n"]}
1
+ {"version":3,"file":"apps.js","sourceRoot":"","sources":["../../../src/routes/pages/apps.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAGhD,MAAM,UAAU,IAAI;IAClB,OAAO,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC,CAAC;AACxC,CAAC;AAQD,MAAM,CAAC,OAAO,UAAU,SAAS;IAC/B,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,EAAE,EAAE,GAAG,cAAc,CACxC,qBAAqB,EACrB,EAAE,iBAAiB,EAAE,KAAK,EAAE,EAC5B;QACE,eAAe,EAAE,KAAK;KACvB,CACF,CAAC;IACF,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,cAAc,CACxC,oBAAoB,EACpB,EAAE,EACF,EAAE,SAAS,EAAE,MAAM,EAAE,CACtB,CAAC;IACF,MAAM,EAAE,GAAG,SAAsC,CAAC;IAClD,MAAM,cAAc,GAAG,EAAE,EAAE,WAAW,IAAI,EAAE,EAAE,IAAI,IAAI,IAAI,CAAC;IAC3D,MAAM,SAAS,GAAI,IAA8B,CAAC,MAAM,CACtD,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,UAAU,CACzB,CAAC;IAEF,OAAO,CACL,KAAC,aAAa,IACZ,KAAK,EAAC,MAAM,EACZ,WAAW,EACT,cAAc;YACZ,CAAC,CAAC,gBAAgB,cAAc,8GAA8G;YAC9I,CAAC,CAAC,+DAA+D,YAGrE,cAAK,SAAS,EAAC,WAAW,YACxB,mBAAS,SAAS,EAAC,WAAW,aAC5B,eAAK,SAAS,EAAC,yCAAyC,aACtD,eAAK,SAAS,EAAC,yBAAyB,aACtC,KAAC,QAAQ,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,uBAAuB,GAAG,EACxD,aAAI,SAAS,EAAC,uCAAuC,YAClD,cAAc;4CACb,CAAC,CAAC,WAAW,cAAc,EAAE;4CAC7B,CAAC,CAAC,gBAAgB,GACjB,IACD,EACN,KAAC,gBAAgB,IACf,KAAK,EAAC,KAAK,EACX,OAAO,EACL,MAAC,MAAM,IAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAC,SAAS,aACjC,KAAC,QAAQ,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,QAAQ,GAAG,WAElC,GAEX,IACE,EAEN,eAAK,SAAS,EAAC,0CAA0C,aACtD,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACtB,KAAC,gBAAgB,IAAc,GAAG,EAAE,GAAG,IAAhB,GAAG,CAAC,EAAE,CAAc,CAC5C,CAAC,EAEF,KAAC,gBAAgB,KAAG,IAChB,IACE,GACN,GACQ,CACjB,CAAC;AACJ,CAAC","sourcesContent":["import { useActionQuery } from \"@agent-native/core/client\";\nimport { IconApps, IconPlus } from \"@tabler/icons-react\";\nimport { CreateAppPopover } from \"@/components/create-app-popover\";\nimport { DispatchShell } from \"@/components/dispatch-shell\";\nimport { WorkspaceAppCard } from \"@/components/workspace-app-card\";\nimport { Button } from \"@/components/ui/button\";\nimport type { WorkspaceAppSummary } from \"@/lib/workspace-apps\";\n\nexport function meta() {\n return [{ title: \"Apps — Dispatch\" }];\n}\n\ninterface WorkspaceInfo {\n name: string | null;\n displayName: string | null;\n appCount: number;\n}\n\nexport default function AppsRoute() {\n const { data: apps = [] } = useActionQuery(\n \"list-workspace-apps\",\n { includeAgentCards: false },\n {\n refetchInterval: 2_000,\n },\n );\n const { data: workspace } = useActionQuery(\n \"get-workspace-info\",\n {},\n { staleTime: 60_000 },\n );\n const ws = workspace as WorkspaceInfo | undefined;\n const workspaceLabel = ws?.displayName ?? ws?.name ?? null;\n const typedApps = (apps as WorkspaceAppSummary[]).filter(\n (app) => !app.isDispatch,\n );\n\n return (\n <DispatchShell\n title=\"Apps\"\n description={\n workspaceLabel\n ? `Apps in the \"${workspaceLabel}\" workspace. Each app gets its own route under this workspace and shares its database, auth, and agent chat.`\n : \"Open workspace apps and start new app creation from Dispatch.\"\n }\n >\n <div className=\"space-y-4\">\n <section className=\"space-y-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex items-center gap-2\">\n <IconApps size={16} className=\"text-muted-foreground\" />\n <h2 className=\"text-sm font-semibold text-foreground\">\n {workspaceLabel\n ? `Apps in ${workspaceLabel}`\n : \"Workspace apps\"}\n </h2>\n </div>\n <CreateAppPopover\n align=\"end\"\n trigger={\n <Button size=\"sm\" variant=\"outline\">\n <IconPlus size={15} className=\"mr-1.5\" />\n App\n </Button>\n }\n />\n </div>\n\n <div className=\"grid gap-3 md:grid-cols-2 xl:grid-cols-3\">\n {typedApps.map((app) => (\n <WorkspaceAppCard key={app.id} app={app} />\n ))}\n\n <CreateAppPopover />\n </div>\n </section>\n </div>\n </DispatchShell>\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"overview.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/overview.tsx"],"names":[],"mappings":"AA4dA,wBAAgB,IAAI;;IAEnB;AAED,MAAM,CAAC,OAAO,UAAU,aAAa,4CA0SpC"}
1
+ {"version":3,"file":"overview.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/overview.tsx"],"names":[],"mappings":"AA6YA,wBAAgB,IAAI;;IAEnB;AAED,MAAM,CAAC,OAAO,UAAU,aAAa,4CA0SpC"}