web-mojo 2.3.2 → 2.3.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.
- package/CHANGELOG.md +169 -5
- package/dist/admin-models.cjs.js +1 -1
- package/dist/admin-models.es.js +1 -1
- package/dist/admin.cjs.js +1 -1
- package/dist/admin.es.js +1 -1
- package/dist/auth.cjs.js +1 -1
- package/dist/auth.es.js +1 -1
- package/dist/charts.cjs.js +1 -1
- package/dist/charts.es.js +1 -1
- package/dist/chunks/{AssistantPanelView-72i9d6rq.js → AssistantPanelView-4cfhmJvL.js} +2 -2
- package/dist/chunks/{AssistantPanelView-72i9d6rq.js.map → AssistantPanelView-4cfhmJvL.js.map} +1 -1
- package/dist/chunks/{AssistantPanelView-D-0f5hZS.js → AssistantPanelView-DxE86Clm.js} +2 -2
- package/dist/chunks/{AssistantPanelView-D-0f5hZS.js.map → AssistantPanelView-DxE86Clm.js.map} +1 -1
- package/dist/chunks/{ChatView-ys5MIiBf.js → ChatView-B-2JVhM3.js} +2 -2
- package/dist/chunks/{ChatView-ys5MIiBf.js.map → ChatView-B-2JVhM3.js.map} +1 -1
- package/dist/chunks/{ChatView-CXykdAV1.js → ChatView-oZ9FggEo.js} +2 -2
- package/dist/chunks/{ChatView-CXykdAV1.js.map → ChatView-oZ9FggEo.js.map} +1 -1
- package/dist/chunks/{Collection-CY6BblFn.js → Collection-BtSHP_BV.js} +2 -2
- package/dist/chunks/{Collection-CY6BblFn.js.map → Collection-BtSHP_BV.js.map} +1 -1
- package/dist/chunks/{Collection-BpUmNuDZ.js → Collection-CtSGTegm.js} +2 -2
- package/dist/chunks/{Collection-BpUmNuDZ.js.map → Collection-CtSGTegm.js.map} +1 -1
- package/dist/chunks/{ContextMenu-XQVFU0mL.js → ContextMenu-0KtfqyQm.js} +2 -2
- package/dist/chunks/{ContextMenu-XQVFU0mL.js.map → ContextMenu-0KtfqyQm.js.map} +1 -1
- package/dist/chunks/{ContextMenu-PtN50qH2.js → ContextMenu-DvQTJA49.js} +2 -2
- package/dist/chunks/{ContextMenu-PtN50qH2.js.map → ContextMenu-DvQTJA49.js.map} +1 -1
- package/dist/chunks/{DataView-BTi_BZHx.js → DataView-DJMOmlKN.js} +2 -2
- package/dist/chunks/{DataView-BTi_BZHx.js.map → DataView-DJMOmlKN.js.map} +1 -1
- package/dist/chunks/{DataView-WHRh1o6E.js → DataView-jHxX-6Gh.js} +2 -2
- package/dist/chunks/{DataView-WHRh1o6E.js.map → DataView-jHxX-6Gh.js.map} +1 -1
- package/dist/chunks/{FormView-DTD-Zy22.js → FormView-C_7xQuI4.js} +3 -3
- package/dist/chunks/{FormView-DTD-Zy22.js.map → FormView-C_7xQuI4.js.map} +1 -1
- package/dist/chunks/{FormView-BzaGMf5_.js → FormView-D8YrH2V4.js} +3 -3
- package/dist/chunks/{FormView-BzaGMf5_.js.map → FormView-D8YrH2V4.js.map} +1 -1
- package/dist/chunks/{ListView-BsnnTcmC.js → ListView-CjJ-4gD7.js} +2 -2
- package/dist/chunks/{ListView-BsnnTcmC.js.map → ListView-CjJ-4gD7.js.map} +1 -1
- package/dist/chunks/{ListView-Xf7kO6Me.js → ListView-mr1Kmuj-.js} +2 -2
- package/dist/chunks/{ListView-Xf7kO6Me.js.map → ListView-mr1Kmuj-.js.map} +1 -1
- package/dist/chunks/{MetricsCountryMapView-BzGOi1d2.js → MetricsCountryMapView-BIsfmvE0.js} +2 -2
- package/dist/chunks/{MetricsCountryMapView-BzGOi1d2.js.map → MetricsCountryMapView-BIsfmvE0.js.map} +1 -1
- package/dist/chunks/{MetricsCountryMapView-C-S00Wiw.js → MetricsCountryMapView-D9J4Gwdi.js} +2 -2
- package/dist/chunks/{MetricsCountryMapView-C-S00Wiw.js.map → MetricsCountryMapView-D9J4Gwdi.js.map} +1 -1
- package/dist/chunks/Modal-DF98u_sN.js +3 -0
- package/dist/chunks/Modal-DF98u_sN.js.map +1 -0
- package/dist/chunks/Modal-DYJadSN8.js +3 -0
- package/dist/chunks/Modal-DYJadSN8.js.map +1 -0
- package/dist/chunks/Passkeys-8ko7Rg8G.js +2 -0
- package/dist/chunks/Passkeys-8ko7Rg8G.js.map +1 -0
- package/dist/chunks/Passkeys-CD8RKUl8.js +2 -0
- package/dist/chunks/Passkeys-CD8RKUl8.js.map +1 -0
- package/dist/chunks/{TokenManager-C6aXkRaI.js → TokenManager-CkZQ8Ep5.js} +2 -2
- package/dist/chunks/{TokenManager-C6aXkRaI.js.map → TokenManager-CkZQ8Ep5.js.map} +1 -1
- package/dist/chunks/{TokenManager-DgvhhTqN.js → TokenManager-SXKHJseu.js} +2 -2
- package/dist/chunks/{TokenManager-DgvhhTqN.js.map → TokenManager-SXKHJseu.js.map} +1 -1
- package/dist/chunks/{User-KTBU_5cr.js → User-C6Tbn6vZ.js} +2 -2
- package/dist/chunks/User-C6Tbn6vZ.js.map +1 -0
- package/dist/chunks/{User-B_Urf7U7.js → User-DRbw-wOB.js} +2 -2
- package/dist/chunks/User-DRbw-wOB.js.map +1 -0
- package/dist/chunks/{UserProfileView-BmduMJ86.js → UserProfileView-DC70hRer.js} +2 -2
- package/dist/chunks/{UserProfileView-COxSyPB0.js.map → UserProfileView-DC70hRer.js.map} +1 -1
- package/dist/chunks/{UserProfileView-COxSyPB0.js → UserProfileView-TQ9BqUE2.js} +2 -2
- package/dist/chunks/{UserProfileView-BmduMJ86.js.map → UserProfileView-TQ9BqUE2.js.map} +1 -1
- package/dist/chunks/{View-C8UWvaSM.js → View-BWOE7WJm.js} +2 -2
- package/dist/chunks/{View-C8UWvaSM.js.map → View-BWOE7WJm.js.map} +1 -1
- package/dist/chunks/{View-Cvs2TY7b.js → View-D6Ug7M6k.js} +2 -2
- package/dist/chunks/{View-Cvs2TY7b.js.map → View-D6Ug7M6k.js.map} +1 -1
- package/dist/chunks/{WebApp-kbRq7dM_.js → WebApp-By80XfTK.js} +2 -2
- package/dist/chunks/{WebApp-kbRq7dM_.js.map → WebApp-By80XfTK.js.map} +1 -1
- package/dist/chunks/{WebApp-DuwanN2O.js → WebApp-CLTFSbto.js} +2 -2
- package/dist/chunks/{WebApp-DuwanN2O.js.map → WebApp-CLTFSbto.js.map} +1 -1
- package/dist/chunks/{admin-DtjMWe6R.js → admin-D7jNNoha.js} +2 -2
- package/dist/chunks/{admin-DtjMWe6R.js.map → admin-D7jNNoha.js.map} +1 -1
- package/dist/chunks/{admin-BcJ_hgzn.js → admin-OAZ54Mf3.js} +2 -2
- package/dist/chunks/{admin-BcJ_hgzn.js.map → admin-OAZ54Mf3.js.map} +1 -1
- package/dist/chunks/{exportChart-Dn2pioNl.js → exportChart-BQXkqsxe.js} +2 -2
- package/dist/chunks/{exportChart-Dn2pioNl.js.map → exportChart-BQXkqsxe.js.map} +1 -1
- package/dist/chunks/{exportChart-Bkxr7mCe.js → exportChart-UQ5nq_mR.js} +2 -2
- package/dist/chunks/{exportChart-Bkxr7mCe.js.map → exportChart-UQ5nq_mR.js.map} +1 -1
- package/dist/chunks/{index-CJeTVskY.js → index-C9wf7N-j.js} +2 -2
- package/dist/chunks/{index-CJeTVskY.js.map → index-C9wf7N-j.js.map} +1 -1
- package/dist/chunks/{index-BCWkcyOy.js → index-TqW1pAMX.js} +2 -2
- package/dist/chunks/{index-BCWkcyOy.js.map → index-TqW1pAMX.js.map} +1 -1
- package/dist/chunks/{version-DkxW4rIi.js → version-73ZSFn6Q.js} +2 -2
- package/dist/chunks/{version-DkxW4rIi.js.map → version-73ZSFn6Q.js.map} +1 -1
- package/dist/chunks/{version-CB0Ssm9c.js → version-Dn158saF.js} +2 -2
- package/dist/chunks/{version-CB0Ssm9c.js.map → version-Dn158saF.js.map} +1 -1
- package/dist/core.css +68 -209
- package/dist/css/web-mojo.css +1 -1
- package/dist/docit.cjs.js +1 -1
- package/dist/docit.es.js +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +1 -1
- package/dist/index.es.js.map +1 -1
- package/dist/lightbox.cjs.js +1 -1
- package/dist/lightbox.es.js +1 -1
- package/dist/map.cjs.js +1 -1
- package/dist/map.es.js +1 -1
- package/dist/timeline.cjs.js +1 -1
- package/dist/timeline.es.js +1 -1
- package/dist/user-profile.cjs.js +1 -1
- package/dist/user-profile.es.js +1 -1
- package/dist/web-mojo.lite.iife.js +95 -168
- package/dist/web-mojo.lite.iife.js.map +1 -1
- package/dist/web-mojo.lite.iife.min.js +98 -98
- package/dist/web-mojo.lite.iife.min.js.map +1 -1
- package/package.json +3 -1
- package/dist/chunks/Modal-GWjyfcz5.js +0 -3
- package/dist/chunks/Modal-GWjyfcz5.js.map +0 -1
- package/dist/chunks/Modal-wrfWfQhv.js +0 -3
- package/dist/chunks/Modal-wrfWfQhv.js.map +0 -1
- package/dist/chunks/Passkeys-BviQX3_5.js +0 -2
- package/dist/chunks/Passkeys-BviQX3_5.js.map +0 -1
- package/dist/chunks/Passkeys-gXR1Rc6C.js +0 -2
- package/dist/chunks/Passkeys-gXR1Rc6C.js.map +0 -1
- package/dist/chunks/User-B_Urf7U7.js.map +0 -1
- package/dist/chunks/User-KTBU_5cr.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,162 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
### Behavior — PortalApp: collapsed theme menu into a single `Theme settings` entry
|
|
6
|
+
|
|
7
|
+
- The auto-injected topbar usermenu used to add **three rows** (Theme:
|
|
8
|
+
Light / Theme: Dark / Theme: System) and re-render the topbar on
|
|
9
|
+
every preference change to keep the active mark in sync. It's now a
|
|
10
|
+
**single row** — `Theme settings` (icon `bi-palette`, action
|
|
11
|
+
`theme-settings`) — that opens a small dialog with the three radios
|
|
12
|
+
wired to `app.setTheme()`. The dialog is the same shape consumers
|
|
13
|
+
were already building by hand (see the previous example portal's
|
|
14
|
+
`openDisplaySettings()` helper).
|
|
15
|
+
- **New public method:** `app.showThemeSettings()` returns the
|
|
16
|
+
underlying `Modal.dialog` promise. Wired to the auto-injected
|
|
17
|
+
menu item, but also callable from any consumer hook (sidebar gear
|
|
18
|
+
icon, slash command, etc.).
|
|
19
|
+
- **Backward compat:** the legacy `theme-light` / `theme-dark` /
|
|
20
|
+
`theme-system` `portal:action` cases still work — apps that wired
|
|
21
|
+
their own buttons to those actions don't need to change. The
|
|
22
|
+
framework's auto-injected menu just no longer emits them.
|
|
23
|
+
- **Opt-out unchanged:** set `topbar.themeToggle: false` to skip the
|
|
24
|
+
auto-inject entirely.
|
|
25
|
+
- **Removed:** the private `_refreshThemeToggleActiveState()` helper
|
|
26
|
+
and the `theme:changed` topbar re-render listener (a single item
|
|
27
|
+
has no active mark to track).
|
|
28
|
+
- The example portal (`examples/portal/app.js`) was updated to remove
|
|
29
|
+
its own `Settings` userMenu row and `bi-sliders` topbar gear, both
|
|
30
|
+
of which duplicated the framework's auto-injected entry.
|
|
31
|
+
|
|
32
|
+
### Feature — Extensible `User` permission registry
|
|
33
|
+
|
|
34
|
+
Two related fixes that make the `User` permission registry behave correctly when apps extend it. Both bugs had the same root cause: framework-only state computed once at module load with no recompute path, so app-level mutations after import had no effect on the UI or the gate-checker.
|
|
35
|
+
|
|
36
|
+
**1. `User.registerCategoryMap()` — app categories implicitly satisfy app granular gates**
|
|
37
|
+
|
|
38
|
+
The user-permission gate already falls back from a granular permission to its parent *category* via `User.GRANULAR_TO_CATEGORY` — so a user holding `permissions.security` automatically satisfies a page gated `permissions: ["view_security"]`. But `GRANULAR_TO_CATEGORY` was built once from the framework-only `User.CATEGORY_GRANULAR_MAP` with no extension point, so app categories registered through `User.APP_CATEGORY_PERMISSIONS` could never satisfy app granular gates. Apps had to list both names in every gate array.
|
|
39
|
+
|
|
40
|
+
- **New** `User.registerCategoryMap(map)` — apps register their own category → granular relationships. Pass an object of the same shape as `CATEGORY_GRANULAR_MAP`:
|
|
41
|
+
```js
|
|
42
|
+
User.registerCategoryMap({ app_cat: ['view_app_thing', 'manage_app_thing'] });
|
|
43
|
+
```
|
|
44
|
+
Merges into `User.CATEGORY_GRANULAR_MAP` (extending existing categories without dropping prior entries) and triggers `rebuildPermissions()`. After registration, a user with `permissions.app_cat === true` passes a gate of `permissions: ["view_app_thing"]` automatically.
|
|
45
|
+
- No change to `User.hasPermission` / `_hasPermission` — they already consult `GRANULAR_TO_CATEGORY`.
|
|
46
|
+
- `Member.hasPermission` still does literal-only matching (no category fallback) — pre-existing, separate.
|
|
47
|
+
|
|
48
|
+
**2. `User.rebuildPermissions()` — UI picks up extended permission tabs and "App" tabset**
|
|
49
|
+
|
|
50
|
+
`User.PERMISSIONS`, `User.PERMISSION_FIELDS`, `User.CATEGORY_PERMISSION_FIELDS`, and `User.GRANULAR_PERMISSION_FIELDS` were computed by IIFEs at module-load time and never re-read their source arrays. Apps documented to extend `User.GRANULAR_PERMISSION_TABS.push(...)` / `User.APP_CATEGORY_PERMISSIONS.push(...)` / `User.CATEGORY_GRANULAR_MAP.x = [...]` after import saw no change in the rendered "Permissions" or "Adv Permissions" forms — the cached field arrays were already frozen.
|
|
51
|
+
|
|
52
|
+
- **New** `User.rebuildPermissions()` — recomputes every cached structure from the live source arrays. Idempotent. Apps call it once after their registry edits:
|
|
53
|
+
```js
|
|
54
|
+
User.GRANULAR_PERMISSION_TABS.push({
|
|
55
|
+
label: 'Custom',
|
|
56
|
+
permissions: [
|
|
57
|
+
{ name: 'view_app_thing', label: 'View App Thing' },
|
|
58
|
+
{ name: 'manage_app_thing', label: 'Manage App Thing' }
|
|
59
|
+
]
|
|
60
|
+
});
|
|
61
|
+
User.APP_CATEGORY_PERMISSIONS.push({ name: 'app_cat', label: 'App Category' });
|
|
62
|
+
User.CATEGORY_GRANULAR_MAP.app_cat = ['view_app_thing', 'manage_app_thing'];
|
|
63
|
+
|
|
64
|
+
User.rebuildPermissions();
|
|
65
|
+
```
|
|
66
|
+
- **Mutates caches in place** so existing references stay live. `UserForms.permissions.fields` (which captures `User.PERMISSION_FIELDS` at module-load) reflects post-rebuild updates without re-import.
|
|
67
|
+
- Replaces the previous IIFE-built initial state — there's now a single `User.rebuildPermissions()` call at the bottom of `User.js` instead. Initial behavior is identical to before.
|
|
68
|
+
- `User.registerCategoryMap()` calls `rebuildPermissions()` automatically; apps mutating `CATEGORY_GRANULAR_MAP` directly should call it themselves.
|
|
69
|
+
|
|
70
|
+
**3. `User.registerPermissions()` — atomic one-shot extension API**
|
|
71
|
+
|
|
72
|
+
Higher-level wrapper for apps that want to declare every extension in a single call rather than push to four separate arrays:
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
User.registerPermissions({
|
|
76
|
+
categories: [{ name: 'app_cat', label: 'App Category' }],
|
|
77
|
+
granularPermissions: [{ name: 'app_perm', label: 'App Perm' }],
|
|
78
|
+
granularTabs: [{
|
|
79
|
+
label: 'Custom',
|
|
80
|
+
permissions: [{ name: 'view_app_thing', label: 'View App Thing' }]
|
|
81
|
+
}],
|
|
82
|
+
categoryGranularMap: { app_cat: ['view_app_thing'] }
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
All four keys are optional. Arrays append, the map merges + dedupes, then `rebuildPermissions()` runs once. Equivalent to the imperative pattern but with no chance of forgetting the rebuild.
|
|
87
|
+
|
|
88
|
+
**4. `User._permSwitch` — exposed field builder**
|
|
89
|
+
|
|
90
|
+
The internal `_permSwitch(p) → { name: 'permissions.<p.name>', type: 'switch', ... }` helper is now `User._permSwitch` so apps building custom permission forms can use it directly and stay aligned with the framework's switch-field shape (no copy-paste drift if the shape evolves).
|
|
91
|
+
|
|
92
|
+
### Feature — `Member` permission registry parity
|
|
93
|
+
|
|
94
|
+
`Member.PERMISSIONS` had no extension point and was a literal source array — apps wanting custom member permissions had to redefine it themselves, breaking on every framework update that added new entries. Mirroring the `User` treatment:
|
|
95
|
+
|
|
96
|
+
- **New** `Member.BASE_PERMISSIONS` — the framework-defined list (renamed from the old `Member.PERMISSIONS` source).
|
|
97
|
+
- **New** `Member.APP_PERMISSIONS` — empty array; apps push their own entries here.
|
|
98
|
+
- **New** `Member.rebuildPermissions()` — recomputes `Member.PERMISSIONS` and `Member.PERMISSION_FIELDS` from `BASE_PERMISSIONS + APP_PERMISSIONS`. Idempotent. Mutates caches in place so cached references (e.g. forms holding `Member.PERMISSION_FIELDS`) stay current.
|
|
99
|
+
- `Member.PERMISSIONS` and `Member.PERMISSION_FIELDS` are now live caches; reading them works exactly as before. Any code that *wrote* directly to `Member.PERMISSIONS` should switch to `APP_PERMISSIONS` (or `BASE_PERMISSIONS` for framework-level edits).
|
|
100
|
+
- `Member.hasPermission` is unchanged — still does literal-only matching with no category fallback (Member has no category concept; this matches existing behavior).
|
|
101
|
+
|
|
102
|
+
**Tests / loader**
|
|
103
|
+
|
|
104
|
+
- New `test/unit/User.test.js` (17 tests) covers: granular→category fallback, `registerCategoryMap` merge semantics + array-form gates + superuser bypass, `rebuildPermissions` picking up extended granular tabs, the "App" tabset appearing when `APP_CATEGORY_PERMISSIONS` is non-empty, `CATEGORY_GRANULAR_MAP` updates flowing through to `GRANULAR_TO_CATEGORY`, idempotency, in-place mutation preserving held references, and `registerPermissions` atomic registration end-to-end (including the gate-check round-trip).
|
|
105
|
+
- New `test/unit/Member.test.js` (6 tests) covers: initial `BASE_PERMISSIONS` exposure through the live cache, switch-field shape, `APP_PERMISSIONS` pickup, in-place mutation invariant, idempotency, and the literal-matching contract for `Member.hasPermission`.
|
|
106
|
+
- `test/utils/simple-module-loader.js` gained `User` and `Member` entries with fallback returns, and now handles aliased named imports (`import { X as Y } from ...`) and unresolved relative named imports (declaring locals as `undefined` so module-load doesn't ReferenceError when names appear only in metadata literals).
|
|
107
|
+
|
|
108
|
+
### Feature — Examples: landing page, legacy removal, automated example tests
|
|
109
|
+
|
|
110
|
+
- **`examples/index.html` (new)** — visiting `http://localhost:3000/examples/` is no longer a blank page. A static landing card-grid links to the **Examples Portal** (canonical demos) and the standalone **Auth** login flow. No JS, no module imports — works even if the framework build is broken.
|
|
111
|
+
- **`examples/legacy/` removed** — the previous portal and one-off HTML demos (frozen on 2026-04-25) are deleted from the working tree. Git history preserves blame; there's nothing to port from. References in `examples/portal/README.md` are gone; references in `planning/done/*.md` and historical CHANGELOG entries are intentionally left as-is (historical record).
|
|
112
|
+
- **Static import-symbol check (`test/build/examples-imports.test.js`)** — runs in `npm test` and `npm run test:build`. For every `*Example.js` under `examples/portal/examples/`, it parses the `import` statements and verifies each named symbol from `'web-mojo'` / `'web-mojo/<sub>'` is actually exported by the corresponding source-tree entry. Catches "this example imports a symbol that no longer exists" without booting a browser.
|
|
113
|
+
- **Headless smoke runner (`scripts/test-examples-smoke.js`, `npm run test:examples`)** — opt-in, NOT wired into the default test run. Boots Vite on an ephemeral port, launches Chromium via Playwright, visits every registry route, and fails on `pageerror` or unhandled rejections. One-time setup: `npx playwright install chromium`. `playwright` is now a `devDependency`.
|
|
114
|
+
|
|
115
|
+
### Feature — Modal chrome: stripe + outline icon + tint (typed alerts only)
|
|
116
|
+
|
|
117
|
+
Pivoted away from the eyebrow band entirely. The 28px colored slab + uppercase tracking-letterspaced label was a 2015-2018 dev-tool aesthetic that aged poorly; modern reference apps (Linear, Stripe, Notion, Vercel, Apple HIG, Material 3) use minimal default chrome and reserve type signaling for typed alerts only.
|
|
118
|
+
|
|
119
|
+
**New design — see [`planning/mockups/modals/10-stripe-icon-tint.html`](planning/mockups/modals/10-stripe-icon-tint.html):**
|
|
120
|
+
|
|
121
|
+
- **Default modals** (`Modal.dialog`, `Modal.show`, `Modal.confirm`, `Modal.prompt`, `Modal.form`, `Modal.modelForm`, `Modal.showModel*`) — stock Bootstrap 5 cards. No stripe, no tint, no extra chrome. Header/footer dividers were already removed.
|
|
122
|
+
- **Typed alerts only** (`Modal.alert` with `type: 'info' | 'success' | 'warning' | 'error'`) get three layered cues, all driven by `--mojo-current-accent`:
|
|
123
|
+
1. **4px top accent stripe** in the type color (Stripe-style; hugs the card's inner radius).
|
|
124
|
+
2. **Outline leading icon** — `bi-info-circle` / `bi-check-circle` / `bi-exclamation-triangle` / `bi-x-circle` — sitting next to the title in the header. Color follows the type accent.
|
|
125
|
+
3. **Soft full-card tint** — 5% type color in light mode, 10% in dark, applied as a top-to-bottom gradient.
|
|
126
|
+
- New `Modal.alert` option: `icon: 'bi-...'` to override the default icon, or `icon: null` to suppress it.
|
|
127
|
+
- Type-colored primary button preserved (red band → red OK button, etc.).
|
|
128
|
+
|
|
129
|
+
**Removed plumbing (was unreleased — no migration required):**
|
|
130
|
+
|
|
131
|
+
- `ModalView` constructor option `eyebrow` (string / `false` / `null`) — gone.
|
|
132
|
+
- `Modal.setEyebrowEnabled(boolean)` / `Modal.isEyebrowEnabled()` static helpers — gone.
|
|
133
|
+
- CSS classes `modal-bandless` and `mojo-no-eyebrow` — gone.
|
|
134
|
+
- Internal helpers `Modal._eyebrowStyle`, `Modal._resolveEyebrow`, `Modal._suppressDuplicateTitle` — gone.
|
|
135
|
+
- `eyebrow` parameter on `Modal.dialog` / `Modal.alert` / `Modal.confirm` / `Modal.prompt` / `Modal.form` / `Modal.modelForm` / `Modal.show` / `Modal.showModelView` — gone.
|
|
136
|
+
- CSS variables `--mojo-eyebrow`, `--mojo-current-eyebrow-fg`, `--mojo-current-tint` — gone (the band's `::before` content var, the eyebrow text color, and the separate tint var).
|
|
137
|
+
- The 28px band-clearance padding rules for `.modal-header` and `.modal-body:first-child` — gone (no band to clear).
|
|
138
|
+
|
|
139
|
+
`Modal.drawer` keeps its own `eyebrow` option — that's a separate concept (a small uppercase label inside the drawer's custom header markup, unrelated to the band).
|
|
140
|
+
|
|
141
|
+
### CSS — Sidebar group selector: clean stock card
|
|
142
|
+
|
|
143
|
+
- `Sidebar.showGroupSearchDialog()` no longer passes an `eyebrow` to its `ModalView`. The selector renders as a clean Bootstrap card with the search header, group rows, and footer count — same as before, just without the band.
|
|
144
|
+
|
|
145
|
+
### CSS — User profile dialog: drop the hardcoded blue gradient strip
|
|
146
|
+
|
|
147
|
+
- `UserProfileView` had its own `.up-accent` element painting a blue 4px linear gradient at the top of the dialog (independent of the framework's modal chrome). Removed — the user profile now reads as a clean default modal, consistent with the new design system.
|
|
148
|
+
|
|
149
|
+
### Feature — Examples portal: simplified display settings
|
|
150
|
+
|
|
151
|
+
- The Display settings dialog drops the "Show eyebrow band" toggle (no eyebrow to toggle anymore). Theme picker (Light / Dark / System) is unchanged. The `examples-portal:eyebrow` localStorage key is no longer read or written.
|
|
152
|
+
|
|
153
|
+
### Mockups — `planning/mockups/modals/`
|
|
154
|
+
|
|
155
|
+
- **08-stripe-minimal.html** — 4px stripe only, no icon, no tint.
|
|
156
|
+
- **09-stripe-and-icon.html** — 4px stripe + leading icon, no tint.
|
|
157
|
+
- **10-stripe-icon-tint.html** — full pattern (stripe + outline icon + tint). **Implemented.**
|
|
158
|
+
- **11-icon-only.html** — Apple HIG / Material 3 icon-badge alternative.
|
|
159
|
+
- Each mockup demos the same five scenarios side-by-side in light + dark: a custom-body modal (Alice Adams), a default confirm, and the four typed alerts.
|
|
160
|
+
|
|
5
161
|
### Feature — Modal eyebrow band: redesign + global controls
|
|
6
162
|
|
|
7
163
|
- `ModalView` now accepts an `eyebrow` constructor option directly:
|
|
@@ -50,11 +206,19 @@
|
|
|
50
206
|
`--mojo-current-eyebrow-fg` at higher specificity, so they keep their
|
|
51
207
|
vivid colored bands. Untyped alerts (`Modal.alert` without a `type`)
|
|
52
208
|
fall back to `--mojo-dialog-accent`.
|
|
53
|
-
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
band
|
|
209
|
+
- Default modals now use a flat `--bs-modal-bg` surface — no background
|
|
210
|
+
tint. Earlier iterations tinted the whole card or just the top ~96px,
|
|
211
|
+
but a body view with its own opaque chrome (e.g. a custom user-detail
|
|
212
|
+
view) inevitably exposed the gradient as a thin colored strip between
|
|
213
|
+
the band and the view's surface. The eyebrow band already carries the
|
|
214
|
+
structural signal; the card itself stays clean.
|
|
215
|
+
- Typed alerts (`.modal-alert.*`) keep the full-height tint — their
|
|
216
|
+
bodies are short text and benefit from full-surface brand presence.
|
|
217
|
+
`--mojo-current-tint` is set per type to drive the gradient.
|
|
218
|
+
- Eyebrow text color uses `var(--bs-emphasis-color)` (high-contrast)
|
|
219
|
+
by default rather than `var(--bs-secondary-color)` (muted) so the
|
|
220
|
+
uppercase label stays legible on the neutral band in both themes.
|
|
221
|
+
Typed alerts continue to render white text on their colored bands.
|
|
58
222
|
- New `.modal-body.modal-body-flush:last-child` rule: edge-to-edge
|
|
59
223
|
bodies (e.g. `noBodyPadding: true` with no footer) clip their
|
|
60
224
|
bottom corners to `var(--bs-modal-inner-border-radius)` so content
|
package/dist/admin-models.cjs.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./chunks/Collection-BpUmNuDZ.js"),t=require("./chunks/User-B_Urf7U7.js");class LoginEvent extends e.Model{constructor(e={}){super(e,{endpoint:"/api/account/logins"})}}class LoginEventList extends e.Collection{constructor(e={}){super({ModelClass:LoginEvent,endpoint:"/api/account/logins",...e})}}class IncidentStats extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/stats",requiresId:!1})}}class IncidentEvent extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/event"})}}class IncidentEventList extends e.Collection{constructor(e={}){super({ModelClass:IncidentEvent,endpoint:"/api/incident/event",size:10,...e})}}const a={edit:{title:"Edit Incident Event",fields:[{name:"category",type:"select",label:"Category",placeholder:"Select category",options:()=>Incident.COMPONENTS,editable:!0,force_top:!0,cols:6},{name:"incident",type:"text",label:"Incident",placeholder:"Enter Incident ID",cols:6},{name:"description",type:"textarea",label:"Description",placeholder:"Enter Description",cols:12},{name:"details",type:"textarea",label:"Details",placeholder:"Enter Details",cols:12},{name:"component",type:"text",label:"Component",placeholder:"Enter Component",cols:8},{name:"component_id",type:"text",label:"Component ID",placeholder:"Enter Component ID",cols:4}]}};class Incident extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/incident"})}}class IncidentList extends e.Collection{constructor(e={}){super({ModelClass:Incident,endpoint:"/api/incident/incident",size:10,...e})}}class IncidentRuleSet extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/event/ruleset"})}}class IncidentRuleSetList extends e.Collection{constructor(e={}){super({ModelClass:IncidentRuleSet,endpoint:"/api/incident/event/ruleset",size:10,...e})}}class IncidentRule extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/event/ruleset/rule"})}}class IncidentRuleList extends e.Collection{constructor(e={}){super({ModelClass:IncidentRule,endpoint:"/api/incident/event/ruleset/rule",size:10,...e})}}class IncidentHistory extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/incident/history"})}}class IncidentHistoryList extends e.Collection{constructor(e={}){super({ModelClass:IncidentHistory,endpoint:"/api/incident/incident/history",size:10,...e})}}const n=[{value:0,label:"No Bundling"},{value:1,label:"By Hostname"},{value:2,label:"By Model Name"},{value:3,label:"By Model Name + ID"},{value:4,label:"By Source IP"},{value:5,label:"By Hostname + Model Name"},{value:6,label:"By Hostname + Model Name + ID"},{value:7,label:"By Source IP + Model Name"},{value:8,label:"By Source IP + Model Name + ID"},{value:9,label:"By Source IP + Hostname"}],l=[{value:0,label:"ALL (must match all rules)"},{value:1,label:"ANY (match any rule)"}],s=[{value:"==",label:"Equal (==)"},{value:"eq",label:"Equal (eq)"},{value:">",label:"Greater Than (>)"},{value:">=",label:"Greater Than or Equal (>=)"},{value:"<",label:"Less Than (<)"},{value:"<=",label:"Less Than or Equal (<=)"},{value:"contains",label:"Contains"},{value:"regex",label:"Regular Expression"}],o=[{value:"str",label:"String"},{value:"int",label:"Integer"},{value:"float",label:"Float"},{value:"bool",label:"Boolean"}],i=[{value:"level",label:"Level",description:"Event level (error, warning, info, debug)",meta:{type:"str"}},{value:"source_ip",label:"Source IP Address",description:"IP address of the event source",meta:{type:"str"}},{value:"rule_id",label:"Rule ID",description:"Numeric rule identifier",meta:{type:"int"}},{value:"hostname",label:"Hostname",description:"Hostname where event occurred",meta:{type:"str"}},{value:"component",label:"Component",description:"System component name",meta:{type:"str"}},{value:"component_id",label:"Component ID",description:"Component identifier",meta:{type:"str"}},{value:"category",label:"Category",description:"Event category (ossec, auth, api_error, etc.)",meta:{type:"str"}},{value:"description",label:"Description",description:"Event description text",meta:{type:"str"}},{value:"details",label:"Details",description:"Additional event details",meta:{type:"str"}},{value:"alert_id",label:"Alert ID",description:"Numeric alert identifier",meta:{type:"int"}},{value:"severity",label:"Severity",description:"Numeric severity level",meta:{type:"int"}},{value:"user",label:"User",description:"Username associated with event",meta:{type:"str"}},{value:"action",label:"Action",description:"Action that triggered the event",meta:{type:"str"}},{value:"status",label:"Status",description:"Status value or code",meta:{type:"str"}},{value:"status_code",label:"Status Code",description:"Numeric status code (e.g., HTTP status)",meta:{type:"int"}},{value:"message",label:"Message",description:"Event message text",meta:{type:"str"}},{value:"path",label:"Path",description:"File path or URL path",meta:{type:"str"}},{value:"title",label:"Title",description:"OSSEC Title",meta:{type:"str"}},{value:"country_code",label:"Country Code",description:"Country code associated with event",meta:{type:"str"}},{value:"region",label:"Region",description:"Region associated with event",meta:{type:"str"}},{value:"city",label:"City",description:"City associated with event",meta:{type:"str"}},{value:"http_user_agent",label:"HTTP User Agent",description:"User agent string associated with event",meta:{type:"str"}},{value:"request_path",label:"Request Path",description:"Request path associated with event",meta:{type:"str"}},{value:"method",label:"Method",description:"HTTP method or function name",meta:{type:"str"}}],r=["global","account","ossec","api","realtime","auth","login:unknown","invalid_password","sms:login_unknown","totp:login_unknown","reset:unknown","magic:unknown","token:unknown","api_denied","unauthenticated","view_permission_denied","edit_permission_denied","group_member_permission_denied","user_permission_denied","security_alert","totp:confirm_failed","totp:login_failed","totp:recovery_used","sms:otp_failed","email:no_mailbox","email:send_failed","sms:send_failed","email_change:bad_password","email_change:requested","account:deactivate_requested","phone_verify:invalid","email_verify:invalid","oauth:unlink_guard_error","rest_error","mojo_rest_error","rest_value_error"],c=r;class RuleSet extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/event/ruleset"})}}class RuleSetList extends e.Collection{constructor(e={}){super({ModelClass:RuleSet,endpoint:"/api/incident/event/ruleset",...e})}}const u=[{value:0,label:"Disabled — each event gets its own incident"},{value:5,label:"5 minutes"},{value:10,label:"10 minutes"},{value:15,label:"15 minutes"},{value:30,label:"30 minutes"},{value:60,label:"1 hour"},{value:120,label:"2 hours"},{value:360,label:"6 hours"},{value:720,label:"12 hours"},{value:1440,label:"1 day"},{value:null,label:"No limit — bundle forever"}],d={create:{title:"Create RuleSet",size:"lg",fields:[{type:"tabset",name:"rulesetTabs",tabs:[{label:"General",fields:[{name:"name",type:"text",label:"Name",required:!0,placeholder:"e.g., Brute Force Detection",columns:6},{name:"match_by",type:"select",label:"Match Logic",value:0,options:l,tooltip:"ALL = every condition must match. ANY = at least one",columns:6},{name:"category",type:"combo",label:"Scope / Category",required:!0,options:r,allowCustom:!0,placeholder:"Type or select...",tooltip:"Scope or event category to match. Use * as a catch-all",columns:6},{name:"priority",type:"number",label:"Evaluation Priority",value:10,required:!0,tooltip:"Lower number = evaluated first",columns:6},{name:"is_active",type:"switch",label:"Active",value:!0,tooltip:"Inactive rules are skipped during event processing",columns:6},{name:"metadata.delete_on_resolution",type:"switch",label:"Delete on Resolution",value:!1,tooltip:"Incidents are permanently deleted when resolved or closed (CASCADE)",columns:6}]},{label:"Bundling",fields:[{name:"bundle_by",type:"select",label:"Bundle By",value:4,options:n,tooltip:"How to group related events into one incident",columns:6},{name:"bundle_minutes",type:"select",label:"Bundle Window",value:30,options:u,tooltip:"Events outside this window create a new incident",columns:6},{name:"bundle_by_rule_set",type:"switch",label:"Bundle by RuleSet",value:!0,tooltip:"Group events matched by this rule into the same incident",columns:6}]},{label:"Thresholds",fields:[{type:"html",columns:12,html:'<div class="alert alert-info small mb-3">\n <i class="bi bi-info-circle me-1"></i>\n <strong>How thresholds work:</strong> Events accumulate in "pending" status.\n Once trigger count is reached, the handler fires and the incident becomes "new".\n Leave empty to fire immediately on the first event.\n </div>'},{name:"trigger_count",type:"number",label:"Trigger Count",placeholder:"Empty = immediate",tooltip:"Number of events before the handler fires",columns:4},{name:"trigger_window",type:"number",label:"Trigger Window (min)",placeholder:"Empty = all events",tooltip:"Only count events within this many minutes",columns:4},{name:"retrigger_every",type:"number",label:"Re-trigger Every",placeholder:"Empty = once",tooltip:"Re-fire handler every N additional events after initial trigger",columns:4}]},{label:"Handler",fields:[{name:"handler",type:"text",label:"Handler Chain",placeholder:"e.g., block://?ttl=3600,ticket://?priority=8",tooltip:"Chain multiple handlers with commas",columns:12},{type:"html",columns:12,html:'<div class="alert alert-light border small mb-0">\n <code>block://?ttl=3600</code> — Block source IP<br>\n <code>ticket://?priority=8</code> — Create ticket<br>\n <code>email://perm@manage_security</code> — Email notification<br>\n <code>sms://perm@manage_security</code> — SMS notification<br>\n <code>notify://perm@manage_security</code> — In-app + push<br>\n <code>llm://</code> — LLM triage agent<br>\n <code>job://myapp.module.function</code> — Async job\n </div>'}]}]}]},edit:{title:"Edit RuleSet",size:"lg",fields:[{type:"tabset",name:"rulesetTabs",tabs:[{label:"General",fields:[{name:"name",type:"text",label:"Name",required:!0,placeholder:"e.g., Brute Force Detection",columns:6},{name:"match_by",type:"select",label:"Match Logic",options:l,tooltip:"ALL = every condition must match. ANY = at least one",columns:6},{name:"category",type:"combo",label:"Scope / Category",options:r,allowCustom:!0,required:!0,placeholder:"Type or select...",tooltip:"Scope or event category to match. Use * as a catch-all",columns:6},{name:"priority",type:"number",label:"Evaluation Priority",required:!0,tooltip:"Lower number = evaluated first",columns:6},{name:"is_active",type:"switch",label:"Active",tooltip:"Inactive rules are skipped during event processing",columns:6},{name:"metadata.delete_on_resolution",type:"switch",label:"Delete on Resolution",tooltip:"Incidents are permanently deleted when resolved or closed (CASCADE)",columns:6}]},{label:"Bundling",fields:[{name:"bundle_by",type:"select",label:"Bundle By",options:n,tooltip:"How to group related events into one incident",columns:6},{name:"bundle_minutes",type:"select",label:"Bundle Window",options:u,tooltip:"Events outside this window create a new incident",columns:6},{name:"bundle_by_rule_set",type:"switch",label:"Bundle by RuleSet",tooltip:"Group events matched by this rule into the same incident",columns:6}]},{label:"Thresholds",fields:[{type:"html",columns:12,html:'<div class="alert alert-info small mb-3">\n <i class="bi bi-info-circle me-1"></i>\n <strong>How thresholds work:</strong> Events accumulate in "pending" status.\n Once trigger count is reached, the handler fires and the incident becomes "new".\n Leave empty to fire immediately on the first event.\n </div>'},{name:"trigger_count",type:"number",label:"Trigger Count",placeholder:"Empty = immediate",tooltip:"Number of events before the handler fires",columns:4},{name:"trigger_window",type:"number",label:"Trigger Window (min)",placeholder:"Empty = all events",tooltip:"Only count events within this window toward the trigger count",columns:4},{name:"retrigger_every",type:"number",label:"Re-trigger Every",placeholder:"Empty = once",tooltip:"Re-fire handler every N additional events after initial trigger",columns:4}]},{label:"Handler",fields:[{name:"handler",type:"text",label:"Handler Chain",placeholder:"e.g., block://?ttl=3600,ticket://?priority=8",tooltip:"Chain multiple handlers with commas",columns:12},{type:"html",columns:12,html:'<div class="alert alert-light border small mb-0">\n <code>block://?ttl=3600</code> — Block source IP<br>\n <code>ticket://?priority=8</code> — Create ticket<br>\n <code>email://perm@manage_security</code> — Email notification<br>\n <code>sms://perm@manage_security</code> — SMS notification<br>\n <code>notify://perm@manage_security</code> — In-app + push<br>\n <code>llm://</code> — LLM triage agent<br>\n <code>job://myapp.module.function</code> — Async job\n </div>'}]}]}]}};class Rule extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/event/ruleset/rule"})}}class RuleList extends e.Collection{constructor(e={}){super({ModelClass:Rule,endpoint:"/api/incident/event/ruleset/rule",...e})}}const p={create:{title:"Create Rule",fields:[{name:"name",type:"text",label:"Name",required:!0,placeholder:"Enter rule name",cols:12},{name:"field_name",type:"combo",label:"Field Name",required:!0,placeholder:"Select or enter field name...",options:i,allowCustom:!0,showDescription:!0,help:"Select a common field or type a custom field name",cols:8},{name:"index",type:"select",label:"Index",required:!0,start:0,end:14,step:1,cols:4},{name:"comparator",type:"select",label:"Comparator",required:!0,options:s,cols:6},{name:"value_type",type:"select",label:"Value Type",required:!0,options:o,value:"str",cols:6},{name:"value",type:"textarea",label:"Value",required:!0,placeholder:"Enter comparison value",cols:12}]},edit:{title:"Edit Rule",fields:[{name:"name",type:"text",label:"Name",required:!0,placeholder:"Enter rule name",cols:12},{name:"field_name",type:"combo",label:"Field Name",required:!0,placeholder:"Select or enter field name...",options:i,allowCustom:!0,showDescription:!0,help:"Select a common field or type a custom field name",cols:12},{name:"comparator",type:"select",label:"Comparator",required:!0,options:s,cols:6},{name:"value_type",type:"select",label:"Value Type",required:!0,options:o,cols:6},{name:"value",type:"text",label:"Value",required:!0,placeholder:"Enter comparison value",cols:12}]}};RuleSet.EDIT_FORM=d.edit,RuleSet.ADD_FORM=d.create,RuleSet.CREATE_FORM=d.create,RuleSet.BundleByOptions=n,RuleSet.MatchByOptions=l,Rule.EDIT_FORM=p.edit,Rule.ADD_FORM=p.create,Rule.CREATE_FORM=p.create,Rule.ComparatorOptions=s,Rule.ValueTypeOptions=o;class PushDevice extends e.Model{constructor(e={}){super(e,{endpoint:"/api/account/devices/push"})}}class PushDeviceList extends e.Collection{constructor(e={}){super({ModelClass:PushDevice,endpoint:"/api/account/devices/push",...e})}}class PushTemplate extends e.Model{constructor(e={}){super(e,{endpoint:"/api/account/devices/push/templates"})}}class PushTemplateList extends e.Collection{constructor(e={}){super({ModelClass:PushTemplate,endpoint:"/api/account/devices/push/templates",...e})}}class PushConfig extends e.Model{constructor(e={}){super(e,{endpoint:"/api/account/devices/push/config"})}}class PushConfigList extends e.Collection{constructor(e={}){super({ModelClass:PushConfig,endpoint:"/api/account/devices/push/config",...e})}}class PushDelivery extends e.Model{constructor(e={}){super(e,{endpoint:"/api/account/devices/push/deliveries"})}}class PushDeliveryList extends e.Collection{constructor(e={}){super({ModelClass:PushDelivery,endpoint:"/api/account/devices/push/deliveries",...e})}}const m={create:{title:"Create Push Configuration",fields:[{name:"name",label:"Name",required:!0},{type:"collection",name:"group",label:"Group (optional)",Collection:t.GroupList,labelField:"name",valueField:"id"},{name:"fcm_service_account",label:"Service Account",type:"textarea",rows:10}]},edit:{title:"Edit Push Configuration",fields:[{name:"name",label:"Name",required:!0},{type:"collection",name:"group",label:"Group (optional)",Collection:t.GroupList,labelField:"name",valueField:"id"},{name:"fcm_service_account",label:"Service Account",type:"textarea",rows:10},{name:"is_active",label:"Is Active",type:"switch",value:!0}]}},b={edit:{title:"Edit Push Template",fields:[{name:"name",label:"Name",required:!0},{name:"category",label:"Category",required:!0},{type:"collection",name:"group",label:"Group (optional)",Collection:t.GroupList,labelField:"name",valueField:"id",defaultParams:{is_active:!0}},{name:"title_template",label:"Title Template",required:!0},{name:"body_template",label:"Body Template",type:"textarea",required:!0},{name:"action_url",label:"Action URL"},{name:"priority",label:"Priority",type:"select",options:["high","normal"]},{name:"variables",label:"Variables",type:"json",help:"JSON format"},{name:"is_active",label:"Is Active",type:"switch"}]}};m.create=m.edit,b.create=b.edit;class Ticket extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/ticket"})}}class TicketList extends e.Collection{constructor(e={}){super({ModelClass:Ticket,endpoint:"/api/incident/ticket",...e})}}const h={ticket:"Ticket",bug:"Bug",feature:"Feature Request",incident:"Incident",security:"Security Incident",fulfillment:"Fulfillment",new_user:"New User",new_group:"New Group",qa:"Quality Assurance"},y=Object.entries(h).map(([e,t])=>({value:e,label:t})),v={create:{title:"Create Ticket",fields:[{name:"title",type:"text",label:"Title",required:!0,cols:12},{name:"description",type:"textarea",label:"Description",required:!1,cols:12},{name:"category",type:"select",label:"Category",options:y,cols:12,value:"ticket"},{name:"priority",type:"number",label:"Priority",value:5,cols:6},{name:"status",type:"select",label:"Status",options:["new","open","paused","resolved","qa","ignored"],cols:6,value:"new"},{type:"collection",name:"assignee",label:"Assignee",Collection:t.UserList,labelField:"display_name",valueField:"id",cols:12},{type:"collection",name:"incident",label:"Incident",Collection:IncidentList,labelField:"title",valueField:"id",cols:12}]},edit:{title:"Edit Ticket",fields:[{name:"title",type:"text",label:"Title",required:!0,cols:12},{name:"description",type:"textarea",label:"Description",required:!1,cols:12},{name:"category",type:"select",label:"Category",options:y,cols:12},{name:"priority",type:"number",label:"Priority",cols:6},{name:"status",type:"select",label:"Status",options:["new","open","paused","resolved","qa","ignored"],cols:6},{type:"collection",name:"assignee",label:"Assignee",Collection:t.UserList,labelField:"display_name",valueField:"id",cols:12},{type:"collection",name:"incident",label:"Incident",Collection:IncidentList,labelField:"title",valueField:"id",cols:12}]}};class TicketNote extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/ticket/note"})}}class TicketNoteList extends e.Collection{constructor(e={}){super({ModelClass:TicketNote,endpoint:"/api/incident/ticket/note",...e})}}class AssistantConversation extends e.Model{constructor(e={}){super(e,{endpoint:"/api/assistant/conversation"})}}class AssistantConversationList extends e.Collection{constructor(e={}){super({ModelClass:AssistantConversation,endpoint:"/api/assistant/conversation",size:50,...e})}}class AssistantSkill extends e.Model{constructor(e={}){super(e,{endpoint:"/api/assistant/skill"})}}class AssistantSkillList extends e.Collection{constructor(e={}){super({ModelClass:AssistantSkill,endpoint:"/api/assistant/skill",size:50,...e})}}class Job extends e.Model{constructor(e={}){super(e,{endpoint:"/api/jobs/job"})}async cancel(){const e=await this.save({cancel_request:!0});return e.success&&e.data.status&&this.set("cancel_requested",!0),e}async retry(e=null){const t=e?{retry_request:{retry:!0,delay:e}}:{retry_request:!0},a=await this.save(t);return a.success&&a.data.status&&a.data.new_job_id?{...a,newJobId:a.data.new_job_id,originalJobId:a.data.original_job_id}:a}async getDetailedStatus(){const e=await this.save({get_status:!0});return e.success&&e.data.status&&this.set(e.data.data),e}async cloneJob(e={}){const t={publish_job:{payload:e,...e}},a=await this.save(t);return a.success&&a.data.status&&a.data.job_id?{...a,newJobId:a.data.job_id,templateJobId:a.data.template_job_id}:a}isActive(){const e=this.get("status");return["pending","running"].includes(e)}isTerminal(){const e=this.get("status");return["completed","failed","canceled","expired"].includes(e)}canRetry(){const e=this.get("status");return["failed","canceled","expired"].includes(e)&&!1!==this.get("is_retriable")}canCancel(){const e=this.get("status");return["pending","running"].includes(e)&&!this.get("cancel_requested")}getStatusBadgeClass(){return{pending:"bg-primary",running:"bg-success",completed:"bg-info",failed:"bg-danger",canceled:"bg-secondary",expired:"bg-warning"}[this.get("status")]||"bg-secondary"}getStatusIcon(){return{pending:"bi-hourglass",running:"bi-arrow-repeat",completed:"bi-check-circle",failed:"bi-x-octagon",canceled:"bi-x-circle",expired:"bi-clock"}[this.get("status")]||"bi-question-circle"}getEvents(){return this.get("recent_events")||[]}getFormattedDuration(){const e=this.get("duration_ms");return e&&0!==e?e<1e3?`${e}ms`:e<6e4?`${(e/1e3).toFixed(1)}s`:e<36e5?`${(e/6e4).toFixed(1)}m`:`${(e/36e5).toFixed(1)}h`:"N/A"}getQueuePosition(){return this.get("queue_position")}hasExpired(){const e=this.get("expires_at");return!!e&&new Date(e)</* @__PURE__ */new Date}getRunnerId(){return this.get("runner_id")}getPayload(){return this.get("payload")||{}}getMetadata(){return this.get("metadata")||{}}}class JobList extends e.Collection{constructor(e={}){super({ModelClass:Job,endpoint:"/api/jobs/job",...e})}async fetchByStatus(e,t={}){return this.fetch({status:e,...t})}async fetchByChannel(e,t={}){return this.fetch({channel:e,...t})}async fetchPending(e={}){return this.fetchByStatus("pending",e)}async fetchRunning(e={}){return this.fetchByStatus("running",e)}async fetchCompleted(e={}){return this.fetchByStatus("completed",e)}async fetchFailed(e={}){return this.fetchByStatus("failed",e)}async fetchScheduled(e={}){return this.fetch({scheduled:!0,...e})}}Job.publish=async function(t){return await e.rest.POST("/api/jobs/publish",t)},Job.getStats=async function(){const t=await e.rest.GET("/api/jobs/stats");return t.success?t.data:null},Job.getHealth=async function(t=null){const a=t?`/api/jobs/health/${t}`:"/api/jobs/health",n=e.rest.GET(a);return n.success?n.data:null},Job.test=async function(){return await e.rest.POST("/api/jobs/test",{})},Job.tests=async function(){return await e.rest.POST("/api/jobs/tests",{})},Job.clearStuck=async function(t=null){const a=t?{channel:t}:{};return await e.rest.POST("/api/jobs/control/clear-stuck",a)},Job.clearChannel=async function(t){return await e.rest.POST("/api/jobs/control/clear-queue",{channel:t,confirm:"yes"})},Job.cleanConsumers=async function(){return await e.rest.POST("/api/jobs/control/cleanup-consumers")},Job.purgeJobs=async function(t){return await e.rest.POST("/api/jobs/control/purge",{days_old:t})};class JobLog extends e.Model{constructor(e={}){super(e,{endpoint:"/api/jobs/logs"})}}class JobLogList extends e.Collection{constructor(e={}){super({endpoint:"/api/jobs/logs",model:JobLog,...e})}}class JobEvent extends e.Model{constructor(e={}){super(e,{endpoint:"/api/jobs/event"})}}class JobEventList extends e.Collection{constructor(e={}){super({endpoint:"/api/jobs/event",model:JobEvent,...e})}}class JobsEngineStats extends e.Model{constructor(e={}){super(e,{endpoint:"/api/jobs/stats",requiresId:!1})}}class EmailDomain extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/aws/email/domain",...t})}async onboard(e={},t={}){if(!this.id)return await this.showError("Cannot onboard domain without ID"),{success:!1,status:400,error:"Missing domain id"};try{const a=`${this.buildUrl(this.id)}/onboard`;return await this.rest.POST(a,e,t.params)}catch(a){return{success:!1,status:a?.status||500,error:a?.message||"Failed to onboard domain"}}}async audit(e={}){if(!this.id)return await this.showError("Cannot audit domain without ID"),{success:!1,status:400,error:"Missing domain id"};const t=(e.method||"GET").toUpperCase(),a=`${this.buildUrl(this.id)}/audit`;try{return"POST"===t?await this.rest.POST(a,e.data||{},e.params):await this.rest.GET(a,e.params)}catch(n){return{success:!1,status:n?.status||500,error:n?.message||"Failed to audit domain"}}}async reconcile(e={},t={}){if(!this.id)return await this.showError("Cannot reconcile domain without ID"),{success:!1,status:400,error:"Missing domain id"};try{const a=`${this.buildUrl(this.id)}/reconcile`;return await this.rest.POST(a,e,t.params)}catch(a){return{success:!1,status:a?.status||500,error:a?.message||"Failed to reconcile domain"}}}static async onboardById(e,t={},a={}){const n=new EmailDomain({id:e},a);return await n.onboard(t,a)}static async auditById(e,t={}){const a=new EmailDomain({id:e},t);return await a.audit(t)}static async reconcileById(e,t={},a={}){const n=new EmailDomain({id:e},a);return await n.reconcile(t,a)}}class EmailDomainList extends e.Collection{constructor(e={}){super({ModelClass:EmailDomain,endpoint:"/api/aws/email/domain",size:10,...e})}}class Mailbox extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/aws/email/mailbox",...t})}}Mailbox.sendEmail=async function(t){return await e.rest.POST("/api/aws/email/send",t)};class MailboxList extends e.Collection{constructor(e={}){super({ModelClass:Mailbox,endpoint:"/api/aws/email/mailbox",size:10,...e})}}const g={create:{title:"Add Mailbox",fields:[{type:"collection",name:"domain",label:"Domain",Collection:EmailDomainList,labelField:"name",valueField:"id",maxItems:10,placeholder:"Search domains...",emptyFetch:!1,required:!0,debounceMs:300,columns:12},{name:"email",type:"email",label:"Email Address",placeholder:"support@example.com",required:!0,columns:12},{name:"allow_inbound",type:"switch",label:"Allow Inbound",columns:6},{name:"allow_outbound",type:"switch",label:"Allow Outbound",defaultValue:!0,columns:6},{name:"is_system_default",type:"switch",label:"System Default",columns:6},{name:"is_domain_default",type:"switch",label:"Domain Default",columns:6},{name:"async_handler",type:"text",label:"Async Handler (optional)",placeholder:"myapp.handlers.process_support",columns:12,help:"Module:function to process inbound messages via task system"}]},edit:{title:"Edit Mailbox",fields:[{name:"email",type:"email",label:"Email Address",required:!0,columns:12},{name:"allow_inbound",type:"switch",label:"Allow Inbound",columns:6},{name:"allow_outbound",type:"switch",label:"Allow Outbound",columns:6},{name:"is_system_default",type:"switch",label:"System Default",columns:6},{name:"is_domain_default",type:"switch",label:"Domain Default",columns:6},{name:"async_handler",type:"text",label:"Async Handler (optional)",placeholder:"myapp.handlers.process_support",columns:12}]}};class SentMessage extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/aws/email/sent",...t})}}class SentMessageList extends e.Collection{constructor(e={}){super({ModelClass:SentMessage,endpoint:"/api/aws/email/sent",size:10,...e})}}class EmailTemplate extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/aws/email/template",...t})}}class EmailTemplateList extends e.Collection{constructor(e={}){super({ModelClass:EmailTemplate,endpoint:"/api/aws/email/template",size:10,...e})}}class PublicMessage extends e.Model{constructor(e={}){super(e,{endpoint:"/api/account/public_message"})}}class PublicMessageList extends e.Collection{constructor(e={}){super({ModelClass:PublicMessage,endpoint:"/api/account/public_message",size:25,...e})}}class PhoneNumber extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/phonehub/number",...t})}static async normalize(t,a="US"){const n={phone_number:t};a&&(n.country_code=a);const l=await e.rest.POST("/api/phonehub/number/normalize",n),s=l?.data??l;return!0===s?.status||!0===s?.success?{success:!0,phone_number:s?.data?.phone_number??s?.phone_number,data:s?.data??s,response:l}:{success:!1,error:s?.error||"Normalization failed",response:l}}static async lookup(t,a={}){const n=await e.rest.POST("/api/phonehub/number/lookup",{phone_number:t,...a}),l=n?.data??n;if(!0===l?.status||!0===l?.success){const e=l?.data??{};return{success:!0,model:new PhoneNumber(e,{endpoint:"/api/phonehub/number"}),data:e,response:n}}return{success:!1,error:l?.error||"Phone lookup failed",response:n}}}class PhoneNumberList extends e.Collection{constructor(e={}){super({ModelClass:PhoneNumber,endpoint:"/api/phonehub/number",size:10,...e})}}class SMS extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/phonehub/sms",...t})}static async send(t={}){const a=await e.rest.POST("/api/phonehub/sms/send",t),n=a?.data??a;if(!0===n?.status||!0===n?.success){const e=n?.data??{};return{success:!0,model:new SMS(e,{endpoint:"/api/phonehub/sms"}),data:e,response:a}}return{success:!1,error:n?.error||"Failed to send SMS",response:a}}}class SMSList extends e.Collection{constructor(e={}){super({ModelClass:SMS,endpoint:"/api/phonehub/sms",size:10,...e})}}const x={PhoneNumber:PhoneNumber,PhoneNumberList:PhoneNumberList,SMS:SMS,SMSList:SMSList};class JobRunner extends e.Model{constructor(e={}){e.runner_id&&!e.id&&(e.id=e.runner_id),super(e,{endpoint:"/api/jobs/runners",idAttribute:"runner_id"})}async ping(e=2){const t=this.getApp();if(!t||!t.rest)throw new Error("App or REST client not available");const a=await t.rest.POST("/api/jobs/runners/ping",{runner_id:this.get("runner_id"),timeout:e});return a.success&&a.data.status&&(this.set("last_heartbeat",/* @__PURE__ */(new Date).toISOString()),this.set("ping_status",a.data.ping_status||"success")),a}async shutdown(e=!0){const t=this.getApp();if(!t||!t.rest)throw new Error("App or REST client not available");const a=await t.rest.POST("/api/jobs/runners/shutdown",{runner_id:this.get("runner_id"),graceful:e});return a.success&&a.data.status&&this.set("alive",!1),a}getChannels(){return this.get("channels")||[]}getStatusBadgeClass(){return this.get("alive")?"bg-success":"bg-danger"}getStatusIcon(){return this.get("alive")?"bi-check-circle-fill":"bi-x-octagon-fill"}isActive(){return!0===this.get("alive")}isHealthy(){if(!this.isActive())return!1;const e=this.get("last_heartbeat");return!!e&&(Date.now()-new Date(e).getTime())/1e3<120}getFormattedHeartbeatAge(){const e=this.get("last_heartbeat");if(!e)return"Never";const t=(Date.now()-new Date(e).getTime())/1e3;return t<60?`${Math.round(t)}s ago`:t<3600?`${Math.round(t/60)}m ago`:`${Math.round(t/3600)}h ago`}getUtilization(){const e=(this.get("jobs_processed")||0)+(this.get("jobs_failed")||0);return e>10?100:Math.min(10*e,100)}getFormattedUptime(){const e=this.get("started");if(!e)return"Unknown";const t=new Date(e),a=/* @__PURE__ */new Date-t,n=Math.floor(a/1e3);return n<60?`${n}s`:n<3600?`${Math.floor(n/60)}m`:n<86400?`${Math.floor(n/3600)}h`:`${Math.floor(n/86400)}d`}getWorkerInfo(){const e=this.get("jobs_processed")||0,t=this.get("jobs_failed")||0;return{processed:e,failed:t,total:e+t,alive:this.get("alive"),utilization:this.getUtilization()}}getDisplayName(){const e=this.get("runner_id");if(!e)return"Unknown Runner";const t=e.split("-");return t.length>1?t[0]:e}canControl(){return!0===this.get("alive")}}class JobRunnerList extends e.Collection{constructor(e={}){super({ModelClass:JobRunner,endpoint:"/api/jobs/runners",...e})}getActive(){return this.where(e=>e.isActive())}getByChannel(e){return this.where(t=>t.getChannels().includes(e))}getHealthy(){return this.where(e=>e.isHealthy())}getTotalProcessed(){return this.models.reduce((e,t)=>e+(t.get("jobs_processed")||0),0)}getTotalFailed(){return this.models.reduce((e,t)=>e+(t.get("jobs_failed")||0),0)}getSystemHealth(){if(0===this.models.length)return 0;const e=this.models.filter(e=>e.get("alive")).length;return Math.round(e/this.models.length*100)}getAllChannels(){const e=/* @__PURE__ */new Set;return this.models.forEach(t=>{t.getChannels().forEach(t=>e.add(t))}),Array.from(e).sort()}}JobRunner.ping=async function(e,t=2){const a="undefined"!=typeof window&&window.app;if(!a||!a.rest)throw new Error("App or REST client not available");return await a.rest.POST("/api/jobs/runners/ping",{runner_id:e,timeout:t})},JobRunner.shutdown=async function(e,t=!0){const a="undefined"!=typeof window&&window.app;if(!a||!a.rest)throw new Error("App or REST client not available");return await a.rest.POST("/api/jobs/runners/shutdown",{runner_id:e,graceful:t})},JobRunner.broadcast=async function(e,t={},a=2){const n="undefined"!=typeof window&&window.app;if(!n||!n.rest)throw new Error("App or REST client not available");return await n.rest.POST("/api/jobs/runners/broadcast",{command:e,data:t,timeout:a})},JobRunner.broadcastStatus=async function(e=2){return JobRunner.broadcast("status",{},e)},JobRunner.broadcastShutdown=async function(e=2){return JobRunner.broadcast("shutdown",{},e)},JobRunner.broadcastPause=async function(e=2){return JobRunner.broadcast("pause",{},e)},JobRunner.broadcastResume=async function(e=2){return JobRunner.broadcast("resume",{},e)},JobRunner.broadcastReload=async function(e=2){return JobRunner.broadcast("reload",{},e)};class ScheduledTask extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/jobs/scheduled_task",...t})}}class ScheduledTaskList extends e.Collection{constructor(e={}){super({ModelClass:ScheduledTask,endpoint:"/api/jobs/scheduled_task",size:25,...e})}}class TaskResult extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/jobs/task_result",...t})}}class TaskResultList extends e.Collection{constructor(e={}){super({ModelClass:TaskResult,endpoint:"/api/jobs/task_result",size:25,...e})}}class BouncerDevice extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/account/bouncer/device",...t})}}class BouncerDeviceList extends e.Collection{constructor(e={}){super({ModelClass:BouncerDevice,endpoint:"/api/account/bouncer/device",size:25,...e})}}class BouncerSignal extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/account/bouncer/signal",...t})}}class BouncerSignalList extends e.Collection{constructor(e={}){super({ModelClass:BouncerSignal,endpoint:"/api/account/bouncer/signal",size:25,...e})}}class BouncerSignature extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/account/bouncer/signature",...t})}}class BouncerSignatureList extends e.Collection{constructor(e={}){super({ModelClass:BouncerSignature,endpoint:"/api/account/bouncer/signature",size:25,...e})}}const S=[{value:"country",label:"Country — Block all traffic from a country"},{value:"abuse",label:"Abuse Feed — Import known attacker IPs"},{value:"datacenter",label:"Datacenter — Block datacenter/hosting ranges"},{value:"custom",label:"Custom — Define your own CIDR list"}],f=[{value:"country",label:"Country"},{value:"abuse",label:"Abuse Feed"},{value:"datacenter",label:"Datacenter"},{value:"custom",label:"Custom"}],w=[{value:"ipdeny",label:"IPDeny (Country Zones)"},{value:"abuseipdb",label:"AbuseIPDB"},{value:"manual",label:"Manual"}],_=[{value:"cn",label:"China"},{value:"ru",label:"Russia"},{value:"kp",label:"North Korea"},{value:"ir",label:"Iran"},{value:"ng",label:"Nigeria"},{value:"ro",label:"Romania"},{value:"br",label:"Brazil"},{value:"in",label:"India"},{value:"pk",label:"Pakistan"},{value:"id",label:"Indonesia"},{value:"vn",label:"Vietnam"},{value:"ua",label:"Ukraine"},{value:"th",label:"Thailand"},{value:"ph",label:"Philippines"},{value:"bd",label:"Bangladesh"},{value:"eg",label:"Egypt"},{value:"tr",label:"Turkey"},{value:"mx",label:"Mexico"},{value:"ar",label:"Argentina"},{value:"co",label:"Colombia"}];class IPSet extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/ipset"})}}class IPSetList extends e.Collection{constructor(e={}){super({ModelClass:IPSet,endpoint:"/api/incident/ipset",...e})}}const C={create:{title:"Create IP Set",size:"md",fields:[{name:"kind",type:"select",label:"What do you want to block?",required:!0,options:S,value:"country",columns:12},{name:"country_code",type:"select",label:"Country",required:!0,options:_,help:"Select a country to block. CIDRs are fetched automatically from IPDeny.",columns:8,showWhen:{field:"kind",value:"country"}},{name:"source_key",type:"text",label:"API Key",required:!0,placeholder:"Your AbuseIPDB API key",help:"Get a free key at abuseipdb.com. Never stored in plaintext.",columns:12,showWhen:{field:"kind",value:"abuse"}},{name:"source_url",type:"url",label:"Source URL",required:!0,placeholder:"https://example.com/datacenter-ranges.txt",help:"URL to a plain text file with one CIDR per line.",columns:12,showWhen:{field:"kind",value:"datacenter"}},{name:"data",type:"textarea",label:"CIDR List",rows:8,placeholder:"# One CIDR per line\n192.0.2.0/24\n198.51.100.0/24\n203.0.113.0/24",help:"Enter IP ranges in CIDR notation. Lines starting with # are ignored.",columns:12,showWhen:{field:"kind",value:"custom"}},{name:"name",type:"text",label:"Name",required:!0,placeholder:"e.g., abuse_ips, dc_aws",help:"Unique identifier. Used as the kernel ipset name.",columns:6,showWhen:{field:"kind",value:"country",negate:!0}},{name:"description",type:"text",label:"Description",placeholder:"Human-readable label",columns:6,showWhen:{field:"kind",value:"country",negate:!0}},{name:"is_enabled",type:"switch",label:"Enable immediately",value:!0,help:"When enabled, CIDRs are synced to the fleet and traffic is blocked.",columns:4}]},edit:{title:"Edit IP Set",size:"md",fields:[{name:"name",type:"text",label:"Name",required:!0,columns:6},{name:"kind",type:"select",label:"Kind",options:f,disabled:!0,columns:3},{name:"is_enabled",type:"switch",label:"Enabled",columns:3},{name:"description",type:"text",label:"Description",columns:12},{name:"source",type:"select",label:"Source",options:w,columns:6},{name:"source_url",type:"url",label:"Source URL",columns:6},{name:"source_key",type:"text",label:"API Key",placeholder:"Leave blank to keep current key",help:"Write-only — current value is never shown.",columns:12}]}};IPSet.EDIT_FORM=C.edit;class S3Bucket extends e.Model{constructor(e={}){super(e,{endpoint:"/api/aws/s3/bucket"})}}class S3BucketList extends e.Collection{constructor(e={}){super({ModelClass:S3Bucket,endpoint:"/api/aws/s3/bucket",size:10,...e})}}exports.Assistant=AssistantConversation,exports.AssistantConversation=AssistantConversation,exports.AssistantConversationList=AssistantConversationList,exports.AssistantSkill=AssistantSkill,exports.AssistantSkillList=AssistantSkillList,exports.BouncerDevice=BouncerDevice,exports.BouncerDeviceList=BouncerDeviceList,exports.BouncerSignal=BouncerSignal,exports.BouncerSignalList=BouncerSignalList,exports.BouncerSignature=BouncerSignature,exports.BouncerSignatureForms={create:{title:"Create Signature",fields:[{name:"sig_type",type:"select",label:"Signature Type",required:!0,columns:6,options:[{value:"user_agent",label:"User Agent"},{value:"ip_pattern",label:"IP Pattern"},{value:"fingerprint",label:"Fingerprint"},{value:"behavior",label:"Behavior"},{value:"header",label:"Header"},{value:"cookie",label:"Cookie"}]},{name:"value",type:"text",label:"Value",required:!0,columns:6,help:"The pattern or value to match against."},{name:"confidence",type:"number",label:"Confidence",columns:6,default:80,min:0,max:100,help:"Confidence level from 0 to 100."},{name:"notes",type:"textarea",label:"Notes",columns:12,help:"Optional notes about this signature."},{name:"is_active",type:"switch",label:"Active",columns:6,default:!0}]},edit:{title:"Edit Signature",fields:[{name:"sig_type",type:"select",label:"Signature Type",required:!0,columns:6,options:[{value:"user_agent",label:"User Agent"},{value:"ip_pattern",label:"IP Pattern"},{value:"fingerprint",label:"Fingerprint"},{value:"behavior",label:"Behavior"},{value:"header",label:"Header"},{value:"cookie",label:"Cookie"}]},{name:"value",type:"text",label:"Value",required:!0,columns:6,help:"The pattern or value to match against."},{name:"confidence",type:"number",label:"Confidence",columns:6,default:80,min:0,max:100,help:"Confidence level from 0 to 100."},{name:"notes",type:"textarea",label:"Notes",columns:12,help:"Optional notes about this signature."},{name:"is_active",type:"switch",label:"Active",columns:6,default:!0}]}},exports.BouncerSignatureList=BouncerSignatureList,exports.BundleByOptions=n,exports.CommonBlockCountries=_,exports.CommonCategoryOptions=r,exports.CommonEventFields=i,exports.CommonScopeOptions=c,exports.ComparatorOptions=s,exports.DAY_LABELS=["Mon","Tue","Wed","Thu","Fri","Sat","Sun"],exports.EmailDomain=EmailDomain,exports.EmailDomainForms={create:{title:"Add Email Domain",fields:[{name:"name",type:"text",label:"Domain Name",placeholder:"example.com",required:!0,columns:12,help:"Enter the root domain to verify with SES (no protocol)."},{name:"region",type:"text",label:"AWS Region (optional)",placeholder:"us-east-1",columns:12,help:"Optional. Defaults to project AWS_REGION if omitted."},{name:"aws_key",type:"text",label:"AWS Key (optional)",placeholder:"enter your AWS Key with SES permissions",columns:12,help:"Optional, AWS Key with SES permissions"},{name:"aws_secret",type:"text",label:"AWS Secret (optional)",placeholder:"enter your AWS Secret with SES permissions",columns:12,help:"Optional, AWS Secret with SES permissions"},{name:"receiving_enabled",type:"switch",label:"Enable Inbound Receiving",columns:12,help:"Catch-all SES receipt rule to S3 + SNS; routing is done in-app."}]},edit:{title:"Edit Email Domain",fields:[{name:"name",type:"text",label:"Domain Name",placeholder:"example.com",required:!0,columns:12,readonly:!0,help:"Domain name cannot be changed after creation."},{name:"region",type:"text",label:"AWS Region",placeholder:"us-east-1",columns:12},{name:"receiving_enabled",type:"switch",label:"Enable Inbound Receiving",columns:12},{name:"s3_inbound_bucket",type:"text",label:"Inbound S3 Bucket",placeholder:"my-inbound-bucket",columns:12},{name:"s3_inbound_prefix",type:"text",label:"Inbound S3 Prefix",placeholder:"inbound/example.com/",columns:12},{name:"dns_mode",type:"select",label:"DNS Mode",options:[{value:"manual",text:"Manual (show records)"},{value:"godaddy",text:"GoDaddy (apply via API)"}],columns:12}]},credentials:{fields:[{name:"region",type:"select",label:"AWS Region (optional)",placeholder:"us-east-1",options:[{value:"us-east-1",text:"US East (N. Virginia)"},{value:"us-east-2",text:"US East (Ohio)"},{value:"us-west-1",text:"US West (N. California)"},{value:"us-west-2",text:"US West (Oregon)"},{value:"ca-central-1",text:"Canada (Central)"},{value:"eu-west-1",text:"Europe (Ireland)"},{value:"eu-west-2",text:"Europe (London)"},{value:"eu-west-3",text:"Europe (Paris)"},{value:"eu-central-1",text:"Europe (Frankfurt)"},{value:"eu-north-1",text:"Europe (Stockholm)"},{value:"eu-south-1",text:"Europe (Milan)"},{value:"ap-southeast-2",text:"Asia Pacific (Sydney)"}],columns:12,help:"Optional. Defaults to project AWS_REGION if omitted."},{name:"aws_key",type:"text",label:"AWS Key (optional)",placeholder:"enter your AWS Key with SES permissions",columns:12,help:"Optional, AWS Key with SES permissions"},{name:"aws_secret",type:"text",label:"AWS Secret (optional)",placeholder:"enter your AWS Secret with SES permissions",columns:12,help:"Optional, AWS Secret with SES permissions"}]},onboard:{title:"Onboard Domain",fields:[{type:"header",text:"Receiving",level:6,className:"mt-2"},{name:"receiving_enabled",type:"switch",label:"Enable Inbound Receiving",columns:12},{name:"s3_inbound_bucket",type:"text",label:"Inbound S3 Bucket",placeholder:"my-inbound-bucket",columns:12,help:"Required if receiving is enabled."},{name:"s3_inbound_prefix",type:"text",label:"Inbound S3 Prefix",placeholder:"inbound/example.com/",columns:12},{type:"header",text:"MAIL FROM (optional)",level:6,className:"mt-3"},{name:"ensure_mail_from",type:"switch",label:"Ensure MAIL FROM Setup",columns:12},{name:"mail_from_subdomain",type:"text",label:"MAIL FROM Subdomain",placeholder:"feedback",columns:12},{type:"header",text:"DNS",level:6,className:"mt-3"},{name:"dns_mode",type:"select",label:"DNS Mode",options:[{value:"manual",text:"Manual (show records)"},{value:"godaddy",text:"GoDaddy (apply via API)"}],value:"manual",columns:12},{name:"godaddy_key",type:"text",label:"GoDaddy API Key",columns:12,help:"Required when DNS Mode = GoDaddy."},{name:"godaddy_secret",type:"password",label:"GoDaddy API Secret",columns:12},{type:"header",text:"Webhook Endpoints",level:6,className:"mt-3"},{name:"endpoints.bounce",type:"text",label:"Bounce Endpoint",placeholder:"https://portal.example.com/api/aws/sns/bounce",columns:12},{name:"endpoints.complaint",type:"text",label:"Complaint Endpoint",placeholder:"https://portal.example.com/api/aws/sns/complaint",columns:12},{name:"endpoints.delivery",type:"text",label:"Delivery Endpoint",placeholder:"https://portal.example.com/api/aws/sns/delivery",columns:12},{name:"endpoints.inbound",type:"text",label:"Inbound Endpoint",placeholder:"https://portal.example.com/api/aws/sns/inbound",columns:12}]}},exports.EmailDomainList=EmailDomainList,exports.EmailTemplate=EmailTemplate,exports.EmailTemplateForms={create:{title:"Add Email Template",fields:[{name:"name",type:"text",label:"Name",required:!0,cols:12},{name:"subject_template",type:"text",label:"Subject Template",cols:12},{type:"tabset",name:"settingsTabs",tabs:[{label:"HTML",fields:[{name:"html_template",type:"htmlpreview",label:"HTML Template",rows:16,cols:12}]},{label:"TEXT",fields:[{name:"text_template",type:"textarea",label:"Text Template",rows:16,cols:12}]}]}]},edit:{title:"Edit Email Template",fields:[{name:"name",type:"text",label:"Name",required:!0,cols:12},{name:"subject_template",type:"text",label:"Subject Template",cols:12},{type:"tabset",name:"settingsTabs",tabs:[{label:"HTML",fields:[{name:"html_template",type:"textarea",label:"HTML Template",rows:16,cols:12}]},{label:"TEXT",fields:[{name:"text_template",type:"textarea",label:"Text Template",rows:16,cols:12}]}]}]}},exports.EmailTemplateList=EmailTemplateList,exports.IPSet=IPSet,exports.IPSetForms=C,exports.IPSetKindBadgeOptions=f,exports.IPSetKindOptions=S,exports.IPSetList=IPSetList,exports.IPSetSourceOptions=w,exports.Incident=Incident,exports.IncidentEvent=IncidentEvent,exports.IncidentEventForms=a,exports.IncidentEventList=IncidentEventList,exports.IncidentForms={create:{title:"Create Incident",fields:[{type:"tabset",name:"settingsTabs",tabs:[{label:"General",fields:[{name:"title",type:"text",label:"Title",required:!0,columns:12},{name:"details",type:"textarea",label:"Details",required:!0,columns:12}]},{label:"Advanced",fields:[{name:"priority",type:"select",label:"Priority",options:["1","2","3","4","5","6","7","8","9","10"],value:5,columns:6},{name:"status",type:"select",label:"Status",value:"open",options:["open","investigating","resolved","closed","paused","ignored"],columns:6},{name:"category",type:"text",label:"Category",value:"manual",columns:6}]},{label:"Metadata",fields:[{name:"metadata",type:"json",label:"Metadata",value:{example:"hello world"},rows:15,columns:12}]}]}]},edit:{title:"Edit Incident",fields:[{name:"category",type:"text",label:"Category",cols:6},{name:"status",type:"select",label:"Status",placeholder:"Select Status",options:["open","investigating","resolved","closed","paused","ignored"],cols:3},{name:"priority",type:"text",label:"Priority"},{name:"details",type:"textarea",label:"Description",placeholder:"Enter Name",cols:12},{name:"model_name",type:"text",label:"Model",placeholder:"Enter Model",cols:8},{name:"model_id",type:"text",label:"Model ID",placeholder:"Enter Model ID",cols:4}]}},exports.IncidentHistory=IncidentHistory,exports.IncidentHistoryList=IncidentHistoryList,exports.IncidentList=IncidentList,exports.IncidentRule=IncidentRule,exports.IncidentRuleList=IncidentRuleList,exports.IncidentRuleSet=IncidentRuleSet,exports.IncidentRuleSetList=IncidentRuleSetList,exports.IncidentStats=IncidentStats,exports.Job=Job,exports.JobEvent=JobEvent,exports.JobEventList=JobEventList,exports.JobForms={publish:{title:"Publish New Job",fields:[{name:"func",type:"text",label:"Function",required:!0,placeholder:"myapp.jobs.send_email",help:"Module path to job function"},{name:"channel",type:"text",label:"Channel",value:"default",help:'Queue channel (default: "default")'},{name:"payload",type:"textarea",label:"Payload (JSON)",required:!0,rows:8,placeholder:'{\n "key": "value"\n}',help:"JSON data passed to the job function"},{name:"delay",type:"number",label:"Delay (seconds)",min:0,help:"Delay execution by specified seconds"},{name:"run_at",type:"datetime-local",label:"Run At",help:"Schedule for specific date/time"},{name:"max_retries",type:"number",label:"Max Retries",value:3,min:0,max:10},{name:"expires_in",type:"number",label:"Expires In (seconds)",value:900,min:60,help:"Job will expire if not completed in this time"},{name:"broadcast",type:"switch",label:"Broadcast to All Workers",help:"Execute on all available workers"}]},retry:{title:"Retry Job",fields:[{name:"delay",type:"number",label:"Delay (seconds)",value:0,min:0,help:"Delay before retry (0 = immediate)"}]},clone:{title:"Clone Job",fields:[{name:"channel",type:"text",label:"Channel",help:"Override channel for cloned job"},{name:"payload",type:"textarea",label:"Modified Payload (JSON)",rows:8,help:"Modified payload for cloned job"},{name:"delay",type:"number",label:"Delay (seconds)",min:0}]}},exports.JobList=JobList,exports.JobLog=JobLog,exports.JobLogList=JobLogList,exports.JobRunner=JobRunner,exports.JobRunnerForms={broadcast:{title:"Broadcast Command",fields:[{name:"command",type:"select",label:"Command",required:!0,options:[{value:"status",label:"Status Check"},{value:"pause",label:"Pause Processing"},{value:"resume",label:"Resume Processing"},{value:"reload",label:"Reload Configuration"},{value:"shutdown",label:"Shutdown All Runners"}],help:"Command to send to all runners"},{name:"timeout",type:"number",label:"Timeout (seconds)",value:2,min:.5,max:10,step:.5,help:"How long to wait for responses"}]}},exports.JobRunnerList=JobRunnerList,exports.JobsEngineStats=JobsEngineStats,exports.LoginEvent=LoginEvent,exports.LoginEventList=LoginEventList,exports.Mailbox=Mailbox,exports.MailboxForms=g,exports.MailboxList=MailboxList,exports.MatchByOptions=l,exports.PhoneNumber=PhoneNumber,exports.PhoneNumberList=PhoneNumberList,exports.Phonehub=x,exports.PublicMessage=PublicMessage,exports.PublicMessageCategoryOptions=[{value:"billing",label:"Billing"},{value:"account",label:"Account"},{value:"bug",label:"Bug"},{value:"other",label:"Other"}],exports.PublicMessageKindOptions=[{value:"contact_us",label:"Contact Us"},{value:"support",label:"Support"}],exports.PublicMessageList=PublicMessageList,exports.PublicMessageMetadataLabels={company:"Company",category:"Category",severity:"Severity",referrer:"Referrer",landing_page:"Landing Page",utm_source:"UTM Source",utm_medium:"UTM Medium",utm_campaign:"UTM Campaign",utm_term:"UTM Term",utm_content:"UTM Content"},exports.PublicMessageSeverityOptions=[{value:"low",label:"Low"},{value:"normal",label:"Normal"},{value:"high",label:"High"}],exports.PublicMessageStatusOptions=[{value:"open",label:"Open"},{value:"closed",label:"Closed"}],exports.PushConfig=PushConfig,exports.PushConfigForms=m,exports.PushConfigList=PushConfigList,exports.PushDelivery=PushDelivery,exports.PushDeliveryList=PushDeliveryList,exports.PushDevice=PushDevice,exports.PushDeviceList=PushDeviceList,exports.PushTemplate=PushTemplate,exports.PushTemplateForms=b,exports.PushTemplateList=PushTemplateList,exports.Rule=Rule,exports.RuleForms=p,exports.RuleList=RuleList,exports.RuleSet=RuleSet,exports.RuleSetForms=d,exports.RuleSetList=RuleSetList,exports.S3Bucket=S3Bucket,exports.S3BucketForms={create:{title:"Add S3 Bucket",fields:[{name:"bucket_name",type:"text",label:"Name",placeholder:"bucket name",help:"Enter a universally unique name for the bucket",required:!0,cols:12},{name:"is_public",type:"switch",label:"Is Public",cols:12}]},edit:{title:"Edit S3 Bucket",fields:[{name:"bucket_name",type:"text",label:"Name",placeholder:"bucket name",help:"Enter a universally unique name for the bucket",required:!0,cols:12},{name:"is_public",type:"switch",label:"Is Public",cols:12}]}},exports.S3BucketList=S3BucketList,exports.SMS=SMS,exports.SMSList=SMSList,exports.ScheduledTask=ScheduledTask,exports.ScheduledTaskForms={create:{title:"Create Scheduled Task",fields:[{name:"name",type:"text",label:"Name",placeholder:"Daily report",required:!0,columns:12},{name:"description",type:"textarea",label:"Description",placeholder:"What this task does...",columns:12},{name:"task_type",type:"select",label:"Task Type",required:!0,columns:6,options:[{value:"llm",label:"LLM Prompt"},{value:"job",label:"Backend Job"},{value:"webhook",label:"Webhook"}]},{name:"enabled",type:"switch",label:"Enabled",columns:6,value:!0},{name:"run_times",type:"text",label:"Run Times (HH:MM)",placeholder:"09:00",required:!0,columns:6,help:'Comma-separated 24h times, max 2. e.g. "09:00, 17:00"'},{name:"run_days",type:"text",label:"Run Days",placeholder:"0,1,2,3,4",columns:6,help:"Comma-separated day numbers (Mon=0). Leave empty for every day."},{name:"run_once",type:"switch",label:"Run Once",columns:6,help:"Task runs once then disables itself."},{name:"max_retries",type:"number",label:"Max Retries",columns:6,value:0},{name:"notify",type:"text",label:"Notify",placeholder:"in_app, email",columns:12,help:"Comma-separated: email, in_app, sms, push"}]},edit:{title:"Edit Scheduled Task",fields:[{name:"name",type:"text",label:"Name",required:!0,columns:12},{name:"description",type:"textarea",label:"Description",columns:12},{name:"enabled",type:"switch",label:"Enabled",columns:6},{name:"run_times",type:"text",label:"Run Times (HH:MM)",required:!0,columns:6,help:"Comma-separated 24h times, max 2."},{name:"run_days",type:"text",label:"Run Days",columns:6,help:"Comma-separated day numbers (Mon=0). Leave empty for every day."},{name:"run_once",type:"switch",label:"Run Once",columns:6},{name:"max_retries",type:"number",label:"Max Retries",columns:6},{name:"notify",type:"text",label:"Notify",placeholder:"in_app, email",columns:12,help:"Comma-separated: email, in_app, sms, push"}]}},exports.ScheduledTaskList=ScheduledTaskList,exports.SentMessage=SentMessage,exports.SentMessageForms={view:{title:"Sent Message Details",fields:[{name:"id",type:"text",label:"ID",readonly:!0,cols:6},{name:"ses_message_id",type:"text",label:"SES Message ID",readonly:!0,cols:6},{name:"from_email",type:"text",label:"From",readonly:!0,cols:12},{name:"to",type:"textarea",label:"To",readonly:!0,rows:2,cols:12},{name:"cc",type:"textarea",label:"CC",readonly:!0,rows:2,cols:12},{name:"bcc",type:"textarea",label:"BCC",readonly:!0,rows:2,cols:12},{name:"subject",type:"text",label:"Subject",readonly:!0,cols:12},{name:"status",type:"text",label:"Status",readonly:!0,cols:6},{name:"status_reason",type:"textarea",label:"Status Reason",readonly:!0,rows:3,cols:12},{name:"created",type:"text",label:"Created",readonly:!0,cols:6}]}},exports.SentMessageList=SentMessageList,exports.TaskResult=TaskResult,exports.TaskResultList=TaskResultList,exports.Ticket=Ticket,exports.TicketCategories=h,exports.TicketForms=v,exports.TicketList=TicketList,exports.TicketNote=TicketNote,exports.TicketNoteList=TicketNoteList,exports.ValueTypeOptions=o;
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./chunks/Collection-CtSGTegm.js"),t=require("./chunks/User-DRbw-wOB.js");class LoginEvent extends e.Model{constructor(e={}){super(e,{endpoint:"/api/account/logins"})}}class LoginEventList extends e.Collection{constructor(e={}){super({ModelClass:LoginEvent,endpoint:"/api/account/logins",...e})}}class IncidentStats extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/stats",requiresId:!1})}}class IncidentEvent extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/event"})}}class IncidentEventList extends e.Collection{constructor(e={}){super({ModelClass:IncidentEvent,endpoint:"/api/incident/event",size:10,...e})}}const a={edit:{title:"Edit Incident Event",fields:[{name:"category",type:"select",label:"Category",placeholder:"Select category",options:()=>Incident.COMPONENTS,editable:!0,force_top:!0,cols:6},{name:"incident",type:"text",label:"Incident",placeholder:"Enter Incident ID",cols:6},{name:"description",type:"textarea",label:"Description",placeholder:"Enter Description",cols:12},{name:"details",type:"textarea",label:"Details",placeholder:"Enter Details",cols:12},{name:"component",type:"text",label:"Component",placeholder:"Enter Component",cols:8},{name:"component_id",type:"text",label:"Component ID",placeholder:"Enter Component ID",cols:4}]}};class Incident extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/incident"})}}class IncidentList extends e.Collection{constructor(e={}){super({ModelClass:Incident,endpoint:"/api/incident/incident",size:10,...e})}}class IncidentRuleSet extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/event/ruleset"})}}class IncidentRuleSetList extends e.Collection{constructor(e={}){super({ModelClass:IncidentRuleSet,endpoint:"/api/incident/event/ruleset",size:10,...e})}}class IncidentRule extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/event/ruleset/rule"})}}class IncidentRuleList extends e.Collection{constructor(e={}){super({ModelClass:IncidentRule,endpoint:"/api/incident/event/ruleset/rule",size:10,...e})}}class IncidentHistory extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/incident/history"})}}class IncidentHistoryList extends e.Collection{constructor(e={}){super({ModelClass:IncidentHistory,endpoint:"/api/incident/incident/history",size:10,...e})}}const n=[{value:0,label:"No Bundling"},{value:1,label:"By Hostname"},{value:2,label:"By Model Name"},{value:3,label:"By Model Name + ID"},{value:4,label:"By Source IP"},{value:5,label:"By Hostname + Model Name"},{value:6,label:"By Hostname + Model Name + ID"},{value:7,label:"By Source IP + Model Name"},{value:8,label:"By Source IP + Model Name + ID"},{value:9,label:"By Source IP + Hostname"}],l=[{value:0,label:"ALL (must match all rules)"},{value:1,label:"ANY (match any rule)"}],s=[{value:"==",label:"Equal (==)"},{value:"eq",label:"Equal (eq)"},{value:">",label:"Greater Than (>)"},{value:">=",label:"Greater Than or Equal (>=)"},{value:"<",label:"Less Than (<)"},{value:"<=",label:"Less Than or Equal (<=)"},{value:"contains",label:"Contains"},{value:"regex",label:"Regular Expression"}],o=[{value:"str",label:"String"},{value:"int",label:"Integer"},{value:"float",label:"Float"},{value:"bool",label:"Boolean"}],i=[{value:"level",label:"Level",description:"Event level (error, warning, info, debug)",meta:{type:"str"}},{value:"source_ip",label:"Source IP Address",description:"IP address of the event source",meta:{type:"str"}},{value:"rule_id",label:"Rule ID",description:"Numeric rule identifier",meta:{type:"int"}},{value:"hostname",label:"Hostname",description:"Hostname where event occurred",meta:{type:"str"}},{value:"component",label:"Component",description:"System component name",meta:{type:"str"}},{value:"component_id",label:"Component ID",description:"Component identifier",meta:{type:"str"}},{value:"category",label:"Category",description:"Event category (ossec, auth, api_error, etc.)",meta:{type:"str"}},{value:"description",label:"Description",description:"Event description text",meta:{type:"str"}},{value:"details",label:"Details",description:"Additional event details",meta:{type:"str"}},{value:"alert_id",label:"Alert ID",description:"Numeric alert identifier",meta:{type:"int"}},{value:"severity",label:"Severity",description:"Numeric severity level",meta:{type:"int"}},{value:"user",label:"User",description:"Username associated with event",meta:{type:"str"}},{value:"action",label:"Action",description:"Action that triggered the event",meta:{type:"str"}},{value:"status",label:"Status",description:"Status value or code",meta:{type:"str"}},{value:"status_code",label:"Status Code",description:"Numeric status code (e.g., HTTP status)",meta:{type:"int"}},{value:"message",label:"Message",description:"Event message text",meta:{type:"str"}},{value:"path",label:"Path",description:"File path or URL path",meta:{type:"str"}},{value:"title",label:"Title",description:"OSSEC Title",meta:{type:"str"}},{value:"country_code",label:"Country Code",description:"Country code associated with event",meta:{type:"str"}},{value:"region",label:"Region",description:"Region associated with event",meta:{type:"str"}},{value:"city",label:"City",description:"City associated with event",meta:{type:"str"}},{value:"http_user_agent",label:"HTTP User Agent",description:"User agent string associated with event",meta:{type:"str"}},{value:"request_path",label:"Request Path",description:"Request path associated with event",meta:{type:"str"}},{value:"method",label:"Method",description:"HTTP method or function name",meta:{type:"str"}}],r=["global","account","ossec","api","realtime","auth","login:unknown","invalid_password","sms:login_unknown","totp:login_unknown","reset:unknown","magic:unknown","token:unknown","api_denied","unauthenticated","view_permission_denied","edit_permission_denied","group_member_permission_denied","user_permission_denied","security_alert","totp:confirm_failed","totp:login_failed","totp:recovery_used","sms:otp_failed","email:no_mailbox","email:send_failed","sms:send_failed","email_change:bad_password","email_change:requested","account:deactivate_requested","phone_verify:invalid","email_verify:invalid","oauth:unlink_guard_error","rest_error","mojo_rest_error","rest_value_error"],c=r;class RuleSet extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/event/ruleset"})}}class RuleSetList extends e.Collection{constructor(e={}){super({ModelClass:RuleSet,endpoint:"/api/incident/event/ruleset",...e})}}const u=[{value:0,label:"Disabled — each event gets its own incident"},{value:5,label:"5 minutes"},{value:10,label:"10 minutes"},{value:15,label:"15 minutes"},{value:30,label:"30 minutes"},{value:60,label:"1 hour"},{value:120,label:"2 hours"},{value:360,label:"6 hours"},{value:720,label:"12 hours"},{value:1440,label:"1 day"},{value:null,label:"No limit — bundle forever"}],d={create:{title:"Create RuleSet",size:"lg",fields:[{type:"tabset",name:"rulesetTabs",tabs:[{label:"General",fields:[{name:"name",type:"text",label:"Name",required:!0,placeholder:"e.g., Brute Force Detection",columns:6},{name:"match_by",type:"select",label:"Match Logic",value:0,options:l,tooltip:"ALL = every condition must match. ANY = at least one",columns:6},{name:"category",type:"combo",label:"Scope / Category",required:!0,options:r,allowCustom:!0,placeholder:"Type or select...",tooltip:"Scope or event category to match. Use * as a catch-all",columns:6},{name:"priority",type:"number",label:"Evaluation Priority",value:10,required:!0,tooltip:"Lower number = evaluated first",columns:6},{name:"is_active",type:"switch",label:"Active",value:!0,tooltip:"Inactive rules are skipped during event processing",columns:6},{name:"metadata.delete_on_resolution",type:"switch",label:"Delete on Resolution",value:!1,tooltip:"Incidents are permanently deleted when resolved or closed (CASCADE)",columns:6}]},{label:"Bundling",fields:[{name:"bundle_by",type:"select",label:"Bundle By",value:4,options:n,tooltip:"How to group related events into one incident",columns:6},{name:"bundle_minutes",type:"select",label:"Bundle Window",value:30,options:u,tooltip:"Events outside this window create a new incident",columns:6},{name:"bundle_by_rule_set",type:"switch",label:"Bundle by RuleSet",value:!0,tooltip:"Group events matched by this rule into the same incident",columns:6}]},{label:"Thresholds",fields:[{type:"html",columns:12,html:'<div class="alert alert-info small mb-3">\n <i class="bi bi-info-circle me-1"></i>\n <strong>How thresholds work:</strong> Events accumulate in "pending" status.\n Once trigger count is reached, the handler fires and the incident becomes "new".\n Leave empty to fire immediately on the first event.\n </div>'},{name:"trigger_count",type:"number",label:"Trigger Count",placeholder:"Empty = immediate",tooltip:"Number of events before the handler fires",columns:4},{name:"trigger_window",type:"number",label:"Trigger Window (min)",placeholder:"Empty = all events",tooltip:"Only count events within this many minutes",columns:4},{name:"retrigger_every",type:"number",label:"Re-trigger Every",placeholder:"Empty = once",tooltip:"Re-fire handler every N additional events after initial trigger",columns:4}]},{label:"Handler",fields:[{name:"handler",type:"text",label:"Handler Chain",placeholder:"e.g., block://?ttl=3600,ticket://?priority=8",tooltip:"Chain multiple handlers with commas",columns:12},{type:"html",columns:12,html:'<div class="alert alert-light border small mb-0">\n <code>block://?ttl=3600</code> — Block source IP<br>\n <code>ticket://?priority=8</code> — Create ticket<br>\n <code>email://perm@manage_security</code> — Email notification<br>\n <code>sms://perm@manage_security</code> — SMS notification<br>\n <code>notify://perm@manage_security</code> — In-app + push<br>\n <code>llm://</code> — LLM triage agent<br>\n <code>job://myapp.module.function</code> — Async job\n </div>'}]}]}]},edit:{title:"Edit RuleSet",size:"lg",fields:[{type:"tabset",name:"rulesetTabs",tabs:[{label:"General",fields:[{name:"name",type:"text",label:"Name",required:!0,placeholder:"e.g., Brute Force Detection",columns:6},{name:"match_by",type:"select",label:"Match Logic",options:l,tooltip:"ALL = every condition must match. ANY = at least one",columns:6},{name:"category",type:"combo",label:"Scope / Category",options:r,allowCustom:!0,required:!0,placeholder:"Type or select...",tooltip:"Scope or event category to match. Use * as a catch-all",columns:6},{name:"priority",type:"number",label:"Evaluation Priority",required:!0,tooltip:"Lower number = evaluated first",columns:6},{name:"is_active",type:"switch",label:"Active",tooltip:"Inactive rules are skipped during event processing",columns:6},{name:"metadata.delete_on_resolution",type:"switch",label:"Delete on Resolution",tooltip:"Incidents are permanently deleted when resolved or closed (CASCADE)",columns:6}]},{label:"Bundling",fields:[{name:"bundle_by",type:"select",label:"Bundle By",options:n,tooltip:"How to group related events into one incident",columns:6},{name:"bundle_minutes",type:"select",label:"Bundle Window",options:u,tooltip:"Events outside this window create a new incident",columns:6},{name:"bundle_by_rule_set",type:"switch",label:"Bundle by RuleSet",tooltip:"Group events matched by this rule into the same incident",columns:6}]},{label:"Thresholds",fields:[{type:"html",columns:12,html:'<div class="alert alert-info small mb-3">\n <i class="bi bi-info-circle me-1"></i>\n <strong>How thresholds work:</strong> Events accumulate in "pending" status.\n Once trigger count is reached, the handler fires and the incident becomes "new".\n Leave empty to fire immediately on the first event.\n </div>'},{name:"trigger_count",type:"number",label:"Trigger Count",placeholder:"Empty = immediate",tooltip:"Number of events before the handler fires",columns:4},{name:"trigger_window",type:"number",label:"Trigger Window (min)",placeholder:"Empty = all events",tooltip:"Only count events within this window toward the trigger count",columns:4},{name:"retrigger_every",type:"number",label:"Re-trigger Every",placeholder:"Empty = once",tooltip:"Re-fire handler every N additional events after initial trigger",columns:4}]},{label:"Handler",fields:[{name:"handler",type:"text",label:"Handler Chain",placeholder:"e.g., block://?ttl=3600,ticket://?priority=8",tooltip:"Chain multiple handlers with commas",columns:12},{type:"html",columns:12,html:'<div class="alert alert-light border small mb-0">\n <code>block://?ttl=3600</code> — Block source IP<br>\n <code>ticket://?priority=8</code> — Create ticket<br>\n <code>email://perm@manage_security</code> — Email notification<br>\n <code>sms://perm@manage_security</code> — SMS notification<br>\n <code>notify://perm@manage_security</code> — In-app + push<br>\n <code>llm://</code> — LLM triage agent<br>\n <code>job://myapp.module.function</code> — Async job\n </div>'}]}]}]}};class Rule extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/event/ruleset/rule"})}}class RuleList extends e.Collection{constructor(e={}){super({ModelClass:Rule,endpoint:"/api/incident/event/ruleset/rule",...e})}}const p={create:{title:"Create Rule",fields:[{name:"name",type:"text",label:"Name",required:!0,placeholder:"Enter rule name",cols:12},{name:"field_name",type:"combo",label:"Field Name",required:!0,placeholder:"Select or enter field name...",options:i,allowCustom:!0,showDescription:!0,help:"Select a common field or type a custom field name",cols:8},{name:"index",type:"select",label:"Index",required:!0,start:0,end:14,step:1,cols:4},{name:"comparator",type:"select",label:"Comparator",required:!0,options:s,cols:6},{name:"value_type",type:"select",label:"Value Type",required:!0,options:o,value:"str",cols:6},{name:"value",type:"textarea",label:"Value",required:!0,placeholder:"Enter comparison value",cols:12}]},edit:{title:"Edit Rule",fields:[{name:"name",type:"text",label:"Name",required:!0,placeholder:"Enter rule name",cols:12},{name:"field_name",type:"combo",label:"Field Name",required:!0,placeholder:"Select or enter field name...",options:i,allowCustom:!0,showDescription:!0,help:"Select a common field or type a custom field name",cols:12},{name:"comparator",type:"select",label:"Comparator",required:!0,options:s,cols:6},{name:"value_type",type:"select",label:"Value Type",required:!0,options:o,cols:6},{name:"value",type:"text",label:"Value",required:!0,placeholder:"Enter comparison value",cols:12}]}};RuleSet.EDIT_FORM=d.edit,RuleSet.ADD_FORM=d.create,RuleSet.CREATE_FORM=d.create,RuleSet.BundleByOptions=n,RuleSet.MatchByOptions=l,Rule.EDIT_FORM=p.edit,Rule.ADD_FORM=p.create,Rule.CREATE_FORM=p.create,Rule.ComparatorOptions=s,Rule.ValueTypeOptions=o;class PushDevice extends e.Model{constructor(e={}){super(e,{endpoint:"/api/account/devices/push"})}}class PushDeviceList extends e.Collection{constructor(e={}){super({ModelClass:PushDevice,endpoint:"/api/account/devices/push",...e})}}class PushTemplate extends e.Model{constructor(e={}){super(e,{endpoint:"/api/account/devices/push/templates"})}}class PushTemplateList extends e.Collection{constructor(e={}){super({ModelClass:PushTemplate,endpoint:"/api/account/devices/push/templates",...e})}}class PushConfig extends e.Model{constructor(e={}){super(e,{endpoint:"/api/account/devices/push/config"})}}class PushConfigList extends e.Collection{constructor(e={}){super({ModelClass:PushConfig,endpoint:"/api/account/devices/push/config",...e})}}class PushDelivery extends e.Model{constructor(e={}){super(e,{endpoint:"/api/account/devices/push/deliveries"})}}class PushDeliveryList extends e.Collection{constructor(e={}){super({ModelClass:PushDelivery,endpoint:"/api/account/devices/push/deliveries",...e})}}const m={create:{title:"Create Push Configuration",fields:[{name:"name",label:"Name",required:!0},{type:"collection",name:"group",label:"Group (optional)",Collection:t.GroupList,labelField:"name",valueField:"id"},{name:"fcm_service_account",label:"Service Account",type:"textarea",rows:10}]},edit:{title:"Edit Push Configuration",fields:[{name:"name",label:"Name",required:!0},{type:"collection",name:"group",label:"Group (optional)",Collection:t.GroupList,labelField:"name",valueField:"id"},{name:"fcm_service_account",label:"Service Account",type:"textarea",rows:10},{name:"is_active",label:"Is Active",type:"switch",value:!0}]}},b={edit:{title:"Edit Push Template",fields:[{name:"name",label:"Name",required:!0},{name:"category",label:"Category",required:!0},{type:"collection",name:"group",label:"Group (optional)",Collection:t.GroupList,labelField:"name",valueField:"id",defaultParams:{is_active:!0}},{name:"title_template",label:"Title Template",required:!0},{name:"body_template",label:"Body Template",type:"textarea",required:!0},{name:"action_url",label:"Action URL"},{name:"priority",label:"Priority",type:"select",options:["high","normal"]},{name:"variables",label:"Variables",type:"json",help:"JSON format"},{name:"is_active",label:"Is Active",type:"switch"}]}};m.create=m.edit,b.create=b.edit;class Ticket extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/ticket"})}}class TicketList extends e.Collection{constructor(e={}){super({ModelClass:Ticket,endpoint:"/api/incident/ticket",...e})}}const h={ticket:"Ticket",bug:"Bug",feature:"Feature Request",incident:"Incident",security:"Security Incident",fulfillment:"Fulfillment",new_user:"New User",new_group:"New Group",qa:"Quality Assurance"},y=Object.entries(h).map(([e,t])=>({value:e,label:t})),v={create:{title:"Create Ticket",fields:[{name:"title",type:"text",label:"Title",required:!0,cols:12},{name:"description",type:"textarea",label:"Description",required:!1,cols:12},{name:"category",type:"select",label:"Category",options:y,cols:12,value:"ticket"},{name:"priority",type:"number",label:"Priority",value:5,cols:6},{name:"status",type:"select",label:"Status",options:["new","open","paused","resolved","qa","ignored"],cols:6,value:"new"},{type:"collection",name:"assignee",label:"Assignee",Collection:t.UserList,labelField:"display_name",valueField:"id",cols:12},{type:"collection",name:"incident",label:"Incident",Collection:IncidentList,labelField:"title",valueField:"id",cols:12}]},edit:{title:"Edit Ticket",fields:[{name:"title",type:"text",label:"Title",required:!0,cols:12},{name:"description",type:"textarea",label:"Description",required:!1,cols:12},{name:"category",type:"select",label:"Category",options:y,cols:12},{name:"priority",type:"number",label:"Priority",cols:6},{name:"status",type:"select",label:"Status",options:["new","open","paused","resolved","qa","ignored"],cols:6},{type:"collection",name:"assignee",label:"Assignee",Collection:t.UserList,labelField:"display_name",valueField:"id",cols:12},{type:"collection",name:"incident",label:"Incident",Collection:IncidentList,labelField:"title",valueField:"id",cols:12}]}};class TicketNote extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/ticket/note"})}}class TicketNoteList extends e.Collection{constructor(e={}){super({ModelClass:TicketNote,endpoint:"/api/incident/ticket/note",...e})}}class AssistantConversation extends e.Model{constructor(e={}){super(e,{endpoint:"/api/assistant/conversation"})}}class AssistantConversationList extends e.Collection{constructor(e={}){super({ModelClass:AssistantConversation,endpoint:"/api/assistant/conversation",size:50,...e})}}class AssistantSkill extends e.Model{constructor(e={}){super(e,{endpoint:"/api/assistant/skill"})}}class AssistantSkillList extends e.Collection{constructor(e={}){super({ModelClass:AssistantSkill,endpoint:"/api/assistant/skill",size:50,...e})}}class Job extends e.Model{constructor(e={}){super(e,{endpoint:"/api/jobs/job"})}async cancel(){const e=await this.save({cancel_request:!0});return e.success&&e.data.status&&this.set("cancel_requested",!0),e}async retry(e=null){const t=e?{retry_request:{retry:!0,delay:e}}:{retry_request:!0},a=await this.save(t);return a.success&&a.data.status&&a.data.new_job_id?{...a,newJobId:a.data.new_job_id,originalJobId:a.data.original_job_id}:a}async getDetailedStatus(){const e=await this.save({get_status:!0});return e.success&&e.data.status&&this.set(e.data.data),e}async cloneJob(e={}){const t={publish_job:{payload:e,...e}},a=await this.save(t);return a.success&&a.data.status&&a.data.job_id?{...a,newJobId:a.data.job_id,templateJobId:a.data.template_job_id}:a}isActive(){const e=this.get("status");return["pending","running"].includes(e)}isTerminal(){const e=this.get("status");return["completed","failed","canceled","expired"].includes(e)}canRetry(){const e=this.get("status");return["failed","canceled","expired"].includes(e)&&!1!==this.get("is_retriable")}canCancel(){const e=this.get("status");return["pending","running"].includes(e)&&!this.get("cancel_requested")}getStatusBadgeClass(){return{pending:"bg-primary",running:"bg-success",completed:"bg-info",failed:"bg-danger",canceled:"bg-secondary",expired:"bg-warning"}[this.get("status")]||"bg-secondary"}getStatusIcon(){return{pending:"bi-hourglass",running:"bi-arrow-repeat",completed:"bi-check-circle",failed:"bi-x-octagon",canceled:"bi-x-circle",expired:"bi-clock"}[this.get("status")]||"bi-question-circle"}getEvents(){return this.get("recent_events")||[]}getFormattedDuration(){const e=this.get("duration_ms");return e&&0!==e?e<1e3?`${e}ms`:e<6e4?`${(e/1e3).toFixed(1)}s`:e<36e5?`${(e/6e4).toFixed(1)}m`:`${(e/36e5).toFixed(1)}h`:"N/A"}getQueuePosition(){return this.get("queue_position")}hasExpired(){const e=this.get("expires_at");return!!e&&new Date(e)</* @__PURE__ */new Date}getRunnerId(){return this.get("runner_id")}getPayload(){return this.get("payload")||{}}getMetadata(){return this.get("metadata")||{}}}class JobList extends e.Collection{constructor(e={}){super({ModelClass:Job,endpoint:"/api/jobs/job",...e})}async fetchByStatus(e,t={}){return this.fetch({status:e,...t})}async fetchByChannel(e,t={}){return this.fetch({channel:e,...t})}async fetchPending(e={}){return this.fetchByStatus("pending",e)}async fetchRunning(e={}){return this.fetchByStatus("running",e)}async fetchCompleted(e={}){return this.fetchByStatus("completed",e)}async fetchFailed(e={}){return this.fetchByStatus("failed",e)}async fetchScheduled(e={}){return this.fetch({scheduled:!0,...e})}}Job.publish=async function(t){return await e.rest.POST("/api/jobs/publish",t)},Job.getStats=async function(){const t=await e.rest.GET("/api/jobs/stats");return t.success?t.data:null},Job.getHealth=async function(t=null){const a=t?`/api/jobs/health/${t}`:"/api/jobs/health",n=e.rest.GET(a);return n.success?n.data:null},Job.test=async function(){return await e.rest.POST("/api/jobs/test",{})},Job.tests=async function(){return await e.rest.POST("/api/jobs/tests",{})},Job.clearStuck=async function(t=null){const a=t?{channel:t}:{};return await e.rest.POST("/api/jobs/control/clear-stuck",a)},Job.clearChannel=async function(t){return await e.rest.POST("/api/jobs/control/clear-queue",{channel:t,confirm:"yes"})},Job.cleanConsumers=async function(){return await e.rest.POST("/api/jobs/control/cleanup-consumers")},Job.purgeJobs=async function(t){return await e.rest.POST("/api/jobs/control/purge",{days_old:t})};class JobLog extends e.Model{constructor(e={}){super(e,{endpoint:"/api/jobs/logs"})}}class JobLogList extends e.Collection{constructor(e={}){super({endpoint:"/api/jobs/logs",model:JobLog,...e})}}class JobEvent extends e.Model{constructor(e={}){super(e,{endpoint:"/api/jobs/event"})}}class JobEventList extends e.Collection{constructor(e={}){super({endpoint:"/api/jobs/event",model:JobEvent,...e})}}class JobsEngineStats extends e.Model{constructor(e={}){super(e,{endpoint:"/api/jobs/stats",requiresId:!1})}}class EmailDomain extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/aws/email/domain",...t})}async onboard(e={},t={}){if(!this.id)return await this.showError("Cannot onboard domain without ID"),{success:!1,status:400,error:"Missing domain id"};try{const a=`${this.buildUrl(this.id)}/onboard`;return await this.rest.POST(a,e,t.params)}catch(a){return{success:!1,status:a?.status||500,error:a?.message||"Failed to onboard domain"}}}async audit(e={}){if(!this.id)return await this.showError("Cannot audit domain without ID"),{success:!1,status:400,error:"Missing domain id"};const t=(e.method||"GET").toUpperCase(),a=`${this.buildUrl(this.id)}/audit`;try{return"POST"===t?await this.rest.POST(a,e.data||{},e.params):await this.rest.GET(a,e.params)}catch(n){return{success:!1,status:n?.status||500,error:n?.message||"Failed to audit domain"}}}async reconcile(e={},t={}){if(!this.id)return await this.showError("Cannot reconcile domain without ID"),{success:!1,status:400,error:"Missing domain id"};try{const a=`${this.buildUrl(this.id)}/reconcile`;return await this.rest.POST(a,e,t.params)}catch(a){return{success:!1,status:a?.status||500,error:a?.message||"Failed to reconcile domain"}}}static async onboardById(e,t={},a={}){const n=new EmailDomain({id:e},a);return await n.onboard(t,a)}static async auditById(e,t={}){const a=new EmailDomain({id:e},t);return await a.audit(t)}static async reconcileById(e,t={},a={}){const n=new EmailDomain({id:e},a);return await n.reconcile(t,a)}}class EmailDomainList extends e.Collection{constructor(e={}){super({ModelClass:EmailDomain,endpoint:"/api/aws/email/domain",size:10,...e})}}class Mailbox extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/aws/email/mailbox",...t})}}Mailbox.sendEmail=async function(t){return await e.rest.POST("/api/aws/email/send",t)};class MailboxList extends e.Collection{constructor(e={}){super({ModelClass:Mailbox,endpoint:"/api/aws/email/mailbox",size:10,...e})}}const g={create:{title:"Add Mailbox",fields:[{type:"collection",name:"domain",label:"Domain",Collection:EmailDomainList,labelField:"name",valueField:"id",maxItems:10,placeholder:"Search domains...",emptyFetch:!1,required:!0,debounceMs:300,columns:12},{name:"email",type:"email",label:"Email Address",placeholder:"support@example.com",required:!0,columns:12},{name:"allow_inbound",type:"switch",label:"Allow Inbound",columns:6},{name:"allow_outbound",type:"switch",label:"Allow Outbound",defaultValue:!0,columns:6},{name:"is_system_default",type:"switch",label:"System Default",columns:6},{name:"is_domain_default",type:"switch",label:"Domain Default",columns:6},{name:"async_handler",type:"text",label:"Async Handler (optional)",placeholder:"myapp.handlers.process_support",columns:12,help:"Module:function to process inbound messages via task system"}]},edit:{title:"Edit Mailbox",fields:[{name:"email",type:"email",label:"Email Address",required:!0,columns:12},{name:"allow_inbound",type:"switch",label:"Allow Inbound",columns:6},{name:"allow_outbound",type:"switch",label:"Allow Outbound",columns:6},{name:"is_system_default",type:"switch",label:"System Default",columns:6},{name:"is_domain_default",type:"switch",label:"Domain Default",columns:6},{name:"async_handler",type:"text",label:"Async Handler (optional)",placeholder:"myapp.handlers.process_support",columns:12}]}};class SentMessage extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/aws/email/sent",...t})}}class SentMessageList extends e.Collection{constructor(e={}){super({ModelClass:SentMessage,endpoint:"/api/aws/email/sent",size:10,...e})}}class EmailTemplate extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/aws/email/template",...t})}}class EmailTemplateList extends e.Collection{constructor(e={}){super({ModelClass:EmailTemplate,endpoint:"/api/aws/email/template",size:10,...e})}}class PublicMessage extends e.Model{constructor(e={}){super(e,{endpoint:"/api/account/public_message"})}}class PublicMessageList extends e.Collection{constructor(e={}){super({ModelClass:PublicMessage,endpoint:"/api/account/public_message",size:25,...e})}}class PhoneNumber extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/phonehub/number",...t})}static async normalize(t,a="US"){const n={phone_number:t};a&&(n.country_code=a);const l=await e.rest.POST("/api/phonehub/number/normalize",n),s=l?.data??l;return!0===s?.status||!0===s?.success?{success:!0,phone_number:s?.data?.phone_number??s?.phone_number,data:s?.data??s,response:l}:{success:!1,error:s?.error||"Normalization failed",response:l}}static async lookup(t,a={}){const n=await e.rest.POST("/api/phonehub/number/lookup",{phone_number:t,...a}),l=n?.data??n;if(!0===l?.status||!0===l?.success){const e=l?.data??{};return{success:!0,model:new PhoneNumber(e,{endpoint:"/api/phonehub/number"}),data:e,response:n}}return{success:!1,error:l?.error||"Phone lookup failed",response:n}}}class PhoneNumberList extends e.Collection{constructor(e={}){super({ModelClass:PhoneNumber,endpoint:"/api/phonehub/number",size:10,...e})}}class SMS extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/phonehub/sms",...t})}static async send(t={}){const a=await e.rest.POST("/api/phonehub/sms/send",t),n=a?.data??a;if(!0===n?.status||!0===n?.success){const e=n?.data??{};return{success:!0,model:new SMS(e,{endpoint:"/api/phonehub/sms"}),data:e,response:a}}return{success:!1,error:n?.error||"Failed to send SMS",response:a}}}class SMSList extends e.Collection{constructor(e={}){super({ModelClass:SMS,endpoint:"/api/phonehub/sms",size:10,...e})}}const x={PhoneNumber:PhoneNumber,PhoneNumberList:PhoneNumberList,SMS:SMS,SMSList:SMSList};class JobRunner extends e.Model{constructor(e={}){e.runner_id&&!e.id&&(e.id=e.runner_id),super(e,{endpoint:"/api/jobs/runners",idAttribute:"runner_id"})}async ping(e=2){const t=this.getApp();if(!t||!t.rest)throw new Error("App or REST client not available");const a=await t.rest.POST("/api/jobs/runners/ping",{runner_id:this.get("runner_id"),timeout:e});return a.success&&a.data.status&&(this.set("last_heartbeat",/* @__PURE__ */(new Date).toISOString()),this.set("ping_status",a.data.ping_status||"success")),a}async shutdown(e=!0){const t=this.getApp();if(!t||!t.rest)throw new Error("App or REST client not available");const a=await t.rest.POST("/api/jobs/runners/shutdown",{runner_id:this.get("runner_id"),graceful:e});return a.success&&a.data.status&&this.set("alive",!1),a}getChannels(){return this.get("channels")||[]}getStatusBadgeClass(){return this.get("alive")?"bg-success":"bg-danger"}getStatusIcon(){return this.get("alive")?"bi-check-circle-fill":"bi-x-octagon-fill"}isActive(){return!0===this.get("alive")}isHealthy(){if(!this.isActive())return!1;const e=this.get("last_heartbeat");return!!e&&(Date.now()-new Date(e).getTime())/1e3<120}getFormattedHeartbeatAge(){const e=this.get("last_heartbeat");if(!e)return"Never";const t=(Date.now()-new Date(e).getTime())/1e3;return t<60?`${Math.round(t)}s ago`:t<3600?`${Math.round(t/60)}m ago`:`${Math.round(t/3600)}h ago`}getUtilization(){const e=(this.get("jobs_processed")||0)+(this.get("jobs_failed")||0);return e>10?100:Math.min(10*e,100)}getFormattedUptime(){const e=this.get("started");if(!e)return"Unknown";const t=new Date(e),a=/* @__PURE__ */new Date-t,n=Math.floor(a/1e3);return n<60?`${n}s`:n<3600?`${Math.floor(n/60)}m`:n<86400?`${Math.floor(n/3600)}h`:`${Math.floor(n/86400)}d`}getWorkerInfo(){const e=this.get("jobs_processed")||0,t=this.get("jobs_failed")||0;return{processed:e,failed:t,total:e+t,alive:this.get("alive"),utilization:this.getUtilization()}}getDisplayName(){const e=this.get("runner_id");if(!e)return"Unknown Runner";const t=e.split("-");return t.length>1?t[0]:e}canControl(){return!0===this.get("alive")}}class JobRunnerList extends e.Collection{constructor(e={}){super({ModelClass:JobRunner,endpoint:"/api/jobs/runners",...e})}getActive(){return this.where(e=>e.isActive())}getByChannel(e){return this.where(t=>t.getChannels().includes(e))}getHealthy(){return this.where(e=>e.isHealthy())}getTotalProcessed(){return this.models.reduce((e,t)=>e+(t.get("jobs_processed")||0),0)}getTotalFailed(){return this.models.reduce((e,t)=>e+(t.get("jobs_failed")||0),0)}getSystemHealth(){if(0===this.models.length)return 0;const e=this.models.filter(e=>e.get("alive")).length;return Math.round(e/this.models.length*100)}getAllChannels(){const e=/* @__PURE__ */new Set;return this.models.forEach(t=>{t.getChannels().forEach(t=>e.add(t))}),Array.from(e).sort()}}JobRunner.ping=async function(e,t=2){const a="undefined"!=typeof window&&window.app;if(!a||!a.rest)throw new Error("App or REST client not available");return await a.rest.POST("/api/jobs/runners/ping",{runner_id:e,timeout:t})},JobRunner.shutdown=async function(e,t=!0){const a="undefined"!=typeof window&&window.app;if(!a||!a.rest)throw new Error("App or REST client not available");return await a.rest.POST("/api/jobs/runners/shutdown",{runner_id:e,graceful:t})},JobRunner.broadcast=async function(e,t={},a=2){const n="undefined"!=typeof window&&window.app;if(!n||!n.rest)throw new Error("App or REST client not available");return await n.rest.POST("/api/jobs/runners/broadcast",{command:e,data:t,timeout:a})},JobRunner.broadcastStatus=async function(e=2){return JobRunner.broadcast("status",{},e)},JobRunner.broadcastShutdown=async function(e=2){return JobRunner.broadcast("shutdown",{},e)},JobRunner.broadcastPause=async function(e=2){return JobRunner.broadcast("pause",{},e)},JobRunner.broadcastResume=async function(e=2){return JobRunner.broadcast("resume",{},e)},JobRunner.broadcastReload=async function(e=2){return JobRunner.broadcast("reload",{},e)};class ScheduledTask extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/jobs/scheduled_task",...t})}}class ScheduledTaskList extends e.Collection{constructor(e={}){super({ModelClass:ScheduledTask,endpoint:"/api/jobs/scheduled_task",size:25,...e})}}class TaskResult extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/jobs/task_result",...t})}}class TaskResultList extends e.Collection{constructor(e={}){super({ModelClass:TaskResult,endpoint:"/api/jobs/task_result",size:25,...e})}}class BouncerDevice extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/account/bouncer/device",...t})}}class BouncerDeviceList extends e.Collection{constructor(e={}){super({ModelClass:BouncerDevice,endpoint:"/api/account/bouncer/device",size:25,...e})}}class BouncerSignal extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/account/bouncer/signal",...t})}}class BouncerSignalList extends e.Collection{constructor(e={}){super({ModelClass:BouncerSignal,endpoint:"/api/account/bouncer/signal",size:25,...e})}}class BouncerSignature extends e.Model{constructor(e={},t={}){super(e,{endpoint:"/api/account/bouncer/signature",...t})}}class BouncerSignatureList extends e.Collection{constructor(e={}){super({ModelClass:BouncerSignature,endpoint:"/api/account/bouncer/signature",size:25,...e})}}const S=[{value:"country",label:"Country — Block all traffic from a country"},{value:"abuse",label:"Abuse Feed — Import known attacker IPs"},{value:"datacenter",label:"Datacenter — Block datacenter/hosting ranges"},{value:"custom",label:"Custom — Define your own CIDR list"}],f=[{value:"country",label:"Country"},{value:"abuse",label:"Abuse Feed"},{value:"datacenter",label:"Datacenter"},{value:"custom",label:"Custom"}],w=[{value:"ipdeny",label:"IPDeny (Country Zones)"},{value:"abuseipdb",label:"AbuseIPDB"},{value:"manual",label:"Manual"}],_=[{value:"cn",label:"China"},{value:"ru",label:"Russia"},{value:"kp",label:"North Korea"},{value:"ir",label:"Iran"},{value:"ng",label:"Nigeria"},{value:"ro",label:"Romania"},{value:"br",label:"Brazil"},{value:"in",label:"India"},{value:"pk",label:"Pakistan"},{value:"id",label:"Indonesia"},{value:"vn",label:"Vietnam"},{value:"ua",label:"Ukraine"},{value:"th",label:"Thailand"},{value:"ph",label:"Philippines"},{value:"bd",label:"Bangladesh"},{value:"eg",label:"Egypt"},{value:"tr",label:"Turkey"},{value:"mx",label:"Mexico"},{value:"ar",label:"Argentina"},{value:"co",label:"Colombia"}];class IPSet extends e.Model{constructor(e={}){super(e,{endpoint:"/api/incident/ipset"})}}class IPSetList extends e.Collection{constructor(e={}){super({ModelClass:IPSet,endpoint:"/api/incident/ipset",...e})}}const C={create:{title:"Create IP Set",size:"md",fields:[{name:"kind",type:"select",label:"What do you want to block?",required:!0,options:S,value:"country",columns:12},{name:"country_code",type:"select",label:"Country",required:!0,options:_,help:"Select a country to block. CIDRs are fetched automatically from IPDeny.",columns:8,showWhen:{field:"kind",value:"country"}},{name:"source_key",type:"text",label:"API Key",required:!0,placeholder:"Your AbuseIPDB API key",help:"Get a free key at abuseipdb.com. Never stored in plaintext.",columns:12,showWhen:{field:"kind",value:"abuse"}},{name:"source_url",type:"url",label:"Source URL",required:!0,placeholder:"https://example.com/datacenter-ranges.txt",help:"URL to a plain text file with one CIDR per line.",columns:12,showWhen:{field:"kind",value:"datacenter"}},{name:"data",type:"textarea",label:"CIDR List",rows:8,placeholder:"# One CIDR per line\n192.0.2.0/24\n198.51.100.0/24\n203.0.113.0/24",help:"Enter IP ranges in CIDR notation. Lines starting with # are ignored.",columns:12,showWhen:{field:"kind",value:"custom"}},{name:"name",type:"text",label:"Name",required:!0,placeholder:"e.g., abuse_ips, dc_aws",help:"Unique identifier. Used as the kernel ipset name.",columns:6,showWhen:{field:"kind",value:"country",negate:!0}},{name:"description",type:"text",label:"Description",placeholder:"Human-readable label",columns:6,showWhen:{field:"kind",value:"country",negate:!0}},{name:"is_enabled",type:"switch",label:"Enable immediately",value:!0,help:"When enabled, CIDRs are synced to the fleet and traffic is blocked.",columns:4}]},edit:{title:"Edit IP Set",size:"md",fields:[{name:"name",type:"text",label:"Name",required:!0,columns:6},{name:"kind",type:"select",label:"Kind",options:f,disabled:!0,columns:3},{name:"is_enabled",type:"switch",label:"Enabled",columns:3},{name:"description",type:"text",label:"Description",columns:12},{name:"source",type:"select",label:"Source",options:w,columns:6},{name:"source_url",type:"url",label:"Source URL",columns:6},{name:"source_key",type:"text",label:"API Key",placeholder:"Leave blank to keep current key",help:"Write-only — current value is never shown.",columns:12}]}};IPSet.EDIT_FORM=C.edit;class S3Bucket extends e.Model{constructor(e={}){super(e,{endpoint:"/api/aws/s3/bucket"})}}class S3BucketList extends e.Collection{constructor(e={}){super({ModelClass:S3Bucket,endpoint:"/api/aws/s3/bucket",size:10,...e})}}exports.Assistant=AssistantConversation,exports.AssistantConversation=AssistantConversation,exports.AssistantConversationList=AssistantConversationList,exports.AssistantSkill=AssistantSkill,exports.AssistantSkillList=AssistantSkillList,exports.BouncerDevice=BouncerDevice,exports.BouncerDeviceList=BouncerDeviceList,exports.BouncerSignal=BouncerSignal,exports.BouncerSignalList=BouncerSignalList,exports.BouncerSignature=BouncerSignature,exports.BouncerSignatureForms={create:{title:"Create Signature",fields:[{name:"sig_type",type:"select",label:"Signature Type",required:!0,columns:6,options:[{value:"user_agent",label:"User Agent"},{value:"ip_pattern",label:"IP Pattern"},{value:"fingerprint",label:"Fingerprint"},{value:"behavior",label:"Behavior"},{value:"header",label:"Header"},{value:"cookie",label:"Cookie"}]},{name:"value",type:"text",label:"Value",required:!0,columns:6,help:"The pattern or value to match against."},{name:"confidence",type:"number",label:"Confidence",columns:6,default:80,min:0,max:100,help:"Confidence level from 0 to 100."},{name:"notes",type:"textarea",label:"Notes",columns:12,help:"Optional notes about this signature."},{name:"is_active",type:"switch",label:"Active",columns:6,default:!0}]},edit:{title:"Edit Signature",fields:[{name:"sig_type",type:"select",label:"Signature Type",required:!0,columns:6,options:[{value:"user_agent",label:"User Agent"},{value:"ip_pattern",label:"IP Pattern"},{value:"fingerprint",label:"Fingerprint"},{value:"behavior",label:"Behavior"},{value:"header",label:"Header"},{value:"cookie",label:"Cookie"}]},{name:"value",type:"text",label:"Value",required:!0,columns:6,help:"The pattern or value to match against."},{name:"confidence",type:"number",label:"Confidence",columns:6,default:80,min:0,max:100,help:"Confidence level from 0 to 100."},{name:"notes",type:"textarea",label:"Notes",columns:12,help:"Optional notes about this signature."},{name:"is_active",type:"switch",label:"Active",columns:6,default:!0}]}},exports.BouncerSignatureList=BouncerSignatureList,exports.BundleByOptions=n,exports.CommonBlockCountries=_,exports.CommonCategoryOptions=r,exports.CommonEventFields=i,exports.CommonScopeOptions=c,exports.ComparatorOptions=s,exports.DAY_LABELS=["Mon","Tue","Wed","Thu","Fri","Sat","Sun"],exports.EmailDomain=EmailDomain,exports.EmailDomainForms={create:{title:"Add Email Domain",fields:[{name:"name",type:"text",label:"Domain Name",placeholder:"example.com",required:!0,columns:12,help:"Enter the root domain to verify with SES (no protocol)."},{name:"region",type:"text",label:"AWS Region (optional)",placeholder:"us-east-1",columns:12,help:"Optional. Defaults to project AWS_REGION if omitted."},{name:"aws_key",type:"text",label:"AWS Key (optional)",placeholder:"enter your AWS Key with SES permissions",columns:12,help:"Optional, AWS Key with SES permissions"},{name:"aws_secret",type:"text",label:"AWS Secret (optional)",placeholder:"enter your AWS Secret with SES permissions",columns:12,help:"Optional, AWS Secret with SES permissions"},{name:"receiving_enabled",type:"switch",label:"Enable Inbound Receiving",columns:12,help:"Catch-all SES receipt rule to S3 + SNS; routing is done in-app."}]},edit:{title:"Edit Email Domain",fields:[{name:"name",type:"text",label:"Domain Name",placeholder:"example.com",required:!0,columns:12,readonly:!0,help:"Domain name cannot be changed after creation."},{name:"region",type:"text",label:"AWS Region",placeholder:"us-east-1",columns:12},{name:"receiving_enabled",type:"switch",label:"Enable Inbound Receiving",columns:12},{name:"s3_inbound_bucket",type:"text",label:"Inbound S3 Bucket",placeholder:"my-inbound-bucket",columns:12},{name:"s3_inbound_prefix",type:"text",label:"Inbound S3 Prefix",placeholder:"inbound/example.com/",columns:12},{name:"dns_mode",type:"select",label:"DNS Mode",options:[{value:"manual",text:"Manual (show records)"},{value:"godaddy",text:"GoDaddy (apply via API)"}],columns:12}]},credentials:{fields:[{name:"region",type:"select",label:"AWS Region (optional)",placeholder:"us-east-1",options:[{value:"us-east-1",text:"US East (N. Virginia)"},{value:"us-east-2",text:"US East (Ohio)"},{value:"us-west-1",text:"US West (N. California)"},{value:"us-west-2",text:"US West (Oregon)"},{value:"ca-central-1",text:"Canada (Central)"},{value:"eu-west-1",text:"Europe (Ireland)"},{value:"eu-west-2",text:"Europe (London)"},{value:"eu-west-3",text:"Europe (Paris)"},{value:"eu-central-1",text:"Europe (Frankfurt)"},{value:"eu-north-1",text:"Europe (Stockholm)"},{value:"eu-south-1",text:"Europe (Milan)"},{value:"ap-southeast-2",text:"Asia Pacific (Sydney)"}],columns:12,help:"Optional. Defaults to project AWS_REGION if omitted."},{name:"aws_key",type:"text",label:"AWS Key (optional)",placeholder:"enter your AWS Key with SES permissions",columns:12,help:"Optional, AWS Key with SES permissions"},{name:"aws_secret",type:"text",label:"AWS Secret (optional)",placeholder:"enter your AWS Secret with SES permissions",columns:12,help:"Optional, AWS Secret with SES permissions"}]},onboard:{title:"Onboard Domain",fields:[{type:"header",text:"Receiving",level:6,className:"mt-2"},{name:"receiving_enabled",type:"switch",label:"Enable Inbound Receiving",columns:12},{name:"s3_inbound_bucket",type:"text",label:"Inbound S3 Bucket",placeholder:"my-inbound-bucket",columns:12,help:"Required if receiving is enabled."},{name:"s3_inbound_prefix",type:"text",label:"Inbound S3 Prefix",placeholder:"inbound/example.com/",columns:12},{type:"header",text:"MAIL FROM (optional)",level:6,className:"mt-3"},{name:"ensure_mail_from",type:"switch",label:"Ensure MAIL FROM Setup",columns:12},{name:"mail_from_subdomain",type:"text",label:"MAIL FROM Subdomain",placeholder:"feedback",columns:12},{type:"header",text:"DNS",level:6,className:"mt-3"},{name:"dns_mode",type:"select",label:"DNS Mode",options:[{value:"manual",text:"Manual (show records)"},{value:"godaddy",text:"GoDaddy (apply via API)"}],value:"manual",columns:12},{name:"godaddy_key",type:"text",label:"GoDaddy API Key",columns:12,help:"Required when DNS Mode = GoDaddy."},{name:"godaddy_secret",type:"password",label:"GoDaddy API Secret",columns:12},{type:"header",text:"Webhook Endpoints",level:6,className:"mt-3"},{name:"endpoints.bounce",type:"text",label:"Bounce Endpoint",placeholder:"https://portal.example.com/api/aws/sns/bounce",columns:12},{name:"endpoints.complaint",type:"text",label:"Complaint Endpoint",placeholder:"https://portal.example.com/api/aws/sns/complaint",columns:12},{name:"endpoints.delivery",type:"text",label:"Delivery Endpoint",placeholder:"https://portal.example.com/api/aws/sns/delivery",columns:12},{name:"endpoints.inbound",type:"text",label:"Inbound Endpoint",placeholder:"https://portal.example.com/api/aws/sns/inbound",columns:12}]}},exports.EmailDomainList=EmailDomainList,exports.EmailTemplate=EmailTemplate,exports.EmailTemplateForms={create:{title:"Add Email Template",fields:[{name:"name",type:"text",label:"Name",required:!0,cols:12},{name:"subject_template",type:"text",label:"Subject Template",cols:12},{type:"tabset",name:"settingsTabs",tabs:[{label:"HTML",fields:[{name:"html_template",type:"htmlpreview",label:"HTML Template",rows:16,cols:12}]},{label:"TEXT",fields:[{name:"text_template",type:"textarea",label:"Text Template",rows:16,cols:12}]}]}]},edit:{title:"Edit Email Template",fields:[{name:"name",type:"text",label:"Name",required:!0,cols:12},{name:"subject_template",type:"text",label:"Subject Template",cols:12},{type:"tabset",name:"settingsTabs",tabs:[{label:"HTML",fields:[{name:"html_template",type:"textarea",label:"HTML Template",rows:16,cols:12}]},{label:"TEXT",fields:[{name:"text_template",type:"textarea",label:"Text Template",rows:16,cols:12}]}]}]}},exports.EmailTemplateList=EmailTemplateList,exports.IPSet=IPSet,exports.IPSetForms=C,exports.IPSetKindBadgeOptions=f,exports.IPSetKindOptions=S,exports.IPSetList=IPSetList,exports.IPSetSourceOptions=w,exports.Incident=Incident,exports.IncidentEvent=IncidentEvent,exports.IncidentEventForms=a,exports.IncidentEventList=IncidentEventList,exports.IncidentForms={create:{title:"Create Incident",fields:[{type:"tabset",name:"settingsTabs",tabs:[{label:"General",fields:[{name:"title",type:"text",label:"Title",required:!0,columns:12},{name:"details",type:"textarea",label:"Details",required:!0,columns:12}]},{label:"Advanced",fields:[{name:"priority",type:"select",label:"Priority",options:["1","2","3","4","5","6","7","8","9","10"],value:5,columns:6},{name:"status",type:"select",label:"Status",value:"open",options:["open","investigating","resolved","closed","paused","ignored"],columns:6},{name:"category",type:"text",label:"Category",value:"manual",columns:6}]},{label:"Metadata",fields:[{name:"metadata",type:"json",label:"Metadata",value:{example:"hello world"},rows:15,columns:12}]}]}]},edit:{title:"Edit Incident",fields:[{name:"category",type:"text",label:"Category",cols:6},{name:"status",type:"select",label:"Status",placeholder:"Select Status",options:["open","investigating","resolved","closed","paused","ignored"],cols:3},{name:"priority",type:"text",label:"Priority"},{name:"details",type:"textarea",label:"Description",placeholder:"Enter Name",cols:12},{name:"model_name",type:"text",label:"Model",placeholder:"Enter Model",cols:8},{name:"model_id",type:"text",label:"Model ID",placeholder:"Enter Model ID",cols:4}]}},exports.IncidentHistory=IncidentHistory,exports.IncidentHistoryList=IncidentHistoryList,exports.IncidentList=IncidentList,exports.IncidentRule=IncidentRule,exports.IncidentRuleList=IncidentRuleList,exports.IncidentRuleSet=IncidentRuleSet,exports.IncidentRuleSetList=IncidentRuleSetList,exports.IncidentStats=IncidentStats,exports.Job=Job,exports.JobEvent=JobEvent,exports.JobEventList=JobEventList,exports.JobForms={publish:{title:"Publish New Job",fields:[{name:"func",type:"text",label:"Function",required:!0,placeholder:"myapp.jobs.send_email",help:"Module path to job function"},{name:"channel",type:"text",label:"Channel",value:"default",help:'Queue channel (default: "default")'},{name:"payload",type:"textarea",label:"Payload (JSON)",required:!0,rows:8,placeholder:'{\n "key": "value"\n}',help:"JSON data passed to the job function"},{name:"delay",type:"number",label:"Delay (seconds)",min:0,help:"Delay execution by specified seconds"},{name:"run_at",type:"datetime-local",label:"Run At",help:"Schedule for specific date/time"},{name:"max_retries",type:"number",label:"Max Retries",value:3,min:0,max:10},{name:"expires_in",type:"number",label:"Expires In (seconds)",value:900,min:60,help:"Job will expire if not completed in this time"},{name:"broadcast",type:"switch",label:"Broadcast to All Workers",help:"Execute on all available workers"}]},retry:{title:"Retry Job",fields:[{name:"delay",type:"number",label:"Delay (seconds)",value:0,min:0,help:"Delay before retry (0 = immediate)"}]},clone:{title:"Clone Job",fields:[{name:"channel",type:"text",label:"Channel",help:"Override channel for cloned job"},{name:"payload",type:"textarea",label:"Modified Payload (JSON)",rows:8,help:"Modified payload for cloned job"},{name:"delay",type:"number",label:"Delay (seconds)",min:0}]}},exports.JobList=JobList,exports.JobLog=JobLog,exports.JobLogList=JobLogList,exports.JobRunner=JobRunner,exports.JobRunnerForms={broadcast:{title:"Broadcast Command",fields:[{name:"command",type:"select",label:"Command",required:!0,options:[{value:"status",label:"Status Check"},{value:"pause",label:"Pause Processing"},{value:"resume",label:"Resume Processing"},{value:"reload",label:"Reload Configuration"},{value:"shutdown",label:"Shutdown All Runners"}],help:"Command to send to all runners"},{name:"timeout",type:"number",label:"Timeout (seconds)",value:2,min:.5,max:10,step:.5,help:"How long to wait for responses"}]}},exports.JobRunnerList=JobRunnerList,exports.JobsEngineStats=JobsEngineStats,exports.LoginEvent=LoginEvent,exports.LoginEventList=LoginEventList,exports.Mailbox=Mailbox,exports.MailboxForms=g,exports.MailboxList=MailboxList,exports.MatchByOptions=l,exports.PhoneNumber=PhoneNumber,exports.PhoneNumberList=PhoneNumberList,exports.Phonehub=x,exports.PublicMessage=PublicMessage,exports.PublicMessageCategoryOptions=[{value:"billing",label:"Billing"},{value:"account",label:"Account"},{value:"bug",label:"Bug"},{value:"other",label:"Other"}],exports.PublicMessageKindOptions=[{value:"contact_us",label:"Contact Us"},{value:"support",label:"Support"}],exports.PublicMessageList=PublicMessageList,exports.PublicMessageMetadataLabels={company:"Company",category:"Category",severity:"Severity",referrer:"Referrer",landing_page:"Landing Page",utm_source:"UTM Source",utm_medium:"UTM Medium",utm_campaign:"UTM Campaign",utm_term:"UTM Term",utm_content:"UTM Content"},exports.PublicMessageSeverityOptions=[{value:"low",label:"Low"},{value:"normal",label:"Normal"},{value:"high",label:"High"}],exports.PublicMessageStatusOptions=[{value:"open",label:"Open"},{value:"closed",label:"Closed"}],exports.PushConfig=PushConfig,exports.PushConfigForms=m,exports.PushConfigList=PushConfigList,exports.PushDelivery=PushDelivery,exports.PushDeliveryList=PushDeliveryList,exports.PushDevice=PushDevice,exports.PushDeviceList=PushDeviceList,exports.PushTemplate=PushTemplate,exports.PushTemplateForms=b,exports.PushTemplateList=PushTemplateList,exports.Rule=Rule,exports.RuleForms=p,exports.RuleList=RuleList,exports.RuleSet=RuleSet,exports.RuleSetForms=d,exports.RuleSetList=RuleSetList,exports.S3Bucket=S3Bucket,exports.S3BucketForms={create:{title:"Add S3 Bucket",fields:[{name:"bucket_name",type:"text",label:"Name",placeholder:"bucket name",help:"Enter a universally unique name for the bucket",required:!0,cols:12},{name:"is_public",type:"switch",label:"Is Public",cols:12}]},edit:{title:"Edit S3 Bucket",fields:[{name:"bucket_name",type:"text",label:"Name",placeholder:"bucket name",help:"Enter a universally unique name for the bucket",required:!0,cols:12},{name:"is_public",type:"switch",label:"Is Public",cols:12}]}},exports.S3BucketList=S3BucketList,exports.SMS=SMS,exports.SMSList=SMSList,exports.ScheduledTask=ScheduledTask,exports.ScheduledTaskForms={create:{title:"Create Scheduled Task",fields:[{name:"name",type:"text",label:"Name",placeholder:"Daily report",required:!0,columns:12},{name:"description",type:"textarea",label:"Description",placeholder:"What this task does...",columns:12},{name:"task_type",type:"select",label:"Task Type",required:!0,columns:6,options:[{value:"llm",label:"LLM Prompt"},{value:"job",label:"Backend Job"},{value:"webhook",label:"Webhook"}]},{name:"enabled",type:"switch",label:"Enabled",columns:6,value:!0},{name:"run_times",type:"text",label:"Run Times (HH:MM)",placeholder:"09:00",required:!0,columns:6,help:'Comma-separated 24h times, max 2. e.g. "09:00, 17:00"'},{name:"run_days",type:"text",label:"Run Days",placeholder:"0,1,2,3,4",columns:6,help:"Comma-separated day numbers (Mon=0). Leave empty for every day."},{name:"run_once",type:"switch",label:"Run Once",columns:6,help:"Task runs once then disables itself."},{name:"max_retries",type:"number",label:"Max Retries",columns:6,value:0},{name:"notify",type:"text",label:"Notify",placeholder:"in_app, email",columns:12,help:"Comma-separated: email, in_app, sms, push"}]},edit:{title:"Edit Scheduled Task",fields:[{name:"name",type:"text",label:"Name",required:!0,columns:12},{name:"description",type:"textarea",label:"Description",columns:12},{name:"enabled",type:"switch",label:"Enabled",columns:6},{name:"run_times",type:"text",label:"Run Times (HH:MM)",required:!0,columns:6,help:"Comma-separated 24h times, max 2."},{name:"run_days",type:"text",label:"Run Days",columns:6,help:"Comma-separated day numbers (Mon=0). Leave empty for every day."},{name:"run_once",type:"switch",label:"Run Once",columns:6},{name:"max_retries",type:"number",label:"Max Retries",columns:6},{name:"notify",type:"text",label:"Notify",placeholder:"in_app, email",columns:12,help:"Comma-separated: email, in_app, sms, push"}]}},exports.ScheduledTaskList=ScheduledTaskList,exports.SentMessage=SentMessage,exports.SentMessageForms={view:{title:"Sent Message Details",fields:[{name:"id",type:"text",label:"ID",readonly:!0,cols:6},{name:"ses_message_id",type:"text",label:"SES Message ID",readonly:!0,cols:6},{name:"from_email",type:"text",label:"From",readonly:!0,cols:12},{name:"to",type:"textarea",label:"To",readonly:!0,rows:2,cols:12},{name:"cc",type:"textarea",label:"CC",readonly:!0,rows:2,cols:12},{name:"bcc",type:"textarea",label:"BCC",readonly:!0,rows:2,cols:12},{name:"subject",type:"text",label:"Subject",readonly:!0,cols:12},{name:"status",type:"text",label:"Status",readonly:!0,cols:6},{name:"status_reason",type:"textarea",label:"Status Reason",readonly:!0,rows:3,cols:12},{name:"created",type:"text",label:"Created",readonly:!0,cols:6}]}},exports.SentMessageList=SentMessageList,exports.TaskResult=TaskResult,exports.TaskResultList=TaskResultList,exports.Ticket=Ticket,exports.TicketCategories=h,exports.TicketForms=v,exports.TicketList=TicketList,exports.TicketNote=TicketNote,exports.TicketNoteList=TicketNoteList,exports.ValueTypeOptions=o;
|
|
2
2
|
//# sourceMappingURL=admin-models.cjs.js.map
|