tmux-weblink 2.0.13 → 2.0.15

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.
@@ -1,379 +0,0 @@
1
- import{closeOtherDrawersExcept as o,wrapDrawerScript as a}from"./drawer-script.js";import{drawerResizeCSS as c,drawerResizeHandleHTML as d,drawerResizeScript as i}from"./drawer-resize.js";import{escapeHtml as l}from"./html.js";import{DELAY_INVALID_MESSAGE as h,DELAY_MAX_MESSAGE as p,MAX_SCHEDULE_MS as u,scheduleDelayParseScript as m}from"./schedule-delay.js";function v(){return`
2
- #sched-backdrop {
3
- position: fixed; inset: 0; background: rgba(0,0,0,0.45); z-index: 999;
4
- opacity: 0; pointer-events: none; transition: opacity 0.2s ease;
5
- }
6
- #sched-backdrop.open { opacity: 1; pointer-events: auto; }
7
- #sched-drawer {
8
- position: fixed; right: 0; top: 0; height: 100%; width: 400px; z-index: 1000;
9
- background: var(--panel-bg); border-left: 1px solid var(--panel-border);
10
- display: flex; flex-direction: column;
11
- transform: translateX(100%); transition: transform 0.25s ease;
12
- }
13
- #sched-drawer.open { transform: translateX(0); }
14
- ${c()}
15
- .sched-form-section {
16
- padding: 14px 16px; border-bottom: 1px solid var(--panel-border); flex-shrink: 0;
17
- display: flex; flex-direction: column; gap: 10px;
18
- }
19
- .sched-field-row { display: flex; gap: 8px; align-items: center; }
20
- .sched-field-row label {
21
- font-size: var(--text-xs); color: var(--panel-muted); text-transform: uppercase;
22
- letter-spacing: 0.08em; font-family: var(--font-mono);
23
- width: 56px; flex-shrink: 0;
24
- }
25
- .sched-input {
26
- flex: 1; background: color-mix(in srgb, var(--page-bg) 70%, transparent); border: 1px solid var(--panel-border);
27
- color: var(--page-fg); font-family: var(--font-mono); font-size: var(--text-xs);
28
- padding: 5px 9px; border-radius: 6px; outline: none; transition: border-color 0.15s;
29
- }
30
- .sched-input:focus { border-color: var(--panel-accent); }
31
- .sched-input.error { border-color: #cc6666; }
32
- select.sched-input { cursor: pointer; }
33
- .sched-presets { display: flex; gap: 6px; padding-left: 64px; }
34
- .sched-preset-btn {
35
- font-size: var(--text-xs); color: var(--panel-muted); background: none;
36
- border: 1px solid var(--panel-border); padding: 6px 12px; border-radius: 6px;
37
- cursor: pointer; font-family: var(--font-mono); transition: all 0.15s;
38
- min-height: 44px;
39
- }
40
- .sched-preset-btn:hover { border-color: var(--panel-accent); color: var(--panel-accent); background: color-mix(in srgb, var(--panel-accent) 8%, transparent); }
41
- .sched-preset-btn:focus-visible { box-shadow: 0 0 0 2px var(--panel-accent); outline: none; }
42
- .sched-bottom-row {
43
- display: flex; justify-content: space-between; align-items: center; gap: 8px;
44
- }
45
- .sched-error-msg {
46
- font-size: var(--text-xs); color: #cc6666; font-family: var(--font-mono); flex: 1;
47
- }
48
- .sched-submit-btn {
49
- font-size: var(--text-xs); background: color-mix(in srgb, var(--panel-success) 12%, transparent);
50
- border: 1px solid var(--panel-success); color: var(--panel-success);
51
- padding: 8px 18px; border-radius: 6px; cursor: pointer; flex-shrink: 0;
52
- font-family: var(--font-mono); transition: all 0.15s;
53
- min-height: 44px;
54
- }
55
- .sched-submit-btn:hover { background: color-mix(in srgb, var(--panel-success) 22%, transparent); }
56
- .sched-submit-btn:focus-visible { box-shadow: 0 0 0 2px var(--panel-success); outline: none; }
57
- .sched-submit-btn:disabled { opacity: 0.4; cursor: not-allowed; }
58
- .sched-tasks-section { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
59
- .sched-tasks-header {
60
- padding: 8px 16px; font-size: var(--text-xs); color: var(--panel-muted);
61
- text-transform: uppercase; letter-spacing: 0.1em; font-family: var(--font-mono);
62
- border-bottom: 1px solid var(--panel-border); flex-shrink: 0;
63
- display: flex; justify-content: space-between; align-items: center;
64
- }
65
- .sched-task-count {
66
- font-size: var(--text-xs); color: var(--panel-success); font-family: var(--font-mono);
67
- }
68
- .sched-task-list {
69
- flex: 1; overflow-y: auto;
70
- scrollbar-width: thin;
71
- scrollbar-color: var(--panel-border) transparent;
72
- }
73
- .sched-task-list::-webkit-scrollbar { width: 4px; }
74
- .sched-task-list::-webkit-scrollbar-track { background: transparent; }
75
- .sched-task-list::-webkit-scrollbar-thumb {
76
- background: var(--panel-border); border-radius: 2px;
77
- }
78
- .sched-task-list::-webkit-scrollbar-thumb:hover { background: var(--panel-muted); }
79
- .sched-task-item {
80
- padding: 10px 16px; border-bottom: 1px solid var(--panel-border);
81
- display: flex; flex-direction: column; gap: 4px;
82
- }
83
- .sched-task-item:last-child { border-bottom: none; }
84
- .sched-task-row1 { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
85
- .sched-task-cmd {
86
- font-size: var(--text-xs); color: var(--panel-accent); font-family: var(--font-mono);
87
- overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
88
- }
89
- .sched-countdown {
90
- font-size: var(--text-xs); color: var(--panel-success); font-family: var(--font-mono);
91
- font-weight: 600; flex-shrink: 0; min-width: 48px; text-align: right;
92
- }
93
- .sched-countdown.urgent { color: #f0c674; }
94
- .sched-countdown.imminent { color: #cc6666; }
95
- .sched-task-row2 { display: flex; justify-content: space-between; align-items: center; }
96
- .sched-task-meta {
97
- font-size: var(--text-xs); color: var(--panel-muted); font-family: var(--font-mono);
98
- }
99
- .sched-cancel-btn {
100
- font-size: var(--text-xs); color: var(--panel-muted); background: none;
101
- border: 1px solid var(--panel-border); padding: 6px 10px; border-radius: 6px;
102
- cursor: pointer; font-family: var(--font-mono); transition: all 0.15s;
103
- min-height: 44px;
104
- }
105
- .sched-cancel-btn:hover { border-color: #cc6666; color: #cc6666; background: color-mix(in srgb, #cc6666 8%, transparent); }
106
- .sched-cancel-btn:focus-visible { box-shadow: 0 0 0 2px #cc6666; outline: none; }
107
- .sched-empty {
108
- padding: 32px 16px; text-align: center; font-size: var(--text-xs);
109
- color: var(--panel-muted); font-family: var(--font-mono);
110
- }
111
- #sched-drawer .drawer-header button {
112
- background: none; border: none; color: var(--panel-muted); cursor: pointer;
113
- font-size: var(--text-lg); line-height: 1; min-width: 44px; min-height: 44px; padding: 8px; border-radius: 8px; transition: color 0.15s, background 0.15s;
114
- }
115
- #sched-drawer .drawer-header button:hover { color: var(--panel-accent); background: color-mix(in srgb, var(--panel-accent) 8%, transparent); }
116
- #sched-drawer .drawer-header button:focus-visible { box-shadow: 0 0 0 2px var(--panel-accent); outline: none; }
117
- @media (max-width: 560px) {
118
- #sched-drawer { width: min(100vw - 16px, 400px); }
119
- }`}function g(e){return`
120
- <div id="sched-backdrop"></div>
121
- <div id="sched-drawer" class="resizable-drawer">
122
- ${d()}
123
- <div class="drawer-header">
124
- <span>${l(e)}</span>
125
- <button id="sched-close">&times;</button>
126
- </div>
127
- <div class="sched-form-section">
128
- <div class="sched-field-row">
129
- <label>Window</label>
130
- <select id="sched-window" class="sched-input"></select>
131
- </div>
132
- <div class="sched-field-row">
133
- <label>Command</label>
134
- <input id="sched-cmd" class="sched-input" type="text"
135
- placeholder="e.g. npm run build" autocomplete="off" spellcheck="false" />
136
- </div>
137
- <div class="sched-field-row">
138
- <label>Delay</label>
139
- <input id="sched-delay" class="sched-input" type="text"
140
- placeholder="1h, 5m, 70h, 30d" autocomplete="off" />
141
- </div>
142
- <div class="sched-presets">
143
- <button class="sched-preset-btn" data-delay="1m">1m</button>
144
- <button class="sched-preset-btn" data-delay="5m">5m</button>
145
- <button class="sched-preset-btn" data-delay="15m">15m</button>
146
- <button class="sched-preset-btn" data-delay="1h">1h</button>
147
- </div>
148
- <div class="sched-bottom-row">
149
- <div class="sched-error-msg" id="sched-error"></div>
150
- <button class="sched-submit-btn" id="sched-submit">Schedule</button>
151
- </div>
152
- </div>
153
- <div class="sched-tasks-section">
154
- <div class="sched-tasks-header">
155
- <span>Pending Tasks</span>
156
- <span class="sched-task-count" id="sched-task-count"></span>
157
- </div>
158
- <div class="sched-task-list" id="sched-task-list"></div>
159
- </div>
160
- </div>`}function k(e){const t=JSON.stringify(e),n=u,s=h,r=p;return a("scheduler",`
161
- const SCHED_SESSION = ${t};
162
- const MAX_SCHEDULE_MS = ${n};
163
- ${i("sched-drawer","tmux-web:drawer-width:scheduler",400)}
164
-
165
- let schedCountdownInterval = null;
166
- let schedTickCount = 0;
167
-
168
- ${m()}
169
-
170
- function formatCountdown(ms) {
171
- if (ms <= 0) return 'FIRING';
172
- const s = Math.ceil(ms / 1000);
173
- const h = Math.floor(s / 3600);
174
- const m = Math.floor((s % 3600) / 60);
175
- const sec = s % 60;
176
- if (h > 0) return h + ':' + String(m).padStart(2, '0') + ':' + String(sec).padStart(2, '0');
177
- return String(m).padStart(2, '0') + ':' + String(sec).padStart(2, '0');
178
- }
179
-
180
- function updateCountdownEl(el) {
181
- const fireAt = parseInt(el.dataset.fireAt, 10);
182
- const remaining = fireAt - Date.now();
183
- el.textContent = formatCountdown(remaining);
184
- el.className = 'sched-countdown';
185
- if (remaining <= 10000) el.classList.add('imminent');
186
- else if (remaining <= 60000) el.classList.add('urgent');
187
- }
188
-
189
- function clearChildren(el) {
190
- while (el.firstChild) el.removeChild(el.firstChild);
191
- }
192
-
193
- function el(tag, cls, text) {
194
- const e = document.createElement(tag);
195
- if (cls) e.className = cls;
196
- if (text !== undefined) e.textContent = text;
197
- return e;
198
- }
199
-
200
- async function fetchSchedWindows() {
201
- try {
202
- const res = await fetch('/api/session/' + encodeURIComponent(SCHED_SESSION) + '/windows');
203
- if (!res.ok) return;
204
- const windows = await res.json();
205
- const sel = document.getElementById('sched-window');
206
- clearChildren(sel);
207
- windows.forEach(w => {
208
- const opt = document.createElement('option');
209
- opt.value = String(w.index);
210
- opt.textContent = w.index + ': ' + w.name + (w.active ? ' (active)' : '');
211
- if (w.active) opt.selected = true;
212
- sel.appendChild(opt);
213
- });
214
- } catch {}
215
- }
216
-
217
- function renderTaskList(tasks) {
218
- const list = document.getElementById('sched-task-list');
219
- const countEl = document.getElementById('sched-task-count');
220
- countEl.textContent = tasks.length ? String(tasks.length) : '';
221
- clearChildren(list);
222
- if (!tasks.length) {
223
- list.appendChild(el('div', 'sched-empty', 'No pending tasks'));
224
- return;
225
- }
226
- for (const task of tasks) {
227
- const item = el('div', 'sched-task-item');
228
-
229
- const row1 = el('div', 'sched-task-row1');
230
- const cmd = el('div', 'sched-task-cmd', task.text);
231
- const countdown = el('div', 'sched-countdown');
232
- countdown.dataset.fireAt = String(task.fireAt);
233
- updateCountdownEl(countdown);
234
- row1.appendChild(cmd);
235
- row1.appendChild(countdown);
236
-
237
- const row2 = el('div', 'sched-task-row2');
238
- const meta = el('div', 'sched-task-meta', 'win:' + task.windowIndex);
239
- const cancelBtn = el('button', 'sched-cancel-btn', 'cancel');
240
- cancelBtn.addEventListener('click', () => cancelTask(task.id));
241
- row2.appendChild(meta);
242
- row2.appendChild(cancelBtn);
243
-
244
- item.appendChild(row1);
245
- item.appendChild(row2);
246
- list.appendChild(item);
247
- }
248
- }
249
-
250
- async function fetchTasks() {
251
- try {
252
- const res = await fetch('/api/schedule?session=' + encodeURIComponent(SCHED_SESSION));
253
- if (!res.ok) return;
254
- renderTaskList(await res.json());
255
- } catch {}
256
- }
257
-
258
- async function cancelTask(id) {
259
- try {
260
- await fetch('/api/schedule/' + id, { method: 'DELETE' });
261
- fetchTasks();
262
- } catch {}
263
- }
264
-
265
- async function scheduleTask() {
266
- const cmdEl = document.getElementById('sched-cmd');
267
- const delayEl = document.getElementById('sched-delay');
268
- const winEl = document.getElementById('sched-window');
269
- const errorEl = document.getElementById('sched-error');
270
- const submitBtn = document.getElementById('sched-submit');
271
-
272
- errorEl.textContent = '';
273
- cmdEl.classList.remove('error');
274
- delayEl.classList.remove('error');
275
-
276
- const cmd = cmdEl.value.trim();
277
- if (!cmd) {
278
- cmdEl.classList.add('error');
279
- errorEl.textContent = 'Command is required.';
280
- return;
281
- }
282
- const delayMs = parseDelay(delayEl.value);
283
- if (!delayMs) {
284
- delayEl.classList.add('error');
285
- errorEl.textContent = ${JSON.stringify(s)};
286
- return;
287
- }
288
- if (delayMs > MAX_SCHEDULE_MS) {
289
- delayEl.classList.add('error');
290
- errorEl.textContent = ${JSON.stringify(r)};
291
- return;
292
- }
293
- const windowIndex = parseInt(winEl.value, 10);
294
-
295
- submitBtn.disabled = true;
296
- try {
297
- const res = await fetch('/api/schedule', {
298
- method: 'POST',
299
- headers: { 'Content-Type': 'application/json' },
300
- body: JSON.stringify({ sessionName: SCHED_SESSION, windowIndex, text: cmd, delayMs }),
301
- });
302
- if (!res.ok) {
303
- const err = await res.json().catch(() => ({}));
304
- errorEl.textContent = err.error || 'Server error.';
305
- return;
306
- }
307
- cmdEl.value = '';
308
- delayEl.value = '';
309
- fetchTasks();
310
- } catch {
311
- errorEl.textContent = 'Network error.';
312
- } finally {
313
- submitBtn.disabled = false;
314
- }
315
- }
316
-
317
- function startSchedTick() {
318
- if (schedCountdownInterval) return;
319
- schedTickCount = 0;
320
- schedCountdownInterval = setInterval(() => {
321
- document.querySelectorAll('.sched-countdown').forEach(updateCountdownEl);
322
- schedTickCount++;
323
- if (schedTickCount % 10 === 0) fetchTasks();
324
- }, 1000);
325
- }
326
-
327
- function stopSchedTick() {
328
- if (schedCountdownInterval) { clearInterval(schedCountdownInterval); schedCountdownInterval = null; }
329
- }
330
-
331
- function openSchedDrawer() {
332
- ${o("scheduler")}
333
- document.getElementById('sched-drawer').classList.add('open');
334
- document.getElementById('sched-backdrop').classList.add('open');
335
- fetchSchedWindows();
336
- fetchTasks();
337
- startSchedTick();
338
- const url = new URL(location.href);
339
- if (url.searchParams.get('tab') !== 'scheduler') {
340
- url.searchParams.set('tab', 'scheduler');
341
- history.pushState({ schedOpen: true }, '', url);
342
- }
343
- }
344
-
345
- function closeSchedDrawer() {
346
- document.getElementById('sched-drawer').classList.remove('open');
347
- document.getElementById('sched-backdrop').classList.remove('open');
348
- stopSchedTick();
349
- const url = new URL(location.href);
350
- if (url.searchParams.get('tab') === 'scheduler') {
351
- url.searchParams.delete('tab');
352
- history.pushState({}, '', url);
353
- }
354
- }
355
-
356
- document.getElementById('sched-toggle').addEventListener('click', () => {
357
- if (document.getElementById('sched-drawer').classList.contains('open')) closeSchedDrawer();
358
- else openSchedDrawer();
359
- });
360
- document.getElementById('sched-close').addEventListener('click', closeSchedDrawer);
361
- document.getElementById('sched-backdrop').addEventListener('click', closeSchedDrawer);
362
- document.getElementById('sched-submit').addEventListener('click', scheduleTask);
363
- document.getElementById('sched-cmd').addEventListener('keydown', (e) => {
364
- if (e.key === 'Enter') scheduleTask();
365
- });
366
- document.querySelectorAll('.sched-preset-btn').forEach(btn => {
367
- btn.addEventListener('click', () => {
368
- document.getElementById('sched-delay').value = btn.dataset.delay;
369
- document.getElementById('sched-cmd').focus();
370
- });
371
- });
372
-
373
- window.addEventListener('popstate', () => {
374
- const tab = new URLSearchParams(location.search).get('tab');
375
- if (tab === 'scheduler') openSchedDrawer();
376
- else closeSchedDrawer();
377
- });
378
-
379
- if (new URLSearchParams(location.search).get('tab') === 'scheduler') openSchedDrawer();`,"closeSchedDrawer")}export{v as schedulerDrawerCSS,g as schedulerDrawerHTML,k as schedulerDrawerScript};
@@ -1 +0,0 @@
1
- import{execFileSync as o}from"node:child_process";import{tmuxEnv as h}from"./tmux-env.js";import{randomUUID as u}from"node:crypto";import{ARM_SCAN_INTERVAL_MS as T,isValidDelayMs as l,MAX_TIMER_MS as d}from"./schedule-delay.js";const m=10080*60*1e3;function f(n,e,s){const t=`${n}:${e}`;o("tmux",["send-keys","-t",t,"-l",s],{timeout:5e3,env:h()}),o("tmux",["send-keys","-t",t,"Enter"],{timeout:5e3,env:h()})}function I(n){if(!n||typeof n!="object")return!1;const e=n;return typeof e.delayMs=="number"&&l(e.delayMs)}function p(n){if(!n||typeof n!="object")return!1;const e=n;return typeof e.sessionName=="string"&&e.sessionName.length>0&&typeof e.windowIndex=="number"&&Number.isInteger(e.windowIndex)&&e.windowIndex>=0&&typeof e.text=="string"&&e.text.length>0&&e.text.length<=4096&&typeof e.delayMs=="number"&&l(e.delayMs)}class b{deps;scheduledTasks=new Map;now;setTimer;clearTimer;setIntervalFn;clearIntervalFn;armScanIntervalMs;createId;sendKeys;onError;onMissedTask;historyRetentionMs;scanIntervalHandle=null;constructor(e){this.deps=e,this.now=e.now??Date.now,this.setTimer=e.setTimer??setTimeout,this.clearTimer=e.clearTimer??clearTimeout,this.setIntervalFn=e.setInterval??setInterval,this.clearIntervalFn=e.clearInterval??clearInterval,this.armScanIntervalMs=e.armScanIntervalMs??T,this.createId=e.createId??u,this.sendKeys=e.sendKeys??f,this.onError=e.onError??(s=>console.error(s)),this.onMissedTask=e.onMissedTask??(()=>{}),this.historyRetentionMs=e.historyRetentionMs??m}record(e){this.deps.db.data.triggeredTasks??=[],this.deps.db.data.triggeredTasks.push(e);const s=this.now()-this.historyRetentionMs;this.deps.db.data.triggeredTasks=this.deps.db.data.triggeredTasks.filter(t=>t.triggeredAt>=s)}listTriggered(){this.deps.db.data.triggeredTasks??=[];const e=this.now()-this.historyRetentionMs;return this.deps.db.data.triggeredTasks.filter(s=>s.triggeredAt>=e).sort((s,t)=>t.triggeredAt-s.triggeredAt)}async restoreFromDb(){const e=this.now(),s=[];for(const r of this.deps.db.data.scheduledTasks)r.fireAt<=e?(s.push(r.id),this.onMissedTask(r),this.record({...r,triggeredAt:e,status:"missed"})):this.registerTask(r);this.deps.db.data.triggeredTasks??=[];const t=this.deps.db.data.triggeredTasks.length,i=e-this.historyRetentionMs;this.deps.db.data.triggeredTasks=this.deps.db.data.triggeredTasks.filter(r=>r.triggeredAt>=i);const a=this.deps.db.data.triggeredTasks.length!==t;s.length&&(this.deps.db.data.scheduledTasks=this.deps.db.data.scheduledTasks.filter(r=>!s.includes(r.id))),(s.length||a)&&await this.deps.db.write(),this.scanPending(),this.startScanLoop()}list(e){return[...this.scheduledTasks.values()].filter(s=>!e||s.sessionName===e).map(({id:s,sessionName:t,windowIndex:i,text:a,fireAt:r,createdAt:c})=>({id:s,sessionName:t,windowIndex:i,text:a,fireAt:r,createdAt:c,remainingMs:Math.max(0,r-this.now())})).sort((s,t)=>s.fireAt-t.fireAt)}async create(e){const s=this.createId(),t=this.now(),i={id:s,sessionName:e.sessionName,windowIndex:e.windowIndex,text:e.text,fireAt:t+e.delayMs,createdAt:t};return this.registerTask(i),this.deps.db.data.scheduledTasks.push(i),await this.deps.db.write(),this.tryArm(i.id),i}async reschedule(e,s){const t=this.scheduledTasks.get(e);if(!t)return null;this.disarmTask(e);const i={...t,fireAt:this.now()+s};this.registerTask(i);const a=this.deps.db.data.scheduledTasks.findIndex(r=>r.id===e);return a>=0&&(this.deps.db.data.scheduledTasks[a]=i),await this.deps.db.write(),this.tryArm(e),i}async delete(e){return this.scheduledTasks.get(e)?(this.disarmTask(e),this.scheduledTasks.delete(e),this.deps.db.data.scheduledTasks=this.deps.db.data.scheduledTasks.filter(t=>t.id!==e),await this.deps.db.write(),!0):!1}cleanup(){if(this.scanIntervalHandle!=null){try{this.clearIntervalFn(this.scanIntervalHandle)}catch{}this.scanIntervalHandle=null}for(const e of this.scheduledTasks.values())if(e.timeoutHandle!=null)try{this.clearTimer(e.timeoutHandle)}catch{}this.scheduledTasks.clear()}scanPending(){for(const e of[...this.scheduledTasks.values()]){const s=e.fireAt-this.now();if(s<=0){e.timeoutHandle!=null&&this.disarmTask(e.id),this.fireTask(e);continue}s<=d&&e.timeoutHandle==null&&this.armTask(e)}}registerTask(e){this.scheduledTasks.set(e.id,{...e,timeoutHandle:null})}disarmTask(e){const s=this.scheduledTasks.get(e);s&&s.timeoutHandle!=null&&(this.clearTimer(s.timeoutHandle),s.timeoutHandle=null)}tryArm(e){const s=this.scheduledTasks.get(e);if(!s)return;const t=s.fireAt-this.now();if(t<=0){this.fireTask(s);return}t<=d&&s.timeoutHandle==null&&this.armTask(s)}armTask(e){const s=e.fireAt-this.now();if(s<=0){this.fireTask(e);return}this.disarmTask(e.id);const t=Math.min(s,d),i=this.setTimer(()=>this.onTimer(e.id),t);this.scheduledTasks.set(e.id,{...e,timeoutHandle:i})}onTimer(e){const s=this.scheduledTasks.get(e);if(s){if(s.timeoutHandle=null,s.fireAt<=this.now()){this.fireTask(s);return}this.armTask(s)}}startScanLoop(){this.scanIntervalHandle==null&&(this.scanIntervalHandle=this.setIntervalFn(()=>this.scanPending(),this.armScanIntervalMs))}fireTask(e){if(!this.scheduledTasks.has(e.id))return;this.disarmTask(e.id);const s=this.now(),t={id:e.id,sessionName:e.sessionName,windowIndex:e.windowIndex,text:e.text,fireAt:e.fireAt,createdAt:e.createdAt};try{this.sendKeys(e.sessionName,e.windowIndex,e.text),this.record({...t,triggeredAt:s,status:"ok"})}catch(i){const a=String(i?.message??i);this.onError(`[scheduler] send-keys to ${e.sessionName}:${e.windowIndex} failed: ${a}`),this.record({...t,triggeredAt:s,status:"error",error:a})}this.scheduledTasks.delete(e.id),this.deps.db.data.scheduledTasks=this.deps.db.data.scheduledTasks.filter(i=>i.id!==e.id),this.deps.db.write().catch(this.onError)}}export{b as SchedulerService,I as isValidRescheduleInput,p as isValidScheduleInput,f as sendTmuxKeys};