@cloud-cli/on 1.8.1 → 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/',
@@ -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"}
@@ -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]);
@@ -8024,7 +8029,8 @@ var Po = "<!DOCTYPE html>\n<html lang=\"en\" class=\"dark\">\n<head>\n <meta ch
8024
8029
  },
8025
8030
  concurrency: t.concurrency,
8026
8031
  steps: t.steps,
8027
- 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
8028
8034
  });
8029
8035
  }
8030
8036
  } catch (t) {
@@ -8032,7 +8038,7 @@ var Po = "<!DOCTYPE html>\n<html lang=\"en\" class=\"dark\">\n<head>\n <meta ch
8032
8038
  }
8033
8039
  return n;
8034
8040
  }
8035
- }, 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 refreshing = false;\n\n const upper = (value) => String(value || '').toUpperCase();\n const terminalStatuses = new Set(['success', 'failed', 'cancelled']);\n const refreshJobs = async () => {\n if (refreshing) return;\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 }\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 refreshTimer = setInterval(refreshJobs, 5000);\n });\n onDestroy(() => clearInterval(refreshTimer));\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";
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";
8036
8042
  //#endregion
8037
8043
  //#region src/dashboard.ts
8038
8044
  function zo(e) {
@@ -8052,20 +8058,77 @@ function Bo(e, t = !1) {
8052
8058
  }));
8053
8059
  }
8054
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
+ }
8114
+ }
8115
+ //#endregion
8055
8116
  //#region src/server.ts
8056
- var Vo = 50, Ho = 500, Uo = class e {
8117
+ var Uo = 50, Wo = 500, Go = class e {
8057
8118
  server;
8058
8119
  preprocessors = /* @__PURE__ */ new Map();
8059
8120
  workflows = [];
8060
8121
  queue;
8061
8122
  secrets;
8062
8123
  adminToken;
8124
+ events = new Vo();
8125
+ workflowsLoaded;
8063
8126
  static async withPort(t) {
8064
8127
  let { port: n, ...r } = t;
8065
8128
  return new e(r).listen(n);
8066
8129
  }
8067
8130
  constructor(e) {
8068
- 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) => {
8069
8132
  this.workflows = t, console.log(`✅ Loaded ${t.length} workflow(s) from ${e.config.workflows}`);
8070
8133
  }), this.registerPreprocessor(new Mo()), this.server = f.createServer((e, t) => this.handleRequest(e, t));
8071
8134
  }
@@ -8076,9 +8139,11 @@ var Vo = 50, Ho = 500, Uo = class e {
8076
8139
  let n = new p(e.url || "/", `${e.headers["x-forwarded-proto"] || "http"}://${e.headers["x-forwarded-host"] || e.headers.host}`);
8077
8140
  if (e.method === "GET" && (n.pathname === "/runs" || n.pathname === "/")) return this.renderDashboard(t);
8078
8141
  if (e.method === "GET" && n.pathname === "/api/jobs") {
8079
- 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 ? Vo : Number(i);
8080
- 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 > Ho ? (t.writeHead(400, { "Content-Type": "application/json; charset=utf-8" }), t.end(JSON.stringify({ error: `limit must be an integer from 1 to ${Ho}` }))) : this.renderDashboardJobs(t, s, a, o);
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);
8081
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);
8082
8147
  if (e.method === "GET" && n.pathname.startsWith("/runs/")) {
8083
8148
  let e = n.pathname.replace("/runs/", "");
8084
8149
  return this.renderRunDetails(e, t);
@@ -8103,7 +8168,7 @@ var Vo = 50, Ho = 500, Uo = class e {
8103
8168
  n.writeHead(401, { "Content-Type": "application/json" }), n.end(JSON.stringify({ error: "Invalid HMAC signature or authentication failed" }));
8104
8169
  return;
8105
8170
  }
8106
- 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" }));
8107
8172
  } catch (e) {
8108
8173
  console.error("❌ Webhook Ingress Error:", e), n.writeHead(500, { "Content-Type": "application/json" }), n.end(JSON.stringify({
8109
8174
  error: "Internal Ingress Error",
@@ -8169,17 +8234,22 @@ var Vo = 50, Ho = 500, Uo = class e {
8169
8234
  workflowId: n.id,
8170
8235
  env: n.env,
8171
8236
  steps: n.steps,
8172
- inputs: t
8237
+ inputs: t,
8238
+ tags: n.tags
8173
8239
  };
8174
- 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 || [] });
8175
8241
  }
8176
8242
  }
8177
8243
  async handleSecretReload(e, t) {
8178
8244
  if (e.headers.authorization !== `Bearer ${this.adminToken}`) return t.writeHead(403, { "Content-Type": "application/json" }), t.end(JSON.stringify({ error: "Unauthorized" }));
8179
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" }));
8180
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
+ }
8181
8251
  async renderDashboard(e) {
8182
- let t = await this.queue.listJobs(51), n = zo(t.slice(0, Vo)), r = this.secrets.redactText(Bo(n, t.length > Vo));
8252
+ let t = await this.queue.listJobs(51), n = zo(t.slice(0, Uo)), r = this.secrets.redactText(Bo(n, t.length > Uo));
8183
8253
  e.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }), e.end(r);
8184
8254
  }
8185
8255
  async renderDashboardJobs(e, t, n, r) {
@@ -8234,7 +8304,7 @@ var Vo = 50, Ho = 500, Uo = class e {
8234
8304
  async handleRestartJob(e, t) {
8235
8305
  let n = await this.queue.restartJob(e);
8236
8306
  if (n) {
8237
- 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 }));
8238
8308
  return;
8239
8309
  }
8240
8310
  t.writeHead(404, { "Content-Type": "application/json" }), t.end(JSON.stringify({ error: "Job not found" }));
@@ -8247,13 +8317,13 @@ var Vo = 50, Ho = 500, Uo = class e {
8247
8317
  });
8248
8318
  }
8249
8319
  async stop() {
8250
- return new Promise((e) => {
8320
+ return this.events.close(), new Promise((e) => {
8251
8321
  this.server.close(() => {
8252
8322
  console.log("🌐 Webhook Ingress Server stopped listening."), e();
8253
8323
  });
8254
8324
  });
8255
8325
  }
8256
- }, Wo = d(v), Go = class {
8326
+ }, Ko = d(v), qo = class {
8257
8327
  name = "systemd";
8258
8328
  async isSupported() {
8259
8329
  try {
@@ -8376,7 +8446,7 @@ var Vo = 50, Ho = 500, Uo = class e {
8376
8446
  cancel: async () => {
8377
8447
  d = !0;
8378
8448
  try {
8379
- t.image && await Wo(`docker stop -t 2 ${o}`).catch(() => {}), await Wo(`systemctl stop ${o}.service`).catch(() => {});
8449
+ t.image && await Ko(`docker stop -t 2 ${o}`).catch(() => {}), await Ko(`systemctl stop ${o}.service`).catch(() => {});
8380
8450
  } catch {}
8381
8451
  },
8382
8452
  logFilePath: i
@@ -8386,7 +8456,7 @@ var Vo = 50, Ho = 500, Uo = class e {
8386
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;
8387
8457
  return t.slice(n, r);
8388
8458
  }
8389
- }, Ko = class {
8459
+ }, Jo = class {
8390
8460
  name = "standard-process";
8391
8461
  async isSupported() {
8392
8462
  return !0;
@@ -8501,13 +8571,13 @@ var Vo = 50, Ho = 500, Uo = class e {
8501
8571
  };
8502
8572
  //#endregion
8503
8573
  //#region src/drivers/index.ts
8504
- async function qo() {
8505
- let e = new Go();
8506
- return await e.isSupported() ? (console.log("⚡ Selected Execution Driver: Systemd (cgroups enabled)"), e) : (console.log("📦 Selected Execution Driver: Standard Process (Fallback)"), new Ko());
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());
8507
8577
  }
8508
8578
  //#endregion
8509
8579
  //#region src/signals.ts
8510
- async function Jo(e) {
8580
+ async function Xo(e) {
8511
8581
  let t = !1;
8512
8582
  async function n(n) {
8513
8583
  if (!t) {
@@ -8515,7 +8585,7 @@ async function Jo(e) {
8515
8585
  console.error("⚠️ Graceful shutdown timed out after 10s. Forcing exit!"), process.exit(1);
8516
8586
  }, 1e4).unref();
8517
8587
  try {
8518
- Xo.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);
8519
8589
  } catch (e) {
8520
8590
  console.error("❌ Error during graceful shutdown:", e.message), process.exit(1);
8521
8591
  }
@@ -8525,34 +8595,99 @@ async function Jo(e) {
8525
8595
  }
8526
8596
  //#endregion
8527
8597
  //#region src/worker.ts
8528
- var Yo = !!process.env.DEBUG, Xo = { isStopping: !1 };
8529
- function Zo(e, t, n, r) {
8530
- Xo.isStopping = !1;
8531
- let i = Array.from({ length: e }, (e, i) => Qo(`worker-${i + 1}`, t, n, r));
8532
- return Jo(i), i;
8533
- }
8534
- async function Qo(e, t, n, r) {
8535
- let i = await qo();
8536
- for (console.log(`[${e}] 🚀 Worker started. Driver: ${i.name}`); !Xo.isStopping;) try {
8537
- let a = await t.claimNextJob();
8538
- if (!a) {
8539
- await new Promise((e) => setTimeout(e, 2e3));
8540
- 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}`);
8541
8654
  }
8542
- await $o({
8543
- workerId: e,
8544
- job: a,
8545
- queue: t,
8546
- secrets: n,
8547
- config: r,
8548
- 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();
8549
8664
  });
8550
- } catch (t) {
8551
- console.error(`[${e}] ⚠️ Worker execution loop error:`, t), await new Promise((e) => setTimeout(e, 5e3));
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)
8684
+ });
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);
8552
8688
  }
8553
- console.log(`[${e}] 🛑 Worker loop stopped cleanly.`);
8554
8689
  }
8555
- async function $o(e) {
8690
+ async function ls(e) {
8556
8691
  let { workerId: t, job: n, config: r, secrets: i, queue: o } = e;
8557
8692
  console.log(`\n[${t}] 📦 Claimed Job #${n.id} (Workflow: ${n.workflow_id})`);
8558
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 = {
@@ -8569,20 +8704,20 @@ async function $o(e) {
8569
8704
  durationMs: 0,
8570
8705
  outputs: {},
8571
8706
  logContent: ""
8572
- })), g = os(n, "running", u, l, m.env, h, s);
8707
+ })), g = gs(n, "running", u, l, m.env, h, s);
8573
8708
  await o.saveReport(n.id, g);
8574
- let { cancelled: _, failed: v } = await es({
8709
+ let { cancelled: _, failed: v } = await us({
8575
8710
  payload: s,
8576
8711
  steps: c,
8577
8712
  executionContext: m,
8578
8713
  ...e
8579
8714
  }, g), y = _ ? "cancelled" : v ? "failed" : "success";
8580
- 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 ss(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);
8581
8716
  }
8582
- async function es(e, t) {
8717
+ async function us(e, t) {
8583
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;
8584
8719
  try {
8585
- s.env && Object.assign(l.env, await rs(s.env, l));
8720
+ s.env && Object.assign(l.env, await ps(s.env, l));
8586
8721
  for (let e = 0; e < c.length; e++) {
8587
8722
  let s = c[e], m = (/* @__PURE__ */ new Date()).toISOString();
8588
8723
  u[e] = {
@@ -8596,7 +8731,7 @@ async function es(e, t) {
8596
8731
  startedAt: m,
8597
8732
  finishedAt: void 0
8598
8733
  }, t.durationMs = Date.now() - Date.parse(t.startedAt), await i.saveReport(o.id, t);
8599
- let h = await ts({
8734
+ let h = await ds({
8600
8735
  workerId: r,
8601
8736
  jobId: o.id,
8602
8737
  step: s,
@@ -8636,22 +8771,22 @@ async function es(e, t) {
8636
8771
  } catch (e) {
8637
8772
  console.log("🛑 Step failed", e), d = !0;
8638
8773
  }
8639
- return p < c.length && (as(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)), {
8640
8775
  failed: d,
8641
8776
  cancelled: f,
8642
8777
  stepReports: u
8643
8778
  };
8644
8779
  }
8645
- async function ts(e) {
8780
+ async function ds(e) {
8646
8781
  let { step: t, stepIndex: n, executionContext: r } = e;
8647
- if (t.id ||= `step-${n}`, t.name ||= t.id, t.if && !await ko.evaluateConditions(t.if, r)) return Yo && 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), {
8648
8783
  failed: !1,
8649
8784
  cancelled: !1,
8650
8785
  skipped: !0,
8651
8786
  report: null
8652
8787
  };
8653
8788
  try {
8654
- let n = await rs(t.env, r), i = {
8789
+ let n = await ps(t.env, r), i = {
8655
8790
  jobId: String(e.jobId),
8656
8791
  step: t,
8657
8792
  command: t.run,
@@ -8665,15 +8800,15 @@ async function ts(e) {
8665
8800
  WORKING_DIR: r.workingDir
8666
8801
  }
8667
8802
  };
8668
- return t.eval ? ns({
8803
+ return t.eval ? fs({
8669
8804
  ...e,
8670
8805
  stepContext: i
8671
- }) : is({
8806
+ }) : ms({
8672
8807
  ...e,
8673
8808
  stepContext: i
8674
8809
  });
8675
8810
  } catch (e) {
8676
- return Yo && console.error(`⏩ Failed to run step ${t.id}`, r, e), {
8811
+ return Zo && console.error(`⏩ Failed to run step ${t.id}`, r, e), {
8677
8812
  failed: !0,
8678
8813
  cancelled: !1,
8679
8814
  skipped: !1,
@@ -8681,7 +8816,7 @@ async function ts(e) {
8681
8816
  };
8682
8817
  }
8683
8818
  }
8684
- async function ns(e) {
8819
+ async function fs(e) {
8685
8820
  let { queue: t, stepContext: n, executionContext: r } = e, { jobId: i, step: a } = n, o = Date.now(), s = a.id, c = a.name;
8686
8821
  try {
8687
8822
  let e = await ko.evaluateExpression(a.eval, r);
@@ -8727,12 +8862,12 @@ async function ns(e) {
8727
8862
  };
8728
8863
  }
8729
8864
  }
8730
- async function rs(e, t) {
8865
+ async function ps(e, t) {
8731
8866
  let n = {};
8732
8867
  if (e) for (let [r, i] of Object.entries(e)) n[r] = String(await ko.evaluateValue(i, t));
8733
8868
  return n;
8734
8869
  }
8735
- async function is(t) {
8870
+ async function ms(t) {
8736
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;
8737
8872
  try {
8738
8873
  d = o.execute(i);
@@ -8747,10 +8882,11 @@ async function is(t) {
8747
8882
  logFilePath: ""
8748
8883
  };
8749
8884
  }
8885
+ $o.add(d);
8750
8886
  let f = !1, p = setInterval(async () => {
8751
8887
  await s.isCancelled(r) && (console.log(`[${n}] 🛑 Job #${r} was cancelled! Halting execution.`), f = !0, clearInterval(p), await d.cancel());
8752
8888
  }, 3e3), m = await d.done;
8753
- if (clearInterval(p), d.logFilePath && e.existsSync(d.logFilePath)) try {
8889
+ if (clearInterval(p), $o.delete(d), d.logFilePath && e.existsSync(d.logFilePath)) try {
8754
8890
  let e = await o.readLog(d.logFilePath);
8755
8891
  await s.saveStepLog(r, l, e);
8756
8892
  } catch (e) {
@@ -8777,7 +8913,7 @@ async function is(t) {
8777
8913
  }
8778
8914
  };
8779
8915
  }
8780
- function as(e, t, n) {
8916
+ function hs(e, t, n) {
8781
8917
  for (let r = t; r < e.length; r++) {
8782
8918
  let t = e[r], i = t.id || `step-${r}`;
8783
8919
  n[r] = {
@@ -8791,7 +8927,7 @@ function as(e, t, n) {
8791
8927
  };
8792
8928
  }
8793
8929
  }
8794
- function os(e, t, n, r, i, a, o) {
8930
+ function gs(e, t, n, r, i, a, o) {
8795
8931
  return {
8796
8932
  jobId: String(e.id),
8797
8933
  parentId: String(e.parentId || ""),
@@ -8810,7 +8946,7 @@ function os(e, t, n, r, i, a, o) {
8810
8946
  })
8811
8947
  };
8812
8948
  }
8813
- async function ss(e, t = [], n) {
8949
+ async function _s(e, t = [], n) {
8814
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) => {
8815
8951
  try {
8816
8952
  await t.report(n);
@@ -8821,7 +8957,7 @@ async function ss(e, t = [], n) {
8821
8957
  }
8822
8958
  //#endregion
8823
8959
  //#region src/reporters/json-file.reporter.ts
8824
- var cs = class {
8960
+ var vs = class {
8825
8961
  name = "json-file";
8826
8962
  outputDir;
8827
8963
  constructor(e) {
@@ -8832,7 +8968,7 @@ var cs = class {
8832
8968
  let n = a.join(this.outputDir, `run-${t.jobId}.json`);
8833
8969
  e.writeFileSync(n, JSON.stringify(t, null, 2), "utf-8"), console.log(`📊 Execution report saved to: ${n}`);
8834
8970
  }
8835
- }, ls = class {
8971
+ }, ys = class {
8836
8972
  name = "slack";
8837
8973
  webhookUrl = "";
8838
8974
  token = "";
@@ -8859,7 +8995,7 @@ var cs = class {
8859
8995
  };
8860
8996
  //#endregion
8861
8997
  //#region src/index.ts
8862
- function us(e) {
8998
+ function bs(e) {
8863
8999
  console.log("🔍 Validating Workflows in:", e.workflows);
8864
9000
  let t = new di(e.workflows), n = r(e.workflows).filter((e) => e.endsWith(".yml") || e.endsWith(".yaml"));
8865
9001
  for (let e of n) {
@@ -8867,13 +9003,13 @@ function us(e) {
8867
9003
  console.log(` ✅ ${e} -> Valid! (${n.length} job matrix variant(s) generated)`);
8868
9004
  }
8869
9005
  }
8870
- async function ds() {
9006
+ async function xs() {
8871
9007
  let { config: e, command: t } = await fe();
8872
- if (e || process.exit(1), t === "validate") return us(e);
9008
+ if (e || process.exit(1), t === "validate") return bs(e);
8873
9009
  let n = new mi("./.env"), r = new pi(process.env.WORKER_NAME || "cli");
8874
9010
  switch (t) {
8875
9011
  case "start-server":
8876
- console.log("🌐 Starting Ingress Gateway..."), await r.init(), await Uo.withPort({
9012
+ console.log("🌐 Starting Ingress Gateway..."), await r.init(), await Go.withPort({
8877
9013
  config: e,
8878
9014
  queue: r,
8879
9015
  secrets: n,
@@ -8882,11 +9018,11 @@ async function ds() {
8882
9018
  });
8883
9019
  break;
8884
9020
  case "start-workers":
8885
- console.log(`⚙️ Starting ${e.workers} Worker Loop(s)...`), await r.init(), Zo(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);
8886
9022
  break;
8887
9023
  default: console.error(`❌ Unknown command: '${t}'`), de(), process.exit(1);
8888
9024
  }
8889
9025
  }
8890
- ds().catch(console.error);
9026
+ xs().catch(console.error);
8891
9027
  //#endregion
8892
- export { Io as HtmlReporter, cs as JsonFileReporter, ls 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
  */
@@ -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,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"}
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;AAOpB,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;IA0D3B;;OAEG;YACW,aAAa;YAyBb,WAAW;IAwBzB,OAAO,CAAC,UAAU;YAiBJ,cAAc;IAyC5B;;OAEG;YACW,kBAAkB;IAehC;;OAEG;YACW,eAAe;YAQf,mBAAmB;IAWjC;;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.1",
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",