@executor-js/plugin-openapi 1.5.11 → 1.5.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{AddOpenApiSource-7O4LSD7C.js → AddOpenApiSource-JFSFWLBZ.js} +45 -11
- package/dist/AddOpenApiSource-JFSFWLBZ.js.map +1 -0
- package/dist/{OpenApiAccountsPanel-7XT6ZMD5.js → OpenApiAccountsPanel-2LHWUKIE.js} +3 -3
- package/dist/UpdateSpecSection-D32QZT3Q.js +104 -0
- package/dist/UpdateSpecSection-D32QZT3Q.js.map +1 -0
- package/dist/api/group.d.ts +37 -2
- package/dist/api/index.d.ts +41 -1
- package/dist/{chunk-WJQIWTZF.js → chunk-3XJSXSED.js} +78 -5
- package/dist/chunk-3XJSXSED.js.map +1 -0
- package/dist/{chunk-C6PH4R7Q.js → chunk-I2XDCVVM.js} +2 -2
- package/dist/{chunk-C3IJX4AN.js → chunk-RFSMGUBJ.js} +7 -1
- package/dist/chunk-RFSMGUBJ.js.map +1 -0
- package/dist/{chunk-IB36ED7Y.js → chunk-UJTNRH4Q.js} +32 -3
- package/dist/chunk-UJTNRH4Q.js.map +1 -0
- package/dist/client.js +5 -5
- package/dist/client.js.map +1 -1
- package/dist/core.js +3 -3
- package/dist/index.js +3 -3
- package/dist/react/OpenApiSourceDetailsFields.d.ts +4 -1
- package/dist/react/UpdateSpecSection.d.ts +2 -0
- package/dist/react/atoms.d.ts +32 -0
- package/dist/react/client.d.ts +34 -1
- package/dist/sdk/extract.d.ts +1 -0
- package/dist/sdk/plugin.d.ts +29 -2
- package/dist/sdk/preview.d.ts +4 -0
- package/dist/sdk/types.d.ts +2 -0
- package/package.json +3 -3
- package/dist/AddOpenApiSource-7O4LSD7C.js.map +0 -1
- package/dist/EditOpenApiSource-GIN5RQPL.js +0 -68
- package/dist/EditOpenApiSource-GIN5RQPL.js.map +0 -1
- package/dist/chunk-C3IJX4AN.js.map +0 -1
- package/dist/chunk-IB36ED7Y.js.map +0 -1
- package/dist/chunk-WJQIWTZF.js.map +0 -1
- package/dist/react/EditOpenApiSource.d.ts +0 -4
- /package/dist/{OpenApiAccountsPanel-7XT6ZMD5.js.map → OpenApiAccountsPanel-2LHWUKIE.js.map} +0 -0
- /package/dist/{chunk-C6PH4R7Q.js.map → chunk-I2XDCVVM.js.map} +0 -0
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
compactGoogleOAuthScopes,
|
|
3
3
|
detectedAuthenticationTemplates,
|
|
4
4
|
isGoogleDiscoveryUrl
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-I2XDCVVM.js";
|
|
6
6
|
import {
|
|
7
7
|
openApiPresets
|
|
8
8
|
} from "./chunk-AQ7JDDRM.js";
|
|
@@ -19,10 +19,10 @@ import {
|
|
|
19
19
|
import {
|
|
20
20
|
addOpenApiSpec,
|
|
21
21
|
previewOpenApiSpec
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-UJTNRH4Q.js";
|
|
23
23
|
import {
|
|
24
24
|
resolveServerUrl
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-RFSMGUBJ.js";
|
|
26
26
|
|
|
27
27
|
// src/react/AddOpenApiSource.tsx
|
|
28
28
|
import { useCallback, useEffect, useMemo as useMemo2, useRef, useState as useState2 } from "react";
|
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
import { CardStack as CardStack2, CardStackContent as CardStackContent2 } from "@executor-js/react/components/card-stack";
|
|
44
44
|
import { FieldLabel as FieldLabel2 } from "@executor-js/react/components/field";
|
|
45
45
|
import { FloatActions } from "@executor-js/react/components/float-actions";
|
|
46
|
-
import { Textarea } from "@executor-js/react/components/textarea";
|
|
46
|
+
import { Textarea as Textarea2 } from "@executor-js/react/components/textarea";
|
|
47
47
|
import { IOSSpinner, Spinner } from "@executor-js/react/components/spinner";
|
|
48
48
|
import {
|
|
49
49
|
addIntegrationErrorMessage,
|
|
@@ -67,18 +67,21 @@ import {
|
|
|
67
67
|
FreeformCombobox
|
|
68
68
|
} from "@executor-js/react/components/combobox";
|
|
69
69
|
import { Input } from "@executor-js/react/components/input";
|
|
70
|
+
import { Textarea } from "@executor-js/react/components/textarea";
|
|
70
71
|
import { IntegrationFavicon } from "@executor-js/react/components/integration-favicon";
|
|
71
72
|
import {
|
|
72
73
|
IntegrationIdentityFieldRows
|
|
73
74
|
} from "@executor-js/react/plugins/integration-identity";
|
|
74
75
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
76
|
+
var isUrlInput = (value) => URL.canParse(value.trim());
|
|
75
77
|
function OpenApiSourceDetailsFields(props) {
|
|
78
|
+
const specIsUrl = props.specUrl !== void 0 && isUrlInput(props.specUrl);
|
|
76
79
|
return /* @__PURE__ */ jsx(CardStack, { children: /* @__PURE__ */ jsxs(CardStackContent, { className: "border-t-0", children: [
|
|
77
80
|
/* @__PURE__ */ jsxs(CardStackEntry, { children: [
|
|
78
81
|
(props.faviconIcon || props.faviconUrl) && /* @__PURE__ */ jsx(IntegrationFavicon, { icon: props.faviconIcon, url: props.faviconUrl, size: 16 }),
|
|
79
82
|
/* @__PURE__ */ jsxs(CardStackEntryContent, { children: [
|
|
80
83
|
/* @__PURE__ */ jsx(CardStackEntryTitle, { children: props.title }),
|
|
81
|
-
props.
|
|
84
|
+
props.subtitle && /* @__PURE__ */ jsx(CardStackEntryDescription, { children: props.subtitle })
|
|
82
85
|
] }),
|
|
83
86
|
props.saveState && props.saveState !== "idle" && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: props.saveState === "saving" ? "Saving\u2026" : "Saved" })
|
|
84
87
|
] }),
|
|
@@ -89,6 +92,17 @@ function OpenApiSourceDetailsFields(props) {
|
|
|
89
92
|
namespaceReadOnly: props.namespaceReadOnly
|
|
90
93
|
}
|
|
91
94
|
),
|
|
95
|
+
props.onDescriptionChange && /* @__PURE__ */ jsx(CardStackEntryField, { label: "Description", children: /* @__PURE__ */ jsx(
|
|
96
|
+
Textarea,
|
|
97
|
+
{
|
|
98
|
+
value: props.description ?? "",
|
|
99
|
+
onChange: (e) => props.onDescriptionChange?.(e.target.value),
|
|
100
|
+
placeholder: "What this API is and when to reach for it",
|
|
101
|
+
rows: 2,
|
|
102
|
+
maxRows: 6,
|
|
103
|
+
className: "text-sm"
|
|
104
|
+
}
|
|
105
|
+
) }),
|
|
92
106
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2", children: [
|
|
93
107
|
/* @__PURE__ */ jsxs(CardStackEntryField, { label: props.baseUrlLabel ?? "Base URL", children: [
|
|
94
108
|
props.baseUrlOptions && props.baseUrlOptions.length > 0 ? /* @__PURE__ */ jsx(
|
|
@@ -113,7 +127,7 @@ function OpenApiSourceDetailsFields(props) {
|
|
|
113
127
|
props.baseUrlMissingMessage && !props.baseUrl && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-amber-600 dark:text-amber-400", children: props.baseUrlMissingMessage }),
|
|
114
128
|
props.baseUrlHint && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: props.baseUrlHint })
|
|
115
129
|
] }),
|
|
116
|
-
|
|
130
|
+
specIsUrl && props.onSpecUrlChange && /* @__PURE__ */ jsx(CardStackEntryField, { label: "Spec URL", children: /* @__PURE__ */ jsx(
|
|
117
131
|
Input,
|
|
118
132
|
{
|
|
119
133
|
value: props.specUrl,
|
|
@@ -124,6 +138,18 @@ function OpenApiSourceDetailsFields(props) {
|
|
|
124
138
|
}
|
|
125
139
|
) })
|
|
126
140
|
] }),
|
|
141
|
+
props.specUrl !== void 0 && !specIsUrl && props.onSpecUrlChange && /* @__PURE__ */ jsx(CardStackEntryField, { label: "Spec", children: /* @__PURE__ */ jsx(
|
|
142
|
+
Textarea,
|
|
143
|
+
{
|
|
144
|
+
value: props.specUrl,
|
|
145
|
+
onChange: (e) => props.onSpecUrlChange?.(e.target.value),
|
|
146
|
+
placeholder: "Pasted OpenAPI JSON/YAML",
|
|
147
|
+
rows: 4,
|
|
148
|
+
maxRows: 12,
|
|
149
|
+
className: "font-mono text-xs",
|
|
150
|
+
disabled: props.specUrlDisabled
|
|
151
|
+
}
|
|
152
|
+
) }),
|
|
127
153
|
props.footer && /* @__PURE__ */ jsx(CardStackEntry, { children: /* @__PURE__ */ jsx(CardStackEntryContent, { children: /* @__PURE__ */ jsx(CardStackEntryTitle, { children: props.footer }) }) })
|
|
128
154
|
] }) });
|
|
129
155
|
}
|
|
@@ -486,6 +512,7 @@ function AddOpenApiSource(props) {
|
|
|
486
512
|
const [analyzeError, setAnalyzeError] = useState2(null);
|
|
487
513
|
const [preview, setPreview] = useState2(null);
|
|
488
514
|
const [baseUrl, setBaseUrl] = useState2("");
|
|
515
|
+
const [descriptionDraft, setDescriptionDraft] = useState2(null);
|
|
489
516
|
const identityFallbackName = isGoogleBundlePreset ? "Google" : preview ? Option.getOrElse(preview.title, () => "") : "";
|
|
490
517
|
const identity = useIntegrationIdentity({
|
|
491
518
|
fallbackName: identityFallbackName,
|
|
@@ -542,6 +569,7 @@ function AddOpenApiSource(props) {
|
|
|
542
569
|
const resolvedBaseUrl = baseUrl.trim();
|
|
543
570
|
const resolvedSourceId = slugifyNamespace(identity.namespace) || (preview ? Option.getOrElse(preview.title, () => "openapi") : "openapi");
|
|
544
571
|
const resolvedDisplayName = identity.name.trim() || (preview ? Option.getOrElse(preview.title, () => resolvedSourceId) : resolvedSourceId);
|
|
572
|
+
const resolvedDescription = descriptionDraft ?? (preview ? Option.getOrElse(preview.description, () => "") : "");
|
|
545
573
|
const authenticationTemplate = useMemo2(
|
|
546
574
|
() => detectedAuthenticationTemplates(
|
|
547
575
|
preview?.headerPresets ?? [],
|
|
@@ -598,7 +626,8 @@ function AddOpenApiSource(props) {
|
|
|
598
626
|
payload: {
|
|
599
627
|
spec: specForAdd,
|
|
600
628
|
slug: resolvedSourceId,
|
|
601
|
-
|
|
629
|
+
name: resolvedDisplayName,
|
|
630
|
+
...resolvedDescription.trim().length > 0 ? { description: resolvedDescription.trim() } : {},
|
|
602
631
|
baseUrl: resolvedBaseUrl,
|
|
603
632
|
// Always send the edited method list (even empty) when the user has
|
|
604
633
|
// inspected a preview: an explicit [] means "no auth methods", while
|
|
@@ -625,6 +654,7 @@ function AddOpenApiSource(props) {
|
|
|
625
654
|
doAdd,
|
|
626
655
|
resolvedSourceId,
|
|
627
656
|
resolvedDisplayName,
|
|
657
|
+
resolvedDescription,
|
|
628
658
|
resolvedBaseUrl,
|
|
629
659
|
editedAuthenticationTemplate
|
|
630
660
|
]);
|
|
@@ -656,7 +686,7 @@ function AddOpenApiSource(props) {
|
|
|
656
686
|
/* @__PURE__ */ jsx3(FieldLabel2, { children: "OpenAPI Spec" }),
|
|
657
687
|
/* @__PURE__ */ jsxs3("div", { className: "relative", children: [
|
|
658
688
|
/* @__PURE__ */ jsx3(
|
|
659
|
-
|
|
689
|
+
Textarea2,
|
|
660
690
|
{
|
|
661
691
|
value: specUrl,
|
|
662
692
|
onChange: (e) => setSpecUrl(e.target.value),
|
|
@@ -674,8 +704,10 @@ function AddOpenApiSource(props) {
|
|
|
674
704
|
OpenApiSourceDetailsFields,
|
|
675
705
|
{
|
|
676
706
|
title: "Google",
|
|
677
|
-
|
|
707
|
+
subtitle: `${bundleDiscoveryUrls.length} Google API${bundleDiscoveryUrls.length !== 1 ? "s" : ""} \xB7 one shared OAuth consent`,
|
|
678
708
|
identity,
|
|
709
|
+
description: resolvedDescription,
|
|
710
|
+
onDescriptionChange: setDescriptionDraft,
|
|
679
711
|
baseUrl: resolvedBaseUrl,
|
|
680
712
|
onBaseUrlChange: setBaseUrl,
|
|
681
713
|
baseUrlLabel: "Base URL override (optional)",
|
|
@@ -686,8 +718,10 @@ function AddOpenApiSource(props) {
|
|
|
686
718
|
OpenApiSourceDetailsFields,
|
|
687
719
|
{
|
|
688
720
|
title: Option.getOrElse(preview.title, () => "API"),
|
|
689
|
-
|
|
721
|
+
subtitle: `${Option.getOrElse(preview.version, () => "")}${Option.isSome(preview.version) ? " \xB7 " : ""}${preview.operationCount} operation${preview.operationCount !== 1 ? "s" : ""}${preview.tags.length > 0 ? ` \xB7 ${preview.tags.length} tag${preview.tags.length !== 1 ? "s" : ""}` : ""}`,
|
|
690
722
|
identity,
|
|
723
|
+
description: resolvedDescription,
|
|
724
|
+
onDescriptionChange: setDescriptionDraft,
|
|
691
725
|
baseUrl: resolvedBaseUrl,
|
|
692
726
|
onBaseUrlChange: setBaseUrl,
|
|
693
727
|
baseUrlOptions,
|
|
@@ -728,4 +762,4 @@ function AddOpenApiSource(props) {
|
|
|
728
762
|
export {
|
|
729
763
|
AddOpenApiSource as default
|
|
730
764
|
};
|
|
731
|
-
//# sourceMappingURL=AddOpenApiSource-
|
|
765
|
+
//# sourceMappingURL=AddOpenApiSource-JFSFWLBZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react/AddOpenApiSource.tsx","../src/react/OpenApiSourceDetailsFields.tsx","../src/react/GoogleProductPicker.tsx","../src/sdk/google-oauth-batches.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useAtomSet } from \"@effect/atom-react\";\nimport * as Effect from \"effect/Effect\";\nimport * as Exit from \"effect/Exit\";\nimport * as Option from \"effect/Option\";\n\nimport { IntegrationSlug } from \"@executor-js/sdk/shared\";\nimport { integrationWriteKeys } from \"@executor-js/react/api/reactivity-keys\";\nimport {\n slugifyNamespace,\n useIntegrationIdentity,\n} from \"@executor-js/react/plugins/integration-identity\";\nimport { Button } from \"@executor-js/react/components/button\";\nimport {\n AuthMethodListEditor,\n useAuthMethodList,\n type AuthMethodRow,\n type AuthMethodSeed,\n} from \"@executor-js/react/components/auth-method-list-editor\";\nimport { CardStack, CardStackContent } from \"@executor-js/react/components/card-stack\";\nimport { FieldLabel } from \"@executor-js/react/components/field\";\nimport { FloatActions } from \"@executor-js/react/components/float-actions\";\nimport { Textarea } from \"@executor-js/react/components/textarea\";\nimport { IOSSpinner, Spinner } from \"@executor-js/react/components/spinner\";\nimport {\n addIntegrationErrorMessage,\n errorMessageFromExit,\n FormErrorAlert,\n SlugCollisionAlert,\n useSlugAlreadyExists,\n} from \"@executor-js/react/lib/integration-add\";\n\nimport {\n authenticationFromEditorValue,\n editorValueFromAuthentication,\n openApiWireAuthInput,\n} from \"./auth-method-config\";\nimport { addOpenApiSpec, previewOpenApiSpec } from \"./atoms\";\nimport { OpenApiSourceDetailsFields } from \"./OpenApiSourceDetailsFields\";\nimport { GoogleProductPicker } from \"./GoogleProductPicker\";\nimport { openApiPresets } from \"../sdk/presets\";\nimport {\n GOOGLE_BUNDLE_PRESET_ID,\n googleOpenApiPresets,\n type GoogleOpenApiPreset,\n} from \"../sdk/google-presets\";\nimport type { SpecPreview } from \"../sdk/preview\";\nimport { type Authentication } from \"../sdk/types\";\nimport { resolveServerUrl } from \"../sdk/openapi-utils\";\nimport { detectedAuthenticationTemplates } from \"../sdk/derive-auth\";\n\nconst GOOGLE_BUNDLE_FAVICON = \"https://fonts.gstatic.com/s/i/productlogos/googleg/v6/192px.svg\";\n\n// The bundle picker opens with the featured Google APIs pre-checked.\nconst googleBundleDefaultPresetIds: ReadonlySet<string> = new Set(\n googleOpenApiPresets\n .filter((preset: GoogleOpenApiPreset) => preset.featured)\n .map((preset: GoogleOpenApiPreset) => preset.id),\n);\n\nconst googleBundleUrls = (\n selectedPresetIds: ReadonlySet<string>,\n customUrls: readonly string[],\n): readonly string[] => {\n const fromPresets = googleOpenApiPresets.flatMap((preset: GoogleOpenApiPreset) =>\n preset.url && selectedPresetIds.has(preset.id) ? [preset.url] : [],\n );\n // Preset URLs first (stable order), then any custom Discovery URLs, de-duped.\n return [...new Set([...fromPresets, ...customUrls])];\n};\n\nconst isGoogleDiscoveryUrl = (url: string): boolean => {\n const trimmed = url.trim();\n if (!URL.canParse(trimmed)) return false;\n const parsed = new URL(trimmed);\n const host = parsed.hostname.toLowerCase();\n if (!host.endsWith(\"googleapis.com\")) return false;\n return parsed.pathname.includes(\"/discovery/\") || parsed.pathname.includes(\"$discovery\");\n};\n\nconst normalizePresetUrl = (url: string): string => {\n const trimmed = url.trim();\n if (!URL.canParse(trimmed)) return trimmed.replace(/\\/$/, \"\");\n const parsed = new URL(trimmed);\n parsed.hash = \"\";\n parsed.searchParams.sort();\n return parsed.toString().replace(/\\/$/, \"\");\n};\n\nconst specInputForAdd = (input: string) => {\n const value = input.trim();\n const parsed = Effect.runSyncExit(\n Effect.try({\n try: () => new URL(value),\n catch: () => null,\n }),\n );\n return Exit.isSuccess(parsed)\n ? isGoogleDiscoveryUrl(value)\n ? { kind: \"googleDiscovery\" as const, url: value }\n : { kind: \"url\" as const, url: value }\n : { kind: \"blob\" as const, value };\n};\n\n// ---------------------------------------------------------------------------\n// Component — single progressive form. Post-redesign: preview → addSpec\n// (register the integration catalog entry with ALL detected auth methods) →\n// route to the integration's detail hub, where the user adds accounts. The add\n// flow no longer creates a connection.\n// ---------------------------------------------------------------------------\n\nexport default function AddOpenApiSource(props: {\n onComplete: (slug?: string) => void;\n onCancel: () => void;\n initialUrl?: string;\n initialPreset?: string;\n initialNamespace?: string;\n}) {\n const isGoogleBundlePreset = props.initialPreset === GOOGLE_BUNDLE_PRESET_ID;\n\n // Spec input. For the Google BUNDLE preset the input is a product picker (a set\n // of selected Discovery URLs), not a single spec URL/blob — the merge happens\n // server-side via `{ kind: \"googleDiscoveryBundle\", urls }`, so the textarea\n // preview path is bypassed entirely.\n const [specUrl, setSpecUrl] = useState(props.initialUrl ?? \"\");\n const [selectedPresetIds, setSelectedPresetIds] = useState<ReadonlySet<string>>(\n googleBundleDefaultPresetIds,\n );\n const [customDiscoveryUrls, setCustomDiscoveryUrls] = useState<readonly string[]>([]);\n const [analyzing, setAnalyzing] = useState(false);\n const [analyzeError, setAnalyzeError] = useState<string | null>(null);\n\n // After analysis\n const [preview, setPreview] = useState<SpecPreview | null>(null);\n const [baseUrl, setBaseUrl] = useState(\"\");\n // Agent-visible description: prefilled from the spec's `info.description`\n // until the user types (null = untouched, keep deriving from the preview).\n const [descriptionDraft, setDescriptionDraft] = useState<string | null>(null);\n const identityFallbackName = isGoogleBundlePreset\n ? \"Google\"\n : preview\n ? Option.getOrElse(preview.title, () => \"\")\n : \"\";\n const identity = useIntegrationIdentity({\n fallbackName: identityFallbackName,\n fallbackNamespace: props.initialNamespace ?? (isGoogleBundlePreset ? \"google\" : undefined),\n });\n\n const bundleDiscoveryUrls = useMemo(\n () => googleBundleUrls(selectedPresetIds, customDiscoveryUrls),\n [selectedPresetIds, customDiscoveryUrls],\n );\n\n const toggleBundlePreset = useCallback((presetId: string, checked: boolean) => {\n setSelectedPresetIds((current: ReadonlySet<string>) => {\n const next = new Set(current);\n if (checked) next.add(presetId);\n else next.delete(presetId);\n return next;\n });\n }, []);\n\n const addCustomDiscoveryUrl = useCallback((url: string) => {\n setCustomDiscoveryUrls((current: readonly string[]) =>\n current.includes(url) ? current : [...current, url],\n );\n }, []);\n\n const removeCustomDiscoveryUrl = useCallback((url: string) => {\n setCustomDiscoveryUrls((current: readonly string[]) =>\n current.filter((entry: string) => entry !== url),\n );\n }, []);\n\n // Submit\n const [adding, setAdding] = useState(false);\n const [addError, setAddError] = useState<string | null>(null);\n\n const doPreview = useAtomSet(previewOpenApiSpec, { mode: \"promiseExit\" });\n const doAdd = useAtomSet(addOpenApiSpec, { mode: \"promiseExit\" });\n\n // Keep the latest handleAnalyze in a ref so the debounced effect doesn't need\n // it as a dependency (it closes over fresh state).\n const handleAnalyzeRef = useRef<() => void>(() => {});\n\n useEffect(() => {\n // The bundle preset never analyzes a single spec — its input is the picker.\n if (isGoogleBundlePreset) return;\n const trimmed = specUrl.trim();\n if (!trimmed) return;\n if (preview) return;\n const handle = setTimeout(() => {\n handleAnalyzeRef.current();\n }, 400);\n return () => clearTimeout(handle);\n }, [specUrl, preview, isGoogleBundlePreset]);\n\n // ---- Derived state ----\n\n const previewHasNoServers = preview !== null && preview.servers.length === 0;\n // Offer the spec's servers (resolved with defaults) as base-URL choices when\n // there's more than one; a single/no server uses a plain input.\n const baseUrlOptions =\n preview && preview.servers.length > 1\n ? preview.servers.map((server) => {\n const url = resolveServerUrl(server.url, Option.getOrUndefined(server.variables), {});\n return { value: url, label: url };\n })\n : undefined;\n const firstServer = preview?.servers[0];\n const firstServerUrl = firstServer\n ? resolveServerUrl(firstServer.url, Option.getOrUndefined(firstServer.variables), {})\n : \"\";\n const previewPresetIcon =\n openApiPresets.find(\n (preset) => preset.url && normalizePresetUrl(preset.url) === normalizePresetUrl(specUrl),\n )?.icon ?? null;\n\n const resolvedBaseUrl = baseUrl.trim();\n const resolvedSourceId =\n slugifyNamespace(identity.namespace) ||\n (preview ? Option.getOrElse(preview.title, () => \"openapi\") : \"openapi\");\n const resolvedDisplayName =\n identity.name.trim() ||\n (preview ? Option.getOrElse(preview.title, () => resolvedSourceId) : resolvedSourceId);\n const resolvedDescription =\n descriptionDraft ?? (preview ? Option.getOrElse(preview.description, () => \"\") : \"\");\n\n // Register EVERY spec-detected auth method, not just a single selected one.\n // Keyed off `preview` (stable per analysis) so the memo doesn't re-run on the\n // freshly-allocated `?? []` fallback arrays.\n const authenticationTemplate: readonly Authentication[] = useMemo(\n () =>\n detectedAuthenticationTemplates(\n preview?.headerPresets ?? [],\n preview?.oauth2Presets ?? [],\n resolvedBaseUrl,\n ),\n [preview, resolvedBaseUrl],\n );\n\n // Editable auth methods, seeded from the spec-detected templates. The add flow\n // registers EVERY method (P6) — so this is a LIST, preserving multi-method\n // specs (e.g. apiKey + OAuth). Each seed carries the detected template's\n // original slug, so an unedited detected method submits with its EXACT\n // original slug (preserving behavior); added methods (no seed) get a\n // deterministic fresh slug. Re-seeded whenever a fresh detection arrives\n // (keyed on the detected templates, stable per analysis + base URL).\n const authMethodSeeds: readonly AuthMethodSeed[] = useMemo(() => {\n const labels = [\n ...(preview?.headerPresets ?? []).map((preset) => preset.label),\n ...(preview?.oauth2Presets ?? []).map((preset) => preset.label),\n ];\n return authenticationTemplate.map(\n (template: Authentication, index: number): AuthMethodSeed => ({\n value: editorValueFromAuthentication(template),\n slug: String(template.slug),\n ...(labels[index] !== undefined ? { label: labels[index] } : {}),\n }),\n );\n }, [preview, authenticationTemplate]);\n const authMethodList = useAuthMethodList(authMethodSeeds);\n\n // The methods to register, mapped back to stored `Authentication[]`. Drops\n // `none` rows (nothing to register). An unedited detected method keeps its\n // original `seedSlug`; an added method gets a deterministic fresh slug.\n const editedAuthenticationTemplate: readonly Authentication[] = useMemo(() => {\n const templates: Authentication[] = [];\n authMethodList.rows.forEach((row: AuthMethodRow, index: number) => {\n const slug =\n row.seedSlug ?? (row.value.kind === \"oauth\" ? `oauth-${index}` : `apikey-${index}`);\n const template = authenticationFromEditorValue(row.value, slug);\n if (template !== null) templates.push(template);\n });\n return templates;\n }, [authMethodList.rows]);\n\n // Pre-empt the API's `IntegrationAlreadyExistsError`: adding an integration\n // whose slug already exists clobbers the existing one's connections/policies,\n // so the API blocks it. Surface that here from the tenant-scoped catalog list.\n const slugAlreadyExists = useSlugAlreadyExists(resolvedSourceId);\n\n // The bundle path is ready once at least one Google API is selected (no\n // network preview gates it); the single/custom-spec path still requires a\n // successful preview. Both require a base URL and a free slug.\n const hasPreviewOrBundle = isGoogleBundlePreset\n ? bundleDiscoveryUrls.length > 0\n : preview !== null;\n // The base URL is optional when the spec declares servers (resolved per call);\n // required only when it doesn't.\n const canAdd =\n hasPreviewOrBundle &&\n !slugAlreadyExists &&\n (!previewHasNoServers || resolvedBaseUrl.length > 0);\n\n // ---- Handlers ----\n\n const handleAnalyze = async () => {\n setAnalyzing(true);\n setAnalyzeError(null);\n setAddError(null);\n const exit = await doPreview({ payload: { spec: specUrl } });\n if (Exit.isFailure(exit)) {\n setAnalyzeError(errorMessageFromExit(exit, \"Failed to parse spec\"));\n setAnalyzing(false);\n return;\n }\n const result = exit.value;\n setPreview(result);\n setBaseUrl(\"\");\n setAnalyzing(false);\n };\n\n handleAnalyzeRef.current = handleAnalyze;\n\n // Persist the integration and return its slug. Registers the catalog entry\n // with every detected auth method. Adding a slug that already exists is\n // rejected by the API (`IntegrationAlreadyExistsError`) — surfaced inline.\n const ensureIntegration = useCallback(async (): Promise<IntegrationSlug | null> => {\n // The Google BUNDLE preset emits the multi-service bundle input; the server\n // merges the selected Discovery documents into one `google` spec and stores\n // the unioned `googleOAuth2` auth template (so no client template is sent).\n // Every other preset/custom input keeps the single-spec url/blob/discovery\n // branch unchanged.\n const specForAdd = isGoogleBundlePreset\n ? ({ kind: \"googleDiscoveryBundle\" as const, urls: [...bundleDiscoveryUrls] } satisfies {\n readonly kind: \"googleDiscoveryBundle\";\n readonly urls: readonly string[];\n })\n : specInputForAdd(specUrl);\n const exit = await doAdd({\n payload: {\n spec: specForAdd,\n slug: resolvedSourceId,\n name: resolvedDisplayName,\n ...(resolvedDescription.trim().length > 0\n ? { description: resolvedDescription.trim() }\n : {}),\n baseUrl: resolvedBaseUrl,\n // Always send the edited method list (even empty) when the user has\n // inspected a preview: an explicit [] means \"no auth methods\", while\n // OMITTING the field tells the server to derive defaults from the\n // spec — which would silently resurrect methods the user deleted.\n // The Google bundle path stays omitted; its auth is converter-derived\n // server-side.\n ...(!isGoogleBundlePreset\n ? {\n // Serialize to the wire input dialect (apikey → request-shaped).\n authenticationTemplate: editedAuthenticationTemplate.map(openApiWireAuthInput),\n }\n : {}),\n },\n reactivityKeys: integrationWriteKeys,\n });\n if (Exit.isFailure(exit)) {\n setAddError(addIntegrationErrorMessage(exit, resolvedSourceId, \"Failed to add integration\"));\n return null;\n }\n return exit.value.slug;\n }, [\n isGoogleBundlePreset,\n bundleDiscoveryUrls,\n specUrl,\n doAdd,\n resolvedSourceId,\n resolvedDisplayName,\n resolvedDescription,\n resolvedBaseUrl,\n editedAuthenticationTemplate,\n ]);\n\n const handleAdd = async () => {\n setAdding(true);\n setAddError(null);\n\n const integration = await ensureIntegration();\n if (!integration) {\n setAdding(false);\n return;\n }\n\n props.onComplete(String(integration));\n };\n\n // ---- Render ----\n\n return (\n <div className=\"flex flex-1 flex-col gap-6\">\n <div>\n <h1 className=\"text-xl font-semibold text-foreground\">\n {isGoogleBundlePreset ? \"Add Google\" : \"Add OpenAPI Integration\"}\n </h1>\n {isGoogleBundlePreset ? (\n <p className=\"mt-1 text-[13px] text-muted-foreground\">\n Bundle Google APIs into one integration from their Discovery documents and register\n their methods as tools under a single shared OAuth consent.\n </p>\n ) : null}\n </div>\n\n {isGoogleBundlePreset ? (\n <GoogleProductPicker\n selectedPresetIds={selectedPresetIds}\n onToggle={toggleBundlePreset}\n customUrls={customDiscoveryUrls}\n onAddCustomUrl={addCustomDiscoveryUrl}\n onRemoveCustomUrl={removeCustomDiscoveryUrl}\n />\n ) : !preview ? (\n <CardStack>\n <CardStackContent className=\"border-t-0\">\n <div className=\"space-y-2 p-3\">\n <FieldLabel>OpenAPI Spec</FieldLabel>\n <div className=\"relative\">\n <Textarea\n value={specUrl}\n onChange={(e) => setSpecUrl((e.target as HTMLTextAreaElement).value)}\n placeholder=\"https://api.example.com/openapi.json\"\n rows={3}\n maxRows={10}\n className=\"font-mono text-sm\"\n />\n {analyzing && (\n <div className=\"pointer-events-none absolute right-2 top-2\">\n <IOSSpinner className=\"size-4\" />\n </div>\n )}\n </div>\n <p className=\"text-[11px] text-muted-foreground\">\n Paste a URL or raw JSON/YAML content.\n </p>\n </div>\n </CardStackContent>\n </CardStack>\n ) : null}\n\n {isGoogleBundlePreset ? (\n <OpenApiSourceDetailsFields\n title=\"Google\"\n subtitle={`${bundleDiscoveryUrls.length} Google API${\n bundleDiscoveryUrls.length !== 1 ? \"s\" : \"\"\n } · one shared OAuth consent`}\n identity={identity}\n description={resolvedDescription}\n onDescriptionChange={setDescriptionDraft}\n baseUrl={resolvedBaseUrl}\n onBaseUrlChange={setBaseUrl}\n baseUrlLabel=\"Base URL override (optional)\"\n faviconIcon={GOOGLE_BUNDLE_FAVICON}\n faviconUrl={resolvedBaseUrl}\n />\n ) : preview ? (\n <OpenApiSourceDetailsFields\n title={Option.getOrElse(preview.title, () => \"API\")}\n subtitle={`${Option.getOrElse(preview.version, () => \"\")}${\n Option.isSome(preview.version) ? \" · \" : \"\"\n }${preview.operationCount} operation${preview.operationCount !== 1 ? \"s\" : \"\"}${\n preview.tags.length > 0\n ? ` · ${preview.tags.length} tag${preview.tags.length !== 1 ? \"s\" : \"\"}`\n : \"\"\n }`}\n identity={identity}\n description={resolvedDescription}\n onDescriptionChange={setDescriptionDraft}\n baseUrl={resolvedBaseUrl}\n onBaseUrlChange={setBaseUrl}\n baseUrlOptions={baseUrlOptions}\n baseUrlLabel={previewHasNoServers ? \"Base URL\" : \"Base URL override (optional)\"}\n baseUrlPlaceholder={firstServerUrl || \"https://api.example.com\"}\n baseUrlHint={\n previewHasNoServers\n ? undefined\n : \"Overrides the spec's servers; leave empty to choose the server (and variables) per tool call.\"\n }\n baseUrlMissingMessage={\n previewHasNoServers ? \"This spec declares no servers — enter a base URL.\" : undefined\n }\n specUrl={specUrl}\n onSpecUrlChange={(value) => {\n setSpecUrl(value);\n setPreview(null);\n setBaseUrl(\"\");\n }}\n faviconIcon={previewPresetIcon}\n faviconUrl={resolvedBaseUrl || firstServerUrl}\n />\n ) : null}\n\n {analyzeError && <FormErrorAlert message={analyzeError} />}\n\n {preview && !isGoogleBundlePreset && (\n <AuthMethodListEditor\n list={authMethodList}\n emptyHint=\"No authentication detected. Add a method, or add the integration without auth and connect an account from the integration page later.\"\n footerHint=\"Every method here is registered with the integration. Connect an account from the integration page after adding.\"\n />\n )}\n\n {hasPreviewOrBundle && slugAlreadyExists && !adding && (\n <SlugCollisionAlert slug={resolvedSourceId} />\n )}\n\n {addError && <FormErrorAlert message={addError} />}\n\n <FloatActions>\n <Button variant=\"ghost\" onClick={() => props.onCancel()} disabled={adding}>\n Cancel\n </Button>\n {(hasPreviewOrBundle || isGoogleBundlePreset) && (\n <Button onClick={() => void handleAdd()} disabled={!canAdd || adding}>\n {adding && <Spinner className=\"size-3.5\" />}\n {adding ? \"Adding…\" : isGoogleBundlePreset ? \"Connect Google\" : \"Add integration\"}\n </Button>\n )}\n </FloatActions>\n </div>\n );\n}\n","import {\n CardStack,\n CardStackContent,\n CardStackEntry,\n CardStackEntryContent,\n CardStackEntryDescription,\n CardStackEntryField,\n CardStackEntryTitle,\n} from \"@executor-js/react/components/card-stack\";\nimport {\n FreeformCombobox,\n type FreeformComboboxOption,\n} from \"@executor-js/react/components/combobox\";\nimport { Input } from \"@executor-js/react/components/input\";\nimport { Textarea } from \"@executor-js/react/components/textarea\";\nimport { IntegrationFavicon } from \"@executor-js/react/components/integration-favicon\";\nimport {\n IntegrationIdentityFieldRows,\n type IntegrationIdentity,\n} from \"@executor-js/react/plugins/integration-identity\";\n\n/** The spec input is shown as a one-line \"Spec URL\" field only when it IS a\n * URL; pasted document content gets a multi-line editor instead. */\nconst isUrlInput = (value: string): boolean => URL.canParse(value.trim());\n\nexport function OpenApiSourceDetailsFields(props: {\n readonly title: string;\n readonly subtitle?: string;\n readonly identity: IntegrationIdentity;\n /** The integration's agent-visible description (prefilled from the spec). */\n readonly description?: string;\n readonly onDescriptionChange?: (value: string) => void;\n readonly baseUrl: string;\n readonly onBaseUrlChange: (value: string) => void;\n readonly baseUrlOptions?: readonly FreeformComboboxOption[];\n readonly baseUrlLabel?: string;\n readonly baseUrlPlaceholder?: string;\n readonly baseUrlHint?: string;\n readonly specUrl?: string;\n readonly onSpecUrlChange?: (value: string) => void;\n readonly faviconIcon?: string | null;\n readonly faviconUrl?: string;\n readonly namespaceReadOnly?: boolean;\n readonly specUrlDisabled?: boolean;\n readonly saveState?: \"idle\" | \"saving\" | \"saved\";\n readonly baseUrlMissingMessage?: string;\n readonly footer?: string;\n}) {\n const specIsUrl = props.specUrl !== undefined && isUrlInput(props.specUrl);\n\n return (\n <CardStack>\n <CardStackContent className=\"border-t-0\">\n <CardStackEntry>\n {(props.faviconIcon || props.faviconUrl) && (\n <IntegrationFavicon icon={props.faviconIcon} url={props.faviconUrl} size={16} />\n )}\n <CardStackEntryContent>\n <CardStackEntryTitle>{props.title}</CardStackEntryTitle>\n {props.subtitle && (\n <CardStackEntryDescription>{props.subtitle}</CardStackEntryDescription>\n )}\n </CardStackEntryContent>\n {props.saveState && props.saveState !== \"idle\" && (\n <span className=\"text-xs text-muted-foreground\">\n {props.saveState === \"saving\" ? \"Saving…\" : \"Saved\"}\n </span>\n )}\n </CardStackEntry>\n <IntegrationIdentityFieldRows\n identity={props.identity}\n namespaceReadOnly={props.namespaceReadOnly}\n />\n {props.onDescriptionChange && (\n <CardStackEntryField label=\"Description\">\n <Textarea\n value={props.description ?? \"\"}\n onChange={(e) => props.onDescriptionChange?.((e.target as HTMLTextAreaElement).value)}\n placeholder=\"What this API is and when to reach for it\"\n rows={2}\n maxRows={6}\n className=\"text-sm\"\n />\n </CardStackEntryField>\n )}\n <div className=\"grid grid-cols-1 md:grid-cols-2\">\n <CardStackEntryField label={props.baseUrlLabel ?? \"Base URL\"}>\n {props.baseUrlOptions && props.baseUrlOptions.length > 0 ? (\n <FreeformCombobox\n value={props.baseUrl}\n onValueChange={props.onBaseUrlChange}\n options={props.baseUrlOptions}\n placeholder={props.baseUrlPlaceholder ?? \"https://api.example.com\"}\n className=\"w-full\"\n inputClassName=\"font-mono text-sm\"\n />\n ) : (\n <Input\n value={props.baseUrl}\n onChange={(e) => props.onBaseUrlChange((e.target as HTMLInputElement).value)}\n placeholder={props.baseUrlPlaceholder ?? \"https://api.example.com\"}\n className=\"font-mono text-sm\"\n />\n )}\n\n {props.baseUrlMissingMessage && !props.baseUrl && (\n <p className=\"text-[11px] text-amber-600 dark:text-amber-400\">\n {props.baseUrlMissingMessage}\n </p>\n )}\n {props.baseUrlHint && (\n <p className=\"text-[11px] text-muted-foreground\">{props.baseUrlHint}</p>\n )}\n </CardStackEntryField>\n {specIsUrl && props.onSpecUrlChange && (\n <CardStackEntryField label=\"Spec URL\">\n <Input\n value={props.specUrl}\n onChange={(e) => props.onSpecUrlChange?.((e.target as HTMLInputElement).value)}\n placeholder=\"https://api.example.com/openapi.json\"\n className=\"font-mono text-sm\"\n disabled={props.specUrlDisabled}\n />\n </CardStackEntryField>\n )}\n </div>\n {props.specUrl !== undefined && !specIsUrl && props.onSpecUrlChange && (\n <CardStackEntryField label=\"Spec\">\n <Textarea\n value={props.specUrl}\n onChange={(e) => props.onSpecUrlChange?.((e.target as HTMLTextAreaElement).value)}\n placeholder=\"Pasted OpenAPI JSON/YAML\"\n rows={4}\n maxRows={12}\n className=\"font-mono text-xs\"\n disabled={props.specUrlDisabled}\n />\n </CardStackEntryField>\n )}\n {props.footer && (\n <CardStackEntry>\n <CardStackEntryContent>\n <CardStackEntryTitle>{props.footer}</CardStackEntryTitle>\n </CardStackEntryContent>\n </CardStackEntry>\n )}\n </CardStackContent>\n </CardStack>\n );\n}\n","import { useMemo, useState } from \"react\";\nimport { ChevronDownIcon, PlusIcon, TriangleAlert, XIcon } from \"lucide-react\";\n\nimport { cn } from \"@executor-js/react/lib/utils\";\nimport { Badge } from \"@executor-js/react/components/badge\";\nimport { Button } from \"@executor-js/react/components/button\";\nimport { Checkbox } from \"@executor-js/react/components/checkbox\";\nimport {\n Collapsible,\n CollapsibleContent,\n CollapsibleTrigger,\n} from \"@executor-js/react/components/collapsible\";\nimport { FieldLabel } from \"@executor-js/react/components/field\";\nimport { Input } from \"@executor-js/react/components/input\";\nimport { IntegrationFavicon } from \"@executor-js/react/components/integration-favicon\";\n\nimport {\n googleOAuthConsentScopesForPreset,\n googleOpenApiPresets,\n type GoogleOpenApiOAuthAudience,\n type GoogleOpenApiPreset,\n} from \"../sdk/google-presets\";\nimport { googleOAuthConsentBatches } from \"../sdk/google-oauth-batches\";\nimport { isGoogleDiscoveryUrl } from \"../sdk/google-discovery\";\n\n// ---------------------------------------------------------------------------\n// GoogleProductPicker — the \"customize your Google connection\" surface.\n//\n// A checkable card grid over `googleOpenApiPresets`, grouped/annotated by\n// `oauthAudience`. The user picks which Google APIs to bundle into the single\n// `google` integration; the parent turns the selected discovery URLs into a\n// `{ kind: \"googleDiscoveryBundle\", urls }` add. A \"View scopes\" panel previews\n// the unioned OAuth consent (via `googleOAuthConsentBatches`) BEFORE connecting,\n// and a custom-URL escape hatch lets advanced users paste any Google Discovery\n// document the preset list doesn't cover.\n// ---------------------------------------------------------------------------\n\n// Audience groups, ordered from least- to most-privileged. The warning tiers\n// (`workspace-admin`, `unsupported-user`) carry a caution chip so the user sees\n// the consent risk before selecting.\nconst AUDIENCE_ORDER: readonly GoogleOpenApiOAuthAudience[] = [\n \"standard-user\",\n \"advanced-user\",\n \"workspace-admin\",\n \"unsupported-user\",\n];\n\nconst AUDIENCE_LABEL: Readonly<Record<GoogleOpenApiOAuthAudience, string>> = {\n \"standard-user\": \"Core Google services\",\n \"advanced-user\": \"Advanced services\",\n \"workspace-admin\": \"Workspace admin\",\n \"unsupported-user\": \"Limited user consent\",\n};\n\nconst AUDIENCE_DESCRIPTION: Readonly<Record<GoogleOpenApiOAuthAudience, string>> = {\n \"standard-user\": \"Connect with a normal Google account — one consent screen.\",\n \"advanced-user\": \"Broader scopes that may need an unverified-app warning to be accepted.\",\n \"workspace-admin\": \"Requires a Google Workspace admin account; not available on personal Gmail.\",\n \"unsupported-user\": \"Google does not grant these scopes through standard user OAuth consent.\",\n};\n\nconst audienceNeedsWarning = (audience: GoogleOpenApiOAuthAudience): boolean =>\n audience === \"workspace-admin\" || audience === \"unsupported-user\";\n\ntype GoogleProductPickerProps = {\n readonly selectedPresetIds: ReadonlySet<string>;\n readonly onToggle: (presetId: string, checked: boolean) => void;\n readonly customUrls: readonly string[];\n readonly onAddCustomUrl: (url: string) => void;\n readonly onRemoveCustomUrl: (url: string) => void;\n};\n\nconst AudienceWarningChip = ({ audience }: { audience: GoogleOpenApiOAuthAudience }) =>\n audience === \"workspace-admin\" ? (\n <Badge\n variant=\"outline\"\n className=\"shrink-0 border-amber-500/40 text-amber-700 dark:text-amber-400\"\n >\n <TriangleAlert className=\"size-3\" />\n Admin only\n </Badge>\n ) : audience === \"unsupported-user\" ? (\n <Badge variant=\"outline\" className=\"shrink-0 border-destructive/40 text-destructive\">\n <TriangleAlert className=\"size-3\" />\n Limited consent\n </Badge>\n ) : null;\n\n// A Google API row — borderless and single-line, leaning on hover/selected\n// fills instead of per-item card chrome (lightest separation that still reads).\n// Name + truncated summary share one baseline for a dense, scannable two-column\n// list; the audience warning chip trails on the right.\nconst ProductRow = ({\n preset,\n checked,\n onToggle,\n}: {\n readonly preset: GoogleOpenApiPreset;\n readonly checked: boolean;\n readonly onToggle: (checked: boolean) => void;\n}) => (\n <FieldLabel\n className={cn(\n // `w-full` overrides FieldLabel's base `w-fit` (which would size the row to\n // its content and overflow the column); `min-w-0` then lets the cell shrink\n // to its track so the name/summary truncates instead of spilling over.\n \"flex w-full min-w-0 cursor-pointer items-center gap-2.5 rounded-md px-2 py-1.5 transition-colors\",\n checked ? \"bg-primary/5\" : \"hover:bg-muted/40\",\n )}\n >\n <Checkbox checked={checked} onCheckedChange={(next) => onToggle(next === true)} />\n <div className=\"shrink-0\">\n <IntegrationFavicon icon={preset.icon} url={preset.url} size={16} />\n </div>\n {/* One truncating line — the name + summary clip to the cell with an\n ellipsis instead of overflowing into the neighbouring column. */}\n <div className=\"min-w-0 flex-1 truncate text-sm\">\n <span className=\"font-medium text-foreground\">{preset.name}</span>{\" \"}\n <span className=\"text-[11px] text-muted-foreground\">{preset.summary}</span>\n </div>\n <AudienceWarningChip audience={preset.oauthAudience} />\n </FieldLabel>\n);\n\nconst CustomUrlEscapeHatch = ({\n customUrls,\n onAddCustomUrl,\n onRemoveCustomUrl,\n}: {\n readonly customUrls: readonly string[];\n readonly onAddCustomUrl: (url: string) => void;\n readonly onRemoveCustomUrl: (url: string) => void;\n}) => {\n const [draft, setDraft] = useState(\"\");\n const trimmed = draft.trim();\n const isValid = isGoogleDiscoveryUrl(trimmed);\n const isDuplicate = customUrls.includes(trimmed);\n\n const commit = () => {\n if (!isValid || isDuplicate) return;\n onAddCustomUrl(trimmed);\n setDraft(\"\");\n };\n\n return (\n <div className=\"space-y-2\">\n <FieldLabel className=\"text-[11px] font-medium text-muted-foreground\">\n Add a custom Google Discovery URL\n </FieldLabel>\n <div className=\"flex items-center gap-2\">\n <Input\n value={draft}\n onChange={(event: React.ChangeEvent<HTMLInputElement>) => setDraft(event.target.value)}\n onKeyDown={(event: React.KeyboardEvent<HTMLInputElement>) => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n commit();\n }\n }}\n placeholder=\"https://www.googleapis.com/discovery/v1/apis/<service>/<version>/rest\"\n className=\"font-mono text-[11px]\"\n />\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={!isValid || isDuplicate}\n onClick={commit}\n >\n <PlusIcon className=\"size-3.5\" />\n Add\n </Button>\n </div>\n {trimmed.length > 0 && !isValid ? (\n <p className=\"text-[11px] text-destructive\">\n Enter a Google Discovery document URL (a *.googleapis.com discovery/$discovery endpoint).\n </p>\n ) : null}\n {customUrls.length > 0 ? (\n <ul className=\"space-y-1\">\n {customUrls.map((url: string) => (\n <li\n key={url}\n className=\"flex items-center justify-between gap-2 rounded-md border border-border bg-muted/20 px-2.5 py-1.5\"\n >\n <span className=\"truncate font-mono text-[11px] text-foreground\">{url}</span>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-6 shrink-0\"\n onClick={() => onRemoveCustomUrl(url)}\n aria-label={`Remove ${url}`}\n >\n <XIcon className=\"size-3.5\" />\n </Button>\n </li>\n ))}\n </ul>\n ) : null}\n </div>\n );\n};\n\nexport function GoogleProductPicker({\n selectedPresetIds,\n onToggle,\n customUrls,\n onAddCustomUrl,\n onRemoveCustomUrl,\n}: GoogleProductPickerProps) {\n const [scopesOpen, setScopesOpen] = useState(false);\n\n const groups = useMemo(\n () =>\n AUDIENCE_ORDER.flatMap((audience: GoogleOpenApiOAuthAudience) => {\n const presets = googleOpenApiPresets.filter(\n (preset: GoogleOpenApiPreset) => preset.oauthAudience === audience,\n );\n return presets.length > 0 ? [{ audience, presets }] : [];\n }),\n [],\n );\n\n // The \"View scopes\" preview unions the selected presets' representative\n // consent scopes through `googleOAuthConsentBatches`, mirroring the unioned\n // scopes the bundle converter ultimately stores on the integration.\n const consentBatches = useMemo(\n () =>\n googleOAuthConsentBatches(\n googleOpenApiPresets\n .filter((preset: GoogleOpenApiPreset) => selectedPresetIds.has(preset.id))\n .map((preset: GoogleOpenApiPreset) => ({\n id: preset.id,\n name: preset.name,\n oauthAudience: preset.oauthAudience,\n scopes: googleOAuthConsentScopesForPreset(preset.id),\n })),\n ),\n [selectedPresetIds],\n );\n\n const selectedCount = selectedPresetIds.size + customUrls.length;\n\n return (\n <section className=\"space-y-4\">\n <div className=\"space-y-1\">\n <FieldLabel>Customize your Google connection</FieldLabel>\n <p className=\"text-[11px] text-muted-foreground\">\n Pick the Google APIs to bundle into one connection. They share a single OAuth consent and\n appear as merged tools under one Google integration.\n </p>\n </div>\n\n {groups.map(\n ({\n audience,\n presets,\n }: {\n readonly audience: GoogleOpenApiOAuthAudience;\n readonly presets: readonly GoogleOpenApiPreset[];\n }) => (\n <div key={audience} className=\"space-y-2\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-[11px] font-semibold tracking-wide text-foreground uppercase\">\n {AUDIENCE_LABEL[audience]}\n </span>\n {audienceNeedsWarning(audience) ? <AudienceWarningChip audience={audience} /> : null}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"ml-auto h-auto px-1.5 py-0.5 text-[11px] font-normal text-muted-foreground hover:bg-transparent hover:text-foreground\"\n onClick={() => {\n const allSelected = presets.every((preset: GoogleOpenApiPreset) =>\n selectedPresetIds.has(preset.id),\n );\n presets.forEach((preset: GoogleOpenApiPreset) =>\n onToggle(preset.id, !allSelected),\n );\n }}\n >\n {presets.every((preset: GoogleOpenApiPreset) => selectedPresetIds.has(preset.id))\n ? \"Clear\"\n : \"Select all\"}\n </Button>\n </div>\n <p className=\"text-[11px] text-muted-foreground\">{AUDIENCE_DESCRIPTION[audience]}</p>\n <div className=\"grid grid-cols-1 gap-x-4 gap-y-0.5 sm:grid-cols-2\">\n {presets.map((preset: GoogleOpenApiPreset) => (\n <ProductRow\n key={preset.id}\n preset={preset}\n checked={selectedPresetIds.has(preset.id)}\n onToggle={(checked: boolean) => onToggle(preset.id, checked)}\n />\n ))}\n </div>\n </div>\n ),\n )}\n\n <CustomUrlEscapeHatch\n customUrls={customUrls}\n onAddCustomUrl={onAddCustomUrl}\n onRemoveCustomUrl={onRemoveCustomUrl}\n />\n\n <div className=\"space-y-1.5 rounded-lg border border-border bg-muted/10 px-3 py-2.5\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-[11px] font-semibold tracking-wide text-foreground uppercase\">\n Authentication\n </span>\n <Badge variant=\"secondary\">OAuth</Badge>\n </div>\n <p className=\"text-[11px] text-muted-foreground\">\n The selected Google APIs share one OAuth consent. Review the scopes below, then connect a\n Google account from the integration page after adding.\n </p>\n </div>\n\n <Collapsible open={scopesOpen} onOpenChange={setScopesOpen}>\n <CollapsibleTrigger asChild>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" disabled={consentBatches.length === 0}>\n <ChevronDownIcon\n className={cn(\"size-3.5 transition-transform\", scopesOpen ? \"rotate-180\" : \"\")}\n />\n View scopes\n {selectedCount > 0 ? (\n <Badge variant=\"secondary\" className=\"ml-1\">\n {selectedCount}\n </Badge>\n ) : null}\n </Button>\n </CollapsibleTrigger>\n <CollapsibleContent className=\"pt-3\">\n {consentBatches.length === 0 ? (\n <p className=\"text-[11px] text-muted-foreground\">\n Select at least one Google API to preview the OAuth consent.\n </p>\n ) : (\n <div className=\"space-y-3\">\n {consentBatches.map((batch) => (\n <div key={batch.id} className=\"space-y-1.5\">\n <span className=\"text-[11px] font-semibold text-foreground\">{batch.label}</span>\n <ul className=\"space-y-1\">\n {batch.apiScopes.map((scope: string) => (\n <li\n key={scope}\n className=\"rounded-md border border-border bg-muted/20 px-2.5 py-1 font-mono text-[11px] break-all text-muted-foreground\"\n >\n {scope}\n </li>\n ))}\n </ul>\n </div>\n ))}\n </div>\n )}\n </CollapsibleContent>\n </Collapsible>\n </section>\n );\n}\n\nexport default GoogleProductPicker;\n","import type { GoogleOpenApiOAuthAudience } from \"./google-presets\";\nimport { compactGoogleOAuthScopes } from \"./google-oauth-scopes\";\n\nexport type GoogleOAuthBatchInput = {\n readonly id: string;\n readonly name: string;\n readonly oauthAudience: GoogleOpenApiOAuthAudience;\n readonly scopes: readonly string[];\n};\n\nexport type GoogleOAuthConsentBatch = {\n readonly id: string;\n readonly label: string;\n readonly apiScopes: readonly string[];\n};\n\nconst GOOGLE_CLOUD_BATCH_IDS = new Set([\"google-bigquery\", \"google-cloud-resource-manager\"]);\n\nexport const googleOAuthConsentBatches = (\n items: readonly GoogleOAuthBatchInput[],\n): readonly GoogleOAuthConsentBatch[] => {\n const standardScopes: string[] = [];\n const cloudScopes: string[] = [];\n const batches: GoogleOAuthConsentBatch[] = [];\n\n for (const item of items) {\n if (item.scopes.length === 0) continue;\n if (item.oauthAudience === \"standard-user\") {\n standardScopes.push(...item.scopes);\n continue;\n }\n if (GOOGLE_CLOUD_BATCH_IDS.has(item.id)) {\n cloudScopes.push(...item.scopes);\n continue;\n }\n batches.push({\n id: item.id,\n label: item.name,\n apiScopes: item.scopes,\n });\n }\n\n const compactedStandardScopes = compactGoogleOAuthScopes(standardScopes);\n const compactedCloudScopes = compactGoogleOAuthScopes(cloudScopes);\n return [\n ...(compactedStandardScopes.length > 0\n ? [\n {\n id: \"google-core\",\n label: \"Core Google services\",\n apiScopes: compactedStandardScopes,\n },\n ]\n : []),\n ...batches.map((batch) => ({\n ...batch,\n apiScopes: compactGoogleOAuthScopes(batch.apiScopes),\n })),\n ...(compactedCloudScopes.length > 0\n ? [\n {\n id: \"google-cloud\",\n label: \"Google Cloud services\",\n apiScopes: compactedCloudScopes,\n },\n ]\n : []),\n ].filter((batch) => batch.apiScopes.length > 0);\n};\n"],"mappings":"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","names":["useMemo","useState","Button","CardStack","CardStackContent","FieldLabel","Textarea","Input","IntegrationFavicon","jsx","jsxs","IntegrationFavicon","Input","jsx","jsxs","isGoogleDiscoveryUrl","useState","useMemo","CardStack","CardStackContent","FieldLabel","Textarea","Button"]}
|
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
import {
|
|
10
10
|
openApiConfigAtom,
|
|
11
11
|
openapiConfigure
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-UJTNRH4Q.js";
|
|
13
|
+
import "./chunk-RFSMGUBJ.js";
|
|
14
14
|
|
|
15
15
|
// src/react/OpenApiAccountsPanel.tsx
|
|
16
16
|
import { useCallback, useMemo } from "react";
|
|
@@ -111,4 +111,4 @@ function OpenApiAccountsPanel(props) {
|
|
|
111
111
|
export {
|
|
112
112
|
OpenApiAccountsPanel as default
|
|
113
113
|
};
|
|
114
|
-
//# sourceMappingURL=OpenApiAccountsPanel-
|
|
114
|
+
//# sourceMappingURL=OpenApiAccountsPanel-2LHWUKIE.js.map
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import {
|
|
2
|
+
openApiConfigAtom,
|
|
3
|
+
updateOpenApiSpec
|
|
4
|
+
} from "./chunk-UJTNRH4Q.js";
|
|
5
|
+
import "./chunk-RFSMGUBJ.js";
|
|
6
|
+
|
|
7
|
+
// src/react/UpdateSpecSection.tsx
|
|
8
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
9
|
+
import { useAtomSet, useAtomValue } from "@effect/atom-react";
|
|
10
|
+
import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
|
|
11
|
+
import * as Exit from "effect/Exit";
|
|
12
|
+
import { integrationWriteKeys } from "@executor-js/react/api/reactivity-keys";
|
|
13
|
+
import { messageFromExit } from "@executor-js/react/api/error-reporting";
|
|
14
|
+
import { FormErrorAlert } from "@executor-js/react/lib/integration-add";
|
|
15
|
+
import { IntegrationSlug } from "@executor-js/sdk/shared";
|
|
16
|
+
import { Checkbox } from "@executor-js/react/components/checkbox";
|
|
17
|
+
import { Label } from "@executor-js/react/components/label";
|
|
18
|
+
import { Textarea } from "@executor-js/react/components/textarea";
|
|
19
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
20
|
+
var outcomeSummary = (outcome) => {
|
|
21
|
+
const added = outcome.addedTools.length;
|
|
22
|
+
const removed = outcome.removedTools.length;
|
|
23
|
+
if (added === 0 && removed === 0) {
|
|
24
|
+
return `Spec updated \u2014 ${outcome.toolCount} tools, no changes to the tool list.`;
|
|
25
|
+
}
|
|
26
|
+
const parts = [
|
|
27
|
+
...added > 0 ? [`+${added} tool${added !== 1 ? "s" : ""}`] : [],
|
|
28
|
+
...removed > 0 ? [`\u2212${removed} tool${removed !== 1 ? "s" : ""}`] : []
|
|
29
|
+
];
|
|
30
|
+
return `Spec updated \u2014 ${parts.join(", ")} (${outcome.toolCount} total).`;
|
|
31
|
+
};
|
|
32
|
+
function UpdateSpecSection(props) {
|
|
33
|
+
const slug = IntegrationSlug.make(props.sourceId);
|
|
34
|
+
const configResult = useAtomValue(openApiConfigAtom(slug));
|
|
35
|
+
const doUpdate = useAtomSet(updateOpenApiSpec, { mode: "promiseExit" });
|
|
36
|
+
const [refetchStaged, setRefetchStaged] = useState(false);
|
|
37
|
+
const [pasted, setPasted] = useState("");
|
|
38
|
+
const [error, setError] = useState(null);
|
|
39
|
+
const config = AsyncResult.isSuccess(configResult) && configResult.value ? configResult.value : null;
|
|
40
|
+
const sourceUrl = config ? config.googleDiscoveryUrls?.length ? `${config.googleDiscoveryUrls.length} Google Discovery document${config.googleDiscoveryUrls.length !== 1 ? "s" : ""}` : config.sourceUrl : void 0;
|
|
41
|
+
const applyStaged = useCallback(async () => {
|
|
42
|
+
const spec = pasted.trim().length > 0 ? { kind: "blob", value: pasted } : void 0;
|
|
43
|
+
if (!spec && !refetchStaged) return { ok: true, summary: null };
|
|
44
|
+
setError(null);
|
|
45
|
+
const exit = await doUpdate({
|
|
46
|
+
params: { slug },
|
|
47
|
+
payload: spec ? { spec } : {},
|
|
48
|
+
reactivityKeys: integrationWriteKeys
|
|
49
|
+
});
|
|
50
|
+
if (Exit.isFailure(exit)) {
|
|
51
|
+
setError(messageFromExit(exit, "Failed to update spec"));
|
|
52
|
+
return { ok: false };
|
|
53
|
+
}
|
|
54
|
+
setRefetchStaged(false);
|
|
55
|
+
setPasted("");
|
|
56
|
+
return { ok: true, summary: outcomeSummary(exit.value) };
|
|
57
|
+
}, [doUpdate, pasted, refetchStaged, slug]);
|
|
58
|
+
const onPendingChangeRef = useRef(props.onPendingChange);
|
|
59
|
+
onPendingChangeRef.current = props.onPendingChange;
|
|
60
|
+
const hasStagedChange = refetchStaged || pasted.trim().length > 0;
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
onPendingChangeRef.current?.(hasStagedChange ? applyStaged : null);
|
|
63
|
+
return () => onPendingChangeRef.current?.(null);
|
|
64
|
+
}, [hasStagedChange, applyStaged]);
|
|
65
|
+
if (!config) return null;
|
|
66
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3 border-t border-border/60 pt-5", children: [
|
|
67
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
68
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground", children: "Update spec" }),
|
|
69
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Rebuild this integration's tools from the latest spec when you save. Connections, credentials, and policies are kept." })
|
|
70
|
+
] }),
|
|
71
|
+
sourceUrl ? /* @__PURE__ */ jsx("div", { className: "space-y-2", children: /* @__PURE__ */ jsxs(Label, { className: "flex items-start gap-2 text-sm font-normal", children: [
|
|
72
|
+
/* @__PURE__ */ jsx(
|
|
73
|
+
Checkbox,
|
|
74
|
+
{
|
|
75
|
+
checked: refetchStaged,
|
|
76
|
+
onCheckedChange: (checked) => setRefetchStaged(checked === true)
|
|
77
|
+
}
|
|
78
|
+
),
|
|
79
|
+
/* @__PURE__ */ jsxs("span", { className: "space-y-0.5", children: [
|
|
80
|
+
/* @__PURE__ */ jsx("span", { className: "block", children: "Re-fetch the spec on save" }),
|
|
81
|
+
/* @__PURE__ */ jsx("span", { className: "block break-all font-mono text-xs text-muted-foreground", children: sourceUrl })
|
|
82
|
+
] })
|
|
83
|
+
] }) }) : /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
84
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "update-spec-content", className: "text-xs text-muted-foreground", children: "Updated spec content" }),
|
|
85
|
+
/* @__PURE__ */ jsx(
|
|
86
|
+
Textarea,
|
|
87
|
+
{
|
|
88
|
+
id: "update-spec-content",
|
|
89
|
+
value: pasted,
|
|
90
|
+
onChange: (e) => setPasted(e.target.value),
|
|
91
|
+
placeholder: "Paste the updated OpenAPI JSON/YAML \u2014 applied when you save. This spec was added inline, so there is no URL to re-fetch.",
|
|
92
|
+
rows: 4,
|
|
93
|
+
maxRows: 10,
|
|
94
|
+
className: "font-mono text-xs"
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
] }),
|
|
98
|
+
error && /* @__PURE__ */ jsx(FormErrorAlert, { message: error })
|
|
99
|
+
] });
|
|
100
|
+
}
|
|
101
|
+
export {
|
|
102
|
+
UpdateSpecSection as default
|
|
103
|
+
};
|
|
104
|
+
//# sourceMappingURL=UpdateSpecSection-D32QZT3Q.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react/UpdateSpecSection.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useAtomSet, useAtomValue } from \"@effect/atom-react\";\nimport * as AsyncResult from \"effect/unstable/reactivity/AsyncResult\";\nimport * as Exit from \"effect/Exit\";\n\nimport { integrationWriteKeys } from \"@executor-js/react/api/reactivity-keys\";\nimport { messageFromExit } from \"@executor-js/react/api/error-reporting\";\nimport { FormErrorAlert } from \"@executor-js/react/lib/integration-add\";\nimport { IntegrationSlug } from \"@executor-js/sdk/shared\";\nimport type { EditSheetApplyResult, EditSheetSectionProps } from \"@executor-js/sdk/client\";\nimport { Checkbox } from \"@executor-js/react/components/checkbox\";\nimport { Label } from \"@executor-js/react/components/label\";\nimport { Textarea } from \"@executor-js/react/components/textarea\";\n\nimport { openApiConfigAtom, updateOpenApiSpec } from \"./atoms\";\n\n// ---------------------------------------------------------------------------\n// Update spec — the openapi plugin's section of the integration Edit sheet.\n// The user STAGES a spec update here (check re-fetch, or paste new content);\n// nothing runs until the sheet's Save. The staged apply re-resolves the spec\n// and rebuilds the tool catalog in place — connections, credentials, policies,\n// and the curated description all survive.\n// ---------------------------------------------------------------------------\n\ntype UpdateOutcome = {\n readonly toolCount: number;\n readonly addedTools: readonly string[];\n readonly removedTools: readonly string[];\n};\n\nconst outcomeSummary = (outcome: UpdateOutcome): string => {\n const added = outcome.addedTools.length;\n const removed = outcome.removedTools.length;\n if (added === 0 && removed === 0) {\n return `Spec updated — ${outcome.toolCount} tools, no changes to the tool list.`;\n }\n const parts = [\n ...(added > 0 ? [`+${added} tool${added !== 1 ? \"s\" : \"\"}`] : []),\n ...(removed > 0 ? [`−${removed} tool${removed !== 1 ? \"s\" : \"\"}`] : []),\n ];\n return `Spec updated — ${parts.join(\", \")} (${outcome.toolCount} total).`;\n};\n\nexport default function UpdateSpecSection(props: EditSheetSectionProps) {\n const slug = IntegrationSlug.make(props.sourceId);\n const configResult = useAtomValue(openApiConfigAtom(slug));\n const doUpdate = useAtomSet(updateOpenApiSpec, { mode: \"promiseExit\" });\n const [refetchStaged, setRefetchStaged] = useState(false);\n const [pasted, setPasted] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n\n const config =\n AsyncResult.isSuccess(configResult) && configResult.value ? configResult.value : null;\n\n const sourceUrl = config\n ? config.googleDiscoveryUrls?.length\n ? `${config.googleDiscoveryUrls.length} Google Discovery document${\n config.googleDiscoveryUrls.length !== 1 ? \"s\" : \"\"\n }`\n : config.sourceUrl\n : undefined;\n\n // The staged apply, rebuilt whenever the staged inputs change. Reported to\n // the sheet through a ref-stable callback so Save can run it.\n const applyStaged = useCallback(async (): Promise<EditSheetApplyResult> => {\n const spec = pasted.trim().length > 0 ? { kind: \"blob\" as const, value: pasted } : undefined;\n if (!spec && !refetchStaged) return { ok: true, summary: null };\n setError(null);\n const exit = await doUpdate({\n params: { slug },\n payload: spec ? { spec } : {},\n reactivityKeys: integrationWriteKeys,\n });\n if (Exit.isFailure(exit)) {\n setError(messageFromExit(exit, \"Failed to update spec\"));\n return { ok: false };\n }\n setRefetchStaged(false);\n setPasted(\"\");\n return { ok: true, summary: outcomeSummary(exit.value) };\n }, [doUpdate, pasted, refetchStaged, slug]);\n\n const onPendingChangeRef = useRef(props.onPendingChange);\n onPendingChangeRef.current = props.onPendingChange;\n const hasStagedChange = refetchStaged || pasted.trim().length > 0;\n useEffect(() => {\n onPendingChangeRef.current?.(hasStagedChange ? applyStaged : null);\n // Clear the staged apply if this section unmounts mid-edit.\n return () => onPendingChangeRef.current?.(null);\n }, [hasStagedChange, applyStaged]);\n\n if (!config) return null;\n\n return (\n <div className=\"space-y-3 border-t border-border/60 pt-5\">\n <div className=\"space-y-1\">\n <p className=\"text-sm font-medium text-foreground\">Update spec</p>\n <p className=\"text-xs text-muted-foreground\">\n Rebuild this integration's tools from the latest spec when you save. Connections,\n credentials, and policies are kept.\n </p>\n </div>\n\n {sourceUrl ? (\n <div className=\"space-y-2\">\n <Label className=\"flex items-start gap-2 text-sm font-normal\">\n <Checkbox\n checked={refetchStaged}\n onCheckedChange={(checked: boolean | \"indeterminate\") =>\n setRefetchStaged(checked === true)\n }\n />\n <span className=\"space-y-0.5\">\n <span className=\"block\">Re-fetch the spec on save</span>\n <span className=\"block break-all font-mono text-xs text-muted-foreground\">\n {sourceUrl}\n </span>\n </span>\n </Label>\n </div>\n ) : (\n <div className=\"space-y-2\">\n <Label htmlFor=\"update-spec-content\" className=\"text-xs text-muted-foreground\">\n Updated spec content\n </Label>\n <Textarea\n id=\"update-spec-content\"\n value={pasted}\n onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setPasted(e.target.value)}\n placeholder=\"Paste the updated OpenAPI JSON/YAML — applied when you save. This spec was added inline, so there is no URL to re-fetch.\"\n rows={4}\n maxRows={10}\n className=\"font-mono text-xs\"\n />\n </div>\n )}\n\n {error && <FormErrorAlert message={error} />}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
package/dist/api/group.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi";
|
|
2
2
|
import { Schema } from "effect";
|
|
3
|
-
import { InternalError, IntegrationAlreadyExistsError } from "@executor-js/sdk/shared";
|
|
3
|
+
import { InternalError, IntegrationAlreadyExistsError, IntegrationNotFoundError } from "@executor-js/sdk/shared";
|
|
4
4
|
import { OpenApiParseError, OpenApiExtractionError, OpenApiOAuthError } from "../sdk/errors";
|
|
5
5
|
export declare const OpenApiGroup: HttpApiGroup.HttpApiGroup<"openapi", HttpApiEndpoint.HttpApiEndpoint<"previewSpec", "POST", "/openapi/preview", HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
|
|
6
6
|
readonly spec: Schema.String;
|
|
7
7
|
}>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
|
|
8
8
|
readonly title: Schema.OptionFromOptional<Schema.String>;
|
|
9
|
+
readonly description: Schema.OptionFromOptional<Schema.String>;
|
|
9
10
|
readonly version: Schema.OptionFromOptional<Schema.String>;
|
|
10
11
|
readonly servers: Schema.$Array<Schema.Struct<{
|
|
11
12
|
readonly url: Schema.String;
|
|
@@ -82,6 +83,7 @@ export declare const OpenApiGroup: HttpApiGroup.HttpApiGroup<"openapi", HttpApiE
|
|
|
82
83
|
readonly urls: Schema.$Array<Schema.String>;
|
|
83
84
|
}>]>;
|
|
84
85
|
readonly slug: Schema.String;
|
|
86
|
+
readonly name: Schema.optional<Schema.String>;
|
|
85
87
|
readonly description: Schema.optional<Schema.String>;
|
|
86
88
|
readonly baseUrl: Schema.optional<Schema.String>;
|
|
87
89
|
readonly headers: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
|
|
@@ -184,6 +186,39 @@ export declare const OpenApiGroup: HttpApiGroup.HttpApiGroup<"openapi", HttpApiE
|
|
|
184
186
|
readonly literal: Schema.optional<Schema.String>;
|
|
185
187
|
}>>;
|
|
186
188
|
}>]>>;
|
|
187
|
-
}>>, HttpApiEndpoint.Json<typeof IntegrationAlreadyExistsError | typeof InternalError | typeof OpenApiParseError | typeof OpenApiExtractionError | typeof OpenApiOAuthError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"
|
|
189
|
+
}>>, HttpApiEndpoint.Json<typeof IntegrationAlreadyExistsError | typeof InternalError | typeof OpenApiParseError | typeof OpenApiExtractionError | typeof OpenApiOAuthError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"updateSpec", "POST", "/openapi/integrations/:slug/spec", HttpApiEndpoint.StringTree<Schema.Struct<{
|
|
190
|
+
slug: Schema.String;
|
|
191
|
+
}>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
|
|
192
|
+
readonly spec: Schema.optional<Schema.Union<readonly [Schema.Struct<{
|
|
193
|
+
readonly kind: Schema.Literal<"url">;
|
|
194
|
+
readonly url: Schema.String;
|
|
195
|
+
}>, Schema.Struct<{
|
|
196
|
+
readonly kind: Schema.Literal<"blob">;
|
|
197
|
+
readonly value: Schema.String;
|
|
198
|
+
}>, Schema.Struct<{
|
|
199
|
+
readonly kind: Schema.Literal<"googleDiscovery">;
|
|
200
|
+
readonly url: Schema.String;
|
|
201
|
+
}>, Schema.Struct<{
|
|
202
|
+
readonly kind: Schema.Literal<"googleDiscoveryBundle">;
|
|
203
|
+
readonly urls: Schema.$Array<Schema.String>;
|
|
204
|
+
}>]>>;
|
|
205
|
+
}>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
|
|
206
|
+
readonly slug: Schema.brand<Schema.String, "IntegrationSlug">;
|
|
207
|
+
readonly toolCount: Schema.Number;
|
|
208
|
+
/** Tool names new in this spec version (same diff for every connection). */
|
|
209
|
+
readonly addedTools: Schema.$Array<Schema.String>;
|
|
210
|
+
/** Tool names the new spec no longer defines. */
|
|
211
|
+
readonly removedTools: Schema.$Array<Schema.String>;
|
|
212
|
+
}>>, HttpApiEndpoint.Json<Schema.decodeTo<Schema.declareConstructor<IntegrationNotFoundError, {
|
|
213
|
+
readonly _tag: "IntegrationNotFoundError";
|
|
214
|
+
readonly slug: string;
|
|
215
|
+
}, readonly [Schema.TaggedStruct<"IntegrationNotFoundError", {
|
|
216
|
+
readonly slug: Schema.brand<Schema.String, "IntegrationSlug">;
|
|
217
|
+
}>], {
|
|
218
|
+
readonly _tag: "IntegrationNotFoundError";
|
|
219
|
+
readonly slug: string & import("effect/Brand").Brand<"IntegrationSlug">;
|
|
220
|
+
}>, Schema.TaggedStruct<"IntegrationNotFoundError", {
|
|
221
|
+
readonly slug: Schema.brand<Schema.String, "IntegrationSlug">;
|
|
222
|
+
}>, never, never> | typeof InternalError | typeof OpenApiParseError | typeof OpenApiExtractionError | typeof OpenApiOAuthError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"removeSpec", "DELETE", "/openapi/integrations/:slug", HttpApiEndpoint.StringTree<Schema.Struct<{
|
|
188
223
|
slug: Schema.String;
|
|
189
224
|
}>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Void>, HttpApiEndpoint.Json<typeof IntegrationAlreadyExistsError | typeof InternalError | typeof OpenApiParseError | typeof OpenApiExtractionError | typeof OpenApiOAuthError>, never, never>, false>;
|
package/dist/api/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export declare const openApiHttpPlugin: import("@executor-js/sdk/core").Configur
|
|
|
13
13
|
readonly tags: readonly string[];
|
|
14
14
|
readonly operationId: string;
|
|
15
15
|
}[];
|
|
16
|
+
readonly description: import("effect/Option").Option<string>;
|
|
16
17
|
readonly title: import("effect/Option").Option<string>;
|
|
17
18
|
readonly servers: readonly {
|
|
18
19
|
readonly description: import("effect/Option").Option<string>;
|
|
@@ -81,6 +82,12 @@ export declare const openApiHttpPlugin: import("@executor-js/sdk/core").Configur
|
|
|
81
82
|
slug: string & import("effect/Brand").Brand<"IntegrationSlug">;
|
|
82
83
|
toolCount: number;
|
|
83
84
|
}, import("@executor-js/sdk/core").StorageFailure | import("@executor-js/sdk/core").IntegrationAlreadyExistsError | import("../sdk").OpenApiParseError | import("../sdk").OpenApiExtractionError | import("../sdk").OpenApiOAuthError, never>;
|
|
85
|
+
updateSpec: (rawSlug: string, input?: import("../sdk/plugin").OpenApiUpdateSpecInput) => import("effect/Effect").Effect<{
|
|
86
|
+
slug: string & import("effect/Brand").Brand<"IntegrationSlug">;
|
|
87
|
+
toolCount: number;
|
|
88
|
+
addedTools: string[];
|
|
89
|
+
removedTools: string[];
|
|
90
|
+
}, import("@executor-js/sdk/core").StorageFailure | import("@executor-js/sdk/core").IntegrationNotFoundError | import("../sdk").OpenApiParseError | import("../sdk").OpenApiExtractionError | import("../sdk").OpenApiOAuthError, never>;
|
|
84
91
|
removeSpec: (slug: string) => import("effect/Effect").Effect<void, import("@executor-js/sdk/core").StorageFailure, never>;
|
|
85
92
|
getIntegration: (slug: string) => import("effect/Effect").Effect<import("@executor-js/sdk/core").Integration | null, import("@executor-js/sdk/core").StorageFailure, never>;
|
|
86
93
|
getConfig: (slug: string) => import("effect/Effect").Effect<import("../sdk").OpenApiIntegrationConfig | null, import("@executor-js/sdk/core").StorageFailure>;
|
|
@@ -89,6 +96,7 @@ export declare const openApiHttpPlugin: import("@executor-js/sdk/core").Configur
|
|
|
89
96
|
readonly spec: import("effect/Schema").String;
|
|
90
97
|
}>>, import("effect/unstable/httpapi/HttpApiEndpoint").StringTree<never>, import("effect/unstable/httpapi/HttpApiEndpoint").Json<import("effect/Schema").Struct<{
|
|
91
98
|
readonly title: import("effect/Schema").OptionFromOptional<import("effect/Schema").String>;
|
|
99
|
+
readonly description: import("effect/Schema").OptionFromOptional<import("effect/Schema").String>;
|
|
92
100
|
readonly version: import("effect/Schema").OptionFromOptional<import("effect/Schema").String>;
|
|
93
101
|
readonly servers: import("effect/Schema").$Array<import("effect/Schema").Struct<{
|
|
94
102
|
readonly url: import("effect/Schema").String;
|
|
@@ -165,6 +173,7 @@ export declare const openApiHttpPlugin: import("@executor-js/sdk/core").Configur
|
|
|
165
173
|
readonly urls: import("effect/Schema").$Array<import("effect/Schema").String>;
|
|
166
174
|
}>]>;
|
|
167
175
|
readonly slug: import("effect/Schema").String;
|
|
176
|
+
readonly name: import("effect/Schema").optional<import("effect/Schema").String>;
|
|
168
177
|
readonly description: import("effect/Schema").optional<import("effect/Schema").String>;
|
|
169
178
|
readonly baseUrl: import("effect/Schema").optional<import("effect/Schema").String>;
|
|
170
179
|
readonly headers: import("effect/Schema").optional<import("effect/Schema").$Record<import("effect/Schema").String, import("effect/Schema").String>>;
|
|
@@ -267,6 +276,37 @@ export declare const openApiHttpPlugin: import("@executor-js/sdk/core").Configur
|
|
|
267
276
|
readonly literal: import("effect/Schema").optional<import("effect/Schema").String>;
|
|
268
277
|
}>>;
|
|
269
278
|
}>]>>;
|
|
270
|
-
}>>, import("effect/unstable/httpapi/HttpApiEndpoint").Json<typeof import("@executor-js/sdk/core").IntegrationAlreadyExistsError | typeof import("@executor-js/api").InternalError | typeof import("../sdk").OpenApiParseError | typeof import("../sdk").OpenApiExtractionError | typeof import("../sdk").OpenApiOAuthError>, never, never> | import("effect/unstable/httpapi/HttpApiEndpoint").HttpApiEndpoint<"
|
|
279
|
+
}>>, import("effect/unstable/httpapi/HttpApiEndpoint").Json<typeof import("@executor-js/sdk/core").IntegrationAlreadyExistsError | typeof import("@executor-js/api").InternalError | typeof import("../sdk").OpenApiParseError | typeof import("../sdk").OpenApiExtractionError | typeof import("../sdk").OpenApiOAuthError>, never, never> | import("effect/unstable/httpapi/HttpApiEndpoint").HttpApiEndpoint<"updateSpec", "POST", "/openapi/integrations/:slug/spec", import("effect/unstable/httpapi/HttpApiEndpoint").StringTree<import("effect/Schema").Struct<{
|
|
280
|
+
slug: import("effect/Schema").String;
|
|
281
|
+
}>>, import("effect/unstable/httpapi/HttpApiEndpoint").StringTree<never>, import("effect/unstable/httpapi/HttpApiEndpoint").Json<import("effect/Schema").Struct<{
|
|
282
|
+
readonly spec: import("effect/Schema").optional<import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
|
|
283
|
+
readonly kind: import("effect/Schema").Literal<"url">;
|
|
284
|
+
readonly url: import("effect/Schema").String;
|
|
285
|
+
}>, import("effect/Schema").Struct<{
|
|
286
|
+
readonly kind: import("effect/Schema").Literal<"blob">;
|
|
287
|
+
readonly value: import("effect/Schema").String;
|
|
288
|
+
}>, import("effect/Schema").Struct<{
|
|
289
|
+
readonly kind: import("effect/Schema").Literal<"googleDiscovery">;
|
|
290
|
+
readonly url: import("effect/Schema").String;
|
|
291
|
+
}>, import("effect/Schema").Struct<{
|
|
292
|
+
readonly kind: import("effect/Schema").Literal<"googleDiscoveryBundle">;
|
|
293
|
+
readonly urls: import("effect/Schema").$Array<import("effect/Schema").String>;
|
|
294
|
+
}>]>>;
|
|
295
|
+
}>>, import("effect/unstable/httpapi/HttpApiEndpoint").StringTree<never>, import("effect/unstable/httpapi/HttpApiEndpoint").Json<import("effect/Schema").Struct<{
|
|
296
|
+
readonly slug: import("effect/Schema").brand<import("effect/Schema").String, "IntegrationSlug">;
|
|
297
|
+
readonly toolCount: import("effect/Schema").Number;
|
|
298
|
+
readonly addedTools: import("effect/Schema").$Array<import("effect/Schema").String>;
|
|
299
|
+
readonly removedTools: import("effect/Schema").$Array<import("effect/Schema").String>;
|
|
300
|
+
}>>, import("effect/unstable/httpapi/HttpApiEndpoint").Json<import("effect/Schema").decodeTo<import("effect/Schema").declareConstructor<import("@executor-js/sdk/core").IntegrationNotFoundError, {
|
|
301
|
+
readonly _tag: "IntegrationNotFoundError";
|
|
302
|
+
readonly slug: string;
|
|
303
|
+
}, readonly [import("effect/Schema").TaggedStruct<"IntegrationNotFoundError", {
|
|
304
|
+
readonly slug: import("effect/Schema").brand<import("effect/Schema").String, "IntegrationSlug">;
|
|
305
|
+
}>], {
|
|
306
|
+
readonly _tag: "IntegrationNotFoundError";
|
|
307
|
+
readonly slug: string & import("effect/Brand").Brand<"IntegrationSlug">;
|
|
308
|
+
}>, import("effect/Schema").TaggedStruct<"IntegrationNotFoundError", {
|
|
309
|
+
readonly slug: import("effect/Schema").brand<import("effect/Schema").String, "IntegrationSlug">;
|
|
310
|
+
}>, never, never> | typeof import("@executor-js/api").InternalError | typeof import("../sdk").OpenApiParseError | typeof import("../sdk").OpenApiExtractionError | typeof import("../sdk").OpenApiOAuthError>, never, never> | import("effect/unstable/httpapi/HttpApiEndpoint").HttpApiEndpoint<"removeSpec", "DELETE", "/openapi/integrations/:slug", import("effect/unstable/httpapi/HttpApiEndpoint").StringTree<import("effect/Schema").Struct<{
|
|
271
311
|
slug: import("effect/Schema").String;
|
|
272
312
|
}>>, import("effect/unstable/httpapi/HttpApiEndpoint").StringTree<never>, import("effect/unstable/httpapi/HttpApiEndpoint").Json<never>, import("effect/unstable/httpapi/HttpApiEndpoint").StringTree<never>, import("effect/unstable/httpapi/HttpApiEndpoint").Json<import("effect/Schema").Void>, import("effect/unstable/httpapi/HttpApiEndpoint").Json<typeof import("@executor-js/sdk/core").IntegrationAlreadyExistsError | typeof import("@executor-js/api").InternalError | typeof import("../sdk").OpenApiParseError | typeof import("../sdk").OpenApiExtractionError | typeof import("../sdk").OpenApiOAuthError>, never, never>, false>>;
|