@cloud-cli/on 1.57.0 → 1.59.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/dist/api-key-repository.d.ts +2 -2
- package/dist/api-key-repository.d.ts.map +1 -1
- package/dist/on.js +220 -102
- package/dist/queue.d.ts +8 -0
- package/dist/queue.d.ts.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/settings-ui.d.ts +1 -1
- package/dist/settings-ui.d.ts.map +1 -1
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/worker.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const API_KEY_SCOPES: readonly ["workflows:read", "workflows:write", "logs:read", "artifacts:read", "runs:control", "runs:dispatch"];
|
|
1
|
+
export declare const API_KEY_SCOPES: readonly ["workflows:read", "workflows:write", "logs:read", "artifacts:read", "runs:control", "runs:dispatch", "workers:read"];
|
|
2
2
|
export type ApiKeyScope = (typeof API_KEY_SCOPES)[number];
|
|
3
3
|
export declare class ApiKeyRepository {
|
|
4
4
|
init(): Promise<void>;
|
|
@@ -6,7 +6,7 @@ export declare class ApiKeyRepository {
|
|
|
6
6
|
issue(name: string, scopes: string[]): Promise<{
|
|
7
7
|
id: `${string}-${string}-${string}-${string}-${string}`;
|
|
8
8
|
name: string;
|
|
9
|
-
scopes: ("workflows:read" | "workflows:write" | "logs:read" | "artifacts:read" | "runs:control" | "runs:dispatch")[];
|
|
9
|
+
scopes: ("workflows:read" | "workflows:write" | "logs:read" | "artifacts:read" | "runs:control" | "runs:dispatch" | "workers:read")[];
|
|
10
10
|
token: string;
|
|
11
11
|
}>;
|
|
12
12
|
scopesForToken(token: string): Promise<string[] | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-key-repository.d.ts","sourceRoot":"","sources":["../src/api-key-repository.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"api-key-repository.d.ts","sourceRoot":"","sources":["../src/api-key-repository.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,cAAc,gIAAiI,CAAC;AAC7J,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D,qBAAa,gBAAgB;IACrB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAYrB,IAAI;IAKJ,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE;;;;;;IAUpC,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC;IAQvD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAI3C"}
|
package/dist/on.js
CHANGED
|
@@ -280,7 +280,7 @@ var ye = class extends l {
|
|
|
280
280
|
return await C.run("UPDATE jobs SET status = 'pending', worker_id = NULL WHERE status = 'running' AND started_at < datetime('now', '-1 hour');");
|
|
281
281
|
}
|
|
282
282
|
async createTables() {
|
|
283
|
-
await C.exec("\n CREATE TABLE IF NOT EXISTS jobs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n parentId INTEGER,\n workflow_id TEXT NOT NULL,\n workflow_revision INTEGER NOT NULL,\n required_tags TEXT NOT NULL DEFAULT '[]',\n payload TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'pending',\n concurrency_key TEXT,\n worker_id TEXT,\n report TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n started_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n finished_at DATETIME DEFAULT CURRENT_TIMESTAMP\n );\n\n CREATE TABLE IF NOT EXISTS step_logs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n job_id INTEGER NOT NULL,\n step_id TEXT NOT NULL,\n log_content TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY(job_id) REFERENCES jobs(id) ON DELETE CASCADE\n );\n\n CREATE INDEX IF NOT EXISTS idx_step_logs_job ON step_logs(job_id);\n\n DELETE FROM step_logs\n WHERE id NOT IN (\n SELECT MAX(id) FROM step_logs GROUP BY job_id, step_id\n );\n CREATE UNIQUE INDEX IF NOT EXISTS idx_step_logs_job_step ON step_logs(job_id, step_id);\n\n CREATE TABLE IF NOT EXISTS stored_files (\n kind TEXT NOT NULL,\n owner_key TEXT NOT NULL,\n file_path TEXT NOT NULL,\n content TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (kind, owner_key, file_path)\n );\n CREATE INDEX IF NOT EXISTS idx_stored_files_owner ON stored_files(kind, owner_key);\n ");
|
|
283
|
+
await C.exec("\n CREATE TABLE IF NOT EXISTS jobs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n parentId INTEGER,\n workflow_id TEXT NOT NULL,\n workflow_revision INTEGER NOT NULL,\n required_tags TEXT NOT NULL DEFAULT '[]',\n payload TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'pending',\n concurrency_key TEXT,\n worker_id TEXT,\n report TEXT,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n started_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n finished_at DATETIME DEFAULT CURRENT_TIMESTAMP\n );\n\n CREATE TABLE IF NOT EXISTS step_logs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n job_id INTEGER NOT NULL,\n step_id TEXT NOT NULL,\n log_content TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY(job_id) REFERENCES jobs(id) ON DELETE CASCADE\n );\n\n CREATE INDEX IF NOT EXISTS idx_step_logs_job ON step_logs(job_id);\n\n DELETE FROM step_logs\n WHERE id NOT IN (\n SELECT MAX(id) FROM step_logs GROUP BY job_id, step_id\n );\n CREATE UNIQUE INDEX IF NOT EXISTS idx_step_logs_job_step ON step_logs(job_id, step_id);\n\n CREATE TABLE IF NOT EXISTS stored_files (\n kind TEXT NOT NULL,\n owner_key TEXT NOT NULL,\n file_path TEXT NOT NULL,\n content TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (kind, owner_key, file_path)\n );\n CREATE INDEX IF NOT EXISTS idx_stored_files_owner ON stored_files(kind, owner_key);\n\n CREATE TABLE IF NOT EXISTS worker_presence (\n worker_id TEXT PRIMARY KEY,\n version TEXT NOT NULL,\n tags TEXT NOT NULL DEFAULT '[]',\n concurrency INTEGER NOT NULL DEFAULT 0,\n active_jobs INTEGER NOT NULL DEFAULT 0,\n last_seen DATETIME DEFAULT CURRENT_TIMESTAMP\n );\n ");
|
|
284
284
|
}
|
|
285
285
|
async getJob(e) {
|
|
286
286
|
return C.get("SELECT * FROM jobs WHERE id = ?", [e]);
|
|
@@ -326,6 +326,26 @@ var ye = class extends l {
|
|
|
326
326
|
async getStoredFiles(e, t) {
|
|
327
327
|
return this.fileStorage.enabled ? this.fileStorage.load(`${e}/${t}`) : C.all("SELECT file_path AS path, content FROM stored_files WHERE kind = ? AND owner_key = ?", [e, t]);
|
|
328
328
|
}
|
|
329
|
+
async updateWorkerPresence(e) {
|
|
330
|
+
await C.run("INSERT INTO worker_presence (worker_id, version, tags, concurrency, active_jobs, last_seen)\n VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP)\n ON CONFLICT(worker_id) DO UPDATE SET version = excluded.version, tags = excluded.tags,\n concurrency = excluded.concurrency, active_jobs = excluded.active_jobs, last_seen = CURRENT_TIMESTAMP", [
|
|
331
|
+
e.id,
|
|
332
|
+
e.version,
|
|
333
|
+
JSON.stringify(e.tags),
|
|
334
|
+
e.concurrency,
|
|
335
|
+
e.activeJobs
|
|
336
|
+
]);
|
|
337
|
+
}
|
|
338
|
+
async listWorkerPresence() {
|
|
339
|
+
return (await C.all("SELECT worker_id, version, tags, concurrency, active_jobs, last_seen FROM worker_presence ORDER BY worker_id")).map((e) => ({
|
|
340
|
+
workerId: e.worker_id,
|
|
341
|
+
version: e.version,
|
|
342
|
+
tags: JSON.parse(e.tags || "[]"),
|
|
343
|
+
concurrency: e.concurrency,
|
|
344
|
+
activeJobs: e.active_jobs,
|
|
345
|
+
lastSeen: e.last_seen,
|
|
346
|
+
online: Date.now() - Date.parse(`${e.last_seen}Z`) < 45e3
|
|
347
|
+
}));
|
|
348
|
+
}
|
|
329
349
|
async saveAiRequest(e, t, n) {
|
|
330
350
|
await C.run("INSERT INTO ai_requests (job_id, workflow_url, request_json) VALUES (?, ?, ?)", [
|
|
331
351
|
e,
|
|
@@ -6340,7 +6360,8 @@ var oa = {
|
|
|
6340
6360
|
"logs:read",
|
|
6341
6361
|
"artifacts:read",
|
|
6342
6362
|
"runs:control",
|
|
6343
|
-
"runs:dispatch"
|
|
6363
|
+
"runs:dispatch",
|
|
6364
|
+
"workers:read"
|
|
6344
6365
|
], ca = class {
|
|
6345
6366
|
async init() {
|
|
6346
6367
|
await C.exec("CREATE TABLE IF NOT EXISTS api_keys (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n key_hash TEXT NOT NULL UNIQUE,\n scopes TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n last_used_at DATETIME,\n revoked_at DATETIME\n )");
|
|
@@ -6375,7 +6396,7 @@ var oa = {
|
|
|
6375
6396
|
let t = await C.run("UPDATE api_keys SET revoked_at = CURRENT_TIMESTAMP WHERE id = ? AND revoked_at IS NULL", [e]);
|
|
6376
6397
|
return Number(t?.changes || 0) > 0;
|
|
6377
6398
|
}
|
|
6378
|
-
}, la = "<!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\" />\n <title>Runner settings</title>\n <script type=\"importmap\">\n { \"imports\": { \"@li3/\": \"https://cdn.li3.dev/@li3/\" } }\n <\/script>\n <link rel=\"component\" href=\"https://sodium.static.apphor.de/lucide-icon.html\" />\n <link rel=\"component\" href=\"/app-header.html\" />\n </head>\n <body data-page=\"__SETTINGS_PAGE__\" class=\"min-h-screen bg-gray-950 p-4 font-sans text-gray-100 md:p-6\">\n <template app>\n <main class=\"mx-auto max-w-6xl space-y-5 py-6\">\n <app-header title=\"Settings\" subtitle=\"Human-admin controls and automation credentials\" back=\"/runs\" back-label=\"Back to jobs\"></app-header>\n <nav class=\"flex flex-wrap gap-1 rounded-xl border border-gray-800 bg-gray-900/70 p-1\" aria-label=\"Settings navigation\">\n <a href=\"/settings/workflows\" class=\"rounded-lg px-3 py-2 text-sm text-gray-300 hover:bg-gray-800\">Workflows</a>\n <a href=\"/settings/secrets\" class=\"rounded-lg px-3 py-2 text-sm text-gray-300 hover:bg-gray-800\">Secrets</a>\n <a href=\"/settings/tokens\" bind-class=\"page === 'tokens' ? 'bg-gray-800.text-white' : ''\" class=\"rounded-lg px-3 py-2 text-sm text-gray-300 hover:bg-gray-800\">API tokens</a>\n <a href=\"/settings/notifications\" bind-class=\"page === 'notifications' ? 'bg-gray-800.text-white' : ''\" class=\"rounded-lg px-3 py-2 text-sm text-gray-300 hover:bg-gray-800\">Notifications</a>\n </nav>\n\n <template if=\"page === 'tokens'\">\n </template>\n <template if=\"page === 'notifications'\">\n <section class=\"rounded-2xl border border-gray-800 bg-gray-900/80 p-5\" aria-labelledby=\"notifications-title\">\n <h2 id=\"notifications-title\" class=\"text-lg font-semibold text-white\">Notifications</h2>\n <p class=\"mt-1 text-sm text-gray-400\">Receive job completion notifications on this device.</p>\n <button type=\"button\" on-click=\"toggleNotifications()\" class=\"mt-5 inline-flex items-center gap-2 rounded-lg border border-indigo-500/60 px-4 py-2.5 text-sm font-medium text-indigo-200 outline-none hover:bg-indigo-500/10 focus-visible:ring-2 focus-visible:ring-indigo-400/60\">\n <lucide-icon bind-icon=\"notificationsEnabled ? 'bell-off' : 'bell-ring'\" size=\"16\"></lucide-icon>\n {{ notificationsEnabled ? 'Disable notifications' : 'Enable notifications' }}\n </button>\n <p class=\"mt-3 text-sm text-gray-400\" role=\"status\">{{ notificationMessage }}</p>\n </section>\n </template>\n\n <template if=\"page === 'tokens'\">\n <section class=\"rounded-2xl border border-gray-800 bg-gray-900/80 p-5\" aria-labelledby=\"keys-title\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <h2 id=\"keys-title\" class=\"text-lg font-semibold text-white\">API keys</h2>\n <p class=\"mt-1 text-sm text-gray-400\">Issue narrowly scoped credentials for automation tools. Keys are shown only once.</p>\n </div>\n </div>\n <div class=\"mt-5 grid gap-3 rounded-xl border border-gray-800 bg-gray-950/50 p-4 md:grid-cols-[1fr_auto]\">\n <label class=\"sr-only\" for=\"key-name\">Key name</label>\n <input id=\"key-name\" bind-value=\"keyName\" on-input=\"setKeyName($event)\" placeholder=\"Documentation bot\" class=\"rounded-lg border border-gray-700 bg-gray-900 px-3 py-2.5 text-sm outline-none focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/30\" />\n <button type=\"button\" on-click=\"issueKey()\" class=\"inline-flex items-center justify-center gap-2 rounded-lg bg-indigo-500 px-4 py-2.5 text-sm font-semibold text-white outline-none hover:bg-indigo-400 focus-visible:ring-2 focus-visible:ring-indigo-400/60\"><lucide-icon icon=\"key-round\" size=\"16\"></lucide-icon>Issue key</button>\n <fieldset class=\"md:col-span-2\">\n <legend class=\"mb-2 text-xs font-semibold uppercase tracking-wider text-gray-500\">Scopes</legend>\n <div class=\"space-y-2\">\n <template for=\"scope of scopeOptions\">\n <label class=\"group flex cursor-pointer items-center gap-3 text-sm text-gray-300\">\n <input type=\"checkbox\" bind-checked=\"scope.selected\" on-change=\"toggleScope(scope)\" class=\"peer sr-only\" />\n <span aria-hidden=\"true\" class=\"flex h-5 w-5 shrink-0 items-center justify-center rounded-md border border-gray-600 bg-gray-900 text-transparent transition peer-checked:border-indigo-400 peer-checked:bg-indigo-500 peer-checked:text-white peer-focus-visible:ring-2 peer-focus-visible:ring-indigo-400/70 peer-disabled:opacity-50\">\n <svg viewBox=\"0 0 16 16\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m3 8 3 3 7-7\"></path></svg>\n </span>\n <span class=\"group-hover:text-white\">{{ scope.label }}</span>\n </label>\n </template>\n </div>\n </fieldset>\n </div>\n <template if=\"newToken\">\n <div class=\"mt-4 rounded-xl border border-amber-500/40 bg-amber-500/10 p-4\" role=\"alert\">\n <p class=\"text-sm font-semibold text-amber-200\">Copy this key now. It cannot be shown again.</p>\n <code class=\"mt-2 block select-all break-all rounded bg-gray-950 p-3 font-mono text-xs text-amber-100\">{{ newToken }}</code>\n </div>\n </template>\n <h3 class=\"mt-5 text-xs font-semibold uppercase tracking-wider text-gray-500\">Active tokens</h3>\n <ul class=\"mt-2 space-y-2\" aria-label=\"Active API keys\">\n <template for=\"key of keys\">\n <li class=\"flex items-center justify-between gap-3 rounded-xl border border-gray-800 bg-gray-950/60 px-4 py-3\">\n <div class=\"min-w-0\"><p class=\"truncate text-sm font-medium text-gray-200\">{{ key.name }}</p><p class=\"text-xs text-gray-500\">{{ key.scopes.join(', ') }} · created {{ key.created_at }}</p></div>\n <button type=\"button\" on-click=\"revokeKey(key)\" class=\"shrink-0 rounded-lg p-2 text-rose-300 outline-none hover:bg-rose-500/10 focus-visible:ring-2 focus-visible:ring-rose-400/60\" aria-label=\"Revoke {{ key.name }}\" title=\"Revoke key\"><lucide-icon icon=\"trash\" size=\"16\"></lucide-icon></button>\n </li>\n </template>\n </ul>\n </section>\n </template>\n <p class=\"text-sm text-rose-300\" role=\"alert\" class-hidden=\"!error\">{{ error }}</p>\n </main>\n <script setup>\n import { onInit, ref } from '@li3/web';\n export default function () {\n const page = document.body.dataset.page;\n const keys = ref([]);\n const keyName = ref('');\n const newToken = ref('');\n const error = ref('');\n const notificationsSupported = ref('Notification' in window && 'serviceWorker' in navigator);\n const notificationsEnabled = ref(notificationsSupported.value && Notification.permission === 'granted' && localStorage.getItem('runner-notifications') === 'enabled');\n const notificationMessage = ref('');\n const scopeOptions = ref([\n { name: 'workflows:read', label: 'Read workflows', selected: true },\n { name: 'workflows:write', label: 'Modify workflows', selected: false },\n { name: 'logs:read', label: 'Read logs', selected: false },\n { name: 'artifacts:read', label: 'Read artifacts', selected: false },\n { name: 'runs:control', label: 'Control runs', selected: false },\n { name: 'runs:dispatch', label: 'Dispatch runs', selected: false },\n ]);\n const api = async (url, options = {}) => { const response = await fetch(url, { headers: { accept: 'application/json', ...(options.body ? { 'content-type': 'application/json' } : {}) }, ...options }); const body = await response.json().catch(() => ({})); if (!response.ok) throw new Error(body.error || `Request failed: ${response.status}`); return body; };\n const load = async () => { keys.value = (await api('/api/api-keys')).keys; };\n const issueKey = async () => { try { error.value = ''; const result = await api('/api/api-keys', { method: 'POST', body: JSON.stringify({ name: keyName.value, scopes: scopeOptions.value.filter((scope) => scope.selected).map((scope) => scope.name) }) }); newToken.value = result.token; keyName.value = ''; await load(); } catch (reason) { error.value = reason.message; } };\n const revokeKey = async (key) => { if (!confirm(`Revoke ${key.name}?`)) return; try { await api(`/api/api-keys/${key.id}`, { method: 'DELETE' }); await load(); } catch (reason) { error.value = reason.message; } };\n const setKeyName = (event) => { keyName.value = event.target.value; };\n const toggleScope = (scope) => { scope.selected = !scope.selected; };\n const toggleNotifications = async () => {\n if (notificationsEnabled.value) {\n notificationsEnabled.value = false;\n localStorage.removeItem('runner-notifications');\n notificationMessage.value = 'Notifications disabled on this device.';\n return;\n }\n if (!notificationsSupported.value) { notificationMessage.value = 'Notifications are not supported by this browser.'; return; }\n const permission = await Notification.requestPermission();\n notificationsEnabled.value = permission === 'granted';\n if (notificationsEnabled.value) { localStorage.setItem('runner-notifications', 'enabled'); notificationMessage.value = 'Notifications enabled.'; }\n else notificationMessage.value = 'Notification permission was not granted.';\n };\n onInit(() => { if (page === 'tokens') load().catch((reason) => { error.value = reason.message; }); });\n return { page, keys, keyName, newToken, error, scopeOptions, issueKey, revokeKey, setKeyName, toggleScope, notificationsEnabled, notificationMessage, toggleNotifications };\n }\n <\/script>\n </template>\n <script type=\"module\">import '@li3/web';<\/script>\n <script src=\"https://cdn.tailwindcss.com\"><\/script>\n </body>\n</html>\n";
|
|
6399
|
+
}, la = "<!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\" />\n <title>Runner settings</title>\n <script type=\"importmap\">\n { \"imports\": { \"@li3/\": \"https://cdn.li3.dev/@li3/\" } }\n <\/script>\n <link rel=\"component\" href=\"https://sodium.static.apphor.de/lucide-icon.html\" />\n <link rel=\"component\" href=\"/app-header.html\" />\n </head>\n <body data-page=\"__SETTINGS_PAGE__\" class=\"min-h-screen bg-gray-950 p-4 font-sans text-gray-100 md:p-6\">\n <template app>\n <main class=\"mx-auto max-w-6xl space-y-5 py-6\">\n <app-header title=\"Settings\" subtitle=\"Human-admin controls and automation credentials\" back=\"/runs\" back-label=\"Back to jobs\"></app-header>\n <nav class=\"flex flex-wrap gap-1 rounded-xl border border-gray-800 bg-gray-900/70 p-1\" aria-label=\"Settings navigation\">\n <a href=\"/settings/workflows\" class=\"rounded-lg px-3 py-2 text-sm text-gray-300 hover:bg-gray-800\">Workflows</a>\n <a href=\"/settings/secrets\" class=\"rounded-lg px-3 py-2 text-sm text-gray-300 hover:bg-gray-800\">Secrets</a>\n <a href=\"/settings/tokens\" bind-class=\"page === 'tokens' ? 'bg-gray-800.text-white' : ''\" class=\"rounded-lg px-3 py-2 text-sm text-gray-300 hover:bg-gray-800\">API tokens</a>\n <a href=\"/settings/notifications\" bind-class=\"page === 'notifications' ? 'bg-gray-800.text-white' : ''\" class=\"rounded-lg px-3 py-2 text-sm text-gray-300 hover:bg-gray-800\">Notifications</a>\n <a href=\"/settings/workers\" bind-class=\"page === 'workers' ? 'bg-gray-800.text-white' : ''\" class=\"rounded-lg px-3 py-2 text-sm text-gray-300 hover:bg-gray-800\">Workers</a>\n </nav>\n\n <template if=\"page === 'tokens'\">\n </template>\n <template if=\"page === 'notifications'\">\n <section class=\"rounded-2xl border border-gray-800 bg-gray-900/80 p-5\" aria-labelledby=\"notifications-title\">\n <h2 id=\"notifications-title\" class=\"text-lg font-semibold text-white\">Notifications</h2>\n <p class=\"mt-1 text-sm text-gray-400\">Receive job completion notifications on this device.</p>\n <button type=\"button\" on-click=\"toggleNotifications()\" class=\"mt-5 inline-flex items-center gap-2 rounded-lg border border-indigo-500/60 px-4 py-2.5 text-sm font-medium text-indigo-200 outline-none hover:bg-indigo-500/10 focus-visible:ring-2 focus-visible:ring-indigo-400/60\">\n <lucide-icon bind-icon=\"notificationsEnabled ? 'bell-off' : 'bell-ring'\" size=\"16\"></lucide-icon>\n {{ notificationsEnabled ? 'Disable notifications' : 'Enable notifications' }}\n </button>\n <p class=\"mt-3 text-sm text-gray-400\" role=\"status\">{{ notificationMessage }}</p>\n </section>\n </template>\n\n <template if=\"page === 'workers'\">\n <section class=\"rounded-2xl border border-gray-800 bg-gray-900/80 p-5\" aria-labelledby=\"workers-title\">\n <h2 id=\"workers-title\" class=\"text-lg font-semibold text-white\">Workers</h2>\n <p class=\"mt-1 text-sm text-gray-400\">Connected runner status, versions, tags, and load.</p>\n <div class=\"mt-5 overflow-hidden rounded-xl border border-gray-800\"><table class=\"w-full text-left text-sm\"><thead class=\"bg-gray-950 text-xs uppercase text-gray-500\"><tr><th class=\"px-4 py-3\">Worker</th><th class=\"px-4 py-3\">Version</th><th class=\"px-4 py-3\">Tags</th><th class=\"px-4 py-3\">Load</th><th class=\"px-4 py-3\">Last seen</th></tr></thead><tbody><template for=\"worker of workers\"><tr class=\"border-t border-gray-800\"><td class=\"px-4 py-3 font-mono text-gray-200\">{{ worker.workerId }} <span class=\"ml-2 rounded-full px-2 py-0.5 text-xs\" bind-class=\"worker.online ? 'bg-emerald-500/10.text-emerald-300' : 'bg-gray-800.text-gray-500'\">{{ worker.online ? 'online' : 'offline' }}</span></td><td class=\"px-4 py-3 font-mono text-gray-400\">{{ worker.version }}</td><td class=\"px-4 py-3 text-gray-400\">{{ worker.tags.join(', ') }}</td><td class=\"px-4 py-3 text-gray-400\">{{ worker.activeJobs }} / {{ worker.concurrency }}</td><td class=\"px-4 py-3 text-gray-500\">{{ worker.lastSeen }}</td></tr></template></tbody></table><p class=\"p-5 text-sm text-gray-500\" class-hidden=\"workers.length > 0\">No worker heartbeats received yet.</p></div>\n </section>\n </template>\n\n <template if=\"page === 'tokens'\">\n <section class=\"rounded-2xl border border-gray-800 bg-gray-900/80 p-5\" aria-labelledby=\"keys-title\">\n <div class=\"flex items-start justify-between gap-3\">\n <div>\n <h2 id=\"keys-title\" class=\"text-lg font-semibold text-white\">API keys</h2>\n <p class=\"mt-1 text-sm text-gray-400\">Issue narrowly scoped credentials for automation tools. Keys are shown only once.</p>\n </div>\n </div>\n <div class=\"mt-5 grid gap-3 rounded-xl border border-gray-800 bg-gray-950/50 p-4 md:grid-cols-[1fr_auto]\">\n <label class=\"sr-only\" for=\"key-name\">Key name</label>\n <input id=\"key-name\" bind-value=\"keyName\" on-input=\"setKeyName($event)\" placeholder=\"Documentation bot\" class=\"rounded-lg border border-gray-700 bg-gray-900 px-3 py-2.5 text-sm outline-none focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/30\" />\n <button type=\"button\" on-click=\"issueKey()\" class=\"inline-flex items-center justify-center gap-2 rounded-lg bg-indigo-500 px-4 py-2.5 text-sm font-semibold text-white outline-none hover:bg-indigo-400 focus-visible:ring-2 focus-visible:ring-indigo-400/60\"><lucide-icon icon=\"key-round\" size=\"16\"></lucide-icon>Issue key</button>\n <fieldset class=\"md:col-span-2\">\n <legend class=\"mb-2 text-xs font-semibold uppercase tracking-wider text-gray-500\">Scopes</legend>\n <div class=\"space-y-2\">\n <template for=\"scope of scopeOptions\">\n <label class=\"group flex cursor-pointer items-center gap-3 text-sm text-gray-300\">\n <input type=\"checkbox\" bind-checked=\"scope.selected\" on-change=\"toggleScope(scope)\" class=\"peer sr-only\" />\n <span aria-hidden=\"true\" class=\"flex h-5 w-5 shrink-0 items-center justify-center rounded-md border border-gray-600 bg-gray-900 text-transparent transition peer-checked:border-indigo-400 peer-checked:bg-indigo-500 peer-checked:text-white peer-focus-visible:ring-2 peer-focus-visible:ring-indigo-400/70 peer-disabled:opacity-50\">\n <svg viewBox=\"0 0 16 16\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m3 8 3 3 7-7\"></path></svg>\n </span>\n <span class=\"group-hover:text-white\">{{ scope.label }}</span>\n </label>\n </template>\n </div>\n </fieldset>\n </div>\n <template if=\"newToken\">\n <div class=\"mt-4 rounded-xl border border-amber-500/40 bg-amber-500/10 p-4\" role=\"alert\">\n <p class=\"text-sm font-semibold text-amber-200\">Copy this key now. It cannot be shown again.</p>\n <code class=\"mt-2 block select-all break-all rounded bg-gray-950 p-3 font-mono text-xs text-amber-100\">{{ newToken }}</code>\n </div>\n </template>\n <h3 class=\"mt-5 text-xs font-semibold uppercase tracking-wider text-gray-500\">Active tokens</h3>\n <ul class=\"mt-2 space-y-2\" aria-label=\"Active API keys\">\n <template for=\"key of keys\">\n <li class=\"flex items-center justify-between gap-3 rounded-xl border border-gray-800 bg-gray-950/60 px-4 py-3\">\n <div class=\"min-w-0\"><p class=\"truncate text-sm font-medium text-gray-200\">{{ key.name }}</p><p class=\"text-xs text-gray-500\">{{ key.scopes.join(', ') }} · created {{ key.created_at }}</p></div>\n <button type=\"button\" on-click=\"revokeKey(key)\" class=\"shrink-0 rounded-lg p-2 text-rose-300 outline-none hover:bg-rose-500/10 focus-visible:ring-2 focus-visible:ring-rose-400/60\" aria-label=\"Revoke {{ key.name }}\" title=\"Revoke key\"><lucide-icon icon=\"trash\" size=\"16\"></lucide-icon></button>\n </li>\n </template>\n </ul>\n </section>\n </template>\n <p class=\"text-sm text-rose-300\" role=\"alert\" class-hidden=\"!error\">{{ error }}</p>\n </main>\n <script setup>\n import { onInit, ref } from '@li3/web';\n export default function () {\n const page = document.body.dataset.page;\n const keys = ref([]);\n const workers = ref([]);\n const keyName = ref('');\n const newToken = ref('');\n const error = ref('');\n const notificationsSupported = ref('Notification' in window && 'serviceWorker' in navigator);\n const notificationsEnabled = ref(notificationsSupported.value && Notification.permission === 'granted' && localStorage.getItem('runner-notifications') === 'enabled');\n const notificationMessage = ref('');\n const scopeOptions = ref([\n { name: 'workflows:read', label: 'Read workflows', selected: true },\n { name: 'workflows:write', label: 'Modify workflows', selected: false },\n { name: 'logs:read', label: 'Read logs', selected: false },\n { name: 'artifacts:read', label: 'Read artifacts', selected: false },\n { name: 'runs:control', label: 'Control runs', selected: false },\n { name: 'runs:dispatch', label: 'Dispatch runs', selected: false },\n ]);\n const api = async (url, options = {}) => { const response = await fetch(url, { headers: { accept: 'application/json', ...(options.body ? { 'content-type': 'application/json' } : {}) }, ...options }); const body = await response.json().catch(() => ({})); if (!response.ok) throw new Error(body.error || `Request failed: ${response.status}`); return body; };\n const load = async () => { keys.value = (await api('/api/api-keys')).keys; };\n const loadWorkers = async () => { workers.value = (await api('/api/workers')).workers; };\n const issueKey = async () => { try { error.value = ''; const result = await api('/api/api-keys', { method: 'POST', body: JSON.stringify({ name: keyName.value, scopes: scopeOptions.value.filter((scope) => scope.selected).map((scope) => scope.name) }) }); newToken.value = result.token; keyName.value = ''; await load(); } catch (reason) { error.value = reason.message; } };\n const revokeKey = async (key) => { if (!confirm(`Revoke ${key.name}?`)) return; try { await api(`/api/api-keys/${key.id}`, { method: 'DELETE' }); await load(); } catch (reason) { error.value = reason.message; } };\n const setKeyName = (event) => { keyName.value = event.target.value; };\n const toggleScope = (scope) => { scope.selected = !scope.selected; };\n const toggleNotifications = async () => {\n if (notificationsEnabled.value) {\n notificationsEnabled.value = false;\n localStorage.removeItem('runner-notifications');\n notificationMessage.value = 'Notifications disabled on this device.';\n return;\n }\n if (!notificationsSupported.value) { notificationMessage.value = 'Notifications are not supported by this browser.'; return; }\n const permission = await Notification.requestPermission();\n notificationsEnabled.value = permission === 'granted';\n if (notificationsEnabled.value) { localStorage.setItem('runner-notifications', 'enabled'); notificationMessage.value = 'Notifications enabled.'; }\n else notificationMessage.value = 'Notification permission was not granted.';\n };\n onInit(() => { if (page === 'tokens') load().catch((reason) => { error.value = reason.message; }); if (page === 'workers') loadWorkers().catch((reason) => { error.value = reason.message; }); });\n return { page, keys, workers, keyName, newToken, error, scopeOptions, issueKey, revokeKey, setKeyName, toggleScope, notificationsEnabled, notificationMessage, toggleNotifications };\n }\n <\/script>\n </template>\n <script type=\"module\">import '@li3/web';<\/script>\n <script src=\"https://cdn.tailwindcss.com\"><\/script>\n </body>\n</html>\n";
|
|
6379
6400
|
//#endregion
|
|
6380
6401
|
//#region src/settings-ui.ts
|
|
6381
6402
|
function ua(e = "tokens") {
|
|
@@ -20852,6 +20873,26 @@ var SC = class {
|
|
|
20852
20873
|
"Cache-Control": "no-store",
|
|
20853
20874
|
"Content-Type": "application/json; charset=utf-8"
|
|
20854
20875
|
}), t.end(JSON.stringify(oa));
|
|
20876
|
+
if (e.method === "POST" && n.pathname === "/api/workers/heartbeat") {
|
|
20877
|
+
if (!this.isWorker(e)) {
|
|
20878
|
+
t.writeHead(403, { "Content-Type": "application/json" }).end(JSON.stringify({ error: "Unauthorized" }));
|
|
20879
|
+
return;
|
|
20880
|
+
}
|
|
20881
|
+
let n = await this.readJson(e, t);
|
|
20882
|
+
if (!n || n.workerId !== e.headers["x-runner-worker-id"]) return;
|
|
20883
|
+
await this.queue.updateWorkerPresence({
|
|
20884
|
+
id: n.workerId,
|
|
20885
|
+
version: String(n.version || "unknown"),
|
|
20886
|
+
tags: Array.isArray(n.tags) ? n.tags : [],
|
|
20887
|
+
concurrency: Number(n.concurrency || 0),
|
|
20888
|
+
activeJobs: Number(n.activeJobs || 0)
|
|
20889
|
+
}), t.writeHead(204).end();
|
|
20890
|
+
return;
|
|
20891
|
+
}
|
|
20892
|
+
if (e.method === "GET" && n.pathname === "/api/workers") return await this.hasScope(e, "workers:read") ? (t.writeHead(200, {
|
|
20893
|
+
"Cache-Control": "no-store",
|
|
20894
|
+
"Content-Type": "application/json; charset=utf-8"
|
|
20895
|
+
}), t.end(JSON.stringify({ workers: await this.queue.listWorkerPresence() }))) : this.requireScope(e, t, "workers:read");
|
|
20855
20896
|
if (e.method === "GET" && n.pathname === "/pages/dashboard.html") return this.renderPageComponent(t, "page-dashboard", Ce);
|
|
20856
20897
|
if (e.method === "GET" && n.pathname === "/pages/run.html") return this.renderPageComponent(t, "page-run", Fe);
|
|
20857
20898
|
if (e.method === "GET" && n.pathname === "/pages/help.html") return this.renderPageComponent(t, "page-help", aa(!1), !0);
|
|
@@ -20867,7 +20908,7 @@ var SC = class {
|
|
|
20867
20908
|
}
|
|
20868
20909
|
if (e.method === "GET" && n.pathname === "/workflows") return this.requireAdmin(e, t) ? (t.writeHead(302, { Location: "/settings/workflows" }), t.end()) : void 0;
|
|
20869
20910
|
if (e.method === "GET" && n.pathname === "/settings") return this.isAdmin(e) ? (t.writeHead(302, { Location: "/settings/workflows" }), t.end()) : this.requireAdmin(e, t);
|
|
20870
|
-
let r = n.pathname.match(/^\/settings\/(workflows|secrets|tokens|notifications)$/);
|
|
20911
|
+
let r = n.pathname.match(/^\/settings\/(workflows|secrets|tokens|notifications|workers)$/);
|
|
20871
20912
|
if (e.method === "GET" && r) return this.requireAdmin(e, t) ? (r[1], this.renderAppShell(t)) : void 0;
|
|
20872
20913
|
if (n.pathname === "/api/api-keys") {
|
|
20873
20914
|
if (!this.isAdmin(e)) return this.requireAdmin(e, t);
|
|
@@ -21712,7 +21753,7 @@ async function AC(e) {
|
|
|
21712
21753
|
if (!t) {
|
|
21713
21754
|
t = !0, console.log(`\n🛑 Received ${n}. Initiating graceful shutdown...`);
|
|
21714
21755
|
try {
|
|
21715
|
-
|
|
21756
|
+
VC(), 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);
|
|
21716
21757
|
} catch (e) {
|
|
21717
21758
|
console.error("❌ Error during graceful shutdown:", e.message), process.exit(1);
|
|
21718
21759
|
}
|
|
@@ -21803,59 +21844,102 @@ PC("github-status", (e) => {
|
|
|
21803
21844
|
});
|
|
21804
21845
|
});
|
|
21805
21846
|
//#endregion
|
|
21806
|
-
//#region src/
|
|
21807
|
-
var IC = {
|
|
21808
|
-
|
|
21809
|
-
|
|
21810
|
-
|
|
21811
|
-
|
|
21812
|
-
|
|
21813
|
-
|
|
21847
|
+
//#region src/version.ts
|
|
21848
|
+
var IC = {
|
|
21849
|
+
name: "@cloud-cli/on",
|
|
21850
|
+
version: "0.0.0",
|
|
21851
|
+
description: "CLI entry point for the on plugin ecosystem.",
|
|
21852
|
+
type: "module",
|
|
21853
|
+
packageManager: "pnpm@12.3.4",
|
|
21854
|
+
license: "MIT",
|
|
21855
|
+
exports: { ".": {
|
|
21856
|
+
types: "./dist/index.d.ts",
|
|
21857
|
+
default: "./dist/on.js"
|
|
21858
|
+
} },
|
|
21859
|
+
files: ["dist"],
|
|
21860
|
+
main: "dist/on.js",
|
|
21861
|
+
bin: { on: "dist/on.js" },
|
|
21862
|
+
dependencies: {
|
|
21863
|
+
acorn: "^8.18.0",
|
|
21864
|
+
openai: "7.15.0",
|
|
21865
|
+
"web-push": "^3.6.7",
|
|
21866
|
+
yaml: "^2.9.0"
|
|
21867
|
+
},
|
|
21868
|
+
scripts: {
|
|
21869
|
+
ci: "pnpm build && pnpm lint && pnpm test",
|
|
21870
|
+
build: "vite build && tsc --emitDeclarationOnly --declaration",
|
|
21871
|
+
lint: "eslint .",
|
|
21872
|
+
test: "vitest run",
|
|
21873
|
+
"test:e2e": "docker build --no-cache -t on:e2e . && node tests/docker-smoke.mjs",
|
|
21874
|
+
release: "npx --yes semantic-release@24 -b main --no-ci"
|
|
21875
|
+
},
|
|
21876
|
+
devDependencies: {
|
|
21877
|
+
"@changesets/cli": "^2.31.1",
|
|
21878
|
+
"@eslint/js": "^10.0.1",
|
|
21879
|
+
"@types/node": "^26.5.1",
|
|
21880
|
+
"@types/web-push": "^3.6.4",
|
|
21881
|
+
"@typescript-eslint/eslint-plugin": "^8.70.0",
|
|
21882
|
+
"@typescript-eslint/parser": "^8.70.0",
|
|
21883
|
+
eslint: "10.8.0",
|
|
21884
|
+
marked: "^18.0.12",
|
|
21885
|
+
tsx: "4.23.13",
|
|
21886
|
+
typescript: "6.0.3",
|
|
21887
|
+
"typescript-eslint": "8.70.0",
|
|
21888
|
+
vite: "8.2.0",
|
|
21889
|
+
vitest: "4.1.11"
|
|
21890
|
+
}
|
|
21891
|
+
}.version, LC = { isStopping: !1 }, RC = /* @__PURE__ */ new Set(), zC = null, BC = null;
|
|
21892
|
+
function VC() {
|
|
21893
|
+
LC.isStopping = !0, zC?.abort(), BC?.();
|
|
21894
|
+
}
|
|
21895
|
+
function HC(e, t, n, r) {
|
|
21896
|
+
LC.isStopping = !1;
|
|
21897
|
+
let i = [UC(e, t, n, r)];
|
|
21814
21898
|
return AC(i), i;
|
|
21815
21899
|
}
|
|
21816
|
-
async function
|
|
21900
|
+
async function UC(e, t, n, r) {
|
|
21817
21901
|
let i = await kC(), a = new SC(), o = /* @__PURE__ */ new Set(), s = 0, c = null, l = () => {
|
|
21818
21902
|
s++, c?.();
|
|
21819
|
-
};
|
|
21820
|
-
|
|
21821
|
-
let
|
|
21822
|
-
for (console.log(`🚀 Worker scheduler started. Driver: ${i.name}. Concurrency: ${e}. Tags: ${r.tags.join(", ") || "(none)"}`); !
|
|
21903
|
+
}, u = process.env.WORKER_NAME || "cli";
|
|
21904
|
+
BC = l, zC = new AbortController();
|
|
21905
|
+
let d = WC(r, zC.signal, l), f = setInterval(() => void JC(r, u, e, o.size), 15e3);
|
|
21906
|
+
for (f.unref(), JC(r, u, e, o.size), console.log(`🚀 Worker scheduler started. Driver: ${i.name}. Concurrency: ${e}. Tags: ${r.tags.join(", ") || "(none)"}`); !LC.isStopping;) {
|
|
21823
21907
|
let n = s;
|
|
21824
21908
|
try {
|
|
21825
|
-
for (; o.size < e && !
|
|
21909
|
+
for (; o.size < e && !LC.isStopping;) {
|
|
21826
21910
|
let e = await t.claimNextJob(r.tags);
|
|
21827
21911
|
if (!e) break;
|
|
21828
|
-
if (
|
|
21912
|
+
if (LC.isStopping) {
|
|
21829
21913
|
await t.releaseJob(e.id);
|
|
21830
21914
|
break;
|
|
21831
21915
|
}
|
|
21832
|
-
let n
|
|
21833
|
-
|
|
21834
|
-
|
|
21835
|
-
let
|
|
21836
|
-
if (!
|
|
21837
|
-
let
|
|
21838
|
-
|
|
21839
|
-
workerId:
|
|
21916
|
+
let n;
|
|
21917
|
+
n = (async () => {
|
|
21918
|
+
qC(r, e.id);
|
|
21919
|
+
let n = await a.getRevision(e.workflow_id, e.workflow_revision);
|
|
21920
|
+
if (!n) throw await t.finishJob(e.id, "failed"), Error(`Workflow ${e.workflow_id} revision ${e.workflow_revision} was not found`);
|
|
21921
|
+
let o = new Se();
|
|
21922
|
+
o.replace(await YC(r, u, e.id)), await XC({
|
|
21923
|
+
workerId: u,
|
|
21840
21924
|
job: e,
|
|
21841
21925
|
queue: t,
|
|
21842
|
-
secrets:
|
|
21926
|
+
secrets: o,
|
|
21843
21927
|
config: r,
|
|
21844
21928
|
driver: i,
|
|
21845
|
-
workflow:
|
|
21929
|
+
workflow: n
|
|
21846
21930
|
});
|
|
21847
|
-
})().catch((e) => console.error(`[${
|
|
21848
|
-
o.delete(
|
|
21849
|
-
}), o.add(
|
|
21931
|
+
})().catch((e) => console.error(`[${u}] ⚠️ Worker execution error:`, e)).finally(() => {
|
|
21932
|
+
o.delete(n), qC(r, e.id), l();
|
|
21933
|
+
}), o.add(n);
|
|
21850
21934
|
}
|
|
21851
21935
|
} catch (e) {
|
|
21852
21936
|
console.error("⚠️ Worker scheduler claim error:", e), setTimeout(l, 5e3).unref();
|
|
21853
21937
|
}
|
|
21854
|
-
!
|
|
21938
|
+
!LC.isStopping && s === n && (await GC(n, () => s, (e) => c = e), c = null);
|
|
21855
21939
|
}
|
|
21856
|
-
|
|
21940
|
+
zC.abort(), clearInterval(f), await Promise.allSettled(o), await d, BC = null, console.log("🛑 Worker scheduler stopped cleanly.");
|
|
21857
21941
|
}
|
|
21858
|
-
async function
|
|
21942
|
+
async function WC(e, t, n) {
|
|
21859
21943
|
let r = 1e3;
|
|
21860
21944
|
for (; !t.aborted;) {
|
|
21861
21945
|
try {
|
|
@@ -21866,10 +21950,10 @@ async function UC(e, t, n) {
|
|
|
21866
21950
|
if (t.aborted || e?.name === "AbortError") break;
|
|
21867
21951
|
console.error(`⚠️ Worker event stream disconnected: ${e.message}`);
|
|
21868
21952
|
}
|
|
21869
|
-
await
|
|
21953
|
+
await KC(r, t), r = Math.min(r * 2, 3e4);
|
|
21870
21954
|
}
|
|
21871
21955
|
}
|
|
21872
|
-
function
|
|
21956
|
+
function GC(e, t, n) {
|
|
21873
21957
|
return new Promise((r) => {
|
|
21874
21958
|
if (t() !== e) return r();
|
|
21875
21959
|
let i = setTimeout(r, 6e4);
|
|
@@ -21878,7 +21962,7 @@ function WC(e, t, n) {
|
|
|
21878
21962
|
});
|
|
21879
21963
|
});
|
|
21880
21964
|
}
|
|
21881
|
-
function
|
|
21965
|
+
function KC(e, t) {
|
|
21882
21966
|
return new Promise((n) => {
|
|
21883
21967
|
if (t.aborted) return n();
|
|
21884
21968
|
let r = () => {
|
|
@@ -21889,7 +21973,7 @@ function GC(e, t) {
|
|
|
21889
21973
|
t.addEventListener("abort", r, { once: !0 });
|
|
21890
21974
|
});
|
|
21891
21975
|
}
|
|
21892
|
-
async function
|
|
21976
|
+
async function qC(e, t) {
|
|
21893
21977
|
if (e.workerToken) try {
|
|
21894
21978
|
let n = await fetch(new URL("/api/events", e.serverUrl), {
|
|
21895
21979
|
method: "POST",
|
|
@@ -21905,7 +21989,29 @@ async function KC(e, t) {
|
|
|
21905
21989
|
dr("Failed to publish job status event:", e);
|
|
21906
21990
|
}
|
|
21907
21991
|
}
|
|
21908
|
-
async function
|
|
21992
|
+
async function JC(e, t, n, r) {
|
|
21993
|
+
if (e.workerToken) try {
|
|
21994
|
+
await fetch(new URL("/api/workers/heartbeat", e.serverUrl), {
|
|
21995
|
+
method: "POST",
|
|
21996
|
+
headers: {
|
|
21997
|
+
Authorization: `Bearer ${e.workerToken}`,
|
|
21998
|
+
"Content-Type": "application/json",
|
|
21999
|
+
"X-Runner-Worker-Id": t
|
|
22000
|
+
},
|
|
22001
|
+
body: JSON.stringify({
|
|
22002
|
+
workerId: t,
|
|
22003
|
+
version: IC,
|
|
22004
|
+
tags: e.tags,
|
|
22005
|
+
concurrency: n,
|
|
22006
|
+
activeJobs: r
|
|
22007
|
+
}),
|
|
22008
|
+
signal: AbortSignal.timeout(5e3)
|
|
22009
|
+
});
|
|
22010
|
+
} catch (e) {
|
|
22011
|
+
dr("Failed to publish worker heartbeat:", e);
|
|
22012
|
+
}
|
|
22013
|
+
}
|
|
22014
|
+
async function YC(e, t, n) {
|
|
21909
22015
|
if (!e.workerToken) return {};
|
|
21910
22016
|
try {
|
|
21911
22017
|
let r = await fetch(new URL(`/api/jobs/${n}/secrets`, e.serverUrl), {
|
|
@@ -21921,7 +22027,7 @@ async function qC(e, t, n) {
|
|
|
21921
22027
|
throw console.error(`Unable to retrieve secrets for job ${n}:`, e), e;
|
|
21922
22028
|
}
|
|
21923
22029
|
}
|
|
21924
|
-
async function
|
|
22030
|
+
async function XC(e) {
|
|
21925
22031
|
let { workerId: t, job: n, config: r, secrets: a, queue: o } = e;
|
|
21926
22032
|
console.log(`\n[${t}] 📦 Claimed Job #${n.id} (Workflow: ${n.workflow_id})`);
|
|
21927
22033
|
let s = typeof n.payload == "string" ? JSON.parse(n.payload) : n.payload;
|
|
@@ -21933,7 +22039,12 @@ async function JC(e) {
|
|
|
21933
22039
|
workflow: l
|
|
21934
22040
|
}, d = l.steps, f = s.inputs || {}, p = Date.now(), m = i.join(r.storagePath, `job-${n.id}`), h = i.join(m, "logs"), g = i.join(m, "wd"), _ = {
|
|
21935
22041
|
inputs: f,
|
|
21936
|
-
env: {
|
|
22042
|
+
env: {
|
|
22043
|
+
...r.env,
|
|
22044
|
+
CI: "true",
|
|
22045
|
+
CI_JOB_ID: String(n.id),
|
|
22046
|
+
CI_JOB_NAME: l.name
|
|
22047
|
+
},
|
|
21937
22048
|
secrets: a.getAll(),
|
|
21938
22049
|
steps: {},
|
|
21939
22050
|
logsDir: h,
|
|
@@ -21946,10 +22057,14 @@ async function JC(e) {
|
|
|
21946
22057
|
durationMs: 0,
|
|
21947
22058
|
outputs: {},
|
|
21948
22059
|
logContent: ""
|
|
21949
|
-
})), b =
|
|
21950
|
-
await o.saveReport(n.id, b),
|
|
22060
|
+
})), b = uw(n, "running", p, f, _.env, y, s);
|
|
22061
|
+
await o.saveReport(n.id, b), qC(r, n.id);
|
|
21951
22062
|
try {
|
|
21952
|
-
Object.assign(_.env, await
|
|
22063
|
+
Object.assign(_.env, await ew(l.env, _)), Object.assign(_.env, {
|
|
22064
|
+
CI: "true",
|
|
22065
|
+
CI_JOB_ID: String(n.id),
|
|
22066
|
+
CI_JOB_NAME: l.name
|
|
22067
|
+
}), sw(l.secretFiles, a, g), v = l.cache ? String(await mr.evaluateValue(l.cache.key, _)) : "", v && l.cache && await ow(o, "cache", `${n.workflow_id}:${v}`, l.cache.paths, g), await o.saveReport(n.id, b);
|
|
21953
22068
|
} catch (e) {
|
|
21954
22069
|
b.status = "failed", b.finishedAt = (/* @__PURE__ */ new Date()).toISOString(), b.durationMs = Date.now() - p;
|
|
21955
22070
|
let r = {
|
|
@@ -21964,7 +22079,7 @@ async function JC(e) {
|
|
|
21964
22079
|
}
|
|
21965
22080
|
let x = [...r.plugins];
|
|
21966
22081
|
for (let e of l.plugins || []) try {
|
|
21967
|
-
let t = await
|
|
22082
|
+
let t = await ew(e.secrets, _);
|
|
21968
22083
|
x.push(FC({
|
|
21969
22084
|
...e,
|
|
21970
22085
|
secrets: t
|
|
@@ -21979,24 +22094,24 @@ async function JC(e) {
|
|
|
21979
22094
|
runUrl: new URL(`/runs/${n.id}`, r.serverUrl).toString()
|
|
21980
22095
|
};
|
|
21981
22096
|
await S.triggerWorkflowStart(ee);
|
|
21982
|
-
let { cancelled: te, failed: ne } = await
|
|
22097
|
+
let { cancelled: te, failed: ne } = await ZC({
|
|
21983
22098
|
payload: s,
|
|
21984
22099
|
steps: d,
|
|
21985
22100
|
executionContext: _,
|
|
21986
22101
|
...u
|
|
21987
22102
|
}, b), re = te ? "cancelled" : ne ? "failed" : "success";
|
|
21988
22103
|
if (re === "success") try {
|
|
21989
|
-
let e = l.artifacts ? await
|
|
22104
|
+
let e = l.artifacts ? await aw(l.artifacts.paths, g) : [];
|
|
21990
22105
|
if (e.length && (await o.saveStoredFiles("artifact", String(n.id), e), b.artifacts = e.map((e) => e.path)), v && l.cache) {
|
|
21991
|
-
let e = await
|
|
22106
|
+
let e = await aw(l.cache.paths, g);
|
|
21992
22107
|
e.length && await o.saveStoredFiles("cache", `${n.workflow_id}:${v}`, e);
|
|
21993
22108
|
}
|
|
21994
22109
|
} catch (e) {
|
|
21995
22110
|
console.error(`[${t}] ⚠️ Failed to store workflow files:`, e), re = "failed";
|
|
21996
22111
|
}
|
|
21997
|
-
b.status = re, b.durationMs = Date.now() - p, b.finishedAt = (/* @__PURE__ */ new Date()).toISOString(), await o.completeJob(n.id, re, b),
|
|
22112
|
+
b.status = re, b.durationMs = Date.now() - p, b.finishedAt = (/* @__PURE__ */ new Date()).toISOString(), await o.completeJob(n.id, re, b), qC(r, n.id), console.log(`[${t}] ✅ Job #${n.id} completed as: ${re}`), await S.triggerWorkflowFinish(ee, re);
|
|
21998
22113
|
}
|
|
21999
|
-
async function
|
|
22114
|
+
async function ZC(e, t) {
|
|
22000
22115
|
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;
|
|
22001
22116
|
try {
|
|
22002
22117
|
for (let s = 0; s < c.length; s++) {
|
|
@@ -22011,14 +22126,14 @@ async function YC(e, t) {
|
|
|
22011
22126
|
logContent: "",
|
|
22012
22127
|
startedAt: h,
|
|
22013
22128
|
finishedAt: void 0
|
|
22014
|
-
}, t.durationMs = Date.now() - Date.parse(t.startedAt), await i.saveReport(o.id, t),
|
|
22129
|
+
}, t.durationMs = Date.now() - Date.parse(t.startedAt), await i.saveReport(o.id, t), qC(a, o.id);
|
|
22015
22130
|
let g = {
|
|
22016
22131
|
failed: !0,
|
|
22017
22132
|
skipped: !1,
|
|
22018
22133
|
cancelled: !1,
|
|
22019
22134
|
report: null
|
|
22020
22135
|
}, _ = e.workflow?.retries ?? 0;
|
|
22021
|
-
for (let e = 1; e <= _ + 1 && (g = await
|
|
22136
|
+
for (let e = 1; e <= _ + 1 && (g = await QC({
|
|
22022
22137
|
workerId: r,
|
|
22023
22138
|
jobId: o.id,
|
|
22024
22139
|
step: m,
|
|
@@ -22046,7 +22161,7 @@ async function YC(e, t) {
|
|
|
22046
22161
|
exitCode: 1,
|
|
22047
22162
|
error: "Step failed before execution started",
|
|
22048
22163
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
22049
|
-
}, p = s + 1, t.durationMs = Date.now() - Date.parse(t.startedAt), await i.saveReport(o.id, t),
|
|
22164
|
+
}, p = s + 1, t.durationMs = Date.now() - Date.parse(t.startedAt), await i.saveReport(o.id, t), qC(a, o.id), g.skipped) break;
|
|
22050
22165
|
if (g.cancelled) {
|
|
22051
22166
|
f = !0, d = !0;
|
|
22052
22167
|
break;
|
|
@@ -22059,13 +22174,13 @@ async function YC(e, t) {
|
|
|
22059
22174
|
} catch (e) {
|
|
22060
22175
|
console.log("🛑 Step failed", e), d = !0;
|
|
22061
22176
|
}
|
|
22062
|
-
return p < c.length && (
|
|
22177
|
+
return p < c.length && (lw(c, p, u), t.durationMs = Date.now() - Date.parse(t.startedAt), await i.saveReport(o.id, t), qC(a, o.id)), {
|
|
22063
22178
|
failed: d,
|
|
22064
22179
|
cancelled: f,
|
|
22065
22180
|
stepReports: u
|
|
22066
22181
|
};
|
|
22067
22182
|
}
|
|
22068
|
-
async function
|
|
22183
|
+
async function QC(e) {
|
|
22069
22184
|
let { step: t, stepIndex: n, executionContext: r } = e;
|
|
22070
22185
|
if (t.id ||= `step-${n}`, t.name ||= t.id, t.if && !await mr.evaluateConditions(t.if, r)) return dr(`⏩ Skipped step ${t.id} based on condition: ${t.if}`, r), {
|
|
22071
22186
|
failed: !1,
|
|
@@ -22074,7 +22189,7 @@ async function XC(e) {
|
|
|
22074
22189
|
report: null
|
|
22075
22190
|
};
|
|
22076
22191
|
try {
|
|
22077
|
-
let
|
|
22192
|
+
let i = await ew(t.env, r), a = {
|
|
22078
22193
|
jobId: String(e.jobId),
|
|
22079
22194
|
step: t,
|
|
22080
22195
|
command: t.run,
|
|
@@ -22087,16 +22202,19 @@ async function XC(e) {
|
|
|
22087
22202
|
logsDir: r.logsDir,
|
|
22088
22203
|
env: {
|
|
22089
22204
|
...r.env,
|
|
22090
|
-
...
|
|
22091
|
-
WORKING_DIR: r.workingDir
|
|
22205
|
+
...i,
|
|
22206
|
+
WORKING_DIR: r.workingDir,
|
|
22207
|
+
CI: "true",
|
|
22208
|
+
CI_JOB_ID: String(e.jobId),
|
|
22209
|
+
CI_JOB_NAME: r.env.CI_JOB_NAME || t.name || t.id || `step-${n}`
|
|
22092
22210
|
}
|
|
22093
22211
|
};
|
|
22094
|
-
return t.eval ?
|
|
22212
|
+
return t.eval ? $C({
|
|
22095
22213
|
...e,
|
|
22096
|
-
stepContext:
|
|
22097
|
-
}) :
|
|
22214
|
+
stepContext: a
|
|
22215
|
+
}) : cw({
|
|
22098
22216
|
...e,
|
|
22099
|
-
stepContext:
|
|
22217
|
+
stepContext: a
|
|
22100
22218
|
});
|
|
22101
22219
|
} catch (n) {
|
|
22102
22220
|
let i = n?.message || String(n);
|
|
@@ -22117,7 +22235,7 @@ async function XC(e) {
|
|
|
22117
22235
|
};
|
|
22118
22236
|
}
|
|
22119
22237
|
}
|
|
22120
|
-
async function
|
|
22238
|
+
async function $C(e) {
|
|
22121
22239
|
let { queue: t, stepContext: n, executionContext: r } = e, { jobId: i, step: a } = n, o = Date.now(), s = a.id, c = a.name, l;
|
|
22122
22240
|
try {
|
|
22123
22241
|
let e = new Promise((e, t) => {
|
|
@@ -22167,7 +22285,7 @@ async function ZC(e) {
|
|
|
22167
22285
|
l && clearTimeout(l);
|
|
22168
22286
|
}
|
|
22169
22287
|
}
|
|
22170
|
-
async function
|
|
22288
|
+
async function ew(e, t) {
|
|
22171
22289
|
let n = {};
|
|
22172
22290
|
if (e) for (let [r, i] of Object.entries(e)) n[r] = String(await mr.evaluateValue(i, {
|
|
22173
22291
|
...t,
|
|
@@ -22178,18 +22296,18 @@ async function QC(e, t) {
|
|
|
22178
22296
|
}));
|
|
22179
22297
|
return n;
|
|
22180
22298
|
}
|
|
22181
|
-
var
|
|
22182
|
-
function
|
|
22299
|
+
var tw = 52428800;
|
|
22300
|
+
function nw(e, t) {
|
|
22183
22301
|
let n = i.resolve(e), r = i.resolve(n, t);
|
|
22184
22302
|
if (r !== n && !r.startsWith(`${n}${i.sep}`)) throw Error(`Stored file path escapes workspace: ${t}`);
|
|
22185
22303
|
return r;
|
|
22186
22304
|
}
|
|
22187
|
-
function
|
|
22305
|
+
function rw(e, t) {
|
|
22188
22306
|
let n = "^";
|
|
22189
22307
|
for (let e = 0; e < t.length; e++) t[e] === "*" && t[e + 1] === "*" ? (n += ".*", e++) : t[e] === "*" ? n += "[^/]*" : n += t[e].replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
|
|
22190
22308
|
return RegExp(`${n}$`).test(e);
|
|
22191
22309
|
}
|
|
22192
|
-
function
|
|
22310
|
+
function iw(t, n) {
|
|
22193
22311
|
let r = i.resolve(n), a = [], o = (t) => {
|
|
22194
22312
|
for (let n of e.readdirSync(t, { withFileTypes: !0 })) {
|
|
22195
22313
|
let e = i.join(t, n.name);
|
|
@@ -22197,42 +22315,42 @@ function nw(t, n) {
|
|
|
22197
22315
|
}
|
|
22198
22316
|
};
|
|
22199
22317
|
return o(r), a.filter((r) => t.some((t) => {
|
|
22200
|
-
let i =
|
|
22318
|
+
let i = nw(n, t);
|
|
22201
22319
|
if (e.existsSync(i)) {
|
|
22202
22320
|
let n = e.statSync(i);
|
|
22203
22321
|
if (n.isFile()) return r === t;
|
|
22204
22322
|
if (n.isDirectory()) return r === t || r.startsWith(`${t.replace(/\/$/, "")}/`);
|
|
22205
22323
|
}
|
|
22206
|
-
return
|
|
22324
|
+
return rw(r, t);
|
|
22207
22325
|
}));
|
|
22208
22326
|
}
|
|
22209
|
-
async function
|
|
22210
|
-
let r =
|
|
22327
|
+
async function aw(t, n) {
|
|
22328
|
+
let r = iw(t, n), i = 0;
|
|
22211
22329
|
return r.map((t) => {
|
|
22212
|
-
let r = e.readFileSync(
|
|
22213
|
-
if (r.byteLength >
|
|
22330
|
+
let r = e.readFileSync(nw(n, t));
|
|
22331
|
+
if (r.byteLength > tw || (i += r.byteLength) > tw) throw Error(`Stored files exceed ${tw} bytes`);
|
|
22214
22332
|
return {
|
|
22215
22333
|
path: t,
|
|
22216
22334
|
content: r.toString("base64")
|
|
22217
22335
|
};
|
|
22218
22336
|
});
|
|
22219
22337
|
}
|
|
22220
|
-
async function
|
|
22338
|
+
async function ow(t, n, r, a, o) {
|
|
22221
22339
|
for (let s of await t.getStoredFiles(n, r)) {
|
|
22222
|
-
if (!a.some((e) => s.path === e || s.path.startsWith(`${e.replace(/\/$/, "")}/`) ||
|
|
22223
|
-
let t =
|
|
22340
|
+
if (!a.some((e) => s.path === e || s.path.startsWith(`${e.replace(/\/$/, "")}/`) || rw(s.path, e))) continue;
|
|
22341
|
+
let t = nw(o, s.path);
|
|
22224
22342
|
e.mkdirSync(i.dirname(t), { recursive: !0 }), e.writeFileSync(t, Buffer.from(s.content, "base64"));
|
|
22225
22343
|
}
|
|
22226
22344
|
}
|
|
22227
|
-
function
|
|
22345
|
+
function sw(t, n, r) {
|
|
22228
22346
|
for (let [a, o] of Object.entries(t || {})) {
|
|
22229
22347
|
let t = n.getValue(o);
|
|
22230
22348
|
if (!t) throw Error(`Secret '${o}' is not available for file '${a}'`);
|
|
22231
|
-
let s =
|
|
22349
|
+
let s = nw(r, a);
|
|
22232
22350
|
e.mkdirSync(i.dirname(s), { recursive: !0 }), e.writeFileSync(s, t.encoding === "base64" ? Buffer.from(t.value, "base64") : t.value, { mode: 384 }), e.chmodSync(s, 384);
|
|
22233
22351
|
}
|
|
22234
22352
|
}
|
|
22235
|
-
async function
|
|
22353
|
+
async function cw(t) {
|
|
22236
22354
|
let { workerId: n, jobId: r, stepContext: i, executionContext: a, driver: o, queue: s } = t, { step: c } = i, l = c.id, u = c.name, d;
|
|
22237
22355
|
try {
|
|
22238
22356
|
d = o.execute(i);
|
|
@@ -22247,11 +22365,11 @@ async function ow(t) {
|
|
|
22247
22365
|
logFilePath: ""
|
|
22248
22366
|
};
|
|
22249
22367
|
}
|
|
22250
|
-
|
|
22368
|
+
RC.add(d);
|
|
22251
22369
|
let f = !1, p = setInterval(async () => {
|
|
22252
22370
|
await s.isCancelled(r) && (console.log(`[${n}] 🛑 Job #${r} was cancelled! Halting execution.`), f = !0, clearInterval(p), await d.cancel());
|
|
22253
22371
|
}, 3e3), m = await d.done;
|
|
22254
|
-
if (clearInterval(p),
|
|
22372
|
+
if (clearInterval(p), RC.delete(d), d.logFilePath && e.existsSync(d.logFilePath)) try {
|
|
22255
22373
|
let e = await o.readLog(d.logFilePath);
|
|
22256
22374
|
await s.saveStepLog(r, l, e);
|
|
22257
22375
|
} catch (e) {
|
|
@@ -22278,7 +22396,7 @@ async function ow(t) {
|
|
|
22278
22396
|
}
|
|
22279
22397
|
};
|
|
22280
22398
|
}
|
|
22281
|
-
function
|
|
22399
|
+
function lw(e, t, n) {
|
|
22282
22400
|
for (let r = t; r < e.length; r++) {
|
|
22283
22401
|
let t = e[r], i = t.id || `step-${r}`;
|
|
22284
22402
|
n[r] = {
|
|
@@ -22292,7 +22410,7 @@ function sw(e, t, n) {
|
|
|
22292
22410
|
};
|
|
22293
22411
|
}
|
|
22294
22412
|
}
|
|
22295
|
-
function
|
|
22413
|
+
function uw(e, t, n, r, i, a, o) {
|
|
22296
22414
|
return {
|
|
22297
22415
|
jobId: String(e.id),
|
|
22298
22416
|
parentId: String(e.parentId || ""),
|
|
@@ -22313,7 +22431,7 @@ function cw(e, t, n, r, i, a, o) {
|
|
|
22313
22431
|
}
|
|
22314
22432
|
//#endregion
|
|
22315
22433
|
//#region src/scheduler.ts
|
|
22316
|
-
function
|
|
22434
|
+
function dw(e, t, n, r) {
|
|
22317
22435
|
return e.split(",").some((e) => {
|
|
22318
22436
|
let [i, a] = e.split("/"), o = a ? Number(a) : 1;
|
|
22319
22437
|
if (!Number.isInteger(o) || o < 1) return !1;
|
|
@@ -22321,7 +22439,7 @@ function lw(e, t, n, r) {
|
|
|
22321
22439
|
return Number.isInteger(l) && Number.isInteger(u) && t >= l && t <= u && (t - l) % o === 0;
|
|
22322
22440
|
});
|
|
22323
22441
|
}
|
|
22324
|
-
function
|
|
22442
|
+
function fw(e, t, n = "UTC") {
|
|
22325
22443
|
let r = e.trim().split(/\s+/);
|
|
22326
22444
|
if (r.length !== 5) return !1;
|
|
22327
22445
|
let i = new Intl.DateTimeFormat("en-US", {
|
|
@@ -22343,16 +22461,16 @@ function uw(e, t, n = "UTC") {
|
|
|
22343
22461
|
"Thu",
|
|
22344
22462
|
"Fri",
|
|
22345
22463
|
"Sat"
|
|
22346
|
-
].indexOf(i.weekday), o =
|
|
22347
|
-
return
|
|
22464
|
+
].indexOf(i.weekday), o = dw(r[2], Number(i.day), 1, 31), s = dw(r[4].replace(/7/g, "0"), a, 0, 6);
|
|
22465
|
+
return dw(r[0], Number(i.minute), 0, 59) && dw(r[1], Number(i.hour), 0, 23) && dw(r[3], Number(i.month), 1, 12) && (r[2] === "*" || r[4] === "*" ? o && s : o || s);
|
|
22348
22466
|
}
|
|
22349
|
-
function
|
|
22467
|
+
function pw(e) {
|
|
22350
22468
|
if (!e) return 0;
|
|
22351
22469
|
let t = e.match(/^([+-]?)(\d+)(m|h)$/);
|
|
22352
22470
|
if (!t) throw Error(`Invalid solar offset: ${e}`);
|
|
22353
22471
|
return (t[1] === "-" ? -1 : 1) * Number(t[2]) * (t[3] === "h" ? 36e5 : 6e4);
|
|
22354
22472
|
}
|
|
22355
|
-
function
|
|
22473
|
+
function mw(e, t) {
|
|
22356
22474
|
let n = t.latitude, r = t.longitude, i = Date.UTC(e.getUTCFullYear(), e.getUTCMonth(), e.getUTCDate()), a = Math.floor((i - Date.UTC(e.getUTCFullYear(), 0, 0)) / 864e5), o = r / 15, s = a + ((t.event === "sunrise" ? 6 : 18) - o) / 24, c = .9856 * s - 3.289, l = c + 1.916 * Math.sin(c * Math.PI / 180) + .02 * Math.sin(2 * c * Math.PI / 180) + 282.634;
|
|
22357
22475
|
l = (l + 360) % 360;
|
|
22358
22476
|
let u = Math.atan(.91764 * Math.tan(l * Math.PI / 180)) * 180 / Math.PI;
|
|
@@ -22360,9 +22478,9 @@ function fw(e, t) {
|
|
|
22360
22478
|
let d = .39782 * Math.sin(l * Math.PI / 180), f = Math.cos(Math.asin(d)), p = (Math.cos(90.833 * Math.PI / 180) - d * Math.sin(n * Math.PI / 180)) / (f * Math.cos(n * Math.PI / 180));
|
|
22361
22479
|
if (p > 1 || p < -1) return null;
|
|
22362
22480
|
let m = (t.event === "sunrise" ? 360 - Math.acos(p) * 180 / Math.PI : Math.acos(p) * 180 / Math.PI) / 15;
|
|
22363
|
-
return m = (m + u - .06571 * s - 6.622 - o + 24) % 24, new Date(i + m * 36e5 +
|
|
22481
|
+
return m = (m + u - .06571 * s - 6.622 - o + 24) % 24, new Date(i + m * 36e5 + pw(t.offset));
|
|
22364
22482
|
}
|
|
22365
|
-
var
|
|
22483
|
+
var hw = class {
|
|
22366
22484
|
queue;
|
|
22367
22485
|
workflows;
|
|
22368
22486
|
config;
|
|
@@ -22379,9 +22497,9 @@ var pw = class {
|
|
|
22379
22497
|
async tick(e = /* @__PURE__ */ new Date()) {
|
|
22380
22498
|
let t = /* @__PURE__ */ new Date(Math.floor(e.getTime() / 6e4) * 6e4);
|
|
22381
22499
|
for (let { definition: e, revision: n } of await this.workflows.published()) {
|
|
22382
|
-
for (let [r, i] of (e.schedule || []).entries())
|
|
22500
|
+
for (let [r, i] of (e.schedule || []).entries()) fw(i.cron, t, i.timezone || "UTC") && await this.enqueue(e, n, i, `cron-${r}`, t);
|
|
22383
22501
|
for (let [r, i] of (e.solar || []).entries()) {
|
|
22384
|
-
let a =
|
|
22502
|
+
let a = mw(t, i);
|
|
22385
22503
|
a && Math.floor(a.getTime() / 6e4) === t.getTime() / 6e4 && await this.enqueue(e, n, i, `solar-${r}`, t);
|
|
22386
22504
|
}
|
|
22387
22505
|
}
|
|
@@ -22407,7 +22525,7 @@ var pw = class {
|
|
|
22407
22525
|
};
|
|
22408
22526
|
//#endregion
|
|
22409
22527
|
//#region src/logger.ts
|
|
22410
|
-
function
|
|
22528
|
+
function gw() {
|
|
22411
22529
|
for (let e of [
|
|
22412
22530
|
"log",
|
|
22413
22531
|
"info",
|
|
@@ -22422,8 +22540,8 @@ function mw() {
|
|
|
22422
22540
|
}
|
|
22423
22541
|
//#endregion
|
|
22424
22542
|
//#region src/index.ts
|
|
22425
|
-
async function
|
|
22426
|
-
|
|
22543
|
+
async function _w() {
|
|
22544
|
+
gw();
|
|
22427
22545
|
let { config: e, command: t } = await ge();
|
|
22428
22546
|
e || process.exit(1);
|
|
22429
22547
|
let n = new Se(), r = new xe(process.env.WORKER_NAME || "cli");
|
|
@@ -22438,13 +22556,13 @@ async function hw() {
|
|
|
22438
22556
|
});
|
|
22439
22557
|
break;
|
|
22440
22558
|
case "start-workers":
|
|
22441
|
-
console.log(`⚙️ Starting worker scheduler with ${e.workers} concurrent slot(s)...`), await r.init(),
|
|
22559
|
+
console.log(`⚙️ Starting worker scheduler with ${e.workers} concurrent slot(s)...`), await r.init(), HC(e.workers, r, n, e);
|
|
22442
22560
|
break;
|
|
22443
22561
|
case "start-scheduler": {
|
|
22444
22562
|
console.log("Starting workflow scheduler..."), await r.init();
|
|
22445
22563
|
let t = new SC();
|
|
22446
22564
|
await t.init();
|
|
22447
|
-
let n = new
|
|
22565
|
+
let n = new hw(r, t, e);
|
|
22448
22566
|
n.start();
|
|
22449
22567
|
let i = () => {
|
|
22450
22568
|
n.stop(), process.exit(0);
|
|
@@ -22455,6 +22573,6 @@ async function hw() {
|
|
|
22455
22573
|
default: console.error(`❌ Unknown command: '${t}'`), he(), process.exit(1);
|
|
22456
22574
|
}
|
|
22457
22575
|
}
|
|
22458
|
-
|
|
22576
|
+
_w().catch(console.error);
|
|
22459
22577
|
//#endregion
|
|
22460
22578
|
export { MC as GitHubStatusPlugin, FC as createWorkflowPlugin, PC as registerWorkflowPlugin };
|
package/dist/queue.d.ts
CHANGED
|
@@ -54,6 +54,14 @@ export declare class QueueManager {
|
|
|
54
54
|
path: string;
|
|
55
55
|
content: string;
|
|
56
56
|
}>>;
|
|
57
|
+
updateWorkerPresence(worker: {
|
|
58
|
+
id: string;
|
|
59
|
+
version: string;
|
|
60
|
+
tags: string[];
|
|
61
|
+
concurrency: number;
|
|
62
|
+
activeJobs: number;
|
|
63
|
+
}): Promise<void>;
|
|
64
|
+
listWorkerPresence(): Promise<any[]>;
|
|
57
65
|
saveAiRequest(jobId: string | number, workflowUrl: string, request: unknown): Promise<void>;
|
|
58
66
|
}
|
|
59
67
|
//# sourceMappingURL=queue.d.ts.map
|
package/dist/queue.d.ts.map
CHANGED
|
@@ -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;AAIvF,qBAAa,YAAY;IAGX,OAAO,CAAC,QAAQ;IAF5B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;gBAE7B,QAAQ,EAAE,MAAM;IAE9B,IAAI;IAKV;;;OAGG;IACG,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,GAAE,MAAM,EAAO,EAAE,cAAc,CAAC,EAAE,MAAM;IAoBrI;;;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;IAUtG,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,WAAW,GAAG,YAAY,CAAC;IAapF,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,YAAY,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;IAiCnF;;OAEG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKrD,cAAc;IAMd,YAAY;
|
|
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;AAIvF,qBAAa,YAAY;IAGX,OAAO,CAAC,QAAQ;IAF5B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;gBAE7B,QAAQ,EAAE,MAAM;IAE9B,IAAI;IAKV;;;OAGG;IACG,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,GAAE,MAAM,EAAO,EAAE,cAAc,CAAC,EAAE,MAAM;IAoBrI;;;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;IAUtG,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,WAAW,GAAG,YAAY,CAAC;IAapF,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,YAAY,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;IAiCnF;;OAEG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKrD,cAAc;IAMd,YAAY;IAyDlB;;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,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAkCrH;;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;IAUnE,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAW7H,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAK/G,oBAAoB,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAUrI,kBAAkB,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAapC,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;CAGlG"}
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAc,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAOxF,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,aAAa,CAA0C;IAC/D,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,gBAAgB,CAA0B;IAClD,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,IAAI,CAAiB;IAC7B,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,WAAW,CAAS;IAC5B,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;IAazC,oBAAoB,CAAC,YAAY,EAAE,mBAAmB;YAIxC,aAAa;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAc,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAOxF,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,aAAa,CAA0C;IAC/D,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,gBAAgB,CAA0B;IAClD,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,IAAI,CAAiB;IAC7B,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,WAAW,CAAS;IAC5B,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;IAazC,oBAAoB,CAAC,YAAY,EAAE,mBAAmB;YAIxC,aAAa;IAkQ3B;;OAEG;YACW,aAAa;YA0Bb,cAAc;YASd,aAAa;YAiBb,iBAAiB;YA0BjB,WAAW;YAwBX,UAAU;YAyBV,cAAc;IA8C5B,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,QAAQ;YAIF,QAAQ;YAQR,YAAY;IAO1B,OAAO,CAAC,YAAY;IAmBpB,OAAO,CAAC,YAAY;YAUN,QAAQ;YAYR,wBAAwB;YAcxB,kBAAkB;YAOlB,iBAAiB;YAcjB,kBAAkB;YAoBlB,oBAAoB;YASpB,qBAAqB;YAWrB,gBAAgB;YAMhB,gBAAgB;YAiBhB,kBAAkB;YASlB,gBAAgB;IAc9B;;OAEG;YACW,kBAAkB;YAclB,iBAAiB;IA2B/B,OAAO,CAAC,mBAAmB;YASb,mBAAmB;YAWnB,qBAAqB;IAWnC;;OAEG;YACW,eAAe;IAK7B,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,mBAAmB;YAcb,mBAAmB;IAwBjC;;OAEG;YACW,gBAAgB;YA+BhB,gBAAgB;YAiBhB,sBAAsB;YAetB,YAAY;YAsDZ,oBAAoB;YA4BpB,eAAe;YAkBf,cAAc;IAS5B,OAAO,CAAC,MAAM;YAMA,UAAU;IAIlB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAU5C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAS5B"}
|
package/dist/settings-ui.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare function generateSettingsHtml(page?: 'tokens' | 'notifications'): string;
|
|
1
|
+
export declare function generateSettingsHtml(page?: 'tokens' | 'notifications' | 'workers'): string;
|
|
2
2
|
//# sourceMappingURL=settings-ui.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-ui.d.ts","sourceRoot":"","sources":["../src/settings-ui.ts"],"names":[],"mappings":"AAEA,wBAAgB,oBAAoB,CAAC,IAAI,GAAE,QAAQ,GAAG,
|
|
1
|
+
{"version":3,"file":"settings-ui.d.ts","sourceRoot":"","sources":["../src/settings-ui.ts"],"names":[],"mappings":"AAEA,wBAAgB,oBAAoB,CAAC,IAAI,GAAE,QAAQ,GAAG,eAAe,GAAG,SAAoB,GAAG,MAAM,CAEpG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,cAAc,QAAsB,CAAC"}
|
package/dist/worker.d.ts.map
CHANGED
|
@@ -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,EAKZ,MAAM,YAAY,CAAC;
|
|
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,EAKZ,MAAM,YAAY,CAAC;AAUpB,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,iBA8ErB;AAqHD;;GAEG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAAE,WAAW,iBAmI9C"}
|