@cloud-cli/on 1.55.2 → 1.56.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.
@@ -1,4 +1,4 @@
1
- export declare const API_KEY_SCOPES: readonly ["workflows:read", "workflows:write", "logs:read", "artifacts:read", "runs:control"];
1
+ export declare const API_KEY_SCOPES: readonly ["workflows:read", "workflows:write", "logs:read", "artifacts:read", "runs:control", "runs:dispatch"];
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")[];
9
+ scopes: ("workflows:read" | "workflows:write" | "logs:read" | "artifacts:read" | "runs:control" | "runs:dispatch")[];
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,+FAAgG,CAAC;AAC5H,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"}
1
+ {"version":3,"file":"api-key-repository.d.ts","sourceRoot":"","sources":["../src/api-key-repository.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,cAAc,gHAAiH,CAAC;AAC7I,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
@@ -227,13 +227,15 @@ var ye = class extends l {
227
227
  await this.createTables(), await this.clearStaleJobs();
228
228
  }
229
229
  async enqueue(e, t, n, r = [], i) {
230
- return i && await C.run("UPDATE jobs SET status = 'cancelled' WHERE concurrency_key = ? AND status IN ('pending', 'running');", [i]), await C.run("INSERT INTO jobs (workflow_id, workflow_revision, required_tags, concurrency_key, payload) VALUES (?, ?, ?, ?, ?);", [
230
+ i && await C.run("UPDATE jobs SET status = 'cancelled' WHERE concurrency_key = ? AND status IN ('pending', 'running');", [i]);
231
+ let a = await C.run("INSERT INTO jobs (workflow_id, workflow_revision, required_tags, concurrency_key, payload) VALUES (?, ?, ?, ?, ?);", [
231
232
  e,
232
233
  t,
233
234
  JSON.stringify(r),
234
235
  i || "",
235
236
  JSON.stringify(n)
236
237
  ]);
238
+ return Number(a?.lastInsertRowid ?? a?.id ?? 0);
237
239
  }
238
240
  async claimNextJob(e = []) {
239
241
  return await C.get("\n UPDATE jobs\n SET\n status = 'running',\n worker_id = ?,\n started_at = CURRENT_TIMESTAMP\n WHERE id = (\n SELECT id FROM jobs\n WHERE status = 'pending'\n AND NOT EXISTS (\n SELECT 1\n FROM json_each(COALESCE(jobs.required_tags, '[]')) AS required_tag\n WHERE required_tag.value NOT IN (SELECT value FROM json_each(?))\n )\n ORDER BY created_at ASC\n LIMIT 1\n )\n RETURNING *;\n ", [this.workerId, JSON.stringify(e)]) || null;
@@ -5739,6 +5741,49 @@ var oa = {
5739
5741
  content: { "application/json": { schema: { $ref: "#/components/schemas/JobList" } } }
5740
5742
  } }
5741
5743
  } },
5744
+ "/api/dispatch/{trigger}": { post: {
5745
+ tags: ["jobs"],
5746
+ summary: "Dispatch a workflow trigger with manual inputs",
5747
+ security: [{ bearerAuth: [] }, { basicAuth: [] }],
5748
+ parameters: [{
5749
+ name: "trigger",
5750
+ in: "path",
5751
+ required: !0,
5752
+ schema: { type: "string" }
5753
+ }],
5754
+ requestBody: {
5755
+ required: !0,
5756
+ content: { "application/json": { schema: { type: "object" } } }
5757
+ },
5758
+ responses: {
5759
+ 202: {
5760
+ description: "Jobs queued",
5761
+ content: { "application/json": { schema: { type: "object" } } }
5762
+ },
5763
+ 401: { $ref: "#/components/responses/Unauthorized" }
5764
+ }
5765
+ } },
5766
+ "/api/jobs/{jobId}/wait": { get: {
5767
+ tags: ["jobs"],
5768
+ summary: "Wait for a job to reach a terminal state",
5769
+ parameters: [{ $ref: "#/components/parameters/JobId" }, {
5770
+ name: "timeout",
5771
+ in: "query",
5772
+ schema: {
5773
+ type: "integer",
5774
+ minimum: 0,
5775
+ maximum: 12e4,
5776
+ default: 3e4
5777
+ }
5778
+ }],
5779
+ responses: {
5780
+ 200: {
5781
+ description: "Latest job state",
5782
+ content: { "application/json": { schema: { type: "object" } } }
5783
+ },
5784
+ 404: { $ref: "#/components/responses/NotFound" }
5785
+ }
5786
+ } },
5742
5787
  "/api/jobs/{jobId}/cancel": { post: {
5743
5788
  tags: ["jobs"],
5744
5789
  summary: "Cancel an active job",
@@ -6262,7 +6307,8 @@ var oa = {
6262
6307
  "workflows:write",
6263
6308
  "logs:read",
6264
6309
  "artifacts:read",
6265
- "runs:control"
6310
+ "runs:control",
6311
+ "runs:dispatch"
6266
6312
  ]
6267
6313
  }
6268
6314
  }
@@ -6293,7 +6339,8 @@ var oa = {
6293
6339
  "workflows:write",
6294
6340
  "logs:read",
6295
6341
  "artifacts:read",
6296
- "runs:control"
6342
+ "runs:control",
6343
+ "runs:dispatch"
6297
6344
  ], ca = class {
6298
6345
  async init() {
6299
6346
  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 )");
@@ -6328,7 +6375,7 @@ var oa = {
6328
6375
  let t = await C.run("UPDATE api_keys SET revoked_at = CURRENT_TIMESTAMP WHERE id = ? AND revoked_at IS NULL", [e]);
6329
6376
  return Number(t?.changes || 0) > 0;
6330
6377
  }
6331
- }, 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 ]);\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";
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";
6332
6379
  //#endregion
6333
6380
  //#region src/settings-ui.ts
6334
6381
  function ua(e = "tokens") {
@@ -20859,23 +20906,27 @@ var SC = class {
20859
20906
  let e = n.searchParams.get("afterId"), r = n.searchParams.get("beforeId"), i = n.searchParams.get("limit"), a = n.searchParams.get("filter")?.trim() || void 0, o = n.searchParams.get("workflowId")?.trim() || void 0, s = e === null ? void 0 : Number(e), c = r === null ? void 0 : Number(r), l = i === null ? CC : Number(i);
20860
20907
  return s !== void 0 && (!Number.isSafeInteger(s) || s < 0) ? (t.writeHead(400, { "Content-Type": "application/json; charset=utf-8" }), t.end(JSON.stringify({ error: "afterId must be a non-negative integer" }))) : c !== void 0 && (!Number.isSafeInteger(c) || c < 1) ? (t.writeHead(400, { "Content-Type": "application/json; charset=utf-8" }), t.end(JSON.stringify({ error: "beforeId must be a positive integer" }))) : !Number.isSafeInteger(l) || l < 1 || l > wC ? (t.writeHead(400, { "Content-Type": "application/json; charset=utf-8" }), t.end(JSON.stringify({ error: `limit must be an integer from 1 to ${wC}` }))) : a && a.length > 200 ? (t.writeHead(400, { "Content-Type": "application/json; charset=utf-8" }), t.end(JSON.stringify({ error: "filter must be at most 200 characters" }))) : this.renderDashboardJobs(t, l, s, c, a, o);
20861
20908
  }
20909
+ let s = n.pathname.match(/^\/api\/dispatch\/([a-z0-9-]+)$/);
20910
+ if (e.method === "POST" && s) return this.handleDispatch(e, t, s[1]);
20911
+ let c = n.pathname.match(/^\/api\/jobs\/(\d+)\/wait$/);
20912
+ if (e.method === "GET" && c) return this.handleJobWait(c[1], n.searchParams.get("timeout"), t);
20862
20913
  if (e.method === "GET" && n.pathname === "/api/events") return this.events.subscribe(e, t);
20863
20914
  if (e.method === "GET" && n.pathname === "/api/push/public-key") return this.handlePushPublicKey(t);
20864
20915
  if (e.method === "POST" && n.pathname === "/api/push/subscriptions") return this.handlePushSubscribe(e, t);
20865
20916
  if (e.method === "DELETE" && n.pathname === "/api/push/subscriptions") return this.handlePushUnsubscribe(e, t);
20866
20917
  if (n.pathname === "/api/workflows/validate" && e.method === "POST") return this.handleWorkflowValidation(e, t);
20867
20918
  if (n.pathname === "/api/secrets" && e.method === "GET") return this.handleSecretList(e, t);
20868
- let s = n.pathname.match(/^\/api\/secrets\/([A-Z][A-Z0-9_]*)$/);
20869
- if (s && e.method === "PUT") return this.handleSecretSave(e, t, s[1]);
20870
- if (s && e.method === "DELETE") return this.handleSecretDelete(e, t, s[1]);
20871
- let c = n.pathname.match(/^\/api\/jobs\/(\d+)\/secrets$/);
20872
- if (c && e.method === "GET") return this.handleJobSecrets(e, t, c[1]);
20873
- let l = n.pathname.match(/^\/api\/jobs\/(\d+)\/cancel$/);
20874
- if (l && e.method === "POST") return this.handleCancelJob(e, t, l[1]);
20919
+ let l = n.pathname.match(/^\/api\/secrets\/([A-Z][A-Z0-9_]*)$/);
20920
+ if (l && e.method === "PUT") return this.handleSecretSave(e, t, l[1]);
20921
+ if (l && e.method === "DELETE") return this.handleSecretDelete(e, t, l[1]);
20922
+ let u = n.pathname.match(/^\/api\/jobs\/(\d+)\/secrets$/);
20923
+ if (u && e.method === "GET") return this.handleJobSecrets(e, t, u[1]);
20924
+ let d = n.pathname.match(/^\/api\/jobs\/(\d+)\/cancel$/);
20925
+ if (d && e.method === "POST") return this.handleCancelJob(e, t, d[1]);
20875
20926
  if (n.pathname === "/api/workflows" && e.method === "GET") return this.handleWorkflowList(e, t);
20876
- let u = n.pathname.match(/^\/api\/workflows\/([a-z0-9-]+)(\/publish)?$/);
20877
- if (u) {
20878
- let [, r, i] = u;
20927
+ let p = n.pathname.match(/^\/api\/workflows\/([a-z0-9-]+)(\/publish)?$/);
20928
+ if (p) {
20929
+ let [, r, i] = p;
20879
20930
  if (e.method === "GET" && !i) return this.handleWorkflowGet(e, t, r, n.searchParams.get("revision"));
20880
20931
  if (e.method === "PUT" && !i) return this.handleWorkflowSave(e, t, r);
20881
20932
  if (e.method === "DELETE" && !i) return this.handleWorkflowDelete(e, t, r);
@@ -20886,13 +20937,15 @@ var SC = class {
20886
20937
  let r = n.pathname.replace("/runs/", "");
20887
20938
  return this.renderRunDetails(r, t, "html", await this.hasScope(e, "logs:read"));
20888
20939
  }
20889
- let d = n.pathname.match(/^\/api\/runs\/(\d+)\/ai-help$/);
20890
- if (e.method === "POST" && d) return this.handleAiHelp(e, t, d[1]);
20940
+ let m = n.pathname.match(/^\/api\/runs\/(\d+)\/ai-help$/);
20941
+ if (e.method === "POST" && m) return this.handleAiHelp(e, t, m[1]);
20891
20942
  if (e.method === "GET" && n.pathname.startsWith("/api/runs/")) {
20892
- let r = n.pathname.match(/^\/api\/runs\/(\d+)\/artifacts\/(.+)$/);
20893
- if (r) return this.handleArtifactDownload(e, t, r[1], decodeURIComponent(r[2]));
20894
- let i = n.pathname.replace("/api/runs/", "");
20895
- return this.renderRunDetails(i, t, "json", await this.hasScope(e, "logs:read"));
20943
+ let r = n.pathname.match(/^\/api\/runs\/(\d+)\/diagnostics$/);
20944
+ if (r) return this.handleDiagnostics(e, t, r[1]);
20945
+ let i = n.pathname.match(/^\/api\/runs\/(\d+)\/artifacts\/(.+)$/);
20946
+ if (i) return this.handleArtifactDownload(e, t, i[1], decodeURIComponent(i[2]));
20947
+ let a = n.pathname.replace("/api/runs/", "");
20948
+ return this.renderRunDetails(a, t, "json", await this.hasScope(e, "logs:read"));
20896
20949
  }
20897
20950
  if (e.method === "POST" && n.pathname.startsWith("/restart/")) {
20898
20951
  let r = n.pathname.replace("/restart/", "");
@@ -20922,6 +20975,62 @@ var SC = class {
20922
20975
  }));
20923
20976
  }
20924
20977
  }
20978
+ async handleDispatch(e, t, n) {
20979
+ if (!await this.requireScope(e, t, "runs:dispatch")) return;
20980
+ let r = await this.readJson(e, t);
20981
+ if (!r || typeof r != "object" || Array.isArray(r)) return;
20982
+ let i = await this.matchWorkflows(n, r, await this.workflows.published(), await this.currentSecrets());
20983
+ t.writeHead(202, { "Content-Type": "application/json; charset=utf-8" }), t.end(JSON.stringify({ jobs: i.map((e) => ({
20984
+ id: e,
20985
+ provider: n
20986
+ })) }));
20987
+ }
20988
+ async handleJobWait(e, t, n) {
20989
+ let r = Math.min(12e4, Math.max(0, Number(t || 3e4))), i = Date.now(), a;
20990
+ do {
20991
+ if (a = await this.queue.getJob(e), !a || [
20992
+ "success",
20993
+ "failed",
20994
+ "cancelled"
20995
+ ].includes(a.status) || Date.now() - i >= r) break;
20996
+ await new Promise((e) => setTimeout(e, 500));
20997
+ } while (1);
20998
+ return a ? (n.writeHead(200, {
20999
+ "Cache-Control": "no-store",
21000
+ "Content-Type": "application/json; charset=utf-8"
21001
+ }), n.end(JSON.stringify({
21002
+ job: we([a])[0],
21003
+ terminal: [
21004
+ "success",
21005
+ "failed",
21006
+ "cancelled"
21007
+ ].includes(a.status)
21008
+ }))) : (n.writeHead(404, { "Content-Type": "application/json" }), n.end(JSON.stringify({ error: "Job not found" })));
21009
+ }
21010
+ async handleDiagnostics(e, t, n) {
21011
+ if (!await this.hasScope(e, "logs:read")) return this.requireScope(e, t, "logs:read");
21012
+ let r = await this.queue.getJob(n);
21013
+ if (!r) return t.writeHead(404, { "Content-Type": "application/json" }), t.end(JSON.stringify({ error: "Run not found" }));
21014
+ let i = r.report ? JSON.parse(r.report) : null, a = await this.workflows.getRevision(r.workflow_id, r.workflow_revision), o = await this.workflows.getRevisionSnapshot(r.workflow_id, r.workflow_revision), s = await this.queue.getJobLogs(n), c = i?.steps?.findIndex((e) => e.status === "failed") ?? -1;
21015
+ return t.writeHead(200, {
21016
+ "Cache-Control": "no-store",
21017
+ "Content-Type": "application/json; charset=utf-8"
21018
+ }), t.end(JSON.stringify({
21019
+ jobId: String(r.id),
21020
+ workflow: {
21021
+ id: r.workflow_id,
21022
+ revision: r.workflow_revision,
21023
+ sourceYaml: o?.sourceYaml
21024
+ },
21025
+ status: r.status,
21026
+ inputs: i?.inputs || {},
21027
+ failedStep: c >= 0 ? i.steps[c] : null,
21028
+ steps: c >= 0 ? i.steps.slice(0, c + 1) : i?.steps || [],
21029
+ logs: s,
21030
+ artifacts: i?.artifacts || [],
21031
+ workflowFound: !!a
21032
+ }));
21033
+ }
20925
21034
  async readRequest(e, t) {
20926
21035
  let n = [], r = 0;
20927
21036
  for await (let i of e) {
@@ -20967,36 +21076,38 @@ var SC = class {
20967
21076
  }
20968
21077
  }
20969
21078
  async matchWorkflows(e, t, n, r = {}) {
20970
- for (let { definition: i, revision: a } of n) {
20971
- if (i.on.provider !== e) continue;
21079
+ let i = [];
21080
+ for (let { definition: a, revision: o } of n) {
21081
+ if (a.on.provider !== e) continue;
20972
21082
  let n = this.preprocessors.get(e);
20973
- if (n?.filter && !n.filter(t, i.on).isValid) {
20974
- dr(`⏩ Skipped ${i.id} based on ${e} webhook filters`, { inputs: t });
21083
+ if (n?.filter && !n.filter(t, a.on).isValid) {
21084
+ dr(`⏩ Skipped ${a.id} based on ${e} webhook filters`, { inputs: t });
20975
21085
  continue;
20976
21086
  }
20977
- if (i.on.if) try {
21087
+ if (a.on.if) try {
20978
21088
  let e = n?.conditionContext?.(t, r) || {};
20979
- if (!await mr.evaluateConditions(i.on.if, {
21089
+ if (!await mr.evaluateConditions(a.on.if, {
20980
21090
  inputs: t,
20981
21091
  ...e
20982
21092
  })) {
20983
- dr(`⏩ Skipped ${i.id} based on conditions: ${i.on.if}`, { inputs: t });
21093
+ dr(`⏩ Skipped ${a.id} based on conditions: ${a.on.if}`, { inputs: t });
20984
21094
  continue;
20985
21095
  }
20986
21096
  } catch (e) {
20987
- console.error(`⚠️ Condition evaluation error in workflow [${i.id}]:`, e.message);
21097
+ console.error(`⚠️ Condition evaluation error in workflow [${a.id}]:`, e.message);
20988
21098
  continue;
20989
21099
  }
20990
- for (let e of hr(i)) {
21100
+ for (let e of hr(a)) {
20991
21101
  let n = await gr(e.tags, e.matrixContext, t), r = "";
20992
21102
  e.concurrency?.group && (r = await mr.evaluateValue(e.concurrency.group, { inputs: t }));
20993
- let o = {
21103
+ let s = {
20994
21104
  inputs: t,
20995
21105
  matrix: e.matrixContext
20996
- };
20997
- await this.queue.enqueue(i.id, a, o, n, r), this.events.publish("jobs.available", { tags: n });
21106
+ }, c = await this.queue.enqueue(a.id, o, s, n, r);
21107
+ c && i.push(c), this.events.publish("jobs.available", { tags: n });
20998
21108
  }
20999
21109
  }
21110
+ return i;
21000
21111
  }
21001
21112
  isAdmin(e) {
21002
21113
  return this.matchesToken(e, this.adminToken, !0);
package/dist/queue.d.ts CHANGED
@@ -8,7 +8,7 @@ export declare class QueueManager {
8
8
  * Enqueues a new job into the database.
9
9
  * Includes simple GitHub-style concurrency cancellation.
10
10
  */
11
- enqueue(workflowId: string, workflowRevision: number, payload: JobPayload, requiredTags?: string[], concurrencyKey?: string): Promise<any>;
11
+ enqueue(workflowId: string, workflowRevision: number, payload: JobPayload, requiredTags?: string[], concurrencyKey?: string): Promise<number>;
12
12
  /**
13
13
  * Atomically claims the oldest pending job compatible with this worker.
14
14
  * Requires SQLite >= 3.35 for the RETURNING clause.
package/dist/server.d.ts CHANGED
@@ -22,6 +22,9 @@ export declare class WebhookServer {
22
22
  * Processes incoming HTTP webhooks
23
23
  */
24
24
  private handleWebhook;
25
+ private handleDispatch;
26
+ private handleJobWait;
27
+ private handleDiagnostics;
25
28
  private readRequest;
26
29
  private preprocess;
27
30
  private matchWorkflows;
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAuBA,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;IAyO3B;;OAEG;YACW,aAAa;YA0Bb,WAAW;YAwBX,UAAU;YAyBV,cAAc;IA2C5B,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,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"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAuBA,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;IAgP3B;;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,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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloud-cli/on",
3
- "version": "1.55.2",
3
+ "version": "1.56.0",
4
4
  "description": "CLI entry point for the on plugin ecosystem.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@12.3.4",