From 2ddcbf21b41f77f8ec6b9bca6d9953cb952f0ba7 Mon Sep 17 00:00:00 2001 From: Harsh-2002 Date: Tue, 11 Aug 2026 11:17:19 +0000 Subject: [PATCH 1/2] Harden scoped SDK auth and KV reliability Entire-Checkpoint: 01KZR8N3JZ3BZ2SKD3RHNF3JSS --- .github/workflows/ci.yml | 2 + Makefile | 4 +- backend/CLAUDE.md | 2 +- backend/cmd/orva/adapters/node/orva.d.ts | 1 + backend/cmd/orva/adapters/node/orva.js | 62 ++-- backend/cmd/orva/adapters/node/package.json | 2 +- backend/cmd/orva/adapters/python/orva.py | 56 ++-- backend/cmd/orva/setup.go | 2 +- backend/internal/database/activity.go | 2 +- backend/internal/database/async.go | 183 +++++++---- backend/internal/database/async_test.go | 55 ++++ backend/internal/database/database.go | 1 + backend/internal/database/executions.go | 9 +- backend/internal/database/kv.go | 305 ++++++++++++++---- backend/internal/database/kv_atomic_test.go | 191 ++++++++++- backend/internal/database/kv_metrics.go | 72 +++++ backend/internal/database/log_entries.go | 6 +- backend/internal/database/migrations.go | 4 +- backend/internal/database/spans.go | 24 +- backend/internal/mcp/reference.md | 33 +- backend/internal/mcp/tools_kv.go | 57 ++-- backend/internal/pool/pool.go | 19 +- backend/internal/proxy/proxy.go | 10 +- backend/internal/scheduler/scheduler.go | 10 + backend/internal/sdkauth/auth.go | 115 +++++++ backend/internal/sdkauth/auth_test.go | 47 +++ backend/internal/server/handlers/cron.go | 32 +- .../handlers/inbound_webhook_trigger.go | 4 + .../server/handlers/internal_invoke.go | 74 +++-- backend/internal/server/handlers/jobs.go | 81 ++--- backend/internal/server/handlers/kv.go | 169 +++++++--- .../internal/server/handlers/kv_operator.go | 72 +++-- .../server/handlers/metrics_format_test.go | 21 ++ backend/internal/server/handlers/replay.go | 4 + .../internal/server/handlers/sdk_auth_test.go | 133 ++++++++ .../internal/server/handlers/sdkversion.go | 6 +- backend/internal/server/handlers/spans.go | 46 +-- backend/internal/server/handlers/system.go | 94 ++++-- .../server/handlers/system_health_test.go | 4 + backend/internal/server/middleware_auth.go | 40 +-- .../internal/server/middleware_auth_test.go | 18 +- backend/internal/server/router.go | 124 +++---- backend/internal/server/server.go | 49 ++- .../assets/{AI-DwO56pB2.js => AI-De72Ye8u.js} | 2 +- ...ivity-C3gP7_TB.js => Activity-6I8QCc07.js} | 2 +- ...piKeys-C9fpYpoP.js => ApiKeys-B5PU7HpA.js} | 2 +- ...nnels-D-vbrSjj.js => Channels-fKQl2oE8.js} | 2 +- ...tor-CAQF33k1.js => CodeEditor-CiRqvRcK.js} | 2 +- ...nJobs-4ao1_N2n.js => CronJobs-CDFaee6D.js} | 2 +- ...oard-CEd_pnH7.js => Dashboard-rKNyCP0W.js} | 2 +- ...ts-DCd1MNSh.js => Deployments-CRPgafhC.js} | 2 +- .../{Docs-DjpX404o.js => Docs-BssSTQCS.js} | 4 +- ...{Drawer-ZggviMzu.js => Drawer-B98TBytl.js} | 2 +- ...{Editor-BENkmvs8.js => Editor-C_JYWteY.js} | 10 +- ...ewall-bYvlPpSq.js => Firewall-Bpe5shll.js} | 2 +- ...f-DBpPBQBL.js => FunctionDiff-DtVzmfsl.js} | 2 +- ...-ByM7ruDh.js => FunctionsList-CPOzZCfJ.js} | 2 +- ..._jOjeYV.js => InboundWebhooks-CFOyt9ev.js} | 2 +- ...BZpwfSFT.js => InvocationsLog-Bz2zpa_f.js} | 2 +- .../{Jobs-DzZOcH5E.js => Jobs-CfHJpgrS.js} | 2 +- .../server/ui_dist/assets/KVStore-DzgumyT2.js | 1 + .../server/ui_dist/assets/KVStore-Ly56eJIu.js | 1 - .../{Login-CGYB-z1p.js => Login-DscOTSMy.js} | 2 +- .../{Modal-BL-P3nUi.js => Modal-BAoZams6.js} | 2 +- ...Menu-CCBXKr4H.js => ModelMenu-BtMjUXcx.js} | 2 +- ...Found-CIp_RSOc.js => NotFound-b7MSgvru.js} | 2 +- ...ing-BI0ADHLS.js => Onboarding-CI9ktN7I.js} | 2 +- ...tings-CS-pXJcG.js => Settings-Bjj6gg_h.js} | 2 +- ...ge-BOCx3ovI.js => StatusBadge-Baoe7YAb.js} | 2 +- ...il-c4yTBunf.js => TraceDetail-wVyyYiTv.js} | 2 +- ...{Traces-DKjSRtpZ.js => Traces-BEuuXgIG.js} | 2 +- ...hooks-Dv44P51d.js => Webhooks-D2CkyonR.js} | 2 +- ...mpts-NjljN3ld.js => aiPrompts-XrsFCpj_.js} | 20 +- ...eft-CEJWChyG.js => arrow-left-Ba3OArNt.js} | 2 +- ...open-BnyP35ri.js => book-open-B7-cMpW6.js} | 2 +- .../server/ui_dist/assets/check-BNre7JFR.js | 1 + .../server/ui_dist/assets/check-DQm9OMOd.js | 1 - .../ui_dist/assets/chevron-right-BufLmSBw.js | 1 - .../ui_dist/assets/chevron-right-CRxFsA9Y.js | 1 + ...{circle-bxz1-xUq.js => circle-NOse79gh.js} | 2 +- ...t-BIdKIOKw.js => circle-alert-BnUYYhGE.js} | 2 +- .../{clock-Dl3hMdU8.js => clock-CiH8bQVI.js} | 2 +- .../{copy-Bt6Rv0BQ.js => copy-BqdwwcxC.js} | 2 +- .../{flag-BU6shp0V.js => flag-CmjNKmxi.js} | 2 +- ...re-Dh2_YhZP.js => git-compare-Co883xFS.js} | 2 +- .../{globe-C84kKXm0.js => globe-BnBar2lO.js} | 2 +- .../{index-C11HmDXN.js => index-DTqMKlE1.js} | 4 +- ...ound-BmyLzD9o.js => key-round-BKXtbC85.js} | 2 +- .../{lock-QGBYN3Zu.js => lock-CttMBTH5.js} | 2 +- ...{pencil-BuAbR0ud.js => pencil-Do2I7soU.js} | 2 +- .../{play-DdJW9ehy.js => play-CmQifd74.js} | 2 +- ...-cw-Bn_tp4gH.js => refresh-cw-Cn8qkf-v.js} | 2 +- ...ccw-DBtrGGiP.js => rotate-ccw-DWwjKCqh.js} | 2 +- ...s-2-C7i2we5c.js => settings-2-D5QtFfdZ.js} | 2 +- ...k-HnHCfFke.js => shield-check-piXkOtNv.js} | 2 +- ...rkles-BmmGPRlQ.js => sparkles-DTHEIS5T.js} | 2 +- ...pen-JECRrUKU.js => square-pen-DCrjAsLy.js} | 2 +- ...minal-BVgEq2_Q.js => terminal-BQdlNiyt.js} | 2 +- ...rash-2-Bj4AFUfw.js => trash-2-DaeYqnW_.js} | 2 +- ...iable-Doeff6Y-.js => variable-DrK2KZuk.js} | 2 +- .../{zap-CGOT0fxk.js => zap-BXGoxm_a.js} | 2 +- backend/internal/server/ui_dist/docs.md | 33 +- backend/internal/server/ui_dist/index.html | 2 +- backend/internal/version/version.go | 2 +- backend/runtimes/node/orva.d.ts | 1 + backend/runtimes/node/orva.js | 62 ++-- backend/runtimes/node/package.json | 2 +- backend/runtimes/python/orva.py | 56 ++-- cli/commands/kv.go | 23 +- cli/commands/reference.md | 33 +- docs/API.md | 15 +- docs/ARCHITECTURE.md | 6 +- docs/CLI.md | 5 +- docs/ERRORS.md | 4 +- docs/SECURITY.md | 16 + docs/TESTING.md | 38 +-- docs/reference.md | 33 +- frontend/public/docs.md | 33 +- frontend/src/templates/index.js | 14 +- frontend/src/utils/aiPrompts.js | 20 +- frontend/src/views/Docs.vue | 2 +- frontend/src/views/KVStore.vue | 42 +-- test/CLAUDE.md | 4 +- test/e2e/tests/test_kv.py | 31 ++ test/e2e/tests/test_security.py | 3 +- test/sdk-test.sh | 137 +++++++- 126 files changed, 2140 insertions(+), 862 deletions(-) create mode 100644 backend/internal/database/async_test.go create mode 100644 backend/internal/database/kv_metrics.go create mode 100644 backend/internal/sdkauth/auth.go create mode 100644 backend/internal/sdkauth/auth_test.go create mode 100644 backend/internal/server/handlers/sdk_auth_test.go rename backend/internal/server/ui_dist/assets/{AI-DwO56pB2.js => AI-De72Ye8u.js} (99%) rename backend/internal/server/ui_dist/assets/{Activity-C3gP7_TB.js => Activity-6I8QCc07.js} (98%) rename backend/internal/server/ui_dist/assets/{ApiKeys-C9fpYpoP.js => ApiKeys-B5PU7HpA.js} (96%) rename backend/internal/server/ui_dist/assets/{Channels-D-vbrSjj.js => Channels-fKQl2oE8.js} (98%) rename backend/internal/server/ui_dist/assets/{CodeEditor-CAQF33k1.js => CodeEditor-CiRqvRcK.js} (90%) rename backend/internal/server/ui_dist/assets/{CronJobs-4ao1_N2n.js => CronJobs-CDFaee6D.js} (98%) rename backend/internal/server/ui_dist/assets/{Dashboard-CEd_pnH7.js => Dashboard-rKNyCP0W.js} (99%) rename backend/internal/server/ui_dist/assets/{Deployments-DCd1MNSh.js => Deployments-CRPgafhC.js} (96%) rename backend/internal/server/ui_dist/assets/{Docs-DjpX404o.js => Docs-BssSTQCS.js} (65%) rename backend/internal/server/ui_dist/assets/{Drawer-ZggviMzu.js => Drawer-B98TBytl.js} (96%) rename backend/internal/server/ui_dist/assets/{Editor-BENkmvs8.js => Editor-C_JYWteY.js} (98%) rename backend/internal/server/ui_dist/assets/{Firewall-bYvlPpSq.js => Firewall-Bpe5shll.js} (98%) rename backend/internal/server/ui_dist/assets/{FunctionDiff-DBpPBQBL.js => FunctionDiff-DtVzmfsl.js} (99%) rename backend/internal/server/ui_dist/assets/{FunctionsList-ByM7ruDh.js => FunctionsList-CPOzZCfJ.js} (96%) rename backend/internal/server/ui_dist/assets/{InboundWebhooks-C_jOjeYV.js => InboundWebhooks-CFOyt9ev.js} (98%) rename backend/internal/server/ui_dist/assets/{InvocationsLog-BZpwfSFT.js => InvocationsLog-Bz2zpa_f.js} (97%) rename backend/internal/server/ui_dist/assets/{Jobs-DzZOcH5E.js => Jobs-CfHJpgrS.js} (97%) create mode 100644 backend/internal/server/ui_dist/assets/KVStore-DzgumyT2.js delete mode 100644 backend/internal/server/ui_dist/assets/KVStore-Ly56eJIu.js rename backend/internal/server/ui_dist/assets/{Login-CGYB-z1p.js => Login-DscOTSMy.js} (93%) rename backend/internal/server/ui_dist/assets/{Modal-BL-P3nUi.js => Modal-BAoZams6.js} (97%) rename backend/internal/server/ui_dist/assets/{ModelMenu-CCBXKr4H.js => ModelMenu-BtMjUXcx.js} (98%) rename backend/internal/server/ui_dist/assets/{NotFound-CIp_RSOc.js => NotFound-b7MSgvru.js} (93%) rename backend/internal/server/ui_dist/assets/{Onboarding-BI0ADHLS.js => Onboarding-CI9ktN7I.js} (97%) rename backend/internal/server/ui_dist/assets/{Settings-CS-pXJcG.js => Settings-Bjj6gg_h.js} (99%) rename backend/internal/server/ui_dist/assets/{StatusBadge-BOCx3ovI.js => StatusBadge-Baoe7YAb.js} (86%) rename backend/internal/server/ui_dist/assets/{TraceDetail-c4yTBunf.js => TraceDetail-wVyyYiTv.js} (97%) rename backend/internal/server/ui_dist/assets/{Traces-DKjSRtpZ.js => Traces-BEuuXgIG.js} (95%) rename backend/internal/server/ui_dist/assets/{Webhooks-Dv44P51d.js => Webhooks-D2CkyonR.js} (97%) rename backend/internal/server/ui_dist/assets/{aiPrompts-NjljN3ld.js => aiPrompts-XrsFCpj_.js} (93%) rename backend/internal/server/ui_dist/assets/{arrow-left-CEJWChyG.js => arrow-left-Ba3OArNt.js} (58%) rename backend/internal/server/ui_dist/assets/{book-open-BnyP35ri.js => book-open-B7-cMpW6.js} (77%) create mode 100644 backend/internal/server/ui_dist/assets/check-BNre7JFR.js delete mode 100644 backend/internal/server/ui_dist/assets/check-DQm9OMOd.js delete mode 100644 backend/internal/server/ui_dist/assets/chevron-right-BufLmSBw.js create mode 100644 backend/internal/server/ui_dist/assets/chevron-right-CRxFsA9Y.js rename backend/internal/server/ui_dist/assets/{circle-bxz1-xUq.js => circle-NOse79gh.js} (76%) rename backend/internal/server/ui_dist/assets/{circle-alert-BIdKIOKw.js => circle-alert-BnUYYhGE.js} (73%) rename backend/internal/server/ui_dist/assets/{clock-Dl3hMdU8.js => clock-CiH8bQVI.js} (77%) rename backend/internal/server/ui_dist/assets/{copy-Bt6Rv0BQ.js => copy-BqdwwcxC.js} (71%) rename backend/internal/server/ui_dist/assets/{flag-BU6shp0V.js => flag-CmjNKmxi.js} (73%) rename backend/internal/server/ui_dist/assets/{git-compare-Dh2_YhZP.js => git-compare-Co883xFS.js} (86%) rename backend/internal/server/ui_dist/assets/{globe-C84kKXm0.js => globe-BnBar2lO.js} (72%) rename backend/internal/server/ui_dist/assets/{index-C11HmDXN.js => index-DTqMKlE1.js} (95%) rename backend/internal/server/ui_dist/assets/{key-round-BmyLzD9o.js => key-round-BKXtbC85.js} (81%) rename backend/internal/server/ui_dist/assets/{lock-QGBYN3Zu.js => lock-CttMBTH5.js} (67%) rename backend/internal/server/ui_dist/assets/{pencil-BuAbR0ud.js => pencil-Do2I7soU.js} (75%) rename backend/internal/server/ui_dist/assets/{play-DdJW9ehy.js => play-CmQifd74.js} (64%) rename backend/internal/server/ui_dist/assets/{refresh-cw-Bn_tp4gH.js => refresh-cw-Cn8qkf-v.js} (79%) rename backend/internal/server/ui_dist/assets/{rotate-ccw-DBtrGGiP.js => rotate-ccw-DWwjKCqh.js} (66%) rename backend/internal/server/ui_dist/assets/{settings-2-C7i2we5c.js => settings-2-D5QtFfdZ.js} (92%) rename backend/internal/server/ui_dist/assets/{shield-check-HnHCfFke.js => shield-check-piXkOtNv.js} (79%) rename backend/internal/server/ui_dist/assets/{sparkles-BmmGPRlQ.js => sparkles-DTHEIS5T.js} (86%) rename backend/internal/server/ui_dist/assets/{square-pen-JECRrUKU.js => square-pen-DCrjAsLy.js} (79%) rename backend/internal/server/ui_dist/assets/{terminal-BVgEq2_Q.js => terminal-BQdlNiyt.js} (57%) rename backend/internal/server/ui_dist/assets/{trash-2-Bj4AFUfw.js => trash-2-DaeYqnW_.js} (79%) rename backend/internal/server/ui_dist/assets/{variable-Doeff6Y-.js => variable-DrK2KZuk.js} (77%) rename backend/internal/server/ui_dist/assets/{zap-CGOT0fxk.js => zap-BXGoxm_a.js} (75%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 541e9f82..b37d400e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -465,6 +465,8 @@ jobs: : > test/e2e/CHECKLIST.md ADMIN_KEY="$(cat "$RUNNER_TEMP/orva-data/.admin-key")" python3 test/e2e/run.py --url http://127.0.0.1:8443 --api-key "$ADMIN_KEY" + ORVA_ENDPOINT=http://127.0.0.1:8443 ORVA_API_KEY="$ADMIN_KEY" \ + bash test/sdk-test.sh - name: Stop Orva if: always() diff --git a/Makefile b/Makefile index ce043022..f2c51aa2 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ docs-embed: # Copy adapter sources + bundled SDK into backend/cmd/orva/adapters/ so # //go:embed has them at build time. Keeps backend/runtimes/ as the # source-of-truth directory (shared with Dockerfile COPY paths). -# Also copies the v0.2 orva SDK module (kv / invoke / jobs). +# Also copies the bundled Orva SDK module (kv / invoke / jobs / tracing). adapters-embed: @rm -rf backend/cmd/orva/adapters @mkdir -p backend/cmd/orva/adapters/node backend/cmd/orva/adapters/python @@ -55,7 +55,7 @@ adapters-embed: @cp backend/runtimes/python/adapter.py backend/cmd/orva/adapters/python/adapter.py @cp backend/runtimes/node/orva.js backend/cmd/orva/adapters/node/orva.js @cp backend/runtimes/python/orva.py backend/cmd/orva/adapters/python/orva.py - @# v0.6 SDK: ship .d.ts + package.json so TS handlers get types; + @# Ship .d.ts + package.json so TS handlers get types; @# py.typed marks the Python module as fully typed for static checkers. @cp backend/runtimes/node/orva.d.ts backend/cmd/orva/adapters/node/orva.d.ts @cp backend/runtimes/node/package.json backend/cmd/orva/adapters/node/package.json diff --git a/backend/CLAUDE.md b/backend/CLAUDE.md index 50887b54..3ffe43a5 100644 --- a/backend/CLAUDE.md +++ b/backend/CLAUDE.md @@ -82,7 +82,7 @@ Key files: `deploy.go`, `deployments.go`, `diff.go`, `rollback.go`, `functions.g `CORS → BodySizeLimit → Auth → RequestID → Logger → Handler` -Auth middleware only runs on paths starting with `/api/`. Everything else (`/fn/`, `/metrics`, `/webhook/`, `/mcp`, custom routes) bypasses the API-key check entirely — per-function auth for invocations is enforced inside `InvokeHandler`. Internal SDK paths (`/api/v1/_kv/`, `/api/v1/_internal/`) use the per-process internal token instead of API keys. +Auth middleware only runs on paths starting with `/api/`. Everything else (`/fn/`, `/metrics`, `/webhook/`, `/mcp`, custom routes) bypasses the API-key check entirely — per-function auth for invocations is enforced inside `InvokeHandler`. Internal SDK paths (`/api/v1/_kv/`, `/api/v1/_internal/`) use process-signed, function-scoped credentials instead of API keys. ## Database diff --git a/backend/cmd/orva/adapters/node/orva.d.ts b/backend/cmd/orva/adapters/node/orva.d.ts index 781c24ba..caa4d7bf 100644 --- a/backend/cmd/orva/adapters/node/orva.d.ts +++ b/backend/cmd/orva/adapters/node/orva.d.ts @@ -33,6 +33,7 @@ export interface KVPutEntry { } export interface KVOptions { + /** Omit to preserve expiry, use 0 to clear it, or a positive value to set it. */ ttlSeconds?: number } diff --git a/backend/cmd/orva/adapters/node/orva.js b/backend/cmd/orva/adapters/node/orva.js index 71254604..b08be9dd 100644 --- a/backend/cmd/orva/adapters/node/orva.js +++ b/backend/cmd/orva/adapters/node/orva.js @@ -1,7 +1,7 @@ // Orva Node.js SDK — kv, invoke, jobs, crons, trace, log, context. // -// Routes through ORVA_API_BASE (loopback) using ORVA_INTERNAL_TOKEN that -// was injected at worker spawn. Both env vars must be present in +// Routes through ORVA_API_BASE using the function-scoped ORVA_INTERNAL_TOKEN +// injected at worker spawn. Both env vars must be present in // production; absent in tests where the SDK throws OrvaUnavailableError // (unless __test_mode__ has supplied an override implementation). // @@ -14,7 +14,7 @@ // SDK version baked at adapter-embed time. Bumped in lockstep with the // server. The string is sent on every internal-token call so operators // can see drift in deployment logs. -const SDK_VERSION = '0.6.0' +const SDK_VERSION = '0.7.0' const COMMON_HEADERS = { 'Content-Type': 'application/json' } @@ -73,8 +73,6 @@ function _traceHeaders() { const exec = _execID() if (trace) h['X-Orva-Trace-Id'] = trace if (span) h['X-Orva-Span-Id'] = span - if (fn) h['X-Orva-Caller-Function'] = fn - if (fn) h['X-Orva-Function-Id'] = fn if (exec) h['X-Orva-Execution-Id'] = exec h['X-Orva-SDK-Version'] = SDK_VERSION return h @@ -146,13 +144,17 @@ const kv = { return data.value != null ? data.value : defaultValue }, - /** Upsert a value. ttlSeconds=0 disables expiry. */ - async put(key, value, { ttlSeconds = 0 } = {}) { + /** Upsert a value. Omit ttlSeconds to preserve expiry; 0 clears it. */ + async put(key, value, options = {}) { const fn = _fnID() + const requestBody = { value } + if (Object.hasOwn(options, 'ttlSeconds')) { + requestBody.ttl_seconds = Number(options.ttlSeconds) + } const { status, body } = await _request( 'PUT', `/api/v1/_kv/${fn}/${encodeURIComponent(key)}`, - { body: { value, ttl_seconds: ttlSeconds | 0 } } + { body: requestBody } ) if (status >= 400) throw new OrvaError(`kv.put(${key}) failed: ${body}`, status) }, @@ -198,6 +200,7 @@ const kv = { }) if (status >= 400) throw new OrvaError(`kv.getMany failed: ${body}`, status) const data = JSON.parse(body) + _assertAtomicBatch(data, 'kv.getMany') const out = {} for (const r of data.results || []) { out[r.key] = r.found ? r.value : null @@ -209,16 +212,16 @@ const kv = { async putMany(entries) { if (!entries || entries.length === 0) return const fn = _fnID() - const ops = entries.map((e) => ({ - op: 'put', - key: e.key, - value: e.value, - ttl_seconds: (e.ttlSeconds | 0) || 0, - })) + const ops = entries.map((e) => { + const op = { op: 'put', key: e.key, value: e.value } + if (Object.hasOwn(e, 'ttlSeconds')) op.ttl_seconds = Number(e.ttlSeconds) + return op + }) const { status, body } = await _request('POST', `/api/v1/_kv/${fn}/batch`, { body: { ops }, }) if (status >= 400) throw new OrvaError(`kv.putMany failed: ${body}`, status) + _assertAtomicBatch(JSON.parse(body), 'kv.putMany') }, /** Delete N keys in one transaction. Returns the number removed. */ @@ -231,16 +234,21 @@ const kv = { }) if (status >= 400) throw new OrvaError(`kv.deleteMany failed: ${body}`, status) const data = JSON.parse(body) + _assertAtomicBatch(data, 'kv.deleteMany') return (data.results || []).filter((r) => r.found).length }, /** Atomic increment. Missing keys are treated as 0. Returns new value. */ - async incr(key, delta = 1, { ttlSeconds = 0 } = {}) { + async incr(key, delta = 1, options = {}) { const fn = _fnID() + const requestBody = { delta } + if (Object.hasOwn(options, 'ttlSeconds')) { + requestBody.ttl_seconds = Number(options.ttlSeconds) + } const { status, body } = await _request( 'POST', `/api/v1/_kv/${fn}/${encodeURIComponent(key)}/incr`, - { body: { delta, ttl_seconds: ttlSeconds | 0 } } + { body: requestBody } ) if (status >= 400) throw new OrvaError(`kv.incr(${key}) failed: ${body}`, status) return JSON.parse(body).value @@ -251,17 +259,20 @@ const kv = { * Returns true on success; on mismatch, throws OrvaCASMismatch carrying * the current value so callers can retry. */ - async cas(key, expected, newValue, { ttlSeconds = 0 } = {}) { + async cas(key, expected, newValue, options = {}) { const fn = _fnID() + const requestBody = { + expected: expected === null ? null : expected, + new: newValue, + } + if (Object.hasOwn(options, 'ttlSeconds')) { + requestBody.ttl_seconds = Number(options.ttlSeconds) + } const { status, body } = await _request( 'POST', `/api/v1/_kv/${fn}/${encodeURIComponent(key)}/cas`, { - body: { - expected: expected === null ? null : expected, - new: newValue, - ttl_seconds: ttlSeconds | 0, - }, + body: requestBody, } ) if (status >= 400) throw new OrvaError(`kv.cas(${key}) failed: ${body}`, status) @@ -271,6 +282,13 @@ const kv = { }, } +function _assertAtomicBatch(data, operation) { + const failed = (data.results || []).find((result) => result.error) + if (failed) { + throw new OrvaError(`${operation} failed: ${failed.error}`, 500) + } +} + // ── Function-to-function invoke ───────────────────────────────────── async function invoke(functionName, payload = {}, { timeoutMs = DEFAULT_TIMEOUT_MS } = {}) { diff --git a/backend/cmd/orva/adapters/node/package.json b/backend/cmd/orva/adapters/node/package.json index 4c1d5af5..5e002ade 100644 --- a/backend/cmd/orva/adapters/node/package.json +++ b/backend/cmd/orva/adapters/node/package.json @@ -1,6 +1,6 @@ { "name": "orva", - "version": "0.6.0", + "version": "0.7.0", "main": "./orva.js", "types": "./orva.d.ts", "private": true, diff --git a/backend/cmd/orva/adapters/python/orva.py b/backend/cmd/orva/adapters/python/orva.py index 448bcda5..d289fb4f 100644 --- a/backend/cmd/orva/adapters/python/orva.py +++ b/backend/cmd/orva/adapters/python/orva.py @@ -1,8 +1,8 @@ """Orva Python SDK — kv, invoke, jobs, crons, trace, log, context. Available inside any function running on Orva. Routes through the -ORVA_API_BASE loopback URL using the per-process ORVA_INTERNAL_TOKEN -that the worker received at spawn time. Both env vars are present in +ORVA_API_BASE URL using the function-scoped ORVA_INTERNAL_TOKEN that the +worker received at spawn time. Both env vars are present in production and absent in tests; helpers raise OrvaUnavailableError when the SDK can't reach the host unless __test_mode__ has installed an override. @@ -27,7 +27,7 @@ # SDK version baked at adapter-embed time. Sent on every internal call. -SDK_VERSION = "0.6.0" +SDK_VERSION = "0.7.0" T = TypeVar("T") @@ -129,9 +129,6 @@ def _trace_headers() -> Dict[str, str]: h["X-Orva-Trace-Id"] = v if v := _span_id(): h["X-Orva-Span-Id"] = v - if v := _function_id(): - h["X-Orva-Caller-Function"] = v - h["X-Orva-Function-Id"] = v if v := _execution_id(): h["X-Orva-Execution-Id"] = v return h @@ -211,9 +208,12 @@ def get(key: str, default: Any = None) -> Any: return data["value"] if data.get("value") is not None else default @staticmethod - def put(key: str, value: Any, *, ttl_seconds: int = 0) -> None: + def put(key: str, value: Any, *, ttl_seconds: Optional[int] = None) -> None: fn = _function_id() - payload = json.dumps({"value": value, "ttl_seconds": int(ttl_seconds)}).encode("utf-8") + request_body: Dict[str, Any] = {"value": value} + if ttl_seconds is not None: + request_body["ttl_seconds"] = int(ttl_seconds) + payload = json.dumps(request_body).encode("utf-8") status, body, _ = _request( "PUT", f"/api/v1/_kv/{fn}/{quote(key, safe='')}", body=payload ) @@ -241,6 +241,7 @@ def list( if status >= 400: raise OrvaError(f"kv.list failed: {body!r}", status=status) data = json.loads(body) + _assert_atomic_batch(data, "kv.get_many") return {"keys": list(data.get("keys") or []), "next_cursor": data.get("next_cursor", "")} @staticmethod @@ -263,19 +264,17 @@ def put_many(entries: List[Dict[str, Any]]) -> None: if not entries: return fn = _function_id() - ops = [ - { - "op": "put", - "key": e["key"], - "value": e["value"], - "ttl_seconds": int(e.get("ttl_seconds", 0)), - } - for e in entries - ] + ops = [] + for entry in entries: + op = {"op": "put", "key": entry["key"], "value": entry["value"]} + if "ttl_seconds" in entry: + op["ttl_seconds"] = int(entry["ttl_seconds"]) + ops.append(op) payload = json.dumps({"ops": ops}).encode("utf-8") status, body, _ = _request("POST", f"/api/v1/_kv/{fn}/batch", body=payload) if status >= 400: raise OrvaError(f"kv.put_many failed: {body!r}", status=status) + _assert_atomic_batch(json.loads(body), "kv.put_many") @staticmethod def delete_many(keys: List[str]) -> int: @@ -287,12 +286,16 @@ def delete_many(keys: List[str]) -> int: if status >= 400: raise OrvaError(f"kv.delete_many failed: {body!r}", status=status) data = json.loads(body) + _assert_atomic_batch(data, "kv.delete_many") return sum(1 for r in data.get("results") or [] if r.get("found")) @staticmethod - def incr(key: str, delta: int = 1, *, ttl_seconds: int = 0) -> int: + def incr(key: str, delta: int = 1, *, ttl_seconds: Optional[int] = None) -> int: fn = _function_id() - payload = json.dumps({"delta": int(delta), "ttl_seconds": int(ttl_seconds)}).encode("utf-8") + request_body: Dict[str, Any] = {"delta": int(delta)} + if ttl_seconds is not None: + request_body["ttl_seconds"] = int(ttl_seconds) + payload = json.dumps(request_body).encode("utf-8") status, body, _ = _request( "POST", f"/api/v1/_kv/{fn}/{quote(key, safe='')}/incr", body=payload ) @@ -301,11 +304,12 @@ def incr(key: str, delta: int = 1, *, ttl_seconds: int = 0) -> int: return int(json.loads(body)["value"]) @staticmethod - def cas(key: str, expected: Any, new: Any, *, ttl_seconds: int = 0) -> bool: + def cas(key: str, expected: Any, new: Any, *, ttl_seconds: Optional[int] = None) -> bool: fn = _function_id() - payload = json.dumps( - {"expected": expected, "new": new, "ttl_seconds": int(ttl_seconds)} - ).encode("utf-8") + request_body: Dict[str, Any] = {"expected": expected, "new": new} + if ttl_seconds is not None: + request_body["ttl_seconds"] = int(ttl_seconds) + payload = json.dumps(request_body).encode("utf-8") status, body, _ = _request( "POST", f"/api/v1/_kv/{fn}/{quote(key, safe='')}/cas", body=payload ) @@ -320,6 +324,12 @@ def cas(key: str, expected: Any, new: Any, *, ttl_seconds: int = 0) -> bool: kv = _KV() +def _assert_atomic_batch(data: Dict[str, Any], operation: str) -> None: + failed = next((item for item in data.get("results") or [] if item.get("error")), None) + if failed: + raise OrvaError(f"{operation} failed: {failed['error']}", status=500) + + # ── Function-to-function invoke ───────────────────────────────────── diff --git a/backend/cmd/orva/setup.go b/backend/cmd/orva/setup.go index cb49b0c0..dfedd9bd 100644 --- a/backend/cmd/orva/setup.go +++ b/backend/cmd/orva/setup.go @@ -293,7 +293,7 @@ func installAdapter(rootfs, runtime string) error { } fmt.Printf("[ok] installed %s adapter at %s\n", runtime, dst) - // v0.6: the runtime SDK ships alongside the adapter. Without these + // The runtime SDK ships alongside the adapter. Without these // files, `require('orva')` / `from orva import …` raise inside a // sandbox. Python places orva.py + py.typed directly under // /opt/orva/; Node places orva.js + orva.d.ts + package.json under diff --git a/backend/internal/database/activity.go b/backend/internal/database/activity.go index 2e4dd63a..9591e6e7 100644 --- a/backend/internal/database/activity.go +++ b/backend/internal/database/activity.go @@ -38,7 +38,7 @@ func (db *Database) InsertActivity(row ActivityRow) { if row.TS == 0 { row.TS = time.Now().UnixMilli() } - db.AsyncExec(` + db.AsyncExecTelemetry(` INSERT INTO activity_log ( ts, source, actor_type, actor_id, actor_label, method, path, status, duration_ms, summary, request_id, metadata, trace_id diff --git a/backend/internal/database/async.go b/backend/internal/database/async.go index 9933ed56..1f7f6469 100644 --- a/backend/internal/database/async.go +++ b/backend/internal/database/async.go @@ -3,6 +3,7 @@ package database import ( "context" "log/slog" + "sync/atomic" "time" ) @@ -22,46 +23,91 @@ type writeJob struct { // while amortizing fsync cost across dozens of rows. type asyncWriter struct { db *Database - ch chan writeJob + critical chan writeJob + telemetry chan writeJob done chan struct{} batchMax int flushEvery time.Duration + dropped atomic.Uint64 + timeouts atomic.Uint64 + failed atomic.Uint64 } func newAsyncWriter(db *Database) *asyncWriter { return &asyncWriter{ db: db, - ch: make(chan writeJob, 1024), + critical: make(chan writeJob, 1024), + telemetry: make(chan writeJob, 1024), done: make(chan struct{}), batchMax: 50, flushEvery: 50 * time.Millisecond, } } -// AsyncExec queues a write for batched execution. Never blocks the caller -// unless the channel is full — in that case falls back to a direct Async -// goroutine so the hot path is never stalled. At sustained saturation the -// fallback signals to the operator that batchMax / buffer should grow. -func (db *Database) AsyncExec(sql string, args ...any) { +// AsyncExec queues a critical write with bounded backpressure. +// Callers that can propagate cancellation should use AsyncExecCritical. +func (db *Database) AsyncExec(sql string, args ...any) error { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := db.AsyncExecCritical(ctx, sql, args...); err != nil { + slog.Warn("critical async write failed", "err", err) + return err + } + return nil +} + +// AsyncExecCritical applies bounded backpressure and reports queue or deadline +// failures. Once enqueued, the writer owns the job. It never creates an +// overflow goroutine or makes request latency wait for SQLite fsync. +func (db *Database) AsyncExecCritical(ctx context.Context, statement string, args ...any) error { if db.writer == nil { - // Writer not initialized (Migrate not yet run, or legacy path). - db.Async(func() { - if _, err := db.write.Exec(sql, args...); err != nil { - slog.Warn("direct async write failed", "err", err) - } - }) + _, err := db.write.ExecContext(ctx, statement, args...) + return err + } + select { + case db.writer.critical <- writeJob{sql: statement, args: args}: + return nil + case <-ctx.Done(): + db.writer.timeouts.Add(1) + return ctx.Err() + } +} + +// AsyncExecTelemetry queues best-effort activity, log, or span data. A full +// queue drops the record and increments an observable counter. +func (db *Database) AsyncExecTelemetry(statement string, args ...any) { + if db.writer == nil { + if _, err := db.write.Exec(statement, args...); err != nil { + slog.Warn("direct telemetry write failed", "err", err) + } return } select { - case db.writer.ch <- writeJob{sql: sql, args: args}: + case db.writer.telemetry <- writeJob{sql: statement, args: args}: default: - // Channel full — use the old fire-and-forget goroutine so we don't - // backpressure the request handler. - db.Async(func() { - if _, err := db.write.Exec(sql, args...); err != nil { - slog.Warn("async writer overflow direct write failed", "err", err) - } - }) + db.writer.dropped.Add(1) + } +} + +type WriterStats struct { + CriticalDepth int + TelemetryDepth int + CriticalCap int + TelemetryCap int + CriticalTimeouts uint64 + CriticalFailures uint64 + DroppedTelemetry uint64 +} + +func (db *Database) WriterStats() WriterStats { + if db == nil || db.writer == nil { + return WriterStats{} + } + return WriterStats{ + CriticalDepth: len(db.writer.critical), TelemetryDepth: len(db.writer.telemetry), + CriticalCap: cap(db.writer.critical), TelemetryCap: cap(db.writer.telemetry), + CriticalTimeouts: db.writer.timeouts.Load(), CriticalFailures: db.writer.failed.Load(), + DroppedTelemetry: db.writer.dropped.Load(), } } @@ -79,81 +125,96 @@ func (a *asyncWriter) start() { func (a *asyncWriter) run() { ticker := time.NewTicker(a.flushEvery) defer ticker.Stop() + critical := a.critical + telemetry := a.telemetry - batch := make([]writeJob, 0, a.batchMax) - flush := func() { - if len(batch) == 0 { + criticalBatch := make([]writeJob, 0, a.batchMax) + telemetryBatch := make([]writeJob, 0, a.batchMax) + flushCritical := func() { + if len(criticalBatch) == 0 { return } - a.commit(batch) - batch = batch[:0] + a.commit(criticalBatch, false) + criticalBatch = criticalBatch[:0] + } + flushTelemetry := func() { + if len(telemetryBatch) == 0 { + return + } + a.commit(telemetryBatch, true) + telemetryBatch = telemetryBatch[:0] } for { select { - case job, ok := <-a.ch: + case job, ok := <-critical: if !ok { - flush() - close(a.done) - return + critical = nil + continue + } + criticalBatch = append(criticalBatch, job) + if len(criticalBatch) >= a.batchMax { + flushCritical() } - batch = append(batch, job) - // Opportunistically drain anything else already queued so we - // commit one transaction per ~50 jobs instead of one per job. - // `break` inside a `select` only breaks the select — use a - // labelled outer for so we can break the loop when the channel - // is empty. - drain: - for len(batch) < a.batchMax { - select { - case j2, ok2 := <-a.ch: - if !ok2 { - flush() - close(a.done) - return - } - batch = append(batch, j2) - default: - break drain - } + case job, ok := <-telemetry: + if !ok { + telemetry = nil + continue } - if len(batch) >= a.batchMax { - flush() + telemetryBatch = append(telemetryBatch, job) + if len(telemetryBatch) >= a.batchMax { + flushTelemetry() } case <-ticker.C: - flush() + flushCritical() + flushTelemetry() + } + if critical == nil && telemetry == nil { + flushCritical() + flushTelemetry() + close(a.done) + return } } } -func (a *asyncWriter) commit(batch []writeJob) { +func (a *asyncWriter) commit(batch []writeJob, telemetry bool) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() tx, err := a.db.write.BeginTx(ctx, nil) if err != nil { - slog.Warn("batch tx begin failed; falling back to per-stmt", "err", err, "n", len(batch)) - for _, j := range batch { - if _, e := a.db.write.Exec(j.sql, j.args...); e != nil { - slog.Warn("batch fallback stmt failed", "err", e) - } - } + a.finish(batch, telemetry, err) return } for _, j := range batch { if _, err := tx.Exec(j.sql, j.args...); err != nil { slog.Warn("batch stmt failed", "err", err) - // Keep going — one bad stmt shouldn't abort the whole batch. + _ = tx.Rollback() + a.finish(batch, telemetry, err) + return } } if err := tx.Commit(); err != nil { slog.Warn("batch commit failed", "err", err) _ = tx.Rollback() + a.finish(batch, telemetry, err) + return + } + a.finish(batch, telemetry, nil) +} + +func (a *asyncWriter) finish(batch []writeJob, telemetry bool, err error) { + if telemetry && err != nil { + a.dropped.Add(uint64(len(batch))) + } else if err != nil { + a.failed.Add(uint64(len(batch))) } } // stop closes the job channel and waits for the consumer to drain. func (a *asyncWriter) stop() { - close(a.ch) + close(a.critical) + close(a.telemetry) <-a.done } diff --git a/backend/internal/database/async_test.go b/backend/internal/database/async_test.go new file mode 100644 index 00000000..f67503cf --- /dev/null +++ b/backend/internal/database/async_test.go @@ -0,0 +1,55 @@ +package database + +import ( + "context" + "errors" + "testing" + "time" +) + +func TestTelemetryQueueDropsWhenSaturated(t *testing.T) { + db := &Database{} + db.writer = newAsyncWriter(db) + for i := 0; i < cap(db.writer.telemetry)+1; i++ { + db.AsyncExecTelemetry("SELECT 1") + } + stats := db.WriterStats() + if stats.TelemetryDepth != stats.TelemetryCap || stats.DroppedTelemetry != 1 { + t.Fatalf("writer stats=%+v", stats) + } +} + +func TestWriterRecordsPostEnqueueFailuresByPriority(t *testing.T) { + db := newTestDB(t) + if err := db.AsyncExecCritical(context.Background(), "not valid sql"); err != nil { + t.Fatalf("enqueue critical: %v", err) + } + db.AsyncExecTelemetry("also not valid sql") + + deadline := time.Now().Add(time.Second) + for time.Now().Before(deadline) { + stats := db.WriterStats() + if stats.CriticalFailures == 1 && stats.DroppedTelemetry == 1 { + return + } + time.Sleep(10 * time.Millisecond) + } + t.Fatalf("writer did not report commit failures: %+v", db.WriterStats()) +} + +func TestCriticalQueueHonorsDeadlineWhenSaturated(t *testing.T) { + db := &Database{} + db.writer = newAsyncWriter(db) + for i := 0; i < cap(db.writer.critical); i++ { + db.writer.critical <- writeJob{} + } + ctx, cancel := context.WithCancel(context.Background()) + cancel() + err := db.AsyncExecCritical(ctx, "SELECT 1") + if !errors.Is(err, context.Canceled) { + t.Fatalf("err=%v, want context.Canceled", err) + } + if db.WriterStats().CriticalTimeouts != 1 { + t.Fatal("critical timeout was not counted") + } +} diff --git a/backend/internal/database/database.go b/backend/internal/database/database.go index 677dd5de..2c648291 100644 --- a/backend/internal/database/database.go +++ b/backend/internal/database/database.go @@ -21,6 +21,7 @@ type Database struct { // pressure under sustained 500+ req/s. nil until Migrate() is called // (so tests that create a Database without migrating still work). writer *asyncWriter + kv kvMetrics // asyncWG tracks fire-and-forget goroutines (log inserts, last-used // updates) so Close() can wait for them to finish before tearing the diff --git a/backend/internal/database/executions.go b/backend/internal/database/executions.go index 96614484..1c2d7cf7 100644 --- a/backend/internal/database/executions.go +++ b/backend/internal/database/executions.go @@ -106,7 +106,8 @@ func (db *Database) InsertExecutionFinal(exec *Execution, durationMS int64, stat } // AsyncInsertExecutionFinal queues the final-write of a completed execution -// for the batched writer. Non-blocking on the hot path. Trace fields are +// for the bounded critical writer. Enqueue applies deadline-aware backpressure +// only when saturated; commit remains off the hot request path. Trace fields are // taken from exec.TraceID/SpanID/ParentSpanID/Trigger/ParentFunctionID; // callers populate them before calling. IsOutlier + BaselineP95MS are NOT // written here — the baseline package back-writes them via UpdateOutlier @@ -231,10 +232,8 @@ func (db *Database) AsyncInsertExecutionLog(log *ExecutionLog) { } // AsyncInsertExecutionRequest queues a captured-request row for the -// batched writer (v0.4 A3). Mirrors the pattern of AsyncInsertExecutionFinal: -// the proxy hot path never blocks on the SQLite writer. Foreign-key -// satisfaction relies on the executions row landing first via the same -// queue — under the batched-tx model both rows commit together. +// bounded critical writer (v0.4 A3). execution_requests intentionally has no +// foreign key because capture happens before the final execution row exists. func (db *Database) AsyncInsertExecutionRequest(req *ExecutionRequest) { truncated := 0 if req.Truncated { diff --git a/backend/internal/database/kv.go b/backend/internal/database/kv.go index 456c8ed9..99d723c7 100644 --- a/backend/internal/database/kv.go +++ b/backend/internal/database/kv.go @@ -1,15 +1,60 @@ package database import ( + "context" "database/sql" + "encoding/json" "errors" + "math" "time" + "unicode/utf8" ) // ErrKVNotFound is returned by KVGet when the key is missing OR expired. // Callers can use errors.Is to disambiguate from real database errors. var ErrKVNotFound = errors.New("kv: key not found") +const ( + KVMaxKeyRunes = 256 + KVMaxValueBytes = 64 * 1024 +) + +func ValidateKVKey(key string) error { + if key == "" { + return errors.New("kv: key is required") + } + if !utf8.ValidString(key) { + return errors.New("kv: key must be valid UTF-8") + } + if utf8.RuneCountInString(key) > KVMaxKeyRunes { + return errors.New("kv: key exceeds 256 characters") + } + return nil +} + +func ValidateKVValue(value []byte) error { + if len(value) == 0 || !json.Valid(value) { + return errors.New("kv: value must be valid JSON") + } + if len(value) > KVMaxValueBytes { + return errors.New("kv: value exceeds 64 KB") + } + return nil +} + +func ValidateKVTTL(ttlSeconds *int) error { + if ttlSeconds == nil { + return nil + } + if *ttlSeconds < 0 { + return errors.New("kv: ttl_seconds must be >= 0") + } + if int64(*ttlSeconds) > math.MaxInt64/int64(time.Second) { + return errors.New("kv: ttl_seconds is too large") + } + return nil +} + // KVEntry is the row shape returned by KVGet / KVList. type KVEntry struct { FunctionID string `json:"function_id"` @@ -20,23 +65,45 @@ type KVEntry struct { UpdatedAt time.Time `json:"updated_at"` } -// KVPut upserts a value. ttlSeconds=0 means no expiry; positive values -// set expires_at to now+ttl. Negative values are clamped to 0 (no expiry) -// since negative TTLs would expire immediately and just create churn. -func (db *Database) KVPut(functionID, key string, value []byte, ttlSeconds int) error { +// KVPut upserts a value. A nil TTL preserves an existing expiry (and makes +// new keys persistent), zero clears expiry, and a positive value sets it. +func (db *Database) KVPut(functionID, key string, value []byte, ttlSeconds *int) error { + return db.KVPutContext(context.Background(), functionID, key, value, ttlSeconds) +} + +func (db *Database) KVPutContext(ctx context.Context, functionID, key string, value []byte, ttlSeconds *int) (err error) { + started := time.Now() + defer func() { db.observeKV("put", started, err) }() + if err := ValidateKVKey(key); err != nil { + return err + } + if err := ValidateKVValue(value); err != nil { + return err + } now := time.Now().UTC() var expires any - if ttlSeconds > 0 { - expires = now.Add(time.Duration(ttlSeconds) * time.Second) - } - _, err := db.write.Exec(` - INSERT INTO kv_store (function_id, key, value, expires_at, created_at, updated_at) - VALUES (?, ?, ?, ?, ?, ?) - ON CONFLICT(function_id, key) DO UPDATE SET - value = excluded.value, - expires_at = excluded.expires_at, - updated_at = excluded.updated_at`, - functionID, key, value, expires, now, now) + specified := 0 + if err := ValidateKVTTL(ttlSeconds); err != nil { + return err + } + if ttlSeconds != nil { + specified = 1 + if *ttlSeconds > 0 { + expires = now.Add(time.Duration(*ttlSeconds) * time.Second) + } + } + _, err = db.write.ExecContext(ctx, ` + INSERT INTO kv_store (function_id, key, value, expires_at, created_at, updated_at) + VALUES (?, ?, ?, ?, ?, ?) + ON CONFLICT(function_id, key) DO UPDATE SET + value = excluded.value, + expires_at = CASE + WHEN ? = 1 THEN excluded.expires_at + WHEN kv_store.expires_at IS NOT NULL AND kv_store.expires_at <= ? THEN NULL + ELSE kv_store.expires_at + END, + updated_at = excluded.updated_at`, + functionID, key, value, expires, now, now, specified, now) return err } @@ -44,9 +111,18 @@ func (db *Database) KVPut(functionID, key string, value []byte, ttlSeconds int) // key is missing or expired. Expired rows are not auto-deleted here — the // scheduler's TTL sweep handles bulk cleanup. func (db *Database) KVGet(functionID, key string) (*KVEntry, error) { + return db.KVGetContext(context.Background(), functionID, key) +} + +func (db *Database) KVGetContext(ctx context.Context, functionID, key string) (entry *KVEntry, err error) { + started := time.Now() + defer func() { db.observeKV("get", started, err) }() + if err := ValidateKVKey(key); err != nil { + return nil, err + } var e KVEntry var expires sql.NullTime - err := db.read.QueryRow(` + err = db.read.QueryRowContext(ctx, ` SELECT function_id, key, value, expires_at, created_at, updated_at FROM kv_store WHERE function_id = ? AND key = ?`, functionID, key, @@ -69,7 +145,16 @@ func (db *Database) KVGet(functionID, key string) (*KVEntry, error) { // KVDelete removes (function_id, key). No error if the row doesn't exist. func (db *Database) KVDelete(functionID, key string) error { - _, err := db.write.Exec( + return db.KVDeleteContext(context.Background(), functionID, key) +} + +func (db *Database) KVDeleteContext(ctx context.Context, functionID, key string) (err error) { + started := time.Now() + defer func() { db.observeKV("delete", started, err) }() + if err := ValidateKVKey(key); err != nil { + return err + } + _, err = db.write.ExecContext(ctx, `DELETE FROM kv_store WHERE function_id = ? AND key = ?`, functionID, key) return err @@ -78,23 +163,28 @@ func (db *Database) KVDelete(functionID, key string) error { // KVList returns up to `limit` keys for a function, optionally filtered by // prefix. Skips expired entries. Used by the dashboard / introspection. func (db *Database) KVList(functionID, prefix string, limit int) ([]*KVEntry, error) { + return db.KVListContext(context.Background(), functionID, prefix, limit) +} + +func (db *Database) KVListContext(ctx context.Context, functionID, prefix string, limit int) (entries []*KVEntry, err error) { + started := time.Now() + defer func() { db.observeKV("list", started, err) }() if limit <= 0 || limit > 1000 { limit = 100 } var ( rows *sql.Rows - err error now = time.Now().UTC() ) if prefix != "" { - rows, err = db.read.Query(` + rows, err = db.read.QueryContext(ctx, ` SELECT function_id, key, value, expires_at, created_at, updated_at FROM kv_store WHERE function_id = ? AND key LIKE ? AND (expires_at IS NULL OR expires_at > ?) ORDER BY key ASC LIMIT ?`, functionID, prefix+"%", now, limit) } else { - rows, err = db.read.Query(` + rows, err = db.read.QueryContext(ctx, ` SELECT function_id, key, value, expires_at, created_at, updated_at FROM kv_store WHERE function_id = ? AND (expires_at IS NULL OR expires_at > ?) @@ -126,8 +216,12 @@ func (db *Database) KVList(functionID, prefix string, limit int) ([]*KVEntry, er // Used by the dashboard "X keys" badge so the operator sees the namespace // size at a glance without having to page through the full list. func (db *Database) KVCount(functionID string) (int, error) { + return db.KVCountContext(context.Background(), functionID) +} + +func (db *Database) KVCountContext(ctx context.Context, functionID string) (int, error) { var n int - err := db.read.QueryRow(` + err := db.read.QueryRowContext(ctx, ` SELECT COUNT(*) FROM kv_store WHERE function_id = ? AND (expires_at IS NULL OR expires_at > ?)`, functionID, time.Now().UTC(), @@ -168,6 +262,12 @@ type KVListPage struct { // page. This keeps the cursor a stable, exclusive `key > cursor` boundary // (no offset arithmetic, safe under concurrent inserts). func (db *Database) KVListWithCursor(functionID, prefix, cursor string, limit int) (*KVListPage, error) { + return db.KVListWithCursorContext(context.Background(), functionID, prefix, cursor, limit) +} + +func (db *Database) KVListWithCursorContext(ctx context.Context, functionID, prefix, cursor string, limit int) (pageResult *KVListPage, err error) { + started := time.Now() + defer func() { db.observeKV("list", started, err) }() if limit <= 0 || limit > 1000 { limit = 100 } @@ -176,11 +276,10 @@ func (db *Database) KVListWithCursor(functionID, prefix, cursor string, limit in var ( rows *sql.Rows - err error ) switch { case prefix != "" && cursor != "": - rows, err = db.read.Query(` + rows, err = db.read.QueryContext(ctx, ` SELECT function_id, key, value, expires_at, created_at, updated_at FROM kv_store WHERE function_id = ? AND key LIKE ? AND key > ? @@ -188,7 +287,7 @@ func (db *Database) KVListWithCursor(functionID, prefix, cursor string, limit in ORDER BY key ASC LIMIT ?`, functionID, prefix+"%", cursor, now, probe) case prefix != "": - rows, err = db.read.Query(` + rows, err = db.read.QueryContext(ctx, ` SELECT function_id, key, value, expires_at, created_at, updated_at FROM kv_store WHERE function_id = ? AND key LIKE ? @@ -196,7 +295,7 @@ func (db *Database) KVListWithCursor(functionID, prefix, cursor string, limit in ORDER BY key ASC LIMIT ?`, functionID, prefix+"%", now, probe) case cursor != "": - rows, err = db.read.Query(` + rows, err = db.read.QueryContext(ctx, ` SELECT function_id, key, value, expires_at, created_at, updated_at FROM kv_store WHERE function_id = ? AND key > ? @@ -204,7 +303,7 @@ func (db *Database) KVListWithCursor(functionID, prefix, cursor string, limit in ORDER BY key ASC LIMIT ?`, functionID, cursor, now, probe) default: - rows, err = db.read.Query(` + rows, err = db.read.QueryContext(ctx, ` SELECT function_id, key, value, expires_at, created_at, updated_at FROM kv_store WHERE function_id = ? @@ -249,10 +348,22 @@ func (db *Database) KVListWithCursor(functionID, prefix, cursor string, limit in // see consistent counters. Missing keys are treated as 0. A value that // is not a JSON-integer returns an error. // -// ttlSeconds: 0 = leave any existing TTL untouched; positive = set/refresh -// expiry to now+ttl; negative is clamped to 0. -func (db *Database) KVIncr(functionID, key string, delta int64, ttlSeconds int) (int64, error) { - tx, err := db.write.Begin() +// A nil TTL preserves an existing expiry (and makes new keys persistent), +// zero clears expiry, and a positive value sets or refreshes it. +func (db *Database) KVIncr(functionID, key string, delta int64, ttlSeconds *int) (int64, error) { + return db.KVIncrContext(context.Background(), functionID, key, delta, ttlSeconds) +} + +func (db *Database) KVIncrContext(ctx context.Context, functionID, key string, delta int64, ttlSeconds *int) (value int64, err error) { + started := time.Now() + defer func() { db.observeKV("incr", started, err) }() + if err := ValidateKVKey(key); err != nil { + return 0, err + } + if err := ValidateKVTTL(ttlSeconds); err != nil { + return 0, err + } + tx, err := db.write.BeginTx(ctx, nil) if err != nil { return 0, err } @@ -262,7 +373,7 @@ func (db *Database) KVIncr(functionID, key string, delta int64, ttlSeconds int) raw []byte expires sql.NullTime ) - err = tx.QueryRow( + err = tx.QueryRowContext(ctx, `SELECT value, expires_at FROM kv_store WHERE function_id = ? AND key = ?`, functionID, key, ).Scan(&raw, &expires) @@ -291,21 +402,21 @@ func (db *Database) KVIncr(functionID, key string, delta int64, ttlSeconds int) var expiresVal any switch { - case ttlSeconds > 0: - expiresVal = now.Add(time.Duration(ttlSeconds) * time.Second) - case ttlSeconds == 0 && exists && !expired && expires.Valid: + case ttlSeconds != nil && *ttlSeconds > 0: + expiresVal = now.Add(time.Duration(*ttlSeconds) * time.Second) + case ttlSeconds == nil && exists && !expired && expires.Valid: // Preserve any existing TTL. expiresVal = expires.Time } newBytes := []byte(fmtIntStr(next)) if exists { - _, err = tx.Exec(` + _, err = tx.ExecContext(ctx, ` UPDATE kv_store SET value = ?, expires_at = ?, updated_at = ? WHERE function_id = ? AND key = ?`, newBytes, expiresVal, now, functionID, key) } else { - _, err = tx.Exec(` + _, err = tx.ExecContext(ctx, ` INSERT INTO kv_store (function_id, key, value, expires_at, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)`, functionID, key, newBytes, expiresVal, now, now) @@ -327,9 +438,29 @@ func (db *Database) KVIncr(functionID, key string, delta int64, ttlSeconds int) // // A nil `expected` means "the key must not currently exist" (a guarded // insert). A successful CAS with positive ttlSeconds sets/refreshes the -// expiry; ttlSeconds == 0 preserves any existing TTL. -func (db *Database) KVCAS(functionID, key string, expected, newValue []byte, ttlSeconds int) (bool, []byte, error) { - tx, err := db.write.Begin() +// expiry; nil preserves an existing TTL and zero clears it. +func (db *Database) KVCAS(functionID, key string, expected, newValue []byte, ttlSeconds *int) (bool, []byte, error) { + return db.KVCASContext(context.Background(), functionID, key, expected, newValue, ttlSeconds) +} + +func (db *Database) KVCASContext(ctx context.Context, functionID, key string, expected, newValue []byte, ttlSeconds *int) (swapped bool, currentValue []byte, err error) { + started := time.Now() + defer func() { db.observeKV("cas", started, err) }() + if err := ValidateKVKey(key); err != nil { + return false, nil, err + } + if err := ValidateKVValue(newValue); err != nil { + return false, nil, err + } + if expected != nil { + if err := ValidateKVValue(expected); err != nil { + return false, nil, errors.New("kv: expected value is invalid: " + err.Error()) + } + } + if err := ValidateKVTTL(ttlSeconds); err != nil { + return false, nil, err + } + tx, err := db.write.BeginTx(ctx, nil) if err != nil { return false, nil, err } @@ -339,7 +470,7 @@ func (db *Database) KVCAS(functionID, key string, expected, newValue []byte, ttl raw []byte expires sql.NullTime ) - err = tx.QueryRow( + err = tx.QueryRowContext(ctx, `SELECT value, expires_at FROM kv_store WHERE function_id = ? AND key = ?`, functionID, key, ).Scan(&raw, &expires) @@ -371,19 +502,19 @@ func (db *Database) KVCAS(functionID, key string, expected, newValue []byte, ttl var expiresVal any switch { - case ttlSeconds > 0: - expiresVal = now.Add(time.Duration(ttlSeconds) * time.Second) - case ttlSeconds == 0 && currentlyPresent && expires.Valid: + case ttlSeconds != nil && *ttlSeconds > 0: + expiresVal = now.Add(time.Duration(*ttlSeconds) * time.Second) + case ttlSeconds == nil && currentlyPresent && expires.Valid: expiresVal = expires.Time } if exists { - _, err = tx.Exec(` + _, err = tx.ExecContext(ctx, ` UPDATE kv_store SET value = ?, expires_at = ?, updated_at = ? WHERE function_id = ? AND key = ?`, newValue, expiresVal, now, functionID, key) } else { - _, err = tx.Exec(` + _, err = tx.ExecContext(ctx, ` INSERT INTO kv_store (function_id, key, value, expires_at, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)`, functionID, key, newValue, expiresVal, now, now) @@ -404,14 +535,12 @@ type KVBatchOp struct { Op string `json:"op"` Key string `json:"key"` Value []byte `json:"value,omitempty"` - TTLSeconds int `json:"ttl_seconds,omitempty"` + TTLSeconds *int `json:"ttl_seconds,omitempty"` } -// KVBatchResult is the per-op outcome. For "get": Value and ExpiresAt -// populated when the key existed (Found=true). For "put"/"delete": only -// Found is meaningful (always true for "put"; true if the row was -// actually removed for "delete"). Err carries a per-op error so a single -// bad item doesn't fail the whole batch on the client. +// KVBatchResult is the per-op outcome. For "get": Value and ExpiresAt are +// populated when the key existed. For "put"/"delete", Found reports whether +// the write affected a key. Validation and database failures abort the batch. type KVBatchResult struct { Op string `json:"op"` Key string `json:"key"` @@ -421,20 +550,52 @@ type KVBatchResult struct { Err string `json:"error,omitempty"` } -// KVBatch executes the given ops in a single write transaction. Order is -// preserved in the result slice. Per-op errors are surfaced in the result -// rather than aborting the batch — callers can choose their own retry -// strategy. +// KVBatch executes the given ops atomically in a single write transaction. +// Every operation is validated before the transaction starts and any database +// failure rolls back all writes. Result order matches operation order. func (db *Database) KVBatch(functionID string, ops []KVBatchOp) ([]KVBatchResult, error) { + return db.KVBatchContext(context.Background(), functionID, ops) +} + +func (db *Database) KVBatchContext(ctx context.Context, functionID string, ops []KVBatchOp) (batchResults []KVBatchResult, err error) { + started := time.Now() + txStarted := false + defer func() { + db.observeKV("batch", started, err) + if txStarted && err != nil { + db.kv.rollbacks.Add(1) + } + }() results := make([]KVBatchResult, len(ops)) if len(ops) == 0 { return results, nil } + if len(ops) > 100 { + return nil, errors.New("kv batch: capped at 100 operations") + } + for _, op := range ops { + if err := ValidateKVKey(op.Key); err != nil { + return nil, err + } + switch op.Op { + case "get", "delete": + case "put": + if err := ValidateKVValue(op.Value); err != nil { + return nil, err + } + if err := ValidateKVTTL(op.TTLSeconds); err != nil { + return nil, err + } + default: + return nil, errors.New("kv batch: unknown op: " + op.Op) + } + } - tx, err := db.write.Begin() + tx, err := db.write.BeginTx(ctx, nil) if err != nil { return nil, err } + txStarted = true defer func() { _ = tx.Rollback() }() now := time.Now().UTC() @@ -444,14 +605,14 @@ func (db *Database) KVBatch(functionID string, ops []KVBatchOp) ([]KVBatchResult case "get": var raw []byte var expires sql.NullTime - err := tx.QueryRow( + err := tx.QueryRowContext(ctx, `SELECT value, expires_at FROM kv_store WHERE function_id = ? AND key = ?`, functionID, op.Key, ).Scan(&raw, &expires) if err == sql.ErrNoRows { // Found = false, no value. } else if err != nil { - r.Err = err.Error() + return nil, err } else { if expires.Valid && expires.Time.Before(now) { // Expired — treat as not found. @@ -466,33 +627,41 @@ func (db *Database) KVBatch(functionID string, ops []KVBatchOp) ([]KVBatchResult } case "put": var expiresVal any - if op.TTLSeconds > 0 { - expiresVal = now.Add(time.Duration(op.TTLSeconds) * time.Second) + specified := 0 + if op.TTLSeconds != nil { + specified = 1 + if *op.TTLSeconds > 0 { + expiresVal = now.Add(time.Duration(*op.TTLSeconds) * time.Second) + } } - _, err := tx.Exec(` + _, err := tx.ExecContext(ctx, ` INSERT INTO kv_store (function_id, key, value, expires_at, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT(function_id, key) DO UPDATE SET value = excluded.value, - expires_at = excluded.expires_at, - updated_at = excluded.updated_at`, - functionID, op.Key, op.Value, expiresVal, now, now) + expires_at = CASE + WHEN ? = 1 THEN excluded.expires_at + WHEN kv_store.expires_at IS NOT NULL AND kv_store.expires_at <= ? THEN NULL + ELSE kv_store.expires_at + END, + updated_at = excluded.updated_at`, + functionID, op.Key, op.Value, expiresVal, now, now, specified, now) if err != nil { - r.Err = err.Error() + return nil, err } else { r.Found = true } case "delete": - res, err := tx.Exec( + res, err := tx.ExecContext(ctx, `DELETE FROM kv_store WHERE function_id = ? AND key = ?`, functionID, op.Key) if err != nil { - r.Err = err.Error() + return nil, err } else if n, _ := res.RowsAffected(); n > 0 { r.Found = true } default: - r.Err = "unknown op: " + op.Op + return nil, errors.New("kv batch: unknown op: " + op.Op) } results[i] = r } diff --git a/backend/internal/database/kv_atomic_test.go b/backend/internal/database/kv_atomic_test.go index 9fb9d0da..43be43d3 100644 --- a/backend/internal/database/kv_atomic_test.go +++ b/backend/internal/database/kv_atomic_test.go @@ -1,6 +1,11 @@ package database import ( + "context" + "errors" + "path/filepath" + "strings" + "sync" "testing" "time" ) @@ -25,21 +30,21 @@ func TestKVIncr(t *testing.T) { kvTestFn(t, db, "fn_kvincr12345") // Fresh key is created at the delta. - if v, err := db.KVIncr("fn_kvincr12345", "n", 1, 0); err != nil || v != 1 { + if v, err := db.KVIncr("fn_kvincr12345", "n", 1, nil); err != nil || v != 1 { t.Fatalf("first incr: v=%d err=%v (want 1, nil)", v, err) } - if v, err := db.KVIncr("fn_kvincr12345", "n", 5, 0); err != nil || v != 6 { + if v, err := db.KVIncr("fn_kvincr12345", "n", 5, nil); err != nil || v != 6 { t.Fatalf("second incr: v=%d err=%v (want 6, nil)", v, err) } - if v, err := db.KVIncr("fn_kvincr12345", "n", -2, 0); err != nil || v != 4 { + if v, err := db.KVIncr("fn_kvincr12345", "n", -2, nil); err != nil || v != 4 { t.Fatalf("negative incr: v=%d err=%v (want 4, nil)", v, err) } // A non-integer value cannot be incremented. - if err := db.KVPut("fn_kvincr12345", "s", []byte(`"abc"`), 0); err != nil { + if err := db.KVPut("fn_kvincr12345", "s", []byte(`"abc"`), nil); err != nil { t.Fatal(err) } - if _, err := db.KVIncr("fn_kvincr12345", "s", 1, 0); err == nil { + if _, err := db.KVIncr("fn_kvincr12345", "s", 1, nil); err == nil { t.Error("expected incr on a non-integer value to error") } } @@ -52,11 +57,11 @@ func TestKVCAS(t *testing.T) { kvTestFn(t, db, "fn_kvcas123456") // Insert-if-absent: expected=nil on a missing key swaps. - if ok, _, err := db.KVCAS("fn_kvcas123456", "lock", nil, []byte(`"held"`), 0); err != nil || !ok { + if ok, _, err := db.KVCAS("fn_kvcas123456", "lock", nil, []byte(`"held"`), nil); err != nil || !ok { t.Fatalf("insert-if-absent: ok=%v err=%v (want true, nil)", ok, err) } // expected=nil on an existing key fails (already held), returns current. - ok, current, err := db.KVCAS("fn_kvcas123456", "lock", nil, []byte(`"other"`), 0) + ok, current, err := db.KVCAS("fn_kvcas123456", "lock", nil, []byte(`"other"`), nil) if err != nil || ok { t.Fatalf("insert-if-absent on existing: ok=%v err=%v (want false, nil)", ok, err) } @@ -64,11 +69,11 @@ func TestKVCAS(t *testing.T) { t.Errorf("expected current=%q, got %q", `"held"`, string(current)) } // Matching expected swaps. - if ok, _, err := db.KVCAS("fn_kvcas123456", "lock", []byte(`"held"`), []byte(`"taken"`), 0); err != nil || !ok { + if ok, _, err := db.KVCAS("fn_kvcas123456", "lock", []byte(`"held"`), []byte(`"taken"`), nil); err != nil || !ok { t.Fatalf("matching cas: ok=%v err=%v (want true, nil)", ok, err) } // Stale expected fails and returns the new current. - ok, current, err = db.KVCAS("fn_kvcas123456", "lock", []byte(`"held"`), []byte(`"z"`), 0) + ok, current, err = db.KVCAS("fn_kvcas123456", "lock", []byte(`"held"`), []byte(`"z"`), nil) if err != nil || ok { t.Fatalf("stale cas: ok=%v err=%v (want false, nil)", ok, err) } @@ -77,6 +82,174 @@ func TestKVCAS(t *testing.T) { } } +func TestKVValidationBoundaries(t *testing.T) { + db := newTestDB(t) + kvTestFn(t, db, "fn_kvlimits123") + + key256 := strings.Repeat("界", 256) + if err := db.KVPut("fn_kvlimits123", key256, []byte(`null`), nil); err != nil { + t.Fatalf("256-character key rejected: %v", err) + } + if err := db.KVPut("fn_kvlimits123", key256+"x", []byte(`null`), nil); err == nil { + t.Fatal("257-character key accepted") + } + value64KiB := []byte(`"` + strings.Repeat("x", KVMaxValueBytes-2) + `"`) + if err := db.KVPut("fn_kvlimits123", "max", value64KiB, nil); err != nil { + t.Fatalf("64 KiB value rejected: %v", err) + } + if err := db.KVPut("fn_kvlimits123", "too-big", append(value64KiB, ' '), nil); err == nil { + t.Fatal("value larger than 64 KiB accepted") + } + if err := db.KVPut("fn_kvlimits123", "bad-json", []byte(`{"broken"`), nil); err == nil { + t.Fatal("malformed JSON accepted") + } +} + +func TestKVTTLOmitClearAndRefresh(t *testing.T) { + db := newTestDB(t) + kvTestFn(t, db, "fn_kvttl123456") + ttl100 := 100 + if err := db.KVPut("fn_kvttl123456", "key", []byte(`1`), &ttl100); err != nil { + t.Fatal(err) + } + first, err := db.KVGet("fn_kvttl123456", "key") + if err != nil || first.ExpiresAt == nil { + t.Fatalf("initial expiry missing: entry=%v err=%v", first, err) + } + if err := db.KVPut("fn_kvttl123456", "key", []byte(`2`), nil); err != nil { + t.Fatal(err) + } + preserved, _ := db.KVGet("fn_kvttl123456", "key") + if preserved.ExpiresAt == nil || !preserved.ExpiresAt.Equal(*first.ExpiresAt) { + t.Fatalf("omitted TTL did not preserve expiry: first=%v next=%v", first.ExpiresAt, preserved.ExpiresAt) + } + ttl200 := 200 + if err := db.KVPut("fn_kvttl123456", "key", []byte(`3`), &ttl200); err != nil { + t.Fatal(err) + } + refreshed, _ := db.KVGet("fn_kvttl123456", "key") + if refreshed.ExpiresAt == nil || !refreshed.ExpiresAt.After(*preserved.ExpiresAt) { + t.Fatalf("positive TTL did not refresh expiry: old=%v new=%v", preserved.ExpiresAt, refreshed.ExpiresAt) + } + zero := 0 + if err := db.KVPut("fn_kvttl123456", "key", []byte(`4`), &zero); err != nil { + t.Fatal(err) + } + cleared, _ := db.KVGet("fn_kvttl123456", "key") + if cleared.ExpiresAt != nil { + t.Fatalf("explicit zero did not clear expiry: %v", cleared.ExpiresAt) + } + negative := -1 + if err := db.KVPut("fn_kvttl123456", "key", []byte(`5`), &negative); err == nil { + t.Fatal("negative TTL accepted") + } +} + +func TestKVBatchRollsBackDatabaseFailure(t *testing.T) { + db := newTestDB(t) + kvTestFn(t, db, "fn_kvbatch1234") + if _, err := db.write.Exec(`CREATE TRIGGER reject_boom BEFORE INSERT ON kv_store WHEN NEW.key = 'boom' BEGIN SELECT RAISE(ABORT, 'boom'); END`); err != nil { + t.Fatal(err) + } + ops := []KVBatchOp{ + {Op: "put", Key: "first", Value: []byte(`1`)}, + {Op: "put", Key: "boom", Value: []byte(`2`)}, + } + if _, err := db.KVBatch("fn_kvbatch1234", ops); err == nil { + t.Fatal("batch database failure was not returned") + } + if _, err := db.KVGet("fn_kvbatch1234", "first"); !errors.Is(err, ErrKVNotFound) { + t.Fatalf("first write survived rollback: %v", err) + } + if got := db.KVMetrics().Rollbacks; got != 1 { + t.Fatalf("rollback metric=%d, want 1", got) + } +} + +func TestKVConcurrentIncrements(t *testing.T) { + db := newTestDB(t) + kvTestFn(t, db, "fn_kvconcurrent") + const increments = 1000 + errs := make(chan error, increments) + var wg sync.WaitGroup + for i := 0; i < increments; i++ { + wg.Add(1) + go func() { + defer wg.Done() + _, err := db.KVIncr("fn_kvconcurrent", "counter", 1, nil) + errs <- err + }() + } + wg.Wait() + close(errs) + for err := range errs { + if err != nil { + t.Fatal(err) + } + } + entry, err := db.KVGet("fn_kvconcurrent", "counter") + if err != nil { + t.Fatal(err) + } + if string(entry.Value) != "1000" { + t.Fatalf("counter=%s, want 1000", entry.Value) + } +} + +func TestKVContextCancellation(t *testing.T) { + db := newTestDB(t) + kvTestFn(t, db, "fn_kvcancel1234") + ctx, cancel := context.WithCancel(context.Background()) + cancel() + if err := db.KVPutContext(ctx, "fn_kvcancel1234", "key", []byte(`1`), nil); !errors.Is(err, context.Canceled) { + t.Fatalf("KVPutContext err=%v, want context.Canceled", err) + } + stats := db.KVMetrics() + if stats.Operations[1].Timeouts != 1 { + t.Fatalf("put timeouts=%d, want 1", stats.Operations[1].Timeouts) + } +} + +func TestKVPersistsAcrossRestart(t *testing.T) { + path := filepath.Join(t.TempDir(), "persist.db") + db, err := New(path) + if err != nil { + t.Fatal(err) + } + if err := db.Migrate(); err != nil { + t.Fatal(err) + } + fn := &Function{ + ID: "fn_kvpersist123", Name: "fn-kv-persist", Runtime: "node", Entrypoint: "handler.js", + TimeoutMS: 30000, MemoryMB: 64, CPUs: 0.5, EnvVars: map[string]string{}, + NetworkMode: "none", Status: "active", + } + if err := db.InsertFunction(fn); err != nil { + t.Fatal(err) + } + if err := db.KVPut("fn_kvpersist123", "key", []byte(`{"ok":true}`), nil); err != nil { + t.Fatal(err) + } + if err := db.Close(); err != nil { + t.Fatal(err) + } + db, err = New(path) + if err != nil { + t.Fatal(err) + } + defer db.Close() + if err := db.Migrate(); err != nil { + t.Fatal(err) + } + entry, err := db.KVGet("fn_kvpersist123", "key") + if err != nil { + t.Fatal(err) + } + if string(entry.Value) != `{"ok":true}` { + t.Fatalf("persisted entry=%s", entry.Value) + } +} + // TestCountDeploymentsForFunction pins the COUNT(*) added so the deployments // list response can carry a truncation signal. func TestCountDeploymentsForFunction(t *testing.T) { diff --git a/backend/internal/database/kv_metrics.go b/backend/internal/database/kv_metrics.go new file mode 100644 index 00000000..d2db2330 --- /dev/null +++ b/backend/internal/database/kv_metrics.go @@ -0,0 +1,72 @@ +package database + +import ( + "context" + "errors" + "sync/atomic" + "time" +) + +var kvOperationNames = [...]string{"get", "put", "delete", "list", "incr", "cas", "batch"} + +type kvOperationMetric struct { + count atomic.Uint64 + errors atomic.Uint64 + timeouts atomic.Uint64 + latencyNS atomic.Uint64 +} + +type kvMetrics struct { + operations [len(kvOperationNames)]kvOperationMetric + rollbacks atomic.Uint64 +} + +type KVOperationStats struct { + Operation string + Count uint64 + Errors uint64 + Timeouts uint64 + LatencyTotalNS uint64 +} + +type KVMetricsSnapshot struct { + Operations []KVOperationStats + Rollbacks uint64 +} + +func kvOperationIndex(operation string) int { + for i, name := range kvOperationNames { + if name == operation { + return i + } + } + return -1 +} + +func (db *Database) observeKV(operation string, started time.Time, err error) { + i := kvOperationIndex(operation) + if i < 0 { + return + } + metric := &db.kv.operations[i] + metric.count.Add(1) + metric.latencyNS.Add(uint64(time.Since(started))) + if err != nil && !errors.Is(err, ErrKVNotFound) { + metric.errors.Add(1) + } + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + metric.timeouts.Add(1) + } +} + +func (db *Database) KVMetrics() KVMetricsSnapshot { + out := KVMetricsSnapshot{Operations: make([]KVOperationStats, 0, len(kvOperationNames)), Rollbacks: db.kv.rollbacks.Load()} + for i, name := range kvOperationNames { + metric := &db.kv.operations[i] + out.Operations = append(out.Operations, KVOperationStats{ + Operation: name, Count: metric.count.Load(), Errors: metric.errors.Load(), + Timeouts: metric.timeouts.Load(), LatencyTotalNS: metric.latencyNS.Load(), + }) + } + return out +} diff --git a/backend/internal/database/log_entries.go b/backend/internal/database/log_entries.go index cdc8ab8c..35bd847d 100644 --- a/backend/internal/database/log_entries.go +++ b/backend/internal/database/log_entries.go @@ -21,8 +21,8 @@ type LogEntry struct { } // AsyncInsertLogEntry queues an insert into execution_log_entries. The -// async writer batches these alongside execution finalization, keeping -// log writes off the hot proxy path. +// telemetry writer batches these on its own bounded, droppable queue, keeping +// log writes off the hot proxy path without delaying critical execution rows. func (db *Database) AsyncInsertLogEntry(e *LogEntry) { if e.TS.IsZero() { e.TS = time.Now().UTC() @@ -30,7 +30,7 @@ func (db *Database) AsyncInsertLogEntry(e *LogEntry) { if e.Level == "" { e.Level = "info" } - db.AsyncExec(` + db.AsyncExecTelemetry(` INSERT INTO execution_log_entries (execution_id, trace_id, span_id, ts, level, message, fields) VALUES (?, ?, ?, ?, ?, ?, ?)`, diff --git a/backend/internal/database/migrations.go b/backend/internal/database/migrations.go index 2ba26cef..813115ec 100644 --- a/backend/internal/database/migrations.go +++ b/backend/internal/database/migrations.go @@ -856,8 +856,8 @@ PRAGMA foreign_keys = ON; // Kick off the batched async writer now that the schema exists. Safe // to call multiple times — Migrate is idempotent and we only start the - // writer if it's nil. Tests that don't call Migrate continue to use - // the goroutine-per-call fallback in Async(). + // writer if it's nil. Tests that don't call Migrate use direct bounded + // writes without spawning overflow goroutines. if db.writer == nil { db.writer = newAsyncWriter(db) db.writer.start() diff --git a/backend/internal/database/spans.go b/backend/internal/database/spans.go index c4a31b0f..e7a628b0 100644 --- a/backend/internal/database/spans.go +++ b/backend/internal/database/spans.go @@ -12,17 +12,17 @@ import ( // `executions` still carries the canonical "this execution ran" span; // user_spans rows describe substages inside that execution. type UserSpan struct { - ID string `json:"id"` - TraceID string `json:"trace_id"` - ParentSpanID string `json:"parent_span_id"` - ExecutionID string `json:"execution_id"` - Name string `json:"name"` - StartedAt time.Time `json:"started_at"` - DurationMS int64 `json:"duration_ms"` - Attributes string `json:"attributes,omitempty"` - Status string `json:"status"` - ErrorMessage string `json:"error_message,omitempty"` - OffsetMS int64 `json:"offset_ms"` + ID string `json:"id"` + TraceID string `json:"trace_id"` + ParentSpanID string `json:"parent_span_id"` + ExecutionID string `json:"execution_id"` + Name string `json:"name"` + StartedAt time.Time `json:"started_at"` + DurationMS int64 `json:"duration_ms"` + Attributes string `json:"attributes,omitempty"` + Status string `json:"status"` + ErrorMessage string `json:"error_message,omitempty"` + OffsetMS int64 `json:"offset_ms"` } // AsyncInsertUserSpan queues an insert of a user-defined span. The @@ -36,7 +36,7 @@ func (db *Database) AsyncInsertUserSpan(s *UserSpan) { if s.Status == "" { s.Status = "ok" } - db.AsyncExec(` + db.AsyncExecTelemetry(` INSERT INTO user_spans (id, trace_id, parent_span_id, execution_id, name, started_at, duration_ms, attributes, status, error_message, offset_ms) diff --git a/backend/internal/mcp/reference.md b/backend/internal/mcp/reference.md index 200444de..abf7d458 100644 --- a/backend/internal/mcp/reference.md +++ b/backend/internal/mcp/reference.md @@ -182,9 +182,9 @@ API. It ships beside the runtime adapter — `require('orva')` (Node) and install`. The Node SDK ships TypeScript declarations (`orva.d.ts`); the Python SDK ships a `py.typed` marker so IDEs surface full type hints. -Surface, as of v0.6: +Surface, as of v0.7: -- **`kv`** — `get` / `put` / `delete` / `list(cursor)` / `getMany` / `putMany` / `deleteMany` / `incr` / `cas`. Per-function namespace, optional TTL, single-RTT batch ops, atomic counter & CAS. +- **`kv`** — `get` / `put` / `delete` / `list(cursor)` / `getMany` / `putMany` / `deleteMany` / `incr` / `cas`. Per-function namespace, optional TTL, all-or-nothing batch ops, atomic counter & CAS. Keys are non-empty UTF-8 up to 256 characters; JSON values are capped at 64 KiB; batches are capped at 100 operations. - **`invoke(name, payload, {timeoutMs})`** — synchronous F2F call with `{statusCode, headers, body}` envelope. 8-deep call cap. - **`invokeStream(name, payload, {timeoutMs})`** — same, but yields `Uint8Array` chunks via `for await`. - **`jobs.enqueue(name, payload, {idempotencyKey, maxAttempts, scheduledAt})`** — durable background queue with built-in dedup. @@ -204,7 +204,8 @@ Surface, as of v0.6: from orva import kv def handler(event): - # Store with optional TTL (seconds). 0 = no expiry. + # Omit TTL to preserve an existing expiry (new keys are persistent). + # Pass 0 to clear expiry, or a positive number to set/refresh it. kv.put("user:42", {"name": "Ada", "tier": "pro"}, ttl_seconds=3600) # Read; default returned if missing or expired. @@ -225,6 +226,8 @@ def handler(event): const { kv } = require('orva') exports.handler = async (event) => { + // Omit ttlSeconds to preserve expiry, use 0 to clear it, or a + // positive number to set/refresh it. await kv.put('user:42', { name: 'Ada', tier: 'pro' }, { ttlSeconds: 3600 }) const user = await kv.get('user:42', null) @@ -242,6 +245,10 @@ exports.handler = async (event) => { > `GET/PUT/DELETE /api/v1/functions//kv[/]`. MCP tools: > `kv_list` / `kv_get` / `kv_put` / `kv_delete`. +> KV batches are atomic: validation or storage failure rejects the request and +> rolls back every write. The SDK's `getMany`, `putMany`, and `deleteMany` +> throw `OrvaError` when the batch fails; they never report partial success. + ### Function-to-function — invoke() ### F2F — Python @@ -1103,17 +1110,17 @@ Secrets are stored encrypted at rest, decrypted only into the worker environment -Every function has the `orva` module pre-imported — zero install, zero config. The SDK reaches orvad over the bridge network using HTTP, so the function MUST be created with `network_mode: "egress"` (or updated to it later). With the default `network_mode: "none"`, every SDK call fails at runtime with `ENETUNREACH` / `OrvaUnavailableError` / `TypeError: fetch failed`. THREE primitives: +Every function has the `orva` module pre-imported — zero install, zero config. The SDK reaches orvad over the bridge network using HTTP, so the function MUST be created with `network_mode: "egress"` (or updated to it later). With the default `network_mode: "none"`, every SDK call fails at runtime with `ENETUNREACH` / `OrvaUnavailableError` / `TypeError: fetch failed`. Core primitives: ## orva.kv — per-function key/value store on SQLite -Per-function namespace; keys never collide across functions. Optional TTL in seconds (sweep every 5 min AND filtered at read time, so stale reads are impossible). Values JSON-serialised; cap 64 KB per value. Keys cap 256 chars. Use for: caches, idempotency keys, rate-limit counters, light session state, feature flags, last-seen markers. NOT a primary database, NOT a queue, NOT for blob storage. +Per-function namespace enforced by a process-signed, function-scoped SDK credential; caller identity never comes from a request header. Keys never collide across functions. TTL is optional: omit it to preserve an existing expiry (new keys are persistent), pass 0 to clear expiry, or pass a positive value to set/refresh it; negative values are rejected. Expiry is filtered at read time and swept every 5 minutes. Values must be valid JSON and are capped at 64 KiB; keys must be non-empty UTF-8 and are capped at 256 characters. Batches are all-or-nothing and capped at 100 operations. Use for: caches, idempotency keys, rate-limit counters, light session state, feature flags, last-seen markers. NOT a primary database, NOT a queue, NOT for blob storage. Python: from orva import kv kv.put("user:42", {"name": "Ada", "tier": "pro"}, ttl_seconds=3600) user = kv.get("user:42", default=None) # → dict, or None - pages = kv.list(prefix="page:", limit=50) # → [keys]; pass cursor= for pagination + pages = kv.list(prefix="page:", limit=50) # → {keys, next_cursor}; pass next_cursor back as cursor kv.delete("user:42") # idempotent; no error if missing Node: @@ -1144,8 +1151,8 @@ Tell the user about this when their function uses kv state and they might want to inspect it. kv.put(f"req:{idempotency_key}", "1", ttl_seconds=86400) -## orva.invoke — function-to-function calls (no HTTP, no auth) -Bypasses the proxy stack and dispatches via the warm pool. Faster than internal HTTP, no signing required. Recursion guard: max call depth 8. The callee's full {statusCode, headers, body} is returned; body is JSON-decoded when possible. +## orva.invoke — function-to-function calls +Uses the scoped internal SDK endpoint and dispatches through the warm pool; the signed credential supplies immutable caller attribution while the named target may be another function. Recursion guard: max call depth 8. The callee's full {statusCode, headers, body} is returned; body is JSON-decoded when possible. Python: from orva import invoke, OrvaError @@ -1372,7 +1379,7 @@ Every night at 3 AM UTC, delete all KV keys that start with "session:" and are o -**Plan.** Python 3.14 handler triggered by a cron schedule `0 3 * * *` UTC. Lists keys with prefix "session:" via orva.kv.list, page by page, and lets the TTL-based sweep handle expiration — but for keys we want gone NOW, we delete explicitly. No deps. No egress. Recommended auth: leave public; cron triggers don't go through the HTTP gate, but if anyone could hit it from outside, set platform_key. +**Plan.** Python 3.14 handler triggered by a cron schedule `0 3 * * *` UTC. Lists keys with prefix "session:" via orva.kv.list, page by page, and lets the TTL-based sweep handle expiration — but for keys we want gone NOW, we delete explicitly. No deps. Requires egress because the bundled KV SDK calls Orva's control plane. Recommended auth: leave public; cron triggers don't go through the HTTP gate, but if anyone could hit it from outside, set platform_key. ```python import json @@ -1385,10 +1392,10 @@ def handler(event): while True: page = kv.list(prefix="session:", limit=200, cursor=cursor) - for key in page.get("keys", []): - kv.delete(key) + for entry in page.get("keys", []): + kv.delete(entry["key"]) deleted += 1 - cursor = page.get("cursor") + cursor = page.get("next_cursor") if not cursor: break @@ -1405,7 +1412,7 @@ def handler(event): "body": {"deleted": deleted}} ``` -Wire it up: open Schedules → New Schedule → expression `0 3 * * *`, timezone UTC, function this one. Set auth_mode to platform_key on the function so the URL can't be triggered manually by random callers. +Wire it up: enable outbound network, then open Schedules → New Schedule → expression `0 3 * * *`, timezone UTC, function this one. Set auth_mode to platform_key on the function so the URL can't be triggered manually by random callers. diff --git a/backend/internal/mcp/tools_kv.go b/backend/internal/mcp/tools_kv.go index 0d4b702f..40d0f9d9 100644 --- a/backend/internal/mcp/tools_kv.go +++ b/backend/internal/mcp/tools_kv.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "errors" - "strings" "time" "github.com/Harsh-2002/Orva/backend/internal/database" @@ -123,11 +122,11 @@ type KVPutInput struct { FunctionID string `json:"function_id" jsonschema:"function id (UUID) or name owning this key"` Key string `json:"key"` Value KVValue `json:"value" jsonschema:"the value to store; populate one typed field matching the type field"` - TTLSeconds int `json:"ttl_seconds,omitempty" jsonschema:"0 (default) = no expiry; positive = expire after that many seconds"` + TTLSeconds *int `json:"ttl_seconds,omitempty" jsonschema:"omit to preserve an existing expiry; 0 removes expiry; positive expires after that many seconds"` } type KVPutOutput struct { Key string `json:"key"` - TTLSeconds int `json:"ttl_seconds"` + TTLSeconds *int `json:"ttl_seconds,omitempty"` } type KVDeleteInput struct { @@ -155,7 +154,7 @@ type KVIncrInput struct { FunctionID string `json:"function_id" jsonschema:"function id (UUID) or name owning this key"` Key string `json:"key"` Delta int64 `json:"delta,omitempty" jsonschema:"increment amount; default 1; negative decrements"` - TTLSeconds int `json:"ttl_seconds,omitempty" jsonschema:"0 = preserve existing TTL; positive = refresh expiry"` + TTLSeconds *int `json:"ttl_seconds,omitempty" jsonschema:"omit to preserve an existing expiry; 0 removes expiry; positive refreshes expiry"` } type KVIncrOutput struct { Value int64 `json:"value"` @@ -168,7 +167,7 @@ type KVCASInput struct { Key string `json:"key"` Expected KVValue `json:"expected" jsonschema:"current value to match before swapping; populate exactly one typed field"` New KVValue `json:"new" jsonschema:"value to install when Expected matches"` - TTLSeconds int `json:"ttl_seconds,omitempty"` + TTLSeconds *int `json:"ttl_seconds,omitempty" jsonschema:"omit to preserve an existing expiry; 0 removes expiry; positive refreshes expiry"` } type KVCASOutput struct { OK bool `json:"ok"` @@ -186,16 +185,16 @@ func registerKVTools(rc *regCtx) { Description: "Read a value from a function's per-namespace KV store. Returns found=false if the key is missing or has expired (TTL elapsed).", Annotations: &mcpsdk.ToolAnnotations{ReadOnlyHint: true, OpenWorldHint: ptrFalse()}, }, - func(_ context.Context, _ *mcpsdk.CallToolRequest, in KVGetInput) (*mcpsdk.CallToolResult, KVGetOutput, error) { + func(ctx context.Context, _ *mcpsdk.CallToolRequest, in KVGetInput) (*mcpsdk.CallToolResult, KVGetOutput, error) { fn, err := resolveFunction(deps, in.FunctionID) if err != nil { return nil, KVGetOutput{}, err } - key := strings.TrimSpace(in.Key) - if key == "" { - return nil, KVGetOutput{}, errors.New("key is required") + key := in.Key + if err := database.ValidateKVKey(key); err != nil { + return nil, KVGetOutput{}, err } - entry, err := deps.DB.KVGet(fn.ID, key) + entry, err := deps.DB.KVGetContext(ctx, fn.ID, key) if errors.Is(err, database.ErrKVNotFound) { return nil, KVGetOutput{Found: false}, nil } @@ -214,20 +213,20 @@ func registerKVTools(rc *regCtx) { Description: "Write a value to a function's KV store. value can be any JSON-serializable type; it's stored as JSON and returned by kv_get with the same shape. Optional ttl_seconds expires the key automatically.", Annotations: &mcpsdk.ToolAnnotations{IdempotentHint: true, OpenWorldHint: ptrFalse()}, }, - func(_ context.Context, _ *mcpsdk.CallToolRequest, in KVPutInput) (*mcpsdk.CallToolResult, KVPutOutput, error) { + func(ctx context.Context, _ *mcpsdk.CallToolRequest, in KVPutInput) (*mcpsdk.CallToolResult, KVPutOutput, error) { fn, err := resolveFunction(deps, in.FunctionID) if err != nil { return nil, KVPutOutput{}, err } - key := strings.TrimSpace(in.Key) - if key == "" { - return nil, KVPutOutput{}, errors.New("key is required") + key := in.Key + if err := database.ValidateKVKey(key); err != nil { + return nil, KVPutOutput{}, err } body, err := encodeKVValue(in.Value) if err != nil { return nil, KVPutOutput{}, err } - if err := deps.DB.KVPut(fn.ID, key, body, in.TTLSeconds); err != nil { + if err := deps.DB.KVPutContext(ctx, fn.ID, key, body, in.TTLSeconds); err != nil { return nil, KVPutOutput{}, err } return nil, KVPutOutput{Key: key, TTLSeconds: in.TTLSeconds}, nil @@ -241,12 +240,12 @@ func registerKVTools(rc *regCtx) { Description: "Remove a single key from a function's KV store. Idempotent — returns ok even if the key never existed.", Annotations: &mcpsdk.ToolAnnotations{IdempotentHint: true, DestructiveHint: ptrTrue(), OpenWorldHint: ptrFalse()}, }, - func(_ context.Context, _ *mcpsdk.CallToolRequest, in KVDeleteInput) (*mcpsdk.CallToolResult, KVDeleteOutput, error) { + func(ctx context.Context, _ *mcpsdk.CallToolRequest, in KVDeleteInput) (*mcpsdk.CallToolResult, KVDeleteOutput, error) { fn, err := resolveFunction(deps, in.FunctionID) if err != nil { return nil, KVDeleteOutput{}, err } - if err := deps.DB.KVDelete(fn.ID, in.Key); err != nil { + if err := deps.DB.KVDeleteContext(ctx, fn.ID, in.Key); err != nil { return nil, KVDeleteOutput{}, err } return nil, KVDeleteOutput{Status: "deleted", Key: in.Key}, nil @@ -260,12 +259,12 @@ func registerKVTools(rc *regCtx) { Description: "List a function's KV keys, optionally filtered by prefix. Useful for inspecting what state a function has accumulated. Expired keys are excluded.", Annotations: &mcpsdk.ToolAnnotations{ReadOnlyHint: true, OpenWorldHint: ptrFalse()}, }, - func(_ context.Context, _ *mcpsdk.CallToolRequest, in KVListInput) (*mcpsdk.CallToolResult, KVListOutput, error) { + func(ctx context.Context, _ *mcpsdk.CallToolRequest, in KVListInput) (*mcpsdk.CallToolResult, KVListOutput, error) { fn, err := resolveFunction(deps, in.FunctionID) if err != nil { return nil, KVListOutput{}, err } - page, err := deps.DB.KVListWithCursor(fn.ID, in.Prefix, in.Cursor, in.Limit) + page, err := deps.DB.KVListWithCursorContext(ctx, fn.ID, in.Prefix, in.Cursor, in.Limit) if err != nil { return nil, KVListOutput{}, err } @@ -287,20 +286,20 @@ func registerKVTools(rc *regCtx) { Description: "Atomically increment an integer counter. Missing keys are treated as 0; pass a negative delta to decrement. Use this instead of read+put when multiple writers can update the same counter concurrently.", Annotations: &mcpsdk.ToolAnnotations{IdempotentHint: false, OpenWorldHint: ptrFalse()}, }, - func(_ context.Context, _ *mcpsdk.CallToolRequest, in KVIncrInput) (*mcpsdk.CallToolResult, KVIncrOutput, error) { + func(ctx context.Context, _ *mcpsdk.CallToolRequest, in KVIncrInput) (*mcpsdk.CallToolResult, KVIncrOutput, error) { fn, err := resolveFunction(deps, in.FunctionID) if err != nil { return nil, KVIncrOutput{}, err } - key := strings.TrimSpace(in.Key) - if key == "" { - return nil, KVIncrOutput{}, errors.New("key is required") + key := in.Key + if err := database.ValidateKVKey(key); err != nil { + return nil, KVIncrOutput{}, err } delta := in.Delta if delta == 0 { delta = 1 } - next, err := deps.DB.KVIncr(fn.ID, key, delta, in.TTLSeconds) + next, err := deps.DB.KVIncrContext(ctx, fn.ID, key, delta, in.TTLSeconds) if err != nil { return nil, KVIncrOutput{}, err } @@ -315,14 +314,14 @@ func registerKVTools(rc *regCtx) { Description: "Atomically swap a key's value only if the current value matches Expected. Useful for safe read-modify-write loops where multiple writers could otherwise overwrite each other. Returns ok=false plus the current value when the precondition fails.", Annotations: &mcpsdk.ToolAnnotations{IdempotentHint: false, OpenWorldHint: ptrFalse()}, }, - func(_ context.Context, _ *mcpsdk.CallToolRequest, in KVCASInput) (*mcpsdk.CallToolResult, KVCASOutput, error) { + func(ctx context.Context, _ *mcpsdk.CallToolRequest, in KVCASInput) (*mcpsdk.CallToolResult, KVCASOutput, error) { fn, err := resolveFunction(deps, in.FunctionID) if err != nil { return nil, KVCASOutput{}, err } - key := strings.TrimSpace(in.Key) - if key == "" { - return nil, KVCASOutput{}, errors.New("key is required") + key := in.Key + if err := database.ValidateKVKey(key); err != nil { + return nil, KVCASOutput{}, err } expected, err := encodeKVValue(in.Expected) if err != nil { @@ -332,7 +331,7 @@ func registerKVTools(rc *regCtx) { if err != nil { return nil, KVCASOutput{}, err } - ok, current, err := deps.DB.KVCAS(fn.ID, key, expected, next, in.TTLSeconds) + ok, current, err := deps.DB.KVCASContext(ctx, fn.ID, key, expected, next, in.TTLSeconds) if err != nil { return nil, KVCASOutput{}, err } diff --git a/backend/internal/pool/pool.go b/backend/internal/pool/pool.go index 7ba08482..d08260ea 100644 --- a/backend/internal/pool/pool.go +++ b/backend/internal/pool/pool.go @@ -62,12 +62,9 @@ type SandboxTemplate struct { // RefreshForDeploy so the next spawn picks up the new values. SecretsLookup func(fnID string) map[string]string - // InternalToken is a process-lifetime token injected into every worker - // as ORVA_INTERNAL_TOKEN. The adapter sends it as the auth header when - // calling the KV / F2F / jobs endpoints (Phases 3, 4, 5). Validated by - // the corresponding handlers; not exposed to user code that doesn't - // already have access to env vars. - InternalToken string + // SDKToken mints a process-scoped credential bound to the worker's + // function ID. User code can read it, but cannot alter the signed caller. + SDKToken func(functionID string) string // APIBaseURL is the base URL the adapter uses when making outbound // calls to Orva's own internal endpoints (KV / F2F / jobs). @@ -730,8 +727,8 @@ func (m *Manager) getOrCreatePool(fnID string) (*functionPool, error) { // KV / F2F / jobs endpoints. Empty when running outside the // server (tests) so user code can probe presence to decide // whether to fall back. - if m.tmpl.InternalToken != "" { - env["ORVA_INTERNAL_TOKEN"] = m.tmpl.InternalToken + if m.tmpl.SDKToken != nil { + env["ORVA_INTERNAL_TOKEN"] = m.tmpl.SDKToken(fn.ID) env["ORVA_API_BASE"] = m.tmpl.APIBaseURL } // Resolve the egress policy for this spawn. Read off m (not the @@ -761,14 +758,14 @@ func (m *Manager) getOrCreatePool(fnID string) (*functionPool, error) { // Without this the warm-pool spawn passed MaxPids=0 → // `--cgroup_pids_max 0` (fork-bomb cap disabled). Fall back to // 32 (the historical sandbox.Execute default) if unset. - MaxPids: maxPidsOr(tmpl.DefaultMaxPids, 32), - Env: env, + MaxPids: maxPidsOr(tmpl.DefaultMaxPids, 32), + Env: env, SeccompPolicy: sandbox.BuildSeccompPolicy(tmpl.DefaultSeccomp, // Outbound network access needs the socket syscalls the base // policies withhold; what it may reach is decided by the // compiled egress policy, not by seccomp. sandbox.SeccompAllowForNetworkMode(fn.NetworkMode), nil), - NetworkMode: fn.NetworkMode, + NetworkMode: fn.NetworkMode, // Operator-managed DNS for egress sandboxes; written by // internal/firewall on every refresh tick. Bound at // /etc/resolv.conf and /etc/hosts when present. diff --git a/backend/internal/proxy/proxy.go b/backend/internal/proxy/proxy.go index 8cc85e4c..b5b7f9e6 100644 --- a/backend/internal/proxy/proxy.go +++ b/backend/internal/proxy/proxy.go @@ -14,6 +14,7 @@ import ( "github.com/Harsh-2002/Orva/backend/internal/database" "github.com/Harsh-2002/Orva/backend/internal/pool" "github.com/Harsh-2002/Orva/backend/internal/sandbox" + "github.com/Harsh-2002/Orva/backend/internal/sdkauth" "github.com/Harsh-2002/Orva/backend/internal/trace" ) @@ -24,7 +25,7 @@ import ( // // Anything that smells like an authentication credential lives here: // platform-issued bearer tokens, browser cookies, the operator-issued -// Orva API key, the per-process internal token used by the SDK, and +// Orva API key, the function-scoped credential used by the SDK, and // HTTP/1.1's proxy auth. If a future header carries credentials, add // it to this map — there is no allow-list fallback. var redactHeaders = map[string]bool{ @@ -112,7 +113,8 @@ type Proxy struct { // DB is used to persist the captured request envelope for replay // (v0.4 A3). Optional: when nil, capture is skipped silently. Tests // without a DB wired up keep working unchanged. - DB *database.Database + DB *database.Database + SDKAuth *sdkauth.Authenticator Config ProxyConfig } @@ -192,6 +194,10 @@ func (p *Proxy) Forward( _ = cpus _ = seccompPolicy _ = coldStart + releaseExecution := p.SDKAuth.BindExecution( + execID, fnID, trace.TraceID(r.Context()), trace.SpanID(r.Context()), startTime, + ) + defer releaseExecution() // Serialize the HTTP request into JSON for the adapter. body, _ := io.ReadAll(r.Body) diff --git a/backend/internal/scheduler/scheduler.go b/backend/internal/scheduler/scheduler.go index ce934f5e..88ccab9f 100644 --- a/backend/internal/scheduler/scheduler.go +++ b/backend/internal/scheduler/scheduler.go @@ -24,6 +24,7 @@ import ( "github.com/Harsh-2002/Orva/backend/internal/firewall" "github.com/Harsh-2002/Orva/backend/internal/metrics" "github.com/Harsh-2002/Orva/backend/internal/pool" + "github.com/Harsh-2002/Orva/backend/internal/sdkauth" "github.com/Harsh-2002/Orva/backend/internal/server/events" "github.com/Harsh-2002/Orva/backend/internal/trace" "github.com/Harsh-2002/Orva/internal/ids" @@ -106,6 +107,7 @@ type Scheduler struct { // baselines. Optional — may be nil in tests; recordExecution // short-circuits the outlier hook when it's missing. metrics *metrics.Metrics + sdkAuth *sdkauth.Authenticator } // SetMetrics wires the metrics container so cron/job-recorded executions @@ -113,6 +115,10 @@ type Scheduler struct { // Optional — leave unset in tests where metrics aren't relevant. func (s *Scheduler) SetMetrics(m *metrics.Metrics) { s.metrics = m } +// SetSDKAuth registers cron and job executions while their worker is active, +// allowing every runtime SDK endpoint to verify execution ownership. +func (s *Scheduler) SetSDKAuth(a *sdkauth.Authenticator) { s.sdkAuth = a } + // SetEgressGuard puts webhook delivery behind the operator's egress policy. // Call before Start — the delivery loop reads httpClient from its own // goroutines, and this is the only writer. @@ -382,6 +388,8 @@ func (s *Scheduler) fireCron(parent context.Context, row *database.CronSchedule) } eventJSON, _ := json.Marshal(event) + releaseExecution := s.sdkAuth.BindExecution(execID, fn.ID, traceID, spanID, ranAt) + defer releaseExecution() respJSON, stderr, err := acq.Worker.Dispatch(ctx, eventJSON) if err != nil { reqErr = err @@ -687,6 +695,8 @@ func (s *Scheduler) runJob(parent context.Context, j *database.Job) { } eventJSON, _ := json.Marshal(event) + releaseExecution := s.sdkAuth.BindExecution(execID, fn.ID, traceID, spanID, startedAt) + defer releaseExecution() respJSON, stderr, err := acq.Worker.Dispatch(ctx, eventJSON) if err != nil { reqErr = err diff --git a/backend/internal/sdkauth/auth.go b/backend/internal/sdkauth/auth.go new file mode 100644 index 00000000..49a44043 --- /dev/null +++ b/backend/internal/sdkauth/auth.go @@ -0,0 +1,115 @@ +// Package sdkauth issues and verifies process-scoped credentials for sandbox +// workers. A credential binds the worker to one immutable function ID; the +// process-random signing key makes every credential expire on server restart. +package sdkauth + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "errors" + "strings" + "sync" + "time" +) + +const tokenVersion = "v1" + +var ErrInvalidToken = errors.New("invalid SDK credential") + +type Authenticator struct { + key []byte + active sync.Map // execution ID -> activeExecution while a worker is dispatching +} + +type activeExecution struct { + functionID string + startedAt time.Time + traceID string + spanID string +} + +func (a *Authenticator) BindExecution(executionID, functionID, traceID, spanID string, startedAt time.Time) func() { + if a == nil || executionID == "" || functionID == "" { + return func() {} + } + a.active.Store(executionID, activeExecution{ + functionID: functionID, startedAt: startedAt, traceID: traceID, spanID: spanID, + }) + return func() { a.active.Delete(executionID) } +} + +func (a *Authenticator) OwnsExecution(executionID, functionID string) bool { + if a == nil { + return false + } + execution, ok := a.active.Load(executionID) + return ok && execution.(activeExecution).functionID == functionID +} + +func (a *Authenticator) ExecutionStart(executionID, functionID string) (time.Time, bool) { + if a == nil { + return time.Time{}, false + } + value, ok := a.active.Load(executionID) + if !ok { + return time.Time{}, false + } + execution := value.(activeExecution) + return execution.startedAt, execution.functionID == functionID +} + +func (a *Authenticator) TraceContext(executionID, functionID string) (traceID, spanID string, startedAt time.Time, ok bool) { + if a == nil { + return "", "", time.Time{}, false + } + value, found := a.active.Load(executionID) + if !found { + return "", "", time.Time{}, false + } + execution := value.(activeExecution) + if execution.functionID != functionID { + return "", "", time.Time{}, false + } + return execution.traceID, execution.spanID, execution.startedAt, true +} + +func New(key []byte) *Authenticator { + return &Authenticator{key: append([]byte(nil), key...)} +} + +func (a *Authenticator) Mint(functionID string) string { + if a == nil || len(a.key) == 0 || functionID == "" { + return "" + } + encodedID := base64.RawURLEncoding.EncodeToString([]byte(functionID)) + payload := tokenVersion + "." + encodedID + mac := hmac.New(sha256.New, a.key) + _, _ = mac.Write([]byte(payload)) + return payload + "." + base64.RawURLEncoding.EncodeToString(mac.Sum(nil)) +} + +func (a *Authenticator) Verify(token string) (string, error) { + if a == nil || len(a.key) == 0 { + return "", ErrInvalidToken + } + parts := strings.Split(token, ".") + if len(parts) != 3 || parts[0] != tokenVersion { + return "", ErrInvalidToken + } + payload := parts[0] + "." + parts[1] + gotMAC, err := base64.RawURLEncoding.DecodeString(parts[2]) + if err != nil { + return "", ErrInvalidToken + } + mac := hmac.New(sha256.New, a.key) + _, _ = mac.Write([]byte(payload)) + if !hmac.Equal(gotMAC, mac.Sum(nil)) { + return "", ErrInvalidToken + } + rawID, err := base64.RawURLEncoding.DecodeString(parts[1]) + if err != nil || len(rawID) == 0 { + return "", ErrInvalidToken + } + return string(rawID), nil +} diff --git a/backend/internal/sdkauth/auth_test.go b/backend/internal/sdkauth/auth_test.go new file mode 100644 index 00000000..2702792a --- /dev/null +++ b/backend/internal/sdkauth/auth_test.go @@ -0,0 +1,47 @@ +package sdkauth + +import ( + "testing" + "time" +) + +func TestScopedCredential(t *testing.T) { + a := New([]byte("01234567890123456789012345678901")) + token := a.Mint("function-a") + got, err := a.Verify(token) + if err != nil || got != "function-a" { + t.Fatalf("verify = %q, %v", got, err) + } + + t.Run("tampered", func(t *testing.T) { + if _, err := a.Verify(token + "x"); err == nil { + t.Fatal("tampered credential was accepted") + } + }) + t.Run("stale after restart", func(t *testing.T) { + other := New([]byte("abcdefghijklmnopqrstuvwxyz123456")) + if _, err := other.Verify(token); err == nil { + t.Fatal("credential signed by a prior process was accepted") + } + }) +} + +func TestExecutionOwnershipIsScopedAndRemoved(t *testing.T) { + auth := New([]byte("secret")) + started := time.Now().Add(-time.Second) + release := auth.BindExecution("exec-1", "fn-a", "trace-a", "span-a", started) + if !auth.OwnsExecution("exec-1", "fn-a") || auth.OwnsExecution("exec-1", "fn-b") { + t.Fatal("execution ownership was not function scoped") + } + if got, ok := auth.ExecutionStart("exec-1", "fn-a"); !ok || !got.Equal(started) { + t.Fatalf("ExecutionStart = %v, %v", got, ok) + } + traceID, spanID, _, ok := auth.TraceContext("exec-1", "fn-a") + if !ok || traceID != "trace-a" || spanID != "span-a" { + t.Fatalf("TraceContext = %q, %q, %v", traceID, spanID, ok) + } + release() + if auth.OwnsExecution("exec-1", "fn-a") { + t.Fatal("execution remained active after release") + } +} diff --git a/backend/internal/server/handlers/cron.go b/backend/internal/server/handlers/cron.go index 6e45242c..8c4ae5a1 100644 --- a/backend/internal/server/handlers/cron.go +++ b/backend/internal/server/handlers/cron.go @@ -1,7 +1,6 @@ package handlers import ( - "crypto/subtle" "encoding/json" "errors" "net/http" @@ -11,6 +10,7 @@ import ( "github.com/Harsh-2002/Orva/backend/internal/database" "github.com/Harsh-2002/Orva/backend/internal/registry" "github.com/Harsh-2002/Orva/backend/internal/scheduler" + "github.com/Harsh-2002/Orva/backend/internal/sdkauth" "github.com/Harsh-2002/Orva/backend/internal/server/handlers/respond" ) @@ -19,9 +19,9 @@ import ( // handler doesn't need a back-channel to notify it because the goroutine // re-queries every tick. type CronHandler struct { - DB *database.Database - Registry *registry.Registry - InternalToken string // accepted by UpsertInternal for SDK-driven crons.upsert + DB *database.Database + Registry *registry.Registry + SDKAuth *sdkauth.Authenticator } // cronRequest is the shape of POST/PUT bodies. Payload is optional and @@ -233,8 +233,8 @@ func (h *CronHandler) ListAll(w http.ResponseWriter, r *http.Request) { } // upsertInternalRequest carries the body for the SDK's crons.upsert call. -// The function ID is derived from the caller's X-Orva-Function-Id header -// (set by the proxy when the function was spawned) rather than the URL. +// The function ID is derived from the verified scoped SDK credential rather +// than a caller-controlled body or header. type upsertInternalRequest struct { Name string `json:"name"` Schedule string `json:"schedule"` @@ -245,25 +245,23 @@ type upsertInternalRequest struct { // UpsertInternal handles POST /api/v1/_internal/crons. SDK-only path // guarded by X-Orva-Internal-Token; the function being scheduled is the -// caller's own function (read from X-Orva-Function-Id) so the SDK doesn't -// need to know its own UUID. +// signed caller's own function, so the SDK doesn't need to send its UUID. func (h *CronHandler) UpsertInternal(w http.ResponseWriter, r *http.Request) { reqID := r.Header.Get("X-Request-ID") - got := r.Header.Get("X-Orva-Internal-Token") - if h.InternalToken == "" || subtle.ConstantTimeCompare([]byte(got), []byte(h.InternalToken)) != 1 { + fnID, authErr := h.SDKAuth.Verify(r.Header.Get("X-Orva-Internal-Token")) + if authErr != nil { respond.Error(w, http.StatusUnauthorized, "UNAUTHORIZED", - "missing or invalid internal token", reqID) + "missing or invalid SDK credential", reqID) return } - fnID := r.Header.Get("X-Orva-Function-Id") - if fnID == "" { - respond.Error(w, http.StatusBadRequest, "VALIDATION", - "missing X-Orva-Function-Id header", reqID) + + body, err := readBoundedBody(r.Body, 1<<20) + if err != nil { + respond.Error(w, http.StatusBadRequest, "INVALID_BODY", err.Error(), reqID) return } - var req upsertInternalRequest - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + if err := json.Unmarshal(body, &req); err != nil { respond.Error(w, http.StatusBadRequest, "INVALID_JSON", "invalid request body", reqID) return } diff --git a/backend/internal/server/handlers/inbound_webhook_trigger.go b/backend/internal/server/handlers/inbound_webhook_trigger.go index 8370adc1..b2c4851e 100644 --- a/backend/internal/server/handlers/inbound_webhook_trigger.go +++ b/backend/internal/server/handlers/inbound_webhook_trigger.go @@ -29,6 +29,7 @@ import ( "github.com/Harsh-2002/Orva/backend/internal/metrics" "github.com/Harsh-2002/Orva/backend/internal/pool" "github.com/Harsh-2002/Orva/backend/internal/registry" + "github.com/Harsh-2002/Orva/backend/internal/sdkauth" "github.com/Harsh-2002/Orva/backend/internal/server/handlers/respond" "github.com/Harsh-2002/Orva/backend/internal/trace" "github.com/Harsh-2002/Orva/internal/ids" @@ -47,6 +48,7 @@ type InboundTriggerHandler struct { Registry *registry.Registry Pool *pool.Manager Metrics *metrics.Metrics + SDKAuth *sdkauth.Authenticator // PublishEvent fires an `event: execution` so the live UI sees // inbound-triggered runs in the same feed as HTTP invokes. Wired in @@ -183,6 +185,8 @@ func (h *InboundTriggerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request } eventJSON, _ := json.Marshal(event) + releaseExecution := h.SDKAuth.BindExecution(execID, fn.ID, traceID, spanID, startedAt) + defer releaseExecution() respJSON, stderr, err := acq.Worker.Dispatch(ctx, eventJSON) durationMS := time.Since(startedAt).Milliseconds() if err != nil { diff --git a/backend/internal/server/handlers/internal_invoke.go b/backend/internal/server/handlers/internal_invoke.go index d110532f..5e57fd2c 100644 --- a/backend/internal/server/handlers/internal_invoke.go +++ b/backend/internal/server/handlers/internal_invoke.go @@ -2,10 +2,8 @@ package handlers import ( "context" - "crypto/subtle" "encoding/json" "errors" - "io" "net/http" "strconv" "time" @@ -14,6 +12,7 @@ import ( "github.com/Harsh-2002/Orva/backend/internal/metrics" "github.com/Harsh-2002/Orva/backend/internal/pool" "github.com/Harsh-2002/Orva/backend/internal/registry" + "github.com/Harsh-2002/Orva/backend/internal/sdkauth" "github.com/Harsh-2002/Orva/backend/internal/server/handlers/respond" "github.com/Harsh-2002/Orva/backend/internal/trace" "github.com/Harsh-2002/Orva/internal/ids" @@ -22,14 +21,14 @@ import ( // InternalInvokeHandler is the F2F (function-to-function) entrypoint. // User code inside a sandbox calls orva.invoke("other-fn", payload); the // adapter POSTs here. We bypass the public auth layer (worker holds the -// internal token) and the per-function rate limiter (loops are bounded +// scoped SDK credential) and the per-function rate limiter (loops are bounded // by call depth, not request rate). type InternalInvokeHandler struct { - DB *database.Database - Registry *registry.Registry - Pool *pool.Manager - Metrics *metrics.Metrics - InternalToken string + DB *database.Database + Registry *registry.Registry + Pool *pool.Manager + Metrics *metrics.Metrics + SDKAuth *sdkauth.Authenticator // MaxCallDepth caps how many invoke()s can be nested before the call // chain is rejected. Without this, a function recursively invoking @@ -45,12 +44,19 @@ const defaultMaxCallDepth = 8 func (h *InternalInvokeHandler) Invoke(w http.ResponseWriter, r *http.Request) { reqID := r.Header.Get("X-Request-ID") - // Per-process internal token. Constant-time compare so timing leaks - // don't help an external attacker who somehow reached this endpoint. - got := r.Header.Get("X-Orva-Internal-Token") - if h.InternalToken == "" || subtle.ConstantTimeCompare([]byte(got), []byte(h.InternalToken)) != 1 { + // The verified function claim supplies immutable caller attribution. + callerFnID, authErr := h.SDKAuth.Verify(r.Header.Get("X-Orva-Internal-Token")) + if authErr != nil { respond.Error(w, http.StatusUnauthorized, "UNAUTHORIZED", - "missing or invalid internal token", reqID) + "missing or invalid SDK credential", reqID) + return + } + callerTraceID, callerSpanID, _, callerActive := h.SDKAuth.TraceContext( + r.Header.Get("X-Orva-Execution-Id"), callerFnID, + ) + if !callerActive { + respond.Error(w, http.StatusForbidden, "SDK_SCOPE_VIOLATION", + "SDK request is not associated with an active caller execution", reqID) return } @@ -88,7 +94,7 @@ func (h *InternalInvokeHandler) Invoke(w http.ResponseWriter, r *http.Request) { return } - body, err := io.ReadAll(io.LimitReader(r.Body, 1<<20)) + body, err := readBoundedBody(r.Body, 1<<20) if err != nil { respond.Error(w, http.StatusBadRequest, "INVALID_BODY", "failed to read body", reqID) return @@ -106,13 +112,9 @@ func (h *InternalInvokeHandler) Invoke(w http.ResponseWriter, r *http.Request) { // caller was started). traceID may be empty for legacy calls; we // generate a fresh root in that case so this F2F still produces a // usable single-span trace. parentSpanID = caller's span = our parent. - traceID := r.Header.Get("X-Orva-Trace-Id") - parentSpanID := r.Header.Get("X-Orva-Span-Id") - if traceID == "" { - traceID = trace.NewTraceID() - } + traceID := callerTraceID + parentSpanID := callerSpanID spanID := trace.NewSpanID() - callerFnID := r.Header.Get("X-Orva-Caller-Function") acq, err := h.Pool.Acquire(ctx, fn.ID) if err != nil { @@ -127,6 +129,9 @@ func (h *InternalInvokeHandler) Invoke(w http.ResponseWriter, r *http.Request) { // log AND in the trace tree as a distinct span. Without this, F2F // children would be invisible to ops tooling. execID := ids.New() + start := time.Now() + releaseExecution := h.SDKAuth.BindExecution(execID, fn.ID, traceID, spanID, start) + defer releaseExecution() // Synthesize the event in the same shape the public /fn/ handler // builds so user code can't tell the difference (which is the point). @@ -148,7 +153,6 @@ func (h *InternalInvokeHandler) Invoke(w http.ResponseWriter, r *http.Request) { } eventJSON, _ := json.Marshal(event) - start := time.Now() respJSON, _, err := acq.Worker.Dispatch(ctx, eventJSON) durationMS := time.Since(start).Milliseconds() if err != nil { @@ -228,10 +232,18 @@ func (h *InternalInvokeHandler) Invoke(w http.ResponseWriter, r *http.Request) { func (h *InternalInvokeHandler) InvokeStream(w http.ResponseWriter, r *http.Request) { reqID := r.Header.Get("X-Request-ID") - got := r.Header.Get("X-Orva-Internal-Token") - if h.InternalToken == "" || subtle.ConstantTimeCompare([]byte(got), []byte(h.InternalToken)) != 1 { + callerFnID, authErr := h.SDKAuth.Verify(r.Header.Get("X-Orva-Internal-Token")) + if authErr != nil { respond.Error(w, http.StatusUnauthorized, "UNAUTHORIZED", - "missing or invalid internal token", reqID) + "missing or invalid SDK credential", reqID) + return + } + callerTraceID, callerSpanID, _, callerActive := h.SDKAuth.TraceContext( + r.Header.Get("X-Orva-Execution-Id"), callerFnID, + ) + if !callerActive { + respond.Error(w, http.StatusForbidden, "SDK_SCOPE_VIOLATION", + "SDK request is not associated with an active caller execution", reqID) return } @@ -264,7 +276,7 @@ func (h *InternalInvokeHandler) InvokeStream(w http.ResponseWriter, r *http.Requ return } - body, err := io.ReadAll(io.LimitReader(r.Body, 1<<20)) + body, err := readBoundedBody(r.Body, 1<<20) if err != nil { respond.Error(w, http.StatusBadRequest, "INVALID_BODY", "failed to read body", reqID) return @@ -277,13 +289,9 @@ func (h *InternalInvokeHandler) InvokeStream(w http.ResponseWriter, r *http.Requ ctx, cancel := context.WithTimeout(r.Context(), timeout) defer cancel() - traceID := r.Header.Get("X-Orva-Trace-Id") - parentSpanID := r.Header.Get("X-Orva-Span-Id") - if traceID == "" { - traceID = trace.NewTraceID() - } + traceID := callerTraceID + parentSpanID := callerSpanID spanID := trace.NewSpanID() - callerFnID := r.Header.Get("X-Orva-Caller-Function") acq, err := h.Pool.Acquire(ctx, fn.ID) if err != nil { @@ -295,6 +303,9 @@ func (h *InternalInvokeHandler) InvokeStream(w http.ResponseWriter, r *http.Requ defer func() { h.Pool.Release(acq, reqErr) }() execID := ids.New() + start := time.Now() + releaseExecution := h.SDKAuth.BindExecution(execID, fn.ID, traceID, spanID, start) + defer releaseExecution() event := map[string]any{ "method": "POST", "path": "/", @@ -312,7 +323,6 @@ func (h *InternalInvokeHandler) InvokeStream(w http.ResponseWriter, r *http.Requ } eventJSON, _ := json.Marshal(event) - start := time.Now() dres, err := acq.Worker.DispatchEx(ctx, eventJSON) if err != nil { reqErr = err diff --git a/backend/internal/server/handlers/jobs.go b/backend/internal/server/handlers/jobs.go index 366e33cb..f7e445e6 100644 --- a/backend/internal/server/handlers/jobs.go +++ b/backend/internal/server/handlers/jobs.go @@ -1,7 +1,6 @@ package handlers import ( - "crypto/subtle" "encoding/json" "net/http" "strconv" @@ -9,6 +8,7 @@ import ( "github.com/Harsh-2002/Orva/backend/internal/database" "github.com/Harsh-2002/Orva/backend/internal/registry" + "github.com/Harsh-2002/Orva/backend/internal/sdkauth" "github.com/Harsh-2002/Orva/backend/internal/server/handlers/respond" ) @@ -17,32 +17,16 @@ import ( // AND from inside worker sandboxes (X-Orva-Internal-Token). The internal // path is what powers orva.jobs.enqueue() in the SDK. type JobsHandler struct { - DB *database.Database - Registry *registry.Registry - InternalToken string + DB *database.Database + Registry *registry.Registry + SDKAuth *sdkauth.Authenticator } -// authorize accepts either a worker's internal token OR the standard -// middleware-stamped session/API-key auth. Returns true when permitted. -// -// This handler independently constant-time-compares the internal token as -// its own defense-in-depth check — it does NOT rely on the middleware for -// the SDK path. For the public (session/API-key) path it does rely on -// authMiddleware, which only forwards /api/v1/* requests to handlers after -// a valid session cookie or API key. Note that authMiddleware requires the -// internal-token header to MATCH the per-process secret before it will skip -// the API-key gate (a merely-present header falls through to normal auth), -// so an unauthenticated caller can never reach this point — the `return -// true` below is the genuinely-authenticated public path, not a bypass. -func (h *JobsHandler) authorize(r *http.Request) bool { - // Internal token path — workers calling the SDK. - got := r.Header.Get("X-Orva-Internal-Token") - if h.InternalToken != "" && subtle.ConstantTimeCompare([]byte(got), []byte(h.InternalToken)) == 1 { - return true - } - // Public path — authMiddleware already enforced session/API-key auth - // before forwarding this request. - return true +// sdkCaller returns immutable caller attribution from a verified scoped SDK +// credential. Public session/API-key requests have no SDK caller. +func (h *JobsHandler) sdkCaller(r *http.Request) string { + caller, _ := h.SDKAuth.Verify(r.Header.Get("X-Orva-Internal-Token")) + return caller } type enqueueRequest struct { @@ -57,14 +41,29 @@ type enqueueRequest struct { // Enqueue handles POST /api/v1/jobs. func (h *JobsHandler) Enqueue(w http.ResponseWriter, r *http.Request) { - if !h.authorize(r) { - respond.Error(w, http.StatusUnauthorized, "UNAUTHORIZED", "not authorized", r.Header.Get("X-Request-ID")) - return - } reqID := r.Header.Get("X-Request-ID") + callerFnID := h.sdkCaller(r) + callerTraceID := "" + callerSpanID := "" + if callerFnID != "" { + var active bool + callerTraceID, callerSpanID, _, active = h.SDKAuth.TraceContext( + r.Header.Get("X-Orva-Execution-Id"), callerFnID, + ) + if !active { + respond.Error(w, http.StatusForbidden, "SDK_SCOPE_VIOLATION", + "SDK request is not associated with an active caller execution", reqID) + return + } + } + body, err := readBoundedBody(r.Body, 1<<20) + if err != nil { + respond.Error(w, http.StatusBadRequest, "INVALID_BODY", err.Error(), reqID) + return + } var req enqueueRequest - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + if err := json.Unmarshal(body, &req); err != nil { respond.Error(w, http.StatusBadRequest, "INVALID_JSON", "invalid request body", reqID) return } @@ -108,9 +107,9 @@ func (h *JobsHandler) Enqueue(w http.ResponseWriter, r *http.Request) { // the job row so the eventual execution lands in the same trace as // whatever enqueued it. Empty headers (dashboard or external API // caller) leave these blank → the picked-up job becomes a new root. - job.TraceID = r.Header.Get("X-Orva-Trace-Id") - job.ParentSpanID = r.Header.Get("X-Orva-Span-Id") - job.EnqueuedByFunctionID = r.Header.Get("X-Orva-Caller-Function") + job.TraceID = callerTraceID + job.ParentSpanID = callerSpanID + job.EnqueuedByFunctionID = callerFnID if err := h.DB.EnqueueJob(job); err != nil { respond.Error(w, http.StatusInternalServerError, "INTERNAL", "enqueue failed: "+err.Error(), reqID) return @@ -123,10 +122,6 @@ func (h *JobsHandler) Enqueue(w http.ResponseWriter, r *http.Request) { // List handles GET /api/v1/jobs?status=...&function_id=...&limit=... func (h *JobsHandler) List(w http.ResponseWriter, r *http.Request) { - if !h.authorize(r) { - respond.Error(w, http.StatusUnauthorized, "UNAUTHORIZED", "not authorized", r.Header.Get("X-Request-ID")) - return - } reqID := r.Header.Get("X-Request-ID") status := r.URL.Query().Get("status") fnID := r.URL.Query().Get("function_id") @@ -149,10 +144,6 @@ func (h *JobsHandler) List(w http.ResponseWriter, r *http.Request) { // Get handles GET /api/v1/jobs/{id}. func (h *JobsHandler) Get(w http.ResponseWriter, r *http.Request) { - if !h.authorize(r) { - respond.Error(w, http.StatusUnauthorized, "UNAUTHORIZED", "not authorized", r.Header.Get("X-Request-ID")) - return - } reqID := r.Header.Get("X-Request-ID") id := r.PathValue("id") job, err := h.DB.GetJob(id) @@ -166,10 +157,6 @@ func (h *JobsHandler) Get(w http.ResponseWriter, r *http.Request) { // Retry handles POST /api/v1/jobs/{id}/retry. Resets a terminal job back // to pending so the next scheduler tick picks it up. func (h *JobsHandler) Retry(w http.ResponseWriter, r *http.Request) { - if !h.authorize(r) { - respond.Error(w, http.StatusUnauthorized, "UNAUTHORIZED", "not authorized", r.Header.Get("X-Request-ID")) - return - } reqID := r.Header.Get("X-Request-ID") id := r.PathValue("id") if _, err := h.DB.GetJob(id); err != nil { @@ -185,10 +172,6 @@ func (h *JobsHandler) Retry(w http.ResponseWriter, r *http.Request) { // Delete handles DELETE /api/v1/jobs/{id}. func (h *JobsHandler) Delete(w http.ResponseWriter, r *http.Request) { - if !h.authorize(r) { - respond.Error(w, http.StatusUnauthorized, "UNAUTHORIZED", "not authorized", r.Header.Get("X-Request-ID")) - return - } reqID := r.Header.Get("X-Request-ID") id := r.PathValue("id") if err := h.DB.DeleteJob(id); err != nil { diff --git a/backend/internal/server/handlers/kv.go b/backend/internal/server/handlers/kv.go index d9c16efc..c962ad61 100644 --- a/backend/internal/server/handlers/kv.go +++ b/backend/internal/server/handlers/kv.go @@ -1,67 +1,69 @@ package handlers import ( - "crypto/subtle" "encoding/json" "errors" "io" "net/http" "strconv" + "strings" "github.com/Harsh-2002/Orva/backend/internal/database" + "github.com/Harsh-2002/Orva/backend/internal/sdkauth" "github.com/Harsh-2002/Orva/backend/internal/server/handlers/respond" ) // KVHandler exposes the per-function key/value store to worker SDKs over -// HTTP loopback. Authentication is the per-process internal token — -// callers (the worker adapters) prove they're inside a sandbox by sending -// it back. Function ID is in the URL; values are arbitrary bytes wrapped -// in JSON for transport. +// HTTP loopback. Authentication uses a process-signed credential bound to one +// function ID. The route namespace must match that signed claim. type KVHandler struct { - DB *database.Database - InternalToken string + DB *database.Database + SDKAuth *sdkauth.Authenticator } -// authorize confirms the request bears the matching X-Orva-Internal-Token -// header. Constant-time comparison so timing leaks can't help guess the -// token. Returns false (and writes a 401) when invalid. Also opportunistically -// records the SDK version for operator visibility. -func (h *KVHandler) authorize(w http.ResponseWriter, r *http.Request) bool { - got := r.Header.Get("X-Orva-Internal-Token") - if h.InternalToken == "" || subtle.ConstantTimeCompare([]byte(got), []byte(h.InternalToken)) != 1 { +// authorize verifies the credential signature and route scope. It returns a +// standard 401 for invalid/stale credentials and 403 for cross-namespace use. +func (h *KVHandler) authorize(w http.ResponseWriter, r *http.Request, functionID string) bool { + caller, err := h.SDKAuth.Verify(r.Header.Get("X-Orva-Internal-Token")) + if err != nil { respond.Error(w, http.StatusUnauthorized, "UNAUTHORIZED", - "missing or invalid internal token", r.Header.Get("X-Request-ID")) + "missing or invalid SDK credential", r.Header.Get("X-Request-ID")) + return false + } + if caller != functionID { + respond.Error(w, http.StatusForbidden, "SDK_SCOPE_VIOLATION", + "SDK credential cannot access another function's KV namespace", r.Header.Get("X-Request-ID")) return false } - observeSDKVersion(r) + observeSDKVersion(r, caller) return true } // kvPutRequest carries the value + optional TTL. type kvPutRequest struct { Value json.RawMessage `json:"value"` - TTLSeconds int `json:"ttl_seconds,omitempty"` + TTLSeconds *int `json:"ttl_seconds,omitempty"` } // kvGetResponse mirrors the put shape so adapters can round-trip values. type kvGetResponse struct { - Value json.RawMessage `json:"value"` - ExpiresAt *string `json:"expires_at,omitempty"` + Value json.RawMessage `json:"value"` + ExpiresAt *string `json:"expires_at,omitempty"` } // Put handles PUT /api/v1/_kv/{fn_id}/{key}. func (h *KVHandler) Put(w http.ResponseWriter, r *http.Request) { - if !h.authorize(w, r) { + if !h.authorize(w, r, r.PathValue("fn_id")) { return } reqID := r.Header.Get("X-Request-ID") fnID := r.PathValue("fn_id") key := r.PathValue("key") - if fnID == "" || key == "" { - respond.Error(w, http.StatusBadRequest, "VALIDATION", "fn_id and key are required", reqID) + if err := database.ValidateKVKey(key); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", err.Error(), reqID) return } - body, err := io.ReadAll(io.LimitReader(r.Body, 1<<20)) // 1 MB cap per value + body, err := readBoundedBody(r.Body, 1<<20) if err != nil { respond.Error(w, http.StatusBadRequest, "INVALID_BODY", "failed to read body", reqID) return @@ -71,11 +73,19 @@ func (h *KVHandler) Put(w http.ResponseWriter, r *http.Request) { respond.Error(w, http.StatusBadRequest, "INVALID_JSON", "invalid request body", reqID) return } - if len(req.Value) == 0 { - respond.Error(w, http.StatusBadRequest, "VALIDATION", "value is required", reqID) + if err := database.ValidateKVValue(req.Value); err != nil { + status := http.StatusBadRequest + if len(req.Value) > database.KVMaxValueBytes { + status = http.StatusRequestEntityTooLarge + } + respond.Error(w, status, "VALIDATION", err.Error(), reqID) return } - if err := h.DB.KVPut(fnID, key, req.Value, req.TTLSeconds); err != nil { + if err := database.ValidateKVTTL(req.TTLSeconds); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", err.Error(), reqID) + return + } + if err := h.DB.KVPutContext(r.Context(), fnID, key, req.Value, req.TTLSeconds); err != nil { respond.Error(w, http.StatusInternalServerError, "INTERNAL", "kv put failed: "+err.Error(), reqID) return } @@ -84,13 +94,17 @@ func (h *KVHandler) Put(w http.ResponseWriter, r *http.Request) { // Get handles GET /api/v1/_kv/{fn_id}/{key}. func (h *KVHandler) Get(w http.ResponseWriter, r *http.Request) { - if !h.authorize(w, r) { + if !h.authorize(w, r, r.PathValue("fn_id")) { return } reqID := r.Header.Get("X-Request-ID") fnID := r.PathValue("fn_id") key := r.PathValue("key") - entry, err := h.DB.KVGet(fnID, key) + if err := database.ValidateKVKey(key); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", err.Error(), reqID) + return + } + entry, err := h.DB.KVGetContext(r.Context(), fnID, key) if errors.Is(err, database.ErrKVNotFound) { respond.Error(w, http.StatusNotFound, "NOT_FOUND", "key not found", reqID) return @@ -109,13 +123,17 @@ func (h *KVHandler) Get(w http.ResponseWriter, r *http.Request) { // Delete handles DELETE /api/v1/_kv/{fn_id}/{key}. func (h *KVHandler) Delete(w http.ResponseWriter, r *http.Request) { - if !h.authorize(w, r) { + if !h.authorize(w, r, r.PathValue("fn_id")) { return } reqID := r.Header.Get("X-Request-ID") fnID := r.PathValue("fn_id") key := r.PathValue("key") - if err := h.DB.KVDelete(fnID, key); err != nil { + if err := database.ValidateKVKey(key); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", err.Error(), reqID) + return + } + if err := h.DB.KVDeleteContext(r.Context(), fnID, key); err != nil { respond.Error(w, http.StatusInternalServerError, "INTERNAL", "kv delete failed: "+err.Error(), reqID) return } @@ -126,7 +144,7 @@ func (h *KVHandler) Delete(w http.ResponseWriter, r *http.Request) { // cursor is the last key from the previous page (exclusive); response // includes next_cursor when more rows remain. func (h *KVHandler) List(w http.ResponseWriter, r *http.Request) { - if !h.authorize(w, r) { + if !h.authorize(w, r, r.PathValue("fn_id")) { return } reqID := r.Header.Get("X-Request-ID") @@ -140,7 +158,7 @@ func (h *KVHandler) List(w http.ResponseWriter, r *http.Request) { limit = n } } - page, err := h.DB.KVListWithCursor(fnID, prefix, cursor, limit) + page, err := h.DB.KVListWithCursorContext(r.Context(), fnID, prefix, cursor, limit) if err != nil { respond.Error(w, http.StatusInternalServerError, "INTERNAL", "kv list failed: "+err.Error(), reqID) return @@ -173,19 +191,19 @@ type kvBatchRequest struct { Op string `json:"op"` Key string `json:"key"` Value json.RawMessage `json:"value,omitempty"` - TTLSeconds int `json:"ttl_seconds,omitempty"` + TTLSeconds *int `json:"ttl_seconds,omitempty"` } `json:"ops"` } // Batch handles POST /api/v1/_kv/{fn_id}/batch. A single batch is capped // at 100 ops to keep the SQLite write transaction bounded. func (h *KVHandler) Batch(w http.ResponseWriter, r *http.Request) { - if !h.authorize(w, r) { + if !h.authorize(w, r, r.PathValue("fn_id")) { return } reqID := r.Header.Get("X-Request-ID") fnID := r.PathValue("fn_id") - body, err := io.ReadAll(io.LimitReader(r.Body, 8<<20)) // 8 MB cap per batch + body, err := readBoundedBody(r.Body, 8<<20) if err != nil { respond.Error(w, http.StatusBadRequest, "INVALID_BODY", "failed to read body", reqID) return @@ -205,6 +223,24 @@ func (h *KVHandler) Batch(w http.ResponseWriter, r *http.Request) { } dbOps := make([]database.KVBatchOp, len(req.Ops)) for i, op := range req.Ops { + if err := database.ValidateKVKey(op.Key); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", err.Error(), reqID) + return + } + if op.Op != "get" && op.Op != "put" && op.Op != "delete" { + respond.Error(w, http.StatusBadRequest, "VALIDATION", "unknown batch operation: "+op.Op, reqID) + return + } + if op.Op == "put" { + if err := database.ValidateKVValue(op.Value); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", err.Error(), reqID) + return + } + if err := database.ValidateKVTTL(op.TTLSeconds); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", err.Error(), reqID) + return + } + } dbOps[i] = database.KVBatchOp{ Op: op.Op, Key: op.Key, @@ -212,9 +248,9 @@ func (h *KVHandler) Batch(w http.ResponseWriter, r *http.Request) { TTLSeconds: op.TTLSeconds, } } - results, err := h.DB.KVBatch(fnID, dbOps) + results, err := h.DB.KVBatchContext(r.Context(), fnID, dbOps) if err != nil { - respond.Error(w, http.StatusInternalServerError, "INTERNAL", "kv batch failed: "+err.Error(), reqID) + respond.Error(w, http.StatusServiceUnavailable, "KV_UNAVAILABLE", "kv batch failed: "+err.Error(), reqID) return } type wireResult struct { @@ -243,19 +279,23 @@ func (h *KVHandler) Batch(w http.ResponseWriter, r *http.Request) { // kvIncrRequest carries the delta and optional TTL refresh. type kvIncrRequest struct { Delta int64 `json:"delta"` - TTLSeconds int `json:"ttl_seconds,omitempty"` + TTLSeconds *int `json:"ttl_seconds,omitempty"` } // Incr handles POST /api/v1/_kv/{fn_id}/{key}/incr. Atomically updates // an integer value and returns the new value. func (h *KVHandler) Incr(w http.ResponseWriter, r *http.Request) { - if !h.authorize(w, r) { + if !h.authorize(w, r, r.PathValue("fn_id")) { return } reqID := r.Header.Get("X-Request-ID") fnID := r.PathValue("fn_id") key := r.PathValue("key") - body, err := io.ReadAll(io.LimitReader(r.Body, 1<<16)) + if err := database.ValidateKVKey(key); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", err.Error(), reqID) + return + } + body, err := readBoundedBody(r.Body, 1<<16) if err != nil { respond.Error(w, http.StatusBadRequest, "INVALID_BODY", "failed to read body", reqID) return @@ -267,9 +307,19 @@ func (h *KVHandler) Incr(w http.ResponseWriter, r *http.Request) { return } } - next, err := h.DB.KVIncr(fnID, key, req.Delta, req.TTLSeconds) + if err := database.ValidateKVTTL(req.TTLSeconds); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", err.Error(), reqID) + return + } + next, err := h.DB.KVIncrContext(r.Context(), fnID, key, req.Delta, req.TTLSeconds) if err != nil { - respond.Error(w, http.StatusConflict, "KV_INCR_FAILED", err.Error(), reqID) + status := http.StatusInternalServerError + code := "INTERNAL" + if strings.Contains(err.Error(), "not an integer") { + status = http.StatusConflict + code = "KV_INCR_FAILED" + } + respond.Error(w, status, code, err.Error(), reqID) return } respond.JSON(w, http.StatusOK, map[string]any{"value": next}) @@ -281,18 +331,22 @@ func (h *KVHandler) Incr(w http.ResponseWriter, r *http.Request) { type kvCASRequest struct { Expected *json.RawMessage `json:"expected"` New json.RawMessage `json:"new"` - TTLSeconds int `json:"ttl_seconds,omitempty"` + TTLSeconds *int `json:"ttl_seconds,omitempty"` } // CAS handles POST /api/v1/_kv/{fn_id}/{key}/cas. func (h *KVHandler) CAS(w http.ResponseWriter, r *http.Request) { - if !h.authorize(w, r) { + if !h.authorize(w, r, r.PathValue("fn_id")) { return } reqID := r.Header.Get("X-Request-ID") fnID := r.PathValue("fn_id") key := r.PathValue("key") - body, err := io.ReadAll(io.LimitReader(r.Body, 1<<20)) + if err := database.ValidateKVKey(key); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", err.Error(), reqID) + return + } + body, err := readBoundedBody(r.Body, 1<<20) if err != nil { respond.Error(w, http.StatusBadRequest, "INVALID_BODY", "failed to read body", reqID) return @@ -306,11 +360,23 @@ func (h *KVHandler) CAS(w http.ResponseWriter, r *http.Request) { respond.Error(w, http.StatusBadRequest, "VALIDATION", "new value is required", reqID) return } + if err := database.ValidateKVValue(req.New); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", err.Error(), reqID) + return + } + if err := database.ValidateKVTTL(req.TTLSeconds); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", err.Error(), reqID) + return + } var expectedBytes []byte if req.Expected != nil { expectedBytes = []byte(*req.Expected) + if err := database.ValidateKVValue(expectedBytes); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", "expected value: "+err.Error(), reqID) + return + } } - ok, current, err := h.DB.KVCAS(fnID, key, expectedBytes, []byte(req.New), req.TTLSeconds) + ok, current, err := h.DB.KVCASContext(r.Context(), fnID, key, expectedBytes, []byte(req.New), req.TTLSeconds) if err != nil { respond.Error(w, http.StatusInternalServerError, "INTERNAL", "kv cas failed: "+err.Error(), reqID) return @@ -321,3 +387,14 @@ func (h *KVHandler) CAS(w http.ResponseWriter, r *http.Request) { } respond.JSON(w, http.StatusOK, resp) } + +func readBoundedBody(body io.Reader, limit int64) ([]byte, error) { + b, err := io.ReadAll(io.LimitReader(body, limit+1)) + if err != nil { + return nil, err + } + if int64(len(b)) > limit { + return nil, errors.New("request body exceeds limit") + } + return b, nil +} diff --git a/backend/internal/server/handlers/kv_operator.go b/backend/internal/server/handlers/kv_operator.go index 8f94d087..f4861d7d 100644 --- a/backend/internal/server/handlers/kv_operator.go +++ b/backend/internal/server/handlers/kv_operator.go @@ -12,23 +12,22 @@ package handlers import ( "encoding/json" "errors" - "io" "net/http" "strconv" "strings" "time" "github.com/Harsh-2002/Orva/backend/internal/database" - "github.com/Harsh-2002/Orva/internal/ids" "github.com/Harsh-2002/Orva/backend/internal/registry" "github.com/Harsh-2002/Orva/backend/internal/server/handlers/respond" + "github.com/Harsh-2002/Orva/internal/ids" ) // Per-value cap for KV writes from the dashboard. Mirrors the cap the // SDK adapter enforces from inside sandboxes — keeps the operator path // honest. The SDK side checks this in a different spot, so we duplicate // the constant rather than couple the two packages. -const kvOperatorMaxValueBytes = 64 * 1024 +const kvOperatorMaxValueBytes = database.KVMaxValueBytes // kvOperatorBodyCap is the request-body read limit. Slightly larger // than the value cap so the {"value": ..., "ttl_seconds": ...} envelope @@ -113,12 +112,12 @@ func (h *KVOperatorHandler) List(w http.ResponseWriter, r *http.Request) { limit = 1000 } - entries, err := h.DB.KVList(fnID, prefix, limit) + entries, err := h.DB.KVListContext(r.Context(), fnID, prefix, limit) if err != nil { respond.Error(w, http.StatusInternalServerError, "INTERNAL", "kv list failed: "+err.Error(), reqID) return } - total, err := h.DB.KVCount(fnID) + total, err := h.DB.KVCountContext(r.Context(), fnID) if err != nil { // Soft-fail on the count — the list itself succeeded, surface 0 // rather than 500ing the whole response. @@ -145,12 +144,12 @@ func (h *KVOperatorHandler) Get(w http.ResponseWriter, r *http.Request) { return } key := r.PathValue("key") - if key == "" { - respond.Error(w, http.StatusBadRequest, "VALIDATION", "key is required", reqID) + if err := database.ValidateKVKey(key); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", err.Error(), reqID) return } - entry, err := h.DB.KVGet(fnID, key) + entry, err := h.DB.KVGetContext(r.Context(), fnID, key) if errors.Is(err, database.ErrKVNotFound) { respond.JSON(w, http.StatusNotFound, map[string]any{"found": false, "key": key}) return @@ -174,7 +173,7 @@ func (h *KVOperatorHandler) Get(w http.ResponseWriter, r *http.Request) { // (any JSON), ttl_seconds is optional (0 = no expiry). type kvOperatorPutRequest struct { Value json.RawMessage `json:"value"` - TTLSeconds int `json:"ttl_seconds,omitempty"` + TTLSeconds *int `json:"ttl_seconds,omitempty"` } // Put handles PUT /api/v1/functions/{fn_id}/kv/{key}. Upserts. @@ -186,12 +185,12 @@ func (h *KVOperatorHandler) Put(w http.ResponseWriter, r *http.Request) { return } key := r.PathValue("key") - if key == "" { - respond.Error(w, http.StatusBadRequest, "VALIDATION", "key is required", reqID) + if err := database.ValidateKVKey(key); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", err.Error(), reqID) return } - body, err := io.ReadAll(io.LimitReader(r.Body, kvOperatorBodyCap)) + body, err := readBoundedBody(r.Body, kvOperatorBodyCap) if err != nil { respond.Error(w, http.StatusBadRequest, "INVALID_BODY", "failed to read body", reqID) return @@ -219,11 +218,12 @@ func (h *KVOperatorHandler) Put(w http.ResponseWriter, r *http.Request) { respond.Error(w, http.StatusBadRequest, "INVALID_JSON", "value must be valid JSON", reqID) return } - if req.TTLSeconds < 0 { - req.TTLSeconds = 0 + if err := database.ValidateKVTTL(req.TTLSeconds); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", err.Error(), reqID) + return } - if err := h.DB.KVPut(fnID, key, req.Value, req.TTLSeconds); err != nil { + if err := h.DB.KVPutContext(r.Context(), fnID, key, req.Value, req.TTLSeconds); err != nil { respond.Error(w, http.StatusInternalServerError, "INTERNAL", "kv put failed: "+err.Error(), reqID) return } @@ -244,11 +244,11 @@ func (h *KVOperatorHandler) Delete(w http.ResponseWriter, r *http.Request) { return } key := r.PathValue("key") - if key == "" { - respond.Error(w, http.StatusBadRequest, "VALIDATION", "key is required", reqID) + if err := database.ValidateKVKey(key); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", err.Error(), reqID) return } - if err := h.DB.KVDelete(fnID, key); err != nil { + if err := h.DB.KVDeleteContext(r.Context(), fnID, key); err != nil { respond.Error(w, http.StatusInternalServerError, "INTERNAL", "kv delete failed: "+err.Error(), reqID) return } @@ -259,7 +259,7 @@ func (h *KVOperatorHandler) Delete(w http.ResponseWriter, r *http.Request) { // the internal kvIncrRequest (kv.go) — same shape, operator auth. type kvOperatorIncrRequest struct { Delta int64 `json:"delta"` - TTLSeconds int `json:"ttl_seconds,omitempty"` + TTLSeconds *int `json:"ttl_seconds,omitempty"` } // Incr handles POST /api/v1/functions/{fn_id}/kv/{key}/incr. Atomically adds @@ -273,11 +273,11 @@ func (h *KVOperatorHandler) Incr(w http.ResponseWriter, r *http.Request) { return } key := r.PathValue("key") - if key == "" { - respond.Error(w, http.StatusBadRequest, "VALIDATION", "key is required", reqID) + if err := database.ValidateKVKey(key); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", err.Error(), reqID) return } - body, err := io.ReadAll(io.LimitReader(r.Body, 1<<16)) + body, err := readBoundedBody(r.Body, 1<<16) if err != nil { respond.Error(w, http.StatusBadRequest, "INVALID_BODY", "failed to read body", reqID) return @@ -289,7 +289,11 @@ func (h *KVOperatorHandler) Incr(w http.ResponseWriter, r *http.Request) { return } } - next, err := h.DB.KVIncr(fnID, key, req.Delta, req.TTLSeconds) + if err := database.ValidateKVTTL(req.TTLSeconds); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", err.Error(), reqID) + return + } + next, err := h.DB.KVIncrContext(r.Context(), fnID, key, req.Delta, req.TTLSeconds) if err != nil { respond.Error(w, http.StatusConflict, "KV_INCR_FAILED", err.Error(), reqID) return @@ -303,7 +307,7 @@ func (h *KVOperatorHandler) Incr(w http.ResponseWriter, r *http.Request) { type kvOperatorCASRequest struct { Expected *json.RawMessage `json:"expected"` New json.RawMessage `json:"new"` - TTLSeconds int `json:"ttl_seconds,omitempty"` + TTLSeconds *int `json:"ttl_seconds,omitempty"` } // CAS handles POST /api/v1/functions/{fn_id}/kv/{key}/cas. A failed precondition @@ -317,11 +321,11 @@ func (h *KVOperatorHandler) CAS(w http.ResponseWriter, r *http.Request) { return } key := r.PathValue("key") - if key == "" { - respond.Error(w, http.StatusBadRequest, "VALIDATION", "key is required", reqID) + if err := database.ValidateKVKey(key); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", err.Error(), reqID) return } - body, err := io.ReadAll(io.LimitReader(r.Body, kvOperatorBodyCap)) + body, err := readBoundedBody(r.Body, kvOperatorBodyCap) if err != nil { respond.Error(w, http.StatusBadRequest, "INVALID_BODY", "failed to read body", reqID) return @@ -338,8 +342,20 @@ func (h *KVOperatorHandler) CAS(w http.ResponseWriter, r *http.Request) { var expectedBytes []byte if req.Expected != nil { expectedBytes = []byte(*req.Expected) + if err := database.ValidateKVValue(expectedBytes); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", "expected value: "+err.Error(), reqID) + return + } + } + if err := database.ValidateKVValue(req.New); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", err.Error(), reqID) + return + } + if err := database.ValidateKVTTL(req.TTLSeconds); err != nil { + respond.Error(w, http.StatusBadRequest, "VALIDATION", err.Error(), reqID) + return } - swapped, current, err := h.DB.KVCAS(fnID, key, expectedBytes, []byte(req.New), req.TTLSeconds) + swapped, current, err := h.DB.KVCASContext(r.Context(), fnID, key, expectedBytes, []byte(req.New), req.TTLSeconds) if err != nil { respond.Error(w, http.StatusInternalServerError, "INTERNAL", "kv cas failed: "+err.Error(), reqID) return diff --git a/backend/internal/server/handlers/metrics_format_test.go b/backend/internal/server/handlers/metrics_format_test.go index b75e0d6c..dd190be2 100644 --- a/backend/internal/server/handlers/metrics_format_test.go +++ b/backend/internal/server/handlers/metrics_format_test.go @@ -42,3 +42,24 @@ func TestMetricsExpositionFormat(t *testing.T) { t.Errorf("duration histogram buckets missing\n---\n%s", body) } } + +func TestKVMetricsAndWriterSaturationAreExposed(t *testing.T) { + db := newTestDB(t) + h := &SystemHandler{Metrics: metrics.New(), DB: db} + req := httptest.NewRequest("GET", "/metrics", nil) + w := httptest.NewRecorder() + h.GetMetrics(w, req) + body := w.Body.String() + for _, want := range []string{ + "# TYPE orva_kv_operations_total counter", + "orva_kv_operations_total{operation=\"put\"}", + "# TYPE orva_kv_batch_rollbacks_total counter", + "# TYPE orva_writer_queue_depth gauge", + "# TYPE orva_writer_critical_failures_total counter", + "# TYPE orva_writer_dropped_telemetry_total counter", + } { + if !strings.Contains(body, want) { + t.Errorf("metrics output missing %q", want) + } + } +} diff --git a/backend/internal/server/handlers/replay.go b/backend/internal/server/handlers/replay.go index 500f51a5..1adc4627 100644 --- a/backend/internal/server/handlers/replay.go +++ b/backend/internal/server/handlers/replay.go @@ -14,6 +14,7 @@ import ( "github.com/Harsh-2002/Orva/backend/internal/metrics" "github.com/Harsh-2002/Orva/backend/internal/pool" "github.com/Harsh-2002/Orva/backend/internal/registry" + "github.com/Harsh-2002/Orva/backend/internal/sdkauth" "github.com/Harsh-2002/Orva/backend/internal/server/handlers/respond" "github.com/Harsh-2002/Orva/backend/internal/trace" "github.com/Harsh-2002/Orva/internal/ids" @@ -30,6 +31,7 @@ type ReplayHandler struct { Registry *registry.Registry Pool *pool.Manager Metrics *metrics.Metrics + SDKAuth *sdkauth.Authenticator // PublishEvent fans the new execution out to the SSE hub so the // dashboard's InvocationsLog prepends the replay row without polling. @@ -179,6 +181,8 @@ func (h *ReplayHandler) Replay(w http.ResponseWriter, r *http.Request) { } eventJSON, _ := json.Marshal(event) + releaseExecution := h.SDKAuth.BindExecution(newExecID, fn.ID, traceID, spanID, start) + defer releaseExecution() respJSON, stderr, err := acq.Worker.Dispatch(ctx, eventJSON) duration := time.Since(start) if h.Metrics != nil { diff --git a/backend/internal/server/handlers/sdk_auth_test.go b/backend/internal/server/handlers/sdk_auth_test.go new file mode 100644 index 00000000..e4965cb3 --- /dev/null +++ b/backend/internal/server/handlers/sdk_auth_test.go @@ -0,0 +1,133 @@ +package handlers + +import ( + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/Harsh-2002/Orva/backend/internal/database" + "github.com/Harsh-2002/Orva/backend/internal/sdkauth" +) + +func insertSDKTestFunction(t *testing.T, db *database.Database, id, name string) { + t.Helper() + err := db.InsertFunction(&database.Function{ + ID: id, Name: name, Runtime: "node", Entrypoint: "handler.js", TimeoutMS: 30000, + MemoryMB: 64, CPUs: 0.5, EnvVars: map[string]string{}, NetworkMode: "none", Status: "active", + }) + if err != nil { + t.Fatal(err) + } +} + +func TestKVScopedCredentialRejectsCrossNamespaceAndCallerSpoof(t *testing.T) { + db := newTestDB(t) + insertSDKTestFunction(t, db, "fn-sdk-a", "sdk-a") + insertSDKTestFunction(t, db, "fn-sdk-b", "sdk-b") + auth := sdkauth.New([]byte("process-secret")) + h := &KVHandler{DB: db, SDKAuth: auth} + + cross := httptest.NewRequest(http.MethodPut, "/api/v1/_kv/fn-sdk-b/key", bytes.NewBufferString(`{"value":1}`)) + cross.SetPathValue("fn_id", "fn-sdk-b") + cross.SetPathValue("key", "key") + cross.Header.Set("X-Orva-Internal-Token", auth.Mint("fn-sdk-a")) + cross.Header.Set("X-Orva-Caller-Function", "fn-sdk-b") + crossResult := httptest.NewRecorder() + h.Put(crossResult, cross) + if crossResult.Code != http.StatusForbidden { + t.Fatalf("cross-namespace status=%d body=%s", crossResult.Code, crossResult.Body.String()) + } + + spoof := httptest.NewRequest(http.MethodPut, "/api/v1/_kv/fn-sdk-a/key", bytes.NewBufferString(`{"value":1}`)) + spoof.SetPathValue("fn_id", "fn-sdk-a") + spoof.SetPathValue("key", "key") + spoof.Header.Set("X-Orva-Internal-Token", auth.Mint("fn-sdk-a")) + spoof.Header.Set("X-Orva-Caller-Function", "fn-sdk-b") + spoofResult := httptest.NewRecorder() + h.Put(spoofResult, spoof) + if spoofResult.Code != http.StatusOK { + t.Fatalf("signed caller rejected because of spoofable header: status=%d body=%s", spoofResult.Code, spoofResult.Body.String()) + } + if _, err := db.KVGet("fn-sdk-a", "key"); err != nil { + t.Fatalf("signed namespace was not written: %v", err) + } + if _, err := db.KVGet("fn-sdk-b", "key"); err != database.ErrKVNotFound { + t.Fatalf("spoofed namespace was written: %v", err) + } +} + +func TestJobAttributionUsesSignedCaller(t *testing.T) { + db := newTestDB(t) + insertSDKTestFunction(t, db, "fn-job-caller", "job-caller") + insertSDKTestFunction(t, db, "fn-job-target", "job-target") + auth := sdkauth.New([]byte("process-secret")) + release := auth.BindExecution("exec-job", "fn-job-caller", "trace-signed", "span-signed", time.Now()) + defer release() + h := &JobsHandler{DB: db, SDKAuth: auth} + body := bytes.NewBufferString(`{"function_name":"job-target","payload":{}}`) + req := httptest.NewRequest(http.MethodPost, "/api/v1/jobs", body) + req.Header.Set("X-Orva-Internal-Token", auth.Mint("fn-job-caller")) + req.Header.Set("X-Orva-Caller-Function", "fn-spoof") + req.Header.Set("X-Orva-Execution-Id", "exec-job") + req.Header.Set("X-Orva-Trace-Id", "trace-spoof") + req.Header.Set("X-Orva-Span-Id", "span-spoof") + w := httptest.NewRecorder() + h.Enqueue(w, req) + if w.Code != http.StatusCreated { + t.Fatalf("status=%d body=%s", w.Code, w.Body.String()) + } + var job database.Job + if err := json.NewDecoder(w.Body).Decode(&job); err != nil { + t.Fatal(err) + } + if job.EnqueuedByFunctionID != "fn-job-caller" { + t.Fatalf("enqueued_by=%q", job.EnqueuedByFunctionID) + } + if job.TraceID != "trace-signed" || job.ParentSpanID != "span-signed" { + t.Fatalf("trace attribution=%q/%q", job.TraceID, job.ParentSpanID) + } +} + +func TestUserSpanMustBelongToSignedCaller(t *testing.T) { + auth := sdkauth.New([]byte("process-secret")) + release := auth.BindExecution("exec-owned", "fn-owner", "trace", "span", time.Now()) + defer release() + h := &SpansHandler{DB: newTestDB(t), SDKAuth: auth} + req := httptest.NewRequest(http.MethodPost, "/api/v1/_internal/spans", bytes.NewBufferString(`{"name":"work","duration_ms":1}`)) + req.Header.Set("X-Orva-Internal-Token", auth.Mint("fn-other")) + req.Header.Set("X-Orva-Trace-Id", "trace") + req.Header.Set("X-Orva-Span-Id", "span") + req.Header.Set("X-Orva-Execution-Id", "exec-owned") + w := httptest.NewRecorder() + h.Create(w, req) + if w.Code != http.StatusForbidden { + t.Fatalf("status=%d body=%s", w.Code, w.Body.String()) + } +} + +func TestCronUpsertUsesSignedCallerScope(t *testing.T) { + db := newTestDB(t) + insertSDKTestFunction(t, db, "fn-cron-owner", "cron-owner") + insertSDKTestFunction(t, db, "fn-cron-other", "cron-other") + auth := sdkauth.New([]byte("process-secret")) + h := &CronHandler{DB: db, SDKAuth: auth} + req := httptest.NewRequest(http.MethodPost, "/api/v1/_internal/crons", bytes.NewBufferString(`{"name":"nightly","schedule":"0 3 * * *"}`)) + req.Header.Set("X-Orva-Internal-Token", auth.Mint("fn-cron-owner")) + req.Header.Set("X-Orva-Function-Id", "fn-cron-other") + w := httptest.NewRecorder() + h.UpsertInternal(w, req) + if w.Code != http.StatusOK { + t.Fatalf("status=%d body=%s", w.Code, w.Body.String()) + } + ownerRows, err := db.ListCronSchedulesForFunction("fn-cron-owner") + if err != nil || len(ownerRows) != 1 { + t.Fatalf("owner schedules=%d err=%v", len(ownerRows), err) + } + otherRows, err := db.ListCronSchedulesForFunction("fn-cron-other") + if err != nil || len(otherRows) != 0 { + t.Fatalf("other schedules=%d err=%v", len(otherRows), err) + } +} diff --git a/backend/internal/server/handlers/sdkversion.go b/backend/internal/server/handlers/sdkversion.go index 36ad1e34..36528f4e 100644 --- a/backend/internal/server/handlers/sdkversion.go +++ b/backend/internal/server/handlers/sdkversion.go @@ -18,7 +18,7 @@ var ( sdkVersionSeen = map[string]struct{}{} ) -// observeSDKVersion peeks at X-Orva-SDK-Version on internal-token +// observeSDKVersion peeks at X-Orva-SDK-Version on scoped-credential // requests. First sighting of a (function_id, sdk_version) tuple logs an // INFO; a version that differs from the SDK this server bundles // (version.SDKVersion) logs a WARN so operators see genuine drift. Always @@ -29,12 +29,12 @@ var ( // NOT version.Version (the vYYYY.MM.DD release tag). Comparing against the // release tag was a bug: the two value-spaces can never be equal, so it // logged a false WARN for every function on its first internal call. -func observeSDKVersion(r *http.Request) { +func observeSDKVersion(r *http.Request, functionID string) { got := r.Header.Get("X-Orva-SDK-Version") if got == "" { return } - fnID := r.Header.Get("X-Orva-Function-Id") + fnID := functionID if fnID == "" { // internal_invoke uses URL path; KV puts fn_id in the path too. We // only need a stable dedup key for the log line, so the empty diff --git a/backend/internal/server/handlers/spans.go b/backend/internal/server/handlers/spans.go index bfc47f07..5e1c98db 100644 --- a/backend/internal/server/handlers/spans.go +++ b/backend/internal/server/handlers/spans.go @@ -1,34 +1,33 @@ package handlers import ( - "crypto/subtle" "encoding/json" - "io" "net/http" "time" "github.com/Harsh-2002/Orva/backend/internal/database" + "github.com/Harsh-2002/Orva/backend/internal/sdkauth" "github.com/Harsh-2002/Orva/backend/internal/server/handlers/respond" ) // SpansHandler accepts user-defined spans emitted from orva.trace.span() -// inside a sandbox. Authentication is the per-process internal token +// inside a sandbox. Authentication is the function-scoped SDK credential // (same as KV and F2F). Insertion is fire-and-forget — the handler // returns 202 Accepted immediately and the async writer commits the row // alongside any other batched writes. type SpansHandler struct { - DB *database.Database - InternalToken string + DB *database.Database + SDKAuth *sdkauth.Authenticator } -func (h *SpansHandler) authorize(w http.ResponseWriter, r *http.Request) bool { - got := r.Header.Get("X-Orva-Internal-Token") - if h.InternalToken == "" || subtle.ConstantTimeCompare([]byte(got), []byte(h.InternalToken)) != 1 { +func (h *SpansHandler) authorize(w http.ResponseWriter, r *http.Request) (string, bool) { + caller, err := h.SDKAuth.Verify(r.Header.Get("X-Orva-Internal-Token")) + if err != nil { respond.Error(w, http.StatusUnauthorized, "UNAUTHORIZED", - "missing or invalid internal token", r.Header.Get("X-Request-ID")) - return false + "missing or invalid SDK credential", r.Header.Get("X-Request-ID")) + return "", false } - return true + return caller, true } // userSpanRequest is the wire shape the runtime SDK sends. The trace ID, @@ -47,20 +46,25 @@ type userSpanRequest struct { // Create handles POST /api/v1/_internal/spans. Returns 202 with the // generated span ID — the actual DB write is batched async. func (h *SpansHandler) Create(w http.ResponseWriter, r *http.Request) { - if !h.authorize(w, r) { + callerFnID, ok := h.authorize(w, r) + if !ok { return } reqID := r.Header.Get("X-Request-ID") - traceID := r.Header.Get("X-Orva-Trace-Id") - parentSpanID := r.Header.Get("X-Orva-Span-Id") execID := r.Header.Get("X-Orva-Execution-Id") - if traceID == "" || parentSpanID == "" || execID == "" { + if execID == "" { respond.Error(w, http.StatusBadRequest, "VALIDATION", - "missing trace/span/execution context headers", reqID) + "missing execution context header", reqID) + return + } + traceID, parentSpanID, executionStart, owned := h.SDKAuth.TraceContext(execID, callerFnID) + if !owned { + respond.Error(w, http.StatusForbidden, "SDK_SCOPE_VIOLATION", + "span execution is not active for the calling function", reqID) return } - body, err := io.ReadAll(io.LimitReader(r.Body, 1<<16)) + body, err := readBoundedBody(r.Body, 1<<16) if err != nil { respond.Error(w, http.StatusBadRequest, "INVALID_BODY", "failed to read body", reqID) return @@ -83,11 +87,9 @@ func (h *SpansHandler) Create(w http.ResponseWriter, r *http.Request) { // row's final insert), we fall back to "now since some reasonable // origin" — the dashboard renders these defensively. offset := int64(0) - if exec, err := h.DB.GetExecution(execID); err == nil && !exec.StartedAt.IsZero() { - d := req.StartedAt.Sub(exec.StartedAt) - if d > 0 { - offset = d.Milliseconds() - } + offset = req.StartedAt.Sub(executionStart).Milliseconds() + if offset < 0 { + offset = 0 } span := &database.UserSpan{ diff --git a/backend/internal/server/handlers/system.go b/backend/internal/server/handlers/system.go index e978604a..d2f0c2ce 100644 --- a/backend/internal/server/handlers/system.go +++ b/backend/internal/server/handlers/system.go @@ -41,24 +41,24 @@ type SystemHandler struct { // /metrics endpoint stays for Prometheus scrapers; this is the cheaper // path for the dashboard so it doesn't have to parse Prom text. type MetricsJSONShape struct { - UptimeSeconds int64 `json:"uptime_seconds"` - Host hostBlock `json:"host"` - Totals totalsBlock `json:"totals"` - Rates ratesBlock `json:"rates"` - ActiveRequests int64 `json:"active_requests"` - LatencyMS latencyBlock `json:"latency_ms"` - Sandbox sandboxBlock `json:"sandbox"` - BuildQueue buildQueueBlock `json:"build_queue"` - Pools []poolBlock `json:"pools"` + UptimeSeconds int64 `json:"uptime_seconds"` + Host hostBlock `json:"host"` + Totals totalsBlock `json:"totals"` + Rates ratesBlock `json:"rates"` + ActiveRequests int64 `json:"active_requests"` + LatencyMS latencyBlock `json:"latency_ms"` + Sandbox sandboxBlock `json:"sandbox"` + BuildQueue buildQueueBlock `json:"build_queue"` + Pools []poolBlock `json:"pools"` } type hostBlock struct { - NumCPU int `json:"num_cpu"` - NumGoroutines int `json:"num_goroutines"` - OrvaAllocMB int64 `json:"orva_alloc_mb"` - MemTotalMB int64 `json:"mem_total_mb"` - MemAvailableMB int64 `json:"mem_available_mb"` - MemReservedMB int64 `json:"mem_reserved_mb"` + NumCPU int `json:"num_cpu"` + NumGoroutines int `json:"num_goroutines"` + OrvaAllocMB int64 `json:"orva_alloc_mb"` + MemTotalMB int64 `json:"mem_total_mb"` + MemAvailableMB int64 `json:"mem_available_mb"` + MemReservedMB int64 `json:"mem_reserved_mb"` } type totalsBlock struct { @@ -102,10 +102,10 @@ type poolBlock struct { LatencyEWMAms float64 `json:"latency_ewma_ms"` DynamicMax int64 `json:"dynamic_max"` Target int `json:"target"` - MemUsedAvgMB float64 `json:"mem_used_avg_mb"` // EWMA of memory.current at release; 0 if cgroups disabled - CPUFracAvg float64 `json:"cpu_frac_avg"` // EWMA of CPU fraction per invocation (0–1) - MemLimitMB int64 `json:"mem_limit_mb"` // configured memory_mb for this function - CPULimit float64 `json:"cpu_limit"` // configured cpus for this function (0 = uncapped) + MemUsedAvgMB float64 `json:"mem_used_avg_mb"` // EWMA of memory.current at release; 0 if cgroups disabled + CPUFracAvg float64 `json:"cpu_frac_avg"` // EWMA of CPU fraction per invocation (0–1) + MemLimitMB int64 `json:"mem_limit_mb"` // configured memory_mb for this function + CPULimit float64 `json:"cpu_limit"` // configured cpus for this function (0 = uncapped) } // Health handles GET /api/v1/system/health. @@ -172,11 +172,26 @@ func (h *SystemHandler) Health(w http.ResponseWriter, r *http.Request) { "runtime": sandboxRuntime, }, "host": map[string]any{ - "num_cpu": runtime.NumCPU(), + "num_cpu": runtime.NumCPU(), "num_goroutine": runtime.NumGoroutine(), - "alloc_mb": int(memStats.Alloc / 1024 / 1024), + "alloc_mb": int(memStats.Alloc / 1024 / 1024), }, } + if h.DB != nil { + writer := h.DB.WriterStats() + writerStatus := "ok" + if (writer.CriticalCap > 0 && writer.CriticalDepth*100/writer.CriticalCap >= 80) || + (writer.TelemetryCap > 0 && writer.TelemetryDepth*100/writer.TelemetryCap >= 80) { + writerStatus = "saturated" + } + resp["writer"] = map[string]any{ + "status": writerStatus, "critical_queue_depth": writer.CriticalDepth, + "telemetry_queue_depth": writer.TelemetryDepth, + "critical_timeouts": writer.CriticalTimeouts, + "critical_failures": writer.CriticalFailures, + "dropped_telemetry": writer.DroppedTelemetry, + } + } respond.JSON(w, httpStatus, resp) } @@ -246,6 +261,29 @@ func (h *SystemHandler) GetMetrics(w http.ResponseWriter, r *http.Request) { // path. Returns 0 cleanly when there are no pending jobs (or when // the DB handle is missing in tests). if h.DB != nil { + writer := h.DB.WriterStats() + promHeader(w, "orva_writer_queue_depth", "gauge", "Pending asynchronous database writes by priority.") + fmt.Fprintf(w, "orva_writer_queue_depth{priority=\"critical\"} %d\n", writer.CriticalDepth) + fmt.Fprintf(w, "orva_writer_queue_depth{priority=\"telemetry\"} %d\n", writer.TelemetryDepth) + promHeader(w, "orva_writer_critical_timeouts_total", "counter", "Critical database writes that exceeded their enqueue deadline.") + fmt.Fprintf(w, "orva_writer_critical_timeouts_total %d\n", writer.CriticalTimeouts) + promHeader(w, "orva_writer_critical_failures_total", "counter", "Critical database writes lost to transaction failures after enqueue.") + fmt.Fprintf(w, "orva_writer_critical_failures_total %d\n", writer.CriticalFailures) + promHeader(w, "orva_writer_dropped_telemetry_total", "counter", "Telemetry writes dropped because the bounded queue was full.") + fmt.Fprintf(w, "orva_writer_dropped_telemetry_total %d\n", writer.DroppedTelemetry) + kv := h.DB.KVMetrics() + promHeader(w, "orva_kv_operations_total", "counter", "KV operations by type.") + promHeader(w, "orva_kv_errors_total", "counter", "KV operation failures by type.") + promHeader(w, "orva_kv_timeouts_total", "counter", "KV operations canceled by request or function deadlines.") + promHeader(w, "orva_kv_latency_ms_total", "counter", "Cumulative KV operation latency in milliseconds.") + for _, operation := range kv.Operations { + fmt.Fprintf(w, "orva_kv_operations_total{operation=%q} %d\n", operation.Operation, operation.Count) + fmt.Fprintf(w, "orva_kv_errors_total{operation=%q} %d\n", operation.Operation, operation.Errors) + fmt.Fprintf(w, "orva_kv_timeouts_total{operation=%q} %d\n", operation.Operation, operation.Timeouts) + fmt.Fprintf(w, "orva_kv_latency_ms_total{operation=%q} %.3f\n", operation.Operation, float64(operation.LatencyTotalNS)/float64(time.Millisecond)) + } + promHeader(w, "orva_kv_batch_rollbacks_total", "counter", "Atomic KV batches rolled back after database or deadline failure.") + fmt.Fprintf(w, "orva_kv_batch_rollbacks_total %d\n", kv.Rollbacks) var depth int64 if rdb := h.DB.ReadDB(); rdb != nil { _ = rdb.QueryRow(`SELECT COUNT(*) FROM jobs WHERE status='pending'`).Scan(&depth) @@ -423,13 +461,13 @@ type SystemStorageHandler struct { // StorageInfo is the response shape for GET /api/v1/system/storage. type StorageInfo struct { - DBBytes int64 `json:"db_bytes"` // size of orva.db on disk - DBPages int64 `json:"db_pages"` // PRAGMA page_count - DBPageSize int64 `json:"db_page_size"` // PRAGMA page_size - DBFreePages int64 `json:"db_free_pages"` // PRAGMA freelist_count — reclaimable on next VACUUM - WALBytes int64 `json:"wal_bytes"` // size of orva.db-wal sidecar - FunctionsBytes int64 `json:"functions_bytes"` // recursive size of /functions - TotalBytes int64 `json:"total_bytes"` // db + wal + functions + DBBytes int64 `json:"db_bytes"` // size of orva.db on disk + DBPages int64 `json:"db_pages"` // PRAGMA page_count + DBPageSize int64 `json:"db_page_size"` // PRAGMA page_size + DBFreePages int64 `json:"db_free_pages"` // PRAGMA freelist_count — reclaimable on next VACUUM + WALBytes int64 `json:"wal_bytes"` // size of orva.db-wal sidecar + FunctionsBytes int64 `json:"functions_bytes"` // recursive size of /functions + TotalBytes int64 `json:"total_bytes"` // db + wal + functions } // VacuumResult is the response shape for POST /api/v1/system/vacuum. diff --git a/backend/internal/server/handlers/system_health_test.go b/backend/internal/server/handlers/system_health_test.go index be9536e5..0e889598 100644 --- a/backend/internal/server/handlers/system_health_test.go +++ b/backend/internal/server/handlers/system_health_test.go @@ -59,6 +59,10 @@ func TestHealthNsjailAbsentStays200(t *testing.T) { if sb["runtime"] != "unavailable" { t.Errorf("sandbox.runtime = %v, want unavailable", sb["runtime"]) } + writer, ok := body["writer"].(map[string]any) + if !ok || writer["status"] != "ok" { + t.Errorf("writer health missing or unhealthy: %v", body["writer"]) + } } // TestHealthDBDownReturns503 confirms the hard gate at the handler level. diff --git a/backend/internal/server/middleware_auth.go b/backend/internal/server/middleware_auth.go index 3f6e6fd4..a57f8555 100644 --- a/backend/internal/server/middleware_auth.go +++ b/backend/internal/server/middleware_auth.go @@ -2,7 +2,6 @@ package server import ( "crypto/sha256" - "crypto/subtle" "database/sql" "encoding/hex" "encoding/json" @@ -12,6 +11,7 @@ import ( "time" "github.com/Harsh-2002/Orva/backend/internal/database" + "github.com/Harsh-2002/Orva/backend/internal/sdkauth" ) // sessionCacheEntry is a short-TTL memo of a successful session lookup so @@ -28,7 +28,7 @@ const sessionCacheTTL = 30 * time.Second // owned by the Router and shared so the keys handler can evict an entry the // instant a key is deleted — otherwise a revoked key would keep authenticating // until process restart (it is only otherwise dropped on expiry). -func authMiddleware(db *database.Database, keyCache *sync.Map, internalToken string, next http.Handler) http.Handler { +func authMiddleware(db *database.Database, keyCache *sync.Map, sdkAuth *sdkauth.Authenticator, next http.Handler) http.Handler { var sessionCache sync.Map // token -> sessionCacheEntry return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -50,37 +50,39 @@ func authMiddleware(db *database.Database, keyCache *sync.Map, internalToken str next.ServeHTTP(w, r) return } - // Internal SDK endpoints (KV / F2F invoke) authenticate via the - // per-process internal token, NOT API keys. Skip the API-key - // middleware here; the handlers themselves enforce the token. We - // still tag the actor as sdk so the activity log identifies the - // calling function (X-Orva-Caller-Function header is set by the - // SDK adapter for both KV and invoke paths). + // Internal SDK endpoints authenticate with a process-signed, + // function-scoped credential rather than an API key. The handlers + // enforce route-specific scope. Strip the legacy caller header: the + // verified claim is carried only in the request actor context, while + // each handler verifies the signed credential independently. if strings.HasPrefix(r.URL.Path, "/api/v1/_kv/") || strings.HasPrefix(r.URL.Path, "/api/v1/_internal/") { - caller := r.Header.Get("X-Orva-Caller-Function") - actor := &Actor{Source: "sdk", Type: "internal_token", ID: caller, Label: caller} + caller, _ := sdkAuth.Verify(r.Header.Get("X-Orva-Internal-Token")) + r.Header.Del("X-Orva-Caller-Function") + actor := &Actor{Source: "sdk", Type: "scoped_token", ID: caller, Label: caller} next.ServeHTTP(w, r.WithContext(WithActor(r.Context(), actor))) return } - // Worker SDK requests bearing the CORRECT internal token bypass the + // Worker SDK requests bearing a valid scoped credential bypass the // public auth gate; this lets jobs.enqueue() from inside a sandbox // share the /api/v1/jobs route with the dashboard. The token MUST - // match the per-process secret — a merely-present header is not + // verify against the process signing key — a merely-present header is not // enough (a non-empty check would let any value skip authentication // entirely, and the downstream handler trusts this middleware to // have authenticated). On mismatch we do NOT short-circuit: fall // through to normal session/API-key auth, so a stray header on a // real request still authenticates and a bogus-token-only request // gets a 401 like any other unauthenticated caller. - if got := r.Header.Get("X-Orva-Internal-Token"); got != "" && - internalToken != "" && - subtle.ConstantTimeCompare([]byte(got), []byte(internalToken)) == 1 { - caller := r.Header.Get("X-Orva-Caller-Function") - actor := &Actor{Source: "sdk", Type: "internal_token", ID: caller, Label: caller} - next.ServeHTTP(w, r.WithContext(WithActor(r.Context(), actor))) - return + if r.Method == http.MethodPost && r.URL.Path == "/api/v1/jobs" { + r.Header.Del("X-Orva-Caller-Function") + caller, err := sdkAuth.Verify(r.Header.Get("X-Orva-Internal-Token")) + if err == nil { + actor := &Actor{Source: "sdk", Type: "scoped_token", ID: caller, Label: caller} + next.ServeHTTP(w, r.WithContext(WithActor(r.Context(), actor))) + return + } } + r.Header.Del("X-Orva-Caller-Function") // Try session cookie first (browser UI). if cookie, err := r.Cookie("session_token"); err == nil { diff --git a/backend/internal/server/middleware_auth_test.go b/backend/internal/server/middleware_auth_test.go index c0de6bbf..92a6863a 100644 --- a/backend/internal/server/middleware_auth_test.go +++ b/backend/internal/server/middleware_auth_test.go @@ -7,19 +7,17 @@ import ( ) // TestInternalTokenBypassClosed is the regression guard for the auth bypass: -// authMiddleware must only skip the API-key gate for the CORRECT per-process -// internal token. A merely-present (bogus) X-Orva-Internal-Token must NOT +// authMiddleware must only skip the job enqueue gate for a verified scoped +// SDK credential. A merely-present (bogus) X-Orva-Internal-Token must NOT // grant access — it has to fall through to normal session/API-key auth and // get a 401, exactly like an unauthenticated caller. // -// Before the fix, `if r.Header.Get("X-Orva-Internal-Token") != ""` let any -// non-empty value skip authentication entirely, so a request with a garbage -// header reached the handlers with full operator power. -func TestInternalTokenBypassClosed(t *testing.T) { +// SDK credentials must never grant access to the broader operator API. +func TestScopedSDKCredentialCannotBypassOperatorAuth(t *testing.T) { tc := newTestServer(t) - real := tc.srv.router.internalToken + real := tc.srv.router.sdkAuth.Mint("function-a") if real == "" { - t.Fatal("router.internalToken is empty; the middleware can never authenticate the SDK path") + t.Fatal("scoped SDK credential is empty") } // A protected admin-surface endpoint that the live exploit hit. @@ -50,11 +48,11 @@ func TestInternalTokenBypassClosed(t *testing.T) { wantStatus: http.StatusUnauthorized, }, { - name: "correct internal token is accepted (SDK path still works)", + name: "valid SDK credential cannot access operator APIs", setup: func(r *http.Request) { r.Header.Set("X-Orva-Internal-Token", real) }, - wantStatus: http.StatusOK, + wantStatus: http.StatusUnauthorized, }, { name: "valid API key is accepted (normal path unaffected)", diff --git a/backend/internal/server/router.go b/backend/internal/server/router.go index 04ebf188..1a0fa196 100644 --- a/backend/internal/server/router.go +++ b/backend/internal/server/router.go @@ -17,6 +17,7 @@ import ( "github.com/Harsh-2002/Orva/backend/internal/pool" "github.com/Harsh-2002/Orva/backend/internal/proxy" "github.com/Harsh-2002/Orva/backend/internal/registry" + "github.com/Harsh-2002/Orva/backend/internal/sdkauth" "github.com/Harsh-2002/Orva/backend/internal/secrets" "github.com/Harsh-2002/Orva/backend/internal/server/events" "github.com/Harsh-2002/Orva/backend/internal/server/handlers" @@ -29,16 +30,16 @@ type Router struct { cfg *config.Config db *database.Database - registry *registry.Registry - proxy *proxy.Proxy - builder *builder.Builder - metrics *metrics.Metrics - secrets *secrets.Manager - buildQueue *builder.Queue - poolMgr *pool.Manager - eventHub *events.Hub - firewall *firewall.Manager - internalToken string + registry *registry.Registry + proxy *proxy.Proxy + builder *builder.Builder + metrics *metrics.Metrics + secrets *secrets.Manager + buildQueue *builder.Queue + poolMgr *pool.Manager + eventHub *events.Hub + firewall *firewall.Manager + sdkAuth *sdkauth.Authenticator ai *aipkg.Manager // in-product AI chat assistant (lazily-built LLM gateway) @@ -49,17 +50,17 @@ type Router struct { // RouterDeps holds the dependencies for creating a Router. type RouterDeps struct { - Registry *registry.Registry - Proxy *proxy.Proxy - Builder *builder.Builder - Metrics *metrics.Metrics - Secrets *secrets.Manager - BuildQueue *builder.Queue - PoolMgr *pool.Manager - EventHub *events.Hub - Firewall *firewall.Manager - InternalToken string // Per-process token for kv/jobs/F2F SDK auth (Phase 3+). - StartTime time.Time // Shared process start time (health/uptime). Zero → time.Now(). + Registry *registry.Registry + Proxy *proxy.Proxy + Builder *builder.Builder + Metrics *metrics.Metrics + Secrets *secrets.Manager + BuildQueue *builder.Queue + PoolMgr *pool.Manager + EventHub *events.Hub + Firewall *firewall.Manager + SDKAuth *sdkauth.Authenticator // Function-scoped worker credential verifier. + StartTime time.Time // Shared process start time (health/uptime). Zero → time.Now(). } func NewRouter(cfg *config.Config, db *database.Database, deps RouterDeps) *Router { @@ -72,12 +73,12 @@ func NewRouter(cfg *config.Config, db *database.Database, deps RouterDeps) *Rout builder: deps.Builder, metrics: deps.Metrics, secrets: deps.Secrets, - buildQueue: deps.BuildQueue, - poolMgr: deps.PoolMgr, - eventHub: deps.EventHub, - firewall: deps.Firewall, - internalToken: deps.InternalToken, - startTime: deps.StartTime, + buildQueue: deps.BuildQueue, + poolMgr: deps.PoolMgr, + eventHub: deps.EventHub, + firewall: deps.Firewall, + sdkAuth: deps.SDKAuth, + startTime: deps.StartTime, } if r.startTime.IsZero() { r.startTime = time.Now() @@ -181,6 +182,7 @@ func (r *Router) setupRoutes() { Registry: r.registry, Pool: r.poolMgr, Metrics: r.metrics, + SDKAuth: r.sdkAuth, } if r.eventHub != nil { replayHandler.PublishEvent = r.eventHub.Publish @@ -241,66 +243,64 @@ func (r *Router) setupRoutes() { // Per-(function, name) UNIQUE; PUT acts as an upsert on the {name} // path segment so callers don't need a separate insert/update split. fixtureHandler := &handlers.FixtureHandler{DB: r.db, Registry: r.registry} - r.mux.HandleFunc("GET /api/v1/functions/{fn_id}/fixtures", fixtureHandler.List) - r.mux.HandleFunc("POST /api/v1/functions/{fn_id}/fixtures", fixtureHandler.Create) + r.mux.HandleFunc("GET /api/v1/functions/{fn_id}/fixtures", fixtureHandler.List) + r.mux.HandleFunc("POST /api/v1/functions/{fn_id}/fixtures", fixtureHandler.Create) r.mux.HandleFunc("GET /api/v1/functions/{fn_id}/fixtures/{name}", fixtureHandler.Get) r.mux.HandleFunc("PUT /api/v1/functions/{fn_id}/fixtures/{name}", fixtureHandler.Upsert) r.mux.HandleFunc("DELETE /api/v1/functions/{fn_id}/fixtures/{name}", fixtureHandler.Delete) // Cron schedules (per-function, fired by internal/scheduler). - cronHandler := &handlers.CronHandler{DB: r.db, Registry: r.registry, InternalToken: r.internalToken} - r.mux.HandleFunc("GET /api/v1/functions/{fn_id}/cron", cronHandler.List) - r.mux.HandleFunc("POST /api/v1/functions/{fn_id}/cron", cronHandler.Create) - r.mux.HandleFunc("PUT /api/v1/functions/{fn_id}/cron/{id}", cronHandler.Update) - r.mux.HandleFunc("DELETE /api/v1/functions/{fn_id}/cron/{id}", cronHandler.Delete) + cronHandler := &handlers.CronHandler{DB: r.db, Registry: r.registry, SDKAuth: r.sdkAuth} + r.mux.HandleFunc("GET /api/v1/functions/{fn_id}/cron", cronHandler.List) + r.mux.HandleFunc("POST /api/v1/functions/{fn_id}/cron", cronHandler.Create) + r.mux.HandleFunc("PUT /api/v1/functions/{fn_id}/cron/{id}", cronHandler.Update) + r.mux.HandleFunc("DELETE /api/v1/functions/{fn_id}/cron/{id}", cronHandler.Delete) // Dashboard "Schedules" page lists schedules across all functions. r.mux.HandleFunc("GET /api/v1/cron", cronHandler.ListAll) // Internal token used by the worker SDK (kv, jobs, F2F invoke). - internalToken := r.internalToken - // Per-function key/value store (Phase 3). Internal-only — auth is the - // per-process internal token, not API keys. - kvHandler := &handlers.KVHandler{DB: r.db, InternalToken: internalToken} + // function-scoped SDK credential, not API keys. + kvHandler := &handlers.KVHandler{DB: r.db, SDKAuth: r.sdkAuth} r.mux.HandleFunc("PUT /api/v1/_kv/{fn_id}/{key}", kvHandler.Put) r.mux.HandleFunc("GET /api/v1/_kv/{fn_id}/{key}", kvHandler.Get) r.mux.HandleFunc("DELETE /api/v1/_kv/{fn_id}/{key}", kvHandler.Delete) - r.mux.HandleFunc("GET /api/v1/_kv/{fn_id}", kvHandler.List) + r.mux.HandleFunc("GET /api/v1/_kv/{fn_id}", kvHandler.List) // v0.6 SDK upgrade: batch / atomic increment / compare-and-swap. - r.mux.HandleFunc("POST /api/v1/_kv/{fn_id}/batch", kvHandler.Batch) - r.mux.HandleFunc("POST /api/v1/_kv/{fn_id}/{key}/incr", kvHandler.Incr) - r.mux.HandleFunc("POST /api/v1/_kv/{fn_id}/{key}/cas", kvHandler.CAS) + r.mux.HandleFunc("POST /api/v1/_kv/{fn_id}/batch", kvHandler.Batch) + r.mux.HandleFunc("POST /api/v1/_kv/{fn_id}/{key}/incr", kvHandler.Incr) + r.mux.HandleFunc("POST /api/v1/_kv/{fn_id}/{key}/cas", kvHandler.CAS) // Function-to-function calls (Phase 4). Path uses the friendly name. f2fHandler := &handlers.InternalInvokeHandler{ - DB: r.db, Registry: r.registry, Pool: r.poolMgr, Metrics: r.metrics, InternalToken: internalToken, + DB: r.db, Registry: r.registry, Pool: r.poolMgr, Metrics: r.metrics, SDKAuth: r.sdkAuth, } - r.mux.HandleFunc("POST /api/v1/_internal/invoke/{name}", f2fHandler.Invoke) + r.mux.HandleFunc("POST /api/v1/_internal/invoke/{name}", f2fHandler.Invoke) // v0.6 SDK upgrade: chunked-transfer streaming variant for invokeStream. r.mux.HandleFunc("POST /api/v1/_internal/invoke/{name}/stream", f2fHandler.InvokeStream) // v0.6 SDK upgrade: user-defined spans + SDK-driven cron upsert. - spansHandler := &handlers.SpansHandler{DB: r.db, InternalToken: internalToken} + spansHandler := &handlers.SpansHandler{DB: r.db, SDKAuth: r.sdkAuth} r.mux.HandleFunc("POST /api/v1/_internal/spans", spansHandler.Create) r.mux.HandleFunc("POST /api/v1/_internal/crons", cronHandler.UpsertInternal) - // Background job queue (Phase 5). Public + internal token both work. + // Background job queue (Phase 5). Public + scoped SDK credentials both work. jobsHandler := &handlers.JobsHandler{ - DB: r.db, Registry: r.registry, InternalToken: internalToken, + DB: r.db, Registry: r.registry, SDKAuth: r.sdkAuth, } - r.mux.HandleFunc("POST /api/v1/jobs", jobsHandler.Enqueue) - r.mux.HandleFunc("GET /api/v1/jobs", jobsHandler.List) - r.mux.HandleFunc("GET /api/v1/jobs/{id}", jobsHandler.Get) + r.mux.HandleFunc("POST /api/v1/jobs", jobsHandler.Enqueue) + r.mux.HandleFunc("GET /api/v1/jobs", jobsHandler.List) + r.mux.HandleFunc("GET /api/v1/jobs/{id}", jobsHandler.Get) r.mux.HandleFunc("POST /api/v1/jobs/{id}/retry", jobsHandler.Retry) - r.mux.HandleFunc("DELETE /api/v1/jobs/{id}", jobsHandler.Delete) + r.mux.HandleFunc("DELETE /api/v1/jobs/{id}", jobsHandler.Delete) // Inbound webhook triggers (v0.4 C2a). CRUD lives under the // authenticated /api/v1/functions/{fn_id}/inbound-webhooks tree; // the public POST /webhook/{id} that external services hit is // registered separately below so the auth middleware skips it. inboundHandler := &handlers.InboundWebhookHandler{DB: r.db, Registry: r.registry} - r.mux.HandleFunc("GET /api/v1/functions/{fn_id}/inbound-webhooks", inboundHandler.List) - r.mux.HandleFunc("POST /api/v1/functions/{fn_id}/inbound-webhooks", inboundHandler.Create) + r.mux.HandleFunc("GET /api/v1/functions/{fn_id}/inbound-webhooks", inboundHandler.List) + r.mux.HandleFunc("POST /api/v1/functions/{fn_id}/inbound-webhooks", inboundHandler.Create) r.mux.HandleFunc("GET /api/v1/functions/{fn_id}/inbound-webhooks/{id}", inboundHandler.Get) r.mux.HandleFunc("PUT /api/v1/functions/{fn_id}/inbound-webhooks/{id}", inboundHandler.Update) r.mux.HandleFunc("DELETE /api/v1/functions/{fn_id}/inbound-webhooks/{id}", inboundHandler.Delete) @@ -310,7 +310,7 @@ func (r *Router) setupRoutes() { // don't need an API key. Authentication is the HMAC signature on // the request body itself. inboundTrigger := &handlers.InboundTriggerHandler{ - DB: r.db, Registry: r.registry, Pool: r.poolMgr, Metrics: r.metrics, + DB: r.db, Registry: r.registry, Pool: r.poolMgr, Metrics: r.metrics, SDKAuth: r.sdkAuth, } if r.eventHub != nil { inboundTrigger.PublishEvent = r.eventHub.Publish @@ -321,13 +321,13 @@ func (r *Router) setupRoutes() { // events fan out to subscribers via internal/scheduler's webhook // delivery loop. webhooksHandler := &handlers.WebhooksHandler{DB: r.db} - r.mux.HandleFunc("GET /api/v1/webhooks", webhooksHandler.List) - r.mux.HandleFunc("POST /api/v1/webhooks", webhooksHandler.Create) - r.mux.HandleFunc("GET /api/v1/webhooks/{id}", webhooksHandler.Get) - r.mux.HandleFunc("PUT /api/v1/webhooks/{id}", webhooksHandler.Update) - r.mux.HandleFunc("DELETE /api/v1/webhooks/{id}", webhooksHandler.Delete) - r.mux.HandleFunc("POST /api/v1/webhooks/{id}/test", webhooksHandler.Test) - r.mux.HandleFunc("GET /api/v1/webhooks/{id}/deliveries", webhooksHandler.ListDeliveries) + r.mux.HandleFunc("GET /api/v1/webhooks", webhooksHandler.List) + r.mux.HandleFunc("POST /api/v1/webhooks", webhooksHandler.Create) + r.mux.HandleFunc("GET /api/v1/webhooks/{id}", webhooksHandler.Get) + r.mux.HandleFunc("PUT /api/v1/webhooks/{id}", webhooksHandler.Update) + r.mux.HandleFunc("DELETE /api/v1/webhooks/{id}", webhooksHandler.Delete) + r.mux.HandleFunc("POST /api/v1/webhooks/{id}/test", webhooksHandler.Test) + r.mux.HandleFunc("GET /api/v1/webhooks/{id}/deliveries", webhooksHandler.ListDeliveries) r.mux.HandleFunc("POST /api/v1/webhooks/deliveries/{id}/retry", webhooksHandler.RetryDelivery) // Custom routes: user-defined URL → function mappings. @@ -552,7 +552,7 @@ func (r *Router) buildMiddlewareChain() { // Build chain from inside out: Handler -> Logger -> RequestID -> Auth -> BodySize -> CORS chain := loggerMiddleware(r.db, r.eventHub, r.mux) chain = requestIDMiddleware(chain) - chain = authMiddleware(r.db, &r.keyCache, r.internalToken, chain) + chain = authMiddleware(r.db, &r.keyCache, r.sdkAuth, chain) chain = bodySizeMiddleware(maxBody, chain) chain = corsMiddleware(origins, chain) diff --git a/backend/internal/server/server.go b/backend/internal/server/server.go index 1c76d507..0b410899 100644 --- a/backend/internal/server/server.go +++ b/backend/internal/server/server.go @@ -26,6 +26,7 @@ import ( "github.com/Harsh-2002/Orva/backend/internal/registry" "github.com/Harsh-2002/Orva/backend/internal/sandbox" "github.com/Harsh-2002/Orva/backend/internal/scheduler" + "github.com/Harsh-2002/Orva/backend/internal/sdkauth" "github.com/Harsh-2002/Orva/backend/internal/secrets" "github.com/Harsh-2002/Orva/backend/internal/server/events" "github.com/Harsh-2002/Orva/backend/internal/server/handlers" @@ -109,12 +110,11 @@ func New(cfg *config.Config, db *database.Database) *Server { // After first run the UI authenticates via session cookies from /auth/login. bootstrapAdminKey(db, cfg.Data.Dir) - // Internal token — process-lifetime random secret injected into every - // sandboxed worker as ORVA_INTERNAL_TOKEN. The adapter uses it to - // authenticate against /api/v1/_kv/, /api/v1/_internal/invoke/, and - // /api/v1/_jobs/. Regenerated on each restart so a leaked token from - // stderr can't be replayed past the next deploy. - internalToken := generateInternalToken() + // Process-lifetime signing key for function-scoped SDK credentials. + // Workers receive a token containing their immutable function ID; the + // server derives caller identity from the verified claim. + internalToken := generateSDKSigningKey() + sdkAuth := sdkauth.New([]byte(internalToken)) // API base for the worker SDK. From inside a sandbox with // network_mode=egress, 127.0.0.1 is the sandbox's own loopback — // useless for reaching orvad. The right destination is whatever IP @@ -147,7 +147,7 @@ func New(cfg *config.Config, db *database.Database) *Server { DataDir: cfg.Data.Dir, DefaultSeccomp: cfg.Sandbox.SeccompPolicy, DefaultMaxPids: cfg.Functions.DefaultMaxPids, - InternalToken: internalToken, + SDKToken: sdkAuth.Mint, APIBaseURL: apiBase, Metrics: met, }, @@ -159,6 +159,7 @@ func New(cfg *config.Config, db *database.Database) *Server { Sandbox: limiter, Pool: poolMgr, DB: db, + SDKAuth: sdkAuth, Config: proxy.ProxyConfig{ NsjailBin: cfg.Sandbox.NsjailBin, RootfsDir: cfg.Sandbox.RootfsDir, @@ -273,17 +274,17 @@ func New(cfg *config.Config, db *database.Database) *Server { fw.Start(context.Background()) deps := RouterDeps{ - Registry: reg, - Proxy: px, - Builder: bld, - Metrics: met, - Secrets: sm, - BuildQueue: buildQueue, - PoolMgr: poolMgr, - EventHub: hub, - Firewall: fw, - InternalToken: internalToken, - StartTime: startTime, + Registry: reg, + Proxy: px, + Builder: bld, + Metrics: met, + Secrets: sm, + BuildQueue: buildQueue, + PoolMgr: poolMgr, + EventHub: hub, + Firewall: fw, + SDKAuth: sdkAuth, + StartTime: startTime, } router := NewRouter(cfg, db, deps) @@ -302,6 +303,7 @@ func New(cfg *config.Config, db *database.Database) *Server { // pass first. sched := scheduler.New(db, poolMgr, cfg.Data.Dir, hub) sched.SetMetrics(met) + sched.SetSDKAuth(sdkAuth) sched.SetEgressGuard(fw) // WebhookFanout subscribes to the Hub and writes webhook_deliveries @@ -564,15 +566,12 @@ func defaultRouteGatewayIPv4() string { return "" } -// generateInternalToken returns a fresh 32-byte random hex string used as -// the per-process token injected into every worker as ORVA_INTERNAL_TOKEN. -// Regenerated on each server start so a leaked token from a single boot -// can't be replayed forever. -func generateInternalToken() string { +// generateSDKSigningKey returns a fresh 32-byte signing key. It is regenerated +// on every start, which makes credentials from a previous process stale. +func generateSDKSigningKey() string { var b [32]byte if _, err := rand.Read(b[:]); err != nil { - // Fall back to a time-based string. Should never happen on Linux. - return fmt.Sprintf("orva-internal-%d", time.Now().UnixNano()) + panic("generate SDK signing key: " + err.Error()) } return hex.EncodeToString(b[:]) } diff --git a/backend/internal/server/ui_dist/assets/AI-DwO56pB2.js b/backend/internal/server/ui_dist/assets/AI-De72Ye8u.js similarity index 99% rename from backend/internal/server/ui_dist/assets/AI-DwO56pB2.js rename to backend/internal/server/ui_dist/assets/AI-De72Ye8u.js index 81a115c0..e32d6979 100644 --- a/backend/internal/server/ui_dist/assets/AI-DwO56pB2.js +++ b/backend/internal/server/ui_dist/assets/AI-De72Ye8u.js @@ -1,4 +1,4 @@ -import{r as e}from"./axios-DVDuIpRy.js";import{C as t,D as n,E as r,F as i,G as a,I as o,M as s,P as c,S as l,T as u,Z as d,c as f,d as p,gt as m,h,k as g,l as _,m as v,r as y,s as b,u as x,vt as S,x as C}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as w}from"./check-DQm9OMOd.js";import{t as T}from"./copy-Bt6Rv0BQ.js";import{i as E,n as D,r as O,t as k}from"./ModelMenu-CCBXKr4H.js";import{t as ee}from"./pencil-BuAbR0ud.js";import{t as te}from"./rotate-ccw-DBtrGGiP.js";import{t as ne}from"./sparkles-BmmGPRlQ.js";import{t as re}from"./trash-2-Bj4AFUfw.js";import{t as ie}from"./zap-CGOT0fxk.js";import{Bt as ae,Dt as oe,Lt as se,Mt as ce,St as le,_t as A,bt as ue,gt as de,jt as j,mt as fe,ut as pe}from"./index-C11HmDXN.js";import{t as me}from"./Drawer-ZggviMzu.js";import{a as M,i as he,n as ge,r as _e,t as ve}from"./github-dark-D7LxIVih.js";import{t as ye}from"./clipboard-D_9N0yai.js";var be=j(`arrow-down`,[[`path`,{d:`M12 5v14`,key:`s699le`}],[`path`,{d:`m19 12-7 7-7-7`,key:`1idqje`}]]),xe=j(`arrow-up`,[[`path`,{d:`m5 12 7-7 7 7`,key:`hav0vg`}],[`path`,{d:`M12 19V5`,key:`x0mq9r`}]]),Se=j(`ban`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M4.929 4.929 19.07 19.071`,key:`196cmz`}]]),Ce=j(`brain`,[[`path`,{d:`M12 18V5`,key:`adv99a`}],[`path`,{d:`M15 13a4.17 4.17 0 0 1-3-4 4.17 4.17 0 0 1-3 4`,key:`1e3is1`}],[`path`,{d:`M17.598 6.5A3 3 0 1 0 12 5a3 3 0 1 0-5.598 1.5`,key:`1gqd8o`}],[`path`,{d:`M17.997 5.125a4 4 0 0 1 2.526 5.77`,key:`iwvgf7`}],[`path`,{d:`M18 18a4 4 0 0 0 2-7.464`,key:`efp6ie`}],[`path`,{d:`M19.967 17.483A4 4 0 1 1 12 18a4 4 0 1 1-7.967-.517`,key:`1gq6am`}],[`path`,{d:`M6 18a4 4 0 0 1-2-7.464`,key:`k1g0md`}],[`path`,{d:`M6.003 5.125a4 4 0 0 0-2.526 5.77`,key:`q97ue3`}]]),we=j(`chevrons-down-up`,[[`path`,{d:`m7 20 5-5 5 5`,key:`13a0gw`}],[`path`,{d:`m7 4 5 5 5-5`,key:`1kwcof`}]]),Te=j(`chevrons-up-down`,[[`path`,{d:`m7 15 5 5 5-5`,key:`1hf1tw`}],[`path`,{d:`m7 9 5-5 5 5`,key:`sgt6xg`}]]),Ee=j(`loader-circle`,[[`path`,{d:`M21 12a9 9 0 1 1-6.219-8.56`,key:`13zald`}]]),De=j(`message-square`,[[`path`,{d:`M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z`,key:`18887p`}]]),Oe=j(`panel-left`,[[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,key:`afitv7`}],[`path`,{d:`M9 3v18`,key:`fh3hqa`}]]),ke=j(`square`,[[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,key:`afitv7`}]]),Ae=j(`wrench`,[[`path`,{d:`M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z`,key:`1ngwbx`}]]),je={class:`flex h-16 shrink-0 items-center justify-between gap-3 border-b border-border px-4`},Me={class:`flex min-w-0 items-center gap-2`},Ne={class:`truncate text-sm font-semibold tracking-tight text-white`},Pe={class:`flex items-center gap-0.5`},Fe={__name:`ChatHeader`,props:{title:{type:String,default:`Assistant`},canExport:{type:Boolean,default:!1}},emits:[`toggle-rail`,`export`],setup(e){return(t,r)=>(n(),p(`header`,je,[f(`div`,Me,[f(`button`,{class:`touch-expand-iconbtn -ml-1 rounded-md p-2 text-foreground-muted transition-colors hover:bg-surface-hover hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background md:hidden`,"aria-label":`Conversations`,onClick:r[0]||=e=>t.$emit(`toggle-rail`)},[h(d(Oe),{class:`h-4 w-4`})]),h(d(De),{class:`hidden h-4 w-4 shrink-0 text-foreground-muted md:block`}),f(`h1`,Ne,S(e.title),1)]),f(`div`,Pe,[e.canExport?(n(),p(`button`,{key:0,class:`touch-expand-iconbtn -mr-1 rounded-md p-2 text-foreground-muted transition-colors hover:bg-surface-hover hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background`,"aria-label":`Export conversation as Markdown`,title:`Export conversation`,onClick:r[1]||=e=>t.$emit(`export`)},[h(d(E),{class:`h-4 w-4`})])):x(``,!0)])]))}},Ie={class:`flex h-full flex-col`},Le={key:0,class:`flex h-16 shrink-0 items-center justify-between px-4 border-b border-border`},Re={class:`flex-1 overflow-y-auto scrollable p-2 space-y-0.5`},ze=[`aria-current`,`onClick`],Be={class:`flex-1 truncate`},Ve=[`onClick`],He=[`onClick`],Ue={key:1,class:`px-2.5 py-2 text-xs text-foreground-muted`},We={key:1,class:`shrink-0 border-t border-border p-2`},Ge={key:0,class:`px-2.5 pb-1.5 text-xs leading-snug text-danger-fg`,role:`alert`},Ke=[`disabled`],qe={__name:`ConversationRail`,props:{embedded:{type:Boolean,default:!1}},emits:[`select`],setup(e,{emit:t}){let r=O(),o=de(),s=a(!1),c=a(``);async function l(e){let t=await o.prompt({title:`Rename conversation`,defaultValue:e.title||``,placeholder:`Conversation name`,confirmLabel:`Rename`});t!=null&&t.trim()&&r.renameConversation(e.id,t.trim())}async function u(e){await o.ask({title:`Delete conversation?`,message:`This permanently deletes the conversation and all its messages.`,danger:!0,confirmLabel:`Delete`})&&r.deleteConversation(e)}async function b(){let e=r.conversations.length;if(!e||s.value)return;let t=e===1?`chat`:`chats`;if(await o.ask({title:`Clear all chats?`,message:`This permanently deletes all ${e} ${t} and their messages. This cannot be undone.`,danger:!0,confirmLabel:`Clear all`})){s.value=!0,c.value=``;try{await r.clearConversations(),C(`select`)}catch(e){c.value=e?.response?.status===409?`A chat is still responding. Stop it and try again.`:`Chats could not be cleared. Try again.`}finally{s.value=!1}}}let C=t;function w(){r.newConversation(),C(`select`)}function T(e){r.openConversation(e),C(`select`)}return(t,a)=>(n(),p(`div`,Ie,[e.embedded?x(``,!0):(n(),p(`div`,Le,[a[1]||=f(`span`,{class:`text-sm font-semibold tracking-tight text-white`},`Conversations`,-1),h(A,{size:`xs`,variant:`secondary`,onClick:w},{default:i(()=>[h(d(le),{class:`h-3.5 w-3.5`}),a[0]||=v(` New `,-1)]),_:1})])),f(`div`,Re,[e.embedded?(n(),p(`button`,{key:0,class:`touch-expand-sm mb-1 flex w-full items-center gap-2 rounded-md border border-border px-2.5 py-2 text-left text-sm text-foreground transition-colors hover:bg-surface-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary`,onClick:w},[h(d(le),{class:`h-3.5 w-3.5 shrink-0`}),a[2]||=v(` New conversation `,-1)])):x(``,!0),(n(!0),p(y,null,g(d(r).conversations,e=>(n(),p(`div`,{key:e.id,class:m([`group flex w-full items-center gap-0.5 rounded-md pr-1 transition-colors`,e.id===d(r).activeId?`bg-primary/15`:`hover:bg-surface-hover`])},[f(`button`,{class:m([`touch-expand-sm flex min-w-0 flex-1 items-center gap-2 rounded-md px-2.5 py-2 text-left text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary`,e.id===d(r).activeId?`text-white`:`text-foreground-muted group-hover:text-white`]),"aria-current":e.id===d(r).activeId?`page`:void 0,onClick:t=>T(e.id)},[h(d(De),{class:`h-3.5 w-3.5 shrink-0 opacity-70`}),f(`span`,Be,S(e.title||`New conversation`),1)],10,ze),f(`button`,{type:`button`,class:`touch-expand-iconbtn shrink-0 rounded-md p-2 text-foreground-muted opacity-0 transition-opacity hover:bg-surface-hover hover:text-white focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary group-hover:opacity-100 max-md:opacity-100`,title:`Rename conversation`,"aria-label":`Rename conversation`,onClick:ae(t=>l(e),[`stop`])},[h(d(ee),{class:`h-3.5 w-3.5`})],8,Ve),f(`button`,{type:`button`,class:`touch-expand-iconbtn shrink-0 rounded-md p-2 text-foreground-muted opacity-0 transition-opacity hover:bg-surface-hover hover:text-danger-fg focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary group-hover:opacity-100 max-md:opacity-100`,title:`Delete conversation`,"aria-label":`Delete conversation`,onClick:ae(t=>u(e.id),[`stop`])},[h(d(re),{class:`h-3.5 w-3.5`})],8,He)],2))),128)),d(r).conversations.length?x(``,!0):(n(),p(`p`,Ue,` No conversations yet. `))]),d(r).conversations.length?(n(),p(`div`,We,[c.value?(n(),p(`p`,Ge,S(c.value),1)):x(``,!0),f(`button`,{type:`button`,class:`touch-expand-sm flex h-8 w-full items-center gap-2 rounded-md px-2.5 text-left text-xs text-foreground-muted transition-colors hover:bg-surface-hover hover:text-danger-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary disabled:cursor-not-allowed disabled:opacity-50`,disabled:s.value,onClick:b},[s.value?(n(),_(d(Ee),{key:0,class:`h-3.5 w-3.5 shrink-0 animate-spin motion-reduce:animate-none`})):(n(),_(d(re),{key:1,class:`h-3.5 w-3.5 shrink-0`})),v(` `+S(s.value?`Clearing…`:`Clear all chats`),1)],8,Ke)])):x(``,!0)]))}},Je={class:`text-center`},Ye={class:`mx-auto mt-5 flex max-w-xl flex-wrap justify-center gap-2`},Xe=[`onClick`],Ze={class:`line-clamp-2`},Qe={__name:`EmptyState`,emits:[`pick`],setup(e){let t=[`How many functions do I have?`,`Which function ran most recently?`,`Any errors in the last 24 hours?`,`Show my most recent executions.`,`List my deployed functions.`,`Summarize today’s invocation errors.`,`Show failed deployments and why.`,`What’s my system health right now?`,`Show storage usage for my instance.`,`List my cron schedules.`,`Are any background jobs failing?`,`Check for failed webhook deliveries.`,`Which runtimes are available?`,`Show my slowest functions by duration.`,`Which functions have egress enabled?`,`List my secrets by name only.`,`Write a Python function that returns the current UTC time.`,`Write a Node function that echoes the request body.`,`Create an hourly cron schedule for a function.`,`Walk me through deploying a new function.`];function r(e,t){let n=[...e];for(let e=n.length-1;e>0;e--){let t=Math.floor(Math.random()*(e+1));[n[e],n[t]]=[n[t],n[e]]}return n.slice(0,t)}let i=a(r(t,3));return(e,t)=>(n(),p(`div`,Je,[t[0]||=f(`h2`,{class:`text-lg font-semibold tracking-tight text-white`},` What would you like to do? `,-1),t[1]||=f(`p`,{class:`mx-auto mt-1.5 max-w-md text-sm leading-relaxed text-foreground-muted`},` Ask about this instance or operate it with natural language. `,-1),f(`div`,Ye,[(n(!0),p(y,null,g(i.value,(t,r)=>(n(),p(`button`,{key:r,type:`button`,class:`rounded-md px-3 py-2 text-left text-xs leading-snug text-foreground-muted transition-colors hover:bg-surface-hover hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background`,onClick:n=>e.$emit(`pick`,t)},[f(`span`,Ze,S(t),1)],8,Xe))),128))])]))}},$e=`[A-Za-z$_][0-9A-Za-z$_]*`,et=`as.in.of.if.for.while.finally.var.new.function.do.return.void.else.break.catch.instanceof.with.throw.case.default.try.switch.continue.typeof.delete.let.yield.const.class.debugger.async.await.static.import.from.export.extends.using`.split(`.`),tt=[`true`,`false`,`null`,`undefined`,`NaN`,`Infinity`],nt=`Object.Function.Boolean.Symbol.Math.Date.Number.BigInt.String.RegExp.Array.Float32Array.Float64Array.Int8Array.Uint8Array.Uint8ClampedArray.Int16Array.Int32Array.Uint16Array.Uint32Array.BigInt64Array.BigUint64Array.Set.Map.WeakSet.WeakMap.ArrayBuffer.SharedArrayBuffer.Atomics.DataView.JSON.Promise.Generator.GeneratorFunction.AsyncFunction.Reflect.Proxy.Intl.WebAssembly`.split(`.`),rt=[`Error`,`EvalError`,`InternalError`,`RangeError`,`ReferenceError`,`SyntaxError`,`TypeError`,`URIError`],it=[`setInterval`,`setTimeout`,`clearInterval`,`clearTimeout`,`require`,`exports`,`eval`,`isFinite`,`isNaN`,`parseFloat`,`parseInt`,`decodeURI`,`decodeURIComponent`,`encodeURI`,`encodeURIComponent`,`escape`,`unescape`],at=[`arguments`,`this`,`super`,`console`,`window`,`document`,`localStorage`,`sessionStorage`,`module`,`global`],ot=[].concat(it,nt,rt);function st(e){let t=e.regex,n=(e,{after:t})=>{let n=``,end:``},a=/<[A-Za-z0-9\\._:-]+\s*\/>/,o={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,t)=>{let r=e[0].length+e.index,i=e.input[r];if(i===`<`||i===`,`){t.ignoreMatch();return}i===`>`&&(n(e,{after:r})||t.ignoreMatch());let a,o=e.input.substring(r);if(a=o.match(/^\s*=/)){t.ignoreMatch();return}if((a=o.match(/^\s+extends\s+/))&&a.index===0){t.ignoreMatch();return}}},s={$pattern:$e,keyword:et,literal:tt,built_in:ot,"variable.language":at},c=`[0-9](_?[0-9])*`,l=`\\.(${c})`,u=`0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*`,d={className:`number`,variants:[{begin:`(\\b(${u})((${l})|\\.)?|(${l}))[eE][+-]?(${c})\\b`},{begin:`\\b(${u})\\b((${l})\\b|\\.)?|(${l})\\b`},{begin:`\\b(0|[1-9](_?[0-9])*)n\\b`},{begin:`\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\b`},{begin:`\\b0[bB][0-1](_?[0-1])*n?\\b`},{begin:`\\b0[oO][0-7](_?[0-7])*n?\\b`},{begin:`\\b0[0-7]+n?\\b`}],relevance:0},f={className:`subst`,begin:`\\$\\{`,end:`\\}`,keywords:s,contains:[]},p={begin:".?html`",end:``,starts:{end:"`",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,f],subLanguage:`xml`}},m={begin:".?css`",end:``,starts:{end:"`",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,f],subLanguage:`css`}},h={begin:".?gql`",end:``,starts:{end:"`",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,f],subLanguage:`graphql`}},g={className:`string`,begin:"`",end:"`",contains:[e.BACKSLASH_ESCAPE,f]},_={className:`comment`,variants:[e.COMMENT(/\/\*\*(?!\/)/,`\\*/`,{relevance:0,contains:[{begin:`(?=@[A-Za-z]+)`,relevance:0,contains:[{className:`doctag`,begin:`@[A-Za-z]+`},{className:`type`,begin:`\\{`,end:`\\}`,excludeEnd:!0,excludeBegin:!0,relevance:0},{className:`variable`,begin:`[A-Za-z$_][0-9A-Za-z$_]*(?=\\s*(-)|$)`,endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}]}),e.C_BLOCK_COMMENT_MODE,e.C_LINE_COMMENT_MODE]},v=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,p,m,h,g,{match:/\$\d+/},d];f.contains=v.concat({begin:/\{/,end:/\}/,keywords:s,contains:[`self`].concat(v)});let y=[].concat(_,f.contains),b=y.concat([{begin:/(\s*)\(/,end:/\)/,keywords:s,contains:[`self`].concat(y)}]),x={className:`params`,begin:/(\s*)\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:s,contains:b},S={variants:[{match:[/class/,/\s+/,r,/\s+/,/extends/,/\s+/,t.concat(r,`(`,t.concat(/\./,r),`)*`)],scope:{1:`keyword`,3:`title.class`,5:`keyword`,7:`title.class.inherited`}},{match:[/class/,/\s+/,r],scope:{1:`keyword`,3:`title.class`}}]},C={relevance:0,match:t.either(/\bJSON/,/\b[A-Z][a-z]+([A-Z][a-z]*|\d)*/,/\b[A-Z]{2,}([A-Z][a-z]+|\d)+([A-Z][a-z]*)*/,/\b[A-Z]{2,}[a-z]+([A-Z][a-z]+|\d)*([A-Z][a-z]*)*/),className:`title.class`,keywords:{_:[...nt,...rt]}},w={label:`use_strict`,className:`meta`,relevance:10,begin:/^\s*['"]use (strict|asm)['"]/},T={variants:[{match:[/function/,/\s+/,r,/(?=\s*\()/]},{match:[/function/,/\s*(?=\()/]}],className:{1:`keyword`,3:`title.function`},label:`func.def`,contains:[x],illegal:/%/},E={relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/,className:`variable.constant`};function D(e){return t.concat(`(?!`,e.join(`|`),`)`)}let O={match:t.concat(/\b/,D([...it,`super`,`import`].map(e=>`${e}\\s*\\(`)),r,t.lookahead(/\s*\(/)),className:`title.function`,relevance:0},k={begin:t.concat(/\./,t.lookahead(t.concat(r,/(?![0-9A-Za-z$_(])/))),end:r,excludeBegin:!0,keywords:`prototype`,className:`property`,relevance:0},ee={match:[/get|set/,/\s+/,r,/(?=\()/],className:{1:`keyword`,3:`title.function`},contains:[{begin:/\(\)/},x]},te=`(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|`+e.UNDERSCORE_IDENT_RE+`)\\s*=>`,ne={match:[/const|var|let/,/\s+/,r,/\s*/,/=\s*/,/(async\s*)?/,t.lookahead(te)],keywords:`async`,className:{1:`keyword`,3:`title.function`},contains:[x]};return{name:`JavaScript`,aliases:[`js`,`jsx`,`mjs`,`cjs`],keywords:s,exports:{PARAMS_CONTAINS:b,CLASS_REFERENCE:C},illegal:/#(?![$_A-z])/,contains:[e.SHEBANG({label:`shebang`,binary:`node`,relevance:5}),w,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,p,m,h,g,_,{match:/\$\d+/},d,C,{scope:`attr`,match:r+t.lookahead(`:`),relevance:0},ne,{begin:`(`+e.RE_STARTERS_RE+`|\\b(case|return|throw)\\b)\\s*`,keywords:`return throw case`,relevance:0,contains:[_,e.REGEXP_MODE,{className:`function`,begin:te,returnBegin:!0,end:`\\s*=>`,contains:[{className:`params`,variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/(\s*)\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:s,contains:b}]}]},{begin:/,/,relevance:0},{match:/\s+/,relevance:0},{variants:[{begin:i.begin,end:i.end},{match:a},{begin:o.begin,"on:begin":o.isTrulyOpeningTag,end:o.end}],subLanguage:`xml`,contains:[{begin:o.begin,end:o.end,skip:!0,contains:[`self`]}]}]},T,{beginKeywords:`while if switch catch for`},{begin:`\\b(?!function)`+e.UNDERSCORE_IDENT_RE+`\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{`,returnBegin:!0,label:`func.def`,contains:[x,e.inherit(e.TITLE_MODE,{begin:r,className:`title.function`})]},{match:/\.\.\./,relevance:0},k,{match:`\\$[A-Za-z$_][0-9A-Za-z$_]*`,relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:`title.function`},contains:[x]},O,E,S,ee,{match:/\$[(.]/}]}}function ct(e){let t=e.regex,n=st(e),r=$e,i=[`any`,`void`,`number`,`boolean`,`string`,`object`,`never`,`symbol`,`bigint`,`unknown`],a={begin:[/namespace/,/\s+/,e.IDENT_RE],beginScope:{1:`keyword`,3:`title.class`}},o={beginKeywords:`interface`,end:/\{/,excludeEnd:!0,keywords:{keyword:`interface extends`,built_in:i},contains:[n.exports.CLASS_REFERENCE]},s={className:`meta`,relevance:10,begin:/^\s*['"]use strict['"]/},c={$pattern:$e,keyword:et.concat([`type`,`interface`,`public`,`private`,`protected`,`implements`,`declare`,`abstract`,`readonly`,`enum`,`override`,`satisfies`]),literal:tt,built_in:ot.concat(i),"variable.language":at},l={className:`meta`,begin:`@[A-Za-z$_][0-9A-Za-z$_]*`},u=(e,t,n)=>{let r=e.contains.findIndex(e=>e.label===t);if(r===-1)throw Error(`can not find mode to replace`);e.contains.splice(r,1,n)};Object.assign(n.keywords,c),n.exports.PARAMS_CONTAINS.push(l);let d=n.contains.find(e=>e.scope===`attr`),f=Object.assign({},d,{match:t.concat(r,t.lookahead(/\s*\?:/))});n.exports.PARAMS_CONTAINS.push([n.exports.CLASS_REFERENCE,d,f]),n.contains=n.contains.concat([l,a,o,f]),u(n,`shebang`,e.SHEBANG()),u(n,`use_strict`,s);let p=n.contains.find(e=>e.label===`func.def`);return p.relevance=0,Object.assign(n,{name:`TypeScript`,aliases:[`ts`,`tsx`,`mts`,`cts`]}),n}function lt(e){let t=e.regex,n=t.concat(/[\p{L}_]/u,t.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),r=/[\p{L}0-9._:-]+/u,i={className:`symbol`,begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},a={begin:/\s/,contains:[{className:`keyword`,begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]},o=e.inherit(a,{begin:/\(/,end:/\)/}),s=e.inherit(e.APOS_STRING_MODE,{className:`string`}),c=e.inherit(e.QUOTE_STRING_MODE,{className:`string`}),l={endsWithParent:!0,illegal:/`]+/}]}]}]};return{name:`HTML, XML`,aliases:[`html`,`xhtml`,`rss`,`atom`,`xjb`,`xsd`,`xsl`,`plist`,`wsf`,`svg`],case_insensitive:!0,unicodeRegex:!0,contains:[{className:`meta`,begin://,relevance:10,contains:[a,c,s,o,{begin:/\[/,end:/\]/,contains:[{className:`meta`,begin://,contains:[a,o,c,s]}]}]},e.COMMENT(//,{relevance:10}),{begin://,relevance:10},i,{className:`meta`,end:/\?>/,variants:[{begin:/<\?xml/,relevance:10,contains:[c]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:`tag`,begin:/)/,end:/>/,keywords:{name:`style`},contains:[l],starts:{end:/<\/style>/,returnEnd:!0,subLanguage:[`css`,`xml`]}},{className:`tag`,begin:/)/,end:/>/,keywords:{name:`script`},contains:[l],starts:{end:/<\/script>/,returnEnd:!0,subLanguage:[`javascript`,`handlebars`,`xml`]}},{className:`tag`,begin:/<>|<\/>/},{className:`tag`,begin:t.concat(//,/>/,/\s/)))),end:/\/?>/,contains:[{className:`name`,begin:n,relevance:0,starts:l}]},{className:`tag`,begin:t.concat(/<\//,t.lookahead(t.concat(n,/>/))),contains:[{className:`name`,begin:n,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}}var ut=e=>({IMPORTANT:{scope:`meta`,begin:`!important`},BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{scope:`number`,begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/},FUNCTION_DISPATCH:{className:`built_in`,begin:/[\w-]+(?=\()/},ATTRIBUTE_SELECTOR_MODE:{scope:`selector-attr`,begin:/\[/,end:/\]/,illegal:`$`,contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{scope:`number`,begin:e.NUMBER_RE+`(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?`,relevance:0},CSS_VARIABLE:{className:`attr`,begin:/--[A-Za-z_][A-Za-z0-9_-]*/}}),dt=`a.abbr.address.article.aside.audio.b.blockquote.body.button.canvas.caption.cite.code.dd.del.details.dfn.div.dl.dt.em.fieldset.figcaption.figure.footer.form.h1.h2.h3.h4.h5.h6.header.hgroup.html.i.iframe.img.input.ins.kbd.label.legend.li.main.mark.menu.nav.object.ol.optgroup.option.p.picture.q.quote.samp.section.select.source.span.strong.summary.sup.table.tbody.td.textarea.tfoot.th.thead.time.tr.ul.var.video`.split(`.`),ft=`defs.g.marker.mask.pattern.svg.switch.symbol.feBlend.feColorMatrix.feComponentTransfer.feComposite.feConvolveMatrix.feDiffuseLighting.feDisplacementMap.feFlood.feGaussianBlur.feImage.feMerge.feMorphology.feOffset.feSpecularLighting.feTile.feTurbulence.linearGradient.radialGradient.stop.circle.ellipse.image.line.path.polygon.polyline.rect.text.use.textPath.tspan.foreignObject.clipPath`.split(`.`),pt=[...dt,...ft],mt=`any-hover.any-pointer.aspect-ratio.color.color-gamut.color-index.device-aspect-ratio.device-height.device-width.display-mode.forced-colors.grid.height.hover.inverted-colors.monochrome.orientation.overflow-block.overflow-inline.pointer.prefers-color-scheme.prefers-contrast.prefers-reduced-motion.prefers-reduced-transparency.resolution.scan.scripting.update.width.min-width.max-width.min-height.max-height`.split(`.`).sort().reverse(),ht=`active.any-link.blank.checked.current.default.defined.dir.disabled.drop.empty.enabled.first.first-child.first-of-type.fullscreen.future.focus.focus-visible.focus-within.has.host.host-context.hover.indeterminate.in-range.invalid.is.lang.last-child.last-of-type.left.link.local-link.not.nth-child.nth-col.nth-last-child.nth-last-col.nth-last-of-type.nth-of-type.only-child.only-of-type.optional.out-of-range.past.placeholder-shown.read-only.read-write.required.right.root.scope.target.target-within.user-invalid.valid.visited.where`.split(`.`).sort().reverse(),gt=[`after`,`backdrop`,`before`,`cue`,`cue-region`,`first-letter`,`first-line`,`grammar-error`,`marker`,`part`,`placeholder`,`selection`,`slotted`,`spelling-error`].sort().reverse(),_t=`accent-color.align-content.align-items.align-self.alignment-baseline.all.anchor-name.animation.animation-composition.animation-delay.animation-direction.animation-duration.animation-fill-mode.animation-iteration-count.animation-name.animation-play-state.animation-range.animation-range-end.animation-range-start.animation-timeline.animation-timing-function.appearance.aspect-ratio.backdrop-filter.backface-visibility.background.background-attachment.background-blend-mode.background-clip.background-color.background-image.background-origin.background-position.background-position-x.background-position-y.background-repeat.background-size.baseline-shift.block-size.border.border-block.border-block-color.border-block-end.border-block-end-color.border-block-end-style.border-block-end-width.border-block-start.border-block-start-color.border-block-start-style.border-block-start-width.border-block-style.border-block-width.border-bottom.border-bottom-color.border-bottom-left-radius.border-bottom-right-radius.border-bottom-style.border-bottom-width.border-collapse.border-color.border-end-end-radius.border-end-start-radius.border-image.border-image-outset.border-image-repeat.border-image-slice.border-image-source.border-image-width.border-inline.border-inline-color.border-inline-end.border-inline-end-color.border-inline-end-style.border-inline-end-width.border-inline-start.border-inline-start-color.border-inline-start-style.border-inline-start-width.border-inline-style.border-inline-width.border-left.border-left-color.border-left-style.border-left-width.border-radius.border-right.border-right-color.border-right-style.border-right-width.border-spacing.border-start-end-radius.border-start-start-radius.border-style.border-top.border-top-color.border-top-left-radius.border-top-right-radius.border-top-style.border-top-width.border-width.bottom.box-align.box-decoration-break.box-direction.box-flex.box-flex-group.box-lines.box-ordinal-group.box-orient.box-pack.box-shadow.box-sizing.break-after.break-before.break-inside.caption-side.caret-color.clear.clip.clip-path.clip-rule.color.color-interpolation.color-interpolation-filters.color-profile.color-rendering.color-scheme.column-count.column-fill.column-gap.column-rule.column-rule-color.column-rule-style.column-rule-width.column-span.column-width.columns.contain.contain-intrinsic-block-size.contain-intrinsic-height.contain-intrinsic-inline-size.contain-intrinsic-size.contain-intrinsic-width.container.container-name.container-type.content.content-visibility.counter-increment.counter-reset.counter-set.cue.cue-after.cue-before.cursor.cx.cy.direction.display.dominant-baseline.empty-cells.enable-background.field-sizing.fill.fill-opacity.fill-rule.filter.flex.flex-basis.flex-direction.flex-flow.flex-grow.flex-shrink.flex-wrap.float.flood-color.flood-opacity.flow.font.font-display.font-family.font-feature-settings.font-kerning.font-language-override.font-optical-sizing.font-palette.font-size.font-size-adjust.font-smooth.font-smoothing.font-stretch.font-style.font-synthesis.font-synthesis-position.font-synthesis-small-caps.font-synthesis-style.font-synthesis-weight.font-variant.font-variant-alternates.font-variant-caps.font-variant-east-asian.font-variant-emoji.font-variant-ligatures.font-variant-numeric.font-variant-position.font-variation-settings.font-weight.forced-color-adjust.gap.glyph-orientation-horizontal.glyph-orientation-vertical.grid.grid-area.grid-auto-columns.grid-auto-flow.grid-auto-rows.grid-column.grid-column-end.grid-column-start.grid-gap.grid-row.grid-row-end.grid-row-start.grid-template.grid-template-areas.grid-template-columns.grid-template-rows.hanging-punctuation.height.hyphenate-character.hyphenate-limit-chars.hyphens.icon.image-orientation.image-rendering.image-resolution.ime-mode.initial-letter.initial-letter-align.inline-size.inset.inset-area.inset-block.inset-block-end.inset-block-start.inset-inline.inset-inline-end.inset-inline-start.isolation.justify-content.justify-items.justify-self.kerning.left.letter-spacing.lighting-color.line-break.line-height.line-height-step.list-style.list-style-image.list-style-position.list-style-type.margin.margin-block.margin-block-end.margin-block-start.margin-bottom.margin-inline.margin-inline-end.margin-inline-start.margin-left.margin-right.margin-top.margin-trim.marker.marker-end.marker-mid.marker-start.marks.mask.mask-border.mask-border-mode.mask-border-outset.mask-border-repeat.mask-border-slice.mask-border-source.mask-border-width.mask-clip.mask-composite.mask-image.mask-mode.mask-origin.mask-position.mask-repeat.mask-size.mask-type.masonry-auto-flow.math-depth.math-shift.math-style.max-block-size.max-height.max-inline-size.max-width.min-block-size.min-height.min-inline-size.min-width.mix-blend-mode.nav-down.nav-index.nav-left.nav-right.nav-up.none.normal.object-fit.object-position.offset.offset-anchor.offset-distance.offset-path.offset-position.offset-rotate.opacity.order.orphans.outline.outline-color.outline-offset.outline-style.outline-width.overflow.overflow-anchor.overflow-block.overflow-clip-margin.overflow-inline.overflow-wrap.overflow-x.overflow-y.overlay.overscroll-behavior.overscroll-behavior-block.overscroll-behavior-inline.overscroll-behavior-x.overscroll-behavior-y.padding.padding-block.padding-block-end.padding-block-start.padding-bottom.padding-inline.padding-inline-end.padding-inline-start.padding-left.padding-right.padding-top.page.page-break-after.page-break-before.page-break-inside.paint-order.pause.pause-after.pause-before.perspective.perspective-origin.place-content.place-items.place-self.pointer-events.position.position-anchor.position-visibility.print-color-adjust.quotes.r.resize.rest.rest-after.rest-before.right.rotate.row-gap.ruby-align.ruby-position.scale.scroll-behavior.scroll-margin.scroll-margin-block.scroll-margin-block-end.scroll-margin-block-start.scroll-margin-bottom.scroll-margin-inline.scroll-margin-inline-end.scroll-margin-inline-start.scroll-margin-left.scroll-margin-right.scroll-margin-top.scroll-padding.scroll-padding-block.scroll-padding-block-end.scroll-padding-block-start.scroll-padding-bottom.scroll-padding-inline.scroll-padding-inline-end.scroll-padding-inline-start.scroll-padding-left.scroll-padding-right.scroll-padding-top.scroll-snap-align.scroll-snap-stop.scroll-snap-type.scroll-timeline.scroll-timeline-axis.scroll-timeline-name.scrollbar-color.scrollbar-gutter.scrollbar-width.shape-image-threshold.shape-margin.shape-outside.shape-rendering.speak.speak-as.src.stop-color.stop-opacity.stroke.stroke-dasharray.stroke-dashoffset.stroke-linecap.stroke-linejoin.stroke-miterlimit.stroke-opacity.stroke-width.tab-size.table-layout.text-align.text-align-all.text-align-last.text-anchor.text-combine-upright.text-decoration.text-decoration-color.text-decoration-line.text-decoration-skip.text-decoration-skip-ink.text-decoration-style.text-decoration-thickness.text-emphasis.text-emphasis-color.text-emphasis-position.text-emphasis-style.text-indent.text-justify.text-orientation.text-overflow.text-rendering.text-shadow.text-size-adjust.text-transform.text-underline-offset.text-underline-position.text-wrap.text-wrap-mode.text-wrap-style.timeline-scope.top.touch-action.transform.transform-box.transform-origin.transform-style.transition.transition-behavior.transition-delay.transition-duration.transition-property.transition-timing-function.translate.unicode-bidi.user-modify.user-select.vector-effect.vertical-align.view-timeline.view-timeline-axis.view-timeline-inset.view-timeline-name.view-transition-name.visibility.voice-balance.voice-duration.voice-family.voice-pitch.voice-range.voice-rate.voice-stress.voice-volume.white-space.white-space-collapse.widows.width.will-change.word-break.word-spacing.word-wrap.writing-mode.x.y.z-index.zoom`.split(`.`).sort().reverse();function vt(e){let t=e.regex,n=ut(e),r={begin:/-(webkit|moz|ms|o)-(?=[a-z])/},i=/@-?\w[\w]*(-\w+)*/,a=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE];return{name:`CSS`,case_insensitive:!0,illegal:/[=|'\$]/,keywords:{keyframePosition:`from to`},classNameAliases:{keyframePosition:`selector-tag`},contains:[n.BLOCK_COMMENT,r,n.CSS_NUMBER_MODE,{className:`selector-id`,begin:/#[A-Za-z0-9_-]+/,relevance:0},{className:`selector-class`,begin:`\\.[a-zA-Z-][a-zA-Z0-9_-]*`,relevance:0},n.ATTRIBUTE_SELECTOR_MODE,{className:`selector-pseudo`,variants:[{begin:`:(`+ht.join(`|`)+`)`},{begin:`:(:)?(`+gt.join(`|`)+`)`}]},n.CSS_VARIABLE,{className:`attribute`,begin:`\\b(`+_t.join(`|`)+`)\\b`},{begin:/:/,end:/[;}{]/,contains:[n.BLOCK_COMMENT,n.HEXCOLOR,n.IMPORTANT,n.CSS_NUMBER_MODE,...a,{begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:`url data-uri`},contains:[...a,{className:`string`,begin:/[^)]/,endsWithParent:!0,excludeEnd:!0}]},n.FUNCTION_DISPATCH]},{begin:t.lookahead(/@/),end:`[{;]`,relevance:0,illegal:/:/,contains:[{className:`keyword`,begin:i},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:{$pattern:/[a-z-]+/,keyword:`and or not only`,attribute:mt.join(` `)},contains:[{begin:/[a-z-]+(?=:)/,className:`attribute`},...a,n.CSS_NUMBER_MODE]}]},{className:`selector-tag`,begin:`\\b(`+pt.join(`|`)+`)\\b`}]}}function yt(e){let t=`true false yes no null`,n={className:`attr`,variants:[{begin:/[\w*@][\w*@ :()\./-]*:(?=[ \t]|$)/},{begin:/"[\w*@][\w*@ :()\./-]*":(?=[ \t]|$)/},{begin:/'[\w*@][\w*@ :()\./-]*':(?=[ \t]|$)/}]},r={className:`template-variable`,variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]},i={className:`string`,relevance:0,begin:/'/,end:/'/,contains:[{match:/''/,scope:`char.escape`,relevance:0}]},a={className:`string`,relevance:0,variants:[{begin:/"/,end:/"/},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,r]},o=e.inherit(a,{variants:[{begin:/'/,end:/'/,contains:[{begin:/''/,relevance:0}]},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),s={className:`number`,begin:`\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b`},c={end:`,`,endsWithParent:!0,excludeEnd:!0,keywords:t,relevance:0},l={begin:/\{/,end:/\}/,contains:[c],illegal:`\\n`,relevance:0},u={begin:`\\[`,end:`\\]`,contains:[c],illegal:`\\n`,relevance:0},d=[n,{className:`meta`,begin:`^---\\s*$`,relevance:10},{className:`string`,begin:`[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*`},{begin:`<%[%=-]?`,end:`[%-]?%>`,subLanguage:`ruby`,excludeBegin:!0,excludeEnd:!0,relevance:0},{className:`type`,begin:`!\\w+![\\w#;/?:@&=+$,.~*'()[\\]]+`},{className:`type`,begin:`!<[\\w#;/?:@&=+$,.~*'()[\\]]+>`},{className:`type`,begin:`![\\w#;/?:@&=+$,.~*'()[\\]]+`},{className:`type`,begin:`!![\\w#;/?:@&=+$,.~*'()[\\]]+`},{className:`meta`,begin:`&`+e.UNDERSCORE_IDENT_RE+`$`},{className:`meta`,begin:`\\*`+e.UNDERSCORE_IDENT_RE+`$`},{className:`bullet`,begin:`-(?=[ ]|$)`,relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:t,keywords:{literal:t}},s,{className:`number`,begin:e.C_NUMBER_RE+`\\b`,relevance:0},l,u,i,a],f=[...d];return f.pop(),f.push(o),c.contains=f,{name:`YAML`,case_insensitive:!0,aliases:[`yml`],contains:d}}M.registerLanguage(`javascript`,he),M.registerLanguage(`typescript`,ct),M.registerLanguage(`python`,_e),M.registerLanguage(`json`,ge),M.registerLanguage(`bash`,ve),M.registerLanguage(`xml`,lt),M.registerLanguage(`css`,vt),M.registerLanguage(`yaml`,yt);var bt={js:`javascript`,ts:`typescript`,py:`python`,sh:`bash`,shell:`bash`,yml:`yaml`,html:`xml`};function xt(e){return e.replace(/&/g,`&`).replace(//g,`>`)}function St(e,t){let n=bt[(t||``).toLowerCase()]||(t||``).toLowerCase();if(n&&M.getLanguage(n))try{return M.highlight(e,{language:n,ignoreIllegals:!0}).value}catch{}return xt(e)}var Ct=M,wt={};function Tt(e){let t=wt[e];if(t)return t;t=wt[e]=[];for(let e=0;e<128;e++){let n=String.fromCharCode(e);t.push(n)}for(let n=0;n=55296&&e<=57343?`���`:String.fromCharCode(e),r+=6;continue}}if((a&248)==240&&r+91114111?t+=`����`:(e-=65536,t+=String.fromCharCode(55296+(e>>10),56320+(e&1023))),r+=9;continue}}t+=`�`}return t})}N.defaultChars=`;/?:@&=+$,#`,N.componentChars=``;var Et={};function Dt(e){let t=Et[e];if(t)return t;t=Et[e]=[];for(let e=0;e<128;e++){let n=String.fromCharCode(e);/^[0-9a-z]$/i.test(n)?t.push(n):t.push(`%`+(`0`+e.toString(16).toUpperCase()).slice(-2))}for(let n=0;n=55296&&o<=57343){if(o>=55296&&o<=56319&&t+1=56320&&n<=57343){i+=encodeURIComponent(e[t]+e[t+1]),t++;continue}}i+=`%EF%BF%BD`;continue}i+=encodeURIComponent(e[t])}return i}P.defaultChars=`;/?:@&=+$,-_.!~*'()#`,P.componentChars=`-_.!~*'()`;function Ot(e){let t=``;return t+=e.protocol||``,t+=e.slashes?`//`:``,t+=e.auth?e.auth+`@`:``,e.hostname&&e.hostname.indexOf(`:`)!==-1?t+=`[`+e.hostname+`]`:t+=e.hostname||``,t+=e.port?`:`+e.port:``,t+=e.pathname||``,t+=e.search||``,t+=e.hash||``,t}function kt(){this.protocol=null,this.slashes=null,this.auth=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.pathname=null}var At=/^([a-z0-9.+-]+:)/i,jt=/:[0-9]*$/,Mt=/^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,Nt=[`%`,`/`,`?`,`;`,`#`,`'`,`{`,`}`,`|`,`\\`,`^`,"`",`<`,`>`,`"`,"`",` `,`\r`,` +import{r as e}from"./axios-DVDuIpRy.js";import{C as t,D as n,E as r,F as i,G as a,I as o,M as s,P as c,S as l,T as u,Z as d,c as f,d as p,gt as m,h,k as g,l as _,m as v,r as y,s as b,u as x,vt as S,x as C}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as w}from"./check-BNre7JFR.js";import{t as T}from"./copy-BqdwwcxC.js";import{i as E,n as D,r as O,t as k}from"./ModelMenu-BtMjUXcx.js";import{t as ee}from"./pencil-Do2I7soU.js";import{t as te}from"./rotate-ccw-DWwjKCqh.js";import{t as ne}from"./sparkles-DTHEIS5T.js";import{t as re}from"./trash-2-DaeYqnW_.js";import{t as ie}from"./zap-BXGoxm_a.js";import{Bt as ae,Dt as oe,Lt as se,Mt as ce,St as le,_t as A,bt as ue,gt as de,jt as j,mt as fe,ut as pe}from"./index-DTqMKlE1.js";import{t as me}from"./Drawer-B98TBytl.js";import{a as M,i as he,n as ge,r as _e,t as ve}from"./github-dark-D7LxIVih.js";import{t as ye}from"./clipboard-D_9N0yai.js";var be=j(`arrow-down`,[[`path`,{d:`M12 5v14`,key:`s699le`}],[`path`,{d:`m19 12-7 7-7-7`,key:`1idqje`}]]),xe=j(`arrow-up`,[[`path`,{d:`m5 12 7-7 7 7`,key:`hav0vg`}],[`path`,{d:`M12 19V5`,key:`x0mq9r`}]]),Se=j(`ban`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M4.929 4.929 19.07 19.071`,key:`196cmz`}]]),Ce=j(`brain`,[[`path`,{d:`M12 18V5`,key:`adv99a`}],[`path`,{d:`M15 13a4.17 4.17 0 0 1-3-4 4.17 4.17 0 0 1-3 4`,key:`1e3is1`}],[`path`,{d:`M17.598 6.5A3 3 0 1 0 12 5a3 3 0 1 0-5.598 1.5`,key:`1gqd8o`}],[`path`,{d:`M17.997 5.125a4 4 0 0 1 2.526 5.77`,key:`iwvgf7`}],[`path`,{d:`M18 18a4 4 0 0 0 2-7.464`,key:`efp6ie`}],[`path`,{d:`M19.967 17.483A4 4 0 1 1 12 18a4 4 0 1 1-7.967-.517`,key:`1gq6am`}],[`path`,{d:`M6 18a4 4 0 0 1-2-7.464`,key:`k1g0md`}],[`path`,{d:`M6.003 5.125a4 4 0 0 0-2.526 5.77`,key:`q97ue3`}]]),we=j(`chevrons-down-up`,[[`path`,{d:`m7 20 5-5 5 5`,key:`13a0gw`}],[`path`,{d:`m7 4 5 5 5-5`,key:`1kwcof`}]]),Te=j(`chevrons-up-down`,[[`path`,{d:`m7 15 5 5 5-5`,key:`1hf1tw`}],[`path`,{d:`m7 9 5-5 5 5`,key:`sgt6xg`}]]),Ee=j(`loader-circle`,[[`path`,{d:`M21 12a9 9 0 1 1-6.219-8.56`,key:`13zald`}]]),De=j(`message-square`,[[`path`,{d:`M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z`,key:`18887p`}]]),Oe=j(`panel-left`,[[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,key:`afitv7`}],[`path`,{d:`M9 3v18`,key:`fh3hqa`}]]),ke=j(`square`,[[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,key:`afitv7`}]]),Ae=j(`wrench`,[[`path`,{d:`M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z`,key:`1ngwbx`}]]),je={class:`flex h-16 shrink-0 items-center justify-between gap-3 border-b border-border px-4`},Me={class:`flex min-w-0 items-center gap-2`},Ne={class:`truncate text-sm font-semibold tracking-tight text-white`},Pe={class:`flex items-center gap-0.5`},Fe={__name:`ChatHeader`,props:{title:{type:String,default:`Assistant`},canExport:{type:Boolean,default:!1}},emits:[`toggle-rail`,`export`],setup(e){return(t,r)=>(n(),p(`header`,je,[f(`div`,Me,[f(`button`,{class:`touch-expand-iconbtn -ml-1 rounded-md p-2 text-foreground-muted transition-colors hover:bg-surface-hover hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background md:hidden`,"aria-label":`Conversations`,onClick:r[0]||=e=>t.$emit(`toggle-rail`)},[h(d(Oe),{class:`h-4 w-4`})]),h(d(De),{class:`hidden h-4 w-4 shrink-0 text-foreground-muted md:block`}),f(`h1`,Ne,S(e.title),1)]),f(`div`,Pe,[e.canExport?(n(),p(`button`,{key:0,class:`touch-expand-iconbtn -mr-1 rounded-md p-2 text-foreground-muted transition-colors hover:bg-surface-hover hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background`,"aria-label":`Export conversation as Markdown`,title:`Export conversation`,onClick:r[1]||=e=>t.$emit(`export`)},[h(d(E),{class:`h-4 w-4`})])):x(``,!0)])]))}},Ie={class:`flex h-full flex-col`},Le={key:0,class:`flex h-16 shrink-0 items-center justify-between px-4 border-b border-border`},Re={class:`flex-1 overflow-y-auto scrollable p-2 space-y-0.5`},ze=[`aria-current`,`onClick`],Be={class:`flex-1 truncate`},Ve=[`onClick`],He=[`onClick`],Ue={key:1,class:`px-2.5 py-2 text-xs text-foreground-muted`},We={key:1,class:`shrink-0 border-t border-border p-2`},Ge={key:0,class:`px-2.5 pb-1.5 text-xs leading-snug text-danger-fg`,role:`alert`},Ke=[`disabled`],qe={__name:`ConversationRail`,props:{embedded:{type:Boolean,default:!1}},emits:[`select`],setup(e,{emit:t}){let r=O(),o=de(),s=a(!1),c=a(``);async function l(e){let t=await o.prompt({title:`Rename conversation`,defaultValue:e.title||``,placeholder:`Conversation name`,confirmLabel:`Rename`});t!=null&&t.trim()&&r.renameConversation(e.id,t.trim())}async function u(e){await o.ask({title:`Delete conversation?`,message:`This permanently deletes the conversation and all its messages.`,danger:!0,confirmLabel:`Delete`})&&r.deleteConversation(e)}async function b(){let e=r.conversations.length;if(!e||s.value)return;let t=e===1?`chat`:`chats`;if(await o.ask({title:`Clear all chats?`,message:`This permanently deletes all ${e} ${t} and their messages. This cannot be undone.`,danger:!0,confirmLabel:`Clear all`})){s.value=!0,c.value=``;try{await r.clearConversations(),C(`select`)}catch(e){c.value=e?.response?.status===409?`A chat is still responding. Stop it and try again.`:`Chats could not be cleared. Try again.`}finally{s.value=!1}}}let C=t;function w(){r.newConversation(),C(`select`)}function T(e){r.openConversation(e),C(`select`)}return(t,a)=>(n(),p(`div`,Ie,[e.embedded?x(``,!0):(n(),p(`div`,Le,[a[1]||=f(`span`,{class:`text-sm font-semibold tracking-tight text-white`},`Conversations`,-1),h(A,{size:`xs`,variant:`secondary`,onClick:w},{default:i(()=>[h(d(le),{class:`h-3.5 w-3.5`}),a[0]||=v(` New `,-1)]),_:1})])),f(`div`,Re,[e.embedded?(n(),p(`button`,{key:0,class:`touch-expand-sm mb-1 flex w-full items-center gap-2 rounded-md border border-border px-2.5 py-2 text-left text-sm text-foreground transition-colors hover:bg-surface-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary`,onClick:w},[h(d(le),{class:`h-3.5 w-3.5 shrink-0`}),a[2]||=v(` New conversation `,-1)])):x(``,!0),(n(!0),p(y,null,g(d(r).conversations,e=>(n(),p(`div`,{key:e.id,class:m([`group flex w-full items-center gap-0.5 rounded-md pr-1 transition-colors`,e.id===d(r).activeId?`bg-primary/15`:`hover:bg-surface-hover`])},[f(`button`,{class:m([`touch-expand-sm flex min-w-0 flex-1 items-center gap-2 rounded-md px-2.5 py-2 text-left text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary`,e.id===d(r).activeId?`text-white`:`text-foreground-muted group-hover:text-white`]),"aria-current":e.id===d(r).activeId?`page`:void 0,onClick:t=>T(e.id)},[h(d(De),{class:`h-3.5 w-3.5 shrink-0 opacity-70`}),f(`span`,Be,S(e.title||`New conversation`),1)],10,ze),f(`button`,{type:`button`,class:`touch-expand-iconbtn shrink-0 rounded-md p-2 text-foreground-muted opacity-0 transition-opacity hover:bg-surface-hover hover:text-white focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary group-hover:opacity-100 max-md:opacity-100`,title:`Rename conversation`,"aria-label":`Rename conversation`,onClick:ae(t=>l(e),[`stop`])},[h(d(ee),{class:`h-3.5 w-3.5`})],8,Ve),f(`button`,{type:`button`,class:`touch-expand-iconbtn shrink-0 rounded-md p-2 text-foreground-muted opacity-0 transition-opacity hover:bg-surface-hover hover:text-danger-fg focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary group-hover:opacity-100 max-md:opacity-100`,title:`Delete conversation`,"aria-label":`Delete conversation`,onClick:ae(t=>u(e.id),[`stop`])},[h(d(re),{class:`h-3.5 w-3.5`})],8,He)],2))),128)),d(r).conversations.length?x(``,!0):(n(),p(`p`,Ue,` No conversations yet. `))]),d(r).conversations.length?(n(),p(`div`,We,[c.value?(n(),p(`p`,Ge,S(c.value),1)):x(``,!0),f(`button`,{type:`button`,class:`touch-expand-sm flex h-8 w-full items-center gap-2 rounded-md px-2.5 text-left text-xs text-foreground-muted transition-colors hover:bg-surface-hover hover:text-danger-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary disabled:cursor-not-allowed disabled:opacity-50`,disabled:s.value,onClick:b},[s.value?(n(),_(d(Ee),{key:0,class:`h-3.5 w-3.5 shrink-0 animate-spin motion-reduce:animate-none`})):(n(),_(d(re),{key:1,class:`h-3.5 w-3.5 shrink-0`})),v(` `+S(s.value?`Clearing…`:`Clear all chats`),1)],8,Ke)])):x(``,!0)]))}},Je={class:`text-center`},Ye={class:`mx-auto mt-5 flex max-w-xl flex-wrap justify-center gap-2`},Xe=[`onClick`],Ze={class:`line-clamp-2`},Qe={__name:`EmptyState`,emits:[`pick`],setup(e){let t=[`How many functions do I have?`,`Which function ran most recently?`,`Any errors in the last 24 hours?`,`Show my most recent executions.`,`List my deployed functions.`,`Summarize today’s invocation errors.`,`Show failed deployments and why.`,`What’s my system health right now?`,`Show storage usage for my instance.`,`List my cron schedules.`,`Are any background jobs failing?`,`Check for failed webhook deliveries.`,`Which runtimes are available?`,`Show my slowest functions by duration.`,`Which functions have egress enabled?`,`List my secrets by name only.`,`Write a Python function that returns the current UTC time.`,`Write a Node function that echoes the request body.`,`Create an hourly cron schedule for a function.`,`Walk me through deploying a new function.`];function r(e,t){let n=[...e];for(let e=n.length-1;e>0;e--){let t=Math.floor(Math.random()*(e+1));[n[e],n[t]]=[n[t],n[e]]}return n.slice(0,t)}let i=a(r(t,3));return(e,t)=>(n(),p(`div`,Je,[t[0]||=f(`h2`,{class:`text-lg font-semibold tracking-tight text-white`},` What would you like to do? `,-1),t[1]||=f(`p`,{class:`mx-auto mt-1.5 max-w-md text-sm leading-relaxed text-foreground-muted`},` Ask about this instance or operate it with natural language. `,-1),f(`div`,Ye,[(n(!0),p(y,null,g(i.value,(t,r)=>(n(),p(`button`,{key:r,type:`button`,class:`rounded-md px-3 py-2 text-left text-xs leading-snug text-foreground-muted transition-colors hover:bg-surface-hover hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background`,onClick:n=>e.$emit(`pick`,t)},[f(`span`,Ze,S(t),1)],8,Xe))),128))])]))}},$e=`[A-Za-z$_][0-9A-Za-z$_]*`,et=`as.in.of.if.for.while.finally.var.new.function.do.return.void.else.break.catch.instanceof.with.throw.case.default.try.switch.continue.typeof.delete.let.yield.const.class.debugger.async.await.static.import.from.export.extends.using`.split(`.`),tt=[`true`,`false`,`null`,`undefined`,`NaN`,`Infinity`],nt=`Object.Function.Boolean.Symbol.Math.Date.Number.BigInt.String.RegExp.Array.Float32Array.Float64Array.Int8Array.Uint8Array.Uint8ClampedArray.Int16Array.Int32Array.Uint16Array.Uint32Array.BigInt64Array.BigUint64Array.Set.Map.WeakSet.WeakMap.ArrayBuffer.SharedArrayBuffer.Atomics.DataView.JSON.Promise.Generator.GeneratorFunction.AsyncFunction.Reflect.Proxy.Intl.WebAssembly`.split(`.`),rt=[`Error`,`EvalError`,`InternalError`,`RangeError`,`ReferenceError`,`SyntaxError`,`TypeError`,`URIError`],it=[`setInterval`,`setTimeout`,`clearInterval`,`clearTimeout`,`require`,`exports`,`eval`,`isFinite`,`isNaN`,`parseFloat`,`parseInt`,`decodeURI`,`decodeURIComponent`,`encodeURI`,`encodeURIComponent`,`escape`,`unescape`],at=[`arguments`,`this`,`super`,`console`,`window`,`document`,`localStorage`,`sessionStorage`,`module`,`global`],ot=[].concat(it,nt,rt);function st(e){let t=e.regex,n=(e,{after:t})=>{let n=``,end:``},a=/<[A-Za-z0-9\\._:-]+\s*\/>/,o={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,t)=>{let r=e[0].length+e.index,i=e.input[r];if(i===`<`||i===`,`){t.ignoreMatch();return}i===`>`&&(n(e,{after:r})||t.ignoreMatch());let a,o=e.input.substring(r);if(a=o.match(/^\s*=/)){t.ignoreMatch();return}if((a=o.match(/^\s+extends\s+/))&&a.index===0){t.ignoreMatch();return}}},s={$pattern:$e,keyword:et,literal:tt,built_in:ot,"variable.language":at},c=`[0-9](_?[0-9])*`,l=`\\.(${c})`,u=`0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*`,d={className:`number`,variants:[{begin:`(\\b(${u})((${l})|\\.)?|(${l}))[eE][+-]?(${c})\\b`},{begin:`\\b(${u})\\b((${l})\\b|\\.)?|(${l})\\b`},{begin:`\\b(0|[1-9](_?[0-9])*)n\\b`},{begin:`\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\b`},{begin:`\\b0[bB][0-1](_?[0-1])*n?\\b`},{begin:`\\b0[oO][0-7](_?[0-7])*n?\\b`},{begin:`\\b0[0-7]+n?\\b`}],relevance:0},f={className:`subst`,begin:`\\$\\{`,end:`\\}`,keywords:s,contains:[]},p={begin:".?html`",end:``,starts:{end:"`",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,f],subLanguage:`xml`}},m={begin:".?css`",end:``,starts:{end:"`",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,f],subLanguage:`css`}},h={begin:".?gql`",end:``,starts:{end:"`",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,f],subLanguage:`graphql`}},g={className:`string`,begin:"`",end:"`",contains:[e.BACKSLASH_ESCAPE,f]},_={className:`comment`,variants:[e.COMMENT(/\/\*\*(?!\/)/,`\\*/`,{relevance:0,contains:[{begin:`(?=@[A-Za-z]+)`,relevance:0,contains:[{className:`doctag`,begin:`@[A-Za-z]+`},{className:`type`,begin:`\\{`,end:`\\}`,excludeEnd:!0,excludeBegin:!0,relevance:0},{className:`variable`,begin:`[A-Za-z$_][0-9A-Za-z$_]*(?=\\s*(-)|$)`,endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}]}),e.C_BLOCK_COMMENT_MODE,e.C_LINE_COMMENT_MODE]},v=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,p,m,h,g,{match:/\$\d+/},d];f.contains=v.concat({begin:/\{/,end:/\}/,keywords:s,contains:[`self`].concat(v)});let y=[].concat(_,f.contains),b=y.concat([{begin:/(\s*)\(/,end:/\)/,keywords:s,contains:[`self`].concat(y)}]),x={className:`params`,begin:/(\s*)\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:s,contains:b},S={variants:[{match:[/class/,/\s+/,r,/\s+/,/extends/,/\s+/,t.concat(r,`(`,t.concat(/\./,r),`)*`)],scope:{1:`keyword`,3:`title.class`,5:`keyword`,7:`title.class.inherited`}},{match:[/class/,/\s+/,r],scope:{1:`keyword`,3:`title.class`}}]},C={relevance:0,match:t.either(/\bJSON/,/\b[A-Z][a-z]+([A-Z][a-z]*|\d)*/,/\b[A-Z]{2,}([A-Z][a-z]+|\d)+([A-Z][a-z]*)*/,/\b[A-Z]{2,}[a-z]+([A-Z][a-z]+|\d)*([A-Z][a-z]*)*/),className:`title.class`,keywords:{_:[...nt,...rt]}},w={label:`use_strict`,className:`meta`,relevance:10,begin:/^\s*['"]use (strict|asm)['"]/},T={variants:[{match:[/function/,/\s+/,r,/(?=\s*\()/]},{match:[/function/,/\s*(?=\()/]}],className:{1:`keyword`,3:`title.function`},label:`func.def`,contains:[x],illegal:/%/},E={relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/,className:`variable.constant`};function D(e){return t.concat(`(?!`,e.join(`|`),`)`)}let O={match:t.concat(/\b/,D([...it,`super`,`import`].map(e=>`${e}\\s*\\(`)),r,t.lookahead(/\s*\(/)),className:`title.function`,relevance:0},k={begin:t.concat(/\./,t.lookahead(t.concat(r,/(?![0-9A-Za-z$_(])/))),end:r,excludeBegin:!0,keywords:`prototype`,className:`property`,relevance:0},ee={match:[/get|set/,/\s+/,r,/(?=\()/],className:{1:`keyword`,3:`title.function`},contains:[{begin:/\(\)/},x]},te=`(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|`+e.UNDERSCORE_IDENT_RE+`)\\s*=>`,ne={match:[/const|var|let/,/\s+/,r,/\s*/,/=\s*/,/(async\s*)?/,t.lookahead(te)],keywords:`async`,className:{1:`keyword`,3:`title.function`},contains:[x]};return{name:`JavaScript`,aliases:[`js`,`jsx`,`mjs`,`cjs`],keywords:s,exports:{PARAMS_CONTAINS:b,CLASS_REFERENCE:C},illegal:/#(?![$_A-z])/,contains:[e.SHEBANG({label:`shebang`,binary:`node`,relevance:5}),w,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,p,m,h,g,_,{match:/\$\d+/},d,C,{scope:`attr`,match:r+t.lookahead(`:`),relevance:0},ne,{begin:`(`+e.RE_STARTERS_RE+`|\\b(case|return|throw)\\b)\\s*`,keywords:`return throw case`,relevance:0,contains:[_,e.REGEXP_MODE,{className:`function`,begin:te,returnBegin:!0,end:`\\s*=>`,contains:[{className:`params`,variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/(\s*)\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:s,contains:b}]}]},{begin:/,/,relevance:0},{match:/\s+/,relevance:0},{variants:[{begin:i.begin,end:i.end},{match:a},{begin:o.begin,"on:begin":o.isTrulyOpeningTag,end:o.end}],subLanguage:`xml`,contains:[{begin:o.begin,end:o.end,skip:!0,contains:[`self`]}]}]},T,{beginKeywords:`while if switch catch for`},{begin:`\\b(?!function)`+e.UNDERSCORE_IDENT_RE+`\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{`,returnBegin:!0,label:`func.def`,contains:[x,e.inherit(e.TITLE_MODE,{begin:r,className:`title.function`})]},{match:/\.\.\./,relevance:0},k,{match:`\\$[A-Za-z$_][0-9A-Za-z$_]*`,relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:`title.function`},contains:[x]},O,E,S,ee,{match:/\$[(.]/}]}}function ct(e){let t=e.regex,n=st(e),r=$e,i=[`any`,`void`,`number`,`boolean`,`string`,`object`,`never`,`symbol`,`bigint`,`unknown`],a={begin:[/namespace/,/\s+/,e.IDENT_RE],beginScope:{1:`keyword`,3:`title.class`}},o={beginKeywords:`interface`,end:/\{/,excludeEnd:!0,keywords:{keyword:`interface extends`,built_in:i},contains:[n.exports.CLASS_REFERENCE]},s={className:`meta`,relevance:10,begin:/^\s*['"]use strict['"]/},c={$pattern:$e,keyword:et.concat([`type`,`interface`,`public`,`private`,`protected`,`implements`,`declare`,`abstract`,`readonly`,`enum`,`override`,`satisfies`]),literal:tt,built_in:ot.concat(i),"variable.language":at},l={className:`meta`,begin:`@[A-Za-z$_][0-9A-Za-z$_]*`},u=(e,t,n)=>{let r=e.contains.findIndex(e=>e.label===t);if(r===-1)throw Error(`can not find mode to replace`);e.contains.splice(r,1,n)};Object.assign(n.keywords,c),n.exports.PARAMS_CONTAINS.push(l);let d=n.contains.find(e=>e.scope===`attr`),f=Object.assign({},d,{match:t.concat(r,t.lookahead(/\s*\?:/))});n.exports.PARAMS_CONTAINS.push([n.exports.CLASS_REFERENCE,d,f]),n.contains=n.contains.concat([l,a,o,f]),u(n,`shebang`,e.SHEBANG()),u(n,`use_strict`,s);let p=n.contains.find(e=>e.label===`func.def`);return p.relevance=0,Object.assign(n,{name:`TypeScript`,aliases:[`ts`,`tsx`,`mts`,`cts`]}),n}function lt(e){let t=e.regex,n=t.concat(/[\p{L}_]/u,t.optional(/[\p{L}0-9_.-]*:/u),/[\p{L}0-9_.-]*/u),r=/[\p{L}0-9._:-]+/u,i={className:`symbol`,begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},a={begin:/\s/,contains:[{className:`keyword`,begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]},o=e.inherit(a,{begin:/\(/,end:/\)/}),s=e.inherit(e.APOS_STRING_MODE,{className:`string`}),c=e.inherit(e.QUOTE_STRING_MODE,{className:`string`}),l={endsWithParent:!0,illegal:/`]+/}]}]}]};return{name:`HTML, XML`,aliases:[`html`,`xhtml`,`rss`,`atom`,`xjb`,`xsd`,`xsl`,`plist`,`wsf`,`svg`],case_insensitive:!0,unicodeRegex:!0,contains:[{className:`meta`,begin://,relevance:10,contains:[a,c,s,o,{begin:/\[/,end:/\]/,contains:[{className:`meta`,begin://,contains:[a,o,c,s]}]}]},e.COMMENT(//,{relevance:10}),{begin://,relevance:10},i,{className:`meta`,end:/\?>/,variants:[{begin:/<\?xml/,relevance:10,contains:[c]},{begin:/<\?[a-z][a-z0-9]+/}]},{className:`tag`,begin:/)/,end:/>/,keywords:{name:`style`},contains:[l],starts:{end:/<\/style>/,returnEnd:!0,subLanguage:[`css`,`xml`]}},{className:`tag`,begin:/)/,end:/>/,keywords:{name:`script`},contains:[l],starts:{end:/<\/script>/,returnEnd:!0,subLanguage:[`javascript`,`handlebars`,`xml`]}},{className:`tag`,begin:/<>|<\/>/},{className:`tag`,begin:t.concat(//,/>/,/\s/)))),end:/\/?>/,contains:[{className:`name`,begin:n,relevance:0,starts:l}]},{className:`tag`,begin:t.concat(/<\//,t.lookahead(t.concat(n,/>/))),contains:[{className:`name`,begin:n,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}}var ut=e=>({IMPORTANT:{scope:`meta`,begin:`!important`},BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{scope:`number`,begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/},FUNCTION_DISPATCH:{className:`built_in`,begin:/[\w-]+(?=\()/},ATTRIBUTE_SELECTOR_MODE:{scope:`selector-attr`,begin:/\[/,end:/\]/,illegal:`$`,contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{scope:`number`,begin:e.NUMBER_RE+`(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?`,relevance:0},CSS_VARIABLE:{className:`attr`,begin:/--[A-Za-z_][A-Za-z0-9_-]*/}}),dt=`a.abbr.address.article.aside.audio.b.blockquote.body.button.canvas.caption.cite.code.dd.del.details.dfn.div.dl.dt.em.fieldset.figcaption.figure.footer.form.h1.h2.h3.h4.h5.h6.header.hgroup.html.i.iframe.img.input.ins.kbd.label.legend.li.main.mark.menu.nav.object.ol.optgroup.option.p.picture.q.quote.samp.section.select.source.span.strong.summary.sup.table.tbody.td.textarea.tfoot.th.thead.time.tr.ul.var.video`.split(`.`),ft=`defs.g.marker.mask.pattern.svg.switch.symbol.feBlend.feColorMatrix.feComponentTransfer.feComposite.feConvolveMatrix.feDiffuseLighting.feDisplacementMap.feFlood.feGaussianBlur.feImage.feMerge.feMorphology.feOffset.feSpecularLighting.feTile.feTurbulence.linearGradient.radialGradient.stop.circle.ellipse.image.line.path.polygon.polyline.rect.text.use.textPath.tspan.foreignObject.clipPath`.split(`.`),pt=[...dt,...ft],mt=`any-hover.any-pointer.aspect-ratio.color.color-gamut.color-index.device-aspect-ratio.device-height.device-width.display-mode.forced-colors.grid.height.hover.inverted-colors.monochrome.orientation.overflow-block.overflow-inline.pointer.prefers-color-scheme.prefers-contrast.prefers-reduced-motion.prefers-reduced-transparency.resolution.scan.scripting.update.width.min-width.max-width.min-height.max-height`.split(`.`).sort().reverse(),ht=`active.any-link.blank.checked.current.default.defined.dir.disabled.drop.empty.enabled.first.first-child.first-of-type.fullscreen.future.focus.focus-visible.focus-within.has.host.host-context.hover.indeterminate.in-range.invalid.is.lang.last-child.last-of-type.left.link.local-link.not.nth-child.nth-col.nth-last-child.nth-last-col.nth-last-of-type.nth-of-type.only-child.only-of-type.optional.out-of-range.past.placeholder-shown.read-only.read-write.required.right.root.scope.target.target-within.user-invalid.valid.visited.where`.split(`.`).sort().reverse(),gt=[`after`,`backdrop`,`before`,`cue`,`cue-region`,`first-letter`,`first-line`,`grammar-error`,`marker`,`part`,`placeholder`,`selection`,`slotted`,`spelling-error`].sort().reverse(),_t=`accent-color.align-content.align-items.align-self.alignment-baseline.all.anchor-name.animation.animation-composition.animation-delay.animation-direction.animation-duration.animation-fill-mode.animation-iteration-count.animation-name.animation-play-state.animation-range.animation-range-end.animation-range-start.animation-timeline.animation-timing-function.appearance.aspect-ratio.backdrop-filter.backface-visibility.background.background-attachment.background-blend-mode.background-clip.background-color.background-image.background-origin.background-position.background-position-x.background-position-y.background-repeat.background-size.baseline-shift.block-size.border.border-block.border-block-color.border-block-end.border-block-end-color.border-block-end-style.border-block-end-width.border-block-start.border-block-start-color.border-block-start-style.border-block-start-width.border-block-style.border-block-width.border-bottom.border-bottom-color.border-bottom-left-radius.border-bottom-right-radius.border-bottom-style.border-bottom-width.border-collapse.border-color.border-end-end-radius.border-end-start-radius.border-image.border-image-outset.border-image-repeat.border-image-slice.border-image-source.border-image-width.border-inline.border-inline-color.border-inline-end.border-inline-end-color.border-inline-end-style.border-inline-end-width.border-inline-start.border-inline-start-color.border-inline-start-style.border-inline-start-width.border-inline-style.border-inline-width.border-left.border-left-color.border-left-style.border-left-width.border-radius.border-right.border-right-color.border-right-style.border-right-width.border-spacing.border-start-end-radius.border-start-start-radius.border-style.border-top.border-top-color.border-top-left-radius.border-top-right-radius.border-top-style.border-top-width.border-width.bottom.box-align.box-decoration-break.box-direction.box-flex.box-flex-group.box-lines.box-ordinal-group.box-orient.box-pack.box-shadow.box-sizing.break-after.break-before.break-inside.caption-side.caret-color.clear.clip.clip-path.clip-rule.color.color-interpolation.color-interpolation-filters.color-profile.color-rendering.color-scheme.column-count.column-fill.column-gap.column-rule.column-rule-color.column-rule-style.column-rule-width.column-span.column-width.columns.contain.contain-intrinsic-block-size.contain-intrinsic-height.contain-intrinsic-inline-size.contain-intrinsic-size.contain-intrinsic-width.container.container-name.container-type.content.content-visibility.counter-increment.counter-reset.counter-set.cue.cue-after.cue-before.cursor.cx.cy.direction.display.dominant-baseline.empty-cells.enable-background.field-sizing.fill.fill-opacity.fill-rule.filter.flex.flex-basis.flex-direction.flex-flow.flex-grow.flex-shrink.flex-wrap.float.flood-color.flood-opacity.flow.font.font-display.font-family.font-feature-settings.font-kerning.font-language-override.font-optical-sizing.font-palette.font-size.font-size-adjust.font-smooth.font-smoothing.font-stretch.font-style.font-synthesis.font-synthesis-position.font-synthesis-small-caps.font-synthesis-style.font-synthesis-weight.font-variant.font-variant-alternates.font-variant-caps.font-variant-east-asian.font-variant-emoji.font-variant-ligatures.font-variant-numeric.font-variant-position.font-variation-settings.font-weight.forced-color-adjust.gap.glyph-orientation-horizontal.glyph-orientation-vertical.grid.grid-area.grid-auto-columns.grid-auto-flow.grid-auto-rows.grid-column.grid-column-end.grid-column-start.grid-gap.grid-row.grid-row-end.grid-row-start.grid-template.grid-template-areas.grid-template-columns.grid-template-rows.hanging-punctuation.height.hyphenate-character.hyphenate-limit-chars.hyphens.icon.image-orientation.image-rendering.image-resolution.ime-mode.initial-letter.initial-letter-align.inline-size.inset.inset-area.inset-block.inset-block-end.inset-block-start.inset-inline.inset-inline-end.inset-inline-start.isolation.justify-content.justify-items.justify-self.kerning.left.letter-spacing.lighting-color.line-break.line-height.line-height-step.list-style.list-style-image.list-style-position.list-style-type.margin.margin-block.margin-block-end.margin-block-start.margin-bottom.margin-inline.margin-inline-end.margin-inline-start.margin-left.margin-right.margin-top.margin-trim.marker.marker-end.marker-mid.marker-start.marks.mask.mask-border.mask-border-mode.mask-border-outset.mask-border-repeat.mask-border-slice.mask-border-source.mask-border-width.mask-clip.mask-composite.mask-image.mask-mode.mask-origin.mask-position.mask-repeat.mask-size.mask-type.masonry-auto-flow.math-depth.math-shift.math-style.max-block-size.max-height.max-inline-size.max-width.min-block-size.min-height.min-inline-size.min-width.mix-blend-mode.nav-down.nav-index.nav-left.nav-right.nav-up.none.normal.object-fit.object-position.offset.offset-anchor.offset-distance.offset-path.offset-position.offset-rotate.opacity.order.orphans.outline.outline-color.outline-offset.outline-style.outline-width.overflow.overflow-anchor.overflow-block.overflow-clip-margin.overflow-inline.overflow-wrap.overflow-x.overflow-y.overlay.overscroll-behavior.overscroll-behavior-block.overscroll-behavior-inline.overscroll-behavior-x.overscroll-behavior-y.padding.padding-block.padding-block-end.padding-block-start.padding-bottom.padding-inline.padding-inline-end.padding-inline-start.padding-left.padding-right.padding-top.page.page-break-after.page-break-before.page-break-inside.paint-order.pause.pause-after.pause-before.perspective.perspective-origin.place-content.place-items.place-self.pointer-events.position.position-anchor.position-visibility.print-color-adjust.quotes.r.resize.rest.rest-after.rest-before.right.rotate.row-gap.ruby-align.ruby-position.scale.scroll-behavior.scroll-margin.scroll-margin-block.scroll-margin-block-end.scroll-margin-block-start.scroll-margin-bottom.scroll-margin-inline.scroll-margin-inline-end.scroll-margin-inline-start.scroll-margin-left.scroll-margin-right.scroll-margin-top.scroll-padding.scroll-padding-block.scroll-padding-block-end.scroll-padding-block-start.scroll-padding-bottom.scroll-padding-inline.scroll-padding-inline-end.scroll-padding-inline-start.scroll-padding-left.scroll-padding-right.scroll-padding-top.scroll-snap-align.scroll-snap-stop.scroll-snap-type.scroll-timeline.scroll-timeline-axis.scroll-timeline-name.scrollbar-color.scrollbar-gutter.scrollbar-width.shape-image-threshold.shape-margin.shape-outside.shape-rendering.speak.speak-as.src.stop-color.stop-opacity.stroke.stroke-dasharray.stroke-dashoffset.stroke-linecap.stroke-linejoin.stroke-miterlimit.stroke-opacity.stroke-width.tab-size.table-layout.text-align.text-align-all.text-align-last.text-anchor.text-combine-upright.text-decoration.text-decoration-color.text-decoration-line.text-decoration-skip.text-decoration-skip-ink.text-decoration-style.text-decoration-thickness.text-emphasis.text-emphasis-color.text-emphasis-position.text-emphasis-style.text-indent.text-justify.text-orientation.text-overflow.text-rendering.text-shadow.text-size-adjust.text-transform.text-underline-offset.text-underline-position.text-wrap.text-wrap-mode.text-wrap-style.timeline-scope.top.touch-action.transform.transform-box.transform-origin.transform-style.transition.transition-behavior.transition-delay.transition-duration.transition-property.transition-timing-function.translate.unicode-bidi.user-modify.user-select.vector-effect.vertical-align.view-timeline.view-timeline-axis.view-timeline-inset.view-timeline-name.view-transition-name.visibility.voice-balance.voice-duration.voice-family.voice-pitch.voice-range.voice-rate.voice-stress.voice-volume.white-space.white-space-collapse.widows.width.will-change.word-break.word-spacing.word-wrap.writing-mode.x.y.z-index.zoom`.split(`.`).sort().reverse();function vt(e){let t=e.regex,n=ut(e),r={begin:/-(webkit|moz|ms|o)-(?=[a-z])/},i=/@-?\w[\w]*(-\w+)*/,a=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE];return{name:`CSS`,case_insensitive:!0,illegal:/[=|'\$]/,keywords:{keyframePosition:`from to`},classNameAliases:{keyframePosition:`selector-tag`},contains:[n.BLOCK_COMMENT,r,n.CSS_NUMBER_MODE,{className:`selector-id`,begin:/#[A-Za-z0-9_-]+/,relevance:0},{className:`selector-class`,begin:`\\.[a-zA-Z-][a-zA-Z0-9_-]*`,relevance:0},n.ATTRIBUTE_SELECTOR_MODE,{className:`selector-pseudo`,variants:[{begin:`:(`+ht.join(`|`)+`)`},{begin:`:(:)?(`+gt.join(`|`)+`)`}]},n.CSS_VARIABLE,{className:`attribute`,begin:`\\b(`+_t.join(`|`)+`)\\b`},{begin:/:/,end:/[;}{]/,contains:[n.BLOCK_COMMENT,n.HEXCOLOR,n.IMPORTANT,n.CSS_NUMBER_MODE,...a,{begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:`url data-uri`},contains:[...a,{className:`string`,begin:/[^)]/,endsWithParent:!0,excludeEnd:!0}]},n.FUNCTION_DISPATCH]},{begin:t.lookahead(/@/),end:`[{;]`,relevance:0,illegal:/:/,contains:[{className:`keyword`,begin:i},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:{$pattern:/[a-z-]+/,keyword:`and or not only`,attribute:mt.join(` `)},contains:[{begin:/[a-z-]+(?=:)/,className:`attribute`},...a,n.CSS_NUMBER_MODE]}]},{className:`selector-tag`,begin:`\\b(`+pt.join(`|`)+`)\\b`}]}}function yt(e){let t=`true false yes no null`,n={className:`attr`,variants:[{begin:/[\w*@][\w*@ :()\./-]*:(?=[ \t]|$)/},{begin:/"[\w*@][\w*@ :()\./-]*":(?=[ \t]|$)/},{begin:/'[\w*@][\w*@ :()\./-]*':(?=[ \t]|$)/}]},r={className:`template-variable`,variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]},i={className:`string`,relevance:0,begin:/'/,end:/'/,contains:[{match:/''/,scope:`char.escape`,relevance:0}]},a={className:`string`,relevance:0,variants:[{begin:/"/,end:/"/},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,r]},o=e.inherit(a,{variants:[{begin:/'/,end:/'/,contains:[{begin:/''/,relevance:0}]},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),s={className:`number`,begin:`\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b`},c={end:`,`,endsWithParent:!0,excludeEnd:!0,keywords:t,relevance:0},l={begin:/\{/,end:/\}/,contains:[c],illegal:`\\n`,relevance:0},u={begin:`\\[`,end:`\\]`,contains:[c],illegal:`\\n`,relevance:0},d=[n,{className:`meta`,begin:`^---\\s*$`,relevance:10},{className:`string`,begin:`[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*`},{begin:`<%[%=-]?`,end:`[%-]?%>`,subLanguage:`ruby`,excludeBegin:!0,excludeEnd:!0,relevance:0},{className:`type`,begin:`!\\w+![\\w#;/?:@&=+$,.~*'()[\\]]+`},{className:`type`,begin:`!<[\\w#;/?:@&=+$,.~*'()[\\]]+>`},{className:`type`,begin:`![\\w#;/?:@&=+$,.~*'()[\\]]+`},{className:`type`,begin:`!![\\w#;/?:@&=+$,.~*'()[\\]]+`},{className:`meta`,begin:`&`+e.UNDERSCORE_IDENT_RE+`$`},{className:`meta`,begin:`\\*`+e.UNDERSCORE_IDENT_RE+`$`},{className:`bullet`,begin:`-(?=[ ]|$)`,relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:t,keywords:{literal:t}},s,{className:`number`,begin:e.C_NUMBER_RE+`\\b`,relevance:0},l,u,i,a],f=[...d];return f.pop(),f.push(o),c.contains=f,{name:`YAML`,case_insensitive:!0,aliases:[`yml`],contains:d}}M.registerLanguage(`javascript`,he),M.registerLanguage(`typescript`,ct),M.registerLanguage(`python`,_e),M.registerLanguage(`json`,ge),M.registerLanguage(`bash`,ve),M.registerLanguage(`xml`,lt),M.registerLanguage(`css`,vt),M.registerLanguage(`yaml`,yt);var bt={js:`javascript`,ts:`typescript`,py:`python`,sh:`bash`,shell:`bash`,yml:`yaml`,html:`xml`};function xt(e){return e.replace(/&/g,`&`).replace(//g,`>`)}function St(e,t){let n=bt[(t||``).toLowerCase()]||(t||``).toLowerCase();if(n&&M.getLanguage(n))try{return M.highlight(e,{language:n,ignoreIllegals:!0}).value}catch{}return xt(e)}var Ct=M,wt={};function Tt(e){let t=wt[e];if(t)return t;t=wt[e]=[];for(let e=0;e<128;e++){let n=String.fromCharCode(e);t.push(n)}for(let n=0;n=55296&&e<=57343?`���`:String.fromCharCode(e),r+=6;continue}}if((a&248)==240&&r+91114111?t+=`����`:(e-=65536,t+=String.fromCharCode(55296+(e>>10),56320+(e&1023))),r+=9;continue}}t+=`�`}return t})}N.defaultChars=`;/?:@&=+$,#`,N.componentChars=``;var Et={};function Dt(e){let t=Et[e];if(t)return t;t=Et[e]=[];for(let e=0;e<128;e++){let n=String.fromCharCode(e);/^[0-9a-z]$/i.test(n)?t.push(n):t.push(`%`+(`0`+e.toString(16).toUpperCase()).slice(-2))}for(let n=0;n=55296&&o<=57343){if(o>=55296&&o<=56319&&t+1=56320&&n<=57343){i+=encodeURIComponent(e[t]+e[t+1]),t++;continue}}i+=`%EF%BF%BD`;continue}i+=encodeURIComponent(e[t])}return i}P.defaultChars=`;/?:@&=+$,-_.!~*'()#`,P.componentChars=`-_.!~*'()`;function Ot(e){let t=``;return t+=e.protocol||``,t+=e.slashes?`//`:``,t+=e.auth?e.auth+`@`:``,e.hostname&&e.hostname.indexOf(`:`)!==-1?t+=`[`+e.hostname+`]`:t+=e.hostname||``,t+=e.port?`:`+e.port:``,t+=e.pathname||``,t+=e.search||``,t+=e.hash||``,t}function kt(){this.protocol=null,this.slashes=null,this.auth=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.pathname=null}var At=/^([a-z0-9.+-]+:)/i,jt=/:[0-9]*$/,Mt=/^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,Nt=[`%`,`/`,`?`,`;`,`#`,`'`,`{`,`}`,`|`,`\\`,`^`,"`",`<`,`>`,`"`,"`",` `,`\r`,` `,` `],Pt=[`/`,`?`,`#`],Ft=255,It=/^[+a-z0-9A-Z_-]{0,63}$/,Lt=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,Rt={javascript:!0,"javascript:":!0},zt={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0};function Bt(e,t){if(e&&e instanceof kt)return e;let n=new kt;return n.parse(e,t),n}kt.prototype.parse=function(e,t){let n,r,i,a=e;if(a=a.trim(),!t&&e.split(`#`).length===1){let e=Mt.exec(a);if(e)return this.pathname=e[1],e[2]&&(this.search=e[2]),this}let o=At.exec(a);if(o&&(o=o[0],n=o.toLowerCase(),this.protocol=o,a=a.substr(o.length)),(t||o||a.match(/^\/\/[^@\/]+@[^@\/]+/))&&(i=a.substr(0,2)===`//`,i&&!(o&&Rt[o])&&(a=a.substr(2),this.slashes=!0)),!Rt[o]&&(i||o&&!zt[o])){let e=-1;for(let t=0;t127?r+=`x`:r+=n[e];if(!r.match(It)){let r=e.slice(0,t),i=e.slice(t+1),o=n.match(Lt);o&&(r.push(o[1]),i.unshift(o[2])),i.length&&(a=i.join(`.`)+a),this.hostname=r.join(`.`);break}}}}this.hostname.length>Ft&&(this.hostname=``),o&&(this.hostname=this.hostname.substr(1,this.hostname.length-2))}let s=a.indexOf(`#`);s!==-1&&(this.hash=a.substr(s),a=a.slice(0,s));let c=a.indexOf(`?`);return c!==-1&&(this.search=a.substr(c),a=a.slice(0,c)),a&&(this.pathname=a),zt[n]&&this.hostname&&!this.pathname&&(this.pathname=``),this},kt.prototype.parseHost=function(e){let t=jt.exec(e);t&&(t=t[0],t!==`:`&&(this.port=t.substr(1)),e=e.substr(0,e.length-t.length)),e&&(this.hostname=e)};var Vt=e({decode:()=>N,encode:()=>P,format:()=>Ot,parse:()=>Bt}),Ht=e({Any:()=>Ut,Cc:()=>Wt,Cf:()=>Gt,P:()=>Kt,S:()=>qt,Z:()=>Jt}),Ut=/[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/,Wt=/[\0-\x1F\x7F-\x9F]/,Gt=/[\xAD\u0600-\u0605\u061C\u06DD\u070F\u0890\u0891\u08E2\u180E\u200B-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u206F\uFEFF\uFFF9-\uFFFB]|\uD804[\uDCBD\uDCCD]|\uD80D[\uDC30-\uDC3F]|\uD82F[\uDCA0-\uDCA3]|\uD834[\uDD73-\uDD7A]|\uDB40[\uDC01\uDC20-\uDC7F]/,Kt=/[!-#%-\*,-\/:;\?@\[-\]_\{\}\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061D-\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u09FD\u0A76\u0AF0\u0C77\u0C84\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B4E\u1B4F\u1B5A-\u1B60\u1B7D-\u1B7F\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E4F\u2E52-\u2E5D\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD803[\uDD6E\uDEAD\uDED0\uDF55-\uDF59\uDF86-\uDF89]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC8\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9\uDFD4\uDFD5\uDFD7\uDFD8]|\uD805[\uDC4B-\uDC4F\uDC5A\uDC5B\uDC5D\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDE60-\uDE6C\uDEB9\uDF3C-\uDF3E]|\uD806[\uDC3B\uDD44-\uDD46\uDDE2\uDE3F-\uDE46\uDE9A-\uDE9C\uDE9E-\uDEA2\uDF00-\uDF09\uDFE1]|\uD807[\uDC41-\uDC45\uDC70\uDC71\uDEF7\uDEF8\uDF43-\uDF4F\uDFFF]|\uD809[\uDC70-\uDC74]|\uD80B[\uDFF1\uDFF2]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD81B[\uDD6D-\uDD6F\uDE97-\uDE9A\uDFE2]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]|\uD839\uDDFF|\uD83A[\uDD5E\uDD5F]/,qt=/[\$\+<->\^`\|~\xA2-\xA6\xA8\xA9\xAC\xAE-\xB1\xB4\xB8\xD7\xF7\u02C2-\u02C5\u02D2-\u02DF\u02E5-\u02EB\u02ED\u02EF-\u02FF\u0375\u0384\u0385\u03F6\u0482\u058D-\u058F\u0606-\u0608\u060B\u060E\u060F\u06DE\u06E9\u06FD\u06FE\u07F6\u07FE\u07FF\u0888\u09F2\u09F3\u09FA\u09FB\u0AF1\u0B70\u0BF3-\u0BFA\u0C7F\u0D4F\u0D79\u0E3F\u0F01-\u0F03\u0F13\u0F15-\u0F17\u0F1A-\u0F1F\u0F34\u0F36\u0F38\u0FBE-\u0FC5\u0FC7-\u0FCC\u0FCE\u0FCF\u0FD5-\u0FD8\u109E\u109F\u1390-\u1399\u166D\u17DB\u1940\u19DE-\u19FF\u1B61-\u1B6A\u1B74-\u1B7C\u1FBD\u1FBF-\u1FC1\u1FCD-\u1FCF\u1FDD-\u1FDF\u1FED-\u1FEF\u1FFD\u1FFE\u2044\u2052\u207A-\u207C\u208A-\u208C\u20A0-\u20C1\u2100\u2101\u2103-\u2106\u2108\u2109\u2114\u2116-\u2118\u211E-\u2123\u2125\u2127\u2129\u212E\u213A\u213B\u2140-\u2144\u214A-\u214D\u214F\u218A\u218B\u2190-\u2307\u230C-\u2328\u232B-\u2429\u2440-\u244A\u249C-\u24E9\u2500-\u2767\u2794-\u27C4\u27C7-\u27E5\u27F0-\u2982\u2999-\u29D7\u29DC-\u29FB\u29FE-\u2B73\u2B76-\u2BFF\u2CE5-\u2CEA\u2E50\u2E51\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFF\u3004\u3012\u3013\u3020\u3036\u3037\u303E\u303F\u309B\u309C\u3190\u3191\u3196-\u319F\u31C0-\u31E5\u31EF\u3200-\u321E\u322A-\u3247\u3250\u3260-\u327F\u328A-\u32B0\u32C0-\u33FF\u4DC0-\u4DFF\uA490-\uA4C6\uA700-\uA716\uA720\uA721\uA789\uA78A\uA828-\uA82B\uA836-\uA839\uAA77-\uAA79\uAB5B\uAB6A\uAB6B\uFB29\uFBB2-\uFBD2\uFD40-\uFD4F\uFD90\uFD91\uFDC8-\uFDCF\uFDFC-\uFDFF\uFE62\uFE64-\uFE66\uFE69\uFF04\uFF0B\uFF1C-\uFF1E\uFF3E\uFF40\uFF5C\uFF5E\uFFE0-\uFFE6\uFFE8-\uFFEE\uFFFC\uFFFD]|\uD800[\uDD37-\uDD3F\uDD79-\uDD89\uDD8C-\uDD8E\uDD90-\uDD9C\uDDA0\uDDD0-\uDDFC]|\uD802[\uDC77\uDC78\uDEC8]|\uD803[\uDD8E\uDD8F\uDED1-\uDED8]|\uD805\uDF3F|\uD807[\uDFD5-\uDFF1]|\uD81A[\uDF3C-\uDF3F\uDF45]|\uD82F\uDC9C|\uD833[\uDC00-\uDCEF\uDCFA-\uDCFC\uDD00-\uDEB3\uDEBA-\uDED0\uDEE0-\uDEF0\uDF50-\uDFC3]|\uD834[\uDC00-\uDCF5\uDD00-\uDD26\uDD29-\uDD64\uDD6A-\uDD6C\uDD83\uDD84\uDD8C-\uDDA9\uDDAE-\uDDEA\uDE00-\uDE41\uDE45\uDF00-\uDF56]|\uD835[\uDEC1\uDEDB\uDEFB\uDF15\uDF35\uDF4F\uDF6F\uDF89\uDFA9\uDFC3]|\uD836[\uDC00-\uDDFF\uDE37-\uDE3A\uDE6D-\uDE74\uDE76-\uDE83\uDE85\uDE86]|\uD838[\uDD4F\uDEFF]|\uD83B[\uDCAC\uDCB0\uDD2E\uDEF0\uDEF1]|\uD83C[\uDC00-\uDC2B\uDC30-\uDC93\uDCA0-\uDCAE\uDCB1-\uDCBF\uDCC1-\uDCCF\uDCD1-\uDCF5\uDD0D-\uDDAD\uDDE6-\uDE02\uDE10-\uDE3B\uDE40-\uDE48\uDE50\uDE51\uDE60-\uDE65\uDF00-\uDFFF]|\uD83D[\uDC00-\uDED8\uDEDC-\uDEEC\uDEF0-\uDEFC\uDF00-\uDFD9\uDFE0-\uDFEB\uDFF0]|\uD83E[\uDC00-\uDC0B\uDC10-\uDC47\uDC50-\uDC59\uDC60-\uDC87\uDC90-\uDCAD\uDCB0-\uDCBB\uDCC0\uDCC1\uDCD0-\uDCD8\uDD00-\uDE57\uDE60-\uDE6D\uDE70-\uDE7C\uDE80-\uDE8A\uDE8E-\uDEC6\uDEC8\uDECD-\uDEDC\uDEDF-\uDEEA\uDEEF-\uDEF8\uDF00-\uDF92\uDF94-\uDFEF\uDFFA]/,Jt=/[ \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]/,Yt=new Map([[0,65533],[128,8364],[130,8218],[131,402],[132,8222],[133,8230],[134,8224],[135,8225],[136,710],[137,8240],[138,352],[139,8249],[140,338],[142,381],[145,8216],[146,8217],[147,8220],[148,8221],[149,8226],[150,8211],[151,8212],[152,732],[153,8482],[154,353],[155,8250],[156,339],[158,382],[159,376]]);function Xt(e){return e>=55296&&e<=57343||e>1114111?65533:Yt.get(e)??e}function Zt(e){let t=atob(e),n=t.length&-2,r=new Uint16Array(n/2);for(let e=0,i=0;e=I.ZERO&&e<=I.NINE}function tn(e){return e>=I.UPPER_A&&e<=I.UPPER_F||e>=I.LOWER_A&&e<=I.LOWER_F}function nn(e){return e>=I.UPPER_A&&e<=I.UPPER_Z||e>=I.LOWER_A&&e<=I.LOWER_Z||en(e)}function rn(e){return e===I.EQUALS||nn(e)}var L;(function(e){e[e.EntityStart=0]=`EntityStart`,e[e.NumericStart=1]=`NumericStart`,e[e.NumericDecimal=2]=`NumericDecimal`,e[e.NumericHex=3]=`NumericHex`,e[e.NamedEntity=4]=`NamedEntity`})(L||={});var R;(function(e){e[e.Legacy=0]=`Legacy`,e[e.Strict=1]=`Strict`,e[e.Attribute=2]=`Attribute`})(R||={});var an=class{decodeTree;emitCodePoint;errors;constructor(e,t,n){this.decodeTree=e,this.emitCodePoint=t,this.errors=n}state=L.EntityStart;consumed=1;result=0;treeIndex=0;excess=1;decodeMode=R.Strict;runConsumed=0;startEntity(e){this.decodeMode=e,this.state=L.EntityStart,this.result=0,this.treeIndex=0,this.excess=1,this.consumed=1,this.runConsumed=0}write(e,t){switch(this.state){case L.EntityStart:return e.charCodeAt(t)===I.NUM?(this.state=L.NumericStart,this.consumed+=1,this.stateNumericStart(e,t+1)):(this.state=L.NamedEntity,this.stateNamedEntity(e,t));case L.NumericStart:return this.stateNumericStart(e,t);case L.NumericDecimal:return this.stateNumericDecimal(e,t);case L.NumericHex:return this.stateNumericHex(e,t);case L.NamedEntity:return this.stateNamedEntity(e,t)}}stateNumericStart(e,t){return t>=e.length?-1:(e.charCodeAt(t)|$t)===I.LOWER_X?(this.state=L.NumericHex,this.consumed+=1,this.stateNumericHex(e,t+1)):(this.state=L.NumericDecimal,this.stateNumericDecimal(e,t))}stateNumericHex(e,t){for(;t>14;for(;t>7;if(this.runConsumed===0){let n=r&F.JUMP_TABLE;if(e.charCodeAt(t)!==n)return this.result===0?0:this.emitNotTerminatedNamedEntity();t++,this.excess++,this.runConsumed++}for(;this.runConsumed=e.length)return-1;let r=this.runConsumed-1,i=n[this.treeIndex+1+(r>>1)],a=r%2==0?i&255:i>>8&255;if(e.charCodeAt(t)!==a)return this.runConsumed=0,this.result===0?0:this.emitNotTerminatedNamedEntity();t++,this.excess++,this.runConsumed++}this.runConsumed=0,this.treeIndex+=1+(a>>1),r=n[this.treeIndex],i=(r&F.VALUE_LENGTH)>>14}if(t>=e.length)break;let a=e.charCodeAt(t);if(a===I.SEMI&&i!==0&&(r&F.FLAG13)!==0)return this.emitNamedEntityData(this.treeIndex,i,this.consumed+this.excess);if(this.treeIndex=sn(n,r,this.treeIndex+Math.max(1,i),a),this.treeIndex<0)return this.result===0||this.decodeMode===R.Attribute&&(i===0||rn(a))?0:this.emitNotTerminatedNamedEntity();if(r=n[this.treeIndex],i=(r&F.VALUE_LENGTH)>>14,i!==0){if(a===I.SEMI)return this.emitNamedEntityData(this.treeIndex,i,this.consumed+this.excess);this.decodeMode!==R.Strict&&(r&F.FLAG13)===0&&(this.result=this.treeIndex,this.consumed+=this.excess,this.excess=0)}t++,this.excess++}return-1}emitNotTerminatedNamedEntity(){let{result:e,decodeTree:t}=this,n=(t[e]&F.VALUE_LENGTH)>>14;return this.emitNamedEntityData(e,n,this.consumed),this.errors?.missingSemicolonAfterCharacterReference(),this.consumed}emitNamedEntityData(e,t,n){let{decodeTree:r}=this;return this.emitCodePoint(t===1?r[e]&~(F.VALUE_LENGTH|F.FLAG13):r[e+1],n),t===3&&this.emitCodePoint(r[e+2],n),n}end(){switch(this.state){case L.NamedEntity:return this.result!==0&&(this.decodeMode!==R.Attribute||this.result===this.treeIndex)?this.emitNotTerminatedNamedEntity():0;case L.NumericDecimal:return this.emitNumericEntity(0,2);case L.NumericHex:return this.emitNumericEntity(0,3);case L.NumericStart:return this.errors?.absenceOfDigitsInNumericCharacterReference(this.consumed),0;case L.EntityStart:return 0}}};function on(e){let t=``,n=new an(e,e=>t+=String.fromCodePoint(e));return function(e,r){let i=0,a=0;for(;(a=e.indexOf(`&`,a))>=0;){t+=e.slice(i,a),n.startEntity(r);let o=n.write(e,a+1);if(o<0){i=a+n.end();break}i=a+o,a=o===0?i+1:i}let o=t+e.slice(i);return t=``,o}}function sn(e,t,n,r){let i=(t&F.BRANCH_LENGTH)>>7,a=t&F.JUMP_TABLE;if(i===0)return a!==0&&r===a?n:-1;if(a){let t=r-a;return t<0||t>=i?-1:e[n+t]-1}let o=i+1>>1,s=0,c=i-1;for(;s<=c;){let t=s+c>>>1,i=e[n+(t>>1)]>>(t&1)*8&255;if(ir)c=t-1;else return e[n+o+t]}return-1}var cn=on(Qt);function ln(e){return cn(e,R.Strict)}var un=class{src_Any=Ut.source;src_Cc=Wt.source;src_Z=Jt.source;src_P=Kt.source;src_ZPCc=[this.src_Z,this.src_P,this.src_Cc].join(`|`);src_ZCc=[this.src_Z,this.src_Cc].join(`|`);cache={};opts={maxLength:1e4,urlAuth:!1,schema_names:[]};constructor(e={}){this.opts={...this.opts,...e}}set(e={}){return this.opts={...this.opts,...e},this.cache={},this}escapeRE(e){return e.replace(/[.?*+^$[\]\\(){}|-]/g,`\\$&`)}nestedPairRE(e,t,n=4){let r=this.escapeRE(e),i=this.escapeRE(t),a=`(?:(?!${this.src_ZCc}|${r}|${i}).)`,o=`${r}${a}{0,1000}${i}`;for(let e=2;e<=n;e++)o=`${r}(?:${a}|${o}){0,1000}${i}`;return o}get_text_separators(){return this.cache.text_separators??=/[><\uff5c]/}get_pseudo_letter(){return this.cache.src_pseudo_letter??=RegExp(`(?:(?!${this.get_text_separators().source}|${this.src_ZPCc})${this.src_Any})`)}get_ipv4_addr(){return this.cache.src_ip4??=RegExp(`(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])[.]){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])`)}get_ipv6_addr(){let e=`[0-9A-Fa-f]{1,4}`,t=`(?:(?:${e}:${e})|${this.get_ipv4_addr().source})`;return this.cache.src_ip6_addr??=RegExp(`(?:(?:${e}:){6}${t}|::(?:${e}:){5}${t}|(?:${e})?::(?:${e}:){4}${t}|(?:(?:${e}:){0,1}${e})?::(?:${e}:){3}${t}|(?:(?:${e}:){0,2}${e})?::(?:${e}:){2}${t}|(?:(?:${e}:){0,3}${e})?::${e}:${t}|(?:(?:${e}:){0,4}${e})?::${t}|(?:(?:${e}:){0,5}${e})?::${e}|(?:(?:${e}:){0,6}${e})?::)`)}get_ipv6_url_host(){return this.cache.src_ip6_host??=RegExp(`\\[${this.get_ipv6_addr().source}\\]`)}get_ipv6_mail_host(){return this.cache.src_ipv6_mail_host??=RegExp(`\\[IPv6:${this.get_ipv6_addr().source}\\]`)}get_auth(){return this.cache.src_auth??=RegExp(`(?:(?:(?!${this.src_ZCc}|[@/\\[\\]()]).){1,50}@)?`)}get_port(){return this.cache.src_port??=RegExp(`(?::(?:6(?:[0-4]\\d{3}|5(?:[0-4]\\d{2}|5(?:[0-2]\\d|3[0-5])))|[1-5]?\\d{1,4}))?`)}get_host_terminator(){return this.cache.src_host_terminator??=RegExp(`(?=$|${this.get_text_separators().source}|${this.src_ZPCc})(?!${this.opts[`---`]?`-(?!--)|`:`-|`}_|:\\d|\\.-|\\.(?!$|${this.src_ZPCc}))`)}get_path_terminator(){return this.cache.src_path_terminator??=RegExp(`${this.src_ZPCc}|${this.get_text_separators().source}`)}get_path(){return this.cache.src_path??=RegExp(`(?:[/?#](?:${this.nestedPairRE(`[`,`]`)}|${this.nestedPairRE(`(`,`)`)}|${this.nestedPairRE(`{`,`}`)}|\\"(?:(?!${this.src_ZCc}|["]).){1,100}\\"|\\'(?:(?!${this.src_ZCc}|[']).){1,100}\\'|\\'(?=${this.get_pseudo_letter().source}|[-])|\\.{2,20}[:]?[a-zA-Z0-9%/&]|\\.(?!${this.src_ZCc}|[.]|$)|`+(this.opts[`---`]?`\\-(?!--(?:[^-]|$))(?:-{0,19})|`:`\\-{1,20}|`)+`,(?!${this.src_ZCc}|$)|;(?!${this.src_ZCc}|$)|\\!{1,20}(?!${this.src_ZCc}|[!]|$)|\\?(?!${this.src_ZCc}|[?]|$)|`+this.get_path_extra().source+`[\\\\/:%@#&=_~*]|(?!${this.get_path_terminator().source}).){1,${this.opts.maxLength}}|\\/)?`)}get_mail_name(){return this.cache.src_mail_name??=RegExp("[-!#$%&'*+/=?^_`{|}~a-zA-Z0-9](?:[-!#$%&'*+/=?^_`{|}~a-zA-Z0-9]|[.](?=[-!#$%&'*+/=?^_`{|}~a-zA-Z0-9])){0,63}")}get_xn(){return this.cache.src_xn??=RegExp(`xn--[a-z0-9\\-]{1,59}`)}get_tld(){if(this.cache.tld)return this.cache.tld;let e=[...new Set(this.opts.tlds||[])].sort().reverse().join(`|`);return this.cache.tld=RegExp(`${e||`$#none#$`}|${this.get_xn().source}`),this.cache.tld}get_domain_root(){return this.cache.src_domain_root??=RegExp(`(?:`+this.get_xn().source+`|${this.get_pseudo_letter().source}{1,63})`)}get_domain(){return this.cache.src_domain??=RegExp(`(?:`+this.get_xn().source+`|(?:${this.get_pseudo_letter().source})|(?:${this.get_pseudo_letter().source}(?:-|${this.get_pseudo_letter().source}){0,61}${this.get_pseudo_letter().source}))`)}get_url_host_port(){return this.cache.url_host_port??=RegExp(`(?:`+this.get_ipv6_url_host().source+`|(?:(?:(?:${this.get_domain().source})\\.){0,10}${this.get_domain().source}))`+this.get_port().source+this.get_host_terminator().source)}get_fuzzy_url_host_port(){return this.cache.fuzzy_url_host_port??=RegExp(`(?:`+(this.opts.fuzzyIP?this.get_ipv4_addr().source+`|`:``)+`(?:(?:(?:${this.get_domain().source})\\.){1,10}(?:${this.get_tld().source})))`+this.get_host_terminator().source)}get_mail_host(){return this.cache.src_mail_host??=RegExp(`(?:`+this.get_ipv6_mail_host().source+`|(?:(?:(?:${this.get_domain().source})\\.){0,4}${this.get_domain().source}))`+this.get_host_terminator().source)}get_fuzzy_mail_host(){return this.cache.src_fuzzy_mail_host??=RegExp(`(?:`+this.get_ipv6_mail_host().source+`|(?:(?:(?:${this.get_domain().source})[.]){1,4}${this.get_domain_root().source}))`+this.get_host_terminator().source)}get_path_extra(){return this.cache.src_path_extra??=RegExp(``)}get_fuzzy_mail_host_search(){return this.cache.mail_fuzzy_host_search??=RegExp(`@${this.get_fuzzy_mail_host().source}`,`ig`)}get_fuzzy_link_search(){return this.cache.link_fuzzy_search??=RegExp(`(^|(?![.:/\\-_@])(?:[$+<=>^\`|\uff5c]|${this.src_ZPCc}))(?:(?![$+<=>^\`|\uff5c])${this.get_fuzzy_url_host_port().source}${this.get_path().source})`,`ig`)}get_http_validator(){return this.cache.http_validator??=RegExp(`\\/\\/`+(this.opts.urlAuth?this.get_auth().source:``)+this.get_url_host_port().source+this.get_path().source,`iy`)}get_relative_proto_validator(){return this.cache.relative_proto_validator??=RegExp((this.opts.urlAuth?this.get_auth().source:``)+`(?:localhost|${this.get_ipv6_url_host().source}|(?:(?:${this.get_domain().source})[.]){1,10}${this.get_domain_root().source})`+this.get_port().source+this.get_host_terminator().source+this.get_path().source,`iy`)}get_mail_name_validator(){return this.cache.mail_name_validator??=RegExp(`(?:^|${this.get_text_separators().source}|"|\\(|${this.src_ZCc})(${this.get_mail_name().source})$`)}get_mailto_validator(){return this.cache.mailto_validator??=RegExp(`${this.get_mail_name().source}@${this.get_mail_host().source}`,`iy`)}get_schema_names(){return this.cache.schema_names??=new RegExp((this.opts.schema_names||[]).map(e=>this.escapeRE(e)).join(`|`))}get_schema_search(){return this.cache.schema_search??=RegExp(`(^|(?!_)(?:[><\uff5c]|${this.src_ZPCc}))(${this.get_schema_names().source})`,`ig`)}get_schema_at_start(){return this.cache.schema_at_start??=RegExp(`^${this.get_schema_search().source}`,`i`)}},dn={validate:(e,t,n)=>{let r=n.re.get_http_validator();r.lastIndex=t;let i=r.exec(e);return i?i[0].length:0},normalize:(e,t)=>t.normalize(e)},fn={"http:":dn,"https:":dn,"ftp:":dn,"//":{validate:function(e,t,n){let r=n.re.get_relative_proto_validator();r.lastIndex=t;let i=r.exec(e);return i?t>=3&&e[t-3]===`:`||t>=3&&e[t-3]===`/`?0:i[0].length:0},normalize:(e,t)=>t.normalize(e)},"mailto:":{validate:function(e,t,n){let r=n.re.get_mailto_validator();r.lastIndex=t;let i=r.exec(e);return i?i[0].length:0},normalize:(e,t)=>t.normalize(e)}},pn=`a:cdefgilmnoqrstuwxz|b:abdefghijmnorstvwyz|c:acdfghiklmnoruvwxyz|d:ejkmoz|e:cegrstu|f:ijkmor|g:abdefghilmnpqrstuwy|h:kmnrtu|i:delmnoqrst|j:emop|k:eghimnprwyz|l:abcikrstuvy|m:acdeghklmnopqrstuvwxyz|n:acefgilopruz|o:m|p:aefghklmnrstwy|q:a|r:eosuw|s:abcdeghijklmnortuvxyz|t:cdfghjklmnortvwz|u:agksyz|v:aceginu|w:fs|y:et|z:amw`,mn=`biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф`;function hn(){let e=mn.split(`|`);return pn.split(`|`).forEach(t=>{let n=t.indexOf(`:`),r=t.slice(0,n);for(let i of t.slice(n+1))e.push(r+i)}),e}var gn={fuzzyLink:!1,fuzzyEmail:!0,fuzzyIP:!1,"---":!1,tlds:hn(),urlAuth:!1,maxLength:1e4},_n=class{schema;index;lastIndex;raw;text;url;constructor(e,t,n,r){let i=e.slice(n,r);this.schema=t.toLowerCase(),this.index=n,this.lastIndex=r,this.raw=i,this.text=i,this.url=i}},vn=class{__opts__;__schemas__;re;constructor(e={}){let{rebuilder:t,...n}=e;this.__opts__={...gn,...n},this.__schemas__={...fn},this.re=t||new un,this.re.set({...this.__opts__,schema_names:Object.keys(this.__schemas__)})}add(e,t=null){if(!t)delete this.__schemas__[e];else{let n={normalize:(e,t)=>t.normalize(e),...t};this.__schemas__[e]=n}return this.re.set({...this.__opts__,schema_names:Object.keys(this.__schemas__)}),this}set(e={}){return this.__opts__={...this.__opts__,...e},this.re.set({...this.__opts__,schema_names:Object.keys(this.__schemas__)}),this}test(e){if(!e.length)return!1;let t,n;for(n=this.re.get_schema_search(),n.lastIndex=0;(t=n.exec(e))!==null;)if(this.testSchemaAt(e,t[2],n.lastIndex))return!0;if(this.__opts__.fuzzyLink&&this.__schemas__[`http:`]&&(n=this.re.get_fuzzy_link_search(),n.lastIndex=0,n.exec(e)!==null))return!0;if(this.__opts__.fuzzyEmail&&this.__schemas__[`mailto:`]&&e.indexOf(`@`)>=0){let n=this.re.get_fuzzy_mail_host_search(),r=this.re.get_mail_name_validator();for(n.lastIndex=0;(t=n.exec(e))!==null;){let n=e.slice(Math.max(0,t.index-65),t.index);if(r.test(n))return!0}}return!1}testSchemaAt(e,t,n){return this.__schemas__[t.toLowerCase()]?this.__schemas__[t.toLowerCase()].validate(e.slice(0,n+this.__opts__.maxLength),n,this):0}match(e){let t=[],n=this.re.get_schema_search(),r,i,a,o,s,c,l=!1,u=!1,d=!1,f=0;if(!e.length)return null;for(n.lastIndex=0,this.__opts__.fuzzyLink&&this.__schemas__[`http:`]&&(r=this.re.get_fuzzy_link_search(),r.lastIndex=0),this.__opts__.fuzzyEmail&&this.__schemas__[`mailto:`]&&(i=this.re.get_fuzzy_mail_host_search(),i.lastIndex=0,a=this.re.get_mail_name_validator());;){let p=Math.max(f-1,0);if(i&&a&&!d&&(!s||s.index=f)break;i.lastIndex=f)break;r.lastIndexm.lastIndex))&&(m=o);let h;if(!l)for(;;){if(!c){n.lastIndexm.index)break;let t=c;c=void 0;let r=this.testSchemaAt(e,t.schema,t.lastIndex);if(r){h={schema:t.schema,index:t.index,lastIndex:t.lastIndex+r};break}}let g=h;if((!g||s&&(s.indexg.lastIndex))&&(g=s),(!g||o&&(o.indexg.lastIndex))&&(g=o),!g)break;g===s?s=void 0:g===o&&(o=void 0);let _=new _n(e,g.schema,g.index,g.lastIndex);_.schema?this.__schemas__[_.schema].normalize(_,this):this.normalize(_),t.push(_),f=g.lastIndex}return t.length?t:null}matchAtStart(e){if(!e.length)return null;let t=this.re.get_schema_at_start().exec(e);if(!t)return null;let n=this.testSchemaAt(e,t[2],t[0].length);if(!n)return null;let r=new _n(e,t[2],t.index+t[1].length,t.index+t[0].length+n);return this.__schemas__[r.schema].normalize(r,this),r}tlds(e,t=!1){return e=Array.isArray(e)?e:[e],t?this.__opts__.tlds=this.__opts__.tlds.concat(e):this.__opts__.tlds=e,this.re.set({...this.__opts__,schema_names:Object.keys(this.__schemas__)}),this}normalize(e){e.schema||(e.url=`http://${e.url}`),e.schema===`mailto:`&&!/^mailto:/i.test(e.url)&&(e.url=`mailto:${e.url}`)}},z=2147483647,B=36,yn=1,bn=26,xn=38,Sn=700,Cn=72,wn=128,Tn=`-`,En=/^xn--/,Dn=/[^\0-\x7F]/,On=/[\x2E\u3002\uFF0E\uFF61]/g,kn={overflow:`Overflow: input needs wider integers to process`,"not-basic":`Illegal input >= 0x80 (not a basic code point)`,"invalid-input":`Invalid input`},An=35,V=Math.floor,jn=String.fromCharCode;function H(e){throw RangeError(kn[e])}function Mn(e,t){let n=[],r=e.length;for(;r--;)n[r]=t(e[r]);return n}function Nn(e,t){let n=e.split(`@`),r=``;n.length>1&&(r=n[0]+`@`,e=n[1]),e=e.replace(On,`.`);let i=Mn(e.split(`.`),t).join(`.`);return r+i}function Pn(e){let t=[],n=0,r=e.length;for(;n=55296&&i<=56319&&nString.fromCodePoint(...e),In=function(e){return e>=48&&e<58?26+(e-48):e>=65&&e<91?e-65:e>=97&&e<123?e-97:B},Ln=function(e,t){return e+22+75*(e<26)-((t!=0)<<5)},Rn=function(e,t,n){let r=0;for(e=n?V(e/Sn):e>>1,e+=V(e/t);e>455;r+=B)e=V(e/An);return V(r+36*e/(e+xn))},zn=function(e){let t=[],n=e.length,r=0,i=wn,a=Cn,o=e.lastIndexOf(Tn);o<0&&(o=0);for(let n=0;n=128&&H(`not-basic`),t.push(e.charCodeAt(n));for(let s=o>0?o+1:0;s=n&&H(`invalid-input`);let o=In(e.charCodeAt(s++));o>=B&&H(`invalid-input`),o>V((z-r)/t)&&H(`overflow`),r+=o*t;let c=i<=a?yn:i>=a+bn?bn:i-a;if(oV(z/l)&&H(`overflow`),t*=l}let c=t.length+1;a=Rn(r-o,c,o==0),V(r/c)>z-i&&H(`overflow`),i+=V(r/c),r%=c,t.splice(r++,0,i)}return String.fromCodePoint(...t)},Bn=function(e){let t=[];e=Pn(e);let n=e.length,r=wn,i=0,a=Cn;for(let n of e)n<128&&t.push(jn(n));let o=t.length,s=o;for(o&&t.push(Tn);s=r&&tV((z-i)/c)&&H(`overflow`),i+=(n-r)*c,r=n;for(let n of e)if(nz&&H(`overflow`),n===r){let e=i;for(let n=B;;n+=B){let r=n<=a?yn:n>=a+bn?bn:n-a;if(e{let n={};for(var r in e)Hn(n,r,{get:e[r],enumerable:!0});return t||Hn(n,Symbol.toStringTag,{value:`Module`}),n},Wn=Un({arrayReplaceAt:()=>Kn,asciiTrim:()=>ur,callable:()=>Gn,escapeHtml:()=>G,escapeRE:()=>ir,fromCodePoint:()=>U,isMdAsciiPunct:()=>sr,isPunctChar:()=>ar,isPunctCharCode:()=>or,isSpace:()=>K,isValidEntityCode:()=>qn,isWhiteSpace:()=>q,lib:()=>dr,normalizeReference:()=>cr,unescapeAll:()=>W,unescapeMd:()=>Qn});function Gn(e){let t=function(...n){return Reflect.construct(e,n,new.target&&new.target!==t?new.target:e)};return Object.defineProperty(t,"name",{value:e.name}),Object.setPrototypeOf(t,e),t.prototype=e.prototype,t}function Kn(e,t,n){return[].concat(e.slice(0,t),n,e.slice(t+1))}function qn(e){return!(e>=55296&&e<=57343||e>=64976&&e<=65007||(e&65535)==65535||(e&65535)==65534||e>=0&&e<=8||e===11||e>=14&&e<=31||e>=127&&e<=159||e>1114111)}function U(e){if(e>65535){e-=65536;let t=55296+(e>>10),n=56320+(e&1023);return String.fromCharCode(t,n)}return String.fromCharCode(e)}var Jn=/\\([!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])/g,Yn=RegExp(`${Jn.source}|&([a-z#][a-z0-9]{1,31});`,`gi`),Xn=/^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))$/i;function Zn(e,t){if(t.charCodeAt(0)===35&&Xn.test(t)){let n=t[1].toLowerCase()===`x`?parseInt(t.slice(2),16):parseInt(t.slice(1),10);return qn(n)?U(n):e}let n=ln(e);return n===e?e:n}function Qn(e){return e.indexOf(`\\`)<0?e:e.replace(Jn,`$1`)}function W(e){return e.indexOf(`\\`)<0&&e.indexOf(`&`)<0?e:e.replace(Yn,function(e,t,n){return t||Zn(e,n)})}var $n=/[&<>"]/,er=/[&<>"]/g,tr={"&":`&`,"<":`<`,">":`>`,'"':`"`};function nr(e){return tr[e]}function G(e){return $n.test(e)?e.replace(er,nr):e}var rr=/[.?*+^$[\]\\(){}|-]/g;function ir(e){return e.replace(rr,`\\$&`)}function K(e){switch(e){case 9:case 32:return!0}return!1}function q(e){if(e>=8192&&e<=8202)return!0;switch(e){case 9:case 10:case 11:case 12:case 13:case 32:case 160:case 5760:case 8239:case 8287:case 12288:return!0}return!1}function ar(e){return Kt.test(e)||qt.test(e)}function or(e){return ar(U(e))}function sr(e){switch(e){case 33:case 34:case 35:case 36:case 37:case 38:case 39:case 40:case 41:case 42:case 43:case 44:case 45:case 46:case 47:case 58:case 59:case 60:case 61:case 62:case 63:case 64:case 91:case 92:case 93:case 94:case 95:case 96:case 123:case 124:case 125:case 126:return!0;default:return!1}}function cr(e){return e=e.trim().replace(/\s+/g,` `),e.toLowerCase().toUpperCase()}function lr(e){return e===32||e===9||e===10||e===13}function ur(e){let t=0;for(;t=t&&lr(e.charCodeAt(n));n--);return e.slice(t,n+1)}var dr={mdurl:Vt,ucmicro:Ht};function fr(e,t,n){let r,i,a,o,s=e.posMax,c=e.pos;for(e.pos=t+1,r=1;e.pos32))return a;if(r===41){if(o===0)break;o--}i++}return t===i||o!==0?a:(a.str=W(e.slice(t,i)),a.pos=i,a.ok=!0,a)}function mr(e,t,n,r){let i,a=t,o={ok:!1,can_continue:!1,pos:0,str:``,marker:0};if(r)o.str=r.str,o.marker=r.marker;else{if(a>=n)return o;let r=e.charCodeAt(a);if(r!==34&&r!==39&&r!==40)return o;t++,a++,r===40&&(r=41),o.marker=r}for(;apr,parseLinkLabel:()=>fr,parseLinkTitle:()=>mr});function gr(e){"@babel/helpers - typeof";return gr=typeof Symbol==`function`&&typeof Symbol.iterator==`symbol`?function(e){return typeof e}:function(e){return e&&typeof Symbol==`function`&&e.constructor===Symbol&&e!==Symbol.prototype?`symbol`:typeof e},gr(e)}function _r(e,t){if(gr(e)!=`object`||!e)return e;var n=e[Symbol.toPrimitive];if(n!==void 0){var r=n.call(e,t||`default`);if(gr(r)!=`object`)return r;throw TypeError(`@@toPrimitive must return a primitive value.`)}return(t===`string`?String:Number)(e)}function vr(e){var t=_r(e,`string`);return gr(t)==`symbol`?t:t+``}function J(e,t,n){return(t=vr(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var Y=class{constructor(e,t,n){J(this,`map`,null),J(this,`level`,0),J(this,`children`,null),J(this,`content`,``),J(this,`markup`,``),J(this,`info`,``),J(this,`block`,!1),J(this,`hidden`,!1),this.type=e,this.tag=t,this.attrs=null,this.nesting=n,this.meta=null}attrIndex(e){if(!this.attrs)return-1;let t=this.attrs;for(let n=0,r=t.length;n=0&&(n=this.attrs[t][1]),n}attrJoin(e,t){let n=this.attrIndex(e);n<0?this.attrPush([e,t]):this.attrs[n][1]=`${this.attrs[n][1]} ${t}`}},yr=class{constructor(){J(this,`__rules__`,[]),J(this,`__cache__`,null)}__find__(e){for(let t=0;t{t.enabled&&t.alt.forEach(t=>{t&&e.add(t)})}),this.__cache__=Object.create(null),this.__cache__[``]=[],this.__rules__.forEach(e=>{e.enabled&&this.__cache__[``].push(e.fn)}),e.forEach(e=>{this.__cache__[e]=[],this.__rules__.forEach(t=>{t.enabled&&t.alt.indexOf(e)>=0&&this.__cache__[e].push(t.fn)})})}at(e,t,n={}){let r=this.__find__(e);if(r===-1)throw Error(`Parser rule not found: ${e}`);this.__rules__[r].fn=t,this.__rules__[r].alt=n.alt||[],this.__cache__=null}before(e,t,n,r={}){let i=this.__find__(e);if(i===-1)throw Error(`Parser rule not found: ${e}`);this.__rules__.splice(i,0,{name:t,enabled:!0,fn:n,alt:r.alt||[]}),this.__cache__=null}after(e,t,n,r={}){let i=this.__find__(e);if(i===-1)throw Error(`Parser rule not found: ${e}`);this.__rules__.splice(i+1,0,{name:t,enabled:!0,fn:n,alt:r.alt||[]}),this.__cache__=null}push(e,t,n={}){this.__rules__.push({name:e,enabled:!0,fn:t,alt:n.alt||[]}),this.__cache__=null}enable(e,t=!1){Array.isArray(e)||(e=[e]);let n=[];return e.forEach(e=>{let r=this.__find__(e);if(r<0){if(t)return;throw Error(`Rules manager: invalid rule name ${e}`)}this.__rules__[r].enabled=!0,n.push(e)}),this.__cache__=null,n}enableOnly(e,t=!1){Array.isArray(e)||(e=[e]),this.__rules__.forEach(e=>{e.enabled=!1}),this.enable(e,t)}disable(e,t=!1){Array.isArray(e)||(e=[e]);let n=[];return e.forEach(e=>{let r=this.__find__(e);if(r<0){if(t)return;throw Error(`Rules manager: invalid rule name ${e}`)}this.__rules__[r].enabled=!1,n.push(e)}),this.__cache__=null,n}getRules(e){return this.__cache__||this.__compile__(),this.__cache__[e]||[]}},X={};X.code_inline=function(e,t,n,r,i){let a=e[t];return`${G(a.content)}`},X.code_block=function(e,t,n,r,i){let a=e[t];return`${G(e[t].content)}\n`},X.fence=function(e,t,n,r,i){let a=e[t],o=a.info?W(a.info).trim():``,s=``,c=``;if(o){let e=o.split(/(\s+)/g);s=e[0],c=e.slice(2).join(``)}let l;if(l=n.highlight&&n.highlight(a.content,s,c)||G(a.content),l.indexOf(`${l}\n`}return`
${l}
\n`},X.image=function(e,t,n,r,i){let a=e[t];return a.attrs[a.attrIndex(`alt`)][1]=i.renderInlineAsText(a.children,n,r),i.renderToken(e,t,n)},X.hardbreak=function(e,t,n){return n.xhtmlOut?`
`:`
diff --git a/backend/internal/server/ui_dist/assets/Activity-C3gP7_TB.js b/backend/internal/server/ui_dist/assets/Activity-6I8QCc07.js similarity index 98% rename from backend/internal/server/ui_dist/assets/Activity-C3gP7_TB.js rename to backend/internal/server/ui_dist/assets/Activity-6I8QCc07.js index 81fa22c5..b04419ef 100644 --- a/backend/internal/server/ui_dist/assets/Activity-C3gP7_TB.js +++ b/backend/internal/server/ui_dist/assets/Activity-6I8QCc07.js @@ -1 +1 @@ -import{D as e,E as t,F as n,G as r,I as ee,S as te,T as ne,Z as i,c as a,d as o,gt as re,h as s,k as c,l,m as u,r as d,s as f,u as p,vt as m,w as ie}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as ae}from"./chevron-right-BufLmSBw.js";import{Lt as oe,N as se,_t as h,jt as g,r as ce,xt as le}from"./index-C11HmDXN.js";import{t as ue}from"./Drawer-ZggviMzu.js";import{t as _}from"./StatusBadge-BOCx3ovI.js";var de=g(`chevron-left`,[[`path`,{d:`m15 18-6-6 6-6`,key:`1wnfg3`}]]),v={__name:`SourceTag`,props:{source:{type:String,default:``}},setup(t){let n=t,r=f(()=>{switch(n.source){case`web`:return`text-indigo-300 border-indigo-900/40`;case`api`:return`text-sky-300 border-sky-900/40`;case`mcp`:return`text-violet-300 border-violet-900/40`;case`sdk`:return`text-teal-300 border-teal-900/40`;case`webhook`:return`text-amber-300 border-amber-900/40`;case`cron`:return`text-emerald-300 border-emerald-900/40`;case`internal`:return`text-foreground-muted border-border`;default:return`text-foreground-muted border-border`}});return(n,ee)=>(e(),o(`span`,{class:re([`inline-flex items-center px-2 py-0.5 rounded text-xs border bg-background font-mono uppercase tracking-wide`,r.value])},m(t.source||i(`—`)),3))}},fe={class:`space-y-6`},pe={class:`flex flex-col sm:flex-row sm:items-center gap-2 sm:flex-wrap`},me={class:`relative w-full sm:flex-1 sm:min-w-[260px] sm:max-w-[420px]`},he={class:`flex items-center gap-2 sm:flex-wrap overflow-x-auto sm:overflow-visible scrollable snap-x min-w-0`},ge={key:0,class:`ml-1 opacity-60 tabular-nums`},_e={class:`bg-background border border-border rounded-lg overflow-x-auto`},ve={class:`sm:hidden divide-y divide-border`},ye=[`onClick`],be={class:`flex items-start justify-between gap-2`},xe={class:`min-w-0 flex-1`},Se={class:`flex items-center gap-2 flex-wrap`},Ce={class:`mt-1 text-xs font-mono text-white break-all`},we={key:0,class:`mt-1 text-[11px] text-foreground-muted break-words`},Te={class:`mt-1 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-[11px] text-foreground-muted font-mono`},Ee={key:0},De={key:1,class:`break-all`},Oe={key:0,class:`px-6 py-12 text-center text-sm text-foreground-muted`},y={class:`hidden sm:table w-full text-sm text-left`},b={class:`divide-y divide-border`},x=[`onClick`],S={class:`px-4 py-2.5 font-mono text-xs text-foreground-muted`},C={class:`px-4 py-2.5`},w={class:`px-4 py-2.5 hidden md:table-cell`},T={class:`text-xs text-white truncate max-w-[200px]`},ke={key:0,class:`text-[10px] text-foreground-muted/70 font-mono truncate`},Ae={class:`px-4 py-2.5 text-xs font-mono text-foreground-muted hidden sm:table-cell`},je={class:`px-4 py-2.5 text-xs font-mono text-white truncate max-w-[440px]`},Me={class:`px-4 py-2.5 hidden sm:table-cell`},Ne={key:1,class:`text-foreground-muted text-xs`},Pe={class:`px-4 py-2.5 text-xs font-mono text-foreground-muted hidden lg:table-cell`},Fe={class:`px-4 py-2.5 text-xs text-foreground-muted truncate max-w-[280px] hidden xl:table-cell`},Ie={key:0},Le={key:0,class:`flex items-center justify-between text-xs`},Re={class:`text-foreground-muted`},ze={class:`flex items-center gap-1`},Be={key:0,class:`p-5 space-y-5 text-sm`},Ve={class:`grid grid-cols-2 gap-3`},He={class:`bg-surface border border-border rounded p-3 min-w-0`},Ue={class:`text-xs text-white font-mono truncate`},We={class:`bg-surface border border-border rounded p-3 min-w-0`},Ge={class:`bg-surface border border-border rounded p-3 min-w-0`},Ke={class:`text-sm text-white truncate`},qe={key:0,class:`text-[11px] text-foreground-muted font-mono truncate mt-0.5`},Je={class:`bg-surface border border-border rounded p-3 min-w-0`},Ye={class:`flex items-center gap-2`},Xe={key:1,class:`text-foreground-muted text-xs`},Ze={key:2,class:`text-xs text-foreground-muted font-mono`},Qe={class:`bg-surface border border-border rounded p-3 min-w-0`},$e={class:`text-xs text-white font-mono truncate`},et={class:`bg-surface border border-border rounded p-3 min-w-0`},tt={class:`text-xs text-white font-mono`},nt={class:`bg-surface border border-border rounded p-3 text-xs text-white font-mono whitespace-pre-wrap break-all`},rt={class:`text-foreground break-words`},it={key:0},at={class:`bg-surface border border-border rounded p-3 text-xs text-foreground-muted font-mono whitespace-pre-wrap break-all`},ot={key:1},st={class:`bg-surface border border-border rounded p-3 text-xs text-foreground font-mono overflow-auto max-h-72 whitespace-pre-wrap break-words`},E=100,D=200,O=Object.assign({name:`ActivityView`},{__name:`Activity`,setup(re){let g=ce(),O=[{label:`All`,value:``},{label:`Web`,value:`web`},{label:`API`,value:`api`},{label:`MCP`,value:`mcp`},{label:`SDK`,value:`sdk`},{label:`Webhook`,value:`webhook`},{label:`Internal`,value:`internal`}],ct=[{label:`All`,value:``},{label:`Success`,value:`ok`},{label:`Errors`,value:`err`}],lt=[{label:`5m`,value:`5m`},{label:`1h`,value:`1h`},{label:`24h`,value:`24h`},{label:`7d`,value:`7d`}],k=r({q:``,source:``,statusBucket:``,range:`24h`}),A=r([]),j=r([]),M=r(!1),N=r(null),P=r(0),F=r(!1),I=r(1),L=r([{since:void 0,until:void 0}]),R=f(()=>I.value===1?[...j.value,...A.value]:A.value),z=f(()=>{let e={};for(let t of R.value)e[t.source]=(e[t.source]||0)+1;return e[``]=R.value.length,e}),B=f(()=>Math.max(L.value.length,I.value)),ut=f(()=>{let e=B.value,t=I.value;return[...new Set([1,e,t-1,t,t+1])].filter(t=>t>=1&&t<=e).sort((e,t)=>e-t)}),dt=e=>{switch(e){case`5m`:return 3e5;case`1h`:return 36e5;case`24h`:return 864e5;case`7d`:return 6048e5;default:return 0}},ft=(e={})=>{let t={limit:E};k.value.source&&(t.source=k.value.source),k.value.statusBucket===`err`&&(t.status_min=400),k.value.q&&(t.q=k.value.q);let n=dt(k.value.range);return n&&(t.since=Date.now()-n),Object.assign(t,e)},V=async e=>{if(e<1||e>L.value.length+1)return;let t=L.value[e-1]?.cursor,n=await se(ft(t?{cursor:t}:{}));A.value=n.data?.rows||[];let r=n.data?.next_cursor||0;F.value=r>0,L.value[e-1]||(L.value[e-1]={}),L.value[e-1].cursor=t,r?(L.value[e]||(L.value[e]={}),L.value[e].cursor=r):L.value=L.value.slice(0,e),P.value=(e-1)*E+A.value.length,I.value=e,e>1&&(j.value=[])},H=async()=>{L.value=[{since:void 0,until:void 0}],j.value=[],I.value=1,await V(1)},U=null,pt=e=>{if(k.value.source&&e.source!==k.value.source||k.value.statusBucket===`err`&&(e.status||0)<400)return!1;if(k.value.q){let t=k.value.q.toLowerCase();if(!(e.path+` `+e.summary+` `+e.actor_label).toLowerCase().includes(t))return!1}return!0},W=e=>{pt(e)&&I.value===1&&(j.value.unshift(e),j.value.length>D&&(j.value=j.value.slice(0,D)))},G=null,mt=()=>{clearTimeout(G),G=setTimeout(H,250)},K=e=>{N.value=e,M.value=!0},ht=f(()=>N.value?N.value.summary||N.value.method+` `+N.value.path:`Activity`),q=f(()=>{if(!N.value?.metadata)return``;try{return JSON.stringify(JSON.parse(N.value.metadata),null,2)}catch{return N.value.metadata}}),J=e=>e?new Date(e).toLocaleTimeString([],{hour:`2-digit`,minute:`2-digit`,second:`2-digit`,hour12:!1}):`—`,gt=e=>e?new Date(e).toLocaleString():`—`,Y=e=>e==null?`—`:e<1?`<1ms`:e<1e3?e+`ms`:(e/1e3).toFixed(2)+`s`,X=e=>e?e>=500?`error`:e>=400?`failed`:e>=200?`success`:`pending`:``,Z=e=>e.id?`db-${e.id}`:`live-${e.ts}-${e.request_id}-${e.path}`,Q=()=>{U||(U=g.subscribe(`activity`,W),g.connect())},$=()=>{U&&=(U(),null)};return ne(()=>{Q(),H()}),t($),te(()=>{Q(),H()}),ie($),(t,r)=>(e(),o(`div`,fe,[r[20]||=a(`div`,null,[a(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` Activity `),a(`p`,{class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},` Every API and MCP request. `)],-1),a(`div`,pe,[a(`div`,me,[s(i(le),{class:`w-3.5 h-3.5 absolute left-2.5 top-1/2 -translate-y-1/2 text-foreground-muted/60 pointer-events-none`}),ee(a(`input`,{"onUpdate:modelValue":r[0]||=e=>k.value.q=e,"aria-label":`Search activity by path, summary, or actor`,placeholder:`Search path, summary, actor…`,class:`w-full bg-background border border-border rounded-md pl-8 pr-3 py-1.5 text-base sm:text-xs text-foreground placeholder-foreground-muted/60 focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,onInput:mt},null,544),[[oe,k.value.q]])]),a(`div`,he,[(e(),o(d,null,c(O,t=>s(h,{key:t.value,variant:`chip`,size:`xs`,active:k.value.source===t.value,class:`shrink-0 snap-start`,onClick:e=>{k.value.source=t.value,H()}},{default:n(()=>[u(m(t.label)+` `,1),z.value[t.value]!=null&&t.value!==``?(e(),o(`span`,ge,m(z.value[t.value]),1)):p(``,!0)]),_:2},1032,[`active`,`onClick`])),64)),r[4]||=a(`span`,{class:`text-foreground-muted/40 shrink-0`},`·`,-1),(e(),o(d,null,c(ct,e=>s(h,{key:e.value,variant:`chip`,size:`xs`,active:k.value.statusBucket===e.value,class:`shrink-0 snap-start`,onClick:t=>{k.value.statusBucket=e.value,H()}},{default:n(()=>[u(m(e.label),1)]),_:2},1032,[`active`,`onClick`])),64)),r[5]||=a(`span`,{class:`text-foreground-muted/40 shrink-0`},`·`,-1),(e(),o(d,null,c(lt,e=>s(h,{key:e.value,variant:`chip`,size:`xs`,active:k.value.range===e.value,class:`shrink-0 snap-start`,onClick:t=>{k.value.range=e.value,H()}},{default:n(()=>[u(m(e.label),1)]),_:2},1032,[`active`,`onClick`])),64))])]),a(`div`,_e,[a(`ul`,ve,[(e(!0),o(d,null,c(R.value,t=>(e(),o(`li`,{key:Z(t),class:`px-4 py-3 cursor-pointer hover:bg-surface-hover transition-colors`,onClick:e=>K(t)},[a(`div`,be,[a(`div`,xe,[a(`div`,Se,[s(v,{source:t.source},null,8,[`source`]),t.status?(e(),l(_,{key:0,status:X(t.status)},null,8,[`status`])):p(``,!0)]),a(`div`,Ce,m(t.method?t.method+` `:``)+m(t.path||i(`—`)),1),t.summary?(e(),o(`div`,we,m(t.summary),1)):p(``,!0),a(`div`,Te,[a(`span`,null,m(J(t.ts)),1),t.duration_ms==null?p(``,!0):(e(),o(`span`,Ee,m(Y(t.duration_ms)),1)),t.actor_label||t.actor_id?(e(),o(`span`,De,m(t.actor_label||t.actor_id),1)):p(``,!0)])])])],8,ye))),128)),R.value.length?p(``,!0):(e(),o(`li`,Oe,` Requests and tool calls appear here. `))]),a(`table`,y,[r[7]||=a(`thead`,{class:`text-xs text-foreground-muted uppercase bg-surface border-b border-border`},[a(`tr`,null,[a(`th`,{class:`px-4 py-3 w-32`},` Time `),a(`th`,{class:`px-4 py-3 w-24`},` Source `),a(`th`,{class:`px-4 py-3 w-40 hidden md:table-cell`},` Actor `),a(`th`,{class:`px-4 py-3 w-20 hidden sm:table-cell`},` Method `),a(`th`,{class:`px-4 py-3`},` Path / Tool `),a(`th`,{class:`px-4 py-3 w-16 hidden sm:table-cell`},` Status `),a(`th`,{class:`px-4 py-3 w-20 hidden lg:table-cell`},` Duration `),a(`th`,{class:`px-4 py-3 hidden xl:table-cell`},` Summary `)])],-1),a(`tbody`,b,[(e(!0),o(d,null,c(R.value,t=>(e(),o(`tr`,{key:Z(t),class:`hover:bg-surface-hover cursor-pointer transition-colors`,onClick:e=>K(t)},[a(`td`,S,m(J(t.ts)),1),a(`td`,C,[s(v,{source:t.source},null,8,[`source`])]),a(`td`,w,[a(`div`,T,m(t.actor_label||t.actor_id||i(`—`)),1),t.actor_label&&t.actor_id&&t.actor_label!==t.actor_id?(e(),o(`div`,ke,m(t.actor_id),1)):p(``,!0)]),a(`td`,Ae,m(t.method||i(`—`)),1),a(`td`,je,m(t.path||i(`—`)),1),a(`td`,Me,[t.status?(e(),l(_,{key:0,status:X(t.status)},null,8,[`status`])):(e(),o(`span`,Ne,m(i(`—`)),1))]),a(`td`,Pe,m(Y(t.duration_ms)),1),a(`td`,Fe,m(t.summary),1)],8,x))),128)),R.value.length?p(``,!0):(e(),o(`tr`,Ie,[...r[6]||=[a(`td`,{colspan:`8`,class:`px-4 py-12 text-center text-foreground-muted text-sm`},` Requests and tool calls appear here. `,-1)]]))])])]),B.value>1?(e(),o(`div`,Le,[a(`div`,Re,` Page `+m(I.value)+` of `+m(B.value)+` · `+m(P.value)+m(F.value?`+`:``)+` rows `,1),a(`div`,ze,[s(h,{variant:`secondary`,size:`xs`,disabled:I.value<=1,onClick:r[1]||=e=>V(I.value-1)},{default:n(()=>[s(i(de),{class:`w-3.5 h-3.5`}),r[8]||=u(` Prev `,-1)]),_:1},8,[`disabled`]),(e(!0),o(d,null,c(ut.value,t=>(e(),l(h,{key:t,variant:t===I.value?`primary`:`secondary`,size:`xs`,onClick:e=>V(t)},{default:n(()=>[u(m(t),1)]),_:2},1032,[`variant`,`onClick`]))),128)),s(h,{variant:`secondary`,size:`xs`,disabled:I.value>=B.value&&!F.value,onClick:r[2]||=e=>V(I.value+1)},{default:n(()=>[r[9]||=u(` Next `,-1),s(i(ae),{class:`w-3.5 h-3.5`})]),_:1},8,[`disabled`])])])):p(``,!0),s(ue,{modelValue:M.value,"onUpdate:modelValue":r[3]||=e=>M.value=e,title:ht.value,width:`640px`},{default:n(()=>[N.value?(e(),o(`div`,Be,[a(`div`,Ve,[a(`div`,He,[r[10]||=a(`div`,{class:`text-[10px] uppercase tracking-wider text-foreground-muted mb-1`},` Time `,-1),a(`div`,Ue,m(gt(N.value.ts)),1)]),a(`div`,We,[r[11]||=a(`div`,{class:`text-[10px] uppercase tracking-wider text-foreground-muted mb-1`},` Source `,-1),s(v,{source:N.value.source},null,8,[`source`])]),a(`div`,Ge,[r[12]||=a(`div`,{class:`text-[10px] uppercase tracking-wider text-foreground-muted mb-1`},` Actor `,-1),a(`div`,Ke,m(N.value.actor_label||i(`—`)),1),N.value.actor_id?(e(),o(`div`,qe,m(N.value.actor_id),1)):p(``,!0)]),a(`div`,Je,[r[13]||=a(`div`,{class:`text-[10px] uppercase tracking-wider text-foreground-muted mb-1`},` Status `,-1),a(`div`,Ye,[N.value.status?(e(),l(_,{key:0,status:X(N.value.status)},null,8,[`status`])):(e(),o(`span`,Xe,m(i(`—`)),1)),N.value.status?(e(),o(`span`,Ze,`HTTP `+m(N.value.status),1)):p(``,!0)])]),a(`div`,Qe,[r[14]||=a(`div`,{class:`text-[10px] uppercase tracking-wider text-foreground-muted mb-1`},` Method `,-1),a(`div`,$e,m(N.value.method||i(`—`)),1)]),a(`div`,et,[r[15]||=a(`div`,{class:`text-[10px] uppercase tracking-wider text-foreground-muted mb-1`},` Duration `,-1),a(`div`,tt,m(Y(N.value.duration_ms)),1)])]),a(`div`,null,[r[16]||=a(`h3`,{class:`text-xs uppercase tracking-wider text-foreground-muted mb-2`},` Path / Tool `,-1),a(`pre`,nt,m(N.value.path||i(`—`)),1)]),a(`div`,null,[r[17]||=a(`h3`,{class:`text-xs uppercase tracking-wider text-foreground-muted mb-2`},` Summary `,-1),a(`div`,rt,m(N.value.summary||i(`—`)),1)]),N.value.request_id?(e(),o(`div`,it,[r[18]||=a(`h3`,{class:`text-xs uppercase tracking-wider text-foreground-muted mb-2`},` Request ID `,-1),a(`pre`,at,m(N.value.request_id),1)])):p(``,!0),q.value?(e(),o(`div`,ot,[r[19]||=a(`h3`,{class:`text-xs uppercase tracking-wider text-foreground-muted mb-2`},` Metadata `,-1),a(`pre`,st,m(q.value),1)])):p(``,!0)])):p(``,!0)]),_:1},8,[`modelValue`,`title`])]))}});export{O as default}; \ No newline at end of file +import{D as e,E as t,F as n,G as r,I as ee,S as te,T as ne,Z as i,c as a,d as o,gt as re,h as s,k as c,l,m as u,r as d,s as f,u as p,vt as m,w as ie}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as ae}from"./chevron-right-CRxFsA9Y.js";import{Lt as oe,N as se,_t as h,jt as g,r as ce,xt as le}from"./index-DTqMKlE1.js";import{t as ue}from"./Drawer-B98TBytl.js";import{t as _}from"./StatusBadge-Baoe7YAb.js";var de=g(`chevron-left`,[[`path`,{d:`m15 18-6-6 6-6`,key:`1wnfg3`}]]),v={__name:`SourceTag`,props:{source:{type:String,default:``}},setup(t){let n=t,r=f(()=>{switch(n.source){case`web`:return`text-indigo-300 border-indigo-900/40`;case`api`:return`text-sky-300 border-sky-900/40`;case`mcp`:return`text-violet-300 border-violet-900/40`;case`sdk`:return`text-teal-300 border-teal-900/40`;case`webhook`:return`text-amber-300 border-amber-900/40`;case`cron`:return`text-emerald-300 border-emerald-900/40`;case`internal`:return`text-foreground-muted border-border`;default:return`text-foreground-muted border-border`}});return(n,ee)=>(e(),o(`span`,{class:re([`inline-flex items-center px-2 py-0.5 rounded text-xs border bg-background font-mono uppercase tracking-wide`,r.value])},m(t.source||i(`—`)),3))}},fe={class:`space-y-6`},pe={class:`flex flex-col sm:flex-row sm:items-center gap-2 sm:flex-wrap`},me={class:`relative w-full sm:flex-1 sm:min-w-[260px] sm:max-w-[420px]`},he={class:`flex items-center gap-2 sm:flex-wrap overflow-x-auto sm:overflow-visible scrollable snap-x min-w-0`},ge={key:0,class:`ml-1 opacity-60 tabular-nums`},_e={class:`bg-background border border-border rounded-lg overflow-x-auto`},ve={class:`sm:hidden divide-y divide-border`},ye=[`onClick`],be={class:`flex items-start justify-between gap-2`},xe={class:`min-w-0 flex-1`},Se={class:`flex items-center gap-2 flex-wrap`},Ce={class:`mt-1 text-xs font-mono text-white break-all`},we={key:0,class:`mt-1 text-[11px] text-foreground-muted break-words`},Te={class:`mt-1 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-[11px] text-foreground-muted font-mono`},Ee={key:0},De={key:1,class:`break-all`},Oe={key:0,class:`px-6 py-12 text-center text-sm text-foreground-muted`},y={class:`hidden sm:table w-full text-sm text-left`},b={class:`divide-y divide-border`},x=[`onClick`],S={class:`px-4 py-2.5 font-mono text-xs text-foreground-muted`},C={class:`px-4 py-2.5`},w={class:`px-4 py-2.5 hidden md:table-cell`},T={class:`text-xs text-white truncate max-w-[200px]`},ke={key:0,class:`text-[10px] text-foreground-muted/70 font-mono truncate`},Ae={class:`px-4 py-2.5 text-xs font-mono text-foreground-muted hidden sm:table-cell`},je={class:`px-4 py-2.5 text-xs font-mono text-white truncate max-w-[440px]`},Me={class:`px-4 py-2.5 hidden sm:table-cell`},Ne={key:1,class:`text-foreground-muted text-xs`},Pe={class:`px-4 py-2.5 text-xs font-mono text-foreground-muted hidden lg:table-cell`},Fe={class:`px-4 py-2.5 text-xs text-foreground-muted truncate max-w-[280px] hidden xl:table-cell`},Ie={key:0},Le={key:0,class:`flex items-center justify-between text-xs`},Re={class:`text-foreground-muted`},ze={class:`flex items-center gap-1`},Be={key:0,class:`p-5 space-y-5 text-sm`},Ve={class:`grid grid-cols-2 gap-3`},He={class:`bg-surface border border-border rounded p-3 min-w-0`},Ue={class:`text-xs text-white font-mono truncate`},We={class:`bg-surface border border-border rounded p-3 min-w-0`},Ge={class:`bg-surface border border-border rounded p-3 min-w-0`},Ke={class:`text-sm text-white truncate`},qe={key:0,class:`text-[11px] text-foreground-muted font-mono truncate mt-0.5`},Je={class:`bg-surface border border-border rounded p-3 min-w-0`},Ye={class:`flex items-center gap-2`},Xe={key:1,class:`text-foreground-muted text-xs`},Ze={key:2,class:`text-xs text-foreground-muted font-mono`},Qe={class:`bg-surface border border-border rounded p-3 min-w-0`},$e={class:`text-xs text-white font-mono truncate`},et={class:`bg-surface border border-border rounded p-3 min-w-0`},tt={class:`text-xs text-white font-mono`},nt={class:`bg-surface border border-border rounded p-3 text-xs text-white font-mono whitespace-pre-wrap break-all`},rt={class:`text-foreground break-words`},it={key:0},at={class:`bg-surface border border-border rounded p-3 text-xs text-foreground-muted font-mono whitespace-pre-wrap break-all`},ot={key:1},st={class:`bg-surface border border-border rounded p-3 text-xs text-foreground font-mono overflow-auto max-h-72 whitespace-pre-wrap break-words`},E=100,D=200,O=Object.assign({name:`ActivityView`},{__name:`Activity`,setup(re){let g=ce(),O=[{label:`All`,value:``},{label:`Web`,value:`web`},{label:`API`,value:`api`},{label:`MCP`,value:`mcp`},{label:`SDK`,value:`sdk`},{label:`Webhook`,value:`webhook`},{label:`Internal`,value:`internal`}],ct=[{label:`All`,value:``},{label:`Success`,value:`ok`},{label:`Errors`,value:`err`}],lt=[{label:`5m`,value:`5m`},{label:`1h`,value:`1h`},{label:`24h`,value:`24h`},{label:`7d`,value:`7d`}],k=r({q:``,source:``,statusBucket:``,range:`24h`}),A=r([]),j=r([]),M=r(!1),N=r(null),P=r(0),F=r(!1),I=r(1),L=r([{since:void 0,until:void 0}]),R=f(()=>I.value===1?[...j.value,...A.value]:A.value),z=f(()=>{let e={};for(let t of R.value)e[t.source]=(e[t.source]||0)+1;return e[``]=R.value.length,e}),B=f(()=>Math.max(L.value.length,I.value)),ut=f(()=>{let e=B.value,t=I.value;return[...new Set([1,e,t-1,t,t+1])].filter(t=>t>=1&&t<=e).sort((e,t)=>e-t)}),dt=e=>{switch(e){case`5m`:return 3e5;case`1h`:return 36e5;case`24h`:return 864e5;case`7d`:return 6048e5;default:return 0}},ft=(e={})=>{let t={limit:E};k.value.source&&(t.source=k.value.source),k.value.statusBucket===`err`&&(t.status_min=400),k.value.q&&(t.q=k.value.q);let n=dt(k.value.range);return n&&(t.since=Date.now()-n),Object.assign(t,e)},V=async e=>{if(e<1||e>L.value.length+1)return;let t=L.value[e-1]?.cursor,n=await se(ft(t?{cursor:t}:{}));A.value=n.data?.rows||[];let r=n.data?.next_cursor||0;F.value=r>0,L.value[e-1]||(L.value[e-1]={}),L.value[e-1].cursor=t,r?(L.value[e]||(L.value[e]={}),L.value[e].cursor=r):L.value=L.value.slice(0,e),P.value=(e-1)*E+A.value.length,I.value=e,e>1&&(j.value=[])},H=async()=>{L.value=[{since:void 0,until:void 0}],j.value=[],I.value=1,await V(1)},U=null,pt=e=>{if(k.value.source&&e.source!==k.value.source||k.value.statusBucket===`err`&&(e.status||0)<400)return!1;if(k.value.q){let t=k.value.q.toLowerCase();if(!(e.path+` `+e.summary+` `+e.actor_label).toLowerCase().includes(t))return!1}return!0},W=e=>{pt(e)&&I.value===1&&(j.value.unshift(e),j.value.length>D&&(j.value=j.value.slice(0,D)))},G=null,mt=()=>{clearTimeout(G),G=setTimeout(H,250)},K=e=>{N.value=e,M.value=!0},ht=f(()=>N.value?N.value.summary||N.value.method+` `+N.value.path:`Activity`),q=f(()=>{if(!N.value?.metadata)return``;try{return JSON.stringify(JSON.parse(N.value.metadata),null,2)}catch{return N.value.metadata}}),J=e=>e?new Date(e).toLocaleTimeString([],{hour:`2-digit`,minute:`2-digit`,second:`2-digit`,hour12:!1}):`—`,gt=e=>e?new Date(e).toLocaleString():`—`,Y=e=>e==null?`—`:e<1?`<1ms`:e<1e3?e+`ms`:(e/1e3).toFixed(2)+`s`,X=e=>e?e>=500?`error`:e>=400?`failed`:e>=200?`success`:`pending`:``,Z=e=>e.id?`db-${e.id}`:`live-${e.ts}-${e.request_id}-${e.path}`,Q=()=>{U||(U=g.subscribe(`activity`,W),g.connect())},$=()=>{U&&=(U(),null)};return ne(()=>{Q(),H()}),t($),te(()=>{Q(),H()}),ie($),(t,r)=>(e(),o(`div`,fe,[r[20]||=a(`div`,null,[a(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` Activity `),a(`p`,{class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},` Every API and MCP request. `)],-1),a(`div`,pe,[a(`div`,me,[s(i(le),{class:`w-3.5 h-3.5 absolute left-2.5 top-1/2 -translate-y-1/2 text-foreground-muted/60 pointer-events-none`}),ee(a(`input`,{"onUpdate:modelValue":r[0]||=e=>k.value.q=e,"aria-label":`Search activity by path, summary, or actor`,placeholder:`Search path, summary, actor…`,class:`w-full bg-background border border-border rounded-md pl-8 pr-3 py-1.5 text-base sm:text-xs text-foreground placeholder-foreground-muted/60 focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,onInput:mt},null,544),[[oe,k.value.q]])]),a(`div`,he,[(e(),o(d,null,c(O,t=>s(h,{key:t.value,variant:`chip`,size:`xs`,active:k.value.source===t.value,class:`shrink-0 snap-start`,onClick:e=>{k.value.source=t.value,H()}},{default:n(()=>[u(m(t.label)+` `,1),z.value[t.value]!=null&&t.value!==``?(e(),o(`span`,ge,m(z.value[t.value]),1)):p(``,!0)]),_:2},1032,[`active`,`onClick`])),64)),r[4]||=a(`span`,{class:`text-foreground-muted/40 shrink-0`},`·`,-1),(e(),o(d,null,c(ct,e=>s(h,{key:e.value,variant:`chip`,size:`xs`,active:k.value.statusBucket===e.value,class:`shrink-0 snap-start`,onClick:t=>{k.value.statusBucket=e.value,H()}},{default:n(()=>[u(m(e.label),1)]),_:2},1032,[`active`,`onClick`])),64)),r[5]||=a(`span`,{class:`text-foreground-muted/40 shrink-0`},`·`,-1),(e(),o(d,null,c(lt,e=>s(h,{key:e.value,variant:`chip`,size:`xs`,active:k.value.range===e.value,class:`shrink-0 snap-start`,onClick:t=>{k.value.range=e.value,H()}},{default:n(()=>[u(m(e.label),1)]),_:2},1032,[`active`,`onClick`])),64))])]),a(`div`,_e,[a(`ul`,ve,[(e(!0),o(d,null,c(R.value,t=>(e(),o(`li`,{key:Z(t),class:`px-4 py-3 cursor-pointer hover:bg-surface-hover transition-colors`,onClick:e=>K(t)},[a(`div`,be,[a(`div`,xe,[a(`div`,Se,[s(v,{source:t.source},null,8,[`source`]),t.status?(e(),l(_,{key:0,status:X(t.status)},null,8,[`status`])):p(``,!0)]),a(`div`,Ce,m(t.method?t.method+` `:``)+m(t.path||i(`—`)),1),t.summary?(e(),o(`div`,we,m(t.summary),1)):p(``,!0),a(`div`,Te,[a(`span`,null,m(J(t.ts)),1),t.duration_ms==null?p(``,!0):(e(),o(`span`,Ee,m(Y(t.duration_ms)),1)),t.actor_label||t.actor_id?(e(),o(`span`,De,m(t.actor_label||t.actor_id),1)):p(``,!0)])])])],8,ye))),128)),R.value.length?p(``,!0):(e(),o(`li`,Oe,` Requests and tool calls appear here. `))]),a(`table`,y,[r[7]||=a(`thead`,{class:`text-xs text-foreground-muted uppercase bg-surface border-b border-border`},[a(`tr`,null,[a(`th`,{class:`px-4 py-3 w-32`},` Time `),a(`th`,{class:`px-4 py-3 w-24`},` Source `),a(`th`,{class:`px-4 py-3 w-40 hidden md:table-cell`},` Actor `),a(`th`,{class:`px-4 py-3 w-20 hidden sm:table-cell`},` Method `),a(`th`,{class:`px-4 py-3`},` Path / Tool `),a(`th`,{class:`px-4 py-3 w-16 hidden sm:table-cell`},` Status `),a(`th`,{class:`px-4 py-3 w-20 hidden lg:table-cell`},` Duration `),a(`th`,{class:`px-4 py-3 hidden xl:table-cell`},` Summary `)])],-1),a(`tbody`,b,[(e(!0),o(d,null,c(R.value,t=>(e(),o(`tr`,{key:Z(t),class:`hover:bg-surface-hover cursor-pointer transition-colors`,onClick:e=>K(t)},[a(`td`,S,m(J(t.ts)),1),a(`td`,C,[s(v,{source:t.source},null,8,[`source`])]),a(`td`,w,[a(`div`,T,m(t.actor_label||t.actor_id||i(`—`)),1),t.actor_label&&t.actor_id&&t.actor_label!==t.actor_id?(e(),o(`div`,ke,m(t.actor_id),1)):p(``,!0)]),a(`td`,Ae,m(t.method||i(`—`)),1),a(`td`,je,m(t.path||i(`—`)),1),a(`td`,Me,[t.status?(e(),l(_,{key:0,status:X(t.status)},null,8,[`status`])):(e(),o(`span`,Ne,m(i(`—`)),1))]),a(`td`,Pe,m(Y(t.duration_ms)),1),a(`td`,Fe,m(t.summary),1)],8,x))),128)),R.value.length?p(``,!0):(e(),o(`tr`,Ie,[...r[6]||=[a(`td`,{colspan:`8`,class:`px-4 py-12 text-center text-foreground-muted text-sm`},` Requests and tool calls appear here. `,-1)]]))])])]),B.value>1?(e(),o(`div`,Le,[a(`div`,Re,` Page `+m(I.value)+` of `+m(B.value)+` · `+m(P.value)+m(F.value?`+`:``)+` rows `,1),a(`div`,ze,[s(h,{variant:`secondary`,size:`xs`,disabled:I.value<=1,onClick:r[1]||=e=>V(I.value-1)},{default:n(()=>[s(i(de),{class:`w-3.5 h-3.5`}),r[8]||=u(` Prev `,-1)]),_:1},8,[`disabled`]),(e(!0),o(d,null,c(ut.value,t=>(e(),l(h,{key:t,variant:t===I.value?`primary`:`secondary`,size:`xs`,onClick:e=>V(t)},{default:n(()=>[u(m(t),1)]),_:2},1032,[`variant`,`onClick`]))),128)),s(h,{variant:`secondary`,size:`xs`,disabled:I.value>=B.value&&!F.value,onClick:r[2]||=e=>V(I.value+1)},{default:n(()=>[r[9]||=u(` Next `,-1),s(i(ae),{class:`w-3.5 h-3.5`})]),_:1},8,[`disabled`])])])):p(``,!0),s(ue,{modelValue:M.value,"onUpdate:modelValue":r[3]||=e=>M.value=e,title:ht.value,width:`640px`},{default:n(()=>[N.value?(e(),o(`div`,Be,[a(`div`,Ve,[a(`div`,He,[r[10]||=a(`div`,{class:`text-[10px] uppercase tracking-wider text-foreground-muted mb-1`},` Time `,-1),a(`div`,Ue,m(gt(N.value.ts)),1)]),a(`div`,We,[r[11]||=a(`div`,{class:`text-[10px] uppercase tracking-wider text-foreground-muted mb-1`},` Source `,-1),s(v,{source:N.value.source},null,8,[`source`])]),a(`div`,Ge,[r[12]||=a(`div`,{class:`text-[10px] uppercase tracking-wider text-foreground-muted mb-1`},` Actor `,-1),a(`div`,Ke,m(N.value.actor_label||i(`—`)),1),N.value.actor_id?(e(),o(`div`,qe,m(N.value.actor_id),1)):p(``,!0)]),a(`div`,Je,[r[13]||=a(`div`,{class:`text-[10px] uppercase tracking-wider text-foreground-muted mb-1`},` Status `,-1),a(`div`,Ye,[N.value.status?(e(),l(_,{key:0,status:X(N.value.status)},null,8,[`status`])):(e(),o(`span`,Xe,m(i(`—`)),1)),N.value.status?(e(),o(`span`,Ze,`HTTP `+m(N.value.status),1)):p(``,!0)])]),a(`div`,Qe,[r[14]||=a(`div`,{class:`text-[10px] uppercase tracking-wider text-foreground-muted mb-1`},` Method `,-1),a(`div`,$e,m(N.value.method||i(`—`)),1)]),a(`div`,et,[r[15]||=a(`div`,{class:`text-[10px] uppercase tracking-wider text-foreground-muted mb-1`},` Duration `,-1),a(`div`,tt,m(Y(N.value.duration_ms)),1)])]),a(`div`,null,[r[16]||=a(`h3`,{class:`text-xs uppercase tracking-wider text-foreground-muted mb-2`},` Path / Tool `,-1),a(`pre`,nt,m(N.value.path||i(`—`)),1)]),a(`div`,null,[r[17]||=a(`h3`,{class:`text-xs uppercase tracking-wider text-foreground-muted mb-2`},` Summary `,-1),a(`div`,rt,m(N.value.summary||i(`—`)),1)]),N.value.request_id?(e(),o(`div`,it,[r[18]||=a(`h3`,{class:`text-xs uppercase tracking-wider text-foreground-muted mb-2`},` Request ID `,-1),a(`pre`,at,m(N.value.request_id),1)])):p(``,!0),q.value?(e(),o(`div`,ot,[r[19]||=a(`h3`,{class:`text-xs uppercase tracking-wider text-foreground-muted mb-2`},` Metadata `,-1),a(`pre`,st,m(q.value),1)])):p(``,!0)])):p(``,!0)]),_:1},8,[`modelValue`,`title`])]))}});export{O as default}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/ApiKeys-C9fpYpoP.js b/backend/internal/server/ui_dist/assets/ApiKeys-B5PU7HpA.js similarity index 96% rename from backend/internal/server/ui_dist/assets/ApiKeys-C9fpYpoP.js rename to backend/internal/server/ui_dist/assets/ApiKeys-B5PU7HpA.js index 85be60fe..41f4d07b 100644 --- a/backend/internal/server/ui_dist/assets/ApiKeys-C9fpYpoP.js +++ b/backend/internal/server/ui_dist/assets/ApiKeys-B5PU7HpA.js @@ -1,3 +1,3 @@ -import{D as e,F as t,G as n,I as r,T as i,Z as a,c as o,d as s,h as c,k as l,l as u,m as d,r as f,u as p,vt as m}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as ee}from"./check-DQm9OMOd.js";import{t as h}from"./copy-Bt6Rv0BQ.js";import{t as g}from"./key-round-BmyLzD9o.js";import{t as _}from"./trash-2-Bj4AFUfw.js";import{It as te,Lt as ne,P as re,_t as v,d as ie,gt as ae,o as oe,vt as se}from"./index-C11HmDXN.js";import{t as ce}from"./clipboard-D_9N0yai.js";import{t as y}from"./IconButton-CsCZOqWo.js";import{n as b,t as x}from"./time-D8OmbYzY.js";var S={class:`space-y-6`},C={class:`flex items-start justify-between gap-4`},w={key:0,class:`bg-background border border-amber-700/40 rounded-lg p-4 space-y-2`},T={class:`flex items-start justify-between gap-3`},E={class:`flex items-center gap-2`},D={class:`flex-1 font-mono text-sm text-white break-all bg-surface px-3 py-2 rounded border border-border`},O={key:1,class:`bg-background border border-border rounded-lg p-5 space-y-4`},k={class:`grid grid-cols-1 md:grid-cols-2 gap-3`},A={class:`flex gap-2 pt-1`},j={class:`bg-background border border-border rounded-lg overflow-x-auto`},M={class:`sm:hidden divide-y divide-border`},N={class:`flex items-start justify-between gap-2`},P={class:`min-w-0 flex-1`},F={class:`flex items-center gap-2 flex-wrap`},I={class:`font-medium text-white truncate`},L={key:0,class:`text-[11px] font-mono text-foreground-muted bg-surface px-1.5 py-0.5 rounded`},R={class:`mt-1 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-[11px] text-foreground-muted`},z={key:0},B={key:1,class:`text-warning-fg`},V={key:2},H={key:3,class:`text-danger-fg`},U={key:4},W={key:0,class:`px-6 py-8 text-center text-sm text-foreground-muted`},le={class:`hidden sm:table w-full text-sm text-left`},ue={class:`divide-y divide-border`},de={class:`px-6 py-4 text-white font-medium`},fe={class:`px-6 py-4 text-foreground-muted font-mono text-xs hidden sm:table-cell`},pe={class:`px-6 py-4 text-foreground-muted hidden xl:table-cell`},me={class:`px-6 py-4 hidden md:table-cell`},he={key:0,class:`text-foreground-muted`},ge={key:1,class:`text-warning-fg text-xs`},_e={class:`px-6 py-4 hidden lg:table-cell`},ve={key:0,class:`text-foreground-muted`},ye={key:1,class:`text-danger-fg text-xs`},be={key:2,class:`text-foreground-muted`},xe={class:`px-6 py-4 text-right`},Se={key:0},Ce={__name:`ApiKeys`,setup(Ce){let G=ae(),K=n([]),q=n(``),J=n(!1),Y=n(!1),X=n(!1),Z=n({name:``,expiresInDays:0}),Q=async()=>{let e=await re();K.value=e.data.keys||[]},we=()=>{Z.value={name:``,expiresInDays:0},Y.value=!0},Te=()=>{Y.value=!1,Z.value={name:``,expiresInDays:0}},Ee=async()=>{X.value=!0;try{let e={name:Z.value.name.trim()};Z.value.expiresInDays>0&&(e.expires_in_days=Z.value.expiresInDays);let t=await oe(e);q.value=t.data.key,J.value=!1,Y.value=!1,await Q()}catch(e){console.error(e),G.notify({title:`Failed to create key`,message:e?.response?.data?.error?.message||`Unknown error`,danger:!0})}finally{X.value=!1}},De=async()=>{await ce(q.value)?(J.value=!0,setTimeout(()=>{J.value=!1},1500)):G.notify({title:`Copy failed`,message:`Could not copy to clipboard. Select the key manually: +import{D as e,F as t,G as n,I as r,T as i,Z as a,c as o,d as s,h as c,k as l,l as u,m as d,r as f,u as p,vt as m}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as ee}from"./check-BNre7JFR.js";import{t as h}from"./copy-BqdwwcxC.js";import{t as g}from"./key-round-BKXtbC85.js";import{t as _}from"./trash-2-DaeYqnW_.js";import{It as te,Lt as ne,P as re,_t as v,d as ie,gt as ae,o as oe,vt as se}from"./index-DTqMKlE1.js";import{t as ce}from"./clipboard-D_9N0yai.js";import{t as y}from"./IconButton-CsCZOqWo.js";import{n as b,t as x}from"./time-D8OmbYzY.js";var S={class:`space-y-6`},C={class:`flex items-start justify-between gap-4`},w={key:0,class:`bg-background border border-amber-700/40 rounded-lg p-4 space-y-2`},T={class:`flex items-start justify-between gap-3`},E={class:`flex items-center gap-2`},D={class:`flex-1 font-mono text-sm text-white break-all bg-surface px-3 py-2 rounded border border-border`},O={key:1,class:`bg-background border border-border rounded-lg p-5 space-y-4`},k={class:`grid grid-cols-1 md:grid-cols-2 gap-3`},A={class:`flex gap-2 pt-1`},j={class:`bg-background border border-border rounded-lg overflow-x-auto`},M={class:`sm:hidden divide-y divide-border`},N={class:`flex items-start justify-between gap-2`},P={class:`min-w-0 flex-1`},F={class:`flex items-center gap-2 flex-wrap`},I={class:`font-medium text-white truncate`},L={key:0,class:`text-[11px] font-mono text-foreground-muted bg-surface px-1.5 py-0.5 rounded`},R={class:`mt-1 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-[11px] text-foreground-muted`},z={key:0},B={key:1,class:`text-warning-fg`},V={key:2},H={key:3,class:`text-danger-fg`},U={key:4},W={key:0,class:`px-6 py-8 text-center text-sm text-foreground-muted`},le={class:`hidden sm:table w-full text-sm text-left`},ue={class:`divide-y divide-border`},de={class:`px-6 py-4 text-white font-medium`},fe={class:`px-6 py-4 text-foreground-muted font-mono text-xs hidden sm:table-cell`},pe={class:`px-6 py-4 text-foreground-muted hidden xl:table-cell`},me={class:`px-6 py-4 hidden md:table-cell`},he={key:0,class:`text-foreground-muted`},ge={key:1,class:`text-warning-fg text-xs`},_e={class:`px-6 py-4 hidden lg:table-cell`},ve={key:0,class:`text-foreground-muted`},ye={key:1,class:`text-danger-fg text-xs`},be={key:2,class:`text-foreground-muted`},xe={class:`px-6 py-4 text-right`},Se={key:0},Ce={__name:`ApiKeys`,setup(Ce){let G=ae(),K=n([]),q=n(``),J=n(!1),Y=n(!1),X=n(!1),Z=n({name:``,expiresInDays:0}),Q=async()=>{let e=await re();K.value=e.data.keys||[]},we=()=>{Z.value={name:``,expiresInDays:0},Y.value=!0},Te=()=>{Y.value=!1,Z.value={name:``,expiresInDays:0}},Ee=async()=>{X.value=!0;try{let e={name:Z.value.name.trim()};Z.value.expiresInDays>0&&(e.expires_in_days=Z.value.expiresInDays);let t=await oe(e);q.value=t.data.key,J.value=!1,Y.value=!1,await Q()}catch(e){console.error(e),G.notify({title:`Failed to create key`,message:e?.response?.data?.error?.message||`Unknown error`,danger:!0})}finally{X.value=!1}},De=async()=>{await ce(q.value)?(J.value=!0,setTimeout(()=>{J.value=!1},1500)):G.notify({title:`Copy failed`,message:`Could not copy to clipboard. Select the key manually: `+q.value})},$=async e=>{if(await G.ask({title:`Delete API key?`,message:`"${e.name||e.id}" will stop working immediately. This cannot be undone.`,confirmLabel:`Delete`,danger:!0}))try{await ie(e.id),await Q()}catch(e){console.error(e),G.notify({title:`Failed to delete key`,message:e?.response?.data?.error?.message||`Unknown error`,danger:!0})}},Oe=e=>new Date(e).toLocaleString();return i(Q),(n,i)=>(e(),s(`div`,S,[o(`div`,C,[i[4]||=o(`div`,null,[o(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` API Keys `),o(`p`,{class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},` Tokens for REST and MCP clients. Secrets are shown once. `)],-1),c(v,{onClick:we},{default:t(()=>[c(a(g),{class:`w-4 h-4`}),i[3]||=d(` New Key `,-1)]),_:1})]),q.value?(e(),s(`div`,w,[o(`div`,T,[i[5]||=o(`div`,null,[o(`h2`,{class:`text-xs font-bold text-amber-300 uppercase tracking-wider`},` Copy this key now `),o(`div`,{class:`text-xs text-foreground-muted mt-0.5`},` Store it securely. This secret will not be shown again. `)],-1),o(`button`,{class:`text-foreground-muted hover:text-white`,title:`Dismiss`,"aria-label":`Dismiss API key`,onClick:i[0]||=e=>q.value=``},[c(a(se),{class:`w-4 h-4`})])]),o(`div`,E,[o(`code`,D,m(q.value),1),o(`button`,{class:`px-3 py-2 rounded-md border border-border bg-surface-hover hover:bg-surface text-foreground-muted hover:text-white transition-colors flex items-center gap-1.5 text-xs`,onClick:De},[J.value?(e(),u(a(ee),{key:0,class:`w-3.5 h-3.5 text-success`})):(e(),u(a(h),{key:1,class:`w-3.5 h-3.5`})),d(` `+m(J.value?`Copied`:`Copy`),1)])])])):p(``,!0),Y.value?(e(),s(`div`,O,[i[11]||=o(`div`,{class:`text-sm font-semibold text-white`},` New API Key `,-1),o(`div`,k,[o(`div`,null,[i[6]||=o(`label`,{for:`api-key-name`,class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-1.5`},`Name`,-1),r(o(`input`,{id:`api-key-name`,"onUpdate:modelValue":i[1]||=e=>Z.value.name=e,placeholder:`e.g. ci-deployer`,class:`w-full bg-surface-hover border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:border-white`},null,512),[[ne,Z.value.name]])]),o(`div`,null,[i[8]||=o(`label`,{for:`api-key-expiry`,class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-1.5`},`Expires in`,-1),r(o(`select`,{id:`api-key-expiry`,"onUpdate:modelValue":i[2]||=e=>Z.value.expiresInDays=e,class:`w-full bg-surface-hover border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:border-white`},[...i[7]||=[o(`option`,{value:0},` Never `,-1),o(`option`,{value:1},` 1 day `,-1),o(`option`,{value:7},` 7 days `,-1),o(`option`,{value:30},` 30 days `,-1),o(`option`,{value:90},` 90 days `,-1),o(`option`,{value:365},` 1 year `,-1)]],512),[[te,Z.value.expiresInDays]])])]),o(`div`,A,[c(v,{disabled:!Z.value.name.trim()||X.value,loading:X.value,onClick:Ee},{default:t(()=>[...i[9]||=[d(` Generate Key `,-1)]]),_:1},8,[`disabled`,`loading`]),c(v,{variant:`secondary`,onClick:Te},{default:t(()=>[...i[10]||=[d(` Cancel `,-1)]]),_:1})])])):p(``,!0),o(`div`,j,[o(`ul`,M,[(e(!0),s(f,null,l(K.value,t=>(e(),s(`li`,{key:t.id,class:`px-4 py-3`},[o(`div`,N,[o(`div`,P,[o(`div`,F,[o(`span`,I,m(t.name||`Unnamed`),1),t.prefix?(e(),s(`code`,L,m(t.prefix)+`…`,1)):p(``,!0)]),o(`div`,R,[t.last_used_at?(e(),s(`span`,z,`used `+m(a(x)(t.last_used_at)),1)):(e(),s(`span`,B,`never used`)),t.expires_at?a(b)(t.expires_at)?(e(),s(`span`,H,`expired `+m(a(x)(t.expires_at)),1)):(e(),s(`span`,U,`expires `+m(a(x)(t.expires_at)),1)):(e(),s(`span`,V,`no expiry`))])]),c(y,{icon:a(_),variant:`danger`,title:`Delete key`,onClick:e=>$(t)},null,8,[`icon`,`onClick`])])]))),128)),K.value.length===0?(e(),s(`li`,W,` No API keys yet. `)):p(``,!0)]),o(`table`,le,[i[13]||=o(`thead`,{class:`text-xs text-foreground-muted uppercase bg-surface border-b border-border`},[o(`tr`,null,[o(`th`,{scope:`col`,class:`px-6 py-3 font-medium`},` Name `),o(`th`,{scope:`col`,class:`px-6 py-3 font-medium hidden sm:table-cell`},` Prefix `),o(`th`,{scope:`col`,class:`px-6 py-3 font-medium hidden xl:table-cell`},` Created `),o(`th`,{scope:`col`,class:`px-6 py-3 font-medium hidden md:table-cell`},` Last Used `),o(`th`,{scope:`col`,class:`px-6 py-3 font-medium hidden lg:table-cell`},` Expires `),o(`th`,{scope:`col`,class:`px-6 py-3 font-medium text-right`},` Actions `)])],-1),o(`tbody`,ue,[(e(!0),s(f,null,l(K.value,t=>(e(),s(`tr`,{key:t.id,class:`hover:bg-surface/50 transition-colors`},[o(`td`,de,m(t.name||`Unnamed`),1),o(`td`,fe,m(t.prefix?t.prefix+`…`:a(`—`)),1),o(`td`,pe,m(Oe(t.created_at)),1),o(`td`,me,[t.last_used_at?(e(),s(`span`,he,m(a(x)(t.last_used_at)),1)):(e(),s(`span`,ge,`Never used`))]),o(`td`,_e,[t.expires_at?a(b)(t.expires_at)?(e(),s(`span`,ye,`Expired `+m(a(x)(t.expires_at)),1)):(e(),s(`span`,be,m(a(x)(t.expires_at)),1)):(e(),s(`span`,ve,`Never`))]),o(`td`,xe,[c(y,{icon:a(_),variant:`danger`,title:`Delete key`,onClick:e=>$(t)},null,8,[`icon`,`onClick`])])]))),128)),K.value.length===0?(e(),s(`tr`,Se,[...i[12]||=[o(`td`,{colspan:`6`,class:`px-6 py-8 text-center text-foreground-muted`},` No API keys yet. `,-1)]])):p(``,!0)])])])]))}};export{Ce as default}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/Channels-D-vbrSjj.js b/backend/internal/server/ui_dist/assets/Channels-fKQl2oE8.js similarity index 98% rename from backend/internal/server/ui_dist/assets/Channels-D-vbrSjj.js rename to backend/internal/server/ui_dist/assets/Channels-fKQl2oE8.js index 4f50a99d..5b993e59 100644 --- a/backend/internal/server/ui_dist/assets/Channels-D-vbrSjj.js +++ b/backend/internal/server/ui_dist/assets/Channels-fKQl2oE8.js @@ -1 +1 @@ -import{D as e,F as t,G as n,I as r,T as i,Z as a,c as o,d as s,gt as c,h as l,k as u,l as d,m as f,r as p,s as m,u as h,vt as g}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as _}from"./check-DQm9OMOd.js";import{t as v}from"./circle-alert-BIdKIOKw.js";import{t as y}from"./copy-Bt6Rv0BQ.js";import{t as b}from"./rotate-ccw-DBtrGGiP.js";import{t as x}from"./trash-2-Bj4AFUfw.js";import{B as S,Bt as C,Ct as w,F as T,It as E,Lt as D,_t as O,f as k,gt as A,kt as j,s as ee,tt as te,vt as M,xt as N}from"./index-C11HmDXN.js";import{t as ne}from"./clipboard-D_9N0yai.js";import{t as P}from"./IconButton-CsCZOqWo.js";import{n as F,t as I}from"./time-D8OmbYzY.js";var L={class:`w-full max-w-2xl bg-background border border-border rounded-lg shadow-lg flex flex-col max-h-[80vh]`},R={class:`px-5 py-4 border-b border-border flex items-start justify-between gap-3`},z={class:`px-5 py-3 border-b border-border flex items-center gap-2`},B={class:`flex-1 overflow-y-auto`},V={key:0,class:`px-5 py-10 text-center text-xs text-foreground-muted italic`},H={key:1,class:`px-5 py-10 text-center`},U={class:`text-xs text-foreground-muted`},W={key:2,class:`divide-y divide-border`},G=[`onClick`],K=[`checked`,`onClick`],q={class:`flex-1 min-w-0`},J={class:`text-sm font-medium text-white truncate`},Y={key:0,class:`text-xs text-foreground-muted mt-0.5 line-clamp-1`},X={class:`text-[11px] text-foreground-muted font-mono shrink-0`},Z={class:`px-5 py-3 border-t border-border flex items-center justify-between gap-3`},Q={class:`text-xs text-foreground-muted tabular-nums`},re={class:`flex gap-2`},ie={__name:`FunctionPickerModal`,props:{selected:{type:Array,default:()=>[]}},emits:[`close`,`apply`],setup(d,{emit:_}){let v=d,y=_,b=n([]),x=n(!0),w=n(``),T=n(new Set(v.selected)),E=e=>{let t=new Set(T.value);t.has(e)?t.delete(e):t.add(e),T.value=t},k=m(()=>{let e=w.value.trim().toLowerCase();return e?b.value.filter(t=>t.name.toLowerCase().includes(e)||(t.description||``).toLowerCase().includes(e)||(t.runtime||``).toLowerCase().includes(e)):b.value}),A=()=>{y(`apply`,Array.from(T.value))};return i(async()=>{try{let e=await S({limit:200});b.value=e.data.functions||[]}finally{x.value=!1}}),(n,i)=>(e(),s(`div`,{class:`fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm`,onClick:i[3]||=C(e=>n.$emit(`close`),[`self`])},[o(`div`,L,[o(`div`,R,[i[4]||=o(`div`,null,[o(`div`,{class:`text-sm font-semibold text-white`},` Pick functions `),o(`div`,{class:`text-xs text-foreground-muted mt-0.5 max-w-prose leading-relaxed`},` Each selection becomes an MCP tool. Dashes become underscores. `)],-1),o(`button`,{class:`text-foreground-muted hover:text-white transition-colors`,title:`Dismiss`,"aria-label":`Close function picker`,onClick:i[0]||=e=>n.$emit(`close`)},[l(a(M),{class:`w-4 h-4`})])]),o(`div`,z,[l(a(N),{class:`w-4 h-4 text-foreground-muted shrink-0`}),r(o(`input`,{"onUpdate:modelValue":i[1]||=e=>w.value=e,"aria-label":`Search functions`,type:`text`,placeholder:`Filter by name, description, or runtime`,class:`flex-1 bg-transparent text-sm text-foreground placeholder-foreground-muted focus:outline-none`},null,512),[[D,w.value]])]),o(`div`,B,[x.value?(e(),s(`div`,V,` Loading functions… `)):k.value.length===0?(e(),s(`div`,H,[l(a(N),{class:`w-8 h-8 text-foreground-muted mx-auto mb-2 opacity-30`}),o(`p`,U,[b.value.length===0?(e(),s(p,{key:0},[f(` No functions deployed yet. `)],64)):(e(),s(p,{key:1},[f(` No functions match "`+g(w.value)+`". `,1)],64))])])):(e(),s(`ul`,W,[(e(!0),s(p,null,u(k.value,t=>(e(),s(`li`,{key:t.id,class:c([`px-5 py-3 flex items-center gap-3 cursor-pointer transition-colors`,T.value.has(t.id)?`bg-surface/30 hover:bg-surface/50`:`hover:bg-surface/40`]),onClick:e=>E(t.id)},[o(`input`,{type:`checkbox`,checked:T.value.has(t.id),class:`accent-primary cursor-pointer`,onClick:C(e=>E(t.id),[`stop`])},null,8,K),o(`div`,q,[o(`div`,J,g(t.name),1),t.description?(e(),s(`div`,Y,g(t.description),1)):h(``,!0)]),o(`code`,X,g(t.runtime),1)],10,G))),128))]))]),o(`div`,Z,[o(`div`,Q,g(T.value.size)+` of `+g(b.value.length)+` selected `,1),o(`div`,re,[l(O,{variant:`secondary`,onClick:i[2]||=e=>n.$emit(`close`)},{default:t(()=>[...i[5]||=[f(` Cancel `,-1)]]),_:1}),l(O,{disabled:T.value.size===0,onClick:A},{default:t(()=>[...i[6]||=[f(` Apply `,-1)]]),_:1},8,[`disabled`])])])])]))}},ae={class:`space-y-6`},oe={class:`flex items-center justify-between gap-4`},se={key:0,class:`bg-background border border-warning-ring rounded-lg p-4 space-y-3`},ce={class:`flex items-start justify-between gap-3`},le={class:`flex items-center gap-2`},ue={class:`flex-1 font-mono text-sm text-white break-all bg-surface px-3 py-2 rounded border border-border`},de={class:`text-xs text-foreground-muted flex flex-wrap items-center gap-x-3 gap-y-1`},fe={class:`text-foreground bg-surface px-1.5 py-0.5 rounded`},pe={key:1,class:`bg-background border border-border rounded-lg p-5 space-y-4`},me={class:`grid grid-cols-1 md:grid-cols-2 gap-3`},he={class:`flex items-center justify-between mb-1.5`},ge={key:0,class:`text-[11px] text-foreground-muted`},_e={key:0,class:`rounded-md border border-red-700/40 bg-red-950/30 p-3 text-xs text-red-200 flex items-start gap-2`},ve={class:`flex gap-2 pt-1`},ye={class:`bg-background border border-border rounded-lg overflow-x-auto`},be={class:`sm:hidden divide-y divide-border`},xe={class:`flex items-start justify-between gap-2`},Se={class:`min-w-0 flex-1`},Ce={class:`flex items-center gap-2 flex-wrap`},we={class:`font-medium text-white truncate`},Te={class:`inline-flex items-center gap-1 text-[11px] text-foreground-muted`},Ee={key:0,class:`mt-1 text-xs text-foreground-muted line-clamp-2`},De={class:`mt-1 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-[11px] text-foreground-muted`},Oe={class:`font-mono`},ke={key:0},Ae={key:1,class:`text-amber-400/80`},je={key:2,class:`text-red-400`},Me={key:3},Ne={class:`flex items-center gap-1 shrink-0`},Pe={key:0,class:`px-6 py-8 text-center text-sm text-foreground-muted`},Fe={class:`hidden sm:table w-full text-sm text-left`},Ie={class:`divide-y divide-border`},Le={class:`px-6 py-4`},Re={class:`font-medium text-white`},ze={key:0,class:`text-xs text-foreground-muted mt-0.5 line-clamp-1 max-w-md`},Be={class:`px-6 py-4`},Ve={class:`inline-flex items-center gap-1.5 text-foreground-muted`},$={class:`tabular-nums`},He={class:`px-6 py-4 hidden sm:table-cell`},Ue={class:`text-foreground-muted font-mono text-xs`},We={class:`px-6 py-4 hidden md:table-cell`},Ge={key:0,class:`text-foreground-muted`},Ke={key:1,class:`text-amber-400/70 text-xs`},qe={class:`px-6 py-4 hidden lg:table-cell`},Je={key:0,class:`text-foreground-muted`},Ye={key:1,class:`text-red-400 text-xs`},Xe={key:2,class:`text-foreground-muted`},Ze={class:`px-6 py-4 text-right`},Qe={class:`inline-flex justify-end gap-1`},$e={key:0},et=Object.assign({name:`ChannelsView`},{__name:`Channels`,setup(c){let S=A(),C=n([]),N=n(``),L=n(!1),R=n(!1),z=n(!1),B=n(``),V=n(!1),H=n({name:``,description:``,expiresInDays:0,functionIds:[]}),U=m(()=>`${window.location.origin}/mcp`),W=m(()=>H.value.name.trim()&&H.value.functionIds.length>0),G=async()=>{let e=await T();C.value=e.data.channels||[]},K=()=>{H.value={name:``,description:``,expiresInDays:0,functionIds:[]},B.value=``,R.value=!0},q=()=>{R.value=!1},J=e=>{H.value.functionIds=e,V.value=!1},Y=async()=>{z.value=!0,B.value=``;try{let e={name:H.value.name.trim(),description:H.value.description.trim(),function_ids:H.value.functionIds};H.value.expiresInDays>0&&(e.expires_in_days=H.value.expiresInDays);let t=await ee(e);N.value=t.data.token,R.value=!1,await G()}catch(e){B.value=e?.response?.data?.error?.message||`Failed to create channel.`}finally{z.value=!1}},X=async()=>{N.value&&await ne(N.value)&&(L.value=!0,setTimeout(()=>{L.value=!1},1500))},Z=async e=>{if(!await S.ask({title:`Rotate ${e.name}?`,message:`A new token will be issued. The previous token stops working immediately. Agents using it will need the new value.`,confirmLabel:`Rotate`,danger:!0}))return;let t=await te(e.id);N.value=t.data.token,await G()},Q=async e=>{await S.ask({title:`Delete ${e.name}?`,message:`${e.name} will lose MCP access immediately. Functions inside are not affected. Re-create the channel if you need it again.`,confirmLabel:`Delete`,danger:!0})&&(await k(e.id),await G())};return i(G),(n,i)=>(e(),s(`div`,ae,[o(`div`,oe,[i[7]||=o(`div`,null,[o(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` Channels `),o(`p`,{class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},` Expose selected functions as scoped MCP tools. `)],-1),l(O,{onClick:K},{default:t(()=>[l(a(w),{class:`w-4 h-4`}),i[6]||=f(` New channel `,-1)]),_:1})]),N.value?(e(),s(`div`,se,[o(`div`,ce,[i[8]||=o(`div`,null,[o(`h2`,{class:`text-xs font-bold text-warning-fg uppercase tracking-wider`},` Copy this token now `),o(`div`,{class:`text-xs text-foreground-muted mt-0.5`},` Store it securely, then add it to your MCP client. `)],-1),o(`button`,{class:`text-foreground-muted hover:text-white transition-colors`,title:`Dismiss`,"aria-label":`Dismiss channel token`,onClick:i[0]||=e=>N.value=``},[l(a(M),{class:`w-4 h-4`})])]),o(`div`,le,[o(`code`,ue,g(N.value),1),o(`button`,{class:`px-3 py-2 rounded-md border border-border bg-surface-hover hover:bg-surface text-foreground-muted hover:text-white transition-colors flex items-center gap-1.5 text-xs`,onClick:X},[L.value?(e(),d(a(_),{key:0,class:`w-3.5 h-3.5 text-success`})):(e(),d(a(y),{key:1,class:`w-3.5 h-3.5`})),f(` `+g(L.value?`Copied`:`Copy`),1)])]),o(`div`,de,[o(`span`,null,[i[9]||=f(`URL `,-1),o(`code`,fe,g(U.value),1)]),i[10]||=o(`span`,null,[f(`Header `),o(`code`,{class:`text-foreground bg-surface px-1.5 py-0.5 rounded`},`Authorization: Bearer `)],-1)])])):h(``,!0),R.value?(e(),s(`div`,pe,[i[18]||=o(`div`,{class:`text-sm font-semibold text-white`},` New channel `,-1),o(`div`,me,[o(`div`,null,[i[11]||=o(`label`,{for:`channel-name`,class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-1.5`},`Name`,-1),r(o(`input`,{id:`channel-name`,"onUpdate:modelValue":i[1]||=e=>H.value.name=e,placeholder:`e.g. support-bot`,class:`w-full bg-surface-hover border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-primary transition-colors`},null,512),[[D,H.value.name]])]),o(`div`,null,[i[13]||=o(`label`,{for:`channel-expiry`,class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-1.5`},`Expires in`,-1),r(o(`select`,{id:`channel-expiry`,"onUpdate:modelValue":i[2]||=e=>H.value.expiresInDays=e,class:`w-full bg-surface-hover border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-primary transition-colors`},[...i[12]||=[o(`option`,{value:0},` Never `,-1),o(`option`,{value:7},` 7 days `,-1),o(`option`,{value:30},` 30 days `,-1),o(`option`,{value:90},` 90 days `,-1)]],512),[[E,H.value.expiresInDays]])])]),o(`div`,null,[i[14]||=o(`label`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-1.5`},`Description (optional)`,-1),r(o(`input`,{"onUpdate:modelValue":i[3]||=e=>H.value.description=e,placeholder:`What this channel is for`,class:`w-full bg-surface-hover border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-primary transition-colors`},null,512),[[D,H.value.description]])]),o(`div`,null,[o(`div`,he,[i[15]||=o(`label`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide`},`Functions`,-1),H.value.functionIds.length>0?(e(),s(`span`,ge,g(H.value.functionIds.length)+` selected`,1)):h(``,!0)]),l(O,{variant:`secondary`,onClick:i[4]||=e=>V.value=!0},{default:t(()=>[l(a(j),{class:`w-4 h-4`}),f(` `+g(H.value.functionIds.length===0?`Pick functions`:`Edit selection`),1)]),_:1})]),B.value?(e(),s(`div`,_e,[l(a(v),{class:`w-4 h-4 text-red-400 shrink-0 mt-0.5`}),o(`span`,null,g(B.value),1)])):h(``,!0),o(`div`,ve,[l(O,{disabled:!W.value||z.value,loading:z.value,onClick:Y},{default:t(()=>[...i[16]||=[f(` Generate token `,-1)]]),_:1},8,[`disabled`,`loading`]),l(O,{variant:`secondary`,onClick:q},{default:t(()=>[...i[17]||=[f(` Cancel `,-1)]]),_:1})])])):h(``,!0),o(`div`,ye,[o(`ul`,be,[(e(!0),s(p,null,u(C.value,t=>(e(),s(`li`,{key:t.id,class:`px-4 py-3`},[o(`div`,xe,[o(`div`,Se,[o(`div`,Ce,[o(`span`,we,g(t.name),1),o(`span`,Te,[l(a(j),{class:`w-3 h-3`}),f(` `+g(t.function_count),1)])]),t.description?(e(),s(`div`,Ee,g(t.description),1)):h(``,!0),o(`div`,De,[o(`code`,Oe,g(t.prefix)+`…`,1),t.last_used_at?(e(),s(`span`,ke,`used `+g(a(I)(t.last_used_at)),1)):(e(),s(`span`,Ae,`never used`)),t.expires_at&&a(F)(t.expires_at)?(e(),s(`span`,je,`expired`)):t.expires_at?(e(),s(`span`,Me,`expires `+g(a(I)(t.expires_at)),1)):h(``,!0)])]),o(`div`,Ne,[l(P,{icon:a(b),title:`Rotate token`,onClick:e=>Z(t)},null,8,[`icon`,`onClick`]),l(P,{icon:a(x),variant:`danger`,title:`Delete channel`,onClick:e=>Q(t)},null,8,[`icon`,`onClick`])])])]))),128)),C.value.length===0?(e(),s(`li`,Pe,` No channels yet. `)):h(``,!0)]),o(`table`,Fe,[i[20]||=o(`thead`,{class:`text-xs text-foreground-muted uppercase bg-surface border-b border-border`},[o(`tr`,null,[o(`th`,{class:`px-6 py-3 font-medium`},` Name `),o(`th`,{class:`px-6 py-3 font-medium`},` Functions `),o(`th`,{class:`px-6 py-3 font-medium hidden sm:table-cell`},` Prefix `),o(`th`,{class:`px-6 py-3 font-medium hidden md:table-cell`},` Last used `),o(`th`,{class:`px-6 py-3 font-medium hidden lg:table-cell`},` Expires `),o(`th`,{class:`px-6 py-3 font-medium text-right`},` Actions `)])],-1),o(`tbody`,Ie,[(e(!0),s(p,null,u(C.value,t=>(e(),s(`tr`,{key:t.id,class:`hover:bg-surface/50 transition-colors`},[o(`td`,Le,[o(`div`,Re,g(t.name),1),t.description?(e(),s(`div`,ze,g(t.description),1)):h(``,!0)]),o(`td`,Be,[o(`span`,Ve,[l(a(j),{class:`w-3.5 h-3.5`}),o(`span`,$,g(t.function_count),1)])]),o(`td`,He,[o(`code`,Ue,g(t.prefix)+`…`,1)]),o(`td`,We,[t.last_used_at?(e(),s(`span`,Ge,g(a(I)(t.last_used_at)),1)):(e(),s(`span`,Ke,`Never used`))]),o(`td`,qe,[t.expires_at?a(F)(t.expires_at)?(e(),s(`span`,Ye,`Expired `+g(a(I)(t.expires_at)),1)):(e(),s(`span`,Xe,g(a(I)(t.expires_at)),1)):(e(),s(`span`,Je,`Never`))]),o(`td`,Ze,[o(`div`,Qe,[l(P,{icon:a(b),title:`Rotate token`,onClick:e=>Z(t)},null,8,[`icon`,`onClick`]),l(P,{icon:a(x),variant:`danger`,title:`Delete channel`,onClick:e=>Q(t)},null,8,[`icon`,`onClick`])])])]))),128)),C.value.length===0?(e(),s(`tr`,$e,[...i[19]||=[o(`td`,{colspan:`6`,class:`px-6 py-8 text-center text-foreground-muted`},[f(` No channels yet. Click `),o(`span`,{class:`text-white`},`New channel`),f(` to bundle functions for an agent. `)],-1)]])):h(``,!0)])])]),V.value?(e(),d(ie,{key:2,selected:H.value.functionIds,onClose:i[5]||=e=>V.value=!1,onApply:J},null,8,[`selected`])):h(``,!0)]))}});export{et as default}; \ No newline at end of file +import{D as e,F as t,G as n,I as r,T as i,Z as a,c as o,d as s,gt as c,h as l,k as u,l as d,m as f,r as p,s as m,u as h,vt as g}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as _}from"./check-BNre7JFR.js";import{t as v}from"./circle-alert-BnUYYhGE.js";import{t as y}from"./copy-BqdwwcxC.js";import{t as b}from"./rotate-ccw-DWwjKCqh.js";import{t as x}from"./trash-2-DaeYqnW_.js";import{B as S,Bt as C,Ct as w,F as T,It as E,Lt as D,_t as O,f as k,gt as A,kt as j,s as ee,tt as te,vt as M,xt as N}from"./index-DTqMKlE1.js";import{t as ne}from"./clipboard-D_9N0yai.js";import{t as P}from"./IconButton-CsCZOqWo.js";import{n as F,t as I}from"./time-D8OmbYzY.js";var L={class:`w-full max-w-2xl bg-background border border-border rounded-lg shadow-lg flex flex-col max-h-[80vh]`},R={class:`px-5 py-4 border-b border-border flex items-start justify-between gap-3`},z={class:`px-5 py-3 border-b border-border flex items-center gap-2`},B={class:`flex-1 overflow-y-auto`},V={key:0,class:`px-5 py-10 text-center text-xs text-foreground-muted italic`},H={key:1,class:`px-5 py-10 text-center`},U={class:`text-xs text-foreground-muted`},W={key:2,class:`divide-y divide-border`},G=[`onClick`],K=[`checked`,`onClick`],q={class:`flex-1 min-w-0`},J={class:`text-sm font-medium text-white truncate`},Y={key:0,class:`text-xs text-foreground-muted mt-0.5 line-clamp-1`},X={class:`text-[11px] text-foreground-muted font-mono shrink-0`},Z={class:`px-5 py-3 border-t border-border flex items-center justify-between gap-3`},Q={class:`text-xs text-foreground-muted tabular-nums`},re={class:`flex gap-2`},ie={__name:`FunctionPickerModal`,props:{selected:{type:Array,default:()=>[]}},emits:[`close`,`apply`],setup(d,{emit:_}){let v=d,y=_,b=n([]),x=n(!0),w=n(``),T=n(new Set(v.selected)),E=e=>{let t=new Set(T.value);t.has(e)?t.delete(e):t.add(e),T.value=t},k=m(()=>{let e=w.value.trim().toLowerCase();return e?b.value.filter(t=>t.name.toLowerCase().includes(e)||(t.description||``).toLowerCase().includes(e)||(t.runtime||``).toLowerCase().includes(e)):b.value}),A=()=>{y(`apply`,Array.from(T.value))};return i(async()=>{try{let e=await S({limit:200});b.value=e.data.functions||[]}finally{x.value=!1}}),(n,i)=>(e(),s(`div`,{class:`fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm`,onClick:i[3]||=C(e=>n.$emit(`close`),[`self`])},[o(`div`,L,[o(`div`,R,[i[4]||=o(`div`,null,[o(`div`,{class:`text-sm font-semibold text-white`},` Pick functions `),o(`div`,{class:`text-xs text-foreground-muted mt-0.5 max-w-prose leading-relaxed`},` Each selection becomes an MCP tool. Dashes become underscores. `)],-1),o(`button`,{class:`text-foreground-muted hover:text-white transition-colors`,title:`Dismiss`,"aria-label":`Close function picker`,onClick:i[0]||=e=>n.$emit(`close`)},[l(a(M),{class:`w-4 h-4`})])]),o(`div`,z,[l(a(N),{class:`w-4 h-4 text-foreground-muted shrink-0`}),r(o(`input`,{"onUpdate:modelValue":i[1]||=e=>w.value=e,"aria-label":`Search functions`,type:`text`,placeholder:`Filter by name, description, or runtime`,class:`flex-1 bg-transparent text-sm text-foreground placeholder-foreground-muted focus:outline-none`},null,512),[[D,w.value]])]),o(`div`,B,[x.value?(e(),s(`div`,V,` Loading functions… `)):k.value.length===0?(e(),s(`div`,H,[l(a(N),{class:`w-8 h-8 text-foreground-muted mx-auto mb-2 opacity-30`}),o(`p`,U,[b.value.length===0?(e(),s(p,{key:0},[f(` No functions deployed yet. `)],64)):(e(),s(p,{key:1},[f(` No functions match "`+g(w.value)+`". `,1)],64))])])):(e(),s(`ul`,W,[(e(!0),s(p,null,u(k.value,t=>(e(),s(`li`,{key:t.id,class:c([`px-5 py-3 flex items-center gap-3 cursor-pointer transition-colors`,T.value.has(t.id)?`bg-surface/30 hover:bg-surface/50`:`hover:bg-surface/40`]),onClick:e=>E(t.id)},[o(`input`,{type:`checkbox`,checked:T.value.has(t.id),class:`accent-primary cursor-pointer`,onClick:C(e=>E(t.id),[`stop`])},null,8,K),o(`div`,q,[o(`div`,J,g(t.name),1),t.description?(e(),s(`div`,Y,g(t.description),1)):h(``,!0)]),o(`code`,X,g(t.runtime),1)],10,G))),128))]))]),o(`div`,Z,[o(`div`,Q,g(T.value.size)+` of `+g(b.value.length)+` selected `,1),o(`div`,re,[l(O,{variant:`secondary`,onClick:i[2]||=e=>n.$emit(`close`)},{default:t(()=>[...i[5]||=[f(` Cancel `,-1)]]),_:1}),l(O,{disabled:T.value.size===0,onClick:A},{default:t(()=>[...i[6]||=[f(` Apply `,-1)]]),_:1},8,[`disabled`])])])])]))}},ae={class:`space-y-6`},oe={class:`flex items-center justify-between gap-4`},se={key:0,class:`bg-background border border-warning-ring rounded-lg p-4 space-y-3`},ce={class:`flex items-start justify-between gap-3`},le={class:`flex items-center gap-2`},ue={class:`flex-1 font-mono text-sm text-white break-all bg-surface px-3 py-2 rounded border border-border`},de={class:`text-xs text-foreground-muted flex flex-wrap items-center gap-x-3 gap-y-1`},fe={class:`text-foreground bg-surface px-1.5 py-0.5 rounded`},pe={key:1,class:`bg-background border border-border rounded-lg p-5 space-y-4`},me={class:`grid grid-cols-1 md:grid-cols-2 gap-3`},he={class:`flex items-center justify-between mb-1.5`},ge={key:0,class:`text-[11px] text-foreground-muted`},_e={key:0,class:`rounded-md border border-red-700/40 bg-red-950/30 p-3 text-xs text-red-200 flex items-start gap-2`},ve={class:`flex gap-2 pt-1`},ye={class:`bg-background border border-border rounded-lg overflow-x-auto`},be={class:`sm:hidden divide-y divide-border`},xe={class:`flex items-start justify-between gap-2`},Se={class:`min-w-0 flex-1`},Ce={class:`flex items-center gap-2 flex-wrap`},we={class:`font-medium text-white truncate`},Te={class:`inline-flex items-center gap-1 text-[11px] text-foreground-muted`},Ee={key:0,class:`mt-1 text-xs text-foreground-muted line-clamp-2`},De={class:`mt-1 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-[11px] text-foreground-muted`},Oe={class:`font-mono`},ke={key:0},Ae={key:1,class:`text-amber-400/80`},je={key:2,class:`text-red-400`},Me={key:3},Ne={class:`flex items-center gap-1 shrink-0`},Pe={key:0,class:`px-6 py-8 text-center text-sm text-foreground-muted`},Fe={class:`hidden sm:table w-full text-sm text-left`},Ie={class:`divide-y divide-border`},Le={class:`px-6 py-4`},Re={class:`font-medium text-white`},ze={key:0,class:`text-xs text-foreground-muted mt-0.5 line-clamp-1 max-w-md`},Be={class:`px-6 py-4`},Ve={class:`inline-flex items-center gap-1.5 text-foreground-muted`},$={class:`tabular-nums`},He={class:`px-6 py-4 hidden sm:table-cell`},Ue={class:`text-foreground-muted font-mono text-xs`},We={class:`px-6 py-4 hidden md:table-cell`},Ge={key:0,class:`text-foreground-muted`},Ke={key:1,class:`text-amber-400/70 text-xs`},qe={class:`px-6 py-4 hidden lg:table-cell`},Je={key:0,class:`text-foreground-muted`},Ye={key:1,class:`text-red-400 text-xs`},Xe={key:2,class:`text-foreground-muted`},Ze={class:`px-6 py-4 text-right`},Qe={class:`inline-flex justify-end gap-1`},$e={key:0},et=Object.assign({name:`ChannelsView`},{__name:`Channels`,setup(c){let S=A(),C=n([]),N=n(``),L=n(!1),R=n(!1),z=n(!1),B=n(``),V=n(!1),H=n({name:``,description:``,expiresInDays:0,functionIds:[]}),U=m(()=>`${window.location.origin}/mcp`),W=m(()=>H.value.name.trim()&&H.value.functionIds.length>0),G=async()=>{let e=await T();C.value=e.data.channels||[]},K=()=>{H.value={name:``,description:``,expiresInDays:0,functionIds:[]},B.value=``,R.value=!0},q=()=>{R.value=!1},J=e=>{H.value.functionIds=e,V.value=!1},Y=async()=>{z.value=!0,B.value=``;try{let e={name:H.value.name.trim(),description:H.value.description.trim(),function_ids:H.value.functionIds};H.value.expiresInDays>0&&(e.expires_in_days=H.value.expiresInDays);let t=await ee(e);N.value=t.data.token,R.value=!1,await G()}catch(e){B.value=e?.response?.data?.error?.message||`Failed to create channel.`}finally{z.value=!1}},X=async()=>{N.value&&await ne(N.value)&&(L.value=!0,setTimeout(()=>{L.value=!1},1500))},Z=async e=>{if(!await S.ask({title:`Rotate ${e.name}?`,message:`A new token will be issued. The previous token stops working immediately. Agents using it will need the new value.`,confirmLabel:`Rotate`,danger:!0}))return;let t=await te(e.id);N.value=t.data.token,await G()},Q=async e=>{await S.ask({title:`Delete ${e.name}?`,message:`${e.name} will lose MCP access immediately. Functions inside are not affected. Re-create the channel if you need it again.`,confirmLabel:`Delete`,danger:!0})&&(await k(e.id),await G())};return i(G),(n,i)=>(e(),s(`div`,ae,[o(`div`,oe,[i[7]||=o(`div`,null,[o(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` Channels `),o(`p`,{class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},` Expose selected functions as scoped MCP tools. `)],-1),l(O,{onClick:K},{default:t(()=>[l(a(w),{class:`w-4 h-4`}),i[6]||=f(` New channel `,-1)]),_:1})]),N.value?(e(),s(`div`,se,[o(`div`,ce,[i[8]||=o(`div`,null,[o(`h2`,{class:`text-xs font-bold text-warning-fg uppercase tracking-wider`},` Copy this token now `),o(`div`,{class:`text-xs text-foreground-muted mt-0.5`},` Store it securely, then add it to your MCP client. `)],-1),o(`button`,{class:`text-foreground-muted hover:text-white transition-colors`,title:`Dismiss`,"aria-label":`Dismiss channel token`,onClick:i[0]||=e=>N.value=``},[l(a(M),{class:`w-4 h-4`})])]),o(`div`,le,[o(`code`,ue,g(N.value),1),o(`button`,{class:`px-3 py-2 rounded-md border border-border bg-surface-hover hover:bg-surface text-foreground-muted hover:text-white transition-colors flex items-center gap-1.5 text-xs`,onClick:X},[L.value?(e(),d(a(_),{key:0,class:`w-3.5 h-3.5 text-success`})):(e(),d(a(y),{key:1,class:`w-3.5 h-3.5`})),f(` `+g(L.value?`Copied`:`Copy`),1)])]),o(`div`,de,[o(`span`,null,[i[9]||=f(`URL `,-1),o(`code`,fe,g(U.value),1)]),i[10]||=o(`span`,null,[f(`Header `),o(`code`,{class:`text-foreground bg-surface px-1.5 py-0.5 rounded`},`Authorization: Bearer `)],-1)])])):h(``,!0),R.value?(e(),s(`div`,pe,[i[18]||=o(`div`,{class:`text-sm font-semibold text-white`},` New channel `,-1),o(`div`,me,[o(`div`,null,[i[11]||=o(`label`,{for:`channel-name`,class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-1.5`},`Name`,-1),r(o(`input`,{id:`channel-name`,"onUpdate:modelValue":i[1]||=e=>H.value.name=e,placeholder:`e.g. support-bot`,class:`w-full bg-surface-hover border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-primary transition-colors`},null,512),[[D,H.value.name]])]),o(`div`,null,[i[13]||=o(`label`,{for:`channel-expiry`,class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-1.5`},`Expires in`,-1),r(o(`select`,{id:`channel-expiry`,"onUpdate:modelValue":i[2]||=e=>H.value.expiresInDays=e,class:`w-full bg-surface-hover border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-primary transition-colors`},[...i[12]||=[o(`option`,{value:0},` Never `,-1),o(`option`,{value:7},` 7 days `,-1),o(`option`,{value:30},` 30 days `,-1),o(`option`,{value:90},` 90 days `,-1)]],512),[[E,H.value.expiresInDays]])])]),o(`div`,null,[i[14]||=o(`label`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-1.5`},`Description (optional)`,-1),r(o(`input`,{"onUpdate:modelValue":i[3]||=e=>H.value.description=e,placeholder:`What this channel is for`,class:`w-full bg-surface-hover border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-primary transition-colors`},null,512),[[D,H.value.description]])]),o(`div`,null,[o(`div`,he,[i[15]||=o(`label`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide`},`Functions`,-1),H.value.functionIds.length>0?(e(),s(`span`,ge,g(H.value.functionIds.length)+` selected`,1)):h(``,!0)]),l(O,{variant:`secondary`,onClick:i[4]||=e=>V.value=!0},{default:t(()=>[l(a(j),{class:`w-4 h-4`}),f(` `+g(H.value.functionIds.length===0?`Pick functions`:`Edit selection`),1)]),_:1})]),B.value?(e(),s(`div`,_e,[l(a(v),{class:`w-4 h-4 text-red-400 shrink-0 mt-0.5`}),o(`span`,null,g(B.value),1)])):h(``,!0),o(`div`,ve,[l(O,{disabled:!W.value||z.value,loading:z.value,onClick:Y},{default:t(()=>[...i[16]||=[f(` Generate token `,-1)]]),_:1},8,[`disabled`,`loading`]),l(O,{variant:`secondary`,onClick:q},{default:t(()=>[...i[17]||=[f(` Cancel `,-1)]]),_:1})])])):h(``,!0),o(`div`,ye,[o(`ul`,be,[(e(!0),s(p,null,u(C.value,t=>(e(),s(`li`,{key:t.id,class:`px-4 py-3`},[o(`div`,xe,[o(`div`,Se,[o(`div`,Ce,[o(`span`,we,g(t.name),1),o(`span`,Te,[l(a(j),{class:`w-3 h-3`}),f(` `+g(t.function_count),1)])]),t.description?(e(),s(`div`,Ee,g(t.description),1)):h(``,!0),o(`div`,De,[o(`code`,Oe,g(t.prefix)+`…`,1),t.last_used_at?(e(),s(`span`,ke,`used `+g(a(I)(t.last_used_at)),1)):(e(),s(`span`,Ae,`never used`)),t.expires_at&&a(F)(t.expires_at)?(e(),s(`span`,je,`expired`)):t.expires_at?(e(),s(`span`,Me,`expires `+g(a(I)(t.expires_at)),1)):h(``,!0)])]),o(`div`,Ne,[l(P,{icon:a(b),title:`Rotate token`,onClick:e=>Z(t)},null,8,[`icon`,`onClick`]),l(P,{icon:a(x),variant:`danger`,title:`Delete channel`,onClick:e=>Q(t)},null,8,[`icon`,`onClick`])])])]))),128)),C.value.length===0?(e(),s(`li`,Pe,` No channels yet. `)):h(``,!0)]),o(`table`,Fe,[i[20]||=o(`thead`,{class:`text-xs text-foreground-muted uppercase bg-surface border-b border-border`},[o(`tr`,null,[o(`th`,{class:`px-6 py-3 font-medium`},` Name `),o(`th`,{class:`px-6 py-3 font-medium`},` Functions `),o(`th`,{class:`px-6 py-3 font-medium hidden sm:table-cell`},` Prefix `),o(`th`,{class:`px-6 py-3 font-medium hidden md:table-cell`},` Last used `),o(`th`,{class:`px-6 py-3 font-medium hidden lg:table-cell`},` Expires `),o(`th`,{class:`px-6 py-3 font-medium text-right`},` Actions `)])],-1),o(`tbody`,Ie,[(e(!0),s(p,null,u(C.value,t=>(e(),s(`tr`,{key:t.id,class:`hover:bg-surface/50 transition-colors`},[o(`td`,Le,[o(`div`,Re,g(t.name),1),t.description?(e(),s(`div`,ze,g(t.description),1)):h(``,!0)]),o(`td`,Be,[o(`span`,Ve,[l(a(j),{class:`w-3.5 h-3.5`}),o(`span`,$,g(t.function_count),1)])]),o(`td`,He,[o(`code`,Ue,g(t.prefix)+`…`,1)]),o(`td`,We,[t.last_used_at?(e(),s(`span`,Ge,g(a(I)(t.last_used_at)),1)):(e(),s(`span`,Ke,`Never used`))]),o(`td`,qe,[t.expires_at?a(F)(t.expires_at)?(e(),s(`span`,Ye,`Expired `+g(a(I)(t.expires_at)),1)):(e(),s(`span`,Xe,g(a(I)(t.expires_at)),1)):(e(),s(`span`,Je,`Never`))]),o(`td`,Ze,[o(`div`,Qe,[l(P,{icon:a(b),title:`Rotate token`,onClick:e=>Z(t)},null,8,[`icon`,`onClick`]),l(P,{icon:a(x),variant:`danger`,title:`Delete channel`,onClick:e=>Q(t)},null,8,[`icon`,`onClick`])])])]))),128)),C.value.length===0?(e(),s(`tr`,$e,[...i[19]||=[o(`td`,{colspan:`6`,class:`px-6 py-8 text-center text-foreground-muted`},[f(` No channels yet. Click `),o(`span`,{class:`text-white`},`New channel`),f(` to bundle functions for an agent. `)],-1)]])):h(``,!0)])])]),V.value?(e(),d(ie,{key:2,selected:H.value.functionIds,onClose:i[5]||=e=>V.value=!1,onApply:J},null,8,[`selected`])):h(``,!0)]))}});export{et as default}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/CodeEditor-CAQF33k1.js b/backend/internal/server/ui_dist/assets/CodeEditor-CiRqvRcK.js similarity index 90% rename from backend/internal/server/ui_dist/assets/CodeEditor-CAQF33k1.js rename to backend/internal/server/ui_dist/assets/CodeEditor-CiRqvRcK.js index b8021f47..d857bb14 100644 --- a/backend/internal/server/ui_dist/assets/CodeEditor-CAQF33k1.js +++ b/backend/internal/server/ui_dist/assets/CodeEditor-CiRqvRcK.js @@ -1 +1 @@ -import{D as e,E as t,G as n,P as r,T as i,d as a}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{ut as o}from"./index-C11HmDXN.js";import{E as s,T as c,a as l,n as u,r as d,t as f,v as p}from"./dist-CR15Grce.js";var m=o({__name:`CodeEditor`,props:{modelValue:{type:String,default:``},language:{type:String,default:`javascript`},readOnly:{type:Boolean,default:!1}},emits:[`update:modelValue`],setup(o,{emit:m}){let h=o,g=m,_=n(null),v=null,y=new c,b=e=>e?.startsWith(`python`)?u():(e?.startsWith(`node`),d());return i(()=>{let e=s.create({doc:h.modelValue,extensions:[l,y.of(b(h.language)),f,p.updateListener.of(e=>{e.docChanged&&g(`update:modelValue`,e.state.doc.toString())}),p.theme({"&":{fontSize:`16px`,height:`100%`},"@media (min-width: 640px)":{"&":{fontSize:`14px`}},".cm-scroller":{fontFamily:`JetBrains Mono, monospace`,lineHeight:`1.6`},".cm-content":{padding:`16px 0`},".cm-line":{padding:`0 16px`}}),s.readOnly.of(h.readOnly)]});v=new p({state:e,parent:_.value})}),t(()=>{v&&v.destroy()}),r(()=>h.modelValue,e=>{v&&e!==v.state.doc.toString()&&v.dispatch({changes:{from:0,to:v.state.doc.length,insert:e}})}),r(()=>h.language,e=>{v&&v.dispatch({effects:y.reconfigure(b(e))})}),(t,n)=>(e(),a(`div`,{ref_key:`editorRef`,ref:_,class:`h-full w-full`},null,512))}},[[`__scopeId`,`data-v-ccca551c`]]);export{m as default}; \ No newline at end of file +import{D as e,E as t,G as n,P as r,T as i,d as a}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{ut as o}from"./index-DTqMKlE1.js";import{E as s,T as c,a as l,n as u,r as d,t as f,v as p}from"./dist-CR15Grce.js";var m=o({__name:`CodeEditor`,props:{modelValue:{type:String,default:``},language:{type:String,default:`javascript`},readOnly:{type:Boolean,default:!1}},emits:[`update:modelValue`],setup(o,{emit:m}){let h=o,g=m,_=n(null),v=null,y=new c,b=e=>e?.startsWith(`python`)?u():(e?.startsWith(`node`),d());return i(()=>{let e=s.create({doc:h.modelValue,extensions:[l,y.of(b(h.language)),f,p.updateListener.of(e=>{e.docChanged&&g(`update:modelValue`,e.state.doc.toString())}),p.theme({"&":{fontSize:`16px`,height:`100%`},"@media (min-width: 640px)":{"&":{fontSize:`14px`}},".cm-scroller":{fontFamily:`JetBrains Mono, monospace`,lineHeight:`1.6`},".cm-content":{padding:`16px 0`},".cm-line":{padding:`0 16px`}}),s.readOnly.of(h.readOnly)]});v=new p({state:e,parent:_.value})}),t(()=>{v&&v.destroy()}),r(()=>h.modelValue,e=>{v&&e!==v.state.doc.toString()&&v.dispatch({changes:{from:0,to:v.state.doc.length,insert:e}})}),r(()=>h.language,e=>{v&&v.dispatch({effects:y.reconfigure(b(e))})}),(t,n)=>(e(),a(`div`,{ref_key:`editorRef`,ref:_,class:`h-full w-full`},null,512))}},[[`__scopeId`,`data-v-ccca551c`]]);export{m as default}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/CronJobs-4ao1_N2n.js b/backend/internal/server/ui_dist/assets/CronJobs-CDFaee6D.js similarity index 98% rename from backend/internal/server/ui_dist/assets/CronJobs-4ao1_N2n.js rename to backend/internal/server/ui_dist/assets/CronJobs-CDFaee6D.js index e9eee498..011f3f52 100644 --- a/backend/internal/server/ui_dist/assets/CronJobs-4ao1_N2n.js +++ b/backend/internal/server/ui_dist/assets/CronJobs-CDFaee6D.js @@ -1 +1 @@ -import{D as e,F as t,G as n,I as r,M as i,T as a,Z as o,c as s,d as c,gt as l,h as u,k as d,l as f,m as p,r as m,u as h,vt as g}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{n as _,t as v}from"./clock-Dl3hMdU8.js";import{t as y}from"./play-DdJW9ehy.js";import{t as b}from"./square-pen-JECRrUKU.js";import{t as x}from"./trash-2-Bj4AFUfw.js";import{B as ee,It as S,L as te,Lt as C,Nt as ne,_t as w,at as T,c as re,gt as ie,i as ae,jt as E,p as oe}from"./index-C11HmDXN.js";import{t as D}from"./IconButton-CsCZOqWo.js";import{t as se}from"./Modal-BL-P3nUi.js";var ce=E(`circle-plus`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M8 12h8`,key:`1wcyev`}],[`path`,{d:`M12 8v8`,key:`napkw2`}]]),O=E(`pause`,[[`rect`,{x:`14`,y:`3`,width:`5`,height:`18`,rx:`1`,key:`kaeet6`}],[`rect`,{x:`5`,y:`3`,width:`5`,height:`18`,rx:`1`,key:`1wsw3u`}]]),le={class:`space-y-6`},ue={class:`flex items-center justify-between`},de={class:`bg-background border border-border rounded-lg overflow-x-auto`},fe={class:`sm:hidden divide-y divide-border`},pe={class:`flex items-start justify-between gap-2`},me={class:`min-w-0 flex-1`},he={class:`flex items-center gap-2 flex-wrap`},ge={class:`font-medium text-foreground truncate`},_e={class:`mt-1 text-[11px] text-foreground font-mono break-all`},ve={class:`mt-0.5 text-[11px] text-foreground-muted`},k={class:`text-foreground-muted/70`},A={class:`mt-1 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-[11px] text-foreground-muted`},j={class:`flex items-center gap-1 shrink-0`},M={key:0,class:`px-6 py-12 text-center`},N={class:`hidden sm:table w-full text-sm text-left`},P={class:`divide-y divide-border`},F={class:`px-6 py-4 font-medium text-foreground max-w-[16rem] truncate`},ye={class:`px-6 py-4`},be={class:`flex flex-col gap-1`},xe={class:`text-foreground font-mono text-xs break-all`},Se={class:`text-foreground-muted text-[10px]`},Ce={class:`text-foreground-muted/70`},we={class:`px-6 py-4 hidden sm:table-cell`},Te={class:`px-6 py-4 text-foreground-muted text-xs hidden md:table-cell`},Ee={class:`px-6 py-4 text-foreground-muted text-xs hidden lg:table-cell`},De={class:`px-6 py-4 text-right`},Oe={class:`inline-flex items-center gap-1`},ke={key:0},Ae={class:`space-y-5`},je=[`disabled`],Me=[`value`],Ne={class:`flex gap-2 bg-background rounded-lg p-1 border border-border`},Pe=[`onClick`],Fe={key:0,class:`space-y-4`},Ie={class:`grid grid-cols-3 gap-3`},I={key:0},Le={key:1},Re={key:2},ze={key:3},Be={class:`bg-background border border-border rounded-lg p-4`},Ve={class:`font-mono text-sm text-foreground`},He={class:`text-xs text-foreground-muted mt-1`},Ue={key:1,class:`space-y-3`},We={class:`bg-background border border-border rounded-lg p-4`},Ge={class:`text-xs text-foreground`},Ke=[`value`],qe={class:`text-xs text-foreground-muted mt-1.5`},Je={class:`bg-surface px-1 rounded`},Ye={class:`flex items-center gap-3`},L={__name:`CronJobs`,setup(E){let L=ae(),Xe=[...new Set([L,`UTC`,`America/Los_Angeles`,`America/New_York`,`America/Chicago`,`America/Denver`,`America/Sao_Paulo`,`Europe/London`,`Europe/Berlin`,`Europe/Paris`,`Europe/Moscow`,`Africa/Lagos`,`Africa/Cairo`,`Africa/Johannesburg`,`Asia/Dubai`,`Asia/Kolkata`,`Asia/Singapore`,`Asia/Shanghai`,`Asia/Tokyo`,`Australia/Sydney`,`Pacific/Auckland`])],R=ie(),z=n([]),B=n([]),V=n(!1),H=n(null),U=n(`simple`),W=n({function_name:``,cron:`0 0 * * *`,timezone:L,enabled:!0}),G=n({frequency:`day`,minute:0,hour:0,dayOfWeek:1,dayOfMonth:1}),K=async()=>{try{let e=await te();z.value=e.data.schedules||[]}catch(e){console.error(`Failed to load cron jobs`,e)}},Ze=async()=>{try{let e=await ee();B.value=e.data.functions||[]}catch(e){console.error(`Failed to load functions`,e)}},q=()=>{let{frequency:e,minute:t,hour:n,dayOfWeek:r,dayOfMonth:i}=G.value;switch(e){case`minute`:W.value.cron=`* * * * *`;break;case`hour`:W.value.cron=`${t} * * * *`;break;case`day`:W.value.cron=`${t} ${n} * * *`;break;case`week`:W.value.cron=`${t} ${n} * * ${r}`;break;case`month`:W.value.cron=`${t} ${n} ${i} * *`}},J=e=>{if(!e)return`Invalid expression`;let t=e.trim().split(/\s+/);if(t.length!==5)return`Invalid format (use 5 fields)`;let[n,r,i,a,o]=t;return e===`* * * * *`?`Every minute`:n!==`*`&&r===`*`&&i===`*`&&a===`*`&&o===`*`?`Every hour at minute ${n}`:n!==`*`&&r!==`*`&&i===`*`&&a===`*`&&o===`*`?`Every day at ${r.padStart(2,`0`)}:${n.padStart(2,`0`)}`:n!==`*`&&r!==`*`&&i===`*`&&a===`*`&&o!==`*`?`Every ${[`Sunday`,`Monday`,`Tuesday`,`Wednesday`,`Thursday`,`Friday`,`Saturday`][o]} at ${r.padStart(2,`0`)}:${n.padStart(2,`0`)}`:n!==`*`&&r!==`*`&&i!==`*`&&a===`*`&&o===`*`?`On day ${i} of every month at ${r.padStart(2,`0`)}:${n.padStart(2,`0`)}`:`Custom: ${e}`},Y=e=>new Date(e).toLocaleString(`en-US`,{month:`short`,day:`numeric`,hour:`2-digit`,minute:`2-digit`}),Qe=async()=>{try{H.value?await T(H.value.id,{function_id:H.value.function_id,cron:W.value.cron,timezone:W.value.timezone,enabled:W.value.enabled}):await re(W.value.function_name,{cron:W.value.cron,timezone:W.value.timezone,enabled:W.value.enabled}),await K(),$()}catch(e){console.error(`Failed to save schedule`,e),R.notify({title:`Failed to save schedule`,danger:!0})}},X=e=>{H.value=e,W.value={function_name:e.function_name,cron:e.cron_expression,timezone:e.timezone||`UTC`,enabled:e.enabled},V.value=!0},Z=async e=>{try{await T(e.id,{function_id:e.function_id,enabled:!e.enabled}),await K()}catch(e){console.error(`Failed to toggle schedule`,e)}},Q=async e=>{if(await R.ask({title:`Delete schedule?`,message:`Cron schedule for "${e.function_name}" will be removed.`,confirmLabel:`Delete`,danger:!0}))try{await oe(e.id,e.function_id),await K()}catch(e){console.error(`Failed to delete schedule`,e)}},$=()=>{V.value=!1,H.value=null,W.value={function_name:``,cron:`0 0 * * *`,timezone:L,enabled:!0},G.value={frequency:`day`,minute:0,hour:0,dayOfWeek:1,dayOfMonth:1},U.value=`simple`};return a(()=>{K(),Ze(),q()}),(n,a)=>(e(),c(`div`,le,[s(`div`,ue,[a[12]||=s(`div`,null,[s(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` Scheduled Jobs `),s(`p`,{class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},` Run functions on a cron schedule. `)],-1),u(w,{onClick:a[0]||=e=>V.value=!0},{default:t(()=>[u(o(ce),{class:`w-4 h-4`}),a[11]||=p(` New Schedule `,-1)]),_:1})]),s(`div`,de,[s(`ul`,fe,[(e(!0),c(m,null,d(z.value,t=>(e(),c(`li`,{key:t.id,class:`px-4 py-3`},[s(`div`,pe,[s(`div`,me,[s(`div`,he,[s(`span`,ge,g(t.function_name),1),s(`span`,{class:l([`inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-medium border`,t.enabled?`bg-success-tint text-success-fg border-success-ring`:`bg-warning-tint text-warning-fg border-warning-ring`])},[(e(),f(i(t.enabled?o(_):o(v)),{class:`h-3 w-3 shrink-0`,"aria-hidden":`true`})),p(` `+g(t.enabled?`Active`:`Paused`),1)],2)]),s(`div`,_e,g(t.cron_expression),1),s(`div`,ve,[p(g(J(t.cron_expression))+` `,1),s(`span`,k,`· `+g(t.timezone||`UTC`),1)]),s(`div`,A,[s(`span`,null,`last `+g(t.last_run_at?Y(t.last_run_at):o(`—`)),1),s(`span`,null,`next `+g(t.next_run_at?Y(t.next_run_at):o(`—`)),1)])]),s(`div`,j,[u(D,{icon:t.enabled?o(O):o(y),title:t.enabled?`Pause`:`Resume`,onClick:e=>Z(t)},null,8,[`icon`,`title`,`onClick`]),u(D,{icon:o(b),title:`Edit`,onClick:e=>X(t)},null,8,[`icon`,`onClick`]),u(D,{icon:o(x),variant:`danger`,title:`Delete`,onClick:e=>Q(t)},null,8,[`icon`,`onClick`])])])]))),128)),z.value.length===0?(e(),c(`li`,M,[...a[13]||=[s(`p`,{class:`text-foreground-muted`},` No scheduled jobs yet. `,-1),s(`p`,{class:`text-foreground-muted text-xs mt-1`},` Create a schedule to run a function automatically. `,-1)]])):h(``,!0)]),s(`table`,N,[a[15]||=s(`thead`,{class:`text-xs text-foreground-muted uppercase bg-surface border-b border-border`},[s(`tr`,null,[s(`th`,{class:`px-6 py-3 font-medium`},` Function `),s(`th`,{class:`px-6 py-3 font-medium`},` Schedule `),s(`th`,{class:`px-6 py-3 font-medium hidden sm:table-cell`},` Status `),s(`th`,{class:`px-6 py-3 font-medium hidden md:table-cell`},` Last Run `),s(`th`,{class:`px-6 py-3 font-medium hidden lg:table-cell`},` Next Run `),s(`th`,{class:`px-6 py-3 font-medium text-right`},` Actions `)])],-1),s(`tbody`,P,[(e(!0),c(m,null,d(z.value,t=>(e(),c(`tr`,{key:t.id,class:`hover:bg-surface-hover transition-colors`},[s(`td`,F,g(t.function_name),1),s(`td`,ye,[s(`div`,be,[s(`span`,xe,g(t.cron_expression),1),s(`span`,Se,[p(g(J(t.cron_expression))+` `,1),s(`span`,Ce,`· `+g(t.timezone||`UTC`),1)])])]),s(`td`,we,[s(`span`,{class:l([`inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs font-medium border`,t.enabled?`bg-success-tint text-success-fg border-success-ring`:`bg-warning-tint text-warning-fg border-warning-ring`])},[(e(),f(i(t.enabled?o(_):o(v)),{class:`h-3 w-3 shrink-0`,"aria-hidden":`true`})),p(` `+g(t.enabled?`Active`:`Paused`),1)],2)]),s(`td`,Te,g(t.last_run_at?Y(t.last_run_at):o(`—`)),1),s(`td`,Ee,g(t.next_run_at?Y(t.next_run_at):o(`—`)),1),s(`td`,De,[s(`div`,Oe,[u(D,{icon:t.enabled?o(O):o(y),title:t.enabled?`Pause`:`Resume`,onClick:e=>Z(t)},null,8,[`icon`,`title`,`onClick`]),u(D,{icon:o(b),title:`Edit`,onClick:e=>X(t)},null,8,[`icon`,`onClick`]),u(D,{icon:o(x),variant:`danger`,title:`Delete`,onClick:e=>Q(t)},null,8,[`icon`,`onClick`])])])]))),128)),z.value.length===0?(e(),c(`tr`,ke,[...a[14]||=[s(`td`,{colspan:`6`,class:`px-6 py-12 text-center`},[s(`p`,{class:`text-foreground-muted`},` No scheduled jobs yet. `),s(`p`,{class:`text-foreground-muted text-xs mt-1`},` Create a schedule to run a function automatically. `)],-1)]])):h(``,!0)])])]),u(se,{"model-value":V.value,title:H.value?`Edit Schedule`:`Create Schedule`,size:`lg`,"onUpdate:modelValue":a[10]||=e=>{e||$()}},{footer:t(()=>[u(w,{variant:`ghost`,onClick:$},{default:t(()=>[...a[36]||=[p(` Cancel `,-1)]]),_:1}),u(w,{disabled:!W.value.function_name||!W.value.cron,onClick:Qe},{default:t(()=>[p(g(H.value?`Update`:`Create`)+` Schedule `,1)]),_:1},8,[`disabled`])]),default:t(()=>[s(`div`,Ae,[s(`div`,null,[a[17]||=s(`label`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-2`},`Function`,-1),r(s(`select`,{"onUpdate:modelValue":a[1]||=e=>W.value.function_name=e,class:`w-full bg-background border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,disabled:!!H.value},[a[16]||=s(`option`,{value:``},` Select a function `,-1),(e(!0),c(m,null,d(B.value,t=>(e(),c(`option`,{key:t.name,value:t.name},g(t.name)+` (`+g(t.runtime)+`) `,9,Me))),128))],8,je),[[S,W.value.function_name]])]),s(`div`,null,[a[18]||=s(`label`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-2`},`Schedule Type`,-1),s(`div`,Ne,[(e(),c(m,null,d([`simple`,`advanced`],e=>s(`button`,{key:e,class:l([`flex-1 py-2 px-3 text-sm font-medium rounded transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background`,U.value===e?`bg-primary text-primary-foreground shadow-sm`:`text-foreground-muted hover:text-foreground`]),onClick:t=>U.value=e},g(e===`simple`?`Natural Language`:`Cron Expression`),11,Pe)),64))])]),U.value===`simple`?(e(),c(`div`,Fe,[s(`div`,Ie,[s(`div`,null,[a[20]||=s(`label`,{class:`text-xs font-medium text-foreground-muted block mb-1.5`},`Frequency`,-1),r(s(`select`,{"onUpdate:modelValue":a[2]||=e=>G.value.frequency=e,class:`w-full bg-background border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,onChange:q},[...a[19]||=[s(`option`,{value:`minute`},` Every Minute `,-1),s(`option`,{value:`hour`},` Hourly `,-1),s(`option`,{value:`day`},` Daily `,-1),s(`option`,{value:`week`},` Weekly `,-1),s(`option`,{value:`month`},` Monthly `,-1)]],544),[[S,G.value.frequency]])]),[`hour`,`day`,`week`,`month`].includes(G.value.frequency)?(e(),c(`div`,I,[a[21]||=s(`label`,{class:`text-xs font-medium text-foreground-muted block mb-1.5`},`At Minute`,-1),r(s(`input`,{"onUpdate:modelValue":a[3]||=e=>G.value.minute=e,type:`number`,min:`0`,max:`59`,class:`w-full bg-background border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,onInput:q},null,544),[[C,G.value.minute,void 0,{number:!0}]])])):h(``,!0),[`day`,`week`,`month`].includes(G.value.frequency)?(e(),c(`div`,Le,[a[22]||=s(`label`,{class:`text-xs font-medium text-foreground-muted block mb-1.5`},`At Hour`,-1),r(s(`input`,{"onUpdate:modelValue":a[4]||=e=>G.value.hour=e,type:`number`,min:`0`,max:`23`,class:`w-full bg-background border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,onInput:q},null,544),[[C,G.value.hour,void 0,{number:!0}]])])):h(``,!0),G.value.frequency===`week`?(e(),c(`div`,Re,[a[24]||=s(`label`,{class:`text-xs font-medium text-foreground-muted block mb-1.5`},`Day of Week`,-1),r(s(`select`,{"onUpdate:modelValue":a[5]||=e=>G.value.dayOfWeek=e,class:`w-full bg-background border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,onChange:q},[...a[23]||=[s(`option`,{value:`0`},` Sunday `,-1),s(`option`,{value:`1`},` Monday `,-1),s(`option`,{value:`2`},` Tuesday `,-1),s(`option`,{value:`3`},` Wednesday `,-1),s(`option`,{value:`4`},` Thursday `,-1),s(`option`,{value:`5`},` Friday `,-1),s(`option`,{value:`6`},` Saturday `,-1)]],544),[[S,G.value.dayOfWeek]])])):h(``,!0),G.value.frequency===`month`?(e(),c(`div`,ze,[a[25]||=s(`label`,{class:`text-xs font-medium text-foreground-muted block mb-1.5`},`Day of Month`,-1),r(s(`input`,{"onUpdate:modelValue":a[6]||=e=>G.value.dayOfMonth=e,type:`number`,min:`1`,max:`31`,class:`w-full bg-background border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,onInput:q},null,544),[[C,G.value.dayOfMonth,void 0,{number:!0}]])])):h(``,!0)]),s(`div`,Be,[a[26]||=s(`div`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide mb-2`},` Generated Expression `,-1),s(`div`,Ve,g(W.value.cron),1),s(`div`,He,g(J(W.value.cron)),1)])])):h(``,!0),U.value===`advanced`?(e(),c(`div`,Ue,[s(`div`,null,[a[27]||=s(`label`,{class:`text-xs font-medium text-foreground-muted block mb-1.5`},`Cron Expression`,-1),r(s(`input`,{"onUpdate:modelValue":a[7]||=e=>W.value.cron=e,placeholder:`* * * * *`,class:`w-full bg-background border border-border rounded-md px-3 py-2 text-sm font-mono text-foreground focus:outline-none focus:ring-1 focus:ring-white focus:border-white`},null,512),[[C,W.value.cron]]),a[28]||=s(`p`,{class:`text-xs text-foreground-muted mt-1.5`},` Format: minute hour day month weekday `,-1)]),s(`div`,We,[a[29]||=s(`div`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide mb-2`},` Preview `,-1),s(`div`,Ge,g(J(W.value.cron)),1)])])):h(``,!0),s(`div`,null,[a[34]||=s(`label`,{class:`block text-xs font-medium text-foreground-muted uppercase tracking-wide mb-1.5`},` Timezone `,-1),r(s(`select`,{"onUpdate:modelValue":a[8]||=e=>W.value.timezone=e,class:`w-full bg-surface-hover border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:border-white`},[(e(!0),c(m,null,d(o(Xe),t=>(e(),c(`option`,{key:t,value:t},g(t)+g(t===o(L)?` (your browser)`:``),9,Ke))),128))],512),[[S,W.value.timezone]]),s(`div`,qe,[a[30]||=p(` The cron expression is interpreted in this zone (e.g. `,-1),a[31]||=s(`code`,{class:`bg-surface px-1 rounded`},`0 9 * * *`,-1),a[32]||=p(` with timezone `,-1),s(`code`,Je,g(W.value.timezone),1),a[33]||=p(` fires at 9 AM local time every day. `,-1)])]),s(`div`,Ye,[r(s(`input`,{id:`enabled-toggle`,"onUpdate:modelValue":a[9]||=e=>W.value.enabled=e,type:`checkbox`,class:`w-4 h-4 text-primary bg-background border-border rounded focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background`},null,512),[[ne,W.value.enabled]]),a[35]||=s(`label`,{for:`enabled-toggle`,class:`text-sm font-medium text-foreground cursor-pointer`},` Enable schedule immediately `,-1)])])]),_:1},8,[`model-value`,`title`])]))}};export{L as default}; \ No newline at end of file +import{D as e,F as t,G as n,I as r,M as i,T as a,Z as o,c as s,d as c,gt as l,h as u,k as d,l as f,m as p,r as m,u as h,vt as g}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{n as _,t as v}from"./clock-CiH8bQVI.js";import{t as y}from"./play-CmQifd74.js";import{t as b}from"./square-pen-DCrjAsLy.js";import{t as x}from"./trash-2-DaeYqnW_.js";import{B as ee,It as S,L as te,Lt as C,Nt as ne,_t as w,at as T,c as re,gt as ie,i as ae,jt as E,p as oe}from"./index-DTqMKlE1.js";import{t as D}from"./IconButton-CsCZOqWo.js";import{t as se}from"./Modal-BAoZams6.js";var ce=E(`circle-plus`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M8 12h8`,key:`1wcyev`}],[`path`,{d:`M12 8v8`,key:`napkw2`}]]),O=E(`pause`,[[`rect`,{x:`14`,y:`3`,width:`5`,height:`18`,rx:`1`,key:`kaeet6`}],[`rect`,{x:`5`,y:`3`,width:`5`,height:`18`,rx:`1`,key:`1wsw3u`}]]),le={class:`space-y-6`},ue={class:`flex items-center justify-between`},de={class:`bg-background border border-border rounded-lg overflow-x-auto`},fe={class:`sm:hidden divide-y divide-border`},pe={class:`flex items-start justify-between gap-2`},me={class:`min-w-0 flex-1`},he={class:`flex items-center gap-2 flex-wrap`},ge={class:`font-medium text-foreground truncate`},_e={class:`mt-1 text-[11px] text-foreground font-mono break-all`},ve={class:`mt-0.5 text-[11px] text-foreground-muted`},k={class:`text-foreground-muted/70`},A={class:`mt-1 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-[11px] text-foreground-muted`},j={class:`flex items-center gap-1 shrink-0`},M={key:0,class:`px-6 py-12 text-center`},N={class:`hidden sm:table w-full text-sm text-left`},P={class:`divide-y divide-border`},F={class:`px-6 py-4 font-medium text-foreground max-w-[16rem] truncate`},ye={class:`px-6 py-4`},be={class:`flex flex-col gap-1`},xe={class:`text-foreground font-mono text-xs break-all`},Se={class:`text-foreground-muted text-[10px]`},Ce={class:`text-foreground-muted/70`},we={class:`px-6 py-4 hidden sm:table-cell`},Te={class:`px-6 py-4 text-foreground-muted text-xs hidden md:table-cell`},Ee={class:`px-6 py-4 text-foreground-muted text-xs hidden lg:table-cell`},De={class:`px-6 py-4 text-right`},Oe={class:`inline-flex items-center gap-1`},ke={key:0},Ae={class:`space-y-5`},je=[`disabled`],Me=[`value`],Ne={class:`flex gap-2 bg-background rounded-lg p-1 border border-border`},Pe=[`onClick`],Fe={key:0,class:`space-y-4`},Ie={class:`grid grid-cols-3 gap-3`},I={key:0},Le={key:1},Re={key:2},ze={key:3},Be={class:`bg-background border border-border rounded-lg p-4`},Ve={class:`font-mono text-sm text-foreground`},He={class:`text-xs text-foreground-muted mt-1`},Ue={key:1,class:`space-y-3`},We={class:`bg-background border border-border rounded-lg p-4`},Ge={class:`text-xs text-foreground`},Ke=[`value`],qe={class:`text-xs text-foreground-muted mt-1.5`},Je={class:`bg-surface px-1 rounded`},Ye={class:`flex items-center gap-3`},L={__name:`CronJobs`,setup(E){let L=ae(),Xe=[...new Set([L,`UTC`,`America/Los_Angeles`,`America/New_York`,`America/Chicago`,`America/Denver`,`America/Sao_Paulo`,`Europe/London`,`Europe/Berlin`,`Europe/Paris`,`Europe/Moscow`,`Africa/Lagos`,`Africa/Cairo`,`Africa/Johannesburg`,`Asia/Dubai`,`Asia/Kolkata`,`Asia/Singapore`,`Asia/Shanghai`,`Asia/Tokyo`,`Australia/Sydney`,`Pacific/Auckland`])],R=ie(),z=n([]),B=n([]),V=n(!1),H=n(null),U=n(`simple`),W=n({function_name:``,cron:`0 0 * * *`,timezone:L,enabled:!0}),G=n({frequency:`day`,minute:0,hour:0,dayOfWeek:1,dayOfMonth:1}),K=async()=>{try{let e=await te();z.value=e.data.schedules||[]}catch(e){console.error(`Failed to load cron jobs`,e)}},Ze=async()=>{try{let e=await ee();B.value=e.data.functions||[]}catch(e){console.error(`Failed to load functions`,e)}},q=()=>{let{frequency:e,minute:t,hour:n,dayOfWeek:r,dayOfMonth:i}=G.value;switch(e){case`minute`:W.value.cron=`* * * * *`;break;case`hour`:W.value.cron=`${t} * * * *`;break;case`day`:W.value.cron=`${t} ${n} * * *`;break;case`week`:W.value.cron=`${t} ${n} * * ${r}`;break;case`month`:W.value.cron=`${t} ${n} ${i} * *`}},J=e=>{if(!e)return`Invalid expression`;let t=e.trim().split(/\s+/);if(t.length!==5)return`Invalid format (use 5 fields)`;let[n,r,i,a,o]=t;return e===`* * * * *`?`Every minute`:n!==`*`&&r===`*`&&i===`*`&&a===`*`&&o===`*`?`Every hour at minute ${n}`:n!==`*`&&r!==`*`&&i===`*`&&a===`*`&&o===`*`?`Every day at ${r.padStart(2,`0`)}:${n.padStart(2,`0`)}`:n!==`*`&&r!==`*`&&i===`*`&&a===`*`&&o!==`*`?`Every ${[`Sunday`,`Monday`,`Tuesday`,`Wednesday`,`Thursday`,`Friday`,`Saturday`][o]} at ${r.padStart(2,`0`)}:${n.padStart(2,`0`)}`:n!==`*`&&r!==`*`&&i!==`*`&&a===`*`&&o===`*`?`On day ${i} of every month at ${r.padStart(2,`0`)}:${n.padStart(2,`0`)}`:`Custom: ${e}`},Y=e=>new Date(e).toLocaleString(`en-US`,{month:`short`,day:`numeric`,hour:`2-digit`,minute:`2-digit`}),Qe=async()=>{try{H.value?await T(H.value.id,{function_id:H.value.function_id,cron:W.value.cron,timezone:W.value.timezone,enabled:W.value.enabled}):await re(W.value.function_name,{cron:W.value.cron,timezone:W.value.timezone,enabled:W.value.enabled}),await K(),$()}catch(e){console.error(`Failed to save schedule`,e),R.notify({title:`Failed to save schedule`,danger:!0})}},X=e=>{H.value=e,W.value={function_name:e.function_name,cron:e.cron_expression,timezone:e.timezone||`UTC`,enabled:e.enabled},V.value=!0},Z=async e=>{try{await T(e.id,{function_id:e.function_id,enabled:!e.enabled}),await K()}catch(e){console.error(`Failed to toggle schedule`,e)}},Q=async e=>{if(await R.ask({title:`Delete schedule?`,message:`Cron schedule for "${e.function_name}" will be removed.`,confirmLabel:`Delete`,danger:!0}))try{await oe(e.id,e.function_id),await K()}catch(e){console.error(`Failed to delete schedule`,e)}},$=()=>{V.value=!1,H.value=null,W.value={function_name:``,cron:`0 0 * * *`,timezone:L,enabled:!0},G.value={frequency:`day`,minute:0,hour:0,dayOfWeek:1,dayOfMonth:1},U.value=`simple`};return a(()=>{K(),Ze(),q()}),(n,a)=>(e(),c(`div`,le,[s(`div`,ue,[a[12]||=s(`div`,null,[s(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` Scheduled Jobs `),s(`p`,{class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},` Run functions on a cron schedule. `)],-1),u(w,{onClick:a[0]||=e=>V.value=!0},{default:t(()=>[u(o(ce),{class:`w-4 h-4`}),a[11]||=p(` New Schedule `,-1)]),_:1})]),s(`div`,de,[s(`ul`,fe,[(e(!0),c(m,null,d(z.value,t=>(e(),c(`li`,{key:t.id,class:`px-4 py-3`},[s(`div`,pe,[s(`div`,me,[s(`div`,he,[s(`span`,ge,g(t.function_name),1),s(`span`,{class:l([`inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-medium border`,t.enabled?`bg-success-tint text-success-fg border-success-ring`:`bg-warning-tint text-warning-fg border-warning-ring`])},[(e(),f(i(t.enabled?o(_):o(v)),{class:`h-3 w-3 shrink-0`,"aria-hidden":`true`})),p(` `+g(t.enabled?`Active`:`Paused`),1)],2)]),s(`div`,_e,g(t.cron_expression),1),s(`div`,ve,[p(g(J(t.cron_expression))+` `,1),s(`span`,k,`· `+g(t.timezone||`UTC`),1)]),s(`div`,A,[s(`span`,null,`last `+g(t.last_run_at?Y(t.last_run_at):o(`—`)),1),s(`span`,null,`next `+g(t.next_run_at?Y(t.next_run_at):o(`—`)),1)])]),s(`div`,j,[u(D,{icon:t.enabled?o(O):o(y),title:t.enabled?`Pause`:`Resume`,onClick:e=>Z(t)},null,8,[`icon`,`title`,`onClick`]),u(D,{icon:o(b),title:`Edit`,onClick:e=>X(t)},null,8,[`icon`,`onClick`]),u(D,{icon:o(x),variant:`danger`,title:`Delete`,onClick:e=>Q(t)},null,8,[`icon`,`onClick`])])])]))),128)),z.value.length===0?(e(),c(`li`,M,[...a[13]||=[s(`p`,{class:`text-foreground-muted`},` No scheduled jobs yet. `,-1),s(`p`,{class:`text-foreground-muted text-xs mt-1`},` Create a schedule to run a function automatically. `,-1)]])):h(``,!0)]),s(`table`,N,[a[15]||=s(`thead`,{class:`text-xs text-foreground-muted uppercase bg-surface border-b border-border`},[s(`tr`,null,[s(`th`,{class:`px-6 py-3 font-medium`},` Function `),s(`th`,{class:`px-6 py-3 font-medium`},` Schedule `),s(`th`,{class:`px-6 py-3 font-medium hidden sm:table-cell`},` Status `),s(`th`,{class:`px-6 py-3 font-medium hidden md:table-cell`},` Last Run `),s(`th`,{class:`px-6 py-3 font-medium hidden lg:table-cell`},` Next Run `),s(`th`,{class:`px-6 py-3 font-medium text-right`},` Actions `)])],-1),s(`tbody`,P,[(e(!0),c(m,null,d(z.value,t=>(e(),c(`tr`,{key:t.id,class:`hover:bg-surface-hover transition-colors`},[s(`td`,F,g(t.function_name),1),s(`td`,ye,[s(`div`,be,[s(`span`,xe,g(t.cron_expression),1),s(`span`,Se,[p(g(J(t.cron_expression))+` `,1),s(`span`,Ce,`· `+g(t.timezone||`UTC`),1)])])]),s(`td`,we,[s(`span`,{class:l([`inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs font-medium border`,t.enabled?`bg-success-tint text-success-fg border-success-ring`:`bg-warning-tint text-warning-fg border-warning-ring`])},[(e(),f(i(t.enabled?o(_):o(v)),{class:`h-3 w-3 shrink-0`,"aria-hidden":`true`})),p(` `+g(t.enabled?`Active`:`Paused`),1)],2)]),s(`td`,Te,g(t.last_run_at?Y(t.last_run_at):o(`—`)),1),s(`td`,Ee,g(t.next_run_at?Y(t.next_run_at):o(`—`)),1),s(`td`,De,[s(`div`,Oe,[u(D,{icon:t.enabled?o(O):o(y),title:t.enabled?`Pause`:`Resume`,onClick:e=>Z(t)},null,8,[`icon`,`title`,`onClick`]),u(D,{icon:o(b),title:`Edit`,onClick:e=>X(t)},null,8,[`icon`,`onClick`]),u(D,{icon:o(x),variant:`danger`,title:`Delete`,onClick:e=>Q(t)},null,8,[`icon`,`onClick`])])])]))),128)),z.value.length===0?(e(),c(`tr`,ke,[...a[14]||=[s(`td`,{colspan:`6`,class:`px-6 py-12 text-center`},[s(`p`,{class:`text-foreground-muted`},` No scheduled jobs yet. `),s(`p`,{class:`text-foreground-muted text-xs mt-1`},` Create a schedule to run a function automatically. `)],-1)]])):h(``,!0)])])]),u(se,{"model-value":V.value,title:H.value?`Edit Schedule`:`Create Schedule`,size:`lg`,"onUpdate:modelValue":a[10]||=e=>{e||$()}},{footer:t(()=>[u(w,{variant:`ghost`,onClick:$},{default:t(()=>[...a[36]||=[p(` Cancel `,-1)]]),_:1}),u(w,{disabled:!W.value.function_name||!W.value.cron,onClick:Qe},{default:t(()=>[p(g(H.value?`Update`:`Create`)+` Schedule `,1)]),_:1},8,[`disabled`])]),default:t(()=>[s(`div`,Ae,[s(`div`,null,[a[17]||=s(`label`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-2`},`Function`,-1),r(s(`select`,{"onUpdate:modelValue":a[1]||=e=>W.value.function_name=e,class:`w-full bg-background border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,disabled:!!H.value},[a[16]||=s(`option`,{value:``},` Select a function `,-1),(e(!0),c(m,null,d(B.value,t=>(e(),c(`option`,{key:t.name,value:t.name},g(t.name)+` (`+g(t.runtime)+`) `,9,Me))),128))],8,je),[[S,W.value.function_name]])]),s(`div`,null,[a[18]||=s(`label`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-2`},`Schedule Type`,-1),s(`div`,Ne,[(e(),c(m,null,d([`simple`,`advanced`],e=>s(`button`,{key:e,class:l([`flex-1 py-2 px-3 text-sm font-medium rounded transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background`,U.value===e?`bg-primary text-primary-foreground shadow-sm`:`text-foreground-muted hover:text-foreground`]),onClick:t=>U.value=e},g(e===`simple`?`Natural Language`:`Cron Expression`),11,Pe)),64))])]),U.value===`simple`?(e(),c(`div`,Fe,[s(`div`,Ie,[s(`div`,null,[a[20]||=s(`label`,{class:`text-xs font-medium text-foreground-muted block mb-1.5`},`Frequency`,-1),r(s(`select`,{"onUpdate:modelValue":a[2]||=e=>G.value.frequency=e,class:`w-full bg-background border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,onChange:q},[...a[19]||=[s(`option`,{value:`minute`},` Every Minute `,-1),s(`option`,{value:`hour`},` Hourly `,-1),s(`option`,{value:`day`},` Daily `,-1),s(`option`,{value:`week`},` Weekly `,-1),s(`option`,{value:`month`},` Monthly `,-1)]],544),[[S,G.value.frequency]])]),[`hour`,`day`,`week`,`month`].includes(G.value.frequency)?(e(),c(`div`,I,[a[21]||=s(`label`,{class:`text-xs font-medium text-foreground-muted block mb-1.5`},`At Minute`,-1),r(s(`input`,{"onUpdate:modelValue":a[3]||=e=>G.value.minute=e,type:`number`,min:`0`,max:`59`,class:`w-full bg-background border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,onInput:q},null,544),[[C,G.value.minute,void 0,{number:!0}]])])):h(``,!0),[`day`,`week`,`month`].includes(G.value.frequency)?(e(),c(`div`,Le,[a[22]||=s(`label`,{class:`text-xs font-medium text-foreground-muted block mb-1.5`},`At Hour`,-1),r(s(`input`,{"onUpdate:modelValue":a[4]||=e=>G.value.hour=e,type:`number`,min:`0`,max:`23`,class:`w-full bg-background border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,onInput:q},null,544),[[C,G.value.hour,void 0,{number:!0}]])])):h(``,!0),G.value.frequency===`week`?(e(),c(`div`,Re,[a[24]||=s(`label`,{class:`text-xs font-medium text-foreground-muted block mb-1.5`},`Day of Week`,-1),r(s(`select`,{"onUpdate:modelValue":a[5]||=e=>G.value.dayOfWeek=e,class:`w-full bg-background border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,onChange:q},[...a[23]||=[s(`option`,{value:`0`},` Sunday `,-1),s(`option`,{value:`1`},` Monday `,-1),s(`option`,{value:`2`},` Tuesday `,-1),s(`option`,{value:`3`},` Wednesday `,-1),s(`option`,{value:`4`},` Thursday `,-1),s(`option`,{value:`5`},` Friday `,-1),s(`option`,{value:`6`},` Saturday `,-1)]],544),[[S,G.value.dayOfWeek]])])):h(``,!0),G.value.frequency===`month`?(e(),c(`div`,ze,[a[25]||=s(`label`,{class:`text-xs font-medium text-foreground-muted block mb-1.5`},`Day of Month`,-1),r(s(`input`,{"onUpdate:modelValue":a[6]||=e=>G.value.dayOfMonth=e,type:`number`,min:`1`,max:`31`,class:`w-full bg-background border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,onInput:q},null,544),[[C,G.value.dayOfMonth,void 0,{number:!0}]])])):h(``,!0)]),s(`div`,Be,[a[26]||=s(`div`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide mb-2`},` Generated Expression `,-1),s(`div`,Ve,g(W.value.cron),1),s(`div`,He,g(J(W.value.cron)),1)])])):h(``,!0),U.value===`advanced`?(e(),c(`div`,Ue,[s(`div`,null,[a[27]||=s(`label`,{class:`text-xs font-medium text-foreground-muted block mb-1.5`},`Cron Expression`,-1),r(s(`input`,{"onUpdate:modelValue":a[7]||=e=>W.value.cron=e,placeholder:`* * * * *`,class:`w-full bg-background border border-border rounded-md px-3 py-2 text-sm font-mono text-foreground focus:outline-none focus:ring-1 focus:ring-white focus:border-white`},null,512),[[C,W.value.cron]]),a[28]||=s(`p`,{class:`text-xs text-foreground-muted mt-1.5`},` Format: minute hour day month weekday `,-1)]),s(`div`,We,[a[29]||=s(`div`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide mb-2`},` Preview `,-1),s(`div`,Ge,g(J(W.value.cron)),1)])])):h(``,!0),s(`div`,null,[a[34]||=s(`label`,{class:`block text-xs font-medium text-foreground-muted uppercase tracking-wide mb-1.5`},` Timezone `,-1),r(s(`select`,{"onUpdate:modelValue":a[8]||=e=>W.value.timezone=e,class:`w-full bg-surface-hover border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:border-white`},[(e(!0),c(m,null,d(o(Xe),t=>(e(),c(`option`,{key:t,value:t},g(t)+g(t===o(L)?` (your browser)`:``),9,Ke))),128))],512),[[S,W.value.timezone]]),s(`div`,qe,[a[30]||=p(` The cron expression is interpreted in this zone (e.g. `,-1),a[31]||=s(`code`,{class:`bg-surface px-1 rounded`},`0 9 * * *`,-1),a[32]||=p(` with timezone `,-1),s(`code`,Je,g(W.value.timezone),1),a[33]||=p(` fires at 9 AM local time every day. `,-1)])]),s(`div`,Ye,[r(s(`input`,{id:`enabled-toggle`,"onUpdate:modelValue":a[9]||=e=>W.value.enabled=e,type:`checkbox`,class:`w-4 h-4 text-primary bg-background border-border rounded focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background`},null,512),[[ne,W.value.enabled]]),a[35]||=s(`label`,{for:`enabled-toggle`,class:`text-sm font-medium text-foreground cursor-pointer`},` Enable schedule immediately `,-1)])])]),_:1},8,[`model-value`,`title`])]))}};export{L as default}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/Dashboard-CEd_pnH7.js b/backend/internal/server/ui_dist/assets/Dashboard-rKNyCP0W.js similarity index 99% rename from backend/internal/server/ui_dist/assets/Dashboard-CEd_pnH7.js rename to backend/internal/server/ui_dist/assets/Dashboard-rKNyCP0W.js index 7d738619..37bc99ef 100644 --- a/backend/internal/server/ui_dist/assets/Dashboard-CEd_pnH7.js +++ b/backend/internal/server/ui_dist/assets/Dashboard-rKNyCP0W.js @@ -1 +1 @@ -import{D as e,E as t,F as n,T as r,Z as i,c as a,d as o,h as s,j as ee,k as te,l as ne,m as c,r as re,s as l,u as ie,v as u,vt as d}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{At as f,St as p,_t as ae,jt as m,kt as oe,n as se}from"./index-C11HmDXN.js";var ce=m(`snowflake`,[[`path`,{d:`m10 20-1.25-2.5L6 18`,key:`18frcb`}],[`path`,{d:`M10 4 8.75 6.5 6 6`,key:`7mghy3`}],[`path`,{d:`m14 20 1.25-2.5L18 18`,key:`1chtki`}],[`path`,{d:`m14 4 1.25 2.5L18 6`,key:`1b4wsy`}],[`path`,{d:`m17 21-3-6h-4`,key:`15hhxa`}],[`path`,{d:`m17 3-3 6 1.5 3`,key:`11697g`}],[`path`,{d:`M2 12h6.5L10 9`,key:`kv9z4n`}],[`path`,{d:`m20 10-1.5 2 1.5 2`,key:`1swlpi`}],[`path`,{d:`M22 12h-6.5L14 15`,key:`1mxi28`}],[`path`,{d:`m4 10 1.5 2L4 14`,key:`k9enpj`}],[`path`,{d:`m7 21 3-6-1.5-3`,key:`j8hb9u`}],[`path`,{d:`m7 3 3 6h4`,key:`1otusx`}]]),le=m(`trending-up`,[[`path`,{d:`M16 7h6v6`,key:`box55l`}],[`path`,{d:`m22 7-8.5 8.5-5-5L2 17`,key:`1t1m79`}]]),ue={class:`space-y-6`},h={class:`grid grid-cols-2 md:grid-cols-4 gap-4`},g={class:`grid grid-cols-1 lg:grid-cols-3 gap-4`},_={class:`bg-background border border-border rounded-lg p-5 lg:col-span-1`},v={class:`bg-background border border-border rounded-lg p-5 lg:col-span-2 space-y-5`},y={class:`grid grid-cols-2 gap-4 text-sm`},b={class:`text-lg font-mono text-white mt-0.5`},x={class:`text-lg font-mono text-white mt-0.5`},S={class:`text-foreground-muted text-sm`},C={class:`text-xs text-foreground-muted mt-0.5`},w={class:`space-y-2`},T={class:`flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-foreground-muted`},E={class:`flex items-center gap-1.5`},D={class:`flex items-center gap-1.5`},O={class:`grid grid-cols-1 md:grid-cols-2 gap-4`},k={class:`bg-background border border-border rounded-lg p-5 space-y-3`},A={class:`grid grid-cols-3 gap-3`},j={key:0,class:`text-xs text-danger-fg flex items-center gap-1.5 pt-1`},M={class:`bg-background border border-border rounded-lg p-5 space-y-3`},N={class:`grid grid-cols-3 gap-3`},P={key:0},F={class:`flex items-baseline justify-between mb-3`},I={class:`text-sm font-semibold text-white`},L={class:`grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4`},de={class:`flex items-start justify-between gap-2`},R={class:`min-w-0`},z={key:1,class:`truncate font-mono text-sm font-medium text-white`},B={class:`text-right shrink-0`},V={class:`text-xs font-mono text-white`},H={class:`grid grid-cols-3 gap-2`},fe={class:`grid grid-cols-2 gap-3 border-t border-border pt-3 text-xs`},pe={class:`mt-0.5 font-mono text-white`},me={class:`mt-0.5 font-mono text-white`},he={class:`text-foreground-muted`},ge={key:1,class:`bg-background border border-border rounded-lg p-8 text-center space-y-4`},U={__name:`Dashboard`,setup(m){let U=se(),W=l(()=>U.metrics||{}),_e=e=>U.poolHistory[e]||[],ve=e=>e==null?`—`:`${e.toFixed(1)}%`,ye=e=>e==null?`0`:e.toFixed(1),G=e=>{let t=e||0;return t>=1024?`${(t/1024).toFixed(1)} GB`:`${Math.round(t)} MB`},K=e=>{let t=Number(e)||0;return t>=1e6?`${(t/1e6).toFixed(1)}M`:t>=1e3?`${(t/1e3).toFixed(1)}k`:String(t)},q=l(()=>W.value.host?.mem_total_mb??0),J=l(()=>W.value.host?.mem_reserved_mb??0),be=l(()=>W.value.host?.mem_available_mb??0),Y=l(()=>Math.max(0,q.value-be.value)),X=l(()=>Math.max(0,q.value-Y.value)),xe=l(()=>q.value>0?Y.value/q.value*100:0);r(()=>U.connect()),t(()=>U.disconnect());let Z={props:{label:String,value:[String,Number],icon:Object},setup(e){return()=>u(`div`,{class:`bg-background border border-border rounded-lg p-5 flex flex-col h-full hover:border-primary/50 transition-colors group`},[u(`div`,{class:`flex items-center justify-between mb-3`},[u(`span`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide`},e.label),e.icon?u(e.icon,{class:`w-4 h-4 text-foreground-muted group-hover:text-primary`}):null]),u(`div`,{class:`text-2xl font-mono text-foreground leading-none`},String(e.value))])}},Se={props:{p50:Number,p95:Number,p99:Number},setup(e){return()=>{let t=[{label:`p50`,ms:e.p50,color:`bg-success/70`},{label:`p95`,ms:e.p95,color:`bg-warning/70`},{label:`p99`,ms:e.p99,color:`bg-danger/70`}],n=Math.max(e.p50||0,e.p95||0,e.p99||0,1);return u(`div`,{class:`space-y-2.5`},t.map(e=>{let t=e.ms==null?0:e.ms/n*100;return u(`div`,{class:`space-y-1`},[u(`div`,{class:`flex items-baseline justify-between text-xs`},[u(`span`,{class:`font-mono uppercase text-foreground-muted tracking-wider`},e.label),u(`span`,{class:`font-mono text-white`},e.ms==null?`—`:`${e.ms}ms`)]),u(`div`,{class:`h-1.5 bg-surface rounded overflow-hidden`},[u(`div`,{class:`h-full ${e.color} transition-[width] duration-500 ease-out`,style:{width:`${t.toFixed(1)}%`}})])])}))}}},Q={props:{label:String,value:[String,Number]},setup(e){return()=>u(`div`,{class:`bg-surface border border-border rounded p-3 flex flex-col h-full`},[u(`div`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},e.label),u(`div`,{class:`text-lg font-mono text-white mt-0.5`},String(e.value??0))])}},$={props:{label:String,value:[String,Number]},setup(e){return()=>u(`div`,{class:`bg-surface border border-border rounded p-2.5 flex flex-col h-full`},[u(`div`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},e.label),u(`div`,{class:`text-base font-mono text-white mt-0.5 leading-none`},String(e.value??0))])}},Ce={props:{total:{type:Number,required:!0},segments:{type:Array,required:!0}},setup(e){return()=>{let t=e.total>0?e.total:1;return u(`div`,{class:`h-2.5 bg-surface rounded overflow-hidden flex`,role:`img`,"aria-label":e.segments.map(t=>`${t.label}: ${t.value} of ${e.total}`).join(`; `)},e.segments.map(e=>u(`div`,{class:`h-full ${e.color}`,style:{width:`${(e.value/t*100).toFixed(2)}%`},title:`${e.label}: ${e.value}`})))}}},we={props:{points:{type:Array,default:()=>[]}},setup(e){return()=>{let t=e.points||[];if(t.length<2)return u(`div`,{class:`h-8 flex items-center text-xs text-foreground-muted`},`Collecting samples…`);let n=Math.max(...t,1),r=100/(t.length-1),i=t.map((e,t)=>{let i=(t*r).toFixed(2),a=(32-e/n*32).toFixed(2);return`${t===0?`M`:`L`}${i},${a}`}).join(` `);return u(`svg`,{viewBox:`0 0 100 32`,class:`w-full h-8 text-primary`,preserveAspectRatio:`none`},[u(`path`,{d:i,fill:`none`,stroke:`currentColor`,"stroke-width":`1.5`})])}}};return(t,r)=>{let l=ee(`router-link`);return e(),o(`div`,ue,[r[17]||=a(`div`,null,[a(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` System Overview `),a(`p`,{class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},` Live platform health and activity. `)],-1),a(`div`,h,[s(Z,{label:`Functions`,value:i(U).functionsCount,icon:i(oe)},null,8,[`value`,`icon`]),s(Z,{label:`In flight`,value:W.value.active_requests??0,icon:i(f)},null,8,[`value`,`icon`]),s(Z,{label:`Invocations`,value:K(W.value.totals?.invocations??0),icon:i(le)},null,8,[`value`,`icon`]),s(Z,{label:`Cold starts`,value:ve(W.value.rates?.cold_start_pct),icon:i(ce)},null,8,[`value`,`icon`])]),a(`div`,g,[a(`div`,_,[r[1]||=a(`div`,{class:`mb-3`},[a(`h2`,{class:`text-sm font-semibold text-white`},` Response time `),a(`div`,{class:`text-xs text-foreground-muted mt-1`},` Invocation latency by percentile. `)],-1),s(Se,{p50:W.value.latency_ms?.p50,p95:W.value.latency_ms?.p95,p99:W.value.latency_ms?.p99},null,8,[`p50`,`p95`,`p99`])]),a(`div`,v,[r[6]||=a(`div`,null,[a(`h2`,{class:`text-sm font-semibold text-white`},` Host machine `),a(`div`,{class:`text-xs text-foreground-muted mt-1`},` Capacity and current memory use. `)],-1),a(`div`,y,[a(`div`,null,[r[2]||=a(`div`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},` CPU cores `,-1),a(`div`,b,d(W.value.host?.num_cpu??`?`),1)]),a(`div`,null,[r[3]||=a(`div`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},` Memory in use `,-1),a(`div`,x,[c(d(G(Y.value))+` `,1),a(`span`,S,`/ `+d(G(q.value)),1)]),a(`div`,C,d(xe.value.toFixed(1))+`% used · `+d(G(J.value))+` reserved by warm pools `,1)])]),a(`div`,w,[s(Ce,{total:q.value,segments:[{label:`In use`,value:Y.value,color:`bg-info/70`},{label:`Free`,value:X.value,color:`bg-success/40`}]},null,8,[`total`,`segments`]),a(`div`,T,[a(`span`,E,[r[4]||=a(`span`,{class:`w-2 h-2 rounded-full bg-info/70`},null,-1),c(` `+d(G(Y.value))+` in use `,1)]),a(`span`,D,[r[5]||=a(`span`,{class:`w-2 h-2 rounded-full bg-success/40`},null,-1),c(` `+d(G(X.value))+` free `,1)]),a(`span`,null,d(G(J.value))+` reserved for warm pools `,1)])])])]),a(`div`,O,[a(`div`,k,[r[8]||=a(`div`,null,[a(`h2`,{class:`text-sm font-semibold text-white`},` Builds `),a(`div`,{class:`text-xs text-foreground-muted mt-1`},` Deployment work in progress. `)],-1),a(`div`,A,[s(Q,{label:`In queue`,value:W.value.build_queue?.pending??0},null,8,[`value`]),s(Q,{label:`Build workers`,value:W.value.build_queue?.workers??0},null,8,[`value`]),s(Q,{label:`Built so far`,value:K(W.value.totals?.builds??0)},null,8,[`value`])]),(W.value.totals?.build_errors??0)>0?(e(),o(`div`,j,[r[7]||=a(`span`,{class:`w-1.5 h-1.5 rounded-full bg-danger`},null,-1),c(` `+d(W.value.totals.build_errors)+` build`+d(W.value.totals.build_errors===1?` has`:`s have`)+` failed since start `,1)])):ie(``,!0)]),a(`div`,M,[r[9]||=a(`div`,null,[a(`h2`,{class:`text-sm font-semibold text-white`},` Sandbox activity `),a(`div`,{class:`text-xs text-foreground-muted mt-1`},` Current sandbox reuse and startup activity. `)],-1),a(`div`,N,[s(Q,{label:`Running now`,value:W.value.sandbox?.active??0},null,8,[`value`]),s(Q,{label:`Reused`,value:K(W.value.totals?.warm_hits??0)},null,8,[`value`]),s(Q,{label:`Spawned fresh`,value:K(W.value.totals?.cold_starts??0)},null,8,[`value`])])])]),(W.value.pools||[]).length?(e(),o(`div`,P,[a(`div`,F,[a(`div`,null,[a(`h2`,I,` Warm pools (`+d(W.value.pools.length)+`) `,1),r[10]||=a(`div`,{class:`text-xs text-foreground-muted mt-1`},` Ready sandboxes by function. `,-1)])]),a(`div`,L,[(e(!0),o(re,null,te(W.value.pools,t=>(e(),o(`div`,{key:t.function_id,class:`bg-background border border-border rounded-lg p-4 space-y-3`},[a(`div`,de,[a(`div`,R,[t.function_name?(e(),ne(l,{key:0,to:{name:`function-detail`,params:{name:t.function_name}},class:`block truncate text-sm font-medium text-white hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary`},{default:n(()=>[c(d(t.function_name),1)]),_:2},1032,[`to`])):(e(),o(`div`,z,d(t.function_id),1))]),a(`div`,B,[r[11]||=a(`div`,{class:`text-xs text-foreground-muted`},` Capacity `,-1),a(`div`,V,d(t.dynamic_max)+` max `,1)])]),a(`div`,H,[s($,{label:`Ready / target`,value:`${t.idle} / ${t.target}`},null,8,[`value`]),s($,{label:`Busy`,value:t.busy},null,8,[`value`]),s($,{label:`Calls / sec`,value:ye(t.rate_ewma)},null,8,[`value`])]),a(`div`,null,[s(we,{points:_e(t.function_id)},null,8,[`points`]),r[12]||=a(`div`,{class:`text-xs text-foreground-muted mt-1`},` Traffic, last 5 minutes `,-1)]),a(`div`,fe,[a(`div`,null,[r[13]||=a(`div`,{class:`text-foreground-muted`},` Avg latency `,-1),a(`div`,pe,d(t.latency_ewma_ms?.toFixed?.(1)??0)+` ms `,1)]),a(`div`,null,[r[14]||=a(`div`,{class:`text-foreground-muted`},` Avg memory / limit `,-1),a(`div`,me,[c(d(t.mem_used_avg_mb>0?`~`+Math.round(t.mem_used_avg_mb):i(`—`))+` `,1),a(`span`,he,`/ `+d(t.mem_limit_mb)+` MB`,1)])])])]))),128))])])):(e(),o(`div`,ge,[r[16]||=a(`div`,null,[a(`div`,{class:`text-sm text-white`},` No warm pools yet `),a(`div`,{class:`text-xs text-foreground-muted mt-1 max-w-prose mx-auto leading-body`},` Deploy a function to start collecting runtime metrics. `)],-1),a(`div`,null,[s(ae,{onClick:r[0]||=e=>t.$router.push(`/functions/new`)},{default:n(()=>[s(i(p),{class:`w-4 h-4`}),r[15]||=c(` Deploy your first function `,-1)]),_:1})])]))])}}};export{U as default}; \ No newline at end of file +import{D as e,E as t,F as n,T as r,Z as i,c as a,d as o,h as s,j as ee,k as te,l as ne,m as c,r as re,s as l,u as ie,v as u,vt as d}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{At as f,St as p,_t as ae,jt as m,kt as oe,n as se}from"./index-DTqMKlE1.js";var ce=m(`snowflake`,[[`path`,{d:`m10 20-1.25-2.5L6 18`,key:`18frcb`}],[`path`,{d:`M10 4 8.75 6.5 6 6`,key:`7mghy3`}],[`path`,{d:`m14 20 1.25-2.5L18 18`,key:`1chtki`}],[`path`,{d:`m14 4 1.25 2.5L18 6`,key:`1b4wsy`}],[`path`,{d:`m17 21-3-6h-4`,key:`15hhxa`}],[`path`,{d:`m17 3-3 6 1.5 3`,key:`11697g`}],[`path`,{d:`M2 12h6.5L10 9`,key:`kv9z4n`}],[`path`,{d:`m20 10-1.5 2 1.5 2`,key:`1swlpi`}],[`path`,{d:`M22 12h-6.5L14 15`,key:`1mxi28`}],[`path`,{d:`m4 10 1.5 2L4 14`,key:`k9enpj`}],[`path`,{d:`m7 21 3-6-1.5-3`,key:`j8hb9u`}],[`path`,{d:`m7 3 3 6h4`,key:`1otusx`}]]),le=m(`trending-up`,[[`path`,{d:`M16 7h6v6`,key:`box55l`}],[`path`,{d:`m22 7-8.5 8.5-5-5L2 17`,key:`1t1m79`}]]),ue={class:`space-y-6`},h={class:`grid grid-cols-2 md:grid-cols-4 gap-4`},g={class:`grid grid-cols-1 lg:grid-cols-3 gap-4`},_={class:`bg-background border border-border rounded-lg p-5 lg:col-span-1`},v={class:`bg-background border border-border rounded-lg p-5 lg:col-span-2 space-y-5`},y={class:`grid grid-cols-2 gap-4 text-sm`},b={class:`text-lg font-mono text-white mt-0.5`},x={class:`text-lg font-mono text-white mt-0.5`},S={class:`text-foreground-muted text-sm`},C={class:`text-xs text-foreground-muted mt-0.5`},w={class:`space-y-2`},T={class:`flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-foreground-muted`},E={class:`flex items-center gap-1.5`},D={class:`flex items-center gap-1.5`},O={class:`grid grid-cols-1 md:grid-cols-2 gap-4`},k={class:`bg-background border border-border rounded-lg p-5 space-y-3`},A={class:`grid grid-cols-3 gap-3`},j={key:0,class:`text-xs text-danger-fg flex items-center gap-1.5 pt-1`},M={class:`bg-background border border-border rounded-lg p-5 space-y-3`},N={class:`grid grid-cols-3 gap-3`},P={key:0},F={class:`flex items-baseline justify-between mb-3`},I={class:`text-sm font-semibold text-white`},L={class:`grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4`},de={class:`flex items-start justify-between gap-2`},R={class:`min-w-0`},z={key:1,class:`truncate font-mono text-sm font-medium text-white`},B={class:`text-right shrink-0`},V={class:`text-xs font-mono text-white`},H={class:`grid grid-cols-3 gap-2`},fe={class:`grid grid-cols-2 gap-3 border-t border-border pt-3 text-xs`},pe={class:`mt-0.5 font-mono text-white`},me={class:`mt-0.5 font-mono text-white`},he={class:`text-foreground-muted`},ge={key:1,class:`bg-background border border-border rounded-lg p-8 text-center space-y-4`},U={__name:`Dashboard`,setup(m){let U=se(),W=l(()=>U.metrics||{}),_e=e=>U.poolHistory[e]||[],ve=e=>e==null?`—`:`${e.toFixed(1)}%`,ye=e=>e==null?`0`:e.toFixed(1),G=e=>{let t=e||0;return t>=1024?`${(t/1024).toFixed(1)} GB`:`${Math.round(t)} MB`},K=e=>{let t=Number(e)||0;return t>=1e6?`${(t/1e6).toFixed(1)}M`:t>=1e3?`${(t/1e3).toFixed(1)}k`:String(t)},q=l(()=>W.value.host?.mem_total_mb??0),J=l(()=>W.value.host?.mem_reserved_mb??0),be=l(()=>W.value.host?.mem_available_mb??0),Y=l(()=>Math.max(0,q.value-be.value)),X=l(()=>Math.max(0,q.value-Y.value)),xe=l(()=>q.value>0?Y.value/q.value*100:0);r(()=>U.connect()),t(()=>U.disconnect());let Z={props:{label:String,value:[String,Number],icon:Object},setup(e){return()=>u(`div`,{class:`bg-background border border-border rounded-lg p-5 flex flex-col h-full hover:border-primary/50 transition-colors group`},[u(`div`,{class:`flex items-center justify-between mb-3`},[u(`span`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide`},e.label),e.icon?u(e.icon,{class:`w-4 h-4 text-foreground-muted group-hover:text-primary`}):null]),u(`div`,{class:`text-2xl font-mono text-foreground leading-none`},String(e.value))])}},Se={props:{p50:Number,p95:Number,p99:Number},setup(e){return()=>{let t=[{label:`p50`,ms:e.p50,color:`bg-success/70`},{label:`p95`,ms:e.p95,color:`bg-warning/70`},{label:`p99`,ms:e.p99,color:`bg-danger/70`}],n=Math.max(e.p50||0,e.p95||0,e.p99||0,1);return u(`div`,{class:`space-y-2.5`},t.map(e=>{let t=e.ms==null?0:e.ms/n*100;return u(`div`,{class:`space-y-1`},[u(`div`,{class:`flex items-baseline justify-between text-xs`},[u(`span`,{class:`font-mono uppercase text-foreground-muted tracking-wider`},e.label),u(`span`,{class:`font-mono text-white`},e.ms==null?`—`:`${e.ms}ms`)]),u(`div`,{class:`h-1.5 bg-surface rounded overflow-hidden`},[u(`div`,{class:`h-full ${e.color} transition-[width] duration-500 ease-out`,style:{width:`${t.toFixed(1)}%`}})])])}))}}},Q={props:{label:String,value:[String,Number]},setup(e){return()=>u(`div`,{class:`bg-surface border border-border rounded p-3 flex flex-col h-full`},[u(`div`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},e.label),u(`div`,{class:`text-lg font-mono text-white mt-0.5`},String(e.value??0))])}},$={props:{label:String,value:[String,Number]},setup(e){return()=>u(`div`,{class:`bg-surface border border-border rounded p-2.5 flex flex-col h-full`},[u(`div`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},e.label),u(`div`,{class:`text-base font-mono text-white mt-0.5 leading-none`},String(e.value??0))])}},Ce={props:{total:{type:Number,required:!0},segments:{type:Array,required:!0}},setup(e){return()=>{let t=e.total>0?e.total:1;return u(`div`,{class:`h-2.5 bg-surface rounded overflow-hidden flex`,role:`img`,"aria-label":e.segments.map(t=>`${t.label}: ${t.value} of ${e.total}`).join(`; `)},e.segments.map(e=>u(`div`,{class:`h-full ${e.color}`,style:{width:`${(e.value/t*100).toFixed(2)}%`},title:`${e.label}: ${e.value}`})))}}},we={props:{points:{type:Array,default:()=>[]}},setup(e){return()=>{let t=e.points||[];if(t.length<2)return u(`div`,{class:`h-8 flex items-center text-xs text-foreground-muted`},`Collecting samples…`);let n=Math.max(...t,1),r=100/(t.length-1),i=t.map((e,t)=>{let i=(t*r).toFixed(2),a=(32-e/n*32).toFixed(2);return`${t===0?`M`:`L`}${i},${a}`}).join(` `);return u(`svg`,{viewBox:`0 0 100 32`,class:`w-full h-8 text-primary`,preserveAspectRatio:`none`},[u(`path`,{d:i,fill:`none`,stroke:`currentColor`,"stroke-width":`1.5`})])}}};return(t,r)=>{let l=ee(`router-link`);return e(),o(`div`,ue,[r[17]||=a(`div`,null,[a(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` System Overview `),a(`p`,{class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},` Live platform health and activity. `)],-1),a(`div`,h,[s(Z,{label:`Functions`,value:i(U).functionsCount,icon:i(oe)},null,8,[`value`,`icon`]),s(Z,{label:`In flight`,value:W.value.active_requests??0,icon:i(f)},null,8,[`value`,`icon`]),s(Z,{label:`Invocations`,value:K(W.value.totals?.invocations??0),icon:i(le)},null,8,[`value`,`icon`]),s(Z,{label:`Cold starts`,value:ve(W.value.rates?.cold_start_pct),icon:i(ce)},null,8,[`value`,`icon`])]),a(`div`,g,[a(`div`,_,[r[1]||=a(`div`,{class:`mb-3`},[a(`h2`,{class:`text-sm font-semibold text-white`},` Response time `),a(`div`,{class:`text-xs text-foreground-muted mt-1`},` Invocation latency by percentile. `)],-1),s(Se,{p50:W.value.latency_ms?.p50,p95:W.value.latency_ms?.p95,p99:W.value.latency_ms?.p99},null,8,[`p50`,`p95`,`p99`])]),a(`div`,v,[r[6]||=a(`div`,null,[a(`h2`,{class:`text-sm font-semibold text-white`},` Host machine `),a(`div`,{class:`text-xs text-foreground-muted mt-1`},` Capacity and current memory use. `)],-1),a(`div`,y,[a(`div`,null,[r[2]||=a(`div`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},` CPU cores `,-1),a(`div`,b,d(W.value.host?.num_cpu??`?`),1)]),a(`div`,null,[r[3]||=a(`div`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},` Memory in use `,-1),a(`div`,x,[c(d(G(Y.value))+` `,1),a(`span`,S,`/ `+d(G(q.value)),1)]),a(`div`,C,d(xe.value.toFixed(1))+`% used · `+d(G(J.value))+` reserved by warm pools `,1)])]),a(`div`,w,[s(Ce,{total:q.value,segments:[{label:`In use`,value:Y.value,color:`bg-info/70`},{label:`Free`,value:X.value,color:`bg-success/40`}]},null,8,[`total`,`segments`]),a(`div`,T,[a(`span`,E,[r[4]||=a(`span`,{class:`w-2 h-2 rounded-full bg-info/70`},null,-1),c(` `+d(G(Y.value))+` in use `,1)]),a(`span`,D,[r[5]||=a(`span`,{class:`w-2 h-2 rounded-full bg-success/40`},null,-1),c(` `+d(G(X.value))+` free `,1)]),a(`span`,null,d(G(J.value))+` reserved for warm pools `,1)])])])]),a(`div`,O,[a(`div`,k,[r[8]||=a(`div`,null,[a(`h2`,{class:`text-sm font-semibold text-white`},` Builds `),a(`div`,{class:`text-xs text-foreground-muted mt-1`},` Deployment work in progress. `)],-1),a(`div`,A,[s(Q,{label:`In queue`,value:W.value.build_queue?.pending??0},null,8,[`value`]),s(Q,{label:`Build workers`,value:W.value.build_queue?.workers??0},null,8,[`value`]),s(Q,{label:`Built so far`,value:K(W.value.totals?.builds??0)},null,8,[`value`])]),(W.value.totals?.build_errors??0)>0?(e(),o(`div`,j,[r[7]||=a(`span`,{class:`w-1.5 h-1.5 rounded-full bg-danger`},null,-1),c(` `+d(W.value.totals.build_errors)+` build`+d(W.value.totals.build_errors===1?` has`:`s have`)+` failed since start `,1)])):ie(``,!0)]),a(`div`,M,[r[9]||=a(`div`,null,[a(`h2`,{class:`text-sm font-semibold text-white`},` Sandbox activity `),a(`div`,{class:`text-xs text-foreground-muted mt-1`},` Current sandbox reuse and startup activity. `)],-1),a(`div`,N,[s(Q,{label:`Running now`,value:W.value.sandbox?.active??0},null,8,[`value`]),s(Q,{label:`Reused`,value:K(W.value.totals?.warm_hits??0)},null,8,[`value`]),s(Q,{label:`Spawned fresh`,value:K(W.value.totals?.cold_starts??0)},null,8,[`value`])])])]),(W.value.pools||[]).length?(e(),o(`div`,P,[a(`div`,F,[a(`div`,null,[a(`h2`,I,` Warm pools (`+d(W.value.pools.length)+`) `,1),r[10]||=a(`div`,{class:`text-xs text-foreground-muted mt-1`},` Ready sandboxes by function. `,-1)])]),a(`div`,L,[(e(!0),o(re,null,te(W.value.pools,t=>(e(),o(`div`,{key:t.function_id,class:`bg-background border border-border rounded-lg p-4 space-y-3`},[a(`div`,de,[a(`div`,R,[t.function_name?(e(),ne(l,{key:0,to:{name:`function-detail`,params:{name:t.function_name}},class:`block truncate text-sm font-medium text-white hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary`},{default:n(()=>[c(d(t.function_name),1)]),_:2},1032,[`to`])):(e(),o(`div`,z,d(t.function_id),1))]),a(`div`,B,[r[11]||=a(`div`,{class:`text-xs text-foreground-muted`},` Capacity `,-1),a(`div`,V,d(t.dynamic_max)+` max `,1)])]),a(`div`,H,[s($,{label:`Ready / target`,value:`${t.idle} / ${t.target}`},null,8,[`value`]),s($,{label:`Busy`,value:t.busy},null,8,[`value`]),s($,{label:`Calls / sec`,value:ye(t.rate_ewma)},null,8,[`value`])]),a(`div`,null,[s(we,{points:_e(t.function_id)},null,8,[`points`]),r[12]||=a(`div`,{class:`text-xs text-foreground-muted mt-1`},` Traffic, last 5 minutes `,-1)]),a(`div`,fe,[a(`div`,null,[r[13]||=a(`div`,{class:`text-foreground-muted`},` Avg latency `,-1),a(`div`,pe,d(t.latency_ewma_ms?.toFixed?.(1)??0)+` ms `,1)]),a(`div`,null,[r[14]||=a(`div`,{class:`text-foreground-muted`},` Avg memory / limit `,-1),a(`div`,me,[c(d(t.mem_used_avg_mb>0?`~`+Math.round(t.mem_used_avg_mb):i(`—`))+` `,1),a(`span`,he,`/ `+d(t.mem_limit_mb)+` MB`,1)])])])]))),128))])])):(e(),o(`div`,ge,[r[16]||=a(`div`,null,[a(`div`,{class:`text-sm text-white`},` No warm pools yet `),a(`div`,{class:`text-xs text-foreground-muted mt-1 max-w-prose mx-auto leading-body`},` Deploy a function to start collecting runtime metrics. `)],-1),a(`div`,null,[s(ae,{onClick:r[0]||=e=>t.$router.push(`/functions/new`)},{default:n(()=>[s(i(p),{class:`w-4 h-4`}),r[15]||=c(` Deploy your first function `,-1)]),_:1})])]))])}}};export{U as default}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/Deployments-DCd1MNSh.js b/backend/internal/server/ui_dist/assets/Deployments-CRPgafhC.js similarity index 96% rename from backend/internal/server/ui_dist/assets/Deployments-DCd1MNSh.js rename to backend/internal/server/ui_dist/assets/Deployments-CRPgafhC.js index caa053b9..7d7515ea 100644 --- a/backend/internal/server/ui_dist/assets/Deployments-DCd1MNSh.js +++ b/backend/internal/server/ui_dist/assets/Deployments-CRPgafhC.js @@ -1,3 +1,3 @@ -import{C as e,D as t,F as n,G as r,P as i,T as ee,Z as a,c as o,d as s,gt as c,h as l,j as te,k as u,l as d,m as f,r as p,s as m,u as h,v as g,vt as _}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{n as ne}from"./clock-Dl3hMdU8.js";import{n as re,t as v}from"./git-compare-Dh2_YhZP.js";import{t as ie}from"./refresh-cw-Bn_tp4gH.js";import{t as y}from"./rotate-ccw-DBtrGGiP.js";import{B as ae,Bt as b,R as oe,_t as x,b as S,et as se,gt as ce,pt as le,r as ue,x as de}from"./index-C11HmDXN.js";import{t as fe}from"./Drawer-ZggviMzu.js";import{t as C}from"./StatusBadge-BOCx3ovI.js";import{t as pe}from"./rollbackDiff-DsaWcdbl.js";var me={class:`space-y-6`},he={class:`flex items-center justify-between`},ge={class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},_e={class:`flex items-center gap-2`},ve={key:0,class:`border-y border-border py-3 flex items-center gap-3`},ye={class:`flex-1 min-w-0`},be={class:`flex items-center gap-2 flex-wrap`},xe={class:`text-xs px-2 py-0.5 rounded bg-success/15 text-success border border-success/30 font-mono`},Se={key:0,class:`text-xs px-2 py-0.5 rounded bg-warning-tint text-warning-fg border border-warning-ring`},Ce={class:`text-xs text-foreground-muted mt-1 font-mono truncate`},we={key:1,class:`bg-danger-tint border border-danger-ring rounded p-3 text-xs text-danger-fg`,role:`alert`},Te={class:`bg-background border border-border rounded-lg overflow-x-auto`},Ee={class:`sm:hidden divide-y divide-border`},De=[`onClick`],Oe={class:`flex items-start justify-between gap-2`},ke={class:`min-w-0 flex-1`},Ae={class:`flex items-center gap-2 flex-wrap`},je={key:0,class:`px-1.5 py-0.5 rounded text-xs bg-success-tint text-success-fg border border-success-ring`},Me={class:`mt-1 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-xs text-foreground-muted`},Ne={key:0,class:`font-mono`},Pe={key:1},Fe={key:0,class:`px-6 py-8 text-center text-sm text-foreground-muted`},Ie={class:`hidden sm:table w-full text-sm text-left`},Le={class:`divide-y divide-border`},Re=[`onClick`],ze={class:`px-6 py-4 font-mono text-xs`},Be={class:`flex items-center gap-2`},Ve={key:0,class:`px-1.5 py-0.5 rounded text-xs bg-success-tint text-success-fg border border-success-ring normal-case`},He={class:`px-6 py-4 text-foreground`},Ue={class:`px-6 py-4`},We={class:`px-6 py-4 text-foreground-muted text-xs hidden md:table-cell`},Ge={class:`px-6 py-4 text-foreground-muted font-mono text-xs hidden sm:table-cell`},Ke={class:`px-6 py-4 text-foreground-muted font-mono text-xs hidden xl:table-cell`},qe={class:`inline-flex items-center gap-2 justify-end`},Je={key:2,class:`text-foreground-muted/50`},Ye={key:3,class:`text-foreground-muted/30`},Xe={key:0},Ze={key:0,class:`p-6 text-sm text-foreground-muted`},Qe={key:1,class:`p-5 space-y-4`},$e={class:`flex items-center gap-2 flex-wrap`},et={key:0,class:`inline-flex items-center px-2.5 py-1 rounded text-xs border bg-background font-mono text-foreground-muted`},tt={class:`grid grid-cols-2 gap-3 text-sm`},nt={key:0},rt={class:`bg-danger-tint border border-danger-ring rounded p-3 text-xs text-danger-fg font-mono whitespace-pre-wrap break-words`},it={class:`flex items-center justify-between mb-2`},at={key:0,class:`text-xs text-success-fg`},ot={class:`bg-surface border border-border rounded p-3 text-xs text-foreground font-mono overflow-auto max-h-96 whitespace-pre-wrap break-words`},st=Object.assign({name:`DeploymentsView`},{__name:`Deployments`,setup(st){let w=ce(),ct=le(),T=m(()=>ct.params.name),E=r(null),D=r(null),O=r([]),k=r(!1),A=r(``),j=r(!1),M=e=>e&&e.status===`succeeded`&&e.code_hash&&!I(e),N=m(()=>O.value.find(e=>I(e))?.id||null),P=e=>e&&e.status===`succeeded`&&e.code_hash&&!I(e)&&N.value,F=async e=>{if(!E.value||!e?.id||j.value)return;let t=(e.code_hash||``).slice(0,12),n=`Code hash ${t}. Current ${D.value?`v`+D.value.version:`version`} stays in history.`;try{let r=(await S(e.id))?.data?.snapshot;if(r&&D.value){let i=pe(D.value,r);n=i.length?`Rolling back to v${e.version} (code ${t}) will also change:\n\n${i.join(` +import{C as e,D as t,F as n,G as r,P as i,T as ee,Z as a,c as o,d as s,gt as c,h as l,j as te,k as u,l as d,m as f,r as p,s as m,u as h,v as g,vt as _}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{n as ne}from"./clock-CiH8bQVI.js";import{n as re,t as v}from"./git-compare-Co883xFS.js";import{t as ie}from"./refresh-cw-Cn8qkf-v.js";import{t as y}from"./rotate-ccw-DWwjKCqh.js";import{B as ae,Bt as b,R as oe,_t as x,b as S,et as se,gt as ce,pt as le,r as ue,x as de}from"./index-DTqMKlE1.js";import{t as fe}from"./Drawer-B98TBytl.js";import{t as C}from"./StatusBadge-Baoe7YAb.js";import{t as pe}from"./rollbackDiff-DsaWcdbl.js";var me={class:`space-y-6`},he={class:`flex items-center justify-between`},ge={class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},_e={class:`flex items-center gap-2`},ve={key:0,class:`border-y border-border py-3 flex items-center gap-3`},ye={class:`flex-1 min-w-0`},be={class:`flex items-center gap-2 flex-wrap`},xe={class:`text-xs px-2 py-0.5 rounded bg-success/15 text-success border border-success/30 font-mono`},Se={key:0,class:`text-xs px-2 py-0.5 rounded bg-warning-tint text-warning-fg border border-warning-ring`},Ce={class:`text-xs text-foreground-muted mt-1 font-mono truncate`},we={key:1,class:`bg-danger-tint border border-danger-ring rounded p-3 text-xs text-danger-fg`,role:`alert`},Te={class:`bg-background border border-border rounded-lg overflow-x-auto`},Ee={class:`sm:hidden divide-y divide-border`},De=[`onClick`],Oe={class:`flex items-start justify-between gap-2`},ke={class:`min-w-0 flex-1`},Ae={class:`flex items-center gap-2 flex-wrap`},je={key:0,class:`px-1.5 py-0.5 rounded text-xs bg-success-tint text-success-fg border border-success-ring`},Me={class:`mt-1 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-xs text-foreground-muted`},Ne={key:0,class:`font-mono`},Pe={key:1},Fe={key:0,class:`px-6 py-8 text-center text-sm text-foreground-muted`},Ie={class:`hidden sm:table w-full text-sm text-left`},Le={class:`divide-y divide-border`},Re=[`onClick`],ze={class:`px-6 py-4 font-mono text-xs`},Be={class:`flex items-center gap-2`},Ve={key:0,class:`px-1.5 py-0.5 rounded text-xs bg-success-tint text-success-fg border border-success-ring normal-case`},He={class:`px-6 py-4 text-foreground`},Ue={class:`px-6 py-4`},We={class:`px-6 py-4 text-foreground-muted text-xs hidden md:table-cell`},Ge={class:`px-6 py-4 text-foreground-muted font-mono text-xs hidden sm:table-cell`},Ke={class:`px-6 py-4 text-foreground-muted font-mono text-xs hidden xl:table-cell`},qe={class:`inline-flex items-center gap-2 justify-end`},Je={key:2,class:`text-foreground-muted/50`},Ye={key:3,class:`text-foreground-muted/30`},Xe={key:0},Ze={key:0,class:`p-6 text-sm text-foreground-muted`},Qe={key:1,class:`p-5 space-y-4`},$e={class:`flex items-center gap-2 flex-wrap`},et={key:0,class:`inline-flex items-center px-2.5 py-1 rounded text-xs border bg-background font-mono text-foreground-muted`},tt={class:`grid grid-cols-2 gap-3 text-sm`},nt={key:0},rt={class:`bg-danger-tint border border-danger-ring rounded p-3 text-xs text-danger-fg font-mono whitespace-pre-wrap break-words`},it={class:`flex items-center justify-between mb-2`},at={key:0,class:`text-xs text-success-fg`},ot={class:`bg-surface border border-border rounded p-3 text-xs text-foreground font-mono overflow-auto max-h-96 whitespace-pre-wrap break-words`},st=Object.assign({name:`DeploymentsView`},{__name:`Deployments`,setup(st){let w=ce(),ct=le(),T=m(()=>ct.params.name),E=r(null),D=r(null),O=r([]),k=r(!1),A=r(``),j=r(!1),M=e=>e&&e.status===`succeeded`&&e.code_hash&&!I(e),N=m(()=>O.value.find(e=>I(e))?.id||null),P=e=>e&&e.status===`succeeded`&&e.code_hash&&!I(e)&&N.value,F=async e=>{if(!E.value||!e?.id||j.value)return;let t=(e.code_hash||``).slice(0,12),n=`Code hash ${t}. Current ${D.value?`v`+D.value.version:`version`} stays in history.`;try{let r=(await S(e.id))?.data?.snapshot;if(r&&D.value){let i=pe(D.value,r);n=i.length?`Rolling back to v${e.version} (code ${t}) will also change:\n\n${i.join(` `)}\n\nSecrets keep their current values; they aren't part of the rollback.`:`Rolling back to v${e.version} (code ${t}). Settings and env are already identical, so only the code changes.`}}catch{}if(N.value&&N.value!==e.id&&(n+=`\n\nFull source diff: ${window.location.origin}/web/functions/${T.value}/diff?from=${e.id}&to=${N.value}`),await w.ask({title:`Restore v${e.version}?`,message:n,confirmLabel:`Rollback`})){j.value=!0;try{await se(E.value,{deployment_id:e.id}),await G()}catch(e){let t=e.response?.data?.error?.code||``,n=e.response?.data?.error?.message||e.message||`Rollback failed`;t===`VERSION_GCD`?w.notify({title:`Version unavailable`,message:`This version has been garbage-collected and can no longer be restored.\n\n${n}`,danger:!0}):w.notify({title:`Rollback failed`,message:n,danger:!0})}finally{j.value=!1}}},I=e=>D.value&&e.version===D.value.version&&e.status===`succeeded`,L=r(!1),R=r(null),z=r([]),B=r(!1),V=null,lt=m(()=>R.value?`Deployment · ${R.value.id?.substring(0,14)}`:`Deployment`),ut=m(()=>z.value.join(` `)),H=e=>e?new Date(e).toLocaleString():`—`,U={props:{label:String,value:[String,Number],mono:Boolean},setup(e){return()=>g(`div`,{class:`bg-surface border border-border rounded p-3`},[g(`div`,{class:`text-xs uppercase tracking-wider text-foreground-muted mb-1`},e.label),g(`div`,{class:[`text-sm text-white`,e.mono&&`font-mono text-xs`].filter(Boolean)},String(e.value))])}},W=async()=>{let e=((await ae()).data.functions||[]).find(e=>e.name===T.value);if(!e)throw Error(`Function "${T.value}" not found`);return e},G=async()=>{k.value=!0,A.value=``;try{let e=await W();E.value=e.id,D.value=e;let t=await oe(E.value,100);O.value=t.data.deployments||[]}catch(e){A.value=e.message||`Failed to load deployments`}finally{k.value=!1}},K=async e=>{R.value=e,L.value=!0,z.value=[],B.value=!1;try{let t=await S(e.id);R.value={...e,...t.data}}catch{}try{let t=await de(e.id,0,1e3);z.value=(t.data.logs||[]).map(q)}catch{}(e.status===`queued`||e.status===`building`)&&dt(e.id)},q=e=>`[${e.stream||`log`}] ${e.line}`,dt=e=>{J();let t=new EventSource(`/api/v1/deployments/${e}/stream`);V=t,B.value=!0,t.addEventListener(`log`,e=>{try{let t=JSON.parse(e.data);z.value.push(q(t))}catch{}}),t.addEventListener(`succeeded`,()=>J(!0)),t.addEventListener(`failed`,()=>J(!0)),t.onerror=()=>{t.readyState===EventSource.CLOSED&&J()}},J=(e=!1)=>{if(V){try{V.close()}catch{}V=null}B.value=!1,e&&R.value&&(S(R.value.id).then(e=>{R.value={...R.value,...e.data}}).catch(()=>{}),G())};i(L,e=>{e||J()});let Y=ue(),X=null,Z=()=>{X||=setTimeout(()=>{X=null,G()},300)},Q=null,$=null;return ee(()=>{G(),Q=Y.subscribe(`deployment`,Z),$=Y.subscribe(`function`,Z)}),e(()=>{J(),Q&&=(Q(),null),$&&=($(),null),X&&=(clearTimeout(X),null)}),(e,r)=>{let i=te(`router-link`);return t(),s(`div`,me,[o(`div`,he,[o(`div`,null,[r[5]||=o(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` Deployments `,-1),o(`p`,ge,[r[4]||=f(` History for `,-1),l(i,{to:`/functions/${T.value}`,class:`text-white underline`},{default:n(()=>[f(_(T.value),1)]),_:1},8,[`to`])])]),o(`div`,_e,[l(x,{variant:`secondary`,onClick:r[0]||=t=>e.$router.push(`/functions/${T.value}`)},{default:n(()=>[l(a(re),{class:`w-4 h-4 mr-2`}),r[6]||=f(` New version `,-1)]),_:1}),l(x,{variant:`secondary`,onClick:G},{default:n(()=>[l(a(ie),{class:c([`w-4 h-4 mr-2`,{"animate-spin":k.value}])},null,8,[`class`]),r[7]||=f(` Refresh `,-1)]),_:1})])]),D.value?(t(),s(`div`,ve,[l(a(ne),{class:`w-4 h-4 text-success-fg shrink-0`}),o(`div`,ye,[o(`div`,be,[r[8]||=o(`span`,{class:`text-sm text-white font-medium`},`Currently serving`,-1),o(`span`,xe,` v`+_(D.value.version),1),D.value.status===`active`?h(``,!0):(t(),s(`span`,Se,` status: `+_(D.value.status),1))]),o(`div`,Ce,` hash: `+_(D.value.code_hash||a(`—`))+` · runtime: `+_(D.value.runtime)+` · updated `+_(H(D.value.updated_at)),1)])])):h(``,!0),A.value?(t(),s(`div`,we,_(A.value),1)):h(``,!0),o(`div`,Te,[o(`ul`,Ee,[(t(!0),s(p,null,u(O.value,e=>(t(),s(`li`,{key:e.id,class:c([`px-4 py-3 cursor-pointer active:bg-surface-hover/50 transition-colors`,I(e)?`bg-success/5`:``]),onClick:t=>K(e)},[o(`div`,Oe,[o(`div`,ke,[o(`div`,Ae,[o(`span`,{class:c([`font-mono text-xs`,I(e)?`text-white font-semibold`:`text-foreground-muted`])},`v`+_(e.version),3),l(C,{status:e.status},null,8,[`status`]),I(e)?(t(),s(`span`,je,`Active`)):h(``,!0)]),o(`div`,Me,[o(`span`,null,_(H(e.submitted_at)),1),e.duration_ms==null?h(``,!0):(t(),s(`span`,Ne,_(e.duration_ms)+` ms`,1)),e.phase?(t(),s(`span`,Pe,_(e.phase),1)):h(``,!0)])]),o(`div`,{class:`shrink-0 flex items-center gap-1`,onClick:r[1]||=b(()=>{},[`stop`])},[P(e)?(t(),d(i,{key:0,to:{name:`function-diff`,params:{name:T.value},query:{from:e.id,to:N.value}},class:`text-foreground-muted hover:text-white text-xs flex items-center gap-1 px-2 py-1`,title:`Compare with active version`},{default:n(()=>[l(a(v),{class:`w-3 h-3`}),r[9]||=f(` Compare `,-1)]),_:1},8,[`to`])):h(``,!0),M(e)?(t(),d(x,{key:1,size:`xs`,variant:`ghost`,disabled:j.value,onClick:t=>F(e)},{default:n(()=>[l(a(y),{class:`w-3 h-3`}),r[10]||=f(` Rollback `,-1)]),_:1},8,[`disabled`,`onClick`])):h(``,!0)])])],10,De))),128)),!k.value&&O.value.length===0?(t(),s(`li`,Fe,` No deployments yet. `)):h(``,!0)]),o(`table`,Ie,[r[14]||=o(`thead`,{class:`text-xs text-foreground-muted uppercase bg-surface border-b border-border`},[o(`tr`,null,[o(`th`,{class:`px-6 py-3 font-medium`},` Version `),o(`th`,{class:`px-6 py-3 font-medium`},` Submitted `),o(`th`,{class:`px-6 py-3 font-medium`},` Status `),o(`th`,{class:`px-6 py-3 font-medium hidden md:table-cell`},` Phase `),o(`th`,{class:`px-6 py-3 font-medium hidden sm:table-cell`},` Duration `),o(`th`,{class:`px-6 py-3 font-medium hidden xl:table-cell`},` Deployment ID `),o(`th`,{class:`px-6 py-3 font-medium text-right`},` Actions `)])],-1),o(`tbody`,Le,[(t(!0),s(p,null,u(O.value,e=>(t(),s(`tr`,{key:e.id,class:c([`hover:bg-surface/50 transition-colors cursor-pointer`,I(e)?`bg-success/5`:``]),onClick:t=>K(e)},[o(`td`,ze,[o(`div`,Be,[o(`span`,{class:c([`text-white`,I(e)?`font-semibold`:`text-foreground-muted`])},`v`+_(e.version),3),I(e)?(t(),s(`span`,Ve,`Active`)):h(``,!0)])]),o(`td`,He,_(H(e.submitted_at)),1),o(`td`,Ue,[l(C,{status:e.status},null,8,[`status`])]),o(`td`,We,_(e.phase||a(`—`)),1),o(`td`,Ge,_(e.duration_ms==null?a(`—`):e.duration_ms+`ms`),1),o(`td`,Ke,_(e.id?.substring(0,14)),1),o(`td`,{class:`px-6 py-4 text-right text-xs`,onClick:r[2]||=b(()=>{},[`stop`])},[o(`div`,qe,[P(e)?(t(),d(i,{key:0,to:{name:`function-diff`,params:{name:T.value},query:{from:e.id,to:N.value}},class:`text-foreground-muted hover:text-white flex items-center gap-1`,title:`Compare with active version`},{default:n(()=>[l(a(v),{class:`w-3 h-3`}),r[11]||=f(` Compare `,-1)]),_:1},8,[`to`])):h(``,!0),M(e)?(t(),d(x,{key:1,size:`xs`,variant:`ghost`,disabled:j.value,onClick:t=>F(e)},{default:n(()=>[l(a(y),{class:`w-3 h-3`}),r[12]||=f(` Rollback `,-1)]),_:1},8,[`disabled`,`onClick`])):h(``,!0),!M(e)&&e.source===`rollback`?(t(),s(`span`,Je,`via rollback`)):!P(e)&&!M(e)?(t(),s(`span`,Ye,_(a(`—`)),1)):h(``,!0)])])],10,Re))),128)),!k.value&&O.value.length===0?(t(),s(`tr`,Xe,[...r[13]||=[o(`td`,{colspan:`7`,class:`px-6 py-8 text-center text-foreground-muted`},` No deployments yet. `,-1)]])):h(``,!0)])])]),l(fe,{modelValue:L.value,"onUpdate:modelValue":r[3]||=e=>L.value=e,title:lt.value,width:`640px`},{default:n(()=>[R.value?(t(),s(`div`,Qe,[o(`div`,$e,[l(C,{status:R.value.status},null,8,[`status`]),R.value.phase?(t(),s(`span`,et,_(R.value.phase),1)):h(``,!0)]),o(`div`,tt,[l(U,{label:`Version`,value:`v${R.value.version}`,mono:``},null,8,[`value`]),l(U,{label:`Duration`,value:R.value.duration_ms==null?a(`—`):R.value.duration_ms+` ms`},null,8,[`value`]),l(U,{label:`Submitted`,value:H(R.value.submitted_at)},null,8,[`value`]),l(U,{label:`Finished`,value:R.value.finished_at?H(R.value.finished_at):a(`—`)},null,8,[`value`])]),R.value.error_message?(t(),s(`div`,nt,[r[15]||=o(`h3`,{class:`text-xs uppercase tracking-wider text-foreground-muted mb-2`},` Error `,-1),o(`pre`,rt,_(R.value.error_message),1)])):h(``,!0),o(`div`,null,[o(`div`,it,[r[16]||=o(`h3`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},` Build log `,-1),B.value?(t(),s(`span`,at,`live`)):h(``,!0)]),o(`pre`,ot,_(ut.value||`(no logs available)`),1)])])):(t(),s(`div`,Ze,` Nothing selected. `))]),_:1},8,[`modelValue`,`title`])])}}});export{st as default}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/Docs-DjpX404o.js b/backend/internal/server/ui_dist/assets/Docs-BssSTQCS.js similarity index 65% rename from backend/internal/server/ui_dist/assets/Docs-DjpX404o.js rename to backend/internal/server/ui_dist/assets/Docs-BssSTQCS.js index b604ac26..e20168d6 100644 --- a/backend/internal/server/ui_dist/assets/Docs-DjpX404o.js +++ b/backend/internal/server/ui_dist/assets/Docs-BssSTQCS.js @@ -1,4 +1,4 @@ -import{C as e,D as t,F as n,G as r,M as i,T as a,Z as o,_ as s,c,d as l,gt as u,h as d,j as ee,k as f,l as p,m,p as h,r as g,s as _,u as te,v,vt as y}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as b}from"./check-DQm9OMOd.js";import{t as x}from"./chevron-right-BufLmSBw.js";import{t as S}from"./copy-Bt6Rv0BQ.js";import{t as C}from"./globe-C84kKXm0.js";import{t as w}from"./key-round-BmyLzD9o.js";import{t as T}from"./lock-QGBYN3Zu.js";import{t as ne}from"./variable-Doeff6Y-.js";import{t as re}from"./client-BF51V3uE.js";import{Dt as ie,Et as ae,Ot as oe,gt as se}from"./index-C11HmDXN.js";import{a as E,i as D,n as ce,r as le,t as O}from"./github-dark-D7LxIVih.js";import{t as k}from"./clipboard-D_9N0yai.js";import{r as ue,t as de}from"./aiPrompts-NjljN3ld.js";function fe(e){let t=e.regex,n=`HTTP/([32]|1\\.[01])`,r={className:`attribute`,begin:t.concat(`^`,/[A-Za-z][A-Za-z0-9-]*/,`(?=\\:\\s)`),starts:{contains:[{className:`punctuation`,begin:/: /,relevance:0,starts:{end:`$`,relevance:0}}]}},i=[r,{begin:`\\n\\n`,starts:{subLanguage:[],endsWithParent:!0}}];return{name:`HTTP`,aliases:[`https`],illegal:/\S/,contains:[{begin:`^(?=HTTP/([32]|1\\.[01]) \\d{3})`,end:/$/,contains:[{className:`meta`,begin:n},{className:`number`,begin:`\\b\\d{3}\\b`}],starts:{end:/\b\B/,illegal:/\S/,contains:i}},{begin:`(?=^[A-Z]+ (.*?) HTTP/([32]|1\\.[01])$)`,end:/$/,contains:[{className:`string`,begin:` `,end:` `,excludeBegin:!0,excludeEnd:!0},{className:`meta`,begin:n},{className:`keyword`,begin:`[A-Z]+`}],starts:{end:/\b\B/,illegal:/\S/,contains:i}},e.inherit(r,{relevance:0})]}}var pe={class:`space-y-12 pb-16`},me={class:`docs-hero`},he={class:`docs-hero-content`},ge={class:`docs-hero-row`},_e={class:`docs-hero-actions`},ve=[`title`,`aria-label`],A={class:`docs-hero-toc`,"aria-label":`Jump to docs section`},j=[`href`],M={class:`docs-hero-toc-num`},N={id:`handler`,class:`space-y-5 scroll-mt-6`},P={class:`doc-table-wrap`},F={class:`doc-table`},ye={class:`doc-cell-key`},be={class:`doc-cell-mono`},xe={class:`doc-cell-mono hidden sm:table-cell`},Se={class:`doc-cell-mono hidden md:table-cell`},Ce={id:`deploy`,class:`space-y-5 scroll-mt-6 border-t border-border pt-12`},we={class:`grid grid-cols-1 lg:grid-cols-2 gap-3`},Te={class:`space-y-2`},Ee={class:`space-y-2`},De={class:`space-y-2`},Oe={id:`config`,class:`space-y-5 scroll-mt-6 border-t border-border pt-12`},ke={class:`doc-table-wrap`},Ae={class:`doc-table`},je={class:`doc-cell-key whitespace-nowrap`},Me={class:`doc-cell-mono hidden sm:table-cell whitespace-nowrap`},Ne={class:`doc-cell-body`},Pe={class:`space-y-2`},Fe={class:`doc-details group`},Ie={class:`doc-details-summary`},Le={class:`doc-details-body`},Re={id:`sdk`,class:`space-y-5 scroll-mt-6 border-t border-border pt-12`},ze={class:`space-y-2`},Be={class:`space-y-2`},Ve={class:`space-y-2`},He={id:`schedules`,class:`space-y-5 scroll-mt-6 border-t border-border pt-12`},Ue={class:`doc-section-head`},We={class:`doc-lede`},Ge={id:`webhooks`,class:`space-y-5 scroll-mt-6 border-t border-border pt-12`},Ke={class:`doc-section-head`},qe={class:`doc-lede`},Je={class:`doc-table-wrap`},Ye={class:`doc-table`},Xe={class:`doc-cell-key whitespace-nowrap`},Ze={class:`doc-cell-body`},Qe={class:`space-y-2`},$e={id:`mcp`,class:`space-y-5 scroll-mt-6 border-t border-border pt-12`},et={class:`grid grid-cols-1 md:grid-cols-3 gap-3`},tt={class:`doc-card`},nt={class:`doc-card-body`},rt={class:`doc-chip break-all`},it={class:`doc-token-bar`},at={class:`flex items-center gap-2 min-w-0 flex-1`},ot={key:0,class:`text-sm text-foreground-muted truncate`},st={key:1,class:`text-sm text-success truncate`},ct={class:`doc-chip`},lt=[`disabled`],ut={class:`doc-details group`},dt={class:`doc-details-summary`},ft={class:`doc-details-body space-y-4`},pt={id:`generate`,class:`space-y-5 scroll-mt-6 border-t border-border pt-12`},mt={class:`ai-prompt-actions`},ht={key:0,class:`prompt-collapse-fade`,"aria-hidden":`true`},gt=[`aria-expanded`],_t={id:`tracing`,class:`space-y-5 scroll-mt-6 border-t border-border pt-12`},vt={class:`doc-table-wrap`},yt={class:`doc-table`},bt={class:`doc-cell-key whitespace-nowrap`},xt={class:`doc-cell-body`},St={id:`errors`,class:`space-y-5 scroll-mt-6 border-t border-border pt-12`},Ct={class:`doc-table-wrap`},wt={class:`doc-table`},Tt={class:`doc-cell-key whitespace-nowrap`},Et={class:`doc-cell-body`},Dt={id:`cli`,class:`space-y-5 scroll-mt-6 border-t border-border pt-12`},Ot={class:`doc-prose`},kt={class:`doc-table-wrap`},At={class:`doc-table`},jt={class:`doc-cell-key whitespace-nowrap`},Mt={class:`doc-cell-mono`},Nt={class:`doc-cell-body hidden md:table-cell`},Pt={class:`space-y-2`},Ft={class:`space-y-2`},It={class:`space-y-2`},Lt={class:`space-y-2`},Rt={class:`space-y-2`},zt=`# Available inside every running function — refresh per-invocation: +import{C as e,D as t,F as n,G as r,M as i,T as a,Z as o,_ as s,c,d as l,gt as u,h as d,j as ee,k as f,l as p,m,p as h,r as g,s as _,u as te,v,vt as y}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as b}from"./check-BNre7JFR.js";import{t as x}from"./chevron-right-CRxFsA9Y.js";import{t as S}from"./copy-BqdwwcxC.js";import{t as C}from"./globe-BnBar2lO.js";import{t as w}from"./key-round-BKXtbC85.js";import{t as T}from"./lock-CttMBTH5.js";import{t as ne}from"./variable-DrK2KZuk.js";import{t as re}from"./client-BF51V3uE.js";import{Dt as ie,Et as ae,Ot as oe,gt as se}from"./index-DTqMKlE1.js";import{a as E,i as D,n as ce,r as le,t as O}from"./github-dark-D7LxIVih.js";import{t as k}from"./clipboard-D_9N0yai.js";import{r as ue,t as de}from"./aiPrompts-XrsFCpj_.js";function fe(e){let t=e.regex,n=`HTTP/([32]|1\\.[01])`,r={className:`attribute`,begin:t.concat(`^`,/[A-Za-z][A-Za-z0-9-]*/,`(?=\\:\\s)`),starts:{contains:[{className:`punctuation`,begin:/: /,relevance:0,starts:{end:`$`,relevance:0}}]}},i=[r,{begin:`\\n\\n`,starts:{subLanguage:[],endsWithParent:!0}}];return{name:`HTTP`,aliases:[`https`],illegal:/\S/,contains:[{begin:`^(?=HTTP/([32]|1\\.[01]) \\d{3})`,end:/$/,contains:[{className:`meta`,begin:n},{className:`number`,begin:`\\b\\d{3}\\b`}],starts:{end:/\b\B/,illegal:/\S/,contains:i}},{begin:`(?=^[A-Z]+ (.*?) HTTP/([32]|1\\.[01])$)`,end:/$/,contains:[{className:`string`,begin:` `,end:` `,excludeBegin:!0,excludeEnd:!0},{className:`meta`,begin:n},{className:`keyword`,begin:`[A-Z]+`}],starts:{end:/\b\B/,illegal:/\S/,contains:i}},e.inherit(r,{relevance:0})]}}var pe={class:`space-y-12 pb-16`},me={class:`docs-hero`},he={class:`docs-hero-content`},ge={class:`docs-hero-row`},_e={class:`docs-hero-actions`},ve=[`title`,`aria-label`],A={class:`docs-hero-toc`,"aria-label":`Jump to docs section`},j=[`href`],M={class:`docs-hero-toc-num`},N={id:`handler`,class:`space-y-5 scroll-mt-6`},P={class:`doc-table-wrap`},F={class:`doc-table`},ye={class:`doc-cell-key`},be={class:`doc-cell-mono`},xe={class:`doc-cell-mono hidden sm:table-cell`},Se={class:`doc-cell-mono hidden md:table-cell`},Ce={id:`deploy`,class:`space-y-5 scroll-mt-6 border-t border-border pt-12`},we={class:`grid grid-cols-1 lg:grid-cols-2 gap-3`},Te={class:`space-y-2`},Ee={class:`space-y-2`},De={class:`space-y-2`},Oe={id:`config`,class:`space-y-5 scroll-mt-6 border-t border-border pt-12`},ke={class:`doc-table-wrap`},Ae={class:`doc-table`},je={class:`doc-cell-key whitespace-nowrap`},Me={class:`doc-cell-mono hidden sm:table-cell whitespace-nowrap`},Ne={class:`doc-cell-body`},Pe={class:`space-y-2`},Fe={class:`doc-details group`},Ie={class:`doc-details-summary`},Le={class:`doc-details-body`},Re={id:`sdk`,class:`space-y-5 scroll-mt-6 border-t border-border pt-12`},ze={class:`space-y-2`},Be={class:`space-y-2`},Ve={class:`space-y-2`},He={id:`schedules`,class:`space-y-5 scroll-mt-6 border-t border-border pt-12`},Ue={class:`doc-section-head`},We={class:`doc-lede`},Ge={id:`webhooks`,class:`space-y-5 scroll-mt-6 border-t border-border pt-12`},Ke={class:`doc-section-head`},qe={class:`doc-lede`},Je={class:`doc-table-wrap`},Ye={class:`doc-table`},Xe={class:`doc-cell-key whitespace-nowrap`},Ze={class:`doc-cell-body`},Qe={class:`space-y-2`},$e={id:`mcp`,class:`space-y-5 scroll-mt-6 border-t border-border pt-12`},et={class:`grid grid-cols-1 md:grid-cols-3 gap-3`},tt={class:`doc-card`},nt={class:`doc-card-body`},rt={class:`doc-chip break-all`},it={class:`doc-token-bar`},at={class:`flex items-center gap-2 min-w-0 flex-1`},ot={key:0,class:`text-sm text-foreground-muted truncate`},st={key:1,class:`text-sm text-success truncate`},ct={class:`doc-chip`},lt=[`disabled`],ut={class:`doc-details group`},dt={class:`doc-details-summary`},ft={class:`doc-details-body space-y-4`},pt={id:`generate`,class:`space-y-5 scroll-mt-6 border-t border-border pt-12`},mt={class:`ai-prompt-actions`},ht={key:0,class:`prompt-collapse-fade`,"aria-hidden":`true`},gt=[`aria-expanded`],_t={id:`tracing`,class:`space-y-5 scroll-mt-6 border-t border-border pt-12`},vt={class:`doc-table-wrap`},yt={class:`doc-table`},bt={class:`doc-cell-key whitespace-nowrap`},xt={class:`doc-cell-body`},St={id:`errors`,class:`space-y-5 scroll-mt-6 border-t border-border pt-12`},Ct={class:`doc-table-wrap`},wt={class:`doc-table`},Tt={class:`doc-cell-key whitespace-nowrap`},Et={class:`doc-cell-body`},Dt={id:`cli`,class:`space-y-5 scroll-mt-6 border-t border-border pt-12`},Ot={class:`doc-prose`},kt={class:`doc-table-wrap`},At={class:`doc-table`},jt={class:`doc-cell-key whitespace-nowrap`},Mt={class:`doc-cell-mono`},Nt={class:`doc-cell-body hidden md:table-cell`},Pt={class:`space-y-2`},Ft={class:`space-y-2`},It={class:`space-y-2`},Lt={class:`space-y-2`},Rt={class:`space-y-2`},zt=`# Available inside every running function — refresh per-invocation: ORVA_TRACE_ID=tr_3e39f6991c66f140577c6021da7dd13b # one per causal chain ORVA_SPAN_ID=sp_4ceba57f6b1c982e # this execution @@ -342,4 +342,4 @@ Auth: OAuth (auto-discovered)`}]),Dn=_(()=>{let e=JSON.stringify({url:L.value+`/ "disabled": false } } -}`}]),Z=s({name:`CodeBlock`,props:{code:{type:String,required:!0},lang:{type:String,default:``}},setup(e){let t=r(!1),n=async()=>{await k(e.code)&&(t.value=!0,setTimeout(()=>{t.value=!1},1200))},i=_(()=>{let t=(e.lang||``).toLowerCase();if(t&&E.getLanguage(t))try{return E.highlight(e.code,{language:t,ignoreIllegals:!0}).value}catch{}return e.code.replace(/&/g,`&`).replace(//g,`>`)});return()=>v(`div`,{class:`codeblock`},[v(`div`,{class:`codeblock-bar`},[v(`span`,{class:`codeblock-lang`},e.lang||``),v(`button`,{class:`codeblock-copy`,onClick:n,title:`Copy code`},[t.value?v(b,{class:`w-3 h-3`}):v(S,{class:`w-3 h-3`}),t.value?`Copied`:`Copy`])]),v(`pre`,{class:`codeblock-pre`},[v(`code`,{class:`hljs language-${(e.lang||`text`).toLowerCase()}`,innerHTML:i.value})])])}}),Q=s({name:`TabbedCode`,props:{tabs:{type:Array,required:!0},storageKey:{type:String,default:``}},setup(e){let t=(()=>{try{if(e.storageKey){let t=localStorage.getItem(e.storageKey);if(t&&e.tabs.some(e=>e.label===t))return t}}catch{}return e.tabs[0]?.label})(),n=r(t),i=t=>{n.value=t;try{e.storageKey&&localStorage.setItem(e.storageKey,t)}catch{}};return()=>{let t=e.tabs.find(e=>e.label===n.value)||e.tabs[0];return v(`div`,{class:`tabbed`},[v(`div`,{class:`tabbed-tabs`},e.tabs.map(e=>v(`button`,{key:e.label,class:[`tabbed-tab`,{active:e.label===n.value}],onClick:()=>i(e.label)},e.label))),t.note?v(`div`,{class:`tabbed-note`},t.note):null,v(Z,{code:t.code,lang:t.lang})])}}}),$=s({name:`DocsCallout`,props:{title:{type:String,default:``},icon:{type:[Object,Function],default:null}},setup(e,{slots:t}){return()=>v(`div`,{class:`callout`},[v(`div`,{class:`callout-head`},[e.icon?v(e.icon,{class:`callout-icon`}):null,e.title?v(`span`,null,e.title):null]),v(`div`,{class:`callout-body`},t.default?.())])}});return(e,r)=>{let a=ee(`router-link`);return t(),l(`div`,pe,[c(`header`,me,[c(`div`,he,[c(`div`,ge,[r[1]||=c(`div`,{class:`docs-hero-text`},[c(`h1`,{class:`docs-hero-title`},` Documentation `),c(`p`,{class:`docs-hero-sub`},` Build, deploy, and operate functions on Orva. `)],-1),c(`div`,_e,[c(`button`,{class:u([`docs-hero-copy-icon`,{copied:G.value}]),title:G.value?`Copied`:`Copy entire docs page as Markdown`,"aria-label":G.value?`Markdown copied to clipboard`:`Copy entire docs page as Markdown`,onClick:Sn},[G.value?(t(),p(o(b),{key:0,class:`w-4 h-4`})):(t(),p(o(S),{key:1,class:`w-4 h-4`}))],10,ve)])]),c(`nav`,A,[r[2]||=c(`span`,{class:`docs-hero-toc-label`},`Jump to`,-1),(t(),l(g,null,f(R,e=>c(`a`,{key:e.id,href:`#${e.id}`,class:u([`docs-hero-toc-link`,{active:z.value===e.id}])},[c(`span`,M,y(e.num),1),c(`span`,null,y(e.label),1)],10,j)),64))])])]),c(`section`,N,[r[4]||=c(`div`,{class:`doc-section-head`},[c(`span`,{class:`doc-section-num`},`01`),c(`div`,null,[c(`h2`,{class:`doc-section-title`},` Handler contract `),c(`p`,{class:`doc-lede`},` One exported function receives the inbound HTTP event and returns an HTTP-shaped response. The adapter handles serialization and headers. `)])],-1),d(o(Q),{tabs:an.value,"storage-key":`docs.handler`},null,8,[`tabs`]),r[5]||=h(`
Event shape
methodpathheadersquerybody
Response
{ statusCode, headers, body }

Non-string bodies are JSON-encoded by the adapter.

Runtime env
Env vars and secrets land in process.env / os.environ.
`,1),c(`div`,P,[c(`table`,F,[r[3]||=c(`thead`,null,[c(`tr`,null,[c(`th`,null,`Runtime`),c(`th`,null,`ID`),c(`th`,{class:`hidden sm:table-cell`},` Entrypoint `),c(`th`,{class:`hidden md:table-cell`},` Dependencies `)])],-1),c(`tbody`,null,[(t(),l(g,null,f(sn,e=>c(`tr`,{key:e.id},[c(`td`,ye,[(t(),p(i(e.icon),{class:`shrink-0`})),m(` `+y(e.name),1)]),c(`td`,be,y(e.id),1),c(`td`,xe,y(e.entry),1),c(`td`,Se,y(e.deps),1)])),64))])])])]),c(`section`,Ce,[r[10]||=h(`
02

Deploy & invoke

The dashboard handles day-to-day work; these calls are for CI and automation. Builds run async; poll /api/v1/deployments/<id> or stream /api/v1/deployments/<id>/stream until phase: done.

`,1),d(o(tn)),c(`div`,we,[c(`div`,Te,[r[6]||=c(`div`,{class:`doc-step-label`},[c(`span`,{class:`doc-step-num`},`1`),m(` Create the function row `)],-1),d(o(Z),{code:ln.value,lang:`bash`},null,8,[`code`])]),c(`div`,Ee,[r[7]||=c(`div`,{class:`doc-step-label`},[c(`span`,{class:`doc-step-num`},`2`),m(` Upload code `)],-1),d(o(Z),{code:un.value,lang:`bash`},null,8,[`code`])])]),c(`div`,De,[r[8]||=c(`div`,{class:`doc-microlabel`},` Invoke `,-1),d(o(Q),{tabs:on.value,"storage-key":`docs.invoke`},null,8,[`tabs`])]),d(o($),{icon:o(C),title:`Custom routes`},{default:n(()=>[...r[9]||=[m(` Attach a friendly path with `,-1),c(`code`,{class:`doc-chip`},`POST /api/v1/routes`,-1),m(`. Reserved prefixes: `,-1),c(`code`,{class:`doc-chip`},`/api/`,-1),c(`code`,{class:`doc-chip`},`/fn/`,-1),c(`code`,{class:`doc-chip`},`/mcp/`,-1),c(`code`,{class:`doc-chip`},`/web/`,-1),c(`code`,{class:`doc-chip`},`/_orva/`,-1),m(`. `,-1)]]),_:1},8,[`icon`])]),c(`section`,Oe,[r[14]||=c(`div`,{class:`doc-section-head`},[c(`span`,{class:`doc-section-num`},`03`),c(`div`,null,[c(`h2`,{class:`doc-section-title`},` Configuration reference `),c(`p`,{class:`doc-lede`},` Everything below lives on the function record. Secrets are stored encrypted and only decrypt into the worker environment at spawn time. `)])],-1),c(`div`,ke,[c(`table`,Ae,[r[11]||=c(`thead`,null,[c(`tr`,null,[c(`th`,null,`Field`),c(`th`,{class:`hidden sm:table-cell`},` Purpose `),c(`th`,null,`Behaviour`)])],-1),c(`tbody`,null,[(t(),l(g,null,f(cn,e=>c(`tr`,{key:e.field,class:`align-top`},[c(`td`,je,[(t(),p(i(e.icon),{class:u([`w-3.5 h-3.5 shrink-0`,e.iconClass])},null,8,[`class`])),c(`code`,null,y(e.field),1)]),c(`td`,Me,y(e.purpose),1),c(`td`,Ne,y(e.body),1)])),64))])])]),c(`div`,Pe,[r[12]||=c(`div`,{class:`doc-microlabel`},` Set a secret `,-1),d(o(Z),{code:dn.value,lang:`bash`},null,8,[`code`])]),c(`details`,Fe,[c(`summary`,Ie,[d(o(x),{class:`w-3.5 h-3.5 transition-transform group-open:rotate-90 text-foreground-muted`}),r[13]||=m(` Signed-invoke recipe (HMAC, opt-in) `,-1)]),c(`div`,Le,[d(o(Z),{code:fn.value,lang:`bash`},null,8,[`code`])])])]),c(`section`,Re,[r[20]||=h(`
04

SDK from inside a function

The bundled orva module exposes three primitives every function can use without extra dependencies: a per-function key/value store, in-process calls to other Orva functions, and a fire-and-forget background job queue. Routes through the per-process internal token injected at worker spawn time.

orva.kv
put / get / delete / list

Per-function namespace on SQLite, optional TTL.

orva.invoke
invoke(name, payload)

In-process call to another function. 8-deep call cap.

orva.jobs
jobs.enqueue(name, payload)

Fire-and-forget; persisted; retried with exp backoff.

`,2),c(`div`,ze,[r[15]||=c(`div`,{class:`doc-microlabel`},` KV: get/put with TTL `,-1),d(o(Q),{tabs:mn,"storage-key":`docs.sdk.kv`}),r[16]||=h(`

Browse / inspect / edit / delete / set keys without leaving the dashboard at /web/functions/<name>/kv (or click the KV button in the editor's action bar). REST mirror at GET/PUT/DELETE /api/v1/functions/<id>/kv[/<key>]; MCP tools kv_list / kv_get / kv_put / kv_delete for agents.

`,1)]),c(`div`,Be,[r[17]||=c(`div`,{class:`doc-microlabel`},` Function-to-function: invoke() `,-1),d(o(Q),{tabs:hn,"storage-key":`docs.sdk.invoke`})]),c(`div`,Ve,[r[18]||=c(`div`,{class:`doc-microlabel`},` Background jobs: jobs.enqueue() `,-1),d(o(Q),{tabs:gn,"storage-key":`docs.sdk.jobs`})]),d(o($),{icon:o(C),title:`Network mode`},{default:n(()=>[...r[19]||=[m(` The SDK reaches orvad over loopback through the host gateway, so the function needs `,-1),c(`code`,{class:`doc-chip`},`network_mode: "egress"`,-1),m(`. On the default `,-1),c(`code`,{class:`doc-chip`},`"none"`,-1),m(` the SDK throws `,-1),c(`code`,{class:`doc-chip`},`OrvaUnavailableError`,-1),m(` with a clear hint. `,-1)]]),_:1},8,[`icon`])]),c(`section`,He,[c(`div`,Ue,[r[31]||=c(`span`,{class:`doc-section-num`},`05`,-1),c(`div`,null,[r[30]||=c(`h2`,{class:`doc-section-title`},` Schedules `,-1),c(`p`,We,[r[22]||=m(` Fire any function on a cron expression. The scheduler runs as part of the orvad process; no external service. Manage from the `,-1),d(a,{to:`/cron`,class:`text-foreground hover:text-white underline decoration-dotted underline-offset-4`},{default:n(()=>[...r[21]||=[m(` Schedules page `,-1)]]),_:1}),r[23]||=m(` or via the API. Standard 5-field cron with the usual shorthands (`,-1),r[24]||=c(`code`,{class:`doc-chip`},`@daily`,-1),r[25]||=m(`, `,-1),r[26]||=c(`code`,{class:`doc-chip`},`@hourly`,-1),r[27]||=m(`, `,-1),r[28]||=c(`code`,{class:`doc-chip`},`*/5 * * * *`,-1),r[29]||=m(`). `,-1)])])]),d(o(Q),{tabs:pn.value,"storage-key":`docs.cron`},null,8,[`tabs`]),d(o($),{icon:o(oe),title:`Cron-fired headers`},{default:n(()=>[...r[32]||=[m(` Every cron-triggered invocation arrives at the function with `,-1),c(`code`,{class:`doc-chip`},`x-orva-trigger: cron`,-1),m(` and `,-1),c(`code`,{class:`doc-chip`},`x-orva-cron-id: cron_…`,-1),m(` on the event headers, so user code can branch on origin. `,-1)]]),_:1},8,[`icon`])]),c(`section`,Ge,[c(`div`,Ke,[r[37]||=c(`span`,{class:`doc-section-num`},`06`,-1),c(`div`,null,[r[36]||=c(`h2`,{class:`doc-section-title`},` Webhooks `,-1),c(`p`,qe,[r[34]||=m(` Operator-managed subscriptions for system events. Configure URLs from the `,-1),d(a,{to:`/webhooks`,class:`text-foreground hover:text-white underline decoration-dotted underline-offset-4`},{default:n(()=>[...r[33]||=[m(` Webhooks page `,-1)]]),_:1}),r[35]||=m(`; Orva delivers signed POSTs to them when matching events fire (deployments, function lifecycle, cron failures, job outcomes). Subscriptions are global, not per-function. `,-1)])])]),d(o(rn)),r[40]||=h(`
Headers
X-Orva-EventX-Orva-Delivery-IdX-Orva-TimestampX-Orva-Signature
Signature
sha256=hex(hmac(secret, ts.body))

Same shape as Stripe / signed-invoke. Receivers verify with the secret returned at create time.

Retries
5 attemptsexp backoff (≤ 1h)

Receiver must 2xx within 15s.

`,1),c(`div`,Je,[c(`table`,Ye,[r[38]||=c(`thead`,null,[c(`tr`,null,[c(`th`,null,`Event`),c(`th`,null,`When it fires`)])],-1),c(`tbody`,null,[(t(),l(g,null,f(_n,e=>c(`tr`,{key:e.name},[c(`td`,Xe,[c(`code`,null,y(e.name),1)]),c(`td`,Ze,y(e.when),1)])),64))])])]),c(`div`,Qe,[r[39]||=c(`div`,{class:`doc-microlabel`},` Verify a delivery `,-1),d(o(Q),{tabs:vn,"storage-key":`docs.webhooks.verify`})])]),c(`section`,$e,[r[52]||=c(`div`,{class:`doc-section-head`},[c(`span`,{class:`doc-section-num`},`07`),c(`div`,null,[c(`h2`,{class:`doc-section-title`},` MCP: Model Context Protocol `),c(`p`,{class:`doc-lede`},` Same API surface the dashboard uses, exposed as 73 tools an agent can call directly. API key permissions scope the available tool set. `)])],-1),c(`div`,et,[c(`div`,tt,[r[41]||=c(`div`,{class:`doc-microlabel`},` Endpoint `,-1),c(`div`,nt,[c(`code`,rt,y(L.value)+`/mcp`,1)])]),r[42]||=h(`
Auth header
Authorization: Bearer <token>

Or as a fallback: X-Orva-API-Key: <token>

Transport
Streamable HTTPStatelessMCP 2026-07-28

Older clients negotiate down — server/discover advertises 2026-07-28, 2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05.

`,2)]),d(o($),{icon:o(w),title:`Two header formats; same auth`},{default:n(()=>[...r[43]||=[m(` Either header works against the same API key store with identical permission gating. `,-1),c(`code`,{class:`doc-chip`},`Authorization: Bearer`,-1),m(` is the MCP / OAuth 2 spec form; every MCP SDK (Claude Code, Claude Desktop, Cursor, mcp-remote, Python `,-1),c(`code`,{class:`doc-chip`},`mcp`,-1),m(`) configures it natively, so prefer it for new setups. `,-1),c(`code`,{class:`doc-chip`},`X-Orva-API-Key`,-1),m(` is the same header the REST API accepts; useful when a tool reuses an existing Orva REST integration. Internally both paths SHA-256-hash the token and look it up against the same `,-1),c(`code`,{class:`doc-chip`},`api_keys`,-1),m(` table. `,-1)]]),_:1},8,[`icon`]),d(o($),{icon:o(C),title:`No handshake, no session`},{default:n(()=>[...r[44]||=[m(` The transport is stateless. There is no `,-1),c(`code`,{class:`doc-chip`},`initialize`,-1),m(` step to perform first, no `,-1),c(`code`,{class:`doc-chip`},`Mcp-Session-Id`,-1),m(` is ever issued, and `,-1),c(`code`,{class:`doc-chip`},`GET /mcp`,-1),m(` / `,-1),c(`code`,{class:`doc-chip`},`DELETE /mcp`,-1),m(` — the SSE-resume and session-teardown verbs of the older session transport — return `,-1),c(`code`,{class:`doc-chip`},`405`,-1),m(`. Every POST carries its own bearer token and is answered on its own; a legacy client that still sends `,-1),c(`code`,{class:`doc-chip`},`initialize`,-1),m(` gets a normal reply with its own `,-1),c(`code`,{class:`doc-chip`},`protocolVersion`,-1),m(` echoed back, and simply never receives a session header. A request that opts into 2026-07-28 sends the headers `,-1),c(`code`,{class:`doc-chip`},`Mcp-Protocol-Version`,-1),m(` and `,-1),c(`code`,{class:`doc-chip`},`Mcp-Method`,-1),m(` — plus `,-1),c(`code`,{class:`doc-chip`},`Mcp-Name`,-1),m(` for `,-1),c(`code`,{class:`doc-chip`},`tools/call`,-1),m(`, `,-1),c(`code`,{class:`doc-chip`},`resources/read`,-1),m(` and `,-1),c(`code`,{class:`doc-chip`},`prompts/get`,-1),m(`, which must repeat the name from the body — plus `,-1),c(`code`,{class:`doc-chip`},`io.modelcontextprotocol/protocolVersion`,-1),m(` and `,-1),c(`code`,{class:`doc-chip`},`io.modelcontextprotocol/clientCapabilities`,-1),m(` in `,-1),c(`code`,{class:`doc-chip`},`params._meta`,-1),m(` (a `,-1),c(`code`,{class:`doc-chip`},`clientInfo`,-1),m(` key is optional). The headers let a proxy route on the operation without parsing the body, so one that disagrees with the body is rejected, not ignored. Every POST must send `,-1),c(`code`,{class:`doc-chip`},`Accept: application/json, text/event-stream`,-1),m(`. A successful reply is one SSE `,-1),c(`code`,{class:`doc-chip`},`message`,-1),m(` event; a request rejected at the transport layer comes back as plain `,-1),c(`code`,{class:`doc-chip`},`application/json`,-1),m(` with a 4xx status. `,-1)]]),_:1},8,[`icon`]),d(o($),{icon:o(T),title:`List results are private and immediately stale`},{default:n(()=>[...r[45]||=[c(`code`,{class:`doc-chip`},`tools/list`,-1),m(`, `,-1),c(`code`,{class:`doc-chip`},`resources/list`,-1),m(`, `,-1),c(`code`,{class:`doc-chip`},`resources/templates/list`,-1),m(`, `,-1),c(`code`,{class:`doc-chip`},`prompts/list`,-1),m(`, `,-1),c(`code`,{class:`doc-chip`},`resources/read`,-1),m(` and `,-1),c(`code`,{class:`doc-chip`},`server/discover`,-1),m(` results carry `,-1),c(`code`,{class:`doc-chip`},`ttlMs`,-1),m(` and `,-1),c(`code`,{class:`doc-chip`},`cacheScope`,-1),m(`. Orva returns `,-1),c(`code`,{class:`doc-chip`},`cacheScope: "private"`,-1),m(` because the tool catalog is permission-scoped (a full-permission key lists 73 tools; a `,-1),c(`code`,{class:`doc-chip`},`read`,-1),m(`-only key lists 27) and channel-specific (a channel token sees only that channel's functions) — a shared cache entry would hand one caller another caller's tool surface. `,-1),c(`code`,{class:`doc-chip`},`ttlMs`,-1),m(` is `,-1),c(`code`,{class:`doc-chip`},`0`,-1),m(` because the catalog changes on any deploy, channel edit, or permission change, and statelessness removed the session a `,-1),c(`code`,{class:`doc-chip`},`tools/list_changed`,-1),m(` notification would have travelled over. Re-list instead of caching. `,-1)]]),_:1},8,[`icon`]),c(`div`,it,[c(`div`,at,[d(o(w),{class:`w-4 h-4 shrink-0 text-foreground-muted`}),J.value?(t(),l(`span`,st,[r[48]||=m(` Token minted: `,-1),c(`code`,ct,y(Cn.value)+`…`,1),r[49]||=m(` Shown once, copy now. `,-1)])):(t(),l(`span`,ot,[r[46]||=m(` Snippets show `,-1),c(`code`,{class:`doc-chip`},y(I)),r[47]||=m(`. Mint a token to substitute it everywhere. `,-1)]))]),c(`button`,{class:`doc-token-btn`,disabled:Y.value,onClick:wn},[d(o(w),{class:`w-3.5 h-3.5`}),m(` `+y(J.value?`Mint another`:Y.value?`Minting…`:`Generate token`),1)],8,lt)]),d(o(Q),{tabs:Tn.value,"storage-key":`docs.mcp.install`},null,8,[`tabs`]),c(`details`,ut,[c(`summary`,dt,[d(o(x),{class:`w-3.5 h-3.5 transition-transform group-open:rotate-90 text-foreground-muted`}),r[50]||=m(` More clients (Cursor, VS Code, Codex CLI, OpenCode, Zed, Windsurf, ChatGPT, manual config) `,-1)]),c(`div`,ft,[d(o(Q),{tabs:En.value,"storage-key":`docs.mcp.install.more`},null,8,[`tabs`]),r[51]||=c(`div`,{class:`doc-microlabel pt-1`},` Hand-edited config files `,-1),d(o(Q),{tabs:On.value,"storage-key":`docs.mcp.manual`},null,8,[`tabs`])])])]),c(`section`,pt,[r[53]||=c(`div`,{class:`doc-section-head`},[c(`span`,{class:`doc-section-num`},`08`),c(`div`,null,[c(`h2`,{class:`doc-section-title`},` System prompt for AI assistants `),c(`p`,{class:`doc-lede`},` Copy Orva's full reference into another AI assistant. `)])],-1),c(`div`,mt,[c(`button`,{class:u([`ai-copy-btn`,{copied:V.value}]),onClick:Qt},[V.value?(t(),p(o(b),{key:0,class:`w-3.5 h-3.5`})):(t(),p(o(S),{key:1,class:`w-3.5 h-3.5`})),m(` `+y(V.value?`Copied`:`Copy system prompt`),1)],2)]),c(`div`,{class:u([`prompt-collapse`,{expanded:q.value}])},[d(o(Z),{code:o(Zt),lang:`text`},null,8,[`code`]),q.value?te(``,!0):(t(),l(`div`,ht))],2),c(`button`,{class:`prompt-expand-btn`,"aria-expanded":q.value,onClick:r[0]||=e=>q.value=!q.value},[d(o(ie),{class:u([`w-3.5 h-3.5 transition-transform`,{"rotate-180":q.value}])},null,8,[`class`]),m(` `+y(q.value?`Collapse system prompt`:`Show full system prompt`),1)],8,gt)]),c(`section`,_t,[r[55]||=h(`
09

Tracing

Every invocation chain is recorded as a causal trace. automatically, with zero changes to your function code. HTTP requests, F2F invokes, jobs, cron, inbound webhooks, and replays all stitch into the same tree. The dashboard renders it as a waterfall at /traces.

Each execution row IS a span. Spans share a trace_id; child spans point at their parent via parent_span_id. You don't instantiate spans, you don't import a tracer; you just write your handler and the platform plumbs IDs through every internal hop.

`,2),d(o(nn)),r[56]||=c(`h3`,{class:`doc-h3`},` What user code sees `,-1),r[57]||=c(`p`,{class:`doc-prose`},` Two env vars are stamped per invocation. Read them only if you want to log the trace_id alongside your own messages; they're optional. `,-1),d(o(Z),{code:zt,lang:`text`}),r[58]||=c(`h3`,{class:`doc-h3`},` Automatic propagation `,-1),r[59]||=c(`p`,{class:`doc-prose`},[m(` When a function calls another via the SDK, the trace context flows through automatically. The called function becomes a child span of the caller; both share the same `),c(`code`,{class:`doc-chip`},`trace_id`),m(`. `)],-1),d(o(Z),{code:Bt,lang:`js`}),r[60]||=h(`

Job enqueues work the same way: orva.jobs.enqueue() records the trace context on the job row. When the scheduler picks the job up later, the resulting execution lands in the same trace as the function that enqueued it, even if the gap is hours or days.

Triggers

Each span carries a trigger label so the UI can show how the chain started.

`,3),c(`div`,vt,[c(`table`,yt,[r[54]||=c(`thead`,null,[c(`tr`,null,[c(`th`,null,`Trigger`),c(`th`,null,`Meaning`)])],-1),c(`tbody`,null,[(t(),l(g,null,f(U,e=>c(`tr`,{key:e.name},[c(`td`,bt,[c(`code`,null,y(e.name),1)]),c(`td`,xt,y(e.desc),1)])),64))])])]),r[61]||=c(`h3`,{class:`doc-h3`},` External correlation (W3C traceparent) `,-1),r[62]||=c(`p`,{class:`doc-prose`},[m(` Send a standard `),c(`code`,{class:`doc-chip`},`traceparent`),m(` header on the inbound HTTP request and Orva makes its trace a child of yours. The same trace_id is echoed back as `),c(`code`,{class:`doc-chip`},`X-Trace-Id`),m(` on every response, so external systems can correlate without parsing bodies. `)],-1),d(o(Z),{code:Vt,lang:`bash`}),r[63]||=h(`

Outlier detection

Each function maintains an in-memory rolling P95 baseline over its last 100 successful warm executions. An invocation is flagged as an outlier when it has at least 20 baseline samples AND its duration exceeds P95 × 2. Cold starts and errors are excluded from the baseline so a flapping function can't drag it down. The flag and baseline P95 are stored on the execution row and rendered as an amber flag icon next to the span.

Where to look

  • /traces: list of recent traces, filterable by function / status / outlier-only.
  • /traces/:id: waterfall + per-span detail. Click a span to jump to its execution in the Invocations log.
  • GET /api/v1/traces/{id}: full span tree as JSON. Pair with list_traces / get_trace MCP tools for AI agents.
  • GET /api/v1/functions/{id}/baseline: current P95/P99/mean for a function.
`,4)]),c(`section`,St,[r[65]||=h(`
10

Errors & recovery

Every error response uses the same envelope so log scrapers and retries can match on code. Deploys are content-addressed; rollback retargets the active version pointer and refreshes warm workers.

`,1),d(o(Z),{code:Ht,lang:`json`}),c(`div`,Ct,[c(`table`,wt,[r[64]||=c(`thead`,null,[c(`tr`,null,[c(`th`,null,`Code`),c(`th`,null,`When you see it`)])],-1),c(`tbody`,null,[(t(),l(g,null,f(yn,e=>c(`tr`,{key:e.code},[c(`td`,Tt,[c(`code`,null,y(e.code),1)]),c(`td`,Et,y(e.when),1)])),64))])])])]),c(`section`,Dt,[r[82]||=h(`
11

CLI

Orva ships a full Linux server binary and a slim cross-platform CLI, both named orva. They share every client command; only the full build adds serve, setup, and init.

Install (server included)
curl … install.sh | sh

Full install: daemon + nsjail + rootfs + CLI.

Install (CLI only)
curl … install-cli.sh | sh

~20 MB binary at /usr/local/bin/orva. No service.

Inside Docker
docker exec orva orva …

CLI auto-authed via ~/.orva/config.yaml.

Authenticate

`,3),c(`p`,Ot,[r[67]||=m(` The CLI reads `,-1),r[68]||=c(`code`,{class:`doc-chip`},`~/.orva/config.yaml`,-1),r[69]||=m(` for `,-1),r[70]||=c(`code`,{class:`doc-chip`},`endpoint`,-1),r[71]||=m(` + `,-1),r[72]||=c(`code`,{class:`doc-chip`},`api_key`,-1),r[73]||=m(`. Generate a key from `,-1),d(a,{to:`/api-keys`,class:`text-foreground hover:text-white underline decoration-dotted underline-offset-4`},{default:n(()=>[...r[66]||=[m(` Keys `,-1)]]),_:1}),r[74]||=m(` in the dashboard, then: `,-1)]),d(o(Z),{code:Ut,lang:`bash`}),r[83]||=c(`h3`,{class:`doc-h3`},` Command index `,-1),c(`div`,kt,[c(`table`,At,[r[75]||=c(`thead`,null,[c(`tr`,null,[c(`th`,null,`Command`),c(`th`,null,`Subcommands`),c(`th`,{class:`hidden md:table-cell`},` Purpose `)])],-1),c(`tbody`,null,[(t(),l(g,null,f(bn,e=>c(`tr`,{key:e.cmd},[c(`td`,jt,[c(`code`,null,`orva `+y(e.cmd),1)]),c(`td`,Mt,y(e.subs),1),c(`td`,Nt,y(e.purpose),1)])),64))])])]),r[84]||=c(`h3`,{class:`doc-h3`},` Common recipes `,-1),c(`div`,Pt,[r[76]||=c(`div`,{class:`doc-microlabel`},` Deploy a function from a directory `,-1),d(o(Z),{code:Wt,lang:`bash`})]),c(`div`,Ft,[r[77]||=c(`div`,{class:`doc-microlabel`},` Invoke + tail logs `,-1),d(o(Z),{code:Gt,lang:`bash`})]),c(`div`,It,[r[78]||=c(`div`,{class:`doc-microlabel`},` Manage KV state `,-1),d(o(Z),{code:Kt,lang:`bash`})]),c(`div`,Lt,[r[79]||=c(`div`,{class:`doc-microlabel`},` Secrets, cron, jobs, webhooks `,-1),d(o(Z),{code:qt,lang:`bash`})]),c(`div`,Rt,[r[80]||=c(`div`,{class:`doc-microlabel`},` System health, metrics, vacuum `,-1),d(o(Z),{code:Jt,lang:`bash`})]),d(o($),{icon:o(w),title:`Shell completion`},{default:n(()=>[...r[81]||=[m(` Generate completion for your shell: `,-1),c(`code`,{class:`doc-chip`},`orva completion bash | sudo tee /etc/bash_completion.d/orva`,-1),m(`, or `,-1),c(`code`,{class:`doc-chip`},`zsh`,-1),m(` / `,-1),c(`code`,{class:`doc-chip`},`fish`,-1),m(` / `,-1),c(`code`,{class:`doc-chip`},`powershell`,-1),m(`. Tab-completes commands, subcommands, and flags. `,-1)]]),_:1},8,[`icon`])])])}}};export{Yt as default}; \ No newline at end of file +}`}]),Z=s({name:`CodeBlock`,props:{code:{type:String,required:!0},lang:{type:String,default:``}},setup(e){let t=r(!1),n=async()=>{await k(e.code)&&(t.value=!0,setTimeout(()=>{t.value=!1},1200))},i=_(()=>{let t=(e.lang||``).toLowerCase();if(t&&E.getLanguage(t))try{return E.highlight(e.code,{language:t,ignoreIllegals:!0}).value}catch{}return e.code.replace(/&/g,`&`).replace(//g,`>`)});return()=>v(`div`,{class:`codeblock`},[v(`div`,{class:`codeblock-bar`},[v(`span`,{class:`codeblock-lang`},e.lang||``),v(`button`,{class:`codeblock-copy`,onClick:n,title:`Copy code`},[t.value?v(b,{class:`w-3 h-3`}):v(S,{class:`w-3 h-3`}),t.value?`Copied`:`Copy`])]),v(`pre`,{class:`codeblock-pre`},[v(`code`,{class:`hljs language-${(e.lang||`text`).toLowerCase()}`,innerHTML:i.value})])])}}),Q=s({name:`TabbedCode`,props:{tabs:{type:Array,required:!0},storageKey:{type:String,default:``}},setup(e){let t=(()=>{try{if(e.storageKey){let t=localStorage.getItem(e.storageKey);if(t&&e.tabs.some(e=>e.label===t))return t}}catch{}return e.tabs[0]?.label})(),n=r(t),i=t=>{n.value=t;try{e.storageKey&&localStorage.setItem(e.storageKey,t)}catch{}};return()=>{let t=e.tabs.find(e=>e.label===n.value)||e.tabs[0];return v(`div`,{class:`tabbed`},[v(`div`,{class:`tabbed-tabs`},e.tabs.map(e=>v(`button`,{key:e.label,class:[`tabbed-tab`,{active:e.label===n.value}],onClick:()=>i(e.label)},e.label))),t.note?v(`div`,{class:`tabbed-note`},t.note):null,v(Z,{code:t.code,lang:t.lang})])}}}),$=s({name:`DocsCallout`,props:{title:{type:String,default:``},icon:{type:[Object,Function],default:null}},setup(e,{slots:t}){return()=>v(`div`,{class:`callout`},[v(`div`,{class:`callout-head`},[e.icon?v(e.icon,{class:`callout-icon`}):null,e.title?v(`span`,null,e.title):null]),v(`div`,{class:`callout-body`},t.default?.())])}});return(e,r)=>{let a=ee(`router-link`);return t(),l(`div`,pe,[c(`header`,me,[c(`div`,he,[c(`div`,ge,[r[1]||=c(`div`,{class:`docs-hero-text`},[c(`h1`,{class:`docs-hero-title`},` Documentation `),c(`p`,{class:`docs-hero-sub`},` Build, deploy, and operate functions on Orva. `)],-1),c(`div`,_e,[c(`button`,{class:u([`docs-hero-copy-icon`,{copied:G.value}]),title:G.value?`Copied`:`Copy entire docs page as Markdown`,"aria-label":G.value?`Markdown copied to clipboard`:`Copy entire docs page as Markdown`,onClick:Sn},[G.value?(t(),p(o(b),{key:0,class:`w-4 h-4`})):(t(),p(o(S),{key:1,class:`w-4 h-4`}))],10,ve)])]),c(`nav`,A,[r[2]||=c(`span`,{class:`docs-hero-toc-label`},`Jump to`,-1),(t(),l(g,null,f(R,e=>c(`a`,{key:e.id,href:`#${e.id}`,class:u([`docs-hero-toc-link`,{active:z.value===e.id}])},[c(`span`,M,y(e.num),1),c(`span`,null,y(e.label),1)],10,j)),64))])])]),c(`section`,N,[r[4]||=c(`div`,{class:`doc-section-head`},[c(`span`,{class:`doc-section-num`},`01`),c(`div`,null,[c(`h2`,{class:`doc-section-title`},` Handler contract `),c(`p`,{class:`doc-lede`},` One exported function receives the inbound HTTP event and returns an HTTP-shaped response. The adapter handles serialization and headers. `)])],-1),d(o(Q),{tabs:an.value,"storage-key":`docs.handler`},null,8,[`tabs`]),r[5]||=h(`
Event shape
methodpathheadersquerybody
Response
{ statusCode, headers, body }

Non-string bodies are JSON-encoded by the adapter.

Runtime env
Env vars and secrets land in process.env / os.environ.
`,1),c(`div`,P,[c(`table`,F,[r[3]||=c(`thead`,null,[c(`tr`,null,[c(`th`,null,`Runtime`),c(`th`,null,`ID`),c(`th`,{class:`hidden sm:table-cell`},` Entrypoint `),c(`th`,{class:`hidden md:table-cell`},` Dependencies `)])],-1),c(`tbody`,null,[(t(),l(g,null,f(sn,e=>c(`tr`,{key:e.id},[c(`td`,ye,[(t(),p(i(e.icon),{class:`shrink-0`})),m(` `+y(e.name),1)]),c(`td`,be,y(e.id),1),c(`td`,xe,y(e.entry),1),c(`td`,Se,y(e.deps),1)])),64))])])])]),c(`section`,Ce,[r[10]||=h(`
02

Deploy & invoke

The dashboard handles day-to-day work; these calls are for CI and automation. Builds run async; poll /api/v1/deployments/<id> or stream /api/v1/deployments/<id>/stream until phase: done.

`,1),d(o(tn)),c(`div`,we,[c(`div`,Te,[r[6]||=c(`div`,{class:`doc-step-label`},[c(`span`,{class:`doc-step-num`},`1`),m(` Create the function row `)],-1),d(o(Z),{code:ln.value,lang:`bash`},null,8,[`code`])]),c(`div`,Ee,[r[7]||=c(`div`,{class:`doc-step-label`},[c(`span`,{class:`doc-step-num`},`2`),m(` Upload code `)],-1),d(o(Z),{code:un.value,lang:`bash`},null,8,[`code`])])]),c(`div`,De,[r[8]||=c(`div`,{class:`doc-microlabel`},` Invoke `,-1),d(o(Q),{tabs:on.value,"storage-key":`docs.invoke`},null,8,[`tabs`])]),d(o($),{icon:o(C),title:`Custom routes`},{default:n(()=>[...r[9]||=[m(` Attach a friendly path with `,-1),c(`code`,{class:`doc-chip`},`POST /api/v1/routes`,-1),m(`. Reserved prefixes: `,-1),c(`code`,{class:`doc-chip`},`/api/`,-1),c(`code`,{class:`doc-chip`},`/fn/`,-1),c(`code`,{class:`doc-chip`},`/mcp/`,-1),c(`code`,{class:`doc-chip`},`/web/`,-1),c(`code`,{class:`doc-chip`},`/_orva/`,-1),m(`. `,-1)]]),_:1},8,[`icon`])]),c(`section`,Oe,[r[14]||=c(`div`,{class:`doc-section-head`},[c(`span`,{class:`doc-section-num`},`03`),c(`div`,null,[c(`h2`,{class:`doc-section-title`},` Configuration reference `),c(`p`,{class:`doc-lede`},` Everything below lives on the function record. Secrets are stored encrypted and only decrypt into the worker environment at spawn time. `)])],-1),c(`div`,ke,[c(`table`,Ae,[r[11]||=c(`thead`,null,[c(`tr`,null,[c(`th`,null,`Field`),c(`th`,{class:`hidden sm:table-cell`},` Purpose `),c(`th`,null,`Behaviour`)])],-1),c(`tbody`,null,[(t(),l(g,null,f(cn,e=>c(`tr`,{key:e.field,class:`align-top`},[c(`td`,je,[(t(),p(i(e.icon),{class:u([`w-3.5 h-3.5 shrink-0`,e.iconClass])},null,8,[`class`])),c(`code`,null,y(e.field),1)]),c(`td`,Me,y(e.purpose),1),c(`td`,Ne,y(e.body),1)])),64))])])]),c(`div`,Pe,[r[12]||=c(`div`,{class:`doc-microlabel`},` Set a secret `,-1),d(o(Z),{code:dn.value,lang:`bash`},null,8,[`code`])]),c(`details`,Fe,[c(`summary`,Ie,[d(o(x),{class:`w-3.5 h-3.5 transition-transform group-open:rotate-90 text-foreground-muted`}),r[13]||=m(` Signed-invoke recipe (HMAC, opt-in) `,-1)]),c(`div`,Le,[d(o(Z),{code:fn.value,lang:`bash`},null,8,[`code`])])])]),c(`section`,Re,[r[20]||=h(`
04

SDK from inside a function

The bundled orva module exposes three primitives every function can use without extra dependencies: a per-function key/value store, in-process calls to other Orva functions, and a fire-and-forget background job queue. Routes through a process-signed, function-scoped credential injected at worker spawn time.

orva.kv
put / get / delete / list

Per-function namespace on SQLite, optional TTL.

orva.invoke
invoke(name, payload)

In-process call to another function. 8-deep call cap.

orva.jobs
jobs.enqueue(name, payload)

Fire-and-forget; persisted; retried with exp backoff.

`,2),c(`div`,ze,[r[15]||=c(`div`,{class:`doc-microlabel`},` KV: get/put with TTL `,-1),d(o(Q),{tabs:mn,"storage-key":`docs.sdk.kv`}),r[16]||=h(`

Browse / inspect / edit / delete / set keys without leaving the dashboard at /web/functions/<name>/kv (or click the KV button in the editor's action bar). REST mirror at GET/PUT/DELETE /api/v1/functions/<id>/kv[/<key>]; MCP tools kv_list / kv_get / kv_put / kv_delete for agents.

`,1)]),c(`div`,Be,[r[17]||=c(`div`,{class:`doc-microlabel`},` Function-to-function: invoke() `,-1),d(o(Q),{tabs:hn,"storage-key":`docs.sdk.invoke`})]),c(`div`,Ve,[r[18]||=c(`div`,{class:`doc-microlabel`},` Background jobs: jobs.enqueue() `,-1),d(o(Q),{tabs:gn,"storage-key":`docs.sdk.jobs`})]),d(o($),{icon:o(C),title:`Network mode`},{default:n(()=>[...r[19]||=[m(` The SDK reaches orvad over loopback through the host gateway, so the function needs `,-1),c(`code`,{class:`doc-chip`},`network_mode: "egress"`,-1),m(`. On the default `,-1),c(`code`,{class:`doc-chip`},`"none"`,-1),m(` the SDK throws `,-1),c(`code`,{class:`doc-chip`},`OrvaUnavailableError`,-1),m(` with a clear hint. `,-1)]]),_:1},8,[`icon`])]),c(`section`,He,[c(`div`,Ue,[r[31]||=c(`span`,{class:`doc-section-num`},`05`,-1),c(`div`,null,[r[30]||=c(`h2`,{class:`doc-section-title`},` Schedules `,-1),c(`p`,We,[r[22]||=m(` Fire any function on a cron expression. The scheduler runs as part of the orvad process; no external service. Manage from the `,-1),d(a,{to:`/cron`,class:`text-foreground hover:text-white underline decoration-dotted underline-offset-4`},{default:n(()=>[...r[21]||=[m(` Schedules page `,-1)]]),_:1}),r[23]||=m(` or via the API. Standard 5-field cron with the usual shorthands (`,-1),r[24]||=c(`code`,{class:`doc-chip`},`@daily`,-1),r[25]||=m(`, `,-1),r[26]||=c(`code`,{class:`doc-chip`},`@hourly`,-1),r[27]||=m(`, `,-1),r[28]||=c(`code`,{class:`doc-chip`},`*/5 * * * *`,-1),r[29]||=m(`). `,-1)])])]),d(o(Q),{tabs:pn.value,"storage-key":`docs.cron`},null,8,[`tabs`]),d(o($),{icon:o(oe),title:`Cron-fired headers`},{default:n(()=>[...r[32]||=[m(` Every cron-triggered invocation arrives at the function with `,-1),c(`code`,{class:`doc-chip`},`x-orva-trigger: cron`,-1),m(` and `,-1),c(`code`,{class:`doc-chip`},`x-orva-cron-id: cron_…`,-1),m(` on the event headers, so user code can branch on origin. `,-1)]]),_:1},8,[`icon`])]),c(`section`,Ge,[c(`div`,Ke,[r[37]||=c(`span`,{class:`doc-section-num`},`06`,-1),c(`div`,null,[r[36]||=c(`h2`,{class:`doc-section-title`},` Webhooks `,-1),c(`p`,qe,[r[34]||=m(` Operator-managed subscriptions for system events. Configure URLs from the `,-1),d(a,{to:`/webhooks`,class:`text-foreground hover:text-white underline decoration-dotted underline-offset-4`},{default:n(()=>[...r[33]||=[m(` Webhooks page `,-1)]]),_:1}),r[35]||=m(`; Orva delivers signed POSTs to them when matching events fire (deployments, function lifecycle, cron failures, job outcomes). Subscriptions are global, not per-function. `,-1)])])]),d(o(rn)),r[40]||=h(`
Headers
X-Orva-EventX-Orva-Delivery-IdX-Orva-TimestampX-Orva-Signature
Signature
sha256=hex(hmac(secret, ts.body))

Same shape as Stripe / signed-invoke. Receivers verify with the secret returned at create time.

Retries
5 attemptsexp backoff (≤ 1h)

Receiver must 2xx within 15s.

`,1),c(`div`,Je,[c(`table`,Ye,[r[38]||=c(`thead`,null,[c(`tr`,null,[c(`th`,null,`Event`),c(`th`,null,`When it fires`)])],-1),c(`tbody`,null,[(t(),l(g,null,f(_n,e=>c(`tr`,{key:e.name},[c(`td`,Xe,[c(`code`,null,y(e.name),1)]),c(`td`,Ze,y(e.when),1)])),64))])])]),c(`div`,Qe,[r[39]||=c(`div`,{class:`doc-microlabel`},` Verify a delivery `,-1),d(o(Q),{tabs:vn,"storage-key":`docs.webhooks.verify`})])]),c(`section`,$e,[r[52]||=c(`div`,{class:`doc-section-head`},[c(`span`,{class:`doc-section-num`},`07`),c(`div`,null,[c(`h2`,{class:`doc-section-title`},` MCP: Model Context Protocol `),c(`p`,{class:`doc-lede`},` Same API surface the dashboard uses, exposed as 73 tools an agent can call directly. API key permissions scope the available tool set. `)])],-1),c(`div`,et,[c(`div`,tt,[r[41]||=c(`div`,{class:`doc-microlabel`},` Endpoint `,-1),c(`div`,nt,[c(`code`,rt,y(L.value)+`/mcp`,1)])]),r[42]||=h(`
Auth header
Authorization: Bearer <token>

Or as a fallback: X-Orva-API-Key: <token>

Transport
Streamable HTTPStatelessMCP 2026-07-28

Older clients negotiate down — server/discover advertises 2026-07-28, 2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05.

`,2)]),d(o($),{icon:o(w),title:`Two header formats; same auth`},{default:n(()=>[...r[43]||=[m(` Either header works against the same API key store with identical permission gating. `,-1),c(`code`,{class:`doc-chip`},`Authorization: Bearer`,-1),m(` is the MCP / OAuth 2 spec form; every MCP SDK (Claude Code, Claude Desktop, Cursor, mcp-remote, Python `,-1),c(`code`,{class:`doc-chip`},`mcp`,-1),m(`) configures it natively, so prefer it for new setups. `,-1),c(`code`,{class:`doc-chip`},`X-Orva-API-Key`,-1),m(` is the same header the REST API accepts; useful when a tool reuses an existing Orva REST integration. Internally both paths SHA-256-hash the token and look it up against the same `,-1),c(`code`,{class:`doc-chip`},`api_keys`,-1),m(` table. `,-1)]]),_:1},8,[`icon`]),d(o($),{icon:o(C),title:`No handshake, no session`},{default:n(()=>[...r[44]||=[m(` The transport is stateless. There is no `,-1),c(`code`,{class:`doc-chip`},`initialize`,-1),m(` step to perform first, no `,-1),c(`code`,{class:`doc-chip`},`Mcp-Session-Id`,-1),m(` is ever issued, and `,-1),c(`code`,{class:`doc-chip`},`GET /mcp`,-1),m(` / `,-1),c(`code`,{class:`doc-chip`},`DELETE /mcp`,-1),m(` — the SSE-resume and session-teardown verbs of the older session transport — return `,-1),c(`code`,{class:`doc-chip`},`405`,-1),m(`. Every POST carries its own bearer token and is answered on its own; a legacy client that still sends `,-1),c(`code`,{class:`doc-chip`},`initialize`,-1),m(` gets a normal reply with its own `,-1),c(`code`,{class:`doc-chip`},`protocolVersion`,-1),m(` echoed back, and simply never receives a session header. A request that opts into 2026-07-28 sends the headers `,-1),c(`code`,{class:`doc-chip`},`Mcp-Protocol-Version`,-1),m(` and `,-1),c(`code`,{class:`doc-chip`},`Mcp-Method`,-1),m(` — plus `,-1),c(`code`,{class:`doc-chip`},`Mcp-Name`,-1),m(` for `,-1),c(`code`,{class:`doc-chip`},`tools/call`,-1),m(`, `,-1),c(`code`,{class:`doc-chip`},`resources/read`,-1),m(` and `,-1),c(`code`,{class:`doc-chip`},`prompts/get`,-1),m(`, which must repeat the name from the body — plus `,-1),c(`code`,{class:`doc-chip`},`io.modelcontextprotocol/protocolVersion`,-1),m(` and `,-1),c(`code`,{class:`doc-chip`},`io.modelcontextprotocol/clientCapabilities`,-1),m(` in `,-1),c(`code`,{class:`doc-chip`},`params._meta`,-1),m(` (a `,-1),c(`code`,{class:`doc-chip`},`clientInfo`,-1),m(` key is optional). The headers let a proxy route on the operation without parsing the body, so one that disagrees with the body is rejected, not ignored. Every POST must send `,-1),c(`code`,{class:`doc-chip`},`Accept: application/json, text/event-stream`,-1),m(`. A successful reply is one SSE `,-1),c(`code`,{class:`doc-chip`},`message`,-1),m(` event; a request rejected at the transport layer comes back as plain `,-1),c(`code`,{class:`doc-chip`},`application/json`,-1),m(` with a 4xx status. `,-1)]]),_:1},8,[`icon`]),d(o($),{icon:o(T),title:`List results are private and immediately stale`},{default:n(()=>[...r[45]||=[c(`code`,{class:`doc-chip`},`tools/list`,-1),m(`, `,-1),c(`code`,{class:`doc-chip`},`resources/list`,-1),m(`, `,-1),c(`code`,{class:`doc-chip`},`resources/templates/list`,-1),m(`, `,-1),c(`code`,{class:`doc-chip`},`prompts/list`,-1),m(`, `,-1),c(`code`,{class:`doc-chip`},`resources/read`,-1),m(` and `,-1),c(`code`,{class:`doc-chip`},`server/discover`,-1),m(` results carry `,-1),c(`code`,{class:`doc-chip`},`ttlMs`,-1),m(` and `,-1),c(`code`,{class:`doc-chip`},`cacheScope`,-1),m(`. Orva returns `,-1),c(`code`,{class:`doc-chip`},`cacheScope: "private"`,-1),m(` because the tool catalog is permission-scoped (a full-permission key lists 73 tools; a `,-1),c(`code`,{class:`doc-chip`},`read`,-1),m(`-only key lists 27) and channel-specific (a channel token sees only that channel's functions) — a shared cache entry would hand one caller another caller's tool surface. `,-1),c(`code`,{class:`doc-chip`},`ttlMs`,-1),m(` is `,-1),c(`code`,{class:`doc-chip`},`0`,-1),m(` because the catalog changes on any deploy, channel edit, or permission change, and statelessness removed the session a `,-1),c(`code`,{class:`doc-chip`},`tools/list_changed`,-1),m(` notification would have travelled over. Re-list instead of caching. `,-1)]]),_:1},8,[`icon`]),c(`div`,it,[c(`div`,at,[d(o(w),{class:`w-4 h-4 shrink-0 text-foreground-muted`}),J.value?(t(),l(`span`,st,[r[48]||=m(` Token minted: `,-1),c(`code`,ct,y(Cn.value)+`…`,1),r[49]||=m(` Shown once, copy now. `,-1)])):(t(),l(`span`,ot,[r[46]||=m(` Snippets show `,-1),c(`code`,{class:`doc-chip`},y(I)),r[47]||=m(`. Mint a token to substitute it everywhere. `,-1)]))]),c(`button`,{class:`doc-token-btn`,disabled:Y.value,onClick:wn},[d(o(w),{class:`w-3.5 h-3.5`}),m(` `+y(J.value?`Mint another`:Y.value?`Minting…`:`Generate token`),1)],8,lt)]),d(o(Q),{tabs:Tn.value,"storage-key":`docs.mcp.install`},null,8,[`tabs`]),c(`details`,ut,[c(`summary`,dt,[d(o(x),{class:`w-3.5 h-3.5 transition-transform group-open:rotate-90 text-foreground-muted`}),r[50]||=m(` More clients (Cursor, VS Code, Codex CLI, OpenCode, Zed, Windsurf, ChatGPT, manual config) `,-1)]),c(`div`,ft,[d(o(Q),{tabs:En.value,"storage-key":`docs.mcp.install.more`},null,8,[`tabs`]),r[51]||=c(`div`,{class:`doc-microlabel pt-1`},` Hand-edited config files `,-1),d(o(Q),{tabs:On.value,"storage-key":`docs.mcp.manual`},null,8,[`tabs`])])])]),c(`section`,pt,[r[53]||=c(`div`,{class:`doc-section-head`},[c(`span`,{class:`doc-section-num`},`08`),c(`div`,null,[c(`h2`,{class:`doc-section-title`},` System prompt for AI assistants `),c(`p`,{class:`doc-lede`},` Copy Orva's full reference into another AI assistant. `)])],-1),c(`div`,mt,[c(`button`,{class:u([`ai-copy-btn`,{copied:V.value}]),onClick:Qt},[V.value?(t(),p(o(b),{key:0,class:`w-3.5 h-3.5`})):(t(),p(o(S),{key:1,class:`w-3.5 h-3.5`})),m(` `+y(V.value?`Copied`:`Copy system prompt`),1)],2)]),c(`div`,{class:u([`prompt-collapse`,{expanded:q.value}])},[d(o(Z),{code:o(Zt),lang:`text`},null,8,[`code`]),q.value?te(``,!0):(t(),l(`div`,ht))],2),c(`button`,{class:`prompt-expand-btn`,"aria-expanded":q.value,onClick:r[0]||=e=>q.value=!q.value},[d(o(ie),{class:u([`w-3.5 h-3.5 transition-transform`,{"rotate-180":q.value}])},null,8,[`class`]),m(` `+y(q.value?`Collapse system prompt`:`Show full system prompt`),1)],8,gt)]),c(`section`,_t,[r[55]||=h(`
09

Tracing

Every invocation chain is recorded as a causal trace. automatically, with zero changes to your function code. HTTP requests, F2F invokes, jobs, cron, inbound webhooks, and replays all stitch into the same tree. The dashboard renders it as a waterfall at /traces.

Each execution row IS a span. Spans share a trace_id; child spans point at their parent via parent_span_id. You don't instantiate spans, you don't import a tracer; you just write your handler and the platform plumbs IDs through every internal hop.

`,2),d(o(nn)),r[56]||=c(`h3`,{class:`doc-h3`},` What user code sees `,-1),r[57]||=c(`p`,{class:`doc-prose`},` Two env vars are stamped per invocation. Read them only if you want to log the trace_id alongside your own messages; they're optional. `,-1),d(o(Z),{code:zt,lang:`text`}),r[58]||=c(`h3`,{class:`doc-h3`},` Automatic propagation `,-1),r[59]||=c(`p`,{class:`doc-prose`},[m(` When a function calls another via the SDK, the trace context flows through automatically. The called function becomes a child span of the caller; both share the same `),c(`code`,{class:`doc-chip`},`trace_id`),m(`. `)],-1),d(o(Z),{code:Bt,lang:`js`}),r[60]||=h(`

Job enqueues work the same way: orva.jobs.enqueue() records the trace context on the job row. When the scheduler picks the job up later, the resulting execution lands in the same trace as the function that enqueued it, even if the gap is hours or days.

Triggers

Each span carries a trigger label so the UI can show how the chain started.

`,3),c(`div`,vt,[c(`table`,yt,[r[54]||=c(`thead`,null,[c(`tr`,null,[c(`th`,null,`Trigger`),c(`th`,null,`Meaning`)])],-1),c(`tbody`,null,[(t(),l(g,null,f(U,e=>c(`tr`,{key:e.name},[c(`td`,bt,[c(`code`,null,y(e.name),1)]),c(`td`,xt,y(e.desc),1)])),64))])])]),r[61]||=c(`h3`,{class:`doc-h3`},` External correlation (W3C traceparent) `,-1),r[62]||=c(`p`,{class:`doc-prose`},[m(` Send a standard `),c(`code`,{class:`doc-chip`},`traceparent`),m(` header on the inbound HTTP request and Orva makes its trace a child of yours. The same trace_id is echoed back as `),c(`code`,{class:`doc-chip`},`X-Trace-Id`),m(` on every response, so external systems can correlate without parsing bodies. `)],-1),d(o(Z),{code:Vt,lang:`bash`}),r[63]||=h(`

Outlier detection

Each function maintains an in-memory rolling P95 baseline over its last 100 successful warm executions. An invocation is flagged as an outlier when it has at least 20 baseline samples AND its duration exceeds P95 × 2. Cold starts and errors are excluded from the baseline so a flapping function can't drag it down. The flag and baseline P95 are stored on the execution row and rendered as an amber flag icon next to the span.

Where to look

  • /traces: list of recent traces, filterable by function / status / outlier-only.
  • /traces/:id: waterfall + per-span detail. Click a span to jump to its execution in the Invocations log.
  • GET /api/v1/traces/{id}: full span tree as JSON. Pair with list_traces / get_trace MCP tools for AI agents.
  • GET /api/v1/functions/{id}/baseline: current P95/P99/mean for a function.
`,4)]),c(`section`,St,[r[65]||=h(`
10

Errors & recovery

Every error response uses the same envelope so log scrapers and retries can match on code. Deploys are content-addressed; rollback retargets the active version pointer and refreshes warm workers.

`,1),d(o(Z),{code:Ht,lang:`json`}),c(`div`,Ct,[c(`table`,wt,[r[64]||=c(`thead`,null,[c(`tr`,null,[c(`th`,null,`Code`),c(`th`,null,`When you see it`)])],-1),c(`tbody`,null,[(t(),l(g,null,f(yn,e=>c(`tr`,{key:e.code},[c(`td`,Tt,[c(`code`,null,y(e.code),1)]),c(`td`,Et,y(e.when),1)])),64))])])])]),c(`section`,Dt,[r[82]||=h(`
11

CLI

Orva ships a full Linux server binary and a slim cross-platform CLI, both named orva. They share every client command; only the full build adds serve, setup, and init.

Install (server included)
curl … install.sh | sh

Full install: daemon + nsjail + rootfs + CLI.

Install (CLI only)
curl … install-cli.sh | sh

~20 MB binary at /usr/local/bin/orva. No service.

Inside Docker
docker exec orva orva …

CLI auto-authed via ~/.orva/config.yaml.

Authenticate

`,3),c(`p`,Ot,[r[67]||=m(` The CLI reads `,-1),r[68]||=c(`code`,{class:`doc-chip`},`~/.orva/config.yaml`,-1),r[69]||=m(` for `,-1),r[70]||=c(`code`,{class:`doc-chip`},`endpoint`,-1),r[71]||=m(` + `,-1),r[72]||=c(`code`,{class:`doc-chip`},`api_key`,-1),r[73]||=m(`. Generate a key from `,-1),d(a,{to:`/api-keys`,class:`text-foreground hover:text-white underline decoration-dotted underline-offset-4`},{default:n(()=>[...r[66]||=[m(` Keys `,-1)]]),_:1}),r[74]||=m(` in the dashboard, then: `,-1)]),d(o(Z),{code:Ut,lang:`bash`}),r[83]||=c(`h3`,{class:`doc-h3`},` Command index `,-1),c(`div`,kt,[c(`table`,At,[r[75]||=c(`thead`,null,[c(`tr`,null,[c(`th`,null,`Command`),c(`th`,null,`Subcommands`),c(`th`,{class:`hidden md:table-cell`},` Purpose `)])],-1),c(`tbody`,null,[(t(),l(g,null,f(bn,e=>c(`tr`,{key:e.cmd},[c(`td`,jt,[c(`code`,null,`orva `+y(e.cmd),1)]),c(`td`,Mt,y(e.subs),1),c(`td`,Nt,y(e.purpose),1)])),64))])])]),r[84]||=c(`h3`,{class:`doc-h3`},` Common recipes `,-1),c(`div`,Pt,[r[76]||=c(`div`,{class:`doc-microlabel`},` Deploy a function from a directory `,-1),d(o(Z),{code:Wt,lang:`bash`})]),c(`div`,Ft,[r[77]||=c(`div`,{class:`doc-microlabel`},` Invoke + tail logs `,-1),d(o(Z),{code:Gt,lang:`bash`})]),c(`div`,It,[r[78]||=c(`div`,{class:`doc-microlabel`},` Manage KV state `,-1),d(o(Z),{code:Kt,lang:`bash`})]),c(`div`,Lt,[r[79]||=c(`div`,{class:`doc-microlabel`},` Secrets, cron, jobs, webhooks `,-1),d(o(Z),{code:qt,lang:`bash`})]),c(`div`,Rt,[r[80]||=c(`div`,{class:`doc-microlabel`},` System health, metrics, vacuum `,-1),d(o(Z),{code:Jt,lang:`bash`})]),d(o($),{icon:o(w),title:`Shell completion`},{default:n(()=>[...r[81]||=[m(` Generate completion for your shell: `,-1),c(`code`,{class:`doc-chip`},`orva completion bash | sudo tee /etc/bash_completion.d/orva`,-1),m(`, or `,-1),c(`code`,{class:`doc-chip`},`zsh`,-1),m(` / `,-1),c(`code`,{class:`doc-chip`},`fish`,-1),m(` / `,-1),c(`code`,{class:`doc-chip`},`powershell`,-1),m(`. Tab-completes commands, subcommands, and flags. `,-1)]]),_:1},8,[`icon`])])])}}};export{Yt as default}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/Drawer-ZggviMzu.js b/backend/internal/server/ui_dist/assets/Drawer-B98TBytl.js similarity index 96% rename from backend/internal/server/ui_dist/assets/Drawer-ZggviMzu.js rename to backend/internal/server/ui_dist/assets/Drawer-B98TBytl.js index 4ff4b8ec..d544765a 100644 --- a/backend/internal/server/ui_dist/assets/Drawer-ZggviMzu.js +++ b/backend/internal/server/ui_dist/assets/Drawer-B98TBytl.js @@ -1 +1 @@ -import{A as e,D as t,E as n,F as r,G as i,P as a,T as o,Z as s,_t as c,a as l,c as u,d,h as f,l as p,m,u as h,vt as g,x as _}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{Mt as v,ut as y,vt as b,zt as x}from"./index-C11HmDXN.js";var S={key:0,class:`fixed inset-0 z-50 pointer-events-none`},C={class:`px-5 py-3 border-b border-border flex items-center justify-between shrink-0`},w={class:`text-sm font-medium text-white truncate`},T={class:`flex-1 overflow-y-auto scrollable`},E={key:0,class:`px-5 py-3 border-t border-border shrink-0`},D=y(Object.assign({name:`CommonDrawer`},{__name:`Drawer`,props:{modelValue:{type:Boolean,default:!1},title:{type:String,default:``},width:{type:String,default:`560px`}},emits:[`update:modelValue`],setup(y,{emit:D}){let O=y,k=D,A=i(null),j=()=>k(`update:modelValue`,!1);a(()=>O.modelValue,async e=>{e&&(await _(),A.value?.focus?.())});let M=e=>{e.key===`Escape`&&O.modelValue&&j()};return o(()=>window.addEventListener(`keydown`,M)),n(()=>window.removeEventListener(`keydown`,M)),(n,i)=>(t(),p(l,{to:`body`},[f(v,{name:`drawer-fade`},{default:r(()=>[y.modelValue?(t(),d(`div`,S,[u(`div`,{class:`absolute inset-0 pointer-events-auto`,onClick:j}),f(v,{name:`drawer-slide`},{default:r(()=>[y.modelValue?(t(),d(`div`,{key:0,ref_key:`root`,ref:A,class:`absolute pointer-events-auto bg-background flex flex-col inset-x-0 bottom-0 max-h-[85dvh] border-t border-border rounded-t-lg pb-safe sm:inset-x-auto sm:right-0 sm:top-0 sm:bottom-0 sm:max-h-none sm:border-t-0 sm:border-l sm:rounded-none sm:pb-0 sm:w-[var(--drawer-w,560px)]`,style:c({"--drawer-w":y.width}),tabindex:`-1`,onKeydown:x(j,[`esc`])},[u(`header`,C,[u(`div`,w,[e(n.$slots,`title`,{},()=>[m(g(y.title),1)],!0)]),u(`button`,{class:`text-foreground-muted hover:text-white transition-colors touch-expand-iconbtn -mr-1`,"aria-label":`Close`,onClick:j},[f(s(b),{class:`w-4 h-4`})])]),u(`div`,T,[e(n.$slots,`default`,{},void 0,!0)]),n.$slots.footer?(t(),d(`footer`,E,[e(n.$slots,`footer`,{},void 0,!0)])):h(``,!0)],36)):h(``,!0)]),_:3})])):h(``,!0)]),_:3})]))}}),[[`__scopeId`,`data-v-42911a7f`]]);export{D as t}; \ No newline at end of file +import{A as e,D as t,E as n,F as r,G as i,P as a,T as o,Z as s,_t as c,a as l,c as u,d,h as f,l as p,m,u as h,vt as g,x as _}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{Mt as v,ut as y,vt as b,zt as x}from"./index-DTqMKlE1.js";var S={key:0,class:`fixed inset-0 z-50 pointer-events-none`},C={class:`px-5 py-3 border-b border-border flex items-center justify-between shrink-0`},w={class:`text-sm font-medium text-white truncate`},T={class:`flex-1 overflow-y-auto scrollable`},E={key:0,class:`px-5 py-3 border-t border-border shrink-0`},D=y(Object.assign({name:`CommonDrawer`},{__name:`Drawer`,props:{modelValue:{type:Boolean,default:!1},title:{type:String,default:``},width:{type:String,default:`560px`}},emits:[`update:modelValue`],setup(y,{emit:D}){let O=y,k=D,A=i(null),j=()=>k(`update:modelValue`,!1);a(()=>O.modelValue,async e=>{e&&(await _(),A.value?.focus?.())});let M=e=>{e.key===`Escape`&&O.modelValue&&j()};return o(()=>window.addEventListener(`keydown`,M)),n(()=>window.removeEventListener(`keydown`,M)),(n,i)=>(t(),p(l,{to:`body`},[f(v,{name:`drawer-fade`},{default:r(()=>[y.modelValue?(t(),d(`div`,S,[u(`div`,{class:`absolute inset-0 pointer-events-auto`,onClick:j}),f(v,{name:`drawer-slide`},{default:r(()=>[y.modelValue?(t(),d(`div`,{key:0,ref_key:`root`,ref:A,class:`absolute pointer-events-auto bg-background flex flex-col inset-x-0 bottom-0 max-h-[85dvh] border-t border-border rounded-t-lg pb-safe sm:inset-x-auto sm:right-0 sm:top-0 sm:bottom-0 sm:max-h-none sm:border-t-0 sm:border-l sm:rounded-none sm:pb-0 sm:w-[var(--drawer-w,560px)]`,style:c({"--drawer-w":y.width}),tabindex:`-1`,onKeydown:x(j,[`esc`])},[u(`header`,C,[u(`div`,w,[e(n.$slots,`title`,{},()=>[m(g(y.title),1)],!0)]),u(`button`,{class:`text-foreground-muted hover:text-white transition-colors touch-expand-iconbtn -mr-1`,"aria-label":`Close`,onClick:j},[f(s(b),{class:`w-4 h-4`})])]),u(`div`,T,[e(n.$slots,`default`,{},void 0,!0)]),n.$slots.footer?(t(),d(`footer`,E,[e(n.$slots,`footer`,{},void 0,!0)])):h(``,!0)],36)):h(``,!0)]),_:3})])):h(``,!0)]),_:3})]))}}),[[`__scopeId`,`data-v-42911a7f`]]);export{D as t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/Editor-BENkmvs8.js b/backend/internal/server/ui_dist/assets/Editor-C_JYWteY.js similarity index 98% rename from backend/internal/server/ui_dist/assets/Editor-BENkmvs8.js rename to backend/internal/server/ui_dist/assets/Editor-C_JYWteY.js index c4f3c964..1190b2b0 100644 --- a/backend/internal/server/ui_dist/assets/Editor-BENkmvs8.js +++ b/backend/internal/server/ui_dist/assets/Editor-C_JYWteY.js @@ -1,5 +1,5 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/CodeEditor-CAQF33k1.js","assets/runtime-core.esm-bundler-BMQPyJ_W.js","assets/index-C11HmDXN.js","assets/axios-DVDuIpRy.js","assets/pinia-B41TZNUX.js","assets/client-BF51V3uE.js","assets/index-C9cn_46N.css","assets/dist-CR15Grce.js","assets/CodeEditor-x1XGuNl0.css"])))=>i.map(i=>d[i]); -import{C as e,D as t,F as n,G as r,I as i,M as ee,P as a,T as te,Z as o,c as s,d as c,g as ne,gt as l,h as u,j as re,k as d,l as ie,m as f,r as p,s as m,u as h,v as ae,vt as g}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as oe}from"./book-open-BnyP35ri.js";import{t as se}from"./check-DQm9OMOd.js";import{n as ce,t as le}from"./git-compare-Dh2_YhZP.js";import{t as ue}from"./copy-Bt6Rv0BQ.js";import{n as de,r as fe,t as pe}from"./settings-2-C7i2we5c.js";import{t as me}from"./globe-C84kKXm0.js";import{t as he}from"./key-round-BmyLzD9o.js";import{t as ge}from"./lock-QGBYN3Zu.js";import{t as _e}from"./play-DdJW9ehy.js";import{t as ve}from"./rotate-ccw-DBtrGGiP.js";import{t as ye}from"./shield-check-HnHCfFke.js";import{t as be}from"./sparkles-BmmGPRlQ.js";import{t as xe}from"./terminal-BVgEq2_Q.js";import{t as Se}from"./trash-2-Bj4AFUfw.js";import{t as Ce}from"./variable-Doeff6Y-.js";import{r as we,t as _}from"./client-BF51V3uE.js";import{Bt as Te,Ct as Ee,Dt as De,It as Oe,Lt as v,Nt as ke,Rt as Ae,W as je,_ as Me,_t as y,et as Ne,gt as Pe,jt as Fe,k as Ie,m as Le,mt as Re,ot as ze,pt as Be,rt as Ve,t as He,ut as Ue,vt as We,yt as Ge,z as Ke,zt as qe}from"./index-C11HmDXN.js";import{t as Je}from"./clipboard-D_9N0yai.js";import{t as Ye}from"./Modal-BL-P3nUi.js";import{t as Xe}from"./rollbackDiff-DsaWcdbl.js";import{n as Ze}from"./aiPrompts-NjljN3ld.js";import{t as Qe}from"./Input-DQ-tWGkn.js";var $e=Fe(`database`,[[`ellipse`,{cx:`12`,cy:`5`,rx:`9`,ry:`3`,key:`msslwz`}],[`path`,{d:`M3 5V19A9 3 0 0 0 21 19V5`,key:`1wlel7`}],[`path`,{d:`M3 12A9 3 0 0 0 21 12`,key:`mv7ke4`}]]),et=Fe(`layers`,[[`path`,{d:`M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z`,key:`zw3jo`}],[`path`,{d:`M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12`,key:`1wduqc`}],[`path`,{d:`M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17`,key:`kqbvx6`}]]),tt=Fe(`shuffle`,[[`path`,{d:`m18 14 4 4-4 4`,key:`10pe0f`}],[`path`,{d:`m18 2 4 4-4 4`,key:`pucp1d`}],[`path`,{d:`M2 18h1.973a4 4 0 0 0 3.3-1.7l5.454-8.6a4 4 0 0 1 3.3-1.7H22`,key:`1ailkh`}],[`path`,{d:`M2 6h1.972a4 4 0 0 1 3.6 2.2`,key:`km57vx`}],[`path`,{d:`M22 18h-6.041a4 4 0 0 1-3.3-1.8l-.359-.45`,key:`os18l9`}]]),b=`amber.arctic.aurora.bold.brave.breezy.bright.brisk.calm.celestial.cobalt.cosmic.crimson.crisp.crystal.dapper.dazzling.deep.eager.ember.fearless.feisty.fierce.flaming.fluent.fluorescent.frosty.gentle.glacial.golden.graceful.happy.hazy.icy.indigo.jade.jolly.jovial.keen.kindred.lavender.lively.lucent.lunar.magenta.magnetic.merry.midnight.mighty.mellow.mossy.mystic.neon.nimble.noble.obsidian.opal.pearl.peppy.pixel.plucky.plush.polar.prime.quartz.quick.quiet.radiant.rapid.rare.roaming.rosy.royal.rugged.runic.rustic.sapphire.scarlet.sharp.silent.silken.silver.sleek.smooth.snowy.snug.solar.sonic.spry.starlit.stellar.sturdy.sublime.sunny.svelte.swift.tame.tender.thunder.tidal.topaz.tropic.turquoise.twilight.urban.velvet.verdant.vibrant.violet.vivid.warm.whisper.wild.wise.witty.woven.zesty.zen`.split(`.`),nt=`albatross.amber.antler.apricot.archer.arrow.atlas.aurora.badger.bayou.beacon.bison.blossom.bramble.breeze.cactus.canyon.caravan.cedar.cliff.comet.compass.coral.cosmos.cypress.dawn.delta.dolphin.drift.dune.eagle.ember.fable.falcon.fern.fjord.flame.flint.forest.galaxy.garnet.geyser.glacier.glade.glint.gorge.gull.harbor.haven.horizon.iceberg.iris.jaguar.jetty.jungle.kelp.kestrel.kettle.kraken.lagoon.lantern.lark.ledge.lily.lighthouse.lupine.lynx.maple.meadow.meridian.meteor.mirage.mistral.monsoon.moon.moss.mountain.nebula.oak.oasis.ocean.orchid.osprey.otter.panda.panther.parrot.pebble.phoenix.pine.pinion.pixel.planet.plume.pond.poppy.prairie.puffin.puma.quartz.quasar.quill.rapids.raven.reef.ridge.river.robin.rune.sage.satellite.savanna.sequoia.shadow.signal.silo.sky.sloth.snow.sparrow.spire.star.stream.summit.swan.tempest.thicket.thistle.thunder.tide.tiger.totem.tower.tundra.twilight.twister.valley.vortex.walnut.wave.whale.whisper.wildflower.willow.wolf.wren.zenith.zephyr`.split(`.`),x=e=>e[Math.floor(Math.random()*e.length)];function rt(){return`${x(b)}-${x(nt)}`}var S=`import json +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/CodeEditor-CiRqvRcK.js","assets/runtime-core.esm-bundler-BMQPyJ_W.js","assets/index-DTqMKlE1.js","assets/axios-DVDuIpRy.js","assets/pinia-B41TZNUX.js","assets/client-BF51V3uE.js","assets/index-C9cn_46N.css","assets/dist-CR15Grce.js","assets/CodeEditor-x1XGuNl0.css"])))=>i.map(i=>d[i]); +import{C as e,D as t,F as n,G as r,I as i,M as ee,P as a,T as te,Z as o,c as s,d as c,g as ne,gt as l,h as u,j as re,k as d,l as ie,m as f,r as p,s as m,u as h,v as ae,vt as g}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as oe}from"./book-open-B7-cMpW6.js";import{t as se}from"./check-BNre7JFR.js";import{n as ce,t as le}from"./git-compare-Co883xFS.js";import{t as ue}from"./copy-BqdwwcxC.js";import{n as de,r as fe,t as pe}from"./settings-2-D5QtFfdZ.js";import{t as me}from"./globe-BnBar2lO.js";import{t as he}from"./key-round-BKXtbC85.js";import{t as ge}from"./lock-CttMBTH5.js";import{t as _e}from"./play-CmQifd74.js";import{t as ve}from"./rotate-ccw-DWwjKCqh.js";import{t as ye}from"./shield-check-piXkOtNv.js";import{t as be}from"./sparkles-DTHEIS5T.js";import{t as xe}from"./terminal-BQdlNiyt.js";import{t as Se}from"./trash-2-DaeYqnW_.js";import{t as Ce}from"./variable-DrK2KZuk.js";import{r as we,t as _}from"./client-BF51V3uE.js";import{Bt as Te,Ct as Ee,Dt as De,It as Oe,Lt as v,Nt as ke,Rt as Ae,W as je,_ as Me,_t as y,et as Ne,gt as Pe,jt as Fe,k as Ie,m as Le,mt as Re,ot as ze,pt as Be,rt as Ve,t as He,ut as Ue,vt as We,yt as Ge,z as Ke,zt as qe}from"./index-DTqMKlE1.js";import{t as Je}from"./clipboard-D_9N0yai.js";import{t as Ye}from"./Modal-BAoZams6.js";import{t as Xe}from"./rollbackDiff-DsaWcdbl.js";import{n as Ze}from"./aiPrompts-XrsFCpj_.js";import{t as Qe}from"./Input-DQ-tWGkn.js";var $e=Fe(`database`,[[`ellipse`,{cx:`12`,cy:`5`,rx:`9`,ry:`3`,key:`msslwz`}],[`path`,{d:`M3 5V19A9 3 0 0 0 21 19V5`,key:`1wlel7`}],[`path`,{d:`M3 12A9 3 0 0 0 21 12`,key:`mv7ke4`}]]),et=Fe(`layers`,[[`path`,{d:`M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z`,key:`zw3jo`}],[`path`,{d:`M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12`,key:`1wduqc`}],[`path`,{d:`M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17`,key:`kqbvx6`}]]),tt=Fe(`shuffle`,[[`path`,{d:`m18 14 4 4-4 4`,key:`10pe0f`}],[`path`,{d:`m18 2 4 4-4 4`,key:`pucp1d`}],[`path`,{d:`M2 18h1.973a4 4 0 0 0 3.3-1.7l5.454-8.6a4 4 0 0 1 3.3-1.7H22`,key:`1ailkh`}],[`path`,{d:`M2 6h1.972a4 4 0 0 1 3.6 2.2`,key:`km57vx`}],[`path`,{d:`M22 18h-6.041a4 4 0 0 1-3.3-1.8l-.359-.45`,key:`os18l9`}]]),b=`amber.arctic.aurora.bold.brave.breezy.bright.brisk.calm.celestial.cobalt.cosmic.crimson.crisp.crystal.dapper.dazzling.deep.eager.ember.fearless.feisty.fierce.flaming.fluent.fluorescent.frosty.gentle.glacial.golden.graceful.happy.hazy.icy.indigo.jade.jolly.jovial.keen.kindred.lavender.lively.lucent.lunar.magenta.magnetic.merry.midnight.mighty.mellow.mossy.mystic.neon.nimble.noble.obsidian.opal.pearl.peppy.pixel.plucky.plush.polar.prime.quartz.quick.quiet.radiant.rapid.rare.roaming.rosy.royal.rugged.runic.rustic.sapphire.scarlet.sharp.silent.silken.silver.sleek.smooth.snowy.snug.solar.sonic.spry.starlit.stellar.sturdy.sublime.sunny.svelte.swift.tame.tender.thunder.tidal.topaz.tropic.turquoise.twilight.urban.velvet.verdant.vibrant.violet.vivid.warm.whisper.wild.wise.witty.woven.zesty.zen`.split(`.`),nt=`albatross.amber.antler.apricot.archer.arrow.atlas.aurora.badger.bayou.beacon.bison.blossom.bramble.breeze.cactus.canyon.caravan.cedar.cliff.comet.compass.coral.cosmos.cypress.dawn.delta.dolphin.drift.dune.eagle.ember.fable.falcon.fern.fjord.flame.flint.forest.galaxy.garnet.geyser.glacier.glade.glint.gorge.gull.harbor.haven.horizon.iceberg.iris.jaguar.jetty.jungle.kelp.kestrel.kettle.kraken.lagoon.lantern.lark.ledge.lily.lighthouse.lupine.lynx.maple.meadow.meridian.meteor.mirage.mistral.monsoon.moon.moss.mountain.nebula.oak.oasis.ocean.orchid.osprey.otter.panda.panther.parrot.pebble.phoenix.pine.pinion.pixel.planet.plume.pond.poppy.prairie.puffin.puma.quartz.quasar.quill.rapids.raven.reef.ridge.river.robin.rune.sage.satellite.savanna.sequoia.shadow.signal.silo.sky.sloth.snow.sparrow.spire.star.stream.summit.swan.tempest.thicket.thistle.thunder.tide.tiger.totem.tower.tundra.twilight.twister.valley.vortex.walnut.wave.whale.whisper.wildflower.willow.wolf.wren.zenith.zephyr`.split(`.`),x=e=>e[Math.floor(Math.random()*e.length)];function rt(){return`${x(b)}-${x(nt)}`}var S=`import json def handler(event): @@ -872,7 +872,7 @@ def handler(event): body: JSON.stringify({ message: \`Hello \${name}!\`, language: 'Node.js' }), } } -`,ft={python:[{id:`py-http-hello`,category:`Starter`,label:`HTTP Hello`,description:`Minimal POST /. Echoes a name from the JSON body.`,code:S,deps:``},{id:`py-stripe-webhook`,category:`Webhooks`,label:`Stripe webhook`,description:`Verify Stripe signature; branch on event.type.`,code:it,deps:``},{id:`py-github-webhook`,category:`Webhooks`,label:`GitHub webhook`,description:`HMAC-SHA256 verification; route on X-GitHub-Event.`,code:C,deps:``},{id:`py-slack-bot`,category:`Webhooks`,label:`Slack slash command`,description:`Parse application/x-www-form-urlencoded; respond inline.`,code:at,deps:``},{id:`py-csv-to-json`,category:`Utility`,label:`CSV → JSON`,description:`Parse a CSV upload and return a JSON array of rows.`,code:ot,deps:``},{id:`py-cron-cleanup`,category:`Scheduled`,label:`Scheduled DB cleanup`,cron:!0,description:`Daily delete of rows older than RETENTION_DAYS.`,code:st,deps:``},{id:`py-rss-summarize`,category:`Utility`,label:`RSS summarizer`,description:`Fetch an RSS / Atom feed and return latest N items.`,code:ct,deps:``},{id:`py-image-thumbnail`,category:`Utility`,label:`Image thumbnail`,description:`Resize a base64 image with Pillow; returns PNG.`,code:w,deps:lt},{id:`py-guestbook`,category:`Showcase`,label:`Guestbook (full-stack showcase)`,cron:!0,description:`HTML page + JSON API + KV + jobs + cron + secrets in one file. Best demo of what one Orva function can do.`,code:ut,deps:``},{id:`py-stream-llm`,category:`Showcase`,label:`Streaming LLM tokens`,description:`Generator that yields one word every 100ms. Demonstrates v0.4 chunked streaming end-to-end.`,code:dt,deps:``},{id:`py-traced-pipeline`,category:`Showcase`,label:`v0.6 SDK pipeline`,description:`trace.span + log.info + kv.incr + idempotent jobs.enqueue. After invoking, open the new trace — child spans and structured logs render inline in the waterfall.`,code:`"""v0.6 SDK showcase: trace.span + log.* + kv.incr + jobs.enqueue. +`,ft={python:[{id:`py-http-hello`,category:`Starter`,label:`HTTP Hello`,description:`Minimal POST /. Echoes a name from the JSON body.`,code:S,deps:``},{id:`py-stripe-webhook`,category:`Webhooks`,label:`Stripe webhook`,description:`Verify Stripe signature; branch on event.type.`,code:it,deps:``},{id:`py-github-webhook`,category:`Webhooks`,label:`GitHub webhook`,description:`HMAC-SHA256 verification; route on X-GitHub-Event.`,code:C,deps:``},{id:`py-slack-bot`,category:`Webhooks`,label:`Slack slash command`,description:`Parse application/x-www-form-urlencoded; respond inline.`,code:at,deps:``},{id:`py-csv-to-json`,category:`Utility`,label:`CSV → JSON`,description:`Parse a CSV upload and return a JSON array of rows.`,code:ot,deps:``},{id:`py-cron-cleanup`,category:`Scheduled`,label:`Scheduled DB cleanup`,cron:!0,description:`Daily delete of rows older than RETENTION_DAYS.`,code:st,deps:``},{id:`py-rss-summarize`,category:`Utility`,label:`RSS summarizer`,description:`Fetch an RSS / Atom feed and return latest N items.`,code:ct,deps:``},{id:`py-image-thumbnail`,category:`Utility`,label:`Image thumbnail`,description:`Resize a base64 image with Pillow; returns PNG.`,code:w,deps:lt},{id:`py-guestbook`,category:`Showcase`,label:`Guestbook (full-stack showcase)`,cron:!0,description:`HTML page + JSON API + KV + jobs + cron + secrets in one file. Best demo of what one Orva function can do.`,code:ut,deps:``},{id:`py-stream-llm`,category:`Showcase`,label:`Streaming LLM tokens`,description:`Generator that yields one word every 100ms. Demonstrates v0.4 chunked streaming end-to-end.`,code:dt,deps:``},{id:`py-traced-pipeline`,category:`Showcase`,label:`v0.7 SDK pipeline`,description:`trace.span + log.info + kv.incr + idempotent jobs.enqueue. After invoking, open the new trace — child spans and structured logs render inline in the waterfall.`,code:`"""v0.7 SDK showcase: trace.span + log.* + kv.incr + jobs.enqueue. Open this trace in the dashboard after invoking — you'll see system spans, your two child spans ('parse' / 'transform'), and the level-tagged log lines all interleaved by timestamp.""" @@ -1187,7 +1187,7 @@ exports.handler = async (event) => { return { statusCode: 302, headers: { 'Location': url }, body: '' } } -`,deps:``},{id:`node-counter-cas`,category:`Showcase`,label:`v0.6 SDK counter (kv.incr + kv.cas)`,description:`Atomic counter and a CAS-retry loop for richer state. Demonstrates v0.6 race-safe primitives and structured logging end-to-end.`,code:`// v0.6 SDK showcase: kv.incr + kv.cas + log.info. +`,deps:``},{id:`node-counter-cas`,category:`Showcase`,label:`v0.7 SDK counter (kv.incr + kv.cas)`,description:`Atomic counter and a CAS-retry loop for richer state. Demonstrates v0.7 race-safe primitives and structured logging end-to-end.`,code:`// v0.7 SDK showcase: kv.incr + kv.cas + log.info. // Demonstrates the recommended pattern for race-safe state mutation // from inside an Orva function. @@ -1274,7 +1274,7 @@ export = handler "include": ["handler.ts"], "exclude": ["node_modules", "dist"] } -`},entrypoint:`handler.ts`}]},pt={python:S,node:T},mt=[`Starter`,`Webhooks`,`Auth`,`Utility`,`Scheduled`,`Showcase`],ht={class:`flex flex-col h-full`},gt={class:`sr-only`},_t={class:`flex flex-col sm:flex-row sm:flex-wrap sm:items-center gap-2 pb-3 border-b border-border`},vt={class:`flex items-center gap-2 sm:mr-auto min-w-0 w-full sm:w-auto`},yt=[`disabled`],bt={class:`text-[11px] text-foreground-muted font-medium tracking-tight shrink-0`},xt={class:`flex flex-wrap items-center gap-2 sm:contents`},St=[`aria-expanded`],Ct={key:0,class:`absolute right-0 mt-1 z-30 min-w-[210px] bg-background border border-border rounded-md shadow-xl overflow-hidden`,role:`menu`},wt={key:0,class:`text-[10px] text-foreground-muted tabular-nums`},Tt={key:0,class:`text-[10px] text-foreground-muted tabular-nums`},Et={class:`text-[10px] text-foreground-muted tabular-nums`},Dt=[`aria-expanded`],Ot={key:0,class:`absolute right-0 mt-1 z-30 min-w-[210px] bg-background border border-border rounded-md shadow-xl overflow-hidden`,role:`menu`},kt={key:0,class:`flex items-center gap-2 px-2 py-1.5 mt-2 border border-border bg-surface rounded text-xs`},At={class:`font-mono text-white truncate flex-1 min-w-0`},jt={class:`flex-1 flex flex-col min-h-0 mt-3 bg-background border border-border rounded-lg overflow-hidden shadow-sm`},Mt={class:`h-9 border-b border-border flex items-center justify-between px-4 bg-surface shrink-0`},Nt={class:`text-xs font-mono text-foreground-muted flex items-center gap-2`},Pt={class:`text-white`},Ft={key:0,class:`text-foreground-muted`},It={class:`text-[10px] text-foreground-muted font-mono`},Lt={class:`mt-3 bg-background border border-border rounded-lg overflow-hidden shrink-0`},Rt={class:`h-9 border-b border-border flex items-center px-2 bg-surface`},zt=[`onClick`],Bt={key:0,class:`ml-1 text-[10px] px-1.5 rounded bg-surface-hover text-foreground-muted`},Vt={class:`ml-auto flex items-center gap-1`},Ht=[`disabled`,`title`],Ut={key:1,class:`run-spinner`},Wt=[`title`,`aria-label`],Gt={class:`h-48 overflow-y-auto bg-background`},Kt={key:0,class:`p-3 font-mono text-xs space-y-0.5`},qt={key:0,class:`text-foreground-muted`},Jt={key:1,class:`grid grid-cols-1 md:grid-cols-[minmax(0,1fr)_minmax(0,1.3fr)] h-full`},Yt={class:`flex flex-col min-h-0 border-b md:border-b-0 md:border-r border-border`},Xt={class:`h-7 px-2 flex items-center gap-1.5 bg-surface/60 border-b border-border shrink-0`},Zt=[`disabled`],Qt=[`value`],$t=[`disabled`],en={class:`relative shrink-0`},tn=[`disabled`,`title`],nn={key:0,class:`text-[10px] text-foreground-muted`},rn={key:0,class:`px-3 py-3 text-[11px] text-foreground-muted italic`},an={key:1,class:`max-h-56 overflow-y-auto`},on=[`onClick`],sn={class:`font-mono text-[10px] text-foreground-muted shrink-0`},cn={class:`truncate flex-1 text-foreground`},ln=[`title`,`aria-label`,`onClick`],un={class:`border-t border-border px-2 py-1.5 bg-surface/50`},dn=[`disabled`],fn={class:`border-b border-border shrink-0`},pn={key:0,class:`ml-1 text-foreground-muted/80 normal-case tracking-normal`},mn={key:0,class:`px-2 py-2 space-y-1`},hn=[`onUpdate:modelValue`,`disabled`],gn=[`onUpdate:modelValue`,`disabled`],_n=[`onClick`],vn=[`disabled`],yn={class:`h-6 px-3 flex items-center justify-between bg-surface/30 border-b border-border shrink-0`},bn={key:0,class:`text-[10px] text-amber-400/80`},xn={key:1,class:`text-[10px] text-foreground-muted/70 font-mono`},Sn=[`disabled`],Cn={class:`flex flex-col min-h-0`},wn={class:`h-7 px-3 flex items-center justify-between bg-surface/60 border-b border-border shrink-0`},Tn={class:`flex items-center gap-2`},En=[`disabled`],Dn={key:1,class:`text-[10px] text-foreground-muted/80 font-mono`},On={class:`flex-1 min-h-0 overflow-y-auto`},kn={key:1,class:`px-3 py-3 text-xs text-foreground-muted italic`},An={key:2,class:`border-t border-border`},jn={class:`h-6 px-3 flex items-center text-[10px] uppercase tracking-[0.14em] text-foreground-muted/80 bg-surface/30`},Mn={class:`px-3 py-2 font-mono text-xs space-y-0.5`},Nn={class:`space-y-4`},Pn={class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-1.5 flex items-center justify-between`},Fn={key:0,class:`text-[10px] normal-case tracking-normal text-success/80`},In={class:`grid grid-cols-2 gap-2`},Ln=[`onClick`],Rn=[`label`],zn=[`value`],Bn={key:0,class:`text-[11px] text-foreground-muted mt-1.5`},Vn={class:`grid grid-cols-2 gap-3`},Hn={class:`border-t border-border pt-4 space-y-2`},Un={class:`grid grid-cols-2 gap-3`},Wn=[`disabled`],Gn={class:`border-t border-border pt-4`},Kn={class:`flex items-start gap-3 cursor-pointer select-none`},qn={class:`min-w-0`},Jn={class:`text-sm font-medium text-white flex items-center gap-2`},Yn={class:`border-t border-border pt-4 space-y-2`},Xn={class:`text-xs font-medium text-foreground-muted uppercase tracking-wide flex items-center gap-2`},Zn={class:`border-t border-border pt-4 space-y-2`},Qn={class:`border-t border-border pt-4 space-y-3`},$n={class:`text-xs font-medium text-foreground-muted uppercase tracking-wide flex items-center gap-2`},er={key:0,class:`text-[10px] normal-case tracking-normal text-foreground-muted`},tr={key:0,class:`text-[11px] text-foreground-muted leading-snug`},nr={key:1,class:`text-[11px] text-foreground-muted leading-snug`},rr={class:`font-mono`},ir={key:2,class:`space-y-1.5`},ar={class:`flex-1 min-w-0 font-mono text-xs text-foreground truncate`},or={key:0,class:`text-[10px] font-mono text-foreground-muted`},sr=[`aria-label`,`onClick`],cr={key:3,class:`space-y-2 pt-1`},lr={class:`flex items-center gap-2`},ur={key:0,class:`text-[11px] text-amber-400 leading-snug`},dr={class:`font-mono`},fr={class:`font-mono`},pr={key:1,class:`text-[11px] text-red-400 leading-snug`},mr={class:`space-y-2`},hr=[`onUpdate:modelValue`],gr=[`onUpdate:modelValue`],_r=[`aria-label`,`onClick`],vr={class:`space-y-2`},yr={class:`text-[10px] text-foreground-muted font-mono`},br={class:`space-y-3`},xr={key:0,class:`text-xs text-foreground-muted`},Sr={key:0},Cr={class:`text-foreground-muted font-mono`},wr=[`aria-label`,`onClick`],Tr={class:`flex items-center gap-2 min-w-0`},Er={class:`text-foreground-muted font-mono`},Dr=[`aria-label`,`onClick`],Or={class:`border-t border-border pt-3 space-y-2`},kr={class:`space-y-2`},Ar={class:`flex items-center gap-2 min-w-0`},jr={class:`font-mono text-foreground-muted shrink-0`},Mr={key:0,class:`px-1.5 py-0.5 rounded text-[10px] bg-success/15 text-success border border-success/30 shrink-0`},Nr=[`title`],Pr={class:`text-foreground-muted shrink-0`},Fr={key:0,class:`shrink-0 flex items-center gap-2`},Ir=[`disabled`,`onClick`],Lr={class:`space-y-3 text-xs text-foreground-muted`},Rr={class:`bg-surface border border-border rounded p-3 font-mono text-[12px] text-white overflow-x-auto whitespace-pre`},zr={class:`space-y-4`},Br={class:`relative`},Vr={class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-1.5 flex items-center justify-between`},Hr={key:0,class:`text-[10px] normal-case tracking-normal text-success/80`},Ur={class:`grid grid-cols-2 gap-2`},Wr=[`onClick`],Gr={class:`grid grid-cols-2 gap-3`},E=Ue({__name:`Editor`,setup(Fe){let Ue=ne({loader:()=>He(()=>import(`./CodeEditor-CAQF33k1.js`),__vite__mapDeps([0,1,2,3,4,5,6,7,8])),loadingComponent:{render(){return ae(`div`,{class:`flex-1 min-h-0 w-full bg-background flex items-start`,"aria-busy":`true`,"aria-label":`Loading code editor`},[ae(`div`,{class:`p-4 space-y-2 w-full font-mono text-xs text-foreground-muted/40`},[ae(`div`,{class:`h-3 w-1/3 bg-surface-hover rounded animate-pulse`}),ae(`div`,{class:`h-3 w-2/3 bg-surface-hover rounded animate-pulse`}),ae(`div`,{class:`h-3 w-1/2 bg-surface-hover rounded animate-pulse`})])])}},delay:0}),b=Be(),nt=Re(),x=Pe(),S=r({settings:!1,envVars:!1,deps:!1,secrets:!1,versions:!1,docs:!1,firstDeploy:!1}),it=r(null),C=r({config:!1,bindings:!1}),at=r(null),ot=r(null),st=()=>{C.value.config=!1,C.value.bindings=!1},ct=e=>{let t=!C.value[e];st(),C.value[e]=t},w=e=>{st(),S.value[e]=!0},lt=e=>{st(),nt.push(e)},ut=e=>{if(!C.value.config&&!C.value.bindings)return;let t=at.value?.contains(e.target),n=ot.value?.contains(e.target);!t&&!n&&st()},dt=e=>{e.key===`Escape`&&(C.value.config||C.value.bindings)&&st()},T=r(!0),E=r(`build`),Kr=m(()=>[{id:`build`,label:`Build`,icon:xe,badge:V.value.length||null},{id:`test`,label:`Test`,icon:_e,badge:H.value.length||null}]),qr=e=>e?e===`node`?`Node.js 24`:e===`python`?`Python 3.14`:e:``,Jr=m(()=>U.value.filter(e=>e.key.trim()).length),D=r(``),O=r({name:``,description:``,runtime:`python`,memory_mb:64,cpus:.5,network_mode:`none`,max_concurrency:0,concurrency_policy:`queue`,auth_mode:`none`,rate_limit_per_min:0}),k=r(``),A=r(`{"name": "World"}`),j=r(`POST`),M=r(`/`),N=r([]),P=r(!1),Yr=[`GET`,`POST`,`PUT`,`PATCH`,`DELETE`],F=r([]),I=r(!1),Xr=m(()=>N.value.filter(e=>e.name&&e.name.trim()).length),L=r(!1),Zr=r(!1),Qr=r(!1),R=r(null),z=r(null),$r=r(0),B=r(``),V=r([]),H=r([]),ei=r(!1),ti=r(!1),ni=r(!1),ri=m(()=>k.value?`${window.location.origin}/fn/${k.value}`:``),ii=async()=>{ri.value&&(await Je(ri.value)?(ei.value=!0,setTimeout(()=>{ei.value=!1},1500)):x.notify({title:`Copy failed`,message:`Could not copy to clipboard. Select the URL manually: +`},entrypoint:`handler.ts`}]},pt={python:S,node:T},mt=[`Starter`,`Webhooks`,`Auth`,`Utility`,`Scheduled`,`Showcase`],ht={class:`flex flex-col h-full`},gt={class:`sr-only`},_t={class:`flex flex-col sm:flex-row sm:flex-wrap sm:items-center gap-2 pb-3 border-b border-border`},vt={class:`flex items-center gap-2 sm:mr-auto min-w-0 w-full sm:w-auto`},yt=[`disabled`],bt={class:`text-[11px] text-foreground-muted font-medium tracking-tight shrink-0`},xt={class:`flex flex-wrap items-center gap-2 sm:contents`},St=[`aria-expanded`],Ct={key:0,class:`absolute right-0 mt-1 z-30 min-w-[210px] bg-background border border-border rounded-md shadow-xl overflow-hidden`,role:`menu`},wt={key:0,class:`text-[10px] text-foreground-muted tabular-nums`},Tt={key:0,class:`text-[10px] text-foreground-muted tabular-nums`},Et={class:`text-[10px] text-foreground-muted tabular-nums`},Dt=[`aria-expanded`],Ot={key:0,class:`absolute right-0 mt-1 z-30 min-w-[210px] bg-background border border-border rounded-md shadow-xl overflow-hidden`,role:`menu`},kt={key:0,class:`flex items-center gap-2 px-2 py-1.5 mt-2 border border-border bg-surface rounded text-xs`},At={class:`font-mono text-white truncate flex-1 min-w-0`},jt={class:`flex-1 flex flex-col min-h-0 mt-3 bg-background border border-border rounded-lg overflow-hidden shadow-sm`},Mt={class:`h-9 border-b border-border flex items-center justify-between px-4 bg-surface shrink-0`},Nt={class:`text-xs font-mono text-foreground-muted flex items-center gap-2`},Pt={class:`text-white`},Ft={key:0,class:`text-foreground-muted`},It={class:`text-[10px] text-foreground-muted font-mono`},Lt={class:`mt-3 bg-background border border-border rounded-lg overflow-hidden shrink-0`},Rt={class:`h-9 border-b border-border flex items-center px-2 bg-surface`},zt=[`onClick`],Bt={key:0,class:`ml-1 text-[10px] px-1.5 rounded bg-surface-hover text-foreground-muted`},Vt={class:`ml-auto flex items-center gap-1`},Ht=[`disabled`,`title`],Ut={key:1,class:`run-spinner`},Wt=[`title`,`aria-label`],Gt={class:`h-48 overflow-y-auto bg-background`},Kt={key:0,class:`p-3 font-mono text-xs space-y-0.5`},qt={key:0,class:`text-foreground-muted`},Jt={key:1,class:`grid grid-cols-1 md:grid-cols-[minmax(0,1fr)_minmax(0,1.3fr)] h-full`},Yt={class:`flex flex-col min-h-0 border-b md:border-b-0 md:border-r border-border`},Xt={class:`h-7 px-2 flex items-center gap-1.5 bg-surface/60 border-b border-border shrink-0`},Zt=[`disabled`],Qt=[`value`],$t=[`disabled`],en={class:`relative shrink-0`},tn=[`disabled`,`title`],nn={key:0,class:`text-[10px] text-foreground-muted`},rn={key:0,class:`px-3 py-3 text-[11px] text-foreground-muted italic`},an={key:1,class:`max-h-56 overflow-y-auto`},on=[`onClick`],sn={class:`font-mono text-[10px] text-foreground-muted shrink-0`},cn={class:`truncate flex-1 text-foreground`},ln=[`title`,`aria-label`,`onClick`],un={class:`border-t border-border px-2 py-1.5 bg-surface/50`},dn=[`disabled`],fn={class:`border-b border-border shrink-0`},pn={key:0,class:`ml-1 text-foreground-muted/80 normal-case tracking-normal`},mn={key:0,class:`px-2 py-2 space-y-1`},hn=[`onUpdate:modelValue`,`disabled`],gn=[`onUpdate:modelValue`,`disabled`],_n=[`onClick`],vn=[`disabled`],yn={class:`h-6 px-3 flex items-center justify-between bg-surface/30 border-b border-border shrink-0`},bn={key:0,class:`text-[10px] text-amber-400/80`},xn={key:1,class:`text-[10px] text-foreground-muted/70 font-mono`},Sn=[`disabled`],Cn={class:`flex flex-col min-h-0`},wn={class:`h-7 px-3 flex items-center justify-between bg-surface/60 border-b border-border shrink-0`},Tn={class:`flex items-center gap-2`},En=[`disabled`],Dn={key:1,class:`text-[10px] text-foreground-muted/80 font-mono`},On={class:`flex-1 min-h-0 overflow-y-auto`},kn={key:1,class:`px-3 py-3 text-xs text-foreground-muted italic`},An={key:2,class:`border-t border-border`},jn={class:`h-6 px-3 flex items-center text-[10px] uppercase tracking-[0.14em] text-foreground-muted/80 bg-surface/30`},Mn={class:`px-3 py-2 font-mono text-xs space-y-0.5`},Nn={class:`space-y-4`},Pn={class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-1.5 flex items-center justify-between`},Fn={key:0,class:`text-[10px] normal-case tracking-normal text-success/80`},In={class:`grid grid-cols-2 gap-2`},Ln=[`onClick`],Rn=[`label`],zn=[`value`],Bn={key:0,class:`text-[11px] text-foreground-muted mt-1.5`},Vn={class:`grid grid-cols-2 gap-3`},Hn={class:`border-t border-border pt-4 space-y-2`},Un={class:`grid grid-cols-2 gap-3`},Wn=[`disabled`],Gn={class:`border-t border-border pt-4`},Kn={class:`flex items-start gap-3 cursor-pointer select-none`},qn={class:`min-w-0`},Jn={class:`text-sm font-medium text-white flex items-center gap-2`},Yn={class:`border-t border-border pt-4 space-y-2`},Xn={class:`text-xs font-medium text-foreground-muted uppercase tracking-wide flex items-center gap-2`},Zn={class:`border-t border-border pt-4 space-y-2`},Qn={class:`border-t border-border pt-4 space-y-3`},$n={class:`text-xs font-medium text-foreground-muted uppercase tracking-wide flex items-center gap-2`},er={key:0,class:`text-[10px] normal-case tracking-normal text-foreground-muted`},tr={key:0,class:`text-[11px] text-foreground-muted leading-snug`},nr={key:1,class:`text-[11px] text-foreground-muted leading-snug`},rr={class:`font-mono`},ir={key:2,class:`space-y-1.5`},ar={class:`flex-1 min-w-0 font-mono text-xs text-foreground truncate`},or={key:0,class:`text-[10px] font-mono text-foreground-muted`},sr=[`aria-label`,`onClick`],cr={key:3,class:`space-y-2 pt-1`},lr={class:`flex items-center gap-2`},ur={key:0,class:`text-[11px] text-amber-400 leading-snug`},dr={class:`font-mono`},fr={class:`font-mono`},pr={key:1,class:`text-[11px] text-red-400 leading-snug`},mr={class:`space-y-2`},hr=[`onUpdate:modelValue`],gr=[`onUpdate:modelValue`],_r=[`aria-label`,`onClick`],vr={class:`space-y-2`},yr={class:`text-[10px] text-foreground-muted font-mono`},br={class:`space-y-3`},xr={key:0,class:`text-xs text-foreground-muted`},Sr={key:0},Cr={class:`text-foreground-muted font-mono`},wr=[`aria-label`,`onClick`],Tr={class:`flex items-center gap-2 min-w-0`},Er={class:`text-foreground-muted font-mono`},Dr=[`aria-label`,`onClick`],Or={class:`border-t border-border pt-3 space-y-2`},kr={class:`space-y-2`},Ar={class:`flex items-center gap-2 min-w-0`},jr={class:`font-mono text-foreground-muted shrink-0`},Mr={key:0,class:`px-1.5 py-0.5 rounded text-[10px] bg-success/15 text-success border border-success/30 shrink-0`},Nr=[`title`],Pr={class:`text-foreground-muted shrink-0`},Fr={key:0,class:`shrink-0 flex items-center gap-2`},Ir=[`disabled`,`onClick`],Lr={class:`space-y-3 text-xs text-foreground-muted`},Rr={class:`bg-surface border border-border rounded p-3 font-mono text-[12px] text-white overflow-x-auto whitespace-pre`},zr={class:`space-y-4`},Br={class:`relative`},Vr={class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-1.5 flex items-center justify-between`},Hr={key:0,class:`text-[10px] normal-case tracking-normal text-success/80`},Ur={class:`grid grid-cols-2 gap-2`},Wr=[`onClick`],Gr={class:`grid grid-cols-2 gap-3`},E=Ue({__name:`Editor`,setup(Fe){let Ue=ne({loader:()=>He(()=>import(`./CodeEditor-CiRqvRcK.js`),__vite__mapDeps([0,1,2,3,4,5,6,7,8])),loadingComponent:{render(){return ae(`div`,{class:`flex-1 min-h-0 w-full bg-background flex items-start`,"aria-busy":`true`,"aria-label":`Loading code editor`},[ae(`div`,{class:`p-4 space-y-2 w-full font-mono text-xs text-foreground-muted/40`},[ae(`div`,{class:`h-3 w-1/3 bg-surface-hover rounded animate-pulse`}),ae(`div`,{class:`h-3 w-2/3 bg-surface-hover rounded animate-pulse`}),ae(`div`,{class:`h-3 w-1/2 bg-surface-hover rounded animate-pulse`})])])}},delay:0}),b=Be(),nt=Re(),x=Pe(),S=r({settings:!1,envVars:!1,deps:!1,secrets:!1,versions:!1,docs:!1,firstDeploy:!1}),it=r(null),C=r({config:!1,bindings:!1}),at=r(null),ot=r(null),st=()=>{C.value.config=!1,C.value.bindings=!1},ct=e=>{let t=!C.value[e];st(),C.value[e]=t},w=e=>{st(),S.value[e]=!0},lt=e=>{st(),nt.push(e)},ut=e=>{if(!C.value.config&&!C.value.bindings)return;let t=at.value?.contains(e.target),n=ot.value?.contains(e.target);!t&&!n&&st()},dt=e=>{e.key===`Escape`&&(C.value.config||C.value.bindings)&&st()},T=r(!0),E=r(`build`),Kr=m(()=>[{id:`build`,label:`Build`,icon:xe,badge:V.value.length||null},{id:`test`,label:`Test`,icon:_e,badge:H.value.length||null}]),qr=e=>e?e===`node`?`Node.js 24`:e===`python`?`Python 3.14`:e:``,Jr=m(()=>U.value.filter(e=>e.key.trim()).length),D=r(``),O=r({name:``,description:``,runtime:`python`,memory_mb:64,cpus:.5,network_mode:`none`,max_concurrency:0,concurrency_policy:`queue`,auth_mode:`none`,rate_limit_per_min:0}),k=r(``),A=r(`{"name": "World"}`),j=r(`POST`),M=r(`/`),N=r([]),P=r(!1),Yr=[`GET`,`POST`,`PUT`,`PATCH`,`DELETE`],F=r([]),I=r(!1),Xr=m(()=>N.value.filter(e=>e.name&&e.name.trim()).length),L=r(!1),Zr=r(!1),Qr=r(!1),R=r(null),z=r(null),$r=r(0),B=r(``),V=r([]),H=r([]),ei=r(!1),ti=r(!1),ni=r(!1),ri=m(()=>k.value?`${window.location.origin}/fn/${k.value}`:``),ii=async()=>{ri.value&&(await Je(ri.value)?(ei.value=!0,setTimeout(()=>{ei.value=!1},1500)):x.notify({title:`Copy failed`,message:`Could not copy to clipboard. Select the URL manually: `+ri.value}))},U=r([{key:``,value:``}]),W=r(``),G=r(``),ai=r([]),oi=r([]),K=r({name:``,value:``}),si=r([]),ci=r(!1),q=r(``),J=r({path:``,methods:`*`}),Y=r(null),li=m(()=>k.value?si.value.filter(e=>e.function_id===k.value):[]),X=r([]),ui=m(()=>oi.value.length+X.value.length),Z=m(()=>!!b.params.name),di=m(()=>Z.value||Qr.value),Q=m(()=>di.value&&!L.value),fi=[{id:`python`,label:`Python 3.14`},{id:`node`,label:`Node.js 24`}],pi=e=>e===`python`,mi=e=>e===`node`,hi=m(()=>pi(O.value.runtime)?`handler.py`:(O.value.runtime,`handler.js`)),gi=m(()=>pi(O.value.runtime)?`def handler(event): return { diff --git a/backend/internal/server/ui_dist/assets/Firewall-bYvlPpSq.js b/backend/internal/server/ui_dist/assets/Firewall-Bpe5shll.js similarity index 98% rename from backend/internal/server/ui_dist/assets/Firewall-bYvlPpSq.js rename to backend/internal/server/ui_dist/assets/Firewall-Bpe5shll.js index ddbc62b8..4aa72eb9 100644 --- a/backend/internal/server/ui_dist/assets/Firewall-bYvlPpSq.js +++ b/backend/internal/server/ui_dist/assets/Firewall-Bpe5shll.js @@ -1 +1 @@ -import{D as e,F as t,G as n,I as r,M as i,S as a,T as o,Z as s,_ as c,c as l,d as u,gt as d,h as f,k as p,l as m,m as h,r as g,s as _,u as v,v as y,vt as b}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as ee}from"./globe-C84kKXm0.js";import{t as te}from"./refresh-cw-Bn_tp4gH.js";import{t as ne}from"./shield-check-HnHCfFke.js";import{t as re}from"./trash-2-Bj4AFUfw.js";import{t as x}from"./client-BF51V3uE.js";import{Lt as S,St as C,_t as w,bt as ie,gt as ae,jt as T,zt as E}from"./index-C11HmDXN.js";import{t as oe}from"./Modal-BL-P3nUi.js";import{t as se}from"./Input-DQ-tWGkn.js";var ce=T(`asterisk`,[[`path`,{d:`M12 6v12`,key:`1vza4d`}],[`path`,{d:`M17.196 9 6.804 15`,key:`1ah31z`}],[`path`,{d:`m6.804 9 10.392 6`,key:`1b6pxd`}]]),D=T(`earth`,[[`path`,{d:`M21.54 15H17a2 2 0 0 0-2 2v4.54`,key:`1djwo0`}],[`path`,{d:`M7 3.34V5a3 3 0 0 0 3 3a2 2 0 0 1 2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2c0-1.1.9-2 2-2h3.17`,key:`1tzkfa`}],[`path`,{d:`M11 21.95V18a2 2 0 0 0-2-2a2 2 0 0 1-2-2v-1a2 2 0 0 0-2-2H2.05`,key:`14pb5j`}],[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}]]),le=T(`hash`,[[`line`,{x1:`4`,x2:`20`,y1:`9`,y2:`9`,key:`4lhtct`}],[`line`,{x1:`4`,x2:`20`,y1:`15`,y2:`15`,key:`vyu0kd`}],[`line`,{x1:`10`,x2:`8`,y1:`3`,y2:`21`,key:`1ggp8o`}],[`line`,{x1:`16`,x2:`14`,y1:`3`,y2:`21`,key:`weycgp`}]]),ue=T(`shield-alert`,[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}],[`path`,{d:`M12 8v4`,key:`1got3b`}],[`path`,{d:`M12 16h.01`,key:`1drbdi`}]]),de=T(`shield-off`,[[`path`,{d:`m2 2 20 20`,key:`1ooewy`}],[`path`,{d:`M5 5a1 1 0 0 0-1 1v7c0 5 3.5 7.5 7.67 8.94a1 1 0 0 0 .67.01c2.35-.82 4.48-1.97 5.9-3.71`,key:`1jlk70`}],[`path`,{d:`M9.309 3.652A12.252 12.252 0 0 0 11.24 2.28a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1v7a9.784 9.784 0 0 1-.08 1.264`,key:`18rp1v`}]]),fe={class:`space-y-8`},pe={class:`space-y-3`},me={class:`flex items-start justify-between gap-4 flex-wrap`},he={class:`flex items-center gap-2`},ge={class:`flex-1 min-w-0 space-y-1`},_e={class:`leading-snug`},ve={key:0,class:`font-mono text-xs leading-snug break-words opacity-90`},ye={key:1,class:`leading-snug opacity-80`},be={key:0,class:`text-foreground-muted hidden sm:inline shrink-0`},xe={class:`group`},Se={class:`policy-meta mt-2`},Ce={class:`policy-chip`},we={class:`policy-chip-v`},Te={class:`policy-chip`},Ee={class:`policy-chip-v`},De={class:`policy-chip`},Oe={class:`policy-chip-v`},ke={key:0,class:`policy-chip policy-chip-pending`},Ae={key:1,class:`policy-chip`},je={class:`policy-chip-v`},Me={key:2,class:`policy-chip`},Ne={class:`policy-chip-v`},Pe={class:`dns-card`},Fe={class:`dns-row`},Ie={class:`dns-current`},Le={key:0,class:`dns-chips`},Re={class:`font-mono`},ze=[`aria-label`,`onClick`],Be={key:1,class:`dns-defaults`},Ve={class:`font-mono`},He={class:`dns-form`},Ue={class:`dns-row`},We={class:`dns-row-label`},Ge={class:`dns-row-meta`},Ke={key:0,class:`dns-records`},qe={class:`font-mono text-white text-xs flex-1 truncate`},Je={class:`font-mono text-foreground text-xs flex-1 truncate`},Ye=[`aria-label`,`onClick`],Xe={key:1,class:`text-xs text-foreground-muted italic px-1`},Ze={class:`dns-form`},Qe={class:`dns-savebar`},$e={key:0,class:`unenforced-note`},et={class:`min-w-0`},tt={class:`rule-filterbar`},nt=[`onClick`],rt={class:`rule-filter-count`},it={key:1,class:`empty-card`},at={class:`text-sm text-white`},ot={class:`text-xs text-foreground-muted mt-1 max-w-sm`},st={key:2,class:`rule-grid`},ct={class:`space-y-4`},lt={class:`grid grid-cols-2 gap-2`},ut=[`onClick`],dt={class:`text-xs text-foreground-muted mt-1.5 leading-snug`},ft={key:0,class:`text-xs text-danger-fg leading-snug`},O=`Wildcard patterns are not enforceable: the egress policy matches IPs and CIDRs, not DNS names. Block a CIDR or an exact hostname instead.`,k={__name:`Firewall`,setup(T){let k=ae(),A=n([]),pt=()=>({ipv4:[],ipv6:[],hostname_map:{},last_error:``,backend:`nstun`,enforced:!1,policy_generation:``,policy_rule_counts:{v4:0,v6:0,allow:0,reject:0},policy_stale:!1,pending_recycle:!1,last_compile_error:``,last_success_at:``,control_plane_allow:{addrs:[],port:0},unenforced_rules:[]}),j=n(pt()),M=n(null),N=n(!1),P=n(!1),F=n(!1),I=n({rule_type:`cidr`,value:``,label:``}),L=n({servers:[],search:``,records:[],defaults:[]}),R=n({servers:[],search:``,records:[]}),z=n(``),B=n(``),V=n(``),H=n(!1),mt=_(()=>JSON.stringify({s:L.value.servers||[],q:L.value.search||``,r:(L.value.records||[]).map(e=>`${e.host}=${e.ip}`).sort()})!==JSON.stringify({s:R.value.servers||[],q:R.value.search||``,r:(R.value.records||[]).map(e=>`${e.host}=${e.ip}`).sort()})),ht=_(()=>{let e=[];return e.push(L.value.servers.length?`${L.value.servers.length} resolver${L.value.servers.length===1?``:`s`}`:`defaults (${(L.value.defaults||[]).join(`, `)||`none`})`),L.value.records.length&&e.push(`${L.value.records.length} override${L.value.records.length===1?``:`s`}`),e.join(` · `)}),gt=async()=>{try{let e=await x.get(`/firewall/dns`);L.value={servers:e.data.servers||[],search:e.data.search||``,records:e.data.records||[],defaults:e.data.defaults||[]},R.value={servers:[...L.value.servers],search:L.value.search,records:L.value.records.map(e=>({...e}))}}catch(e){console.error(`loadDNS failed`,e)}},U=()=>{let e=z.value.trim();if(e){if(!(/^[0-9.]+$/.test(e)||e.includes(`:`))){k.notify({title:`Invalid IP`,message:`"${e}" doesn't look like an IPv4 or IPv6 address.`});return}if(L.value.servers.includes(e)){z.value=``;return}L.value.servers=[...L.value.servers,e],z.value=``}},_t=e=>{L.value.servers=L.value.servers.filter((t,n)=>n!==e)},W=()=>{let e=B.value.trim(),t=V.value.trim();if(!e||!t)return;let n=/^[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)*$/.test(e),r=/^[0-9.]+$/.test(t)||t.includes(`:`);if(!n){k.notify({title:`Invalid hostname`,message:`"${e}" is not a valid hostname.`});return}if(!r){k.notify({title:`Invalid IP`,message:`"${t}" is not a literal IPv4 or IPv6 address.`});return}if((L.value.records||[]).some(t=>t.host===e)){k.notify({title:`Duplicate host`,message:`"${e}" already has an override.`});return}L.value.records=[...L.value.records||[],{host:e,ip:t}],B.value=``,V.value=``},vt=e=>{L.value.records=L.value.records.filter((t,n)=>n!==e)},yt=()=>{L.value.servers=[],L.value.search=``,L.value.records=[]},bt=async()=>{H.value=!0;try{let e=await x.put(`/firewall/dns`,{servers:L.value.servers,search:L.value.search||``,records:L.value.records||[]});L.value={servers:e.data.servers||[],search:e.data.search||``,records:e.data.records||[],defaults:e.data.defaults||L.value.defaults},R.value={servers:[...L.value.servers],search:L.value.search,records:L.value.records.map(e=>({...e}))}}catch(e){k.notify({title:`Save failed`,message:e.response?.data?.error?.message||e.message,danger:!0})}finally{H.value=!1}},xt=_(()=>A.value.filter(e=>e.kind===`custom`)),St=_(()=>{let e=new Map;for(let t of j.value.unenforced_rules||[])e.set(t.id,t.reason);return e}),Ct=e=>St.value.get(e.id)||(e.rule_type===`wildcard`?O:``),G=e=>Ct(e)!==``,K=_(()=>{let e=new Map;for(let t of j.value.unenforced_rules||[])e.set(t.id,{id:t.id,value:t.value,reason:t.reason});for(let t of A.value)t.rule_type===`wildcard`&&!e.has(t.id)&&e.set(t.id,{id:t.id,value:t.value,reason:O});return[...e.values()]}),wt={"169.254.0.0/16":{name:`Cloud metadata service`,why:`Blocks the special address AWS, Azure, and GCP use to expose VM credentials and instance settings. Leaving this open is a common credential-leak path.`},"fd00:ec2::254/128":{name:`Cloud metadata (IPv6)`,why:`Same as above, but the IPv6 path GCP uses. Recommended on.`},"10.0.0.0/8":{name:`Private network, 10.x`,why:`Standard internal-network range. Turn on if your functions should not reach internal services on your LAN.`},"172.16.0.0/12":{name:`Private network, 172.16.x`,why:`Another internal-network range (often used by Docker default bridge). Turn on for stricter isolation.`},"192.168.0.0/16":{name:`Private network, 192.168.x`,why:`Common home/office network range. Turn on if functions should not reach your local LAN.`},"100.64.0.0/10":{name:`CGNAT / Tailscale`,why:`Used by Tailscale and large ISPs. Turn on to keep functions out of your tailnet.`}},q=e=>e.kind===`custom`?e.label||e.value:wt[e.value]?.name||e.label||e.value,Tt=e=>e.kind===`custom`?``:wt[e.value]?.why||``,J=n(`enforced`),Et=_(()=>{let e=[{id:`all`,label:`All`,count:A.value.length},{id:`enforced`,label:`Enforced`,count:Y.value},{id:`off`,label:`Off`,count:X.value}];return Z.value.length&&e.push({id:`unenforced`,label:`Not enforced`,count:Z.value.length}),e.push({id:`yours`,label:`Yours`,count:xt.value.length}),e}),Dt=_(()=>{let e={default:0,suggested:1,custom:2};return[...A.value.filter(e=>J.value===`enforced`?e.enabled&&!G(e):J.value===`off`?!e.enabled:J.value===`unenforced`?G(e):J.value!==`yours`||e.kind===`custom`)].sort((t,n)=>{let r=G(t);return r===G(n)?t.enabled===n.enabled?e[t.kind]===e[n.kind]?q(t).localeCompare(q(n)):e[t.kind]-e[n.kind]:t.enabled?-1:1:r?1:-1})}),Y=_(()=>A.value.filter(e=>e.enabled&&!G(e)).length),X=_(()=>A.value.filter(e=>!e.enabled).length),Z=_(()=>A.value.filter(e=>G(e))),Ot=_(()=>{if(!A.value.length)return`Nothing in the blocklist yet.`;let e=Y.value,t=X.value,n=[j.value.enforced?`${e} block${e===1?``:`s`} enforced`:`${e} block${e===1?``:`s`} staged, none enforced`,`${t} available to turn on`,`${xt.value.length} you added`];return Z.value.length&&n.push(`${Z.value.length} not enforceable`),n.join(` · `)}),kt=[{value:`cidr`,label:`IP / Range`,icon:le},{value:`hostname`,label:`Hostname`,icon:ee}],At=_(()=>I.value.rule_type===`hostname`?`api.internal.corp`:`192.168.1.0/24`),jt=_(()=>I.value.rule_type===`hostname`?`A specific website or service name. We resolve it to IPs and block those, re-resolving on every refresh.`:`A single IP (e.g. 1.2.3.4) or a CIDR range (e.g. 10.0.0.0/8) to block all addresses inside it.`),Mt=_(()=>I.value.value.includes(`*`)),Q=_(()=>j.value.enforced&&j.value.policy_stale?`stale`:j.value.enforced?j.value.last_error?`degraded`:`ok`:`unenforced`),Nt=_(()=>{switch(Q.value){case`unenforced`:return`border-danger-ring bg-danger-tint text-danger-fg`;case`stale`:case`degraded`:return`border-warning-ring bg-warning-tint text-warning-fg`;default:return`border-success-ring bg-success-tint text-foreground-muted`}}),Pt=_(()=>Q.value===`ok`?ne:ie),Ft=_(()=>{switch(Q.value){case`unenforced`:return`No egress policy is compiled, so nothing below is in force. Orva fails closed: functions with outbound network enabled cannot start until a policy compiles.`;case`stale`:return`Enforcing the last known-good policy (generation ${j.value.policy_generation}). The newest recompile failed, so recent changes are not live yet.`;case`degraded`:return`The policy is in force, but the last refresh reported a problem.`;default:return`Enforced per sandbox by nsjail NSTUN, generation ${j.value.policy_generation}. Applies to every function with outbound network enabled.`}}),It=_(()=>{switch(Q.value){case`unenforced`:return`Hit Apply now to recompile. There is nothing to install: if invocations fail with a sandbox error instead, nsjail needs /dev/net/tun (Docker: --device /dev/net/tun).`;case`stale`:case`degraded`:return`Fix the reported rule or resolver, then hit Apply now to recompile.`;default:return``}}),Lt=_(()=>{let e=j.value.policy_rule_counts||{};return`${e.v4??0} v4 · ${e.v6??0} v6 · ${e.reject??0} reject · ${e.allow??0} allow`}),Rt=_(()=>{let e=j.value.control_plane_allow||{},t=e.addrs||[];return t.length?`${t.join(`, `)}${e.port?`:${e.port}`:``}`:``}),zt=_(()=>{let e=j.value.last_success_at;if(!e)return``;let t=new Date(e);return Number.isNaN(t.getTime())?e:t.toLocaleTimeString()}),$=async()=>{let e=await x.get(`/firewall/rules`);A.value=e.data.rules||[],j.value={...pt(),...e.data.status||{}}},Bt=async e=>{M.value=e.id;try{await x.put(`/firewall/rules/${e.id}`,{enabled:!e.enabled}),await $()}catch(e){k.notify({title:`Toggle failed`,message:e.response?.data?.error?.message||e.message,danger:!0})}finally{M.value=null}},Vt=async e=>{if(await k.ask({title:`Delete custom rule?`,message:`"${e.value}" will be removed from the blocklist.`,confirmLabel:`Delete`,danger:!0}))try{await x.delete(`/firewall/rules/${e.id}`),await $()}catch(e){k.notify({title:`Delete failed`,message:e.response?.data?.error?.message||e.message,danger:!0})}},Ht=async()=>{if(I.value.value.trim()){P.value=!0;try{await x.post(`/firewall/rules`,{rule_type:I.value.rule_type,value:I.value.value.trim(),label:I.value.label.trim()}),N.value=!1,I.value={rule_type:`cidr`,value:``,label:``},await $()}catch(e){k.notify({title:`Failed to add rule`,message:e.response?.data?.error?.message||e.message,danger:!0})}finally{P.value=!1}}},Ut=async()=>{F.value=!0;try{let e=await x.post(`/firewall/resolve`);e.data.error&&k.notify({title:`Resolve had errors`,message:e.data.error,danger:!0}),await $()}catch(e){k.notify({title:`Resolve failed`,message:e.response?.data?.error?.message||e.message,danger:!0})}finally{F.value=!1}},Wt=async()=>{await Promise.all([$(),gt()])};o(Wt),a(Wt);let Gt=c({name:`PanelSection`,props:{title:{type:String,default:``},subtitle:{type:String,default:``}},setup(e,{slots:t}){return()=>y(`section`,{class:`space-y-3`},[y(`div`,null,[y(`h2`,{class:`text-sm font-semibold text-white tracking-tight`},e.title),e.subtitle?y(`p`,{class:`text-xs text-foreground-muted mt-0.5`},e.subtitle):null]),y(`div`,null,t.default?.())])}}),Kt={default:{label:`Recommended`,cls:`kind-recommended`},suggested:{label:`Optional`,cls:`kind-optional`},custom:{label:`Yours`,cls:`kind-yours`}},qt=c({name:`RuleCard`,props:{rule:{type:Object,required:!0},status:{type:Object,required:!0},unenforcedReason:{type:String,default:``},busy:{type:Boolean,default:!1},readonlyEdit:{type:Boolean,default:!1}},emits:[`toggle`,`delete`],setup(e,{emit:t}){let n=_(()=>{switch(e.rule.rule_type){case`hostname`:return ee;case`wildcard`:return ce;default:return le}}),r=_(()=>e.rule.rule_type===`cidr`?[e.rule.value]:e.status.hostname_map?.[e.rule.value]||[]),i=_(()=>q(e.rule)),a=_(()=>Tt(e.rule)),o=_(()=>Kt[e.rule.kind]||Kt.custom),s=_(()=>e.unenforcedReason?`unenforced`:e.rule.enabled?e.status.enforced?`on`:`pending`:`off`),c=_(()=>s.value===`unenforced`&&!e.rule.enabled),l=_(()=>c.value?`Cannot be enforced. See the reason on this card.`:e.rule.enabled?`Click to allow`:`Click to block`);return()=>y(`div`,{class:[`rule-card`,`is-${s.value}`]},[y(`div`,{class:`rule-card-row`},[y(`div`,{class:`rule-card-titlewrap`},[y(`div`,{class:`rule-card-title`},i.value),y(`div`,{class:`rule-card-pills`},[y(`span`,{class:[`rule-kind-pill`,o.value.cls]},o.value.label),s.value===`unenforced`?y(`span`,{class:`rule-kind-pill kind-inert`},`Not enforced`):null,s.value===`pending`?y(`span`,{class:`rule-kind-pill kind-inert`},`Not in force`):null])]),y(`button`,{class:[`rule-toggle`,s.value===`on`?`on`:s.value===`off`?`off`:[`inert`,e.rule.enabled?`is-set`:``],e.busy?`busy`:``,`focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background`],disabled:e.busy||c.value,title:l.value,onClick:()=>t(`toggle`)},[y(`span`,{class:`rule-toggle-knob`})])]),e.unenforcedReason?y(`p`,{class:`rule-card-warn`},e.unenforcedReason):null,a.value?y(`p`,{class:`rule-card-why`},a.value):null,y(`div`,{class:`rule-card-foot`},[y(n.value,{class:`rule-card-type-icon`}),y(`code`,{class:`rule-card-value`},e.rule.value),r.value.length&&e.rule.rule_type!==`cidr`?y(`span`,{class:`rule-card-resolved`},`→ ${r.value.slice(0,2).join(`, `)}${r.value.length>2?` +${r.value.length-2}`:``}`):null]),e.readonlyEdit?null:y(`button`,{class:`rule-card-delete`,title:`Remove this block`,onClick:()=>t(`delete`)},[y(re,{class:`w-3.5 h-3.5`})])])}});return(n,a)=>(e(),u(`div`,fe,[l(`header`,pe,[l(`div`,me,[a[12]||=l(`div`,{class:`max-w-2xl`},[l(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` Egress `),l(`p`,{class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},` Control outbound destinations and DNS for function sandboxes. `)],-1),l(`div`,he,[f(w,{variant:`secondary`,size:`sm`,loading:F.value,onClick:Ut},{default:t(()=>[f(s(te),{class:`w-4 h-4`}),a[10]||=h(` Apply now `,-1)]),_:1},8,[`loading`]),f(w,{size:`sm`,onClick:a[0]||=e=>N.value=!0},{default:t(()=>[f(s(C),{class:`w-4 h-4`}),a[11]||=h(` Add block `,-1)]),_:1})])]),l(`div`,{class:d([`flex items-start gap-3 text-xs px-3 py-2 rounded-md border`,Nt.value])},[(e(),m(i(Pt.value),{class:`w-4 h-4 shrink-0 mt-0.5`})),l(`div`,ge,[l(`p`,_e,b(Ft.value),1),j.value.last_error?(e(),u(`p`,ve,b(j.value.last_error),1)):v(``,!0),It.value?(e(),u(`p`,ye,b(It.value),1)):v(``,!0)]),j.value.enforced?(e(),u(`span`,be,b(Y.value)+` enforced · `+b(X.value)+` off `,1)):v(``,!0)],2),l(`details`,xe,[a[19]||=l(`summary`,{class:`cursor-pointer text-xs text-foreground-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary`},` Policy details `,-1),l(`div`,Se,[l(`span`,Ce,[a[13]||=l(`span`,{class:`policy-chip-k`},`Backend`,-1),l(`span`,we,b(j.value.backend||`nstun`),1)]),l(`span`,Te,[a[14]||=l(`span`,{class:`policy-chip-k`},`Generation`,-1),l(`span`,Ee,b(j.value.policy_generation||`none`),1)]),l(`span`,De,[a[15]||=l(`span`,{class:`policy-chip-k`},`Compiled rules`,-1),l(`span`,Oe,b(Lt.value),1)]),j.value.pending_recycle?(e(),u(`span`,ke,[...a[16]||=[l(`span`,{class:`policy-chip-k`},`Rollout`,-1),l(`span`,{class:`policy-chip-v`},`warm workers still on the previous generation`,-1)]])):v(``,!0),Rt.value?(e(),u(`span`,Ae,[a[17]||=l(`span`,{class:`policy-chip-k`},`SDK carve-out`,-1),l(`span`,je,b(Rt.value),1)])):v(``,!0),zt.value?(e(),u(`span`,Me,[a[18]||=l(`span`,{class:`policy-chip-k`},`Applied`,-1),l(`span`,Ne,b(zt.value),1)])):v(``,!0)])])]),f(s(Gt),{title:`DNS`,subtitle:ht.value},{default:t(()=>[l(`div`,Pe,[l(`div`,Fe,[a[22]||=l(`div`,{class:`dns-row-label`},` Upstream resolvers `,-1),l(`div`,Ie,[L.value.servers.length?(e(),u(`div`,Le,[(e(!0),u(g,null,p(L.value.servers,(t,n)=>(e(),u(`span`,{key:t+n,class:`dns-chip`},[f(s(D),{class:`w-3 h-3 opacity-60`}),l(`span`,Re,b(t),1),l(`button`,{class:`dns-chip-x`,title:`Remove`,"aria-label":`Remove resolver ${t}`,onClick:e=>_t(n)},` × `,8,ze)]))),128))])):(e(),u(`div`,Be,[a[20]||=l(`span`,{class:`text-foreground-muted text-xs`},`Defaults:`,-1),(e(!0),u(g,null,p(L.value.defaults,t=>(e(),u(`span`,{key:t,class:`dns-chip muted`},[f(s(D),{class:`w-3 h-3 opacity-60`}),l(`span`,Ve,b(t),1)]))),128))]))]),l(`div`,He,[r(l(`input`,{"onUpdate:modelValue":a[1]||=e=>z.value=e,"aria-label":`Resolver address`,placeholder:`1.1.1.1`,class:`dns-input`,onKeydown:E(U,[`enter`])},null,544),[[S,z.value]]),f(w,{variant:`secondary`,size:`sm`,disabled:!z.value.trim(),onClick:U},{default:t(()=>[f(s(C),{class:`w-3.5 h-3.5`}),a[21]||=h(` Add resolver `,-1)]),_:1},8,[`disabled`]),r(l(`input`,{"onUpdate:modelValue":a[2]||=e=>L.value.search=e,"aria-label":`DNS search domain`,placeholder:`search domain`,class:`dns-input narrow`},null,512),[[S,L.value.search]])])]),l(`div`,Ue,[l(`div`,We,[a[23]||=h(` Host overrides `,-1),l(`span`,Ge,b(L.value.records.length)+` record`+b(L.value.records.length===1?``:`s`),1)]),L.value.records.length?(e(),u(`div`,Ke,[(e(!0),u(g,null,p(L.value.records,(t,n)=>(e(),u(`div`,{key:t.host+n,class:`dns-record`},[l(`span`,qe,b(t.host),1),a[24]||=l(`span`,{class:`text-foreground-muted text-xs`},`→`,-1),l(`span`,Je,b(t.ip),1),l(`button`,{class:`dns-chip-x`,title:`Remove`,"aria-label":`Remove host override ${t.host}`,onClick:e=>vt(n)},` × `,8,Ye)]))),128))])):(e(),u(`div`,Xe,` No overrides. Anything resolves through the upstream resolvers above. `)),l(`div`,Ze,[r(l(`input`,{"onUpdate:modelValue":a[3]||=e=>B.value=e,"aria-label":`Override hostname`,placeholder:`api.internal`,class:`dns-input host`,onKeydown:E(W,[`enter`])},null,544),[[S,B.value]]),a[26]||=l(`span`,{class:`text-foreground-muted text-xs`},`→`,-1),r(l(`input`,{"onUpdate:modelValue":a[4]||=e=>V.value=e,"aria-label":`Override IP address`,placeholder:`10.0.5.10`,class:`dns-input`,onKeydown:E(W,[`enter`])},null,544),[[S,V.value]]),f(w,{variant:`secondary`,size:`sm`,disabled:!(B.value.trim()&&V.value.trim()),onClick:W},{default:t(()=>[f(s(C),{class:`w-3.5 h-3.5`}),a[25]||=h(` Add record `,-1)]),_:1},8,[`disabled`])])]),l(`div`,Qe,[a[28]||=l(`span`,{class:`dns-hint`},` Overrides bypass upstream DNS. Existing warm workers update after recycle. `,-1),L.value.servers.length||L.value.search||L.value.records.length?(e(),u(`button`,{key:0,class:`text-xs text-foreground-muted hover:text-white px-2 py-1 transition-colors`,onClick:yt},` Reset `)):v(``,!0),f(w,{size:`sm`,loading:H.value,disabled:!mt.value,onClick:bt},{default:t(()=>[...a[27]||=[h(` Save `,-1)]]),_:1},8,[`loading`,`disabled`])])])]),_:1},8,[`subtitle`]),f(s(Gt),{title:`Blocklist`,subtitle:Ot.value},{default:t(()=>[K.value.length?(e(),u(`div`,$e,[f(s(ie),{class:`w-3.5 h-3.5 shrink-0 mt-0.5`}),l(`div`,et,[l(`p`,null,b(K.value.length===1?`One stored rule is not part of the compiled policy and blocks nothing:`:`${K.value.length} stored rules are not part of the compiled policy and block nothing:`),1),l(`ul`,null,[(e(!0),u(g,null,p(K.value,t=>(e(),u(`li`,{key:t.id},[l(`code`,null,b(t.value),1),h(`: `+b(t.reason),1)]))),128))])])])):v(``,!0),l(`div`,tt,[(e(!0),u(g,null,p(Et.value,t=>(e(),u(`button`,{key:t.id,class:d([`rule-filter`,{active:J.value===t.id}]),onClick:e=>J.value=t.id},[h(b(t.label)+` `,1),l(`span`,rt,b(t.count),1)],10,nt))),128))]),Dt.value.length?(e(),u(`div`,st,[(e(!0),u(g,null,p(Dt.value,t=>(e(),m(s(qt),{key:t.id,rule:t,status:j.value,"unenforced-reason":Ct(t),busy:M.value===t.id,"readonly-edit":t.kind!==`custom`,onToggle:e=>Bt(t),onDelete:e=>Vt(t)},null,8,[`rule`,`status`,`unenforced-reason`,`busy`,`readonly-edit`,`onToggle`,`onDelete`]))),128))])):(e(),u(`div`,it,[f(s(de),{class:`w-5 h-5 mb-2 text-foreground-muted/60`}),l(`p`,at,b(J.value===`yours`?`No custom blocks yet`:`Nothing matches this filter`),1),l(`p`,ot,b(J.value===`yours`?`Add an IP, network, or hostname.`:`Try another filter.`),1),J.value===`yours`?(e(),m(w,{key:0,class:`mt-4`,size:`sm`,variant:`secondary`,onClick:a[5]||=e=>N.value=!0},{default:t(()=>[f(s(C),{class:`w-3.5 h-3.5`}),a[29]||=h(` Add block `,-1)]),_:1})):v(``,!0)]))]),_:1},8,[`subtitle`]),f(oe,{modelValue:N.value,"onUpdate:modelValue":a[9]||=e=>N.value=e,title:`Add block`,icon:s(ue),size:`md`},{footer:t(()=>[f(w,{variant:`secondary`,onClick:a[8]||=e=>N.value=!1},{default:t(()=>[...a[33]||=[h(` Cancel `,-1)]]),_:1}),f(w,{loading:P.value,disabled:!I.value.value.trim()||Mt.value,onClick:Ht},{default:t(()=>[f(s(C),{class:`w-4 h-4`}),a[34]||=h(` Block it `,-1)]),_:1},8,[`loading`,`disabled`])]),default:t(()=>[l(`div`,ct,[a[31]||=l(`p`,{class:`text-xs text-foreground-muted leading-snug`},` Block an IP, network, or hostname. Wildcards are not supported. `,-1),l(`div`,null,[a[30]||=l(`label`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-2`},` What is it? `,-1),l(`div`,lt,[(e(),u(g,null,p(kt,t=>l(`button`,{key:t.value,class:d([`px-2 py-2 rounded border text-xs font-medium transition-colors flex flex-col items-center gap-1`,I.value.rule_type===t.value?`bg-white text-black border-white`:`bg-surface-hover text-foreground-muted border-border hover:border-foreground-muted`]),onClick:e=>I.value.rule_type=t.value},[(e(),m(i(t.icon),{class:`w-3.5 h-3.5`})),h(` `+b(t.label),1)],10,ut)),64))]),l(`p`,dt,b(jt.value),1)]),f(se,{modelValue:I.value.value,"onUpdate:modelValue":a[6]||=e=>I.value.value=e,label:I.value.rule_type===`hostname`?`Hostname`:`IP or network`,placeholder:At.value},null,8,[`modelValue`,`label`,`placeholder`]),Mt.value?(e(),u(`p`,ft,b(O))):v(``,!0),f(se,{modelValue:I.value.label,"onUpdate:modelValue":a[7]||=e=>I.value.label=e,label:`Why? (optional)`,placeholder:`e.g. our staging Postgres`},null,8,[`modelValue`]),a[32]||=l(`p`,{class:`text-xs text-foreground-muted leading-snug`},` Applies within seconds and recycles warm functions. `,-1)])]),_:1},8,[`modelValue`,`icon`])]))}};export{k as default}; \ No newline at end of file +import{D as e,F as t,G as n,I as r,M as i,S as a,T as o,Z as s,_ as c,c as l,d as u,gt as d,h as f,k as p,l as m,m as h,r as g,s as _,u as v,v as y,vt as b}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as ee}from"./globe-BnBar2lO.js";import{t as te}from"./refresh-cw-Cn8qkf-v.js";import{t as ne}from"./shield-check-piXkOtNv.js";import{t as re}from"./trash-2-DaeYqnW_.js";import{t as x}from"./client-BF51V3uE.js";import{Lt as S,St as C,_t as w,bt as ie,gt as ae,jt as T,zt as E}from"./index-DTqMKlE1.js";import{t as oe}from"./Modal-BAoZams6.js";import{t as se}from"./Input-DQ-tWGkn.js";var ce=T(`asterisk`,[[`path`,{d:`M12 6v12`,key:`1vza4d`}],[`path`,{d:`M17.196 9 6.804 15`,key:`1ah31z`}],[`path`,{d:`m6.804 9 10.392 6`,key:`1b6pxd`}]]),D=T(`earth`,[[`path`,{d:`M21.54 15H17a2 2 0 0 0-2 2v4.54`,key:`1djwo0`}],[`path`,{d:`M7 3.34V5a3 3 0 0 0 3 3a2 2 0 0 1 2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2c0-1.1.9-2 2-2h3.17`,key:`1tzkfa`}],[`path`,{d:`M11 21.95V18a2 2 0 0 0-2-2a2 2 0 0 1-2-2v-1a2 2 0 0 0-2-2H2.05`,key:`14pb5j`}],[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}]]),le=T(`hash`,[[`line`,{x1:`4`,x2:`20`,y1:`9`,y2:`9`,key:`4lhtct`}],[`line`,{x1:`4`,x2:`20`,y1:`15`,y2:`15`,key:`vyu0kd`}],[`line`,{x1:`10`,x2:`8`,y1:`3`,y2:`21`,key:`1ggp8o`}],[`line`,{x1:`16`,x2:`14`,y1:`3`,y2:`21`,key:`weycgp`}]]),ue=T(`shield-alert`,[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}],[`path`,{d:`M12 8v4`,key:`1got3b`}],[`path`,{d:`M12 16h.01`,key:`1drbdi`}]]),de=T(`shield-off`,[[`path`,{d:`m2 2 20 20`,key:`1ooewy`}],[`path`,{d:`M5 5a1 1 0 0 0-1 1v7c0 5 3.5 7.5 7.67 8.94a1 1 0 0 0 .67.01c2.35-.82 4.48-1.97 5.9-3.71`,key:`1jlk70`}],[`path`,{d:`M9.309 3.652A12.252 12.252 0 0 0 11.24 2.28a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1v7a9.784 9.784 0 0 1-.08 1.264`,key:`18rp1v`}]]),fe={class:`space-y-8`},pe={class:`space-y-3`},me={class:`flex items-start justify-between gap-4 flex-wrap`},he={class:`flex items-center gap-2`},ge={class:`flex-1 min-w-0 space-y-1`},_e={class:`leading-snug`},ve={key:0,class:`font-mono text-xs leading-snug break-words opacity-90`},ye={key:1,class:`leading-snug opacity-80`},be={key:0,class:`text-foreground-muted hidden sm:inline shrink-0`},xe={class:`group`},Se={class:`policy-meta mt-2`},Ce={class:`policy-chip`},we={class:`policy-chip-v`},Te={class:`policy-chip`},Ee={class:`policy-chip-v`},De={class:`policy-chip`},Oe={class:`policy-chip-v`},ke={key:0,class:`policy-chip policy-chip-pending`},Ae={key:1,class:`policy-chip`},je={class:`policy-chip-v`},Me={key:2,class:`policy-chip`},Ne={class:`policy-chip-v`},Pe={class:`dns-card`},Fe={class:`dns-row`},Ie={class:`dns-current`},Le={key:0,class:`dns-chips`},Re={class:`font-mono`},ze=[`aria-label`,`onClick`],Be={key:1,class:`dns-defaults`},Ve={class:`font-mono`},He={class:`dns-form`},Ue={class:`dns-row`},We={class:`dns-row-label`},Ge={class:`dns-row-meta`},Ke={key:0,class:`dns-records`},qe={class:`font-mono text-white text-xs flex-1 truncate`},Je={class:`font-mono text-foreground text-xs flex-1 truncate`},Ye=[`aria-label`,`onClick`],Xe={key:1,class:`text-xs text-foreground-muted italic px-1`},Ze={class:`dns-form`},Qe={class:`dns-savebar`},$e={key:0,class:`unenforced-note`},et={class:`min-w-0`},tt={class:`rule-filterbar`},nt=[`onClick`],rt={class:`rule-filter-count`},it={key:1,class:`empty-card`},at={class:`text-sm text-white`},ot={class:`text-xs text-foreground-muted mt-1 max-w-sm`},st={key:2,class:`rule-grid`},ct={class:`space-y-4`},lt={class:`grid grid-cols-2 gap-2`},ut=[`onClick`],dt={class:`text-xs text-foreground-muted mt-1.5 leading-snug`},ft={key:0,class:`text-xs text-danger-fg leading-snug`},O=`Wildcard patterns are not enforceable: the egress policy matches IPs and CIDRs, not DNS names. Block a CIDR or an exact hostname instead.`,k={__name:`Firewall`,setup(T){let k=ae(),A=n([]),pt=()=>({ipv4:[],ipv6:[],hostname_map:{},last_error:``,backend:`nstun`,enforced:!1,policy_generation:``,policy_rule_counts:{v4:0,v6:0,allow:0,reject:0},policy_stale:!1,pending_recycle:!1,last_compile_error:``,last_success_at:``,control_plane_allow:{addrs:[],port:0},unenforced_rules:[]}),j=n(pt()),M=n(null),N=n(!1),P=n(!1),F=n(!1),I=n({rule_type:`cidr`,value:``,label:``}),L=n({servers:[],search:``,records:[],defaults:[]}),R=n({servers:[],search:``,records:[]}),z=n(``),B=n(``),V=n(``),H=n(!1),mt=_(()=>JSON.stringify({s:L.value.servers||[],q:L.value.search||``,r:(L.value.records||[]).map(e=>`${e.host}=${e.ip}`).sort()})!==JSON.stringify({s:R.value.servers||[],q:R.value.search||``,r:(R.value.records||[]).map(e=>`${e.host}=${e.ip}`).sort()})),ht=_(()=>{let e=[];return e.push(L.value.servers.length?`${L.value.servers.length} resolver${L.value.servers.length===1?``:`s`}`:`defaults (${(L.value.defaults||[]).join(`, `)||`none`})`),L.value.records.length&&e.push(`${L.value.records.length} override${L.value.records.length===1?``:`s`}`),e.join(` · `)}),gt=async()=>{try{let e=await x.get(`/firewall/dns`);L.value={servers:e.data.servers||[],search:e.data.search||``,records:e.data.records||[],defaults:e.data.defaults||[]},R.value={servers:[...L.value.servers],search:L.value.search,records:L.value.records.map(e=>({...e}))}}catch(e){console.error(`loadDNS failed`,e)}},U=()=>{let e=z.value.trim();if(e){if(!(/^[0-9.]+$/.test(e)||e.includes(`:`))){k.notify({title:`Invalid IP`,message:`"${e}" doesn't look like an IPv4 or IPv6 address.`});return}if(L.value.servers.includes(e)){z.value=``;return}L.value.servers=[...L.value.servers,e],z.value=``}},_t=e=>{L.value.servers=L.value.servers.filter((t,n)=>n!==e)},W=()=>{let e=B.value.trim(),t=V.value.trim();if(!e||!t)return;let n=/^[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]*[A-Za-z0-9])?)*$/.test(e),r=/^[0-9.]+$/.test(t)||t.includes(`:`);if(!n){k.notify({title:`Invalid hostname`,message:`"${e}" is not a valid hostname.`});return}if(!r){k.notify({title:`Invalid IP`,message:`"${t}" is not a literal IPv4 or IPv6 address.`});return}if((L.value.records||[]).some(t=>t.host===e)){k.notify({title:`Duplicate host`,message:`"${e}" already has an override.`});return}L.value.records=[...L.value.records||[],{host:e,ip:t}],B.value=``,V.value=``},vt=e=>{L.value.records=L.value.records.filter((t,n)=>n!==e)},yt=()=>{L.value.servers=[],L.value.search=``,L.value.records=[]},bt=async()=>{H.value=!0;try{let e=await x.put(`/firewall/dns`,{servers:L.value.servers,search:L.value.search||``,records:L.value.records||[]});L.value={servers:e.data.servers||[],search:e.data.search||``,records:e.data.records||[],defaults:e.data.defaults||L.value.defaults},R.value={servers:[...L.value.servers],search:L.value.search,records:L.value.records.map(e=>({...e}))}}catch(e){k.notify({title:`Save failed`,message:e.response?.data?.error?.message||e.message,danger:!0})}finally{H.value=!1}},xt=_(()=>A.value.filter(e=>e.kind===`custom`)),St=_(()=>{let e=new Map;for(let t of j.value.unenforced_rules||[])e.set(t.id,t.reason);return e}),Ct=e=>St.value.get(e.id)||(e.rule_type===`wildcard`?O:``),G=e=>Ct(e)!==``,K=_(()=>{let e=new Map;for(let t of j.value.unenforced_rules||[])e.set(t.id,{id:t.id,value:t.value,reason:t.reason});for(let t of A.value)t.rule_type===`wildcard`&&!e.has(t.id)&&e.set(t.id,{id:t.id,value:t.value,reason:O});return[...e.values()]}),wt={"169.254.0.0/16":{name:`Cloud metadata service`,why:`Blocks the special address AWS, Azure, and GCP use to expose VM credentials and instance settings. Leaving this open is a common credential-leak path.`},"fd00:ec2::254/128":{name:`Cloud metadata (IPv6)`,why:`Same as above, but the IPv6 path GCP uses. Recommended on.`},"10.0.0.0/8":{name:`Private network, 10.x`,why:`Standard internal-network range. Turn on if your functions should not reach internal services on your LAN.`},"172.16.0.0/12":{name:`Private network, 172.16.x`,why:`Another internal-network range (often used by Docker default bridge). Turn on for stricter isolation.`},"192.168.0.0/16":{name:`Private network, 192.168.x`,why:`Common home/office network range. Turn on if functions should not reach your local LAN.`},"100.64.0.0/10":{name:`CGNAT / Tailscale`,why:`Used by Tailscale and large ISPs. Turn on to keep functions out of your tailnet.`}},q=e=>e.kind===`custom`?e.label||e.value:wt[e.value]?.name||e.label||e.value,Tt=e=>e.kind===`custom`?``:wt[e.value]?.why||``,J=n(`enforced`),Et=_(()=>{let e=[{id:`all`,label:`All`,count:A.value.length},{id:`enforced`,label:`Enforced`,count:Y.value},{id:`off`,label:`Off`,count:X.value}];return Z.value.length&&e.push({id:`unenforced`,label:`Not enforced`,count:Z.value.length}),e.push({id:`yours`,label:`Yours`,count:xt.value.length}),e}),Dt=_(()=>{let e={default:0,suggested:1,custom:2};return[...A.value.filter(e=>J.value===`enforced`?e.enabled&&!G(e):J.value===`off`?!e.enabled:J.value===`unenforced`?G(e):J.value!==`yours`||e.kind===`custom`)].sort((t,n)=>{let r=G(t);return r===G(n)?t.enabled===n.enabled?e[t.kind]===e[n.kind]?q(t).localeCompare(q(n)):e[t.kind]-e[n.kind]:t.enabled?-1:1:r?1:-1})}),Y=_(()=>A.value.filter(e=>e.enabled&&!G(e)).length),X=_(()=>A.value.filter(e=>!e.enabled).length),Z=_(()=>A.value.filter(e=>G(e))),Ot=_(()=>{if(!A.value.length)return`Nothing in the blocklist yet.`;let e=Y.value,t=X.value,n=[j.value.enforced?`${e} block${e===1?``:`s`} enforced`:`${e} block${e===1?``:`s`} staged, none enforced`,`${t} available to turn on`,`${xt.value.length} you added`];return Z.value.length&&n.push(`${Z.value.length} not enforceable`),n.join(` · `)}),kt=[{value:`cidr`,label:`IP / Range`,icon:le},{value:`hostname`,label:`Hostname`,icon:ee}],At=_(()=>I.value.rule_type===`hostname`?`api.internal.corp`:`192.168.1.0/24`),jt=_(()=>I.value.rule_type===`hostname`?`A specific website or service name. We resolve it to IPs and block those, re-resolving on every refresh.`:`A single IP (e.g. 1.2.3.4) or a CIDR range (e.g. 10.0.0.0/8) to block all addresses inside it.`),Mt=_(()=>I.value.value.includes(`*`)),Q=_(()=>j.value.enforced&&j.value.policy_stale?`stale`:j.value.enforced?j.value.last_error?`degraded`:`ok`:`unenforced`),Nt=_(()=>{switch(Q.value){case`unenforced`:return`border-danger-ring bg-danger-tint text-danger-fg`;case`stale`:case`degraded`:return`border-warning-ring bg-warning-tint text-warning-fg`;default:return`border-success-ring bg-success-tint text-foreground-muted`}}),Pt=_(()=>Q.value===`ok`?ne:ie),Ft=_(()=>{switch(Q.value){case`unenforced`:return`No egress policy is compiled, so nothing below is in force. Orva fails closed: functions with outbound network enabled cannot start until a policy compiles.`;case`stale`:return`Enforcing the last known-good policy (generation ${j.value.policy_generation}). The newest recompile failed, so recent changes are not live yet.`;case`degraded`:return`The policy is in force, but the last refresh reported a problem.`;default:return`Enforced per sandbox by nsjail NSTUN, generation ${j.value.policy_generation}. Applies to every function with outbound network enabled.`}}),It=_(()=>{switch(Q.value){case`unenforced`:return`Hit Apply now to recompile. There is nothing to install: if invocations fail with a sandbox error instead, nsjail needs /dev/net/tun (Docker: --device /dev/net/tun).`;case`stale`:case`degraded`:return`Fix the reported rule or resolver, then hit Apply now to recompile.`;default:return``}}),Lt=_(()=>{let e=j.value.policy_rule_counts||{};return`${e.v4??0} v4 · ${e.v6??0} v6 · ${e.reject??0} reject · ${e.allow??0} allow`}),Rt=_(()=>{let e=j.value.control_plane_allow||{},t=e.addrs||[];return t.length?`${t.join(`, `)}${e.port?`:${e.port}`:``}`:``}),zt=_(()=>{let e=j.value.last_success_at;if(!e)return``;let t=new Date(e);return Number.isNaN(t.getTime())?e:t.toLocaleTimeString()}),$=async()=>{let e=await x.get(`/firewall/rules`);A.value=e.data.rules||[],j.value={...pt(),...e.data.status||{}}},Bt=async e=>{M.value=e.id;try{await x.put(`/firewall/rules/${e.id}`,{enabled:!e.enabled}),await $()}catch(e){k.notify({title:`Toggle failed`,message:e.response?.data?.error?.message||e.message,danger:!0})}finally{M.value=null}},Vt=async e=>{if(await k.ask({title:`Delete custom rule?`,message:`"${e.value}" will be removed from the blocklist.`,confirmLabel:`Delete`,danger:!0}))try{await x.delete(`/firewall/rules/${e.id}`),await $()}catch(e){k.notify({title:`Delete failed`,message:e.response?.data?.error?.message||e.message,danger:!0})}},Ht=async()=>{if(I.value.value.trim()){P.value=!0;try{await x.post(`/firewall/rules`,{rule_type:I.value.rule_type,value:I.value.value.trim(),label:I.value.label.trim()}),N.value=!1,I.value={rule_type:`cidr`,value:``,label:``},await $()}catch(e){k.notify({title:`Failed to add rule`,message:e.response?.data?.error?.message||e.message,danger:!0})}finally{P.value=!1}}},Ut=async()=>{F.value=!0;try{let e=await x.post(`/firewall/resolve`);e.data.error&&k.notify({title:`Resolve had errors`,message:e.data.error,danger:!0}),await $()}catch(e){k.notify({title:`Resolve failed`,message:e.response?.data?.error?.message||e.message,danger:!0})}finally{F.value=!1}},Wt=async()=>{await Promise.all([$(),gt()])};o(Wt),a(Wt);let Gt=c({name:`PanelSection`,props:{title:{type:String,default:``},subtitle:{type:String,default:``}},setup(e,{slots:t}){return()=>y(`section`,{class:`space-y-3`},[y(`div`,null,[y(`h2`,{class:`text-sm font-semibold text-white tracking-tight`},e.title),e.subtitle?y(`p`,{class:`text-xs text-foreground-muted mt-0.5`},e.subtitle):null]),y(`div`,null,t.default?.())])}}),Kt={default:{label:`Recommended`,cls:`kind-recommended`},suggested:{label:`Optional`,cls:`kind-optional`},custom:{label:`Yours`,cls:`kind-yours`}},qt=c({name:`RuleCard`,props:{rule:{type:Object,required:!0},status:{type:Object,required:!0},unenforcedReason:{type:String,default:``},busy:{type:Boolean,default:!1},readonlyEdit:{type:Boolean,default:!1}},emits:[`toggle`,`delete`],setup(e,{emit:t}){let n=_(()=>{switch(e.rule.rule_type){case`hostname`:return ee;case`wildcard`:return ce;default:return le}}),r=_(()=>e.rule.rule_type===`cidr`?[e.rule.value]:e.status.hostname_map?.[e.rule.value]||[]),i=_(()=>q(e.rule)),a=_(()=>Tt(e.rule)),o=_(()=>Kt[e.rule.kind]||Kt.custom),s=_(()=>e.unenforcedReason?`unenforced`:e.rule.enabled?e.status.enforced?`on`:`pending`:`off`),c=_(()=>s.value===`unenforced`&&!e.rule.enabled),l=_(()=>c.value?`Cannot be enforced. See the reason on this card.`:e.rule.enabled?`Click to allow`:`Click to block`);return()=>y(`div`,{class:[`rule-card`,`is-${s.value}`]},[y(`div`,{class:`rule-card-row`},[y(`div`,{class:`rule-card-titlewrap`},[y(`div`,{class:`rule-card-title`},i.value),y(`div`,{class:`rule-card-pills`},[y(`span`,{class:[`rule-kind-pill`,o.value.cls]},o.value.label),s.value===`unenforced`?y(`span`,{class:`rule-kind-pill kind-inert`},`Not enforced`):null,s.value===`pending`?y(`span`,{class:`rule-kind-pill kind-inert`},`Not in force`):null])]),y(`button`,{class:[`rule-toggle`,s.value===`on`?`on`:s.value===`off`?`off`:[`inert`,e.rule.enabled?`is-set`:``],e.busy?`busy`:``,`focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background`],disabled:e.busy||c.value,title:l.value,onClick:()=>t(`toggle`)},[y(`span`,{class:`rule-toggle-knob`})])]),e.unenforcedReason?y(`p`,{class:`rule-card-warn`},e.unenforcedReason):null,a.value?y(`p`,{class:`rule-card-why`},a.value):null,y(`div`,{class:`rule-card-foot`},[y(n.value,{class:`rule-card-type-icon`}),y(`code`,{class:`rule-card-value`},e.rule.value),r.value.length&&e.rule.rule_type!==`cidr`?y(`span`,{class:`rule-card-resolved`},`→ ${r.value.slice(0,2).join(`, `)}${r.value.length>2?` +${r.value.length-2}`:``}`):null]),e.readonlyEdit?null:y(`button`,{class:`rule-card-delete`,title:`Remove this block`,onClick:()=>t(`delete`)},[y(re,{class:`w-3.5 h-3.5`})])])}});return(n,a)=>(e(),u(`div`,fe,[l(`header`,pe,[l(`div`,me,[a[12]||=l(`div`,{class:`max-w-2xl`},[l(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` Egress `),l(`p`,{class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},` Control outbound destinations and DNS for function sandboxes. `)],-1),l(`div`,he,[f(w,{variant:`secondary`,size:`sm`,loading:F.value,onClick:Ut},{default:t(()=>[f(s(te),{class:`w-4 h-4`}),a[10]||=h(` Apply now `,-1)]),_:1},8,[`loading`]),f(w,{size:`sm`,onClick:a[0]||=e=>N.value=!0},{default:t(()=>[f(s(C),{class:`w-4 h-4`}),a[11]||=h(` Add block `,-1)]),_:1})])]),l(`div`,{class:d([`flex items-start gap-3 text-xs px-3 py-2 rounded-md border`,Nt.value])},[(e(),m(i(Pt.value),{class:`w-4 h-4 shrink-0 mt-0.5`})),l(`div`,ge,[l(`p`,_e,b(Ft.value),1),j.value.last_error?(e(),u(`p`,ve,b(j.value.last_error),1)):v(``,!0),It.value?(e(),u(`p`,ye,b(It.value),1)):v(``,!0)]),j.value.enforced?(e(),u(`span`,be,b(Y.value)+` enforced · `+b(X.value)+` off `,1)):v(``,!0)],2),l(`details`,xe,[a[19]||=l(`summary`,{class:`cursor-pointer text-xs text-foreground-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary`},` Policy details `,-1),l(`div`,Se,[l(`span`,Ce,[a[13]||=l(`span`,{class:`policy-chip-k`},`Backend`,-1),l(`span`,we,b(j.value.backend||`nstun`),1)]),l(`span`,Te,[a[14]||=l(`span`,{class:`policy-chip-k`},`Generation`,-1),l(`span`,Ee,b(j.value.policy_generation||`none`),1)]),l(`span`,De,[a[15]||=l(`span`,{class:`policy-chip-k`},`Compiled rules`,-1),l(`span`,Oe,b(Lt.value),1)]),j.value.pending_recycle?(e(),u(`span`,ke,[...a[16]||=[l(`span`,{class:`policy-chip-k`},`Rollout`,-1),l(`span`,{class:`policy-chip-v`},`warm workers still on the previous generation`,-1)]])):v(``,!0),Rt.value?(e(),u(`span`,Ae,[a[17]||=l(`span`,{class:`policy-chip-k`},`SDK carve-out`,-1),l(`span`,je,b(Rt.value),1)])):v(``,!0),zt.value?(e(),u(`span`,Me,[a[18]||=l(`span`,{class:`policy-chip-k`},`Applied`,-1),l(`span`,Ne,b(zt.value),1)])):v(``,!0)])])]),f(s(Gt),{title:`DNS`,subtitle:ht.value},{default:t(()=>[l(`div`,Pe,[l(`div`,Fe,[a[22]||=l(`div`,{class:`dns-row-label`},` Upstream resolvers `,-1),l(`div`,Ie,[L.value.servers.length?(e(),u(`div`,Le,[(e(!0),u(g,null,p(L.value.servers,(t,n)=>(e(),u(`span`,{key:t+n,class:`dns-chip`},[f(s(D),{class:`w-3 h-3 opacity-60`}),l(`span`,Re,b(t),1),l(`button`,{class:`dns-chip-x`,title:`Remove`,"aria-label":`Remove resolver ${t}`,onClick:e=>_t(n)},` × `,8,ze)]))),128))])):(e(),u(`div`,Be,[a[20]||=l(`span`,{class:`text-foreground-muted text-xs`},`Defaults:`,-1),(e(!0),u(g,null,p(L.value.defaults,t=>(e(),u(`span`,{key:t,class:`dns-chip muted`},[f(s(D),{class:`w-3 h-3 opacity-60`}),l(`span`,Ve,b(t),1)]))),128))]))]),l(`div`,He,[r(l(`input`,{"onUpdate:modelValue":a[1]||=e=>z.value=e,"aria-label":`Resolver address`,placeholder:`1.1.1.1`,class:`dns-input`,onKeydown:E(U,[`enter`])},null,544),[[S,z.value]]),f(w,{variant:`secondary`,size:`sm`,disabled:!z.value.trim(),onClick:U},{default:t(()=>[f(s(C),{class:`w-3.5 h-3.5`}),a[21]||=h(` Add resolver `,-1)]),_:1},8,[`disabled`]),r(l(`input`,{"onUpdate:modelValue":a[2]||=e=>L.value.search=e,"aria-label":`DNS search domain`,placeholder:`search domain`,class:`dns-input narrow`},null,512),[[S,L.value.search]])])]),l(`div`,Ue,[l(`div`,We,[a[23]||=h(` Host overrides `,-1),l(`span`,Ge,b(L.value.records.length)+` record`+b(L.value.records.length===1?``:`s`),1)]),L.value.records.length?(e(),u(`div`,Ke,[(e(!0),u(g,null,p(L.value.records,(t,n)=>(e(),u(`div`,{key:t.host+n,class:`dns-record`},[l(`span`,qe,b(t.host),1),a[24]||=l(`span`,{class:`text-foreground-muted text-xs`},`→`,-1),l(`span`,Je,b(t.ip),1),l(`button`,{class:`dns-chip-x`,title:`Remove`,"aria-label":`Remove host override ${t.host}`,onClick:e=>vt(n)},` × `,8,Ye)]))),128))])):(e(),u(`div`,Xe,` No overrides. Anything resolves through the upstream resolvers above. `)),l(`div`,Ze,[r(l(`input`,{"onUpdate:modelValue":a[3]||=e=>B.value=e,"aria-label":`Override hostname`,placeholder:`api.internal`,class:`dns-input host`,onKeydown:E(W,[`enter`])},null,544),[[S,B.value]]),a[26]||=l(`span`,{class:`text-foreground-muted text-xs`},`→`,-1),r(l(`input`,{"onUpdate:modelValue":a[4]||=e=>V.value=e,"aria-label":`Override IP address`,placeholder:`10.0.5.10`,class:`dns-input`,onKeydown:E(W,[`enter`])},null,544),[[S,V.value]]),f(w,{variant:`secondary`,size:`sm`,disabled:!(B.value.trim()&&V.value.trim()),onClick:W},{default:t(()=>[f(s(C),{class:`w-3.5 h-3.5`}),a[25]||=h(` Add record `,-1)]),_:1},8,[`disabled`])])]),l(`div`,Qe,[a[28]||=l(`span`,{class:`dns-hint`},` Overrides bypass upstream DNS. Existing warm workers update after recycle. `,-1),L.value.servers.length||L.value.search||L.value.records.length?(e(),u(`button`,{key:0,class:`text-xs text-foreground-muted hover:text-white px-2 py-1 transition-colors`,onClick:yt},` Reset `)):v(``,!0),f(w,{size:`sm`,loading:H.value,disabled:!mt.value,onClick:bt},{default:t(()=>[...a[27]||=[h(` Save `,-1)]]),_:1},8,[`loading`,`disabled`])])])]),_:1},8,[`subtitle`]),f(s(Gt),{title:`Blocklist`,subtitle:Ot.value},{default:t(()=>[K.value.length?(e(),u(`div`,$e,[f(s(ie),{class:`w-3.5 h-3.5 shrink-0 mt-0.5`}),l(`div`,et,[l(`p`,null,b(K.value.length===1?`One stored rule is not part of the compiled policy and blocks nothing:`:`${K.value.length} stored rules are not part of the compiled policy and block nothing:`),1),l(`ul`,null,[(e(!0),u(g,null,p(K.value,t=>(e(),u(`li`,{key:t.id},[l(`code`,null,b(t.value),1),h(`: `+b(t.reason),1)]))),128))])])])):v(``,!0),l(`div`,tt,[(e(!0),u(g,null,p(Et.value,t=>(e(),u(`button`,{key:t.id,class:d([`rule-filter`,{active:J.value===t.id}]),onClick:e=>J.value=t.id},[h(b(t.label)+` `,1),l(`span`,rt,b(t.count),1)],10,nt))),128))]),Dt.value.length?(e(),u(`div`,st,[(e(!0),u(g,null,p(Dt.value,t=>(e(),m(s(qt),{key:t.id,rule:t,status:j.value,"unenforced-reason":Ct(t),busy:M.value===t.id,"readonly-edit":t.kind!==`custom`,onToggle:e=>Bt(t),onDelete:e=>Vt(t)},null,8,[`rule`,`status`,`unenforced-reason`,`busy`,`readonly-edit`,`onToggle`,`onDelete`]))),128))])):(e(),u(`div`,it,[f(s(de),{class:`w-5 h-5 mb-2 text-foreground-muted/60`}),l(`p`,at,b(J.value===`yours`?`No custom blocks yet`:`Nothing matches this filter`),1),l(`p`,ot,b(J.value===`yours`?`Add an IP, network, or hostname.`:`Try another filter.`),1),J.value===`yours`?(e(),m(w,{key:0,class:`mt-4`,size:`sm`,variant:`secondary`,onClick:a[5]||=e=>N.value=!0},{default:t(()=>[f(s(C),{class:`w-3.5 h-3.5`}),a[29]||=h(` Add block `,-1)]),_:1})):v(``,!0)]))]),_:1},8,[`subtitle`]),f(oe,{modelValue:N.value,"onUpdate:modelValue":a[9]||=e=>N.value=e,title:`Add block`,icon:s(ue),size:`md`},{footer:t(()=>[f(w,{variant:`secondary`,onClick:a[8]||=e=>N.value=!1},{default:t(()=>[...a[33]||=[h(` Cancel `,-1)]]),_:1}),f(w,{loading:P.value,disabled:!I.value.value.trim()||Mt.value,onClick:Ht},{default:t(()=>[f(s(C),{class:`w-4 h-4`}),a[34]||=h(` Block it `,-1)]),_:1},8,[`loading`,`disabled`])]),default:t(()=>[l(`div`,ct,[a[31]||=l(`p`,{class:`text-xs text-foreground-muted leading-snug`},` Block an IP, network, or hostname. Wildcards are not supported. `,-1),l(`div`,null,[a[30]||=l(`label`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-2`},` What is it? `,-1),l(`div`,lt,[(e(),u(g,null,p(kt,t=>l(`button`,{key:t.value,class:d([`px-2 py-2 rounded border text-xs font-medium transition-colors flex flex-col items-center gap-1`,I.value.rule_type===t.value?`bg-white text-black border-white`:`bg-surface-hover text-foreground-muted border-border hover:border-foreground-muted`]),onClick:e=>I.value.rule_type=t.value},[(e(),m(i(t.icon),{class:`w-3.5 h-3.5`})),h(` `+b(t.label),1)],10,ut)),64))]),l(`p`,dt,b(jt.value),1)]),f(se,{modelValue:I.value.value,"onUpdate:modelValue":a[6]||=e=>I.value.value=e,label:I.value.rule_type===`hostname`?`Hostname`:`IP or network`,placeholder:At.value},null,8,[`modelValue`,`label`,`placeholder`]),Mt.value?(e(),u(`p`,ft,b(O))):v(``,!0),f(se,{modelValue:I.value.label,"onUpdate:modelValue":a[7]||=e=>I.value.label=e,label:`Why? (optional)`,placeholder:`e.g. our staging Postgres`},null,8,[`modelValue`]),a[32]||=l(`p`,{class:`text-xs text-foreground-muted leading-snug`},` Applies within seconds and recycles warm functions. `,-1)])]),_:1},8,[`modelValue`,`icon`])]))}};export{k as default}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/FunctionDiff-DBpPBQBL.js b/backend/internal/server/ui_dist/assets/FunctionDiff-DtVzmfsl.js similarity index 99% rename from backend/internal/server/ui_dist/assets/FunctionDiff-DBpPBQBL.js rename to backend/internal/server/ui_dist/assets/FunctionDiff-DtVzmfsl.js index 422a9140..5e940db6 100644 --- a/backend/internal/server/ui_dist/assets/FunctionDiff-DBpPBQBL.js +++ b/backend/internal/server/ui_dist/assets/FunctionDiff-DtVzmfsl.js @@ -1,4 +1,4 @@ -import{D as e,E as t,F as n,G as r,P as i,T as a,Z as o,c as s,d as c,gt as l,h as u,j as d,k as f,l as p,m,r as h,s as g,u as _,vt as v,x as ee}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as te}from"./copy-Bt6Rv0BQ.js";import{n as ne,r as re,t as ie}from"./settings-2-C7i2we5c.js";import{t as ae}from"./rotate-ccw-DBtrGGiP.js";import{t as oe}from"./zap-CGOT0fxk.js";import{B as se,Dt as ce,R as le,_t as ue,a as de,b as fe,et as pe,gt as me,jt as he,mt as ge,pt as _e}from"./index-C11HmDXN.js";import{A as y,C as ve,D as b,E as ye,M as x,N as S,O as C,S as w,T,_ as E,a as be,b as xe,c as D,d as Se,f as O,g as k,h as A,i as Ce,j,k as M,l as N,m as we,n as Te,o as Ee,p as De,r as Oe,s as ke,t as Ae,u as je,v as P,w as Me,x as Ne,y as F}from"./dist-CR15Grce.js";import{t as Pe}from"./clipboard-D_9N0yai.js";import{t as Fe}from"./rollbackDiff-DsaWcdbl.js";var Ie=he(`arrow-left-right`,[[`path`,{d:`M8 3 4 7l4 4`,key:`9rb6wj`}],[`path`,{d:`M4 7h16`,key:`6tx8e3`}],[`path`,{d:`m16 21 4-4-4-4`,key:`siv7j2`}],[`path`,{d:`M20 17H4`,key:`h6l3hr`}]]),Le=he(`list`,[[`path`,{d:`M3 5h.01`,key:`18ugdj`}],[`path`,{d:`M3 12h.01`,key:`nlz23k`}],[`path`,{d:`M3 19h.01`,key:`noohij`}],[`path`,{d:`M8 5h13`,key:`1pao27`}],[`path`,{d:`M8 12h13`,key:`1za7za`}],[`path`,{d:`M8 19h13`,key:`m83p4d`}]]),I=class e{constructor(e,t,n,r){this.fromA=e,this.toA=t,this.fromB=n,this.toB=r}offset(t,n=t){return new e(this.fromA+t,this.toA+t,this.fromB+n,this.toB+n)}};function L(e,t,n,r,i,a){if(e==r)return[];let o=Be(e,t,n,r,i,a),s=Ve(e,t+o,n,r,i+o,a);t+=o,n-=s,i+=o,a-=s;let c=n-t,l=a-i;if(!c||!l)return[new I(t,n,i,a)];if(c>l){let o=e.slice(t,n).indexOf(r.slice(i,a));if(o>-1)return[new I(t,t+o,i,i),new I(t+o+l,n,a,a)]}else if(l>c){let o=r.slice(i,a).indexOf(e.slice(t,n));if(o>-1)return[new I(t,t,i,i+o),new I(n,n,i+o+c,a)]}if(c==1||l==1)return[new I(t,n,i,a)];let u=Ue(e,t,n,r,i,a);if(u){let[o,s,c]=u;return L(e,t,o,r,i,s).concat(L(e,o+c,n,r,s+c,a))}return Re(e,t,n,r,i,a)}var R=1e9,z=0,B=!1;function Re(e,t,n,r,i,a){let o=n-t,s=a-i;if(R<1e9&&Math.min(o,s)>R*16||z>0&&Date.now()>z)return Math.min(o,s)>R*64?[new I(t,n,i,a)]:G(e,t,n,r,i,a);let c=Math.ceil((o+s)/2);ze.reset(c),H.reset(c);let l=(n,a)=>e.charCodeAt(t+n)==r.charCodeAt(i+a),u=(t,i)=>e.charCodeAt(n-t-1)==r.charCodeAt(a-i-1),d=(o-s)%2==0?null:H,f=d?null:ze;for(let p=0;pR||z>0&&!(p&63)&&Date.now()>z)return G(e,t,n,r,i,a);let m=ze.advance(p,o,s,c,d,!1,l)||H.advance(p,o,s,c,f,!0,u);if(m)return U(e,t,n,t+m[0],r,i,a,i+m[1])}return[new I(t,n,i,a)]}var V=class{constructor(){this.vec=[]}reset(e){this.len=e<<1;for(let e=0;et)this.end+=2;else if(u>n)this.start+=2;else if(i){let e=r+(t-n)-s;if(e>=0&&e=t-l)return[n,r+n-e]}else{let n=t-i.vec[e];if(l>=n)return[l,u]}}}return null}},ze=new V,H=new V;function U(e,t,n,r,i,a,o,s){let c=!1;return!Y(e,r)&&++r==n&&(c=!0),!Y(i,s)&&++s==o&&(c=!0),c?[new I(t,n,a,o)]:L(e,t,r,i,a,s).concat(L(e,r,n,i,s,o))}function W(e,t){let n=1,r=Math.min(e,t);for(;nn||l>a||e.slice(s,i)!=r.slice(c,l)){if(o==1)return s-t-+!Y(e,s);o>>=1}else if(i==n||l==a)return i-t;else s=i,c=l}}function Ve(e,t,n,r,i,a){if(t==n||i==a||e.charCodeAt(n-1)!=r.charCodeAt(a-1))return 0;let o=W(n-t,a-i);for(let s=n,c=a;;){let a=s-o,l=c-o;if(a>=1}else if(a==t||l==i)return n-a;else s=a,c=l}}function He(e,t,n,r,i,a,o,s){let c=r.slice(i,a),l=null;for(;;){if(l||o=n)break;let d=e.slice(s,u),f=-1;for(;(f=c.indexOf(d,f+1))!=-1;){let o=Be(e,u,n,r,i+f+d.length,a),c=Ve(e,t,s,r,i,i+f),p=d.length+o+c;(!l||l[2]>=1}}function Ue(e,t,n,r,i,a){let o=n-t,s=a-i;if(oi.fromA-t&&r.toB>i.fromB-t&&(e[n-1]=new I(r.fromA,i.toA,r.fromB,i.toB),e.splice(n--,1))}}function Ge(e,t,n){for(;;){We(n,1);let r=!1;for(let i=0;i3||s>3){let c=i==e.length-1?t.length:e[i+1].fromA,l=a.fromA-r,u=c-a.toA,d=Xe(t,a.fromA,l),f=Ye(t,a.toA,u),p=a.fromA-d,m=f-a.toA;if((!o||!s)&&p&&m){let l=Math.max(o,s),[u,h,g]=o?[t,a.fromA,a.toA]:[n,a.fromB,a.toB];l>p&&t.slice(d,a.fromA)==u.slice(g-p,g)?(a=e[i]=new I(d,d+o,a.fromB-p,a.toB-p),d=a.fromA,f=Ye(t,a.toA,c-a.toA)):l>m&&t.slice(a.toA,f)==u.slice(h,h+m)&&(a=e[i]=new I(f-o,f,a.fromB+m,a.toB+m),f=a.toA,d=Xe(t,a.fromA,a.fromA-r)),p=a.fromA-d,m=f-a.toA}if(p||m)a=e[i]=new I(a.fromA-p,a.toA+m,a.fromB-p,a.toB+m);else if(!o){let t=Qe(n,a.fromB,a.toB),r,o=t<0?-1:Ze(n,a.toB,a.fromB);t>-1&&(r=t-a.fromB)<=u&&n.slice(a.fromB,t)==n.slice(a.toB,a.toB+r)?a=e[i]=a.offset(r):o>-1&&(r=a.toB-o)<=l&&n.slice(a.fromB-r,a.fromB)==n.slice(o,a.toB)&&(a=e[i]=a.offset(-r))}else if(!s){let n=Qe(t,a.fromA,a.toA),r,o=n<0?-1:Ze(t,a.toA,a.fromA);n>-1&&(r=n-a.fromA)<=u&&t.slice(a.fromA,n)==t.slice(a.toA,a.toA+r)?a=e[i]=a.offset(r):o>-1&&(r=a.toA-o)<=l&&t.slice(a.fromA-r,a.fromA)==t.slice(o,a.toA)&&(a=e[i]=a.offset(-r))}}r=a.toA}return We(e,3),e}var q;try{q=RegExp(`[\\p{Alphabetic}\\p{Number}]`,`u`)}catch{}function Ke(e){return e>48&&e<58||e>64&&e<91||e>96&&e<123}function qe(e,t){if(t==e.length)return 0;let n=e.charCodeAt(t);return n<192?+!!Ke(n):q?!$e(n)||t==e.length-1?+!!q.test(String.fromCharCode(n)):q.test(e.slice(t,t+2))?2:0:0}function J(e,t){if(!t)return 0;let n=e.charCodeAt(t-1);return n<192?+!!Ke(n):q?!et(n)||t==1?+!!q.test(String.fromCharCode(n)):q.test(e.slice(t-2,t))?2:0:0}var Je=8;function Ye(e,t,n){if(t==e.length||!J(e,t))return t;for(let r=t,i=t+n,a=0;ai)return r;r+=t}return t}function Xe(e,t,n){if(!t||!qe(e,t))return t;for(let r=t,i=t-n,a=0;ae>=55296&&e<=56319,et=e=>e>=56320&&e<=57343;function Y(e,t){return!t||t==e.length||!$e(e.charCodeAt(t-1))||!et(e.charCodeAt(t))}function tt(e,t,n){let r=n?.override;return r?r(e,t):(R=(n?.scanLimit??1e9)>>1,z=n?.timeout?Date.now()+n.timeout:0,B=!1,Ge(e,t,L(e,0,e.length,t,0,t.length)))}function nt(){return!B}function rt(e,t,n){return K(tt(e,t,n),e,t)}var X=b.define({combine:e=>e[0]}),it=j.define(),at=b.define(),Z=x.define({create(e){return null},update(e,t){for(let n of t.effects)n.is(it)&&(e=n.value);for(let n of t.state.facet(at))e=n(e,t);return e}}),Q=class e{constructor(e,t,n,r,i,a=!0){this.changes=e,this.fromA=t,this.toA=n,this.fromB=r,this.toB=i,this.precise=a}offset(t,n){return t||n?new e(this.changes,this.fromA+t,this.toA+t,this.fromB+n,this.toB+n,this.precise):this}get endA(){return Math.max(this.fromA,this.toA-1)}get endB(){return Math.max(this.fromB,this.toB-1)}static build(e,t,n){return ct(rt(e.toString(),t.toString(),n),e,t,0,0,nt())}static updateA(e,t,n,r,i){return ft(dt(e,r,!0,n.length),e,t,n,i)}static updateB(e,t,n,r,i){return ft(dt(e,r,!1,t.length),e,t,n,i)}};function ot(e,t,n,r){let i=n.lineAt(e),a=r.lineAt(t);return i.to==e&&a.to==t&&ed+1&&c>f+1)break;p.push(a.offset(-l+r,-u+i)),[d,f]=st(a.toA+r,a.toB+i,t,n),s++}o.push(new Q(p,l,Math.max(l,d),u,Math.max(u,f),a))}return o}var lt=1e3;function ut(e,t,n,r){let i=0,a=e.length;for(;;){if(i==a){let r=0,a=0;i&&({toA:r,toB:a}=e[i-1]);let o=t-(n?r:a);return[r+o,a+o]}let o=i+a>>1,s=e[o],[c,l]=n?[s.fromA,s.toA]:[s.fromB,s.toB];if(c>t)a=o;else if(l<=t)i=o+1;else return r?[s.fromA,s.fromB]:[s.toA,s.toB]}}function dt(e,t,n,r){let i=[];return t.iterChangedRanges((a,o,s,c)=>{let l=0,u=n?t.length:r,d=0,f=n?r:t.length;a>lt&&([l,d]=ut(e,a-lt,n,!0)),o=l?i[i.length-1]={fromA:m.fromA,fromB:m.fromB,toA:u,toB:f,diffA:m.diffA+h,diffB:m.diffB+g}:i.push({fromA:l,toA:u,fromB:d,toB:f,diffA:h,diffB:g})}),i}function ft(e,t,n,r,i){if(!e.length)return t;let a=[];for(let o=0,s=0,c=0,l=0;;o++){let u=o==e.length?null:e[o],d=u?u.fromA+s:n.length,f=u?u.fromB+c:r.length;for(;ld||e.toB+c>f))break;a.push(e.offset(s,c)),l++}if(!u)break;let p=u.toA+s+u.diffA,m=u.toB+c+u.diffB,h=rt(n.sliceString(d,p),r.sliceString(f,m),i);for(let e of ct(h,n,r,d,f,nt()))a.push(e);for(s+=u.diffA,c+=u.diffB;lp&&e.fromB+c>m)break;l++}}return a}var pt={scanLimit:500},mt=xe.fromClass(class{constructor(e){({deco:this.deco,gutter:this.gutter}=wt(e))}update(e){(e.docChanged||e.viewportChanged||gt(e.startState,e.state)||_t(e.startState,e.state))&&({deco:this.deco,gutter:this.gutter}=wt(e.view))}},{decorations:e=>e.deco}),ht=C.low(w({class:`cm-changeGutter`,markers:e=>e.plugin(mt)?.gutter||M.empty}));function gt(e,t){return e.field(Z,!1)!=t.field(Z,!1)}function _t(e,t){return e.facet(X)!=t.facet(X)}var vt=E.line({class:`cm-changedLine`}),yt=E.mark({class:`cm-changedText`}),bt=E.mark({tagName:`ins`,class:`cm-insertedLine`}),xt=E.mark({tagName:`del`,class:`cm-deletedLine`}),St=new class extends F{constructor(){super(...arguments),this.elementClass=`cm-changedLineGutter`}};function Ct(e,t,n,r,i,a){let o=n?e.fromA:e.fromB,s=n?e.toA:e.toB,c=0;if(o!=s){i.add(o,o,vt),i.add(o,s,n?xt:bt),a&&a.add(o,o,St);for(let l=t.iterRange(o,s-1),u=o;!l.next().done;){if(l.lineBreak){u++,i.add(u,u,vt),a&&a.add(u,u,St);continue}let t=u+l.value.length;if(r)for(;c=u)break;(o?n.toA:n.toB)>l&&(!a||!a(e.state,n,s,c))&&Ct(n,e.state.doc,o,r,s,c)}return{deco:s.finish(),gutter:c&&c.finish()}}var Tt=class extends Ne{constructor(e){super(),this.height=e}eq(e){return this.height==e.height}toDOM(){let e=document.createElement(`div`);return e.className=`cm-mergeSpacer`,e.style.height=this.height+`px`,e}updateDOM(e){return e.style.height=this.height+`px`,!0}get estimatedHeight(){return this.height}ignoreEvent(){return!1}},Et=j.define({map:(e,t)=>e.map(t)}),Dt=x.define({create:()=>E.none,update:(e,t)=>{for(let e of t.effects)if(e.is(Et))return e.value;return e.map(t.changes)},provide:e=>P.decorations.from(e)}),Ot=.01;function kt(e,t){if(e.size!=t.size)return!1;let n=e.iter(),r=t.iter();for(;n.value;){if(n.from!=r.from||Math.abs(n.value.spec.widget.height-r.value.spec.widget.height)>1)return!1;n.next(),r.next()}return!0}function At(e,t,n){let r=new y,i=new y,a=e.state.field(Dt).iter(),o=t.state.field(Dt).iter(),s=0,c=0,l=0,u=0,d=e.viewport,f=t.viewport;for(let p=0;;p++){let m=pOt&&(u+=n,i.add(c,c,E.widget({widget:new Tt(n),block:!0,side:-1})))}if(h>s+1e3&&sd.from&&cf.from){let e=Math.min(d.from-s,f.from-c);s+=e,c+=e,p--}else if(m)s=m.toA,c=m.toB;else break;for(;a.value&&a.fromOt&&i.add(t.state.doc.length,t.state.doc.length,E.widget({widget:new Tt(p),block:!0,side:1}));let m=r.finish(),h=i.finish();kt(m,e.state.field(Dt))||e.dispatch({effects:Et.of(m)}),kt(h,t.state.field(Dt))||t.dispatch({effects:Et.of(h)})}var jt=j.define({map:(e,t)=>t.mapPos(e)}),Mt=class extends Ne{constructor(e){super(),this.lines=e}eq(e){return this.lines==e.lines}toDOM(e){let t=document.createElement(`div`);return t.className=`cm-collapsedLines`,t.textContent=e.state.phrase(`$ unchanged lines`,this.lines),t.addEventListener(`click`,t=>{let n=e.posAtDOM(t.target);e.dispatch({effects:jt.of(n)});let{side:r,sibling:i}=e.state.facet(X);i&&i().dispatch({effects:jt.of(Nt(n,e.state.field(Z),r==`a`))})}),t}ignoreEvent(e){return e instanceof MouseEvent}get estimatedHeight(){return 27}get type(){return`collapsed-unchanged-code`}};function Nt(e,t,n){let r=0,i=0;for(let a=0;;a++){let o=a=e)return i+(e-r);[r,i]=n?[o.toA,o.toB]:[o.toB,o.toA]}}var Pt=x.define({create(e){return E.none},update(e,t){e=e.map(t.changes);for(let n of t.effects)n.is(jt)&&(e=e.update({filter:e=>e!=n.value}));if(e.size&&t.state.field(Z)!=t.startState.field(Z,!1)){let n=t.state.facet(X).side==`a`,r=[];for(let i of t.state.field(Z))e.between(n?i.fromA:i.fromB,n?i.toA:i.toB,e=>{r.push(e)});r.length&&(e=e.update({filter:e=>r.indexOf(e)<0}))}return e},provide:e=>P.decorations.from(e)});function Ft({margin:e=3,minSize:t=4}){return Pt.init(n=>It(n,e,t))}function It(e,t,n){let r=new y,i=e.facet(X).side==`a`,a=e.field(Z),o=1;for(let s=0;;s++){let c=s=n&&r.add(e.doc.line(l).from,e.doc.line(u).to,E.replace({widget:new Mt(d),block:!0})),!c)break;o=e.doc.lineAt(Math.min(e.doc.length,i?c.toA:c.toB)).number}return r.finish()}var Lt=P.styleModule.of(new ve({".cm-mergeView":{overflowY:`auto`},".cm-mergeViewEditors":{display:`flex`,alignItems:`stretch`},".cm-mergeViewEditor":{flexGrow:1,flexBasis:0,overflow:`hidden`},".cm-merge-revert":{width:`1.6em`,flexGrow:0,flexShrink:0,position:`relative`},".cm-merge-revert button":{position:`absolute`,display:`block`,width:`100%`,boxSizing:`border-box`,textAlign:`center`,background:`none`,border:`none`,font:`inherit`,cursor:`pointer`}})),Rt=P.baseTheme({".cm-mergeView & .cm-scroller, .cm-mergeView &":{height:`auto !important`,overflowY:`visible !important`},"&.cm-merge-a .cm-changedLine, .cm-deletedChunk":{backgroundColor:`rgba(160, 128, 100, .08)`},"&.cm-merge-b .cm-changedLine, .cm-inlineChangedLine":{backgroundColor:`rgba(100, 160, 128, .08)`},"&light.cm-merge-a .cm-changedText, &light .cm-deletedChunk .cm-deletedText":{background:`linear-gradient(#ee443366, #ee443366) bottom/100% 2px no-repeat`},"&dark.cm-merge-a .cm-changedText, &dark .cm-deletedChunk .cm-deletedText":{background:`linear-gradient(#ffaa9966, #ffaa9966) bottom/100% 2px no-repeat`},"&light.cm-merge-b .cm-changedText":{background:`linear-gradient(#22bb22aa, #22bb22aa) bottom/100% 2px no-repeat`},"&dark.cm-merge-b .cm-changedText":{background:`linear-gradient(#88ff88aa, #88ff88aa) bottom/100% 2px no-repeat`},"&.cm-merge-b .cm-deletedText":{background:`#ff000033`},".cm-insertedLine, .cm-deletedLine, .cm-deletedLine del":{textDecoration:`none`},".cm-deletedChunk":{paddingLeft:`6px`,"& .cm-chunkButtons":{position:`absolute`,insetInlineEnd:`5px`},"& button":{border:`none`,cursor:`pointer`,color:`white`,margin:`0 2px`,borderRadius:`3px`,"&[name=accept]":{background:`#2a2`},"&[name=reject]":{background:`#d43`}}},".cm-collapsedLines":{padding:`5px 5px 5px 10px`,cursor:`pointer`,"&:before":{content:`"⦚"`,marginInlineEnd:`7px`},"&:after":{content:`"⦚"`,marginInlineStart:`7px`}},"&light .cm-collapsedLines":{color:`#444`,background:`linear-gradient(to bottom, transparent 0, #f3f3f3 30%, #f3f3f3 70%, transparent 100%)`},"&dark .cm-collapsedLines":{color:`#ddd`,background:`linear-gradient(to bottom, transparent 0, #222 30%, #222 70%, transparent 100%)`},".cm-changeGutter":{width:`3px`,paddingLeft:`1px`},"&light.cm-merge-a .cm-changedLineGutter, &light .cm-deletedLineGutter":{background:`#e43`},"&dark.cm-merge-a .cm-changedLineGutter, &dark .cm-deletedLineGutter":{background:`#fa9`},"&light.cm-merge-b .cm-changedLineGutter":{background:`#2b2`},"&dark.cm-merge-b .cm-changedLineGutter":{background:`#8f8`},".cm-inlineChangedLineGutter":{background:`#75d`}}),zt=new T,Bt=new T,Vt=class{constructor(e){this.revertDOM=null,this.revertToA=!1,this.revertToLeft=!1,this.measuring=-1,this.diffConf=e.diffConfig||pt;let t=[C.low(mt),Rt,Lt,Dt,P.updateListener.of(e=>{this.measuring<0&&(e.heightChanged||e.viewportChanged)&&!e.transactions.some(e=>e.effects.some(e=>e.is(Et)))&&this.measure()})],n=[X.of({side:`a`,sibling:()=>this.b,highlightChanges:e.highlightChanges!==!1,markGutter:e.gutter!==!1})];e.gutter!==!1&&n.push(ht);let r=ye.create({doc:e.a.doc,selection:e.a.selection,extensions:[e.a.extensions||[],P.editorAttributes.of({class:`cm-merge-a`}),Bt.of(n),t]}),i=[X.of({side:`b`,sibling:()=>this.a,highlightChanges:e.highlightChanges!==!1,markGutter:e.gutter!==!1})];e.gutter!==!1&&i.push(ht);let a=ye.create({doc:e.b.doc,selection:e.b.selection,extensions:[e.b.extensions||[],P.editorAttributes.of({class:`cm-merge-b`}),Bt.of(i),t]});this.chunks=Q.build(r.doc,a.doc,this.diffConf);let o=[Z.init(()=>this.chunks),zt.of(e.collapseUnchanged?Ft(e.collapseUnchanged):[])];r=r.update({effects:j.appendConfig.of(o)}).state,a=a.update({effects:j.appendConfig.of(o)}).state,this.dom=document.createElement(`div`),this.dom.className=`cm-mergeView`,this.editorDOM=this.dom.appendChild(document.createElement(`div`)),this.editorDOM.className=`cm-mergeViewEditors`;let s=e.orientation||`a-b`,c=document.createElement(`div`);c.className=`cm-mergeViewEditor`;let l=document.createElement(`div`);l.className=`cm-mergeViewEditor`,this.editorDOM.appendChild(s==`a-b`?c:l),this.editorDOM.appendChild(s==`a-b`?l:c),this.a=new P({state:r,parent:c,root:e.root,dispatchTransactions:e=>this.dispatch(e,this.a)}),this.b=new P({state:a,parent:l,root:e.root,dispatchTransactions:e=>this.dispatch(e,this.b)}),this.setupRevertControls(!!e.revertControls,e.revertControls==`b-to-a`,e.renderRevertControl),e.parent&&e.parent.appendChild(this.dom),this.scheduleMeasure()}dispatch(e,t){if(e.some(e=>e.docChanged)){let n=e[e.length-1],r=e.reduce((e,t)=>e.compose(t.changes),Me.empty(e[0].startState.doc.length));this.chunks=t==this.a?Q.updateA(this.chunks,n.newDoc,this.b.state.doc,r,this.diffConf):Q.updateB(this.chunks,this.a.state.doc,n.newDoc,r,this.diffConf),t.update([...e,n.state.update({effects:it.of(this.chunks)})]);let i=t==this.a?this.b:this.a;i.update([i.state.update({effects:it.of(this.chunks)})]),this.scheduleMeasure()}else t.update(e)}reconfigure(e){if(`diffConfig`in e&&(this.diffConf=e.diffConfig),`orientation`in e){let t=e.orientation!=`b-a`;if(t!=(this.editorDOM.firstChild==this.a.dom.parentNode)){let e=this.a.dom.parentNode,n=this.b.dom.parentNode;e.remove(),n.remove(),this.editorDOM.insertBefore(t?e:n,this.editorDOM.firstChild),this.editorDOM.appendChild(t?n:e),this.revertToLeft=!this.revertToLeft,this.revertDOM&&(this.revertDOM.textContent=``)}}if(`revertControls`in e||`renderRevertControl`in e){let t=!!this.revertDOM,n=this.revertToA,r=this.renderRevert;`revertControls`in e&&(t=!!e.revertControls,n=e.revertControls==`b-to-a`),`renderRevertControl`in e&&(r=e.renderRevertControl),this.setupRevertControls(t,n,r)}let t=`highlightChanges`in e,n=`gutter`in e,r=`collapseUnchanged`in e;if(t||n||r){let i=[],a=[];if(t||n){let r=this.a.state.facet(X),o=n?e.gutter!==!1:r.markGutter,s=t?e.highlightChanges!==!1:r.highlightChanges;i.push(Bt.reconfigure([X.of({side:`a`,sibling:()=>this.b,highlightChanges:s,markGutter:o}),o?ht:[]])),a.push(Bt.reconfigure([X.of({side:`b`,sibling:()=>this.a,highlightChanges:s,markGutter:o}),o?ht:[]]))}if(r){let t=zt.reconfigure(e.collapseUnchanged?Ft(e.collapseUnchanged):[]);i.push(t),a.push(t)}this.a.dispatch({effects:i}),this.b.dispatch({effects:a})}this.scheduleMeasure()}setupRevertControls(e,t,n){this.revertToA=t,this.revertToLeft=this.revertToA==(this.editorDOM.firstChild==this.a.dom.parentNode),this.renderRevert=n,!e&&this.revertDOM?(this.revertDOM.remove(),this.revertDOM=null):e&&!this.revertDOM?(this.revertDOM=this.editorDOM.insertBefore(document.createElement(`div`),this.editorDOM.firstChild.nextSibling),this.revertDOM.addEventListener(`mousedown`,e=>this.revertClicked(e)),this.revertDOM.className=`cm-merge-revert`):this.revertDOM&&(this.revertDOM.textContent=``)}scheduleMeasure(){if(this.measuring<0){let e=this.dom.ownerDocument.defaultView||window;this.measuring=e.requestAnimationFrame(()=>{this.measuring=-1,this.measure()})}}measure(){At(this.a,this.b,this.chunks),this.revertDOM&&this.updateRevertButtons()}updateRevertButtons(){let e=this.revertDOM,t=e.firstChild,n=this.a.viewport,r=this.b.viewport;for(let i=0;in.to||a.fromB>r.to)break;if(a.fromA-1&&(this.dom.ownerDocument.defaultView||window).cancelAnimationFrame(this.measuring),this.dom.remove()}};function Ht(e){let t=e.nextSibling;return e.remove(),t}var Ut=new class extends F{constructor(){super(...arguments),this.elementClass=`cm-deletedLineGutter`}},Wt=C.low(w({class:`cm-changeGutter`,markers:e=>e.plugin(mt)?.gutter||M.empty,widgetMarker:(e,t)=>t instanceof Jt?Ut:null}));function Gt(e){let t=typeof e.original==`string`?S.of(e.original.split(/\r?\n/)):e.original,n=e.diffConfig||pt;return[C.low(mt),$t,Rt,P.editorAttributes.of({class:`cm-merge-b`}),at.of((e,t)=>{let r=t.effects.find(e=>e.is(Kt));return r&&(e=Q.updateA(e,r.value.doc,t.startState.doc,r.value.changes,n)),t.docChanged&&(e=Q.updateB(e,t.state.field($),t.newDoc,t.changes,n)),e}),X.of({highlightChanges:e.highlightChanges!==!1,markGutter:e.gutter!==!1,syntaxHighlightDeletions:e.syntaxHighlightDeletions!==!1,syntaxHighlightDeletionsMaxLength:3e3,mergeControls:e.mergeControls??!0,overrideChunk:e.allowInlineDiffs?on:void 0,side:`b`}),$.init(()=>t),e.gutter===!1?[]:Wt,e.collapseUnchanged?Ft(e.collapseUnchanged):[],Z.init(e=>Q.build(t,e.doc,n))]}var Kt=j.define(),$=x.define({create:()=>S.empty,update(e,t){for(let n of t.effects)n.is(Kt)&&(e=n.value.doc);return e}}),qt=new WeakMap,Jt=class extends Ne{constructor(e){super(),this.buildDOM=e,this.dom=null}eq(e){return this.dom==e.dom}toDOM(e){return this.dom||=this.buildDOM(e)}};function Yt(e,t,n){let r=qt.get(t.changes);if(r)return r;let i=E.widget({block:!0,side:-1,widget:new Jt(r=>{let{highlightChanges:i,syntaxHighlightDeletions:a,syntaxHighlightDeletionsMaxLength:o,mergeControls:s}=e.facet(X),c=document.createElement(`div`);if(c.className=`cm-deletedChunk`,s){let t=c.appendChild(document.createElement(`div`));t.className=`cm-chunkButtons`;let n=e=>{e.preventDefault(),Xt(r,r.posAtDOM(c))},i=e=>{e.preventDefault(),Zt(r,r.posAtDOM(c))};if(typeof s==`function`)t.appendChild(s(`accept`,n)),t.appendChild(s(`reject`,i));else{let r=t.appendChild(document.createElement(`button`));r.name=`accept`,r.textContent=e.phrase(`Accept`),r.onmousedown=n;let a=t.appendChild(document.createElement(`button`));a.name=`reject`,a.textContent=e.phrase(`Reject`),a.onmousedown=i}}if(n||t.fromA>=t.toA)return c;let l=r.state.field($).sliceString(t.fromA,t.endA),u=a&&e.facet(De),d=h(),f=t.changes,p=0,m=!1;function h(){let e=c.appendChild(document.createElement(`div`));return e.className=`cm-deletedLine`,e.appendChild(document.createElement(`del`))}function g(e,t,n){for(let r=e;rl){let o=e.slice(t,n).indexOf(r.slice(i,a));if(o>-1)return[new I(t,t+o,i,i),new I(t+o+l,n,a,a)]}else if(l>c){let o=r.slice(i,a).indexOf(e.slice(t,n));if(o>-1)return[new I(t,t,i,i+o),new I(n,n,i+o+c,a)]}if(c==1||l==1)return[new I(t,n,i,a)];let u=Ue(e,t,n,r,i,a);if(u){let[o,s,c]=u;return L(e,t,o,r,i,s).concat(L(e,o+c,n,r,s+c,a))}return Re(e,t,n,r,i,a)}var R=1e9,z=0,B=!1;function Re(e,t,n,r,i,a){let o=n-t,s=a-i;if(R<1e9&&Math.min(o,s)>R*16||z>0&&Date.now()>z)return Math.min(o,s)>R*64?[new I(t,n,i,a)]:G(e,t,n,r,i,a);let c=Math.ceil((o+s)/2);ze.reset(c),H.reset(c);let l=(n,a)=>e.charCodeAt(t+n)==r.charCodeAt(i+a),u=(t,i)=>e.charCodeAt(n-t-1)==r.charCodeAt(a-i-1),d=(o-s)%2==0?null:H,f=d?null:ze;for(let p=0;pR||z>0&&!(p&63)&&Date.now()>z)return G(e,t,n,r,i,a);let m=ze.advance(p,o,s,c,d,!1,l)||H.advance(p,o,s,c,f,!0,u);if(m)return U(e,t,n,t+m[0],r,i,a,i+m[1])}return[new I(t,n,i,a)]}var V=class{constructor(){this.vec=[]}reset(e){this.len=e<<1;for(let e=0;et)this.end+=2;else if(u>n)this.start+=2;else if(i){let e=r+(t-n)-s;if(e>=0&&e=t-l)return[n,r+n-e]}else{let n=t-i.vec[e];if(l>=n)return[l,u]}}}return null}},ze=new V,H=new V;function U(e,t,n,r,i,a,o,s){let c=!1;return!Y(e,r)&&++r==n&&(c=!0),!Y(i,s)&&++s==o&&(c=!0),c?[new I(t,n,a,o)]:L(e,t,r,i,a,s).concat(L(e,r,n,i,s,o))}function W(e,t){let n=1,r=Math.min(e,t);for(;nn||l>a||e.slice(s,i)!=r.slice(c,l)){if(o==1)return s-t-+!Y(e,s);o>>=1}else if(i==n||l==a)return i-t;else s=i,c=l}}function Ve(e,t,n,r,i,a){if(t==n||i==a||e.charCodeAt(n-1)!=r.charCodeAt(a-1))return 0;let o=W(n-t,a-i);for(let s=n,c=a;;){let a=s-o,l=c-o;if(a>=1}else if(a==t||l==i)return n-a;else s=a,c=l}}function He(e,t,n,r,i,a,o,s){let c=r.slice(i,a),l=null;for(;;){if(l||o=n)break;let d=e.slice(s,u),f=-1;for(;(f=c.indexOf(d,f+1))!=-1;){let o=Be(e,u,n,r,i+f+d.length,a),c=Ve(e,t,s,r,i,i+f),p=d.length+o+c;(!l||l[2]>=1}}function Ue(e,t,n,r,i,a){let o=n-t,s=a-i;if(oi.fromA-t&&r.toB>i.fromB-t&&(e[n-1]=new I(r.fromA,i.toA,r.fromB,i.toB),e.splice(n--,1))}}function Ge(e,t,n){for(;;){We(n,1);let r=!1;for(let i=0;i3||s>3){let c=i==e.length-1?t.length:e[i+1].fromA,l=a.fromA-r,u=c-a.toA,d=Xe(t,a.fromA,l),f=Ye(t,a.toA,u),p=a.fromA-d,m=f-a.toA;if((!o||!s)&&p&&m){let l=Math.max(o,s),[u,h,g]=o?[t,a.fromA,a.toA]:[n,a.fromB,a.toB];l>p&&t.slice(d,a.fromA)==u.slice(g-p,g)?(a=e[i]=new I(d,d+o,a.fromB-p,a.toB-p),d=a.fromA,f=Ye(t,a.toA,c-a.toA)):l>m&&t.slice(a.toA,f)==u.slice(h,h+m)&&(a=e[i]=new I(f-o,f,a.fromB+m,a.toB+m),f=a.toA,d=Xe(t,a.fromA,a.fromA-r)),p=a.fromA-d,m=f-a.toA}if(p||m)a=e[i]=new I(a.fromA-p,a.toA+m,a.fromB-p,a.toB+m);else if(!o){let t=Qe(n,a.fromB,a.toB),r,o=t<0?-1:Ze(n,a.toB,a.fromB);t>-1&&(r=t-a.fromB)<=u&&n.slice(a.fromB,t)==n.slice(a.toB,a.toB+r)?a=e[i]=a.offset(r):o>-1&&(r=a.toB-o)<=l&&n.slice(a.fromB-r,a.fromB)==n.slice(o,a.toB)&&(a=e[i]=a.offset(-r))}else if(!s){let n=Qe(t,a.fromA,a.toA),r,o=n<0?-1:Ze(t,a.toA,a.fromA);n>-1&&(r=n-a.fromA)<=u&&t.slice(a.fromA,n)==t.slice(a.toA,a.toA+r)?a=e[i]=a.offset(r):o>-1&&(r=a.toA-o)<=l&&t.slice(a.fromA-r,a.fromA)==t.slice(o,a.toA)&&(a=e[i]=a.offset(-r))}}r=a.toA}return We(e,3),e}var q;try{q=RegExp(`[\\p{Alphabetic}\\p{Number}]`,`u`)}catch{}function Ke(e){return e>48&&e<58||e>64&&e<91||e>96&&e<123}function qe(e,t){if(t==e.length)return 0;let n=e.charCodeAt(t);return n<192?+!!Ke(n):q?!$e(n)||t==e.length-1?+!!q.test(String.fromCharCode(n)):q.test(e.slice(t,t+2))?2:0:0}function J(e,t){if(!t)return 0;let n=e.charCodeAt(t-1);return n<192?+!!Ke(n):q?!et(n)||t==1?+!!q.test(String.fromCharCode(n)):q.test(e.slice(t-2,t))?2:0:0}var Je=8;function Ye(e,t,n){if(t==e.length||!J(e,t))return t;for(let r=t,i=t+n,a=0;ai)return r;r+=t}return t}function Xe(e,t,n){if(!t||!qe(e,t))return t;for(let r=t,i=t-n,a=0;ae>=55296&&e<=56319,et=e=>e>=56320&&e<=57343;function Y(e,t){return!t||t==e.length||!$e(e.charCodeAt(t-1))||!et(e.charCodeAt(t))}function tt(e,t,n){let r=n?.override;return r?r(e,t):(R=(n?.scanLimit??1e9)>>1,z=n?.timeout?Date.now()+n.timeout:0,B=!1,Ge(e,t,L(e,0,e.length,t,0,t.length)))}function nt(){return!B}function rt(e,t,n){return K(tt(e,t,n),e,t)}var X=b.define({combine:e=>e[0]}),it=j.define(),at=b.define(),Z=x.define({create(e){return null},update(e,t){for(let n of t.effects)n.is(it)&&(e=n.value);for(let n of t.state.facet(at))e=n(e,t);return e}}),Q=class e{constructor(e,t,n,r,i,a=!0){this.changes=e,this.fromA=t,this.toA=n,this.fromB=r,this.toB=i,this.precise=a}offset(t,n){return t||n?new e(this.changes,this.fromA+t,this.toA+t,this.fromB+n,this.toB+n,this.precise):this}get endA(){return Math.max(this.fromA,this.toA-1)}get endB(){return Math.max(this.fromB,this.toB-1)}static build(e,t,n){return ct(rt(e.toString(),t.toString(),n),e,t,0,0,nt())}static updateA(e,t,n,r,i){return ft(dt(e,r,!0,n.length),e,t,n,i)}static updateB(e,t,n,r,i){return ft(dt(e,r,!1,t.length),e,t,n,i)}};function ot(e,t,n,r){let i=n.lineAt(e),a=r.lineAt(t);return i.to==e&&a.to==t&&ed+1&&c>f+1)break;p.push(a.offset(-l+r,-u+i)),[d,f]=st(a.toA+r,a.toB+i,t,n),s++}o.push(new Q(p,l,Math.max(l,d),u,Math.max(u,f),a))}return o}var lt=1e3;function ut(e,t,n,r){let i=0,a=e.length;for(;;){if(i==a){let r=0,a=0;i&&({toA:r,toB:a}=e[i-1]);let o=t-(n?r:a);return[r+o,a+o]}let o=i+a>>1,s=e[o],[c,l]=n?[s.fromA,s.toA]:[s.fromB,s.toB];if(c>t)a=o;else if(l<=t)i=o+1;else return r?[s.fromA,s.fromB]:[s.toA,s.toB]}}function dt(e,t,n,r){let i=[];return t.iterChangedRanges((a,o,s,c)=>{let l=0,u=n?t.length:r,d=0,f=n?r:t.length;a>lt&&([l,d]=ut(e,a-lt,n,!0)),o=l?i[i.length-1]={fromA:m.fromA,fromB:m.fromB,toA:u,toB:f,diffA:m.diffA+h,diffB:m.diffB+g}:i.push({fromA:l,toA:u,fromB:d,toB:f,diffA:h,diffB:g})}),i}function ft(e,t,n,r,i){if(!e.length)return t;let a=[];for(let o=0,s=0,c=0,l=0;;o++){let u=o==e.length?null:e[o],d=u?u.fromA+s:n.length,f=u?u.fromB+c:r.length;for(;ld||e.toB+c>f))break;a.push(e.offset(s,c)),l++}if(!u)break;let p=u.toA+s+u.diffA,m=u.toB+c+u.diffB,h=rt(n.sliceString(d,p),r.sliceString(f,m),i);for(let e of ct(h,n,r,d,f,nt()))a.push(e);for(s+=u.diffA,c+=u.diffB;lp&&e.fromB+c>m)break;l++}}return a}var pt={scanLimit:500},mt=xe.fromClass(class{constructor(e){({deco:this.deco,gutter:this.gutter}=wt(e))}update(e){(e.docChanged||e.viewportChanged||gt(e.startState,e.state)||_t(e.startState,e.state))&&({deco:this.deco,gutter:this.gutter}=wt(e.view))}},{decorations:e=>e.deco}),ht=C.low(w({class:`cm-changeGutter`,markers:e=>e.plugin(mt)?.gutter||M.empty}));function gt(e,t){return e.field(Z,!1)!=t.field(Z,!1)}function _t(e,t){return e.facet(X)!=t.facet(X)}var vt=E.line({class:`cm-changedLine`}),yt=E.mark({class:`cm-changedText`}),bt=E.mark({tagName:`ins`,class:`cm-insertedLine`}),xt=E.mark({tagName:`del`,class:`cm-deletedLine`}),St=new class extends F{constructor(){super(...arguments),this.elementClass=`cm-changedLineGutter`}};function Ct(e,t,n,r,i,a){let o=n?e.fromA:e.fromB,s=n?e.toA:e.toB,c=0;if(o!=s){i.add(o,o,vt),i.add(o,s,n?xt:bt),a&&a.add(o,o,St);for(let l=t.iterRange(o,s-1),u=o;!l.next().done;){if(l.lineBreak){u++,i.add(u,u,vt),a&&a.add(u,u,St);continue}let t=u+l.value.length;if(r)for(;c=u)break;(o?n.toA:n.toB)>l&&(!a||!a(e.state,n,s,c))&&Ct(n,e.state.doc,o,r,s,c)}return{deco:s.finish(),gutter:c&&c.finish()}}var Tt=class extends Ne{constructor(e){super(),this.height=e}eq(e){return this.height==e.height}toDOM(){let e=document.createElement(`div`);return e.className=`cm-mergeSpacer`,e.style.height=this.height+`px`,e}updateDOM(e){return e.style.height=this.height+`px`,!0}get estimatedHeight(){return this.height}ignoreEvent(){return!1}},Et=j.define({map:(e,t)=>e.map(t)}),Dt=x.define({create:()=>E.none,update:(e,t)=>{for(let e of t.effects)if(e.is(Et))return e.value;return e.map(t.changes)},provide:e=>P.decorations.from(e)}),Ot=.01;function kt(e,t){if(e.size!=t.size)return!1;let n=e.iter(),r=t.iter();for(;n.value;){if(n.from!=r.from||Math.abs(n.value.spec.widget.height-r.value.spec.widget.height)>1)return!1;n.next(),r.next()}return!0}function At(e,t,n){let r=new y,i=new y,a=e.state.field(Dt).iter(),o=t.state.field(Dt).iter(),s=0,c=0,l=0,u=0,d=e.viewport,f=t.viewport;for(let p=0;;p++){let m=pOt&&(u+=n,i.add(c,c,E.widget({widget:new Tt(n),block:!0,side:-1})))}if(h>s+1e3&&sd.from&&cf.from){let e=Math.min(d.from-s,f.from-c);s+=e,c+=e,p--}else if(m)s=m.toA,c=m.toB;else break;for(;a.value&&a.fromOt&&i.add(t.state.doc.length,t.state.doc.length,E.widget({widget:new Tt(p),block:!0,side:1}));let m=r.finish(),h=i.finish();kt(m,e.state.field(Dt))||e.dispatch({effects:Et.of(m)}),kt(h,t.state.field(Dt))||t.dispatch({effects:Et.of(h)})}var jt=j.define({map:(e,t)=>t.mapPos(e)}),Mt=class extends Ne{constructor(e){super(),this.lines=e}eq(e){return this.lines==e.lines}toDOM(e){let t=document.createElement(`div`);return t.className=`cm-collapsedLines`,t.textContent=e.state.phrase(`$ unchanged lines`,this.lines),t.addEventListener(`click`,t=>{let n=e.posAtDOM(t.target);e.dispatch({effects:jt.of(n)});let{side:r,sibling:i}=e.state.facet(X);i&&i().dispatch({effects:jt.of(Nt(n,e.state.field(Z),r==`a`))})}),t}ignoreEvent(e){return e instanceof MouseEvent}get estimatedHeight(){return 27}get type(){return`collapsed-unchanged-code`}};function Nt(e,t,n){let r=0,i=0;for(let a=0;;a++){let o=a=e)return i+(e-r);[r,i]=n?[o.toA,o.toB]:[o.toB,o.toA]}}var Pt=x.define({create(e){return E.none},update(e,t){e=e.map(t.changes);for(let n of t.effects)n.is(jt)&&(e=e.update({filter:e=>e!=n.value}));if(e.size&&t.state.field(Z)!=t.startState.field(Z,!1)){let n=t.state.facet(X).side==`a`,r=[];for(let i of t.state.field(Z))e.between(n?i.fromA:i.fromB,n?i.toA:i.toB,e=>{r.push(e)});r.length&&(e=e.update({filter:e=>r.indexOf(e)<0}))}return e},provide:e=>P.decorations.from(e)});function Ft({margin:e=3,minSize:t=4}){return Pt.init(n=>It(n,e,t))}function It(e,t,n){let r=new y,i=e.facet(X).side==`a`,a=e.field(Z),o=1;for(let s=0;;s++){let c=s=n&&r.add(e.doc.line(l).from,e.doc.line(u).to,E.replace({widget:new Mt(d),block:!0})),!c)break;o=e.doc.lineAt(Math.min(e.doc.length,i?c.toA:c.toB)).number}return r.finish()}var Lt=P.styleModule.of(new ve({".cm-mergeView":{overflowY:`auto`},".cm-mergeViewEditors":{display:`flex`,alignItems:`stretch`},".cm-mergeViewEditor":{flexGrow:1,flexBasis:0,overflow:`hidden`},".cm-merge-revert":{width:`1.6em`,flexGrow:0,flexShrink:0,position:`relative`},".cm-merge-revert button":{position:`absolute`,display:`block`,width:`100%`,boxSizing:`border-box`,textAlign:`center`,background:`none`,border:`none`,font:`inherit`,cursor:`pointer`}})),Rt=P.baseTheme({".cm-mergeView & .cm-scroller, .cm-mergeView &":{height:`auto !important`,overflowY:`visible !important`},"&.cm-merge-a .cm-changedLine, .cm-deletedChunk":{backgroundColor:`rgba(160, 128, 100, .08)`},"&.cm-merge-b .cm-changedLine, .cm-inlineChangedLine":{backgroundColor:`rgba(100, 160, 128, .08)`},"&light.cm-merge-a .cm-changedText, &light .cm-deletedChunk .cm-deletedText":{background:`linear-gradient(#ee443366, #ee443366) bottom/100% 2px no-repeat`},"&dark.cm-merge-a .cm-changedText, &dark .cm-deletedChunk .cm-deletedText":{background:`linear-gradient(#ffaa9966, #ffaa9966) bottom/100% 2px no-repeat`},"&light.cm-merge-b .cm-changedText":{background:`linear-gradient(#22bb22aa, #22bb22aa) bottom/100% 2px no-repeat`},"&dark.cm-merge-b .cm-changedText":{background:`linear-gradient(#88ff88aa, #88ff88aa) bottom/100% 2px no-repeat`},"&.cm-merge-b .cm-deletedText":{background:`#ff000033`},".cm-insertedLine, .cm-deletedLine, .cm-deletedLine del":{textDecoration:`none`},".cm-deletedChunk":{paddingLeft:`6px`,"& .cm-chunkButtons":{position:`absolute`,insetInlineEnd:`5px`},"& button":{border:`none`,cursor:`pointer`,color:`white`,margin:`0 2px`,borderRadius:`3px`,"&[name=accept]":{background:`#2a2`},"&[name=reject]":{background:`#d43`}}},".cm-collapsedLines":{padding:`5px 5px 5px 10px`,cursor:`pointer`,"&:before":{content:`"⦚"`,marginInlineEnd:`7px`},"&:after":{content:`"⦚"`,marginInlineStart:`7px`}},"&light .cm-collapsedLines":{color:`#444`,background:`linear-gradient(to bottom, transparent 0, #f3f3f3 30%, #f3f3f3 70%, transparent 100%)`},"&dark .cm-collapsedLines":{color:`#ddd`,background:`linear-gradient(to bottom, transparent 0, #222 30%, #222 70%, transparent 100%)`},".cm-changeGutter":{width:`3px`,paddingLeft:`1px`},"&light.cm-merge-a .cm-changedLineGutter, &light .cm-deletedLineGutter":{background:`#e43`},"&dark.cm-merge-a .cm-changedLineGutter, &dark .cm-deletedLineGutter":{background:`#fa9`},"&light.cm-merge-b .cm-changedLineGutter":{background:`#2b2`},"&dark.cm-merge-b .cm-changedLineGutter":{background:`#8f8`},".cm-inlineChangedLineGutter":{background:`#75d`}}),zt=new T,Bt=new T,Vt=class{constructor(e){this.revertDOM=null,this.revertToA=!1,this.revertToLeft=!1,this.measuring=-1,this.diffConf=e.diffConfig||pt;let t=[C.low(mt),Rt,Lt,Dt,P.updateListener.of(e=>{this.measuring<0&&(e.heightChanged||e.viewportChanged)&&!e.transactions.some(e=>e.effects.some(e=>e.is(Et)))&&this.measure()})],n=[X.of({side:`a`,sibling:()=>this.b,highlightChanges:e.highlightChanges!==!1,markGutter:e.gutter!==!1})];e.gutter!==!1&&n.push(ht);let r=ye.create({doc:e.a.doc,selection:e.a.selection,extensions:[e.a.extensions||[],P.editorAttributes.of({class:`cm-merge-a`}),Bt.of(n),t]}),i=[X.of({side:`b`,sibling:()=>this.a,highlightChanges:e.highlightChanges!==!1,markGutter:e.gutter!==!1})];e.gutter!==!1&&i.push(ht);let a=ye.create({doc:e.b.doc,selection:e.b.selection,extensions:[e.b.extensions||[],P.editorAttributes.of({class:`cm-merge-b`}),Bt.of(i),t]});this.chunks=Q.build(r.doc,a.doc,this.diffConf);let o=[Z.init(()=>this.chunks),zt.of(e.collapseUnchanged?Ft(e.collapseUnchanged):[])];r=r.update({effects:j.appendConfig.of(o)}).state,a=a.update({effects:j.appendConfig.of(o)}).state,this.dom=document.createElement(`div`),this.dom.className=`cm-mergeView`,this.editorDOM=this.dom.appendChild(document.createElement(`div`)),this.editorDOM.className=`cm-mergeViewEditors`;let s=e.orientation||`a-b`,c=document.createElement(`div`);c.className=`cm-mergeViewEditor`;let l=document.createElement(`div`);l.className=`cm-mergeViewEditor`,this.editorDOM.appendChild(s==`a-b`?c:l),this.editorDOM.appendChild(s==`a-b`?l:c),this.a=new P({state:r,parent:c,root:e.root,dispatchTransactions:e=>this.dispatch(e,this.a)}),this.b=new P({state:a,parent:l,root:e.root,dispatchTransactions:e=>this.dispatch(e,this.b)}),this.setupRevertControls(!!e.revertControls,e.revertControls==`b-to-a`,e.renderRevertControl),e.parent&&e.parent.appendChild(this.dom),this.scheduleMeasure()}dispatch(e,t){if(e.some(e=>e.docChanged)){let n=e[e.length-1],r=e.reduce((e,t)=>e.compose(t.changes),Me.empty(e[0].startState.doc.length));this.chunks=t==this.a?Q.updateA(this.chunks,n.newDoc,this.b.state.doc,r,this.diffConf):Q.updateB(this.chunks,this.a.state.doc,n.newDoc,r,this.diffConf),t.update([...e,n.state.update({effects:it.of(this.chunks)})]);let i=t==this.a?this.b:this.a;i.update([i.state.update({effects:it.of(this.chunks)})]),this.scheduleMeasure()}else t.update(e)}reconfigure(e){if(`diffConfig`in e&&(this.diffConf=e.diffConfig),`orientation`in e){let t=e.orientation!=`b-a`;if(t!=(this.editorDOM.firstChild==this.a.dom.parentNode)){let e=this.a.dom.parentNode,n=this.b.dom.parentNode;e.remove(),n.remove(),this.editorDOM.insertBefore(t?e:n,this.editorDOM.firstChild),this.editorDOM.appendChild(t?n:e),this.revertToLeft=!this.revertToLeft,this.revertDOM&&(this.revertDOM.textContent=``)}}if(`revertControls`in e||`renderRevertControl`in e){let t=!!this.revertDOM,n=this.revertToA,r=this.renderRevert;`revertControls`in e&&(t=!!e.revertControls,n=e.revertControls==`b-to-a`),`renderRevertControl`in e&&(r=e.renderRevertControl),this.setupRevertControls(t,n,r)}let t=`highlightChanges`in e,n=`gutter`in e,r=`collapseUnchanged`in e;if(t||n||r){let i=[],a=[];if(t||n){let r=this.a.state.facet(X),o=n?e.gutter!==!1:r.markGutter,s=t?e.highlightChanges!==!1:r.highlightChanges;i.push(Bt.reconfigure([X.of({side:`a`,sibling:()=>this.b,highlightChanges:s,markGutter:o}),o?ht:[]])),a.push(Bt.reconfigure([X.of({side:`b`,sibling:()=>this.a,highlightChanges:s,markGutter:o}),o?ht:[]]))}if(r){let t=zt.reconfigure(e.collapseUnchanged?Ft(e.collapseUnchanged):[]);i.push(t),a.push(t)}this.a.dispatch({effects:i}),this.b.dispatch({effects:a})}this.scheduleMeasure()}setupRevertControls(e,t,n){this.revertToA=t,this.revertToLeft=this.revertToA==(this.editorDOM.firstChild==this.a.dom.parentNode),this.renderRevert=n,!e&&this.revertDOM?(this.revertDOM.remove(),this.revertDOM=null):e&&!this.revertDOM?(this.revertDOM=this.editorDOM.insertBefore(document.createElement(`div`),this.editorDOM.firstChild.nextSibling),this.revertDOM.addEventListener(`mousedown`,e=>this.revertClicked(e)),this.revertDOM.className=`cm-merge-revert`):this.revertDOM&&(this.revertDOM.textContent=``)}scheduleMeasure(){if(this.measuring<0){let e=this.dom.ownerDocument.defaultView||window;this.measuring=e.requestAnimationFrame(()=>{this.measuring=-1,this.measure()})}}measure(){At(this.a,this.b,this.chunks),this.revertDOM&&this.updateRevertButtons()}updateRevertButtons(){let e=this.revertDOM,t=e.firstChild,n=this.a.viewport,r=this.b.viewport;for(let i=0;in.to||a.fromB>r.to)break;if(a.fromA-1&&(this.dom.ownerDocument.defaultView||window).cancelAnimationFrame(this.measuring),this.dom.remove()}};function Ht(e){let t=e.nextSibling;return e.remove(),t}var Ut=new class extends F{constructor(){super(...arguments),this.elementClass=`cm-deletedLineGutter`}},Wt=C.low(w({class:`cm-changeGutter`,markers:e=>e.plugin(mt)?.gutter||M.empty,widgetMarker:(e,t)=>t instanceof Jt?Ut:null}));function Gt(e){let t=typeof e.original==`string`?S.of(e.original.split(/\r?\n/)):e.original,n=e.diffConfig||pt;return[C.low(mt),$t,Rt,P.editorAttributes.of({class:`cm-merge-b`}),at.of((e,t)=>{let r=t.effects.find(e=>e.is(Kt));return r&&(e=Q.updateA(e,r.value.doc,t.startState.doc,r.value.changes,n)),t.docChanged&&(e=Q.updateB(e,t.state.field($),t.newDoc,t.changes,n)),e}),X.of({highlightChanges:e.highlightChanges!==!1,markGutter:e.gutter!==!1,syntaxHighlightDeletions:e.syntaxHighlightDeletions!==!1,syntaxHighlightDeletionsMaxLength:3e3,mergeControls:e.mergeControls??!0,overrideChunk:e.allowInlineDiffs?on:void 0,side:`b`}),$.init(()=>t),e.gutter===!1?[]:Wt,e.collapseUnchanged?Ft(e.collapseUnchanged):[],Z.init(e=>Q.build(t,e.doc,n))]}var Kt=j.define(),$=x.define({create:()=>S.empty,update(e,t){for(let n of t.effects)n.is(Kt)&&(e=n.value.doc);return e}}),qt=new WeakMap,Jt=class extends Ne{constructor(e){super(),this.buildDOM=e,this.dom=null}eq(e){return this.dom==e.dom}toDOM(e){return this.dom||=this.buildDOM(e)}};function Yt(e,t,n){let r=qt.get(t.changes);if(r)return r;let i=E.widget({block:!0,side:-1,widget:new Jt(r=>{let{highlightChanges:i,syntaxHighlightDeletions:a,syntaxHighlightDeletionsMaxLength:o,mergeControls:s}=e.facet(X),c=document.createElement(`div`);if(c.className=`cm-deletedChunk`,s){let t=c.appendChild(document.createElement(`div`));t.className=`cm-chunkButtons`;let n=e=>{e.preventDefault(),Xt(r,r.posAtDOM(c))},i=e=>{e.preventDefault(),Zt(r,r.posAtDOM(c))};if(typeof s==`function`)t.appendChild(s(`accept`,n)),t.appendChild(s(`reject`,i));else{let r=t.appendChild(document.createElement(`button`));r.name=`accept`,r.textContent=e.phrase(`Accept`),r.onmousedown=n;let a=t.appendChild(document.createElement(`button`));a.name=`reject`,a.textContent=e.phrase(`Reject`),a.onmousedown=i}}if(n||t.fromA>=t.toA)return c;let l=r.state.field($).sliceString(t.fromA,t.endA),u=a&&e.facet(De),d=h(),f=t.changes,p=0,m=!1;function h(){let e=c.appendChild(document.createElement(`div`));return e.className=`cm-deletedLine`,e.appendChild(document.createElement(`del`))}function g(e,t,n){for(let r=e;r-1&&sr){let t=document.createTextNode(l.slice(r,e));if(a){let e=d.appendChild(document.createElement(`span`));e.className=a,e.appendChild(t)}else d.appendChild(t);r=e}o&&(m=!m)}}if(u&&t.toA-t.fromA<=o){let t=u.parser.parse(l),n=0;we(t,{style:t=>Se(e,t)},(e,t,r)=>{e>n&&g(n,e,``),g(e,t,r),n=t}),g(n,l.length,``)}else g(0,l.length,``);return d.firstChild||d.appendChild(document.createElement(`br`)),c})});return qt.set(t.changes,i),i}function Xt(e,t){let{state:n}=e,r=t??n.selection.main.head,i=e.state.field(Z).find(e=>e.fromB<=r&&e.endB>=r);if(!i)return!1;let a=e.state.sliceDoc(i.fromB,Math.max(i.fromB,i.toB-1)),o=e.state.field($);i.fromB!=i.toB&&i.toA<=o.length&&(a+=e.state.lineBreak);let s=Me.of({from:i.fromA,to:Math.min(o.length,i.toA),insert:a},o.length);return e.dispatch({effects:Kt.of({doc:s.apply(o),changes:s}),userEvent:`accept`}),!0}function Zt(e,t){let{state:n}=e,r=t??n.selection.main.head,i=n.field(Z).find(e=>e.fromB<=r&&e.endB>=r);if(!i)return!1;let a=n.field($).sliceString(i.fromA,Math.max(i.fromA,i.toA-1));return i.fromA!=i.toA&&i.toB<=n.doc.length&&(a+=n.lineBreak),e.dispatch({changes:{from:i.fromB,to:Math.min(n.doc.length,i.toB),insert:a},userEvent:`revert`}),!0}function Qt(e){let t=new y;for(let n of e.field(Z)){let r=e.facet(X).overrideChunk&&tn(e,n);t.add(n.fromB,n.fromB,Yt(e,n,!!r))}return t.finish()}var $t=x.define({create:e=>Qt(e),update(e,t){return t.state.field(Z,!1)==t.startState.field(Z,!1)?e:Qt(t.state)},provide:e=>P.decorations.from(e)}),en=new WeakMap;function tn(e,t){let n=en.get(t);if(n!==void 0)return n;n=null;let r=e.field($),i=e.doc,a=r.lineAt(t.endA).number-r.lineAt(t.fromA).number+1,o=i.lineAt(t.endB).number-i.lineAt(t.fromB).number+1;abort:if(a==o&&a<10){let e=[],i=0,o=t.fromA,s=t.fromB;for(let n of t.changes){if(n.fromA=t.endB)break;o=e.doc.lineAt(o.to+1)}return!0}var sn=A({String:k.string,Number:k.number,"True False":k.bool,PropertyName:k.propertyName,Null:k.null,", :":k.separator,"[ ]":k.squareBracket,"{ }":k.brace}),cn=Ce.deserialize({version:14,states:"$bOVQPOOOOQO'#Cb'#CbOnQPO'#CeOvQPO'#ClOOQO'#Cr'#CrQOQPOOOOQO'#Cg'#CgO}QPO'#CfO!SQPO'#CtOOQO,59P,59PO![QPO,59PO!aQPO'#CuOOQO,59W,59WO!iQPO,59WOVQPO,59QOqQPO'#CmO!nQPO,59`OOQO1G.k1G.kOVQPO'#CnO!vQPO,59aOOQO1G.r1G.rOOQO1G.l1G.lOOQO,59X,59XOOQO-E6k-E6kOOQO,59Y,59YOOQO-E6l-E6l",stateData:`#O~OeOS~OQSORSOSSOTSOWQO_ROgPO~OVXOgUO~O^[O~PVO[^O~O]_OVhX~OVaO~O]bO^iX~O^dO~O]_OVha~O]bO^ia~O`,goto:"!kjPPPPPPkPPkqwPPPPk{!RPPP!XP!e!hXSOR^bQWQRf_TVQ_Q`WRg`QcZRicQTOQZRQe^RhbRYQR]R",nodeNames:`⚠ JsonText True False Null Number String } { Object Property PropertyName : , ] [ Array`,maxTerm:25,nodeProps:[[`isolate`,-2,6,11,``],[`openedBy`,7,`{`,14,`[`],[`closedBy`,8,`}`,15,`]`]],propSources:[sn],skippedNodes:[0],repeatNodeCount:2,tokenData:"(|~RaXY!WYZ!W]^!Wpq!Wrs!]|}$u}!O$z!Q!R%T!R![&c![!]&t!}#O&y#P#Q'O#Y#Z'T#b#c'r#h#i(Z#o#p(r#q#r(w~!]Oe~~!`Wpq!]qr!]rs!xs#O!]#O#P!}#P;'S!];'S;=`$o<%lO!]~!}Og~~#QXrs!]!P!Q!]#O#P!]#U#V!]#Y#Z!]#b#c!]#f#g!]#h#i!]#i#j#m~#pR!Q![#y!c!i#y#T#Z#y~#|R!Q![$V!c!i$V#T#Z$V~$YR!Q![$c!c!i$c#T#Z$c~$fR!Q![!]!c!i!]#T#Z!]~$rP;=`<%l!]~$zO]~~$}Q!Q!R%T!R![&c~%YRT~!O!P%c!g!h%w#X#Y%w~%fP!Q![%i~%nRT~!Q![%i!g!h%w#X#Y%w~%zR{|&T}!O&T!Q![&Z~&WP!Q![&Z~&`PT~!Q![&Z~&hST~!O!P%c!Q![&c!g!h%w#X#Y%w~&yO[~~'OO_~~'TO^~~'WP#T#U'Z~'^P#`#a'a~'dP#g#h'g~'jP#X#Y'm~'rOR~~'uP#i#j'x~'{P#`#a(O~(RP#`#a(U~(ZOS~~(^P#f#g(a~(dP#i#j(g~(jP#X#Y(m~(rOQ~~(wOW~~(|OV~",tokenizers:[0],topRules:{JsonText:[0,1]},tokenPrec:0}),ln=Ee.define({name:`json`,parser:cn.configure({props:[O.add({Object:D({except:/^\s*\}/}),Array:D({except:/^\s*\]/})}),je.add({"Object Array":N})]}),languageData:{closeBrackets:{brackets:[`[`,`{`,`"`]},indentOnInput:/^\s*[\}\]]$/}});function un(){return new ke(ln)}var dn={class:`space-y-6`},fn={class:`flex items-end justify-between gap-4 flex-wrap`},pn={class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},mn={class:`flex items-center gap-2`},hn={class:`pb-5 border-b border-border`},gn={class:`grid gap-3 sm:gap-4 sm:grid-cols-[1fr_auto_1fr] items-end`},_n={class:`space-y-1.5`},vn=[`value`],yn=[`value`,`disabled`],bn=[`disabled`],xn={class:`space-y-1.5`},Sn=[`value`],Cn=[`value`,`disabled`],wn={key:0,class:`mt-4 flex flex-wrap items-center gap-2 justify-end`},Tn={key:0,class:`text-sm text-foreground-muted py-12 text-center`},En={key:1,class:`text-sm text-foreground-muted py-12 text-center`},Dn={key:2,class:`bg-warning/10 border border-warning/30 rounded-lg p-5 space-y-3 text-sm`},On={key:0,class:`font-mono text-xs space-y-1`},kn=[`title`,`onClick`],An={key:1,class:`text-warning/90`},jn={key:1,class:`text-warning/70 italic text-xs`},Mn={key:3,class:`bg-danger/10 border border-danger/30 rounded-lg p-5 text-sm text-danger`},Nn={key:4,class:`bg-danger/10 border border-danger/30 rounded-lg p-5 text-sm text-danger`},Pn={key:0,class:`text-xs text-foreground-muted -mt-2`},Fn={key:1,class:`bg-background border border-border rounded-lg`},In={class:`px-4 py-3 flex items-center justify-between gap-3`},Ln={class:`text-xs font-bold uppercase tracking-wider text-foreground-muted flex items-center gap-2`},Rn={class:`text-[10px] px-1.5 py-0.5 rounded bg-warning/15 text-warning border border-warning/30 font-medium tracking-normal normal-case`},zn=[`aria-label`],Bn={key:0,class:`px-4 pb-4 pt-3 border-t border-border/60`},Vn={class:`font-mono text-xs space-y-1`},Hn={key:2,class:`bg-background border border-border rounded-lg overflow-hidden`},Un={class:`px-4 py-2.5 flex items-center justify-between gap-3 bg-surface border-b border-border`},Wn={class:`text-xs font-bold uppercase tracking-wider text-white flex items-center gap-2`},Gn={class:`font-mono normal-case tracking-normal text-sm font-medium`},Kn={key:0,class:`text-[10px] px-1.5 py-0.5 rounded bg-success/15 text-success border border-success/30 font-medium tracking-normal normal-case`},qn={key:1,class:`text-[10px] px-1.5 py-0.5 rounded bg-danger/15 text-danger border border-danger/30 font-medium tracking-normal normal-case`},Jn=[`title`,`aria-pressed`],Yn={key:3,class:`bg-background border border-border rounded-lg overflow-hidden`},Xn={class:`px-4 py-2.5 flex items-center justify-between gap-3 bg-surface border-b border-border`},Zn={class:`text-xs font-bold uppercase tracking-wider text-foreground-muted flex items-center gap-2`},Qn={class:`font-mono normal-case tracking-normal text-white text-sm font-medium`},$n=[`aria-label`],er={key:4,class:`text-sm text-foreground-muted py-12 text-center`},tr=`orva:diff:sideBySide`,nr={__name:`FunctionDiff`,setup(he){let y=_e(),ve=ge(),b=g(()=>y.params.name),x=g(()=>y.query.from||``),S=g(()=>y.query.to||``),C=r(null),w=r([]),T=r(null),E=r(``),xe=r(``),D=r([]),Se=r(`Copy link`),O=r(!0),k=r(!0),A=r(!1),Ce=me(),j=r(!0),M=r(null),N=g(()=>M.value===null?j.value:M.value);try{let e=typeof window<`u`&&window.localStorage?.getItem?.(tr);e===`true`?M.value=!0:e===`false`&&(M.value=!1)}catch{}let we=()=>{let e=!N.value;M.value=e;try{window.localStorage?.setItem?.(tr,String(e))}catch{}V()},Ee=r(null),De=r(null),ke=null,je=null,Me=g(()=>C.value?.runtime?.startsWith(`python`)?[Te()]:[Oe()]),Ne=P.theme({"&.cm-merge-a .cm-changedLine":{backgroundColor:`rgba(248, 81, 73, 0.15)`},"&.cm-merge-b .cm-changedLine":{backgroundColor:`rgba(63, 185, 80, 0.15)`},"&.cm-merge-a .cm-changedText":{background:`none`,backgroundColor:`rgba(248, 81, 73, 0.35)`,borderRadius:`2px`},"&.cm-merge-b .cm-changedText":{background:`none`,backgroundColor:`rgba(63, 185, 80, 0.30)`,borderRadius:`2px`},"&.cm-merge-a .cm-changedLineGutter":{color:`#f85149`},"&.cm-merge-b .cm-changedLineGutter":{color:`#3fb950`},".cm-deletedChunk":{backgroundColor:`rgba(248, 81, 73, 0.15)`},".cm-deletedChunk .cm-deletedText":{background:`none`,backgroundColor:`rgba(248, 81, 73, 0.35)`,borderRadius:`2px`},".cm-deletedChunk .cm-changedLineGutter, .cm-deletedChunk .cm-deletedLineGutter":{color:`#f85149`},".cm-insertedLine, .cm-changedLine, .cm-inlineChangedLine":{backgroundColor:`rgba(63, 185, 80, 0.15)`},".cm-changedLine .cm-changedText, .cm-inlineChangedLine .cm-changedText":{background:`none`,backgroundColor:`rgba(63, 185, 80, 0.30)`,borderRadius:`2px`},".cm-changedLineGutter, .cm-inlineChangedLineGutter":{color:`#3fb950`}}),F=e=>[be,...e,Ae,Ne,ye.readOnly.of(!0),P.lineWrapping],I=null,L=()=>{if(typeof window>`u`||!window.matchMedia)return;let e=window.matchMedia(`(min-width: 768px)`),t=e=>{let t=!!e.matches;t!==j.value&&(j.value=t,M.value===null&&V())};t(e),e.addEventListener?(e.addEventListener(`change`,t),I=()=>e.removeEventListener(`change`,t)):(e.addListener(t),I=()=>e.removeListener(t))},R=(e,t,n,r)=>e?N.value?new Vt({a:{doc:t,extensions:F(r)},b:{doc:n,extensions:F(r)},parent:e,orientation:`a-b`,revertControls:!1,highlightChanges:!0,gutter:!0}):new P({parent:e,state:ye.create({doc:n,extensions:[...F(r),Gt({original:t,mergeControls:!1})]})}):null,z=()=>{je?.destroy?.(),je=null},B=()=>{ke?.destroy?.(),z(),ke=null},Re=()=>{let e=U.value;!e||!k.value||e.before===e.after&&!e.added&&!e.removed||(je=R(De.value,e.before||``,e.after||``,[un()]))},V=async()=>{if(await ee(),B(),!T.value)return;let e=H.value;e&&(ke=R(Ee.value,e.before||``,e.after||``,Me.value)),Re()},ze=async()=>{if(E.value=``,xe.value=``,T.value=null,B(),!(!C.value||!x.value||!S.value||x.value===S.value))try{let e=await de(C.value.id,x.value,S.value,`json`);T.value=e.data,await V()}catch(e){let t=e.response?.data?.error;E.value=t?.code||`INTERNAL`,xe.value=t?.message||e.message||`Failed to load diff.`,E.value===`VERSION_GCD`&&(D.value=t?.details?.available_hashes||[])}},H=g(()=>T.value?.files?.find(e=>e.kind===`handler`)),U=g(()=>T.value?.files?.find(e=>e.kind===`manifest`)),W=g(()=>{let e=T.value?.from?.snapshot,t=T.value?.to?.snapshot;return!e||!t?[]:Fe(e,t)}),Be=e=>e.startsWith(`+`)?`text-success`:e.startsWith(`-`)?`text-danger`:`text-warning`,Ve=g(()=>{let e=C.value?.code_hash||``;return w.value.filter(e=>e.status===`succeeded`&&e.code_hash).map(t=>({id:t.id,version:t.version,shortHash:(t.code_hash||``).slice(0,12),submittedAt:t.submitted_at,isActive:!!e&&t.code_hash===e}))}),He=e=>{if(!e)return``;let t=new Date(e);return`${t.toLocaleDateString(void 0,{month:`numeric`,day:`numeric`})} ${t.toLocaleTimeString(void 0,{hour:`numeric`,minute:`2-digit`})}`},Ue=e=>`v${e.version} · ${e.shortHash} · ${He(e.submittedAt)}${e.isActive?` · active`:``}`,G=({from:e,to:t})=>{ve.replace({query:{from:e??x.value,to:t??S.value}})},We=()=>{!x.value||!S.value||ve.replace({query:{from:S.value,to:x.value}})},Ge=async()=>{let e=await Pe(window.location.href);Se.value=e?`Copied!`:`Copy failed`,setTimeout(()=>{Se.value=`Copy link`},1500)},K=g(()=>{let e=C.value?.code_hash;return e&&w.value.find(t=>t.status===`succeeded`&&t.code_hash===e)?.id||null}),q=g(()=>w.value.find(e=>e.id===x.value)||null),Ke=g(()=>!!(q.value&&C.value?.code_hash&&q.value.code_hash===C.value.code_hash)),qe=()=>{if(!K.value||!C.value)return;let e=C.value.code_hash,t=w.value.filter(t=>t.status===`succeeded`&&t.code_hash&&t.code_hash!==e)[0];G({from:t?.id||x.value,to:K.value})},J=e=>w.value.find(t=>t.code_hash===e)||null,Je=e=>{let t=J(e);if(!t)return;let n=w.value.find(e=>e.id===S.value);n&&D.value.length&&!D.value.includes(n.code_hash)?G({from:t.id,to:K.value||S.value}):G({from:t.id,to:S.value})},Ye=async()=>{try{let e=((await se()).data?.functions||[]).find(e=>e.name===b.value);e&&(C.value=e);let t=await le(C.value.id,100);w.value=t.data.deployments||t.data||[]}catch{}},Xe=async()=>{if(A.value||!C.value||!q.value||Ke.value)return;let e=q.value,t=(e.code_hash||``).slice(0,12),n=`Code hash ${t}. The current version stays in history.`;try{let r=(await fe(e.id))?.data?.snapshot;if(r){let i=Fe(C.value,r);n=i.length?`Rolling back to v${e.version} (code ${t}) will also change:\n\n${i.join(` `)}\n\nSecrets keep their current values; they aren't part of the rollback.`:`Rolling back to v${e.version} (code ${t}). Settings and env are already identical, so only the code changes.`}}catch{}if(await Ce.ask({title:`Restore v${e.version}?`,message:n,confirmLabel:`Rollback`})){A.value=!0;try{let n=await pe(C.value.id,{deployment_id:e.id}),r=n?.data?.id||n?.data?.deployment_id;await Ye(),r&&G({from:x.value,to:r}),Ce.notify({title:`Rollback complete`,message:`v${e.version} (code ${t}) is now serving.`})}catch(e){let t=e.response?.data?.error?.code||``,n=e.response?.data?.error?.message||e.message||`Rollback failed`;t===`VERSION_GCD`?Ce.notify({title:`Version unavailable`,message:`This version has been garbage-collected and can no longer be restored.\n\n${n}`,danger:!0}):Ce.notify({title:`Rollback failed`,message:n,danger:!0})}finally{A.value=!1}}};return a(async()=>{L();try{let e=((await se()).data?.functions||[]).find(e=>e.name===b.value);if(!e)throw Error(`not found`);C.value=e}catch(e){E.value=e.response?.data?.error?.code||`FUNCTION_NOT_FOUND`,xe.value=e.response?.data?.error?.message||`Function not found.`;return}try{let e=await le(C.value.id,100);w.value=e.data.deployments||e.data||[]}catch{w.value=[]}await ze()}),i(k,async e=>{await ee(),z(),e&&Re()}),i([x,S],ze),i(()=>C.value?.runtime,()=>{V()}),t(()=>{B(),I?.()}),(t,r)=>{let i=d(`router-link`);return e(),c(`div`,dn,[s(`div`,fn,[s(`div`,null,[r[7]||=s(`h1`,{class:`text-xl font-semibold text-white tracking-tight text-balance`},` Compare versions `,-1),s(`p`,pn,[r[5]||=m(` Compare source and configuration for `,-1),u(i,{to:`/functions/${b.value}`,class:`text-white underline`},{default:n(()=>[m(v(b.value),1)]),_:1},8,[`to`]),r[6]||=m(`. `,-1)])]),s(`div`,mn,[s(`button`,{class:`text-xs text-foreground-muted hover:text-white hover:bg-surface-hover rounded-md flex items-center justify-center gap-1.5 px-2 py-1.5 transition-colors min-w-[6rem] focus:outline-none focus-visible:ring-1 focus-visible:ring-white`,title:`Copy share link`,"aria-label":`Copy share link`,onClick:Ge},[u(o(te),{class:`w-3.5 h-3.5`}),m(` `+v(Se.value),1)]),u(ue,{variant:`secondary`,onClick:r[0]||=e=>t.$router.push(`/functions/${b.value}/deployments`)},{default:n(()=>[u(o(Le),{class:`w-4 h-4 mr-2`}),r[8]||=m(` All deployments `,-1)]),_:1})])]),s(`div`,hn,[s(`div`,gn,[s(`div`,_n,[r[10]||=s(`label`,{for:`diff-from`,class:`block text-xs font-medium uppercase tracking-label text-foreground-muted`},[m(` From `),s(`span`,{class:`ml-1 text-foreground-muted/60 normal-case font-medium tracking-normal`},`(older)`)],-1),s(`select`,{id:`diff-from`,value:x.value,class:`w-full bg-background border border-border rounded-md px-3 py-2 text-sm font-mono text-foreground focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,onChange:r[1]||=e=>G({from:e.target.value})},[r[9]||=s(`option`,{value:``,disabled:``},` Pick a version `,-1),(e(!0),c(h,null,f(Ve.value,t=>(e(),c(`option`,{key:t.id,value:t.id,disabled:t.id===S.value},v(Ue(t)),9,yn))),128))],40,vn)]),s(`button`,{disabled:!x.value||!S.value,class:`justify-self-center sm:self-end mb-1 h-9 w-9 flex items-center justify-center rounded-md text-foreground-muted hover:text-white hover:bg-surface-hover transition-colors disabled:opacity-30 disabled:hover:bg-transparent disabled:hover:text-foreground-muted`,title:`Swap from / to`,"aria-label":`Swap from and to versions`,onClick:We},[u(o(Ie),{class:`w-4 h-4 rotate-90 sm:rotate-0 motion-reduce:transition-none transition-transform`})],8,bn),s(`div`,xn,[r[12]||=s(`label`,{for:`diff-to`,class:`block text-xs font-medium uppercase tracking-label text-foreground-muted`},[m(` To `),s(`span`,{class:`ml-1 text-foreground-muted/60 normal-case font-medium tracking-normal`},`(newer)`)],-1),s(`select`,{id:`diff-to`,value:S.value,class:`w-full bg-background border border-border rounded-md px-3 py-2 text-sm font-mono text-foreground focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,onChange:r[2]||=e=>G({to:e.target.value})},[r[11]||=s(`option`,{value:``,disabled:``},` Pick a version `,-1),(e(!0),c(h,null,f(Ve.value,t=>(e(),c(`option`,{key:t.id,value:t.id,disabled:t.id===x.value},v(Ue(t)),9,Cn))),128))],40,Sn)])]),C.value?(e(),c(`div`,wn,[K.value&&S.value!==K.value?(e(),c(`button`,{key:0,class:`inline-flex items-center gap-1.5 text-xs text-foreground-muted hover:text-white hover:bg-surface-hover rounded-md px-2.5 py-1.5 transition-colors`,title:`Compare previous version with currently active`,onClick:qe},[u(o(oe),{class:`w-3.5 h-3.5`}),r[13]||=m(` vs active `,-1)])):_(``,!0),q.value&&!Ke.value?(e(),p(ue,{key:1,variant:`primary`,size:`sm`,loading:A.value,disabled:A.value,onClick:Xe},{default:n(()=>[u(o(ae),{class:`w-3.5 h-3.5 mr-1.5`}),m(` Roll back to v`+v(q.value.version),1)]),_:1},8,[`loading`,`disabled`])):_(``,!0)])):_(``,!0)]),!x.value||!S.value?(e(),c(`p`,Tn,` Pick two different deployments above to compare. `)):x.value===S.value?(e(),c(`p`,En,` Same version on both sides. Pick a different deployment to see a diff. `)):E.value===`VERSION_GCD`?(e(),c(`div`,Dn,[r[14]||=s(`div`,{class:`text-warning font-medium`},` Source data for one or both versions has been garbage-collected. `,-1),r[15]||=s(`p`,{class:`text-warning/80 leading-body`},` The deployment row still exists, but the on-disk code tree was pruned by the version GC. Pick a version whose source is still archived: `,-1),D.value.length?(e(),c(`ul`,On,[(e(!0),c(h,null,f(D.value,t=>(e(),c(`li`,{key:t},[J(t)?(e(),c(`button`,{key:0,class:`text-left text-warning hover:text-white hover:bg-warning/15 rounded px-1.5 py-0.5 -mx-1.5 transition-colors`,title:`Use v${J(t).version} as the From side`,onClick:e=>Je(t)},` v`+v(J(t).version)+` · `+v(t.slice(0,12))+`… · `+v(He(J(t).submitted_at)),9,kn)):(e(),c(`span`,An,v(t.slice(0,12))+`…`,1))]))),128))])):(e(),c(`p`,jn,` No surviving on-disk versions for this function. Redeploy the original source to compare against the current code. `))])):E.value===`VERSION_NOT_FOUND`?(e(),c(`div`,Mn,[r[17]||=m(` One of the supplied deployment IDs doesn't exist. Pick from the dropdowns above, or go back to the `,-1),u(i,{to:`/functions/${b.value}/deployments`,class:`underline`},{default:n(()=>[...r[16]||=[m(` deployment history `,-1)]]),_:1},8,[`to`]),r[18]||=m(`. `,-1)])):E.value&&E.value!==`OK`?(e(),c(`div`,Nn,v(xe.value||`Failed to load diff.`),1)):T.value?(e(),c(h,{key:5},[W.value.length?(e(),c(`section`,Fn,[s(`header`,In,[s(`h2`,Ln,[u(o(ie),{class:`w-3.5 h-3.5`}),r[19]||=m(` Settings & env `,-1),s(`span`,Rn,v(W.value.length)+` change`+v(W.value.length===1?``:`s`),1)]),s(`button`,{class:`text-foreground-muted hover:text-white`,"aria-label":O.value?`Collapse settings diff`:`Expand settings diff`,onClick:r[3]||=e=>O.value=!O.value},[u(o(ce),{class:l([`w-4 h-4 transition-transform motion-reduce:transition-none`,{"rotate-180":O.value}])},null,8,[`class`])],8,zn)]),O.value?(e(),c(`div`,Bn,[s(`ul`,Vn,[(e(!0),c(h,null,f(W.value,(t,n)=>(e(),c(`li`,{key:n,class:l(Be(t))},v(t),3))),128))]),r[20]||=s(`p`,{class:`text-[11px] text-foreground-muted/70 mt-3`},` Secrets aren't tracked per-version; they always reflect the current values. `,-1)])):_(``,!0)])):(e(),c(`p`,Pn,` Settings and env are identical between these versions. Secrets aren't tracked per-version. `)),H.value?(e(),c(`section`,Hn,[s(`header`,Un,[s(`h2`,Wn,[u(o(re),{class:`w-3.5 h-3.5 text-foreground-muted`}),s(`span`,Gn,v(H.value.path),1),H.value.added?(e(),c(`span`,Kn,`added`)):H.value.removed?(e(),c(`span`,qn,`removed`)):_(``,!0)]),s(`button`,{class:`text-[10px] font-medium uppercase tracking-label text-foreground-muted hover:text-white hover:bg-surface-hover rounded px-2 py-1 transition-colors`,title:N.value?`Switch to unified inline view`:`Switch to side-by-side view`,"aria-pressed":N.value,onClick:we},v(N.value?`side-by-side`:`unified`),9,Jn)]),s(`div`,{ref_key:`codeMountRef`,ref:Ee,class:`orva-merge`},null,512)])):_(``,!0),U.value&&(U.value.before!==U.value.after||U.value.added||U.value.removed)?(e(),c(`section`,Yn,[s(`header`,Xn,[s(`h2`,Zn,[u(o(ne),{class:`w-3.5 h-3.5`}),s(`span`,Qn,v(U.value.path),1),r[21]||=s(`span`,{class:`text-[10px] px-1.5 py-0.5 rounded bg-warning/15 text-warning border border-warning/30 font-medium tracking-normal normal-case`},` changed `,-1)]),s(`button`,{class:`text-foreground-muted hover:text-white`,"aria-label":k.value?`Collapse manifest diff`:`Expand manifest diff`,onClick:r[4]||=e=>k.value=!k.value},[u(o(ce),{class:l([`w-4 h-4 transition-transform motion-reduce:transition-none`,{"rotate-180":k.value}])},null,8,[`class`])],8,$n)]),k.value?(e(),c(`div`,{key:0,ref_key:`manifestMountRef`,ref:De,class:`orva-merge`},null,512)):_(``,!0)])):_(``,!0),!H.value&&!U.value?(e(),c(`p`,er,` No code changes between these versions. `)):_(``,!0)],64)):_(``,!0)])}}};export{nr as default}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/FunctionsList-ByM7ruDh.js b/backend/internal/server/ui_dist/assets/FunctionsList-CPOzZCfJ.js similarity index 96% rename from backend/internal/server/ui_dist/assets/FunctionsList-ByM7ruDh.js rename to backend/internal/server/ui_dist/assets/FunctionsList-CPOzZCfJ.js index 521688ba..d5df68c5 100644 --- a/backend/internal/server/ui_dist/assets/FunctionsList-ByM7ruDh.js +++ b/backend/internal/server/ui_dist/assets/FunctionsList-CPOzZCfJ.js @@ -1,3 +1,3 @@ -import{D as e,E as t,F as n,G as r,I as ee,S as te,T as ne,Z as i,c as a,d as o,gt as re,h as s,k as c,l as ie,m as l,r as u,s as d,u as f,vt as p,w as ae}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as oe}from"./check-DQm9OMOd.js";import{t as se}from"./copy-Bt6Rv0BQ.js";import{t as m}from"./globe-C84kKXm0.js";import{t as h}from"./lock-QGBYN3Zu.js";import{t as g}from"./pencil-BuAbR0ud.js";import{t as ce}from"./refresh-cw-Bn_tp4gH.js";import{t as _}from"./trash-2-Bj4AFUfw.js";import{t as v}from"./client-BF51V3uE.js";import{B as le,Et as ue,Lt as de,Mt as fe,St as y,_t as b,gt as pe,mt as me,r as he,xt as ge}from"./index-C11HmDXN.js";import{t as _e}from"./clipboard-D_9N0yai.js";import{t as x}from"./IconButton-CsCZOqWo.js";var ve={class:`space-y-6`},ye={class:`flex items-start justify-between gap-4 flex-wrap`},be={key:0,class:`space-y-3`},xe={class:`flex items-center gap-2 flex-wrap`},Se={class:`relative flex-1 min-w-0 sm:min-w-[280px] max-w-full sm:max-w-[440px]`},S={class:`text-[11px] text-foreground-muted shrink-0 tabular-nums`},C={class:`bg-background border border-border rounded-lg overflow-x-auto`},w={class:`sm:hidden divide-y divide-border`},T={class:`flex items-start justify-between gap-2`},E={class:`min-w-0 flex-1`},D={class:`flex items-center gap-1.5 flex-wrap`},O={class:`font-medium text-white truncate`},k={key:0,class:`inline-flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[10px] bg-warning-tint text-warning-fg border border-warning-ring`},A={key:1,class:`inline-flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[10px] bg-info-tint text-info-fg border border-info-ring`},j={key:0,class:`mt-1 text-xs text-foreground-muted line-clamp-2`},Ce={class:`mt-1.5 flex items-center gap-3 text-[11px] text-foreground-muted font-mono`},we={class:`flex items-center gap-1 shrink-0`},Te={key:0,class:`px-6 py-8 text-center text-sm text-foreground-muted space-y-3`},Ee={class:`hidden sm:table w-full text-sm text-left`},De={class:`text-xs text-foreground-muted uppercase bg-surface border-b border-border`},Oe={class:`px-4 py-3 w-8`},ke=[`checked`,`.indeterminate`],Ae={class:`divide-y divide-border`},je={class:`px-4 py-3 align-middle`},Me=[`checked`,`onChange`],Ne={class:`px-4 py-3 font-medium text-white`},Pe={class:`flex items-center gap-2 flex-wrap`},Fe={key:0,class:`inline-flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[10px] bg-warning-tint text-warning-fg border border-warning-ring`,title:`Outbound network enabled`},Ie=[`title`],Le=[`title`],Re=[`title`],ze={class:`px-4 py-3 text-foreground hidden sm:table-cell`},Be={class:`inline-flex items-center px-2 py-0.5 rounded text-xs border border-border bg-background text-foreground-muted font-mono`},M={class:`px-4 py-3 text-foreground-muted font-mono text-xs hidden lg:table-cell`},Ve={class:`px-4 py-3 hidden md:table-cell align-middle`},He={class:`flex items-center gap-2 min-w-0`},Ue=[`title`],We={class:`px-4 py-3 text-foreground-muted hidden xl:table-cell`},Ge={class:`px-4 py-3 text-right`},Ke={class:`inline-flex items-center gap-1`},qe={key:0},Je={colspan:`7`,class:`px-6 py-8 text-center text-foreground-muted`},Ye={class:`space-y-3`},Xe={key:0,class:`flex justify-center border-t border-border py-3 bg-surface/30`},Ze=[`disabled`],Qe={key:1,class:`bg-background border border-border rounded-lg p-8 text-center space-y-4`},$e={key:0,class:`fixed bottom-[calc(1rem+env(safe-area-inset-bottom,0px))] left-1/2 -translate-x-1/2 z-30 flex items-center gap-3 bg-background border border-border shadow-lg rounded-full pl-4 pr-2 py-2`},et={class:`text-xs text-white`},tt=25,nt={__name:`FunctionsList`,setup(nt){let N=pe(),P=me(),F=r([]),I=r(0),L=r(!1),R=r(``),z=r(``),B=r(``),V=r(!1),H=r(new Set),U=d(()=>{let e=R.value.trim().toLowerCase();return e?F.value.filter(t=>t.name?.toLowerCase().includes(e)||t.description?.toLowerCase().includes(e)||t.runtime?.toLowerCase().includes(e)||t.id?.toLowerCase().includes(e)):F.value}),rt=d(()=>F.value.lengthU.value.length>0&&U.value.every(e=>H.value.has(e.id))),it=d(()=>U.value.some(e=>H.value.has(e.id))),at=e=>{let t=new Set(H.value);t.has(e)?t.delete(e):t.add(e),H.value=t},ot=()=>{if(W.value)H.value=new Set;else{let e=new Set(H.value);U.value.forEach(t=>e.add(t.id)),H.value=e}},G=e=>`${window.location.origin}/fn/${e.id}`,st=async e=>{await _e(G(e))?(z.value=e.id,setTimeout(()=>{z.value===e.id&&(z.value=``)},1500)):N.notify({title:`Copy failed`,message:`Could not copy to clipboard. URL: +import{D as e,E as t,F as n,G as r,I as ee,S as te,T as ne,Z as i,c as a,d as o,gt as re,h as s,k as c,l as ie,m as l,r as u,s as d,u as f,vt as p,w as ae}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as oe}from"./check-BNre7JFR.js";import{t as se}from"./copy-BqdwwcxC.js";import{t as m}from"./globe-BnBar2lO.js";import{t as h}from"./lock-CttMBTH5.js";import{t as g}from"./pencil-Do2I7soU.js";import{t as ce}from"./refresh-cw-Cn8qkf-v.js";import{t as _}from"./trash-2-DaeYqnW_.js";import{t as v}from"./client-BF51V3uE.js";import{B as le,Et as ue,Lt as de,Mt as fe,St as y,_t as b,gt as pe,mt as me,r as he,xt as ge}from"./index-DTqMKlE1.js";import{t as _e}from"./clipboard-D_9N0yai.js";import{t as x}from"./IconButton-CsCZOqWo.js";var ve={class:`space-y-6`},ye={class:`flex items-start justify-between gap-4 flex-wrap`},be={key:0,class:`space-y-3`},xe={class:`flex items-center gap-2 flex-wrap`},Se={class:`relative flex-1 min-w-0 sm:min-w-[280px] max-w-full sm:max-w-[440px]`},S={class:`text-[11px] text-foreground-muted shrink-0 tabular-nums`},C={class:`bg-background border border-border rounded-lg overflow-x-auto`},w={class:`sm:hidden divide-y divide-border`},T={class:`flex items-start justify-between gap-2`},E={class:`min-w-0 flex-1`},D={class:`flex items-center gap-1.5 flex-wrap`},O={class:`font-medium text-white truncate`},k={key:0,class:`inline-flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[10px] bg-warning-tint text-warning-fg border border-warning-ring`},A={key:1,class:`inline-flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[10px] bg-info-tint text-info-fg border border-info-ring`},j={key:0,class:`mt-1 text-xs text-foreground-muted line-clamp-2`},Ce={class:`mt-1.5 flex items-center gap-3 text-[11px] text-foreground-muted font-mono`},we={class:`flex items-center gap-1 shrink-0`},Te={key:0,class:`px-6 py-8 text-center text-sm text-foreground-muted space-y-3`},Ee={class:`hidden sm:table w-full text-sm text-left`},De={class:`text-xs text-foreground-muted uppercase bg-surface border-b border-border`},Oe={class:`px-4 py-3 w-8`},ke=[`checked`,`.indeterminate`],Ae={class:`divide-y divide-border`},je={class:`px-4 py-3 align-middle`},Me=[`checked`,`onChange`],Ne={class:`px-4 py-3 font-medium text-white`},Pe={class:`flex items-center gap-2 flex-wrap`},Fe={key:0,class:`inline-flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[10px] bg-warning-tint text-warning-fg border border-warning-ring`,title:`Outbound network enabled`},Ie=[`title`],Le=[`title`],Re=[`title`],ze={class:`px-4 py-3 text-foreground hidden sm:table-cell`},Be={class:`inline-flex items-center px-2 py-0.5 rounded text-xs border border-border bg-background text-foreground-muted font-mono`},M={class:`px-4 py-3 text-foreground-muted font-mono text-xs hidden lg:table-cell`},Ve={class:`px-4 py-3 hidden md:table-cell align-middle`},He={class:`flex items-center gap-2 min-w-0`},Ue=[`title`],We={class:`px-4 py-3 text-foreground-muted hidden xl:table-cell`},Ge={class:`px-4 py-3 text-right`},Ke={class:`inline-flex items-center gap-1`},qe={key:0},Je={colspan:`7`,class:`px-6 py-8 text-center text-foreground-muted`},Ye={class:`space-y-3`},Xe={key:0,class:`flex justify-center border-t border-border py-3 bg-surface/30`},Ze=[`disabled`],Qe={key:1,class:`bg-background border border-border rounded-lg p-8 text-center space-y-4`},$e={key:0,class:`fixed bottom-[calc(1rem+env(safe-area-inset-bottom,0px))] left-1/2 -translate-x-1/2 z-30 flex items-center gap-3 bg-background border border-border shadow-lg rounded-full pl-4 pr-2 py-2`},et={class:`text-xs text-white`},tt=25,nt={__name:`FunctionsList`,setup(nt){let N=pe(),P=me(),F=r([]),I=r(0),L=r(!1),R=r(``),z=r(``),B=r(``),V=r(!1),H=r(new Set),U=d(()=>{let e=R.value.trim().toLowerCase();return e?F.value.filter(t=>t.name?.toLowerCase().includes(e)||t.description?.toLowerCase().includes(e)||t.runtime?.toLowerCase().includes(e)||t.id?.toLowerCase().includes(e)):F.value}),rt=d(()=>F.value.lengthU.value.length>0&&U.value.every(e=>H.value.has(e.id))),it=d(()=>U.value.some(e=>H.value.has(e.id))),at=e=>{let t=new Set(H.value);t.has(e)?t.delete(e):t.add(e),H.value=t},ot=()=>{if(W.value)H.value=new Set;else{let e=new Set(H.value);U.value.forEach(t=>e.add(t.id)),H.value=e}},G=e=>`${window.location.origin}/fn/${e.id}`,st=async e=>{await _e(G(e))?(z.value=e.id,setTimeout(()=>{z.value===e.id&&(z.value=``)},1500)):N.notify({title:`Copy failed`,message:`Could not copy to clipboard. URL: `+G(e)})},K=async e=>{L.value=!0;try{let t=await le({limit:tt,offset:e}),n=t.data.functions||[];I.value=t.data.total??n.length,e===0?F.value=n:F.value=[...F.value,...n]}catch(e){console.error(e)}finally{L.value=!1}},ct=()=>K(F.value.length),q=()=>K(0),J=async e=>{if(await N.ask({title:`Delete "${e.name}"?`,message:`This is irreversible. Code, deployments, secrets, and routes for this function are removed.`,confirmLabel:`Delete`,danger:!0})){B.value=e.id;try{await v.delete(`/functions/${e.id}`),await q(),H.value.delete(e.id),H.value=new Set(H.value)}catch(e){let t=e.response?.data?.error?.message||e.message||`Delete failed`;N.notify({title:`Delete failed`,message:t,danger:!0})}finally{B.value=``}}},lt=async()=>{let e=H.value.size;if(!await N.ask({title:`Delete ${e} ${e===1?`function`:`functions`}?`,message:`Each one is irreversible. Code, deployments, secrets, and routes are removed.`,confirmLabel:`Delete ${e}`,danger:!0}))return;V.value=!0;let t=[...H.value],n=0;try{for(let e of t)try{await v.delete(`/functions/${e}`)}catch{n++}H.value=new Set,await q(),n&&N.notify({title:`Some deletes failed`,message:`${n} of ${t.length} could not be deleted.`,danger:!0})}finally{V.value=!1}},Y=he(),X=null,Z=()=>{X||=setTimeout(()=>{X=null,K(0)},300)},Q=null,$=null;return ne(()=>{K(0),Q=Y.subscribe(`function`,Z),$=Y.subscribe(`deployment`,Z)}),t(()=>{Q&&=(Q(),null),$&&=($(),null),X&&=(clearTimeout(X),null)}),te(()=>K(0)),ae(()=>{X&&=(clearTimeout(X),null)}),(t,r)=>(e(),o(`div`,ve,[a(`div`,ye,[r[7]||=a(`div`,null,[a(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` Functions `),a(`p`,{class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},` Deployed functions and invoke endpoints. `)],-1),s(b,{onClick:r[0]||=e=>i(P).push(`/functions/new`)},{default:n(()=>[s(i(y),{class:`w-4 h-4`}),r[6]||=l(` New Function `,-1)]),_:1})]),F.value.length>0||L.value?(e(),o(`div`,be,[a(`div`,xe,[a(`div`,Se,[s(i(ge),{class:`w-3.5 h-3.5 absolute left-2.5 top-1/2 -translate-y-1/2 text-foreground-muted/60 pointer-events-none`}),ee(a(`input`,{"onUpdate:modelValue":r[1]||=e=>R.value=e,placeholder:`Search by name, runtime, or function id…`,class:`w-full bg-background border border-border rounded-md pl-8 pr-3 py-1.5 text-base sm:text-xs text-foreground placeholder-foreground-muted/60 focus:outline-none focus:ring-1 focus:ring-white focus:border-white`},null,512),[[de,R.value]])]),a(`span`,S,p(U.value.length)+` of `+p(F.value.length),1)]),a(`div`,C,[a(`ul`,w,[(e(!0),o(u,null,c(U.value,t=>(e(),o(`li`,{key:t.id,class:`px-4 py-3 active:bg-surface-hover/50 transition-colors`},[a(`div`,T,[a(`div`,E,[a(`div`,D,[a(`span`,O,p(t.name),1),t.network_mode===`egress`?(e(),o(`span`,k,[s(i(m),{class:`w-3 h-3`}),r[8]||=l(` egress `,-1)])):f(``,!0),t.auth_mode&&t.auth_mode!==`none`?(e(),o(`span`,A,[s(i(h),{class:`w-3 h-3`}),l(` `+p(t.auth_mode===`platform_key`?`key`:`signed`),1)])):f(``,!0)]),t.description?(e(),o(`p`,j,p(t.description),1)):f(``,!0),a(`div`,Ce,[a(`span`,null,p(t.runtime),1),a(`span`,null,p(t.cpus)+` CPU / `+p(t.memory_mb)+`MB`,1)])]),a(`div`,we,[s(x,{icon:i(g),title:`Edit function`,onClick:e=>i(P).push(`/functions/`+t.name)},null,8,[`icon`,`onClick`]),s(x,{icon:i(_),variant:`danger`,title:`Delete function`,disabled:B.value===t.id,onClick:e=>J(t)},null,8,[`icon`,`disabled`,`onClick`])])])]))),128)),!U.value.length&&!L.value&&R.value?(e(),o(`li`,Te,[a(`div`,null,`No matches for "`+p(R.value)+`".`,1),a(`button`,{class:`text-xs text-foreground hover:text-white underline underline-offset-2`,onClick:r[2]||=e=>R.value=``},` Clear search `)])):f(``,!0)]),a(`table`,Ee,[a(`thead`,De,[a(`tr`,null,[a(`th`,Oe,[a(`input`,{type:`checkbox`,checked:W.value,".indeterminate":it.value&&!W.value,class:`w-3.5 h-3.5 rounded border-border bg-background focus:outline-none focus:ring-1 focus:ring-white`,onChange:ot},null,40,ke)]),r[9]||=a(`th`,{class:`px-4 py-3 font-medium`},` Name `,-1),r[10]||=a(`th`,{class:`px-4 py-3 font-medium hidden sm:table-cell`},` Runtime `,-1),r[11]||=a(`th`,{class:`px-4 py-3 font-medium hidden lg:table-cell`},` Resources `,-1),r[12]||=a(`th`,{class:`px-4 py-3 font-medium hidden md:table-cell`},` Function ID `,-1),r[13]||=a(`th`,{class:`px-4 py-3 font-medium hidden xl:table-cell`},` Last Update `,-1),r[14]||=a(`th`,{class:`px-4 py-3 font-medium text-right`},` Actions `,-1)])]),a(`tbody`,Ae,[(e(!0),o(u,null,c(U.value,t=>(e(),o(`tr`,{key:t.id,class:re([`hover:bg-surface/50 transition-colors`,{"bg-surface/30":H.value.has(t.id)}])},[a(`td`,je,[a(`input`,{checked:H.value.has(t.id),type:`checkbox`,class:`w-3.5 h-3.5 rounded border-border bg-background focus:outline-none focus:ring-1 focus:ring-white`,onChange:e=>at(t.id)},null,40,Me)]),a(`td`,Ne,[a(`div`,Pe,[a(`span`,null,p(t.name),1),t.network_mode===`egress`?(e(),o(`span`,Fe,[s(i(m),{class:`w-3 h-3`}),r[15]||=l(` egress `,-1)])):f(``,!0),t.auth_mode&&t.auth_mode!==`none`?(e(),o(`span`,{key:1,class:`inline-flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[10px] bg-info-tint text-info-fg border border-info-ring`,title:t.auth_mode===`platform_key`?`Requires Orva API key`:`Requires HMAC signature`},[s(i(h),{class:`w-3 h-3`}),l(` `+p(t.auth_mode===`platform_key`?`key`:`signed`),1)],8,Ie)):f(``,!0),t.rate_limit_per_min>0?(e(),o(`span`,{key:2,class:`inline-flex items-center gap-1 px-1.5 py-0.5 rounded-full text-[10px] bg-primary/15 text-primary border border-primary/30 tabular-nums`,title:`Rate limit: ${t.rate_limit_per_min}/min per IP`},[s(i(ue),{class:`w-3 h-3`}),l(` `+p(t.rate_limit_per_min)+`/m `,1)],8,Le)):f(``,!0)]),t.description?(e(),o(`p`,{key:0,class:`mt-1 text-xs font-normal text-foreground-muted line-clamp-2`,title:t.description},p(t.description),9,Re)):f(``,!0)]),a(`td`,ze,[a(`span`,Be,p(t.runtime),1)]),a(`td`,M,p(t.cpus)+` CPU / `+p(t.memory_mb)+`MB `,1),a(`td`,Ve,[a(`div`,He,[a(`code`,{class:`text-xs font-mono text-foreground-muted bg-surface px-2 py-1 rounded border border-border truncate min-w-0 max-w-[14ch]`,title:t.id},p(t.id),9,Ue),s(x,{icon:z.value===t.id?i(oe):i(se),title:z.value===t.id?`Copied!`:`Copy invoke URL`,variant:z.value===t.id?`primary`:`default`,onClick:e=>st(t)},null,8,[`icon`,`title`,`variant`,`onClick`])])]),a(`td`,We,p(new Date(t.updated_at).toLocaleDateString()),1),a(`td`,Ge,[a(`div`,Ke,[s(x,{icon:i(g),title:`Edit function`,onClick:e=>i(P).push(`/functions/`+t.name)},null,8,[`icon`,`onClick`]),s(x,{icon:i(_),variant:`danger`,title:`Delete function`,disabled:B.value===t.id,onClick:e=>J(t)},null,8,[`icon`,`disabled`,`onClick`])])])],2))),128)),!U.value.length&&!L.value&&R.value?(e(),o(`tr`,qe,[a(`td`,Je,[a(`div`,Ye,[a(`div`,null,`No matches for "`+p(R.value)+`".`,1),a(`button`,{class:`text-xs text-foreground hover:text-white underline underline-offset-2`,onClick:r[3]||=e=>R.value=``},` Clear search `)])])])):f(``,!0)])]),rt.value?(e(),o(`div`,Xe,[a(`button`,{class:`text-xs text-foreground-muted hover:text-white transition-colors flex items-center gap-1.5`,disabled:L.value,onClick:ct},[L.value?(e(),ie(i(ce),{key:0,class:`w-3 h-3 animate-spin`})):f(``,!0),l(` `+p(L.value?`Loading…`:`Load more (${I.value-F.value.length} remaining)`),1)],8,Ze)])):f(``,!0)])])):f(``,!0),!L.value&&F.value.length===0?(e(),o(`div`,Qe,[r[17]||=a(`div`,{class:`space-y-1.5`},[a(`div`,{class:`text-sm text-white`},` No functions deployed yet `),a(`div`,{class:`text-xs text-foreground-muted max-w-prose mx-auto leading-body`},` Choose a runtime and deploy your first handler. `)],-1),a(`div`,null,[s(b,{onClick:r[4]||=e=>i(P).push(`/functions/new`)},{default:n(()=>[s(i(y),{class:`w-4 h-4`}),r[16]||=l(` Deploy your first function `,-1)]),_:1})])])):f(``,!0),s(fe,{name:`fade`},{default:n(()=>[H.value.size?(e(),o(`div`,$e,[a(`span`,et,p(H.value.size)+` selected `,1),r[18]||=a(`span`,{class:`w-px h-4 bg-border`},null,-1),a(`button`,{class:`text-xs text-foreground-muted hover:text-white transition-colors px-2 py-1`,onClick:r[5]||=e=>H.value=new Set},` Clear `),s(b,{variant:`danger`,size:`sm`,class:`!rounded-full px-4`,loading:V.value,onClick:lt},{default:n(()=>[s(i(_),{class:`w-3.5 h-3.5`}),l(` Delete `+p(H.value.size),1)]),_:1},8,[`loading`])])):f(``,!0)]),_:1})]))}};export{nt as default}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/InboundWebhooks-C_jOjeYV.js b/backend/internal/server/ui_dist/assets/InboundWebhooks-CFOyt9ev.js similarity index 98% rename from backend/internal/server/ui_dist/assets/InboundWebhooks-C_jOjeYV.js rename to backend/internal/server/ui_dist/assets/InboundWebhooks-CFOyt9ev.js index 50b1bab4..757b7f1b 100644 --- a/backend/internal/server/ui_dist/assets/InboundWebhooks-C_jOjeYV.js +++ b/backend/internal/server/ui_dist/assets/InboundWebhooks-CFOyt9ev.js @@ -1,4 +1,4 @@ -import{D as e,F as t,G as n,I as r,T as i,U as a,Z as o,c as s,d as c,gt as l,h as u,j as ee,k as d,m as f,r as p,s as m,u as h,vt as g}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as te}from"./refresh-cw-Bn_tp4gH.js";import{t as _}from"./trash-2-Bj4AFUfw.js";import{It as ne,Lt as v,St as re,V as ie,_t as y,gt as ae,h as oe,jt as se,l as ce,pt as le}from"./index-C11HmDXN.js";import{t as b}from"./Drawer-ZggviMzu.js";import{t as x}from"./IconButton-CsCZOqWo.js";var S=se(`send`,[[`path`,{d:`M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z`,key:`1ffxy3`}],[`path`,{d:`m21.854 2.147-10.94 10.939`,key:`12cjpa`}]]),ue={class:`space-y-6`},de={class:`flex items-start justify-between gap-4`},fe={class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},pe={class:`flex items-center gap-2`},me={class:`text-xs text-foreground-muted`},he={key:0,class:`rounded-lg border border-amber-500/40 bg-amber-500/10 p-4 space-y-3`},ge={class:`flex items-center justify-between gap-4`},_e={class:`text-xs space-y-2`},ve={class:`ml-2 font-mono text-white break-all`},ye={class:`ml-2 font-mono text-white break-all`},C={class:`mt-1 bg-background border border-border rounded p-3 text-[11px] font-mono text-white whitespace-pre-wrap overflow-x-auto`},w={class:`bg-background border border-border rounded-lg overflow-x-auto`},T={class:`sm:hidden divide-y divide-border`},E={class:`flex items-start justify-between gap-2`},D={class:`min-w-0 flex-1`},O={class:`flex items-center gap-2 flex-wrap`},k={class:`font-medium text-white truncate`},A={class:`mt-1 text-[11px] text-foreground-muted font-mono break-all`},j={class:`mt-1 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-[11px] text-foreground-muted`},M={class:`font-mono`},N={class:`flex items-center gap-1 shrink-0`},P={key:0,class:`px-6 py-12 text-center text-sm text-foreground-muted`},be={class:`hidden sm:table w-full text-sm text-left`},xe={class:`divide-y divide-border`},Se={class:`px-4 py-3 font-medium text-white`},Ce={class:`flex flex-col`},we={class:`text-xs text-foreground-muted font-mono`},Te={class:`px-4 py-3 font-mono text-xs text-foreground-muted hidden md:table-cell`},Ee={class:`break-all`},De={class:`px-4 py-3 hidden sm:table-cell`},Oe={class:`inline-flex items-center px-2 py-0.5 rounded text-xs border bg-surface text-foreground-muted border-border font-mono`},ke={class:`px-4 py-3 font-mono text-xs text-foreground-muted hidden lg:table-cell`},Ae={class:`px-4 py-3 hidden md:table-cell`},je={class:`px-4 py-3 text-foreground-muted text-xs hidden lg:table-cell`},Me={class:`px-4 py-3 text-right`},Ne={class:`inline-flex items-center gap-1`},Pe={key:0},F={class:`p-5 space-y-5 text-sm`},Fe={key:0,class:`text-xs text-danger-fg`},Ie={class:`flex items-center justify-end gap-2`},Le={key:0,class:`p-5 space-y-5 text-sm`},Re={class:`text-xs text-foreground-muted`},ze={key:0,class:`text-xs text-danger-fg`},Be={key:1,class:`space-y-1`},Ve={class:`text-xs uppercase tracking-wider text-foreground-muted`},He={class:`bg-background border border-border rounded p-3 text-[11px] font-mono text-white whitespace-pre-wrap overflow-x-auto`},Ue={class:`flex items-center justify-end gap-2`},I={__name:`InboundWebhooks`,setup(se){let I=le(),L=ae(),R=m(()=>I.params.name),z=n(``),B=n([]),V=n(!1),H=n(!1),U=n(!1),W=n(null),G=m(()=>window.location.origin),K=a({open:!1,name:``,format:`hmac_sha256_hex`,error:``}),q=a({open:!1,row:null,secret:``,body:`{"hello":"orva"}`,error:``,response:null}),J=e=>e?new Date(e).toLocaleString():`—`,Y=async()=>{V.value=!0;try{z.value||=R.value;let e=await ie(z.value);B.value=e.data?.inbound_webhooks||[]}catch(e){console.error(`load inbound webhooks failed`,e),L.notify({title:`Failed to load inbound webhooks`,message:e?.response?.data?.error?.message||e.message,danger:!0})}finally{V.value=!1}},We=()=>{K.name=``,K.format=`hmac_sha256_hex`,K.error=``,K.open=!0},Ge=async()=>{let e=K.name.trim();if(!e){K.error=`Name is required`;return}H.value=!0,K.error=``;try{let t=await ce(z.value,{name:e,signature_format:K.format});W.value={...t.data.inbound_webhook,secret:t.data.secret,trigger_url:t.data.trigger_url},K.open=!1,await Y()}catch(e){K.error=e?.response?.data?.error?.message||`Create failed`}finally{H.value=!1}},X=async e=>{if(await L.ask({title:`Delete inbound webhook?`,message:`Trigger "${e.name}" (${e.id}) will stop accepting calls immediately. This cannot be undone.`,confirmLabel:`Delete`,danger:!0}))try{await oe(z.value,e.id),await Y()}catch(e){L.notify({title:`Delete failed`,message:e?.response?.data?.error?.message||e.message,danger:!0})}},Z=e=>{q.row=e,q.secret=``,q.body=`{"hello":"orva"}`,q.error=``,q.response=null,q.open=!0},Q=async(e,t)=>{let n=new TextEncoder,r=await crypto.subtle.importKey(`raw`,n.encode(e),{name:`HMAC`,hash:`SHA-256`},!1,[`sign`]),i=await crypto.subtle.sign(`HMAC`,r,n.encode(t));return[...new Uint8Array(i)].map(e=>e.toString(16).padStart(2,`0`)).join(``)},Ke=async()=>{q.error=``,q.response=null,U.value=!0;try{let e=q.row.signature_format,t;if(e===`hmac_sha256_hex`)t=await Q(q.secret.trim(),q.body);else if(e===`github`)t=`sha256=`+await Q(q.secret.trim(),q.body);else{q.error=`Browser test only signs hmac_sha256_hex and github. For ${e}, use the CLI or curl with openssl.`;return}let n=G.value+`/webhook/`+q.row.id,r=await fetch(n,{method:`POST`,headers:{"Content-Type":`application/json`,[q.row.signature_header]:t},body:q.body}),i=await r.text();q.response={status:r.status,body:i}}catch(e){q.error=e.message||`Test failed`}finally{U.value=!1}},qe=e=>{let t=G.value+e.trigger_url,n=e.signature_format;return n===`github`?[`BODY='{"hello":"orva"}'`,`SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "${e.secret}" | sed 's/^.* //')`,`curl -X POST "${t}" \\`,` -H "Content-Type: application/json" \\`,` -H "${e.signature_header}: sha256=$SIG" \\`,` -d "$BODY"`].join(` +import{D as e,F as t,G as n,I as r,T as i,U as a,Z as o,c as s,d as c,gt as l,h as u,j as ee,k as d,m as f,r as p,s as m,u as h,vt as g}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as te}from"./refresh-cw-Cn8qkf-v.js";import{t as _}from"./trash-2-DaeYqnW_.js";import{It as ne,Lt as v,St as re,V as ie,_t as y,gt as ae,h as oe,jt as se,l as ce,pt as le}from"./index-DTqMKlE1.js";import{t as b}from"./Drawer-B98TBytl.js";import{t as x}from"./IconButton-CsCZOqWo.js";var S=se(`send`,[[`path`,{d:`M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z`,key:`1ffxy3`}],[`path`,{d:`m21.854 2.147-10.94 10.939`,key:`12cjpa`}]]),ue={class:`space-y-6`},de={class:`flex items-start justify-between gap-4`},fe={class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},pe={class:`flex items-center gap-2`},me={class:`text-xs text-foreground-muted`},he={key:0,class:`rounded-lg border border-amber-500/40 bg-amber-500/10 p-4 space-y-3`},ge={class:`flex items-center justify-between gap-4`},_e={class:`text-xs space-y-2`},ve={class:`ml-2 font-mono text-white break-all`},ye={class:`ml-2 font-mono text-white break-all`},C={class:`mt-1 bg-background border border-border rounded p-3 text-[11px] font-mono text-white whitespace-pre-wrap overflow-x-auto`},w={class:`bg-background border border-border rounded-lg overflow-x-auto`},T={class:`sm:hidden divide-y divide-border`},E={class:`flex items-start justify-between gap-2`},D={class:`min-w-0 flex-1`},O={class:`flex items-center gap-2 flex-wrap`},k={class:`font-medium text-white truncate`},A={class:`mt-1 text-[11px] text-foreground-muted font-mono break-all`},j={class:`mt-1 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-[11px] text-foreground-muted`},M={class:`font-mono`},N={class:`flex items-center gap-1 shrink-0`},P={key:0,class:`px-6 py-12 text-center text-sm text-foreground-muted`},be={class:`hidden sm:table w-full text-sm text-left`},xe={class:`divide-y divide-border`},Se={class:`px-4 py-3 font-medium text-white`},Ce={class:`flex flex-col`},we={class:`text-xs text-foreground-muted font-mono`},Te={class:`px-4 py-3 font-mono text-xs text-foreground-muted hidden md:table-cell`},Ee={class:`break-all`},De={class:`px-4 py-3 hidden sm:table-cell`},Oe={class:`inline-flex items-center px-2 py-0.5 rounded text-xs border bg-surface text-foreground-muted border-border font-mono`},ke={class:`px-4 py-3 font-mono text-xs text-foreground-muted hidden lg:table-cell`},Ae={class:`px-4 py-3 hidden md:table-cell`},je={class:`px-4 py-3 text-foreground-muted text-xs hidden lg:table-cell`},Me={class:`px-4 py-3 text-right`},Ne={class:`inline-flex items-center gap-1`},Pe={key:0},F={class:`p-5 space-y-5 text-sm`},Fe={key:0,class:`text-xs text-danger-fg`},Ie={class:`flex items-center justify-end gap-2`},Le={key:0,class:`p-5 space-y-5 text-sm`},Re={class:`text-xs text-foreground-muted`},ze={key:0,class:`text-xs text-danger-fg`},Be={key:1,class:`space-y-1`},Ve={class:`text-xs uppercase tracking-wider text-foreground-muted`},He={class:`bg-background border border-border rounded p-3 text-[11px] font-mono text-white whitespace-pre-wrap overflow-x-auto`},Ue={class:`flex items-center justify-end gap-2`},I={__name:`InboundWebhooks`,setup(se){let I=le(),L=ae(),R=m(()=>I.params.name),z=n(``),B=n([]),V=n(!1),H=n(!1),U=n(!1),W=n(null),G=m(()=>window.location.origin),K=a({open:!1,name:``,format:`hmac_sha256_hex`,error:``}),q=a({open:!1,row:null,secret:``,body:`{"hello":"orva"}`,error:``,response:null}),J=e=>e?new Date(e).toLocaleString():`—`,Y=async()=>{V.value=!0;try{z.value||=R.value;let e=await ie(z.value);B.value=e.data?.inbound_webhooks||[]}catch(e){console.error(`load inbound webhooks failed`,e),L.notify({title:`Failed to load inbound webhooks`,message:e?.response?.data?.error?.message||e.message,danger:!0})}finally{V.value=!1}},We=()=>{K.name=``,K.format=`hmac_sha256_hex`,K.error=``,K.open=!0},Ge=async()=>{let e=K.name.trim();if(!e){K.error=`Name is required`;return}H.value=!0,K.error=``;try{let t=await ce(z.value,{name:e,signature_format:K.format});W.value={...t.data.inbound_webhook,secret:t.data.secret,trigger_url:t.data.trigger_url},K.open=!1,await Y()}catch(e){K.error=e?.response?.data?.error?.message||`Create failed`}finally{H.value=!1}},X=async e=>{if(await L.ask({title:`Delete inbound webhook?`,message:`Trigger "${e.name}" (${e.id}) will stop accepting calls immediately. This cannot be undone.`,confirmLabel:`Delete`,danger:!0}))try{await oe(z.value,e.id),await Y()}catch(e){L.notify({title:`Delete failed`,message:e?.response?.data?.error?.message||e.message,danger:!0})}},Z=e=>{q.row=e,q.secret=``,q.body=`{"hello":"orva"}`,q.error=``,q.response=null,q.open=!0},Q=async(e,t)=>{let n=new TextEncoder,r=await crypto.subtle.importKey(`raw`,n.encode(e),{name:`HMAC`,hash:`SHA-256`},!1,[`sign`]),i=await crypto.subtle.sign(`HMAC`,r,n.encode(t));return[...new Uint8Array(i)].map(e=>e.toString(16).padStart(2,`0`)).join(``)},Ke=async()=>{q.error=``,q.response=null,U.value=!0;try{let e=q.row.signature_format,t;if(e===`hmac_sha256_hex`)t=await Q(q.secret.trim(),q.body);else if(e===`github`)t=`sha256=`+await Q(q.secret.trim(),q.body);else{q.error=`Browser test only signs hmac_sha256_hex and github. For ${e}, use the CLI or curl with openssl.`;return}let n=G.value+`/webhook/`+q.row.id,r=await fetch(n,{method:`POST`,headers:{"Content-Type":`application/json`,[q.row.signature_header]:t},body:q.body}),i=await r.text();q.response={status:r.status,body:i}}catch(e){q.error=e.message||`Test failed`}finally{U.value=!1}},qe=e=>{let t=G.value+e.trigger_url,n=e.signature_format;return n===`github`?[`BODY='{"hello":"orva"}'`,`SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "${e.secret}" | sed 's/^.* //')`,`curl -X POST "${t}" \\`,` -H "Content-Type: application/json" \\`,` -H "${e.signature_header}: sha256=$SIG" \\`,` -d "$BODY"`].join(` `):n===`stripe`?[`BODY='{"hello":"orva"}'`,`TS=$(date +%s)`,`SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "${e.secret}" | sed 's/^.* //')`,`curl -X POST "${t}" \\`,` -H "Content-Type: application/json" \\`,` -H "${e.signature_header}: t=$TS,v1=$SIG" \\`,` -d "$BODY"`].join(` `):n===`slack`?[`BODY='{"hello":"orva"}'`,`TS=$(date +%s)`,`SIG=$(printf 'v0:%s:%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "${e.secret}" | sed 's/^.* //')`,`curl -X POST "${t}" \\`,` -H "Content-Type: application/json" \\`,` -H "X-Slack-Request-Timestamp: $TS" \\`,` -H "${e.signature_header}: v0=$SIG" \\`,` -d "$BODY"`].join(` `):[`BODY='{"hello":"orva"}'`,`SIG=$(printf '%s' "$BODY" | ${n===`hmac_sha256_base64`?`openssl dgst -sha256 -hmac "${e.secret}" -binary | base64`:`openssl dgst -sha256 -hmac "${e.secret}" | sed 's/^.* //'`})`,`curl -X POST "${t}" \\`,` -H "Content-Type: application/json" \\`,` -H "${e.signature_header}: $SIG" \\`,` -d "$BODY"`].join(` diff --git a/backend/internal/server/ui_dist/assets/InvocationsLog-BZpwfSFT.js b/backend/internal/server/ui_dist/assets/InvocationsLog-Bz2zpa_f.js similarity index 97% rename from backend/internal/server/ui_dist/assets/InvocationsLog-BZpwfSFT.js rename to backend/internal/server/ui_dist/assets/InvocationsLog-Bz2zpa_f.js index db6ba7fa..a22fcdc8 100644 --- a/backend/internal/server/ui_dist/assets/InvocationsLog-BZpwfSFT.js +++ b/backend/internal/server/ui_dist/assets/InvocationsLog-Bz2zpa_f.js @@ -1,4 +1,4 @@ -import{D as e,E as t,F as n,G as r,I as i,P as a,S as ee,T as te,Z as o,_ as ne,c as s,d as c,gt as l,h as u,k as d,l as re,m as f,r as p,s as m,u as h,v as g,vt as _,w as ie}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as ae}from"./check-DQm9OMOd.js";import{t as oe}from"./circle-alert-BIdKIOKw.js";import{t as se}from"./play-DdJW9ehy.js";import{t as v}from"./refresh-cw-Bn_tp4gH.js";import{t as ce}from"./rotate-ccw-DBtrGGiP.js";import{t as le}from"./sparkles-BmmGPRlQ.js";import{t as ue}from"./trash-2-Bj4AFUfw.js";import{t as de}from"./client-BF51V3uE.js";import{B as fe,Bt as y,C as pe,Dt as me,E as he,H as ge,It as _e,Lt as ve,Mt as ye,S as be,T as xe,Y as Se,_t as b,gt as Ce,mt as we,w as Te,wt as Ee,xt as De}from"./index-C11HmDXN.js";import{t as Oe}from"./Drawer-ZggviMzu.js";import{t as ke}from"./clipboard-D_9N0yai.js";import{t as x}from"./StatusBadge-BOCx3ovI.js";import{n as Ae}from"./aiPrompts-NjljN3ld.js";var je={class:`space-y-6`},Me={class:`flex items-center justify-between`},Ne={class:`flex items-center gap-2 flex-wrap`},Pe={class:`relative flex-1 min-w-[280px] max-w-[440px]`},Fe=[`value`],Ie={key:0,class:`bg-background border border-border rounded-lg px-6 py-12 text-center`},Le={class:`mt-1 text-xs text-foreground-muted`},Re={key:1,class:`bg-background border border-border rounded-lg overflow-x-auto`},ze={class:`sm:hidden divide-y divide-border`},Be=[`onClick`],Ve={class:`flex items-start gap-3`},He=[`checked`,`onChange`],Ue={class:`min-w-0 flex-1`},We={class:`flex items-center justify-between gap-2`},Ge={class:`font-medium text-white truncate`},Ke={class:`mt-1 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-[11px] text-foreground-muted`},qe={key:0,class:`font-mono`},Je={key:1,class:`font-mono`},Ye={key:2,class:`inline-flex items-center px-1.5 py-0.5 rounded text-[10px] border bg-background font-mono text-info-fg border-info-ring`},Xe={key:0,class:`mt-1 text-[11px] text-foreground-muted font-mono break-all`},Ze={key:0,class:`px-6 py-12 text-center text-sm text-foreground-muted`},Qe={class:`hidden sm:table w-full text-sm text-left`},$e={class:`text-xs text-foreground-muted uppercase bg-surface border-b border-border`},et={class:`px-4 py-3 w-8`},tt=[`checked`],nt={class:`divide-y divide-border`},rt=[`onClick`],it=[`checked`,`onChange`],at={class:`px-4 py-3 text-foreground`},ot={class:`px-4 py-3 font-medium text-white`},st=[`onClick`],ct={class:`px-4 py-3`},lt={class:`px-4 py-3 hidden md:table-cell`},ut={key:0,class:`inline-flex items-center px-2 py-0.5 rounded text-xs border bg-background font-mono text-info-fg border-info-ring`},dt={key:1,class:`text-foreground-muted text-xs`},ft={class:`px-4 py-3 text-foreground-muted font-mono text-xs hidden lg:table-cell`},pt={class:`px-4 py-3 text-foreground-muted font-mono text-xs hidden sm:table-cell`},mt={class:`px-4 py-3 hidden lg:table-cell`},ht=[`title`,`onClick`],gt={key:1,class:`text-foreground-muted`},_t={class:`px-4 py-3 text-right text-foreground-muted font-mono text-xs hidden xl:table-cell`},vt={key:0},yt={colspan:`9`,class:`px-6 py-8 text-center text-foreground-muted`},bt={key:0,class:`flex justify-center border-t border-border py-3 bg-surface/30`},xt=[`disabled`],St={key:0,class:`fixed bottom-4 left-1/2 -translate-x-1/2 z-30 flex items-center gap-3 bg-background border border-border shadow-lg rounded-full pl-4 pr-2 py-2`},Ct={class:`text-xs text-white`},wt={key:0,class:`p-6 text-sm text-foreground-muted`},Tt={key:1,class:`p-8 text-center`},Et={class:`mt-1 text-xs text-foreground-muted`},Dt={key:2,class:`p-6 text-sm text-foreground-muted`},Ot={key:3,class:`p-5 space-y-5`},kt={class:`flex items-center gap-2 flex-wrap`},At={key:0,class:`inline-flex items-center px-2.5 py-1 rounded text-xs border bg-background font-mono text-info-fg border-info-ring`},jt={key:1,class:`inline-flex items-center px-2.5 py-1 rounded text-xs border bg-background font-mono text-foreground-muted`},Mt={class:`grid grid-cols-2 gap-3 text-sm`},Nt={key:0},Pt={class:`bg-danger-tint border border-danger-ring rounded p-3 text-xs text-danger-fg font-mono whitespace-pre-wrap break-words`},Ft={key:1},It={class:`bg-surface border border-border rounded p-3 space-y-3`},Lt={class:`flex items-center gap-2 font-mono text-xs`},Rt={class:`px-2 py-0.5 rounded bg-background text-white border border-border`},zt={class:`text-foreground-muted truncate`},Bt={key:0},Vt={class:`bg-background border border-border rounded p-2 max-h-40 overflow-auto`},Ht={class:`text-foreground-muted shrink-0`},Ut={key:1},Wt={class:`bg-background border border-border rounded p-2 text-xs text-foreground font-mono overflow-auto max-h-40 whitespace-pre-wrap break-words`},Gt={key:2,class:`text-[11px] text-warning-fg`},Kt={key:2},qt={class:`text-xs uppercase tracking-wider text-foreground-muted mb-2`},Jt={class:`bg-surface border border-border rounded p-3 text-xs font-mono space-y-1 max-h-72 overflow-auto`},Yt={class:`text-foreground-muted text-[10px] tabular-nums`},Xt={class:`text-white truncate`},Zt={key:0,class:`text-[10px] text-foreground-muted truncate`},Qt={class:`flex items-center justify-between mb-2`},$t={class:`bg-surface border border-border rounded p-3 text-xs text-foreground font-mono overflow-auto max-h-72 whitespace-pre-wrap break-words`},en={class:`pt-2 border-t border-border flex items-center gap-3`},tn={key:1,class:`text-xs text-foreground-muted`},nn=50,rn={__name:`InvocationsLog`,setup(rn){let S=Ce(),C=we(),w=r([]),T=r(0),E=r(!1),D=r(``),O=r(!1),k=r(!1),an=r(!1),A=r(``),j=r(null),M=r(new Set),N=r(``),P=r([]),on=e=>{switch(e){case`error`:return`text-danger-fg`;case`warn`:return`text-warning-fg`;case`debug`:return`text-foreground-muted`;default:return`text-primary-light`}},sn=e=>{if(!e)return``;try{let t=new Date(e);return t.toLocaleTimeString(void 0,{hour12:!1})+`.`+String(t.getMilliseconds()).padStart(3,`0`)}catch{return e}},F=r(!1),I=r({}),L=r(null),R=r(!1),z=r(!1),B=r(!1),V=r(null),H=null,cn=m(()=>w.value.lengthw.value.length>0&&w.value.every(e=>M.value.has(e.id))),ln=m(()=>w.value.some(e=>M.value.has(e.id))),W=r(null);a(()=>ln.value&&!U.value,e=>{W.value&&(W.value.indeterminate=e)},{immediate:!0});let un=e=>{let t=new Set(M.value);t.has(e)?t.delete(e):t.add(e),M.value=t},dn=()=>{if(U.value)M.value=new Set;else{let e=new Set(M.value);w.value.forEach(t=>e.add(t.id)),M.value=e}},G=r({fnId:``,status:``,range:``,q:``}),fn=[{value:``,label:`All`},{value:`success`,label:`Success`},{value:`error`,label:`Error`}],pn=[{value:``,label:`All time`},{value:`1h`,label:`1h`},{value:`24h`,label:`24h`},{value:`7d`,label:`7d`}],K=m(()=>!!(G.value.fnId||G.value.status||G.value.range||G.value.q)),mn=()=>{G.value={fnId:``,status:``,range:``,q:``},Q()},q=null,hn=()=>{q&&clearTimeout(q),q=setTimeout(()=>Q(),300)},J=()=>Q(),gn=e=>{if(!e)return``;let t={"1h":36e5,"24h":864e5,"7d":6048e5}[e];return t?new Date(Date.now()-t).toISOString():``},_n=m(()=>j.value?`Invocation · ${j.value.id?.substring(0,14)}`:`Invocation`),vn=m(()=>R.value?`request not captured`:L.value?.truncated?`body was truncated; replay would be inaccurate`:`Re-run this exact request against the current code`),yn=m(()=>{let e=j.value;return e?typeof e.status_code==`number`&&e.status_code>=500||!!e.error_message:!1}),bn=m(()=>N.value?`Build a paste-ready debug prompt with source + request + stderr`:`no stderr to debug from`),Y={props:{label:String,value:[String,Number],mono:Boolean},template:` +import{D as e,E as t,F as n,G as r,I as i,P as a,S as ee,T as te,Z as o,_ as ne,c as s,d as c,gt as l,h as u,k as d,l as re,m as f,r as p,s as m,u as h,v as g,vt as _,w as ie}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as ae}from"./check-BNre7JFR.js";import{t as oe}from"./circle-alert-BnUYYhGE.js";import{t as se}from"./play-CmQifd74.js";import{t as v}from"./refresh-cw-Cn8qkf-v.js";import{t as ce}from"./rotate-ccw-DWwjKCqh.js";import{t as le}from"./sparkles-DTHEIS5T.js";import{t as ue}from"./trash-2-DaeYqnW_.js";import{t as de}from"./client-BF51V3uE.js";import{B as fe,Bt as y,C as pe,Dt as me,E as he,H as ge,It as _e,Lt as ve,Mt as ye,S as be,T as xe,Y as Se,_t as b,gt as Ce,mt as we,w as Te,wt as Ee,xt as De}from"./index-DTqMKlE1.js";import{t as Oe}from"./Drawer-B98TBytl.js";import{t as ke}from"./clipboard-D_9N0yai.js";import{t as x}from"./StatusBadge-Baoe7YAb.js";import{n as Ae}from"./aiPrompts-XrsFCpj_.js";var je={class:`space-y-6`},Me={class:`flex items-center justify-between`},Ne={class:`flex items-center gap-2 flex-wrap`},Pe={class:`relative flex-1 min-w-[280px] max-w-[440px]`},Fe=[`value`],Ie={key:0,class:`bg-background border border-border rounded-lg px-6 py-12 text-center`},Le={class:`mt-1 text-xs text-foreground-muted`},Re={key:1,class:`bg-background border border-border rounded-lg overflow-x-auto`},ze={class:`sm:hidden divide-y divide-border`},Be=[`onClick`],Ve={class:`flex items-start gap-3`},He=[`checked`,`onChange`],Ue={class:`min-w-0 flex-1`},We={class:`flex items-center justify-between gap-2`},Ge={class:`font-medium text-white truncate`},Ke={class:`mt-1 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-[11px] text-foreground-muted`},qe={key:0,class:`font-mono`},Je={key:1,class:`font-mono`},Ye={key:2,class:`inline-flex items-center px-1.5 py-0.5 rounded text-[10px] border bg-background font-mono text-info-fg border-info-ring`},Xe={key:0,class:`mt-1 text-[11px] text-foreground-muted font-mono break-all`},Ze={key:0,class:`px-6 py-12 text-center text-sm text-foreground-muted`},Qe={class:`hidden sm:table w-full text-sm text-left`},$e={class:`text-xs text-foreground-muted uppercase bg-surface border-b border-border`},et={class:`px-4 py-3 w-8`},tt=[`checked`],nt={class:`divide-y divide-border`},rt=[`onClick`],it=[`checked`,`onChange`],at={class:`px-4 py-3 text-foreground`},ot={class:`px-4 py-3 font-medium text-white`},st=[`onClick`],ct={class:`px-4 py-3`},lt={class:`px-4 py-3 hidden md:table-cell`},ut={key:0,class:`inline-flex items-center px-2 py-0.5 rounded text-xs border bg-background font-mono text-info-fg border-info-ring`},dt={key:1,class:`text-foreground-muted text-xs`},ft={class:`px-4 py-3 text-foreground-muted font-mono text-xs hidden lg:table-cell`},pt={class:`px-4 py-3 text-foreground-muted font-mono text-xs hidden sm:table-cell`},mt={class:`px-4 py-3 hidden lg:table-cell`},ht=[`title`,`onClick`],gt={key:1,class:`text-foreground-muted`},_t={class:`px-4 py-3 text-right text-foreground-muted font-mono text-xs hidden xl:table-cell`},vt={key:0},yt={colspan:`9`,class:`px-6 py-8 text-center text-foreground-muted`},bt={key:0,class:`flex justify-center border-t border-border py-3 bg-surface/30`},xt=[`disabled`],St={key:0,class:`fixed bottom-4 left-1/2 -translate-x-1/2 z-30 flex items-center gap-3 bg-background border border-border shadow-lg rounded-full pl-4 pr-2 py-2`},Ct={class:`text-xs text-white`},wt={key:0,class:`p-6 text-sm text-foreground-muted`},Tt={key:1,class:`p-8 text-center`},Et={class:`mt-1 text-xs text-foreground-muted`},Dt={key:2,class:`p-6 text-sm text-foreground-muted`},Ot={key:3,class:`p-5 space-y-5`},kt={class:`flex items-center gap-2 flex-wrap`},At={key:0,class:`inline-flex items-center px-2.5 py-1 rounded text-xs border bg-background font-mono text-info-fg border-info-ring`},jt={key:1,class:`inline-flex items-center px-2.5 py-1 rounded text-xs border bg-background font-mono text-foreground-muted`},Mt={class:`grid grid-cols-2 gap-3 text-sm`},Nt={key:0},Pt={class:`bg-danger-tint border border-danger-ring rounded p-3 text-xs text-danger-fg font-mono whitespace-pre-wrap break-words`},Ft={key:1},It={class:`bg-surface border border-border rounded p-3 space-y-3`},Lt={class:`flex items-center gap-2 font-mono text-xs`},Rt={class:`px-2 py-0.5 rounded bg-background text-white border border-border`},zt={class:`text-foreground-muted truncate`},Bt={key:0},Vt={class:`bg-background border border-border rounded p-2 max-h-40 overflow-auto`},Ht={class:`text-foreground-muted shrink-0`},Ut={key:1},Wt={class:`bg-background border border-border rounded p-2 text-xs text-foreground font-mono overflow-auto max-h-40 whitespace-pre-wrap break-words`},Gt={key:2,class:`text-[11px] text-warning-fg`},Kt={key:2},qt={class:`text-xs uppercase tracking-wider text-foreground-muted mb-2`},Jt={class:`bg-surface border border-border rounded p-3 text-xs font-mono space-y-1 max-h-72 overflow-auto`},Yt={class:`text-foreground-muted text-[10px] tabular-nums`},Xt={class:`text-white truncate`},Zt={key:0,class:`text-[10px] text-foreground-muted truncate`},Qt={class:`flex items-center justify-between mb-2`},$t={class:`bg-surface border border-border rounded p-3 text-xs text-foreground font-mono overflow-auto max-h-72 whitespace-pre-wrap break-words`},en={class:`pt-2 border-t border-border flex items-center gap-3`},tn={key:1,class:`text-xs text-foreground-muted`},nn=50,rn={__name:`InvocationsLog`,setup(rn){let S=Ce(),C=we(),w=r([]),T=r(0),E=r(!1),D=r(``),O=r(!1),k=r(!1),an=r(!1),A=r(``),j=r(null),M=r(new Set),N=r(``),P=r([]),on=e=>{switch(e){case`error`:return`text-danger-fg`;case`warn`:return`text-warning-fg`;case`debug`:return`text-foreground-muted`;default:return`text-primary-light`}},sn=e=>{if(!e)return``;try{let t=new Date(e);return t.toLocaleTimeString(void 0,{hour12:!1})+`.`+String(t.getMilliseconds()).padStart(3,`0`)}catch{return e}},F=r(!1),I=r({}),L=r(null),R=r(!1),z=r(!1),B=r(!1),V=r(null),H=null,cn=m(()=>w.value.lengthw.value.length>0&&w.value.every(e=>M.value.has(e.id))),ln=m(()=>w.value.some(e=>M.value.has(e.id))),W=r(null);a(()=>ln.value&&!U.value,e=>{W.value&&(W.value.indeterminate=e)},{immediate:!0});let un=e=>{let t=new Set(M.value);t.has(e)?t.delete(e):t.add(e),M.value=t},dn=()=>{if(U.value)M.value=new Set;else{let e=new Set(M.value);w.value.forEach(t=>e.add(t.id)),M.value=e}},G=r({fnId:``,status:``,range:``,q:``}),fn=[{value:``,label:`All`},{value:`success`,label:`Success`},{value:`error`,label:`Error`}],pn=[{value:``,label:`All time`},{value:`1h`,label:`1h`},{value:`24h`,label:`24h`},{value:`7d`,label:`7d`}],K=m(()=>!!(G.value.fnId||G.value.status||G.value.range||G.value.q)),mn=()=>{G.value={fnId:``,status:``,range:``,q:``},Q()},q=null,hn=()=>{q&&clearTimeout(q),q=setTimeout(()=>Q(),300)},J=()=>Q(),gn=e=>{if(!e)return``;let t={"1h":36e5,"24h":864e5,"7d":6048e5}[e];return t?new Date(Date.now()-t).toISOString():``},_n=m(()=>j.value?`Invocation · ${j.value.id?.substring(0,14)}`:`Invocation`),vn=m(()=>R.value?`request not captured`:L.value?.truncated?`body was truncated; replay would be inaccurate`:`Re-run this exact request against the current code`),yn=m(()=>{let e=j.value;return e?typeof e.status_code==`number`&&e.status_code>=500||!!e.error_message:!1}),bn=m(()=>N.value?`Build a paste-ready debug prompt with source + request + stderr`:`no stderr to debug from`),Y={props:{label:String,value:[String,Number],mono:Boolean},template:`
{{ label }}
{{ value }}
diff --git a/backend/internal/server/ui_dist/assets/Jobs-DzZOcH5E.js b/backend/internal/server/ui_dist/assets/Jobs-CfHJpgrS.js similarity index 97% rename from backend/internal/server/ui_dist/assets/Jobs-DzZOcH5E.js rename to backend/internal/server/ui_dist/assets/Jobs-CfHJpgrS.js index 01c6b078..975abbe7 100644 --- a/backend/internal/server/ui_dist/assets/Jobs-DzZOcH5E.js +++ b/backend/internal/server/ui_dist/assets/Jobs-CfHJpgrS.js @@ -1 +1 @@ -import{C as e,D as t,F as n,G as r,I as i,M as a,T as o,U as ee,Z as s,c,d as l,gt as u,h as d,k as f,l as p,m,r as h,s as g,u as _,vt as v}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{n as te,t as y}from"./clock-Dl3hMdU8.js";import{n as ne,t as re}from"./circle-bxz1-xUq.js";import{t as b}from"./rotate-ccw-DBtrGGiP.js";import{t as x}from"./trash-2-Bj4AFUfw.js";import{B as ie,It as ae,Lt as S,Nt as oe,St as se,U as ce,X as le,_t as C,g as ue,gt as de,jt as fe,y as pe}from"./index-C11HmDXN.js";import{t as me}from"./Drawer-ZggviMzu.js";import{t as w}from"./IconButton-CsCZOqWo.js";var T=fe(`refresh-ccw`,[[`path`,{d:`M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8`,key:`14sxne`}],[`path`,{d:`M3 3v5h5`,key:`1xhq8a`}],[`path`,{d:`M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16`,key:`1hlbsb`}],[`path`,{d:`M16 16h5v5`,key:`ccwih5`}]]),E={class:`space-y-4`},D={class:`flex items-start justify-between gap-4 flex-wrap`},O={class:`flex items-center gap-2 text-xs text-foreground-muted`},k={class:`flex items-center gap-2`},A={class:`flex items-center gap-2 sm:flex-wrap overflow-x-auto sm:overflow-visible scrollable snap-x min-w-0 flex-1`},j={key:0,class:`ml-1 opacity-70 tabular-nums`},M={class:`flex items-center gap-2 shrink-0`},N={class:`p-5 space-y-5 text-sm`},P=[`value`],F={class:`flex items-center gap-2 text-xs text-foreground-muted`},I={key:0},L={key:1,class:`text-xs text-danger-fg`},R={class:`flex items-center justify-end gap-2`},z={class:`bg-background border border-border rounded-lg overflow-x-auto`},he={class:`sm:hidden divide-y divide-border`},ge={class:`flex items-start justify-between gap-2`},_e={class:`min-w-0 flex-1`},ve={class:`font-medium text-white truncate`},ye={class:`text-[10px] text-foreground-muted font-mono break-all`},be={class:`mt-2`},xe=[`title`],Se={class:`mt-2 grid grid-cols-2 gap-x-3 gap-y-1 text-[11px] text-foreground-muted`},Ce={class:`col-span-2`},we={class:`flex items-center gap-1 shrink-0`},B={key:0,class:`px-4 py-12 text-center`},Te={class:`text-foreground-muted text-sm`},Ee={class:`hidden sm:table w-full text-sm text-left`},De={class:`divide-y divide-border`},Oe={class:`px-4 py-3 font-medium text-white`},ke={class:`flex flex-col`},Ae={class:`text-[10px] text-foreground-muted font-mono`},je={class:`px-4 py-3`},Me=[`title`],Ne={class:`px-4 py-3 text-foreground-muted text-xs hidden md:table-cell`},Pe={class:`px-4 py-3 text-foreground-muted text-xs hidden lg:table-cell`},Fe={class:`px-4 py-3 text-foreground-muted text-xs hidden xl:table-cell`},Ie={class:`px-4 py-3 text-right`},Le={class:`inline-flex items-center gap-1`},Re={key:0},ze={colspan:`6`,class:`px-4 py-12 text-center`},Be={class:`text-foreground-muted text-sm`},V=Object.assign({name:`JobsView`},{__name:`Jobs`,setup(fe){let V=de(),H=r([]),U=r([]),W=r(`all`),G=null,K=ee({open:!1,fnId:``,payload:`{}`,scheduleLater:!1,scheduledAt:``,saving:!1,error:``}),Ve=[{value:`all`,label:`All`},{value:`pending`,label:`Pending`},{value:`running`,label:`Running`},{value:`succeeded`,label:`Succeeded`},{value:`failed`,label:`Failed`}],He=g(()=>H.value.length),q=g(()=>W.value===`all`?H.value:H.value.filter(e=>e.status===W.value)),J=g(()=>{let e={all:H.value.length};for(let t of H.value)e[t.status]=(e[t.status]||0)+1;return e}),Y=e=>{switch(e){case`pending`:return{classes:`bg-warning-tint text-warning-fg border-warning-ring`,icon:y};case`running`:return{classes:`bg-info-tint text-info-fg border-info-ring`,icon:y};case`succeeded`:return{classes:`bg-success-tint text-success-fg border-success-ring`,icon:te};case`failed`:return{classes:`bg-danger-tint text-danger-fg border-danger-ring`,icon:ne};default:return{classes:`bg-surface text-foreground-muted border-border`,icon:re}}},X=e=>e?new Date(e).toLocaleString(`en-US`,{month:`short`,day:`numeric`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`}):`—`,Z=async()=>{try{let e=await ce({limit:200});H.value=e.data.jobs||[]}catch(e){console.error(`Failed to load jobs`,e)}},Q=async e=>{try{await le(e.id),await Z()}catch(e){V.notify({title:`Retry failed`,message:e.message,danger:!0})}},$=async e=>{if(await V.ask({title:`Delete job?`,message:`Job ${e.id} will be removed. This cannot be undone.`,confirmLabel:`Delete`,danger:!0}))try{await ue(e.id),await Z()}catch(e){V.notify({title:`Delete failed`,message:e.message,danger:!0})}},Ue=async()=>{if(K.error=``,K.payload=`{}`,K.scheduleLater=!1,K.scheduledAt=``,U.value.length===0)try{let e=await ie();U.value=e.data?.functions||[],U.value.length&&!K.fnId&&(K.fnId=U.value[0].id)}catch(e){console.error(`list functions failed`,e)}K.open=!0},We=async()=>{K.error=``;let e;try{e=K.payload.trim()?JSON.parse(K.payload):{}}catch(e){K.error=`Payload must be valid JSON: `+e.message;return}let t={function_id:K.fnId,payload:e};if(K.scheduleLater){if(!K.scheduledAt){K.error=`Pick a date/time, or untick "Schedule for later".`;return}t.scheduled_at=new Date(K.scheduledAt).toISOString()}K.saving=!0;try{await pe(t),K.open=!1,await Z()}catch(e){K.error=e?.response?.data?.error?.message||e.message||`Enqueue failed`}finally{K.saving=!1}};return o(()=>{Z(),G||=setInterval(Z,5e3)}),e(()=>{G&&=(clearInterval(G),null)}),(e,r)=>(t(),l(`div`,E,[c(`div`,D,[r[6]||=c(`div`,null,[c(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` Jobs `),c(`p`,{class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},` Queued background work with automatic retries. `)],-1),c(`div`,O,` Background queue · `+v(He.value)+` jobs `,1)]),c(`div`,k,[c(`div`,A,[(t(),l(h,null,f(Ve,e=>d(C,{key:e.value,variant:`chip`,size:`xs`,active:W.value===e.value,class:`shrink-0 snap-start`,onClick:t=>W.value=e.value},{default:n(()=>[m(v(e.label)+` `,1),J.value[e.value]===void 0?_(``,!0):(t(),l(`span`,j,v(J.value[e.value]),1))]),_:2},1032,[`active`,`onClick`])),64))]),c(`div`,M,[d(C,{size:`xs`,onClick:Ue},{default:n(()=>[d(s(se),{class:`w-3 h-3`}),r[7]||=m(` Enqueue `,-1)]),_:1}),d(C,{variant:`secondary`,size:`xs`,onClick:Z},{default:n(()=>[d(s(T),{class:`w-3 h-3`}),r[8]||=m(` Refresh `,-1)]),_:1})])]),d(me,{modelValue:K.open,"onUpdate:modelValue":r[5]||=e=>K.open=e,title:`Enqueue a job`,width:`560px`},{footer:n(()=>[c(`div`,R,[d(C,{variant:`ghost`,size:`sm`,onClick:r[4]||=e=>K.open=!1},{default:n(()=>[...r[14]||=[m(` Cancel `,-1)]]),_:1}),d(C,{size:`sm`,disabled:!K.fnId||K.saving,loading:K.saving,onClick:We},{default:n(()=>[...r[15]||=[m(` Enqueue `,-1)]]),_:1},8,[`disabled`,`loading`])])]),default:n(()=>[c(`div`,N,[c(`div`,null,[r[9]||=c(`label`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},`Function`,-1),i(c(`select`,{"onUpdate:modelValue":r[0]||=e=>K.fnId=e,class:`mt-2 w-full bg-surface border border-border rounded px-3 py-2 text-sm text-white focus:outline-none focus:border-white`},[(t(!0),l(h,null,f(U.value,e=>(t(),l(`option`,{key:e.id,value:e.id},v(e.name)+` (`+v(e.runtime)+`) `,9,P))),128))],512),[[ae,K.fnId]])]),c(`div`,null,[r[10]||=c(`label`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},`Payload (JSON)`,-1),i(c(`textarea`,{"onUpdate:modelValue":r[1]||=e=>K.payload=e,rows:`6`,spellcheck:`false`,class:`mt-2 w-full bg-surface border border-border rounded p-3 text-xs text-white font-mono focus:outline-none focus:border-white`,placeholder:`{"hello":"world"}`},null,512),[[S,K.payload]])]),c(`div`,null,[c(`label`,F,[i(c(`input`,{"onUpdate:modelValue":r[2]||=e=>K.scheduleLater=e,type:`checkbox`},null,512),[[oe,K.scheduleLater]]),r[11]||=m(` Schedule for later `,-1)]),r[12]||=c(`p`,{class:`text-[11px] text-foreground-muted mt-1`},` Off: runs on the next scheduler tick (~5s). On: holds until the timestamp below. `,-1)]),K.scheduleLater?(t(),l(`div`,I,[r[13]||=c(`label`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},`Run at (local time)`,-1),i(c(`input`,{"onUpdate:modelValue":r[3]||=e=>K.scheduledAt=e,type:`datetime-local`,class:`mt-2 w-full bg-surface border border-border rounded px-3 py-2 text-sm text-white focus:outline-none focus:border-white`},null,512),[[S,K.scheduledAt]])])):_(``,!0),K.error?(t(),l(`div`,L,v(K.error),1)):_(``,!0)])]),_:1},8,[`modelValue`]),c(`div`,z,[c(`ul`,he,[(t(!0),l(h,null,f(q.value,e=>(t(),l(`li`,{key:e.id,class:`px-4 py-3`},[c(`div`,ge,[c(`div`,_e,[c(`div`,ve,v(e.function_name||e.function_id),1),c(`div`,ye,v(e.id),1),c(`div`,be,[c(`span`,{class:u([`inline-flex items-center gap-1 px-2 py-0.5 rounded text-[11px] font-medium border`,Y(e.status).classes])},[(t(),p(a(Y(e.status).icon),{class:`w-3 h-3 shrink-0`,"aria-hidden":`true`})),m(` `+v(e.status),1)],2)]),e.last_error?(t(),l(`p`,{key:0,class:`text-[11px] text-danger-fg mt-1 break-all`,title:e.last_error},v(e.last_error),9,xe)):_(``,!0),c(`dl`,Se,[c(`div`,null,[r[16]||=c(`dt`,{class:`uppercase tracking-wider text-[10px]`},` Attempts `,-1),c(`dd`,null,v(e.attempts)+` / `+v(e.max_attempts),1)]),c(`div`,null,[r[17]||=c(`dt`,{class:`uppercase tracking-wider text-[10px]`},` Scheduled `,-1),c(`dd`,null,v(X(e.scheduled_at)),1)]),c(`div`,Ce,[r[18]||=c(`dt`,{class:`uppercase tracking-wider text-[10px]`},` Finished `,-1),c(`dd`,null,v(e.finished_at?X(e.finished_at):s(`—`)),1)])])]),c(`div`,we,[e.status===`failed`?(t(),p(w,{key:0,icon:s(b),variant:`success`,title:`Retry`,onClick:t=>Q(e)},null,8,[`icon`,`onClick`])):_(``,!0),d(w,{icon:s(x),variant:`danger`,title:`Delete`,onClick:t=>$(e)},null,8,[`icon`,`onClick`])])])]))),128)),q.value.length===0?(t(),l(`li`,B,[c(`p`,Te,v(W.value===`all`?`No jobs yet.`:`No ${W.value} jobs.`),1),r[19]||=c(`p`,{class:`text-foreground-muted text-xs mt-1`},[m(` Enqueue jobs from a function with `),c(`code`,{class:`font-mono text-xs`},`orva.jobs.enqueue()`),m(`. `)],-1)])):_(``,!0)]),c(`table`,Ee,[r[21]||=c(`thead`,{class:`text-xs text-foreground-muted uppercase bg-surface border-b border-border`},[c(`tr`,null,[c(`th`,{class:`px-4 py-3 font-medium`},` Function `),c(`th`,{class:`px-4 py-3 font-medium`},` Status `),c(`th`,{class:`px-4 py-3 font-medium hidden md:table-cell`},` Attempts `),c(`th`,{class:`px-4 py-3 font-medium hidden lg:table-cell`},` Scheduled `),c(`th`,{class:`px-4 py-3 font-medium hidden xl:table-cell`},` Finished `),c(`th`,{class:`px-4 py-3 font-medium text-right`},` Actions `)])],-1),c(`tbody`,De,[(t(!0),l(h,null,f(q.value,e=>(t(),l(`tr`,{key:e.id,class:`hover:bg-surface/50 transition-colors`},[c(`td`,Oe,[c(`div`,ke,[c(`span`,null,v(e.function_name||e.function_id),1),c(`span`,Ae,v(e.id),1)])]),c(`td`,je,[c(`span`,{class:u([`inline-flex items-center gap-1 px-2 py-0.5 rounded text-[11px] font-medium border`,Y(e.status).classes])},[(t(),p(a(Y(e.status).icon),{class:`w-3 h-3 shrink-0`,"aria-hidden":`true`})),m(` `+v(e.status),1)],2),e.last_error?(t(),l(`p`,{key:0,class:`text-[11px] text-danger-fg mt-1 truncate max-w-xs`,title:e.last_error},v(e.last_error),9,Me)):_(``,!0)]),c(`td`,Ne,v(e.attempts)+` / `+v(e.max_attempts),1),c(`td`,Pe,v(X(e.scheduled_at)),1),c(`td`,Fe,v(e.finished_at?X(e.finished_at):s(`—`)),1),c(`td`,Ie,[c(`div`,Le,[e.status===`failed`?(t(),p(w,{key:0,icon:s(b),variant:`success`,title:`Retry`,onClick:t=>Q(e)},null,8,[`icon`,`onClick`])):_(``,!0),d(w,{icon:s(x),variant:`danger`,title:`Delete`,onClick:t=>$(e)},null,8,[`icon`,`onClick`])])])]))),128)),q.value.length===0?(t(),l(`tr`,Re,[c(`td`,ze,[c(`p`,Be,v(W.value===`all`?`No jobs yet.`:`No ${W.value} jobs.`),1),r[20]||=c(`p`,{class:`text-foreground-muted text-xs mt-1`},[m(` Enqueue jobs from a function with `),c(`code`,{class:`font-mono text-xs`},`orva.jobs.enqueue()`),m(`. `)],-1)])])):_(``,!0)])])])]))}});export{V as default}; \ No newline at end of file +import{C as e,D as t,F as n,G as r,I as i,M as a,T as o,U as ee,Z as s,c,d as l,gt as u,h as d,k as f,l as p,m,r as h,s as g,u as _,vt as v}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{n as te,t as y}from"./clock-CiH8bQVI.js";import{n as ne,t as re}from"./circle-NOse79gh.js";import{t as b}from"./rotate-ccw-DWwjKCqh.js";import{t as x}from"./trash-2-DaeYqnW_.js";import{B as ie,It as ae,Lt as S,Nt as oe,St as se,U as ce,X as le,_t as C,g as ue,gt as de,jt as fe,y as pe}from"./index-DTqMKlE1.js";import{t as me}from"./Drawer-B98TBytl.js";import{t as w}from"./IconButton-CsCZOqWo.js";var T=fe(`refresh-ccw`,[[`path`,{d:`M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8`,key:`14sxne`}],[`path`,{d:`M3 3v5h5`,key:`1xhq8a`}],[`path`,{d:`M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16`,key:`1hlbsb`}],[`path`,{d:`M16 16h5v5`,key:`ccwih5`}]]),E={class:`space-y-4`},D={class:`flex items-start justify-between gap-4 flex-wrap`},O={class:`flex items-center gap-2 text-xs text-foreground-muted`},k={class:`flex items-center gap-2`},A={class:`flex items-center gap-2 sm:flex-wrap overflow-x-auto sm:overflow-visible scrollable snap-x min-w-0 flex-1`},j={key:0,class:`ml-1 opacity-70 tabular-nums`},M={class:`flex items-center gap-2 shrink-0`},N={class:`p-5 space-y-5 text-sm`},P=[`value`],F={class:`flex items-center gap-2 text-xs text-foreground-muted`},I={key:0},L={key:1,class:`text-xs text-danger-fg`},R={class:`flex items-center justify-end gap-2`},z={class:`bg-background border border-border rounded-lg overflow-x-auto`},he={class:`sm:hidden divide-y divide-border`},ge={class:`flex items-start justify-between gap-2`},_e={class:`min-w-0 flex-1`},ve={class:`font-medium text-white truncate`},ye={class:`text-[10px] text-foreground-muted font-mono break-all`},be={class:`mt-2`},xe=[`title`],Se={class:`mt-2 grid grid-cols-2 gap-x-3 gap-y-1 text-[11px] text-foreground-muted`},Ce={class:`col-span-2`},we={class:`flex items-center gap-1 shrink-0`},B={key:0,class:`px-4 py-12 text-center`},Te={class:`text-foreground-muted text-sm`},Ee={class:`hidden sm:table w-full text-sm text-left`},De={class:`divide-y divide-border`},Oe={class:`px-4 py-3 font-medium text-white`},ke={class:`flex flex-col`},Ae={class:`text-[10px] text-foreground-muted font-mono`},je={class:`px-4 py-3`},Me=[`title`],Ne={class:`px-4 py-3 text-foreground-muted text-xs hidden md:table-cell`},Pe={class:`px-4 py-3 text-foreground-muted text-xs hidden lg:table-cell`},Fe={class:`px-4 py-3 text-foreground-muted text-xs hidden xl:table-cell`},Ie={class:`px-4 py-3 text-right`},Le={class:`inline-flex items-center gap-1`},Re={key:0},ze={colspan:`6`,class:`px-4 py-12 text-center`},Be={class:`text-foreground-muted text-sm`},V=Object.assign({name:`JobsView`},{__name:`Jobs`,setup(fe){let V=de(),H=r([]),U=r([]),W=r(`all`),G=null,K=ee({open:!1,fnId:``,payload:`{}`,scheduleLater:!1,scheduledAt:``,saving:!1,error:``}),Ve=[{value:`all`,label:`All`},{value:`pending`,label:`Pending`},{value:`running`,label:`Running`},{value:`succeeded`,label:`Succeeded`},{value:`failed`,label:`Failed`}],He=g(()=>H.value.length),q=g(()=>W.value===`all`?H.value:H.value.filter(e=>e.status===W.value)),J=g(()=>{let e={all:H.value.length};for(let t of H.value)e[t.status]=(e[t.status]||0)+1;return e}),Y=e=>{switch(e){case`pending`:return{classes:`bg-warning-tint text-warning-fg border-warning-ring`,icon:y};case`running`:return{classes:`bg-info-tint text-info-fg border-info-ring`,icon:y};case`succeeded`:return{classes:`bg-success-tint text-success-fg border-success-ring`,icon:te};case`failed`:return{classes:`bg-danger-tint text-danger-fg border-danger-ring`,icon:ne};default:return{classes:`bg-surface text-foreground-muted border-border`,icon:re}}},X=e=>e?new Date(e).toLocaleString(`en-US`,{month:`short`,day:`numeric`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`}):`—`,Z=async()=>{try{let e=await ce({limit:200});H.value=e.data.jobs||[]}catch(e){console.error(`Failed to load jobs`,e)}},Q=async e=>{try{await le(e.id),await Z()}catch(e){V.notify({title:`Retry failed`,message:e.message,danger:!0})}},$=async e=>{if(await V.ask({title:`Delete job?`,message:`Job ${e.id} will be removed. This cannot be undone.`,confirmLabel:`Delete`,danger:!0}))try{await ue(e.id),await Z()}catch(e){V.notify({title:`Delete failed`,message:e.message,danger:!0})}},Ue=async()=>{if(K.error=``,K.payload=`{}`,K.scheduleLater=!1,K.scheduledAt=``,U.value.length===0)try{let e=await ie();U.value=e.data?.functions||[],U.value.length&&!K.fnId&&(K.fnId=U.value[0].id)}catch(e){console.error(`list functions failed`,e)}K.open=!0},We=async()=>{K.error=``;let e;try{e=K.payload.trim()?JSON.parse(K.payload):{}}catch(e){K.error=`Payload must be valid JSON: `+e.message;return}let t={function_id:K.fnId,payload:e};if(K.scheduleLater){if(!K.scheduledAt){K.error=`Pick a date/time, or untick "Schedule for later".`;return}t.scheduled_at=new Date(K.scheduledAt).toISOString()}K.saving=!0;try{await pe(t),K.open=!1,await Z()}catch(e){K.error=e?.response?.data?.error?.message||e.message||`Enqueue failed`}finally{K.saving=!1}};return o(()=>{Z(),G||=setInterval(Z,5e3)}),e(()=>{G&&=(clearInterval(G),null)}),(e,r)=>(t(),l(`div`,E,[c(`div`,D,[r[6]||=c(`div`,null,[c(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` Jobs `),c(`p`,{class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},` Queued background work with automatic retries. `)],-1),c(`div`,O,` Background queue · `+v(He.value)+` jobs `,1)]),c(`div`,k,[c(`div`,A,[(t(),l(h,null,f(Ve,e=>d(C,{key:e.value,variant:`chip`,size:`xs`,active:W.value===e.value,class:`shrink-0 snap-start`,onClick:t=>W.value=e.value},{default:n(()=>[m(v(e.label)+` `,1),J.value[e.value]===void 0?_(``,!0):(t(),l(`span`,j,v(J.value[e.value]),1))]),_:2},1032,[`active`,`onClick`])),64))]),c(`div`,M,[d(C,{size:`xs`,onClick:Ue},{default:n(()=>[d(s(se),{class:`w-3 h-3`}),r[7]||=m(` Enqueue `,-1)]),_:1}),d(C,{variant:`secondary`,size:`xs`,onClick:Z},{default:n(()=>[d(s(T),{class:`w-3 h-3`}),r[8]||=m(` Refresh `,-1)]),_:1})])]),d(me,{modelValue:K.open,"onUpdate:modelValue":r[5]||=e=>K.open=e,title:`Enqueue a job`,width:`560px`},{footer:n(()=>[c(`div`,R,[d(C,{variant:`ghost`,size:`sm`,onClick:r[4]||=e=>K.open=!1},{default:n(()=>[...r[14]||=[m(` Cancel `,-1)]]),_:1}),d(C,{size:`sm`,disabled:!K.fnId||K.saving,loading:K.saving,onClick:We},{default:n(()=>[...r[15]||=[m(` Enqueue `,-1)]]),_:1},8,[`disabled`,`loading`])])]),default:n(()=>[c(`div`,N,[c(`div`,null,[r[9]||=c(`label`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},`Function`,-1),i(c(`select`,{"onUpdate:modelValue":r[0]||=e=>K.fnId=e,class:`mt-2 w-full bg-surface border border-border rounded px-3 py-2 text-sm text-white focus:outline-none focus:border-white`},[(t(!0),l(h,null,f(U.value,e=>(t(),l(`option`,{key:e.id,value:e.id},v(e.name)+` (`+v(e.runtime)+`) `,9,P))),128))],512),[[ae,K.fnId]])]),c(`div`,null,[r[10]||=c(`label`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},`Payload (JSON)`,-1),i(c(`textarea`,{"onUpdate:modelValue":r[1]||=e=>K.payload=e,rows:`6`,spellcheck:`false`,class:`mt-2 w-full bg-surface border border-border rounded p-3 text-xs text-white font-mono focus:outline-none focus:border-white`,placeholder:`{"hello":"world"}`},null,512),[[S,K.payload]])]),c(`div`,null,[c(`label`,F,[i(c(`input`,{"onUpdate:modelValue":r[2]||=e=>K.scheduleLater=e,type:`checkbox`},null,512),[[oe,K.scheduleLater]]),r[11]||=m(` Schedule for later `,-1)]),r[12]||=c(`p`,{class:`text-[11px] text-foreground-muted mt-1`},` Off: runs on the next scheduler tick (~5s). On: holds until the timestamp below. `,-1)]),K.scheduleLater?(t(),l(`div`,I,[r[13]||=c(`label`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},`Run at (local time)`,-1),i(c(`input`,{"onUpdate:modelValue":r[3]||=e=>K.scheduledAt=e,type:`datetime-local`,class:`mt-2 w-full bg-surface border border-border rounded px-3 py-2 text-sm text-white focus:outline-none focus:border-white`},null,512),[[S,K.scheduledAt]])])):_(``,!0),K.error?(t(),l(`div`,L,v(K.error),1)):_(``,!0)])]),_:1},8,[`modelValue`]),c(`div`,z,[c(`ul`,he,[(t(!0),l(h,null,f(q.value,e=>(t(),l(`li`,{key:e.id,class:`px-4 py-3`},[c(`div`,ge,[c(`div`,_e,[c(`div`,ve,v(e.function_name||e.function_id),1),c(`div`,ye,v(e.id),1),c(`div`,be,[c(`span`,{class:u([`inline-flex items-center gap-1 px-2 py-0.5 rounded text-[11px] font-medium border`,Y(e.status).classes])},[(t(),p(a(Y(e.status).icon),{class:`w-3 h-3 shrink-0`,"aria-hidden":`true`})),m(` `+v(e.status),1)],2)]),e.last_error?(t(),l(`p`,{key:0,class:`text-[11px] text-danger-fg mt-1 break-all`,title:e.last_error},v(e.last_error),9,xe)):_(``,!0),c(`dl`,Se,[c(`div`,null,[r[16]||=c(`dt`,{class:`uppercase tracking-wider text-[10px]`},` Attempts `,-1),c(`dd`,null,v(e.attempts)+` / `+v(e.max_attempts),1)]),c(`div`,null,[r[17]||=c(`dt`,{class:`uppercase tracking-wider text-[10px]`},` Scheduled `,-1),c(`dd`,null,v(X(e.scheduled_at)),1)]),c(`div`,Ce,[r[18]||=c(`dt`,{class:`uppercase tracking-wider text-[10px]`},` Finished `,-1),c(`dd`,null,v(e.finished_at?X(e.finished_at):s(`—`)),1)])])]),c(`div`,we,[e.status===`failed`?(t(),p(w,{key:0,icon:s(b),variant:`success`,title:`Retry`,onClick:t=>Q(e)},null,8,[`icon`,`onClick`])):_(``,!0),d(w,{icon:s(x),variant:`danger`,title:`Delete`,onClick:t=>$(e)},null,8,[`icon`,`onClick`])])])]))),128)),q.value.length===0?(t(),l(`li`,B,[c(`p`,Te,v(W.value===`all`?`No jobs yet.`:`No ${W.value} jobs.`),1),r[19]||=c(`p`,{class:`text-foreground-muted text-xs mt-1`},[m(` Enqueue jobs from a function with `),c(`code`,{class:`font-mono text-xs`},`orva.jobs.enqueue()`),m(`. `)],-1)])):_(``,!0)]),c(`table`,Ee,[r[21]||=c(`thead`,{class:`text-xs text-foreground-muted uppercase bg-surface border-b border-border`},[c(`tr`,null,[c(`th`,{class:`px-4 py-3 font-medium`},` Function `),c(`th`,{class:`px-4 py-3 font-medium`},` Status `),c(`th`,{class:`px-4 py-3 font-medium hidden md:table-cell`},` Attempts `),c(`th`,{class:`px-4 py-3 font-medium hidden lg:table-cell`},` Scheduled `),c(`th`,{class:`px-4 py-3 font-medium hidden xl:table-cell`},` Finished `),c(`th`,{class:`px-4 py-3 font-medium text-right`},` Actions `)])],-1),c(`tbody`,De,[(t(!0),l(h,null,f(q.value,e=>(t(),l(`tr`,{key:e.id,class:`hover:bg-surface/50 transition-colors`},[c(`td`,Oe,[c(`div`,ke,[c(`span`,null,v(e.function_name||e.function_id),1),c(`span`,Ae,v(e.id),1)])]),c(`td`,je,[c(`span`,{class:u([`inline-flex items-center gap-1 px-2 py-0.5 rounded text-[11px] font-medium border`,Y(e.status).classes])},[(t(),p(a(Y(e.status).icon),{class:`w-3 h-3 shrink-0`,"aria-hidden":`true`})),m(` `+v(e.status),1)],2),e.last_error?(t(),l(`p`,{key:0,class:`text-[11px] text-danger-fg mt-1 truncate max-w-xs`,title:e.last_error},v(e.last_error),9,Me)):_(``,!0)]),c(`td`,Ne,v(e.attempts)+` / `+v(e.max_attempts),1),c(`td`,Pe,v(X(e.scheduled_at)),1),c(`td`,Fe,v(e.finished_at?X(e.finished_at):s(`—`)),1),c(`td`,Ie,[c(`div`,Le,[e.status===`failed`?(t(),p(w,{key:0,icon:s(b),variant:`success`,title:`Retry`,onClick:t=>Q(e)},null,8,[`icon`,`onClick`])):_(``,!0),d(w,{icon:s(x),variant:`danger`,title:`Delete`,onClick:t=>$(e)},null,8,[`icon`,`onClick`])])])]))),128)),q.value.length===0?(t(),l(`tr`,Re,[c(`td`,ze,[c(`p`,Be,v(W.value===`all`?`No jobs yet.`:`No ${W.value} jobs.`),1),r[20]||=c(`p`,{class:`text-foreground-muted text-xs mt-1`},[m(` Enqueue jobs from a function with `),c(`code`,{class:`font-mono text-xs`},`orva.jobs.enqueue()`),m(`. `)],-1)])])):_(``,!0)])])])]))}});export{V as default}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/KVStore-DzgumyT2.js b/backend/internal/server/ui_dist/assets/KVStore-DzgumyT2.js new file mode 100644 index 00000000..92842ea5 --- /dev/null +++ b/backend/internal/server/ui_dist/assets/KVStore-DzgumyT2.js @@ -0,0 +1 @@ +import{D as e,F as t,G as n,I as r,S as i,T as a,U as o,Z as s,c,d as l,gt as u,h as d,j as ee,k as f,m as p,r as m,s as h,u as g,vt as _,w as te}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as ne}from"./refresh-cw-Cn8qkf-v.js";import{t as v}from"./trash-2-DaeYqnW_.js";import{A as re,Bt as y,Lt as b,M as x,St as ie,_t as S,gt as ae,j as oe,pt as se,xt as ce}from"./index-DTqMKlE1.js";import{t as C}from"./Drawer-B98TBytl.js";import{t as w}from"./IconButton-CsCZOqWo.js";var le={class:`space-y-6`},ue={class:`flex items-start justify-between gap-4`},de={class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},fe={class:`flex items-center gap-2`},pe={class:`text-xs text-foreground-muted`},me={key:0},he={class:`flex items-center gap-2 flex-wrap`},ge={class:`relative flex-1 min-w-[260px] max-w-[420px]`},_e={key:1,class:`text-xs text-amber-400/80`},ve={class:`bg-background border border-border rounded-lg overflow-x-auto`},ye={class:`sm:hidden divide-y divide-border`},be=[`onClick`],xe={class:`flex items-start justify-between gap-2`},Se={class:`min-w-0 flex-1`},Ce={class:`font-mono text-xs text-white break-all`},we={class:`mt-1 font-mono text-xs text-foreground-muted break-all`},T={class:`mt-1 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-xs text-foreground-muted`},E={class:`font-mono`},D={key:0,class:`px-6 py-12 text-center text-sm text-foreground-muted`},O={class:`bg-surface px-1.5 py-0.5 rounded text-xs font-mono`},k={class:`hidden sm:table w-full text-sm text-left`},Te={class:`divide-y divide-border`},Ee=[`onClick`],De={class:`px-4 py-3 font-mono text-xs text-white truncate max-w-[360px]`},Oe={class:`px-4 py-3 font-mono text-xs text-foreground-muted truncate max-w-[420px] hidden md:table-cell`},ke={class:`px-4 py-3 hidden sm:table-cell`},Ae={key:1,class:`text-foreground-muted text-xs`},je={class:`px-4 py-3 text-xs font-mono text-foreground-muted hidden lg:table-cell`},Me={class:`px-4 py-3 text-xs text-foreground-muted hidden md:table-cell`},Ne={key:0},Pe={colspan:`6`,class:`px-4 py-12 text-center text-foreground-muted text-sm`},Fe={class:`bg-surface px-1.5 py-0.5 rounded text-xs font-mono`},Ie={key:0,class:`p-5 space-y-5 text-sm`},Le={class:`grid grid-cols-2 gap-3`},Re={class:`bg-surface border border-border rounded p-3 min-w-0`},ze={class:`text-xs text-white font-mono break-all`},Be={class:`bg-surface border border-border rounded p-3 min-w-0`},Ve={class:`bg-surface border border-border rounded p-3 min-w-0`},He={class:`text-xs text-white font-mono truncate`},Ue={class:`bg-surface border border-border rounded p-3 min-w-0`},We={class:`text-xs text-white font-mono`},Ge={class:`flex items-center justify-between mb-2`},Ke={key:0,class:`text-xs text-red-400`},qe={class:`flex items-center justify-between`},Je={class:`flex items-center gap-2`},Ye={class:`p-5 space-y-5 text-sm`},Xe={class:`flex items-center justify-between mb-2`},Ze={key:0,class:`text-xs text-red-400`},Qe={class:`flex items-center justify-end gap-2`},$e=31536e3,et={__name:`KVStore`,setup(et){let tt=se(),A=ae(),j=h(()=>tt.params.name),M=n([]),N=n(0),P=n(!1),F=n(!1),I=n(!1),L=n(``),R=o({open:!1,row:null,text:``,ttlSeconds:0,ttlTouched:!1,error:``}),z=o({open:!1,key:``,text:``,ttlSeconds:0,ttlTouched:!1,error:``}),B=e=>{let t=Number(e);return!Number.isFinite(t)||t<0||t>$e},V=h(()=>M.value.reduce((e,t)=>e+(t.size_bytes||0),0)),H=async()=>{F.value=!0;try{let e={limit:200};L.value&&(e.prefix=L.value);let t=await oe(j.value,e);M.value=t.data?.entries||[],N.value=t.data?.total??M.value.length,P.value=!!t.data?.truncated}catch(e){console.error(`kvList failed`,e),A.notify({title:`Failed to load KV`,message:e?.response?.data?.error?.message||`Unknown error`,danger:!0})}finally{F.value=!1}},U=null,nt=()=>{clearTimeout(U),U=setTimeout(H,250)},W=e=>{R.row=e,R.text=ot(e.value),R.ttlSeconds=e.expires_at?Math.max(0,Math.floor((new Date(e.expires_at)-Date.now())/1e3)):0,R.ttlTouched=!1,R.error=``,R.open=!0},rt=async()=>{if(B(R.ttlSeconds)){R.error=`TTL must be between 0 and 31536000 (1 year).`;return}let e;try{e=JSON.parse(R.text)}catch(e){R.error=`Invalid JSON: `+e.message;return}R.error=``,I.value=!0;try{let t={value:e};R.ttlTouched&&(t.ttl_seconds=R.ttlSeconds),await x(j.value,R.row.key,t),R.open=!1,await H()}catch(e){R.error=e?.response?.data?.error?.message||`Save failed`}finally{I.value=!1}},it=async()=>{R.row&&await A.ask({title:`Delete key?`,message:`"${R.row.key}" will be removed from this function's KV store. This cannot be undone.`,confirmLabel:`Delete`,danger:!0})&&(await q(R.row.key),R.open=!1)},at=()=>{z.key=``,z.text=``,z.ttlSeconds=0,z.ttlTouched=!1,z.error=``,z.open=!0},G=async()=>{let e=z.key.trim();if(!e){z.error=`Key is required`;return}if(B(z.ttlSeconds)){z.error=`TTL must be between 0 and 31536000 (1 year).`;return}let t=z.text.trim()||`""`,n;try{n=JSON.parse(t)}catch(e){z.error=`Invalid JSON: `+e.message;return}z.error=``,I.value=!0;try{let t={value:n};z.ttlTouched&&(t.ttl_seconds=z.ttlSeconds),await x(j.value,e,t),z.open=!1,await H()}catch(e){z.error=e?.response?.data?.error?.message||`Save failed`}finally{I.value=!1}},K=async e=>{await A.ask({title:`Delete key?`,message:`"${e.key}" will be removed from this function's KV store.`,confirmLabel:`Delete`,danger:!0})&&await q(e.key)},q=async e=>{try{await re(j.value,e),await H()}catch(e){A.notify({title:`Failed to delete key`,message:e?.response?.data?.error?.message||`Unknown error`,danger:!0})}},J=(e,t=3)=>{if(t<=0||typeof e!=`string`)return e;let n=e.trim();if(!n||!`{["tfn0123456789-`.includes(n[0]))return e;try{let e=JSON.parse(n);return J(e,t-1)}catch{return e}},ot=e=>{let t=J(e);try{return JSON.stringify(t,null,2)}catch{return String(e)}},Y=e=>{if(e==null)return`—`;let t=J(e);if(typeof t==`string`){let e=JSON.stringify(t);return e.length>80?e.slice(0,80)+`…`:e}try{let e=JSON.stringify(t);return e.length>80?e.slice(0,80)+`…`:e}catch{return String(t)}},X=e=>e==null?`—`:e<1024?e+` B`:e<1048576?(e/1024).toFixed(1)+` KB`:(e/1024/1024).toFixed(1)+` MB`,Z=e=>{if(!e)return`—`;let t=Date.now()-new Date(e).getTime();if(t<0)return`just now`;let n=Math.floor(t/1e3);if(n<60)return n+`s ago`;let r=Math.floor(n/60);if(r<60)return r+`m ago`;let i=Math.floor(r/60);return i<24?i+`h ago`:Math.floor(i/24)+`d ago`},st=e=>e?new Date(e).toLocaleString():`—`,Q=e=>{let t=new Date(e).getTime()-Date.now();if(t<=0)return`expired`;let n=Math.floor(t/1e3);if(n<60)return`in `+n+`s`;let r=Math.floor(n/60);if(r<60)return`in `+r+`m`;let i=Math.floor(r/60);return i<24?`in `+i+`h `+r%60+`m`:`in `+Math.floor(i/24)+`d `+i%24+`h`},$=e=>{if(!e)return`text-foreground-muted`;let t=new Date(e).getTime()-Date.now();return t<=6e4?`text-red-400`:t<=3e5?`text-amber-400`:`text-foreground-muted`};return a(H),i(H),te(()=>{clearTimeout(U)}),(n,i)=>{let a=ee(`router-link`);return e(),l(`div`,le,[c(`div`,ue,[c(`div`,null,[i[18]||=c(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` KV Store `,-1),c(`p`,de,[i[16]||=p(` JSON state for `,-1),d(a,{to:`/functions/${j.value}`,class:`text-white underline`},{default:t(()=>[p(_(j.value),1)]),_:1},8,[`to`]),i[17]||=p(` with optional TTL. `,-1)])]),c(`div`,fe,[c(`span`,pe,[p(_(N.value)+` `+_(N.value===1?`key`:`keys`)+` `,1),V.value>0?(e(),l(`span`,me,`· `+_(X(V.value)),1)):g(``,!0)]),d(S,{variant:`secondary`,size:`sm`,onClick:H},{default:t(()=>[d(s(ne),{class:u([`w-3.5 h-3.5`,{"animate-spin":F.value}])},null,8,[`class`]),i[19]||=p(` Refresh `,-1)]),_:1}),d(S,{size:`sm`,onClick:i[0]||=e=>at()},{default:t(()=>[d(s(ie),{class:`w-3.5 h-3.5`}),i[20]||=p(` Set key `,-1)]),_:1})])]),c(`div`,he,[c(`div`,ge,[d(s(ce),{class:`w-3.5 h-3.5 absolute left-2.5 top-1/2 -translate-y-1/2 text-foreground-muted/60 pointer-events-none`}),r(c(`input`,{"onUpdate:modelValue":i[1]||=e=>L.value=e,"aria-label":`Search keys by prefix`,placeholder:`Search by key prefix… (e.g. user:)`,class:`w-full bg-background border border-border rounded-md pl-8 pr-3 py-1.5 text-xs text-foreground placeholder-foreground-muted/60 focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,onInput:nt},null,544),[[b,L.value]])]),L.value?(e(),l(`button`,{key:0,class:`text-xs text-foreground-muted hover:text-white px-2 py-1.5 transition-colors`,onClick:i[2]||=e=>{L.value=``,H()}},` Clear `)):g(``,!0),P.value?(e(),l(`span`,_e,` Showing first `+_(M.value.length)+`. Narrow the prefix to see more. `,1)):g(``,!0)]),c(`div`,ve,[c(`ul`,ye,[(e(!0),l(m,null,f(M.value,t=>(e(),l(`li`,{key:t.key,class:`px-4 py-3 cursor-pointer hover:bg-surface-hover transition-colors`,onClick:e=>W(t)},[c(`div`,xe,[c(`div`,Se,[c(`div`,Ce,_(t.key),1),c(`div`,we,_(Y(t.value)),1),c(`div`,T,[t.expires_at?(e(),l(`span`,{key:0,class:u($(t.expires_at))},_(Q(t.expires_at)),3)):g(``,!0),c(`span`,E,_(X(t.size_bytes)),1),c(`span`,null,_(Z(t.updated_at)),1)])]),c(`div`,{class:`shrink-0`,onClick:i[3]||=y(()=>{},[`stop`])},[d(w,{icon:s(v),variant:`danger`,title:`Delete key`,onClick:e=>K(t)},null,8,[`icon`,`onClick`])])])],8,be))),128)),!F.value&&!M.value.length?(e(),l(`li`,D,[L.value?(e(),l(m,{key:0},[i[21]||=p(` No keys match `,-1),c(`code`,O,_(L.value),1),i[22]||=p(`. `,-1)],64)):(e(),l(m,{key:1},[i[23]||=p(` No keys yet. Values written with `,-1),i[24]||=c(`code`,{class:`font-mono text-xs`},`orva.kv.put()`,-1),i[25]||=p(` appear here. `,-1)],64))])):g(``,!0)]),c(`table`,k,[i[31]||=c(`thead`,{class:`text-xs text-foreground-muted uppercase bg-surface border-b border-border`},[c(`tr`,null,[c(`th`,{class:`px-4 py-3`},` Key `),c(`th`,{class:`px-4 py-3 hidden md:table-cell`},` Value preview `),c(`th`,{class:`px-4 py-3 w-28 hidden sm:table-cell`},` TTL `),c(`th`,{class:`px-4 py-3 w-20 hidden lg:table-cell`},` Size `),c(`th`,{class:`px-4 py-3 w-28 hidden md:table-cell`},` Updated `),c(`th`,{class:`px-4 py-3 w-10 text-right`})])],-1),c(`tbody`,Te,[(e(!0),l(m,null,f(M.value,t=>(e(),l(`tr`,{key:t.key,class:`hover:bg-surface-hover cursor-pointer transition-colors`,onClick:e=>W(t)},[c(`td`,De,_(t.key),1),c(`td`,Oe,_(Y(t.value)),1),c(`td`,ke,[t.expires_at?(e(),l(`span`,{key:0,class:u([`text-xs`,$(t.expires_at)])},_(Q(t.expires_at)),3)):(e(),l(`span`,Ae,_(s(`—`)),1))]),c(`td`,je,_(X(t.size_bytes)),1),c(`td`,Me,_(Z(t.updated_at)),1),c(`td`,{class:`px-4 py-3 text-right`,onClick:i[4]||=y(()=>{},[`stop`])},[d(w,{icon:s(v),variant:`danger`,title:`Delete key`,onClick:e=>K(t)},null,8,[`icon`,`onClick`])])],8,Ee))),128)),!F.value&&!M.value.length?(e(),l(`tr`,Ne,[c(`td`,Pe,[L.value?(e(),l(m,{key:0},[i[26]||=p(` No keys match `,-1),c(`code`,Fe,_(L.value),1),i[27]||=p(`. `,-1)],64)):(e(),l(m,{key:1},[i[28]||=p(` No keys yet. Values written with `,-1),i[29]||=c(`code`,{class:`font-mono text-xs`},`orva.kv.put()`,-1),i[30]||=p(` appear here. `,-1)],64))])])):g(``,!0)])])]),d(C,{modelValue:R.open,"onUpdate:modelValue":i[9]||=e=>R.open=e,title:R.row?R.row.key:`Inspect key`,width:`640px`},{footer:t(()=>[c(`div`,qe,[d(S,{variant:`danger`,size:`sm`,disabled:I.value,onClick:it},{default:t(()=>[d(s(v),{class:`w-3.5 h-3.5`}),i[38]||=p(` Delete `,-1)]),_:1},8,[`disabled`]),c(`div`,Je,[d(S,{variant:`ghost`,size:`sm`,onClick:i[8]||=e=>R.open=!1},{default:t(()=>[...i[39]||=[p(` Cancel `,-1)]]),_:1}),d(S,{size:`sm`,disabled:I.value,loading:I.value,onClick:rt},{default:t(()=>[...i[40]||=[p(` Save `,-1)]]),_:1},8,[`disabled`,`loading`])])])]),default:t(()=>[R.row?(e(),l(`div`,Ie,[c(`div`,Le,[c(`div`,Re,[i[32]||=c(`div`,{class:`text-xs uppercase tracking-wider text-foreground-muted mb-1`},` Key `,-1),c(`div`,ze,_(R.row.key),1)]),c(`div`,Be,[i[33]||=c(`div`,{class:`text-xs uppercase tracking-wider text-foreground-muted mb-1`},` TTL `,-1),c(`div`,{class:u([`text-xs text-white font-mono`,$(R.row.expires_at)])},_(R.row.expires_at?Q(R.row.expires_at):`Never`),3)]),c(`div`,Ve,[i[34]||=c(`div`,{class:`text-xs uppercase tracking-wider text-foreground-muted mb-1`},` Updated `,-1),c(`div`,He,_(st(R.row.updated_at)),1)]),c(`div`,Ue,[i[35]||=c(`div`,{class:`text-xs uppercase tracking-wider text-foreground-muted mb-1`},` Size `,-1),c(`div`,We,_(X(R.row.size_bytes)),1)])]),c(`div`,null,[i[36]||=c(`label`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},` TTL seconds (0 = never) `,-1),r(c(`input`,{"onUpdate:modelValue":i[5]||=e=>R.ttlSeconds=e,type:`number`,min:`0`,max:`31536000`,class:`mt-2 w-full bg-surface border border-border rounded px-3 py-2 text-sm text-white font-mono focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,onInput:i[6]||=e=>R.ttlTouched=!0},null,544),[[b,R.ttlSeconds,void 0,{number:!0}]]),c(`p`,{class:u([`text-xs mt-1.5`,B(R.ttlSeconds)?`text-danger-fg`:`text-foreground-muted`])},` Must be between 0 and 31536000 (1 year). `,2)]),c(`div`,null,[c(`div`,Ge,[i[37]||=c(`label`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},`Value (JSON)`,-1),R.error?(e(),l(`span`,Ke,_(R.error),1)):g(``,!0)]),r(c(`textarea`,{"onUpdate:modelValue":i[7]||=e=>R.text=e,rows:`14`,spellcheck:`false`,class:`w-full bg-surface border border-border rounded p-3 text-xs text-white font-mono leading-relaxed focus:outline-none focus:border-white whitespace-pre overflow-x-auto`},null,512),[[b,R.text]])])])):g(``,!0)]),_:1},8,[`modelValue`,`title`]),d(C,{modelValue:z.open,"onUpdate:modelValue":i[15]||=e=>z.open=e,title:`Set key`,width:`640px`},{footer:t(()=>[c(`div`,Qe,[d(S,{variant:`ghost`,size:`sm`,onClick:i[14]||=e=>z.open=!1},{default:t(()=>[...i[44]||=[p(` Cancel `,-1)]]),_:1}),d(S,{size:`sm`,disabled:I.value||!z.key.trim(),loading:I.value,onClick:G},{default:t(()=>[...i[45]||=[p(` Save `,-1)]]),_:1},8,[`disabled`,`loading`])])]),default:t(()=>[c(`div`,Ye,[c(`div`,null,[i[41]||=c(`label`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},`Key`,-1),r(c(`input`,{"onUpdate:modelValue":i[10]||=e=>z.key=e,placeholder:`e.g. user:42`,class:`mt-2 w-full bg-surface border border-border rounded px-3 py-2 text-sm text-white font-mono focus:outline-none focus:border-white`,spellcheck:`false`},null,512),[[b,z.key]])]),c(`div`,null,[i[42]||=c(`label`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},` TTL seconds (0 = never) `,-1),r(c(`input`,{"onUpdate:modelValue":i[11]||=e=>z.ttlSeconds=e,type:`number`,min:`0`,max:`31536000`,class:`mt-2 w-full bg-surface border border-border rounded px-3 py-2 text-sm text-white font-mono focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,onInput:i[12]||=e=>z.ttlTouched=!0},null,544),[[b,z.ttlSeconds,void 0,{number:!0}]]),c(`p`,{class:u([`text-xs mt-1.5`,B(z.ttlSeconds)?`text-danger-fg`:`text-foreground-muted`])},` Must be between 0 and 31536000 (1 year). `,2)]),c(`div`,null,[c(`div`,Xe,[i[43]||=c(`label`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},`Value (JSON)`,-1),z.error?(e(),l(`span`,Ze,_(z.error),1)):g(``,!0)]),r(c(`textarea`,{"onUpdate:modelValue":i[13]||=e=>z.text=e,rows:`14`,spellcheck:`false`,placeholder:`{"hello": "world"}`,class:`w-full bg-surface border border-border rounded p-3 text-xs text-white font-mono leading-relaxed focus:outline-none focus:border-white whitespace-pre overflow-x-auto`},null,512),[[b,z.text]])])])]),_:1},8,[`modelValue`])])}}};export{et as default}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/KVStore-Ly56eJIu.js b/backend/internal/server/ui_dist/assets/KVStore-Ly56eJIu.js deleted file mode 100644 index b1c96444..00000000 --- a/backend/internal/server/ui_dist/assets/KVStore-Ly56eJIu.js +++ /dev/null @@ -1 +0,0 @@ -import{D as e,F as t,G as n,I as r,S as i,T as a,U as o,Z as s,c,d as l,gt as u,h as d,j as ee,k as f,m as p,r as m,s as h,u as g,vt as _,w as te}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as ne}from"./refresh-cw-Bn_tp4gH.js";import{t as v}from"./trash-2-Bj4AFUfw.js";import{A as re,Bt as y,Lt as b,M as x,St as ie,_t as S,gt as ae,j as oe,pt as se,xt as ce}from"./index-C11HmDXN.js";import{t as C}from"./Drawer-ZggviMzu.js";import{t as w}from"./IconButton-CsCZOqWo.js";var le={class:`space-y-6`},ue={class:`flex items-start justify-between gap-4`},de={class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},fe={class:`flex items-center gap-2`},pe={class:`text-xs text-foreground-muted`},me={key:0},he={class:`flex items-center gap-2 flex-wrap`},ge={class:`relative flex-1 min-w-[260px] max-w-[420px]`},_e={key:1,class:`text-[11px] text-amber-400/80`},ve={class:`bg-background border border-border rounded-lg overflow-x-auto`},ye={class:`sm:hidden divide-y divide-border`},be=[`onClick`],xe={class:`flex items-start justify-between gap-2`},Se={class:`min-w-0 flex-1`},Ce={class:`font-mono text-xs text-white break-all`},we={class:`mt-1 font-mono text-[11px] text-foreground-muted break-all`},T={class:`mt-1 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-[11px] text-foreground-muted`},E={class:`font-mono`},D={key:0,class:`px-6 py-12 text-center text-sm text-foreground-muted`},O={class:`bg-surface px-1.5 py-0.5 rounded text-xs font-mono`},k={class:`hidden sm:table w-full text-sm text-left`},Te={class:`divide-y divide-border`},Ee=[`onClick`],De={class:`px-4 py-3 font-mono text-xs text-white truncate max-w-[360px]`},Oe={class:`px-4 py-3 font-mono text-xs text-foreground-muted truncate max-w-[420px] hidden md:table-cell`},ke={class:`px-4 py-3 hidden sm:table-cell`},Ae={key:1,class:`text-foreground-muted text-xs`},je={class:`px-4 py-3 text-xs font-mono text-foreground-muted hidden lg:table-cell`},Me={class:`px-4 py-3 text-xs text-foreground-muted hidden md:table-cell`},Ne={key:0},Pe={colspan:`6`,class:`px-4 py-12 text-center text-foreground-muted text-sm`},Fe={class:`bg-surface px-1.5 py-0.5 rounded text-xs font-mono`},Ie={key:0,class:`p-5 space-y-5 text-sm`},Le={class:`grid grid-cols-2 gap-3`},Re={class:`bg-surface border border-border rounded p-3 min-w-0`},ze={class:`text-xs text-white font-mono break-all`},Be={class:`bg-surface border border-border rounded p-3 min-w-0`},Ve={class:`bg-surface border border-border rounded p-3 min-w-0`},He={class:`text-xs text-white font-mono truncate`},Ue={class:`bg-surface border border-border rounded p-3 min-w-0`},We={class:`text-xs text-white font-mono`},Ge={class:`flex items-center justify-between mb-2`},Ke={key:0,class:`text-xs text-red-400`},qe={class:`flex items-center justify-between`},Je={class:`flex items-center gap-2`},Ye={class:`p-5 space-y-5 text-sm`},Xe={class:`flex items-center justify-between mb-2`},Ze={key:0,class:`text-xs text-red-400`},Qe={class:`flex items-center justify-end gap-2`},$e=31536e3,et={__name:`KVStore`,setup(et){let tt=se(),A=ae(),j=h(()=>tt.params.name),M=n([]),N=n(0),P=n(!1),F=n(!1),I=n(!1),L=n(``),R=o({open:!1,row:null,text:``,ttlSeconds:0,error:``}),z=o({open:!1,key:``,text:``,ttlSeconds:0,error:``}),B=e=>{let t=Number(e);return!Number.isFinite(t)||t<0||t>$e},V=h(()=>M.value.reduce((e,t)=>e+(t.size_bytes||0),0)),H=async()=>{F.value=!0;try{let e={limit:200};L.value&&(e.prefix=L.value);let t=await oe(j.value,e);M.value=t.data?.entries||[],N.value=t.data?.total??M.value.length,P.value=!!t.data?.truncated}catch(e){console.error(`kvList failed`,e),A.notify({title:`Failed to load KV`,message:e?.response?.data?.error?.message||`Unknown error`,danger:!0})}finally{F.value=!1}},U=null,nt=()=>{clearTimeout(U),U=setTimeout(H,250)},W=e=>{R.row=e,R.text=ot(e.value),R.ttlSeconds=e.expires_at?Math.max(0,Math.floor((new Date(e.expires_at)-Date.now())/1e3)):0,R.error=``,R.open=!0},rt=async()=>{if(B(R.ttlSeconds)){R.error=`TTL must be between 0 and 31536000 (1 year).`;return}let e;try{e=JSON.parse(R.text)}catch(e){R.error=`Invalid JSON: `+e.message;return}R.error=``,I.value=!0;try{await x(j.value,R.row.key,{value:e,ttl_seconds:R.ttlSeconds||0}),R.open=!1,await H()}catch(e){R.error=e?.response?.data?.error?.message||`Save failed`}finally{I.value=!1}},it=async()=>{R.row&&await A.ask({title:`Delete key?`,message:`"${R.row.key}" will be removed from this function's KV store. This cannot be undone.`,confirmLabel:`Delete`,danger:!0})&&(await q(R.row.key),R.open=!1)},at=()=>{z.key=``,z.text=``,z.ttlSeconds=0,z.error=``,z.open=!0},G=async()=>{let e=z.key.trim();if(!e){z.error=`Key is required`;return}if(B(z.ttlSeconds)){z.error=`TTL must be between 0 and 31536000 (1 year).`;return}let t=z.text.trim()||`""`,n;try{n=JSON.parse(t)}catch(e){z.error=`Invalid JSON: `+e.message;return}z.error=``,I.value=!0;try{await x(j.value,e,{value:n,ttl_seconds:z.ttlSeconds||0}),z.open=!1,await H()}catch(e){z.error=e?.response?.data?.error?.message||`Save failed`}finally{I.value=!1}},K=async e=>{await A.ask({title:`Delete key?`,message:`"${e.key}" will be removed from this function's KV store.`,confirmLabel:`Delete`,danger:!0})&&await q(e.key)},q=async e=>{try{await re(j.value,e),await H()}catch(e){A.notify({title:`Failed to delete key`,message:e?.response?.data?.error?.message||`Unknown error`,danger:!0})}},J=(e,t=3)=>{if(t<=0||typeof e!=`string`)return e;let n=e.trim();if(!n||!`{["tfn0123456789-`.includes(n[0]))return e;try{let e=JSON.parse(n);return J(e,t-1)}catch{return e}},ot=e=>{let t=J(e);try{return JSON.stringify(t,null,2)}catch{return String(e)}},Y=e=>{if(e==null)return`—`;let t=J(e);if(typeof t==`string`){let e=JSON.stringify(t);return e.length>80?e.slice(0,80)+`…`:e}try{let e=JSON.stringify(t);return e.length>80?e.slice(0,80)+`…`:e}catch{return String(t)}},X=e=>e==null?`—`:e<1024?e+` B`:e<1048576?(e/1024).toFixed(1)+` KB`:(e/1024/1024).toFixed(1)+` MB`,Z=e=>{if(!e)return`—`;let t=Date.now()-new Date(e).getTime();if(t<0)return`just now`;let n=Math.floor(t/1e3);if(n<60)return n+`s ago`;let r=Math.floor(n/60);if(r<60)return r+`m ago`;let i=Math.floor(r/60);return i<24?i+`h ago`:Math.floor(i/24)+`d ago`},st=e=>e?new Date(e).toLocaleString():`—`,Q=e=>{let t=new Date(e).getTime()-Date.now();if(t<=0)return`expired`;let n=Math.floor(t/1e3);if(n<60)return`in `+n+`s`;let r=Math.floor(n/60);if(r<60)return`in `+r+`m`;let i=Math.floor(r/60);return i<24?`in `+i+`h `+r%60+`m`:`in `+Math.floor(i/24)+`d `+i%24+`h`},$=e=>{if(!e)return`text-foreground-muted`;let t=new Date(e).getTime()-Date.now();return t<=6e4?`text-red-400`:t<=3e5?`text-amber-400`:`text-foreground-muted`};return a(H),i(H),te(()=>{clearTimeout(U)}),(n,i)=>{let a=ee(`router-link`);return e(),l(`div`,le,[c(`div`,ue,[c(`div`,null,[i[16]||=c(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` KV Store `,-1),c(`p`,de,[i[14]||=p(` JSON state for `,-1),d(a,{to:`/functions/${j.value}`,class:`text-white underline`},{default:t(()=>[p(_(j.value),1)]),_:1},8,[`to`]),i[15]||=p(` with optional TTL. `,-1)])]),c(`div`,fe,[c(`span`,pe,[p(_(N.value)+` `+_(N.value===1?`key`:`keys`)+` `,1),V.value>0?(e(),l(`span`,me,`· `+_(X(V.value)),1)):g(``,!0)]),d(S,{variant:`secondary`,size:`sm`,onClick:H},{default:t(()=>[d(s(ne),{class:u([`w-3.5 h-3.5`,{"animate-spin":F.value}])},null,8,[`class`]),i[17]||=p(` Refresh `,-1)]),_:1}),d(S,{size:`sm`,onClick:i[0]||=e=>at()},{default:t(()=>[d(s(ie),{class:`w-3.5 h-3.5`}),i[18]||=p(` Set key `,-1)]),_:1})])]),c(`div`,he,[c(`div`,ge,[d(s(ce),{class:`w-3.5 h-3.5 absolute left-2.5 top-1/2 -translate-y-1/2 text-foreground-muted/60 pointer-events-none`}),r(c(`input`,{"onUpdate:modelValue":i[1]||=e=>L.value=e,"aria-label":`Search keys by prefix`,placeholder:`Search by key prefix… (e.g. user:)`,class:`w-full bg-background border border-border rounded-md pl-8 pr-3 py-1.5 text-xs text-foreground placeholder-foreground-muted/60 focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,onInput:nt},null,544),[[b,L.value]])]),L.value?(e(),l(`button`,{key:0,class:`text-[11px] text-foreground-muted hover:text-white px-2 py-1.5 transition-colors`,onClick:i[2]||=e=>{L.value=``,H()}},` Clear `)):g(``,!0),P.value?(e(),l(`span`,_e,` Showing first `+_(M.value.length)+`. Narrow the prefix to see more. `,1)):g(``,!0)]),c(`div`,ve,[c(`ul`,ye,[(e(!0),l(m,null,f(M.value,t=>(e(),l(`li`,{key:t.key,class:`px-4 py-3 cursor-pointer hover:bg-surface-hover transition-colors`,onClick:e=>W(t)},[c(`div`,xe,[c(`div`,Se,[c(`div`,Ce,_(t.key),1),c(`div`,we,_(Y(t.value)),1),c(`div`,T,[t.expires_at?(e(),l(`span`,{key:0,class:u($(t.expires_at))},_(Q(t.expires_at)),3)):g(``,!0),c(`span`,E,_(X(t.size_bytes)),1),c(`span`,null,_(Z(t.updated_at)),1)])]),c(`div`,{class:`shrink-0`,onClick:i[3]||=y(()=>{},[`stop`])},[d(w,{icon:s(v),variant:`danger`,title:`Delete key`,onClick:e=>K(t)},null,8,[`icon`,`onClick`])])])],8,be))),128)),!F.value&&!M.value.length?(e(),l(`li`,D,[L.value?(e(),l(m,{key:0},[i[19]||=p(` No keys match `,-1),c(`code`,O,_(L.value),1),i[20]||=p(`. `,-1)],64)):(e(),l(m,{key:1},[i[21]||=p(` No keys yet. Values written with `,-1),i[22]||=c(`code`,{class:`font-mono text-xs`},`orva.kv.put()`,-1),i[23]||=p(` appear here. `,-1)],64))])):g(``,!0)]),c(`table`,k,[i[29]||=c(`thead`,{class:`text-xs text-foreground-muted uppercase bg-surface border-b border-border`},[c(`tr`,null,[c(`th`,{class:`px-4 py-3`},` Key `),c(`th`,{class:`px-4 py-3 hidden md:table-cell`},` Value preview `),c(`th`,{class:`px-4 py-3 w-28 hidden sm:table-cell`},` TTL `),c(`th`,{class:`px-4 py-3 w-20 hidden lg:table-cell`},` Size `),c(`th`,{class:`px-4 py-3 w-28 hidden md:table-cell`},` Updated `),c(`th`,{class:`px-4 py-3 w-10 text-right`})])],-1),c(`tbody`,Te,[(e(!0),l(m,null,f(M.value,t=>(e(),l(`tr`,{key:t.key,class:`hover:bg-surface-hover cursor-pointer transition-colors`,onClick:e=>W(t)},[c(`td`,De,_(t.key),1),c(`td`,Oe,_(Y(t.value)),1),c(`td`,ke,[t.expires_at?(e(),l(`span`,{key:0,class:u([`text-xs`,$(t.expires_at)])},_(Q(t.expires_at)),3)):(e(),l(`span`,Ae,_(s(`—`)),1))]),c(`td`,je,_(X(t.size_bytes)),1),c(`td`,Me,_(Z(t.updated_at)),1),c(`td`,{class:`px-4 py-3 text-right`,onClick:i[4]||=y(()=>{},[`stop`])},[d(w,{icon:s(v),variant:`danger`,title:`Delete key`,onClick:e=>K(t)},null,8,[`icon`,`onClick`])])],8,Ee))),128)),!F.value&&!M.value.length?(e(),l(`tr`,Ne,[c(`td`,Pe,[L.value?(e(),l(m,{key:0},[i[24]||=p(` No keys match `,-1),c(`code`,Fe,_(L.value),1),i[25]||=p(`. `,-1)],64)):(e(),l(m,{key:1},[i[26]||=p(` No keys yet. Values written with `,-1),i[27]||=c(`code`,{class:`font-mono text-xs`},`orva.kv.put()`,-1),i[28]||=p(` appear here. `,-1)],64))])])):g(``,!0)])])]),d(C,{modelValue:R.open,"onUpdate:modelValue":i[8]||=e=>R.open=e,title:R.row?R.row.key:`Inspect key`,width:`640px`},{footer:t(()=>[c(`div`,qe,[d(S,{variant:`danger`,size:`sm`,disabled:I.value,onClick:it},{default:t(()=>[d(s(v),{class:`w-3.5 h-3.5`}),i[36]||=p(` Delete `,-1)]),_:1},8,[`disabled`]),c(`div`,Je,[d(S,{variant:`ghost`,size:`sm`,onClick:i[7]||=e=>R.open=!1},{default:t(()=>[...i[37]||=[p(` Cancel `,-1)]]),_:1}),d(S,{size:`sm`,disabled:I.value,loading:I.value,onClick:rt},{default:t(()=>[...i[38]||=[p(` Save `,-1)]]),_:1},8,[`disabled`,`loading`])])])]),default:t(()=>[R.row?(e(),l(`div`,Ie,[c(`div`,Le,[c(`div`,Re,[i[30]||=c(`div`,{class:`text-[10px] uppercase tracking-wider text-foreground-muted mb-1`},` Key `,-1),c(`div`,ze,_(R.row.key),1)]),c(`div`,Be,[i[31]||=c(`div`,{class:`text-[10px] uppercase tracking-wider text-foreground-muted mb-1`},` TTL `,-1),c(`div`,{class:u([`text-xs text-white font-mono`,$(R.row.expires_at)])},_(R.row.expires_at?Q(R.row.expires_at):`Never`),3)]),c(`div`,Ve,[i[32]||=c(`div`,{class:`text-[10px] uppercase tracking-wider text-foreground-muted mb-1`},` Updated `,-1),c(`div`,He,_(st(R.row.updated_at)),1)]),c(`div`,Ue,[i[33]||=c(`div`,{class:`text-[10px] uppercase tracking-wider text-foreground-muted mb-1`},` Size `,-1),c(`div`,We,_(X(R.row.size_bytes)),1)])]),c(`div`,null,[i[34]||=c(`label`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},` TTL seconds (0 = never) `,-1),r(c(`input`,{"onUpdate:modelValue":i[5]||=e=>R.ttlSeconds=e,type:`number`,min:`0`,max:`31536000`,class:`mt-2 w-full bg-surface border border-border rounded px-3 py-2 text-sm text-white font-mono focus:outline-none focus:ring-1 focus:ring-white focus:border-white`},null,512),[[b,R.ttlSeconds,void 0,{number:!0}]]),c(`p`,{class:u([`text-[11px] mt-1.5`,B(R.ttlSeconds)?`text-danger-fg`:`text-foreground-muted`])},` Must be between 0 and 31536000 (1 year). `,2)]),c(`div`,null,[c(`div`,Ge,[i[35]||=c(`label`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},`Value (JSON)`,-1),R.error?(e(),l(`span`,Ke,_(R.error),1)):g(``,!0)]),r(c(`textarea`,{"onUpdate:modelValue":i[6]||=e=>R.text=e,rows:`14`,spellcheck:`false`,class:`w-full bg-surface border border-border rounded p-3 text-xs text-white font-mono leading-relaxed focus:outline-none focus:border-white whitespace-pre overflow-x-auto`},null,512),[[b,R.text]])])])):g(``,!0)]),_:1},8,[`modelValue`,`title`]),d(C,{modelValue:z.open,"onUpdate:modelValue":i[13]||=e=>z.open=e,title:`Set key`,width:`640px`},{footer:t(()=>[c(`div`,Qe,[d(S,{variant:`ghost`,size:`sm`,onClick:i[12]||=e=>z.open=!1},{default:t(()=>[...i[42]||=[p(` Cancel `,-1)]]),_:1}),d(S,{size:`sm`,disabled:I.value||!z.key.trim(),loading:I.value,onClick:G},{default:t(()=>[...i[43]||=[p(` Save `,-1)]]),_:1},8,[`disabled`,`loading`])])]),default:t(()=>[c(`div`,Ye,[c(`div`,null,[i[39]||=c(`label`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},`Key`,-1),r(c(`input`,{"onUpdate:modelValue":i[9]||=e=>z.key=e,placeholder:`e.g. user:42`,class:`mt-2 w-full bg-surface border border-border rounded px-3 py-2 text-sm text-white font-mono focus:outline-none focus:border-white`,spellcheck:`false`},null,512),[[b,z.key]])]),c(`div`,null,[i[40]||=c(`label`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},` TTL seconds (0 = never) `,-1),r(c(`input`,{"onUpdate:modelValue":i[10]||=e=>z.ttlSeconds=e,type:`number`,min:`0`,max:`31536000`,class:`mt-2 w-full bg-surface border border-border rounded px-3 py-2 text-sm text-white font-mono focus:outline-none focus:ring-1 focus:ring-white focus:border-white`},null,512),[[b,z.ttlSeconds,void 0,{number:!0}]]),c(`p`,{class:u([`text-[11px] mt-1.5`,B(z.ttlSeconds)?`text-danger-fg`:`text-foreground-muted`])},` Must be between 0 and 31536000 (1 year). `,2)]),c(`div`,null,[c(`div`,Xe,[i[41]||=c(`label`,{class:`text-xs uppercase tracking-wider text-foreground-muted`},`Value (JSON)`,-1),z.error?(e(),l(`span`,Ze,_(z.error),1)):g(``,!0)]),r(c(`textarea`,{"onUpdate:modelValue":i[11]||=e=>z.text=e,rows:`14`,spellcheck:`false`,placeholder:`{"hello": "world"}`,class:`w-full bg-surface border border-border rounded p-3 text-xs text-white font-mono leading-relaxed focus:outline-none focus:border-white whitespace-pre overflow-x-auto`},null,512),[[b,z.text]])])])]),_:1},8,[`modelValue`])])}}};export{et as default}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/Login-CGYB-z1p.js b/backend/internal/server/ui_dist/assets/Login-DscOTSMy.js similarity index 93% rename from backend/internal/server/ui_dist/assets/Login-CGYB-z1p.js rename to backend/internal/server/ui_dist/assets/Login-DscOTSMy.js index ff0cf54a..bc8c125e 100644 --- a/backend/internal/server/ui_dist/assets/Login-CGYB-z1p.js +++ b/backend/internal/server/ui_dist/assets/Login-DscOTSMy.js @@ -1 +1 @@ -import{D as e,F as t,G as n,I as r,T as i,Z as a,c as o,d as s,h as c,m as l,u,vt as d}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as f}from"./circle-alert-BIdKIOKw.js";import{Bt as p,Lt as m,_t as h,dt as g,jt as _,lt as v,mt as y}from"./index-C11HmDXN.js";var b=_(`log-in`,[[`path`,{d:`m10 17 5-5-5-5`,key:`1bsop3`}],[`path`,{d:`M15 12H3`,key:`6jk70r`}],[`path`,{d:`M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4`,key:`u53s6r`}]]),x={class:`min-h-screen flex items-center justify-center bg-background p-4`},S={class:`w-full max-w-md`},C={class:`text-center mb-8`},w={class:`flex items-center justify-center gap-3 mb-4`},T={class:`bg-surface border border-border rounded-lg shadow-lg p-8`},E=[`disabled`],D=[`disabled`],O={key:0,class:`bg-danger-tint border border-danger-ring rounded-md px-4 py-3 flex items-start gap-2`,role:`alert`},k={class:`text-sm text-danger-fg`},A={__name:`Login`,setup(_){let A=y(),j=g();i(async()=>{await j.fetchAuthStatus()===!1&&A.replace(`/onboarding`)});let M=n({username:``,password:``}),N=n(``),P=n(!1),F=async()=>{N.value=``,P.value=!0;let e=await j.login(M.value.username,M.value.password);P.value=!1,e.success?A.push(`/`):e.code===`ONBOARDING_REQUIRED`?A.push(`/onboarding`):N.value=e.error};return(n,i)=>(e(),s(`div`,x,[o(`div`,S,[o(`div`,C,[o(`div`,w,[c(v,{class:`w-12 h-12`}),i[2]||=o(`h1`,{class:`text-3xl font-bold tracking-tight text-foreground`},` Orva `,-1)]),i[3]||=o(`p`,{class:`text-foreground-muted text-sm`},` Sign in to this instance `,-1)]),o(`div`,T,[i[7]||=o(`h2`,{class:`text-xl font-semibold text-foreground mb-6`},` Sign In `,-1),o(`form`,{class:`space-y-5`,onSubmit:p(F,[`prevent`])},[o(`div`,null,[i[4]||=o(`label`,{for:`login-username`,class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-2`},` Username `,-1),r(o(`input`,{id:`login-username`,"onUpdate:modelValue":i[0]||=e=>M.value.username=e,type:`text`,required:``,autocomplete:`username`,class:`w-full bg-background border border-border rounded-md px-4 py-2.5 text-sm text-foreground placeholder-foreground-muted focus:outline-none focus:ring-2 focus:ring-primary transition-colors`,placeholder:`Enter your username`,disabled:P.value},null,8,E),[[m,M.value.username]])]),o(`div`,null,[i[5]||=o(`label`,{for:`login-password`,class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-2`},` Password `,-1),r(o(`input`,{id:`login-password`,"onUpdate:modelValue":i[1]||=e=>M.value.password=e,type:`password`,required:``,autocomplete:`current-password`,class:`w-full bg-background border border-border rounded-md px-4 py-2.5 text-sm text-foreground placeholder-foreground-muted focus:outline-none focus:ring-2 focus:ring-primary transition-colors`,placeholder:`Enter your password`,disabled:P.value},null,8,D),[[m,M.value.password]])]),N.value?(e(),s(`div`,O,[c(a(f),{class:`w-5 h-5 text-danger-fg shrink-0 mt-0.5`}),o(`p`,k,d(N.value),1)])):u(``,!0),c(h,{type:`submit`,class:`w-full`,loading:P.value,disabled:!M.value.username||!M.value.password||P.value},{default:t(()=>[c(a(b),{class:`w-4 h-4`}),i[6]||=l(` Sign In `,-1)]),_:1},8,[`loading`,`disabled`])],32)])])]))}};export{A as default}; \ No newline at end of file +import{D as e,F as t,G as n,I as r,T as i,Z as a,c as o,d as s,h as c,m as l,u,vt as d}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as f}from"./circle-alert-BnUYYhGE.js";import{Bt as p,Lt as m,_t as h,dt as g,jt as _,lt as v,mt as y}from"./index-DTqMKlE1.js";var b=_(`log-in`,[[`path`,{d:`m10 17 5-5-5-5`,key:`1bsop3`}],[`path`,{d:`M15 12H3`,key:`6jk70r`}],[`path`,{d:`M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4`,key:`u53s6r`}]]),x={class:`min-h-screen flex items-center justify-center bg-background p-4`},S={class:`w-full max-w-md`},C={class:`text-center mb-8`},w={class:`flex items-center justify-center gap-3 mb-4`},T={class:`bg-surface border border-border rounded-lg shadow-lg p-8`},E=[`disabled`],D=[`disabled`],O={key:0,class:`bg-danger-tint border border-danger-ring rounded-md px-4 py-3 flex items-start gap-2`,role:`alert`},k={class:`text-sm text-danger-fg`},A={__name:`Login`,setup(_){let A=y(),j=g();i(async()=>{await j.fetchAuthStatus()===!1&&A.replace(`/onboarding`)});let M=n({username:``,password:``}),N=n(``),P=n(!1),F=async()=>{N.value=``,P.value=!0;let e=await j.login(M.value.username,M.value.password);P.value=!1,e.success?A.push(`/`):e.code===`ONBOARDING_REQUIRED`?A.push(`/onboarding`):N.value=e.error};return(n,i)=>(e(),s(`div`,x,[o(`div`,S,[o(`div`,C,[o(`div`,w,[c(v,{class:`w-12 h-12`}),i[2]||=o(`h1`,{class:`text-3xl font-bold tracking-tight text-foreground`},` Orva `,-1)]),i[3]||=o(`p`,{class:`text-foreground-muted text-sm`},` Sign in to this instance `,-1)]),o(`div`,T,[i[7]||=o(`h2`,{class:`text-xl font-semibold text-foreground mb-6`},` Sign In `,-1),o(`form`,{class:`space-y-5`,onSubmit:p(F,[`prevent`])},[o(`div`,null,[i[4]||=o(`label`,{for:`login-username`,class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-2`},` Username `,-1),r(o(`input`,{id:`login-username`,"onUpdate:modelValue":i[0]||=e=>M.value.username=e,type:`text`,required:``,autocomplete:`username`,class:`w-full bg-background border border-border rounded-md px-4 py-2.5 text-sm text-foreground placeholder-foreground-muted focus:outline-none focus:ring-2 focus:ring-primary transition-colors`,placeholder:`Enter your username`,disabled:P.value},null,8,E),[[m,M.value.username]])]),o(`div`,null,[i[5]||=o(`label`,{for:`login-password`,class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-2`},` Password `,-1),r(o(`input`,{id:`login-password`,"onUpdate:modelValue":i[1]||=e=>M.value.password=e,type:`password`,required:``,autocomplete:`current-password`,class:`w-full bg-background border border-border rounded-md px-4 py-2.5 text-sm text-foreground placeholder-foreground-muted focus:outline-none focus:ring-2 focus:ring-primary transition-colors`,placeholder:`Enter your password`,disabled:P.value},null,8,D),[[m,M.value.password]])]),N.value?(e(),s(`div`,O,[c(a(f),{class:`w-5 h-5 text-danger-fg shrink-0 mt-0.5`}),o(`p`,k,d(N.value),1)])):u(``,!0),c(h,{type:`submit`,class:`w-full`,loading:P.value,disabled:!M.value.username||!M.value.password||P.value},{default:t(()=>[c(a(b),{class:`w-4 h-4`}),i[6]||=l(` Sign In `,-1)]),_:1},8,[`loading`,`disabled`])],32)])])]))}};export{A as default}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/Modal-BL-P3nUi.js b/backend/internal/server/ui_dist/assets/Modal-BAoZams6.js similarity index 97% rename from backend/internal/server/ui_dist/assets/Modal-BL-P3nUi.js rename to backend/internal/server/ui_dist/assets/Modal-BAoZams6.js index 8ff2d14c..b79c78aa 100644 --- a/backend/internal/server/ui_dist/assets/Modal-BL-P3nUi.js +++ b/backend/internal/server/ui_dist/assets/Modal-BAoZams6.js @@ -1 +1 @@ -import{A as e,D as t,E as n,F as r,G as i,M as a,T as o,Y as s,Z as c,a as l,c as u,d,gt as f,h as p,l as m,s as h,u as g,vt as _}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{Bt as v,Mt as y,ht as b,vt as x}from"./index-C11HmDXN.js";var S={class:`flex items-center justify-between px-5 py-3 border-b border-border shrink-0`},C={class:`flex items-center gap-2 min-w-0`},w=[`aria-label`],T={class:`p-5 overflow-y-auto scrollable flex-1 max-h-[calc(100dvh-9rem)] sm:max-h-[70vh]`},E={key:0,class:`px-5 py-3 border-t border-border flex items-center justify-end gap-2 bg-surface/40 shrink-0`},D=Object.assign({name:`CommonModal`},{__name:`Modal`,props:{modelValue:{type:Boolean,default:!1},title:{type:String,required:!0},icon:{type:[Object,Function],default:null},size:{type:String,default:`md`,validator:e=>[`sm`,`md`,`lg`,`xl`].includes(e)}},emits:[`update:modelValue`],setup(D,{emit:O}){let k=D,A=O,j=`modal-title-${Math.random().toString(36).slice(2,10)}`,M=i(null);b(M,s(k,`modelValue`));let N=h(()=>{switch(k.size){case`sm`:return`sm:max-w-sm`;case`lg`:return`sm:max-w-2xl`;case`xl`:return`sm:max-w-4xl`;default:return`sm:max-w-lg`}}),P=()=>A(`update:modelValue`,!1),F=e=>{e.key===`Escape`&&k.modelValue&&P()};return o(()=>window.addEventListener(`keydown`,F)),n(()=>window.removeEventListener(`keydown`,F)),(n,i)=>(t(),m(l,{to:`body`},[p(y,{name:`fade`},{default:r(()=>[D.modelValue?(t(),d(`div`,{key:0,class:`fixed inset-0 z-40 flex items-stretch sm:items-center justify-center overflow-y-auto bg-black/60 backdrop-blur-sm pt-safe pb-safe pl-safe pr-safe p-2 sm:p-4`,onClick:v(P,[`self`])},[u(`div`,{ref_key:`dialogRoot`,ref:M,class:f([`w-full bg-background border border-border rounded-lg shadow-xl my-0 sm:my-auto flex flex-col max-w-full`,N.value]),role:`dialog`,"aria-modal":`true`,"aria-labelledby":j},[u(`header`,S,[u(`div`,C,[D.icon?(t(),m(a(D.icon),{key:0,class:`w-4 h-4 text-foreground-muted shrink-0`})):g(``,!0),u(`h3`,{id:j,class:`text-sm font-semibold text-white tracking-tight truncate`},_(D.title),1)]),u(`button`,{class:`p-1.5 -mr-1.5 rounded text-foreground-muted hover:text-white hover:bg-surface-hover transition-colors touch-expand-iconbtn shrink-0`,"aria-label":`Close ${D.title}`,onClick:P},[p(c(x),{class:`w-4 h-4`})],8,w)]),u(`div`,T,[e(n.$slots,`default`)]),n.$slots.footer?(t(),d(`footer`,E,[e(n.$slots,`footer`)])):g(``,!0)],2)])):g(``,!0)]),_:3})]))}});export{D as t}; \ No newline at end of file +import{A as e,D as t,E as n,F as r,G as i,M as a,T as o,Y as s,Z as c,a as l,c as u,d,gt as f,h as p,l as m,s as h,u as g,vt as _}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{Bt as v,Mt as y,ht as b,vt as x}from"./index-DTqMKlE1.js";var S={class:`flex items-center justify-between px-5 py-3 border-b border-border shrink-0`},C={class:`flex items-center gap-2 min-w-0`},w=[`aria-label`],T={class:`p-5 overflow-y-auto scrollable flex-1 max-h-[calc(100dvh-9rem)] sm:max-h-[70vh]`},E={key:0,class:`px-5 py-3 border-t border-border flex items-center justify-end gap-2 bg-surface/40 shrink-0`},D=Object.assign({name:`CommonModal`},{__name:`Modal`,props:{modelValue:{type:Boolean,default:!1},title:{type:String,required:!0},icon:{type:[Object,Function],default:null},size:{type:String,default:`md`,validator:e=>[`sm`,`md`,`lg`,`xl`].includes(e)}},emits:[`update:modelValue`],setup(D,{emit:O}){let k=D,A=O,j=`modal-title-${Math.random().toString(36).slice(2,10)}`,M=i(null);b(M,s(k,`modelValue`));let N=h(()=>{switch(k.size){case`sm`:return`sm:max-w-sm`;case`lg`:return`sm:max-w-2xl`;case`xl`:return`sm:max-w-4xl`;default:return`sm:max-w-lg`}}),P=()=>A(`update:modelValue`,!1),F=e=>{e.key===`Escape`&&k.modelValue&&P()};return o(()=>window.addEventListener(`keydown`,F)),n(()=>window.removeEventListener(`keydown`,F)),(n,i)=>(t(),m(l,{to:`body`},[p(y,{name:`fade`},{default:r(()=>[D.modelValue?(t(),d(`div`,{key:0,class:`fixed inset-0 z-40 flex items-stretch sm:items-center justify-center overflow-y-auto bg-black/60 backdrop-blur-sm pt-safe pb-safe pl-safe pr-safe p-2 sm:p-4`,onClick:v(P,[`self`])},[u(`div`,{ref_key:`dialogRoot`,ref:M,class:f([`w-full bg-background border border-border rounded-lg shadow-xl my-0 sm:my-auto flex flex-col max-w-full`,N.value]),role:`dialog`,"aria-modal":`true`,"aria-labelledby":j},[u(`header`,S,[u(`div`,C,[D.icon?(t(),m(a(D.icon),{key:0,class:`w-4 h-4 text-foreground-muted shrink-0`})):g(``,!0),u(`h3`,{id:j,class:`text-sm font-semibold text-white tracking-tight truncate`},_(D.title),1)]),u(`button`,{class:`p-1.5 -mr-1.5 rounded text-foreground-muted hover:text-white hover:bg-surface-hover transition-colors touch-expand-iconbtn shrink-0`,"aria-label":`Close ${D.title}`,onClick:P},[p(c(x),{class:`w-4 h-4`})],8,w)]),u(`div`,T,[e(n.$slots,`default`)]),n.$slots.footer?(t(),d(`footer`,E,[e(n.$slots,`footer`)])):g(``,!0)],2)])):g(``,!0)]),_:3})]))}});export{D as t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/ModelMenu-CCBXKr4H.js b/backend/internal/server/ui_dist/assets/ModelMenu-BtMjUXcx.js similarity index 98% rename from backend/internal/server/ui_dist/assets/ModelMenu-CCBXKr4H.js rename to backend/internal/server/ui_dist/assets/ModelMenu-BtMjUXcx.js index 79a452cf..ca79e166 100644 --- a/backend/internal/server/ui_dist/assets/ModelMenu-CCBXKr4H.js +++ b/backend/internal/server/ui_dist/assets/ModelMenu-BtMjUXcx.js @@ -1,4 +1,4 @@ -import{A as e,D as t,E as n,F as r,G as i,I as a,P as o,T as s,Z as c,_t as l,a as u,c as d,d as f,gt as p,h as m,k as h,l as g,r as _,s as v,u as y,vt as b,x}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{n as S}from"./pinia-B41TZNUX.js";import{t as C}from"./check-DQm9OMOd.js";import{r as w,t as T}from"./client-BF51V3uE.js";import{Dt as E,Lt as D,Mt as O,jt as k,xt as A,zt as j}from"./index-C11HmDXN.js";import{t as M}from"./Drawer-ZggviMzu.js";var N=k(`cpu`,[[`path`,{d:`M12 20v2`,key:`1lh1kg`}],[`path`,{d:`M12 2v2`,key:`tus03m`}],[`path`,{d:`M17 20v2`,key:`1rnc9c`}],[`path`,{d:`M17 2v2`,key:`11trls`}],[`path`,{d:`M2 12h2`,key:`1t8f8n`}],[`path`,{d:`M2 17h2`,key:`7oei6x`}],[`path`,{d:`M2 7h2`,key:`asdhe0`}],[`path`,{d:`M20 12h2`,key:`1q8mjw`}],[`path`,{d:`M20 17h2`,key:`1fpfkl`}],[`path`,{d:`M20 7h2`,key:`1o8tra`}],[`path`,{d:`M7 20v2`,key:`4gnj0m`}],[`path`,{d:`M7 2v2`,key:`1i4yhu`}],[`rect`,{x:`4`,y:`4`,width:`16`,height:`16`,rx:`2`,key:`1vbyd7`}],[`rect`,{x:`8`,y:`8`,width:`8`,height:`8`,rx:`1`,key:`z9xiuo`}]]),P=k(`download`,[[`path`,{d:`M12 15V3`,key:`m9g1x1`}],[`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`,key:`ih7n3h`}],[`path`,{d:`m7 10 5 5 5-5`,key:`brsn70`}]]),F=S(`ai`,()=>{let e=i([]),t=i(null),n=i([]),r=i(!1),a=i(!1),o=i(``),s=i(null),c=i([]),l=i(!1),u=i(null),d=i(``),f=i(``),p=i(`off`),m=i([]),h=i(``),g=i(!1),_=-1,v=null,y=null;function b(){let e={thinking:p.value};return d.value&&(e.provider=d.value),f.value&&(e.model=f.value),e}let x=0;function S(e,t){o.value=e,n.value.push({kind:`error`,id:`err-${++x}`,message:e,code:t||``})}function C(e){n.value=n.value.filter(t=>t.kind!==`error`||t.id!==e)}function E(){n.value=n.value.filter(e=>e.kind!==`error`)}async function D(){if(t.value)try{let{data:e}=await T.get(`/ai/conversations/${t.value}`),r=n.value.filter(e=>e.kind===`error`);n.value=[...H(e),...r]}catch{}}function O(){_>=0&&n.value[_]?.kind===`message`||(n.value.push({kind:`message`,role:`assistant`,parts:[]}),_=n.value.length-1)}function k(e){let t=n.value[_];if(!t)return;let r={...t,parts:t.parts.slice()};e(r),n.value[_]=r}function A(e){let t=`message`,n=``;for(let r of e.split(` +import{A as e,D as t,E as n,F as r,G as i,I as a,P as o,T as s,Z as c,_t as l,a as u,c as d,d as f,gt as p,h as m,k as h,l as g,r as _,s as v,u as y,vt as b,x}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{n as S}from"./pinia-B41TZNUX.js";import{t as C}from"./check-BNre7JFR.js";import{r as w,t as T}from"./client-BF51V3uE.js";import{Dt as E,Lt as D,Mt as O,jt as k,xt as A,zt as j}from"./index-DTqMKlE1.js";import{t as M}from"./Drawer-B98TBytl.js";var N=k(`cpu`,[[`path`,{d:`M12 20v2`,key:`1lh1kg`}],[`path`,{d:`M12 2v2`,key:`tus03m`}],[`path`,{d:`M17 20v2`,key:`1rnc9c`}],[`path`,{d:`M17 2v2`,key:`11trls`}],[`path`,{d:`M2 12h2`,key:`1t8f8n`}],[`path`,{d:`M2 17h2`,key:`7oei6x`}],[`path`,{d:`M2 7h2`,key:`asdhe0`}],[`path`,{d:`M20 12h2`,key:`1q8mjw`}],[`path`,{d:`M20 17h2`,key:`1fpfkl`}],[`path`,{d:`M20 7h2`,key:`1o8tra`}],[`path`,{d:`M7 20v2`,key:`4gnj0m`}],[`path`,{d:`M7 2v2`,key:`1i4yhu`}],[`rect`,{x:`4`,y:`4`,width:`16`,height:`16`,rx:`2`,key:`1vbyd7`}],[`rect`,{x:`8`,y:`8`,width:`8`,height:`8`,rx:`1`,key:`z9xiuo`}]]),P=k(`download`,[[`path`,{d:`M12 15V3`,key:`m9g1x1`}],[`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`,key:`ih7n3h`}],[`path`,{d:`m7 10 5 5 5-5`,key:`brsn70`}]]),F=S(`ai`,()=>{let e=i([]),t=i(null),n=i([]),r=i(!1),a=i(!1),o=i(``),s=i(null),c=i([]),l=i(!1),u=i(null),d=i(``),f=i(``),p=i(`off`),m=i([]),h=i(``),g=i(!1),_=-1,v=null,y=null;function b(){let e={thinking:p.value};return d.value&&(e.provider=d.value),f.value&&(e.model=f.value),e}let x=0;function S(e,t){o.value=e,n.value.push({kind:`error`,id:`err-${++x}`,message:e,code:t||``})}function C(e){n.value=n.value.filter(t=>t.kind!==`error`||t.id!==e)}function E(){n.value=n.value.filter(e=>e.kind!==`error`)}async function D(){if(t.value)try{let{data:e}=await T.get(`/ai/conversations/${t.value}`),r=n.value.filter(e=>e.kind===`error`);n.value=[...H(e),...r]}catch{}}function O(){_>=0&&n.value[_]?.kind===`message`||(n.value.push({kind:`message`,role:`assistant`,parts:[]}),_=n.value.length-1)}function k(e){let t=n.value[_];if(!t)return;let r={...t,parts:t.parts.slice()};e(r),n.value[_]=r}function A(e){let t=`message`,n=``;for(let r of e.split(` `))r.startsWith(`event:`)?t=r.slice(6).trim():r.startsWith(`data:`)&&(n+=r.slice(5).trim());let r={};if(n)try{r=JSON.parse(n)}catch{r={raw:n}}return{event:t,data:r}}function j(i,o){switch(i){case`conversation`:t.value=o.id,e.value.find(e=>e.id===o.id)||e.value.unshift({id:o.id,title:o.title||`New conversation`,updated_at:new Date().toISOString()});break;case`message_start`:n.value.push({kind:`message`,role:`assistant`,id:o.message_id,parts:[]}),_=n.value.length-1;break;case`delta`:O(),k(e=>{let t=e.parts[e.parts.length-1];t&&t.type===`text`?e.parts[e.parts.length-1]={...t,text:t.text+o.text}:e.parts.push({type:`text`,text:o.text})});break;case`thinking`:O(),k(e=>{let t=e.parts.findIndex(e=>e.type===`thinking`);t>=0?e.parts[t]={...e.parts[t],text:e.parts[t].text+o.text}:e.parts.unshift({type:`thinking`,text:o.text,streaming:!0,startedAt:Date.now()})});break;case`tool_call`:n.value.push({kind:`tool`,id:o.id,call_id:o.call_id,name:o.name,group:o.group,args:o.args,status:o.requires_approval?`pending_approval`:`running`,result:null});break;case`tool_result`:for(let e=n.value.length-1;e>=0;e--){let t=n.value[e];if(t.kind===`tool`&&t.id===o.id){n.value[e]={...t,status:o.status,result:o.result};break}}break;case`awaiting_approval`:a.value=!0,r.value=!1,_>=0&&k(e=>{let t=e.parts.findIndex(e=>e.type===`thinking`);t>=0&&(e.parts[t]={...e.parts[t],streaming:!1})});break;case`message_end`:_>=0&&k(e=>{let t=e.parts.findIndex(e=>e.type===`thinking`);t>=0&&(e.parts[t]={...e.parts[t],streaming:!1})}),_=-1;break;case`done`:r.value=!1;break;case`error`:S(o.message||`stream error`,o.code),r.value=!1,_>=0&&k(e=>{let t=e.parts.findIndex(e=>e.type===`thinking`);t>=0&&(e.parts[t]={...e.parts[t],streaming:!1})}),_=-1}}async function M(e,t){let n={"Content-Type":`application/json`},r=w();r&&(n[`X-Orva-API-Key`]=r);let i=await fetch(e,{method:`POST`,credentials:`include`,headers:n,body:JSON.stringify(t),signal:v?.signal});if(!i.ok||!i.body)throw Error(`chat request failed (${i.status})`);let a=i.body.getReader(),o=new TextDecoder,s=``;for(;;){let{done:e,value:t}=await a.read();if(e)break;s+=o.decode(t,{stream:!0});let n;for(;(n=s.indexOf(` `))>=0;){let e=s.slice(0,n);if(s=s.slice(n+2),e.trim()){let{event:t,data:n}=A(e);j(t,n)}}}}async function N(e){if(!(!e.trim()||r.value)){o.value=``,E(),a.value=!1,y={type:`chat`,content:e},n.value.push({kind:`message`,role:`user`,parts:[{type:`text`,text:e}]}),r.value=!0,_=-1,v=new AbortController;try{let n={content:e,...b()};t.value&&(n.conversation_id=t.value),await M(`/api/v1/ai/chat`,n)}catch(e){e.name!==`AbortError`&&S(e.message)}finally{r.value=!1,v=null,await D()}}}async function P(){if(r.value||!t.value)return;let e=n.value,i=e.length;for(let t=e.length-1;t>=0&&(e[t].kind!==`message`||e[t].role!==`user`);t--)i=t;n.value=e.slice(0,i),o.value=``,a.value=!1,y={type:`regenerate`},r.value=!0,_=-1,v=new AbortController;try{await M(`/api/v1/ai/conversations/${t.value}/regenerate`,b())}catch(e){e.name!==`AbortError`&&S(e.message)}finally{r.value=!1,v=null,await D()}}async function F(e,i){if(r.value||!t.value||!i.trim())return;let s=n.value,c=s.findIndex(t=>t.kind===`message`&&t.id===e);if(!(c<0)){n.value=[...s.slice(0,c),{...s[c],parts:[{type:`text`,text:i}]}],o.value=``,a.value=!1,y={type:`edit`,messageId:e,content:i},r.value=!0,_=-1,v=new AbortController;try{await M(`/api/v1/ai/conversations/${t.value}/messages/${e}/edit`,{content:i,...b()})}catch(e){e.name!==`AbortError`&&S(e.message)}finally{r.value=!1,v=null,await D()}}}async function I(e){if(!t.value||r.value)return;let i=n.value,a=i.findIndex(t=>t.kind===`message`&&t.id===e);if(!(a<0)){n.value=i.slice(0,a);try{await T.delete(`/ai/conversations/${t.value}/messages/${e}`)}catch(e){S(e.message),await D()}}}async function L(){E();let e=y;if(e?.type===`tool`)return z(e.rowId,e.approved);if(e?.type===`regenerate`)return P();await D();let t=n.value[n.value.length-1];if(t&&t.kind===`message`&&t.role===`user`)return P();if(e?.content)return N(e.content)}function R(){v&&v.abort(),r.value=!1}async function z(e,t){if(r.value)return;o.value=``,E(),a.value=!1,y={type:`tool`,rowId:e,approved:t},r.value=!0,_=-1,v=new AbortController;let n=t?`approve`:`reject`;try{await M(`/api/v1/ai/tool-calls/${e}/${n}`,{})}catch(e){e.name!==`AbortError`&&S(e.message)}finally{r.value=!1,v=null,await D()}}let B=e=>z(e,!0),V=e=>z(e,!1);function H(e){let t=[],n={};for(let t of e.tool_calls||[])(n[t.message_id]||=[]).push(t);for(let r of e.messages||[]){let e=U(r.parts);if(r.role===`user`)t.push({kind:`message`,role:`user`,id:r.id,parts:e});else if(r.role===`assistant`){t.push({kind:`message`,role:`assistant`,id:r.id,parts:e});for(let e of n[r.id]||[])t.push({kind:`tool`,id:e.id,call_id:e.call_id,name:e.tool_name,group:e.tool_group,args:e.args,status:e.status,result:e.result})}}return t}function U(e){let t;try{t=JSON.parse(e||`[]`)}catch{t=[]}return t.filter(e=>e.type===`text`||e.type===`thinking`)}async function W(){let{data:t}=await T.get(`/ai/conversations`);e.value=t.conversations||[]}async function G(e){R(),_=-1;let{data:r}=await T.get(`/ai/conversations/${e}`);t.value=e,n.value=H(r),a.value=!1,o.value=``}function K(){R(),_=-1,t.value=null,n.value=[],a.value=!1,o.value=``}async function q(n){await T.delete(`/ai/conversations/${n}`),e.value=e.value.filter(e=>e.id!==n),t.value===n&&K()}async function J(){R();let{data:t}=await T.delete(`/ai/conversations`);return e.value=[],K(),t.deleted||0}function Y(){let r=n.value;if(!r.length)return;let i=e.value.find(e=>e.id===t.value),a=[`# ${i?.title||`Conversation`}`,``];for(let e of r)if(e.kind===`message`){let t=(e.parts||[]).filter(e=>e.type===`text`&&e.text).map(e=>e.text).join(` diff --git a/backend/internal/server/ui_dist/assets/NotFound-CIp_RSOc.js b/backend/internal/server/ui_dist/assets/NotFound-b7MSgvru.js similarity index 93% rename from backend/internal/server/ui_dist/assets/NotFound-CIp_RSOc.js rename to backend/internal/server/ui_dist/assets/NotFound-b7MSgvru.js index 2b4727f4..866922ab 100644 --- a/backend/internal/server/ui_dist/assets/NotFound-CIp_RSOc.js +++ b/backend/internal/server/ui_dist/assets/NotFound-b7MSgvru.js @@ -1 +1 @@ -import{D as e,Z as t,c as n,d as r,h as i,m as a,s as o,vt as s}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as c}from"./arrow-left-CEJWChyG.js";import{t as l}from"./book-open-BnyP35ri.js";import{lt as u,mt as d,pt as f}from"./index-C11HmDXN.js";var p={class:`min-h-screen flex items-center justify-center bg-background px-6 py-16`},m={class:`max-w-lg w-full`},h={class:`flex items-center gap-3 mb-10`},g={class:`bg-surface border border-border rounded-md px-3 py-2 mb-8 overflow-x-auto`},_={class:`text-xs font-mono text-foreground break-all`},v={class:`flex flex-wrap gap-3`},y={__name:`NotFound`,setup(y){let b=f(),x=d(),S=o(()=>b.fullPath||`/`),C=()=>x.push(`/functions`),w=()=>x.push(`/docs`);return(o,d)=>(e(),r(`div`,p,[n(`div`,m,[n(`div`,h,[i(u,{class:`w-7 h-7`}),d[0]||=n(`span`,{class:`font-semibold tracking-tight text-foreground text-lg`},`Orva`,-1)]),d[3]||=n(`p`,{class:`text-xs uppercase tracking-[0.2em] text-foreground-muted mb-3`},` Error 404 `,-1),d[4]||=n(`h1`,{class:`text-3xl sm:text-4xl font-semibold text-foreground mb-4 leading-tight`},` Page not found. `,-1),d[5]||=n(`p`,{class:`text-sm text-foreground-muted mb-4`},` This Orva instance has no page at: `,-1),n(`div`,g,[n(`code`,_,s(S.value),1)]),n(`div`,v,[n(`button`,{class:`inline-flex items-center gap-2 px-4 py-2 rounded-md bg-white text-black text-sm font-medium hover:bg-white/90 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary`,onClick:C},[i(t(c),{class:`w-4 h-4`}),d[1]||=a(` Go to Functions `,-1)]),n(`button`,{class:`inline-flex items-center gap-2 px-4 py-2 rounded-md border border-border text-sm text-foreground hover:bg-surface-hover transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary`,onClick:w},[i(t(l),{class:`w-4 h-4`}),d[2]||=a(` Open docs `,-1)])])])]))}};export{y as default}; \ No newline at end of file +import{D as e,Z as t,c as n,d as r,h as i,m as a,s as o,vt as s}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as c}from"./arrow-left-Ba3OArNt.js";import{t as l}from"./book-open-B7-cMpW6.js";import{lt as u,mt as d,pt as f}from"./index-DTqMKlE1.js";var p={class:`min-h-screen flex items-center justify-center bg-background px-6 py-16`},m={class:`max-w-lg w-full`},h={class:`flex items-center gap-3 mb-10`},g={class:`bg-surface border border-border rounded-md px-3 py-2 mb-8 overflow-x-auto`},_={class:`text-xs font-mono text-foreground break-all`},v={class:`flex flex-wrap gap-3`},y={__name:`NotFound`,setup(y){let b=f(),x=d(),S=o(()=>b.fullPath||`/`),C=()=>x.push(`/functions`),w=()=>x.push(`/docs`);return(o,d)=>(e(),r(`div`,p,[n(`div`,m,[n(`div`,h,[i(u,{class:`w-7 h-7`}),d[0]||=n(`span`,{class:`font-semibold tracking-tight text-foreground text-lg`},`Orva`,-1)]),d[3]||=n(`p`,{class:`text-xs uppercase tracking-[0.2em] text-foreground-muted mb-3`},` Error 404 `,-1),d[4]||=n(`h1`,{class:`text-3xl sm:text-4xl font-semibold text-foreground mb-4 leading-tight`},` Page not found. `,-1),d[5]||=n(`p`,{class:`text-sm text-foreground-muted mb-4`},` This Orva instance has no page at: `,-1),n(`div`,g,[n(`code`,_,s(S.value),1)]),n(`div`,v,[n(`button`,{class:`inline-flex items-center gap-2 px-4 py-2 rounded-md bg-white text-black text-sm font-medium hover:bg-white/90 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary`,onClick:C},[i(t(c),{class:`w-4 h-4`}),d[1]||=a(` Go to Functions `,-1)]),n(`button`,{class:`inline-flex items-center gap-2 px-4 py-2 rounded-md border border-border text-sm text-foreground hover:bg-surface-hover transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary`,onClick:w},[i(t(l),{class:`w-4 h-4`}),d[2]||=a(` Open docs `,-1)])])])]))}};export{y as default}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/Onboarding-BI0ADHLS.js b/backend/internal/server/ui_dist/assets/Onboarding-CI9ktN7I.js similarity index 97% rename from backend/internal/server/ui_dist/assets/Onboarding-BI0ADHLS.js rename to backend/internal/server/ui_dist/assets/Onboarding-CI9ktN7I.js index 0418dd3a..e45e5717 100644 --- a/backend/internal/server/ui_dist/assets/Onboarding-BI0ADHLS.js +++ b/backend/internal/server/ui_dist/assets/Onboarding-CI9ktN7I.js @@ -1 +1 @@ -import{D as e,F as t,G as n,I as r,T as i,Z as a,c as o,d as s,gt as c,h as l,k as u,l as d,m as f,r as p,s as m,u as h,vt as g}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as _}from"./circle-alert-BIdKIOKw.js";import{t as v}from"./copy-Bt6Rv0BQ.js";import{Bt as y,Lt as b,Pt as x,_t as S,dt as C,jt as w,lt as T,mt as E}from"./index-C11HmDXN.js";var D=w(`eye-off`,[[`path`,{d:`M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49`,key:`ct8e1f`}],[`path`,{d:`M14.084 14.158a3 3 0 0 1-4.242-4.242`,key:`151rxh`}],[`path`,{d:`M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143`,key:`13bj9a`}],[`path`,{d:`m2 2 20 20`,key:`1ooewy`}]]),O=w(`eye`,[[`path`,{d:`M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0`,key:`1nclc0`}],[`circle`,{cx:`12`,cy:`12`,r:`3`,key:`1v7zrd`}]]),k={class:`min-h-screen flex flex-col items-center justify-center bg-background pt-safe pb-safe pl-safe pr-safe px-page py-8 sm:py-12`},A={class:`w-full max-w-md space-y-8`},j={class:`space-y-3`},M={class:`flex items-center gap-3`},N=[`disabled`],P={class:`flex flex-col sm:flex-row gap-2`},F={class:`relative flex-1 min-w-0`},I=[`type`,`disabled`],L={class:`absolute right-1.5 top-1/2 -translate-y-1/2 flex gap-0.5`},R=[`disabled`],ee=[`disabled`,`aria-label`,`title`],z=[`disabled`],B={class:`mt-3 grid grid-cols-2 gap-y-1.5 gap-x-3`},V={key:0,class:`bg-danger/10 border border-danger/30 rounded-md px-4 py-3 flex items-start gap-3`},H={class:`text-sm text-danger`},U={__name:`Onboarding`,setup(w){let U=E(),W=C(),G=n({username:`orva`,password:``}),K=n(``),q=n(!1),J=n(!1),Y=m(()=>({length:G.value.password.length>=10,lower:/[a-z]/.test(G.value.password),upper:/[A-Z]/.test(G.value.password),digit:/[0-9]/.test(G.value.password),symbol:/[^A-Za-z0-9]/.test(G.value.password)})),X=m(()=>{let e=Y.value;return e.length&&e.lower&&e.upper&&e.digit&&e.symbol}),Z=e=>({length:`10+ characters`,lower:`Lowercase`,upper:`Uppercase`,digit:`Number`,symbol:`Symbol`})[e]||e,Q=()=>{let e=e=>{let t=Math.floor(4294967295/e)*e,n;do n=crypto.getRandomValues(new Uint32Array(1))[0];while(n>=t);return n%e},t=t=>t[e(t.length)],n=[t(`abcdefghijklmnopqrstuvwxyz`),t(`ABCDEFGHIJKLMNOPQRSTUVWXYZ`),t(`0123456789`),t(`!@#$%^&*()-_=+[]{}|;:,.<>?`)];for(let e=n.length;e<16;e+=1)n.push(t(`abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+[]{}|;:,.<>?`));for(let t=n.length-1;t>0;--t){let r=e(t+1);[n[t],n[r]]=[n[r],n[t]]}G.value.password=n.join(``)},$=async()=>{try{await navigator.clipboard.writeText(G.value.password)}catch(e){console.error(`Failed to copy password:`,e)}},te=async()=>{K.value=``,q.value=!0;let e=await W.onboard(G.value.username,G.value.password);q.value=!1,e.success?U.push(`/`):K.value=e.error};return i(async()=>{await W.fetchAuthStatus()&&U.push(`/login`)}),(n,i)=>(e(),s(`div`,k,[o(`div`,A,[o(`div`,j,[o(`div`,M,[l(T,{class:`w-9 h-9`}),i[3]||=o(`span`,{class:`text-2xl font-semibold tracking-tight text-white`},`Orva`,-1)]),i[4]||=o(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` Set up admin access `,-1),i[5]||=o(`p`,{class:`text-sm text-foreground-muted leading-relaxed`},` Create the root user for this Orva instance. Store the password securely. `,-1)]),o(`form`,{class:`space-y-5`,onSubmit:y(te,[`prevent`])},[o(`div`,null,[i[6]||=o(`label`,{for:`onboard-username`,class:`block text-xs font-medium text-foreground-muted uppercase tracking-wide mb-1.5`},`Username`,-1),r(o(`input`,{id:`onboard-username`,"onUpdate:modelValue":i[0]||=e=>G.value.username=e,type:`text`,required:``,autocomplete:`username`,class:`w-full bg-background border border-border rounded-md px-3 py-2.5 text-base sm:text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-white focus:border-white transition-colors`,placeholder:`orva`,disabled:q.value},null,8,N),[[b,G.value.username]])]),o(`div`,null,[i[7]||=o(`label`,{for:`onboard-password`,class:`block text-xs font-medium text-foreground-muted uppercase tracking-wide mb-1.5`},`Password`,-1),o(`div`,P,[o(`div`,F,[r(o(`input`,{id:`onboard-password`,"onUpdate:modelValue":i[1]||=e=>G.value.password=e,type:J.value?`text`:`password`,required:``,autocomplete:`new-password`,class:`w-full bg-background border border-border rounded-md px-3 py-2.5 pr-20 text-base sm:text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-white focus:border-white transition-colors font-mono`,placeholder:`Minimum 10 characters`,disabled:q.value},null,8,I),[[x,G.value.password]]),o(`div`,L,[G.value.password?(e(),s(`button`,{key:0,type:`button`,class:`p-1.5 rounded hover:bg-surface-hover transition-colors touch-expand-iconbtn`,disabled:q.value,"aria-label":`Copy password`,title:`Copy password`,onClick:$},[l(a(v),{class:`w-4 h-4 text-foreground-muted hover:text-foreground`})],8,R)):h(``,!0),o(`button`,{type:`button`,class:`p-1.5 rounded hover:bg-surface-hover transition-colors touch-expand-iconbtn`,disabled:q.value,"aria-label":J.value?`Hide password`:`Show password`,title:J.value?`Hide password`:`Show password`,onClick:i[2]||=e=>J.value=!J.value},[J.value?(e(),d(a(D),{key:1,class:`w-4 h-4 text-foreground-muted hover:text-foreground`})):(e(),d(a(O),{key:0,class:`w-4 h-4 text-foreground-muted hover:text-foreground`}))],8,ee)])]),o(`button`,{type:`button`,class:`px-4 py-2.5 rounded-md border border-border bg-surface hover:bg-surface-hover text-sm font-medium text-foreground transition-colors shrink-0`,disabled:q.value,onClick:Q},` Generate `,8,z)]),o(`div`,B,[(e(!0),s(p,null,u(Y.value,(t,n)=>(e(),s(`div`,{key:n,class:c([`flex items-center gap-1.5 text-xs transition-colors duration-200`,t?`text-success`:`text-foreground-muted`])},[o(`div`,{class:c([`w-1.5 h-1.5 rounded-full`,t?`bg-success`:`bg-foreground-muted/40`])},null,2),o(`span`,null,g(Z(n)),1)],2))),128))])]),K.value?(e(),s(`div`,V,[l(a(_),{class:`w-5 h-5 text-danger shrink-0 mt-0.5`}),o(`p`,H,g(K.value),1)])):h(``,!0),l(S,{type:`submit`,class:`w-full`,loading:q.value,disabled:!X.value||!G.value.username||q.value},{default:t(()=>[...i[8]||=[f(` Create account `,-1)]]),_:1},8,[`loading`,`disabled`]),i[9]||=o(`p`,{class:`text-xs text-center text-foreground-muted pt-2`},` This action initialises your instance and cannot be undone. `,-1)],32)])]))}};export{U as default}; \ No newline at end of file +import{D as e,F as t,G as n,I as r,T as i,Z as a,c as o,d as s,gt as c,h as l,k as u,l as d,m as f,r as p,s as m,u as h,vt as g}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as _}from"./circle-alert-BnUYYhGE.js";import{t as v}from"./copy-BqdwwcxC.js";import{Bt as y,Lt as b,Pt as x,_t as S,dt as C,jt as w,lt as T,mt as E}from"./index-DTqMKlE1.js";var D=w(`eye-off`,[[`path`,{d:`M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49`,key:`ct8e1f`}],[`path`,{d:`M14.084 14.158a3 3 0 0 1-4.242-4.242`,key:`151rxh`}],[`path`,{d:`M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143`,key:`13bj9a`}],[`path`,{d:`m2 2 20 20`,key:`1ooewy`}]]),O=w(`eye`,[[`path`,{d:`M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0`,key:`1nclc0`}],[`circle`,{cx:`12`,cy:`12`,r:`3`,key:`1v7zrd`}]]),k={class:`min-h-screen flex flex-col items-center justify-center bg-background pt-safe pb-safe pl-safe pr-safe px-page py-8 sm:py-12`},A={class:`w-full max-w-md space-y-8`},j={class:`space-y-3`},M={class:`flex items-center gap-3`},N=[`disabled`],P={class:`flex flex-col sm:flex-row gap-2`},F={class:`relative flex-1 min-w-0`},I=[`type`,`disabled`],L={class:`absolute right-1.5 top-1/2 -translate-y-1/2 flex gap-0.5`},R=[`disabled`],ee=[`disabled`,`aria-label`,`title`],z=[`disabled`],B={class:`mt-3 grid grid-cols-2 gap-y-1.5 gap-x-3`},V={key:0,class:`bg-danger/10 border border-danger/30 rounded-md px-4 py-3 flex items-start gap-3`},H={class:`text-sm text-danger`},U={__name:`Onboarding`,setup(w){let U=E(),W=C(),G=n({username:`orva`,password:``}),K=n(``),q=n(!1),J=n(!1),Y=m(()=>({length:G.value.password.length>=10,lower:/[a-z]/.test(G.value.password),upper:/[A-Z]/.test(G.value.password),digit:/[0-9]/.test(G.value.password),symbol:/[^A-Za-z0-9]/.test(G.value.password)})),X=m(()=>{let e=Y.value;return e.length&&e.lower&&e.upper&&e.digit&&e.symbol}),Z=e=>({length:`10+ characters`,lower:`Lowercase`,upper:`Uppercase`,digit:`Number`,symbol:`Symbol`})[e]||e,Q=()=>{let e=e=>{let t=Math.floor(4294967295/e)*e,n;do n=crypto.getRandomValues(new Uint32Array(1))[0];while(n>=t);return n%e},t=t=>t[e(t.length)],n=[t(`abcdefghijklmnopqrstuvwxyz`),t(`ABCDEFGHIJKLMNOPQRSTUVWXYZ`),t(`0123456789`),t(`!@#$%^&*()-_=+[]{}|;:,.<>?`)];for(let e=n.length;e<16;e+=1)n.push(t(`abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+[]{}|;:,.<>?`));for(let t=n.length-1;t>0;--t){let r=e(t+1);[n[t],n[r]]=[n[r],n[t]]}G.value.password=n.join(``)},$=async()=>{try{await navigator.clipboard.writeText(G.value.password)}catch(e){console.error(`Failed to copy password:`,e)}},te=async()=>{K.value=``,q.value=!0;let e=await W.onboard(G.value.username,G.value.password);q.value=!1,e.success?U.push(`/`):K.value=e.error};return i(async()=>{await W.fetchAuthStatus()&&U.push(`/login`)}),(n,i)=>(e(),s(`div`,k,[o(`div`,A,[o(`div`,j,[o(`div`,M,[l(T,{class:`w-9 h-9`}),i[3]||=o(`span`,{class:`text-2xl font-semibold tracking-tight text-white`},`Orva`,-1)]),i[4]||=o(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` Set up admin access `,-1),i[5]||=o(`p`,{class:`text-sm text-foreground-muted leading-relaxed`},` Create the root user for this Orva instance. Store the password securely. `,-1)]),o(`form`,{class:`space-y-5`,onSubmit:y(te,[`prevent`])},[o(`div`,null,[i[6]||=o(`label`,{for:`onboard-username`,class:`block text-xs font-medium text-foreground-muted uppercase tracking-wide mb-1.5`},`Username`,-1),r(o(`input`,{id:`onboard-username`,"onUpdate:modelValue":i[0]||=e=>G.value.username=e,type:`text`,required:``,autocomplete:`username`,class:`w-full bg-background border border-border rounded-md px-3 py-2.5 text-base sm:text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-white focus:border-white transition-colors`,placeholder:`orva`,disabled:q.value},null,8,N),[[b,G.value.username]])]),o(`div`,null,[i[7]||=o(`label`,{for:`onboard-password`,class:`block text-xs font-medium text-foreground-muted uppercase tracking-wide mb-1.5`},`Password`,-1),o(`div`,P,[o(`div`,F,[r(o(`input`,{id:`onboard-password`,"onUpdate:modelValue":i[1]||=e=>G.value.password=e,type:J.value?`text`:`password`,required:``,autocomplete:`new-password`,class:`w-full bg-background border border-border rounded-md px-3 py-2.5 pr-20 text-base sm:text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-white focus:border-white transition-colors font-mono`,placeholder:`Minimum 10 characters`,disabled:q.value},null,8,I),[[x,G.value.password]]),o(`div`,L,[G.value.password?(e(),s(`button`,{key:0,type:`button`,class:`p-1.5 rounded hover:bg-surface-hover transition-colors touch-expand-iconbtn`,disabled:q.value,"aria-label":`Copy password`,title:`Copy password`,onClick:$},[l(a(v),{class:`w-4 h-4 text-foreground-muted hover:text-foreground`})],8,R)):h(``,!0),o(`button`,{type:`button`,class:`p-1.5 rounded hover:bg-surface-hover transition-colors touch-expand-iconbtn`,disabled:q.value,"aria-label":J.value?`Hide password`:`Show password`,title:J.value?`Hide password`:`Show password`,onClick:i[2]||=e=>J.value=!J.value},[J.value?(e(),d(a(D),{key:1,class:`w-4 h-4 text-foreground-muted hover:text-foreground`})):(e(),d(a(O),{key:0,class:`w-4 h-4 text-foreground-muted hover:text-foreground`}))],8,ee)])]),o(`button`,{type:`button`,class:`px-4 py-2.5 rounded-md border border-border bg-surface hover:bg-surface-hover text-sm font-medium text-foreground transition-colors shrink-0`,disabled:q.value,onClick:Q},` Generate `,8,z)]),o(`div`,B,[(e(!0),s(p,null,u(Y.value,(t,n)=>(e(),s(`div`,{key:n,class:c([`flex items-center gap-1.5 text-xs transition-colors duration-200`,t?`text-success`:`text-foreground-muted`])},[o(`div`,{class:c([`w-1.5 h-1.5 rounded-full`,t?`bg-success`:`bg-foreground-muted/40`])},null,2),o(`span`,null,g(Z(n)),1)],2))),128))])]),K.value?(e(),s(`div`,V,[l(a(_),{class:`w-5 h-5 text-danger shrink-0 mt-0.5`}),o(`p`,H,g(K.value),1)])):h(``,!0),l(S,{type:`submit`,class:`w-full`,loading:q.value,disabled:!X.value||!G.value.username||q.value},{default:t(()=>[...i[8]||=[f(` Create account `,-1)]]),_:1},8,[`loading`,`disabled`]),i[9]||=o(`p`,{class:`text-xs text-center text-foreground-muted pt-2`},` This action initialises your instance and cannot be undone. `,-1)],32)])]))}};export{U as default}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/Settings-CS-pXJcG.js b/backend/internal/server/ui_dist/assets/Settings-Bjj6gg_h.js similarity index 99% rename from backend/internal/server/ui_dist/assets/Settings-CS-pXJcG.js rename to backend/internal/server/ui_dist/assets/Settings-Bjj6gg_h.js index b7745f97..ed3534d8 100644 --- a/backend/internal/server/ui_dist/assets/Settings-CS-pXJcG.js +++ b/backend/internal/server/ui_dist/assets/Settings-Bjj6gg_h.js @@ -1 +1 @@ -import{D as e,F as t,G as n,I as r,M as i,T as a,Z as o,_t as s,c,d as l,gt as u,h as d,k as f,l as ee,m as p,r as m,s as h,u as g,vt as _}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as te}from"./copy-Bt6Rv0BQ.js";import{i as ne,r as re,t as v}from"./ModelMenu-CCBXKr4H.js";import{t as y}from"./key-round-BmyLzD9o.js";import{t as b}from"./terminal-BVgEq2_Q.js";import{t as x}from"./trash-2-Bj4AFUfw.js";import{$ as S,Bt as C,Ct as w,D as ie,Ft as ae,G as oe,I as T,It as se,Lt as E,Q as ce,Tt as le,_t as D,ct as ue,dt as de,ft as fe,gt as pe,jt as O,mt as me,n as he,nt as ge,ut as k}from"./index-C11HmDXN.js";import{t as _e}from"./clipboard-D_9N0yai.js";import{t as A}from"./time-D8OmbYzY.js";import{t as j}from"./Input-DQ-tWGkn.js";var ve=O(`code-xml`,[[`path`,{d:`m18 16 4-4-4-4`,key:`1inbqp`}],[`path`,{d:`m6 8-4 4 4 4`,key:`15zrgr`}],[`path`,{d:`m14.5 4-5 16`,key:`e7oirm`}]]),ye=O(`database-backup`,[[`ellipse`,{cx:`12`,cy:`5`,rx:`9`,ry:`3`,key:`msslwz`}],[`path`,{d:`M3 12a9 3 0 0 0 5 2.69`,key:`1ui2ym`}],[`path`,{d:`M21 9.3V5`,key:`6k6cib`}],[`path`,{d:`M3 5v14a9 3 0 0 0 6.47 2.88`,key:`i62tjy`}],[`path`,{d:`M12 12v4h4`,key:`1bxaet`}],[`path`,{d:`M13 20a5 5 0 0 0 9-3 4.5 4.5 0 0 0-4.5-4.5c-1.33 0-2.54.54-3.41 1.41L12 16`,key:`1f4ei9`}]]),be=O(`hard-drive`,[[`path`,{d:`M10 16h.01`,key:`1bzywj`}],[`path`,{d:`M2.212 11.577a2 2 0 0 0-.212.896V18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5.527a2 2 0 0 0-.212-.896L18.55 5.11A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z`,key:`18tbho`}],[`path`,{d:`M21.946 12.013H2.054`,key:`zqlbp7`}],[`path`,{d:`M6 16h.01`,key:`1pmjb7`}]]),xe=O(`info`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M12 16v-4`,key:`1dtifu`}],[`path`,{d:`M12 8h.01`,key:`e9boi3`}]]),Se=O(`log-out`,[[`path`,{d:`m16 17 5-5-5-5`,key:`1bji2h`}],[`path`,{d:`M21 12H9`,key:`dn1m92`}],[`path`,{d:`M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4`,key:`1uf3rs`}]]),Ce=O(`monitor`,[[`rect`,{width:`20`,height:`14`,x:`2`,y:`3`,rx:`2`,key:`48i651`}],[`line`,{x1:`8`,x2:`16`,y1:`21`,y2:`21`,key:`1svkeh`}],[`line`,{x1:`12`,x2:`12`,y1:`17`,y2:`21`,key:`vw1qmm`}]]),M=O(`mouse-pointer-2`,[[`path`,{d:`M4.037 4.688a.495.495 0 0 1 .651-.651l16 6.5a.5.5 0 0 1-.063.947l-6.124 1.58a2 2 0 0 0-1.438 1.435l-1.579 6.126a.5.5 0 0 1-.947.063z`,key:`edeuup`}]]),we=O(`upload`,[[`path`,{d:`M12 3v12`,key:`1x0j5s`}],[`path`,{d:`m17 8-5-5-5 5`,key:`7q97r8`}],[`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`,key:`ih7n3h`}]]),Te=O(`wand-sparkles`,[[`path`,{d:`m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72`,key:`ul74o6`}],[`path`,{d:`m14 7 3 3`,key:`1r5n42`}],[`path`,{d:`M5 6v4`,key:`ilb8ba`}],[`path`,{d:`M19 14v4`,key:`blhpug`}],[`path`,{d:`M10 2v2`,key:`7u0qdc`}],[`path`,{d:`M7 8H3`,key:`zfb6yr`}],[`path`,{d:`M21 16h-4`,key:`1cnmox`}],[`path`,{d:`M11 3H9`,key:`1obp7u`}]]),N={class:`space-y-6`},P={class:`space-y-3`},F={key:0,class:`grid grid-cols-1 gap-3 sm:grid-cols-2 sm:items-start`},Ee=[`value`],De=[`value`],I={class:`mt-1.5`},L={key:0,class:`text-xs text-danger-fg sm:col-span-2`},R={class:`divide-y divide-border border-y border-border`},z={class:`flex items-center gap-2 px-3 py-2`},B={class:`font-mono text-sm text-foreground`},V={key:0,class:`text-xs text-foreground-muted`},H={key:1,class:`text-xs uppercase tracking-label rounded px-1.5 py-0.5 bg-primary/15 text-primary-hover`},U={key:0,class:`text-xs text-foreground-muted`},W={class:`group border-t border-border pt-4`},G={class:`mt-4 space-y-3`},Oe=[`value`],ke={key:0,class:`space-y-3 border-t border-border pt-5`},Ae={class:`space-y-2`},je=[`value`],Me={key:0,class:`h-2 w-2 rounded-full bg-primary`},K={class:`min-w-0`},q={class:`block text-sm font-medium text-foreground`},J={class:`mt-0.5 block text-xs text-foreground-muted leading-snug`},Ne={__name:`AISettingsPanel`,setup(i){let s=re(),ee=pe();async function te(e){await ee.ask({title:`Remove provider?`,message:`This permanently removes the provider and its encrypted API key.`,danger:!0,confirmLabel:`Remove`})&&s.deleteProvider(e)}let ne=[`openai`,`anthropic`,`groq`,`gemini`,`ollama`,`mistral`,`openrouter`,`xai`,`cohere`],y=[{value:`all_writes`,label:`Ask before changes (recommended)`,hint:`Confirm creates, updates, and deletes.`},{value:`destructive_only`,label:`Ask before deletes only`,hint:`Confirm deletes; run other changes automatically.`},{value:`auto`,label:`Bypass: allow everything`,hint:`Run every action without confirmation.`}],b=n({provider:`openai`,label:``,api_key:``,base_url:``}),x=[`ollama`],S=h(()=>x.includes(b.value.provider)),C=h(()=>S.value&&!b.value.base_url.trim()?`Base URL is required for Ollama.`:``),w=h(()=>!!b.value.provider&&!C.value),ie=h(()=>S.value?`http://192.168.1.50:11434 or http://ollama.lan:11434`:`https://api.openai.com/v1 or https://your-host/v1`),oe=h(()=>S.value?`Where your server is listening. A private LAN address works; Orva permits it when your egress blocklist does.`:`For custom / self-hosted endpoints. Either with or without /v1 works. A private LAN address works too.`),T=n(!1),E=n(!1);a(async()=>{await s.loadSettings(),await s.loadProviders()});async function ce(e){await s.selectProvider(e.target.value)}async function le(){if(w.value){T.value=!0;try{await s.saveProvider({provider:b.value.provider,label:b.value.label,api_key:b.value.api_key,base_url:b.value.base_url,enabled:!0}),b.value.api_key=``}finally{T.value=!1}}}async function ue(){E.value=!0;try{await s.saveSettings(s.settings)}finally{E.value=!1}}return(n,i)=>(e(),l(`div`,N,[c(`section`,P,[i[9]||=c(`div`,null,[c(`h3`,{class:`text-sm font-semibold text-foreground`},` Providers `),c(`p`,{class:`text-xs text-foreground-muted mt-1.5 max-w-prose leading-snug`},` Keys are encrypted at rest. Choose which provider and model Chat uses. `)],-1),o(s).providers.length?(e(),l(`div`,F,[c(`div`,null,[i[5]||=c(`label`,{for:`ai-active-provider`,class:`text-xs font-medium text-foreground-muted uppercase tracking-wide`},`Active provider`,-1),c(`select`,{id:`ai-active-provider`,value:o(s).selectedProviderId||``,class:`mt-1.5 h-10 w-full bg-background border border-border rounded-md text-sm px-3 text-foreground transition-colors duration-200 focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,onChange:ce},[(e(!0),l(m,null,f(o(s).providers,t=>(e(),l(`option`,{key:t.id,value:t.id},_(t.label?`${t.provider} (${t.label})`:t.provider),9,De))),128))],40,Ee)]),c(`div`,null,[i[6]||=c(`label`,{for:`ai-active-model`,class:`text-xs font-medium text-foreground-muted uppercase tracking-wide`},` Active model `,-1),c(`div`,I,[d(v,{wide:``,"trigger-id":`ai-active-model`})])]),o(s).modelsError?(e(),l(`p`,L,` Models could not be loaded. Check the provider endpoint and credentials. `)):g(``,!0)])):g(``,!0),c(`div`,R,[(e(!0),l(m,null,f(o(s).providers,n=>(e(),l(`div`,{key:n.id,class:`py-1`},[c(`div`,z,[c(`span`,B,_(n.provider),1),n.label?(e(),l(`span`,V,_(n.label),1)):g(``,!0),o(s).selectedProviderId===n.id?(e(),l(`span`,H,`active`)):g(``,!0),c(`span`,{class:u([`text-xs uppercase tracking-label rounded px-1.5 py-0.5`,n.has_key?`bg-success-tint text-success-fg`:`bg-surface-hover text-foreground-muted`])},_(n.has_key?`key set`:`no key`),3),i[8]||=c(`span`,{class:`flex-1`},null,-1),d(D,{size:`xs`,variant:`ghost`,onClick:e=>te(n.id)},{default:t(()=>[...i[7]||=[p(` Remove `,-1)]]),_:1},8,[`onClick`])])]))),128)),o(s).providers.length?g(``,!0):(e(),l(`p`,U,` No providers configured yet. `))])]),c(`details`,W,[i[13]||=c(`summary`,{class:`flex cursor-pointer list-none items-center justify-between rounded-sm text-sm font-semibold text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary`},[p(` Add provider `),c(`span`,{class:`text-foreground-muted transition-transform group-open:rotate-45`,"aria-hidden":`true`},`+`)],-1),c(`div`,G,[i[12]||=c(`p`,{class:`text-xs text-foreground-muted max-w-prose leading-snug`},[p(` For compatible endpoints, choose `),c(`span`,{class:`font-mono`},`openai`),p(` and add the Base URL. `)],-1),c(`div`,null,[i[10]||=c(`label`,{for:`ai-provider`,class:`text-xs font-medium text-foreground-muted uppercase tracking-wide`},`Provider`,-1),r(c(`select`,{id:`ai-provider`,"onUpdate:modelValue":i[0]||=e=>b.value.provider=e,class:`mt-1.5 w-full bg-background border border-border rounded-md text-sm px-3 py-2 text-foreground transition-colors duration-200 focus:outline-none focus:ring-1 focus:ring-white focus:border-white`},[(e(),l(m,null,f(ne,e=>c(`option`,{key:e,value:e},_(e),9,Oe)),64))],512),[[se,b.value.provider]])]),d(j,{modelValue:b.value.label,"onUpdate:modelValue":i[1]||=e=>b.value.label=e,label:`Label`,placeholder:`e.g. personal, work (optional)`},null,8,[`modelValue`]),d(j,{modelValue:b.value.base_url,"onUpdate:modelValue":i[2]||=e=>b.value.base_url=e,label:S.value?`Base URL (required)`:`Base URL (optional)`,placeholder:ie.value,hint:oe.value,error:C.value,required:S.value},null,8,[`modelValue`,`label`,`placeholder`,`hint`,`error`,`required`]),d(j,{modelValue:b.value.api_key,"onUpdate:modelValue":i[3]||=e=>b.value.api_key=e,label:`API key`,type:`password`,placeholder:`sk-…`,hint:`Stored encrypted; never shown again. Leave blank when updating to keep the current key.`},null,8,[`modelValue`]),d(D,{variant:`primary`,loading:T.value,disabled:!w.value,onClick:le},{default:t(()=>[...i[11]||=[p(` Save provider `,-1)]]),_:1},8,[`loading`,`disabled`])])]),o(s).settings?(e(),l(`section`,ke,[i[17]||=c(`h3`,{class:`text-sm font-semibold text-foreground`},` Defaults `,-1),c(`fieldset`,Ae,[i[14]||=c(`legend`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide`},` Approval policy `,-1),i[15]||=c(`p`,{class:`text-xs text-foreground-muted leading-snug`},` Choose when changes require confirmation. `,-1),(e(),l(m,null,f(y,t=>c(`label`,{key:t.value,class:`flex cursor-pointer items-start gap-3 px-1 py-2 transition-colors hover:bg-surface-hover focus-within:outline-none focus-within:ring-2 focus-within:ring-inset focus-within:ring-primary`},[r(c(`input`,{"onUpdate:modelValue":i[4]||=e=>o(s).settings.approval_policy=e,type:`radio`,name:`approval-policy`,value:t.value,class:`sr-only`},null,8,je),[[ae,o(s).settings.approval_policy]]),c(`span`,{class:u([`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full border transition-colors`,o(s).settings.approval_policy===t.value?`border-primary`:`border-foreground-muted/50`])},[o(s).settings.approval_policy===t.value?(e(),l(`span`,Me)):g(``,!0)],2),c(`span`,K,[c(`span`,q,_(t.label),1),c(`span`,J,_(t.hint),1)])])),64))]),d(D,{variant:`primary`,loading:E.value,onClick:ue},{default:t(()=>[...i[16]||=[p(` Save defaults `,-1)]]),_:1},8,[`loading`])])):g(``,!0)]))}},Y={},X={xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 24 24`,fill:`currentColor`,"aria-hidden":`true`};function Pe(t,n){return e(),l(`svg`,X,[...n[0]||=[c(`path`,{d:`M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.4069-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z`},null,-1)]])}var Fe=k(Y,[[`render`,Pe]]),Ie={},Le={xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 24 24`,fill:`currentColor`,"aria-hidden":`true`};function Z(t,n){return e(),l(`svg`,Le,[...n[0]||=[c(`path`,{d:`M4.709 15.955l4.72-2.647.08-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.146-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.418 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.349-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.049.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.658-.851-.747-1.926-1.622h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.608.213-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.908-1.312-.006.006z`},null,-1)]])}var Q=k(Ie,[[`render`,Z]]);function Re(e){let t=(e||``).toLowerCase();return t.includes(`claude code`)?{icon:b,accent:`text-orange-400`}:t.includes(`chatgpt`)||t.includes(`openai`)?{icon:Fe,accent:`text-emerald-400`}:t.includes(`claude`)||t.includes(`anthropic`)?{icon:Q,accent:`text-orange-400`}:t.includes(`cursor`)?{icon:M,accent:`text-blue-400`}:t.includes(`vscode`)||t.includes(`vs code`)||t.includes(`code`)?{icon:ve,accent:`text-blue-400`}:{icon:w,accent:`text-foreground-muted`}}var ze={class:`space-y-8`},Be={class:`group border-b border-border pb-6`},Ve={class:`flex cursor-pointer list-none items-center gap-2 text-sm font-semibold text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary [&::-webkit-details-marker]:hidden`},He={class:`ml-auto text-xs font-normal text-foreground-muted group-open:hidden`},Ue={class:`mt-4 grid grid-cols-[max-content,1fr] gap-x-6 gap-y-2 text-xs`},We={class:`font-mono text-white`},Ge={class:`font-mono text-white`},Ke={class:`font-mono text-white`},qe={class:`font-mono text-white flex items-center gap-2 min-w-0`},Je={class:`truncate`},Ye={key:1,class:`text-xs text-primary-hover shrink-0`},Xe={id:`ai`,class:`scroll-mt-6 space-y-4 border-b border-border pb-8`},Ze={class:`text-base font-semibold text-white flex items-center gap-2`},Qe={class:`space-y-4 border-b border-border pb-8`},$e={class:`flex items-start justify-between gap-4`},et={class:`text-base font-semibold text-white flex items-center gap-2`},tt={key:0,class:`text-xs text-foreground-muted italic`},nt={key:1,class:`rounded-md border border-danger-ring bg-danger-tint p-3 text-xs text-danger-fg`},rt={class:`font-mono break-all`},it={key:2,class:`space-y-3`},at={class:`h-2 w-full rounded-full overflow-hidden bg-border/60 flex`},ot=[`title`],st=[`title`],ct=[`title`],lt={class:`grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-1.5 text-xs`},ut={class:`flex items-center justify-between`},dt={class:`font-mono text-white`},ft={class:`flex items-center justify-between`},pt={class:`font-mono text-white`},mt={key:0,class:`flex items-center justify-between`},ht={class:`font-mono text-white`},gt={class:`flex items-center justify-between`},_t={class:`font-mono text-white font-semibold`},vt={key:0,class:`text-xs text-foreground-muted pt-1 border-t border-border`},yt={key:3,class:`rounded-md border border-success-ring bg-success-tint p-3 text-xs text-success-fg`},bt={class:`font-mono`},xt={key:4,class:`rounded-md border border-danger-ring bg-danger-tint p-3 text-xs text-danger-fg`},St={class:`font-mono break-all`},Ct={class:`space-y-4 border-b border-border pb-8`},wt={class:`text-base font-semibold text-white flex items-center gap-2`},Tt={class:`grid grid-cols-1 sm:grid-cols-3 gap-3`},Et={class:`flex flex-col gap-1`},Dt={class:`flex flex-col gap-1`},Ot={class:`flex flex-col gap-1`},kt={key:0,id:`pw-error`,class:`rounded-md border border-danger-ring bg-danger-tint p-2.5 text-xs text-danger-fg`},At={key:1,class:`rounded-md border border-success-ring bg-success-tint p-2.5 text-xs text-success-fg`},jt={class:`pt-2 border-t border-border`},Mt={class:`space-y-4 border-b border-border pb-8`},Nt={class:`flex items-start justify-between gap-4`},Pt={class:`text-base font-semibold text-white flex items-center gap-2`},Ft={class:`text-xs text-foreground-muted mt-1 max-w-prose`},It={key:0,class:`text-xs text-foreground-muted self-center`},Lt={key:0,class:`rounded-md border border-danger-ring bg-danger-tint p-3 text-xs text-danger-fg`},Rt={class:`font-mono break-all`},zt={key:1,class:`text-xs text-foreground-muted italic`},Bt={key:2,class:`py-3`},Vt={key:3,class:`divide-y divide-border -mx-5`},Ht={class:`flex-1 min-w-0`},Ut={class:`text-sm font-medium text-white truncate`},Wt={class:`text-xs text-foreground-muted mt-0.5 flex flex-wrap gap-x-3 gap-y-0.5`},Gt={key:0},Kt={key:1,class:`italic opacity-70`},qt={key:2},Jt={class:`flex flex-wrap gap-1 mt-2`},Yt=[`disabled`,`onClick`],Xt={class:`space-y-4 border-b border-border pb-8`},Zt={class:`flex items-start justify-between gap-4`},Qt={class:`text-base font-semibold text-white flex items-center gap-2`},$t={key:0,class:`text-xs text-foreground-muted self-center`},en={key:0,class:`rounded-md border border-danger-ring bg-danger-tint p-3 text-xs text-danger-fg`},tn={class:`font-mono break-all`},nn={key:1,class:`divide-y divide-border -mx-5`},rn={class:`flex-1 min-w-0`},an={class:`text-sm font-medium text-white flex items-center gap-2 flex-wrap`},on={key:0},sn={key:1,class:`font-mono text-xs`},cn={key:2,class:`text-xs px-1.5 py-0.5 rounded bg-success-tint text-success-fg font-medium`},ln={class:`text-xs text-foreground-muted mt-0.5`},un=[`disabled`,`onClick`],dn={class:`space-y-4`},fn={class:`flex items-start justify-between gap-4`},pn={class:`text-base font-semibold text-white flex items-center gap-2`},mn={class:`flex flex-col sm:flex-row gap-3 pt-2 border-t border-border`},hn={key:0,class:`rounded-md border border-danger-ring bg-danger-tint p-3 text-xs text-danger-fg`},gn={class:`font-mono break-all`},_n={key:1,class:`rounded-md border border-success-ring bg-success-tint p-3 text-xs text-success-fg`},$=Object.assign({name:`SettingsView`},{__name:`Settings`,setup(re){let v=pe(),b=de(),ae=me(),se=he(),O=h(()=>se.buildInfo),k=n(!1),j=async()=>{O.value?.image&&await _e(O.value.image)&&(k.value=!0,setTimeout(()=>{k.value=!1},1500))},ve=e=>{if(!e||e===`unknown`)return`—`;try{return new Date(e).toISOString().replace(`T`,` `).replace(/\.\d+Z$/,` UTC`)}catch{return e}},M=n({current:``,next:``,confirm:``}),N=n(!1),P=n(``),F=n(!1),Ee=async()=>{if(P.value=``,F.value=!1,!M.value.current||!M.value.next||!M.value.confirm){P.value=`All three fields are required.`;return}if(M.value.next.length<8){P.value=`New password must be at least 8 characters.`;return}if(M.value.next!==M.value.confirm){P.value=`New password and confirmation do not match.`;return}N.value=!0;try{await b.changePassword(M.value.current,M.value.next),F.value=!0,M.value={current:``,next:``,confirm:``}}catch(e){P.value=e?.response?.data?.error?.message||`Failed to update password.`}finally{N.value=!1}},De=async()=>{await b.logout(),ae.push(`/login`)},I=n(null),L=n(!1),R=n(``),z=n(!1),B=n(null),V=n(``),H=n(!1),U=n(``),W=n(null),G=h(()=>B.value&&B.value.total_bytes>0?Math.max(.5,B.value.db_bytes/B.value.total_bytes*100):0),Oe=h(()=>B.value&&B.value.total_bytes>0&&B.value.wal_bytes>0?Math.max(.5,B.value.wal_bytes/B.value.total_bytes*100):0),ke=h(()=>B.value&&B.value.total_bytes>0?Math.max(.5,B.value.functions_bytes/B.value.total_bytes*100):0),Ae=h(()=>B.value?(B.value.db_free_pages||0)*(B.value.db_page_size||0):0),je=async()=>{try{V.value=``;let e=await ie();B.value=e.data}catch(e){V.value=e?.response?.data?.error?.message||e?.message||`unknown error`}},Me=async()=>{if(await v.ask({title:`Compact database?`,message:`VACUUM rewrites orva.db to drop the freelist and shrink the file. It holds an exclusive lock for the duration; every other writer (deploys, invocations recording executions, KV puts, job enqueues) blocks until it returns. Typical runtime is sub-second, but a heavily-loaded instance can stall for several seconds.`,confirmLabel:`Compact`,danger:!1})){H.value=!0,U.value=``,W.value=null;try{let e=await ge();W.value=e.data,await je()}catch(e){U.value=e?.response?.data?.error?.message||e?.message||`vacuum failed`}finally{H.value=!1}}},K=e=>{if(e==null||isNaN(e))return`—`;let t=1024;if(e=t&&i{window.location.hash===`#ai`&&window.requestAnimationFrame(()=>{document.getElementById(`ai`)?.scrollIntoView({behavior:`smooth`,block:`start`})})});let q=n([]),J=n(!1),Y=n(``),X=n(``),Pe=async()=>{J.value=!0,Y.value=``;try{let e=await T();q.value=e.data.apps||[]}catch(e){Y.value=e?.response?.data?.error?.message||e?.message||`unknown error`}finally{J.value=!1}},Fe=async e=>{if(await v.ask({title:`Revoke ${e.client_name}?`,message:`${e.client_name} will lose access immediately. Any in-flight request will fail with 401. The connector can be re-authorized at any time from the originating app.`,confirmLabel:`Revoke`,danger:!0})){X.value=e.id;try{await ce(e.id),await Pe()}catch(e){Y.value=e?.response?.data?.error?.message||e?.message||`failed to revoke`}finally{X.value=``}}},Ie=e=>(e||``).split(/\s+/).filter(Boolean),Le=e=>{switch(e){case`admin`:return`bg-danger-tint text-danger-fg`;case`write`:return`bg-warning-tint text-warning-fg`;case`invoke`:return`bg-info-tint text-info-fg`;case`read`:return`bg-foreground-muted/15 text-foreground-muted`;default:return`bg-foreground-muted/10 text-foreground-muted`}},Z=n([]),Q=n(``),$=n(``),vn=async()=>{Q.value=``;try{let e=await oe();Z.value=e.data.sessions||[]}catch(e){Q.value=e?.response?.data?.error?.message||e?.message||`unknown error`}},yn=async e=>{if(await v.ask({title:`Revoke this session?`,message:`The browser using this session will be logged out on its next request. Use this if you suspect a device was lost or to clean up old logins.`,confirmLabel:`Revoke`,danger:!0})){$.value=e.prefix;try{await S(e.prefix),await vn()}catch(e){Q.value=e?.response?.data?.error?.message||e?.message||`failed to revoke`}finally{$.value=``}}},bn=e=>!e||e.length<8?e:e.slice(0,1)+`••••••••`+e.slice(-4);a(Pe),a(vn);let xn=()=>{window.location.assign(`/api/v1/backup?ts=`+Date.now())},Sn=()=>{R.value=``,z.value=!1,I.value?.click()},Cn=async e=>{let t=e.target.files?.[0];if(e.target.value=``,t&&await v.ask({title:`Restore from backup?`,message:`This will replace the live database and function code with the contents of "${t.name}". The current orva.db is moved aside as orva.db.before-restore- in case rollback is needed. You will need to reload after restore completes.`,confirmLabel:`Restore`,danger:!0})){L.value=!0,R.value=``,z.value=!1;try{await ue(t),z.value=!0}catch(e){R.value=e?.response?.data?.error?.message||e?.message||`Restore failed`}finally{L.value=!1}}},wn=()=>{window.location.reload()};return(n,a)=>(e(),l(`div`,ze,[a[43]||=c(`div`,null,[c(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` Settings `),c(`p`,{class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},` Instance, access, storage, backups, and AI. `)],-1),c(`details`,Be,[c(`summary`,Ve,[d(o(xe),{class:`w-4 h-4 text-foreground-muted`}),a[3]||=p(` Build info `,-1),c(`span`,He,_(O.value?.version||o(`—`)),1)]),c(`dl`,Ue,[a[4]||=c(`dt`,{class:`text-foreground-muted`},` Version `,-1),c(`dd`,We,_(O.value?.version||o(`—`)),1),a[5]||=c(`dt`,{class:`text-foreground-muted`},` Commit `,-1),c(`dd`,Ge,_(O.value?.commit&&O.value.commit!==`unknown`?O.value.commit:`dev build`),1),a[6]||=c(`dt`,{class:`text-foreground-muted`},` Built `,-1),c(`dd`,Ke,_(ve(O.value?.buildTime)),1),a[7]||=c(`dt`,{class:`text-foreground-muted`},` Image `,-1),c(`dd`,qe,[c(`span`,Je,_(O.value?.image||o(`—`)),1),O.value?.image?(e(),l(`button`,{key:0,class:`p-1 rounded hover:bg-surface text-foreground-muted hover:text-white transition-colors shrink-0`,title:`Copy image reference`,"aria-label":`Copy image reference`,onClick:j},[d(o(te),{class:`w-3.5 h-3.5`})])):g(``,!0),k.value?(e(),l(`span`,Ye,`copied`)):g(``,!0)])])]),c(`section`,Xe,[c(`div`,null,[c(`h2`,Ze,[d(o(le),{class:`w-4 h-4 text-foreground-muted`}),a[8]||=p(` AI assistant `,-1)])]),d(Ne)]),c(`section`,Qe,[c(`div`,$e,[c(`div`,null,[c(`h2`,et,[d(o(be),{class:`w-4 h-4 text-foreground-muted`}),a[9]||=p(` Storage `,-1)]),a[10]||=c(`p`,{class:`text-xs text-foreground-muted mt-1 max-w-prose`},` Data directory usage and database maintenance. `,-1)]),d(D,{variant:`secondary`,loading:H.value,disabled:!B.value||H.value,onClick:Me},{default:t(()=>[d(o(Te),{class:`w-4 h-4`}),a[11]||=p(` Compact database `,-1)]),_:1},8,[`loading`,`disabled`])]),!B.value&&!V.value?(e(),l(`div`,tt,` Loading storage stats… `)):g(``,!0),V.value?(e(),l(`div`,nt,[a[12]||=c(`div`,{class:`font-semibold mb-1`},` Failed to load storage stats `,-1),c(`div`,rt,_(V.value),1)])):g(``,!0),B.value?(e(),l(`div`,it,[c(`div`,at,[G.value>0?(e(),l(`div`,{key:0,class:`bg-info h-full`,style:s({width:G.value+`%`}),title:`orva.db: ${K(B.value.db_bytes)}`},null,12,ot)):g(``,!0),Oe.value>0?(e(),l(`div`,{key:1,class:`bg-warning h-full`,style:s({width:Oe.value+`%`}),title:`WAL: ${K(B.value.wal_bytes)}`},null,12,st)):g(``,!0),ke.value>0?(e(),l(`div`,{key:2,class:`bg-success h-full`,style:s({width:ke.value+`%`}),title:`functions/: ${K(B.value.functions_bytes)}`},null,12,ct)):g(``,!0)]),c(`div`,lt,[c(`div`,ut,[a[13]||=c(`span`,{class:`flex items-center gap-2 text-foreground-muted`},[c(`span`,{class:`w-2 h-2 rounded-sm bg-info`}),p(` orva.db `)],-1),c(`span`,dt,_(K(B.value.db_bytes)),1)]),c(`div`,ft,[a[14]||=c(`span`,{class:`flex items-center gap-2 text-foreground-muted`},[c(`span`,{class:`w-2 h-2 rounded-sm bg-success`}),p(` functions/ `)],-1),c(`span`,pt,_(K(B.value.functions_bytes)),1)]),B.value.wal_bytes>0?(e(),l(`div`,mt,[a[15]||=c(`span`,{class:`flex items-center gap-2 text-foreground-muted`},[c(`span`,{class:`w-2 h-2 rounded-sm bg-warning`}),p(` orva.db-wal `)],-1),c(`span`,ht,_(K(B.value.wal_bytes)),1)])):g(``,!0),c(`div`,gt,[a[16]||=c(`span`,{class:`text-foreground-muted`},`total`,-1),c(`span`,_t,_(K(B.value.total_bytes)),1)])]),Ae.value>0?(e(),l(`div`,vt,_(K(Ae.value))+` reclaimable (`+_(B.value.db_free_pages)+` free SQLite pages) `,1)):g(``,!0)])):g(``,!0),W.value?(e(),l(`div`,yt,[p(` Compacted in `+_(W.value.duration_ms)+` ms and freed `,1),c(`span`,bt,_(K(W.value.freed_bytes)),1),p(` (`+_(K(W.value.before_bytes))+` → `+_(K(W.value.after_bytes))+`). `,1)])):g(``,!0),U.value?(e(),l(`div`,xt,[a[17]||=c(`div`,{class:`font-semibold mb-1`},` Compact failed `,-1),c(`div`,St,_(U.value),1)])):g(``,!0)]),c(`section`,Ct,[c(`div`,null,[c(`h2`,wt,[d(o(y),{class:`w-4 h-4 text-foreground-muted`}),a[18]||=p(` Account `,-1)]),a[19]||=c(`p`,{class:`text-xs text-foreground-muted mt-1`},` Update your password or end your session. `,-1)]),c(`form`,{class:`space-y-3 pt-2`,onSubmit:C(Ee,[`prevent`])},[a[24]||=c(`h3`,{class:`text-sm font-medium text-foreground`},` Change password `,-1),c(`div`,Tt,[c(`div`,Et,[a[20]||=c(`label`,{for:`settings-current-password`,class:`text-xs text-foreground-muted`},`Current password`,-1),r(c(`input`,{id:`settings-current-password`,"onUpdate:modelValue":a[0]||=e=>M.value.current=e,type:`password`,autocomplete:`current-password`,"aria-describedby":`pw-error`,class:`bg-surface border border-border rounded-md px-3 py-2 text-sm text-white placeholder:text-foreground-muted focus:outline-none focus:ring-1 focus:ring-primary`,placeholder:`••••••••`},null,512),[[E,M.value.current]])]),c(`div`,Dt,[a[21]||=c(`label`,{for:`settings-new-password`,class:`text-xs text-foreground-muted`},`New password`,-1),r(c(`input`,{id:`settings-new-password`,"onUpdate:modelValue":a[1]||=e=>M.value.next=e,type:`password`,autocomplete:`new-password`,"aria-describedby":`pw-error`,class:`bg-surface border border-border rounded-md px-3 py-2 text-sm text-white placeholder:text-foreground-muted focus:outline-none focus:ring-1 focus:ring-primary`,placeholder:`••••••••`},null,512),[[E,M.value.next]])]),c(`div`,Ot,[a[22]||=c(`label`,{for:`settings-confirm-password`,class:`text-xs text-foreground-muted`},`Confirm new password`,-1),r(c(`input`,{id:`settings-confirm-password`,"onUpdate:modelValue":a[2]||=e=>M.value.confirm=e,type:`password`,autocomplete:`new-password`,"aria-describedby":`pw-error`,class:`bg-surface border border-border rounded-md px-3 py-2 text-sm text-white placeholder:text-foreground-muted focus:outline-none focus:ring-1 focus:ring-primary`,placeholder:`••••••••`},null,512),[[E,M.value.confirm]])])]),P.value?(e(),l(`div`,kt,_(P.value),1)):g(``,!0),F.value?(e(),l(`div`,At,` Password updated successfully. `)):g(``,!0),d(D,{type:`submit`,variant:`primary`,loading:N.value,disabled:N.value},{default:t(()=>[d(o(y),{class:`w-4 h-4`}),a[23]||=p(` Update password `,-1)]),_:1},8,[`loading`,`disabled`])],32),c(`div`,jt,[d(D,{variant:`danger`,onClick:De},{default:t(()=>[d(o(Se),{class:`w-4 h-4`}),a[25]||=p(` Log out `,-1)]),_:1})])]),c(`section`,Mt,[c(`div`,Nt,[c(`div`,null,[c(`h2`,Pt,[d(o(w),{class:`w-4 h-4 text-foreground-muted`}),a[26]||=p(` Connected applications `,-1)]),c(`p`,Ft,[a[28]||=p(` OAuth clients with access to this instance. Add connectors from `,-1),d(o(fe),{to:`/docs#mcp`,class:`text-primary hover:underline`},{default:t(()=>[...a[27]||=[p(` Docs `,-1)]]),_:1})])]),q.value.length>0?(e(),l(`span`,It,_(q.value.length)+` active `,1)):g(``,!0)]),Y.value?(e(),l(`div`,Lt,[a[29]||=c(`div`,{class:`font-semibold mb-1`},` Failed to load connected apps `,-1),c(`div`,Rt,_(Y.value),1)])):J.value?(e(),l(`div`,zt,` Loading… `)):q.value.length===0?(e(),l(`div`,Bt,[...a[30]||=[c(`p`,{class:`text-xs text-foreground-muted`},` No connected applications. `,-1)]])):(e(),l(`ul`,Vt,[(e(!0),l(m,null,f(q.value,t=>(e(),l(`li`,{key:t.id,class:`px-5 py-3 flex items-start gap-3`},[(e(),ee(i(o(Re)(t.client_name).icon),{class:u([`w-5 h-5 mt-0.5 shrink-0`,o(Re)(t.client_name).accent])},null,8,[`class`])),c(`div`,Ht,[c(`div`,Ut,_(t.client_name),1),c(`div`,Wt,[c(`span`,null,`Authorized `+_(o(A)(t.issued_at)),1),t.last_used_at?(e(),l(`span`,Gt,` · Last used `+_(o(A)(t.last_used_at)),1)):(e(),l(`span`,Kt,`· Never used`)),t.refresh_expires_at?(e(),l(`span`,qt,` · Re-consent `+_(o(A)(t.refresh_expires_at)),1)):g(``,!0)]),c(`div`,Jt,[(e(!0),l(m,null,f(Ie(t.scope),t=>(e(),l(`span`,{key:t,class:u([`text-xs px-1.5 py-0.5 rounded font-mono`,Le(t)])},_(t),3))),128))])]),c(`button`,{type:`button`,class:`text-xs text-foreground-muted hover:text-danger-fg transition-colors flex items-center gap-1 shrink-0 self-center`,disabled:X.value===t.id,onClick:e=>Fe(t)},[d(o(x),{class:`w-3.5 h-3.5`}),a[31]||=p(` Revoke `,-1)],8,Yt)]))),128))]))]),c(`section`,Xt,[c(`div`,Zt,[c(`div`,null,[c(`h2`,Qt,[d(o(Ce),{class:`w-4 h-4 text-foreground-muted`}),a[32]||=p(` Active sessions `,-1)]),a[33]||=c(`p`,{class:`text-xs text-foreground-muted mt-1 max-w-prose`},` Browsers signed in to this instance. `,-1)]),Z.value.length>0?(e(),l(`span`,$t,_(Z.value.length)+` active `,1)):g(``,!0)]),Q.value?(e(),l(`div`,en,[a[34]||=c(`div`,{class:`font-semibold mb-1`},` Failed to load sessions `,-1),c(`div`,tn,_(Q.value),1)])):(e(),l(`ul`,nn,[(e(!0),l(m,null,f(Z.value,t=>(e(),l(`li`,{key:t.prefix,class:`px-5 py-3 flex items-start gap-3`},[d(o(Ce),{class:u([`w-5 h-5 mt-0.5 shrink-0`,t.current?`text-success-fg`:`text-foreground-muted`])},null,8,[`class`]),c(`div`,rn,[c(`div`,an,[t.current?(e(),l(`span`,on,`This session`)):(e(),l(`span`,sn,_(bn(t.prefix)),1)),t.current?(e(),l(`span`,cn,` current `)):g(``,!0)]),c(`div`,ln,` Signed in `+_(o(A)(t.created_at))+` · expires `+_(o(A)(t.expires_at)),1)]),t.current?g(``,!0):(e(),l(`button`,{key:0,type:`button`,class:`text-xs text-foreground-muted hover:text-danger-fg transition-colors flex items-center gap-1 shrink-0 self-center`,disabled:$.value===t.prefix,onClick:e=>yn(t)},[d(o(x),{class:`w-3.5 h-3.5`}),a[35]||=p(` Revoke `,-1)],8,un))]))),128))]))]),c(`section`,dn,[c(`div`,fn,[c(`div`,null,[c(`h2`,pn,[d(o(ye),{class:`w-4 h-4 text-foreground-muted`}),a[36]||=p(` Backup & Restore `,-1)]),a[37]||=c(`p`,{class:`text-xs text-foreground-muted mt-1 max-w-prose`},` Download or restore a complete instance snapshot. `,-1),a[38]||=c(`p`,{class:`text-xs text-warning-fg mt-2 max-w-prose`},` Backups contain secret keys. Store them securely. `,-1)])]),c(`div`,mn,[d(D,{variant:`primary`,onClick:xn},{default:t(()=>[d(o(ne),{class:`w-4 h-4`}),a[39]||=p(` Download backup `,-1)]),_:1}),d(D,{variant:`secondary`,loading:L.value,onClick:Sn},{default:t(()=>[d(o(we),{class:`w-4 h-4`}),a[40]||=p(` Restore from backup `,-1)]),_:1},8,[`loading`]),c(`input`,{ref_key:`fileInput`,ref:I,type:`file`,accept:`.tar.gz,.tgz,application/gzip`,class:`hidden`,onChange:Cn},null,544)]),R.value?(e(),l(`div`,hn,[a[41]||=c(`div`,{class:`font-semibold mb-1`},` Restore failed `,-1),c(`div`,gn,_(R.value),1)])):g(``,!0),z.value?(e(),l(`div`,_n,[a[42]||=p(` Restore complete. The server is restarting to load the new data. Reload in a few seconds. `,-1),c(`button`,{class:`underline ml-1`,onClick:wn},` Reload now `)])):g(``,!0)])]))}});export{$ as default}; \ No newline at end of file +import{D as e,F as t,G as n,I as r,M as i,T as a,Z as o,_t as s,c,d as l,gt as u,h as d,k as f,l as ee,m as p,r as m,s as h,u as g,vt as _}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as te}from"./copy-BqdwwcxC.js";import{i as ne,r as re,t as v}from"./ModelMenu-BtMjUXcx.js";import{t as y}from"./key-round-BKXtbC85.js";import{t as b}from"./terminal-BQdlNiyt.js";import{t as x}from"./trash-2-DaeYqnW_.js";import{$ as S,Bt as C,Ct as w,D as ie,Ft as ae,G as oe,I as T,It as se,Lt as E,Q as ce,Tt as le,_t as D,ct as ue,dt as de,ft as fe,gt as pe,jt as O,mt as me,n as he,nt as ge,ut as k}from"./index-DTqMKlE1.js";import{t as _e}from"./clipboard-D_9N0yai.js";import{t as A}from"./time-D8OmbYzY.js";import{t as j}from"./Input-DQ-tWGkn.js";var ve=O(`code-xml`,[[`path`,{d:`m18 16 4-4-4-4`,key:`1inbqp`}],[`path`,{d:`m6 8-4 4 4 4`,key:`15zrgr`}],[`path`,{d:`m14.5 4-5 16`,key:`e7oirm`}]]),ye=O(`database-backup`,[[`ellipse`,{cx:`12`,cy:`5`,rx:`9`,ry:`3`,key:`msslwz`}],[`path`,{d:`M3 12a9 3 0 0 0 5 2.69`,key:`1ui2ym`}],[`path`,{d:`M21 9.3V5`,key:`6k6cib`}],[`path`,{d:`M3 5v14a9 3 0 0 0 6.47 2.88`,key:`i62tjy`}],[`path`,{d:`M12 12v4h4`,key:`1bxaet`}],[`path`,{d:`M13 20a5 5 0 0 0 9-3 4.5 4.5 0 0 0-4.5-4.5c-1.33 0-2.54.54-3.41 1.41L12 16`,key:`1f4ei9`}]]),be=O(`hard-drive`,[[`path`,{d:`M10 16h.01`,key:`1bzywj`}],[`path`,{d:`M2.212 11.577a2 2 0 0 0-.212.896V18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5.527a2 2 0 0 0-.212-.896L18.55 5.11A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z`,key:`18tbho`}],[`path`,{d:`M21.946 12.013H2.054`,key:`zqlbp7`}],[`path`,{d:`M6 16h.01`,key:`1pmjb7`}]]),xe=O(`info`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M12 16v-4`,key:`1dtifu`}],[`path`,{d:`M12 8h.01`,key:`e9boi3`}]]),Se=O(`log-out`,[[`path`,{d:`m16 17 5-5-5-5`,key:`1bji2h`}],[`path`,{d:`M21 12H9`,key:`dn1m92`}],[`path`,{d:`M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4`,key:`1uf3rs`}]]),Ce=O(`monitor`,[[`rect`,{width:`20`,height:`14`,x:`2`,y:`3`,rx:`2`,key:`48i651`}],[`line`,{x1:`8`,x2:`16`,y1:`21`,y2:`21`,key:`1svkeh`}],[`line`,{x1:`12`,x2:`12`,y1:`17`,y2:`21`,key:`vw1qmm`}]]),M=O(`mouse-pointer-2`,[[`path`,{d:`M4.037 4.688a.495.495 0 0 1 .651-.651l16 6.5a.5.5 0 0 1-.063.947l-6.124 1.58a2 2 0 0 0-1.438 1.435l-1.579 6.126a.5.5 0 0 1-.947.063z`,key:`edeuup`}]]),we=O(`upload`,[[`path`,{d:`M12 3v12`,key:`1x0j5s`}],[`path`,{d:`m17 8-5-5-5 5`,key:`7q97r8`}],[`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`,key:`ih7n3h`}]]),Te=O(`wand-sparkles`,[[`path`,{d:`m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72`,key:`ul74o6`}],[`path`,{d:`m14 7 3 3`,key:`1r5n42`}],[`path`,{d:`M5 6v4`,key:`ilb8ba`}],[`path`,{d:`M19 14v4`,key:`blhpug`}],[`path`,{d:`M10 2v2`,key:`7u0qdc`}],[`path`,{d:`M7 8H3`,key:`zfb6yr`}],[`path`,{d:`M21 16h-4`,key:`1cnmox`}],[`path`,{d:`M11 3H9`,key:`1obp7u`}]]),N={class:`space-y-6`},P={class:`space-y-3`},F={key:0,class:`grid grid-cols-1 gap-3 sm:grid-cols-2 sm:items-start`},Ee=[`value`],De=[`value`],I={class:`mt-1.5`},L={key:0,class:`text-xs text-danger-fg sm:col-span-2`},R={class:`divide-y divide-border border-y border-border`},z={class:`flex items-center gap-2 px-3 py-2`},B={class:`font-mono text-sm text-foreground`},V={key:0,class:`text-xs text-foreground-muted`},H={key:1,class:`text-xs uppercase tracking-label rounded px-1.5 py-0.5 bg-primary/15 text-primary-hover`},U={key:0,class:`text-xs text-foreground-muted`},W={class:`group border-t border-border pt-4`},G={class:`mt-4 space-y-3`},Oe=[`value`],ke={key:0,class:`space-y-3 border-t border-border pt-5`},Ae={class:`space-y-2`},je=[`value`],Me={key:0,class:`h-2 w-2 rounded-full bg-primary`},K={class:`min-w-0`},q={class:`block text-sm font-medium text-foreground`},J={class:`mt-0.5 block text-xs text-foreground-muted leading-snug`},Ne={__name:`AISettingsPanel`,setup(i){let s=re(),ee=pe();async function te(e){await ee.ask({title:`Remove provider?`,message:`This permanently removes the provider and its encrypted API key.`,danger:!0,confirmLabel:`Remove`})&&s.deleteProvider(e)}let ne=[`openai`,`anthropic`,`groq`,`gemini`,`ollama`,`mistral`,`openrouter`,`xai`,`cohere`],y=[{value:`all_writes`,label:`Ask before changes (recommended)`,hint:`Confirm creates, updates, and deletes.`},{value:`destructive_only`,label:`Ask before deletes only`,hint:`Confirm deletes; run other changes automatically.`},{value:`auto`,label:`Bypass: allow everything`,hint:`Run every action without confirmation.`}],b=n({provider:`openai`,label:``,api_key:``,base_url:``}),x=[`ollama`],S=h(()=>x.includes(b.value.provider)),C=h(()=>S.value&&!b.value.base_url.trim()?`Base URL is required for Ollama.`:``),w=h(()=>!!b.value.provider&&!C.value),ie=h(()=>S.value?`http://192.168.1.50:11434 or http://ollama.lan:11434`:`https://api.openai.com/v1 or https://your-host/v1`),oe=h(()=>S.value?`Where your server is listening. A private LAN address works; Orva permits it when your egress blocklist does.`:`For custom / self-hosted endpoints. Either with or without /v1 works. A private LAN address works too.`),T=n(!1),E=n(!1);a(async()=>{await s.loadSettings(),await s.loadProviders()});async function ce(e){await s.selectProvider(e.target.value)}async function le(){if(w.value){T.value=!0;try{await s.saveProvider({provider:b.value.provider,label:b.value.label,api_key:b.value.api_key,base_url:b.value.base_url,enabled:!0}),b.value.api_key=``}finally{T.value=!1}}}async function ue(){E.value=!0;try{await s.saveSettings(s.settings)}finally{E.value=!1}}return(n,i)=>(e(),l(`div`,N,[c(`section`,P,[i[9]||=c(`div`,null,[c(`h3`,{class:`text-sm font-semibold text-foreground`},` Providers `),c(`p`,{class:`text-xs text-foreground-muted mt-1.5 max-w-prose leading-snug`},` Keys are encrypted at rest. Choose which provider and model Chat uses. `)],-1),o(s).providers.length?(e(),l(`div`,F,[c(`div`,null,[i[5]||=c(`label`,{for:`ai-active-provider`,class:`text-xs font-medium text-foreground-muted uppercase tracking-wide`},`Active provider`,-1),c(`select`,{id:`ai-active-provider`,value:o(s).selectedProviderId||``,class:`mt-1.5 h-10 w-full bg-background border border-border rounded-md text-sm px-3 text-foreground transition-colors duration-200 focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,onChange:ce},[(e(!0),l(m,null,f(o(s).providers,t=>(e(),l(`option`,{key:t.id,value:t.id},_(t.label?`${t.provider} (${t.label})`:t.provider),9,De))),128))],40,Ee)]),c(`div`,null,[i[6]||=c(`label`,{for:`ai-active-model`,class:`text-xs font-medium text-foreground-muted uppercase tracking-wide`},` Active model `,-1),c(`div`,I,[d(v,{wide:``,"trigger-id":`ai-active-model`})])]),o(s).modelsError?(e(),l(`p`,L,` Models could not be loaded. Check the provider endpoint and credentials. `)):g(``,!0)])):g(``,!0),c(`div`,R,[(e(!0),l(m,null,f(o(s).providers,n=>(e(),l(`div`,{key:n.id,class:`py-1`},[c(`div`,z,[c(`span`,B,_(n.provider),1),n.label?(e(),l(`span`,V,_(n.label),1)):g(``,!0),o(s).selectedProviderId===n.id?(e(),l(`span`,H,`active`)):g(``,!0),c(`span`,{class:u([`text-xs uppercase tracking-label rounded px-1.5 py-0.5`,n.has_key?`bg-success-tint text-success-fg`:`bg-surface-hover text-foreground-muted`])},_(n.has_key?`key set`:`no key`),3),i[8]||=c(`span`,{class:`flex-1`},null,-1),d(D,{size:`xs`,variant:`ghost`,onClick:e=>te(n.id)},{default:t(()=>[...i[7]||=[p(` Remove `,-1)]]),_:1},8,[`onClick`])])]))),128)),o(s).providers.length?g(``,!0):(e(),l(`p`,U,` No providers configured yet. `))])]),c(`details`,W,[i[13]||=c(`summary`,{class:`flex cursor-pointer list-none items-center justify-between rounded-sm text-sm font-semibold text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary`},[p(` Add provider `),c(`span`,{class:`text-foreground-muted transition-transform group-open:rotate-45`,"aria-hidden":`true`},`+`)],-1),c(`div`,G,[i[12]||=c(`p`,{class:`text-xs text-foreground-muted max-w-prose leading-snug`},[p(` For compatible endpoints, choose `),c(`span`,{class:`font-mono`},`openai`),p(` and add the Base URL. `)],-1),c(`div`,null,[i[10]||=c(`label`,{for:`ai-provider`,class:`text-xs font-medium text-foreground-muted uppercase tracking-wide`},`Provider`,-1),r(c(`select`,{id:`ai-provider`,"onUpdate:modelValue":i[0]||=e=>b.value.provider=e,class:`mt-1.5 w-full bg-background border border-border rounded-md text-sm px-3 py-2 text-foreground transition-colors duration-200 focus:outline-none focus:ring-1 focus:ring-white focus:border-white`},[(e(),l(m,null,f(ne,e=>c(`option`,{key:e,value:e},_(e),9,Oe)),64))],512),[[se,b.value.provider]])]),d(j,{modelValue:b.value.label,"onUpdate:modelValue":i[1]||=e=>b.value.label=e,label:`Label`,placeholder:`e.g. personal, work (optional)`},null,8,[`modelValue`]),d(j,{modelValue:b.value.base_url,"onUpdate:modelValue":i[2]||=e=>b.value.base_url=e,label:S.value?`Base URL (required)`:`Base URL (optional)`,placeholder:ie.value,hint:oe.value,error:C.value,required:S.value},null,8,[`modelValue`,`label`,`placeholder`,`hint`,`error`,`required`]),d(j,{modelValue:b.value.api_key,"onUpdate:modelValue":i[3]||=e=>b.value.api_key=e,label:`API key`,type:`password`,placeholder:`sk-…`,hint:`Stored encrypted; never shown again. Leave blank when updating to keep the current key.`},null,8,[`modelValue`]),d(D,{variant:`primary`,loading:T.value,disabled:!w.value,onClick:le},{default:t(()=>[...i[11]||=[p(` Save provider `,-1)]]),_:1},8,[`loading`,`disabled`])])]),o(s).settings?(e(),l(`section`,ke,[i[17]||=c(`h3`,{class:`text-sm font-semibold text-foreground`},` Defaults `,-1),c(`fieldset`,Ae,[i[14]||=c(`legend`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide`},` Approval policy `,-1),i[15]||=c(`p`,{class:`text-xs text-foreground-muted leading-snug`},` Choose when changes require confirmation. `,-1),(e(),l(m,null,f(y,t=>c(`label`,{key:t.value,class:`flex cursor-pointer items-start gap-3 px-1 py-2 transition-colors hover:bg-surface-hover focus-within:outline-none focus-within:ring-2 focus-within:ring-inset focus-within:ring-primary`},[r(c(`input`,{"onUpdate:modelValue":i[4]||=e=>o(s).settings.approval_policy=e,type:`radio`,name:`approval-policy`,value:t.value,class:`sr-only`},null,8,je),[[ae,o(s).settings.approval_policy]]),c(`span`,{class:u([`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full border transition-colors`,o(s).settings.approval_policy===t.value?`border-primary`:`border-foreground-muted/50`])},[o(s).settings.approval_policy===t.value?(e(),l(`span`,Me)):g(``,!0)],2),c(`span`,K,[c(`span`,q,_(t.label),1),c(`span`,J,_(t.hint),1)])])),64))]),d(D,{variant:`primary`,loading:E.value,onClick:ue},{default:t(()=>[...i[16]||=[p(` Save defaults `,-1)]]),_:1},8,[`loading`])])):g(``,!0)]))}},Y={},X={xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 24 24`,fill:`currentColor`,"aria-hidden":`true`};function Pe(t,n){return e(),l(`svg`,X,[...n[0]||=[c(`path`,{d:`M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.4069-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z`},null,-1)]])}var Fe=k(Y,[[`render`,Pe]]),Ie={},Le={xmlns:`http://www.w3.org/2000/svg`,viewBox:`0 0 24 24`,fill:`currentColor`,"aria-hidden":`true`};function Z(t,n){return e(),l(`svg`,Le,[...n[0]||=[c(`path`,{d:`M4.709 15.955l4.72-2.647.08-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.146-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.418 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.349-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.049.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.658-.851-.747-1.926-1.622h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.608.213-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.908-1.312-.006.006z`},null,-1)]])}var Q=k(Ie,[[`render`,Z]]);function Re(e){let t=(e||``).toLowerCase();return t.includes(`claude code`)?{icon:b,accent:`text-orange-400`}:t.includes(`chatgpt`)||t.includes(`openai`)?{icon:Fe,accent:`text-emerald-400`}:t.includes(`claude`)||t.includes(`anthropic`)?{icon:Q,accent:`text-orange-400`}:t.includes(`cursor`)?{icon:M,accent:`text-blue-400`}:t.includes(`vscode`)||t.includes(`vs code`)||t.includes(`code`)?{icon:ve,accent:`text-blue-400`}:{icon:w,accent:`text-foreground-muted`}}var ze={class:`space-y-8`},Be={class:`group border-b border-border pb-6`},Ve={class:`flex cursor-pointer list-none items-center gap-2 text-sm font-semibold text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary [&::-webkit-details-marker]:hidden`},He={class:`ml-auto text-xs font-normal text-foreground-muted group-open:hidden`},Ue={class:`mt-4 grid grid-cols-[max-content,1fr] gap-x-6 gap-y-2 text-xs`},We={class:`font-mono text-white`},Ge={class:`font-mono text-white`},Ke={class:`font-mono text-white`},qe={class:`font-mono text-white flex items-center gap-2 min-w-0`},Je={class:`truncate`},Ye={key:1,class:`text-xs text-primary-hover shrink-0`},Xe={id:`ai`,class:`scroll-mt-6 space-y-4 border-b border-border pb-8`},Ze={class:`text-base font-semibold text-white flex items-center gap-2`},Qe={class:`space-y-4 border-b border-border pb-8`},$e={class:`flex items-start justify-between gap-4`},et={class:`text-base font-semibold text-white flex items-center gap-2`},tt={key:0,class:`text-xs text-foreground-muted italic`},nt={key:1,class:`rounded-md border border-danger-ring bg-danger-tint p-3 text-xs text-danger-fg`},rt={class:`font-mono break-all`},it={key:2,class:`space-y-3`},at={class:`h-2 w-full rounded-full overflow-hidden bg-border/60 flex`},ot=[`title`],st=[`title`],ct=[`title`],lt={class:`grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-1.5 text-xs`},ut={class:`flex items-center justify-between`},dt={class:`font-mono text-white`},ft={class:`flex items-center justify-between`},pt={class:`font-mono text-white`},mt={key:0,class:`flex items-center justify-between`},ht={class:`font-mono text-white`},gt={class:`flex items-center justify-between`},_t={class:`font-mono text-white font-semibold`},vt={key:0,class:`text-xs text-foreground-muted pt-1 border-t border-border`},yt={key:3,class:`rounded-md border border-success-ring bg-success-tint p-3 text-xs text-success-fg`},bt={class:`font-mono`},xt={key:4,class:`rounded-md border border-danger-ring bg-danger-tint p-3 text-xs text-danger-fg`},St={class:`font-mono break-all`},Ct={class:`space-y-4 border-b border-border pb-8`},wt={class:`text-base font-semibold text-white flex items-center gap-2`},Tt={class:`grid grid-cols-1 sm:grid-cols-3 gap-3`},Et={class:`flex flex-col gap-1`},Dt={class:`flex flex-col gap-1`},Ot={class:`flex flex-col gap-1`},kt={key:0,id:`pw-error`,class:`rounded-md border border-danger-ring bg-danger-tint p-2.5 text-xs text-danger-fg`},At={key:1,class:`rounded-md border border-success-ring bg-success-tint p-2.5 text-xs text-success-fg`},jt={class:`pt-2 border-t border-border`},Mt={class:`space-y-4 border-b border-border pb-8`},Nt={class:`flex items-start justify-between gap-4`},Pt={class:`text-base font-semibold text-white flex items-center gap-2`},Ft={class:`text-xs text-foreground-muted mt-1 max-w-prose`},It={key:0,class:`text-xs text-foreground-muted self-center`},Lt={key:0,class:`rounded-md border border-danger-ring bg-danger-tint p-3 text-xs text-danger-fg`},Rt={class:`font-mono break-all`},zt={key:1,class:`text-xs text-foreground-muted italic`},Bt={key:2,class:`py-3`},Vt={key:3,class:`divide-y divide-border -mx-5`},Ht={class:`flex-1 min-w-0`},Ut={class:`text-sm font-medium text-white truncate`},Wt={class:`text-xs text-foreground-muted mt-0.5 flex flex-wrap gap-x-3 gap-y-0.5`},Gt={key:0},Kt={key:1,class:`italic opacity-70`},qt={key:2},Jt={class:`flex flex-wrap gap-1 mt-2`},Yt=[`disabled`,`onClick`],Xt={class:`space-y-4 border-b border-border pb-8`},Zt={class:`flex items-start justify-between gap-4`},Qt={class:`text-base font-semibold text-white flex items-center gap-2`},$t={key:0,class:`text-xs text-foreground-muted self-center`},en={key:0,class:`rounded-md border border-danger-ring bg-danger-tint p-3 text-xs text-danger-fg`},tn={class:`font-mono break-all`},nn={key:1,class:`divide-y divide-border -mx-5`},rn={class:`flex-1 min-w-0`},an={class:`text-sm font-medium text-white flex items-center gap-2 flex-wrap`},on={key:0},sn={key:1,class:`font-mono text-xs`},cn={key:2,class:`text-xs px-1.5 py-0.5 rounded bg-success-tint text-success-fg font-medium`},ln={class:`text-xs text-foreground-muted mt-0.5`},un=[`disabled`,`onClick`],dn={class:`space-y-4`},fn={class:`flex items-start justify-between gap-4`},pn={class:`text-base font-semibold text-white flex items-center gap-2`},mn={class:`flex flex-col sm:flex-row gap-3 pt-2 border-t border-border`},hn={key:0,class:`rounded-md border border-danger-ring bg-danger-tint p-3 text-xs text-danger-fg`},gn={class:`font-mono break-all`},_n={key:1,class:`rounded-md border border-success-ring bg-success-tint p-3 text-xs text-success-fg`},$=Object.assign({name:`SettingsView`},{__name:`Settings`,setup(re){let v=pe(),b=de(),ae=me(),se=he(),O=h(()=>se.buildInfo),k=n(!1),j=async()=>{O.value?.image&&await _e(O.value.image)&&(k.value=!0,setTimeout(()=>{k.value=!1},1500))},ve=e=>{if(!e||e===`unknown`)return`—`;try{return new Date(e).toISOString().replace(`T`,` `).replace(/\.\d+Z$/,` UTC`)}catch{return e}},M=n({current:``,next:``,confirm:``}),N=n(!1),P=n(``),F=n(!1),Ee=async()=>{if(P.value=``,F.value=!1,!M.value.current||!M.value.next||!M.value.confirm){P.value=`All three fields are required.`;return}if(M.value.next.length<8){P.value=`New password must be at least 8 characters.`;return}if(M.value.next!==M.value.confirm){P.value=`New password and confirmation do not match.`;return}N.value=!0;try{await b.changePassword(M.value.current,M.value.next),F.value=!0,M.value={current:``,next:``,confirm:``}}catch(e){P.value=e?.response?.data?.error?.message||`Failed to update password.`}finally{N.value=!1}},De=async()=>{await b.logout(),ae.push(`/login`)},I=n(null),L=n(!1),R=n(``),z=n(!1),B=n(null),V=n(``),H=n(!1),U=n(``),W=n(null),G=h(()=>B.value&&B.value.total_bytes>0?Math.max(.5,B.value.db_bytes/B.value.total_bytes*100):0),Oe=h(()=>B.value&&B.value.total_bytes>0&&B.value.wal_bytes>0?Math.max(.5,B.value.wal_bytes/B.value.total_bytes*100):0),ke=h(()=>B.value&&B.value.total_bytes>0?Math.max(.5,B.value.functions_bytes/B.value.total_bytes*100):0),Ae=h(()=>B.value?(B.value.db_free_pages||0)*(B.value.db_page_size||0):0),je=async()=>{try{V.value=``;let e=await ie();B.value=e.data}catch(e){V.value=e?.response?.data?.error?.message||e?.message||`unknown error`}},Me=async()=>{if(await v.ask({title:`Compact database?`,message:`VACUUM rewrites orva.db to drop the freelist and shrink the file. It holds an exclusive lock for the duration; every other writer (deploys, invocations recording executions, KV puts, job enqueues) blocks until it returns. Typical runtime is sub-second, but a heavily-loaded instance can stall for several seconds.`,confirmLabel:`Compact`,danger:!1})){H.value=!0,U.value=``,W.value=null;try{let e=await ge();W.value=e.data,await je()}catch(e){U.value=e?.response?.data?.error?.message||e?.message||`vacuum failed`}finally{H.value=!1}}},K=e=>{if(e==null||isNaN(e))return`—`;let t=1024;if(e=t&&i{window.location.hash===`#ai`&&window.requestAnimationFrame(()=>{document.getElementById(`ai`)?.scrollIntoView({behavior:`smooth`,block:`start`})})});let q=n([]),J=n(!1),Y=n(``),X=n(``),Pe=async()=>{J.value=!0,Y.value=``;try{let e=await T();q.value=e.data.apps||[]}catch(e){Y.value=e?.response?.data?.error?.message||e?.message||`unknown error`}finally{J.value=!1}},Fe=async e=>{if(await v.ask({title:`Revoke ${e.client_name}?`,message:`${e.client_name} will lose access immediately. Any in-flight request will fail with 401. The connector can be re-authorized at any time from the originating app.`,confirmLabel:`Revoke`,danger:!0})){X.value=e.id;try{await ce(e.id),await Pe()}catch(e){Y.value=e?.response?.data?.error?.message||e?.message||`failed to revoke`}finally{X.value=``}}},Ie=e=>(e||``).split(/\s+/).filter(Boolean),Le=e=>{switch(e){case`admin`:return`bg-danger-tint text-danger-fg`;case`write`:return`bg-warning-tint text-warning-fg`;case`invoke`:return`bg-info-tint text-info-fg`;case`read`:return`bg-foreground-muted/15 text-foreground-muted`;default:return`bg-foreground-muted/10 text-foreground-muted`}},Z=n([]),Q=n(``),$=n(``),vn=async()=>{Q.value=``;try{let e=await oe();Z.value=e.data.sessions||[]}catch(e){Q.value=e?.response?.data?.error?.message||e?.message||`unknown error`}},yn=async e=>{if(await v.ask({title:`Revoke this session?`,message:`The browser using this session will be logged out on its next request. Use this if you suspect a device was lost or to clean up old logins.`,confirmLabel:`Revoke`,danger:!0})){$.value=e.prefix;try{await S(e.prefix),await vn()}catch(e){Q.value=e?.response?.data?.error?.message||e?.message||`failed to revoke`}finally{$.value=``}}},bn=e=>!e||e.length<8?e:e.slice(0,1)+`••••••••`+e.slice(-4);a(Pe),a(vn);let xn=()=>{window.location.assign(`/api/v1/backup?ts=`+Date.now())},Sn=()=>{R.value=``,z.value=!1,I.value?.click()},Cn=async e=>{let t=e.target.files?.[0];if(e.target.value=``,t&&await v.ask({title:`Restore from backup?`,message:`This will replace the live database and function code with the contents of "${t.name}". The current orva.db is moved aside as orva.db.before-restore- in case rollback is needed. You will need to reload after restore completes.`,confirmLabel:`Restore`,danger:!0})){L.value=!0,R.value=``,z.value=!1;try{await ue(t),z.value=!0}catch(e){R.value=e?.response?.data?.error?.message||e?.message||`Restore failed`}finally{L.value=!1}}},wn=()=>{window.location.reload()};return(n,a)=>(e(),l(`div`,ze,[a[43]||=c(`div`,null,[c(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` Settings `),c(`p`,{class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},` Instance, access, storage, backups, and AI. `)],-1),c(`details`,Be,[c(`summary`,Ve,[d(o(xe),{class:`w-4 h-4 text-foreground-muted`}),a[3]||=p(` Build info `,-1),c(`span`,He,_(O.value?.version||o(`—`)),1)]),c(`dl`,Ue,[a[4]||=c(`dt`,{class:`text-foreground-muted`},` Version `,-1),c(`dd`,We,_(O.value?.version||o(`—`)),1),a[5]||=c(`dt`,{class:`text-foreground-muted`},` Commit `,-1),c(`dd`,Ge,_(O.value?.commit&&O.value.commit!==`unknown`?O.value.commit:`dev build`),1),a[6]||=c(`dt`,{class:`text-foreground-muted`},` Built `,-1),c(`dd`,Ke,_(ve(O.value?.buildTime)),1),a[7]||=c(`dt`,{class:`text-foreground-muted`},` Image `,-1),c(`dd`,qe,[c(`span`,Je,_(O.value?.image||o(`—`)),1),O.value?.image?(e(),l(`button`,{key:0,class:`p-1 rounded hover:bg-surface text-foreground-muted hover:text-white transition-colors shrink-0`,title:`Copy image reference`,"aria-label":`Copy image reference`,onClick:j},[d(o(te),{class:`w-3.5 h-3.5`})])):g(``,!0),k.value?(e(),l(`span`,Ye,`copied`)):g(``,!0)])])]),c(`section`,Xe,[c(`div`,null,[c(`h2`,Ze,[d(o(le),{class:`w-4 h-4 text-foreground-muted`}),a[8]||=p(` AI assistant `,-1)])]),d(Ne)]),c(`section`,Qe,[c(`div`,$e,[c(`div`,null,[c(`h2`,et,[d(o(be),{class:`w-4 h-4 text-foreground-muted`}),a[9]||=p(` Storage `,-1)]),a[10]||=c(`p`,{class:`text-xs text-foreground-muted mt-1 max-w-prose`},` Data directory usage and database maintenance. `,-1)]),d(D,{variant:`secondary`,loading:H.value,disabled:!B.value||H.value,onClick:Me},{default:t(()=>[d(o(Te),{class:`w-4 h-4`}),a[11]||=p(` Compact database `,-1)]),_:1},8,[`loading`,`disabled`])]),!B.value&&!V.value?(e(),l(`div`,tt,` Loading storage stats… `)):g(``,!0),V.value?(e(),l(`div`,nt,[a[12]||=c(`div`,{class:`font-semibold mb-1`},` Failed to load storage stats `,-1),c(`div`,rt,_(V.value),1)])):g(``,!0),B.value?(e(),l(`div`,it,[c(`div`,at,[G.value>0?(e(),l(`div`,{key:0,class:`bg-info h-full`,style:s({width:G.value+`%`}),title:`orva.db: ${K(B.value.db_bytes)}`},null,12,ot)):g(``,!0),Oe.value>0?(e(),l(`div`,{key:1,class:`bg-warning h-full`,style:s({width:Oe.value+`%`}),title:`WAL: ${K(B.value.wal_bytes)}`},null,12,st)):g(``,!0),ke.value>0?(e(),l(`div`,{key:2,class:`bg-success h-full`,style:s({width:ke.value+`%`}),title:`functions/: ${K(B.value.functions_bytes)}`},null,12,ct)):g(``,!0)]),c(`div`,lt,[c(`div`,ut,[a[13]||=c(`span`,{class:`flex items-center gap-2 text-foreground-muted`},[c(`span`,{class:`w-2 h-2 rounded-sm bg-info`}),p(` orva.db `)],-1),c(`span`,dt,_(K(B.value.db_bytes)),1)]),c(`div`,ft,[a[14]||=c(`span`,{class:`flex items-center gap-2 text-foreground-muted`},[c(`span`,{class:`w-2 h-2 rounded-sm bg-success`}),p(` functions/ `)],-1),c(`span`,pt,_(K(B.value.functions_bytes)),1)]),B.value.wal_bytes>0?(e(),l(`div`,mt,[a[15]||=c(`span`,{class:`flex items-center gap-2 text-foreground-muted`},[c(`span`,{class:`w-2 h-2 rounded-sm bg-warning`}),p(` orva.db-wal `)],-1),c(`span`,ht,_(K(B.value.wal_bytes)),1)])):g(``,!0),c(`div`,gt,[a[16]||=c(`span`,{class:`text-foreground-muted`},`total`,-1),c(`span`,_t,_(K(B.value.total_bytes)),1)])]),Ae.value>0?(e(),l(`div`,vt,_(K(Ae.value))+` reclaimable (`+_(B.value.db_free_pages)+` free SQLite pages) `,1)):g(``,!0)])):g(``,!0),W.value?(e(),l(`div`,yt,[p(` Compacted in `+_(W.value.duration_ms)+` ms and freed `,1),c(`span`,bt,_(K(W.value.freed_bytes)),1),p(` (`+_(K(W.value.before_bytes))+` → `+_(K(W.value.after_bytes))+`). `,1)])):g(``,!0),U.value?(e(),l(`div`,xt,[a[17]||=c(`div`,{class:`font-semibold mb-1`},` Compact failed `,-1),c(`div`,St,_(U.value),1)])):g(``,!0)]),c(`section`,Ct,[c(`div`,null,[c(`h2`,wt,[d(o(y),{class:`w-4 h-4 text-foreground-muted`}),a[18]||=p(` Account `,-1)]),a[19]||=c(`p`,{class:`text-xs text-foreground-muted mt-1`},` Update your password or end your session. `,-1)]),c(`form`,{class:`space-y-3 pt-2`,onSubmit:C(Ee,[`prevent`])},[a[24]||=c(`h3`,{class:`text-sm font-medium text-foreground`},` Change password `,-1),c(`div`,Tt,[c(`div`,Et,[a[20]||=c(`label`,{for:`settings-current-password`,class:`text-xs text-foreground-muted`},`Current password`,-1),r(c(`input`,{id:`settings-current-password`,"onUpdate:modelValue":a[0]||=e=>M.value.current=e,type:`password`,autocomplete:`current-password`,"aria-describedby":`pw-error`,class:`bg-surface border border-border rounded-md px-3 py-2 text-sm text-white placeholder:text-foreground-muted focus:outline-none focus:ring-1 focus:ring-primary`,placeholder:`••••••••`},null,512),[[E,M.value.current]])]),c(`div`,Dt,[a[21]||=c(`label`,{for:`settings-new-password`,class:`text-xs text-foreground-muted`},`New password`,-1),r(c(`input`,{id:`settings-new-password`,"onUpdate:modelValue":a[1]||=e=>M.value.next=e,type:`password`,autocomplete:`new-password`,"aria-describedby":`pw-error`,class:`bg-surface border border-border rounded-md px-3 py-2 text-sm text-white placeholder:text-foreground-muted focus:outline-none focus:ring-1 focus:ring-primary`,placeholder:`••••••••`},null,512),[[E,M.value.next]])]),c(`div`,Ot,[a[22]||=c(`label`,{for:`settings-confirm-password`,class:`text-xs text-foreground-muted`},`Confirm new password`,-1),r(c(`input`,{id:`settings-confirm-password`,"onUpdate:modelValue":a[2]||=e=>M.value.confirm=e,type:`password`,autocomplete:`new-password`,"aria-describedby":`pw-error`,class:`bg-surface border border-border rounded-md px-3 py-2 text-sm text-white placeholder:text-foreground-muted focus:outline-none focus:ring-1 focus:ring-primary`,placeholder:`••••••••`},null,512),[[E,M.value.confirm]])])]),P.value?(e(),l(`div`,kt,_(P.value),1)):g(``,!0),F.value?(e(),l(`div`,At,` Password updated successfully. `)):g(``,!0),d(D,{type:`submit`,variant:`primary`,loading:N.value,disabled:N.value},{default:t(()=>[d(o(y),{class:`w-4 h-4`}),a[23]||=p(` Update password `,-1)]),_:1},8,[`loading`,`disabled`])],32),c(`div`,jt,[d(D,{variant:`danger`,onClick:De},{default:t(()=>[d(o(Se),{class:`w-4 h-4`}),a[25]||=p(` Log out `,-1)]),_:1})])]),c(`section`,Mt,[c(`div`,Nt,[c(`div`,null,[c(`h2`,Pt,[d(o(w),{class:`w-4 h-4 text-foreground-muted`}),a[26]||=p(` Connected applications `,-1)]),c(`p`,Ft,[a[28]||=p(` OAuth clients with access to this instance. Add connectors from `,-1),d(o(fe),{to:`/docs#mcp`,class:`text-primary hover:underline`},{default:t(()=>[...a[27]||=[p(` Docs `,-1)]]),_:1})])]),q.value.length>0?(e(),l(`span`,It,_(q.value.length)+` active `,1)):g(``,!0)]),Y.value?(e(),l(`div`,Lt,[a[29]||=c(`div`,{class:`font-semibold mb-1`},` Failed to load connected apps `,-1),c(`div`,Rt,_(Y.value),1)])):J.value?(e(),l(`div`,zt,` Loading… `)):q.value.length===0?(e(),l(`div`,Bt,[...a[30]||=[c(`p`,{class:`text-xs text-foreground-muted`},` No connected applications. `,-1)]])):(e(),l(`ul`,Vt,[(e(!0),l(m,null,f(q.value,t=>(e(),l(`li`,{key:t.id,class:`px-5 py-3 flex items-start gap-3`},[(e(),ee(i(o(Re)(t.client_name).icon),{class:u([`w-5 h-5 mt-0.5 shrink-0`,o(Re)(t.client_name).accent])},null,8,[`class`])),c(`div`,Ht,[c(`div`,Ut,_(t.client_name),1),c(`div`,Wt,[c(`span`,null,`Authorized `+_(o(A)(t.issued_at)),1),t.last_used_at?(e(),l(`span`,Gt,` · Last used `+_(o(A)(t.last_used_at)),1)):(e(),l(`span`,Kt,`· Never used`)),t.refresh_expires_at?(e(),l(`span`,qt,` · Re-consent `+_(o(A)(t.refresh_expires_at)),1)):g(``,!0)]),c(`div`,Jt,[(e(!0),l(m,null,f(Ie(t.scope),t=>(e(),l(`span`,{key:t,class:u([`text-xs px-1.5 py-0.5 rounded font-mono`,Le(t)])},_(t),3))),128))])]),c(`button`,{type:`button`,class:`text-xs text-foreground-muted hover:text-danger-fg transition-colors flex items-center gap-1 shrink-0 self-center`,disabled:X.value===t.id,onClick:e=>Fe(t)},[d(o(x),{class:`w-3.5 h-3.5`}),a[31]||=p(` Revoke `,-1)],8,Yt)]))),128))]))]),c(`section`,Xt,[c(`div`,Zt,[c(`div`,null,[c(`h2`,Qt,[d(o(Ce),{class:`w-4 h-4 text-foreground-muted`}),a[32]||=p(` Active sessions `,-1)]),a[33]||=c(`p`,{class:`text-xs text-foreground-muted mt-1 max-w-prose`},` Browsers signed in to this instance. `,-1)]),Z.value.length>0?(e(),l(`span`,$t,_(Z.value.length)+` active `,1)):g(``,!0)]),Q.value?(e(),l(`div`,en,[a[34]||=c(`div`,{class:`font-semibold mb-1`},` Failed to load sessions `,-1),c(`div`,tn,_(Q.value),1)])):(e(),l(`ul`,nn,[(e(!0),l(m,null,f(Z.value,t=>(e(),l(`li`,{key:t.prefix,class:`px-5 py-3 flex items-start gap-3`},[d(o(Ce),{class:u([`w-5 h-5 mt-0.5 shrink-0`,t.current?`text-success-fg`:`text-foreground-muted`])},null,8,[`class`]),c(`div`,rn,[c(`div`,an,[t.current?(e(),l(`span`,on,`This session`)):(e(),l(`span`,sn,_(bn(t.prefix)),1)),t.current?(e(),l(`span`,cn,` current `)):g(``,!0)]),c(`div`,ln,` Signed in `+_(o(A)(t.created_at))+` · expires `+_(o(A)(t.expires_at)),1)]),t.current?g(``,!0):(e(),l(`button`,{key:0,type:`button`,class:`text-xs text-foreground-muted hover:text-danger-fg transition-colors flex items-center gap-1 shrink-0 self-center`,disabled:$.value===t.prefix,onClick:e=>yn(t)},[d(o(x),{class:`w-3.5 h-3.5`}),a[35]||=p(` Revoke `,-1)],8,un))]))),128))]))]),c(`section`,dn,[c(`div`,fn,[c(`div`,null,[c(`h2`,pn,[d(o(ye),{class:`w-4 h-4 text-foreground-muted`}),a[36]||=p(` Backup & Restore `,-1)]),a[37]||=c(`p`,{class:`text-xs text-foreground-muted mt-1 max-w-prose`},` Download or restore a complete instance snapshot. `,-1),a[38]||=c(`p`,{class:`text-xs text-warning-fg mt-2 max-w-prose`},` Backups contain secret keys. Store them securely. `,-1)])]),c(`div`,mn,[d(D,{variant:`primary`,onClick:xn},{default:t(()=>[d(o(ne),{class:`w-4 h-4`}),a[39]||=p(` Download backup `,-1)]),_:1}),d(D,{variant:`secondary`,loading:L.value,onClick:Sn},{default:t(()=>[d(o(we),{class:`w-4 h-4`}),a[40]||=p(` Restore from backup `,-1)]),_:1},8,[`loading`]),c(`input`,{ref_key:`fileInput`,ref:I,type:`file`,accept:`.tar.gz,.tgz,application/gzip`,class:`hidden`,onChange:Cn},null,544)]),R.value?(e(),l(`div`,hn,[a[41]||=c(`div`,{class:`font-semibold mb-1`},` Restore failed `,-1),c(`div`,gn,_(R.value),1)])):g(``,!0),z.value?(e(),l(`div`,_n,[a[42]||=p(` Restore complete. The server is restarting to load the new data. Reload in a few seconds. `,-1),c(`button`,{class:`underline ml-1`,onClick:wn},` Reload now `)])):g(``,!0)])]))}});export{$ as default}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/StatusBadge-BOCx3ovI.js b/backend/internal/server/ui_dist/assets/StatusBadge-Baoe7YAb.js similarity index 86% rename from backend/internal/server/ui_dist/assets/StatusBadge-BOCx3ovI.js rename to backend/internal/server/ui_dist/assets/StatusBadge-Baoe7YAb.js index 9dcc79c8..15fc5535 100644 --- a/backend/internal/server/ui_dist/assets/StatusBadge-BOCx3ovI.js +++ b/backend/internal/server/ui_dist/assets/StatusBadge-Baoe7YAb.js @@ -1 +1 @@ -import{D as e,M as t,d as n,gt as r,l as i,m as a,s as o,vt as s}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{n as c,t as l}from"./clock-Dl3hMdU8.js";import{n as u,t as d}from"./circle-bxz1-xUq.js";var f={__name:`StatusBadge`,props:{status:{type:String,required:!0}},setup(f){let p=f,m=o(()=>{switch(p.status){case`succeeded`:case`success`:case`active`:return{classes:`text-success-fg border-success-ring`,icon:c};case`failed`:case`error`:case`crashed`:return{classes:`text-danger-fg border-danger-ring`,icon:u};case`queued`:case`building`:case`pending`:case`timeout`:return{classes:`text-warning-fg border-warning-ring`,icon:l};default:return{classes:`text-foreground-muted border-border`,icon:d}}});return(o,c)=>(e(),n(`span`,{class:r([`inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs border bg-background font-mono`,m.value.classes])},[(e(),i(t(m.value.icon),{class:`h-3 w-3 shrink-0`,"aria-hidden":`true`})),a(` `+s(f.status),1)],2))}};export{f as t}; \ No newline at end of file +import{D as e,M as t,d as n,gt as r,l as i,m as a,s as o,vt as s}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{n as c,t as l}from"./clock-CiH8bQVI.js";import{n as u,t as d}from"./circle-NOse79gh.js";var f={__name:`StatusBadge`,props:{status:{type:String,required:!0}},setup(f){let p=f,m=o(()=>{switch(p.status){case`succeeded`:case`success`:case`active`:return{classes:`text-success-fg border-success-ring`,icon:c};case`failed`:case`error`:case`crashed`:return{classes:`text-danger-fg border-danger-ring`,icon:u};case`queued`:case`building`:case`pending`:case`timeout`:return{classes:`text-warning-fg border-warning-ring`,icon:l};default:return{classes:`text-foreground-muted border-border`,icon:d}}});return(o,c)=>(e(),n(`span`,{class:r([`inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs border bg-background font-mono`,m.value.classes])},[(e(),i(t(m.value.icon),{class:`h-3 w-3 shrink-0`,"aria-hidden":`true`})),a(` `+s(f.status),1)],2))}};export{f as t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/TraceDetail-c4yTBunf.js b/backend/internal/server/ui_dist/assets/TraceDetail-wVyyYiTv.js similarity index 97% rename from backend/internal/server/ui_dist/assets/TraceDetail-c4yTBunf.js rename to backend/internal/server/ui_dist/assets/TraceDetail-wVyyYiTv.js index a814d0ff..e83eafdc 100644 --- a/backend/internal/server/ui_dist/assets/TraceDetail-c4yTBunf.js +++ b/backend/internal/server/ui_dist/assets/TraceDetail-wVyyYiTv.js @@ -1 +1 @@ -import{D as e,G as t,T as n,Z as r,_t as i,c as a,d as o,gt as s,h as c,k as l,l as u,m as d,r as f,s as ee,u as p,vt as m}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as te}from"./arrow-left-CEJWChyG.js";import{t as ne}from"./circle-alert-BIdKIOKw.js";import{t as re}from"./copy-Bt6Rv0BQ.js";import{t as h}from"./flag-BU6shp0V.js";import{O as ie,mt as ae,pt as oe}from"./index-C11HmDXN.js";import{t as g}from"./StatusBadge-BOCx3ovI.js";var se={class:`space-y-6`},_={class:`flex items-start justify-between gap-4`},v={key:0,class:`rounded-md border border-danger-ring bg-danger-tint p-3 text-xs text-danger-fg`},y={key:1,class:`text-xs text-foreground-muted italic`},b={class:`bg-background border border-border rounded-lg p-5 space-y-4`},x={class:`flex items-center gap-3 flex-wrap text-xs`},S={class:`bg-surface text-white px-2 py-0.5 rounded font-mono`},C={class:`grid grid-cols-2 sm:grid-cols-4 gap-4 pt-3 border-t border-border text-xs`},w={class:`inline-flex items-center px-2 py-0.5 rounded text-xs border bg-background font-mono text-foreground-muted border-border lowercase`},T={class:`text-white font-mono`},E={class:`text-white`},D={class:`flex items-center gap-2`},O={key:0,class:`inline-flex items-center gap-1 text-[10px] uppercase tracking-wide text-warning-fg`},k={class:`bg-background border border-border rounded-lg p-5`},A={class:`space-y-1.5`},j=[`onClick`],M={class:`col-span-3 truncate flex items-center gap-1.5`},N={class:`text-white`},P={class:`inline-flex items-center px-1.5 py-0.5 rounded text-[10px] border bg-background font-mono text-foreground-muted border-border lowercase`},F={class:`col-span-7 relative h-4`},I=[`title`],L={class:`col-span-2 text-right font-mono`},R={class:`text-white`},z={key:0,class:`block text-[10px] text-foreground-muted`},B={class:`col-span-3 truncate flex items-center gap-1.5 pl-5`},V={class:`text-foreground-muted`},H={key:0,class:`inline-flex items-center px-1.5 py-0.5 rounded text-[10px] bg-danger-tint text-danger-fg border border-danger-ring`},U={class:`col-span-7 relative h-3`},W=[`title`],G={class:`col-span-2 text-right font-mono text-foreground-muted`},K={key:0,class:`bg-background border border-border rounded-lg p-5`},ce={class:`text-xs text-foreground-muted uppercase tracking-wide mb-3`},le={class:`space-y-1 font-mono text-xs`},ue={class:`text-foreground-muted text-[10px] tabular-nums`},de={class:`text-white truncate`},fe={key:0,class:`text-[10px] text-foreground-muted truncate`},pe={class:`bg-background border border-border rounded-lg overflow-x-auto`},me={class:`w-full text-sm text-left`},he={class:`divide-y divide-border`},ge=[`onClick`],_e={class:`px-4 py-2.5 font-mono text-xs text-foreground-muted`},ve={class:`px-4 py-2.5 text-white`},ye={class:`px-4 py-2.5 hidden md:table-cell`},be={class:`inline-flex items-center px-2 py-0.5 rounded text-xs border bg-background font-mono text-foreground-muted border-border lowercase`},xe={class:`px-4 py-2.5 text-right font-mono text-xs text-foreground-muted`},Se={class:`px-4 py-2.5 text-right font-mono text-xs text-white`},Ce={class:`px-4 py-2.5`},we={__name:`TraceDetail`,setup(we){let Te=oe(),q=ae(),J=t(null),Y=t(!1),X=t(``),Ee=async()=>{Y.value=!0,X.value=``;try{let e=await ie(Te.params.id);J.value=e.data}catch(e){e?.response?.status===404?X.value=`No spans found for that trace.`:X.value=e?.response?.data?.error?.message||e?.message||`failed to load trace`}finally{Y.value=!1}},Z=ee(()=>Math.max(1,J.value?.total_duration_ms||1)),De=e=>{let t=e.offset_ms/Z.value*100,n=Math.max(.5,e.duration_ms/Z.value*100);return{left:`${t}%`,width:`${n}%`}},Oe=e=>e.status===`error`?`bg-danger/70`:e.is_outlier?`bg-warning/80`:`bg-primary/80`,Q=e=>!J.value?.user_spans||!e?.span_id?[]:J.value.user_spans.filter(t=>t.parent_span_id===e.span_id).sort((e,t)=>(e.offset_ms||0)-(t.offset_ms||0)),ke=e=>{let t=(e.offset_ms||0)/Z.value*100,n=Math.max(.5,(e.duration_ms||0)/Z.value*100);return{left:`${t}%`,width:`${n}%`}},Ae=e=>{switch(e){case`error`:return`text-danger-fg`;case`warn`:return`text-warning-fg`;case`debug`:return`text-foreground-muted`;default:return`text-primary-light`}},je=e=>e.level===`error`?`bg-danger-tint`:``,Me=e=>{if(!e)return``;try{let t=new Date(e);return t.toLocaleTimeString(void 0,{hour12:!1})+`.`+String(t.getMilliseconds()).padStart(3,`0`)}catch{return e}},$=e=>{e.execution_id&&q.push({path:`/invocations`,query:{exec:e.execution_id}})},Ne=async()=>{if(J.value?.trace_id)try{await navigator.clipboard.writeText(J.value.trace_id)}catch{}};return n(Ee),(t,n)=>(e(),o(`div`,se,[a(`div`,_,[a(`div`,null,[a(`button`,{class:`inline-flex items-center gap-1 text-xs text-foreground-muted hover:text-white mb-1 transition-colors`,onClick:n[0]||=e=>r(q).push(`/traces`)},[c(r(te),{class:`w-3.5 h-3.5`}),n[1]||=d(` All traces `,-1)]),n[2]||=a(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` Trace `,-1),n[3]||=a(`p`,{class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},` Spans in this invocation chain. `,-1)])]),X.value?(e(),o(`div`,v,m(X.value),1)):Y.value&&!J.value?(e(),o(`div`,y,` Loading trace… `)):J.value?(e(),o(f,{key:2},[a(`div`,b,[a(`div`,x,[n[4]||=a(`span`,{class:`text-foreground-muted uppercase tracking-wide`},` trace id `,-1),a(`code`,S,m(J.value.trace_id),1),a(`button`,{class:`p-1 rounded hover:bg-surface text-foreground-muted hover:text-white transition-colors`,title:`Copy trace id`,"aria-label":`Copy trace ID`,onClick:Ne},[c(r(re),{class:`w-3.5 h-3.5`})])]),a(`div`,C,[a(`div`,null,[n[5]||=a(`div`,{class:`text-foreground-muted uppercase tracking-wide mb-1`},` Trigger `,-1),a(`span`,w,m(J.value.trigger||r(`—`)),1)]),a(`div`,null,[n[6]||=a(`div`,{class:`text-foreground-muted uppercase tracking-wide mb-1`},` Total duration `,-1),a(`div`,T,m(J.value.total_duration_ms)+`ms `,1)]),a(`div`,null,[n[7]||=a(`div`,{class:`text-foreground-muted uppercase tracking-wide mb-1`},` Spans `,-1),a(`div`,E,m(J.value.span_count),1)]),a(`div`,null,[n[9]||=a(`div`,{class:`text-foreground-muted uppercase tracking-wide mb-1`},` Status `,-1),a(`div`,D,[c(g,{status:J.value.status},null,8,[`status`]),J.value.has_outlier?(e(),o(`span`,O,[c(r(h),{class:`w-3 h-3`}),n[8]||=d(` Outlier `,-1)])):p(``,!0)])])])]),a(`div`,k,[n[11]||=a(`div`,{class:`text-xs text-foreground-muted uppercase tracking-wide mb-4`},` Waterfall `,-1),a(`div`,A,[(e(!0),o(f,null,l(J.value.spans,(t,c)=>(e(),o(f,{key:t.span_id||`s${c}`},[a(`div`,{class:`grid grid-cols-12 gap-2 items-center text-xs hover:bg-surface/40 px-2 py-1.5 rounded cursor-pointer transition-colors`,onClick:e=>$(t)},[a(`div`,M,[a(`span`,N,m(t.function_name||t.function_id),1),a(`span`,P,m(t.trigger||r(`—`)),1),t.status===`error`?(e(),u(r(ne),{key:0,class:`w-3 h-3 shrink-0 text-danger-fg`,"aria-label":`error`})):p(``,!0),t.is_outlier?(e(),u(r(h),{key:1,class:`w-3 h-3 shrink-0 text-warning-fg`,"aria-label":`outlier`})):p(``,!0)]),a(`div`,F,[a(`div`,{class:s([`absolute h-2 top-1 rounded-sm`,Oe(t)]),style:i(De(t)),title:`+${t.offset_ms}ms · ${t.duration_ms}ms`},null,14,I)]),a(`div`,L,[a(`span`,R,m(t.duration_ms)+`ms`,1),t.baseline_p95_ms?(e(),o(`span`,z,` p95 `+m(t.baseline_p95_ms)+`ms `,1)):p(``,!0)])],8,j),(e(!0),o(f,null,l(Q(t),t=>(e(),o(`div`,{key:`us-${t.id}`,class:`grid grid-cols-12 gap-2 items-center text-xs hover:bg-surface/40 px-2 py-1 rounded transition-colors`},[a(`div`,B,[n[10]||=a(`span`,{class:`text-foreground-muted text-[10px]`},`└`,-1),a(`span`,V,m(t.name),1),t.status===`error`?(e(),o(`span`,H,` error `)):p(``,!0)]),a(`div`,U,[a(`div`,{class:`absolute h-1.5 top-1 rounded-sm bg-accent-muted/70`,style:i(ke(t)),title:`+${t.offset_ms}ms · ${t.duration_ms}ms`},null,12,W)]),a(`div`,G,m(t.duration_ms)+`ms `,1)]))),128))],64))),128))])]),J.value.log_entries&&J.value.log_entries.length?(e(),o(`div`,K,[a(`div`,ce,` Logs (`+m(J.value.log_entries.length)+`) `,1),a(`div`,le,[(e(!0),o(f,null,l(J.value.log_entries,t=>(e(),o(`div`,{key:`log-${t.id}`,class:s([`flex items-baseline gap-2 px-2 py-1 rounded hover:bg-surface/40 transition-colors`,je(t)])},[a(`span`,ue,m(Me(t.ts)),1),a(`span`,{class:s([`text-[10px] uppercase tracking-wide`,Ae(t.level)])},m(t.level),3),a(`span`,de,m(t.message),1),t.fields?(e(),o(`code`,fe,m(t.fields),1)):p(``,!0)],2))),128))])])):p(``,!0),a(`div`,pe,[a(`table`,me,[n[12]||=a(`thead`,{class:`text-xs text-foreground-muted uppercase bg-surface border-b border-border`},[a(`tr`,null,[a(`th`,{class:`px-4 py-3 w-32`},` Span `),a(`th`,{class:`px-4 py-3`},` Function `),a(`th`,{class:`px-4 py-3 w-28 hidden md:table-cell`},` Trigger `),a(`th`,{class:`px-4 py-3 w-24 text-right`},` Offset `),a(`th`,{class:`px-4 py-3 w-24 text-right`},` Duration `),a(`th`,{class:`px-4 py-3 w-24`},` Status `)])],-1),a(`tbody`,he,[(e(!0),o(f,null,l(J.value.spans,t=>(e(),o(`tr`,{key:`tbl-${t.span_id}`,class:`hover:bg-surface/40 cursor-pointer transition-colors`,onClick:e=>$(t)},[a(`td`,_e,m(t.span_id?.slice(0,11)||r(`—`)),1),a(`td`,ve,m(t.function_name||t.function_id),1),a(`td`,ye,[a(`span`,be,m(t.trigger||r(`—`)),1)]),a(`td`,xe,` +`+m(t.offset_ms)+`ms `,1),a(`td`,Se,m(t.duration_ms)+`ms `,1),a(`td`,Ce,[c(g,{status:t.status},null,8,[`status`])])],8,ge))),128))])])])],64)):p(``,!0)]))}};export{we as default}; \ No newline at end of file +import{D as e,G as t,T as n,Z as r,_t as i,c as a,d as o,gt as s,h as c,k as l,l as u,m as d,r as f,s as ee,u as p,vt as m}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as te}from"./arrow-left-Ba3OArNt.js";import{t as ne}from"./circle-alert-BnUYYhGE.js";import{t as re}from"./copy-BqdwwcxC.js";import{t as h}from"./flag-CmjNKmxi.js";import{O as ie,mt as ae,pt as oe}from"./index-DTqMKlE1.js";import{t as g}from"./StatusBadge-Baoe7YAb.js";var se={class:`space-y-6`},_={class:`flex items-start justify-between gap-4`},v={key:0,class:`rounded-md border border-danger-ring bg-danger-tint p-3 text-xs text-danger-fg`},y={key:1,class:`text-xs text-foreground-muted italic`},b={class:`bg-background border border-border rounded-lg p-5 space-y-4`},x={class:`flex items-center gap-3 flex-wrap text-xs`},S={class:`bg-surface text-white px-2 py-0.5 rounded font-mono`},C={class:`grid grid-cols-2 sm:grid-cols-4 gap-4 pt-3 border-t border-border text-xs`},w={class:`inline-flex items-center px-2 py-0.5 rounded text-xs border bg-background font-mono text-foreground-muted border-border lowercase`},T={class:`text-white font-mono`},E={class:`text-white`},D={class:`flex items-center gap-2`},O={key:0,class:`inline-flex items-center gap-1 text-[10px] uppercase tracking-wide text-warning-fg`},k={class:`bg-background border border-border rounded-lg p-5`},A={class:`space-y-1.5`},j=[`onClick`],M={class:`col-span-3 truncate flex items-center gap-1.5`},N={class:`text-white`},P={class:`inline-flex items-center px-1.5 py-0.5 rounded text-[10px] border bg-background font-mono text-foreground-muted border-border lowercase`},F={class:`col-span-7 relative h-4`},I=[`title`],L={class:`col-span-2 text-right font-mono`},R={class:`text-white`},z={key:0,class:`block text-[10px] text-foreground-muted`},B={class:`col-span-3 truncate flex items-center gap-1.5 pl-5`},V={class:`text-foreground-muted`},H={key:0,class:`inline-flex items-center px-1.5 py-0.5 rounded text-[10px] bg-danger-tint text-danger-fg border border-danger-ring`},U={class:`col-span-7 relative h-3`},W=[`title`],G={class:`col-span-2 text-right font-mono text-foreground-muted`},K={key:0,class:`bg-background border border-border rounded-lg p-5`},ce={class:`text-xs text-foreground-muted uppercase tracking-wide mb-3`},le={class:`space-y-1 font-mono text-xs`},ue={class:`text-foreground-muted text-[10px] tabular-nums`},de={class:`text-white truncate`},fe={key:0,class:`text-[10px] text-foreground-muted truncate`},pe={class:`bg-background border border-border rounded-lg overflow-x-auto`},me={class:`w-full text-sm text-left`},he={class:`divide-y divide-border`},ge=[`onClick`],_e={class:`px-4 py-2.5 font-mono text-xs text-foreground-muted`},ve={class:`px-4 py-2.5 text-white`},ye={class:`px-4 py-2.5 hidden md:table-cell`},be={class:`inline-flex items-center px-2 py-0.5 rounded text-xs border bg-background font-mono text-foreground-muted border-border lowercase`},xe={class:`px-4 py-2.5 text-right font-mono text-xs text-foreground-muted`},Se={class:`px-4 py-2.5 text-right font-mono text-xs text-white`},Ce={class:`px-4 py-2.5`},we={__name:`TraceDetail`,setup(we){let Te=oe(),q=ae(),J=t(null),Y=t(!1),X=t(``),Ee=async()=>{Y.value=!0,X.value=``;try{let e=await ie(Te.params.id);J.value=e.data}catch(e){e?.response?.status===404?X.value=`No spans found for that trace.`:X.value=e?.response?.data?.error?.message||e?.message||`failed to load trace`}finally{Y.value=!1}},Z=ee(()=>Math.max(1,J.value?.total_duration_ms||1)),De=e=>{let t=e.offset_ms/Z.value*100,n=Math.max(.5,e.duration_ms/Z.value*100);return{left:`${t}%`,width:`${n}%`}},Oe=e=>e.status===`error`?`bg-danger/70`:e.is_outlier?`bg-warning/80`:`bg-primary/80`,Q=e=>!J.value?.user_spans||!e?.span_id?[]:J.value.user_spans.filter(t=>t.parent_span_id===e.span_id).sort((e,t)=>(e.offset_ms||0)-(t.offset_ms||0)),ke=e=>{let t=(e.offset_ms||0)/Z.value*100,n=Math.max(.5,(e.duration_ms||0)/Z.value*100);return{left:`${t}%`,width:`${n}%`}},Ae=e=>{switch(e){case`error`:return`text-danger-fg`;case`warn`:return`text-warning-fg`;case`debug`:return`text-foreground-muted`;default:return`text-primary-light`}},je=e=>e.level===`error`?`bg-danger-tint`:``,Me=e=>{if(!e)return``;try{let t=new Date(e);return t.toLocaleTimeString(void 0,{hour12:!1})+`.`+String(t.getMilliseconds()).padStart(3,`0`)}catch{return e}},$=e=>{e.execution_id&&q.push({path:`/invocations`,query:{exec:e.execution_id}})},Ne=async()=>{if(J.value?.trace_id)try{await navigator.clipboard.writeText(J.value.trace_id)}catch{}};return n(Ee),(t,n)=>(e(),o(`div`,se,[a(`div`,_,[a(`div`,null,[a(`button`,{class:`inline-flex items-center gap-1 text-xs text-foreground-muted hover:text-white mb-1 transition-colors`,onClick:n[0]||=e=>r(q).push(`/traces`)},[c(r(te),{class:`w-3.5 h-3.5`}),n[1]||=d(` All traces `,-1)]),n[2]||=a(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` Trace `,-1),n[3]||=a(`p`,{class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},` Spans in this invocation chain. `,-1)])]),X.value?(e(),o(`div`,v,m(X.value),1)):Y.value&&!J.value?(e(),o(`div`,y,` Loading trace… `)):J.value?(e(),o(f,{key:2},[a(`div`,b,[a(`div`,x,[n[4]||=a(`span`,{class:`text-foreground-muted uppercase tracking-wide`},` trace id `,-1),a(`code`,S,m(J.value.trace_id),1),a(`button`,{class:`p-1 rounded hover:bg-surface text-foreground-muted hover:text-white transition-colors`,title:`Copy trace id`,"aria-label":`Copy trace ID`,onClick:Ne},[c(r(re),{class:`w-3.5 h-3.5`})])]),a(`div`,C,[a(`div`,null,[n[5]||=a(`div`,{class:`text-foreground-muted uppercase tracking-wide mb-1`},` Trigger `,-1),a(`span`,w,m(J.value.trigger||r(`—`)),1)]),a(`div`,null,[n[6]||=a(`div`,{class:`text-foreground-muted uppercase tracking-wide mb-1`},` Total duration `,-1),a(`div`,T,m(J.value.total_duration_ms)+`ms `,1)]),a(`div`,null,[n[7]||=a(`div`,{class:`text-foreground-muted uppercase tracking-wide mb-1`},` Spans `,-1),a(`div`,E,m(J.value.span_count),1)]),a(`div`,null,[n[9]||=a(`div`,{class:`text-foreground-muted uppercase tracking-wide mb-1`},` Status `,-1),a(`div`,D,[c(g,{status:J.value.status},null,8,[`status`]),J.value.has_outlier?(e(),o(`span`,O,[c(r(h),{class:`w-3 h-3`}),n[8]||=d(` Outlier `,-1)])):p(``,!0)])])])]),a(`div`,k,[n[11]||=a(`div`,{class:`text-xs text-foreground-muted uppercase tracking-wide mb-4`},` Waterfall `,-1),a(`div`,A,[(e(!0),o(f,null,l(J.value.spans,(t,c)=>(e(),o(f,{key:t.span_id||`s${c}`},[a(`div`,{class:`grid grid-cols-12 gap-2 items-center text-xs hover:bg-surface/40 px-2 py-1.5 rounded cursor-pointer transition-colors`,onClick:e=>$(t)},[a(`div`,M,[a(`span`,N,m(t.function_name||t.function_id),1),a(`span`,P,m(t.trigger||r(`—`)),1),t.status===`error`?(e(),u(r(ne),{key:0,class:`w-3 h-3 shrink-0 text-danger-fg`,"aria-label":`error`})):p(``,!0),t.is_outlier?(e(),u(r(h),{key:1,class:`w-3 h-3 shrink-0 text-warning-fg`,"aria-label":`outlier`})):p(``,!0)]),a(`div`,F,[a(`div`,{class:s([`absolute h-2 top-1 rounded-sm`,Oe(t)]),style:i(De(t)),title:`+${t.offset_ms}ms · ${t.duration_ms}ms`},null,14,I)]),a(`div`,L,[a(`span`,R,m(t.duration_ms)+`ms`,1),t.baseline_p95_ms?(e(),o(`span`,z,` p95 `+m(t.baseline_p95_ms)+`ms `,1)):p(``,!0)])],8,j),(e(!0),o(f,null,l(Q(t),t=>(e(),o(`div`,{key:`us-${t.id}`,class:`grid grid-cols-12 gap-2 items-center text-xs hover:bg-surface/40 px-2 py-1 rounded transition-colors`},[a(`div`,B,[n[10]||=a(`span`,{class:`text-foreground-muted text-[10px]`},`└`,-1),a(`span`,V,m(t.name),1),t.status===`error`?(e(),o(`span`,H,` error `)):p(``,!0)]),a(`div`,U,[a(`div`,{class:`absolute h-1.5 top-1 rounded-sm bg-accent-muted/70`,style:i(ke(t)),title:`+${t.offset_ms}ms · ${t.duration_ms}ms`},null,12,W)]),a(`div`,G,m(t.duration_ms)+`ms `,1)]))),128))],64))),128))])]),J.value.log_entries&&J.value.log_entries.length?(e(),o(`div`,K,[a(`div`,ce,` Logs (`+m(J.value.log_entries.length)+`) `,1),a(`div`,le,[(e(!0),o(f,null,l(J.value.log_entries,t=>(e(),o(`div`,{key:`log-${t.id}`,class:s([`flex items-baseline gap-2 px-2 py-1 rounded hover:bg-surface/40 transition-colors`,je(t)])},[a(`span`,ue,m(Me(t.ts)),1),a(`span`,{class:s([`text-[10px] uppercase tracking-wide`,Ae(t.level)])},m(t.level),3),a(`span`,de,m(t.message),1),t.fields?(e(),o(`code`,fe,m(t.fields),1)):p(``,!0)],2))),128))])])):p(``,!0),a(`div`,pe,[a(`table`,me,[n[12]||=a(`thead`,{class:`text-xs text-foreground-muted uppercase bg-surface border-b border-border`},[a(`tr`,null,[a(`th`,{class:`px-4 py-3 w-32`},` Span `),a(`th`,{class:`px-4 py-3`},` Function `),a(`th`,{class:`px-4 py-3 w-28 hidden md:table-cell`},` Trigger `),a(`th`,{class:`px-4 py-3 w-24 text-right`},` Offset `),a(`th`,{class:`px-4 py-3 w-24 text-right`},` Duration `),a(`th`,{class:`px-4 py-3 w-24`},` Status `)])],-1),a(`tbody`,he,[(e(!0),o(f,null,l(J.value.spans,t=>(e(),o(`tr`,{key:`tbl-${t.span_id}`,class:`hover:bg-surface/40 cursor-pointer transition-colors`,onClick:e=>$(t)},[a(`td`,_e,m(t.span_id?.slice(0,11)||r(`—`)),1),a(`td`,ve,m(t.function_name||t.function_id),1),a(`td`,ye,[a(`span`,be,m(t.trigger||r(`—`)),1)]),a(`td`,xe,` +`+m(t.offset_ms)+`ms `,1),a(`td`,Se,m(t.duration_ms)+`ms `,1),a(`td`,Ce,[c(g,{status:t.status},null,8,[`status`])])],8,ge))),128))])])])],64)):p(``,!0)]))}};export{we as default}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/Traces-DKjSRtpZ.js b/backend/internal/server/ui_dist/assets/Traces-BEuuXgIG.js similarity index 95% rename from backend/internal/server/ui_dist/assets/Traces-DKjSRtpZ.js rename to backend/internal/server/ui_dist/assets/Traces-BEuuXgIG.js index d2cc2006..6f3e4f46 100644 --- a/backend/internal/server/ui_dist/assets/Traces-DKjSRtpZ.js +++ b/backend/internal/server/ui_dist/assets/Traces-BEuuXgIG.js @@ -1 +1 @@ -import{D as e,F as t,G as n,I as r,T as i,Z as a,c as o,d as s,h as c,k as l,l as u,m as d,r as f,u as p,vt as m}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as h}from"./flag-BU6shp0V.js";import{t as g}from"./refresh-cw-Bn_tp4gH.js";import{K as _,Lt as v,_t as y,mt as b,wt as x,xt as S,zt as C}from"./index-C11HmDXN.js";import{t as ee}from"./StatusBadge-BOCx3ovI.js";var te={class:`space-y-6`},w={class:`flex items-start justify-between gap-4`},T={class:`flex flex-col sm:flex-row sm:items-center gap-2 sm:flex-wrap`},E={class:`relative w-full sm:flex-1 sm:min-w-[260px] sm:max-w-[420px]`},D={class:`flex items-center gap-2 sm:flex-wrap overflow-x-auto sm:overflow-visible scrollable snap-x min-w-0`},O={key:0,class:`rounded-md border border-danger-ring bg-danger-tint p-3 text-xs text-danger-fg`},k={key:1,class:`bg-background border border-border rounded-lg p-8 text-center text-sm text-foreground-muted`},A={key:2,class:`bg-background border border-border rounded-lg overflow-x-auto`},j={class:`w-full text-sm text-left`},M={class:`divide-y divide-border`},N=[`onClick`],P={class:`px-4 py-2.5 font-mono text-xs text-foreground-muted whitespace-nowrap`},F={class:`px-4 py-2.5 font-mono text-xs text-foreground-muted`},I={class:`px-4 py-2.5 text-white`},L={class:`px-4 py-2.5 hidden md:table-cell`},R={class:`inline-flex items-center px-2 py-0.5 rounded text-xs border bg-background font-mono text-foreground-muted border-border lowercase`},z={class:`px-4 py-2.5 text-right font-mono text-xs text-foreground-muted hidden sm:table-cell`},ne={class:`px-4 py-2.5`},B={class:`px-4 py-2.5 text-right`},V={key:3,class:`flex justify-center`},H=Object.assign({name:`TracesView`},{__name:`Traces`,setup(H){let U=b(),W=n([]),G=n(!1),K=n(``),q=n(``),J=n(``),Y=n(``),X=n(!1),Z=[{value:``,label:`All`},{value:`success`,label:`Success`},{value:`error`,label:`Errors`}],Q=async({append:e=!1}={})=>{G.value=!0,K.value=``;try{let t={limit:50};J.value&&(t.function_id=J.value),Y.value&&(t.status=Y.value),X.value&&(t.outlier_only=`1`),e&&q.value&&(t.before=q.value);let n=await _(t),r=n.data?.traces||[];e?W.value.push(...r):W.value=r,q.value=n.data?.next_cursor||``}catch(e){K.value=e?.response?.data?.error?.message||e?.message||`failed to load traces`}finally{G.value=!1}},$=()=>{q.value=``,Q()},re=()=>Q({append:!0}),ie=e=>{Y.value=e,$()},ae=()=>{X.value=!X.value,$()},oe=e=>U.push(`/traces/${e}`),se=e=>e?new Date(e).toLocaleTimeString(void 0,{hour12:!1}):`—`;return i($),(n,i)=>(e(),s(`div`,te,[o(`div`,w,[i[2]||=o(`div`,null,[o(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` Traces `),o(`p`,{class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},` Invocation chains across functions and triggers. `)],-1),c(y,{variant:`secondary`,size:`sm`,loading:G.value,onClick:$},{default:t(()=>[c(a(g),{class:`w-3.5 h-3.5`}),i[1]||=d(` Refresh `,-1)]),_:1},8,[`loading`])]),o(`div`,T,[o(`div`,E,[c(a(S),{class:`w-3.5 h-3.5 absolute left-2.5 top-1/2 -translate-y-1/2 text-foreground-muted/60 pointer-events-none`}),r(o(`input`,{"onUpdate:modelValue":i[0]||=e=>J.value=e,placeholder:`Filter by function id or name…`,class:`w-full bg-background border border-border rounded-md pl-8 pr-3 py-1.5 text-base sm:text-xs text-foreground placeholder-foreground-muted/60 focus:outline-none focus:border-white`,onKeydown:C($,[`enter`])},null,544),[[v,J.value]])]),o(`div`,D,[(e(),s(f,null,l(Z,e=>c(y,{key:e.value,variant:`chip`,size:`xs`,active:Y.value===e.value,class:`shrink-0 snap-start`,onClick:t=>ie(e.value)},{default:t(()=>[d(m(e.label),1)]),_:2},1032,[`active`,`onClick`])),64)),i[4]||=o(`span`,{class:`text-foreground-muted/40 shrink-0`},`·`,-1),c(y,{variant:`chip`,size:`xs`,active:X.value,class:`shrink-0 snap-start`,onClick:ae},{default:t(()=>[c(a(h),{class:`w-3 h-3`}),i[3]||=d(` Outliers only `,-1)]),_:1},8,[`active`])])]),K.value?(e(),s(`div`,O,m(K.value),1)):!W.value.length&&!G.value?(e(),s(`div`,k,[c(a(x),{class:`w-6 h-6 mx-auto mb-3 text-foreground-muted/50`}),i[5]||=o(`p`,null,`No traces yet.`,-1),i[6]||=o(`p`,{class:`text-xs mt-1 text-foreground-muted/60`},` HTTP and scheduled invocations appear here. `,-1)])):(e(),s(`div`,A,[o(`table`,j,[i[7]||=o(`thead`,{class:`text-xs text-foreground-muted uppercase bg-surface border-b border-border`},[o(`tr`,null,[o(`th`,{class:`px-4 py-3 w-32`},` Time `),o(`th`,{class:`px-4 py-3 w-40`},` Trace `),o(`th`,{class:`px-4 py-3`},` Root function `),o(`th`,{class:`px-4 py-3 w-28 hidden md:table-cell`},` Trigger `),o(`th`,{class:`px-4 py-3 w-24 text-right hidden sm:table-cell`},` Duration `),o(`th`,{class:`px-4 py-3 w-24`},` Status `),o(`th`,{class:`px-4 py-3 w-10`})])],-1),o(`tbody`,M,[(e(!0),s(f,null,l(W.value,t=>(e(),s(`tr`,{key:t.trace_id,class:`hover:bg-surface/40 cursor-pointer transition-colors`,onClick:e=>oe(t.trace_id)},[o(`td`,P,m(se(t.started_at)),1),o(`td`,F,m(t.trace_id.slice(0,11)),1),o(`td`,I,m(t.function_name||t.root_function_id),1),o(`td`,L,[o(`span`,R,m(t.trigger||a(`—`)),1)]),o(`td`,z,m(t.duration_ms==null?a(`—`):`${t.duration_ms}ms`),1),o(`td`,ne,[c(ee,{status:t.status},null,8,[`status`])]),o(`td`,B,[t.is_outlier?(e(),u(a(h),{key:0,class:`w-3.5 h-3.5 text-amber-400 inline`,title:`Latency outlier vs P95 baseline`})):p(``,!0)])],8,N))),128))])])])),q.value?(e(),s(`div`,V,[c(y,{variant:`ghost`,size:`sm`,onClick:re},{default:t(()=>[...i[8]||=[d(` Load more `,-1)]]),_:1})])):p(``,!0)]))}});export{H as default}; \ No newline at end of file +import{D as e,F as t,G as n,I as r,T as i,Z as a,c as o,d as s,h as c,k as l,l as u,m as d,r as f,u as p,vt as m}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as h}from"./flag-CmjNKmxi.js";import{t as g}from"./refresh-cw-Cn8qkf-v.js";import{K as _,Lt as v,_t as y,mt as b,wt as x,xt as S,zt as C}from"./index-DTqMKlE1.js";import{t as ee}from"./StatusBadge-Baoe7YAb.js";var te={class:`space-y-6`},w={class:`flex items-start justify-between gap-4`},T={class:`flex flex-col sm:flex-row sm:items-center gap-2 sm:flex-wrap`},E={class:`relative w-full sm:flex-1 sm:min-w-[260px] sm:max-w-[420px]`},D={class:`flex items-center gap-2 sm:flex-wrap overflow-x-auto sm:overflow-visible scrollable snap-x min-w-0`},O={key:0,class:`rounded-md border border-danger-ring bg-danger-tint p-3 text-xs text-danger-fg`},k={key:1,class:`bg-background border border-border rounded-lg p-8 text-center text-sm text-foreground-muted`},A={key:2,class:`bg-background border border-border rounded-lg overflow-x-auto`},j={class:`w-full text-sm text-left`},M={class:`divide-y divide-border`},N=[`onClick`],P={class:`px-4 py-2.5 font-mono text-xs text-foreground-muted whitespace-nowrap`},F={class:`px-4 py-2.5 font-mono text-xs text-foreground-muted`},I={class:`px-4 py-2.5 text-white`},L={class:`px-4 py-2.5 hidden md:table-cell`},R={class:`inline-flex items-center px-2 py-0.5 rounded text-xs border bg-background font-mono text-foreground-muted border-border lowercase`},z={class:`px-4 py-2.5 text-right font-mono text-xs text-foreground-muted hidden sm:table-cell`},ne={class:`px-4 py-2.5`},B={class:`px-4 py-2.5 text-right`},V={key:3,class:`flex justify-center`},H=Object.assign({name:`TracesView`},{__name:`Traces`,setup(H){let U=b(),W=n([]),G=n(!1),K=n(``),q=n(``),J=n(``),Y=n(``),X=n(!1),Z=[{value:``,label:`All`},{value:`success`,label:`Success`},{value:`error`,label:`Errors`}],Q=async({append:e=!1}={})=>{G.value=!0,K.value=``;try{let t={limit:50};J.value&&(t.function_id=J.value),Y.value&&(t.status=Y.value),X.value&&(t.outlier_only=`1`),e&&q.value&&(t.before=q.value);let n=await _(t),r=n.data?.traces||[];e?W.value.push(...r):W.value=r,q.value=n.data?.next_cursor||``}catch(e){K.value=e?.response?.data?.error?.message||e?.message||`failed to load traces`}finally{G.value=!1}},$=()=>{q.value=``,Q()},re=()=>Q({append:!0}),ie=e=>{Y.value=e,$()},ae=()=>{X.value=!X.value,$()},oe=e=>U.push(`/traces/${e}`),se=e=>e?new Date(e).toLocaleTimeString(void 0,{hour12:!1}):`—`;return i($),(n,i)=>(e(),s(`div`,te,[o(`div`,w,[i[2]||=o(`div`,null,[o(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` Traces `),o(`p`,{class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},` Invocation chains across functions and triggers. `)],-1),c(y,{variant:`secondary`,size:`sm`,loading:G.value,onClick:$},{default:t(()=>[c(a(g),{class:`w-3.5 h-3.5`}),i[1]||=d(` Refresh `,-1)]),_:1},8,[`loading`])]),o(`div`,T,[o(`div`,E,[c(a(S),{class:`w-3.5 h-3.5 absolute left-2.5 top-1/2 -translate-y-1/2 text-foreground-muted/60 pointer-events-none`}),r(o(`input`,{"onUpdate:modelValue":i[0]||=e=>J.value=e,placeholder:`Filter by function id or name…`,class:`w-full bg-background border border-border rounded-md pl-8 pr-3 py-1.5 text-base sm:text-xs text-foreground placeholder-foreground-muted/60 focus:outline-none focus:border-white`,onKeydown:C($,[`enter`])},null,544),[[v,J.value]])]),o(`div`,D,[(e(),s(f,null,l(Z,e=>c(y,{key:e.value,variant:`chip`,size:`xs`,active:Y.value===e.value,class:`shrink-0 snap-start`,onClick:t=>ie(e.value)},{default:t(()=>[d(m(e.label),1)]),_:2},1032,[`active`,`onClick`])),64)),i[4]||=o(`span`,{class:`text-foreground-muted/40 shrink-0`},`·`,-1),c(y,{variant:`chip`,size:`xs`,active:X.value,class:`shrink-0 snap-start`,onClick:ae},{default:t(()=>[c(a(h),{class:`w-3 h-3`}),i[3]||=d(` Outliers only `,-1)]),_:1},8,[`active`])])]),K.value?(e(),s(`div`,O,m(K.value),1)):!W.value.length&&!G.value?(e(),s(`div`,k,[c(a(x),{class:`w-6 h-6 mx-auto mb-3 text-foreground-muted/50`}),i[5]||=o(`p`,null,`No traces yet.`,-1),i[6]||=o(`p`,{class:`text-xs mt-1 text-foreground-muted/60`},` HTTP and scheduled invocations appear here. `,-1)])):(e(),s(`div`,A,[o(`table`,j,[i[7]||=o(`thead`,{class:`text-xs text-foreground-muted uppercase bg-surface border-b border-border`},[o(`tr`,null,[o(`th`,{class:`px-4 py-3 w-32`},` Time `),o(`th`,{class:`px-4 py-3 w-40`},` Trace `),o(`th`,{class:`px-4 py-3`},` Root function `),o(`th`,{class:`px-4 py-3 w-28 hidden md:table-cell`},` Trigger `),o(`th`,{class:`px-4 py-3 w-24 text-right hidden sm:table-cell`},` Duration `),o(`th`,{class:`px-4 py-3 w-24`},` Status `),o(`th`,{class:`px-4 py-3 w-10`})])],-1),o(`tbody`,M,[(e(!0),s(f,null,l(W.value,t=>(e(),s(`tr`,{key:t.trace_id,class:`hover:bg-surface/40 cursor-pointer transition-colors`,onClick:e=>oe(t.trace_id)},[o(`td`,P,m(se(t.started_at)),1),o(`td`,F,m(t.trace_id.slice(0,11)),1),o(`td`,I,m(t.function_name||t.root_function_id),1),o(`td`,L,[o(`span`,R,m(t.trigger||a(`—`)),1)]),o(`td`,z,m(t.duration_ms==null?a(`—`):`${t.duration_ms}ms`),1),o(`td`,ne,[c(ee,{status:t.status},null,8,[`status`])]),o(`td`,B,[t.is_outlier?(e(),u(a(h),{key:0,class:`w-3.5 h-3.5 text-amber-400 inline`,title:`Latency outlier vs P95 baseline`})):p(``,!0)])],8,N))),128))])])])),q.value?(e(),s(`div`,V,[c(y,{variant:`ghost`,size:`sm`,onClick:re},{default:t(()=>[...i[8]||=[d(` Load more `,-1)]]),_:1})])):p(``,!0)]))}});export{H as default}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/Webhooks-Dv44P51d.js b/backend/internal/server/ui_dist/assets/Webhooks-D2CkyonR.js similarity index 97% rename from backend/internal/server/ui_dist/assets/Webhooks-Dv44P51d.js rename to backend/internal/server/ui_dist/assets/Webhooks-D2CkyonR.js index 86bdc4ca..fa311c56 100644 --- a/backend/internal/server/ui_dist/assets/Webhooks-Dv44P51d.js +++ b/backend/internal/server/ui_dist/assets/Webhooks-D2CkyonR.js @@ -1 +1 @@ -import{C as e,D as t,F as n,G as r,I as i,T as ee,Z as a,c as o,d as s,gt as c,h as l,k as u,l as d,m as f,r as p,s as m,u as h,vt as g}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as te}from"./check-DQm9OMOd.js";import{t as ne}from"./copy-Bt6Rv0BQ.js";import{t as re}from"./rotate-ccw-DBtrGGiP.js";import{t as ie}from"./square-pen-JECRrUKU.js";import{t as ae}from"./trash-2-Bj4AFUfw.js";import{t as oe}from"./zap-CGOT0fxk.js";import{Bt as _,J as se,Lt as v,Nt as ce,St as le,Z as ue,_t as y,gt as de,it as fe,jt as pe,q as me,st as he,u as ge,v as _e,vt as ve}from"./index-C11HmDXN.js";import{t as b}from"./clipboard-D_9N0yai.js";import{t as x}from"./IconButton-CsCZOqWo.js";import{t as S}from"./Modal-BL-P3nUi.js";var C=pe(`circle-check-big`,[[`path`,{d:`M21.801 10A10 10 0 1 1 17 3.335`,key:`yps3ct`}],[`path`,{d:`m9 11 3 3L22 4`,key:`1pflzl`}]]),w={class:`space-y-4`},T={class:`flex items-center justify-between gap-4 flex-wrap`},E={class:`bg-background border border-border rounded-lg overflow-x-auto`},D={class:`w-full text-sm text-left`},O={class:`divide-y divide-border`},k=[`onClick`],A={class:`px-4 py-3 font-medium text-white`},j={class:`flex flex-col`},M={class:`text-xs text-foreground-muted font-mono`},N={class:`px-4 py-3 text-xs text-foreground-muted truncate max-w-xs hidden md:table-cell`},P={class:`px-4 py-3 hidden sm:table-cell`},F={class:`flex flex-wrap gap-1`},ye={class:`px-4 py-3`},be={class:`px-4 py-3 text-foreground-muted text-xs hidden lg:table-cell`},xe={class:`inline-flex items-center gap-1`},Se={key:0},Ce={key:0,class:`space-y-4`},we={class:`flex flex-wrap gap-1.5`},Te={class:`flex items-center gap-2 pt-1`},Ee={key:1,class:`space-y-3`},De={class:`flex items-center gap-2 text-success-fg`},Oe={class:`bg-background border border-border rounded p-3 font-mono text-xs break-all flex items-center gap-2`},ke={class:`flex-1 text-foreground`},Ae={class:`bg-background border-l border-border w-full max-w-2xl h-full overflow-y-auto`},je={class:`border-b border-border px-6 py-4 flex items-center justify-between bg-surface sticky top-0`},Me={class:`min-w-0`},Ne={class:`text-base font-semibold text-foreground truncate`},Pe={class:`text-xs text-foreground-muted font-mono truncate`},Fe={class:`p-4 space-y-2`},Ie={key:0,class:`text-center text-foreground-muted text-sm py-12`},I={class:`flex items-center justify-between gap-2 flex-wrap`},Le={class:`text-xs font-mono text-foreground`},Re={class:`flex items-center justify-between text-xs text-foreground-muted gap-2 flex-wrap`},ze={class:`font-mono`},Be={class:`flex items-center justify-between text-xs text-foreground-muted gap-2 flex-wrap`},Ve={key:0},He=[`title`],L=Object.assign({name:`WebhooksView`},{__name:`Webhooks`,setup(pe){let L=de(),Ue=[{value:`*`},{value:`deployment.succeeded`},{value:`deployment.failed`},{value:`function.created`},{value:`function.updated`},{value:`function.deleted`},{value:`execution.error`},{value:`cron.failed`},{value:`job.succeeded`},{value:`job.failed`}],R=r([]),z=r(!1),B=r(null),V=r(!1),H=r(``),U=r(!1),W=r({name:``,url:``,events:[`*`],enabled:!0}),G=r(null),K=r([]),q=null,J=m(()=>W.value.name.trim()&&W.value.url.trim()&&W.value.events.length>0),We=e=>!e.events||e.events.length===0?[`*`]:e.events.length>3?[...e.events.slice(0,2),`+${e.events.length-2}`]:e.events,Ge=e=>e.enabled?e.last_status===`failed`?`bg-danger-tint text-danger-fg border-danger-ring`:e.last_status===`ok`?`bg-success-tint text-success-fg border-success-ring`:`bg-warning-tint text-warning-fg border-warning-ring`:`bg-warning-tint text-warning-fg border-warning-ring`,Ke=e=>e.enabled?e.last_status===`failed`?`bg-danger-fg`:e.last_status===`ok`?`bg-success-fg`:`bg-warning-fg`:`bg-warning-fg`,qe=e=>e.enabled?e.last_status===`failed`?`failing`:e.last_status===`ok`?`healthy`:`pending first delivery`:`paused`,Je=e=>{switch(e){case`pending`:return`bg-warning-tint text-warning-fg border-warning-ring`;case`running`:return`bg-info-tint text-info-fg border-info-ring`;case`succeeded`:return`bg-success-tint text-success-fg border-success-ring`;case`failed`:return`bg-danger-tint text-danger-fg border-danger-ring`;default:return`bg-surface text-foreground-muted border-border`}},Y=e=>e?new Date(e).toLocaleString(`en-US`,{month:`short`,day:`numeric`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`}):`—`,X=async()=>{try{let e=await se();R.value=e.data.subscriptions||[]}catch(e){console.error(`Failed to load webhooks`,e)}},Ye=()=>{B.value=null,W.value={name:``,url:``,events:[`*`],enabled:!0},H.value=``,U.value=!1,z.value=!0},Xe=e=>{B.value=e.id,W.value={name:e.name,url:e.url,events:[...e.events||[`*`]],enabled:e.enabled},H.value=``,z.value=!0},Z=()=>{z.value=!1,B.value=null,H.value=``},Ze=e=>{let t=W.value.events.indexOf(e);t>=0?W.value.events.splice(t,1):W.value.events.push(e)},Qe=async()=>{if(!(!J.value||V.value)){V.value=!0;try{if(B.value)await he(B.value,{name:W.value.name.trim(),url:W.value.url.trim(),events:W.value.events,enabled:W.value.enabled}),await X(),Z();else{let e=await ge({name:W.value.name.trim(),url:W.value.url.trim(),events:W.value.events,enabled:W.value.enabled});H.value=e.data.secret,await X()}}catch(e){L.notify({title:`Failed to save webhook`,message:e?.response?.data?.error?.message||e.message,danger:!0})}finally{V.value=!1}}},$e=async()=>{await b(H.value)&&(U.value=!0,setTimeout(()=>{U.value=!1},1500))},et=async e=>{if(await L.ask({title:`Delete "${e.name}"?`,message:`Future events will not fire to this URL. Existing deliveries will be removed too.`,confirmLabel:`Delete`,danger:!0}))try{await _e(e.id),await X()}catch(e){L.notify({title:`Delete failed`,message:e.message,danger:!0})}},tt=async e=>{try{await fe(e.id),L.notify({title:`Test event queued`,message:`Will deliver to ${e.url} within 5s. Open the row to watch the delivery.`})}catch(e){L.notify({title:`Test failed`,message:e.message,danger:!0})}},nt=async e=>{G.value=e,await Q(e.id),q=setInterval(()=>Q(e.id),4e3)},Q=async e=>{try{let t=await me(e);K.value=t.data.deliveries||[]}catch(e){console.error(`Failed to load deliveries`,e)}},$=()=>{G.value=null,K.value=[],q&&clearInterval(q),q=null},rt=async e=>{try{await ue(e.id),G.value&&await Q(G.value.id)}catch(e){L.notify({title:`Retry failed`,message:e.message,danger:!0})}};return ee(()=>X()),e(()=>{q&&clearInterval(q)}),(e,r)=>(t(),s(`div`,w,[o(`div`,T,[r[6]||=o(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` Webhooks `,-1),l(y,{onClick:Ye},{default:n(()=>[l(a(le),{class:`w-4 h-4`}),r[5]||=f(` New webhook `,-1)]),_:1})]),r[21]||=o(`p`,{class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},` Send signed system events to external URLs. `,-1),o(`div`,E,[o(`table`,D,[r[8]||=o(`thead`,{class:`text-xs text-foreground-muted uppercase bg-surface border-b border-border`},[o(`tr`,null,[o(`th`,{class:`px-4 py-3 font-medium`},` Name `),o(`th`,{class:`px-4 py-3 font-medium hidden md:table-cell`},` URL `),o(`th`,{class:`px-4 py-3 font-medium hidden sm:table-cell`},` Events `),o(`th`,{class:`px-4 py-3 font-medium`},` Status `),o(`th`,{class:`px-4 py-3 font-medium hidden lg:table-cell`},` Last delivery `),o(`th`,{class:`px-4 py-3 font-medium text-right`},` Actions `)])],-1),o(`tbody`,O,[(t(!0),s(p,null,u(R.value,e=>(t(),s(`tr`,{key:e.id,class:`hover:bg-surface/40 transition-colors cursor-pointer`,onClick:t=>nt(e)},[o(`td`,A,[o(`div`,j,[o(`span`,null,g(e.name),1),o(`span`,M,g(e.id),1)])]),o(`td`,N,g(e.url),1),o(`td`,P,[o(`div`,F,[(t(!0),s(p,null,u(We(e),e=>(t(),s(`span`,{key:e,class:`inline-flex items-center px-1.5 py-0.5 rounded text-xs bg-surface border border-border text-foreground font-mono`},g(e),1))),128))])]),o(`td`,ye,[o(`span`,{class:c([`inline-flex items-center gap-1.5 px-2 py-0.5 rounded text-xs font-medium border`,Ge(e)])},[o(`span`,{class:c([`w-1.5 h-1.5 rounded-full`,Ke(e)])},null,2),f(` `+g(qe(e)),1)],2)]),o(`td`,be,g(e.last_delivery_at?Y(e.last_delivery_at):a(`—`)),1),o(`td`,{class:`px-4 py-3 text-right`,onClick:r[0]||=_(()=>{},[`stop`])},[o(`div`,xe,[l(x,{icon:a(oe),title:`Send test event`,onClick:t=>tt(e)},null,8,[`icon`,`onClick`]),l(x,{icon:a(ie),title:`Edit`,onClick:t=>Xe(e)},null,8,[`icon`,`onClick`]),l(x,{icon:a(ae),variant:`danger`,title:`Delete`,onClick:t=>et(e)},null,8,[`icon`,`onClick`])])])],8,k))),128)),R.value.length===0?(t(),s(`tr`,Se,[...r[7]||=[o(`td`,{colspan:`6`,class:`px-4 py-12 text-center`},[o(`p`,{class:`text-foreground-muted text-sm`},` No webhooks yet. `),o(`p`,{class:`text-foreground-muted text-xs mt-1`},` Add an endpoint to receive signed system events. `)],-1)]])):h(``,!0)])])]),l(S,{"model-value":z.value,title:B.value?`Edit webhook`:`New webhook`,size:`lg`,"onUpdate:modelValue":r[4]||=e=>e?null:Z()},{footer:n(()=>[H.value?h(``,!0):(t(),d(y,{key:0,variant:`ghost`,onClick:Z},{default:n(()=>[...r[17]||=[f(` Cancel `,-1)]]),_:1})),H.value?(t(),d(y,{key:2,onClick:Z},{default:n(()=>[...r[18]||=[f(` Done `,-1)]]),_:1})):(t(),d(y,{key:1,disabled:!J.value||V.value,onClick:Qe},{default:n(()=>[f(g(V.value?`Saving…`:B.value?`Save`:`Create`),1)]),_:1},8,[`disabled`]))]),default:n(()=>[H.value?(t(),s(`div`,Ee,[o(`div`,De,[l(a(C),{class:`w-5 h-5`}),r[15]||=o(`span`,{class:`text-sm font-medium`},`Webhook created`,-1)]),r[16]||=o(`p`,{class:`text-xs text-foreground-muted`},[f(` Copy this secret `),o(`span`,{class:`text-foreground font-medium`},`now`),f(`. It won't be shown again. `)],-1),o(`div`,Oe,[o(`code`,ke,g(H.value),1),l(x,{icon:U.value?a(te):a(ne),title:U.value?`Copied`:`Copy secret`,onClick:$e},null,8,[`icon`,`title`])])])):(t(),s(`div`,Ce,[o(`div`,null,[r[9]||=o(`label`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-1.5`},`Name`,-1),i(o(`input`,{"onUpdate:modelValue":r[1]||=e=>W.value.name=e,placeholder:`ops-slack`,class:`w-full bg-background border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-white focus:border-white`},null,512),[[v,W.value.name]])]),o(`div`,null,[r[10]||=o(`label`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-1.5`},`Receiver URL`,-1),i(o(`input`,{"onUpdate:modelValue":r[2]||=e=>W.value.url=e,placeholder:`https://hooks.slack.com/services/...`,class:`w-full bg-background border border-border rounded-md px-3 py-2 text-sm text-foreground font-mono focus:outline-none focus:ring-1 focus:ring-white focus:border-white`},null,512),[[v,W.value.url]]),r[11]||=o(`p`,{class:`text-xs text-foreground-muted mt-1.5`},` The receiver must respond 2xx within 15s. Failed deliveries retry up to 5× with exponential backoff. `,-1)]),o(`div`,null,[r[12]||=o(`label`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-1.5`},`Events`,-1),o(`div`,we,[(t(),s(p,null,u(Ue,e=>l(y,{key:e.value,variant:`chip`,size:`xs`,active:W.value.events.includes(e.value),class:`font-mono`,onClick:t=>Ze(e.value)},{default:n(()=>[f(g(e.value),1)]),_:2},1032,[`active`,`onClick`])),64))]),r[13]||=o(`p`,{class:`text-xs text-foreground-muted mt-1.5`},[f(` Pick `),o(`code`,{class:`font-mono`},`*`),f(` to receive every event. Each badge above is one of the 8 system events that can fire today. `)],-1)]),o(`div`,Te,[i(o(`input`,{id:`enabled`,"onUpdate:modelValue":r[3]||=e=>W.value.enabled=e,type:`checkbox`,class:`w-4 h-4 rounded border-border bg-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background`},null,512),[[ce,W.value.enabled]]),r[14]||=o(`label`,{for:`enabled`,class:`text-sm text-foreground`},`Enabled`,-1)])]))]),_:1},8,[`model-value`,`title`]),G.value?(t(),s(`div`,{key:0,class:`fixed inset-0 bg-black/60 backdrop-blur-sm flex justify-end z-50`,onClick:_($,[`self`])},[o(`div`,Ae,[o(`div`,je,[o(`div`,Me,[o(`h2`,Ne,` Deliveries · `+g(G.value.name),1),o(`p`,Pe,g(G.value.id),1)]),l(x,{icon:a(ve),title:`Close`,onClick:$},null,8,[`icon`])]),o(`div`,Fe,[K.value.length?h(``,!0):(t(),s(`div`,Ie,[...r[19]||=[f(` No deliveries yet. Trigger a system event or use `,-1),o(`span`,{class:`text-foreground`},`Send test event`,-1),f(` to seed one. `,-1)]])),(t(!0),s(p,null,u(K.value,e=>(t(),s(`div`,{key:e.id,class:`bg-surface border border-border rounded p-3 space-y-1.5`},[o(`div`,I,[o(`code`,Le,g(e.event_name),1),o(`span`,{class:c([`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium border`,Je(e.status)])},g(e.status),3)]),o(`div`,Re,[o(`span`,ze,g(e.id),1),o(`span`,null,g(Y(e.created_at)),1)]),o(`div`,Be,[o(`span`,null,`attempts `+g(e.attempts)+` / `+g(e.max_attempts),1),e.response_status?(t(),s(`span`,Ve,`HTTP `+g(e.response_status),1)):h(``,!0)]),e.last_error?(t(),s(`p`,{key:0,class:`text-xs text-danger-fg truncate`,title:e.last_error},g(e.last_error),9,He)):h(``,!0),e.status===`failed`?(t(),d(y,{key:1,size:`xs`,variant:`ghost`,onClick:t=>rt(e)},{default:n(()=>[l(a(re),{class:`w-3.5 h-3.5`}),r[20]||=f(` Retry `,-1)]),_:1},8,[`onClick`])):h(``,!0)]))),128))])])])):h(``,!0)]))}});export{L as default}; \ No newline at end of file +import{C as e,D as t,F as n,G as r,I as i,T as ee,Z as a,c as o,d as s,gt as c,h as l,k as u,l as d,m as f,r as p,s as m,u as h,vt as g}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{t as te}from"./check-BNre7JFR.js";import{t as ne}from"./copy-BqdwwcxC.js";import{t as re}from"./rotate-ccw-DWwjKCqh.js";import{t as ie}from"./square-pen-DCrjAsLy.js";import{t as ae}from"./trash-2-DaeYqnW_.js";import{t as oe}from"./zap-BXGoxm_a.js";import{Bt as _,J as se,Lt as v,Nt as ce,St as le,Z as ue,_t as y,gt as de,it as fe,jt as pe,q as me,st as he,u as ge,v as _e,vt as ve}from"./index-DTqMKlE1.js";import{t as b}from"./clipboard-D_9N0yai.js";import{t as x}from"./IconButton-CsCZOqWo.js";import{t as S}from"./Modal-BAoZams6.js";var C=pe(`circle-check-big`,[[`path`,{d:`M21.801 10A10 10 0 1 1 17 3.335`,key:`yps3ct`}],[`path`,{d:`m9 11 3 3L22 4`,key:`1pflzl`}]]),w={class:`space-y-4`},T={class:`flex items-center justify-between gap-4 flex-wrap`},E={class:`bg-background border border-border rounded-lg overflow-x-auto`},D={class:`w-full text-sm text-left`},O={class:`divide-y divide-border`},k=[`onClick`],A={class:`px-4 py-3 font-medium text-white`},j={class:`flex flex-col`},M={class:`text-xs text-foreground-muted font-mono`},N={class:`px-4 py-3 text-xs text-foreground-muted truncate max-w-xs hidden md:table-cell`},P={class:`px-4 py-3 hidden sm:table-cell`},F={class:`flex flex-wrap gap-1`},ye={class:`px-4 py-3`},be={class:`px-4 py-3 text-foreground-muted text-xs hidden lg:table-cell`},xe={class:`inline-flex items-center gap-1`},Se={key:0},Ce={key:0,class:`space-y-4`},we={class:`flex flex-wrap gap-1.5`},Te={class:`flex items-center gap-2 pt-1`},Ee={key:1,class:`space-y-3`},De={class:`flex items-center gap-2 text-success-fg`},Oe={class:`bg-background border border-border rounded p-3 font-mono text-xs break-all flex items-center gap-2`},ke={class:`flex-1 text-foreground`},Ae={class:`bg-background border-l border-border w-full max-w-2xl h-full overflow-y-auto`},je={class:`border-b border-border px-6 py-4 flex items-center justify-between bg-surface sticky top-0`},Me={class:`min-w-0`},Ne={class:`text-base font-semibold text-foreground truncate`},Pe={class:`text-xs text-foreground-muted font-mono truncate`},Fe={class:`p-4 space-y-2`},Ie={key:0,class:`text-center text-foreground-muted text-sm py-12`},I={class:`flex items-center justify-between gap-2 flex-wrap`},Le={class:`text-xs font-mono text-foreground`},Re={class:`flex items-center justify-between text-xs text-foreground-muted gap-2 flex-wrap`},ze={class:`font-mono`},Be={class:`flex items-center justify-between text-xs text-foreground-muted gap-2 flex-wrap`},Ve={key:0},He=[`title`],L=Object.assign({name:`WebhooksView`},{__name:`Webhooks`,setup(pe){let L=de(),Ue=[{value:`*`},{value:`deployment.succeeded`},{value:`deployment.failed`},{value:`function.created`},{value:`function.updated`},{value:`function.deleted`},{value:`execution.error`},{value:`cron.failed`},{value:`job.succeeded`},{value:`job.failed`}],R=r([]),z=r(!1),B=r(null),V=r(!1),H=r(``),U=r(!1),W=r({name:``,url:``,events:[`*`],enabled:!0}),G=r(null),K=r([]),q=null,J=m(()=>W.value.name.trim()&&W.value.url.trim()&&W.value.events.length>0),We=e=>!e.events||e.events.length===0?[`*`]:e.events.length>3?[...e.events.slice(0,2),`+${e.events.length-2}`]:e.events,Ge=e=>e.enabled?e.last_status===`failed`?`bg-danger-tint text-danger-fg border-danger-ring`:e.last_status===`ok`?`bg-success-tint text-success-fg border-success-ring`:`bg-warning-tint text-warning-fg border-warning-ring`:`bg-warning-tint text-warning-fg border-warning-ring`,Ke=e=>e.enabled?e.last_status===`failed`?`bg-danger-fg`:e.last_status===`ok`?`bg-success-fg`:`bg-warning-fg`:`bg-warning-fg`,qe=e=>e.enabled?e.last_status===`failed`?`failing`:e.last_status===`ok`?`healthy`:`pending first delivery`:`paused`,Je=e=>{switch(e){case`pending`:return`bg-warning-tint text-warning-fg border-warning-ring`;case`running`:return`bg-info-tint text-info-fg border-info-ring`;case`succeeded`:return`bg-success-tint text-success-fg border-success-ring`;case`failed`:return`bg-danger-tint text-danger-fg border-danger-ring`;default:return`bg-surface text-foreground-muted border-border`}},Y=e=>e?new Date(e).toLocaleString(`en-US`,{month:`short`,day:`numeric`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`}):`—`,X=async()=>{try{let e=await se();R.value=e.data.subscriptions||[]}catch(e){console.error(`Failed to load webhooks`,e)}},Ye=()=>{B.value=null,W.value={name:``,url:``,events:[`*`],enabled:!0},H.value=``,U.value=!1,z.value=!0},Xe=e=>{B.value=e.id,W.value={name:e.name,url:e.url,events:[...e.events||[`*`]],enabled:e.enabled},H.value=``,z.value=!0},Z=()=>{z.value=!1,B.value=null,H.value=``},Ze=e=>{let t=W.value.events.indexOf(e);t>=0?W.value.events.splice(t,1):W.value.events.push(e)},Qe=async()=>{if(!(!J.value||V.value)){V.value=!0;try{if(B.value)await he(B.value,{name:W.value.name.trim(),url:W.value.url.trim(),events:W.value.events,enabled:W.value.enabled}),await X(),Z();else{let e=await ge({name:W.value.name.trim(),url:W.value.url.trim(),events:W.value.events,enabled:W.value.enabled});H.value=e.data.secret,await X()}}catch(e){L.notify({title:`Failed to save webhook`,message:e?.response?.data?.error?.message||e.message,danger:!0})}finally{V.value=!1}}},$e=async()=>{await b(H.value)&&(U.value=!0,setTimeout(()=>{U.value=!1},1500))},et=async e=>{if(await L.ask({title:`Delete "${e.name}"?`,message:`Future events will not fire to this URL. Existing deliveries will be removed too.`,confirmLabel:`Delete`,danger:!0}))try{await _e(e.id),await X()}catch(e){L.notify({title:`Delete failed`,message:e.message,danger:!0})}},tt=async e=>{try{await fe(e.id),L.notify({title:`Test event queued`,message:`Will deliver to ${e.url} within 5s. Open the row to watch the delivery.`})}catch(e){L.notify({title:`Test failed`,message:e.message,danger:!0})}},nt=async e=>{G.value=e,await Q(e.id),q=setInterval(()=>Q(e.id),4e3)},Q=async e=>{try{let t=await me(e);K.value=t.data.deliveries||[]}catch(e){console.error(`Failed to load deliveries`,e)}},$=()=>{G.value=null,K.value=[],q&&clearInterval(q),q=null},rt=async e=>{try{await ue(e.id),G.value&&await Q(G.value.id)}catch(e){L.notify({title:`Retry failed`,message:e.message,danger:!0})}};return ee(()=>X()),e(()=>{q&&clearInterval(q)}),(e,r)=>(t(),s(`div`,w,[o(`div`,T,[r[6]||=o(`h1`,{class:`text-xl font-semibold text-white tracking-tight`},` Webhooks `,-1),l(y,{onClick:Ye},{default:n(()=>[l(a(le),{class:`w-4 h-4`}),r[5]||=f(` New webhook `,-1)]),_:1})]),r[21]||=o(`p`,{class:`text-sm text-foreground-muted mt-1.5 max-w-prose leading-body`},` Send signed system events to external URLs. `,-1),o(`div`,E,[o(`table`,D,[r[8]||=o(`thead`,{class:`text-xs text-foreground-muted uppercase bg-surface border-b border-border`},[o(`tr`,null,[o(`th`,{class:`px-4 py-3 font-medium`},` Name `),o(`th`,{class:`px-4 py-3 font-medium hidden md:table-cell`},` URL `),o(`th`,{class:`px-4 py-3 font-medium hidden sm:table-cell`},` Events `),o(`th`,{class:`px-4 py-3 font-medium`},` Status `),o(`th`,{class:`px-4 py-3 font-medium hidden lg:table-cell`},` Last delivery `),o(`th`,{class:`px-4 py-3 font-medium text-right`},` Actions `)])],-1),o(`tbody`,O,[(t(!0),s(p,null,u(R.value,e=>(t(),s(`tr`,{key:e.id,class:`hover:bg-surface/40 transition-colors cursor-pointer`,onClick:t=>nt(e)},[o(`td`,A,[o(`div`,j,[o(`span`,null,g(e.name),1),o(`span`,M,g(e.id),1)])]),o(`td`,N,g(e.url),1),o(`td`,P,[o(`div`,F,[(t(!0),s(p,null,u(We(e),e=>(t(),s(`span`,{key:e,class:`inline-flex items-center px-1.5 py-0.5 rounded text-xs bg-surface border border-border text-foreground font-mono`},g(e),1))),128))])]),o(`td`,ye,[o(`span`,{class:c([`inline-flex items-center gap-1.5 px-2 py-0.5 rounded text-xs font-medium border`,Ge(e)])},[o(`span`,{class:c([`w-1.5 h-1.5 rounded-full`,Ke(e)])},null,2),f(` `+g(qe(e)),1)],2)]),o(`td`,be,g(e.last_delivery_at?Y(e.last_delivery_at):a(`—`)),1),o(`td`,{class:`px-4 py-3 text-right`,onClick:r[0]||=_(()=>{},[`stop`])},[o(`div`,xe,[l(x,{icon:a(oe),title:`Send test event`,onClick:t=>tt(e)},null,8,[`icon`,`onClick`]),l(x,{icon:a(ie),title:`Edit`,onClick:t=>Xe(e)},null,8,[`icon`,`onClick`]),l(x,{icon:a(ae),variant:`danger`,title:`Delete`,onClick:t=>et(e)},null,8,[`icon`,`onClick`])])])],8,k))),128)),R.value.length===0?(t(),s(`tr`,Se,[...r[7]||=[o(`td`,{colspan:`6`,class:`px-4 py-12 text-center`},[o(`p`,{class:`text-foreground-muted text-sm`},` No webhooks yet. `),o(`p`,{class:`text-foreground-muted text-xs mt-1`},` Add an endpoint to receive signed system events. `)],-1)]])):h(``,!0)])])]),l(S,{"model-value":z.value,title:B.value?`Edit webhook`:`New webhook`,size:`lg`,"onUpdate:modelValue":r[4]||=e=>e?null:Z()},{footer:n(()=>[H.value?h(``,!0):(t(),d(y,{key:0,variant:`ghost`,onClick:Z},{default:n(()=>[...r[17]||=[f(` Cancel `,-1)]]),_:1})),H.value?(t(),d(y,{key:2,onClick:Z},{default:n(()=>[...r[18]||=[f(` Done `,-1)]]),_:1})):(t(),d(y,{key:1,disabled:!J.value||V.value,onClick:Qe},{default:n(()=>[f(g(V.value?`Saving…`:B.value?`Save`:`Create`),1)]),_:1},8,[`disabled`]))]),default:n(()=>[H.value?(t(),s(`div`,Ee,[o(`div`,De,[l(a(C),{class:`w-5 h-5`}),r[15]||=o(`span`,{class:`text-sm font-medium`},`Webhook created`,-1)]),r[16]||=o(`p`,{class:`text-xs text-foreground-muted`},[f(` Copy this secret `),o(`span`,{class:`text-foreground font-medium`},`now`),f(`. It won't be shown again. `)],-1),o(`div`,Oe,[o(`code`,ke,g(H.value),1),l(x,{icon:U.value?a(te):a(ne),title:U.value?`Copied`:`Copy secret`,onClick:$e},null,8,[`icon`,`title`])])])):(t(),s(`div`,Ce,[o(`div`,null,[r[9]||=o(`label`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-1.5`},`Name`,-1),i(o(`input`,{"onUpdate:modelValue":r[1]||=e=>W.value.name=e,placeholder:`ops-slack`,class:`w-full bg-background border border-border rounded-md px-3 py-2 text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-white focus:border-white`},null,512),[[v,W.value.name]])]),o(`div`,null,[r[10]||=o(`label`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-1.5`},`Receiver URL`,-1),i(o(`input`,{"onUpdate:modelValue":r[2]||=e=>W.value.url=e,placeholder:`https://hooks.slack.com/services/...`,class:`w-full bg-background border border-border rounded-md px-3 py-2 text-sm text-foreground font-mono focus:outline-none focus:ring-1 focus:ring-white focus:border-white`},null,512),[[v,W.value.url]]),r[11]||=o(`p`,{class:`text-xs text-foreground-muted mt-1.5`},` The receiver must respond 2xx within 15s. Failed deliveries retry up to 5× with exponential backoff. `,-1)]),o(`div`,null,[r[12]||=o(`label`,{class:`text-xs font-medium text-foreground-muted uppercase tracking-wide block mb-1.5`},`Events`,-1),o(`div`,we,[(t(),s(p,null,u(Ue,e=>l(y,{key:e.value,variant:`chip`,size:`xs`,active:W.value.events.includes(e.value),class:`font-mono`,onClick:t=>Ze(e.value)},{default:n(()=>[f(g(e.value),1)]),_:2},1032,[`active`,`onClick`])),64))]),r[13]||=o(`p`,{class:`text-xs text-foreground-muted mt-1.5`},[f(` Pick `),o(`code`,{class:`font-mono`},`*`),f(` to receive every event. Each badge above is one of the 8 system events that can fire today. `)],-1)]),o(`div`,Te,[i(o(`input`,{id:`enabled`,"onUpdate:modelValue":r[3]||=e=>W.value.enabled=e,type:`checkbox`,class:`w-4 h-4 rounded border-border bg-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background`},null,512),[[ce,W.value.enabled]]),r[14]||=o(`label`,{for:`enabled`,class:`text-sm text-foreground`},`Enabled`,-1)])]))]),_:1},8,[`model-value`,`title`]),G.value?(t(),s(`div`,{key:0,class:`fixed inset-0 bg-black/60 backdrop-blur-sm flex justify-end z-50`,onClick:_($,[`self`])},[o(`div`,Ae,[o(`div`,je,[o(`div`,Me,[o(`h2`,Ne,` Deliveries · `+g(G.value.name),1),o(`p`,Pe,g(G.value.id),1)]),l(x,{icon:a(ve),title:`Close`,onClick:$},null,8,[`icon`])]),o(`div`,Fe,[K.value.length?h(``,!0):(t(),s(`div`,Ie,[...r[19]||=[f(` No deliveries yet. Trigger a system event or use `,-1),o(`span`,{class:`text-foreground`},`Send test event`,-1),f(` to seed one. `,-1)]])),(t(!0),s(p,null,u(K.value,e=>(t(),s(`div`,{key:e.id,class:`bg-surface border border-border rounded p-3 space-y-1.5`},[o(`div`,I,[o(`code`,Le,g(e.event_name),1),o(`span`,{class:c([`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium border`,Je(e.status)])},g(e.status),3)]),o(`div`,Re,[o(`span`,ze,g(e.id),1),o(`span`,null,g(Y(e.created_at)),1)]),o(`div`,Be,[o(`span`,null,`attempts `+g(e.attempts)+` / `+g(e.max_attempts),1),e.response_status?(t(),s(`span`,Ve,`HTTP `+g(e.response_status),1)):h(``,!0)]),e.last_error?(t(),s(`p`,{key:0,class:`text-xs text-danger-fg truncate`,title:e.last_error},g(e.last_error),9,He)):h(``,!0),e.status===`failed`?(t(),d(y,{key:1,size:`xs`,variant:`ghost`,onClick:t=>rt(e)},{default:n(()=>[l(a(re),{class:`w-3.5 h-3.5`}),r[20]||=f(` Retry `,-1)]),_:1},8,[`onClick`])):h(``,!0)]))),128))])])])):h(``,!0)]))}});export{L as default}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/aiPrompts-NjljN3ld.js b/backend/internal/server/ui_dist/assets/aiPrompts-XrsFCpj_.js similarity index 93% rename from backend/internal/server/ui_dist/assets/aiPrompts-NjljN3ld.js rename to backend/internal/server/ui_dist/assets/aiPrompts-XrsFCpj_.js index 88c7e829..263405ca 100644 --- a/backend/internal/server/ui_dist/assets/aiPrompts-NjljN3ld.js +++ b/backend/internal/server/ui_dist/assets/aiPrompts-XrsFCpj_.js @@ -49,17 +49,17 @@ Secrets are stored encrypted at rest, decrypted only into the worker environment -Every function has the \`orva\` module pre-imported — zero install, zero config. The SDK reaches orvad over the bridge network using HTTP, so the function MUST be created with \`network_mode: "egress"\` (or updated to it later). With the default \`network_mode: "none"\`, every SDK call fails at runtime with \`ENETUNREACH\` / \`OrvaUnavailableError\` / \`TypeError: fetch failed\`. THREE primitives: +Every function has the \`orva\` module pre-imported — zero install, zero config. The SDK reaches orvad over the bridge network using HTTP, so the function MUST be created with \`network_mode: "egress"\` (or updated to it later). With the default \`network_mode: "none"\`, every SDK call fails at runtime with \`ENETUNREACH\` / \`OrvaUnavailableError\` / \`TypeError: fetch failed\`. Core primitives: ## orva.kv — per-function key/value store on SQLite -Per-function namespace; keys never collide across functions. Optional TTL in seconds (sweep every 5 min AND filtered at read time, so stale reads are impossible). Values JSON-serialised; cap 64 KB per value. Keys cap 256 chars. Use for: caches, idempotency keys, rate-limit counters, light session state, feature flags, last-seen markers. NOT a primary database, NOT a queue, NOT for blob storage. +Per-function namespace enforced by a process-signed, function-scoped SDK credential; caller identity never comes from a request header. Keys never collide across functions. TTL is optional: omit it to preserve an existing expiry (new keys are persistent), pass 0 to clear expiry, or pass a positive value to set/refresh it; negative values are rejected. Expiry is filtered at read time and swept every 5 minutes. Values must be valid JSON and are capped at 64 KiB; keys must be non-empty UTF-8 and are capped at 256 characters. Batches are all-or-nothing and capped at 100 operations. Use for: caches, idempotency keys, rate-limit counters, light session state, feature flags, last-seen markers. NOT a primary database, NOT a queue, NOT for blob storage. Python: from orva import kv kv.put("user:42", {"name": "Ada", "tier": "pro"}, ttl_seconds=3600) user = kv.get("user:42", default=None) # → dict, or None - pages = kv.list(prefix="page:", limit=50) # → [keys]; pass cursor= for pagination + pages = kv.list(prefix="page:", limit=50) # → {keys, next_cursor}; pass next_cursor back as cursor kv.delete("user:42") # idempotent; no error if missing Node: @@ -90,8 +90,8 @@ Tell the user about this when their function uses kv state and they might want to inspect it. kv.put(f"req:{idempotency_key}", "1", ttl_seconds=86400) -## orva.invoke — function-to-function calls (no HTTP, no auth) -Bypasses the proxy stack and dispatches via the warm pool. Faster than internal HTTP, no signing required. Recursion guard: max call depth 8. The callee's full {statusCode, headers, body} is returned; body is JSON-decoded when possible. +## orva.invoke — function-to-function calls +Uses the scoped internal SDK endpoint and dispatches through the warm pool; the signed credential supplies immutable caller attribution while the named target may be another function. Recursion guard: max call depth 8. The callee's full {statusCode, headers, body} is returned; body is JSON-decoded when possible. Python: from orva import invoke, OrvaError @@ -330,7 +330,7 @@ Every night at 3 AM UTC, delete all KV keys that start with "session:" and are o -**Plan.** Python 3.14 handler triggered by a cron schedule \`0 3 * * *\` UTC. Lists keys with prefix "session:" via orva.kv.list, page by page, and lets the TTL-based sweep handle expiration — but for keys we want gone NOW, we delete explicitly. No deps. No egress. Recommended auth: leave public; cron triggers don't go through the HTTP gate, but if anyone could hit it from outside, set platform_key. +**Plan.** Python 3.14 handler triggered by a cron schedule \`0 3 * * *\` UTC. Lists keys with prefix "session:" via orva.kv.list, page by page, and lets the TTL-based sweep handle expiration — but for keys we want gone NOW, we delete explicitly. No deps. Requires egress because the bundled KV SDK calls Orva's control plane. Recommended auth: leave public; cron triggers don't go through the HTTP gate, but if anyone could hit it from outside, set platform_key. \`\`\`python import json @@ -343,10 +343,10 @@ def handler(event): while True: page = kv.list(prefix="session:", limit=200, cursor=cursor) - for key in page.get("keys", []): - kv.delete(key) + for entry in page.get("keys", []): + kv.delete(entry["key"]) deleted += 1 - cursor = page.get("cursor") + cursor = page.get("next_cursor") if not cursor: break @@ -363,7 +363,7 @@ def handler(event): "body": {"deleted": deleted}} \`\`\` -Wire it up: open Schedules → New Schedule → expression \`0 3 * * *\`, timezone UTC, function this one. Set auth_mode to platform_key on the function so the URL can't be triggered manually by random callers. +Wire it up: enable outbound network, then open Schedules → New Schedule → expression \`0 3 * * *\`, timezone UTC, function this one. Set auth_mode to platform_key on the function so the URL can't be triggered manually by random callers. diff --git a/backend/internal/server/ui_dist/assets/arrow-left-CEJWChyG.js b/backend/internal/server/ui_dist/assets/arrow-left-Ba3OArNt.js similarity index 58% rename from backend/internal/server/ui_dist/assets/arrow-left-CEJWChyG.js rename to backend/internal/server/ui_dist/assets/arrow-left-Ba3OArNt.js index b3a8d017..24aa1139 100644 --- a/backend/internal/server/ui_dist/assets/arrow-left-CEJWChyG.js +++ b/backend/internal/server/ui_dist/assets/arrow-left-Ba3OArNt.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`arrow-left`,[[`path`,{d:`m12 19-7-7 7-7`,key:`1l729n`}],[`path`,{d:`M19 12H5`,key:`x3x0zl`}]]);export{t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`arrow-left`,[[`path`,{d:`m12 19-7-7 7-7`,key:`1l729n`}],[`path`,{d:`M19 12H5`,key:`x3x0zl`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/book-open-BnyP35ri.js b/backend/internal/server/ui_dist/assets/book-open-B7-cMpW6.js similarity index 77% rename from backend/internal/server/ui_dist/assets/book-open-BnyP35ri.js rename to backend/internal/server/ui_dist/assets/book-open-B7-cMpW6.js index 97ccd1c4..3d9e9c35 100644 --- a/backend/internal/server/ui_dist/assets/book-open-BnyP35ri.js +++ b/backend/internal/server/ui_dist/assets/book-open-B7-cMpW6.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`book-open`,[[`path`,{d:`M12 5v16`,key:`1f6ucr`}],[`path`,{d:`M20.001 19A2 2 0 0022 17V5a2 2 0 00-1.999-2L16 3.002A5 5 0 0012 5a5 5 0 00-4-2H4a2 2 0 00-2 2v12a2 2 0 001.999 2H8a5 5 0 014 2 5 5 0 014-2z`,key:`1fyvmf`}]]);export{t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`book-open`,[[`path`,{d:`M12 5v16`,key:`1f6ucr`}],[`path`,{d:`M20.001 19A2 2 0 0022 17V5a2 2 0 00-1.999-2L16 3.002A5 5 0 0012 5a5 5 0 00-4-2H4a2 2 0 00-2 2v12a2 2 0 001.999 2H8a5 5 0 014 2 5 5 0 014-2z`,key:`1fyvmf`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/check-BNre7JFR.js b/backend/internal/server/ui_dist/assets/check-BNre7JFR.js new file mode 100644 index 00000000..f8728d08 --- /dev/null +++ b/backend/internal/server/ui_dist/assets/check-BNre7JFR.js @@ -0,0 +1 @@ +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`check`,[[`path`,{d:`M20 6 9 17l-5-5`,key:`1gmf2c`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/check-DQm9OMOd.js b/backend/internal/server/ui_dist/assets/check-DQm9OMOd.js deleted file mode 100644 index da7e8ca9..00000000 --- a/backend/internal/server/ui_dist/assets/check-DQm9OMOd.js +++ /dev/null @@ -1 +0,0 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`check`,[[`path`,{d:`M20 6 9 17l-5-5`,key:`1gmf2c`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/chevron-right-BufLmSBw.js b/backend/internal/server/ui_dist/assets/chevron-right-BufLmSBw.js deleted file mode 100644 index 02be1a1f..00000000 --- a/backend/internal/server/ui_dist/assets/chevron-right-BufLmSBw.js +++ /dev/null @@ -1 +0,0 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`chevron-right`,[[`path`,{d:`m9 18 6-6-6-6`,key:`mthhwq`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/chevron-right-CRxFsA9Y.js b/backend/internal/server/ui_dist/assets/chevron-right-CRxFsA9Y.js new file mode 100644 index 00000000..67dc0945 --- /dev/null +++ b/backend/internal/server/ui_dist/assets/chevron-right-CRxFsA9Y.js @@ -0,0 +1 @@ +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`chevron-right`,[[`path`,{d:`m9 18 6-6-6-6`,key:`mthhwq`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/circle-bxz1-xUq.js b/backend/internal/server/ui_dist/assets/circle-NOse79gh.js similarity index 76% rename from backend/internal/server/ui_dist/assets/circle-bxz1-xUq.js rename to backend/internal/server/ui_dist/assets/circle-NOse79gh.js index 0bfddbc3..d2d9343f 100644 --- a/backend/internal/server/ui_dist/assets/circle-bxz1-xUq.js +++ b/backend/internal/server/ui_dist/assets/circle-NOse79gh.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`circle-x`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m15 9-6 6`,key:`1uzhvr`}],[`path`,{d:`m9 9 6 6`,key:`z0biqf`}]]),n=e(`circle`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}]]);export{t as n,n as t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`circle-x`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m15 9-6 6`,key:`1uzhvr`}],[`path`,{d:`m9 9 6 6`,key:`z0biqf`}]]),n=e(`circle`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}]]);export{t as n,n as t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/circle-alert-BIdKIOKw.js b/backend/internal/server/ui_dist/assets/circle-alert-BnUYYhGE.js similarity index 73% rename from backend/internal/server/ui_dist/assets/circle-alert-BIdKIOKw.js rename to backend/internal/server/ui_dist/assets/circle-alert-BnUYYhGE.js index 3f836323..d3cff568 100644 --- a/backend/internal/server/ui_dist/assets/circle-alert-BIdKIOKw.js +++ b/backend/internal/server/ui_dist/assets/circle-alert-BnUYYhGE.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`circle-alert`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`line`,{x1:`12`,x2:`12`,y1:`8`,y2:`12`,key:`1pkeuh`}],[`line`,{x1:`12`,x2:`12.01`,y1:`16`,y2:`16`,key:`4dfq90`}]]);export{t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`circle-alert`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`line`,{x1:`12`,x2:`12`,y1:`8`,y2:`12`,key:`1pkeuh`}],[`line`,{x1:`12`,x2:`12.01`,y1:`16`,y2:`16`,key:`4dfq90`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/clock-Dl3hMdU8.js b/backend/internal/server/ui_dist/assets/clock-CiH8bQVI.js similarity index 77% rename from backend/internal/server/ui_dist/assets/clock-Dl3hMdU8.js rename to backend/internal/server/ui_dist/assets/clock-CiH8bQVI.js index 52fec8e8..02257288 100644 --- a/backend/internal/server/ui_dist/assets/clock-Dl3hMdU8.js +++ b/backend/internal/server/ui_dist/assets/clock-CiH8bQVI.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`circle-check`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m9 12 2 2 4-4`,key:`dzmm74`}]]),n=e(`clock`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M12 6v6l4 2`,key:`mmk7yg`}]]);export{t as n,n as t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`circle-check`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m9 12 2 2 4-4`,key:`dzmm74`}]]),n=e(`clock`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M12 6v6l4 2`,key:`mmk7yg`}]]);export{t as n,n as t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/copy-Bt6Rv0BQ.js b/backend/internal/server/ui_dist/assets/copy-BqdwwcxC.js similarity index 71% rename from backend/internal/server/ui_dist/assets/copy-Bt6Rv0BQ.js rename to backend/internal/server/ui_dist/assets/copy-BqdwwcxC.js index 964ae265..b017db8e 100644 --- a/backend/internal/server/ui_dist/assets/copy-Bt6Rv0BQ.js +++ b/backend/internal/server/ui_dist/assets/copy-BqdwwcxC.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`copy`,[[`rect`,{width:`14`,height:`14`,x:`8`,y:`8`,rx:`2`,ry:`2`,key:`17jyea`}],[`path`,{d:`M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2`,key:`zix9uf`}]]);export{t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`copy`,[[`rect`,{width:`14`,height:`14`,x:`8`,y:`8`,rx:`2`,ry:`2`,key:`17jyea`}],[`path`,{d:`M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2`,key:`zix9uf`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/flag-BU6shp0V.js b/backend/internal/server/ui_dist/assets/flag-CmjNKmxi.js similarity index 73% rename from backend/internal/server/ui_dist/assets/flag-BU6shp0V.js rename to backend/internal/server/ui_dist/assets/flag-CmjNKmxi.js index b92f200c..ade93ef8 100644 --- a/backend/internal/server/ui_dist/assets/flag-BU6shp0V.js +++ b/backend/internal/server/ui_dist/assets/flag-CmjNKmxi.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`flag`,[[`path`,{d:`M4 22V4a1 1 0 0 1 .4-.8A6 6 0 0 1 8 2c3 0 5 2 7.333 2q2 0 3.067-.8A1 1 0 0 1 20 4v10a1 1 0 0 1-.4.8A6 6 0 0 1 16 16c-3 0-5-2-8-2a6 6 0 0 0-4 1.528`,key:`1jaruq`}]]);export{t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`flag`,[[`path`,{d:`M4 22V4a1 1 0 0 1 .4-.8A6 6 0 0 1 8 2c3 0 5 2 7.333 2q2 0 3.067-.8A1 1 0 0 1 20 4v10a1 1 0 0 1-.4.8A6 6 0 0 1 16 16c-3 0-5-2-8-2a6 6 0 0 0-4 1.528`,key:`1jaruq`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/git-compare-Dh2_YhZP.js b/backend/internal/server/ui_dist/assets/git-compare-Co883xFS.js similarity index 86% rename from backend/internal/server/ui_dist/assets/git-compare-Dh2_YhZP.js rename to backend/internal/server/ui_dist/assets/git-compare-Co883xFS.js index 075f0cfb..0e35728a 100644 --- a/backend/internal/server/ui_dist/assets/git-compare-Dh2_YhZP.js +++ b/backend/internal/server/ui_dist/assets/git-compare-Co883xFS.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`cloud-upload`,[[`path`,{d:`M12 13v8`,key:`1l5pq0`}],[`path`,{d:`M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242`,key:`1pljnt`}],[`path`,{d:`m8 17 4-4 4 4`,key:`1quai1`}]]),n=e(`git-compare`,[[`circle`,{cx:`18`,cy:`18`,r:`3`,key:`1xkwt0`}],[`circle`,{cx:`6`,cy:`6`,r:`3`,key:`1lh9wr`}],[`path`,{d:`M13 6h3a2 2 0 0 1 2 2v7`,key:`1yeb86`}],[`path`,{d:`M11 18H8a2 2 0 0 1-2-2V9`,key:`19pyzm`}]]);export{t as n,n as t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`cloud-upload`,[[`path`,{d:`M12 13v8`,key:`1l5pq0`}],[`path`,{d:`M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242`,key:`1pljnt`}],[`path`,{d:`m8 17 4-4 4 4`,key:`1quai1`}]]),n=e(`git-compare`,[[`circle`,{cx:`18`,cy:`18`,r:`3`,key:`1xkwt0`}],[`circle`,{cx:`6`,cy:`6`,r:`3`,key:`1lh9wr`}],[`path`,{d:`M13 6h3a2 2 0 0 1 2 2v7`,key:`1yeb86`}],[`path`,{d:`M11 18H8a2 2 0 0 1-2-2V9`,key:`19pyzm`}]]);export{t as n,n as t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/globe-C84kKXm0.js b/backend/internal/server/ui_dist/assets/globe-BnBar2lO.js similarity index 72% rename from backend/internal/server/ui_dist/assets/globe-C84kKXm0.js rename to backend/internal/server/ui_dist/assets/globe-BnBar2lO.js index 544a75be..e97950fc 100644 --- a/backend/internal/server/ui_dist/assets/globe-C84kKXm0.js +++ b/backend/internal/server/ui_dist/assets/globe-BnBar2lO.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`globe`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20`,key:`13o1zl`}],[`path`,{d:`M2 12h20`,key:`9i4pu4`}]]);export{t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`globe`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20`,key:`13o1zl`}],[`path`,{d:`M2 12h20`,key:`9i4pu4`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/index-C11HmDXN.js b/backend/internal/server/ui_dist/assets/index-DTqMKlE1.js similarity index 95% rename from backend/internal/server/ui_dist/assets/index-C11HmDXN.js rename to backend/internal/server/ui_dist/assets/index-DTqMKlE1.js index 06948700..46916ad8 100644 --- a/backend/internal/server/ui_dist/assets/index-C11HmDXN.js +++ b/backend/internal/server/ui_dist/assets/index-DTqMKlE1.js @@ -1,2 +1,2 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/Login-CGYB-z1p.js","assets/runtime-core.esm-bundler-BMQPyJ_W.js","assets/circle-alert-BIdKIOKw.js","assets/Onboarding-BI0ADHLS.js","assets/copy-Bt6Rv0BQ.js","assets/Dashboard-CEd_pnH7.js","assets/AI-DwO56pB2.js","assets/axios-DVDuIpRy.js","assets/check-DQm9OMOd.js","assets/ModelMenu-CCBXKr4H.js","assets/pinia-B41TZNUX.js","assets/client-BF51V3uE.js","assets/Drawer-ZggviMzu.js","assets/Drawer-9hPWZO7g.css","assets/pencil-BuAbR0ud.js","assets/rotate-ccw-DBtrGGiP.js","assets/sparkles-BmmGPRlQ.js","assets/trash-2-Bj4AFUfw.js","assets/zap-CGOT0fxk.js","assets/github-dark-D7LxIVih.js","assets/github-dark-C8LL_u4z.css","assets/clipboard-D_9N0yai.js","assets/AI-DWprRWt2.css","assets/FunctionsList-ByM7ruDh.js","assets/globe-C84kKXm0.js","assets/lock-QGBYN3Zu.js","assets/refresh-cw-Bn_tp4gH.js","assets/IconButton-CsCZOqWo.js","assets/Editor-BENkmvs8.js","assets/book-open-BnyP35ri.js","assets/git-compare-Dh2_YhZP.js","assets/settings-2-C7i2we5c.js","assets/key-round-BmyLzD9o.js","assets/play-DdJW9ehy.js","assets/shield-check-HnHCfFke.js","assets/terminal-BVgEq2_Q.js","assets/variable-Doeff6Y-.js","assets/Modal-BL-P3nUi.js","assets/rollbackDiff-DsaWcdbl.js","assets/aiPrompts-NjljN3ld.js","assets/Input-DQ-tWGkn.js","assets/Editor-4XWCDeWw.css","assets/Deployments-DCd1MNSh.js","assets/clock-Dl3hMdU8.js","assets/StatusBadge-BOCx3ovI.js","assets/circle-bxz1-xUq.js","assets/FunctionDiff-DBpPBQBL.js","assets/dist-CR15Grce.js","assets/FunctionDiff-C-FH-cay.css","assets/KVStore-Ly56eJIu.js","assets/InboundWebhooks-C_jOjeYV.js","assets/CronJobs-4ao1_N2n.js","assets/square-pen-JECRrUKU.js","assets/Jobs-DzZOcH5E.js","assets/Activity-C3gP7_TB.js","assets/chevron-right-BufLmSBw.js","assets/InvocationsLog-BZpwfSFT.js","assets/Traces-DKjSRtpZ.js","assets/flag-BU6shp0V.js","assets/TraceDetail-c4yTBunf.js","assets/arrow-left-CEJWChyG.js","assets/ApiKeys-C9fpYpoP.js","assets/time-D8OmbYzY.js","assets/Channels-D-vbrSjj.js","assets/Webhooks-Dv44P51d.js","assets/Firewall-bYvlPpSq.js","assets/Firewall-Cdy796t2.css","assets/Settings-CS-pXJcG.js","assets/Docs-DjpX404o.js","assets/Docs-D8AUJ3IN.css","assets/NotFound-CIp_RSOc.js"])))=>i.map(i=>d[i]); -import{t as e}from"./axios-DVDuIpRy.js";import{$ as t,A as n,D as r,E as i,F as a,G as o,I as s,K as c,M as l,O as u,P as d,Q as f,T as p,U as m,W as h,Z as g,_,a as v,at as y,b,c as x,ct as S,d as C,dt as w,et as T,f as E,ft as D,gt as O,h as k,ht as A,i as ee,it as j,j as te,k as M,l as N,lt as ne,m as re,mt as ie,n as ae,nt as oe,o as se,ot as ce,pt as P,q as le,r as F,rt as ue,s as I,st as de,t as fe,tt as pe,u as L,ut as me,v as he,vt as R,x as z,yt as ge}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{n as _e,t as ve}from"./pinia-B41TZNUX.js";import{n as ye,t as B}from"./client-BF51V3uE.js";(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var be=void 0,xe=typeof window<`u`&&window.trustedTypes;if(xe)try{be=xe.createPolicy(`vue`,{createHTML:e=>e})}catch{}var Se=be?e=>be.createHTML(e):e=>e,Ce=`http://www.w3.org/2000/svg`,we=`http://www.w3.org/1998/Math/MathML`,V=typeof document<`u`?document:null,Te=V&&V.createElement(`template`),Ee={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{let t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,r)=>{let i=t===`svg`?V.createElementNS(Ce,e):t===`mathml`?V.createElementNS(we,e):n?V.createElement(e,{is:n}):V.createElement(e);return e===`select`&&r&&r.multiple!=null&&i.setAttribute(`multiple`,r.multiple),i},createText:e=>V.createTextNode(e),createComment:e=>V.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>V.querySelector(e),setScopeId(e,t){e.setAttribute(t,``)},insertStaticContent(e,t,n,r,i,a){let o=n?n.previousSibling:t.lastChild;if(i&&(i===a||i.nextSibling))for(;t.insertBefore(i.cloneNode(!0),n),!(i===a||!(i=i.nextSibling)););else{Te.innerHTML=Se(r===`svg`?`${e}`:r===`mathml`?`${e}`:e);let i=Te.content;if(r===`svg`||r===`mathml`){let e=i.firstChild;for(;e.firstChild;)i.appendChild(e.firstChild);i.removeChild(e)}t.insertBefore(i,n)}return[o?o.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}},H=`transition`,De=`animation`,Oe=Symbol(`_vtc`),ke={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},Ae=T({},ae,ke),je=(e=>(e.displayName=`Transition`,e.props=Ae,e))((e,{slots:t})=>he(fe,Pe(e),t)),Me=(e,t=[])=>{j(e)?e.forEach(e=>e(...t)):e&&e(...t)},Ne=e=>e?j(e)?e.some(e=>e.length>1):e.length>1:!1;function Pe(e){let t={};for(let n in e)n in ke||(t[n]=e[n]);if(e.css===!1)return t;let{name:n=`v`,type:r,duration:i,enterFromClass:a=`${n}-enter-from`,enterActiveClass:o=`${n}-enter-active`,enterToClass:s=`${n}-enter-to`,appearFromClass:c=a,appearActiveClass:l=o,appearToClass:u=s,leaveFromClass:d=`${n}-leave-from`,leaveActiveClass:f=`${n}-leave-active`,leaveToClass:p=`${n}-leave-to`}=e,m=Fe(i),h=m&&m[0],g=m&&m[1],{onBeforeEnter:_,onEnter:v,onEnterCancelled:y,onLeave:b,onLeaveCancelled:x,onBeforeAppear:S=_,onAppear:C=v,onAppearCancelled:w=y}=t,E=(e,t,n,r)=>{e._enterCancelled=r,Le(e,t?u:s),Le(e,t?l:o),n&&n()},D=(e,t)=>{e._isLeaving=!1,Le(e,d),Le(e,p),Le(e,f),t&&t()},O=e=>(t,n)=>{let i=e?C:v,o=()=>E(t,e,n);Me(i,[t,o]),Re(()=>{Le(t,e?c:a),U(t,e?u:s),Ne(i)||Be(t,r,h,o)})};return T(t,{onBeforeEnter(e){Me(_,[e]),U(e,a),U(e,o)},onBeforeAppear(e){Me(S,[e]),U(e,c),U(e,l)},onEnter:O(!1),onAppear:O(!0),onLeave(e,t){e._isLeaving=!0;let n=()=>D(e,t);U(e,d),e._enterCancelled?(U(e,f),We(e)):(We(e),U(e,f)),Re(()=>{e._isLeaving&&(Le(e,d),U(e,p),Ne(b)||Be(e,r,g,n))}),Me(b,[e,n])},onEnterCancelled(e){E(e,!1,void 0,!0),Me(y,[e])},onAppearCancelled(e){E(e,!0,void 0,!0),Me(w,[e])},onLeaveCancelled(e){D(e),Me(x,[e])}})}function Fe(e){if(e==null)return null;if(de(e))return[Ie(e.enter),Ie(e.leave)];{let t=Ie(e);return[t,t]}}function Ie(e){return ge(e)}function U(e,t){t.split(/\s+/).forEach(t=>t&&e.classList.add(t)),(e[Oe]||(e[Oe]=new Set)).add(t)}function Le(e,t){t.split(/\s+/).forEach(t=>t&&e.classList.remove(t));let n=e[Oe];n&&(n.delete(t),n.size||(e[Oe]=void 0))}function Re(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}var ze=0;function Be(e,t,n,r){let i=e._endId=++ze,a=()=>{i===e._endId&&r()};if(n!=null)return setTimeout(a,n);let{type:o,timeout:s,propCount:c}=Ve(e,t);if(!o)return r();let l=o+`end`,u=0,d=()=>{e.removeEventListener(l,f),a()},f=t=>{t.target===e&&++u>=c&&d()};setTimeout(()=>{u(n[e]||``).split(`, `),i=r(`${H}Delay`),a=r(`${H}Duration`),o=He(i,a),s=r(`${De}Delay`),c=r(`${De}Duration`),l=He(s,c),u=null,d=0,f=0;t===H?o>0&&(u=H,d=o,f=a.length):t===De?l>0&&(u=De,d=l,f=c.length):(d=Math.max(o,l),u=d>0?o>l?H:De:null,f=u?u===H?a.length:c.length:0);let p=u===H&&/\b(?:transform|all)(?:,|$)/.test(r(`${H}Property`).toString());return{type:u,timeout:d,propCount:f,hasTransform:p}}function He(e,t){for(;e.lengthUe(t)+Ue(e[n])))}function Ue(e){return e===`auto`?0:Number(e.slice(0,-1).replace(`,`,`.`))*1e3}function We(e){return(e?e.ownerDocument:document).body.offsetHeight}function Ge(e,t,n){let r=e[Oe];r&&(t=(t?[t,...r]:[...r]).join(` `)),t==null?e.removeAttribute(`class`):n?e.setAttribute(`class`,t):e.className=t}var Ke=Symbol(`_vod`),qe=Symbol(`_vsh`),Je={name:`show`,beforeMount(e,{value:t},{transition:n}){e[Ke]=e.style.display===`none`?``:e.style.display,n&&t?n.beforeEnter(e):Ye(e,t)},mounted(e,{value:t},{transition:n}){n&&t&&n.enter(e)},updated(e,{value:t,oldValue:n},{transition:r}){!t!=!n&&(r?t?(r.beforeEnter(e),Ye(e,!0),r.enter(e)):r.leave(e,()=>{Ye(e,!1)}):Ye(e,t))},beforeUnmount(e,{value:t}){Ye(e,t)}};function Ye(e,t){e.style.display=t?e[Ke]:`none`,e[qe]=!t}var Xe=Symbol(``),Ze=/(?:^|;)\s*display\s*:/;function Qe(e,t,n){let r=e.style,i=w(n),a=!1;if(n&&!i){if(t)if(w(t))for(let e of t.split(`;`)){let t=e.slice(0,e.indexOf(`:`)).trim();n[t]??et(r,t,``)}else for(let e in t)n[e]??et(r,e,``);for(let i in n){i===`display`&&(a=!0);let o=n[i];o==null?et(r,i,``):it(e,i,!w(t)&&t?t[i]:void 0,o)||et(r,i,o)}}else if(i){if(t!==n){let e=r[Xe];e&&(n+=`;`+e),r.cssText=n,a=Ze.test(n)}}else t&&e.removeAttribute(`style`);Ke in e&&(e[Ke]=a?r.display:``,e[qe]&&(r.display=`none`))}var $e=/\s*!important$/;function et(e,t,n){if(j(n))n.forEach(n=>et(e,t,n));else if(n??=``,t.startsWith(`--`))e.setProperty(t,n);else{let r=rt(e,t);$e.test(n)?e.setProperty(pe(r),n.replace($e,``),`important`):e[r]=n}}var tt=[`Webkit`,`Moz`,`ms`],nt={};function rt(e,n){let r=nt[n];if(r)return r;let i=f(n);if(i!==`filter`&&i in e)return nt[n]=i;i=t(i);for(let t=0;tmt||=(ht.then(()=>mt=0),Date.now());function _t(e,t){let n=e=>{if(!e._vts)e._vts=Date.now();else if(e._vts<=n.attached)return;let r=n.value;if(j(r)){let n=e.stopImmediatePropagation;e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0};let i=r.slice(),a=[e];for(let n=0;ne.charCodeAt(0)===111&&e.charCodeAt(1)===110&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,yt=(e,t,n,r,i,a)=>{let o=i===`svg`;t===`class`?Ge(e,r,o):t===`style`?Qe(e,n,r):S(t)?ce(t)||ut(e,t,n,r,a):(t[0]===`.`?(t=t.slice(1),!0):t[0]===`^`?(t=t.slice(1),!1):bt(e,t,r,o))?(st(e,t,r),!e.tagName.includes(`-`)&&(t===`value`||t===`checked`||t===`selected`)&&ot(e,t,r,o,a,t!==`value`)):e._isVueCE&&(xt(e,t)||e._def.__asyncLoader&&(/[A-Z]/.test(t)||!w(r)))?st(e,f(t),r,a,t):(t===`true-value`?e._trueValue=r:t===`false-value`&&(e._falseValue=r),ot(e,t,r,o))};function bt(e,t,n,r){if(r)return!!(t===`innerHTML`||t===`textContent`||t in e&&vt(t)&&y(n));if(t===`spellcheck`||t===`draggable`||t===`translate`||t===`autocorrect`||t===`sandbox`&&e.tagName===`IFRAME`||t===`form`||t===`list`&&e.tagName===`INPUT`||t===`type`&&e.tagName===`TEXTAREA`)return!1;if(t===`width`||t===`height`){let t=e.tagName;if(t===`IMG`||t===`VIDEO`||t===`CANVAS`||t===`SOURCE`)return!1}return vt(t)&&w(n)?!1:t in e}function xt(e,t){let n=e._def.props;if(!n)return!1;let r=f(t);return Array.isArray(n)?n.some(e=>f(e)===r):Object.keys(n).some(e=>f(e)===r)}var G=e=>{let t=e.props[`onUpdate:modelValue`]||!1;return j(t)?e=>ue(t,e):t};function St(e){e.target.composing=!0}function Ct(e){let t=e.target;t.composing&&(t.composing=!1,t.dispatchEvent(new Event(`input`)))}var K=Symbol(`_assign`);function wt(e,t,n){return t&&(e=e.trim()),n&&(e=A(e)),e}var Tt={created(e,{modifiers:{lazy:t,trim:n,number:r}},i){e[K]=G(i);let a=r||i.props&&i.props.type===`number`;W(e,t?`change`:`input`,t=>{t.target.composing||e[K](wt(e.value,n,a))}),(n||a)&&W(e,`change`,()=>{e.value=wt(e.value,n,a)}),t||(W(e,`compositionstart`,St),W(e,`compositionend`,Ct),W(e,`change`,Ct))},mounted(e,{value:t}){e.value=t??``},beforeUpdate(e,{value:t,oldValue:n,modifiers:{lazy:r,trim:i,number:a}},o){if(e[K]=G(o),e.composing)return;let s=(a||e.type===`number`)&&!/^0\d/.test(e.value)?A(e.value):e.value,c=t??``;if(s===c)return;let l=e.getRootNode();(l instanceof Document||l instanceof ShadowRoot)&&l.activeElement===e&&e.type!==`range`&&(r&&t===n||i&&e.value.trim()===c)||(e.value=c)}},Et={deep:!0,created(e,t,n){e[K]=G(n),W(e,`change`,()=>{let t=e._modelValue,n=jt(e),r=e.checked,i=e[K];if(j(t)){let e=ie(t,n),a=e!==-1;if(r&&!a)i(t.concat(n));else if(!r&&a){let n=[...t];n.splice(e,1),i(n)}}else if(ne(t)){let e=new Set(t);r?e.add(n):e.delete(n),i(e)}else i(Mt(e,r))})},mounted:Dt,beforeUpdate(e,t,n){e[K]=G(n),Dt(e,t,n)}};function Dt(e,{value:t,oldValue:n},r){e._modelValue=t;let i;if(j(t))i=ie(t,r.props.value)>-1;else if(ne(t))i=t.has(r.props.value);else{if(t===n)return;i=P(t,Mt(e,!0))}e.checked!==i&&(e.checked=i)}var Ot={created(e,{value:t},n){e.checked=P(t,n.props.value),e[K]=G(n),W(e,`change`,()=>{e[K](jt(e))})},beforeUpdate(e,{value:t,oldValue:n},r){e[K]=G(r),t!==n&&(e.checked=P(t,r.props.value))}},kt={deep:!0,created(e,{value:t,modifiers:{number:n}},r){e._modelValue=t,W(e,`change`,()=>{let t=Array.prototype.filter.call(e.options,e=>e.selected).map(e=>n?A(jt(e)):jt(e));e[K](e.multiple?ne(e._modelValue)?new Set(t):t:t[0]),e._assigning=!0,z(()=>{e._assigning=!1})}),e[K]=G(r)},mounted(e,{value:t}){At(e,t)},beforeUpdate(e,{value:t},n){e._modelValue=t,e[K]=G(n)},updated(e,{value:t}){e._assigning||At(e,t)}};function At(e,t){let n=e.multiple,r=j(t);if(!(n&&!r&&!ne(t))){for(let i=0,a=e.options.length;iString(e)===String(o)):ie(t,o)>-1}else a.selected=t.has(o);else if(P(jt(a),t)){e.selectedIndex!==i&&(e.selectedIndex=i);return}}!n&&e.selectedIndex!==-1&&(e.selectedIndex=-1)}}function jt(e){return`_value`in e?e._value:e.value}function Mt(e,t){let n=t?`_trueValue`:`_falseValue`;return n in e?e[n]:t}var Nt={created(e,t,n){Ft(e,t,n,null,`created`)},mounted(e,t,n){Ft(e,t,n,null,`mounted`)},beforeUpdate(e,t,n,r){Ft(e,t,n,r,`beforeUpdate`)},updated(e,t,n,r){Ft(e,t,n,r,`updated`)}};function Pt(e,t){switch(e){case`SELECT`:return kt;case`TEXTAREA`:return Tt;default:switch(t){case`checkbox`:return Et;case`radio`:return Ot;default:return Tt}}}function Ft(e,t,n,r,i){let a=Pt(e.tagName,n.props&&n.props.type)[i];a&&a(e,t,n,r)}var It=[`ctrl`,`shift`,`alt`,`meta`],Lt={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>`button`in e&&e.button!==0,middle:e=>`button`in e&&e.button!==1,right:e=>`button`in e&&e.button!==2,exact:(e,t)=>It.some(n=>e[`${n}Key`]&&!t.includes(n))},Rt=(e,t)=>{if(!e)return e;let n=e._withMods||={},r=t.join(`.`);return n[r]||(n[r]=((n,...r)=>{for(let e=0;e{let n=e._withKeys||={},r=t.join(`.`);return n[r]||(n[r]=(n=>{if(!(`key`in n))return;let r=pe(n.key);if(t.some(e=>e===r||zt[e]===r))return e(n)}))},Vt=T({patchProp:yt},Ee),Ht;function Ut(){return Ht||=E(Vt)}var Wt=((...e)=>{let t=Ut().createApp(...e),{mount:n}=t;return t.mount=e=>{let r=Kt(e);if(!r)return;let i=t._component;!y(i)&&!i.render&&!i.template&&(i.template=r.innerHTML),r.nodeType===1&&(r.textContent=``);let a=n(r,!1,Gt(r));return r instanceof Element&&(r.removeAttribute(`v-cloak`),r.setAttribute(`data-v-app`,``)),a},t});function Gt(e){if(e instanceof SVGElement)return`svg`;if(typeof MathMLElement==`function`&&e instanceof MathMLElement)return`mathml`}function Kt(e){return w(e)?document.querySelector(e):e}var qt=e=>e===``,Jt=(...e)=>e.filter((e,t,n)=>!!e&&e.trim()!==``&&n.indexOf(e)===t).join(` `).trim(),Yt=e=>e.replace(/([a-z0-9])([A-Z])/g,`$1-$2`).toLowerCase(),Xt=e=>e.replace(/^([A-Z])|[\s-_]+(\w)/g,(e,t,n)=>n?n.toUpperCase():t.toLowerCase()),Zt=e=>{let t=Xt(e);return t.charAt(0).toUpperCase()+t.slice(1)},Qt={xmlns:`http://www.w3.org/2000/svg`,width:24,height:24,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":2,"stroke-linecap":`round`,"stroke-linejoin":`round`},$t=Symbol(`lucide-icons`);function en(){return b($t,{})}var tn=({name:e,iconNode:t,"icon-node":n,absoluteStrokeWidth:r,"absolute-stroke-width":i,strokeWidth:a,"stroke-width":o,size:s,color:c,...l},{slots:u})=>{let{size:d,color:f,strokeWidth:p=2,absoluteStrokeWidth:m=!1,class:h=``}=en(),g=I(()=>{let e=qt(r)||qt(i)||r===!0||i===!0||m===!0,t=a||o||p||Qt[`stroke-width`];return e?Number(t)*24/Number(s??d??Qt.width):t});return he(`svg`,{...Qt,...l,width:s??d??Qt.width,height:s??d??Qt.height,stroke:c??f??Qt.stroke,"stroke-width":g.value,class:Jt(`lucide`,h,...e?[`lucide-${Yt(Zt(e))}-icon`,`lucide-${Yt(e)}`]:[`lucide-icon`])},[...(t??n??[]).map(e=>he(...e)),...u.default?[u.default()]:[]])},q=(e,t)=>(n,{slots:r,attrs:i})=>he(tn,{...i,...n,iconNode:t,name:e},r.default?{default:r.default}:void 0),nn=q(`activity`,[[`path`,{d:`M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2`,key:`169zse`}]]),rn=q(`boxes`,[[`path`,{d:`M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z`,key:`lc1i9w`}],[`path`,{d:`m7 16.5-4.74-2.85`,key:`1o9zyk`}],[`path`,{d:`m7 16.5 5-3`,key:`va8pkn`}],[`path`,{d:`M7 16.5v5.17`,key:`jnp8gn`}],[`path`,{d:`M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z`,key:`8zsnat`}],[`path`,{d:`m17 16.5-5-3`,key:`8arw3v`}],[`path`,{d:`m17 16.5 4.74-2.85`,key:`8rfmw`}],[`path`,{d:`M17 16.5v5.17`,key:`k6z78m`}],[`path`,{d:`M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z`,key:`1xygjf`}],[`path`,{d:`M12 8 7.26 5.15`,key:`1vbdud`}],[`path`,{d:`m12 8 4.74-2.85`,key:`3rx089`}],[`path`,{d:`M12 13.5V8`,key:`1io7kd`}]]),an=q(`calendar-clock`,[[`path`,{d:`M16 14v2.2l1.6 1`,key:`fo4ql5`}],[`path`,{d:`M16 2v3`,key:`otl347`}],[`path`,{d:`M21 7.338V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h2.338`,key:`7hb8p4`}],[`path`,{d:`M3 9h5.859`,key:`numkqi`}],[`path`,{d:`M8 2v3`,key:`1ioesn`}],[`circle`,{cx:`16`,cy:`16`,r:`6`,key:`qoo3c4`}]]),on=q(`chevron-down`,[[`path`,{d:`m6 9 6 6 6-6`,key:`qrunsl`}]]),sn=q(`circle-question-mark`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3`,key:`1u773s`}],[`path`,{d:`M12 17h.01`,key:`p32p05`}]]),cn=q(`fingerprint-pattern`,[[`path`,{d:`M12 10a2 2 0 0 0-2 2c0 1.02-.1 2.51-.26 4`,key:`1nerag`}],[`path`,{d:`M14 13.12c0 2.38 0 6.38-1 8.88`,key:`o46ks0`}],[`path`,{d:`M17.29 21.02c.12-.6.43-2.3.5-3.02`,key:`ptglia`}],[`path`,{d:`M2 12a10 10 0 0 1 18-6`,key:`ydlgp0`}],[`path`,{d:`M2 16h.01`,key:`1gqxmh`}],[`path`,{d:`M21.8 16c.2-2 .131-5.354 0-6`,key:`drycrb`}],[`path`,{d:`M5 19.5C5.5 18 6 15 6 12a6 6 0 0 1 .34-2`,key:`1tidbn`}],[`path`,{d:`M8.65 22c.21-.66.45-1.32.57-2`,key:`13wd9y`}],[`path`,{d:`M9 6.8a6 6 0 0 1 9 5.2v2`,key:`1fr1j5`}]]),ln=q(`gauge`,[[`path`,{d:`m12 14 4-4`,key:`9kzdfg`}],[`path`,{d:`M3.34 19a10 10 0 1 1 17.32 0`,key:`19p75a`}]]),un=q(`library-big`,[[`rect`,{width:`8`,height:`18`,x:`3`,y:`3`,rx:`1`,key:`oynpb5`}],[`path`,{d:`M7 3v18`,key:`bbkbws`}],[`path`,{d:`M20.4 18.9c.2.5-.1 1.1-.6 1.3l-1.9.7c-.5.2-1.1-.1-1.3-.6L11.1 5.1c-.2-.5.1-1.1.6-1.3l1.9-.7c.5-.2 1.1.1 1.3.6Z`,key:`1qboyk`}]]),dn=q(`list-checks`,[[`path`,{d:`M13 5h8`,key:`a7qcls`}],[`path`,{d:`M13 12h8`,key:`h98zly`}],[`path`,{d:`M13 19h8`,key:`c3s6r1`}],[`path`,{d:`m3 17 2 2 4-4`,key:`1jhpwq`}],[`path`,{d:`m3 7 2 2 4-4`,key:`1obspn`}]]),fn=q(`list-tree`,[[`path`,{d:`M8 5h13`,key:`1pao27`}],[`path`,{d:`M13 12h8`,key:`h98zly`}],[`path`,{d:`M13 19h8`,key:`c3s6r1`}],[`path`,{d:`M3 10a2 2 0 0 0 2 2h3`,key:`1npucw`}],[`path`,{d:`M3 5v12a2 2 0 0 0 2 2h3`,key:`x1gjn2`}]]),pn=q(`menu`,[[`path`,{d:`M4 5h16`,key:`1tepv9`}],[`path`,{d:`M4 12h16`,key:`1lakjw`}],[`path`,{d:`M4 19h16`,key:`1djgab`}]]),mn=q(`messages-square`,[[`path`,{d:`M16 10a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 14.286V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z`,key:`1n2ejm`}],[`path`,{d:`M20 9a2 2 0 0 1 2 2v10.286a.71.71 0 0 1-1.212.502l-2.202-2.202A2 2 0 0 0 17.172 19H10a2 2 0 0 1-2-2v-1`,key:`1qfcsi`}]]),hn=q(`network`,[[`rect`,{x:`16`,y:`16`,width:`6`,height:`6`,rx:`1`,key:`4q2zg0`}],[`rect`,{x:`2`,y:`16`,width:`6`,height:`6`,rx:`1`,key:`8cvhb9`}],[`rect`,{x:`9`,y:`2`,width:`6`,height:`6`,rx:`1`,key:`1egb70`}],[`path`,{d:`M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3`,key:`1jsf9p`}],[`path`,{d:`M12 12V8`,key:`2874zd`}]]),gn=q(`plug`,[[`path`,{d:`M12 22v-5`,key:`1ega77`}],[`path`,{d:`M15 8V2`,key:`18g5xt`}],[`path`,{d:`M17 8a1 1 0 0 1 1 1v4a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1z`,key:`1xoxul`}],[`path`,{d:`M9 8V2`,key:`14iosj`}]]),_n=q(`plus`,[[`path`,{d:`M5 12h14`,key:`1ays0h`}],[`path`,{d:`M12 5v14`,key:`s699le`}]]),vn=q(`search`,[[`path`,{d:`m21 21-4.34-4.34`,key:`14j7rj`}],[`circle`,{cx:`11`,cy:`11`,r:`8`,key:`4ej97u`}]]),yn=q(`settings`,[[`path`,{d:`M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915`,key:`1i5ecw`}],[`circle`,{cx:`12`,cy:`12`,r:`3`,key:`1v7zrd`}]]),bn=q(`shield-half`,[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}],[`path`,{d:`M12 22V2`,key:`zs6s6o`}]]),xn=q(`triangle-alert`,[[`path`,{d:`m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3`,key:`wmoenq`}],[`path`,{d:`M12 9v4`,key:`juzpu7`}],[`path`,{d:`M12 17h.01`,key:`p32p05`}]]),Sn=q(`webhook`,[[`path`,{d:`M18 16.98h-5.99c-1.1 0-1.95.94-2.48 1.9A4 4 0 0 1 2 17c.01-.7.2-1.4.57-2`,key:`q3hayz`}],[`path`,{d:`m6 17 3.13-5.78c.53-.97.1-2.18-.5-3.1a4 4 0 1 1 6.89-4.06`,key:`1go1hn`}],[`path`,{d:`m12 6 3.13 5.73C15.66 12.7 16.9 13 18 13a4 4 0 0 1 0 8`,key:`qlwsc0`}]]),Cn=q(`x`,[[`path`,{d:`M18 6 6 18`,key:`1bl5f8`}],[`path`,{d:`m6 6 12 12`,key:`d8bk6v`}]]),wn=[`disabled`],Tn={key:0,class:`animate-spin -ml-1 mr-2 h-4 w-4`,xmlns:`http://www.w3.org/2000/svg`,fill:`none`,viewBox:`0 0 24 24`},En={__name:`Button`,props:{variant:{type:String,default:`primary`,validator:e=>[`primary`,`secondary`,`danger`,`ghost`,`chip`].includes(e)},size:{type:String,default:`md`,validator:e=>[`xs`,`sm`,`md`,`lg`].includes(e)},active:{type:Boolean,default:!1},disabled:Boolean,loading:Boolean},setup(e){let t=e,i=I(()=>{switch(t.size){case`xs`:return`h-7 px-2.5 text-xs touch-expand-xs`;case`sm`:return`h-8 px-3 text-xs touch-expand-sm`;case`lg`:return`h-12 px-6 text-base`;default:return`h-10 px-4 text-sm`}}),a=I(()=>{switch(t.variant){case`secondary`:return`bg-secondary text-secondary-foreground hover:bg-secondary/80 border border-border shadow-sm`;case`danger`:return`bg-danger text-foreground-strong border border-danger hover:bg-danger-fg focus-visible:ring-danger shadow-sm`;case`ghost`:return`bg-transparent text-foreground-muted hover:text-foreground hover:bg-surface-hover`;case`chip`:return t.active?`bg-primary text-primary-foreground border border-primary`:`bg-surface text-foreground-muted border border-border hover:text-white hover:border-foreground-muted`;default:return`bg-primary text-primary-foreground hover:bg-primary/90 focus-visible:ring-primary shadow-sm border border-transparent`}});return(t,o)=>(r(),C(`button`,{class:O([`inline-flex items-center justify-center gap-2 rounded-md font-medium transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:opacity-50 disabled:cursor-not-allowed`,i.value,a.value]),disabled:e.disabled||e.loading},[e.loading?(r(),C(`svg`,Tn,[...o[0]||=[x(`circle`,{class:`opacity-25`,cx:`12`,cy:`12`,r:`10`,stroke:`currentColor`,"stroke-width":`4`},null,-1),x(`path`,{class:`opacity-75`,fill:`currentColor`,d:`M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z`},null,-1)]])):L(``,!0),n(t.$slots,`default`)],10,wn))}},Dn=_e(`confirm`,()=>{let e=o(!1),t=o(``),n=o(``),r=o(`Confirm`),i=o(`Cancel`),a=o(!1),s=o(!1),c=o(!1),l=o(``),u=o(``),d=null;return{visible:e,title:t,message:n,confirmLabel:r,cancelLabel:i,danger:a,noticeOnly:s,promptMode:c,promptValue:l,promptPlaceholder:u,ask:(o={})=>(t.value=o.title||`Are you sure?`,n.value=o.message||``,r.value=o.confirmLabel||`Confirm`,i.value=o.cancelLabel||`Cancel`,a.value=!!o.danger,s.value=!1,c.value=!1,e.value=!0,new Promise(e=>{d=e})),notify:(i={})=>(t.value=i.title||`Notice`,n.value=i.message||``,r.value=i.confirmLabel||`OK`,a.value=!!i.danger,s.value=!0,c.value=!1,e.value=!0,new Promise(e=>{d=e})),prompt:(o={})=>(t.value=o.title||`Enter a value`,n.value=o.message||``,r.value=o.confirmLabel||`OK`,i.value=o.cancelLabel||`Cancel`,a.value=!!o.danger,s.value=!1,c.value=!0,l.value=o.defaultValue||``,u.value=o.placeholder||``,e.value=!0,new Promise(e=>{d=e})),settle:t=>{e.value=!1,d&&=(c.value?d(t?l.value:null):d(t),null),c.value=!1}}}),On=[`a[href]`,`button:not([disabled])`,`input:not([disabled]):not([type="hidden"])`,`textarea:not([disabled])`,`select:not([disabled])`,`[tabindex]:not([tabindex="-1"])`,`audio[controls]`,`video[controls]`,`details > summary`].join(`,`);function kn(e,t){let n=null,r=null,i=null,a=async()=>{n=document.activeElement instanceof HTMLElement?document.activeElement:null,i=document.getElementById(`app`),i&&i.setAttribute(`inert`,``),await z();let t=e.value;t&&((t.querySelector(`[autofocus]`)||t.querySelector(On))?.focus?.(),r=e=>{if(e.key!==`Tab`)return;let n=Array.from(t.querySelectorAll(On)).filter(e=>!e.hasAttribute(`inert`)&&e.offsetParent!==null);if(!n.length){e.preventDefault();return}let r=n[0],i=n[n.length-1],a=document.activeElement;e.shiftKey&&a===r?(e.preventDefault(),i.focus()):!e.shiftKey&&a===i&&(e.preventDefault(),r.focus())},document.addEventListener(`keydown`,r))},o=()=>{r&&=(document.removeEventListener(`keydown`,r),null),i&&=(i.removeAttribute(`inert`),null),n&&n.isConnected&&typeof n.focus==`function`&&n.focus(),n=null};d(t,e=>{e?a():o()},{immediate:!1})}var An={class:`flex items-start gap-3`},jn={class:`flex-1 min-w-0`},Mn={key:0,class:`text-sm text-foreground-muted mt-1 whitespace-pre-line break-words`},Nn=[`placeholder`],Pn={class:`flex flex-col-reverse sm:flex-row sm:justify-end gap-2 pt-2`},Fn=`confirm-dialog-title`,In={__name:`ConfirmDialog`,setup(e){let t=Dn(),n=o(null),c=o(null);kn(c,I(()=>t.visible)),d(()=>t.visible&&t.promptMode,e=>{e&&z(()=>{let e=n.value;e&&(e.focus(),window.innerWidth<640&&setTimeout(()=>{e.scrollIntoView({block:`center`,behavior:`smooth`})},50))})});let l=e=>{t.visible&&(e.key===`Escape`&&t.settle(!1),e.key===`Enter`&&!t.promptMode&&t.settle(!0))};return p(()=>window.addEventListener(`keydown`,l)),i(()=>window.removeEventListener(`keydown`,l)),(e,i)=>(r(),N(v,{to:`body`},[k(je,{name:`fade`},{default:a(()=>[g(t).visible?(r(),C(`div`,{key:0,class:`fixed inset-0 z-50 flex items-end sm:items-center justify-center bg-black/60 backdrop-blur-sm pt-safe pb-safe pl-safe pr-safe p-2 sm:p-4`,onClick:i[4]||=Rt(e=>g(t).settle(!1),[`self`]),onKeydown:i[5]||=Bt(e=>g(t).settle(!1),[`esc`])},[x(`div`,{ref_key:`dialogRoot`,ref:c,class:`w-full sm:max-w-md bg-background border border-border rounded-t-lg sm:rounded-lg shadow-xl p-5 sm:p-6 space-y-4 max-h-[calc(100dvh-1rem)] overflow-y-auto scrollable`,role:`dialog`,"aria-modal":`true`,"aria-labelledby":Fn},[x(`div`,An,[g(t).promptMode?L(``,!0):(r(),C(`div`,{key:0,class:O([`shrink-0 w-9 h-9 rounded-full flex items-center justify-center`,g(t).danger?`bg-red-500/15 text-red-400`:`bg-primary/15 text-primary`])},[g(t).danger?(r(),N(g(xn),{key:0,class:`w-5 h-5`})):(r(),N(g(sn),{key:1,class:`w-5 h-5`}))],2)),x(`div`,jn,[x(`h3`,{id:Fn,class:`text-sm font-semibold text-white tracking-tight`},R(g(t).title),1),g(t).message?(r(),C(`p`,Mn,R(g(t).message),1)):L(``,!0),g(t).promptMode?s((r(),C(`input`,{key:1,ref_key:`promptInput`,ref:n,"onUpdate:modelValue":i[0]||=e=>g(t).promptValue=e,placeholder:g(t).promptPlaceholder,type:`text`,class:`mt-3 w-full bg-background border border-border rounded-md px-3 py-2 text-base sm:text-sm text-foreground placeholder-foreground-muted/50 transition-colors duration-200 focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,onKeydown:i[1]||=Bt(Rt(e=>g(t).settle(!0),[`stop`,`prevent`]),[`enter`])},null,40,Nn)),[[Tt,g(t).promptValue]]):L(``,!0)])]),x(`div`,Pn,[g(t).noticeOnly?L(``,!0):(r(),N(En,{key:0,variant:`secondary`,class:`w-full sm:w-auto`,onClick:i[2]||=e=>g(t).settle(!1)},{default:a(()=>[re(R(g(t).cancelLabel),1)]),_:1})),k(En,{variant:g(t).danger?`danger`:`primary`,class:`w-full sm:w-auto`,onClick:i[3]||=e=>g(t).settle(!0)},{default:a(()=>[re(R(g(t).confirmLabel),1)]),_:1},8,[`variant`])])],512)],32)):L(``,!0)]),_:1})]))}},Ln={__name:`App`,setup(e){return(e,t)=>{let n=te(`router-view`);return r(),C(F,null,[k(n),k(In)],64)}}};function Rn(e){return typeof e==`object`||`displayName`in e||`props`in e||`__vccOpts`in e}function zn(e){return e.__esModule||e[Symbol.toStringTag]===`Module`||e.default&&Rn(e.default)}var J=Object.assign;function Bn(e,t){let n={};for(let r in t){let i=t[r];n[r]=Y(i)?i.map(e):e(i)}return n}var Vn=()=>{},Y=Array.isArray;function Hn(e,t){let n={};for(let r in e)n[r]=r in t?t[r]:e[r];return n}var Un=Symbol(``);function Wn(e,t){return J(Error(),{type:e,[Un]:!0},t)}function X(e,t){return e instanceof Error&&Un in e&&(t==null||!!(e.type&t))}var Gn=Symbol(``),Kn=Symbol(``),qn=Symbol(``),Jn=Symbol(``),Yn=Symbol(``);function Xn(){return b(qn)}function Zn(e){return b(Jn)}var Qn=typeof document<`u`,$n=/#/g,er=/&/g,tr=/\//g,nr=/=/g,rr=/\?/g,ir=/\+/g,ar=/%5B/g,or=/%5D/g,sr=/%5E/g,cr=/%60/g,lr=/%7B/g,ur=/%7C/g,dr=/%7D/g,fr=/%20/g;function pr(e){return e==null?``:encodeURI(``+e).replace(ur,`|`).replace(ar,`[`).replace(or,`]`)}function mr(e){return pr(e).replace(lr,`{`).replace(dr,`}`).replace(sr,`^`)}function hr(e){return pr(e).replace(ir,`%2B`).replace(fr,`+`).replace($n,`%23`).replace(er,`%26`).replace(cr,"`").replace(lr,`{`).replace(dr,`}`).replace(sr,`^`)}function gr(e){return hr(e).replace(nr,`%3D`)}function _r(e){return pr(e).replace($n,`%23`).replace(rr,`%3F`)}function vr(e){return _r(e).replace(tr,`%2F`)}function yr(e){if(e==null)return null;try{return decodeURIComponent(``+e)}catch{}return``+e}var br=/\/$/,xr=e=>e.replace(br,``);function Sr(e,t,n=`/`){let r,i={},a=``,o=``,s=t.indexOf(`#`),c=t.indexOf(`?`);return c=s>=0&&c>s?-1:c,c>=0&&(r=t.slice(0,c),a=t.slice(c,s>0?s:t.length),i=e(a.slice(1))),s>=0&&(r||=t.slice(0,s),o=t.slice(s,t.length)),r=Ar(r??t,n),{fullPath:r+a+o,path:r,query:i,hash:yr(o)}}function Cr(e,t){let n=t.query?e(t.query):``;return t.path+(n&&`?`)+n+(t.hash||``)}function wr(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||`/`}function Tr(e,t,n){let r=t.matched.length-1,i=n.matched.length-1;return r>-1&&r===i&&Er(t.matched[r],n.matched[i])&&Dr(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function Er(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function Dr(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(var n in e)if(!Or(e[n],t[n]))return!1;return!0}function Or(e,t){return Y(e)?kr(e,t):Y(t)?kr(t,e):(e&&e.valueOf())===(t&&t.valueOf())}function kr(e,t){return Y(t)?e.length===t.length&&e.every((e,n)=>e===t[n]):e.length===1&&e[0]===t}function Ar(e,t){if(e.startsWith(`/`))return e;if(!e)return t;let n=t.split(`/`),r=e.split(`/`),i=r[r.length-1];(i===`..`||i===`.`)&&r.push(``);let a=n.length-1,o,s;for(o=0;o1&&a--;else break;return n.slice(0,a).join(`/`)+`/`+r.slice(o).join(`/`)}var Z={path:`/`,name:void 0,params:{},query:{},hash:``,fullPath:`/`,matched:[],meta:{},redirectedFrom:void 0};function jr(e){if(!e)if(Qn){let t=document.querySelector(`base`);e=t&&t.getAttribute(`href`)||`/`,e=e.replace(/^\w+:\/\/[^/]+/,``)}else e=`/`;return e[0]!==`/`&&e[0]!==`#`&&(e=`/`+e),xr(e)}var Mr=/^[^#]+#/;function Nr(e,t){return e.replace(Mr,`#`)+t}function Pr(e,t){let n=document.documentElement.getBoundingClientRect(),r=e.getBoundingClientRect();return{behavior:t.behavior,left:r.left-n.left-(t.left||0),top:r.top-n.top-(t.top||0)}}var Fr=()=>({left:window.scrollX,top:window.scrollY});function Ir(e){let t;if(`el`in e){let n=e.el,r=typeof n==`string`&&n.startsWith(`#`),i=typeof n==`string`?r?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!i)return;t=Pr(i,e)}else t=e;`scrollBehavior`in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left==null?window.scrollX:t.left,t.top==null?window.scrollY:t.top)}function Lr(e,t){return(history.state?history.state.position-t:-1)+e}var Rr=new Map;function zr(e,t){Rr.set(e,t)}function Br(e){let t=Rr.get(e);return Rr.delete(e),t}function Vr(e){return typeof e==`string`||e&&typeof e==`object`}function Hr(e){return typeof e==`string`||typeof e==`symbol`}function Ur(e){let t={};if(e===``||e===`?`)return t;let n=(e[0]===`?`?e.slice(1):e).split(`&`);for(let e=0;ee&&hr(e)):[r&&hr(r)]).forEach(e=>{e!==void 0&&(t+=(t.length?`&`:``)+n,e!=null&&(t+=`=`+e))})}return t}function Gr(e){let t={};for(let n in e){let r=e[n];r!==void 0&&(t[n]=Y(r)?r.map(e=>e==null?null:``+e):r==null?r:``+r)}return t}function Kr(){let e=[];function t(t){return e.push(t),()=>{let n=e.indexOf(t);n>-1&&e.splice(n,1)}}function n(){e=[]}return{add:t,list:()=>e.slice(),reset:n}}function Q(e,t,n,r,i,a=e=>e()){let o=r&&(r.enterCallbacks[i]=r.enterCallbacks[i]||[]);return()=>new Promise((s,c)=>{let l=e=>{e===!1?c(Wn(4,{from:n,to:t})):e instanceof Error?c(e):Vr(e)?c(Wn(2,{from:t,to:e})):(o&&r.enterCallbacks[i]===o&&typeof e==`function`&&o.push(e),s())},u=a(()=>e.call(r&&r.instances[i],t,n,l)),d=Promise.resolve(u);e.length<3&&(d=d.then(l)),d.catch(e=>c(e))})}function qr(e,t,n,r,i=e=>e()){let a=[];for(let o of e)for(let e in o.components){let s=o.components[e];if(!(t!==`beforeRouteEnter`&&!o.instances[e]))if(Rn(s)){let c=(s.__vccOpts||s)[t];c&&a.push(Q(c,n,r,o,e,i))}else{let c=s();a.push(()=>c.then(a=>{if(!a)throw Error(`Couldn't resolve component "${e}" at "${o.path}"`);let s=zn(a)?a.default:a;o.mods[e]=a,o.components[e]=s;let c=(s.__vccOpts||s)[t];return c&&Q(c,n,r,o,e,i)()}))}}return a}function Jr(e,t){let n=[],r=[],i=[],a=Math.max(t.matched.length,e.matched.length);for(let o=0;oEr(e,a))?r.push(a):n.push(a));let s=e.matched[o];s&&(t.matched.find(e=>Er(e,s))||i.push(s))}return[n,r,i]}var Yr=()=>location.protocol+`//`+location.host;function Xr(e,t){let{pathname:n,search:r,hash:i}=t,a=e.indexOf(`#`);if(a>-1){let t=i.includes(e.slice(a))?e.slice(a).length:1,n=i.slice(t);return n[0]!==`/`&&(n=`/`+n),wr(n,``)}return wr(n,e)+r+i}function Zr(e,t,n,r){let i=[],a=[],o=null,s=({state:a})=>{let s=Xr(e,location),c=n.value,l=t.value,u=0;if(a){if(n.value=s,t.value=a,o&&o===c){o=null;return}u=l?a.position-l.position:0}else r(s);i.forEach(e=>{e(n.value,c,{delta:u,type:`pop`,direction:u?u>0?`forward`:`back`:``})})};function c(){o=n.value}function l(e){i.push(e);let t=()=>{let t=i.indexOf(e);t>-1&&i.splice(t,1)};return a.push(t),t}function u(){if(document.visibilityState===`hidden`){let{history:e}=window;if(!e.state)return;e.replaceState(J({},e.state,{scroll:Fr()}),``)}}function d(){for(let e of a)e();a=[],window.removeEventListener(`popstate`,s),window.removeEventListener(`pagehide`,u),document.removeEventListener(`visibilitychange`,u)}return window.addEventListener(`popstate`,s),window.addEventListener(`pagehide`,u),document.addEventListener(`visibilitychange`,u),{pauseListeners:c,listen:l,destroy:d}}function Qr(e,t,n,r=!1,i=!1){return{back:e,current:t,forward:n,replaced:r,position:window.history.length,scroll:i?Fr():null}}function $r(e){let{history:t,location:n}=window,r={value:Xr(e,n)},i={value:t.state};i.value||a(r.value,{back:null,current:r.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function a(r,a,o){let s=e.indexOf(`#`),c=s>-1?(n.host&&document.querySelector(`base`)?e:e.slice(s))+r:Yr()+e+r;try{t[o?`replaceState`:`pushState`](a,``,c),i.value=a}catch(e){console.error(e),n[o?`replace`:`assign`](c)}}function o(e,n){a(e,J({},t.state,Qr(i.value.back,e,i.value.forward,!0),n,{position:i.value.position}),!0),r.value=e}function s(e,n){let o=J({},i.value,t.state,{forward:e,scroll:Fr()});a(o.current,o,!0),a(e,J({},Qr(r.value,e,null),{position:o.position+1},n),!1),r.value=e}return{location:r,state:i,push:s,replace:o}}function ei(e){e=jr(e);let t=$r(e),n=Zr(e,t.state,t.location,t.replace);function r(e,t=!0){t||n.pauseListeners(),history.go(e)}let i=J({location:``,base:e,go:r,createHref:Nr.bind(null,e)},t,n);return Object.defineProperty(i,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(i,"state",{enumerable:!0,get:()=>t.state.value}),i}var ti={type:0,value:``},ni=/[a-zA-Z0-9_]/;function ri(e){if(!e)return[[]];if(e===`/`)return[[ti]];if(!e.startsWith(`/`))throw Error(`Invalid path "${e}"`);function t(e){throw Error(`ERR (${n})/"${l}": ${e}`)}let n=0,r=n,i=[],a;function o(){a&&i.push(a),a=[]}let s=0,c,l=``,u=``;function d(){l&&=(n===0?a.push({type:0,value:l}):n===1||n===2||n===3?(a.length>1&&(c===`*`||c===`+`)&&t(`A repeatable param (${l}) must be alone in its segment. eg: '/:ids+.`),a.push({type:1,value:l,regexp:u,repeatable:c===`*`||c===`+`,optional:c===`*`||c===`?`})):t(`Invalid state to consume buffer`),``)}function f(){l+=c}for(;st.length?t.length===1&&t[0]===80?1:-1:0}function li(e,t){let n=0,r=e.score,i=t.score;for(;n0&&t[t.length-1]<0}var di={strict:!1,end:!0,sensitive:!1};function fi(e,t,n){let r=J(si(ri(e.path),n),{record:e,parent:t,children:[],alias:[]});return t&&!r.record.aliasOf==!t.record.aliasOf&&t.children.push(r),r}function pi(e,t){let n=[],r=new Map;t=Hn(di,t);function i(e){return r.get(e)}function a(e,n,r){let i=!r,s=hi(e);s.aliasOf=r&&r.record;let l=Hn(t,e),u=[s];if(`alias`in e){let t=typeof e.alias==`string`?[e.alias]:e.alias;for(let e of t)u.push(hi(J({},s,{components:r?r.record.components:s.components,path:e,aliasOf:r?r.record:s})))}let d,f;for(let t of u){let{path:u}=t;if(n&&u[0]!==`/`){let e=n.record.path,r=e[e.length-1]===`/`?``:`/`;t.path=n.record.path+(u&&r+u)}if(d=fi(t,n,l),r?r.alias.push(d):(f||=d,f!==d&&f.alias.push(d),i&&e.name&&!_i(d)&&o(e.name)),xi(d)&&c(d),s.children){let e=s.children;for(let t=0;t{o(f)}:Vn}function o(e){if(Hr(e)){let t=r.get(e);t&&(r.delete(e),n.splice(n.indexOf(t),1),t.children.forEach(o),t.alias.forEach(o))}else{let t=n.indexOf(e);t>-1&&(n.splice(t,1),e.record.name&&r.delete(e.record.name),e.children.forEach(o),e.alias.forEach(o))}}function s(){return n}function c(e){let t=yi(e,n);n.splice(t,0,e),e.record.name&&!_i(e)&&r.set(e.record.name,e)}function l(e,t){let i,a={},o,s;if(`name`in e&&e.name){if(i=r.get(e.name),!i)throw Wn(1,{location:e});s=i.record.name,a=J(mi(t.params,i.keys.filter(e=>!e.optional).concat(i.parent?i.parent.keys.filter(e=>e.optional):[]).map(e=>e.name)),e.params&&mi(e.params,i.keys.map(e=>e.name))),o=i.stringify(a)}else if(e.path!=null)o=e.path,i=n.find(e=>e.re.test(o)),i&&(a=i.parse(o),s=i.record.name,i.keys.forEach(e=>{e.optional&&!a[e.name]&&delete a[e.name]}));else{if(i=t.name?r.get(t.name):n.find(e=>e.re.test(t.path)),!i)throw Wn(1,{location:e,currentLocation:t});s=i.record.name,a=J({},t.params,e.params),o=i.stringify(a)}let c=[],l=i;for(;l;)c.unshift(l.record),l=l.parent;return{name:s,path:o,params:a,matched:c,meta:vi(c)}}e.forEach(e=>a(e));function u(){n.length=0,r.clear()}return{addRoute:a,resolve:l,removeRoute:o,clearRoutes:u,getRoutes:s,getRecordMatcher:i}}function mi(e,t){let n={};for(let r of t)r in e&&(n[r]=e[r]);return n}function hi(e){let t={path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:e.aliasOf,beforeEnter:e.beforeEnter,props:gi(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:`components`in e?e.components||null:e.component&&{default:e.component}};return Object.defineProperty(t,"mods",{value:{}}),t}function gi(e){let t={},n=e.props||!1;if(`component`in e)t.default=n;else for(let r in e.components)t[r]=typeof n==`object`?n[r]:n;return t}function _i(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function vi(e){return e.reduce((e,t)=>J(e,t.meta),{})}function yi(e,t){let n=0,r=t.length;for(;n!==r;){let i=n+r>>1;li(e,t[i])<0?r=i:n=i+1}let i=bi(e);return i&&(r=t.lastIndexOf(i,r-1)),r}function bi(e){let t=e;for(;t=t.parent;)if(xi(t)&&li(e,t)===0)return t}function xi({record:e}){return!!(e.name||e.components&&Object.keys(e.components).length||e.redirect)}function Si(e){let t=b(qn),n=b(Jn),r=I(()=>{let n=g(e.to);return t.resolve(n)}),i=I(()=>{let{matched:e}=r.value,{length:t}=e,i=e[t-1],a=n.matched;if(!i||!a.length)return-1;let o=a.findIndex(Er.bind(null,i));if(o>-1)return o;let s=Di(e[t-2]);return t>1&&Di(i)===s&&a[a.length-1].path!==s?a.findIndex(Er.bind(null,e[t-2])):o}),a=I(()=>i.value>-1&&Ei(n.params,r.value.params)),o=I(()=>i.value>-1&&i.value===n.matched.length-1&&Dr(n.params,r.value.params));function s(n={}){if(Ti(n)){let n=t[g(e.replace)?`replace`:`push`](g(e.to)).catch(Vn);return e.viewTransition&&typeof document<`u`&&`startViewTransition`in document&&document.startViewTransition(()=>n),n}return Promise.resolve()}return{route:r,href:I(()=>r.value.href),isActive:a,isExactActive:o,navigate:s}}function Ci(e){return e.length===1?e[0]:e}var wi=_({name:`RouterLink`,compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:`page`},viewTransition:Boolean},useLink:Si,setup(e,{slots:t}){let n=m(Si(e)),{options:r}=b(qn),i=I(()=>({[Oi(e.activeClass,r.linkActiveClass,`router-link-active`)]:n.isActive,[Oi(e.exactActiveClass,r.linkExactActiveClass,`router-link-exact-active`)]:n.isExactActive}));return()=>{let r=t.default&&Ci(t.default(n));return e.custom?r:he(`a`,{"aria-current":n.isExactActive?e.ariaCurrentValue:null,href:n.href,onClick:n.navigate,class:i.value},r)}}});function Ti(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&(e.button===void 0||e.button===0)){if(e.currentTarget&&e.currentTarget.getAttribute){let t=e.currentTarget.getAttribute(`target`);if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function Ei(e,t){for(let n in t){let r=t[n],i=e[n];if(typeof r==`string`){if(r!==i)return!1}else if(!Y(i)||i.length!==r.length||r.some((e,t)=>e.valueOf()!==i[t].valueOf()))return!1}return!0}function Di(e){return e?e.aliasOf?e.aliasOf.path:e.path:``}var Oi=(e,t,n)=>e??t??n,ki=_({name:`RouterView`,inheritAttrs:!1,props:{name:{type:String,default:`default`},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:n}){let r=b(Yn),i=I(()=>e.route||r.value),a=b(Kn,0),s=I(()=>{let e=g(a),{matched:t}=i.value,n;for(;(n=t[e])&&!n.components;)e++;return e}),c=I(()=>i.value.matched[s.value]);u(Kn,I(()=>s.value+1)),u(Gn,c),u(Yn,i);let l=o();return d(()=>[l.value,c.value,e.name],([e,t,n],[r,i,a])=>{t&&(t.instances[n]=e,i&&i!==t&&e&&e===r&&(t.leaveGuards.size||(t.leaveGuards=i.leaveGuards),t.updateGuards.size||(t.updateGuards=i.updateGuards))),e&&t&&(!i||!Er(t,i)||!r)&&(t.enterCallbacks[n]||[]).forEach(t=>t(e))},{flush:`post`}),()=>{let r=i.value,a=e.name,o=c.value,s=o&&o.components[a];if(!s)return Ai(n.default,{Component:s,route:r});let u=o.props[a],d=u?u===!0?r.params:typeof u==`function`?u(r):u:null,f=he(s,J({},d,t,{onVnodeUnmounted:e=>{e.component.isUnmounted&&(o.instances[a]=null)},ref:l}));return Ai(n.default,{Component:f,route:r})||f}}});function Ai(e,t){if(!e)return null;let n=e(t);return n.length===1?n[0]:n}var ji=ki;function Mi(e){let t=pi(e.routes,e),n=e.parseQuery||Ur,r=e.stringifyQuery||Wr,i=e.history,a=Kr(),o=Kr(),s=Kr(),l=le(Z),u=Z;Qn&&e.scrollBehavior&&`scrollRestoration`in history&&(history.scrollRestoration=`manual`);let d=Bn.bind(null,e=>``+e),f=Bn.bind(null,vr),p=Bn.bind(null,yr);function m(e,n){let r,i;return Hr(e)?(r=t.getRecordMatcher(e),i=n):i=e,t.addRoute(i,r)}function h(e){let n=t.getRecordMatcher(e);n&&t.removeRoute(n)}function _(){return t.getRoutes().map(e=>e.record)}function v(e){return!!t.getRecordMatcher(e)}function y(e,a){if(a=J({},a||l.value),typeof e==`string`){let r=Sr(n,e,a.path),o=t.resolve({path:r.path},a),s=i.createHref(r.fullPath);return J(r,o,{params:p(o.params),redirectedFrom:void 0,href:s})}let o;if(e.path!=null)o=J({},e,{path:Sr(n,e.path,a.path).path});else{let t=J({},e.params);for(let e in t)t[e]??delete t[e];o=J({},e,{params:f(t)}),a.params=f(a.params)}let s=t.resolve(o,a),c=e.hash||``;s.params=d(p(s.params));let u=Cr(r,J({},e,{hash:mr(c),path:s.path})),m=i.createHref(u);return J({fullPath:u,hash:c,query:r===Wr?Gr(e.query):e.query||{}},s,{redirectedFrom:void 0,href:m})}function b(e){return typeof e==`string`?Sr(n,e,l.value.path):J({},e)}function x(e,t){if(u!==e)return Wn(8,{from:t,to:e})}function S(e){return T(e)}function C(e){return S(J(b(e),{replace:!0}))}function w(e,t){let n=e.matched[e.matched.length-1];if(n&&n.redirect){let{redirect:r}=n,i=typeof r==`function`?r(e,t):r;return typeof i==`string`&&(i=i.includes(`?`)||i.includes(`#`)?i=b(i):{path:i},i.params={}),J({query:e.query,hash:e.hash,params:i.path==null?e.params:{}},i)}}function T(e,t){let n=u=y(e),i=l.value,a=e.state,o=e.force,s=e.replace===!0,c=w(n,i);if(c)return T(J(b(c),{state:typeof c==`object`?J({},a,c.state):a,force:o,replace:s}),t||n);let d=n;d.redirectedFrom=t;let f;return!o&&Tr(r,i,n)&&(f=Wn(16,{to:d,from:i}),ae(i,i,!0,!1)),(f?Promise.resolve(f):O(d,i)).catch(e=>X(e)?X(e,2)?e:ie(e):ne(e,d,i)).then(e=>{if(e){if(X(e,2))return T(J({replace:s},b(e.to),{state:typeof e.to==`object`?J({},a,e.to.state):a,force:o}),t||d)}else e=A(d,i,!0,s,a);return k(d,i,e),e})}function E(e,t){let n=x(e,t);return n?Promise.reject(n):Promise.resolve()}function D(e){let t=ce.values().next().value;return t&&typeof t.runWithContext==`function`?t.runWithContext(e):e()}function O(e,t){let n,[r,i,s]=Jr(e,t);n=qr(r.reverse(),`beforeRouteLeave`,e,t);for(let i of r)i.leaveGuards.forEach(r=>{n.push(Q(r,e,t))});let c=E.bind(null,e,t);return n.push(c),F(n).then(()=>{n=[];for(let r of a.list())n.push(Q(r,e,t));return n.push(c),F(n)}).then(()=>{n=qr(i,`beforeRouteUpdate`,e,t);for(let r of i)r.updateGuards.forEach(r=>{n.push(Q(r,e,t))});return n.push(c),F(n)}).then(()=>{n=[];for(let r of s)if(r.beforeEnter)if(Y(r.beforeEnter))for(let i of r.beforeEnter)n.push(Q(i,e,t));else n.push(Q(r.beforeEnter,e,t));return n.push(c),F(n)}).then(()=>(e.matched.forEach(e=>e.enterCallbacks={}),n=qr(s,`beforeRouteEnter`,e,t,D),n.push(c),F(n))).then(()=>{n=[];for(let r of o.list())n.push(Q(r,e,t));return n.push(c),F(n)}).catch(e=>X(e,8)?e:Promise.reject(e))}function k(e,t,n){s.list().forEach(r=>D(()=>r(e,t,n)))}function A(e,t,n,r,a){let o=x(e,t);if(o)return o;let s=t===Z,c=Qn?history.state:{};n&&(r||s?i.replace(e.fullPath,J({scroll:s&&c&&c.scroll},a)):i.push(e.fullPath,a)),l.value=e,ae(e,t,n,s),ie()}let ee;function j(){ee||=i.listen((e,t,n)=>{if(!P.listening)return;let r=y(e),a=w(r,P.currentRoute.value);if(a){T(J(a,{replace:!0,force:!0}),r).catch(Vn);return}u=r;let o=l.value;Qn&&zr(Lr(o.fullPath,n.delta),Fr()),O(r,o).catch(e=>X(e,12)?e:X(e,2)?(T(J(b(e.to),{force:!0}),r).then(e=>{X(e,20)&&!n.delta&&n.type===`pop`&&i.go(-1,!1)}).catch(Vn),Promise.reject()):(n.delta&&i.go(-n.delta,!1),ne(e,r,o))).then(e=>{e||=A(r,o,!1),e&&(n.delta&&!X(e,8)?i.go(-n.delta,!1):n.type===`pop`&&X(e,20)&&i.go(-1,!1)),k(r,o,e)}).catch(Vn)})}let te=Kr(),M=Kr(),N;function ne(e,t,n){ie(e);let r=M.list();return r.length?r.forEach(r=>r(e,t,n)):console.error(e),Promise.reject(e)}function re(){return N&&l.value!==Z?Promise.resolve():new Promise((e,t)=>{te.add([e,t])})}function ie(e){return N||(N=!e,j(),te.list().forEach(([t,n])=>e?n(e):t()),te.reset()),e}function ae(t,n,r,i){let{scrollBehavior:a}=e;if(!Qn||!a)return Promise.resolve();let o=!r&&Br(Lr(t.fullPath,0))||(i||!r)&&history.state&&history.state.scroll||null;return z().then(()=>a(t,n,o)).then(e=>t===l.value&&e&&Ir(e)).catch(e=>t===l.value&&ne(e,t,n))}let oe=e=>i.go(e),se,ce=new Set,P={currentRoute:l,listening:!0,addRoute:m,removeRoute:h,clearRoutes:t.clearRoutes,hasRoute:v,getRoutes:_,resolve:y,options:e,push:S,replace:C,go:oe,back:()=>oe(-1),forward:()=>oe(1),beforeEach:a.add,beforeResolve:o.add,afterEach:s.add,onError:M.add,isReady:re,install(e){e.component(`RouterLink`,wi),e.component(`RouterView`,ji),e.config.globalProperties.$router=P,Object.defineProperty(e.config.globalProperties,"$route",{enumerable:!0,get:()=>g(l)}),Qn&&!se&&l.value===Z&&(se=!0,S(i.location).catch(e=>{}));let t={};for(let e in Z)Object.defineProperty(t,e,{get:()=>l.value[e],enumerable:!0});e.provide(qn,P),e.provide(Jn,c(t)),e.provide(Yn,l);let n=e.unmount;ce.add(e),e.unmount=function(){ce.delete(e),ce.size<1&&(u=Z,ee&&ee(),ee=null,l.value=Z,se=!1,N=!1),n()}}};function F(e){return e.reduce((e,t)=>e.then(()=>D(t)),Promise.resolve())}return P}var Ni=e.create({baseURL:``,timeout:3e4,withCredentials:!0,headers:{"Content-Type":`application/json`}}),Pi=12;try{localStorage.removeItem(`orva.hasUser`)}catch{}var Fi=_e(`auth`,()=>{let e=o(null),t=o(!1),n=o(!1),r=o(null),i=o(null),a=o(!1),s=o(0),c=e=>{r.value=e},l=async(r,i)=>{n.value=!0;try{let n=await Ni.post(`/api/v1/auth/login`,{username:r,password:i});return e.value=n.data.user,t.value=!0,c(!0),{success:!0}}catch(e){return{success:!1,error:e.response?.data?.error?.message||`Login failed`}}finally{n.value=!1}},u=async(r,i)=>{n.value=!0;try{let n=await Ni.post(`/api/v1/auth/onboard`,{username:r,password:i});return e.value=n.data.user,t.value=!0,c(!0),{success:!0}}catch(e){return{success:!1,error:e.response?.data?.error?.message||`Setup failed`}}finally{n.value=!1}},d=async()=>{try{await Ni.post(`/api/v1/auth/logout`)}catch{}e.value=null,t.value=!1},f=async({force:e=!1}={})=>{if(!e&&r.value!==null)return r.value;try{let e=await Ni.get(`/api/v1/auth/status`);return c(!!e.data.has_user),r.value}catch{return r.value===null&&c(!0),r.value}},p=async()=>{try{let n=await Ni.get(`/api/v1/auth/me`);return e.value=n.data,t.value=!0,c(!0),i.value=n.data.expires_at||null,!0}catch{return e.value=null,t.value=!1,i.value=null,!1}},m=async()=>{a.value=!0;try{let e=await Ni.post(`/api/v1/auth/refresh`);return i.value=e.data.expires_at||null,{success:!0}}catch(e){return t.value=!1,i.value=null,{success:!1,error:e.response?.data?.error?.message||`Refresh failed`}}finally{a.value=!1}},h=I(()=>i.value?(new Date(i.value).getTime()-Date.now())/1e3:null);return{user:e,isAuthenticated:t,loading:n,hasUser:r,expiresAt:i,refreshing:a,secondsUntilExpiry:h,shouldShowExpiryToast:I(()=>{if(!t.value)return!1;let e=h.value;return e==null||e<=0||Date.now(){s.value=Date.now()+36e5},changePassword:async(e,t)=>{await Ni.post(`/api/v1/auth/change-password`,{old_password:e,new_password:t})}}}),Ii=(e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n},Li={},Ri={viewBox:`0 0 32 32`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,class:`text-primary`};function zi(e,t){return r(),C(`svg`,Ri,[...t[0]||=[x(`rect`,{width:`32`,height:`32`,rx:`8`,fill:`currentColor`},null,-1),x(`text`,{x:`16`,y:`21`,"font-family":`'Inter', system-ui, sans-serif`,"font-size":`13`,"font-weight":`700`,fill:`white`,"text-anchor":`middle`,"letter-spacing":`-0.5`},` f(x) `,-1)]])}var Bi=Ii(Li,[[`render`,zi]]),Vi={class:`lg:hidden fixed top-0 inset-x-0 h-14 bg-background border-b border-border z-30 flex items-center justify-between px-4 pt-safe pl-safe pr-safe`},Hi={class:`flex items-center gap-2 text-white font-mono`},Ui=[`aria-label`,`aria-expanded`],Wi={class:`h-16 flex items-center px-6 border-b border-border`},Gi={class:`flex items-center gap-3 text-white font-mono tracking-tight text-lg`},Ki={class:`flex-1 p-3 space-y-1 overflow-y-auto scrollable`},qi=[`aria-expanded`,`aria-controls`,`onClick`],Ji={class:`flex-1 text-left`},Yi=[`id`],Xi={class:`mt-2 space-y-1 border-t border-border pt-2`},Zi={__name:`Sidebar`,setup(e){let t=Zn(),n=o(!1),i=o(null),c=o(null),u=e=>e===`/`?t.path===`/`:t.path.startsWith(e),f=[{path:`/`,label:`Overview`,icon:ln},{path:`/ai`,label:`Chat`,icon:mn},{path:`/functions`,label:`Functions`,icon:rn}],p=[{id:`automation`,label:`Automation`,icon:an,items:[{path:`/cron`,label:`Schedules`,icon:an},{path:`/jobs`,label:`Jobs`,icon:dn}]},{id:`observe`,label:`Observe`,icon:nn,items:[{path:`/activity`,label:`Activity`,icon:nn},{path:`/invocations`,label:`Invocations`,icon:fn},{path:`/traces`,label:`Traces`,icon:hn}]},{id:`connect`,label:`Connect`,icon:gn,items:[{path:`/api-keys`,label:`Keys`,icon:cn},{path:`/channels`,label:`Channels`,icon:gn},{path:`/webhooks`,label:`Webhooks`,icon:Sn},{path:`/firewall`,label:`Egress`,icon:bn}]}],m=[{path:`/settings`,label:`Settings`,icon:yn},{path:`/docs`,label:`Docs`,icon:un}],h=o(Object.fromEntries(p.map(e=>[e.id,!1]))),_=e=>e.items.some(e=>u(e.path)),v=()=>{let e=p.find(_);e&&(h.value[e.id]=!0)};v(),d(()=>t.fullPath,()=>{n.value=!1,v()}),d(n,async e=>{await z(),e?(i.value?.querySelector(`a[href]`))?.focus?.():i.value?.contains(document.activeElement)&&c.value?.focus?.()});let y=0,b=0,S=!1,w=e=>{if(window.innerWidth>=1024||!n.value)return;let t=e.touches[0];y=t.clientX,b=t.clientY,S=!0},T=e=>{if(!S)return;let t=e.touches[0],r=t.clientX-y,i=Math.abs(t.clientY-b);r<-60&&i<40&&(n.value=!1,S=!1)},E=()=>{S=!1};return(e,t)=>{let o=te(`router-link`);return r(),C(F,null,[x(`header`,Vi,[x(`div`,Hi,[k(Bi,{class:`w-6 h-6`}),t[5]||=x(`span`,{class:`font-bold tracking-tight`},`Orva`,-1)]),x(`button`,{ref_key:`toggleBtn`,ref:c,class:`p-2 rounded-md text-foreground-muted hover:text-white hover:bg-surface transition-colors touch-expand-iconbtn`,"aria-label":n.value?`Close menu`:`Open menu`,"aria-expanded":n.value,"aria-controls":`primary-navigation`,onClick:t[0]||=e=>n.value=!n.value},[n.value?(r(),N(g(Cn),{key:1,class:`w-5 h-5`})):(r(),N(g(pn),{key:0,class:`w-5 h-5`}))],8,Ui)]),k(je,{name:`fade`},{default:a(()=>[n.value?(r(),C(`div`,{key:0,class:`lg:hidden fixed inset-0 bg-black/50 z-30 backdrop-blur-sm`,onClick:t[1]||=e=>n.value=!1})):L(``,!0)]),_:1}),x(`aside`,{id:`primary-navigation`,ref_key:`drawerEl`,ref:i,class:O([`bg-background border-r border-border flex flex-col h-full shrink-0 z-40 w-64 lg:w-52 fixed inset-y-0 left-0 transform transition-transform duration-150 ease-out lg:static lg:translate-x-0 lg:transform-none lg:transition-none pt-safe pb-safe pl-safe`,n.value?`translate-x-0`:`-translate-x-full lg:translate-x-0`]),onTouchstart:w,onTouchmove:T,onTouchend:E},[x(`div`,Wi,[x(`div`,Gi,[k(Bi,{class:`w-8 h-8`}),t[6]||=x(`span`,{class:`font-bold tracking-tight text-white`},`Orva`,-1)])]),x(`nav`,Ki,[(r(),C(F,null,M(f,e=>k(o,{key:e.path,to:e.path,class:O([`flex items-center gap-3 px-3 py-2.5 rounded-md text-sm transition-colors duration-150 group font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary`,u(e.path)?`text-white bg-primary/15`:`text-foreground-muted hover:text-white hover:bg-surface-hover`]),onClick:t[2]||=e=>n.value=!1},{default:a(()=>[(r(),N(l(e.icon),{class:O([`w-4 h-4 transition-colors`,u(e.path)?`text-white`:`text-foreground-muted group-hover:text-white`])},null,8,[`class`])),x(`span`,null,R(e.label),1)]),_:2},1032,[`to`,`class`])),64)),(r(),C(F,null,M(p,e=>x(`div`,{key:e.id,class:`pt-1`},[x(`button`,{type:`button`,class:O([`flex w-full items-center gap-3 rounded-md px-3 py-2.5 text-sm font-medium text-foreground-muted transition-colors hover:bg-surface-hover hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary`,_(e)?`text-white`:``]),"aria-expanded":h.value[e.id],"aria-controls":`nav-group-${e.id}`,onClick:t=>h.value[e.id]=!h.value[e.id]},[(r(),N(l(e.icon),{class:`h-4 w-4`})),x(`span`,Ji,R(e.label),1),k(g(on),{class:O([`h-3.5 w-3.5 transition-transform`,h.value[e.id]?`rotate-0`:`-rotate-90`])},null,8,[`class`])],10,qi),s(x(`div`,{id:`nav-group-${e.id}`,class:`ml-3 mt-1 space-y-0.5 border-l border-border pl-2`},[(r(!0),C(F,null,M(e.items,e=>(r(),N(o,{key:e.path,to:e.path,class:O([`flex items-center gap-3 px-3 py-2 rounded-md text-sm transition-colors duration-150 group font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary`,u(e.path)?`text-white bg-primary/15`:`text-foreground-muted hover:text-white hover:bg-surface-hover`]),onClick:t[3]||=e=>n.value=!1},{default:a(()=>[(r(),N(l(e.icon),{class:`h-4 w-4`})),x(`span`,null,R(e.label),1)]),_:2},1032,[`to`,`class`]))),128))],8,Yi),[[Je,h.value[e.id]]])])),64)),x(`div`,Xi,[(r(),C(F,null,M(m,e=>k(o,{key:e.path,to:e.path,class:O([`flex items-center gap-3 px-3 py-2.5 rounded-md text-sm transition-colors duration-150 group font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary`,u(e.path)?`text-white bg-primary/15`:`text-foreground-muted hover:text-white hover:bg-surface-hover`]),onClick:t[4]||=e=>n.value=!1},{default:a(()=>[(r(),N(l(e.icon),{class:`h-4 w-4`})),x(`span`,null,R(e.label),1)]),_:2},1032,[`to`,`class`])),64))])])],34)],64)}}},Qi={key:0,class:`fixed z-50 bg-background border border-border shadow-lg rounded-lg p-4 flex items-start gap-3 inset-x-2 bottom-2 max-w-sm mx-auto pb-safe sm:inset-x-auto sm:bottom-6 sm:right-6 sm:mx-0 sm:pb-4`},$i={class:`flex-1 min-w-0`},ea={key:0,class:`text-sm font-medium text-white mb-0.5`},ta={class:`text-xs text-foreground-muted`},na={class:`flex flex-col gap-2 shrink-0`},ra=[`disabled`],ia=Ii(Object.assign({name:`CommonToast`},{__name:`Toast`,props:{visible:{type:Boolean,default:!1},title:{type:String,default:``},actionLabel:{type:String,default:``},actionLoading:{type:Boolean,default:!1},dismissible:{type:Boolean,default:!0}},emits:[`action`,`dismiss`],setup(e){return(t,i)=>(r(),N(v,{to:`body`},[k(je,{name:`toast`},{default:a(()=>[e.visible?(r(),C(`div`,Qi,[x(`div`,$i,[e.title?(r(),C(`div`,ea,R(e.title),1)):L(``,!0),x(`div`,ta,[n(t.$slots,`default`,{},void 0,!0)])]),x(`div`,na,[e.actionLabel?(r(),C(`button`,{key:0,class:`px-3 py-1 rounded text-xs font-medium bg-white text-black hover:bg-foreground-muted transition-colors`,disabled:e.actionLoading,onClick:i[0]||=e=>t.$emit(`action`)},R(e.actionLoading?`…`:e.actionLabel),9,ra)):L(``,!0),e.dismissible?(r(),C(`button`,{key:1,class:`text-foreground-muted hover:text-white text-xs`,onClick:i[1]||=e=>t.$emit(`dismiss`)},` Dismiss `)):L(``,!0)])])):L(``,!0)]),_:3})]))}}),[[`__scopeId`,`data-v-1cbabcec`]]),aa={class:`flex items-center gap-2 px-4 py-3 border-b border-border`},oa=[`aria-selected`,`onClick`,`onMouseenter`],sa={class:`flex-1 truncate`},ca={key:0,class:`hidden sm:inline-flex items-center gap-1 text-[10px] font-mono text-foreground-muted`},la={key:0,class:`px-4 py-6 text-center text-sm text-foreground-muted`},ua=800,da={__name:`CommandPalette`,setup(e,{expose:t}){let n=Xn(),c=o(!1),u=o(``),f=o(0),m=o(null),h=o(null),_=o(null);kn(_,c);let y=[{id:`fn-new`,label:`New function`,icon:_n,action:()=>n.push(`/functions/new`),shortcut:[`c`,`n`]},{id:`go-fns`,label:`Functions`,icon:rn,action:()=>n.push(`/functions`),shortcut:[`g`,`f`]},{id:`go-inv`,label:`Invocations`,icon:fn,action:()=>n.push(`/invocations`),shortcut:[`g`,`i`]},{id:`go-jobs`,label:`Jobs`,icon:dn,action:()=>n.push(`/jobs`),shortcut:[`g`,`j`]},{id:`go-cron`,label:`Schedules`,icon:an,action:()=>n.push(`/cron`)},{id:`go-activity`,label:`Activity`,icon:nn,action:()=>n.push(`/activity`)},{id:`go-traces`,label:`Traces`,icon:hn,action:()=>n.push(`/traces`)},{id:`go-keys`,label:`API Keys`,icon:cn,action:()=>n.push(`/api-keys`)},{id:`go-channels`,label:`Channels`,icon:gn,action:()=>n.push(`/channels`)},{id:`go-hooks`,label:`Webhooks`,icon:Sn,action:()=>n.push(`/webhooks`)},{id:`go-fw`,label:`Egress`,icon:bn,action:()=>n.push(`/firewall`),keywords:`firewall blocklist dns`},{id:`go-settings`,label:`Settings`,icon:yn,action:()=>n.push(`/settings`)},{id:`go-docs`,label:`Docs`,icon:un,action:()=>n.push(`/docs`)},{id:`go-overview`,label:`Overview`,icon:ln,action:()=>n.push(`/`)}],b=I(()=>{let e=u.value.trim().toLowerCase();return e?y.filter(t=>t.label.toLowerCase().includes(e)||(t.keywords||``).includes(e)):y});d(b,()=>{f.value=0});let S=e=>{let t=b.value.length;t&&(f.value=(f.value+e+t)%t,z(()=>{(h.value?.querySelectorAll(`li[role="option"]`)[f.value])?.scrollIntoView?.({block:`nearest`})}))},w=e=>{e&&(T(),z(()=>e.action()))},T=()=>{c.value=!1,u.value=``,f.value=0},E=()=>{c.value=!0,z(()=>m.value?.focus())},D=``,A=null,ee=e=>{if(!e)return!1;let t=e.tagName;return!!(t===`INPUT`||t===`TEXTAREA`||t===`SELECT`||e.isContentEditable)},j=e=>{if((e.metaKey||e.ctrlKey)&&e.key.toLowerCase()===`k`){e.preventDefault(),c.value?T():E();return}if((e.metaKey||e.ctrlKey)&&e.key.toLowerCase()===`s`&&window.location.pathname.includes(`/functions/`)){e.preventDefault(),window.dispatchEvent(new CustomEvent(`orva:deploy`));return}if(e.metaKey||e.ctrlKey||e.altKey||ee(document.activeElement))return;if(!D){if(e.key===`g`||e.key===`c`){D=e.key,clearTimeout(A),A=setTimeout(()=>{D=``},ua);return}return}let t=D+e.key;D=``,clearTimeout(A);let n=y.find(e=>e.shortcut&&e.shortcut.join(``)===t);n&&(e.preventDefault(),n.action())};return p(()=>{window.addEventListener(`keydown`,j)}),i(()=>{window.removeEventListener(`keydown`,j),clearTimeout(A)}),t({show:E,close:T}),(e,t)=>(r(),N(v,{to:`body`},[k(je,{name:`fade`},{default:a(()=>[c.value?(r(),C(`div`,{key:0,class:`fixed inset-0 z-50 flex items-start justify-center bg-black/60 backdrop-blur-sm pt-[10vh] sm:pt-[15vh] px-4`,onClick:Rt(T,[`self`])},[x(`div`,{ref_key:`dialogRoot`,ref:_,class:`w-full max-w-lg bg-background border border-border rounded-lg shadow-xl overflow-hidden`,role:`dialog`,"aria-modal":`true`,"aria-labelledby":`command-palette-label`},[x(`div`,aa,[k(g(vn),{class:`w-4 h-4 text-foreground-muted shrink-0`}),t[4]||=x(`span`,{id:`command-palette-label`,class:`sr-only`},`Command palette`,-1),s(x(`input`,{ref_key:`searchInput`,ref:m,"onUpdate:modelValue":t[0]||=e=>u.value=e,type:`text`,placeholder:`Search routes, actions…`,class:`flex-1 bg-transparent border-0 text-base sm:text-sm text-white placeholder-foreground-muted focus:outline-none`,onKeydown:[t[1]||=Bt(Rt(e=>S(1),[`prevent`]),[`down`]),t[2]||=Bt(Rt(e=>S(-1),[`prevent`]),[`up`]),t[3]||=Bt(Rt(e=>w(b.value[f.value]),[`prevent`]),[`enter`]),Bt(T,[`esc`])]},null,544),[[Tt,u.value]]),t[5]||=x(`kbd`,{class:`hidden sm:inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-mono text-foreground-muted bg-surface border border-border`},`esc`,-1)]),x(`ul`,{ref_key:`listRef`,ref:h,class:`max-h-[60dvh] overflow-y-auto scrollable py-1`,role:`listbox`},[(r(!0),C(F,null,M(b.value,(e,t)=>(r(),C(`li`,{key:e.id,role:`option`,"aria-selected":t===f.value,class:O([`flex items-center gap-3 px-4 py-2.5 min-h-[44px] text-base sm:text-sm cursor-pointer`,t===f.value?`bg-primary/15 text-white`:`text-foreground hover:bg-surface-hover`]),onClick:t=>w(e),onMouseenter:e=>f.value=t},[(r(),N(l(e.icon),{class:`w-4 h-4 shrink-0 text-foreground-muted`})),x(`span`,sa,R(e.label),1),e.shortcut?(r(),C(`span`,ca,[(r(!0),C(F,null,M(e.shortcut,e=>(r(),C(`kbd`,{key:e,class:`px-1.5 py-0.5 rounded bg-surface border border-border`},R(e),1))),128))])):L(``,!0)],42,oa))),128)),b.value.length?L(``,!0):(r(),C(`li`,la,` Nothing matches "`+R(u.value)+`". `,1))],512),t[6]||=x(`div`,{class:`px-4 py-2 border-t border-border bg-surface/40 flex items-center justify-between text-[10px] text-foreground-muted`},[x(`span`,{class:`flex items-center gap-2`},[x(`kbd`,{class:`px-1.5 py-0.5 rounded font-mono bg-surface border border-border`},`↑↓`),x(`span`,null,`navigate`)]),x(`span`,{class:`flex items-center gap-2`},[x(`kbd`,{class:`px-1.5 py-0.5 rounded font-mono bg-surface border border-border`},`↵`),x(`span`,null,`activate`)])],-1)],512)])):L(``,!0)]),_:1})]))}},fa=()=>B.get(`/system/health`),pa=e=>B.get(`/functions`,{params:e}),ma=e=>B.get(`/functions/${e}`),ha=e=>B.get(`/functions/${e}/source`),ga=()=>B.get(`/routes`),_a=(e,t,n=`*`)=>B.post(`/routes`,{path:e,function_id:t,methods:n}),va=e=>B.delete(`/routes`,{params:{path:e}}),ya=(e,{method:t=`POST`,path:n=`/`,headers:r={},body:i=``}={})=>{let a=`/${e.replace(/^fn_/,``)}`;n&&n!==`/`&&(a+=n.startsWith(`/`)?n:`/${n}`);let o={url:a,method:t,headers:{...r},responseType:`text`,transformRequest:[e=>e]},s=(t||`POST`).toUpperCase();return i&&s!==`GET`&&s!==`HEAD`&&(o.data=i),ye.request(o)},ba=e=>B.get(`/executions`,{params:e}),xa=e=>B.get(`/executions/${e}`),Sa=e=>B.get(`/executions/${e}/logs`),Ca=e=>B.get(`/executions/${e}/request`),wa=e=>B.post(`/executions/${e}/replay`,null,{responseType:`text`}),Ta=e=>B.get(`/activity`,{params:e}),Ea=(e,t)=>B.get(`/functions/${encodeURIComponent(e)}/kv`,{params:t}),Da=(e,t,n)=>B.put(`/functions/${encodeURIComponent(e)}/kv/${encodeURIComponent(t)}`,n),Oa=(e,t)=>B.delete(`/functions/${encodeURIComponent(e)}/kv/${encodeURIComponent(t)}`),ka=e=>B.get(`/functions/${encodeURIComponent(e)}/fixtures`),Aa=(e,t,n)=>B.put(`/functions/${encodeURIComponent(e)}/fixtures/${encodeURIComponent(t)}`,n),ja=(e,t)=>B.delete(`/functions/${encodeURIComponent(e)}/fixtures/${encodeURIComponent(t)}`),Ma=()=>B.get(`/keys`),Na=e=>B.post(`/keys`,e),Pa=e=>B.delete(`/keys/${e}`),Fa=()=>B.get(`/oauth/connected-apps`),Ia=e=>B.delete(`/oauth/connected-apps/${e}`),La=()=>B.get(`/auth/sessions`),Ra=e=>B.delete(`/auth/sessions/${e}`),za=()=>B.get(`/channels`),Ba=e=>B.post(`/channels`,e),Va=e=>B.post(`/channels/${e}/rotate`),Ha=e=>B.delete(`/channels/${e}`),Ua=()=>B.get(`/system/metrics.json`),Wa=e=>B.get(`/deployments/${e}`),Ga=(e,t=0,n=200)=>B.get(`/deployments/${e}/logs`,{params:{from:t,limit:n}}),Ka=(e,t=50)=>B.get(`/functions/${e}/deployments`,{params:{limit:t}}),qa=(e,t,n,r=`json`)=>B.get(`/functions/${encodeURIComponent(e)}/diff`,{params:{from:t,to:n,format:r},responseType:r===`unified`?`text`:`json`}),Ja=(e,t)=>B.post(`/functions/${e}/rollback`,t),Ya=async e=>{let t=((await B.get(`/functions`)).data.functions||[]).find(t=>t.name===e);if(!t)throw Error(`Function "${e}" not found`);return t.id},Xa=e=>({...e,cron_expression:e.cron_expr}),Za=async()=>({data:{schedules:((await B.get(`/cron`)).data.schedules||[]).map(Xa)}}),Qa=async(e,t)=>{let n=await Ya(e),r={cron_expr:t.cron,timezone:t.timezone||eo(),enabled:t.enabled!==!1,payload:t.payload??{}};return{data:Xa((await B.post(`/functions/${n}/cron`,r)).data)}},$a=async(e,t)=>{let n=t.function_id;if(!n)throw Error(`updateCronSchedule: function_id is required`);let r={};return t.cron!==void 0&&(r.cron_expr=t.cron),t.timezone!==void 0&&(r.timezone=t.timezone),t.enabled!==void 0&&(r.enabled=t.enabled),t.payload!==void 0&&(r.payload=t.payload),{data:Xa((await B.put(`/functions/${n}/cron/${e}`,r)).data)}},eo=()=>{try{return Intl.DateTimeFormat().resolvedOptions().timeZone||`UTC`}catch{return`UTC`}},to=async(e,t)=>{if(!t)throw Error(`deleteCronSchedule: functionId is required`);return B.delete(`/functions/${t}/cron/${e}`)},no=(e={})=>B.get(`/jobs`,{params:e}),ro=e=>B.post(`/jobs`,e),io=e=>B.post(`/jobs/${e}/retry`),ao=e=>B.delete(`/jobs/${e}`),oo=()=>B.get(`/system/storage`),so=()=>B.post(`/system/vacuum`),co=e=>{let t=new FormData;return t.append(`archive`,e),B.post(`/restore?confirm=1`,t,{headers:{"Content-Type":`multipart/form-data`},timeout:6e5})},lo=e=>B.get(`/traces/${e}`),uo=(e={})=>B.get(`/traces`,{params:e}),fo=()=>B.get(`/webhooks`),po=e=>B.post(`/webhooks`,e),mo=(e,t)=>B.put(`/webhooks/${e}`,t),ho=e=>B.delete(`/webhooks/${e}`),go=e=>B.post(`/webhooks/${e}/test`),_o=e=>B.get(`/webhooks/${e}/deliveries`),vo=e=>B.post(`/webhooks/deliveries/${e}/retry`),yo=e=>B.get(`/functions/${encodeURIComponent(e)}/inbound-webhooks`),bo=(e,t)=>B.post(`/functions/${encodeURIComponent(e)}/inbound-webhooks`,t),xo=(e,t)=>B.delete(`/functions/${encodeURIComponent(e)}/inbound-webhooks/${t}`),So=[500,1e3,2e3,5e3,1e4],Co=_e(`events`,()=>{let e=o(!1),t=o(0),n=new Map,r=null,i=null,a=(e,t)=>{let r=n.get(e);if(r)for(let e of r)try{e(t)}catch(e){console.error(`events callback error`,e)}},s=n=>{n.onopen=()=>{e.value=!0,t.value=0};for(let e of[`metrics`,`execution`,`deployment`,`function`,`activity`])n.addEventListener(e,t=>{try{let n=JSON.parse(t.data);a(e,n)}catch(e){console.warn(`failed to parse SSE payload`,e,t.data)}});n.onerror=()=>{if(e.value=!1,r){try{r.close()}catch{}r=null}let n=So[Math.min(t.value,So.length-1)];t.value+=1,clearTimeout(i),i=setTimeout(()=>c(),n)}},c=()=>{if(!r)try{r=new EventSource(`/api/v1/events`,{withCredentials:!0}),s(r)}catch(t){console.error(`failed to open /api/v1/events`,t),e.value=!1}};return{connected:h(e),reconnectAttempt:h(t),connect:c,disconnect:()=>{if(clearTimeout(i),i=null,r){try{r.close()}catch{}r=null}e.value=!1,t.value=0},subscribe:(e,t)=>(n.has(e)||n.set(e,new Set),n.get(e).add(t),()=>{let r=n.get(e);r&&r.delete(t)})}}),wo=60,To=_e(`system`,()=>{let e=o(!1),t=o(null),n=o(0),r=o([]),i=o({}),a=o(null),s=null,c=null,l=null,u=e=>{t.value=e;let n=new Set;for(let t of e.pools||[]){n.add(t.function_id);let e=i.value[t.function_id]||[];e.push(t.rate_ewma),e.length>wo&&e.splice(0,e.length-wo),i.value[t.function_id]=e}for(let e of Object.keys(i.value))n.has(e)||delete i.value[e]},d=async()=>{try{let[t,i,o,s]=await Promise.all([Ua(),pa().catch(()=>({data:{functions:[],total:0}})),ba({limit:20}).catch(()=>({data:{executions:[]}})),fa().catch(()=>({data:null}))]);u(t.data),n.value=i.data.total??(i.data.functions||[]).length,r.value=o.data.executions||[],s.data&&(a.value={version:s.data.version,commit:s.data.commit,buildTime:s.data.build_time,image:s.data.image,uptimeSeconds:s.data.uptime_seconds}),e.value=!0}catch(t){console.error(`seed fetch error:`,t),e.value=!1}};return{isConnected:e,metrics:t,functionsCount:n,recentInvocations:r,poolHistory:i,buildInfo:a,connect:()=>{let t=Co();d(),s=t.subscribe(`metrics`,t=>{u(t),e.value=!0}),c=t.subscribe(`execution`,e=>{r.value=[e,...r.value].slice(0,20)}),l=t.subscribe(`function`,e=>{e.action===`deleted`?n.value=Math.max(0,n.value-1):e.action===`created`&&(n.value+=1)})},disconnect:()=>{s&&=(s(),null),c&&=(c(),null),l&&=(l(),null),e.value=!1}}}),Eo={class:`flex h-screen w-full bg-background overflow-hidden font-sans antialiased text-foreground`},Do={class:`flex-1 flex flex-col min-w-0 overflow-hidden relative pt-14 lg:pt-0`},Oo={__name:`Layout`,setup(e){let t=To(),n=Co(),s=Fi(),c=o(0),u=null,d=I(()=>{c.value;let e=s.secondsUntilExpiry;return e==null||e<=0?`—`:e<60?`${Math.floor(e)}s`:e<3600?`${Math.floor(e/60)} min`:`${Math.floor(e/3600)} h`}),f=async()=>{(await s.refreshSession()).success||(window.location.href=`/login`)};return p(async()=>{await s.checkAuth(),n.connect(),t.connect(),u=setInterval(()=>{c.value++},3e4)}),i(()=>{t.disconnect(),n.disconnect(),u&&clearInterval(u)}),(e,t)=>{let n=te(`router-view`);return r(),C(`div`,Eo,[k(Zi),x(`main`,Do,[k(n,null,{default:a(({Component:e})=>[(r(),N(ee,{max:10},[(r(),N(l(e),{class:`flex-1 overflow-auto scrollable p-page`}))],1024))]),_:1})]),k(da),k(ia,{visible:g(s).shouldShowExpiryToast,"action-loading":g(s).refreshing,title:`Session expiring soon`,"action-label":`Stay signed in`,onAction:f,onDismiss:g(s).dismissExpiryToast},{default:a(()=>[re(` Your session expires in `+R(d.value)+`. Click to extend it for another 7 days. `,1)]),_:1},8,[`visible`,`action-loading`,`onDismiss`])])}}},ko=`modulepreload`,Ao=function(e){return`/web/`+e},jo={},$=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?import.meta.resolve(e):new URL(e,import.meta.url).href}r=o(t.map(t=>{if(t=Ao(t,n),t=s(t),t in jo)return;jo[t]=!0;let r=t.endsWith(`.css`);for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}let i=document.createElement(`link`);if(i.rel=r?`stylesheet`:ko,r||(i.as=`script`),i.crossOrigin=``,i.href=t,a&&i.setAttribute(`nonce`,a),document.head.appendChild(i),r)return new Promise((e,n)=>{i.addEventListener(`load`,e),i.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})},Mo=Mi({history:ei(`/web/`),routes:[{path:`/login`,name:`login`,component:()=>$(()=>import(`./Login-CGYB-z1p.js`),__vite__mapDeps([0,1,2])),meta:{requiresAuth:!1}},{path:`/onboarding`,name:`onboarding`,component:()=>$(()=>import(`./Onboarding-BI0ADHLS.js`),__vite__mapDeps([3,1,2,4])),meta:{requiresAuth:!1}},{path:`/`,component:Oo,meta:{requiresAuth:!0},children:[{path:``,name:`dashboard`,component:()=>$(()=>import(`./Dashboard-CEd_pnH7.js`),__vite__mapDeps([5,1]))},{path:`ai`,name:`ai`,component:()=>$(()=>import(`./AI-DwO56pB2.js`),__vite__mapDeps([6,7,1,8,4,9,10,11,12,13,14,15,16,17,18,19,20,21,22]))},{path:`functions`,name:`functions`,component:()=>$(()=>import(`./FunctionsList-ByM7ruDh.js`),__vite__mapDeps([23,1,8,4,24,25,14,26,17,11,7,21,27]))},{path:`functions/:name`,name:`function-detail`,component:()=>$(()=>import(`./Editor-BENkmvs8.js`),__vite__mapDeps([28,1,29,8,30,4,31,24,32,25,33,15,34,16,35,17,36,11,7,21,37,38,39,40,41]))},{path:`functions/:name/deployments`,name:`function-deployments`,component:()=>$(()=>import(`./Deployments-DCd1MNSh.js`),__vite__mapDeps([42,1,43,30,26,15,12,13,44,45,38]))},{path:`functions/:name/diff`,name:`function-diff`,component:()=>$(()=>import(`./FunctionDiff-DBpPBQBL.js`),__vite__mapDeps([46,1,4,31,15,18,47,21,38,48]))},{path:`functions/:name/kv`,name:`function-kv`,component:()=>$(()=>import(`./KVStore-Ly56eJIu.js`),__vite__mapDeps([49,1,26,17,12,13,27]))},{path:`functions/:name/inbound-webhooks`,name:`function-inbound-webhooks`,component:()=>$(()=>import(`./InboundWebhooks-C_jOjeYV.js`),__vite__mapDeps([50,1,26,17,12,13,27]))},{path:`functions/new`,name:`function-new`,component:()=>$(()=>import(`./Editor-BENkmvs8.js`),__vite__mapDeps([28,1,29,8,30,4,31,24,32,25,33,15,34,16,35,17,36,11,7,21,37,38,39,40,41]))},{path:`deploy`,redirect:{name:`function-new`}},{path:`cron`,name:`cron`,component:()=>$(()=>import(`./CronJobs-4ao1_N2n.js`),__vite__mapDeps([51,1,43,33,52,17,27,37]))},{path:`jobs`,name:`jobs`,component:()=>$(()=>import(`./Jobs-DzZOcH5E.js`),__vite__mapDeps([53,1,43,45,15,17,12,13,27]))},{path:`activity`,name:`activity`,component:()=>$(()=>import(`./Activity-C3gP7_TB.js`),__vite__mapDeps([54,1,55,12,13,44,43,45]))},{path:`invocations`,name:`invocations`,component:()=>$(()=>import(`./InvocationsLog-BZpwfSFT.js`),__vite__mapDeps([56,1,8,2,33,26,15,16,17,11,7,12,13,21,44,43,45,39]))},{path:`traces`,name:`traces`,component:()=>$(()=>import(`./Traces-DKjSRtpZ.js`),__vite__mapDeps([57,1,58,26,44,43,45]))},{path:`traces/:id`,name:`trace-detail`,component:()=>$(()=>import(`./TraceDetail-c4yTBunf.js`),__vite__mapDeps([59,1,60,2,4,58,44,43,45]))},{path:`api-keys`,name:`api-keys`,component:()=>$(()=>import(`./ApiKeys-C9fpYpoP.js`),__vite__mapDeps([61,1,8,4,32,17,21,27,62]))},{path:`channels`,name:`channels`,component:()=>$(()=>import(`./Channels-D-vbrSjj.js`),__vite__mapDeps([63,1,8,2,4,15,17,21,27,62]))},{path:`webhooks`,name:`webhooks`,component:()=>$(()=>import(`./Webhooks-Dv44P51d.js`),__vite__mapDeps([64,1,8,4,15,52,17,18,21,27,37]))},{path:`firewall`,name:`firewall`,component:()=>$(()=>import(`./Firewall-bYvlPpSq.js`),__vite__mapDeps([65,1,24,26,34,17,11,7,37,40,66]))},{path:`settings`,name:`settings`,component:()=>$(()=>import(`./Settings-CS-pXJcG.js`),__vite__mapDeps([67,1,4,9,10,8,11,7,12,13,32,35,17,21,62,40]))},{path:`docs`,name:`docs`,component:()=>$(()=>import(`./Docs-DjpX404o.js`),__vite__mapDeps([68,1,8,55,4,24,32,25,36,11,7,19,20,21,39,69]))}]},{path:`/:pathMatch(.*)*`,name:`not-found`,component:()=>$(()=>import(`./NotFound-CIp_RSOc.js`),__vite__mapDeps([70,1,60,29])),meta:{requiresAuth:!1}}]});Mo.beforeEach((e,t,n)=>{if(t.fullPath===e.fullPath&&t.name)return n(!1);n()}),Mo.beforeEach(async(e,t,n)=>{let r=Fi();if(!await r.fetchAuthStatus())return e.name===`onboarding`?n():n({name:`onboarding`,replace:!0});if(r.isAuthenticated===!1&&await r.checkAuth(),r.isAuthenticated)return e.name===`onboarding`||e.name===`login`?n({name:`dashboard`}):n();if(e.name===`login`)return n();if(e.name===`onboarding`)return n({name:`login`,replace:!0});n({name:`login`,query:{redirect:e.fullPath}})});var No=Wt(Ln),Po=ve();No.use(Po),No.use(Mo),No.mount(`#app`);export{Ra as $,Oa as A,nn as At,pa as B,Rt as Bt,ma as C,gn as Ct,oo as D,on as Dt,Sa as E,ln as Et,za as F,Ot as Ft,La as G,ba as H,Fa as I,kt as It,fo as J,uo as K,Za as L,Tt as Lt,Da as M,je as Mt,Ta as N,Et as Nt,lo as O,an as Ot,Ma as P,Nt as Pt,Ia as Q,Ka as R,Je as Rt,Ca as S,_n as St,xa as T,mn as Tt,no as U,yo as V,ga as W,io as X,wa as Y,vo as Z,va as _,En as _t,qa as a,$a as at,Wa as b,xn as bt,Qa as c,co as ct,Pa as d,Fi as dt,Ja as et,Ha as f,wi as ft,ao as g,Dn as gt,xo as h,kn as ht,eo as i,go as it,Ea as j,q as jt,ya as k,rn as kt,bo as l,Bi as lt,ja as m,Xn as mt,To as n,so as nt,Na as o,Aa as ot,to as p,Zn as pt,_o as q,Co as r,_a as rt,Ba as s,mo as st,$ as t,Va as tt,po as u,Ii as ut,ho as v,Cn as vt,ha as w,hn as wt,Ga as x,vn as xt,ro as y,Sn as yt,ka as z,Bt as zt}; \ No newline at end of file +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/Login-DscOTSMy.js","assets/runtime-core.esm-bundler-BMQPyJ_W.js","assets/circle-alert-BnUYYhGE.js","assets/Onboarding-CI9ktN7I.js","assets/copy-BqdwwcxC.js","assets/Dashboard-rKNyCP0W.js","assets/AI-De72Ye8u.js","assets/axios-DVDuIpRy.js","assets/check-BNre7JFR.js","assets/ModelMenu-BtMjUXcx.js","assets/pinia-B41TZNUX.js","assets/client-BF51V3uE.js","assets/Drawer-B98TBytl.js","assets/Drawer-9hPWZO7g.css","assets/pencil-Do2I7soU.js","assets/rotate-ccw-DWwjKCqh.js","assets/sparkles-DTHEIS5T.js","assets/trash-2-DaeYqnW_.js","assets/zap-BXGoxm_a.js","assets/github-dark-D7LxIVih.js","assets/github-dark-C8LL_u4z.css","assets/clipboard-D_9N0yai.js","assets/AI-DWprRWt2.css","assets/FunctionsList-CPOzZCfJ.js","assets/globe-BnBar2lO.js","assets/lock-CttMBTH5.js","assets/refresh-cw-Cn8qkf-v.js","assets/IconButton-CsCZOqWo.js","assets/Editor-C_JYWteY.js","assets/book-open-B7-cMpW6.js","assets/git-compare-Co883xFS.js","assets/settings-2-D5QtFfdZ.js","assets/key-round-BKXtbC85.js","assets/play-CmQifd74.js","assets/shield-check-piXkOtNv.js","assets/terminal-BQdlNiyt.js","assets/variable-DrK2KZuk.js","assets/Modal-BAoZams6.js","assets/rollbackDiff-DsaWcdbl.js","assets/aiPrompts-XrsFCpj_.js","assets/Input-DQ-tWGkn.js","assets/Editor-4XWCDeWw.css","assets/Deployments-CRPgafhC.js","assets/clock-CiH8bQVI.js","assets/StatusBadge-Baoe7YAb.js","assets/circle-NOse79gh.js","assets/FunctionDiff-DtVzmfsl.js","assets/dist-CR15Grce.js","assets/FunctionDiff-C-FH-cay.css","assets/KVStore-DzgumyT2.js","assets/InboundWebhooks-CFOyt9ev.js","assets/CronJobs-CDFaee6D.js","assets/square-pen-DCrjAsLy.js","assets/Jobs-CfHJpgrS.js","assets/Activity-6I8QCc07.js","assets/chevron-right-CRxFsA9Y.js","assets/InvocationsLog-Bz2zpa_f.js","assets/Traces-BEuuXgIG.js","assets/flag-CmjNKmxi.js","assets/TraceDetail-wVyyYiTv.js","assets/arrow-left-Ba3OArNt.js","assets/ApiKeys-B5PU7HpA.js","assets/time-D8OmbYzY.js","assets/Channels-fKQl2oE8.js","assets/Webhooks-D2CkyonR.js","assets/Firewall-Bpe5shll.js","assets/Firewall-Cdy796t2.css","assets/Settings-Bjj6gg_h.js","assets/Docs-BssSTQCS.js","assets/Docs-D8AUJ3IN.css","assets/NotFound-b7MSgvru.js"])))=>i.map(i=>d[i]); +import{t as e}from"./axios-DVDuIpRy.js";import{$ as t,A as n,D as r,E as i,F as a,G as o,I as s,K as c,M as l,O as u,P as d,Q as f,T as p,U as m,W as h,Z as g,_,a as v,at as y,b,c as x,ct as S,d as C,dt as w,et as T,f as E,ft as D,gt as O,h as k,ht as A,i as ee,it as j,j as te,k as M,l as N,lt as ne,m as re,mt as ie,n as ae,nt as oe,o as se,ot as ce,pt as P,q as le,r as F,rt as ue,s as I,st as de,t as fe,tt as pe,u as L,ut as me,v as he,vt as R,x as z,yt as ge}from"./runtime-core.esm-bundler-BMQPyJ_W.js";import{n as _e,t as ve}from"./pinia-B41TZNUX.js";import{n as ye,t as B}from"./client-BF51V3uE.js";(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var be=void 0,xe=typeof window<`u`&&window.trustedTypes;if(xe)try{be=xe.createPolicy(`vue`,{createHTML:e=>e})}catch{}var Se=be?e=>be.createHTML(e):e=>e,Ce=`http://www.w3.org/2000/svg`,we=`http://www.w3.org/1998/Math/MathML`,V=typeof document<`u`?document:null,Te=V&&V.createElement(`template`),Ee={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{let t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,r)=>{let i=t===`svg`?V.createElementNS(Ce,e):t===`mathml`?V.createElementNS(we,e):n?V.createElement(e,{is:n}):V.createElement(e);return e===`select`&&r&&r.multiple!=null&&i.setAttribute(`multiple`,r.multiple),i},createText:e=>V.createTextNode(e),createComment:e=>V.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>V.querySelector(e),setScopeId(e,t){e.setAttribute(t,``)},insertStaticContent(e,t,n,r,i,a){let o=n?n.previousSibling:t.lastChild;if(i&&(i===a||i.nextSibling))for(;t.insertBefore(i.cloneNode(!0),n),!(i===a||!(i=i.nextSibling)););else{Te.innerHTML=Se(r===`svg`?`${e}`:r===`mathml`?`${e}`:e);let i=Te.content;if(r===`svg`||r===`mathml`){let e=i.firstChild;for(;e.firstChild;)i.appendChild(e.firstChild);i.removeChild(e)}t.insertBefore(i,n)}return[o?o.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}},H=`transition`,De=`animation`,Oe=Symbol(`_vtc`),ke={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},Ae=T({},ae,ke),je=(e=>(e.displayName=`Transition`,e.props=Ae,e))((e,{slots:t})=>he(fe,Pe(e),t)),Me=(e,t=[])=>{j(e)?e.forEach(e=>e(...t)):e&&e(...t)},Ne=e=>e?j(e)?e.some(e=>e.length>1):e.length>1:!1;function Pe(e){let t={};for(let n in e)n in ke||(t[n]=e[n]);if(e.css===!1)return t;let{name:n=`v`,type:r,duration:i,enterFromClass:a=`${n}-enter-from`,enterActiveClass:o=`${n}-enter-active`,enterToClass:s=`${n}-enter-to`,appearFromClass:c=a,appearActiveClass:l=o,appearToClass:u=s,leaveFromClass:d=`${n}-leave-from`,leaveActiveClass:f=`${n}-leave-active`,leaveToClass:p=`${n}-leave-to`}=e,m=Fe(i),h=m&&m[0],g=m&&m[1],{onBeforeEnter:_,onEnter:v,onEnterCancelled:y,onLeave:b,onLeaveCancelled:x,onBeforeAppear:S=_,onAppear:C=v,onAppearCancelled:w=y}=t,E=(e,t,n,r)=>{e._enterCancelled=r,Le(e,t?u:s),Le(e,t?l:o),n&&n()},D=(e,t)=>{e._isLeaving=!1,Le(e,d),Le(e,p),Le(e,f),t&&t()},O=e=>(t,n)=>{let i=e?C:v,o=()=>E(t,e,n);Me(i,[t,o]),Re(()=>{Le(t,e?c:a),U(t,e?u:s),Ne(i)||Be(t,r,h,o)})};return T(t,{onBeforeEnter(e){Me(_,[e]),U(e,a),U(e,o)},onBeforeAppear(e){Me(S,[e]),U(e,c),U(e,l)},onEnter:O(!1),onAppear:O(!0),onLeave(e,t){e._isLeaving=!0;let n=()=>D(e,t);U(e,d),e._enterCancelled?(U(e,f),We(e)):(We(e),U(e,f)),Re(()=>{e._isLeaving&&(Le(e,d),U(e,p),Ne(b)||Be(e,r,g,n))}),Me(b,[e,n])},onEnterCancelled(e){E(e,!1,void 0,!0),Me(y,[e])},onAppearCancelled(e){E(e,!0,void 0,!0),Me(w,[e])},onLeaveCancelled(e){D(e),Me(x,[e])}})}function Fe(e){if(e==null)return null;if(de(e))return[Ie(e.enter),Ie(e.leave)];{let t=Ie(e);return[t,t]}}function Ie(e){return ge(e)}function U(e,t){t.split(/\s+/).forEach(t=>t&&e.classList.add(t)),(e[Oe]||(e[Oe]=new Set)).add(t)}function Le(e,t){t.split(/\s+/).forEach(t=>t&&e.classList.remove(t));let n=e[Oe];n&&(n.delete(t),n.size||(e[Oe]=void 0))}function Re(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}var ze=0;function Be(e,t,n,r){let i=e._endId=++ze,a=()=>{i===e._endId&&r()};if(n!=null)return setTimeout(a,n);let{type:o,timeout:s,propCount:c}=Ve(e,t);if(!o)return r();let l=o+`end`,u=0,d=()=>{e.removeEventListener(l,f),a()},f=t=>{t.target===e&&++u>=c&&d()};setTimeout(()=>{u(n[e]||``).split(`, `),i=r(`${H}Delay`),a=r(`${H}Duration`),o=He(i,a),s=r(`${De}Delay`),c=r(`${De}Duration`),l=He(s,c),u=null,d=0,f=0;t===H?o>0&&(u=H,d=o,f=a.length):t===De?l>0&&(u=De,d=l,f=c.length):(d=Math.max(o,l),u=d>0?o>l?H:De:null,f=u?u===H?a.length:c.length:0);let p=u===H&&/\b(?:transform|all)(?:,|$)/.test(r(`${H}Property`).toString());return{type:u,timeout:d,propCount:f,hasTransform:p}}function He(e,t){for(;e.lengthUe(t)+Ue(e[n])))}function Ue(e){return e===`auto`?0:Number(e.slice(0,-1).replace(`,`,`.`))*1e3}function We(e){return(e?e.ownerDocument:document).body.offsetHeight}function Ge(e,t,n){let r=e[Oe];r&&(t=(t?[t,...r]:[...r]).join(` `)),t==null?e.removeAttribute(`class`):n?e.setAttribute(`class`,t):e.className=t}var Ke=Symbol(`_vod`),qe=Symbol(`_vsh`),Je={name:`show`,beforeMount(e,{value:t},{transition:n}){e[Ke]=e.style.display===`none`?``:e.style.display,n&&t?n.beforeEnter(e):Ye(e,t)},mounted(e,{value:t},{transition:n}){n&&t&&n.enter(e)},updated(e,{value:t,oldValue:n},{transition:r}){!t!=!n&&(r?t?(r.beforeEnter(e),Ye(e,!0),r.enter(e)):r.leave(e,()=>{Ye(e,!1)}):Ye(e,t))},beforeUnmount(e,{value:t}){Ye(e,t)}};function Ye(e,t){e.style.display=t?e[Ke]:`none`,e[qe]=!t}var Xe=Symbol(``),Ze=/(?:^|;)\s*display\s*:/;function Qe(e,t,n){let r=e.style,i=w(n),a=!1;if(n&&!i){if(t)if(w(t))for(let e of t.split(`;`)){let t=e.slice(0,e.indexOf(`:`)).trim();n[t]??et(r,t,``)}else for(let e in t)n[e]??et(r,e,``);for(let i in n){i===`display`&&(a=!0);let o=n[i];o==null?et(r,i,``):it(e,i,!w(t)&&t?t[i]:void 0,o)||et(r,i,o)}}else if(i){if(t!==n){let e=r[Xe];e&&(n+=`;`+e),r.cssText=n,a=Ze.test(n)}}else t&&e.removeAttribute(`style`);Ke in e&&(e[Ke]=a?r.display:``,e[qe]&&(r.display=`none`))}var $e=/\s*!important$/;function et(e,t,n){if(j(n))n.forEach(n=>et(e,t,n));else if(n??=``,t.startsWith(`--`))e.setProperty(t,n);else{let r=rt(e,t);$e.test(n)?e.setProperty(pe(r),n.replace($e,``),`important`):e[r]=n}}var tt=[`Webkit`,`Moz`,`ms`],nt={};function rt(e,n){let r=nt[n];if(r)return r;let i=f(n);if(i!==`filter`&&i in e)return nt[n]=i;i=t(i);for(let t=0;tmt||=(ht.then(()=>mt=0),Date.now());function _t(e,t){let n=e=>{if(!e._vts)e._vts=Date.now();else if(e._vts<=n.attached)return;let r=n.value;if(j(r)){let n=e.stopImmediatePropagation;e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0};let i=r.slice(),a=[e];for(let n=0;ne.charCodeAt(0)===111&&e.charCodeAt(1)===110&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,yt=(e,t,n,r,i,a)=>{let o=i===`svg`;t===`class`?Ge(e,r,o):t===`style`?Qe(e,n,r):S(t)?ce(t)||ut(e,t,n,r,a):(t[0]===`.`?(t=t.slice(1),!0):t[0]===`^`?(t=t.slice(1),!1):bt(e,t,r,o))?(st(e,t,r),!e.tagName.includes(`-`)&&(t===`value`||t===`checked`||t===`selected`)&&ot(e,t,r,o,a,t!==`value`)):e._isVueCE&&(xt(e,t)||e._def.__asyncLoader&&(/[A-Z]/.test(t)||!w(r)))?st(e,f(t),r,a,t):(t===`true-value`?e._trueValue=r:t===`false-value`&&(e._falseValue=r),ot(e,t,r,o))};function bt(e,t,n,r){if(r)return!!(t===`innerHTML`||t===`textContent`||t in e&&vt(t)&&y(n));if(t===`spellcheck`||t===`draggable`||t===`translate`||t===`autocorrect`||t===`sandbox`&&e.tagName===`IFRAME`||t===`form`||t===`list`&&e.tagName===`INPUT`||t===`type`&&e.tagName===`TEXTAREA`)return!1;if(t===`width`||t===`height`){let t=e.tagName;if(t===`IMG`||t===`VIDEO`||t===`CANVAS`||t===`SOURCE`)return!1}return vt(t)&&w(n)?!1:t in e}function xt(e,t){let n=e._def.props;if(!n)return!1;let r=f(t);return Array.isArray(n)?n.some(e=>f(e)===r):Object.keys(n).some(e=>f(e)===r)}var G=e=>{let t=e.props[`onUpdate:modelValue`]||!1;return j(t)?e=>ue(t,e):t};function St(e){e.target.composing=!0}function Ct(e){let t=e.target;t.composing&&(t.composing=!1,t.dispatchEvent(new Event(`input`)))}var K=Symbol(`_assign`);function wt(e,t,n){return t&&(e=e.trim()),n&&(e=A(e)),e}var Tt={created(e,{modifiers:{lazy:t,trim:n,number:r}},i){e[K]=G(i);let a=r||i.props&&i.props.type===`number`;W(e,t?`change`:`input`,t=>{t.target.composing||e[K](wt(e.value,n,a))}),(n||a)&&W(e,`change`,()=>{e.value=wt(e.value,n,a)}),t||(W(e,`compositionstart`,St),W(e,`compositionend`,Ct),W(e,`change`,Ct))},mounted(e,{value:t}){e.value=t??``},beforeUpdate(e,{value:t,oldValue:n,modifiers:{lazy:r,trim:i,number:a}},o){if(e[K]=G(o),e.composing)return;let s=(a||e.type===`number`)&&!/^0\d/.test(e.value)?A(e.value):e.value,c=t??``;if(s===c)return;let l=e.getRootNode();(l instanceof Document||l instanceof ShadowRoot)&&l.activeElement===e&&e.type!==`range`&&(r&&t===n||i&&e.value.trim()===c)||(e.value=c)}},Et={deep:!0,created(e,t,n){e[K]=G(n),W(e,`change`,()=>{let t=e._modelValue,n=jt(e),r=e.checked,i=e[K];if(j(t)){let e=ie(t,n),a=e!==-1;if(r&&!a)i(t.concat(n));else if(!r&&a){let n=[...t];n.splice(e,1),i(n)}}else if(ne(t)){let e=new Set(t);r?e.add(n):e.delete(n),i(e)}else i(Mt(e,r))})},mounted:Dt,beforeUpdate(e,t,n){e[K]=G(n),Dt(e,t,n)}};function Dt(e,{value:t,oldValue:n},r){e._modelValue=t;let i;if(j(t))i=ie(t,r.props.value)>-1;else if(ne(t))i=t.has(r.props.value);else{if(t===n)return;i=P(t,Mt(e,!0))}e.checked!==i&&(e.checked=i)}var Ot={created(e,{value:t},n){e.checked=P(t,n.props.value),e[K]=G(n),W(e,`change`,()=>{e[K](jt(e))})},beforeUpdate(e,{value:t,oldValue:n},r){e[K]=G(r),t!==n&&(e.checked=P(t,r.props.value))}},kt={deep:!0,created(e,{value:t,modifiers:{number:n}},r){e._modelValue=t,W(e,`change`,()=>{let t=Array.prototype.filter.call(e.options,e=>e.selected).map(e=>n?A(jt(e)):jt(e));e[K](e.multiple?ne(e._modelValue)?new Set(t):t:t[0]),e._assigning=!0,z(()=>{e._assigning=!1})}),e[K]=G(r)},mounted(e,{value:t}){At(e,t)},beforeUpdate(e,{value:t},n){e._modelValue=t,e[K]=G(n)},updated(e,{value:t}){e._assigning||At(e,t)}};function At(e,t){let n=e.multiple,r=j(t);if(!(n&&!r&&!ne(t))){for(let i=0,a=e.options.length;iString(e)===String(o)):ie(t,o)>-1}else a.selected=t.has(o);else if(P(jt(a),t)){e.selectedIndex!==i&&(e.selectedIndex=i);return}}!n&&e.selectedIndex!==-1&&(e.selectedIndex=-1)}}function jt(e){return`_value`in e?e._value:e.value}function Mt(e,t){let n=t?`_trueValue`:`_falseValue`;return n in e?e[n]:t}var Nt={created(e,t,n){Ft(e,t,n,null,`created`)},mounted(e,t,n){Ft(e,t,n,null,`mounted`)},beforeUpdate(e,t,n,r){Ft(e,t,n,r,`beforeUpdate`)},updated(e,t,n,r){Ft(e,t,n,r,`updated`)}};function Pt(e,t){switch(e){case`SELECT`:return kt;case`TEXTAREA`:return Tt;default:switch(t){case`checkbox`:return Et;case`radio`:return Ot;default:return Tt}}}function Ft(e,t,n,r,i){let a=Pt(e.tagName,n.props&&n.props.type)[i];a&&a(e,t,n,r)}var It=[`ctrl`,`shift`,`alt`,`meta`],Lt={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>`button`in e&&e.button!==0,middle:e=>`button`in e&&e.button!==1,right:e=>`button`in e&&e.button!==2,exact:(e,t)=>It.some(n=>e[`${n}Key`]&&!t.includes(n))},Rt=(e,t)=>{if(!e)return e;let n=e._withMods||={},r=t.join(`.`);return n[r]||(n[r]=((n,...r)=>{for(let e=0;e{let n=e._withKeys||={},r=t.join(`.`);return n[r]||(n[r]=(n=>{if(!(`key`in n))return;let r=pe(n.key);if(t.some(e=>e===r||zt[e]===r))return e(n)}))},Vt=T({patchProp:yt},Ee),Ht;function Ut(){return Ht||=E(Vt)}var Wt=((...e)=>{let t=Ut().createApp(...e),{mount:n}=t;return t.mount=e=>{let r=Kt(e);if(!r)return;let i=t._component;!y(i)&&!i.render&&!i.template&&(i.template=r.innerHTML),r.nodeType===1&&(r.textContent=``);let a=n(r,!1,Gt(r));return r instanceof Element&&(r.removeAttribute(`v-cloak`),r.setAttribute(`data-v-app`,``)),a},t});function Gt(e){if(e instanceof SVGElement)return`svg`;if(typeof MathMLElement==`function`&&e instanceof MathMLElement)return`mathml`}function Kt(e){return w(e)?document.querySelector(e):e}var qt=e=>e===``,Jt=(...e)=>e.filter((e,t,n)=>!!e&&e.trim()!==``&&n.indexOf(e)===t).join(` `).trim(),Yt=e=>e.replace(/([a-z0-9])([A-Z])/g,`$1-$2`).toLowerCase(),Xt=e=>e.replace(/^([A-Z])|[\s-_]+(\w)/g,(e,t,n)=>n?n.toUpperCase():t.toLowerCase()),Zt=e=>{let t=Xt(e);return t.charAt(0).toUpperCase()+t.slice(1)},Qt={xmlns:`http://www.w3.org/2000/svg`,width:24,height:24,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":2,"stroke-linecap":`round`,"stroke-linejoin":`round`},$t=Symbol(`lucide-icons`);function en(){return b($t,{})}var tn=({name:e,iconNode:t,"icon-node":n,absoluteStrokeWidth:r,"absolute-stroke-width":i,strokeWidth:a,"stroke-width":o,size:s,color:c,...l},{slots:u})=>{let{size:d,color:f,strokeWidth:p=2,absoluteStrokeWidth:m=!1,class:h=``}=en(),g=I(()=>{let e=qt(r)||qt(i)||r===!0||i===!0||m===!0,t=a||o||p||Qt[`stroke-width`];return e?Number(t)*24/Number(s??d??Qt.width):t});return he(`svg`,{...Qt,...l,width:s??d??Qt.width,height:s??d??Qt.height,stroke:c??f??Qt.stroke,"stroke-width":g.value,class:Jt(`lucide`,h,...e?[`lucide-${Yt(Zt(e))}-icon`,`lucide-${Yt(e)}`]:[`lucide-icon`])},[...(t??n??[]).map(e=>he(...e)),...u.default?[u.default()]:[]])},q=(e,t)=>(n,{slots:r,attrs:i})=>he(tn,{...i,...n,iconNode:t,name:e},r.default?{default:r.default}:void 0),nn=q(`activity`,[[`path`,{d:`M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2`,key:`169zse`}]]),rn=q(`boxes`,[[`path`,{d:`M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z`,key:`lc1i9w`}],[`path`,{d:`m7 16.5-4.74-2.85`,key:`1o9zyk`}],[`path`,{d:`m7 16.5 5-3`,key:`va8pkn`}],[`path`,{d:`M7 16.5v5.17`,key:`jnp8gn`}],[`path`,{d:`M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z`,key:`8zsnat`}],[`path`,{d:`m17 16.5-5-3`,key:`8arw3v`}],[`path`,{d:`m17 16.5 4.74-2.85`,key:`8rfmw`}],[`path`,{d:`M17 16.5v5.17`,key:`k6z78m`}],[`path`,{d:`M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z`,key:`1xygjf`}],[`path`,{d:`M12 8 7.26 5.15`,key:`1vbdud`}],[`path`,{d:`m12 8 4.74-2.85`,key:`3rx089`}],[`path`,{d:`M12 13.5V8`,key:`1io7kd`}]]),an=q(`calendar-clock`,[[`path`,{d:`M16 14v2.2l1.6 1`,key:`fo4ql5`}],[`path`,{d:`M16 2v3`,key:`otl347`}],[`path`,{d:`M21 7.338V5a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h2.338`,key:`7hb8p4`}],[`path`,{d:`M3 9h5.859`,key:`numkqi`}],[`path`,{d:`M8 2v3`,key:`1ioesn`}],[`circle`,{cx:`16`,cy:`16`,r:`6`,key:`qoo3c4`}]]),on=q(`chevron-down`,[[`path`,{d:`m6 9 6 6 6-6`,key:`qrunsl`}]]),sn=q(`circle-question-mark`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3`,key:`1u773s`}],[`path`,{d:`M12 17h.01`,key:`p32p05`}]]),cn=q(`fingerprint-pattern`,[[`path`,{d:`M12 10a2 2 0 0 0-2 2c0 1.02-.1 2.51-.26 4`,key:`1nerag`}],[`path`,{d:`M14 13.12c0 2.38 0 6.38-1 8.88`,key:`o46ks0`}],[`path`,{d:`M17.29 21.02c.12-.6.43-2.3.5-3.02`,key:`ptglia`}],[`path`,{d:`M2 12a10 10 0 0 1 18-6`,key:`ydlgp0`}],[`path`,{d:`M2 16h.01`,key:`1gqxmh`}],[`path`,{d:`M21.8 16c.2-2 .131-5.354 0-6`,key:`drycrb`}],[`path`,{d:`M5 19.5C5.5 18 6 15 6 12a6 6 0 0 1 .34-2`,key:`1tidbn`}],[`path`,{d:`M8.65 22c.21-.66.45-1.32.57-2`,key:`13wd9y`}],[`path`,{d:`M9 6.8a6 6 0 0 1 9 5.2v2`,key:`1fr1j5`}]]),ln=q(`gauge`,[[`path`,{d:`m12 14 4-4`,key:`9kzdfg`}],[`path`,{d:`M3.34 19a10 10 0 1 1 17.32 0`,key:`19p75a`}]]),un=q(`library-big`,[[`rect`,{width:`8`,height:`18`,x:`3`,y:`3`,rx:`1`,key:`oynpb5`}],[`path`,{d:`M7 3v18`,key:`bbkbws`}],[`path`,{d:`M20.4 18.9c.2.5-.1 1.1-.6 1.3l-1.9.7c-.5.2-1.1-.1-1.3-.6L11.1 5.1c-.2-.5.1-1.1.6-1.3l1.9-.7c.5-.2 1.1.1 1.3.6Z`,key:`1qboyk`}]]),dn=q(`list-checks`,[[`path`,{d:`M13 5h8`,key:`a7qcls`}],[`path`,{d:`M13 12h8`,key:`h98zly`}],[`path`,{d:`M13 19h8`,key:`c3s6r1`}],[`path`,{d:`m3 17 2 2 4-4`,key:`1jhpwq`}],[`path`,{d:`m3 7 2 2 4-4`,key:`1obspn`}]]),fn=q(`list-tree`,[[`path`,{d:`M8 5h13`,key:`1pao27`}],[`path`,{d:`M13 12h8`,key:`h98zly`}],[`path`,{d:`M13 19h8`,key:`c3s6r1`}],[`path`,{d:`M3 10a2 2 0 0 0 2 2h3`,key:`1npucw`}],[`path`,{d:`M3 5v12a2 2 0 0 0 2 2h3`,key:`x1gjn2`}]]),pn=q(`menu`,[[`path`,{d:`M4 5h16`,key:`1tepv9`}],[`path`,{d:`M4 12h16`,key:`1lakjw`}],[`path`,{d:`M4 19h16`,key:`1djgab`}]]),mn=q(`messages-square`,[[`path`,{d:`M16 10a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 14.286V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z`,key:`1n2ejm`}],[`path`,{d:`M20 9a2 2 0 0 1 2 2v10.286a.71.71 0 0 1-1.212.502l-2.202-2.202A2 2 0 0 0 17.172 19H10a2 2 0 0 1-2-2v-1`,key:`1qfcsi`}]]),hn=q(`network`,[[`rect`,{x:`16`,y:`16`,width:`6`,height:`6`,rx:`1`,key:`4q2zg0`}],[`rect`,{x:`2`,y:`16`,width:`6`,height:`6`,rx:`1`,key:`8cvhb9`}],[`rect`,{x:`9`,y:`2`,width:`6`,height:`6`,rx:`1`,key:`1egb70`}],[`path`,{d:`M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3`,key:`1jsf9p`}],[`path`,{d:`M12 12V8`,key:`2874zd`}]]),gn=q(`plug`,[[`path`,{d:`M12 22v-5`,key:`1ega77`}],[`path`,{d:`M15 8V2`,key:`18g5xt`}],[`path`,{d:`M17 8a1 1 0 0 1 1 1v4a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1z`,key:`1xoxul`}],[`path`,{d:`M9 8V2`,key:`14iosj`}]]),_n=q(`plus`,[[`path`,{d:`M5 12h14`,key:`1ays0h`}],[`path`,{d:`M12 5v14`,key:`s699le`}]]),vn=q(`search`,[[`path`,{d:`m21 21-4.34-4.34`,key:`14j7rj`}],[`circle`,{cx:`11`,cy:`11`,r:`8`,key:`4ej97u`}]]),yn=q(`settings`,[[`path`,{d:`M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915`,key:`1i5ecw`}],[`circle`,{cx:`12`,cy:`12`,r:`3`,key:`1v7zrd`}]]),bn=q(`shield-half`,[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}],[`path`,{d:`M12 22V2`,key:`zs6s6o`}]]),xn=q(`triangle-alert`,[[`path`,{d:`m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3`,key:`wmoenq`}],[`path`,{d:`M12 9v4`,key:`juzpu7`}],[`path`,{d:`M12 17h.01`,key:`p32p05`}]]),Sn=q(`webhook`,[[`path`,{d:`M18 16.98h-5.99c-1.1 0-1.95.94-2.48 1.9A4 4 0 0 1 2 17c.01-.7.2-1.4.57-2`,key:`q3hayz`}],[`path`,{d:`m6 17 3.13-5.78c.53-.97.1-2.18-.5-3.1a4 4 0 1 1 6.89-4.06`,key:`1go1hn`}],[`path`,{d:`m12 6 3.13 5.73C15.66 12.7 16.9 13 18 13a4 4 0 0 1 0 8`,key:`qlwsc0`}]]),Cn=q(`x`,[[`path`,{d:`M18 6 6 18`,key:`1bl5f8`}],[`path`,{d:`m6 6 12 12`,key:`d8bk6v`}]]),wn=[`disabled`],Tn={key:0,class:`animate-spin -ml-1 mr-2 h-4 w-4`,xmlns:`http://www.w3.org/2000/svg`,fill:`none`,viewBox:`0 0 24 24`},En={__name:`Button`,props:{variant:{type:String,default:`primary`,validator:e=>[`primary`,`secondary`,`danger`,`ghost`,`chip`].includes(e)},size:{type:String,default:`md`,validator:e=>[`xs`,`sm`,`md`,`lg`].includes(e)},active:{type:Boolean,default:!1},disabled:Boolean,loading:Boolean},setup(e){let t=e,i=I(()=>{switch(t.size){case`xs`:return`h-7 px-2.5 text-xs touch-expand-xs`;case`sm`:return`h-8 px-3 text-xs touch-expand-sm`;case`lg`:return`h-12 px-6 text-base`;default:return`h-10 px-4 text-sm`}}),a=I(()=>{switch(t.variant){case`secondary`:return`bg-secondary text-secondary-foreground hover:bg-secondary/80 border border-border shadow-sm`;case`danger`:return`bg-danger text-foreground-strong border border-danger hover:bg-danger-fg focus-visible:ring-danger shadow-sm`;case`ghost`:return`bg-transparent text-foreground-muted hover:text-foreground hover:bg-surface-hover`;case`chip`:return t.active?`bg-primary text-primary-foreground border border-primary`:`bg-surface text-foreground-muted border border-border hover:text-white hover:border-foreground-muted`;default:return`bg-primary text-primary-foreground hover:bg-primary/90 focus-visible:ring-primary shadow-sm border border-transparent`}});return(t,o)=>(r(),C(`button`,{class:O([`inline-flex items-center justify-center gap-2 rounded-md font-medium transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:opacity-50 disabled:cursor-not-allowed`,i.value,a.value]),disabled:e.disabled||e.loading},[e.loading?(r(),C(`svg`,Tn,[...o[0]||=[x(`circle`,{class:`opacity-25`,cx:`12`,cy:`12`,r:`10`,stroke:`currentColor`,"stroke-width":`4`},null,-1),x(`path`,{class:`opacity-75`,fill:`currentColor`,d:`M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z`},null,-1)]])):L(``,!0),n(t.$slots,`default`)],10,wn))}},Dn=_e(`confirm`,()=>{let e=o(!1),t=o(``),n=o(``),r=o(`Confirm`),i=o(`Cancel`),a=o(!1),s=o(!1),c=o(!1),l=o(``),u=o(``),d=null;return{visible:e,title:t,message:n,confirmLabel:r,cancelLabel:i,danger:a,noticeOnly:s,promptMode:c,promptValue:l,promptPlaceholder:u,ask:(o={})=>(t.value=o.title||`Are you sure?`,n.value=o.message||``,r.value=o.confirmLabel||`Confirm`,i.value=o.cancelLabel||`Cancel`,a.value=!!o.danger,s.value=!1,c.value=!1,e.value=!0,new Promise(e=>{d=e})),notify:(i={})=>(t.value=i.title||`Notice`,n.value=i.message||``,r.value=i.confirmLabel||`OK`,a.value=!!i.danger,s.value=!0,c.value=!1,e.value=!0,new Promise(e=>{d=e})),prompt:(o={})=>(t.value=o.title||`Enter a value`,n.value=o.message||``,r.value=o.confirmLabel||`OK`,i.value=o.cancelLabel||`Cancel`,a.value=!!o.danger,s.value=!1,c.value=!0,l.value=o.defaultValue||``,u.value=o.placeholder||``,e.value=!0,new Promise(e=>{d=e})),settle:t=>{e.value=!1,d&&=(c.value?d(t?l.value:null):d(t),null),c.value=!1}}}),On=[`a[href]`,`button:not([disabled])`,`input:not([disabled]):not([type="hidden"])`,`textarea:not([disabled])`,`select:not([disabled])`,`[tabindex]:not([tabindex="-1"])`,`audio[controls]`,`video[controls]`,`details > summary`].join(`,`);function kn(e,t){let n=null,r=null,i=null,a=async()=>{n=document.activeElement instanceof HTMLElement?document.activeElement:null,i=document.getElementById(`app`),i&&i.setAttribute(`inert`,``),await z();let t=e.value;t&&((t.querySelector(`[autofocus]`)||t.querySelector(On))?.focus?.(),r=e=>{if(e.key!==`Tab`)return;let n=Array.from(t.querySelectorAll(On)).filter(e=>!e.hasAttribute(`inert`)&&e.offsetParent!==null);if(!n.length){e.preventDefault();return}let r=n[0],i=n[n.length-1],a=document.activeElement;e.shiftKey&&a===r?(e.preventDefault(),i.focus()):!e.shiftKey&&a===i&&(e.preventDefault(),r.focus())},document.addEventListener(`keydown`,r))},o=()=>{r&&=(document.removeEventListener(`keydown`,r),null),i&&=(i.removeAttribute(`inert`),null),n&&n.isConnected&&typeof n.focus==`function`&&n.focus(),n=null};d(t,e=>{e?a():o()},{immediate:!1})}var An={class:`flex items-start gap-3`},jn={class:`flex-1 min-w-0`},Mn={key:0,class:`text-sm text-foreground-muted mt-1 whitespace-pre-line break-words`},Nn=[`placeholder`],Pn={class:`flex flex-col-reverse sm:flex-row sm:justify-end gap-2 pt-2`},Fn=`confirm-dialog-title`,In={__name:`ConfirmDialog`,setup(e){let t=Dn(),n=o(null),c=o(null);kn(c,I(()=>t.visible)),d(()=>t.visible&&t.promptMode,e=>{e&&z(()=>{let e=n.value;e&&(e.focus(),window.innerWidth<640&&setTimeout(()=>{e.scrollIntoView({block:`center`,behavior:`smooth`})},50))})});let l=e=>{t.visible&&(e.key===`Escape`&&t.settle(!1),e.key===`Enter`&&!t.promptMode&&t.settle(!0))};return p(()=>window.addEventListener(`keydown`,l)),i(()=>window.removeEventListener(`keydown`,l)),(e,i)=>(r(),N(v,{to:`body`},[k(je,{name:`fade`},{default:a(()=>[g(t).visible?(r(),C(`div`,{key:0,class:`fixed inset-0 z-50 flex items-end sm:items-center justify-center bg-black/60 backdrop-blur-sm pt-safe pb-safe pl-safe pr-safe p-2 sm:p-4`,onClick:i[4]||=Rt(e=>g(t).settle(!1),[`self`]),onKeydown:i[5]||=Bt(e=>g(t).settle(!1),[`esc`])},[x(`div`,{ref_key:`dialogRoot`,ref:c,class:`w-full sm:max-w-md bg-background border border-border rounded-t-lg sm:rounded-lg shadow-xl p-5 sm:p-6 space-y-4 max-h-[calc(100dvh-1rem)] overflow-y-auto scrollable`,role:`dialog`,"aria-modal":`true`,"aria-labelledby":Fn},[x(`div`,An,[g(t).promptMode?L(``,!0):(r(),C(`div`,{key:0,class:O([`shrink-0 w-9 h-9 rounded-full flex items-center justify-center`,g(t).danger?`bg-red-500/15 text-red-400`:`bg-primary/15 text-primary`])},[g(t).danger?(r(),N(g(xn),{key:0,class:`w-5 h-5`})):(r(),N(g(sn),{key:1,class:`w-5 h-5`}))],2)),x(`div`,jn,[x(`h3`,{id:Fn,class:`text-sm font-semibold text-white tracking-tight`},R(g(t).title),1),g(t).message?(r(),C(`p`,Mn,R(g(t).message),1)):L(``,!0),g(t).promptMode?s((r(),C(`input`,{key:1,ref_key:`promptInput`,ref:n,"onUpdate:modelValue":i[0]||=e=>g(t).promptValue=e,placeholder:g(t).promptPlaceholder,type:`text`,class:`mt-3 w-full bg-background border border-border rounded-md px-3 py-2 text-base sm:text-sm text-foreground placeholder-foreground-muted/50 transition-colors duration-200 focus:outline-none focus:ring-1 focus:ring-white focus:border-white`,onKeydown:i[1]||=Bt(Rt(e=>g(t).settle(!0),[`stop`,`prevent`]),[`enter`])},null,40,Nn)),[[Tt,g(t).promptValue]]):L(``,!0)])]),x(`div`,Pn,[g(t).noticeOnly?L(``,!0):(r(),N(En,{key:0,variant:`secondary`,class:`w-full sm:w-auto`,onClick:i[2]||=e=>g(t).settle(!1)},{default:a(()=>[re(R(g(t).cancelLabel),1)]),_:1})),k(En,{variant:g(t).danger?`danger`:`primary`,class:`w-full sm:w-auto`,onClick:i[3]||=e=>g(t).settle(!0)},{default:a(()=>[re(R(g(t).confirmLabel),1)]),_:1},8,[`variant`])])],512)],32)):L(``,!0)]),_:1})]))}},Ln={__name:`App`,setup(e){return(e,t)=>{let n=te(`router-view`);return r(),C(F,null,[k(n),k(In)],64)}}};function Rn(e){return typeof e==`object`||`displayName`in e||`props`in e||`__vccOpts`in e}function zn(e){return e.__esModule||e[Symbol.toStringTag]===`Module`||e.default&&Rn(e.default)}var J=Object.assign;function Bn(e,t){let n={};for(let r in t){let i=t[r];n[r]=Y(i)?i.map(e):e(i)}return n}var Vn=()=>{},Y=Array.isArray;function Hn(e,t){let n={};for(let r in e)n[r]=r in t?t[r]:e[r];return n}var Un=Symbol(``);function Wn(e,t){return J(Error(),{type:e,[Un]:!0},t)}function X(e,t){return e instanceof Error&&Un in e&&(t==null||!!(e.type&t))}var Gn=Symbol(``),Kn=Symbol(``),qn=Symbol(``),Jn=Symbol(``),Yn=Symbol(``);function Xn(){return b(qn)}function Zn(e){return b(Jn)}var Qn=typeof document<`u`,$n=/#/g,er=/&/g,tr=/\//g,nr=/=/g,rr=/\?/g,ir=/\+/g,ar=/%5B/g,or=/%5D/g,sr=/%5E/g,cr=/%60/g,lr=/%7B/g,ur=/%7C/g,dr=/%7D/g,fr=/%20/g;function pr(e){return e==null?``:encodeURI(``+e).replace(ur,`|`).replace(ar,`[`).replace(or,`]`)}function mr(e){return pr(e).replace(lr,`{`).replace(dr,`}`).replace(sr,`^`)}function hr(e){return pr(e).replace(ir,`%2B`).replace(fr,`+`).replace($n,`%23`).replace(er,`%26`).replace(cr,"`").replace(lr,`{`).replace(dr,`}`).replace(sr,`^`)}function gr(e){return hr(e).replace(nr,`%3D`)}function _r(e){return pr(e).replace($n,`%23`).replace(rr,`%3F`)}function vr(e){return _r(e).replace(tr,`%2F`)}function yr(e){if(e==null)return null;try{return decodeURIComponent(``+e)}catch{}return``+e}var br=/\/$/,xr=e=>e.replace(br,``);function Sr(e,t,n=`/`){let r,i={},a=``,o=``,s=t.indexOf(`#`),c=t.indexOf(`?`);return c=s>=0&&c>s?-1:c,c>=0&&(r=t.slice(0,c),a=t.slice(c,s>0?s:t.length),i=e(a.slice(1))),s>=0&&(r||=t.slice(0,s),o=t.slice(s,t.length)),r=Ar(r??t,n),{fullPath:r+a+o,path:r,query:i,hash:yr(o)}}function Cr(e,t){let n=t.query?e(t.query):``;return t.path+(n&&`?`)+n+(t.hash||``)}function wr(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||`/`}function Tr(e,t,n){let r=t.matched.length-1,i=n.matched.length-1;return r>-1&&r===i&&Er(t.matched[r],n.matched[i])&&Dr(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function Er(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function Dr(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(var n in e)if(!Or(e[n],t[n]))return!1;return!0}function Or(e,t){return Y(e)?kr(e,t):Y(t)?kr(t,e):(e&&e.valueOf())===(t&&t.valueOf())}function kr(e,t){return Y(t)?e.length===t.length&&e.every((e,n)=>e===t[n]):e.length===1&&e[0]===t}function Ar(e,t){if(e.startsWith(`/`))return e;if(!e)return t;let n=t.split(`/`),r=e.split(`/`),i=r[r.length-1];(i===`..`||i===`.`)&&r.push(``);let a=n.length-1,o,s;for(o=0;o1&&a--;else break;return n.slice(0,a).join(`/`)+`/`+r.slice(o).join(`/`)}var Z={path:`/`,name:void 0,params:{},query:{},hash:``,fullPath:`/`,matched:[],meta:{},redirectedFrom:void 0};function jr(e){if(!e)if(Qn){let t=document.querySelector(`base`);e=t&&t.getAttribute(`href`)||`/`,e=e.replace(/^\w+:\/\/[^/]+/,``)}else e=`/`;return e[0]!==`/`&&e[0]!==`#`&&(e=`/`+e),xr(e)}var Mr=/^[^#]+#/;function Nr(e,t){return e.replace(Mr,`#`)+t}function Pr(e,t){let n=document.documentElement.getBoundingClientRect(),r=e.getBoundingClientRect();return{behavior:t.behavior,left:r.left-n.left-(t.left||0),top:r.top-n.top-(t.top||0)}}var Fr=()=>({left:window.scrollX,top:window.scrollY});function Ir(e){let t;if(`el`in e){let n=e.el,r=typeof n==`string`&&n.startsWith(`#`),i=typeof n==`string`?r?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!i)return;t=Pr(i,e)}else t=e;`scrollBehavior`in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left==null?window.scrollX:t.left,t.top==null?window.scrollY:t.top)}function Lr(e,t){return(history.state?history.state.position-t:-1)+e}var Rr=new Map;function zr(e,t){Rr.set(e,t)}function Br(e){let t=Rr.get(e);return Rr.delete(e),t}function Vr(e){return typeof e==`string`||e&&typeof e==`object`}function Hr(e){return typeof e==`string`||typeof e==`symbol`}function Ur(e){let t={};if(e===``||e===`?`)return t;let n=(e[0]===`?`?e.slice(1):e).split(`&`);for(let e=0;ee&&hr(e)):[r&&hr(r)]).forEach(e=>{e!==void 0&&(t+=(t.length?`&`:``)+n,e!=null&&(t+=`=`+e))})}return t}function Gr(e){let t={};for(let n in e){let r=e[n];r!==void 0&&(t[n]=Y(r)?r.map(e=>e==null?null:``+e):r==null?r:``+r)}return t}function Kr(){let e=[];function t(t){return e.push(t),()=>{let n=e.indexOf(t);n>-1&&e.splice(n,1)}}function n(){e=[]}return{add:t,list:()=>e.slice(),reset:n}}function Q(e,t,n,r,i,a=e=>e()){let o=r&&(r.enterCallbacks[i]=r.enterCallbacks[i]||[]);return()=>new Promise((s,c)=>{let l=e=>{e===!1?c(Wn(4,{from:n,to:t})):e instanceof Error?c(e):Vr(e)?c(Wn(2,{from:t,to:e})):(o&&r.enterCallbacks[i]===o&&typeof e==`function`&&o.push(e),s())},u=a(()=>e.call(r&&r.instances[i],t,n,l)),d=Promise.resolve(u);e.length<3&&(d=d.then(l)),d.catch(e=>c(e))})}function qr(e,t,n,r,i=e=>e()){let a=[];for(let o of e)for(let e in o.components){let s=o.components[e];if(!(t!==`beforeRouteEnter`&&!o.instances[e]))if(Rn(s)){let c=(s.__vccOpts||s)[t];c&&a.push(Q(c,n,r,o,e,i))}else{let c=s();a.push(()=>c.then(a=>{if(!a)throw Error(`Couldn't resolve component "${e}" at "${o.path}"`);let s=zn(a)?a.default:a;o.mods[e]=a,o.components[e]=s;let c=(s.__vccOpts||s)[t];return c&&Q(c,n,r,o,e,i)()}))}}return a}function Jr(e,t){let n=[],r=[],i=[],a=Math.max(t.matched.length,e.matched.length);for(let o=0;oEr(e,a))?r.push(a):n.push(a));let s=e.matched[o];s&&(t.matched.find(e=>Er(e,s))||i.push(s))}return[n,r,i]}var Yr=()=>location.protocol+`//`+location.host;function Xr(e,t){let{pathname:n,search:r,hash:i}=t,a=e.indexOf(`#`);if(a>-1){let t=i.includes(e.slice(a))?e.slice(a).length:1,n=i.slice(t);return n[0]!==`/`&&(n=`/`+n),wr(n,``)}return wr(n,e)+r+i}function Zr(e,t,n,r){let i=[],a=[],o=null,s=({state:a})=>{let s=Xr(e,location),c=n.value,l=t.value,u=0;if(a){if(n.value=s,t.value=a,o&&o===c){o=null;return}u=l?a.position-l.position:0}else r(s);i.forEach(e=>{e(n.value,c,{delta:u,type:`pop`,direction:u?u>0?`forward`:`back`:``})})};function c(){o=n.value}function l(e){i.push(e);let t=()=>{let t=i.indexOf(e);t>-1&&i.splice(t,1)};return a.push(t),t}function u(){if(document.visibilityState===`hidden`){let{history:e}=window;if(!e.state)return;e.replaceState(J({},e.state,{scroll:Fr()}),``)}}function d(){for(let e of a)e();a=[],window.removeEventListener(`popstate`,s),window.removeEventListener(`pagehide`,u),document.removeEventListener(`visibilitychange`,u)}return window.addEventListener(`popstate`,s),window.addEventListener(`pagehide`,u),document.addEventListener(`visibilitychange`,u),{pauseListeners:c,listen:l,destroy:d}}function Qr(e,t,n,r=!1,i=!1){return{back:e,current:t,forward:n,replaced:r,position:window.history.length,scroll:i?Fr():null}}function $r(e){let{history:t,location:n}=window,r={value:Xr(e,n)},i={value:t.state};i.value||a(r.value,{back:null,current:r.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function a(r,a,o){let s=e.indexOf(`#`),c=s>-1?(n.host&&document.querySelector(`base`)?e:e.slice(s))+r:Yr()+e+r;try{t[o?`replaceState`:`pushState`](a,``,c),i.value=a}catch(e){console.error(e),n[o?`replace`:`assign`](c)}}function o(e,n){a(e,J({},t.state,Qr(i.value.back,e,i.value.forward,!0),n,{position:i.value.position}),!0),r.value=e}function s(e,n){let o=J({},i.value,t.state,{forward:e,scroll:Fr()});a(o.current,o,!0),a(e,J({},Qr(r.value,e,null),{position:o.position+1},n),!1),r.value=e}return{location:r,state:i,push:s,replace:o}}function ei(e){e=jr(e);let t=$r(e),n=Zr(e,t.state,t.location,t.replace);function r(e,t=!0){t||n.pauseListeners(),history.go(e)}let i=J({location:``,base:e,go:r,createHref:Nr.bind(null,e)},t,n);return Object.defineProperty(i,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(i,"state",{enumerable:!0,get:()=>t.state.value}),i}var ti={type:0,value:``},ni=/[a-zA-Z0-9_]/;function ri(e){if(!e)return[[]];if(e===`/`)return[[ti]];if(!e.startsWith(`/`))throw Error(`Invalid path "${e}"`);function t(e){throw Error(`ERR (${n})/"${l}": ${e}`)}let n=0,r=n,i=[],a;function o(){a&&i.push(a),a=[]}let s=0,c,l=``,u=``;function d(){l&&=(n===0?a.push({type:0,value:l}):n===1||n===2||n===3?(a.length>1&&(c===`*`||c===`+`)&&t(`A repeatable param (${l}) must be alone in its segment. eg: '/:ids+.`),a.push({type:1,value:l,regexp:u,repeatable:c===`*`||c===`+`,optional:c===`*`||c===`?`})):t(`Invalid state to consume buffer`),``)}function f(){l+=c}for(;st.length?t.length===1&&t[0]===80?1:-1:0}function li(e,t){let n=0,r=e.score,i=t.score;for(;n0&&t[t.length-1]<0}var di={strict:!1,end:!0,sensitive:!1};function fi(e,t,n){let r=J(si(ri(e.path),n),{record:e,parent:t,children:[],alias:[]});return t&&!r.record.aliasOf==!t.record.aliasOf&&t.children.push(r),r}function pi(e,t){let n=[],r=new Map;t=Hn(di,t);function i(e){return r.get(e)}function a(e,n,r){let i=!r,s=hi(e);s.aliasOf=r&&r.record;let l=Hn(t,e),u=[s];if(`alias`in e){let t=typeof e.alias==`string`?[e.alias]:e.alias;for(let e of t)u.push(hi(J({},s,{components:r?r.record.components:s.components,path:e,aliasOf:r?r.record:s})))}let d,f;for(let t of u){let{path:u}=t;if(n&&u[0]!==`/`){let e=n.record.path,r=e[e.length-1]===`/`?``:`/`;t.path=n.record.path+(u&&r+u)}if(d=fi(t,n,l),r?r.alias.push(d):(f||=d,f!==d&&f.alias.push(d),i&&e.name&&!_i(d)&&o(e.name)),xi(d)&&c(d),s.children){let e=s.children;for(let t=0;t{o(f)}:Vn}function o(e){if(Hr(e)){let t=r.get(e);t&&(r.delete(e),n.splice(n.indexOf(t),1),t.children.forEach(o),t.alias.forEach(o))}else{let t=n.indexOf(e);t>-1&&(n.splice(t,1),e.record.name&&r.delete(e.record.name),e.children.forEach(o),e.alias.forEach(o))}}function s(){return n}function c(e){let t=yi(e,n);n.splice(t,0,e),e.record.name&&!_i(e)&&r.set(e.record.name,e)}function l(e,t){let i,a={},o,s;if(`name`in e&&e.name){if(i=r.get(e.name),!i)throw Wn(1,{location:e});s=i.record.name,a=J(mi(t.params,i.keys.filter(e=>!e.optional).concat(i.parent?i.parent.keys.filter(e=>e.optional):[]).map(e=>e.name)),e.params&&mi(e.params,i.keys.map(e=>e.name))),o=i.stringify(a)}else if(e.path!=null)o=e.path,i=n.find(e=>e.re.test(o)),i&&(a=i.parse(o),s=i.record.name,i.keys.forEach(e=>{e.optional&&!a[e.name]&&delete a[e.name]}));else{if(i=t.name?r.get(t.name):n.find(e=>e.re.test(t.path)),!i)throw Wn(1,{location:e,currentLocation:t});s=i.record.name,a=J({},t.params,e.params),o=i.stringify(a)}let c=[],l=i;for(;l;)c.unshift(l.record),l=l.parent;return{name:s,path:o,params:a,matched:c,meta:vi(c)}}e.forEach(e=>a(e));function u(){n.length=0,r.clear()}return{addRoute:a,resolve:l,removeRoute:o,clearRoutes:u,getRoutes:s,getRecordMatcher:i}}function mi(e,t){let n={};for(let r of t)r in e&&(n[r]=e[r]);return n}function hi(e){let t={path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:e.aliasOf,beforeEnter:e.beforeEnter,props:gi(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:`components`in e?e.components||null:e.component&&{default:e.component}};return Object.defineProperty(t,"mods",{value:{}}),t}function gi(e){let t={},n=e.props||!1;if(`component`in e)t.default=n;else for(let r in e.components)t[r]=typeof n==`object`?n[r]:n;return t}function _i(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function vi(e){return e.reduce((e,t)=>J(e,t.meta),{})}function yi(e,t){let n=0,r=t.length;for(;n!==r;){let i=n+r>>1;li(e,t[i])<0?r=i:n=i+1}let i=bi(e);return i&&(r=t.lastIndexOf(i,r-1)),r}function bi(e){let t=e;for(;t=t.parent;)if(xi(t)&&li(e,t)===0)return t}function xi({record:e}){return!!(e.name||e.components&&Object.keys(e.components).length||e.redirect)}function Si(e){let t=b(qn),n=b(Jn),r=I(()=>{let n=g(e.to);return t.resolve(n)}),i=I(()=>{let{matched:e}=r.value,{length:t}=e,i=e[t-1],a=n.matched;if(!i||!a.length)return-1;let o=a.findIndex(Er.bind(null,i));if(o>-1)return o;let s=Di(e[t-2]);return t>1&&Di(i)===s&&a[a.length-1].path!==s?a.findIndex(Er.bind(null,e[t-2])):o}),a=I(()=>i.value>-1&&Ei(n.params,r.value.params)),o=I(()=>i.value>-1&&i.value===n.matched.length-1&&Dr(n.params,r.value.params));function s(n={}){if(Ti(n)){let n=t[g(e.replace)?`replace`:`push`](g(e.to)).catch(Vn);return e.viewTransition&&typeof document<`u`&&`startViewTransition`in document&&document.startViewTransition(()=>n),n}return Promise.resolve()}return{route:r,href:I(()=>r.value.href),isActive:a,isExactActive:o,navigate:s}}function Ci(e){return e.length===1?e[0]:e}var wi=_({name:`RouterLink`,compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:`page`},viewTransition:Boolean},useLink:Si,setup(e,{slots:t}){let n=m(Si(e)),{options:r}=b(qn),i=I(()=>({[Oi(e.activeClass,r.linkActiveClass,`router-link-active`)]:n.isActive,[Oi(e.exactActiveClass,r.linkExactActiveClass,`router-link-exact-active`)]:n.isExactActive}));return()=>{let r=t.default&&Ci(t.default(n));return e.custom?r:he(`a`,{"aria-current":n.isExactActive?e.ariaCurrentValue:null,href:n.href,onClick:n.navigate,class:i.value},r)}}});function Ti(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&(e.button===void 0||e.button===0)){if(e.currentTarget&&e.currentTarget.getAttribute){let t=e.currentTarget.getAttribute(`target`);if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function Ei(e,t){for(let n in t){let r=t[n],i=e[n];if(typeof r==`string`){if(r!==i)return!1}else if(!Y(i)||i.length!==r.length||r.some((e,t)=>e.valueOf()!==i[t].valueOf()))return!1}return!0}function Di(e){return e?e.aliasOf?e.aliasOf.path:e.path:``}var Oi=(e,t,n)=>e??t??n,ki=_({name:`RouterView`,inheritAttrs:!1,props:{name:{type:String,default:`default`},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:n}){let r=b(Yn),i=I(()=>e.route||r.value),a=b(Kn,0),s=I(()=>{let e=g(a),{matched:t}=i.value,n;for(;(n=t[e])&&!n.components;)e++;return e}),c=I(()=>i.value.matched[s.value]);u(Kn,I(()=>s.value+1)),u(Gn,c),u(Yn,i);let l=o();return d(()=>[l.value,c.value,e.name],([e,t,n],[r,i,a])=>{t&&(t.instances[n]=e,i&&i!==t&&e&&e===r&&(t.leaveGuards.size||(t.leaveGuards=i.leaveGuards),t.updateGuards.size||(t.updateGuards=i.updateGuards))),e&&t&&(!i||!Er(t,i)||!r)&&(t.enterCallbacks[n]||[]).forEach(t=>t(e))},{flush:`post`}),()=>{let r=i.value,a=e.name,o=c.value,s=o&&o.components[a];if(!s)return Ai(n.default,{Component:s,route:r});let u=o.props[a],d=u?u===!0?r.params:typeof u==`function`?u(r):u:null,f=he(s,J({},d,t,{onVnodeUnmounted:e=>{e.component.isUnmounted&&(o.instances[a]=null)},ref:l}));return Ai(n.default,{Component:f,route:r})||f}}});function Ai(e,t){if(!e)return null;let n=e(t);return n.length===1?n[0]:n}var ji=ki;function Mi(e){let t=pi(e.routes,e),n=e.parseQuery||Ur,r=e.stringifyQuery||Wr,i=e.history,a=Kr(),o=Kr(),s=Kr(),l=le(Z),u=Z;Qn&&e.scrollBehavior&&`scrollRestoration`in history&&(history.scrollRestoration=`manual`);let d=Bn.bind(null,e=>``+e),f=Bn.bind(null,vr),p=Bn.bind(null,yr);function m(e,n){let r,i;return Hr(e)?(r=t.getRecordMatcher(e),i=n):i=e,t.addRoute(i,r)}function h(e){let n=t.getRecordMatcher(e);n&&t.removeRoute(n)}function _(){return t.getRoutes().map(e=>e.record)}function v(e){return!!t.getRecordMatcher(e)}function y(e,a){if(a=J({},a||l.value),typeof e==`string`){let r=Sr(n,e,a.path),o=t.resolve({path:r.path},a),s=i.createHref(r.fullPath);return J(r,o,{params:p(o.params),redirectedFrom:void 0,href:s})}let o;if(e.path!=null)o=J({},e,{path:Sr(n,e.path,a.path).path});else{let t=J({},e.params);for(let e in t)t[e]??delete t[e];o=J({},e,{params:f(t)}),a.params=f(a.params)}let s=t.resolve(o,a),c=e.hash||``;s.params=d(p(s.params));let u=Cr(r,J({},e,{hash:mr(c),path:s.path})),m=i.createHref(u);return J({fullPath:u,hash:c,query:r===Wr?Gr(e.query):e.query||{}},s,{redirectedFrom:void 0,href:m})}function b(e){return typeof e==`string`?Sr(n,e,l.value.path):J({},e)}function x(e,t){if(u!==e)return Wn(8,{from:t,to:e})}function S(e){return T(e)}function C(e){return S(J(b(e),{replace:!0}))}function w(e,t){let n=e.matched[e.matched.length-1];if(n&&n.redirect){let{redirect:r}=n,i=typeof r==`function`?r(e,t):r;return typeof i==`string`&&(i=i.includes(`?`)||i.includes(`#`)?i=b(i):{path:i},i.params={}),J({query:e.query,hash:e.hash,params:i.path==null?e.params:{}},i)}}function T(e,t){let n=u=y(e),i=l.value,a=e.state,o=e.force,s=e.replace===!0,c=w(n,i);if(c)return T(J(b(c),{state:typeof c==`object`?J({},a,c.state):a,force:o,replace:s}),t||n);let d=n;d.redirectedFrom=t;let f;return!o&&Tr(r,i,n)&&(f=Wn(16,{to:d,from:i}),ae(i,i,!0,!1)),(f?Promise.resolve(f):O(d,i)).catch(e=>X(e)?X(e,2)?e:ie(e):ne(e,d,i)).then(e=>{if(e){if(X(e,2))return T(J({replace:s},b(e.to),{state:typeof e.to==`object`?J({},a,e.to.state):a,force:o}),t||d)}else e=A(d,i,!0,s,a);return k(d,i,e),e})}function E(e,t){let n=x(e,t);return n?Promise.reject(n):Promise.resolve()}function D(e){let t=ce.values().next().value;return t&&typeof t.runWithContext==`function`?t.runWithContext(e):e()}function O(e,t){let n,[r,i,s]=Jr(e,t);n=qr(r.reverse(),`beforeRouteLeave`,e,t);for(let i of r)i.leaveGuards.forEach(r=>{n.push(Q(r,e,t))});let c=E.bind(null,e,t);return n.push(c),F(n).then(()=>{n=[];for(let r of a.list())n.push(Q(r,e,t));return n.push(c),F(n)}).then(()=>{n=qr(i,`beforeRouteUpdate`,e,t);for(let r of i)r.updateGuards.forEach(r=>{n.push(Q(r,e,t))});return n.push(c),F(n)}).then(()=>{n=[];for(let r of s)if(r.beforeEnter)if(Y(r.beforeEnter))for(let i of r.beforeEnter)n.push(Q(i,e,t));else n.push(Q(r.beforeEnter,e,t));return n.push(c),F(n)}).then(()=>(e.matched.forEach(e=>e.enterCallbacks={}),n=qr(s,`beforeRouteEnter`,e,t,D),n.push(c),F(n))).then(()=>{n=[];for(let r of o.list())n.push(Q(r,e,t));return n.push(c),F(n)}).catch(e=>X(e,8)?e:Promise.reject(e))}function k(e,t,n){s.list().forEach(r=>D(()=>r(e,t,n)))}function A(e,t,n,r,a){let o=x(e,t);if(o)return o;let s=t===Z,c=Qn?history.state:{};n&&(r||s?i.replace(e.fullPath,J({scroll:s&&c&&c.scroll},a)):i.push(e.fullPath,a)),l.value=e,ae(e,t,n,s),ie()}let ee;function j(){ee||=i.listen((e,t,n)=>{if(!P.listening)return;let r=y(e),a=w(r,P.currentRoute.value);if(a){T(J(a,{replace:!0,force:!0}),r).catch(Vn);return}u=r;let o=l.value;Qn&&zr(Lr(o.fullPath,n.delta),Fr()),O(r,o).catch(e=>X(e,12)?e:X(e,2)?(T(J(b(e.to),{force:!0}),r).then(e=>{X(e,20)&&!n.delta&&n.type===`pop`&&i.go(-1,!1)}).catch(Vn),Promise.reject()):(n.delta&&i.go(-n.delta,!1),ne(e,r,o))).then(e=>{e||=A(r,o,!1),e&&(n.delta&&!X(e,8)?i.go(-n.delta,!1):n.type===`pop`&&X(e,20)&&i.go(-1,!1)),k(r,o,e)}).catch(Vn)})}let te=Kr(),M=Kr(),N;function ne(e,t,n){ie(e);let r=M.list();return r.length?r.forEach(r=>r(e,t,n)):console.error(e),Promise.reject(e)}function re(){return N&&l.value!==Z?Promise.resolve():new Promise((e,t)=>{te.add([e,t])})}function ie(e){return N||(N=!e,j(),te.list().forEach(([t,n])=>e?n(e):t()),te.reset()),e}function ae(t,n,r,i){let{scrollBehavior:a}=e;if(!Qn||!a)return Promise.resolve();let o=!r&&Br(Lr(t.fullPath,0))||(i||!r)&&history.state&&history.state.scroll||null;return z().then(()=>a(t,n,o)).then(e=>t===l.value&&e&&Ir(e)).catch(e=>t===l.value&&ne(e,t,n))}let oe=e=>i.go(e),se,ce=new Set,P={currentRoute:l,listening:!0,addRoute:m,removeRoute:h,clearRoutes:t.clearRoutes,hasRoute:v,getRoutes:_,resolve:y,options:e,push:S,replace:C,go:oe,back:()=>oe(-1),forward:()=>oe(1),beforeEach:a.add,beforeResolve:o.add,afterEach:s.add,onError:M.add,isReady:re,install(e){e.component(`RouterLink`,wi),e.component(`RouterView`,ji),e.config.globalProperties.$router=P,Object.defineProperty(e.config.globalProperties,"$route",{enumerable:!0,get:()=>g(l)}),Qn&&!se&&l.value===Z&&(se=!0,S(i.location).catch(e=>{}));let t={};for(let e in Z)Object.defineProperty(t,e,{get:()=>l.value[e],enumerable:!0});e.provide(qn,P),e.provide(Jn,c(t)),e.provide(Yn,l);let n=e.unmount;ce.add(e),e.unmount=function(){ce.delete(e),ce.size<1&&(u=Z,ee&&ee(),ee=null,l.value=Z,se=!1,N=!1),n()}}};function F(e){return e.reduce((e,t)=>e.then(()=>D(t)),Promise.resolve())}return P}var Ni=e.create({baseURL:``,timeout:3e4,withCredentials:!0,headers:{"Content-Type":`application/json`}}),Pi=12;try{localStorage.removeItem(`orva.hasUser`)}catch{}var Fi=_e(`auth`,()=>{let e=o(null),t=o(!1),n=o(!1),r=o(null),i=o(null),a=o(!1),s=o(0),c=e=>{r.value=e},l=async(r,i)=>{n.value=!0;try{let n=await Ni.post(`/api/v1/auth/login`,{username:r,password:i});return e.value=n.data.user,t.value=!0,c(!0),{success:!0}}catch(e){return{success:!1,error:e.response?.data?.error?.message||`Login failed`}}finally{n.value=!1}},u=async(r,i)=>{n.value=!0;try{let n=await Ni.post(`/api/v1/auth/onboard`,{username:r,password:i});return e.value=n.data.user,t.value=!0,c(!0),{success:!0}}catch(e){return{success:!1,error:e.response?.data?.error?.message||`Setup failed`}}finally{n.value=!1}},d=async()=>{try{await Ni.post(`/api/v1/auth/logout`)}catch{}e.value=null,t.value=!1},f=async({force:e=!1}={})=>{if(!e&&r.value!==null)return r.value;try{let e=await Ni.get(`/api/v1/auth/status`);return c(!!e.data.has_user),r.value}catch{return r.value===null&&c(!0),r.value}},p=async()=>{try{let n=await Ni.get(`/api/v1/auth/me`);return e.value=n.data,t.value=!0,c(!0),i.value=n.data.expires_at||null,!0}catch{return e.value=null,t.value=!1,i.value=null,!1}},m=async()=>{a.value=!0;try{let e=await Ni.post(`/api/v1/auth/refresh`);return i.value=e.data.expires_at||null,{success:!0}}catch(e){return t.value=!1,i.value=null,{success:!1,error:e.response?.data?.error?.message||`Refresh failed`}}finally{a.value=!1}},h=I(()=>i.value?(new Date(i.value).getTime()-Date.now())/1e3:null);return{user:e,isAuthenticated:t,loading:n,hasUser:r,expiresAt:i,refreshing:a,secondsUntilExpiry:h,shouldShowExpiryToast:I(()=>{if(!t.value)return!1;let e=h.value;return e==null||e<=0||Date.now(){s.value=Date.now()+36e5},changePassword:async(e,t)=>{await Ni.post(`/api/v1/auth/change-password`,{old_password:e,new_password:t})}}}),Ii=(e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n},Li={},Ri={viewBox:`0 0 32 32`,fill:`none`,xmlns:`http://www.w3.org/2000/svg`,class:`text-primary`};function zi(e,t){return r(),C(`svg`,Ri,[...t[0]||=[x(`rect`,{width:`32`,height:`32`,rx:`8`,fill:`currentColor`},null,-1),x(`text`,{x:`16`,y:`21`,"font-family":`'Inter', system-ui, sans-serif`,"font-size":`13`,"font-weight":`700`,fill:`white`,"text-anchor":`middle`,"letter-spacing":`-0.5`},` f(x) `,-1)]])}var Bi=Ii(Li,[[`render`,zi]]),Vi={class:`lg:hidden fixed top-0 inset-x-0 h-14 bg-background border-b border-border z-30 flex items-center justify-between px-4 pt-safe pl-safe pr-safe`},Hi={class:`flex items-center gap-2 text-white font-mono`},Ui=[`aria-label`,`aria-expanded`],Wi={class:`h-16 flex items-center px-6 border-b border-border`},Gi={class:`flex items-center gap-3 text-white font-mono tracking-tight text-lg`},Ki={class:`flex-1 p-3 space-y-1 overflow-y-auto scrollable`},qi=[`aria-expanded`,`aria-controls`,`onClick`],Ji={class:`flex-1 text-left`},Yi=[`id`],Xi={class:`mt-2 space-y-1 border-t border-border pt-2`},Zi={__name:`Sidebar`,setup(e){let t=Zn(),n=o(!1),i=o(null),c=o(null),u=e=>e===`/`?t.path===`/`:t.path.startsWith(e),f=[{path:`/`,label:`Overview`,icon:ln},{path:`/ai`,label:`Chat`,icon:mn},{path:`/functions`,label:`Functions`,icon:rn}],p=[{id:`automation`,label:`Automation`,icon:an,items:[{path:`/cron`,label:`Schedules`,icon:an},{path:`/jobs`,label:`Jobs`,icon:dn}]},{id:`observe`,label:`Observe`,icon:nn,items:[{path:`/activity`,label:`Activity`,icon:nn},{path:`/invocations`,label:`Invocations`,icon:fn},{path:`/traces`,label:`Traces`,icon:hn}]},{id:`connect`,label:`Connect`,icon:gn,items:[{path:`/api-keys`,label:`Keys`,icon:cn},{path:`/channels`,label:`Channels`,icon:gn},{path:`/webhooks`,label:`Webhooks`,icon:Sn},{path:`/firewall`,label:`Egress`,icon:bn}]}],m=[{path:`/settings`,label:`Settings`,icon:yn},{path:`/docs`,label:`Docs`,icon:un}],h=o(Object.fromEntries(p.map(e=>[e.id,!1]))),_=e=>e.items.some(e=>u(e.path)),v=()=>{let e=p.find(_);e&&(h.value[e.id]=!0)};v(),d(()=>t.fullPath,()=>{n.value=!1,v()}),d(n,async e=>{await z(),e?(i.value?.querySelector(`a[href]`))?.focus?.():i.value?.contains(document.activeElement)&&c.value?.focus?.()});let y=0,b=0,S=!1,w=e=>{if(window.innerWidth>=1024||!n.value)return;let t=e.touches[0];y=t.clientX,b=t.clientY,S=!0},T=e=>{if(!S)return;let t=e.touches[0],r=t.clientX-y,i=Math.abs(t.clientY-b);r<-60&&i<40&&(n.value=!1,S=!1)},E=()=>{S=!1};return(e,t)=>{let o=te(`router-link`);return r(),C(F,null,[x(`header`,Vi,[x(`div`,Hi,[k(Bi,{class:`w-6 h-6`}),t[5]||=x(`span`,{class:`font-bold tracking-tight`},`Orva`,-1)]),x(`button`,{ref_key:`toggleBtn`,ref:c,class:`p-2 rounded-md text-foreground-muted hover:text-white hover:bg-surface transition-colors touch-expand-iconbtn`,"aria-label":n.value?`Close menu`:`Open menu`,"aria-expanded":n.value,"aria-controls":`primary-navigation`,onClick:t[0]||=e=>n.value=!n.value},[n.value?(r(),N(g(Cn),{key:1,class:`w-5 h-5`})):(r(),N(g(pn),{key:0,class:`w-5 h-5`}))],8,Ui)]),k(je,{name:`fade`},{default:a(()=>[n.value?(r(),C(`div`,{key:0,class:`lg:hidden fixed inset-0 bg-black/50 z-30 backdrop-blur-sm`,onClick:t[1]||=e=>n.value=!1})):L(``,!0)]),_:1}),x(`aside`,{id:`primary-navigation`,ref_key:`drawerEl`,ref:i,class:O([`bg-background border-r border-border flex flex-col h-full shrink-0 z-40 w-64 lg:w-52 fixed inset-y-0 left-0 transform transition-transform duration-150 ease-out lg:static lg:translate-x-0 lg:transform-none lg:transition-none pt-safe pb-safe pl-safe`,n.value?`translate-x-0`:`-translate-x-full lg:translate-x-0`]),onTouchstart:w,onTouchmove:T,onTouchend:E},[x(`div`,Wi,[x(`div`,Gi,[k(Bi,{class:`w-8 h-8`}),t[6]||=x(`span`,{class:`font-bold tracking-tight text-white`},`Orva`,-1)])]),x(`nav`,Ki,[(r(),C(F,null,M(f,e=>k(o,{key:e.path,to:e.path,class:O([`flex items-center gap-3 px-3 py-2.5 rounded-md text-sm transition-colors duration-150 group font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary`,u(e.path)?`text-white bg-primary/15`:`text-foreground-muted hover:text-white hover:bg-surface-hover`]),onClick:t[2]||=e=>n.value=!1},{default:a(()=>[(r(),N(l(e.icon),{class:O([`w-4 h-4 transition-colors`,u(e.path)?`text-white`:`text-foreground-muted group-hover:text-white`])},null,8,[`class`])),x(`span`,null,R(e.label),1)]),_:2},1032,[`to`,`class`])),64)),(r(),C(F,null,M(p,e=>x(`div`,{key:e.id,class:`pt-1`},[x(`button`,{type:`button`,class:O([`flex w-full items-center gap-3 rounded-md px-3 py-2.5 text-sm font-medium text-foreground-muted transition-colors hover:bg-surface-hover hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary`,_(e)?`text-white`:``]),"aria-expanded":h.value[e.id],"aria-controls":`nav-group-${e.id}`,onClick:t=>h.value[e.id]=!h.value[e.id]},[(r(),N(l(e.icon),{class:`h-4 w-4`})),x(`span`,Ji,R(e.label),1),k(g(on),{class:O([`h-3.5 w-3.5 transition-transform`,h.value[e.id]?`rotate-0`:`-rotate-90`])},null,8,[`class`])],10,qi),s(x(`div`,{id:`nav-group-${e.id}`,class:`ml-3 mt-1 space-y-0.5 border-l border-border pl-2`},[(r(!0),C(F,null,M(e.items,e=>(r(),N(o,{key:e.path,to:e.path,class:O([`flex items-center gap-3 px-3 py-2 rounded-md text-sm transition-colors duration-150 group font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary`,u(e.path)?`text-white bg-primary/15`:`text-foreground-muted hover:text-white hover:bg-surface-hover`]),onClick:t[3]||=e=>n.value=!1},{default:a(()=>[(r(),N(l(e.icon),{class:`h-4 w-4`})),x(`span`,null,R(e.label),1)]),_:2},1032,[`to`,`class`]))),128))],8,Yi),[[Je,h.value[e.id]]])])),64)),x(`div`,Xi,[(r(),C(F,null,M(m,e=>k(o,{key:e.path,to:e.path,class:O([`flex items-center gap-3 px-3 py-2.5 rounded-md text-sm transition-colors duration-150 group font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary`,u(e.path)?`text-white bg-primary/15`:`text-foreground-muted hover:text-white hover:bg-surface-hover`]),onClick:t[4]||=e=>n.value=!1},{default:a(()=>[(r(),N(l(e.icon),{class:`h-4 w-4`})),x(`span`,null,R(e.label),1)]),_:2},1032,[`to`,`class`])),64))])])],34)],64)}}},Qi={key:0,class:`fixed z-50 bg-background border border-border shadow-lg rounded-lg p-4 flex items-start gap-3 inset-x-2 bottom-2 max-w-sm mx-auto pb-safe sm:inset-x-auto sm:bottom-6 sm:right-6 sm:mx-0 sm:pb-4`},$i={class:`flex-1 min-w-0`},ea={key:0,class:`text-sm font-medium text-white mb-0.5`},ta={class:`text-xs text-foreground-muted`},na={class:`flex flex-col gap-2 shrink-0`},ra=[`disabled`],ia=Ii(Object.assign({name:`CommonToast`},{__name:`Toast`,props:{visible:{type:Boolean,default:!1},title:{type:String,default:``},actionLabel:{type:String,default:``},actionLoading:{type:Boolean,default:!1},dismissible:{type:Boolean,default:!0}},emits:[`action`,`dismiss`],setup(e){return(t,i)=>(r(),N(v,{to:`body`},[k(je,{name:`toast`},{default:a(()=>[e.visible?(r(),C(`div`,Qi,[x(`div`,$i,[e.title?(r(),C(`div`,ea,R(e.title),1)):L(``,!0),x(`div`,ta,[n(t.$slots,`default`,{},void 0,!0)])]),x(`div`,na,[e.actionLabel?(r(),C(`button`,{key:0,class:`px-3 py-1 rounded text-xs font-medium bg-white text-black hover:bg-foreground-muted transition-colors`,disabled:e.actionLoading,onClick:i[0]||=e=>t.$emit(`action`)},R(e.actionLoading?`…`:e.actionLabel),9,ra)):L(``,!0),e.dismissible?(r(),C(`button`,{key:1,class:`text-foreground-muted hover:text-white text-xs`,onClick:i[1]||=e=>t.$emit(`dismiss`)},` Dismiss `)):L(``,!0)])])):L(``,!0)]),_:3})]))}}),[[`__scopeId`,`data-v-1cbabcec`]]),aa={class:`flex items-center gap-2 px-4 py-3 border-b border-border`},oa=[`aria-selected`,`onClick`,`onMouseenter`],sa={class:`flex-1 truncate`},ca={key:0,class:`hidden sm:inline-flex items-center gap-1 text-[10px] font-mono text-foreground-muted`},la={key:0,class:`px-4 py-6 text-center text-sm text-foreground-muted`},ua=800,da={__name:`CommandPalette`,setup(e,{expose:t}){let n=Xn(),c=o(!1),u=o(``),f=o(0),m=o(null),h=o(null),_=o(null);kn(_,c);let y=[{id:`fn-new`,label:`New function`,icon:_n,action:()=>n.push(`/functions/new`),shortcut:[`c`,`n`]},{id:`go-fns`,label:`Functions`,icon:rn,action:()=>n.push(`/functions`),shortcut:[`g`,`f`]},{id:`go-inv`,label:`Invocations`,icon:fn,action:()=>n.push(`/invocations`),shortcut:[`g`,`i`]},{id:`go-jobs`,label:`Jobs`,icon:dn,action:()=>n.push(`/jobs`),shortcut:[`g`,`j`]},{id:`go-cron`,label:`Schedules`,icon:an,action:()=>n.push(`/cron`)},{id:`go-activity`,label:`Activity`,icon:nn,action:()=>n.push(`/activity`)},{id:`go-traces`,label:`Traces`,icon:hn,action:()=>n.push(`/traces`)},{id:`go-keys`,label:`API Keys`,icon:cn,action:()=>n.push(`/api-keys`)},{id:`go-channels`,label:`Channels`,icon:gn,action:()=>n.push(`/channels`)},{id:`go-hooks`,label:`Webhooks`,icon:Sn,action:()=>n.push(`/webhooks`)},{id:`go-fw`,label:`Egress`,icon:bn,action:()=>n.push(`/firewall`),keywords:`firewall blocklist dns`},{id:`go-settings`,label:`Settings`,icon:yn,action:()=>n.push(`/settings`)},{id:`go-docs`,label:`Docs`,icon:un,action:()=>n.push(`/docs`)},{id:`go-overview`,label:`Overview`,icon:ln,action:()=>n.push(`/`)}],b=I(()=>{let e=u.value.trim().toLowerCase();return e?y.filter(t=>t.label.toLowerCase().includes(e)||(t.keywords||``).includes(e)):y});d(b,()=>{f.value=0});let S=e=>{let t=b.value.length;t&&(f.value=(f.value+e+t)%t,z(()=>{(h.value?.querySelectorAll(`li[role="option"]`)[f.value])?.scrollIntoView?.({block:`nearest`})}))},w=e=>{e&&(T(),z(()=>e.action()))},T=()=>{c.value=!1,u.value=``,f.value=0},E=()=>{c.value=!0,z(()=>m.value?.focus())},D=``,A=null,ee=e=>{if(!e)return!1;let t=e.tagName;return!!(t===`INPUT`||t===`TEXTAREA`||t===`SELECT`||e.isContentEditable)},j=e=>{if((e.metaKey||e.ctrlKey)&&e.key.toLowerCase()===`k`){e.preventDefault(),c.value?T():E();return}if((e.metaKey||e.ctrlKey)&&e.key.toLowerCase()===`s`&&window.location.pathname.includes(`/functions/`)){e.preventDefault(),window.dispatchEvent(new CustomEvent(`orva:deploy`));return}if(e.metaKey||e.ctrlKey||e.altKey||ee(document.activeElement))return;if(!D){if(e.key===`g`||e.key===`c`){D=e.key,clearTimeout(A),A=setTimeout(()=>{D=``},ua);return}return}let t=D+e.key;D=``,clearTimeout(A);let n=y.find(e=>e.shortcut&&e.shortcut.join(``)===t);n&&(e.preventDefault(),n.action())};return p(()=>{window.addEventListener(`keydown`,j)}),i(()=>{window.removeEventListener(`keydown`,j),clearTimeout(A)}),t({show:E,close:T}),(e,t)=>(r(),N(v,{to:`body`},[k(je,{name:`fade`},{default:a(()=>[c.value?(r(),C(`div`,{key:0,class:`fixed inset-0 z-50 flex items-start justify-center bg-black/60 backdrop-blur-sm pt-[10vh] sm:pt-[15vh] px-4`,onClick:Rt(T,[`self`])},[x(`div`,{ref_key:`dialogRoot`,ref:_,class:`w-full max-w-lg bg-background border border-border rounded-lg shadow-xl overflow-hidden`,role:`dialog`,"aria-modal":`true`,"aria-labelledby":`command-palette-label`},[x(`div`,aa,[k(g(vn),{class:`w-4 h-4 text-foreground-muted shrink-0`}),t[4]||=x(`span`,{id:`command-palette-label`,class:`sr-only`},`Command palette`,-1),s(x(`input`,{ref_key:`searchInput`,ref:m,"onUpdate:modelValue":t[0]||=e=>u.value=e,type:`text`,placeholder:`Search routes, actions…`,class:`flex-1 bg-transparent border-0 text-base sm:text-sm text-white placeholder-foreground-muted focus:outline-none`,onKeydown:[t[1]||=Bt(Rt(e=>S(1),[`prevent`]),[`down`]),t[2]||=Bt(Rt(e=>S(-1),[`prevent`]),[`up`]),t[3]||=Bt(Rt(e=>w(b.value[f.value]),[`prevent`]),[`enter`]),Bt(T,[`esc`])]},null,544),[[Tt,u.value]]),t[5]||=x(`kbd`,{class:`hidden sm:inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-mono text-foreground-muted bg-surface border border-border`},`esc`,-1)]),x(`ul`,{ref_key:`listRef`,ref:h,class:`max-h-[60dvh] overflow-y-auto scrollable py-1`,role:`listbox`},[(r(!0),C(F,null,M(b.value,(e,t)=>(r(),C(`li`,{key:e.id,role:`option`,"aria-selected":t===f.value,class:O([`flex items-center gap-3 px-4 py-2.5 min-h-[44px] text-base sm:text-sm cursor-pointer`,t===f.value?`bg-primary/15 text-white`:`text-foreground hover:bg-surface-hover`]),onClick:t=>w(e),onMouseenter:e=>f.value=t},[(r(),N(l(e.icon),{class:`w-4 h-4 shrink-0 text-foreground-muted`})),x(`span`,sa,R(e.label),1),e.shortcut?(r(),C(`span`,ca,[(r(!0),C(F,null,M(e.shortcut,e=>(r(),C(`kbd`,{key:e,class:`px-1.5 py-0.5 rounded bg-surface border border-border`},R(e),1))),128))])):L(``,!0)],42,oa))),128)),b.value.length?L(``,!0):(r(),C(`li`,la,` Nothing matches "`+R(u.value)+`". `,1))],512),t[6]||=x(`div`,{class:`px-4 py-2 border-t border-border bg-surface/40 flex items-center justify-between text-[10px] text-foreground-muted`},[x(`span`,{class:`flex items-center gap-2`},[x(`kbd`,{class:`px-1.5 py-0.5 rounded font-mono bg-surface border border-border`},`↑↓`),x(`span`,null,`navigate`)]),x(`span`,{class:`flex items-center gap-2`},[x(`kbd`,{class:`px-1.5 py-0.5 rounded font-mono bg-surface border border-border`},`↵`),x(`span`,null,`activate`)])],-1)],512)])):L(``,!0)]),_:1})]))}},fa=()=>B.get(`/system/health`),pa=e=>B.get(`/functions`,{params:e}),ma=e=>B.get(`/functions/${e}`),ha=e=>B.get(`/functions/${e}/source`),ga=()=>B.get(`/routes`),_a=(e,t,n=`*`)=>B.post(`/routes`,{path:e,function_id:t,methods:n}),va=e=>B.delete(`/routes`,{params:{path:e}}),ya=(e,{method:t=`POST`,path:n=`/`,headers:r={},body:i=``}={})=>{let a=`/${e.replace(/^fn_/,``)}`;n&&n!==`/`&&(a+=n.startsWith(`/`)?n:`/${n}`);let o={url:a,method:t,headers:{...r},responseType:`text`,transformRequest:[e=>e]},s=(t||`POST`).toUpperCase();return i&&s!==`GET`&&s!==`HEAD`&&(o.data=i),ye.request(o)},ba=e=>B.get(`/executions`,{params:e}),xa=e=>B.get(`/executions/${e}`),Sa=e=>B.get(`/executions/${e}/logs`),Ca=e=>B.get(`/executions/${e}/request`),wa=e=>B.post(`/executions/${e}/replay`,null,{responseType:`text`}),Ta=e=>B.get(`/activity`,{params:e}),Ea=(e,t)=>B.get(`/functions/${encodeURIComponent(e)}/kv`,{params:t}),Da=(e,t,n)=>B.put(`/functions/${encodeURIComponent(e)}/kv/${encodeURIComponent(t)}`,n),Oa=(e,t)=>B.delete(`/functions/${encodeURIComponent(e)}/kv/${encodeURIComponent(t)}`),ka=e=>B.get(`/functions/${encodeURIComponent(e)}/fixtures`),Aa=(e,t,n)=>B.put(`/functions/${encodeURIComponent(e)}/fixtures/${encodeURIComponent(t)}`,n),ja=(e,t)=>B.delete(`/functions/${encodeURIComponent(e)}/fixtures/${encodeURIComponent(t)}`),Ma=()=>B.get(`/keys`),Na=e=>B.post(`/keys`,e),Pa=e=>B.delete(`/keys/${e}`),Fa=()=>B.get(`/oauth/connected-apps`),Ia=e=>B.delete(`/oauth/connected-apps/${e}`),La=()=>B.get(`/auth/sessions`),Ra=e=>B.delete(`/auth/sessions/${e}`),za=()=>B.get(`/channels`),Ba=e=>B.post(`/channels`,e),Va=e=>B.post(`/channels/${e}/rotate`),Ha=e=>B.delete(`/channels/${e}`),Ua=()=>B.get(`/system/metrics.json`),Wa=e=>B.get(`/deployments/${e}`),Ga=(e,t=0,n=200)=>B.get(`/deployments/${e}/logs`,{params:{from:t,limit:n}}),Ka=(e,t=50)=>B.get(`/functions/${e}/deployments`,{params:{limit:t}}),qa=(e,t,n,r=`json`)=>B.get(`/functions/${encodeURIComponent(e)}/diff`,{params:{from:t,to:n,format:r},responseType:r===`unified`?`text`:`json`}),Ja=(e,t)=>B.post(`/functions/${e}/rollback`,t),Ya=async e=>{let t=((await B.get(`/functions`)).data.functions||[]).find(t=>t.name===e);if(!t)throw Error(`Function "${e}" not found`);return t.id},Xa=e=>({...e,cron_expression:e.cron_expr}),Za=async()=>({data:{schedules:((await B.get(`/cron`)).data.schedules||[]).map(Xa)}}),Qa=async(e,t)=>{let n=await Ya(e),r={cron_expr:t.cron,timezone:t.timezone||eo(),enabled:t.enabled!==!1,payload:t.payload??{}};return{data:Xa((await B.post(`/functions/${n}/cron`,r)).data)}},$a=async(e,t)=>{let n=t.function_id;if(!n)throw Error(`updateCronSchedule: function_id is required`);let r={};return t.cron!==void 0&&(r.cron_expr=t.cron),t.timezone!==void 0&&(r.timezone=t.timezone),t.enabled!==void 0&&(r.enabled=t.enabled),t.payload!==void 0&&(r.payload=t.payload),{data:Xa((await B.put(`/functions/${n}/cron/${e}`,r)).data)}},eo=()=>{try{return Intl.DateTimeFormat().resolvedOptions().timeZone||`UTC`}catch{return`UTC`}},to=async(e,t)=>{if(!t)throw Error(`deleteCronSchedule: functionId is required`);return B.delete(`/functions/${t}/cron/${e}`)},no=(e={})=>B.get(`/jobs`,{params:e}),ro=e=>B.post(`/jobs`,e),io=e=>B.post(`/jobs/${e}/retry`),ao=e=>B.delete(`/jobs/${e}`),oo=()=>B.get(`/system/storage`),so=()=>B.post(`/system/vacuum`),co=e=>{let t=new FormData;return t.append(`archive`,e),B.post(`/restore?confirm=1`,t,{headers:{"Content-Type":`multipart/form-data`},timeout:6e5})},lo=e=>B.get(`/traces/${e}`),uo=(e={})=>B.get(`/traces`,{params:e}),fo=()=>B.get(`/webhooks`),po=e=>B.post(`/webhooks`,e),mo=(e,t)=>B.put(`/webhooks/${e}`,t),ho=e=>B.delete(`/webhooks/${e}`),go=e=>B.post(`/webhooks/${e}/test`),_o=e=>B.get(`/webhooks/${e}/deliveries`),vo=e=>B.post(`/webhooks/deliveries/${e}/retry`),yo=e=>B.get(`/functions/${encodeURIComponent(e)}/inbound-webhooks`),bo=(e,t)=>B.post(`/functions/${encodeURIComponent(e)}/inbound-webhooks`,t),xo=(e,t)=>B.delete(`/functions/${encodeURIComponent(e)}/inbound-webhooks/${t}`),So=[500,1e3,2e3,5e3,1e4],Co=_e(`events`,()=>{let e=o(!1),t=o(0),n=new Map,r=null,i=null,a=(e,t)=>{let r=n.get(e);if(r)for(let e of r)try{e(t)}catch(e){console.error(`events callback error`,e)}},s=n=>{n.onopen=()=>{e.value=!0,t.value=0};for(let e of[`metrics`,`execution`,`deployment`,`function`,`activity`])n.addEventListener(e,t=>{try{let n=JSON.parse(t.data);a(e,n)}catch(e){console.warn(`failed to parse SSE payload`,e,t.data)}});n.onerror=()=>{if(e.value=!1,r){try{r.close()}catch{}r=null}let n=So[Math.min(t.value,So.length-1)];t.value+=1,clearTimeout(i),i=setTimeout(()=>c(),n)}},c=()=>{if(!r)try{r=new EventSource(`/api/v1/events`,{withCredentials:!0}),s(r)}catch(t){console.error(`failed to open /api/v1/events`,t),e.value=!1}};return{connected:h(e),reconnectAttempt:h(t),connect:c,disconnect:()=>{if(clearTimeout(i),i=null,r){try{r.close()}catch{}r=null}e.value=!1,t.value=0},subscribe:(e,t)=>(n.has(e)||n.set(e,new Set),n.get(e).add(t),()=>{let r=n.get(e);r&&r.delete(t)})}}),wo=60,To=_e(`system`,()=>{let e=o(!1),t=o(null),n=o(0),r=o([]),i=o({}),a=o(null),s=null,c=null,l=null,u=e=>{t.value=e;let n=new Set;for(let t of e.pools||[]){n.add(t.function_id);let e=i.value[t.function_id]||[];e.push(t.rate_ewma),e.length>wo&&e.splice(0,e.length-wo),i.value[t.function_id]=e}for(let e of Object.keys(i.value))n.has(e)||delete i.value[e]},d=async()=>{try{let[t,i,o,s]=await Promise.all([Ua(),pa().catch(()=>({data:{functions:[],total:0}})),ba({limit:20}).catch(()=>({data:{executions:[]}})),fa().catch(()=>({data:null}))]);u(t.data),n.value=i.data.total??(i.data.functions||[]).length,r.value=o.data.executions||[],s.data&&(a.value={version:s.data.version,commit:s.data.commit,buildTime:s.data.build_time,image:s.data.image,uptimeSeconds:s.data.uptime_seconds}),e.value=!0}catch(t){console.error(`seed fetch error:`,t),e.value=!1}};return{isConnected:e,metrics:t,functionsCount:n,recentInvocations:r,poolHistory:i,buildInfo:a,connect:()=>{let t=Co();d(),s=t.subscribe(`metrics`,t=>{u(t),e.value=!0}),c=t.subscribe(`execution`,e=>{r.value=[e,...r.value].slice(0,20)}),l=t.subscribe(`function`,e=>{e.action===`deleted`?n.value=Math.max(0,n.value-1):e.action===`created`&&(n.value+=1)})},disconnect:()=>{s&&=(s(),null),c&&=(c(),null),l&&=(l(),null),e.value=!1}}}),Eo={class:`flex h-screen w-full bg-background overflow-hidden font-sans antialiased text-foreground`},Do={class:`flex-1 flex flex-col min-w-0 overflow-hidden relative pt-14 lg:pt-0`},Oo={__name:`Layout`,setup(e){let t=To(),n=Co(),s=Fi(),c=o(0),u=null,d=I(()=>{c.value;let e=s.secondsUntilExpiry;return e==null||e<=0?`—`:e<60?`${Math.floor(e)}s`:e<3600?`${Math.floor(e/60)} min`:`${Math.floor(e/3600)} h`}),f=async()=>{(await s.refreshSession()).success||(window.location.href=`/login`)};return p(async()=>{await s.checkAuth(),n.connect(),t.connect(),u=setInterval(()=>{c.value++},3e4)}),i(()=>{t.disconnect(),n.disconnect(),u&&clearInterval(u)}),(e,t)=>{let n=te(`router-view`);return r(),C(`div`,Eo,[k(Zi),x(`main`,Do,[k(n,null,{default:a(({Component:e})=>[(r(),N(ee,{max:10},[(r(),N(l(e),{class:`flex-1 overflow-auto scrollable p-page`}))],1024))]),_:1})]),k(da),k(ia,{visible:g(s).shouldShowExpiryToast,"action-loading":g(s).refreshing,title:`Session expiring soon`,"action-label":`Stay signed in`,onAction:f,onDismiss:g(s).dismissExpiryToast},{default:a(()=>[re(` Your session expires in `+R(d.value)+`. Click to extend it for another 7 days. `,1)]),_:1},8,[`visible`,`action-loading`,`onDismiss`])])}}},ko=`modulepreload`,Ao=function(e){return`/web/`+e},jo={},$=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?import.meta.resolve(e):new URL(e,import.meta.url).href}r=o(t.map(t=>{if(t=Ao(t,n),t=s(t),t in jo)return;jo[t]=!0;let r=t.endsWith(`.css`);for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}let i=document.createElement(`link`);if(i.rel=r?`stylesheet`:ko,r||(i.as=`script`),i.crossOrigin=``,i.href=t,a&&i.setAttribute(`nonce`,a),document.head.appendChild(i),r)return new Promise((e,n)=>{i.addEventListener(`load`,e),i.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})},Mo=Mi({history:ei(`/web/`),routes:[{path:`/login`,name:`login`,component:()=>$(()=>import(`./Login-DscOTSMy.js`),__vite__mapDeps([0,1,2])),meta:{requiresAuth:!1}},{path:`/onboarding`,name:`onboarding`,component:()=>$(()=>import(`./Onboarding-CI9ktN7I.js`),__vite__mapDeps([3,1,2,4])),meta:{requiresAuth:!1}},{path:`/`,component:Oo,meta:{requiresAuth:!0},children:[{path:``,name:`dashboard`,component:()=>$(()=>import(`./Dashboard-rKNyCP0W.js`),__vite__mapDeps([5,1]))},{path:`ai`,name:`ai`,component:()=>$(()=>import(`./AI-De72Ye8u.js`),__vite__mapDeps([6,7,1,8,4,9,10,11,12,13,14,15,16,17,18,19,20,21,22]))},{path:`functions`,name:`functions`,component:()=>$(()=>import(`./FunctionsList-CPOzZCfJ.js`),__vite__mapDeps([23,1,8,4,24,25,14,26,17,11,7,21,27]))},{path:`functions/:name`,name:`function-detail`,component:()=>$(()=>import(`./Editor-C_JYWteY.js`),__vite__mapDeps([28,1,29,8,30,4,31,24,32,25,33,15,34,16,35,17,36,11,7,21,37,38,39,40,41]))},{path:`functions/:name/deployments`,name:`function-deployments`,component:()=>$(()=>import(`./Deployments-CRPgafhC.js`),__vite__mapDeps([42,1,43,30,26,15,12,13,44,45,38]))},{path:`functions/:name/diff`,name:`function-diff`,component:()=>$(()=>import(`./FunctionDiff-DtVzmfsl.js`),__vite__mapDeps([46,1,4,31,15,18,47,21,38,48]))},{path:`functions/:name/kv`,name:`function-kv`,component:()=>$(()=>import(`./KVStore-DzgumyT2.js`),__vite__mapDeps([49,1,26,17,12,13,27]))},{path:`functions/:name/inbound-webhooks`,name:`function-inbound-webhooks`,component:()=>$(()=>import(`./InboundWebhooks-CFOyt9ev.js`),__vite__mapDeps([50,1,26,17,12,13,27]))},{path:`functions/new`,name:`function-new`,component:()=>$(()=>import(`./Editor-C_JYWteY.js`),__vite__mapDeps([28,1,29,8,30,4,31,24,32,25,33,15,34,16,35,17,36,11,7,21,37,38,39,40,41]))},{path:`deploy`,redirect:{name:`function-new`}},{path:`cron`,name:`cron`,component:()=>$(()=>import(`./CronJobs-CDFaee6D.js`),__vite__mapDeps([51,1,43,33,52,17,27,37]))},{path:`jobs`,name:`jobs`,component:()=>$(()=>import(`./Jobs-CfHJpgrS.js`),__vite__mapDeps([53,1,43,45,15,17,12,13,27]))},{path:`activity`,name:`activity`,component:()=>$(()=>import(`./Activity-6I8QCc07.js`),__vite__mapDeps([54,1,55,12,13,44,43,45]))},{path:`invocations`,name:`invocations`,component:()=>$(()=>import(`./InvocationsLog-Bz2zpa_f.js`),__vite__mapDeps([56,1,8,2,33,26,15,16,17,11,7,12,13,21,44,43,45,39]))},{path:`traces`,name:`traces`,component:()=>$(()=>import(`./Traces-BEuuXgIG.js`),__vite__mapDeps([57,1,58,26,44,43,45]))},{path:`traces/:id`,name:`trace-detail`,component:()=>$(()=>import(`./TraceDetail-wVyyYiTv.js`),__vite__mapDeps([59,1,60,2,4,58,44,43,45]))},{path:`api-keys`,name:`api-keys`,component:()=>$(()=>import(`./ApiKeys-B5PU7HpA.js`),__vite__mapDeps([61,1,8,4,32,17,21,27,62]))},{path:`channels`,name:`channels`,component:()=>$(()=>import(`./Channels-fKQl2oE8.js`),__vite__mapDeps([63,1,8,2,4,15,17,21,27,62]))},{path:`webhooks`,name:`webhooks`,component:()=>$(()=>import(`./Webhooks-D2CkyonR.js`),__vite__mapDeps([64,1,8,4,15,52,17,18,21,27,37]))},{path:`firewall`,name:`firewall`,component:()=>$(()=>import(`./Firewall-Bpe5shll.js`),__vite__mapDeps([65,1,24,26,34,17,11,7,37,40,66]))},{path:`settings`,name:`settings`,component:()=>$(()=>import(`./Settings-Bjj6gg_h.js`),__vite__mapDeps([67,1,4,9,10,8,11,7,12,13,32,35,17,21,62,40]))},{path:`docs`,name:`docs`,component:()=>$(()=>import(`./Docs-BssSTQCS.js`),__vite__mapDeps([68,1,8,55,4,24,32,25,36,11,7,19,20,21,39,69]))}]},{path:`/:pathMatch(.*)*`,name:`not-found`,component:()=>$(()=>import(`./NotFound-b7MSgvru.js`),__vite__mapDeps([70,1,60,29])),meta:{requiresAuth:!1}}]});Mo.beforeEach((e,t,n)=>{if(t.fullPath===e.fullPath&&t.name)return n(!1);n()}),Mo.beforeEach(async(e,t,n)=>{let r=Fi();if(!await r.fetchAuthStatus())return e.name===`onboarding`?n():n({name:`onboarding`,replace:!0});if(r.isAuthenticated===!1&&await r.checkAuth(),r.isAuthenticated)return e.name===`onboarding`||e.name===`login`?n({name:`dashboard`}):n();if(e.name===`login`)return n();if(e.name===`onboarding`)return n({name:`login`,replace:!0});n({name:`login`,query:{redirect:e.fullPath}})});var No=Wt(Ln),Po=ve();No.use(Po),No.use(Mo),No.mount(`#app`);export{Ra as $,Oa as A,nn as At,pa as B,Rt as Bt,ma as C,gn as Ct,oo as D,on as Dt,Sa as E,ln as Et,za as F,Ot as Ft,La as G,ba as H,Fa as I,kt as It,fo as J,uo as K,Za as L,Tt as Lt,Da as M,je as Mt,Ta as N,Et as Nt,lo as O,an as Ot,Ma as P,Nt as Pt,Ia as Q,Ka as R,Je as Rt,Ca as S,_n as St,xa as T,mn as Tt,no as U,yo as V,ga as W,io as X,wa as Y,vo as Z,va as _,En as _t,qa as a,$a as at,Wa as b,xn as bt,Qa as c,co as ct,Pa as d,Fi as dt,Ja as et,Ha as f,wi as ft,ao as g,Dn as gt,xo as h,kn as ht,eo as i,go as it,Ea as j,q as jt,ya as k,rn as kt,bo as l,Bi as lt,ja as m,Xn as mt,To as n,so as nt,Na as o,Aa as ot,to as p,Zn as pt,_o as q,Co as r,_a as rt,Ba as s,mo as st,$ as t,Va as tt,po as u,Ii as ut,ho as v,Cn as vt,ha as w,hn as wt,Ga as x,vn as xt,ro as y,Sn as yt,ka as z,Bt as zt}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/key-round-BmyLzD9o.js b/backend/internal/server/ui_dist/assets/key-round-BKXtbC85.js similarity index 81% rename from backend/internal/server/ui_dist/assets/key-round-BmyLzD9o.js rename to backend/internal/server/ui_dist/assets/key-round-BKXtbC85.js index 72dc2976..1009b9be 100644 --- a/backend/internal/server/ui_dist/assets/key-round-BmyLzD9o.js +++ b/backend/internal/server/ui_dist/assets/key-round-BKXtbC85.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`key-round`,[[`path`,{d:`M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z`,key:`1s6t7t`}],[`circle`,{cx:`16.5`,cy:`7.5`,r:`.5`,fill:`currentColor`,key:`w0ekpg`}]]);export{t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`key-round`,[[`path`,{d:`M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z`,key:`1s6t7t`}],[`circle`,{cx:`16.5`,cy:`7.5`,r:`.5`,fill:`currentColor`,key:`w0ekpg`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/lock-QGBYN3Zu.js b/backend/internal/server/ui_dist/assets/lock-CttMBTH5.js similarity index 67% rename from backend/internal/server/ui_dist/assets/lock-QGBYN3Zu.js rename to backend/internal/server/ui_dist/assets/lock-CttMBTH5.js index 7918e68e..c05fe8a0 100644 --- a/backend/internal/server/ui_dist/assets/lock-QGBYN3Zu.js +++ b/backend/internal/server/ui_dist/assets/lock-CttMBTH5.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`lock`,[[`rect`,{width:`18`,height:`11`,x:`3`,y:`11`,rx:`2`,ry:`2`,key:`1w4ew1`}],[`path`,{d:`M7 11V7a5 5 0 0 1 10 0v4`,key:`fwvmzm`}]]);export{t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`lock`,[[`rect`,{width:`18`,height:`11`,x:`3`,y:`11`,rx:`2`,ry:`2`,key:`1w4ew1`}],[`path`,{d:`M7 11V7a5 5 0 0 1 10 0v4`,key:`fwvmzm`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/pencil-BuAbR0ud.js b/backend/internal/server/ui_dist/assets/pencil-Do2I7soU.js similarity index 75% rename from backend/internal/server/ui_dist/assets/pencil-BuAbR0ud.js rename to backend/internal/server/ui_dist/assets/pencil-Do2I7soU.js index 503cd277..559fa270 100644 --- a/backend/internal/server/ui_dist/assets/pencil-BuAbR0ud.js +++ b/backend/internal/server/ui_dist/assets/pencil-Do2I7soU.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`pencil`,[[`path`,{d:`M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z`,key:`1a8usu`}],[`path`,{d:`m15 5 4 4`,key:`1mk7zo`}]]);export{t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`pencil`,[[`path`,{d:`M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z`,key:`1a8usu`}],[`path`,{d:`m15 5 4 4`,key:`1mk7zo`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/play-DdJW9ehy.js b/backend/internal/server/ui_dist/assets/play-CmQifd74.js similarity index 64% rename from backend/internal/server/ui_dist/assets/play-DdJW9ehy.js rename to backend/internal/server/ui_dist/assets/play-CmQifd74.js index 2bb283e6..52d0b570 100644 --- a/backend/internal/server/ui_dist/assets/play-DdJW9ehy.js +++ b/backend/internal/server/ui_dist/assets/play-CmQifd74.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`play`,[[`path`,{d:`M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z`,key:`10ikf1`}]]);export{t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`play`,[[`path`,{d:`M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z`,key:`10ikf1`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/refresh-cw-Bn_tp4gH.js b/backend/internal/server/ui_dist/assets/refresh-cw-Cn8qkf-v.js similarity index 79% rename from backend/internal/server/ui_dist/assets/refresh-cw-Bn_tp4gH.js rename to backend/internal/server/ui_dist/assets/refresh-cw-Cn8qkf-v.js index 608de23e..d50b3078 100644 --- a/backend/internal/server/ui_dist/assets/refresh-cw-Bn_tp4gH.js +++ b/backend/internal/server/ui_dist/assets/refresh-cw-Cn8qkf-v.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`refresh-cw`,[[`path`,{d:`M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8`,key:`v9h5vc`}],[`path`,{d:`M21 3v5h-5`,key:`1q7to0`}],[`path`,{d:`M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16`,key:`3uifl3`}],[`path`,{d:`M8 16H3v5`,key:`1cv678`}]]);export{t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`refresh-cw`,[[`path`,{d:`M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8`,key:`v9h5vc`}],[`path`,{d:`M21 3v5h-5`,key:`1q7to0`}],[`path`,{d:`M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16`,key:`3uifl3`}],[`path`,{d:`M8 16H3v5`,key:`1cv678`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/rotate-ccw-DBtrGGiP.js b/backend/internal/server/ui_dist/assets/rotate-ccw-DWwjKCqh.js similarity index 66% rename from backend/internal/server/ui_dist/assets/rotate-ccw-DBtrGGiP.js rename to backend/internal/server/ui_dist/assets/rotate-ccw-DWwjKCqh.js index c61747b8..d26172e9 100644 --- a/backend/internal/server/ui_dist/assets/rotate-ccw-DBtrGGiP.js +++ b/backend/internal/server/ui_dist/assets/rotate-ccw-DWwjKCqh.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`rotate-ccw`,[[`path`,{d:`M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8`,key:`1357e3`}],[`path`,{d:`M3 3v5h5`,key:`1xhq8a`}]]);export{t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`rotate-ccw`,[[`path`,{d:`M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8`,key:`1357e3`}],[`path`,{d:`M3 3v5h5`,key:`1xhq8a`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/settings-2-C7i2we5c.js b/backend/internal/server/ui_dist/assets/settings-2-D5QtFfdZ.js similarity index 92% rename from backend/internal/server/ui_dist/assets/settings-2-C7i2we5c.js rename to backend/internal/server/ui_dist/assets/settings-2-D5QtFfdZ.js index 9e48e392..b16b818e 100644 --- a/backend/internal/server/ui_dist/assets/settings-2-C7i2we5c.js +++ b/backend/internal/server/ui_dist/assets/settings-2-D5QtFfdZ.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`file-code`,[[`path`,{d:`M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z`,key:`1oefj6`}],[`path`,{d:`M14 2v5a1 1 0 0 0 1 1h5`,key:`wfsgrz`}],[`path`,{d:`M10 12.5 8 15l2 2.5`,key:`1tg20x`}],[`path`,{d:`m14 12.5 2 2.5-2 2.5`,key:`yinavb`}]]),n=e(`package`,[[`path`,{d:`M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z`,key:`1a0edw`}],[`path`,{d:`M12 22V12`,key:`d0xqtd`}],[`polyline`,{points:`3.29 7 12 12 20.71 7`,key:`ousv84`}],[`path`,{d:`m7.5 4.27 9 5.15`,key:`1c824w`}]]),r=e(`settings-2`,[[`path`,{d:`M14 17H5`,key:`gfn3mx`}],[`path`,{d:`M19 7h-9`,key:`6i9tg`}],[`circle`,{cx:`17`,cy:`17`,r:`3`,key:`18b49y`}],[`circle`,{cx:`7`,cy:`7`,r:`3`,key:`dfmy0x`}]]);export{n,t as r,r as t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`file-code`,[[`path`,{d:`M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z`,key:`1oefj6`}],[`path`,{d:`M14 2v5a1 1 0 0 0 1 1h5`,key:`wfsgrz`}],[`path`,{d:`M10 12.5 8 15l2 2.5`,key:`1tg20x`}],[`path`,{d:`m14 12.5 2 2.5-2 2.5`,key:`yinavb`}]]),n=e(`package`,[[`path`,{d:`M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z`,key:`1a0edw`}],[`path`,{d:`M12 22V12`,key:`d0xqtd`}],[`polyline`,{points:`3.29 7 12 12 20.71 7`,key:`ousv84`}],[`path`,{d:`m7.5 4.27 9 5.15`,key:`1c824w`}]]),r=e(`settings-2`,[[`path`,{d:`M14 17H5`,key:`gfn3mx`}],[`path`,{d:`M19 7h-9`,key:`6i9tg`}],[`circle`,{cx:`17`,cy:`17`,r:`3`,key:`18b49y`}],[`circle`,{cx:`7`,cy:`7`,r:`3`,key:`dfmy0x`}]]);export{n,t as r,r as t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/shield-check-HnHCfFke.js b/backend/internal/server/ui_dist/assets/shield-check-piXkOtNv.js similarity index 79% rename from backend/internal/server/ui_dist/assets/shield-check-HnHCfFke.js rename to backend/internal/server/ui_dist/assets/shield-check-piXkOtNv.js index cc7d2772..ef3eecb0 100644 --- a/backend/internal/server/ui_dist/assets/shield-check-HnHCfFke.js +++ b/backend/internal/server/ui_dist/assets/shield-check-piXkOtNv.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`shield-check`,[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}],[`path`,{d:`m9 12 2 2 4-4`,key:`dzmm74`}]]);export{t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`shield-check`,[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}],[`path`,{d:`m9 12 2 2 4-4`,key:`dzmm74`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/sparkles-BmmGPRlQ.js b/backend/internal/server/ui_dist/assets/sparkles-DTHEIS5T.js similarity index 86% rename from backend/internal/server/ui_dist/assets/sparkles-BmmGPRlQ.js rename to backend/internal/server/ui_dist/assets/sparkles-DTHEIS5T.js index d99a2703..fb2df9e5 100644 --- a/backend/internal/server/ui_dist/assets/sparkles-BmmGPRlQ.js +++ b/backend/internal/server/ui_dist/assets/sparkles-DTHEIS5T.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`sparkles`,[[`path`,{d:`M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z`,key:`1s2grr`}],[`path`,{d:`M20 2v4`,key:`1rf3ol`}],[`path`,{d:`M22 4h-4`,key:`gwowj6`}],[`circle`,{cx:`4`,cy:`20`,r:`2`,key:`6kqj1y`}]]);export{t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`sparkles`,[[`path`,{d:`M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z`,key:`1s2grr`}],[`path`,{d:`M20 2v4`,key:`1rf3ol`}],[`path`,{d:`M22 4h-4`,key:`gwowj6`}],[`circle`,{cx:`4`,cy:`20`,r:`2`,key:`6kqj1y`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/square-pen-JECRrUKU.js b/backend/internal/server/ui_dist/assets/square-pen-DCrjAsLy.js similarity index 79% rename from backend/internal/server/ui_dist/assets/square-pen-JECRrUKU.js rename to backend/internal/server/ui_dist/assets/square-pen-DCrjAsLy.js index d0d33b69..1e372903 100644 --- a/backend/internal/server/ui_dist/assets/square-pen-JECRrUKU.js +++ b/backend/internal/server/ui_dist/assets/square-pen-DCrjAsLy.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`square-pen`,[[`path`,{d:`M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7`,key:`1m0v6g`}],[`path`,{d:`M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z`,key:`ohrbg2`}]]);export{t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`square-pen`,[[`path`,{d:`M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7`,key:`1m0v6g`}],[`path`,{d:`M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z`,key:`ohrbg2`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/terminal-BVgEq2_Q.js b/backend/internal/server/ui_dist/assets/terminal-BQdlNiyt.js similarity index 57% rename from backend/internal/server/ui_dist/assets/terminal-BVgEq2_Q.js rename to backend/internal/server/ui_dist/assets/terminal-BQdlNiyt.js index dd79d340..6902b41c 100644 --- a/backend/internal/server/ui_dist/assets/terminal-BVgEq2_Q.js +++ b/backend/internal/server/ui_dist/assets/terminal-BQdlNiyt.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`terminal`,[[`path`,{d:`M12 19h8`,key:`baeox8`}],[`path`,{d:`m4 17 6-6-6-6`,key:`1yngyt`}]]);export{t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`terminal`,[[`path`,{d:`M12 19h8`,key:`baeox8`}],[`path`,{d:`m4 17 6-6-6-6`,key:`1yngyt`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/trash-2-Bj4AFUfw.js b/backend/internal/server/ui_dist/assets/trash-2-DaeYqnW_.js similarity index 79% rename from backend/internal/server/ui_dist/assets/trash-2-Bj4AFUfw.js rename to backend/internal/server/ui_dist/assets/trash-2-DaeYqnW_.js index f09c28d7..2fe35548 100644 --- a/backend/internal/server/ui_dist/assets/trash-2-Bj4AFUfw.js +++ b/backend/internal/server/ui_dist/assets/trash-2-DaeYqnW_.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`trash-2`,[[`path`,{d:`M10 11v6`,key:`nco0om`}],[`path`,{d:`M14 11v6`,key:`outv1u`}],[`path`,{d:`M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6`,key:`miytrc`}],[`path`,{d:`M3 6h18`,key:`d0wm0j`}],[`path`,{d:`M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2`,key:`e791ji`}]]);export{t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`trash-2`,[[`path`,{d:`M10 11v6`,key:`nco0om`}],[`path`,{d:`M14 11v6`,key:`outv1u`}],[`path`,{d:`M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6`,key:`miytrc`}],[`path`,{d:`M3 6h18`,key:`d0wm0j`}],[`path`,{d:`M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2`,key:`e791ji`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/variable-Doeff6Y-.js b/backend/internal/server/ui_dist/assets/variable-DrK2KZuk.js similarity index 77% rename from backend/internal/server/ui_dist/assets/variable-Doeff6Y-.js rename to backend/internal/server/ui_dist/assets/variable-DrK2KZuk.js index 001d3a13..62a94564 100644 --- a/backend/internal/server/ui_dist/assets/variable-Doeff6Y-.js +++ b/backend/internal/server/ui_dist/assets/variable-DrK2KZuk.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`variable`,[[`path`,{d:`M8 21s-4-3-4-9 4-9 4-9`,key:`uto9ud`}],[`path`,{d:`M16 3s4 3 4 9-4 9-4 9`,key:`4w2vsq`}],[`line`,{x1:`15`,x2:`9`,y1:`9`,y2:`15`,key:`f7djnv`}],[`line`,{x1:`9`,x2:`15`,y1:`9`,y2:`15`,key:`1shsy8`}]]);export{t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`variable`,[[`path`,{d:`M8 21s-4-3-4-9 4-9 4-9`,key:`uto9ud`}],[`path`,{d:`M16 3s4 3 4 9-4 9-4 9`,key:`4w2vsq`}],[`line`,{x1:`15`,x2:`9`,y1:`9`,y2:`15`,key:`f7djnv`}],[`line`,{x1:`9`,x2:`15`,y1:`9`,y2:`15`,key:`1shsy8`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/assets/zap-CGOT0fxk.js b/backend/internal/server/ui_dist/assets/zap-BXGoxm_a.js similarity index 75% rename from backend/internal/server/ui_dist/assets/zap-CGOT0fxk.js rename to backend/internal/server/ui_dist/assets/zap-BXGoxm_a.js index 49772281..0216f3a6 100644 --- a/backend/internal/server/ui_dist/assets/zap-CGOT0fxk.js +++ b/backend/internal/server/ui_dist/assets/zap-BXGoxm_a.js @@ -1 +1 @@ -import{jt as e}from"./index-C11HmDXN.js";var t=e(`zap`,[[`path`,{d:`M15.914 4a1.5 1.5 0 00-2.474-1.561l-9 9A1.5 1.5 0 005.5 14h4.002a.5.5 0 01.471.666L8.086 20a1.5 1.5 0 002.475 1.56l9-9A1.5 1.5 0 0018.5 10h-3.997a.5.5 0 01-.472-.667z`,key:`1v7up4`}]]);export{t}; \ No newline at end of file +import{jt as e}from"./index-DTqMKlE1.js";var t=e(`zap`,[[`path`,{d:`M15.914 4a1.5 1.5 0 00-2.474-1.561l-9 9A1.5 1.5 0 005.5 14h4.002a.5.5 0 01.471.666L8.086 20a1.5 1.5 0 002.475 1.56l9-9A1.5 1.5 0 0018.5 10h-3.997a.5.5 0 01-.472-.667z`,key:`1v7up4`}]]);export{t}; \ No newline at end of file diff --git a/backend/internal/server/ui_dist/docs.md b/backend/internal/server/ui_dist/docs.md index 200444de..abf7d458 100644 --- a/backend/internal/server/ui_dist/docs.md +++ b/backend/internal/server/ui_dist/docs.md @@ -182,9 +182,9 @@ API. It ships beside the runtime adapter — `require('orva')` (Node) and install`. The Node SDK ships TypeScript declarations (`orva.d.ts`); the Python SDK ships a `py.typed` marker so IDEs surface full type hints. -Surface, as of v0.6: +Surface, as of v0.7: -- **`kv`** — `get` / `put` / `delete` / `list(cursor)` / `getMany` / `putMany` / `deleteMany` / `incr` / `cas`. Per-function namespace, optional TTL, single-RTT batch ops, atomic counter & CAS. +- **`kv`** — `get` / `put` / `delete` / `list(cursor)` / `getMany` / `putMany` / `deleteMany` / `incr` / `cas`. Per-function namespace, optional TTL, all-or-nothing batch ops, atomic counter & CAS. Keys are non-empty UTF-8 up to 256 characters; JSON values are capped at 64 KiB; batches are capped at 100 operations. - **`invoke(name, payload, {timeoutMs})`** — synchronous F2F call with `{statusCode, headers, body}` envelope. 8-deep call cap. - **`invokeStream(name, payload, {timeoutMs})`** — same, but yields `Uint8Array` chunks via `for await`. - **`jobs.enqueue(name, payload, {idempotencyKey, maxAttempts, scheduledAt})`** — durable background queue with built-in dedup. @@ -204,7 +204,8 @@ Surface, as of v0.6: from orva import kv def handler(event): - # Store with optional TTL (seconds). 0 = no expiry. + # Omit TTL to preserve an existing expiry (new keys are persistent). + # Pass 0 to clear expiry, or a positive number to set/refresh it. kv.put("user:42", {"name": "Ada", "tier": "pro"}, ttl_seconds=3600) # Read; default returned if missing or expired. @@ -225,6 +226,8 @@ def handler(event): const { kv } = require('orva') exports.handler = async (event) => { + // Omit ttlSeconds to preserve expiry, use 0 to clear it, or a + // positive number to set/refresh it. await kv.put('user:42', { name: 'Ada', tier: 'pro' }, { ttlSeconds: 3600 }) const user = await kv.get('user:42', null) @@ -242,6 +245,10 @@ exports.handler = async (event) => { > `GET/PUT/DELETE /api/v1/functions//kv[/]`. MCP tools: > `kv_list` / `kv_get` / `kv_put` / `kv_delete`. +> KV batches are atomic: validation or storage failure rejects the request and +> rolls back every write. The SDK's `getMany`, `putMany`, and `deleteMany` +> throw `OrvaError` when the batch fails; they never report partial success. + ### Function-to-function — invoke() ### F2F — Python @@ -1103,17 +1110,17 @@ Secrets are stored encrypted at rest, decrypted only into the worker environment -Every function has the `orva` module pre-imported — zero install, zero config. The SDK reaches orvad over the bridge network using HTTP, so the function MUST be created with `network_mode: "egress"` (or updated to it later). With the default `network_mode: "none"`, every SDK call fails at runtime with `ENETUNREACH` / `OrvaUnavailableError` / `TypeError: fetch failed`. THREE primitives: +Every function has the `orva` module pre-imported — zero install, zero config. The SDK reaches orvad over the bridge network using HTTP, so the function MUST be created with `network_mode: "egress"` (or updated to it later). With the default `network_mode: "none"`, every SDK call fails at runtime with `ENETUNREACH` / `OrvaUnavailableError` / `TypeError: fetch failed`. Core primitives: ## orva.kv — per-function key/value store on SQLite -Per-function namespace; keys never collide across functions. Optional TTL in seconds (sweep every 5 min AND filtered at read time, so stale reads are impossible). Values JSON-serialised; cap 64 KB per value. Keys cap 256 chars. Use for: caches, idempotency keys, rate-limit counters, light session state, feature flags, last-seen markers. NOT a primary database, NOT a queue, NOT for blob storage. +Per-function namespace enforced by a process-signed, function-scoped SDK credential; caller identity never comes from a request header. Keys never collide across functions. TTL is optional: omit it to preserve an existing expiry (new keys are persistent), pass 0 to clear expiry, or pass a positive value to set/refresh it; negative values are rejected. Expiry is filtered at read time and swept every 5 minutes. Values must be valid JSON and are capped at 64 KiB; keys must be non-empty UTF-8 and are capped at 256 characters. Batches are all-or-nothing and capped at 100 operations. Use for: caches, idempotency keys, rate-limit counters, light session state, feature flags, last-seen markers. NOT a primary database, NOT a queue, NOT for blob storage. Python: from orva import kv kv.put("user:42", {"name": "Ada", "tier": "pro"}, ttl_seconds=3600) user = kv.get("user:42", default=None) # → dict, or None - pages = kv.list(prefix="page:", limit=50) # → [keys]; pass cursor= for pagination + pages = kv.list(prefix="page:", limit=50) # → {keys, next_cursor}; pass next_cursor back as cursor kv.delete("user:42") # idempotent; no error if missing Node: @@ -1144,8 +1151,8 @@ Tell the user about this when their function uses kv state and they might want to inspect it. kv.put(f"req:{idempotency_key}", "1", ttl_seconds=86400) -## orva.invoke — function-to-function calls (no HTTP, no auth) -Bypasses the proxy stack and dispatches via the warm pool. Faster than internal HTTP, no signing required. Recursion guard: max call depth 8. The callee's full {statusCode, headers, body} is returned; body is JSON-decoded when possible. +## orva.invoke — function-to-function calls +Uses the scoped internal SDK endpoint and dispatches through the warm pool; the signed credential supplies immutable caller attribution while the named target may be another function. Recursion guard: max call depth 8. The callee's full {statusCode, headers, body} is returned; body is JSON-decoded when possible. Python: from orva import invoke, OrvaError @@ -1372,7 +1379,7 @@ Every night at 3 AM UTC, delete all KV keys that start with "session:" and are o -**Plan.** Python 3.14 handler triggered by a cron schedule `0 3 * * *` UTC. Lists keys with prefix "session:" via orva.kv.list, page by page, and lets the TTL-based sweep handle expiration — but for keys we want gone NOW, we delete explicitly. No deps. No egress. Recommended auth: leave public; cron triggers don't go through the HTTP gate, but if anyone could hit it from outside, set platform_key. +**Plan.** Python 3.14 handler triggered by a cron schedule `0 3 * * *` UTC. Lists keys with prefix "session:" via orva.kv.list, page by page, and lets the TTL-based sweep handle expiration — but for keys we want gone NOW, we delete explicitly. No deps. Requires egress because the bundled KV SDK calls Orva's control plane. Recommended auth: leave public; cron triggers don't go through the HTTP gate, but if anyone could hit it from outside, set platform_key. ```python import json @@ -1385,10 +1392,10 @@ def handler(event): while True: page = kv.list(prefix="session:", limit=200, cursor=cursor) - for key in page.get("keys", []): - kv.delete(key) + for entry in page.get("keys", []): + kv.delete(entry["key"]) deleted += 1 - cursor = page.get("cursor") + cursor = page.get("next_cursor") if not cursor: break @@ -1405,7 +1412,7 @@ def handler(event): "body": {"deleted": deleted}} ``` -Wire it up: open Schedules → New Schedule → expression `0 3 * * *`, timezone UTC, function this one. Set auth_mode to platform_key on the function so the URL can't be triggered manually by random callers. +Wire it up: enable outbound network, then open Schedules → New Schedule → expression `0 3 * * *`, timezone UTC, function this one. Set auth_mode to platform_key on the function so the URL can't be triggered manually by random callers. diff --git a/backend/internal/server/ui_dist/index.html b/backend/internal/server/ui_dist/index.html index 1eaf6b5d..0fc047bd 100644 --- a/backend/internal/server/ui_dist/index.html +++ b/backend/internal/server/ui_dist/index.html @@ -8,7 +8,7 @@ - + diff --git a/backend/internal/version/version.go b/backend/internal/version/version.go index 83201f5a..eccb8832 100644 --- a/backend/internal/version/version.go +++ b/backend/internal/version/version.go @@ -35,4 +35,4 @@ var BuildTime = "unknown" // MUST stay in lock-step with the adapters that stamp the header: // backend/runtimes/node/orva.js → const SDK_VERSION // backend/runtimes/python/orva.py → SDK_VERSION -const SDKVersion = "0.6.0" +const SDKVersion = "0.7.0" diff --git a/backend/runtimes/node/orva.d.ts b/backend/runtimes/node/orva.d.ts index 781c24ba..caa4d7bf 100644 --- a/backend/runtimes/node/orva.d.ts +++ b/backend/runtimes/node/orva.d.ts @@ -33,6 +33,7 @@ export interface KVPutEntry { } export interface KVOptions { + /** Omit to preserve expiry, use 0 to clear it, or a positive value to set it. */ ttlSeconds?: number } diff --git a/backend/runtimes/node/orva.js b/backend/runtimes/node/orva.js index 71254604..b08be9dd 100644 --- a/backend/runtimes/node/orva.js +++ b/backend/runtimes/node/orva.js @@ -1,7 +1,7 @@ // Orva Node.js SDK — kv, invoke, jobs, crons, trace, log, context. // -// Routes through ORVA_API_BASE (loopback) using ORVA_INTERNAL_TOKEN that -// was injected at worker spawn. Both env vars must be present in +// Routes through ORVA_API_BASE using the function-scoped ORVA_INTERNAL_TOKEN +// injected at worker spawn. Both env vars must be present in // production; absent in tests where the SDK throws OrvaUnavailableError // (unless __test_mode__ has supplied an override implementation). // @@ -14,7 +14,7 @@ // SDK version baked at adapter-embed time. Bumped in lockstep with the // server. The string is sent on every internal-token call so operators // can see drift in deployment logs. -const SDK_VERSION = '0.6.0' +const SDK_VERSION = '0.7.0' const COMMON_HEADERS = { 'Content-Type': 'application/json' } @@ -73,8 +73,6 @@ function _traceHeaders() { const exec = _execID() if (trace) h['X-Orva-Trace-Id'] = trace if (span) h['X-Orva-Span-Id'] = span - if (fn) h['X-Orva-Caller-Function'] = fn - if (fn) h['X-Orva-Function-Id'] = fn if (exec) h['X-Orva-Execution-Id'] = exec h['X-Orva-SDK-Version'] = SDK_VERSION return h @@ -146,13 +144,17 @@ const kv = { return data.value != null ? data.value : defaultValue }, - /** Upsert a value. ttlSeconds=0 disables expiry. */ - async put(key, value, { ttlSeconds = 0 } = {}) { + /** Upsert a value. Omit ttlSeconds to preserve expiry; 0 clears it. */ + async put(key, value, options = {}) { const fn = _fnID() + const requestBody = { value } + if (Object.hasOwn(options, 'ttlSeconds')) { + requestBody.ttl_seconds = Number(options.ttlSeconds) + } const { status, body } = await _request( 'PUT', `/api/v1/_kv/${fn}/${encodeURIComponent(key)}`, - { body: { value, ttl_seconds: ttlSeconds | 0 } } + { body: requestBody } ) if (status >= 400) throw new OrvaError(`kv.put(${key}) failed: ${body}`, status) }, @@ -198,6 +200,7 @@ const kv = { }) if (status >= 400) throw new OrvaError(`kv.getMany failed: ${body}`, status) const data = JSON.parse(body) + _assertAtomicBatch(data, 'kv.getMany') const out = {} for (const r of data.results || []) { out[r.key] = r.found ? r.value : null @@ -209,16 +212,16 @@ const kv = { async putMany(entries) { if (!entries || entries.length === 0) return const fn = _fnID() - const ops = entries.map((e) => ({ - op: 'put', - key: e.key, - value: e.value, - ttl_seconds: (e.ttlSeconds | 0) || 0, - })) + const ops = entries.map((e) => { + const op = { op: 'put', key: e.key, value: e.value } + if (Object.hasOwn(e, 'ttlSeconds')) op.ttl_seconds = Number(e.ttlSeconds) + return op + }) const { status, body } = await _request('POST', `/api/v1/_kv/${fn}/batch`, { body: { ops }, }) if (status >= 400) throw new OrvaError(`kv.putMany failed: ${body}`, status) + _assertAtomicBatch(JSON.parse(body), 'kv.putMany') }, /** Delete N keys in one transaction. Returns the number removed. */ @@ -231,16 +234,21 @@ const kv = { }) if (status >= 400) throw new OrvaError(`kv.deleteMany failed: ${body}`, status) const data = JSON.parse(body) + _assertAtomicBatch(data, 'kv.deleteMany') return (data.results || []).filter((r) => r.found).length }, /** Atomic increment. Missing keys are treated as 0. Returns new value. */ - async incr(key, delta = 1, { ttlSeconds = 0 } = {}) { + async incr(key, delta = 1, options = {}) { const fn = _fnID() + const requestBody = { delta } + if (Object.hasOwn(options, 'ttlSeconds')) { + requestBody.ttl_seconds = Number(options.ttlSeconds) + } const { status, body } = await _request( 'POST', `/api/v1/_kv/${fn}/${encodeURIComponent(key)}/incr`, - { body: { delta, ttl_seconds: ttlSeconds | 0 } } + { body: requestBody } ) if (status >= 400) throw new OrvaError(`kv.incr(${key}) failed: ${body}`, status) return JSON.parse(body).value @@ -251,17 +259,20 @@ const kv = { * Returns true on success; on mismatch, throws OrvaCASMismatch carrying * the current value so callers can retry. */ - async cas(key, expected, newValue, { ttlSeconds = 0 } = {}) { + async cas(key, expected, newValue, options = {}) { const fn = _fnID() + const requestBody = { + expected: expected === null ? null : expected, + new: newValue, + } + if (Object.hasOwn(options, 'ttlSeconds')) { + requestBody.ttl_seconds = Number(options.ttlSeconds) + } const { status, body } = await _request( 'POST', `/api/v1/_kv/${fn}/${encodeURIComponent(key)}/cas`, { - body: { - expected: expected === null ? null : expected, - new: newValue, - ttl_seconds: ttlSeconds | 0, - }, + body: requestBody, } ) if (status >= 400) throw new OrvaError(`kv.cas(${key}) failed: ${body}`, status) @@ -271,6 +282,13 @@ const kv = { }, } +function _assertAtomicBatch(data, operation) { + const failed = (data.results || []).find((result) => result.error) + if (failed) { + throw new OrvaError(`${operation} failed: ${failed.error}`, 500) + } +} + // ── Function-to-function invoke ───────────────────────────────────── async function invoke(functionName, payload = {}, { timeoutMs = DEFAULT_TIMEOUT_MS } = {}) { diff --git a/backend/runtimes/node/package.json b/backend/runtimes/node/package.json index 4c1d5af5..5e002ade 100644 --- a/backend/runtimes/node/package.json +++ b/backend/runtimes/node/package.json @@ -1,6 +1,6 @@ { "name": "orva", - "version": "0.6.0", + "version": "0.7.0", "main": "./orva.js", "types": "./orva.d.ts", "private": true, diff --git a/backend/runtimes/python/orva.py b/backend/runtimes/python/orva.py index 448bcda5..d289fb4f 100644 --- a/backend/runtimes/python/orva.py +++ b/backend/runtimes/python/orva.py @@ -1,8 +1,8 @@ """Orva Python SDK — kv, invoke, jobs, crons, trace, log, context. Available inside any function running on Orva. Routes through the -ORVA_API_BASE loopback URL using the per-process ORVA_INTERNAL_TOKEN -that the worker received at spawn time. Both env vars are present in +ORVA_API_BASE URL using the function-scoped ORVA_INTERNAL_TOKEN that the +worker received at spawn time. Both env vars are present in production and absent in tests; helpers raise OrvaUnavailableError when the SDK can't reach the host unless __test_mode__ has installed an override. @@ -27,7 +27,7 @@ # SDK version baked at adapter-embed time. Sent on every internal call. -SDK_VERSION = "0.6.0" +SDK_VERSION = "0.7.0" T = TypeVar("T") @@ -129,9 +129,6 @@ def _trace_headers() -> Dict[str, str]: h["X-Orva-Trace-Id"] = v if v := _span_id(): h["X-Orva-Span-Id"] = v - if v := _function_id(): - h["X-Orva-Caller-Function"] = v - h["X-Orva-Function-Id"] = v if v := _execution_id(): h["X-Orva-Execution-Id"] = v return h @@ -211,9 +208,12 @@ def get(key: str, default: Any = None) -> Any: return data["value"] if data.get("value") is not None else default @staticmethod - def put(key: str, value: Any, *, ttl_seconds: int = 0) -> None: + def put(key: str, value: Any, *, ttl_seconds: Optional[int] = None) -> None: fn = _function_id() - payload = json.dumps({"value": value, "ttl_seconds": int(ttl_seconds)}).encode("utf-8") + request_body: Dict[str, Any] = {"value": value} + if ttl_seconds is not None: + request_body["ttl_seconds"] = int(ttl_seconds) + payload = json.dumps(request_body).encode("utf-8") status, body, _ = _request( "PUT", f"/api/v1/_kv/{fn}/{quote(key, safe='')}", body=payload ) @@ -241,6 +241,7 @@ def list( if status >= 400: raise OrvaError(f"kv.list failed: {body!r}", status=status) data = json.loads(body) + _assert_atomic_batch(data, "kv.get_many") return {"keys": list(data.get("keys") or []), "next_cursor": data.get("next_cursor", "")} @staticmethod @@ -263,19 +264,17 @@ def put_many(entries: List[Dict[str, Any]]) -> None: if not entries: return fn = _function_id() - ops = [ - { - "op": "put", - "key": e["key"], - "value": e["value"], - "ttl_seconds": int(e.get("ttl_seconds", 0)), - } - for e in entries - ] + ops = [] + for entry in entries: + op = {"op": "put", "key": entry["key"], "value": entry["value"]} + if "ttl_seconds" in entry: + op["ttl_seconds"] = int(entry["ttl_seconds"]) + ops.append(op) payload = json.dumps({"ops": ops}).encode("utf-8") status, body, _ = _request("POST", f"/api/v1/_kv/{fn}/batch", body=payload) if status >= 400: raise OrvaError(f"kv.put_many failed: {body!r}", status=status) + _assert_atomic_batch(json.loads(body), "kv.put_many") @staticmethod def delete_many(keys: List[str]) -> int: @@ -287,12 +286,16 @@ def delete_many(keys: List[str]) -> int: if status >= 400: raise OrvaError(f"kv.delete_many failed: {body!r}", status=status) data = json.loads(body) + _assert_atomic_batch(data, "kv.delete_many") return sum(1 for r in data.get("results") or [] if r.get("found")) @staticmethod - def incr(key: str, delta: int = 1, *, ttl_seconds: int = 0) -> int: + def incr(key: str, delta: int = 1, *, ttl_seconds: Optional[int] = None) -> int: fn = _function_id() - payload = json.dumps({"delta": int(delta), "ttl_seconds": int(ttl_seconds)}).encode("utf-8") + request_body: Dict[str, Any] = {"delta": int(delta)} + if ttl_seconds is not None: + request_body["ttl_seconds"] = int(ttl_seconds) + payload = json.dumps(request_body).encode("utf-8") status, body, _ = _request( "POST", f"/api/v1/_kv/{fn}/{quote(key, safe='')}/incr", body=payload ) @@ -301,11 +304,12 @@ def incr(key: str, delta: int = 1, *, ttl_seconds: int = 0) -> int: return int(json.loads(body)["value"]) @staticmethod - def cas(key: str, expected: Any, new: Any, *, ttl_seconds: int = 0) -> bool: + def cas(key: str, expected: Any, new: Any, *, ttl_seconds: Optional[int] = None) -> bool: fn = _function_id() - payload = json.dumps( - {"expected": expected, "new": new, "ttl_seconds": int(ttl_seconds)} - ).encode("utf-8") + request_body: Dict[str, Any] = {"expected": expected, "new": new} + if ttl_seconds is not None: + request_body["ttl_seconds"] = int(ttl_seconds) + payload = json.dumps(request_body).encode("utf-8") status, body, _ = _request( "POST", f"/api/v1/_kv/{fn}/{quote(key, safe='')}/cas", body=payload ) @@ -320,6 +324,12 @@ def cas(key: str, expected: Any, new: Any, *, ttl_seconds: int = 0) -> bool: kv = _KV() +def _assert_atomic_batch(data: Dict[str, Any], operation: str) -> None: + failed = next((item for item in data.get("results") or [] if item.get("error")), None) + if failed: + raise OrvaError(f"{operation} failed: {failed['error']}", status=500) + + # ── Function-to-function invoke ───────────────────────────────────── diff --git a/cli/commands/kv.go b/cli/commands/kv.go index 2148cc50..995019ff 100644 --- a/cli/commands/kv.go +++ b/cli/commands/kv.go @@ -124,15 +124,15 @@ func init() { kvListCmd.Flags().Int("limit", 200, "maximum number of entries to return (max 1000)") kvPutCmd.Flags().String("value", "", "JSON value to store: inline, @file, or @- for stdin (required)") - kvPutCmd.Flags().Int("ttl", 0, "TTL in seconds (0 = no expiry)") + kvPutCmd.Flags().Int("ttl", 0, "TTL seconds (omit=preserve, 0=clear, positive=set)") kvPutCmd.MarkFlagRequired("value") kvIncrCmd.Flags().Int64("by", 1, "amount to add (may be negative)") - kvIncrCmd.Flags().Int("ttl", 0, "TTL in seconds to (re)set on the key (0 = preserve existing)") + kvIncrCmd.Flags().Int("ttl", 0, "TTL seconds (omit=preserve, 0=clear, positive=set)") kvCASCmd.Flags().String("expected", "", "expected current JSON value; 'null' means the key must not exist (required)") kvCASCmd.Flags().String("new", "", "new JSON value to store if the precondition holds (required)") - kvCASCmd.Flags().Int("ttl", 0, "TTL in seconds to set on the new value (0 = no expiry)") + kvCASCmd.Flags().Int("ttl", 0, "TTL seconds (omit=preserve, 0=clear, positive=set)") kvCASCmd.MarkFlagRequired("expected") kvCASCmd.MarkFlagRequired("new") @@ -270,6 +270,9 @@ func runKVPut(cmd *cobra.Command, args []string) error { valueArg, _ := cmd.Flags().GetString("value") ttl, _ := cmd.Flags().GetInt("ttl") + if ttl < 0 { + return fmt.Errorf("put: --ttl must be >= 0") + } data, err := readBodyArg(valueArg) if err != nil { @@ -283,7 +286,7 @@ func runKVPut(cmd *cobra.Command, args []string) error { body := map[string]any{ "value": value, } - if ttl > 0 { + if cmd.Flags().Changed("ttl") { body["ttl_seconds"] = ttl } @@ -300,7 +303,7 @@ func runKVPut(cmd *cobra.Command, args []string) error { if outputJSON(cmd) { return emitRaw(respBody) } - if ttl > 0 { + if cmd.Flags().Changed("ttl") { okf(cmd, "KV entry %q saved (ttl %ds)", key, ttl) } else { okf(cmd, "KV entry %q saved", key) @@ -354,9 +357,12 @@ func runKVIncr(cmd *cobra.Command, args []string) error { key := args[1] by, _ := cmd.Flags().GetInt64("by") ttl, _ := cmd.Flags().GetInt("ttl") + if ttl < 0 { + return fmt.Errorf("incr: --ttl must be >= 0") + } body := map[string]any{"delta": by} - if ttl > 0 { + if cmd.Flags().Changed("ttl") { body["ttl_seconds"] = ttl } @@ -398,6 +404,9 @@ func runKVCAS(cmd *cobra.Command, args []string) error { expectedArg, _ := cmd.Flags().GetString("expected") newArg, _ := cmd.Flags().GetString("new") ttl, _ := cmd.Flags().GetInt("ttl") + if ttl < 0 { + return fmt.Errorf("cas: --ttl must be >= 0") + } var expected json.RawMessage if err := json.Unmarshal([]byte(expectedArg), &expected); err != nil { @@ -409,7 +418,7 @@ func runKVCAS(cmd *cobra.Command, args []string) error { } body := map[string]any{"expected": expected, "new": newVal} - if ttl > 0 { + if cmd.Flags().Changed("ttl") { body["ttl_seconds"] = ttl } diff --git a/cli/commands/reference.md b/cli/commands/reference.md index 200444de..abf7d458 100644 --- a/cli/commands/reference.md +++ b/cli/commands/reference.md @@ -182,9 +182,9 @@ API. It ships beside the runtime adapter — `require('orva')` (Node) and install`. The Node SDK ships TypeScript declarations (`orva.d.ts`); the Python SDK ships a `py.typed` marker so IDEs surface full type hints. -Surface, as of v0.6: +Surface, as of v0.7: -- **`kv`** — `get` / `put` / `delete` / `list(cursor)` / `getMany` / `putMany` / `deleteMany` / `incr` / `cas`. Per-function namespace, optional TTL, single-RTT batch ops, atomic counter & CAS. +- **`kv`** — `get` / `put` / `delete` / `list(cursor)` / `getMany` / `putMany` / `deleteMany` / `incr` / `cas`. Per-function namespace, optional TTL, all-or-nothing batch ops, atomic counter & CAS. Keys are non-empty UTF-8 up to 256 characters; JSON values are capped at 64 KiB; batches are capped at 100 operations. - **`invoke(name, payload, {timeoutMs})`** — synchronous F2F call with `{statusCode, headers, body}` envelope. 8-deep call cap. - **`invokeStream(name, payload, {timeoutMs})`** — same, but yields `Uint8Array` chunks via `for await`. - **`jobs.enqueue(name, payload, {idempotencyKey, maxAttempts, scheduledAt})`** — durable background queue with built-in dedup. @@ -204,7 +204,8 @@ Surface, as of v0.6: from orva import kv def handler(event): - # Store with optional TTL (seconds). 0 = no expiry. + # Omit TTL to preserve an existing expiry (new keys are persistent). + # Pass 0 to clear expiry, or a positive number to set/refresh it. kv.put("user:42", {"name": "Ada", "tier": "pro"}, ttl_seconds=3600) # Read; default returned if missing or expired. @@ -225,6 +226,8 @@ def handler(event): const { kv } = require('orva') exports.handler = async (event) => { + // Omit ttlSeconds to preserve expiry, use 0 to clear it, or a + // positive number to set/refresh it. await kv.put('user:42', { name: 'Ada', tier: 'pro' }, { ttlSeconds: 3600 }) const user = await kv.get('user:42', null) @@ -242,6 +245,10 @@ exports.handler = async (event) => { > `GET/PUT/DELETE /api/v1/functions//kv[/]`. MCP tools: > `kv_list` / `kv_get` / `kv_put` / `kv_delete`. +> KV batches are atomic: validation or storage failure rejects the request and +> rolls back every write. The SDK's `getMany`, `putMany`, and `deleteMany` +> throw `OrvaError` when the batch fails; they never report partial success. + ### Function-to-function — invoke() ### F2F — Python @@ -1103,17 +1110,17 @@ Secrets are stored encrypted at rest, decrypted only into the worker environment -Every function has the `orva` module pre-imported — zero install, zero config. The SDK reaches orvad over the bridge network using HTTP, so the function MUST be created with `network_mode: "egress"` (or updated to it later). With the default `network_mode: "none"`, every SDK call fails at runtime with `ENETUNREACH` / `OrvaUnavailableError` / `TypeError: fetch failed`. THREE primitives: +Every function has the `orva` module pre-imported — zero install, zero config. The SDK reaches orvad over the bridge network using HTTP, so the function MUST be created with `network_mode: "egress"` (or updated to it later). With the default `network_mode: "none"`, every SDK call fails at runtime with `ENETUNREACH` / `OrvaUnavailableError` / `TypeError: fetch failed`. Core primitives: ## orva.kv — per-function key/value store on SQLite -Per-function namespace; keys never collide across functions. Optional TTL in seconds (sweep every 5 min AND filtered at read time, so stale reads are impossible). Values JSON-serialised; cap 64 KB per value. Keys cap 256 chars. Use for: caches, idempotency keys, rate-limit counters, light session state, feature flags, last-seen markers. NOT a primary database, NOT a queue, NOT for blob storage. +Per-function namespace enforced by a process-signed, function-scoped SDK credential; caller identity never comes from a request header. Keys never collide across functions. TTL is optional: omit it to preserve an existing expiry (new keys are persistent), pass 0 to clear expiry, or pass a positive value to set/refresh it; negative values are rejected. Expiry is filtered at read time and swept every 5 minutes. Values must be valid JSON and are capped at 64 KiB; keys must be non-empty UTF-8 and are capped at 256 characters. Batches are all-or-nothing and capped at 100 operations. Use for: caches, idempotency keys, rate-limit counters, light session state, feature flags, last-seen markers. NOT a primary database, NOT a queue, NOT for blob storage. Python: from orva import kv kv.put("user:42", {"name": "Ada", "tier": "pro"}, ttl_seconds=3600) user = kv.get("user:42", default=None) # → dict, or None - pages = kv.list(prefix="page:", limit=50) # → [keys]; pass cursor= for pagination + pages = kv.list(prefix="page:", limit=50) # → {keys, next_cursor}; pass next_cursor back as cursor kv.delete("user:42") # idempotent; no error if missing Node: @@ -1144,8 +1151,8 @@ Tell the user about this when their function uses kv state and they might want to inspect it. kv.put(f"req:{idempotency_key}", "1", ttl_seconds=86400) -## orva.invoke — function-to-function calls (no HTTP, no auth) -Bypasses the proxy stack and dispatches via the warm pool. Faster than internal HTTP, no signing required. Recursion guard: max call depth 8. The callee's full {statusCode, headers, body} is returned; body is JSON-decoded when possible. +## orva.invoke — function-to-function calls +Uses the scoped internal SDK endpoint and dispatches through the warm pool; the signed credential supplies immutable caller attribution while the named target may be another function. Recursion guard: max call depth 8. The callee's full {statusCode, headers, body} is returned; body is JSON-decoded when possible. Python: from orva import invoke, OrvaError @@ -1372,7 +1379,7 @@ Every night at 3 AM UTC, delete all KV keys that start with "session:" and are o -**Plan.** Python 3.14 handler triggered by a cron schedule `0 3 * * *` UTC. Lists keys with prefix "session:" via orva.kv.list, page by page, and lets the TTL-based sweep handle expiration — but for keys we want gone NOW, we delete explicitly. No deps. No egress. Recommended auth: leave public; cron triggers don't go through the HTTP gate, but if anyone could hit it from outside, set platform_key. +**Plan.** Python 3.14 handler triggered by a cron schedule `0 3 * * *` UTC. Lists keys with prefix "session:" via orva.kv.list, page by page, and lets the TTL-based sweep handle expiration — but for keys we want gone NOW, we delete explicitly. No deps. Requires egress because the bundled KV SDK calls Orva's control plane. Recommended auth: leave public; cron triggers don't go through the HTTP gate, but if anyone could hit it from outside, set platform_key. ```python import json @@ -1385,10 +1392,10 @@ def handler(event): while True: page = kv.list(prefix="session:", limit=200, cursor=cursor) - for key in page.get("keys", []): - kv.delete(key) + for entry in page.get("keys", []): + kv.delete(entry["key"]) deleted += 1 - cursor = page.get("cursor") + cursor = page.get("next_cursor") if not cursor: break @@ -1405,7 +1412,7 @@ def handler(event): "body": {"deleted": deleted}} ``` -Wire it up: open Schedules → New Schedule → expression `0 3 * * *`, timezone UTC, function this one. Set auth_mode to platform_key on the function so the URL can't be triggered manually by random callers. +Wire it up: enable outbound network, then open Schedules → New Schedule → expression `0 3 * * *`, timezone UTC, function this one. Set auth_mode to platform_key on the function so the URL can't be triggered manually by random callers. diff --git a/docs/API.md b/docs/API.md index d6fd560f..1e6e25be 100644 --- a/docs/API.md +++ b/docs/API.md @@ -254,9 +254,20 @@ Operator-facing function resources use the normal API-key/session auth: - `PUT|DELETE /api/v1/functions/{id}/cron/{schedule_id}` - `GET /api/v1/cron` — list schedules across all functions. +KV keys must be non-empty UTF-8 up to 256 characters and values must be valid +JSON up to 64 KiB. Internal SDK batches accept at most 100 operations and are +atomic. For put/increment/CAS, omitted `ttl_seconds` preserves an existing +expiry (new keys remain persistent), zero clears expiry, a positive value sets +or refreshes it, and a negative value returns `400 VALIDATION`. + The `/_kv` and `/_internal` route families are sandbox-SDK transport -endpoints authenticated with invocation-scoped credentials. They are not an -operator API and should not be called with long-lived API keys. +endpoints authenticated with a process-signed, function-scoped credential. +The verified claim supplies caller identity; caller headers are ignored. A +credential expires when orvad restarts, KV access is restricted to its own +namespace, cron upsert is restricted to its own schedules, and user spans must +name an active execution owned by the credential's function. Invokes and job +enqueue may target another function while retaining signed caller attribution. +These routes are not an operator API and do not accept long-lived API keys. ## Jobs diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index a1f71a8e..7cc0d12d 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -301,7 +301,11 @@ SQLite access. No ORM — explicit queries, prepared statements where hot. - `migrations.go` — schema + idempotent ALTER loop -- `async.go` — batched-flush writer for executions (50-job batches or 50ms tick) +- `async.go` — bounded priority writer: critical execution writes apply + deadline-aware backpressure; droppable logs/spans/activity use a separate + telemetry queue with saturation counters. Both queues batch commits. +- `kv.go` — validated, context-aware per-function JSON KV operations and + all-or-nothing batches; `kv_metrics.go` records operation latency/errors. - One file per resource: `functions.go`, `deployments.go`, `secrets.go`, etc. ### `backend/internal/proxy/` diff --git a/docs/CLI.md b/docs/CLI.md index f569e918..1af75987 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -314,7 +314,10 @@ orva kv delete greeter visits ``` `kv put --value` accepts an inline string, `@file`, or `@-` (stdin). -Values are JSON, capped at 64 KB. +Keys must be non-empty UTF-8 and are capped at 256 characters. Values must be +valid JSON and are capped at 64 KiB. Omitting `--ttl` preserves an existing +expiry (and creates persistent new keys); `--ttl 0` removes expiry; a positive +TTL sets or refreshes it. Negative TTLs are rejected. Atomic counters and compare-and-swap are available too: diff --git a/docs/ERRORS.md b/docs/ERRORS.md index f97ae5e3..25b84339 100644 --- a/docs/ERRORS.md +++ b/docs/ERRORS.md @@ -28,8 +28,9 @@ Fields beyond `code` and `message` are optional and may be absent. Transient err |---|---|---|---| | `INVALID_JSON` | 400 | malformed request body | no — fix the payload | | `VALIDATION` | 400 | required field missing or invalid value | no — fix the request | -| `UNAUTHORIZED` | 401 | missing or invalid API key / session | no — re-auth | +| `UNAUTHORIZED` | 401 | missing or invalid API key/session, or invalid/stale SDK credential | no — re-auth or let the worker restart | | `FORBIDDEN` | 403 | authenticated but lacks the required permission | no | +| `SDK_SCOPE_VIOLATION` | 403 | a signed worker credential attempted another KV/cron namespace or an execution/trace it does not own | no — fix the SDK request; caller scope cannot be elevated | | `NOT_FOUND` | 404 | function or route doesn't exist | no | | `FUNCTION_NOT_FOUND` | 404 | function lookup miss on a name-or-ID path (diff endpoint) | no | | `VERSION_NOT_FOUND` | 404 | deployment row not in DB (e.g. `orva diff` with a bad `--from`/`--to`) | no | @@ -53,6 +54,7 @@ Fields beyond `code` and `message` are optional and may be absent. Transient err | `POOL_AT_CAPACITY` | 503 | function pool at `dynamicMax` and ctx fired waiting | yes — `Retry-After: 5` | | `MEMORY_EXHAUSTED` | 503 | host memory budget at 80% reservation | yes — `Retry-After: 30` | | `EGRESS_POLICY_UNAVAILABLE` | 503 | a `network_mode=egress` function was invoked while no sandbox egress policy had compiled; the spawn is refused rather than run unfiltered | yes — `Retry-After: 10` (the manager recompiles every 10s), but a bad rule must be fixed first | +| `KV_UNAVAILABLE` | 503 | an atomic KV batch could not commit; no batch writes were retained | yes — retry the complete batch | | `INSUFFICIENT_DISK` | 503 | not enough free disk on the data volume to start the build | no — free space or lower `system_config.min_free_disk_mb` | | `SHUTTING_DOWN` | 503 | server is closing down | no, on this host — redirect | | `SANDBOX_ERROR` | 503 | unmapped sandbox / dispatch failure | no — investigate | diff --git a/docs/SECURITY.md b/docs/SECURITY.md index 7b644a0d..a9dde64e 100644 --- a/docs/SECURITY.md +++ b/docs/SECURITY.md @@ -28,6 +28,22 @@ Orva is **not** designed to defend against: - Side-channel attacks (Spectre / Rowhammer / cache timing) — these are out of scope for any container-grade isolation +## Sandbox SDK identity + +Each worker receives a process-signed SDK credential containing a token +version and its immutable function ID. Orvad verifies the signature on KV, +function invoke/streaming, job enqueue, cron upsert, and user-span requests; +it never trusts `X-Orva-Caller-Function` for authorization or attribution. +The signing key is random per process, so credentials copied from an old worker +become invalid after restart. + +KV access is limited to the credential's function namespace, cron upsert can +only change that function's schedules, and user spans can only attach to an +active execution owned by that function. Functions may intentionally invoke or +enqueue work for another function, but the caller recorded in traces and jobs +always comes from the signed claim. The SDK credential cannot authenticate to +operator APIs. + ## What's between user code and the host ``` diff --git a/docs/TESTING.md b/docs/TESTING.md index 4068e35f..9a8b69cd 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -778,9 +778,9 @@ Two things to know when wiring it up: ### 3.4 The shell suites Sixteen scripts under `test/`, all of which run against an **already-running** -instance (only `loadtest.sh` starts its own, destructively). **None of them are -executed by CI** — CI only `shellcheck`s them (`ci.yml:197-205`). A -syntax/quoting regression *will* redden CI; a behavioral one will not. +instance (only `loadtest.sh` starts its own, destructively). CI runs +`sdk-test.sh` against the provisioned Node/Python sandbox in both source-E2E +architecture legs; the remaining scripts are only `shellcheck`ed. Default `BASE_URL` is **18443**, so on any other port you must export it. @@ -795,8 +795,7 @@ export API_KEY=$(cat /tmp/orva-scratch/.admin-key) > nothing. Three of them are not read-only. `atscale.sh` contains **zero > `DELETE` calls** and leaves 20 functions behind permanently — verify it > yourself in one second, `grep -c DELETE test/atscale.sh` → `0` — and it is a -> **member of `run-all.sh`**, so the umbrella inherits that litter. `sdk-test.sh` -> leaves `sdk-test-noop` by design. `egress-test.sh` mutates the **global** +> **member of `run-all.sh`**, so the umbrella inherits that litter. `egress-test.sh` mutates the **global** > `egress_blocklist` mid-run and retires every warm egress pool on the instance. > If you only have a live instance, read the `run-all.sh` detail below before > running the umbrella at all. @@ -815,7 +814,7 @@ All rows below were measured against a live instance: | `build-cache-test.sh` | per-function dep-cache lifecycle: cold build, warm rebuild, purge → 200, idempotent purge, purge refused for non-functions, deploy-after-purge | **6.5 s** (warm npm) | `build-cache: 10 passed, 0 failed` | yes | | `heavy-deploy-test.sh` | async build queue: POST returns <500 ms with a `deployment_id`, real pip build, SSE stream emits, garbage requirements → `failed` while the function stays `active` | **14.3 s** (warm pip) | `heavy-deploy-test pass=12 fail=0` | yes, but writes a log file (§9) | | `tracing-test.sh` | http root span + `X-Trace-Id`, F2F parent linkage, W3C `traceparent` honored, replay = fresh trace, outlier + `baseline_p95_ms` | **10.2 s** | ANSI `✓`/`✗` then `PASS: 10` / `FAIL: 0` | yes — trap + pre-run sweep | -| `sdk-test.sh` | Python `kv.incr`/`kv.cas`/`trace.span`/`log.info`; Node `kv.putMany`/cursor `kv.list`/`kv.getMany`/`jobs.enqueue` idempotency | **3.2 s** | `sdk-test: PASS=13 FAIL=0` | **no** — leaves `sdk-test-noop` by design | +| `sdk-test.sh` | Scoped SDK auth plus Python/Node KV TTL and atomic batches, invoke/stream, jobs, cron, spans, and logs | CI-gated | `sdk-test: PASS=n FAIL=0` | yes | | `onboarding-flow.sh` | on a **virgin DB only**: onboard → session cookie → `/auth/me` → session auth → 409 re-onboard → refresh rotates → old token revoked → logout invalidates | **0.03 s** (skip path) · **0.28 s** for all 13 checks on a virgin DB | `skip (users already exist…)` · on a virgin DB `onboarding-flow pass=13 fail=0` | leaves its user | | `run-all.sh` | umbrella over 11 suites | **49.9 s** | `test/run-all-results.tsv`; exit 1 if any row is `fail` | inherits — **including `atscale.sh`'s 20 permanent functions** | | `atscale.sh` | **nothing is asserted** — deploys 20 fns and dumps metrics TSV | exit 2 without `hey` | none | **no — leaves 20 functions forever** | @@ -833,12 +832,8 @@ never confirmed by execution. `onboarding-flow.sh` use `exit $FAIL` — **the exit code is the failure count.** Never compare to 1; compare to 0. -**`sdk-test.sh`'s exit code is meaningless.** Its verdict lives in -`trap 'echo …; [ "$FAIL" -eq 0 ]' EXIT`, and bash does not propagate an EXIT -trap's last-command status to the script's exit code. Reproduced in isolation -(`FAIL=1` in the trap → script exits 0) and in situ (`PASS=0 FAIL=15` exited -**7**, the status of the last cleanup `curl`). Parse the -`sdk-test: PASS=n FAIL=m` line instead. +`sdk-test.sh` exits non-zero when any assertion fails and cleans up all three +functions it creates, including its no-op invocation target. #### `run-all.sh` in detail @@ -856,8 +851,8 @@ Runs, in order: `secrets`, `routes`, `heavy-deploy`, **`build-cache`**, `onboarding-flow`, `errors`, `rollback`, `egress`, `auth`, `tracing`, `atscale` — 11 suites. (`test/CLAUDE.md` and `docs/CONTRIBUTING.md` both omit `build-cache-test.sh` from that list.) Deliberately excluded: `api-smoke.sh` -(pure overlap), `sdk-test.sh` (different env contract and an unreliable exit -code), `loadtest.sh` (structurally incompatible — it kills whatever holds 8443 +(pure overlap), `sdk-test.sh` (different env contract and separately CI-gated), +`loadtest.sh` (structurally incompatible — it kills whatever holds 8443 and deletes `~/.orva/orva.db*`), `ceiling.sh` (needs two positional args the umbrella cannot supply). @@ -937,7 +932,7 @@ functions on `localhost:3000`): ```bash ORVA_ENDPOINT=$BASE_URL ORVA_API_KEY=$API_KEY bash test/tracing-test.sh # PASS: 10 / FAIL: 0 -ORVA_ENDPOINT=$BASE_URL ORVA_API_KEY=$API_KEY bash test/sdk-test.sh # then delete sdk-test-noop +ORVA_ENDPOINT=$BASE_URL ORVA_API_KEY=$API_KEY bash test/sdk-test.sh ``` **`run-all.sh` cannot go green on a host without `hey`** — `atscale.sh` exits 2 @@ -957,7 +952,7 @@ Several suites use **fixed, non-unique resource names**, so concurrent runs on one instance collide and an interrupted run leaves squatters: `routes-test.sh` (`/webhooks/stripe`, `/customer/*`, `/restricted/post-only`), `errors-test.sh` (`/errs/method-only`), `tracing-test.sh` -(`trace_chain_a/b/c`, `trace_outlier`), `sdk-test.sh` (`sdk-test-noop`), +(`trace_chain_a/b/c`, `trace_outlier`), `atscale.sh` (`ascale-*`), and `egress-test.sh`'s firewall rule (`example.com`). Everything else uses `$$`. @@ -1366,7 +1361,7 @@ exports.handler = async () => { }; ``` -→ `{"ok":true,"counter":3,"roundtrip":{"n":3},"keys":["qa-counter","qa-last"],"sdk":"0.6.0"}` +→ `{"ok":true,"counter":3,"roundtrip":{"n":3},"keys":["qa-counter","qa-last"],"sdk":"0.7.0"}` Operator view: `orva … kv list `, `orva … kv get qa-counter` (the `…` is the `--endpoint`/`--api-key` pair, as everywhere in this section), and @@ -2111,6 +2106,7 @@ The only job that builds this branch and runs it against a real nsjail with unprivileged user namespaces) 7. `: > test/e2e/CHECKLIST.md` — so a committed checklist can never impersonate diagnostics — then `run.py --url http://127.0.0.1:8443 --api-key "$ADMIN_KEY"` +8. `sdk-test.sh` against that same instance, covering scoped Node/Python SDK calls The arch matrix exists because this is the only place the build jail's seccomp profile is compiled by **Kafel**, and aarch64's syscall table omits several @@ -2137,7 +2133,7 @@ A regression in `env.py` is invisible to CI. | `go` vuln | `go run golang.org/x/vuln/cmd/govulncheck@v1.6.0 ./...` | a new advisory reddens CI with zero code change | | `ui` | `cd frontend && npm audit --audit-level=moderate && npm run lint && npm run build` | CI uses **`npm ci`** (lockfile-strict); `make ui` uses `npm install` and may rewrite the lockfile. `npm audit` is the classic green-locally-red-in-CI leg | | `docker` | `docker build -t orva:ci . && docker run -d -p 127.0.0.1:18443:8443 …` then poll health | CI uses `no-cache: true, pull: true`. Neither passes `--build-arg VERSION`, so both report `dev`/`unknown` — only `release.yml` stamps identity. The first 2–3 health curls fail with `Empty reply`; the 30× loop is required | -| `e2e` | §1.2 | CI uses a **host** nsjail with `setcap` + `ORVA_DISABLE_USERNS=1`; local Docker mode keeps user namespaces and `--cap-add SYS_ADMIN`. Different nsjail configuration. `make build` embeds the **committed** `ui_dist`, so the E2E job serves the committed UI snapshot | +| `e2e` | §1.2 plus `ORVA_ENDPOINT=$B ORVA_API_KEY=$K bash test/sdk-test.sh` | CI uses a **host** nsjail with `setcap` + `ORVA_DISABLE_USERNS=1`; local Docker mode keeps user namespaces and `--cap-add SYS_ADMIN`. Different nsjail configuration. `make build` embeds the **committed** `ui_dist`, so the E2E job serves the committed UI snapshot | | `cli-unit` | `go vet ./cli/... ./internal/...` && `go test ./cli/commands/ -count=1` | CI adds `-v` | | `cli-cross-build` | `bash test/cli/build-matrix.sh && bash test/cli/command-tree.sh` | the qemu-aarch64 `--version` leg is a `warn`, never a failure, and is skipped without qemu | | `install-matrix` | `ORVA_BROWSER_LEG=0 bash test/install/run-distro.sh ubuntu24` | verified: exit 0, ~4 min, host port **19449** | @@ -2146,7 +2142,7 @@ A regression in `env.py` is invisible to CI. ### 8.5 What CI never runs -**Zero `test/*.sh` suites execute in CI.** They are `shellcheck`ed only. The +**Only `test/sdk-test.sh` executes in CI.** The other shell suites are `shellcheck`ed only. The `docker image smoke` job does two inline `curl` calls — it does not call `api-smoke.sh`. Also never executed anywhere in CI: `test/install/matrix.sh`, `test/install/failure-modes.sh`, @@ -2171,7 +2167,6 @@ it; fix rather than route around where you can. |---|---| | `test/loadtest.sh` | Broken **and** destructive. `fuser -k 8443/tcp` kills whatever holds 8443; `rm -f ~/.orva/orva.db*` destroys the default-datadir DB; `HEY=~/go/bin/hey` with no guard; `orva=./orva` points at a gitignored repo-root binary; and `grep -q "deployed"` can never match because the CLI prints `Deploy submitted (deployment …)`, so all 6 deploys report `✗ FAILED` even when they succeed. It reads no env vars, so it cannot be retargeted. **`[UNVERIFIED]` — deliberately never executed.** | | `test/atscale.sh` | Its header claims it asserts budget, isolation, and no-503; the file contains **no check or assert of any kind** and no verdict. It has **zero `DELETE` calls** and permanently leaves 20 functions (`ascale-node-1..10`, `ascale-py-1..10`) — and it is a member of `run-all.sh`, so the umbrella litters any instance it is pointed at. | -| `test/sdk-test.sh` | Exit code is meaningless (§3.4). Also `[ "$x" -ge 2 ]` on a possibly-empty value emits a raw bash error instead of a clean fail. | | `test/tracing-test.sh` | Ignores `BASE_URL`/`API_KEY`, so `run-all.sh` cannot drive it (§3.4). Its header documents `T3: Job propagation` and `T4: Cron is a root trace`; **neither exists in the file** — it runs T1, T2, T5, T6, T7. Two docs repeat the fiction. | | `test/onboarding-flow.sh` | Reports `pass` while asserting nothing on any non-virgin instance — i.e. inside `run-all.sh`, always. Its 13 checks are real and all pass on a virgin DB (verified, §3.4), but it never deletes the user it creates, so it is single-shot even there. | | `test/install/failure-modes.sh` | Cannot pass. It does `jq -r '.[].name'` on `/api/v1/functions`, which returns an **object** `{"functions":[…],"total":N}` — jq errors, the marker check always fails, and it reports "marker function did not survive reinstall". Its sibling `uninstall-flow.sh` uses the correct `jq -r '.functions[]? .name'`. Its onboard call also posts `{"email":…}` where the API requires `username`. Nobody noticed because CI lints it and never runs it. | @@ -2263,7 +2258,7 @@ carries both flags for that reason. | `bash test/build-cache-test.sh` | 6.5 s | `build-cache: 10 passed, 0 failed` | | `bash test/heavy-deploy-test.sh` | 14.3 s | `heavy-deploy-test pass=12 fail=0` | | `ORVA_ENDPOINT=$B ORVA_API_KEY=$K bash test/tracing-test.sh` | 10.2 s | `PASS: 10` / `FAIL: 0` | -| `ORVA_ENDPOINT=$B ORVA_API_KEY=$K bash test/sdk-test.sh` | 3.2 s | `sdk-test: PASS=13 FAIL=0` — **ignore the exit code**; delete `sdk-test-noop` after | +| `ORVA_ENDPOINT=$B ORVA_API_KEY=$K bash test/sdk-test.sh` | CI-gated | `sdk-test: PASS=n FAIL=0`; non-zero on failure; self-cleaning | | `bash test/run-all.sh` | 49.9 s | `run-all-results.tsv`, no `fail` in column 2 — **scratch instance only**: its `atscale.sh` member leaves 20 functions behind wherever `BASE_URL` points (§3.4) | | `cd test/e2e && PYTHONPATH=. ORVA_URL=$B ORVA_API_KEY=$K python3 tests/test_system.py` | ~1 s | `RESULT pass=7 fail=0` | | `cd test/e2e && … ORVA_REQUIRE_SANDBOX=1 python3 tests/test_deploy_invoke.py` | ~30 s | `ALL PASSED — 31 checks` | @@ -2288,7 +2283,6 @@ carries both flags for that reason. | `make clean` without `make embed` | deletes tracked `ui_dist/` and breaks the build | | trust `sandbox.runtime == "ok"` | it is a bare `os.Stat` | | trust a green `run.py` in isolated mode without `--rebuild` | the image may be months old | -| trust `$?` from `sdk-test.sh` | it is the last cleanup curl's status | | compare `$?` to 1 for the `exit $FAIL` suites | the code is the failure *count* | | run any `orva` command without `--endpoint`/`--api-key` | it silently targets `~/.orva/config.yaml` — your real instance (§4) | | run `run-all.sh` (or `atscale.sh`) against an instance you care about | zero `DELETE` calls; 20 functions stay forever | diff --git a/docs/reference.md b/docs/reference.md index 200444de..abf7d458 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -182,9 +182,9 @@ API. It ships beside the runtime adapter — `require('orva')` (Node) and install`. The Node SDK ships TypeScript declarations (`orva.d.ts`); the Python SDK ships a `py.typed` marker so IDEs surface full type hints. -Surface, as of v0.6: +Surface, as of v0.7: -- **`kv`** — `get` / `put` / `delete` / `list(cursor)` / `getMany` / `putMany` / `deleteMany` / `incr` / `cas`. Per-function namespace, optional TTL, single-RTT batch ops, atomic counter & CAS. +- **`kv`** — `get` / `put` / `delete` / `list(cursor)` / `getMany` / `putMany` / `deleteMany` / `incr` / `cas`. Per-function namespace, optional TTL, all-or-nothing batch ops, atomic counter & CAS. Keys are non-empty UTF-8 up to 256 characters; JSON values are capped at 64 KiB; batches are capped at 100 operations. - **`invoke(name, payload, {timeoutMs})`** — synchronous F2F call with `{statusCode, headers, body}` envelope. 8-deep call cap. - **`invokeStream(name, payload, {timeoutMs})`** — same, but yields `Uint8Array` chunks via `for await`. - **`jobs.enqueue(name, payload, {idempotencyKey, maxAttempts, scheduledAt})`** — durable background queue with built-in dedup. @@ -204,7 +204,8 @@ Surface, as of v0.6: from orva import kv def handler(event): - # Store with optional TTL (seconds). 0 = no expiry. + # Omit TTL to preserve an existing expiry (new keys are persistent). + # Pass 0 to clear expiry, or a positive number to set/refresh it. kv.put("user:42", {"name": "Ada", "tier": "pro"}, ttl_seconds=3600) # Read; default returned if missing or expired. @@ -225,6 +226,8 @@ def handler(event): const { kv } = require('orva') exports.handler = async (event) => { + // Omit ttlSeconds to preserve expiry, use 0 to clear it, or a + // positive number to set/refresh it. await kv.put('user:42', { name: 'Ada', tier: 'pro' }, { ttlSeconds: 3600 }) const user = await kv.get('user:42', null) @@ -242,6 +245,10 @@ exports.handler = async (event) => { > `GET/PUT/DELETE /api/v1/functions//kv[/]`. MCP tools: > `kv_list` / `kv_get` / `kv_put` / `kv_delete`. +> KV batches are atomic: validation or storage failure rejects the request and +> rolls back every write. The SDK's `getMany`, `putMany`, and `deleteMany` +> throw `OrvaError` when the batch fails; they never report partial success. + ### Function-to-function — invoke() ### F2F — Python @@ -1103,17 +1110,17 @@ Secrets are stored encrypted at rest, decrypted only into the worker environment -Every function has the `orva` module pre-imported — zero install, zero config. The SDK reaches orvad over the bridge network using HTTP, so the function MUST be created with `network_mode: "egress"` (or updated to it later). With the default `network_mode: "none"`, every SDK call fails at runtime with `ENETUNREACH` / `OrvaUnavailableError` / `TypeError: fetch failed`. THREE primitives: +Every function has the `orva` module pre-imported — zero install, zero config. The SDK reaches orvad over the bridge network using HTTP, so the function MUST be created with `network_mode: "egress"` (or updated to it later). With the default `network_mode: "none"`, every SDK call fails at runtime with `ENETUNREACH` / `OrvaUnavailableError` / `TypeError: fetch failed`. Core primitives: ## orva.kv — per-function key/value store on SQLite -Per-function namespace; keys never collide across functions. Optional TTL in seconds (sweep every 5 min AND filtered at read time, so stale reads are impossible). Values JSON-serialised; cap 64 KB per value. Keys cap 256 chars. Use for: caches, idempotency keys, rate-limit counters, light session state, feature flags, last-seen markers. NOT a primary database, NOT a queue, NOT for blob storage. +Per-function namespace enforced by a process-signed, function-scoped SDK credential; caller identity never comes from a request header. Keys never collide across functions. TTL is optional: omit it to preserve an existing expiry (new keys are persistent), pass 0 to clear expiry, or pass a positive value to set/refresh it; negative values are rejected. Expiry is filtered at read time and swept every 5 minutes. Values must be valid JSON and are capped at 64 KiB; keys must be non-empty UTF-8 and are capped at 256 characters. Batches are all-or-nothing and capped at 100 operations. Use for: caches, idempotency keys, rate-limit counters, light session state, feature flags, last-seen markers. NOT a primary database, NOT a queue, NOT for blob storage. Python: from orva import kv kv.put("user:42", {"name": "Ada", "tier": "pro"}, ttl_seconds=3600) user = kv.get("user:42", default=None) # → dict, or None - pages = kv.list(prefix="page:", limit=50) # → [keys]; pass cursor= for pagination + pages = kv.list(prefix="page:", limit=50) # → {keys, next_cursor}; pass next_cursor back as cursor kv.delete("user:42") # idempotent; no error if missing Node: @@ -1144,8 +1151,8 @@ Tell the user about this when their function uses kv state and they might want to inspect it. kv.put(f"req:{idempotency_key}", "1", ttl_seconds=86400) -## orva.invoke — function-to-function calls (no HTTP, no auth) -Bypasses the proxy stack and dispatches via the warm pool. Faster than internal HTTP, no signing required. Recursion guard: max call depth 8. The callee's full {statusCode, headers, body} is returned; body is JSON-decoded when possible. +## orva.invoke — function-to-function calls +Uses the scoped internal SDK endpoint and dispatches through the warm pool; the signed credential supplies immutable caller attribution while the named target may be another function. Recursion guard: max call depth 8. The callee's full {statusCode, headers, body} is returned; body is JSON-decoded when possible. Python: from orva import invoke, OrvaError @@ -1372,7 +1379,7 @@ Every night at 3 AM UTC, delete all KV keys that start with "session:" and are o -**Plan.** Python 3.14 handler triggered by a cron schedule `0 3 * * *` UTC. Lists keys with prefix "session:" via orva.kv.list, page by page, and lets the TTL-based sweep handle expiration — but for keys we want gone NOW, we delete explicitly. No deps. No egress. Recommended auth: leave public; cron triggers don't go through the HTTP gate, but if anyone could hit it from outside, set platform_key. +**Plan.** Python 3.14 handler triggered by a cron schedule `0 3 * * *` UTC. Lists keys with prefix "session:" via orva.kv.list, page by page, and lets the TTL-based sweep handle expiration — but for keys we want gone NOW, we delete explicitly. No deps. Requires egress because the bundled KV SDK calls Orva's control plane. Recommended auth: leave public; cron triggers don't go through the HTTP gate, but if anyone could hit it from outside, set platform_key. ```python import json @@ -1385,10 +1392,10 @@ def handler(event): while True: page = kv.list(prefix="session:", limit=200, cursor=cursor) - for key in page.get("keys", []): - kv.delete(key) + for entry in page.get("keys", []): + kv.delete(entry["key"]) deleted += 1 - cursor = page.get("cursor") + cursor = page.get("next_cursor") if not cursor: break @@ -1405,7 +1412,7 @@ def handler(event): "body": {"deleted": deleted}} ``` -Wire it up: open Schedules → New Schedule → expression `0 3 * * *`, timezone UTC, function this one. Set auth_mode to platform_key on the function so the URL can't be triggered manually by random callers. +Wire it up: enable outbound network, then open Schedules → New Schedule → expression `0 3 * * *`, timezone UTC, function this one. Set auth_mode to platform_key on the function so the URL can't be triggered manually by random callers. diff --git a/frontend/public/docs.md b/frontend/public/docs.md index 200444de..abf7d458 100644 --- a/frontend/public/docs.md +++ b/frontend/public/docs.md @@ -182,9 +182,9 @@ API. It ships beside the runtime adapter — `require('orva')` (Node) and install`. The Node SDK ships TypeScript declarations (`orva.d.ts`); the Python SDK ships a `py.typed` marker so IDEs surface full type hints. -Surface, as of v0.6: +Surface, as of v0.7: -- **`kv`** — `get` / `put` / `delete` / `list(cursor)` / `getMany` / `putMany` / `deleteMany` / `incr` / `cas`. Per-function namespace, optional TTL, single-RTT batch ops, atomic counter & CAS. +- **`kv`** — `get` / `put` / `delete` / `list(cursor)` / `getMany` / `putMany` / `deleteMany` / `incr` / `cas`. Per-function namespace, optional TTL, all-or-nothing batch ops, atomic counter & CAS. Keys are non-empty UTF-8 up to 256 characters; JSON values are capped at 64 KiB; batches are capped at 100 operations. - **`invoke(name, payload, {timeoutMs})`** — synchronous F2F call with `{statusCode, headers, body}` envelope. 8-deep call cap. - **`invokeStream(name, payload, {timeoutMs})`** — same, but yields `Uint8Array` chunks via `for await`. - **`jobs.enqueue(name, payload, {idempotencyKey, maxAttempts, scheduledAt})`** — durable background queue with built-in dedup. @@ -204,7 +204,8 @@ Surface, as of v0.6: from orva import kv def handler(event): - # Store with optional TTL (seconds). 0 = no expiry. + # Omit TTL to preserve an existing expiry (new keys are persistent). + # Pass 0 to clear expiry, or a positive number to set/refresh it. kv.put("user:42", {"name": "Ada", "tier": "pro"}, ttl_seconds=3600) # Read; default returned if missing or expired. @@ -225,6 +226,8 @@ def handler(event): const { kv } = require('orva') exports.handler = async (event) => { + // Omit ttlSeconds to preserve expiry, use 0 to clear it, or a + // positive number to set/refresh it. await kv.put('user:42', { name: 'Ada', tier: 'pro' }, { ttlSeconds: 3600 }) const user = await kv.get('user:42', null) @@ -242,6 +245,10 @@ exports.handler = async (event) => { > `GET/PUT/DELETE /api/v1/functions//kv[/]`. MCP tools: > `kv_list` / `kv_get` / `kv_put` / `kv_delete`. +> KV batches are atomic: validation or storage failure rejects the request and +> rolls back every write. The SDK's `getMany`, `putMany`, and `deleteMany` +> throw `OrvaError` when the batch fails; they never report partial success. + ### Function-to-function — invoke() ### F2F — Python @@ -1103,17 +1110,17 @@ Secrets are stored encrypted at rest, decrypted only into the worker environment -Every function has the `orva` module pre-imported — zero install, zero config. The SDK reaches orvad over the bridge network using HTTP, so the function MUST be created with `network_mode: "egress"` (or updated to it later). With the default `network_mode: "none"`, every SDK call fails at runtime with `ENETUNREACH` / `OrvaUnavailableError` / `TypeError: fetch failed`. THREE primitives: +Every function has the `orva` module pre-imported — zero install, zero config. The SDK reaches orvad over the bridge network using HTTP, so the function MUST be created with `network_mode: "egress"` (or updated to it later). With the default `network_mode: "none"`, every SDK call fails at runtime with `ENETUNREACH` / `OrvaUnavailableError` / `TypeError: fetch failed`. Core primitives: ## orva.kv — per-function key/value store on SQLite -Per-function namespace; keys never collide across functions. Optional TTL in seconds (sweep every 5 min AND filtered at read time, so stale reads are impossible). Values JSON-serialised; cap 64 KB per value. Keys cap 256 chars. Use for: caches, idempotency keys, rate-limit counters, light session state, feature flags, last-seen markers. NOT a primary database, NOT a queue, NOT for blob storage. +Per-function namespace enforced by a process-signed, function-scoped SDK credential; caller identity never comes from a request header. Keys never collide across functions. TTL is optional: omit it to preserve an existing expiry (new keys are persistent), pass 0 to clear expiry, or pass a positive value to set/refresh it; negative values are rejected. Expiry is filtered at read time and swept every 5 minutes. Values must be valid JSON and are capped at 64 KiB; keys must be non-empty UTF-8 and are capped at 256 characters. Batches are all-or-nothing and capped at 100 operations. Use for: caches, idempotency keys, rate-limit counters, light session state, feature flags, last-seen markers. NOT a primary database, NOT a queue, NOT for blob storage. Python: from orva import kv kv.put("user:42", {"name": "Ada", "tier": "pro"}, ttl_seconds=3600) user = kv.get("user:42", default=None) # → dict, or None - pages = kv.list(prefix="page:", limit=50) # → [keys]; pass cursor= for pagination + pages = kv.list(prefix="page:", limit=50) # → {keys, next_cursor}; pass next_cursor back as cursor kv.delete("user:42") # idempotent; no error if missing Node: @@ -1144,8 +1151,8 @@ Tell the user about this when their function uses kv state and they might want to inspect it. kv.put(f"req:{idempotency_key}", "1", ttl_seconds=86400) -## orva.invoke — function-to-function calls (no HTTP, no auth) -Bypasses the proxy stack and dispatches via the warm pool. Faster than internal HTTP, no signing required. Recursion guard: max call depth 8. The callee's full {statusCode, headers, body} is returned; body is JSON-decoded when possible. +## orva.invoke — function-to-function calls +Uses the scoped internal SDK endpoint and dispatches through the warm pool; the signed credential supplies immutable caller attribution while the named target may be another function. Recursion guard: max call depth 8. The callee's full {statusCode, headers, body} is returned; body is JSON-decoded when possible. Python: from orva import invoke, OrvaError @@ -1372,7 +1379,7 @@ Every night at 3 AM UTC, delete all KV keys that start with "session:" and are o -**Plan.** Python 3.14 handler triggered by a cron schedule `0 3 * * *` UTC. Lists keys with prefix "session:" via orva.kv.list, page by page, and lets the TTL-based sweep handle expiration — but for keys we want gone NOW, we delete explicitly. No deps. No egress. Recommended auth: leave public; cron triggers don't go through the HTTP gate, but if anyone could hit it from outside, set platform_key. +**Plan.** Python 3.14 handler triggered by a cron schedule `0 3 * * *` UTC. Lists keys with prefix "session:" via orva.kv.list, page by page, and lets the TTL-based sweep handle expiration — but for keys we want gone NOW, we delete explicitly. No deps. Requires egress because the bundled KV SDK calls Orva's control plane. Recommended auth: leave public; cron triggers don't go through the HTTP gate, but if anyone could hit it from outside, set platform_key. ```python import json @@ -1385,10 +1392,10 @@ def handler(event): while True: page = kv.list(prefix="session:", limit=200, cursor=cursor) - for key in page.get("keys", []): - kv.delete(key) + for entry in page.get("keys", []): + kv.delete(entry["key"]) deleted += 1 - cursor = page.get("cursor") + cursor = page.get("next_cursor") if not cursor: break @@ -1405,7 +1412,7 @@ def handler(event): "body": {"deleted": deleted}} ``` -Wire it up: open Schedules → New Schedule → expression `0 3 * * *`, timezone UTC, function this one. Set auth_mode to platform_key on the function so the URL can't be triggered manually by random callers. +Wire it up: enable outbound network, then open Schedules → New Schedule → expression `0 3 * * *`, timezone UTC, function this one. Set auth_mode to platform_key on the function so the URL can't be triggered manually by random callers. diff --git a/frontend/src/templates/index.js b/frontend/src/templates/index.js index a8a596d4..ea5f013e 100644 --- a/frontend/src/templates/index.js +++ b/frontend/src/templates/index.js @@ -1293,10 +1293,10 @@ const ts_hello_tsconfig = `{ // above. The Editor reads this list directly. // ───────────────────────────────────────────────────────────────────── -// v0.6 SDK showcase: structured logs, user-defined spans, atomic counters, +// v0.7 SDK showcase: structured logs, user-defined spans, atomic counters, // idempotent jobs — every new primitive in one inline handler so users // can see the dashboard waterfall + logs lane after one click. -const py_traced_pipeline = `"""v0.6 SDK showcase: trace.span + log.* + kv.incr + jobs.enqueue. +const py_traced_pipeline = `"""v0.7 SDK showcase: trace.span + log.* + kv.incr + jobs.enqueue. Open this trace in the dashboard after invoking — you'll see system spans, your two child spans ('parse' / 'transform'), and the level-tagged log lines all interleaved by timestamp.""" @@ -1375,15 +1375,15 @@ const pythonTemplates = [ description: 'Generator that yields one word every 100ms. Demonstrates v0.4 chunked streaming end-to-end.', code: py_stream_llm, deps: '' }, - { id: 'py-traced-pipeline', category: 'Showcase', label: 'v0.6 SDK pipeline', + { id: 'py-traced-pipeline', category: 'Showcase', label: 'v0.7 SDK pipeline', description: 'trace.span + log.info + kv.incr + idempotent jobs.enqueue. After invoking, open the new trace — child spans and structured logs render inline in the waterfall.', code: py_traced_pipeline, deps: '' }, ] -// v0.6 SDK showcase (Node): atomic incr, compare-and-swap loop, +// v0.7 SDK showcase (Node): atomic incr, compare-and-swap loop, // structured logging. The dashboard's Logs lane on the resulting // invocation will show every log.* call interleaved with the spans. -const node_counter_cas = `// v0.6 SDK showcase: kv.incr + kv.cas + log.info. +const node_counter_cas = `// v0.7 SDK showcase: kv.incr + kv.cas + log.info. // Demonstrates the recommended pattern for race-safe state mutation // from inside an Orva function. @@ -1454,8 +1454,8 @@ const nodeTemplates = [ description: 'POST creates a slug, GET redirects. Uses Orva KV.', code: node_url_shortener, deps: '' }, - { id: 'node-counter-cas', category: 'Showcase', label: 'v0.6 SDK counter (kv.incr + kv.cas)', - description: 'Atomic counter and a CAS-retry loop for richer state. Demonstrates v0.6 race-safe primitives and structured logging end-to-end.', + { id: 'node-counter-cas', category: 'Showcase', label: 'v0.7 SDK counter (kv.incr + kv.cas)', + description: 'Atomic counter and a CAS-retry loop for richer state. Demonstrates v0.7 race-safe primitives and structured logging end-to-end.', code: node_counter_cas, deps: '' }, // TypeScript starter — the builder's tsc step picks this up via the diff --git a/frontend/src/utils/aiPrompts.js b/frontend/src/utils/aiPrompts.js index d9f6e9f1..849d83e8 100644 --- a/frontend/src/utils/aiPrompts.js +++ b/frontend/src/utils/aiPrompts.js @@ -64,17 +64,17 @@ Secrets are stored encrypted at rest, decrypted only into the worker environment -Every function has the \`orva\` module pre-imported — zero install, zero config. The SDK reaches orvad over the bridge network using HTTP, so the function MUST be created with \`network_mode: "egress"\` (or updated to it later). With the default \`network_mode: "none"\`, every SDK call fails at runtime with \`ENETUNREACH\` / \`OrvaUnavailableError\` / \`TypeError: fetch failed\`. THREE primitives: +Every function has the \`orva\` module pre-imported — zero install, zero config. The SDK reaches orvad over the bridge network using HTTP, so the function MUST be created with \`network_mode: "egress"\` (or updated to it later). With the default \`network_mode: "none"\`, every SDK call fails at runtime with \`ENETUNREACH\` / \`OrvaUnavailableError\` / \`TypeError: fetch failed\`. Core primitives: ## orva.kv — per-function key/value store on SQLite -Per-function namespace; keys never collide across functions. Optional TTL in seconds (sweep every 5 min AND filtered at read time, so stale reads are impossible). Values JSON-serialised; cap 64 KB per value. Keys cap 256 chars. Use for: caches, idempotency keys, rate-limit counters, light session state, feature flags, last-seen markers. NOT a primary database, NOT a queue, NOT for blob storage. +Per-function namespace enforced by a process-signed, function-scoped SDK credential; caller identity never comes from a request header. Keys never collide across functions. TTL is optional: omit it to preserve an existing expiry (new keys are persistent), pass 0 to clear expiry, or pass a positive value to set/refresh it; negative values are rejected. Expiry is filtered at read time and swept every 5 minutes. Values must be valid JSON and are capped at 64 KiB; keys must be non-empty UTF-8 and are capped at 256 characters. Batches are all-or-nothing and capped at 100 operations. Use for: caches, idempotency keys, rate-limit counters, light session state, feature flags, last-seen markers. NOT a primary database, NOT a queue, NOT for blob storage. Python: from orva import kv kv.put("user:42", {"name": "Ada", "tier": "pro"}, ttl_seconds=3600) user = kv.get("user:42", default=None) # → dict, or None - pages = kv.list(prefix="page:", limit=50) # → [keys]; pass cursor= for pagination + pages = kv.list(prefix="page:", limit=50) # → {keys, next_cursor}; pass next_cursor back as cursor kv.delete("user:42") # idempotent; no error if missing Node: @@ -105,8 +105,8 @@ Tell the user about this when their function uses kv state and they might want to inspect it. kv.put(f"req:{idempotency_key}", "1", ttl_seconds=86400) -## orva.invoke — function-to-function calls (no HTTP, no auth) -Bypasses the proxy stack and dispatches via the warm pool. Faster than internal HTTP, no signing required. Recursion guard: max call depth 8. The callee's full {statusCode, headers, body} is returned; body is JSON-decoded when possible. +## orva.invoke — function-to-function calls +Uses the scoped internal SDK endpoint and dispatches through the warm pool; the signed credential supplies immutable caller attribution while the named target may be another function. Recursion guard: max call depth 8. The callee's full {statusCode, headers, body} is returned; body is JSON-decoded when possible. Python: from orva import invoke, OrvaError @@ -345,7 +345,7 @@ Every night at 3 AM UTC, delete all KV keys that start with "session:" and are o -**Plan.** Python 3.14 handler triggered by a cron schedule \`0 3 * * *\` UTC. Lists keys with prefix "session:" via orva.kv.list, page by page, and lets the TTL-based sweep handle expiration — but for keys we want gone NOW, we delete explicitly. No deps. No egress. Recommended auth: leave public; cron triggers don't go through the HTTP gate, but if anyone could hit it from outside, set platform_key. +**Plan.** Python 3.14 handler triggered by a cron schedule \`0 3 * * *\` UTC. Lists keys with prefix "session:" via orva.kv.list, page by page, and lets the TTL-based sweep handle expiration — but for keys we want gone NOW, we delete explicitly. No deps. Requires egress because the bundled KV SDK calls Orva's control plane. Recommended auth: leave public; cron triggers don't go through the HTTP gate, but if anyone could hit it from outside, set platform_key. \`\`\`python import json @@ -358,10 +358,10 @@ def handler(event): while True: page = kv.list(prefix="session:", limit=200, cursor=cursor) - for key in page.get("keys", []): - kv.delete(key) + for entry in page.get("keys", []): + kv.delete(entry["key"]) deleted += 1 - cursor = page.get("cursor") + cursor = page.get("next_cursor") if not cursor: break @@ -378,7 +378,7 @@ def handler(event): "body": {"deleted": deleted}} \`\`\` -Wire it up: open Schedules → New Schedule → expression \`0 3 * * *\`, timezone UTC, function this one. Set auth_mode to platform_key on the function so the URL can't be triggered manually by random callers. +Wire it up: enable outbound network, then open Schedules → New Schedule → expression \`0 3 * * *\`, timezone UTC, function this one. Set auth_mode to platform_key on the function so the URL can't be triggered manually by random callers. diff --git a/frontend/src/views/Docs.vue b/frontend/src/views/Docs.vue index af28bdca..cdd8d701 100644 --- a/frontend/src/views/Docs.vue +++ b/frontend/src/views/Docs.vue @@ -321,7 +321,7 @@ module exposes three primitives every function can use without extra dependencies: a per-function key/value store, in-process calls to other Orva functions, and a fire-and-forget background - job queue. Routes through the per-process internal token + job queue. Routes through a process-signed, function-scoped credential injected at worker spawn time.

diff --git a/frontend/src/views/KVStore.vue b/frontend/src/views/KVStore.vue index e83618b2..ce9d025d 100644 --- a/frontend/src/views/KVStore.vue +++ b/frontend/src/views/KVStore.vue @@ -59,14 +59,14 @@ Showing first {{ rows.length }}. Narrow the prefix to see more. @@ -87,10 +87,10 @@
{{ row.key }}
-
+
{{ valuePreview(row.value) }}
-
+
-
+
Key
@@ -234,7 +234,7 @@
-
+
TTL
-
+
Updated
@@ -253,7 +253,7 @@
-
+
Size
@@ -274,9 +274,10 @@ min="0" max="31536000" class="mt-2 w-full bg-surface border border-border rounded px-3 py-2 text-sm text-white font-mono focus:outline-none focus:ring-1 focus:ring-white focus:border-white" + @input="inspect.ttlTouched = true" >

Must be between 0 and 31536000 (1 year). @@ -359,9 +360,10 @@ min="0" max="31536000" class="mt-2 w-full bg-surface border border-border rounded px-3 py-2 text-sm text-white font-mono focus:outline-none focus:ring-1 focus:ring-white focus:border-white" + @input="setKey.ttlTouched = true" >

Must be between 0 and 31536000 (1 year). @@ -432,12 +434,13 @@ const saving = ref(false) const prefix = ref('') // Inspect drawer state. row holds the wire entry; text is the editable -// JSON; ttlSeconds is what we send back on Save (0 = never). +// JSON; untouched TTL inputs are omitted so updates preserve expiry. const inspect = reactive({ open: false, row: null, text: '', ttlSeconds: 0, + ttlTouched: false, error: '', }) @@ -446,6 +449,7 @@ const setKey = reactive({ key: '', text: '', ttlSeconds: 0, + ttlTouched: false, error: '', }) @@ -502,6 +506,7 @@ const openInspect = (row) => { // escape-laden one-line string. inspect.text = prettyJSON(row.value) inspect.ttlSeconds = row.expires_at ? Math.max(0, Math.floor((new Date(row.expires_at) - Date.now()) / 1000)) : 0 + inspect.ttlTouched = false inspect.error = '' inspect.open = true } @@ -521,10 +526,9 @@ const saveInspect = async () => { inspect.error = '' saving.value = true try { - await kvPut(fnName.value, inspect.row.key, { - value: parsed, - ttl_seconds: inspect.ttlSeconds || 0, - }) + const payload = { value: parsed } + if (inspect.ttlTouched) payload.ttl_seconds = inspect.ttlSeconds + await kvPut(fnName.value, inspect.row.key, payload) inspect.open = false await refresh() } catch (e) { @@ -552,6 +556,7 @@ const openSet = () => { setKey.key = '' setKey.text = '' setKey.ttlSeconds = 0 + setKey.ttlTouched = false setKey.error = '' setKey.open = true } @@ -580,10 +585,9 @@ const saveSetKey = async () => { setKey.error = '' saving.value = true try { - await kvPut(fnName.value, key, { - value: parsed, - ttl_seconds: setKey.ttlSeconds || 0, - }) + const payload = { value: parsed } + if (setKey.ttlTouched) payload.ttl_seconds = setKey.ttlSeconds + await kvPut(fnName.value, key, payload) setKey.open = false await refresh() } catch (e) { diff --git a/test/CLAUDE.md b/test/CLAUDE.md index 9787a4f6..da546e41 100644 --- a/test/CLAUDE.md +++ b/test/CLAUDE.md @@ -30,8 +30,8 @@ also accept `ORVA_ENDPOINT`/`ORVA_API_KEY` and fall back to `~/.orva/config.yaml # Umbrella suite — requires API_KEY set; writes run-all-results.tsv. Runs 11: # secrets, routes, heavy-deploy, onboarding, errors, rollback, egress, auth, # tracing, build-cache, atscale. (api-smoke / loadtest / ceiling / sdk-test are -# run individually.) NOT a CI gate — ci.yml only shellchecks test/*.sh, it never -# executes them. Several mutate the instance they run against (atscale.sh issues +# run individually.) NOT a CI gate — ci.yml shellchecks test/*.sh and runs only +# sdk-test.sh against its provisioned sandbox instance. Several mutate the instance they run against (atscale.sh issues # no DELETEs at all), so point BASE_URL at a scratch instance, not one you care # about. See docs/TESTING.md. ./test/run-all.sh diff --git a/test/e2e/tests/test_kv.py b/test/e2e/tests/test_kv.py index b989c00e..d6c5260e 100644 --- a/test/e2e/tests/test_kv.py +++ b/test/e2e/tests/test_kv.py @@ -4,6 +4,7 @@ DB directly. Lifecycle: create fn -> put key -> get -> list -> validation -> delete -> confirm gone -> cleanup fn.""" import sys +import urllib.parse from harness import OrvaClient, section, check, summary @@ -73,6 +74,36 @@ def main(): e1 = next((e for e in (entries or []) if e.get("key") == KEY), None) check("entry has size_bytes > 0", bool(e1) and isinstance(e1.get("size_bytes"), int) and e1["size_bytes"] > 0) + section("ttl semantics") + ttl_key = "e2e-ttl" + tc, _ = c.req("PUT", f"{base}/{ttl_key}", {"value": 1, "ttl_seconds": 3600}, expect=range(200, 599)) + check("positive ttl accepted", tc == 200, f"status {tc}") + _, ttl_first = c.req("GET", f"{base}/{ttl_key}", expect=range(200, 599)) + first_expiry = (ttl_first or {}).get("expires_at") + oc, _ = c.req("PUT", f"{base}/{ttl_key}", {"value": 2}, expect=range(200, 599)) + _, ttl_preserved = c.req("GET", f"{base}/{ttl_key}", expect=range(200, 599)) + check("omitted ttl preserves expiry", oc == 200 and first_expiry and + ttl_preserved.get("expires_at") == first_expiry, str(ttl_preserved)[:160]) + zc, _ = c.req("PUT", f"{base}/{ttl_key}", {"value": 3, "ttl_seconds": 0}, expect=range(200, 599)) + _, ttl_cleared = c.req("GET", f"{base}/{ttl_key}", expect=range(200, 599)) + check("explicit zero clears expiry", zc == 200 and ttl_cleared.get("expires_at") is None, + str(ttl_cleared)[:160]) + negc, _ = c.req("PUT", f"{base}/{ttl_key}", {"value": 4, "ttl_seconds": -1}, expect=range(200, 599)) + check("negative ttl rejected", negc == 400, f"status {negc}") + + section("documented boundaries") + key_256 = "k" * 256 + key_257 = "k" * 257 + kc, _ = c.req("PUT", f"{base}/{urllib.parse.quote(key_256)}", {"value": None}, expect=range(200, 599)) + check("256-character key accepted", kc == 200, f"status {kc}") + koc, _ = c.req("PUT", f"{base}/{urllib.parse.quote(key_257)}", {"value": None}, expect=range(200, 599)) + check("257-character key rejected", koc == 400, f"status {koc}") + exact_value = "v" * (64 * 1024 - 2) + ec, _ = c.req("PUT", f"{base}/exact-64k", {"value": exact_value}, expect=range(200, 599)) + check("64 KiB JSON value accepted", ec == 200, f"status {ec}") + oc, _ = c.req("PUT", f"{base}/over-64k", {"value": exact_value + "x"}, expect=range(200, 599)) + check("value over 64 KiB rejected", oc == 413, f"status {oc}") + section("validation") # Missing required `value` field -> 400 VALIDATION. vc, _ = c.req("PUT", f"{base}/{KEY}", {"ttl_seconds": 5}, expect=range(200, 599)) diff --git a/test/e2e/tests/test_security.py b/test/e2e/tests/test_security.py index 28885419..1ef27b0c 100644 --- a/test/e2e/tests/test_security.py +++ b/test/e2e/tests/test_security.py @@ -5,7 +5,8 @@ H1: authMiddleware used to skip the API-key gate for ANY non-empty X-Orva-Internal-Token header, so a request with a garbage value reached the handlers with full operator power. It must now be rejected (401) — only the -correct per-process token, which a test can't know, is accepted. +correct function-scoped, process-signed credential, which a test can't know, +is accepted. Mostly read-only; the M4 section creates one function and cleans it up.""" import sys diff --git a/test/sdk-test.sh b/test/sdk-test.sh index 2d611266..8503d43f 100755 --- a/test/sdk-test.sh +++ b/test/sdk-test.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# sdk-test.sh — end-to-end verification of the v0.6 runtime SDK surface. +# sdk-test.sh — end-to-end verification of the v0.7 runtime SDK surface. # # Deploys one Python and one Node function whose handlers exercise the # new primitives (kv.incr, kv.cas, kv.list cursor, jobs idempotency, @@ -26,7 +26,30 @@ CURL=(curl -sf -H "X-Orva-API-Key: $KEY") PASS=0 FAIL=0 -trap 'echo; echo "sdk-test: PASS=$PASS FAIL=$FAIL"; [ "$FAIL" -eq 0 ]' EXIT +noop_id="" +py_id="" +node_id="" + +finish() { + local status=$? + trap - EXIT + if [ "${SDK_TEST_KEEP_RESOURCES:-0}" = "1" ]; then + echo "sdk-test: keeping resources py=$py_id node=$node_id noop=$noop_id" + else + for id in "$py_id" "$node_id" "$noop_id"; do + if [ -n "$id" ]; then + "${CURL[@]}" -X DELETE "$BASE/api/v1/functions/$id" > /dev/null 2>&1 || true + fi + done + fi + echo + echo "sdk-test: PASS=$PASS FAIL=$FAIL" + if [ "$FAIL" -ne 0 ]; then + exit 1 + fi + exit "$status" +} +trap finish EXIT assert_eq() { local label="$1" want="$2" got="$3" @@ -93,7 +116,7 @@ fi PY_FN="sdk-test-py-$$" PY_SRC=$(cat <<'EOF' import json -from orva import kv, log, trace +from orva import kv, log, trace, jobs, crons, invoke, invoke_stream, OrvaError def handler(event): log.info("py handler start", fields={"path": event.get("path","/")}) @@ -110,8 +133,36 @@ def handler(event): with trace.span("cas"): ok = kv.cas("sdk:cas-value", "v0", "v1") + kv.put("sdk:ttl", {"step": 1}, ttl_seconds=3600) + ttl_before = kv.list(prefix="sdk:ttl")["keys"][0]["expires_at"] + kv.put("sdk:ttl", {"step": 2}) + ttl_preserved = kv.list(prefix="sdk:ttl")["keys"][0]["expires_at"] == ttl_before + kv.put("sdk:ttl", {"step": 3}, ttl_seconds=0) + ttl_cleared = "expires_at" not in kv.list(prefix="sdk:ttl")["keys"][0] + + try: + kv.put_many([ + {"key": "sdk:batch-should-rollback", "value": 1}, + {"key": "x" * 257, "value": 2}, + ]) + batch_failed = False + except OrvaError: + batch_failed = True + batch_atomic = kv.get("sdk:batch-should-rollback") is None + + invoked = invoke("sdk-test-noop", {})["body"] == "ok" + streamed = b"".join(invoke_stream("sdk-test-noop", {})).decode("utf-8") == "ok" + job = jobs.enqueue("sdk-test-noop", {}, idempotency_key="py-sdk-auth", idempotency_window_seconds=60) + cron = crons.upsert("sdk-auth-test", "0 3 * * *", enabled=False) + log.info("py handler done", fields={"final": b}) - return {"statusCode": 200, "body": {"a": a, "b": b, "cas": ok}} + return {"statusCode": 200, "body": { + "a": a, "b": b, "cas": ok, + "ttlPreserved": ttl_preserved, "ttlCleared": ttl_cleared, + "batchFailed": batch_failed, "batchAtomic": batch_atomic, + "invoked": invoked, "streamed": streamed, + "job": bool(job["id"]), "cronScoped": cron["function_id"] == __import__("orva").context.function_id, + }} EOF ) @@ -126,16 +177,37 @@ py_deploy=$(jq -nc --arg src "$PY_SRC" '{code:$src,filename:"handler.py"}') -H "Content-Type: application/json" -d "$py_deploy" > /dev/null wait_active "$py_id" -py_resp=$("${CURL[@]}" -X POST "$BASE/fn/$py_id" -H "Content-Type: application/json" -d '{}') +py_resp=$(curl -sS -H "X-Orva-API-Key: $KEY" -X POST "$BASE/fn/$py_id" \ + -H "Content-Type: application/json" -d '{}') py_body=$(echo "$py_resp" | head -c 4096) a=$(echo "$py_body" | jq -r '.a // empty') b=$(echo "$py_body" | jq -r '.b // empty') cas_ok=$(echo "$py_body" | jq -r '.cas // empty') +py_ttl_preserved=$(echo "$py_body" | jq -r '.ttlPreserved // empty') +py_ttl_cleared=$(echo "$py_body" | jq -r '.ttlCleared // empty') +py_batch_failed=$(echo "$py_body" | jq -r '.batchFailed // empty') +py_batch_atomic=$(echo "$py_body" | jq -r '.batchAtomic // empty') +py_invoked=$(echo "$py_body" | jq -r '.invoked // empty') +py_streamed=$(echo "$py_body" | jq -r '.streamed // empty') +py_job=$(echo "$py_body" | jq -r '.job // empty') +py_cron_scoped=$(echo "$py_body" | jq -r '.cronScoped // empty') + +if [ -z "$a" ]; then + echo "diag python response: $py_body" +fi assert_eq "python kv.incr first call returns 7" "7" "$a" assert_eq "python kv.incr second call returns 5" "5" "$b" assert_eq "python kv.cas succeeds on match" "true" "$cas_ok" +assert_eq "python omitted TTL preserves expiry" "true" "$py_ttl_preserved" +assert_eq "python explicit zero clears expiry" "true" "$py_ttl_cleared" +assert_eq "python invalid batch throws" "true" "$py_batch_failed" +assert_eq "python failed batch writes nothing" "true" "$py_batch_atomic" +assert_eq "python scoped invoke succeeds" "true" "$py_invoked" +assert_eq "python scoped stream invoke succeeds" "true" "$py_streamed" +assert_eq "python scoped job enqueue succeeds" "true" "$py_job" +assert_eq "python cron stays in caller scope" "true" "$py_cron_scoped" # Fetch the most recent execution for this function to peek at trace/logs. py_exec=$("${CURL[@]}" "$BASE/api/v1/executions?function_id=$py_id&limit=1" | jq -r '.executions[0]') @@ -169,7 +241,7 @@ py_log_entries=$(echo "$py_trace" | jq -r '.log_entries | length') NODE_FN="sdk-test-node-$$" NODE_SRC=$(cat <<'EOF' -const { kv, jobs, log } = require('orva') +const { kv, jobs, crons, invoke, invokeStream, context, log, OrvaError } = require('orva') exports.handler = async (event) => { log.info('node handler start') @@ -191,6 +263,30 @@ exports.handler = async (event) => { const many = await kv.getMany(['b:1', 'b:3', 'b:9999']) + await kv.put('b:ttl', { step: 1 }, { ttlSeconds: 3600 }) + const ttlBefore = (await kv.list({ prefix: 'b:ttl' })).keys[0].expires_at + await kv.put('b:ttl', { step: 2 }) + const ttlPreserved = (await kv.list({ prefix: 'b:ttl' })).keys[0].expires_at === ttlBefore + await kv.put('b:ttl', { step: 3 }, { ttlSeconds: 0 }) + const ttlCleared = !('expires_at' in (await kv.list({ prefix: 'b:ttl' })).keys[0]) + + let batchFailed = false + try { + await kv.putMany([ + { key: 'b:batch-should-rollback', value: 1 }, + { key: 'x'.repeat(257), value: 2 }, + ]) + } catch (error) { + if (!(error instanceof OrvaError)) throw error + batchFailed = true + } + const batchAtomic = (await kv.get('b:batch-should-rollback', null)) === null + const invoked = (await invoke('sdk-test-noop', {})).body === 'ok' + const streamChunks = [] + for await (const chunk of invokeStream('sdk-test-noop', {})) streamChunks.push(chunk) + const streamed = Buffer.concat(streamChunks).toString('utf8') === 'ok' + const cron = await crons.upsert('sdk-auth-test', '0 3 * * *', { enabled: false }) + // Idempotent enqueue — same key twice should return the same job id. // Target the noop helper function the test script pre-deploys. const j1 = await jobs.enqueue('sdk-test-noop', {}, { @@ -206,6 +302,8 @@ exports.handler = async (event) => { return { statusCode: 200, body: JSON.stringify({ walked, many, sameJob: j1.id === j2.id, j2Replayed: j2.replayed, + ttlPreserved, ttlCleared, batchFailed, batchAtomic, + invoked, streamed, cronScoped: cron.function_id === context.functionId, }) } } EOF @@ -229,7 +327,8 @@ wait_active "$node_id" "${CURL[@]}" -X DELETE "$BASE/api/v1/functions/$node_id/kv/$k" > /dev/null done -node_resp=$("${CURL[@]}" -X POST "$BASE/fn/$node_id" -H "Content-Type: application/json" -d '{}') +node_resp=$(curl -sS -H "X-Orva-API-Key: $KEY" -X POST "$BASE/fn/$node_id" \ + -H "Content-Type: application/json" -d '{}') node_body=$(echo "$node_resp" | head -c 4096) walked=$(echo "$node_body" | jq -r '.walked | length') @@ -237,13 +336,29 @@ many_one=$(echo "$node_body" | jq -r '.many["b:1"]') many_missing=$(echo "$node_body" | jq -r '.many["b:9999"]') same=$(echo "$node_body" | jq -r '.sameJob') replayed=$(echo "$node_body" | jq -r '.j2Replayed') +node_ttl_preserved=$(echo "$node_body" | jq -r '.ttlPreserved') +node_ttl_cleared=$(echo "$node_body" | jq -r '.ttlCleared') +node_batch_failed=$(echo "$node_body" | jq -r '.batchFailed') +node_batch_atomic=$(echo "$node_body" | jq -r '.batchAtomic') +node_invoked=$(echo "$node_body" | jq -r '.invoked') +node_streamed=$(echo "$node_body" | jq -r '.streamed') +node_cron_scoped=$(echo "$node_body" | jq -r '.cronScoped') + +if [ "$walked" = "null" ]; then + echo "diag node response: $node_body" +fi assert_eq "node kv.list cursor walks all 6 keys" "6" "$walked" assert_eq "node kv.getMany returns value for present key" "1" "$many_one" assert_eq "node kv.getMany returns null for missing key" "null" "$many_missing" assert_eq "node jobs.enqueue idempotency returns same id" "true" "$same" assert_eq "node jobs.enqueue second call has replayed=t" "true" "$replayed" - -# ── Cleanup ── -"${CURL[@]}" -X DELETE "$BASE/api/v1/functions/$py_id" > /dev/null -"${CURL[@]}" -X DELETE "$BASE/api/v1/functions/$node_id" > /dev/null +assert_eq "node omitted TTL preserves expiry" "true" "$node_ttl_preserved" +assert_eq "node explicit zero clears expiry" "true" "$node_ttl_cleared" +assert_eq "node invalid batch throws" "true" "$node_batch_failed" +assert_eq "node failed batch writes nothing" "true" "$node_batch_atomic" +assert_eq "node scoped invoke succeeds" "true" "$node_invoked" +assert_eq "node scoped stream invoke succeeds" "true" "$node_streamed" +assert_eq "node cron stays in caller scope" "true" "$node_cron_scoped" + +exit 0 From d1f465ddb1d6cbc142d2d403109c508d3b73f9c3 Mon Sep 17 00:00:00 2001 From: Harsh-2002 Date: Tue, 11 Aug 2026 11:22:42 +0000 Subject: [PATCH 2/2] Wait for async SDK trace finalization Entire-Checkpoint: 01KZR8YYCNK9XMV2RQS7SKRN04 --- test/sdk-test.sh | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/test/sdk-test.sh b/test/sdk-test.sh index 8503d43f..ef3a107e 100755 --- a/test/sdk-test.sh +++ b/test/sdk-test.sh @@ -209,15 +209,24 @@ assert_eq "python scoped stream invoke succeeds" "true" "$py_streamed" assert_eq "python scoped job enqueue succeeds" "true" "$py_job" assert_eq "python cron stays in caller scope" "true" "$py_cron_scoped" -# Fetch the most recent execution for this function to peek at trace/logs. -py_exec=$("${CURL[@]}" "$BASE/api/v1/executions?function_id=$py_id&limit=1" | jq -r '.executions[0]') -py_trace_id=$(echo "$py_exec" | jq -r '.trace_id // empty') +# Execution finalization uses the bounded critical writer. Poll briefly for the +# row instead of racing its next 50 ms flush on fast CI hosts. +py_trace_id="" +for _ in $(seq 1 30); do + py_trace_id=$("${CURL[@]}" "$BASE/api/v1/executions?function_id=$py_id&limit=1" \ + | jq -r '.executions[0].trace_id // empty') + [ -n "$py_trace_id" ] && break + sleep 0.1 +done assert_nonempty "python execution trace_id propagated" "$py_trace_id" sleep 1.5 # async writer -py_trace=$("${CURL[@]}" "$BASE/api/v1/traces/$py_trace_id") -py_user_spans=$(echo "$py_trace" | jq -r '.user_spans | length') -py_log_entries=$(echo "$py_trace" | jq -r '.log_entries | length') +py_trace='{}' +if [ -n "$py_trace_id" ]; then + py_trace=$("${CURL[@]}" "$BASE/api/v1/traces/$py_trace_id") +fi +py_user_spans=$(echo "$py_trace" | jq -r '(.user_spans // []) | length') +py_log_entries=$(echo "$py_trace" | jq -r '(.log_entries // []) | length') # 3 user spans: setup, incr, cas [ "$py_user_spans" -ge 3 ] && {