@executor-js/plugin-openapi 1.5.14 → 1.5.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AddOpenApiSource-U7AYB224.js +369 -0
- package/dist/AddOpenApiSource-U7AYB224.js.map +1 -0
- package/dist/{OpenApiAccountsPanel-Y2RGBSSL.js → OpenApiAccountsPanel-GHFHHE6P.js} +15 -39
- package/dist/OpenApiAccountsPanel-GHFHHE6P.js.map +1 -0
- package/dist/{UpdateSpecSection-MIZ5GLBM.js → UpdateSpecSection-PLCBUU4I.js} +4 -4
- package/dist/UpdateSpecSection-PLCBUU4I.js.map +1 -0
- package/dist/api/group.d.ts +0 -21
- package/dist/api/index.d.ts +0 -21
- package/dist/{chunk-PNOEE3ET.js → chunk-3FM2SWM4.js} +643 -569
- package/dist/chunk-3FM2SWM4.js.map +1 -0
- package/dist/chunk-CKBX4SXK.js +95 -0
- package/dist/chunk-CKBX4SXK.js.map +1 -0
- package/dist/{chunk-GS5YN6J4.js → chunk-CPPTKUOW.js} +5 -14
- package/dist/chunk-CPPTKUOW.js.map +1 -0
- package/dist/{chunk-RFSMGUBJ.js → chunk-KVPUDOJZ.js} +83 -8
- package/dist/chunk-KVPUDOJZ.js.map +1 -0
- package/dist/{chunk-PAHWRRS3.js → chunk-QQFCICLX.js} +4 -13
- package/dist/chunk-QQFCICLX.js.map +1 -0
- package/dist/client.js +4 -5
- package/dist/client.js.map +1 -1
- package/dist/core.js +25 -15
- package/dist/core.js.map +1 -1
- package/dist/index.js +4 -5
- package/dist/index.js.map +1 -1
- package/dist/react/atoms.d.ts +0 -22
- package/dist/react/client.d.ts +0 -21
- package/dist/react/index.d.ts +2 -0
- package/dist/sdk/backing.d.ts +54 -0
- package/dist/sdk/config.d.ts +0 -2
- package/dist/sdk/derive-auth.d.ts +5 -3
- package/dist/sdk/extract.d.ts +12 -1
- package/dist/sdk/index.d.ts +2 -1
- package/dist/sdk/invoke.d.ts +12 -1
- package/dist/sdk/plugin.d.ts +4 -10
- package/dist/sdk/presets.d.ts +0 -1
- package/dist/sdk/preview.d.ts +79 -0
- package/dist/sdk/types.d.ts +53 -0
- package/dist/testing/index.d.ts +0 -6
- package/package.json +6 -5
- package/dist/AddOpenApiSource-7TI5XUUY.js +0 -765
- package/dist/AddOpenApiSource-7TI5XUUY.js.map +0 -1
- package/dist/OpenApiAccountsPanel-Y2RGBSSL.js.map +0 -1
- package/dist/UpdateSpecSection-MIZ5GLBM.js.map +0 -1
- package/dist/chunk-GS5YN6J4.js.map +0 -1
- package/dist/chunk-I2XDCVVM.js +0 -712
- package/dist/chunk-I2XDCVVM.js.map +0 -1
- package/dist/chunk-MZWZQ24W.js +0 -226
- package/dist/chunk-MZWZQ24W.js.map +0 -1
- package/dist/chunk-PAHWRRS3.js.map +0 -1
- package/dist/chunk-PNOEE3ET.js.map +0 -1
- package/dist/chunk-RFSMGUBJ.js.map +0 -1
- package/dist/react/GoogleProductPicker.d.ts +0 -9
- package/dist/sdk/google-discovery.d.ts +0 -43
- package/dist/sdk/google-discovery.test.d.ts +0 -1
- package/dist/sdk/google-oauth-batches.d.ts +0 -13
- package/dist/sdk/google-oauth-batches.test.d.ts +0 -1
- package/dist/sdk/google-oauth-scopes.d.ts +0 -3
- package/dist/sdk/google-oauth-scopes.test.d.ts +0 -1
- package/dist/sdk/google-presets.d.ts +0 -16
- package/dist/sdk/google-presets.test.d.ts +0 -1
- package/dist/sdk/google-product-picker-scopes.test.d.ts +0 -1
- /package/dist/sdk/{google-bundle.test.d.ts → store.test.d.ts} +0 -0
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
-
convertGoogleDiscoveryBundleToOpenApi,
|
|
3
|
-
convertGoogleDiscoveryToOpenApi,
|
|
4
2
|
deriveAuthenticationTemplateFromPreview,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
isGoogleDiscoveryUrl
|
|
8
|
-
} from "./chunk-I2XDCVVM.js";
|
|
3
|
+
firstBaseUrlForPreview
|
|
4
|
+
} from "./chunk-CKBX4SXK.js";
|
|
9
5
|
import {
|
|
10
6
|
openApiPresets
|
|
11
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-QQFCICLX.js";
|
|
12
8
|
import {
|
|
13
9
|
InvocationResult,
|
|
14
10
|
OpenApiExtractionError,
|
|
@@ -18,11 +14,10 @@ import {
|
|
|
18
14
|
extract,
|
|
19
15
|
normalizeOpenApiAuthInputs,
|
|
20
16
|
parse,
|
|
21
|
-
previewSpec,
|
|
22
17
|
previewSpecText,
|
|
23
18
|
resolveServerUrl,
|
|
24
19
|
resolveSpecText
|
|
25
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-KVPUDOJZ.js";
|
|
26
21
|
|
|
27
22
|
// src/sdk/config.ts
|
|
28
23
|
import { Option, Schema } from "effect";
|
|
@@ -46,8 +41,6 @@ var OpenApiIntegrationConfigSchema = Schema.Struct({
|
|
|
46
41
|
specHash: Schema.optional(Schema.String),
|
|
47
42
|
/** Origin URL the spec was fetched from, when known. Enables refresh. */
|
|
48
43
|
sourceUrl: Schema.optional(Schema.String),
|
|
49
|
-
/** Google Discovery bundle URLs, when the spec came from a Google bundle. */
|
|
50
|
-
googleDiscoveryUrls: Schema.optional(Schema.Array(Schema.String)),
|
|
51
44
|
/** Optional base URL override. */
|
|
52
45
|
baseUrl: Schema.optional(Schema.String),
|
|
53
46
|
/** Static headers applied to every request (no secret material). */
|
|
@@ -73,8 +66,101 @@ var requiredTemplateVariables = (template) => {
|
|
|
73
66
|
return requiredPlacementVariables(template.placements);
|
|
74
67
|
};
|
|
75
68
|
|
|
69
|
+
// src/sdk/store.ts
|
|
70
|
+
import { Effect, Option as Option2, Predicate, Schema as Schema2 } from "effect";
|
|
71
|
+
var OPERATION_COLLECTION = "operation";
|
|
72
|
+
var STORE_OWNER = "org";
|
|
73
|
+
var OPERATION_KEY_VERSION = "op";
|
|
74
|
+
var encodeBinding = Schema2.encodeSync(OperationBinding);
|
|
75
|
+
var decodeBinding = Schema2.decodeUnknownSync(OperationBinding);
|
|
76
|
+
var decodeBindingJson = Schema2.decodeUnknownSync(Schema2.fromJsonString(OperationBinding));
|
|
77
|
+
var toJsonRecord = (value) => value;
|
|
78
|
+
var OperationStorage = Schema2.Struct({
|
|
79
|
+
integration: Schema2.String,
|
|
80
|
+
toolName: Schema2.String,
|
|
81
|
+
binding: Schema2.Unknown
|
|
82
|
+
});
|
|
83
|
+
var decodeOperationStorage = Schema2.decodeUnknownOption(OperationStorage);
|
|
84
|
+
var rowToOperation = (row) => {
|
|
85
|
+
const decoded = decodeOperationStorage(row.data);
|
|
86
|
+
if (Option2.isNone(decoded)) return null;
|
|
87
|
+
const operation = decoded.value;
|
|
88
|
+
return {
|
|
89
|
+
integration: operation.integration,
|
|
90
|
+
toolName: operation.toolName,
|
|
91
|
+
binding: decodeBinding(
|
|
92
|
+
typeof operation.binding === "string" ? decodeBindingJson(operation.binding) : operation.binding
|
|
93
|
+
)
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
var stableKeyHash = (value) => {
|
|
97
|
+
let hash = 0xcbf29ce484222325n;
|
|
98
|
+
const prime = 0x100000001b3n;
|
|
99
|
+
const mask = 0xffffffffffffffffn;
|
|
100
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
101
|
+
hash ^= BigInt(value.charCodeAt(index));
|
|
102
|
+
hash = hash * prime & mask;
|
|
103
|
+
}
|
|
104
|
+
return hash.toString(36).padStart(13, "0");
|
|
105
|
+
};
|
|
106
|
+
var operationKey = (integration, toolName) => `${OPERATION_KEY_VERSION}.${stableKeyHash(integration)}.${stableKeyHash(toolName)}`;
|
|
107
|
+
var legacyOperationKey = (integration, toolName) => `${integration}.${toolName}`;
|
|
108
|
+
var specBlobKey = (specHash) => `spec/${specHash}`;
|
|
109
|
+
var makeDefaultOpenapiStore = ({ pluginStorage, blobs }) => {
|
|
110
|
+
const operationData = (operation) => ({
|
|
111
|
+
integration: operation.integration,
|
|
112
|
+
toolName: operation.toolName,
|
|
113
|
+
binding: toJsonRecord(encodeBinding(operation.binding))
|
|
114
|
+
});
|
|
115
|
+
const listRows = (integration) => pluginStorage.list({ collection: OPERATION_COLLECTION }).pipe(
|
|
116
|
+
Effect.map(
|
|
117
|
+
(rows) => rows.filter((row) => rowToOperation(row)?.integration === integration)
|
|
118
|
+
)
|
|
119
|
+
);
|
|
120
|
+
const removeOperations = (integration) => Effect.gen(function* () {
|
|
121
|
+
const rows = yield* listRows(integration);
|
|
122
|
+
yield* pluginStorage.removeMany({
|
|
123
|
+
owner: STORE_OWNER,
|
|
124
|
+
entries: rows.map((row) => ({ collection: OPERATION_COLLECTION, key: row.key }))
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
return {
|
|
128
|
+
putOperations: (integration, operations) => Effect.gen(function* () {
|
|
129
|
+
yield* removeOperations(integration);
|
|
130
|
+
yield* pluginStorage.putMany({
|
|
131
|
+
owner: STORE_OWNER,
|
|
132
|
+
entries: operations.map((operation) => ({
|
|
133
|
+
collection: OPERATION_COLLECTION,
|
|
134
|
+
key: operationKey(integration, operation.toolName),
|
|
135
|
+
data: operationData(operation)
|
|
136
|
+
}))
|
|
137
|
+
});
|
|
138
|
+
}),
|
|
139
|
+
getOperation: (integration, toolName) => Effect.gen(function* () {
|
|
140
|
+
const row = yield* pluginStorage.get({
|
|
141
|
+
collection: OPERATION_COLLECTION,
|
|
142
|
+
key: operationKey(integration, toolName)
|
|
143
|
+
});
|
|
144
|
+
if (row) return rowToOperation(row);
|
|
145
|
+
const legacyKey = legacyOperationKey(integration, toolName);
|
|
146
|
+
if (legacyKey.length > 255) return null;
|
|
147
|
+
const legacyRow = yield* pluginStorage.get({
|
|
148
|
+
collection: OPERATION_COLLECTION,
|
|
149
|
+
key: legacyKey
|
|
150
|
+
});
|
|
151
|
+
return legacyRow ? rowToOperation(legacyRow) : null;
|
|
152
|
+
}),
|
|
153
|
+
listOperations: (integration) => listRows(integration).pipe(
|
|
154
|
+
Effect.map((rows) => rows.map(rowToOperation).filter(Predicate.isNotNull))
|
|
155
|
+
),
|
|
156
|
+
removeOperations,
|
|
157
|
+
putSpec: (specHash, specText) => blobs.put(specBlobKey(specHash), specText, { owner: STORE_OWNER }),
|
|
158
|
+
getSpec: (specHash) => blobs.get(specBlobKey(specHash))
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
|
|
76
162
|
// src/sdk/invoke.ts
|
|
77
|
-
import { Effect, Layer, Option as
|
|
163
|
+
import { Effect as Effect2, Layer, Option as Option3 } from "effect";
|
|
78
164
|
import { HttpClient, HttpClientRequest } from "effect/unstable/http";
|
|
79
165
|
var CONTAINER_KEYS = {
|
|
80
166
|
path: ["path", "pathParams", "params"],
|
|
@@ -107,13 +193,13 @@ var encodeReservedAware = (raw, allowReserved) => {
|
|
|
107
193
|
var queryParamValues = (value, param) => {
|
|
108
194
|
if (value === void 0 || value === null) return [];
|
|
109
195
|
if (!Array.isArray(value)) return [primitiveToString(value)];
|
|
110
|
-
const style =
|
|
111
|
-
const explode =
|
|
196
|
+
const style = Option3.getOrUndefined(param.style) ?? "form";
|
|
197
|
+
const explode = Option3.getOrElse(param.explode, () => true);
|
|
112
198
|
if (explode) return value.map(primitiveToString);
|
|
113
199
|
const separator = style === "spaceDelimited" ? " " : style === "pipeDelimited" ? "|" : ",";
|
|
114
200
|
return [value.map(primitiveToString).join(separator)];
|
|
115
201
|
};
|
|
116
|
-
var resolvePath =
|
|
202
|
+
var resolvePath = Effect2.fn("OpenApi.resolvePath")(function* (pathTemplate, args, parameters) {
|
|
117
203
|
let resolved = pathTemplate;
|
|
118
204
|
for (const param of parameters) {
|
|
119
205
|
if (param.location !== "path") continue;
|
|
@@ -122,14 +208,14 @@ var resolvePath = Effect.fn("OpenApi.resolvePath")(function* (pathTemplate, args
|
|
|
122
208
|
if (param.required) {
|
|
123
209
|
return yield* new OpenApiInvocationError({
|
|
124
210
|
message: `Missing required path parameter: ${param.name}`,
|
|
125
|
-
statusCode:
|
|
211
|
+
statusCode: Option3.none()
|
|
126
212
|
});
|
|
127
213
|
}
|
|
128
214
|
continue;
|
|
129
215
|
}
|
|
130
216
|
const encoded = encodeReservedAware(
|
|
131
217
|
String(value),
|
|
132
|
-
|
|
218
|
+
Option3.getOrElse(param.allowReserved, () => false)
|
|
133
219
|
);
|
|
134
220
|
resolved = resolved.replaceAll(`{${param.name}}`, encoded);
|
|
135
221
|
resolved = resolved.replaceAll(`{+${param.name}}`, encoded);
|
|
@@ -145,7 +231,7 @@ var resolvePath = Effect.fn("OpenApi.resolvePath")(function* (pathTemplate, args
|
|
|
145
231
|
if (unresolved.length > 0) {
|
|
146
232
|
return yield* new OpenApiInvocationError({
|
|
147
233
|
message: `Unresolved path parameters: ${[...new Set(unresolved)].join(", ")}`,
|
|
148
|
-
statusCode:
|
|
234
|
+
statusCode: Option3.none()
|
|
149
235
|
});
|
|
150
236
|
}
|
|
151
237
|
return resolved;
|
|
@@ -172,6 +258,72 @@ var isXmlContentType = (ct) => {
|
|
|
172
258
|
};
|
|
173
259
|
var isTextContentType = (ct) => normalizeContentType(ct).startsWith("text/");
|
|
174
260
|
var isOctetStream = (ct) => normalizeContentType(ct) === "application/octet-stream";
|
|
261
|
+
var bytesToBase64 = (bytes) => {
|
|
262
|
+
let binary = "";
|
|
263
|
+
const chunkSize = 32768;
|
|
264
|
+
for (let i = 0; i < bytes.length; i += chunkSize) {
|
|
265
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + chunkSize));
|
|
266
|
+
}
|
|
267
|
+
return btoa(binary);
|
|
268
|
+
};
|
|
269
|
+
var normalizeBase64 = (value, encoding) => {
|
|
270
|
+
const compact = value.replace(/\s/g, "");
|
|
271
|
+
const alphabet = encoding === "base64url" ? compact.replace(/-/g, "+").replace(/_/g, "/") : compact;
|
|
272
|
+
const remainder = alphabet.length % 4;
|
|
273
|
+
return remainder === 0 ? alphabet : `${alphabet}${"=".repeat(4 - remainder)}`;
|
|
274
|
+
};
|
|
275
|
+
var byteLengthFromBase64 = (base64) => {
|
|
276
|
+
const compact = base64.replace(/\s/g, "");
|
|
277
|
+
const padding = compact.endsWith("==") ? 2 : compact.endsWith("=") ? 1 : 0;
|
|
278
|
+
return Math.max(0, Math.floor(compact.length * 3 / 4) - padding);
|
|
279
|
+
};
|
|
280
|
+
var isGenericMimeType = (mimeType) => normalizeContentType(mimeType) === "application/octet-stream";
|
|
281
|
+
var startsWithBytes = (bytes, prefix) => prefix.every((byte, index) => bytes[index] === byte);
|
|
282
|
+
var isLikelyUtf8Text = (bytes) => {
|
|
283
|
+
if (bytes.length === 0) return false;
|
|
284
|
+
let text;
|
|
285
|
+
try {
|
|
286
|
+
text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
287
|
+
} catch {
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
let suspicious = 0;
|
|
291
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
292
|
+
const code = text.charCodeAt(index);
|
|
293
|
+
const allowedControl = code === 9 || code === 10 || code === 12 || code === 13;
|
|
294
|
+
if (code === 0) return false;
|
|
295
|
+
if (code < 32 && !allowedControl) suspicious += 1;
|
|
296
|
+
}
|
|
297
|
+
return suspicious / Math.max(1, text.length) <= 0.02;
|
|
298
|
+
};
|
|
299
|
+
var sniffMimeType = (bytes) => {
|
|
300
|
+
if (startsWithBytes(bytes, [255, 216, 255])) return "image/jpeg";
|
|
301
|
+
if (startsWithBytes(bytes, [137, 80, 78, 71, 13, 10, 26, 10])) {
|
|
302
|
+
return "image/png";
|
|
303
|
+
}
|
|
304
|
+
if (startsWithBytes(bytes, [71, 73, 70, 56, 55, 97]) || startsWithBytes(bytes, [71, 73, 70, 56, 57, 97])) {
|
|
305
|
+
return "image/gif";
|
|
306
|
+
}
|
|
307
|
+
if (startsWithBytes(bytes, [82, 73, 70, 70]) && bytes[8] === 87 && bytes[9] === 69 && bytes[10] === 66 && bytes[11] === 80) {
|
|
308
|
+
return "image/webp";
|
|
309
|
+
}
|
|
310
|
+
if (startsWithBytes(bytes, [37, 80, 68, 70, 45])) return "application/pdf";
|
|
311
|
+
if (startsWithBytes(bytes, [80, 75, 3, 4]) || startsWithBytes(bytes, [80, 75, 5, 6]) || startsWithBytes(bytes, [80, 75, 7, 8])) {
|
|
312
|
+
return "application/zip";
|
|
313
|
+
}
|
|
314
|
+
if (isLikelyUtf8Text(bytes)) return "text/plain";
|
|
315
|
+
return null;
|
|
316
|
+
};
|
|
317
|
+
var bytesFromBase64Prefix = (base64) => {
|
|
318
|
+
const prefix = base64.slice(0, Math.min(base64.length, 64));
|
|
319
|
+
const binary = atob(prefix);
|
|
320
|
+
const bytes = new Uint8Array(binary.length);
|
|
321
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
322
|
+
bytes[index] = binary.charCodeAt(index);
|
|
323
|
+
}
|
|
324
|
+
return bytes;
|
|
325
|
+
};
|
|
326
|
+
var sniffMimeTypeFromBase64 = (base64) => sniffMimeType(bytesFromBase64Prefix(base64));
|
|
175
327
|
var toUint8Array = (value) => {
|
|
176
328
|
if (value instanceof Uint8Array) return value;
|
|
177
329
|
if (value instanceof ArrayBuffer) return new Uint8Array(value);
|
|
@@ -184,6 +336,37 @@ var toUint8Array = (value) => {
|
|
|
184
336
|
}
|
|
185
337
|
return null;
|
|
186
338
|
};
|
|
339
|
+
var readHintString = (option, fallback) => Option3.getOrElse(option, () => fallback);
|
|
340
|
+
var readHintMimeType = (hint, fallback) => Option3.getOrElse(hint.mimeType, () => fallback);
|
|
341
|
+
var readHintEncoding = (hint) => Option3.getOrElse(hint.encoding, () => "base64");
|
|
342
|
+
var fileFromByteField = (body, hint) => {
|
|
343
|
+
if (typeof body !== "object" || body === null || Array.isArray(body)) return null;
|
|
344
|
+
const record = body;
|
|
345
|
+
const dataField = readHintString(hint.dataField, "data");
|
|
346
|
+
const rawData = record[dataField];
|
|
347
|
+
if (typeof rawData !== "string") return null;
|
|
348
|
+
const data = normalizeBase64(rawData, readHintEncoding(hint));
|
|
349
|
+
const sizeField = Option3.getOrUndefined(hint.sizeField);
|
|
350
|
+
const byteLength = sizeField && typeof record[sizeField] === "number" ? record[sizeField] : byteLengthFromBase64(data);
|
|
351
|
+
const hintedMimeType = readHintMimeType(hint, "application/octet-stream");
|
|
352
|
+
return {
|
|
353
|
+
_tag: "ToolFile",
|
|
354
|
+
mimeType: isGenericMimeType(hintedMimeType) ? sniffMimeTypeFromBase64(data) ?? hintedMimeType : hintedMimeType,
|
|
355
|
+
encoding: "base64",
|
|
356
|
+
data,
|
|
357
|
+
byteLength
|
|
358
|
+
};
|
|
359
|
+
};
|
|
360
|
+
var fileFromBinaryBytes = (bytes, hint, contentType) => {
|
|
361
|
+
const hintedMimeType = contentType ?? readHintMimeType(hint, "application/octet-stream");
|
|
362
|
+
return {
|
|
363
|
+
_tag: "ToolFile",
|
|
364
|
+
mimeType: isGenericMimeType(hintedMimeType) ? sniffMimeType(bytes) ?? hintedMimeType : hintedMimeType,
|
|
365
|
+
encoding: "base64",
|
|
366
|
+
data: bytesToBase64(bytes),
|
|
367
|
+
byteLength: bytes.byteLength
|
|
368
|
+
};
|
|
369
|
+
};
|
|
187
370
|
var toArrayBuffer = (bytes) => {
|
|
188
371
|
const copy = new ArrayBuffer(bytes.byteLength);
|
|
189
372
|
new Uint8Array(copy).set(bytes);
|
|
@@ -197,9 +380,9 @@ var DEFAULT_FORM_STYLE = {
|
|
|
197
380
|
var resolveStyleExplode = (e) => {
|
|
198
381
|
if (!e) return DEFAULT_FORM_STYLE;
|
|
199
382
|
return {
|
|
200
|
-
style:
|
|
201
|
-
explode:
|
|
202
|
-
allowReserved:
|
|
383
|
+
style: Option3.getOrElse(e.style, () => DEFAULT_FORM_STYLE.style),
|
|
384
|
+
explode: Option3.getOrElse(e.explode, () => DEFAULT_FORM_STYLE.explode),
|
|
385
|
+
allowReserved: Option3.getOrElse(e.allowReserved, () => DEFAULT_FORM_STYLE.allowReserved)
|
|
203
386
|
};
|
|
204
387
|
};
|
|
205
388
|
var encodeFormValue = (v, allowReserved) => {
|
|
@@ -259,7 +442,7 @@ var coerceFormDataRecord = (value, encoding) => {
|
|
|
259
442
|
const out = {};
|
|
260
443
|
for (const [key, raw] of Object.entries(value)) {
|
|
261
444
|
if (raw === void 0 || raw === null) continue;
|
|
262
|
-
const partType = encoding?.[key] ?
|
|
445
|
+
const partType = encoding?.[key] ? Option3.getOrUndefined(encoding[key].contentType) : void 0;
|
|
263
446
|
if (partType) {
|
|
264
447
|
const isJson = partType.startsWith("application/json") || partType.includes("+json");
|
|
265
448
|
const serialized = typeof raw === "string" ? raw : isJson ? JSON.stringify(raw) : typeof raw === "object" ? JSON.stringify(raw) : String(raw);
|
|
@@ -340,9 +523,9 @@ var applyRequestBody = (request, contentType, bodyValue, encoding) => {
|
|
|
340
523
|
if (bytes) return HttpClientRequest.bodyUint8Array(request, bytes, contentType);
|
|
341
524
|
return HttpClientRequest.bodyText(request, JSON.stringify(bodyValue), contentType);
|
|
342
525
|
};
|
|
343
|
-
var invoke =
|
|
526
|
+
var invoke = Effect2.fn("OpenApi.invoke")(function* (operation, args, resolvedHeaders, sourceQueryParams = {}) {
|
|
344
527
|
const client = yield* HttpClient.HttpClient;
|
|
345
|
-
yield*
|
|
528
|
+
yield* Effect2.annotateCurrentSpan({
|
|
346
529
|
"http.method": operation.method.toUpperCase(),
|
|
347
530
|
"http.route": operation.pathTemplate,
|
|
348
531
|
"plugin.openapi.method": operation.method.toUpperCase(),
|
|
@@ -368,50 +551,57 @@ var invoke = Effect.fn("OpenApi.invoke")(function* (operation, args, resolvedHea
|
|
|
368
551
|
if (value === void 0 || value === null) continue;
|
|
369
552
|
request = HttpClientRequest.setHeader(request, param.name, String(value));
|
|
370
553
|
}
|
|
371
|
-
if (
|
|
554
|
+
if (Option3.isSome(operation.requestBody)) {
|
|
372
555
|
const rb = operation.requestBody.value;
|
|
373
556
|
const bodyValue = args.body ?? args.input;
|
|
374
557
|
if (bodyValue !== void 0) {
|
|
375
|
-
const contentsOpt =
|
|
558
|
+
const contentsOpt = Option3.getOrUndefined(rb.contents);
|
|
376
559
|
const requestedCt = typeof args.contentType === "string" ? args.contentType : void 0;
|
|
377
560
|
const selected = contentsOpt && requestedCt ? contentsOpt.find((c) => c.contentType === requestedCt) : void 0;
|
|
378
561
|
const chosenCt = selected?.contentType ?? rb.contentType;
|
|
379
|
-
const chosenEncoding = selected ?
|
|
562
|
+
const chosenEncoding = selected ? Option3.getOrUndefined(selected.encoding) : contentsOpt && contentsOpt[0] ? Option3.getOrUndefined(contentsOpt[0].encoding) : void 0;
|
|
380
563
|
request = applyRequestBody(request, chosenCt, bodyValue, chosenEncoding);
|
|
381
564
|
}
|
|
382
565
|
}
|
|
383
566
|
request = applyHeaders(request, resolvedHeaders);
|
|
384
567
|
const response = yield* client.execute(request).pipe(
|
|
385
|
-
|
|
568
|
+
Effect2.mapError(
|
|
386
569
|
(err) => new OpenApiInvocationError({
|
|
387
570
|
message: "HTTP request failed",
|
|
388
|
-
statusCode:
|
|
571
|
+
statusCode: Option3.none(),
|
|
389
572
|
cause: err
|
|
390
573
|
})
|
|
391
574
|
)
|
|
392
575
|
);
|
|
393
576
|
const status = response.status;
|
|
394
|
-
yield*
|
|
577
|
+
yield* Effect2.annotateCurrentSpan({
|
|
395
578
|
"http.status_code": status
|
|
396
579
|
});
|
|
397
580
|
const responseHeaders = { ...response.headers };
|
|
398
581
|
const contentType = response.headers["content-type"] ?? null;
|
|
399
|
-
const mapBodyError =
|
|
582
|
+
const mapBodyError = Effect2.mapError(
|
|
400
583
|
(err) => new OpenApiInvocationError({
|
|
401
584
|
message: "Failed to read response body",
|
|
402
|
-
statusCode:
|
|
585
|
+
statusCode: Option3.some(status),
|
|
403
586
|
cause: err
|
|
404
587
|
})
|
|
405
588
|
);
|
|
406
|
-
const
|
|
407
|
-
|
|
589
|
+
const responseBodyBinding = Option3.getOrUndefined(operation.responseBody);
|
|
590
|
+
const fileHint = responseBodyBinding ? Option3.getOrUndefined(responseBodyBinding.fileHint) : void 0;
|
|
591
|
+
const ok = status >= 200 && status < 300;
|
|
592
|
+
const responseBody = status === 204 ? null : ok && fileHint?.kind === "binaryResponse" ? fileFromBinaryBytes(
|
|
593
|
+
new Uint8Array(yield* response.arrayBuffer.pipe(mapBodyError)),
|
|
594
|
+
fileHint,
|
|
595
|
+
contentType
|
|
596
|
+
) : isJsonContentType(contentType) ? yield* response.json.pipe(
|
|
597
|
+
Effect2.catch(() => response.text),
|
|
408
598
|
mapBodyError
|
|
409
599
|
) : yield* response.text.pipe(mapBodyError);
|
|
410
|
-
const
|
|
600
|
+
const dataBody = ok && fileHint?.kind === "byteField" ? fileFromByteField(responseBody, fileHint) ?? responseBody : responseBody;
|
|
411
601
|
return InvocationResult.make({
|
|
412
602
|
status,
|
|
413
603
|
headers: responseHeaders,
|
|
414
|
-
data: ok ?
|
|
604
|
+
data: ok ? dataBody : null,
|
|
415
605
|
error: ok ? null : responseBody
|
|
416
606
|
});
|
|
417
607
|
});
|
|
@@ -426,25 +616,25 @@ var resolveRequestHost = (servers, serverArg, baseUrl) => {
|
|
|
426
616
|
if (value != null && value !== "") overrides[name] = String(value);
|
|
427
617
|
}
|
|
428
618
|
}
|
|
429
|
-
return resolveServerUrl(chosen.url,
|
|
619
|
+
return resolveServerUrl(chosen.url, Option3.getOrUndefined(chosen.variables), overrides);
|
|
430
620
|
};
|
|
431
621
|
var invokeWithLayer = (operation, args, baseUrl, resolvedHeaders, sourceQueryParams, httpClientLayer) => {
|
|
432
622
|
const effectiveBaseUrl = resolveRequestHost(operation.servers ?? [], args.server, baseUrl);
|
|
433
623
|
const clientWithBaseUrl = effectiveBaseUrl ? Layer.effect(
|
|
434
624
|
HttpClient.HttpClient,
|
|
435
|
-
|
|
436
|
-
|
|
625
|
+
Effect2.map(
|
|
626
|
+
Effect2.service(HttpClient.HttpClient),
|
|
437
627
|
HttpClient.mapRequest(HttpClientRequest.prependUrl(effectiveBaseUrl))
|
|
438
628
|
)
|
|
439
629
|
).pipe(Layer.provide(httpClientLayer)) : httpClientLayer;
|
|
440
630
|
return invoke(operation, args, resolvedHeaders, sourceQueryParams).pipe(
|
|
441
|
-
|
|
631
|
+
Effect2.provide(clientWithBaseUrl),
|
|
442
632
|
// `invoke` annotates http.status_code on ITS span (`OpenApi.invoke`,
|
|
443
633
|
// via Effect.fn) — annotateCurrentSpan inside it never reaches this
|
|
444
634
|
// wrapper span. Stamp the status here too so queries against
|
|
445
635
|
// `plugin.openapi.invoke` see the upstream outcome directly.
|
|
446
|
-
|
|
447
|
-
|
|
636
|
+
Effect2.tap((result) => Effect2.annotateCurrentSpan({ "http.status_code": result.status })),
|
|
637
|
+
Effect2.withSpan("plugin.openapi.invoke", {
|
|
448
638
|
attributes: {
|
|
449
639
|
"plugin.openapi.method": operation.method.toUpperCase(),
|
|
450
640
|
"plugin.openapi.path_template": operation.pathTemplate,
|
|
@@ -463,88 +653,13 @@ var annotationsForOperation = (method, pathTemplate) => {
|
|
|
463
653
|
};
|
|
464
654
|
};
|
|
465
655
|
|
|
466
|
-
// src/sdk/
|
|
467
|
-
import { Effect as Effect2, Option as Option3, Predicate, Schema as Schema2 } from "effect";
|
|
468
|
-
var OPERATION_COLLECTION = "operation";
|
|
469
|
-
var STORE_OWNER = "org";
|
|
470
|
-
var encodeBinding = Schema2.encodeSync(OperationBinding);
|
|
471
|
-
var decodeBinding = Schema2.decodeUnknownSync(OperationBinding);
|
|
472
|
-
var decodeBindingJson = Schema2.decodeUnknownSync(Schema2.fromJsonString(OperationBinding));
|
|
473
|
-
var toJsonRecord = (value) => value;
|
|
474
|
-
var OperationStorage = Schema2.Struct({
|
|
475
|
-
integration: Schema2.String,
|
|
476
|
-
toolName: Schema2.String,
|
|
477
|
-
binding: Schema2.Unknown
|
|
478
|
-
});
|
|
479
|
-
var decodeOperationStorage = Schema2.decodeUnknownOption(OperationStorage);
|
|
480
|
-
var rowToOperation = (row) => {
|
|
481
|
-
const decoded = decodeOperationStorage(row.data);
|
|
482
|
-
if (Option3.isNone(decoded)) return null;
|
|
483
|
-
const operation = decoded.value;
|
|
484
|
-
return {
|
|
485
|
-
integration: operation.integration,
|
|
486
|
-
toolName: operation.toolName,
|
|
487
|
-
binding: decodeBinding(
|
|
488
|
-
typeof operation.binding === "string" ? decodeBindingJson(operation.binding) : operation.binding
|
|
489
|
-
)
|
|
490
|
-
};
|
|
491
|
-
};
|
|
492
|
-
var operationKey = (integration, toolName) => `${integration}.${toolName}`;
|
|
493
|
-
var specBlobKey = (specHash) => `spec/${specHash}`;
|
|
494
|
-
var makeDefaultOpenapiStore = ({ pluginStorage, blobs }) => {
|
|
495
|
-
const operationData = (operation) => ({
|
|
496
|
-
integration: operation.integration,
|
|
497
|
-
toolName: operation.toolName,
|
|
498
|
-
binding: toJsonRecord(encodeBinding(operation.binding))
|
|
499
|
-
});
|
|
500
|
-
const listRows = (integration) => pluginStorage.list({ collection: OPERATION_COLLECTION, keyPrefix: `${integration}.` }).pipe(
|
|
501
|
-
Effect2.map(
|
|
502
|
-
(rows) => rows.filter((row) => rowToOperation(row)?.integration === integration)
|
|
503
|
-
)
|
|
504
|
-
);
|
|
505
|
-
const removeOperations = (integration) => Effect2.gen(function* () {
|
|
506
|
-
const rows = yield* listRows(integration);
|
|
507
|
-
yield* pluginStorage.removeMany({
|
|
508
|
-
owner: STORE_OWNER,
|
|
509
|
-
entries: rows.map((row) => ({ collection: OPERATION_COLLECTION, key: row.key }))
|
|
510
|
-
});
|
|
511
|
-
});
|
|
512
|
-
return {
|
|
513
|
-
putOperations: (integration, operations) => Effect2.gen(function* () {
|
|
514
|
-
yield* removeOperations(integration);
|
|
515
|
-
yield* pluginStorage.putMany({
|
|
516
|
-
owner: STORE_OWNER,
|
|
517
|
-
entries: operations.map((operation) => ({
|
|
518
|
-
collection: OPERATION_COLLECTION,
|
|
519
|
-
key: operationKey(integration, operation.toolName),
|
|
520
|
-
data: operationData(operation)
|
|
521
|
-
}))
|
|
522
|
-
});
|
|
523
|
-
}),
|
|
524
|
-
getOperation: (integration, toolName) => pluginStorage.get({ collection: OPERATION_COLLECTION, key: operationKey(integration, toolName) }).pipe(Effect2.map((row) => row ? rowToOperation(row) : null)),
|
|
525
|
-
listOperations: (integration) => listRows(integration).pipe(
|
|
526
|
-
Effect2.map((rows) => rows.map(rowToOperation).filter(Predicate.isNotNull))
|
|
527
|
-
),
|
|
528
|
-
removeOperations,
|
|
529
|
-
putSpec: (specHash, specText) => blobs.put(specBlobKey(specHash), specText, { owner: STORE_OWNER }),
|
|
530
|
-
getSpec: (specHash) => blobs.get(specBlobKey(specHash))
|
|
531
|
-
};
|
|
532
|
-
};
|
|
533
|
-
|
|
534
|
-
// src/sdk/plugin.ts
|
|
656
|
+
// src/sdk/backing.ts
|
|
535
657
|
import { Effect as Effect3, Option as Option5, Schema as Schema3 } from "effect";
|
|
536
658
|
import {
|
|
537
|
-
|
|
538
|
-
IntegrationDetectionResult,
|
|
539
|
-
IntegrationNotFoundError,
|
|
540
|
-
IntegrationSlug,
|
|
659
|
+
ToolFileJsonSchema,
|
|
541
660
|
ToolName,
|
|
542
661
|
ToolResult,
|
|
543
|
-
authToolFailure
|
|
544
|
-
definePlugin,
|
|
545
|
-
mergeAuthTemplates,
|
|
546
|
-
sha256Hex,
|
|
547
|
-
tool
|
|
662
|
+
authToolFailure
|
|
548
663
|
} from "@executor-js/sdk/core";
|
|
549
664
|
|
|
550
665
|
// src/sdk/definitions.ts
|
|
@@ -692,8 +807,7 @@ var compileToolDefinitions = (operations) => {
|
|
|
692
807
|
return resolveCollisions(raw).sort((a, b) => a.toolPath.localeCompare(b.toolPath));
|
|
693
808
|
};
|
|
694
809
|
|
|
695
|
-
// src/sdk/
|
|
696
|
-
import { ApiKeyAuthTemplate, describeApiKeyAuthMethod } from "@executor-js/sdk/http-auth";
|
|
810
|
+
// src/sdk/backing.ts
|
|
697
811
|
var STRINGIFIED_BODY_CAP = 1024;
|
|
698
812
|
var UpstreamMessageBody = Schema3.Struct({ message: Schema3.String });
|
|
699
813
|
var UpstreamErrorMessageBody = Schema3.Struct({ errorMessage: Schema3.String });
|
|
@@ -727,7 +841,7 @@ var clampedStringify = (value) => {
|
|
|
727
841
|
return s.length > STRINGIFIED_BODY_CAP ? `${s.slice(0, STRINGIFIED_BODY_CAP)}\u2026` : s;
|
|
728
842
|
};
|
|
729
843
|
var firstNonEmpty = (...values) => values.find((value) => value !== void 0 && value.length > 0);
|
|
730
|
-
var
|
|
844
|
+
var extractOpenApiUpstreamMessage = (body, status) => {
|
|
731
845
|
if (typeof body === "string") {
|
|
732
846
|
return body.length > 0 ? body : `Upstream returned HTTP ${status}`;
|
|
733
847
|
}
|
|
@@ -758,162 +872,353 @@ var extractUpstreamMessage = (body, status) => {
|
|
|
758
872
|
}
|
|
759
873
|
return `Upstream returned HTTP ${status}`;
|
|
760
874
|
};
|
|
761
|
-
var
|
|
762
|
-
|
|
875
|
+
var openApiAuthToolFailure = (failure) => authToolFailure({
|
|
876
|
+
code: failure.code,
|
|
877
|
+
message: failure.message,
|
|
878
|
+
source: { id: failure.integration, scope: failure.owner },
|
|
879
|
+
credential: {
|
|
880
|
+
kind: failure.credentialKind,
|
|
881
|
+
...failure.credentialLabel ? { label: failure.credentialLabel } : {}
|
|
882
|
+
},
|
|
883
|
+
...failure.status !== void 0 ? { status: failure.status } : {},
|
|
884
|
+
...failure.details !== void 0 ? {
|
|
885
|
+
upstream: {
|
|
886
|
+
...failure.status !== void 0 ? { status: failure.status } : {},
|
|
887
|
+
details: failure.details
|
|
888
|
+
}
|
|
889
|
+
} : {}
|
|
890
|
+
});
|
|
891
|
+
var normalizeOpenApiRefs = (node) => {
|
|
892
|
+
if (node == null || typeof node !== "object") return node;
|
|
893
|
+
if (Array.isArray(node)) {
|
|
894
|
+
let changed2 = false;
|
|
895
|
+
const out = node.map((item) => {
|
|
896
|
+
const n = normalizeOpenApiRefs(item);
|
|
897
|
+
if (n !== item) changed2 = true;
|
|
898
|
+
return n;
|
|
899
|
+
});
|
|
900
|
+
return changed2 ? out : node;
|
|
901
|
+
}
|
|
902
|
+
const obj = node;
|
|
903
|
+
if (typeof obj.$ref === "string") {
|
|
904
|
+
const match = obj.$ref.match(/^#\/components\/schemas\/(.+)$/);
|
|
905
|
+
if (match) return { ...obj, $ref: `#/$defs/${match[1]}` };
|
|
906
|
+
return obj;
|
|
907
|
+
}
|
|
908
|
+
let changed = false;
|
|
909
|
+
const result = {};
|
|
910
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
911
|
+
const n = normalizeOpenApiRefs(v);
|
|
912
|
+
if (n !== v) changed = true;
|
|
913
|
+
result[k] = n;
|
|
914
|
+
}
|
|
915
|
+
return changed ? result : obj;
|
|
916
|
+
};
|
|
917
|
+
var toBinding = (def) => OperationBinding.make({
|
|
918
|
+
method: def.operation.method,
|
|
919
|
+
servers: def.operation.servers,
|
|
920
|
+
pathTemplate: def.operation.pathTemplate,
|
|
921
|
+
parameters: [...def.operation.parameters],
|
|
922
|
+
requestBody: def.operation.requestBody,
|
|
923
|
+
responseBody: def.operation.responseBody
|
|
763
924
|
});
|
|
764
|
-
var
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
925
|
+
var descriptionFor = (def) => {
|
|
926
|
+
const op = def.operation;
|
|
927
|
+
return Option5.getOrElse(
|
|
928
|
+
op.description,
|
|
929
|
+
() => Option5.getOrElse(op.summary, () => `${op.method.toUpperCase()} ${op.pathTemplate}`)
|
|
930
|
+
);
|
|
931
|
+
};
|
|
932
|
+
var compileOpenApiDocument = (doc) => Effect3.gen(function* () {
|
|
933
|
+
const result = yield* extract(doc);
|
|
934
|
+
const hoistedDefs = {};
|
|
935
|
+
if (doc.components?.schemas) {
|
|
936
|
+
for (const [k, v] of Object.entries(doc.components.schemas)) {
|
|
937
|
+
hoistedDefs[k] = normalizeOpenApiRefs(v);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
return {
|
|
941
|
+
definitions: compileToolDefinitions(result.operations),
|
|
942
|
+
hoistedDefs,
|
|
943
|
+
title: Option5.getOrUndefined(result.title),
|
|
944
|
+
description: Option5.getOrUndefined(result.description)
|
|
945
|
+
};
|
|
768
946
|
});
|
|
769
|
-
var
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
variables: Schema3.NullOr(Schema3.Record(Schema3.String, StaticPreviewServerVariableSchema))
|
|
947
|
+
var compileOpenApiSpec = (specText) => Effect3.gen(function* () {
|
|
948
|
+
const doc = yield* parse(specText);
|
|
949
|
+
return yield* compileOpenApiDocument(doc);
|
|
773
950
|
});
|
|
774
|
-
var
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
951
|
+
var openApiToolDefsFromCompiled = (compiled) => compiled.definitions.map(
|
|
952
|
+
(def) => ({
|
|
953
|
+
name: ToolName.make(def.toolPath),
|
|
954
|
+
description: descriptionFor(def),
|
|
955
|
+
inputSchema: normalizeOpenApiRefs(Option5.getOrUndefined(def.operation.inputSchema)),
|
|
956
|
+
outputSchema: Option5.match(def.operation.responseBody, {
|
|
957
|
+
onNone: () => normalizeOpenApiRefs(Option5.getOrUndefined(def.operation.outputSchema)),
|
|
958
|
+
onSome: (responseBody) => Option5.isSome(responseBody.fileHint) ? ToolFileJsonSchema : normalizeOpenApiRefs(Option5.getOrUndefined(def.operation.outputSchema))
|
|
959
|
+
}),
|
|
960
|
+
annotations: annotationsForOperation(def.operation.method, def.operation.pathTemplate)
|
|
961
|
+
})
|
|
962
|
+
);
|
|
963
|
+
var openApiStoredOperationsFromCompiled = (integration, compiled) => compiled.definitions.map((def) => ({
|
|
964
|
+
integration,
|
|
965
|
+
toolName: def.toolPath,
|
|
966
|
+
binding: toBinding(def)
|
|
967
|
+
}));
|
|
968
|
+
var loadOpenApiSpecText = (storage, config) => config.specHash != null ? storage.getSpec(config.specHash) : Effect3.succeed(null);
|
|
969
|
+
var resolveOpenApiBackedTools = ({
|
|
970
|
+
config,
|
|
971
|
+
storage
|
|
972
|
+
}) => Effect3.gen(function* () {
|
|
973
|
+
const openApiConfig = decodeOpenApiIntegrationConfig(config);
|
|
974
|
+
if (!openApiConfig) return { tools: [], definitions: {} };
|
|
975
|
+
const specText = yield* loadOpenApiSpecText(storage, openApiConfig);
|
|
976
|
+
if (specText == null) return { tools: [], definitions: {} };
|
|
977
|
+
const compiled = yield* compileOpenApiSpec(specText).pipe(
|
|
978
|
+
Effect3.catch(() => Effect3.succeed(null))
|
|
979
|
+
);
|
|
980
|
+
if (!compiled) return { tools: [], definitions: {} };
|
|
981
|
+
return {
|
|
982
|
+
tools: openApiToolDefsFromCompiled(compiled),
|
|
983
|
+
definitions: compiled.hoistedDefs
|
|
984
|
+
};
|
|
779
985
|
});
|
|
780
|
-
var
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
986
|
+
var invokeOpenApiBackedTool = (input) => Effect3.gen(function* () {
|
|
987
|
+
const integration = input.toolRow.integration;
|
|
988
|
+
const config = decodeOpenApiIntegrationConfig(input.credential.config);
|
|
989
|
+
let binding = (yield* input.ctx.storage.getOperation(integration, input.toolRow.name))?.binding;
|
|
990
|
+
if ((!binding || Option5.isNone(binding.responseBody)) && config) {
|
|
991
|
+
const specText = yield* loadOpenApiSpecText(input.ctx.storage, config).pipe(
|
|
992
|
+
Effect3.catch(() => Effect3.succeed(null))
|
|
993
|
+
);
|
|
994
|
+
const compiled = specText == null ? null : yield* compileOpenApiSpec(specText).pipe(Effect3.catch(() => Effect3.succeed(null)));
|
|
995
|
+
binding = compiled ? openApiStoredOperationsFromCompiled(integration, compiled).find(
|
|
996
|
+
(op) => op.toolName === input.toolRow.name
|
|
997
|
+
)?.binding : void 0;
|
|
998
|
+
}
|
|
999
|
+
if (!binding) {
|
|
1000
|
+
return yield* new OpenApiExtractionError({
|
|
1001
|
+
message: `No OpenAPI operation found for tool "${input.toolRow.name}" on "${integration}"`
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
const headers = { ...config?.headers ?? {} };
|
|
1005
|
+
const queryParams = {
|
|
1006
|
+
...config?.queryParams ?? {}
|
|
1007
|
+
};
|
|
1008
|
+
const template = (config?.authenticationTemplate ?? []).find(
|
|
1009
|
+
(entry) => String(entry.slug) === String(input.credential.template)
|
|
1010
|
+
);
|
|
1011
|
+
if (template) {
|
|
1012
|
+
const missing = requiredTemplateVariables(template).filter((name) => {
|
|
1013
|
+
const value = input.credential.values[name];
|
|
1014
|
+
return value == null || value === "";
|
|
1015
|
+
});
|
|
1016
|
+
if (missing.length > 0) {
|
|
1017
|
+
return openApiAuthToolFailure({
|
|
1018
|
+
code: template.kind === "oauth2" ? "oauth_connection_missing" : "connection_value_missing",
|
|
1019
|
+
message: `Connection "${input.credential.connection}" for "${integration}" has no resolvable credential value. Re-authenticate or update the connection.`,
|
|
1020
|
+
owner: input.credential.owner,
|
|
1021
|
+
integration,
|
|
1022
|
+
connection: String(input.credential.connection),
|
|
1023
|
+
credentialKind: template.kind === "oauth2" ? "oauth" : "secret"
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
const rendered = renderAuthTemplate(template, input.credential.values);
|
|
1027
|
+
Object.assign(headers, rendered.headers);
|
|
1028
|
+
Object.assign(queryParams, rendered.queryParams);
|
|
1029
|
+
}
|
|
1030
|
+
const result = yield* invokeWithLayer(
|
|
1031
|
+
binding,
|
|
1032
|
+
input.args ?? {},
|
|
1033
|
+
config?.baseUrl ?? "",
|
|
1034
|
+
headers,
|
|
1035
|
+
queryParams,
|
|
1036
|
+
input.httpClientLayer
|
|
1037
|
+
);
|
|
1038
|
+
const ok = result.status >= 200 && result.status < 300;
|
|
1039
|
+
if (!ok) {
|
|
1040
|
+
if (result.status === 401 || result.status === 403) {
|
|
1041
|
+
return openApiAuthToolFailure({
|
|
1042
|
+
code: "connection_rejected",
|
|
1043
|
+
status: result.status,
|
|
1044
|
+
message: `Upstream rejected credentials for "${integration}" with HTTP ${result.status}. Re-authenticate or update the connection "${input.credential.connection}" before retrying this tool.`,
|
|
1045
|
+
owner: input.credential.owner,
|
|
1046
|
+
integration,
|
|
1047
|
+
connection: String(input.credential.connection),
|
|
1048
|
+
credentialKind: "upstream",
|
|
1049
|
+
credentialLabel: "Upstream authorization",
|
|
1050
|
+
details: result.error
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
return ToolResult.fail({
|
|
1054
|
+
code: "upstream_http_error",
|
|
1055
|
+
status: result.status,
|
|
1056
|
+
message: extractOpenApiUpstreamMessage(result.error, result.status),
|
|
1057
|
+
details: result.error
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
return ToolResult.ok(result.data, {
|
|
1061
|
+
http: { status: result.status, headers: result.headers }
|
|
1062
|
+
});
|
|
1063
|
+
});
|
|
1064
|
+
var resolveOpenApiBackedAnnotations = (input) => Effect3.gen(function* () {
|
|
1065
|
+
const ops = yield* input.ctx.storage.listOperations(String(input.integration));
|
|
1066
|
+
const byName = /* @__PURE__ */ new Map();
|
|
1067
|
+
for (const op of ops) byName.set(op.toolName, op.binding);
|
|
1068
|
+
const out = {};
|
|
1069
|
+
for (const row of input.toolRows) {
|
|
1070
|
+
const binding = byName.get(row.name);
|
|
1071
|
+
if (binding) {
|
|
1072
|
+
out[row.name] = annotationsForOperation(binding.method, binding.pathTemplate);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
return out;
|
|
1076
|
+
});
|
|
1077
|
+
|
|
1078
|
+
// src/sdk/plugin.ts
|
|
1079
|
+
import { Effect as Effect4, Option as Option6, Schema as Schema4 } from "effect";
|
|
1080
|
+
import {
|
|
1081
|
+
IntegrationAlreadyExistsError,
|
|
1082
|
+
IntegrationDetectionResult,
|
|
1083
|
+
IntegrationNotFoundError,
|
|
1084
|
+
IntegrationSlug,
|
|
1085
|
+
ToolResult as ToolResult2,
|
|
1086
|
+
definePlugin,
|
|
1087
|
+
mergeAuthTemplates,
|
|
1088
|
+
sha256Hex,
|
|
1089
|
+
tool
|
|
1090
|
+
} from "@executor-js/sdk/core";
|
|
1091
|
+
import { ApiKeyAuthTemplate, describeApiKeyAuthMethod } from "@executor-js/sdk/http-auth";
|
|
1092
|
+
var PreviewSpecInputSchema = Schema4.Struct({
|
|
1093
|
+
spec: Schema4.String
|
|
1094
|
+
});
|
|
1095
|
+
var StaticPreviewServerVariableSchema = Schema4.Struct({
|
|
1096
|
+
default: Schema4.String,
|
|
1097
|
+
enum: Schema4.NullOr(Schema4.Array(Schema4.String)),
|
|
1098
|
+
description: Schema4.NullOr(Schema4.String)
|
|
1099
|
+
});
|
|
1100
|
+
var StaticPreviewServerSchema = Schema4.Struct({
|
|
1101
|
+
url: Schema4.String,
|
|
1102
|
+
description: Schema4.NullOr(Schema4.String),
|
|
1103
|
+
variables: Schema4.NullOr(Schema4.Record(Schema4.String, StaticPreviewServerVariableSchema))
|
|
784
1104
|
});
|
|
785
|
-
var
|
|
786
|
-
|
|
787
|
-
|
|
1105
|
+
var StaticPreviewOAuthAuthorizationCodeFlowSchema = Schema4.Struct({
|
|
1106
|
+
authorizationUrl: Schema4.String,
|
|
1107
|
+
tokenUrl: Schema4.String,
|
|
1108
|
+
refreshUrl: Schema4.NullOr(Schema4.String),
|
|
1109
|
+
scopes: Schema4.Record(Schema4.String, Schema4.String)
|
|
788
1110
|
});
|
|
789
|
-
var
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
bearerFormat: Schema3.NullOr(Schema3.String),
|
|
794
|
-
in: Schema3.NullOr(Schema3.Literals(["header", "query", "cookie"])),
|
|
795
|
-
headerName: Schema3.NullOr(Schema3.String),
|
|
796
|
-
description: Schema3.NullOr(Schema3.String),
|
|
797
|
-
flows: Schema3.NullOr(StaticPreviewOAuthFlowsSchema),
|
|
798
|
-
openIdConnectUrl: Schema3.NullOr(Schema3.String)
|
|
1111
|
+
var StaticPreviewOAuthClientCredentialsFlowSchema = Schema4.Struct({
|
|
1112
|
+
tokenUrl: Schema4.String,
|
|
1113
|
+
refreshUrl: Schema4.NullOr(Schema4.String),
|
|
1114
|
+
scopes: Schema4.Record(Schema4.String, Schema4.String)
|
|
799
1115
|
});
|
|
800
|
-
var
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
1116
|
+
var StaticPreviewOAuthFlowsSchema = Schema4.Struct({
|
|
1117
|
+
authorizationCode: Schema4.NullOr(StaticPreviewOAuthAuthorizationCodeFlowSchema),
|
|
1118
|
+
clientCredentials: Schema4.NullOr(StaticPreviewOAuthClientCredentialsFlowSchema)
|
|
1119
|
+
});
|
|
1120
|
+
var StaticPreviewSecuritySchemeSchema = Schema4.Struct({
|
|
1121
|
+
name: Schema4.String,
|
|
1122
|
+
type: Schema4.Literals(["http", "apiKey", "oauth2", "openIdConnect"]),
|
|
1123
|
+
scheme: Schema4.NullOr(Schema4.String),
|
|
1124
|
+
bearerFormat: Schema4.NullOr(Schema4.String),
|
|
1125
|
+
in: Schema4.NullOr(Schema4.Literals(["header", "query", "cookie"])),
|
|
1126
|
+
headerName: Schema4.NullOr(Schema4.String),
|
|
1127
|
+
description: Schema4.NullOr(Schema4.String),
|
|
1128
|
+
flows: Schema4.NullOr(StaticPreviewOAuthFlowsSchema),
|
|
1129
|
+
openIdConnectUrl: Schema4.NullOr(Schema4.String)
|
|
1130
|
+
});
|
|
1131
|
+
var StaticPreviewOAuth2PresetSchema = Schema4.Struct({
|
|
1132
|
+
label: Schema4.String,
|
|
1133
|
+
securitySchemeName: Schema4.String,
|
|
1134
|
+
flow: Schema4.Literals(["authorizationCode", "clientCredentials"]),
|
|
1135
|
+
authorizationUrl: Schema4.NullOr(Schema4.String),
|
|
1136
|
+
tokenUrl: Schema4.String,
|
|
1137
|
+
refreshUrl: Schema4.NullOr(Schema4.String),
|
|
1138
|
+
scopes: Schema4.Record(Schema4.String, Schema4.String),
|
|
1139
|
+
identityScopes: Schema4.Union([
|
|
1140
|
+
Schema4.Literal("auto"),
|
|
1141
|
+
Schema4.Literal(false),
|
|
1142
|
+
Schema4.Array(Schema4.String)
|
|
812
1143
|
])
|
|
813
1144
|
});
|
|
814
|
-
var StaticPreviewSpecOutputSchema =
|
|
815
|
-
title:
|
|
816
|
-
version:
|
|
817
|
-
servers:
|
|
818
|
-
operationCount:
|
|
819
|
-
tags:
|
|
820
|
-
securitySchemes:
|
|
821
|
-
authStrategies:
|
|
822
|
-
headerPresets:
|
|
823
|
-
|
|
824
|
-
label:
|
|
825
|
-
headers:
|
|
826
|
-
secretHeaders:
|
|
1145
|
+
var StaticPreviewSpecOutputSchema = Schema4.Struct({
|
|
1146
|
+
title: Schema4.NullOr(Schema4.String),
|
|
1147
|
+
version: Schema4.NullOr(Schema4.String),
|
|
1148
|
+
servers: Schema4.Array(StaticPreviewServerSchema),
|
|
1149
|
+
operationCount: Schema4.Number,
|
|
1150
|
+
tags: Schema4.Array(Schema4.String),
|
|
1151
|
+
securitySchemes: Schema4.Array(StaticPreviewSecuritySchemeSchema),
|
|
1152
|
+
authStrategies: Schema4.Array(Schema4.Struct({ schemes: Schema4.Array(Schema4.String) })),
|
|
1153
|
+
headerPresets: Schema4.Array(
|
|
1154
|
+
Schema4.Struct({
|
|
1155
|
+
label: Schema4.String,
|
|
1156
|
+
headers: Schema4.Record(Schema4.String, Schema4.NullOr(Schema4.String)),
|
|
1157
|
+
secretHeaders: Schema4.Array(Schema4.String)
|
|
827
1158
|
})
|
|
828
1159
|
),
|
|
829
|
-
oauth2Presets:
|
|
1160
|
+
oauth2Presets: Schema4.Array(StaticPreviewOAuth2PresetSchema)
|
|
830
1161
|
});
|
|
831
|
-
var OpenApiSpecInputSchema =
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
Schema3.Struct({
|
|
835
|
-
kind: Schema3.Literal("googleDiscovery"),
|
|
836
|
-
url: Schema3.String
|
|
837
|
-
}),
|
|
838
|
-
Schema3.Struct({
|
|
839
|
-
kind: Schema3.Literal("googleDiscoveryBundle"),
|
|
840
|
-
urls: Schema3.Array(Schema3.String)
|
|
841
|
-
})
|
|
1162
|
+
var OpenApiSpecInputSchema = Schema4.Union([
|
|
1163
|
+
Schema4.Struct({ kind: Schema4.Literal("url"), url: Schema4.String }),
|
|
1164
|
+
Schema4.Struct({ kind: Schema4.Literal("blob"), value: Schema4.String })
|
|
842
1165
|
]);
|
|
843
|
-
var AuthenticationSchema2 =
|
|
844
|
-
|
|
845
|
-
slug:
|
|
846
|
-
kind:
|
|
847
|
-
authorizationUrl:
|
|
848
|
-
tokenUrl:
|
|
849
|
-
scopes:
|
|
1166
|
+
var AuthenticationSchema2 = Schema4.Union([
|
|
1167
|
+
Schema4.Struct({
|
|
1168
|
+
slug: Schema4.String,
|
|
1169
|
+
kind: Schema4.Literal("oauth2"),
|
|
1170
|
+
authorizationUrl: Schema4.String,
|
|
1171
|
+
tokenUrl: Schema4.String,
|
|
1172
|
+
scopes: Schema4.Array(Schema4.String)
|
|
850
1173
|
}),
|
|
851
|
-
// Credential methods are authored request-shaped
|
|
1174
|
+
// Credential methods are authored request-shaped - the ONE apikey input
|
|
852
1175
|
// dialect: `{ type: "apiKey", headers: { Authorization: ["Bearer ",
|
|
853
1176
|
// variable("token")] }, queryParams: { … } }`.
|
|
854
1177
|
ApiKeyAuthTemplate
|
|
855
1178
|
]);
|
|
856
|
-
var AddSourceInputSchema =
|
|
1179
|
+
var AddSourceInputSchema = Schema4.Struct({
|
|
857
1180
|
spec: OpenApiSpecInputSchema,
|
|
858
|
-
slug:
|
|
859
|
-
description:
|
|
860
|
-
baseUrl:
|
|
861
|
-
headers:
|
|
862
|
-
queryParams:
|
|
863
|
-
authenticationTemplate:
|
|
1181
|
+
slug: Schema4.String,
|
|
1182
|
+
description: Schema4.optional(Schema4.String),
|
|
1183
|
+
baseUrl: Schema4.optional(Schema4.String),
|
|
1184
|
+
headers: Schema4.optional(Schema4.Record(Schema4.String, Schema4.String)),
|
|
1185
|
+
queryParams: Schema4.optional(Schema4.Record(Schema4.String, Schema4.String)),
|
|
1186
|
+
authenticationTemplate: Schema4.optional(Schema4.Array(AuthenticationSchema2))
|
|
864
1187
|
});
|
|
865
|
-
var AddSourceOutputSchema =
|
|
866
|
-
slug:
|
|
867
|
-
toolCount:
|
|
1188
|
+
var AddSourceOutputSchema = Schema4.Struct({
|
|
1189
|
+
slug: Schema4.String,
|
|
1190
|
+
toolCount: Schema4.Number
|
|
868
1191
|
});
|
|
869
|
-
var PreviewSpecInputStandardSchema =
|
|
870
|
-
|
|
1192
|
+
var PreviewSpecInputStandardSchema = Schema4.toStandardSchemaV1(
|
|
1193
|
+
Schema4.toStandardJSONSchemaV1(PreviewSpecInputSchema)
|
|
871
1194
|
);
|
|
872
|
-
var PreviewSpecOutputStandardSchema =
|
|
873
|
-
|
|
1195
|
+
var PreviewSpecOutputStandardSchema = Schema4.toStandardSchemaV1(
|
|
1196
|
+
Schema4.toStandardJSONSchemaV1(StaticPreviewSpecOutputSchema)
|
|
874
1197
|
);
|
|
875
|
-
var AddSourceInputStandardSchema =
|
|
876
|
-
|
|
1198
|
+
var AddSourceInputStandardSchema = Schema4.toStandardSchemaV1(
|
|
1199
|
+
Schema4.toStandardJSONSchemaV1(AddSourceInputSchema)
|
|
877
1200
|
);
|
|
878
|
-
var AddSourceOutputStandardSchema =
|
|
879
|
-
|
|
1201
|
+
var AddSourceOutputStandardSchema = Schema4.toStandardSchemaV1(
|
|
1202
|
+
Schema4.toStandardJSONSchemaV1(AddSourceOutputSchema)
|
|
880
1203
|
);
|
|
881
|
-
var openApiToolFailure = (code, message, details) =>
|
|
1204
|
+
var openApiToolFailure = (code, message, details) => ToolResult2.fail({
|
|
882
1205
|
code,
|
|
883
1206
|
message,
|
|
884
1207
|
...details === void 0 ? {} : { details }
|
|
885
1208
|
});
|
|
886
|
-
var openApiAuthToolFailure = (failure) => authToolFailure({
|
|
887
|
-
// The auth-tool-failure helper's code set is shared with v1; keep the
|
|
888
|
-
// string but reference the connection rather than v1's source/slot.
|
|
889
|
-
code: failure.code,
|
|
890
|
-
message: failure.message,
|
|
891
|
-
source: { id: failure.integration, scope: failure.owner },
|
|
892
|
-
credential: {
|
|
893
|
-
kind: failure.credentialKind,
|
|
894
|
-
...failure.credentialLabel ? { label: failure.credentialLabel } : {}
|
|
895
|
-
},
|
|
896
|
-
...failure.status !== void 0 ? { status: failure.status } : {},
|
|
897
|
-
...failure.details !== void 0 ? {
|
|
898
|
-
upstream: {
|
|
899
|
-
...failure.status !== void 0 ? { status: failure.status } : {},
|
|
900
|
-
details: failure.details
|
|
901
|
-
}
|
|
902
|
-
} : {}
|
|
903
|
-
});
|
|
904
1209
|
var staticPreviewOutput = (preview) => ({
|
|
905
|
-
title:
|
|
906
|
-
version:
|
|
1210
|
+
title: Option6.getOrNull(preview.title),
|
|
1211
|
+
version: Option6.getOrNull(preview.version),
|
|
907
1212
|
servers: preview.servers.map((server) => ({
|
|
908
1213
|
url: server.url,
|
|
909
|
-
description:
|
|
910
|
-
variables:
|
|
911
|
-
Object.entries(
|
|
1214
|
+
description: Option6.getOrNull(server.description),
|
|
1215
|
+
variables: Option6.getOrNull(server.variables) ? Object.fromEntries(
|
|
1216
|
+
Object.entries(Option6.getOrNull(server.variables) ?? {}).map(([name, variable]) => [
|
|
912
1217
|
name,
|
|
913
1218
|
{
|
|
914
1219
|
default: variable.default,
|
|
915
|
-
enum:
|
|
916
|
-
description:
|
|
1220
|
+
enum: Option6.getOrNull(variable.enum),
|
|
1221
|
+
description: Option6.getOrNull(variable.description)
|
|
917
1222
|
}
|
|
918
1223
|
])
|
|
919
1224
|
) : null
|
|
@@ -923,25 +1228,25 @@ var staticPreviewOutput = (preview) => ({
|
|
|
923
1228
|
securitySchemes: preview.securitySchemes.map((scheme) => ({
|
|
924
1229
|
name: scheme.name,
|
|
925
1230
|
type: scheme.type,
|
|
926
|
-
scheme:
|
|
927
|
-
bearerFormat:
|
|
928
|
-
in:
|
|
929
|
-
headerName:
|
|
930
|
-
description:
|
|
931
|
-
flows:
|
|
932
|
-
authorizationCode:
|
|
1231
|
+
scheme: Option6.getOrNull(scheme.scheme),
|
|
1232
|
+
bearerFormat: Option6.getOrNull(scheme.bearerFormat),
|
|
1233
|
+
in: Option6.getOrNull(scheme.in),
|
|
1234
|
+
headerName: Option6.getOrNull(scheme.headerName),
|
|
1235
|
+
description: Option6.getOrNull(scheme.description),
|
|
1236
|
+
flows: Option6.isSome(scheme.flows) ? {
|
|
1237
|
+
authorizationCode: Option6.isSome(scheme.flows.value.authorizationCode) ? {
|
|
933
1238
|
authorizationUrl: scheme.flows.value.authorizationCode.value.authorizationUrl,
|
|
934
1239
|
tokenUrl: scheme.flows.value.authorizationCode.value.tokenUrl,
|
|
935
|
-
refreshUrl:
|
|
1240
|
+
refreshUrl: Option6.getOrNull(scheme.flows.value.authorizationCode.value.refreshUrl),
|
|
936
1241
|
scopes: scheme.flows.value.authorizationCode.value.scopes
|
|
937
1242
|
} : null,
|
|
938
|
-
clientCredentials:
|
|
1243
|
+
clientCredentials: Option6.isSome(scheme.flows.value.clientCredentials) ? {
|
|
939
1244
|
tokenUrl: scheme.flows.value.clientCredentials.value.tokenUrl,
|
|
940
|
-
refreshUrl:
|
|
1245
|
+
refreshUrl: Option6.getOrNull(scheme.flows.value.clientCredentials.value.refreshUrl),
|
|
941
1246
|
scopes: scheme.flows.value.clientCredentials.value.scopes
|
|
942
1247
|
} : null
|
|
943
1248
|
} : null,
|
|
944
|
-
openIdConnectUrl:
|
|
1249
|
+
openIdConnectUrl: Option6.getOrNull(scheme.openIdConnectUrl)
|
|
945
1250
|
})),
|
|
946
1251
|
authStrategies: preview.authStrategies,
|
|
947
1252
|
headerPresets: preview.headerPresets,
|
|
@@ -949,55 +1254,14 @@ var staticPreviewOutput = (preview) => ({
|
|
|
949
1254
|
label: preset.label,
|
|
950
1255
|
securitySchemeName: preset.securitySchemeName,
|
|
951
1256
|
flow: preset.flow,
|
|
952
|
-
authorizationUrl:
|
|
1257
|
+
authorizationUrl: Option6.getOrNull(preset.authorizationUrl),
|
|
953
1258
|
tokenUrl: preset.tokenUrl,
|
|
954
|
-
refreshUrl:
|
|
1259
|
+
refreshUrl: Option6.getOrNull(preset.refreshUrl),
|
|
955
1260
|
scopes: preset.scopes,
|
|
956
1261
|
identityScopes: preset.identityScopes
|
|
957
1262
|
}))
|
|
958
1263
|
});
|
|
959
|
-
var
|
|
960
|
-
if (node == null || typeof node !== "object") return node;
|
|
961
|
-
if (Array.isArray(node)) {
|
|
962
|
-
let changed2 = false;
|
|
963
|
-
const out = node.map((item) => {
|
|
964
|
-
const n = normalizeOpenApiRefs(item);
|
|
965
|
-
if (n !== item) changed2 = true;
|
|
966
|
-
return n;
|
|
967
|
-
});
|
|
968
|
-
return changed2 ? out : node;
|
|
969
|
-
}
|
|
970
|
-
const obj = node;
|
|
971
|
-
if (typeof obj.$ref === "string") {
|
|
972
|
-
const match = obj.$ref.match(/^#\/components\/schemas\/(.+)$/);
|
|
973
|
-
if (match) return { ...obj, $ref: `#/$defs/${match[1]}` };
|
|
974
|
-
return obj;
|
|
975
|
-
}
|
|
976
|
-
let changed = false;
|
|
977
|
-
const result = {};
|
|
978
|
-
for (const [k, v] of Object.entries(obj)) {
|
|
979
|
-
const n = normalizeOpenApiRefs(v);
|
|
980
|
-
if (n !== v) changed = true;
|
|
981
|
-
result[k] = n;
|
|
982
|
-
}
|
|
983
|
-
return changed ? result : obj;
|
|
984
|
-
};
|
|
985
|
-
var toBinding = (def) => OperationBinding.make({
|
|
986
|
-
method: def.operation.method,
|
|
987
|
-
servers: def.operation.servers,
|
|
988
|
-
pathTemplate: def.operation.pathTemplate,
|
|
989
|
-
parameters: [...def.operation.parameters],
|
|
990
|
-
requestBody: def.operation.requestBody
|
|
991
|
-
});
|
|
992
|
-
var descriptionFor = (def) => {
|
|
993
|
-
const op = def.operation;
|
|
994
|
-
return Option5.getOrElse(
|
|
995
|
-
op.description,
|
|
996
|
-
() => Option5.getOrElse(op.summary, () => `${op.method.toUpperCase()} ${op.pathTemplate}`)
|
|
997
|
-
);
|
|
998
|
-
};
|
|
999
|
-
var specInputToSourceUrl = (spec) => spec.kind === "url" || spec.kind === "googleDiscovery" ? spec.url : void 0;
|
|
1000
|
-
var specInputToGoogleBundle = (spec) => spec.kind === "googleDiscoveryBundle" ? spec.urls : void 0;
|
|
1264
|
+
var specInputToSourceUrl = (spec) => spec.kind === "url" ? spec.url : void 0;
|
|
1001
1265
|
var describeOpenApiAuthMethods = (record) => {
|
|
1002
1266
|
const config = decodeOpenApiIntegrationConfig(record.config);
|
|
1003
1267
|
if (!config) return [];
|
|
@@ -1024,76 +1288,10 @@ var describeOpenApiIntegrationDisplay = (record) => {
|
|
|
1024
1288
|
const config = decodeOpenApiIntegrationConfig(record.config);
|
|
1025
1289
|
return { url: config?.baseUrl ?? config?.sourceUrl };
|
|
1026
1290
|
};
|
|
1027
|
-
var loadSpecText = (storage, config) => config.specHash != null ? storage.getSpec(config.specHash) : Effect3.succeed(null);
|
|
1028
|
-
var compileSpec = (specText) => Effect3.gen(function* () {
|
|
1029
|
-
const doc = yield* parse(specText);
|
|
1030
|
-
const result = yield* extract(doc);
|
|
1031
|
-
const hoistedDefs = {};
|
|
1032
|
-
if (doc.components?.schemas) {
|
|
1033
|
-
for (const [k, v] of Object.entries(doc.components.schemas)) {
|
|
1034
|
-
hoistedDefs[k] = normalizeOpenApiRefs(v);
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
return {
|
|
1038
|
-
definitions: compileToolDefinitions(result.operations),
|
|
1039
|
-
hoistedDefs,
|
|
1040
|
-
title: Option5.getOrUndefined(result.title),
|
|
1041
|
-
description: Option5.getOrUndefined(result.description)
|
|
1042
|
-
};
|
|
1043
|
-
});
|
|
1044
|
-
var toolDefsFromCompiled = (compiled) => compiled.definitions.map(
|
|
1045
|
-
(def) => ({
|
|
1046
|
-
name: ToolName.make(def.toolPath),
|
|
1047
|
-
description: descriptionFor(def),
|
|
1048
|
-
inputSchema: normalizeOpenApiRefs(Option5.getOrUndefined(def.operation.inputSchema)),
|
|
1049
|
-
// The output schema is the upstream response body only — transport
|
|
1050
|
-
// status/headers live in the ToolResult `http` side channel, not the
|
|
1051
|
-
// payload (see the invoke handler).
|
|
1052
|
-
outputSchema: normalizeOpenApiRefs(Option5.getOrUndefined(def.operation.outputSchema)),
|
|
1053
|
-
annotations: annotationsForOperation(def.operation.method, def.operation.pathTemplate)
|
|
1054
|
-
})
|
|
1055
|
-
);
|
|
1056
|
-
var storedOperationsFromCompiled = (integration, compiled) => compiled.definitions.map((def) => ({
|
|
1057
|
-
integration,
|
|
1058
|
-
toolName: def.toolPath,
|
|
1059
|
-
binding: toBinding(def)
|
|
1060
|
-
}));
|
|
1061
|
-
var fetchGoogleDiscoveryBundleConversion = (urls, httpClientLayer) => Effect3.forEach(
|
|
1062
|
-
urls,
|
|
1063
|
-
(url) => fetchGoogleDiscoveryDocument(url).pipe(
|
|
1064
|
-
Effect3.provide(httpClientLayer),
|
|
1065
|
-
Effect3.map((documentText) => ({ discoveryUrl: url, documentText }))
|
|
1066
|
-
),
|
|
1067
|
-
{ concurrency: 4 }
|
|
1068
|
-
).pipe(Effect3.flatMap((documents) => convertGoogleDiscoveryBundleToOpenApi({ documents })));
|
|
1069
1291
|
var openApiPlugin = definePlugin((options) => {
|
|
1070
|
-
const resolveSpecForInput = (spec, httpClientLayer) =>
|
|
1071
|
-
if (spec.kind === "googleDiscovery") {
|
|
1072
|
-
const conversion = yield* fetchGoogleDiscoveryDocument(spec.url).pipe(
|
|
1073
|
-
Effect3.provide(httpClientLayer),
|
|
1074
|
-
Effect3.flatMap(
|
|
1075
|
-
(documentText) => convertGoogleDiscoveryToOpenApi({
|
|
1076
|
-
discoveryUrl: spec.url,
|
|
1077
|
-
documentText
|
|
1078
|
-
})
|
|
1079
|
-
)
|
|
1080
|
-
);
|
|
1081
|
-
return {
|
|
1082
|
-
specText: conversion.specText,
|
|
1083
|
-
baseUrl: conversion.baseUrl,
|
|
1084
|
-
...conversion.authenticationTemplate ? { authenticationTemplate: conversion.authenticationTemplate } : {}
|
|
1085
|
-
};
|
|
1086
|
-
}
|
|
1087
|
-
if (spec.kind === "googleDiscoveryBundle") {
|
|
1088
|
-
const conversion = yield* fetchGoogleDiscoveryBundleConversion(spec.urls, httpClientLayer);
|
|
1089
|
-
return {
|
|
1090
|
-
specText: conversion.specText,
|
|
1091
|
-
baseUrl: conversion.baseUrl,
|
|
1092
|
-
...conversion.authenticationTemplate ? { authenticationTemplate: conversion.authenticationTemplate } : {}
|
|
1093
|
-
};
|
|
1094
|
-
}
|
|
1292
|
+
const resolveSpecForInput = (spec, httpClientLayer) => Effect4.gen(function* () {
|
|
1095
1293
|
if (spec.kind === "url") {
|
|
1096
|
-
const specText = yield* resolveSpecText(spec.url).pipe(
|
|
1294
|
+
const specText = yield* resolveSpecText(spec.url).pipe(Effect4.provide(httpClientLayer));
|
|
1097
1295
|
return { specText };
|
|
1098
1296
|
}
|
|
1099
1297
|
return { specText: spec.value };
|
|
@@ -1112,12 +1310,12 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
1112
1310
|
storage: (deps) => makeDefaultOpenapiStore(deps),
|
|
1113
1311
|
extension: (ctx) => {
|
|
1114
1312
|
const httpClientLayer = options?.httpClientLayer ?? ctx.httpClientLayer;
|
|
1115
|
-
const addSpec = (config) =>
|
|
1313
|
+
const addSpec = (config) => Effect4.gen(function* () {
|
|
1116
1314
|
const resolved = yield* resolveSpecForInput(config.spec, httpClientLayer);
|
|
1117
|
-
const compiled = yield*
|
|
1118
|
-
const explicitBaseUrl = config.baseUrl
|
|
1315
|
+
const compiled = yield* compileOpenApiSpec(resolved.specText);
|
|
1316
|
+
const explicitBaseUrl = config.baseUrl;
|
|
1119
1317
|
const needsDerivedBaseUrl = explicitBaseUrl == null;
|
|
1120
|
-
const needsDerivedAuth = config.authenticationTemplate == null
|
|
1318
|
+
const needsDerivedAuth = config.authenticationTemplate == null;
|
|
1121
1319
|
const preview = needsDerivedBaseUrl || needsDerivedAuth ? yield* previewSpecText(resolved.specText) : void 0;
|
|
1122
1320
|
const derivedBaseUrl = needsDerivedBaseUrl && preview ? firstBaseUrlForPreview(preview) : void 0;
|
|
1123
1321
|
const effectiveBaseUrl = explicitBaseUrl ?? (derivedBaseUrl || void 0);
|
|
@@ -1131,55 +1329,52 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
1131
1329
|
const integrationConfig = {
|
|
1132
1330
|
specHash,
|
|
1133
1331
|
...specInputToSourceUrl(config.spec) !== void 0 ? { sourceUrl: specInputToSourceUrl(config.spec) } : {},
|
|
1134
|
-
...specInputToGoogleBundle(config.spec) !== void 0 ? { googleDiscoveryUrls: specInputToGoogleBundle(config.spec) } : {},
|
|
1135
1332
|
// baseUrl is an optional override only. The host is otherwise
|
|
1136
1333
|
// resolved per call from the operation's `servers` (extracted from
|
|
1137
1334
|
// the spec), so we never bake a derived base URL into the config.
|
|
1138
1335
|
...config.baseUrl ? { baseUrl: config.baseUrl } : {},
|
|
1139
1336
|
...config.headers ? { headers: config.headers } : {},
|
|
1140
1337
|
...config.queryParams ? { queryParams: config.queryParams } : {},
|
|
1141
|
-
// Prefer the caller's explicit template; otherwise
|
|
1142
|
-
//
|
|
1143
|
-
// path relies on this — it has no preview to detect auth from);
|
|
1144
|
-
// otherwise derive from the spec's declared security schemes.
|
|
1338
|
+
// Prefer the caller's explicit template; otherwise derive from the
|
|
1339
|
+
// spec's declared security schemes.
|
|
1145
1340
|
...config.authenticationTemplate ? {
|
|
1146
1341
|
authenticationTemplate: normalizeOpenApiAuthInputs(config.authenticationTemplate)
|
|
1147
|
-
} :
|
|
1342
|
+
} : derivedAuthenticationTemplate && derivedAuthenticationTemplate.length > 0 ? { authenticationTemplate: derivedAuthenticationTemplate } : {}
|
|
1148
1343
|
};
|
|
1149
1344
|
yield* ctx.storage.putSpec(specHash, resolved.specText);
|
|
1150
1345
|
yield* ctx.transaction(
|
|
1151
|
-
|
|
1346
|
+
Effect4.gen(function* () {
|
|
1152
1347
|
yield* ctx.core.integrations.register({
|
|
1153
1348
|
slug,
|
|
1154
1349
|
name: config.name?.trim() || compiled.title || config.slug,
|
|
1155
1350
|
description: config.description ?? compiled.description ?? compiled.title ?? config.slug,
|
|
1156
1351
|
config: integrationConfig,
|
|
1157
1352
|
canRemove: true,
|
|
1158
|
-
canRefresh: specInputToSourceUrl(config.spec) != null
|
|
1353
|
+
canRefresh: specInputToSourceUrl(config.spec) != null
|
|
1159
1354
|
});
|
|
1160
1355
|
yield* ctx.storage.putOperations(
|
|
1161
1356
|
config.slug,
|
|
1162
|
-
|
|
1357
|
+
openApiStoredOperationsFromCompiled(config.slug, compiled)
|
|
1163
1358
|
);
|
|
1164
1359
|
})
|
|
1165
1360
|
);
|
|
1166
1361
|
return { slug, toolCount: compiled.definitions.length };
|
|
1167
1362
|
});
|
|
1168
|
-
const updateSpec = (rawSlug, input) =>
|
|
1363
|
+
const updateSpec = (rawSlug, input) => Effect4.gen(function* () {
|
|
1169
1364
|
const slug = IntegrationSlug.make(rawSlug);
|
|
1170
1365
|
const record = yield* ctx.core.integrations.get(slug);
|
|
1171
1366
|
const current = record ? decodeOpenApiIntegrationConfig(record.config) : null;
|
|
1172
1367
|
if (!record || !current) {
|
|
1173
1368
|
return yield* new IntegrationNotFoundError({ slug });
|
|
1174
1369
|
}
|
|
1175
|
-
const specInput = input?.spec ?? (current.
|
|
1370
|
+
const specInput = input?.spec ?? (current.sourceUrl ? { kind: "url", url: current.sourceUrl } : null);
|
|
1176
1371
|
if (specInput === null) {
|
|
1177
1372
|
return yield* new OpenApiParseError({
|
|
1178
1373
|
message: "This integration's spec was pasted inline and has no source URL to re-fetch. Provide the updated spec content."
|
|
1179
1374
|
});
|
|
1180
1375
|
}
|
|
1181
1376
|
const resolved = yield* resolveSpecForInput(specInput, httpClientLayer);
|
|
1182
|
-
const compiled = yield*
|
|
1377
|
+
const compiled = yield* compileOpenApiSpec(resolved.specText);
|
|
1183
1378
|
const previousOperations = yield* ctx.storage.listOperations(rawSlug);
|
|
1184
1379
|
const previousNames = new Set(previousOperations.map((op) => op.toolName));
|
|
1185
1380
|
const nextNames = new Set(compiled.definitions.map((def) => def.toolPath));
|
|
@@ -1188,32 +1383,31 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
1188
1383
|
const nextConfig = {
|
|
1189
1384
|
...current,
|
|
1190
1385
|
specHash,
|
|
1191
|
-
...specInputToSourceUrl(specInput) !== void 0 ? { sourceUrl: specInputToSourceUrl(specInput) } : {}
|
|
1192
|
-
...specInputToGoogleBundle(specInput) !== void 0 ? { googleDiscoveryUrls: specInputToGoogleBundle(specInput) } : {}
|
|
1386
|
+
...specInputToSourceUrl(specInput) !== void 0 ? { sourceUrl: specInputToSourceUrl(specInput) } : {}
|
|
1193
1387
|
};
|
|
1194
1388
|
yield* ctx.transaction(
|
|
1195
|
-
|
|
1389
|
+
Effect4.gen(function* () {
|
|
1196
1390
|
yield* ctx.core.integrations.update(slug, {
|
|
1197
1391
|
config: nextConfig
|
|
1198
1392
|
});
|
|
1199
1393
|
yield* ctx.storage.putOperations(
|
|
1200
1394
|
rawSlug,
|
|
1201
|
-
|
|
1395
|
+
openApiStoredOperationsFromCompiled(rawSlug, compiled)
|
|
1202
1396
|
);
|
|
1203
1397
|
})
|
|
1204
1398
|
);
|
|
1205
1399
|
const connections = yield* ctx.connections.list({ integration: slug });
|
|
1206
|
-
yield*
|
|
1400
|
+
yield* Effect4.forEach(
|
|
1207
1401
|
connections,
|
|
1208
1402
|
(connection) => ctx.connections.refresh({
|
|
1209
1403
|
owner: connection.owner,
|
|
1210
1404
|
integration: connection.integration,
|
|
1211
1405
|
name: connection.name
|
|
1212
|
-
}).pipe(
|
|
1406
|
+
}).pipe(Effect4.catchTag("ConnectionNotFoundError", () => Effect4.succeed([]))),
|
|
1213
1407
|
{ discard: true }
|
|
1214
1408
|
).pipe(
|
|
1215
|
-
|
|
1216
|
-
|
|
1409
|
+
Effect4.catchTag("IntegrationNotFoundError", () => Effect4.void),
|
|
1410
|
+
Effect4.withSpan("openapi.plugin.update_spec.refresh_connections", {
|
|
1217
1411
|
attributes: { "openapi.connection_count": connections.length }
|
|
1218
1412
|
})
|
|
1219
1413
|
);
|
|
@@ -1224,35 +1418,28 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
1224
1418
|
removedTools: [...previousNames].filter((name) => !nextNames.has(name)).sort()
|
|
1225
1419
|
};
|
|
1226
1420
|
}).pipe(
|
|
1227
|
-
|
|
1421
|
+
Effect4.withSpan("openapi.plugin.update_spec", {
|
|
1228
1422
|
attributes: { "openapi.integration.slug": rawSlug }
|
|
1229
1423
|
})
|
|
1230
1424
|
);
|
|
1231
1425
|
return {
|
|
1232
|
-
previewSpec: (input) =>
|
|
1426
|
+
previewSpec: (input) => Effect4.gen(function* () {
|
|
1233
1427
|
const previewInput = typeof input === "string" ? { spec: input } : input;
|
|
1234
|
-
const specText =
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
discoveryUrl: previewInput.spec,
|
|
1239
|
-
documentText
|
|
1240
|
-
})
|
|
1241
|
-
),
|
|
1242
|
-
Effect3.map((conversion) => conversion.specText)
|
|
1243
|
-
) : yield* resolveSpecText(previewInput.spec).pipe(Effect3.provide(httpClientLayer));
|
|
1244
|
-
return yield* previewSpec(specText).pipe(Effect3.provide(httpClientLayer));
|
|
1428
|
+
const specText = yield* resolveSpecText(previewInput.spec).pipe(
|
|
1429
|
+
Effect4.provide(httpClientLayer)
|
|
1430
|
+
);
|
|
1431
|
+
return yield* previewSpecText(specText);
|
|
1245
1432
|
}),
|
|
1246
1433
|
addSpec,
|
|
1247
1434
|
updateSpec,
|
|
1248
1435
|
removeSpec: (slug) => ctx.transaction(
|
|
1249
|
-
|
|
1436
|
+
Effect4.gen(function* () {
|
|
1250
1437
|
yield* ctx.storage.removeOperations(slug);
|
|
1251
|
-
yield* ctx.core.integrations.remove(IntegrationSlug.make(slug)).pipe(
|
|
1438
|
+
yield* ctx.core.integrations.remove(IntegrationSlug.make(slug)).pipe(Effect4.catchTag("IntegrationRemovalNotAllowedError", () => Effect4.void));
|
|
1252
1439
|
})
|
|
1253
1440
|
),
|
|
1254
1441
|
getIntegration: (slug) => ctx.core.integrations.get(IntegrationSlug.make(slug)).pipe(
|
|
1255
|
-
|
|
1442
|
+
Effect4.map(
|
|
1256
1443
|
(record) => record ? {
|
|
1257
1444
|
slug: record.slug,
|
|
1258
1445
|
description: record.description,
|
|
@@ -1263,12 +1450,12 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
1263
1450
|
)
|
|
1264
1451
|
),
|
|
1265
1452
|
getConfig: (slug) => ctx.core.integrations.get(IntegrationSlug.make(slug)).pipe(
|
|
1266
|
-
|
|
1453
|
+
Effect4.map(
|
|
1267
1454
|
(record) => record ? decodeOpenApiIntegrationConfig(record.config) : null
|
|
1268
1455
|
)
|
|
1269
1456
|
),
|
|
1270
1457
|
configure: (slug, input) => ctx.transaction(
|
|
1271
|
-
|
|
1458
|
+
Effect4.gen(function* () {
|
|
1272
1459
|
const record = yield* ctx.core.integrations.get(IntegrationSlug.make(slug));
|
|
1273
1460
|
if (!record) return [];
|
|
1274
1461
|
const current = decodeOpenApiIntegrationConfig(record.config);
|
|
@@ -1299,17 +1486,17 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
1299
1486
|
inputSchema: PreviewSpecInputStandardSchema,
|
|
1300
1487
|
outputSchema: PreviewSpecOutputStandardSchema,
|
|
1301
1488
|
execute: (input) => self.previewSpec(input).pipe(
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
OpenApiParseError: ({ message }) =>
|
|
1305
|
-
OpenApiExtractionError: ({ message }) =>
|
|
1306
|
-
OpenApiOAuthError: ({ message }) =>
|
|
1489
|
+
Effect4.map((preview) => ToolResult2.ok(staticPreviewOutput(preview))),
|
|
1490
|
+
Effect4.catchTags({
|
|
1491
|
+
OpenApiParseError: ({ message }) => Effect4.succeed(openApiToolFailure("openapi_parse_failed", message)),
|
|
1492
|
+
OpenApiExtractionError: ({ message }) => Effect4.succeed(openApiToolFailure("openapi_extraction_failed", message)),
|
|
1493
|
+
OpenApiOAuthError: ({ message }) => Effect4.succeed(openApiToolFailure("openapi_oauth_failed", message))
|
|
1307
1494
|
})
|
|
1308
1495
|
)
|
|
1309
1496
|
}),
|
|
1310
1497
|
tool({
|
|
1311
1498
|
name: "addSpec",
|
|
1312
|
-
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
|
|
1499
|
+
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 - apiKey header/query, or oauth bearer - or an empty array for no auth methods).",
|
|
1313
1500
|
annotations: {
|
|
1314
1501
|
requiresApproval: true,
|
|
1315
1502
|
approvalDescription: "Add an OpenAPI integration"
|
|
@@ -1325,17 +1512,17 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
1325
1512
|
queryParams: input.queryParams,
|
|
1326
1513
|
authenticationTemplate: input.authenticationTemplate
|
|
1327
1514
|
}).pipe(
|
|
1328
|
-
|
|
1329
|
-
(result) =>
|
|
1515
|
+
Effect4.map(
|
|
1516
|
+
(result) => ToolResult2.ok({
|
|
1330
1517
|
slug: String(result.slug),
|
|
1331
1518
|
toolCount: result.toolCount
|
|
1332
1519
|
})
|
|
1333
1520
|
),
|
|
1334
|
-
|
|
1335
|
-
OpenApiParseError: ({ message }) =>
|
|
1336
|
-
OpenApiExtractionError: ({ message }) =>
|
|
1337
|
-
OpenApiOAuthError: ({ message }) =>
|
|
1338
|
-
IntegrationAlreadyExistsError: ({ slug }) =>
|
|
1521
|
+
Effect4.catchTags({
|
|
1522
|
+
OpenApiParseError: ({ message }) => Effect4.succeed(openApiToolFailure("openapi_parse_failed", message)),
|
|
1523
|
+
OpenApiExtractionError: ({ message }) => Effect4.succeed(openApiToolFailure("openapi_extraction_failed", message)),
|
|
1524
|
+
OpenApiOAuthError: ({ message }) => Effect4.succeed(openApiToolFailure("openapi_oauth_failed", message)),
|
|
1525
|
+
IntegrationAlreadyExistsError: ({ slug }) => Effect4.succeed(
|
|
1339
1526
|
openApiToolFailure(
|
|
1340
1527
|
"integration_already_exists",
|
|
1341
1528
|
`Integration ${slug} already exists; update it instead of re-adding.`
|
|
@@ -1350,173 +1537,50 @@ var openApiPlugin = definePlugin((options) => {
|
|
|
1350
1537
|
describeAuthMethods: describeOpenApiAuthMethods,
|
|
1351
1538
|
describeIntegrationDisplay: describeOpenApiIntegrationDisplay,
|
|
1352
1539
|
// Produce one tool per spec operation. Spec-derived, identical for every
|
|
1353
|
-
// connection on the integration
|
|
1540
|
+
// connection on the integration - so `getValue` is never called here. The
|
|
1354
1541
|
// operation bindings invokeTool needs are persisted at addSpec time; this
|
|
1355
1542
|
// hook only shapes the per-connection ToolDefs from the spec blob the
|
|
1356
1543
|
// catalog config points at.
|
|
1357
|
-
resolveTools: ({
|
|
1358
|
-
|
|
1359
|
-
storage
|
|
1360
|
-
}) => Effect3.gen(function* () {
|
|
1361
|
-
const openApiConfig = decodeOpenApiIntegrationConfig(config);
|
|
1362
|
-
if (!openApiConfig) return { tools: [], definitions: {} };
|
|
1363
|
-
const specText = yield* loadSpecText(storage, openApiConfig);
|
|
1364
|
-
if (specText == null) return { tools: [], definitions: {} };
|
|
1365
|
-
const compiled = yield* compileSpec(specText).pipe(
|
|
1366
|
-
Effect3.catch(() => Effect3.succeed(null))
|
|
1367
|
-
);
|
|
1368
|
-
if (!compiled) return { tools: [], definitions: {} };
|
|
1369
|
-
return {
|
|
1370
|
-
tools: toolDefsFromCompiled(compiled),
|
|
1371
|
-
definitions: compiled.hoistedDefs
|
|
1372
|
-
};
|
|
1373
|
-
}),
|
|
1374
|
-
invokeTool: ({
|
|
1375
|
-
ctx: invokeCtx,
|
|
1376
|
-
toolRow,
|
|
1377
|
-
credential,
|
|
1378
|
-
args
|
|
1379
|
-
}) => Effect3.gen(function* () {
|
|
1544
|
+
resolveTools: ({ config, storage }) => resolveOpenApiBackedTools({ config, storage }),
|
|
1545
|
+
invokeTool: ({ ctx: invokeCtx, toolRow, credential, args }) => {
|
|
1380
1546
|
const httpClientLayer = options?.httpClientLayer ?? invokeCtx.httpClientLayer;
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
Effect3.catch(() => Effect3.succeed(null))
|
|
1387
|
-
);
|
|
1388
|
-
const compiled = specText == null ? null : yield* compileSpec(specText).pipe(Effect3.catch(() => Effect3.succeed(null)));
|
|
1389
|
-
binding = compiled ? storedOperationsFromCompiled(integration, compiled).find(
|
|
1390
|
-
(op) => op.toolName === toolRow.name
|
|
1391
|
-
)?.binding : void 0;
|
|
1392
|
-
}
|
|
1393
|
-
if (!binding) {
|
|
1394
|
-
return yield* new OpenApiExtractionError({
|
|
1395
|
-
message: `No OpenAPI operation found for tool "${toolRow.name}" on "${integration}"`
|
|
1396
|
-
});
|
|
1397
|
-
}
|
|
1398
|
-
const headers = { ...config?.headers ?? {} };
|
|
1399
|
-
const queryParams = {
|
|
1400
|
-
...config?.queryParams ?? {}
|
|
1401
|
-
};
|
|
1402
|
-
const template = (config?.authenticationTemplate ?? []).find(
|
|
1403
|
-
(entry) => String(entry.slug) === String(credential.template)
|
|
1404
|
-
);
|
|
1405
|
-
if (template) {
|
|
1406
|
-
const missing = requiredTemplateVariables(template).filter((name) => {
|
|
1407
|
-
const value = credential.values[name];
|
|
1408
|
-
return value == null || value === "";
|
|
1409
|
-
});
|
|
1410
|
-
if (missing.length > 0) {
|
|
1411
|
-
return openApiAuthToolFailure({
|
|
1412
|
-
code: template.kind === "oauth2" ? "oauth_connection_missing" : "connection_value_missing",
|
|
1413
|
-
message: `Connection "${credential.connection}" for "${integration}" has no resolvable credential value. Re-authenticate or update the connection.`,
|
|
1414
|
-
owner: credential.owner,
|
|
1415
|
-
integration,
|
|
1416
|
-
connection: String(credential.connection),
|
|
1417
|
-
credentialKind: template.kind === "oauth2" ? "oauth" : "secret"
|
|
1418
|
-
});
|
|
1419
|
-
}
|
|
1420
|
-
const rendered = renderAuthTemplate(template, credential.values);
|
|
1421
|
-
Object.assign(headers, rendered.headers);
|
|
1422
|
-
Object.assign(queryParams, rendered.queryParams);
|
|
1423
|
-
}
|
|
1424
|
-
const result = yield* invokeWithLayer(
|
|
1425
|
-
binding,
|
|
1426
|
-
args ?? {},
|
|
1427
|
-
config?.baseUrl ?? "",
|
|
1428
|
-
headers,
|
|
1429
|
-
queryParams,
|
|
1547
|
+
return invokeOpenApiBackedTool({
|
|
1548
|
+
ctx: invokeCtx,
|
|
1549
|
+
toolRow,
|
|
1550
|
+
credential,
|
|
1551
|
+
args,
|
|
1430
1552
|
httpClientLayer
|
|
1431
|
-
);
|
|
1432
|
-
const ok = result.status >= 200 && result.status < 300;
|
|
1433
|
-
if (!ok) {
|
|
1434
|
-
if (result.status === 401 || result.status === 403) {
|
|
1435
|
-
return openApiAuthToolFailure({
|
|
1436
|
-
code: "connection_rejected",
|
|
1437
|
-
status: result.status,
|
|
1438
|
-
message: `Upstream rejected credentials for "${integration}" with HTTP ${result.status}. Re-authenticate or update the connection "${credential.connection}" before retrying this tool.`,
|
|
1439
|
-
owner: credential.owner,
|
|
1440
|
-
integration,
|
|
1441
|
-
connection: String(credential.connection),
|
|
1442
|
-
credentialKind: "upstream",
|
|
1443
|
-
credentialLabel: "Upstream authorization",
|
|
1444
|
-
details: result.error
|
|
1445
|
-
});
|
|
1446
|
-
}
|
|
1447
|
-
return ToolResult.fail({
|
|
1448
|
-
code: "upstream_http_error",
|
|
1449
|
-
status: result.status,
|
|
1450
|
-
message: extractUpstreamMessage(result.error, result.status),
|
|
1451
|
-
details: result.error
|
|
1452
|
-
});
|
|
1453
|
-
}
|
|
1454
|
-
return ToolResult.ok(result.data, {
|
|
1455
|
-
http: { status: result.status, headers: result.headers }
|
|
1456
1553
|
});
|
|
1457
|
-
}
|
|
1458
|
-
resolveAnnotations: ({
|
|
1554
|
+
},
|
|
1555
|
+
resolveAnnotations: ({ ctx: annotationsCtx, integration, toolRows }) => resolveOpenApiBackedAnnotations({
|
|
1459
1556
|
ctx: annotationsCtx,
|
|
1460
|
-
integration,
|
|
1557
|
+
integration: String(integration),
|
|
1461
1558
|
toolRows
|
|
1462
|
-
}) => Effect3.gen(function* () {
|
|
1463
|
-
const ops = yield* annotationsCtx.storage.listOperations(String(integration));
|
|
1464
|
-
const byName = /* @__PURE__ */ new Map();
|
|
1465
|
-
for (const op of ops) byName.set(op.toolName, op.binding);
|
|
1466
|
-
const out = {};
|
|
1467
|
-
for (const row of toolRows) {
|
|
1468
|
-
const binding = byName.get(row.name);
|
|
1469
|
-
if (binding) {
|
|
1470
|
-
out[row.name] = annotationsForOperation(binding.method, binding.pathTemplate);
|
|
1471
|
-
}
|
|
1472
|
-
}
|
|
1473
|
-
return out;
|
|
1474
1559
|
}),
|
|
1475
|
-
removeConnection: () =>
|
|
1560
|
+
removeConnection: () => Effect4.void,
|
|
1476
1561
|
detect: ({
|
|
1477
1562
|
ctx: detectCtx,
|
|
1478
1563
|
url
|
|
1479
|
-
}) =>
|
|
1564
|
+
}) => Effect4.gen(function* () {
|
|
1480
1565
|
const httpClientLayer = options?.httpClientLayer ?? detectCtx.httpClientLayer;
|
|
1481
1566
|
const trimmed = url.trim();
|
|
1482
1567
|
if (!trimmed) return null;
|
|
1483
|
-
const parsed = yield*
|
|
1568
|
+
const parsed = yield* Effect4.try({
|
|
1484
1569
|
try: () => new URL(trimmed),
|
|
1485
1570
|
catch: (error) => error
|
|
1486
|
-
}).pipe(
|
|
1487
|
-
if (
|
|
1488
|
-
if (isGoogleDiscoveryUrl(trimmed)) {
|
|
1489
|
-
const conversion = yield* fetchGoogleDiscoveryDocument(trimmed).pipe(
|
|
1490
|
-
Effect3.provide(httpClientLayer),
|
|
1491
|
-
Effect3.flatMap(
|
|
1492
|
-
(documentText) => convertGoogleDiscoveryToOpenApi({
|
|
1493
|
-
discoveryUrl: trimmed,
|
|
1494
|
-
documentText
|
|
1495
|
-
})
|
|
1496
|
-
),
|
|
1497
|
-
Effect3.catch(() => Effect3.succeed(null))
|
|
1498
|
-
);
|
|
1499
|
-
if (conversion) {
|
|
1500
|
-
return IntegrationDetectionResult.make({
|
|
1501
|
-
kind: "openapi",
|
|
1502
|
-
confidence: "high",
|
|
1503
|
-
endpoint: trimmed,
|
|
1504
|
-
name: conversion.title,
|
|
1505
|
-
slug: conversion.title.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || `google_${conversion.service}`
|
|
1506
|
-
});
|
|
1507
|
-
}
|
|
1508
|
-
}
|
|
1571
|
+
}).pipe(Effect4.option);
|
|
1572
|
+
if (Option6.isNone(parsed)) return null;
|
|
1509
1573
|
const specText = yield* resolveSpecText(trimmed).pipe(
|
|
1510
|
-
|
|
1511
|
-
|
|
1574
|
+
Effect4.provide(httpClientLayer),
|
|
1575
|
+
Effect4.catch(() => Effect4.succeed(null))
|
|
1512
1576
|
);
|
|
1513
1577
|
if (specText === null) return null;
|
|
1514
|
-
const doc = yield* parse(specText).pipe(
|
|
1578
|
+
const doc = yield* parse(specText).pipe(Effect4.catch(() => Effect4.succeed(null)));
|
|
1515
1579
|
if (!doc) return null;
|
|
1516
|
-
const result = yield* extract(doc).pipe(
|
|
1580
|
+
const result = yield* extract(doc).pipe(Effect4.catch(() => Effect4.succeed(null)));
|
|
1517
1581
|
if (!result) return null;
|
|
1518
|
-
const slug =
|
|
1519
|
-
const name =
|
|
1582
|
+
const slug = Option6.getOrElse(result.title, () => "api").toLowerCase().replace(/[^a-z0-9]+/g, "_");
|
|
1583
|
+
const name = Option6.getOrElse(result.title, () => slug);
|
|
1520
1584
|
return IntegrationDetectionResult.make({
|
|
1521
1585
|
kind: "openapi",
|
|
1522
1586
|
confidence: "high",
|
|
@@ -1533,10 +1597,20 @@ export {
|
|
|
1533
1597
|
OpenApiIntegrationConfigSchema,
|
|
1534
1598
|
decodeOpenApiIntegrationConfig,
|
|
1535
1599
|
renderAuthTemplate,
|
|
1600
|
+
makeDefaultOpenapiStore,
|
|
1536
1601
|
invoke,
|
|
1537
1602
|
invokeWithLayer,
|
|
1538
1603
|
annotationsForOperation,
|
|
1539
|
-
|
|
1604
|
+
extractOpenApiUpstreamMessage,
|
|
1605
|
+
normalizeOpenApiRefs,
|
|
1606
|
+
compileOpenApiDocument,
|
|
1607
|
+
compileOpenApiSpec,
|
|
1608
|
+
openApiToolDefsFromCompiled,
|
|
1609
|
+
openApiStoredOperationsFromCompiled,
|
|
1610
|
+
loadOpenApiSpecText,
|
|
1611
|
+
resolveOpenApiBackedTools,
|
|
1612
|
+
invokeOpenApiBackedTool,
|
|
1613
|
+
resolveOpenApiBackedAnnotations,
|
|
1540
1614
|
openApiPlugin
|
|
1541
1615
|
};
|
|
1542
|
-
//# sourceMappingURL=chunk-
|
|
1616
|
+
//# sourceMappingURL=chunk-3FM2SWM4.js.map
|