cc-team-viewer 1.5.4 → 1.5.5
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/cli/cli.js +12 -0
- package/package.json +1 -1
- package/proxy/interceptor.js +23 -3
- package/server/mobile-bridge.js +24 -21
- package/server/sse.js +1 -1
package/cli/cli.js
CHANGED
|
@@ -247,6 +247,18 @@ async function runProxyCommand(args) {
|
|
|
247
247
|
cmdArgs.unshift(settingsJson);
|
|
248
248
|
cmdArgs.unshift('--settings');
|
|
249
249
|
|
|
250
|
+
// 打印 viewer 访问地址(服务器由 interceptor 自动启动)
|
|
251
|
+
// 等待 viewer 服务器就绪后打印
|
|
252
|
+
const { getPort } = await import('../server/server.js');
|
|
253
|
+
await new Promise(resolve => {
|
|
254
|
+
const check = () => { getPort() ? resolve() : setTimeout(check, 200); };
|
|
255
|
+
setTimeout(check, 300);
|
|
256
|
+
});
|
|
257
|
+
const viewerPort = getPort();
|
|
258
|
+
if (viewerPort) {
|
|
259
|
+
console.error(`\n[cctv] 监控面板: http://127.0.0.1:${viewerPort}\n`);
|
|
260
|
+
}
|
|
261
|
+
|
|
250
262
|
const child = spawn(cmd, cmdArgs, { stdio: 'inherit', env });
|
|
251
263
|
|
|
252
264
|
child.on('exit', (code) => {
|
package/package.json
CHANGED
package/proxy/interceptor.js
CHANGED
|
@@ -10,6 +10,8 @@ import { dirname, join, basename } from 'node:path';
|
|
|
10
10
|
import { LOG_DIR } from '../cli/findcc.js';
|
|
11
11
|
import { initLogger, logInfo, logWarn, logError, logDebug } from '../lib/logger.js';
|
|
12
12
|
|
|
13
|
+
const MOBILE_DEBUG = process.env.CCV_MOBILE_DEBUG === '1';
|
|
14
|
+
const mobileLog = MOBILE_DEBUG ? (...args) => logDebug('[MOBILE-DEBUG]', ...args) : () => {};
|
|
13
15
|
|
|
14
16
|
const __filename = fileURLToPath(import.meta.url);
|
|
15
17
|
const __dirname = dirname(__filename);
|
|
@@ -605,15 +607,15 @@ export function setOnLogWrittenCallback(callback) {
|
|
|
605
607
|
* @param {Object|Array} newEntries - 新增的日志条目(单个对象或数组)
|
|
606
608
|
*/
|
|
607
609
|
function triggerLogWrittenCallback(newEntries) {
|
|
608
|
-
|
|
610
|
+
mobileLog('triggerLogWrittenCallback called, hasCallback:', !!_onLogWrittenCallback);
|
|
609
611
|
if (_onLogWrittenCallback) {
|
|
610
612
|
try {
|
|
611
613
|
// 统一转为数组格式
|
|
612
614
|
const entries = Array.isArray(newEntries) ? newEntries : (newEntries ? [newEntries] : []);
|
|
613
|
-
|
|
615
|
+
mobileLog('calling callback with', entries.length, 'entries');
|
|
614
616
|
_onLogWrittenCallback(entries);
|
|
615
617
|
} catch (err) {
|
|
616
|
-
|
|
618
|
+
mobileLog('callback error:', err.message);
|
|
617
619
|
}
|
|
618
620
|
}
|
|
619
621
|
}
|
|
@@ -668,6 +670,24 @@ export function setupInterceptor() {
|
|
|
668
670
|
stdio: 'ignore',
|
|
669
671
|
env: { ...process.env, CCV_DETACHED: '1' }
|
|
670
672
|
}).unref();
|
|
673
|
+
|
|
674
|
+
// 等待 viewer 服务器就绪后打印访问地址
|
|
675
|
+
const waitAndPrint = async () => {
|
|
676
|
+
for (let i = 0; i < 30; i++) {
|
|
677
|
+
await new Promise(r => setTimeout(r, 500));
|
|
678
|
+
for (let p = 7008; p <= 7020; p++) {
|
|
679
|
+
const ok = await new Promise(res => {
|
|
680
|
+
const c = connect(p, '127.0.0.1', () => { c.end(); res(true); });
|
|
681
|
+
c.on('error', () => res(false));
|
|
682
|
+
});
|
|
683
|
+
if (ok) {
|
|
684
|
+
process.stderr.write(`\n[cctv] 监控面板: http://127.0.0.1:${p}\n`);
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
waitAndPrint().catch(() => {});
|
|
671
691
|
};
|
|
672
692
|
|
|
673
693
|
tryStartProxy().catch(() => {});
|
package/server/mobile-bridge.js
CHANGED
|
@@ -18,6 +18,9 @@ import { readLogFile } from './sse.js';
|
|
|
18
18
|
import { buildSessionsFromEntries, assignMessageTimestamps } from '../src/utils/sessionBuilder.js';
|
|
19
19
|
import { classifyUserContent } from '../src/utils/contentFilter.js';
|
|
20
20
|
|
|
21
|
+
const MOBILE_DEBUG = process.env.CCV_MOBILE_DEBUG === '1';
|
|
22
|
+
const mobileLog = MOBILE_DEBUG ? (...args) => console.log('[MOBILE-DEBUG]', ...args) : () => {};
|
|
23
|
+
|
|
21
24
|
let actualPort = 0;
|
|
22
25
|
let accessToken = '';
|
|
23
26
|
let mobileWss = null;
|
|
@@ -173,10 +176,10 @@ function simplifyMessage(message, index) {
|
|
|
173
176
|
|
|
174
177
|
function buildLatestChatSnapshot() {
|
|
175
178
|
const entries = readLogFile();
|
|
176
|
-
|
|
179
|
+
mobileLog('buildLatestChatSnapshot: entries count:', entries?.length || 0);
|
|
177
180
|
|
|
178
181
|
if (!Array.isArray(entries) || entries.length === 0) {
|
|
179
|
-
|
|
182
|
+
mobileLog('buildLatestChatSnapshot: no entries, returning empty snapshot');
|
|
180
183
|
return {
|
|
181
184
|
type: 'chat_snapshot',
|
|
182
185
|
connectionSessionId: connectionState.connectionSessionId || '',
|
|
@@ -190,26 +193,26 @@ function buildLatestChatSnapshot() {
|
|
|
190
193
|
|
|
191
194
|
assignMessageTimestamps(entries);
|
|
192
195
|
const sessions = buildSessionsFromEntries(entries);
|
|
193
|
-
|
|
196
|
+
mobileLog('buildLatestChatSnapshot: sessions count:', sessions?.length || 0);
|
|
194
197
|
|
|
195
198
|
// DEBUG: 检查第一条 entry 的 mainAgent 标记
|
|
196
199
|
if (entries.length > 0 && sessions.length === 0) {
|
|
197
200
|
const first = entries[0];
|
|
198
|
-
|
|
201
|
+
mobileLog('DEBUG: first entry has mainAgent:', first?.mainAgent, 'url:', first?.url, 'timestamp:', first?.timestamp);
|
|
199
202
|
// 检查 body.messages
|
|
200
203
|
const hasMessages = Array.isArray(first?.body?.messages);
|
|
201
|
-
|
|
204
|
+
mobileLog('DEBUG: first entry has body.messages:', hasMessages, 'count:', first?.body?.messages?.length);
|
|
202
205
|
// 检查 system
|
|
203
206
|
const hasSystem = !!first?.body?.system;
|
|
204
|
-
|
|
207
|
+
mobileLog('DEBUG: first entry has body.system:', hasSystem);
|
|
205
208
|
const sysText = Array.isArray(first?.body?.system)
|
|
206
209
|
? first.body.system.map(s => s?.text || '').join('')
|
|
207
210
|
: (first?.body?.system || '');
|
|
208
|
-
|
|
211
|
+
mobileLog('DEBUG: system contains "Claude Code":', sysText.includes('Claude Code'));
|
|
209
212
|
}
|
|
210
213
|
|
|
211
214
|
const latestSession = sessions[sessions.length - 1];
|
|
212
|
-
|
|
215
|
+
mobileLog('buildLatestChatSnapshot: latestSession messages:', latestSession?.messages?.length || 0);
|
|
213
216
|
|
|
214
217
|
const messages = Array.isArray(latestSession?.messages)
|
|
215
218
|
? latestSession.messages
|
|
@@ -217,7 +220,7 @@ function buildLatestChatSnapshot() {
|
|
|
217
220
|
.filter(msg => msg.text || msg.role !== 'user')
|
|
218
221
|
: [];
|
|
219
222
|
|
|
220
|
-
|
|
223
|
+
mobileLog('buildLatestChatSnapshot: final messages after filter:', messages.length);
|
|
221
224
|
|
|
222
225
|
const latestReply = normalizeTextContent(latestSession?.response?.body?.content);
|
|
223
226
|
|
|
@@ -281,7 +284,7 @@ function sendHubMessage(message) {
|
|
|
281
284
|
}
|
|
282
285
|
|
|
283
286
|
function broadcastLocal(message) {
|
|
284
|
-
|
|
287
|
+
mobileLog('broadcastLocal: clients count:', mobileClients.size);
|
|
285
288
|
const payload = typeof message === 'string' ? message : JSON.stringify(message);
|
|
286
289
|
for (const client of mobileClients) {
|
|
287
290
|
if (client.readyState === WebSocket.OPEN) {
|
|
@@ -293,7 +296,7 @@ function broadcastLocal(message) {
|
|
|
293
296
|
}
|
|
294
297
|
|
|
295
298
|
function broadcast(message, { includeHub = true } = {}) {
|
|
296
|
-
|
|
299
|
+
mobileLog('broadcast called');
|
|
297
300
|
broadcastLocal(message);
|
|
298
301
|
if (includeHub) {
|
|
299
302
|
sendHubMessage(message);
|
|
@@ -319,26 +322,26 @@ function broadcastState({ includeHub = true } = {}) {
|
|
|
319
322
|
}
|
|
320
323
|
|
|
321
324
|
function maybeBroadcastChatSnapshot({ includeHub = true } = {}) {
|
|
322
|
-
|
|
325
|
+
mobileLog('maybeBroadcastChatSnapshot called, active:', connectionState.active);
|
|
323
326
|
if (!connectionState.active) {
|
|
324
|
-
|
|
327
|
+
mobileLog('skipped: connection not active');
|
|
325
328
|
return;
|
|
326
329
|
}
|
|
327
330
|
const snapshot = buildLatestChatSnapshot();
|
|
328
|
-
|
|
331
|
+
mobileLog('snapshot built, messages:', snapshot?.messages?.length, 'cached:', cachedMessages.length);
|
|
329
332
|
// 去重时排除时间戳字段,只比较消息内容
|
|
330
333
|
const { updatedAt, ...contentOnly } = snapshot;
|
|
331
334
|
const nextJson = JSON.stringify(contentOnly);
|
|
332
335
|
|
|
333
336
|
// DEBUG: 输出 JSON 长度变化,帮助诊断问题
|
|
334
|
-
|
|
337
|
+
mobileLog('json len:', nextJson.length, 'vs last:', lastChatSnapshotJson.length, 'equal:', nextJson === lastChatSnapshotJson);
|
|
335
338
|
|
|
336
339
|
if (nextJson === lastChatSnapshotJson) {
|
|
337
|
-
|
|
340
|
+
mobileLog('skipped: snapshot unchanged (dedup)');
|
|
338
341
|
return;
|
|
339
342
|
}
|
|
340
343
|
lastChatSnapshotJson = nextJson;
|
|
341
|
-
|
|
344
|
+
mobileLog('broadcasting snapshot to', mobileClients.size, 'clients');
|
|
342
345
|
// 更新缓存
|
|
343
346
|
cachedMessages = snapshot.messages;
|
|
344
347
|
cachedLatestReply = snapshot.latestReply;
|
|
@@ -501,7 +504,7 @@ async function handlePromptAnswer(answer, sessionId) {
|
|
|
501
504
|
}
|
|
502
505
|
|
|
503
506
|
function handlePtyData(data) {
|
|
504
|
-
|
|
507
|
+
mobileLog('handlePtyData called, data length:', data?.length, 'active:', connectionState.active, 'mode:', connectionState.mode);
|
|
505
508
|
const clean = stripAnsi(data);
|
|
506
509
|
if (!clean) return;
|
|
507
510
|
ptyBuffer += clean;
|
|
@@ -520,7 +523,7 @@ function handlePtyData(data) {
|
|
|
520
523
|
timestamp: new Date().toISOString(),
|
|
521
524
|
});
|
|
522
525
|
} else {
|
|
523
|
-
|
|
526
|
+
mobileLog('terminal_output NOT sent: hubSocket=', hubSocket ? hubSocket.readyState : 'null', 'hubConnected=', connectionState.hubConnected);
|
|
524
527
|
}
|
|
525
528
|
}
|
|
526
529
|
}
|
|
@@ -743,7 +746,7 @@ export function initMobileBridge({ server, port, token, projectName }) {
|
|
|
743
746
|
ptyExitUnsubscribe = onPtyExit(handlePtyExit);
|
|
744
747
|
|
|
745
748
|
mobileWss.on('connection', (ws) => {
|
|
746
|
-
|
|
749
|
+
mobileLog('mobile client connected');
|
|
747
750
|
mobileClients.add(ws);
|
|
748
751
|
refreshConnectedClients();
|
|
749
752
|
sendJson(ws, buildConnectionStateMessage());
|
|
@@ -978,7 +981,7 @@ export function stopMobileConnection() {
|
|
|
978
981
|
}
|
|
979
982
|
|
|
980
983
|
export function notifyMobileLogEntries(newEntries) {
|
|
981
|
-
|
|
984
|
+
mobileLog('notifyMobileLogEntries called, entries:', newEntries?.length, 'active:', connectionState.active);
|
|
982
985
|
// 直接走完整日志重建路径
|
|
983
986
|
// 实时性来自 interceptor 的直接回调(跳过 fs.watchFile 轮询延迟)
|
|
984
987
|
// 可靠性来自完整日志重建(避免增量逻辑的各种边界问题)
|
package/server/sse.js
CHANGED
|
@@ -446,7 +446,7 @@ export function watchLogDirectory() {
|
|
|
446
446
|
|
|
447
447
|
if (entries.length > 0) {
|
|
448
448
|
// 通知移动端,传递新增条目以支持增量构建
|
|
449
|
-
|
|
449
|
+
debugLog('sse.js handleFileChange triggered, calling notifyMobileLogEntries');
|
|
450
450
|
notifyMobileLogEntries(entries);
|
|
451
451
|
// 根据日志文件路径查找对应的 team
|
|
452
452
|
let team = getTeamByLogFile(filePath);
|