@goodandready/dsh-cron 0.3.24 → 0.3.25
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/lib/api-item.js +2 -1
- package/lib/client.js +48 -3
- package/lib/log-streamer.js +10 -2
- package/package.json +5 -3
package/lib/api-item.js
CHANGED
|
@@ -21,7 +21,8 @@ export async function handleTaskItem({ store, scheduler, req, res, url, id, acti
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
if (req.method === 'GET') {
|
|
24
|
-
|
|
24
|
+
// Both /dsh-cron/tasks/:id/stream and /dsh-cron/tasks/:id/logs/stream (#392)
|
|
25
|
+
if (action === 'stream' || (action === 'logs' && subAction === 'stream')) {
|
|
25
26
|
logStreamer.handleSseRequest(id, req, res);
|
|
26
27
|
return;
|
|
27
28
|
}
|
package/lib/client.js
CHANGED
|
@@ -2736,14 +2736,46 @@ if (window.__dshLiveLogsEvtSource) {
|
|
|
2736
2736
|
try { window.__dshLiveLogsEvtSource.close(); } catch (e) { void e; }
|
|
2737
2737
|
window.__dshLiveLogsEvtSource = null;
|
|
2738
2738
|
}
|
|
2739
|
+
if (window.__dshLiveLogsPollTimer) {
|
|
2740
|
+
clearInterval(window.__dshLiveLogsPollTimer);
|
|
2741
|
+
window.__dshLiveLogsPollTimer = null;
|
|
2742
|
+
}
|
|
2739
2743
|
setLiveLogsTaskId(task.id);
|
|
2740
2744
|
setLiveLogsTaskTitle(task.title || task.id);
|
|
2741
2745
|
setLiveLogsLines([]);
|
|
2742
2746
|
setLiveLogsStatus(task.running ? 'connecting' : 'idle');
|
|
2743
2747
|
setLiveLogsCompleted(false);
|
|
2744
2748
|
setLiveLogsModalOpen(true);
|
|
2749
|
+
const startPollingFallback = () => {
|
|
2750
|
+
if (window.__dshLiveLogsPollTimer) return;
|
|
2751
|
+
const pollFn = () => {
|
|
2752
|
+
fetchJsonWithTimeout('/dsh-cron/tasks/' + encodeURIComponent(task.id) + '/history?limit=1')
|
|
2753
|
+
.then((data) => {
|
|
2754
|
+
if (data && data.history && data.history.length > 0) {
|
|
2755
|
+
const lastRun = data.history[0];
|
|
2756
|
+
if (lastRun.output || lastRun.error) {
|
|
2757
|
+
const text = lastRun.output || lastRun.error;
|
|
2758
|
+
setLiveLogsLines([{ chunk: text, stream: lastRun.error ? 'stderr' : 'stdout', ts: lastRun.at }]);
|
|
2759
|
+
}
|
|
2760
|
+
if (lastRun.status) {
|
|
2761
|
+
setLiveLogsStatus(lastRun.status);
|
|
2762
|
+
setLiveLogsCompleted(true);
|
|
2763
|
+
if (window.__dshLiveLogsPollTimer) {
|
|
2764
|
+
clearInterval(window.__dshLiveLogsPollTimer);
|
|
2765
|
+
window.__dshLiveLogsPollTimer = null;
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2769
|
+
})
|
|
2770
|
+
.catch(() => {});
|
|
2771
|
+
};
|
|
2772
|
+
pollFn();
|
|
2773
|
+
window.__dshLiveLogsPollTimer = setInterval(pollFn, 2000);
|
|
2774
|
+
};
|
|
2775
|
+
if (typeof EventSource !== 'undefined') {
|
|
2745
2776
|
try {
|
|
2746
|
-
const
|
|
2777
|
+
const streamUrl = '/dsh-cron/tasks/' + encodeURIComponent(task.id) + '/logs/stream';
|
|
2778
|
+
const es = new EventSource(streamUrl);
|
|
2747
2779
|
window.__dshLiveLogsEvtSource = es;
|
|
2748
2780
|
es.addEventListener('init', (e) => {
|
|
2749
2781
|
try {
|
|
@@ -2767,12 +2799,21 @@ try { es.close(); } catch (e) { void e; }
|
|
|
2767
2799
|
window.__dshLiveLogsEvtSource = null;
|
|
2768
2800
|
});
|
|
2769
2801
|
es.onerror = () => {
|
|
2770
|
-
setLiveLogsStatus((curr) => curr === 'running' ? 'completed' : 'closed');
|
|
2771
2802
|
try { es.close(); } catch (e) { void e; }
|
|
2772
2803
|
window.__dshLiveLogsEvtSource = null;
|
|
2804
|
+
setLiveLogsStatus((curr) => {
|
|
2805
|
+
if (curr === 'running' || curr === 'connecting') {
|
|
2806
|
+
startPollingFallback();
|
|
2807
|
+
return curr;
|
|
2808
|
+
}
|
|
2809
|
+
return 'completed';
|
|
2810
|
+
});
|
|
2773
2811
|
};
|
|
2774
2812
|
} catch (err) {
|
|
2775
|
-
|
|
2813
|
+
startPollingFallback();
|
|
2814
|
+
}
|
|
2815
|
+
} else {
|
|
2816
|
+
startPollingFallback();
|
|
2776
2817
|
}
|
|
2777
2818
|
}
|
|
2778
2819
|
function handleCloseLiveLogs() {
|
|
@@ -2780,6 +2821,10 @@ if (window.__dshLiveLogsEvtSource) {
|
|
|
2780
2821
|
try { window.__dshLiveLogsEvtSource.close(); } catch (e) { void e; }
|
|
2781
2822
|
window.__dshLiveLogsEvtSource = null;
|
|
2782
2823
|
}
|
|
2824
|
+
if (window.__dshLiveLogsPollTimer) {
|
|
2825
|
+
clearInterval(window.__dshLiveLogsPollTimer);
|
|
2826
|
+
window.__dshLiveLogsPollTimer = null;
|
|
2827
|
+
}
|
|
2783
2828
|
setLiveLogsModalOpen(false);
|
|
2784
2829
|
}
|
|
2785
2830
|
const openAnalyticsDigest = async (days = 7) => {
|
package/lib/log-streamer.js
CHANGED
|
@@ -50,12 +50,15 @@ export class LogStreamer extends EventEmitter {
|
|
|
50
50
|
ts: Date.now(),
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
+
entry.eventSeq = (entry.eventSeq || 0) + 1;
|
|
54
|
+
item.id = entry.eventSeq;
|
|
55
|
+
|
|
53
56
|
entry.buffer.push(item);
|
|
54
57
|
if (entry.buffer.length > MAX_BUFFER_LINES) {
|
|
55
58
|
entry.buffer.shift();
|
|
56
59
|
}
|
|
57
60
|
|
|
58
|
-
const ssePayload = `
|
|
61
|
+
const ssePayload = `id: ${item.id}\nevent: log\ndata: ${JSON.stringify(item)}\n\n`;
|
|
59
62
|
for (const res of entry.subscribers) {
|
|
60
63
|
try {
|
|
61
64
|
res.write(ssePayload);
|
|
@@ -111,12 +114,17 @@ export class LogStreamer extends EventEmitter {
|
|
|
111
114
|
|
|
112
115
|
const entry = this.streams.get(taskId);
|
|
113
116
|
|
|
117
|
+
// Support W3C Last-Event-ID for reconnection (#392)
|
|
118
|
+
const lastEventIdStr = req?.headers ? req.headers['last-event-id'] : undefined;
|
|
119
|
+
const lastEventId = lastEventIdStr ? parseInt(lastEventIdStr, 10) : 0;
|
|
120
|
+
const effectiveLogs = (entry?.buffer || []).filter((item) => !lastEventId || (item.id && item.id > lastEventId));
|
|
121
|
+
|
|
114
122
|
// Initial event with current state and buffered logs
|
|
115
123
|
const initData = {
|
|
116
124
|
taskId,
|
|
117
125
|
runId: entry?.runId || null,
|
|
118
126
|
status: entry ? (entry.completed ? 'completed' : 'running') : 'idle',
|
|
119
|
-
bufferedLogs:
|
|
127
|
+
bufferedLogs: effectiveLogs,
|
|
120
128
|
};
|
|
121
129
|
res.write(`event: init\ndata: ${JSON.stringify(initData)}\n\n`);
|
|
122
130
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-cron",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.25",
|
|
4
4
|
"description": "Background automation runner for DSH: isolated agent runs, script/HTTP/SSH/Docker runtimes, cost guard, notifications, heartbeats.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"build:client": "node scripts/build-client.mjs",
|
|
24
24
|
"pretest": "node scripts/build-client.mjs",
|
|
25
25
|
"preflight": "node scripts/ci-preflight.mjs",
|
|
26
|
-
"test:engine": "node scripts/test-engines.mjs"
|
|
26
|
+
"test:engine": "node scripts/test-engines.mjs",
|
|
27
|
+
"build:client:check": "node scripts/build-client.mjs --check"
|
|
27
28
|
},
|
|
28
29
|
"keywords": [
|
|
29
30
|
"dsh",
|
|
@@ -108,6 +109,7 @@
|
|
|
108
109
|
"@deepseek-ai/dsh-session": ">=0.1.0-rc.6 <0.1.1 || >=0.1.1-rc.1 <0.1.2 || >=0.1.2-alpha.1 <1.0.0",
|
|
109
110
|
"@deepseek-ai/dsh-settings": ">=0.1.0-rc.6 <0.1.1 || >=0.1.1-rc.1 <0.1.2 || >=0.1.2-alpha.1 <1.0.0",
|
|
110
111
|
"@deepseek-ai/dsh-tools": ">=0.1.0-rc.6 <0.1.1 || >=0.1.1-rc.1 <0.1.2 || >=0.1.2-alpha.1 <1.0.0",
|
|
111
|
-
"@deepseek-ai/schemastery": "^3.18.1"
|
|
112
|
+
"@deepseek-ai/schemastery": "^3.18.1",
|
|
113
|
+
"esbuild": "^0.28.2"
|
|
112
114
|
}
|
|
113
115
|
}
|