@glwhappen/web-code 1.32.6 → 1.32.7
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/{index-BceOOjmP.js → index-CCGk0QgG.js} +26 -24
- package/dist/assets/index-DdxLnCfK.css +32 -0
- package/dist/index.html +2 -2
- package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js +4 -4
- package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js +7 -1
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +7 -1
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js +7 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js +6 -0
- package/dist-server/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js +49 -39
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js +5 -3
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
- package/package.json +1 -1
- package/server/modules/projects/services/projects-with-sessions-fetch.service.ts +4 -4
- package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +9 -1
- package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +9 -1
- package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +9 -2
- package/server/modules/providers/list/gemini/gemini-session-synchronizer.provider.ts +8 -0
- package/server/modules/providers/services/sessions-watcher.service.ts +60 -40
- package/server/modules/websocket/services/chat-websocket.service.ts +5 -3
- package/server/shared/types.ts +1 -0
- package/dist/assets/index-hQY0reSj.css +0 -32
|
@@ -8,22 +8,22 @@ import { sessionSynchronizerService } from '@/modules/providers/services/session
|
|
|
8
8
|
import { WS_OPEN_STATE, connectedClients } from '@/modules/websocket/index.js';
|
|
9
9
|
import type { LLMProvider } from '@/shared/types.js';
|
|
10
10
|
import { getProjectsWithSessions } from '@/modules/projects/index.js';
|
|
11
|
-
import {
|
|
11
|
+
import { userDb } from '@/modules/database/index.js';
|
|
12
12
|
import { AppError } from '@/shared/utils.js';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* Resolves
|
|
15
|
+
* Resolves active app users that should receive background synchronizations.
|
|
16
16
|
*
|
|
17
|
-
* Returns
|
|
17
|
+
* Returns an empty list (with a console warning) when no user has registered yet so
|
|
18
18
|
* that the watcher can keep running silently until the first account exists.
|
|
19
19
|
*/
|
|
20
|
-
function
|
|
20
|
+
function resolveWatcherOwnerUserIds(): number[] {
|
|
21
21
|
try {
|
|
22
|
-
return
|
|
22
|
+
return userDb.listUsers().map((user) => Number(user.id)).filter((id) => Number.isFinite(id));
|
|
23
23
|
} catch (error) {
|
|
24
24
|
if (error instanceof AppError && error.code === 'DEFAULT_USER_NOT_AVAILABLE') {
|
|
25
25
|
console.warn('Session watcher skipping synchronization: no registered user yet');
|
|
26
|
-
return
|
|
26
|
+
return [];
|
|
27
27
|
}
|
|
28
28
|
throw error;
|
|
29
29
|
}
|
|
@@ -161,33 +161,38 @@ async function flushPendingWatcherUpdate(): Promise<void> {
|
|
|
161
161
|
watcherRefreshInFlight = true;
|
|
162
162
|
|
|
163
163
|
try {
|
|
164
|
-
const ownerUserId = resolveWatcherOwnerUserId();
|
|
165
|
-
const updatedProjects = ownerUserId !== null
|
|
166
|
-
? await getProjectsWithSessions(ownerUserId, { skipSynchronization: true })
|
|
167
|
-
: [];
|
|
168
164
|
const changeTypes = Array.from(queuedUpdate.changeTypes);
|
|
169
165
|
const watchProviders = Array.from(queuedUpdate.providers);
|
|
170
166
|
const updatedSessionIds = Array.from(queuedUpdate.updatedSessionIds);
|
|
167
|
+
const targetUserIds = Array.from(
|
|
168
|
+
new Set(
|
|
169
|
+
Array.from(connectedClients)
|
|
170
|
+
.map((client) => Number(client.userId))
|
|
171
|
+
.filter((userId) => Number.isFinite(userId)),
|
|
172
|
+
),
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
await Promise.all(targetUserIds.map(async (userId) => {
|
|
176
|
+
const updatedProjects = await getProjectsWithSessions(userId, { skipSynchronization: true });
|
|
177
|
+
const updateMessage = JSON.stringify({
|
|
178
|
+
type: 'projects_updated',
|
|
179
|
+
projects: updatedProjects,
|
|
180
|
+
timestamp: new Date().toISOString(),
|
|
181
|
+
changeType: changeTypes[0] ?? 'change',
|
|
182
|
+
updatedSessionId: updatedSessionIds[0] ?? undefined,
|
|
183
|
+
watchProvider: watchProviders[0] ?? undefined,
|
|
184
|
+
changeTypes,
|
|
185
|
+
updatedSessionIds,
|
|
186
|
+
watchProviders,
|
|
187
|
+
batched: true,
|
|
188
|
+
});
|
|
171
189
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
updatedSessionId: updatedSessionIds[0] ?? undefined,
|
|
179
|
-
watchProvider: watchProviders[0] ?? undefined,
|
|
180
|
-
changeTypes,
|
|
181
|
-
updatedSessionIds,
|
|
182
|
-
watchProviders,
|
|
183
|
-
batched: true,
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
connectedClients.forEach(client => {
|
|
187
|
-
if (client.readyState === WS_OPEN_STATE) {
|
|
188
|
-
client.send(updateMessage);
|
|
189
|
-
}
|
|
190
|
-
});
|
|
190
|
+
connectedClients.forEach((client) => {
|
|
191
|
+
if (client.readyState === WS_OPEN_STATE && Number(client.userId) === userId) {
|
|
192
|
+
client.send(updateMessage);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}));
|
|
191
196
|
} catch (error) {
|
|
192
197
|
const message = error instanceof Error ? error.message : String(error);
|
|
193
198
|
console.error('Session watcher refresh failed while broadcasting projects_updated', { error: message });
|
|
@@ -214,21 +219,28 @@ async function onUpdate(
|
|
|
214
219
|
}
|
|
215
220
|
|
|
216
221
|
try {
|
|
217
|
-
const
|
|
218
|
-
if (
|
|
222
|
+
const ownerUserIds = resolveWatcherOwnerUserIds();
|
|
223
|
+
if (ownerUserIds.length === 0) {
|
|
219
224
|
return;
|
|
220
225
|
}
|
|
221
226
|
|
|
222
|
-
const
|
|
223
|
-
|
|
227
|
+
const results = await Promise.all(
|
|
228
|
+
ownerUserIds.map((ownerUserId) =>
|
|
229
|
+
sessionSynchronizerService.synchronizeProviderFile(ownerUserId, provider, filePath),
|
|
230
|
+
),
|
|
231
|
+
);
|
|
232
|
+
const indexedResults = results.filter((result) => result.indexed);
|
|
233
|
+
if (indexedResults.length === 0) {
|
|
224
234
|
return;
|
|
225
235
|
}
|
|
226
236
|
|
|
227
237
|
console.log(`Session synchronization triggered by ${eventType} event for provider "${provider}"`, {
|
|
228
238
|
filePath,
|
|
229
|
-
|
|
239
|
+
sessionIds: indexedResults.map((result) => result.sessionId).filter(Boolean),
|
|
240
|
+
});
|
|
241
|
+
indexedResults.forEach((result) => {
|
|
242
|
+
queuePendingWatcherUpdate(eventType, provider, result.sessionId);
|
|
230
243
|
});
|
|
231
|
-
queuePendingWatcherUpdate(eventType, provider, result.sessionId);
|
|
232
244
|
} catch (error) {
|
|
233
245
|
const message = error instanceof Error ? error.message : String(error);
|
|
234
246
|
console.error(`Session watcher sync failed for provider "${provider}"`, {
|
|
@@ -245,12 +257,20 @@ async function onUpdate(
|
|
|
245
257
|
export async function initializeSessionsWatcher(): Promise<void> {
|
|
246
258
|
console.log('Setting up session watchers');
|
|
247
259
|
|
|
248
|
-
const
|
|
249
|
-
if (
|
|
250
|
-
const
|
|
260
|
+
const initialOwnerUserIds = resolveWatcherOwnerUserIds();
|
|
261
|
+
if (initialOwnerUserIds.length > 0) {
|
|
262
|
+
const initialSyncResults = await Promise.all(
|
|
263
|
+
initialOwnerUserIds.map(async (ownerUserId) => ({
|
|
264
|
+
userId: ownerUserId,
|
|
265
|
+
result: await sessionSynchronizerService.synchronizeSessions(ownerUserId),
|
|
266
|
+
})),
|
|
267
|
+
);
|
|
251
268
|
console.log('Initial session synchronization complete', {
|
|
252
|
-
|
|
253
|
-
|
|
269
|
+
users: initialSyncResults.map(({ userId, result }) => ({
|
|
270
|
+
userId,
|
|
271
|
+
processedByProvider: result.processedByProvider,
|
|
272
|
+
failures: result.failures,
|
|
273
|
+
})),
|
|
254
274
|
});
|
|
255
275
|
} else {
|
|
256
276
|
console.log('Initial session synchronization skipped: no registered user yet');
|
|
@@ -127,9 +127,11 @@ export function handleChatConnection(
|
|
|
127
127
|
dependencies: ChatWebSocketDependencies
|
|
128
128
|
): void {
|
|
129
129
|
console.log('[INFO] Chat WebSocket connected');
|
|
130
|
-
|
|
130
|
+
const clientConnection = ws as WebSocket & { userId?: string | number | null };
|
|
131
|
+
clientConnection.userId = readRequestUserId(request);
|
|
132
|
+
connectedClients.add(clientConnection);
|
|
131
133
|
|
|
132
|
-
const writer = new WebSocketWriter(ws,
|
|
134
|
+
const writer = new WebSocketWriter(ws, clientConnection.userId ?? null);
|
|
133
135
|
|
|
134
136
|
const sendAuthorizationError = (error: AppError, provider: LLMProvider): void => {
|
|
135
137
|
writer.send({
|
|
@@ -338,6 +340,6 @@ export function handleChatConnection(
|
|
|
338
340
|
|
|
339
341
|
ws.on('close', () => {
|
|
340
342
|
console.log('[INFO] Chat client disconnected');
|
|
341
|
-
connectedClients.delete(
|
|
343
|
+
connectedClients.delete(clientConnection);
|
|
342
344
|
});
|
|
343
345
|
}
|