@ha-bits/cortex-core 0.1.0-next.69 → 1.1.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/index.cjs +240 -1729
- package/index.cjs.map +4 -4
- package/index.d.ts +17 -0
- package/index.js +245 -1722
- package/index.js.map +4 -4
- package/package.json +2 -10
package/index.js
CHANGED
|
@@ -7,27 +7,13 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
7
7
|
|
|
8
8
|
// packages/cortex/core/src/WorkflowExecutor.ts
|
|
9
9
|
import { v4 as uuidv4 } from "uuid";
|
|
10
|
-
import { Cron } from "croner";
|
|
11
10
|
|
|
12
11
|
// packages/bindings/src/runtime.ts
|
|
13
12
|
function isTauri() {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
function getTauri() {
|
|
18
|
-
if (!isTauri()) return null;
|
|
19
|
-
return globalThis.__TAURI__;
|
|
20
|
-
}
|
|
21
|
-
function getTauriPlugin(pluginName) {
|
|
22
|
-
const tauri = getTauri();
|
|
23
|
-
if (!tauri) {
|
|
24
|
-
throw new Error(`Tauri is not available. Make sure withGlobalTauri is enabled in tauri.conf.json.`);
|
|
25
|
-
}
|
|
26
|
-
const plugin = tauri[pluginName];
|
|
27
|
-
if (!plugin) {
|
|
28
|
-
throw new Error(`Tauri plugin '${pluginName}' is not available. Make sure the plugin is installed and enabled.`);
|
|
13
|
+
if (typeof window === "undefined") {
|
|
14
|
+
return false;
|
|
29
15
|
}
|
|
30
|
-
return
|
|
16
|
+
return !!(window.__TAURI__ || window.__TAURI_INTERNALS__);
|
|
31
17
|
}
|
|
32
18
|
function isNode() {
|
|
33
19
|
return typeof process !== "undefined" && process.versions != null && process.versions.node != null;
|
|
@@ -231,12 +217,16 @@ function dirname(p) {
|
|
|
231
217
|
// packages/bindings/src/shell.ts
|
|
232
218
|
var nodeChildProcess = null;
|
|
233
219
|
var nodeUtil = null;
|
|
220
|
+
var tauriShell = null;
|
|
234
221
|
if (isNode()) {
|
|
235
222
|
nodeChildProcess = __require("child_process");
|
|
236
223
|
nodeUtil = __require("util");
|
|
237
224
|
}
|
|
238
|
-
function getTauriShell() {
|
|
239
|
-
|
|
225
|
+
async function getTauriShell() {
|
|
226
|
+
if (!tauriShell) {
|
|
227
|
+
tauriShell = await import("@tauri-apps/plugin-shell");
|
|
228
|
+
}
|
|
229
|
+
return tauriShell;
|
|
240
230
|
}
|
|
241
231
|
function filterEnv(env) {
|
|
242
232
|
if (!env) return void 0;
|
|
@@ -250,7 +240,7 @@ function filterEnv(env) {
|
|
|
250
240
|
}
|
|
251
241
|
async function exec(command, options = {}) {
|
|
252
242
|
if (isTauri()) {
|
|
253
|
-
const shell = getTauriShell();
|
|
243
|
+
const shell = await getTauriShell();
|
|
254
244
|
const shellProgram = process.platform === "win32" ? "cmd" : "sh";
|
|
255
245
|
const shellArgs = process.platform === "win32" ? ["/c", command] : ["-c", command];
|
|
256
246
|
const cmd = shell.Command.create(shellProgram, shellArgs, {
|
|
@@ -291,6 +281,11 @@ async function exec(command, options = {}) {
|
|
|
291
281
|
throw new Error("exec is not supported in this environment");
|
|
292
282
|
}
|
|
293
283
|
|
|
284
|
+
// packages/core/src/types.ts
|
|
285
|
+
function isFrontendWorkflow(workflow) {
|
|
286
|
+
return "nodes" in workflow && "edges" in workflow && Array.isArray(workflow.nodes) && Array.isArray(workflow.edges);
|
|
287
|
+
}
|
|
288
|
+
|
|
294
289
|
// packages/core/src/logger/types.ts
|
|
295
290
|
var LOG_LEVEL_PRIORITY = {
|
|
296
291
|
trace: 0,
|
|
@@ -608,9 +603,9 @@ var ConfigResolver = class {
|
|
|
608
603
|
}
|
|
609
604
|
const formatEnv = env[LOG_ENV_VARS.FORMAT];
|
|
610
605
|
if (formatEnv && ["text", "json"].includes(formatEnv.toLowerCase())) {
|
|
611
|
-
const
|
|
606
|
+
const format = formatEnv.toLowerCase();
|
|
612
607
|
result.outputs = result.outputs.map(
|
|
613
|
-
(o) => o.type === "console" || o.type === "file" ? { ...o, format
|
|
608
|
+
(o) => o.type === "console" || o.type === "file" ? { ...o, format } : o
|
|
614
609
|
);
|
|
615
610
|
}
|
|
616
611
|
for (const [key, value] of Object.entries(env)) {
|
|
@@ -1172,8 +1167,8 @@ var LoggerFactory = class {
|
|
|
1172
1167
|
/**
|
|
1173
1168
|
* Create a formatter based on format type
|
|
1174
1169
|
*/
|
|
1175
|
-
static createFormatter(
|
|
1176
|
-
return
|
|
1170
|
+
static createFormatter(format) {
|
|
1171
|
+
return format === "json" ? new JsonFormatter() : new TextFormatter();
|
|
1177
1172
|
}
|
|
1178
1173
|
};
|
|
1179
1174
|
|
|
@@ -2201,17 +2196,6 @@ function getModuleMainFile(moduleDefinition) {
|
|
|
2201
2196
|
|
|
2202
2197
|
// packages/cortex/core/src/utils/moduleLoader.ts
|
|
2203
2198
|
var logger2 = LoggerFactory.getRoot();
|
|
2204
|
-
var bundledModulesRegistry = /* @__PURE__ */ new Map();
|
|
2205
|
-
function registerBundledModule(moduleName, moduleExports) {
|
|
2206
|
-
bundledModulesRegistry.set(moduleName, moduleExports);
|
|
2207
|
-
console.log(`\u{1F4E6} Registered bundled module: ${moduleName}`);
|
|
2208
|
-
}
|
|
2209
|
-
function getBundledModule(moduleName) {
|
|
2210
|
-
return bundledModulesRegistry.get(moduleName);
|
|
2211
|
-
}
|
|
2212
|
-
function isBundledModule(moduleName) {
|
|
2213
|
-
return bundledModulesRegistry.has(moduleName);
|
|
2214
|
-
}
|
|
2215
2199
|
function getModuleName(moduleDefinition) {
|
|
2216
2200
|
if (moduleDefinition.source === "github") {
|
|
2217
2201
|
const url = moduleDefinition.repository;
|
|
@@ -2230,20 +2214,6 @@ function getModuleName(moduleDefinition) {
|
|
|
2230
2214
|
var MODULES_CONFIG_PATH = join(process.cwd(), "modules.json");
|
|
2231
2215
|
async function ensureModuleInstalled(moduleDefinition) {
|
|
2232
2216
|
const moduleName = getModuleName(moduleDefinition);
|
|
2233
|
-
if (isBundledModule(moduleDefinition.repository)) {
|
|
2234
|
-
console.log(`\u2713 Module ${moduleName} is pre-bundled, skipping installation
|
|
2235
|
-
`);
|
|
2236
|
-
return moduleDefinition.repository;
|
|
2237
|
-
}
|
|
2238
|
-
if (typeof __require !== "undefined") {
|
|
2239
|
-
try {
|
|
2240
|
-
__require(moduleDefinition.repository);
|
|
2241
|
-
console.log(`\u2713 Module ${moduleName} already available via require
|
|
2242
|
-
`);
|
|
2243
|
-
return moduleDefinition.repository;
|
|
2244
|
-
} catch {
|
|
2245
|
-
}
|
|
2246
|
-
}
|
|
2247
2217
|
console.log(`
|
|
2248
2218
|
\u{1F50D} ensureModuleInstalled called:`);
|
|
2249
2219
|
console.log(` Module name: ${moduleName}`);
|
|
@@ -2265,157 +2235,75 @@ async function ensureModuleInstalled(moduleDefinition) {
|
|
|
2265
2235
|
// packages/cortex/core/src/n8n/executionContext.ts
|
|
2266
2236
|
import * as path3 from "path";
|
|
2267
2237
|
|
|
2268
|
-
// packages/
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
function
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
if (Array.isArray(headers)) {
|
|
2282
|
-
const obj = {};
|
|
2283
|
-
for (const [key, value] of headers) {
|
|
2284
|
-
obj[key] = value;
|
|
2285
|
-
}
|
|
2286
|
-
return obj;
|
|
2287
|
-
}
|
|
2288
|
-
return headers;
|
|
2289
|
-
}
|
|
2290
|
-
function wrapTauriResponse(response) {
|
|
2291
|
-
return {
|
|
2292
|
-
ok: response.ok,
|
|
2293
|
-
status: response.status,
|
|
2294
|
-
statusText: response.statusText || "",
|
|
2295
|
-
headers: response.headers,
|
|
2296
|
-
url: response.url,
|
|
2297
|
-
redirected: response.redirected || false,
|
|
2298
|
-
text: () => response.text(),
|
|
2299
|
-
json: () => response.json(),
|
|
2300
|
-
arrayBuffer: () => response.arrayBuffer(),
|
|
2301
|
-
blob: () => response.blob()
|
|
2302
|
-
};
|
|
2303
|
-
}
|
|
2304
|
-
function wrapNativeResponse(response) {
|
|
2305
|
-
return {
|
|
2306
|
-
ok: response.ok,
|
|
2307
|
-
status: response.status,
|
|
2308
|
-
statusText: response.statusText,
|
|
2309
|
-
headers: response.headers,
|
|
2310
|
-
url: response.url,
|
|
2311
|
-
redirected: response.redirected,
|
|
2312
|
-
text: () => response.text(),
|
|
2313
|
-
json: () => response.json(),
|
|
2314
|
-
arrayBuffer: () => response.arrayBuffer(),
|
|
2315
|
-
blob: () => response.blob()
|
|
2238
|
+
// packages/cortex/core/src/n8n/httpRequest.ts
|
|
2239
|
+
import axios from "axios";
|
|
2240
|
+
import FormData from "form-data";
|
|
2241
|
+
var logger3 = LoggerFactory.getRoot();
|
|
2242
|
+
function convertN8nRequestToAxios(requestOptions) {
|
|
2243
|
+
const { headers, method, timeout, auth, url, body, qs } = requestOptions;
|
|
2244
|
+
const axiosConfig = {
|
|
2245
|
+
headers: headers ?? {},
|
|
2246
|
+
method: method || "GET",
|
|
2247
|
+
timeout: timeout || 3e5,
|
|
2248
|
+
url,
|
|
2249
|
+
maxBodyLength: Infinity,
|
|
2250
|
+
maxContentLength: Infinity
|
|
2316
2251
|
};
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
if (isTauri()) {
|
|
2320
|
-
return fetchTauri(url, options);
|
|
2321
|
-
}
|
|
2322
|
-
return fetchNative(url, options);
|
|
2323
|
-
}
|
|
2324
|
-
async function fetchNative(url, options = {}) {
|
|
2325
|
-
const { timeout, maxRedirections, body, ...nativeOptions } = options;
|
|
2326
|
-
let processedBody = void 0;
|
|
2327
|
-
if (body !== null && body !== void 0) {
|
|
2328
|
-
if (typeof body === "object" && !(body instanceof ArrayBuffer) && !(body instanceof Blob) && !(body instanceof FormData) && !(body instanceof URLSearchParams) && !(body instanceof ReadableStream) && !ArrayBuffer.isView(body)) {
|
|
2329
|
-
processedBody = JSON.stringify(body);
|
|
2330
|
-
} else {
|
|
2331
|
-
processedBody = body;
|
|
2332
|
-
}
|
|
2252
|
+
if (qs) {
|
|
2253
|
+
axiosConfig.params = qs;
|
|
2333
2254
|
}
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2255
|
+
if (auth) {
|
|
2256
|
+
axiosConfig.auth = {
|
|
2257
|
+
username: auth.username || "",
|
|
2258
|
+
password: auth.password || ""
|
|
2259
|
+
};
|
|
2339
2260
|
}
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
...nativeOptions,
|
|
2343
|
-
body: processedBody,
|
|
2344
|
-
signal: controller?.signal ?? options.signal
|
|
2345
|
-
});
|
|
2346
|
-
return wrapNativeResponse(response);
|
|
2347
|
-
} finally {
|
|
2348
|
-
if (timeoutId) {
|
|
2349
|
-
clearTimeout(timeoutId);
|
|
2350
|
-
}
|
|
2261
|
+
if (requestOptions.baseURL) {
|
|
2262
|
+
axiosConfig.baseURL = requestOptions.baseURL;
|
|
2351
2263
|
}
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
const http = getTauriHttp();
|
|
2355
|
-
const { timeout, maxRedirections, body, headers, ...restOptions } = options;
|
|
2356
|
-
let processedBody = void 0;
|
|
2357
|
-
if (body !== null && body !== void 0) {
|
|
2358
|
-
if (typeof body === "object" && !(body instanceof ArrayBuffer) && !(body instanceof Blob) && !(body instanceof FormData) && !(body instanceof URLSearchParams) && !(body instanceof ReadableStream) && !ArrayBuffer.isView(body)) {
|
|
2359
|
-
processedBody = JSON.stringify(body);
|
|
2360
|
-
} else {
|
|
2361
|
-
processedBody = body;
|
|
2362
|
-
}
|
|
2264
|
+
if (requestOptions.disableFollowRedirect) {
|
|
2265
|
+
axiosConfig.maxRedirects = 0;
|
|
2363
2266
|
}
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
body: processedBody,
|
|
2367
|
-
headers: headersToObject(headers)
|
|
2368
|
-
};
|
|
2369
|
-
if (timeout !== void 0) {
|
|
2370
|
-
tauriOptions.timeout = timeout;
|
|
2267
|
+
if (requestOptions.encoding) {
|
|
2268
|
+
axiosConfig.responseType = requestOptions.encoding;
|
|
2371
2269
|
}
|
|
2372
|
-
if (
|
|
2373
|
-
tauriOptions.maxRedirections = maxRedirections;
|
|
2270
|
+
if (requestOptions.skipSslCertificateValidation) {
|
|
2374
2271
|
}
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
}
|
|
2272
|
+
if (body) {
|
|
2273
|
+
if (body instanceof FormData) {
|
|
2274
|
+
axiosConfig.data = body;
|
|
2275
|
+
axiosConfig.headers = {
|
|
2276
|
+
...axiosConfig.headers,
|
|
2277
|
+
...body.getHeaders()
|
|
2278
|
+
};
|
|
2279
|
+
} else if (body instanceof URLSearchParams) {
|
|
2280
|
+
axiosConfig.headers = {
|
|
2281
|
+
...axiosConfig.headers,
|
|
2282
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
2283
|
+
};
|
|
2284
|
+
axiosConfig.data = body;
|
|
2285
|
+
} else if (typeof body === "object" && Object.keys(body).length > 0) {
|
|
2286
|
+
axiosConfig.data = body;
|
|
2287
|
+
} else if (typeof body === "string") {
|
|
2288
|
+
axiosConfig.data = body;
|
|
2393
2289
|
}
|
|
2394
|
-
fullUrl += (fullUrl.includes("?") ? "&" : "?") + params.toString();
|
|
2395
2290
|
}
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
const formHeaders = body.getHeaders();
|
|
2402
|
-
Object.assign(headers, formHeaders);
|
|
2403
|
-
return body;
|
|
2404
|
-
}
|
|
2405
|
-
if (body instanceof URLSearchParams) {
|
|
2406
|
-
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
|
2407
|
-
return body.toString();
|
|
2291
|
+
if (requestOptions.json) {
|
|
2292
|
+
axiosConfig.headers = {
|
|
2293
|
+
...axiosConfig.headers,
|
|
2294
|
+
Accept: "application/json"
|
|
2295
|
+
};
|
|
2408
2296
|
}
|
|
2409
|
-
if (
|
|
2410
|
-
|
|
2411
|
-
headers
|
|
2412
|
-
|
|
2413
|
-
|
|
2297
|
+
if (!axiosConfig.headers?.["User-Agent"]) {
|
|
2298
|
+
axiosConfig.headers = {
|
|
2299
|
+
...axiosConfig.headers,
|
|
2300
|
+
"User-Agent": "n8n-habits-executor"
|
|
2301
|
+
};
|
|
2414
2302
|
}
|
|
2415
|
-
if (
|
|
2416
|
-
|
|
2303
|
+
if (requestOptions.ignoreHttpStatusErrors) {
|
|
2304
|
+
axiosConfig.validateStatus = () => true;
|
|
2417
2305
|
}
|
|
2418
|
-
return
|
|
2306
|
+
return axiosConfig;
|
|
2419
2307
|
}
|
|
2420
2308
|
async function httpRequest(requestOptions) {
|
|
2421
2309
|
const noBodyMethods = ["GET", "HEAD", "OPTIONS"];
|
|
@@ -2423,69 +2311,27 @@ async function httpRequest(requestOptions) {
|
|
|
2423
2311
|
if (noBodyMethods.includes(method) && requestOptions.body && Object.keys(requestOptions.body).length === 0) {
|
|
2424
2312
|
delete requestOptions.body;
|
|
2425
2313
|
}
|
|
2426
|
-
const
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
if (requestOptions.auth) {
|
|
2430
|
-
const credentials = Buffer.from(
|
|
2431
|
-
`${requestOptions.auth.username || ""}:${requestOptions.auth.password || ""}`
|
|
2432
|
-
).toString("base64");
|
|
2433
|
-
headers["Authorization"] = `Basic ${credentials}`;
|
|
2434
|
-
}
|
|
2435
|
-
if (requestOptions.json) {
|
|
2436
|
-
headers["Accept"] = "application/json";
|
|
2437
|
-
}
|
|
2438
|
-
if (!headers["User-Agent"]) {
|
|
2439
|
-
headers["User-Agent"] = "n8n-habits-executor";
|
|
2314
|
+
const axiosConfig = convertN8nRequestToAxios(requestOptions);
|
|
2315
|
+
if (axiosConfig.data === void 0 || axiosConfig.method?.toUpperCase() === "GET") {
|
|
2316
|
+
delete axiosConfig.data;
|
|
2440
2317
|
}
|
|
2441
|
-
|
|
2442
|
-
let body;
|
|
2443
|
-
if (!noBodyMethods.includes(method)) {
|
|
2444
|
-
body = prepareBody(requestOptions.body, headers);
|
|
2445
|
-
}
|
|
2446
|
-
logger3.log(`\u{1F310} Making HTTP request: ${method} ${url}`);
|
|
2318
|
+
logger3.log(`\u{1F310} Making HTTP request: ${axiosConfig.method} ${axiosConfig.url}`);
|
|
2447
2319
|
try {
|
|
2448
|
-
const response = await
|
|
2449
|
-
method,
|
|
2450
|
-
headers,
|
|
2451
|
-
body,
|
|
2452
|
-
redirect: requestOptions.disableFollowRedirect ? "manual" : "follow"
|
|
2453
|
-
});
|
|
2454
|
-
let responseData;
|
|
2455
|
-
const contentType = response.headers.get("content-type") || "";
|
|
2456
|
-
if (requestOptions.encoding === "arraybuffer" || contentType.includes("application/octet-stream")) {
|
|
2457
|
-
responseData = await response.arrayBuffer();
|
|
2458
|
-
} else if (contentType.includes("application/json")) {
|
|
2459
|
-
responseData = await response.json();
|
|
2460
|
-
} else {
|
|
2461
|
-
responseData = await response.text();
|
|
2462
|
-
try {
|
|
2463
|
-
responseData = JSON.parse(responseData);
|
|
2464
|
-
} catch {
|
|
2465
|
-
}
|
|
2466
|
-
}
|
|
2467
|
-
if (!response.ok && !requestOptions.ignoreHttpStatusErrors) {
|
|
2468
|
-
logger3.error(`HTTP Error (${response.status}): ${JSON.stringify(responseData)}`);
|
|
2469
|
-
throw new Error(`HTTP Error (${response.status}): ${JSON.stringify(responseData)}`);
|
|
2470
|
-
}
|
|
2320
|
+
const response = await axios(axiosConfig);
|
|
2471
2321
|
if (requestOptions.returnFullResponse) {
|
|
2472
|
-
const responseHeaders = {};
|
|
2473
|
-
response.headers.forEach((value, key) => {
|
|
2474
|
-
responseHeaders[key] = value;
|
|
2475
|
-
});
|
|
2476
2322
|
return {
|
|
2477
|
-
body:
|
|
2478
|
-
headers:
|
|
2323
|
+
body: response.data,
|
|
2324
|
+
headers: response.headers,
|
|
2479
2325
|
statusCode: response.status,
|
|
2480
2326
|
statusMessage: response.statusText
|
|
2481
2327
|
};
|
|
2482
2328
|
}
|
|
2483
|
-
return
|
|
2329
|
+
return response.data;
|
|
2484
2330
|
} catch (error) {
|
|
2485
|
-
if (error.
|
|
2486
|
-
|
|
2331
|
+
if (error.response) {
|
|
2332
|
+
logger3.error(`HTTP Error (${error.response.status}): ${JSON.stringify(error.response.data)}`);
|
|
2333
|
+
throw new Error(`HTTP Error (${error.response.status}): ${JSON.stringify(error.response.data)}`);
|
|
2487
2334
|
}
|
|
2488
|
-
logger3.error(`Request failed: ${error.message}`);
|
|
2489
2335
|
throw error;
|
|
2490
2336
|
}
|
|
2491
2337
|
}
|
|
@@ -2701,7 +2547,7 @@ function createExecutionContext(node, params, options) {
|
|
|
2701
2547
|
},
|
|
2702
2548
|
// Helpers object with HTTP request and other utilities
|
|
2703
2549
|
helpers: {
|
|
2704
|
-
// HTTP request function
|
|
2550
|
+
// HTTP request function (real implementation)
|
|
2705
2551
|
httpRequest: async (opts) => {
|
|
2706
2552
|
return await httpRequest(opts);
|
|
2707
2553
|
},
|
|
@@ -2886,6 +2732,7 @@ function createExecutionContext(node, params, options) {
|
|
|
2886
2732
|
|
|
2887
2733
|
// packages/cortex/core/src/n8n/nodeExecution.ts
|
|
2888
2734
|
import * as path4 from "path";
|
|
2735
|
+
import axios2 from "axios";
|
|
2889
2736
|
var logger6 = LoggerFactory.getRoot();
|
|
2890
2737
|
async function loadNodeFromModule(moduleDefinition, mainFilePath) {
|
|
2891
2738
|
const moduleName = getModuleName(moduleDefinition);
|
|
@@ -3093,33 +2940,23 @@ async function executeRoutingBasedNode(node, params, context) {
|
|
|
3093
2940
|
}
|
|
3094
2941
|
logger6.log(`\u{1F310} Making routing-based request: ${method} ${url}`);
|
|
3095
2942
|
logger6.log(`\u{1F4E4} Request body:`, { body });
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
}
|
|
3106
|
-
const requestBody = Object.keys(body).length > 0 ? JSON.stringify(body) : void 0;
|
|
3107
|
-
if (requestBody && !headers["Content-Type"]) {
|
|
3108
|
-
headers["Content-Type"] = "application/json";
|
|
2943
|
+
const axiosConfig = {
|
|
2944
|
+
method,
|
|
2945
|
+
url,
|
|
2946
|
+
headers,
|
|
2947
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : void 0,
|
|
2948
|
+
data: Object.keys(body).length > 0 ? body : void 0
|
|
2949
|
+
};
|
|
2950
|
+
if (requestConfig.encoding === "arraybuffer") {
|
|
2951
|
+
axiosConfig.responseType = "arraybuffer";
|
|
3109
2952
|
}
|
|
3110
2953
|
try {
|
|
3111
|
-
const response = await
|
|
3112
|
-
method,
|
|
3113
|
-
headers,
|
|
3114
|
-
body: requestBody
|
|
3115
|
-
});
|
|
2954
|
+
const response = await axios2(axiosConfig);
|
|
3116
2955
|
logger6.log(`\u2705 Routing-based request successful: ${response.status}`);
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
3120
|
-
const binaryData = Buffer.from(arrayBuffer);
|
|
2956
|
+
if (requestConfig.encoding === "arraybuffer" || response.headers["content-type"]?.includes("audio")) {
|
|
2957
|
+
const binaryData = Buffer.from(response.data);
|
|
3121
2958
|
const base64Data = binaryData.toString("base64");
|
|
3122
|
-
const mimeType =
|
|
2959
|
+
const mimeType = response.headers["content-type"] || "audio/mpeg";
|
|
3123
2960
|
return [[{
|
|
3124
2961
|
json: {
|
|
3125
2962
|
success: true,
|
|
@@ -3136,28 +2973,14 @@ async function executeRoutingBasedNode(node, params, context) {
|
|
|
3136
2973
|
}
|
|
3137
2974
|
}]];
|
|
3138
2975
|
}
|
|
3139
|
-
|
|
3140
|
-
if (contentType.includes("application/json")) {
|
|
3141
|
-
responseData = await response.json();
|
|
3142
|
-
} else {
|
|
3143
|
-
const text = await response.text();
|
|
3144
|
-
try {
|
|
3145
|
-
responseData = JSON.parse(text);
|
|
3146
|
-
} catch {
|
|
3147
|
-
responseData = text;
|
|
3148
|
-
}
|
|
3149
|
-
}
|
|
3150
|
-
if (!response.ok) {
|
|
3151
|
-
const errorMessage = typeof responseData === "object" ? JSON.stringify(responseData) : String(responseData);
|
|
3152
|
-
logger6.error(`HTTP Error (${response.status}): ${errorMessage}`);
|
|
3153
|
-
throw new Error(`HTTP Error (${response.status}): ${errorMessage}`);
|
|
3154
|
-
}
|
|
3155
|
-
return [[{ json: responseData }]];
|
|
2976
|
+
return [[{ json: response.data }]];
|
|
3156
2977
|
} catch (error) {
|
|
3157
|
-
if (error.
|
|
3158
|
-
|
|
2978
|
+
if (error.response) {
|
|
2979
|
+
const errorData = error.response.data;
|
|
2980
|
+
const errorMessage = Buffer.isBuffer(errorData) ? errorData.toString("utf-8") : JSON.stringify(errorData);
|
|
2981
|
+
logger6.error(`HTTP Error (${error.response.status}): ${errorMessage}`);
|
|
2982
|
+
throw new Error(`HTTP Error (${error.response.status}): ${errorMessage}`);
|
|
3159
2983
|
}
|
|
3160
|
-
logger6.error(`Request failed: ${error.message}`);
|
|
3161
2984
|
throw error;
|
|
3162
2985
|
}
|
|
3163
2986
|
}
|
|
@@ -3437,13 +3260,13 @@ async function ensureActivepiecesModulesLoaded2() {
|
|
|
3437
3260
|
trimVersionFromAlias2 = shared.trimVersionFromAlias;
|
|
3438
3261
|
}
|
|
3439
3262
|
var logger9 = LoggerFactory.getRoot();
|
|
3440
|
-
var TriggerHookType = /* @__PURE__ */ ((
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
return
|
|
3263
|
+
var TriggerHookType = /* @__PURE__ */ ((TriggerHookType3) => {
|
|
3264
|
+
TriggerHookType3["ON_ENABLE"] = "ON_ENABLE";
|
|
3265
|
+
TriggerHookType3["ON_DISABLE"] = "ON_DISABLE";
|
|
3266
|
+
TriggerHookType3["RUN"] = "RUN";
|
|
3267
|
+
TriggerHookType3["TEST"] = "TEST";
|
|
3268
|
+
TriggerHookType3["HANDSHAKE"] = "HANDSHAKE";
|
|
3269
|
+
return TriggerHookType3;
|
|
3447
3270
|
})(TriggerHookType || {});
|
|
3448
3271
|
function createSimpleStore(prefix = "") {
|
|
3449
3272
|
const storage = /* @__PURE__ */ new Map();
|
|
@@ -3835,6 +3658,7 @@ var triggerHelper = {
|
|
|
3835
3658
|
};
|
|
3836
3659
|
|
|
3837
3660
|
// packages/cortex/core/src/bits/declarativeExecutor.ts
|
|
3661
|
+
import axios3 from "axios";
|
|
3838
3662
|
function resolveExpression(expression, context) {
|
|
3839
3663
|
if (expression === null || expression === void 0) {
|
|
3840
3664
|
return expression;
|
|
@@ -4172,73 +3996,30 @@ async function executeDeclarativeNode(node, context) {
|
|
|
4172
3996
|
throw new Error("No URL specified in request configuration or defaults");
|
|
4173
3997
|
}
|
|
4174
3998
|
try {
|
|
4175
|
-
|
|
4176
|
-
if (requestConfig.baseURL && !fullUrl.startsWith("http://") && !fullUrl.startsWith("https://")) {
|
|
4177
|
-
fullUrl = requestConfig.baseURL.replace(/\/$/, "") + "/" + fullUrl.replace(/^\//, "");
|
|
4178
|
-
}
|
|
4179
|
-
if (requestConfig.params && Object.keys(requestConfig.params).length > 0) {
|
|
4180
|
-
const params = new URLSearchParams();
|
|
4181
|
-
for (const [key, value] of Object.entries(requestConfig.params)) {
|
|
4182
|
-
if (value !== void 0 && value !== null) {
|
|
4183
|
-
params.append(key, String(value));
|
|
4184
|
-
}
|
|
4185
|
-
}
|
|
4186
|
-
fullUrl += (fullUrl.includes("?") ? "&" : "?") + params.toString();
|
|
4187
|
-
}
|
|
4188
|
-
const headers = { ...requestConfig.headers };
|
|
4189
|
-
let body;
|
|
4190
|
-
if (requestConfig.data) {
|
|
4191
|
-
if (!headers["Content-Type"]) {
|
|
4192
|
-
headers["Content-Type"] = "application/json";
|
|
4193
|
-
}
|
|
4194
|
-
body = typeof requestConfig.data === "string" ? requestConfig.data : JSON.stringify(requestConfig.data);
|
|
4195
|
-
}
|
|
4196
|
-
const response = await fetch2(fullUrl, {
|
|
4197
|
-
method: requestConfig.method || "GET",
|
|
4198
|
-
headers,
|
|
4199
|
-
body
|
|
4200
|
-
});
|
|
4201
|
-
let responseData;
|
|
4202
|
-
const contentType = response.headers.get("content-type") || "";
|
|
4203
|
-
if (contentType.includes("application/json")) {
|
|
4204
|
-
responseData = await response.json();
|
|
4205
|
-
} else {
|
|
4206
|
-
const text = await response.text();
|
|
4207
|
-
try {
|
|
4208
|
-
responseData = JSON.parse(text);
|
|
4209
|
-
} catch {
|
|
4210
|
-
responseData = text;
|
|
4211
|
-
}
|
|
4212
|
-
}
|
|
4213
|
-
const responseHeaders = {};
|
|
4214
|
-
response.headers.forEach((value, key) => {
|
|
4215
|
-
responseHeaders[key] = value;
|
|
4216
|
-
});
|
|
3999
|
+
const response = await axios3(requestConfig);
|
|
4217
4000
|
const operationRouting = findOperationRouting(description.properties, context.parameters);
|
|
4218
|
-
if (!response.ok) {
|
|
4219
|
-
if (operationRouting?.request?.ignoreHttpStatusErrors) {
|
|
4220
|
-
return {
|
|
4221
|
-
success: true,
|
|
4222
|
-
data: responseData,
|
|
4223
|
-
status: response.status,
|
|
4224
|
-
headers: responseHeaders
|
|
4225
|
-
};
|
|
4226
|
-
}
|
|
4227
|
-
throw new Error(`HTTP Error (${response.status}): ${JSON.stringify(responseData)}`);
|
|
4228
|
-
}
|
|
4229
4001
|
const expressionContext = {
|
|
4230
4002
|
$parameter: context.parameters,
|
|
4231
4003
|
$credentials: context.credentials || {},
|
|
4232
|
-
$response:
|
|
4004
|
+
$response: response.data
|
|
4233
4005
|
};
|
|
4234
|
-
const processedData = processResponse(
|
|
4006
|
+
const processedData = processResponse(response.data, operationRouting, expressionContext);
|
|
4235
4007
|
return {
|
|
4236
4008
|
success: true,
|
|
4237
4009
|
data: processedData,
|
|
4238
4010
|
status: response.status,
|
|
4239
|
-
headers:
|
|
4011
|
+
headers: response.headers
|
|
4240
4012
|
};
|
|
4241
4013
|
} catch (error) {
|
|
4014
|
+
const operationRouting = findOperationRouting(description.properties, context.parameters);
|
|
4015
|
+
if (operationRouting?.request?.ignoreHttpStatusErrors && error.response) {
|
|
4016
|
+
return {
|
|
4017
|
+
success: true,
|
|
4018
|
+
data: error.response.data,
|
|
4019
|
+
status: error.response.status,
|
|
4020
|
+
headers: error.response.headers
|
|
4021
|
+
};
|
|
4022
|
+
}
|
|
4242
4023
|
throw new Error(`Request failed: ${error.message}`);
|
|
4243
4024
|
}
|
|
4244
4025
|
}
|
|
@@ -4317,374 +4098,12 @@ function extractDeclarativeNode(loadedModule) {
|
|
|
4317
4098
|
return null;
|
|
4318
4099
|
}
|
|
4319
4100
|
|
|
4320
|
-
// packages/cortex/core/src/store.ts
|
|
4321
|
-
var logger10 = LoggerFactory.getRoot();
|
|
4322
|
-
var driverModule = null;
|
|
4323
|
-
var driverLoadAttempted = false;
|
|
4324
|
-
var useInMemoryFallback = false;
|
|
4325
|
-
var inMemoryStore = /* @__PURE__ */ new Map();
|
|
4326
|
-
var inMemoryDriver = {
|
|
4327
|
-
async store(params) {
|
|
4328
|
-
const key = `${params.collection}:${params.key}`;
|
|
4329
|
-
inMemoryStore.set(key, params.value);
|
|
4330
|
-
return { success: true };
|
|
4331
|
-
},
|
|
4332
|
-
async get(params) {
|
|
4333
|
-
const key = `${params.collection}:${params.key}`;
|
|
4334
|
-
if (inMemoryStore.has(key)) {
|
|
4335
|
-
return { found: true, value: inMemoryStore.get(key) };
|
|
4336
|
-
}
|
|
4337
|
-
return { found: false, value: null };
|
|
4338
|
-
},
|
|
4339
|
-
async del(params) {
|
|
4340
|
-
const key = `${params.collection}:${params.key}`;
|
|
4341
|
-
inMemoryStore.delete(key);
|
|
4342
|
-
return { success: true };
|
|
4343
|
-
},
|
|
4344
|
-
async list(params) {
|
|
4345
|
-
const prefix = `${params.collection}:${params.prefix || ""}`;
|
|
4346
|
-
const keys = [];
|
|
4347
|
-
for (const key of inMemoryStore.keys()) {
|
|
4348
|
-
if (key.startsWith(prefix)) {
|
|
4349
|
-
keys.push(key.replace(`${params.collection}:`, ""));
|
|
4350
|
-
}
|
|
4351
|
-
}
|
|
4352
|
-
return { keys: keys.slice(0, params.limit || 100) };
|
|
4353
|
-
}
|
|
4354
|
-
};
|
|
4355
|
-
async function getDriver() {
|
|
4356
|
-
if (useInMemoryFallback) {
|
|
4357
|
-
return inMemoryDriver;
|
|
4358
|
-
}
|
|
4359
|
-
if (!driverModule && !driverLoadAttempted) {
|
|
4360
|
-
driverLoadAttempted = true;
|
|
4361
|
-
try {
|
|
4362
|
-
const modulePath = "@ha-bits/bit-database-sql/driver";
|
|
4363
|
-
driverModule = await import(
|
|
4364
|
-
/* webpackIgnore: true */
|
|
4365
|
-
modulePath
|
|
4366
|
-
);
|
|
4367
|
-
logger10.log("\u{1F4BE} Polling store: Loaded database driver");
|
|
4368
|
-
} catch (err) {
|
|
4369
|
-
logger10.warn(`\u{1F4BE} Polling store: Database driver not available, using in-memory fallback: ${err.message}`);
|
|
4370
|
-
useInMemoryFallback = true;
|
|
4371
|
-
return inMemoryDriver;
|
|
4372
|
-
}
|
|
4373
|
-
}
|
|
4374
|
-
return driverModule || inMemoryDriver;
|
|
4375
|
-
}
|
|
4376
|
-
var PollingStore = class {
|
|
4377
|
-
constructor(options = {}) {
|
|
4378
|
-
this.options = {
|
|
4379
|
-
database: options.database ?? "habits-polling.db",
|
|
4380
|
-
collection: options.collection ?? "polling",
|
|
4381
|
-
dedupStrategy: options.dedupStrategy ?? "id",
|
|
4382
|
-
ttlDays: options.ttlDays ?? 30
|
|
4383
|
-
};
|
|
4384
|
-
}
|
|
4385
|
-
/**
|
|
4386
|
-
* Generate a unique key for a seen item
|
|
4387
|
-
*/
|
|
4388
|
-
getItemKey(ctx, itemId) {
|
|
4389
|
-
return `${ctx.workflowId}:${ctx.triggerId}:${itemId}`;
|
|
4390
|
-
}
|
|
4391
|
-
/**
|
|
4392
|
-
* Generate the key for storing last polled date
|
|
4393
|
-
*/
|
|
4394
|
-
getLastPolledKey(ctx) {
|
|
4395
|
-
return `${ctx.workflowId}:${ctx.triggerId}:__lastPolled__`;
|
|
4396
|
-
}
|
|
4397
|
-
/**
|
|
4398
|
-
* Check if an item has been seen before
|
|
4399
|
-
*/
|
|
4400
|
-
async hasSeenItem(ctx, itemId, itemDate) {
|
|
4401
|
-
const driver = await getDriver();
|
|
4402
|
-
const key = this.getItemKey(ctx, itemId);
|
|
4403
|
-
const result = await driver.get({
|
|
4404
|
-
collection: this.options.collection,
|
|
4405
|
-
key,
|
|
4406
|
-
database: this.options.database
|
|
4407
|
-
});
|
|
4408
|
-
if (!result.found) {
|
|
4409
|
-
return false;
|
|
4410
|
-
}
|
|
4411
|
-
if (this.options.dedupStrategy === "date" && itemDate) {
|
|
4412
|
-
const record = result.value;
|
|
4413
|
-
return new Date(record.sourceDate) >= new Date(itemDate);
|
|
4414
|
-
}
|
|
4415
|
-
return true;
|
|
4416
|
-
}
|
|
4417
|
-
/**
|
|
4418
|
-
* Mark an item as seen
|
|
4419
|
-
*/
|
|
4420
|
-
async markItemSeen(ctx, itemId, sourceDate, data) {
|
|
4421
|
-
const driver = await getDriver();
|
|
4422
|
-
const key = this.getItemKey(ctx, itemId);
|
|
4423
|
-
const record = {
|
|
4424
|
-
id: itemId,
|
|
4425
|
-
sourceDate,
|
|
4426
|
-
seenAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4427
|
-
data
|
|
4428
|
-
};
|
|
4429
|
-
await driver.store({
|
|
4430
|
-
collection: this.options.collection,
|
|
4431
|
-
key,
|
|
4432
|
-
value: record,
|
|
4433
|
-
database: this.options.database
|
|
4434
|
-
});
|
|
4435
|
-
logger10.log(`\u{1F4BE} Polling store: Marked item as seen: ${itemId}`);
|
|
4436
|
-
}
|
|
4437
|
-
/**
|
|
4438
|
-
* Get multiple items' seen status in batch
|
|
4439
|
-
* Returns a Set of item IDs that have been seen
|
|
4440
|
-
*/
|
|
4441
|
-
async getSeenItems(ctx, itemIds) {
|
|
4442
|
-
const seen = /* @__PURE__ */ new Set();
|
|
4443
|
-
for (const itemId of itemIds) {
|
|
4444
|
-
if (await this.hasSeenItem(ctx, itemId)) {
|
|
4445
|
-
seen.add(itemId);
|
|
4446
|
-
}
|
|
4447
|
-
}
|
|
4448
|
-
return seen;
|
|
4449
|
-
}
|
|
4450
|
-
/**
|
|
4451
|
-
* Mark multiple items as seen in batch
|
|
4452
|
-
*/
|
|
4453
|
-
async markItemsSeen(ctx, items) {
|
|
4454
|
-
for (const item of items) {
|
|
4455
|
-
await this.markItemSeen(ctx, item.id, item.date, item.data);
|
|
4456
|
-
}
|
|
4457
|
-
}
|
|
4458
|
-
/**
|
|
4459
|
-
* Get the last polled timestamp for a trigger
|
|
4460
|
-
* Returns null if never polled before
|
|
4461
|
-
*/
|
|
4462
|
-
async getLastPolledDate(ctx) {
|
|
4463
|
-
const driver = await getDriver();
|
|
4464
|
-
const key = this.getLastPolledKey(ctx);
|
|
4465
|
-
const result = await driver.get({
|
|
4466
|
-
collection: this.options.collection,
|
|
4467
|
-
key,
|
|
4468
|
-
database: this.options.database
|
|
4469
|
-
});
|
|
4470
|
-
if (!result.found) {
|
|
4471
|
-
return null;
|
|
4472
|
-
}
|
|
4473
|
-
return result.value;
|
|
4474
|
-
}
|
|
4475
|
-
/**
|
|
4476
|
-
* Set the last polled timestamp for a trigger
|
|
4477
|
-
*/
|
|
4478
|
-
async setLastPolledDate(ctx, date) {
|
|
4479
|
-
const driver = await getDriver();
|
|
4480
|
-
const key = this.getLastPolledKey(ctx);
|
|
4481
|
-
await driver.store({
|
|
4482
|
-
collection: this.options.collection,
|
|
4483
|
-
key,
|
|
4484
|
-
value: date,
|
|
4485
|
-
database: this.options.database
|
|
4486
|
-
});
|
|
4487
|
-
logger10.log(`\u{1F4BE} Polling store: Updated last polled date: ${date}`);
|
|
4488
|
-
}
|
|
4489
|
-
/**
|
|
4490
|
-
* Get the count of seen items for a trigger
|
|
4491
|
-
*/
|
|
4492
|
-
async getSeenCount(ctx) {
|
|
4493
|
-
const driver = await getDriver();
|
|
4494
|
-
const prefix = `${ctx.workflowId}:${ctx.triggerId}:`;
|
|
4495
|
-
const result = await driver.list({
|
|
4496
|
-
collection: this.options.collection,
|
|
4497
|
-
prefix,
|
|
4498
|
-
limit: 1e4,
|
|
4499
|
-
// High limit to count all
|
|
4500
|
-
database: this.options.database
|
|
4501
|
-
});
|
|
4502
|
-
const count = result.keys.filter((k) => !k.endsWith("__lastPolled__")).length;
|
|
4503
|
-
return count;
|
|
4504
|
-
}
|
|
4505
|
-
/**
|
|
4506
|
-
* Clear all seen items for a trigger (useful for testing or reset)
|
|
4507
|
-
*/
|
|
4508
|
-
async clearTrigger(ctx) {
|
|
4509
|
-
const driver = await getDriver();
|
|
4510
|
-
const prefix = `${ctx.workflowId}:${ctx.triggerId}:`;
|
|
4511
|
-
const result = await driver.list({
|
|
4512
|
-
collection: this.options.collection,
|
|
4513
|
-
prefix,
|
|
4514
|
-
limit: 1e4,
|
|
4515
|
-
database: this.options.database
|
|
4516
|
-
});
|
|
4517
|
-
let deleted = 0;
|
|
4518
|
-
for (const key of result.keys) {
|
|
4519
|
-
await driver.del({
|
|
4520
|
-
collection: this.options.collection,
|
|
4521
|
-
key,
|
|
4522
|
-
database: this.options.database
|
|
4523
|
-
});
|
|
4524
|
-
deleted++;
|
|
4525
|
-
}
|
|
4526
|
-
logger10.log(`\u{1F4BE} Polling store: Cleared ${deleted} items for trigger ${ctx.triggerId}`);
|
|
4527
|
-
return deleted;
|
|
4528
|
-
}
|
|
4529
|
-
/**
|
|
4530
|
-
* TODO: Implement TTL-based cleanup
|
|
4531
|
-
* Clean up old records based on ttlDays configuration
|
|
4532
|
-
*/
|
|
4533
|
-
async cleanup() {
|
|
4534
|
-
logger10.log(`\u{1F4BE} Polling store: Cleanup not yet implemented (TTL: ${this.options.ttlDays} days)`);
|
|
4535
|
-
return 0;
|
|
4536
|
-
}
|
|
4537
|
-
};
|
|
4538
|
-
function createPollingStore(options) {
|
|
4539
|
-
return new PollingStore(options);
|
|
4540
|
-
}
|
|
4541
|
-
|
|
4542
|
-
// packages/cortex/core/src/bits/oauthTokenStore.ts
|
|
4543
|
-
var OAuth2TokenStore = class {
|
|
4544
|
-
constructor() {
|
|
4545
|
-
this.tokens = /* @__PURE__ */ new Map();
|
|
4546
|
-
this.logger = LoggerFactory.create(void 0, void 0, { bitName: "OAuth2TokenStore" });
|
|
4547
|
-
}
|
|
4548
|
-
/**
|
|
4549
|
-
* Store tokens for a bit
|
|
4550
|
-
* @param bitId - Unique identifier for the bit (e.g., "bit-oauth-mock")
|
|
4551
|
-
* @param tokens - Token set from OAuth provider
|
|
4552
|
-
* @param config - OAuth config used (for refresh)
|
|
4553
|
-
*/
|
|
4554
|
-
setToken(bitId, tokens, config) {
|
|
4555
|
-
this.tokens.set(bitId, {
|
|
4556
|
-
tokens,
|
|
4557
|
-
config,
|
|
4558
|
-
storedAt: Date.now()
|
|
4559
|
-
});
|
|
4560
|
-
this.logger.info("Token stored", { bitId, expiresAt: tokens.expiresAt });
|
|
4561
|
-
}
|
|
4562
|
-
/**
|
|
4563
|
-
* Get tokens for a bit
|
|
4564
|
-
* @param bitId - Unique identifier for the bit
|
|
4565
|
-
* @returns Token set or null if not found
|
|
4566
|
-
*/
|
|
4567
|
-
getToken(bitId) {
|
|
4568
|
-
const entry = this.tokens.get(bitId);
|
|
4569
|
-
if (!entry) {
|
|
4570
|
-
return null;
|
|
4571
|
-
}
|
|
4572
|
-
return entry.tokens;
|
|
4573
|
-
}
|
|
4574
|
-
/**
|
|
4575
|
-
* Get full token entry including config
|
|
4576
|
-
* @param bitId - Unique identifier for the bit
|
|
4577
|
-
*/
|
|
4578
|
-
getEntry(bitId) {
|
|
4579
|
-
return this.tokens.get(bitId) || null;
|
|
4580
|
-
}
|
|
4581
|
-
/**
|
|
4582
|
-
* Check if a valid (non-expired) token exists for a bit
|
|
4583
|
-
* @param bitId - Unique identifier for the bit
|
|
4584
|
-
* @returns true if valid token exists
|
|
4585
|
-
*/
|
|
4586
|
-
hasValidToken(bitId) {
|
|
4587
|
-
const entry = this.tokens.get(bitId);
|
|
4588
|
-
if (!entry) {
|
|
4589
|
-
return false;
|
|
4590
|
-
}
|
|
4591
|
-
return !this.isExpired(bitId);
|
|
4592
|
-
}
|
|
4593
|
-
/**
|
|
4594
|
-
* Check if token is expired
|
|
4595
|
-
* @param bitId - Unique identifier for the bit
|
|
4596
|
-
* @returns true if token is expired or doesn't exist
|
|
4597
|
-
*/
|
|
4598
|
-
isExpired(bitId) {
|
|
4599
|
-
const entry = this.tokens.get(bitId);
|
|
4600
|
-
if (!entry) {
|
|
4601
|
-
return true;
|
|
4602
|
-
}
|
|
4603
|
-
if (!entry.tokens.expiresAt) {
|
|
4604
|
-
return false;
|
|
4605
|
-
}
|
|
4606
|
-
return Date.now() > entry.tokens.expiresAt - 6e4;
|
|
4607
|
-
}
|
|
4608
|
-
/**
|
|
4609
|
-
* Remove token for a bit
|
|
4610
|
-
* @param bitId - Unique identifier for the bit
|
|
4611
|
-
*/
|
|
4612
|
-
removeToken(bitId) {
|
|
4613
|
-
this.tokens.delete(bitId);
|
|
4614
|
-
this.logger.info("Token removed", { bitId });
|
|
4615
|
-
}
|
|
4616
|
-
/**
|
|
4617
|
-
* Get all stored bit IDs
|
|
4618
|
-
*/
|
|
4619
|
-
getAllBitIds() {
|
|
4620
|
-
return Array.from(this.tokens.keys());
|
|
4621
|
-
}
|
|
4622
|
-
/**
|
|
4623
|
-
* Clear all tokens
|
|
4624
|
-
*/
|
|
4625
|
-
clear() {
|
|
4626
|
-
this.tokens.clear();
|
|
4627
|
-
this.logger.info("All tokens cleared");
|
|
4628
|
-
}
|
|
4629
|
-
/**
|
|
4630
|
-
* Refresh an expired token using the refresh token
|
|
4631
|
-
* @param bitId - Unique identifier for the bit
|
|
4632
|
-
* @returns New token set or null if refresh failed
|
|
4633
|
-
*/
|
|
4634
|
-
async refreshToken(bitId) {
|
|
4635
|
-
const entry = this.tokens.get(bitId);
|
|
4636
|
-
if (!entry || !entry.tokens.refreshToken) {
|
|
4637
|
-
this.logger.warn("Cannot refresh token: no refresh token available", { bitId });
|
|
4638
|
-
return null;
|
|
4639
|
-
}
|
|
4640
|
-
const { config, tokens } = entry;
|
|
4641
|
-
try {
|
|
4642
|
-
const params = new URLSearchParams({
|
|
4643
|
-
grant_type: "refresh_token",
|
|
4644
|
-
refresh_token: tokens.refreshToken,
|
|
4645
|
-
client_id: config.clientId
|
|
4646
|
-
});
|
|
4647
|
-
if (config.clientSecret) {
|
|
4648
|
-
params.set("client_secret", config.clientSecret);
|
|
4649
|
-
}
|
|
4650
|
-
const response = await fetch(config.tokenUrl, {
|
|
4651
|
-
method: "POST",
|
|
4652
|
-
headers: {
|
|
4653
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
4654
|
-
},
|
|
4655
|
-
body: params.toString()
|
|
4656
|
-
});
|
|
4657
|
-
if (!response.ok) {
|
|
4658
|
-
const errorText = await response.text();
|
|
4659
|
-
this.logger.error("Token refresh failed", { bitId, status: response.status, error: errorText });
|
|
4660
|
-
return null;
|
|
4661
|
-
}
|
|
4662
|
-
const data = await response.json();
|
|
4663
|
-
const newTokens = {
|
|
4664
|
-
accessToken: data.access_token,
|
|
4665
|
-
refreshToken: data.refresh_token || tokens.refreshToken,
|
|
4666
|
-
// Keep old refresh token if not returned
|
|
4667
|
-
tokenType: data.token_type || "Bearer",
|
|
4668
|
-
expiresAt: data.expires_in ? Date.now() + data.expires_in * 1e3 : void 0,
|
|
4669
|
-
scope: data.scope
|
|
4670
|
-
};
|
|
4671
|
-
this.setToken(bitId, newTokens, config);
|
|
4672
|
-
this.logger.info("Token refreshed successfully", { bitId });
|
|
4673
|
-
return newTokens;
|
|
4674
|
-
} catch (error) {
|
|
4675
|
-
this.logger.error("Token refresh error", { bitId, error: String(error) });
|
|
4676
|
-
return null;
|
|
4677
|
-
}
|
|
4678
|
-
}
|
|
4679
|
-
};
|
|
4680
|
-
var oauthTokenStore = new OAuth2TokenStore();
|
|
4681
|
-
|
|
4682
4101
|
// packages/cortex/core/src/bits/bitsDoer.ts
|
|
4683
|
-
var
|
|
4102
|
+
var logger10 = LoggerFactory.getRoot();
|
|
4684
4103
|
function extractBitsPieceFromModule(loadedModule) {
|
|
4685
4104
|
const declarativeNode = extractDeclarativeNode(loadedModule);
|
|
4686
4105
|
if (declarativeNode) {
|
|
4687
|
-
|
|
4106
|
+
logger10.log(`\u{1F4CB} Detected declarative node: ${declarativeNode.description.displayName}`);
|
|
4688
4107
|
return convertDeclarativeNodeToBitsPiece(declarativeNode);
|
|
4689
4108
|
}
|
|
4690
4109
|
let piece = null;
|
|
@@ -4705,8 +4124,6 @@ function extractBitsPieceFromModule(loadedModule) {
|
|
|
4705
4124
|
throw new Error("No valid bits piece found in module. Expected export with actions and triggers.");
|
|
4706
4125
|
}
|
|
4707
4126
|
return {
|
|
4708
|
-
id: piece.id,
|
|
4709
|
-
// Webhook routing ID (e.g., 'gohighlevel', 'hubspot')
|
|
4710
4127
|
displayName: piece.displayName || "Unknown Piece",
|
|
4711
4128
|
description: piece.description,
|
|
4712
4129
|
logoUrl: piece.logoUrl,
|
|
@@ -4824,20 +4241,11 @@ function createDeclarativeActionRunner(node, operation) {
|
|
|
4824
4241
|
}
|
|
4825
4242
|
async function pieceFromModule2(moduleDefinition) {
|
|
4826
4243
|
const moduleName = getModuleName(moduleDefinition);
|
|
4827
|
-
if (isBundledModule(moduleDefinition.repository)) {
|
|
4828
|
-
logger11.log(`\u{1F4E6} Using pre-bundled module: ${moduleName}`);
|
|
4829
|
-
const loadedModule = getBundledModule(moduleDefinition.repository);
|
|
4830
|
-
if (loadedModule) {
|
|
4831
|
-
const piece = extractBitsPieceFromModule(loadedModule);
|
|
4832
|
-
return piece;
|
|
4833
|
-
}
|
|
4834
|
-
throw new Error(`Bundled module ${moduleName} not found in registry`);
|
|
4835
|
-
}
|
|
4836
4244
|
const mainFilePath = getModuleMainFile(moduleDefinition);
|
|
4837
4245
|
if (!mainFilePath) {
|
|
4838
4246
|
throw new Error(`Could not locate main file for module: ${moduleName}`);
|
|
4839
4247
|
}
|
|
4840
|
-
|
|
4248
|
+
logger10.log(`\u{1F4E6} Bits module ready at: ${mainFilePath}`);
|
|
4841
4249
|
const originalCwd = process.cwd();
|
|
4842
4250
|
const moduleDir = dirname(mainFilePath);
|
|
4843
4251
|
let nodeModulesDir = moduleDir;
|
|
@@ -4852,18 +4260,18 @@ async function pieceFromModule2(moduleDefinition) {
|
|
|
4852
4260
|
return piece;
|
|
4853
4261
|
} catch (error) {
|
|
4854
4262
|
process.chdir(originalCwd);
|
|
4855
|
-
|
|
4263
|
+
logger10.error(error.stack);
|
|
4856
4264
|
throw error;
|
|
4857
4265
|
}
|
|
4858
4266
|
}
|
|
4859
4267
|
async function executeGenericBitsPiece(params, moduleDefinition) {
|
|
4860
4268
|
try {
|
|
4861
4269
|
const piece = await pieceFromModule2(moduleDefinition);
|
|
4862
|
-
|
|
4270
|
+
logger10.log(`\u{1F680} Executing Bits piece: ${piece.displayName}`);
|
|
4863
4271
|
const actionName = params.params.operation;
|
|
4864
4272
|
const pieceActions = piece.actions();
|
|
4865
|
-
|
|
4866
|
-
|
|
4273
|
+
logger10.log(`Available actions: ${Object.keys(pieceActions).join(", ")}`);
|
|
4274
|
+
logger10.log(`Requested action: ${actionName}`);
|
|
4867
4275
|
const action = pieceActions[actionName];
|
|
4868
4276
|
if (!action) {
|
|
4869
4277
|
throw new Error(
|
|
@@ -4872,80 +4280,11 @@ async function executeGenericBitsPiece(params, moduleDefinition) {
|
|
|
4872
4280
|
}
|
|
4873
4281
|
let auth = void 0;
|
|
4874
4282
|
const { credentials, ...actionProps } = params.params;
|
|
4875
|
-
if (
|
|
4876
|
-
const parts = moduleDefinition.repository.split("/");
|
|
4877
|
-
const bitId = parts[parts.length - 1];
|
|
4878
|
-
const oauthCredKey = Object.keys(credentials || {}).find((key) => {
|
|
4879
|
-
const cred = credentials[key];
|
|
4880
|
-
return cred && (cred.accessToken || cred.access_token);
|
|
4881
|
-
});
|
|
4882
|
-
if (oauthCredKey && credentials[oauthCredKey]) {
|
|
4883
|
-
const directTokens = credentials[oauthCredKey];
|
|
4884
|
-
auth = {
|
|
4885
|
-
accessToken: directTokens.accessToken || directTokens.access_token,
|
|
4886
|
-
refreshToken: directTokens.refreshToken || directTokens.refresh_token,
|
|
4887
|
-
tokenType: directTokens.tokenType || directTokens.token_type || "Bearer",
|
|
4888
|
-
expiresAt: directTokens.expiresAt || directTokens.expires_at
|
|
4889
|
-
};
|
|
4890
|
-
logger11.log(`\u{1F510} Using OAuth2 tokens from credentials for: ${bitId}`);
|
|
4891
|
-
if (auth.refreshToken) {
|
|
4892
|
-
const pieceAuth = piece.auth;
|
|
4893
|
-
oauthTokenStore.setToken(bitId, auth, {
|
|
4894
|
-
displayName: pieceAuth.displayName || bitId,
|
|
4895
|
-
required: pieceAuth.required || false,
|
|
4896
|
-
authorizationUrl: pieceAuth.authorizationUrl || "",
|
|
4897
|
-
tokenUrl: pieceAuth.tokenUrl || "",
|
|
4898
|
-
clientId: pieceAuth.clientId || "",
|
|
4899
|
-
clientSecret: pieceAuth.clientSecret,
|
|
4900
|
-
scopes: pieceAuth.scopes || []
|
|
4901
|
-
});
|
|
4902
|
-
}
|
|
4903
|
-
} else if (params.oauthTokens && params.oauthTokens[bitId]) {
|
|
4904
|
-
const userToken = params.oauthTokens[bitId];
|
|
4905
|
-
auth = {
|
|
4906
|
-
accessToken: userToken.accessToken,
|
|
4907
|
-
refreshToken: userToken.refreshToken,
|
|
4908
|
-
tokenType: userToken.tokenType,
|
|
4909
|
-
expiresAt: userToken.expiresAt
|
|
4910
|
-
};
|
|
4911
|
-
logger11.log(`\u{1F510} Using per-user OAuth2 token from cookies for: ${bitId}`);
|
|
4912
|
-
if (userToken.expiresAt && userToken.expiresAt < Date.now()) {
|
|
4913
|
-
logger11.warn(`\u26A0\uFE0F Per-user OAuth token expired for ${bitId}. User needs to re-authenticate.`);
|
|
4914
|
-
}
|
|
4915
|
-
} else {
|
|
4916
|
-
const oauthToken = oauthTokenStore.getToken(bitId);
|
|
4917
|
-
if (oauthToken) {
|
|
4918
|
-
auth = {
|
|
4919
|
-
accessToken: oauthToken.accessToken,
|
|
4920
|
-
refreshToken: oauthToken.refreshToken,
|
|
4921
|
-
tokenType: oauthToken.tokenType,
|
|
4922
|
-
expiresAt: oauthToken.expiresAt
|
|
4923
|
-
};
|
|
4924
|
-
logger11.log(`\u{1F510} Using OAuth2 PKCE token for: ${bitId}`);
|
|
4925
|
-
if (oauthTokenStore.isExpired(bitId)) {
|
|
4926
|
-
logger11.log(`\u26A0\uFE0F OAuth token expired for ${bitId}, attempting refresh...`);
|
|
4927
|
-
const refreshedToken = await oauthTokenStore.refreshToken(bitId);
|
|
4928
|
-
if (refreshedToken) {
|
|
4929
|
-
auth = {
|
|
4930
|
-
accessToken: refreshedToken.accessToken,
|
|
4931
|
-
refreshToken: refreshedToken.refreshToken,
|
|
4932
|
-
tokenType: refreshedToken.tokenType,
|
|
4933
|
-
expiresAt: refreshedToken.expiresAt
|
|
4934
|
-
};
|
|
4935
|
-
logger11.log(`\u2705 OAuth token refreshed for ${bitId}`);
|
|
4936
|
-
} else {
|
|
4937
|
-
logger11.warn(`\u274C Failed to refresh OAuth token for ${bitId}`);
|
|
4938
|
-
}
|
|
4939
|
-
}
|
|
4940
|
-
} else {
|
|
4941
|
-
logger11.warn(`\u26A0\uFE0F No OAuth token found for ${bitId}. Provide tokens via credentials or complete the OAuth flow.`);
|
|
4942
|
-
}
|
|
4943
|
-
}
|
|
4944
|
-
} else if (credentials) {
|
|
4283
|
+
if (credentials) {
|
|
4945
4284
|
const credentialKeys = Object.keys(credentials);
|
|
4946
4285
|
if (credentialKeys.length > 0) {
|
|
4947
4286
|
auth = credentials[credentialKeys[0]];
|
|
4948
|
-
|
|
4287
|
+
logger10.log(`\u{1F510} Using credentials for: ${credentialKeys[0]}`);
|
|
4949
4288
|
}
|
|
4950
4289
|
}
|
|
4951
4290
|
let bitLogger = void 0;
|
|
@@ -4963,10 +4302,9 @@ async function executeGenericBitsPiece(params, moduleDefinition) {
|
|
|
4963
4302
|
propsValue: {
|
|
4964
4303
|
...actionProps
|
|
4965
4304
|
},
|
|
4966
|
-
logger: bitLogger
|
|
4967
|
-
executor: params.executor
|
|
4305
|
+
logger: bitLogger
|
|
4968
4306
|
});
|
|
4969
|
-
|
|
4307
|
+
logger10.log(`\u2705 Successfully executed Bits piece action: ${actionName}`, result);
|
|
4970
4308
|
return {
|
|
4971
4309
|
success: true,
|
|
4972
4310
|
module: moduleDefinition.repository,
|
|
@@ -4981,7 +4319,7 @@ async function executeGenericBitsPiece(params, moduleDefinition) {
|
|
|
4981
4319
|
}
|
|
4982
4320
|
};
|
|
4983
4321
|
} catch (error) {
|
|
4984
|
-
|
|
4322
|
+
logger10.error(error.stack);
|
|
4985
4323
|
throw error;
|
|
4986
4324
|
}
|
|
4987
4325
|
}
|
|
@@ -4995,7 +4333,7 @@ async function executeBitsModule(params) {
|
|
|
4995
4333
|
throw new Error(`Bits module '${params.moduleName}' not found in modules.json`);
|
|
4996
4334
|
}
|
|
4997
4335
|
const inferredModuleName = getModuleName(moduleDefinition);
|
|
4998
|
-
|
|
4336
|
+
logger10.log(`
|
|
4999
4337
|
\u{1F50D} Ensuring bits module is ready: ${inferredModuleName}`);
|
|
5000
4338
|
await ensureModuleInstalled(moduleDefinition);
|
|
5001
4339
|
try {
|
|
@@ -5006,7 +4344,7 @@ async function executeBitsModule(params) {
|
|
|
5006
4344
|
}
|
|
5007
4345
|
|
|
5008
4346
|
// packages/cortex/core/src/bits/bitsWatcher.ts
|
|
5009
|
-
var
|
|
4347
|
+
var logger11 = LoggerFactory.getRoot();
|
|
5010
4348
|
function createSimpleStore2(prefix = "") {
|
|
5011
4349
|
const storage = /* @__PURE__ */ new Map();
|
|
5012
4350
|
return {
|
|
@@ -5024,39 +4362,6 @@ function createSimpleStore2(prefix = "") {
|
|
|
5024
4362
|
}
|
|
5025
4363
|
};
|
|
5026
4364
|
}
|
|
5027
|
-
function createBoundPollingStore(workflowId, triggerId, dedupStrategy = "id") {
|
|
5028
|
-
const store = createPollingStore({
|
|
5029
|
-
collection: "polling_seen_items",
|
|
5030
|
-
dedupStrategy
|
|
5031
|
-
});
|
|
5032
|
-
const ctx = { workflowId, triggerId };
|
|
5033
|
-
const baseStore = createSimpleStore2(`polling:${workflowId}:${triggerId}`);
|
|
5034
|
-
return {
|
|
5035
|
-
// Base store methods
|
|
5036
|
-
get: baseStore.get,
|
|
5037
|
-
put: baseStore.put,
|
|
5038
|
-
delete: baseStore.delete,
|
|
5039
|
-
// Polling-specific methods
|
|
5040
|
-
async hasSeenItem(itemId, itemDate) {
|
|
5041
|
-
return store.hasSeenItem(ctx, itemId, itemDate);
|
|
5042
|
-
},
|
|
5043
|
-
async markItemSeen(itemId, sourceDate, data) {
|
|
5044
|
-
return store.markItemSeen(ctx, itemId, sourceDate, data);
|
|
5045
|
-
},
|
|
5046
|
-
async getLastPolledDate() {
|
|
5047
|
-
return store.getLastPolledDate(ctx);
|
|
5048
|
-
},
|
|
5049
|
-
async setLastPolledDate(date) {
|
|
5050
|
-
return store.setLastPolledDate(ctx, date);
|
|
5051
|
-
},
|
|
5052
|
-
async getSeenCount() {
|
|
5053
|
-
return store.getSeenCount(ctx);
|
|
5054
|
-
},
|
|
5055
|
-
async clearTrigger() {
|
|
5056
|
-
return store.clearTrigger(ctx);
|
|
5057
|
-
}
|
|
5058
|
-
};
|
|
5059
|
-
}
|
|
5060
4365
|
function isNil2(value) {
|
|
5061
4366
|
return value === null || value === void 0;
|
|
5062
4367
|
}
|
|
@@ -5101,7 +4406,7 @@ var bitsTriggerHelper = {
|
|
|
5101
4406
|
* Execute a trigger based on hook type
|
|
5102
4407
|
*/
|
|
5103
4408
|
async executeTrigger(params) {
|
|
5104
|
-
const { moduleDefinition, triggerName, input, hookType, trigger, payload, webhookUrl, isTest, store
|
|
4409
|
+
const { moduleDefinition, triggerName, input, hookType, trigger, payload, webhookUrl, isTest, store } = params;
|
|
5105
4410
|
if (isNil2(triggerName)) {
|
|
5106
4411
|
throw new Error("Trigger name is not set");
|
|
5107
4412
|
}
|
|
@@ -5112,19 +4417,11 @@ var bitsTriggerHelper = {
|
|
|
5112
4417
|
const { trigger: loadedTrigger } = await this.getTrigger(moduleDefinition, triggerName);
|
|
5113
4418
|
bitsTrigger = loadedTrigger;
|
|
5114
4419
|
}
|
|
5115
|
-
|
|
4420
|
+
logger11.log(`\u{1F514} Executing bits trigger: ${triggerName} (${hookType})`);
|
|
5116
4421
|
const appListeners = [];
|
|
5117
4422
|
let scheduleOptions;
|
|
5118
4423
|
const storePrefix = isTest ? "test" : triggerName;
|
|
5119
4424
|
const triggerStore = store || createSimpleStore2(storePrefix);
|
|
5120
|
-
const triggerType = mapTriggerType(bitsTrigger.type);
|
|
5121
|
-
let pollingStore;
|
|
5122
|
-
if (triggerType === "POLLING" /* POLLING */ && workflowId) {
|
|
5123
|
-
const dedupStrategy = input.dedupBy || "id";
|
|
5124
|
-
const triggerId = `${moduleDefinition.repository}:${triggerName}`;
|
|
5125
|
-
pollingStore = createBoundPollingStore(workflowId, triggerId, dedupStrategy);
|
|
5126
|
-
logger12.log(`\u{1F4CA} Created polling store for ${triggerId} (dedup: ${dedupStrategy})`);
|
|
5127
|
-
}
|
|
5128
4425
|
let auth = void 0;
|
|
5129
4426
|
const { credentials, ...triggerProps } = input;
|
|
5130
4427
|
if (credentials) {
|
|
@@ -5135,7 +4432,7 @@ var bitsTriggerHelper = {
|
|
|
5135
4432
|
props: credentialData,
|
|
5136
4433
|
...credentialData
|
|
5137
4434
|
};
|
|
5138
|
-
|
|
4435
|
+
logger11.log(`\u{1F510} Using credentials for trigger: ${credentialKeys[0]}`);
|
|
5139
4436
|
}
|
|
5140
4437
|
}
|
|
5141
4438
|
const context = {
|
|
@@ -5154,11 +4451,7 @@ var bitsTriggerHelper = {
|
|
|
5154
4451
|
cronExpression: options.cronExpression,
|
|
5155
4452
|
timezone: options.timezone ?? "UTC"
|
|
5156
4453
|
};
|
|
5157
|
-
}
|
|
5158
|
-
executor,
|
|
5159
|
-
workflowId,
|
|
5160
|
-
nodeId,
|
|
5161
|
-
pollingStore
|
|
4454
|
+
}
|
|
5162
4455
|
};
|
|
5163
4456
|
try {
|
|
5164
4457
|
switch (hookType) {
|
|
@@ -5166,11 +4459,11 @@ var bitsTriggerHelper = {
|
|
|
5166
4459
|
if (bitsTrigger.onEnable) {
|
|
5167
4460
|
await bitsTrigger.onEnable(context);
|
|
5168
4461
|
}
|
|
5169
|
-
const
|
|
4462
|
+
const triggerType = mapTriggerType(bitsTrigger.type);
|
|
5170
4463
|
return {
|
|
5171
4464
|
success: true,
|
|
5172
4465
|
listeners: appListeners,
|
|
5173
|
-
scheduleOptions:
|
|
4466
|
+
scheduleOptions: triggerType === "POLLING" /* POLLING */ ? scheduleOptions : void 0
|
|
5174
4467
|
};
|
|
5175
4468
|
}
|
|
5176
4469
|
case "ON_DISABLE" /* ON_DISABLE */: {
|
|
@@ -5227,7 +4520,7 @@ var bitsTriggerHelper = {
|
|
|
5227
4520
|
};
|
|
5228
4521
|
}
|
|
5229
4522
|
} catch (error) {
|
|
5230
|
-
|
|
4523
|
+
logger11.error(`Error executing trigger ${triggerName}:`, error);
|
|
5231
4524
|
return {
|
|
5232
4525
|
success: false,
|
|
5233
4526
|
message: `Error executing trigger: ${error}`,
|
|
@@ -5256,13 +4549,13 @@ var bitsTriggerHelper = {
|
|
|
5256
4549
|
* For webhooks: calls run directly
|
|
5257
4550
|
*/
|
|
5258
4551
|
async executeBitsTrigger(params) {
|
|
5259
|
-
const { moduleDefinition, triggerName, input, payload, webhookUrl, store
|
|
4552
|
+
const { moduleDefinition, triggerName, input, payload, webhookUrl, store } = params;
|
|
5260
4553
|
const { piece, trigger } = await this.getTrigger(moduleDefinition, triggerName);
|
|
5261
4554
|
const triggerType = mapTriggerType(trigger.type);
|
|
5262
4555
|
const triggerStore = store || createSimpleStore2(`trigger:${triggerName}`);
|
|
5263
4556
|
switch (triggerType) {
|
|
5264
4557
|
case "POLLING" /* POLLING */: {
|
|
5265
|
-
|
|
4558
|
+
logger11.log(`Polling trigger flow: onEnable \u2192 run`);
|
|
5266
4559
|
const onEnableResult = await this.executeTrigger({
|
|
5267
4560
|
moduleDefinition,
|
|
5268
4561
|
triggerName,
|
|
@@ -5272,22 +4565,19 @@ var bitsTriggerHelper = {
|
|
|
5272
4565
|
payload,
|
|
5273
4566
|
webhookUrl,
|
|
5274
4567
|
isTest: false,
|
|
5275
|
-
store: triggerStore
|
|
5276
|
-
executor,
|
|
5277
|
-
workflowId,
|
|
5278
|
-
nodeId
|
|
4568
|
+
store: triggerStore
|
|
5279
4569
|
});
|
|
5280
4570
|
if (!onEnableResult.success) {
|
|
5281
4571
|
return onEnableResult;
|
|
5282
4572
|
}
|
|
5283
|
-
|
|
4573
|
+
logger11.log(` \u2705 onEnable completed`);
|
|
5284
4574
|
if (onEnableResult.scheduleOptions) {
|
|
5285
|
-
|
|
4575
|
+
logger11.log(` \u{1F4C5} Schedule: ${onEnableResult.scheduleOptions.cronExpression} (${onEnableResult.scheduleOptions.timezone})`);
|
|
5286
4576
|
}
|
|
5287
4577
|
if (onEnableResult.listeners && onEnableResult.listeners.length > 0) {
|
|
5288
|
-
|
|
4578
|
+
logger11.log(` \u{1F442} Listeners: ${onEnableResult.listeners.length}`);
|
|
5289
4579
|
}
|
|
5290
|
-
|
|
4580
|
+
logger11.log(` \u2192 Calling run to fetch items...`);
|
|
5291
4581
|
const runResult = await this.executeTrigger({
|
|
5292
4582
|
moduleDefinition,
|
|
5293
4583
|
triggerName,
|
|
@@ -5297,20 +4587,17 @@ var bitsTriggerHelper = {
|
|
|
5297
4587
|
payload,
|
|
5298
4588
|
webhookUrl,
|
|
5299
4589
|
isTest: false,
|
|
5300
|
-
store: triggerStore
|
|
5301
|
-
executor,
|
|
5302
|
-
workflowId,
|
|
5303
|
-
nodeId
|
|
4590
|
+
store: triggerStore
|
|
5304
4591
|
});
|
|
5305
4592
|
if (!runResult.success) {
|
|
5306
|
-
|
|
4593
|
+
logger11.warn(` \u26A0\uFE0F Run failed: ${runResult.message}`);
|
|
5307
4594
|
} else {
|
|
5308
|
-
|
|
4595
|
+
logger11.log(` \u2705 Run completed, items found: ${runResult.output?.length || 0}`);
|
|
5309
4596
|
}
|
|
5310
4597
|
return runResult;
|
|
5311
4598
|
}
|
|
5312
4599
|
case "WEBHOOK" /* WEBHOOK */: {
|
|
5313
|
-
|
|
4600
|
+
logger11.log(`Webhook trigger`);
|
|
5314
4601
|
return await this.executeTrigger({
|
|
5315
4602
|
moduleDefinition,
|
|
5316
4603
|
triggerName,
|
|
@@ -5318,10 +4605,7 @@ var bitsTriggerHelper = {
|
|
|
5318
4605
|
hookType: "RUN" /* RUN */,
|
|
5319
4606
|
payload,
|
|
5320
4607
|
webhookUrl,
|
|
5321
|
-
isTest: false
|
|
5322
|
-
executor,
|
|
5323
|
-
workflowId,
|
|
5324
|
-
nodeId
|
|
4608
|
+
isTest: false
|
|
5325
4609
|
});
|
|
5326
4610
|
}
|
|
5327
4611
|
default: {
|
|
@@ -5341,14 +4625,14 @@ var bitsTriggerHelper = {
|
|
|
5341
4625
|
const triggers = piece.triggers();
|
|
5342
4626
|
for (const [triggerName, trigger] of Object.entries(triggers)) {
|
|
5343
4627
|
const triggerType = mapTriggerType(trigger.type);
|
|
5344
|
-
|
|
4628
|
+
logger11.log(`\u{1F514} Hooking trigger: ${triggerName} (type: ${triggerType})`);
|
|
5345
4629
|
switch (triggerType) {
|
|
5346
4630
|
case "POLLING" /* POLLING */:
|
|
5347
|
-
|
|
4631
|
+
logger11.log(` \u2192 Setting up polling trigger`);
|
|
5348
4632
|
if (options?.onPollingTrigger) {
|
|
5349
4633
|
options.onPollingTrigger(triggerName, trigger);
|
|
5350
4634
|
}
|
|
5351
|
-
|
|
4635
|
+
logger11.log(` \u{1F680} Running polling trigger immediately...`);
|
|
5352
4636
|
try {
|
|
5353
4637
|
const result = await this.executeBitsTrigger({
|
|
5354
4638
|
moduleDefinition,
|
|
@@ -5356,32 +4640,32 @@ var bitsTriggerHelper = {
|
|
|
5356
4640
|
input: options?.input || {}
|
|
5357
4641
|
});
|
|
5358
4642
|
if (result.success) {
|
|
5359
|
-
|
|
5360
|
-
|
|
4643
|
+
logger11.log(` \u2705 Trigger ${triggerName} executed successfully`);
|
|
4644
|
+
logger11.log(` \u{1F4E6} Output items: ${result.output?.length || 0}`);
|
|
5361
4645
|
} else {
|
|
5362
|
-
|
|
4646
|
+
logger11.warn(` \u26A0\uFE0F Trigger ${triggerName} failed: ${result.message}`);
|
|
5363
4647
|
}
|
|
5364
4648
|
if (options?.onTriggerResult) {
|
|
5365
4649
|
options.onTriggerResult(triggerName, result);
|
|
5366
4650
|
}
|
|
5367
4651
|
} catch (error) {
|
|
5368
|
-
|
|
4652
|
+
logger11.error(` \u274C Error running trigger ${triggerName}:`, error.message);
|
|
5369
4653
|
}
|
|
5370
4654
|
break;
|
|
5371
4655
|
case "WEBHOOK" /* WEBHOOK */:
|
|
5372
|
-
|
|
4656
|
+
logger11.log(` \u2192 Setting up webhook trigger`);
|
|
5373
4657
|
if (options?.onWebhookTrigger) {
|
|
5374
4658
|
options.onWebhookTrigger(triggerName, trigger);
|
|
5375
4659
|
}
|
|
5376
4660
|
break;
|
|
5377
4661
|
case "APP_WEBHOOK" /* APP_WEBHOOK */:
|
|
5378
|
-
|
|
4662
|
+
logger11.log(` \u2192 Setting up app webhook trigger`);
|
|
5379
4663
|
if (options?.onAppWebhookTrigger) {
|
|
5380
4664
|
options.onAppWebhookTrigger(triggerName, trigger);
|
|
5381
4665
|
}
|
|
5382
4666
|
break;
|
|
5383
4667
|
default:
|
|
5384
|
-
|
|
4668
|
+
logger11.warn(` \u26A0\uFE0F Unknown trigger type: ${triggerType}`);
|
|
5385
4669
|
}
|
|
5386
4670
|
}
|
|
5387
4671
|
},
|
|
@@ -5419,7 +4703,7 @@ var bitsTriggerHelper = {
|
|
|
5419
4703
|
* Execute a webhook trigger with received payload
|
|
5420
4704
|
*/
|
|
5421
4705
|
async executeWebhookTrigger(nodeId, payload, headers, query) {
|
|
5422
|
-
|
|
4706
|
+
logger11.log(`\u{1F514} Executing webhook trigger for node: ${nodeId}`);
|
|
5423
4707
|
return {
|
|
5424
4708
|
success: true,
|
|
5425
4709
|
output: [
|
|
@@ -5440,10 +4724,10 @@ var bitsTriggerHelper = {
|
|
|
5440
4724
|
import * as fs3 from "fs";
|
|
5441
4725
|
import * as path5 from "path";
|
|
5442
4726
|
import * as vm from "vm";
|
|
5443
|
-
import { spawn
|
|
4727
|
+
import { spawn, execSync } from "child_process";
|
|
5444
4728
|
import * as os from "os";
|
|
5445
4729
|
import * as ts from "typescript";
|
|
5446
|
-
var
|
|
4730
|
+
var logger12 = LoggerFactory.getRoot();
|
|
5447
4731
|
function convertDenoImports(code) {
|
|
5448
4732
|
const npmPackages = [];
|
|
5449
4733
|
const imports = [];
|
|
@@ -5616,7 +4900,7 @@ async function executeJavaScript(code, params, context) {
|
|
|
5616
4900
|
});
|
|
5617
4901
|
return result;
|
|
5618
4902
|
} catch (error) {
|
|
5619
|
-
|
|
4903
|
+
logger12.error(`JavaScript execution error: ${error.message}`);
|
|
5620
4904
|
throw error;
|
|
5621
4905
|
}
|
|
5622
4906
|
}
|
|
@@ -5645,7 +4929,7 @@ if __name__ == "__main__":
|
|
|
5645
4929
|
fs3.writeFileSync(scriptPath, wrappedCode);
|
|
5646
4930
|
return new Promise((resolve4, reject) => {
|
|
5647
4931
|
const pythonCmd = process.platform === "win32" ? "python" : "python3";
|
|
5648
|
-
const proc =
|
|
4932
|
+
const proc = spawn(pythonCmd, [scriptPath], {
|
|
5649
4933
|
env: { ...process.env },
|
|
5650
4934
|
cwd: tmpDir
|
|
5651
4935
|
});
|
|
@@ -5724,8 +5008,8 @@ func main() {
|
|
|
5724
5008
|
fs3.writeFileSync(scriptPath, wrappedCode);
|
|
5725
5009
|
return new Promise((resolve4, reject) => {
|
|
5726
5010
|
try {
|
|
5727
|
-
|
|
5728
|
-
const proc =
|
|
5011
|
+
execSync(`go build -o main`, { cwd: scriptDir, stdio: "pipe" });
|
|
5012
|
+
const proc = spawn("./main", [], {
|
|
5729
5013
|
cwd: scriptDir,
|
|
5730
5014
|
env: { ...process.env }
|
|
5731
5015
|
});
|
|
@@ -5797,7 +5081,7 @@ ${code}
|
|
|
5797
5081
|
`;
|
|
5798
5082
|
fs3.writeFileSync(scriptPath, wrappedCode, { mode: 493 });
|
|
5799
5083
|
return new Promise((resolve4, reject) => {
|
|
5800
|
-
const proc =
|
|
5084
|
+
const proc = spawn("bash", [scriptPath], {
|
|
5801
5085
|
env: { ...process.env },
|
|
5802
5086
|
cwd: tmpDir
|
|
5803
5087
|
});
|
|
@@ -5871,9 +5155,9 @@ async function executeScriptModule(paramsOrModuleName, maybeParams) {
|
|
|
5871
5155
|
executionParams = paramsOrModuleName.params;
|
|
5872
5156
|
inlineScript = paramsOrModuleName.script;
|
|
5873
5157
|
}
|
|
5874
|
-
|
|
5158
|
+
logger12.log(`
|
|
5875
5159
|
\u{1F300} Executing Script module: ${moduleName}`);
|
|
5876
|
-
|
|
5160
|
+
logger12.log(` Source: ${source}`);
|
|
5877
5161
|
let script = null;
|
|
5878
5162
|
if (source === "inline" && inlineScript) {
|
|
5879
5163
|
script = inlineScript;
|
|
@@ -5885,7 +5169,7 @@ async function executeScriptModule(paramsOrModuleName, maybeParams) {
|
|
|
5885
5169
|
if (!script || !script.content) {
|
|
5886
5170
|
throw new Error(`Could not load script: ${moduleName}`);
|
|
5887
5171
|
}
|
|
5888
|
-
|
|
5172
|
+
logger12.log(` Language: ${script.language}`);
|
|
5889
5173
|
const context = {
|
|
5890
5174
|
flow_input: executionParams,
|
|
5891
5175
|
previous_result: executionParams.previous_result || null,
|
|
@@ -5911,7 +5195,7 @@ async function executeScriptModule(paramsOrModuleName, maybeParams) {
|
|
|
5911
5195
|
default:
|
|
5912
5196
|
throw new Error(`Unsupported language: ${script.language}`);
|
|
5913
5197
|
}
|
|
5914
|
-
|
|
5198
|
+
logger12.log(`\u2705 Script executed successfully`);
|
|
5915
5199
|
return {
|
|
5916
5200
|
success: true,
|
|
5917
5201
|
module: moduleName,
|
|
@@ -5925,7 +5209,7 @@ async function executeScriptModule(paramsOrModuleName, maybeParams) {
|
|
|
5925
5209
|
}
|
|
5926
5210
|
};
|
|
5927
5211
|
} catch (error) {
|
|
5928
|
-
|
|
5212
|
+
logger12.error(`\u274C Script failed: ${error.message}`);
|
|
5929
5213
|
return {
|
|
5930
5214
|
success: false,
|
|
5931
5215
|
module: moduleName,
|
|
@@ -5952,7 +5236,7 @@ function getSecurityConfig() {
|
|
|
5952
5236
|
const moderationEnabled = process.env.HABITS_MODERATION_ENABLED === "true";
|
|
5953
5237
|
return { dlpEnabled, dlpIcapUrl, dlpIcapTimeout, piiMode, moderationEnabled };
|
|
5954
5238
|
}
|
|
5955
|
-
async function scanInputForSecurity(data, config,
|
|
5239
|
+
async function scanInputForSecurity(data, config, logger13) {
|
|
5956
5240
|
let processedData = data;
|
|
5957
5241
|
if (!config.dlpEnabled && !config.piiMode && !config.moderationEnabled) {
|
|
5958
5242
|
return processedData;
|
|
@@ -5960,50 +5244,50 @@ async function scanInputForSecurity(data, config, logger15) {
|
|
|
5960
5244
|
try {
|
|
5961
5245
|
const intersect = await import("@codenteam/intersect");
|
|
5962
5246
|
if (config.dlpEnabled) {
|
|
5963
|
-
|
|
5247
|
+
logger13.log("\u{1F510} [Security] Running DLP scan on input data...");
|
|
5964
5248
|
try {
|
|
5965
5249
|
const dlpOptions = {};
|
|
5966
5250
|
if (config.dlpIcapUrl) {
|
|
5967
5251
|
dlpOptions.icapUrl = config.dlpIcapUrl;
|
|
5968
5252
|
dlpOptions.timeout = config.dlpIcapTimeout;
|
|
5969
|
-
|
|
5253
|
+
logger13.log(`\u{1F510} [DLP] Using ICAP server: ${config.dlpIcapUrl}`);
|
|
5970
5254
|
}
|
|
5971
5255
|
const dlpResult = await intersect.dlp.consume(processedData, dlpOptions);
|
|
5972
5256
|
processedData = dlpResult.data ?? processedData;
|
|
5973
5257
|
if (dlpResult.findings && dlpResult.findings.length > 0) {
|
|
5974
|
-
|
|
5258
|
+
logger13.log(`\u{1F510} [DLP] Found ${dlpResult.findings.length} sensitive data instance(s)`);
|
|
5975
5259
|
}
|
|
5976
5260
|
} catch (dlpError) {
|
|
5977
|
-
|
|
5261
|
+
logger13.warn(`\u26A0\uFE0F [DLP] Scan failed: ${dlpError.message}`);
|
|
5978
5262
|
}
|
|
5979
5263
|
}
|
|
5980
5264
|
if (config.piiMode) {
|
|
5981
|
-
|
|
5265
|
+
logger13.log(`\u{1F510} [Security] Running PII scan (mode: ${config.piiMode}) on input data...`);
|
|
5982
5266
|
try {
|
|
5983
5267
|
const piiResult = await intersect.pii.consume(processedData, { mode: config.piiMode });
|
|
5984
5268
|
processedData = piiResult.data ?? processedData;
|
|
5985
5269
|
if (piiResult.detections && piiResult.detections.length > 0) {
|
|
5986
|
-
|
|
5270
|
+
logger13.log(`\u{1F510} [PII] Found ${piiResult.detections.length} PII instance(s) - mode: ${config.piiMode}`);
|
|
5987
5271
|
}
|
|
5988
5272
|
} catch (piiError) {
|
|
5989
|
-
|
|
5273
|
+
logger13.warn(`\u26A0\uFE0F [PII] Scan failed: ${piiError.message}`);
|
|
5990
5274
|
}
|
|
5991
5275
|
}
|
|
5992
5276
|
if (config.moderationEnabled) {
|
|
5993
|
-
|
|
5277
|
+
logger13.log("\u{1F510} [Security] Running moderation scan on input data...");
|
|
5994
5278
|
try {
|
|
5995
5279
|
const modResult = await intersect.moderation.consume(processedData);
|
|
5996
5280
|
if (modResult.flagged) {
|
|
5997
|
-
|
|
5281
|
+
logger13.warn(`\u26A0\uFE0F [Moderation] Content flagged: ${JSON.stringify(modResult.categories)}`);
|
|
5998
5282
|
}
|
|
5999
5283
|
processedData = modResult.data ?? processedData;
|
|
6000
5284
|
} catch (modError) {
|
|
6001
|
-
|
|
5285
|
+
logger13.warn(`\u26A0\uFE0F [Moderation] Scan failed: ${modError.message}`);
|
|
6002
5286
|
}
|
|
6003
5287
|
}
|
|
6004
5288
|
} catch (importError) {
|
|
6005
|
-
|
|
6006
|
-
|
|
5289
|
+
logger13.warn(`\u26A0\uFE0F [Security] @codenteam/intersect package not available: ${importError.message}`);
|
|
5290
|
+
logger13.warn(" Security features (DLP, PII, Moderation) are disabled.");
|
|
6007
5291
|
}
|
|
6008
5292
|
return processedData;
|
|
6009
5293
|
}
|
|
@@ -6016,8 +5300,6 @@ var WorkflowExecutor = class {
|
|
|
6016
5300
|
this.config = null;
|
|
6017
5301
|
this.logger = LoggerFactory.getRoot();
|
|
6018
5302
|
this.env = {};
|
|
6019
|
-
/** Active polling cron jobs - keyed by workflowId:nodeId */
|
|
6020
|
-
this.pollingCronJobs = /* @__PURE__ */ new Map();
|
|
6021
5303
|
}
|
|
6022
5304
|
/**
|
|
6023
5305
|
* Initialize from in-memory data (no file system access needed)
|
|
@@ -6072,7 +5354,7 @@ var WorkflowExecutor = class {
|
|
|
6072
5354
|
this.logger.log(` \u{1F511} Environment variables:`);
|
|
6073
5355
|
for (const key of Object.keys(this.env)) {
|
|
6074
5356
|
if (key.startsWith("HABITS_")) {
|
|
6075
|
-
this.logger.log(` - ${key}: ${this.env[key]
|
|
5357
|
+
this.logger.log(` - ${key}: ${this.env[key]}`);
|
|
6076
5358
|
}
|
|
6077
5359
|
}
|
|
6078
5360
|
this.logger.log(` \u{1F4CB} Resolvable habits params in workflow:`);
|
|
@@ -6082,7 +5364,7 @@ var WorkflowExecutor = class {
|
|
|
6082
5364
|
if (param.startsWith("habits.env.")) {
|
|
6083
5365
|
const envVar = param.slice("habits.env.".length);
|
|
6084
5366
|
const value = this.env[envVar];
|
|
6085
|
-
this.logger.log(` - {{${param}}} \u2192 ${value !== void 0 ?
|
|
5367
|
+
this.logger.log(` - {{${param}}} \u2192 ${value !== void 0 ? value : "(not set)"}`);
|
|
6086
5368
|
} else {
|
|
6087
5369
|
this.logger.log(` - {{${param}}} \u2192 (resolved at runtime)`);
|
|
6088
5370
|
}
|
|
@@ -6152,7 +5434,6 @@ var WorkflowExecutor = class {
|
|
|
6152
5434
|
\u{1F4E6} Module preloading complete
|
|
6153
5435
|
`);
|
|
6154
5436
|
await this.hookPollingTriggers();
|
|
6155
|
-
await this.registerBitsPollingTriggers();
|
|
6156
5437
|
}
|
|
6157
5438
|
/**
|
|
6158
5439
|
* Hook polling triggers for all loaded workflows
|
|
@@ -6215,145 +5496,6 @@ var WorkflowExecutor = class {
|
|
|
6215
5496
|
}
|
|
6216
5497
|
}
|
|
6217
5498
|
}
|
|
6218
|
-
/**
|
|
6219
|
-
* Register bits polling triggers with cron scheduling.
|
|
6220
|
-
* Scans workflows for polling trigger nodes, calls their onEnable() to get schedule,
|
|
6221
|
-
* then creates cron jobs using croner to periodically call run().
|
|
6222
|
-
*/
|
|
6223
|
-
async registerBitsPollingTriggers() {
|
|
6224
|
-
const workflows = this.getAllWorkflows();
|
|
6225
|
-
this.logger.log(`
|
|
6226
|
-
\u23F0 Scanning ${workflows.length} workflow(s) for polling triggers...`);
|
|
6227
|
-
let registeredCount = 0;
|
|
6228
|
-
for (const { reference, workflow } of workflows) {
|
|
6229
|
-
if (reference.enabled === false) continue;
|
|
6230
|
-
const workflowId = reference.id || workflow.id;
|
|
6231
|
-
for (const node of workflow.nodes || []) {
|
|
6232
|
-
const nodeData = node.data;
|
|
6233
|
-
const isTrigger = nodeData?.isTrigger === true || node.type === "trigger";
|
|
6234
|
-
const isBits = nodeData?.framework === "bits";
|
|
6235
|
-
const hasModule = !!nodeData?.module;
|
|
6236
|
-
if (!isTrigger || !isBits || !hasModule) continue;
|
|
6237
|
-
const moduleName = nodeData.module;
|
|
6238
|
-
const triggerName = nodeData.operation || "default";
|
|
6239
|
-
try {
|
|
6240
|
-
let bitPiece = null;
|
|
6241
|
-
const moduleDefinition = {
|
|
6242
|
-
source: nodeData.source || "npm",
|
|
6243
|
-
module: moduleName,
|
|
6244
|
-
framework: "bits",
|
|
6245
|
-
repository: moduleName
|
|
6246
|
-
};
|
|
6247
|
-
try {
|
|
6248
|
-
bitPiece = await pieceFromModule2(moduleDefinition);
|
|
6249
|
-
} catch (loadError) {
|
|
6250
|
-
this.logger.warn(` \u26A0\uFE0F Could not load module ${moduleName}: ${loadError}`);
|
|
6251
|
-
continue;
|
|
6252
|
-
}
|
|
6253
|
-
const triggers = typeof bitPiece.triggers === "function" ? bitPiece.triggers() : bitPiece.triggers;
|
|
6254
|
-
const trigger = triggers?.[triggerName];
|
|
6255
|
-
if (!trigger) {
|
|
6256
|
-
this.logger.warn(` \u26A0\uFE0F Trigger ${triggerName} not found in module ${moduleName}`);
|
|
6257
|
-
continue;
|
|
6258
|
-
}
|
|
6259
|
-
const triggerType = trigger.type?.toUpperCase?.() || trigger.type;
|
|
6260
|
-
if (triggerType !== "POLLING") {
|
|
6261
|
-
continue;
|
|
6262
|
-
}
|
|
6263
|
-
const rawProps = nodeData.params || {};
|
|
6264
|
-
const triggerProps = this.resolveParameters(rawProps, {});
|
|
6265
|
-
this.logger.log(` \u23F0 Enabling polling trigger: ${workflowId}/${node.id} (${moduleName}:${triggerName})`);
|
|
6266
|
-
const result = await bitsTriggerHelper.executeTrigger({
|
|
6267
|
-
moduleDefinition,
|
|
6268
|
-
triggerName,
|
|
6269
|
-
input: triggerProps,
|
|
6270
|
-
hookType: "ON_ENABLE" /* ON_ENABLE */,
|
|
6271
|
-
trigger,
|
|
6272
|
-
workflowId,
|
|
6273
|
-
nodeId: node.id
|
|
6274
|
-
});
|
|
6275
|
-
if (!result.success) {
|
|
6276
|
-
this.logger.warn(` \u26A0\uFE0F Failed to enable ${workflowId}/${node.id}: ${result.message}`);
|
|
6277
|
-
continue;
|
|
6278
|
-
}
|
|
6279
|
-
const scheduleOptions = result.scheduleOptions;
|
|
6280
|
-
if (!scheduleOptions?.cronExpression) {
|
|
6281
|
-
this.logger.warn(` \u26A0\uFE0F No schedule returned from onEnable for ${workflowId}/${node.id}`);
|
|
6282
|
-
continue;
|
|
6283
|
-
}
|
|
6284
|
-
const cronKey = `${workflowId}:${node.id}`;
|
|
6285
|
-
const { cronExpression, timezone = "UTC" } = scheduleOptions;
|
|
6286
|
-
this.logger.log(` \u{1F4C5} Creating cron job: ${cronExpression} (${timezone}) for ${cronKey}`);
|
|
6287
|
-
const cronJob = new Cron(cronExpression, { timezone, name: cronKey }, async () => {
|
|
6288
|
-
const triggeredAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6289
|
-
this.logger.log(` \u23F0 Cron fired: ${cronKey} at ${triggeredAt}`);
|
|
6290
|
-
try {
|
|
6291
|
-
const runResult = await bitsTriggerHelper.executeTrigger({
|
|
6292
|
-
moduleDefinition,
|
|
6293
|
-
triggerName,
|
|
6294
|
-
input: triggerProps,
|
|
6295
|
-
hookType: "RUN" /* RUN */,
|
|
6296
|
-
trigger,
|
|
6297
|
-
workflowId,
|
|
6298
|
-
nodeId: node.id
|
|
6299
|
-
});
|
|
6300
|
-
if (runResult.success && runResult.output && runResult.output.length > 0) {
|
|
6301
|
-
this.logger.log(` \u{1F4E6} Trigger returned ${runResult.output.length} item(s), executing workflow for each...`);
|
|
6302
|
-
const loadedWorkflow = this.getWorkflow(workflowId);
|
|
6303
|
-
if (!loadedWorkflow) {
|
|
6304
|
-
this.logger.error(` \u274C Workflow ${workflowId} not found`);
|
|
6305
|
-
return;
|
|
6306
|
-
}
|
|
6307
|
-
for (let i = 0; i < runResult.output.length; i++) {
|
|
6308
|
-
const item = runResult.output[i];
|
|
6309
|
-
this.logger.log(` \u{1F504} Processing item ${i + 1}/${runResult.output.length}`);
|
|
6310
|
-
try {
|
|
6311
|
-
const execution = await this.executeWorkflow(loadedWorkflow.workflow, {
|
|
6312
|
-
initialContext: {
|
|
6313
|
-
"habits.input": item,
|
|
6314
|
-
// Single item, not array
|
|
6315
|
-
__pollingTrigger: true,
|
|
6316
|
-
__pollingNodeId: node.id,
|
|
6317
|
-
__triggeredAt: triggeredAt,
|
|
6318
|
-
__pollingItemIndex: i
|
|
6319
|
-
},
|
|
6320
|
-
skipTriggerWait: true,
|
|
6321
|
-
triggerNodeId: node.id
|
|
6322
|
-
});
|
|
6323
|
-
this.logger.log(` \u2705 Item ${i + 1} processed: ${execution.status}`);
|
|
6324
|
-
} catch (itemErr) {
|
|
6325
|
-
this.logger.error(` \u274C Item ${i + 1} failed: ${itemErr.message}`);
|
|
6326
|
-
}
|
|
6327
|
-
}
|
|
6328
|
-
this.logger.log(` \u2705 Workflow ${workflowId} completed processing ${runResult.output.length} item(s)`);
|
|
6329
|
-
} else {
|
|
6330
|
-
this.logger.log(` \u23F3 No new items from trigger, skipping workflow execution`);
|
|
6331
|
-
}
|
|
6332
|
-
} catch (err) {
|
|
6333
|
-
this.logger.error(` \u274C Error in polling trigger: ${err.message}`);
|
|
6334
|
-
}
|
|
6335
|
-
});
|
|
6336
|
-
this.pollingCronJobs.set(cronKey, cronJob);
|
|
6337
|
-
registeredCount++;
|
|
6338
|
-
this.logger.log(` \u2705 Registered: ${workflowId}/${node.id} (${triggerName}) -> ${cronExpression}`);
|
|
6339
|
-
} catch (error) {
|
|
6340
|
-
this.logger.error(` \u274C Error enabling polling trigger for ${workflowId}/${node.id}: ${error}`);
|
|
6341
|
-
}
|
|
6342
|
-
}
|
|
6343
|
-
}
|
|
6344
|
-
this.logger.log(`\u23F0 Enabled ${registeredCount} polling trigger(s)
|
|
6345
|
-
`);
|
|
6346
|
-
}
|
|
6347
|
-
/**
|
|
6348
|
-
* Stop all polling cron jobs
|
|
6349
|
-
*/
|
|
6350
|
-
stopPollingTriggers() {
|
|
6351
|
-
for (const [key, cron] of this.pollingCronJobs) {
|
|
6352
|
-
cron.stop();
|
|
6353
|
-
this.logger.log(` \u23F9\uFE0F Stopped polling trigger: ${key}`);
|
|
6354
|
-
}
|
|
6355
|
-
this.pollingCronJobs.clear();
|
|
6356
|
-
}
|
|
6357
5499
|
/**
|
|
6358
5500
|
* Find all template params starting with "habits." in a workflow
|
|
6359
5501
|
*/
|
|
@@ -6463,7 +5605,7 @@ var WorkflowExecutor = class {
|
|
|
6463
5605
|
});
|
|
6464
5606
|
try {
|
|
6465
5607
|
const webhookTriggers = this.findWebhookTriggers(workflow.nodes);
|
|
6466
|
-
if (webhookTriggers.length > 0
|
|
5608
|
+
if (webhookTriggers.length > 0) {
|
|
6467
5609
|
if (!options?.webhookHandler) {
|
|
6468
5610
|
throw new Error(
|
|
6469
5611
|
`Workflow "${workflow.name}" contains ${webhookTriggers.length} webhook trigger(s) but no webhookHandler was provided. Webhook triggers are only supported in Node.js server environments. Provide a webhookHandler via options or remove webhook triggers from the workflow.`
|
|
@@ -6475,9 +5617,6 @@ var WorkflowExecutor = class {
|
|
|
6475
5617
|
this.logger.log(` - Node: ${trigger.nodeId}`);
|
|
6476
5618
|
}
|
|
6477
5619
|
this.logger.log("\n\u23F3 Waiting for webhook(s) to be triggered...\n");
|
|
6478
|
-
} else if (webhookTriggers.length > 0 && options?.skipTriggerWait) {
|
|
6479
|
-
this.logger.log(`
|
|
6480
|
-
\u{1F4CB} Detected ${webhookTriggers.length} webhook trigger(s) - using pre-provided payload`);
|
|
6481
5620
|
}
|
|
6482
5621
|
const dependencies = this.buildDependencyMap(workflow.nodes, workflow?.edges || []);
|
|
6483
5622
|
execution.nodeStatuses = workflow.nodes.map((node) => ({
|
|
@@ -6485,9 +5624,6 @@ var WorkflowExecutor = class {
|
|
|
6485
5624
|
status: "pending"
|
|
6486
5625
|
}));
|
|
6487
5626
|
let context = options?.initialContext ? { ...options.initialContext } : {};
|
|
6488
|
-
if (options?.oauthTokens) {
|
|
6489
|
-
context._oauthTokens = options.oauthTokens;
|
|
6490
|
-
}
|
|
6491
5627
|
if (!context.habits) {
|
|
6492
5628
|
context.habits = {};
|
|
6493
5629
|
}
|
|
@@ -6556,76 +5692,31 @@ var WorkflowExecutor = class {
|
|
|
6556
5692
|
status: "running"
|
|
6557
5693
|
});
|
|
6558
5694
|
try {
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
const pollingData = context["habits.input"];
|
|
6563
|
-
const scannedResult = await scanInputForSecurity(pollingData, securityConfig, this.logger);
|
|
5695
|
+
if (this.isWebhookTriggerNode(node)) {
|
|
5696
|
+
const webhookResult = await this.handleWebhookTrigger(node, context, execution, options.webhookHandler, options?.webhookTimeout);
|
|
5697
|
+
const scannedResult = await scanInputForSecurity(webhookResult.result, securityConfig, this.logger);
|
|
6564
5698
|
context[`${nodeId}`] = scannedResult;
|
|
6565
5699
|
context[nodeId] = scannedResult;
|
|
6566
5700
|
context.previous_result = scannedResult;
|
|
6567
|
-
|
|
5701
|
+
context.webhookPayload = scannedResult;
|
|
5702
|
+
this.updateNodeStatus(execution, nodeId, webhookResult.success ? "completed" : "failed", {
|
|
6568
5703
|
result: scannedResult,
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
5704
|
+
error: webhookResult.error,
|
|
5705
|
+
startTime: new Date(webhookResult.timestamp.getTime() - webhookResult.duration),
|
|
5706
|
+
endTime: webhookResult.timestamp,
|
|
5707
|
+
duration: webhookResult.duration
|
|
6572
5708
|
});
|
|
6573
5709
|
emitStreamEvent({
|
|
6574
|
-
type: "node_completed",
|
|
5710
|
+
type: webhookResult.success ? "node_completed" : "node_failed",
|
|
6575
5711
|
nodeId,
|
|
6576
5712
|
nodeName: node.data.label,
|
|
6577
|
-
status: "completed",
|
|
5713
|
+
status: webhookResult.success ? "completed" : "failed",
|
|
6578
5714
|
result: scannedResult,
|
|
6579
|
-
|
|
5715
|
+
error: webhookResult.error,
|
|
5716
|
+
duration: webhookResult.duration
|
|
6580
5717
|
});
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
if (skipWait && context.webhookPayload) {
|
|
6584
|
-
this.logger.log(`\u{1F514} Using pre-provided webhook payload for trigger node: ${nodeId}`);
|
|
6585
|
-
const scannedResult = await scanInputForSecurity(context.webhookPayload, securityConfig, this.logger);
|
|
6586
|
-
context[`${nodeId}`] = scannedResult;
|
|
6587
|
-
context[nodeId] = scannedResult;
|
|
6588
|
-
context.previous_result = scannedResult;
|
|
6589
|
-
this.updateNodeStatus(execution, nodeId, "completed", {
|
|
6590
|
-
result: scannedResult,
|
|
6591
|
-
startTime: /* @__PURE__ */ new Date(),
|
|
6592
|
-
endTime: /* @__PURE__ */ new Date(),
|
|
6593
|
-
duration: 0
|
|
6594
|
-
});
|
|
6595
|
-
emitStreamEvent({
|
|
6596
|
-
type: "node_completed",
|
|
6597
|
-
nodeId,
|
|
6598
|
-
nodeName: node.data.label,
|
|
6599
|
-
status: "completed",
|
|
6600
|
-
result: scannedResult,
|
|
6601
|
-
duration: 0
|
|
6602
|
-
});
|
|
6603
|
-
} else {
|
|
6604
|
-
const webhookResult = await this.handleWebhookTrigger(node, context, execution, options.webhookHandler, options?.webhookTimeout);
|
|
6605
|
-
const scannedResult = await scanInputForSecurity(webhookResult.result, securityConfig, this.logger);
|
|
6606
|
-
context[`${nodeId}`] = scannedResult;
|
|
6607
|
-
context[nodeId] = scannedResult;
|
|
6608
|
-
context.previous_result = scannedResult;
|
|
6609
|
-
context.webhookPayload = scannedResult;
|
|
6610
|
-
this.updateNodeStatus(execution, nodeId, webhookResult.success ? "completed" : "failed", {
|
|
6611
|
-
result: scannedResult,
|
|
6612
|
-
error: webhookResult.error,
|
|
6613
|
-
startTime: new Date(webhookResult.timestamp.getTime() - webhookResult.duration),
|
|
6614
|
-
endTime: webhookResult.timestamp,
|
|
6615
|
-
duration: webhookResult.duration
|
|
6616
|
-
});
|
|
6617
|
-
emitStreamEvent({
|
|
6618
|
-
type: webhookResult.success ? "node_completed" : "node_failed",
|
|
6619
|
-
nodeId,
|
|
6620
|
-
nodeName: node.data.label,
|
|
6621
|
-
status: webhookResult.success ? "completed" : "failed",
|
|
6622
|
-
result: scannedResult,
|
|
6623
|
-
error: webhookResult.error,
|
|
6624
|
-
duration: webhookResult.duration
|
|
6625
|
-
});
|
|
6626
|
-
if (!webhookResult.success) {
|
|
6627
|
-
this.logger.error(`\u274C Webhook trigger ${nodeId} failed: ${webhookResult.error}`);
|
|
6628
|
-
}
|
|
5718
|
+
if (!webhookResult.success) {
|
|
5719
|
+
this.logger.error(`\u274C Webhook trigger ${nodeId} failed: ${webhookResult.error}`);
|
|
6629
5720
|
}
|
|
6630
5721
|
} else {
|
|
6631
5722
|
const nodeResult = await this.executeNode(node, context, execution);
|
|
@@ -6997,10 +6088,7 @@ var WorkflowExecutor = class {
|
|
|
6997
6088
|
triggerName: node.data.operation || "unknown",
|
|
6998
6089
|
input: fullParams,
|
|
6999
6090
|
payload: context.triggerPayload || context.webhookPayload || {},
|
|
7000
|
-
webhookUrl: context.webhookUrl
|
|
7001
|
-
executor: this,
|
|
7002
|
-
workflowId: execution.workflowId,
|
|
7003
|
-
nodeId: node.id
|
|
6091
|
+
webhookUrl: context.webhookUrl
|
|
7004
6092
|
});
|
|
7005
6093
|
if (!triggerResult.success) {
|
|
7006
6094
|
throw new Error(triggerResult.message || "Trigger execution failed");
|
|
@@ -7015,10 +6103,7 @@ var WorkflowExecutor = class {
|
|
|
7015
6103
|
logger: this.logger,
|
|
7016
6104
|
workflowId: execution.workflowId,
|
|
7017
6105
|
nodeId: node.id,
|
|
7018
|
-
executionId: execution.id
|
|
7019
|
-
executor: this,
|
|
7020
|
-
// Pass per-user OAuth tokens from context (for multi-user server mode)
|
|
7021
|
-
oauthTokens: context._oauthTokens
|
|
6106
|
+
executionId: execution.id
|
|
7022
6107
|
});
|
|
7023
6108
|
nodeResult = output.result;
|
|
7024
6109
|
}
|
|
@@ -7095,34 +6180,9 @@ var WorkflowExecutor = class {
|
|
|
7095
6180
|
}
|
|
7096
6181
|
/**
|
|
7097
6182
|
* Evaluate a JavaScript expression in the given context
|
|
7098
|
-
* Supports default values with || operator: {{habits.input.value || 'default'}}
|
|
7099
6183
|
*/
|
|
7100
6184
|
evaluateExpression(expression, context) {
|
|
7101
6185
|
try {
|
|
7102
|
-
const defaultMatch = expression.match(/^(.+?)\s*\|\|\s*(['"`])(.*)(\2)$/);
|
|
7103
|
-
if (defaultMatch) {
|
|
7104
|
-
const [, mainExpr, , defaultValue] = defaultMatch;
|
|
7105
|
-
const result = this.evaluateExpression(mainExpr.trim(), context);
|
|
7106
|
-
if (result === void 0 || result === null || result === "" || result === mainExpr.trim()) {
|
|
7107
|
-
return defaultValue;
|
|
7108
|
-
}
|
|
7109
|
-
return result;
|
|
7110
|
-
}
|
|
7111
|
-
const defaultUnquotedMatch = expression.match(/^(.+?)\s*\|\|\s*([^'"`].*)$/);
|
|
7112
|
-
if (defaultUnquotedMatch) {
|
|
7113
|
-
const [, mainExpr, defaultValue] = defaultUnquotedMatch;
|
|
7114
|
-
const result = this.evaluateExpression(mainExpr.trim(), context);
|
|
7115
|
-
if (result === void 0 || result === null || result === "" || result === mainExpr.trim()) {
|
|
7116
|
-
const trimmedDefault = defaultValue.trim();
|
|
7117
|
-
if (trimmedDefault === "true") return true;
|
|
7118
|
-
if (trimmedDefault === "false") return false;
|
|
7119
|
-
if (trimmedDefault === "null") return null;
|
|
7120
|
-
const num = Number(trimmedDefault);
|
|
7121
|
-
if (!isNaN(num)) return num;
|
|
7122
|
-
return trimmedDefault;
|
|
7123
|
-
}
|
|
7124
|
-
return result;
|
|
7125
|
-
}
|
|
7126
6186
|
if (expression.startsWith("habits.env.")) {
|
|
7127
6187
|
const envVar = expression.slice("habits.env.".length);
|
|
7128
6188
|
const value = this.env[envVar] ?? (typeof process !== "undefined" ? process.env?.[envVar] : void 0);
|
|
@@ -7457,6 +6517,7 @@ function assertNotNullOrUndefined(value, fieldName) {
|
|
|
7457
6517
|
}
|
|
7458
6518
|
|
|
7459
6519
|
// packages/cortex/core/src/bits/framework.ts
|
|
6520
|
+
import axios4 from "axios";
|
|
7460
6521
|
var AuthenticationType = /* @__PURE__ */ ((AuthenticationType2) => {
|
|
7461
6522
|
AuthenticationType2["BEARER_TOKEN"] = "BEARER_TOKEN";
|
|
7462
6523
|
AuthenticationType2["BASIC"] = "BASIC";
|
|
@@ -7477,42 +6538,42 @@ var HttpMethod = /* @__PURE__ */ ((HttpMethod2) => {
|
|
|
7477
6538
|
})(HttpMethod || {});
|
|
7478
6539
|
var httpClient = {
|
|
7479
6540
|
async sendRequest(request) {
|
|
7480
|
-
const
|
|
6541
|
+
const config = {
|
|
6542
|
+
url: request.url,
|
|
6543
|
+
method: request.method,
|
|
6544
|
+
headers: { ...request.headers },
|
|
6545
|
+
data: request.body,
|
|
6546
|
+
params: request.queryParams,
|
|
6547
|
+
timeout: request.timeout || 3e4
|
|
6548
|
+
};
|
|
7481
6549
|
if (request.authentication) {
|
|
7482
6550
|
switch (request.authentication.type) {
|
|
7483
6551
|
case "BEARER_TOKEN" /* BEARER_TOKEN */:
|
|
7484
|
-
headers
|
|
6552
|
+
config.headers = {
|
|
6553
|
+
...config.headers,
|
|
6554
|
+
Authorization: `Bearer ${request.authentication.token}`
|
|
6555
|
+
};
|
|
7485
6556
|
break;
|
|
7486
6557
|
case "BASIC" /* BASIC */:
|
|
7487
|
-
|
|
7488
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
6558
|
+
config.auth = {
|
|
6559
|
+
username: request.authentication.username || "",
|
|
6560
|
+
password: request.authentication.password || ""
|
|
6561
|
+
};
|
|
7491
6562
|
break;
|
|
7492
6563
|
case "API_KEY" /* API_KEY */:
|
|
7493
6564
|
const headerName = request.authentication.headerName || "X-API-Key";
|
|
7494
|
-
headers
|
|
6565
|
+
config.headers = {
|
|
6566
|
+
...config.headers,
|
|
6567
|
+
[headerName]: request.authentication.apiKey || ""
|
|
6568
|
+
};
|
|
7495
6569
|
break;
|
|
7496
6570
|
}
|
|
7497
6571
|
}
|
|
7498
|
-
|
|
7499
|
-
if (request.queryParams && Object.keys(request.queryParams).length > 0) {
|
|
7500
|
-
const params = new URLSearchParams(request.queryParams);
|
|
7501
|
-
url += (url.includes("?") ? "&" : "?") + params.toString();
|
|
7502
|
-
}
|
|
7503
|
-
const response = await fetch2(url, {
|
|
7504
|
-
method: request.method,
|
|
7505
|
-
headers,
|
|
7506
|
-
body: request.body ? JSON.stringify(request.body) : void 0
|
|
7507
|
-
});
|
|
7508
|
-
const responseHeaders = {};
|
|
7509
|
-
response.headers.forEach((value, key) => {
|
|
7510
|
-
responseHeaders[key] = value;
|
|
7511
|
-
});
|
|
6572
|
+
const response = await axios4(config);
|
|
7512
6573
|
return {
|
|
7513
6574
|
status: response.status,
|
|
7514
|
-
headers:
|
|
7515
|
-
body:
|
|
6575
|
+
headers: response.headers,
|
|
6576
|
+
body: response.data
|
|
7516
6577
|
};
|
|
7517
6578
|
}
|
|
7518
6579
|
};
|
|
@@ -7662,24 +6723,13 @@ var BitAuth = {
|
|
|
7662
6723
|
},
|
|
7663
6724
|
OAuth2(config) {
|
|
7664
6725
|
return {
|
|
7665
|
-
type: "
|
|
7666
|
-
displayName:
|
|
6726
|
+
type: "CUSTOM_AUTH",
|
|
6727
|
+
displayName: "OAuth2",
|
|
7667
6728
|
description: config.description,
|
|
7668
6729
|
required: config.required,
|
|
7669
|
-
|
|
7670
|
-
tokenUrl: config.tokenUrl,
|
|
7671
|
-
clientId: config.clientId,
|
|
7672
|
-
clientSecret: config.clientSecret,
|
|
7673
|
-
scopes: config.scopes,
|
|
7674
|
-
pkce: config.pkce ?? true,
|
|
7675
|
-
// PKCE enabled by default
|
|
7676
|
-
extraAuthParams: config.extraAuthParams
|
|
6730
|
+
...config
|
|
7677
6731
|
};
|
|
7678
6732
|
},
|
|
7679
|
-
/** @deprecated Use OAuth2() instead */
|
|
7680
|
-
OAuth2PKCE(config) {
|
|
7681
|
-
return BitAuth.OAuth2({ ...config, pkce: true });
|
|
7682
|
-
},
|
|
7683
6733
|
BasicAuth(config) {
|
|
7684
6734
|
return {
|
|
7685
6735
|
type: "CUSTOM_AUTH",
|
|
@@ -7721,7 +6771,6 @@ function createTrigger(config) {
|
|
|
7721
6771
|
run: config.run,
|
|
7722
6772
|
test: config.test,
|
|
7723
6773
|
onHandshake: config.onHandshake,
|
|
7724
|
-
filter: config.filter,
|
|
7725
6774
|
sampleData: config.sampleData
|
|
7726
6775
|
};
|
|
7727
6776
|
}
|
|
@@ -7768,7 +6817,6 @@ function createBit(config) {
|
|
|
7768
6817
|
}
|
|
7769
6818
|
}
|
|
7770
6819
|
return {
|
|
7771
|
-
id: config.id,
|
|
7772
6820
|
displayName: config.displayName,
|
|
7773
6821
|
description: config.description,
|
|
7774
6822
|
logoUrl: config.logoUrl,
|
|
@@ -7844,530 +6892,14 @@ function createCustomApiCallAction(config) {
|
|
|
7844
6892
|
}
|
|
7845
6893
|
});
|
|
7846
6894
|
}
|
|
7847
|
-
|
|
7848
|
-
// packages/cortex/core/src/bits/oauthDiscovery.ts
|
|
7849
|
-
var logger14 = LoggerFactory.create(void 0, void 0, { bitName: "OAuthDiscovery" });
|
|
7850
|
-
function resolveEnvExpression(value) {
|
|
7851
|
-
if (typeof value !== "string") return value;
|
|
7852
|
-
const envPattern = /\{\{habits\.env\.([A-Za-z_][A-Za-z0-9_]*)\}\}/g;
|
|
7853
|
-
return value.replace(envPattern, (match, envVar) => {
|
|
7854
|
-
return process.env[envVar] || "";
|
|
7855
|
-
});
|
|
7856
|
-
}
|
|
7857
|
-
function extractAuthCredentials(nodeAuth) {
|
|
7858
|
-
if (!nodeAuth) return {};
|
|
7859
|
-
let clientId = nodeAuth.clientId || nodeAuth.CLIENT_ID || nodeAuth.client_id;
|
|
7860
|
-
let clientSecret = nodeAuth.clientSecret || nodeAuth.CLIENT_SECRET || nodeAuth.client_secret;
|
|
7861
|
-
if (clientId) clientId = resolveEnvExpression(clientId);
|
|
7862
|
-
if (clientSecret) clientSecret = resolveEnvExpression(clientSecret);
|
|
7863
|
-
return { clientId, clientSecret };
|
|
7864
|
-
}
|
|
7865
|
-
function extractBitId(moduleName) {
|
|
7866
|
-
const parts = moduleName.split("/");
|
|
7867
|
-
return parts[parts.length - 1];
|
|
7868
|
-
}
|
|
7869
|
-
async function getAuthFromModule(moduleDefinition) {
|
|
7870
|
-
const moduleName = getModuleName(moduleDefinition);
|
|
7871
|
-
try {
|
|
7872
|
-
if (isBundledModule(moduleDefinition.repository)) {
|
|
7873
|
-
const loadedModule = getBundledModule(moduleDefinition.repository);
|
|
7874
|
-
if (loadedModule) {
|
|
7875
|
-
const piece = extractBitsPieceFromModule(loadedModule);
|
|
7876
|
-
return { auth: piece.auth, displayName: piece.displayName };
|
|
7877
|
-
}
|
|
7878
|
-
return null;
|
|
7879
|
-
}
|
|
7880
|
-
const mainFilePath = getModuleMainFile(moduleDefinition);
|
|
7881
|
-
if (!mainFilePath) {
|
|
7882
|
-
return null;
|
|
7883
|
-
}
|
|
7884
|
-
const originalCwd = process.cwd();
|
|
7885
|
-
const moduleDir = dirname(mainFilePath);
|
|
7886
|
-
let nodeModulesDir = moduleDir;
|
|
7887
|
-
while (nodeModulesDir && !nodeModulesDir.endsWith("/node_modules") && nodeModulesDir !== dirname(nodeModulesDir)) {
|
|
7888
|
-
nodeModulesDir = dirname(nodeModulesDir);
|
|
7889
|
-
}
|
|
7890
|
-
try {
|
|
7891
|
-
process.chdir(moduleDir);
|
|
7892
|
-
const loadedModule = simpleRequire(mainFilePath, nodeModulesDir);
|
|
7893
|
-
const piece = extractBitsPieceFromModule(loadedModule);
|
|
7894
|
-
process.chdir(originalCwd);
|
|
7895
|
-
return { auth: piece.auth, displayName: piece.displayName };
|
|
7896
|
-
} catch (error) {
|
|
7897
|
-
process.chdir(originalCwd);
|
|
7898
|
-
throw error;
|
|
7899
|
-
}
|
|
7900
|
-
} catch (error) {
|
|
7901
|
-
logger14.warn("Failed to load auth from module", { moduleName, error: String(error) });
|
|
7902
|
-
return null;
|
|
7903
|
-
}
|
|
7904
|
-
}
|
|
7905
|
-
function isOAuth2(auth) {
|
|
7906
|
-
return auth && (auth.type === "OAUTH2" || auth.type === "OAUTH2_PKCE");
|
|
7907
|
-
}
|
|
7908
|
-
async function discoverOAuthRequirements(workflows) {
|
|
7909
|
-
const requirements = [];
|
|
7910
|
-
const processedModules = /* @__PURE__ */ new Set();
|
|
7911
|
-
for (const [workflowId, workflow] of workflows) {
|
|
7912
|
-
for (const node of workflow.nodes) {
|
|
7913
|
-
const nodeData = node.data;
|
|
7914
|
-
if (nodeData?.framework !== "bits") {
|
|
7915
|
-
continue;
|
|
7916
|
-
}
|
|
7917
|
-
const moduleName = nodeData.module;
|
|
7918
|
-
if (!moduleName || processedModules.has(moduleName)) {
|
|
7919
|
-
continue;
|
|
7920
|
-
}
|
|
7921
|
-
processedModules.add(moduleName);
|
|
7922
|
-
const moduleDefinition = {
|
|
7923
|
-
repository: moduleName,
|
|
7924
|
-
source: "npm",
|
|
7925
|
-
framework: "bits"
|
|
7926
|
-
};
|
|
7927
|
-
const moduleAuth = await getAuthFromModule(moduleDefinition);
|
|
7928
|
-
if (!moduleAuth || !isOAuth2(moduleAuth.auth)) {
|
|
7929
|
-
continue;
|
|
7930
|
-
}
|
|
7931
|
-
const bitId = extractBitId(moduleName);
|
|
7932
|
-
const hasValidToken = oauthTokenStore.hasValidToken(bitId);
|
|
7933
|
-
const isExpired = oauthTokenStore.isExpired(bitId);
|
|
7934
|
-
let status;
|
|
7935
|
-
if (hasValidToken && !isExpired) {
|
|
7936
|
-
status = "valid";
|
|
7937
|
-
} else if (hasValidToken && isExpired) {
|
|
7938
|
-
status = "expired";
|
|
7939
|
-
} else {
|
|
7940
|
-
status = "needed";
|
|
7941
|
-
}
|
|
7942
|
-
const nodeAuthCredentials = extractAuthCredentials(nodeData.auth);
|
|
7943
|
-
const clientId = nodeAuthCredentials.clientId || moduleAuth.auth.clientId;
|
|
7944
|
-
const clientSecret = nodeAuthCredentials.clientSecret || moduleAuth.auth.clientSecret;
|
|
7945
|
-
requirements.push({
|
|
7946
|
-
bitId,
|
|
7947
|
-
moduleName,
|
|
7948
|
-
displayName: moduleAuth.displayName,
|
|
7949
|
-
config: {
|
|
7950
|
-
displayName: moduleAuth.auth.displayName,
|
|
7951
|
-
description: moduleAuth.auth.description,
|
|
7952
|
-
required: moduleAuth.auth.required,
|
|
7953
|
-
authorizationUrl: moduleAuth.auth.authorizationUrl,
|
|
7954
|
-
tokenUrl: moduleAuth.auth.tokenUrl,
|
|
7955
|
-
clientId,
|
|
7956
|
-
clientSecret,
|
|
7957
|
-
scopes: moduleAuth.auth.scopes,
|
|
7958
|
-
extraAuthParams: moduleAuth.auth.extraAuthParams
|
|
7959
|
-
},
|
|
7960
|
-
hasValidToken,
|
|
7961
|
-
status
|
|
7962
|
-
});
|
|
7963
|
-
}
|
|
7964
|
-
}
|
|
7965
|
-
return requirements;
|
|
7966
|
-
}
|
|
7967
|
-
async function printOAuthRequirements(requirements, getAuthUrl) {
|
|
7968
|
-
const neededRequirements = requirements.filter((r) => r.status !== "valid");
|
|
7969
|
-
if (neededRequirements.length === 0) {
|
|
7970
|
-
logger14.info("All OAuth2 tokens are valid");
|
|
7971
|
-
return;
|
|
7972
|
-
}
|
|
7973
|
-
console.log("\n" + "=".repeat(60));
|
|
7974
|
-
console.log("\u{1F510} OAuth2 Authorization Required");
|
|
7975
|
-
console.log("=".repeat(60));
|
|
7976
|
-
for (const req of neededRequirements) {
|
|
7977
|
-
const statusIcon = req.status === "expired" ? "\u26A0\uFE0F (expired)" : "\u274C (missing)";
|
|
7978
|
-
console.log(`
|
|
7979
|
-
\u{1F4E6} ${req.displayName} ${statusIcon}`);
|
|
7980
|
-
console.log(` Module: ${req.moduleName}`);
|
|
7981
|
-
console.log(` Scopes: ${req.config.scopes.join(", ")}`);
|
|
7982
|
-
const authUrl = await getAuthUrl(req);
|
|
7983
|
-
console.log(` \u279C Visit: ${authUrl}`);
|
|
7984
|
-
}
|
|
7985
|
-
console.log("\n" + "=".repeat(60));
|
|
7986
|
-
console.log("Open the URLs above in your browser to authorize access.");
|
|
7987
|
-
console.log("After authorization, the tokens will be stored automatically.");
|
|
7988
|
-
console.log("=".repeat(60) + "\n");
|
|
7989
|
-
}
|
|
7990
|
-
|
|
7991
|
-
// packages/cortex/core/src/bits/OAuthFlowManager.ts
|
|
7992
|
-
var OAuthFlowManager = class {
|
|
7993
|
-
constructor(options) {
|
|
7994
|
-
this.pendingFlows = /* @__PURE__ */ new Map();
|
|
7995
|
-
this.callbackBaseUrl = options.callbackBaseUrl.replace(/\/$/, "");
|
|
7996
|
-
this.logger = options.logger ?? LoggerFactory.create(void 0, void 0, { bitName: "OAuthFlowManager" });
|
|
7997
|
-
}
|
|
7998
|
-
/**
|
|
7999
|
-
* Generate a cryptographically secure random string for PKCE code verifier.
|
|
8000
|
-
* RFC 7636 requires 43-128 characters, URL-safe.
|
|
8001
|
-
*
|
|
8002
|
-
* Uses Web Crypto API for platform compatibility.
|
|
8003
|
-
*/
|
|
8004
|
-
generateCodeVerifier() {
|
|
8005
|
-
const randomBytes = new Uint8Array(32);
|
|
8006
|
-
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
8007
|
-
crypto.getRandomValues(randomBytes);
|
|
8008
|
-
} else {
|
|
8009
|
-
for (let i = 0; i < 32; i++) {
|
|
8010
|
-
randomBytes[i] = Math.floor(Math.random() * 256);
|
|
8011
|
-
}
|
|
8012
|
-
}
|
|
8013
|
-
return this.base64UrlEncode(randomBytes).slice(0, 43);
|
|
8014
|
-
}
|
|
8015
|
-
/**
|
|
8016
|
-
* Generate code challenge from verifier using SHA256.
|
|
8017
|
-
* RFC 7636 S256 method.
|
|
8018
|
-
*/
|
|
8019
|
-
async generateCodeChallenge(codeVerifier) {
|
|
8020
|
-
const encoder = new TextEncoder();
|
|
8021
|
-
const data = encoder.encode(codeVerifier);
|
|
8022
|
-
if (typeof crypto !== "undefined" && crypto.subtle) {
|
|
8023
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
8024
|
-
return this.base64UrlEncode(new Uint8Array(hashBuffer));
|
|
8025
|
-
}
|
|
8026
|
-
try {
|
|
8027
|
-
const nodeCrypto = await import("crypto");
|
|
8028
|
-
const hash = nodeCrypto.createHash("sha256").update(codeVerifier).digest();
|
|
8029
|
-
return this.base64UrlEncode(new Uint8Array(hash));
|
|
8030
|
-
} catch {
|
|
8031
|
-
throw new Error("No crypto implementation available for PKCE code challenge");
|
|
8032
|
-
}
|
|
8033
|
-
}
|
|
8034
|
-
/**
|
|
8035
|
-
* Generate a random state parameter for CSRF protection.
|
|
8036
|
-
*/
|
|
8037
|
-
generateState() {
|
|
8038
|
-
const randomBytes = new Uint8Array(16);
|
|
8039
|
-
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
8040
|
-
crypto.getRandomValues(randomBytes);
|
|
8041
|
-
} else {
|
|
8042
|
-
for (let i = 0; i < 16; i++) {
|
|
8043
|
-
randomBytes[i] = Math.floor(Math.random() * 256);
|
|
8044
|
-
}
|
|
8045
|
-
}
|
|
8046
|
-
return Array.from(randomBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
8047
|
-
}
|
|
8048
|
-
/**
|
|
8049
|
-
* Base64URL encode a Uint8Array (RFC 4648).
|
|
8050
|
-
*/
|
|
8051
|
-
base64UrlEncode(data) {
|
|
8052
|
-
let binary = "";
|
|
8053
|
-
for (let i = 0; i < data.length; i++) {
|
|
8054
|
-
binary += String.fromCharCode(data[i]);
|
|
8055
|
-
}
|
|
8056
|
-
const base64 = btoa(binary);
|
|
8057
|
-
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
8058
|
-
}
|
|
8059
|
-
/**
|
|
8060
|
-
* Initiate an OAuth2 flow for a bit.
|
|
8061
|
-
*
|
|
8062
|
-
* @param bitId - Unique identifier for the bit (e.g., "bit-google-drive")
|
|
8063
|
-
* @param config - OAuth2 configuration
|
|
8064
|
-
* @returns Authorization URL and flow info
|
|
8065
|
-
*/
|
|
8066
|
-
async initiateFlow(bitId, config) {
|
|
8067
|
-
const state = this.generateState();
|
|
8068
|
-
const redirectUri = `${this.callbackBaseUrl}/${bitId}/callback`;
|
|
8069
|
-
const usePkce = config.pkce !== false;
|
|
8070
|
-
const codeVerifier = usePkce ? this.generateCodeVerifier() : void 0;
|
|
8071
|
-
const codeChallenge = usePkce && codeVerifier ? await this.generateCodeChallenge(codeVerifier) : void 0;
|
|
8072
|
-
const oauthState = {
|
|
8073
|
-
codeVerifier,
|
|
8074
|
-
codeChallenge,
|
|
8075
|
-
state,
|
|
8076
|
-
redirectUri,
|
|
8077
|
-
bitId,
|
|
8078
|
-
createdAt: Date.now(),
|
|
8079
|
-
config
|
|
8080
|
-
};
|
|
8081
|
-
this.pendingFlows.set(state, oauthState);
|
|
8082
|
-
this.logger.info("OAuth flow initiated", { bitId, redirectUri, pkce: usePkce });
|
|
8083
|
-
const params = new URLSearchParams({
|
|
8084
|
-
response_type: "code",
|
|
8085
|
-
client_id: config.clientId,
|
|
8086
|
-
redirect_uri: redirectUri,
|
|
8087
|
-
scope: config.scopes.join(" "),
|
|
8088
|
-
state
|
|
8089
|
-
});
|
|
8090
|
-
if (usePkce && codeChallenge) {
|
|
8091
|
-
params.set("code_challenge", codeChallenge);
|
|
8092
|
-
params.set("code_challenge_method", "S256");
|
|
8093
|
-
}
|
|
8094
|
-
if (config.extraAuthParams) {
|
|
8095
|
-
for (const [key, value] of Object.entries(config.extraAuthParams)) {
|
|
8096
|
-
params.set(key, value);
|
|
8097
|
-
}
|
|
8098
|
-
}
|
|
8099
|
-
const authUrl = `${config.authorizationUrl}?${params.toString()}`;
|
|
8100
|
-
return { authUrl, state, redirectUri };
|
|
8101
|
-
}
|
|
8102
|
-
/**
|
|
8103
|
-
* Parse an OAuth callback URL to extract parameters.
|
|
8104
|
-
* Supports both query parameters (?code=...) and hash fragments (#access_token=...).
|
|
8105
|
-
*
|
|
8106
|
-
* @param callbackUrl - Full callback URL received from OAuth provider
|
|
8107
|
-
* @returns Parsed callback parameters
|
|
8108
|
-
*/
|
|
8109
|
-
parseCallbackUrl(callbackUrl) {
|
|
8110
|
-
const result = {};
|
|
8111
|
-
try {
|
|
8112
|
-
const url = new URL(callbackUrl);
|
|
8113
|
-
const queryParams = url.searchParams;
|
|
8114
|
-
if (queryParams.has("code")) {
|
|
8115
|
-
result.code = queryParams.get("code") || void 0;
|
|
8116
|
-
}
|
|
8117
|
-
if (queryParams.has("state")) {
|
|
8118
|
-
result.state = queryParams.get("state") || void 0;
|
|
8119
|
-
}
|
|
8120
|
-
if (queryParams.has("error")) {
|
|
8121
|
-
result.error = queryParams.get("error") || void 0;
|
|
8122
|
-
result.errorDescription = queryParams.get("error_description") || void 0;
|
|
8123
|
-
}
|
|
8124
|
-
if (url.hash) {
|
|
8125
|
-
const hashParams = new URLSearchParams(url.hash.slice(1));
|
|
8126
|
-
if (hashParams.has("access_token")) {
|
|
8127
|
-
result.accessToken = hashParams.get("access_token") || void 0;
|
|
8128
|
-
result.tokenType = hashParams.get("token_type") || "Bearer";
|
|
8129
|
-
const expiresIn = hashParams.get("expires_in");
|
|
8130
|
-
if (expiresIn) {
|
|
8131
|
-
result.expiresIn = parseInt(expiresIn, 10);
|
|
8132
|
-
}
|
|
8133
|
-
result.refreshToken = hashParams.get("refresh_token") || void 0;
|
|
8134
|
-
}
|
|
8135
|
-
if (hashParams.has("state") && !result.state) {
|
|
8136
|
-
result.state = hashParams.get("state") || void 0;
|
|
8137
|
-
}
|
|
8138
|
-
if (hashParams.has("error") && !result.error) {
|
|
8139
|
-
result.error = hashParams.get("error") || void 0;
|
|
8140
|
-
result.errorDescription = hashParams.get("error_description") || void 0;
|
|
8141
|
-
}
|
|
8142
|
-
}
|
|
8143
|
-
} catch (e) {
|
|
8144
|
-
const parts = callbackUrl.split("#");
|
|
8145
|
-
if (parts.length > 1) {
|
|
8146
|
-
const hashParams = new URLSearchParams(parts[1]);
|
|
8147
|
-
result.accessToken = hashParams.get("access_token") || void 0;
|
|
8148
|
-
result.tokenType = hashParams.get("token_type") || void 0;
|
|
8149
|
-
result.state = hashParams.get("state") || void 0;
|
|
8150
|
-
result.error = hashParams.get("error") || void 0;
|
|
8151
|
-
result.errorDescription = hashParams.get("error_description") || void 0;
|
|
8152
|
-
const expiresIn = hashParams.get("expires_in");
|
|
8153
|
-
if (expiresIn) {
|
|
8154
|
-
result.expiresIn = parseInt(expiresIn, 10);
|
|
8155
|
-
}
|
|
8156
|
-
}
|
|
8157
|
-
const queryParts = callbackUrl.split("?");
|
|
8158
|
-
if (queryParts.length > 1) {
|
|
8159
|
-
const queryStr = queryParts[1].split("#")[0];
|
|
8160
|
-
const queryParams = new URLSearchParams(queryStr);
|
|
8161
|
-
if (!result.code) result.code = queryParams.get("code") || void 0;
|
|
8162
|
-
if (!result.state) result.state = queryParams.get("state") || void 0;
|
|
8163
|
-
if (!result.error) result.error = queryParams.get("error") || void 0;
|
|
8164
|
-
if (!result.errorDescription) result.errorDescription = queryParams.get("error_description") || void 0;
|
|
8165
|
-
}
|
|
8166
|
-
}
|
|
8167
|
-
return result;
|
|
8168
|
-
}
|
|
8169
|
-
/**
|
|
8170
|
-
* Handle a callback URL directly (convenience method combining parse + exchange/handleImplicit).
|
|
8171
|
-
*
|
|
8172
|
-
* @param callbackUrl - Full callback URL received from OAuth provider
|
|
8173
|
-
* @returns Exchange result with bit ID and tokens
|
|
8174
|
-
*/
|
|
8175
|
-
async handleCallback(callbackUrl) {
|
|
8176
|
-
const params = this.parseCallbackUrl(callbackUrl);
|
|
8177
|
-
if (params.error) {
|
|
8178
|
-
throw new Error(`OAuth error: ${params.error}${params.errorDescription ? ` - ${params.errorDescription}` : ""}`);
|
|
8179
|
-
}
|
|
8180
|
-
if (params.accessToken && params.state) {
|
|
8181
|
-
return this.handleImplicitCallback(params);
|
|
8182
|
-
}
|
|
8183
|
-
if (!params.code || !params.state) {
|
|
8184
|
-
throw new Error("Missing code or state parameter in OAuth callback");
|
|
8185
|
-
}
|
|
8186
|
-
return this.exchangeCode(params.state, params.code);
|
|
8187
|
-
}
|
|
8188
|
-
/**
|
|
8189
|
-
* Handle implicit flow callback (access token directly in callback URL).
|
|
8190
|
-
*/
|
|
8191
|
-
handleImplicitCallback(params) {
|
|
8192
|
-
if (!params.state || !params.accessToken) {
|
|
8193
|
-
throw new Error("Missing state or access_token for implicit flow");
|
|
8194
|
-
}
|
|
8195
|
-
const oauthState = this.pendingFlows.get(params.state);
|
|
8196
|
-
if (!oauthState) {
|
|
8197
|
-
throw new Error("Invalid or expired OAuth state. Please restart the authorization flow.");
|
|
8198
|
-
}
|
|
8199
|
-
this.pendingFlows.delete(params.state);
|
|
8200
|
-
const tokens = {
|
|
8201
|
-
accessToken: params.accessToken,
|
|
8202
|
-
refreshToken: params.refreshToken,
|
|
8203
|
-
tokenType: params.tokenType || "Bearer",
|
|
8204
|
-
expiresAt: params.expiresIn ? Date.now() + params.expiresIn * 1e3 : void 0
|
|
8205
|
-
};
|
|
8206
|
-
oauthTokenStore.setToken(oauthState.bitId, tokens, oauthState.config);
|
|
8207
|
-
this.logger.info("OAuth implicit flow completed successfully", { bitId: oauthState.bitId });
|
|
8208
|
-
return { bitId: oauthState.bitId, tokens };
|
|
8209
|
-
}
|
|
8210
|
-
/**
|
|
8211
|
-
* Exchange authorization code for tokens.
|
|
8212
|
-
*
|
|
8213
|
-
* @param state - State parameter from callback
|
|
8214
|
-
* @param code - Authorization code from callback
|
|
8215
|
-
* @returns Token set or throws error
|
|
8216
|
-
*/
|
|
8217
|
-
async exchangeCode(state, code) {
|
|
8218
|
-
const oauthState = this.pendingFlows.get(state);
|
|
8219
|
-
if (!oauthState) {
|
|
8220
|
-
throw new Error("Invalid or expired OAuth state. Please restart the authorization flow.");
|
|
8221
|
-
}
|
|
8222
|
-
this.pendingFlows.delete(state);
|
|
8223
|
-
const { config, codeVerifier, redirectUri, bitId } = oauthState;
|
|
8224
|
-
const params = new URLSearchParams({
|
|
8225
|
-
grant_type: "authorization_code",
|
|
8226
|
-
code,
|
|
8227
|
-
redirect_uri: redirectUri,
|
|
8228
|
-
client_id: config.clientId
|
|
8229
|
-
});
|
|
8230
|
-
if (codeVerifier) {
|
|
8231
|
-
params.set("code_verifier", codeVerifier);
|
|
8232
|
-
}
|
|
8233
|
-
if (config.clientSecret) {
|
|
8234
|
-
params.set("client_secret", config.clientSecret);
|
|
8235
|
-
}
|
|
8236
|
-
this.logger.info("Exchanging authorization code", { bitId });
|
|
8237
|
-
const response = await fetch(config.tokenUrl, {
|
|
8238
|
-
method: "POST",
|
|
8239
|
-
headers: {
|
|
8240
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
8241
|
-
"Accept": "application/json"
|
|
8242
|
-
},
|
|
8243
|
-
body: params.toString()
|
|
8244
|
-
});
|
|
8245
|
-
if (!response.ok) {
|
|
8246
|
-
const errorText = await response.text();
|
|
8247
|
-
this.logger.error("Token exchange failed", { bitId, status: response.status, error: errorText });
|
|
8248
|
-
throw new Error(`Token exchange failed: ${response.status} - ${errorText}`);
|
|
8249
|
-
}
|
|
8250
|
-
const data = await response.json();
|
|
8251
|
-
const tokens = {
|
|
8252
|
-
accessToken: data.access_token,
|
|
8253
|
-
refreshToken: data.refresh_token,
|
|
8254
|
-
tokenType: data.token_type || "Bearer",
|
|
8255
|
-
expiresAt: data.expires_in ? Date.now() + data.expires_in * 1e3 : void 0,
|
|
8256
|
-
scope: data.scope
|
|
8257
|
-
};
|
|
8258
|
-
oauthTokenStore.setToken(bitId, tokens, config);
|
|
8259
|
-
this.logger.info("OAuth flow completed successfully", { bitId });
|
|
8260
|
-
return { bitId, tokens };
|
|
8261
|
-
}
|
|
8262
|
-
/**
|
|
8263
|
-
* Get the pending OAuth state for a given state parameter.
|
|
8264
|
-
* Useful for validation without consuming the state.
|
|
8265
|
-
*/
|
|
8266
|
-
getPendingFlow(state) {
|
|
8267
|
-
return this.pendingFlows.get(state);
|
|
8268
|
-
}
|
|
8269
|
-
/**
|
|
8270
|
-
* Check if a flow is pending for a bit.
|
|
8271
|
-
*/
|
|
8272
|
-
hasPendingFlow(bitId) {
|
|
8273
|
-
for (const state of this.pendingFlows.values()) {
|
|
8274
|
-
if (state.bitId === bitId) {
|
|
8275
|
-
return true;
|
|
8276
|
-
}
|
|
8277
|
-
}
|
|
8278
|
-
return false;
|
|
8279
|
-
}
|
|
8280
|
-
/**
|
|
8281
|
-
* Get pending authorization URL for a bit (if flow already initiated).
|
|
8282
|
-
*/
|
|
8283
|
-
getPendingAuthUrl(bitId) {
|
|
8284
|
-
for (const [state, oauthState] of this.pendingFlows.entries()) {
|
|
8285
|
-
if (oauthState.bitId === bitId) {
|
|
8286
|
-
const { config, codeChallenge, redirectUri } = oauthState;
|
|
8287
|
-
const params = new URLSearchParams({
|
|
8288
|
-
response_type: "code",
|
|
8289
|
-
client_id: config.clientId,
|
|
8290
|
-
redirect_uri: redirectUri,
|
|
8291
|
-
scope: config.scopes.join(" "),
|
|
8292
|
-
state
|
|
8293
|
-
});
|
|
8294
|
-
if (codeChallenge) {
|
|
8295
|
-
params.set("code_challenge", codeChallenge);
|
|
8296
|
-
params.set("code_challenge_method", "S256");
|
|
8297
|
-
}
|
|
8298
|
-
if (config.extraAuthParams) {
|
|
8299
|
-
for (const [key, value] of Object.entries(config.extraAuthParams)) {
|
|
8300
|
-
params.set(key, value);
|
|
8301
|
-
}
|
|
8302
|
-
}
|
|
8303
|
-
return `${config.authorizationUrl}?${params.toString()}`;
|
|
8304
|
-
}
|
|
8305
|
-
}
|
|
8306
|
-
return null;
|
|
8307
|
-
}
|
|
8308
|
-
/**
|
|
8309
|
-
* Cancel a pending OAuth flow.
|
|
8310
|
-
*/
|
|
8311
|
-
cancelFlow(state) {
|
|
8312
|
-
return this.pendingFlows.delete(state);
|
|
8313
|
-
}
|
|
8314
|
-
/**
|
|
8315
|
-
* Cancel all pending flows for a bit.
|
|
8316
|
-
*/
|
|
8317
|
-
cancelFlowsForBit(bitId) {
|
|
8318
|
-
let cancelled = 0;
|
|
8319
|
-
for (const [state, oauthState] of this.pendingFlows.entries()) {
|
|
8320
|
-
if (oauthState.bitId === bitId) {
|
|
8321
|
-
this.pendingFlows.delete(state);
|
|
8322
|
-
cancelled++;
|
|
8323
|
-
}
|
|
8324
|
-
}
|
|
8325
|
-
return cancelled;
|
|
8326
|
-
}
|
|
8327
|
-
/**
|
|
8328
|
-
* Clean up expired flows (older than 10 minutes).
|
|
8329
|
-
*/
|
|
8330
|
-
cleanupExpiredFlows() {
|
|
8331
|
-
const now = Date.now();
|
|
8332
|
-
const expirationTime = 10 * 60 * 1e3;
|
|
8333
|
-
let cleaned = 0;
|
|
8334
|
-
for (const [state, oauthState] of this.pendingFlows.entries()) {
|
|
8335
|
-
if (now - oauthState.createdAt > expirationTime) {
|
|
8336
|
-
this.pendingFlows.delete(state);
|
|
8337
|
-
this.logger.debug("Expired OAuth flow cleaned up", { bitId: oauthState.bitId });
|
|
8338
|
-
cleaned++;
|
|
8339
|
-
}
|
|
8340
|
-
}
|
|
8341
|
-
return cleaned;
|
|
8342
|
-
}
|
|
8343
|
-
/**
|
|
8344
|
-
* Get all pending flow states (for debugging/status).
|
|
8345
|
-
*/
|
|
8346
|
-
getPendingFlowStates() {
|
|
8347
|
-
return Array.from(this.pendingFlows.entries()).map(([state, oauthState]) => ({
|
|
8348
|
-
bitId: oauthState.bitId,
|
|
8349
|
-
state,
|
|
8350
|
-
createdAt: oauthState.createdAt,
|
|
8351
|
-
redirectUri: oauthState.redirectUri
|
|
8352
|
-
}));
|
|
8353
|
-
}
|
|
8354
|
-
};
|
|
8355
|
-
|
|
8356
|
-
// packages/core/src/types.ts
|
|
8357
|
-
function isFrontendWorkflow(workflow) {
|
|
8358
|
-
return "nodes" in workflow && "edges" in workflow && Array.isArray(workflow.nodes) && Array.isArray(workflow.edges);
|
|
8359
|
-
}
|
|
8360
6895
|
export {
|
|
8361
6896
|
AuthenticationType,
|
|
8362
6897
|
BitAuth,
|
|
8363
6898
|
BitCategory,
|
|
8364
6899
|
HabitsExecutor,
|
|
8365
6900
|
HttpMethod,
|
|
8366
|
-
LoggerFactory,
|
|
8367
|
-
OAuthFlowManager,
|
|
8368
6901
|
PieceAuth,
|
|
8369
6902
|
PieceCategory,
|
|
8370
|
-
PollingStore,
|
|
8371
6903
|
Property,
|
|
8372
6904
|
StoreScope,
|
|
8373
6905
|
TriggerHookType,
|
|
@@ -8382,29 +6914,20 @@ export {
|
|
|
8382
6914
|
createBitTrigger,
|
|
8383
6915
|
createCustomApiCallAction,
|
|
8384
6916
|
createPiece,
|
|
8385
|
-
createPollingStore,
|
|
8386
6917
|
createTrigger,
|
|
8387
6918
|
customRequire,
|
|
8388
|
-
discoverOAuthRequirements,
|
|
8389
6919
|
ensureModuleInstalled,
|
|
8390
6920
|
executeActivepiecesModule,
|
|
8391
6921
|
executeBitsModule,
|
|
8392
6922
|
executeN8nModule,
|
|
8393
6923
|
executeScriptModule,
|
|
8394
|
-
extractBitsPieceFromModule,
|
|
8395
|
-
getBundledModule,
|
|
8396
6924
|
getLocalModulePath,
|
|
8397
6925
|
getModuleFullPath,
|
|
8398
6926
|
getNodesBasePath,
|
|
8399
6927
|
getNodesPath,
|
|
8400
6928
|
getSecurityConfig,
|
|
8401
6929
|
httpClient,
|
|
8402
|
-
isBundledModule,
|
|
8403
6930
|
isFrontendWorkflow,
|
|
8404
|
-
oauthTokenStore,
|
|
8405
|
-
pieceFromModule2 as pieceFromModule,
|
|
8406
|
-
printOAuthRequirements,
|
|
8407
|
-
registerBundledModule,
|
|
8408
6931
|
registerCortexModule,
|
|
8409
6932
|
scanInputForSecurity,
|
|
8410
6933
|
triggerHelper
|