@yeaft/webchat-agent 0.1.608 → 0.1.611
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.
|
@@ -36,7 +36,7 @@ import { sendToServer, flushMessageBuffer } from './buffer.js';
|
|
|
36
36
|
import { handleRestartAgent, handleUpgradeAgent } from './upgrade.js';
|
|
37
37
|
import { loadMcpServers, updateMcpConfig } from '../mcp.js';
|
|
38
38
|
import { getLlmConfig, updateLlmConfig, getUnifySettings, updateUnifySettings } from '../unify/config-api.js';
|
|
39
|
-
import { handleUnifyChat, handleUnifyGroupChat, handleUnifyModeSwitch, handleUnifyModelSwitch, resetUnifySession, handleUnifyLoadHistory, handleUnifyMergeThread, handleUnifyForkThread, handleUnifyAbortThread, handleUnifyAbortAll, handleUnifyVpSubscribe, handleUnifyVpCreate, handleUnifyVpUpdate, handleUnifyVpDelete, handleUnifyVpRead, handleUnifyTaskMessage, handleUnifyUserMemoryWrite, handleUnifyUserMemoryRemove, handleUnifyMemoryScopeList, handleUnifyMemoryQuery, handleUnifyMemoryTrace, handleUnifyFetchSummaryHistory, handleUnifyTaskCrud, handleUnifyListGroups, handleUnifyCreateGroup, handleUnifyRenameGroup, handleUnifyArchiveGroup, handleUnifyAddMember, handleUnifyRemoveMember, handleUnifySetDefaultVp, handleUnifyDreamTrigger } from '../unify/web-bridge.js';
|
|
39
|
+
import { handleUnifyChat, handleUnifyGroupChat, handleUnifyModeSwitch, handleUnifyModelSwitch, resetUnifySession, handleUnifyLoadHistory, handleUnifyMergeThread, handleUnifyForkThread, handleUnifyAbortThread, handleUnifyAbortAll, handleUnifyVpSubscribe, handleUnifyVpCreate, handleUnifyVpUpdate, handleUnifyVpDelete, handleUnifyVpRead, handleUnifyTaskMessage, handleUnifyUserMemoryWrite, handleUnifyUserMemoryRemove, handleUnifyMemoryScopeList, handleUnifyMemoryQuery, handleUnifyMemoryTrace, handleUnifyFetchSummaryHistory, handleUnifyTaskCrud, handleUnifyListGroups, handleUnifyCreateGroup, handleUnifyRenameGroup, handleUnifyArchiveGroup, handleUnifyDeleteGroup, handleUnifyAddMember, handleUnifyRemoveMember, handleUnifySetDefaultVp, handleUnifyDreamTrigger } from '../unify/web-bridge.js';
|
|
40
40
|
|
|
41
41
|
export async function handleMessage(msg) {
|
|
42
42
|
switch (msg.type) {
|
|
@@ -479,6 +479,9 @@ export async function handleMessage(msg) {
|
|
|
479
479
|
case 'unify_archive_group':
|
|
480
480
|
handleUnifyArchiveGroup(msg);
|
|
481
481
|
break;
|
|
482
|
+
case 'unify_delete_group':
|
|
483
|
+
handleUnifyDeleteGroup(msg);
|
|
484
|
+
break;
|
|
482
485
|
case 'unify_add_member':
|
|
483
486
|
handleUnifyAddMember(msg);
|
|
484
487
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yeaft/webchat-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.611",
|
|
4
4
|
"description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"dev": "nodemon index.js"
|
|
14
14
|
},
|
|
15
15
|
"engines": {
|
|
16
|
-
"node": ">=
|
|
16
|
+
"node": ">=22.5.0"
|
|
17
17
|
},
|
|
18
18
|
"keywords": [
|
|
19
19
|
"claude",
|
|
@@ -54,7 +54,6 @@
|
|
|
54
54
|
"ext": "js"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"better-sqlite3": "^11.0.0",
|
|
58
57
|
"dotenv": "^16.3.1",
|
|
59
58
|
"tweetnacl": "^1.0.3",
|
|
60
59
|
"tweetnacl-util": "^0.15.1",
|
package/unify/debug-trace.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Reference: server/db/connection.js — Database(path), pragma WAL
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import
|
|
10
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
11
11
|
import { randomUUID } from 'crypto';
|
|
12
12
|
import { statSync } from 'fs';
|
|
13
13
|
|
|
@@ -80,7 +80,7 @@ function truncate(str, max) {
|
|
|
80
80
|
* DebugTrace — SQLite-backed debug trace.
|
|
81
81
|
*/
|
|
82
82
|
export class DebugTrace {
|
|
83
|
-
/** @type {
|
|
83
|
+
/** @type {import('node:sqlite').DatabaseSync} */
|
|
84
84
|
#db;
|
|
85
85
|
|
|
86
86
|
/** @type {string} */
|
|
@@ -94,9 +94,9 @@ export class DebugTrace {
|
|
|
94
94
|
*/
|
|
95
95
|
constructor(dbPath) {
|
|
96
96
|
this.#dbPath = dbPath;
|
|
97
|
-
this.#db = new
|
|
98
|
-
this.#db.
|
|
99
|
-
this.#db.
|
|
97
|
+
this.#db = new DatabaseSync(dbPath);
|
|
98
|
+
this.#db.exec('PRAGMA journal_mode = WAL');
|
|
99
|
+
this.#db.exec('PRAGMA foreign_keys = ON');
|
|
100
100
|
this.#db.exec(SCHEMA);
|
|
101
101
|
}
|
|
102
102
|
|
|
@@ -282,9 +282,9 @@ export class DebugTrace {
|
|
|
282
282
|
* @returns {{ turnCount: number, toolCount: number, eventCount: number, dbSizeBytes: number }}
|
|
283
283
|
*/
|
|
284
284
|
stats() {
|
|
285
|
-
const turnCount = this.#db.prepare('SELECT COUNT(*) as c FROM trace_turns').get().c;
|
|
286
|
-
const toolCount = this.#db.prepare('SELECT COUNT(*) as c FROM trace_tools').get().c;
|
|
287
|
-
const eventCount = this.#db.prepare('SELECT COUNT(*) as c FROM trace_events').get().c;
|
|
285
|
+
const turnCount = Number(this.#db.prepare('SELECT COUNT(*) as c FROM trace_turns').get().c);
|
|
286
|
+
const toolCount = Number(this.#db.prepare('SELECT COUNT(*) as c FROM trace_tools').get().c);
|
|
287
|
+
const eventCount = Number(this.#db.prepare('SELECT COUNT(*) as c FROM trace_events').get().c);
|
|
288
288
|
let dbSizeBytes = 0;
|
|
289
289
|
try {
|
|
290
290
|
dbSizeBytes = statSync(this.#dbPath).size;
|
|
@@ -301,17 +301,17 @@ export class DebugTrace {
|
|
|
301
301
|
*/
|
|
302
302
|
cleanup(retentionDays = 30) {
|
|
303
303
|
const cutoff = Date.now() - retentionDays * 24 * 60 * 60 * 1000;
|
|
304
|
-
const deletedTools = this.#db.prepare(`
|
|
304
|
+
const deletedTools = Number(this.#db.prepare(`
|
|
305
305
|
DELETE FROM trace_tools WHERE turn_id IN (
|
|
306
306
|
SELECT id FROM trace_turns WHERE started_at < ?
|
|
307
307
|
)
|
|
308
|
-
`).run(cutoff).changes;
|
|
309
|
-
const deletedTurns = this.#db.prepare(`
|
|
308
|
+
`).run(cutoff).changes);
|
|
309
|
+
const deletedTurns = Number(this.#db.prepare(`
|
|
310
310
|
DELETE FROM trace_turns WHERE started_at < ?
|
|
311
|
-
`).run(cutoff).changes;
|
|
312
|
-
const deletedEvents = this.#db.prepare(`
|
|
311
|
+
`).run(cutoff).changes);
|
|
312
|
+
const deletedEvents = Number(this.#db.prepare(`
|
|
313
313
|
DELETE FROM trace_events WHERE created_at < ?
|
|
314
|
-
`).run(cutoff).changes;
|
|
314
|
+
`).run(cutoff).changes);
|
|
315
315
|
return { deletedTurns, deletedTools, deletedEvents };
|
|
316
316
|
}
|
|
317
317
|
|
|
@@ -333,7 +333,7 @@ export class DebugTrace {
|
|
|
333
333
|
* Get or create a prepared statement.
|
|
334
334
|
* @param {string} key
|
|
335
335
|
* @param {string} sql
|
|
336
|
-
* @returns {
|
|
336
|
+
* @returns {import('node:sqlite').StatementSync}
|
|
337
337
|
*/
|
|
338
338
|
#prepare(key, sql) {
|
|
339
339
|
if (!this.#stmts[key]) {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
* 'reserved'/'invalid_vp_id'/... — bubbled from ids.js validators
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
|
-
import { existsSync, renameSync } from 'fs';
|
|
35
|
+
import { existsSync, renameSync, rmSync, readdirSync, statSync } from 'fs';
|
|
36
36
|
import { randomBytes } from 'crypto';
|
|
37
37
|
import { join } from 'path';
|
|
38
38
|
import {
|
|
@@ -184,6 +184,74 @@ export function archiveGroup(yeaftDir, groupId) {
|
|
|
184
184
|
return { groupId, archivedAs: dstDir };
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
+
/**
|
|
188
|
+
* (A.3.b) Delete — physically remove the group directory and all its
|
|
189
|
+
* contents (group.json, messages/, tasks/, vps/). Irreversible.
|
|
190
|
+
*
|
|
191
|
+
* Bug 8 fix: replaces the soft-archive flow that left `.archived-*` dirs
|
|
192
|
+
* lying around in `~/.yeaft/groups/`. Per user request, "delete" means
|
|
193
|
+
* physical deletion, not rename.
|
|
194
|
+
*
|
|
195
|
+
* Also sweeps any sibling `.archived-*-<groupId>` dirs that were left
|
|
196
|
+
* behind by the previous soft-archive implementation, so a single
|
|
197
|
+
* delete cleans up legacy state too.
|
|
198
|
+
*/
|
|
199
|
+
export function deleteGroup(yeaftDir, groupId) {
|
|
200
|
+
const root = groupsRoot(yeaftDir);
|
|
201
|
+
const srcDir = join(root, groupId);
|
|
202
|
+
const liveExists = existsSync(srcDir) && !!loadGroupMeta(srcDir);
|
|
203
|
+
|
|
204
|
+
// Collect any leftover soft-archive directories matching this groupId.
|
|
205
|
+
const legacyDirs = [];
|
|
206
|
+
if (existsSync(root)) {
|
|
207
|
+
for (const name of readdirSync(root)) {
|
|
208
|
+
if (!name.startsWith('.archived-')) continue;
|
|
209
|
+
// Soft-archive format: .archived-<ts>-<suffix>-<groupId>
|
|
210
|
+
if (!name.endsWith(`-${groupId}`)) continue;
|
|
211
|
+
const p = join(root, name);
|
|
212
|
+
try {
|
|
213
|
+
if (statSync(p).isDirectory()) legacyDirs.push(p);
|
|
214
|
+
} catch { /* skip */ }
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (!liveExists && legacyDirs.length === 0) {
|
|
219
|
+
throw new GroupCrudError('not_found', groupId);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (liveExists) {
|
|
223
|
+
rmSync(srcDir, { recursive: true, force: true });
|
|
224
|
+
}
|
|
225
|
+
for (const dir of legacyDirs) {
|
|
226
|
+
rmSync(dir, { recursive: true, force: true });
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return { groupId, deleted: true, legacyCleanedUp: legacyDirs.length };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Sweep any leftover `.archived-*` directories under groups/ that are
|
|
234
|
+
* orphans of the old soft-archive flow. Used at boot so users don't see
|
|
235
|
+
* ghost groups in subsequent loads. Returns the list of removed paths.
|
|
236
|
+
*/
|
|
237
|
+
export function purgeArchivedGroups(yeaftDir) {
|
|
238
|
+
const root = groupsRoot(yeaftDir);
|
|
239
|
+
if (!existsSync(root)) return [];
|
|
240
|
+
const removed = [];
|
|
241
|
+
for (const name of readdirSync(root)) {
|
|
242
|
+
if (!name.startsWith('.archived-')) continue;
|
|
243
|
+
const p = join(root, name);
|
|
244
|
+
try {
|
|
245
|
+
if (!statSync(p).isDirectory()) continue;
|
|
246
|
+
} catch { continue; }
|
|
247
|
+
try {
|
|
248
|
+
rmSync(p, { recursive: true, force: true });
|
|
249
|
+
removed.push(p);
|
|
250
|
+
} catch { /* skip */ }
|
|
251
|
+
}
|
|
252
|
+
return removed;
|
|
253
|
+
}
|
|
254
|
+
|
|
187
255
|
/**
|
|
188
256
|
* (A.4) Add a VP to the group roster. Idempotent — no-op if already present.
|
|
189
257
|
* Returns the new meta.
|
|
@@ -151,6 +151,8 @@ export function listGroups(groupsRoot) {
|
|
|
151
151
|
if (!existsSync(groupsRoot)) return [];
|
|
152
152
|
const out = [];
|
|
153
153
|
for (const name of readdirSync(groupsRoot)) {
|
|
154
|
+
// Skip dotfiles and legacy soft-archive dirs (`.archived-*`).
|
|
155
|
+
if (name.startsWith('.')) continue;
|
|
154
156
|
const p = join(groupsRoot, name);
|
|
155
157
|
try {
|
|
156
158
|
if (!statSync(p).isDirectory()) continue;
|
package/unify/groups/index.js
CHANGED
package/unify/web-bridge.js
CHANGED
|
@@ -40,6 +40,8 @@ import {
|
|
|
40
40
|
createGroupFromSpec,
|
|
41
41
|
renameGroup,
|
|
42
42
|
archiveGroup,
|
|
43
|
+
deleteGroup,
|
|
44
|
+
purgeArchivedGroups,
|
|
43
45
|
addMember,
|
|
44
46
|
removeMember,
|
|
45
47
|
setGroupDefaultVp,
|
|
@@ -455,6 +457,23 @@ export function handleUnifyArchiveGroup(msg) {
|
|
|
455
457
|
}
|
|
456
458
|
}
|
|
457
459
|
|
|
460
|
+
/**
|
|
461
|
+
* Bug 8: physical delete — removes the group dir and any legacy
|
|
462
|
+
* `.archived-*-<groupId>` siblings. Replies with op:'delete'.
|
|
463
|
+
*/
|
|
464
|
+
export function handleUnifyDeleteGroup(msg) {
|
|
465
|
+
const requestId = msg && msg.requestId;
|
|
466
|
+
const groupId = msg && msg.groupId;
|
|
467
|
+
try {
|
|
468
|
+
const yeaftDir = ctx.CONFIG?.yeaftDir;
|
|
469
|
+
const result = deleteGroup(yeaftDir, groupId);
|
|
470
|
+
sendGroupCrudResult({ op: 'delete', requestId, ok: true, groupId: result.groupId });
|
|
471
|
+
sendGroupSnapshotBroadcast();
|
|
472
|
+
} catch (err) {
|
|
473
|
+
sendGroupCrudResult({ op: 'delete', requestId, ok: false, error: groupErrorPayload(err) });
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
458
477
|
export function handleUnifyAddMember(msg) {
|
|
459
478
|
const requestId = msg && msg.requestId;
|
|
460
479
|
const groupId = msg && msg.groupId;
|
|
@@ -1370,6 +1389,21 @@ export async function handleUnifyChat(msg) {
|
|
|
1370
1389
|
runAutoArchiveSweep(session);
|
|
1371
1390
|
scheduleAutoArchive(session);
|
|
1372
1391
|
|
|
1392
|
+
// Bug 8: clean up any legacy `.archived-*` group directories left
|
|
1393
|
+
// behind by the previous soft-archive flow. This is a one-shot
|
|
1394
|
+
// boot-time sweep — physical deletes after this point are immediate.
|
|
1395
|
+
try {
|
|
1396
|
+
const yeaftDir = ctx.CONFIG?.yeaftDir;
|
|
1397
|
+
if (yeaftDir) {
|
|
1398
|
+
const removed = purgeArchivedGroups(yeaftDir);
|
|
1399
|
+
if (removed && removed.length > 0) {
|
|
1400
|
+
console.log(`[Unify] purged ${removed.length} legacy .archived group dir(s)`);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
} catch (err) {
|
|
1404
|
+
console.warn('[Unify] purgeArchivedGroups failed:', err?.message || err);
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1373
1407
|
// Create a stable conversationId for the Unify session
|
|
1374
1408
|
unifyConversationId = `unify-${Date.now()}`;
|
|
1375
1409
|
|