@agent-native/dispatch 0.9.3 → 0.10.1

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 (31) hide show
  1. package/README.md +37 -52
  2. package/dist/actions/index.d.ts.map +1 -1
  3. package/dist/actions/index.js +2 -0
  4. package/dist/actions/index.js.map +1 -1
  5. package/dist/actions/provider-api-catalog.d.ts.map +1 -1
  6. package/dist/actions/provider-api-catalog.js +9 -6
  7. package/dist/actions/provider-api-catalog.js.map +1 -1
  8. package/dist/actions/provider-api-docs.d.ts.map +1 -1
  9. package/dist/actions/provider-api-docs.js +7 -5
  10. package/dist/actions/provider-api-docs.js.map +1 -1
  11. package/dist/actions/provider-api-register.d.ts +3 -0
  12. package/dist/actions/provider-api-register.d.ts.map +1 -0
  13. package/dist/actions/provider-api-register.js +160 -0
  14. package/dist/actions/provider-api-register.js.map +1 -0
  15. package/dist/actions/provider-api-request.d.ts.map +1 -1
  16. package/dist/actions/provider-api-request.js +33 -5
  17. package/dist/actions/provider-api-request.js.map +1 -1
  18. package/dist/components/ui/button.js +1 -1
  19. package/dist/components/ui/button.js.map +1 -1
  20. package/dist/server/lib/provider-api.d.ts +3 -3
  21. package/dist/server/lib/provider-api.d.ts.map +1 -1
  22. package/dist/server/lib/provider-api.js +29 -5
  23. package/dist/server/lib/provider-api.js.map +1 -1
  24. package/package.json +1 -1
  25. package/src/actions/index.ts +2 -0
  26. package/src/actions/provider-api-catalog.ts +11 -12
  27. package/src/actions/provider-api-docs.ts +9 -11
  28. package/src/actions/provider-api-register.ts +198 -0
  29. package/src/actions/provider-api-request.ts +49 -10
  30. package/src/components/ui/button.tsx +1 -1
  31. package/src/server/lib/provider-api.ts +35 -8
@@ -1,12 +1,14 @@
1
1
  import { defineAction } from "@agent-native/core";
2
2
  import { z } from "zod";
3
- import { DISPATCH_PROVIDER_API_IDS, executeProviderApiRequest, } from "../server/lib/provider-api.js";
4
- const ProviderSchema = z.enum(DISPATCH_PROVIDER_API_IDS);
3
+ import { executeProviderApiRequest } from "../server/lib/provider-api.js";
5
4
  const MethodSchema = z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"]);
6
5
  export default defineAction({
7
- description: "Make an arbitrary authenticated HTTP request to a shared workspace integration or configured provider API. Use this as the flexible escape hatch when Dispatch needs a provider endpoint, filter, pagination mode, payload, or API version that no canned action models. The request is constrained to the provider host, uses configured credentials automatically, blocks private/internal URLs, and redacts secrets from responses.",
6
+ description: "Make an arbitrary authenticated HTTP request to a shared workspace integration, configured provider API, or custom provider registered via provider-api-register. Use this as the flexible escape hatch when Dispatch needs a provider endpoint, filter, pagination mode, payload, or API version that no canned action models. The request is constrained to the provider host, uses configured credentials automatically, blocks private/internal URLs, and redacts secrets from responses.",
8
7
  schema: z.object({
9
- provider: ProviderSchema.describe("Configured provider API to call, e.g. slack, github, notion, hubspot, gmail, google_drive, google_calendar, granola, stripe, jira."),
8
+ provider: z
9
+ .string()
10
+ .min(1)
11
+ .describe("Provider id to call — built-in (e.g. slack, github, notion, hubspot, gmail, google_drive, google_calendar, granola, stripe, jira) or a custom provider id registered via provider-api-register. Use provider-api-catalog to list available providers."),
10
12
  method: MethodSchema.default("GET").describe("HTTP method to use."),
11
13
  path: z
12
14
  .string()
@@ -49,7 +51,33 @@ export default defineAction({
49
51
  .min(1_000)
50
52
  .max(4 * 1024 * 1024)
51
53
  .optional()
52
- .describe("Maximum response bytes to read. Default 1MB, max 4MB."),
54
+ .describe("Maximum response bytes to read. Default 1MB, max 4MB. Ignored when saveToFile is set (allows up to 20MB)."),
55
+ saveToFile: z
56
+ .string()
57
+ .optional()
58
+ .describe("Workspace file path to save the full response body to instead of returning it in context (e.g. 'analysis/hubspot-deals.json'). When set, returns only a compact summary {savedTo, status, bytes, preview} and allows up to 20MB response. Useful for large datasets that would overflow context."),
59
+ fetchAllPages: z
60
+ .object({
61
+ cursorPath: z
62
+ .string()
63
+ .describe("Dot-path in the JSON response body where the next-page cursor lives, e.g. 'meta.next_cursor' or 'pagination.next_page_token'."),
64
+ cursorParam: z
65
+ .string()
66
+ .describe("Query parameter name to pass the cursor on subsequent pages, e.g. 'cursor' or 'page_token'."),
67
+ itemsPath: z
68
+ .string()
69
+ .optional()
70
+ .describe("Dot-path to the items array in each response, e.g. 'results' or 'data.items'. When omitted, the whole response body is appended per page."),
71
+ maxPages: z.coerce
72
+ .number()
73
+ .int()
74
+ .min(1)
75
+ .max(50)
76
+ .optional()
77
+ .describe("Maximum pages to fetch. Default 10, max 50. Stops early when the cursor is empty."),
78
+ })
79
+ .optional()
80
+ .describe("Enable cursor-based pagination. After each response, reads cursorPath from the JSON body and re-issues the request with cursorParam set, accumulating items from itemsPath (or whole bodies) until cursor is empty or maxPages is reached. Combine with saveToFile to write the full dataset to a workspace file."),
53
81
  }),
54
82
  http: false,
55
83
  run: async (args) => executeProviderApiRequest(args),
@@ -1 +1 @@
1
- {"version":3,"file":"provider-api-request.js","sourceRoot":"","sources":["../../src/actions/provider-api-request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,+BAA+B,CAAC;AAEvC,MAAM,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;AACzD,MAAM,YAAY,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;AAE/E,eAAe,YAAY,CAAC;IAC1B,WAAW,EACT,waAAwa;IAC1a,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAC/B,oIAAoI,CACrI;QACD,MAAM,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,qBAAqB,CAAC;QACnE,IAAI,EAAE,CAAC;aACJ,MAAM,EAAE;aACR,GAAG,CAAC,CAAC,CAAC;aACN,QAAQ,CACP,wMAAwM,CACzM;QACH,KAAK,EAAE,CAAC;aACL,OAAO,EAAE;aACT,QAAQ,EAAE;aACV,QAAQ,CACP,4FAA4F,CAC7F;QACH,OAAO,EAAE,CAAC;aACP,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;aAC/B,QAAQ,EAAE;aACV,QAAQ,CACP,mHAAmH,CACpH;QACH,IAAI,EAAE,CAAC;aACJ,OAAO,EAAE;aACT,QAAQ,EAAE;aACV,QAAQ,CACP,iFAAiF,CAClF;QACH,IAAI,EAAE,CAAC;aACJ,IAAI,CAAC,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;aACzB,OAAO,CAAC,SAAS,CAAC;aAClB,QAAQ,CACP,iIAAiI,CAClI;QACH,YAAY,EAAE,CAAC;aACZ,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CACP,oGAAoG,CACrG;QACH,SAAS,EAAE,CAAC;aACT,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CACP,8GAA8G,CAC/G;QACH,SAAS,EAAE,CAAC,CAAC,MAAM;aAChB,MAAM,EAAE;aACR,GAAG,EAAE;aACL,GAAG,CAAC,KAAK,CAAC;aACV,GAAG,CAAC,OAAO,CAAC;aACZ,QAAQ,EAAE;aACV,QAAQ,CAAC,6DAA6D,CAAC;QAC1E,QAAQ,EAAE,CAAC,CAAC,MAAM;aACf,MAAM,EAAE;aACR,GAAG,EAAE;aACL,GAAG,CAAC,KAAK,CAAC;aACV,GAAG,CAAC,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;aACpB,QAAQ,EAAE;aACV,QAAQ,CAAC,uDAAuD,CAAC;KACrE,CAAC;IACF,IAAI,EAAE,KAAK;IACX,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC;CACrD,CAAC,CAAC","sourcesContent":["import { defineAction } from \"@agent-native/core\";\nimport { z } from \"zod\";\nimport {\n DISPATCH_PROVIDER_API_IDS,\n executeProviderApiRequest,\n} from \"../server/lib/provider-api.js\";\n\nconst ProviderSchema = z.enum(DISPATCH_PROVIDER_API_IDS);\nconst MethodSchema = z.enum([\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"HEAD\"]);\n\nexport default defineAction({\n description:\n \"Make an arbitrary authenticated HTTP request to a shared workspace integration or configured provider API. Use this as the flexible escape hatch when Dispatch needs a provider endpoint, filter, pagination mode, payload, or API version that no canned action models. The request is constrained to the provider host, uses configured credentials automatically, blocks private/internal URLs, and redacts secrets from responses.\",\n schema: z.object({\n provider: ProviderSchema.describe(\n \"Configured provider API to call, e.g. slack, github, notion, hubspot, gmail, google_drive, google_calendar, granola, stripe, jira.\",\n ),\n method: MethodSchema.default(\"GET\").describe(\"HTTP method to use.\"),\n path: z\n .string()\n .min(1)\n .describe(\n \"Provider API path such as /search.messages, /repos/org/repo/issues, /crm/v3/objects/deals/search, or a full URL on an allowed provider host. Use placeholders from provider-api-catalog when provided.\",\n ),\n query: z\n .unknown()\n .optional()\n .describe(\n \"Optional query params as a JSON object/string. Array values produce repeated query params.\",\n ),\n headers: z\n .record(z.string(), z.unknown())\n .optional()\n .describe(\n \"Optional extra headers. Unsafe hop-by-hop headers are ignored. Auth headers are injected from stored credentials.\",\n ),\n body: z\n .unknown()\n .optional()\n .describe(\n \"Optional request body. Objects/arrays are JSON encoded; strings are sent as-is.\",\n ),\n auth: z\n .enum([\"default\", \"none\"])\n .default(\"default\")\n .describe(\n \"Use default to inject configured provider auth. Use none only for public provider endpoints that intentionally require no auth.\",\n ),\n connectionId: z\n .string()\n .optional()\n .describe(\n \"Optional shared workspace connection id to use when the provider has multiple granted connections.\",\n ),\n accountId: z\n .string()\n .optional()\n .describe(\n \"Optional OAuth account id to use for OAuth-backed providers such as Gmail, Google Calendar, or Google Drive.\",\n ),\n timeoutMs: z.coerce\n .number()\n .int()\n .min(1_000)\n .max(120_000)\n .optional()\n .describe(\"Request timeout in milliseconds. Default 30000, max 120000.\"),\n maxBytes: z.coerce\n .number()\n .int()\n .min(1_000)\n .max(4 * 1024 * 1024)\n .optional()\n .describe(\"Maximum response bytes to read. Default 1MB, max 4MB.\"),\n }),\n http: false,\n run: async (args) => executeProviderApiRequest(args),\n});\n"]}
1
+ {"version":3,"file":"provider-api-request.js","sourceRoot":"","sources":["../../src/actions/provider-api-request.ts"],"names":[],"mappings":"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","sourcesContent":["import { defineAction } from \"@agent-native/core\";\nimport { z } from \"zod\";\nimport { executeProviderApiRequest } from \"../server/lib/provider-api.js\";\n\nconst MethodSchema = z.enum([\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"HEAD\"]);\n\nexport default defineAction({\n description:\n \"Make an arbitrary authenticated HTTP request to a shared workspace integration, configured provider API, or custom provider registered via provider-api-register. Use this as the flexible escape hatch when Dispatch needs a provider endpoint, filter, pagination mode, payload, or API version that no canned action models. The request is constrained to the provider host, uses configured credentials automatically, blocks private/internal URLs, and redacts secrets from responses.\",\n schema: z.object({\n provider: z\n .string()\n .min(1)\n .describe(\n \"Provider id to call — built-in (e.g. slack, github, notion, hubspot, gmail, google_drive, google_calendar, granola, stripe, jira) or a custom provider id registered via provider-api-register. Use provider-api-catalog to list available providers.\",\n ),\n method: MethodSchema.default(\"GET\").describe(\"HTTP method to use.\"),\n path: z\n .string()\n .min(1)\n .describe(\n \"Provider API path such as /search.messages, /repos/org/repo/issues, /crm/v3/objects/deals/search, or a full URL on an allowed provider host. Use placeholders from provider-api-catalog when provided.\",\n ),\n query: z\n .unknown()\n .optional()\n .describe(\n \"Optional query params as a JSON object/string. Array values produce repeated query params.\",\n ),\n headers: z\n .record(z.string(), z.unknown())\n .optional()\n .describe(\n \"Optional extra headers. Unsafe hop-by-hop headers are ignored. Auth headers are injected from stored credentials.\",\n ),\n body: z\n .unknown()\n .optional()\n .describe(\n \"Optional request body. Objects/arrays are JSON encoded; strings are sent as-is.\",\n ),\n auth: z\n .enum([\"default\", \"none\"])\n .default(\"default\")\n .describe(\n \"Use default to inject configured provider auth. Use none only for public provider endpoints that intentionally require no auth.\",\n ),\n connectionId: z\n .string()\n .optional()\n .describe(\n \"Optional shared workspace connection id to use when the provider has multiple granted connections.\",\n ),\n accountId: z\n .string()\n .optional()\n .describe(\n \"Optional OAuth account id to use for OAuth-backed providers such as Gmail, Google Calendar, or Google Drive.\",\n ),\n timeoutMs: z.coerce\n .number()\n .int()\n .min(1_000)\n .max(120_000)\n .optional()\n .describe(\"Request timeout in milliseconds. Default 30000, max 120000.\"),\n maxBytes: z.coerce\n .number()\n .int()\n .min(1_000)\n .max(4 * 1024 * 1024)\n .optional()\n .describe(\n \"Maximum response bytes to read. Default 1MB, max 4MB. Ignored when saveToFile is set (allows up to 20MB).\",\n ),\n saveToFile: z\n .string()\n .optional()\n .describe(\n \"Workspace file path to save the full response body to instead of returning it in context (e.g. 'analysis/hubspot-deals.json'). When set, returns only a compact summary {savedTo, status, bytes, preview} and allows up to 20MB response. Useful for large datasets that would overflow context.\",\n ),\n fetchAllPages: z\n .object({\n cursorPath: z\n .string()\n .describe(\n \"Dot-path in the JSON response body where the next-page cursor lives, e.g. 'meta.next_cursor' or 'pagination.next_page_token'.\",\n ),\n cursorParam: z\n .string()\n .describe(\n \"Query parameter name to pass the cursor on subsequent pages, e.g. 'cursor' or 'page_token'.\",\n ),\n itemsPath: z\n .string()\n .optional()\n .describe(\n \"Dot-path to the items array in each response, e.g. 'results' or 'data.items'. When omitted, the whole response body is appended per page.\",\n ),\n maxPages: z.coerce\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(\n \"Maximum pages to fetch. Default 10, max 50. Stops early when the cursor is empty.\",\n ),\n })\n .optional()\n .describe(\n \"Enable cursor-based pagination. After each response, reads cursorPath from the JSON body and re-issues the request with cursorParam set, accumulating items from itemsPath (or whole bodies) until cursor is empty or maxPages is reached. Combine with saveToFile to write the full dataset to a workspace file.\",\n ),\n }),\n http: false,\n run: async (args) => executeProviderApiRequest(args),\n});\n"]}
@@ -8,7 +8,7 @@ const buttonVariants = cva("inline-flex items-center justify-center gap-2 whites
8
8
  variant: {
9
9
  default: "bg-primary text-primary-foreground hover:bg-primary/90",
10
10
  destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
11
- outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
11
+ outline: "border border-input dark:border-[hsl(220_4%_30%)] bg-background hover:bg-accent hover:text-accent-foreground",
12
12
  secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
13
13
  ghost: "hover:bg-accent hover:text-accent-foreground",
14
14
  link: "text-primary underline-offset-4 hover:underline",
@@ -1 +1 @@
1
- {"version":3,"file":"button.js","sourceRoot":"","sources":["../../../src/components/ui/button.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,GAAG,EAAqB,MAAM,0BAA0B,CAAC;AAElE,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEjC,MAAM,cAAc,GAAG,GAAG,CACxB,0VAA0V,EAC1V;IACE,QAAQ,EAAE;QACR,OAAO,EAAE;YACP,OAAO,EAAE,wDAAwD;YACjE,WAAW,EACT,oEAAoE;YACtE,OAAO,EACL,gFAAgF;YAClF,SAAS,EACP,8DAA8D;YAChE,KAAK,EAAE,8CAA8C;YACrD,IAAI,EAAE,iDAAiD;SACxD;QACD,IAAI,EAAE;YACJ,OAAO,EAAE,gBAAgB;YACzB,EAAE,EAAE,qBAAqB;YACzB,EAAE,EAAE,sBAAsB;YAC1B,IAAI,EAAE,WAAW;SAClB;KACF;IACD,eAAe,EAAE;QACf,OAAO,EAAE,SAAS;QAClB,IAAI,EAAE,SAAS;KAChB;CACF,CACF,CAAC;AASF,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAC7B,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE;IAC/D,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC;IACvC,OAAO,CACL,KAAC,IAAI,IACH,SAAS,EAAE,EAAE,CAAC,cAAc,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,EAC3D,GAAG,EAAE,GAAG,KACJ,KAAK,GACT,CACH,CAAC;AACJ,CAAC,CACF,CAAC;AACF,MAAM,CAAC,WAAW,GAAG,QAAQ,CAAC;AAE9B,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC","sourcesContent":["import * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n outline:\n \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-10 px-4 py-2\",\n sm: \"h-9 rounded-md px-3\",\n lg: \"h-11 rounded-md px-8\",\n icon: \"h-10 w-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nexport interface ButtonProps\n extends\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean;\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\";\n return (\n <Comp\n className={cn(buttonVariants({ variant, size, className }))}\n ref={ref}\n {...props}\n />\n );\n },\n);\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants };\n"]}
1
+ {"version":3,"file":"button.js","sourceRoot":"","sources":["../../../src/components/ui/button.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,GAAG,EAAqB,MAAM,0BAA0B,CAAC;AAElE,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEjC,MAAM,cAAc,GAAG,GAAG,CACxB,0VAA0V,EAC1V;IACE,QAAQ,EAAE;QACR,OAAO,EAAE;YACP,OAAO,EAAE,wDAAwD;YACjE,WAAW,EACT,oEAAoE;YACtE,OAAO,EACL,8GAA8G;YAChH,SAAS,EACP,8DAA8D;YAChE,KAAK,EAAE,8CAA8C;YACrD,IAAI,EAAE,iDAAiD;SACxD;QACD,IAAI,EAAE;YACJ,OAAO,EAAE,gBAAgB;YACzB,EAAE,EAAE,qBAAqB;YACzB,EAAE,EAAE,sBAAsB;YAC1B,IAAI,EAAE,WAAW;SAClB;KACF;IACD,eAAe,EAAE;QACf,OAAO,EAAE,SAAS;QAClB,IAAI,EAAE,SAAS;KAChB;CACF,CACF,CAAC;AASF,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAC7B,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,EAAE,EAAE;IAC/D,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC;IACvC,OAAO,CACL,KAAC,IAAI,IACH,SAAS,EAAE,EAAE,CAAC,cAAc,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,EAC3D,GAAG,EAAE,GAAG,KACJ,KAAK,GACT,CACH,CAAC;AACJ,CAAC,CACF,CAAC;AACF,MAAM,CAAC,WAAW,GAAG,QAAQ,CAAC;AAE9B,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC","sourcesContent":["import * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n outline:\n \"border border-input dark:border-[hsl(220_4%_30%)] bg-background hover:bg-accent hover:text-accent-foreground\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-10 px-4 py-2\",\n sm: \"h-9 rounded-md px-3\",\n lg: \"h-11 rounded-md px-8\",\n icon: \"h-10 w-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nexport interface ButtonProps\n extends\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean;\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\";\n return (\n <Comp\n className={cn(buttonVariants({ variant, size, className }))}\n ref={ref}\n {...props}\n />\n );\n },\n);\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants };\n"]}
@@ -2,7 +2,7 @@ import { type ProviderApiId, type ProviderApiMethod, type ProviderApiRequestArgs
2
2
  export declare const DISPATCH_PROVIDER_API_IDS: readonly ["amplitude", "apollo", "bigquery", "commonroom", "dataforseo", "ga4", "gcloud", "github", "gmail", "gong", "google_calendar", "google_drive", "granola", "grafana", "hubspot", "jira", "mixpanel", "notion", "posthog", "prometheus", "pylon", "sentry", "slack", "stripe", "twitter"];
3
3
  export type DispatchProviderApiId = ProviderApiId;
4
4
  export type { ProviderApiMethod, ProviderApiRequestArgs };
5
- export declare function listProviderApiCatalog(provider?: DispatchProviderApiId): {
5
+ export declare function listProviderApiCatalog(provider?: string): {
6
6
  id: "amplitude" | "bigquery" | "sentry" | "posthog" | "mixpanel" | "github" | "slack" | "twitter" | "notion" | "gmail" | "google_drive" | "hubspot" | "granola" | "apollo" | "commonroom" | "dataforseo" | "ga4" | "gcloud" | "gong" | "google_calendar" | "grafana" | "jira" | "prometheus" | "pylon" | "stripe";
7
7
  label: string;
8
8
  defaultBaseUrl: string;
@@ -17,9 +17,9 @@ export declare function listProviderApiCatalog(provider?: DispatchProviderApiId)
17
17
  examples: readonly import("@agent-native/core/provider-api").ProviderApiExample[];
18
18
  notes: readonly string[];
19
19
  templateUses: readonly import("@agent-native/core").WorkspaceConnectionTemplateUse[];
20
- }[];
20
+ }[] | Promise<unknown[]>;
21
21
  export declare function fetchProviderApiDocs(options: {
22
- provider: DispatchProviderApiId;
22
+ provider: string;
23
23
  url?: string;
24
24
  maxBytes?: number;
25
25
  }): Promise<unknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"provider-api.d.ts","sourceRoot":"","sources":["../../../src/server/lib/provider-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC5B,MAAM,iCAAiC,CAAC;AAGzC,eAAO,MAAM,yBAAyB,kSAAmB,CAAC;AAC1D,MAAM,MAAM,qBAAqB,GAAG,aAAa,CAAC;AAClD,YAAY,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,CAAC;AAgB1D,wBAAgB,sBAAsB,CAAC,QAAQ,CAAC,EAAE,qBAAqB;;;;;;;;;;;;;;;IAEtE;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAC5C,QAAQ,EAAE,qBAAqB,CAAC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,oBAEA;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,sBAAsB,oBAErE"}
1
+ {"version":3,"file":"provider-api.d.ts","sourceRoot":"","sources":["../../../src/server/lib/provider-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC5B,MAAM,iCAAiC,CAAC;AAGzC,eAAO,MAAM,yBAAyB,kSAAmB,CAAC;AAC1D,MAAM,MAAM,qBAAqB,GAAG,aAAa,CAAC;AAClD,YAAY,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,CAAC;AA0C1D,wBAAgB,sBAAsB,CAAC,QAAQ,CAAC,EAAE,MAAM;;;;;;;;;;;;;;;yBAEvD;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,oBAEA;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,sBAAsB,oBAErE"}
@@ -1,15 +1,39 @@
1
- import { PROVIDER_API_IDS, createProviderApiRuntime, } from "@agent-native/core/provider-api";
1
+ import { PROVIDER_API_IDS, createProviderApiRuntime, listCustomProviders, } from "@agent-native/core/provider-api";
2
2
  import { getCredentialContext } from "@agent-native/core/server";
3
3
  export const DISPATCH_PROVIDER_API_IDS = PROVIDER_API_IDS;
4
+ function requireCtx(action) {
5
+ const ctx = getCredentialContext();
6
+ if (!ctx) {
7
+ throw new Error(`Dispatch provider API ${action} requires an authenticated request context.`);
8
+ }
9
+ return ctx;
10
+ }
4
11
  const runtime = createProviderApiRuntime({
5
12
  appId: "dispatch",
6
13
  localCredentialSource: "dispatch_local",
7
- getCredentialContext: () => {
14
+ getCredentialContext: () => requireCtx("requests"),
15
+ getCustomProviders: async () => {
8
16
  const ctx = getCredentialContext();
9
- if (!ctx) {
10
- throw new Error("Dispatch provider API requests require an authenticated request context.");
17
+ if (!ctx)
18
+ return [];
19
+ // Load custom providers for both user scope and org scope.
20
+ const results = await Promise.allSettled([
21
+ listCustomProviders("user", ctx.userEmail),
22
+ ctx.orgId ? listCustomProviders("org", ctx.orgId) : Promise.resolve([]),
23
+ ]);
24
+ const userProviders = results[0].status === "fulfilled" ? results[0].value : [];
25
+ const orgProviders = results[1].status === "fulfilled" ? results[1].value : [];
26
+ // Merge: user-scope providers take precedence over org-scope ones with the
27
+ // same id, and neither can shadow a built-in id.
28
+ const seen = new Set(PROVIDER_API_IDS);
29
+ const merged = [];
30
+ for (const p of [...userProviders, ...orgProviders]) {
31
+ if (!seen.has(p.id)) {
32
+ seen.add(p.id);
33
+ merged.push(p);
34
+ }
11
35
  }
12
- return ctx;
36
+ return merged;
13
37
  },
14
38
  });
15
39
  export function listProviderApiCatalog(provider) {
@@ -1 +1 @@
1
- {"version":3,"file":"provider-api.js","sourceRoot":"","sources":["../../../src/server/lib/provider-api.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,wBAAwB,GAIzB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEjE,MAAM,CAAC,MAAM,yBAAyB,GAAG,gBAAgB,CAAC;AAI1D,MAAM,OAAO,GAAG,wBAAwB,CAAC;IACvC,KAAK,EAAE,UAAU;IACjB,qBAAqB,EAAE,gBAAgB;IACvC,oBAAoB,EAAE,GAAG,EAAE;QACzB,MAAM,GAAG,GAAG,oBAAoB,EAAE,CAAC;QACnC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,KAAK,CACb,0EAA0E,CAC3E,CAAC;QACJ,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,UAAU,sBAAsB,CAAC,QAAgC;IACrE,OAAO,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,OAIpC;IACC,OAAO,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,IAA4B;IACpE,OAAO,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC","sourcesContent":["import {\n PROVIDER_API_IDS,\n createProviderApiRuntime,\n type ProviderApiId,\n type ProviderApiMethod,\n type ProviderApiRequestArgs,\n} from \"@agent-native/core/provider-api\";\nimport { getCredentialContext } from \"@agent-native/core/server\";\n\nexport const DISPATCH_PROVIDER_API_IDS = PROVIDER_API_IDS;\nexport type DispatchProviderApiId = ProviderApiId;\nexport type { ProviderApiMethod, ProviderApiRequestArgs };\n\nconst runtime = createProviderApiRuntime({\n appId: \"dispatch\",\n localCredentialSource: \"dispatch_local\",\n getCredentialContext: () => {\n const ctx = getCredentialContext();\n if (!ctx) {\n throw new Error(\n \"Dispatch provider API requests require an authenticated request context.\",\n );\n }\n return ctx;\n },\n});\n\nexport function listProviderApiCatalog(provider?: DispatchProviderApiId) {\n return runtime.listCatalog(provider);\n}\n\nexport function fetchProviderApiDocs(options: {\n provider: DispatchProviderApiId;\n url?: string;\n maxBytes?: number;\n}) {\n return runtime.fetchDocs(options);\n}\n\nexport function executeProviderApiRequest(args: ProviderApiRequestArgs) {\n return runtime.executeRequest(args);\n}\n"]}
1
+ {"version":3,"file":"provider-api.js","sourceRoot":"","sources":["../../../src/server/lib/provider-api.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,wBAAwB,EACxB,mBAAmB,GAIpB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEjE,MAAM,CAAC,MAAM,yBAAyB,GAAG,gBAAgB,CAAC;AAI1D,SAAS,UAAU,CAAC,MAAc;IAChC,MAAM,GAAG,GAAG,oBAAoB,EAAE,CAAC;IACnC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CACb,yBAAyB,MAAM,6CAA6C,CAC7E,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,OAAO,GAAG,wBAAwB,CAAC;IACvC,KAAK,EAAE,UAAU;IACjB,qBAAqB,EAAE,gBAAgB;IACvC,oBAAoB,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC;IAClD,kBAAkB,EAAE,KAAK,IAAI,EAAE;QAC7B,MAAM,GAAG,GAAG,oBAAoB,EAAE,CAAC;QACnC,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,CAAC;QACpB,2DAA2D;QAC3D,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC;YACvC,mBAAmB,CAAC,MAAM,EAAE,GAAG,CAAC,SAAS,CAAC;YAC1C,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,mBAAmB,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;SACxE,CAAC,CAAC;QACH,MAAM,aAAa,GACjB,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5D,MAAM,YAAY,GAChB,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5D,2EAA2E;QAC3E,iDAAiD;QACjD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAS,gBAAuC,CAAC,CAAC;QACtE,MAAM,MAAM,GAAG,EAAE,CAAC;QAClB,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,aAAa,EAAE,GAAG,YAAY,CAAC,EAAE,CAAC;YACpD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;gBACpB,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBACf,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACjB,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,UAAU,sBAAsB,CAAC,QAAiB;IACtD,OAAO,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,OAIpC;IACC,OAAO,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,IAA4B;IACpE,OAAO,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC","sourcesContent":["import {\n PROVIDER_API_IDS,\n createProviderApiRuntime,\n listCustomProviders,\n type ProviderApiId,\n type ProviderApiMethod,\n type ProviderApiRequestArgs,\n} from \"@agent-native/core/provider-api\";\nimport { getCredentialContext } from \"@agent-native/core/server\";\n\nexport const DISPATCH_PROVIDER_API_IDS = PROVIDER_API_IDS;\nexport type DispatchProviderApiId = ProviderApiId;\nexport type { ProviderApiMethod, ProviderApiRequestArgs };\n\nfunction requireCtx(action: string) {\n const ctx = getCredentialContext();\n if (!ctx) {\n throw new Error(\n `Dispatch provider API ${action} requires an authenticated request context.`,\n );\n }\n return ctx;\n}\n\nconst runtime = createProviderApiRuntime({\n appId: \"dispatch\",\n localCredentialSource: \"dispatch_local\",\n getCredentialContext: () => requireCtx(\"requests\"),\n getCustomProviders: async () => {\n const ctx = getCredentialContext();\n if (!ctx) return [];\n // Load custom providers for both user scope and org scope.\n const results = await Promise.allSettled([\n listCustomProviders(\"user\", ctx.userEmail),\n ctx.orgId ? listCustomProviders(\"org\", ctx.orgId) : Promise.resolve([]),\n ]);\n const userProviders =\n results[0].status === \"fulfilled\" ? results[0].value : [];\n const orgProviders =\n results[1].status === \"fulfilled\" ? results[1].value : [];\n // Merge: user-scope providers take precedence over org-scope ones with the\n // same id, and neither can shadow a built-in id.\n const seen = new Set<string>(PROVIDER_API_IDS as unknown as string[]);\n const merged = [];\n for (const p of [...userProviders, ...orgProviders]) {\n if (!seen.has(p.id)) {\n seen.add(p.id);\n merged.push(p);\n }\n }\n return merged;\n },\n});\n\nexport function listProviderApiCatalog(provider?: string) {\n return runtime.listCatalog(provider);\n}\n\nexport function fetchProviderApiDocs(options: {\n provider: string;\n url?: string;\n maxBytes?: number;\n}) {\n return runtime.fetchDocs(options);\n}\n\nexport function executeProviderApiRequest(args: ProviderApiRequestArgs) {\n return runtime.executeRequest(args);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/dispatch",
3
- "version": "0.9.3",
3
+ "version": "0.10.1",
4
4
  "type": "module",
5
5
  "description": "Dispatch — workspace control plane for agent-native apps. Vault, integrations, destinations, scheduled jobs, and cross-app delegation, shipped as a single drop-in package.",
6
6
  "license": "MIT",
@@ -57,6 +57,7 @@ import previewDreamProposal from "./preview-dream-proposal.js";
57
57
  import previewWorkspaceResourceChange from "./preview-workspace-resource-change.js";
58
58
  import providerApiCatalog from "./provider-api-catalog.js";
59
59
  import providerApiDocs from "./provider-api-docs.js";
60
+ import providerApiRegister from "./provider-api-register.js";
60
61
  import providerApiRequest from "./provider-api-request.js";
61
62
  import rejectDispatchChange from "./reject-dispatch-change.js";
62
63
  import rejectDreamProposal from "./reject-dream-proposal.js";
@@ -149,6 +150,7 @@ export const dispatchActions: Record<string, ActionEntry> = {
149
150
  "preview-workspace-resource-change": previewWorkspaceResourceChange,
150
151
  "provider-api-catalog": providerApiCatalog,
151
152
  "provider-api-docs": providerApiDocs,
153
+ "provider-api-register": providerApiRegister,
152
154
  "provider-api-request": providerApiRequest,
153
155
  "reject-dispatch-change": rejectDispatchChange,
154
156
  "reject-dream-proposal": rejectDreamProposal,
@@ -1,27 +1,26 @@
1
1
  import { defineAction } from "@agent-native/core";
2
2
  import { z } from "zod";
3
- import {
4
- DISPATCH_PROVIDER_API_IDS,
5
- listProviderApiCatalog,
6
- } from "../server/lib/provider-api.js";
7
-
8
- const ProviderSchema = z.enum(DISPATCH_PROVIDER_API_IDS);
3
+ import { listProviderApiCatalog } from "../server/lib/provider-api.js";
9
4
 
10
5
  export default defineAction({
11
6
  description:
12
- "List raw HTTP API capabilities for shared workspace integrations and configured providers. Use before provider-api-request when grant/setup metadata is not enough and the provider's actual API must be inspected. Returns base URLs, auth style, credential key names, docs/spec URLs, placeholders, and examples; never returns secret values.",
7
+ "List raw HTTP API capabilities for shared workspace integrations, configured providers, and custom registered providers. Use before provider-api-request when grant/setup metadata is not enough and the provider's actual API must be inspected. Returns base URLs, auth style, credential key names, docs/spec URLs, placeholders, and examples; never returns secret values. Custom providers registered via provider-api-register are included.",
13
8
  schema: z.object({
14
- provider: ProviderSchema.optional().describe(
15
- "Optional provider id to inspect. Omit to list every provider API escape hatch.",
16
- ),
9
+ provider: z
10
+ .string()
11
+ .optional()
12
+ .describe(
13
+ "Optional provider id to inspect (built-in or custom). Omit to list every available provider API.",
14
+ ),
17
15
  }),
18
16
  http: { method: "GET" },
19
17
  readOnly: true,
20
18
  run: async ({ provider }) => {
19
+ const providers = await listProviderApiCatalog(provider);
21
20
  return {
22
- providers: listProviderApiCatalog(provider),
21
+ providers,
23
22
  guidance:
24
- "Workspace integrations and grants are not capability limits. When a provider can answer a question through its HTTP API, inspect docs/spec URLs here and call provider-api-request with the exact provider API method/path/query/body instead of adding a rigid one-off action.",
23
+ "Workspace integrations and grants are not capability limits. When a provider can answer a question through its HTTP API, inspect docs/spec URLs here and call provider-api-request with the exact provider API method/path/query/body instead of adding a rigid one-off action. Custom providers registered via provider-api-register also appear here.",
25
24
  };
26
25
  },
27
26
  });
@@ -1,25 +1,23 @@
1
1
  import { defineAction } from "@agent-native/core";
2
2
  import { z } from "zod";
3
- import {
4
- DISPATCH_PROVIDER_API_IDS,
5
- fetchProviderApiDocs,
6
- } from "../server/lib/provider-api.js";
7
-
8
- const ProviderSchema = z.enum(DISPATCH_PROVIDER_API_IDS);
3
+ import { fetchProviderApiDocs } from "../server/lib/provider-api.js";
9
4
 
10
5
  export default defineAction({
11
6
  description:
12
- "Inspect provider API docs/spec metadata, or fetch a registered provider docs/spec URL. Use this before arbitrary provider-api-request calls when the exact endpoint, filter operator, payload shape, pagination, or API version is uncertain.",
7
+ "Inspect provider API docs/spec metadata, or fetch ANY public API documentation page, OpenAPI spec, changelog, or web page. Registered docs/spec URLs from provider-api-catalog are curated starting points, but any public https/http URL is allowed. Use web-search to find documentation URLs first when uncertain, then fetch them here. SSRF guard still applies — private/internal addresses are blocked.",
13
8
  schema: z.object({
14
- provider: ProviderSchema.describe(
15
- "Provider whose API docs/spec to inspect.",
16
- ),
9
+ provider: z
10
+ .string()
11
+ .min(1)
12
+ .describe(
13
+ "Provider whose API docs/spec to inspect. Can be a built-in provider id or a custom provider id registered via provider-api-register.",
14
+ ),
17
15
  url: z
18
16
  .string()
19
17
  .url()
20
18
  .optional()
21
19
  .describe(
22
- "Optional docs/spec URL from provider-api-catalog to fetch. Only registered docs/spec origins are allowed.",
20
+ "Optional URL to fetch. Can be any public https/http URL API documentation pages, OpenAPI specs, changelogs, README files, etc. Registered docs/spec URLs from provider-api-catalog are curated starting points.",
23
21
  ),
24
22
  maxBytes: z.coerce
25
23
  .number()
@@ -0,0 +1,198 @@
1
+ import { defineAction } from "@agent-native/core";
2
+ import { z } from "zod";
3
+ import {
4
+ upsertCustomProvider,
5
+ deleteCustomProvider,
6
+ listCustomProviders,
7
+ getCustomProvider,
8
+ } from "@agent-native/core/provider-api";
9
+ import { getCredentialContext } from "@agent-native/core/server";
10
+
11
+ const AuthSchema = z.discriminatedUnion("type", [
12
+ z.object({
13
+ type: z.literal("none"),
14
+ }),
15
+ z.object({
16
+ type: z.literal("bearer"),
17
+ credentialKey: z
18
+ .string()
19
+ .min(1)
20
+ .describe(
21
+ "Name of the credential key (e.g. MY_API_TOKEN). Must already be saved via app secrets — this action never accepts secret values.",
22
+ ),
23
+ }),
24
+ z.object({
25
+ type: z.literal("basic"),
26
+ usernameKey: z
27
+ .string()
28
+ .min(1)
29
+ .describe("Credential key name for the username/login."),
30
+ passwordKey: z
31
+ .string()
32
+ .min(1)
33
+ .describe("Credential key name for the password/secret."),
34
+ }),
35
+ z.object({
36
+ type: z.literal("api-key-header"),
37
+ credentialKey: z
38
+ .string()
39
+ .min(1)
40
+ .describe("Credential key name for the API key value."),
41
+ headerName: z
42
+ .string()
43
+ .min(1)
44
+ .describe("HTTP header name to send the key in (e.g. X-Api-Key)."),
45
+ }),
46
+ ]);
47
+
48
+ export default defineAction({
49
+ description: `Register or update a custom API provider so the agent can call it via provider-api-request and look up its docs via provider-api-docs.
50
+
51
+ IMPORTANT — credentials:
52
+ - This action stores only credential KEY NAMES, never secret values.
53
+ - If the required API key is not yet saved, instruct the user to add it via app Settings → Keys, or use the create-vault-secret action, before calling this action.
54
+ - Supported auth kinds: none, bearer, basic, api-key-header.
55
+ google-service-account and oauth-bearer are NOT supported for custom providers.
56
+
57
+ After registration the provider appears in provider-api-catalog and can be used with provider-api-request.`,
58
+ schema: z.object({
59
+ operation: z
60
+ .enum(["upsert", "delete", "list", "get"])
61
+ .default("upsert")
62
+ .describe(
63
+ "Operation: upsert (create or update), delete (remove), list (all custom providers), get (single provider).",
64
+ ),
65
+ id: z
66
+ .string()
67
+ .min(1)
68
+ .max(64)
69
+ .optional()
70
+ .describe(
71
+ "Provider slug (e.g. my-api). Lowercase letters, digits, hyphens only. Required for upsert/delete/get.",
72
+ ),
73
+ label: z
74
+ .string()
75
+ .min(1)
76
+ .max(200)
77
+ .optional()
78
+ .describe(
79
+ "Human-readable name (e.g. 'My Analytics API'). Required for upsert.",
80
+ ),
81
+ baseUrl: z
82
+ .string()
83
+ .url()
84
+ .optional()
85
+ .describe(
86
+ "Base URL for the API (e.g. https://api.example.com/v1). Required for upsert. Must be a public https/http URL.",
87
+ ),
88
+ auth: AuthSchema.optional().describe(
89
+ "Auth configuration. Required for upsert. Use type 'none' for public APIs.",
90
+ ),
91
+ docsUrls: z
92
+ .array(z.string().url())
93
+ .optional()
94
+ .describe("Optional list of documentation URLs for this provider."),
95
+ allowedHostSuffixes: z
96
+ .array(z.string())
97
+ .optional()
98
+ .describe(
99
+ "Optional list of additional host suffixes requests may target beyond the base URL origin (e.g. ['example.com']).",
100
+ ),
101
+ defaultHeaders: z
102
+ .record(z.string(), z.string())
103
+ .optional()
104
+ .describe(
105
+ "Optional headers to include on every request (e.g. { 'Accept': 'application/json' }).",
106
+ ),
107
+ notes: z
108
+ .string()
109
+ .max(1000)
110
+ .optional()
111
+ .describe("Optional notes about this provider shown in the catalog."),
112
+ scope: z
113
+ .enum(["user", "org"])
114
+ .default("org")
115
+ .describe(
116
+ "Whether to store the provider for the current user only ('user') or for the whole workspace ('org').",
117
+ ),
118
+ }),
119
+ http: false,
120
+ run: async ({
121
+ operation,
122
+ id,
123
+ label,
124
+ baseUrl,
125
+ auth,
126
+ docsUrls,
127
+ allowedHostSuffixes,
128
+ defaultHeaders,
129
+ notes,
130
+ scope,
131
+ }) => {
132
+ const ctx = getCredentialContext();
133
+ if (!ctx) {
134
+ throw new Error(
135
+ "provider-api-register requires an authenticated request context.",
136
+ );
137
+ }
138
+ const scopeId =
139
+ scope === "org" ? (ctx.orgId ?? ctx.userEmail) : ctx.userEmail;
140
+
141
+ if (operation === "list") {
142
+ const providers = await listCustomProviders(scope, scopeId);
143
+ return {
144
+ providers: providers.map((p) => ({
145
+ id: p.id,
146
+ label: p.label,
147
+ baseUrl: p.baseUrl,
148
+ authType: p.auth.type,
149
+ docsUrls: p.docsUrls,
150
+ notes: p.notes,
151
+ updatedAt: p.updatedAt,
152
+ })),
153
+ count: providers.length,
154
+ };
155
+ }
156
+
157
+ if (operation === "get") {
158
+ if (!id) throw new Error("id is required for get operation.");
159
+ const provider = await getCustomProvider(scope, scopeId, id);
160
+ if (!provider) {
161
+ return { found: false, id };
162
+ }
163
+ return { found: true, provider };
164
+ }
165
+
166
+ if (operation === "delete") {
167
+ if (!id) throw new Error("id is required for delete operation.");
168
+ const deleted = await deleteCustomProvider(scope, scopeId, id);
169
+ return { deleted, id };
170
+ }
171
+
172
+ // upsert
173
+ if (!id) throw new Error("id is required for upsert operation.");
174
+ if (!label) throw new Error("label is required for upsert operation.");
175
+ if (!baseUrl) throw new Error("baseUrl is required for upsert operation.");
176
+ if (!auth) throw new Error("auth is required for upsert operation.");
177
+
178
+ await upsertCustomProvider({
179
+ scope,
180
+ scopeId,
181
+ id,
182
+ label,
183
+ baseUrl,
184
+ auth,
185
+ docsUrls,
186
+ allowedHostSuffixes,
187
+ defaultHeaders,
188
+ notes,
189
+ });
190
+
191
+ return {
192
+ registered: true,
193
+ id,
194
+ label,
195
+ message: `Custom provider "${id}" registered. Use provider-api-catalog to inspect it and provider-api-request to call it.`,
196
+ };
197
+ },
198
+ });
@@ -1,20 +1,19 @@
1
1
  import { defineAction } from "@agent-native/core";
2
2
  import { z } from "zod";
3
- import {
4
- DISPATCH_PROVIDER_API_IDS,
5
- executeProviderApiRequest,
6
- } from "../server/lib/provider-api.js";
3
+ import { executeProviderApiRequest } from "../server/lib/provider-api.js";
7
4
 
8
- const ProviderSchema = z.enum(DISPATCH_PROVIDER_API_IDS);
9
5
  const MethodSchema = z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"]);
10
6
 
11
7
  export default defineAction({
12
8
  description:
13
- "Make an arbitrary authenticated HTTP request to a shared workspace integration or configured provider API. Use this as the flexible escape hatch when Dispatch needs a provider endpoint, filter, pagination mode, payload, or API version that no canned action models. The request is constrained to the provider host, uses configured credentials automatically, blocks private/internal URLs, and redacts secrets from responses.",
9
+ "Make an arbitrary authenticated HTTP request to a shared workspace integration, configured provider API, or custom provider registered via provider-api-register. Use this as the flexible escape hatch when Dispatch needs a provider endpoint, filter, pagination mode, payload, or API version that no canned action models. The request is constrained to the provider host, uses configured credentials automatically, blocks private/internal URLs, and redacts secrets from responses.",
14
10
  schema: z.object({
15
- provider: ProviderSchema.describe(
16
- "Configured provider API to call, e.g. slack, github, notion, hubspot, gmail, google_drive, google_calendar, granola, stripe, jira.",
17
- ),
11
+ provider: z
12
+ .string()
13
+ .min(1)
14
+ .describe(
15
+ "Provider id to call — built-in (e.g. slack, github, notion, hubspot, gmail, google_drive, google_calendar, granola, stripe, jira) or a custom provider id registered via provider-api-register. Use provider-api-catalog to list available providers.",
16
+ ),
18
17
  method: MethodSchema.default("GET").describe("HTTP method to use."),
19
18
  path: z
20
19
  .string()
@@ -71,7 +70,47 @@ export default defineAction({
71
70
  .min(1_000)
72
71
  .max(4 * 1024 * 1024)
73
72
  .optional()
74
- .describe("Maximum response bytes to read. Default 1MB, max 4MB."),
73
+ .describe(
74
+ "Maximum response bytes to read. Default 1MB, max 4MB. Ignored when saveToFile is set (allows up to 20MB).",
75
+ ),
76
+ saveToFile: z
77
+ .string()
78
+ .optional()
79
+ .describe(
80
+ "Workspace file path to save the full response body to instead of returning it in context (e.g. 'analysis/hubspot-deals.json'). When set, returns only a compact summary {savedTo, status, bytes, preview} and allows up to 20MB response. Useful for large datasets that would overflow context.",
81
+ ),
82
+ fetchAllPages: z
83
+ .object({
84
+ cursorPath: z
85
+ .string()
86
+ .describe(
87
+ "Dot-path in the JSON response body where the next-page cursor lives, e.g. 'meta.next_cursor' or 'pagination.next_page_token'.",
88
+ ),
89
+ cursorParam: z
90
+ .string()
91
+ .describe(
92
+ "Query parameter name to pass the cursor on subsequent pages, e.g. 'cursor' or 'page_token'.",
93
+ ),
94
+ itemsPath: z
95
+ .string()
96
+ .optional()
97
+ .describe(
98
+ "Dot-path to the items array in each response, e.g. 'results' or 'data.items'. When omitted, the whole response body is appended per page.",
99
+ ),
100
+ maxPages: z.coerce
101
+ .number()
102
+ .int()
103
+ .min(1)
104
+ .max(50)
105
+ .optional()
106
+ .describe(
107
+ "Maximum pages to fetch. Default 10, max 50. Stops early when the cursor is empty.",
108
+ ),
109
+ })
110
+ .optional()
111
+ .describe(
112
+ "Enable cursor-based pagination. After each response, reads cursorPath from the JSON body and re-issues the request with cursorParam set, accumulating items from itemsPath (or whole bodies) until cursor is empty or maxPages is reached. Combine with saveToFile to write the full dataset to a workspace file.",
113
+ ),
75
114
  }),
76
115
  http: false,
77
116
  run: async (args) => executeProviderApiRequest(args),
@@ -13,7 +13,7 @@ const buttonVariants = cva(
13
13
  destructive:
14
14
  "bg-destructive text-destructive-foreground hover:bg-destructive/90",
15
15
  outline:
16
- "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
16
+ "border border-input dark:border-[hsl(220_4%_30%)] bg-background hover:bg-accent hover:text-accent-foreground",
17
17
  secondary:
18
18
  "bg-secondary text-secondary-foreground hover:bg-secondary/80",
19
19
  ghost: "hover:bg-accent hover:text-accent-foreground",