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.
@@ -1 +1 @@
1
- import{W as s}from"./main-CXnYZgYH.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};
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-CXnYZgYH.js"></script>
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-CHxafkjI.css">
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.statSync(resolved.filePath);
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
- // Interval for periodic status sync (30 seconds)
114
- const STATUS_SYNC_INTERVAL = 30000;
115
- let statusSyncTimer = null;
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 (statusSyncTimer) {
135
- clearInterval(statusSyncTimer);
131
+ if (statusPollTimer) {
132
+ clearInterval(statusPollTimer);
136
133
  }
137
- statusSyncTimer = setInterval(() => {
138
- syncAllAgentStatus();
139
- }, STATUS_SYNC_INTERVAL);
140
- if (orphanPollTimer) {
141
- clearInterval(orphanPollTimer);
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 (statusSyncTimer) {
156
- clearInterval(statusSyncTimer);
157
- statusSyncTimer = null;
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
- export function broadcast(message) {
33
- try {
34
- const data = JSON.stringify(message, (key, value) => {
35
- if (value === undefined)
36
- return null;
37
- if (typeof value === 'function') {
38
- return `[Function: ${value.name || 'anonymous'}]`;
39
- }
40
- if (value instanceof Error) {
41
- return {
42
- name: value.name,
43
- message: value.message,
44
- stack: value.stack,
45
- };
46
- }
47
- if (typeof value === 'object' && value !== null) {
48
- if (value instanceof Date)
49
- return value.toISOString();
50
- if (value instanceof Map)
51
- return Array.from(value.entries());
52
- if (value instanceof Set)
53
- return Array.from(value);
54
- if (typeof value[Symbol.iterator] === 'function' && !Array.isArray(value)) {
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
- return value;
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 = JSON.stringify(message);
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(JSON.stringify(message));
122
+ ws.send(serializeMessage(message));
133
123
  },
134
124
  sendError: (message) => {
135
- ws.send(JSON.stringify({ type: 'error', payload: { message } }));
125
+ ws.send(serializeMessage({ type: 'error', payload: { message } }));
136
126
  },
137
127
  sendActivity,
138
128
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tide-commander",
3
- "version": "0.74.0",
3
+ "version": "0.76.0",
4
4
  "description": "Visual multi-agent orchestrator and manager for Claude Code with 3D/2D interface",
5
5
  "repository": {
6
6
  "type": "git",