History

Tighten plugin ownership boundaries

Rhys Sullivan committed 7 months ago

Commit f2a7b111 parent 4f7dbcc38 files · +237 −350Browse files

Unified · Split

38 files
  1. apps/web/src/components/shell.tsx +3 −3
  2. apps/web/src/components/source-favicon.tsx +6 −11
  3. apps/web/src/lib/source-favicon.ts +0 −123
  4. apps/web/src/main.tsx +2 −2
  5. apps/web/src/plugins/index.ts +4 −0
  6. apps/web/src/plugins/pages.tsx +0 −0
  7. apps/web/src/views/home.tsx +1 −1
  8. bun.lock +0 −15
  9. packages/auth/mcp-oauth/package.json +0 −23
  10. packages/auth/mcp-oauth/tsconfig.json +0 −19
  11. packages/clients/react/src/plugins/components/badge.tsx +0 −0
  12. packages/clients/react/src/plugins/components/code-block.tsx +0 −0
  13. packages/clients/react/src/plugins/components/document-panel.tsx +0 −0
  14. packages/clients/react/src/plugins/components/icons.tsx +0 −0
  15. packages/clients/react/src/plugins/components/loadable.tsx +0 −0
  16. packages/clients/react/src/plugins/components/markdown.tsx +0 −0
  17. packages/clients/react/src/plugins/components/source-tool-explorer.tsx +0 −0
  18. packages/clients/react/src/plugins/index.ts +104 −1
  19. packages/clients/react/src/plugins/lib/cn.ts +0 −0
  20. packages/clients/react/src/plugins/lib/shiki.ts +0 −0
  21. packages/clients/react/src/plugins/paths.ts +0 −0
  22. packages/clients/react/src/plugins/plugin-route-context.tsx +0 −0
  23. packages/clients/react/src/plugins/registry.ts +0 −0
  24. packages/clients/react/src/plugins/route-context.tsx +0 −0
  25. packages/clients/react/src/plugins/types.ts +1 −0
  26. packages/clients/react/src/source-plugins/index.ts +1 −104
  27. plugins/google-discovery/http/index.ts +23 −12
  28. plugins/google-discovery/react/index.tsx +55 −12
  29. plugins/google-discovery/sdk/catalog.ts +3 −2
  30. plugins/google-discovery/sdk/index.ts +15 −13
  31. plugins/google-discovery/sdk/tools.ts +3 −2
  32. plugins/google-discovery/sdk/types.ts +2 −1
  33. plugins/google-discovery/shared/index.ts +8 −0
  34. plugins/mcp/sdk/discovery.ts +1 −1
  35. plugins/mcp/sdk/index.ts +1 −1
  36. plugins/mcp/sdk/oauth-effect-errors.ts +0 −0
  37. plugins/mcp/sdk/oauth.ts +4 −3
  38. plugins/mcp/sdk/package.json +0 −1
apps/web/src/components/shell.tsx+3−3
@@ -6,7 +6,7 @@ import { cn } from "../lib/utils"; · 5 unmodified lines
6import { IconPlus, IconCopy, IconCheck } from "./icons";6import { IconPlus, IconCopy, IconCheck } from "./icons";
7import { LoadableBlock } from "./loadable";7import { LoadableBlock } from "./loadable";
8import { SourceFavicon } from "./source-favicon";8import { SourceFavicon } from "./source-favicon";
9import { getSourceFrontendPaths } from "../source-plugins";9import { getSourceFrontendPaths } from "../plugins";
10 10
11// ── Status dot color ─────────────────────────────────────────────────────11// ── Status dot color ─────────────────────────────────────────────────────
12 12
@@ -324,7 +324,7 @@ function SourceItem(props: { · 311 unmodified lines
324 return (324 return (
325 <div className="group flex items-center gap-2 rounded-md px-2.5 py-1.5 text-[13px] text-sidebar-foreground/60">325 <div className="group flex items-center gap-2 rounded-md px-2.5 py-1.5 text-[13px] text-sidebar-foreground/60">
326 <div className="flex size-3 shrink-0 items-center justify-center text-muted-foreground/50">326 <div className="flex size-3 shrink-0 items-center justify-center text-muted-foreground/50">
327 <SourceFavicon kind={props.source.kind} className="size-3" />327 <SourceFavicon source={props.source} className="size-3" />
328 </div>328 </div>
329 <span className="flex-1 truncate">{props.source.name}</span>329 <span className="flex-1 truncate">{props.source.name}</span>
330 <span330 <span
@@ -352,7 +352,7 @@ function SourceItem(props: { · 21 unmodified lines
352 )}352 )}
353 >353 >
354 <div className="flex size-3 shrink-0 items-center justify-center text-muted-foreground/50">354 <div className="flex size-3 shrink-0 items-center justify-center text-muted-foreground/50">
355 <SourceFavicon kind={props.source.kind} className="size-3" />355 <SourceFavicon source={props.source} className="size-3" />
356 </div>356 </div>
357 <span className="flex-1 truncate">{props.source.name}</span>357 <span className="flex-1 truncate">{props.source.name}</span>
358 <span358 <span
apps/web/src/components/source-favicon.tsx+6−11
@@ -1,30 +1,25 @@
1import { useMemo, useState } from "react";1import { useState } from "react";
2import type { Source } from "@executor/react";2import type { Source } from "@executor/react";
3import { getGoogleProductIconUrl, getSourceFaviconUrl } from "../lib/source-favicon";3import { getSourceFrontendIconUrl } from "../plugins";
4import { cn } from "../lib/utils";4import { cn } from "../lib/utils";
5 5
6type SourceKind = Source["kind"] | string;6type SourceKind = Source["kind"] | string;
7 7
8export function SourceFavicon({8export function SourceFavicon({
9 endpoint,9 source,
10 kind: _kind,
11 className,10 className,
12 size = 16,11 size = 16,
13}: {12}: {
14 endpoint?: string | null;13 source: Source;
15 kind: SourceKind;
16 className?: string;14 className?: string;
17 size?: number;15 size?: number;
18}) {16}) {
19 const faviconUrl = useMemo(() => {17 const faviconUrl = getSourceFrontendIconUrl(source);
20 return getGoogleProductIconUrl(endpoint) ?? getSourceFaviconUrl(endpoint);
21 }, [endpoint]);
22
23 const [failedUrl, setFailedUrl] = useState<string | null>(null);18 const [failedUrl, setFailedUrl] = useState<string | null>(null);
24 const isFailed = Boolean(faviconUrl && failedUrl === faviconUrl);19 const isFailed = Boolean(faviconUrl && failedUrl === faviconUrl);
25 20
26 if (!faviconUrl || isFailed) {21 if (!faviconUrl || isFailed) {
27 return <DefaultSourceIcon kind={_kind} className={className} />;22 return <DefaultSourceIcon kind={source.kind} className={className} />;
28 }23 }
29 24
30 return (25 return (
apps/web/src/lib/source-favicon.ts+0−123
@@ -1,123 +0,0 @@
1import { parse as parseDomain } from "tldts";
2
3const RAW_HOSTS = new Set([
4 "raw.githubusercontent.com",
5 "cdn.jsdelivr.net",
6 "unpkg.com",
7 "raw.github.com",
8]);
9
10// ---------------------------------------------------------------------------
11// Google product icon URLs (official CDN assets)
12// ---------------------------------------------------------------------------
13
14const GOOGLE_SERVICE_ICONS: Record<string, string> = {
15 calendar: "https://ssl.gstatic.com/calendar/images/dynamiclogo_2020q4/calendar_31_2x.png",
16 drive: "https://ssl.gstatic.com/images/branding/product/2x/drive_2020q4_48dp.png",
17 gmail: "https://ssl.gstatic.com/ui/v1/icons/mail/rfr/gmail.ico",
18 docs: "https://ssl.gstatic.com/docs/documents/images/kix-favicon7.ico",
19 sheets: "https://ssl.gstatic.com/docs/spreadsheets/favicon3.ico",
20 slides: "https://ssl.gstatic.com/docs/presentations/images/favicon5.ico",
21 forms: "https://ssl.gstatic.com/docs/forms/device_home/android_192.png",
22 searchconsole: "https://ssl.gstatic.com/search-console/scfe/search_console-64.png",
23 people: "https://ssl.gstatic.com/images/branding/product/2x/contacts_2022_48dp.png",
24 tasks: "https://ssl.gstatic.com/tasks/images/favicon.ico",
25 chat: "https://ssl.gstatic.com/chat/favicon/favicon_v2.ico",
26 keep: "https://ssl.gstatic.com/keep/icon_2020q4v2_128.png",
27 classroom: "https://ssl.gstatic.com/classroom/favicon.png",
28 admin: "https://ssl.gstatic.com/images/branding/product/2x/admin_2020q4_48dp.png",
29 script: "https://ssl.gstatic.com/script/images/favicon.ico",
30 bigquery: "https://ssl.gstatic.com/bqui1/favicon.ico",
31 cloudresourcemanager: "https://www.gstatic.com/devrel-devsite/prod/v0e0f589edd85502a40d78d7d0825db8ea5ef3b99b1571571945f0f3f764ff61b/cloud/images/favicons/onecloud/favicon.ico",
32 youtube: "https://www.youtube.com/s/desktop/a94e1818/img/favicon_32x32.png",
33};
34
35/**
36 * For google_discovery endpoints, extract the service name and return
37 * the official product icon URL if we have one.
38 */
39const getGoogleServiceIconUrl = (endpoint: string | null | undefined): string | null => {
40 if (!endpoint) return null;
41 try {
42 const url = new URL(endpoint);
43 const host = url.hostname;
44
45 // Direct service subdomain: "calendar-json.googleapis.com", "gmail.googleapis.com"
46 if (host.endsWith(".googleapis.com")) {
47 const sub = host.replace(/\.googleapis\.com$/, "").replace(/-json$/, "");
48 if (GOOGLE_SERVICE_ICONS[sub]) return GOOGLE_SERVICE_ICONS[sub];
49 }
50
51 // Discovery URL pattern: /discovery/v1/apis/{service}/{version}/rest
52 const discoveryMatch = url.pathname.match(/\/apis\/([^/]+)\//);
53 if (discoveryMatch?.[1] && GOOGLE_SERVICE_ICONS[discoveryMatch[1]]) {
54 return GOOGLE_SERVICE_ICONS[discoveryMatch[1]];
55 }
56
57 // $discovery/rest pattern on service subdomains is already handled above
58 } catch {
59 // fall through
60 }
61 return null;
62};
63
64// ---------------------------------------------------------------------------
65// General favicon resolution
66// ---------------------------------------------------------------------------
67
68const parseUrl = (value: string | null | undefined): URL | null => {
69 if (!value) {
70 return null;
71 }
72
73 try {
74 return new URL(value);
75 } catch {
76 return null;
77 }
78};
79
80const normalizeSeedUrl = (value: string | null | undefined): string | null => {
81 const parsed = parseUrl(value);
82 if (!parsed) {
83 return null;
84 }
85
86 if (!RAW_HOSTS.has(parsed.hostname)) {
87 return parsed.origin;
88 }
89
90 const segments = parsed.pathname
91 .split("/")
92 .map((segment) => segment.trim())
93 .filter((segment) => segment.length > 0);
94
95 for (const segment of segments) {
96 const withoutExtension = segment.replace(/\.(ya?ml|json)$/i, "");
97 if (/^[a-z0-9-]+(\.[a-z0-9-]+)+$/i.test(withoutExtension)) {
98 return `https://${withoutExtension}`;
99 }
100 }
101
102 return parsed.origin;
103};
104
105export const getGoogleProductIconUrl = getGoogleServiceIconUrl;
106
107export const getSourceFaviconUrl = (
108 value: string | null | undefined,
109): string | null => {
110 const seedUrl = normalizeSeedUrl(value);
111 if (!seedUrl) {
112 return null;
113 }
114
115 try {
116 const hostname = new URL(seedUrl).hostname;
117 const parsed = parseDomain(hostname);
118 const domain = parsed.domain ?? hostname;
119 return `https://www.google.com/s2/favicons?domain=${encodeURIComponent(domain)}&sz=32`;
120 } catch {
121 return null;
122 }
123};
apps/web/src/main.tsx+2−2
@@ -24,7 +24,7 @@ import { AppShell } from "./components/shell"; · 23 unmodified lines
24import {24import {
25 registeredFrontendPluginRoutes,25 registeredFrontendPluginRoutes,
26 registeredSourceFrontendTypes,26 registeredSourceFrontendTypes,
27} from "./source-plugins";27} from "./plugins";
28import {28import {
29 ExecutorPluginRoutePage,29 ExecutorPluginRoutePage,
30 SourcePluginAddPage,30 SourcePluginAddPage,
@@ -32,7 +32,7 @@ import { · 1 unmodified line
32 SourcePluginDetailPage,32 SourcePluginDetailPage,
33 SourcePluginEditPage,33 SourcePluginEditPage,
34 SourcePluginsIndexPage,34 SourcePluginsIndexPage,
35} from "./source-plugins/pages";35} from "./plugins/pages";
36import { HomePage } from "./views/home";36import { HomePage } from "./views/home";
37import { SecretsPage } from "./views/secrets";37import { SecretsPage } from "./views/secrets";
38 38
apps/web/src/plugins/index.tsrenamed from apps/web/src/source-plugins/index.ts+4−0
@@ -1,3 +1,4 @@
1import type { Source } from "@executor/react";
1import {2import {
2 GoogleDiscoveryReactPlugin,3 GoogleDiscoveryReactPlugin,
3} from "@executor/plugin-google-discovery-react";4} from "@executor/plugin-google-discovery-react";
@@ -59,3 +60,6 @@ export const getSourceFrontendPaths = (kind: string) => { · 55 unmodified lines
59 const definition = getSourceFrontendType(kind);60 const definition = getSourceFrontendType(kind);
60 return definition ? createSourcePluginPaths(definition.key) : null;61 return definition ? createSourcePluginPaths(definition.key) : null;
61};62};
63
64export const getSourceFrontendIconUrl = (source: Source) =>
65 getSourceFrontendTypeEntry(source.kind)?.definition.getIconUrl?.(source) ?? null;
apps/web/src/plugins/pages.tsxrenamed from apps/web/src/source-plugins/pages.tsx+0−0

File renamed without changes.

apps/web/src/views/home.tsx+1−1
@@ -8,7 +8,7 @@ import { SourcePluginsResetState } from "../components/source-plugins-reset-stat · 7 unmodified lines
8import {8import {
9 getSourceFrontendPaths,9 getSourceFrontendPaths,
10 registeredSourceFrontendTypes,10 registeredSourceFrontendTypes,
11} from "../source-plugins";11} from "../plugins";
12 12
13export function HomePage() {13export function HomePage() {
14 const sources = useSources();14 const sources = useSources();
bun.lock+0−15
@@ -123,18 +123,6 @@ · 122 unmodified lines
123 "bun-types": "catalog:",123 "bun-types": "catalog:",
124 },124 },
125 },125 },
126 "packages/auth/mcp-oauth": {
127 "name": "@executor/auth-mcp-oauth",
128 "version": "0.0.0",
129 "dependencies": {
130 "@modelcontextprotocol/sdk": "^1.26.0",
131 "effect": "catalog:",
132 },
133 "devDependencies": {
134 "@types/node": "catalog:",
135 "bun-types": "catalog:",
136 },
137 },
138 "packages/clients/react": {126 "packages/clients/react": {
139 "name": "@executor/react",127 "name": "@executor/react",
140 "version": "0.0.0",128 "version": "0.0.0",
@@ -587,7 +575,6 @@ · 446 unmodified lines
587 "name": "@executor/plugin-mcp-sdk",575 "name": "@executor/plugin-mcp-sdk",
588 "version": "0.0.0",576 "version": "0.0.0",
589 "dependencies": {577 "dependencies": {
590 "@executor/auth-mcp-oauth": "workspace:*",
591 "@executor/codemode-core": "workspace:*",578 "@executor/codemode-core": "workspace:*",
592 "@executor/ir": "workspace:*",579 "@executor/ir": "workspace:*",
593 "@executor/platform-sdk": "workspace:*",580 "@executor/platform-sdk": "workspace:*",
@@ -883,8 +870,6 @@ · 289 unmodified lines
883 870
884 "@executor/ai-sdk-adapter": ["@executor/ai-sdk-adapter@workspace:packages/hosts/ai-sdk"],871 "@executor/ai-sdk-adapter": ["@executor/ai-sdk-adapter@workspace:packages/hosts/ai-sdk"],
885 872
886 "@executor/auth-mcp-oauth": ["@executor/auth-mcp-oauth@workspace:packages/auth/mcp-oauth"],
887
888 "@executor/codemode-core": ["@executor/codemode-core@workspace:packages/kernel/core"],873 "@executor/codemode-core": ["@executor/codemode-core@workspace:packages/kernel/core"],
889 874
890 "@executor/docs": ["@executor/docs@workspace:apps/docs"],875 "@executor/docs": ["@executor/docs@workspace:apps/docs"],
packages/auth/mcp-oauth/package.json+0−23
@@ -1,23 +0,0 @@
1{
2 "name": "@executor/auth-mcp-oauth",
3 "private": true,
4 "type": "module",
5 "exports": {
6 ".": "./src/index.ts"
7 },
8 "files": [
9 "src"
10 ],
11 "scripts": {
12 "typecheck": "bunx tsc --noEmit -p tsconfig.json"
13 },
14 "dependencies": {
15 "@modelcontextprotocol/sdk": "^1.26.0",
16 "effect": "catalog:"
17 },
18 "devDependencies": {
19 "@types/node": "catalog:",
20 "bun-types": "catalog:"
21 },
22 "version": "0.0.0"
23}
packages/auth/mcp-oauth/tsconfig.json+0−19
@@ -1,19 +0,0 @@
1{
2 "compilerOptions": {
3 "target": "ES2022",
4 "module": "ESNext",
5 "moduleResolution": "Bundler",
6 "strict": true,
7 "skipLibCheck": true,
8 "noEmit": true,
9 "lib": ["ES2022"],
10 "types": ["bun-types", "node"]
11 },
12 "include": [
13 "src/**/*.ts",
14 "src/**/*.d.ts"
15 ],
16 "exclude": [
17 "src/**/*.test.ts"
18 ]
19}
packages/clients/react/src/plugins/components/badge.tsxrenamed from packages/clients/react/src/source-plugins/components/badge.tsx+0−0

File renamed without changes.

packages/clients/react/src/plugins/components/code-block.tsxrenamed from packages/clients/react/src/source-plugins/components/code-block.tsx+0−0

File renamed without changes.

packages/clients/react/src/plugins/components/document-panel.tsxrenamed from packages/clients/react/src/source-plugins/components/document-panel.tsx+0−0

File renamed without changes.

packages/clients/react/src/plugins/components/icons.tsxrenamed from packages/clients/react/src/source-plugins/components/icons.tsx+0−0

File renamed without changes.

packages/clients/react/src/plugins/components/loadable.tsxrenamed from packages/clients/react/src/source-plugins/components/loadable.tsx+0−0

File renamed without changes.

packages/clients/react/src/plugins/components/markdown.tsxrenamed from packages/clients/react/src/source-plugins/components/markdown.tsx+0−0

File renamed without changes.

packages/clients/react/src/plugins/components/source-tool-explorer.tsxrenamed from packages/clients/react/src/source-plugins/components/source-tool-explorer.tsx+0−0

File renamed without changes.

packages/clients/react/src/plugins/index.ts+104−1
@@ -1 +1,104 @@
1export * from "../source-plugins/index";1export type {
2 ExecutorFrontendPlugin,
3 ExecutorPluginNavigation,
4 ExecutorPluginRouteContextValue,
5 FrontendPluginRouteDefinition,
6 FrontendPluginRouteParams,
7 FrontendPluginRouteSearch,
8 FrontendSourceDetailRouteDefinition,
9 FrontendSourceTypeDefinition,
10 SourcePluginNavigation,
11 SourcePluginRouteContextValue,
12 SourcePluginRouteParams,
13 SourcePluginRouteSearch,
14} from "./types";
15
16export {
17 createExecutorPluginPaths,
18 createSourcePluginPaths,
19 executorPluginBasePath,
20 executorPluginRoutePath,
21 executorPluginRoutePattern,
22 normalizeExecutorPluginPath,
23 normalizeSourcePluginPath,
24 sourcePluginAddPath,
25 sourcePluginChildPath,
26 sourcePluginChildPattern,
27 sourcePluginDetailPath,
28 sourcePluginDetailPattern,
29 sourcePluginEditPath,
30 sourcePluginEditPattern,
31 sourcePluginsIndexPath,
32 type ExecutorPluginPaths,
33 type SourcePluginPaths,
34} from "./paths";
35
36export {
37 defineExecutorFrontendPlugin,
38 defineFrontendPluginRoute,
39 defineFrontendSourceType,
40 registerExecutorFrontendPlugins,
41 type RegisteredFrontendPluginRoute,
42 type RegisteredFrontendSourceType,
43} from "./registry";
44
45export {
46 ExecutorPluginRouteProvider,
47 useExecutorPlugin,
48 useExecutorPluginNavigation,
49 useExecutorPluginPaths,
50 useExecutorPluginRoute,
51 useExecutorPluginRouteDefinition,
52 useExecutorPluginRouteParams,
53 useExecutorPluginSearch,
54} from "./plugin-route-context";
55
56export {
57 SourcePluginRouteProvider,
58 useSourcePlugin,
59 useSourcePluginDefinition,
60 useSourcePluginNavigation,
61 useSourcePluginPaths,
62 useSourcePluginRoute,
63 useSourcePluginRouteParams,
64 useSourcePluginSearch,
65} from "./route-context";
66
67export {
68 cn,
69} from "./lib/cn";
70
71export {
72 Badge,
73 MethodBadge,
74} from "./components/badge";
75export {
76 CodeBlock,
77} from "./components/code-block";
78export {
79 DocumentPanel,
80} from "./components/document-panel";
81export {
82 IconCheck,
83 IconChevron,
84 IconClose,
85 IconCopy,
86 IconEmpty,
87 IconFolder,
88 IconPencil,
89 IconSearch,
90 IconSpinner,
91 IconTool,
92} from "./components/icons";
93export {
94 EmptyState,
95 LoadableBlock,
96} from "./components/loadable";
97export {
98 Markdown,
99} from "./components/markdown";
100export {
101 SourceToolExplorer,
102 type SourceToolExplorerSearch,
103 parseSourceToolExplorerSearch,
104} from "./components/source-tool-explorer";
packages/clients/react/src/plugins/lib/cn.tsrenamed from packages/clients/react/src/source-plugins/lib/cn.ts+0−0

File renamed without changes.

packages/clients/react/src/plugins/lib/shiki.tsrenamed from packages/clients/react/src/source-plugins/lib/shiki.ts+0−0

File renamed without changes.

packages/clients/react/src/plugins/paths.tsrenamed from packages/clients/react/src/source-plugins/paths.ts+0−0

File renamed without changes.

packages/clients/react/src/plugins/plugin-route-context.tsxrenamed from packages/clients/react/src/source-plugins/plugin-route-context.tsx+0−0

File renamed without changes.

packages/clients/react/src/plugins/registry.tsrenamed from packages/clients/react/src/source-plugins/registry.ts+0−0

File renamed without changes.

packages/clients/react/src/plugins/route-context.tsxrenamed from packages/clients/react/src/source-plugins/route-context.tsx+0−0

File renamed without changes.

packages/clients/react/src/plugins/types.tsrenamed from packages/clients/react/src/source-plugins/types.ts+1−0
@@ -59,6 +59,7 @@ export type FrontendSourceTypeDefinition = { · 58 unmodified lines
59 kind: string;59 kind: string;
60 displayName: string;60 displayName: string;
61 description?: string;61 description?: string;
62 getIconUrl?: (source: Source) => string | null;
62 renderAddPage: ComponentType;63 renderAddPage: ComponentType;
63 renderEditPage?: ComponentType<{ source: Source }>;64 renderEditPage?: ComponentType<{ source: Source }>;
64 renderDetailPage?: ComponentType<{ source: Source }>;65 renderDetailPage?: ComponentType<{ source: Source }>;
packages/clients/react/src/source-plugins/index.ts+1−104
@@ -1,104 +1 @@
1export type {1export * from "../plugins/index";
2 ExecutorFrontendPlugin,
3 ExecutorPluginNavigation,
4 ExecutorPluginRouteContextValue,
5 FrontendPluginRouteDefinition,
6 FrontendPluginRouteParams,
7 FrontendPluginRouteSearch,
8 FrontendSourceDetailRouteDefinition,
9 FrontendSourceTypeDefinition,
10 SourcePluginNavigation,
11 SourcePluginRouteContextValue,
12 SourcePluginRouteParams,
13 SourcePluginRouteSearch,
14} from "./types";
15
16export {
17 createExecutorPluginPaths,
18 createSourcePluginPaths,
19 executorPluginBasePath,
20 executorPluginRoutePath,
21 executorPluginRoutePattern,
22 normalizeExecutorPluginPath,
23 normalizeSourcePluginPath,
24 sourcePluginAddPath,
25 sourcePluginChildPath,
26 sourcePluginChildPattern,
27 sourcePluginDetailPath,
28 sourcePluginDetailPattern,
29 sourcePluginEditPath,
30 sourcePluginEditPattern,
31 sourcePluginsIndexPath,
32 type ExecutorPluginPaths,
33 type SourcePluginPaths,
34} from "./paths";
35
36export {
37 defineExecutorFrontendPlugin,
38 defineFrontendPluginRoute,
39 defineFrontendSourceType,
40 registerExecutorFrontendPlugins,
41 type RegisteredFrontendPluginRoute,
42 type RegisteredFrontendSourceType,
43} from "./registry";
44
45export {
46 ExecutorPluginRouteProvider,
47 useExecutorPlugin,
48 useExecutorPluginNavigation,
49 useExecutorPluginPaths,
50 useExecutorPluginRoute,
51 useExecutorPluginRouteDefinition,
52 useExecutorPluginRouteParams,
53 useExecutorPluginSearch,
54} from "./plugin-route-context";
55
56export {
57 SourcePluginRouteProvider,
58 useSourcePlugin,
59 useSourcePluginDefinition,
60 useSourcePluginNavigation,
61 useSourcePluginPaths,
62 useSourcePluginRoute,
63 useSourcePluginRouteParams,
64 useSourcePluginSearch,
65} from "./route-context";
66
67export {
68 cn,
69} from "./lib/cn";
70
71export {
72 Badge,
73 MethodBadge,
74} from "./components/badge";
75export {
76 CodeBlock,
77} from "./components/code-block";
78export {
79 DocumentPanel,
80} from "./components/document-panel";
81export {
82 IconCheck,
83 IconChevron,
84 IconClose,
85 IconCopy,
86 IconEmpty,
87 IconFolder,
88 IconPencil,
89 IconSearch,
90 IconSpinner,
91 IconTool,
92} from "./components/icons";
93export {
94 EmptyState,
95 LoadableBlock,
96} from "./components/loadable";
97export {
98 Markdown,
99} from "./components/markdown";
100export {
101 SourceToolExplorer,
102 type SourceToolExplorerSearch,
103 parseSourceToolExplorerSearch,
104} from "./components/source-tool-explorer";
plugins/google-discovery/http/index.ts+23−12
@@ -25,6 +25,9 @@ import * as Effect from "effect/Effect"; · 24 unmodified lines
25import * as Schema from "effect/Schema";25import * as Schema from "effect/Schema";
26 26
27import {27import {
28 GOOGLE_DISCOVERY_EXECUTOR_KEY,
29 GOOGLE_DISCOVERY_OAUTH_STORAGE_PREFIX,
30 GOOGLE_DISCOVERY_PLUGIN_KEY,
28 GoogleDiscoveryConnectInputSchema,31 GoogleDiscoveryConnectInputSchema,
29 GoogleDiscoveryOAuthPopupResultSchema,32 GoogleDiscoveryOAuthPopupResultSchema,
30 GoogleDiscoverySourceConfigPayloadSchema,33 GoogleDiscoverySourceConfigPayloadSchema,
@@ -39,7 +42,7 @@ import { · 8 unmodified lines
39} from "@executor/plugin-google-discovery-shared";42} from "@executor/plugin-google-discovery-shared";
40 43
41type GoogleDiscoveryExecutorExtension = {44type GoogleDiscoveryExecutorExtension = {
42 googleDiscovery: {45 [GOOGLE_DISCOVERY_EXECUTOR_KEY]: {
43 createSource: (46 createSource: (
44 input: GoogleDiscoveryConnectInput,47 input: GoogleDiscoveryConnectInput,
45 ) => Effect.Effect<Source, Error>;48 ) => Effect.Effect<Source, Error>;
@@ -77,7 +80,7 @@ const callbackParamsSchema = Schema.Struct({ · 31 unmodified lines
77 error_description: Schema.optional(Schema.String),80 error_description: Schema.optional(Schema.String),
78});81});
79 82
80export const GoogleDiscoveryHttpGroup = HttpApiGroup.make("googleDiscovery")83export const GoogleDiscoveryHttpGroup = HttpApiGroup.make(GOOGLE_DISCOVERY_PLUGIN_KEY)
81 .add(84 .add(
82 HttpApiEndpoint.post("createSource")`/workspaces/${workspaceIdParam}/plugins/google-discovery/sources`85 HttpApiEndpoint.post("createSource")`/workspaces/${workspaceIdParam}/plugins/google-discovery/sources`
83 .setPayload(GoogleDiscoveryConnectInputSchema)86 .setPayload(GoogleDiscoveryConnectInputSchema)
@@ -129,7 +132,7 @@ export const GoogleDiscoveryHttpGroup = HttpApiGroup.make("googleDiscovery") · 45 unmodified lines
129 .prefix("/v1");132 .prefix("/v1");
130 133
131export const googleDiscoveryHttpApiExtension = {134export const googleDiscoveryHttpApiExtension = {
132 key: "googleDiscovery",135 key: GOOGLE_DISCOVERY_PLUGIN_KEY,
133 group: GoogleDiscoveryHttpGroup,136 group: GoogleDiscoveryHttpGroup,
134} satisfies ExecutorHttpApiExtension<typeof GoogleDiscoveryHttpGroup>;137} satisfies ExecutorHttpApiExtension<typeof GoogleDiscoveryHttpGroup>;
135 138
@@ -199,7 +202,7 @@ const popupDocument = (payload: GoogleDiscoveryOAuthPopupResult): string => { · 63 unmodified lines
199 (() => {202 (() => {
200 const payload = ${serialized};203 const payload = ${serialized};
201 try {204 try {
202 window.localStorage.setItem("executor:google-discovery-oauth:" + (payload.ok ? payload.sessionId : "failed"), JSON.stringify(payload));205 window.localStorage.setItem("${GOOGLE_DISCOVERY_OAUTH_STORAGE_PREFIX}" + (payload.ok ? payload.sessionId : "failed"), JSON.stringify(payload));
203 } catch {}206 } catch {}
204 try {207 try {
205 if (window.opener) {208 if (window.opener) {
@@ -218,17 +221,19 @@ export const googleDiscoveryHttpPlugin = (): ExecutorHttpPlugin< · 12 unmodified lines
218 typeof GoogleDiscoveryHttpGroup,221 typeof GoogleDiscoveryHttpGroup,
219 GoogleDiscoveryExecutorExtension222 GoogleDiscoveryExecutorExtension
220> => ({223> => ({
221 key: "googleDiscovery",224 key: GOOGLE_DISCOVERY_PLUGIN_KEY,
222 group: GoogleDiscoveryHttpGroup,225 group: GoogleDiscoveryHttpGroup,
223 build: ({ executor }) =>226 build: ({ executor }) =>
224 HttpApiBuilder.group(GoogleDiscoveryHttpApi, "googleDiscovery", (handlers) =>227 HttpApiBuilder.group(GoogleDiscoveryHttpApi, GOOGLE_DISCOVERY_PLUGIN_KEY, (handlers) =>
225 handlers228 handlers
226 .handle("createSource", ({ path, payload }) =>229 .handle("createSource", ({ path, payload }) =>
227 resolveRequestedLocalWorkspace(230 resolveRequestedLocalWorkspace(
228 "googleDiscovery.createSource",231 "googleDiscovery.createSource",
229 path.workspaceId,232 path.workspaceId,
230 ).pipe(233 ).pipe(
231 Effect.flatMap(() => executor.googleDiscovery.createSource(payload)),234 Effect.flatMap(() =>
235 executor[GOOGLE_DISCOVERY_EXECUTOR_KEY].createSource(payload)
236 ),
232 Effect.mapError((cause) =>237 Effect.mapError((cause) =>
233 toStorageError("googleDiscovery.createSource", cause)238 toStorageError("googleDiscovery.createSource", cause)
234 ),239 ),
@@ -240,7 +245,9 @@ export const googleDiscoveryHttpPlugin = (): ExecutorHttpPlugin< · 5 unmodified lines
240 path.workspaceId,245 path.workspaceId,
241 ).pipe(246 ).pipe(
242 Effect.flatMap(() =>247 Effect.flatMap(() =>
243 executor.googleDiscovery.getSourceConfig(path.sourceId)248 executor[GOOGLE_DISCOVERY_EXECUTOR_KEY].getSourceConfig(
249 path.sourceId,
250 )
244 ),251 ),
245 Effect.mapError((cause) =>252 Effect.mapError((cause) =>
246 mapPluginStorageError("googleDiscovery.getSourceConfig", cause)253 mapPluginStorageError("googleDiscovery.getSourceConfig", cause)
@@ -253,7 +260,7 @@ export const googleDiscoveryHttpPlugin = (): ExecutorHttpPlugin< · 6 unmodified lines
253 path.workspaceId,260 path.workspaceId,
254 ).pipe(261 ).pipe(
255 Effect.flatMap(() =>262 Effect.flatMap(() =>
256 executor.googleDiscovery.updateSource({263 executor[GOOGLE_DISCOVERY_EXECUTOR_KEY].updateSource({
257 sourceId: path.sourceId,264 sourceId: path.sourceId,
258 config: payload,265 config: payload,
259 })266 })
@@ -269,7 +276,9 @@ export const googleDiscoveryHttpPlugin = (): ExecutorHttpPlugin< · 9 unmodified lines
269 path.workspaceId,276 path.workspaceId,
270 ).pipe(277 ).pipe(
271 Effect.flatMap(() =>278 Effect.flatMap(() =>
272 executor.googleDiscovery.removeSource(path.sourceId)279 executor[GOOGLE_DISCOVERY_EXECUTOR_KEY].removeSource(
280 path.sourceId,
281 )
273 ),282 ),
274 Effect.map((removed) => ({ removed })),283 Effect.map((removed) => ({ removed })),
275 Effect.mapError((cause) =>284 Effect.mapError((cause) =>
@@ -282,14 +291,16 @@ export const googleDiscoveryHttpPlugin = (): ExecutorHttpPlugin< · 6 unmodified lines
282 "googleDiscovery.startOAuth",291 "googleDiscovery.startOAuth",
283 path.workspaceId,292 path.workspaceId,
284 ).pipe(293 ).pipe(
285 Effect.flatMap(() => executor.googleDiscovery.startOAuth(payload)),294 Effect.flatMap(() =>
295 executor[GOOGLE_DISCOVERY_EXECUTOR_KEY].startOAuth(payload)
296 ),
286 Effect.mapError((cause) =>297 Effect.mapError((cause) =>
287 toStorageError("googleDiscovery.startOAuth", cause)298 toStorageError("googleDiscovery.startOAuth", cause)
288 ),299 ),
289 )300 )
290 )301 )
291 .handle("oauthCallback", ({ urlParams }) =>302 .handle("oauthCallback", ({ urlParams }) =>
292 executor.googleDiscovery.completeOAuth({303 executor[GOOGLE_DISCOVERY_EXECUTOR_KEY].completeOAuth({
293 state: urlParams.state,304 state: urlParams.state,
294 code: urlParams.code,305 code: urlParams.code,
295 error: urlParams.error,306 error: urlParams.error,
plugins/google-discovery/react/index.tsx+55−12
@@ -24,6 +24,10 @@ import { · 23 unmodified lines
24 googleDiscoveryHttpApiExtension,24 googleDiscoveryHttpApiExtension,
25} from "@executor/plugin-google-discovery-http";25} from "@executor/plugin-google-discovery-http";
26import {26import {
27 GOOGLE_DISCOVERY_OAUTH_CALLBACK_PATH,
28 GOOGLE_DISCOVERY_OAUTH_STORAGE_PREFIX,
29 GOOGLE_DISCOVERY_PLUGIN_KEY,
30 GOOGLE_DISCOVERY_SOURCE_KIND,
27 defaultGoogleDiscoveryUrl,31 defaultGoogleDiscoveryUrl,
28 type GoogleDiscoveryConnectInput,32 type GoogleDiscoveryConnectInput,
29 type GoogleDiscoveryConnectionAuth,33 type GoogleDiscoveryConnectionAuth,
@@ -31,9 +35,47 @@ import { · 1 unmodified line
31 type GoogleDiscoveryStartOAuthInput,35 type GoogleDiscoveryStartOAuthInput,
32} from "@executor/plugin-google-discovery-shared";36} from "@executor/plugin-google-discovery-shared";
33 37
34const OAUTH_STORAGE_PREFIX = "executor:google-discovery-oauth:";
35const OAUTH_TIMEOUT_MS = 2 * 60_000;38const OAUTH_TIMEOUT_MS = 2 * 60_000;
36 39
40const GOOGLE_DISCOVERY_SERVICE_ICONS: Record<string, string> = {
41 admin: "https://ssl.gstatic.com/images/branding/product/2x/admin_2020q4_48dp.png",
42 bigquery: "https://ssl.gstatic.com/bqui1/favicon.ico",
43 calendar: "https://ssl.gstatic.com/calendar/images/dynamiclogo_2020q4/calendar_31_2x.png",
44 chat: "https://ssl.gstatic.com/chat/favicon/favicon_v2.ico",
45 classroom: "https://ssl.gstatic.com/classroom/favicon.png",
46 cloudresourcemanager: "https://www.gstatic.com/devrel-devsite/prod/v0e0f589edd85502a40d78d7d0825db8ea5ef3b99b1571571945f0f3f764ff61b/cloud/images/favicons/onecloud/favicon.ico",
47 docs: "https://ssl.gstatic.com/docs/documents/images/kix-favicon7.ico",
48 drive: "https://ssl.gstatic.com/images/branding/product/2x/drive_2020q4_48dp.png",
49 forms: "https://ssl.gstatic.com/docs/forms/device_home/android_192.png",
50 gmail: "https://ssl.gstatic.com/ui/v1/icons/mail/rfr/gmail.ico",
51 keep: "https://ssl.gstatic.com/keep/icon_2020q4v2_128.png",
52 people: "https://ssl.gstatic.com/images/branding/product/2x/contacts_2022_48dp.png",
53 script: "https://ssl.gstatic.com/script/images/favicon.ico",
54 searchconsole: "https://ssl.gstatic.com/search-console/scfe/search_console-64.png",
55 sheets: "https://ssl.gstatic.com/docs/spreadsheets/favicon3.ico",
56 slides: "https://ssl.gstatic.com/docs/presentations/images/favicon5.ico",
57 tasks: "https://ssl.gstatic.com/tasks/images/favicon.ico",
58 youtube: "https://www.youtube.com/s/desktop/a94e1818/img/favicon_32x32.png",
59};
60
61const getGoogleDiscoveryServiceKey = (
62 source: Pick<Source, "namespace">,
63): string | null => {
64 const namespace = source.namespace?.trim();
65 if (!namespace || !namespace.startsWith("google.")) {
66 return null;
67 }
68
69 return namespace.slice("google.".length).replaceAll(".", "");
70};
71
72const getGoogleDiscoveryIconUrl = (
73 source: Pick<Source, "namespace">,
74): string | null => {
75 const serviceKey = getGoogleDiscoveryServiceKey(source);
76 return serviceKey ? GOOGLE_DISCOVERY_SERVICE_ICONS[serviceKey] ?? null : null;
77};
78
37const getGoogleDiscoveryHttpClient =79const getGoogleDiscoveryHttpClient =
38 defineExecutorPluginHttpApiClient<"GoogleDiscoveryReactHttpClient">()(80 defineExecutorPluginHttpApiClient<"GoogleDiscoveryReactHttpClient">()(
39 "GoogleDiscoveryReactHttpClient",81 "GoogleDiscoveryReactHttpClient",
@@ -105,7 +147,7 @@ const waitForOauthPopupResult = async ( · 65 unmodified lines
105 sessionId: string,147 sessionId: string,
106): Promise<GoogleDiscoveryOAuthPopupResult> =>148): Promise<GoogleDiscoveryOAuthPopupResult> =>
107 new Promise((resolve, reject) => {149 new Promise((resolve, reject) => {
108 const storageKey = `${OAUTH_STORAGE_PREFIX}${sessionId}`;150 const storageKey = `${GOOGLE_DISCOVERY_OAUTH_STORAGE_PREFIX}${sessionId}`;
109 const startedAt = Date.now();151 const startedAt = Date.now();
110 152
111 const cleanup = () => {153 const cleanup = () => {
@@ -168,7 +210,7 @@ function GoogleDiscoverySourceForm(props: { · 56 unmodified lines
168 const client = getGoogleDiscoveryHttpClient();210 const client = getGoogleDiscoveryHttpClient();
169 const secrets = useSecrets();211 const secrets = useSecrets();
170 const startOAuth = useAtomSet(212 const startOAuth = useAtomSet(
171 client.mutation("googleDiscovery", "startOAuth"),213 client.mutation(GOOGLE_DISCOVERY_PLUGIN_KEY, "startOAuth"),
172 { mode: "promise" },214 { mode: "promise" },
173 );215 );
174 const submitMutation = useExecutorMutation<GoogleDiscoveryConnectInput, void>(props.onSubmit);216 const submitMutation = useExecutorMutation<GoogleDiscoveryConnectInput, void>(props.onSubmit);
@@ -219,7 +261,7 @@ function GoogleDiscoverySourceForm(props: { · 44 unmodified lines
219 : null,261 : null,
220 clientAuthentication: clientSecretRef ? "client_secret_post" : "none",262 clientAuthentication: clientSecretRef ? "client_secret_post" : "none",
221 redirectUrl: new URL(263 redirectUrl: new URL(
222 "/v1/plugins/google-discovery/oauth/callback",264 GOOGLE_DISCOVERY_OAUTH_CALLBACK_PATH,
223 window.location.origin,265 window.location.origin,
224 ).toString(),266 ).toString(),
225 };267 };
@@ -492,7 +534,7 @@ function GoogleDiscoveryAddPage() { · 266 unmodified lines
492 const installation = useLocalInstallation();534 const installation = useLocalInstallation();
493 const client = getGoogleDiscoveryHttpClient();535 const client = getGoogleDiscoveryHttpClient();
494 const createSource = useAtomSet(536 const createSource = useAtomSet(
495 client.mutation("googleDiscovery", "createSource"),537 client.mutation(GOOGLE_DISCOVERY_PLUGIN_KEY, "createSource"),
496 { mode: "promise" },538 { mode: "promise" },
497 );539 );
498 540
@@ -533,7 +575,7 @@ function GoogleDiscoveryEditPage(props: { · 34 unmodified lines
533 const client = getGoogleDiscoveryHttpClient();575 const client = getGoogleDiscoveryHttpClient();
534 const configResult = useAtomValue(576 const configResult = useAtomValue(
535 installation.status === "ready"577 installation.status === "ready"
536 ? client.query("googleDiscovery", "getSourceConfig", {578 ? client.query(GOOGLE_DISCOVERY_PLUGIN_KEY, "getSourceConfig", {
537 path: {579 path: {
538 workspaceId: installation.data.scopeId,580 workspaceId: installation.data.scopeId,
539 sourceId: props.source.id,581 sourceId: props.source.id,
@@ -548,7 +590,7 @@ function GoogleDiscoveryEditPage(props: { · 8 unmodified lines
548 }) as never,590 }) as never,
549 );591 );
550 const updateSource = useAtomSet(592 const updateSource = useAtomSet(
551 client.mutation("googleDiscovery", "updateSource"),593 client.mutation(GOOGLE_DISCOVERY_PLUGIN_KEY, "updateSource"),
552 { mode: "promise" },594 { mode: "promise" },
553 );595 );
554 596
@@ -608,12 +650,12 @@ function GoogleDiscoveryDetailPage(props: { · 53 unmodified lines
608 const installation = useLocalInstallation();650 const installation = useLocalInstallation();
609 const client = getGoogleDiscoveryHttpClient();651 const client = getGoogleDiscoveryHttpClient();
610 const removeSource = useAtomSet(652 const removeSource = useAtomSet(
611 client.mutation("googleDiscovery", "removeSource"),653 client.mutation(GOOGLE_DISCOVERY_PLUGIN_KEY, "removeSource"),
612 { mode: "promise" },654 { mode: "promise" },
613 );655 );
614 const configResult = useAtomValue(656 const configResult = useAtomValue(
615 installation.status === "ready"657 installation.status === "ready"
616 ? client.query("googleDiscovery", "getSourceConfig", {658 ? client.query(GOOGLE_DISCOVERY_PLUGIN_KEY, "getSourceConfig", {
617 path: {659 path: {
618 workspaceId: installation.data.scopeId,660 workspaceId: installation.data.scopeId,
619 sourceId: props.source.id,661 sourceId: props.source.id,
@@ -710,16 +752,17 @@ function GoogleDiscoveryDetailPage(props: { · 90 unmodified lines
710}752}
711 753
712const googleDiscoverySourceType = defineFrontendSourceType({754const googleDiscoverySourceType = defineFrontendSourceType({
713 key: "google-discovery",755 key: GOOGLE_DISCOVERY_PLUGIN_KEY,
714 kind: "google_discovery",756 kind: GOOGLE_DISCOVERY_SOURCE_KIND,
715 displayName: "Google Discovery",757 displayName: "Google Discovery",
716 description: "Import Google APIs from discovery documents with plugin-owned OAuth.",758 description: "Import Google APIs from discovery documents with plugin-owned OAuth.",
759 getIconUrl: getGoogleDiscoveryIconUrl,
717 renderAddPage: GoogleDiscoveryAddPage,760 renderAddPage: GoogleDiscoveryAddPage,
718 renderEditPage: GoogleDiscoveryEditPage,761 renderEditPage: GoogleDiscoveryEditPage,
719 renderDetailPage: GoogleDiscoveryDetailPage,762 renderDetailPage: GoogleDiscoveryDetailPage,
720});763});
721 764
722export const GoogleDiscoveryReactPlugin = defineExecutorFrontendPlugin({765export const GoogleDiscoveryReactPlugin = defineExecutorFrontendPlugin({
723 key: "google-discovery",766 key: GOOGLE_DISCOVERY_PLUGIN_KEY,
724 sourceTypes: [googleDiscoverySourceType],767 sourceTypes: [googleDiscoverySourceType],
725});768});
plugins/google-discovery/sdk/catalog.ts+3−2
@@ -38,6 +38,7 @@ import { · 37 unmodified lines
38 stableHash,38 stableHash,
39 toolPathSegments,39 toolPathSegments,
40} from "@executor/source-core";40} from "@executor/source-core";
41import { GOOGLE_DISCOVERY_SOURCE_KIND } from "@executor/plugin-google-discovery-shared";
41 42
42import type { GoogleDiscoveryToolProviderData } from "./types";43import type { GoogleDiscoveryToolProviderData } from "./types";
43 44
@@ -284,7 +285,7 @@ const createHttpCapabilityFromGoogleDiscovery = (input: { · 240 unmodified lines
284 id: executableId,285 id: executableId,
285 capabilityId,286 capabilityId,
286 scopeId: input.serviceScopeId,287 scopeId: input.serviceScopeId,
287 pluginKey: "google_discovery",288 pluginKey: GOOGLE_DISCOVERY_SOURCE_KIND,
288 bindingVersion: EXECUTABLE_BINDING_VERSION,289 bindingVersion: EXECUTABLE_BINDING_VERSION,
289 binding: input.operation.providerData,290 binding: input.operation.providerData,
290 projection: {291 projection: {
@@ -384,7 +385,7 @@ export const createGoogleDiscoveryCatalogSnapshot = (input: { · 93 unmodified lines
384 createCatalogSnapshotV1FromFragments({385 createCatalogSnapshotV1FromFragments({
385 import: createCatalogImportMetadata({386 import: createCatalogImportMetadata({
386 source: input.source,387 source: input.source,
387 pluginKey: "google_discovery",388 pluginKey: GOOGLE_DISCOVERY_SOURCE_KIND,
388 }),389 }),
389 fragments: [createGoogleDiscoveryCatalogFragment(input)],390 fragments: [createGoogleDiscoveryCatalogFragment(input)],
390 });391 });
plugins/google-discovery/sdk/index.ts+15−13
@@ -20,6 +20,8 @@ import { · 19 unmodified lines
20 provideExecutorRuntime,20 provideExecutorRuntime,
21} from "@executor/platform-sdk/runtime";21} from "@executor/platform-sdk/runtime";
22import {22import {
23 GOOGLE_DISCOVERY_EXECUTOR_KEY,
24 GOOGLE_DISCOVERY_SOURCE_KIND,
23 GoogleDiscoveryConnectionAuthSchema,25 GoogleDiscoveryConnectionAuthSchema,
24 GoogleDiscoveryOAuthSessionSchema,26 GoogleDiscoveryOAuthSessionSchema,
25 defaultGoogleDiscoveryUrl,27 defaultGoogleDiscoveryUrl,
@@ -115,7 +117,7 @@ export type GoogleDiscoverySdk = { · 89 unmodified lines
115};117};
116 118
117const GoogleDiscoveryExecutorAddInputSchema = Schema.Struct({119const GoogleDiscoveryExecutorAddInputSchema = Schema.Struct({
118 kind: Schema.Literal("google_discovery"),120 kind: Schema.Literal(GOOGLE_DISCOVERY_SOURCE_KIND),
119 name: Schema.String,121 name: Schema.String,
120 service: Schema.String,122 service: Schema.String,
121 version: Schema.String,123 version: Schema.String,
@@ -476,7 +478,7 @@ const createGoogleDiscoverySourceSdk = ( · 354 unmodified lines
476 getSourceConfig: (sourceId: Source["id"]) =>478 getSourceConfig: (sourceId: Source["id"]) =>
477 Effect.gen(function* () {479 Effect.gen(function* () {
478 const source = yield* host.sources.get(sourceId);480 const source = yield* host.sources.get(sourceId);
479 if (source.kind !== "google_discovery") {481 if (source.kind !== GOOGLE_DISCOVERY_SOURCE_KIND) {
480 return yield* Effect.fail(482 return yield* Effect.fail(
481 new Error(`Source ${sourceId} is not a Google Discovery source.`),483 new Error(`Source ${sourceId} is not a Google Discovery source.`),
482 );484 );
@@ -500,7 +502,7 @@ const createGoogleDiscoverySourceSdk = ( · 17 unmodified lines
500 const createdSource = yield* host.sources.create({502 const createdSource = yield* host.sources.create({
501 source: {503 source: {
502 name: input.name.trim(),504 name: input.name.trim(),
503 kind: "google_discovery",505 kind: GOOGLE_DISCOVERY_SOURCE_KIND,
504 status: "connected",506 status: "connected",
505 enabled: true,507 enabled: true,
506 namespace: deriveGoogleDiscoveryNamespace(input.service),508 namespace: deriveGoogleDiscoveryNamespace(input.service),
@@ -518,7 +520,7 @@ const createGoogleDiscoverySourceSdk = ( · 11 unmodified lines
518 updateSource: (input: GoogleDiscoveryUpdateSourceInput) =>520 updateSource: (input: GoogleDiscoveryUpdateSourceInput) =>
519 Effect.gen(function* () {521 Effect.gen(function* () {
520 const source = yield* host.sources.get(input.sourceId as Source["id"]);522 const source = yield* host.sources.get(input.sourceId as Source["id"]);
521 if (source.kind !== "google_discovery") {523 if (source.kind !== GOOGLE_DISCOVERY_SOURCE_KIND) {
522 return yield* Effect.fail(524 return yield* Effect.fail(
523 new Error(`Source ${input.sourceId} is not a Google Discovery source.`),525 new Error(`Source ${input.sourceId} is not a Google Discovery source.`),
524 );526 );
@@ -542,7 +544,7 @@ const createGoogleDiscoverySourceSdk = ( · 17 unmodified lines
542 removeSource: (sourceId: Source["id"]) =>544 removeSource: (sourceId: Source["id"]) =>
543 Effect.gen(function* () {545 Effect.gen(function* () {
544 const source = yield* host.sources.get(sourceId);546 const source = yield* host.sources.get(sourceId);
545 if (source.kind !== "google_discovery") {547 if (source.kind !== GOOGLE_DISCOVERY_SOURCE_KIND) {
546 return yield* Effect.fail(548 return yield* Effect.fail(
547 new Error(`Source ${sourceId} is not a Google Discovery source.`),549 new Error(`Source ${sourceId} is not a Google Discovery source.`),
548 );550 );
@@ -577,7 +579,7 @@ const googleDiscoverySourceConnector = ( · 28 unmodified lines
577 oauthSessions: GoogleDiscoveryOAuthSessionStorage;579 oauthSessions: GoogleDiscoveryOAuthSessionStorage;
578 },580 },
579): ExecutorSourceConnector<GoogleDiscoveryExecutorAddInput> => ({581): ExecutorSourceConnector<GoogleDiscoveryExecutorAddInput> => ({
580 kind: "google_discovery",582 kind: GOOGLE_DISCOVERY_SOURCE_KIND,
581 displayName: "Google Discovery",583 displayName: "Google Discovery",
582 inputSchema: GoogleDiscoveryExecutorAddInputSchema,584 inputSchema: GoogleDiscoveryExecutorAddInputSchema,
583 inputSignatureWidth: 320,585 inputSignatureWidth: 320,
@@ -592,11 +594,11 @@ const googleDiscoverySourceConnector = ( · 8 unmodified lines
592const createGoogleDiscoverySourceRuntime = (options: {594const createGoogleDiscoverySourceRuntime = (options: {
593 storage: GoogleDiscoverySourceStorage;595 storage: GoogleDiscoverySourceStorage;
594}): SourcePluginRuntime => ({596}): SourcePluginRuntime => ({
595 kind: "google_discovery",597 kind: GOOGLE_DISCOVERY_SOURCE_KIND,
596 displayName: "Google Discovery",598 displayName: "Google Discovery",
597 catalogKind: "imported",599 catalogKind: "imported",
598 catalogIdentity: ({ source }) => ({600 catalogIdentity: ({ source }) => ({
599 kind: "google_discovery",601 kind: GOOGLE_DISCOVERY_SOURCE_KIND,
600 sourceId: source.id,602 sourceId: source.id,
601 }),603 }),
602 getIrModel: ({ source }) =>604 getIrModel: ({ source }) =>
@@ -613,7 +615,7 @@ const createGoogleDiscoverySourceRuntime = (options: { · 10 unmodified lines
613 importMetadata: {615 importMetadata: {
614 ...createCatalogImportMetadata({616 ...createCatalogImportMetadata({
615 source,617 source,
616 pluginKey: "google_discovery",618 pluginKey: GOOGLE_DISCOVERY_SOURCE_KIND,
617 }),619 }),
618 importerVersion: "ir.v1.google_discovery",620 importerVersion: "ir.v1.google_discovery",
619 sourceConfigHash: "missing",621 sourceConfigHash: "missing",
@@ -647,7 +649,7 @@ const createGoogleDiscoverySourceRuntime = (options: { · 27 unmodified lines
647 source,649 source,
648 documents: [650 documents: [
649 {651 {
650 documentKind: "google_discovery",652 documentKind: GOOGLE_DISCOVERY_SOURCE_KIND,
651 documentKey: stored.discoveryUrl,653 documentKey: stored.discoveryUrl,
652 contentText: document,654 contentText: document,
653 fetchedAt: now,655 fetchedAt: now,
@@ -658,7 +660,7 @@ const createGoogleDiscoverySourceRuntime = (options: { · 4 unmodified lines
658 importMetadata: {660 importMetadata: {
659 ...createCatalogImportMetadata({661 ...createCatalogImportMetadata({
660 source,662 source,
661 pluginKey: "google_discovery",663 pluginKey: GOOGLE_DISCOVERY_SOURCE_KIND,
662 }),664 }),
663 importerVersion: "ir.v1.google_discovery",665 importerVersion: "ir.v1.google_discovery",
664 },666 },
@@ -753,8 +755,8 @@ const createGoogleDiscoverySourceRuntime = (options: { · 88 unmodified lines
753export const googleDiscoverySdkPlugin = (options: {755export const googleDiscoverySdkPlugin = (options: {
754 storage: GoogleDiscoverySourceStorage;756 storage: GoogleDiscoverySourceStorage;
755 oauthSessions: GoogleDiscoveryOAuthSessionStorage;757 oauthSessions: GoogleDiscoveryOAuthSessionStorage;
756}): ExecutorSdkPlugin<"googleDiscovery", GoogleDiscoverySdk> => ({758}): ExecutorSdkPlugin<typeof GOOGLE_DISCOVERY_EXECUTOR_KEY, GoogleDiscoverySdk> => ({
757 key: "googleDiscovery",759 key: GOOGLE_DISCOVERY_EXECUTOR_KEY,
758 sources: [createGoogleDiscoverySourceRuntime(options)],760 sources: [createGoogleDiscoverySourceRuntime(options)],
759 sourceConnectors: [googleDiscoverySourceConnector(options)],761 sourceConnectors: [googleDiscoverySourceConnector(options)],
760 extendExecutor: ({ host, executor }) => {762 extendExecutor: ({ host, executor }) => {
plugins/google-discovery/sdk/tools.ts+3−2
@@ -18,6 +18,7 @@ import { sourceCoreEffectError } from "@executor/source-core"; · 17 unmodified lines
18import * as Effect from "effect/Effect";18import * as Effect from "effect/Effect";
19import * as Layer from "effect/Layer";19import * as Layer from "effect/Layer";
20import * as Schema from "effect/Schema";20import * as Schema from "effect/Schema";
21import { GOOGLE_DISCOVERY_SOURCE_KIND } from "@executor/plugin-google-discovery-shared";
21 22
22import type {23import type {
23 GoogleDiscoveryManifestMethod,24 GoogleDiscoveryManifestMethod,
@@ -43,7 +44,7 @@ export const googleDiscoveryProviderDataFromDefinition = ( · 19 unmodified lines
43 definition: GoogleDiscoveryManifestMethod;44 definition: GoogleDiscoveryManifestMethod;
44 },45 },
45): typeof GoogleDiscoveryToolProviderDataSchema.Type => ({46): typeof GoogleDiscoveryToolProviderDataSchema.Type => ({
46 kind: "google_discovery",47 kind: GOOGLE_DISCOVERY_SOURCE_KIND,
47 service: input.service,48 service: input.service,
48 version: input.version,49 version: input.version,
49 toolId: input.definition.toolId,50 toolId: input.definition.toolId,
@@ -467,7 +468,7 @@ export const createGoogleDiscoveryToolFromDefinition = ( · 417 unmodified lines
467 : {}),468 : {}),
468 },469 },
469 sourceKey: input.sourceKey,470 sourceKey: input.sourceKey,
470 pluginKind: "google_discovery",471 pluginKind: GOOGLE_DISCOVERY_SOURCE_KIND,
471 pluginData: providerData,472 pluginData: providerData,
472 };473 };
473 474
plugins/google-discovery/sdk/types.ts+2−1
@@ -1,4 +1,5 @@
1import { Schema } from "effect";1import { Schema } from "effect";
2import { GOOGLE_DISCOVERY_SOURCE_KIND } from "@executor/plugin-google-discovery-shared";
2 3
3export const GOOGLE_DISCOVERY_HTTP_METHODS = [4export const GOOGLE_DISCOVERY_HTTP_METHODS = [
4 "get",5 "get",
@@ -52,7 +53,7 @@ export const GoogleDiscoveryInvocationPayloadSchema = Schema.Struct({ · 47 unmodified lines
52});53});
53 54
54export const GoogleDiscoveryToolProviderDataSchema = Schema.Struct({55export const GoogleDiscoveryToolProviderDataSchema = Schema.Struct({
55 kind: Schema.Literal("google_discovery"),56 kind: Schema.Literal(GOOGLE_DISCOVERY_SOURCE_KIND),
56 service: Schema.String,57 service: Schema.String,
57 version: Schema.String,58 version: Schema.String,
58 toolId: Schema.String,59 toolId: Schema.String,
plugins/google-discovery/shared/index.ts+8−0
@@ -5,6 +5,14 @@ import { · 4 unmodified lines
5 StringMapSchema,5 StringMapSchema,
6} from "@executor/platform-sdk/schema";6} from "@executor/platform-sdk/schema";
7 7
8export const GOOGLE_DISCOVERY_SOURCE_KIND = "google_discovery" as const;
9export const GOOGLE_DISCOVERY_PLUGIN_KEY = "google-discovery" as const;
10export const GOOGLE_DISCOVERY_EXECUTOR_KEY = "googleDiscovery" as const;
11export const GOOGLE_DISCOVERY_OAUTH_STORAGE_PREFIX =
12 "executor:google-discovery-oauth:" as const;
13export const GOOGLE_DISCOVERY_OAUTH_CALLBACK_PATH =
14 `/v1/plugins/${GOOGLE_DISCOVERY_PLUGIN_KEY}/oauth/callback` as const;
15
8export const GoogleDiscoveryOAuthClientAuthenticationSchema = Schema.Literal(16export const GoogleDiscoveryOAuthClientAuthenticationSchema = Schema.Literal(
9 "none",17 "none",
10 "client_secret_post",18 "client_secret_post",
plugins/mcp/sdk/discovery.ts+1−1
@@ -6,11 +6,11 @@ import { · 5 unmodified lines
6 type SourceDiscoveryProbeInput,6 type SourceDiscoveryProbeInput,
7 type SourceDiscoveryResult,7 type SourceDiscoveryResult,
8} from "@executor/source-core";8} from "@executor/source-core";
9import { startMcpOAuthAuthorization } from "@executor/auth-mcp-oauth";
10import * as Either from "effect/Either";9import * as Either from "effect/Either";
11import * as Effect from "effect/Effect";10import * as Effect from "effect/Effect";
12 11
13import { createSdkMcpConnector } from "./connection";12import { createSdkMcpConnector } from "./connection";
13import { startMcpOAuthAuthorization } from "./oauth";
14import { discoverMcpToolsFromConnector } from "./tools";14import { discoverMcpToolsFromConnector } from "./tools";
15 15
16export const detectMcpSource = (16export const detectMcpSource = (
plugins/mcp/sdk/index.ts+1−1
@@ -14,7 +14,7 @@ import type { · 13 unmodified lines
14import {14import {
15 exchangeMcpOAuthAuthorizationCode,15 exchangeMcpOAuthAuthorizationCode,
16 startMcpOAuthAuthorization,16 startMcpOAuthAuthorization,
17} from "@executor/auth-mcp-oauth";17} from "./oauth";
18import {18import {
19 contentHash,19 contentHash,
20 createCatalogImportMetadata,20 createCatalogImportMetadata,
plugins/mcp/sdk/oauth-effect-errors.tsrenamed from packages/auth/mcp-oauth/src/effect-errors.ts+0−0

File renamed without changes.

plugins/mcp/sdk/oauth.tsrenamed from packages/auth/mcp-oauth/src/index.ts+4−3
@@ -8,7 +8,8 @@ import type { · 7 unmodified lines
8 OAuthTokens,8 OAuthTokens,
9} from "@modelcontextprotocol/sdk/shared/auth.js";9} from "@modelcontextprotocol/sdk/shared/auth.js";
10import * as Effect from "effect/Effect";10import * as Effect from "effect/Effect";
11import { mcpOAuthEffectError } from "./effect-errors";11
12import { mcpOAuthEffectError } from "./oauth-effect-errors";
12 13
13type JsonValue =14type JsonValue =
14 | string15 | string
@@ -123,7 +124,7 @@ export const startMcpOAuthAuthorization = (input: { · 108 unmodified lines
123 });124 });
124 125
125 if (result !== "REDIRECT" || !captured.authorizationUrl || !captured.codeVerifier) {126 if (result !== "REDIRECT" || !captured.authorizationUrl || !captured.codeVerifier) {
126 return yield* mcpOAuthEffectError("index", "OAuth flow did not produce an authorization redirect");127 return yield* mcpOAuthEffectError("oauth", "OAuth flow did not produce an authorization redirect");
127 }128 }
128 129
129 return {130 return {
@@ -196,7 +197,7 @@ export const exchangeMcpOAuthAuthorizationCode = (input: { · 66 unmodified lines
196 });197 });
197 198
198 if (result !== "AUTHORIZED" || !captured.tokens) {199 if (result !== "AUTHORIZED" || !captured.tokens) {
199 return yield* mcpOAuthEffectError("index", "OAuth redirect did not complete MCP OAuth setup");200 return yield* mcpOAuthEffectError("oauth", "OAuth redirect did not complete MCP OAuth setup");
200 }201 }
201 202
202 return {203 return {
plugins/mcp/sdk/package.json+0−1
@@ -12,7 +12,6 @@ · 11 unmodified lines
12 "typecheck": "bunx tsc --noEmit -p tsconfig.json"12 "typecheck": "bunx tsc --noEmit -p tsconfig.json"
13 },13 },
14 "dependencies": {14 "dependencies": {
15 "@executor/auth-mcp-oauth": "workspace:*",
16 "@executor/codemode-core": "workspace:*",15 "@executor/codemode-core": "workspace:*",
17 "@executor/ir": "workspace:*",16 "@executor/ir": "workspace:*",
18 "@executor/platform-sdk": "workspace:*",17 "@executor/platform-sdk": "workspace:*",