@cloud-cli/on 1.18.1 → 1.20.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/migrations.d.ts +2 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/on.js +129 -114
- package/dist/server.d.ts.map +1 -1
- package/dist/workflows.d.ts +2 -1
- package/dist/workflows.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../src/migrations.ts"],"names":[],"mappings":"AAmBA,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAcnD"}
|
package/dist/on.js
CHANGED
|
@@ -3857,7 +3857,7 @@ var qn = class {
|
|
|
3857
3857
|
let t = Buffer.from(e, "base64"), n = c.createDecipheriv("aes-256-gcm", this.encryptionKey(), t.subarray(0, 12));
|
|
3858
3858
|
return n.setAuthTag(t.subarray(12, 28)), Buffer.concat([n.update(t.subarray(28)), n.final()]).toString("utf8");
|
|
3859
3859
|
}
|
|
3860
|
-
}, Jn = "<!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 <meta name=\"theme-color\" content=\"#030712\" />\n <meta name=\"mobile-web-app-capable\" content=\"yes\" />\n <link rel=\"manifest\" href=\"/manifest.webmanifest\" />\n <link rel=\"icon\" href=\"/app-icon.svg\" type=\"image/svg+xml\" />\n <title>Runner Workflow Management</title>\n <script type=\"importmap\">\n { \"imports\": { \"@li3/\": \"https://cdn.li3.dev/@li3/\" } }\n <\/script>\n <style>\n body[data-page='list'] .editor-panel {\n display: none;\n }\n body[data-page='editor'] .workflow-list,\n body[data-page='editor'] .secrets-panel {\n display: none;\n }\n </style>\n </head>\n <body data-page=\"__WORKFLOW_PAGE__\" class=\"min-h-screen bg-gray-950 p-4 font-sans text-gray-100 md:p-6\">\n <link rel=\"component\" href=\"https://sodium.static.apphor.de/code-editor.html\" />\n <link rel=\"component\" href=\"https://sodium.static.apphor.de/lucide-icon.html\" />\n\n <template app>\n <main class=\"mx-auto max-w-6xl space-y-5\">\n <header class=\"flex flex-col gap-4 border-b border-gray-800 pb-5 sm:flex-row sm:items-end sm:justify-between\">\n <div class=\"flex items-start gap-3\">\n <a\n href=\"/runs\"\n aria-label=\"Back to dashboard\"\n class=\"mt-1 rounded-lg border border-gray-800 bg-gray-900 p-2 text-gray-400 transition hover:border-indigo-500/60 hover:text-indigo-300\"\n >\n <lucide-icon icon=\"arrow-left\" size=\"17\"></lucide-icon>\n </a>\n <div>\n <p class=\"text-xs font-semibold uppercase tracking-[0.18em] text-indigo-400\">Runner engine</p>\n <h1 class=\"mt-1 text-2xl font-bold tracking-tight text-white\">Workflow control room</h1>\n <p class=\"mt-1 text-sm text-gray-400\">Draft, validate, and publish revisioned YAML definitions.</p>\n </div>\n </div>\n <a\n href=\"/workflows/new\"\n 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 shadow-lg shadow-indigo-950/40 transition hover:bg-indigo-400\"\n >\n <lucide-icon icon=\"plus\" size=\"16\"></lucide-icon>\n New workflow\n </a>\n </header>\n\n <template if=\"notice\">\n <div\n role=\"status\"\n class=\"flex items-center gap-2 rounded-lg border px-3 py-2.5 text-sm\"\n bind-class=\"noticeError ? 'border-rose-500/40.bg-rose-500/10.text-rose-100' : 'border-emerald-500/40.bg-emerald-500/10.text-emerald-100'\"\n >\n <lucide-icon bind-icon=\"noticeError ? 'circle-alert' : 'circle-check'\" size=\"17\"></lucide-icon>\n <span>{{ notice }}</span>\n </div>\n </template>\n\n <section id=\"workflow-layout\" class=\"space-y-5\">\n <aside class=\"workflow-list rounded-xl border border-gray-800 bg-gray-900/80 p-3 shadow-xl shadow-black/10\">\n <div class=\"mb-3 flex items-center justify-between px-2\">\n <h2 class=\"text-xs font-semibold uppercase tracking-wider text-gray-400\">Workflows</h2>\n <span class=\"rounded-full bg-gray-800 px-2 py-0.5 text-xs font-mono text-gray-400\"\n >{{ workflows.length }}</span\n >\n </div>\n <div class=\"space-y-1 flex flex-col w-full\">\n <template for=\"workflow of workflows\">\n <button\n on-click=\"visitWorkflow(workflow.id)\"\n class=\"flex w-full items-center justify-between rounded-lg px-3 py-2.5 text-left text-sm transition hover:bg-gray-800\"\n bind-class=\"workflow.id === selectedId ? 'bg-indigo-500/15.text-indigo-100' : 'text-gray-300'\"\n >\n <span class=\"truncate font-medium\">{{ workflow.name }}</span>\n <span\n class=\"ml-3 rounded-full border border-gray-700 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-gray-400\"\n >{{ workflow.status }}</span\n >\n </button>\n </template>\n <p class=\"px-3 py-5 text-center text-sm text-gray-500\" class-hidden=\"workflows.length > 0\">\n No workflows yet.\n </p>\n </div>\n </aside>\n\n <section\n class=\"editor-panel overflow-hidden rounded-xl border border-gray-800 bg-gray-900/80 shadow-xl shadow-black/10\"\n >\n <div class=\"border-b border-gray-800 bg-gray-900 px-4 py-4 sm:px-5\">\n <div class=\"flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between\">\n <label class=\"block max-w-xl flex-1 text-xs font-semibold uppercase tracking-wider text-gray-400\"\n >Workflow ID\n <input\n bind-value=\"workflowId\"\n on-input=\"setWorkflowId($event)\"\n class=\"mt-2 w-full rounded-lg border border-gray-700 bg-gray-950 px-3 py-2.5 font-mono text-sm normal-case tracking-normal text-white outline-none transition focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20\"\n placeholder=\"derived-from-workflow-name\"\n />\n </label>\n <div class=\"flex flex-wrap gap-2\">\n <button\n on-click=\"validate()\"\n bind-disabled=\"busy\"\n class=\"inline-flex items-center gap-2 rounded-lg border border-gray-700 px-3 py-2.5 text-sm font-medium text-gray-200 transition hover:border-gray-500 hover:bg-gray-800 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <lucide-icon icon=\"badge-check\" size=\"16\"></lucide-icon>\n Validate\n </button>\n <button\n on-click=\"save()\"\n bind-disabled=\"busy\"\n class=\"inline-flex items-center gap-2 rounded-lg bg-indigo-500 px-3 py-2.5 text-sm font-semibold text-white transition hover:bg-indigo-400 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <lucide-icon icon=\"save\" size=\"16\"></lucide-icon>\n Save draft\n </button>\n <button\n on-click=\"publish()\"\n bind-disabled=\"busy\"\n class=\"inline-flex items-center gap-2 rounded-lg border border-emerald-500/50 px-3 py-2.5 text-sm font-medium text-emerald-300 transition hover:bg-emerald-500/10 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <lucide-icon icon=\"rocket\" size=\"16\"></lucide-icon>\n Publish\n </button>\n <button\n on-click=\"remove()\"\n bind-disabled=\"busy\"\n class=\"inline-flex items-center gap-2 rounded-lg border border-rose-500/50 px-3 py-2.5 text-sm font-medium text-rose-300 transition hover:bg-rose-500/10 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <lucide-icon icon=\"trash-2\" size=\"16\"></lucide-icon>\n Delete\n </button>\n </div>\n </div>\n </div>\n <div class=\"p-3 sm:p-5\">\n <div class=\"mb-2 flex items-center justify-between px-1 text-xs text-gray-500\">\n <span class=\"font-medium\">YAML definition</span>\n <span class=\"font-mono\">{{ source.length }} characters</span>\n </div>\n <code-editor\n id=\"source-yaml\"\n language=\"yaml\"\n theme=\"atom-one-dark\"\n nostatus=\"true\"\n class=\"block h-[calc(100vh-23rem)] min-h-[28rem] overflow-hidden rounded-lg border border-gray-700 bg-gray-950\"\n bind-value=\"source\"\n on-change=\"setSource($event.detail)\"\n ></code-editor>\n </div>\n </section>\n </section>\n\n <section\n class=\"secrets-panel rounded-xl border border-gray-800 bg-gray-900/80 p-4 shadow-xl shadow-black/10 sm:p-5\"\n >\n <div class=\"mb-4 flex items-start gap-3\">\n <span class=\"rounded-lg bg-indigo-500/10 p-2 text-indigo-300\"\n ><lucide-icon icon=\"key-round\" size=\"18\"></lucide-icon\n ></span>\n <div>\n <h2 class=\"text-base font-semibold text-white\">Secrets</h2>\n <p class=\"mt-0.5 text-sm text-gray-400\">\n Values are write-only and encrypted. Re-saving a name replaces its value.\n </p>\n </div>\n </div>\n <div class=\"grid gap-3 md:grid-cols-[14rem_1fr_auto_auto]\">\n <input\n bind-value=\"secretName\"\n on-input=\"setSecretName($event)\"\n class=\"rounded-lg border border-gray-700 bg-gray-950 px-3 py-2.5 font-mono text-sm uppercase outline-none transition focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20\"\n placeholder=\"NPM_TOKEN\"\n />\n <input\n ref=\"secretValueInput\"\n bind-value=\"secretValue\"\n on-input=\"setSecretValue($event)\"\n type=\"password\"\n class=\"rounded-lg border border-gray-700 bg-gray-950 px-3 py-2.5 font-mono text-sm outline-none transition focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20\"\n placeholder=\"New secret value\"\n />\n <button\n on-click=\"saveSecret()\"\n bind-disabled=\"busy\"\n class=\"inline-flex items-center justify-center gap-2 rounded-lg border border-indigo-500/60 px-3 py-2.5 text-sm font-medium text-indigo-200 transition hover:bg-indigo-500/10 disabled:opacity-50\"\n >\n <lucide-icon icon=\"lock-keyhole\" size=\"16\"></lucide-icon>Save value\n </button>\n <button\n on-click=\"removeSecret()\"\n bind-disabled=\"busy\"\n class=\"inline-flex items-center justify-center gap-2 rounded-lg border border-rose-500/50 px-3 py-2.5 text-sm font-medium text-rose-300 transition hover:bg-rose-500/10 disabled:opacity-50\"\n >\n <lucide-icon icon=\"trash-2\" size=\"16\"></lucide-icon>Delete\n </button>\n </div>\n <div class=\"mt-4 flex flex-col space-y-1\">\n <template for=\"name of secrets\"\n ><button\n on-click=\"selectSecret(name)\"\n class=\"rounded-md border border-gray-700 px-2.5 py-1.5 font-mono text-xs text-gray-300 transition hover:border-indigo-500 hover:text-indigo-200 w-full\"\n >\n {{ name }}\n </button></template\n >\n </div>\n </section>\n </main>\n\n <script setup>\n import { onInit, ref, templateRef } from '@li3/web';\n\n export default function () {\n const page = document.body.dataset.page;\n const initialId = __WORKFLOW_ID__;\n const workflows = ref([]);\n const secrets = ref([]);\n const source = ref('');\n const workflowId = ref('');\n const selectedId = ref('');\n const secretName = ref('');\n const secretValue = ref('');\n const notice = ref('');\n const noticeError = ref(false);\n const busy = ref(false);\n const secretValueInput = templateRef('secretValueInput');\n\n const showNotice = (message, error = false) => {\n notice.value = message;\n noticeError.value = error;\n };\n const api = async (url, options = {}) => {\n const response = await fetch(url, {\n headers: { accept: 'application/json', ...(options.body ? { 'content-type': 'application/json' } : {}) },\n ...options,\n });\n const body = response.status === 204 ? null : await response.json().catch(() => ({}));\n if (!response.ok) throw new Error(body.error || `Request failed: ${response.status}`);\n return body;\n };\n const derivedId = () =>\n source.value\n .match(/^\\s*(?:id:\\s*([^\\n]+)|name:\\s*([^\\n]+))/m)\n ?.slice(1)\n .find(Boolean)\n ?.trim()\n .toLowerCase()\n .replace(/[^a-z0-9]/g, '-')\n .replace(/(^-|-$)/g, '') || '';\n const run = async (action) => {\n if (busy.value) return;\n busy.value = true;\n try {\n await action();\n } catch (error) {\n showNotice(error.message, true);\n } finally {\n busy.value = false;\n }\n };\n const loadWorkflows = async () => {\n workflows.value = (await api('/api/workflows')).workflows;\n };\n const loadSecrets = async () => {\n secrets.value = (await api('/api/secrets')).secrets;\n };\n const openWorkflow = async (id) => {\n const workflow = await api(`/api/workflows/${id}`);\n selectedId.value = workflow.id;\n workflowId.value = workflow.id;\n source.value = workflow.sourceYaml;\n };\n const validate = () =>\n run(async () => {\n const result = await api('/api/workflows/validate', {\n method: 'POST',\n body: JSON.stringify({ sourceYaml: source.value }),\n });\n showNotice(`Valid: ${result.workflows.length} workflow definition(s).`);\n });\n const save = () =>\n run(async () => {\n const id = workflowId.value.trim() || derivedId();\n if (!id) throw new Error('Set an ID or add a workflow name first.');\n const workflow = await api(`/api/workflows/${id}`, {\n method: 'PUT',\n body: JSON.stringify({ sourceYaml: source.value }),\n });\n selectedId.value = workflow.id;\n workflowId.value = workflow.id;\n showNotice(`Saved ${workflow.id} as draft revision ${workflow.revision}.`);\n if (!initialId) history.replaceState(null, '', `/workflows/${workflow.id}`);\n });\n const publish = () =>\n run(async () => {\n const id = workflowId.value.trim() || selectedId.value;\n if (!id) throw new Error('Save a workflow before publishing it.');\n const workflow = await api(`/api/workflows/${id}/publish`, { method: 'POST' });\n selectedId.value = workflow.id;\n showNotice(`Published ${workflow.id} revision ${workflow.revision}.`);\n });\n const remove = () =>\n run(async () => {\n const id = workflowId.value.trim() || selectedId.value;\n if (!id || !confirm(`Delete ${id} and all of its revisions?`)) return;\n await api(`/api/workflows/${id}`, { method: 'DELETE' });\n window.location.href = '/workflows';\n });\n const saveSecret = () =>\n run(async () => {\n const name = secretName.value.trim();\n if (!name || !secretValue.value) throw new Error('Set a secret name and value first.');\n await api(`/api/secrets/${name}`, { method: 'PUT', body: JSON.stringify({ value: secretValue.value }) });\n secretValue.value = '';\n await loadSecrets();\n showNotice(`Saved ${name}.`);\n });\n const removeSecret = () =>\n run(async () => {\n const name = secretName.value.trim();\n if (!name || !confirm(`Delete ${name}?`)) return;\n await api(`/api/secrets/${name}`, { method: 'DELETE' });\n secretName.value = '';\n secretValue.value = '';\n await loadSecrets();\n showNotice(`Deleted ${name}.`);\n });\n const setSource = (value) => {\n source.value = value;\n };\n const setWorkflowId = (event) => {\n workflowId.value = event.target.value;\n };\n const setSecretName = (event) => {\n secretName.value = event.target.value;\n };\n const setSecretValue = (event) => {\n secretValue.value = event.target.value;\n };\n const selectSecret = (name) => {\n secretName.value = name;\n secretValueInput.value?.focus();\n };\n const visitWorkflow = (id) => {\n window.location.href = `/workflows/${id}`;\n };\n\n onInit(() => {\n const loadPage =\n page === 'editor'\n ? initialId\n ? openWorkflow(initialId)\n : Promise.resolve()\n : Promise.all([loadWorkflows(), loadSecrets()]);\n loadPage.catch((error) => showNotice(error.message, true));\n });\n return {\n workflows,\n secrets,\n source,\n workflowId,\n selectedId,\n secretName,\n secretValue,\n notice,\n noticeError,\n busy,\n validate,\n save,\n publish,\n remove,\n saveSecret,\n removeSecret,\n setSource,\n setWorkflowId,\n setSecretName,\n setSecretValue,\n selectSecret,\n visitWorkflow,\n };\n }\n <\/script>\n </template>\n\n <script type=\"module\">\n import '@li3/web';\n <\/script>\n <script src=\"https://cdn.tailwindcss.com\"><\/script>\n </body>\n</html>\n";
|
|
3860
|
+
}, Jn = "<!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 <meta name=\"theme-color\" content=\"#030712\" />\n <meta name=\"mobile-web-app-capable\" content=\"yes\" />\n <link rel=\"manifest\" href=\"/manifest.webmanifest\" />\n <link rel=\"icon\" href=\"/app-icon.svg\" type=\"image/svg+xml\" />\n <title>Runner Workflow Management</title>\n <script type=\"importmap\">\n { \"imports\": { \"@li3/\": \"https://cdn.li3.dev/@li3/\" } }\n <\/script>\n <style>\n body[data-page='list'] .editor-panel {\n display: none;\n }\n body[data-page='editor'] .workflow-list,\n body[data-page='editor'] .secrets-panel {\n display: none;\n }\n </style>\n </head>\n <body data-page=\"__WORKFLOW_PAGE__\" class=\"min-h-screen bg-gray-950 p-4 font-sans text-gray-100 md:p-6\">\n <link rel=\"component\" href=\"https://sodium.static.apphor.de/code-editor.html\" />\n <link rel=\"component\" href=\"https://sodium.static.apphor.de/lucide-icon.html\" />\n\n <template app>\n <main class=\"mx-auto max-w-6xl space-y-5\">\n <header class=\"flex flex-col gap-4 border-b border-gray-800 pb-5 sm:flex-row sm:items-end sm:justify-between\">\n <div class=\"flex items-start gap-3\">\n <a\n href=\"/runs\"\n aria-label=\"Back to dashboard\"\n class=\"mt-1 rounded-lg border border-gray-800 bg-gray-900 p-2 text-gray-400 transition hover:border-indigo-500/60 hover:text-indigo-300\"\n >\n <lucide-icon icon=\"arrow-left\" size=\"17\"></lucide-icon>\n </a>\n <div>\n <p class=\"text-xs font-semibold uppercase tracking-[0.18em] text-indigo-400\">Runner engine</p>\n <h1 class=\"mt-1 text-2xl font-bold tracking-tight text-white\">Workflow control room</h1>\n <p class=\"mt-1 text-sm text-gray-400\">Draft, validate, and publish revisioned YAML definitions.</p>\n </div>\n </div>\n <a\n href=\"/workflows/new\"\n 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 shadow-lg shadow-indigo-950/40 transition hover:bg-indigo-400\"\n >\n <lucide-icon icon=\"plus\" size=\"16\"></lucide-icon>\n New workflow\n </a>\n </header>\n\n <template if=\"notice\">\n <div\n role=\"status\"\n class=\"flex items-center gap-2 rounded-lg border px-3 py-2.5 text-sm\"\n bind-class=\"noticeError ? 'border-rose-500/40.bg-rose-500/10.text-rose-100' : 'border-emerald-500/40.bg-emerald-500/10.text-emerald-100'\"\n >\n <lucide-icon bind-icon=\"noticeError ? 'circle-alert' : 'circle-check'\" size=\"17\"></lucide-icon>\n <span>{{ notice }}</span>\n </div>\n </template>\n\n <section id=\"workflow-layout\" class=\"space-y-5\">\n <aside class=\"workflow-list rounded-xl border border-gray-800 bg-gray-900/80 p-3 shadow-xl shadow-black/10\">\n <div class=\"mb-3 flex items-center justify-between px-2\">\n <h2 class=\"text-xs font-semibold uppercase tracking-wider text-gray-400\">Workflows</h2>\n <span class=\"rounded-full bg-gray-800 px-2 py-0.5 text-xs font-mono text-gray-400\"\n >{{ workflows.length }}</span\n >\n </div>\n <div class=\"space-y-1 flex flex-col w-full\">\n <template for=\"workflow of workflows\">\n <button\n on-click=\"visitWorkflow(workflow.id)\"\n class=\"flex w-full items-center justify-between rounded-lg px-3 py-2.5 text-left text-sm transition hover:bg-gray-800\"\n bind-class=\"workflow.id === selectedId ? 'bg-indigo-500/15.text-indigo-100' : 'text-gray-300'\"\n >\n <span class=\"truncate font-medium\">{{ workflow.name }}</span>\n <span\n class=\"ml-3 rounded-full border border-gray-700 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-gray-400\"\n >{{ workflow.status }}</span\n >\n </button>\n </template>\n <p class=\"px-3 py-5 text-center text-sm text-gray-500\" class-hidden=\"workflows.length > 0\">\n No workflows yet.\n </p>\n </div>\n </aside>\n\n <section\n class=\"editor-panel overflow-hidden rounded-xl border border-gray-800 bg-gray-900/80 shadow-xl shadow-black/10\"\n >\n <div class=\"border-b border-gray-800 bg-gray-900 px-4 py-4 sm:px-5\">\n <div class=\"flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between\">\n <label class=\"block max-w-xl flex-1 text-xs font-semibold uppercase tracking-wider text-gray-400\"\n >Workflow ID\n <input\n bind-value=\"workflowId\"\n on-input=\"setWorkflowId($event)\"\n class=\"mt-2 w-full rounded-lg border border-gray-700 bg-gray-950 px-3 py-2.5 font-mono text-sm normal-case tracking-normal text-white outline-none transition focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20\"\n placeholder=\"derived-from-workflow-name\"\n />\n </label>\n <label class=\"inline-flex items-center gap-2 text-sm text-gray-300\">\n <input\n type=\"checkbox\"\n bind-checked=\"enabled\"\n on-change=\"setEnabled($event)\"\n class=\"h-4 w-4 rounded border-gray-700 bg-gray-950 text-indigo-500 focus:ring-2 focus:ring-indigo-500/30\"\n />\n Enabled\n </label>\n <div class=\"flex flex-wrap gap-2\">\n <button\n on-click=\"validate()\"\n bind-disabled=\"busy\"\n class=\"inline-flex items-center gap-2 rounded-lg border border-gray-700 px-3 py-2.5 text-sm font-medium text-gray-200 transition hover:border-gray-500 hover:bg-gray-800 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <lucide-icon icon=\"badge-check\" size=\"16\"></lucide-icon>\n Validate\n </button>\n <button\n on-click=\"save()\"\n bind-disabled=\"busy\"\n class=\"inline-flex items-center gap-2 rounded-lg bg-indigo-500 px-3 py-2.5 text-sm font-semibold text-white transition hover:bg-indigo-400 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <lucide-icon icon=\"save\" size=\"16\"></lucide-icon>\n Save draft\n </button>\n <button\n on-click=\"publish()\"\n bind-disabled=\"busy\"\n class=\"inline-flex items-center gap-2 rounded-lg border border-emerald-500/50 px-3 py-2.5 text-sm font-medium text-emerald-300 transition hover:bg-emerald-500/10 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <lucide-icon icon=\"rocket\" size=\"16\"></lucide-icon>\n Publish\n </button>\n <button\n on-click=\"remove()\"\n bind-disabled=\"busy\"\n class=\"inline-flex items-center gap-2 rounded-lg border border-rose-500/50 px-3 py-2.5 text-sm font-medium text-rose-300 transition hover:bg-rose-500/10 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <lucide-icon icon=\"trash-2\" size=\"16\"></lucide-icon>\n Delete\n </button>\n </div>\n </div>\n </div>\n <div class=\"p-3 sm:p-5\">\n <div class=\"mb-2 flex items-center justify-between px-1 text-xs text-gray-500\">\n <span class=\"font-medium\">YAML definition</span>\n <span class=\"font-mono\">{{ source.length }} characters</span>\n </div>\n <code-editor\n id=\"source-yaml\"\n language=\"yaml\"\n theme=\"atom-one-dark\"\n nostatus=\"true\"\n class=\"block h-[calc(100vh-23rem)] min-h-[28rem] overflow-hidden rounded-lg border border-gray-700 bg-gray-950\"\n bind-value=\"source\"\n on-change=\"setSource($event.detail)\"\n ></code-editor>\n </div>\n </section>\n </section>\n\n <section\n class=\"secrets-panel rounded-xl border border-gray-800 bg-gray-900/80 p-4 shadow-xl shadow-black/10 sm:p-5\"\n >\n <div class=\"mb-4 flex items-start gap-3\">\n <span class=\"rounded-lg bg-indigo-500/10 p-2 text-indigo-300\"\n ><lucide-icon icon=\"key-round\" size=\"18\"></lucide-icon\n ></span>\n <div>\n <h2 class=\"text-base font-semibold text-white\">Secrets</h2>\n <p class=\"mt-0.5 text-sm text-gray-400\">\n Values are write-only and encrypted. Re-saving a name replaces its value.\n </p>\n </div>\n </div>\n <div class=\"grid gap-3 md:grid-cols-[14rem_1fr_auto_auto]\">\n <input\n bind-value=\"secretName\"\n on-input=\"setSecretName($event)\"\n class=\"rounded-lg border border-gray-700 bg-gray-950 px-3 py-2.5 font-mono text-sm uppercase outline-none transition focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20\"\n placeholder=\"NPM_TOKEN\"\n />\n <input\n ref=\"secretValueInput\"\n bind-value=\"secretValue\"\n on-input=\"setSecretValue($event)\"\n type=\"password\"\n class=\"rounded-lg border border-gray-700 bg-gray-950 px-3 py-2.5 font-mono text-sm outline-none transition focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20\"\n placeholder=\"New secret value\"\n />\n <button\n on-click=\"saveSecret()\"\n bind-disabled=\"busy\"\n class=\"inline-flex items-center justify-center gap-2 rounded-lg border border-indigo-500/60 px-3 py-2.5 text-sm font-medium text-indigo-200 transition hover:bg-indigo-500/10 disabled:opacity-50\"\n >\n <lucide-icon icon=\"lock-keyhole\" size=\"16\"></lucide-icon>Save value\n </button>\n <button\n on-click=\"removeSecret()\"\n bind-disabled=\"busy\"\n class=\"inline-flex items-center justify-center gap-2 rounded-lg border border-rose-500/50 px-3 py-2.5 text-sm font-medium text-rose-300 transition hover:bg-rose-500/10 disabled:opacity-50\"\n >\n <lucide-icon icon=\"trash-2\" size=\"16\"></lucide-icon>Delete\n </button>\n </div>\n <div class=\"mt-4 flex flex-col space-y-1\">\n <template for=\"name of secrets\"\n ><button\n on-click=\"selectSecret(name)\"\n class=\"rounded-md border border-gray-700 px-2.5 py-1.5 font-mono text-xs text-gray-300 transition hover:border-indigo-500 hover:text-indigo-200 w-full\"\n >\n {{ name }}\n </button></template\n >\n </div>\n </section>\n </main>\n\n <script setup>\n import { onInit, ref, templateRef } from '@li3/web';\n\n export default function () {\n const page = document.body.dataset.page;\n const initialId = __WORKFLOW_ID__;\n const workflows = ref([]);\n const secrets = ref([]);\n const source = ref('');\n const workflowId = ref('');\n const enabled = ref(true);\n const selectedId = ref('');\n const secretName = ref('');\n const secretValue = ref('');\n const notice = ref('');\n const noticeError = ref(false);\n const busy = ref(false);\n const secretValueInput = templateRef('secretValueInput');\n\n const showNotice = (message, error = false) => {\n notice.value = message;\n noticeError.value = error;\n };\n const api = async (url, options = {}) => {\n const response = await fetch(url, {\n headers: { accept: 'application/json', ...(options.body ? { 'content-type': 'application/json' } : {}) },\n ...options,\n });\n const body = response.status === 204 ? null : await response.json().catch(() => ({}));\n if (!response.ok) throw new Error(body.error || `Request failed: ${response.status}`);\n return body;\n };\n const derivedId = () =>\n source.value\n .match(/^\\s*(?:id:\\s*([^\\n]+)|name:\\s*([^\\n]+))/m)\n ?.slice(1)\n .find(Boolean)\n ?.trim()\n .toLowerCase()\n .replace(/[^a-z0-9]/g, '-')\n .replace(/(^-|-$)/g, '') || '';\n const run = async (action) => {\n if (busy.value) return;\n busy.value = true;\n try {\n await action();\n } catch (error) {\n showNotice(error.message, true);\n } finally {\n busy.value = false;\n }\n };\n const loadWorkflows = async () => {\n workflows.value = (await api('/api/workflows')).workflows;\n };\n const loadSecrets = async () => {\n secrets.value = (await api('/api/secrets')).secrets;\n };\n const openWorkflow = async (id) => {\n const workflow = await api(`/api/workflows/${id}`);\n selectedId.value = workflow.id;\n workflowId.value = workflow.id;\n source.value = workflow.sourceYaml;\n enabled.value = workflow.enabled;\n };\n const validate = () =>\n run(async () => {\n const result = await api('/api/workflows/validate', {\n method: 'POST',\n body: JSON.stringify({ sourceYaml: source.value }),\n });\n showNotice(`Valid: ${result.workflows.length} workflow definition(s).`);\n });\n const save = () =>\n run(async () => {\n const id = workflowId.value.trim() || derivedId();\n if (!id) throw new Error('Set an ID or add a workflow name first.');\n const workflow = await api(`/api/workflows/${id}`, {\n method: 'PUT',\n body: JSON.stringify({ sourceYaml: source.value, enabled: enabled.value }),\n });\n selectedId.value = workflow.id;\n workflowId.value = workflow.id;\n showNotice(`Saved ${workflow.id} as draft revision ${workflow.revision}.`);\n if (!initialId) history.replaceState(null, '', `/workflows/${workflow.id}`);\n });\n const publish = () =>\n run(async () => {\n const id = workflowId.value.trim() || selectedId.value;\n if (!id) throw new Error('Save a workflow before publishing it.');\n const workflow = await api(`/api/workflows/${id}/publish`, { method: 'POST' });\n selectedId.value = workflow.id;\n showNotice(`Published ${workflow.id} revision ${workflow.revision}.`);\n });\n const remove = () =>\n run(async () => {\n const id = workflowId.value.trim() || selectedId.value;\n if (!id || !confirm(`Delete ${id} and all of its revisions?`)) return;\n await api(`/api/workflows/${id}`, { method: 'DELETE' });\n window.location.href = '/workflows';\n });\n const saveSecret = () =>\n run(async () => {\n const name = secretName.value.trim();\n if (!name || !secretValue.value) throw new Error('Set a secret name and value first.');\n await api(`/api/secrets/${name}`, { method: 'PUT', body: JSON.stringify({ value: secretValue.value }) });\n secretValue.value = '';\n await loadSecrets();\n showNotice(`Saved ${name}.`);\n });\n const removeSecret = () =>\n run(async () => {\n const name = secretName.value.trim();\n if (!name || !confirm(`Delete ${name}?`)) return;\n await api(`/api/secrets/${name}`, { method: 'DELETE' });\n secretName.value = '';\n secretValue.value = '';\n await loadSecrets();\n showNotice(`Deleted ${name}.`);\n });\n const setSource = (value) => {\n source.value = value;\n };\n const setWorkflowId = (event) => {\n workflowId.value = event.target.value;\n };\n const setEnabled = (event) => {\n enabled.value = event.target.checked;\n };\n const setSecretName = (event) => {\n secretName.value = event.target.value;\n };\n const setSecretValue = (event) => {\n secretValue.value = event.target.value;\n };\n const selectSecret = (name) => {\n secretName.value = name;\n secretValueInput.value?.focus();\n };\n const visitWorkflow = (id) => {\n window.location.href = `/workflows/${id}`;\n };\n\n onInit(() => {\n const loadPage =\n page === 'editor'\n ? initialId\n ? openWorkflow(initialId)\n : Promise.resolve()\n : Promise.all([loadWorkflows(), loadSecrets()]);\n loadPage.catch((error) => showNotice(error.message, true));\n });\n return {\n workflows,\n secrets,\n source,\n workflowId,\n enabled,\n selectedId,\n secretName,\n secretValue,\n notice,\n noticeError,\n busy,\n validate,\n save,\n publish,\n remove,\n saveSecret,\n removeSecret,\n setSource,\n setWorkflowId,\n setEnabled,\n setSecretName,\n setSecretValue,\n selectSecret,\n visitWorkflow,\n };\n }\n <\/script>\n </template>\n\n <script type=\"module\">\n import '@li3/web';\n <\/script>\n <script src=\"https://cdn.tailwindcss.com\"><\/script>\n </body>\n</html>\n";
|
|
3861
3861
|
//#endregion
|
|
3862
3862
|
//#region src/workflows-ui.ts
|
|
3863
3863
|
function Yn(e, t = "") {
|
|
@@ -8138,10 +8138,20 @@ var qo = /* @__PURE__ */ g({
|
|
|
8138
8138
|
stringify: () => Ko,
|
|
8139
8139
|
visit: () => lr,
|
|
8140
8140
|
visitAsync: () => dr
|
|
8141
|
-
})
|
|
8141
|
+
}), Jo = [{
|
|
8142
|
+
version: "001_add_workflow_enabled",
|
|
8143
|
+
async apply() {
|
|
8144
|
+
(await S.all("PRAGMA table_info(workflows)")).some((e) => e.name === "enabled") || await S.run("ALTER TABLE workflows ADD COLUMN enabled INTEGER NOT NULL DEFAULT 1");
|
|
8145
|
+
}
|
|
8146
|
+
}];
|
|
8147
|
+
async function Yo() {
|
|
8148
|
+
await S.exec("\n CREATE TABLE IF NOT EXISTS schema_migrations (\n version TEXT PRIMARY KEY,\n applied_at DATETIME DEFAULT CURRENT_TIMESTAMP\n )\n ");
|
|
8149
|
+
let e = new Set((await S.all("SELECT version FROM schema_migrations")).map((e) => e.version));
|
|
8150
|
+
for (let t of Jo) e.has(t.version) || (await t.apply(), await S.run("INSERT INTO schema_migrations (version) VALUES (?)", [t.version]));
|
|
8151
|
+
}
|
|
8142
8152
|
//#endregion
|
|
8143
8153
|
//#region src/parser/matrix-expander.ts
|
|
8144
|
-
function
|
|
8154
|
+
function Xo(e) {
|
|
8145
8155
|
if (!e.matrix || Object.keys(e.matrix).length === 0) return [e];
|
|
8146
8156
|
let t = e.matrix;
|
|
8147
8157
|
return Object.keys(t).reduce((e, n) => {
|
|
@@ -8168,22 +8178,22 @@ function Jo(e) {
|
|
|
8168
8178
|
}
|
|
8169
8179
|
//#endregion
|
|
8170
8180
|
//#region src/workflows.ts
|
|
8171
|
-
function
|
|
8181
|
+
function Zo(e) {
|
|
8172
8182
|
let t = String(e || "").toLowerCase().replace(/[^a-z0-9]/g, "-").replace(/^-+|-+$/g, "");
|
|
8173
8183
|
if (!t) throw Error("Workflow requires a name or id");
|
|
8174
8184
|
return t;
|
|
8175
8185
|
}
|
|
8176
|
-
function
|
|
8186
|
+
function Qo(e) {
|
|
8177
8187
|
let t = qo.parse(e);
|
|
8178
8188
|
if (!t || typeof t != "object" || Array.isArray(t)) throw Error("Workflow must be a YAML object");
|
|
8179
8189
|
if (t.includes) throw Error("includes is not supported for database workflows");
|
|
8180
8190
|
if (typeof t.name != "string" || !t.name.trim()) throw Error("Workflow name is required");
|
|
8181
8191
|
if (!Array.isArray(t.steps) || !t.steps.length) throw Error("Workflow requires at least one step");
|
|
8182
8192
|
if (!t.on || typeof t.on != "object") throw Error("Workflow requires an on block");
|
|
8183
|
-
return
|
|
8193
|
+
return Xo(t).map((e) => {
|
|
8184
8194
|
let [t, n] = Object.entries(e.on).find(([e]) => e !== "schedule" && e !== "solar") || ["generic", {}];
|
|
8185
8195
|
return {
|
|
8186
|
-
id:
|
|
8196
|
+
id: Zo(e.id || e.name),
|
|
8187
8197
|
name: e.name,
|
|
8188
8198
|
on: {
|
|
8189
8199
|
...n,
|
|
@@ -8198,37 +8208,39 @@ function Xo(e) {
|
|
|
8198
8208
|
};
|
|
8199
8209
|
});
|
|
8200
8210
|
}
|
|
8201
|
-
var
|
|
8211
|
+
var $o = class {
|
|
8202
8212
|
async init() {
|
|
8203
|
-
await S.exec("\n CREATE TABLE IF NOT EXISTS workflows (\n id TEXT PRIMARY KEY, name TEXT NOT NULL, source_yaml TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'draft', active_revision INTEGER,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n );\n CREATE TABLE IF NOT EXISTS workflow_revisions (\n workflow_id TEXT NOT NULL, revision INTEGER NOT NULL, source_yaml TEXT NOT NULL,\n normalized_json TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (workflow_id, revision)\n );\n CREATE TABLE IF NOT EXISTS scheduled_runs (\n workflow_id TEXT NOT NULL, trigger_id TEXT NOT NULL, scheduled_for TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (workflow_id, trigger_id, scheduled_for)\n );\n ");
|
|
8213
|
+
await S.exec("\n CREATE TABLE IF NOT EXISTS workflows (\n id TEXT PRIMARY KEY, name TEXT NOT NULL, source_yaml TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'draft', active_revision INTEGER, enabled INTEGER NOT NULL DEFAULT 1,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP\n );\n CREATE TABLE IF NOT EXISTS workflow_revisions (\n workflow_id TEXT NOT NULL, revision INTEGER NOT NULL, source_yaml TEXT NOT NULL,\n normalized_json TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (workflow_id, revision)\n );\n CREATE TABLE IF NOT EXISTS scheduled_runs (\n workflow_id TEXT NOT NULL, trigger_id TEXT NOT NULL, scheduled_for TEXT NOT NULL,\n created_at DATETIME DEFAULT CURRENT_TIMESTAMP,\n PRIMARY KEY (workflow_id, trigger_id, scheduled_for)\n );\n "), await Yo();
|
|
8204
8214
|
}
|
|
8205
8215
|
validate(e) {
|
|
8206
|
-
let t =
|
|
8216
|
+
let t = Qo(e);
|
|
8207
8217
|
for (let e of t) {
|
|
8208
8218
|
for (let t of e.schedule || []) if (typeof t.cron != "string" || t.cron.trim().split(/\s+/).length !== 5) throw Error("schedule.cron must have five fields");
|
|
8209
8219
|
for (let t of e.solar || []) if (!t.event || !Number.isFinite(t.latitude) || !Number.isFinite(t.longitude)) throw Error("solar triggers require event, latitude, and longitude");
|
|
8210
8220
|
}
|
|
8211
8221
|
return t;
|
|
8212
8222
|
}
|
|
8213
|
-
async saveDraft(e, t) {
|
|
8214
|
-
let
|
|
8215
|
-
if (
|
|
8216
|
-
let
|
|
8217
|
-
return await S.run("INSERT INTO workflows (id, name, source_yaml, status) VALUES (?, ?, ?, 'draft')\n ON CONFLICT(id) DO UPDATE SET name = excluded.name, source_yaml = excluded.source_yaml, status = 'draft', updated_at = CURRENT_TIMESTAMP", [
|
|
8223
|
+
async saveDraft(e, t, n = !0) {
|
|
8224
|
+
let r = this.validate(t);
|
|
8225
|
+
if (r.length !== 1 || r[0].id !== e) throw Error("Workflow id must match the YAML name or id");
|
|
8226
|
+
let i = await S.get("SELECT COALESCE(MAX(revision), 0) AS revision FROM workflow_revisions WHERE workflow_id = ?", [e]), a = Number(i?.revision || 0) + 1;
|
|
8227
|
+
return await S.run("INSERT INTO workflows (id, name, source_yaml, status, enabled) VALUES (?, ?, ?, 'draft', ?)\n ON CONFLICT(id) DO UPDATE SET name = excluded.name, source_yaml = excluded.source_yaml, status = 'draft', enabled = excluded.enabled, updated_at = CURRENT_TIMESTAMP", [
|
|
8218
8228
|
e,
|
|
8219
|
-
|
|
8220
|
-
t
|
|
8229
|
+
r[0].name,
|
|
8230
|
+
t,
|
|
8231
|
+
+!!n
|
|
8221
8232
|
]), await S.run("INSERT INTO workflow_revisions (workflow_id, revision, source_yaml, normalized_json) VALUES (?, ?, ?, ?)", [
|
|
8222
8233
|
e,
|
|
8223
|
-
|
|
8234
|
+
a,
|
|
8224
8235
|
t,
|
|
8225
|
-
JSON.stringify(
|
|
8236
|
+
JSON.stringify(r[0])
|
|
8226
8237
|
]), {
|
|
8227
8238
|
id: e,
|
|
8228
|
-
name:
|
|
8239
|
+
name: r[0].name,
|
|
8229
8240
|
sourceYaml: t,
|
|
8230
|
-
revision:
|
|
8231
|
-
status: "draft"
|
|
8241
|
+
revision: a,
|
|
8242
|
+
status: "draft",
|
|
8243
|
+
enabled: n
|
|
8232
8244
|
};
|
|
8233
8245
|
}
|
|
8234
8246
|
async publish(e) {
|
|
@@ -8241,33 +8253,36 @@ var Zo = class {
|
|
|
8241
8253
|
name: t.name,
|
|
8242
8254
|
sourceYaml: t.source_yaml,
|
|
8243
8255
|
revision: Number(n.revision),
|
|
8244
|
-
status: "published"
|
|
8256
|
+
status: "published",
|
|
8257
|
+
enabled: !!t.enabled
|
|
8245
8258
|
};
|
|
8246
8259
|
}
|
|
8247
8260
|
async list() {
|
|
8248
|
-
return (await S.all("SELECT id, name, source_yaml, COALESCE(active_revision, 0) AS revision, status FROM workflows ORDER BY name")).map((e) => ({
|
|
8261
|
+
return (await S.all("SELECT id, name, source_yaml, COALESCE(active_revision, 0) AS revision, status, enabled FROM workflows ORDER BY name")).map((e) => ({
|
|
8249
8262
|
id: e.id,
|
|
8250
8263
|
name: e.name,
|
|
8251
8264
|
sourceYaml: e.source_yaml,
|
|
8252
8265
|
revision: Number(e.revision),
|
|
8253
|
-
status: e.status
|
|
8266
|
+
status: e.status,
|
|
8267
|
+
enabled: !!e.enabled
|
|
8254
8268
|
}));
|
|
8255
8269
|
}
|
|
8256
8270
|
async get(e) {
|
|
8257
|
-
let t = await S.get("SELECT id, name, source_yaml, COALESCE(active_revision, 0) AS revision, status FROM workflows WHERE id = ?", [e]);
|
|
8271
|
+
let t = await S.get("SELECT id, name, source_yaml, COALESCE(active_revision, 0) AS revision, status, enabled FROM workflows WHERE id = ?", [e]);
|
|
8258
8272
|
return t ? {
|
|
8259
8273
|
id: t.id,
|
|
8260
8274
|
name: t.name,
|
|
8261
8275
|
sourceYaml: t.source_yaml,
|
|
8262
8276
|
revision: Number(t.revision),
|
|
8263
|
-
status: t.status
|
|
8277
|
+
status: t.status,
|
|
8278
|
+
enabled: !!t.enabled
|
|
8264
8279
|
} : null;
|
|
8265
8280
|
}
|
|
8266
8281
|
async delete(e) {
|
|
8267
8282
|
return await this.get(e) ? (await S.run("DELETE FROM scheduled_runs WHERE workflow_id = ?", [e]), await S.run("DELETE FROM workflow_revisions WHERE workflow_id = ?", [e]), await S.run("DELETE FROM workflows WHERE id = ?", [e]), !0) : !1;
|
|
8268
8283
|
}
|
|
8269
8284
|
async published() {
|
|
8270
|
-
return (await S.all("SELECT w.id AS workflow_id, r.revision, r.normalized_json FROM workflows w JOIN workflow_revisions r\n ON r.workflow_id = w.id AND r.revision = w.active_revision WHERE w.status = 'published'")).map((e) => ({
|
|
8285
|
+
return (await S.all("SELECT w.id AS workflow_id, r.revision, r.normalized_json FROM workflows w JOIN workflow_revisions r\n ON r.workflow_id = w.id AND r.revision = w.active_revision WHERE w.status = 'published' AND w.enabled = 1")).map((e) => ({
|
|
8271
8286
|
workflowId: e.workflow_id,
|
|
8272
8287
|
revision: Number(e.revision),
|
|
8273
8288
|
definition: JSON.parse(e.normalized_json)
|
|
@@ -8284,10 +8299,10 @@ var Zo = class {
|
|
|
8284
8299
|
n
|
|
8285
8300
|
]);
|
|
8286
8301
|
}
|
|
8287
|
-
},
|
|
8302
|
+
}, es = 50, ts = 500, ns = class e {
|
|
8288
8303
|
server;
|
|
8289
8304
|
preprocessors = /* @__PURE__ */ new Map();
|
|
8290
|
-
workflows = new
|
|
8305
|
+
workflows = new $o();
|
|
8291
8306
|
secretRepository = new qn();
|
|
8292
8307
|
queue;
|
|
8293
8308
|
secrets;
|
|
@@ -8334,8 +8349,8 @@ var Zo = class {
|
|
|
8334
8349
|
}), t.end(Yn("editor", n));
|
|
8335
8350
|
}
|
|
8336
8351
|
if (e.method === "GET" && n.pathname === "/api/jobs") {
|
|
8337
|
-
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 ?
|
|
8338
|
-
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 >
|
|
8352
|
+
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 ? es : Number(i);
|
|
8353
|
+
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 > ts ? (t.writeHead(400, { "Content-Type": "application/json; charset=utf-8" }), t.end(JSON.stringify({ error: `limit must be an integer from 1 to ${ts}` }))) : this.renderDashboardJobs(t, s, a, o);
|
|
8339
8354
|
}
|
|
8340
8355
|
if (e.method === "GET" && n.pathname === "/api/events") return this.events.subscribe(e, t);
|
|
8341
8356
|
if (n.pathname === "/api/workflows/validate" && e.method === "POST") return this.handleWorkflowValidation(e, t);
|
|
@@ -8526,7 +8541,7 @@ var Zo = class {
|
|
|
8526
8541
|
return;
|
|
8527
8542
|
}
|
|
8528
8543
|
try {
|
|
8529
|
-
let e = await this.workflows.saveDraft(n, r.sourceYaml);
|
|
8544
|
+
let e = await this.workflows.saveDraft(n, r.sourceYaml, r.enabled !== !1);
|
|
8530
8545
|
t.writeHead(200, { "Content-Type": "application/json; charset=utf-8" }), t.end(JSON.stringify(e));
|
|
8531
8546
|
} catch (e) {
|
|
8532
8547
|
t.writeHead(422, { "Content-Type": "application/json; charset=utf-8" }), t.end(JSON.stringify({ error: e.message }));
|
|
@@ -8593,7 +8608,7 @@ var Zo = class {
|
|
|
8593
8608
|
this.events.publish("jobs.changed", r ? { jobId: r } : {}), t.writeHead(202).end();
|
|
8594
8609
|
}
|
|
8595
8610
|
async renderDashboard(e) {
|
|
8596
|
-
let t = await this.queue.listJobs(51), n = fe(t.slice(0,
|
|
8611
|
+
let t = await this.queue.listJobs(51), n = fe(t.slice(0, es)), r = await this.redactText(pe(n, t.length > es));
|
|
8597
8612
|
e.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }), e.end(r);
|
|
8598
8613
|
}
|
|
8599
8614
|
async renderDashboardJobs(e, t, n, r) {
|
|
@@ -8653,8 +8668,8 @@ var Zo = class {
|
|
|
8653
8668
|
async redactText(e) {
|
|
8654
8669
|
return this.redact(e, await this.currentSecrets());
|
|
8655
8670
|
}
|
|
8656
|
-
listen(e) {
|
|
8657
|
-
return new Promise((t) => {
|
|
8671
|
+
async listen(e) {
|
|
8672
|
+
return await this.workflowsLoaded, new Promise((t) => {
|
|
8658
8673
|
this.server.listen(e, process.env.RUNNER_HOST || "0.0.0.0", () => {
|
|
8659
8674
|
console.log(`🌐 Webhook Ingress Server running on port ${e}`), t(this);
|
|
8660
8675
|
});
|
|
@@ -8667,7 +8682,7 @@ var Zo = class {
|
|
|
8667
8682
|
});
|
|
8668
8683
|
});
|
|
8669
8684
|
}
|
|
8670
|
-
},
|
|
8685
|
+
}, rs = class extends m {
|
|
8671
8686
|
fd;
|
|
8672
8687
|
pending = "";
|
|
8673
8688
|
constructor(e) {
|
|
@@ -8690,7 +8705,7 @@ var Zo = class {
|
|
|
8690
8705
|
}
|
|
8691
8706
|
e.write(this.fd, t, 0, "utf8", (e) => n(e));
|
|
8692
8707
|
}
|
|
8693
|
-
},
|
|
8708
|
+
}, is = s(d), as = class {
|
|
8694
8709
|
name = "systemd";
|
|
8695
8710
|
async isSupported() {
|
|
8696
8711
|
try {
|
|
@@ -8702,7 +8717,7 @@ var Zo = class {
|
|
|
8702
8717
|
execute(t) {
|
|
8703
8718
|
let n = null, r = null, a = Date.now(), o = i.join(t.logsDir, `step-${t.step.id}.log`);
|
|
8704
8719
|
try {
|
|
8705
|
-
e.mkdirSync(t.logsDir, { recursive: !0 }), e.mkdirSync(t.workingDir, { recursive: !0 }), e.chmodSync(t.workingDir, 511), n = e.openSync(o, "a"), r = new
|
|
8720
|
+
e.mkdirSync(t.logsDir, { recursive: !0 }), e.mkdirSync(t.workingDir, { recursive: !0 }), e.chmodSync(t.workingDir, 511), n = e.openSync(o, "a"), r = new rs(n);
|
|
8706
8721
|
} catch (e) {
|
|
8707
8722
|
return {
|
|
8708
8723
|
done: Promise.resolve({
|
|
@@ -8812,7 +8827,7 @@ var Zo = class {
|
|
|
8812
8827
|
cancel: async () => {
|
|
8813
8828
|
p = !0;
|
|
8814
8829
|
try {
|
|
8815
|
-
t.image && await
|
|
8830
|
+
t.image && await is(`docker stop -t 2 ${s}`).catch(() => {}), await is(`systemctl stop ${s}.service`).catch(() => {});
|
|
8816
8831
|
} catch {}
|
|
8817
8832
|
},
|
|
8818
8833
|
logFilePath: o
|
|
@@ -8822,7 +8837,7 @@ var Zo = class {
|
|
|
8822
8837
|
let t = await p(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;
|
|
8823
8838
|
return t.slice(n, r);
|
|
8824
8839
|
}
|
|
8825
|
-
},
|
|
8840
|
+
}, os = class {
|
|
8826
8841
|
name = "standard-process";
|
|
8827
8842
|
async isSupported() {
|
|
8828
8843
|
return !0;
|
|
@@ -8830,7 +8845,7 @@ var Zo = class {
|
|
|
8830
8845
|
execute(t) {
|
|
8831
8846
|
let n = null, r = null, a = Date.now(), o = i.join(t.logsDir, `step-${t.step.id}.log`);
|
|
8832
8847
|
try {
|
|
8833
|
-
e.mkdirSync(t.logsDir, { recursive: !0 }), e.mkdirSync(t.workingDir, { recursive: !0 }), e.chmodSync(t.workingDir, 511), n = e.openSync(o, "a"), r = new
|
|
8848
|
+
e.mkdirSync(t.logsDir, { recursive: !0 }), e.mkdirSync(t.workingDir, { recursive: !0 }), e.chmodSync(t.workingDir, 511), n = e.openSync(o, "a"), r = new rs(n);
|
|
8834
8849
|
} catch (e) {
|
|
8835
8850
|
return {
|
|
8836
8851
|
done: Promise.resolve({
|
|
@@ -8936,19 +8951,19 @@ var Zo = class {
|
|
|
8936
8951
|
};
|
|
8937
8952
|
//#endregion
|
|
8938
8953
|
//#region src/drivers/index.ts
|
|
8939
|
-
async function
|
|
8940
|
-
let e = new
|
|
8941
|
-
return await e.isSupported() ? (console.log("⚡ Selected Execution Driver: Systemd (cgroups enabled)"), e) : (console.log("📦 Selected Execution Driver: Standard Process (Fallback)"), new
|
|
8954
|
+
async function ss() {
|
|
8955
|
+
let e = new as();
|
|
8956
|
+
return await e.isSupported() ? (console.log("⚡ Selected Execution Driver: Systemd (cgroups enabled)"), e) : (console.log("📦 Selected Execution Driver: Standard Process (Fallback)"), new os());
|
|
8942
8957
|
}
|
|
8943
8958
|
//#endregion
|
|
8944
8959
|
//#region src/signals.ts
|
|
8945
|
-
async function
|
|
8960
|
+
async function cs(e) {
|
|
8946
8961
|
let t = !1;
|
|
8947
8962
|
async function n(n) {
|
|
8948
8963
|
if (!t) {
|
|
8949
8964
|
t = !0, console.log(`\n🛑 Received ${n}. Initiating graceful shutdown...`);
|
|
8950
8965
|
try {
|
|
8951
|
-
|
|
8966
|
+
ms(), 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);
|
|
8952
8967
|
} catch (e) {
|
|
8953
8968
|
console.error("❌ Error during graceful shutdown:", e.message), process.exit(1);
|
|
8954
8969
|
}
|
|
@@ -8958,7 +8973,7 @@ async function os(e) {
|
|
|
8958
8973
|
}
|
|
8959
8974
|
//#endregion
|
|
8960
8975
|
//#region src/plugins/manager.ts
|
|
8961
|
-
var
|
|
8976
|
+
var ls = class {
|
|
8962
8977
|
plugins;
|
|
8963
8978
|
constructor(e = []) {
|
|
8964
8979
|
this.plugins = e;
|
|
@@ -8980,38 +8995,38 @@ var ss = class {
|
|
|
8980
8995
|
console.error(`[Plugin Error] ${n.name}.onWorkflowFinish:`, e);
|
|
8981
8996
|
}
|
|
8982
8997
|
}
|
|
8983
|
-
},
|
|
8984
|
-
function
|
|
8985
|
-
|
|
8998
|
+
}, us = { isStopping: !1 }, ds = /* @__PURE__ */ new Set(), fs = null, ps = null;
|
|
8999
|
+
function ms() {
|
|
9000
|
+
us.isStopping = !0, fs?.abort(), ps?.();
|
|
8986
9001
|
}
|
|
8987
|
-
function
|
|
8988
|
-
|
|
8989
|
-
let i = [
|
|
8990
|
-
return
|
|
9002
|
+
function hs(e, t, n, r) {
|
|
9003
|
+
us.isStopping = !1;
|
|
9004
|
+
let i = [gs(e, t, n, r)];
|
|
9005
|
+
return cs(i), i;
|
|
8991
9006
|
}
|
|
8992
|
-
async function
|
|
8993
|
-
let i = await
|
|
9007
|
+
async function gs(e, t, n, r) {
|
|
9008
|
+
let i = await ss(), a = new $o(), o = /* @__PURE__ */ new Set(), s = 0, c = null, l = () => {
|
|
8994
9009
|
s++, c?.();
|
|
8995
9010
|
};
|
|
8996
|
-
|
|
8997
|
-
let u =
|
|
8998
|
-
for (console.log(`🚀 Worker scheduler started. Driver: ${i.name}. Concurrency: ${e}. Tags: ${r.tags.join(", ") || "(none)"}`); !
|
|
9011
|
+
ps = l, fs = new AbortController();
|
|
9012
|
+
let u = _s(r, fs.signal, l);
|
|
9013
|
+
for (console.log(`🚀 Worker scheduler started. Driver: ${i.name}. Concurrency: ${e}. Tags: ${r.tags.join(", ") || "(none)"}`); !us.isStopping;) {
|
|
8999
9014
|
let n = s;
|
|
9000
9015
|
try {
|
|
9001
|
-
for (; o.size < e && !
|
|
9016
|
+
for (; o.size < e && !us.isStopping;) {
|
|
9002
9017
|
let e = await t.claimNextJob(r.tags);
|
|
9003
9018
|
if (!e) break;
|
|
9004
|
-
if (
|
|
9019
|
+
if (us.isStopping) {
|
|
9005
9020
|
await t.releaseJob(e.id);
|
|
9006
9021
|
break;
|
|
9007
9022
|
}
|
|
9008
9023
|
let n = process.env.WORKER_NAME || "cli", s;
|
|
9009
9024
|
s = (async () => {
|
|
9010
|
-
|
|
9025
|
+
bs(r, e.id);
|
|
9011
9026
|
let o = await a.getRevision(e.workflow_id, e.workflow_revision);
|
|
9012
9027
|
if (!o) throw await t.finishJob(e.id, "failed"), Error(`Workflow ${e.workflow_id} revision ${e.workflow_revision} was not found`);
|
|
9013
9028
|
let s = new le();
|
|
9014
|
-
s.replace(await
|
|
9029
|
+
s.replace(await xs(r, n, e.id)), await Ss({
|
|
9015
9030
|
workerId: n,
|
|
9016
9031
|
job: e,
|
|
9017
9032
|
queue: t,
|
|
@@ -9021,17 +9036,17 @@ async function ms(e, t, n, r) {
|
|
|
9021
9036
|
workflow: o
|
|
9022
9037
|
});
|
|
9023
9038
|
})().catch((e) => console.error(`[${n}] ⚠️ Worker execution error:`, e)).finally(() => {
|
|
9024
|
-
o.delete(s),
|
|
9039
|
+
o.delete(s), bs(r, e.id), l();
|
|
9025
9040
|
}), o.add(s);
|
|
9026
9041
|
}
|
|
9027
9042
|
} catch (e) {
|
|
9028
9043
|
console.error("⚠️ Worker scheduler claim error:", e), setTimeout(l, 5e3).unref();
|
|
9029
9044
|
}
|
|
9030
|
-
!
|
|
9045
|
+
!us.isStopping && s === n && (await vs(n, () => s, (e) => c = e), c = null);
|
|
9031
9046
|
}
|
|
9032
|
-
|
|
9047
|
+
fs.abort(), await Promise.allSettled(o), await u, ps = null, console.log("🛑 Worker scheduler stopped cleanly.");
|
|
9033
9048
|
}
|
|
9034
|
-
async function
|
|
9049
|
+
async function _s(e, t, n) {
|
|
9035
9050
|
let r = 1e3;
|
|
9036
9051
|
for (; !t.aborted;) {
|
|
9037
9052
|
try {
|
|
@@ -9042,10 +9057,10 @@ async function hs(e, t, n) {
|
|
|
9042
9057
|
if (t.aborted || e?.name === "AbortError") break;
|
|
9043
9058
|
console.error(`⚠️ Worker event stream disconnected: ${e.message}`);
|
|
9044
9059
|
}
|
|
9045
|
-
await
|
|
9060
|
+
await ys(r, t), r = Math.min(r * 2, 3e4);
|
|
9046
9061
|
}
|
|
9047
9062
|
}
|
|
9048
|
-
function
|
|
9063
|
+
function vs(e, t, n) {
|
|
9049
9064
|
return new Promise((r) => {
|
|
9050
9065
|
if (t() !== e) return r();
|
|
9051
9066
|
let i = setTimeout(r, 6e4);
|
|
@@ -9054,7 +9069,7 @@ function gs(e, t, n) {
|
|
|
9054
9069
|
});
|
|
9055
9070
|
});
|
|
9056
9071
|
}
|
|
9057
|
-
function
|
|
9072
|
+
function ys(e, t) {
|
|
9058
9073
|
return new Promise((n) => {
|
|
9059
9074
|
if (t.aborted) return n();
|
|
9060
9075
|
let r = () => {
|
|
@@ -9065,7 +9080,7 @@ function _s(e, t) {
|
|
|
9065
9080
|
t.addEventListener("abort", r, { once: !0 });
|
|
9066
9081
|
});
|
|
9067
9082
|
}
|
|
9068
|
-
async function
|
|
9083
|
+
async function bs(e, t) {
|
|
9069
9084
|
if (e.workerToken) try {
|
|
9070
9085
|
let n = await fetch(new URL("/api/events", e.serverUrl), {
|
|
9071
9086
|
method: "POST",
|
|
@@ -9081,7 +9096,7 @@ async function vs(e, t) {
|
|
|
9081
9096
|
Hn("Failed to publish job status event:", e);
|
|
9082
9097
|
}
|
|
9083
9098
|
}
|
|
9084
|
-
async function
|
|
9099
|
+
async function xs(e, t, n) {
|
|
9085
9100
|
if (!e.workerToken) return {};
|
|
9086
9101
|
try {
|
|
9087
9102
|
let r = await fetch(new URL(`/api/jobs/${n}/secrets`, e.serverUrl), {
|
|
@@ -9097,7 +9112,7 @@ async function ys(e, t, n) {
|
|
|
9097
9112
|
throw console.error(`Unable to retrieve secrets for job ${n}:`, e), e;
|
|
9098
9113
|
}
|
|
9099
9114
|
}
|
|
9100
|
-
async function
|
|
9115
|
+
async function Ss(e) {
|
|
9101
9116
|
let { workerId: t, job: n, config: r, secrets: a, queue: o } = e;
|
|
9102
9117
|
console.log(`\n[${t}] 📦 Claimed Job #${n.id} (Workflow: ${n.workflow_id})`);
|
|
9103
9118
|
let s = typeof n.payload == "string" ? JSON.parse(n.payload) : n.payload;
|
|
@@ -9117,27 +9132,27 @@ async function bs(e) {
|
|
|
9117
9132
|
durationMs: 0,
|
|
9118
9133
|
outputs: {},
|
|
9119
9134
|
logContent: ""
|
|
9120
|
-
})), g =
|
|
9121
|
-
await o.saveReport(n.id, g),
|
|
9122
|
-
let _ = new
|
|
9135
|
+
})), g = ks(n, "running", u, l, m.env, h, s);
|
|
9136
|
+
await o.saveReport(n.id, g), bs(r, n.id);
|
|
9137
|
+
let _ = new ls(r.plugins), v = {
|
|
9123
9138
|
jobId: String(n.id),
|
|
9124
9139
|
workflowName: n.workflow_id,
|
|
9125
9140
|
inputs: l,
|
|
9126
9141
|
runUrl: new URL(`/runs/${n.id}`, r.serverUrl).toString()
|
|
9127
9142
|
};
|
|
9128
9143
|
await _.triggerWorkflowStart(v);
|
|
9129
|
-
let { cancelled: y, failed: ee } = await
|
|
9144
|
+
let { cancelled: y, failed: ee } = await Cs({
|
|
9130
9145
|
payload: s,
|
|
9131
9146
|
steps: c,
|
|
9132
9147
|
executionContext: m,
|
|
9133
9148
|
...e
|
|
9134
9149
|
}, g), b = y ? "cancelled" : ee ? "failed" : "success";
|
|
9135
|
-
g.status = b, g.durationMs = Date.now() - u, g.finishedAt = (/* @__PURE__ */ new Date()).toISOString(), await o.completeJob(n.id, b, g),
|
|
9150
|
+
g.status = b, g.durationMs = Date.now() - u, g.finishedAt = (/* @__PURE__ */ new Date()).toISOString(), await o.completeJob(n.id, b, g), bs(r, n.id), console.log(`[${t}] ✅ Job #${n.id} completed as: ${b}`), await _.triggerWorkflowFinish(v, b);
|
|
9136
9151
|
}
|
|
9137
|
-
async function
|
|
9152
|
+
async function Cs(e, t) {
|
|
9138
9153
|
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;
|
|
9139
9154
|
try {
|
|
9140
|
-
e.workflow?.env && Object.assign(l.env, await
|
|
9155
|
+
e.workflow?.env && Object.assign(l.env, await Es(e.workflow.env, l));
|
|
9141
9156
|
for (let e = 0; e < c.length; e++) {
|
|
9142
9157
|
let s = c[e], m = (/* @__PURE__ */ new Date()).toISOString();
|
|
9143
9158
|
u[e] = {
|
|
@@ -9150,8 +9165,8 @@ async function xs(e, t) {
|
|
|
9150
9165
|
logContent: "",
|
|
9151
9166
|
startedAt: m,
|
|
9152
9167
|
finishedAt: void 0
|
|
9153
|
-
}, t.durationMs = Date.now() - Date.parse(t.startedAt), await i.saveReport(o.id, t),
|
|
9154
|
-
let h = await
|
|
9168
|
+
}, t.durationMs = Date.now() - Date.parse(t.startedAt), await i.saveReport(o.id, t), bs(a, o.id);
|
|
9169
|
+
let h = await ws({
|
|
9155
9170
|
workerId: r,
|
|
9156
9171
|
jobId: o.id,
|
|
9157
9172
|
step: s,
|
|
@@ -9178,7 +9193,7 @@ async function xs(e, t) {
|
|
|
9178
9193
|
exitCode: 1,
|
|
9179
9194
|
error: "Step failed before execution started",
|
|
9180
9195
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9181
|
-
}, p = e + 1, t.durationMs = Date.now() - Date.parse(t.startedAt), await i.saveReport(o.id, t),
|
|
9196
|
+
}, p = e + 1, t.durationMs = Date.now() - Date.parse(t.startedAt), await i.saveReport(o.id, t), bs(a, o.id), h.skipped) break;
|
|
9182
9197
|
if (h.cancelled) {
|
|
9183
9198
|
f = !0, d = !0;
|
|
9184
9199
|
break;
|
|
@@ -9191,13 +9206,13 @@ async function xs(e, t) {
|
|
|
9191
9206
|
} catch (e) {
|
|
9192
9207
|
console.log("🛑 Step failed", e), d = !0;
|
|
9193
9208
|
}
|
|
9194
|
-
return p < c.length && (
|
|
9209
|
+
return p < c.length && (Os(c, p, u), t.durationMs = Date.now() - Date.parse(t.startedAt), await i.saveReport(o.id, t), bs(a, o.id)), {
|
|
9195
9210
|
failed: d,
|
|
9196
9211
|
cancelled: f,
|
|
9197
9212
|
stepReports: u
|
|
9198
9213
|
};
|
|
9199
9214
|
}
|
|
9200
|
-
async function
|
|
9215
|
+
async function ws(e) {
|
|
9201
9216
|
let { step: t, stepIndex: n, executionContext: r } = e;
|
|
9202
9217
|
if (t.id ||= `step-${n}`, t.name ||= t.id, t.if && !await Gn.evaluateConditions(t.if, r)) return Hn(`⏩ Skipped step ${t.id} based on condition: ${t.if}`, r), {
|
|
9203
9218
|
failed: !1,
|
|
@@ -9206,7 +9221,7 @@ async function Ss(e) {
|
|
|
9206
9221
|
report: null
|
|
9207
9222
|
};
|
|
9208
9223
|
try {
|
|
9209
|
-
let n = await
|
|
9224
|
+
let n = await Es(t.env, r), i = {
|
|
9210
9225
|
jobId: String(e.jobId),
|
|
9211
9226
|
step: t,
|
|
9212
9227
|
command: t.run,
|
|
@@ -9220,10 +9235,10 @@ async function Ss(e) {
|
|
|
9220
9235
|
WORKING_DIR: r.workingDir
|
|
9221
9236
|
}
|
|
9222
9237
|
};
|
|
9223
|
-
return t.eval ?
|
|
9238
|
+
return t.eval ? Ts({
|
|
9224
9239
|
...e,
|
|
9225
9240
|
stepContext: i
|
|
9226
|
-
}) :
|
|
9241
|
+
}) : Ds({
|
|
9227
9242
|
...e,
|
|
9228
9243
|
stepContext: i
|
|
9229
9244
|
});
|
|
@@ -9236,7 +9251,7 @@ async function Ss(e) {
|
|
|
9236
9251
|
};
|
|
9237
9252
|
}
|
|
9238
9253
|
}
|
|
9239
|
-
async function
|
|
9254
|
+
async function Ts(e) {
|
|
9240
9255
|
let { queue: t, stepContext: n, executionContext: r } = e, { jobId: i, step: a } = n, o = Date.now(), s = a.id, c = a.name;
|
|
9241
9256
|
try {
|
|
9242
9257
|
let e = await Gn.evaluateExpression(a.eval, r);
|
|
@@ -9282,12 +9297,12 @@ async function Cs(e) {
|
|
|
9282
9297
|
};
|
|
9283
9298
|
}
|
|
9284
9299
|
}
|
|
9285
|
-
async function
|
|
9300
|
+
async function Es(e, t) {
|
|
9286
9301
|
let n = {};
|
|
9287
9302
|
if (e) for (let [r, i] of Object.entries(e)) n[r] = String(await Gn.evaluateValue(i, t));
|
|
9288
9303
|
return n;
|
|
9289
9304
|
}
|
|
9290
|
-
async function
|
|
9305
|
+
async function Ds(t) {
|
|
9291
9306
|
let { workerId: n, jobId: r, stepContext: i, executionContext: a, driver: o, queue: s } = t, { step: c } = i, l = c.id, u = c.name, d;
|
|
9292
9307
|
try {
|
|
9293
9308
|
d = o.execute(i);
|
|
@@ -9302,11 +9317,11 @@ async function Ts(t) {
|
|
|
9302
9317
|
logFilePath: ""
|
|
9303
9318
|
};
|
|
9304
9319
|
}
|
|
9305
|
-
|
|
9320
|
+
ds.add(d);
|
|
9306
9321
|
let f = !1, p = setInterval(async () => {
|
|
9307
9322
|
await s.isCancelled(r) && (console.log(`[${n}] 🛑 Job #${r} was cancelled! Halting execution.`), f = !0, clearInterval(p), await d.cancel());
|
|
9308
9323
|
}, 3e3), m = await d.done;
|
|
9309
|
-
if (clearInterval(p),
|
|
9324
|
+
if (clearInterval(p), ds.delete(d), d.logFilePath && e.existsSync(d.logFilePath)) try {
|
|
9310
9325
|
let e = await o.readLog(d.logFilePath);
|
|
9311
9326
|
await s.saveStepLog(r, l, e);
|
|
9312
9327
|
} catch (e) {
|
|
@@ -9333,7 +9348,7 @@ async function Ts(t) {
|
|
|
9333
9348
|
}
|
|
9334
9349
|
};
|
|
9335
9350
|
}
|
|
9336
|
-
function
|
|
9351
|
+
function Os(e, t, n) {
|
|
9337
9352
|
for (let r = t; r < e.length; r++) {
|
|
9338
9353
|
let t = e[r], i = t.id || `step-${r}`;
|
|
9339
9354
|
n[r] = {
|
|
@@ -9347,7 +9362,7 @@ function Es(e, t, n) {
|
|
|
9347
9362
|
};
|
|
9348
9363
|
}
|
|
9349
9364
|
}
|
|
9350
|
-
function
|
|
9365
|
+
function ks(e, t, n, r, i, a, o) {
|
|
9351
9366
|
return {
|
|
9352
9367
|
jobId: String(e.id),
|
|
9353
9368
|
parentId: String(e.parentId || ""),
|
|
@@ -9368,7 +9383,7 @@ function Ds(e, t, n, r, i, a, o) {
|
|
|
9368
9383
|
}
|
|
9369
9384
|
//#endregion
|
|
9370
9385
|
//#region src/scheduler.ts
|
|
9371
|
-
function
|
|
9386
|
+
function As(e, t, n, r) {
|
|
9372
9387
|
return e.split(",").some((e) => {
|
|
9373
9388
|
let [i, a] = e.split("/"), o = a ? Number(a) : 1;
|
|
9374
9389
|
if (!Number.isInteger(o) || o < 1) return !1;
|
|
@@ -9376,7 +9391,7 @@ function Os(e, t, n, r) {
|
|
|
9376
9391
|
return Number.isInteger(l) && Number.isInteger(u) && t >= l && t <= u && (t - l) % o === 0;
|
|
9377
9392
|
});
|
|
9378
9393
|
}
|
|
9379
|
-
function
|
|
9394
|
+
function js(e, t, n = "UTC") {
|
|
9380
9395
|
let r = e.trim().split(/\s+/);
|
|
9381
9396
|
if (r.length !== 5) return !1;
|
|
9382
9397
|
let i = new Intl.DateTimeFormat("en-US", {
|
|
@@ -9398,16 +9413,16 @@ function ks(e, t, n = "UTC") {
|
|
|
9398
9413
|
"Thu",
|
|
9399
9414
|
"Fri",
|
|
9400
9415
|
"Sat"
|
|
9401
|
-
].indexOf(i.weekday), o =
|
|
9402
|
-
return
|
|
9416
|
+
].indexOf(i.weekday), o = As(r[2], Number(i.day), 1, 31), s = As(r[4].replace(/7/g, "0"), a, 0, 6);
|
|
9417
|
+
return As(r[0], Number(i.minute), 0, 59) && As(r[1], Number(i.hour), 0, 23) && As(r[3], Number(i.month), 1, 12) && (r[2] === "*" || r[4] === "*" ? o && s : o || s);
|
|
9403
9418
|
}
|
|
9404
|
-
function
|
|
9419
|
+
function Ms(e) {
|
|
9405
9420
|
if (!e) return 0;
|
|
9406
9421
|
let t = e.match(/^([+-]?)(\d+)(m|h)$/);
|
|
9407
9422
|
if (!t) throw Error(`Invalid solar offset: ${e}`);
|
|
9408
9423
|
return (t[1] === "-" ? -1 : 1) * Number(t[2]) * (t[3] === "h" ? 36e5 : 6e4);
|
|
9409
9424
|
}
|
|
9410
|
-
function
|
|
9425
|
+
function Ns(e, t) {
|
|
9411
9426
|
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;
|
|
9412
9427
|
l = (l + 360) % 360;
|
|
9413
9428
|
let u = Math.atan(.91764 * Math.tan(l * Math.PI / 180)) * 180 / Math.PI;
|
|
@@ -9415,9 +9430,9 @@ function js(e, t) {
|
|
|
9415
9430
|
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));
|
|
9416
9431
|
if (p > 1 || p < -1) return null;
|
|
9417
9432
|
let m = (t.event === "sunrise" ? 360 - Math.acos(p) * 180 / Math.PI : Math.acos(p) * 180 / Math.PI) / 15;
|
|
9418
|
-
return m = (m + u - .06571 * s - 6.622 - o + 24) % 24, new Date(i + m * 36e5 +
|
|
9433
|
+
return m = (m + u - .06571 * s - 6.622 - o + 24) % 24, new Date(i + m * 36e5 + Ms(t.offset));
|
|
9419
9434
|
}
|
|
9420
|
-
var
|
|
9435
|
+
var Ps = class {
|
|
9421
9436
|
queue;
|
|
9422
9437
|
workflows;
|
|
9423
9438
|
config;
|
|
@@ -9434,9 +9449,9 @@ var Ms = class {
|
|
|
9434
9449
|
async tick(e = /* @__PURE__ */ new Date()) {
|
|
9435
9450
|
let t = /* @__PURE__ */ new Date(Math.floor(e.getTime() / 6e4) * 6e4);
|
|
9436
9451
|
for (let { definition: e, revision: n } of await this.workflows.published()) {
|
|
9437
|
-
for (let [r, i] of (e.schedule || []).entries())
|
|
9452
|
+
for (let [r, i] of (e.schedule || []).entries()) js(i.cron, t, i.timezone || "UTC") && await this.enqueue(e, n, i, `cron-${r}`, t);
|
|
9438
9453
|
for (let [r, i] of (e.solar || []).entries()) {
|
|
9439
|
-
let a =
|
|
9454
|
+
let a = Ns(t, i);
|
|
9440
9455
|
a && Math.floor(a.getTime() / 6e4) === t.getTime() / 6e4 && await this.enqueue(e, n, i, `solar-${r}`, t);
|
|
9441
9456
|
}
|
|
9442
9457
|
}
|
|
@@ -9452,7 +9467,7 @@ var Ms = class {
|
|
|
9452
9467
|
} } };
|
|
9453
9468
|
await this.queue.enqueue(e.id, t, o, e.tags), console.log(`Scheduled ${e.id} via ${a}`);
|
|
9454
9469
|
}
|
|
9455
|
-
},
|
|
9470
|
+
}, Fs = class {
|
|
9456
9471
|
name = "github-commit-status";
|
|
9457
9472
|
token;
|
|
9458
9473
|
apiUrl;
|
|
@@ -9494,13 +9509,13 @@ var Ms = class {
|
|
|
9494
9509
|
};
|
|
9495
9510
|
//#endregion
|
|
9496
9511
|
//#region src/index.ts
|
|
9497
|
-
async function
|
|
9512
|
+
async function Is() {
|
|
9498
9513
|
let { config: e, command: t } = await se();
|
|
9499
9514
|
e || process.exit(1);
|
|
9500
9515
|
let n = new le(), r = new ce(process.env.WORKER_NAME || "cli");
|
|
9501
9516
|
switch (t) {
|
|
9502
9517
|
case "start-server":
|
|
9503
|
-
console.log("🌐 Starting Ingress Gateway..."), await r.init(), await
|
|
9518
|
+
console.log("🌐 Starting Ingress Gateway..."), await r.init(), await ns.withPort({
|
|
9504
9519
|
config: e,
|
|
9505
9520
|
queue: r,
|
|
9506
9521
|
secrets: n,
|
|
@@ -9509,13 +9524,13 @@ async function Ps() {
|
|
|
9509
9524
|
});
|
|
9510
9525
|
break;
|
|
9511
9526
|
case "start-workers":
|
|
9512
|
-
console.log(`⚙️ Starting worker scheduler with ${e.workers} concurrent slot(s)...`), await r.init(),
|
|
9527
|
+
console.log(`⚙️ Starting worker scheduler with ${e.workers} concurrent slot(s)...`), await r.init(), hs(e.workers, r, n, e);
|
|
9513
9528
|
break;
|
|
9514
9529
|
case "start-scheduler": {
|
|
9515
9530
|
console.log("Starting workflow scheduler..."), await r.init();
|
|
9516
|
-
let t = new
|
|
9531
|
+
let t = new $o();
|
|
9517
9532
|
await t.init();
|
|
9518
|
-
let n = new
|
|
9533
|
+
let n = new Ps(r, t, e);
|
|
9519
9534
|
n.start();
|
|
9520
9535
|
let i = () => {
|
|
9521
9536
|
n.stop(), process.exit(0);
|
|
@@ -9526,6 +9541,6 @@ async function Ps() {
|
|
|
9526
9541
|
default: console.error(`❌ Unknown command: '${t}'`), oe(), process.exit(1);
|
|
9527
9542
|
}
|
|
9528
9543
|
}
|
|
9529
|
-
|
|
9544
|
+
Is().catch(console.error);
|
|
9530
9545
|
//#endregion
|
|
9531
|
-
export {
|
|
9546
|
+
export { Fs as GitHubStatusPlugin };
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAYA,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,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;IAYzC,oBAAoB,CAAC,YAAY,EAAE,mBAAmB;YAIxC,aAAa;IA8H3B;;OAEG;YACW,aAAa;YA0Bb,WAAW;YAwBX,UAAU;YAyBV,cAAc;IAuC5B,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,YAAY;IAmBpB,OAAO,CAAC,YAAY;YAUN,QAAQ;YAYR,wBAAwB;YAcxB,kBAAkB;YAOlB,iBAAiB;YAWjB,kBAAkB;YAoBlB,oBAAoB;YASpB,qBAAqB;YAWrB,gBAAgB;YAMhB,gBAAgB;YAiBhB,kBAAkB;YASlB,gBAAgB;IAc9B;;OAEG;YACW,kBAAkB;YAclB,iBAAiB;IAuB/B;;OAEG;YACW,eAAe;YAQf,mBAAmB;IAWjC;;OAEG;YACW,gBAAgB;YAuBhB,gBAAgB;YAehB,eAAe;YAkBf,cAAc;IAS5B,OAAO,CAAC,MAAM;YAMA,UAAU;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAYA,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,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;IAYzC,oBAAoB,CAAC,YAAY,EAAE,mBAAmB;YAIxC,aAAa;IA8H3B;;OAEG;YACW,aAAa;YA0Bb,WAAW;YAwBX,UAAU;YAyBV,cAAc;IAuC5B,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,YAAY;IAmBpB,OAAO,CAAC,YAAY;YAUN,QAAQ;YAYR,wBAAwB;YAcxB,kBAAkB;YAOlB,iBAAiB;YAWjB,kBAAkB;YAoBlB,oBAAoB;YASpB,qBAAqB;YAWrB,gBAAgB;YAMhB,gBAAgB;YAiBhB,kBAAkB;YASlB,gBAAgB;IAc9B;;OAEG;YACW,kBAAkB;YAclB,iBAAiB;IAuB/B;;OAEG;YACW,eAAe;YAQf,mBAAmB;IAWjC;;OAEG;YACW,gBAAgB;YAuBhB,gBAAgB;YAehB,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/workflows.d.ts
CHANGED
|
@@ -5,13 +5,14 @@ export interface StoredWorkflow {
|
|
|
5
5
|
sourceYaml: string;
|
|
6
6
|
revision: number;
|
|
7
7
|
status: 'draft' | 'published' | 'archived';
|
|
8
|
+
enabled: boolean;
|
|
8
9
|
}
|
|
9
10
|
/** Parses the portable, DB-stored YAML format. Includes are deliberately unsupported. */
|
|
10
11
|
export declare function parseWorkflow(sourceYaml: string): WorkflowDefinition[];
|
|
11
12
|
export declare class WorkflowRepository {
|
|
12
13
|
init(): Promise<void>;
|
|
13
14
|
validate(sourceYaml: string): WorkflowDefinition[];
|
|
14
|
-
saveDraft(id: string, sourceYaml: string): Promise<StoredWorkflow>;
|
|
15
|
+
saveDraft(id: string, sourceYaml: string, enabled?: boolean): Promise<StoredWorkflow>;
|
|
15
16
|
publish(id: string): Promise<StoredWorkflow | null>;
|
|
16
17
|
list(): Promise<StoredWorkflow[]>;
|
|
17
18
|
get(id: string): Promise<StoredWorkflow | null>;
|
package/dist/workflows.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflows.d.ts","sourceRoot":"","sources":["../src/workflows.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"workflows.d.ts","sourceRoot":"","sources":["../src/workflows.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEvE,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,GAAG,WAAW,GAAG,UAAU,CAAC;IAC3C,OAAO,EAAE,OAAO,CAAC;CAClB;AAQD,yFAAyF;AACzF,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAuBtE;AAED,qBAAa,kBAAkB;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAqB3B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,EAAE;IAa5C,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,UAAO,GAAG,OAAO,CAAC,cAAc,CAAC;IAWlF,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IASnD,IAAI,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAKjC,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAK/C,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IASpC,SAAS,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAMxC,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IAKrF,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAKvG"}
|