tide-commander 0.74.0 → 0.76.0
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/dist/assets/main-BiQtmZ5W.css +1 -0
- package/dist/assets/{main-CXnYZgYH.js → main-lEmlQfdS.js} +99 -99
- package/dist/assets/{web-D_HPE20F.js → web-BuapUqDI.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/src/packages/server/claude/session-loader.js +1 -1
- package/dist/src/packages/server/services/runtime-service.js +13 -25
- package/dist/src/packages/server/websocket/handler.js +37 -47
- package/package.json +1 -1
- package/dist/assets/main-CHxafkjI.css +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{W as s}from"./main-
|
|
1
|
+
import{W as s}from"./main-lEmlQfdS.js";import"./modulepreload-polyfill-B5Qt9EMX.js";import"./vendor-react-uS-d4TUT.js";import"./vendor-three-DJ4p3FLF.js";class f extends s{constructor(){super(...arguments),this.pending=[],this.deliveredNotifications=[],this.hasNotificationSupport=()=>{if(!("Notification"in window)||!Notification.requestPermission)return!1;if(Notification.permission!=="granted")try{new Notification("")}catch(i){if(i instanceof Error&&i.name==="TypeError")return!1}return!0}}async getDeliveredNotifications(){const i=[];for(const t of this.deliveredNotifications){const e={title:t.title,id:parseInt(t.tag),body:t.body};i.push(e)}return{notifications:i}}async removeDeliveredNotifications(i){for(const t of i.notifications){const e=this.deliveredNotifications.find(n=>n.tag===String(t.id));e==null||e.close(),this.deliveredNotifications=this.deliveredNotifications.filter(()=>!e)}}async removeAllDeliveredNotifications(){for(const i of this.deliveredNotifications)i.close();this.deliveredNotifications=[]}async createChannel(){throw this.unimplemented("Not implemented on web.")}async deleteChannel(){throw this.unimplemented("Not implemented on web.")}async listChannels(){throw this.unimplemented("Not implemented on web.")}async schedule(i){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");for(const t of i.notifications)this.sendNotification(t);return{notifications:i.notifications.map(t=>({id:t.id}))}}async getPending(){return{notifications:this.pending}}async registerActionTypes(){throw this.unimplemented("Not implemented on web.")}async cancel(i){this.pending=this.pending.filter(t=>!i.notifications.find(e=>e.id===t.id))}async areEnabled(){const{display:i}=await this.checkPermissions();return{value:i==="granted"}}async changeExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async checkExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async requestPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(await Notification.requestPermission())}}async checkPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(Notification.permission)}}transformNotificationPermission(i){switch(i){case"granted":return"granted";case"denied":return"denied";default:return"prompt"}}sendPending(){var i;const t=[],e=new Date().getTime();for(const n of this.pending)!((i=n.schedule)===null||i===void 0)&&i.at&&n.schedule.at.getTime()<=e&&(this.buildNotification(n),t.push(n));this.pending=this.pending.filter(n=>!t.find(o=>o===n))}sendNotification(i){var t;if(!((t=i.schedule)===null||t===void 0)&&t.at){const e=i.schedule.at.getTime()-new Date().getTime();this.pending.push(i),setTimeout(()=>{this.sendPending()},e);return}this.buildNotification(i)}buildNotification(i){const t=new Notification(i.title,{body:i.body,tag:String(i.id)});return t.addEventListener("click",this.onClick.bind(this,i),!1),t.addEventListener("show",this.onShow.bind(this,i),!1),t.addEventListener("close",()=>{this.deliveredNotifications=this.deliveredNotifications.filter(()=>!this)},!1),this.deliveredNotifications.push(t),t}onClick(i){const t={actionId:"tap",notification:i};this.notifyListeners("localNotificationActionPerformed",t)}onShow(i){this.notifyListeners("localNotificationReceived",i)}}export{f as LocalNotificationsWeb};
|
package/dist/index.html
CHANGED
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/icons/favicon-16x16.png" />
|
|
23
23
|
<link rel="apple-touch-icon" sizes="180x180" href="/assets/icons/apple-touch-icon.png" />
|
|
24
24
|
<title>Tide Commander</title>
|
|
25
|
-
<script type="module" crossorigin src="/assets/main-
|
|
25
|
+
<script type="module" crossorigin src="/assets/main-lEmlQfdS.js"></script>
|
|
26
26
|
<link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-B5Qt9EMX.js">
|
|
27
27
|
<link rel="modulepreload" crossorigin href="/assets/vendor-react-uS-d4TUT.js">
|
|
28
28
|
<link rel="modulepreload" crossorigin href="/assets/vendor-three-DJ4p3FLF.js">
|
|
29
|
-
<link rel="stylesheet" crossorigin href="/assets/main-
|
|
29
|
+
<link rel="stylesheet" crossorigin href="/assets/main-BiQtmZ5W.css">
|
|
30
30
|
</head>
|
|
31
31
|
<body>
|
|
32
32
|
<div id="app"></div>
|
|
@@ -673,7 +673,7 @@ export async function getSessionActivityStatus(cwd, sessionId, activeThresholdSe
|
|
|
673
673
|
if (!resolved) {
|
|
674
674
|
return null;
|
|
675
675
|
}
|
|
676
|
-
const stats = fs.
|
|
676
|
+
const stats = await fs.promises.stat(resolved.filePath);
|
|
677
677
|
const lastModified = stats.mtime;
|
|
678
678
|
const now = new Date();
|
|
679
679
|
const secondsSinceModified = (now.getTime() - lastModified.getTime()) / 1000;
|
|
@@ -110,12 +110,9 @@ const statusSync = createRuntimeStatusSync({
|
|
|
110
110
|
}
|
|
111
111
|
},
|
|
112
112
|
});
|
|
113
|
-
//
|
|
114
|
-
const
|
|
115
|
-
let
|
|
116
|
-
// Interval for polling orphaned agents (10 seconds)
|
|
117
|
-
const ORPHAN_POLL_INTERVAL = 10000;
|
|
118
|
-
let orphanPollTimer = null;
|
|
113
|
+
// Combined interval for status sync + orphan polling (20 seconds)
|
|
114
|
+
const STATUS_POLL_INTERVAL = 20000;
|
|
115
|
+
let statusPollTimer = null;
|
|
119
116
|
export function init() {
|
|
120
117
|
runners.set('claude', runtimeProviders.claude.createRunner({
|
|
121
118
|
onEvent: runtimeEvents.handleEvent,
|
|
@@ -131,19 +128,14 @@ export function init() {
|
|
|
131
128
|
onComplete: runtimeEvents.handleComplete,
|
|
132
129
|
onError: runtimeEvents.handleError,
|
|
133
130
|
}));
|
|
134
|
-
if (
|
|
135
|
-
clearInterval(
|
|
131
|
+
if (statusPollTimer) {
|
|
132
|
+
clearInterval(statusPollTimer);
|
|
136
133
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
orphanPollTimer = setInterval(() => {
|
|
144
|
-
statusSync.pollOrphanedAgents();
|
|
145
|
-
}, ORPHAN_POLL_INTERVAL);
|
|
146
|
-
log.log(' Initialized with periodic status sync and orphan polling');
|
|
134
|
+
statusPollTimer = setInterval(async () => {
|
|
135
|
+
await statusSync.pollOrphanedAgents();
|
|
136
|
+
await syncAllAgentStatus();
|
|
137
|
+
}, STATUS_POLL_INTERVAL);
|
|
138
|
+
log.log(' Initialized with combined status sync + orphan polling (20s)');
|
|
147
139
|
}
|
|
148
140
|
/**
|
|
149
141
|
* Shutdown the runtime service
|
|
@@ -152,13 +144,9 @@ export function init() {
|
|
|
152
144
|
* Set to true for clean shutdown, false for commander restart/crash recovery.
|
|
153
145
|
*/
|
|
154
146
|
export async function shutdown(killProcesses = false) {
|
|
155
|
-
if (
|
|
156
|
-
clearInterval(
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
if (orphanPollTimer) {
|
|
160
|
-
clearInterval(orphanPollTimer);
|
|
161
|
-
orphanPollTimer = null;
|
|
147
|
+
if (statusPollTimer) {
|
|
148
|
+
clearInterval(statusPollTimer);
|
|
149
|
+
statusPollTimer = null;
|
|
162
150
|
}
|
|
163
151
|
for (const runner of runners.values()) {
|
|
164
152
|
await runner.stopAll(killProcesses);
|
|
@@ -29,51 +29,42 @@ const clients = new Set();
|
|
|
29
29
|
// ============================================================================
|
|
30
30
|
// Broadcasting
|
|
31
31
|
// ============================================================================
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
try {
|
|
56
|
-
return Array.from(value);
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
// Not iterable, let default handling continue
|
|
60
|
-
}
|
|
61
|
-
}
|
|
32
|
+
/**
|
|
33
|
+
* JSON replacer that handles non-standard types (Error, Map, Set, Date, etc.)
|
|
34
|
+
* Serialize once, reuse the string for all clients.
|
|
35
|
+
*/
|
|
36
|
+
function messageReplacer(_key, value) {
|
|
37
|
+
if (value === undefined)
|
|
38
|
+
return null;
|
|
39
|
+
if (typeof value === 'function') {
|
|
40
|
+
return `[Function: ${value.name || 'anonymous'}]`;
|
|
41
|
+
}
|
|
42
|
+
if (value instanceof Error) {
|
|
43
|
+
return { name: value.name, message: value.message, stack: value.stack };
|
|
44
|
+
}
|
|
45
|
+
if (typeof value === 'object' && value !== null) {
|
|
46
|
+
if (value instanceof Date)
|
|
47
|
+
return value.toISOString();
|
|
48
|
+
if (value instanceof Map)
|
|
49
|
+
return Array.from(value.entries());
|
|
50
|
+
if (value instanceof Set)
|
|
51
|
+
return Array.from(value);
|
|
52
|
+
if (typeof value[Symbol.iterator] === 'function' && !Array.isArray(value)) {
|
|
53
|
+
try {
|
|
54
|
+
return Array.from(value);
|
|
62
55
|
}
|
|
63
|
-
|
|
64
|
-
});
|
|
65
|
-
if (data.length === 0 || !data.startsWith('{')) {
|
|
66
|
-
log.error(`[BROADCAST] Invalid JSON generated for ${message.type}:`, data.substring(0, 100));
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
try {
|
|
70
|
-
JSON.parse(data);
|
|
71
|
-
}
|
|
72
|
-
catch (parseErr) {
|
|
73
|
-
log.error(`[BROADCAST] Generated invalid JSON for ${message.type}:`, parseErr);
|
|
74
|
-
log.error(`[BROADCAST] Data preview:`, data.substring(0, 200));
|
|
75
|
-
return;
|
|
56
|
+
catch { /* not iterable */ }
|
|
76
57
|
}
|
|
58
|
+
}
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
/** Serialize a message once, reuse for all send calls. */
|
|
62
|
+
function serializeMessage(message) {
|
|
63
|
+
return JSON.stringify(message, messageReplacer);
|
|
64
|
+
}
|
|
65
|
+
export function broadcast(message) {
|
|
66
|
+
try {
|
|
67
|
+
const data = serializeMessage(message);
|
|
77
68
|
let sentCount = 0;
|
|
78
69
|
let errorCount = 0;
|
|
79
70
|
for (const client of clients) {
|
|
@@ -95,11 +86,10 @@ export function broadcast(message) {
|
|
|
95
86
|
}
|
|
96
87
|
catch (err) {
|
|
97
88
|
log.error(`[BROADCAST] Failed to serialize message of type ${message.type}:`, err);
|
|
98
|
-
log.error(`[BROADCAST] Message type structure:`, typeof message, Object.keys(message || {}));
|
|
99
89
|
}
|
|
100
90
|
}
|
|
101
91
|
function broadcastToOthers(sender, message) {
|
|
102
|
-
const data =
|
|
92
|
+
const data = serializeMessage(message);
|
|
103
93
|
for (const client of clients) {
|
|
104
94
|
if (client !== sender && client.readyState === WebSocket.OPEN) {
|
|
105
95
|
client.send(data);
|
|
@@ -129,10 +119,10 @@ function createHandlerContext(ws) {
|
|
|
129
119
|
broadcastToOthers(ws, message);
|
|
130
120
|
},
|
|
131
121
|
sendToClient: (message) => {
|
|
132
|
-
ws.send(
|
|
122
|
+
ws.send(serializeMessage(message));
|
|
133
123
|
},
|
|
134
124
|
sendError: (message) => {
|
|
135
|
-
ws.send(
|
|
125
|
+
ws.send(serializeMessage({ type: 'error', payload: { message } }));
|
|
136
126
|
},
|
|
137
127
|
sendActivity,
|
|
138
128
|
};
|