@cloud-cli/on 1.59.0 → 1.60.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,895 @@
1
+ import { t as e } from "./timestamped-log-Dtz_Euu0.js";
2
+ import { t } from "./workflow-registry-D4IciIF7.js";
3
+ import { a as n, i as r, r as i, t as a } from "./matrix-expander-BItKqWu2.js";
4
+ import { t as o } from "./secrets-BR6K16WY.js";
5
+ import { n as s } from "./events-BoODn7MY.js";
6
+ import { n as c } from "./workflows-Dm_0dWb_.js";
7
+ import l from "node:fs";
8
+ import u from "node:path";
9
+ import { promisify as d } from "node:util";
10
+ import { exec as f, spawn as p } from "node:child_process";
11
+ import { readFile as m } from "node:fs/promises";
12
+ //#region src/drivers/systemd.driver.ts
13
+ var h = d(f), g = class {
14
+ name = "systemd";
15
+ async isSupported() {
16
+ try {
17
+ return l.existsSync("/run/systemd/system");
18
+ } catch {
19
+ return !1;
20
+ }
21
+ }
22
+ execute(t) {
23
+ let r = null, i = null, a = Date.now(), o = u.join(t.logsDir, `step-${t.step.id}.log`);
24
+ try {
25
+ l.mkdirSync(t.logsDir, { recursive: !0 }), l.mkdirSync(t.workingDir, { recursive: !0 }), l.chmodSync(t.workingDir, 511), r = l.openSync(o, "a"), i = new e(r);
26
+ } catch (e) {
27
+ return {
28
+ done: Promise.resolve({
29
+ exitCode: 1,
30
+ durationMs: 0,
31
+ error: /* @__PURE__ */ Error(`Failed to initialize step log file: ${e.message}`)
32
+ }),
33
+ cancel: async () => {},
34
+ logFilePath: ""
35
+ };
36
+ }
37
+ let s = `workflow-${t.jobId}-${t.step.id}-attempt-${t.attempt || 1}-${Date.now()}`.replace(/[^a-zA-Z0-9_-]/g, "_"), c = [
38
+ `--unit=${s}`,
39
+ "--wait",
40
+ "--pipe",
41
+ "--collect",
42
+ "-p",
43
+ "RemainAfterExit=no",
44
+ `--working-directory=${t.workingDir}`
45
+ ], d = {
46
+ ...t.env || {},
47
+ PATH: t.env?.PATH || process.env.PATH
48
+ };
49
+ for (let [e, t] of Object.entries(d)) c.push(`--setenv=${e}=${t}`);
50
+ if (t.timeoutMs) {
51
+ let e = Math.ceil(t.timeoutMs / 1e3);
52
+ c.push(`--property=RuntimeMaxSec=${e}`);
53
+ }
54
+ let f;
55
+ t.image ? (c.push("--setenv=WORKING_DIR=/workspace"), f = [
56
+ "docker",
57
+ "run",
58
+ "--rm",
59
+ "--init",
60
+ `--name=${s}`,
61
+ ...t.volumes?.flatMap((e) => ["-v", e]) || [],
62
+ "-v",
63
+ `${t.workingDir}:/workspace`,
64
+ "-w",
65
+ "/workspace",
66
+ ...Object.keys(d).filter((e) => e !== "PATH").flatMap((e) => ["-e", e]),
67
+ "--entrypoint",
68
+ "/bin/sh",
69
+ ...t.dockerArgs || [],
70
+ t.image,
71
+ "-e",
72
+ "-c",
73
+ t.command
74
+ ]) : (c.push(`--setenv=WORKING_DIR=${t.workingDir}`), f = [
75
+ process.env.SHELL || "sh",
76
+ "-e",
77
+ "-c",
78
+ t.command
79
+ ]), n("$ systemd-run", [
80
+ ...c,
81
+ "--",
82
+ ...f
83
+ ].join(" "));
84
+ let m;
85
+ try {
86
+ m = p("systemd-run", [
87
+ ...c,
88
+ "--",
89
+ ...f
90
+ ], { stdio: [
91
+ "ignore",
92
+ "pipe",
93
+ "pipe"
94
+ ] }), m.stdout?.on("data", (e) => i?.write(e)), m.stderr?.on("data", (e) => i?.write(e));
95
+ } catch (e) {
96
+ try {
97
+ l.closeSync(r);
98
+ } catch {}
99
+ return {
100
+ done: Promise.resolve({
101
+ exitCode: 1,
102
+ durationMs: Date.now() - a,
103
+ error: /* @__PURE__ */ Error(`Failed to spawn systemd-run: ${e.message}`)
104
+ }),
105
+ cancel: async () => {},
106
+ logFilePath: o
107
+ };
108
+ }
109
+ let g = !1;
110
+ return {
111
+ done: new Promise((e) => {
112
+ let t = !1, n = (n) => {
113
+ t || (t = !0, i?.end(() => {
114
+ try {
115
+ l.closeSync(r);
116
+ } catch {}
117
+ e(n);
118
+ }));
119
+ };
120
+ m.on("close", (e, t) => {
121
+ n({
122
+ exitCode: e === null ? t ? g ? 130 : 1 : 0 : e,
123
+ durationMs: Date.now() - a,
124
+ error: g ? /* @__PURE__ */ Error("Step cancelled by user or systemd timeout") : void 0
125
+ });
126
+ }), m.on("error", (e) => {
127
+ n({
128
+ exitCode: 1,
129
+ durationMs: Date.now() - a,
130
+ error: e
131
+ });
132
+ });
133
+ }),
134
+ cancel: async () => {
135
+ g = !0;
136
+ try {
137
+ t.image && await h(`docker stop -t 2 ${s}`).catch(() => {}), await h(`systemctl stop ${s}.service`).catch(() => {});
138
+ } catch {}
139
+ },
140
+ logFilePath: o
141
+ };
142
+ }
143
+ async readLog(e) {
144
+ let t = await m(e, "utf-8"), n = t.includes("Running as unit: ") ? t.indexOf("\n") + 1 : 0, r = t.includes("Finished with result: ") ? t.lastIndexOf("Finished with result: ") : t.length;
145
+ return t.slice(n, r);
146
+ }
147
+ }, _ = class {
148
+ name = "standard-process";
149
+ async isSupported() {
150
+ return !0;
151
+ }
152
+ execute(t) {
153
+ let r = null, i = null, a = Date.now(), o = u.join(t.logsDir, `step-${t.step.id}.log`);
154
+ try {
155
+ l.mkdirSync(t.logsDir, { recursive: !0 }), l.mkdirSync(t.workingDir, { recursive: !0 }), l.chmodSync(t.workingDir, 511), r = l.openSync(o, "a"), i = new e(r);
156
+ } catch (e) {
157
+ return {
158
+ done: Promise.resolve({
159
+ exitCode: 1,
160
+ durationMs: 0,
161
+ error: /* @__PURE__ */ Error(`Failed to initialize step log file: ${e.message}`)
162
+ }),
163
+ cancel: async () => {},
164
+ logFilePath: ""
165
+ };
166
+ }
167
+ let s, c, d = { ...t.env };
168
+ t.image ? (d.WORKING_DIR = "/workspace", s = "docker", c = [
169
+ "run",
170
+ "--rm",
171
+ "--init",
172
+ "-v",
173
+ `${t.workingDir}:/workspace`,
174
+ "-w",
175
+ "/workspace",
176
+ "--entrypoint",
177
+ "/bin/sh",
178
+ ...t.volumes?.flatMap((e) => ["-v", e]) || [],
179
+ ...t.dockerArgs || [],
180
+ ...Object.keys(d).filter((e) => e !== "PATH").flatMap((e) => ["-e", e]),
181
+ t.image,
182
+ "-c",
183
+ t.command
184
+ ]) : (d.WORKING_DIR = t.workingDir, s = process.env.SHELL || "sh", c = [
185
+ "-e",
186
+ "-c",
187
+ t.command
188
+ ]), n("$ " + s, c.join(" "));
189
+ let f;
190
+ try {
191
+ f = p(s, c, {
192
+ cwd: t.workingDir,
193
+ env: d,
194
+ detached: !0,
195
+ stdio: [
196
+ "ignore",
197
+ "pipe",
198
+ "pipe"
199
+ ]
200
+ }), f.stdout?.on("data", (e) => i?.write(e)), f.stderr?.on("data", (e) => i?.write(e));
201
+ } catch (e) {
202
+ try {
203
+ l.closeSync(r);
204
+ } catch {}
205
+ return {
206
+ done: Promise.resolve({
207
+ exitCode: 1,
208
+ durationMs: Date.now() - a,
209
+ error: /* @__PURE__ */ Error(`Failed to spawn process: ${e.message}`)
210
+ }),
211
+ cancel: async () => {},
212
+ logFilePath: o
213
+ };
214
+ }
215
+ let m = !1, h = null;
216
+ return {
217
+ done: new Promise((e) => {
218
+ let n = !1, o = (t) => {
219
+ n || (n = !0, h && clearTimeout(h), i?.end(() => {
220
+ try {
221
+ l.closeSync(r);
222
+ } catch {}
223
+ e(t);
224
+ }));
225
+ };
226
+ t.timeoutMs && (h = setTimeout(() => {
227
+ m = !0, this.killProcessGroup(f);
228
+ }, t.timeoutMs), h.unref()), f.on("close", (e) => {
229
+ o({
230
+ exitCode: e ?? (m ? 130 : 1),
231
+ durationMs: Date.now() - a,
232
+ error: m ? /* @__PURE__ */ Error("Step timed out or was cancelled by user") : void 0
233
+ });
234
+ }), f.on("error", (e) => {
235
+ o({
236
+ exitCode: 1,
237
+ durationMs: Date.now() - a,
238
+ error: e
239
+ });
240
+ });
241
+ }),
242
+ cancel: async () => {
243
+ m = !0, this.killProcessGroup(f);
244
+ },
245
+ logFilePath: o
246
+ };
247
+ }
248
+ killProcessGroup(e) {
249
+ if (e.pid && !e.killed) try {
250
+ process.kill(-e.pid, "SIGTERM"), setTimeout(() => {
251
+ try {
252
+ e.pid && !e.killed && process.kill(-e.pid, "SIGKILL");
253
+ } catch {}
254
+ }, 5e3).unref();
255
+ } catch {}
256
+ }
257
+ async readLog(e) {
258
+ return m(e, "utf-8");
259
+ }
260
+ };
261
+ //#endregion
262
+ //#region src/drivers/index.ts
263
+ async function v() {
264
+ let e = new g();
265
+ return await e.isSupported() ? (console.log("⚡ Selected Execution Driver: Systemd (cgroups enabled)"), e) : (console.log("📦 Selected Execution Driver: Standard Process (Fallback)"), new _());
266
+ }
267
+ //#endregion
268
+ //#region src/signals.ts
269
+ async function y(e) {
270
+ let t = !1;
271
+ async function n(n) {
272
+ if (!t) {
273
+ t = !0, console.log(`\n🛑 Received ${n}. Initiating graceful shutdown...`);
274
+ try {
275
+ D(), e.length > 0 && (console.log("⚙️ Waiting for active worker jobs to drain..."), await Promise.allSettled(e)), console.log("✨ Engine stopped cleanly. Goodbye!"), process.exit(0);
276
+ } catch (e) {
277
+ console.error("❌ Error during graceful shutdown:", e.message), process.exit(1);
278
+ }
279
+ }
280
+ }
281
+ process.on("SIGINT", () => n("SIGINT")), process.on("SIGTERM", () => n("SIGTERM")), process.on("SIGUSR1", () => n("SIGUSR1"));
282
+ }
283
+ //#endregion
284
+ //#region src/plugins/manager.ts
285
+ var b = class {
286
+ plugins;
287
+ constructor(e = []) {
288
+ this.plugins = e;
289
+ }
290
+ register(e) {
291
+ console.log(`🔌 Registered Plugin: ${e.name}`), this.plugins.push(e);
292
+ }
293
+ async triggerWorkflowStart(e) {
294
+ for (let t of this.plugins) if (t.onWorkflowStart) try {
295
+ await t.onWorkflowStart(e);
296
+ } catch (e) {
297
+ console.error(`[Plugin Error] ${t.name}.onWorkflowStart:`, e);
298
+ }
299
+ }
300
+ async triggerWorkflowFinish(e, t) {
301
+ for (let n of this.plugins) if (n.onWorkflowFinish) try {
302
+ await n.onWorkflowFinish(e, t);
303
+ } catch (e) {
304
+ console.error(`[Plugin Error] ${n.name}.onWorkflowFinish:`, e);
305
+ }
306
+ }
307
+ }, x = {
308
+ name: "@cloud-cli/on",
309
+ version: "0.0.0",
310
+ description: "CLI entry point for the on plugin ecosystem.",
311
+ type: "module",
312
+ packageManager: "pnpm@12.3.4",
313
+ license: "MIT",
314
+ exports: { ".": {
315
+ types: "./dist/index.d.ts",
316
+ default: "./dist/on.js"
317
+ } },
318
+ files: ["dist"],
319
+ main: "dist/on.js",
320
+ bin: { on: "dist/on.js" },
321
+ dependencies: {
322
+ acorn: "^8.18.0",
323
+ openai: "7.15.0",
324
+ "web-push": "^3.6.7",
325
+ yaml: "^2.9.0"
326
+ },
327
+ scripts: {
328
+ ci: "pnpm build && pnpm lint && pnpm test",
329
+ build: "vite build && tsc --emitDeclarationOnly --declaration",
330
+ lint: "eslint .",
331
+ test: "vitest run",
332
+ "test:e2e": "docker build --no-cache -t on:e2e . && node tests/docker-smoke.mjs",
333
+ release: "npx --yes semantic-release@24 -b main --no-ci"
334
+ },
335
+ devDependencies: {
336
+ "@changesets/cli": "^2.31.1",
337
+ "@eslint/js": "^10.0.1",
338
+ "@types/node": "^26.5.1",
339
+ "@types/web-push": "^3.6.4",
340
+ "@typescript-eslint/eslint-plugin": "^8.70.0",
341
+ "@typescript-eslint/parser": "^8.70.0",
342
+ eslint: "10.8.0",
343
+ marked: "^18.0.12",
344
+ tsx: "4.23.13",
345
+ typescript: "6.0.3",
346
+ "typescript-eslint": "8.70.0",
347
+ vite: "8.2.0",
348
+ vitest: "4.1.11"
349
+ }
350
+ }.version, S = { isStopping: !1 }, C = /* @__PURE__ */ new Set(), w = null, T = null;
351
+ async function E() {
352
+ C.size && (console.log("⚡ Cancelling active step execution handles due to worker shutdown..."), await Promise.allSettled(Array.from(C, (e) => e.cancel())), C.clear());
353
+ }
354
+ function D() {
355
+ S.isStopping = !0, w?.abort(), T?.();
356
+ }
357
+ function O(e, t, n, r) {
358
+ S.isStopping = !1;
359
+ let i = [k(e, t, n, r)];
360
+ return y(i), i;
361
+ }
362
+ async function k(e, t, n, r) {
363
+ let i = await v(), a = new c(), s = /* @__PURE__ */ new Set(), l = 0, u = null, d = () => {
364
+ l++, u?.();
365
+ }, f = process.env.WORKER_NAME || "cli";
366
+ T = d, w = new AbortController();
367
+ let p = A(r, w.signal, d), m = setInterval(() => void P(r, f, e, s.size), 15e3);
368
+ for (m.unref(), P(r, f, e, s.size), console.log(`🚀 Worker scheduler started. Driver: ${i.name}. Concurrency: ${e}. Tags: ${r.tags.join(", ") || "(none)"}`); !S.isStopping;) {
369
+ let n = l;
370
+ try {
371
+ for (; s.size < e && !S.isStopping;) {
372
+ let e = await t.claimNextJob(r.tags);
373
+ if (!e) break;
374
+ if (S.isStopping) {
375
+ await t.releaseJob(e.id);
376
+ break;
377
+ }
378
+ let n;
379
+ n = (async () => {
380
+ N(r, e.id);
381
+ let n = await a.getRevision(e.workflow_id, e.workflow_revision);
382
+ if (!n) throw await t.finishJob(e.id, "failed"), Error(`Workflow ${e.workflow_id} revision ${e.workflow_revision} was not found`);
383
+ let s = new o();
384
+ s.replace(await F(r, f, e.id)), await I({
385
+ workerId: f,
386
+ job: e,
387
+ queue: t,
388
+ secrets: s,
389
+ config: r,
390
+ driver: i,
391
+ workflow: n
392
+ });
393
+ })().catch((e) => console.error(`[${f}] ⚠️ Worker execution error:`, e)).finally(() => {
394
+ s.delete(n), N(r, e.id), d();
395
+ }), s.add(n);
396
+ }
397
+ } catch (e) {
398
+ console.error("⚠️ Worker scheduler claim error:", e), setTimeout(d, 5e3).unref();
399
+ }
400
+ !S.isStopping && l === n && (await j(n, () => l, (e) => u = e), u = null);
401
+ }
402
+ w.abort(), clearInterval(m), await Promise.allSettled(s), await p, T = null, console.log("🛑 Worker scheduler stopped cleanly.");
403
+ }
404
+ async function A(e, t, n) {
405
+ let r = 1e3;
406
+ for (; !t.aborted;) {
407
+ try {
408
+ await s(e.serverUrl, t, (t, r) => {
409
+ t === "jobs.available" && (Array.isArray(r.tags) ? r.tags.filter((e) => typeof e == "string") : []).every((t) => e.tags.includes(t)) && n();
410
+ }, n), r = 1e3;
411
+ } catch (e) {
412
+ if (t.aborted || e?.name === "AbortError") break;
413
+ console.error(`⚠️ Worker event stream disconnected: ${e.message}`);
414
+ }
415
+ await M(r, t), r = Math.min(r * 2, 3e4);
416
+ }
417
+ }
418
+ function j(e, t, n) {
419
+ return new Promise((r) => {
420
+ if (t() !== e) return r();
421
+ let i = setTimeout(r, 6e4);
422
+ n(() => {
423
+ clearTimeout(i), r();
424
+ });
425
+ });
426
+ }
427
+ function M(e, t) {
428
+ return new Promise((n) => {
429
+ if (t.aborted) return n();
430
+ let r = () => {
431
+ clearTimeout(i), n();
432
+ }, i = setTimeout(() => {
433
+ t.removeEventListener("abort", r), n();
434
+ }, e);
435
+ t.addEventListener("abort", r, { once: !0 });
436
+ });
437
+ }
438
+ async function N(e, t) {
439
+ if (e.workerToken) try {
440
+ let r = await fetch(new URL("/api/events", e.serverUrl), {
441
+ method: "POST",
442
+ headers: {
443
+ Authorization: `Bearer ${e.workerToken}`,
444
+ "Content-Type": "application/json"
445
+ },
446
+ body: JSON.stringify({ jobId: t }),
447
+ signal: AbortSignal.timeout(5e3)
448
+ });
449
+ r.ok || n(`Failed to publish job status event: HTTP ${r.status}`);
450
+ } catch (e) {
451
+ n("Failed to publish job status event:", e);
452
+ }
453
+ }
454
+ async function P(e, t, r, i) {
455
+ if (e.workerToken) try {
456
+ await fetch(new URL("/api/workers/heartbeat", e.serverUrl), {
457
+ method: "POST",
458
+ headers: {
459
+ Authorization: `Bearer ${e.workerToken}`,
460
+ "Content-Type": "application/json",
461
+ "X-Runner-Worker-Id": t
462
+ },
463
+ body: JSON.stringify({
464
+ workerId: t,
465
+ version: x,
466
+ tags: e.tags,
467
+ concurrency: r,
468
+ activeJobs: i
469
+ }),
470
+ signal: AbortSignal.timeout(5e3)
471
+ });
472
+ } catch (e) {
473
+ n("Failed to publish worker heartbeat:", e);
474
+ }
475
+ }
476
+ async function F(e, t, n) {
477
+ if (!e.workerToken) return {};
478
+ try {
479
+ let r = await fetch(new URL(`/api/jobs/${n}/secrets`, e.serverUrl), {
480
+ headers: {
481
+ Authorization: `Bearer ${e.workerToken}`,
482
+ "X-Runner-Worker-Id": t
483
+ },
484
+ signal: AbortSignal.timeout(5e3)
485
+ });
486
+ if (!r.ok) throw Error(`HTTP ${r.status}`);
487
+ return (await r.json()).secrets || {};
488
+ } catch (e) {
489
+ throw console.error(`Unable to retrieve secrets for job ${n}:`, e), e;
490
+ }
491
+ }
492
+ async function I(e) {
493
+ let { workerId: n, job: o, config: s, secrets: c, queue: l } = e;
494
+ console.log(`\n[${n}] 📦 Claimed Job #${o.id} (Workflow: ${o.workflow_id})`);
495
+ let d = typeof o.payload == "string" ? JSON.parse(o.payload) : o.payload;
496
+ if (!e.workflow) throw Error(`Workflow ${o.workflow_id} revision ${o.workflow_revision} was not resolved`);
497
+ let f = d.matrix ? a(e.workflow).find((e) => JSON.stringify(e.matrixContext) === JSON.stringify(d.matrix)) : e.workflow;
498
+ if (!f) throw Error(`Matrix variant for job ${o.id} was not found`);
499
+ let p = f, m = {
500
+ ...e,
501
+ workflow: p
502
+ }, h = p.steps, g = d.inputs || {}, _ = Date.now(), v = u.join(s.storagePath, `job-${o.id}`), y = u.join(v, "logs"), x = u.join(v, "wd"), S = {
503
+ inputs: g,
504
+ env: {
505
+ ...s.env,
506
+ CI: "true",
507
+ CI_JOB_ID: String(o.id),
508
+ CI_JOB_NAME: p.name
509
+ },
510
+ secrets: c.getAll(),
511
+ steps: {},
512
+ logsDir: y,
513
+ workingDir: x,
514
+ files: r(x)
515
+ }, C = "", w = h.map((e, t) => (e.id ||= `step-${t}`, e.name ||= e.id, {
516
+ id: e.id,
517
+ name: e.name,
518
+ status: "pending",
519
+ durationMs: 0,
520
+ outputs: {},
521
+ logContent: ""
522
+ })), T = X(o, "running", _, g, S.env, w, d);
523
+ await l.saveReport(o.id, T), N(s, o.id);
524
+ try {
525
+ Object.assign(S.env, await B(p.env, S)), Object.assign(S.env, {
526
+ CI: "true",
527
+ CI_JOB_ID: String(o.id),
528
+ CI_JOB_NAME: p.name
529
+ }), q(p.secretFiles, c, x), C = p.cache ? String(await i.evaluateValue(p.cache.key, S)) : "", C && p.cache && await K(l, "cache", `${o.workflow_id}:${C}`, p.cache.paths, x), await l.saveReport(o.id, T);
530
+ } catch (e) {
531
+ T.status = "failed", T.finishedAt = (/* @__PURE__ */ new Date()).toISOString(), T.durationMs = Date.now() - _;
532
+ let t = {
533
+ ...T,
534
+ steps: T.steps.map((e) => ({
535
+ ...e,
536
+ status: "skipped"
537
+ }))
538
+ };
539
+ await l.completeJob(o.id, "failed", t), console.error(`[${n}] ❌ Job setup failed:`, e);
540
+ return;
541
+ }
542
+ let E = [...s.plugins];
543
+ for (let e of p.plugins || []) try {
544
+ let n = await B(e.secrets, S);
545
+ E.push(t({
546
+ ...e,
547
+ secrets: n
548
+ }));
549
+ } catch (t) {
550
+ console.error(`[${n}] Workflow plugin '${e.name}' failed to initialize:`, t);
551
+ }
552
+ let D = new b(E), O = {
553
+ jobId: String(o.id),
554
+ workflowName: p.name,
555
+ inputs: g,
556
+ runUrl: new URL(`/runs/${o.id}`, s.serverUrl).toString()
557
+ };
558
+ await D.triggerWorkflowStart(O);
559
+ let { cancelled: k, failed: A } = await L({
560
+ payload: d,
561
+ steps: h,
562
+ executionContext: S,
563
+ ...m
564
+ }, T), j = k ? "cancelled" : A ? "failed" : "success";
565
+ if (j === "success") try {
566
+ let e = p.artifacts ? await G(p.artifacts.paths, x) : [];
567
+ if (e.length && (await l.saveStoredFiles("artifact", String(o.id), e), T.artifacts = e.map((e) => e.path)), C && p.cache) {
568
+ let e = await G(p.cache.paths, x);
569
+ e.length && await l.saveStoredFiles("cache", `${o.workflow_id}:${C}`, e);
570
+ }
571
+ } catch (e) {
572
+ console.error(`[${n}] ⚠️ Failed to store workflow files:`, e), j = "failed";
573
+ }
574
+ T.status = j, T.durationMs = Date.now() - _, T.finishedAt = (/* @__PURE__ */ new Date()).toISOString(), await l.completeJob(o.id, j, T), N(s, o.id), console.log(`[${n}] ✅ Job #${o.id} completed as: ${j}`), await D.triggerWorkflowFinish(O, j);
575
+ }
576
+ async function L(e, t) {
577
+ let { driver: n, workerId: r, queue: i, config: a, job: o, payload: s, steps: c, executionContext: l } = e, u = t.steps, d = !1, f = !1, p = 0;
578
+ try {
579
+ for (let s = 0; s < c.length; s++) {
580
+ let m = c[s], h = (/* @__PURE__ */ new Date()).toISOString();
581
+ u[s] = {
582
+ ...u[s],
583
+ status: "running",
584
+ durationMs: 0,
585
+ exitCode: void 0,
586
+ error: void 0,
587
+ outputs: {},
588
+ logContent: "",
589
+ startedAt: h,
590
+ finishedAt: void 0
591
+ }, t.durationMs = Date.now() - Date.parse(t.startedAt), await i.saveReport(o.id, t), N(a, o.id);
592
+ let g = {
593
+ failed: !0,
594
+ skipped: !1,
595
+ cancelled: !1,
596
+ report: null
597
+ }, _ = e.workflow?.retries ?? 0;
598
+ for (let e = 1; e <= _ + 1 && (g = await R({
599
+ workerId: r,
600
+ jobId: o.id,
601
+ step: m,
602
+ stepIndex: s,
603
+ attempt: e,
604
+ executionContext: l,
605
+ driver: n,
606
+ queue: i,
607
+ config: a
608
+ }), !(!g.failed || g.cancelled || e > _)); e++) console.log(`[${r}] 🔁 Retrying step [${m.id}] (${e}/${_ + 1})`);
609
+ if (g.report ? u[s] = {
610
+ ...g.report,
611
+ startedAt: h,
612
+ finishedAt: (/* @__PURE__ */ new Date()).toISOString()
613
+ } : g.skipped ? u[s] = {
614
+ ...u[s],
615
+ status: "skipped",
616
+ durationMs: 0,
617
+ exitCode: 0,
618
+ finishedAt: (/* @__PURE__ */ new Date()).toISOString()
619
+ } : u[s] = {
620
+ ...u[s],
621
+ status: "failed",
622
+ durationMs: 0,
623
+ exitCode: 1,
624
+ error: "Step failed before execution started",
625
+ finishedAt: (/* @__PURE__ */ new Date()).toISOString()
626
+ }, p = s + 1, t.durationMs = Date.now() - Date.parse(t.startedAt), await i.saveReport(o.id, t), N(a, o.id), g.skipped) break;
627
+ if (g.cancelled) {
628
+ f = !0, d = !0;
629
+ break;
630
+ }
631
+ if (g.failed) {
632
+ d = !0;
633
+ break;
634
+ }
635
+ }
636
+ } catch (e) {
637
+ console.log("🛑 Step failed", e), d = !0;
638
+ }
639
+ return p < c.length && (Y(c, p, u), t.durationMs = Date.now() - Date.parse(t.startedAt), await i.saveReport(o.id, t), N(a, o.id)), {
640
+ failed: d,
641
+ cancelled: f,
642
+ stepReports: u
643
+ };
644
+ }
645
+ async function R(e) {
646
+ let { step: t, stepIndex: r, executionContext: a } = e;
647
+ if (t.id ||= `step-${r}`, t.name ||= t.id, t.if && !await i.evaluateConditions(t.if, a)) return n(`⏩ Skipped step ${t.id} based on condition: ${t.if}`, a), {
648
+ failed: !1,
649
+ cancelled: !1,
650
+ skipped: !0,
651
+ report: null
652
+ };
653
+ try {
654
+ let n = await B(t.env, a), i = {
655
+ jobId: String(e.jobId),
656
+ step: t,
657
+ command: t.run,
658
+ timeoutMs: t.timeoutMs ?? 3e4,
659
+ attempt: e.attempt,
660
+ image: t.image,
661
+ volumes: t.volumes,
662
+ dockerArgs: t.dockerArgs,
663
+ workingDir: a.workingDir,
664
+ logsDir: a.logsDir,
665
+ env: {
666
+ ...a.env,
667
+ ...n,
668
+ WORKING_DIR: a.workingDir,
669
+ CI: "true",
670
+ CI_JOB_ID: String(e.jobId),
671
+ CI_JOB_NAME: a.env.CI_JOB_NAME || t.name || t.id || `step-${r}`
672
+ }
673
+ };
674
+ return t.eval ? z({
675
+ ...e,
676
+ stepContext: i
677
+ }) : J({
678
+ ...e,
679
+ stepContext: i
680
+ });
681
+ } catch (r) {
682
+ let i = r?.message || String(r);
683
+ return await e.queue.saveStepLog(e.jobId, t.id, `[STEP ERROR]:\n${i}`), n(`⏩ Failed to run step ${t.id}`, a, r), {
684
+ failed: !0,
685
+ cancelled: !1,
686
+ skipped: !1,
687
+ report: {
688
+ id: t.id,
689
+ name: t.name,
690
+ status: "failed",
691
+ durationMs: 0,
692
+ exitCode: 1,
693
+ error: i,
694
+ outputs: {},
695
+ logContent: ""
696
+ }
697
+ };
698
+ }
699
+ }
700
+ async function z(e) {
701
+ let { queue: t, stepContext: n, executionContext: r } = e, { jobId: a, step: o } = n, s = Date.now(), c = o.id, l = o.name, u;
702
+ try {
703
+ let e = new Promise((e, t) => {
704
+ u = setTimeout(() => t(/* @__PURE__ */ Error(`Step timed out after ${n.timeoutMs}ms`)), n.timeoutMs), u.unref();
705
+ }), d = await Promise.race([i.evaluateExpression(o.eval, r), e]);
706
+ r.steps[c] = {
707
+ status: "success",
708
+ exitCode: 0,
709
+ outputs: d ?? {}
710
+ };
711
+ let f = typeof d == "object" ? JSON.stringify(d, null, 2) : String(d ?? "OK");
712
+ return await t.saveStepLog(a, c, `[JS EVAL OUTPUT]:\n${f}`), console.log(`[${c}] ✅ JS Eval step complete.`), {
713
+ failed: !1,
714
+ cancelled: !1,
715
+ skipped: !1,
716
+ report: {
717
+ id: c,
718
+ name: l,
719
+ status: "success",
720
+ durationMs: Date.now() - s,
721
+ exitCode: 0,
722
+ outputs: r.steps[c].outputs,
723
+ logContent: ""
724
+ }
725
+ };
726
+ } catch (e) {
727
+ return console.error(`[${c}] ❌ JS Eval step failed:`, e), r.steps[c] = {
728
+ status: "failed",
729
+ exitCode: 1,
730
+ outputs: {}
731
+ }, await t.saveStepLog(a, c, `[JS EVAL ERROR]:\n${e.message}`), {
732
+ failed: !0,
733
+ cancelled: !1,
734
+ skipped: !1,
735
+ report: {
736
+ id: c,
737
+ name: l,
738
+ status: "failed",
739
+ durationMs: Date.now() - s,
740
+ exitCode: 1,
741
+ error: e.message,
742
+ outputs: {},
743
+ logContent: ""
744
+ }
745
+ };
746
+ } finally {
747
+ u && clearTimeout(u);
748
+ }
749
+ }
750
+ async function B(e, t) {
751
+ let n = {};
752
+ if (e) for (let [r, a] of Object.entries(e)) n[r] = String(await i.evaluateValue(a, {
753
+ ...t,
754
+ env: {
755
+ ...t.env,
756
+ ...n
757
+ }
758
+ }));
759
+ return n;
760
+ }
761
+ var V = 52428800;
762
+ function H(e, t) {
763
+ let n = u.resolve(e), r = u.resolve(n, t);
764
+ if (r !== n && !r.startsWith(`${n}${u.sep}`)) throw Error(`Stored file path escapes workspace: ${t}`);
765
+ return r;
766
+ }
767
+ function U(e, t) {
768
+ let n = "^";
769
+ for (let e = 0; e < t.length; e++) t[e] === "*" && t[e + 1] === "*" ? (n += ".*", e++) : t[e] === "*" ? n += "[^/]*" : n += t[e].replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
770
+ return RegExp(`${n}$`).test(e);
771
+ }
772
+ function W(e, t) {
773
+ let n = u.resolve(t), r = [], i = (e) => {
774
+ for (let t of l.readdirSync(e, { withFileTypes: !0 })) {
775
+ let a = u.join(e, t.name);
776
+ t.isDirectory() ? i(a) : t.isFile() && r.push(u.relative(n, a).split(u.sep).join("/"));
777
+ }
778
+ };
779
+ return i(n), r.filter((n) => e.some((e) => {
780
+ let r = H(t, e);
781
+ if (l.existsSync(r)) {
782
+ let t = l.statSync(r);
783
+ if (t.isFile()) return n === e;
784
+ if (t.isDirectory()) return n === e || n.startsWith(`${e.replace(/\/$/, "")}/`);
785
+ }
786
+ return U(n, e);
787
+ }));
788
+ }
789
+ async function G(e, t) {
790
+ let n = W(e, t), r = 0;
791
+ return n.map((e) => {
792
+ let n = l.readFileSync(H(t, e));
793
+ if (n.byteLength > V || (r += n.byteLength) > V) throw Error(`Stored files exceed ${V} bytes`);
794
+ return {
795
+ path: e,
796
+ content: n.toString("base64")
797
+ };
798
+ });
799
+ }
800
+ async function K(e, t, n, r, i) {
801
+ for (let a of await e.getStoredFiles(t, n)) {
802
+ if (!r.some((e) => a.path === e || a.path.startsWith(`${e.replace(/\/$/, "")}/`) || U(a.path, e))) continue;
803
+ let e = H(i, a.path);
804
+ l.mkdirSync(u.dirname(e), { recursive: !0 }), l.writeFileSync(e, Buffer.from(a.content, "base64"));
805
+ }
806
+ }
807
+ function q(e, t, n) {
808
+ for (let [r, i] of Object.entries(e || {})) {
809
+ let e = t.getValue(i);
810
+ if (!e) throw Error(`Secret '${i}' is not available for file '${r}'`);
811
+ let a = H(n, r);
812
+ l.mkdirSync(u.dirname(a), { recursive: !0 }), l.writeFileSync(a, e.encoding === "base64" ? Buffer.from(e.value, "base64") : e.value, { mode: 384 }), l.chmodSync(a, 384);
813
+ }
814
+ }
815
+ async function J(e) {
816
+ let { workerId: t, jobId: n, stepContext: r, executionContext: i, driver: a, queue: o } = e, { step: s } = r, c = s.id, u = s.name, d;
817
+ try {
818
+ d = a.execute(r);
819
+ } catch (e) {
820
+ d = {
821
+ done: Promise.resolve({
822
+ exitCode: 1,
823
+ durationMs: 0,
824
+ error: Error(String(e))
825
+ }),
826
+ cancel: async () => {},
827
+ logFilePath: ""
828
+ };
829
+ }
830
+ C.add(d);
831
+ let f = !1, p = setInterval(async () => {
832
+ await o.isCancelled(n) && (console.log(`[${t}] 🛑 Job #${n} was cancelled! Halting execution.`), f = !0, clearInterval(p), await d.cancel());
833
+ }, 3e3), m = await d.done;
834
+ if (clearInterval(p), C.delete(d), d.logFilePath && l.existsSync(d.logFilePath)) try {
835
+ let e = await a.readLog(d.logFilePath);
836
+ await o.saveStepLog(n, c, e);
837
+ } catch (e) {
838
+ console.error(`[${t}] ⚠️ Failed to read step log file:`, e.message);
839
+ }
840
+ let h = m.exitCode !== 0 || f, g = f ? "cancelled" : m.exitCode === 0 ? "success" : "failed";
841
+ return i.steps[c] = {
842
+ status: g,
843
+ exitCode: m.exitCode,
844
+ outputs: {}
845
+ }, h && console.error(`[${t}] ❌ Step [${c}] finished with status: ${g}`), {
846
+ failed: h,
847
+ cancelled: f,
848
+ skipped: !1,
849
+ report: {
850
+ id: c,
851
+ name: u,
852
+ status: g,
853
+ durationMs: m.durationMs,
854
+ exitCode: m.exitCode,
855
+ error: m.error?.message,
856
+ outputs: {},
857
+ logContent: ""
858
+ }
859
+ };
860
+ }
861
+ function Y(e, t, n) {
862
+ for (let r = t; r < e.length; r++) {
863
+ let t = e[r], i = t.id || `step-${r}`;
864
+ n[r] = {
865
+ id: i,
866
+ name: t.name || i,
867
+ status: "skipped",
868
+ durationMs: 0,
869
+ exitCode: 0,
870
+ outputs: {},
871
+ logContent: ""
872
+ };
873
+ }
874
+ }
875
+ function X(e, t, n, r, i, a, o) {
876
+ return {
877
+ jobId: String(e.id),
878
+ parentId: String(e.parentId || ""),
879
+ workflowName: e.workflow_id,
880
+ status: t,
881
+ durationMs: Date.now() - n,
882
+ startedAt: new Date(n).toISOString(),
883
+ finishedAt: t === "running" ? void 0 : (/* @__PURE__ */ new Date()).toISOString(),
884
+ inputs: r,
885
+ environment: i,
886
+ steps: a,
887
+ artifacts: [],
888
+ rerunToken: JSON.stringify({
889
+ jobId: e.id,
890
+ payload: o
891
+ })
892
+ };
893
+ }
894
+ //#endregion
895
+ export { E as abortActiveWorkerTask, I as processJob, D as requestWorkerShutdown, S as shutdownState, k as startWorkerScheduler, O as startWorkers };