@kedem/okdb 1.0.1 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/bin/okdb.js +1 -1
  2. package/keys/public.pem +3 -0
  3. package/okdb.js +1 -1
  4. package/package.json +2 -2
  5. package/public/admin-app.js +1 -1
  6. package/public/components/admin-route-host.ok.js +1 -1
  7. package/public/components/env-picker.ok.js +1 -1
  8. package/public/components/field-path-input.ok.js +1 -1
  9. package/public/components/field.html +154 -2
  10. package/public/components/geo-map.ok.js +1 -1
  11. package/public/components/ok-cron-input.ok.js +1 -1
  12. package/public/components/ok-dropzone.ok.js +1 -1
  13. package/public/components/ok-js-input.ok.js +1 -1
  14. package/public/components/ok-json-input.ok.js +1 -1
  15. package/public/components/ok-text-monaco-input.ok.js +1 -1
  16. package/public/components/theme-toggle.ok.js +1 -1
  17. package/public/index.html +29 -2
  18. package/public/layouts/_default.ok.html +1 -1
  19. package/public/layouts/okdb-cube.ok.html +1 -1
  20. package/public/login-app.js +1 -1
  21. package/public/login.html +205 -1
  22. package/public/modules/ok.js +1 -1
  23. package/public/sections/api/index.ok.html +1 -1
  24. package/public/sections/api/parts/api-endpoint.ok.js +1 -1
  25. package/public/sections/auth/index.ok.html +1 -1
  26. package/public/sections/auth/modals/create-token-modal.ok.js +1 -1
  27. package/public/sections/auth/modals/edit-token-modal.ok.js +1 -1
  28. package/public/sections/auth/modals/perm-info-popover.ok.js +1 -1
  29. package/public/sections/auth/parts/token-detail.ok.js +1 -1
  30. package/public/sections/auth/parts/token-list.ok.js +1 -1
  31. package/public/sections/db/index.ok.html +1 -1
  32. package/public/sections/db/modals/add-item-modal.ok.js +1 -1
  33. package/public/sections/db/modals/create-env-modal.ok.js +1 -1
  34. package/public/sections/db/modals/create-fts-modal.ok.js +1 -1
  35. package/public/sections/db/modals/create-index-modal.ok.js +1 -1
  36. package/public/sections/db/modals/create-pipeline-modal.ok.js +1 -1
  37. package/public/sections/db/modals/demo-env-modal.ok.js +1 -1
  38. package/public/sections/db/modals/remove-env-modal.ok.js +1 -1
  39. package/public/sections/db/modals/schema-edit-modal.ok.js +1 -1
  40. package/public/sections/db/parts/db-overview.ok.js +1 -1
  41. package/public/sections/db/parts/db-pipelines-panel.ok.js +1 -1
  42. package/public/sections/db/parts/edit-row.ok.js +1 -1
  43. package/public/sections/db/parts/tm-timeline.ok.js +1 -1
  44. package/public/sections/db/parts/type-changelog.ok.js +1 -1
  45. package/public/sections/db/parts/type-list.ok.js +1 -1
  46. package/public/sections/db/parts/type-pipelines-tab.ok.js +1 -1
  47. package/public/sections/db/parts/type-schema-tab.ok.js +1 -1
  48. package/public/sections/db/parts/type-viewer.ok.js +1 -1
  49. package/public/sections/db/parts/type-violations-tab.ok.js +1 -1
  50. package/public/sections/docs/index.ok.html +1 -1
  51. package/public/sections/embeddings/index.ok.html +1 -1
  52. package/public/sections/embeddings/modals/register-model-modal.ok.js +1 -1
  53. package/public/sections/embeddings/parts/embed-create-panel.ok.js +1 -1
  54. package/public/sections/embeddings/parts/embed-embedder-panel.ok.js +1 -1
  55. package/public/sections/embeddings/parts/embed-engine-declaration-editor.ok.js +1 -1
  56. package/public/sections/embeddings/parts/embed-engine-list.ok.js +1 -1
  57. package/public/sections/embeddings/parts/embed-indexer-panel.ok.js +1 -1
  58. package/public/sections/embeddings/parts/embed-lifecycle-bar.ok.js +1 -1
  59. package/public/sections/embeddings/parts/embed-model-catalog.ok.js +1 -1
  60. package/public/sections/embeddings/parts/embed-overview.ok.js +1 -1
  61. package/public/sections/embeddings/parts/embed-search-panel.ok.js +1 -1
  62. package/public/sections/embeddings/parts/embed-worker-panel.ok.js +1 -1
  63. package/public/sections/embeddings/parts/embeddings-engine-panel.ok.js +1 -1
  64. package/public/sections/embeddings/parts/engines-overview.ok.js +1 -1
  65. package/public/sections/embeddings/parts/pipeline-create-panel.ok.js +1 -1
  66. package/public/sections/engines/engine-panel-shared-style.js +1 -1
  67. package/public/sections/engines/engine-ui-utils.js +1 -1
  68. package/public/sections/engines/index.ok.html +1 -1
  69. package/public/sections/engines/modals/engine-modal.ok.js +1 -1
  70. package/public/sections/engines/parts/engine-declaration-editor.ok.js +1 -1
  71. package/public/sections/engines/parts/engine-lifecycle-actions.ok.js +1 -1
  72. package/public/sections/engines/parts/engine-panel-shell.ok.js +1 -1
  73. package/public/sections/engines/parts/engine-resource-card.ok.js +1 -1
  74. package/public/sections/engines/parts/engine-row-item.ok.js +1 -1
  75. package/public/sections/engines/parts/generic-engine-panel.ok.js +1 -1
  76. package/public/sections/files/index.ok.html +1 -1
  77. package/public/sections/files/parts/files-blob-status.ok.js +1 -1
  78. package/public/sections/files/parts/files-collection-list.ok.js +1 -1
  79. package/public/sections/files/parts/files-collection-panel.ok.js +1 -1
  80. package/public/sections/files/parts/files-file-list.ok.js +1 -1
  81. package/public/sections/files/parts/files-overview.ok.js +1 -1
  82. package/public/sections/files/parts/files-path-browser.ok.js +1 -1
  83. package/public/sections/files/parts/files-upload-panel.ok.js +1 -1
  84. package/public/sections/functions/index.ok.html +1 -1
  85. package/public/sections/functions/modals/function-modal.ok.js +1 -1
  86. package/public/sections/functions/parts/function-detail.ok.js +1 -1
  87. package/public/sections/functions/parts/function-logs.ok.js +1 -1
  88. package/public/sections/functions/parts/function-runner.ok.js +1 -1
  89. package/public/sections/functions/parts/functions-list.ok.js +1 -1
  90. package/public/sections/license/index.ok.html +1 -1
  91. package/public/sections/license/parts/license-panel.ok.js +1 -1
  92. package/public/sections/processors/index.ok.html +1 -1
  93. package/public/sections/queue/index.ok.html +1 -1
  94. package/public/sections/queue/modals/create-bucket-modal.ok.js +1 -1
  95. package/public/sections/queue/modals/edit-bucket-modal.ok.js +1 -1
  96. package/public/sections/queue/modals/edit-job-modal.ok.js +1 -1
  97. package/public/sections/queue/modals/enqueue-job-modal.ok.js +1 -1
  98. package/public/sections/queue/modals/job-type-modal.ok.js +1 -1
  99. package/public/sections/queue/parts/ok-job-form.ok.js +1 -1
  100. package/public/sections/queue/parts/queue-buckets.ok.js +1 -1
  101. package/public/sections/queue/parts/queue-job-types.ok.js +1 -1
  102. package/public/sections/queue/parts/queue-jobs.ok.js +1 -1
  103. package/public/sections/queue/parts/queue-types.ok.js +1 -1
  104. package/public/sections/queue/parts/queue-workers.ok.js +1 -1
  105. package/public/sections/sync/index.ok.html +1 -1
  106. package/public/sections/sync/parts/sync-node-detail.ok.js +1 -1
  107. package/public/sections/sync/parts/sync-retention.ok.js +1 -1
  108. package/public/sections/sync/parts/sync-topology.ok.js +1 -1
  109. package/public/sections/system/index.ok.html +1 -1
  110. package/public/sections/system/modals/data-ops-modal.ok.js +1 -1
  111. package/public/sections/system/parts/system-api-panel.ok.js +1 -1
  112. package/public/sections/system/parts/system-auth-panel.ok.js +1 -1
  113. package/public/sections/system/parts/system-log-history.ok.js +1 -1
  114. package/public/sections/system/parts/system-log.ok.js +1 -1
  115. package/public/sections/system/parts/system-status.ok.js +1 -1
  116. package/public/sections/system/parts/system-sync-panel.ok.js +1 -1
  117. package/public/sections/views/index.ok.html +1 -1
  118. package/public/sections/views/modals/create-view-modal.ok.js +1 -1
  119. package/public/sections/views/parts/view-detail.ok.js +1 -1
  120. package/public/sections/views/parts/views-list.ok.js +1 -1
  121. package/public/sections/views/parts/views-overview.ok.js +1 -1
  122. package/public/setup-app.js +1 -1
  123. package/public/setup.html +189 -1
  124. package/public/stores/auth-store.js +1 -1
  125. package/public/stores/events-store.js +1 -1
  126. package/public/stores/functions-store.js +1 -1
  127. package/public/stores/okdb-store.js +1 -1
  128. package/public/stores/theme-store.js +1 -1
  129. package/public/test-cron-input.html +53 -3
  130. package/public/test-json-input.html +182 -3
  131. package/public/util/permissions.js +1 -1
  132. package/public/util/sift-schema.js +1 -1
  133. package/public/vendor/okjs/ok.esm.min.js +1 -2
  134. package/public/view-models/type-query-vm.js +1 -1
  135. package/okdb-data/default/data.mdb +0 -0
  136. package/okdb-data/default/lock.mdb +0 -0
  137. package/okdb-data/~log/data.mdb +0 -0
  138. package/okdb-data/~log/lock.mdb +0 -0
  139. package/okdb-data/~system/data.mdb +0 -0
  140. package/okdb-data/~system/lock.mdb +0 -0
  141. package/public/images/logo.png +0 -0
  142. package/public/images/logo.svg +0 -333
@@ -1 +1 @@
1
- function _0x447a(){const _0x18bb3f=['runId','Failed\x20to\x20load\x20run','error','9ReaVmH','join','781718KRnHbB','dryRunMode','setPayloadDraft','55870SLrCeX','execution','dryRunActions','stringify','finishedAt','672655FRjYmW','toast','toLocaleTimeString','\x0a\x20\x20<div\x20style=\x22display:flex;\x20flex-direction:column;\x20gap:12px;\x20min-height:0;\x20height:100%;\x22>\x0a\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20align-items:center;\x20justify-content:space-between;\x20gap:8px;\x22>\x0a\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20flex-direction:column;\x20gap:4px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22font-size:11px;\x20text-transform:uppercase;\x20color:var(--okdb-app-fg-muted);\x22>Run\x20selected\x20function</div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22font-size:12px;\x20color:var(--okdb-app-fg-muted);\x22>Only\x20affects\x20the\x20currently\x20selected\x20function.</div>\x0a\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20gap:8px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<button\x20@click=\x22refresh()\x22\x20:disabled=\x22!store.selectedName\x20||\x20running\x22\x20style=\x22padding:6px\x2010px;\x20font-size:12px;\x22>Refresh</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<button\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20@click=\x22toggleDryRun()\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20:disabled=\x22running\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20:style=\x22dryRunMode\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20?\x20\x27padding:6px\x2010px;\x20font-size:12px;\x20border-color:var(--okdb-accent);\x20color:var(--okdb-accent);\x27\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20:\x20\x27padding:6px\x2010px;\x20font-size:12px;\x27\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Dry\x20Run{{\x20dryRunMode\x20?\x20\x27\x20✓\x27\x20:\x20\x27\x27\x20}}\x0a\x20\x20\x20\x20\x20\x20\x20\x20</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<button\x20@click=\x22runNow()\x22\x20:disabled=\x22!store.selectedName\x20||\x20running\x22\x20style=\x22padding:6px\x2010px;\x20font-size:12px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20{{\x20running\x20?\x20\x27Running…\x27\x20:\x20(dryRunMode\x20?\x20\x27Run\x20(dry)\x27\x20:\x20\x27Run\x27)\x20}}\x0a\x20\x20\x20\x20\x20\x20\x20\x20</button>\x0a\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20</div>\x0a\x0a\x20\x20\x20\x20<div\x20style=\x22height:220px;\x20min-height:220px;\x20border:1px\x20solid\x20var(--okdb-app-border);\x20border-radius:10px;\x20overflow:hidden;\x20background:var(--okdb-app-bg-deep);\x22>\x0a\x20\x20\x20\x20\x20\x20<ok-text-monaco-input\x0a\x20\x20\x20\x20\x20\x20\x20\x20style=\x22height:100%;\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20language=\x22json\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20:value=\x22payloadDraft\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20@edit:debounce.150=\x22setPayload($event.detail)\x22\x0a\x20\x20\x20\x20\x20\x20></ok-text-monaco-input>\x0a\x20\x20\x20\x20</div>\x0a\x0a\x20\x20\x20\x20<if\x20:=\x22payloadError\x22>\x0a\x20\x20\x20\x20\x20\x20<div\x20style=\x22font-size:12px;\x20color:var(--okdb-danger);\x20line-height:1.5;\x22>{{\x20payloadError\x20}}</div>\x0a\x20\x20\x20\x20</if>\x0a\x0a\x20\x20\x20\x20<div\x20style=\x22padding:12px;\x20border:1px\x20solid\x20var(--okdb-app-border);\x20border-radius:10px;\x20background:var(--okdb-app-bg-deep);\x20min-height:0;\x20overflow:auto;\x22>\x0a\x20\x20\x20\x20\x20\x20<div\x20style=\x22font-size:11px;\x20text-transform:uppercase;\x20color:var(--okdb-app-fg-muted);\x20margin-bottom:8px;\x22>Recent\x20runs\x20for\x20this\x20function</div>\x0a\x20\x20\x20\x20\x20\x20<if\x20:=\x22runs.length\x20===\x200\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22font-size:12px;\x20color:var(--okdb-app-fg-muted);\x22>No\x20runs\x20yet.</div>\x0a\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20flex-direction:column;\x20gap:6px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<each\x20:of=\x22runs.slice(0,\x208)\x22\x20let:item=\x22run\x22\x20let:index=\x22idx\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<button\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20@click=\x22pickRun(run)\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20:style=\x22isSelected(run)\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20?\x20\x27text-align:left;\x20padding:8px\x2010px;\x20border:1px\x20solid\x20color-mix(in\x20srgb,\x20var(--okdb-accent)\x2053%,\x20transparent);\x20box-shadow:0\x200\x200\x201px\x20color-mix(in\x20srgb,\x20var(--okdb-accent)\x2013%,\x20transparent)\x20inset,\x200\x200\x2018px\x20color-mix(in\x20srgb,\x20var(--okdb-accent)\x209%,\x20transparent);\x20background:var(--okdb-app-bg);\x20color:var(--okdb-app-fg-strong);\x20border-radius:8px;\x27\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20:\x20\x27text-align:left;\x20padding:8px\x2010px;\x20border:1px\x20solid\x20var(--okdb-app-border);\x20background:var(--okdb-app-bg-deep);\x20color:var(--okdb-app-fg-muted);\x20border-radius:8px;\x27\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20align-items:center;\x20justify-content:space-between;\x20gap:8px;\x20font-size:12px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<strong\x20style=\x22font-family:monospace;\x20font-size:11px;\x22>{{\x20run.id\x20||\x20run.runId\x20||\x20\x27run\x27\x20}}</strong>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20align-items:center;\x20gap:8px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22idx\x20===\x200\x22><span\x20style=\x22font-size:10px;\x20padding:2px\x206px;\x20border-radius:999px;\x20border:1px\x20solid\x20color-mix(in\x20srgb,\x20var(--okdb-accent)\x2027%,\x20transparent);\x20color:var(--okdb-accent);\x22>Latest</span></if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span>{{\x20run.status\x20||\x20(run.error\x20?\x20\x27error\x27\x20:\x20\x27ok\x27)\x20}}</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20align-items:center;\x20justify-content:space-between;\x20gap:8px;\x20margin-top:6px;\x20font-size:11px;\x20color:var(--okdb-app-fg-muted);\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span>{{\x20runTime(run)\x20}}</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span>{{\x20run.execution?.durationMs\x20!=\x20null\x20?\x20run.execution.durationMs\x20+\x20\x27ms\x27\x20:\x20\x27—\x27\x20}}</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</each>\x0a\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20</div>\x0a\x0a\x20\x20\x20\x20<div\x20style=\x22padding:12px;\x20border:1px\x20solid\x20var(--okdb-app-border);\x20border-radius:10px;\x20background:var(--okdb-app-bg-deep);\x20min-height:0;\x20overflow:auto;\x22>\x0a\x20\x20\x20\x20\x20\x20<if\x20:=\x22!selectedRun\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22font-size:11px;\x20text-transform:uppercase;\x20color:var(--okdb-app-fg-muted);\x20margin-bottom:8px;\x22>Selected\x20run\x20result</div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22font-size:12px;\x20color:var(--okdb-app-fg-muted);\x22>No\x20run\x20selected\x20yet.</div>\x0a\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20<if\x20:=\x22selectedRun\x20&&\x20dryRunActions\x20!==\x20null\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22font-size:11px;\x20text-transform:uppercase;\x20color:var(--okdb-accent);\x20margin-bottom:8px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Dry\x20run\x20—\x20intercepted\x20writes\x20({{\x20dryRunActions.length\x20}})\x0a\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22dryRunActions.length\x20===\x200\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22font-size:12px;\x20color:var(--okdb-app-fg-muted);\x20margin-bottom:8px;\x22>No\x20writes\x20intercepted.</div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20flex-direction:column;\x20gap:4px;\x20margin-bottom:10px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<each\x20:of=\x22dryRunActions\x22\x20let:item=\x22action\x22\x20let:index=\x22i\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22padding:6px\x208px;\x20border:1px\x20solid\x20color-mix(in\x20srgb,\x20var(--okdb-accent)\x2020%,\x20transparent);\x20border-radius:6px;\x20background:color-mix(in\x20srgb,\x20var(--okdb-accent)\x204%,\x20transparent);\x20font-family:monospace;\x20font-size:11px;\x20color:var(--okdb-app-fg-muted);\x20line-height:1.45;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20style=\x22color:var(--okdb-accent);\x22>{{\x20action.scope\x20}}.{{\x20action.method\x20}}</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22action.envName\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20style=\x22opacity:.6;\x22>\x20[{{\x20action.envName\x20}}]</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22actionArgs(action)\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span>\x20({{\x20actionArgs(action)\x20}})</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20</each>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22selectedRun.result?.value\x20!==\x20undefined\x20&&\x20selectedRun.result?.value\x20!==\x20null\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22font-size:11px;\x20text-transform:uppercase;\x20color:var(--okdb-app-fg-muted);\x20margin-bottom:6px;\x22>Return\x20value</div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<pre\x20style=\x22margin:0;\x20white-space:pre-wrap;\x20word-break:break-word;\x20color:var(--okdb-app-fg-muted);\x20font-size:12px;\x20line-height:1.55;\x22>{{\x20formatJson(selectedRun.result.value)\x20}}</pre>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20<if\x20:=\x22selectedRun\x20&&\x20dryRunActions\x20===\x20null\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22font-size:11px;\x20text-transform:uppercase;\x20color:var(--okdb-app-fg-muted);\x20margin-bottom:8px;\x22>Selected\x20run\x20result</div>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<pre\x20style=\x22margin:0;\x20white-space:pre-wrap;\x20word-break:break-word;\x20color:var(--okdb-app-fg-muted);\x20font-size:12px;\x20line-height:1.55;\x22>{{\x20formatJson(selectedRun)\x20}}</pre>\x0a\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</div>\x0a\x20\x20','Failed\x20to\x20refresh\x20function','activeRun','map','2-digit','payloadError','store','679812HzJkPf','success','selectRun','394808avVouA','13876sfUDfT','549600FUCFYf','shared','$ok','payloadDraft','Function\x20execution\x20failed','request','runs','functions','Function\x20executed','69HCagHp','message','Function\x20refreshed','running','value','requestedAt','2UCONek','length'];_0x447a=function(){return _0x18bb3f;};return _0x447a();}const _0x1ea880=_0x3a43;(function(_0x586d1b,_0x3f5c84){const _0x8e62d5=_0x3a43,_0x7f5fc8=_0x586d1b();while(!![]){try{const _0x3f6e8d=parseInt(_0x8e62d5(0x1a4))/0x1*(-parseInt(_0x8e62d5(0x1ae))/0x2)+-parseInt(_0x8e62d5(0x19e))/0x3*(parseInt(_0x8e62d5(0x194))/0x4)+parseInt(_0x8e62d5(0x1b3))/0x5+parseInt(_0x8e62d5(0x190))/0x6+-parseInt(_0x8e62d5(0x1ab))/0x7+-parseInt(_0x8e62d5(0x193))/0x8*(-parseInt(_0x8e62d5(0x1a9))/0x9)+parseInt(_0x8e62d5(0x195))/0xa;if(_0x3f6e8d===_0x3f5c84)break;else _0x7f5fc8['push'](_0x7f5fc8['shift']());}catch(_0x1042d0){_0x7f5fc8['push'](_0x7f5fc8['shift']());}}}(_0x447a,0x1996d));import _0x51f0c2 from'../../../components/ok-text-monaco-input.ok.js';function _0x3a43(_0x458056,_0x2a7e0c){_0x458056=_0x458056-0x18d;const _0x447ada=_0x447a();let _0x3a43c1=_0x447ada[_0x458056];return _0x3a43c1;}export default{'tag':'function-runner','dependencies':[_0x51f0c2],'context':_0x526a14=>({get 'store'(){const _0x386772=_0x3a43;return _0x526a14[_0x386772(0x197)]['stores'][_0x386772(0x19c)];},get 'payloadDraft'(){const _0x2a3b5f=_0x3a43;return this[_0x2a3b5f(0x18f)][_0x2a3b5f(0x198)];},get 'selectedRun'(){const _0x150c54=_0x3a43;return this[_0x150c54(0x18f)][_0x150c54(0x1b8)];},get 'runs'(){const _0x1d851b=_0x3a43;return(this[_0x1d851b(0x18f)][_0x1d851b(0x19b)]||[])[_0x1d851b(0x1b9)](_0x66a17c=>_0x66a17c?.[_0x1d851b(0x1a2)]||_0x66a17c);},get 'running'(){const _0x2cfd9b=_0x3a43;return this[_0x2cfd9b(0x18f)][_0x2cfd9b(0x1a1)];},get 'payloadError'(){const _0x2df566=_0x3a43;return this['store'][_0x2df566(0x18e)];},get 'dryRunMode'(){return this['store']['dryRunMode'];},get 'dryRunActions'(){const _0x1b4107=_0x3a43;return this['selectedRun']?.['result']?.[_0x1b4107(0x1b0)]??null;},'toggleDryRun'(){const _0x910e24=_0x3a43;this['store'][_0x910e24(0x1ac)]=!this[_0x910e24(0x18f)][_0x910e24(0x1ac)];},'runTime'(_0x486bfb){const _0x36a181=_0x3a43,_0x550f0f=_0x486bfb?.[_0x36a181(0x1af)]?.[_0x36a181(0x1b2)]??_0x486bfb?.[_0x36a181(0x19a)]?.[_0x36a181(0x1a3)]??null;return _0x550f0f?new Date(_0x550f0f)[_0x36a181(0x1b5)]([],{'hour':_0x36a181(0x18d),'minute':_0x36a181(0x18d),'second':'2-digit'}):'—';},'isSelected'(_0x19e177){const _0x109c59=_0x3a43,_0x58a849=this['selectedRun'];return!(!_0x58a849||!_0x19e177)&&(_0x58a849['id']||_0x58a849[_0x109c59(0x1a6)])===(_0x19e177['id']||_0x19e177[_0x109c59(0x1a6)]);},'setPayload'(_0xa8d3f2){const _0x211f3f=_0x3a43;this['store'][_0x211f3f(0x1ad)](_0xa8d3f2??'{\x0a\x20\x20\x0a}');},async 'pickRun'(_0x402d3d){const _0x4a1340=_0x3a43;if(_0x402d3d)try{await this[_0x4a1340(0x18f)][_0x4a1340(0x192)](_0x402d3d['id']||_0x402d3d[_0x4a1340(0x1a6)]);}catch(_0x54d5ec){_0x526a14[_0x4a1340(0x197)][_0x4a1340(0x196)]?.[_0x4a1340(0x1b4)]?.[_0x4a1340(0x1a8)]?.(_0x54d5ec?.[_0x4a1340(0x19f)]||_0x4a1340(0x1a7));}},'formatJson'(_0x112dae){const _0x15afbb=_0x3a43;try{return JSON[_0x15afbb(0x1b1)](_0x112dae,null,0x2);}catch{return String(_0x112dae??'');}},'actionArgs'(_0x50dd48){const _0x47653d=_0x3a43,_0x4b9c25=_0x50dd48['args']??[];return 0x0===_0x4b9c25[_0x47653d(0x1a5)]?'':_0x4b9c25[_0x47653d(0x1b9)](_0x5481bb=>JSON[_0x47653d(0x1b1)](_0x5481bb))[_0x47653d(0x1aa)](',\x20');},async 'runNow'(){const _0x4c77c1=_0x3a43;try{await this[_0x4c77c1(0x18f)]['runSelected'](),_0x526a14[_0x4c77c1(0x197)]['shared']?.[_0x4c77c1(0x1b4)]?.[_0x4c77c1(0x191)]?.(this['dryRunMode']?'Dry\x20run\x20complete':_0x4c77c1(0x19d));}catch(_0x1f92a8){_0x526a14[_0x4c77c1(0x197)]['shared']?.[_0x4c77c1(0x1b4)]?.[_0x4c77c1(0x1a8)]?.(_0x1f92a8?.[_0x4c77c1(0x19f)]||_0x4c77c1(0x199));}},async 'refresh'(){const _0x1ba9a0=_0x3a43;try{await this[_0x1ba9a0(0x18f)]['refreshSelected'](),_0x526a14[_0x1ba9a0(0x197)][_0x1ba9a0(0x196)]?.[_0x1ba9a0(0x1b4)]?.[_0x1ba9a0(0x191)]?.(_0x1ba9a0(0x1a0));}catch(_0x16a804){_0x526a14[_0x1ba9a0(0x197)][_0x1ba9a0(0x196)]?.[_0x1ba9a0(0x1b4)]?.['error']?.(_0x16a804?.[_0x1ba9a0(0x19f)]||_0x1ba9a0(0x1b7));}}}),'template':_0x1ea880(0x1b6)};
1
+ import e from"../../../components/ok-text-monaco-input.ok.js";export default{tag:"function-runner",dependencies:[e],context:e=>({get store(){return e.$ok.stores.functions},get payloadDraft(){return this.store.payloadDraft},get selectedRun(){return this.store.activeRun},get runs(){return(this.store.runs||[]).map(e=>e?.value||e)},get running(){return this.store.running},get payloadError(){return this.store.payloadError},get dryRunMode(){return this.store.dryRunMode},get dryRunActions(){return this.selectedRun?.result?.dryRunActions??null},toggleDryRun(){this.store.dryRunMode=!this.store.dryRunMode},runTime(e){const n=e?.execution?.finishedAt??e?.request?.requestedAt??null;return n?new Date(n).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit",second:"2-digit"}):"—"},isSelected(e){const n=this.selectedRun;return!(!n||!e)&&(n.id||n.runId)===(e.id||e.runId)},setPayload(e){this.store.setPayloadDraft(e??"{\n \n}")},async pickRun(n){if(n)try{await this.store.selectRun(n.id||n.runId)}catch(n){e.$ok.shared?.toast?.error?.(n?.message||"Failed to load run")}},formatJson(e){try{return JSON.stringify(e,null,2)}catch{return String(e??"")}},actionArgs(e){const n=e.args??[];return 0===n.length?"":n.map(e=>JSON.stringify(e)).join(", ")},async runNow(){try{await this.store.runSelected(),e.$ok.shared?.toast?.success?.(this.dryRunMode?"Dry run complete":"Function executed")}catch(n){e.$ok.shared?.toast?.error?.(n?.message||"Function execution failed")}},async refresh(){try{await this.store.refreshSelected(),e.$ok.shared?.toast?.success?.("Function refreshed")}catch(n){e.$ok.shared?.toast?.error?.(n?.message||"Failed to refresh function")}}}),template:'\n <div style="display:flex; flex-direction:column; gap:12px; min-height:0; height:100%;">\n <div style="display:flex; align-items:center; justify-content:space-between; gap:8px;">\n <div style="display:flex; flex-direction:column; gap:4px;">\n <div style="font-size:11px; text-transform:uppercase; color:var(--okdb-app-fg-muted);">Run selected function</div>\n <div style="font-size:12px; color:var(--okdb-app-fg-muted);">Only affects the currently selected function.</div>\n </div>\n <div style="display:flex; gap:8px;">\n <button @click="refresh()" :disabled="!store.selectedName || running" style="padding:6px 10px; font-size:12px;">Refresh</button>\n <button\n @click="toggleDryRun()"\n :disabled="running"\n :style="dryRunMode\n ? \'padding:6px 10px; font-size:12px; border-color:var(--okdb-accent); color:var(--okdb-accent);\'\n : \'padding:6px 10px; font-size:12px;\'">\n Dry Run{{ dryRunMode ? \' ✓\' : \'\' }}\n </button>\n <button @click="runNow()" :disabled="!store.selectedName || running" style="padding:6px 10px; font-size:12px;">\n {{ running ? \'Running…\' : (dryRunMode ? \'Run (dry)\' : \'Run\') }}\n </button>\n </div>\n </div>\n\n <div style="height:220px; min-height:220px; border:1px solid var(--okdb-app-border); border-radius:10px; overflow:hidden; background:var(--okdb-app-bg-deep);">\n <ok-text-monaco-input\n style="height:100%;"\n language="json"\n :value="payloadDraft"\n @edit:debounce.150="setPayload($event.detail)"\n ></ok-text-monaco-input>\n </div>\n\n <if :="payloadError">\n <div style="font-size:12px; color:var(--okdb-danger); line-height:1.5;">{{ payloadError }}</div>\n </if>\n\n <div style="padding:12px; border:1px solid var(--okdb-app-border); border-radius:10px; background:var(--okdb-app-bg-deep); min-height:0; overflow:auto;">\n <div style="font-size:11px; text-transform:uppercase; color:var(--okdb-app-fg-muted); margin-bottom:8px;">Recent runs for this function</div>\n <if :="runs.length === 0">\n <div style="font-size:12px; color:var(--okdb-app-fg-muted);">No runs yet.</div>\n </if>\n <div style="display:flex; flex-direction:column; gap:6px;">\n <each :of="runs.slice(0, 8)" let:item="run" let:index="idx">\n <button\n @click="pickRun(run)"\n :style="isSelected(run)\n ? \'text-align:left; padding:8px 10px; border:1px solid color-mix(in srgb, var(--okdb-accent) 53%, transparent); box-shadow:0 0 0 1px color-mix(in srgb, var(--okdb-accent) 13%, transparent) inset, 0 0 18px color-mix(in srgb, var(--okdb-accent) 9%, transparent); background:var(--okdb-app-bg); color:var(--okdb-app-fg-strong); border-radius:8px;\'\n : \'text-align:left; padding:8px 10px; border:1px solid var(--okdb-app-border); background:var(--okdb-app-bg-deep); color:var(--okdb-app-fg-muted); border-radius:8px;\'">\n <div style="display:flex; align-items:center; justify-content:space-between; gap:8px; font-size:12px;">\n <strong style="font-family:monospace; font-size:11px;">{{ run.id || run.runId || \'run\' }}</strong>\n <div style="display:flex; align-items:center; gap:8px;">\n <if :="idx === 0"><span style="font-size:10px; padding:2px 6px; border-radius:999px; border:1px solid color-mix(in srgb, var(--okdb-accent) 27%, transparent); color:var(--okdb-accent);">Latest</span></if>\n <span>{{ run.status || (run.error ? \'error\' : \'ok\') }}</span>\n </div>\n </div>\n <div style="display:flex; align-items:center; justify-content:space-between; gap:8px; margin-top:6px; font-size:11px; color:var(--okdb-app-fg-muted);">\n <span>{{ runTime(run) }}</span>\n <span>{{ run.execution?.durationMs != null ? run.execution.durationMs + \'ms\' : \'—\' }}</span>\n </div>\n </button>\n </each>\n </div>\n </div>\n\n <div style="padding:12px; border:1px solid var(--okdb-app-border); border-radius:10px; background:var(--okdb-app-bg-deep); min-height:0; overflow:auto;">\n <if :="!selectedRun">\n <div style="font-size:11px; text-transform:uppercase; color:var(--okdb-app-fg-muted); margin-bottom:8px;">Selected run result</div>\n <div style="font-size:12px; color:var(--okdb-app-fg-muted);">No run selected yet.</div>\n </if>\n <if :="selectedRun && dryRunActions !== null">\n <div style="font-size:11px; text-transform:uppercase; color:var(--okdb-accent); margin-bottom:8px;">\n Dry run — intercepted writes ({{ dryRunActions.length }})\n </div>\n <if :="dryRunActions.length === 0">\n <div style="font-size:12px; color:var(--okdb-app-fg-muted); margin-bottom:8px;">No writes intercepted.</div>\n </if>\n <div style="display:flex; flex-direction:column; gap:4px; margin-bottom:10px;">\n <each :of="dryRunActions" let:item="action" let:index="i">\n <div style="padding:6px 8px; border:1px solid color-mix(in srgb, var(--okdb-accent) 20%, transparent); border-radius:6px; background:color-mix(in srgb, var(--okdb-accent) 4%, transparent); font-family:monospace; font-size:11px; color:var(--okdb-app-fg-muted); line-height:1.45;">\n <span style="color:var(--okdb-accent);">{{ action.scope }}.{{ action.method }}</span>\n <if :="action.envName">\n <span style="opacity:.6;"> [{{ action.envName }}]</span>\n </if>\n <if :="actionArgs(action)">\n <span> ({{ actionArgs(action) }})</span>\n </if>\n </div>\n </each>\n </div>\n <if :="selectedRun.result?.value !== undefined && selectedRun.result?.value !== null">\n <div style="font-size:11px; text-transform:uppercase; color:var(--okdb-app-fg-muted); margin-bottom:6px;">Return value</div>\n <pre style="margin:0; white-space:pre-wrap; word-break:break-word; color:var(--okdb-app-fg-muted); font-size:12px; line-height:1.55;">{{ formatJson(selectedRun.result.value) }}</pre>\n </if>\n </if>\n <if :="selectedRun && dryRunActions === null">\n <div style="font-size:11px; text-transform:uppercase; color:var(--okdb-app-fg-muted); margin-bottom:8px;">Selected run result</div>\n <pre style="margin:0; white-space:pre-wrap; word-break:break-word; color:var(--okdb-app-fg-muted); font-size:12px; line-height:1.55;">{{ formatJson(selectedRun) }}</pre>\n </if>\n </div>\n </div>\n '};
@@ -1 +1 @@
1
- function _0x5060(_0x2716c9,_0x426b08){_0x2716c9=_0x2716c9-0xdf;var _0x5ead84=_0x5ead();var _0x5060c7=_0x5ead84[_0x2716c9];return _0x5060c7;}function _0x5ead(){var _0x101919=['$ok','8nlrqDT','functions','overview','create','3PgxRTz','2774277uoEnzo','800199DKRhlS','2243941TeTsnz','27485CqwaVI','selectedName','items','16522890azJltf','42jMLedq','currentView','store','2513096ZHRMys','951334WJfwJD','\x0a\x20\x20<div\x20style=\x22display:flex;\x20flex-direction:column;\x20min-height:0;\x20height:100%;\x22>\x0a\x20\x20\x20\x20<button\x20class=\x22functions-list__add\x22\x20@click=\x22createNew()\x22>+\x20New</button>\x0a\x0a\x20\x20\x20\x20<button\x0a\x20\x20\x20\x20\x20\x20class=\x22functions-list__item\x22\x0a\x20\x20\x20\x20\x20\x20:class=\x22isOverview\x20?\x20\x27functions-list__item--selected\x27\x20:\x20\x27\x27\x22\x0a\x20\x20\x20\x20\x20\x20@click=\x22openOverview()\x22>\x0a\x20\x20\x20\x20\x20\x20<span\x20class=\x22functions-list__name\x22>Overview</span>\x0a\x20\x20\x20\x20</button>\x0a\x0a\x20\x20\x20\x20<div\x20style=\x22height:1px;\x20background:var(--okdb-app-surface-2);\x20margin:2px\x200\x206px;\x22></div>\x0a\x0a\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20flex-direction:column;\x20gap:2px;\x20min-height:0;\x20overflow:auto;\x22>\x0a\x20\x20\x20\x20\x20\x20<if\x20:=\x22loading\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22padding:10px;\x20border:1px\x20dashed\x20var(--okdb-app-border);\x20border-radius:8px;\x20color:var(--okdb-app-fg-muted);\x20font-size:12px;\x20line-height:1.5;\x20margin-bottom:6px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Loading\x20functions…\x0a\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20\x20\x20</if>\x0a\x0a\x20\x20\x20\x20\x20\x20<if\x20:=\x22items.length\x20===\x200\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<div\x20style=\x22padding:10px;\x20border:1px\x20dashed\x20var(--okdb-app-border);\x20border-radius:8px;\x20color:var(--okdb-app-fg-muted);\x20font-size:12px;\x20line-height:1.5;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20No\x20functions\x20in\x20this\x20scope\x20yet.\x0a\x20\x20\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20\x20\x20</if>\x0a\x0a\x20\x20\x20\x20\x20\x20<each\x20:of=\x22items\x22\x20let:item=\x22fn\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<button\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20class=\x22functions-list__item\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20:class=\x22fn.key\x20===\x20selectedName\x20?\x20\x27functions-list__item--selected\x27\x20:\x20\x27\x27\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20@click=\x22pick(fn.key)\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20class=\x22functions-list__name\x22>{{\x20fn.key\x20}}</span>\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22fn.value?.unsafe\x22><span\x20style=\x22font-size:10px;\x20color:var(--okdb-orange);\x20flex-shrink:0;\x22\x20title=\x22Unsafe\x20function\x22>&#9888;</span></if>\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20class=\x22functions-list__meta\x22>v{{\x20fn.value?.version\x20??\x20\x27—\x27\x20}}</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</button>\x0a\x20\x20\x20\x20\x20\x20</each>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</div>\x0a\x20\x20','loading','$dispatch','detail','stores'];_0x5ead=function(){return _0x101919;};return _0x5ead();}var _0x42ea31=_0x5060;(function(_0x15460d,_0x2a8f36){var _0x3574ce=_0x5060,_0xdfbbe4=_0x15460d();while(!![]){try{var _0x5aa7d1=-parseInt(_0x3574ce(0xf3))/0x1+-parseInt(_0x3574ce(0xe6))/0x2*(parseInt(_0x3574ce(0xf1))/0x3)+-parseInt(_0x3574ce(0xe5))/0x4+-parseInt(_0x3574ce(0xf5))/0x5*(-parseInt(_0x3574ce(0xe2))/0x6)+parseInt(_0x3574ce(0xf4))/0x7*(parseInt(_0x3574ce(0xed))/0x8)+parseInt(_0x3574ce(0xf2))/0x9+parseInt(_0x3574ce(0xe1))/0xa;if(_0x5aa7d1===_0x2a8f36)break;else _0xdfbbe4['push'](_0xdfbbe4['shift']());}catch(_0x5933af){_0xdfbbe4['push'](_0xdfbbe4['shift']());}}}(_0x5ead,0x656d4));export default{'tag':'functions-list','context':_0x4f53e5=>({get 'store'(){var _0x16ae00=_0x5060;return _0x4f53e5[_0x16ae00(0xec)][_0x16ae00(0xeb)][_0x16ae00(0xee)];},get 'items'(){var _0x188035=_0x5060;return this[_0x188035(0xe4)][_0x188035(0xe0)]||[];},get 'selectedName'(){var _0x436184=_0x5060;return this[_0x436184(0xe4)][_0x436184(0xdf)];},get 'loading'(){var _0x2677c9=_0x5060;return this[_0x2677c9(0xe4)][_0x2677c9(0xe8)];},get 'isOverview'(){var _0x3b2624=_0x5060;return _0x3b2624(0xea)!==this[_0x3b2624(0xe4)][_0x3b2624(0xe3)]||!this[_0x3b2624(0xdf)];},'pick'(_0x1fd2f8){_0x4f53e5['$dispatch']('open',_0x1fd2f8);},'openOverview'(){var _0x1be626=_0x5060;_0x4f53e5[_0x1be626(0xe9)](_0x1be626(0xef));},'createNew'(){var _0x50c32a=_0x5060;_0x4f53e5[_0x50c32a(0xe9)](_0x50c32a(0xf0));}}),'style':'\x0a\x20\x20\x20\x20functions-list\x20{\x0a\x20\x20\x20\x20\x20\x20position:\x20relative;\x0a\x20\x20\x20\x20\x20\x20display:\x20flex;\x0a\x20\x20\x20\x20\x20\x20flex-direction:\x20column;\x0a\x20\x20\x20\x20\x20\x20gap:\x202px;\x0a\x20\x20\x20\x20\x20\x20min-height:\x200;\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20functions-list\x20button\x20{\x0a\x20\x20\x20\x20\x20\x20text-align:\x20left;\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20.functions-list__add\x20{\x0a\x20\x20\x20\x20\x20\x20font-size:\x2012px;\x0a\x20\x20\x20\x20\x20\x20padding:\x207px\x2010px;\x0a\x20\x20\x20\x20\x20\x20margin-bottom:\x204px;\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20.functions-list__item\x20{\x0a\x20\x20\x20\x20\x20\x20display:\x20flex;\x0a\x20\x20\x20\x20\x20\x20align-items:\x20center;\x0a\x20\x20\x20\x20\x20\x20gap:\x206px;\x0a\x20\x20\x20\x20\x20\x20padding:\x206px\x2010px;\x0a\x20\x20\x20\x20\x20\x20font-size:\x2012px;\x0a\x20\x20\x20\x20\x20\x20background:\x20transparent;\x0a\x20\x20\x20\x20\x20\x20border-color:\x20transparent;\x0a\x20\x20\x20\x20\x20\x20color:\x20var(--okdb-app-fg-muted);\x0a\x20\x20\x20\x20\x20\x20border-radius:\x207px;\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20.functions-list__item:hover\x20{\x0a\x20\x20\x20\x20\x20\x20background:\x20var(--okdb-app-surface-2);\x0a\x20\x20\x20\x20\x20\x20border-color:\x20var(--okdb-app-border);\x0a\x20\x20\x20\x20\x20\x20color:\x20var(--okdb-app-fg);\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20.functions-list__item--selected\x20{\x0a\x20\x20\x20\x20\x20\x20background:\x20var(--okdb-app-bg-deep)\x20!important;\x0a\x20\x20\x20\x20\x20\x20border-color:\x20color-mix(in\x20srgb,\x20var(--okdb-accent)\x2027%,\x20transparent)\x20!important;\x0a\x20\x20\x20\x20\x20\x20color:\x20var(--okdb-accent)\x20!important;\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20.functions-list__name\x20{\x0a\x20\x20\x20\x20\x20\x20flex:\x201;\x0a\x20\x20\x20\x20\x20\x20font-family:\x20monospace;\x0a\x20\x20\x20\x20\x20\x20font-size:\x2012px;\x0a\x20\x20\x20\x20\x20\x20overflow:\x20hidden;\x0a\x20\x20\x20\x20\x20\x20text-overflow:\x20ellipsis;\x0a\x20\x20\x20\x20\x20\x20white-space:\x20nowrap;\x0a\x20\x20\x20\x20}\x0a\x20\x20\x20\x20.functions-list__meta\x20{\x0a\x20\x20\x20\x20\x20\x20font-size:\x2011px;\x0a\x20\x20\x20\x20\x20\x20opacity:\x20.55;\x0a\x20\x20\x20\x20\x20\x20flex-shrink:\x200;\x0a\x20\x20\x20\x20}\x0a\x20\x20','template':_0x42ea31(0xe7)};
1
+ export default{tag:"functions-list",context:n=>({get store(){return n.$ok.stores.functions},get items(){return this.store.items||[]},get selectedName(){return this.store.selectedName},get loading(){return this.store.loading},get isOverview(){return"detail"!==this.store.currentView||!this.selectedName},pick(e){n.$dispatch("open",e)},openOverview(){n.$dispatch("overview")},createNew(){n.$dispatch("create")}}),style:"\n functions-list {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-height: 0;\n }\n functions-list button {\n text-align: left;\n }\n .functions-list__add {\n font-size: 12px;\n padding: 7px 10px;\n margin-bottom: 4px;\n }\n .functions-list__item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n font-size: 12px;\n background: transparent;\n border-color: transparent;\n color: var(--okdb-app-fg-muted);\n border-radius: 7px;\n }\n .functions-list__item:hover {\n background: var(--okdb-app-surface-2);\n border-color: var(--okdb-app-border);\n color: var(--okdb-app-fg);\n }\n .functions-list__item--selected {\n background: var(--okdb-app-bg-deep) !important;\n border-color: color-mix(in srgb, var(--okdb-accent) 27%, transparent) !important;\n color: var(--okdb-accent) !important;\n }\n .functions-list__name {\n flex: 1;\n font-family: monospace;\n font-size: 12px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .functions-list__meta {\n font-size: 11px;\n opacity: .55;\n flex-shrink: 0;\n }\n ",template:'\n <div style="display:flex; flex-direction:column; min-height:0; height:100%;">\n <button class="functions-list__add" @click="createNew()">+ New</button>\n\n <button\n class="functions-list__item"\n :class="isOverview ? \'functions-list__item--selected\' : \'\'"\n @click="openOverview()">\n <span class="functions-list__name">Overview</span>\n </button>\n\n <div style="height:1px; background:var(--okdb-app-surface-2); margin:2px 0 6px;"></div>\n\n <div style="display:flex; flex-direction:column; gap:2px; min-height:0; overflow:auto;">\n <if :="loading">\n <div style="padding:10px; border:1px dashed var(--okdb-app-border); border-radius:8px; color:var(--okdb-app-fg-muted); font-size:12px; line-height:1.5; margin-bottom:6px;">\n Loading functions…\n </div>\n </if>\n\n <if :="items.length === 0">\n <div style="padding:10px; border:1px dashed var(--okdb-app-border); border-radius:8px; color:var(--okdb-app-fg-muted); font-size:12px; line-height:1.5;">\n No functions in this scope yet.\n </div>\n </if>\n\n <each :of="items" let:item="fn">\n <button\n class="functions-list__item"\n :class="fn.key === selectedName ? \'functions-list__item--selected\' : \'\'"\n @click="pick(fn.key)">\n <span class="functions-list__name">{{ fn.key }}</span> <if :="fn.value?.unsafe"><span style="font-size:10px; color:var(--okdb-orange); flex-shrink:0;" title="Unsafe function">&#9888;</span></if> <span class="functions-list__meta">v{{ fn.value?.version ?? \'—\' }}</span>\n </button>\n </each>\n </div>\n </div>\n '};
@@ -1 +1 @@
1
- <script type="module">import licensePanel from"./parts/license-panel.ok.js";export default{tag:"license-index",unwrap:!0,dependencies:[licensePanel]}</script><template><component import="../../layouts/_default.ok.html"><template name="header"></template><template name="default"><license-panel/></template></component></template>
1
+ <script type="module">import e from"./parts/license-panel.ok.js";export default{tag:"license-index",unwrap:!0,dependencies:[e]};</script><template><component import="../../layouts/_default.ok.html"><template name="header"></template><template name="default"><license-panel/></template></component></template>
@@ -1 +1 @@
1
- const _0x40278c=_0x3405;function _0x3405(_0x3900ca,_0x4e2825){_0x3900ca=_0x3900ca-0x19a;const _0x302332=_0x3023();let _0x34059f=_0x302332[_0x3900ca];return _0x34059f;}(function(_0x1211ba,_0x3d7b49){const _0x460011=_0x3405,_0x273419=_0x1211ba();while(!![]){try{const _0x42eeae=parseInt(_0x460011(0x1a4))/0x1*(parseInt(_0x460011(0x1bc))/0x2)+-parseInt(_0x460011(0x1de))/0x3+-parseInt(_0x460011(0x1b7))/0x4*(parseInt(_0x460011(0x1e0))/0x5)+parseInt(_0x460011(0x1d9))/0x6+parseInt(_0x460011(0x1d3))/0x7*(parseInt(_0x460011(0x1c7))/0x8)+parseInt(_0x460011(0x1cc))/0x9*(parseInt(_0x460011(0x1b9))/0xa)+parseInt(_0x460011(0x1df))/0xb*(-parseInt(_0x460011(0x1a7))/0xc);if(_0x42eeae===_0x3d7b49)break;else _0x273419['push'](_0x273419['shift']());}catch(_0x3322fd){_0x273419['push'](_0x273419['shift']());}}}(_0x3023,0x56a12));const t=()=>__ENV__[_0x40278c(0x1a8)][_0x40278c(0x1ae)](/\/api$/,'')+'/license';function _0x3023(){const _0xffabcc=['shared','map','clipboard','min','licenses','success','570444XZjTmI','data','license-panel{display:block;padding:24px;max-width:860px}.lp-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:20px}.lp-title{font-size:18px;font-weight:600}.lp-status-banner{padding:12px\x2016px;border-radius:10px;border:1px\x20solid;font-size:13px;margin-bottom:20px;display:flex;align-items:center;gap:10px}.lp-status-banner--ok{background:var(--okdb-success-soft-bg,\x20rgba(72,187,120,.08));border-color:var(--okdb-success-soft-border,\x20rgba(72,187,120,.2));color:var(--okdb-success,\x20#48bb78)}.lp-status-banner--warn{background:var(--okdb-danger-soft-bg,\x20rgba(245,101,101,.08));border-color:var(--okdb-danger-soft-border,\x20rgba(245,101,101,.2));color:var(--okdb-danger,\x20#f56565)}.lp-pin-box{background:var(--okdb-app-surface-2,\x20rgba(255,255,255,.04));border:1px\x20solid\x20var(--okdb-app-border);border-radius:8px;padding:12px\x2016px;margin-bottom:20px;font-size:12px}.lp-pin-label{opacity:.5;text-transform:uppercase;letter-spacing:.06em;font-size:10px;margin-bottom:4px}.lp-pin-value{font-family:monospace;font-size:15px;font-weight:600;letter-spacing:.08em}.lp-lic-cells,.lp-lic-header{display:grid;grid-template-columns:90px\x201fr\x2090px\x2080px\x2080px\x20auto;gap:0\x2010px;align-items:center;padding:6px\x2010px}.lp-lic-header{font-size:10px;text-transform:uppercase;letter-spacing:.06em;opacity:.45}.lp-lic-header,.lp-lic-row{border-bottom:1px\x20solid\x20var(--okdb-app-border)}.lp-lic-row:last-child{border-bottom:none}.lp-add-form{background:var(--okdb-app-surface-2,\x20rgba(255,255,255,.03));border:1px\x20solid\x20var(--okdb-app-border);border-radius:10px;padding:16px;margin-bottom:20px;display:flex;flex-direction:column;gap:12px}.lp-form-label{font-size:10px;opacity:.5;text-transform:uppercase;letter-spacing:.06em;margin-bottom:4px}.lp-activate-row,.lp-btn{border:1px\x20solid\x20var(--okdb-app-border)}.lp-activate-row{background:var(--okdb-app-surface-2,\x20rgba(255,255,255,.03));border-radius:8px;padding:10px\x2012px;display:flex;flex-direction:column;gap:8px;margin-top:4px}.lp-btn{padding:5px\x2012px;border-radius:6px;background:0\x200;color:var(--okdb-app-fg-muted);font-size:12px;font-weight:500;cursor:pointer;transition:background\x20.1s,color\x20.1s}.lp-btn:hover{background:var(--okdb-app-surface-2);color:var(--okdb-app-fg-strong)}.lp-btn--primary{background:var(--okdb-sky-soft-bg,\x20rgba(99,179,237,.1));border-color:var(--okdb-sky-soft-border,\x20rgba(99,179,237,.25));color:var(--okdb-sky,\x20#63b3ed)}.lp-btn--danger{background:var(--okdb-danger-soft-bg,\x20rgba(245,101,101,.08));border-color:var(--okdb-danger-soft-border,\x20rgba(245,101,101,.2));color:var(--okdb-danger,\x20#f56565)}','entries','POST','loading','1759416vHqvfv','writeText','Remove\x20failed','License\x20removed','var(--okdb-warn,#edc94b)','165078HjjocU','11rFRfFm','72095SHpWSD','addLoading','toISOString','$ok','toLocaleString','DELETE','error','<div><if\x20:=\x22loading\x22><div\x20style=\x22font-size:13px;\x20opacity:.5;\x20padding:20px\x200;\x22>Loading…</div></if><if\x20:=\x22!loading\x20&&\x20unavailable\x22><div\x20style=\x22font-size:13px;\x20opacity:.6;\x20padding:20px\x200;\x22>License\x20system\x20not\x20available\x20in\x20this\x20build.</div></if><if\x20:=\x22!loading\x20&&\x20!unavailable\x22><div\x20class=\x22lp-header\x22><div\x20class=\x22lp-title\x22>License</div><button\x20class=\x22lp-btn\x20lp-btn--primary\x22\x20@click=\x22openAddForm()\x22>+\x20Add\x20License</button></div><if\x20:=\x22hasActive()\x22><div\x20class=\x22lp-status-banner\x20lp-status-banner--ok\x22><span>&#10003;\x20Licensed</span>\x20<span>—</span>\x20<span\x20style=\x22font-weight:600;\x22>{{\x20activeLicensee()\x20}}</span><if\x20:=\x22activeExpiry()\x22><span\x20style=\x22opacity:.7;\x22>·\x20Expires\x20{{\x20formatDate(activeExpiry())\x20}}</span></if></div></if><if\x20:=\x22!hasActive()\x22><div\x20class=\x22lp-status-banner\x20lp-status-banner--warn\x22><span>&#9888;\x20Free\x20tier</span><if\x20:=\x22status\x20&&\x20status.effective\x22><span\x20style=\x22opacity:.7;\x20font-size:12px;\x22>—\x20envs={{\x20status.effective.limits.envs\x20}},\x20types/env={{\x20status.effective.limits.typesPerEnv\x20}},\x20writes={{\x20(status.effective.limits.maxWrites||0).toLocaleString()\x20}}</span></if></div><if\x20:=\x22status\x20&&\x20status.isLicensedBuild\x20&&\x20pinDisplay()\x20!==\x20\x27—\x27\x22><div\x20class=\x22lp-pin-box\x22><div\x20class=\x22lp-pin-label\x22>Node\x20PIN\x20—\x20share\x20this\x20with\x20your\x20license\x20provider</div><div\x20style=\x22display:flex;\x20align-items:center;\x20gap:10px;\x22><div\x20class=\x22lp-pin-value\x22>{{\x20pinDisplay()\x20}}</div><button\x20class=\x22lp-btn\x22\x20style=\x22font-size:11px;\x20padding:3px\x208px;\x22\x20@click=\x22copyPin()\x22>Copy</button></div><div\x20style=\x22font-size:11px;\x20opacity:.55;\x20margin-top:6px;\x22>How\x20activation\x20works:\x20paste\x20your\x20<strong>license\x20blob</strong>\x20below\x20→\x20vendor\x20signs\x20an\x20activation\x20token\x20bound\x20to\x20this\x20PIN\x20→\x20paste\x20the\x20activation\x20token\x20to\x20enable\x20the\x20license.</div></div></if><if\x20:=\x22status\x20&&\x20status.isLicensedBuild\x20&&\x20status.buildFingerprint\x22><div\x20class=\x22lp-pin-box\x22\x20style=\x22margin-top:-12px;\x22><div\x20class=\x22lp-pin-label\x22>Build\x20signing-key\x20fingerprint</div><div\x20style=\x22font-family:monospace;\x20font-size:12px;\x22>{{\x20status.buildFingerprint\x20}}</div><div\x20style=\x22font-size:11px;\x20opacity:.55;\x20margin-top:6px;\x22>Your\x20license\x20must\x20be\x20signed\x20by\x20the\x20private\x20key\x20paired\x20with\x20this\x20fingerprint.\x20If\x20you\x20see\x20<em>\x22License\x20signature\x20invalid\x22</em>,\x20the\x20license\x20was\x20signed\x20by\x20a\x20different\x20keypair\x20than\x20the\x20one\x20this\x20build\x20uses.</div></div></if><if\x20:=\x22status\x20&&\x20!status.isLicensedBuild\x22><div\x20class=\x22lp-pin-box\x22\x20style=\x22opacity:.6\x22><div\x20class=\x22lp-pin-label\x22>Development\x20build\x20—\x20license\x20enforcement\x20disabled</div><div\x20style=\x22font-size:12px;\x20margin-top:4px;\x22>Set\x20<code>OKDB_LICENSED_BUILD=true</code>\x20in\x20<code>.env</code>\x20to\x20enable\x20enforcement\x20in\x20dev,\x20or\x20run\x20<code>npm\x20run\x20build:prod</code>\x20for\x20a\x20production\x20build.</div></div></if></if><if\x20:=\x22status\x20&&\x20status.usage\x20&&\x20status.effective\x22><div\x20style=\x22display:flex;\x20flex-direction:column;\x20gap:10px;\x20margin-bottom:20px;\x22><if\x20:=\x22status.effective.limits.maxWrites\x22><div><div\x20style=\x22display:flex;\x20justify-content:space-between;\x20font-size:11px;\x20opacity:.7;\x20margin-bottom:4px;\x22><span>Writes</span>\x20<span\x20style=\x22font-family:monospace;\x22>{{\x20formatNum(status.usage.writes)\x20}}\x20/\x20{{\x20formatNum(status.effective.limits.maxWrites)\x20}}</span></div><div\x20style=\x22height:6px;\x20background:var(--okdb-app-surface-2,rgba(255,255,255,.06));\x20border-radius:3px;\x20overflow:hidden;\x22><div\x20:style=\x22\x27height:100%;width:\x27\x20+\x20usagePct(status.usage.writes,\x20status.effective.limits.maxWrites)\x20+\x20\x27%;background:\x27\x20+\x20usageBarColor(usagePct(status.usage.writes,\x20status.effective.limits.maxWrites))\x20+\x20\x27;transition:width\x20.2s;\x27\x22/></div></div></if><if\x20:=\x22status.effective.limits.envs\x22><div><div\x20style=\x22display:flex;\x20justify-content:space-between;\x20font-size:11px;\x20opacity:.7;\x20margin-bottom:4px;\x22><span>Environments</span>\x20<span\x20style=\x22font-family:monospace;\x22>{{\x20status.usage.envs\x20}}\x20/\x20{{\x20status.effective.limits.envs\x20}}</span></div><div\x20style=\x22height:6px;\x20background:var(--okdb-app-surface-2,rgba(255,255,255,.06));\x20border-radius:3px;\x20overflow:hidden;\x22><div\x20:style=\x22\x27height:100%;width:\x27\x20+\x20usagePct(status.usage.envs,\x20status.effective.limits.envs)\x20+\x20\x27%;background:\x27\x20+\x20usageBarColor(usagePct(status.usage.envs,\x20status.effective.limits.envs))\x20+\x20\x27;transition:width\x20.2s;\x27\x22/></div></div></if></div></if><if\x20:=\x22showAddForm\x22><div\x20class=\x22lp-add-form\x22><div\x20style=\x22font-size:13px;\x20font-weight:600;\x22>Add\x20License\x20or\x20Activation\x20Token</div><div\x20style=\x22font-size:11px;\x20opacity:.6;\x22>Paste\x20either\x20a\x20<strong>license\x20blob</strong>\x20(issued\x20by\x20your\x20vendor)\x20or\x20an\x20<strong>activation\x20token</strong>\x20(vendor\x27s\x20response\x20to\x20your\x20PIN).\x20The\x20server\x20detects\x20which\x20it\x20is.</div><div><textarea\x20:value=\x22addInput\x22\x20@input=\x22addInput\x20=\x20$event.target.value\x22\x20rows=\x226\x22\x20placeholder=\x22Paste\x20license\x20blob\x20or\x20activation\x20token\x20here…\x22\x20style=\x22width:100%;\x20font-family:monospace;\x20font-size:11px;\x20resize:vertical;\x22/></div><if\x20:=\x22addError\x22><div\x20style=\x22font-size:12px;\x20color:var(--okdb-danger);\x20background:var(--okdb-danger-soft-bg,rgba(245,101,101,.08));\x20border:1px\x20solid\x20var(--okdb-danger-soft-border,rgba(245,101,101,.2));\x20border-radius:6px;\x20padding:8px\x2012px;\x22>{{\x20addError\x20}}</div></if><div\x20style=\x22display:flex;\x20gap:8px;\x20justify-content:flex-end;\x22><button\x20class=\x22lp-btn\x22\x20@click=\x22cancelAddForm()\x22>Cancel</button>\x20<button\x20class=\x22lp-btn\x20lp-btn--primary\x22\x20@click=\x22submitAdd()\x22\x20:disabled=\x22!addInput.trim()\x20||\x20addLoading\x22>{{\x20addLoading\x20?\x20\x27Submitting…\x27\x20:\x20\x27Submit\x27\x20}}</button></div></div></if><if\x20:=\x22status\x20&&\x20status.effective\x22><div\x20style=\x22margin-top:20px;\x20font-size:10px;\x20text-transform:uppercase;\x20letter-spacing:.06em;\x20opacity:.45;\x20margin-bottom:8px;\x22>Current\x20capabilities</div><table\x20class=\x22lp-table\x22\x20style=\x22margin-bottom:16px;\x22><thead><tr><th>Feature</th><th>Status</th><th>Limit</th></tr></thead><tbody><tr><td>Sync</td><td><span\x20:style=\x22status.effective.features.sync\x20?\x20\x27color:var(--okdb-success,#38a169)\x27\x20:\x20\x27opacity:.45\x27\x22>{{\x20status.effective.features.sync\x20?\x20\x27✓\x20enabled\x27\x20:\x20\x27✗\x20disabled\x27\x20}}</span></td><td\x20style=\x22font-family:monospace;\x22>{{\x20status.effective.limits.syncPeers\x20?\x20\x27max\x20\x27\x20+\x20status.effective.limits.syncPeers\x20+\x20\x27\x20peers\x27\x20:\x20(status.effective.features.sync\x20?\x20\x27unlimited\x27\x20:\x20\x27—\x27)\x20}}</td></tr><tr><td>Embeddings</td><td><span\x20:style=\x22status.effective.features.embeddings\x20?\x20\x27color:var(--okdb-success,#38a169)\x27\x20:\x20\x27opacity:.45\x27\x22>{{\x20status.effective.features.embeddings\x20?\x20\x27✓\x20enabled\x27\x20:\x20\x27✗\x20disabled\x27\x20}}</span></td><td>—</td></tr><tr><td>Engines</td><td><span\x20:style=\x22status.effective.features.engines\x20?\x20\x27color:var(--okdb-success,#38a169)\x27\x20:\x20\x27opacity:.45\x27\x22>{{\x20status.effective.features.engines\x20?\x20\x27✓\x20enabled\x27\x20:\x20\x27✗\x20disabled\x27\x20}}</span></td><td>—</td></tr><tr><td>Full-text\x20search</td><td><span\x20:style=\x22status.effective.features.fts\x20?\x20\x27color:var(--okdb-success,#38a169)\x27\x20:\x20\x27opacity:.45\x27\x22>{{\x20status.effective.features.fts\x20?\x20\x27✓\x20enabled\x27\x20:\x20\x27✗\x20disabled\x27\x20}}</span></td><td>—</td></tr><tr><td>Time\x20machine</td><td><span\x20:style=\x22status.effective.features.timeMachine\x20?\x20\x27color:var(--okdb-success,#38a169)\x27\x20:\x20\x27opacity:.45\x27\x22>{{\x20status.effective.features.timeMachine\x20?\x20\x27✓\x20enabled\x27\x20:\x20\x27✗\x20disabled\x27\x20}}</span></td><td>—</td></tr><tr><td>Views</td><td><span\x20:style=\x22status.effective.features.views\x20?\x20\x27color:var(--okdb-success,#38a169)\x27\x20:\x20\x27opacity:.45\x27\x22>{{\x20status.effective.features.views\x20?\x20\x27✓\x20enabled\x27\x20:\x20\x27✗\x20disabled\x27\x20}}</span></td><td>—</td></tr><tr><td>MCP</td><td><span\x20:style=\x22status.effective.features.mcp\x20?\x20\x27color:var(--okdb-success,#38a169)\x27\x20:\x20\x27opacity:.45\x27\x22>{{\x20status.effective.features.mcp\x20?\x20\x27✓\x20enabled\x27\x20:\x20\x27✗\x20disabled\x27\x20}}</span></td><td>—</td></tr><tr><td>Files</td><td><span\x20:style=\x22status.effective.features.files\x20?\x20\x27color:var(--okdb-success,#38a169)\x27\x20:\x20\x27opacity:.45\x27\x22>{{\x20status.effective.features.files\x20?\x20\x27✓\x20enabled\x27\x20:\x20\x27✗\x20disabled\x27\x20}}</span></td><td>—</td></tr><tr><td>Environments</td><td><span\x20style=\x22color:var(--okdb-success,#38a169)\x22>✓\x20enabled</span></td><td\x20style=\x22font-family:monospace;\x22>{{\x20status.effective.limits.envs\x20?\x20\x27max\x20\x27\x20+\x20status.effective.limits.envs\x20:\x20\x27unlimited\x27\x20}}</td></tr><tr><td>Types\x20per\x20env</td><td><span\x20style=\x22color:var(--okdb-success,#38a169)\x22>✓\x20enabled</span></td><td\x20style=\x22font-family:monospace;\x22>{{\x20status.effective.limits.typesPerEnv\x20?\x20\x27max\x20\x27\x20+\x20status.effective.limits.typesPerEnv\x20:\x20\x27unlimited\x27\x20}}</td></tr><tr><td>Total\x20writes</td><td><span\x20style=\x22color:var(--okdb-success,#38a169)\x22>✓\x20enabled</span></td><td\x20style=\x22font-family:monospace;\x22>{{\x20status.effective.limits.maxWrites\x20?\x20(status.effective.limits.maxWrites).toLocaleString()\x20+\x20\x27\x20max\x27\x20:\x20\x27unlimited\x27\x20}}</td></tr></tbody></table></if><if\x20:=\x22status\x20&&\x20status.licenses\x20&&\x20status.licenses.length\x20>\x200\x22><div\x20class=\x22lp-lic-header\x22><span>ID</span><span>Licensee</span><span>Expires</span><span>Status</span><span>Features</span><span>Actions</span></div><each\x20:of=\x22status.licenses\x22\x20let:lic><div\x20class=\x22lp-lic-row\x22><div\x20class=\x22lp-lic-cells\x22><span\x20style=\x22font-family:monospace;\x20opacity:.7;\x20font-size:11px;\x22>{{\x20shortId(lic.id)\x20}}</span>\x20<span\x20style=\x22font-size:12px;\x22>{{\x20lic.licensee\x20||\x20\x27—\x27\x20}}</span>\x20<span\x20style=\x22font-family:monospace;\x20font-size:12px;\x22>{{\x20formatDate(lic.expiresAt)\x20}}</span>\x20<span><span\x20:style=\x22badgeStyle(lic.status)\x22>{{\x20lic.status\x20}}</span></span>\x20<span\x20style=\x22opacity:.7;\x20font-size:12px;\x22>{{\x20featureSummary(lic.features)\x20}}</span><div\x20style=\x22display:flex;\x20gap:6px;\x20flex-wrap:wrap;\x20align-items:center;\x22><if\x20:=\x22lic.status\x20===\x20\x27pending\x27\x20||\x20lic.status\x20===\x20\x27wrong_node\x27\x22><button\x20class=\x22lp-btn\x22\x20style=\x22font-size:11px;\x22\x20@click=\x22openActivate(lic.id)\x22>Activate</button></if><button\x20class=\x22lp-btn\x20lp-btn--danger\x22\x20style=\x22font-size:11px;\x22\x20@click=\x22removeLicense(lic.id)\x22>Remove</button></div></div><if\x20:=\x22activatingId\x20===\x20lic.id\x22><div\x20class=\x22lp-activate-row\x22><div\x20style=\x22font-size:11px;\x20opacity:.7;\x22>PIN\x20for\x20this\x20license:\x20<span\x20style=\x22font-family:monospace;\x20font-weight:600;\x22>{{\x20licensePin(lic)\x20}}</span></div><textarea\x20:value=\x22activateToken\x22\x20@input=\x22activateToken\x20=\x20$event.target.value\x22\x20rows=\x223\x22\x20placeholder=\x22Paste\x20activation\x20token…\x22\x20style=\x22width:100%;\x20font-family:monospace;\x20font-size:11px;\x20resize:vertical;\x22/><if\x20:=\x22activateError\x22><div\x20style=\x22font-size:11px;\x20color:var(--okdb-danger);\x22>{{\x20activateError\x20}}</div></if><div\x20style=\x22display:flex;\x20gap:6px;\x20justify-content:flex-end;\x22><button\x20class=\x22lp-btn\x22\x20style=\x22font-size:11px;\x22\x20@click=\x22cancelActivate()\x22>Cancel</button>\x20<button\x20class=\x22lp-btn\x20lp-btn--primary\x22\x20style=\x22font-size:11px;\x22\x20@click=\x22submitActivate(lic.id)\x22\x20:disabled=\x22!activateToken.trim()\x20||\x20activateLoading\x22>{{\x20activateLoading\x20?\x20\x27Activating…\x27\x20:\x20\x27Activate\x27\x20}}</button></div></div></if></div></each></if><if\x20:=\x22error\x22><div\x20style=\x22font-size:13px;\x20color:var(--okdb-danger);\x20padding:12px\x200;\x22>{{\x20error\x20}}</div></if></if></div>','active','unavailable','licenseId','Remove\x20this\x20license?','showAddForm','then','status','activateLoading','background:rgba(237,201,75,.1);border-color:rgba(237,201,75,.3);color:#edc94b;','4yLFghs','none','addError','7764168rOjgeC','ADMIN_API','Activation\x20failed','string','toast','var(--okdb-danger,#f56565)','result','replace','slice','license-panel','round','Failed\x20to\x20load\x20license\x20status','background:var(--okdb-success-soft-bg,rgba(72,187,120,.12));border-color:var(--okdb-success-soft-border,rgba(72,187,120,.25));color:var(--okdb-success,#48bb78);','length','load','activateToken','52zKjnaQ','message','683330MVRDiN','pin','catch','319138zUcPEr','trim','json','isArray','filter','activatingId','find','stringify','join','background:var(--okdb-danger-soft-bg,rgba(245,101,101,.1));border-color:var(--okdb-danger-soft-border,rgba(245,101,101,.25));color:var(--okdb-danger,#f56565);','addInput','24zztQxi','licensee','activateError','Failed\x20to\x20add\x20license','Copy\x20failed','9XWIoaa'];_0x3023=function(){return _0xffabcc;};return _0x3023();}export default{'tag':_0x40278c(0x1b0),'context':_0x5d3e76=>({'status':null,'loading':!0x0,'error':null,'unavailable':!0x1,'showAddForm':!0x1,'addInput':'','addLoading':!0x1,'addError':null,'activatingId':null,'activateToken':'','activateLoading':!0x1,'activateError':null,async 'load'(){const _0x256663=_0x40278c;this['loading']=!0x0,this[_0x256663(0x1e6)]=null,this[_0x256663(0x19c)]=!0x1;try{const _0x26478a=await fetch(t()+'/status');if(!_0x26478a['ok']){const _0x14c412=await _0x26478a['json']()[_0x256663(0x1bb)](()=>({}));throw new Error(_0x14c412?.[_0x256663(0x1e6)]?.['message']||_0x14c412?.[_0x256663(0x1e6)]||_0x256663(0x1b2));}const _0x53602a=await _0x26478a[_0x256663(0x1be)]();this[_0x256663(0x1a1)]=_0x53602a[_0x256663(0x1d4)]??_0x53602a['result']??_0x53602a;}catch(_0x3baa84){this[_0x256663(0x1e6)]=_0x3baa84?.['message']||String(_0x3baa84);}finally{this[_0x256663(0x1d8)]=!0x1;}},'openAddForm'(){const _0x29d0cd=_0x40278c;this['showAddForm']=!0x0,this[_0x29d0cd(0x1c6)]='',this['addError']=null;},'cancelAddForm'(){const _0x5a8699=_0x40278c;this['showAddForm']=!0x1,this[_0x5a8699(0x1a6)]=null;},async 'submitAdd'(){const _0x5e08ce=_0x40278c;if(this[_0x5e08ce(0x1c6)]['trim']()&&!this['addLoading']){this[_0x5e08ce(0x1e1)]=!0x0,this[_0x5e08ce(0x1a6)]=null;try{const _0x505639=await fetch(''+t(),{'method':_0x5e08ce(0x1d7),'headers':{'Content-Type':'application/json'},'body':JSON[_0x5e08ce(0x1c3)]({'input':this['addInput'][_0x5e08ce(0x1bd)]()})}),_0x493d52=await _0x505639[_0x5e08ce(0x1be)]();if(!_0x505639['ok']){const _0x251f05=_0x493d52?.['error']?.[_0x5e08ce(0x1b8)]||_0x493d52?.[_0x5e08ce(0x1e6)]||_0x493d52?.[_0x5e08ce(0x1d4)]?.[_0x5e08ce(0x1e6)]||_0x493d52?.[_0x5e08ce(0x1ad)]?.[_0x5e08ce(0x1e6)]||_0x5e08ce(0x1ca);throw new Error('string'==typeof _0x251f05?_0x251f05:JSON[_0x5e08ce(0x1c3)](_0x251f05));}this[_0x5e08ce(0x19f)]=!0x1,await this[_0x5e08ce(0x1b5)]();}catch(_0x4d1b20){this[_0x5e08ce(0x1a6)]=_0x4d1b20?.['message']||String(_0x4d1b20);}finally{this[_0x5e08ce(0x1e1)]=!0x1;}}},'copyPin'(){const _0x30ed9c=_0x40278c,_0x4013bf=this['pinDisplay']();_0x4013bf&&'—'!==_0x4013bf&&navigator[_0x30ed9c(0x1cf)]?.[_0x30ed9c(0x1da)]&&navigator[_0x30ed9c(0x1cf)]['writeText'](_0x4013bf)[_0x30ed9c(0x1a0)](()=>_0x5d3e76['$ok']['shared']?.['toast']?.['success']?.('PIN\x20copied'),()=>_0x5d3e76['$ok'][_0x30ed9c(0x1cd)]?.[_0x30ed9c(0x1ab)]?.[_0x30ed9c(0x1e6)]?.(_0x30ed9c(0x1cb)));},'usagePct':(_0x261012,_0x284ddd)=>_0x284ddd&&0x0!==_0x284ddd?Math[_0x40278c(0x1d0)](0x64,Math[_0x40278c(0x1b1)](_0x261012/_0x284ddd*0x64)):0x0,'usageBarColor':_0x40d3c6=>_0x40d3c6>=0x5a?_0x40278c(0x1ac):_0x40d3c6>=0x4b?_0x40278c(0x1dd):'var(--okdb-success,#48bb78)','formatNum':_0x5a9207=>null==_0x5a9207?'—':Number(_0x5a9207)[_0x40278c(0x1e4)](),'openActivate'(_0x3cfa40){const _0x24a4f3=_0x40278c;this[_0x24a4f3(0x1c1)]=_0x3cfa40,this[_0x24a4f3(0x1b6)]='',this[_0x24a4f3(0x1c9)]=null;},'cancelActivate'(){const _0x1ce05b=_0x40278c;this[_0x1ce05b(0x1c1)]=null,this[_0x1ce05b(0x1c9)]=null;},async 'submitActivate'(_0x286a9d){const _0x1d6f20=_0x40278c;if(this[_0x1d6f20(0x1b6)]['trim']()&&!this[_0x1d6f20(0x1a2)]){this[_0x1d6f20(0x1a2)]=!0x0,this[_0x1d6f20(0x1c9)]=null;try{const _0x141367=await fetch(t()+'/'+encodeURIComponent(_0x286a9d)+'/activate',{'method':_0x1d6f20(0x1d7),'headers':{'Content-Type':'application/json'},'body':JSON[_0x1d6f20(0x1c3)]({'activation':this[_0x1d6f20(0x1b6)][_0x1d6f20(0x1bd)]()})}),_0x5852a5=await _0x141367[_0x1d6f20(0x1be)]();if(!_0x141367['ok']){const _0x5bbba2=_0x5852a5?.[_0x1d6f20(0x1e6)]?.[_0x1d6f20(0x1b8)]||_0x5852a5?.[_0x1d6f20(0x1e6)]||_0x5852a5?.[_0x1d6f20(0x1d4)]?.[_0x1d6f20(0x1e6)]||_0x5852a5?.[_0x1d6f20(0x1ad)]?.[_0x1d6f20(0x1e6)]||_0x1d6f20(0x1a9);throw new Error(_0x1d6f20(0x1aa)==typeof _0x5bbba2?_0x5bbba2:JSON[_0x1d6f20(0x1c3)](_0x5bbba2));}this[_0x1d6f20(0x1c1)]=null,await this['load']();}catch(_0x58691b){this[_0x1d6f20(0x1c9)]=_0x58691b?.[_0x1d6f20(0x1b8)]||String(_0x58691b);}finally{this[_0x1d6f20(0x1a2)]=!0x1;}}},async 'removeLicense'(_0x5d1c3b){const _0x23963e=_0x40278c;if(confirm(_0x23963e(0x19e)))try{const _0x323c8f=await fetch(t()+'/'+encodeURIComponent(_0x5d1c3b),{'method':_0x23963e(0x1e5)}),_0x5039f4=await _0x323c8f[_0x23963e(0x1be)]();if(!_0x323c8f['ok']){const _0x44c42f=_0x5039f4?.[_0x23963e(0x1e6)]?.[_0x23963e(0x1b8)]||_0x5039f4?.['error']||_0x5039f4?.['data']?.[_0x23963e(0x1e6)]||_0x5039f4?.[_0x23963e(0x1ad)]?.[_0x23963e(0x1e6)]||'Remove\x20failed';throw new Error('string'==typeof _0x44c42f?_0x44c42f:JSON[_0x23963e(0x1c3)](_0x44c42f));}_0x5d3e76[_0x23963e(0x1e3)][_0x23963e(0x1cd)]?.['toast']?.[_0x23963e(0x1d2)]?.(_0x23963e(0x1dc)),await this[_0x23963e(0x1b5)]();}catch(_0x119561){_0x5d3e76[_0x23963e(0x1e3)][_0x23963e(0x1cd)]?.[_0x23963e(0x1ab)]?.[_0x23963e(0x1e6)]?.(_0x119561?.[_0x23963e(0x1b8)]||_0x23963e(0x1db));}},'formatDate':_0x5bc146=>_0x5bc146?new Date(_0x5bc146)[_0x40278c(0x1e2)]()[_0x40278c(0x1af)](0x0,0xa):'—','shortId':_0x3ecf69=>_0x3ecf69?_0x3ecf69[_0x40278c(0x1af)](0x0,0x8)+'…':'—','featureSummary'(_0x7cc10){const _0x1a4b2a=_0x40278c;if(!_0x7cc10)return'—';const _0x26479c=Object[_0x1a4b2a(0x1d6)](_0x7cc10)[_0x1a4b2a(0x1c0)](([,_0x30f2f9])=>_0x30f2f9)[_0x1a4b2a(0x1ce)](([_0x59b0ba])=>_0x59b0ba);return _0x26479c[_0x1a4b2a(0x1b4)]?_0x26479c[0x0]+(_0x26479c['length']>0x1?'\x20+'+(_0x26479c[_0x1a4b2a(0x1b4)]-0x1):''):_0x1a4b2a(0x1a5);},'pinDisplay'(){const _0x2e8327=_0x40278c,_0x9dceb8=this[_0x2e8327(0x1a1)]?.[_0x2e8327(0x1ba)];return _0x9dceb8?Array[_0x2e8327(0x1bf)](_0x9dceb8)?0x0===_0x9dceb8[_0x2e8327(0x1b4)]?'—':_0x9dceb8[_0x2e8327(0x1ce)](_0x1c9227=>_0x2e8327(0x1aa)==typeof _0x1c9227?_0x1c9227:_0x1c9227?.[_0x2e8327(0x1ba)])['filter'](Boolean)[_0x2e8327(0x1c4)](',\x20')||'—':_0x9dceb8:'—';},'hasActive'(){return(this['status']?.['activeCount']??0x0)>0x0;},'activeLicensee'(){const _0x32c850=_0x40278c;return this['status']?.[_0x32c850(0x1d1)]?.[_0x32c850(0x1c2)](_0x25495f=>_0x32c850(0x19b)===_0x25495f[_0x32c850(0x1a1)])?.[_0x32c850(0x1c8)]??null;},'activeExpiry'(){const _0x544694=_0x40278c;return this[_0x544694(0x1a1)]?.[_0x544694(0x1d1)]?.[_0x544694(0x1c2)](_0x27a48f=>_0x544694(0x19b)===_0x27a48f[_0x544694(0x1a1)])?.['expiresAt']??null;},'badgeStyle':_0x44b2cc=>({'active':_0x40278c(0x1b3),'expired':_0x40278c(0x1c5),'invalid':_0x40278c(0x1c5),'pending':_0x40278c(0x1a3),'wrong_node':'background:rgba(237,137,54,.1);border-color:rgba(237,137,54,.3);color:#ed8936;'}[_0x44b2cc]||'')+'font-size:10px;padding:2px\x207px;border-radius:4px;border:1px\x20solid;font-weight:500;text-transform:uppercase;letter-spacing:.04em;','licensePin'(_0x177792){const _0x2efbe4=_0x40278c,_0x28a9cb=this['status']?.['pin'];if(Array[_0x2efbe4(0x1bf)](_0x28a9cb)){const _0x10735f=_0x28a9cb[_0x2efbe4(0x1c2)](_0x1932a8=>_0x1932a8?.[_0x2efbe4(0x19d)]===_0x177792?.['id']);if(_0x10735f?.[_0x2efbe4(0x1ba)])return _0x10735f[_0x2efbe4(0x1ba)];}return'string'==typeof _0x177792?.[_0x2efbe4(0x1ba)]?_0x177792[_0x2efbe4(0x1ba)]:'—';}}),'mount'(){const _0x20a5b3=_0x40278c;this[_0x20a5b3(0x1b5)]();},'style':_0x40278c(0x1d5),'template':_0x40278c(0x19a)};
1
+ const t=()=>__ENV__.ADMIN_API.replace(/\/api$/,"")+"/license";export default{tag:"license-panel",context:e=>({status:null,loading:!0,error:null,unavailable:!1,showAddForm:!1,addInput:"",addLoading:!1,addError:null,activatingId:null,activateToken:"",activateLoading:!1,activateError:null,async load(){this.loading=!0,this.error=null,this.unavailable=!1;try{const e=await fetch(`${t()}/status`);if(!e.ok){const t=await e.json().catch(()=>({}));throw new Error(t?.error?.message||t?.error||"Failed to load license status")}const s=await e.json();this.status=s.data??s.result??s}catch(t){this.error=t?.message||String(t)}finally{this.loading=!1}},openAddForm(){this.showAddForm=!0,this.addInput="",this.addError=null},cancelAddForm(){this.showAddForm=!1,this.addError=null},async submitAdd(){if(this.addInput.trim()&&!this.addLoading){this.addLoading=!0,this.addError=null;try{const e=await fetch(`${t()}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({input:this.addInput.trim()})}),s=await e.json();if(!e.ok){const t=s?.error?.message||s?.error||s?.data?.error||s?.result?.error||"Failed to add license";throw new Error("string"==typeof t?t:JSON.stringify(t))}this.showAddForm=!1,await this.load()}catch(t){this.addError=t?.message||String(t)}finally{this.addLoading=!1}}},copyPin(){const t=this.pinDisplay();t&&"—"!==t&&navigator.clipboard?.writeText&&navigator.clipboard.writeText(t).then(()=>e.$ok.shared?.toast?.success?.("PIN copied"),()=>e.$ok.shared?.toast?.error?.("Copy failed"))},usagePct:(t,e)=>e&&0!==e?Math.min(100,Math.round(t/e*100)):0,usageBarColor:t=>t>=90?"var(--okdb-danger,#f56565)":t>=75?"var(--okdb-warn,#edc94b)":"var(--okdb-success,#48bb78)",formatNum:t=>null==t?"—":Number(t).toLocaleString(),openActivate(t){this.activatingId=t,this.activateToken="",this.activateError=null},cancelActivate(){this.activatingId=null,this.activateError=null},async submitActivate(e){if(this.activateToken.trim()&&!this.activateLoading){this.activateLoading=!0,this.activateError=null;try{const s=await fetch(`${t()}/${encodeURIComponent(e)}/activate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({activation:this.activateToken.trim()})}),a=await s.json();if(!s.ok){const t=a?.error?.message||a?.error||a?.data?.error||a?.result?.error||"Activation failed";throw new Error("string"==typeof t?t:JSON.stringify(t))}this.activatingId=null,await this.load()}catch(t){this.activateError=t?.message||String(t)}finally{this.activateLoading=!1}}},async removeLicense(s){if(confirm("Remove this license?"))try{const a=await fetch(`${t()}/${encodeURIComponent(s)}`,{method:"DELETE"}),i=await a.json();if(!a.ok){const t=i?.error?.message||i?.error||i?.data?.error||i?.result?.error||"Remove failed";throw new Error("string"==typeof t?t:JSON.stringify(t))}e.$ok.shared?.toast?.success?.("License removed"),await this.load()}catch(t){e.$ok.shared?.toast?.error?.(t?.message||"Remove failed")}},formatDate:t=>t?new Date(t).toISOString().slice(0,10):"—",shortId:t=>t?t.slice(0,8)+"…":"—",featureSummary(t){if(!t)return"—";const e=Object.entries(t).filter(([,t])=>t).map(([t])=>t);return e.length?e[0]+(e.length>1?" +"+(e.length-1):""):"none"},pinDisplay(){const t=this.status?.pin;return t?Array.isArray(t)?0===t.length?"—":t.map(t=>"string"==typeof t?t:t?.pin).filter(Boolean).join(", ")||"—":t:"—"},hasActive(){return(this.status?.activeCount??0)>0},activeLicensee(){return this.status?.licenses?.find(t=>"active"===t.status)?.licensee??null},activeExpiry(){return this.status?.licenses?.find(t=>"active"===t.status)?.expiresAt??null},badgeStyle:t=>(({active:"background:var(--okdb-success-soft-bg,rgba(72,187,120,.12));border-color:var(--okdb-success-soft-border,rgba(72,187,120,.25));color:var(--okdb-success,#48bb78);",expired:"background:var(--okdb-danger-soft-bg,rgba(245,101,101,.1));border-color:var(--okdb-danger-soft-border,rgba(245,101,101,.25));color:var(--okdb-danger,#f56565);",invalid:"background:var(--okdb-danger-soft-bg,rgba(245,101,101,.1));border-color:var(--okdb-danger-soft-border,rgba(245,101,101,.25));color:var(--okdb-danger,#f56565);",pending:"background:rgba(237,201,75,.1);border-color:rgba(237,201,75,.3);color:#edc94b;",wrong_node:"background:rgba(237,137,54,.1);border-color:rgba(237,137,54,.3);color:#ed8936;"}[t]||"")+"font-size:10px;padding:2px 7px;border-radius:4px;border:1px solid;font-weight:500;text-transform:uppercase;letter-spacing:.04em;"),licensePin(t){const e=this.status?.pin;if(Array.isArray(e)){const s=e.find(e=>e?.licenseId===t?.id);if(s?.pin)return s.pin}return"string"==typeof t?.pin?t.pin:"—"}}),mount(){this.load()},style:"license-panel{display:block;padding:24px;max-width:860px}.lp-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:20px}.lp-title{font-size:18px;font-weight:600}.lp-status-banner{padding:12px 16px;border-radius:10px;border:1px solid;font-size:13px;margin-bottom:20px;display:flex;align-items:center;gap:10px}.lp-status-banner--ok{background:var(--okdb-success-soft-bg, rgba(72,187,120,.08));border-color:var(--okdb-success-soft-border, rgba(72,187,120,.2));color:var(--okdb-success, #48bb78)}.lp-status-banner--warn{background:var(--okdb-danger-soft-bg, rgba(245,101,101,.08));border-color:var(--okdb-danger-soft-border, rgba(245,101,101,.2));color:var(--okdb-danger, #f56565)}.lp-pin-box{background:var(--okdb-app-surface-2, rgba(255,255,255,.04));border:1px solid var(--okdb-app-border);border-radius:8px;padding:12px 16px;margin-bottom:20px;font-size:12px}.lp-pin-label{opacity:.5;text-transform:uppercase;letter-spacing:.06em;font-size:10px;margin-bottom:4px}.lp-pin-value{font-family:monospace;font-size:15px;font-weight:600;letter-spacing:.08em}.lp-lic-cells,.lp-lic-header{display:grid;grid-template-columns:90px 1fr 90px 80px 80px auto;gap:0 10px;align-items:center;padding:6px 10px}.lp-lic-header{font-size:10px;text-transform:uppercase;letter-spacing:.06em;opacity:.45}.lp-lic-header,.lp-lic-row{border-bottom:1px solid var(--okdb-app-border)}.lp-lic-row:last-child{border-bottom:none}.lp-add-form{background:var(--okdb-app-surface-2, rgba(255,255,255,.03));border:1px solid var(--okdb-app-border);border-radius:10px;padding:16px;margin-bottom:20px;display:flex;flex-direction:column;gap:12px}.lp-form-label{font-size:10px;opacity:.5;text-transform:uppercase;letter-spacing:.06em;margin-bottom:4px}.lp-activate-row,.lp-btn{border:1px solid var(--okdb-app-border)}.lp-activate-row{background:var(--okdb-app-surface-2, rgba(255,255,255,.03));border-radius:8px;padding:10px 12px;display:flex;flex-direction:column;gap:8px;margin-top:4px}.lp-btn{padding:5px 12px;border-radius:6px;background:0 0;color:var(--okdb-app-fg-muted);font-size:12px;font-weight:500;cursor:pointer;transition:background .1s,color .1s}.lp-btn:hover{background:var(--okdb-app-surface-2);color:var(--okdb-app-fg-strong)}.lp-btn--primary{background:var(--okdb-sky-soft-bg, rgba(99,179,237,.1));border-color:var(--okdb-sky-soft-border, rgba(99,179,237,.25));color:var(--okdb-sky, #63b3ed)}.lp-btn--danger{background:var(--okdb-danger-soft-bg, rgba(245,101,101,.08));border-color:var(--okdb-danger-soft-border, rgba(245,101,101,.2));color:var(--okdb-danger, #f56565)}",template:'<div><if :="loading"><div style="font-size:13px; opacity:.5; padding:20px 0;">Loading…</div></if><if :="!loading && unavailable"><div style="font-size:13px; opacity:.6; padding:20px 0;">License system not available in this build.</div></if><if :="!loading && !unavailable"><div class="lp-header"><div class="lp-title">License</div><button class="lp-btn lp-btn--primary" @click="openAddForm()">+ Add License</button></div><if :="hasActive()"><div class="lp-status-banner lp-status-banner--ok"><span>&#10003; Licensed</span> <span>—</span> <span style="font-weight:600;">{{ activeLicensee() }}</span><if :="activeExpiry()"><span style="opacity:.7;">· Expires {{ formatDate(activeExpiry()) }}</span></if></div></if><if :="!hasActive()"><div class="lp-status-banner lp-status-banner--warn"><span>&#9888; Free tier</span><if :="status && status.effective"><span style="opacity:.7; font-size:12px;">— envs={{ status.effective.limits.envs }}, types/env={{ status.effective.limits.typesPerEnv }}, writes={{ (status.effective.limits.maxWrites||0).toLocaleString() }}</span></if></div><if :="status && status.isLicensedBuild && pinDisplay() !== \'—\'"><div class="lp-pin-box"><div class="lp-pin-label">Node PIN — share this with your license provider</div><div style="display:flex; align-items:center; gap:10px;"><div class="lp-pin-value">{{ pinDisplay() }}</div><button class="lp-btn" style="font-size:11px; padding:3px 8px;" @click="copyPin()">Copy</button></div><div style="font-size:11px; opacity:.55; margin-top:6px;">How activation works: paste your <strong>license blob</strong> below → vendor signs an activation token bound to this PIN → paste the activation token to enable the license.</div></div></if><if :="status && status.isLicensedBuild && status.buildFingerprint"><div class="lp-pin-box" style="margin-top:-12px;"><div class="lp-pin-label">Build signing-key fingerprint</div><div style="font-family:monospace; font-size:12px;">{{ status.buildFingerprint }}</div><div style="font-size:11px; opacity:.55; margin-top:6px;">Your license must be signed by the private key paired with this fingerprint. If you see <em>"License signature invalid"</em>, the license was signed by a different keypair than the one this build uses.</div></div></if><if :="status && !status.isLicensedBuild"><div class="lp-pin-box" style="opacity:.6"><div class="lp-pin-label">Development build — license enforcement disabled</div><div style="font-size:12px; margin-top:4px;">Set <code>OKDB_LICENSED_BUILD=true</code> in <code>.env</code> to enable enforcement in dev, or run <code>npm run build:prod</code> for a production build.</div></div></if></if><if :="status && status.usage && status.effective"><div style="display:flex; flex-direction:column; gap:10px; margin-bottom:20px;"><if :="status.effective.limits.maxWrites"><div><div style="display:flex; justify-content:space-between; font-size:11px; opacity:.7; margin-bottom:4px;"><span>Writes</span> <span style="font-family:monospace;">{{ formatNum(status.usage.writes) }} / {{ formatNum(status.effective.limits.maxWrites) }}</span></div><div style="height:6px; background:var(--okdb-app-surface-2,rgba(255,255,255,.06)); border-radius:3px; overflow:hidden;"><div :style="\'height:100%;width:\' + usagePct(status.usage.writes, status.effective.limits.maxWrites) + \'%;background:\' + usageBarColor(usagePct(status.usage.writes, status.effective.limits.maxWrites)) + \';transition:width .2s;\'"/></div></div></if><if :="status.effective.limits.envs"><div><div style="display:flex; justify-content:space-between; font-size:11px; opacity:.7; margin-bottom:4px;"><span>Environments</span> <span style="font-family:monospace;">{{ status.usage.envs }} / {{ status.effective.limits.envs }}</span></div><div style="height:6px; background:var(--okdb-app-surface-2,rgba(255,255,255,.06)); border-radius:3px; overflow:hidden;"><div :style="\'height:100%;width:\' + usagePct(status.usage.envs, status.effective.limits.envs) + \'%;background:\' + usageBarColor(usagePct(status.usage.envs, status.effective.limits.envs)) + \';transition:width .2s;\'"/></div></div></if></div></if><if :="showAddForm"><div class="lp-add-form"><div style="font-size:13px; font-weight:600;">Add License or Activation Token</div><div style="font-size:11px; opacity:.6;">Paste either a <strong>license blob</strong> (issued by your vendor) or an <strong>activation token</strong> (vendor\'s response to your PIN). The server detects which it is.</div><div><textarea :value="addInput" @input="addInput = $event.target.value" rows="6" placeholder="Paste license blob or activation token here…" style="width:100%; font-family:monospace; font-size:11px; resize:vertical;"/></div><if :="addError"><div style="font-size:12px; color:var(--okdb-danger); background:var(--okdb-danger-soft-bg,rgba(245,101,101,.08)); border:1px solid var(--okdb-danger-soft-border,rgba(245,101,101,.2)); border-radius:6px; padding:8px 12px;">{{ addError }}</div></if><div style="display:flex; gap:8px; justify-content:flex-end;"><button class="lp-btn" @click="cancelAddForm()">Cancel</button> <button class="lp-btn lp-btn--primary" @click="submitAdd()" :disabled="!addInput.trim() || addLoading">{{ addLoading ? \'Submitting…\' : \'Submit\' }}</button></div></div></if><if :="status && status.effective"><div style="margin-top:20px; font-size:10px; text-transform:uppercase; letter-spacing:.06em; opacity:.45; margin-bottom:8px;">Current capabilities</div><table class="lp-table" style="margin-bottom:16px;"><thead><tr><th>Feature</th><th>Status</th><th>Limit</th></tr></thead><tbody><tr><td>Sync</td><td><span :style="status.effective.features.sync ? \'color:var(--okdb-success,#38a169)\' : \'opacity:.45\'">{{ status.effective.features.sync ? \'✓ enabled\' : \'✗ disabled\' }}</span></td><td style="font-family:monospace;">{{ status.effective.limits.syncPeers ? \'max \' + status.effective.limits.syncPeers + \' peers\' : (status.effective.features.sync ? \'unlimited\' : \'—\') }}</td></tr><tr><td>Embeddings</td><td><span :style="status.effective.features.embeddings ? \'color:var(--okdb-success,#38a169)\' : \'opacity:.45\'">{{ status.effective.features.embeddings ? \'✓ enabled\' : \'✗ disabled\' }}</span></td><td>—</td></tr><tr><td>Engines</td><td><span :style="status.effective.features.engines ? \'color:var(--okdb-success,#38a169)\' : \'opacity:.45\'">{{ status.effective.features.engines ? \'✓ enabled\' : \'✗ disabled\' }}</span></td><td>—</td></tr><tr><td>Full-text search</td><td><span :style="status.effective.features.fts ? \'color:var(--okdb-success,#38a169)\' : \'opacity:.45\'">{{ status.effective.features.fts ? \'✓ enabled\' : \'✗ disabled\' }}</span></td><td>—</td></tr><tr><td>Time machine</td><td><span :style="status.effective.features.timeMachine ? \'color:var(--okdb-success,#38a169)\' : \'opacity:.45\'">{{ status.effective.features.timeMachine ? \'✓ enabled\' : \'✗ disabled\' }}</span></td><td>—</td></tr><tr><td>Views</td><td><span :style="status.effective.features.views ? \'color:var(--okdb-success,#38a169)\' : \'opacity:.45\'">{{ status.effective.features.views ? \'✓ enabled\' : \'✗ disabled\' }}</span></td><td>—</td></tr><tr><td>MCP</td><td><span :style="status.effective.features.mcp ? \'color:var(--okdb-success,#38a169)\' : \'opacity:.45\'">{{ status.effective.features.mcp ? \'✓ enabled\' : \'✗ disabled\' }}</span></td><td>—</td></tr><tr><td>Files</td><td><span :style="status.effective.features.files ? \'color:var(--okdb-success,#38a169)\' : \'opacity:.45\'">{{ status.effective.features.files ? \'✓ enabled\' : \'✗ disabled\' }}</span></td><td>—</td></tr><tr><td>Environments</td><td><span style="color:var(--okdb-success,#38a169)">✓ enabled</span></td><td style="font-family:monospace;">{{ status.effective.limits.envs ? \'max \' + status.effective.limits.envs : \'unlimited\' }}</td></tr><tr><td>Types per env</td><td><span style="color:var(--okdb-success,#38a169)">✓ enabled</span></td><td style="font-family:monospace;">{{ status.effective.limits.typesPerEnv ? \'max \' + status.effective.limits.typesPerEnv : \'unlimited\' }}</td></tr><tr><td>Total writes</td><td><span style="color:var(--okdb-success,#38a169)">✓ enabled</span></td><td style="font-family:monospace;">{{ status.effective.limits.maxWrites ? (status.effective.limits.maxWrites).toLocaleString() + \' max\' : \'unlimited\' }}</td></tr></tbody></table></if><if :="status && status.licenses && status.licenses.length > 0"><div class="lp-lic-header"><span>ID</span><span>Licensee</span><span>Expires</span><span>Status</span><span>Features</span><span>Actions</span></div><each :of="status.licenses" let:lic><div class="lp-lic-row"><div class="lp-lic-cells"><span style="font-family:monospace; opacity:.7; font-size:11px;">{{ shortId(lic.id) }}</span> <span style="font-size:12px;">{{ lic.licensee || \'—\' }}</span> <span style="font-family:monospace; font-size:12px;">{{ formatDate(lic.expiresAt) }}</span> <span><span :style="badgeStyle(lic.status)">{{ lic.status }}</span></span> <span style="opacity:.7; font-size:12px;">{{ featureSummary(lic.features) }}</span><div style="display:flex; gap:6px; flex-wrap:wrap; align-items:center;"><if :="lic.status === \'pending\' || lic.status === \'wrong_node\'"><button class="lp-btn" style="font-size:11px;" @click="openActivate(lic.id)">Activate</button></if><button class="lp-btn lp-btn--danger" style="font-size:11px;" @click="removeLicense(lic.id)">Remove</button></div></div><if :="activatingId === lic.id"><div class="lp-activate-row"><div style="font-size:11px; opacity:.7;">PIN for this license: <span style="font-family:monospace; font-weight:600;">{{ licensePin(lic) }}</span></div><textarea :value="activateToken" @input="activateToken = $event.target.value" rows="3" placeholder="Paste activation token…" style="width:100%; font-family:monospace; font-size:11px; resize:vertical;"/><if :="activateError"><div style="font-size:11px; color:var(--okdb-danger);">{{ activateError }}</div></if><div style="display:flex; gap:6px; justify-content:flex-end;"><button class="lp-btn" style="font-size:11px;" @click="cancelActivate()">Cancel</button> <button class="lp-btn lp-btn--primary" style="font-size:11px;" @click="submitActivate(lic.id)" :disabled="!activateToken.trim() || activateLoading">{{ activateLoading ? \'Activating…\' : \'Activate\' }}</button></div></div></if></div></each></if><if :="error"><div style="font-size:13px; color:var(--okdb-danger); padding:12px 0;">{{ error }}</div></if></if></div>'};
@@ -1 +1 @@
1
- <script type="module">import eventsStore from"../../stores/events-store.js";export default{tag:"processors-index",unwrap:!0,context:s=>({processors:[],busy:!1,_unsubs:[],async load(){this.busy=!0;try{const s=await fetch(__ENV__.API+"/processors");s.ok&&(this.processors=(await s.json()).data??[])}catch{}finally{this.busy=!1}},async doPause(s){await fetch(__ENV__.API+"/processors/"+encodeURIComponent(s)+"/pause",{method:"POST"}),await this.load()},async doResume(s){await fetch(__ENV__.API+"/processors/"+encodeURIComponent(s)+"/resume",{method:"POST"}),await this.load()},async doRetry(s){await fetch(__ENV__.API+"/processors/"+encodeURIComponent(s)+"/retry",{method:"POST"}),await this.load()},async doResetCursor(s){s&&(await fetch(__ENV__.API+"/processors/cursor/reset",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({cursorKey:s})}),await this.load())},stateColor(s){switch(s){case"online":return"var(--okdb-success)";case"building":return"var(--okdb-warning)";case"error":return"var(--okdb-danger)";case"waiting":return"var(--okdb-method-default)";case"paused":return"#818cf8";default:return"var(--okdb-app-fg-subtle)"}},lagLabel:s=>0===s.lag?"caught up":s.lag+" behind",progressPct:s=>s.progress?Math.round(100*(s.progress.percent??0)):null}),async init(s){await this.load();const e=()=>this.load();this._unsubs=[eventsStore.on("processor:ready",e),eventsStore.on("processor:error",e),eventsStore.on("processor:paused",e),eventsStore.on("processor:resumed",e),eventsStore.on("processor:retry",e)]},unmount(){for(const s of this._unsubs)s();this._unsubs=[]}}</script><style>.proc-grid{display:grid;grid-template-columns:1fr;gap:12px;padding:16px}.proc-card{background:var(--okdb-app-bg);border:1px solid var(--okdb-app-border);border-radius:8px;padding:14px 16px;font-size:13px}.proc-card__header{display:flex;align-items:center;gap:8px;margin-bottom:8px}.proc-card__dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}.proc-card__name{font-weight:600;color:var(--okdb-app-fg-strong);flex:1}.proc-card__state{font-size:11px;padding:2px 8px;border-radius:4px;text-transform:uppercase;letter-spacing:.5px}.proc-card__row{display:flex;gap:16px;margin-top:6px;font-size:12px;color:var(--okdb-method-default)}.proc-card__row strong{color:var(--okdb-app-fg)}.proc-card__actions{display:flex;gap:6px;margin-top:10px}.proc-card__actions button{font-size:11px;padding:3px 10px;border-radius:4px;border:1px solid var(--okdb-app-border-strong);background:var(--okdb-app-surface);color:var(--okdb-method-default);cursor:pointer}.proc-card__actions button:hover{background:var(--okdb-app-border);color:var(--okdb-app-fg-strong)}.proc-card__error{margin-top:6px;padding:6px 8px;background:var(--okdb-danger-soft-bg);border:1px solid #7f1d1d;border-radius:4px;color:var(--okdb-danger);font-size:12px;word-break:break-all}.proc-card__progress{margin-top:6px;height:4px;background:var(--okdb-app-border);border-radius:2px;overflow:hidden}.proc-card__progress-bar{height:100%;background:var(--okdb-accent);transition:width .3s ease;border-radius:2px}.proc-empty{color:var(--okdb-app-fg-subtle);text-align:center;padding:40px 16px;font-size:14px}</style><template><component import="../../layouts/_default.ok.html"><template name="sidebar"><div style="padding:8px 8px;font-size:12px;color:var(--okdb-method-default)"><strong style="color:var(--okdb-app-fg-strong)">Processors</strong><div style="margin-top:4px;opacity:.6">{{ processors.length }} registered</div></div></template><template name="header"><h2 style="margin:0;font-size:16px;color:var(--okdb-app-fg-strong)">Change Processors</h2></template><template name="default"><if :="processors.length === 0 && !busy"><div class="proc-empty">No processors registered.<br><span style="font-size:12px;opacity:.6">Processors are registered by subsystems (FTS, embeddings) or via <code>okdb.processor.register()</code>.</span></div></if><div class="proc-grid"><for :of="processors" let:p><div class="proc-card"><div class="proc-card__header"><span class="proc-card__dot" :style="'background:' + stateColor(p.paused ? 'paused' : p.state)"></span> <span class="proc-card__name">{{ p.meta?.name || p.id.slice(0, 8) }} </span><span class="proc-card__state" :style="'color:' + stateColor(p.paused ? 'paused' : p.state)">{{ p.paused ? 'paused' : p.state }}</span></div><div class="proc-card__row"><span>Type: <strong>{{ p.type }}</strong></span> <span>Mode: <strong>{{ p.mode }}</strong></span> <span>Lag: <strong>{{ lagLabel(p) }}</strong></span></div><div class="proc-card__row"><span>Clock: <strong>{{ p.lastClock }}</strong> / {{ p.headClock }}</span><if :="p.bootstrap"><span>Bootstrap: <strong>{{ p.bootstrap }}</strong></span></if></div><if :="progressPct(p) !== null && p.state === 'building'"><div class="proc-card__progress"><div class="proc-card__progress-bar" :style="'width:' + progressPct(p) + '%'"></div></div><div style="font-size:11px;color:var(--okdb-app-fg-subtle);margin-top:2px">{{ p.progress?.phase }} — {{ progressPct(p) }}%</div></if><if :="p.error"><div class="proc-card__error">{{ p.error }}</div></if><div class="proc-card__actions"><if :="!p.paused && p.state === 'online'"><button @click="doPause(p.id)">⏸ Pause</button></if><if :="p.paused"><button @click="doResume(p.id)">▶ Resume</button></if><if :="p.state === 'error'"><button @click="doRetry(p.id)">↻ Retry</button></if><if :="p.cursorKey"><button @click="doResetCursor(p.cursorKey)" title="Reset cursor for this processor — forces full reprocess on next start">⟲ Reset Cursor</button></if></div></div></for></div></template></component></template>
1
+ <script type="module">import s from"../../stores/events-store.js";export default{tag:"processors-index",unwrap:!0,context:s=>({processors:[],busy:!1,_unsubs:[],async load(){this.busy=!0;try{const s=await fetch(__ENV__.API+"/processors");s.ok&&(this.processors=(await s.json()).data??[])}catch{}finally{this.busy=!1}},async doPause(s){await fetch(__ENV__.API+"/processors/"+encodeURIComponent(s)+"/pause",{method:"POST"}),await this.load()},async doResume(s){await fetch(__ENV__.API+"/processors/"+encodeURIComponent(s)+"/resume",{method:"POST"}),await this.load()},async doRetry(s){await fetch(__ENV__.API+"/processors/"+encodeURIComponent(s)+"/retry",{method:"POST"}),await this.load()},async doResetCursor(s){s&&(await fetch(__ENV__.API+"/processors/cursor/reset",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({cursorKey:s})}),await this.load())},stateColor(s){switch(s){case"online":return"var(--okdb-success)";case"building":return"var(--okdb-warning)";case"error":return"var(--okdb-danger)";case"waiting":return"var(--okdb-method-default)";case"paused":return"#818cf8";default:return"var(--okdb-app-fg-subtle)"}},lagLabel:s=>0===s.lag?"caught up":s.lag+" behind",progressPct:s=>s.progress?Math.round(100*(s.progress.percent??0)):null}),async init(o){await this.load();const e=()=>this.load();this._unsubs=[s.on("processor:ready",e),s.on("processor:error",e),s.on("processor:paused",e),s.on("processor:resumed",e),s.on("processor:retry",e)]},unmount(){for(const s of this._unsubs)s();this._unsubs=[]}};</script><style>.proc-grid{display:grid;grid-template-columns:1fr;gap:12px;padding:16px}.proc-card{background:var(--okdb-app-bg);border:1px solid var(--okdb-app-border);border-radius:8px;padding:14px 16px;font-size:13px}.proc-card__header{display:flex;align-items:center;gap:8px;margin-bottom:8px}.proc-card__dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}.proc-card__name{font-weight:600;color:var(--okdb-app-fg-strong);flex:1}.proc-card__state{font-size:11px;padding:2px 8px;border-radius:4px;text-transform:uppercase;letter-spacing:.5px}.proc-card__row{display:flex;gap:16px;margin-top:6px;font-size:12px;color:var(--okdb-method-default)}.proc-card__row strong{color:var(--okdb-app-fg)}.proc-card__actions{display:flex;gap:6px;margin-top:10px}.proc-card__actions button{font-size:11px;padding:3px 10px;border-radius:4px;border:1px solid var(--okdb-app-border-strong);background:var(--okdb-app-surface);color:var(--okdb-method-default);cursor:pointer}.proc-card__actions button:hover{background:var(--okdb-app-border);color:var(--okdb-app-fg-strong)}.proc-card__error{margin-top:6px;padding:6px 8px;background:var(--okdb-danger-soft-bg);border:1px solid #7f1d1d;border-radius:4px;color:var(--okdb-danger);font-size:12px;word-break:break-all}.proc-card__progress{margin-top:6px;height:4px;background:var(--okdb-app-border);border-radius:2px;overflow:hidden}.proc-card__progress-bar{height:100%;background:var(--okdb-accent);transition:width .3s ease;border-radius:2px}.proc-empty{color:var(--okdb-app-fg-subtle);text-align:center;padding:40px 16px;font-size:14px}</style><template><component import="../../layouts/_default.ok.html"><template name="sidebar"><div style="padding:8px 8px;font-size:12px;color:var(--okdb-method-default)"><strong style="color:var(--okdb-app-fg-strong)">Processors</strong><div style="margin-top:4px;opacity:.6">{{ processors.length }} registered</div></div></template><template name="header"><h2 style="margin:0;font-size:16px;color:var(--okdb-app-fg-strong)">Change Processors</h2></template><template name="default"><if :="processors.length === 0 && !busy"><div class="proc-empty">No processors registered.<br><span style="font-size:12px;opacity:.6">Processors are registered by subsystems (FTS, embeddings) or via <code>okdb.processor.register()</code>.</span></div></if><div class="proc-grid"><for :of="processors" let:p><div class="proc-card"><div class="proc-card__header"><span class="proc-card__dot" :style="'background:' + stateColor(p.paused ? 'paused' : p.state)"></span> <span class="proc-card__name">{{ p.meta?.name || p.id.slice(0, 8) }} </span><span class="proc-card__state" :style="'color:' + stateColor(p.paused ? 'paused' : p.state)">{{ p.paused ? 'paused' : p.state }}</span></div><div class="proc-card__row"><span>Type: <strong>{{ p.type }}</strong></span> <span>Mode: <strong>{{ p.mode }}</strong></span> <span>Lag: <strong>{{ lagLabel(p) }}</strong></span></div><div class="proc-card__row"><span>Clock: <strong>{{ p.lastClock }}</strong> / {{ p.headClock }}</span><if :="p.bootstrap"><span>Bootstrap: <strong>{{ p.bootstrap }}</strong></span></if></div><if :="progressPct(p) !== null && p.state === 'building'"><div class="proc-card__progress"><div class="proc-card__progress-bar" :style="'width:' + progressPct(p) + '%'"></div></div><div style="font-size:11px;color:var(--okdb-app-fg-subtle);margin-top:2px">{{ p.progress?.phase }} — {{ progressPct(p) }}%</div></if><if :="p.error"><div class="proc-card__error">{{ p.error }}</div></if><div class="proc-card__actions"><if :="!p.paused && p.state === 'online'"><button @click="doPause(p.id)">⏸ Pause</button></if><if :="p.paused"><button @click="doResume(p.id)">▶ Resume</button></if><if :="p.state === 'error'"><button @click="doRetry(p.id)">↻ Retry</button></if><if :="p.cursorKey"><button @click="doResetCursor(p.cursorKey)" title="Reset cursor for this processor — forces full reprocess on next start">⟲ Reset Cursor</button></if></div></div></for></div></template></component></template>
@@ -1 +1 @@
1
- <script type="module">import queueBuckets from"./parts/queue-buckets.ok.js";import queueJobs from"./parts/queue-jobs.ok.js";import queueTypes from"./parts/queue-types.ok.js";import queueWorkers from"./parts/queue-workers.ok.js";import queueJobTypes from"./parts/queue-job-types.ok.js";export default{tag:"queue-index",unwrap:!0,dependencies:[queueTypes,queueJobs,queueBuckets,queueWorkers,queueJobTypes],context:e=>({view:"jobs",selectedType:null,typesEl:null,jobsEl:null,onTypeSelected(e){const t=e.detail??null;this.selectedType!==t&&(this.selectedType=t,this.syncRouteState())},setView(e){["jobs","workers","buckets","types"].includes(e)||(e="jobs"),this.view!==e&&(this.view=e,this.syncRouteState())},applyRouteState(){const t=e.$vars?.$router,s=t?._search||{},o=["jobs","workers","buckets","types"].includes(s.view)?s.view:"jobs",r=s.type?String(s.type):null;this.view!==o&&(this.view=o),this.selectedType!==r&&(this.selectedType=r)},syncRouteState(){const t=e.$vars?.$router;if(!t?.resolve||!t?.replace)return;const s=t._search||{},o={...s};e.$ok?.stores?.okdb?.selectedEnv&&(o.env=e.$ok.stores.okdb.selectedEnv),this.view&&"jobs"!==this.view?o.view=this.view:delete o.view,this.selectedType?o.type=this.selectedType:delete o.type;const r=["jobs","workers","buckets","types"].includes(s.view)?s.view:"jobs",i=s.type?String(s.type):null,u=o.view?String(o.view):"jobs",n=o.type?String(o.type):null,p=s.env?String(s.env):null,l=o.env?String(o.env):null;r===u&&i===n&&p===l||t.replace(t.resolve(t._path||"/queue",o,t._anchor||""))},onJobsRefresh(){this.typesEl?.$scope?.$innerContext?.refresh?.()},async onEnvChanged(){this.selectedType=null,await(this.typesEl?.$scope?.$innerContext?.refresh?.()),await(this.jobsEl?.$scope?.$innerContext?.query?.load?.()),await(this.jobsEl?.$scope?.$innerContext?.loadStats?.())}}),init(e){this.applyRouteState(),e.$watch(()=>e.$ok.stores.okdb.selectedEnv,()=>this.onEnvChanged()),e.$watch(()=>[e.$vars?.$router?._search?.view??"",e.$vars?.$router?._search?.type??""].join("||"),()=>this.applyRouteState())}}</script><style>.queue-nav-btn{width:100%;text-align:left;padding:6px 10px;border-radius:6px;border:1px solid transparent;background:0 0;color:var(--okdb-app-fg-muted);font-size:12px;cursor:pointer;transition:background .12s,border-color .12s,color .12s}.queue-nav-btn:hover{background:var(--okdb-app-surface);border-color:var(--okdb-app-border);color:var(--okdb-app-fg-strong)}.queue-nav-btn--on{background:var(--okdb-app-surface);border-color:var(--okdb-accent);color:var(--okdb-app-fg-strong)}</style><template><component import="../../layouts/_default.ok.html"><template name="sidebar"><div style="display:flex;flex-direction:column;gap:10px;height:100%;min-height:0"><div style="display:flex;flex-direction:column;gap:4px"><button @click="setView('jobs')" :class="view==='jobs' ? 'queue-nav-btn queue-nav-btn--on' : 'queue-nav-btn'">Jobs</button> <button @click="setView('workers')" :class="view==='workers' ? 'queue-nav-btn queue-nav-btn--on' : 'queue-nav-btn'">Workers</button> <button @click="setView('buckets')" :class="view==='buckets' ? 'queue-nav-btn queue-nav-btn--on' : 'queue-nav-btn'">Buckets</button> <button @click="setView('types')" :class="view==='types' ? 'queue-nav-btn queue-nav-btn--on' : 'queue-nav-btn'">Types</button></div><div style="height:1px;background:var(--okdb-app-border);margin:4px 0"></div><if :="view === 'jobs' || view === 'workers'"><queue-types :ref="typesEl = $el" :selectedtype="selectedType" @type="onTypeSelected"/></if><if :="view === 'buckets'"><div style="opacity:.55;font-size:12px;line-height:1.7">Token buckets rate-limit job throughput.<br>Assign a bucket name to a job at enqueue time to control how many tokens are consumed per job.</div></if><if :="view === 'types'"><div style="opacity:.55;font-size:12px;line-height:1.7">Register job types to define defaults, limit concurrency, and pause or drain a type without touching individual jobs.</div></if><if :="view === 'workers'"><div style="height:1px;background:var(--okdb-app-border);margin:2px 0"></div><div style="opacity:.45;font-size:11px;line-height:1.6">Select a job type above to scope workers, or see all workers.</div></if></div></template><template name="default"><if :="view === 'jobs'"><queue-jobs :ref="jobsEl = $el" :type="selectedType" @refresh="onJobsRefresh" @go-to-buckets="setView('buckets')"/></if><if :="view === 'workers'"><queue-workers :jobtype="selectedType"/></if><if :="view === 'buckets'"><queue-buckets/></if><if :="view === 'types'"><queue-job-types/></if></template></component></template>
1
+ <script type="module">import e from"./parts/queue-buckets.ok.js";import t from"./parts/queue-jobs.ok.js";import s from"./parts/queue-types.ok.js";import o from"./parts/queue-workers.ok.js";import i from"./parts/queue-job-types.ok.js";export default{tag:"queue-index",unwrap:!0,dependencies:[s,t,e,o,i],context:e=>({view:"jobs",selectedType:null,typesEl:null,jobsEl:null,onTypeSelected(e){const t=e.detail??null;this.selectedType!==t&&(this.selectedType=t,this.syncRouteState())},setView(e){["jobs","workers","buckets","types"].includes(e)||(e="jobs"),this.view!==e&&(this.view=e,this.syncRouteState())},applyRouteState(){const t=e.$vars?.$router,s=t?._search||{},o=["jobs","workers","buckets","types"].includes(s.view)?s.view:"jobs",i=s.type?String(s.type):null;this.view!==o&&(this.view=o),this.selectedType!==i&&(this.selectedType=i)},syncRouteState(){const t=e.$vars?.$router;if(!t?.resolve||!t?.replace)return;const s=t._search||{},o={...s};e.$ok?.stores?.okdb?.selectedEnv&&(o.env=e.$ok.stores.okdb.selectedEnv),this.view&&"jobs"!==this.view?o.view=this.view:delete o.view,this.selectedType?o.type=this.selectedType:delete o.type;const i=["jobs","workers","buckets","types"].includes(s.view)?s.view:"jobs",r=s.type?String(s.type):null,n=o.view?String(o.view):"jobs",l=o.type?String(o.type):null,p=s.env?String(s.env):null,u=o.env?String(o.env):null;i===n&&r===l&&p===u||t.replace(t.resolve(t._path||"/queue",o,t._anchor||""))},onJobsRefresh(){this.typesEl?.$scope?.$innerContext?.refresh?.()},async onEnvChanged(){this.selectedType=null,await(this.typesEl?.$scope?.$innerContext?.refresh?.()),await(this.jobsEl?.$scope?.$innerContext?.query?.load?.()),await(this.jobsEl?.$scope?.$innerContext?.loadStats?.())}}),init(e){this.applyRouteState(),e.$watch(()=>e.$ok.stores.okdb.selectedEnv,()=>this.onEnvChanged()),e.$watch(()=>[e.$vars?.$router?._search?.view??"",e.$vars?.$router?._search?.type??""].join("||"),()=>this.applyRouteState())}};</script><style>.queue-nav-btn{width:100%;text-align:left;padding:6px 10px;border-radius:6px;border:1px solid transparent;background:0 0;color:var(--okdb-app-fg-muted);font-size:12px;cursor:pointer;transition:background .12s,border-color .12s,color .12s}.queue-nav-btn:hover{background:var(--okdb-app-surface);border-color:var(--okdb-app-border);color:var(--okdb-app-fg-strong)}.queue-nav-btn--on{background:var(--okdb-app-surface);border-color:var(--okdb-accent);color:var(--okdb-app-fg-strong)}</style><template><component import="../../layouts/_default.ok.html"><template name="sidebar"><div style="display:flex;flex-direction:column;gap:10px;height:100%;min-height:0"><div style="display:flex;flex-direction:column;gap:4px"><button @click="setView('jobs')" :class="view==='jobs' ? 'queue-nav-btn queue-nav-btn--on' : 'queue-nav-btn'">Jobs</button> <button @click="setView('workers')" :class="view==='workers' ? 'queue-nav-btn queue-nav-btn--on' : 'queue-nav-btn'">Workers</button> <button @click="setView('buckets')" :class="view==='buckets' ? 'queue-nav-btn queue-nav-btn--on' : 'queue-nav-btn'">Buckets</button> <button @click="setView('types')" :class="view==='types' ? 'queue-nav-btn queue-nav-btn--on' : 'queue-nav-btn'">Types</button></div><div style="height:1px;background:var(--okdb-app-border);margin:4px 0"></div><if :="view === 'jobs' || view === 'workers'"><queue-types :ref="typesEl = $el" :selectedtype="selectedType" @type="onTypeSelected"/></if><if :="view === 'buckets'"><div style="opacity:.55;font-size:12px;line-height:1.7">Token buckets rate-limit job throughput.<br>Assign a bucket name to a job at enqueue time to control how many tokens are consumed per job.</div></if><if :="view === 'types'"><div style="opacity:.55;font-size:12px;line-height:1.7">Register job types to define defaults, limit concurrency, and pause or drain a type without touching individual jobs.</div></if><if :="view === 'workers'"><div style="height:1px;background:var(--okdb-app-border);margin:2px 0"></div><div style="opacity:.45;font-size:11px;line-height:1.6">Select a job type above to scope workers, or see all workers.</div></if></div></template><template name="default"><if :="view === 'jobs'"><queue-jobs :ref="jobsEl = $el" :type="selectedType" @refresh="onJobsRefresh" @go-to-buckets="setView('buckets')"/></if><if :="view === 'workers'"><queue-workers :jobtype="selectedType"/></if><if :="view === 'buckets'"><queue-buckets/></if><if :="view === 'types'"><queue-job-types/></if></template></component></template>
@@ -1 +1 @@
1
- function _0x35ed(){const _0x1270b6=['35306ytmOAb','\x0a<div\x20style=\x22min-width:320px;\x20display:flex;\x20flex-direction:column;\x20gap:12px;\x22>\x0a\x0a\x20\x20<if\x20:=\x22error\x22>\x0a\x20\x20\x20\x20<div\x20style=\x22color:var(--okdb-danger);\x20font-size:12px;\x20padding:8px;\x20border:1px\x20solid\x20var(--okdb-danger);\x20border-radius:6px;\x22>\x0a\x20\x20\x20\x20\x20\x20{{\x20error\x20}}\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</if>\x0a\x0a\x20\x20<div\x20style=\x22display:grid;\x20grid-template-columns:130px\x201fr;\x20gap:8px\x2012px;\x20align-items:center;\x20font-size:13px;\x22>\x0a\x0a\x20\x20\x20\x20<label\x20style=\x22opacity:.7;\x22>Bucket\x20ID</label>\x0a\x20\x20\x20\x20<input\x0a\x20\x20\x20\x20\x20\x20:value=\x22form.id\x22\x0a\x20\x20\x20\x20\x20\x20@input=\x22form.id\x20=\x20$el.value\x22\x0a\x20\x20\x20\x20\x20\x20placeholder=\x22e.g.\x20openai-api\x22\x0a\x20\x20\x20\x20\x20\x20style=\x22font-family:monospace;\x22\x0a\x20\x20\x20\x20\x20\x20@keydown.enter=\x22save()\x22\x0a\x20\x20\x20\x20/>\x0a\x0a\x20\x20\x20\x20<label\x20style=\x22opacity:.7;\x22>Capacity</label>\x0a\x20\x20\x20\x20<input\x20type=\x22number\x22\x20min=\x221\x22\x0a\x20\x20\x20\x20\x20\x20:value=\x22form.capacity\x22\x0a\x20\x20\x20\x20\x20\x20@input=\x22form.capacity\x20=\x20Number($el.value)\x22\x0a\x20\x20\x20\x20/>\x0a\x0a\x20\x20\x20\x20<label\x20style=\x22opacity:.7;\x22>Initial\x20Tokens</label>\x0a\x20\x20\x20\x20<input\x20type=\x22number\x22\x20min=\x220\x22\x0a\x20\x20\x20\x20\x20\x20:value=\x22form.tokens\x22\x0a\x20\x20\x20\x20\x20\x20@input=\x22form.tokens\x20=\x20Number($el.value)\x22\x0a\x20\x20\x20\x20/>\x0a\x0a\x20\x20\x20\x20<div\x20style=\x22grid-column:1/-1;\x20height:1px;\x20background:var(--okdb-app-border);\x20margin:2px\x200;\x22></div>\x0a\x0a\x20\x20\x20\x20<div\x20style=\x22grid-column:1/-1;\x20font-size:11px;\x20opacity:.5;\x20text-transform:uppercase;\x20letter-spacing:.05em;\x22>\x0a\x20\x20\x20\x20\x20\x20Auto-Refill\x20(optional)\x0a\x20\x20\x20\x20</div>\x0a\x0a\x20\x20\x20\x20<label\x20style=\x22opacity:.7;\x22>Refill\x20Amount</label>\x0a\x20\x20\x20\x20<input\x20type=\x22number\x22\x20min=\x220\x22\x0a\x20\x20\x20\x20\x20\x20:value=\x22form.refill_amount\x22\x0a\x20\x20\x20\x20\x20\x20@input=\x22form.refill_amount\x20=\x20Number($el.value)\x22\x0a\x20\x20\x20\x20\x20\x20placeholder=\x220\x20=\x20disabled\x22\x0a\x20\x20\x20\x20/>\x0a\x0a\x20\x20\x20\x20<label\x20style=\x22opacity:.7;\x22>Refill\x20Every\x20(ms)</label>\x0a\x20\x20\x20\x20<input\x20type=\x22number\x22\x20min=\x220\x22\x0a\x20\x20\x20\x20\x20\x20:value=\x22form.refill_every\x22\x0a\x20\x20\x20\x20\x20\x20@input=\x22form.refill_every\x20=\x20Number($el.value)\x22\x0a\x20\x20\x20\x20\x20\x20placeholder=\x22e.g.\x2060000\x20for\x201\x20min\x22\x0a\x20\x20\x20\x20/>\x0a\x0a\x20\x20</div>\x0a\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:10px;\x20justify-content:flex-end;\x20margin-top:4px;\x22>\x0a\x20\x20\x20\x20<button\x20@click=\x22cancel()\x22>Cancel</button>\x0a\x20\x20\x20\x20<button\x20@click=\x22save()\x22\x20:disabled=\x22busy\x20||\x20!form.id\x22>\x0a\x20\x20\x20\x20\x20\x20{{\x20busy\x20?\x20\x27Creating…\x27\x20:\x20\x27Create\x27\x20}}\x0a\x20\x20\x20\x20</button>\x0a\x20\x20</div>\x0a\x0a</div>\x0a','\x22\x20created','183501FYmndI','Bucket\x20ID\x20is\x20required','1308183FkZFhD','capacity','json','2009483rcvPfq','2490JzaTRL','success','tokens','Create\x20failed','120UQwKgf','refill_every','415IiTiPX','POST','$attr','/queue/bucket/add/','error','toast','busy','14716430qXtXNH','modal','form','1199172XRwrOM','apiBase','close','message','stringify','20cDytBg'];_0x35ed=function(){return _0x1270b6;};return _0x35ed();}const _0x5b87c6=_0x5705;(function(_0x36c495,_0x1c2d57){const _0xd134c6=_0x5705,_0x24e6e4=_0x36c495();while(!![]){try{const _0xc223b7=parseInt(_0xd134c6(0x19c))/0x1*(parseInt(_0xd134c6(0x19d))/0x2)+parseInt(_0xd134c6(0x1a2))/0x3+parseInt(_0xd134c6(0x197))/0x4+-parseInt(_0xd134c6(0x18d))/0x5*(-parseInt(_0xd134c6(0x187))/0x6)+parseInt(_0xd134c6(0x186))/0x7+-parseInt(_0xd134c6(0x18b))/0x8*(-parseInt(_0xd134c6(0x1a0))/0x9)+-parseInt(_0xd134c6(0x194))/0xa;if(_0xc223b7===_0x1c2d57)break;else _0x24e6e4['push'](_0x24e6e4['shift']());}catch(_0x5e36ae){_0x24e6e4['push'](_0x24e6e4['shift']());}}}(_0x35ed,0x3bb8c));function _0x5705(_0x10f1bd,_0x474748){_0x10f1bd=_0x10f1bd-0x184;const _0x35ed5b=_0x35ed();let _0x5705aa=_0x35ed5b[_0x10f1bd];return _0x5705aa;}import _0x4c5e10 from'../../../stores/okdb-store.js';export default{'tag':'create-bucket-modal','attr':{'modal':{},'onDone':{}},'context':_0x5b1575=>({'busy':!0x1,'error':null,'form':{'id':'','capacity':0x64,'tokens':0x64,'refill_amount':0x0,'refill_every':0x0},async 'save'(){const _0x10c168=_0x5705,_0x822671=String(this[_0x10c168(0x196)]['id']||'')['trim']();if(_0x822671){this[_0x10c168(0x193)]=!0x0,this[_0x10c168(0x191)]=null;try{const _0x1ae62e=await fetch(_0x4c5e10[_0x10c168(0x198)]+_0x10c168(0x190)+encodeURIComponent(_0x822671),{'method':_0x10c168(0x18e),'headers':{'Content-Type':'application/json'},'body':JSON[_0x10c168(0x19b)]({'capacity':Number(this[_0x10c168(0x196)][_0x10c168(0x184)])||0x0,'tokens':Number(this['form'][_0x10c168(0x189)])||0x0,'refill_amount':Number(this[_0x10c168(0x196)]['refill_amount'])||0x0,'refill_every':Number(this['form'][_0x10c168(0x18c)])||0x0})}),_0x5c5c7a=await _0x1ae62e[_0x10c168(0x185)]();if(!_0x1ae62e['ok'])throw new Error(_0x5c5c7a?.[_0x10c168(0x191)]?.[_0x10c168(0x19a)]||_0x10c168(0x18a));_0x5b1575['$ok']['shared']?.[_0x10c168(0x192)]?.[_0x10c168(0x188)]?.('Bucket\x20\x22'+_0x822671+_0x10c168(0x19f)),await _0x5b1575[_0x10c168(0x18f)][_0x10c168(0x195)][_0x10c168(0x199)](),await _0x5b1575['$attr']['onDone']?.(_0x822671);}catch(_0x398a4f){this[_0x10c168(0x191)]=_0x398a4f?.[_0x10c168(0x19a)]||_0x10c168(0x18a);}finally{this[_0x10c168(0x193)]=!0x1;}}else this[_0x10c168(0x191)]=_0x10c168(0x1a1);},'cancel'(){const _0x33c620=_0x5705;_0x5b1575[_0x33c620(0x18f)]['modal']['close']();}}),'template':_0x5b87c6(0x19e)};
1
+ import e from"../../../stores/okdb-store.js";export default{tag:"create-bucket-modal",attr:{modal:{},onDone:{}},context:n=>({busy:!1,error:null,form:{id:"",capacity:100,tokens:100,refill_amount:0,refill_every:0},async save(){const t=String(this.form.id||"").trim();if(t){this.busy=!0,this.error=null;try{const r=await fetch(`${e.apiBase}/queue/bucket/add/${encodeURIComponent(t)}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({capacity:Number(this.form.capacity)||0,tokens:Number(this.form.tokens)||0,refill_amount:Number(this.form.refill_amount)||0,refill_every:Number(this.form.refill_every)||0})}),i=await r.json();if(!r.ok)throw new Error(i?.error?.message||"Create failed");n.$ok.shared?.toast?.success?.(`Bucket "${t}" created`),await n.$attr.modal.close(),await(n.$attr.onDone?.(t))}catch(e){this.error=e?.message||"Create failed"}finally{this.busy=!1}}else this.error="Bucket ID is required"},cancel(){n.$attr.modal.close()}}),template:'\n<div style="min-width:320px; display:flex; flex-direction:column; gap:12px;">\n\n <if :="error">\n <div style="color:var(--okdb-danger); font-size:12px; padding:8px; border:1px solid var(--okdb-danger); border-radius:6px;">\n {{ error }}\n </div>\n </if>\n\n <div style="display:grid; grid-template-columns:130px 1fr; gap:8px 12px; align-items:center; font-size:13px;">\n\n <label style="opacity:.7;">Bucket ID</label>\n <input\n :value="form.id"\n @input="form.id = $el.value"\n placeholder="e.g. openai-api"\n style="font-family:monospace;"\n @keydown.enter="save()"\n />\n\n <label style="opacity:.7;">Capacity</label>\n <input type="number" min="1"\n :value="form.capacity"\n @input="form.capacity = Number($el.value)"\n />\n\n <label style="opacity:.7;">Initial Tokens</label>\n <input type="number" min="0"\n :value="form.tokens"\n @input="form.tokens = Number($el.value)"\n />\n\n <div style="grid-column:1/-1; height:1px; background:var(--okdb-app-border); margin:2px 0;"></div>\n\n <div style="grid-column:1/-1; font-size:11px; opacity:.5; text-transform:uppercase; letter-spacing:.05em;">\n Auto-Refill (optional)\n </div>\n\n <label style="opacity:.7;">Refill Amount</label>\n <input type="number" min="0"\n :value="form.refill_amount"\n @input="form.refill_amount = Number($el.value)"\n placeholder="0 = disabled"\n />\n\n <label style="opacity:.7;">Refill Every (ms)</label>\n <input type="number" min="0"\n :value="form.refill_every"\n @input="form.refill_every = Number($el.value)"\n placeholder="e.g. 60000 for 1 min"\n />\n\n </div>\n\n <div style="display:flex; gap:10px; justify-content:flex-end; margin-top:4px;">\n <button @click="cancel()">Cancel</button>\n <button @click="save()" :disabled="busy || !form.id">\n {{ busy ? \'Creating…\' : \'Create\' }}\n </button>\n </div>\n\n</div>\n'};
@@ -1 +1 @@
1
- const _0x1a659d=_0x3af5;(function(_0xa7ed8f,_0x2bd5af){const _0x1ab64a=_0x3af5,_0x47df7c=_0xa7ed8f();while(!![]){try{const _0x49b450=parseInt(_0x1ab64a(0xc9))/0x1+-parseInt(_0x1ab64a(0xd4))/0x2+-parseInt(_0x1ab64a(0xbc))/0x3*(parseInt(_0x1ab64a(0xcf))/0x4)+parseInt(_0x1ab64a(0xbe))/0x5+parseInt(_0x1ab64a(0xcc))/0x6+-parseInt(_0x1ab64a(0xc7))/0x7+parseInt(_0x1ab64a(0xca))/0x8*(parseInt(_0x1ab64a(0xb3))/0x9);if(_0x49b450===_0x2bd5af)break;else _0x47df7c['push'](_0x47df7c['shift']());}catch(_0x525c7f){_0x47df7c['push'](_0x47df7c['shift']());}}}(_0x5879,0x1ab31));function _0x3af5(_0x85da85,_0x1963b0){_0x85da85=_0x85da85-0xb1;const _0x58794d=_0x5879();let _0x3af521=_0x58794d[_0x85da85];return _0x3af521;}function _0x5879(){const _0x471d5b=['POST','toast','error','Bucket\x20ID\x20is\x20required','366708kZOUyH','tokens','capacity','\x0a<div\x20style=\x22min-width:320px;\x20display:flex;\x20flex-direction:column;\x20gap:12px;\x22>\x0a\x0a\x20\x20<if\x20:=\x22error\x22>\x0a\x20\x20\x20\x20<div\x20style=\x22color:var(--okdb-danger);\x20font-size:12px;\x20padding:8px;\x20border:1px\x20solid\x20var(--okdb-danger);\x20border-radius:6px;\x22>{{\x20error\x20}}</div>\x0a\x20\x20</if>\x0a\x0a\x20\x20<div\x20style=\x22display:grid;\x20grid-template-columns:120px\x201fr;\x20gap:8px\x2012px;\x20align-items:center;\x20font-size:13px;\x22>\x0a\x0a\x20\x20\x20\x20<label\x20style=\x22opacity:.7;\x22>Bucket\x20ID</label>\x0a\x20\x20\x20\x20<input\x0a\x20\x20\x20\x20\x20\x20:value=\x22form.id\x22\x0a\x20\x20\x20\x20\x20\x20@input=\x22form.id\x20=\x20$el.value\x22\x0a\x20\x20\x20\x20\x20\x20:disabled=\x22editMode\x22\x0a\x20\x20\x20\x20\x20\x20placeholder=\x22e.g.\x20openai-api\x22\x0a\x20\x20\x20\x20\x20\x20style=\x22font-family:monospace;\x22\x0a\x20\x20\x20\x20\x20\x20@keydown.enter=\x22save()\x22\x0a\x20\x20\x20\x20/>\x0a\x0a\x20\x20\x20\x20<label\x20style=\x22opacity:.7;\x22>Capacity</label>\x0a\x20\x20\x20\x20<input\x20type=\x22number\x22\x20min=\x221\x22\x20:value=\x22form.capacity\x22\x20@input=\x22form.capacity\x20=\x20Number($el.value)\x22\x20/>\x0a\x0a\x20\x20\x20\x20<label\x20style=\x22opacity:.7;\x22>Initial\x20Tokens</label>\x0a\x20\x20\x20\x20<input\x20type=\x22number\x22\x20min=\x220\x22\x20:value=\x22form.tokens\x22\x20@input=\x22form.tokens\x20=\x20Number($el.value)\x22\x20/>\x0a\x0a\x20\x20\x20\x20<div\x20style=\x22grid-column:1/-1;\x20height:1px;\x20background:var(--okdb-app-border);\x20margin:4px\x200;\x22></div>\x0a\x0a\x20\x20\x20\x20<div\x20style=\x22grid-column:1/-1;\x20font-size:11px;\x20opacity:.5;\x20text-transform:uppercase;\x20letter-spacing:.05em;\x22>\x0a\x20\x20\x20\x20\x20\x20Auto-Refill\x20(optional)\x0a\x20\x20\x20\x20</div>\x0a\x0a\x20\x20\x20\x20<label\x20style=\x22opacity:.7;\x22>Refill\x20Amount</label>\x0a\x20\x20\x20\x20<input\x20type=\x22number\x22\x20min=\x220\x22\x20:value=\x22form.refill_amount\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20@input=\x22form.refill_amount\x20=\x20Number($el.value)\x22\x20placeholder=\x220\x20=\x20disabled\x22\x20/>\x0a\x0a\x20\x20\x20\x20<label\x20style=\x22opacity:.7;\x22>Refill\x20Every\x20(ms)</label>\x0a\x20\x20\x20\x20<input\x20type=\x22number\x22\x20min=\x220\x22\x20:value=\x22form.refill_every\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20@input=\x22form.refill_every\x20=\x20Number($el.value)\x22\x20placeholder=\x22e.g.\x2060000\x20for\x201\x20min\x22\x20/>\x0a\x0a\x20\x20</div>\x0a\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:10px;\x20justify-content:flex-end;\x20margin-top:4px;\x22>\x0a\x20\x20\x20\x20<button\x20@click=\x22cancel()\x22>Cancel</button>\x0a\x20\x20\x20\x20<button\x20@click=\x22save()\x22\x20:disabled=\x22busy\x20||\x20!form.id\x22>\x0a\x20\x20\x20\x20\x20\x20{{\x20busy\x20?\x20\x27Saving…\x27\x20:\x20(editMode\x20?\x20\x27Save\x27\x20:\x20\x27Create\x27)\x20}}\x0a\x20\x20\x20\x20</button>\x0a\x20\x20</div>\x0a\x0a</div>\x0a','252LZuoAY','Bucket\x20created','edit-bucket-modal','onDone','busy','editMode','message','Save\x20failed','/queue/bucket/add/','3qKBrxt','refill_every','34295YJJoOc','stringify','success','close','application/json','$attr','initialForm','/queue/bucket/update/','refill_amount','234843DGtHlg','form','215029NNdzsk','22384tzISpI','shared','1003602QNNFDO','modal','apiBase','564940Lshvck'];_0x5879=function(){return _0x471d5b;};return _0x5879();}import _0x33cc82 from'../../../stores/okdb-store.js';export default{'tag':_0x1a659d(0xb5),'attr':{'modal':{},'editMode':{},'initialForm':{},'onDone':{}},'context'(_0xdf3291){const _0x148624=_0x1a659d,_0x1faec7=_0xdf3291[_0x148624(0xc3)][_0x148624(0xc4)]??{};return{'busy':!0x1,'error':null,'form':{'id':_0x1faec7['id']??'','capacity':_0x1faec7[_0x148624(0xb1)]??0x64,'tokens':_0x1faec7[_0x148624(0xd5)]??0x64,'refill_amount':_0x1faec7[_0x148624(0xc6)]??0x0,'refill_every':_0x1faec7[_0x148624(0xbd)]??0x0},get 'editMode'(){const _0x56a90a=_0x148624;return!!_0xdf3291['$attr'][_0x56a90a(0xb8)];},async 'save'(){const _0x421f9d=_0x148624,_0x2d800c=String(this[_0x421f9d(0xc8)]['id']||'')['trim']();if(_0x2d800c){this[_0x421f9d(0xb7)]=!0x0,this['error']=null;try{const _0x23def3=this['editMode']?_0x33cc82[_0x421f9d(0xce)]+_0x421f9d(0xc5)+encodeURIComponent(_0x2d800c):_0x33cc82[_0x421f9d(0xce)]+_0x421f9d(0xbb)+encodeURIComponent(_0x2d800c),_0x42eb16=await fetch(_0x23def3,{'method':_0x421f9d(0xd0),'headers':{'Content-Type':_0x421f9d(0xc2)},'body':JSON[_0x421f9d(0xbf)]({'capacity':Number(this[_0x421f9d(0xc8)][_0x421f9d(0xb1)])||0x0,'tokens':Number(this[_0x421f9d(0xc8)][_0x421f9d(0xd5)])??0x0,'refill_amount':Number(this[_0x421f9d(0xc8)][_0x421f9d(0xc6)])||0x0,'refill_every':Number(this['form']['refill_every'])||0x0})}),_0x14b325=await _0x42eb16['json']();if(!_0x42eb16['ok'])throw new Error(_0x14b325?.[_0x421f9d(0xd2)]?.[_0x421f9d(0xb9)]||_0x421f9d(0xba));_0xdf3291['$ok'][_0x421f9d(0xcb)]?.[_0x421f9d(0xd1)]?.[_0x421f9d(0xc0)]?.(this['editMode']?'Bucket\x20updated':_0x421f9d(0xb4)),await _0xdf3291[_0x421f9d(0xc3)][_0x421f9d(0xcd)]['close'](),await _0xdf3291['$attr'][_0x421f9d(0xb6)]?.();}catch(_0x52bd85){this[_0x421f9d(0xd2)]=_0x52bd85?.[_0x421f9d(0xb9)]||'Save\x20failed';}finally{this[_0x421f9d(0xb7)]=!0x1;}}else this['error']=_0x421f9d(0xd3);},'cancel'(){const _0x1d297d=_0x148624;_0xdf3291[_0x1d297d(0xc3)][_0x1d297d(0xcd)][_0x1d297d(0xc1)]();}};},'template':_0x1a659d(0xb2)};
1
+ import e from"../../../stores/okdb-store.js";export default{tag:"edit-bucket-modal",attr:{modal:{},editMode:{},initialForm:{},onDone:{}},context(t){const n=t.$attr.initialForm??{};return{busy:!1,error:null,form:{id:n.id??"",capacity:n.capacity??100,tokens:n.tokens??100,refill_amount:n.refill_amount??0,refill_every:n.refill_every??0},get editMode(){return!!t.$attr.editMode},async save(){const n=String(this.form.id||"").trim();if(n){this.busy=!0,this.error=null;try{const i=this.editMode?`${e.apiBase}/queue/bucket/update/${encodeURIComponent(n)}`:`${e.apiBase}/queue/bucket/add/${encodeURIComponent(n)}`,a=await fetch(i,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({capacity:Number(this.form.capacity)||0,tokens:Number(this.form.tokens)??0,refill_amount:Number(this.form.refill_amount)||0,refill_every:Number(this.form.refill_every)||0})}),r=await a.json();if(!a.ok)throw new Error(r?.error?.message||"Save failed");t.$ok.shared?.toast?.success?.(this.editMode?"Bucket updated":"Bucket created"),await t.$attr.modal.close(),await(t.$attr.onDone?.())}catch(e){this.error=e?.message||"Save failed"}finally{this.busy=!1}}else this.error="Bucket ID is required"},cancel(){t.$attr.modal.close()}}},template:'\n<div style="min-width:320px; display:flex; flex-direction:column; gap:12px;">\n\n <if :="error">\n <div style="color:var(--okdb-danger); font-size:12px; padding:8px; border:1px solid var(--okdb-danger); border-radius:6px;">{{ error }}</div>\n </if>\n\n <div style="display:grid; grid-template-columns:120px 1fr; gap:8px 12px; align-items:center; font-size:13px;">\n\n <label style="opacity:.7;">Bucket ID</label>\n <input\n :value="form.id"\n @input="form.id = $el.value"\n :disabled="editMode"\n placeholder="e.g. openai-api"\n style="font-family:monospace;"\n @keydown.enter="save()"\n />\n\n <label style="opacity:.7;">Capacity</label>\n <input type="number" min="1" :value="form.capacity" @input="form.capacity = Number($el.value)" />\n\n <label style="opacity:.7;">Initial Tokens</label>\n <input type="number" min="0" :value="form.tokens" @input="form.tokens = Number($el.value)" />\n\n <div style="grid-column:1/-1; height:1px; background:var(--okdb-app-border); margin:4px 0;"></div>\n\n <div style="grid-column:1/-1; font-size:11px; opacity:.5; text-transform:uppercase; letter-spacing:.05em;">\n Auto-Refill (optional)\n </div>\n\n <label style="opacity:.7;">Refill Amount</label>\n <input type="number" min="0" :value="form.refill_amount"\n @input="form.refill_amount = Number($el.value)" placeholder="0 = disabled" />\n\n <label style="opacity:.7;">Refill Every (ms)</label>\n <input type="number" min="0" :value="form.refill_every"\n @input="form.refill_every = Number($el.value)" placeholder="e.g. 60000 for 1 min" />\n\n </div>\n\n <div style="display:flex; gap:10px; justify-content:flex-end; margin-top:4px;">\n <button @click="cancel()">Cancel</button>\n <button @click="save()" :disabled="busy || !form.id">\n {{ busy ? \'Saving…\' : (editMode ? \'Save\' : \'Create\') }}\n </button>\n </div>\n\n</div>\n'};
@@ -1 +1 @@
1
- const _0x370ce6=_0x5578;(function(_0x46c72b,_0x217ddb){const _0x4bb3c2=_0x5578,_0x7dd057=_0x46c72b();while(!![]){try{const _0x2a6bb4=-parseInt(_0x4bb3c2(0xa6))/0x1+-parseInt(_0x4bb3c2(0xb0))/0x2*(-parseInt(_0x4bb3c2(0x84))/0x3)+-parseInt(_0x4bb3c2(0xa5))/0x4*(parseInt(_0x4bb3c2(0x89))/0x5)+parseInt(_0x4bb3c2(0x9b))/0x6*(parseInt(_0x4bb3c2(0xa8))/0x7)+-parseInt(_0x4bb3c2(0x8d))/0x8*(parseInt(_0x4bb3c2(0x76))/0x9)+-parseInt(_0x4bb3c2(0x9a))/0xa*(parseInt(_0x4bb3c2(0x72))/0xb)+parseInt(_0x4bb3c2(0x97))/0xc;if(_0x2a6bb4===_0x217ddb)break;else _0x7dd057['push'](_0x7dd057['shift']());}catch(_0xb683e){_0x7dd057['push'](_0x7dd057['shift']());}}}(_0x28f1,0x94b37));import _0x385b33 from'@toolkit/elements/ok-icon.ok.js';import _0x356c2c from'@toolkit/core/ok-overlay.ok.js';import _0x161b4c from'../../../components/ok-cron-input.ok.js';import _0x319d9c from'../../../components/ok-json-input.ok.js';function _0x5578(_0x39a4af,_0x562bce){_0x39a4af=_0x39a4af-0x6b;const _0x28f1e0=_0x28f1();let _0x5578e2=_0x28f1e0[_0x39a4af];return _0x5578e2;}import _0x126164 from'./create-bucket-modal.ok.js';function _0x28f1(){const _0x2d8a59=['busy','Failed\x20to\x20save\x20job','map','bucket','whenMode','asap','shared','timestamp','retryDelay','payloadInit','604967ZdaYcW','success','payload','filter','8828289RUuvMJ','loadBuckets','message','job','getTime','$ok','backoff','open','apiBase','close','/queue/job/','isArray','type','length','2583DUMwkP','trim','max_tries','$attr','loading','5PnWUwV','split','tags','POST','8ALmtNM','max','priority','maxTries','bucket_tokens','data','whenDatetime','progress','when','join','9849264EdQsDA','application/json','Job\x20updated','50KWusTF','18vficnL','idempotencyKey','applyJob','modal','bucketOptions','all','toast','onDone','tokens','Failed\x20to\x20load\x20job','3976620zztria','80484leMkjP','error','2410639RXfvmZ','__create__','datetime','backoff_multiplier','whenTs','<create-bucket-modal\x20:modal=\x22modal\x22\x20:on-done=\x22onDone\x22\x20/>','cron','json','2522WDaXJt','/queue/buckets','.mode-btn{font-size:11px;padding:2px\x208px;opacity:.6;border-radius:4px}.mode-btn--on{opacity:1;background:color-mix(in\x20srgb,var(--okdb-accent)\x2013%,transparent);border-color:color-mix(in\x20srgb,var(--okdb-accent)\x2033%,transparent);color:var(--okdb-accent)}.btn-ghost{background:0\x200;border:1px\x20solid\x20var(--okdb-app-border);border-radius:5px;color:inherit;cursor:pointer;opacity:.6;padding:2px\x208px;font-size:12px}.btn-ghost:hover{opacity:1;border-color:color-mix(in\x20srgb,var(--okdb-accent)\x2033%,transparent)}.btn-ghost--sm{font-size:11px;padding:1px\x207px}','\x0a<div\x20style=\x22position:relative;\x20min-width:720px;\x20max-width:calc(100vw\x20-\x2080px);\x20display:flex;\x20flex-direction:column;\x20gap:12px;\x22>\x0a\x20\x20<if\x20:=\x22busy\x20||\x20loading\x22>\x0a\x20\x20\x20\x20<ok-overlay>\x0a\x20\x20\x20\x20\x20\x20<div\x20class=\x22loader-center\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<ok-icon\x20name=\x22loader\x22\x20class=\x22spin\x22\x20style=\x22width:32px;height:32px;\x22></ok-icon>\x0a\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20</ok-overlay>\x0a\x20\x20</if>\x0a\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:8px;\x20align-items:center;\x20flex-wrap:wrap;\x20opacity:.8;\x20font-size:12px;\x22>\x0a\x20\x20\x20\x20<span>ID:\x20<b>{{\x20job?.id\x20}}</b></span>\x0a\x20\x20\x20\x20<span>Status:\x20<b>{{\x20job?.status\x20}}</b></span>\x0a\x20\x20\x20\x20<span>Tries:\x20<b>{{\x20job?.tries\x20}}\x20/\x20{{\x20job?.max_tries\x20}}</b></span>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20Row\x201:\x20type\x20+\x20priority\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:8px;\x20align-items:flex-end;\x22>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22><label>Type</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20placeholder=\x22e.g.\x20email.send\x22\x20:value=\x22type\x22\x20@input=\x22type\x20=\x20$el.value\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22width:120px;\x22><label>Priority</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20placeholder=\x220\x22\x20:value=\x22priority\x22\x20@input=\x22priority\x20=\x20Number($el.value)\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20Row\x202:\x20tags\x20+\x20idempotency\x20key\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:10px;\x22>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22><label>Tags</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20placeholder=\x22comma\x20separated\x22\x20:value=\x22tags\x22\x20@input=\x22tags\x20=\x20$el.value\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22><label>Idempotency\x20Key</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20placeholder=\x22optional\x20—\x20prevents\x20duplicate\x20jobs\x22\x20:value=\x22idempotencyKey\x22\x20@input=\x22idempotencyKey\x20=\x20$el.value\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20Buckets\x20-->\x0a\x20\x20<div\x20class=\x22field\x22>\x0a\x20\x20\x20\x20<label>Buckets\x20<span\x20style=\x22opacity:.4;\x20font-weight:400;\x20font-size:11px;\x22>(rate\x20limiting)</span></label>\x0a\x20\x20\x20\x20<each\x20:of=\x22buckets\x22\x20let:item=\x22b\x22\x20let:index=\x22i\x22>\x0a\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20gap:6px;\x20align-items:center;\x20margin-bottom:4px;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<select\x20style=\x22flex:1;\x22\x20@change=\x22onBucketSelect(i,\x20$el.value)\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<option\x20value=\x22\x22>—\x20none\x20—</option>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<option\x20as=\x22repeat\x22\x20:of=\x22bucketOptions\x22\x20let:item=\x22opt\x22\x20:value=\x22opt.id\x22\x20:selected=\x22opt.id\x20===\x20b.id\x22>{{\x20opt.id\x20}}</option>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<option\x20value=\x22__create__\x22>+\x20Create\x20bucket…</option>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</select>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<input\x20style=\x22width:72px;\x22\x20type=\x22number\x22\x20min=\x221\x22\x20placeholder=\x22tokens\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20:value=\x22b.tokens\x22\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20@input=\x22setBucketTokens(i,\x20$el.value)\x22\x20/>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<button\x20class=\x22btn-ghost\x22\x20@click=\x22removeBucket(i)\x22\x20title=\x22Remove\x22>✕</button>\x0a\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20</each>\x0a\x20\x20\x20\x20<button\x20class=\x22btn-ghost\x20btn-ghost--sm\x22\x20@click=\x22addBucket()\x22\x20style=\x22margin-top:2px;\x22>+\x20Add\x20bucket</button>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20Row\x203:\x20when\x20+\x20max\x20tries\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:8px;\x20align-items:flex-end;\x22>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22>\x0a\x20\x20\x20\x20\x20\x20<label>When</label>\x0a\x20\x20\x20\x20\x20\x20<div\x20style=\x22display:flex;\x20gap:4px;\x20align-items:center;\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<button\x20:class=\x22whenMode===\x27asap\x27\x20?\x20\x27mode-btn\x20mode-btn--on\x27\x20:\x20\x27mode-btn\x27\x22\x20@click=\x22whenMode=\x27asap\x27\x22>ASAP</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<button\x20:class=\x22whenMode===\x27datetime\x27\x20?\x20\x27mode-btn\x20mode-btn--on\x27\x20:\x20\x27mode-btn\x27\x22\x20@click=\x22whenMode=\x27datetime\x27\x22>Date</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<button\x20:class=\x22whenMode===\x27timestamp\x27\x20?\x20\x27mode-btn\x20mode-btn--on\x27\x20:\x20\x27mode-btn\x27\x22\x20@click=\x22whenMode=\x27timestamp\x27\x22>TS</button>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22whenMode\x20===\x20\x27datetime\x27\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<input\x20type=\x22datetime-local\x22\x20style=\x22flex:1;\x22\x20:value=\x22whenDatetime\x22\x20@input=\x22whenDatetime\x20=\x20$el.value\x22\x20/>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22whenMode\x20===\x20\x27timestamp\x27\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20style=\x22flex:1;\x22\x20placeholder=\x22ms\x20timestamp\x22\x20:value=\x22whenTs\x22\x20@input=\x22whenTs\x20=\x20$el.value\x20===\x20\x27\x27\x20?\x20null\x20:\x20Number($el.value)\x22\x20/>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20\x20\x20<if\x20:=\x22whenMode\x20===\x20\x27asap\x27\x22>\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span\x20style=\x22opacity:.5;\x20font-size:12px;\x20padding:0\x208px;\x22>Run\x20immediately</span>\x0a\x20\x20\x20\x20\x20\x20\x20\x20</if>\x0a\x20\x20\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22width:120px;\x22><label>Max\x20tries</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20placeholder=\x22unlimited\x22\x20:value=\x22maxTries\x22\x20@input=\x22maxTries\x20=\x20$el.value\x20===\x20\x27\x27\x20?\x20null\x20:\x20Number($el.value)\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20Row\x204:\x20retry\x20delay\x20+\x20backoff\x20+\x20cron\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:8px;\x20align-items:flex-end;\x22>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22><label>Retry\x20delay\x20(ms)</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20placeholder=\x220\x22\x20:value=\x22retryDelay\x22\x20@input=\x22retryDelay\x20=\x20Number($el.value)\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22width:120px;\x22><label>Backoff\x20multiplier</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20type=\x22number\x22\x20step=\x220.1\x22\x20placeholder=\x221\x22\x20:value=\x22backoff\x22\x20@input=\x22backoff\x20=\x20Number($el.value)\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22><label>Cron</label>\x0a\x20\x20\x20\x20\x20\x20<ok-cron-input\x20:value=\x22cron\x22\x20placeholder=\x22optional\x22\x20@change=\x22cron\x20=\x20$event.detail\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20Row\x205:\x20progress\x20+\x20status\x20message\x20(edit-specific)\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:8px;\x20align-items:flex-end;\x22>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22><label>Progress</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20placeholder=\x22—\x22\x20:value=\x22progress\x22\x20@input=\x22progress\x20=\x20$el.value\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20\x20\x20<div\x20class=\x22field\x22\x20style=\x22flex:1;\x22><label>Status\x20message</label>\x0a\x20\x20\x20\x20\x20\x20<input\x20placeholder=\x22—\x22\x20:value=\x22statusMessage\x22\x20@input=\x22statusMessage\x20=\x20$el.value\x22\x20/>\x0a\x20\x20\x20\x20</div>\x0a\x20\x20</div>\x0a\x0a\x20\x20<!--\x20Payload\x20-->\x0a\x20\x20<div\x20style=\x22display:flex;\x20flex-direction:column;\x20gap:6px;\x22>\x0a\x20\x20\x20\x20<span\x20style=\x22opacity:.7;\x20font-size:12px;\x22>Payload</span>\x0a\x20\x20\x20\x20<ok-json-input\x0a\x20\x20\x20\x20\x20\x20mode=\x22textarea\x22\x0a\x20\x20\x20\x20\x20\x20style=\x22height:260px;\x22\x0a\x20\x20\x20\x20\x20\x20:value=\x22payloadInit\x22\x0a\x20\x20\x20\x20\x20\x20@edit=\x22payload\x20=\x20$event.detail\x22\x0a\x20\x20\x20\x20/>\x0a\x20\x20</div>\x0a\x0a\x20\x20<div\x20style=\x22display:flex;\x20gap:10px;\x20justify-content:flex-end;\x22>\x0a\x20\x20\x20\x20<button\x20@click=\x22cancel()\x22>Cancel</button>\x0a\x20\x20\x20\x20<button\x20@click=\x22save()\x22\x20:disabled=\x22busy\x20||\x20loading\x20||\x20!type\x22>Save</button>\x0a\x20\x20</div>\x0a</div>\x0a','buckets','/update'];_0x28f1=function(){return _0x2d8a59;};return _0x28f1();}import _0x51a8f3 from'../../../stores/okdb-store.js';export default{'tag':'edit-job-modal','dependencies':[_0x161b4c,_0x319d9c,_0x126164,_0x356c2c,_0x385b33],'attr':{'modal':{},'jobId':{},'onDone':{}},'context':_0x1c2c2d=>({'busy':!0x1,'loading':!0x1,'job':null,'type':'','payloadInit':{},'payload':{},'priority':0x0,'maxTries':null,'retryDelay':0x0,'backoff':0x1,'tags':'','buckets':[],'cron':'','idempotencyKey':'','whenMode':_0x370ce6(0x6d),'whenDatetime':'','whenTs':null,'progress':'','statusMessage':'','bucketOptions':[],async 'loadBuckets'(){const _0x5befb8=_0x370ce6;try{const _0x3a195b=await fetch(_0x51a8f3[_0x5befb8(0x7e)]+_0x5befb8(0xb1)),_0x55bf57=await _0x3a195b[_0x5befb8(0xaf)]();this[_0x5befb8(0x9f)]=_0x55bf57?.['data']??[];}catch{this['bucketOptions']=[];}},async 'onBucketSelect'(_0xfcc37c,_0x2d6531){const _0x10314e=_0x370ce6;if(_0x10314e(0xa9)===_0x2d6531){const _0x1477e1=this;await _0x1c2c2d[_0x10314e(0x7b)]['shared'][_0x10314e(0x9e)][_0x10314e(0x7d)]({'owner':_0x1c2c2d,'title':'Add\x20Token\x20Bucket','content':_0x10314e(0xad),'context':_0x1a3e23=>({'modal':_0x1a3e23,'onDone':async _0x30c57b=>{const _0x445192=_0x10314e;await _0x1477e1[_0x445192(0x77)](),_0x1477e1['buckets']=_0x1477e1[_0x445192(0xb4)][_0x445192(0xb8)]((_0x3c9d81,_0x198a86)=>_0x198a86===_0xfcc37c?{..._0x3c9d81,'id':_0x30c57b}:_0x3c9d81);}})});}else this[_0x10314e(0xb4)]=this[_0x10314e(0xb4)]['map']((_0x30dea9,_0x33637a)=>_0x33637a===_0xfcc37c?{..._0x30dea9,'id':_0x2d6531}:_0x30dea9);},'setBucketTokens'(_0x1c6692,_0x45413e){const _0x132351=_0x370ce6,_0x6f42cc=''===_0x45413e?0x1:Math[_0x132351(0x8e)](0x1,parseInt(_0x45413e)||0x1);this[_0x132351(0xb4)]=this[_0x132351(0xb4)][_0x132351(0xb8)]((_0x2c833e,_0x491b2e)=>_0x491b2e===_0x1c6692?{..._0x2c833e,'tokens':_0x6f42cc}:_0x2c833e);},'addBucket'(){const _0x2fe541=_0x370ce6;this[_0x2fe541(0xb4)]=[...this[_0x2fe541(0xb4)],{'id':'','tokens':0x1}];},'removeBucket'(_0x50348d){const _0x4f2fb2=_0x370ce6;this['buckets']=this[_0x4f2fb2(0xb4)][_0x4f2fb2(0x75)]((_0x584824,_0x17a9be)=>_0x17a9be!==_0x50348d);},'getWhen'(){const _0x34b1cd=_0x370ce6;if('asap'===this['whenMode'])return null;if(_0x34b1cd(0xaa)===this['whenMode']){const _0x442281=new Date(this[_0x34b1cd(0x93)]);return isNaN(_0x442281[_0x34b1cd(0x7a)]())?null:_0x442281[_0x34b1cd(0x7a)]();}return this[_0x34b1cd(0xac)];},'_parseTags'(_0x3a2733){const _0x4139a0=_0x370ce6;if(!_0x3a2733)return;const _0x4c155d=String(_0x3a2733)[_0x4139a0(0x8a)](',')[_0x4139a0(0xb8)](_0xe6bc5a=>_0xe6bc5a[_0x4139a0(0x85)]())['filter'](Boolean);return _0x4c155d[_0x4139a0(0x83)]?_0x4c155d:void 0x0;},'applyJob'(_0x3eaa6c){const _0x599a75=_0x370ce6;_0x3eaa6c&&(this[_0x599a75(0x82)]=_0x3eaa6c[_0x599a75(0x82)]??'',this[_0x599a75(0x71)]=_0x3eaa6c[_0x599a75(0x74)]??{},this[_0x599a75(0x74)]=_0x3eaa6c['payload']??{},this[_0x599a75(0x8f)]=_0x3eaa6c['priority']??0x0,this['maxTries']=_0x3eaa6c[_0x599a75(0x86)]??null,this[_0x599a75(0x70)]=_0x3eaa6c['retry_delay']??0x0,this[_0x599a75(0x7c)]=_0x3eaa6c[_0x599a75(0xab)]??0x1,this[_0x599a75(0x8b)]=Array[_0x599a75(0x81)](_0x3eaa6c[_0x599a75(0x8b)])?_0x3eaa6c[_0x599a75(0x8b)][_0x599a75(0x96)](',\x20'):'',this[_0x599a75(0xae)]=_0x3eaa6c[_0x599a75(0xae)]??'',this[_0x599a75(0x9c)]=_0x3eaa6c?.['idempotency_key']||'',this[_0x599a75(0x94)]=_0x3eaa6c['progress']??'',this['statusMessage']=_0x3eaa6c['status_message']??'',_0x3eaa6c['when']&&_0x3eaa6c[_0x599a75(0x95)]>Date['now']()-0x3e8?(this[_0x599a75(0x6c)]=_0x599a75(0x6f),this[_0x599a75(0x93)]=new Date(_0x3eaa6c[_0x599a75(0x95)])['toISOString']()['slice'](0x0,0x10),this[_0x599a75(0xac)]=_0x3eaa6c['when']):(this['whenMode']='asap',this['whenDatetime']='',this['whenTs']=null),Array[_0x599a75(0x81)](_0x3eaa6c[_0x599a75(0xb4)])&&_0x3eaa6c['buckets'][_0x599a75(0x83)]>0x0?this['buckets']=_0x3eaa6c[_0x599a75(0xb4)][_0x599a75(0xb8)](_0x4e77b0=>({'id':_0x4e77b0['id'],'tokens':_0x4e77b0[_0x599a75(0xa3)]??0x1})):_0x3eaa6c[_0x599a75(0x6b)]?this[_0x599a75(0xb4)]=[{'id':_0x3eaa6c[_0x599a75(0x6b)],'tokens':_0x3eaa6c[_0x599a75(0x91)]??0x1}]:this[_0x599a75(0xb4)]=[]);},async 'load'(){const _0x31915c=_0x370ce6;this[_0x31915c(0x88)]=!0x0;try{const _0x31b46f=await fetch(_0x51a8f3[_0x31915c(0x7e)]+_0x31915c(0x80)+encodeURIComponent(_0x1c2c2d['$attr']['jobId'])),_0x24684a=await _0x31b46f['json']();if(!_0x31b46f['ok'])throw new Error(_0x24684a?.['error']?.[_0x31915c(0x78)]||_0x31915c(0xa4));this['job']=_0x24684a?.[_0x31915c(0x92)]??null,this[_0x31915c(0x9d)](this[_0x31915c(0x79)]);}catch(_0x19261f){_0x1c2c2d[_0x31915c(0x7b)]['shared']?.[_0x31915c(0xa1)]?.[_0x31915c(0xa7)]?.(_0x19261f?.[_0x31915c(0x78)]||_0x31915c(0xa4)),await _0x1c2c2d[_0x31915c(0x87)]['modal'][_0x31915c(0x7f)]();}finally{this['loading']=!0x1;}},async 'save'(){const _0x52b483=_0x370ce6;if(this[_0x52b483(0x79)]){this[_0x52b483(0xb6)]=!0x0;try{const _0x296b20=this['getWhen'](),_0x479fe9=String(this[_0x52b483(0xae)]||'')[_0x52b483(0x85)]()||null,_0x10de61=this[_0x52b483(0xb4)][_0x52b483(0x75)](_0x544e99=>_0x544e99['id'])[_0x52b483(0xb8)](_0x4ddf53=>({'id':_0x4ddf53['id'],'tokens':_0x4ddf53['tokens']||0x1})),_0x349f20=this['_parseTags'](this['tags']),_0x5640ad={'type':String(this[_0x52b483(0x82)]||'')['trim'](),'payload':this[_0x52b483(0x74)],'when':_0x296b20,'priority':Number(this['priority'])||0x0,'tags':_0x349f20??this[_0x52b483(0x79)]['tags'],'buckets':_0x10de61,'cron':_0x479fe9,'max_tries':null!==this[_0x52b483(0x90)]&&''!==this['maxTries']?Number(this[_0x52b483(0x90)]):null,'retry_delay':Number(this[_0x52b483(0x70)])||0x0,'backoff_multiplier':Number(this[_0x52b483(0x7c)])||0x1,'progress':this[_0x52b483(0x94)]||null,'status_message':this['statusMessage']||null,'idempotency_key':this[_0x52b483(0x9c)][_0x52b483(0x85)]()||null},_0x56bf48=await fetch(_0x51a8f3[_0x52b483(0x7e)]+'/queue/job/'+encodeURIComponent(this[_0x52b483(0x79)]['id'])+_0x52b483(0xb5),{'method':_0x52b483(0x8c),'headers':{'Content-Type':_0x52b483(0x98)},'body':JSON['stringify'](_0x5640ad)}),_0x3ffab5=await _0x56bf48['json']();if(!_0x56bf48['ok'])throw new Error(_0x3ffab5?.[_0x52b483(0xa7)]?.['message']||_0x52b483(0xb7));_0x1c2c2d[_0x52b483(0x7b)][_0x52b483(0x6e)]?.['toast']?.[_0x52b483(0x73)]?.(_0x52b483(0x99)),await _0x1c2c2d[_0x52b483(0x87)][_0x52b483(0x9e)][_0x52b483(0x7f)](),await _0x1c2c2d[_0x52b483(0x87)][_0x52b483(0xa2)]?.(_0x3ffab5?.[_0x52b483(0x92)]??null);}catch(_0x49efd0){_0x1c2c2d[_0x52b483(0x7b)][_0x52b483(0x6e)]?.[_0x52b483(0xa1)]?.[_0x52b483(0xa7)]?.(_0x49efd0?.[_0x52b483(0x78)]||'Failed\x20to\x20save\x20job');}finally{this[_0x52b483(0xb6)]=!0x1;}}},'cancel'(){const _0x391309=_0x370ce6;_0x1c2c2d[_0x391309(0x87)][_0x391309(0x9e)][_0x391309(0x7f)]();}}),async 'mount'(){const _0x195601=_0x370ce6;await Promise[_0x195601(0xa0)]([this['load'](),this[_0x195601(0x77)]()]);},'template':_0x370ce6(0xb3),'style':_0x370ce6(0xb2)};
1
+ import e from"@toolkit/elements/ok-icon.ok.js";import t from"@toolkit/core/ok-overlay.ok.js";import n from"../../../components/ok-cron-input.ok.js";import a from"../../../components/ok-json-input.ok.js";import i from"./create-bucket-modal.ok.js";import s from"../../../stores/okdb-store.js";export default{tag:"edit-job-modal",dependencies:[n,a,i,t,e],attr:{modal:{},jobId:{},onDone:{}},context:e=>({busy:!1,loading:!1,job:null,type:"",payloadInit:{},payload:{},priority:0,maxTries:null,retryDelay:0,backoff:1,tags:"",buckets:[],cron:"",idempotencyKey:"",whenMode:"asap",whenDatetime:"",whenTs:null,progress:"",statusMessage:"",bucketOptions:[],async loadBuckets(){try{const e=await fetch(s.apiBase+"/queue/buckets"),t=await e.json();this.bucketOptions=t?.data??[]}catch{this.bucketOptions=[]}},async onBucketSelect(t,n){if("__create__"===n){const n=this;await e.$ok.shared.modal.open({owner:e,title:"Add Token Bucket",content:'<create-bucket-modal :modal="modal" :on-done="onDone" />',context:e=>({modal:e,onDone:async e=>{await n.loadBuckets(),n.buckets=n.buckets.map((n,a)=>a===t?{...n,id:e}:n)}})})}else this.buckets=this.buckets.map((e,a)=>a===t?{...e,id:n}:e)},setBucketTokens(e,t){const n=""===t?1:Math.max(1,parseInt(t)||1);this.buckets=this.buckets.map((t,a)=>a===e?{...t,tokens:n}:t)},addBucket(){this.buckets=[...this.buckets,{id:"",tokens:1}]},removeBucket(e){this.buckets=this.buckets.filter((t,n)=>n!==e)},getWhen(){if("asap"===this.whenMode)return null;if("datetime"===this.whenMode){const e=new Date(this.whenDatetime);return isNaN(e.getTime())?null:e.getTime()}return this.whenTs},_parseTags(e){if(!e)return;const t=String(e).split(",").map(e=>e.trim()).filter(Boolean);return t.length?t:void 0},applyJob(e){e&&(this.type=e.type??"",this.payloadInit=e.payload??{},this.payload=e.payload??{},this.priority=e.priority??0,this.maxTries=e.max_tries??null,this.retryDelay=e.retry_delay??0,this.backoff=e.backoff_multiplier??1,this.tags=Array.isArray(e.tags)?e.tags.join(", "):"",this.cron=e.cron??"",this.idempotencyKey=e?.idempotency_key||"",this.progress=e.progress??"",this.statusMessage=e.status_message??"",e.when&&e.when>Date.now()-1e3?(this.whenMode="timestamp",this.whenDatetime=new Date(e.when).toISOString().slice(0,16),this.whenTs=e.when):(this.whenMode="asap",this.whenDatetime="",this.whenTs=null),Array.isArray(e.buckets)&&e.buckets.length>0?this.buckets=e.buckets.map(e=>({id:e.id,tokens:e.tokens??1})):e.bucket?this.buckets=[{id:e.bucket,tokens:e.bucket_tokens??1}]:this.buckets=[])},async load(){this.loading=!0;try{const t=await fetch(s.apiBase+"/queue/job/"+encodeURIComponent(e.$attr.jobId)),n=await t.json();if(!t.ok)throw new Error(n?.error?.message||"Failed to load job");this.job=n?.data??null,this.applyJob(this.job)}catch(t){e.$ok.shared?.toast?.error?.(t?.message||"Failed to load job"),await e.$attr.modal.close()}finally{this.loading=!1}},async save(){if(this.job){this.busy=!0;try{const t=this.getWhen(),n=String(this.cron||"").trim()||null,a=this.buckets.filter(e=>e.id).map(e=>({id:e.id,tokens:e.tokens||1})),i=this._parseTags(this.tags),l={type:String(this.type||"").trim(),payload:this.payload,when:t,priority:Number(this.priority)||0,tags:i??this.job.tags,buckets:a,cron:n,max_tries:null!==this.maxTries&&""!==this.maxTries?Number(this.maxTries):null,retry_delay:Number(this.retryDelay)||0,backoff_multiplier:Number(this.backoff)||1,progress:this.progress||null,status_message:this.statusMessage||null,idempotency_key:this.idempotencyKey.trim()||null},o=await fetch(s.apiBase+"/queue/job/"+encodeURIComponent(this.job.id)+"/update",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(l)}),d=await o.json();if(!o.ok)throw new Error(d?.error?.message||"Failed to save job");e.$ok.shared?.toast?.success?.("Job updated"),await e.$attr.modal.close(),await(e.$attr.onDone?.(d?.data??null))}catch(t){e.$ok.shared?.toast?.error?.(t?.message||"Failed to save job")}finally{this.busy=!1}}},cancel(){e.$attr.modal.close()}}),async mount(){await Promise.all([this.load(),this.loadBuckets()])},template:'\n<div style="position:relative; min-width:720px; max-width:calc(100vw - 80px); display:flex; flex-direction:column; gap:12px;">\n <if :="busy || loading">\n <ok-overlay>\n <div class="loader-center">\n <ok-icon name="loader" class="spin" style="width:32px;height:32px;"></ok-icon>\n </div>\n </ok-overlay>\n </if>\n\n <div style="display:flex; gap:8px; align-items:center; flex-wrap:wrap; opacity:.8; font-size:12px;">\n <span>ID: <b>{{ job?.id }}</b></span>\n <span>Status: <b>{{ job?.status }}</b></span>\n <span>Tries: <b>{{ job?.tries }} / {{ job?.max_tries }}</b></span>\n </div>\n\n \x3c!-- Row 1: type + priority --\x3e\n <div style="display:flex; gap:8px; align-items:flex-end;">\n <div class="field" style="flex:1;"><label>Type</label>\n <input placeholder="e.g. email.send" :value="type" @input="type = $el.value" />\n </div>\n <div class="field" style="width:120px;"><label>Priority</label>\n <input type="number" placeholder="0" :value="priority" @input="priority = Number($el.value)" />\n </div>\n </div>\n\n \x3c!-- Row 2: tags + idempotency key --\x3e\n <div style="display:flex; gap:10px;">\n <div class="field" style="flex:1;"><label>Tags</label>\n <input placeholder="comma separated" :value="tags" @input="tags = $el.value" />\n </div>\n <div class="field" style="flex:1;"><label>Idempotency Key</label>\n <input placeholder="optional — prevents duplicate jobs" :value="idempotencyKey" @input="idempotencyKey = $el.value" />\n </div>\n </div>\n\n \x3c!-- Buckets --\x3e\n <div class="field">\n <label>Buckets <span style="opacity:.4; font-weight:400; font-size:11px;">(rate limiting)</span></label>\n <each :of="buckets" let:item="b" let:index="i">\n <div style="display:flex; gap:6px; align-items:center; margin-bottom:4px;">\n <select style="flex:1;" @change="onBucketSelect(i, $el.value)">\n <option value="">— none —</option>\n <option as="repeat" :of="bucketOptions" let:item="opt" :value="opt.id" :selected="opt.id === b.id">{{ opt.id }}</option>\n <option value="__create__">+ Create bucket…</option>\n </select>\n <input style="width:72px;" type="number" min="1" placeholder="tokens"\n :value="b.tokens"\n @input="setBucketTokens(i, $el.value)" />\n <button class="btn-ghost" @click="removeBucket(i)" title="Remove">✕</button>\n </div>\n </each>\n <button class="btn-ghost btn-ghost--sm" @click="addBucket()" style="margin-top:2px;">+ Add bucket</button>\n </div>\n\n \x3c!-- Row 3: when + max tries --\x3e\n <div style="display:flex; gap:8px; align-items:flex-end;">\n <div class="field" style="flex:1;">\n <label>When</label>\n <div style="display:flex; gap:4px; align-items:center;">\n <button :class="whenMode===\'asap\' ? \'mode-btn mode-btn--on\' : \'mode-btn\'" @click="whenMode=\'asap\'">ASAP</button>\n <button :class="whenMode===\'datetime\' ? \'mode-btn mode-btn--on\' : \'mode-btn\'" @click="whenMode=\'datetime\'">Date</button>\n <button :class="whenMode===\'timestamp\' ? \'mode-btn mode-btn--on\' : \'mode-btn\'" @click="whenMode=\'timestamp\'">TS</button>\n <if :="whenMode === \'datetime\'">\n <input type="datetime-local" style="flex:1;" :value="whenDatetime" @input="whenDatetime = $el.value" />\n </if>\n <if :="whenMode === \'timestamp\'">\n <input type="number" style="flex:1;" placeholder="ms timestamp" :value="whenTs" @input="whenTs = $el.value === \'\' ? null : Number($el.value)" />\n </if>\n <if :="whenMode === \'asap\'">\n <span style="opacity:.5; font-size:12px; padding:0 8px;">Run immediately</span>\n </if>\n </div>\n </div>\n <div class="field" style="width:120px;"><label>Max tries</label>\n <input type="number" placeholder="unlimited" :value="maxTries" @input="maxTries = $el.value === \'\' ? null : Number($el.value)" />\n </div>\n </div>\n\n \x3c!-- Row 4: retry delay + backoff + cron --\x3e\n <div style="display:flex; gap:8px; align-items:flex-end;">\n <div class="field" style="flex:1;"><label>Retry delay (ms)</label>\n <input type="number" placeholder="0" :value="retryDelay" @input="retryDelay = Number($el.value)" />\n </div>\n <div class="field" style="width:120px;"><label>Backoff multiplier</label>\n <input type="number" step="0.1" placeholder="1" :value="backoff" @input="backoff = Number($el.value)" />\n </div>\n <div class="field" style="flex:1;"><label>Cron</label>\n <ok-cron-input :value="cron" placeholder="optional" @change="cron = $event.detail" />\n </div>\n </div>\n\n \x3c!-- Row 5: progress + status message (edit-specific) --\x3e\n <div style="display:flex; gap:8px; align-items:flex-end;">\n <div class="field" style="flex:1;"><label>Progress</label>\n <input placeholder="—" :value="progress" @input="progress = $el.value" />\n </div>\n <div class="field" style="flex:1;"><label>Status message</label>\n <input placeholder="—" :value="statusMessage" @input="statusMessage = $el.value" />\n </div>\n </div>\n\n \x3c!-- Payload --\x3e\n <div style="display:flex; flex-direction:column; gap:6px;">\n <span style="opacity:.7; font-size:12px;">Payload</span>\n <ok-json-input\n mode="textarea"\n style="height:260px;"\n :value="payloadInit"\n @edit="payload = $event.detail"\n />\n </div>\n\n <div style="display:flex; gap:10px; justify-content:flex-end;">\n <button @click="cancel()">Cancel</button>\n <button @click="save()" :disabled="busy || loading || !type">Save</button>\n </div>\n</div>\n',style:".mode-btn{font-size:11px;padding:2px 8px;opacity:.6;border-radius:4px}.mode-btn--on{opacity:1;background:color-mix(in srgb,var(--okdb-accent) 13%,transparent);border-color:color-mix(in srgb,var(--okdb-accent) 33%,transparent);color:var(--okdb-accent)}.btn-ghost{background:0 0;border:1px solid var(--okdb-app-border);border-radius:5px;color:inherit;cursor:pointer;opacity:.6;padding:2px 8px;font-size:12px}.btn-ghost:hover{opacity:1;border-color:color-mix(in srgb,var(--okdb-accent) 33%,transparent)}.btn-ghost--sm{font-size:11px;padding:1px 7px}"};