@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.
Files changed (3) hide show
  1. package/connection.js +49 -38
  2. package/package.json +1 -1
  3. 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
- try {
272
- const pkgName = ctx.pkgName || '@yeaft/webchat-agent';
273
- // Check latest version before installing
274
- const latestVersion = execSync(`npm view ${pkgName} version`, { stdio: 'pipe' }).toString().trim();
275
- if (latestVersion === ctx.agentVersion) {
276
- console.log(`[Agent] Already at latest version (${ctx.agentVersion}), skipping upgrade.`);
277
- sendToServer({ type: 'upgrade_agent_ack', success: true, alreadyLatest: true, version: ctx.agentVersion });
278
- break;
279
- }
280
- console.log(`[Agent] Upgrading from ${ctx.agentVersion} to ${latestVersion}...`);
281
- execSync(`npm install -g ${pkgName}@latest`, { stdio: 'pipe' });
282
- console.log('[Agent] Upgrade successful, restarting...');
283
- sendToServer({ type: 'upgrade_agent_ack', success: true, version: latestVersion });
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
- clearTimeout(ctx.reconnectTimer);
302
- console.log('[Agent] Cleanup done, exiting for auto-restart...');
303
- process.exit(1);
304
- }, 500);
305
- } catch (e) {
306
- console.error('[Agent] Upgrade failed:', e.message);
307
- sendToServer({ type: 'upgrade_agent_ack', success: false, error: e.message });
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",
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 langMap = {
47
- '.js': 'javascript', '.mjs': 'javascript', '.cjs': 'javascript',
48
- '.ts': 'javascript', '.tsx': 'javascript', '.jsx': 'javascript',
49
- '.py': 'python', '.pyw': 'python',
50
- '.html': 'htmlmixed', '.htm': 'htmlmixed',
51
- '.css': 'css', '.scss': 'css', '.less': 'css',
52
- '.json': 'javascript',
53
- '.md': 'markdown',
54
- '.sh': 'shell', '.bash': 'shell', '.zsh': 'shell',
55
- '.cs': 'text/x-csharp', '.java': 'text/x-java',
56
- '.cpp': 'text/x-c++src', '.c': 'text/x-csrc', '.h': 'text/x-csrc',
57
- '.xml': 'xml', '.svg': 'xml',
58
- '.yaml': 'yaml', '.yml': 'yaml',
59
- '.sql': 'sql',
60
- '.go': 'go', '.rs': 'rust', '.rb': 'ruby',
61
- '.php': 'php', '.swift': 'swift'
62
- };
63
-
64
- console.log('[Agent] Sending file_content:', { filePath: resolved, contentLen: content.length, conversationId });
65
- ctx.sendToServer({
66
- type: 'file_content',
67
- conversationId,
68
- _requestUserId,
69
- filePath: resolved,
70
- content,
71
- language: langMap[ext] || null
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',