@executor-js/plugin-openapi 1.4.32 → 1.5.0
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-7M52SRUX.js +893 -0
- package/dist/AddOpenApiSource-7M52SRUX.js.map +1 -0
- package/dist/EditOpenApiSource-WTAMRJUK.js +68 -0
- package/dist/EditOpenApiSource-WTAMRJUK.js.map +1 -0
- package/dist/OpenApiAccountsPanel-3VJJXNQF.js +112 -0
- package/dist/OpenApiAccountsPanel-3VJJXNQF.js.map +1 -0
- package/dist/api/group.d.ts +104 -225
- package/dist/api/index.d.ts +127 -271
- package/dist/{chunk-BB5IAKRG.js → chunk-AQ7JDDRM.js} +12 -2
- package/dist/chunk-AQ7JDDRM.js.map +1 -0
- package/dist/chunk-BSLE6HCE.js +181 -0
- package/dist/chunk-BSLE6HCE.js.map +1 -0
- package/dist/chunk-MZWZQ24W.js +226 -0
- package/dist/chunk-MZWZQ24W.js.map +1 -0
- package/dist/chunk-OSIFYIQP.js +623 -0
- package/dist/chunk-OSIFYIQP.js.map +1 -0
- package/dist/chunk-QSSRVK6M.js +139 -0
- package/dist/chunk-QSSRVK6M.js.map +1 -0
- package/dist/chunk-V7VCHYOY.js +1544 -0
- package/dist/chunk-V7VCHYOY.js.map +1 -0
- package/dist/{chunk-AN4HJFNP.js → chunk-YVRI7KRC.js} +162 -186
- package/dist/chunk-YVRI7KRC.js.map +1 -0
- package/dist/client.js +9 -8
- package/dist/client.js.map +1 -1
- package/dist/core.js +28 -11
- package/dist/index.js +11 -4
- package/dist/react/AddOpenApiSource.d.ts +2 -13
- package/dist/react/GoogleProductPicker.d.ts +9 -0
- package/dist/react/OpenApiAccountsPanel.d.ts +6 -0
- package/dist/react/OpenApiSourceDetailsFields.d.ts +3 -2
- package/dist/react/atoms.d.ts +177 -192
- package/dist/react/auth-method-config.d.ts +15 -0
- package/dist/react/client.d.ts +103 -224
- package/dist/react/index.d.ts +2 -2
- package/dist/react/source-plugin.d.ts +2 -2
- package/dist/sdk/config.d.ts +75 -0
- package/dist/sdk/configure.test.d.ts +1 -0
- package/dist/sdk/describe-auth-methods.test.d.ts +1 -0
- package/dist/sdk/errors.d.ts +4 -6
- package/dist/sdk/extract.d.ts +7 -5
- package/dist/sdk/google-bundle.test.d.ts +1 -0
- package/dist/sdk/google-discovery.d.ts +43 -0
- package/dist/sdk/google-discovery.test.d.ts +1 -0
- package/dist/sdk/google-oauth-batches.d.ts +13 -0
- package/dist/sdk/google-oauth-batches.test.d.ts +1 -0
- package/dist/sdk/google-oauth-scopes.d.ts +3 -0
- package/dist/sdk/google-oauth-scopes.test.d.ts +1 -0
- package/dist/sdk/google-presets.d.ts +16 -0
- package/dist/sdk/google-presets.test.d.ts +1 -0
- package/dist/sdk/google-product-picker-scopes.test.d.ts +1 -0
- package/dist/sdk/index.d.ts +7 -5
- package/dist/sdk/invoke.d.ts +6 -9
- package/dist/sdk/openapi-utils.d.ts +1 -0
- package/dist/sdk/plugin.d.ts +74 -231
- package/dist/sdk/presets.d.ts +2 -1
- package/dist/sdk/preview.d.ts +20 -15
- package/dist/sdk/query-serialization.test.d.ts +1 -0
- package/dist/sdk/store.d.ts +14 -41
- package/dist/sdk/types.d.ts +23 -51
- package/dist/testing/index.d.ts +49 -38
- package/dist/testing.js +46 -18
- package/dist/testing.js.map +1 -1
- package/package.json +6 -4
- package/dist/AddOpenApiSource-NSCULGTM.js +0 -19
- package/dist/AddOpenApiSource-NSCULGTM.js.map +0 -1
- package/dist/EditOpenApiSource-MV7NYTRP.js +0 -774
- package/dist/EditOpenApiSource-MV7NYTRP.js.map +0 -1
- package/dist/OpenApiSourceSummary-7JBS7PUV.js +0 -122
- package/dist/OpenApiSourceSummary-7JBS7PUV.js.map +0 -1
- package/dist/chunk-2ZKKZYZH.js +0 -1181
- package/dist/chunk-2ZKKZYZH.js.map +0 -1
- package/dist/chunk-AN4HJFNP.js.map +0 -1
- package/dist/chunk-BB5IAKRG.js.map +0 -1
- package/dist/chunk-PS5XP3DG.js +0 -2086
- package/dist/chunk-PS5XP3DG.js.map +0 -1
- package/dist/chunk-X5JX3KTA.js +0 -201
- package/dist/chunk-X5JX3KTA.js.map +0 -1
- package/dist/react/OpenApiSourceSummary.d.ts +0 -5
- package/dist/sdk/credential-status.d.ts +0 -23
- package/dist/sdk/source-contracts.d.ts +0 -55
- /package/dist/{sdk/credential-status.test.d.ts → react/auth-method-config.test.d.ts} +0 -0
|
@@ -0,0 +1,1544 @@
|
|
|
1
|
+
import {
|
|
2
|
+
convertGoogleDiscoveryBundleToOpenApi,
|
|
3
|
+
convertGoogleDiscoveryToOpenApi,
|
|
4
|
+
fetchGoogleDiscoveryDocument,
|
|
5
|
+
isGoogleDiscoveryUrl
|
|
6
|
+
} from "./chunk-OSIFYIQP.js";
|
|
7
|
+
import {
|
|
8
|
+
openApiPresets
|
|
9
|
+
} from "./chunk-AQ7JDDRM.js";
|
|
10
|
+
import {
|
|
11
|
+
InvocationResult,
|
|
12
|
+
OpenApiExtractionError,
|
|
13
|
+
OpenApiInvocationError,
|
|
14
|
+
OperationBinding,
|
|
15
|
+
TOKEN_VARIABLE,
|
|
16
|
+
extract,
|
|
17
|
+
parse,
|
|
18
|
+
previewSpec,
|
|
19
|
+
resolveSpecText
|
|
20
|
+
} from "./chunk-YVRI7KRC.js";
|
|
21
|
+
|
|
22
|
+
// src/sdk/config.ts
|
|
23
|
+
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
|
+
});
|
|
38
|
+
var OAuthAuthenticationSchema = Schema.Struct({
|
|
39
|
+
slug: Schema.String,
|
|
40
|
+
type: Schema.Literal("oauth"),
|
|
41
|
+
authorizationUrl: Schema.String,
|
|
42
|
+
tokenUrl: Schema.String,
|
|
43
|
+
scopes: Schema.Array(Schema.String)
|
|
44
|
+
});
|
|
45
|
+
var AuthenticationSchema = Schema.Union([
|
|
46
|
+
OAuthAuthenticationSchema,
|
|
47
|
+
APIKeyAuthenticationSchema
|
|
48
|
+
]);
|
|
49
|
+
var OpenApiIntegrationConfigSchema = Schema.Struct({
|
|
50
|
+
/** Inlined OpenAPI document text (resolved + parsed source of truth). */
|
|
51
|
+
spec: Schema.String,
|
|
52
|
+
/** Origin URL the spec was fetched from, when known. Enables refresh. */
|
|
53
|
+
sourceUrl: Schema.optional(Schema.String),
|
|
54
|
+
/** Google Discovery bundle URLs, when the spec came from a Google bundle. */
|
|
55
|
+
googleDiscoveryUrls: Schema.optional(Schema.Array(Schema.String)),
|
|
56
|
+
/** Base URL override; falls back to the spec's first server. */
|
|
57
|
+
baseUrl: Schema.optional(Schema.String),
|
|
58
|
+
/** Static headers applied to every request (no secret material). */
|
|
59
|
+
headers: Schema.optional(Schema.Record(Schema.String, Schema.String)),
|
|
60
|
+
/** Static query params applied to every request (no secret material). */
|
|
61
|
+
queryParams: Schema.optional(Schema.Record(Schema.String, Schema.String)),
|
|
62
|
+
/** The auth methods a connection's value can be applied through. */
|
|
63
|
+
authenticationTemplate: Schema.optional(Schema.Array(AuthenticationSchema))
|
|
64
|
+
});
|
|
65
|
+
var decodeConfig = Schema.decodeUnknownOption(OpenApiIntegrationConfigSchema);
|
|
66
|
+
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
|
+
var renderAuthTemplate = (template, values) => {
|
|
73
|
+
if (template.type === "oauth") {
|
|
74
|
+
return {
|
|
75
|
+
headers: { authorization: `Bearer ${values[TOKEN_VARIABLE] ?? ""}` },
|
|
76
|
+
queryParams: {}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
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 };
|
|
88
|
+
};
|
|
89
|
+
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];
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// src/sdk/invoke.ts
|
|
104
|
+
import { Effect, Layer, Option as Option2 } from "effect";
|
|
105
|
+
import { HttpClient, HttpClientRequest } from "effect/unstable/http";
|
|
106
|
+
var CONTAINER_KEYS = {
|
|
107
|
+
path: ["path", "pathParams", "params"],
|
|
108
|
+
query: ["query", "queryParams", "params"],
|
|
109
|
+
header: ["headers", "header"],
|
|
110
|
+
cookie: ["cookies", "cookie"]
|
|
111
|
+
};
|
|
112
|
+
var readParamValue = (args, param) => {
|
|
113
|
+
const direct = args[param.name];
|
|
114
|
+
if (direct !== void 0) return direct;
|
|
115
|
+
for (const key of CONTAINER_KEYS[param.location] ?? []) {
|
|
116
|
+
const container = args[key];
|
|
117
|
+
if (typeof container === "object" && container !== null && !Array.isArray(container)) {
|
|
118
|
+
const nested = container[param.name];
|
|
119
|
+
if (nested !== void 0) return nested;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return void 0;
|
|
123
|
+
};
|
|
124
|
+
var primitiveToString = (value) => typeof value === "object" && value !== null ? JSON.stringify(value) : String(value);
|
|
125
|
+
var RESERVED_UNENCODED_RE = /[A-Za-z0-9\-._~:/?#[\]@!$&'()*+,;=]/;
|
|
126
|
+
var encodeReservedAware = (raw, allowReserved) => {
|
|
127
|
+
if (!allowReserved) return encodeURIComponent(raw);
|
|
128
|
+
let out = "";
|
|
129
|
+
for (const ch of raw) {
|
|
130
|
+
out += RESERVED_UNENCODED_RE.test(ch) ? ch : encodeURIComponent(ch);
|
|
131
|
+
}
|
|
132
|
+
return out;
|
|
133
|
+
};
|
|
134
|
+
var queryParamValues = (value, param) => {
|
|
135
|
+
if (value === void 0 || value === null) return [];
|
|
136
|
+
if (!Array.isArray(value)) return [primitiveToString(value)];
|
|
137
|
+
const style = Option2.getOrUndefined(param.style) ?? "form";
|
|
138
|
+
const explode = Option2.getOrElse(param.explode, () => true);
|
|
139
|
+
if (explode) return value.map(primitiveToString);
|
|
140
|
+
const separator = style === "spaceDelimited" ? " " : style === "pipeDelimited" ? "|" : ",";
|
|
141
|
+
return [value.map(primitiveToString).join(separator)];
|
|
142
|
+
};
|
|
143
|
+
var resolvePath = Effect.fn("OpenApi.resolvePath")(function* (pathTemplate, args, parameters) {
|
|
144
|
+
let resolved = pathTemplate;
|
|
145
|
+
for (const param of parameters) {
|
|
146
|
+
if (param.location !== "path") continue;
|
|
147
|
+
const value = readParamValue(args, param);
|
|
148
|
+
if (value === void 0 || value === null) {
|
|
149
|
+
if (param.required) {
|
|
150
|
+
return yield* new OpenApiInvocationError({
|
|
151
|
+
message: `Missing required path parameter: ${param.name}`,
|
|
152
|
+
statusCode: Option2.none()
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const encoded = encodeReservedAware(
|
|
158
|
+
String(value),
|
|
159
|
+
Option2.getOrElse(param.allowReserved, () => false)
|
|
160
|
+
);
|
|
161
|
+
resolved = resolved.replaceAll(`{${param.name}}`, encoded);
|
|
162
|
+
resolved = resolved.replaceAll(`{+${param.name}}`, encoded);
|
|
163
|
+
}
|
|
164
|
+
const remaining = [...resolved.matchAll(/\{([^{}]+)\}/g)].map((m) => m[1]).filter((v) => typeof v === "string");
|
|
165
|
+
for (const name of remaining) {
|
|
166
|
+
const value = args[name];
|
|
167
|
+
if (value !== void 0 && value !== null) {
|
|
168
|
+
resolved = resolved.replaceAll(`{${name}}`, encodeURIComponent(String(value)));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const unresolved = [...resolved.matchAll(/\{([^{}]+)\}/g)].map((m) => m[1]).filter((v) => typeof v === "string");
|
|
172
|
+
if (unresolved.length > 0) {
|
|
173
|
+
return yield* new OpenApiInvocationError({
|
|
174
|
+
message: `Unresolved path parameters: ${[...new Set(unresolved)].join(", ")}`,
|
|
175
|
+
statusCode: Option2.none()
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return resolved;
|
|
179
|
+
});
|
|
180
|
+
var applyHeaders = (request, headers) => {
|
|
181
|
+
let req = request;
|
|
182
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
183
|
+
req = HttpClientRequest.setHeader(req, name, value);
|
|
184
|
+
}
|
|
185
|
+
return req;
|
|
186
|
+
};
|
|
187
|
+
var normalizeContentType = (ct) => ct?.split(";")[0]?.trim().toLowerCase() ?? "";
|
|
188
|
+
var isJsonContentType = (ct) => {
|
|
189
|
+
const normalized = normalizeContentType(ct);
|
|
190
|
+
if (!normalized) return false;
|
|
191
|
+
return normalized === "application/json" || normalized.includes("+json") || normalized.includes("json");
|
|
192
|
+
};
|
|
193
|
+
var isFormUrlEncoded = (ct) => normalizeContentType(ct) === "application/x-www-form-urlencoded";
|
|
194
|
+
var isMultipartFormData = (ct) => normalizeContentType(ct).startsWith("multipart/form-data");
|
|
195
|
+
var isXmlContentType = (ct) => {
|
|
196
|
+
const normalized = normalizeContentType(ct);
|
|
197
|
+
if (!normalized) return false;
|
|
198
|
+
return normalized === "application/xml" || normalized === "text/xml" || normalized.endsWith("+xml");
|
|
199
|
+
};
|
|
200
|
+
var isTextContentType = (ct) => normalizeContentType(ct).startsWith("text/");
|
|
201
|
+
var isOctetStream = (ct) => normalizeContentType(ct) === "application/octet-stream";
|
|
202
|
+
var toUint8Array = (value) => {
|
|
203
|
+
if (value instanceof Uint8Array) return value;
|
|
204
|
+
if (value instanceof ArrayBuffer) return new Uint8Array(value);
|
|
205
|
+
if (ArrayBuffer.isView(value)) {
|
|
206
|
+
const view = value;
|
|
207
|
+
return new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
|
|
208
|
+
}
|
|
209
|
+
if (Array.isArray(value) && value.every((v) => typeof v === "number")) {
|
|
210
|
+
return new Uint8Array(value);
|
|
211
|
+
}
|
|
212
|
+
return null;
|
|
213
|
+
};
|
|
214
|
+
var toArrayBuffer = (bytes) => {
|
|
215
|
+
const copy = new ArrayBuffer(bytes.byteLength);
|
|
216
|
+
new Uint8Array(copy).set(bytes);
|
|
217
|
+
return copy;
|
|
218
|
+
};
|
|
219
|
+
var DEFAULT_FORM_STYLE = {
|
|
220
|
+
style: "form",
|
|
221
|
+
explode: true,
|
|
222
|
+
allowReserved: false
|
|
223
|
+
};
|
|
224
|
+
var resolveStyleExplode = (e) => {
|
|
225
|
+
if (!e) return DEFAULT_FORM_STYLE;
|
|
226
|
+
return {
|
|
227
|
+
style: Option2.getOrElse(e.style, () => DEFAULT_FORM_STYLE.style),
|
|
228
|
+
explode: Option2.getOrElse(e.explode, () => DEFAULT_FORM_STYLE.explode),
|
|
229
|
+
allowReserved: Option2.getOrElse(e.allowReserved, () => DEFAULT_FORM_STYLE.allowReserved)
|
|
230
|
+
};
|
|
231
|
+
};
|
|
232
|
+
var encodeFormValue = (v, allowReserved) => {
|
|
233
|
+
const raw = typeof v === "object" && v !== null ? JSON.stringify(v) : String(v);
|
|
234
|
+
return encodeReservedAware(raw, allowReserved);
|
|
235
|
+
};
|
|
236
|
+
var serializeFormUrlEncoded = (value, encoding) => {
|
|
237
|
+
const parts = [];
|
|
238
|
+
for (const [key, raw] of Object.entries(value)) {
|
|
239
|
+
if (raw === void 0 || raw === null) continue;
|
|
240
|
+
const { style, explode, allowReserved } = resolveStyleExplode(encoding?.[key]);
|
|
241
|
+
const encKey = encodeURIComponent(key);
|
|
242
|
+
if (Array.isArray(raw)) {
|
|
243
|
+
if (explode) {
|
|
244
|
+
for (const v of raw) {
|
|
245
|
+
parts.push(`${encKey}=${encodeFormValue(v, allowReserved)}`);
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
const sep = style === "spaceDelimited" ? " " : style === "pipeDelimited" ? "|" : ",";
|
|
249
|
+
parts.push(
|
|
250
|
+
`${encKey}=${encodeFormValue(
|
|
251
|
+
raw.map((v) => typeof v === "object" ? JSON.stringify(v) : String(v)).join(sep),
|
|
252
|
+
allowReserved
|
|
253
|
+
)}`
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
if (typeof raw === "object") {
|
|
259
|
+
const entries = Object.entries(raw).filter(
|
|
260
|
+
([, v]) => v !== void 0 && v !== null
|
|
261
|
+
);
|
|
262
|
+
if (style === "deepObject") {
|
|
263
|
+
for (const [subkey, subval] of entries) {
|
|
264
|
+
parts.push(
|
|
265
|
+
`${encodeURIComponent(`${key}[${subkey}]`)}=${encodeFormValue(subval, allowReserved)}`
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
} else if (explode) {
|
|
269
|
+
for (const [subkey, subval] of entries) {
|
|
270
|
+
parts.push(`${encodeURIComponent(subkey)}=${encodeFormValue(subval, allowReserved)}`);
|
|
271
|
+
}
|
|
272
|
+
} else {
|
|
273
|
+
const flat = entries.flatMap(([k, v]) => [
|
|
274
|
+
k,
|
|
275
|
+
typeof v === "object" ? JSON.stringify(v) : String(v)
|
|
276
|
+
]);
|
|
277
|
+
parts.push(`${encKey}=${encodeFormValue(flat.join(","), allowReserved)}`);
|
|
278
|
+
}
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
parts.push(`${encKey}=${encodeFormValue(raw, allowReserved)}`);
|
|
282
|
+
}
|
|
283
|
+
return parts.join("&");
|
|
284
|
+
};
|
|
285
|
+
var coerceFormDataRecord = (value, encoding) => {
|
|
286
|
+
const out = {};
|
|
287
|
+
for (const [key, raw] of Object.entries(value)) {
|
|
288
|
+
if (raw === void 0 || raw === null) continue;
|
|
289
|
+
const partType = encoding?.[key] ? Option2.getOrUndefined(encoding[key].contentType) : void 0;
|
|
290
|
+
if (partType) {
|
|
291
|
+
const isJson = partType.startsWith("application/json") || partType.includes("+json");
|
|
292
|
+
const serialized = typeof raw === "string" ? raw : isJson ? JSON.stringify(raw) : typeof raw === "object" ? JSON.stringify(raw) : String(raw);
|
|
293
|
+
out[key] = new Blob([serialized], { type: partType });
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
if (typeof raw === "string" || typeof raw === "number" || typeof raw === "boolean" || raw instanceof Blob || typeof File !== "undefined" && raw instanceof File) {
|
|
297
|
+
out[key] = raw;
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
if (Array.isArray(raw)) {
|
|
301
|
+
out[key] = raw.map(
|
|
302
|
+
(v) => typeof v === "string" || typeof v === "number" || typeof v === "boolean" || v instanceof Blob || typeof File !== "undefined" && v instanceof File ? v : JSON.stringify(v)
|
|
303
|
+
);
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
const bytes = toUint8Array(raw);
|
|
307
|
+
if (bytes) {
|
|
308
|
+
out[key] = new Blob([toArrayBuffer(bytes)]);
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
out[key] = JSON.stringify(raw);
|
|
312
|
+
}
|
|
313
|
+
return out;
|
|
314
|
+
};
|
|
315
|
+
var applyRequestBody = (request, contentType, bodyValue, encoding) => {
|
|
316
|
+
if (isJsonContentType(contentType)) {
|
|
317
|
+
if (typeof bodyValue === "string") {
|
|
318
|
+
return HttpClientRequest.bodyText(request, bodyValue, contentType);
|
|
319
|
+
}
|
|
320
|
+
return HttpClientRequest.bodyJsonUnsafe(request, bodyValue);
|
|
321
|
+
}
|
|
322
|
+
if (isFormUrlEncoded(contentType)) {
|
|
323
|
+
if (typeof bodyValue === "string") {
|
|
324
|
+
return HttpClientRequest.bodyText(request, bodyValue, contentType);
|
|
325
|
+
}
|
|
326
|
+
if (typeof bodyValue === "object" && bodyValue !== null && !Array.isArray(bodyValue)) {
|
|
327
|
+
const serialized = serializeFormUrlEncoded(bodyValue, encoding);
|
|
328
|
+
return HttpClientRequest.bodyText(request, serialized, contentType);
|
|
329
|
+
}
|
|
330
|
+
return HttpClientRequest.bodyUrlParams(
|
|
331
|
+
request,
|
|
332
|
+
bodyValue
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
if (isMultipartFormData(contentType)) {
|
|
336
|
+
if (bodyValue instanceof FormData) {
|
|
337
|
+
return HttpClientRequest.bodyFormData(request, bodyValue);
|
|
338
|
+
}
|
|
339
|
+
if (typeof bodyValue === "object" && bodyValue !== null) {
|
|
340
|
+
return HttpClientRequest.bodyFormDataRecord(
|
|
341
|
+
request,
|
|
342
|
+
coerceFormDataRecord(bodyValue, encoding)
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
return HttpClientRequest.bodyText(request, String(bodyValue), contentType);
|
|
346
|
+
}
|
|
347
|
+
if (isOctetStream(contentType)) {
|
|
348
|
+
const bytes2 = toUint8Array(bodyValue);
|
|
349
|
+
if (bytes2) return HttpClientRequest.bodyUint8Array(request, bytes2, contentType);
|
|
350
|
+
if (typeof bodyValue === "string") {
|
|
351
|
+
return HttpClientRequest.bodyText(request, bodyValue, contentType);
|
|
352
|
+
}
|
|
353
|
+
return HttpClientRequest.bodyText(request, JSON.stringify(bodyValue), contentType);
|
|
354
|
+
}
|
|
355
|
+
if (isXmlContentType(contentType) || isTextContentType(contentType)) {
|
|
356
|
+
if (typeof bodyValue === "string") {
|
|
357
|
+
return HttpClientRequest.bodyText(request, bodyValue, contentType);
|
|
358
|
+
}
|
|
359
|
+
const bytes2 = toUint8Array(bodyValue);
|
|
360
|
+
if (bytes2) return HttpClientRequest.bodyUint8Array(request, bytes2, contentType);
|
|
361
|
+
return HttpClientRequest.bodyText(request, JSON.stringify(bodyValue), contentType);
|
|
362
|
+
}
|
|
363
|
+
if (typeof bodyValue === "string") {
|
|
364
|
+
return HttpClientRequest.bodyText(request, bodyValue, contentType);
|
|
365
|
+
}
|
|
366
|
+
const bytes = toUint8Array(bodyValue);
|
|
367
|
+
if (bytes) return HttpClientRequest.bodyUint8Array(request, bytes, contentType);
|
|
368
|
+
return HttpClientRequest.bodyText(request, JSON.stringify(bodyValue), contentType);
|
|
369
|
+
};
|
|
370
|
+
var invoke = Effect.fn("OpenApi.invoke")(function* (operation, args, resolvedHeaders, sourceQueryParams = {}) {
|
|
371
|
+
const client = yield* HttpClient.HttpClient;
|
|
372
|
+
yield* Effect.annotateCurrentSpan({
|
|
373
|
+
"http.method": operation.method.toUpperCase(),
|
|
374
|
+
"http.route": operation.pathTemplate,
|
|
375
|
+
"plugin.openapi.method": operation.method.toUpperCase(),
|
|
376
|
+
"plugin.openapi.path_template": operation.pathTemplate,
|
|
377
|
+
"plugin.openapi.headers.resolved_count": Object.keys(resolvedHeaders).length
|
|
378
|
+
});
|
|
379
|
+
const resolvedPath = yield* resolvePath(operation.pathTemplate, args, operation.parameters);
|
|
380
|
+
const path = resolvedPath.startsWith("/") ? resolvedPath : `/${resolvedPath}`;
|
|
381
|
+
let request = HttpClientRequest.make(operation.method.toUpperCase())(path);
|
|
382
|
+
for (const [name, value] of Object.entries(sourceQueryParams)) {
|
|
383
|
+
request = HttpClientRequest.setUrlParam(request, name, value);
|
|
384
|
+
}
|
|
385
|
+
for (const param of operation.parameters) {
|
|
386
|
+
if (param.location !== "query") continue;
|
|
387
|
+
const value = readParamValue(args, param);
|
|
388
|
+
for (const paramValue of queryParamValues(value, param)) {
|
|
389
|
+
request = HttpClientRequest.appendUrlParam(request, param.name, paramValue);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
for (const param of operation.parameters) {
|
|
393
|
+
if (param.location !== "header") continue;
|
|
394
|
+
const value = readParamValue(args, param);
|
|
395
|
+
if (value === void 0 || value === null) continue;
|
|
396
|
+
request = HttpClientRequest.setHeader(request, param.name, String(value));
|
|
397
|
+
}
|
|
398
|
+
if (Option2.isSome(operation.requestBody)) {
|
|
399
|
+
const rb = operation.requestBody.value;
|
|
400
|
+
const bodyValue = args.body ?? args.input;
|
|
401
|
+
if (bodyValue !== void 0) {
|
|
402
|
+
const contentsOpt = Option2.getOrUndefined(rb.contents);
|
|
403
|
+
const requestedCt = typeof args.contentType === "string" ? args.contentType : void 0;
|
|
404
|
+
const selected = contentsOpt && requestedCt ? contentsOpt.find((c) => c.contentType === requestedCt) : void 0;
|
|
405
|
+
const chosenCt = selected?.contentType ?? rb.contentType;
|
|
406
|
+
const chosenEncoding = selected ? Option2.getOrUndefined(selected.encoding) : contentsOpt && contentsOpt[0] ? Option2.getOrUndefined(contentsOpt[0].encoding) : void 0;
|
|
407
|
+
request = applyRequestBody(request, chosenCt, bodyValue, chosenEncoding);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
request = applyHeaders(request, resolvedHeaders);
|
|
411
|
+
const response = yield* client.execute(request).pipe(
|
|
412
|
+
Effect.mapError(
|
|
413
|
+
(err) => new OpenApiInvocationError({
|
|
414
|
+
message: "HTTP request failed",
|
|
415
|
+
statusCode: Option2.none(),
|
|
416
|
+
cause: err
|
|
417
|
+
})
|
|
418
|
+
)
|
|
419
|
+
);
|
|
420
|
+
const status = response.status;
|
|
421
|
+
yield* Effect.annotateCurrentSpan({
|
|
422
|
+
"http.status_code": status
|
|
423
|
+
});
|
|
424
|
+
const responseHeaders = { ...response.headers };
|
|
425
|
+
const contentType = response.headers["content-type"] ?? null;
|
|
426
|
+
const mapBodyError = Effect.mapError(
|
|
427
|
+
(err) => new OpenApiInvocationError({
|
|
428
|
+
message: "Failed to read response body",
|
|
429
|
+
statusCode: Option2.some(status),
|
|
430
|
+
cause: err
|
|
431
|
+
})
|
|
432
|
+
);
|
|
433
|
+
const responseBody = status === 204 ? null : isJsonContentType(contentType) ? yield* response.json.pipe(
|
|
434
|
+
Effect.catch(() => response.text),
|
|
435
|
+
mapBodyError
|
|
436
|
+
) : yield* response.text.pipe(mapBodyError);
|
|
437
|
+
const ok = status >= 200 && status < 300;
|
|
438
|
+
return InvocationResult.make({
|
|
439
|
+
status,
|
|
440
|
+
headers: responseHeaders,
|
|
441
|
+
data: ok ? responseBody : null,
|
|
442
|
+
error: ok ? null : responseBody
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
var invokeWithLayer = (operation, args, baseUrl, resolvedHeaders, sourceQueryParams, httpClientLayer) => {
|
|
446
|
+
const operationBaseUrl = operation.baseUrl;
|
|
447
|
+
const effectiveBaseUrl = operationBaseUrl ?? baseUrl;
|
|
448
|
+
const clientWithBaseUrl = effectiveBaseUrl ? Layer.effect(
|
|
449
|
+
HttpClient.HttpClient,
|
|
450
|
+
Effect.map(
|
|
451
|
+
Effect.service(HttpClient.HttpClient),
|
|
452
|
+
HttpClient.mapRequest(HttpClientRequest.prependUrl(effectiveBaseUrl))
|
|
453
|
+
)
|
|
454
|
+
).pipe(Layer.provide(httpClientLayer)) : httpClientLayer;
|
|
455
|
+
return invoke(operation, args, resolvedHeaders, sourceQueryParams).pipe(
|
|
456
|
+
Effect.provide(clientWithBaseUrl),
|
|
457
|
+
Effect.withSpan("plugin.openapi.invoke", {
|
|
458
|
+
attributes: {
|
|
459
|
+
"plugin.openapi.method": operation.method.toUpperCase(),
|
|
460
|
+
"plugin.openapi.path_template": operation.pathTemplate,
|
|
461
|
+
"plugin.openapi.base_url": effectiveBaseUrl
|
|
462
|
+
}
|
|
463
|
+
})
|
|
464
|
+
);
|
|
465
|
+
};
|
|
466
|
+
var REQUIRE_APPROVAL = /* @__PURE__ */ new Set(["post", "put", "patch", "delete"]);
|
|
467
|
+
var annotationsForOperation = (method, pathTemplate) => {
|
|
468
|
+
const m = method.toLowerCase();
|
|
469
|
+
if (!REQUIRE_APPROVAL.has(m)) return {};
|
|
470
|
+
return {
|
|
471
|
+
requiresApproval: true,
|
|
472
|
+
approvalDescription: `${method.toUpperCase()} ${pathTemplate}`
|
|
473
|
+
};
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
// src/sdk/store.ts
|
|
477
|
+
import { Effect as Effect2, Option as Option3, Predicate, Schema as Schema2 } from "effect";
|
|
478
|
+
var OPERATION_COLLECTION = "operation";
|
|
479
|
+
var STORE_OWNER = "org";
|
|
480
|
+
var encodeBinding = Schema2.encodeSync(OperationBinding);
|
|
481
|
+
var decodeBinding = Schema2.decodeUnknownSync(OperationBinding);
|
|
482
|
+
var decodeBindingJson = Schema2.decodeUnknownSync(Schema2.fromJsonString(OperationBinding));
|
|
483
|
+
var toJsonRecord = (value) => value;
|
|
484
|
+
var OperationStorage = Schema2.Struct({
|
|
485
|
+
integration: Schema2.String,
|
|
486
|
+
toolName: Schema2.String,
|
|
487
|
+
binding: Schema2.Unknown
|
|
488
|
+
});
|
|
489
|
+
var decodeOperationStorage = Schema2.decodeUnknownOption(OperationStorage);
|
|
490
|
+
var rowToOperation = (row) => {
|
|
491
|
+
const decoded = decodeOperationStorage(row.data);
|
|
492
|
+
if (Option3.isNone(decoded)) return null;
|
|
493
|
+
const operation = decoded.value;
|
|
494
|
+
return {
|
|
495
|
+
integration: operation.integration,
|
|
496
|
+
toolName: operation.toolName,
|
|
497
|
+
binding: decodeBinding(
|
|
498
|
+
typeof operation.binding === "string" ? decodeBindingJson(operation.binding) : operation.binding
|
|
499
|
+
)
|
|
500
|
+
};
|
|
501
|
+
};
|
|
502
|
+
var operationKey = (integration, toolName) => `${integration}.${toolName}`;
|
|
503
|
+
var makeDefaultOpenapiStore = ({ pluginStorage }) => {
|
|
504
|
+
const operationData = (operation) => ({
|
|
505
|
+
integration: operation.integration,
|
|
506
|
+
toolName: operation.toolName,
|
|
507
|
+
binding: toJsonRecord(encodeBinding(operation.binding))
|
|
508
|
+
});
|
|
509
|
+
const listRows = (integration) => pluginStorage.list({ collection: OPERATION_COLLECTION, keyPrefix: `${integration}.` }).pipe(
|
|
510
|
+
Effect2.map(
|
|
511
|
+
(rows) => rows.filter((row) => rowToOperation(row)?.integration === integration)
|
|
512
|
+
)
|
|
513
|
+
);
|
|
514
|
+
const removeOperations = (integration) => Effect2.gen(function* () {
|
|
515
|
+
const rows = yield* listRows(integration);
|
|
516
|
+
yield* pluginStorage.removeMany({
|
|
517
|
+
owner: STORE_OWNER,
|
|
518
|
+
entries: rows.map((row) => ({ collection: OPERATION_COLLECTION, key: row.key }))
|
|
519
|
+
});
|
|
520
|
+
});
|
|
521
|
+
return {
|
|
522
|
+
putOperations: (integration, operations) => Effect2.gen(function* () {
|
|
523
|
+
yield* removeOperations(integration);
|
|
524
|
+
yield* pluginStorage.putMany({
|
|
525
|
+
owner: STORE_OWNER,
|
|
526
|
+
entries: operations.map((operation) => ({
|
|
527
|
+
collection: OPERATION_COLLECTION,
|
|
528
|
+
key: operationKey(integration, operation.toolName),
|
|
529
|
+
data: operationData(operation)
|
|
530
|
+
}))
|
|
531
|
+
});
|
|
532
|
+
}),
|
|
533
|
+
getOperation: (integration, toolName) => pluginStorage.get({ collection: OPERATION_COLLECTION, key: operationKey(integration, toolName) }).pipe(Effect2.map((row) => row ? rowToOperation(row) : null)),
|
|
534
|
+
listOperations: (integration) => listRows(integration).pipe(
|
|
535
|
+
Effect2.map((rows) => rows.map(rowToOperation).filter(Predicate.isNotNull))
|
|
536
|
+
),
|
|
537
|
+
removeOperations
|
|
538
|
+
};
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
// src/sdk/plugin.ts
|
|
542
|
+
import { Effect as Effect3, Option as Option5, Schema as Schema3 } from "effect";
|
|
543
|
+
import {
|
|
544
|
+
AuthTemplateSlug,
|
|
545
|
+
IntegrationAlreadyExistsError,
|
|
546
|
+
IntegrationDetectionResult,
|
|
547
|
+
IntegrationSlug,
|
|
548
|
+
ToolName,
|
|
549
|
+
ToolResult,
|
|
550
|
+
authToolFailure,
|
|
551
|
+
definePlugin,
|
|
552
|
+
tool
|
|
553
|
+
} from "@executor-js/sdk/core";
|
|
554
|
+
|
|
555
|
+
// src/sdk/definitions.ts
|
|
556
|
+
import { Option as Option4 } from "effect";
|
|
557
|
+
var splitWords = (value) => value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z0-9]+)/g, "$1 $2").replace(/[^a-zA-Z0-9]+/g, " ").trim().split(/\s+/).filter((part) => part.length > 0);
|
|
558
|
+
var normalizeWord = (value) => value.toLowerCase();
|
|
559
|
+
var toCamelCase = (value) => {
|
|
560
|
+
const words = splitWords(value).map(normalizeWord);
|
|
561
|
+
if (words.length === 0) return "tool";
|
|
562
|
+
const [first, ...rest] = words;
|
|
563
|
+
return `${first}${rest.map((p) => `${p[0]?.toUpperCase() ?? ""}${p.slice(1)}`).join("")}`;
|
|
564
|
+
};
|
|
565
|
+
var toPascalCase = (value) => {
|
|
566
|
+
const camel = toCamelCase(value);
|
|
567
|
+
return `${camel[0]?.toUpperCase() ?? ""}${camel.slice(1)}`;
|
|
568
|
+
};
|
|
569
|
+
var VERSION_SEGMENT_REGEX = /^v\d+(?:[._-]\d+)?$/i;
|
|
570
|
+
var IGNORED_PATH_SEGMENTS = /* @__PURE__ */ new Set(["api"]);
|
|
571
|
+
var pathSegmentsFromTemplate = (pathTemplate) => pathTemplate.split("/").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
572
|
+
var isPathParameterSegment = (segment) => segment.startsWith("{") && segment.endsWith("}");
|
|
573
|
+
var normalizeGroupSegment = (value) => {
|
|
574
|
+
const candidate = value?.trim();
|
|
575
|
+
if (!candidate) return null;
|
|
576
|
+
return toCamelCase(candidate);
|
|
577
|
+
};
|
|
578
|
+
var deriveVersionSegment = (pathTemplate) => pathSegmentsFromTemplate(pathTemplate).map((s) => s.toLowerCase()).find((s) => VERSION_SEGMENT_REGEX.test(s));
|
|
579
|
+
var derivePathGroup = (pathTemplate) => {
|
|
580
|
+
for (const segment of pathSegmentsFromTemplate(pathTemplate)) {
|
|
581
|
+
const lower = segment.toLowerCase();
|
|
582
|
+
if (VERSION_SEGMENT_REGEX.test(lower)) continue;
|
|
583
|
+
if (IGNORED_PATH_SEGMENTS.has(lower)) continue;
|
|
584
|
+
if (isPathParameterSegment(segment)) continue;
|
|
585
|
+
return normalizeGroupSegment(segment) ?? "root";
|
|
586
|
+
}
|
|
587
|
+
return "root";
|
|
588
|
+
};
|
|
589
|
+
var splitOperationIdSegments = (value) => value.split(/[/.]+/).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
590
|
+
var deriveLeafSeed = (operationId, group) => {
|
|
591
|
+
const segments = splitOperationIdSegments(operationId);
|
|
592
|
+
if (segments.length > 1) {
|
|
593
|
+
const [first, ...rest] = segments;
|
|
594
|
+
if ((normalizeGroupSegment(first) ?? first) === group && rest.length > 0) {
|
|
595
|
+
return rest.join(" ");
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
return operationId;
|
|
599
|
+
};
|
|
600
|
+
var fallbackLeafSeed = (method, pathTemplate, group) => {
|
|
601
|
+
const relevantSegments = pathSegmentsFromTemplate(pathTemplate).filter((s) => !VERSION_SEGMENT_REGEX.test(s.toLowerCase())).filter((s) => !IGNORED_PATH_SEGMENTS.has(s.toLowerCase())).filter((s) => !isPathParameterSegment(s)).map((s) => normalizeGroupSegment(s) ?? s).filter((s) => s !== group);
|
|
602
|
+
const segmentSuffix = relevantSegments.map((s) => toPascalCase(s)).join("");
|
|
603
|
+
return `${method}${segmentSuffix || "Operation"}`;
|
|
604
|
+
};
|
|
605
|
+
var deriveLeaf = (operationId, method, pathTemplate, group) => {
|
|
606
|
+
const preferred = toCamelCase(deriveLeafSeed(operationId, group));
|
|
607
|
+
if (preferred.length > 0 && preferred !== group) return preferred;
|
|
608
|
+
return toCamelCase(fallbackLeafSeed(method, pathTemplate, group));
|
|
609
|
+
};
|
|
610
|
+
var resolveCollisions = (definitions) => {
|
|
611
|
+
const staged = definitions.map((d) => ({ ...d }));
|
|
612
|
+
const applyFactory = (items, factory) => {
|
|
613
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
614
|
+
for (const item of items) {
|
|
615
|
+
const bucket = byPath.get(item.toolPath) ?? [];
|
|
616
|
+
bucket.push(item);
|
|
617
|
+
byPath.set(item.toolPath, bucket);
|
|
618
|
+
}
|
|
619
|
+
for (const bucket of byPath.values()) {
|
|
620
|
+
if (bucket.length < 2) continue;
|
|
621
|
+
for (const d of bucket) {
|
|
622
|
+
d.toolPath = factory(d);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
applyFactory(
|
|
627
|
+
staged,
|
|
628
|
+
(d) => d.versionSegment ? `${d.group}.${d.versionSegment}.${d.leaf}` : d.toolPath
|
|
629
|
+
);
|
|
630
|
+
applyFactory(staged, (d) => {
|
|
631
|
+
const prefix = d.versionSegment ? `${d.group}.${d.versionSegment}` : d.group;
|
|
632
|
+
return `${prefix}.${d.leaf}${toPascalCase(d.method)}`;
|
|
633
|
+
});
|
|
634
|
+
applyFactory(staged, (d) => {
|
|
635
|
+
const prefix = d.versionSegment ? `${d.group}.${d.versionSegment}` : d.group;
|
|
636
|
+
return `${prefix}.${d.leaf}${toPascalCase(d.method)}${d.operationHash.slice(0, 8)}`;
|
|
637
|
+
});
|
|
638
|
+
return staged.map((d) => ({
|
|
639
|
+
toolPath: d.toolPath,
|
|
640
|
+
group: d.group,
|
|
641
|
+
leaf: d.leaf,
|
|
642
|
+
operationIndex: d.operationIndex,
|
|
643
|
+
operation: d.operation
|
|
644
|
+
}));
|
|
645
|
+
};
|
|
646
|
+
var stableHash = (value) => {
|
|
647
|
+
const str = JSON.stringify(value, Object.keys(value).sort());
|
|
648
|
+
let hash = 0;
|
|
649
|
+
for (let i = 0; i < str.length; i++) {
|
|
650
|
+
hash = (hash << 5) - hash + str.charCodeAt(i) | 0;
|
|
651
|
+
}
|
|
652
|
+
return Math.abs(hash).toString(36).padStart(8, "0");
|
|
653
|
+
};
|
|
654
|
+
var compileToolDefinitions = (operations) => {
|
|
655
|
+
const raw = operations.map((op, index) => {
|
|
656
|
+
const operationId = op.operationId;
|
|
657
|
+
const explicitToolPath = Option4.getOrUndefined(op.toolPath);
|
|
658
|
+
if (explicitToolPath) {
|
|
659
|
+
const [group2 = "root", ...leafParts] = explicitToolPath.split(".").filter(Boolean);
|
|
660
|
+
const leaf2 = leafParts.join(".") || group2;
|
|
661
|
+
const versionSegment2 = deriveVersionSegment(op.pathTemplate);
|
|
662
|
+
const operationHash2 = stableHash({
|
|
663
|
+
method: op.method,
|
|
664
|
+
path: op.pathTemplate,
|
|
665
|
+
operationId
|
|
666
|
+
});
|
|
667
|
+
return {
|
|
668
|
+
toolPath: explicitToolPath,
|
|
669
|
+
group: group2,
|
|
670
|
+
leaf: leaf2,
|
|
671
|
+
versionSegment: versionSegment2,
|
|
672
|
+
method: op.method,
|
|
673
|
+
operationHash: operationHash2,
|
|
674
|
+
operationIndex: index,
|
|
675
|
+
operation: op
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
const group = normalizeGroupSegment(op.tags[0]) ?? derivePathGroup(op.pathTemplate);
|
|
679
|
+
const leaf = deriveLeaf(operationId, op.method, op.pathTemplate, group);
|
|
680
|
+
const versionSegment = deriveVersionSegment(op.pathTemplate);
|
|
681
|
+
const operationHash = stableHash({
|
|
682
|
+
method: op.method,
|
|
683
|
+
path: op.pathTemplate,
|
|
684
|
+
operationId
|
|
685
|
+
});
|
|
686
|
+
return {
|
|
687
|
+
toolPath: `${group}.${leaf}`,
|
|
688
|
+
group,
|
|
689
|
+
leaf,
|
|
690
|
+
versionSegment,
|
|
691
|
+
method: op.method,
|
|
692
|
+
operationHash,
|
|
693
|
+
operationIndex: index,
|
|
694
|
+
operation: op
|
|
695
|
+
};
|
|
696
|
+
});
|
|
697
|
+
return resolveCollisions(raw).sort((a, b) => a.toolPath.localeCompare(b.toolPath));
|
|
698
|
+
};
|
|
699
|
+
|
|
700
|
+
// src/sdk/plugin.ts
|
|
701
|
+
var STRINGIFIED_BODY_CAP = 1024;
|
|
702
|
+
var UpstreamMessageBody = Schema3.Struct({ message: Schema3.String });
|
|
703
|
+
var UpstreamErrorMessageBody = Schema3.Struct({ errorMessage: Schema3.String });
|
|
704
|
+
var UpstreamNestedErrorBody = Schema3.Struct({ error: UpstreamMessageBody });
|
|
705
|
+
var UpstreamErrorsArrayBody = Schema3.Struct({
|
|
706
|
+
errors: Schema3.Array(
|
|
707
|
+
Schema3.Struct({
|
|
708
|
+
detail: Schema3.optional(Schema3.String),
|
|
709
|
+
message: Schema3.optional(Schema3.String),
|
|
710
|
+
title: Schema3.optional(Schema3.String)
|
|
711
|
+
})
|
|
712
|
+
)
|
|
713
|
+
});
|
|
714
|
+
var UpstreamDescriptionBody = Schema3.Struct({
|
|
715
|
+
detail: Schema3.optional(Schema3.String),
|
|
716
|
+
title: Schema3.optional(Schema3.String),
|
|
717
|
+
description: Schema3.optional(Schema3.String)
|
|
718
|
+
});
|
|
719
|
+
var decodeUpstreamMessageBody = Schema3.decodeUnknownOption(UpstreamMessageBody);
|
|
720
|
+
var decodeUpstreamErrorMessageBody = Schema3.decodeUnknownOption(UpstreamErrorMessageBody);
|
|
721
|
+
var decodeUpstreamNestedErrorBody = Schema3.decodeUnknownOption(UpstreamNestedErrorBody);
|
|
722
|
+
var decodeUpstreamErrorsArrayBody = Schema3.decodeUnknownOption(UpstreamErrorsArrayBody);
|
|
723
|
+
var decodeUpstreamDescriptionBody = Schema3.decodeUnknownOption(UpstreamDescriptionBody);
|
|
724
|
+
var clampedStringify = (value) => {
|
|
725
|
+
let s;
|
|
726
|
+
try {
|
|
727
|
+
s = JSON.stringify(value);
|
|
728
|
+
} catch {
|
|
729
|
+
s = String(value);
|
|
730
|
+
}
|
|
731
|
+
return s.length > STRINGIFIED_BODY_CAP ? `${s.slice(0, STRINGIFIED_BODY_CAP)}\u2026` : s;
|
|
732
|
+
};
|
|
733
|
+
var firstNonEmpty = (...values) => values.find((value) => value !== void 0 && value.length > 0);
|
|
734
|
+
var extractUpstreamMessage = (body, status) => {
|
|
735
|
+
if (typeof body === "string") {
|
|
736
|
+
return body.length > 0 ? body : `Upstream returned HTTP ${status}`;
|
|
737
|
+
}
|
|
738
|
+
const nested = Option5.getOrUndefined(decodeUpstreamNestedErrorBody(body));
|
|
739
|
+
const messageBody = Option5.getOrUndefined(decodeUpstreamMessageBody(body));
|
|
740
|
+
const errorMessageBody = Option5.getOrUndefined(decodeUpstreamErrorMessageBody(body));
|
|
741
|
+
const errorsBody = Option5.getOrUndefined(decodeUpstreamErrorsArrayBody(body));
|
|
742
|
+
const descriptionBody = Option5.getOrUndefined(decodeUpstreamDescriptionBody(body));
|
|
743
|
+
const arrayMessage = errorsBody?.errors.map(
|
|
744
|
+
({
|
|
745
|
+
detail,
|
|
746
|
+
message: upstreamMessage,
|
|
747
|
+
title
|
|
748
|
+
}) => firstNonEmpty(detail, upstreamMessage, title)
|
|
749
|
+
).find((message2) => message2 !== void 0);
|
|
750
|
+
const message = firstNonEmpty(
|
|
751
|
+
nested?.error.message,
|
|
752
|
+
messageBody?.message,
|
|
753
|
+
errorMessageBody?.errorMessage,
|
|
754
|
+
arrayMessage,
|
|
755
|
+
descriptionBody?.detail,
|
|
756
|
+
descriptionBody?.title,
|
|
757
|
+
descriptionBody?.description
|
|
758
|
+
);
|
|
759
|
+
if (message !== void 0) return message;
|
|
760
|
+
if (body !== null && typeof body === "object") {
|
|
761
|
+
return clampedStringify(body);
|
|
762
|
+
}
|
|
763
|
+
return `Upstream returned HTTP ${status}`;
|
|
764
|
+
};
|
|
765
|
+
var PreviewSpecInputSchema = Schema3.Struct({
|
|
766
|
+
spec: Schema3.String
|
|
767
|
+
});
|
|
768
|
+
var StaticPreviewServerVariableSchema = Schema3.Struct({
|
|
769
|
+
default: Schema3.String,
|
|
770
|
+
enum: Schema3.NullOr(Schema3.Array(Schema3.String)),
|
|
771
|
+
description: Schema3.NullOr(Schema3.String)
|
|
772
|
+
});
|
|
773
|
+
var StaticPreviewServerSchema = Schema3.Struct({
|
|
774
|
+
url: Schema3.String,
|
|
775
|
+
description: Schema3.NullOr(Schema3.String),
|
|
776
|
+
variables: Schema3.NullOr(Schema3.Record(Schema3.String, StaticPreviewServerVariableSchema))
|
|
777
|
+
});
|
|
778
|
+
var StaticPreviewOAuthAuthorizationCodeFlowSchema = Schema3.Struct({
|
|
779
|
+
authorizationUrl: Schema3.String,
|
|
780
|
+
tokenUrl: Schema3.String,
|
|
781
|
+
refreshUrl: Schema3.NullOr(Schema3.String),
|
|
782
|
+
scopes: Schema3.Record(Schema3.String, Schema3.String)
|
|
783
|
+
});
|
|
784
|
+
var StaticPreviewOAuthClientCredentialsFlowSchema = Schema3.Struct({
|
|
785
|
+
tokenUrl: Schema3.String,
|
|
786
|
+
refreshUrl: Schema3.NullOr(Schema3.String),
|
|
787
|
+
scopes: Schema3.Record(Schema3.String, Schema3.String)
|
|
788
|
+
});
|
|
789
|
+
var StaticPreviewOAuthFlowsSchema = Schema3.Struct({
|
|
790
|
+
authorizationCode: Schema3.NullOr(StaticPreviewOAuthAuthorizationCodeFlowSchema),
|
|
791
|
+
clientCredentials: Schema3.NullOr(StaticPreviewOAuthClientCredentialsFlowSchema)
|
|
792
|
+
});
|
|
793
|
+
var StaticPreviewSecuritySchemeSchema = Schema3.Struct({
|
|
794
|
+
name: Schema3.String,
|
|
795
|
+
type: Schema3.Literals(["http", "apiKey", "oauth2", "openIdConnect"]),
|
|
796
|
+
scheme: Schema3.NullOr(Schema3.String),
|
|
797
|
+
bearerFormat: Schema3.NullOr(Schema3.String),
|
|
798
|
+
in: Schema3.NullOr(Schema3.Literals(["header", "query", "cookie"])),
|
|
799
|
+
headerName: Schema3.NullOr(Schema3.String),
|
|
800
|
+
description: Schema3.NullOr(Schema3.String),
|
|
801
|
+
flows: Schema3.NullOr(StaticPreviewOAuthFlowsSchema),
|
|
802
|
+
openIdConnectUrl: Schema3.NullOr(Schema3.String)
|
|
803
|
+
});
|
|
804
|
+
var StaticPreviewOAuth2PresetSchema = Schema3.Struct({
|
|
805
|
+
label: Schema3.String,
|
|
806
|
+
securitySchemeName: Schema3.String,
|
|
807
|
+
flow: Schema3.Literals(["authorizationCode", "clientCredentials"]),
|
|
808
|
+
authorizationUrl: Schema3.NullOr(Schema3.String),
|
|
809
|
+
tokenUrl: Schema3.String,
|
|
810
|
+
refreshUrl: Schema3.NullOr(Schema3.String),
|
|
811
|
+
scopes: Schema3.Record(Schema3.String, Schema3.String),
|
|
812
|
+
identityScopes: Schema3.Union([
|
|
813
|
+
Schema3.Literal("auto"),
|
|
814
|
+
Schema3.Literal(false),
|
|
815
|
+
Schema3.Array(Schema3.String)
|
|
816
|
+
])
|
|
817
|
+
});
|
|
818
|
+
var StaticPreviewSpecOutputSchema = Schema3.Struct({
|
|
819
|
+
title: Schema3.NullOr(Schema3.String),
|
|
820
|
+
version: Schema3.NullOr(Schema3.String),
|
|
821
|
+
servers: Schema3.Array(StaticPreviewServerSchema),
|
|
822
|
+
operationCount: Schema3.Number,
|
|
823
|
+
tags: Schema3.Array(Schema3.String),
|
|
824
|
+
securitySchemes: Schema3.Array(StaticPreviewSecuritySchemeSchema),
|
|
825
|
+
authStrategies: Schema3.Array(Schema3.Struct({ schemes: Schema3.Array(Schema3.String) })),
|
|
826
|
+
headerPresets: Schema3.Array(
|
|
827
|
+
Schema3.Struct({
|
|
828
|
+
label: Schema3.String,
|
|
829
|
+
headers: Schema3.Record(Schema3.String, Schema3.NullOr(Schema3.String)),
|
|
830
|
+
secretHeaders: Schema3.Array(Schema3.String)
|
|
831
|
+
})
|
|
832
|
+
),
|
|
833
|
+
oauth2Presets: Schema3.Array(StaticPreviewOAuth2PresetSchema)
|
|
834
|
+
});
|
|
835
|
+
var OpenApiSpecInputSchema = Schema3.Union([
|
|
836
|
+
Schema3.Struct({ kind: Schema3.Literal("url"), url: Schema3.String }),
|
|
837
|
+
Schema3.Struct({ kind: Schema3.Literal("blob"), value: Schema3.String }),
|
|
838
|
+
Schema3.Struct({
|
|
839
|
+
kind: Schema3.Literal("googleDiscovery"),
|
|
840
|
+
url: Schema3.String
|
|
841
|
+
}),
|
|
842
|
+
Schema3.Struct({
|
|
843
|
+
kind: Schema3.Literal("googleDiscoveryBundle"),
|
|
844
|
+
urls: Schema3.Array(Schema3.String)
|
|
845
|
+
})
|
|
846
|
+
]);
|
|
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
|
+
var AuthenticationSchema2 = Schema3.Union([
|
|
856
|
+
Schema3.Struct({
|
|
857
|
+
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"),
|
|
865
|
+
authorizationUrl: Schema3.String,
|
|
866
|
+
tokenUrl: Schema3.String,
|
|
867
|
+
scopes: Schema3.Array(Schema3.String)
|
|
868
|
+
})
|
|
869
|
+
]);
|
|
870
|
+
var AddSourceInputSchema = Schema3.Struct({
|
|
871
|
+
spec: OpenApiSpecInputSchema,
|
|
872
|
+
slug: Schema3.String,
|
|
873
|
+
description: Schema3.optional(Schema3.String),
|
|
874
|
+
baseUrl: Schema3.optional(Schema3.String),
|
|
875
|
+
headers: Schema3.optional(Schema3.Record(Schema3.String, Schema3.String)),
|
|
876
|
+
queryParams: Schema3.optional(Schema3.Record(Schema3.String, Schema3.String)),
|
|
877
|
+
authenticationTemplate: Schema3.optional(Schema3.Array(AuthenticationSchema2))
|
|
878
|
+
});
|
|
879
|
+
var AddSourceOutputSchema = Schema3.Struct({
|
|
880
|
+
slug: Schema3.String,
|
|
881
|
+
toolCount: Schema3.Number
|
|
882
|
+
});
|
|
883
|
+
var PreviewSpecInputStandardSchema = Schema3.toStandardSchemaV1(
|
|
884
|
+
Schema3.toStandardJSONSchemaV1(PreviewSpecInputSchema)
|
|
885
|
+
);
|
|
886
|
+
var PreviewSpecOutputStandardSchema = Schema3.toStandardSchemaV1(
|
|
887
|
+
Schema3.toStandardJSONSchemaV1(StaticPreviewSpecOutputSchema)
|
|
888
|
+
);
|
|
889
|
+
var AddSourceInputStandardSchema = Schema3.toStandardSchemaV1(
|
|
890
|
+
Schema3.toStandardJSONSchemaV1(AddSourceInputSchema)
|
|
891
|
+
);
|
|
892
|
+
var AddSourceOutputStandardSchema = Schema3.toStandardSchemaV1(
|
|
893
|
+
Schema3.toStandardJSONSchemaV1(AddSourceOutputSchema)
|
|
894
|
+
);
|
|
895
|
+
var openApiToolFailure = (code, message, details) => ToolResult.fail({
|
|
896
|
+
code,
|
|
897
|
+
message,
|
|
898
|
+
...details === void 0 ? {} : { details }
|
|
899
|
+
});
|
|
900
|
+
var openApiAuthToolFailure = (failure) => authToolFailure({
|
|
901
|
+
// The auth-tool-failure helper's code set is shared with v1; keep the
|
|
902
|
+
// string but reference the connection rather than v1's source/slot.
|
|
903
|
+
code: failure.code,
|
|
904
|
+
message: failure.message,
|
|
905
|
+
source: { id: failure.integration, scope: failure.owner },
|
|
906
|
+
credential: {
|
|
907
|
+
kind: failure.credentialKind,
|
|
908
|
+
...failure.credentialLabel ? { label: failure.credentialLabel } : {}
|
|
909
|
+
},
|
|
910
|
+
...failure.status !== void 0 ? { status: failure.status } : {},
|
|
911
|
+
...failure.details !== void 0 ? {
|
|
912
|
+
upstream: {
|
|
913
|
+
...failure.status !== void 0 ? { status: failure.status } : {},
|
|
914
|
+
details: failure.details
|
|
915
|
+
}
|
|
916
|
+
} : {}
|
|
917
|
+
});
|
|
918
|
+
var staticPreviewOutput = (preview) => ({
|
|
919
|
+
title: Option5.getOrNull(preview.title),
|
|
920
|
+
version: Option5.getOrNull(preview.version),
|
|
921
|
+
servers: preview.servers.map((server) => ({
|
|
922
|
+
url: server.url,
|
|
923
|
+
description: Option5.getOrNull(server.description),
|
|
924
|
+
variables: Option5.getOrNull(server.variables) ? Object.fromEntries(
|
|
925
|
+
Object.entries(Option5.getOrNull(server.variables) ?? {}).map(([name, variable]) => [
|
|
926
|
+
name,
|
|
927
|
+
{
|
|
928
|
+
default: variable.default,
|
|
929
|
+
enum: Option5.getOrNull(variable.enum),
|
|
930
|
+
description: Option5.getOrNull(variable.description)
|
|
931
|
+
}
|
|
932
|
+
])
|
|
933
|
+
) : null
|
|
934
|
+
})),
|
|
935
|
+
operationCount: preview.operationCount,
|
|
936
|
+
tags: preview.tags,
|
|
937
|
+
securitySchemes: preview.securitySchemes.map((scheme) => ({
|
|
938
|
+
name: scheme.name,
|
|
939
|
+
type: scheme.type,
|
|
940
|
+
scheme: Option5.getOrNull(scheme.scheme),
|
|
941
|
+
bearerFormat: Option5.getOrNull(scheme.bearerFormat),
|
|
942
|
+
in: Option5.getOrNull(scheme.in),
|
|
943
|
+
headerName: Option5.getOrNull(scheme.headerName),
|
|
944
|
+
description: Option5.getOrNull(scheme.description),
|
|
945
|
+
flows: Option5.isSome(scheme.flows) ? {
|
|
946
|
+
authorizationCode: Option5.isSome(scheme.flows.value.authorizationCode) ? {
|
|
947
|
+
authorizationUrl: scheme.flows.value.authorizationCode.value.authorizationUrl,
|
|
948
|
+
tokenUrl: scheme.flows.value.authorizationCode.value.tokenUrl,
|
|
949
|
+
refreshUrl: Option5.getOrNull(scheme.flows.value.authorizationCode.value.refreshUrl),
|
|
950
|
+
scopes: scheme.flows.value.authorizationCode.value.scopes
|
|
951
|
+
} : null,
|
|
952
|
+
clientCredentials: Option5.isSome(scheme.flows.value.clientCredentials) ? {
|
|
953
|
+
tokenUrl: scheme.flows.value.clientCredentials.value.tokenUrl,
|
|
954
|
+
refreshUrl: Option5.getOrNull(scheme.flows.value.clientCredentials.value.refreshUrl),
|
|
955
|
+
scopes: scheme.flows.value.clientCredentials.value.scopes
|
|
956
|
+
} : null
|
|
957
|
+
} : null,
|
|
958
|
+
openIdConnectUrl: Option5.getOrNull(scheme.openIdConnectUrl)
|
|
959
|
+
})),
|
|
960
|
+
authStrategies: preview.authStrategies,
|
|
961
|
+
headerPresets: preview.headerPresets,
|
|
962
|
+
oauth2Presets: preview.oauth2Presets.map((preset) => ({
|
|
963
|
+
label: preset.label,
|
|
964
|
+
securitySchemeName: preset.securitySchemeName,
|
|
965
|
+
flow: preset.flow,
|
|
966
|
+
authorizationUrl: Option5.getOrNull(preset.authorizationUrl),
|
|
967
|
+
tokenUrl: preset.tokenUrl,
|
|
968
|
+
refreshUrl: Option5.getOrNull(preset.refreshUrl),
|
|
969
|
+
scopes: preset.scopes,
|
|
970
|
+
identityScopes: preset.identityScopes
|
|
971
|
+
}))
|
|
972
|
+
});
|
|
973
|
+
var normalizeOpenApiRefs = (node) => {
|
|
974
|
+
if (node == null || typeof node !== "object") return node;
|
|
975
|
+
if (Array.isArray(node)) {
|
|
976
|
+
let changed2 = false;
|
|
977
|
+
const out = node.map((item) => {
|
|
978
|
+
const n = normalizeOpenApiRefs(item);
|
|
979
|
+
if (n !== item) changed2 = true;
|
|
980
|
+
return n;
|
|
981
|
+
});
|
|
982
|
+
return changed2 ? out : node;
|
|
983
|
+
}
|
|
984
|
+
const obj = node;
|
|
985
|
+
if (typeof obj.$ref === "string") {
|
|
986
|
+
const match = obj.$ref.match(/^#\/components\/schemas\/(.+)$/);
|
|
987
|
+
if (match) return { ...obj, $ref: `#/$defs/${match[1]}` };
|
|
988
|
+
return obj;
|
|
989
|
+
}
|
|
990
|
+
let changed = false;
|
|
991
|
+
const result = {};
|
|
992
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
993
|
+
const n = normalizeOpenApiRefs(v);
|
|
994
|
+
if (n !== v) changed = true;
|
|
995
|
+
result[k] = n;
|
|
996
|
+
}
|
|
997
|
+
return changed ? result : obj;
|
|
998
|
+
};
|
|
999
|
+
var toBinding = (def) => OperationBinding.make({
|
|
1000
|
+
method: def.operation.method,
|
|
1001
|
+
baseUrl: def.operation.baseUrl,
|
|
1002
|
+
pathTemplate: def.operation.pathTemplate,
|
|
1003
|
+
parameters: [...def.operation.parameters],
|
|
1004
|
+
requestBody: def.operation.requestBody
|
|
1005
|
+
});
|
|
1006
|
+
var descriptionFor = (def) => {
|
|
1007
|
+
const op = def.operation;
|
|
1008
|
+
return Option5.getOrElse(
|
|
1009
|
+
op.description,
|
|
1010
|
+
() => Option5.getOrElse(op.summary, () => `${op.method.toUpperCase()} ${op.pathTemplate}`)
|
|
1011
|
+
);
|
|
1012
|
+
};
|
|
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
|
+
var specInputToSourceUrl = (spec) => spec.kind === "url" || spec.kind === "googleDiscovery" ? spec.url : void 0;
|
|
1027
|
+
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
|
+
var describeOpenApiAuthMethods = (record) => {
|
|
1080
|
+
const config = decodeOpenApiIntegrationConfig(record.config);
|
|
1081
|
+
if (!config) return [];
|
|
1082
|
+
return (config.authenticationTemplate ?? []).map(
|
|
1083
|
+
(template) => {
|
|
1084
|
+
const slug = String(template.slug);
|
|
1085
|
+
if (template.type === "oauth") {
|
|
1086
|
+
return {
|
|
1087
|
+
id: slug,
|
|
1088
|
+
label: "OAuth2",
|
|
1089
|
+
kind: "oauth",
|
|
1090
|
+
template: slug,
|
|
1091
|
+
oauth: {
|
|
1092
|
+
authorizationUrl: template.authorizationUrl,
|
|
1093
|
+
tokenUrl: template.tokenUrl,
|
|
1094
|
+
scopes: template.scopes
|
|
1095
|
+
}
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
const placements = placementsFromAuthentication(template);
|
|
1099
|
+
return {
|
|
1100
|
+
id: slug,
|
|
1101
|
+
label: apiKeyLabel(slug, placements),
|
|
1102
|
+
kind: "apikey",
|
|
1103
|
+
template: slug,
|
|
1104
|
+
placements
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
);
|
|
1108
|
+
};
|
|
1109
|
+
var describeOpenApiIntegrationDisplay = (record) => {
|
|
1110
|
+
const config = decodeOpenApiIntegrationConfig(record.config);
|
|
1111
|
+
return { url: config?.baseUrl ?? config?.sourceUrl };
|
|
1112
|
+
};
|
|
1113
|
+
var compileSpec = (specText) => Effect3.gen(function* () {
|
|
1114
|
+
const doc = yield* parse(specText);
|
|
1115
|
+
const result = yield* extract(doc);
|
|
1116
|
+
const hoistedDefs = {};
|
|
1117
|
+
if (doc.components?.schemas) {
|
|
1118
|
+
for (const [k, v] of Object.entries(doc.components.schemas)) {
|
|
1119
|
+
hoistedDefs[k] = normalizeOpenApiRefs(v);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
return {
|
|
1123
|
+
definitions: compileToolDefinitions(result.operations),
|
|
1124
|
+
hoistedDefs,
|
|
1125
|
+
title: Option5.getOrUndefined(result.title)
|
|
1126
|
+
};
|
|
1127
|
+
});
|
|
1128
|
+
var toolDefsFromCompiled = (compiled) => compiled.definitions.map(
|
|
1129
|
+
(def) => ({
|
|
1130
|
+
name: ToolName.make(def.toolPath),
|
|
1131
|
+
description: descriptionFor(def),
|
|
1132
|
+
inputSchema: normalizeOpenApiRefs(Option5.getOrUndefined(def.operation.inputSchema)),
|
|
1133
|
+
outputSchema: openApiTransportOutputSchema(
|
|
1134
|
+
normalizeOpenApiRefs(Option5.getOrUndefined(def.operation.outputSchema))
|
|
1135
|
+
),
|
|
1136
|
+
annotations: annotationsForOperation(def.operation.method, def.operation.pathTemplate)
|
|
1137
|
+
})
|
|
1138
|
+
);
|
|
1139
|
+
var storedOperationsFromCompiled = (integration, compiled) => compiled.definitions.map((def) => ({
|
|
1140
|
+
integration,
|
|
1141
|
+
toolName: def.toolPath,
|
|
1142
|
+
binding: toBinding(def)
|
|
1143
|
+
}));
|
|
1144
|
+
var fetchGoogleDiscoveryBundleConversion = (urls, httpClientLayer) => Effect3.forEach(
|
|
1145
|
+
urls,
|
|
1146
|
+
(url) => fetchGoogleDiscoveryDocument(url).pipe(
|
|
1147
|
+
Effect3.provide(httpClientLayer),
|
|
1148
|
+
Effect3.map((documentText) => ({ discoveryUrl: url, documentText }))
|
|
1149
|
+
),
|
|
1150
|
+
{ concurrency: 4 }
|
|
1151
|
+
).pipe(Effect3.flatMap((documents) => convertGoogleDiscoveryBundleToOpenApi({ documents })));
|
|
1152
|
+
var openApiPlugin = definePlugin((options) => {
|
|
1153
|
+
const resolveSpecForInput = (spec, httpClientLayer) => Effect3.gen(function* () {
|
|
1154
|
+
if (spec.kind === "googleDiscovery") {
|
|
1155
|
+
const conversion = yield* fetchGoogleDiscoveryDocument(spec.url).pipe(
|
|
1156
|
+
Effect3.provide(httpClientLayer),
|
|
1157
|
+
Effect3.flatMap(
|
|
1158
|
+
(documentText) => convertGoogleDiscoveryToOpenApi({
|
|
1159
|
+
discoveryUrl: spec.url,
|
|
1160
|
+
documentText
|
|
1161
|
+
})
|
|
1162
|
+
)
|
|
1163
|
+
);
|
|
1164
|
+
return {
|
|
1165
|
+
specText: conversion.specText,
|
|
1166
|
+
baseUrl: conversion.baseUrl,
|
|
1167
|
+
...conversion.authenticationTemplate ? { authenticationTemplate: conversion.authenticationTemplate } : {}
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
if (spec.kind === "googleDiscoveryBundle") {
|
|
1171
|
+
const conversion = yield* fetchGoogleDiscoveryBundleConversion(spec.urls, httpClientLayer);
|
|
1172
|
+
return {
|
|
1173
|
+
specText: conversion.specText,
|
|
1174
|
+
baseUrl: conversion.baseUrl,
|
|
1175
|
+
...conversion.authenticationTemplate ? { authenticationTemplate: conversion.authenticationTemplate } : {}
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
if (spec.kind === "url") {
|
|
1179
|
+
const specText = yield* resolveSpecText(spec.url).pipe(Effect3.provide(httpClientLayer));
|
|
1180
|
+
return { specText };
|
|
1181
|
+
}
|
|
1182
|
+
return { specText: spec.value };
|
|
1183
|
+
});
|
|
1184
|
+
return {
|
|
1185
|
+
id: "openapi",
|
|
1186
|
+
packageName: "@executor-js/plugin-openapi",
|
|
1187
|
+
integrationPresets: openApiPresets.map((preset) => ({
|
|
1188
|
+
id: preset.id,
|
|
1189
|
+
name: preset.name,
|
|
1190
|
+
summary: preset.summary,
|
|
1191
|
+
...preset.url ? { url: preset.url } : {},
|
|
1192
|
+
...preset.icon ? { icon: preset.icon } : {},
|
|
1193
|
+
...preset.featured ? { featured: preset.featured } : {}
|
|
1194
|
+
})),
|
|
1195
|
+
storage: (deps) => makeDefaultOpenapiStore(deps),
|
|
1196
|
+
extension: (ctx) => {
|
|
1197
|
+
const httpClientLayer = options?.httpClientLayer ?? ctx.httpClientLayer;
|
|
1198
|
+
const addSpec = (config) => Effect3.gen(function* () {
|
|
1199
|
+
const resolved = yield* resolveSpecForInput(config.spec, httpClientLayer);
|
|
1200
|
+
const compiled = yield* compileSpec(resolved.specText);
|
|
1201
|
+
const slug = IntegrationSlug.make(config.slug);
|
|
1202
|
+
const existing = yield* ctx.core.integrations.get(slug);
|
|
1203
|
+
if (existing) {
|
|
1204
|
+
return yield* new IntegrationAlreadyExistsError({ slug });
|
|
1205
|
+
}
|
|
1206
|
+
const integrationConfig = {
|
|
1207
|
+
spec: resolved.specText,
|
|
1208
|
+
...specInputToSourceUrl(config.spec) !== void 0 ? { sourceUrl: specInputToSourceUrl(config.spec) } : {},
|
|
1209
|
+
...specInputToGoogleBundle(config.spec) !== void 0 ? { googleDiscoveryUrls: specInputToGoogleBundle(config.spec) } : {},
|
|
1210
|
+
...config.baseUrl ?? resolved.baseUrl ? { baseUrl: config.baseUrl ?? resolved.baseUrl } : {},
|
|
1211
|
+
...config.headers ? { headers: config.headers } : {},
|
|
1212
|
+
...config.queryParams ? { queryParams: config.queryParams } : {},
|
|
1213
|
+
// Prefer the caller's explicit template; otherwise adopt the one the
|
|
1214
|
+
// 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 } : {}
|
|
1217
|
+
};
|
|
1218
|
+
yield* ctx.transaction(
|
|
1219
|
+
Effect3.gen(function* () {
|
|
1220
|
+
yield* ctx.core.integrations.register({
|
|
1221
|
+
slug,
|
|
1222
|
+
description: config.description ?? compiled.title ?? config.slug,
|
|
1223
|
+
config: integrationConfig,
|
|
1224
|
+
canRemove: true,
|
|
1225
|
+
canRefresh: specInputToSourceUrl(config.spec) != null || specInputToGoogleBundle(config.spec) != null
|
|
1226
|
+
});
|
|
1227
|
+
yield* ctx.storage.putOperations(
|
|
1228
|
+
config.slug,
|
|
1229
|
+
storedOperationsFromCompiled(config.slug, compiled)
|
|
1230
|
+
);
|
|
1231
|
+
})
|
|
1232
|
+
);
|
|
1233
|
+
return { slug, toolCount: compiled.definitions.length };
|
|
1234
|
+
});
|
|
1235
|
+
return {
|
|
1236
|
+
previewSpec: (input) => Effect3.gen(function* () {
|
|
1237
|
+
const previewInput = typeof input === "string" ? { spec: input } : input;
|
|
1238
|
+
const specText = isGoogleDiscoveryUrl(previewInput.spec) ? yield* fetchGoogleDiscoveryDocument(previewInput.spec).pipe(
|
|
1239
|
+
Effect3.provide(httpClientLayer),
|
|
1240
|
+
Effect3.flatMap(
|
|
1241
|
+
(documentText) => convertGoogleDiscoveryToOpenApi({
|
|
1242
|
+
discoveryUrl: previewInput.spec,
|
|
1243
|
+
documentText
|
|
1244
|
+
})
|
|
1245
|
+
),
|
|
1246
|
+
Effect3.map((conversion) => conversion.specText)
|
|
1247
|
+
) : yield* resolveSpecText(previewInput.spec).pipe(Effect3.provide(httpClientLayer));
|
|
1248
|
+
return yield* previewSpec(specText).pipe(Effect3.provide(httpClientLayer));
|
|
1249
|
+
}),
|
|
1250
|
+
addSpec,
|
|
1251
|
+
removeSpec: (slug) => ctx.transaction(
|
|
1252
|
+
Effect3.gen(function* () {
|
|
1253
|
+
yield* ctx.storage.removeOperations(slug);
|
|
1254
|
+
yield* ctx.core.integrations.remove(IntegrationSlug.make(slug)).pipe(Effect3.catchTag("IntegrationRemovalNotAllowedError", () => Effect3.void));
|
|
1255
|
+
})
|
|
1256
|
+
),
|
|
1257
|
+
getIntegration: (slug) => ctx.core.integrations.get(IntegrationSlug.make(slug)).pipe(
|
|
1258
|
+
Effect3.map(
|
|
1259
|
+
(record) => record ? {
|
|
1260
|
+
slug: record.slug,
|
|
1261
|
+
description: record.description,
|
|
1262
|
+
kind: record.kind,
|
|
1263
|
+
canRemove: record.canRemove,
|
|
1264
|
+
canRefresh: record.canRefresh
|
|
1265
|
+
} : null
|
|
1266
|
+
)
|
|
1267
|
+
),
|
|
1268
|
+
getConfig: (slug) => ctx.core.integrations.get(IntegrationSlug.make(slug)).pipe(
|
|
1269
|
+
Effect3.map(
|
|
1270
|
+
(record) => record ? decodeOpenApiIntegrationConfig(record.config) : null
|
|
1271
|
+
)
|
|
1272
|
+
),
|
|
1273
|
+
configure: (slug, input) => ctx.transaction(
|
|
1274
|
+
Effect3.gen(function* () {
|
|
1275
|
+
const record = yield* ctx.core.integrations.get(IntegrationSlug.make(slug));
|
|
1276
|
+
if (!record) return [];
|
|
1277
|
+
const current = decodeOpenApiIntegrationConfig(record.config);
|
|
1278
|
+
if (!current) return [];
|
|
1279
|
+
const merged = input.mode === "replace" ? input.authenticationTemplate : mergeAuthenticationTemplate(
|
|
1280
|
+
current.authenticationTemplate ?? [],
|
|
1281
|
+
input.authenticationTemplate
|
|
1282
|
+
);
|
|
1283
|
+
const next = {
|
|
1284
|
+
...current,
|
|
1285
|
+
authenticationTemplate: merged
|
|
1286
|
+
};
|
|
1287
|
+
yield* ctx.core.integrations.update(IntegrationSlug.make(slug), {
|
|
1288
|
+
config: next
|
|
1289
|
+
});
|
|
1290
|
+
return merged;
|
|
1291
|
+
})
|
|
1292
|
+
)
|
|
1293
|
+
};
|
|
1294
|
+
},
|
|
1295
|
+
staticSources: (self) => [
|
|
1296
|
+
{
|
|
1297
|
+
id: "openapi",
|
|
1298
|
+
kind: "executor",
|
|
1299
|
+
name: "OpenAPI",
|
|
1300
|
+
tools: [
|
|
1301
|
+
tool({
|
|
1302
|
+
name: "previewSpec",
|
|
1303
|
+
description: "Preview an OpenAPI document before adding it as an integration. Call this first when the user provides a spec URL/blob so you can inspect servers, auth schemes, operation count, and tags before `addSpec`. Do not collect API keys or OAuth client secrets in chat; use the connections tools for those values.",
|
|
1304
|
+
inputSchema: PreviewSpecInputStandardSchema,
|
|
1305
|
+
outputSchema: PreviewSpecOutputStandardSchema,
|
|
1306
|
+
execute: (input) => self.previewSpec(input).pipe(
|
|
1307
|
+
Effect3.map((preview) => ToolResult.ok(staticPreviewOutput(preview))),
|
|
1308
|
+
Effect3.catchTags({
|
|
1309
|
+
OpenApiParseError: ({ message }) => Effect3.succeed(openApiToolFailure("openapi_parse_failed", message)),
|
|
1310
|
+
OpenApiExtractionError: ({ message }) => Effect3.succeed(openApiToolFailure("openapi_extraction_failed", message)),
|
|
1311
|
+
OpenApiOAuthError: ({ message }) => Effect3.succeed(openApiToolFailure("openapi_oauth_failed", message))
|
|
1312
|
+
})
|
|
1313
|
+
)
|
|
1314
|
+
}),
|
|
1315
|
+
tool({
|
|
1316
|
+
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`.",
|
|
1318
|
+
annotations: {
|
|
1319
|
+
requiresApproval: true,
|
|
1320
|
+
approvalDescription: "Add an OpenAPI integration"
|
|
1321
|
+
},
|
|
1322
|
+
inputSchema: AddSourceInputStandardSchema,
|
|
1323
|
+
outputSchema: AddSourceOutputStandardSchema,
|
|
1324
|
+
execute: (input) => self.addSpec({
|
|
1325
|
+
spec: input.spec,
|
|
1326
|
+
slug: input.slug,
|
|
1327
|
+
description: input.description,
|
|
1328
|
+
baseUrl: input.baseUrl,
|
|
1329
|
+
headers: input.headers,
|
|
1330
|
+
queryParams: input.queryParams,
|
|
1331
|
+
authenticationTemplate: input.authenticationTemplate
|
|
1332
|
+
}).pipe(
|
|
1333
|
+
Effect3.map(
|
|
1334
|
+
(result) => ToolResult.ok({
|
|
1335
|
+
slug: String(result.slug),
|
|
1336
|
+
toolCount: result.toolCount
|
|
1337
|
+
})
|
|
1338
|
+
),
|
|
1339
|
+
Effect3.catchTags({
|
|
1340
|
+
OpenApiParseError: ({ message }) => Effect3.succeed(openApiToolFailure("openapi_parse_failed", message)),
|
|
1341
|
+
OpenApiExtractionError: ({ message }) => Effect3.succeed(openApiToolFailure("openapi_extraction_failed", message)),
|
|
1342
|
+
OpenApiOAuthError: ({ message }) => Effect3.succeed(openApiToolFailure("openapi_oauth_failed", message)),
|
|
1343
|
+
IntegrationAlreadyExistsError: ({ slug }) => Effect3.succeed(
|
|
1344
|
+
openApiToolFailure(
|
|
1345
|
+
"integration_already_exists",
|
|
1346
|
+
`Integration ${slug} already exists; update it instead of re-adding.`
|
|
1347
|
+
)
|
|
1348
|
+
)
|
|
1349
|
+
})
|
|
1350
|
+
)
|
|
1351
|
+
})
|
|
1352
|
+
]
|
|
1353
|
+
}
|
|
1354
|
+
],
|
|
1355
|
+
describeAuthMethods: describeOpenApiAuthMethods,
|
|
1356
|
+
describeIntegrationDisplay: describeOpenApiIntegrationDisplay,
|
|
1357
|
+
// Produce one tool per spec operation. Spec-derived, identical for every
|
|
1358
|
+
// connection on the integration — so `getValue` is never called here. The
|
|
1359
|
+
// operation bindings invokeTool needs are persisted at addSpec time; this
|
|
1360
|
+
// hook only shapes the per-connection ToolDefs from the catalog config.
|
|
1361
|
+
resolveTools: ({
|
|
1362
|
+
config
|
|
1363
|
+
}) => Effect3.gen(function* () {
|
|
1364
|
+
const openApiConfig = decodeOpenApiIntegrationConfig(config);
|
|
1365
|
+
if (!openApiConfig) return { tools: [], definitions: {} };
|
|
1366
|
+
const compiled = yield* compileSpec(openApiConfig.spec).pipe(
|
|
1367
|
+
Effect3.catch(() => Effect3.succeed(null))
|
|
1368
|
+
);
|
|
1369
|
+
if (!compiled) return { tools: [], definitions: {} };
|
|
1370
|
+
return {
|
|
1371
|
+
tools: toolDefsFromCompiled(compiled),
|
|
1372
|
+
definitions: compiled.hoistedDefs
|
|
1373
|
+
};
|
|
1374
|
+
}),
|
|
1375
|
+
invokeTool: ({
|
|
1376
|
+
ctx: invokeCtx,
|
|
1377
|
+
toolRow,
|
|
1378
|
+
credential,
|
|
1379
|
+
args
|
|
1380
|
+
}) => Effect3.gen(function* () {
|
|
1381
|
+
const httpClientLayer = options?.httpClientLayer ?? invokeCtx.httpClientLayer;
|
|
1382
|
+
const integration = toolRow.integration;
|
|
1383
|
+
const config = decodeOpenApiIntegrationConfig(credential.config);
|
|
1384
|
+
let binding = (yield* invokeCtx.storage.getOperation(integration, toolRow.name))?.binding;
|
|
1385
|
+
if (!binding && config) {
|
|
1386
|
+
const compiled = yield* compileSpec(config.spec).pipe(
|
|
1387
|
+
Effect3.catch(() => Effect3.succeed(null))
|
|
1388
|
+
);
|
|
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.type === "oauth" ? "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.type === "oauth" ? "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,
|
|
1430
|
+
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({
|
|
1455
|
+
status: result.status,
|
|
1456
|
+
headers: result.headers,
|
|
1457
|
+
data: result.data
|
|
1458
|
+
});
|
|
1459
|
+
}),
|
|
1460
|
+
resolveAnnotations: ({
|
|
1461
|
+
ctx: annotationsCtx,
|
|
1462
|
+
integration,
|
|
1463
|
+
toolRows
|
|
1464
|
+
}) => Effect3.gen(function* () {
|
|
1465
|
+
const ops = yield* annotationsCtx.storage.listOperations(String(integration));
|
|
1466
|
+
const byName = /* @__PURE__ */ new Map();
|
|
1467
|
+
for (const op of ops) byName.set(op.toolName, op.binding);
|
|
1468
|
+
const out = {};
|
|
1469
|
+
for (const row of toolRows) {
|
|
1470
|
+
const binding = byName.get(row.name);
|
|
1471
|
+
if (binding) {
|
|
1472
|
+
out[row.name] = annotationsForOperation(binding.method, binding.pathTemplate);
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
return out;
|
|
1476
|
+
}),
|
|
1477
|
+
removeConnection: () => Effect3.void,
|
|
1478
|
+
detect: ({
|
|
1479
|
+
ctx: detectCtx,
|
|
1480
|
+
url
|
|
1481
|
+
}) => Effect3.gen(function* () {
|
|
1482
|
+
const httpClientLayer = options?.httpClientLayer ?? detectCtx.httpClientLayer;
|
|
1483
|
+
const trimmed = url.trim();
|
|
1484
|
+
if (!trimmed) return null;
|
|
1485
|
+
const parsed = yield* Effect3.try({
|
|
1486
|
+
try: () => new URL(trimmed),
|
|
1487
|
+
catch: (error) => error
|
|
1488
|
+
}).pipe(Effect3.option);
|
|
1489
|
+
if (Option5.isNone(parsed)) return null;
|
|
1490
|
+
if (isGoogleDiscoveryUrl(trimmed)) {
|
|
1491
|
+
const conversion = yield* fetchGoogleDiscoveryDocument(trimmed).pipe(
|
|
1492
|
+
Effect3.provide(httpClientLayer),
|
|
1493
|
+
Effect3.flatMap(
|
|
1494
|
+
(documentText) => convertGoogleDiscoveryToOpenApi({
|
|
1495
|
+
discoveryUrl: trimmed,
|
|
1496
|
+
documentText
|
|
1497
|
+
})
|
|
1498
|
+
),
|
|
1499
|
+
Effect3.catch(() => Effect3.succeed(null))
|
|
1500
|
+
);
|
|
1501
|
+
if (conversion) {
|
|
1502
|
+
return IntegrationDetectionResult.make({
|
|
1503
|
+
kind: "openapi",
|
|
1504
|
+
confidence: "high",
|
|
1505
|
+
endpoint: trimmed,
|
|
1506
|
+
name: conversion.title,
|
|
1507
|
+
slug: conversion.title.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || `google_${conversion.service}`
|
|
1508
|
+
});
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
const specText = yield* resolveSpecText(trimmed).pipe(
|
|
1512
|
+
Effect3.provide(httpClientLayer),
|
|
1513
|
+
Effect3.catch(() => Effect3.succeed(null))
|
|
1514
|
+
);
|
|
1515
|
+
if (specText === null) return null;
|
|
1516
|
+
const doc = yield* parse(specText).pipe(Effect3.catch(() => Effect3.succeed(null)));
|
|
1517
|
+
if (!doc) return null;
|
|
1518
|
+
const result = yield* extract(doc).pipe(Effect3.catch(() => Effect3.succeed(null)));
|
|
1519
|
+
if (!result) return null;
|
|
1520
|
+
const slug = Option5.getOrElse(result.title, () => "api").toLowerCase().replace(/[^a-z0-9]+/g, "_");
|
|
1521
|
+
const name = Option5.getOrElse(result.title, () => slug);
|
|
1522
|
+
return IntegrationDetectionResult.make({
|
|
1523
|
+
kind: "openapi",
|
|
1524
|
+
confidence: "high",
|
|
1525
|
+
endpoint: trimmed,
|
|
1526
|
+
name,
|
|
1527
|
+
slug
|
|
1528
|
+
});
|
|
1529
|
+
})
|
|
1530
|
+
};
|
|
1531
|
+
});
|
|
1532
|
+
|
|
1533
|
+
export {
|
|
1534
|
+
AuthenticationSchema,
|
|
1535
|
+
OpenApiIntegrationConfigSchema,
|
|
1536
|
+
decodeOpenApiIntegrationConfig,
|
|
1537
|
+
renderAuthTemplate,
|
|
1538
|
+
invoke,
|
|
1539
|
+
invokeWithLayer,
|
|
1540
|
+
annotationsForOperation,
|
|
1541
|
+
makeDefaultOpenapiStore,
|
|
1542
|
+
openApiPlugin
|
|
1543
|
+
};
|
|
1544
|
+
//# sourceMappingURL=chunk-V7VCHYOY.js.map
|