codex-autorunner 1.2.1__py3-none-any.whl → 1.3.0__py3-none-any.whl
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.
- codex_autorunner/bootstrap.py +26 -5
- codex_autorunner/core/config.py +176 -59
- codex_autorunner/core/filesystem.py +24 -0
- codex_autorunner/core/flows/controller.py +50 -12
- codex_autorunner/core/flows/runtime.py +8 -3
- codex_autorunner/core/hub.py +293 -16
- codex_autorunner/core/lifecycle_events.py +44 -5
- codex_autorunner/core/pma_delivery.py +81 -0
- codex_autorunner/core/pma_dispatches.py +224 -0
- codex_autorunner/core/pma_lane_worker.py +122 -0
- codex_autorunner/core/pma_queue.py +167 -18
- codex_autorunner/core/pma_reactive.py +91 -0
- codex_autorunner/core/pma_safety.py +58 -0
- codex_autorunner/core/pma_sink.py +104 -0
- codex_autorunner/core/pma_transcripts.py +183 -0
- codex_autorunner/core/safe_paths.py +117 -0
- codex_autorunner/housekeeping.py +77 -23
- codex_autorunner/integrations/agents/codex_backend.py +18 -12
- codex_autorunner/integrations/agents/wiring.py +2 -0
- codex_autorunner/integrations/app_server/client.py +31 -0
- codex_autorunner/integrations/app_server/supervisor.py +3 -0
- codex_autorunner/integrations/telegram/constants.py +1 -1
- codex_autorunner/integrations/telegram/handlers/commands/execution.py +16 -15
- codex_autorunner/integrations/telegram/handlers/commands/files.py +5 -8
- codex_autorunner/integrations/telegram/handlers/commands/github.py +10 -6
- codex_autorunner/integrations/telegram/handlers/commands/shared.py +9 -8
- codex_autorunner/integrations/telegram/handlers/commands/workspace.py +85 -2
- codex_autorunner/integrations/telegram/handlers/commands_runtime.py +29 -8
- codex_autorunner/integrations/telegram/helpers.py +30 -2
- codex_autorunner/integrations/telegram/ticket_flow_bridge.py +54 -3
- codex_autorunner/static/docChatCore.js +2 -0
- codex_autorunner/static/hub.js +59 -0
- codex_autorunner/static/index.html +70 -54
- codex_autorunner/static/notificationBell.js +173 -0
- codex_autorunner/static/notifications.js +154 -36
- codex_autorunner/static/pma.js +96 -35
- codex_autorunner/static/styles.css +415 -4
- codex_autorunner/static/utils.js +5 -1
- codex_autorunner/surfaces/cli/cli.py +206 -129
- codex_autorunner/surfaces/cli/template_repos.py +157 -0
- codex_autorunner/surfaces/web/app.py +193 -5
- codex_autorunner/surfaces/web/routes/file_chat.py +109 -61
- codex_autorunner/surfaces/web/routes/flows.py +125 -67
- codex_autorunner/surfaces/web/routes/pma.py +638 -57
- codex_autorunner/tickets/agent_pool.py +6 -1
- codex_autorunner/tickets/outbox.py +27 -14
- codex_autorunner/tickets/replies.py +4 -10
- codex_autorunner/tickets/runner.py +1 -0
- codex_autorunner/workspace/paths.py +8 -3
- {codex_autorunner-1.2.1.dist-info → codex_autorunner-1.3.0.dist-info}/METADATA +1 -1
- {codex_autorunner-1.2.1.dist-info → codex_autorunner-1.3.0.dist-info}/RECORD +55 -45
- {codex_autorunner-1.2.1.dist-info → codex_autorunner-1.3.0.dist-info}/WHEEL +0 -0
- {codex_autorunner-1.2.1.dist-info → codex_autorunner-1.3.0.dist-info}/entry_points.txt +0 -0
- {codex_autorunner-1.2.1.dist-info → codex_autorunner-1.3.0.dist-info}/licenses/LICENSE +0 -0
- {codex_autorunner-1.2.1.dist-info → codex_autorunner-1.3.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// GENERATED FILE - do not edit directly. Source: static_src/
|
|
2
|
+
import { api, escapeHtml, flash, openModal, resolvePath } from "./utils.js";
|
|
3
|
+
let bellInitialized = false;
|
|
4
|
+
let modalOpen = false;
|
|
5
|
+
let closeModal = null;
|
|
6
|
+
function getBellButtons() {
|
|
7
|
+
return Array.from(document.querySelectorAll(".notification-bell"));
|
|
8
|
+
}
|
|
9
|
+
function setBadges(count) {
|
|
10
|
+
getBellButtons().forEach((btn) => {
|
|
11
|
+
const badge = btn.querySelector(".notification-badge");
|
|
12
|
+
if (!badge)
|
|
13
|
+
return;
|
|
14
|
+
if (count > 0) {
|
|
15
|
+
badge.textContent = String(count);
|
|
16
|
+
badge.classList.remove("hidden");
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
badge.textContent = "";
|
|
20
|
+
badge.classList.add("hidden");
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function itemTitle(item) {
|
|
25
|
+
const payload = item.dispatch || item.message || {};
|
|
26
|
+
return payload.title || payload.mode || "Message";
|
|
27
|
+
}
|
|
28
|
+
function itemBody(item) {
|
|
29
|
+
const payload = item.dispatch || item.message || {};
|
|
30
|
+
return payload.body || "";
|
|
31
|
+
}
|
|
32
|
+
function renderList(items) {
|
|
33
|
+
const listEl = document.getElementById("notification-list");
|
|
34
|
+
if (!listEl)
|
|
35
|
+
return;
|
|
36
|
+
if (!items.length) {
|
|
37
|
+
listEl.innerHTML = '<div class="muted">No dispatches</div>';
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const html = items
|
|
41
|
+
.map((item) => {
|
|
42
|
+
const title = itemTitle(item);
|
|
43
|
+
const excerpt = itemBody(item).slice(0, 180);
|
|
44
|
+
const repoLabel = item.repo_display_name || item.repo_id;
|
|
45
|
+
const href = item.open_url || `/repos/${item.repo_id}/?tab=inbox&run_id=${item.run_id}`;
|
|
46
|
+
const seq = item.seq ? `#${item.seq}` : "";
|
|
47
|
+
return `
|
|
48
|
+
<div class="notification-item">
|
|
49
|
+
<div class="notification-item-header">
|
|
50
|
+
<span class="notification-repo">${escapeHtml(repoLabel)} <span class="muted">(${item.run_id.slice(0, 8)}${seq})</span></span>
|
|
51
|
+
<span class="pill pill-small pill-warn">paused</span>
|
|
52
|
+
</div>
|
|
53
|
+
<div class="notification-title">${escapeHtml(title)}</div>
|
|
54
|
+
<div class="notification-excerpt">${escapeHtml(excerpt)}</div>
|
|
55
|
+
<div class="notification-actions">
|
|
56
|
+
<a class="notification-action" href="${escapeHtml(resolvePath(href))}">Open run</a>
|
|
57
|
+
<button class="notification-action" data-action="copy-run-id" data-run-id="${escapeHtml(item.run_id)}">Copy ID</button>
|
|
58
|
+
${item.repo_id ? `<button class="notification-action" data-action="copy-repo-id" data-repo-id="${escapeHtml(item.repo_id)}">Copy repo</button>` : ""}
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
`;
|
|
62
|
+
})
|
|
63
|
+
.join("");
|
|
64
|
+
listEl.innerHTML = html;
|
|
65
|
+
}
|
|
66
|
+
async function fetchNotifications() {
|
|
67
|
+
const payload = (await api("/hub/messages", { method: "GET" }));
|
|
68
|
+
return payload?.items || [];
|
|
69
|
+
}
|
|
70
|
+
async function refreshNotifications(options = {}) {
|
|
71
|
+
const { silent = true, render = false } = options;
|
|
72
|
+
try {
|
|
73
|
+
const items = await fetchNotifications();
|
|
74
|
+
setBadges(items.length);
|
|
75
|
+
if (modalOpen || render) {
|
|
76
|
+
renderList(items);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
if (!silent) {
|
|
81
|
+
flash(err.message || "Failed to load dispatches", "error");
|
|
82
|
+
}
|
|
83
|
+
setBadges(0);
|
|
84
|
+
if (modalOpen || render) {
|
|
85
|
+
renderList([]);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function openNotificationsModal() {
|
|
90
|
+
const modal = document.getElementById("notification-modal");
|
|
91
|
+
const closeBtn = document.getElementById("notification-close");
|
|
92
|
+
if (!modal)
|
|
93
|
+
return;
|
|
94
|
+
if (closeModal)
|
|
95
|
+
closeModal();
|
|
96
|
+
closeModal = openModal(modal, {
|
|
97
|
+
initialFocus: closeBtn || modal,
|
|
98
|
+
onRequestClose: () => {
|
|
99
|
+
modalOpen = false;
|
|
100
|
+
if (closeModal) {
|
|
101
|
+
const close = closeModal;
|
|
102
|
+
closeModal = null;
|
|
103
|
+
close();
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
modalOpen = true;
|
|
108
|
+
void refreshNotifications({ render: true, silent: true });
|
|
109
|
+
}
|
|
110
|
+
function attachModalHandlers() {
|
|
111
|
+
const modal = document.getElementById("notification-modal");
|
|
112
|
+
if (!modal)
|
|
113
|
+
return;
|
|
114
|
+
const closeBtn = document.getElementById("notification-close");
|
|
115
|
+
const refreshBtn = document.getElementById("notification-refresh");
|
|
116
|
+
closeBtn?.addEventListener("click", () => {
|
|
117
|
+
if (closeModal) {
|
|
118
|
+
const close = closeModal;
|
|
119
|
+
closeModal = null;
|
|
120
|
+
modalOpen = false;
|
|
121
|
+
close();
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
refreshBtn?.addEventListener("click", () => {
|
|
125
|
+
void refreshNotifications({ render: true, silent: false });
|
|
126
|
+
});
|
|
127
|
+
const listEl = document.getElementById("notification-list");
|
|
128
|
+
listEl?.addEventListener("click", (event) => {
|
|
129
|
+
const target = event.target;
|
|
130
|
+
if (!target)
|
|
131
|
+
return;
|
|
132
|
+
const action = target.dataset.action || "";
|
|
133
|
+
if (action === "copy-run-id") {
|
|
134
|
+
const runId = target.dataset.runId || "";
|
|
135
|
+
if (runId) {
|
|
136
|
+
void navigator.clipboard.writeText(runId).then(() => {
|
|
137
|
+
flash("Copied run ID", "info");
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (action === "copy-repo-id") {
|
|
142
|
+
const repoId = target.dataset.repoId || "";
|
|
143
|
+
if (repoId) {
|
|
144
|
+
void navigator.clipboard.writeText(repoId).then(() => {
|
|
145
|
+
flash("Copied repo ID", "info");
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
export function initNotificationBell() {
|
|
152
|
+
if (bellInitialized)
|
|
153
|
+
return;
|
|
154
|
+
bellInitialized = true;
|
|
155
|
+
const buttons = getBellButtons();
|
|
156
|
+
if (!buttons.length)
|
|
157
|
+
return;
|
|
158
|
+
buttons.forEach((btn) => {
|
|
159
|
+
btn.addEventListener("click", () => {
|
|
160
|
+
openNotificationsModal();
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
attachModalHandlers();
|
|
164
|
+
void refreshNotifications({ render: false, silent: true });
|
|
165
|
+
window.setInterval(() => {
|
|
166
|
+
if (document.hidden)
|
|
167
|
+
return;
|
|
168
|
+
void refreshNotifications({ render: false, silent: true });
|
|
169
|
+
}, 15000);
|
|
170
|
+
}
|
|
171
|
+
export const __notificationBellTest = {
|
|
172
|
+
refreshNotifications,
|
|
173
|
+
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// GENERATED FILE - do not edit directly. Source: static_src/
|
|
2
|
-
import { api, escapeHtml, openModal, resolvePath } from "./utils.js";
|
|
2
|
+
import { api, confirmModal, escapeHtml, flash, inputModal, openModal, resolvePath } from "./utils.js";
|
|
3
3
|
import { registerAutoRefresh } from "./autoRefresh.js";
|
|
4
4
|
let notificationsInitialized = false;
|
|
5
|
-
|
|
5
|
+
const notificationItemsByRoot = {};
|
|
6
6
|
let activeRoot = null;
|
|
7
7
|
let closeModalFn = null;
|
|
8
8
|
let documentListenerInstalled = false;
|
|
@@ -29,10 +29,11 @@ function getRootElements(root) {
|
|
|
29
29
|
const dropdown = root.querySelector("[data-notifications-dropdown]");
|
|
30
30
|
if (!trigger || !badge || !dropdown)
|
|
31
31
|
return null;
|
|
32
|
-
|
|
32
|
+
const key = root.getAttribute("data-notifications-root") || "hub";
|
|
33
|
+
return { root, trigger, badge, dropdown, key };
|
|
33
34
|
}
|
|
34
|
-
function setBadgeCount(count) {
|
|
35
|
-
const roots = document.querySelectorAll(
|
|
35
|
+
function setBadgeCount(rootKey, count) {
|
|
36
|
+
const roots = document.querySelectorAll(`[data-notifications-root="${rootKey}"]`);
|
|
36
37
|
roots.forEach((root) => {
|
|
37
38
|
const elements = getRootElements(root);
|
|
38
39
|
if (!elements)
|
|
@@ -42,7 +43,7 @@ function setBadgeCount(count) {
|
|
|
42
43
|
elements.trigger.setAttribute("aria-label", count > 0 ? `Notifications (${count})` : "Notifications");
|
|
43
44
|
});
|
|
44
45
|
}
|
|
45
|
-
function
|
|
46
|
+
function normalizeHubItem(item) {
|
|
46
47
|
const repoId = String(item.repo_id || "");
|
|
47
48
|
const repoDisplay = item.repo_display_name || repoId;
|
|
48
49
|
const mode = item.dispatch?.mode || "";
|
|
@@ -53,6 +54,7 @@ function normalizeItem(item) {
|
|
|
53
54
|
const runId = String(item.run_id || "");
|
|
54
55
|
const openUrl = item.open_url || `/repos/${repoId}/?tab=inbox&run_id=${runId}`;
|
|
55
56
|
return {
|
|
57
|
+
kind: "hub",
|
|
56
58
|
repoId,
|
|
57
59
|
repoDisplay,
|
|
58
60
|
runId,
|
|
@@ -63,21 +65,42 @@ function normalizeItem(item) {
|
|
|
63
65
|
body,
|
|
64
66
|
isHandoff,
|
|
65
67
|
openUrl,
|
|
68
|
+
pillLabel: isHandoff ? "handoff" : "paused",
|
|
66
69
|
};
|
|
67
70
|
}
|
|
71
|
+
function normalizePmaItem(item) {
|
|
72
|
+
const title = (item.title || "PMA dispatch").trim();
|
|
73
|
+
const body = item.body || "";
|
|
74
|
+
const priority = (item.priority || "info").toLowerCase();
|
|
75
|
+
const isHandoff = priority === "action";
|
|
76
|
+
return {
|
|
77
|
+
kind: "pma",
|
|
78
|
+
title,
|
|
79
|
+
body,
|
|
80
|
+
isHandoff,
|
|
81
|
+
openUrl: "/?view=pma",
|
|
82
|
+
pillLabel: priority,
|
|
83
|
+
priority,
|
|
84
|
+
links: item.links || [],
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function getItemsForRoot(rootKey) {
|
|
88
|
+
return notificationItemsByRoot[rootKey] || [];
|
|
89
|
+
}
|
|
68
90
|
function renderDropdown(root) {
|
|
69
91
|
if (!root)
|
|
70
92
|
return;
|
|
71
|
-
|
|
93
|
+
const items = getItemsForRoot(root.key);
|
|
94
|
+
if (!items.length) {
|
|
72
95
|
root.dropdown.innerHTML = '<div class="notifications-empty muted small">No pending dispatches</div>';
|
|
73
96
|
return;
|
|
74
97
|
}
|
|
75
|
-
const html =
|
|
98
|
+
const html = items
|
|
76
99
|
.map((item, index) => {
|
|
77
|
-
const pill = item.isHandoff ? "handoff" : "paused";
|
|
100
|
+
const pill = item.pillLabel || (item.isHandoff ? "handoff" : "paused");
|
|
78
101
|
return `
|
|
79
102
|
<button class="notifications-item" type="button" data-index="${index}">
|
|
80
|
-
<span class="notifications-item-repo">${escapeHtml(item.repoDisplay)}</span>
|
|
103
|
+
<span class="notifications-item-repo">${escapeHtml(item.repoDisplay || "PMA")}</span>
|
|
81
104
|
<span class="notifications-item-title">${escapeHtml(item.title)}</span>
|
|
82
105
|
<span class="pill pill-small pill-warn notifications-item-pill">${escapeHtml(pill)}</span>
|
|
83
106
|
</button>
|
|
@@ -174,35 +197,104 @@ function closeNotificationsModal() {
|
|
|
174
197
|
closeModalFn();
|
|
175
198
|
closeModalFn = null;
|
|
176
199
|
}
|
|
200
|
+
function isSameNotification(a, b) {
|
|
201
|
+
return (a.kind === b.kind &&
|
|
202
|
+
a.repoId === b.repoId &&
|
|
203
|
+
a.runId === b.runId &&
|
|
204
|
+
a.seq === b.seq);
|
|
205
|
+
}
|
|
177
206
|
function openNotificationsModal(item, returnFocusTo) {
|
|
178
207
|
const modal = getModalElements();
|
|
179
208
|
if (!modal)
|
|
180
209
|
return;
|
|
181
210
|
closeNotificationsModal();
|
|
182
|
-
const runLabel = item.seq ? `${item.runId.slice(0, 8)} (#${item.seq})` : item.runId.slice(0, 8);
|
|
183
|
-
const modeLabel = item.mode ? ` (${item.mode})` : "";
|
|
184
211
|
const body = item.body?.trim() ? escapeHtml(item.body) : '<span class="muted">No message body.</span>';
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
212
|
+
if (item.kind === "pma") {
|
|
213
|
+
const priority = item.priority || "info";
|
|
214
|
+
const links = (item.links || [])
|
|
215
|
+
.map((link) => `<a href="${escapeHtml(link.href)}" target="_blank" rel="noopener">${escapeHtml(link.label)}</a>`)
|
|
216
|
+
.join("");
|
|
217
|
+
const linkBlock = links ? `<div class="notifications-modal-links">${links}</div>` : "";
|
|
218
|
+
modal.body.innerHTML = `
|
|
219
|
+
<div class="notifications-modal-meta">
|
|
220
|
+
<div class="notifications-modal-row">
|
|
221
|
+
<span class="notifications-modal-label">Dispatch</span>
|
|
222
|
+
<span class="notifications-modal-value">${escapeHtml(item.title)}</span>
|
|
223
|
+
</div>
|
|
224
|
+
<div class="notifications-modal-row">
|
|
225
|
+
<span class="notifications-modal-label">Priority</span>
|
|
226
|
+
<span class="notifications-modal-value">${escapeHtml(priority)}</span>
|
|
227
|
+
</div>
|
|
190
228
|
</div>
|
|
191
|
-
<div class="notifications-modal-
|
|
192
|
-
|
|
193
|
-
|
|
229
|
+
<div class="notifications-modal-body">${body}</div>
|
|
230
|
+
${linkBlock}
|
|
231
|
+
<div class="notifications-modal-actions">
|
|
232
|
+
<a class="primary sm notifications-open-run" href="${escapeHtml(resolvePath(item.openUrl))}">Open PMA</a>
|
|
194
233
|
</div>
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
234
|
+
`;
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
const runId = item.runId || "";
|
|
238
|
+
const runLabel = item.seq ? `${runId.slice(0, 8)} (#${item.seq})` : runId.slice(0, 8);
|
|
239
|
+
const modeLabel = item.mode ? ` (${item.mode})` : "";
|
|
240
|
+
modal.body.innerHTML = `
|
|
241
|
+
<div class="notifications-modal-meta">
|
|
242
|
+
<div class="notifications-modal-row">
|
|
243
|
+
<span class="notifications-modal-label">Repo</span>
|
|
244
|
+
<span class="notifications-modal-value">${escapeHtml(item.repoDisplay || "")}</span>
|
|
245
|
+
</div>
|
|
246
|
+
<div class="notifications-modal-row">
|
|
247
|
+
<span class="notifications-modal-label">Run</span>
|
|
248
|
+
<span class="notifications-modal-value mono">${escapeHtml(runLabel)}</span>
|
|
249
|
+
</div>
|
|
250
|
+
<div class="notifications-modal-row">
|
|
251
|
+
<span class="notifications-modal-label">Dispatch</span>
|
|
252
|
+
<span class="notifications-modal-value">${escapeHtml(item.title)}${escapeHtml(modeLabel)}</span>
|
|
253
|
+
</div>
|
|
198
254
|
</div>
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
255
|
+
<div class="notifications-modal-body">${body}</div>
|
|
256
|
+
<div class="notifications-modal-actions">
|
|
257
|
+
<a class="primary sm notifications-open-run" href="${escapeHtml(resolvePath(item.openUrl))}">Open run</a>
|
|
258
|
+
${item.seq ? '<button class="ghost sm notifications-dismiss" type="button">Dismiss</button>' : ""}
|
|
259
|
+
</div>
|
|
260
|
+
<div class="notifications-modal-placeholder">Reply here (coming soon).</div>
|
|
261
|
+
`;
|
|
262
|
+
const dismissBtn = modal.body.querySelector(".notifications-dismiss");
|
|
263
|
+
if (dismissBtn && item.seq) {
|
|
264
|
+
dismissBtn.addEventListener("click", async () => {
|
|
265
|
+
const confirmed = await confirmModal("Dismiss this inbox item?", {
|
|
266
|
+
confirmText: "Dismiss",
|
|
267
|
+
danger: false,
|
|
268
|
+
});
|
|
269
|
+
if (!confirmed)
|
|
270
|
+
return;
|
|
271
|
+
const reason = await inputModal("Dismiss reason (optional)", {
|
|
272
|
+
placeholder: "obsolete, resolved elsewhere, ...",
|
|
273
|
+
confirmText: "Dismiss",
|
|
274
|
+
allowEmpty: true,
|
|
275
|
+
});
|
|
276
|
+
if (reason === null)
|
|
277
|
+
return;
|
|
278
|
+
await api("/hub/messages/dismiss", {
|
|
279
|
+
method: "POST",
|
|
280
|
+
body: {
|
|
281
|
+
repo_id: item.repoId,
|
|
282
|
+
run_id: item.runId,
|
|
283
|
+
seq: item.seq,
|
|
284
|
+
reason,
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
const hubItems = getItemsForRoot("hub").filter((entry) => !isSameNotification(entry, item));
|
|
288
|
+
notificationItemsByRoot.hub = hubItems;
|
|
289
|
+
setBadgeCount("hub", hubItems.length);
|
|
290
|
+
if (activeRoot && activeRoot.key === "hub") {
|
|
291
|
+
renderDropdown(activeRoot);
|
|
292
|
+
}
|
|
293
|
+
closeNotificationsModal();
|
|
294
|
+
flash("Dispatch dismissed");
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
}
|
|
206
298
|
closeModalFn = openModal(modal.overlay, {
|
|
207
299
|
closeOnEscape: true,
|
|
208
300
|
closeOnOverlay: true,
|
|
@@ -215,10 +307,28 @@ async function refreshNotifications(_ctx) {
|
|
|
215
307
|
return;
|
|
216
308
|
isRefreshing = true;
|
|
217
309
|
try {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
310
|
+
let hubPayload = null;
|
|
311
|
+
let pmaPayload = null;
|
|
312
|
+
try {
|
|
313
|
+
hubPayload = (await api("/hub/messages", { method: "GET" }));
|
|
314
|
+
}
|
|
315
|
+
catch {
|
|
316
|
+
hubPayload = null;
|
|
317
|
+
}
|
|
318
|
+
try {
|
|
319
|
+
pmaPayload = (await api("/hub/pma/dispatches?include_resolved=false", {
|
|
320
|
+
method: "GET",
|
|
321
|
+
}));
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
pmaPayload = { items: [] };
|
|
325
|
+
}
|
|
326
|
+
const hubItems = (hubPayload?.items || []).map(normalizeHubItem);
|
|
327
|
+
const pmaItems = (pmaPayload?.items || []).map(normalizePmaItem);
|
|
328
|
+
notificationItemsByRoot.hub = hubItems;
|
|
329
|
+
notificationItemsByRoot.pma = pmaItems;
|
|
330
|
+
setBadgeCount("hub", hubItems.length);
|
|
331
|
+
setBadgeCount("pma", pmaItems.length);
|
|
222
332
|
if (activeRoot) {
|
|
223
333
|
renderDropdown(activeRoot);
|
|
224
334
|
}
|
|
@@ -248,12 +358,20 @@ function attachRoot(root) {
|
|
|
248
358
|
const target = event.target?.closest(".notifications-item");
|
|
249
359
|
if (!target)
|
|
250
360
|
return;
|
|
361
|
+
event.preventDefault();
|
|
362
|
+
event.stopPropagation();
|
|
251
363
|
const index = Number(target.dataset.index || "-1");
|
|
252
|
-
const
|
|
364
|
+
const items = getItemsForRoot(root.key);
|
|
365
|
+
const item = items[index];
|
|
253
366
|
if (!item)
|
|
254
367
|
return;
|
|
255
368
|
closeDropdown();
|
|
256
|
-
|
|
369
|
+
const mouseEvent = event;
|
|
370
|
+
if (mouseEvent.shiftKey) {
|
|
371
|
+
openNotificationsModal(item, root.trigger);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
window.location.href = resolvePath(item.openUrl);
|
|
257
375
|
});
|
|
258
376
|
}
|
|
259
377
|
function attachModalHandlers() {
|