@cloud-cli/on 1.22.1 → 1.23.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/on.js CHANGED
@@ -173,11 +173,18 @@ var ce = class {
173
173
  async getJob(e) {
174
174
  return C.get("SELECT * FROM jobs WHERE id = ?", [e]);
175
175
  }
176
- async listJobs(e = 50, t, n) {
177
- let r = [], i = [];
178
- t !== void 0 && (r.push("id > ?"), i.push(t)), n !== void 0 && (r.push("id < ?"), i.push(n));
179
- let a = r.length ? ` WHERE ${r.join(" AND ")}` : "";
180
- return C.all(`SELECT * FROM jobs${a} ORDER BY id DESC LIMIT ?;`, [...i, e]);
176
+ async listJobs(e = 50, t, n, r) {
177
+ let i = [], a = [];
178
+ if (t !== void 0 && (i.push("id > ?"), a.push(t)), n !== void 0 && (i.push("id < ?"), a.push(n)), r) {
179
+ let e = r.indexOf(":");
180
+ if (e > 0) {
181
+ let t = r.slice(0, e).trim(), n = r.slice(e + 1).trim();
182
+ if (!/^[A-Za-z0-9_-]+$/.test(t) || !n) throw Error("Invalid job filter");
183
+ i.push("json_extract(payload, '$.inputs.' || ?) GLOB ?"), a.push(t, n);
184
+ } else i.push("LOWER(payload) LIKE LOWER(?)"), a.push(`%${r}%`);
185
+ }
186
+ let o = i.length ? ` WHERE ${i.join(" AND ")}` : "";
187
+ return C.all(`SELECT * FROM jobs${o} ORDER BY id DESC LIMIT ?;`, [...a, e]);
181
188
  }
182
189
  async saveReport(e, t) {
183
190
  await C.run("UPDATE jobs SET report = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?", [JSON.stringify(t), e]);
@@ -214,7 +221,7 @@ var ce = class {
214
221
  for (let n of t) e = e.replaceAll(n, "****");
215
222
  return e;
216
223
  }
217
- }, ue = "<!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 <link rel=\"component\" href=\"https://sodium.static.apphor.de/lucide-icon.html\" />\n <title>Workflow Engine Dashboard</title>\n <script type=\"importmap\">\n { \"imports\": { \"@li3/\": \"https://cdn.li3.dev/@li3/\" } }\n <\/script>\n </head>\n <body class=\"bg-gray-950 text-gray-100 min-h-screen p-4 md:p-6 font-sans\">\n <template app>\n <main class=\"max-w-6xl mx-auto space-y-6\">\n <header class=\"flex flex-col sm:flex-row sm:items-center justify-between gap-3 border-b border-gray-800 pb-4\">\n <div>\n <h1 class=\"text-2xl font-bold text-white\">Runner Engine Status</h1>\n <p class=\"text-xs text-gray-400\">Live Job Queue &amp; Execution Traces</p>\n </div>\n <div class=\"flex flex-wrap items-center gap-3 text-xs font-mono\">\n <a href=\"/workflows\" class=\"text-indigo-400 hover:text-indigo-300 hover:underline\">Manage workflows</a>\n <template if=\"notificationsSupported\">\n <button\n class=\"inline-flex h-9 w-9 items-center justify-center rounded-full border\"\n bind-class=\"notificationsEnabled ? 'border-emerald-500/30.bg-emerald-500/10.text-emerald-300' : 'border-indigo-500/40.bg-indigo-500/10.text-indigo-300'\"\n on-click=\"toggleNotifications()\"\n >\n <lucide-icon bind-icon=\"notificationsEnabled ? 'bell-ring' : 'bell-off'\" size=\"16\"></lucide-icon>\n <span class=\"sr-only\">{{ notificationsEnabled ? 'Notifications on' : 'Enable notifications' }}</span>\n </button>\n </template>\n <span class=\"text-gray-500\">Updated {{ lastUpdated }}</span>\n <template if=\"refreshError\">\n <span class=\"rounded border border-rose-500/20 bg-rose-500/10 px-3 py-1 text-rose-400\"\n >Refresh failed</span\n >\n </template>\n </div>\n </header>\n\n <template if=\"notificationNotice\">\n <button\n class=\"w-full rounded-lg border border-gray-700 bg-gray-900 px-4 py-3 text-left text-sm text-gray-300\"\n on-click=\"notificationNotice = ''\"\n >\n {{ notificationNotice }}\n </button>\n </template>\n\n <section class=\"bg-gray-900 border border-gray-800 rounded-xl overflow-hidden hidden md:block\">\n <table class=\"w-full text-left text-sm\">\n <thead class=\"bg-gray-800/50 text-gray-400 text-xs uppercase font-mono border-b border-gray-800\">\n <tr>\n <th class=\"py-3 px-4\">Job ID</th>\n <th class=\"py-3 px-4\">Workflow</th>\n <th class=\"py-3 px-4\">Status</th>\n <th class=\"py-3 px-4 hidden xl:table-cell\">Worker</th>\n <th class=\"py-3 px-4 hidden lg:table-cell\">Created At</th>\n <th class=\"py-3 px-4 text-right\">Action</th>\n </tr>\n </thead>\n <tbody>\n <template for=\"job of jobs\">\n <tr class=\"border-b border-gray-800 hover:bg-gray-800/40 transition\">\n <td class=\"py-3 px-4 font-mono text-indigo-400\">\n <a attr-href=\"'/runs/' + job.id\" class=\"hover:underline\">#{{ job.id }}</a>\n </td>\n <td class=\"py-3 px-4 font-medium text-white\">{{ job.workflowId }}</td>\n <td class=\"py-3 px-4\">\n <span\n class=\"px-2.5 py-0.5 rounded-full text-xs font-semibold\"\n bind-class=\"{\n 'bg-emerald-500/10.text-emerald-400': job.status === 'success',\n 'bg-rose-500/10.text-rose-400': job.status === 'failed',\n 'bg-indigo-500/10.text-indigo-400.animate-pulse': job.status === 'running',\n 'bg-gray-500/10.text-gray-400': !['success', 'failed', 'running'].includes(job.status)\n }\"\n >\n {{ upper(job.status) }}\n </span>\n </td>\n <td class=\"py-3 px-4 hidden xl:table-cell text-xs font-mono text-gray-400\">\n {{ job.workerId || '-' }}\n </td>\n <td class=\"py-3 px-4 hidden lg:table-cell text-xs text-gray-400\">{{ job.createdAt }}</td>\n <td class=\"py-3 px-4 text-right whitespace-nowrap\">\n <a\n attr-href=\"'/runs/' + job.id\"\n class=\"text-xs bg-gray-800 hover:bg-gray-700 text-gray-200 px-3 py-1 rounded border border-gray-700\"\n >View Trace</a\n >\n </td>\n </tr>\n </template>\n </tbody>\n </table>\n <template if=\"!jobs.length\">\n <div class=\"p-8 text-center text-gray-500\">No jobs recorded yet.</div>\n </template>\n </section>\n\n <section\n class=\"md:hidden bg-gray-900 border border-gray-800 rounded-xl divide-y divide-gray-800 overflow-hidden\"\n >\n <template for=\"job of jobs\">\n <a\n attr-href=\"'/runs/' + job.id\"\n class=\"block p-4 bg-gray-900/60 hover:bg-gray-800/40 focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-indigo-400\"\n >\n <div class=\"flex items-start justify-between gap-3\">\n <div class=\"min-w-0\">\n <div class=\"flex items-center gap-2\">\n <span\n class=\"w-2.5 h-2.5 rounded-full shrink-0\"\n bind-class=\"{\n 'bg-emerald-400': job.status === 'success',\n 'bg-rose-400': job.status === 'failed',\n 'bg-indigo-400.animate-pulse': job.status === 'running',\n 'bg-gray-500': !['success', 'failed', 'running'].includes(job.status)\n }\"\n ></span>\n <h2 class=\"font-medium text-white truncate\">{{ job.workflowId }}</h2>\n </div>\n <p class=\"mt-1 pl-[18px] text-xs font-mono text-gray-400\">#{{ job.id }} · {{ upper(job.status) }}</p>\n </div>\n </div>\n </a>\n </template>\n <template if=\"!jobs.length\">\n <div class=\"p-8 text-center text-gray-500\">No jobs recorded yet.</div>\n </template>\n </section>\n\n <template if=\"hasMore\">\n <div class=\"flex justify-center\">\n <button\n class=\"px-4 py-2 text-sm text-gray-200 bg-gray-800 hover:bg-gray-700 border border-gray-700 rounded-lg\"\n on-click=\"loadMore()\"\n >\n {{ loadingMore ? 'Loading...' : 'Load more' }}\n </button>\n </div>\n </template>\n </main>\n\n <script setup>\n import { onDestroy, onInit, ref } from '@li3/web';\n\n export default function setup() {\n const jobs = ref([]);\n const lastUpdated = ref('now');\n const refreshError = ref(false);\n const hasMore = ref(false);\n const loadingMore = ref(false);\n const notificationsSupported = ref('Notification' in window && 'serviceWorker' in navigator);\n const notificationsEnabled = ref(\n notificationsSupported.value &&\n Notification.permission === 'granted' &&\n localStorage.getItem('runner-notifications') === 'enabled',\n );\n const notificationNotice = ref('');\n let refreshTimer;\n let eventSource;\n let refreshing = false;\n let refreshPending = false;\n\n const upper = (value) => String(value || '').toUpperCase();\n const terminalStatuses = new Set(['success', 'failed', 'cancelled']);\n const notifyCompletedJob = async (job) => {\n if (!notificationsEnabled.value || !['success', 'failed'].includes(job.status)) return;\n try {\n const registration = await navigator.serviceWorker.ready;\n await registration.showNotification(\n job.status === 'success' ? `Job #${job.id} succeeded` : `Job #${job.id} failed`,\n {\n body: `${job.workflowId} finished with status ${job.status}.`,\n icon: '/app-icon.svg',\n badge: '/app-icon.svg',\n tag: `runner-job-${job.id}`,\n renotify: true,\n data: { url: `/runs/${job.id}` },\n },\n );\n } catch (error) {\n console.error('Unable to show job notification', error);\n }\n };\n const toggleNotifications = async () => {\n if (notificationsEnabled.value) {\n notificationsEnabled.value = false;\n localStorage.removeItem('runner-notifications');\n await disablePushNotifications();\n notificationNotice.value = 'Job notifications are paused on this device.';\n return;\n }\n if (Notification.permission === 'denied') {\n notificationNotice.value =\n 'Notifications are blocked. Allow them in this site’s Android browser settings.';\n return;\n }\n const permission = await Notification.requestPermission();\n notificationsEnabled.value = permission === 'granted';\n if (notificationsEnabled.value) {\n localStorage.setItem('runner-notifications', 'enabled');\n await enablePushNotifications();\n notificationNotice.value =\n 'Notifications enabled. Background delivery is available when server push is configured.';\n } else {\n notificationNotice.value = 'Notification permission was not granted.';\n }\n };\n const urlBase64ToUint8Array = (value) => {\n const padding = '='.repeat((4 - (value.length % 4)) % 4);\n const raw = atob((value + padding).replace(/-/g, '+').replace(/_/g, '/'));\n return Uint8Array.from([...raw].map((character) => character.charCodeAt(0)));\n };\n const enablePushNotifications = async () => {\n try {\n if (!notificationsSupported.value) return;\n const response = await fetch('/api/push/public-key', { headers: { accept: 'application/json' } });\n if (!response.ok) return;\n const { publicKey } = await response.json();\n const registration = await navigator.serviceWorker.ready;\n const subscription =\n (await registration.pushManager.getSubscription()) ||\n (await registration.pushManager.subscribe({\n userVisibleOnly: true,\n applicationServerKey: urlBase64ToUint8Array(publicKey),\n }));\n const saveResponse = await fetch('/api/push/subscriptions', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(subscription),\n });\n if (!saveResponse.ok) throw new Error(`Push subscription failed: ${saveResponse.status}`);\n } catch (error) {\n console.error('Unable to enable background notifications', error);\n }\n };\n const disablePushNotifications = async () => {\n try {\n const registration = await navigator.serviceWorker.ready;\n const subscription = await registration.pushManager.getSubscription();\n if (!subscription) return;\n await fetch('/api/push/subscriptions', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ endpoint: subscription.endpoint }),\n });\n await subscription.unsubscribe();\n } catch (error) {\n console.error('Unable to disable background notifications', error);\n }\n };\n const refreshJobs = async () => {\n if (refreshing) {\n refreshPending = true;\n return;\n }\n refreshing = true;\n\n try {\n const activeIds = jobs.value\n .filter((job) => !terminalStatuses.has(job.status))\n .map((job) => Number(job.id));\n const knownIds = jobs.value.map((job) => Number(job.id));\n const afterId = activeIds.length\n ? Math.max(0, Math.min(...activeIds) - 1)\n : knownIds.length\n ? Math.max(...knownIds)\n : null;\n const url = afterId === null ? '/api/jobs?limit=500' : `/api/jobs?afterId=${afterId}&limit=500`;\n const response = await fetch(url, { headers: { accept: 'application/json' } });\n if (!response.ok) throw new Error(`Dashboard refresh failed: ${response.status}`);\n\n const data = await response.json();\n const previousStatuses = new Map(jobs.value.map((job) => [job.id, job.status]));\n const merged = new Map(jobs.value.map((job) => [job.id, job]));\n for (const job of data.jobs) {\n const previousStatus = previousStatuses.get(job.id);\n merged.set(job.id, job);\n if (previousStatus && !terminalStatuses.has(previousStatus) && terminalStatuses.has(job.status)) {\n void notifyCompletedJob(job);\n }\n }\n jobs.value = Array.from(merged.values()).sort((a, b) => Number(b.id) - Number(a.id));\n lastUpdated.value = new Date().toLocaleTimeString();\n refreshError.value = false;\n } catch (error) {\n console.error(error);\n refreshError.value = true;\n } finally {\n refreshing = false;\n if (refreshPending) {\n refreshPending = false;\n void refreshJobs();\n }\n }\n };\n\n const loadMore = async () => {\n if (loadingMore.value || !hasMore.value || !jobs.value.length) return;\n loadingMore.value = true;\n\n try {\n const beforeId = Math.min(...jobs.value.map((job) => Number(job.id)));\n const response = await fetch(`/api/jobs?beforeId=${beforeId}`, {\n headers: { accept: 'application/json' },\n });\n if (!response.ok) throw new Error(`Loading older jobs failed: ${response.status}`);\n\n const data = await response.json();\n const merged = new Map(jobs.value.map((job) => [job.id, job]));\n for (const job of data.jobs) merged.set(job.id, job);\n jobs.value = Array.from(merged.values()).sort((a, b) => Number(b.id) - Number(a.id));\n hasMore.value = data.hasMore;\n } catch (error) {\n console.error(error);\n refreshError.value = true;\n } finally {\n loadingMore.value = false;\n }\n };\n\n onInit(() => {\n if (notificationsSupported.value) {\n navigator.serviceWorker\n .register('/service-worker.js')\n .then(() => {\n if (notificationsEnabled.value) void enablePushNotifications();\n })\n .catch((error) => {\n console.error('Service worker registration failed', error);\n });\n }\n eventSource = new EventSource('/api/events');\n eventSource.addEventListener('jobs.available', refreshJobs);\n eventSource.addEventListener('jobs.changed', refreshJobs);\n eventSource.onerror = () => {\n refreshError.value = true;\n };\n refreshTimer = setInterval(refreshJobs, 60000);\n });\n onDestroy(() => {\n eventSource?.close();\n clearInterval(refreshTimer);\n });\n\n return {\n jobs,\n lastUpdated,\n refreshError,\n hasMore,\n loadingMore,\n loadMore,\n upper,\n notificationsSupported,\n notificationsEnabled,\n notificationNotice,\n toggleNotifications,\n };\n }\n <\/script>\n <script state>\n __DASHBOARD_STATE__\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";
224
+ }, ue = "<!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 <link rel=\"component\" href=\"https://sodium.static.apphor.de/lucide-icon.html\" />\n <title>Workflow Engine Dashboard</title>\n <script type=\"importmap\">\n { \"imports\": { \"@li3/\": \"https://cdn.li3.dev/@li3/\" } }\n <\/script>\n </head>\n <body class=\"bg-gray-950 text-gray-100 min-h-screen p-4 md:p-6 font-sans\">\n <template app>\n <main class=\"max-w-6xl mx-auto space-y-6\">\n <header class=\"flex flex-col sm:flex-row sm:items-center justify-between gap-3 border-b border-gray-800 pb-4\">\n <div>\n <h1 class=\"text-2xl font-bold text-white\">Runner Engine Status</h1>\n <p class=\"text-xs text-gray-400\">Live Job Queue &amp; Execution Traces</p>\n </div>\n <div class=\"flex flex-wrap items-center gap-3 text-xs font-mono\">\n <a href=\"/workflows\" class=\"text-indigo-400 hover:text-indigo-300 hover:underline\">Manage workflows</a>\n <template if=\"notificationsSupported\">\n <button\n class=\"inline-flex h-9 w-9 items-center justify-center rounded-full border\"\n bind-class=\"notificationsEnabled ? 'border-emerald-500/30.bg-emerald-500/10.text-emerald-300' : 'border-indigo-500/40.bg-indigo-500/10.text-indigo-300'\"\n on-click=\"toggleNotifications()\"\n >\n <lucide-icon bind-icon=\"notificationsEnabled ? 'bell-ring' : 'bell-off'\" size=\"16\"></lucide-icon>\n <span class=\"sr-only\">{{ notificationsEnabled ? 'Notifications on' : 'Enable notifications' }}</span>\n </button>\n </template>\n <span class=\"text-gray-500\">Updated {{ lastUpdated }}</span>\n <template if=\"refreshError\">\n <span class=\"rounded border border-rose-500/20 bg-rose-500/10 px-3 py-1 text-rose-400\"\n >Refresh failed</span\n >\n </template>\n </div>\n </header>\n\n <form class=\"flex flex-col gap-2 sm:flex-row\" on-submit=\"applyFilter($event)\">\n <label class=\"sr-only\" for=\"job-filter\">Filter jobs</label>\n <input\n id=\"job-filter\"\n bind-value=\"filter\"\n class=\"min-w-0 flex-1 rounded-lg border border-gray-800 bg-gray-900 px-3 py-2 text-sm text-gray-200 outline-none placeholder:text-gray-600 focus:border-indigo-500\"\n placeholder=\"Filter: name: cloud-cli/* or search JSON\"\n />\n <button\n type=\"submit\"\n class=\"rounded-lg border border-indigo-500/40 bg-indigo-500/10 px-4 py-2 text-sm text-indigo-200 hover:bg-indigo-500/20\"\n >\n Search\n </button>\n <button\n type=\"button\"\n on-click=\"clearFilter()\"\n class=\"rounded-lg border border-gray-700 px-4 py-2 text-sm text-gray-300 hover:bg-gray-800\"\n >\n Clear\n </button>\n </form>\n\n <template if=\"notificationNotice\">\n <button\n class=\"w-full rounded-lg border border-gray-700 bg-gray-900 px-4 py-3 text-left text-sm text-gray-300\"\n on-click=\"notificationNotice = ''\"\n >\n {{ notificationNotice }}\n </button>\n </template>\n\n <section class=\"bg-gray-900 border border-gray-800 rounded-xl overflow-hidden hidden md:block\">\n <table class=\"w-full text-left text-sm\">\n <thead class=\"bg-gray-800/50 text-gray-400 text-xs uppercase font-mono border-b border-gray-800\">\n <tr>\n <th class=\"py-3 px-4\">Job ID</th>\n <th class=\"py-3 px-4\">Workflow</th>\n <th class=\"py-3 px-4\">Status</th>\n <th class=\"py-3 px-4 hidden xl:table-cell\">Worker</th>\n <th class=\"py-3 px-4 hidden lg:table-cell\">Created At</th>\n <th class=\"py-3 px-4 text-right\">Action</th>\n </tr>\n </thead>\n <tbody>\n <template for=\"job of jobs\">\n <tr class=\"border-b border-gray-800 hover:bg-gray-800/40 transition\">\n <td class=\"py-3 px-4 font-mono text-indigo-400\">\n <a attr-href=\"'/runs/' + job.id\" class=\"hover:underline\">#{{ job.id }}</a>\n </td>\n <td class=\"py-3 px-4 font-medium text-white\">{{ job.workflowId }}</td>\n <td class=\"py-3 px-4\">\n <span\n class=\"px-2.5 py-0.5 rounded-full text-xs font-semibold\"\n bind-class=\"{\n 'bg-emerald-500/10.text-emerald-400': job.status === 'success',\n 'bg-rose-500/10.text-rose-400': job.status === 'failed',\n 'bg-indigo-500/10.text-indigo-400.animate-pulse': job.status === 'running',\n 'bg-gray-500/10.text-gray-400': !['success', 'failed', 'running'].includes(job.status)\n }\"\n >\n {{ upper(job.status) }}\n </span>\n </td>\n <td class=\"py-3 px-4 hidden xl:table-cell text-xs font-mono text-gray-400\">\n {{ job.workerId || '-' }}\n </td>\n <td class=\"py-3 px-4 hidden lg:table-cell text-xs text-gray-400\">{{ job.createdAt }}</td>\n <td class=\"py-3 px-4 text-right whitespace-nowrap\">\n <a\n attr-href=\"'/runs/' + job.id\"\n class=\"text-xs bg-gray-800 hover:bg-gray-700 text-gray-200 px-3 py-1 rounded border border-gray-700\"\n >View Trace</a\n >\n </td>\n </tr>\n </template>\n </tbody>\n </table>\n <template if=\"!jobs.length\">\n <div class=\"p-8 text-center text-gray-500\">No jobs recorded yet.</div>\n </template>\n </section>\n\n <section\n class=\"md:hidden bg-gray-900 border border-gray-800 rounded-xl divide-y divide-gray-800 overflow-hidden\"\n >\n <template for=\"job of jobs\">\n <a\n attr-href=\"'/runs/' + job.id\"\n class=\"block p-4 bg-gray-900/60 hover:bg-gray-800/40 focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-indigo-400\"\n >\n <div class=\"flex items-start justify-between gap-3\">\n <div class=\"min-w-0\">\n <div class=\"flex items-center gap-2\">\n <span\n class=\"w-2.5 h-2.5 rounded-full shrink-0\"\n bind-class=\"{\n 'bg-emerald-400': job.status === 'success',\n 'bg-rose-400': job.status === 'failed',\n 'bg-indigo-400.animate-pulse': job.status === 'running',\n 'bg-gray-500': !['success', 'failed', 'running'].includes(job.status)\n }\"\n ></span>\n <h2 class=\"font-medium text-white truncate\">{{ job.workflowId }}</h2>\n </div>\n <p class=\"mt-1 pl-[18px] text-xs font-mono text-gray-400\">#{{ job.id }} · {{ upper(job.status) }}</p>\n </div>\n </div>\n </a>\n </template>\n <template if=\"!jobs.length\">\n <div class=\"p-8 text-center text-gray-500\">No jobs recorded yet.</div>\n </template>\n </section>\n\n <template if=\"hasMore\">\n <div class=\"flex justify-center\">\n <button\n class=\"px-4 py-2 text-sm text-gray-200 bg-gray-800 hover:bg-gray-700 border border-gray-700 rounded-lg\"\n on-click=\"loadMore()\"\n >\n {{ loadingMore ? 'Loading...' : 'Load more' }}\n </button>\n </div>\n </template>\n </main>\n\n <script setup>\n import { onDestroy, onInit, ref } from '@li3/web';\n\n export default function setup() {\n const jobs = ref([]);\n const lastUpdated = ref('now');\n const refreshError = ref(false);\n const hasMore = ref(false);\n const loadingMore = ref(false);\n const filter = ref('');\n const activeFilter = ref('');\n const notificationsSupported = ref('Notification' in window && 'serviceWorker' in navigator);\n const notificationsEnabled = ref(\n notificationsSupported.value &&\n Notification.permission === 'granted' &&\n localStorage.getItem('runner-notifications') === 'enabled',\n );\n const notificationNotice = ref('');\n let refreshTimer;\n let eventSource;\n let refreshing = false;\n let refreshPending = false;\n\n const upper = (value) => String(value || '').toUpperCase();\n const terminalStatuses = new Set(['success', 'failed', 'cancelled']);\n const notifyCompletedJob = async (job) => {\n if (!notificationsEnabled.value || !['success', 'failed'].includes(job.status)) return;\n try {\n const registration = await navigator.serviceWorker.ready;\n await registration.showNotification(\n job.status === 'success' ? `Job #${job.id} succeeded` : `Job #${job.id} failed`,\n {\n body: `${job.workflowId} finished with status ${job.status}.`,\n icon: '/app-icon.svg',\n badge: '/app-icon.svg',\n tag: `runner-job-${job.id}`,\n renotify: true,\n data: { url: `/runs/${job.id}` },\n },\n );\n } catch (error) {\n console.error('Unable to show job notification', error);\n }\n };\n const toggleNotifications = async () => {\n if (notificationsEnabled.value) {\n notificationsEnabled.value = false;\n localStorage.removeItem('runner-notifications');\n await disablePushNotifications();\n notificationNotice.value = 'Job notifications are paused on this device.';\n return;\n }\n if (Notification.permission === 'denied') {\n notificationNotice.value =\n 'Notifications are blocked. Allow them in this site’s Android browser settings.';\n return;\n }\n const permission = await Notification.requestPermission();\n notificationsEnabled.value = permission === 'granted';\n if (notificationsEnabled.value) {\n localStorage.setItem('runner-notifications', 'enabled');\n await enablePushNotifications();\n notificationNotice.value =\n 'Notifications enabled. Background delivery is available when server push is configured.';\n } else {\n notificationNotice.value = 'Notification permission was not granted.';\n }\n };\n const urlBase64ToUint8Array = (value) => {\n const padding = '='.repeat((4 - (value.length % 4)) % 4);\n const raw = atob((value + padding).replace(/-/g, '+').replace(/_/g, '/'));\n return Uint8Array.from([...raw].map((character) => character.charCodeAt(0)));\n };\n const enablePushNotifications = async () => {\n try {\n if (!notificationsSupported.value) return;\n const response = await fetch('/api/push/public-key', { headers: { accept: 'application/json' } });\n if (!response.ok) return;\n const { publicKey } = await response.json();\n const registration = await navigator.serviceWorker.ready;\n const subscription =\n (await registration.pushManager.getSubscription()) ||\n (await registration.pushManager.subscribe({\n userVisibleOnly: true,\n applicationServerKey: urlBase64ToUint8Array(publicKey),\n }));\n const saveResponse = await fetch('/api/push/subscriptions', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(subscription),\n });\n if (!saveResponse.ok) throw new Error(`Push subscription failed: ${saveResponse.status}`);\n } catch (error) {\n console.error('Unable to enable background notifications', error);\n }\n };\n const disablePushNotifications = async () => {\n try {\n const registration = await navigator.serviceWorker.ready;\n const subscription = await registration.pushManager.getSubscription();\n if (!subscription) return;\n await fetch('/api/push/subscriptions', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ endpoint: subscription.endpoint }),\n });\n await subscription.unsubscribe();\n } catch (error) {\n console.error('Unable to disable background notifications', error);\n }\n };\n const refreshJobs = async () => {\n if (refreshing) {\n refreshPending = true;\n return;\n }\n refreshing = true;\n\n try {\n const activeIds = jobs.value\n .filter((job) => !terminalStatuses.has(job.status))\n .map((job) => Number(job.id));\n const knownIds = jobs.value.map((job) => Number(job.id));\n const afterId = activeIds.length\n ? Math.max(0, Math.min(...activeIds) - 1)\n : knownIds.length\n ? Math.max(...knownIds)\n : null;\n const filterQuery = activeFilter.value ? `&filter=${encodeURIComponent(activeFilter.value)}` : '';\n const url =\n afterId === null\n ? `/api/jobs?limit=500${filterQuery}`\n : `/api/jobs?afterId=${afterId}&limit=500${filterQuery}`;\n const response = await fetch(url, { headers: { accept: 'application/json' } });\n if (!response.ok) throw new Error(`Dashboard refresh failed: ${response.status}`);\n\n const data = await response.json();\n const previousStatuses = new Map(jobs.value.map((job) => [job.id, job.status]));\n const merged = new Map(jobs.value.map((job) => [job.id, job]));\n for (const job of data.jobs) {\n const previousStatus = previousStatuses.get(job.id);\n merged.set(job.id, job);\n if (previousStatus && !terminalStatuses.has(previousStatus) && terminalStatuses.has(job.status)) {\n void notifyCompletedJob(job);\n }\n }\n jobs.value = Array.from(merged.values()).sort((a, b) => Number(b.id) - Number(a.id));\n lastUpdated.value = new Date().toLocaleTimeString();\n refreshError.value = false;\n } catch (error) {\n console.error(error);\n refreshError.value = true;\n } finally {\n refreshing = false;\n if (refreshPending) {\n refreshPending = false;\n void refreshJobs();\n }\n }\n };\n\n const loadMore = async () => {\n if (loadingMore.value || !hasMore.value || !jobs.value.length) return;\n loadingMore.value = true;\n\n try {\n const beforeId = Math.min(...jobs.value.map((job) => Number(job.id)));\n const filterQuery = activeFilter.value ? `&filter=${encodeURIComponent(activeFilter.value)}` : '';\n const response = await fetch(`/api/jobs?beforeId=${beforeId}${filterQuery}`, {\n headers: { accept: 'application/json' },\n });\n if (!response.ok) throw new Error(`Loading older jobs failed: ${response.status}`);\n\n const data = await response.json();\n const merged = new Map(jobs.value.map((job) => [job.id, job]));\n for (const job of data.jobs) merged.set(job.id, job);\n jobs.value = Array.from(merged.values()).sort((a, b) => Number(b.id) - Number(a.id));\n hasMore.value = data.hasMore;\n } catch (error) {\n console.error(error);\n refreshError.value = true;\n } finally {\n loadingMore.value = false;\n }\n };\n const applyFilter = (event) => {\n event.preventDefault();\n activeFilter.value = filter.value.trim();\n jobs.value = [];\n hasMore.value = false;\n void refreshJobs();\n };\n const clearFilter = () => {\n filter.value = '';\n activeFilter.value = '';\n jobs.value = [];\n hasMore.value = false;\n void refreshJobs();\n };\n\n onInit(() => {\n if (notificationsSupported.value) {\n navigator.serviceWorker\n .register('/service-worker.js')\n .then(() => {\n if (notificationsEnabled.value) void enablePushNotifications();\n })\n .catch((error) => {\n console.error('Service worker registration failed', error);\n });\n }\n eventSource = new EventSource('/api/events');\n eventSource.addEventListener('jobs.available', refreshJobs);\n eventSource.addEventListener('jobs.changed', refreshJobs);\n eventSource.onerror = () => {\n refreshError.value = true;\n };\n refreshTimer = setInterval(refreshJobs, 60000);\n });\n onDestroy(() => {\n eventSource?.close();\n clearInterval(refreshTimer);\n });\n\n return {\n jobs,\n lastUpdated,\n refreshError,\n hasMore,\n loadingMore,\n filter,\n loadMore,\n applyFilter,\n clearFilter,\n upper,\n notificationsSupported,\n notificationsEnabled,\n notificationNotice,\n toggleNotifications,\n };\n }\n <\/script>\n <script state>\n __DASHBOARD_STATE__\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";
218
225
  //#endregion
219
226
  //#region src/html-state.ts
220
227
  function de(e) {
@@ -8413,8 +8420,8 @@ var ts = class {
8413
8420
  }), t.end(Zn("editor", n));
8414
8421
  }
8415
8422
  if (e.method === "GET" && n.pathname === "/api/jobs") {
8416
- 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 ? ns : Number(i);
8417
- 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 > rs ? (t.writeHead(400, { "Content-Type": "application/json; charset=utf-8" }), t.end(JSON.stringify({ error: `limit must be an integer from 1 to ${rs}` }))) : this.renderDashboardJobs(t, s, a, o);
8423
+ 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 = e === null ? void 0 : Number(e), s = r === null ? void 0 : Number(r), c = i === null ? ns : Number(i);
8424
+ return o !== void 0 && (!Number.isSafeInteger(o) || o < 0) ? (t.writeHead(400, { "Content-Type": "application/json; charset=utf-8" }), t.end(JSON.stringify({ error: "afterId must be a non-negative integer" }))) : s !== void 0 && (!Number.isSafeInteger(s) || s < 1) ? (t.writeHead(400, { "Content-Type": "application/json; charset=utf-8" }), t.end(JSON.stringify({ error: "beforeId must be a positive integer" }))) : !Number.isSafeInteger(c) || c < 1 || c > rs ? (t.writeHead(400, { "Content-Type": "application/json; charset=utf-8" }), t.end(JSON.stringify({ error: `limit must be an integer from 1 to ${rs}` }))) : 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, c, o, s, a);
8418
8425
  }
8419
8426
  if (e.method === "GET" && n.pathname === "/api/events") return this.events.subscribe(e, t);
8420
8427
  if (e.method === "GET" && n.pathname === "/api/push/public-key") return this.handlePushPublicKey(t);
@@ -8709,15 +8716,21 @@ var ts = class {
8709
8716
  let t = await this.queue.listJobs(51), n = fe(t.slice(0, ns)), r = await this.redactText(pe(n, t.length > ns));
8710
8717
  e.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }), e.end(r);
8711
8718
  }
8712
- async renderDashboardJobs(e, t, n, r) {
8713
- let i = await this.queue.listJobs(t + 1, n, r), a = fe(i.slice(0, t)), o = await this.redactText(JSON.stringify({
8714
- jobs: a,
8715
- hasMore: i.length > t
8719
+ async renderDashboardJobs(e, t, n, r, i) {
8720
+ let a;
8721
+ try {
8722
+ a = await this.queue.listJobs(t + 1, n, r, i);
8723
+ } catch (t) {
8724
+ return e.writeHead(400, { "Content-Type": "application/json; charset=utf-8" }), e.end(JSON.stringify({ error: t.message }));
8725
+ }
8726
+ let o = fe(a.slice(0, t)), s = await this.redactText(JSON.stringify({
8727
+ jobs: o,
8728
+ hasMore: a.length > t
8716
8729
  }));
8717
8730
  e.writeHead(200, {
8718
8731
  "Cache-Control": "no-store",
8719
8732
  "Content-Type": "application/json; charset=utf-8"
8720
- }), e.end(o);
8733
+ }), e.end(s);
8721
8734
  }
8722
8735
  async renderRunDetails(e, t, n, r) {
8723
8736
  let i = await this.queue.getJob(e);
package/dist/queue.d.ts CHANGED
@@ -34,7 +34,7 @@ export declare class QueueManager {
34
34
  /**
35
35
  * List recent jobs for dashboard status monitoring
36
36
  */
37
- listJobs(limit?: number, afterId?: number, beforeId?: number): Promise<any[]>;
37
+ listJobs(limit?: number, afterId?: number, beforeId?: number, filter?: string): Promise<any[]>;
38
38
  /**
39
39
  * Save lightweight summary report (NO heavy logs in this JSON!)
40
40
  */
@@ -1 +1 @@
1
- {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvF,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ;gBAAR,QAAQ,EAAE,MAAM;IAE9B,IAAI;IAKV;;;OAGG;IACG,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,GAAE,MAAM,EAAO,EAAE,cAAc,CAAC,EAAE,MAAM;IAoBrI;;;OAGG;IACG,YAAY,CAAC,UAAU,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IA6BlE,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOvD;;OAEG;IACG,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,SAAS;IAInD,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAUtG,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,WAAW,GAAG,YAAY,CAAC;IAapF,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAyBvC;;OAEG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKrD,cAAc;IAMd,YAAY;IAsClB;;OAEG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAIlD;;OAEG;IACG,QAAQ,CAAC,KAAK,SAAK,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAiB/E;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAOxF,2DAA2D;IACrD,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU5F;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAS1E"}
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,uBAAuB,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvF,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ;gBAAR,QAAQ,EAAE,MAAM;IAE9B,IAAI;IAKV;;;OAGG;IACG,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,GAAE,MAAM,EAAO,EAAE,cAAc,CAAC,EAAE,MAAM;IAoBrI;;;OAGG;IACG,YAAY,CAAC,UAAU,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IA6BlE,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOvD;;OAEG;IACG,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,SAAS;IAInD,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAUtG,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,WAAW,GAAG,YAAY,CAAC;IAapF,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAyBvC;;OAEG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKrD,cAAc;IAMd,YAAY;IAsClB;;OAEG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAIlD;;OAEG;IACG,QAAQ,CAAC,KAAK,SAAK,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IA8BhG;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAOxF,2DAA2D;IACrD,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU5F;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAS1E"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAaA,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,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;IAkI3B;;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;IA2B/B,OAAO,CAAC,mBAAmB;YASb,mBAAmB;YAWnB,qBAAqB;IAWnC;;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"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAaA,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,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;IAuI3B;;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;IA2B/B,OAAO,CAAC,mBAAmB;YASb,mBAAmB;YAWnB,qBAAqB;IAWnC;;OAEG;YACW,eAAe;YAQf,mBAAmB;IAuBjC;;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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloud-cli/on",
3
- "version": "1.22.1",
3
+ "version": "1.23.0",
4
4
  "description": "CLI entry point for the on plugin ecosystem.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@10.30.3",