History

Remove legacy source oauth and discovery flows

Rhys Sullivan committed 7 months ago

Commit f25b0911 parent 813a1c724 files · +2 −1898Browse files

Unified · Split

24 files
  1. packages/clients/react/src/index.ts +0 −177
  2. packages/platform/api/package.json +0 −2
  3. packages/platform/api/src/api.ts +0 −2
  4. packages/platform/api/src/client.ts +0 −58
  5. packages/platform/api/src/http.ts +0 −2
  6. packages/platform/api/src/index.ts +0 −18
  7. packages/platform/api/src/oauth/api.ts +0 −99
  8. packages/platform/api/src/oauth/http.ts +0 −315
  9. packages/platform/api/src/sources/api.ts +0 −75
  10. packages/platform/api/src/sources/http.ts +0 −358
  11. packages/platform/internal/src/tools/executor-admin.ts +0 −190
  12. packages/platform/sdk/src/executor-effect.ts +0 −114
  13. packages/platform/sdk/src/executor.ts +0 −21
  14. packages/platform/sdk/src/operations.ts +0 −1
  15. packages/platform/sdk/src/runtime/index.ts +0 −1
  16. packages/platform/sdk/src/runtime/sources/source-auth-service.ts +0 −80
  17. packages/platform/sdk/src/runtime/sources/source-discovery.test.ts +0 −278
  18. packages/platform/sdk/src/schema/enums.ts +0 −16
  19. packages/platform/sdk/src/schema/index.ts +0 −1
  20. packages/platform/sdk/src/schema/models/source-discovery.ts +0 −16
  21. packages/platform/sdk/src/sources/contracts.ts +0 −10
  22. packages/platform/sdk/src/sources/discovery.ts +0 −56
  23. packages/sources/core/src/discovery-models.ts +1 −4
  24. packages/sources/core/src/discovery.ts +1 −4
packages/clients/react/src/index.ts+0−177
@@ -9,9 +9,7 @@ import type { · 8 unmodified lines
99 CreateSecretPayload,
1010 CreateSecretResult,
1111 CreateSourcePayload,
12− CreateWorkspaceOauthClientPayload,
1312 DeleteSecretResult,
14− DiscoverSourcePayload,
1513 InstanceConfig,
1614 LocalInstallation,
1715 SecretListItem,
@@ -21,11 +19,9 @@ import type { · 3 unmodified lines
2119 } from "@executor/platform-api";
2220 import type {
2321 Source,
24− SourceDiscoveryResult,
2522 SourceInspection,
2623 SourceInspectionDiscoverResult,
2724 SourceInspectionToolDetail,
28− ScopeOauthClient as WorkspaceOauthClient,
2925 } from "@executor/platform-sdk/schema";
3026 import * as Cause from "effect/Cause";
3127 import * as Effect from "effect/Effect";
@@ -67,13 +63,6 @@ type SourceDiscoveryKeyParts = readonly [ · 35 unmodified lines
6763 string,
6864 number | null,
6965 ];
70−type WorkspaceOauthClientsKeyParts = readonly [
71− boolean,
72− Source["scopeId"],
73− string,
74− string,
75−];
76−
7766 type InvalidationTarget = {
7867 workspaceId?: Source["scopeId"];
7968 accountId?: string;
@@ -83,7 +72,6 @@ type InvalidationTarget = { · 3 unmodified lines
8372 type ActiveQueryCollections = {
8473 sourceLists: Set<string>;
8574 sources: Set<string>;
86− workspaceOauthClients: Set<string>;
8775 inspections: Set<string>;
8876 toolDetails: Set<string>;
8977 discoveries: Set<string>;
@@ -171,16 +159,6 @@ const encodeDiscoveryKey = ( · 81 unmodified lines
171159 ): string =>
172160 encodeAtomKey([enabled, workspaceId, accountId, sourceId, query, limit] satisfies SourceDiscoveryKeyParts);
173161
174−const encodeWorkspaceOauthClientsKey = (
175− enabled: boolean,
176− workspaceId: Source["scopeId"],
177− accountId: string,
178− providerKey: string,
179−): string =>
180− encodeAtomKey(
181− [enabled, workspaceId, accountId, providerKey] satisfies WorkspaceOauthClientsKeyParts,
182− );
183−
184162 const toError = (cause: unknown): Error =>
185163 cause instanceof Error ? cause : new Error(String(cause));
186164
@@ -391,26 +369,6 @@ const sourceDiscoveryAtom = Atom.family((key: string) => { · 204 unmodified lines
391369 ).pipe(Atom.keepAlive);
392370 });
393371
394−const workspaceOauthClientsAtom = Atom.family((key: string) => {
395− const [enabled, workspaceId, accountId, providerKey] = decodeAtomKey<WorkspaceOauthClientsKeyParts>(key);
396−
397− return Atom.make(
398− enabled
399− ? controlPlaneRequest({
400− accountId,
401− execute: (client) => client.sources.listWorkspaceOauthClients({
402− path: {
403− workspaceId,
404− },
405− urlParams: {
406− providerKey,
407− },
408− }),
409− })
410− : Effect.never,
411− ).pipe(Atom.keepAlive);
412−});
413−
414372 export type Loadable<T> =
415373 | { status: "loading" }
416374 | { status: "error"; error: Error }
@@ -539,7 +497,6 @@ const setCachedAtomValue = <A>( · 122 unmodified lines
539497 const createActiveQueryCollections = (): ActiveQueryCollections => ({
540498 sourceLists: new Set(),
541499 sources: new Set(),
542− workspaceOauthClients: new Set(),
543500 inspections: new Set(),
544501 toolDetails: new Set(),
545502 discoveries: new Set(),
@@ -582,13 +539,6 @@ const invalidateTrackedQueries = ( · 36 unmodified lines
582539 }
583540 });
584541
585− activeQueries.workspaceOauthClients.forEach((key) => {
586− const [enabled, workspaceId, accountId] = decodeAtomKey<WorkspaceOauthClientsKeyParts>(key);
587− if (enabled && targetMatches(target, workspaceId, accountId)) {
588− registry.refresh(workspaceOauthClientsAtom(key));
589− }
590− });
591−
592542 activeQueries.inspections.forEach((key) => {
593543 const [enabled, workspaceId, accountId, sourceId] = decodeAtomKey<SourceKeyParts>(key);
594544 if (enabled && targetMatches(target, workspaceId, accountId, sourceId)) {
@@ -946,37 +896,6 @@ export const useSource = (sourceId: string): Loadable<Source> => { · 351 unmodified lines
946896 return workspace.enabled ? source : pendingLoadable(workspace.workspace);
947897 };
948898
949−export const useWorkspaceOauthClients = (
950− providerKey: string | null,
951−): Loadable<ReadonlyArray<WorkspaceOauthClient>> => {
952− const workspace = useWorkspaceRequestContext();
953− const key = encodeWorkspaceOauthClientsKey(
954− workspace.enabled && providerKey !== null,
955− workspace.workspaceId,
956− workspace.accountId,
957− providerKey ?? "",
958− );
959− useTrackActiveKey(
960− "workspaceOauthClients",
961− key,
962− workspace.enabled && providerKey !== null,
963− );
964− const oauthClients = useLoadableAtom(workspaceOauthClientsAtom(key));
965−
966− if (!workspace.enabled) {
967− return pendingLoadable(workspace.workspace);
968− }
969−
970− if (providerKey === null) {
971− return {
972− status: "ready",
973− data: [],
974− };
975− }
976−
977− return oauthClients;
978−};
979−
980899 export const useSourceInspection = (sourceId: string): Loadable<SourceInspection> => {
981900 const workspace = useWorkspaceRequestContext();
982901 const requestedSourceId = workspace.enabled
@@ -1214,116 +1133,20 @@ export const useRemoveSource = () => · 231 unmodified lines
12141133 },
12151134 );
12161135
1217−export const useDiscoverSource = () =>
1218− useSourceMutation<DiscoverSourcePayload, SourceDiscoveryResult>(
1219− React.useCallback(
1220− ({ accountId, payload }) =>
1221− runControlPlane({
1222− accountId,
1223− execute: (client) => client.sources.discover({
1224− payload,
1225− }),
1226− }),
1227− [],
1228− ),
1229− );
1230−
1231−export const useCreateWorkspaceOauthClient = () =>
1232− useSourceMutation<CreateWorkspaceOauthClientPayload, WorkspaceOauthClient>(
1233− React.useCallback(
1234− ({ workspaceId, accountId, payload }) =>
1235− runControlPlane({
1236− accountId,
1237− execute: (client) => client.sources.createWorkspaceOauthClient({
1238− path: {
1239− workspaceId,
1240− },
1241− payload,
1242− }),
1243− }),
1244− [],
1245− ),
1246− {
1247− onSuccess: (context) => {
1248− context.invalidateQueries({
1249− workspaceId: context.workspaceId,
1250− accountId: context.accountId,
1251− });
1252− },
1253− },
1254− );
1255−
1256−export const useRemoveWorkspaceOauthClient = () =>
1257− useSourceMutation<WorkspaceOauthClient["id"], { removed: boolean }>(
1258− React.useCallback(
1259− ({ workspaceId, accountId, payload }) =>
1260− runControlPlane({
1261− accountId,
1262− execute: (client) => client.sources.removeWorkspaceOauthClient({
1263− path: {
1264− workspaceId,
1265− oauthClientId: payload,
1266− },
1267− }),
1268− }),
1269− [],
1270− ),
1271− {
1272− onSuccess: (context) => {
1273− context.invalidateQueries({
1274− workspaceId: context.workspaceId,
1275− accountId: context.accountId,
1276− });
1277− },
1278− },
1279− );
1280−
1281−export const useRemoveProviderAuthGrant = () =>
1282− useSourceMutation<
1283− Extract<Source["auth"], { kind: "provider_grant_ref" }>["grantId"],
1284− { removed: boolean }
1285− >(
1286− React.useCallback(
1287− ({ workspaceId, accountId, payload }) =>
1288− runControlPlane({
1289− accountId,
1290− execute: (client) => client.sources.removeProviderAuthGrant({
1291− path: {
1292− workspaceId,
1293− grantId: payload,
1294− },
1295− }),
1296− }),
1297− [],
1298− ),
1299− {
1300− onSuccess: (context) => {
1301− context.invalidateQueries({
1302− workspaceId: context.workspaceId,
1303− accountId: context.accountId,
1304− });
1305− },
1306− },
1307− );
1308−
13091136 export type {
13101137 CreateSecretPayload,
13111138 CreateSecretResult,
13121139 CreateSourcePayload,
1313− CreateWorkspaceOauthClientPayload,
13141140 DeleteSecretResult,
1315− DiscoverSourcePayload,
13161141 InstanceConfig,
13171142 LocalInstallation,
13181143
13191144 SecretListItem,
13201145 Source,
1321− SourceDiscoveryResult,
13221146 SourceInspection,
13231147 SourceInspectionDiscoverResult,
13241148 SourceInspectionToolDetail,
13251149 UpdateSecretPayload,
13261150 UpdateSecretResult,
13271151 UpdateSourcePayload,
1328− WorkspaceOauthClient,
13291152 };
packages/platform/api/package.json+0−2
@@ -12,8 +12,6 @@ · 11 unmodified lines
1212 "./local/api": "./src/local/api.ts",
1313 "./local/http": "./src/local/http.ts",
1414 "./local-context": "./src/local-context.ts",
15− "./oauth/api": "./src/oauth/api.ts",
16− "./oauth/http": "./src/oauth/http.ts",
1715 "./policies/api": "./src/policies/api.ts",
1816 "./policies/http": "./src/policies/http.ts",
1917 "./sources/api": "./src/sources/api.ts",
packages/platform/api/src/api.ts+0−2
@@ -2,13 +2,11 @@ import { HttpApi, OpenApi } from "@effect/platform"; · 1 unmodified line
22
33 import { ExecutionsApi } from "./executions/api";
44 import { LocalApi } from "./local/api";
5−import { OAuthApi } from "./oauth/api";
65 import { PoliciesApi } from "./policies/api";
76 import { SourcesApi } from "./sources/api";
87
98 export class ExecutorApi extends HttpApi.make("executor")
109 .add(LocalApi)
11− .add(OAuthApi)
1210 .add(SourcesApi)
1311 .add(PoliciesApi)
1412 .add(ExecutionsApi)
packages/platform/api/src/client.ts+0−58
@@ -34,19 +34,6 @@ export type ExecutorApiClient = { · 33 unmodified lines
3434 remove: PromiseMethod<ExecutorApiEffectClient["policies"]["remove"]>;
3535 };
3636 sources: {
37− discover: PromiseMethod<ExecutorApiEffectClient["sources"]["discover"]>;
38− listWorkspaceOauthClients: PromiseMethod<
39− ExecutorApiEffectClient["sources"]["listWorkspaceOauthClients"]
40− >;
41− createWorkspaceOauthClient: PromiseMethod<
42− ExecutorApiEffectClient["sources"]["createWorkspaceOauthClient"]
43− >;
44− removeWorkspaceOauthClient: PromiseMethod<
45− ExecutorApiEffectClient["sources"]["removeWorkspaceOauthClient"]
46− >;
47− removeProviderAuthGrant: PromiseMethod<
48− ExecutorApiEffectClient["sources"]["removeProviderAuthGrant"]
49− >;
5037 list: PromiseMethod<ExecutorApiEffectClient["sources"]["list"]>;
5138 create: PromiseMethod<ExecutorApiEffectClient["sources"]["create"]>;
5239 get: PromiseMethod<ExecutorApiEffectClient["sources"]["get"]>;
@@ -59,16 +46,6 @@ export type ExecutorApiClient = { · 6 unmodified lines
5946 >;
6047 credentialPage: PromiseMethod<ExecutorApiEffectClient["sources"]["credentialPage"]>;
6148 credentialSubmit: PromiseMethod<ExecutorApiEffectClient["sources"]["credentialSubmit"]>;
62− credentialComplete: PromiseMethod<
63− ExecutorApiEffectClient["sources"]["credentialComplete"]
64− >;
65− providerOauthComplete: PromiseMethod<
66− ExecutorApiEffectClient["sources"]["providerOauthComplete"]
67− >;
68− };
69− oauth: {
70− startSourceAuth: PromiseMethod<ExecutorApiEffectClient["oauth"]["startSourceAuth"]>;
71− sourceAuthCallback: PromiseMethod<ExecutorApiEffectClient["oauth"]["sourceAuthCallback"]>;
7249 };
7350 executions: {
7451 create: PromiseMethod<ExecutorApiEffectClient["executions"]["create"]>;
@@ -125,23 +102,6 @@ export const createExecutorApiClient = async (input: { · 50 unmodified lines
125102 remove: wrapMethod(effectClientPromise, (client) => client.policies.remove),
126103 },
127104 sources: {
128− discover: wrapMethod(effectClientPromise, (client) => client.sources.discover),
129− listWorkspaceOauthClients: wrapMethod(
130− effectClientPromise,
131− (client) => client.sources.listWorkspaceOauthClients,
132− ),
133− createWorkspaceOauthClient: wrapMethod(
134− effectClientPromise,
135− (client) => client.sources.createWorkspaceOauthClient,
136− ),
137− removeWorkspaceOauthClient: wrapMethod(
138− effectClientPromise,
139− (client) => client.sources.removeWorkspaceOauthClient,
140− ),
141− removeProviderAuthGrant: wrapMethod(
142− effectClientPromise,
143− (client) => client.sources.removeProviderAuthGrant,
144− ),
145105 list: wrapMethod(effectClientPromise, (client) => client.sources.list),
146106 create: wrapMethod(effectClientPromise, (client) => client.sources.create),
147107 get: wrapMethod(effectClientPromise, (client) => client.sources.get),
@@ -164,24 +124,6 @@ export const createExecutorApiClient = async (input: { · 16 unmodified lines
164124 effectClientPromise,
165125 (client) => client.sources.credentialSubmit,
166126 ),
167− credentialComplete: wrapMethod(
168− effectClientPromise,
169− (client) => client.sources.credentialComplete,
170− ),
171− providerOauthComplete: wrapMethod(
172− effectClientPromise,
173− (client) => client.sources.providerOauthComplete,
174− ),
175− },
176− oauth: {
177− startSourceAuth: wrapMethod(
178− effectClientPromise,
179− (client) => client.oauth.startSourceAuth,
180− ),
181− sourceAuthCallback: wrapMethod(
182− effectClientPromise,
183− (client) => client.oauth.sourceAuthCallback,
184− ),
185127 },
186128 executions: {
187129 create: wrapMethod(effectClientPromise, (client) => client.executions.create),
packages/platform/api/src/http.ts+0−2
@@ -6,13 +6,11 @@ import { ExecutorApi } from "./api"; · 5 unmodified lines
66 import { createControlPlaneExecutorLayer } from "./executor-context";
77 import { ExecutorExecutionsLive } from "./executions/http";
88 import { ExecutorLocalLive } from "./local/http";
9−import { ExecutorOAuthLive } from "./oauth/http";
109 import { ExecutorPoliciesLive } from "./policies/http";
1110 import { ExecutorSourcesLive } from "./sources/http";
1211
1312 export const ExecutorApiLive = HttpApiBuilder.api(ExecutorApi).pipe(
1413 Layer.provide(ExecutorLocalLive),
15− Layer.provide(ExecutorOAuthLive),
1614 Layer.provide(ExecutorSourcesLive),
1715 Layer.provide(ExecutorPoliciesLive),
1816 Layer.provide(ExecutorExecutionsLive),
packages/platform/api/src/index.ts+0−18
@@ -38,28 +38,10 @@ export { · 37 unmodified lines
3838 } from "./local/api";
3939
4040 export {
41− OAuthApi,
42− StartSourceOAuthPayloadSchema,
43− StartSourceOAuthResultSchema,
44− CompleteSourceOAuthResultSchema,
45− SourceOAuthPopupFailureResultSchema,
46− SourceOAuthPopupResultSchema,
47− SourceOAuthPopupSuccessResultSchema,
48− type StartSourceOAuthPayload,
49− type StartSourceOAuthResult,
50− type CompleteSourceOAuthResult,
51− type SourceOAuthPopupResult,
52−} from "./oauth/api";
53−
54−export {
55− CreateWorkspaceOauthClientPayloadSchema,
5641 CreateSourcePayloadSchema,
57− DiscoverSourcePayloadSchema,
5842 SourcesApi,
5943 UpdateSourcePayloadSchema,
60− type CreateWorkspaceOauthClientPayload,
6144 type CreateSourcePayload,
62− type DiscoverSourcePayload,
6345 type UpdateSourcePayload,
6446 } from "./sources/api";
6547
packages/platform/api/src/oauth/api.ts+0−99
@@ -1,99 +0,0 @@
1−import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "@effect/platform";
2−import {
3− JsonObjectSchema,
4− SecretRefSchema,
5− SourceAuthSessionIdSchema,
6− ScopeIdSchema as WorkspaceIdSchema,
7−} from "@executor/platform-sdk/schema";
8−import * as Schema from "effect/Schema";
9−
10−import {
11− ControlPlaneBadRequestError,
12− ControlPlaneForbiddenError,
13− ControlPlaneStorageError,
14− ControlPlaneUnauthorizedError,
15−} from "@executor/platform-sdk/errors";
16−import { TrimmedNonEmptyStringSchema } from "@executor/platform-sdk/string-schemas";
17−
18−const workspaceIdParam = HttpApiSchema.param("workspaceId", WorkspaceIdSchema);
19−
20−const HtmlSchema = HttpApiSchema.Text({
21− contentType: "text/html",
22−});
23−
24−export const StartSourceOAuthPayloadSchema = Schema.Struct({
25− providerKey: TrimmedNonEmptyStringSchema,
26− name: Schema.optional(Schema.NullOr(Schema.String)),
27− config: Schema.optional(Schema.NullOr(JsonObjectSchema)),
28−});
29−
30−export type StartSourceOAuthPayload = typeof StartSourceOAuthPayloadSchema.Type;
31−
32−export const StartSourceOAuthResultSchema = Schema.Struct({
33− sessionId: SourceAuthSessionIdSchema,
34− authorizationUrl: Schema.String,
35−});
36−
37−export type StartSourceOAuthResult = typeof StartSourceOAuthResultSchema.Type;
38−
39−export const SourceOAuthAuthSchema = Schema.Struct({
40− kind: Schema.Literal("oauth2"),
41− headerName: Schema.String,
42− prefix: Schema.String,
43− accessToken: SecretRefSchema,
44− refreshToken: Schema.NullOr(SecretRefSchema),
45−});
46−
47−export const CompleteSourceOAuthResultSchema = Schema.Struct({
48− sessionId: SourceAuthSessionIdSchema,
49− auth: SourceOAuthAuthSchema,
50−});
51−
52−export type CompleteSourceOAuthResult = typeof CompleteSourceOAuthResultSchema.Type;
53−
54−export const SourceOAuthPopupSuccessResultSchema = Schema.Struct({
55− type: Schema.Literal("executor:oauth-result"),
56− ok: Schema.Literal(true),
57− sessionId: SourceAuthSessionIdSchema,
58− auth: SourceOAuthAuthSchema,
59−});
60−
61−export const SourceOAuthPopupFailureResultSchema = Schema.Struct({
62− type: Schema.Literal("executor:oauth-result"),
63− ok: Schema.Literal(false),
64− sessionId: Schema.Null,
65− error: Schema.String,
66−});
67−
68−export const SourceOAuthPopupResultSchema = Schema.Union(
69− SourceOAuthPopupSuccessResultSchema,
70− SourceOAuthPopupFailureResultSchema,
71−);
72−
73−export type SourceOAuthPopupResult = typeof SourceOAuthPopupResultSchema.Type;
74−
75−const OAuthCallbackUrlParamsSchema = Schema.Struct({
76− state: Schema.String,
77− code: Schema.optional(Schema.String),
78− error: Schema.optional(Schema.String),
79− error_description: Schema.optional(Schema.String),
80−});
81−
82−export class OAuthApi extends HttpApiGroup.make("oauth")
83− .add(
84− HttpApiEndpoint.post("startSourceAuth")`/workspaces/${workspaceIdParam}/oauth/source-auth/start`
85− .setPayload(StartSourceOAuthPayloadSchema)
86− .addSuccess(StartSourceOAuthResultSchema)
87− .addError(ControlPlaneBadRequestError)
88− .addError(ControlPlaneUnauthorizedError)
89− .addError(ControlPlaneForbiddenError)
90− .addError(ControlPlaneStorageError),
91− )
92− .add(
93− HttpApiEndpoint.get("sourceAuthCallback")`/oauth/source-auth/callback`
94− .setUrlParams(OAuthCallbackUrlParamsSchema)
95− .addSuccess(HtmlSchema)
96− .addError(ControlPlaneBadRequestError)
97− .addError(ControlPlaneStorageError),
98− )
99− .prefix("/v1") {}
packages/platform/api/src/oauth/http.ts+0−315
@@ -1,315 +0,0 @@
1−import {
2− HttpApiBuilder,
3− HttpServerRequest,
4− HttpServerResponse,
5−} from "@effect/platform";
6−import type { ScopeId as WorkspaceId } from "@executor/platform-sdk/schema";
7−import * as Cause from "effect/Cause";
8−import * as Either from "effect/Either";
9−import * as Effect from "effect/Effect";
10−import * as Schema from "effect/Schema";
11−
12−import { ExecutorApi } from "../api";
13−import {
14− ControlPlaneBadRequestError,
15− ControlPlaneStorageError,
16−} from "../errors";
17−import {
18− getControlPlaneExecutor,
19− resolveRequestedLocalWorkspace,
20−} from "../local-context";
21−import {
22− SourceOAuthPopupResultSchema,
23− type SourceOAuthPopupResult,
24−} from "./api";
25−
26−const OAUTH_RESULT_STORAGE_KEY_PREFIX = "executor:oauth-result:";
27−
28−const escapeHtml = (value: string): string =>
29− value
30− .replaceAll("&", "&amp;")
31− .replaceAll("<", "&lt;")
32− .replaceAll(">", "&gt;")
33− .replaceAll('"', "&quot;")
34− .replaceAll("'", "&#39;");
35−
36−const serializeValueForScript = (value: unknown): string =>
37− JSON.stringify(value)
38− .replaceAll("<", "\\u003c")
39− .replaceAll(">", "\\u003e")
40− .replaceAll("&", "\\u0026");
41−
42−const encodeSourceOAuthPopupResultJson = Schema.encodeSync(
43− Schema.parseJson(SourceOAuthPopupResultSchema),
44−);
45−
46−const serializeJsonLiteralForScript = (value: string): string =>
47− value
48− .replaceAll("<", "\\u003c")
49− .replaceAll(">", "\\u003e")
50− .replaceAll("&", "\\u0026");
51−
52−const htmlResponse = (html: string, status = 200) =>
53− HttpServerResponse.html(html).pipe(
54− HttpServerResponse.setStatus(status),
55− );
56−
57−const readHeader = (headers: unknown, name: string): string | null => {
58− if (headers instanceof Headers) {
59− return headers.get(name);
60− }
61−
62− if (headers && typeof headers === "object") {
63− const record = headers as Record<string, unknown>;
64− const direct = record[name];
65− if (typeof direct === "string" && direct.length > 0) {
66− return direct;
67− }
68−
69− const lower = record[name.toLowerCase()];
70− if (typeof lower === "string" && lower.length > 0) {
71− return lower;
72− }
73− }
74−
75− return null;
76−};
77−
78−const resolveRequestOrigin = (request: { url: string; headers: unknown }): string | null => {
79− try {
80− return new URL(request.url).origin;
81− } catch {
82− const forwardedHost = readHeader(request.headers, "x-forwarded-host");
83− const host = forwardedHost ?? readHeader(request.headers, "host");
84− if (!host) {
85− return null;
86− }
87−
88− const forwardedProto = readHeader(request.headers, "x-forwarded-proto");
89− const protocol = forwardedProto && forwardedProto.length > 0 ? forwardedProto : "http";
90− return `${protocol}://${host}`;
91− }
92−};
93−
94−const toStartSourceOAuthError = (input: {
95− workspaceId: WorkspaceId;
96− providerKey: string;
97− cause: Cause.Cause<unknown>;
98−}) => {
99− const pretty = Cause.pretty(input.cause);
100− console.error("oauth.start_source_auth failed", {
101− workspaceId: input.workspaceId,
102− providerKey: input.providerKey,
103− pretty,
104− });
105−
106− if (pretty.includes("Invalid URL") || pretty.includes("required")) {
107− return new ControlPlaneBadRequestError({
108− operation: "oauth.start_source_auth",
109− message: pretty,
110− details: pretty,
111− });
112− }
113−
114− return new ControlPlaneStorageError({
115− operation: "oauth.start_source_auth",
116− message: pretty,
117− details: pretty,
118− });
119−};
120−
121−const sourceOAuthPopupResultDocument = (input: {
122− title: string;
123− message: string;
124− status: "connected" | "failed";
125− sessionId: string;
126− payload: SourceOAuthPopupResult;
127−}) => `<!doctype html>
128−<html lang="en">
129− <head>
130− <meta charset="utf-8" />
131− <meta name="viewport" content="width=device-width, initial-scale=1" />
132− <title>${escapeHtml(input.title)}</title>
133− <style>
134− :root {
135− --bg: #fafaf9;
136− --surface: #ffffff;
137− --text: #1c1917;
138− --text-secondary: #78716c;
139− --border: #e7e5e4;
140− --green: #16a34a;
141− --green-light: #f0fdf4;
142− --green-border: #bbf7d0;
143− --red: #dc2626;
144− --red-light: #fef2f2;
145− --red-border: #fecaca;
146− }
147−
148− * { box-sizing: border-box; margin: 0; padding: 0; }
149−
150− body {
151− min-height: 100vh;
152− display: flex;
153− align-items: center;
154− justify-content: center;
155− background: var(--bg);
156− color: var(--text);
157− font-family: system-ui, -apple-system, sans-serif;
158− padding: 24px;
159− }
160−
161− .card {
162− width: 100%;
163− max-width: 420px;
164− background: var(--surface);
165− border: 1px solid var(--border);
166− border-radius: 16px;
167− padding: 28px;
168− text-align: center;
169− }
170−
171− .status {
172− display: inline-flex;
173− align-items: center;
174− justify-content: center;
175− padding: 6px 12px;
176− border-radius: 999px;
177− font-size: 12px;
178− font-weight: 600;
179− margin-bottom: 14px;
180− }
181−
182− .status[data-status='connected'] {
183− background: var(--green-light);
184− border: 1px solid var(--green-border);
185− color: var(--green);
186− }
187−
188− .status[data-status='failed'] {
189− background: var(--red-light);
190− border: 1px solid var(--red-border);
191− color: var(--red);
192− }
193−
194− h1 {
195− font-size: 22px;
196− line-height: 1.2;
197− margin-bottom: 10px;
198− }
199−
200− p {
201− color: var(--text-secondary);
202− font-size: 14px;
203− line-height: 1.6;
204− }
205− </style>
206− </head>
207− <body>
208− <main class="card">
209− <div class="status" data-status="${escapeHtml(input.status)}">${escapeHtml(input.status)}</div>
210− <h1>${escapeHtml(input.title)}</h1>
211− <p>${escapeHtml(input.message)}</p>
212− </main>
213− <script>
214− (() => {
215− const payload = ${serializeJsonLiteralForScript(encodeSourceOAuthPopupResultJson(input.payload))};
216− const storageKey = ${serializeValueForScript(`${OAUTH_RESULT_STORAGE_KEY_PREFIX}${input.sessionId}`)};
217− try {
218− window.localStorage.setItem(storageKey, JSON.stringify(payload));
219− } catch {}
220− try {
221− if (window.opener) {
222− window.opener.postMessage(payload, window.location.origin);
223− }
224− } finally {
225− window.setTimeout(() => window.close(), 120);
226− }
227− })();
228− </script>
229− </body>
230−</html>`;
231−
232−export const ExecutorOAuthLive = HttpApiBuilder.group(
233− ExecutorApi,
234− "oauth",
235− (handlers) =>
236− handlers
237− .handle("startSourceAuth", ({ path, payload }) =>
238− resolveRequestedLocalWorkspace("oauth.start_source_auth", path.workspaceId).pipe(
239− Effect.flatMap((executor) =>
240− Effect.gen(function* () {
241− const request = yield* HttpServerRequest.HttpServerRequest;
242− return yield* executor.oauth.startSourceAuth({
243− baseUrl: resolveRequestOrigin(request),
244− displayName: payload.name,
245− provider: {
246− providerKey: payload.providerKey,
247− config: payload.config ?? null,
248− },
249− });
250− }).pipe(
251− Effect.catchAllCause((cause) =>
252− Effect.fail(
253− toStartSourceOAuthError({
254− workspaceId: path.workspaceId,
255− providerKey: payload.providerKey,
256− cause,
257− }),
258− ),
259− ),
260− )
261− ),
262− ),
263− )
264− .handle("sourceAuthCallback", ({ urlParams }) =>
265− Effect.flatMap(getControlPlaneExecutor(), (executor) =>
266− Effect.gen(function* () {
267− const completed = yield* Effect.either(
268− executor.oauth.completeSourceAuth({
269− state: urlParams.state,
270− code: urlParams.code,
271− error: urlParams.error,
272− errorDescription: urlParams.error_description,
273− }),
274− );
275−
276− if (Either.isRight(completed)) {
277− return htmlResponse(
278− sourceOAuthPopupResultDocument({
279− title: "OAuth connected",
280− message: "OAuth credentials are ready. Return to the source form to finish saving.",
281− status: "connected",
282− sessionId: completed.right.sessionId,
283− payload: {
284− type: "executor:oauth-result",
285− ok: true,
286− sessionId: completed.right.sessionId,
287− auth: completed.right.auth,
288− },
289− }),
290− );
291− }
292−
293− const message = completed.left instanceof Error
294− ? completed.left.message
295− : "Failed completing OAuth";
296−
297− return htmlResponse(
298− sourceOAuthPopupResultDocument({
299− title: "OAuth failed",
300− message,
301− status: "failed",
302− sessionId: "failed",
303− payload: {
304− type: "executor:oauth-result",
305− ok: false,
306− sessionId: null,
307− error: message,
308− },
309− }),
310− 500,
311− );
312− }),
313− ),
314− ),
315−);
packages/platform/api/src/sources/api.ts+0−75
@@ -1,13 +1,9 @@
11 import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "@effect/platform";
22 import {
33 CreateSourcePayloadSchema,
4− CreateScopeOauthClientPayloadSchema as CreateWorkspaceOauthClientPayloadSchema,
5− CredentialOauthCompleteUrlParamsSchema,
64 CredentialPageUrlParamsSchema,
75 CredentialSubmitPayloadSchema,
8− DiscoverSourcePayloadSchema,
96 UpdateSourcePayloadSchema,
10− ScopeOauthClientQuerySchema as WorkspaceOauthClientQuerySchema,
117 } from "@executor/platform-sdk/contracts";
128 import {
139 ControlPlaneBadRequestError,
@@ -17,90 +13,35 @@ import { · 3 unmodified lines
1713 ControlPlaneUnauthorizedError,
1814 } from "@executor/platform-sdk/errors";
1915 import {
20− ProviderAuthGrantIdSchema,
21− SourceDiscoveryResultSchema,
2216 SourceIdSchema,
2317 SourceInspectionDiscoverPayloadSchema,
2418 SourceInspectionDiscoverResultSchema,
2519 SourceInspectionSchema,
2620 SourceInspectionToolDetailSchema,
2721 SourceSchema,
28− ScopeOauthClientIdSchema as WorkspaceOauthClientIdSchema,
29− ScopeOauthClientSchema as WorkspaceOauthClientSchema,
3022 ScopeIdSchema as WorkspaceIdSchema,
3123 } from "@executor/platform-sdk/schema";
3224 import * as Schema from "effect/Schema";
3325
3426 export type {
3527 CreateSourcePayload,
36− CreateScopeOauthClientPayload as CreateWorkspaceOauthClientPayload,
37− DiscoverSourcePayload,
3828 UpdateSourcePayload,
3929 } from "@executor/platform-sdk/contracts";
4030
4131 export {
4232 CreateSourcePayloadSchema,
43− CreateWorkspaceOauthClientPayloadSchema,
44− DiscoverSourcePayloadSchema,
4533 UpdateSourcePayloadSchema,
4634 };
4735
4836 const workspaceIdParam = HttpApiSchema.param("workspaceId", WorkspaceIdSchema);
4937 const sourceIdParam = HttpApiSchema.param("sourceId", SourceIdSchema);
5038 const toolPathParam = HttpApiSchema.param("toolPath", Schema.String);
51−const oauthClientIdParam = HttpApiSchema.param(
52− "oauthClientId",
53− WorkspaceOauthClientIdSchema,
54−);
55−const grantIdParam = HttpApiSchema.param("grantId", ProviderAuthGrantIdSchema);
5639
5740 const HtmlSchema = HttpApiSchema.Text({
5841 contentType: "text/html",
5942 });
6043
6144 export class SourcesApi extends HttpApiGroup.make("sources")
62− .add(
63− HttpApiEndpoint.post("discover")`/sources/discover`
64− .setPayload(DiscoverSourcePayloadSchema)
65− .addSuccess(SourceDiscoveryResultSchema)
66− .addError(ControlPlaneBadRequestError)
67− .addError(ControlPlaneUnauthorizedError)
68− .addError(ControlPlaneForbiddenError),
69− )
70− .add(
71− HttpApiEndpoint.get("listWorkspaceOauthClients")`/workspaces/${workspaceIdParam}/oauth-clients`
72− .setUrlParams(WorkspaceOauthClientQuerySchema)
73− .addSuccess(Schema.Array(WorkspaceOauthClientSchema))
74− .addError(ControlPlaneBadRequestError)
75− .addError(ControlPlaneUnauthorizedError)
76− .addError(ControlPlaneForbiddenError)
77− .addError(ControlPlaneStorageError),
78− )
79− .add(
80− HttpApiEndpoint.post("createWorkspaceOauthClient")`/workspaces/${workspaceIdParam}/oauth-clients`
81− .setPayload(CreateWorkspaceOauthClientPayloadSchema)
82− .addSuccess(WorkspaceOauthClientSchema)
83− .addError(ControlPlaneBadRequestError)
84− .addError(ControlPlaneUnauthorizedError)
85− .addError(ControlPlaneForbiddenError)
86− .addError(ControlPlaneStorageError),
87− )
88− .add(
89− HttpApiEndpoint.del("removeWorkspaceOauthClient")`/workspaces/${workspaceIdParam}/oauth-clients/${oauthClientIdParam}`
90− .addSuccess(Schema.Struct({ removed: Schema.Boolean }))
91− .addError(ControlPlaneBadRequestError)
92− .addError(ControlPlaneUnauthorizedError)
93− .addError(ControlPlaneForbiddenError)
94− .addError(ControlPlaneStorageError),
95− )
96− .add(
97− HttpApiEndpoint.del("removeProviderAuthGrant")`/workspaces/${workspaceIdParam}/provider-grants/${grantIdParam}`
98− .addSuccess(Schema.Struct({ removed: Schema.Boolean }))
99− .addError(ControlPlaneBadRequestError)
100− .addError(ControlPlaneUnauthorizedError)
101− .addError(ControlPlaneForbiddenError)
102− .addError(ControlPlaneStorageError),
103− )
10445 .add(
10546 HttpApiEndpoint.get("list")`/workspaces/${workspaceIdParam}/sources`
10647 .addSuccess(Schema.Array(SourceSchema))
@@ -162,22 +103,6 @@ export class SourcesApi extends HttpApiGroup.make("sources") · 55 unmodified lines
162103 .addError(ControlPlaneNotFoundError)
163104 .addError(ControlPlaneStorageError),
164105 )
165− .add(
166− HttpApiEndpoint.get("credentialComplete")`/workspaces/${workspaceIdParam}/sources/${sourceIdParam}/credentials/oauth/complete`
167− .setUrlParams(CredentialOauthCompleteUrlParamsSchema)
168− .addSuccess(HtmlSchema)
169− .addError(ControlPlaneBadRequestError)
170− .addError(ControlPlaneNotFoundError)
171− .addError(ControlPlaneStorageError),
172− )
173− .add(
174− HttpApiEndpoint.get("providerOauthComplete")`/workspaces/${workspaceIdParam}/oauth/provider/callback`
175− .setUrlParams(CredentialOauthCompleteUrlParamsSchema)
176− .addSuccess(HtmlSchema)
177− .addError(ControlPlaneBadRequestError)
178− .addError(ControlPlaneNotFoundError)
179− .addError(ControlPlaneStorageError),
180− )
181106 .add(
182107 HttpApiEndpoint.get("inspection")`/workspaces/${workspaceIdParam}/sources/${sourceIdParam}/inspection`
183108 .addSuccess(SourceInspectionSchema)
packages/platform/api/src/sources/http.ts+0−358
@@ -37,14 +37,6 @@ const escapeHtml = (value: string): string => · 36 unmodified lines
3737 .replaceAll('"', "&quot;")
3838 .replaceAll("'", "&#39;");
3939
40−const serializeJsonForScript = (value: unknown): string =>
41− JSON.stringify(value)
42− .replaceAll("<", "\\u003c")
43− .replaceAll(">", "\\u003e")
44− .replaceAll("&", "\\u0026");
45−
46−const SOURCE_OAUTH_RESULT_STORAGE_KEY_PREFIX = "executor:oauth-result:";
47−
4840 const credentialPageDocument = (input: {
4941 title: string;
5042 eyebrow: string;
@@ -404,133 +396,6 @@ const credentialPageDocument = (input: { · 353 unmodified lines
404396 </body>
405397 </html>`;
406398
407−const sourceOAuthPopupResultDocument = (input: {
408− title: string;
409− message: string;
410− state: "stored" | "cancelled";
411− sessionId?: string | null;
412− payload:
413− | {
414− type: "executor:source-oauth-result";
415− ok: true;
416− sourceId: Source["id"];
417− }
418− | {
419− type: "executor:source-oauth-result";
420− ok: false;
421− error: string;
422− };
423−}) => `<!doctype html>
424−<html lang="en">
425− <head>
426− <meta charset="utf-8" />
427− <meta name="viewport" content="width=device-width, initial-scale=1" />
428− <title>${escapeHtml(input.title)}</title>
429− <style>
430− :root {
431− --bg: #fafaf9;
432− --surface: #ffffff;
433− --text: #1c1917;
434− --text-secondary: #78716c;
435− --border: #e7e5e4;
436− --green: #16a34a;
437− --green-light: #f0fdf4;
438− --green-border: #bbf7d0;
439− --red: #dc2626;
440− --red-light: #fef2f2;
441− --red-border: #fecaca;
442− }
443−
444− * { box-sizing: border-box; margin: 0; padding: 0; }
445−
446− body {
447− min-height: 100vh;
448− display: flex;
449− align-items: center;
450− justify-content: center;
451− background: var(--bg);
452− color: var(--text);
453− font-family: system-ui, -apple-system, sans-serif;
454− padding: 24px;
455− }
456−
457− .card {
458− width: 100%;
459− max-width: 420px;
460− background: var(--surface);
461− border: 1px solid var(--border);
462− border-radius: 16px;
463− padding: 28px;
464− text-align: center;
465− }
466−
467− .status {
468− display: inline-flex;
469− align-items: center;
470− justify-content: center;
471− padding: 6px 12px;
472− border-radius: 999px;
473− font-size: 12px;
474− font-weight: 600;
475− margin-bottom: 14px;
476− }
477−
478− .status[data-state='stored'] {
479− background: var(--green-light);
480− border: 1px solid var(--green-border);
481− color: var(--green);
482− }
483−
484− .status[data-state='cancelled'] {
485− background: var(--red-light);
486− border: 1px solid var(--red-border);
487− color: var(--red);
488− }
489−
490− h1 {
491− font-size: 22px;
492− line-height: 1.2;
493− margin-bottom: 10px;
494− }
495−
496− p {
497− color: var(--text-secondary);
498− font-size: 14px;
499− line-height: 1.6;
500− }
501− </style>
502− </head>
503− <body>
504− <main class="card">
505− <div class="status" data-state="${escapeHtml(input.state)}">${escapeHtml(input.state === "stored" ? "connected" : "failed")}</div>
506− <h1>${escapeHtml(input.title)}</h1>
507− <p>${escapeHtml(input.message)}</p>
508− </main>
509− <script>
510− (() => {
511− const payload = ${serializeJsonForScript(input.payload)};
512− const storageKey = ${serializeJsonForScript(
513− input.sessionId
514− ? `${SOURCE_OAUTH_RESULT_STORAGE_KEY_PREFIX}${input.sessionId}`
515− : null,
516− )};
517− try {
518− if (storageKey) {
519− window.localStorage.setItem(storageKey, JSON.stringify(payload));
520− }
521− } catch {}
522− try {
523− if (window.opener) {
524− window.opener.postMessage(payload, window.location.origin);
525− }
526− } finally {
527− window.setTimeout(() => window.close(), 120);
528− }
529− })();
530− </script>
531− </body>
532−</html>`;
533−
534399 const htmlResponse = (
535400 html: string,
536401 status = 200,
@@ -616,93 +481,6 @@ export const ExecutorSourcesLive = HttpApiBuilder.group( · 79 unmodified lines
616481 "sources",
617482 (handlers) =>
618483 handlers
619− .handle("discover", ({ payload }) =>
620− Effect.flatMap(getControlPlaneExecutor(), (executor) =>
621− executor.sources.discover({
622− url: payload.url,
623− probeAuth: payload.probeAuth,
624− }).pipe(
625− Effect.catchAll((cause) =>
626− Effect.fail(toBadRequestError("sources.discover", cause)),
627− ),
628− )
629− )
630− )
631− .handle("listWorkspaceOauthClients", ({ path, urlParams }) =>
632− resolveRequestedLocalWorkspace(
633− "sources.listWorkspaceOauthClients",
634− path.workspaceId,
635− ).pipe(
636− Effect.flatMap((executor) =>
637− executor.sources.oauthClients.list(urlParams.providerKey).pipe(
638− Effect.catchAll((cause) =>
639− Effect.fail(
640− toBadRequestError("sources.listWorkspaceOauthClients", cause),
641− ),
642− ),
643− )
644− ),
645− ),
646− )
647− .handle("createWorkspaceOauthClient", ({ path, payload }) =>
648− resolveRequestedLocalWorkspace(
649− "sources.createWorkspaceOauthClient",
650− path.workspaceId,
651− ).pipe(
652− Effect.flatMap((executor) =>
653− executor.sources.oauthClients.create(payload).pipe(
654− Effect.catchAll((cause) =>
655− Effect.fail(
656− toBadRequestError(
657− "sources.createWorkspaceOauthClient",
658− cause,
659− ),
660− ),
661− ),
662− )
663− ),
664− ),
665− )
666− .handle("removeWorkspaceOauthClient", ({ path }) =>
667− resolveRequestedLocalWorkspace(
668− "sources.removeWorkspaceOauthClient",
669− path.workspaceId,
670− ).pipe(
671− Effect.flatMap((executor) =>
672− executor.sources.oauthClients.remove(path.oauthClientId).pipe(
673− Effect.map((result) => ({ removed: result })),
674− Effect.catchAll((cause) =>
675− Effect.fail(
676− toBadRequestError(
677− "sources.removeWorkspaceOauthClient",
678− cause,
679− ),
680− ),
681− ),
682− )
683− ),
684− ),
685− )
686− .handle("removeProviderAuthGrant", ({ path }) =>
687− resolveRequestedLocalWorkspace(
688− "sources.removeProviderAuthGrant",
689− path.workspaceId,
690− ).pipe(
691− Effect.flatMap((executor) =>
692− executor.sources.providerGrants.remove(path.grantId).pipe(
693− Effect.map((result) => ({ removed: result })),
694− Effect.catchAll((cause) =>
695− Effect.fail(
696− toBadRequestError(
697− "sources.removeProviderAuthGrant",
698− cause,
699− ),
700− ),
701− ),
702− )
703− ),
704− ),
705− )
706484 .handle("list", ({ path }) =>
707485 resolveRequestedLocalWorkspace("sources.list", path.workspaceId).pipe(
708486 Effect.flatMap((executor) => executor.sources.list()),
@@ -883,140 +661,4 @@ export const ExecutorSourcesLive = HttpApiBuilder.group( · 174 unmodified lines
883661 }),
884662 ),
885663 )
886− .handle("credentialComplete", ({ path, urlParams }) =>
887− resolveRequestedLocalWorkspace(
888− "sources.credentialComplete",
889− path.workspaceId,
890− ).pipe(
891− Effect.flatMap((executor) =>
892− executor.local.credentials.complete({
893− sourceId: path.sourceId,
894− state: urlParams.state,
895− code: urlParams.code,
896− error: urlParams.error,
897− errorDescription: urlParams.error_description,
898− }),
899− ),
900− Effect.map((completed) =>
901− htmlResponse(
902− sourceOAuthPopupResultDocument({
903− title: "Source connected",
904− message: `Source connected: ${completed.source.id}. You can close this window.`,
905− state: "stored",
906− sessionId: completed.sessionId,
907− payload: {
908− type: "executor:source-oauth-result",
909− ok: true,
910− sourceId: completed.source.id,
911− },
912− }),
913− ),
914− ),
915− Effect.catchAll((error) =>
916− Effect.succeed(
917− htmlResponse(
918− sourceOAuthPopupResultDocument({
919− title: "OAuth failed",
920− message:
921− error instanceof Error
922− ? error.message
923− : "Failed completing OAuth",
924− state: "cancelled",
925− payload: {
926− type: "executor:source-oauth-result",
927− ok: false,
928− error:
929− error instanceof Error
930− ? error.message
931− : "Failed completing OAuth",
932− },
933− }),
934− 500,
935− ),
936− ),
937− ),
938− ),
939− )
940− .handle("providerOauthComplete", ({ path, urlParams }) =>
941− resolveRequestedLocalWorkspace(
942− "sources.providerOauthComplete",
943− path.workspaceId,
944− ).pipe(
945− Effect.flatMap((executor) =>
946− Effect.gen(function* () {
947− const completed = yield* executor.oauth.completeProviderCallback({
948− scopeId: path.workspaceId,
949− state: urlParams.state,
950− code: urlParams.code,
951− error: urlParams.error,
952− errorDescription: urlParams.error_description,
953− });
954− const primarySource = completed.sources[0] ?? null;
955−
956− return htmlResponse(
957− sourceOAuthPopupResultDocument({
958− title: "Sources connected",
959− message: `Connected ${completed.sources.length} source${completed.sources.length === 1 ? "" : "s"}. You can close this window.`,
960− state: "stored",
961− sessionId: completed.sessionId,
962− payload: {
963− type: "executor:source-oauth-result",
964− ok: true,
965− sourceId:
966− primarySource?.id ??
967− ("src_oauth_complete" as Source["id"]),
968− },
969− }),
970− );
971− }).pipe(
972− Effect.catchAll((error) =>
973− Effect.succeed(
974− htmlResponse(
975− sourceOAuthPopupResultDocument({
976− title: "OAuth failed",
977− message:
978− error instanceof Error
979− ? error.message
980− : "Failed completing OAuth",
981− state: "cancelled",
982− payload: {
983− type: "executor:source-oauth-result",
984− ok: false,
985− error:
986− error instanceof Error
987− ? error.message
988− : "Failed completing OAuth",
989− },
990− }),
991− 500,
992− ),
993− ),
994− ),
995− ),
996− ),
997− Effect.catchAll((error) =>
998− Effect.succeed(
999− htmlResponse(
1000− sourceOAuthPopupResultDocument({
1001− title: "OAuth failed",
1002− message:
1003− error instanceof Error
1004− ? error.message
1005− : "Failed completing OAuth",
1006− state: "cancelled",
1007− payload: {
1008− type: "executor:source-oauth-result",
1009− ok: false,
1010− error:
1011− error instanceof Error
1012− ? error.message
1013− : "Failed completing OAuth",
1014− },
1015− }),
1016− 500,
1017− ),
1018− ),
1019− ),
1020− ),
1021− ),
1022664 );
packages/platform/internal/src/tools/executor-admin.ts+0−190
@@ -4,7 +4,6 @@ import { · 3 unmodified lines
44 type ScopeId as AccountId,
55 LocalInstallationSchema,
66 LocalScopePolicySchema,
7− SourceDiscoveryResultSchema,
87 SourceIdSchema,
98 SourceInspectionDiscoverPayloadSchema,
109 SourceInspectionDiscoverResultSchema,
@@ -12,7 +11,6 @@ import { · 1 unmodified line
1211 SourceInspectionToolDetailSchema,
1312 SourceSchema,
1413 type ScopeId as WorkspaceId,
15− ScopeOauthClientSchema,
1614 } from "@executor/platform-sdk/schema";
1715 import * as Effect from "effect/Effect";
1816 import * as Layer from "effect/Layer";
@@ -59,7 +57,6 @@ import { · 40 unmodified lines
5957 createLocalSecret,
6058 createPolicy,
6159 deleteLocalSecret,
62− discoverSource,
6360 discoverSourceInspectionTools,
6461 getPolicy,
6562 getSource,
@@ -81,9 +78,6 @@ import { · 15 unmodified lines
8178 UpdatePolicyPayloadSchema,
8279 } from "@executor/platform-sdk/contracts";
8380 import {
84− CreateScopeOauthClientPayloadSchema,
85− DiscoverSourcePayloadSchema,
86− type CreateScopeOauthClientPayload as CreateWorkspaceOauthClientPayload,
8781 UpdateSourcePayloadSchema,
8882 } from "@executor/platform-sdk/contracts";
8983
@@ -129,9 +123,6 @@ const updateSourceInputSchema = Schema.standardSchemaV1( · 39 unmodified lines
129123 payload: UpdateSourcePayloadSchema,
130124 }),
131125 );
132−const discoverSourceInputSchema = Schema.standardSchemaV1(
133− DiscoverSourcePayloadSchema,
134−);
135126 const inspectToolInputSchema = Schema.standardSchemaV1(
136127 Schema.Struct({
137128 sourceId: SourceIdSchema,
@@ -161,27 +152,6 @@ const updatePolicyInputSchema = Schema.standardSchemaV1( · 23 unmodified lines
161152 payload: UpdatePolicyPayloadSchema,
162153 }),
163154 );
164−const workspaceOauthClientListInputSchema = Schema.standardSchemaV1(
165− Schema.Struct({
166− providerKey: Schema.String,
167− }),
168−);
169−const workspaceOauthClientListOutputSchema = Schema.standardSchemaV1(
170− Schema.Array(ScopeOauthClientSchema),
171−);
172−const createWorkspaceOauthClientInputSchema = Schema.standardSchemaV1(
173− CreateScopeOauthClientPayloadSchema,
174−);
175−const removeWorkspaceOauthClientInputSchema = Schema.standardSchemaV1(
176− Schema.Struct({
177− oauthClientId: Schema.String,
178− }),
179−);
180−const removeProviderGrantInputSchema = Schema.standardSchemaV1(
181− Schema.Struct({
182− grantId: Schema.String,
183− }),
184−);
185155 const sourceOutputSchema = Schema.standardSchemaV1(SourceSchema);
186156 const sourceInspectionOutputSchema = Schema.standardSchemaV1(
187157 SourceInspectionSchema,
@@ -192,9 +162,6 @@ const sourceInspectionToolOutputSchema = Schema.standardSchemaV1( · 4 unmodified lines
192162 const sourceInspectionDiscoverOutputSchema = Schema.standardSchemaV1(
193163 SourceInspectionDiscoverResultSchema,
194164 );
195−const sourceDiscoveryOutputSchema = Schema.standardSchemaV1(
196− SourceDiscoveryResultSchema,
197−);
198165 const localScopePolicyOutputSchema = Schema.standardSchemaV1(
199166 LocalScopePolicySchema,
200167 );
@@ -384,22 +351,6 @@ export const createWorkspaceExecutorAdminToolMap = ( · 183 unmodified lines
384351 },
385352 metadata,
386353 }),
387− "executor.sources.discover": toTool({
388− tool: {
389− description:
390− "Probe a URL and infer whether it looks like MCP, OpenAPI, GraphQL, or another supported source.",
391− inputSchema: discoverSourceInputSchema,
392− outputSchema: sourceDiscoveryOutputSchema,
393− execute: (payload: { url: string; probeAuth?: unknown }) =>
394− Effect.runPromise(
395− discoverSource({
396− url: payload.url,
397− probeAuth: payload.probeAuth as never,
398− }),
399− ),
400− },
401− metadata,
402− }),
403354 "executor.sources.list": toTool({
404355 tool: {
405356 description: "List sources connected in the current workspace.",
@@ -539,82 +490,6 @@ export const createWorkspaceExecutorAdminToolMap = ( · 133 unmodified lines
539490 },
540491 metadata,
541492 }),
542− "executor.sources.oauthClients.list": toTool({
543− tool: {
544− description: "List workspace OAuth clients for a provider key.",
545− inputSchema: workspaceOauthClientListInputSchema,
546− outputSchema: workspaceOauthClientListOutputSchema,
547− execute: ({ providerKey }: { providerKey: string }) =>
548− runScopeStorageEffect({
549− effect: input.sourceAuthService.listScopeOauthClients({
550− scopeId: input.scopeId,
551− providerKey,
552− }),
553− scopeStorageLayer,
554− runtimeLocalScope: input.runtimeLocalScope,
555− }),
556− },
557− metadata,
558− }),
559− "executor.sources.oauthClients.create": toTool({
560− tool: {
561− description:
562− "Create a workspace OAuth client used for shared provider auth flows.",
563− inputSchema: createWorkspaceOauthClientInputSchema,
564− outputSchema: Schema.standardSchemaV1(ScopeOauthClientSchema),
565− execute: (payload: CreateWorkspaceOauthClientPayload) =>
566− runScopeStorageEffect({
567− effect: input.sourceAuthService.createScopeOauthClient({
568− scopeId: input.scopeId,
569− providerKey: payload.providerKey,
570− label: payload.label,
571− oauthClient: payload.oauthClient,
572− }),
573− scopeStorageLayer,
574− runtimeLocalScope: input.runtimeLocalScope,
575− }),
576− },
577− metadata,
578− }),
579− "executor.sources.oauthClients.remove": toTool({
580− tool: {
581− description: "Remove a workspace OAuth client.",
582− inputSchema: removeWorkspaceOauthClientInputSchema,
583− outputSchema: removeResultSchema,
584− execute: ({ oauthClientId }: { oauthClientId: string }) =>
585− runScopeStorageEffect({
586− effect: input.sourceAuthService
587− .removeScopeOauthClient({
588− scopeId: input.scopeId,
589− oauthClientId: oauthClientId as never,
590− })
591− .pipe(Effect.map((removed) => ({ removed }))),
592− scopeStorageLayer,
593− runtimeLocalScope: input.runtimeLocalScope,
594− }),
595− },
596− metadata,
597− }),
598− "executor.sources.providerGrants.remove": toTool({
599− tool: {
600− description:
601− "Revoke a shared provider grant reference from the local workspace.",
602− inputSchema: removeProviderGrantInputSchema,
603− outputSchema: removeResultSchema,
604− execute: ({ grantId }: { grantId: string }) =>
605− runScopeStorageEffect({
606− effect: input.sourceAuthService
607− .removeProviderAuthGrant({
608− scopeId: input.scopeId,
609− grantId: grantId as never,
610− })
611− .pipe(Effect.map((removed) => ({ removed }))),
612− scopeStorageLayer,
613− runtimeLocalScope: input.runtimeLocalScope,
614− }),
615− },
616− metadata,
617− }),
618493 "executor.policies.list": toTool({
619494 tool: {
620495 description: "List local workspace policies.",
@@ -780,20 +655,6 @@ export const createExecutorAdminToolMap = (input: { · 159 unmodified lines
780655 },
781656 metadata,
782657 }),
783− "executor.sources.discover": toTool({
784− tool: {
785− description:
786− "Probe a URL and infer whether it looks like MCP, OpenAPI, GraphQL, or another supported source.",
787− inputSchema: discoverSourceInputSchema,
788− outputSchema: sourceDiscoveryOutputSchema,
789− execute: (payload: { url: string; probeAuth?: unknown }) =>
790− input.executor.sources.discover({
791− url: payload.url,
792− probeAuth: payload.probeAuth as never,
793− }),
794− },
795− metadata,
796− }),
797658 "executor.sources.list": toTool({
798659 tool: {
799660 description: "List sources connected in the current workspace.",
@@ -888,57 +749,6 @@ export const createExecutorAdminToolMap = (input: { · 88 unmodified lines
888749 },
889750 metadata,
890751 }),
891− "executor.sources.oauthClients.list": toTool({
892− tool: {
893− description: "List stored workspace OAuth clients for one provider.",
894− inputSchema: workspaceOauthClientListInputSchema,
895− outputSchema: workspaceOauthClientListOutputSchema,
896− execute: ({ providerKey }: { providerKey: string }) =>
897− input.executor.sources.oauthClients.list(providerKey),
898− },
899− metadata,
900− }),
901− "executor.sources.oauthClients.create": toTool({
902− tool: {
903− description:
904− "Create a workspace OAuth client for a provider-backed source flow.",
905− inputSchema: createWorkspaceOauthClientInputSchema,
906− outputSchema: Schema.standardSchemaV1(ScopeOauthClientSchema),
907− execute: (payload: CreateWorkspaceOauthClientPayload) =>
908− input.executor.sources.oauthClients.create(payload),
909− },
910− metadata,
911− }),
912− "executor.sources.oauthClients.remove": toTool({
913− tool: {
914− description: "Remove a stored workspace OAuth client.",
915− inputSchema: removeWorkspaceOauthClientInputSchema,
916− outputSchema: removeResultSchema,
917− execute: async ({
918− oauthClientId,
919− }: {
920− oauthClientId: string;
921− }) => ({
922− removed: await input.executor.sources.oauthClients.remove(
923− oauthClientId as never,
924− ),
925− }),
926− },
927− metadata,
928− }),
929− "executor.sources.providerGrants.remove": toTool({
930− tool: {
931− description: "Remove one stored provider auth grant.",
932− inputSchema: removeProviderGrantInputSchema,
933− outputSchema: removeResultSchema,
934− execute: async ({ grantId }: { grantId: string }) => ({
935− removed: await input.executor.sources.providerGrants.remove(
936− grantId as never,
937− ),
938− }),
939− },
940− metadata,
941− }),
942752 "executor.policies.list": toTool({
943753 tool: {
944754 description: "List local workspace policies.",
packages/platform/sdk/src/executor-effect.ts+0−114
@@ -7,8 +7,6 @@ import type { · 6 unmodified lines
77 ExecutionInteraction,
88 LocalInstallation,
99 LocalScopePolicy,
10− ProviderAuthGrant,
11− ScopeOauthClient,
1210 Source,
1311 } from "./schema";
1412 import { ExecutionIdSchema } from "./schema";
@@ -50,11 +48,9 @@ import { · 35 unmodified lines
5048 updatePolicy,
5149 } from "./policies/operations";
5250 import type {
53− CreateScopeOauthClientPayload,
5451 CreateSourcePayload,
5552 UpdateSourcePayload,
5653 } from "./sources/contracts";
57−import { discoverSource } from "./sources/discovery";
5854 import {
5955 discoverSourceInspectionTools,
6056 getSourceInspection,
@@ -83,13 +79,9 @@ import { · 22 unmodified lines
8379 resumeExecution,
8480 } from "./runtime/execution/service";
8581 import type {
86− CompleteProviderOauthCallbackResult,
8782 CompleteSourceCredentialSetupResult,
88− CompleteSourceOAuthSessionResult,
8983 ExecutorAddSourceInput,
9084 ExecutorSourceAddResult,
91− StartSourceOAuthSessionInput,
92− StartSourceOAuthSessionResult,
9385 } from "./runtime/sources/source-auth-service";
9486
9587 type DistributiveOmit<T, Keys extends PropertyKey> = T extends unknown
@@ -110,11 +102,6 @@ export type ExecutorSourceInput = DistributiveOmit< · 14 unmodified lines
110102 "scopeId" | "actorScopeId" | "executionId" | "interactionId"
111103 >;
112104
113−export type ExecutorSourceOAuthInput = DistributiveOmit<
114− StartSourceOAuthSessionInput,
115− "scopeId" | "actorScopeId"
116−>;
117−
118105 export type ExecutorEffect = {
119106 runtime: ExecutorRuntime;
120107 installation: LocalInstallation;
@@ -187,10 +174,6 @@ export type ExecutorEffect = { · 66 unmodified lines
187174 baseUrl?: string | null;
188175 },
189176 ) => Effect.Effect<ExecutorSourceAddResult, Error, never>;
190− discover: (input: {
191− url: string;
192− probeAuth?: Parameters<typeof discoverSource>[0]["probeAuth"];
193− }) => ProvidedEffect<ReturnType<typeof discoverSource>>;
194177 list: () => ProvidedEffect<ReturnType<typeof listSources>>;
195178 create: (
196179 payload: CreateSourcePayload,
@@ -218,41 +201,6 @@ export type ExecutorEffect = { · 21 unmodified lines
218201 ReturnType<typeof discoverSourceInspectionTools>
219202 >;
220203 };
221− oauthClients: {
222− list: (
223− providerKey: string,
224− ) => Effect.Effect<ReadonlyArray<ScopeOauthClient>, Error, never>;
225− create: (
226− payload: CreateScopeOauthClientPayload,
227− ) => Effect.Effect<ScopeOauthClient, Error, never>;
228− remove: (
229− oauthClientId: ScopeOauthClient["id"],
230− ) => Effect.Effect<boolean, Error, never>;
231− };
232− providerGrants: {
233− remove: (
234− grantId: ProviderAuthGrant["id"],
235− ) => Effect.Effect<boolean, Error, never>;
236− };
237− };
238− oauth: {
239− startSourceAuth: (
240− input: ExecutorSourceOAuthInput,
241− ) => Effect.Effect<StartSourceOAuthSessionResult, Error, never>;
242− completeSourceAuth: (input: {
243− state: string;
244− code?: string | null;
245− error?: string | null;
246− errorDescription?: string | null;
247− }) => Effect.Effect<CompleteSourceOAuthSessionResult, Error, never>;
248− completeProviderCallback: (input: {
249− scopeId?: ScopeId;
250− actorScopeId?: ScopeId | null;
251− state: string;
252− code?: string | null;
253− error?: string | null;
254− errorDescription?: string | null;
255− }) => Effect.Effect<CompleteProviderOauthCallbackResult, Error, never>;
256204 };
257205 executions: {
258206 create: (
@@ -366,7 +314,6 @@ const fromRuntime = (runtime: ExecutorRuntime): ExecutorEffect => { · 107 unmodified lines
366314 options,
367315 );
368316 }),
369− discover: (input) => provide(discoverSource(input)),
370317 list: () => provide(listSources({ scopeId, actorScopeId })),
371318 create: (payload) => provide(createSource({ scopeId, actorScopeId, payload })),
372319 get: (sourceId) => provide(getSource({ scopeId, sourceId, actorScopeId })),
@@ -395,67 +342,6 @@ const fromRuntime = (runtime: ExecutorRuntime): ExecutorEffect => { · 22 unmodified lines
395342 }),
396343 ),
397344 },
398− oauthClients: {
399− list: (providerKey) =>
400− provideSourceAuth((service) =>
401− service.listScopeOauthClients({
402− scopeId,
403− providerKey,
404− }),
405− ),
406− create: (payload) =>
407− provideSourceAuth((service) =>
408− service.createScopeOauthClient({
409− scopeId,
410− providerKey: payload.providerKey,
411− label: payload.label,
412− oauthClient: payload.oauthClient,
413− }),
414− ),
415− remove: (oauthClientId) =>
416− provideSourceAuth((service) =>
417− service.removeScopeOauthClient({
418− scopeId,
419− oauthClientId,
420− }),
421− ),
422− },
423− providerGrants: {
424− remove: (grantId) =>
425− provideSourceAuth((service) =>
426− service.removeProviderAuthGrant({
427− scopeId,
428− grantId,
429− }),
430− ),
431− },
432− },
433− oauth: {
434− startSourceAuth: (input) =>
435− provideSourceAuth((service) =>
436− service.startSourceOAuthSession({
437− ...input,
438− scopeId,
439− actorScopeId,
440− }),
441− ),
442− completeSourceAuth: ({ state, code, error, errorDescription }) =>
443− provideSourceAuth((service) =>
444− service.completeSourceOAuthSession({
445− state,
446− code,
447− error,
448− errorDescription,
449− }),
450− ),
451− completeProviderCallback: (input) =>
452− provideSourceAuth((service) =>
453− service.completeProviderOauthCallback({
454− ...input,
455− scopeId: input.scopeId ?? scopeId,
456− actorScopeId: input.actorScopeId ?? actorScopeId,
457− }),
458− ),
459345 },
460346 executions: {
461347 create: (payload) =>
packages/platform/sdk/src/executor.ts+0−21
@@ -5,7 +5,6 @@ import { · 4 unmodified lines
55 type CreateExecutorEffectOptions,
66 type ExecutorEffect,
77 type ExecutorSourceInput,
8− type ExecutorSourceOAuthInput,
98 } from "./executor-effect";
109
1110 type Promiseify<T> = T extends Effect.Effect<infer A, any, any>
@@ -22,7 +21,6 @@ export type Executor = Omit<Promiseify<ExecutorEffect>, "runtime">; · 10 unmodified lines
2221 export type CreateExecutorOptions = CreateExecutorEffectOptions;
2322 export type {
2423 ExecutorSourceInput,
25− ExecutorSourceOAuthInput,
2624 };
2725
2826 const toPromiseExecutor = (executor: ExecutorEffect): Executor => {
@@ -60,7 +58,6 @@ const toPromiseExecutor = (executor: ExecutorEffect): Executor => { · 31 unmodified lines
6058 },
6159 sources: {
6260 add: (input, options) => run(executor.sources.add(input, options)),
63− discover: (input) => run(executor.sources.discover(input)),
6461 list: () => run(executor.sources.list()),
6562 create: (payload) => run(executor.sources.create(payload)),
6663 get: (sourceId) => run(executor.sources.get(sourceId)),
@@ -72,24 +69,6 @@ const toPromiseExecutor = (executor: ExecutorEffect): Executor => { · 5 unmodified lines
7269 tool: (input) => run(executor.sources.inspection.tool(input)),
7370 discover: (input) => run(executor.sources.inspection.discover(input)),
7471 },
75− oauthClients: {
76− list: (providerKey) =>
77− run(executor.sources.oauthClients.list(providerKey)),
78− create: (payload) => run(executor.sources.oauthClients.create(payload)),
79− remove: (oauthClientId) =>
80− run(executor.sources.oauthClients.remove(oauthClientId)),
81− },
82− providerGrants: {
83− remove: (grantId) =>
84− run(executor.sources.providerGrants.remove(grantId)),
85− },
86− },
87− oauth: {
88− startSourceAuth: (input) => run(executor.oauth.startSourceAuth(input)),
89− completeSourceAuth: (input) =>
90− run(executor.oauth.completeSourceAuth(input)),
91− completeProviderCallback: (input) =>
92− run(executor.oauth.completeProviderCallback(input)),
9372 },
9473 executions: {
9574 create: (payload) => run(executor.executions.create(payload)),
packages/platform/sdk/src/operations.ts+0−1
@@ -1,6 +1,5 @@
11 export * from "./local/operations";
22 export * from "./local/secrets";
33 export * from "./policies/operations";
4−export * from "./sources/discovery";
54 export * from "./sources/inspection";
65 export * from "./sources/operations";
packages/platform/sdk/src/runtime/index.ts+0−1
@@ -111,7 +111,6 @@ export * from "./sources/source-store"; · 110 unmodified lines
111111 export * from "./executor-state-store";
112112 export * from "./execution/scope/environment";
113113 export * from "../sources/inspection";
114−export * from "../sources/discovery";
115114 export * from "./execution/service";
116115 export type {
117116 CreateScopeInternalToolMap,
packages/platform/sdk/src/runtime/sources/source-auth-service.ts+0−80
@@ -3,14 +3,11 @@ import type { · 2 unmodified lines
33 SecretRef,
44 } from "@executor/source-core";
55 import {
6− type ProviderAuthGrant,
76 type ScopeId,
8− type ScopeOauthClient,
97 type Source,
108 type SourceAuthSession,
119 SourceAuthSessionIdSchema,
1210 SourceSchema,
13− type SourceOauthClientInput,
1411 } from "#schema";
1512 import * as Context from "effect/Context";
1613 import * as Effect from "effect/Effect";
@@ -92,46 +89,11 @@ export type ExecutorAddSourceInput = RegisteredExecutorAddSourceInput & { · 75 unmodified lines
9289 interactionId: SourceAuthSession["interactionId"];
9390 };
9491
95−export type CreateScopeOauthClientInput = {
96− scopeId: ScopeId;
97− providerKey: string;
98− label?: string | null;
99− oauthClient: SourceOauthClientInput;
100−};
101−
102−export type SourceOAuthProviderInput = {
103− providerKey: string;
104− config?: Record<string, unknown> | null;
105−};
106−
107−export type StartSourceOAuthSessionInput = {
108− scopeId: ScopeId;
109− actorScopeId?: ScopeId | null;
110− provider: SourceOAuthProviderInput;
111− baseUrl?: string | null;
112− displayName?: string | null;
113−};
114−
115−export type StartSourceOAuthSessionResult = {
116− sessionId: SourceAuthSession["id"];
117− authorizationUrl: string;
118−};
119−
120−export type CompleteSourceOAuthSessionResult = {
121− sessionId: SourceAuthSession["id"];
122− auth: Extract<Source["auth"], { kind: "oauth2" }>;
123−};
124−
12592 export type CompleteSourceCredentialSetupResult = {
12693 sessionId: SourceAuthSession["id"];
12794 source: Source;
12895 };
12996
130−export type CompleteProviderOauthCallbackResult = {
131− sessionId: SourceAuthSession["id"];
132− sources: ReadonlyArray<Source>;
133−};
134−
13597 type RuntimeSourceAuthServiceShape = {
13698 getSourceById: (input: {
13799 scopeId: ScopeId;
@@ -150,38 +112,6 @@ type RuntimeSourceAuthServiceShape = { · 12 unmodified lines
150112 baseUrl?: string | null;
151113 },
152114 ) => Effect.Effect<ExecutorSourceAddResult, Error, ScopeStorageServices>;
153− listScopeOauthClients: (input: {
154− scopeId: ScopeId;
155− providerKey: string;
156− }) => Effect.Effect<readonly ScopeOauthClient[], Error, ScopeStorageServices>;
157− createScopeOauthClient: (
158− input: CreateScopeOauthClientInput,
159− ) => Effect.Effect<ScopeOauthClient, Error, ScopeStorageServices>;
160− removeScopeOauthClient: (input: {
161− scopeId: ScopeId;
162− oauthClientId: ScopeOauthClient["id"];
163− }) => Effect.Effect<boolean, Error, ScopeStorageServices>;
164− removeProviderAuthGrant: (input: {
165− scopeId: ScopeId;
166− grantId: ProviderAuthGrant["id"];
167− }) => Effect.Effect<boolean, Error, ScopeStorageServices>;
168− startSourceOAuthSession: (
169− input: StartSourceOAuthSessionInput,
170− ) => Effect.Effect<StartSourceOAuthSessionResult, Error, never>;
171− completeSourceOAuthSession: (input: {
172− state: string;
173− code?: string | null;
174− error?: string | null;
175− errorDescription?: string | null;
176− }) => Effect.Effect<CompleteSourceOAuthSessionResult, Error, ScopeStorageServices>;
177− completeProviderOauthCallback: (input: {
178− scopeId: ScopeId;
179− actorScopeId?: ScopeId | null;
180− state: string;
181− code?: string | null;
182− error?: string | null;
183− errorDescription?: string | null;
184− }) => Effect.Effect<CompleteProviderOauthCallbackResult, Error, ScopeStorageServices>;
185115 completeSourceCredentialSetup: (input: {
186116 scopeId: ScopeId;
187117 sourceId: Source["id"];
@@ -228,16 +158,6 @@ export const createRuntimeSourceAuthService = ( · 40 unmodified lines
228158 }),
229159 addExecutorSource: () =>
230160 provideLocalWorkspace(disabledSourcePlugins("addExecutorSource")),
231− listScopeOauthClients: () => provideLocalWorkspace(Effect.succeed([])),
232− createScopeOauthClient: () =>
233− provideLocalWorkspace(disabledSourcePlugins("createScopeOauthClient")),
234− removeScopeOauthClient: () => provideLocalWorkspace(Effect.succeed(false)),
235− removeProviderAuthGrant: () => provideLocalWorkspace(Effect.succeed(false)),
236− startSourceOAuthSession: () => disabledSourcePlugins("startSourceOAuthSession"),
237− completeSourceOAuthSession: () =>
238− provideLocalWorkspace(disabledSourcePlugins("completeSourceOAuthSession")),
239− completeProviderOauthCallback: () =>
240− provideLocalWorkspace(disabledSourcePlugins("completeProviderOauthCallback")),
241161 completeSourceCredentialSetup: () =>
242162 provideLocalWorkspace(disabledSourcePlugins("completeSourceCredentialSetup")),
243163 };
packages/platform/sdk/src/runtime/sources/source-discovery.test.ts+0−278
@@ -1,278 +0,0 @@
1−import {
2− createServer,
3− type IncomingMessage,
4− type ServerResponse,
5−} from "node:http";
6−
7−import {
8− describe,
9− expect,
10− it,
11−} from "@effect/vitest";
12−import * as Effect from "effect/Effect";
13−
14−import {
15− discoverSource,
16−} from "../../sources/discovery";
17−
18−type TestServer = {
19− url: string;
20−};
21−
22−const toError = (cause: unknown): Error =>
23− cause instanceof Error ? cause : new Error(String(cause));
24−
25−const makeServer = (
26− handler: (input: {
27− request: IncomingMessage;
28− response: ServerResponse<IncomingMessage>;
29− url: string;
30− }) => void,
31−) =>
32− Effect.acquireRelease(
33− Effect.tryPromise({
34− try: () =>
35− new Promise<{
36− server: ReturnType<typeof createServer>;
37− sockets: Set<import("node:net").Socket>;
38− url: string;
39− }>((resolve, reject) => {
40− let serverUrl = "";
41− const server = createServer((request, response) =>
42− handler({
43− request,
44− response,
45− url: serverUrl,
46− }),
47− );
48− const sockets = new Set<import("node:net").Socket>();
49−
50− server.on("connection", (socket) => {
51− sockets.add(socket);
52− socket.on("close", () => {
53− sockets.delete(socket);
54− });
55− });
56−
57− server.once("error", reject);
58− server.listen(0, "127.0.0.1", () => {
59− const address = server.address();
60− if (!address || typeof address === "string") {
61− reject(new Error("Failed to resolve test server address"));
62− return;
63− }
64−
65− serverUrl = `http://127.0.0.1:${address.port}`;
66− resolve({
67− server,
68− sockets,
69− url: serverUrl,
70− });
71− });
72− }),
73− catch: toError,
74− }),
75− ({ server, sockets }) =>
76− Effect.tryPromise({
77− try: () =>
78− new Promise<void>((resolve, reject) => {
79− server.closeAllConnections?.();
80− sockets.forEach((socket) => socket.destroy());
81− server.close((error) => {
82− if (error) {
83− reject(error);
84− return;
85− }
86− resolve();
87− });
88− }),
89− catch: toError,
90− }),
91− ).pipe(Effect.map(({ url }) => ({ url }) satisfies TestServer));
92−
93−describe("source-discovery", () => {
94− it.scoped(
95− "detects OpenAPI and infers bearer auth from security schemes",
96− () =>
97− Effect.gen(function* () {
98− const server = yield* makeServer(({ request, response, url }) => {
99− if (request.url !== "/openapi.json") {
100− response.statusCode = 404;
101− response.end();
102− return;
103− }
104−
105− if (request.headers.authorization !== "Bearer top-secret") {
106− response.statusCode = 401;
107− response.setHeader("www-authenticate", 'Bearer realm="spec"');
108− response.end("Unauthorized");
109− return;
110− }
111−
112− response.statusCode = 200;
113− response.setHeader("content-type", "application/json");
114− response.end(
115− JSON.stringify({
116− openapi: "3.0.3",
117− info: {
118− title: "Secure Example API",
119− version: "1.0.0",
120− },
121− servers: [{ url: `${url}/api` }],
122− components: {
123− securitySchemes: {
124− bearerAuth: {
125− type: "http",
126− scheme: "bearer",
127− },
128− },
129− },
130− security: [{ bearerAuth: [] }],
131− paths: {
132− "/widgets": {
133− get: {
134− operationId: "widgets/list",
135− responses: {
136− 200: {
137− description: "ok",
138− },
139− },
140− },
141− },
142− },
143− }),
144− );
145− });
146−
147− const result = yield* discoverSource({
148− url: `${server.url}/openapi.json`,
149− probeAuth: {
150− kind: "bearer",
151− token: "top-secret",
152− },
153− });
154−
155− expect(result.detectedKind).toBe("openapi");
156− expect(result.authInference.suggestedKind).toBe("bearer");
157− expect(result.authInference.supported).toBe(true);
158− expect(result.authInference.headerName).toBe("Authorization");
159− expect(result.specUrl).toBe(`${server.url}/openapi.json`);
160− expect(result.endpoint).toBe(`${server.url}/api`);
161− expect(result.toolCount).toBe(1);
162− }),
163− );
164−
165− it.scoped("detects GraphQL from successful introspection", () =>
166− Effect.gen(function* () {
167− const server = yield* makeServer(({ request, response }) => {
168− if (request.url !== "/graphql" || request.method !== "POST") {
169− response.statusCode = 404;
170− response.end();
171− return;
172− }
173−
174− response.statusCode = 200;
175− response.setHeader("content-type", "application/json");
176− response.end(
177− JSON.stringify({
178− data: {
179− __schema: {
180− queryType: {
181− name: "Query",
182− },
183− },
184− },
185− }),
186− );
187− });
188−
189− const result = yield* discoverSource({
190− url: `${server.url}/graphql`,
191− });
192−
193− expect(result.detectedKind).toBe("graphql");
194− expect(result.confidence).toBe("high");
195− expect(result.authInference.suggestedKind).toBe("none");
196− expect(result.specUrl).toBeNull();
197− }),
198− );
199−
200− it.scoped("detects Google Discovery documents", () =>
201− Effect.gen(function* () {
202− const server = yield* makeServer(({ request, response, url }) => {
203− if (request.url !== "/gmail/$discovery/rest?version=v1") {
204− response.statusCode = 404;
205− response.end();
206− return;
207− }
208−
209− response.statusCode = 200;
210− response.setHeader("content-type", "application/json");
211− response.end(
212− JSON.stringify({
213− name: "gmail",
214− version: "v1",
215− title: "Gmail API",
216− description: "Access Gmail mailboxes and settings.",
217− rootUrl: `${url}/`,
218− servicePath: "gmail/v1/users/",
219− auth: {
220− oauth2: {
221− scopes: {
222− "https://www.googleapis.com/auth/gmail.readonly": {
223− description: "View your email messages and settings",
224− },
225− },
226− },
227− },
228− methods: {
229− getProfile: {
230− id: "gmail.users.getProfile",
231− path: "{userId}/profile",
232− httpMethod: "GET",
233− parameters: {
234− userId: {
235− type: "string",
236− required: true,
237− location: "path",
238− },
239− },
240− response: {
241− $ref: "Profile",
242− },
243− },
244− },
245− schemas: {
246− Profile: {
247− id: "Profile",
248− type: "object",
249− properties: {
250− emailAddress: {
251− type: "string",
252− },
253− },
254− },
255− },
256− }),
257− );
258− });
259−
260− const result = yield* discoverSource({
261− url: `${server.url}/gmail/$discovery/rest?version=v1`,
262− });
263−
264− expect(result.detectedKind).toBe("google_discovery");
265− expect(result.specUrl).toBe(
266− `${server.url}/gmail/$discovery/rest?version=v1`,
267− );
268− expect(result.endpoint).toBe(`${server.url}/gmail/v1/users/`);
269− expect(result.name).toBe("Gmail API");
270− expect(result.namespace).toBe("gmail");
271− expect(result.authInference.suggestedKind).toBe("oauth2");
272− expect(result.authInference.oauthScopes).toEqual([
273− "https://www.googleapis.com/auth/gmail.readonly",
274− ]);
275− expect(result.toolCount).toBe(1);
276− }),
277− );
278−});
packages/platform/sdk/src/schema/enums.ts+0−16
@@ -23,22 +23,6 @@ export { · 22 unmodified lines
2323 type SourceCatalogKind,
2424 type SourceCatalogVisibility,
2525 } from "./models/source-catalog";
26−export {
27− SourceAuthInferenceSchema,
28− SourceDiscoveryAuthKindSchema,
29− SourceDiscoveryAuthParameterLocationSchema,
30− SourceDiscoveryConfidenceSchema,
31− SourceDiscoveryKindSchema,
32− SourceDiscoveryResultSchema,
33− SourceProbeAuthSchema,
34− type SourceAuthInference,
35− type SourceDiscoveryAuthKind,
36− type SourceDiscoveryAuthParameterLocation,
37− type SourceDiscoveryConfidence,
38− type SourceDiscoveryKind,
39− type SourceDiscoveryResult,
40− type SourceProbeAuth,
41−} from "./models/source-discovery";
4226 export {
4327 AuthArtifactKindSchema,
4428 AuthArtifactSlotSchema,
packages/platform/sdk/src/schema/index.ts+0−1
@@ -16,5 +16,4 @@ export * from "./models/local-installation"; · 15 unmodified lines
1616 export * from "./models/local-config";
1717 export * from "./models/execution";
1818 export * from "./models/source-inspection";
19−export * from "./models/source-discovery";
2019 export * from "./models/code-migration";
packages/platform/sdk/src/schema/models/source-discovery.ts+0−16
@@ -1,16 +0,0 @@
1−export {
2− SourceAuthInferenceSchema,
3− SourceDiscoveryAuthKindSchema,
4− SourceDiscoveryAuthParameterLocationSchema,
5− SourceDiscoveryConfidenceSchema,
6− SourceDiscoveryKindSchema,
7− SourceDiscoveryResultSchema,
8− SourceProbeAuthSchema,
9− type SourceAuthInference,
10− type SourceDiscoveryAuthKind,
11− type SourceDiscoveryAuthParameterLocation,
12− type SourceDiscoveryConfidence,
13− type SourceDiscoveryKind,
14− type SourceDiscoveryResult,
15− type SourceProbeAuth,
16−} from "@executor/source-core";
packages/platform/sdk/src/sources/contracts.ts+0−10
@@ -3,10 +3,8 @@ import { · 2 unmodified lines
33 JsonObjectSchema,
44 ProviderAuthGrantIdSchema,
55 SourceAuthSchema,
6− SourceDiscoveryResultSchema,
76 SourceImportAuthPolicySchema,
87 SourceKindSchema,
9− SourceProbeAuthSchema,
108 SourceSchema,
119 SourceStatusSchema,
1210 SourceOauthClientInputSchema,
@@ -93,15 +91,7 @@ export type CreateScopeOauthClientPayload = · 80 unmodified lines
9391 export const oauthClientIdParam = ScopeOauthClientIdSchema;
9492 export const grantIdParam = ProviderAuthGrantIdSchema;
9593
96−export const DiscoverSourcePayloadSchema = Schema.Struct({
97− url: TrimmedNonEmptyStringSchema,
98− probeAuth: Schema.optional(SourceProbeAuthSchema),
99−});
100−
101−export type DiscoverSourcePayload = typeof DiscoverSourcePayloadSchema.Type;
102−
10394 export {
104− SourceDiscoveryResultSchema,
10595 ScopeIdSchema,
10696 ScopeOauthClientSchema,
10797 };
packages/platform/sdk/src/sources/discovery.ts+0−56
@@ -1,56 +0,0 @@
1−import * as Effect from "effect/Effect";
2−
3−import {
4− fallbackSourceDiscoveryResult,
5− normalizeSourceDiscoveryUrl,
6− probeHeadersFromAuth,
7− type SourceAdapter,
8− type SourceProbeAuth,
9− type SourceDiscoveryResult,
10−} from "@executor/source-core";
11−
12−import {
13− builtInSourceAdapters,
14−} from "../runtime/sources/source-adapters";
15−
16−type DiscoverableSourceAdapter = SourceAdapter & {
17− detectSource: NonNullable<SourceAdapter["detectSource"]>;
18−};
19−
20−const sourceAdapters: ReadonlyArray<SourceAdapter> = builtInSourceAdapters;
21−
22−const discoverableSourceAdapters = sourceAdapters.filter(
23− (adapter): adapter is DiscoverableSourceAdapter =>
24− adapter.detectSource !== undefined,
25−);
26−
27−export const discoverSource = (input: {
28− url: string;
29− probeAuth?: SourceProbeAuth | null;
30−}): Effect.Effect<SourceDiscoveryResult, Error, never> =>
31− Effect.gen(function* () {
32− const normalizedUrl = yield* Effect.try({
33− try: () => normalizeSourceDiscoveryUrl(input.url),
34− catch: (cause) =>
35− cause instanceof Error ? cause : new Error(String(cause)),
36− });
37− const headers = probeHeadersFromAuth(input.probeAuth);
38−
39− const adaptersByPriority = [...discoverableSourceAdapters].sort(
40− (left, right) =>
41− (right.discoveryPriority?.({ normalizedUrl }) ?? 0) -
42− (left.discoveryPriority?.({ normalizedUrl }) ?? 0),
43− );
44−
45− for (const adapter of adaptersByPriority) {
46− const detected = yield* adapter.detectSource({
47− normalizedUrl,
48− headers,
49− });
50− if (detected) {
51− return detected;
52− }
53− }
54−
55− return fallbackSourceDiscoveryResult(normalizedUrl);
56− });
packages/sources/core/src/discovery-models.ts+1−4
@@ -6,10 +6,7 @@ import { · 5 unmodified lines
66 } from "./source-models";
77
88 export const SourceDiscoveryKindSchema = Schema.Literal(
9− "mcp",
10− "openapi",
11− "google_discovery",
12− "graphql",
9+ "plugin",
1310 "unknown",
1411 );
1512
packages/sources/core/src/discovery.ts+1−4
@@ -215,9 +215,6 @@ export const executeHttpProbe = (input: { · 214 unmodified lines
215215 catch: (cause) => cause instanceof Error ? cause : new Error(String(cause)),
216216 });
217217
218−export const looksLikeGraphqlEndpoint = (normalizedUrl: string): boolean =>
219− /graphql/i.test(new URL(normalizedUrl).pathname);
220−
221218 export const tryParseJson = (value: string): unknown => {
222219 try {
223220 return JSON.parse(value) as unknown;
@@ -244,7 +241,7 @@ export const fallbackSourceDiscoveryResult = ( · 20 unmodified lines
244241 ),
245242 toolCount: null,
246243 warnings: [
247− "Could not confirm whether the URL is Google Discovery, OpenAPI, GraphQL, or MCP.",
244+ "Could not confirm whether the URL matches any registered source plugin.",
248245 ],
249246 };
250247 };