@yeaft/webchat-agent 0.0.2
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/claude.js +405 -0
- package/cli.js +151 -0
- package/connection.js +391 -0
- package/context.js +26 -0
- package/conversation.js +452 -0
- package/encryption.js +105 -0
- package/history.js +283 -0
- package/index.js +159 -0
- package/package.json +75 -0
- package/proxy.js +169 -0
- package/sdk/index.js +9 -0
- package/sdk/query.js +396 -0
- package/sdk/stream.js +112 -0
- package/sdk/types.js +13 -0
- package/sdk/utils.js +194 -0
- package/service.js +587 -0
- package/terminal.js +176 -0
- package/workbench.js +907 -0
package/terminal.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { platform } from 'os';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
import ctx from './context.js';
|
|
4
|
+
|
|
5
|
+
// 动态加载 node-pty (optionalDependency)
|
|
6
|
+
export async function loadNodePty() {
|
|
7
|
+
if (ctx.nodePty !== null) return ctx.nodePty;
|
|
8
|
+
try {
|
|
9
|
+
let pty = await import('node-pty');
|
|
10
|
+
if (pty.default) pty = pty.default;
|
|
11
|
+
ctx.nodePty = pty;
|
|
12
|
+
console.log('[PTY] node-pty loaded successfully');
|
|
13
|
+
return pty;
|
|
14
|
+
} catch (e) {
|
|
15
|
+
console.warn('[PTY] node-pty not available:', e.message);
|
|
16
|
+
ctx.nodePty = false;
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function handleTerminalCreate(msg) {
|
|
22
|
+
const { conversationId, cols, rows } = msg;
|
|
23
|
+
const terminalId = msg.terminalId || conversationId;
|
|
24
|
+
const conv = ctx.conversations.get(conversationId);
|
|
25
|
+
const workDir = conv?.workDir || ctx.CONFIG.workDir;
|
|
26
|
+
|
|
27
|
+
// 如果已存在终端,先关闭
|
|
28
|
+
if (ctx.terminals.has(terminalId)) {
|
|
29
|
+
const existing = ctx.terminals.get(terminalId);
|
|
30
|
+
if (existing.pty) {
|
|
31
|
+
try { existing.pty.kill(); } catch {}
|
|
32
|
+
}
|
|
33
|
+
if (existing.timer) clearTimeout(existing.timer);
|
|
34
|
+
ctx.terminals.delete(terminalId);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const pty = await loadNodePty();
|
|
38
|
+
if (!pty) {
|
|
39
|
+
ctx.sendToServer({
|
|
40
|
+
type: 'terminal_error',
|
|
41
|
+
conversationId,
|
|
42
|
+
terminalId,
|
|
43
|
+
message: 'node-pty is not installed. Run: npm install node-pty'
|
|
44
|
+
});
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const shell = platform() === 'win32'
|
|
50
|
+
? (existsSync('C:\\Program Files\\PowerShell\\7\\pwsh.exe')
|
|
51
|
+
? 'C:\\Program Files\\PowerShell\\7\\pwsh.exe'
|
|
52
|
+
: (existsSync(`${process.env.SystemRoot || 'C:\\Windows'}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`)
|
|
53
|
+
? `${process.env.SystemRoot || 'C:\\Windows'}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`
|
|
54
|
+
: (process.env.COMSPEC || 'cmd.exe')))
|
|
55
|
+
: (process.env.SHELL || 'bash');
|
|
56
|
+
const ptyProcess = pty.spawn(shell, [], {
|
|
57
|
+
name: 'xterm-256color',
|
|
58
|
+
cols: cols || 80,
|
|
59
|
+
rows: rows || 24,
|
|
60
|
+
cwd: workDir,
|
|
61
|
+
env: process.env
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// 输出缓冲 - 每 16ms 批量发送
|
|
65
|
+
let buffer = '';
|
|
66
|
+
let timer = null;
|
|
67
|
+
|
|
68
|
+
ptyProcess.onData(data => {
|
|
69
|
+
buffer += data;
|
|
70
|
+
if (!timer) {
|
|
71
|
+
timer = setTimeout(() => {
|
|
72
|
+
ctx.sendToServer({
|
|
73
|
+
type: 'terminal_output',
|
|
74
|
+
conversationId,
|
|
75
|
+
terminalId,
|
|
76
|
+
data: buffer
|
|
77
|
+
});
|
|
78
|
+
buffer = '';
|
|
79
|
+
timer = null;
|
|
80
|
+
}, 16);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
ptyProcess.onExit(({ exitCode }) => {
|
|
85
|
+
// 发送剩余缓冲
|
|
86
|
+
if (buffer) {
|
|
87
|
+
ctx.sendToServer({
|
|
88
|
+
type: 'terminal_output',
|
|
89
|
+
conversationId,
|
|
90
|
+
terminalId,
|
|
91
|
+
data: buffer
|
|
92
|
+
});
|
|
93
|
+
buffer = '';
|
|
94
|
+
}
|
|
95
|
+
if (timer) clearTimeout(timer);
|
|
96
|
+
|
|
97
|
+
console.log(`[PTY] Process exited for ${terminalId}, code: ${exitCode}`);
|
|
98
|
+
ctx.terminals.delete(terminalId);
|
|
99
|
+
ctx.sendToServer({
|
|
100
|
+
type: 'terminal_closed',
|
|
101
|
+
conversationId,
|
|
102
|
+
terminalId
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
ctx.terminals.set(terminalId, {
|
|
107
|
+
pty: ptyProcess,
|
|
108
|
+
conversationId,
|
|
109
|
+
cols: cols || 80,
|
|
110
|
+
rows: rows || 24,
|
|
111
|
+
buffer: '',
|
|
112
|
+
timer: null
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
console.log(`[PTY] Created terminal ${terminalId} for ${conversationId} in ${workDir}`);
|
|
116
|
+
ctx.sendToServer({
|
|
117
|
+
type: 'terminal_created',
|
|
118
|
+
conversationId,
|
|
119
|
+
terminalId,
|
|
120
|
+
success: true
|
|
121
|
+
});
|
|
122
|
+
} catch (e) {
|
|
123
|
+
console.error(`[PTY] Failed to create terminal:`, e.message);
|
|
124
|
+
ctx.sendToServer({
|
|
125
|
+
type: 'terminal_error',
|
|
126
|
+
conversationId,
|
|
127
|
+
terminalId,
|
|
128
|
+
message: `Failed to create terminal: ${e.message}`
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function handleTerminalInput(msg) {
|
|
134
|
+
const terminalId = msg.terminalId || msg.conversationId;
|
|
135
|
+
const term = ctx.terminals.get(terminalId);
|
|
136
|
+
if (term?.pty) {
|
|
137
|
+
try {
|
|
138
|
+
term.pty.write(msg.data);
|
|
139
|
+
} catch (e) {
|
|
140
|
+
console.error(`[PTY] Write error for ${terminalId}:`, e.message);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function handleTerminalResize(msg) {
|
|
146
|
+
const terminalId = msg.terminalId || msg.conversationId;
|
|
147
|
+
const { cols, rows } = msg;
|
|
148
|
+
const term = ctx.terminals.get(terminalId);
|
|
149
|
+
if (term?.pty && cols > 0 && rows > 0) {
|
|
150
|
+
try {
|
|
151
|
+
term.pty.resize(cols, rows);
|
|
152
|
+
term.cols = cols;
|
|
153
|
+
term.rows = rows;
|
|
154
|
+
} catch (e) {
|
|
155
|
+
console.error(`[PTY] Resize error for ${terminalId}:`, e.message);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function handleTerminalClose(msg) {
|
|
161
|
+
const terminalId = msg.terminalId || msg.conversationId;
|
|
162
|
+
const term = ctx.terminals.get(terminalId);
|
|
163
|
+
if (term) {
|
|
164
|
+
if (term.pty) {
|
|
165
|
+
try { term.pty.kill(); } catch {}
|
|
166
|
+
}
|
|
167
|
+
if (term.timer) clearTimeout(term.timer);
|
|
168
|
+
ctx.terminals.delete(terminalId);
|
|
169
|
+
console.log(`[PTY] Closed terminal ${terminalId}`);
|
|
170
|
+
ctx.sendToServer({
|
|
171
|
+
type: 'terminal_closed',
|
|
172
|
+
conversationId: term.conversationId || msg.conversationId,
|
|
173
|
+
terminalId
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|