@yeaft/webchat-agent 1.0.413 → 1.0.415
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/browser-runtime/browser-install.js +497 -0
- package/browser-runtime/cli.js +88 -0
- package/browser-runtime/config.js +116 -0
- package/browser-runtime/errors.js +8 -0
- package/browser-runtime/extension/manifest.json +18 -0
- package/browser-runtime/extension/offscreen.html +5 -0
- package/browser-runtime/extension/offscreen.js +101 -0
- package/browser-runtime/extension/popup.html +5 -0
- package/browser-runtime/extension/popup.js +1 -0
- package/browser-runtime/extension/service-worker.js +48 -0
- package/browser-runtime/extension.js +45 -0
- package/browser-runtime/index.js +5 -0
- package/browser-runtime/probe.js +427 -0
- package/browser-runtime/protocol.js +71 -0
- package/browser-runtime/service.js +132 -0
- package/browser-runtime/windows-version-job.ps1 +233 -0
- package/browser-runtime/windows-version-worker.js +75 -0
- package/browser-runtime/windows-version.js +85 -0
- package/cli.js +24 -7
- package/connection/index.js +12 -0
- package/context.js +1 -0
- package/index.js +19 -2
- package/llm-config-cli.js +24 -21
- package/local-runtime/server/client-protocol.js +14 -0
- package/local-runtime/server/context.js +3 -2
- package/local-runtime/server/handlers/agent-file-terminal.js +185 -115
- package/local-runtime/server/handlers/agent-output.js +3 -0
- package/local-runtime/server/handlers/client-misc.js +21 -4
- package/local-runtime/server/handlers/client-workbench.js +222 -41
- package/local-runtime/server/workbench-correlation.js +184 -0
- package/local-runtime/server/workbench-route.js +180 -0
- package/local-runtime/server/ws-agent.js +4 -0
- package/local-runtime/server/ws-client.js +25 -3
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +191 -135
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/index.html +2 -2
- package/local-runtime/web/style.bundle.css +1 -1
- package/local-runtime/web/style.bundle.css.gz +0 -0
- package/package.json +5 -1
- package/service/config.js +23 -2
- package/service/index.js +1 -0
- package/service/linux.js +3 -2
- package/terminal.js +167 -30
- package/workbench/file-ops.js +21 -20
- package/workbench/file-search.js +4 -3
- package/workbench/git-ops.js +23 -22
- package/workbench/request-routing.js +16 -0
- package/yeaft/cli.js +57 -1
- package/yeaft/config-api.js +138 -192
- package/yeaft/config-store.js +192 -0
- package/yeaft/config.js +3 -0
- package/yeaft/init.js +20 -7
- package/yeaft/sessions/feature-flag.js +15 -33
- package/yeaft/sessions/session-manifest.js +114 -10
- package/yeaft/stdio-protocol.js +57 -0
- package/yeaft/storage/atomic.js +43 -17
- package/yeaft/tools/process-runner.js +86 -13
package/workbench/git-ops.js
CHANGED
|
@@ -2,6 +2,7 @@ import { readFile, writeFile } from 'fs/promises';
|
|
|
2
2
|
import { join, resolve } from 'path';
|
|
3
3
|
import ctx from '../context.js';
|
|
4
4
|
import { execAsync, resolveAndValidatePath, getGitRoot, validateGitPath } from './utils.js';
|
|
5
|
+
import { sendWorkbenchResult } from './request-routing.js';
|
|
5
6
|
|
|
6
7
|
export async function handleGitStatus(msg) {
|
|
7
8
|
const { conversationId, _requestUserId } = msg;
|
|
@@ -58,7 +59,7 @@ export async function handleGitStatus(msg) {
|
|
|
58
59
|
files.push({ path: displayPath, indexStatus, workTreeStatus });
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
ctx
|
|
62
|
+
sendWorkbenchResult(ctx, msg, {
|
|
62
63
|
type: 'git_status_result',
|
|
63
64
|
conversationId,
|
|
64
65
|
_requestUserId,
|
|
@@ -70,7 +71,7 @@ export async function handleGitStatus(msg) {
|
|
|
70
71
|
gitRoot
|
|
71
72
|
});
|
|
72
73
|
} catch (e) {
|
|
73
|
-
ctx
|
|
74
|
+
sendWorkbenchResult(ctx, msg, {
|
|
74
75
|
type: 'git_status_result',
|
|
75
76
|
conversationId,
|
|
76
77
|
_requestUserId,
|
|
@@ -88,7 +89,7 @@ export async function handleGitDiff(msg) {
|
|
|
88
89
|
try {
|
|
89
90
|
// 安全检查:验证 filePath 不包含 shell 注入字符
|
|
90
91
|
if (!filePath || /[`$;|&><!\n\r]/.test(filePath)) {
|
|
91
|
-
ctx
|
|
92
|
+
sendWorkbenchResult(ctx, msg, {
|
|
92
93
|
type: 'git_diff_result',
|
|
93
94
|
conversationId,
|
|
94
95
|
_requestUserId,
|
|
@@ -114,7 +115,7 @@ export async function handleGitDiff(msg) {
|
|
|
114
115
|
const fullPath = resolve(gitRoot, filePath);
|
|
115
116
|
const resolved = resolveAndValidatePath(fullPath, gitRoot);
|
|
116
117
|
const content = await readFile(resolved, 'utf-8');
|
|
117
|
-
ctx
|
|
118
|
+
sendWorkbenchResult(ctx, msg, {
|
|
118
119
|
type: 'git_diff_result',
|
|
119
120
|
conversationId,
|
|
120
121
|
_requestUserId,
|
|
@@ -148,7 +149,7 @@ export async function handleGitDiff(msg) {
|
|
|
148
149
|
windowsHide: true
|
|
149
150
|
});
|
|
150
151
|
if (cachedOut.trim()) {
|
|
151
|
-
ctx
|
|
152
|
+
sendWorkbenchResult(ctx, msg, {
|
|
152
153
|
type: 'git_diff_result',
|
|
153
154
|
conversationId,
|
|
154
155
|
_requestUserId,
|
|
@@ -167,7 +168,7 @@ export async function handleGitDiff(msg) {
|
|
|
167
168
|
windowsHide: true
|
|
168
169
|
});
|
|
169
170
|
if (wtOut.trim()) {
|
|
170
|
-
ctx
|
|
171
|
+
sendWorkbenchResult(ctx, msg, {
|
|
171
172
|
type: 'git_diff_result',
|
|
172
173
|
conversationId,
|
|
173
174
|
_requestUserId,
|
|
@@ -179,7 +180,7 @@ export async function handleGitDiff(msg) {
|
|
|
179
180
|
}
|
|
180
181
|
}
|
|
181
182
|
|
|
182
|
-
ctx
|
|
183
|
+
sendWorkbenchResult(ctx, msg, {
|
|
183
184
|
type: 'git_diff_result',
|
|
184
185
|
conversationId,
|
|
185
186
|
_requestUserId,
|
|
@@ -188,7 +189,7 @@ export async function handleGitDiff(msg) {
|
|
|
188
189
|
diff: stdout
|
|
189
190
|
});
|
|
190
191
|
} catch (e) {
|
|
191
|
-
ctx
|
|
192
|
+
sendWorkbenchResult(ctx, msg, {
|
|
192
193
|
type: 'git_diff_result',
|
|
193
194
|
conversationId,
|
|
194
195
|
_requestUserId,
|
|
@@ -210,15 +211,15 @@ export async function handleGitAdd(msg) {
|
|
|
210
211
|
await execAsync('git add -A', { cwd: gitRoot, timeout: 10000, windowsHide: true });
|
|
211
212
|
} else {
|
|
212
213
|
if (!validateGitPath(filePath)) {
|
|
213
|
-
ctx
|
|
214
|
+
sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'add', success: false, error: 'Invalid file path' });
|
|
214
215
|
return;
|
|
215
216
|
}
|
|
216
217
|
await execAsync(`git add -- "${filePath}"`, { cwd: gitRoot, timeout: 10000, windowsHide: true });
|
|
217
218
|
}
|
|
218
219
|
|
|
219
|
-
ctx
|
|
220
|
+
sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'add', success: true, message: addAll ? 'All files staged' : `Staged: ${filePath}` });
|
|
220
221
|
} catch (e) {
|
|
221
|
-
ctx
|
|
222
|
+
sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'add', success: false, error: e.message });
|
|
222
223
|
}
|
|
223
224
|
}
|
|
224
225
|
|
|
@@ -234,15 +235,15 @@ export async function handleGitReset(msg) {
|
|
|
234
235
|
await execAsync('git reset HEAD', { cwd: gitRoot, timeout: 10000, windowsHide: true });
|
|
235
236
|
} else {
|
|
236
237
|
if (!validateGitPath(filePath)) {
|
|
237
|
-
ctx
|
|
238
|
+
sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'reset', success: false, error: 'Invalid file path' });
|
|
238
239
|
return;
|
|
239
240
|
}
|
|
240
241
|
await execAsync(`git reset HEAD -- "${filePath}"`, { cwd: gitRoot, timeout: 10000, windowsHide: true });
|
|
241
242
|
}
|
|
242
243
|
|
|
243
|
-
ctx
|
|
244
|
+
sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'reset', success: true, message: resetAll ? 'All files unstaged' : `Unstaged: ${filePath}` });
|
|
244
245
|
} catch (e) {
|
|
245
|
-
ctx
|
|
246
|
+
sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'reset', success: false, error: e.message });
|
|
246
247
|
}
|
|
247
248
|
}
|
|
248
249
|
|
|
@@ -253,15 +254,15 @@ export async function handleGitRestore(msg) {
|
|
|
253
254
|
|
|
254
255
|
try {
|
|
255
256
|
if (!validateGitPath(filePath)) {
|
|
256
|
-
ctx
|
|
257
|
+
sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'restore', success: false, error: 'Invalid file path' });
|
|
257
258
|
return;
|
|
258
259
|
}
|
|
259
260
|
|
|
260
261
|
const gitRoot = await getGitRoot(workDir);
|
|
261
262
|
await execAsync(`git restore -- "${filePath}"`, { cwd: gitRoot, timeout: 10000, windowsHide: true });
|
|
262
|
-
ctx
|
|
263
|
+
sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'restore', success: true, message: `Restored: ${filePath}` });
|
|
263
264
|
} catch (e) {
|
|
264
|
-
ctx
|
|
265
|
+
sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'restore', success: false, error: e.message });
|
|
265
266
|
}
|
|
266
267
|
}
|
|
267
268
|
|
|
@@ -272,7 +273,7 @@ export async function handleGitCommit(msg) {
|
|
|
272
273
|
|
|
273
274
|
try {
|
|
274
275
|
if (!commitMessage || !commitMessage.trim()) {
|
|
275
|
-
ctx
|
|
276
|
+
sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'commit', success: false, error: 'Commit message is required' });
|
|
276
277
|
return;
|
|
277
278
|
}
|
|
278
279
|
|
|
@@ -286,13 +287,13 @@ export async function handleGitCommit(msg) {
|
|
|
286
287
|
const { stdout } = await execAsync(`git commit -F "${tmpFile}"`, {
|
|
287
288
|
cwd: gitRoot, timeout: 30000, windowsHide: true
|
|
288
289
|
});
|
|
289
|
-
ctx
|
|
290
|
+
sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'commit', success: true, message: stdout.trim() });
|
|
290
291
|
} finally {
|
|
291
292
|
// Clean up temp file
|
|
292
293
|
try { await writeFile(tmpFile, '', 'utf8'); } catch {}
|
|
293
294
|
}
|
|
294
295
|
} catch (e) {
|
|
295
|
-
ctx
|
|
296
|
+
sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'commit', success: false, error: e.stderr?.trim() || e.message });
|
|
296
297
|
}
|
|
297
298
|
}
|
|
298
299
|
|
|
@@ -306,8 +307,8 @@ export async function handleGitPush(msg) {
|
|
|
306
307
|
const { stdout, stderr } = await execAsync('git push', {
|
|
307
308
|
cwd: gitRoot, timeout: 60000, windowsHide: true
|
|
308
309
|
});
|
|
309
|
-
ctx
|
|
310
|
+
sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'push', success: true, message: (stdout + '\n' + stderr).trim() || 'Push complete' });
|
|
310
311
|
} catch (e) {
|
|
311
|
-
ctx
|
|
312
|
+
sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'push', success: false, error: e.stderr?.trim() || e.message });
|
|
312
313
|
}
|
|
313
314
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function workbenchRequestRouting(source) {
|
|
2
|
+
return {
|
|
3
|
+
...(source?._workbenchRequestId ? { _workbenchRequestId: source._workbenchRequestId } : {}),
|
|
4
|
+
...(source?.workbenchRouteKey ? { workbenchRouteKey: source.workbenchRouteKey } : {}),
|
|
5
|
+
...(source?.workbenchWorkspaceGeneration
|
|
6
|
+
? { workbenchWorkspaceGeneration: source.workbenchWorkspaceGeneration }
|
|
7
|
+
: {}),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function sendWorkbenchResult(ctx, request, result) {
|
|
12
|
+
ctx.sendToServer({
|
|
13
|
+
...result,
|
|
14
|
+
...workbenchRequestRouting(request),
|
|
15
|
+
});
|
|
16
|
+
}
|
package/yeaft/cli.js
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
import { createInterface } from 'readline';
|
|
27
27
|
import { randomUUID } from 'node:crypto';
|
|
28
|
+
import { realpathSync } from 'node:fs';
|
|
28
29
|
import { resolve } from 'node:path';
|
|
29
30
|
import { fileURLToPath } from 'node:url';
|
|
30
31
|
import { join } from 'path';
|
|
@@ -35,13 +36,16 @@ import { listModels, resolveModel, parseModelRef, resolveContextWindow, resolveM
|
|
|
35
36
|
import { buildSystemPrompt } from './prompts.js';
|
|
36
37
|
import { searchMessages } from './conversation/search.js';
|
|
37
38
|
import { ConversationStore } from './conversation/persist.js';
|
|
38
|
-
import { snapshotSessions } from './sessions/session-crud.js';
|
|
39
|
+
import { sessionsRoot, snapshotSessions } from './sessions/session-crud.js';
|
|
39
40
|
import { loadSessionConfig, resolveSessionConfig } from './sessions/session-config.js';
|
|
41
|
+
import { createSession } from './sessions/session-store.js';
|
|
42
|
+
import { addOrUpdateManifestSession, withSessionManifestLock } from './sessions/session-manifest.js';
|
|
40
43
|
import { validateSessionId } from './sessions/ids.js';
|
|
41
44
|
import {
|
|
42
45
|
createJsonlWriter,
|
|
43
46
|
JsonlInput,
|
|
44
47
|
normalizeStreamRoutingIntent,
|
|
48
|
+
normalizeStreamSessionBootstrap,
|
|
45
49
|
runStreamTurn,
|
|
46
50
|
runStreamSessionTurn,
|
|
47
51
|
} from './stdio-protocol.js';
|
|
@@ -900,6 +904,7 @@ async function runStreamJson(config, args) {
|
|
|
900
904
|
let taskEventIntakeOpen = true;
|
|
901
905
|
let hadError = false;
|
|
902
906
|
let singleEngineTail = Promise.resolve();
|
|
907
|
+
let streamSessionBootstrapOpen = false;
|
|
903
908
|
|
|
904
909
|
const loadStreamHistory = () => conversationStore.loadRecentBySession(sessionId, 20).map(message => ({
|
|
905
910
|
role: message.role,
|
|
@@ -1033,6 +1038,16 @@ async function runStreamJson(config, args) {
|
|
|
1033
1038
|
|
|
1034
1039
|
configureStreamEngine(engine, null);
|
|
1035
1040
|
sessionRunner = createCliSessionRunner({ loaded, sessionId, workDir, configureEngine: configureStreamEngine });
|
|
1041
|
+
if (sessionRunner) {
|
|
1042
|
+
snapshotSessions(loaded.yeaftDir);
|
|
1043
|
+
const meta = sessionRunner.meta;
|
|
1044
|
+
if (meta) addOrUpdateManifestSession(
|
|
1045
|
+
loaded.yeaftDir,
|
|
1046
|
+
meta,
|
|
1047
|
+
join(sessionsRoot(loaded.yeaftDir), sessionId),
|
|
1048
|
+
);
|
|
1049
|
+
}
|
|
1050
|
+
streamSessionBootstrapOpen = !sessionRunner;
|
|
1036
1051
|
|
|
1037
1052
|
write({
|
|
1038
1053
|
type: 'system',
|
|
@@ -1091,6 +1106,47 @@ async function runStreamJson(config, args) {
|
|
|
1091
1106
|
};
|
|
1092
1107
|
|
|
1093
1108
|
const runPrompt = async (prompt, message = null) => {
|
|
1109
|
+
if (streamSessionBootstrapOpen) {
|
|
1110
|
+
streamSessionBootstrapOpen = false;
|
|
1111
|
+
const bootstrap = normalizeStreamSessionBootstrap(message);
|
|
1112
|
+
if (bootstrap) {
|
|
1113
|
+
let workspaceKey = '';
|
|
1114
|
+
try { workspaceKey = realpathSync(resolve(workDir)); } catch { /* leave empty */ }
|
|
1115
|
+
withSessionManifestLock(loaded.yeaftDir, () => {
|
|
1116
|
+
sessionRunner = createCliSessionRunner({
|
|
1117
|
+
loaded,
|
|
1118
|
+
sessionId,
|
|
1119
|
+
workDir,
|
|
1120
|
+
configureEngine: configureStreamEngine,
|
|
1121
|
+
});
|
|
1122
|
+
if (sessionRunner) return;
|
|
1123
|
+
const handle = createSession(sessionsRoot(loaded.yeaftDir), {
|
|
1124
|
+
id: sessionId,
|
|
1125
|
+
name: sessionId,
|
|
1126
|
+
roster: bootstrap.roster,
|
|
1127
|
+
defaultVpId: bootstrap.defaultVpId,
|
|
1128
|
+
workDir,
|
|
1129
|
+
workspaceKey,
|
|
1130
|
+
});
|
|
1131
|
+
handle.close();
|
|
1132
|
+
});
|
|
1133
|
+
if (!sessionRunner) {
|
|
1134
|
+
sessionRunner = createCliSessionRunner({
|
|
1135
|
+
loaded,
|
|
1136
|
+
sessionId,
|
|
1137
|
+
workDir,
|
|
1138
|
+
configureEngine: configureStreamEngine,
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1141
|
+
if (!sessionRunner) throw new Error(`Failed to initialize formal stream-json Session ${sessionId}`);
|
|
1142
|
+
const meta = sessionRunner.meta;
|
|
1143
|
+
if (meta) addOrUpdateManifestSession(
|
|
1144
|
+
loaded.yeaftDir,
|
|
1145
|
+
meta,
|
|
1146
|
+
join(sessionsRoot(loaded.yeaftDir), sessionId),
|
|
1147
|
+
);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1094
1150
|
const routingIntent = normalizeStreamRoutingIntent(message);
|
|
1095
1151
|
if (routingIntent && !sessionRunner) {
|
|
1096
1152
|
throw new Error('stream-json VP selectors require an existing formal Session with a persisted roster');
|