@yeaft/webchat-agent 0.0.5 → 0.0.6
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/connection.js +49 -38
- package/package.json +1 -1
- package/workbench.js +61 -30
package/connection.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import WebSocket from 'ws';
|
|
2
|
-
import { execSync } from 'child_process';
|
|
2
|
+
import { execSync, execFile } from 'child_process';
|
|
3
3
|
import ctx from './context.js';
|
|
4
4
|
import { encrypt, decrypt, isEncrypted, decodeKey } from './encryption.js';
|
|
5
5
|
import { handleTerminalCreate, handleTerminalInput, handleTerminalResize, handleTerminalClose } from './terminal.js';
|
|
@@ -268,44 +268,55 @@ async function handleMessage(msg) {
|
|
|
268
268
|
|
|
269
269
|
case 'upgrade_agent':
|
|
270
270
|
console.log('[Agent] Upgrade requested, checking for updates...');
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
// Restart after upgrade (same as restart_agent)
|
|
285
|
-
setTimeout(() => {
|
|
286
|
-
for (const [, term] of ctx.terminals) {
|
|
287
|
-
if (term.pty) { try { term.pty.kill(); } catch {} }
|
|
288
|
-
if (term.timer) clearTimeout(term.timer);
|
|
289
|
-
}
|
|
290
|
-
ctx.terminals.clear();
|
|
291
|
-
for (const [, state] of ctx.conversations) {
|
|
292
|
-
if (state.abortController) state.abortController.abort();
|
|
293
|
-
if (state.inputStream) state.inputStream.done();
|
|
294
|
-
}
|
|
295
|
-
ctx.conversations.clear();
|
|
296
|
-
stopAgentHeartbeat();
|
|
297
|
-
if (ctx.ws) {
|
|
298
|
-
ctx.ws.removeAllListeners('close');
|
|
299
|
-
ctx.ws.close();
|
|
271
|
+
(async () => {
|
|
272
|
+
try {
|
|
273
|
+
const pkgName = ctx.pkgName || '@yeaft/webchat-agent';
|
|
274
|
+
// Check latest version (async to avoid blocking heartbeat)
|
|
275
|
+
const latestVersion = await new Promise((resolve, reject) => {
|
|
276
|
+
execFile('npm', ['view', pkgName, 'version'], { stdio: 'pipe' }, (err, stdout) => {
|
|
277
|
+
if (err) reject(err); else resolve(stdout.toString().trim());
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
if (latestVersion === ctx.agentVersion) {
|
|
281
|
+
console.log(`[Agent] Already at latest version (${ctx.agentVersion}), skipping upgrade.`);
|
|
282
|
+
sendToServer({ type: 'upgrade_agent_ack', success: true, alreadyLatest: true, version: ctx.agentVersion });
|
|
283
|
+
return;
|
|
300
284
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
285
|
+
console.log(`[Agent] Upgrading from ${ctx.agentVersion} to ${latestVersion}...`);
|
|
286
|
+
// Use async execFile to avoid blocking event loop (heartbeat must keep running)
|
|
287
|
+
await new Promise((resolve, reject) => {
|
|
288
|
+
execFile('npm', ['install', '-g', `${pkgName}@latest`], { stdio: 'pipe' }, (err) => {
|
|
289
|
+
if (err) reject(err); else resolve();
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
console.log('[Agent] Upgrade successful, restarting...');
|
|
293
|
+
sendToServer({ type: 'upgrade_agent_ack', success: true, version: latestVersion });
|
|
294
|
+
// Restart after upgrade (same as restart_agent)
|
|
295
|
+
setTimeout(() => {
|
|
296
|
+
for (const [, term] of ctx.terminals) {
|
|
297
|
+
if (term.pty) { try { term.pty.kill(); } catch {} }
|
|
298
|
+
if (term.timer) clearTimeout(term.timer);
|
|
299
|
+
}
|
|
300
|
+
ctx.terminals.clear();
|
|
301
|
+
for (const [, state] of ctx.conversations) {
|
|
302
|
+
if (state.abortController) state.abortController.abort();
|
|
303
|
+
if (state.inputStream) state.inputStream.done();
|
|
304
|
+
}
|
|
305
|
+
ctx.conversations.clear();
|
|
306
|
+
stopAgentHeartbeat();
|
|
307
|
+
if (ctx.ws) {
|
|
308
|
+
ctx.ws.removeAllListeners('close');
|
|
309
|
+
ctx.ws.close();
|
|
310
|
+
}
|
|
311
|
+
clearTimeout(ctx.reconnectTimer);
|
|
312
|
+
console.log('[Agent] Cleanup done, exiting for auto-restart...');
|
|
313
|
+
process.exit(1);
|
|
314
|
+
}, 500);
|
|
315
|
+
} catch (e) {
|
|
316
|
+
console.error('[Agent] Upgrade failed:', e.message);
|
|
317
|
+
sendToServer({ type: 'upgrade_agent_ack', success: false, error: e.message });
|
|
318
|
+
}
|
|
319
|
+
})();
|
|
309
320
|
break;
|
|
310
321
|
}
|
|
311
322
|
}
|
package/package.json
CHANGED
package/workbench.js
CHANGED
|
@@ -31,6 +31,19 @@ export function validateGitPath(filePath) {
|
|
|
31
31
|
return filePath && !/[`$;|&><!\n\r]/.test(filePath);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
// Binary file extensions → MIME type mapping
|
|
35
|
+
const BINARY_EXTENSIONS = {
|
|
36
|
+
'.pdf': 'application/pdf',
|
|
37
|
+
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
38
|
+
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
39
|
+
'.xls': 'application/vnd.ms-excel',
|
|
40
|
+
'.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
41
|
+
'.ppt': 'application/vnd.ms-powerpoint',
|
|
42
|
+
'.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg',
|
|
43
|
+
'.gif': 'image/gif', '.webp': 'image/webp', '.bmp': 'image/bmp',
|
|
44
|
+
'.ico': 'image/x-icon'
|
|
45
|
+
};
|
|
46
|
+
|
|
34
47
|
export async function handleReadFile(msg) {
|
|
35
48
|
const { conversationId, filePath, _requestUserId } = msg;
|
|
36
49
|
console.log('[Agent] handleReadFile received:', { filePath, conversationId, workDir: msg.workDir });
|
|
@@ -39,37 +52,55 @@ export async function handleReadFile(msg) {
|
|
|
39
52
|
|
|
40
53
|
try {
|
|
41
54
|
const resolved = resolveAndValidatePath(filePath, workDir);
|
|
42
|
-
const content = await readFile(resolved, 'utf-8');
|
|
43
|
-
|
|
44
|
-
// 检测语言
|
|
45
55
|
const ext = extname(resolved).toLowerCase();
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
56
|
+
const mimeType = BINARY_EXTENSIONS[ext];
|
|
57
|
+
|
|
58
|
+
if (mimeType) {
|
|
59
|
+
// Binary file: read as Buffer, send base64
|
|
60
|
+
const buffer = await readFile(resolved);
|
|
61
|
+
console.log('[Agent] Sending binary file_content:', { filePath: resolved, size: buffer.length, mimeType, conversationId });
|
|
62
|
+
ctx.sendToServer({
|
|
63
|
+
type: 'file_content',
|
|
64
|
+
conversationId,
|
|
65
|
+
_requestUserId,
|
|
66
|
+
filePath: resolved,
|
|
67
|
+
content: buffer.toString('base64'),
|
|
68
|
+
binary: true,
|
|
69
|
+
mimeType
|
|
70
|
+
});
|
|
71
|
+
} else {
|
|
72
|
+
// Text file: read as utf-8
|
|
73
|
+
const content = await readFile(resolved, 'utf-8');
|
|
74
|
+
|
|
75
|
+
// 检测语言
|
|
76
|
+
const langMap = {
|
|
77
|
+
'.js': 'javascript', '.mjs': 'javascript', '.cjs': 'javascript',
|
|
78
|
+
'.ts': 'javascript', '.tsx': 'javascript', '.jsx': 'javascript',
|
|
79
|
+
'.py': 'python', '.pyw': 'python',
|
|
80
|
+
'.html': 'htmlmixed', '.htm': 'htmlmixed',
|
|
81
|
+
'.css': 'css', '.scss': 'css', '.less': 'css',
|
|
82
|
+
'.json': 'javascript',
|
|
83
|
+
'.md': 'markdown',
|
|
84
|
+
'.sh': 'shell', '.bash': 'shell', '.zsh': 'shell',
|
|
85
|
+
'.cs': 'text/x-csharp', '.java': 'text/x-java',
|
|
86
|
+
'.cpp': 'text/x-c++src', '.c': 'text/x-csrc', '.h': 'text/x-csrc',
|
|
87
|
+
'.xml': 'xml', '.svg': 'xml',
|
|
88
|
+
'.yaml': 'yaml', '.yml': 'yaml',
|
|
89
|
+
'.sql': 'sql',
|
|
90
|
+
'.go': 'go', '.rs': 'rust', '.rb': 'ruby',
|
|
91
|
+
'.php': 'php', '.swift': 'swift'
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
console.log('[Agent] Sending file_content:', { filePath: resolved, contentLen: content.length, conversationId });
|
|
95
|
+
ctx.sendToServer({
|
|
96
|
+
type: 'file_content',
|
|
97
|
+
conversationId,
|
|
98
|
+
_requestUserId,
|
|
99
|
+
filePath: resolved,
|
|
100
|
+
content,
|
|
101
|
+
language: langMap[ext] || null
|
|
102
|
+
});
|
|
103
|
+
}
|
|
73
104
|
} catch (e) {
|
|
74
105
|
ctx.sendToServer({
|
|
75
106
|
type: 'file_content',
|