@executor-js/plugin-openapi 1.5.5 → 1.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/{AddOpenApiSource-7M52SRUX.js → AddOpenApiSource-7O4LSD7C.js} +79 -241
  2. package/dist/AddOpenApiSource-7O4LSD7C.js.map +1 -0
  3. package/dist/{EditOpenApiSource-WTAMRJUK.js → EditOpenApiSource-GIN5RQPL.js} +3 -3
  4. package/dist/{OpenApiAccountsPanel-3VJJXNQF.js → OpenApiAccountsPanel-7XT6ZMD5.js} +30 -28
  5. package/dist/OpenApiAccountsPanel-7XT6ZMD5.js.map +1 -0
  6. package/dist/api/group.d.ts +38 -37
  7. package/dist/api/index.d.ts +39 -38
  8. package/dist/{chunk-YVRI7KRC.js → chunk-C3IJX4AN.js} +257 -219
  9. package/dist/chunk-C3IJX4AN.js.map +1 -0
  10. package/dist/{chunk-OSIFYIQP.js → chunk-C6PH4R7Q.js} +94 -5
  11. package/dist/chunk-C6PH4R7Q.js.map +1 -0
  12. package/dist/{chunk-BSLE6HCE.js → chunk-IB36ED7Y.js} +13 -27
  13. package/dist/chunk-IB36ED7Y.js.map +1 -0
  14. package/dist/chunk-RCBR3QMJ.js +73 -0
  15. package/dist/chunk-RCBR3QMJ.js.map +1 -0
  16. package/dist/{chunk-V7VCHYOY.js → chunk-WJQIWTZF.js} +97 -172
  17. package/dist/chunk-WJQIWTZF.js.map +1 -0
  18. package/dist/client.js +3 -3
  19. package/dist/core.js +133 -7
  20. package/dist/core.js.map +1 -1
  21. package/dist/index.js +7 -5
  22. package/dist/index.js.map +1 -1
  23. package/dist/react/AddOpenApiSource.d.ts +0 -1
  24. package/dist/react/OpenApiSourceDetailsFields.d.ts +3 -0
  25. package/dist/react/atoms.d.ts +40 -52
  26. package/dist/react/auth-method-config.d.ts +7 -4
  27. package/dist/react/client.d.ts +38 -37
  28. package/dist/sdk/config.d.ts +27 -27
  29. package/dist/sdk/derive-auth.d.ts +9 -0
  30. package/dist/sdk/extract.d.ts +11 -1
  31. package/dist/sdk/index.d.ts +6 -2
  32. package/dist/sdk/invoke.d.ts +11 -1
  33. package/dist/sdk/migrate-config.d.ts +4 -0
  34. package/dist/sdk/migrate-config.test.d.ts +1 -0
  35. package/dist/sdk/openapi-utils.d.ts +4 -8
  36. package/dist/sdk/output-schema-migration.d.ts +21 -0
  37. package/dist/sdk/output-schema-migration.test.d.ts +1 -0
  38. package/dist/sdk/plugin.d.ts +6 -4
  39. package/dist/sdk/preview.d.ts +77 -1
  40. package/dist/sdk/server-url-resolution.test.d.ts +1 -0
  41. package/dist/sdk/spec-blob-migration.d.ts +7 -0
  42. package/dist/sdk/spec-blob.test.d.ts +1 -0
  43. package/dist/sdk/store.d.ts +10 -1
  44. package/dist/sdk/tool-row-projection.test.d.ts +1 -0
  45. package/dist/sdk/types.d.ts +61 -36
  46. package/dist/testing/index.d.ts +1 -1
  47. package/dist/testing.js +4 -20
  48. package/dist/testing.js.map +1 -1
  49. package/package.json +3 -3
  50. package/dist/AddOpenApiSource-7M52SRUX.js.map +0 -1
  51. package/dist/OpenApiAccountsPanel-3VJJXNQF.js.map +0 -1
  52. package/dist/chunk-BSLE6HCE.js.map +0 -1
  53. package/dist/chunk-OSIFYIQP.js.map +0 -1
  54. package/dist/chunk-QSSRVK6M.js +0 -139
  55. package/dist/chunk-QSSRVK6M.js.map +0 -1
  56. package/dist/chunk-V7VCHYOY.js.map +0 -1
  57. package/dist/chunk-YVRI7KRC.js.map +0 -1
  58. /package/dist/{EditOpenApiSource-WTAMRJUK.js.map → EditOpenApiSource-GIN5RQPL.js.map} +0 -0
@@ -0,0 +1,73 @@
1
+ // src/react/auth-method-config.ts
2
+ import { AuthTemplateSlug } from "@executor-js/sdk/shared";
3
+ import {
4
+ authMethodFromSharedTemplate,
5
+ editorPlacementsFromWire,
6
+ editorValueFromSharedMethod,
7
+ wireAuthInputFromShared,
8
+ wirePlacementsFromEditor
9
+ } from "@executor-js/react/lib/shared-auth-method-codec";
10
+ var openApiWireAuthInput = (method) => method.kind === "oauth2" ? method : wireAuthInputFromShared(method);
11
+ var oauthAuthMethod = (template) => {
12
+ const slug = String(template.slug);
13
+ return {
14
+ id: slug,
15
+ label: "OAuth2",
16
+ kind: "oauth",
17
+ source: slug.startsWith("custom_") ? "custom" : "spec",
18
+ template: AuthTemplateSlug.make(slug),
19
+ placements: [],
20
+ // Carry the integration's declared endpoints/scopes so the
21
+ // client-registration form pre-fills them.
22
+ oauth: {
23
+ authorizationUrl: template.authorizationUrl,
24
+ tokenUrl: template.tokenUrl,
25
+ scopes: template.scopes
26
+ }
27
+ };
28
+ };
29
+ function authMethodsFromConfig(templates) {
30
+ return templates.map((template) => {
31
+ if (template.kind === "oauth2") return oauthAuthMethod(template);
32
+ return authMethodFromSharedTemplate(template);
33
+ });
34
+ }
35
+ function templateFromPlacements(placements, slug) {
36
+ return {
37
+ slug: slug ?? "",
38
+ kind: "apikey",
39
+ placements: wirePlacementsFromEditor(placements)
40
+ };
41
+ }
42
+ function editorValueFromAuthentication(template) {
43
+ if (template.kind === "oauth2") {
44
+ return {
45
+ kind: "oauth",
46
+ authorizationUrl: template.authorizationUrl ?? "",
47
+ tokenUrl: template.tokenUrl ?? "",
48
+ scopes: template.scopes ?? []
49
+ };
50
+ }
51
+ return editorValueFromSharedMethod(template);
52
+ }
53
+ var oauthTemplateFromEditorValue = (value, slug) => ({
54
+ slug: AuthTemplateSlug.make(slug ?? ""),
55
+ kind: "oauth2",
56
+ authorizationUrl: value.authorizationUrl,
57
+ tokenUrl: value.tokenUrl,
58
+ scopes: [...value.scopes]
59
+ });
60
+ function authenticationFromEditorValue(value, slug) {
61
+ if (value.kind === "none") return null;
62
+ if (value.kind === "oauth") return oauthTemplateFromEditorValue(value, slug);
63
+ return templateFromPlacements(value.placements, slug);
64
+ }
65
+
66
+ export {
67
+ openApiWireAuthInput,
68
+ authMethodsFromConfig,
69
+ templateFromPlacements,
70
+ editorValueFromAuthentication,
71
+ authenticationFromEditorValue
72
+ };
73
+ //# sourceMappingURL=chunk-RCBR3QMJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/auth-method-config.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// OpenAPI ↔ generic auth-method converters — a thin oauth adapter over the\n// shared codec (`@executor-js/react/lib/shared-auth-method-codec`). The\n// apikey/none paths (multi-placement, multi-variable) live in the shared\n// codec; OpenAPI only contributes its oauth flavor: stored endpoints + scopes\n// (`kind: \"oauth2\"`, the core `OAuthAuthentication` shape) that pre-fill the client-registration form.\n// ---------------------------------------------------------------------------\n\nimport { AuthTemplateSlug } from \"@executor-js/sdk/shared\";\nimport type { AuthMethod, Placement } from \"@executor-js/react/lib/auth-placements\";\nimport type { AuthTemplateEditorValue } from \"@executor-js/react/components/auth-template-editor\";\nimport {\n authMethodFromSharedTemplate,\n editorPlacementsFromWire,\n editorValueFromSharedMethod,\n wireAuthInputFromShared,\n wirePlacementsFromEditor,\n} from \"@executor-js/react/lib/shared-auth-method-codec\";\n\nimport type { APIKeyAuthentication, Authentication, AuthenticationInput } from \"../sdk/types\";\n\n/** Serialize a canonical method into the wire input union (apikey → the\n * request-shaped dialect; oauth passes through). */\nexport const openApiWireAuthInput = (method: Authentication): AuthenticationInput =>\n method.kind === \"oauth2\" ? method : (wireAuthInputFromShared(method) as AuthenticationInput);\n\nexport const placementsFromApiKey = (template: APIKeyAuthentication): readonly Placement[] =>\n editorPlacementsFromWire(template.placements);\n\nconst oauthAuthMethod = (template: Extract<Authentication, { kind: \"oauth2\" }>): AuthMethod => {\n const slug = String(template.slug);\n return {\n id: slug,\n label: \"OAuth2\",\n kind: \"oauth\",\n source: slug.startsWith(\"custom_\") ? \"custom\" : \"spec\",\n template: AuthTemplateSlug.make(slug),\n placements: [],\n // Carry the integration's declared endpoints/scopes so the\n // client-registration form pre-fills them.\n oauth: {\n authorizationUrl: template.authorizationUrl,\n tokenUrl: template.tokenUrl,\n scopes: template.scopes,\n },\n };\n};\n\n/** Map each stored auth template to a generic `AuthMethod`. */\nexport function authMethodsFromConfig(templates: readonly Authentication[]): AuthMethod[] {\n return templates.map((template: Authentication): AuthMethod => {\n if (template.kind === \"oauth2\") return oauthAuthMethod(template);\n return authMethodFromSharedTemplate(template);\n });\n}\n\n/** Build an apikey method from generic placements. When `slug` is omitted the\n * backend assigns a `custom_<id>` slug. */\nexport function templateFromPlacements(\n placements: readonly Placement[],\n slug?: string,\n): APIKeyAuthentication {\n return {\n slug: slug ?? \"\",\n kind: \"apikey\",\n placements: wirePlacementsFromEditor(placements),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Stored `Authentication` ⇆ generic `AuthTemplateEditorValue`.\n// ---------------------------------------------------------------------------\n\n/** Convert one stored `Authentication` template into a generic editor value. */\nexport function editorValueFromAuthentication(template: Authentication): AuthTemplateEditorValue {\n if (template.kind === \"oauth2\") {\n return {\n kind: \"oauth\",\n authorizationUrl: template.authorizationUrl ?? \"\",\n tokenUrl: template.tokenUrl ?? \"\",\n scopes: template.scopes ?? [],\n };\n }\n return editorValueFromSharedMethod(template);\n}\n\n/** Build an `OAuthAuthentication` template from a generic oauth editor value. */\nconst oauthTemplateFromEditorValue = (\n value: Extract<AuthTemplateEditorValue, { kind: \"oauth\" }>,\n slug?: string,\n): Authentication => ({\n slug: AuthTemplateSlug.make(slug ?? \"\"),\n kind: \"oauth2\",\n authorizationUrl: value.authorizationUrl,\n tokenUrl: value.tokenUrl,\n scopes: [...value.scopes],\n});\n\n/** Convert one generic editor value back into a stored `Authentication`, or\n * `null` for `none` (no method to register). The optional `slug` names the\n * template; when omitted the backend backfills `custom_<id>`. */\nexport function authenticationFromEditorValue(\n value: AuthTemplateEditorValue,\n slug?: string,\n): Authentication | null {\n if (value.kind === \"none\") return null;\n if (value.kind === \"oauth\") return oauthTemplateFromEditorValue(value, slug);\n return templateFromPlacements(value.placements, slug);\n}\n"],"mappings":";AAQA,SAAS,wBAAwB;AAGjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAMA,IAAM,uBAAuB,CAAC,WACnC,OAAO,SAAS,WAAW,SAAU,wBAAwB,MAAM;AAKrE,IAAM,kBAAkB,CAAC,aAAsE;AAC7F,QAAM,OAAO,OAAO,SAAS,IAAI;AACjC,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ,KAAK,WAAW,SAAS,IAAI,WAAW;AAAA,IAChD,UAAU,iBAAiB,KAAK,IAAI;AAAA,IACpC,YAAY,CAAC;AAAA;AAAA;AAAA,IAGb,OAAO;AAAA,MACL,kBAAkB,SAAS;AAAA,MAC3B,UAAU,SAAS;AAAA,MACnB,QAAQ,SAAS;AAAA,IACnB;AAAA,EACF;AACF;AAGO,SAAS,sBAAsB,WAAoD;AACxF,SAAO,UAAU,IAAI,CAAC,aAAyC;AAC7D,QAAI,SAAS,SAAS,SAAU,QAAO,gBAAgB,QAAQ;AAC/D,WAAO,6BAA6B,QAAQ;AAAA,EAC9C,CAAC;AACH;AAIO,SAAS,uBACd,YACA,MACsB;AACtB,SAAO;AAAA,IACL,MAAM,QAAQ;AAAA,IACd,MAAM;AAAA,IACN,YAAY,yBAAyB,UAAU;AAAA,EACjD;AACF;AAOO,SAAS,8BAA8B,UAAmD;AAC/F,MAAI,SAAS,SAAS,UAAU;AAC9B,WAAO;AAAA,MACL,MAAM;AAAA,MACN,kBAAkB,SAAS,oBAAoB;AAAA,MAC/C,UAAU,SAAS,YAAY;AAAA,MAC/B,QAAQ,SAAS,UAAU,CAAC;AAAA,IAC9B;AAAA,EACF;AACA,SAAO,4BAA4B,QAAQ;AAC7C;AAGA,IAAM,+BAA+B,CACnC,OACA,UACoB;AAAA,EACpB,MAAM,iBAAiB,KAAK,QAAQ,EAAE;AAAA,EACtC,MAAM;AAAA,EACN,kBAAkB,MAAM;AAAA,EACxB,UAAU,MAAM;AAAA,EAChB,QAAQ,CAAC,GAAG,MAAM,MAAM;AAC1B;AAKO,SAAS,8BACd,OACA,MACuB;AACvB,MAAI,MAAM,SAAS,OAAQ,QAAO;AAClC,MAAI,MAAM,SAAS,QAAS,QAAO,6BAA6B,OAAO,IAAI;AAC3E,SAAO,uBAAuB,MAAM,YAAY,IAAI;AACtD;","names":[]}
@@ -1,9 +1,11 @@
1
1
  import {
2
2
  convertGoogleDiscoveryBundleToOpenApi,
3
3
  convertGoogleDiscoveryToOpenApi,
4
+ deriveAuthenticationTemplateFromPreview,
4
5
  fetchGoogleDiscoveryDocument,
6
+ firstBaseUrlForPreview,
5
7
  isGoogleDiscoveryUrl
6
- } from "./chunk-OSIFYIQP.js";
8
+ } from "./chunk-C6PH4R7Q.js";
7
9
  import {
8
10
  openApiPresets
9
11
  } from "./chunk-AQ7JDDRM.js";
@@ -12,48 +14,40 @@ import {
12
14
  OpenApiExtractionError,
13
15
  OpenApiInvocationError,
14
16
  OperationBinding,
15
- TOKEN_VARIABLE,
16
17
  extract,
18
+ normalizeOpenApiAuthInputs,
17
19
  parse,
18
20
  previewSpec,
21
+ previewSpecText,
22
+ resolveServerUrl,
19
23
  resolveSpecText
20
- } from "./chunk-YVRI7KRC.js";
24
+ } from "./chunk-C3IJX4AN.js";
21
25
 
22
26
  // src/sdk/config.ts
23
27
  import { Option, Schema } from "effect";
24
- var AuthenticationVariableSchema = Schema.Struct({
25
- type: Schema.Literal("variable"),
26
- name: Schema.String
27
- });
28
- var AuthenticationTemplateValueSchema = Schema.Union([
29
- Schema.String,
30
- Schema.Array(Schema.Union([Schema.String, AuthenticationVariableSchema]))
31
- ]);
32
- var APIKeyAuthenticationSchema = Schema.Struct({
33
- slug: Schema.String,
34
- type: Schema.Literal("apiKey"),
35
- headers: Schema.optional(Schema.Record(Schema.String, AuthenticationTemplateValueSchema)),
36
- queryParams: Schema.optional(Schema.Record(Schema.String, AuthenticationTemplateValueSchema))
37
- });
28
+ import {
29
+ ApiKeyAuthMethod,
30
+ TOKEN_VARIABLE,
31
+ renderAuthPlacements,
32
+ requiredPlacementVariables
33
+ } from "@executor-js/sdk/http-auth";
38
34
  var OAuthAuthenticationSchema = Schema.Struct({
39
35
  slug: Schema.String,
40
- type: Schema.Literal("oauth"),
36
+ kind: Schema.Literal("oauth2"),
41
37
  authorizationUrl: Schema.String,
42
38
  tokenUrl: Schema.String,
43
39
  scopes: Schema.Array(Schema.String)
44
40
  });
45
- var AuthenticationSchema = Schema.Union([
46
- OAuthAuthenticationSchema,
47
- APIKeyAuthenticationSchema
48
- ]);
41
+ var AuthenticationSchema = Schema.Union([OAuthAuthenticationSchema, ApiKeyAuthMethod]);
49
42
  var OpenApiIntegrationConfigSchema = Schema.Struct({
50
- /** Inlined OpenAPI document text (resolved + parsed source of truth). */
51
- spec: Schema.String,
43
+ /** Hex SHA-256 of the resolved spec text the content address of the spec
44
+ * blob (`spec/<hash>` in the plugin blob store). */
45
+ specHash: Schema.optional(Schema.String),
52
46
  /** Origin URL the spec was fetched from, when known. Enables refresh. */
53
47
  sourceUrl: Schema.optional(Schema.String),
54
48
  /** Google Discovery bundle URLs, when the spec came from a Google bundle. */
55
49
  googleDiscoveryUrls: Schema.optional(Schema.Array(Schema.String)),
56
- /** Base URL override; falls back to the spec's first server. */
50
+ /** Optional base URL override. */
57
51
  baseUrl: Schema.optional(Schema.String),
58
52
  /** Static headers applied to every request (no secret material). */
59
53
  headers: Schema.optional(Schema.Record(Schema.String, Schema.String)),
@@ -64,40 +58,18 @@ var OpenApiIntegrationConfigSchema = Schema.Struct({
64
58
  });
65
59
  var decodeConfig = Schema.decodeUnknownOption(OpenApiIntegrationConfigSchema);
66
60
  var decodeOpenApiIntegrationConfig = (value) => Option.getOrNull(decodeConfig(value));
67
- var isVariable = (part) => typeof part !== "string";
68
- var renderTemplateValue = (template, values) => {
69
- if (typeof template === "string") return template;
70
- return template.map((part) => isVariable(part) ? values[part.name] ?? "" : part).join("");
71
- };
72
61
  var renderAuthTemplate = (template, values) => {
73
- if (template.type === "oauth") {
62
+ if (template.kind === "oauth2") {
74
63
  return {
75
64
  headers: { authorization: `Bearer ${values[TOKEN_VARIABLE] ?? ""}` },
76
65
  queryParams: {}
77
66
  };
78
67
  }
79
- const headers = {};
80
- const queryParams = {};
81
- for (const [name, tmpl] of Object.entries(template.headers ?? {})) {
82
- headers[name] = renderTemplateValue(tmpl, values);
83
- }
84
- for (const [name, tmpl] of Object.entries(template.queryParams ?? {})) {
85
- queryParams[name] = renderTemplateValue(tmpl, values);
86
- }
87
- return { headers, queryParams };
68
+ return renderAuthPlacements(template.placements, values);
88
69
  };
89
70
  var requiredTemplateVariables = (template) => {
90
- if (template.type === "oauth") return [TOKEN_VARIABLE];
91
- const names = /* @__PURE__ */ new Set();
92
- const collect = (tmpl) => {
93
- if (typeof tmpl === "string") return;
94
- for (const part of tmpl) {
95
- if (isVariable(part)) names.add(part.name);
96
- }
97
- };
98
- for (const tmpl of Object.values(template.headers ?? {})) collect(tmpl);
99
- for (const tmpl of Object.values(template.queryParams ?? {})) collect(tmpl);
100
- return [...names];
71
+ if (template.kind === "oauth2") return [TOKEN_VARIABLE];
72
+ return requiredPlacementVariables(template.placements);
101
73
  };
102
74
 
103
75
  // src/sdk/invoke.ts
@@ -442,9 +414,21 @@ var invoke = Effect.fn("OpenApi.invoke")(function* (operation, args, resolvedHea
442
414
  error: ok ? null : responseBody
443
415
  });
444
416
  });
417
+ var resolveRequestHost = (servers, serverArg, baseUrl) => {
418
+ if (baseUrl) return baseUrl;
419
+ if (servers.length === 0) return "";
420
+ const arg = typeof serverArg === "object" && serverArg !== null && !Array.isArray(serverArg) ? serverArg : {};
421
+ const chosen = servers.find((server) => server.url === arg.url) ?? servers[0];
422
+ const overrides = {};
423
+ if (typeof arg.variables === "object" && arg.variables !== null) {
424
+ for (const [name, value] of Object.entries(arg.variables)) {
425
+ if (value != null && value !== "") overrides[name] = String(value);
426
+ }
427
+ }
428
+ return resolveServerUrl(chosen.url, Option2.getOrUndefined(chosen.variables), overrides);
429
+ };
445
430
  var invokeWithLayer = (operation, args, baseUrl, resolvedHeaders, sourceQueryParams, httpClientLayer) => {
446
- const operationBaseUrl = operation.baseUrl;
447
- const effectiveBaseUrl = operationBaseUrl ?? baseUrl;
431
+ const effectiveBaseUrl = resolveRequestHost(operation.servers ?? [], args.server, baseUrl);
448
432
  const clientWithBaseUrl = effectiveBaseUrl ? Layer.effect(
449
433
  HttpClient.HttpClient,
450
434
  Effect.map(
@@ -500,7 +484,8 @@ var rowToOperation = (row) => {
500
484
  };
501
485
  };
502
486
  var operationKey = (integration, toolName) => `${integration}.${toolName}`;
503
- var makeDefaultOpenapiStore = ({ pluginStorage }) => {
487
+ var specBlobKey = (specHash) => `spec/${specHash}`;
488
+ var makeDefaultOpenapiStore = ({ pluginStorage, blobs }) => {
504
489
  const operationData = (operation) => ({
505
490
  integration: operation.integration,
506
491
  toolName: operation.toolName,
@@ -534,14 +519,15 @@ var makeDefaultOpenapiStore = ({ pluginStorage }) => {
534
519
  listOperations: (integration) => listRows(integration).pipe(
535
520
  Effect2.map((rows) => rows.map(rowToOperation).filter(Predicate.isNotNull))
536
521
  ),
537
- removeOperations
522
+ removeOperations,
523
+ putSpec: (specHash, specText) => blobs.put(specBlobKey(specHash), specText, { owner: STORE_OWNER }),
524
+ getSpec: (specHash) => blobs.get(specBlobKey(specHash))
538
525
  };
539
526
  };
540
527
 
541
528
  // src/sdk/plugin.ts
542
529
  import { Effect as Effect3, Option as Option5, Schema as Schema3 } from "effect";
543
530
  import {
544
- AuthTemplateSlug,
545
531
  IntegrationAlreadyExistsError,
546
532
  IntegrationDetectionResult,
547
533
  IntegrationSlug,
@@ -549,6 +535,8 @@ import {
549
535
  ToolResult,
550
536
  authToolFailure,
551
537
  definePlugin,
538
+ mergeAuthTemplates,
539
+ sha256Hex,
552
540
  tool
553
541
  } from "@executor-js/sdk/core";
554
542
 
@@ -698,6 +686,7 @@ var compileToolDefinitions = (operations) => {
698
686
  };
699
687
 
700
688
  // src/sdk/plugin.ts
689
+ import { ApiKeyAuthTemplate, describeApiKeyAuthMethod } from "@executor-js/sdk/http-auth";
701
690
  var STRINGIFIED_BODY_CAP = 1024;
702
691
  var UpstreamMessageBody = Schema3.Struct({ message: Schema3.String });
703
692
  var UpstreamErrorMessageBody = Schema3.Struct({ errorMessage: Schema3.String });
@@ -844,28 +833,18 @@ var OpenApiSpecInputSchema = Schema3.Union([
844
833
  urls: Schema3.Array(Schema3.String)
845
834
  })
846
835
  ]);
847
- var AuthenticationVariableSchema2 = Schema3.Struct({
848
- type: Schema3.Literal("variable"),
849
- name: Schema3.String
850
- });
851
- var AuthenticationTemplateValueSchema2 = Schema3.Union([
852
- Schema3.String,
853
- Schema3.Array(Schema3.Union([Schema3.String, AuthenticationVariableSchema2]))
854
- ]);
855
836
  var AuthenticationSchema2 = Schema3.Union([
856
837
  Schema3.Struct({
857
838
  slug: Schema3.String,
858
- type: Schema3.Literal("apiKey"),
859
- headers: Schema3.optional(Schema3.Record(Schema3.String, AuthenticationTemplateValueSchema2)),
860
- queryParams: Schema3.optional(Schema3.Record(Schema3.String, AuthenticationTemplateValueSchema2))
861
- }),
862
- Schema3.Struct({
863
- slug: Schema3.String,
864
- type: Schema3.Literal("oauth"),
839
+ kind: Schema3.Literal("oauth2"),
865
840
  authorizationUrl: Schema3.String,
866
841
  tokenUrl: Schema3.String,
867
842
  scopes: Schema3.Array(Schema3.String)
868
- })
843
+ }),
844
+ // Credential methods are authored request-shaped — the ONE apikey input
845
+ // dialect: `{ type: "apiKey", headers: { Authorization: ["Bearer ",
846
+ // variable("token")] }, queryParams: { … } }`.
847
+ ApiKeyAuthTemplate
869
848
  ]);
870
849
  var AddSourceInputSchema = Schema3.Struct({
871
850
  spec: OpenApiSpecInputSchema,
@@ -998,7 +977,7 @@ var normalizeOpenApiRefs = (node) => {
998
977
  };
999
978
  var toBinding = (def) => OperationBinding.make({
1000
979
  method: def.operation.method,
1001
- baseUrl: def.operation.baseUrl,
980
+ servers: def.operation.servers,
1002
981
  pathTemplate: def.operation.pathTemplate,
1003
982
  parameters: [...def.operation.parameters],
1004
983
  requestBody: def.operation.requestBody
@@ -1010,84 +989,19 @@ var descriptionFor = (def) => {
1010
989
  () => Option5.getOrElse(op.summary, () => `${op.method.toUpperCase()} ${op.pathTemplate}`)
1011
990
  );
1012
991
  };
1013
- var openApiTransportOutputSchema = (dataSchema) => ({
1014
- type: "object",
1015
- additionalProperties: false,
1016
- required: ["status", "headers", "data"],
1017
- properties: {
1018
- status: { type: "integer" },
1019
- headers: {
1020
- type: "object",
1021
- additionalProperties: { type: "string" }
1022
- },
1023
- data: dataSchema ?? {}
1024
- }
1025
- });
1026
992
  var specInputToSourceUrl = (spec) => spec.kind === "url" || spec.kind === "googleDiscovery" ? spec.url : void 0;
1027
993
  var specInputToGoogleBundle = (spec) => spec.kind === "googleDiscoveryBundle" ? spec.urls : void 0;
1028
- var shortId = () => Math.random().toString(36).slice(2, 8);
1029
- var freshCustomSlug = (taken) => {
1030
- let candidate = `custom_${shortId()}`;
1031
- while (taken.has(candidate)) candidate = `custom_${shortId()}`;
1032
- return AuthTemplateSlug.make(candidate);
1033
- };
1034
- var mergeAuthenticationTemplate = (existing, incoming) => {
1035
- const result = existing.map((entry) => entry);
1036
- const taken = new Set(result.map((entry) => String(entry.slug)));
1037
- for (const entry of incoming) {
1038
- const rawSlug = entry.slug;
1039
- const requested = typeof rawSlug === "string" ? rawSlug.trim() : "";
1040
- const existingIndex = result.findIndex((current) => String(current.slug) === requested);
1041
- if (requested.length > 0 && existingIndex >= 0) {
1042
- result[existingIndex] = entry;
1043
- continue;
1044
- }
1045
- const slug = requested.length > 0 && !taken.has(requested) ? AuthTemplateSlug.make(requested) : freshCustomSlug(taken);
1046
- taken.add(String(slug));
1047
- result.push({ ...entry, slug });
1048
- }
1049
- return result;
1050
- };
1051
- var parseTemplateValue = (value) => {
1052
- if (typeof value === "string") return { prefix: "", variable: TOKEN_VARIABLE };
1053
- const parts = [];
1054
- for (const part of value) {
1055
- if (typeof part !== "string" && part.type === "variable") {
1056
- return { prefix: parts.join(""), variable: part.name };
1057
- }
1058
- if (typeof part === "string") parts.push(part);
1059
- }
1060
- return { prefix: parts.join(""), variable: TOKEN_VARIABLE };
1061
- };
1062
- var placementsFromAuthentication = (template) => {
1063
- const placements = [];
1064
- for (const [name, value] of Object.entries(template.headers ?? {})) {
1065
- const { prefix, variable } = parseTemplateValue(value);
1066
- placements.push({ carrier: "header", name, prefix, variable });
1067
- }
1068
- for (const [name, value] of Object.entries(template.queryParams ?? {})) {
1069
- const { prefix, variable } = parseTemplateValue(value);
1070
- placements.push({ carrier: "query", name, prefix, variable });
1071
- }
1072
- return placements;
1073
- };
1074
- var apiKeyLabel = (slug, placements) => {
1075
- const first = placements[0];
1076
- if (first) return `API key (${first.name || (first.carrier === "header" ? "header" : "query")})`;
1077
- return `API key (${slug})`;
1078
- };
1079
994
  var describeOpenApiAuthMethods = (record) => {
1080
995
  const config = decodeOpenApiIntegrationConfig(record.config);
1081
996
  if (!config) return [];
1082
997
  return (config.authenticationTemplate ?? []).map(
1083
998
  (template) => {
1084
- const slug = String(template.slug);
1085
- if (template.type === "oauth") {
999
+ if (template.kind === "oauth2") {
1086
1000
  return {
1087
- id: slug,
1001
+ id: String(template.slug),
1088
1002
  label: "OAuth2",
1089
1003
  kind: "oauth",
1090
- template: slug,
1004
+ template: String(template.slug),
1091
1005
  oauth: {
1092
1006
  authorizationUrl: template.authorizationUrl,
1093
1007
  tokenUrl: template.tokenUrl,
@@ -1095,14 +1009,7 @@ var describeOpenApiAuthMethods = (record) => {
1095
1009
  }
1096
1010
  };
1097
1011
  }
1098
- const placements = placementsFromAuthentication(template);
1099
- return {
1100
- id: slug,
1101
- label: apiKeyLabel(slug, placements),
1102
- kind: "apikey",
1103
- template: slug,
1104
- placements
1105
- };
1012
+ return describeApiKeyAuthMethod(template);
1106
1013
  }
1107
1014
  );
1108
1015
  };
@@ -1110,6 +1017,7 @@ var describeOpenApiIntegrationDisplay = (record) => {
1110
1017
  const config = decodeOpenApiIntegrationConfig(record.config);
1111
1018
  return { url: config?.baseUrl ?? config?.sourceUrl };
1112
1019
  };
1020
+ var loadSpecText = (storage, config) => config.specHash != null ? storage.getSpec(config.specHash) : Effect3.succeed(null);
1113
1021
  var compileSpec = (specText) => Effect3.gen(function* () {
1114
1022
  const doc = yield* parse(specText);
1115
1023
  const result = yield* extract(doc);
@@ -1130,9 +1038,10 @@ var toolDefsFromCompiled = (compiled) => compiled.definitions.map(
1130
1038
  name: ToolName.make(def.toolPath),
1131
1039
  description: descriptionFor(def),
1132
1040
  inputSchema: normalizeOpenApiRefs(Option5.getOrUndefined(def.operation.inputSchema)),
1133
- outputSchema: openApiTransportOutputSchema(
1134
- normalizeOpenApiRefs(Option5.getOrUndefined(def.operation.outputSchema))
1135
- ),
1041
+ // The output schema is the upstream response body only — transport
1042
+ // status/headers live in the ToolResult `http` side channel, not the
1043
+ // payload (see the invoke handler).
1044
+ outputSchema: normalizeOpenApiRefs(Option5.getOrUndefined(def.operation.outputSchema)),
1136
1045
  annotations: annotationsForOperation(def.operation.method, def.operation.pathTemplate)
1137
1046
  })
1138
1047
  );
@@ -1198,23 +1107,38 @@ var openApiPlugin = definePlugin((options) => {
1198
1107
  const addSpec = (config) => Effect3.gen(function* () {
1199
1108
  const resolved = yield* resolveSpecForInput(config.spec, httpClientLayer);
1200
1109
  const compiled = yield* compileSpec(resolved.specText);
1110
+ const explicitBaseUrl = config.baseUrl ?? resolved.baseUrl;
1111
+ const needsDerivedBaseUrl = explicitBaseUrl == null;
1112
+ const needsDerivedAuth = config.authenticationTemplate == null && resolved.authenticationTemplate == null;
1113
+ const preview = needsDerivedBaseUrl || needsDerivedAuth ? yield* previewSpecText(resolved.specText) : void 0;
1114
+ const derivedBaseUrl = needsDerivedBaseUrl && preview ? firstBaseUrlForPreview(preview) : void 0;
1115
+ const effectiveBaseUrl = explicitBaseUrl ?? (derivedBaseUrl || void 0);
1116
+ const derivedAuthenticationTemplate = needsDerivedAuth && preview ? deriveAuthenticationTemplateFromPreview(preview, effectiveBaseUrl) : void 0;
1201
1117
  const slug = IntegrationSlug.make(config.slug);
1202
1118
  const existing = yield* ctx.core.integrations.get(slug);
1203
1119
  if (existing) {
1204
1120
  return yield* new IntegrationAlreadyExistsError({ slug });
1205
1121
  }
1122
+ const specHash = yield* sha256Hex(resolved.specText);
1206
1123
  const integrationConfig = {
1207
- spec: resolved.specText,
1124
+ specHash,
1208
1125
  ...specInputToSourceUrl(config.spec) !== void 0 ? { sourceUrl: specInputToSourceUrl(config.spec) } : {},
1209
1126
  ...specInputToGoogleBundle(config.spec) !== void 0 ? { googleDiscoveryUrls: specInputToGoogleBundle(config.spec) } : {},
1210
- ...config.baseUrl ?? resolved.baseUrl ? { baseUrl: config.baseUrl ?? resolved.baseUrl } : {},
1127
+ // baseUrl is an optional override only. The host is otherwise
1128
+ // resolved per call from the operation's `servers` (extracted from
1129
+ // the spec), so we never bake a derived base URL into the config.
1130
+ ...config.baseUrl ? { baseUrl: config.baseUrl } : {},
1211
1131
  ...config.headers ? { headers: config.headers } : {},
1212
1132
  ...config.queryParams ? { queryParams: config.queryParams } : {},
1213
1133
  // Prefer the caller's explicit template; otherwise adopt the one the
1214
1134
  // Google Discovery converter derived from the spec (the bundle add
1215
- // path relies on this — it has no preview to detect auth from).
1216
- ...config.authenticationTemplate ? { authenticationTemplate: config.authenticationTemplate } : resolved.authenticationTemplate ? { authenticationTemplate: resolved.authenticationTemplate } : {}
1135
+ // path relies on this — it has no preview to detect auth from);
1136
+ // otherwise derive from the spec's declared security schemes.
1137
+ ...config.authenticationTemplate ? {
1138
+ authenticationTemplate: normalizeOpenApiAuthInputs(config.authenticationTemplate)
1139
+ } : resolved.authenticationTemplate ? { authenticationTemplate: resolved.authenticationTemplate } : derivedAuthenticationTemplate && derivedAuthenticationTemplate.length > 0 ? { authenticationTemplate: derivedAuthenticationTemplate } : {}
1217
1140
  };
1141
+ yield* ctx.storage.putSpec(specHash, resolved.specText);
1218
1142
  yield* ctx.transaction(
1219
1143
  Effect3.gen(function* () {
1220
1144
  yield* ctx.core.integrations.register({
@@ -1276,10 +1200,8 @@ var openApiPlugin = definePlugin((options) => {
1276
1200
  if (!record) return [];
1277
1201
  const current = decodeOpenApiIntegrationConfig(record.config);
1278
1202
  if (!current) return [];
1279
- const merged = input.mode === "replace" ? input.authenticationTemplate : mergeAuthenticationTemplate(
1280
- current.authenticationTemplate ?? [],
1281
- input.authenticationTemplate
1282
- );
1203
+ const incoming = normalizeOpenApiAuthInputs(input.authenticationTemplate);
1204
+ const merged = input.mode === "replace" ? incoming : mergeAuthTemplates(current.authenticationTemplate ?? [], incoming);
1283
1205
  const next = {
1284
1206
  ...current,
1285
1207
  authenticationTemplate: merged
@@ -1314,7 +1236,7 @@ var openApiPlugin = definePlugin((options) => {
1314
1236
  }),
1315
1237
  tool({
1316
1238
  name: "addSpec",
1317
- description: "Add an OpenAPI integration to the catalog and persist its operations as tools. Recommended flow: call `previewSpec`, choose a `slug`, declare an `authenticationTemplate` for how a credential is applied (apiKey header/query, or oauth bearer), then create a connection for that integration with the user's API key or via `oauth.start`.",
1239
+ description: "Add an OpenAPI integration to the catalog and persist its operations as tools. Recommended flow: call `previewSpec`, choose a `slug`, then create a connection for that integration with the user's API key or via `oauth.start`. When `baseUrl` is omitted it defaults to the spec's first server; when `authenticationTemplate` is omitted the auth methods are derived from the spec's declared security schemes (pass an explicit template to override how a credential is applied \u2014 apiKey header/query, or oauth bearer \u2014 or an empty array for no auth methods).",
1318
1240
  annotations: {
1319
1241
  requiresApproval: true,
1320
1242
  approvalDescription: "Add an OpenAPI integration"
@@ -1357,13 +1279,17 @@ var openApiPlugin = definePlugin((options) => {
1357
1279
  // Produce one tool per spec operation. Spec-derived, identical for every
1358
1280
  // connection on the integration — so `getValue` is never called here. The
1359
1281
  // operation bindings invokeTool needs are persisted at addSpec time; this
1360
- // hook only shapes the per-connection ToolDefs from the catalog config.
1282
+ // hook only shapes the per-connection ToolDefs from the spec blob the
1283
+ // catalog config points at.
1361
1284
  resolveTools: ({
1362
- config
1285
+ config,
1286
+ storage
1363
1287
  }) => Effect3.gen(function* () {
1364
1288
  const openApiConfig = decodeOpenApiIntegrationConfig(config);
1365
1289
  if (!openApiConfig) return { tools: [], definitions: {} };
1366
- const compiled = yield* compileSpec(openApiConfig.spec).pipe(
1290
+ const specText = yield* loadSpecText(storage, openApiConfig);
1291
+ if (specText == null) return { tools: [], definitions: {} };
1292
+ const compiled = yield* compileSpec(specText).pipe(
1367
1293
  Effect3.catch(() => Effect3.succeed(null))
1368
1294
  );
1369
1295
  if (!compiled) return { tools: [], definitions: {} };
@@ -1383,9 +1309,10 @@ var openApiPlugin = definePlugin((options) => {
1383
1309
  const config = decodeOpenApiIntegrationConfig(credential.config);
1384
1310
  let binding = (yield* invokeCtx.storage.getOperation(integration, toolRow.name))?.binding;
1385
1311
  if (!binding && config) {
1386
- const compiled = yield* compileSpec(config.spec).pipe(
1312
+ const specText = yield* loadSpecText(invokeCtx.storage, config).pipe(
1387
1313
  Effect3.catch(() => Effect3.succeed(null))
1388
1314
  );
1315
+ const compiled = specText == null ? null : yield* compileSpec(specText).pipe(Effect3.catch(() => Effect3.succeed(null)));
1389
1316
  binding = compiled ? storedOperationsFromCompiled(integration, compiled).find(
1390
1317
  (op) => op.toolName === toolRow.name
1391
1318
  )?.binding : void 0;
@@ -1409,12 +1336,12 @@ var openApiPlugin = definePlugin((options) => {
1409
1336
  });
1410
1337
  if (missing.length > 0) {
1411
1338
  return openApiAuthToolFailure({
1412
- code: template.type === "oauth" ? "oauth_connection_missing" : "connection_value_missing",
1339
+ code: template.kind === "oauth2" ? "oauth_connection_missing" : "connection_value_missing",
1413
1340
  message: `Connection "${credential.connection}" for "${integration}" has no resolvable credential value. Re-authenticate or update the connection.`,
1414
1341
  owner: credential.owner,
1415
1342
  integration,
1416
1343
  connection: String(credential.connection),
1417
- credentialKind: template.type === "oauth" ? "oauth" : "secret"
1344
+ credentialKind: template.kind === "oauth2" ? "oauth" : "secret"
1418
1345
  });
1419
1346
  }
1420
1347
  const rendered = renderAuthTemplate(template, credential.values);
@@ -1451,10 +1378,8 @@ var openApiPlugin = definePlugin((options) => {
1451
1378
  details: result.error
1452
1379
  });
1453
1380
  }
1454
- return ToolResult.ok({
1455
- status: result.status,
1456
- headers: result.headers,
1457
- data: result.data
1381
+ return ToolResult.ok(result.data, {
1382
+ http: { status: result.status, headers: result.headers }
1458
1383
  });
1459
1384
  }),
1460
1385
  resolveAnnotations: ({
@@ -1541,4 +1466,4 @@ export {
1541
1466
  makeDefaultOpenapiStore,
1542
1467
  openApiPlugin
1543
1468
  };
1544
- //# sourceMappingURL=chunk-V7VCHYOY.js.map
1469
+ //# sourceMappingURL=chunk-WJQIWTZF.js.map