@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.cjs
CHANGED
|
@@ -35,11 +35,8 @@ __export(index_exports, {
|
|
|
35
35
|
BitCategory: () => BitCategory,
|
|
36
36
|
HabitsExecutor: () => HabitsExecutor,
|
|
37
37
|
HttpMethod: () => HttpMethod,
|
|
38
|
-
LoggerFactory: () => LoggerFactory,
|
|
39
|
-
OAuthFlowManager: () => OAuthFlowManager,
|
|
40
38
|
PieceAuth: () => PieceAuth,
|
|
41
39
|
PieceCategory: () => PieceCategory,
|
|
42
|
-
PollingStore: () => PollingStore,
|
|
43
40
|
Property: () => Property,
|
|
44
41
|
StoreScope: () => StoreScope,
|
|
45
42
|
TriggerHookType: () => TriggerHookType,
|
|
@@ -54,29 +51,20 @@ __export(index_exports, {
|
|
|
54
51
|
createBitTrigger: () => createBitTrigger,
|
|
55
52
|
createCustomApiCallAction: () => createCustomApiCallAction,
|
|
56
53
|
createPiece: () => createPiece,
|
|
57
|
-
createPollingStore: () => createPollingStore,
|
|
58
54
|
createTrigger: () => createTrigger,
|
|
59
55
|
customRequire: () => customRequire,
|
|
60
|
-
discoverOAuthRequirements: () => discoverOAuthRequirements,
|
|
61
56
|
ensureModuleInstalled: () => ensureModuleInstalled,
|
|
62
57
|
executeActivepiecesModule: () => executeActivepiecesModule,
|
|
63
58
|
executeBitsModule: () => executeBitsModule,
|
|
64
59
|
executeN8nModule: () => executeN8nModule,
|
|
65
60
|
executeScriptModule: () => executeScriptModule,
|
|
66
|
-
extractBitsPieceFromModule: () => extractBitsPieceFromModule,
|
|
67
|
-
getBundledModule: () => getBundledModule,
|
|
68
61
|
getLocalModulePath: () => getLocalModulePath,
|
|
69
62
|
getModuleFullPath: () => getModuleFullPath,
|
|
70
63
|
getNodesBasePath: () => getNodesBasePath,
|
|
71
64
|
getNodesPath: () => getNodesPath,
|
|
72
65
|
getSecurityConfig: () => getSecurityConfig,
|
|
73
66
|
httpClient: () => httpClient,
|
|
74
|
-
isBundledModule: () => isBundledModule,
|
|
75
67
|
isFrontendWorkflow: () => isFrontendWorkflow,
|
|
76
|
-
oauthTokenStore: () => oauthTokenStore,
|
|
77
|
-
pieceFromModule: () => pieceFromModule2,
|
|
78
|
-
printOAuthRequirements: () => printOAuthRequirements,
|
|
79
|
-
registerBundledModule: () => registerBundledModule,
|
|
80
68
|
registerCortexModule: () => registerCortexModule,
|
|
81
69
|
scanInputForSecurity: () => scanInputForSecurity,
|
|
82
70
|
triggerHelper: () => triggerHelper
|
|
@@ -85,27 +73,13 @@ module.exports = __toCommonJS(index_exports);
|
|
|
85
73
|
|
|
86
74
|
// packages/cortex/core/src/WorkflowExecutor.ts
|
|
87
75
|
var import_uuid = require("uuid");
|
|
88
|
-
var import_croner = require("croner");
|
|
89
76
|
|
|
90
77
|
// packages/bindings/src/runtime.ts
|
|
91
78
|
function isTauri() {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
function getTauri() {
|
|
96
|
-
if (!isTauri()) return null;
|
|
97
|
-
return globalThis.__TAURI__;
|
|
98
|
-
}
|
|
99
|
-
function getTauriPlugin(pluginName) {
|
|
100
|
-
const tauri = getTauri();
|
|
101
|
-
if (!tauri) {
|
|
102
|
-
throw new Error(`Tauri is not available. Make sure withGlobalTauri is enabled in tauri.conf.json.`);
|
|
103
|
-
}
|
|
104
|
-
const plugin = tauri[pluginName];
|
|
105
|
-
if (!plugin) {
|
|
106
|
-
throw new Error(`Tauri plugin '${pluginName}' is not available. Make sure the plugin is installed and enabled.`);
|
|
79
|
+
if (typeof window === "undefined") {
|
|
80
|
+
return false;
|
|
107
81
|
}
|
|
108
|
-
return
|
|
82
|
+
return !!(window.__TAURI__ || window.__TAURI_INTERNALS__);
|
|
109
83
|
}
|
|
110
84
|
function isNode() {
|
|
111
85
|
return typeof process !== "undefined" && process.versions != null && process.versions.node != null;
|
|
@@ -309,12 +283,16 @@ function dirname(p) {
|
|
|
309
283
|
// packages/bindings/src/shell.ts
|
|
310
284
|
var nodeChildProcess = null;
|
|
311
285
|
var nodeUtil = null;
|
|
286
|
+
var tauriShell = null;
|
|
312
287
|
if (isNode()) {
|
|
313
288
|
nodeChildProcess = require("child_process");
|
|
314
289
|
nodeUtil = require("util");
|
|
315
290
|
}
|
|
316
|
-
function getTauriShell() {
|
|
317
|
-
|
|
291
|
+
async function getTauriShell() {
|
|
292
|
+
if (!tauriShell) {
|
|
293
|
+
tauriShell = await import("@tauri-apps/plugin-shell");
|
|
294
|
+
}
|
|
295
|
+
return tauriShell;
|
|
318
296
|
}
|
|
319
297
|
function filterEnv(env) {
|
|
320
298
|
if (!env) return void 0;
|
|
@@ -328,7 +306,7 @@ function filterEnv(env) {
|
|
|
328
306
|
}
|
|
329
307
|
async function exec(command, options = {}) {
|
|
330
308
|
if (isTauri()) {
|
|
331
|
-
const shell = getTauriShell();
|
|
309
|
+
const shell = await getTauriShell();
|
|
332
310
|
const shellProgram = process.platform === "win32" ? "cmd" : "sh";
|
|
333
311
|
const shellArgs = process.platform === "win32" ? ["/c", command] : ["-c", command];
|
|
334
312
|
const cmd = shell.Command.create(shellProgram, shellArgs, {
|
|
@@ -369,6 +347,11 @@ async function exec(command, options = {}) {
|
|
|
369
347
|
throw new Error("exec is not supported in this environment");
|
|
370
348
|
}
|
|
371
349
|
|
|
350
|
+
// packages/core/src/types.ts
|
|
351
|
+
function isFrontendWorkflow(workflow) {
|
|
352
|
+
return "nodes" in workflow && "edges" in workflow && Array.isArray(workflow.nodes) && Array.isArray(workflow.edges);
|
|
353
|
+
}
|
|
354
|
+
|
|
372
355
|
// packages/core/src/logger/types.ts
|
|
373
356
|
var LOG_LEVEL_PRIORITY = {
|
|
374
357
|
trace: 0,
|
|
@@ -686,9 +669,9 @@ var ConfigResolver = class {
|
|
|
686
669
|
}
|
|
687
670
|
const formatEnv = env[LOG_ENV_VARS.FORMAT];
|
|
688
671
|
if (formatEnv && ["text", "json"].includes(formatEnv.toLowerCase())) {
|
|
689
|
-
const
|
|
672
|
+
const format = formatEnv.toLowerCase();
|
|
690
673
|
result.outputs = result.outputs.map(
|
|
691
|
-
(o) => o.type === "console" || o.type === "file" ? { ...o, format
|
|
674
|
+
(o) => o.type === "console" || o.type === "file" ? { ...o, format } : o
|
|
692
675
|
);
|
|
693
676
|
}
|
|
694
677
|
for (const [key, value] of Object.entries(env)) {
|
|
@@ -1250,8 +1233,8 @@ var LoggerFactory = class {
|
|
|
1250
1233
|
/**
|
|
1251
1234
|
* Create a formatter based on format type
|
|
1252
1235
|
*/
|
|
1253
|
-
static createFormatter(
|
|
1254
|
-
return
|
|
1236
|
+
static createFormatter(format) {
|
|
1237
|
+
return format === "json" ? new JsonFormatter() : new TextFormatter();
|
|
1255
1238
|
}
|
|
1256
1239
|
};
|
|
1257
1240
|
|
|
@@ -2279,17 +2262,6 @@ function getModuleMainFile(moduleDefinition) {
|
|
|
2279
2262
|
|
|
2280
2263
|
// packages/cortex/core/src/utils/moduleLoader.ts
|
|
2281
2264
|
var logger2 = LoggerFactory.getRoot();
|
|
2282
|
-
var bundledModulesRegistry = /* @__PURE__ */ new Map();
|
|
2283
|
-
function registerBundledModule(moduleName, moduleExports) {
|
|
2284
|
-
bundledModulesRegistry.set(moduleName, moduleExports);
|
|
2285
|
-
console.log(`\u{1F4E6} Registered bundled module: ${moduleName}`);
|
|
2286
|
-
}
|
|
2287
|
-
function getBundledModule(moduleName) {
|
|
2288
|
-
return bundledModulesRegistry.get(moduleName);
|
|
2289
|
-
}
|
|
2290
|
-
function isBundledModule(moduleName) {
|
|
2291
|
-
return bundledModulesRegistry.has(moduleName);
|
|
2292
|
-
}
|
|
2293
2265
|
function getModuleName(moduleDefinition) {
|
|
2294
2266
|
if (moduleDefinition.source === "github") {
|
|
2295
2267
|
const url = moduleDefinition.repository;
|
|
@@ -2308,20 +2280,6 @@ function getModuleName(moduleDefinition) {
|
|
|
2308
2280
|
var MODULES_CONFIG_PATH = join(process.cwd(), "modules.json");
|
|
2309
2281
|
async function ensureModuleInstalled(moduleDefinition) {
|
|
2310
2282
|
const moduleName = getModuleName(moduleDefinition);
|
|
2311
|
-
if (isBundledModule(moduleDefinition.repository)) {
|
|
2312
|
-
console.log(`\u2713 Module ${moduleName} is pre-bundled, skipping installation
|
|
2313
|
-
`);
|
|
2314
|
-
return moduleDefinition.repository;
|
|
2315
|
-
}
|
|
2316
|
-
if (typeof require !== "undefined") {
|
|
2317
|
-
try {
|
|
2318
|
-
require(moduleDefinition.repository);
|
|
2319
|
-
console.log(`\u2713 Module ${moduleName} already available via require
|
|
2320
|
-
`);
|
|
2321
|
-
return moduleDefinition.repository;
|
|
2322
|
-
} catch {
|
|
2323
|
-
}
|
|
2324
|
-
}
|
|
2325
2283
|
console.log(`
|
|
2326
2284
|
\u{1F50D} ensureModuleInstalled called:`);
|
|
2327
2285
|
console.log(` Module name: ${moduleName}`);
|
|
@@ -2343,157 +2301,75 @@ async function ensureModuleInstalled(moduleDefinition) {
|
|
|
2343
2301
|
// packages/cortex/core/src/n8n/executionContext.ts
|
|
2344
2302
|
var path3 = __toESM(require("path"));
|
|
2345
2303
|
|
|
2346
|
-
// packages/
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
function
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
if (Array.isArray(headers)) {
|
|
2360
|
-
const obj = {};
|
|
2361
|
-
for (const [key, value] of headers) {
|
|
2362
|
-
obj[key] = value;
|
|
2363
|
-
}
|
|
2364
|
-
return obj;
|
|
2365
|
-
}
|
|
2366
|
-
return headers;
|
|
2367
|
-
}
|
|
2368
|
-
function wrapTauriResponse(response) {
|
|
2369
|
-
return {
|
|
2370
|
-
ok: response.ok,
|
|
2371
|
-
status: response.status,
|
|
2372
|
-
statusText: response.statusText || "",
|
|
2373
|
-
headers: response.headers,
|
|
2374
|
-
url: response.url,
|
|
2375
|
-
redirected: response.redirected || false,
|
|
2376
|
-
text: () => response.text(),
|
|
2377
|
-
json: () => response.json(),
|
|
2378
|
-
arrayBuffer: () => response.arrayBuffer(),
|
|
2379
|
-
blob: () => response.blob()
|
|
2380
|
-
};
|
|
2381
|
-
}
|
|
2382
|
-
function wrapNativeResponse(response) {
|
|
2383
|
-
return {
|
|
2384
|
-
ok: response.ok,
|
|
2385
|
-
status: response.status,
|
|
2386
|
-
statusText: response.statusText,
|
|
2387
|
-
headers: response.headers,
|
|
2388
|
-
url: response.url,
|
|
2389
|
-
redirected: response.redirected,
|
|
2390
|
-
text: () => response.text(),
|
|
2391
|
-
json: () => response.json(),
|
|
2392
|
-
arrayBuffer: () => response.arrayBuffer(),
|
|
2393
|
-
blob: () => response.blob()
|
|
2304
|
+
// packages/cortex/core/src/n8n/httpRequest.ts
|
|
2305
|
+
var import_axios = __toESM(require("axios"));
|
|
2306
|
+
var import_form_data = __toESM(require("form-data"));
|
|
2307
|
+
var logger3 = LoggerFactory.getRoot();
|
|
2308
|
+
function convertN8nRequestToAxios(requestOptions) {
|
|
2309
|
+
const { headers, method, timeout, auth, url, body, qs } = requestOptions;
|
|
2310
|
+
const axiosConfig = {
|
|
2311
|
+
headers: headers ?? {},
|
|
2312
|
+
method: method || "GET",
|
|
2313
|
+
timeout: timeout || 3e5,
|
|
2314
|
+
url,
|
|
2315
|
+
maxBodyLength: Infinity,
|
|
2316
|
+
maxContentLength: Infinity
|
|
2394
2317
|
};
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
if (isTauri()) {
|
|
2398
|
-
return fetchTauri(url, options);
|
|
2399
|
-
}
|
|
2400
|
-
return fetchNative(url, options);
|
|
2401
|
-
}
|
|
2402
|
-
async function fetchNative(url, options = {}) {
|
|
2403
|
-
const { timeout, maxRedirections, body, ...nativeOptions } = options;
|
|
2404
|
-
let processedBody = void 0;
|
|
2405
|
-
if (body !== null && body !== void 0) {
|
|
2406
|
-
if (typeof body === "object" && !(body instanceof ArrayBuffer) && !(body instanceof Blob) && !(body instanceof FormData) && !(body instanceof URLSearchParams) && !(body instanceof ReadableStream) && !ArrayBuffer.isView(body)) {
|
|
2407
|
-
processedBody = JSON.stringify(body);
|
|
2408
|
-
} else {
|
|
2409
|
-
processedBody = body;
|
|
2410
|
-
}
|
|
2318
|
+
if (qs) {
|
|
2319
|
+
axiosConfig.params = qs;
|
|
2411
2320
|
}
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2321
|
+
if (auth) {
|
|
2322
|
+
axiosConfig.auth = {
|
|
2323
|
+
username: auth.username || "",
|
|
2324
|
+
password: auth.password || ""
|
|
2325
|
+
};
|
|
2417
2326
|
}
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
...nativeOptions,
|
|
2421
|
-
body: processedBody,
|
|
2422
|
-
signal: controller?.signal ?? options.signal
|
|
2423
|
-
});
|
|
2424
|
-
return wrapNativeResponse(response);
|
|
2425
|
-
} finally {
|
|
2426
|
-
if (timeoutId) {
|
|
2427
|
-
clearTimeout(timeoutId);
|
|
2428
|
-
}
|
|
2327
|
+
if (requestOptions.baseURL) {
|
|
2328
|
+
axiosConfig.baseURL = requestOptions.baseURL;
|
|
2429
2329
|
}
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
const http = getTauriHttp();
|
|
2433
|
-
const { timeout, maxRedirections, body, headers, ...restOptions } = options;
|
|
2434
|
-
let processedBody = void 0;
|
|
2435
|
-
if (body !== null && body !== void 0) {
|
|
2436
|
-
if (typeof body === "object" && !(body instanceof ArrayBuffer) && !(body instanceof Blob) && !(body instanceof FormData) && !(body instanceof URLSearchParams) && !(body instanceof ReadableStream) && !ArrayBuffer.isView(body)) {
|
|
2437
|
-
processedBody = JSON.stringify(body);
|
|
2438
|
-
} else {
|
|
2439
|
-
processedBody = body;
|
|
2440
|
-
}
|
|
2330
|
+
if (requestOptions.disableFollowRedirect) {
|
|
2331
|
+
axiosConfig.maxRedirects = 0;
|
|
2441
2332
|
}
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
body: processedBody,
|
|
2445
|
-
headers: headersToObject(headers)
|
|
2446
|
-
};
|
|
2447
|
-
if (timeout !== void 0) {
|
|
2448
|
-
tauriOptions.timeout = timeout;
|
|
2333
|
+
if (requestOptions.encoding) {
|
|
2334
|
+
axiosConfig.responseType = requestOptions.encoding;
|
|
2449
2335
|
}
|
|
2450
|
-
if (
|
|
2451
|
-
tauriOptions.maxRedirections = maxRedirections;
|
|
2336
|
+
if (requestOptions.skipSslCertificateValidation) {
|
|
2452
2337
|
}
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
}
|
|
2338
|
+
if (body) {
|
|
2339
|
+
if (body instanceof import_form_data.default) {
|
|
2340
|
+
axiosConfig.data = body;
|
|
2341
|
+
axiosConfig.headers = {
|
|
2342
|
+
...axiosConfig.headers,
|
|
2343
|
+
...body.getHeaders()
|
|
2344
|
+
};
|
|
2345
|
+
} else if (body instanceof URLSearchParams) {
|
|
2346
|
+
axiosConfig.headers = {
|
|
2347
|
+
...axiosConfig.headers,
|
|
2348
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
2349
|
+
};
|
|
2350
|
+
axiosConfig.data = body;
|
|
2351
|
+
} else if (typeof body === "object" && Object.keys(body).length > 0) {
|
|
2352
|
+
axiosConfig.data = body;
|
|
2353
|
+
} else if (typeof body === "string") {
|
|
2354
|
+
axiosConfig.data = body;
|
|
2471
2355
|
}
|
|
2472
|
-
fullUrl += (fullUrl.includes("?") ? "&" : "?") + params.toString();
|
|
2473
2356
|
}
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
const formHeaders = body.getHeaders();
|
|
2480
|
-
Object.assign(headers, formHeaders);
|
|
2481
|
-
return body;
|
|
2482
|
-
}
|
|
2483
|
-
if (body instanceof URLSearchParams) {
|
|
2484
|
-
headers["Content-Type"] = "application/x-www-form-urlencoded";
|
|
2485
|
-
return body.toString();
|
|
2357
|
+
if (requestOptions.json) {
|
|
2358
|
+
axiosConfig.headers = {
|
|
2359
|
+
...axiosConfig.headers,
|
|
2360
|
+
Accept: "application/json"
|
|
2361
|
+
};
|
|
2486
2362
|
}
|
|
2487
|
-
if (
|
|
2488
|
-
|
|
2489
|
-
headers
|
|
2490
|
-
|
|
2491
|
-
|
|
2363
|
+
if (!axiosConfig.headers?.["User-Agent"]) {
|
|
2364
|
+
axiosConfig.headers = {
|
|
2365
|
+
...axiosConfig.headers,
|
|
2366
|
+
"User-Agent": "n8n-habits-executor"
|
|
2367
|
+
};
|
|
2492
2368
|
}
|
|
2493
|
-
if (
|
|
2494
|
-
|
|
2369
|
+
if (requestOptions.ignoreHttpStatusErrors) {
|
|
2370
|
+
axiosConfig.validateStatus = () => true;
|
|
2495
2371
|
}
|
|
2496
|
-
return
|
|
2372
|
+
return axiosConfig;
|
|
2497
2373
|
}
|
|
2498
2374
|
async function httpRequest(requestOptions) {
|
|
2499
2375
|
const noBodyMethods = ["GET", "HEAD", "OPTIONS"];
|
|
@@ -2501,69 +2377,27 @@ async function httpRequest(requestOptions) {
|
|
|
2501
2377
|
if (noBodyMethods.includes(method) && requestOptions.body && Object.keys(requestOptions.body).length === 0) {
|
|
2502
2378
|
delete requestOptions.body;
|
|
2503
2379
|
}
|
|
2504
|
-
const
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
if (requestOptions.auth) {
|
|
2508
|
-
const credentials = Buffer.from(
|
|
2509
|
-
`${requestOptions.auth.username || ""}:${requestOptions.auth.password || ""}`
|
|
2510
|
-
).toString("base64");
|
|
2511
|
-
headers["Authorization"] = `Basic ${credentials}`;
|
|
2512
|
-
}
|
|
2513
|
-
if (requestOptions.json) {
|
|
2514
|
-
headers["Accept"] = "application/json";
|
|
2380
|
+
const axiosConfig = convertN8nRequestToAxios(requestOptions);
|
|
2381
|
+
if (axiosConfig.data === void 0 || axiosConfig.method?.toUpperCase() === "GET") {
|
|
2382
|
+
delete axiosConfig.data;
|
|
2515
2383
|
}
|
|
2516
|
-
|
|
2517
|
-
headers["User-Agent"] = "n8n-habits-executor";
|
|
2518
|
-
}
|
|
2519
|
-
const url = buildUrl(requestOptions.url, requestOptions.baseURL, requestOptions.qs);
|
|
2520
|
-
let body;
|
|
2521
|
-
if (!noBodyMethods.includes(method)) {
|
|
2522
|
-
body = prepareBody(requestOptions.body, headers);
|
|
2523
|
-
}
|
|
2524
|
-
logger3.log(`\u{1F310} Making HTTP request: ${method} ${url}`);
|
|
2384
|
+
logger3.log(`\u{1F310} Making HTTP request: ${axiosConfig.method} ${axiosConfig.url}`);
|
|
2525
2385
|
try {
|
|
2526
|
-
const response = await
|
|
2527
|
-
method,
|
|
2528
|
-
headers,
|
|
2529
|
-
body,
|
|
2530
|
-
redirect: requestOptions.disableFollowRedirect ? "manual" : "follow"
|
|
2531
|
-
});
|
|
2532
|
-
let responseData;
|
|
2533
|
-
const contentType = response.headers.get("content-type") || "";
|
|
2534
|
-
if (requestOptions.encoding === "arraybuffer" || contentType.includes("application/octet-stream")) {
|
|
2535
|
-
responseData = await response.arrayBuffer();
|
|
2536
|
-
} else if (contentType.includes("application/json")) {
|
|
2537
|
-
responseData = await response.json();
|
|
2538
|
-
} else {
|
|
2539
|
-
responseData = await response.text();
|
|
2540
|
-
try {
|
|
2541
|
-
responseData = JSON.parse(responseData);
|
|
2542
|
-
} catch {
|
|
2543
|
-
}
|
|
2544
|
-
}
|
|
2545
|
-
if (!response.ok && !requestOptions.ignoreHttpStatusErrors) {
|
|
2546
|
-
logger3.error(`HTTP Error (${response.status}): ${JSON.stringify(responseData)}`);
|
|
2547
|
-
throw new Error(`HTTP Error (${response.status}): ${JSON.stringify(responseData)}`);
|
|
2548
|
-
}
|
|
2386
|
+
const response = await (0, import_axios.default)(axiosConfig);
|
|
2549
2387
|
if (requestOptions.returnFullResponse) {
|
|
2550
|
-
const responseHeaders = {};
|
|
2551
|
-
response.headers.forEach((value, key) => {
|
|
2552
|
-
responseHeaders[key] = value;
|
|
2553
|
-
});
|
|
2554
2388
|
return {
|
|
2555
|
-
body:
|
|
2556
|
-
headers:
|
|
2389
|
+
body: response.data,
|
|
2390
|
+
headers: response.headers,
|
|
2557
2391
|
statusCode: response.status,
|
|
2558
2392
|
statusMessage: response.statusText
|
|
2559
2393
|
};
|
|
2560
2394
|
}
|
|
2561
|
-
return
|
|
2395
|
+
return response.data;
|
|
2562
2396
|
} catch (error) {
|
|
2563
|
-
if (error.
|
|
2564
|
-
|
|
2397
|
+
if (error.response) {
|
|
2398
|
+
logger3.error(`HTTP Error (${error.response.status}): ${JSON.stringify(error.response.data)}`);
|
|
2399
|
+
throw new Error(`HTTP Error (${error.response.status}): ${JSON.stringify(error.response.data)}`);
|
|
2565
2400
|
}
|
|
2566
|
-
logger3.error(`Request failed: ${error.message}`);
|
|
2567
2401
|
throw error;
|
|
2568
2402
|
}
|
|
2569
2403
|
}
|
|
@@ -2779,7 +2613,7 @@ function createExecutionContext(node, params, options) {
|
|
|
2779
2613
|
},
|
|
2780
2614
|
// Helpers object with HTTP request and other utilities
|
|
2781
2615
|
helpers: {
|
|
2782
|
-
// HTTP request function
|
|
2616
|
+
// HTTP request function (real implementation)
|
|
2783
2617
|
httpRequest: async (opts) => {
|
|
2784
2618
|
return await httpRequest(opts);
|
|
2785
2619
|
},
|
|
@@ -2964,6 +2798,7 @@ function createExecutionContext(node, params, options) {
|
|
|
2964
2798
|
|
|
2965
2799
|
// packages/cortex/core/src/n8n/nodeExecution.ts
|
|
2966
2800
|
var path4 = __toESM(require("path"));
|
|
2801
|
+
var import_axios2 = __toESM(require("axios"));
|
|
2967
2802
|
var logger6 = LoggerFactory.getRoot();
|
|
2968
2803
|
async function loadNodeFromModule(moduleDefinition, mainFilePath) {
|
|
2969
2804
|
const moduleName = getModuleName(moduleDefinition);
|
|
@@ -3171,33 +3006,23 @@ async function executeRoutingBasedNode(node, params, context) {
|
|
|
3171
3006
|
}
|
|
3172
3007
|
logger6.log(`\u{1F310} Making routing-based request: ${method} ${url}`);
|
|
3173
3008
|
logger6.log(`\u{1F4E4} Request body:`, { body });
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
}
|
|
3184
|
-
const requestBody = Object.keys(body).length > 0 ? JSON.stringify(body) : void 0;
|
|
3185
|
-
if (requestBody && !headers["Content-Type"]) {
|
|
3186
|
-
headers["Content-Type"] = "application/json";
|
|
3009
|
+
const axiosConfig = {
|
|
3010
|
+
method,
|
|
3011
|
+
url,
|
|
3012
|
+
headers,
|
|
3013
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : void 0,
|
|
3014
|
+
data: Object.keys(body).length > 0 ? body : void 0
|
|
3015
|
+
};
|
|
3016
|
+
if (requestConfig.encoding === "arraybuffer") {
|
|
3017
|
+
axiosConfig.responseType = "arraybuffer";
|
|
3187
3018
|
}
|
|
3188
3019
|
try {
|
|
3189
|
-
const response = await
|
|
3190
|
-
method,
|
|
3191
|
-
headers,
|
|
3192
|
-
body: requestBody
|
|
3193
|
-
});
|
|
3020
|
+
const response = await (0, import_axios2.default)(axiosConfig);
|
|
3194
3021
|
logger6.log(`\u2705 Routing-based request successful: ${response.status}`);
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
3198
|
-
const binaryData = Buffer.from(arrayBuffer);
|
|
3022
|
+
if (requestConfig.encoding === "arraybuffer" || response.headers["content-type"]?.includes("audio")) {
|
|
3023
|
+
const binaryData = Buffer.from(response.data);
|
|
3199
3024
|
const base64Data = binaryData.toString("base64");
|
|
3200
|
-
const mimeType =
|
|
3025
|
+
const mimeType = response.headers["content-type"] || "audio/mpeg";
|
|
3201
3026
|
return [[{
|
|
3202
3027
|
json: {
|
|
3203
3028
|
success: true,
|
|
@@ -3214,28 +3039,14 @@ async function executeRoutingBasedNode(node, params, context) {
|
|
|
3214
3039
|
}
|
|
3215
3040
|
}]];
|
|
3216
3041
|
}
|
|
3217
|
-
|
|
3218
|
-
if (contentType.includes("application/json")) {
|
|
3219
|
-
responseData = await response.json();
|
|
3220
|
-
} else {
|
|
3221
|
-
const text = await response.text();
|
|
3222
|
-
try {
|
|
3223
|
-
responseData = JSON.parse(text);
|
|
3224
|
-
} catch {
|
|
3225
|
-
responseData = text;
|
|
3226
|
-
}
|
|
3227
|
-
}
|
|
3228
|
-
if (!response.ok) {
|
|
3229
|
-
const errorMessage = typeof responseData === "object" ? JSON.stringify(responseData) : String(responseData);
|
|
3230
|
-
logger6.error(`HTTP Error (${response.status}): ${errorMessage}`);
|
|
3231
|
-
throw new Error(`HTTP Error (${response.status}): ${errorMessage}`);
|
|
3232
|
-
}
|
|
3233
|
-
return [[{ json: responseData }]];
|
|
3042
|
+
return [[{ json: response.data }]];
|
|
3234
3043
|
} catch (error) {
|
|
3235
|
-
if (error.
|
|
3236
|
-
|
|
3044
|
+
if (error.response) {
|
|
3045
|
+
const errorData = error.response.data;
|
|
3046
|
+
const errorMessage = Buffer.isBuffer(errorData) ? errorData.toString("utf-8") : JSON.stringify(errorData);
|
|
3047
|
+
logger6.error(`HTTP Error (${error.response.status}): ${errorMessage}`);
|
|
3048
|
+
throw new Error(`HTTP Error (${error.response.status}): ${errorMessage}`);
|
|
3237
3049
|
}
|
|
3238
|
-
logger6.error(`Request failed: ${error.message}`);
|
|
3239
3050
|
throw error;
|
|
3240
3051
|
}
|
|
3241
3052
|
}
|
|
@@ -3515,13 +3326,13 @@ async function ensureActivepiecesModulesLoaded2() {
|
|
|
3515
3326
|
trimVersionFromAlias2 = shared.trimVersionFromAlias;
|
|
3516
3327
|
}
|
|
3517
3328
|
var logger9 = LoggerFactory.getRoot();
|
|
3518
|
-
var TriggerHookType = /* @__PURE__ */ ((
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
return
|
|
3329
|
+
var TriggerHookType = /* @__PURE__ */ ((TriggerHookType3) => {
|
|
3330
|
+
TriggerHookType3["ON_ENABLE"] = "ON_ENABLE";
|
|
3331
|
+
TriggerHookType3["ON_DISABLE"] = "ON_DISABLE";
|
|
3332
|
+
TriggerHookType3["RUN"] = "RUN";
|
|
3333
|
+
TriggerHookType3["TEST"] = "TEST";
|
|
3334
|
+
TriggerHookType3["HANDSHAKE"] = "HANDSHAKE";
|
|
3335
|
+
return TriggerHookType3;
|
|
3525
3336
|
})(TriggerHookType || {});
|
|
3526
3337
|
function createSimpleStore(prefix = "") {
|
|
3527
3338
|
const storage = /* @__PURE__ */ new Map();
|
|
@@ -3913,6 +3724,7 @@ var triggerHelper = {
|
|
|
3913
3724
|
};
|
|
3914
3725
|
|
|
3915
3726
|
// packages/cortex/core/src/bits/declarativeExecutor.ts
|
|
3727
|
+
var import_axios3 = __toESM(require("axios"));
|
|
3916
3728
|
function resolveExpression(expression, context) {
|
|
3917
3729
|
if (expression === null || expression === void 0) {
|
|
3918
3730
|
return expression;
|
|
@@ -4250,73 +4062,30 @@ async function executeDeclarativeNode(node, context) {
|
|
|
4250
4062
|
throw new Error("No URL specified in request configuration or defaults");
|
|
4251
4063
|
}
|
|
4252
4064
|
try {
|
|
4253
|
-
|
|
4254
|
-
if (requestConfig.baseURL && !fullUrl.startsWith("http://") && !fullUrl.startsWith("https://")) {
|
|
4255
|
-
fullUrl = requestConfig.baseURL.replace(/\/$/, "") + "/" + fullUrl.replace(/^\//, "");
|
|
4256
|
-
}
|
|
4257
|
-
if (requestConfig.params && Object.keys(requestConfig.params).length > 0) {
|
|
4258
|
-
const params = new URLSearchParams();
|
|
4259
|
-
for (const [key, value] of Object.entries(requestConfig.params)) {
|
|
4260
|
-
if (value !== void 0 && value !== null) {
|
|
4261
|
-
params.append(key, String(value));
|
|
4262
|
-
}
|
|
4263
|
-
}
|
|
4264
|
-
fullUrl += (fullUrl.includes("?") ? "&" : "?") + params.toString();
|
|
4265
|
-
}
|
|
4266
|
-
const headers = { ...requestConfig.headers };
|
|
4267
|
-
let body;
|
|
4268
|
-
if (requestConfig.data) {
|
|
4269
|
-
if (!headers["Content-Type"]) {
|
|
4270
|
-
headers["Content-Type"] = "application/json";
|
|
4271
|
-
}
|
|
4272
|
-
body = typeof requestConfig.data === "string" ? requestConfig.data : JSON.stringify(requestConfig.data);
|
|
4273
|
-
}
|
|
4274
|
-
const response = await fetch2(fullUrl, {
|
|
4275
|
-
method: requestConfig.method || "GET",
|
|
4276
|
-
headers,
|
|
4277
|
-
body
|
|
4278
|
-
});
|
|
4279
|
-
let responseData;
|
|
4280
|
-
const contentType = response.headers.get("content-type") || "";
|
|
4281
|
-
if (contentType.includes("application/json")) {
|
|
4282
|
-
responseData = await response.json();
|
|
4283
|
-
} else {
|
|
4284
|
-
const text = await response.text();
|
|
4285
|
-
try {
|
|
4286
|
-
responseData = JSON.parse(text);
|
|
4287
|
-
} catch {
|
|
4288
|
-
responseData = text;
|
|
4289
|
-
}
|
|
4290
|
-
}
|
|
4291
|
-
const responseHeaders = {};
|
|
4292
|
-
response.headers.forEach((value, key) => {
|
|
4293
|
-
responseHeaders[key] = value;
|
|
4294
|
-
});
|
|
4065
|
+
const response = await (0, import_axios3.default)(requestConfig);
|
|
4295
4066
|
const operationRouting = findOperationRouting(description.properties, context.parameters);
|
|
4296
|
-
if (!response.ok) {
|
|
4297
|
-
if (operationRouting?.request?.ignoreHttpStatusErrors) {
|
|
4298
|
-
return {
|
|
4299
|
-
success: true,
|
|
4300
|
-
data: responseData,
|
|
4301
|
-
status: response.status,
|
|
4302
|
-
headers: responseHeaders
|
|
4303
|
-
};
|
|
4304
|
-
}
|
|
4305
|
-
throw new Error(`HTTP Error (${response.status}): ${JSON.stringify(responseData)}`);
|
|
4306
|
-
}
|
|
4307
4067
|
const expressionContext = {
|
|
4308
4068
|
$parameter: context.parameters,
|
|
4309
4069
|
$credentials: context.credentials || {},
|
|
4310
|
-
$response:
|
|
4070
|
+
$response: response.data
|
|
4311
4071
|
};
|
|
4312
|
-
const processedData = processResponse(
|
|
4072
|
+
const processedData = processResponse(response.data, operationRouting, expressionContext);
|
|
4313
4073
|
return {
|
|
4314
4074
|
success: true,
|
|
4315
4075
|
data: processedData,
|
|
4316
4076
|
status: response.status,
|
|
4317
|
-
headers:
|
|
4077
|
+
headers: response.headers
|
|
4318
4078
|
};
|
|
4319
4079
|
} catch (error) {
|
|
4080
|
+
const operationRouting = findOperationRouting(description.properties, context.parameters);
|
|
4081
|
+
if (operationRouting?.request?.ignoreHttpStatusErrors && error.response) {
|
|
4082
|
+
return {
|
|
4083
|
+
success: true,
|
|
4084
|
+
data: error.response.data,
|
|
4085
|
+
status: error.response.status,
|
|
4086
|
+
headers: error.response.headers
|
|
4087
|
+
};
|
|
4088
|
+
}
|
|
4320
4089
|
throw new Error(`Request failed: ${error.message}`);
|
|
4321
4090
|
}
|
|
4322
4091
|
}
|
|
@@ -4395,374 +4164,12 @@ function extractDeclarativeNode(loadedModule) {
|
|
|
4395
4164
|
return null;
|
|
4396
4165
|
}
|
|
4397
4166
|
|
|
4398
|
-
// packages/cortex/core/src/store.ts
|
|
4399
|
-
var logger10 = LoggerFactory.getRoot();
|
|
4400
|
-
var driverModule = null;
|
|
4401
|
-
var driverLoadAttempted = false;
|
|
4402
|
-
var useInMemoryFallback = false;
|
|
4403
|
-
var inMemoryStore = /* @__PURE__ */ new Map();
|
|
4404
|
-
var inMemoryDriver = {
|
|
4405
|
-
async store(params) {
|
|
4406
|
-
const key = `${params.collection}:${params.key}`;
|
|
4407
|
-
inMemoryStore.set(key, params.value);
|
|
4408
|
-
return { success: true };
|
|
4409
|
-
},
|
|
4410
|
-
async get(params) {
|
|
4411
|
-
const key = `${params.collection}:${params.key}`;
|
|
4412
|
-
if (inMemoryStore.has(key)) {
|
|
4413
|
-
return { found: true, value: inMemoryStore.get(key) };
|
|
4414
|
-
}
|
|
4415
|
-
return { found: false, value: null };
|
|
4416
|
-
},
|
|
4417
|
-
async del(params) {
|
|
4418
|
-
const key = `${params.collection}:${params.key}`;
|
|
4419
|
-
inMemoryStore.delete(key);
|
|
4420
|
-
return { success: true };
|
|
4421
|
-
},
|
|
4422
|
-
async list(params) {
|
|
4423
|
-
const prefix = `${params.collection}:${params.prefix || ""}`;
|
|
4424
|
-
const keys = [];
|
|
4425
|
-
for (const key of inMemoryStore.keys()) {
|
|
4426
|
-
if (key.startsWith(prefix)) {
|
|
4427
|
-
keys.push(key.replace(`${params.collection}:`, ""));
|
|
4428
|
-
}
|
|
4429
|
-
}
|
|
4430
|
-
return { keys: keys.slice(0, params.limit || 100) };
|
|
4431
|
-
}
|
|
4432
|
-
};
|
|
4433
|
-
async function getDriver() {
|
|
4434
|
-
if (useInMemoryFallback) {
|
|
4435
|
-
return inMemoryDriver;
|
|
4436
|
-
}
|
|
4437
|
-
if (!driverModule && !driverLoadAttempted) {
|
|
4438
|
-
driverLoadAttempted = true;
|
|
4439
|
-
try {
|
|
4440
|
-
const modulePath = "@ha-bits/bit-database-sql/driver";
|
|
4441
|
-
driverModule = await import(
|
|
4442
|
-
/* webpackIgnore: true */
|
|
4443
|
-
modulePath
|
|
4444
|
-
);
|
|
4445
|
-
logger10.log("\u{1F4BE} Polling store: Loaded database driver");
|
|
4446
|
-
} catch (err) {
|
|
4447
|
-
logger10.warn(`\u{1F4BE} Polling store: Database driver not available, using in-memory fallback: ${err.message}`);
|
|
4448
|
-
useInMemoryFallback = true;
|
|
4449
|
-
return inMemoryDriver;
|
|
4450
|
-
}
|
|
4451
|
-
}
|
|
4452
|
-
return driverModule || inMemoryDriver;
|
|
4453
|
-
}
|
|
4454
|
-
var PollingStore = class {
|
|
4455
|
-
constructor(options = {}) {
|
|
4456
|
-
this.options = {
|
|
4457
|
-
database: options.database ?? "habits-polling.db",
|
|
4458
|
-
collection: options.collection ?? "polling",
|
|
4459
|
-
dedupStrategy: options.dedupStrategy ?? "id",
|
|
4460
|
-
ttlDays: options.ttlDays ?? 30
|
|
4461
|
-
};
|
|
4462
|
-
}
|
|
4463
|
-
/**
|
|
4464
|
-
* Generate a unique key for a seen item
|
|
4465
|
-
*/
|
|
4466
|
-
getItemKey(ctx, itemId) {
|
|
4467
|
-
return `${ctx.workflowId}:${ctx.triggerId}:${itemId}`;
|
|
4468
|
-
}
|
|
4469
|
-
/**
|
|
4470
|
-
* Generate the key for storing last polled date
|
|
4471
|
-
*/
|
|
4472
|
-
getLastPolledKey(ctx) {
|
|
4473
|
-
return `${ctx.workflowId}:${ctx.triggerId}:__lastPolled__`;
|
|
4474
|
-
}
|
|
4475
|
-
/**
|
|
4476
|
-
* Check if an item has been seen before
|
|
4477
|
-
*/
|
|
4478
|
-
async hasSeenItem(ctx, itemId, itemDate) {
|
|
4479
|
-
const driver = await getDriver();
|
|
4480
|
-
const key = this.getItemKey(ctx, itemId);
|
|
4481
|
-
const result = await driver.get({
|
|
4482
|
-
collection: this.options.collection,
|
|
4483
|
-
key,
|
|
4484
|
-
database: this.options.database
|
|
4485
|
-
});
|
|
4486
|
-
if (!result.found) {
|
|
4487
|
-
return false;
|
|
4488
|
-
}
|
|
4489
|
-
if (this.options.dedupStrategy === "date" && itemDate) {
|
|
4490
|
-
const record = result.value;
|
|
4491
|
-
return new Date(record.sourceDate) >= new Date(itemDate);
|
|
4492
|
-
}
|
|
4493
|
-
return true;
|
|
4494
|
-
}
|
|
4495
|
-
/**
|
|
4496
|
-
* Mark an item as seen
|
|
4497
|
-
*/
|
|
4498
|
-
async markItemSeen(ctx, itemId, sourceDate, data) {
|
|
4499
|
-
const driver = await getDriver();
|
|
4500
|
-
const key = this.getItemKey(ctx, itemId);
|
|
4501
|
-
const record = {
|
|
4502
|
-
id: itemId,
|
|
4503
|
-
sourceDate,
|
|
4504
|
-
seenAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4505
|
-
data
|
|
4506
|
-
};
|
|
4507
|
-
await driver.store({
|
|
4508
|
-
collection: this.options.collection,
|
|
4509
|
-
key,
|
|
4510
|
-
value: record,
|
|
4511
|
-
database: this.options.database
|
|
4512
|
-
});
|
|
4513
|
-
logger10.log(`\u{1F4BE} Polling store: Marked item as seen: ${itemId}`);
|
|
4514
|
-
}
|
|
4515
|
-
/**
|
|
4516
|
-
* Get multiple items' seen status in batch
|
|
4517
|
-
* Returns a Set of item IDs that have been seen
|
|
4518
|
-
*/
|
|
4519
|
-
async getSeenItems(ctx, itemIds) {
|
|
4520
|
-
const seen = /* @__PURE__ */ new Set();
|
|
4521
|
-
for (const itemId of itemIds) {
|
|
4522
|
-
if (await this.hasSeenItem(ctx, itemId)) {
|
|
4523
|
-
seen.add(itemId);
|
|
4524
|
-
}
|
|
4525
|
-
}
|
|
4526
|
-
return seen;
|
|
4527
|
-
}
|
|
4528
|
-
/**
|
|
4529
|
-
* Mark multiple items as seen in batch
|
|
4530
|
-
*/
|
|
4531
|
-
async markItemsSeen(ctx, items) {
|
|
4532
|
-
for (const item of items) {
|
|
4533
|
-
await this.markItemSeen(ctx, item.id, item.date, item.data);
|
|
4534
|
-
}
|
|
4535
|
-
}
|
|
4536
|
-
/**
|
|
4537
|
-
* Get the last polled timestamp for a trigger
|
|
4538
|
-
* Returns null if never polled before
|
|
4539
|
-
*/
|
|
4540
|
-
async getLastPolledDate(ctx) {
|
|
4541
|
-
const driver = await getDriver();
|
|
4542
|
-
const key = this.getLastPolledKey(ctx);
|
|
4543
|
-
const result = await driver.get({
|
|
4544
|
-
collection: this.options.collection,
|
|
4545
|
-
key,
|
|
4546
|
-
database: this.options.database
|
|
4547
|
-
});
|
|
4548
|
-
if (!result.found) {
|
|
4549
|
-
return null;
|
|
4550
|
-
}
|
|
4551
|
-
return result.value;
|
|
4552
|
-
}
|
|
4553
|
-
/**
|
|
4554
|
-
* Set the last polled timestamp for a trigger
|
|
4555
|
-
*/
|
|
4556
|
-
async setLastPolledDate(ctx, date) {
|
|
4557
|
-
const driver = await getDriver();
|
|
4558
|
-
const key = this.getLastPolledKey(ctx);
|
|
4559
|
-
await driver.store({
|
|
4560
|
-
collection: this.options.collection,
|
|
4561
|
-
key,
|
|
4562
|
-
value: date,
|
|
4563
|
-
database: this.options.database
|
|
4564
|
-
});
|
|
4565
|
-
logger10.log(`\u{1F4BE} Polling store: Updated last polled date: ${date}`);
|
|
4566
|
-
}
|
|
4567
|
-
/**
|
|
4568
|
-
* Get the count of seen items for a trigger
|
|
4569
|
-
*/
|
|
4570
|
-
async getSeenCount(ctx) {
|
|
4571
|
-
const driver = await getDriver();
|
|
4572
|
-
const prefix = `${ctx.workflowId}:${ctx.triggerId}:`;
|
|
4573
|
-
const result = await driver.list({
|
|
4574
|
-
collection: this.options.collection,
|
|
4575
|
-
prefix,
|
|
4576
|
-
limit: 1e4,
|
|
4577
|
-
// High limit to count all
|
|
4578
|
-
database: this.options.database
|
|
4579
|
-
});
|
|
4580
|
-
const count = result.keys.filter((k) => !k.endsWith("__lastPolled__")).length;
|
|
4581
|
-
return count;
|
|
4582
|
-
}
|
|
4583
|
-
/**
|
|
4584
|
-
* Clear all seen items for a trigger (useful for testing or reset)
|
|
4585
|
-
*/
|
|
4586
|
-
async clearTrigger(ctx) {
|
|
4587
|
-
const driver = await getDriver();
|
|
4588
|
-
const prefix = `${ctx.workflowId}:${ctx.triggerId}:`;
|
|
4589
|
-
const result = await driver.list({
|
|
4590
|
-
collection: this.options.collection,
|
|
4591
|
-
prefix,
|
|
4592
|
-
limit: 1e4,
|
|
4593
|
-
database: this.options.database
|
|
4594
|
-
});
|
|
4595
|
-
let deleted = 0;
|
|
4596
|
-
for (const key of result.keys) {
|
|
4597
|
-
await driver.del({
|
|
4598
|
-
collection: this.options.collection,
|
|
4599
|
-
key,
|
|
4600
|
-
database: this.options.database
|
|
4601
|
-
});
|
|
4602
|
-
deleted++;
|
|
4603
|
-
}
|
|
4604
|
-
logger10.log(`\u{1F4BE} Polling store: Cleared ${deleted} items for trigger ${ctx.triggerId}`);
|
|
4605
|
-
return deleted;
|
|
4606
|
-
}
|
|
4607
|
-
/**
|
|
4608
|
-
* TODO: Implement TTL-based cleanup
|
|
4609
|
-
* Clean up old records based on ttlDays configuration
|
|
4610
|
-
*/
|
|
4611
|
-
async cleanup() {
|
|
4612
|
-
logger10.log(`\u{1F4BE} Polling store: Cleanup not yet implemented (TTL: ${this.options.ttlDays} days)`);
|
|
4613
|
-
return 0;
|
|
4614
|
-
}
|
|
4615
|
-
};
|
|
4616
|
-
function createPollingStore(options) {
|
|
4617
|
-
return new PollingStore(options);
|
|
4618
|
-
}
|
|
4619
|
-
|
|
4620
|
-
// packages/cortex/core/src/bits/oauthTokenStore.ts
|
|
4621
|
-
var OAuth2TokenStore = class {
|
|
4622
|
-
constructor() {
|
|
4623
|
-
this.tokens = /* @__PURE__ */ new Map();
|
|
4624
|
-
this.logger = LoggerFactory.create(void 0, void 0, { bitName: "OAuth2TokenStore" });
|
|
4625
|
-
}
|
|
4626
|
-
/**
|
|
4627
|
-
* Store tokens for a bit
|
|
4628
|
-
* @param bitId - Unique identifier for the bit (e.g., "bit-oauth-mock")
|
|
4629
|
-
* @param tokens - Token set from OAuth provider
|
|
4630
|
-
* @param config - OAuth config used (for refresh)
|
|
4631
|
-
*/
|
|
4632
|
-
setToken(bitId, tokens, config) {
|
|
4633
|
-
this.tokens.set(bitId, {
|
|
4634
|
-
tokens,
|
|
4635
|
-
config,
|
|
4636
|
-
storedAt: Date.now()
|
|
4637
|
-
});
|
|
4638
|
-
this.logger.info("Token stored", { bitId, expiresAt: tokens.expiresAt });
|
|
4639
|
-
}
|
|
4640
|
-
/**
|
|
4641
|
-
* Get tokens for a bit
|
|
4642
|
-
* @param bitId - Unique identifier for the bit
|
|
4643
|
-
* @returns Token set or null if not found
|
|
4644
|
-
*/
|
|
4645
|
-
getToken(bitId) {
|
|
4646
|
-
const entry = this.tokens.get(bitId);
|
|
4647
|
-
if (!entry) {
|
|
4648
|
-
return null;
|
|
4649
|
-
}
|
|
4650
|
-
return entry.tokens;
|
|
4651
|
-
}
|
|
4652
|
-
/**
|
|
4653
|
-
* Get full token entry including config
|
|
4654
|
-
* @param bitId - Unique identifier for the bit
|
|
4655
|
-
*/
|
|
4656
|
-
getEntry(bitId) {
|
|
4657
|
-
return this.tokens.get(bitId) || null;
|
|
4658
|
-
}
|
|
4659
|
-
/**
|
|
4660
|
-
* Check if a valid (non-expired) token exists for a bit
|
|
4661
|
-
* @param bitId - Unique identifier for the bit
|
|
4662
|
-
* @returns true if valid token exists
|
|
4663
|
-
*/
|
|
4664
|
-
hasValidToken(bitId) {
|
|
4665
|
-
const entry = this.tokens.get(bitId);
|
|
4666
|
-
if (!entry) {
|
|
4667
|
-
return false;
|
|
4668
|
-
}
|
|
4669
|
-
return !this.isExpired(bitId);
|
|
4670
|
-
}
|
|
4671
|
-
/**
|
|
4672
|
-
* Check if token is expired
|
|
4673
|
-
* @param bitId - Unique identifier for the bit
|
|
4674
|
-
* @returns true if token is expired or doesn't exist
|
|
4675
|
-
*/
|
|
4676
|
-
isExpired(bitId) {
|
|
4677
|
-
const entry = this.tokens.get(bitId);
|
|
4678
|
-
if (!entry) {
|
|
4679
|
-
return true;
|
|
4680
|
-
}
|
|
4681
|
-
if (!entry.tokens.expiresAt) {
|
|
4682
|
-
return false;
|
|
4683
|
-
}
|
|
4684
|
-
return Date.now() > entry.tokens.expiresAt - 6e4;
|
|
4685
|
-
}
|
|
4686
|
-
/**
|
|
4687
|
-
* Remove token for a bit
|
|
4688
|
-
* @param bitId - Unique identifier for the bit
|
|
4689
|
-
*/
|
|
4690
|
-
removeToken(bitId) {
|
|
4691
|
-
this.tokens.delete(bitId);
|
|
4692
|
-
this.logger.info("Token removed", { bitId });
|
|
4693
|
-
}
|
|
4694
|
-
/**
|
|
4695
|
-
* Get all stored bit IDs
|
|
4696
|
-
*/
|
|
4697
|
-
getAllBitIds() {
|
|
4698
|
-
return Array.from(this.tokens.keys());
|
|
4699
|
-
}
|
|
4700
|
-
/**
|
|
4701
|
-
* Clear all tokens
|
|
4702
|
-
*/
|
|
4703
|
-
clear() {
|
|
4704
|
-
this.tokens.clear();
|
|
4705
|
-
this.logger.info("All tokens cleared");
|
|
4706
|
-
}
|
|
4707
|
-
/**
|
|
4708
|
-
* Refresh an expired token using the refresh token
|
|
4709
|
-
* @param bitId - Unique identifier for the bit
|
|
4710
|
-
* @returns New token set or null if refresh failed
|
|
4711
|
-
*/
|
|
4712
|
-
async refreshToken(bitId) {
|
|
4713
|
-
const entry = this.tokens.get(bitId);
|
|
4714
|
-
if (!entry || !entry.tokens.refreshToken) {
|
|
4715
|
-
this.logger.warn("Cannot refresh token: no refresh token available", { bitId });
|
|
4716
|
-
return null;
|
|
4717
|
-
}
|
|
4718
|
-
const { config, tokens } = entry;
|
|
4719
|
-
try {
|
|
4720
|
-
const params = new URLSearchParams({
|
|
4721
|
-
grant_type: "refresh_token",
|
|
4722
|
-
refresh_token: tokens.refreshToken,
|
|
4723
|
-
client_id: config.clientId
|
|
4724
|
-
});
|
|
4725
|
-
if (config.clientSecret) {
|
|
4726
|
-
params.set("client_secret", config.clientSecret);
|
|
4727
|
-
}
|
|
4728
|
-
const response = await fetch(config.tokenUrl, {
|
|
4729
|
-
method: "POST",
|
|
4730
|
-
headers: {
|
|
4731
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
4732
|
-
},
|
|
4733
|
-
body: params.toString()
|
|
4734
|
-
});
|
|
4735
|
-
if (!response.ok) {
|
|
4736
|
-
const errorText = await response.text();
|
|
4737
|
-
this.logger.error("Token refresh failed", { bitId, status: response.status, error: errorText });
|
|
4738
|
-
return null;
|
|
4739
|
-
}
|
|
4740
|
-
const data = await response.json();
|
|
4741
|
-
const newTokens = {
|
|
4742
|
-
accessToken: data.access_token,
|
|
4743
|
-
refreshToken: data.refresh_token || tokens.refreshToken,
|
|
4744
|
-
// Keep old refresh token if not returned
|
|
4745
|
-
tokenType: data.token_type || "Bearer",
|
|
4746
|
-
expiresAt: data.expires_in ? Date.now() + data.expires_in * 1e3 : void 0,
|
|
4747
|
-
scope: data.scope
|
|
4748
|
-
};
|
|
4749
|
-
this.setToken(bitId, newTokens, config);
|
|
4750
|
-
this.logger.info("Token refreshed successfully", { bitId });
|
|
4751
|
-
return newTokens;
|
|
4752
|
-
} catch (error) {
|
|
4753
|
-
this.logger.error("Token refresh error", { bitId, error: String(error) });
|
|
4754
|
-
return null;
|
|
4755
|
-
}
|
|
4756
|
-
}
|
|
4757
|
-
};
|
|
4758
|
-
var oauthTokenStore = new OAuth2TokenStore();
|
|
4759
|
-
|
|
4760
4167
|
// packages/cortex/core/src/bits/bitsDoer.ts
|
|
4761
|
-
var
|
|
4168
|
+
var logger10 = LoggerFactory.getRoot();
|
|
4762
4169
|
function extractBitsPieceFromModule(loadedModule) {
|
|
4763
4170
|
const declarativeNode = extractDeclarativeNode(loadedModule);
|
|
4764
4171
|
if (declarativeNode) {
|
|
4765
|
-
|
|
4172
|
+
logger10.log(`\u{1F4CB} Detected declarative node: ${declarativeNode.description.displayName}`);
|
|
4766
4173
|
return convertDeclarativeNodeToBitsPiece(declarativeNode);
|
|
4767
4174
|
}
|
|
4768
4175
|
let piece = null;
|
|
@@ -4783,8 +4190,6 @@ function extractBitsPieceFromModule(loadedModule) {
|
|
|
4783
4190
|
throw new Error("No valid bits piece found in module. Expected export with actions and triggers.");
|
|
4784
4191
|
}
|
|
4785
4192
|
return {
|
|
4786
|
-
id: piece.id,
|
|
4787
|
-
// Webhook routing ID (e.g., 'gohighlevel', 'hubspot')
|
|
4788
4193
|
displayName: piece.displayName || "Unknown Piece",
|
|
4789
4194
|
description: piece.description,
|
|
4790
4195
|
logoUrl: piece.logoUrl,
|
|
@@ -4902,20 +4307,11 @@ function createDeclarativeActionRunner(node, operation) {
|
|
|
4902
4307
|
}
|
|
4903
4308
|
async function pieceFromModule2(moduleDefinition) {
|
|
4904
4309
|
const moduleName = getModuleName(moduleDefinition);
|
|
4905
|
-
if (isBundledModule(moduleDefinition.repository)) {
|
|
4906
|
-
logger11.log(`\u{1F4E6} Using pre-bundled module: ${moduleName}`);
|
|
4907
|
-
const loadedModule = getBundledModule(moduleDefinition.repository);
|
|
4908
|
-
if (loadedModule) {
|
|
4909
|
-
const piece = extractBitsPieceFromModule(loadedModule);
|
|
4910
|
-
return piece;
|
|
4911
|
-
}
|
|
4912
|
-
throw new Error(`Bundled module ${moduleName} not found in registry`);
|
|
4913
|
-
}
|
|
4914
4310
|
const mainFilePath = getModuleMainFile(moduleDefinition);
|
|
4915
4311
|
if (!mainFilePath) {
|
|
4916
4312
|
throw new Error(`Could not locate main file for module: ${moduleName}`);
|
|
4917
4313
|
}
|
|
4918
|
-
|
|
4314
|
+
logger10.log(`\u{1F4E6} Bits module ready at: ${mainFilePath}`);
|
|
4919
4315
|
const originalCwd = process.cwd();
|
|
4920
4316
|
const moduleDir = dirname(mainFilePath);
|
|
4921
4317
|
let nodeModulesDir = moduleDir;
|
|
@@ -4930,18 +4326,18 @@ async function pieceFromModule2(moduleDefinition) {
|
|
|
4930
4326
|
return piece;
|
|
4931
4327
|
} catch (error) {
|
|
4932
4328
|
process.chdir(originalCwd);
|
|
4933
|
-
|
|
4329
|
+
logger10.error(error.stack);
|
|
4934
4330
|
throw error;
|
|
4935
4331
|
}
|
|
4936
4332
|
}
|
|
4937
4333
|
async function executeGenericBitsPiece(params, moduleDefinition) {
|
|
4938
4334
|
try {
|
|
4939
4335
|
const piece = await pieceFromModule2(moduleDefinition);
|
|
4940
|
-
|
|
4336
|
+
logger10.log(`\u{1F680} Executing Bits piece: ${piece.displayName}`);
|
|
4941
4337
|
const actionName = params.params.operation;
|
|
4942
4338
|
const pieceActions = piece.actions();
|
|
4943
|
-
|
|
4944
|
-
|
|
4339
|
+
logger10.log(`Available actions: ${Object.keys(pieceActions).join(", ")}`);
|
|
4340
|
+
logger10.log(`Requested action: ${actionName}`);
|
|
4945
4341
|
const action = pieceActions[actionName];
|
|
4946
4342
|
if (!action) {
|
|
4947
4343
|
throw new Error(
|
|
@@ -4950,80 +4346,11 @@ async function executeGenericBitsPiece(params, moduleDefinition) {
|
|
|
4950
4346
|
}
|
|
4951
4347
|
let auth = void 0;
|
|
4952
4348
|
const { credentials, ...actionProps } = params.params;
|
|
4953
|
-
if (
|
|
4954
|
-
const parts = moduleDefinition.repository.split("/");
|
|
4955
|
-
const bitId = parts[parts.length - 1];
|
|
4956
|
-
const oauthCredKey = Object.keys(credentials || {}).find((key) => {
|
|
4957
|
-
const cred = credentials[key];
|
|
4958
|
-
return cred && (cred.accessToken || cred.access_token);
|
|
4959
|
-
});
|
|
4960
|
-
if (oauthCredKey && credentials[oauthCredKey]) {
|
|
4961
|
-
const directTokens = credentials[oauthCredKey];
|
|
4962
|
-
auth = {
|
|
4963
|
-
accessToken: directTokens.accessToken || directTokens.access_token,
|
|
4964
|
-
refreshToken: directTokens.refreshToken || directTokens.refresh_token,
|
|
4965
|
-
tokenType: directTokens.tokenType || directTokens.token_type || "Bearer",
|
|
4966
|
-
expiresAt: directTokens.expiresAt || directTokens.expires_at
|
|
4967
|
-
};
|
|
4968
|
-
logger11.log(`\u{1F510} Using OAuth2 tokens from credentials for: ${bitId}`);
|
|
4969
|
-
if (auth.refreshToken) {
|
|
4970
|
-
const pieceAuth = piece.auth;
|
|
4971
|
-
oauthTokenStore.setToken(bitId, auth, {
|
|
4972
|
-
displayName: pieceAuth.displayName || bitId,
|
|
4973
|
-
required: pieceAuth.required || false,
|
|
4974
|
-
authorizationUrl: pieceAuth.authorizationUrl || "",
|
|
4975
|
-
tokenUrl: pieceAuth.tokenUrl || "",
|
|
4976
|
-
clientId: pieceAuth.clientId || "",
|
|
4977
|
-
clientSecret: pieceAuth.clientSecret,
|
|
4978
|
-
scopes: pieceAuth.scopes || []
|
|
4979
|
-
});
|
|
4980
|
-
}
|
|
4981
|
-
} else if (params.oauthTokens && params.oauthTokens[bitId]) {
|
|
4982
|
-
const userToken = params.oauthTokens[bitId];
|
|
4983
|
-
auth = {
|
|
4984
|
-
accessToken: userToken.accessToken,
|
|
4985
|
-
refreshToken: userToken.refreshToken,
|
|
4986
|
-
tokenType: userToken.tokenType,
|
|
4987
|
-
expiresAt: userToken.expiresAt
|
|
4988
|
-
};
|
|
4989
|
-
logger11.log(`\u{1F510} Using per-user OAuth2 token from cookies for: ${bitId}`);
|
|
4990
|
-
if (userToken.expiresAt && userToken.expiresAt < Date.now()) {
|
|
4991
|
-
logger11.warn(`\u26A0\uFE0F Per-user OAuth token expired for ${bitId}. User needs to re-authenticate.`);
|
|
4992
|
-
}
|
|
4993
|
-
} else {
|
|
4994
|
-
const oauthToken = oauthTokenStore.getToken(bitId);
|
|
4995
|
-
if (oauthToken) {
|
|
4996
|
-
auth = {
|
|
4997
|
-
accessToken: oauthToken.accessToken,
|
|
4998
|
-
refreshToken: oauthToken.refreshToken,
|
|
4999
|
-
tokenType: oauthToken.tokenType,
|
|
5000
|
-
expiresAt: oauthToken.expiresAt
|
|
5001
|
-
};
|
|
5002
|
-
logger11.log(`\u{1F510} Using OAuth2 PKCE token for: ${bitId}`);
|
|
5003
|
-
if (oauthTokenStore.isExpired(bitId)) {
|
|
5004
|
-
logger11.log(`\u26A0\uFE0F OAuth token expired for ${bitId}, attempting refresh...`);
|
|
5005
|
-
const refreshedToken = await oauthTokenStore.refreshToken(bitId);
|
|
5006
|
-
if (refreshedToken) {
|
|
5007
|
-
auth = {
|
|
5008
|
-
accessToken: refreshedToken.accessToken,
|
|
5009
|
-
refreshToken: refreshedToken.refreshToken,
|
|
5010
|
-
tokenType: refreshedToken.tokenType,
|
|
5011
|
-
expiresAt: refreshedToken.expiresAt
|
|
5012
|
-
};
|
|
5013
|
-
logger11.log(`\u2705 OAuth token refreshed for ${bitId}`);
|
|
5014
|
-
} else {
|
|
5015
|
-
logger11.warn(`\u274C Failed to refresh OAuth token for ${bitId}`);
|
|
5016
|
-
}
|
|
5017
|
-
}
|
|
5018
|
-
} else {
|
|
5019
|
-
logger11.warn(`\u26A0\uFE0F No OAuth token found for ${bitId}. Provide tokens via credentials or complete the OAuth flow.`);
|
|
5020
|
-
}
|
|
5021
|
-
}
|
|
5022
|
-
} else if (credentials) {
|
|
4349
|
+
if (credentials) {
|
|
5023
4350
|
const credentialKeys = Object.keys(credentials);
|
|
5024
4351
|
if (credentialKeys.length > 0) {
|
|
5025
4352
|
auth = credentials[credentialKeys[0]];
|
|
5026
|
-
|
|
4353
|
+
logger10.log(`\u{1F510} Using credentials for: ${credentialKeys[0]}`);
|
|
5027
4354
|
}
|
|
5028
4355
|
}
|
|
5029
4356
|
let bitLogger = void 0;
|
|
@@ -5041,10 +4368,9 @@ async function executeGenericBitsPiece(params, moduleDefinition) {
|
|
|
5041
4368
|
propsValue: {
|
|
5042
4369
|
...actionProps
|
|
5043
4370
|
},
|
|
5044
|
-
logger: bitLogger
|
|
5045
|
-
executor: params.executor
|
|
4371
|
+
logger: bitLogger
|
|
5046
4372
|
});
|
|
5047
|
-
|
|
4373
|
+
logger10.log(`\u2705 Successfully executed Bits piece action: ${actionName}`, result);
|
|
5048
4374
|
return {
|
|
5049
4375
|
success: true,
|
|
5050
4376
|
module: moduleDefinition.repository,
|
|
@@ -5059,7 +4385,7 @@ async function executeGenericBitsPiece(params, moduleDefinition) {
|
|
|
5059
4385
|
}
|
|
5060
4386
|
};
|
|
5061
4387
|
} catch (error) {
|
|
5062
|
-
|
|
4388
|
+
logger10.error(error.stack);
|
|
5063
4389
|
throw error;
|
|
5064
4390
|
}
|
|
5065
4391
|
}
|
|
@@ -5073,7 +4399,7 @@ async function executeBitsModule(params) {
|
|
|
5073
4399
|
throw new Error(`Bits module '${params.moduleName}' not found in modules.json`);
|
|
5074
4400
|
}
|
|
5075
4401
|
const inferredModuleName = getModuleName(moduleDefinition);
|
|
5076
|
-
|
|
4402
|
+
logger10.log(`
|
|
5077
4403
|
\u{1F50D} Ensuring bits module is ready: ${inferredModuleName}`);
|
|
5078
4404
|
await ensureModuleInstalled(moduleDefinition);
|
|
5079
4405
|
try {
|
|
@@ -5084,7 +4410,7 @@ async function executeBitsModule(params) {
|
|
|
5084
4410
|
}
|
|
5085
4411
|
|
|
5086
4412
|
// packages/cortex/core/src/bits/bitsWatcher.ts
|
|
5087
|
-
var
|
|
4413
|
+
var logger11 = LoggerFactory.getRoot();
|
|
5088
4414
|
function createSimpleStore2(prefix = "") {
|
|
5089
4415
|
const storage = /* @__PURE__ */ new Map();
|
|
5090
4416
|
return {
|
|
@@ -5102,39 +4428,6 @@ function createSimpleStore2(prefix = "") {
|
|
|
5102
4428
|
}
|
|
5103
4429
|
};
|
|
5104
4430
|
}
|
|
5105
|
-
function createBoundPollingStore(workflowId, triggerId, dedupStrategy = "id") {
|
|
5106
|
-
const store = createPollingStore({
|
|
5107
|
-
collection: "polling_seen_items",
|
|
5108
|
-
dedupStrategy
|
|
5109
|
-
});
|
|
5110
|
-
const ctx = { workflowId, triggerId };
|
|
5111
|
-
const baseStore = createSimpleStore2(`polling:${workflowId}:${triggerId}`);
|
|
5112
|
-
return {
|
|
5113
|
-
// Base store methods
|
|
5114
|
-
get: baseStore.get,
|
|
5115
|
-
put: baseStore.put,
|
|
5116
|
-
delete: baseStore.delete,
|
|
5117
|
-
// Polling-specific methods
|
|
5118
|
-
async hasSeenItem(itemId, itemDate) {
|
|
5119
|
-
return store.hasSeenItem(ctx, itemId, itemDate);
|
|
5120
|
-
},
|
|
5121
|
-
async markItemSeen(itemId, sourceDate, data) {
|
|
5122
|
-
return store.markItemSeen(ctx, itemId, sourceDate, data);
|
|
5123
|
-
},
|
|
5124
|
-
async getLastPolledDate() {
|
|
5125
|
-
return store.getLastPolledDate(ctx);
|
|
5126
|
-
},
|
|
5127
|
-
async setLastPolledDate(date) {
|
|
5128
|
-
return store.setLastPolledDate(ctx, date);
|
|
5129
|
-
},
|
|
5130
|
-
async getSeenCount() {
|
|
5131
|
-
return store.getSeenCount(ctx);
|
|
5132
|
-
},
|
|
5133
|
-
async clearTrigger() {
|
|
5134
|
-
return store.clearTrigger(ctx);
|
|
5135
|
-
}
|
|
5136
|
-
};
|
|
5137
|
-
}
|
|
5138
4431
|
function isNil2(value) {
|
|
5139
4432
|
return value === null || value === void 0;
|
|
5140
4433
|
}
|
|
@@ -5179,7 +4472,7 @@ var bitsTriggerHelper = {
|
|
|
5179
4472
|
* Execute a trigger based on hook type
|
|
5180
4473
|
*/
|
|
5181
4474
|
async executeTrigger(params) {
|
|
5182
|
-
const { moduleDefinition, triggerName, input, hookType, trigger, payload, webhookUrl, isTest, store
|
|
4475
|
+
const { moduleDefinition, triggerName, input, hookType, trigger, payload, webhookUrl, isTest, store } = params;
|
|
5183
4476
|
if (isNil2(triggerName)) {
|
|
5184
4477
|
throw new Error("Trigger name is not set");
|
|
5185
4478
|
}
|
|
@@ -5190,19 +4483,11 @@ var bitsTriggerHelper = {
|
|
|
5190
4483
|
const { trigger: loadedTrigger } = await this.getTrigger(moduleDefinition, triggerName);
|
|
5191
4484
|
bitsTrigger = loadedTrigger;
|
|
5192
4485
|
}
|
|
5193
|
-
|
|
4486
|
+
logger11.log(`\u{1F514} Executing bits trigger: ${triggerName} (${hookType})`);
|
|
5194
4487
|
const appListeners = [];
|
|
5195
4488
|
let scheduleOptions;
|
|
5196
4489
|
const storePrefix = isTest ? "test" : triggerName;
|
|
5197
4490
|
const triggerStore = store || createSimpleStore2(storePrefix);
|
|
5198
|
-
const triggerType = mapTriggerType(bitsTrigger.type);
|
|
5199
|
-
let pollingStore;
|
|
5200
|
-
if (triggerType === "POLLING" /* POLLING */ && workflowId) {
|
|
5201
|
-
const dedupStrategy = input.dedupBy || "id";
|
|
5202
|
-
const triggerId = `${moduleDefinition.repository}:${triggerName}`;
|
|
5203
|
-
pollingStore = createBoundPollingStore(workflowId, triggerId, dedupStrategy);
|
|
5204
|
-
logger12.log(`\u{1F4CA} Created polling store for ${triggerId} (dedup: ${dedupStrategy})`);
|
|
5205
|
-
}
|
|
5206
4491
|
let auth = void 0;
|
|
5207
4492
|
const { credentials, ...triggerProps } = input;
|
|
5208
4493
|
if (credentials) {
|
|
@@ -5213,7 +4498,7 @@ var bitsTriggerHelper = {
|
|
|
5213
4498
|
props: credentialData,
|
|
5214
4499
|
...credentialData
|
|
5215
4500
|
};
|
|
5216
|
-
|
|
4501
|
+
logger11.log(`\u{1F510} Using credentials for trigger: ${credentialKeys[0]}`);
|
|
5217
4502
|
}
|
|
5218
4503
|
}
|
|
5219
4504
|
const context = {
|
|
@@ -5232,11 +4517,7 @@ var bitsTriggerHelper = {
|
|
|
5232
4517
|
cronExpression: options.cronExpression,
|
|
5233
4518
|
timezone: options.timezone ?? "UTC"
|
|
5234
4519
|
};
|
|
5235
|
-
}
|
|
5236
|
-
executor,
|
|
5237
|
-
workflowId,
|
|
5238
|
-
nodeId,
|
|
5239
|
-
pollingStore
|
|
4520
|
+
}
|
|
5240
4521
|
};
|
|
5241
4522
|
try {
|
|
5242
4523
|
switch (hookType) {
|
|
@@ -5244,11 +4525,11 @@ var bitsTriggerHelper = {
|
|
|
5244
4525
|
if (bitsTrigger.onEnable) {
|
|
5245
4526
|
await bitsTrigger.onEnable(context);
|
|
5246
4527
|
}
|
|
5247
|
-
const
|
|
4528
|
+
const triggerType = mapTriggerType(bitsTrigger.type);
|
|
5248
4529
|
return {
|
|
5249
4530
|
success: true,
|
|
5250
4531
|
listeners: appListeners,
|
|
5251
|
-
scheduleOptions:
|
|
4532
|
+
scheduleOptions: triggerType === "POLLING" /* POLLING */ ? scheduleOptions : void 0
|
|
5252
4533
|
};
|
|
5253
4534
|
}
|
|
5254
4535
|
case "ON_DISABLE" /* ON_DISABLE */: {
|
|
@@ -5305,7 +4586,7 @@ var bitsTriggerHelper = {
|
|
|
5305
4586
|
};
|
|
5306
4587
|
}
|
|
5307
4588
|
} catch (error) {
|
|
5308
|
-
|
|
4589
|
+
logger11.error(`Error executing trigger ${triggerName}:`, error);
|
|
5309
4590
|
return {
|
|
5310
4591
|
success: false,
|
|
5311
4592
|
message: `Error executing trigger: ${error}`,
|
|
@@ -5334,13 +4615,13 @@ var bitsTriggerHelper = {
|
|
|
5334
4615
|
* For webhooks: calls run directly
|
|
5335
4616
|
*/
|
|
5336
4617
|
async executeBitsTrigger(params) {
|
|
5337
|
-
const { moduleDefinition, triggerName, input, payload, webhookUrl, store
|
|
4618
|
+
const { moduleDefinition, triggerName, input, payload, webhookUrl, store } = params;
|
|
5338
4619
|
const { piece, trigger } = await this.getTrigger(moduleDefinition, triggerName);
|
|
5339
4620
|
const triggerType = mapTriggerType(trigger.type);
|
|
5340
4621
|
const triggerStore = store || createSimpleStore2(`trigger:${triggerName}`);
|
|
5341
4622
|
switch (triggerType) {
|
|
5342
4623
|
case "POLLING" /* POLLING */: {
|
|
5343
|
-
|
|
4624
|
+
logger11.log(`Polling trigger flow: onEnable \u2192 run`);
|
|
5344
4625
|
const onEnableResult = await this.executeTrigger({
|
|
5345
4626
|
moduleDefinition,
|
|
5346
4627
|
triggerName,
|
|
@@ -5350,22 +4631,19 @@ var bitsTriggerHelper = {
|
|
|
5350
4631
|
payload,
|
|
5351
4632
|
webhookUrl,
|
|
5352
4633
|
isTest: false,
|
|
5353
|
-
store: triggerStore
|
|
5354
|
-
executor,
|
|
5355
|
-
workflowId,
|
|
5356
|
-
nodeId
|
|
4634
|
+
store: triggerStore
|
|
5357
4635
|
});
|
|
5358
4636
|
if (!onEnableResult.success) {
|
|
5359
4637
|
return onEnableResult;
|
|
5360
4638
|
}
|
|
5361
|
-
|
|
4639
|
+
logger11.log(` \u2705 onEnable completed`);
|
|
5362
4640
|
if (onEnableResult.scheduleOptions) {
|
|
5363
|
-
|
|
4641
|
+
logger11.log(` \u{1F4C5} Schedule: ${onEnableResult.scheduleOptions.cronExpression} (${onEnableResult.scheduleOptions.timezone})`);
|
|
5364
4642
|
}
|
|
5365
4643
|
if (onEnableResult.listeners && onEnableResult.listeners.length > 0) {
|
|
5366
|
-
|
|
4644
|
+
logger11.log(` \u{1F442} Listeners: ${onEnableResult.listeners.length}`);
|
|
5367
4645
|
}
|
|
5368
|
-
|
|
4646
|
+
logger11.log(` \u2192 Calling run to fetch items...`);
|
|
5369
4647
|
const runResult = await this.executeTrigger({
|
|
5370
4648
|
moduleDefinition,
|
|
5371
4649
|
triggerName,
|
|
@@ -5375,20 +4653,17 @@ var bitsTriggerHelper = {
|
|
|
5375
4653
|
payload,
|
|
5376
4654
|
webhookUrl,
|
|
5377
4655
|
isTest: false,
|
|
5378
|
-
store: triggerStore
|
|
5379
|
-
executor,
|
|
5380
|
-
workflowId,
|
|
5381
|
-
nodeId
|
|
4656
|
+
store: triggerStore
|
|
5382
4657
|
});
|
|
5383
4658
|
if (!runResult.success) {
|
|
5384
|
-
|
|
4659
|
+
logger11.warn(` \u26A0\uFE0F Run failed: ${runResult.message}`);
|
|
5385
4660
|
} else {
|
|
5386
|
-
|
|
4661
|
+
logger11.log(` \u2705 Run completed, items found: ${runResult.output?.length || 0}`);
|
|
5387
4662
|
}
|
|
5388
4663
|
return runResult;
|
|
5389
4664
|
}
|
|
5390
4665
|
case "WEBHOOK" /* WEBHOOK */: {
|
|
5391
|
-
|
|
4666
|
+
logger11.log(`Webhook trigger`);
|
|
5392
4667
|
return await this.executeTrigger({
|
|
5393
4668
|
moduleDefinition,
|
|
5394
4669
|
triggerName,
|
|
@@ -5396,10 +4671,7 @@ var bitsTriggerHelper = {
|
|
|
5396
4671
|
hookType: "RUN" /* RUN */,
|
|
5397
4672
|
payload,
|
|
5398
4673
|
webhookUrl,
|
|
5399
|
-
isTest: false
|
|
5400
|
-
executor,
|
|
5401
|
-
workflowId,
|
|
5402
|
-
nodeId
|
|
4674
|
+
isTest: false
|
|
5403
4675
|
});
|
|
5404
4676
|
}
|
|
5405
4677
|
default: {
|
|
@@ -5419,14 +4691,14 @@ var bitsTriggerHelper = {
|
|
|
5419
4691
|
const triggers = piece.triggers();
|
|
5420
4692
|
for (const [triggerName, trigger] of Object.entries(triggers)) {
|
|
5421
4693
|
const triggerType = mapTriggerType(trigger.type);
|
|
5422
|
-
|
|
4694
|
+
logger11.log(`\u{1F514} Hooking trigger: ${triggerName} (type: ${triggerType})`);
|
|
5423
4695
|
switch (triggerType) {
|
|
5424
4696
|
case "POLLING" /* POLLING */:
|
|
5425
|
-
|
|
4697
|
+
logger11.log(` \u2192 Setting up polling trigger`);
|
|
5426
4698
|
if (options?.onPollingTrigger) {
|
|
5427
4699
|
options.onPollingTrigger(triggerName, trigger);
|
|
5428
4700
|
}
|
|
5429
|
-
|
|
4701
|
+
logger11.log(` \u{1F680} Running polling trigger immediately...`);
|
|
5430
4702
|
try {
|
|
5431
4703
|
const result = await this.executeBitsTrigger({
|
|
5432
4704
|
moduleDefinition,
|
|
@@ -5434,32 +4706,32 @@ var bitsTriggerHelper = {
|
|
|
5434
4706
|
input: options?.input || {}
|
|
5435
4707
|
});
|
|
5436
4708
|
if (result.success) {
|
|
5437
|
-
|
|
5438
|
-
|
|
4709
|
+
logger11.log(` \u2705 Trigger ${triggerName} executed successfully`);
|
|
4710
|
+
logger11.log(` \u{1F4E6} Output items: ${result.output?.length || 0}`);
|
|
5439
4711
|
} else {
|
|
5440
|
-
|
|
4712
|
+
logger11.warn(` \u26A0\uFE0F Trigger ${triggerName} failed: ${result.message}`);
|
|
5441
4713
|
}
|
|
5442
4714
|
if (options?.onTriggerResult) {
|
|
5443
4715
|
options.onTriggerResult(triggerName, result);
|
|
5444
4716
|
}
|
|
5445
4717
|
} catch (error) {
|
|
5446
|
-
|
|
4718
|
+
logger11.error(` \u274C Error running trigger ${triggerName}:`, error.message);
|
|
5447
4719
|
}
|
|
5448
4720
|
break;
|
|
5449
4721
|
case "WEBHOOK" /* WEBHOOK */:
|
|
5450
|
-
|
|
4722
|
+
logger11.log(` \u2192 Setting up webhook trigger`);
|
|
5451
4723
|
if (options?.onWebhookTrigger) {
|
|
5452
4724
|
options.onWebhookTrigger(triggerName, trigger);
|
|
5453
4725
|
}
|
|
5454
4726
|
break;
|
|
5455
4727
|
case "APP_WEBHOOK" /* APP_WEBHOOK */:
|
|
5456
|
-
|
|
4728
|
+
logger11.log(` \u2192 Setting up app webhook trigger`);
|
|
5457
4729
|
if (options?.onAppWebhookTrigger) {
|
|
5458
4730
|
options.onAppWebhookTrigger(triggerName, trigger);
|
|
5459
4731
|
}
|
|
5460
4732
|
break;
|
|
5461
4733
|
default:
|
|
5462
|
-
|
|
4734
|
+
logger11.warn(` \u26A0\uFE0F Unknown trigger type: ${triggerType}`);
|
|
5463
4735
|
}
|
|
5464
4736
|
}
|
|
5465
4737
|
},
|
|
@@ -5497,7 +4769,7 @@ var bitsTriggerHelper = {
|
|
|
5497
4769
|
* Execute a webhook trigger with received payload
|
|
5498
4770
|
*/
|
|
5499
4771
|
async executeWebhookTrigger(nodeId, payload, headers, query) {
|
|
5500
|
-
|
|
4772
|
+
logger11.log(`\u{1F514} Executing webhook trigger for node: ${nodeId}`);
|
|
5501
4773
|
return {
|
|
5502
4774
|
success: true,
|
|
5503
4775
|
output: [
|
|
@@ -5521,7 +4793,7 @@ var vm = __toESM(require("vm"));
|
|
|
5521
4793
|
var import_child_process = require("child_process");
|
|
5522
4794
|
var os = __toESM(require("os"));
|
|
5523
4795
|
var ts = __toESM(require("typescript"));
|
|
5524
|
-
var
|
|
4796
|
+
var logger12 = LoggerFactory.getRoot();
|
|
5525
4797
|
function convertDenoImports(code) {
|
|
5526
4798
|
const npmPackages = [];
|
|
5527
4799
|
const imports = [];
|
|
@@ -5694,7 +4966,7 @@ async function executeJavaScript(code, params, context) {
|
|
|
5694
4966
|
});
|
|
5695
4967
|
return result;
|
|
5696
4968
|
} catch (error) {
|
|
5697
|
-
|
|
4969
|
+
logger12.error(`JavaScript execution error: ${error.message}`);
|
|
5698
4970
|
throw error;
|
|
5699
4971
|
}
|
|
5700
4972
|
}
|
|
@@ -5949,9 +5221,9 @@ async function executeScriptModule(paramsOrModuleName, maybeParams) {
|
|
|
5949
5221
|
executionParams = paramsOrModuleName.params;
|
|
5950
5222
|
inlineScript = paramsOrModuleName.script;
|
|
5951
5223
|
}
|
|
5952
|
-
|
|
5224
|
+
logger12.log(`
|
|
5953
5225
|
\u{1F300} Executing Script module: ${moduleName}`);
|
|
5954
|
-
|
|
5226
|
+
logger12.log(` Source: ${source}`);
|
|
5955
5227
|
let script = null;
|
|
5956
5228
|
if (source === "inline" && inlineScript) {
|
|
5957
5229
|
script = inlineScript;
|
|
@@ -5963,7 +5235,7 @@ async function executeScriptModule(paramsOrModuleName, maybeParams) {
|
|
|
5963
5235
|
if (!script || !script.content) {
|
|
5964
5236
|
throw new Error(`Could not load script: ${moduleName}`);
|
|
5965
5237
|
}
|
|
5966
|
-
|
|
5238
|
+
logger12.log(` Language: ${script.language}`);
|
|
5967
5239
|
const context = {
|
|
5968
5240
|
flow_input: executionParams,
|
|
5969
5241
|
previous_result: executionParams.previous_result || null,
|
|
@@ -5989,7 +5261,7 @@ async function executeScriptModule(paramsOrModuleName, maybeParams) {
|
|
|
5989
5261
|
default:
|
|
5990
5262
|
throw new Error(`Unsupported language: ${script.language}`);
|
|
5991
5263
|
}
|
|
5992
|
-
|
|
5264
|
+
logger12.log(`\u2705 Script executed successfully`);
|
|
5993
5265
|
return {
|
|
5994
5266
|
success: true,
|
|
5995
5267
|
module: moduleName,
|
|
@@ -6003,7 +5275,7 @@ async function executeScriptModule(paramsOrModuleName, maybeParams) {
|
|
|
6003
5275
|
}
|
|
6004
5276
|
};
|
|
6005
5277
|
} catch (error) {
|
|
6006
|
-
|
|
5278
|
+
logger12.error(`\u274C Script failed: ${error.message}`);
|
|
6007
5279
|
return {
|
|
6008
5280
|
success: false,
|
|
6009
5281
|
module: moduleName,
|
|
@@ -6030,7 +5302,7 @@ function getSecurityConfig() {
|
|
|
6030
5302
|
const moderationEnabled = process.env.HABITS_MODERATION_ENABLED === "true";
|
|
6031
5303
|
return { dlpEnabled, dlpIcapUrl, dlpIcapTimeout, piiMode, moderationEnabled };
|
|
6032
5304
|
}
|
|
6033
|
-
async function scanInputForSecurity(data, config,
|
|
5305
|
+
async function scanInputForSecurity(data, config, logger13) {
|
|
6034
5306
|
let processedData = data;
|
|
6035
5307
|
if (!config.dlpEnabled && !config.piiMode && !config.moderationEnabled) {
|
|
6036
5308
|
return processedData;
|
|
@@ -6038,50 +5310,50 @@ async function scanInputForSecurity(data, config, logger15) {
|
|
|
6038
5310
|
try {
|
|
6039
5311
|
const intersect = await import("@codenteam/intersect");
|
|
6040
5312
|
if (config.dlpEnabled) {
|
|
6041
|
-
|
|
5313
|
+
logger13.log("\u{1F510} [Security] Running DLP scan on input data...");
|
|
6042
5314
|
try {
|
|
6043
5315
|
const dlpOptions = {};
|
|
6044
5316
|
if (config.dlpIcapUrl) {
|
|
6045
5317
|
dlpOptions.icapUrl = config.dlpIcapUrl;
|
|
6046
5318
|
dlpOptions.timeout = config.dlpIcapTimeout;
|
|
6047
|
-
|
|
5319
|
+
logger13.log(`\u{1F510} [DLP] Using ICAP server: ${config.dlpIcapUrl}`);
|
|
6048
5320
|
}
|
|
6049
5321
|
const dlpResult = await intersect.dlp.consume(processedData, dlpOptions);
|
|
6050
5322
|
processedData = dlpResult.data ?? processedData;
|
|
6051
5323
|
if (dlpResult.findings && dlpResult.findings.length > 0) {
|
|
6052
|
-
|
|
5324
|
+
logger13.log(`\u{1F510} [DLP] Found ${dlpResult.findings.length} sensitive data instance(s)`);
|
|
6053
5325
|
}
|
|
6054
5326
|
} catch (dlpError) {
|
|
6055
|
-
|
|
5327
|
+
logger13.warn(`\u26A0\uFE0F [DLP] Scan failed: ${dlpError.message}`);
|
|
6056
5328
|
}
|
|
6057
5329
|
}
|
|
6058
5330
|
if (config.piiMode) {
|
|
6059
|
-
|
|
5331
|
+
logger13.log(`\u{1F510} [Security] Running PII scan (mode: ${config.piiMode}) on input data...`);
|
|
6060
5332
|
try {
|
|
6061
5333
|
const piiResult = await intersect.pii.consume(processedData, { mode: config.piiMode });
|
|
6062
5334
|
processedData = piiResult.data ?? processedData;
|
|
6063
5335
|
if (piiResult.detections && piiResult.detections.length > 0) {
|
|
6064
|
-
|
|
5336
|
+
logger13.log(`\u{1F510} [PII] Found ${piiResult.detections.length} PII instance(s) - mode: ${config.piiMode}`);
|
|
6065
5337
|
}
|
|
6066
5338
|
} catch (piiError) {
|
|
6067
|
-
|
|
5339
|
+
logger13.warn(`\u26A0\uFE0F [PII] Scan failed: ${piiError.message}`);
|
|
6068
5340
|
}
|
|
6069
5341
|
}
|
|
6070
5342
|
if (config.moderationEnabled) {
|
|
6071
|
-
|
|
5343
|
+
logger13.log("\u{1F510} [Security] Running moderation scan on input data...");
|
|
6072
5344
|
try {
|
|
6073
5345
|
const modResult = await intersect.moderation.consume(processedData);
|
|
6074
5346
|
if (modResult.flagged) {
|
|
6075
|
-
|
|
5347
|
+
logger13.warn(`\u26A0\uFE0F [Moderation] Content flagged: ${JSON.stringify(modResult.categories)}`);
|
|
6076
5348
|
}
|
|
6077
5349
|
processedData = modResult.data ?? processedData;
|
|
6078
5350
|
} catch (modError) {
|
|
6079
|
-
|
|
5351
|
+
logger13.warn(`\u26A0\uFE0F [Moderation] Scan failed: ${modError.message}`);
|
|
6080
5352
|
}
|
|
6081
5353
|
}
|
|
6082
5354
|
} catch (importError) {
|
|
6083
|
-
|
|
6084
|
-
|
|
5355
|
+
logger13.warn(`\u26A0\uFE0F [Security] @codenteam/intersect package not available: ${importError.message}`);
|
|
5356
|
+
logger13.warn(" Security features (DLP, PII, Moderation) are disabled.");
|
|
6085
5357
|
}
|
|
6086
5358
|
return processedData;
|
|
6087
5359
|
}
|
|
@@ -6094,8 +5366,6 @@ var WorkflowExecutor = class {
|
|
|
6094
5366
|
this.config = null;
|
|
6095
5367
|
this.logger = LoggerFactory.getRoot();
|
|
6096
5368
|
this.env = {};
|
|
6097
|
-
/** Active polling cron jobs - keyed by workflowId:nodeId */
|
|
6098
|
-
this.pollingCronJobs = /* @__PURE__ */ new Map();
|
|
6099
5369
|
}
|
|
6100
5370
|
/**
|
|
6101
5371
|
* Initialize from in-memory data (no file system access needed)
|
|
@@ -6150,7 +5420,7 @@ var WorkflowExecutor = class {
|
|
|
6150
5420
|
this.logger.log(` \u{1F511} Environment variables:`);
|
|
6151
5421
|
for (const key of Object.keys(this.env)) {
|
|
6152
5422
|
if (key.startsWith("HABITS_")) {
|
|
6153
|
-
this.logger.log(` - ${key}: ${this.env[key]
|
|
5423
|
+
this.logger.log(` - ${key}: ${this.env[key]}`);
|
|
6154
5424
|
}
|
|
6155
5425
|
}
|
|
6156
5426
|
this.logger.log(` \u{1F4CB} Resolvable habits params in workflow:`);
|
|
@@ -6160,7 +5430,7 @@ var WorkflowExecutor = class {
|
|
|
6160
5430
|
if (param.startsWith("habits.env.")) {
|
|
6161
5431
|
const envVar = param.slice("habits.env.".length);
|
|
6162
5432
|
const value = this.env[envVar];
|
|
6163
|
-
this.logger.log(` - {{${param}}} \u2192 ${value !== void 0 ?
|
|
5433
|
+
this.logger.log(` - {{${param}}} \u2192 ${value !== void 0 ? value : "(not set)"}`);
|
|
6164
5434
|
} else {
|
|
6165
5435
|
this.logger.log(` - {{${param}}} \u2192 (resolved at runtime)`);
|
|
6166
5436
|
}
|
|
@@ -6230,7 +5500,6 @@ var WorkflowExecutor = class {
|
|
|
6230
5500
|
\u{1F4E6} Module preloading complete
|
|
6231
5501
|
`);
|
|
6232
5502
|
await this.hookPollingTriggers();
|
|
6233
|
-
await this.registerBitsPollingTriggers();
|
|
6234
5503
|
}
|
|
6235
5504
|
/**
|
|
6236
5505
|
* Hook polling triggers for all loaded workflows
|
|
@@ -6293,145 +5562,6 @@ var WorkflowExecutor = class {
|
|
|
6293
5562
|
}
|
|
6294
5563
|
}
|
|
6295
5564
|
}
|
|
6296
|
-
/**
|
|
6297
|
-
* Register bits polling triggers with cron scheduling.
|
|
6298
|
-
* Scans workflows for polling trigger nodes, calls their onEnable() to get schedule,
|
|
6299
|
-
* then creates cron jobs using croner to periodically call run().
|
|
6300
|
-
*/
|
|
6301
|
-
async registerBitsPollingTriggers() {
|
|
6302
|
-
const workflows = this.getAllWorkflows();
|
|
6303
|
-
this.logger.log(`
|
|
6304
|
-
\u23F0 Scanning ${workflows.length} workflow(s) for polling triggers...`);
|
|
6305
|
-
let registeredCount = 0;
|
|
6306
|
-
for (const { reference, workflow } of workflows) {
|
|
6307
|
-
if (reference.enabled === false) continue;
|
|
6308
|
-
const workflowId = reference.id || workflow.id;
|
|
6309
|
-
for (const node of workflow.nodes || []) {
|
|
6310
|
-
const nodeData = node.data;
|
|
6311
|
-
const isTrigger = nodeData?.isTrigger === true || node.type === "trigger";
|
|
6312
|
-
const isBits = nodeData?.framework === "bits";
|
|
6313
|
-
const hasModule = !!nodeData?.module;
|
|
6314
|
-
if (!isTrigger || !isBits || !hasModule) continue;
|
|
6315
|
-
const moduleName = nodeData.module;
|
|
6316
|
-
const triggerName = nodeData.operation || "default";
|
|
6317
|
-
try {
|
|
6318
|
-
let bitPiece = null;
|
|
6319
|
-
const moduleDefinition = {
|
|
6320
|
-
source: nodeData.source || "npm",
|
|
6321
|
-
module: moduleName,
|
|
6322
|
-
framework: "bits",
|
|
6323
|
-
repository: moduleName
|
|
6324
|
-
};
|
|
6325
|
-
try {
|
|
6326
|
-
bitPiece = await pieceFromModule2(moduleDefinition);
|
|
6327
|
-
} catch (loadError) {
|
|
6328
|
-
this.logger.warn(` \u26A0\uFE0F Could not load module ${moduleName}: ${loadError}`);
|
|
6329
|
-
continue;
|
|
6330
|
-
}
|
|
6331
|
-
const triggers = typeof bitPiece.triggers === "function" ? bitPiece.triggers() : bitPiece.triggers;
|
|
6332
|
-
const trigger = triggers?.[triggerName];
|
|
6333
|
-
if (!trigger) {
|
|
6334
|
-
this.logger.warn(` \u26A0\uFE0F Trigger ${triggerName} not found in module ${moduleName}`);
|
|
6335
|
-
continue;
|
|
6336
|
-
}
|
|
6337
|
-
const triggerType = trigger.type?.toUpperCase?.() || trigger.type;
|
|
6338
|
-
if (triggerType !== "POLLING") {
|
|
6339
|
-
continue;
|
|
6340
|
-
}
|
|
6341
|
-
const rawProps = nodeData.params || {};
|
|
6342
|
-
const triggerProps = this.resolveParameters(rawProps, {});
|
|
6343
|
-
this.logger.log(` \u23F0 Enabling polling trigger: ${workflowId}/${node.id} (${moduleName}:${triggerName})`);
|
|
6344
|
-
const result = await bitsTriggerHelper.executeTrigger({
|
|
6345
|
-
moduleDefinition,
|
|
6346
|
-
triggerName,
|
|
6347
|
-
input: triggerProps,
|
|
6348
|
-
hookType: "ON_ENABLE" /* ON_ENABLE */,
|
|
6349
|
-
trigger,
|
|
6350
|
-
workflowId,
|
|
6351
|
-
nodeId: node.id
|
|
6352
|
-
});
|
|
6353
|
-
if (!result.success) {
|
|
6354
|
-
this.logger.warn(` \u26A0\uFE0F Failed to enable ${workflowId}/${node.id}: ${result.message}`);
|
|
6355
|
-
continue;
|
|
6356
|
-
}
|
|
6357
|
-
const scheduleOptions = result.scheduleOptions;
|
|
6358
|
-
if (!scheduleOptions?.cronExpression) {
|
|
6359
|
-
this.logger.warn(` \u26A0\uFE0F No schedule returned from onEnable for ${workflowId}/${node.id}`);
|
|
6360
|
-
continue;
|
|
6361
|
-
}
|
|
6362
|
-
const cronKey = `${workflowId}:${node.id}`;
|
|
6363
|
-
const { cronExpression, timezone = "UTC" } = scheduleOptions;
|
|
6364
|
-
this.logger.log(` \u{1F4C5} Creating cron job: ${cronExpression} (${timezone}) for ${cronKey}`);
|
|
6365
|
-
const cronJob = new import_croner.Cron(cronExpression, { timezone, name: cronKey }, async () => {
|
|
6366
|
-
const triggeredAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6367
|
-
this.logger.log(` \u23F0 Cron fired: ${cronKey} at ${triggeredAt}`);
|
|
6368
|
-
try {
|
|
6369
|
-
const runResult = await bitsTriggerHelper.executeTrigger({
|
|
6370
|
-
moduleDefinition,
|
|
6371
|
-
triggerName,
|
|
6372
|
-
input: triggerProps,
|
|
6373
|
-
hookType: "RUN" /* RUN */,
|
|
6374
|
-
trigger,
|
|
6375
|
-
workflowId,
|
|
6376
|
-
nodeId: node.id
|
|
6377
|
-
});
|
|
6378
|
-
if (runResult.success && runResult.output && runResult.output.length > 0) {
|
|
6379
|
-
this.logger.log(` \u{1F4E6} Trigger returned ${runResult.output.length} item(s), executing workflow for each...`);
|
|
6380
|
-
const loadedWorkflow = this.getWorkflow(workflowId);
|
|
6381
|
-
if (!loadedWorkflow) {
|
|
6382
|
-
this.logger.error(` \u274C Workflow ${workflowId} not found`);
|
|
6383
|
-
return;
|
|
6384
|
-
}
|
|
6385
|
-
for (let i = 0; i < runResult.output.length; i++) {
|
|
6386
|
-
const item = runResult.output[i];
|
|
6387
|
-
this.logger.log(` \u{1F504} Processing item ${i + 1}/${runResult.output.length}`);
|
|
6388
|
-
try {
|
|
6389
|
-
const execution = await this.executeWorkflow(loadedWorkflow.workflow, {
|
|
6390
|
-
initialContext: {
|
|
6391
|
-
"habits.input": item,
|
|
6392
|
-
// Single item, not array
|
|
6393
|
-
__pollingTrigger: true,
|
|
6394
|
-
__pollingNodeId: node.id,
|
|
6395
|
-
__triggeredAt: triggeredAt,
|
|
6396
|
-
__pollingItemIndex: i
|
|
6397
|
-
},
|
|
6398
|
-
skipTriggerWait: true,
|
|
6399
|
-
triggerNodeId: node.id
|
|
6400
|
-
});
|
|
6401
|
-
this.logger.log(` \u2705 Item ${i + 1} processed: ${execution.status}`);
|
|
6402
|
-
} catch (itemErr) {
|
|
6403
|
-
this.logger.error(` \u274C Item ${i + 1} failed: ${itemErr.message}`);
|
|
6404
|
-
}
|
|
6405
|
-
}
|
|
6406
|
-
this.logger.log(` \u2705 Workflow ${workflowId} completed processing ${runResult.output.length} item(s)`);
|
|
6407
|
-
} else {
|
|
6408
|
-
this.logger.log(` \u23F3 No new items from trigger, skipping workflow execution`);
|
|
6409
|
-
}
|
|
6410
|
-
} catch (err) {
|
|
6411
|
-
this.logger.error(` \u274C Error in polling trigger: ${err.message}`);
|
|
6412
|
-
}
|
|
6413
|
-
});
|
|
6414
|
-
this.pollingCronJobs.set(cronKey, cronJob);
|
|
6415
|
-
registeredCount++;
|
|
6416
|
-
this.logger.log(` \u2705 Registered: ${workflowId}/${node.id} (${triggerName}) -> ${cronExpression}`);
|
|
6417
|
-
} catch (error) {
|
|
6418
|
-
this.logger.error(` \u274C Error enabling polling trigger for ${workflowId}/${node.id}: ${error}`);
|
|
6419
|
-
}
|
|
6420
|
-
}
|
|
6421
|
-
}
|
|
6422
|
-
this.logger.log(`\u23F0 Enabled ${registeredCount} polling trigger(s)
|
|
6423
|
-
`);
|
|
6424
|
-
}
|
|
6425
|
-
/**
|
|
6426
|
-
* Stop all polling cron jobs
|
|
6427
|
-
*/
|
|
6428
|
-
stopPollingTriggers() {
|
|
6429
|
-
for (const [key, cron] of this.pollingCronJobs) {
|
|
6430
|
-
cron.stop();
|
|
6431
|
-
this.logger.log(` \u23F9\uFE0F Stopped polling trigger: ${key}`);
|
|
6432
|
-
}
|
|
6433
|
-
this.pollingCronJobs.clear();
|
|
6434
|
-
}
|
|
6435
5565
|
/**
|
|
6436
5566
|
* Find all template params starting with "habits." in a workflow
|
|
6437
5567
|
*/
|
|
@@ -6541,7 +5671,7 @@ var WorkflowExecutor = class {
|
|
|
6541
5671
|
});
|
|
6542
5672
|
try {
|
|
6543
5673
|
const webhookTriggers = this.findWebhookTriggers(workflow.nodes);
|
|
6544
|
-
if (webhookTriggers.length > 0
|
|
5674
|
+
if (webhookTriggers.length > 0) {
|
|
6545
5675
|
if (!options?.webhookHandler) {
|
|
6546
5676
|
throw new Error(
|
|
6547
5677
|
`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.`
|
|
@@ -6553,9 +5683,6 @@ var WorkflowExecutor = class {
|
|
|
6553
5683
|
this.logger.log(` - Node: ${trigger.nodeId}`);
|
|
6554
5684
|
}
|
|
6555
5685
|
this.logger.log("\n\u23F3 Waiting for webhook(s) to be triggered...\n");
|
|
6556
|
-
} else if (webhookTriggers.length > 0 && options?.skipTriggerWait) {
|
|
6557
|
-
this.logger.log(`
|
|
6558
|
-
\u{1F4CB} Detected ${webhookTriggers.length} webhook trigger(s) - using pre-provided payload`);
|
|
6559
5686
|
}
|
|
6560
5687
|
const dependencies = this.buildDependencyMap(workflow.nodes, workflow?.edges || []);
|
|
6561
5688
|
execution.nodeStatuses = workflow.nodes.map((node) => ({
|
|
@@ -6563,9 +5690,6 @@ var WorkflowExecutor = class {
|
|
|
6563
5690
|
status: "pending"
|
|
6564
5691
|
}));
|
|
6565
5692
|
let context = options?.initialContext ? { ...options.initialContext } : {};
|
|
6566
|
-
if (options?.oauthTokens) {
|
|
6567
|
-
context._oauthTokens = options.oauthTokens;
|
|
6568
|
-
}
|
|
6569
5693
|
if (!context.habits) {
|
|
6570
5694
|
context.habits = {};
|
|
6571
5695
|
}
|
|
@@ -6634,76 +5758,31 @@ var WorkflowExecutor = class {
|
|
|
6634
5758
|
status: "running"
|
|
6635
5759
|
});
|
|
6636
5760
|
try {
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
const pollingData = context["habits.input"];
|
|
6641
|
-
const scannedResult = await scanInputForSecurity(pollingData, securityConfig, this.logger);
|
|
5761
|
+
if (this.isWebhookTriggerNode(node)) {
|
|
5762
|
+
const webhookResult = await this.handleWebhookTrigger(node, context, execution, options.webhookHandler, options?.webhookTimeout);
|
|
5763
|
+
const scannedResult = await scanInputForSecurity(webhookResult.result, securityConfig, this.logger);
|
|
6642
5764
|
context[`${nodeId}`] = scannedResult;
|
|
6643
5765
|
context[nodeId] = scannedResult;
|
|
6644
5766
|
context.previous_result = scannedResult;
|
|
6645
|
-
|
|
5767
|
+
context.webhookPayload = scannedResult;
|
|
5768
|
+
this.updateNodeStatus(execution, nodeId, webhookResult.success ? "completed" : "failed", {
|
|
6646
5769
|
result: scannedResult,
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
5770
|
+
error: webhookResult.error,
|
|
5771
|
+
startTime: new Date(webhookResult.timestamp.getTime() - webhookResult.duration),
|
|
5772
|
+
endTime: webhookResult.timestamp,
|
|
5773
|
+
duration: webhookResult.duration
|
|
6650
5774
|
});
|
|
6651
5775
|
emitStreamEvent({
|
|
6652
|
-
type: "node_completed",
|
|
5776
|
+
type: webhookResult.success ? "node_completed" : "node_failed",
|
|
6653
5777
|
nodeId,
|
|
6654
5778
|
nodeName: node.data.label,
|
|
6655
|
-
status: "completed",
|
|
5779
|
+
status: webhookResult.success ? "completed" : "failed",
|
|
6656
5780
|
result: scannedResult,
|
|
6657
|
-
|
|
5781
|
+
error: webhookResult.error,
|
|
5782
|
+
duration: webhookResult.duration
|
|
6658
5783
|
});
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
if (skipWait && context.webhookPayload) {
|
|
6662
|
-
this.logger.log(`\u{1F514} Using pre-provided webhook payload for trigger node: ${nodeId}`);
|
|
6663
|
-
const scannedResult = await scanInputForSecurity(context.webhookPayload, securityConfig, this.logger);
|
|
6664
|
-
context[`${nodeId}`] = scannedResult;
|
|
6665
|
-
context[nodeId] = scannedResult;
|
|
6666
|
-
context.previous_result = scannedResult;
|
|
6667
|
-
this.updateNodeStatus(execution, nodeId, "completed", {
|
|
6668
|
-
result: scannedResult,
|
|
6669
|
-
startTime: /* @__PURE__ */ new Date(),
|
|
6670
|
-
endTime: /* @__PURE__ */ new Date(),
|
|
6671
|
-
duration: 0
|
|
6672
|
-
});
|
|
6673
|
-
emitStreamEvent({
|
|
6674
|
-
type: "node_completed",
|
|
6675
|
-
nodeId,
|
|
6676
|
-
nodeName: node.data.label,
|
|
6677
|
-
status: "completed",
|
|
6678
|
-
result: scannedResult,
|
|
6679
|
-
duration: 0
|
|
6680
|
-
});
|
|
6681
|
-
} else {
|
|
6682
|
-
const webhookResult = await this.handleWebhookTrigger(node, context, execution, options.webhookHandler, options?.webhookTimeout);
|
|
6683
|
-
const scannedResult = await scanInputForSecurity(webhookResult.result, securityConfig, this.logger);
|
|
6684
|
-
context[`${nodeId}`] = scannedResult;
|
|
6685
|
-
context[nodeId] = scannedResult;
|
|
6686
|
-
context.previous_result = scannedResult;
|
|
6687
|
-
context.webhookPayload = scannedResult;
|
|
6688
|
-
this.updateNodeStatus(execution, nodeId, webhookResult.success ? "completed" : "failed", {
|
|
6689
|
-
result: scannedResult,
|
|
6690
|
-
error: webhookResult.error,
|
|
6691
|
-
startTime: new Date(webhookResult.timestamp.getTime() - webhookResult.duration),
|
|
6692
|
-
endTime: webhookResult.timestamp,
|
|
6693
|
-
duration: webhookResult.duration
|
|
6694
|
-
});
|
|
6695
|
-
emitStreamEvent({
|
|
6696
|
-
type: webhookResult.success ? "node_completed" : "node_failed",
|
|
6697
|
-
nodeId,
|
|
6698
|
-
nodeName: node.data.label,
|
|
6699
|
-
status: webhookResult.success ? "completed" : "failed",
|
|
6700
|
-
result: scannedResult,
|
|
6701
|
-
error: webhookResult.error,
|
|
6702
|
-
duration: webhookResult.duration
|
|
6703
|
-
});
|
|
6704
|
-
if (!webhookResult.success) {
|
|
6705
|
-
this.logger.error(`\u274C Webhook trigger ${nodeId} failed: ${webhookResult.error}`);
|
|
6706
|
-
}
|
|
5784
|
+
if (!webhookResult.success) {
|
|
5785
|
+
this.logger.error(`\u274C Webhook trigger ${nodeId} failed: ${webhookResult.error}`);
|
|
6707
5786
|
}
|
|
6708
5787
|
} else {
|
|
6709
5788
|
const nodeResult = await this.executeNode(node, context, execution);
|
|
@@ -7075,10 +6154,7 @@ var WorkflowExecutor = class {
|
|
|
7075
6154
|
triggerName: node.data.operation || "unknown",
|
|
7076
6155
|
input: fullParams,
|
|
7077
6156
|
payload: context.triggerPayload || context.webhookPayload || {},
|
|
7078
|
-
webhookUrl: context.webhookUrl
|
|
7079
|
-
executor: this,
|
|
7080
|
-
workflowId: execution.workflowId,
|
|
7081
|
-
nodeId: node.id
|
|
6157
|
+
webhookUrl: context.webhookUrl
|
|
7082
6158
|
});
|
|
7083
6159
|
if (!triggerResult.success) {
|
|
7084
6160
|
throw new Error(triggerResult.message || "Trigger execution failed");
|
|
@@ -7093,10 +6169,7 @@ var WorkflowExecutor = class {
|
|
|
7093
6169
|
logger: this.logger,
|
|
7094
6170
|
workflowId: execution.workflowId,
|
|
7095
6171
|
nodeId: node.id,
|
|
7096
|
-
executionId: execution.id
|
|
7097
|
-
executor: this,
|
|
7098
|
-
// Pass per-user OAuth tokens from context (for multi-user server mode)
|
|
7099
|
-
oauthTokens: context._oauthTokens
|
|
6172
|
+
executionId: execution.id
|
|
7100
6173
|
});
|
|
7101
6174
|
nodeResult = output.result;
|
|
7102
6175
|
}
|
|
@@ -7173,34 +6246,9 @@ var WorkflowExecutor = class {
|
|
|
7173
6246
|
}
|
|
7174
6247
|
/**
|
|
7175
6248
|
* Evaluate a JavaScript expression in the given context
|
|
7176
|
-
* Supports default values with || operator: {{habits.input.value || 'default'}}
|
|
7177
6249
|
*/
|
|
7178
6250
|
evaluateExpression(expression, context) {
|
|
7179
6251
|
try {
|
|
7180
|
-
const defaultMatch = expression.match(/^(.+?)\s*\|\|\s*(['"`])(.*)(\2)$/);
|
|
7181
|
-
if (defaultMatch) {
|
|
7182
|
-
const [, mainExpr, , defaultValue] = defaultMatch;
|
|
7183
|
-
const result = this.evaluateExpression(mainExpr.trim(), context);
|
|
7184
|
-
if (result === void 0 || result === null || result === "" || result === mainExpr.trim()) {
|
|
7185
|
-
return defaultValue;
|
|
7186
|
-
}
|
|
7187
|
-
return result;
|
|
7188
|
-
}
|
|
7189
|
-
const defaultUnquotedMatch = expression.match(/^(.+?)\s*\|\|\s*([^'"`].*)$/);
|
|
7190
|
-
if (defaultUnquotedMatch) {
|
|
7191
|
-
const [, mainExpr, defaultValue] = defaultUnquotedMatch;
|
|
7192
|
-
const result = this.evaluateExpression(mainExpr.trim(), context);
|
|
7193
|
-
if (result === void 0 || result === null || result === "" || result === mainExpr.trim()) {
|
|
7194
|
-
const trimmedDefault = defaultValue.trim();
|
|
7195
|
-
if (trimmedDefault === "true") return true;
|
|
7196
|
-
if (trimmedDefault === "false") return false;
|
|
7197
|
-
if (trimmedDefault === "null") return null;
|
|
7198
|
-
const num = Number(trimmedDefault);
|
|
7199
|
-
if (!isNaN(num)) return num;
|
|
7200
|
-
return trimmedDefault;
|
|
7201
|
-
}
|
|
7202
|
-
return result;
|
|
7203
|
-
}
|
|
7204
6252
|
if (expression.startsWith("habits.env.")) {
|
|
7205
6253
|
const envVar = expression.slice("habits.env.".length);
|
|
7206
6254
|
const value = this.env[envVar] ?? (typeof process !== "undefined" ? process.env?.[envVar] : void 0);
|
|
@@ -7535,6 +6583,7 @@ function assertNotNullOrUndefined(value, fieldName) {
|
|
|
7535
6583
|
}
|
|
7536
6584
|
|
|
7537
6585
|
// packages/cortex/core/src/bits/framework.ts
|
|
6586
|
+
var import_axios4 = __toESM(require("axios"));
|
|
7538
6587
|
var AuthenticationType = /* @__PURE__ */ ((AuthenticationType2) => {
|
|
7539
6588
|
AuthenticationType2["BEARER_TOKEN"] = "BEARER_TOKEN";
|
|
7540
6589
|
AuthenticationType2["BASIC"] = "BASIC";
|
|
@@ -7555,42 +6604,42 @@ var HttpMethod = /* @__PURE__ */ ((HttpMethod2) => {
|
|
|
7555
6604
|
})(HttpMethod || {});
|
|
7556
6605
|
var httpClient = {
|
|
7557
6606
|
async sendRequest(request) {
|
|
7558
|
-
const
|
|
6607
|
+
const config = {
|
|
6608
|
+
url: request.url,
|
|
6609
|
+
method: request.method,
|
|
6610
|
+
headers: { ...request.headers },
|
|
6611
|
+
data: request.body,
|
|
6612
|
+
params: request.queryParams,
|
|
6613
|
+
timeout: request.timeout || 3e4
|
|
6614
|
+
};
|
|
7559
6615
|
if (request.authentication) {
|
|
7560
6616
|
switch (request.authentication.type) {
|
|
7561
6617
|
case "BEARER_TOKEN" /* BEARER_TOKEN */:
|
|
7562
|
-
headers
|
|
6618
|
+
config.headers = {
|
|
6619
|
+
...config.headers,
|
|
6620
|
+
Authorization: `Bearer ${request.authentication.token}`
|
|
6621
|
+
};
|
|
7563
6622
|
break;
|
|
7564
6623
|
case "BASIC" /* BASIC */:
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
6624
|
+
config.auth = {
|
|
6625
|
+
username: request.authentication.username || "",
|
|
6626
|
+
password: request.authentication.password || ""
|
|
6627
|
+
};
|
|
7569
6628
|
break;
|
|
7570
6629
|
case "API_KEY" /* API_KEY */:
|
|
7571
6630
|
const headerName = request.authentication.headerName || "X-API-Key";
|
|
7572
|
-
headers
|
|
6631
|
+
config.headers = {
|
|
6632
|
+
...config.headers,
|
|
6633
|
+
[headerName]: request.authentication.apiKey || ""
|
|
6634
|
+
};
|
|
7573
6635
|
break;
|
|
7574
6636
|
}
|
|
7575
6637
|
}
|
|
7576
|
-
|
|
7577
|
-
if (request.queryParams && Object.keys(request.queryParams).length > 0) {
|
|
7578
|
-
const params = new URLSearchParams(request.queryParams);
|
|
7579
|
-
url += (url.includes("?") ? "&" : "?") + params.toString();
|
|
7580
|
-
}
|
|
7581
|
-
const response = await fetch2(url, {
|
|
7582
|
-
method: request.method,
|
|
7583
|
-
headers,
|
|
7584
|
-
body: request.body ? JSON.stringify(request.body) : void 0
|
|
7585
|
-
});
|
|
7586
|
-
const responseHeaders = {};
|
|
7587
|
-
response.headers.forEach((value, key) => {
|
|
7588
|
-
responseHeaders[key] = value;
|
|
7589
|
-
});
|
|
6638
|
+
const response = await (0, import_axios4.default)(config);
|
|
7590
6639
|
return {
|
|
7591
6640
|
status: response.status,
|
|
7592
|
-
headers:
|
|
7593
|
-
body:
|
|
6641
|
+
headers: response.headers,
|
|
6642
|
+
body: response.data
|
|
7594
6643
|
};
|
|
7595
6644
|
}
|
|
7596
6645
|
};
|
|
@@ -7740,24 +6789,13 @@ var BitAuth = {
|
|
|
7740
6789
|
},
|
|
7741
6790
|
OAuth2(config) {
|
|
7742
6791
|
return {
|
|
7743
|
-
type: "
|
|
7744
|
-
displayName:
|
|
6792
|
+
type: "CUSTOM_AUTH",
|
|
6793
|
+
displayName: "OAuth2",
|
|
7745
6794
|
description: config.description,
|
|
7746
6795
|
required: config.required,
|
|
7747
|
-
|
|
7748
|
-
tokenUrl: config.tokenUrl,
|
|
7749
|
-
clientId: config.clientId,
|
|
7750
|
-
clientSecret: config.clientSecret,
|
|
7751
|
-
scopes: config.scopes,
|
|
7752
|
-
pkce: config.pkce ?? true,
|
|
7753
|
-
// PKCE enabled by default
|
|
7754
|
-
extraAuthParams: config.extraAuthParams
|
|
6796
|
+
...config
|
|
7755
6797
|
};
|
|
7756
6798
|
},
|
|
7757
|
-
/** @deprecated Use OAuth2() instead */
|
|
7758
|
-
OAuth2PKCE(config) {
|
|
7759
|
-
return BitAuth.OAuth2({ ...config, pkce: true });
|
|
7760
|
-
},
|
|
7761
6799
|
BasicAuth(config) {
|
|
7762
6800
|
return {
|
|
7763
6801
|
type: "CUSTOM_AUTH",
|
|
@@ -7799,7 +6837,6 @@ function createTrigger(config) {
|
|
|
7799
6837
|
run: config.run,
|
|
7800
6838
|
test: config.test,
|
|
7801
6839
|
onHandshake: config.onHandshake,
|
|
7802
|
-
filter: config.filter,
|
|
7803
6840
|
sampleData: config.sampleData
|
|
7804
6841
|
};
|
|
7805
6842
|
}
|
|
@@ -7846,7 +6883,6 @@ function createBit(config) {
|
|
|
7846
6883
|
}
|
|
7847
6884
|
}
|
|
7848
6885
|
return {
|
|
7849
|
-
id: config.id,
|
|
7850
6886
|
displayName: config.displayName,
|
|
7851
6887
|
description: config.description,
|
|
7852
6888
|
logoUrl: config.logoUrl,
|
|
@@ -7922,519 +6958,6 @@ function createCustomApiCallAction(config) {
|
|
|
7922
6958
|
}
|
|
7923
6959
|
});
|
|
7924
6960
|
}
|
|
7925
|
-
|
|
7926
|
-
// packages/cortex/core/src/bits/oauthDiscovery.ts
|
|
7927
|
-
var logger14 = LoggerFactory.create(void 0, void 0, { bitName: "OAuthDiscovery" });
|
|
7928
|
-
function resolveEnvExpression(value) {
|
|
7929
|
-
if (typeof value !== "string") return value;
|
|
7930
|
-
const envPattern = /\{\{habits\.env\.([A-Za-z_][A-Za-z0-9_]*)\}\}/g;
|
|
7931
|
-
return value.replace(envPattern, (match, envVar) => {
|
|
7932
|
-
return process.env[envVar] || "";
|
|
7933
|
-
});
|
|
7934
|
-
}
|
|
7935
|
-
function extractAuthCredentials(nodeAuth) {
|
|
7936
|
-
if (!nodeAuth) return {};
|
|
7937
|
-
let clientId = nodeAuth.clientId || nodeAuth.CLIENT_ID || nodeAuth.client_id;
|
|
7938
|
-
let clientSecret = nodeAuth.clientSecret || nodeAuth.CLIENT_SECRET || nodeAuth.client_secret;
|
|
7939
|
-
if (clientId) clientId = resolveEnvExpression(clientId);
|
|
7940
|
-
if (clientSecret) clientSecret = resolveEnvExpression(clientSecret);
|
|
7941
|
-
return { clientId, clientSecret };
|
|
7942
|
-
}
|
|
7943
|
-
function extractBitId(moduleName) {
|
|
7944
|
-
const parts = moduleName.split("/");
|
|
7945
|
-
return parts[parts.length - 1];
|
|
7946
|
-
}
|
|
7947
|
-
async function getAuthFromModule(moduleDefinition) {
|
|
7948
|
-
const moduleName = getModuleName(moduleDefinition);
|
|
7949
|
-
try {
|
|
7950
|
-
if (isBundledModule(moduleDefinition.repository)) {
|
|
7951
|
-
const loadedModule = getBundledModule(moduleDefinition.repository);
|
|
7952
|
-
if (loadedModule) {
|
|
7953
|
-
const piece = extractBitsPieceFromModule(loadedModule);
|
|
7954
|
-
return { auth: piece.auth, displayName: piece.displayName };
|
|
7955
|
-
}
|
|
7956
|
-
return null;
|
|
7957
|
-
}
|
|
7958
|
-
const mainFilePath = getModuleMainFile(moduleDefinition);
|
|
7959
|
-
if (!mainFilePath) {
|
|
7960
|
-
return null;
|
|
7961
|
-
}
|
|
7962
|
-
const originalCwd = process.cwd();
|
|
7963
|
-
const moduleDir = dirname(mainFilePath);
|
|
7964
|
-
let nodeModulesDir = moduleDir;
|
|
7965
|
-
while (nodeModulesDir && !nodeModulesDir.endsWith("/node_modules") && nodeModulesDir !== dirname(nodeModulesDir)) {
|
|
7966
|
-
nodeModulesDir = dirname(nodeModulesDir);
|
|
7967
|
-
}
|
|
7968
|
-
try {
|
|
7969
|
-
process.chdir(moduleDir);
|
|
7970
|
-
const loadedModule = simpleRequire(mainFilePath, nodeModulesDir);
|
|
7971
|
-
const piece = extractBitsPieceFromModule(loadedModule);
|
|
7972
|
-
process.chdir(originalCwd);
|
|
7973
|
-
return { auth: piece.auth, displayName: piece.displayName };
|
|
7974
|
-
} catch (error) {
|
|
7975
|
-
process.chdir(originalCwd);
|
|
7976
|
-
throw error;
|
|
7977
|
-
}
|
|
7978
|
-
} catch (error) {
|
|
7979
|
-
logger14.warn("Failed to load auth from module", { moduleName, error: String(error) });
|
|
7980
|
-
return null;
|
|
7981
|
-
}
|
|
7982
|
-
}
|
|
7983
|
-
function isOAuth2(auth) {
|
|
7984
|
-
return auth && (auth.type === "OAUTH2" || auth.type === "OAUTH2_PKCE");
|
|
7985
|
-
}
|
|
7986
|
-
async function discoverOAuthRequirements(workflows) {
|
|
7987
|
-
const requirements = [];
|
|
7988
|
-
const processedModules = /* @__PURE__ */ new Set();
|
|
7989
|
-
for (const [workflowId, workflow] of workflows) {
|
|
7990
|
-
for (const node of workflow.nodes) {
|
|
7991
|
-
const nodeData = node.data;
|
|
7992
|
-
if (nodeData?.framework !== "bits") {
|
|
7993
|
-
continue;
|
|
7994
|
-
}
|
|
7995
|
-
const moduleName = nodeData.module;
|
|
7996
|
-
if (!moduleName || processedModules.has(moduleName)) {
|
|
7997
|
-
continue;
|
|
7998
|
-
}
|
|
7999
|
-
processedModules.add(moduleName);
|
|
8000
|
-
const moduleDefinition = {
|
|
8001
|
-
repository: moduleName,
|
|
8002
|
-
source: "npm",
|
|
8003
|
-
framework: "bits"
|
|
8004
|
-
};
|
|
8005
|
-
const moduleAuth = await getAuthFromModule(moduleDefinition);
|
|
8006
|
-
if (!moduleAuth || !isOAuth2(moduleAuth.auth)) {
|
|
8007
|
-
continue;
|
|
8008
|
-
}
|
|
8009
|
-
const bitId = extractBitId(moduleName);
|
|
8010
|
-
const hasValidToken = oauthTokenStore.hasValidToken(bitId);
|
|
8011
|
-
const isExpired = oauthTokenStore.isExpired(bitId);
|
|
8012
|
-
let status;
|
|
8013
|
-
if (hasValidToken && !isExpired) {
|
|
8014
|
-
status = "valid";
|
|
8015
|
-
} else if (hasValidToken && isExpired) {
|
|
8016
|
-
status = "expired";
|
|
8017
|
-
} else {
|
|
8018
|
-
status = "needed";
|
|
8019
|
-
}
|
|
8020
|
-
const nodeAuthCredentials = extractAuthCredentials(nodeData.auth);
|
|
8021
|
-
const clientId = nodeAuthCredentials.clientId || moduleAuth.auth.clientId;
|
|
8022
|
-
const clientSecret = nodeAuthCredentials.clientSecret || moduleAuth.auth.clientSecret;
|
|
8023
|
-
requirements.push({
|
|
8024
|
-
bitId,
|
|
8025
|
-
moduleName,
|
|
8026
|
-
displayName: moduleAuth.displayName,
|
|
8027
|
-
config: {
|
|
8028
|
-
displayName: moduleAuth.auth.displayName,
|
|
8029
|
-
description: moduleAuth.auth.description,
|
|
8030
|
-
required: moduleAuth.auth.required,
|
|
8031
|
-
authorizationUrl: moduleAuth.auth.authorizationUrl,
|
|
8032
|
-
tokenUrl: moduleAuth.auth.tokenUrl,
|
|
8033
|
-
clientId,
|
|
8034
|
-
clientSecret,
|
|
8035
|
-
scopes: moduleAuth.auth.scopes,
|
|
8036
|
-
extraAuthParams: moduleAuth.auth.extraAuthParams
|
|
8037
|
-
},
|
|
8038
|
-
hasValidToken,
|
|
8039
|
-
status
|
|
8040
|
-
});
|
|
8041
|
-
}
|
|
8042
|
-
}
|
|
8043
|
-
return requirements;
|
|
8044
|
-
}
|
|
8045
|
-
async function printOAuthRequirements(requirements, getAuthUrl) {
|
|
8046
|
-
const neededRequirements = requirements.filter((r) => r.status !== "valid");
|
|
8047
|
-
if (neededRequirements.length === 0) {
|
|
8048
|
-
logger14.info("All OAuth2 tokens are valid");
|
|
8049
|
-
return;
|
|
8050
|
-
}
|
|
8051
|
-
console.log("\n" + "=".repeat(60));
|
|
8052
|
-
console.log("\u{1F510} OAuth2 Authorization Required");
|
|
8053
|
-
console.log("=".repeat(60));
|
|
8054
|
-
for (const req of neededRequirements) {
|
|
8055
|
-
const statusIcon = req.status === "expired" ? "\u26A0\uFE0F (expired)" : "\u274C (missing)";
|
|
8056
|
-
console.log(`
|
|
8057
|
-
\u{1F4E6} ${req.displayName} ${statusIcon}`);
|
|
8058
|
-
console.log(` Module: ${req.moduleName}`);
|
|
8059
|
-
console.log(` Scopes: ${req.config.scopes.join(", ")}`);
|
|
8060
|
-
const authUrl = await getAuthUrl(req);
|
|
8061
|
-
console.log(` \u279C Visit: ${authUrl}`);
|
|
8062
|
-
}
|
|
8063
|
-
console.log("\n" + "=".repeat(60));
|
|
8064
|
-
console.log("Open the URLs above in your browser to authorize access.");
|
|
8065
|
-
console.log("After authorization, the tokens will be stored automatically.");
|
|
8066
|
-
console.log("=".repeat(60) + "\n");
|
|
8067
|
-
}
|
|
8068
|
-
|
|
8069
|
-
// packages/cortex/core/src/bits/OAuthFlowManager.ts
|
|
8070
|
-
var OAuthFlowManager = class {
|
|
8071
|
-
constructor(options) {
|
|
8072
|
-
this.pendingFlows = /* @__PURE__ */ new Map();
|
|
8073
|
-
this.callbackBaseUrl = options.callbackBaseUrl.replace(/\/$/, "");
|
|
8074
|
-
this.logger = options.logger ?? LoggerFactory.create(void 0, void 0, { bitName: "OAuthFlowManager" });
|
|
8075
|
-
}
|
|
8076
|
-
/**
|
|
8077
|
-
* Generate a cryptographically secure random string for PKCE code verifier.
|
|
8078
|
-
* RFC 7636 requires 43-128 characters, URL-safe.
|
|
8079
|
-
*
|
|
8080
|
-
* Uses Web Crypto API for platform compatibility.
|
|
8081
|
-
*/
|
|
8082
|
-
generateCodeVerifier() {
|
|
8083
|
-
const randomBytes = new Uint8Array(32);
|
|
8084
|
-
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
8085
|
-
crypto.getRandomValues(randomBytes);
|
|
8086
|
-
} else {
|
|
8087
|
-
for (let i = 0; i < 32; i++) {
|
|
8088
|
-
randomBytes[i] = Math.floor(Math.random() * 256);
|
|
8089
|
-
}
|
|
8090
|
-
}
|
|
8091
|
-
return this.base64UrlEncode(randomBytes).slice(0, 43);
|
|
8092
|
-
}
|
|
8093
|
-
/**
|
|
8094
|
-
* Generate code challenge from verifier using SHA256.
|
|
8095
|
-
* RFC 7636 S256 method.
|
|
8096
|
-
*/
|
|
8097
|
-
async generateCodeChallenge(codeVerifier) {
|
|
8098
|
-
const encoder = new TextEncoder();
|
|
8099
|
-
const data = encoder.encode(codeVerifier);
|
|
8100
|
-
if (typeof crypto !== "undefined" && crypto.subtle) {
|
|
8101
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
8102
|
-
return this.base64UrlEncode(new Uint8Array(hashBuffer));
|
|
8103
|
-
}
|
|
8104
|
-
try {
|
|
8105
|
-
const nodeCrypto = await import("crypto");
|
|
8106
|
-
const hash = nodeCrypto.createHash("sha256").update(codeVerifier).digest();
|
|
8107
|
-
return this.base64UrlEncode(new Uint8Array(hash));
|
|
8108
|
-
} catch {
|
|
8109
|
-
throw new Error("No crypto implementation available for PKCE code challenge");
|
|
8110
|
-
}
|
|
8111
|
-
}
|
|
8112
|
-
/**
|
|
8113
|
-
* Generate a random state parameter for CSRF protection.
|
|
8114
|
-
*/
|
|
8115
|
-
generateState() {
|
|
8116
|
-
const randomBytes = new Uint8Array(16);
|
|
8117
|
-
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
8118
|
-
crypto.getRandomValues(randomBytes);
|
|
8119
|
-
} else {
|
|
8120
|
-
for (let i = 0; i < 16; i++) {
|
|
8121
|
-
randomBytes[i] = Math.floor(Math.random() * 256);
|
|
8122
|
-
}
|
|
8123
|
-
}
|
|
8124
|
-
return Array.from(randomBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
8125
|
-
}
|
|
8126
|
-
/**
|
|
8127
|
-
* Base64URL encode a Uint8Array (RFC 4648).
|
|
8128
|
-
*/
|
|
8129
|
-
base64UrlEncode(data) {
|
|
8130
|
-
let binary = "";
|
|
8131
|
-
for (let i = 0; i < data.length; i++) {
|
|
8132
|
-
binary += String.fromCharCode(data[i]);
|
|
8133
|
-
}
|
|
8134
|
-
const base64 = btoa(binary);
|
|
8135
|
-
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
8136
|
-
}
|
|
8137
|
-
/**
|
|
8138
|
-
* Initiate an OAuth2 flow for a bit.
|
|
8139
|
-
*
|
|
8140
|
-
* @param bitId - Unique identifier for the bit (e.g., "bit-google-drive")
|
|
8141
|
-
* @param config - OAuth2 configuration
|
|
8142
|
-
* @returns Authorization URL and flow info
|
|
8143
|
-
*/
|
|
8144
|
-
async initiateFlow(bitId, config) {
|
|
8145
|
-
const state = this.generateState();
|
|
8146
|
-
const redirectUri = `${this.callbackBaseUrl}/${bitId}/callback`;
|
|
8147
|
-
const usePkce = config.pkce !== false;
|
|
8148
|
-
const codeVerifier = usePkce ? this.generateCodeVerifier() : void 0;
|
|
8149
|
-
const codeChallenge = usePkce && codeVerifier ? await this.generateCodeChallenge(codeVerifier) : void 0;
|
|
8150
|
-
const oauthState = {
|
|
8151
|
-
codeVerifier,
|
|
8152
|
-
codeChallenge,
|
|
8153
|
-
state,
|
|
8154
|
-
redirectUri,
|
|
8155
|
-
bitId,
|
|
8156
|
-
createdAt: Date.now(),
|
|
8157
|
-
config
|
|
8158
|
-
};
|
|
8159
|
-
this.pendingFlows.set(state, oauthState);
|
|
8160
|
-
this.logger.info("OAuth flow initiated", { bitId, redirectUri, pkce: usePkce });
|
|
8161
|
-
const params = new URLSearchParams({
|
|
8162
|
-
response_type: "code",
|
|
8163
|
-
client_id: config.clientId,
|
|
8164
|
-
redirect_uri: redirectUri,
|
|
8165
|
-
scope: config.scopes.join(" "),
|
|
8166
|
-
state
|
|
8167
|
-
});
|
|
8168
|
-
if (usePkce && codeChallenge) {
|
|
8169
|
-
params.set("code_challenge", codeChallenge);
|
|
8170
|
-
params.set("code_challenge_method", "S256");
|
|
8171
|
-
}
|
|
8172
|
-
if (config.extraAuthParams) {
|
|
8173
|
-
for (const [key, value] of Object.entries(config.extraAuthParams)) {
|
|
8174
|
-
params.set(key, value);
|
|
8175
|
-
}
|
|
8176
|
-
}
|
|
8177
|
-
const authUrl = `${config.authorizationUrl}?${params.toString()}`;
|
|
8178
|
-
return { authUrl, state, redirectUri };
|
|
8179
|
-
}
|
|
8180
|
-
/**
|
|
8181
|
-
* Parse an OAuth callback URL to extract parameters.
|
|
8182
|
-
* Supports both query parameters (?code=...) and hash fragments (#access_token=...).
|
|
8183
|
-
*
|
|
8184
|
-
* @param callbackUrl - Full callback URL received from OAuth provider
|
|
8185
|
-
* @returns Parsed callback parameters
|
|
8186
|
-
*/
|
|
8187
|
-
parseCallbackUrl(callbackUrl) {
|
|
8188
|
-
const result = {};
|
|
8189
|
-
try {
|
|
8190
|
-
const url = new URL(callbackUrl);
|
|
8191
|
-
const queryParams = url.searchParams;
|
|
8192
|
-
if (queryParams.has("code")) {
|
|
8193
|
-
result.code = queryParams.get("code") || void 0;
|
|
8194
|
-
}
|
|
8195
|
-
if (queryParams.has("state")) {
|
|
8196
|
-
result.state = queryParams.get("state") || void 0;
|
|
8197
|
-
}
|
|
8198
|
-
if (queryParams.has("error")) {
|
|
8199
|
-
result.error = queryParams.get("error") || void 0;
|
|
8200
|
-
result.errorDescription = queryParams.get("error_description") || void 0;
|
|
8201
|
-
}
|
|
8202
|
-
if (url.hash) {
|
|
8203
|
-
const hashParams = new URLSearchParams(url.hash.slice(1));
|
|
8204
|
-
if (hashParams.has("access_token")) {
|
|
8205
|
-
result.accessToken = hashParams.get("access_token") || void 0;
|
|
8206
|
-
result.tokenType = hashParams.get("token_type") || "Bearer";
|
|
8207
|
-
const expiresIn = hashParams.get("expires_in");
|
|
8208
|
-
if (expiresIn) {
|
|
8209
|
-
result.expiresIn = parseInt(expiresIn, 10);
|
|
8210
|
-
}
|
|
8211
|
-
result.refreshToken = hashParams.get("refresh_token") || void 0;
|
|
8212
|
-
}
|
|
8213
|
-
if (hashParams.has("state") && !result.state) {
|
|
8214
|
-
result.state = hashParams.get("state") || void 0;
|
|
8215
|
-
}
|
|
8216
|
-
if (hashParams.has("error") && !result.error) {
|
|
8217
|
-
result.error = hashParams.get("error") || void 0;
|
|
8218
|
-
result.errorDescription = hashParams.get("error_description") || void 0;
|
|
8219
|
-
}
|
|
8220
|
-
}
|
|
8221
|
-
} catch (e) {
|
|
8222
|
-
const parts = callbackUrl.split("#");
|
|
8223
|
-
if (parts.length > 1) {
|
|
8224
|
-
const hashParams = new URLSearchParams(parts[1]);
|
|
8225
|
-
result.accessToken = hashParams.get("access_token") || void 0;
|
|
8226
|
-
result.tokenType = hashParams.get("token_type") || void 0;
|
|
8227
|
-
result.state = hashParams.get("state") || void 0;
|
|
8228
|
-
result.error = hashParams.get("error") || void 0;
|
|
8229
|
-
result.errorDescription = hashParams.get("error_description") || void 0;
|
|
8230
|
-
const expiresIn = hashParams.get("expires_in");
|
|
8231
|
-
if (expiresIn) {
|
|
8232
|
-
result.expiresIn = parseInt(expiresIn, 10);
|
|
8233
|
-
}
|
|
8234
|
-
}
|
|
8235
|
-
const queryParts = callbackUrl.split("?");
|
|
8236
|
-
if (queryParts.length > 1) {
|
|
8237
|
-
const queryStr = queryParts[1].split("#")[0];
|
|
8238
|
-
const queryParams = new URLSearchParams(queryStr);
|
|
8239
|
-
if (!result.code) result.code = queryParams.get("code") || void 0;
|
|
8240
|
-
if (!result.state) result.state = queryParams.get("state") || void 0;
|
|
8241
|
-
if (!result.error) result.error = queryParams.get("error") || void 0;
|
|
8242
|
-
if (!result.errorDescription) result.errorDescription = queryParams.get("error_description") || void 0;
|
|
8243
|
-
}
|
|
8244
|
-
}
|
|
8245
|
-
return result;
|
|
8246
|
-
}
|
|
8247
|
-
/**
|
|
8248
|
-
* Handle a callback URL directly (convenience method combining parse + exchange/handleImplicit).
|
|
8249
|
-
*
|
|
8250
|
-
* @param callbackUrl - Full callback URL received from OAuth provider
|
|
8251
|
-
* @returns Exchange result with bit ID and tokens
|
|
8252
|
-
*/
|
|
8253
|
-
async handleCallback(callbackUrl) {
|
|
8254
|
-
const params = this.parseCallbackUrl(callbackUrl);
|
|
8255
|
-
if (params.error) {
|
|
8256
|
-
throw new Error(`OAuth error: ${params.error}${params.errorDescription ? ` - ${params.errorDescription}` : ""}`);
|
|
8257
|
-
}
|
|
8258
|
-
if (params.accessToken && params.state) {
|
|
8259
|
-
return this.handleImplicitCallback(params);
|
|
8260
|
-
}
|
|
8261
|
-
if (!params.code || !params.state) {
|
|
8262
|
-
throw new Error("Missing code or state parameter in OAuth callback");
|
|
8263
|
-
}
|
|
8264
|
-
return this.exchangeCode(params.state, params.code);
|
|
8265
|
-
}
|
|
8266
|
-
/**
|
|
8267
|
-
* Handle implicit flow callback (access token directly in callback URL).
|
|
8268
|
-
*/
|
|
8269
|
-
handleImplicitCallback(params) {
|
|
8270
|
-
if (!params.state || !params.accessToken) {
|
|
8271
|
-
throw new Error("Missing state or access_token for implicit flow");
|
|
8272
|
-
}
|
|
8273
|
-
const oauthState = this.pendingFlows.get(params.state);
|
|
8274
|
-
if (!oauthState) {
|
|
8275
|
-
throw new Error("Invalid or expired OAuth state. Please restart the authorization flow.");
|
|
8276
|
-
}
|
|
8277
|
-
this.pendingFlows.delete(params.state);
|
|
8278
|
-
const tokens = {
|
|
8279
|
-
accessToken: params.accessToken,
|
|
8280
|
-
refreshToken: params.refreshToken,
|
|
8281
|
-
tokenType: params.tokenType || "Bearer",
|
|
8282
|
-
expiresAt: params.expiresIn ? Date.now() + params.expiresIn * 1e3 : void 0
|
|
8283
|
-
};
|
|
8284
|
-
oauthTokenStore.setToken(oauthState.bitId, tokens, oauthState.config);
|
|
8285
|
-
this.logger.info("OAuth implicit flow completed successfully", { bitId: oauthState.bitId });
|
|
8286
|
-
return { bitId: oauthState.bitId, tokens };
|
|
8287
|
-
}
|
|
8288
|
-
/**
|
|
8289
|
-
* Exchange authorization code for tokens.
|
|
8290
|
-
*
|
|
8291
|
-
* @param state - State parameter from callback
|
|
8292
|
-
* @param code - Authorization code from callback
|
|
8293
|
-
* @returns Token set or throws error
|
|
8294
|
-
*/
|
|
8295
|
-
async exchangeCode(state, code) {
|
|
8296
|
-
const oauthState = this.pendingFlows.get(state);
|
|
8297
|
-
if (!oauthState) {
|
|
8298
|
-
throw new Error("Invalid or expired OAuth state. Please restart the authorization flow.");
|
|
8299
|
-
}
|
|
8300
|
-
this.pendingFlows.delete(state);
|
|
8301
|
-
const { config, codeVerifier, redirectUri, bitId } = oauthState;
|
|
8302
|
-
const params = new URLSearchParams({
|
|
8303
|
-
grant_type: "authorization_code",
|
|
8304
|
-
code,
|
|
8305
|
-
redirect_uri: redirectUri,
|
|
8306
|
-
client_id: config.clientId
|
|
8307
|
-
});
|
|
8308
|
-
if (codeVerifier) {
|
|
8309
|
-
params.set("code_verifier", codeVerifier);
|
|
8310
|
-
}
|
|
8311
|
-
if (config.clientSecret) {
|
|
8312
|
-
params.set("client_secret", config.clientSecret);
|
|
8313
|
-
}
|
|
8314
|
-
this.logger.info("Exchanging authorization code", { bitId });
|
|
8315
|
-
const response = await fetch(config.tokenUrl, {
|
|
8316
|
-
method: "POST",
|
|
8317
|
-
headers: {
|
|
8318
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
8319
|
-
"Accept": "application/json"
|
|
8320
|
-
},
|
|
8321
|
-
body: params.toString()
|
|
8322
|
-
});
|
|
8323
|
-
if (!response.ok) {
|
|
8324
|
-
const errorText = await response.text();
|
|
8325
|
-
this.logger.error("Token exchange failed", { bitId, status: response.status, error: errorText });
|
|
8326
|
-
throw new Error(`Token exchange failed: ${response.status} - ${errorText}`);
|
|
8327
|
-
}
|
|
8328
|
-
const data = await response.json();
|
|
8329
|
-
const tokens = {
|
|
8330
|
-
accessToken: data.access_token,
|
|
8331
|
-
refreshToken: data.refresh_token,
|
|
8332
|
-
tokenType: data.token_type || "Bearer",
|
|
8333
|
-
expiresAt: data.expires_in ? Date.now() + data.expires_in * 1e3 : void 0,
|
|
8334
|
-
scope: data.scope
|
|
8335
|
-
};
|
|
8336
|
-
oauthTokenStore.setToken(bitId, tokens, config);
|
|
8337
|
-
this.logger.info("OAuth flow completed successfully", { bitId });
|
|
8338
|
-
return { bitId, tokens };
|
|
8339
|
-
}
|
|
8340
|
-
/**
|
|
8341
|
-
* Get the pending OAuth state for a given state parameter.
|
|
8342
|
-
* Useful for validation without consuming the state.
|
|
8343
|
-
*/
|
|
8344
|
-
getPendingFlow(state) {
|
|
8345
|
-
return this.pendingFlows.get(state);
|
|
8346
|
-
}
|
|
8347
|
-
/**
|
|
8348
|
-
* Check if a flow is pending for a bit.
|
|
8349
|
-
*/
|
|
8350
|
-
hasPendingFlow(bitId) {
|
|
8351
|
-
for (const state of this.pendingFlows.values()) {
|
|
8352
|
-
if (state.bitId === bitId) {
|
|
8353
|
-
return true;
|
|
8354
|
-
}
|
|
8355
|
-
}
|
|
8356
|
-
return false;
|
|
8357
|
-
}
|
|
8358
|
-
/**
|
|
8359
|
-
* Get pending authorization URL for a bit (if flow already initiated).
|
|
8360
|
-
*/
|
|
8361
|
-
getPendingAuthUrl(bitId) {
|
|
8362
|
-
for (const [state, oauthState] of this.pendingFlows.entries()) {
|
|
8363
|
-
if (oauthState.bitId === bitId) {
|
|
8364
|
-
const { config, codeChallenge, redirectUri } = oauthState;
|
|
8365
|
-
const params = new URLSearchParams({
|
|
8366
|
-
response_type: "code",
|
|
8367
|
-
client_id: config.clientId,
|
|
8368
|
-
redirect_uri: redirectUri,
|
|
8369
|
-
scope: config.scopes.join(" "),
|
|
8370
|
-
state
|
|
8371
|
-
});
|
|
8372
|
-
if (codeChallenge) {
|
|
8373
|
-
params.set("code_challenge", codeChallenge);
|
|
8374
|
-
params.set("code_challenge_method", "S256");
|
|
8375
|
-
}
|
|
8376
|
-
if (config.extraAuthParams) {
|
|
8377
|
-
for (const [key, value] of Object.entries(config.extraAuthParams)) {
|
|
8378
|
-
params.set(key, value);
|
|
8379
|
-
}
|
|
8380
|
-
}
|
|
8381
|
-
return `${config.authorizationUrl}?${params.toString()}`;
|
|
8382
|
-
}
|
|
8383
|
-
}
|
|
8384
|
-
return null;
|
|
8385
|
-
}
|
|
8386
|
-
/**
|
|
8387
|
-
* Cancel a pending OAuth flow.
|
|
8388
|
-
*/
|
|
8389
|
-
cancelFlow(state) {
|
|
8390
|
-
return this.pendingFlows.delete(state);
|
|
8391
|
-
}
|
|
8392
|
-
/**
|
|
8393
|
-
* Cancel all pending flows for a bit.
|
|
8394
|
-
*/
|
|
8395
|
-
cancelFlowsForBit(bitId) {
|
|
8396
|
-
let cancelled = 0;
|
|
8397
|
-
for (const [state, oauthState] of this.pendingFlows.entries()) {
|
|
8398
|
-
if (oauthState.bitId === bitId) {
|
|
8399
|
-
this.pendingFlows.delete(state);
|
|
8400
|
-
cancelled++;
|
|
8401
|
-
}
|
|
8402
|
-
}
|
|
8403
|
-
return cancelled;
|
|
8404
|
-
}
|
|
8405
|
-
/**
|
|
8406
|
-
* Clean up expired flows (older than 10 minutes).
|
|
8407
|
-
*/
|
|
8408
|
-
cleanupExpiredFlows() {
|
|
8409
|
-
const now = Date.now();
|
|
8410
|
-
const expirationTime = 10 * 60 * 1e3;
|
|
8411
|
-
let cleaned = 0;
|
|
8412
|
-
for (const [state, oauthState] of this.pendingFlows.entries()) {
|
|
8413
|
-
if (now - oauthState.createdAt > expirationTime) {
|
|
8414
|
-
this.pendingFlows.delete(state);
|
|
8415
|
-
this.logger.debug("Expired OAuth flow cleaned up", { bitId: oauthState.bitId });
|
|
8416
|
-
cleaned++;
|
|
8417
|
-
}
|
|
8418
|
-
}
|
|
8419
|
-
return cleaned;
|
|
8420
|
-
}
|
|
8421
|
-
/**
|
|
8422
|
-
* Get all pending flow states (for debugging/status).
|
|
8423
|
-
*/
|
|
8424
|
-
getPendingFlowStates() {
|
|
8425
|
-
return Array.from(this.pendingFlows.entries()).map(([state, oauthState]) => ({
|
|
8426
|
-
bitId: oauthState.bitId,
|
|
8427
|
-
state,
|
|
8428
|
-
createdAt: oauthState.createdAt,
|
|
8429
|
-
redirectUri: oauthState.redirectUri
|
|
8430
|
-
}));
|
|
8431
|
-
}
|
|
8432
|
-
};
|
|
8433
|
-
|
|
8434
|
-
// packages/core/src/types.ts
|
|
8435
|
-
function isFrontendWorkflow(workflow) {
|
|
8436
|
-
return "nodes" in workflow && "edges" in workflow && Array.isArray(workflow.nodes) && Array.isArray(workflow.edges);
|
|
8437
|
-
}
|
|
8438
6961
|
// Annotate the CommonJS export names for ESM import in node:
|
|
8439
6962
|
0 && (module.exports = {
|
|
8440
6963
|
AuthenticationType,
|
|
@@ -8442,11 +6965,8 @@ function isFrontendWorkflow(workflow) {
|
|
|
8442
6965
|
BitCategory,
|
|
8443
6966
|
HabitsExecutor,
|
|
8444
6967
|
HttpMethod,
|
|
8445
|
-
LoggerFactory,
|
|
8446
|
-
OAuthFlowManager,
|
|
8447
6968
|
PieceAuth,
|
|
8448
6969
|
PieceCategory,
|
|
8449
|
-
PollingStore,
|
|
8450
6970
|
Property,
|
|
8451
6971
|
StoreScope,
|
|
8452
6972
|
TriggerHookType,
|
|
@@ -8461,29 +6981,20 @@ function isFrontendWorkflow(workflow) {
|
|
|
8461
6981
|
createBitTrigger,
|
|
8462
6982
|
createCustomApiCallAction,
|
|
8463
6983
|
createPiece,
|
|
8464
|
-
createPollingStore,
|
|
8465
6984
|
createTrigger,
|
|
8466
6985
|
customRequire,
|
|
8467
|
-
discoverOAuthRequirements,
|
|
8468
6986
|
ensureModuleInstalled,
|
|
8469
6987
|
executeActivepiecesModule,
|
|
8470
6988
|
executeBitsModule,
|
|
8471
6989
|
executeN8nModule,
|
|
8472
6990
|
executeScriptModule,
|
|
8473
|
-
extractBitsPieceFromModule,
|
|
8474
|
-
getBundledModule,
|
|
8475
6991
|
getLocalModulePath,
|
|
8476
6992
|
getModuleFullPath,
|
|
8477
6993
|
getNodesBasePath,
|
|
8478
6994
|
getNodesPath,
|
|
8479
6995
|
getSecurityConfig,
|
|
8480
6996
|
httpClient,
|
|
8481
|
-
isBundledModule,
|
|
8482
6997
|
isFrontendWorkflow,
|
|
8483
|
-
oauthTokenStore,
|
|
8484
|
-
pieceFromModule,
|
|
8485
|
-
printOAuthRequirements,
|
|
8486
|
-
registerBundledModule,
|
|
8487
6998
|
registerCortexModule,
|
|
8488
6999
|
scanInputForSecurity,
|
|
8489
7000
|
triggerHelper
|