@cloud-cli/on 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -82,6 +82,9 @@ concurrency:
82
82
  group: release-${inputs.repo}
83
83
  cancel-in-progress: true
84
84
 
85
+ # Every tag must be advertised by the worker that claims this workflow.
86
+ tags: [linux, docker]
87
+
85
88
  steps:
86
89
  - id: checkout
87
90
  name: Checkout Code Repository
@@ -147,7 +150,7 @@ npx @cloud-cli/on [command] [options]
147
150
  | Command | Description |
148
151
  | ------------------- | ---------------------------------------------------------------------------------------- |
149
152
  | **`start-server`** | Runs Webhook Ingress Gateway (the HTTP server receiving webhooks). |
150
- | **`start-workers`** | Runs Worker Polling loops (Scalable Workers). |
153
+ | **`start-workers`** | Runs the event-driven worker scheduler (Scalable Workers). |
151
154
  | **`validate`** | Parses and validates all YAML workflows in your workflows folder without executing jobs. |
152
155
 
153
156
  ### CLI and Environment Options
@@ -159,8 +162,12 @@ npx @cloud-cli/on [command] [options]
159
162
  | `-d` | `--database` | - | `RUNNER_DATABASE_URL` | SQLite database file path or HTTP URL. |
160
163
  | `-w` | `--workflows` | `on/` | `RUNNER_WORKFLOWS` | Directory where workflow YAML files live. |
161
164
  | `-p` | `--port` | `11235` | `PORT` | Port for the Ingress HTTP server. |
162
- | `-k` | `--workers` | `5` | `RUNNER_WORKERS` | Number of worker loop threads to spawn. |
165
+ | `-k` | `--workers` | `5` | `RUNNER_WORKERS` | Maximum concurrent jobs on this node. |
163
166
  | | | | `RUNNER_ADMIN_SECRET` | Admin token to refresh secrets via API |
167
+ | | | | `RUNNER_SERVER_URL` | Webhook server URL used by workers. |
168
+ | | | | `RUNNER_TAGS` | Comma-separated worker capability tags. |
169
+
170
+ Set the same non-empty `RUNNER_ADMIN_SECRET` on the server and workers to publish live job-status refresh events. Job availability and the 60-second recovery refresh continue to work without it.
164
171
 
165
172
  ### Secrets
166
173
 
@@ -177,6 +184,8 @@ import { HtmlReporter, SlackReporter, JsonFileReporter } from '@cloud-cli/on/rep
177
184
  export default {
178
185
  port: 3000,
179
186
  workers: 5,
187
+ serverUrl: 'https://runner.example.com/',
188
+ tags: ['linux', 'docker'],
180
189
  workflows: '/home/workflows/',
181
190
  storagePath: '/tmp/workspaces',
182
191
  database: 'https://remote.db.com/',
@@ -250,6 +259,7 @@ The Ingress Gateway listens for incoming HTTP requests and serves the live web U
250
259
  | ---------- | ------------------ | -------------------------------------------------------------------------------------- |
251
260
  | **`POST`** | `/webhooks/github` | Webhook endpoint for GitHub events. Applies GitHub filters and evaluates `on.github.if`. |
252
261
  | **`GET`** | `/runs` | **Dashboard:** Live dark-mode monitoring page listing recent jobs and worker health. |
262
+ | **`GET`** | `/api/jobs?afterId=<id>&beforeId=<id>&limit=<n>` | Dashboard jobs with exclusive lower and upper ID cursors and a limit from 1 to 500 (default 50). |
253
263
  | **`GET`** | `/runs/:jobId` | **Job Report:** Interactive HTML trace view with step timings and terminal log output. |
254
264
 
255
265
  ### Dashboard Features
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAG5D,wBAAsB,UAAU,CAAC,MAAM,KAAA,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAyBrE;AAED,wBAAgB,aAAa,CAAC,cAAc,EAAE,gBAAgB,EAAE,aAAa,EAAE,gBAAgB,GAAG,YAAY,CAY7G;AAED,wBAAgB,SAAS,SAsBxB;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC;IAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAwB9F"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAG5D,wBAAsB,UAAU,CAAC,MAAM,KAAA,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAyBrE;AAED,wBAAgB,aAAa,CAAC,cAAc,EAAE,gBAAgB,EAAE,aAAa,EAAE,gBAAgB,GAAG,YAAY,CAgB7G;AAED,wBAAgB,SAAS,SAwBxB;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC;IAAE,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAwB9F"}
@@ -7,5 +7,5 @@ export interface DashboardJob {
7
7
  updatedAt: string;
8
8
  }
9
9
  export declare function toDashboardJobs(rows: any[]): DashboardJob[];
10
- export declare function generateDashboardHtml(jobs: DashboardJob[]): string;
10
+ export declare function generateDashboardHtml(jobs: DashboardJob[], hasMore?: boolean): string;
11
11
  //# sourceMappingURL=dashboard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../src/dashboard.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,YAAY,EAAE,CAS3D;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,MAAM,CAElE"}
1
+ {"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../src/dashboard.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,YAAY,EAAE,CAS3D;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,OAAO,UAAQ,GAAG,MAAM,CAEnF"}
@@ -0,0 +1,12 @@
1
+ import type http from 'node:http';
2
+ export type RunnerEvent = 'jobs.available' | 'jobs.changed';
3
+ export declare class EventBroker {
4
+ private clients;
5
+ private nextId;
6
+ private readonly maxClients;
7
+ subscribe(req: http.IncomingMessage, res: http.ServerResponse): void;
8
+ publish(event: RunnerEvent, data?: Record<string, unknown>): void;
9
+ close(): void;
10
+ }
11
+ export declare function consumeRunnerEvents(serverUrl: string, signal: AbortSignal, onEvent: (event: RunnerEvent, data: Record<string, unknown>) => void, onOpen?: () => void): Promise<void>;
12
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,MAAM,MAAM,WAAW,GAAG,gBAAgB,GAAG,cAAc,CAAC;AAE5D,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAkD;IACjE,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IAEnC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE,IAAI,CAAC,cAAc,GAAG,IAAI;IAuBpE,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,IAAI;IAUrE,KAAK,IAAI,IAAI;CAOd;AAED,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,EACpE,MAAM,CAAC,EAAE,MAAM,IAAI,GAClB,OAAO,CAAC,IAAI,CAAC,CA4Bf"}
package/dist/on.js CHANGED
@@ -63,20 +63,22 @@ async function le(e) {
63
63
  return t(o.workflows) ? (ce(o.database), o) : (console.warn(`⚠️ Warning: Workflows directory '${o.workflows}' not found.`), null);
64
64
  }
65
65
  function ue(e, t) {
66
- let n = process.env;
66
+ let n = process.env, r = Number(e.port || t.port || n.PORT || 11235), i = e.tags ?? (n.RUNNER_TAGS ? n.RUNNER_TAGS.split(",") : []);
67
67
  return {
68
- port: Number(e.port || t.port || n.PORT || 11235),
68
+ port: r,
69
69
  adminToken: e.adminToken ?? n.RUNNER_ADMIN_SECRET ?? "",
70
70
  database: e.database ?? t.database ?? n.RUNNER_DATABASE_URL ?? "",
71
71
  workflows: e.workflows ?? t.workflows ?? n.RUNNER_WORKFLOWS ?? "on/",
72
72
  workers: Number(e.workers ?? t.workers ?? n.RUNNER_WORKERS ?? 5),
73
+ serverUrl: e.serverUrl ?? n.RUNNER_SERVER_URL ?? `http://127.0.0.1:${r}`,
74
+ tags: i.map((e) => e.trim()).filter(Boolean),
73
75
  storagePath: e.storagePath ?? n.RUNNER_TMP ?? "/tmp/workspaces",
74
76
  env: e.env ?? {},
75
77
  reporters: e.reporters ?? []
76
78
  };
77
79
  }
78
80
  function de() {
79
- console.log("\n🏃 Runner CLI 🏃\n\nUsage:\n npx -y @cloud-cli/on <command> [options]\n pnpm dlx -y @cloud-cli/on <command> [options]\n\nCommands:\n start Runs both Webhook Ingress Server and Workers (Default)\n start-server Runs Webhook Ingress Server only (API Gateway mode)\n start-workers Runs Worker Polling loops only (Scalable Worker mode)\n validate Parses and validates workflow YAML files without running\n\nOptions:\n -c, --config Path to runner.config.mjs (default: ./runner.config.mjs, env: RUNNER_CONFIG_PATH)\n -d, --database SQLite Database URL (env: RUNNER_DATABASE_URL)\n -w, --workflows Path to where your workflows are defined (default: on/, env: RUNNER_WORKFLOWS_PATH)\n -p, --port Port for Webhook Ingress Server (default: 11235, env: PORT)\n -k, --workers Number of worker thread loops to spawn (default: 5, env: RUNNER_WORKERS)\n -h, --help Show this help message\n ");
81
+ console.log("\n🏃 Runner CLI 🏃\n\nUsage:\n npx -y @cloud-cli/on <command> [options]\n pnpm dlx -y @cloud-cli/on <command> [options]\n\nCommands:\n start Runs both Webhook Ingress Server and Workers (Default)\n start-server Runs Webhook Ingress Server only (API Gateway mode)\n start-workers Runs event-driven workers (Scalable Worker mode)\n validate Parses and validates workflow YAML files without running\n\nOptions:\n -c, --config Path to runner.config.mjs (default: ./runner.config.mjs, env: RUNNER_CONFIG_PATH)\n -d, --database SQLite Database URL (env: RUNNER_DATABASE_URL)\n -w, --workflows Path to where your workflows are defined (default: on/, env: RUNNER_WORKFLOWS_PATH)\n -p, --port Port for Webhook Ingress Server (default: 11235, env: PORT)\n -k, --workers Maximum concurrent jobs (default: 5, env: RUNNER_WORKERS)\n Worker tags (comma-separated env: RUNNER_TAGS)\n Webhook server URL (env: RUNNER_SERVER_URL)\n -h, --help Show this help message\n ");
80
82
  }
81
83
  async function fe() {
82
84
  let { values: e, positionals: t } = l({
@@ -4462,8 +4464,11 @@ var pi = class {
4462
4464
  JSON.stringify(t)
4463
4465
  ]);
4464
4466
  }
4465
- async claimNextJob() {
4466
- return await S.get("\n UPDATE jobs\n SET\n status = 'running',\n worker_id = ?,\n started_at = CURRENT_TIMESTAMP\n WHERE id = (\n SELECT id FROM jobs\n WHERE status = 'pending'\n ORDER BY created_at ASC\n LIMIT 1\n )\n RETURNING *;\n ", [this.workerId]) || null;
4467
+ async claimNextJob(e = []) {
4468
+ return await S.get("\n UPDATE jobs\n SET\n status = 'running',\n worker_id = ?,\n started_at = CURRENT_TIMESTAMP\n WHERE id = (\n SELECT id FROM jobs\n WHERE status = 'pending'\n AND NOT EXISTS (\n SELECT 1\n FROM json_each(COALESCE(json_extract(jobs.payload, '$.tags'), '[]')) AS required_tag\n WHERE required_tag.value NOT IN (SELECT value FROM json_each(?))\n )\n ORDER BY created_at ASC\n LIMIT 1\n )\n RETURNING *;\n ", [this.workerId, JSON.stringify(e)]) || null;
4469
+ }
4470
+ async releaseJob(e) {
4471
+ await S.run("UPDATE jobs SET status = 'pending', worker_id = NULL, started_at = NULL WHERE id = ? AND status = 'running';", [e]);
4467
4472
  }
4468
4473
  async finishJob(e, t) {
4469
4474
  await S.run("UPDATE jobs SET status = ?, finished_at = CURRENT_TIMESTAMP WHERE id = ?;", [t, e]);
@@ -4491,8 +4496,11 @@ var pi = class {
4491
4496
  async getJob(e) {
4492
4497
  return S.get("SELECT * FROM jobs WHERE id = ?", [e]);
4493
4498
  }
4494
- async listJobs(e = 50) {
4495
- return S.all("SELECT * FROM jobs ORDER BY id DESC LIMIT ?;", [e]);
4499
+ async listJobs(e = 50, t, n) {
4500
+ let r = [], i = [];
4501
+ t !== void 0 && (r.push("id > ?"), i.push(t)), n !== void 0 && (r.push("id < ?"), i.push(n));
4502
+ let a = r.length ? ` WHERE ${r.join(" AND ")}` : "";
4503
+ return S.all(`SELECT * FROM jobs${a} ORDER BY id DESC LIMIT ?;`, [...i, e]);
4496
4504
  }
4497
4505
  async saveReport(e, t) {
4498
4506
  await S.run("UPDATE jobs SET report = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?", [JSON.stringify(t), e]);
@@ -8021,7 +8029,8 @@ var Po = "<!DOCTYPE html>\n<html lang=\"en\" class=\"dark\">\n<head>\n <meta ch
8021
8029
  },
8022
8030
  concurrency: t.concurrency,
8023
8031
  steps: t.steps,
8024
- env: t.env
8032
+ env: t.env,
8033
+ tags: Array.isArray(t.tags) ? [...new Set(t.tags.filter((e) => typeof e == "string").map((e) => e.trim()).filter(Boolean))] : void 0
8025
8034
  });
8026
8035
  }
8027
8036
  } catch (t) {
@@ -8029,7 +8038,7 @@ var Po = "<!DOCTYPE html>\n<html lang=\"en\" class=\"dark\">\n<head>\n <meta ch
8029
8038
  }
8030
8039
  return n;
8031
8040
  }
8032
- }, Ro = "<!DOCTYPE html>\n<html lang=\"en\" class=\"dark\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Workflow Engine Dashboard</title>\n <script type=\"importmap\">\n { \"imports\": { \"@li3/\": \"https://cdn.li3.dev/@li3/\" } }\n <\/script>\n</head>\n<body class=\"bg-gray-950 text-gray-100 min-h-screen p-4 md:p-6 font-sans\">\n <template app>\n <main class=\"max-w-6xl mx-auto space-y-6\">\n <header class=\"flex flex-col sm:flex-row sm:items-center justify-between gap-3 border-b border-gray-800 pb-4\">\n <div>\n <h1 class=\"text-2xl font-bold text-white\">Runner Engine Status</h1>\n <p class=\"text-xs text-gray-400\">Live Job Queue &amp; Execution Traces</p>\n </div>\n <div class=\"flex items-center gap-3 text-xs font-mono\">\n <span class=\"text-gray-500\">Updated {{ lastUpdated }}</span>\n <span\n class=\"px-3 py-1 rounded-full border\"\n bind-class=\"{\n 'bg-emerald-500/10.text-emerald-400.border-emerald-500/20': !refreshError,\n 'bg-rose-500/10.text-rose-400.border-rose-500/20': refreshError\n }\"\n >\n {{ refreshError ? 'Refresh failed' : 'System operational' }}\n </span>\n </div>\n </header>\n\n <section class=\"bg-gray-900 border border-gray-800 rounded-xl overflow-hidden hidden md:block\">\n <table class=\"w-full text-left text-sm\">\n <thead class=\"bg-gray-800/50 text-gray-400 text-xs uppercase font-mono border-b border-gray-800\">\n <tr>\n <th class=\"py-3 px-4\">Job ID</th>\n <th class=\"py-3 px-4\">Workflow</th>\n <th class=\"py-3 px-4\">Status</th>\n <th class=\"py-3 px-4 hidden xl:table-cell\">Worker</th>\n <th class=\"py-3 px-4 hidden lg:table-cell\">Created At</th>\n <th class=\"py-3 px-4 text-right\">Action</th>\n </tr>\n </thead>\n <tbody>\n <template for=\"job of jobs\">\n <tr class=\"border-b border-gray-800 hover:bg-gray-800/40 transition\">\n <td class=\"py-3 px-4 font-mono text-indigo-400\">\n <a attr-href=\"'/runs/' + job.id\" class=\"hover:underline\">#{{ job.id }}</a>\n </td>\n <td class=\"py-3 px-4 font-medium text-white\">{{ job.workflowId }}</td>\n <td class=\"py-3 px-4\">\n <span\n class=\"px-2.5 py-0.5 rounded-full text-xs font-semibold\"\n bind-class=\"{\n 'bg-emerald-500/10.text-emerald-400': job.status === 'success',\n 'bg-rose-500/10.text-rose-400': job.status === 'failed',\n 'bg-indigo-500/10.text-indigo-400.animate-pulse': job.status === 'running',\n 'bg-gray-500/10.text-gray-400': !['success', 'failed', 'running'].includes(job.status)\n }\"\n >\n {{ upper(job.status) }}\n </span>\n </td>\n <td class=\"py-3 px-4 hidden xl:table-cell text-xs font-mono text-gray-400\">{{ job.workerId || '-' }}</td>\n <td class=\"py-3 px-4 hidden lg:table-cell text-xs text-gray-400\">{{ job.createdAt }}</td>\n <td class=\"py-3 px-4 text-right whitespace-nowrap\">\n <a attr-href=\"'/runs/' + job.id\" class=\"text-xs bg-gray-800 hover:bg-gray-700 text-gray-200 px-3 py-1 rounded border border-gray-700\">View Trace</a>\n </td>\n </tr>\n </template>\n </tbody>\n </table>\n <template if=\"!jobs.length\">\n <div class=\"p-8 text-center text-gray-500\">No jobs recorded yet.</div>\n </template>\n </section>\n\n <section class=\"md:hidden bg-gray-900 border border-gray-800 rounded-xl divide-y divide-gray-800 overflow-hidden\">\n <template for=\"job of jobs\">\n <article class=\"p-4 bg-gray-900/60\">\n <div class=\"flex items-start justify-between gap-3\">\n <div class=\"min-w-0\">\n <div class=\"flex items-center gap-2\">\n <span\n class=\"w-2.5 h-2.5 rounded-full shrink-0\"\n bind-class=\"{\n 'bg-emerald-400': job.status === 'success',\n 'bg-rose-400': job.status === 'failed',\n 'bg-indigo-400.animate-pulse': job.status === 'running',\n 'bg-gray-500': !['success', 'failed', 'running'].includes(job.status)\n }\"\n ></span>\n <h2 class=\"font-medium text-white truncate\">{{ job.workflowId }}</h2>\n </div>\n <p class=\"mt-1 pl-[18px] text-xs font-mono text-gray-400\">#{{ job.id }} · {{ upper(job.status) }}</p>\n </div>\n <a attr-href=\"'/runs/' + job.id\" class=\"shrink-0 text-xs bg-gray-800 hover:bg-gray-700 text-gray-300 px-2.5 py-1 rounded border border-gray-700\">Trace</a>\n </div>\n </article>\n </template>\n <template if=\"!jobs.length\">\n <div class=\"p-8 text-center text-gray-500\">No jobs recorded yet.</div>\n </template>\n </section>\n </main>\n\n <script setup>\n import { onDestroy, onInit, ref } from '@li3/web';\n\n export default function setup() {\n const jobs = ref([]);\n const lastUpdated = ref('now');\n const refreshError = ref(false);\n let refreshTimer;\n let refreshing = false;\n\n const upper = (value) => String(value || '').toUpperCase();\n const refreshJobs = async () => {\n if (refreshing) return;\n refreshing = true;\n\n try {\n const response = await fetch('/api/jobs', { headers: { accept: 'application/json' } });\n if (!response.ok) throw new Error(`Dashboard refresh failed: ${response.status}`);\n\n const data = await response.json();\n jobs.value = data.jobs;\n lastUpdated.value = new Date().toLocaleTimeString();\n refreshError.value = false;\n } catch (error) {\n console.error(error);\n refreshError.value = true;\n } finally {\n refreshing = false;\n }\n };\n\n onInit(() => {\n refreshTimer = setInterval(refreshJobs, 5000);\n });\n onDestroy(() => clearInterval(refreshTimer));\n\n return { jobs, lastUpdated, refreshError, upper };\n }\n <\/script>\n <script state>__DASHBOARD_STATE__<\/script>\n </template>\n\n <script type=\"module\">import '@li3/web';<\/script>\n <script src=\"https://cdn.tailwindcss.com\"><\/script>\n</body>\n</html>\n";
8041
+ }, Ro = "<!DOCTYPE html>\n<html lang=\"en\" class=\"dark\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Workflow Engine Dashboard</title>\n <script type=\"importmap\">\n { \"imports\": { \"@li3/\": \"https://cdn.li3.dev/@li3/\" } }\n <\/script>\n</head>\n<body class=\"bg-gray-950 text-gray-100 min-h-screen p-4 md:p-6 font-sans\">\n <template app>\n <main class=\"max-w-6xl mx-auto space-y-6\">\n <header class=\"flex flex-col sm:flex-row sm:items-center justify-between gap-3 border-b border-gray-800 pb-4\">\n <div>\n <h1 class=\"text-2xl font-bold text-white\">Runner Engine Status</h1>\n <p class=\"text-xs text-gray-400\">Live Job Queue &amp; Execution Traces</p>\n </div>\n <div class=\"flex items-center gap-3 text-xs font-mono\">\n <span class=\"text-gray-500\">Updated {{ lastUpdated }}</span>\n <span\n class=\"px-3 py-1 rounded-full border\"\n bind-class=\"{\n 'bg-emerald-500/10.text-emerald-400.border-emerald-500/20': !refreshError,\n 'bg-rose-500/10.text-rose-400.border-rose-500/20': refreshError\n }\"\n >\n {{ refreshError ? 'Refresh failed' : 'System operational' }}\n </span>\n </div>\n </header>\n\n <section class=\"bg-gray-900 border border-gray-800 rounded-xl overflow-hidden hidden md:block\">\n <table class=\"w-full text-left text-sm\">\n <thead class=\"bg-gray-800/50 text-gray-400 text-xs uppercase font-mono border-b border-gray-800\">\n <tr>\n <th class=\"py-3 px-4\">Job ID</th>\n <th class=\"py-3 px-4\">Workflow</th>\n <th class=\"py-3 px-4\">Status</th>\n <th class=\"py-3 px-4 hidden xl:table-cell\">Worker</th>\n <th class=\"py-3 px-4 hidden lg:table-cell\">Created At</th>\n <th class=\"py-3 px-4 text-right\">Action</th>\n </tr>\n </thead>\n <tbody>\n <template for=\"job of jobs\">\n <tr class=\"border-b border-gray-800 hover:bg-gray-800/40 transition\">\n <td class=\"py-3 px-4 font-mono text-indigo-400\">\n <a attr-href=\"'/runs/' + job.id\" class=\"hover:underline\">#{{ job.id }}</a>\n </td>\n <td class=\"py-3 px-4 font-medium text-white\">{{ job.workflowId }}</td>\n <td class=\"py-3 px-4\">\n <span\n class=\"px-2.5 py-0.5 rounded-full text-xs font-semibold\"\n bind-class=\"{\n 'bg-emerald-500/10.text-emerald-400': job.status === 'success',\n 'bg-rose-500/10.text-rose-400': job.status === 'failed',\n 'bg-indigo-500/10.text-indigo-400.animate-pulse': job.status === 'running',\n 'bg-gray-500/10.text-gray-400': !['success', 'failed', 'running'].includes(job.status)\n }\"\n >\n {{ upper(job.status) }}\n </span>\n </td>\n <td class=\"py-3 px-4 hidden xl:table-cell text-xs font-mono text-gray-400\">{{ job.workerId || '-' }}</td>\n <td class=\"py-3 px-4 hidden lg:table-cell text-xs text-gray-400\">{{ job.createdAt }}</td>\n <td class=\"py-3 px-4 text-right whitespace-nowrap\">\n <a attr-href=\"'/runs/' + job.id\" class=\"text-xs bg-gray-800 hover:bg-gray-700 text-gray-200 px-3 py-1 rounded border border-gray-700\">View Trace</a>\n </td>\n </tr>\n </template>\n </tbody>\n </table>\n <template if=\"!jobs.length\">\n <div class=\"p-8 text-center text-gray-500\">No jobs recorded yet.</div>\n </template>\n </section>\n\n <section class=\"md:hidden bg-gray-900 border border-gray-800 rounded-xl divide-y divide-gray-800 overflow-hidden\">\n <template for=\"job of jobs\">\n <article class=\"p-4 bg-gray-900/60\">\n <div class=\"flex items-start justify-between gap-3\">\n <div class=\"min-w-0\">\n <div class=\"flex items-center gap-2\">\n <span\n class=\"w-2.5 h-2.5 rounded-full shrink-0\"\n bind-class=\"{\n 'bg-emerald-400': job.status === 'success',\n 'bg-rose-400': job.status === 'failed',\n 'bg-indigo-400.animate-pulse': job.status === 'running',\n 'bg-gray-500': !['success', 'failed', 'running'].includes(job.status)\n }\"\n ></span>\n <h2 class=\"font-medium text-white truncate\">{{ job.workflowId }}</h2>\n </div>\n <p class=\"mt-1 pl-[18px] text-xs font-mono text-gray-400\">#{{ job.id }} · {{ upper(job.status) }}</p>\n </div>\n <a attr-href=\"'/runs/' + job.id\" class=\"shrink-0 text-xs bg-gray-800 hover:bg-gray-700 text-gray-300 px-2.5 py-1 rounded border border-gray-700\">Trace</a>\n </div>\n </article>\n </template>\n <template if=\"!jobs.length\">\n <div class=\"p-8 text-center text-gray-500\">No jobs recorded yet.</div>\n </template>\n </section>\n\n <template if=\"hasMore\">\n <div class=\"flex justify-center\">\n <button\n class=\"px-4 py-2 text-sm text-gray-200 bg-gray-800 hover:bg-gray-700 border border-gray-700 rounded-lg\"\n on-click=\"loadMore()\"\n >\n {{ loadingMore ? 'Loading...' : 'Load more' }}\n </button>\n </div>\n </template>\n </main>\n\n <script setup>\n import { onDestroy, onInit, ref } from '@li3/web';\n\n export default function setup() {\n const jobs = ref([]);\n const lastUpdated = ref('now');\n const refreshError = ref(false);\n const hasMore = ref(false);\n const loadingMore = ref(false);\n let refreshTimer;\n let eventSource;\n let refreshing = false;\n let refreshPending = false;\n\n const upper = (value) => String(value || '').toUpperCase();\n const terminalStatuses = new Set(['success', 'failed', 'cancelled']);\n const refreshJobs = async () => {\n if (refreshing) {\n refreshPending = true;\n return;\n }\n refreshing = true;\n\n try {\n const activeIds = jobs.value\n .filter((job) => !terminalStatuses.has(job.status))\n .map((job) => Number(job.id));\n const knownIds = jobs.value.map((job) => Number(job.id));\n const afterId = activeIds.length\n ? Math.max(0, Math.min(...activeIds) - 1)\n : knownIds.length\n ? Math.max(...knownIds)\n : null;\n const url = afterId === null ? '/api/jobs?limit=500' : `/api/jobs?afterId=${afterId}&limit=500`;\n const response = await fetch(url, { headers: { accept: 'application/json' } });\n if (!response.ok) throw new Error(`Dashboard refresh failed: ${response.status}`);\n\n const data = await response.json();\n const merged = new Map(jobs.value.map((job) => [job.id, job]));\n for (const job of data.jobs) merged.set(job.id, job);\n jobs.value = Array.from(merged.values()).sort((a, b) => Number(b.id) - Number(a.id));\n lastUpdated.value = new Date().toLocaleTimeString();\n refreshError.value = false;\n } catch (error) {\n console.error(error);\n refreshError.value = true;\n } finally {\n refreshing = false;\n if (refreshPending) {\n refreshPending = false;\n void refreshJobs();\n }\n }\n };\n\n const loadMore = async () => {\n if (loadingMore.value || !hasMore.value || !jobs.value.length) return;\n loadingMore.value = true;\n\n try {\n const beforeId = Math.min(...jobs.value.map((job) => Number(job.id)));\n const response = await fetch(`/api/jobs?beforeId=${beforeId}`, {\n headers: { accept: 'application/json' },\n });\n if (!response.ok) throw new Error(`Loading older jobs failed: ${response.status}`);\n\n const data = await response.json();\n const merged = new Map(jobs.value.map((job) => [job.id, job]));\n for (const job of data.jobs) merged.set(job.id, job);\n jobs.value = Array.from(merged.values()).sort((a, b) => Number(b.id) - Number(a.id));\n hasMore.value = data.hasMore;\n } catch (error) {\n console.error(error);\n refreshError.value = true;\n } finally {\n loadingMore.value = false;\n }\n };\n\n onInit(() => {\n eventSource = new EventSource('/api/events');\n eventSource.addEventListener('jobs.available', refreshJobs);\n eventSource.addEventListener('jobs.changed', refreshJobs);\n eventSource.onerror = () => {\n refreshError.value = true;\n };\n refreshTimer = setInterval(refreshJobs, 60000);\n });\n onDestroy(() => {\n eventSource?.close();\n clearInterval(refreshTimer);\n });\n\n return { jobs, lastUpdated, refreshError, hasMore, loadingMore, loadMore, upper };\n }\n <\/script>\n <script state>__DASHBOARD_STATE__<\/script>\n </template>\n\n <script type=\"module\">import '@li3/web';<\/script>\n <script src=\"https://cdn.tailwindcss.com\"><\/script>\n</body>\n</html>\n";
8033
8042
  //#endregion
8034
8043
  //#region src/dashboard.ts
8035
8044
  function zo(e) {
@@ -8042,24 +8051,84 @@ function zo(e) {
8042
8051
  updatedAt: e.updated_at
8043
8052
  }));
8044
8053
  }
8045
- function Bo(e) {
8046
- return Ro.replace("__DASHBOARD_STATE__", () => No({ jobs: e }));
8054
+ function Bo(e, t = !1) {
8055
+ return Ro.replace("__DASHBOARD_STATE__", () => No({
8056
+ jobs: e,
8057
+ hasMore: t
8058
+ }));
8059
+ }
8060
+ //#endregion
8061
+ //#region src/events.ts
8062
+ var Vo = class {
8063
+ clients = /* @__PURE__ */ new Map();
8064
+ nextId = 1;
8065
+ maxClients = 1e3;
8066
+ subscribe(e, t) {
8067
+ if (this.clients.size >= this.maxClients) {
8068
+ t.writeHead(503, {
8069
+ "Content-Type": "application/json",
8070
+ "Retry-After": "5"
8071
+ }), t.end(JSON.stringify({ error: "Too many event stream clients" }));
8072
+ return;
8073
+ }
8074
+ t.writeHead(200, {
8075
+ "Cache-Control": "no-cache, no-transform",
8076
+ Connection: "keep-alive",
8077
+ "Content-Type": "text/event-stream",
8078
+ "X-Accel-Buffering": "no"
8079
+ }), t.write("retry: 2000\n\n");
8080
+ let n = setInterval(() => t.write(": heartbeat\n\n"), 3e4);
8081
+ n.unref(), this.clients.set(t, n), e.once("close", () => {
8082
+ clearInterval(n), this.clients.delete(t);
8083
+ });
8084
+ }
8085
+ publish(e, t = {}) {
8086
+ let n = `id: ${this.nextId++}\nevent: ${e}\ndata: ${JSON.stringify(t)}\n\n`;
8087
+ for (let [e, t] of this.clients) e.write(n) || (clearInterval(t), this.clients.delete(e), e.end());
8088
+ }
8089
+ close() {
8090
+ for (let [e, t] of this.clients) clearInterval(t), e.end();
8091
+ this.clients.clear();
8092
+ }
8093
+ };
8094
+ async function Ho(e, t, n, r) {
8095
+ let i = await fetch(new URL("/api/events", e), {
8096
+ headers: { Accept: "text/event-stream" },
8097
+ signal: t
8098
+ });
8099
+ if (!i.ok || !i.body) throw Error(`Event stream failed with HTTP ${i.status}`);
8100
+ r?.();
8101
+ let a = new TextDecoder(), o = "";
8102
+ for await (let e of i.body) {
8103
+ o += a.decode(e, { stream: !0 }).replaceAll("\r\n", "\n");
8104
+ let t = o.indexOf("\n\n");
8105
+ for (; t !== -1;) {
8106
+ let e = o.slice(0, t);
8107
+ o = o.slice(t + 2), t = o.indexOf("\n\n");
8108
+ let r = e.match(/^event: (.+)$/m)?.[1];
8109
+ if (!r) continue;
8110
+ let i = e.match(/^data: (.*)$/m)?.[1];
8111
+ n(r, i ? JSON.parse(i) : {});
8112
+ }
8113
+ }
8047
8114
  }
8048
8115
  //#endregion
8049
8116
  //#region src/server.ts
8050
- var Vo = class e {
8117
+ var Uo = 50, Wo = 500, Go = class e {
8051
8118
  server;
8052
8119
  preprocessors = /* @__PURE__ */ new Map();
8053
8120
  workflows = [];
8054
8121
  queue;
8055
8122
  secrets;
8056
8123
  adminToken;
8124
+ events = new Vo();
8125
+ workflowsLoaded;
8057
8126
  static async withPort(t) {
8058
8127
  let { port: n, ...r } = t;
8059
8128
  return new e(r).listen(n);
8060
8129
  }
8061
8130
  constructor(e) {
8062
- this.queue = e.queue, this.secrets = e.secrets, this.adminToken = e.adminToken, Lo.from(e.config.workflows).then((t) => {
8131
+ this.queue = e.queue, this.secrets = e.secrets, this.adminToken = e.adminToken, this.workflowsLoaded = Lo.from(e.config.workflows).then((t) => {
8063
8132
  this.workflows = t, console.log(`✅ Loaded ${t.length} workflow(s) from ${e.config.workflows}`);
8064
8133
  }), this.registerPreprocessor(new Mo()), this.server = f.createServer((e, t) => this.handleRequest(e, t));
8065
8134
  }
@@ -8069,7 +8138,12 @@ var Vo = class e {
8069
8138
  async handleRequest(e, t) {
8070
8139
  let n = new p(e.url || "/", `${e.headers["x-forwarded-proto"] || "http"}://${e.headers["x-forwarded-host"] || e.headers.host}`);
8071
8140
  if (e.method === "GET" && (n.pathname === "/runs" || n.pathname === "/")) return this.renderDashboard(t);
8072
- if (e.method === "GET" && n.pathname === "/api/jobs") return this.renderDashboardJobs(t);
8141
+ if (e.method === "GET" && n.pathname === "/api/jobs") {
8142
+ let e = n.searchParams.get("afterId"), r = n.searchParams.get("beforeId"), i = n.searchParams.get("limit"), a = e === null ? void 0 : Number(e), o = r === null ? void 0 : Number(r), s = i === null ? Uo : Number(i);
8143
+ return a !== void 0 && (!Number.isSafeInteger(a) || a < 0) ? (t.writeHead(400, { "Content-Type": "application/json; charset=utf-8" }), t.end(JSON.stringify({ error: "afterId must be a non-negative integer" }))) : o !== void 0 && (!Number.isSafeInteger(o) || o < 1) ? (t.writeHead(400, { "Content-Type": "application/json; charset=utf-8" }), t.end(JSON.stringify({ error: "beforeId must be a positive integer" }))) : !Number.isSafeInteger(s) || s < 1 || s > Wo ? (t.writeHead(400, { "Content-Type": "application/json; charset=utf-8" }), t.end(JSON.stringify({ error: `limit must be an integer from 1 to ${Wo}` }))) : this.renderDashboardJobs(t, s, a, o);
8144
+ }
8145
+ if (e.method === "GET" && n.pathname === "/api/events") return this.events.subscribe(e, t);
8146
+ if (e.method === "POST" && n.pathname === "/api/events") return this.handleWorkerEvent(e, t);
8073
8147
  if (e.method === "GET" && n.pathname.startsWith("/runs/")) {
8074
8148
  let e = n.pathname.replace("/runs/", "");
8075
8149
  return this.renderRunDetails(e, t);
@@ -8094,7 +8168,7 @@ var Vo = class e {
8094
8168
  n.writeHead(401, { "Content-Type": "application/json" }), n.end(JSON.stringify({ error: "Invalid HMAC signature or authentication failed" }));
8095
8169
  return;
8096
8170
  }
8097
- this.matchWorkflows(e, o), n.writeHead(202, { "Content-Type": "application/json" }), n.end(JSON.stringify({ message: "OK" }));
8171
+ await this.workflowsLoaded, await this.matchWorkflows(e, o), n.writeHead(202, { "Content-Type": "application/json" }), n.end(JSON.stringify({ message: "OK" }));
8098
8172
  } catch (e) {
8099
8173
  console.error("❌ Webhook Ingress Error:", e), n.writeHead(500, { "Content-Type": "application/json" }), n.end(JSON.stringify({
8100
8174
  error: "Internal Ingress Error",
@@ -8160,25 +8234,33 @@ var Vo = class e {
8160
8234
  workflowId: n.id,
8161
8235
  env: n.env,
8162
8236
  steps: n.steps,
8163
- inputs: t
8237
+ inputs: t,
8238
+ tags: n.tags
8164
8239
  };
8165
- await this.queue.enqueue(n.id, a, i);
8240
+ await this.queue.enqueue(n.id, a, i), this.events.publish("jobs.available", { tags: n.tags || [] });
8166
8241
  }
8167
8242
  }
8168
8243
  async handleSecretReload(e, t) {
8169
8244
  if (e.headers.authorization !== `Bearer ${this.adminToken}`) return t.writeHead(403, { "Content-Type": "application/json" }), t.end(JSON.stringify({ error: "Unauthorized" }));
8170
8245
  this.secrets.reload(), console.log("🔄 SecretStore reloaded successfully without downtime!"), t.writeHead(200, { "Content-Type": "application/json" }), t.end(JSON.stringify({ message: "Secrets reloaded successfully" }));
8171
8246
  }
8247
+ async handleWorkerEvent(e, t) {
8248
+ if (!this.adminToken || e.headers.authorization !== `Bearer ${this.adminToken}`) return t.writeHead(403, { "Content-Type": "application/json" }), t.end(JSON.stringify({ error: "Unauthorized" }));
8249
+ this.events.publish("jobs.changed"), t.writeHead(202).end();
8250
+ }
8172
8251
  async renderDashboard(e) {
8173
- let t = zo(await this.queue.listJobs(500)), n = this.secrets.redactText(Bo(t));
8174
- e.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }), e.end(n);
8252
+ let t = await this.queue.listJobs(51), n = zo(t.slice(0, Uo)), r = this.secrets.redactText(Bo(n, t.length > Uo));
8253
+ e.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }), e.end(r);
8175
8254
  }
8176
- async renderDashboardJobs(e) {
8177
- let t = zo(await this.queue.listJobs(500)), n = this.secrets.redactText(JSON.stringify({ jobs: t }));
8255
+ async renderDashboardJobs(e, t, n, r) {
8256
+ let i = await this.queue.listJobs(t + 1, n, r), a = zo(i.slice(0, t)), o = this.secrets.redactText(JSON.stringify({
8257
+ jobs: a,
8258
+ hasMore: i.length > t
8259
+ }));
8178
8260
  e.writeHead(200, {
8179
8261
  "Cache-Control": "no-store",
8180
8262
  "Content-Type": "application/json; charset=utf-8"
8181
- }), e.end(n);
8263
+ }), e.end(o);
8182
8264
  }
8183
8265
  async renderRunDetails(e, t) {
8184
8266
  let n = await this.queue.getJob(e);
@@ -8222,7 +8304,7 @@ var Vo = class e {
8222
8304
  async handleRestartJob(e, t) {
8223
8305
  let n = await this.queue.restartJob(e);
8224
8306
  if (n) {
8225
- t.writeHead(201, { "Content-Type": "application/json" }), t.end(JSON.stringify({ id: n }));
8307
+ this.events.publish("jobs.available"), t.writeHead(201, { "Content-Type": "application/json" }), t.end(JSON.stringify({ id: n }));
8226
8308
  return;
8227
8309
  }
8228
8310
  t.writeHead(404, { "Content-Type": "application/json" }), t.end(JSON.stringify({ error: "Job not found" }));
@@ -8235,13 +8317,13 @@ var Vo = class e {
8235
8317
  });
8236
8318
  }
8237
8319
  async stop() {
8238
- return new Promise((e) => {
8320
+ return this.events.close(), new Promise((e) => {
8239
8321
  this.server.close(() => {
8240
8322
  console.log("🌐 Webhook Ingress Server stopped listening."), e();
8241
8323
  });
8242
8324
  });
8243
8325
  }
8244
- }, Ho = d(v), Uo = class {
8326
+ }, Ko = d(v), qo = class {
8245
8327
  name = "systemd";
8246
8328
  async isSupported() {
8247
8329
  try {
@@ -8364,7 +8446,7 @@ var Vo = class e {
8364
8446
  cancel: async () => {
8365
8447
  d = !0;
8366
8448
  try {
8367
- t.image && await Ho(`docker stop -t 2 ${o}`).catch(() => {}), await Ho(`systemctl stop ${o}.service`).catch(() => {});
8449
+ t.image && await Ko(`docker stop -t 2 ${o}`).catch(() => {}), await Ko(`systemctl stop ${o}.service`).catch(() => {});
8368
8450
  } catch {}
8369
8451
  },
8370
8452
  logFilePath: i
@@ -8374,7 +8456,7 @@ var Vo = class e {
8374
8456
  let t = await g(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;
8375
8457
  return t.slice(n, r);
8376
8458
  }
8377
- }, Wo = class {
8459
+ }, Jo = class {
8378
8460
  name = "standard-process";
8379
8461
  async isSupported() {
8380
8462
  return !0;
@@ -8489,13 +8571,13 @@ var Vo = class e {
8489
8571
  };
8490
8572
  //#endregion
8491
8573
  //#region src/drivers/index.ts
8492
- async function Go() {
8493
- let e = new Uo();
8494
- return await e.isSupported() ? (console.log("⚡ Selected Execution Driver: Systemd (cgroups enabled)"), e) : (console.log("📦 Selected Execution Driver: Standard Process (Fallback)"), new Wo());
8574
+ async function Yo() {
8575
+ let e = new qo();
8576
+ return await e.isSupported() ? (console.log("⚡ Selected Execution Driver: Systemd (cgroups enabled)"), e) : (console.log("📦 Selected Execution Driver: Standard Process (Fallback)"), new Jo());
8495
8577
  }
8496
8578
  //#endregion
8497
8579
  //#region src/signals.ts
8498
- async function Ko(e) {
8580
+ async function Xo(e) {
8499
8581
  let t = !1;
8500
8582
  async function n(n) {
8501
8583
  if (!t) {
@@ -8503,7 +8585,7 @@ async function Ko(e) {
8503
8585
  console.error("⚠️ Graceful shutdown timed out after 10s. Forcing exit!"), process.exit(1);
8504
8586
  }, 1e4).unref();
8505
8587
  try {
8506
- Jo.isStopping = !0, 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);
8588
+ ns(), 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);
8507
8589
  } catch (e) {
8508
8590
  console.error("❌ Error during graceful shutdown:", e.message), process.exit(1);
8509
8591
  }
@@ -8513,34 +8595,99 @@ async function Ko(e) {
8513
8595
  }
8514
8596
  //#endregion
8515
8597
  //#region src/worker.ts
8516
- var qo = !!process.env.DEBUG, Jo = { isStopping: !1 };
8517
- function Yo(e, t, n, r) {
8518
- Jo.isStopping = !1;
8519
- let i = Array.from({ length: e }, (e, i) => Xo(`worker-${i + 1}`, t, n, r));
8520
- return Ko(i), i;
8521
- }
8522
- async function Xo(e, t, n, r) {
8523
- let i = await Go();
8524
- for (console.log(`[${e}] 🚀 Worker started. Driver: ${i.name}`); !Jo.isStopping;) try {
8525
- let a = await t.claimNextJob();
8526
- if (!a) {
8527
- await new Promise((e) => setTimeout(e, 2e3));
8528
- continue;
8598
+ var Zo = !!process.env.DEBUG, Qo = { isStopping: !1 }, $o = /* @__PURE__ */ new Set(), es = null, ts = null;
8599
+ function ns() {
8600
+ Qo.isStopping = !0, es?.abort(), ts?.();
8601
+ }
8602
+ function rs(e, t, n, r) {
8603
+ Qo.isStopping = !1;
8604
+ let i = [is(e, t, n, r)];
8605
+ return Xo(i), i;
8606
+ }
8607
+ async function is(e, t, n, r) {
8608
+ let i = await Yo(), a = /* @__PURE__ */ new Set(), o = 0, s = null, c = () => {
8609
+ o++, s?.();
8610
+ };
8611
+ ts = c, es = new AbortController();
8612
+ let l = as(r, es.signal, c);
8613
+ for (console.log(`🚀 Worker scheduler started. Driver: ${i.name}. Concurrency: ${e}. Tags: ${r.tags.join(", ") || "(none)"}`); !Qo.isStopping;) {
8614
+ let l = o;
8615
+ try {
8616
+ for (; a.size < e && !Qo.isStopping;) {
8617
+ let e = await t.claimNextJob(r.tags);
8618
+ if (!e) break;
8619
+ if (Qo.isStopping) {
8620
+ await t.releaseJob(e.id);
8621
+ break;
8622
+ }
8623
+ let o = `worker-${e.id}`, s;
8624
+ s = (async () => {
8625
+ cs(r), await ls({
8626
+ workerId: o,
8627
+ job: e,
8628
+ queue: t,
8629
+ secrets: n,
8630
+ config: r,
8631
+ driver: i
8632
+ });
8633
+ })().catch((e) => console.error(`[${o}] ⚠️ Worker execution error:`, e)).finally(() => {
8634
+ a.delete(s), cs(r), c();
8635
+ }), a.add(s);
8636
+ }
8637
+ } catch (e) {
8638
+ console.error("⚠️ Worker scheduler claim error:", e), setTimeout(c, 5e3).unref();
8639
+ }
8640
+ !Qo.isStopping && o === l && (await os(l, () => o, (e) => s = e), s = null);
8641
+ }
8642
+ es.abort(), await Promise.allSettled(a), await l, ts = null, console.log("🛑 Worker scheduler stopped cleanly.");
8643
+ }
8644
+ async function as(e, t, n) {
8645
+ let r = 1e3;
8646
+ for (; !t.aborted;) {
8647
+ try {
8648
+ await Ho(e.serverUrl, t, (t, r) => {
8649
+ t === "jobs.available" && (Array.isArray(r.tags) ? r.tags.filter((e) => typeof e == "string") : []).every((t) => e.tags.includes(t)) && n();
8650
+ }, n), r = 1e3;
8651
+ } catch (e) {
8652
+ if (t.aborted || e?.name === "AbortError") break;
8653
+ console.error(`⚠️ Worker event stream disconnected: ${e.message}`);
8529
8654
  }
8530
- await Zo({
8531
- workerId: e,
8532
- job: a,
8533
- queue: t,
8534
- secrets: n,
8535
- config: r,
8536
- driver: i
8655
+ await ss(r, t), r = Math.min(r * 2, 3e4);
8656
+ }
8657
+ }
8658
+ function os(e, t, n) {
8659
+ return new Promise((r) => {
8660
+ if (t() !== e) return r();
8661
+ let i = setTimeout(r, 6e4);
8662
+ n(() => {
8663
+ clearTimeout(i), r();
8664
+ });
8665
+ });
8666
+ }
8667
+ function ss(e, t) {
8668
+ return new Promise((n) => {
8669
+ if (t.aborted) return n();
8670
+ let r = () => {
8671
+ clearTimeout(i), n();
8672
+ }, i = setTimeout(() => {
8673
+ t.removeEventListener("abort", r), n();
8674
+ }, e);
8675
+ t.addEventListener("abort", r, { once: !0 });
8676
+ });
8677
+ }
8678
+ async function cs(e) {
8679
+ if (e.adminToken) try {
8680
+ let t = await fetch(new URL("/api/events", e.serverUrl), {
8681
+ method: "POST",
8682
+ headers: { Authorization: `Bearer ${e.adminToken}` },
8683
+ signal: AbortSignal.timeout(5e3)
8537
8684
  });
8538
- } catch (t) {
8539
- console.error(`[${e}] ⚠️ Worker execution loop error:`, t), await new Promise((e) => setTimeout(e, 5e3));
8685
+ !t.ok && Zo && console.error(`Failed to publish job status event: HTTP ${t.status}`);
8686
+ } catch (e) {
8687
+ Zo && console.error("Failed to publish job status event:", e);
8540
8688
  }
8541
- console.log(`[${e}] 🛑 Worker loop stopped cleanly.`);
8542
8689
  }
8543
- async function Zo(e) {
8690
+ async function ls(e) {
8544
8691
  let { workerId: t, job: n, config: r, secrets: i, queue: o } = e;
8545
8692
  console.log(`\n[${t}] 📦 Claimed Job #${n.id} (Workflow: ${n.workflow_id})`);
8546
8693
  let s = typeof n.payload == "string" ? JSON.parse(n.payload) : n.payload, c = s.steps || [], l = s.inputs || {}, u = Date.now(), d = a.join(r.storagePath, `job-${n.id}`), f = a.join(d, "logs"), p = a.join(d, "wd"), m = {
@@ -8557,20 +8704,20 @@ async function Zo(e) {
8557
8704
  durationMs: 0,
8558
8705
  outputs: {},
8559
8706
  logContent: ""
8560
- })), g = is(n, "running", u, l, m.env, h, s);
8707
+ })), g = gs(n, "running", u, l, m.env, h, s);
8561
8708
  await o.saveReport(n.id, g);
8562
- let { cancelled: _, failed: v } = await Qo({
8709
+ let { cancelled: _, failed: v } = await us({
8563
8710
  payload: s,
8564
8711
  steps: c,
8565
8712
  executionContext: m,
8566
8713
  ...e
8567
8714
  }, g), y = _ ? "cancelled" : v ? "failed" : "success";
8568
- g.status = y, g.durationMs = Date.now() - u, g.finishedAt = (/* @__PURE__ */ new Date()).toISOString(), await o.completeJob(n.id, y, g), console.log(`[${t}] ✅ Job #${n.id} completed as: ${y}`), await as(t, r.reporters, g);
8715
+ g.status = y, g.durationMs = Date.now() - u, g.finishedAt = (/* @__PURE__ */ new Date()).toISOString(), await o.completeJob(n.id, y, g), console.log(`[${t}] ✅ Job #${n.id} completed as: ${y}`), await _s(t, r.reporters, g);
8569
8716
  }
8570
- async function Qo(e, t) {
8717
+ async function us(e, t) {
8571
8718
  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;
8572
8719
  try {
8573
- s.env && Object.assign(l.env, await ts(s.env, l));
8720
+ s.env && Object.assign(l.env, await ps(s.env, l));
8574
8721
  for (let e = 0; e < c.length; e++) {
8575
8722
  let s = c[e], m = (/* @__PURE__ */ new Date()).toISOString();
8576
8723
  u[e] = {
@@ -8584,7 +8731,7 @@ async function Qo(e, t) {
8584
8731
  startedAt: m,
8585
8732
  finishedAt: void 0
8586
8733
  }, t.durationMs = Date.now() - Date.parse(t.startedAt), await i.saveReport(o.id, t);
8587
- let h = await $o({
8734
+ let h = await ds({
8588
8735
  workerId: r,
8589
8736
  jobId: o.id,
8590
8737
  step: s,
@@ -8624,22 +8771,22 @@ async function Qo(e, t) {
8624
8771
  } catch (e) {
8625
8772
  console.log("🛑 Step failed", e), d = !0;
8626
8773
  }
8627
- return p < c.length && (rs(c, p, u), t.durationMs = Date.now() - Date.parse(t.startedAt), await i.saveReport(o.id, t)), {
8774
+ return p < c.length && (hs(c, p, u), t.durationMs = Date.now() - Date.parse(t.startedAt), await i.saveReport(o.id, t)), {
8628
8775
  failed: d,
8629
8776
  cancelled: f,
8630
8777
  stepReports: u
8631
8778
  };
8632
8779
  }
8633
- async function $o(e) {
8780
+ async function ds(e) {
8634
8781
  let { step: t, stepIndex: n, executionContext: r } = e;
8635
- if (t.id ||= `step-${n}`, t.name ||= t.id, t.if && !await ko.evaluateConditions(t.if, r)) return qo && console.log(`⏩ Skipped step ${t.id} based on condition: ${t.if}`, r), {
8782
+ if (t.id ||= `step-${n}`, t.name ||= t.id, t.if && !await ko.evaluateConditions(t.if, r)) return Zo && console.log(`⏩ Skipped step ${t.id} based on condition: ${t.if}`, r), {
8636
8783
  failed: !1,
8637
8784
  cancelled: !1,
8638
8785
  skipped: !0,
8639
8786
  report: null
8640
8787
  };
8641
8788
  try {
8642
- let n = await ts(t.env, r), i = {
8789
+ let n = await ps(t.env, r), i = {
8643
8790
  jobId: String(e.jobId),
8644
8791
  step: t,
8645
8792
  command: t.run,
@@ -8653,15 +8800,15 @@ async function $o(e) {
8653
8800
  WORKING_DIR: r.workingDir
8654
8801
  }
8655
8802
  };
8656
- return t.eval ? es({
8803
+ return t.eval ? fs({
8657
8804
  ...e,
8658
8805
  stepContext: i
8659
- }) : ns({
8806
+ }) : ms({
8660
8807
  ...e,
8661
8808
  stepContext: i
8662
8809
  });
8663
8810
  } catch (e) {
8664
- return qo && console.error(`⏩ Failed to run step ${t.id}`, r, e), {
8811
+ return Zo && console.error(`⏩ Failed to run step ${t.id}`, r, e), {
8665
8812
  failed: !0,
8666
8813
  cancelled: !1,
8667
8814
  skipped: !1,
@@ -8669,7 +8816,7 @@ async function $o(e) {
8669
8816
  };
8670
8817
  }
8671
8818
  }
8672
- async function es(e) {
8819
+ async function fs(e) {
8673
8820
  let { queue: t, stepContext: n, executionContext: r } = e, { jobId: i, step: a } = n, o = Date.now(), s = a.id, c = a.name;
8674
8821
  try {
8675
8822
  let e = await ko.evaluateExpression(a.eval, r);
@@ -8715,12 +8862,12 @@ async function es(e) {
8715
8862
  };
8716
8863
  }
8717
8864
  }
8718
- async function ts(e, t) {
8865
+ async function ps(e, t) {
8719
8866
  let n = {};
8720
8867
  if (e) for (let [r, i] of Object.entries(e)) n[r] = String(await ko.evaluateValue(i, t));
8721
8868
  return n;
8722
8869
  }
8723
- async function ns(t) {
8870
+ async function ms(t) {
8724
8871
  let { workerId: n, jobId: r, stepContext: i, executionContext: a, driver: o, queue: s } = t, { step: c } = i, l = c.id, u = c.name, d;
8725
8872
  try {
8726
8873
  d = o.execute(i);
@@ -8735,10 +8882,11 @@ async function ns(t) {
8735
8882
  logFilePath: ""
8736
8883
  };
8737
8884
  }
8885
+ $o.add(d);
8738
8886
  let f = !1, p = setInterval(async () => {
8739
8887
  await s.isCancelled(r) && (console.log(`[${n}] 🛑 Job #${r} was cancelled! Halting execution.`), f = !0, clearInterval(p), await d.cancel());
8740
8888
  }, 3e3), m = await d.done;
8741
- if (clearInterval(p), d.logFilePath && e.existsSync(d.logFilePath)) try {
8889
+ if (clearInterval(p), $o.delete(d), d.logFilePath && e.existsSync(d.logFilePath)) try {
8742
8890
  let e = await o.readLog(d.logFilePath);
8743
8891
  await s.saveStepLog(r, l, e);
8744
8892
  } catch (e) {
@@ -8765,7 +8913,7 @@ async function ns(t) {
8765
8913
  }
8766
8914
  };
8767
8915
  }
8768
- function rs(e, t, n) {
8916
+ function hs(e, t, n) {
8769
8917
  for (let r = t; r < e.length; r++) {
8770
8918
  let t = e[r], i = t.id || `step-${r}`;
8771
8919
  n[r] = {
@@ -8779,7 +8927,7 @@ function rs(e, t, n) {
8779
8927
  };
8780
8928
  }
8781
8929
  }
8782
- function is(e, t, n, r, i, a, o) {
8930
+ function gs(e, t, n, r, i, a, o) {
8783
8931
  return {
8784
8932
  jobId: String(e.id),
8785
8933
  parentId: String(e.parentId || ""),
@@ -8798,7 +8946,7 @@ function is(e, t, n, r, i, a, o) {
8798
8946
  })
8799
8947
  };
8800
8948
  }
8801
- async function as(e, t = [], n) {
8949
+ async function _s(e, t = [], n) {
8802
8950
  !Array.isArray(t) || t.length === 0 || (console.log(`[${e}] 📢 Dispatching execution report to ${t.length} reporter(s)...`), await Promise.allSettled(t.map(async (t) => {
8803
8951
  try {
8804
8952
  await t.report(n);
@@ -8809,7 +8957,7 @@ async function as(e, t = [], n) {
8809
8957
  }
8810
8958
  //#endregion
8811
8959
  //#region src/reporters/json-file.reporter.ts
8812
- var os = class {
8960
+ var vs = class {
8813
8961
  name = "json-file";
8814
8962
  outputDir;
8815
8963
  constructor(e) {
@@ -8820,7 +8968,7 @@ var os = class {
8820
8968
  let n = a.join(this.outputDir, `run-${t.jobId}.json`);
8821
8969
  e.writeFileSync(n, JSON.stringify(t, null, 2), "utf-8"), console.log(`📊 Execution report saved to: ${n}`);
8822
8970
  }
8823
- }, ss = class {
8971
+ }, ys = class {
8824
8972
  name = "slack";
8825
8973
  webhookUrl = "";
8826
8974
  token = "";
@@ -8847,7 +8995,7 @@ var os = class {
8847
8995
  };
8848
8996
  //#endregion
8849
8997
  //#region src/index.ts
8850
- function cs(e) {
8998
+ function bs(e) {
8851
8999
  console.log("🔍 Validating Workflows in:", e.workflows);
8852
9000
  let t = new di(e.workflows), n = r(e.workflows).filter((e) => e.endsWith(".yml") || e.endsWith(".yaml"));
8853
9001
  for (let e of n) {
@@ -8855,13 +9003,13 @@ function cs(e) {
8855
9003
  console.log(` ✅ ${e} -> Valid! (${n.length} job matrix variant(s) generated)`);
8856
9004
  }
8857
9005
  }
8858
- async function ls() {
9006
+ async function xs() {
8859
9007
  let { config: e, command: t } = await fe();
8860
- if (e || process.exit(1), t === "validate") return cs(e);
9008
+ if (e || process.exit(1), t === "validate") return bs(e);
8861
9009
  let n = new mi("./.env"), r = new pi(process.env.WORKER_NAME || "cli");
8862
9010
  switch (t) {
8863
9011
  case "start-server":
8864
- console.log("🌐 Starting Ingress Gateway..."), await r.init(), await Vo.withPort({
9012
+ console.log("🌐 Starting Ingress Gateway..."), await r.init(), await Go.withPort({
8865
9013
  config: e,
8866
9014
  queue: r,
8867
9015
  secrets: n,
@@ -8870,11 +9018,11 @@ async function ls() {
8870
9018
  });
8871
9019
  break;
8872
9020
  case "start-workers":
8873
- console.log(`⚙️ Starting ${e.workers} Worker Loop(s)...`), await r.init(), Yo(e.workers, r, n, e);
9021
+ console.log(`⚙️ Starting worker scheduler with ${e.workers} concurrent slot(s)...`), await r.init(), rs(e.workers, r, n, e);
8874
9022
  break;
8875
9023
  default: console.error(`❌ Unknown command: '${t}'`), de(), process.exit(1);
8876
9024
  }
8877
9025
  }
8878
- ls().catch(console.error);
9026
+ xs().catch(console.error);
8879
9027
  //#endregion
8880
- export { Io as HtmlReporter, os as JsonFileReporter, ss as SlackReporter };
9028
+ export { Io as HtmlReporter, vs as JsonFileReporter, ys as SlackReporter };
@@ -1 +1 @@
1
- {"version":3,"file":"yaml-loader.d.ts","sourceRoot":"","sources":["../../src/parser/yaml-loader.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGjD,qBAAa,UAAU;WACR,IAAI,CAAC,IAAI,EAAE,MAAM;IAiB9B,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,uBAAuB;CAqChE"}
1
+ {"version":3,"file":"yaml-loader.d.ts","sourceRoot":"","sources":["../../src/parser/yaml-loader.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGjD,qBAAa,UAAU;WACR,IAAI,CAAC,IAAI,EAAE,MAAM;IAiB9B,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,uBAAuB;CAwChE"}
package/dist/queue.d.ts CHANGED
@@ -9,10 +9,11 @@ export declare class QueueManager {
9
9
  */
10
10
  enqueue(workflowId: string, payload: JobPayload, concurrencyKey?: string): Promise<any>;
11
11
  /**
12
- * ATOMICALY claims the oldest pending job.
12
+ * Atomically claims the oldest pending job compatible with this worker.
13
13
  * Requires SQLite >= 3.35 for the RETURNING clause.
14
14
  */
15
- claimNextJob(): Promise<JobRecord | null>;
15
+ claimNextJob(workerTags?: string[]): Promise<JobRecord | null>;
16
+ releaseJob(jobId: string | number): Promise<void>;
16
17
  /**
17
18
  * Marks a job as completed or failed
18
19
  */
@@ -32,7 +33,7 @@ export declare class QueueManager {
32
33
  /**
33
34
  * List recent jobs for dashboard status monitoring
34
35
  */
35
- listJobs(limit?: number): Promise<any[]>;
36
+ listJobs(limit?: number, afterId?: number, beforeId?: number): Promise<any[]>;
36
37
  /**
37
38
  * Save lightweight summary report (NO heavy logs in this JSON!)
38
39
  */
@@ -1 +1 @@
1
- {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvF,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ;gBAAR,QAAQ,EAAE,MAAM;IAE9B,IAAI;IAKV;;;OAGG;IACG,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,CAAC,EAAE,MAAM;IAkB9E;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAwB/C;;OAEG;IACG,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,SAAS;IAInD,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAStG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAiBvC;;OAEG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKrD,cAAc;IAMd,YAAY;IAoClB;;OAEG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAIlD;;OAEG;IACG,QAAQ,CAAC,KAAK,SAAK,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAI1C;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAOxF,2DAA2D;IACrD,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU5F;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAS1E"}
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvF,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ;gBAAR,QAAQ,EAAE,MAAM;IAE9B,IAAI;IAKV;;;OAGG;IACG,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,CAAC,EAAE,MAAM;IAkB9E;;;OAGG;IACG,YAAY,CAAC,UAAU,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IA6BlE,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOvD;;OAEG;IACG,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,SAAS;IAInD,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAStG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAiBvC;;OAEG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKrD,cAAc;IAMd,YAAY;IAoClB;;OAEG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAIlD;;OAEG;IACG,QAAQ,CAAC,KAAK,SAAK,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAiB/E;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAOxF,2DAA2D;IACrD,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU5F;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAS1E"}
package/dist/server.d.ts CHANGED
@@ -6,6 +6,8 @@ export declare class WebhookServer {
6
6
  private queue;
7
7
  private secrets;
8
8
  private adminToken;
9
+ private events;
10
+ private workflowsLoaded;
9
11
  static withPort(options: WebhookServerOptions & {
10
12
  port: number;
11
13
  }): Promise<WebhookServer>;
@@ -23,6 +25,7 @@ export declare class WebhookServer {
23
25
  * Handles Zero-Downtime Secret Reload
24
26
  */
25
27
  private handleSecretReload;
28
+ private handleWorkerEvent;
26
29
  /**
27
30
  * Serves the Server Health & Jobs Dashboard
28
31
  */
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAEV,mBAAmB,EACnB,oBAAoB,EAGrB,MAAM,YAAY,CAAC;AAIpB,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,aAAa,CAA0C;IAC/D,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,UAAU,CAAS;WAEd,QAAQ,CAAC,OAAO,EAAE,oBAAoB,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;gBAK1D,OAAO,EAAE,oBAAoB;IAczC,oBAAoB,CAAC,YAAY,EAAE,mBAAmB;YAIxC,aAAa;IAsC3B;;OAEG;YACW,aAAa;YAyBb,WAAW;IAwBzB,OAAO,CAAC,UAAU;YAiBJ,cAAc;IAyC5B;;OAEG;YACW,kBAAkB;IAehC;;OAEG;YACW,eAAe;YAOf,mBAAmB;IAUjC;;OAEG;YACW,gBAAgB;IAkC9B,OAAO,CAAC,kBAAkB;YA0BZ,gBAAgB;IAa9B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAStC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ5B"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAEV,mBAAmB,EACnB,oBAAoB,EAGrB,MAAM,YAAY,CAAC;AAQpB,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,aAAa,CAA0C;IAC/D,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,eAAe,CAAgB;WAE1B,QAAQ,CAAC,OAAO,EAAE,oBAAoB,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;gBAK1D,OAAO,EAAE,oBAAoB;IAczC,oBAAoB,CAAC,YAAY,EAAE,mBAAmB;YAIxC,aAAa;IAkE3B;;OAEG;YACW,aAAa;YA0Bb,WAAW;IAwBzB,OAAO,CAAC,UAAU;YAiBJ,cAAc;IA2C5B;;OAEG;YACW,kBAAkB;YAelB,iBAAiB;IAU/B;;OAEG;YACW,eAAe;YAQf,mBAAmB;IAWjC;;OAEG;YACW,gBAAgB;IAkC9B,OAAO,CAAC,kBAAkB;YA0BZ,gBAAgB;IAc9B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAStC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAS5B"}
package/dist/types.d.ts CHANGED
@@ -15,6 +15,10 @@ export interface RunnerConfig {
15
15
  workflows: string;
16
16
  /** Number of concurrent worker loops to spawn */
17
17
  workers: number;
18
+ /** Webhook server URL used for worker event notifications */
19
+ serverUrl: string;
20
+ /** Capabilities advertised by this worker node */
21
+ tags: string[];
18
22
  /** Storage path for job workspaces and step logs */
19
23
  storagePath: string;
20
24
  /** Global environment variables injected into all step runs */
@@ -105,6 +109,8 @@ export interface WorkflowDefinition {
105
109
  };
106
110
  steps: WorkflowStep[];
107
111
  env?: Record<string, string>;
112
+ /** Worker capabilities required to execute this workflow */
113
+ tags?: string[];
108
114
  }
109
115
  export interface WebhookServerOptions {
110
116
  config: RunnerConfig;
@@ -129,6 +135,7 @@ export interface JobPayload {
129
135
  steps: WorkflowStep[];
130
136
  inputs: WorkflowInputs;
131
137
  env?: Record<string, string>;
138
+ tags?: string[];
132
139
  }
133
140
  export interface MatrixStrategy {
134
141
  matrix?: Record<string, (string | number | boolean)[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/worker.d.ts CHANGED
@@ -8,14 +8,15 @@ export declare const shutdownState: {
8
8
  * Called by signal handlers in index.ts during graceful shutdown
9
9
  */
10
10
  export declare function abortActiveWorkerTask(): Promise<void>;
11
+ export declare function requestWorkerShutdown(): void;
11
12
  /**
12
13
  * Spawns worker loops as requested by configuration
13
14
  */
14
15
  export declare function startWorkers(count: number, queue: QueueManager, secrets: SecretStore, config: RunnerConfig): Promise<void>[];
15
16
  /**
16
- * Main worker polling loop
17
+ * Event-driven worker scheduler with bounded local concurrency.
17
18
  */
18
- export declare function startWorkerLoop(workerId: string, queue: QueueManager, secrets: SecretStore, config: RunnerConfig): Promise<void>;
19
+ export declare function startWorkerScheduler(concurrency: number, queue: QueueManager, secrets: SecretStore, config: RunnerConfig): Promise<void>;
19
20
  /**
20
21
  * Processes a single job sequentially
21
22
  */
@@ -1 +1 @@
1
- {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAGL,YAAY,EAOZ,WAAW,EAIZ,MAAM,YAAY,CAAC;AAKpB,eAAO,MAAM,aAAa;;CAEzB,CAAC;AAKF;;GAEG;AACH,wBAAsB,qBAAqB,kBAM1C;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,mBAO1G;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,YAAY,iBAsBrB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAAE,WAAW,iBA2D9C"}
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAGL,YAAY,EAOZ,WAAW,EAIZ,MAAM,YAAY,CAAC;AAMpB,eAAO,MAAM,aAAa;;CAEzB,CAAC;AAMF;;GAEG;AACH,wBAAsB,qBAAqB,kBAK1C;AAED,wBAAgB,qBAAqB,SAIpC;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,mBAK1G;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,YAAY,iBA4DrB;AA4ED;;GAEG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAAE,WAAW,iBA2D9C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloud-cli/on",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "CLI entry point for the on plugin ecosystem.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.30.3",