@powerhousedao/reactor-workflow 6.2.3-dev.11

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.
@@ -0,0 +1,1419 @@
1
+ import { A as REACTOR_MODEL, C as jsonSafe, D as REACTOR_EXECUTE, E as REACTOR_CREATE, M as STORE_DELETE, N as STORE_GET, O as REACTOR_FIND, P as STORE_PUT, T as OUTPUT_UPDATE, a as redactMessage, c as DEFAULT_EGRESS_POLICY, g as rewriteFileRefs, i as redactError, j as REACTOR_MODELS, k as REACTOR_GET, n as containsRedactedMarker, o as rememberSecrets, r as redact, s as secretsFor, t as collectSecretValues, w as LOG_WRITE } from "./redact-C7LWgAyD.js";
2
+ import { createRequire } from "node:module";
3
+ import { AppConnectionType } from "@powerhousedao/pieces-framework";
4
+ import { execFile, fork } from "node:child_process";
5
+ import { existsSync, readFileSync } from "node:fs";
6
+ import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
7
+ import path, { dirname, isAbsolute, join } from "node:path";
8
+ import { promisify } from "node:util";
9
+ import { gunzip } from "node:zlib";
10
+ import { fileURLToPath, pathToFileURL } from "node:url";
11
+ import { randomUUID } from "node:crypto";
12
+ import { childLogger } from "document-model";
13
+ //#region src/pieces/activepieces/fetch.ts
14
+ const gunzip$1 = promisify(gunzip);
15
+ const CDN_PIECES_URL = "https://cdn.activepieces.com/pieces/bundled/";
16
+ const NPM_REGISTRY_URL = "https://registry.npmjs.org";
17
+ function cdnTarballUrl(name, version) {
18
+ return `${CDN_PIECES_URL}${name.replace("/", "-")}-${version}.tgz`;
19
+ }
20
+ function npmTarballUrl(name, version) {
21
+ return `${NPM_REGISTRY_URL}/${name}/-/${name.startsWith("@") ? name.split("/")[1] : name}-${version}.tgz`;
22
+ }
23
+ const NPM_NAME_RE = /^(@[a-z0-9][a-z0-9-_.]*\/)?[a-z0-9][a-z0-9-_.]*$/;
24
+ const NPM_VERSION_RE = /^[A-Za-z0-9][A-Za-z0-9.+-]*$/;
25
+ function assertValidPackageCoordinate(name, version) {
26
+ if (!NPM_NAME_RE.test(name)) throw new Error(`Invalid piece package name: ${name}`);
27
+ if (!NPM_VERSION_RE.test(version)) throw new Error(`Invalid piece package version: ${version}`);
28
+ }
29
+ function assertWithinCacheDir(dir, cacheDir) {
30
+ const root = path.resolve(cacheDir);
31
+ const resolved = path.resolve(dir);
32
+ if (resolved !== root && !resolved.startsWith(root + path.sep)) throw new Error(`Resolved piece cache path escapes cacheDir: ${dir}`);
33
+ }
34
+ function readString(header, offset, length) {
35
+ const slice = header.subarray(offset, offset + length);
36
+ const nul = slice.indexOf(0);
37
+ return slice.subarray(0, nul === -1 ? length : nul).toString("utf8");
38
+ }
39
+ const MAX_EXTRACTED_BYTES = 64 * 1024 * 1024;
40
+ const MAX_COMPRESSED_BYTES = 64 * 1024 * 1024;
41
+ async function extractTarball(tgz, dest) {
42
+ const tar = await gunzip$1(tgz, { maxOutputLength: MAX_EXTRACTED_BYTES }).catch((error) => {
43
+ throw new Error(`Failed to decompress piece bundle: ${String(error)}`);
44
+ });
45
+ let offset = 0;
46
+ while (offset + 512 <= tar.length) {
47
+ const header = tar.subarray(offset, offset + 512);
48
+ if (header.every((byte) => byte === 0)) break;
49
+ const name = readString(header, 0, 100);
50
+ const prefix = readString(header, 345, 155);
51
+ const size = parseInt(readString(header, 124, 12).trim() || "0", 8);
52
+ const type = String.fromCharCode(header[156]);
53
+ offset += 512;
54
+ if ((type === "0" || type === "\0" || type === "") && size >= 0) {
55
+ const rel = (prefix ? `${prefix}/${name}` : name).replace(/^[^/]+\//, "");
56
+ const target = path.resolve(dest, rel);
57
+ if (rel && target.startsWith(path.resolve(dest) + path.sep)) {
58
+ await mkdir(path.dirname(target), { recursive: true });
59
+ await writeFile(target, tar.subarray(offset, offset + size));
60
+ }
61
+ }
62
+ offset += Math.ceil(size / 512) * 512;
63
+ }
64
+ }
65
+ async function readBoundedBody(response, url) {
66
+ const declared = response.headers.get("content-length");
67
+ if (declared && Number(declared) > MAX_COMPRESSED_BYTES) throw new Error(`${url} exceeds the ${MAX_COMPRESSED_BYTES}-byte compressed size cap (Content-Length: ${declared})`);
68
+ if (!response.body) return Buffer.from(await response.arrayBuffer());
69
+ const reader = response.body.getReader();
70
+ const chunks = [];
71
+ let total = 0;
72
+ for (;;) {
73
+ const { done, value } = await reader.read();
74
+ if (done) break;
75
+ total += value.byteLength;
76
+ if (total > MAX_COMPRESSED_BYTES) {
77
+ await reader.cancel();
78
+ throw new Error(`${url} exceeds the ${MAX_COMPRESSED_BYTES}-byte compressed size cap`);
79
+ }
80
+ chunks.push(value);
81
+ }
82
+ return Buffer.concat(chunks);
83
+ }
84
+ async function downloadTarball(name, version, timeoutMs) {
85
+ const sources = [{
86
+ source: "cdn",
87
+ url: cdnTarballUrl(name, version)
88
+ }, {
89
+ source: "npm",
90
+ url: npmTarballUrl(name, version)
91
+ }];
92
+ let lastError;
93
+ for (const { source, url } of sources) try {
94
+ const response = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
95
+ if (!response.ok) {
96
+ lastError = /* @__PURE__ */ new Error(`${url} responded ${response.status}`);
97
+ continue;
98
+ }
99
+ return {
100
+ tgz: await readBoundedBody(response, url),
101
+ source
102
+ };
103
+ } catch (error) {
104
+ lastError = error;
105
+ }
106
+ throw new Error(`Failed to fetch piece bundle ${name}@${version}: ${String(lastError)}`);
107
+ }
108
+ async function fetchPieceBundle(options) {
109
+ const { name, version, cacheDir, timeoutMs = 3e4 } = options;
110
+ assertValidPackageCoordinate(name, version);
111
+ const dir = path.join(cacheDir, `${name.replace("/", "-")}-${version}`);
112
+ assertWithinCacheDir(dir, cacheDir);
113
+ if (existsSync(path.join(dir, "package.json"))) return {
114
+ dir,
115
+ source: "cache",
116
+ dependencies: await readDependencies(dir),
117
+ installed: false
118
+ };
119
+ const { tgz, source } = await downloadTarball(name, version, timeoutMs);
120
+ const staging = `${dir}.tmp-${process.pid}`;
121
+ await rm(staging, {
122
+ recursive: true,
123
+ force: true
124
+ });
125
+ await extractTarball(tgz, staging);
126
+ await rm(dir, {
127
+ recursive: true,
128
+ force: true
129
+ });
130
+ try {
131
+ await rename(staging, dir);
132
+ } catch (error) {
133
+ await rm(staging, {
134
+ recursive: true,
135
+ force: true
136
+ });
137
+ if (!existsSync(path.join(dir, "package.json"))) throw error;
138
+ }
139
+ return {
140
+ dir,
141
+ source,
142
+ dependencies: await readDependencies(dir),
143
+ installed: false
144
+ };
145
+ }
146
+ async function installPieceBundle(options) {
147
+ const { name, version, cacheDir, timeoutMs = 12e4 } = options;
148
+ assertValidPackageCoordinate(name, version);
149
+ const workspace = path.join(cacheDir, `${name.replace("/", "-")}-${version}.install`);
150
+ assertWithinCacheDir(workspace, cacheDir);
151
+ const dir = path.join(workspace, "node_modules", name);
152
+ if (existsSync(path.join(workspace, "ready")) && existsSync(path.join(dir, "package.json"))) return {
153
+ dir,
154
+ source: "cache",
155
+ dependencies: await readDependencies(dir),
156
+ installed: true
157
+ };
158
+ const { tgz, source } = await downloadTarball(name, version, timeoutMs);
159
+ await rm(workspace, {
160
+ recursive: true,
161
+ force: true
162
+ });
163
+ await mkdir(workspace, { recursive: true });
164
+ await writeFile(path.join(workspace, "bundle.tgz"), tgz);
165
+ await writeFile(path.join(workspace, "package.json"), JSON.stringify({
166
+ name: "piece-workspace",
167
+ version: "1.0.0",
168
+ private: true,
169
+ dependencies: { [name]: "file:./bundle.tgz" }
170
+ }));
171
+ await runNpmInstall(workspace, timeoutMs);
172
+ await writeFile(path.join(workspace, "ready"), "true");
173
+ return {
174
+ dir,
175
+ source,
176
+ dependencies: await readDependencies(dir),
177
+ installed: true
178
+ };
179
+ }
180
+ const inFlight = /* @__PURE__ */ new Map();
181
+ async function ensurePieceBundle(options) {
182
+ const key = `${options.cacheDir}\u0000${options.name}@${options.version}`;
183
+ const pending = inFlight.get(key);
184
+ if (pending) return pending;
185
+ const started = resolveBundle(options).finally(() => inFlight.delete(key));
186
+ inFlight.set(key, started);
187
+ return started;
188
+ }
189
+ async function resolveBundle(options) {
190
+ const fetched = await fetchPieceBundle(options);
191
+ if (Object.keys(fetched.dependencies).length === 0) return fetched;
192
+ return installPieceBundle(options);
193
+ }
194
+ async function runNpmInstall(cwd, timeoutMs) {
195
+ const windows = process.platform === "win32";
196
+ await new Promise((resolve, reject) => {
197
+ execFile(windows ? "npm.cmd" : "npm", [
198
+ "install",
199
+ "--ignore-scripts",
200
+ "--no-audit",
201
+ "--no-fund",
202
+ "--loglevel=error"
203
+ ], {
204
+ cwd,
205
+ timeout: timeoutMs,
206
+ shell: windows
207
+ }, (error, _stdout, stderr) => {
208
+ if (error) reject(/* @__PURE__ */ new Error(`npm install failed in ${cwd}: ${stderr || error.message}`));
209
+ else resolve();
210
+ });
211
+ });
212
+ }
213
+ async function readDependencies(dir) {
214
+ const raw = await readFile(path.join(dir, "package.json"), "utf8");
215
+ return JSON.parse(raw).dependencies ?? {};
216
+ }
217
+ //#endregion
218
+ //#region src/pieces/activepieces/resolver.ts
219
+ function bundleResolver(options) {
220
+ return { async resolve(name, version) {
221
+ return {
222
+ name,
223
+ version,
224
+ bundleDir: (await ensurePieceBundle({
225
+ name,
226
+ version,
227
+ cacheDir: options.cacheDir,
228
+ ...options.timeoutMs ? { timeoutMs: options.timeoutMs } : {}
229
+ })).dir,
230
+ local: false
231
+ };
232
+ } };
233
+ }
234
+ function localFirstResolver(lookup, fallback) {
235
+ return { async resolve(name, version) {
236
+ const local = await lookup(name);
237
+ if (!local) return fallback.resolve(name, version);
238
+ return {
239
+ name,
240
+ version: local.version,
241
+ ...local.entryPath ? { entryPath: local.entryPath } : {},
242
+ ...local.bundleDir ? { bundleDir: local.bundleDir } : {},
243
+ local: true
244
+ };
245
+ } };
246
+ }
247
+ function pieceModuleRef(piece) {
248
+ return piece.entryPath ? { entryPath: piece.entryPath } : { bundleDir: piece.bundleDir };
249
+ }
250
+ //#endregion
251
+ //#region src/pieces/activepieces/worker/transport.ts
252
+ function defaultEntryPath() {
253
+ let dir = path.dirname(fileURLToPath(import.meta.url));
254
+ while (!existsSync(path.join(dir, "package.json"))) {
255
+ const parent = path.dirname(dir);
256
+ if (parent === dir) throw new Error("Could not locate package root");
257
+ dir = parent;
258
+ }
259
+ const entry = path.join(dir, "dist", "worker-entry.js");
260
+ if (!existsSync(entry)) throw new Error(`Worker entry not built at ${entry} — run pnpm build`);
261
+ return entry;
262
+ }
263
+ function createForkTransport(entryPath) {
264
+ const child = fork(entryPath, [], {
265
+ env: {},
266
+ execArgv: [],
267
+ serialization: "advanced",
268
+ stdio: [
269
+ "ignore",
270
+ "ignore",
271
+ "ignore",
272
+ "ipc"
273
+ ]
274
+ });
275
+ const exitListeners = /* @__PURE__ */ new Map();
276
+ return {
277
+ get connected() {
278
+ return child.connected;
279
+ },
280
+ send(message) {
281
+ child.send(message);
282
+ },
283
+ on(event, listener) {
284
+ if (event === "exit") {
285
+ const adapted = (code, signal) => listener({
286
+ code,
287
+ signal
288
+ });
289
+ exitListeners.set(listener, adapted);
290
+ child.on("exit", adapted);
291
+ return;
292
+ }
293
+ child.on("message", listener);
294
+ },
295
+ off(event, listener) {
296
+ if (event === "exit") {
297
+ const adapted = exitListeners.get(listener);
298
+ if (adapted) {
299
+ child.off("exit", adapted);
300
+ exitListeners.delete(listener);
301
+ }
302
+ return;
303
+ }
304
+ child.off("message", listener);
305
+ },
306
+ kill() {
307
+ child.kill("SIGKILL");
308
+ }
309
+ };
310
+ }
311
+ //#endregion
312
+ //#region src/pieces/activepieces/worker/host.ts
313
+ var PieceWorkerError = class extends Error {
314
+ serialized;
315
+ constructor(serialized) {
316
+ super(`${serialized.name}: ${serialized.message}`);
317
+ this.name = "PieceWorkerError";
318
+ this.serialized = serialized;
319
+ }
320
+ };
321
+ var PieceWorkerTimeoutError = class extends Error {
322
+ constructor(timeoutMs) {
323
+ super(`Piece action timed out after ${timeoutMs}ms; worker was replaced`);
324
+ this.name = "PieceWorkerTimeoutError";
325
+ }
326
+ };
327
+ var PieceWorkerExitError = class extends Error {
328
+ constructor(code, signal) {
329
+ super(`Piece worker exited unexpectedly (code=${code}, signal=${signal})`);
330
+ this.name = "PieceWorkerExitError";
331
+ }
332
+ };
333
+ function serveNotify(message, handlers) {
334
+ const handler = handlers?.[message.method];
335
+ if (!handler) return;
336
+ try {
337
+ Promise.resolve(handler(message.payload)).catch(() => void 0);
338
+ } catch {}
339
+ }
340
+ var PieceWorker = class {
341
+ connect;
342
+ defaultTimeoutMs;
343
+ worker;
344
+ queue = Promise.resolve();
345
+ nextId = 1;
346
+ constructor(options = {}) {
347
+ const entryPath = options.entryPath;
348
+ this.connect = options.transport ?? (() => createForkTransport(entryPath ?? defaultEntryPath()));
349
+ this.defaultTimeoutMs = options.defaultTimeoutMs ?? 3e4;
350
+ }
351
+ runAction(request, options = {}) {
352
+ return this.enqueue("run", request, options.timeoutMs, options);
353
+ }
354
+ resolveOptions(request, options = {}) {
355
+ return this.enqueue("resolve-options", request, options.timeoutMs, options);
356
+ }
357
+ checkConnection(request, options = {}) {
358
+ return this.enqueue("check-connection", request, options.timeoutMs);
359
+ }
360
+ describePiece(request, options = {}) {
361
+ return this.enqueue("describe", request, options.timeoutMs);
362
+ }
363
+ runTriggerHook(request, options = {}) {
364
+ return this.enqueue("trigger-hook", request, options.timeoutMs, options);
365
+ }
366
+ enqueue(type, request, timeoutMs, taps = {}) {
367
+ const run = this.queue.then(() => this.execute(type, request, timeoutMs ?? this.defaultTimeoutMs, taps));
368
+ this.queue = run.catch(() => void 0);
369
+ return run;
370
+ }
371
+ dispose() {
372
+ this.worker?.kill();
373
+ this.worker = void 0;
374
+ }
375
+ spawn() {
376
+ if (this.worker) return this.worker;
377
+ const worker = this.connect();
378
+ const forget = () => {
379
+ if (this.worker === worker) this.worker = void 0;
380
+ worker.off("exit", forget);
381
+ };
382
+ worker.on("exit", forget);
383
+ this.worker = worker;
384
+ return worker;
385
+ }
386
+ execute(type, request, timeoutMs, taps) {
387
+ const worker = this.spawn();
388
+ const id = this.nextId++;
389
+ return new Promise((resolve, reject) => {
390
+ const timer = setTimeout(() => {
391
+ cleanup();
392
+ worker.kill();
393
+ this.worker = void 0;
394
+ reject(new PieceWorkerTimeoutError(timeoutMs));
395
+ }, timeoutMs);
396
+ const onMessage = (value) => {
397
+ const response = value;
398
+ if (response.type === "host-call" || response.type === "host-notify") return;
399
+ if (response.id !== id) return;
400
+ cleanup();
401
+ if (response.type === "result") resolve({
402
+ output: response.output,
403
+ touched: response.touched,
404
+ tlsPoisoned: response.tlsPoisoned,
405
+ files: response.files,
406
+ storeState: response.storeState,
407
+ schedules: response.schedules,
408
+ listeners: response.listeners
409
+ });
410
+ else reject(new PieceWorkerError(response.error));
411
+ };
412
+ const onExit = ({ code, signal }) => {
413
+ cleanup();
414
+ reject(new PieceWorkerExitError(code, signal));
415
+ };
416
+ const onHostCall = (value) => {
417
+ const message = value;
418
+ if (message.type === "host-call") {
419
+ this.serveHostCall(worker, message, taps.hostCalls);
420
+ return;
421
+ }
422
+ if (message.type === "host-notify") serveNotify(message, taps.notifications);
423
+ };
424
+ const cleanup = () => {
425
+ clearTimeout(timer);
426
+ worker.off("message", onMessage);
427
+ worker.off("message", onHostCall);
428
+ worker.off("exit", onExit);
429
+ };
430
+ worker.on("message", onMessage);
431
+ worker.on("message", onHostCall);
432
+ worker.on("exit", onExit);
433
+ worker.send(jsonSafe({
434
+ id,
435
+ type,
436
+ request
437
+ }));
438
+ });
439
+ }
440
+ async serveHostCall(worker, message, handlers) {
441
+ let response;
442
+ try {
443
+ const handler = handlers?.[message.method];
444
+ if (!handler) throw new Error(`No host handler for "${message.method}"`);
445
+ response = {
446
+ id: message.id,
447
+ type: "host-result",
448
+ value: await handler(message.payload)
449
+ };
450
+ } catch (error) {
451
+ response = {
452
+ id: message.id,
453
+ type: "host-result",
454
+ error: error instanceof Error ? error.message : String(error)
455
+ };
456
+ }
457
+ if (worker.connected) worker.send(response);
458
+ }
459
+ };
460
+ //#endregion
461
+ //#region src/pieces/activepieces/worker/pool.ts
462
+ var PieceWorkerPoolBusyError = class extends Error {
463
+ constructor(size, waiting) {
464
+ super(`All ${size} piece workers are busy and ${waiting} runs are already waiting`);
465
+ this.name = "PieceWorkerPoolBusyError";
466
+ }
467
+ };
468
+ var PieceWorkerSessionClosedError = class extends Error {
469
+ constructor() {
470
+ super("This piece worker session is closed");
471
+ this.name = "PieceWorkerSessionClosedError";
472
+ }
473
+ };
474
+ const DEFAULT_SIZE = 4;
475
+ var PieceWorkerSession = class {
476
+ worker;
477
+ taking;
478
+ held = false;
479
+ closed = false;
480
+ constructor(pool) {
481
+ this.pool = pool;
482
+ }
483
+ runAction(request, options) {
484
+ return this.request((worker) => worker.runAction(request, options));
485
+ }
486
+ resolveOptions(request, options) {
487
+ return this.request((worker) => worker.resolveOptions(request, options));
488
+ }
489
+ checkConnection(request, options) {
490
+ return this.request((worker) => worker.checkConnection(request, options));
491
+ }
492
+ describePiece(request, options) {
493
+ return this.request((worker) => worker.describePiece(request, options));
494
+ }
495
+ runTriggerHook(request, options) {
496
+ return this.request((worker) => worker.runTriggerHook(request, options));
497
+ }
498
+ close() {
499
+ if (this.closed) return;
500
+ this.closed = true;
501
+ this.pool.cancel(this);
502
+ this.worker?.dispose();
503
+ this.worker = void 0;
504
+ this.pool.forget(this);
505
+ this.giveBack();
506
+ }
507
+ dispose() {
508
+ this.close();
509
+ }
510
+ async request(fn) {
511
+ const worker = await this.take();
512
+ if (this.closed) throw new PieceWorkerSessionClosedError();
513
+ return fn(worker);
514
+ }
515
+ take() {
516
+ if (this.closed) return Promise.reject(new PieceWorkerSessionClosedError());
517
+ if (this.worker) return Promise.resolve(this.worker);
518
+ this.taking ??= this.acquire();
519
+ return this.taking;
520
+ }
521
+ async acquire() {
522
+ await this.pool.acquire(this);
523
+ this.held = true;
524
+ if (this.closed) {
525
+ this.giveBack();
526
+ throw new PieceWorkerSessionClosedError();
527
+ }
528
+ this.worker = this.pool.build();
529
+ return this.worker;
530
+ }
531
+ giveBack() {
532
+ if (!this.held) return;
533
+ this.held = false;
534
+ this.pool.release();
535
+ }
536
+ };
537
+ var PieceWorkerPool = class {
538
+ size;
539
+ maxQueueDepth;
540
+ createWorker;
541
+ workerOptions;
542
+ sessions = /* @__PURE__ */ new Set();
543
+ waiters = [];
544
+ inUse = 0;
545
+ disposed = false;
546
+ constructor(options = {}) {
547
+ const { size, maxQueueDepth, createWorker, ...workerOptions } = options;
548
+ this.size = Math.max(1, Math.trunc(size ?? DEFAULT_SIZE));
549
+ this.maxQueueDepth = Math.max(0, Math.trunc(maxQueueDepth ?? 0));
550
+ this.createWorker = createWorker ?? ((o) => new PieceWorker(o));
551
+ this.workerOptions = workerOptions;
552
+ }
553
+ session() {
554
+ if (this.disposed) throw new Error("Piece worker pool is disposed");
555
+ const session = new PieceWorkerSession(this);
556
+ this.sessions.add(session);
557
+ return session;
558
+ }
559
+ stats() {
560
+ return {
561
+ size: this.size,
562
+ active: this.inUse,
563
+ waiting: this.waiters.length
564
+ };
565
+ }
566
+ dispose() {
567
+ this.disposed = true;
568
+ for (const waiter of this.waiters.splice(0)) waiter.reject(/* @__PURE__ */ new Error("Piece worker pool is disposed"));
569
+ for (const session of [...this.sessions]) session.close();
570
+ this.sessions.clear();
571
+ }
572
+ /** @internal — a session taking its slot. */
573
+ acquire(session) {
574
+ if (this.disposed) return Promise.reject(/* @__PURE__ */ new Error("Piece worker pool is disposed"));
575
+ if (this.inUse < this.size) {
576
+ this.inUse++;
577
+ return Promise.resolve();
578
+ }
579
+ if (this.maxQueueDepth > 0 && this.waiters.length >= this.maxQueueDepth) return Promise.reject(new PieceWorkerPoolBusyError(this.size, this.waiters.length));
580
+ return new Promise((resolve, reject) => {
581
+ this.waiters.push({
582
+ session,
583
+ resolve,
584
+ reject
585
+ });
586
+ });
587
+ }
588
+ /** @internal — a session that closed while queueing. */
589
+ cancel(session) {
590
+ const index = this.waiters.findIndex((w) => w.session === session);
591
+ if (index < 0) return;
592
+ const [waiter] = this.waiters.splice(index, 1);
593
+ waiter.reject(new PieceWorkerSessionClosedError());
594
+ }
595
+ /** @internal — a session giving its slot back. */
596
+ release() {
597
+ const next = this.waiters.shift();
598
+ if (next) {
599
+ next.resolve();
600
+ return;
601
+ }
602
+ this.inUse = Math.max(0, this.inUse - 1);
603
+ }
604
+ /** @internal — one child for one session. */
605
+ build() {
606
+ return this.createWorker(this.workerOptions);
607
+ }
608
+ /** @internal — a session that closed itself. */
609
+ forget(session) {
610
+ this.sessions.delete(session);
611
+ }
612
+ };
613
+ //#endregion
614
+ //#region src/pieces/engine/secrets.ts
615
+ const SECRET_REF_PREFIX = "secret://v1:";
616
+ const SECRET_ID_PATTERN = /^[0-9a-f]{32}$/;
617
+ var SecretNotFoundError = class extends Error {
618
+ constructor(ref) {
619
+ super(`No secret found for ref "${ref}"`);
620
+ this.name = "SecretNotFoundError";
621
+ }
622
+ };
623
+ var SecretDeletedError = class extends Error {
624
+ constructor(ref) {
625
+ super(`Secret "${ref}" has been deleted`);
626
+ this.name = "SecretDeletedError";
627
+ }
628
+ };
629
+ var InvalidSecretRefError = class extends Error {
630
+ constructor(ref) {
631
+ super(`Invalid secret ref "${ref}"; expected ${SECRET_REF_PREFIX}<32 hex chars>`);
632
+ this.name = "InvalidSecretRefError";
633
+ }
634
+ };
635
+ function isSecretRef(ref) {
636
+ return ref.startsWith("secret://v1:") && SECRET_ID_PATTERN.test(ref.slice(12));
637
+ }
638
+ function parseSecretRef(ref) {
639
+ if (!isSecretRef(ref)) throw new InvalidSecretRefError(ref);
640
+ return ref.slice(12);
641
+ }
642
+ function secretRefFromId(id) {
643
+ if (!SECRET_ID_PATTERN.test(id)) throw new InvalidSecretRefError(id);
644
+ return `${SECRET_REF_PREFIX}${id}`;
645
+ }
646
+ var InMemorySecretProvider = class {
647
+ secrets;
648
+ constructor(secrets) {
649
+ this.secrets = new Map(Object.entries(secrets));
650
+ }
651
+ get(ref) {
652
+ const value = this.secrets.get(ref);
653
+ if (value === void 0) return Promise.reject(new SecretNotFoundError(ref));
654
+ return Promise.resolve(value);
655
+ }
656
+ };
657
+ //#endregion
658
+ //#region src/pieces/engine/expressions.ts
659
+ const WHOLE_EXPRESSION = /^\{\{\s*([^{}]+?)\s*\}\}$/;
660
+ const EMBEDDED_EXPRESSION = /\{\{\s*([^{}]+?)\s*\}\}/g;
661
+ function lookupPath(scope, path) {
662
+ let current = scope;
663
+ for (const segment of path.split(".")) {
664
+ if (current === null || typeof current !== "object") return void 0;
665
+ current = current[segment];
666
+ }
667
+ return current;
668
+ }
669
+ const STRING_LITERAL = /^(['"])(.*)\1$/;
670
+ function resolveExpression(scope, expression) {
671
+ for (const term of expression.split("||")) {
672
+ const trimmed = term.trim();
673
+ if (!trimmed) continue;
674
+ const literal = STRING_LITERAL.exec(trimmed);
675
+ const value = literal ? literal[2] : lookupPath(scope, trimmed);
676
+ if (value !== void 0 && value !== null && value !== "") return value;
677
+ }
678
+ }
679
+ function interpolate(value) {
680
+ if (value === void 0 || value === null) return "";
681
+ if (typeof value === "object") return JSON.stringify(value);
682
+ return String(value);
683
+ }
684
+ function resolveExpressions(value, scope) {
685
+ if (typeof value === "string") {
686
+ const whole = WHOLE_EXPRESSION.exec(value);
687
+ if (whole) return resolveExpression(scope, whole[1]);
688
+ return value.replaceAll(EMBEDDED_EXPRESSION, (_, path) => interpolate(resolveExpression(scope, path)));
689
+ }
690
+ if (Array.isArray(value)) return value.map((item) => resolveExpressions(item, scope));
691
+ if (value !== null && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, resolveExpressions(entry, scope)]));
692
+ return value;
693
+ }
694
+ function evaluateCondition(condition, scope) {
695
+ const resolved = resolveExpressions(condition, scope);
696
+ if (resolved === "false" || resolved === "0") return false;
697
+ return Boolean(resolved);
698
+ }
699
+ //#endregion
700
+ //#region src/pieces/engine/connections.ts
701
+ var ConnectionNotFoundError = class extends Error {
702
+ constructor(connectionId) {
703
+ super(`No connection registered for id "${connectionId}"`);
704
+ this.name = "ConnectionNotFoundError";
705
+ }
706
+ };
707
+ var ConnectionNotBoundError = class extends Error {
708
+ constructor(connectionId) {
709
+ super(`Connection "${connectionId}" is not bound to this workflow`);
710
+ this.name = "ConnectionNotBoundError";
711
+ }
712
+ };
713
+ var UnsupportedAuthTypeError = class extends Error {
714
+ constructor(authType) {
715
+ super(`Auth type "${authType}" is not supported yet`);
716
+ this.name = "UnsupportedAuthTypeError";
717
+ }
718
+ };
719
+ async function resolveSecrets(source, secrets) {
720
+ const entries = await Promise.all((source.secretRefs ?? []).map(async ({ name, ref }) => [name, await secrets.get(ref)]));
721
+ return Object.fromEntries(entries);
722
+ }
723
+ async function shapeConnection(source, secrets) {
724
+ const resolved = await resolveSecrets(source, secrets);
725
+ return {
726
+ auth: shapeAuth(source, resolved),
727
+ secretValues: Object.values(resolved)
728
+ };
729
+ }
730
+ function shapeAuth(source, resolved) {
731
+ switch (source.authType) {
732
+ case "NONE": return;
733
+ case "SECRET_TEXT": {
734
+ const values = Object.values(resolved);
735
+ if (values.length !== 1) throw new Error(`SECRET_TEXT connection must have exactly one secret ref, got ${values.length}`);
736
+ return {
737
+ type: AppConnectionType.SECRET_TEXT,
738
+ secret_text: values[0]
739
+ };
740
+ }
741
+ case "BASIC_AUTH": {
742
+ const props = {
743
+ ...source.config,
744
+ ...resolved
745
+ };
746
+ return {
747
+ type: AppConnectionType.BASIC_AUTH,
748
+ username: props.username,
749
+ password: props.password
750
+ };
751
+ }
752
+ case "CUSTOM_AUTH": return {
753
+ type: AppConnectionType.CUSTOM_AUTH,
754
+ props: {
755
+ ...source.config,
756
+ ...resolved
757
+ }
758
+ };
759
+ default: throw new UnsupportedAuthTypeError(source.authType);
760
+ }
761
+ }
762
+ var StaticConnectionResolver = class {
763
+ connections;
764
+ constructor(connections, secrets) {
765
+ this.secrets = secrets;
766
+ this.connections = new Map(Object.entries(connections));
767
+ }
768
+ resolve(connectionId) {
769
+ return this.resolveWithSecrets(connectionId).then((resolved) => resolved.auth);
770
+ }
771
+ resolveWithSecrets(connectionId) {
772
+ const source = this.connections.get(connectionId);
773
+ if (!source) return Promise.reject(new ConnectionNotFoundError(connectionId));
774
+ return shapeConnection(source, this.secrets);
775
+ }
776
+ };
777
+ function declaredConnectionIds(definition) {
778
+ const declared = /* @__PURE__ */ new Set();
779
+ const declare = (connectionId) => {
780
+ if (connectionId && !connectionId.includes("{{")) declared.add(connectionId);
781
+ };
782
+ declare(definition.trigger?.connectionId);
783
+ for (const step of definition.steps) declare(step.connectionId);
784
+ return declared;
785
+ }
786
+ var BoundConnectionResolver = class {
787
+ resolveWithSecrets;
788
+ constructor(inner, binding, onRefused) {
789
+ this.inner = inner;
790
+ this.binding = binding;
791
+ this.onRefused = onRefused;
792
+ const withSecrets = inner.resolveWithSecrets?.bind(inner);
793
+ if (!withSecrets) return;
794
+ this.resolveWithSecrets = (connectionId, request) => this.bound(connectionId, request) ? withSecrets(connectionId, request) : Promise.reject(new ConnectionNotBoundError(connectionId));
795
+ }
796
+ resolve(connectionId, request) {
797
+ if (!this.bound(connectionId, request)) return Promise.reject(new ConnectionNotBoundError(connectionId));
798
+ return this.inner.resolve(connectionId, request);
799
+ }
800
+ bound(connectionId, request) {
801
+ if (this.binding()?.has(connectionId)) return true;
802
+ this.onRefused?.(connectionId, request);
803
+ return false;
804
+ }
805
+ };
806
+ //#endregion
807
+ //#region src/pieces/engine/blocks.ts
808
+ var UnknownBlockTypeError = class extends Error {
809
+ constructor(blockType) {
810
+ super(`No executor registered for block type "${blockType}"`);
811
+ this.name = "UnknownBlockTypeError";
812
+ }
813
+ };
814
+ var TriggerBlockAsStepError = class extends Error {
815
+ constructor(blockType) {
816
+ super(`Trigger block type "${blockType}" cannot run as a workflow step`);
817
+ this.name = "TriggerBlockAsStepError";
818
+ }
819
+ };
820
+ var CoreBlockExecutor = class {
821
+ static handles(blockType) {
822
+ return blockType.startsWith("core#");
823
+ }
824
+ execute(execution) {
825
+ if (execution.blockType === "core#branch") {
826
+ const { condition, equals } = execution.config;
827
+ const normalize = (value) => (typeof value === "string" ? value : value === void 0 || value === null ? "" : JSON.stringify(value)).trim().toLowerCase();
828
+ const taken = typeof equals === "string" ? normalize(condition) === normalize(equals) : Boolean(condition) && condition !== "false" && condition !== "0";
829
+ return Promise.resolve({
830
+ output: { condition: condition ?? null },
831
+ port: taken ? "true" : "false"
832
+ });
833
+ }
834
+ if (execution.blockType === "core#assert") return this.assert(execution);
835
+ return Promise.reject(new UnknownBlockTypeError(execution.blockType));
836
+ }
837
+ assert(execution) {
838
+ const { value, rejectValues, allowValues, allowEmpty, message } = execution.config;
839
+ const trimmed = (typeof value === "string" ? value : value === void 0 || value === null ? "" : JSON.stringify(value)).trim();
840
+ const fail = (reason) => Promise.reject(new Error(typeof message === "string" && message ? message : `core#assert: ${reason}`));
841
+ if (!trimmed && allowEmpty !== true) return fail("value is empty");
842
+ const normalizeList = (entries) => (typeof entries === "string" ? [entries] : Array.isArray(entries) ? entries : []).map((entry) => String(entry).trim().toLowerCase()).filter(Boolean);
843
+ if (normalizeList(rejectValues).includes(trimmed.toLowerCase())) return fail(`value is a rejected value ("${trimmed}")`);
844
+ const allowed = normalizeList(allowValues);
845
+ if (allowed.length > 0 && !allowed.includes(trimmed.toLowerCase())) return fail(`value "${trimmed}" is not one of the allowed values (${allowed.join(", ")})`);
846
+ return Promise.resolve({
847
+ output: { value },
848
+ port: "next"
849
+ });
850
+ }
851
+ };
852
+ const ATTACHMENT_REF = /^attachment:\/\//i;
853
+ function collectRefs(value, found) {
854
+ if (typeof value === "string") {
855
+ if (ATTACHMENT_REF.test(value)) found.add(value);
856
+ return;
857
+ }
858
+ if (Array.isArray(value)) {
859
+ for (const entry of value) collectRefs(entry, found);
860
+ return;
861
+ }
862
+ if (typeof value === "object" && value !== null) for (const entry of Object.values(value)) collectRefs(entry, found);
863
+ }
864
+ function storeScopeOf(payload) {
865
+ return payload?.scope === "PROJECT" ? "PROJECT" : "FLOW";
866
+ }
867
+ function storeKeyOf(payload) {
868
+ const key = payload?.key;
869
+ if (typeof key !== "string" || key === "") throw new Error("Store call carried no key");
870
+ return key;
871
+ }
872
+ function reactorInput(payload) {
873
+ if (typeof payload !== "object" || payload === null || Array.isArray(payload)) throw new Error("Reactor call carried no input object");
874
+ return payload;
875
+ }
876
+ function requiredString(payload, field) {
877
+ const value = payload[field];
878
+ if (typeof value !== "string" || value === "") throw new Error(`Reactor call carried no "${field}"`);
879
+ return value;
880
+ }
881
+ function optionalString(payload, field) {
882
+ const value = payload[field];
883
+ return typeof value === "string" && value !== "" ? value : void 0;
884
+ }
885
+ function reactorActions(payload) {
886
+ const actions = payload.actions;
887
+ if (!Array.isArray(actions) || actions.length === 0) throw new Error("Reactor call carried no actions");
888
+ return actions.map((entry, index) => {
889
+ const action = entry;
890
+ if (!action || typeof action.type !== "string") throw new Error(`Reactor call: actions[${index}] needs a string "type"`);
891
+ return {
892
+ type: action.type,
893
+ input: action.input,
894
+ ...typeof action.scope === "string" ? { scope: action.scope } : {}
895
+ };
896
+ });
897
+ }
898
+ const MAX_FIND_LIMIT = 100;
899
+ function findLimit(value) {
900
+ if (typeof value !== "number" || !Number.isFinite(value)) return void 0;
901
+ return Math.min(Math.max(Math.floor(value), 1), MAX_FIND_LIMIT);
902
+ }
903
+ function findMatch(value) {
904
+ if (typeof value !== "object" || value === null) return void 0;
905
+ const record = value;
906
+ const path = record.path;
907
+ const wanted = record.value;
908
+ if (typeof path !== "string" || path.trim() === "") return void 0;
909
+ if (typeof wanted !== "string") return void 0;
910
+ return {
911
+ path: path.trim(),
912
+ value: wanted
913
+ };
914
+ }
915
+ function reactorHandlers(port) {
916
+ return {
917
+ [REACTOR_MODELS]: () => port.models(),
918
+ [REACTOR_MODEL]: (payload) => port.model(requiredString(reactorInput(payload), "documentType")),
919
+ [REACTOR_GET]: (payload) => {
920
+ const input = reactorInput(payload);
921
+ return port.get({
922
+ documentId: requiredString(input, "documentId"),
923
+ ...optionalString(input, "branch") ? { branch: optionalString(input, "branch") } : {}
924
+ });
925
+ },
926
+ [REACTOR_FIND]: (payload) => {
927
+ const input = reactorInput(payload);
928
+ return port.find({
929
+ ...optionalString(input, "documentType") ? { documentType: optionalString(input, "documentType") } : {},
930
+ ...optionalString(input, "parentId") ? { parentId: optionalString(input, "parentId") } : {},
931
+ ...findLimit(input.limit) !== void 0 ? { limit: findLimit(input.limit) } : {},
932
+ ...findMatch(input.match) ? { match: findMatch(input.match) } : {},
933
+ ...input.withState === true ? { withState: true } : {}
934
+ });
935
+ },
936
+ [REACTOR_CREATE]: (payload) => {
937
+ const input = reactorInput(payload);
938
+ return port.create({
939
+ documentType: requiredString(input, "documentType"),
940
+ ...optionalString(input, "name") ? { name: optionalString(input, "name") } : {},
941
+ ...optionalString(input, "parentId") ? { parentId: optionalString(input, "parentId") } : {}
942
+ });
943
+ },
944
+ [REACTOR_EXECUTE]: (payload) => {
945
+ const input = reactorInput(payload);
946
+ return port.execute({
947
+ documentId: requiredString(input, "documentId"),
948
+ ...optionalString(input, "branch") ? { branch: optionalString(input, "branch") } : {},
949
+ actions: reactorActions(input)
950
+ });
951
+ }
952
+ };
953
+ }
954
+ function storeHandlers(port) {
955
+ return {
956
+ [STORE_GET]: (payload) => port.get(storeKeyOf(payload), storeScopeOf(payload)),
957
+ [STORE_PUT]: async (payload) => {
958
+ await port.put(storeKeyOf(payload), payload.value, storeScopeOf(payload));
959
+ return null;
960
+ },
961
+ [STORE_DELETE]: async (payload) => {
962
+ await port.delete(storeKeyOf(payload), storeScopeOf(payload));
963
+ return null;
964
+ }
965
+ };
966
+ }
967
+ function stepTaps(options, execution, values) {
968
+ const { onPieceLog, onPartialOutput } = options;
969
+ if (!onPieceLog && !onPartialOutput) return void 0;
970
+ const handlers = {};
971
+ if (onPieceLog) handlers[LOG_WRITE] = (payload) => {
972
+ const entry = payload;
973
+ return onPieceLog({
974
+ ...entry,
975
+ message: redactMessage(entry.message, { values })
976
+ }, execution);
977
+ };
978
+ if (onPartialOutput) handlers[OUTPUT_UPDATE] = (payload) => onPartialOutput(payload, execution);
979
+ return handlers;
980
+ }
981
+ function redactThrown(error, values) {
982
+ if (error instanceof Error) {
983
+ error.message = redactMessage(error.message, { values });
984
+ if (error.stack) error.stack = redactMessage(error.stack, { values });
985
+ return rememberSecrets(error, values);
986
+ }
987
+ return rememberSecrets(redactError(error, { values }), values);
988
+ }
989
+ const TRIGGER_FRAGMENT = "trigger:";
990
+ function parseBlockType(blockType, packages = {}) {
991
+ const separator = blockType.lastIndexOf("#");
992
+ if (separator <= 0) return void 0;
993
+ const packageSpec = blockType.slice(0, separator);
994
+ const fragment = blockType.slice(separator + 1);
995
+ const isTrigger = fragment.startsWith(TRIGGER_FRAGMENT);
996
+ const name = isTrigger ? fragment.slice(8) : fragment;
997
+ if (!name) return void 0;
998
+ const kind = isTrigger ? "trigger" : "action";
999
+ const versionAt = packageSpec.indexOf("@", 1);
1000
+ if (versionAt > 0) return {
1001
+ packageName: packageSpec.slice(0, versionAt),
1002
+ version: packageSpec.slice(versionAt + 1),
1003
+ kind,
1004
+ name
1005
+ };
1006
+ const version = packages[packageSpec];
1007
+ if (!version) return void 0;
1008
+ return {
1009
+ packageName: packageSpec,
1010
+ version,
1011
+ kind,
1012
+ name
1013
+ };
1014
+ }
1015
+ var ActivepiecesBlockExecutor = class {
1016
+ own;
1017
+ resolver;
1018
+ constructor(options) {
1019
+ this.options = options;
1020
+ this.resolver = options.resolver ?? bundleResolver({ cacheDir: options.cacheDir });
1021
+ }
1022
+ worker() {
1023
+ const supplied = this.options.worker;
1024
+ if (typeof supplied === "function") {
1025
+ const worker = supplied();
1026
+ if (worker) return worker;
1027
+ } else if (supplied) return supplied;
1028
+ return this.own ??= new PieceWorker();
1029
+ }
1030
+ packages() {
1031
+ const supplied = this.options.packages;
1032
+ return Promise.resolve(typeof supplied === "function" ? supplied() : supplied ?? {});
1033
+ }
1034
+ async execute(execution) {
1035
+ const parsed = parseBlockType(execution.blockType, await this.packages());
1036
+ if (!parsed) throw new UnknownBlockTypeError(execution.blockType);
1037
+ if (parsed.kind !== "action") throw new TriggerBlockAsStepError(execution.blockType);
1038
+ const stagingDir = this.options.stagingRoot ? path.join(this.options.stagingRoot, randomUUID()) : void 0;
1039
+ let redactValues = [];
1040
+ try {
1041
+ const piece = await this.resolver.resolve(parsed.packageName, parsed.version);
1042
+ const connection = await this.resolveConnection(execution.connectionId, {
1043
+ blockType: execution.blockType,
1044
+ piecePackage: parsed.packageName,
1045
+ stepId: execution.step.id,
1046
+ stepKey: execution.step.key
1047
+ });
1048
+ const auth = connection?.auth;
1049
+ redactValues = connection?.secretValues ?? [];
1050
+ const timeoutMs = execution.step.timeoutSeconds ? execution.step.timeoutSeconds * 1e3 : this.options.defaultTimeoutMs;
1051
+ const stagedInputs = await this.stageInputs(execution.config, stagingDir);
1052
+ const pieceStore = this.options.pieceStore;
1053
+ const notifications = stepTaps(this.options, execution, redactValues);
1054
+ const egress = this.options.egress === void 0 ? DEFAULT_EGRESS_POLICY : this.options.egress;
1055
+ const reactor = piece.local ? this.options.reactor : void 0;
1056
+ const result = await this.worker().runAction({
1057
+ ...pieceModuleRef(piece),
1058
+ actionName: parsed.name,
1059
+ propsValue: execution.config,
1060
+ auth,
1061
+ ...redactValues.length > 0 ? { redactValues } : {},
1062
+ ...stagingDir ? { stagingDir } : {},
1063
+ ...stagedInputs ? { stagedInputs } : {},
1064
+ ...pieceStore ? { durableStore: true } : {},
1065
+ ...reactor ? { reactorAccess: true } : {},
1066
+ ...this.options.onPieceLog ? { captureLogs: true } : {},
1067
+ ...this.options.onPartialOutput ? { liveOutput: true } : {},
1068
+ ...egress ? { egress } : {}
1069
+ }, {
1070
+ ...timeoutMs ? { timeoutMs } : {},
1071
+ ...pieceStore || reactor ? { hostCalls: {
1072
+ ...pieceStore ? storeHandlers(pieceStore) : {},
1073
+ ...reactor ? reactorHandlers(reactor) : {}
1074
+ } } : {},
1075
+ ...notifications ? { notifications } : {}
1076
+ });
1077
+ return {
1078
+ output: await this.ingestFiles(result.output, result.files),
1079
+ redactValues
1080
+ };
1081
+ } catch (error) {
1082
+ throw redactThrown(error, redactValues);
1083
+ } finally {
1084
+ if (stagingDir) await rm(stagingDir, {
1085
+ recursive: true,
1086
+ force: true
1087
+ });
1088
+ }
1089
+ }
1090
+ async resolveConnection(connectionId, request) {
1091
+ const connections = this.options.connections;
1092
+ if (!connectionId || !connections) return void 0;
1093
+ if (connections.resolveWithSecrets) return connections.resolveWithSecrets(connectionId, request);
1094
+ const auth = await connections.resolve(connectionId, request);
1095
+ return {
1096
+ auth,
1097
+ secretValues: [...collectSecretValues(auth)]
1098
+ };
1099
+ }
1100
+ async stageInputs(config, stagingDir) {
1101
+ const port = this.options.attachments;
1102
+ if (!stagingDir || !port) return void 0;
1103
+ const refs = /* @__PURE__ */ new Set();
1104
+ collectRefs(config, refs);
1105
+ if (refs.size === 0) return void 0;
1106
+ await mkdir(stagingDir, { recursive: true });
1107
+ const staged = [];
1108
+ let index = 0;
1109
+ for (const ref of refs) {
1110
+ const destPath = path.join(stagingDir, `in-${index++}`);
1111
+ const meta = await port.read(ref, destPath);
1112
+ staged.push({
1113
+ ref,
1114
+ path: destPath,
1115
+ ...meta
1116
+ });
1117
+ }
1118
+ return staged;
1119
+ }
1120
+ async ingestFiles(output, files) {
1121
+ if (!files || files.length === 0) return output;
1122
+ const port = this.options.attachments;
1123
+ if (!port) throw new Error(`The action wrote ${files.length} file(s) through ctx.files, but no attachment store is configured for this reactor`);
1124
+ const refs = /* @__PURE__ */ new Map();
1125
+ for (const file of files) refs.set(file.token, await port.write({
1126
+ path: file.path,
1127
+ fileName: file.fileName,
1128
+ size: file.size,
1129
+ contentType: file.contentType
1130
+ }));
1131
+ return rewriteFileRefs(output, refs);
1132
+ }
1133
+ dispose() {
1134
+ this.own?.dispose();
1135
+ this.own = void 0;
1136
+ }
1137
+ };
1138
+ var CompositeBlockExecutor = class {
1139
+ core = new CoreBlockExecutor();
1140
+ constructor(pieces, handlers = {}) {
1141
+ this.pieces = pieces;
1142
+ this.handlers = handlers;
1143
+ }
1144
+ execute(execution) {
1145
+ const handler = this.handlers[execution.blockType];
1146
+ if (handler) return handler.execute(execution);
1147
+ if (CoreBlockExecutor.handles(execution.blockType)) return this.core.execute(execution);
1148
+ return this.pieces.execute(execution);
1149
+ }
1150
+ };
1151
+ //#endregion
1152
+ //#region src/pieces/engine/coordinator.ts
1153
+ function errorMessage(error) {
1154
+ if (error instanceof Error) return error.message;
1155
+ return String(error);
1156
+ }
1157
+ function journaled(value, values) {
1158
+ return value === void 0 ? void 0 : redact(value, { values });
1159
+ }
1160
+ async function runWorkflow(options) {
1161
+ const { definition, executor } = options;
1162
+ const scope = {
1163
+ trigger: { payload: options.triggerPayload },
1164
+ steps: {},
1165
+ variables: Object.fromEntries((definition.variables ?? []).map((v) => [v.key, v.value ?? null]))
1166
+ };
1167
+ const records = /* @__PURE__ */ new Map();
1168
+ const edgeDecisions = /* @__PURE__ */ new Map();
1169
+ let runFailed;
1170
+ let executedCount = 0;
1171
+ const journal = async (record) => {
1172
+ if (!options.onStep) return;
1173
+ const ordinal = executedCount++;
1174
+ try {
1175
+ await options.onStep(record, ordinal);
1176
+ } catch {}
1177
+ };
1178
+ const decideOutgoing = (sourceId, port) => {
1179
+ for (const edge of definition.edges) {
1180
+ if (edge.from !== sourceId) continue;
1181
+ const taken = port !== void 0 && edge.port === port && (!edge.condition || evaluateCondition(edge.condition, scope));
1182
+ edgeDecisions.set(edge.id, taken);
1183
+ }
1184
+ };
1185
+ if (definition.trigger) decideOutgoing(definition.trigger.id, "next");
1186
+ const inboundEdges = (step) => definition.edges.filter((edge) => edge.to === step.id);
1187
+ const isEntryStep = (step) => !definition.trigger && inboundEdges(step).length === 0;
1188
+ const skipStep = (step) => {
1189
+ records.set(step.id, {
1190
+ stepId: step.id,
1191
+ key: step.key,
1192
+ blockType: step.blockType,
1193
+ status: "SKIPPED"
1194
+ });
1195
+ decideOutgoing(step.id, void 0);
1196
+ };
1197
+ const refuseReplay = (step) => {
1198
+ const error = `Journaled output of step "${step.key}" was redacted and cannot be replayed; fire the workflow again instead of rerunning it`;
1199
+ records.set(step.id, {
1200
+ stepId: step.id,
1201
+ key: step.key,
1202
+ blockType: step.blockType,
1203
+ status: "FAILED",
1204
+ error
1205
+ });
1206
+ runFailed = error;
1207
+ };
1208
+ const executeStep = async (step) => {
1209
+ const replay = options.completedSteps?.get(step.id);
1210
+ if (replay) {
1211
+ if (containsRedactedMarker(replay.output)) {
1212
+ refuseReplay(step);
1213
+ return;
1214
+ }
1215
+ const port = replay.port ?? "next";
1216
+ const record = {
1217
+ stepId: step.id,
1218
+ key: step.key,
1219
+ blockType: step.blockType,
1220
+ status: "REPLAYED",
1221
+ output: replay.output,
1222
+ port
1223
+ };
1224
+ records.set(step.id, record);
1225
+ await journal(record);
1226
+ scope.steps[step.key] = { output: replay.output };
1227
+ decideOutgoing(step.id, port);
1228
+ return;
1229
+ }
1230
+ const input = resolveExpressions(step.config, scope);
1231
+ try {
1232
+ const result = await executor.execute({
1233
+ blockType: step.blockType,
1234
+ config: input,
1235
+ connectionId: step.connectionId,
1236
+ step
1237
+ });
1238
+ const port = result.port ?? "next";
1239
+ const record = {
1240
+ stepId: step.id,
1241
+ key: step.key,
1242
+ blockType: step.blockType,
1243
+ status: "SUCCEEDED",
1244
+ input: journaled(input, result.redactValues),
1245
+ output: journaled(result.output, result.redactValues),
1246
+ port
1247
+ };
1248
+ records.set(step.id, record);
1249
+ await journal(record);
1250
+ scope.steps[step.key] = { output: result.output };
1251
+ decideOutgoing(step.id, port);
1252
+ } catch (error) {
1253
+ const values = secretsFor(error);
1254
+ const detail = redactMessage(errorMessage(error), { values });
1255
+ const record = {
1256
+ stepId: step.id,
1257
+ key: step.key,
1258
+ blockType: step.blockType,
1259
+ status: "FAILED",
1260
+ input: journaled(input, values),
1261
+ error: detail
1262
+ };
1263
+ records.set(step.id, record);
1264
+ await journal(record);
1265
+ decideOutgoing(step.id, "error");
1266
+ if (!definition.edges.some((edge) => edge.from === step.id && edgeDecisions.get(edge.id))) runFailed = `Step "${step.key}" failed: ${detail}`;
1267
+ }
1268
+ };
1269
+ let progressed = true;
1270
+ while (progressed && !runFailed) {
1271
+ progressed = false;
1272
+ for (const step of definition.steps) {
1273
+ if (records.has(step.id)) continue;
1274
+ const inbound = inboundEdges(step);
1275
+ if (isEntryStep(step)) {
1276
+ await executeStep(step);
1277
+ progressed = true;
1278
+ if (runFailed) break;
1279
+ continue;
1280
+ }
1281
+ if (inbound.length === 0) continue;
1282
+ if (!inbound.every((edge) => edgeDecisions.has(edge.id))) continue;
1283
+ if (inbound.some((edge) => edgeDecisions.get(edge.id))) await executeStep(step);
1284
+ else skipStep(step);
1285
+ progressed = true;
1286
+ if (runFailed) break;
1287
+ }
1288
+ }
1289
+ for (const step of definition.steps) if (!records.has(step.id)) skipStep(step);
1290
+ const steps = definition.steps.map((step) => records.get(step.id));
1291
+ return runFailed ? {
1292
+ status: "FAILED",
1293
+ steps,
1294
+ error: runFailed
1295
+ } : {
1296
+ status: "SUCCEEDED",
1297
+ steps
1298
+ };
1299
+ }
1300
+ //#endregion
1301
+ //#region src/reactor/piece-registry.ts
1302
+ const logger = childLogger(["workflow", "piece-registry"]);
1303
+ const MANIFEST_CANDIDATES = ["dist/node/pieces/index.mjs", "pieces/index.ts"];
1304
+ const require = createRequire(import.meta.url);
1305
+ function packageRoot(packageName) {
1306
+ try {
1307
+ return dirname(require.resolve(`${packageName}/package.json`));
1308
+ } catch {}
1309
+ try {
1310
+ let dir = dirname(require.resolve(packageName));
1311
+ while (!existsSync(join(dir, "package.json"))) {
1312
+ const parent = dirname(dir);
1313
+ if (parent === dir) return void 0;
1314
+ dir = parent;
1315
+ }
1316
+ return dir;
1317
+ } catch {
1318
+ return;
1319
+ }
1320
+ }
1321
+ function configuredPackages(projectRoot) {
1322
+ const file = join(projectRoot, "powerhouse.config.json");
1323
+ try {
1324
+ return (JSON.parse(readFileSync(file, "utf8")).packages ?? []).map((entry) => entry.packageName).filter((name) => typeof name === "string" && name !== "");
1325
+ } catch {
1326
+ return [];
1327
+ }
1328
+ }
1329
+ async function readManifest(root) {
1330
+ for (const candidate of MANIFEST_CANDIDATES) {
1331
+ const file = join(root, candidate);
1332
+ if (!existsSync(file)) continue;
1333
+ const module = await import(
1334
+ /* @vite-ignore */
1335
+ pathToFileURL(file).href
1336
+ );
1337
+ const declared = module.pieces ?? module.default;
1338
+ if (Array.isArray(declared)) return declared;
1339
+ logger.warn(`${file} exports no "pieces" array; ignoring it`);
1340
+ return [];
1341
+ }
1342
+ return [];
1343
+ }
1344
+ function locate(declared, root, source) {
1345
+ const where = declared.entry ?? declared.bundle;
1346
+ if (!where) {
1347
+ logger.warn(`Piece "${declared.name}" from ${source} declares no bundle or entry`);
1348
+ return;
1349
+ }
1350
+ const path = isAbsolute(where) ? where : join(root, where);
1351
+ if (!(declared.entry ? existsSync(path) : existsSync(join(path, "package.json")))) {
1352
+ logger.warn(`Piece "${declared.name}" from ${source} is declared but not built at ${path}`);
1353
+ return;
1354
+ }
1355
+ return {
1356
+ name: declared.name,
1357
+ version: declared.version,
1358
+ ...declared.entry ? { entryPath: path } : { bundleDir: path }
1359
+ };
1360
+ }
1361
+ var PieceRegistry = class {
1362
+ byName = /* @__PURE__ */ new Map();
1363
+ loading;
1364
+ load(projectRoot = process.cwd()) {
1365
+ return this.loading = this.discover(projectRoot);
1366
+ }
1367
+ async discover(projectRoot) {
1368
+ const found = /* @__PURE__ */ new Map();
1369
+ const sources = [{
1370
+ source: "this project",
1371
+ root: projectRoot
1372
+ }, ...configuredPackages(projectRoot).flatMap((name) => {
1373
+ const root = packageRoot(name);
1374
+ if (!root) {
1375
+ logger.warn(`Package "${name}" is configured but not resolvable`);
1376
+ return [];
1377
+ }
1378
+ return [{
1379
+ source: name,
1380
+ root
1381
+ }];
1382
+ })];
1383
+ for (const { source, root } of sources) {
1384
+ let declared;
1385
+ try {
1386
+ declared = await readManifest(root);
1387
+ } catch (error) {
1388
+ logger.warn(`Could not read the pieces of ${source}: ${String(error)}`);
1389
+ continue;
1390
+ }
1391
+ for (const entry of declared) {
1392
+ if (found.has(entry.name)) continue;
1393
+ const piece = locate(entry, root, source);
1394
+ if (piece) found.set(entry.name, piece);
1395
+ }
1396
+ }
1397
+ this.byName = found;
1398
+ if (found.size > 0) logger.info(`Loaded ${found.size} package piece(s): ${[...found.keys()].join(", ")}`);
1399
+ }
1400
+ ready(projectRoot = process.cwd()) {
1401
+ return this.loading ??= this.discover(projectRoot);
1402
+ }
1403
+ lookup = (name) => this.byName.get(name);
1404
+ entries() {
1405
+ return [...this.byName.values()];
1406
+ }
1407
+ versions() {
1408
+ return Object.fromEntries([...this.byName.values()].map((piece) => [piece.name, piece.version]));
1409
+ }
1410
+ reset() {
1411
+ this.byName = /* @__PURE__ */ new Map();
1412
+ this.loading = void 0;
1413
+ }
1414
+ };
1415
+ const packagePieces = new PieceRegistry();
1416
+ //#endregion
1417
+ export { pieceModuleRef as C, localFirstResolver as S, fetchPieceBundle as T, secretRefFromId as _, CompositeBlockExecutor as a, PieceWorkerError as b, storeHandlers as c, declaredConnectionIds as d, shapeConnection as f, parseSecretRef as g, SecretNotFoundError as h, ActivepiecesBlockExecutor as i, BoundConnectionResolver as l, SecretDeletedError as m, packagePieces as n, parseBlockType as o, InMemorySecretProvider as p, runWorkflow as r, reactorHandlers as s, PieceRegistry as t, StaticConnectionResolver as u, PieceWorkerPool as v, ensurePieceBundle as w, PieceWorkerTimeoutError as x, PieceWorker as y };
1418
+
1419
+ //# sourceMappingURL=piece-registry-BWWihLyp.js.map