@memtensor/memos-local-openclaw-plugin 1.0.5 → 1.0.6-beta.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/dist/capture/index.d.ts.map +1 -1
- package/dist/capture/index.js +24 -0
- package/dist/capture/index.js.map +1 -1
- package/dist/client/connector.d.ts.map +1 -1
- package/dist/client/connector.js +33 -5
- package/dist/client/connector.js.map +1 -1
- package/dist/client/hub.d.ts.map +1 -1
- package/dist/client/hub.js +4 -0
- package/dist/client/hub.js.map +1 -1
- package/dist/hub/server.d.ts +2 -0
- package/dist/hub/server.d.ts.map +1 -1
- package/dist/hub/server.js +116 -54
- package/dist/hub/server.js.map +1 -1
- package/dist/ingest/providers/index.d.ts +4 -0
- package/dist/ingest/providers/index.d.ts.map +1 -1
- package/dist/ingest/providers/index.js +32 -86
- package/dist/ingest/providers/index.js.map +1 -1
- package/dist/ingest/providers/openai.d.ts.map +1 -1
- package/dist/ingest/providers/openai.js +29 -13
- package/dist/ingest/providers/openai.js.map +1 -1
- package/dist/recall/engine.d.ts.map +1 -1
- package/dist/recall/engine.js +33 -32
- package/dist/recall/engine.js.map +1 -1
- package/dist/storage/sqlite.d.ts +43 -7
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +179 -58
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/tools/memory-get.d.ts.map +1 -1
- package/dist/tools/memory-get.js +4 -1
- package/dist/tools/memory-get.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/viewer/html.d.ts.map +1 -1
- package/dist/viewer/html.js +71 -21
- package/dist/viewer/html.js.map +1 -1
- package/dist/viewer/server.d.ts +24 -0
- package/dist/viewer/server.d.ts.map +1 -1
- package/dist/viewer/server.js +398 -144
- package/dist/viewer/server.js.map +1 -1
- package/index.ts +86 -34
- package/package.json +1 -1
- package/scripts/postinstall.cjs +21 -5
- package/src/capture/index.ts +36 -0
- package/src/client/connector.ts +32 -5
- package/src/client/hub.ts +4 -0
- package/src/hub/server.ts +110 -50
- package/src/ingest/providers/index.ts +37 -92
- package/src/ingest/providers/openai.ts +31 -13
- package/src/recall/engine.ts +32 -30
- package/src/storage/sqlite.ts +196 -63
- package/src/tools/memory-get.ts +4 -1
- package/src/types.ts +2 -0
- package/src/viewer/html.ts +71 -21
- package/src/viewer/server.ts +387 -139
- package/prebuilds/darwin-arm64/better_sqlite3.node +0 -0
- package/prebuilds/darwin-x64/better_sqlite3.node +0 -0
- package/prebuilds/linux-x64/better_sqlite3.node +0 -0
- package/prebuilds/win32-x64/better_sqlite3.node +0 -0
- package/telemetry.credentials.json +0 -5
package/dist/viewer/server.js
CHANGED
|
@@ -37,6 +37,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.ViewerServer = void 0;
|
|
40
|
+
exports.computeMigrationSuccess = computeMigrationSuccess;
|
|
41
|
+
exports.createInitialMigrationState = createInitialMigrationState;
|
|
42
|
+
exports.applyMigrationItemToState = applyMigrationItemToState;
|
|
40
43
|
const node_http_1 = __importDefault(require("node:http"));
|
|
41
44
|
const node_os_1 = __importDefault(require("node:os"));
|
|
42
45
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
@@ -57,10 +60,53 @@ const hub_1 = require("../client/hub");
|
|
|
57
60
|
const skill_sync_1 = require("../client/skill-sync");
|
|
58
61
|
const html_1 = require("./html");
|
|
59
62
|
const uuid_1 = require("uuid");
|
|
60
|
-
function
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
function createInitialStepFailures() {
|
|
64
|
+
return { summarization: 0, dedup: 0, embedding: 0 };
|
|
65
|
+
}
|
|
66
|
+
function computeMigrationSuccess(state) {
|
|
67
|
+
const sf = state.stepFailures;
|
|
68
|
+
return state.errors === 0 && sf.summarization === 0 && sf.dedup === 0 && sf.embedding === 0;
|
|
69
|
+
}
|
|
70
|
+
function createInitialMigrationState() {
|
|
71
|
+
const stepFailures = createInitialStepFailures();
|
|
72
|
+
return {
|
|
73
|
+
phase: "",
|
|
74
|
+
stored: 0,
|
|
75
|
+
skipped: 0,
|
|
76
|
+
merged: 0,
|
|
77
|
+
errors: 0,
|
|
78
|
+
processed: 0,
|
|
79
|
+
total: 0,
|
|
80
|
+
lastItem: null,
|
|
81
|
+
done: false,
|
|
82
|
+
stopped: false,
|
|
83
|
+
stepFailures,
|
|
84
|
+
success: computeMigrationSuccess({ errors: 0, stepFailures }),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function applyMigrationItemToState(state, d) {
|
|
88
|
+
if (d.status === "stored")
|
|
89
|
+
state.stored++;
|
|
90
|
+
else if (d.status === "skipped" || d.status === "duplicate")
|
|
91
|
+
state.skipped++;
|
|
92
|
+
else if (d.status === "merged")
|
|
93
|
+
state.merged++;
|
|
94
|
+
else if (d.status === "error")
|
|
95
|
+
state.errors++;
|
|
96
|
+
if (Array.isArray(d.stepFailures)) {
|
|
97
|
+
for (const step of d.stepFailures) {
|
|
98
|
+
if (step === "summarization")
|
|
99
|
+
state.stepFailures.summarization++;
|
|
100
|
+
else if (step === "dedup")
|
|
101
|
+
state.stepFailures.dedup++;
|
|
102
|
+
else if (step === "embedding")
|
|
103
|
+
state.stepFailures.embedding++;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
state.processed = d.index ?? state.processed + 1;
|
|
107
|
+
state.total = d.total ?? state.total;
|
|
108
|
+
state.lastItem = d;
|
|
109
|
+
state.success = computeMigrationSuccess(state);
|
|
64
110
|
}
|
|
65
111
|
class ViewerServer {
|
|
66
112
|
server = null;
|
|
@@ -87,7 +133,7 @@ class ViewerServer {
|
|
|
87
133
|
resetToken;
|
|
88
134
|
migrationRunning = false;
|
|
89
135
|
migrationAbort = false;
|
|
90
|
-
migrationState =
|
|
136
|
+
migrationState = createInitialMigrationState();
|
|
91
137
|
migrationSSEClients = [];
|
|
92
138
|
ppRunning = false;
|
|
93
139
|
ppAbort = false;
|
|
@@ -591,14 +637,15 @@ class ViewerServer {
|
|
|
591
637
|
if (chunkIds.length > 0) {
|
|
592
638
|
try {
|
|
593
639
|
const placeholders = chunkIds.map(() => "?").join(",");
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
640
|
+
if (this.sharingRole === "hub") {
|
|
641
|
+
const sharedRows = db.prepare(`SELECT source_chunk_id, visibility, group_id FROM hub_memories WHERE source_chunk_id IN (${placeholders})`).all(...chunkIds);
|
|
642
|
+
for (const r of sharedRows)
|
|
643
|
+
sharingMap.set(r.source_chunk_id, r);
|
|
644
|
+
}
|
|
645
|
+
else {
|
|
646
|
+
const teamMetaRows = db.prepare(`SELECT chunk_id, visibility, group_id FROM team_shared_chunks WHERE chunk_id IN (${placeholders})`).all(...chunkIds);
|
|
647
|
+
for (const r of teamMetaRows)
|
|
600
648
|
sharingMap.set(r.chunk_id, { visibility: r.visibility, group_id: r.group_id });
|
|
601
|
-
}
|
|
602
649
|
}
|
|
603
650
|
const localRows = db.prepare(`SELECT chunk_id, original_owner, shared_at FROM local_shared_memories WHERE chunk_id IN (${placeholders})`).all(...chunkIds);
|
|
604
651
|
for (const r of localRows)
|
|
@@ -662,7 +709,7 @@ class ViewerServer {
|
|
|
662
709
|
const db = this.store.db;
|
|
663
710
|
const items = tasks.map((t) => {
|
|
664
711
|
const meta = db.prepare("SELECT skill_status, owner FROM tasks WHERE id = ?").get(t.id);
|
|
665
|
-
const
|
|
712
|
+
const hubTask = this.getHubTaskForLocal(t.id);
|
|
666
713
|
return {
|
|
667
714
|
id: t.id,
|
|
668
715
|
sessionKey: t.sessionKey,
|
|
@@ -674,7 +721,7 @@ class ViewerServer {
|
|
|
674
721
|
chunkCount: this.store.countChunksByTask(t.id),
|
|
675
722
|
skillStatus: meta?.skill_status ?? null,
|
|
676
723
|
owner: meta?.owner ?? "agent:main",
|
|
677
|
-
sharingVisibility:
|
|
724
|
+
sharingVisibility: hubTask?.visibility ?? null,
|
|
678
725
|
};
|
|
679
726
|
});
|
|
680
727
|
this.jsonResponse(res, { tasks: items, total, limit, offset });
|
|
@@ -703,7 +750,7 @@ class ViewerServer {
|
|
|
703
750
|
}));
|
|
704
751
|
const db = this.store.db;
|
|
705
752
|
const meta = db.prepare("SELECT skill_status, skill_reason FROM tasks WHERE id = ?").get(taskId);
|
|
706
|
-
const
|
|
753
|
+
const hubTask = this.getHubTaskForLocal(taskId);
|
|
707
754
|
this.jsonResponse(res, {
|
|
708
755
|
id: task.id,
|
|
709
756
|
sessionKey: task.sessionKey,
|
|
@@ -717,9 +764,9 @@ class ViewerServer {
|
|
|
717
764
|
skillStatus: meta?.skill_status ?? null,
|
|
718
765
|
skillReason: meta?.skill_reason ?? null,
|
|
719
766
|
skillLinks,
|
|
720
|
-
sharingVisibility:
|
|
721
|
-
sharingGroupId:
|
|
722
|
-
hubTaskId:
|
|
767
|
+
sharingVisibility: hubTask?.visibility ?? null,
|
|
768
|
+
sharingGroupId: hubTask?.group_id ?? null,
|
|
769
|
+
hubTaskId: hubTask ? true : false,
|
|
723
770
|
});
|
|
724
771
|
}
|
|
725
772
|
serveStats(res, url) {
|
|
@@ -924,10 +971,9 @@ class ViewerServer {
|
|
|
924
971
|
if (visibility) {
|
|
925
972
|
skills = skills.filter(s => s.visibility === visibility);
|
|
926
973
|
}
|
|
927
|
-
const db = this.store.db;
|
|
928
974
|
const enriched = skills.map(s => {
|
|
929
|
-
const
|
|
930
|
-
return { ...s, sharingVisibility:
|
|
975
|
+
const hubSkill = this.getHubSkillForLocal(s.id);
|
|
976
|
+
return { ...s, sharingVisibility: hubSkill?.visibility ?? null };
|
|
931
977
|
});
|
|
932
978
|
this.jsonResponse(res, { skills: enriched });
|
|
933
979
|
}
|
|
@@ -942,10 +988,9 @@ class ViewerServer {
|
|
|
942
988
|
const versions = this.store.getSkillVersions(skillId);
|
|
943
989
|
const relatedTasks = this.store.getTasksBySkill(skillId);
|
|
944
990
|
const files = node_fs_1.default.existsSync(skill.dirPath) ? this.walkDir(skill.dirPath, skill.dirPath) : [];
|
|
945
|
-
const
|
|
946
|
-
const sharedSkill = db.prepare("SELECT visibility, group_id FROM hub_skills WHERE source_skill_id = ? ORDER BY updated_at DESC LIMIT 1").get(skillId);
|
|
991
|
+
const hubSkill = this.getHubSkillForLocal(skillId);
|
|
947
992
|
this.jsonResponse(res, {
|
|
948
|
-
skill: { ...skill, sharingVisibility:
|
|
993
|
+
skill: { ...skill, sharingVisibility: hubSkill?.visibility ?? null, sharingGroupId: hubSkill?.group_id ?? null },
|
|
949
994
|
versions: versions.map(v => ({
|
|
950
995
|
id: v.id,
|
|
951
996
|
version: v.version,
|
|
@@ -1081,7 +1126,7 @@ class ViewerServer {
|
|
|
1081
1126
|
method: "POST",
|
|
1082
1127
|
body: JSON.stringify({ visibility: "public", groupId: null, metadata: bundle.metadata, bundle: bundle.bundle }),
|
|
1083
1128
|
});
|
|
1084
|
-
if (hubClient.userId) {
|
|
1129
|
+
if (this.sharingRole === "hub" && hubClient.userId) {
|
|
1085
1130
|
const existing = this.store.getHubSkillBySource(hubClient.userId, skillId);
|
|
1086
1131
|
this.store.upsertHubSkill({
|
|
1087
1132
|
id: response?.skillId ?? existing?.id ?? node_crypto_1.default.randomUUID(),
|
|
@@ -1092,6 +1137,15 @@ class ViewerServer {
|
|
|
1092
1137
|
createdAt: existing?.createdAt ?? Date.now(), updatedAt: Date.now(),
|
|
1093
1138
|
});
|
|
1094
1139
|
}
|
|
1140
|
+
else {
|
|
1141
|
+
const conn = this.store.getClientHubConnection();
|
|
1142
|
+
this.store.upsertTeamSharedSkill(skillId, {
|
|
1143
|
+
hubSkillId: String(response?.skillId ?? ""),
|
|
1144
|
+
visibility: "public",
|
|
1145
|
+
groupId: null,
|
|
1146
|
+
hubInstanceId: conn?.hubInstanceId ?? "",
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1095
1149
|
hubSynced = true;
|
|
1096
1150
|
this.log.info(`Skill "${skill.name}" published to Hub`);
|
|
1097
1151
|
}
|
|
@@ -1100,8 +1154,10 @@ class ViewerServer {
|
|
|
1100
1154
|
method: "POST",
|
|
1101
1155
|
body: JSON.stringify({ sourceSkillId: skillId }),
|
|
1102
1156
|
});
|
|
1103
|
-
if (hubClient.userId)
|
|
1157
|
+
if (this.sharingRole === "hub" && hubClient.userId)
|
|
1104
1158
|
this.store.deleteHubSkillBySource(hubClient.userId, skillId);
|
|
1159
|
+
else
|
|
1160
|
+
this.store.deleteTeamSharedSkill(skillId);
|
|
1105
1161
|
hubSynced = true;
|
|
1106
1162
|
this.log.info(`Skill "${skill.name}" unpublished from Hub`);
|
|
1107
1163
|
}
|
|
@@ -1399,7 +1455,8 @@ class ViewerServer {
|
|
|
1399
1455
|
});
|
|
1400
1456
|
}
|
|
1401
1457
|
else if (hubClient.userId) {
|
|
1402
|
-
this.store.
|
|
1458
|
+
const conn = this.store.getClientHubConnection();
|
|
1459
|
+
this.store.upsertTeamSharedChunk(chunkId, { hubMemoryId: memoryId, visibility: "public", groupId: null, hubInstanceId: conn?.hubInstanceId ?? "" });
|
|
1403
1460
|
}
|
|
1404
1461
|
hubSynced = true;
|
|
1405
1462
|
}
|
|
@@ -1415,7 +1472,7 @@ class ViewerServer {
|
|
|
1415
1472
|
await (0, hub_1.hubRequestJson)(hubClient.hubUrl, hubClient.userToken, "/api/v1/hub/memories/unshare", {
|
|
1416
1473
|
method: "POST", body: JSON.stringify({ sourceChunkId: chunkId }),
|
|
1417
1474
|
});
|
|
1418
|
-
if (hubClient.userId)
|
|
1475
|
+
if (this.sharingRole === "hub" && hubClient.userId)
|
|
1419
1476
|
this.store.deleteHubMemoryBySource(hubClient.userId, chunkId);
|
|
1420
1477
|
this.store.deleteTeamSharedChunk(chunkId);
|
|
1421
1478
|
hubSynced = true;
|
|
@@ -1434,7 +1491,7 @@ class ViewerServer {
|
|
|
1434
1491
|
await (0, hub_1.hubRequestJson)(hubClient.hubUrl, hubClient.userToken, "/api/v1/hub/memories/unshare", {
|
|
1435
1492
|
method: "POST", body: JSON.stringify({ sourceChunkId: chunkId }),
|
|
1436
1493
|
});
|
|
1437
|
-
if (hubClient.userId)
|
|
1494
|
+
if (this.sharingRole === "hub" && hubClient.userId)
|
|
1438
1495
|
this.store.deleteHubMemoryBySource(hubClient.userId, chunkId);
|
|
1439
1496
|
this.store.deleteTeamSharedChunk(chunkId);
|
|
1440
1497
|
hubSynced = true;
|
|
@@ -1488,21 +1545,24 @@ class ViewerServer {
|
|
|
1488
1545
|
chunks: chunks.map((c) => ({ id: c.id, hubTaskId: refreshedTask.id, sourceTaskId: refreshedTask.id, sourceChunkId: c.id, role: c.role, content: c.content, summary: c.summary, kind: c.kind, groupId: null, visibility: "public", createdAt: c.createdAt ?? Date.now() })),
|
|
1489
1546
|
}),
|
|
1490
1547
|
});
|
|
1491
|
-
|
|
1548
|
+
const hubTaskId = String(response?.taskId ?? "");
|
|
1549
|
+
if (this.sharingRole === "hub" && hubClient.userId) {
|
|
1492
1550
|
const existing = this.store.getHubTaskBySource(hubClient.userId, taskId);
|
|
1493
1551
|
this.store.upsertHubTask({
|
|
1494
|
-
id:
|
|
1552
|
+
id: hubTaskId || existing?.id || node_crypto_1.default.randomUUID(),
|
|
1495
1553
|
sourceTaskId: taskId, sourceUserId: hubClient.userId, title: refreshedTask.title ?? "",
|
|
1496
1554
|
summary: refreshedTask.summary ?? "", groupId: null, visibility: "public",
|
|
1497
1555
|
createdAt: existing?.createdAt ?? Date.now(), updatedAt: Date.now(),
|
|
1498
1556
|
});
|
|
1499
1557
|
}
|
|
1558
|
+
const conn = this.store.getClientHubConnection();
|
|
1559
|
+
this.store.markTaskShared(taskId, hubTaskId, chunks.length, "public", null, conn?.hubInstanceId ?? "");
|
|
1500
1560
|
hubSynced = true;
|
|
1501
1561
|
}
|
|
1502
1562
|
if (!isLocalShared) {
|
|
1503
1563
|
const originalOwner = task.owner;
|
|
1504
1564
|
const db = this.store.db;
|
|
1505
|
-
db.prepare("INSERT INTO local_shared_tasks (task_id, hub_task_id, original_owner, shared_at) VALUES (?, ?, ?, ?) ON CONFLICT(task_id) DO UPDATE SET original_owner = excluded.original_owner, shared_at = excluded.shared_at").run(taskId, "", originalOwner, Date.now());
|
|
1565
|
+
db.prepare("INSERT INTO local_shared_tasks (task_id, hub_task_id, original_owner, hub_instance_id, shared_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(task_id) DO UPDATE SET original_owner = excluded.original_owner, hub_instance_id = excluded.hub_instance_id, shared_at = excluded.shared_at").run(taskId, "", originalOwner, "", Date.now());
|
|
1506
1566
|
db.prepare("UPDATE tasks SET owner = 'public' WHERE id = ?").run(taskId);
|
|
1507
1567
|
}
|
|
1508
1568
|
}
|
|
@@ -1520,8 +1580,10 @@ class ViewerServer {
|
|
|
1520
1580
|
await (0, hub_1.hubRequestJson)(hubClient.hubUrl, hubClient.userToken, "/api/v1/hub/tasks/unshare", {
|
|
1521
1581
|
method: "POST", body: JSON.stringify({ sourceTaskId: taskId }),
|
|
1522
1582
|
});
|
|
1523
|
-
if (hubClient.userId)
|
|
1583
|
+
if (this.sharingRole === "hub" && hubClient.userId)
|
|
1524
1584
|
this.store.deleteHubTaskBySource(hubClient.userId, taskId);
|
|
1585
|
+
else
|
|
1586
|
+
this.store.downgradeTeamSharedTaskToLocal(taskId);
|
|
1525
1587
|
hubSynced = true;
|
|
1526
1588
|
}
|
|
1527
1589
|
catch (err) {
|
|
@@ -1535,8 +1597,10 @@ class ViewerServer {
|
|
|
1535
1597
|
await (0, hub_1.hubRequestJson)(hubClient.hubUrl, hubClient.userToken, "/api/v1/hub/tasks/unshare", {
|
|
1536
1598
|
method: "POST", body: JSON.stringify({ sourceTaskId: taskId }),
|
|
1537
1599
|
});
|
|
1538
|
-
if (hubClient.userId)
|
|
1600
|
+
if (this.sharingRole === "hub" && hubClient.userId)
|
|
1539
1601
|
this.store.deleteHubTaskBySource(hubClient.userId, taskId);
|
|
1602
|
+
else if (!isLocalShared)
|
|
1603
|
+
this.store.unmarkTaskShared(taskId);
|
|
1540
1604
|
hubSynced = true;
|
|
1541
1605
|
}
|
|
1542
1606
|
catch (err) {
|
|
@@ -1591,10 +1655,11 @@ class ViewerServer {
|
|
|
1591
1655
|
method: "POST",
|
|
1592
1656
|
body: JSON.stringify({ visibility: "public", groupId: null, metadata: bundle.metadata, bundle: bundle.bundle }),
|
|
1593
1657
|
});
|
|
1594
|
-
|
|
1658
|
+
const hubSkillId = String(response?.skillId ?? "");
|
|
1659
|
+
if (this.sharingRole === "hub" && hubClient.userId) {
|
|
1595
1660
|
const existing = this.store.getHubSkillBySource(hubClient.userId, skillId);
|
|
1596
1661
|
this.store.upsertHubSkill({
|
|
1597
|
-
id:
|
|
1662
|
+
id: hubSkillId || existing?.id || node_crypto_1.default.randomUUID(),
|
|
1598
1663
|
sourceSkillId: skillId, sourceUserId: hubClient.userId,
|
|
1599
1664
|
name: skill.name, description: skill.description, version: skill.version,
|
|
1600
1665
|
groupId: null, visibility: "public",
|
|
@@ -1602,6 +1667,10 @@ class ViewerServer {
|
|
|
1602
1667
|
createdAt: existing?.createdAt ?? Date.now(), updatedAt: Date.now(),
|
|
1603
1668
|
});
|
|
1604
1669
|
}
|
|
1670
|
+
else {
|
|
1671
|
+
const conn = this.store.getClientHubConnection();
|
|
1672
|
+
this.store.upsertTeamSharedSkill(skillId, { hubSkillId, visibility: "public", groupId: null, hubInstanceId: conn?.hubInstanceId ?? "" });
|
|
1673
|
+
}
|
|
1605
1674
|
hubSynced = true;
|
|
1606
1675
|
}
|
|
1607
1676
|
if (!isLocalShared)
|
|
@@ -1617,8 +1686,10 @@ class ViewerServer {
|
|
|
1617
1686
|
await (0, hub_1.hubRequestJson)(hubClient.hubUrl, hubClient.userToken, "/api/v1/hub/skills/unpublish", {
|
|
1618
1687
|
method: "POST", body: JSON.stringify({ sourceSkillId: skillId }),
|
|
1619
1688
|
});
|
|
1620
|
-
if (hubClient.userId)
|
|
1689
|
+
if (this.sharingRole === "hub" && hubClient.userId)
|
|
1621
1690
|
this.store.deleteHubSkillBySource(hubClient.userId, skillId);
|
|
1691
|
+
else
|
|
1692
|
+
this.store.deleteTeamSharedSkill(skillId);
|
|
1622
1693
|
hubSynced = true;
|
|
1623
1694
|
}
|
|
1624
1695
|
catch (err) {
|
|
@@ -1632,8 +1703,10 @@ class ViewerServer {
|
|
|
1632
1703
|
await (0, hub_1.hubRequestJson)(hubClient.hubUrl, hubClient.userToken, "/api/v1/hub/skills/unpublish", {
|
|
1633
1704
|
method: "POST", body: JSON.stringify({ sourceSkillId: skillId }),
|
|
1634
1705
|
});
|
|
1635
|
-
if (hubClient.userId)
|
|
1706
|
+
if (this.sharingRole === "hub" && hubClient.userId)
|
|
1636
1707
|
this.store.deleteHubSkillBySource(hubClient.userId, skillId);
|
|
1708
|
+
else
|
|
1709
|
+
this.store.deleteTeamSharedSkill(skillId);
|
|
1637
1710
|
hubSynced = true;
|
|
1638
1711
|
}
|
|
1639
1712
|
catch (err) {
|
|
@@ -1650,28 +1723,52 @@ class ViewerServer {
|
|
|
1650
1723
|
}
|
|
1651
1724
|
});
|
|
1652
1725
|
}
|
|
1726
|
+
get sharingRole() {
|
|
1727
|
+
return this.ctx?.config?.sharing?.role;
|
|
1728
|
+
}
|
|
1729
|
+
isCurrentClientHubInstance(hubInstanceId) {
|
|
1730
|
+
if (this.sharingRole !== "client")
|
|
1731
|
+
return true;
|
|
1732
|
+
const scopedHubInstanceId = String(hubInstanceId ?? "");
|
|
1733
|
+
if (!scopedHubInstanceId)
|
|
1734
|
+
return true;
|
|
1735
|
+
const currentHubInstanceId = this.store.getClientHubConnection()?.hubInstanceId ?? "";
|
|
1736
|
+
if (!currentHubInstanceId)
|
|
1737
|
+
return true;
|
|
1738
|
+
return scopedHubInstanceId === currentHubInstanceId;
|
|
1739
|
+
}
|
|
1653
1740
|
getHubMemoryForChunk(chunkId) {
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1741
|
+
if (this.sharingRole === "hub") {
|
|
1742
|
+
const db = this.store.db;
|
|
1743
|
+
return db.prepare("SELECT * FROM hub_memories WHERE source_chunk_id = ? LIMIT 1").get(chunkId);
|
|
1744
|
+
}
|
|
1658
1745
|
const ts = this.store.getTeamSharedChunk(chunkId);
|
|
1659
|
-
if (ts) {
|
|
1660
|
-
return {
|
|
1661
|
-
source_chunk_id: chunkId,
|
|
1662
|
-
visibility: ts.visibility,
|
|
1663
|
-
group_id: ts.groupId,
|
|
1664
|
-
};
|
|
1746
|
+
if (ts && this.isCurrentClientHubInstance(ts.hubInstanceId)) {
|
|
1747
|
+
return { source_chunk_id: chunkId, visibility: ts.visibility, group_id: ts.groupId };
|
|
1665
1748
|
}
|
|
1666
1749
|
return undefined;
|
|
1667
1750
|
}
|
|
1668
1751
|
getHubTaskForLocal(taskId) {
|
|
1669
|
-
|
|
1670
|
-
|
|
1752
|
+
if (this.sharingRole === "hub") {
|
|
1753
|
+
const db = this.store.db;
|
|
1754
|
+
return db.prepare("SELECT * FROM hub_tasks WHERE source_task_id = ? LIMIT 1").get(taskId);
|
|
1755
|
+
}
|
|
1756
|
+
const shared = this.store.getLocalSharedTask(taskId);
|
|
1757
|
+
if (shared && shared.hubTaskId && this.isCurrentClientHubInstance(shared.hubInstanceId)) {
|
|
1758
|
+
return { source_task_id: taskId, visibility: shared.visibility, group_id: shared.groupId };
|
|
1759
|
+
}
|
|
1760
|
+
return undefined;
|
|
1671
1761
|
}
|
|
1672
1762
|
getHubSkillForLocal(skillId) {
|
|
1673
|
-
|
|
1674
|
-
|
|
1763
|
+
if (this.sharingRole === "hub") {
|
|
1764
|
+
const db = this.store.db;
|
|
1765
|
+
return db.prepare("SELECT * FROM hub_skills WHERE source_skill_id = ? LIMIT 1").get(skillId);
|
|
1766
|
+
}
|
|
1767
|
+
const ts = this.store.getTeamSharedSkill(skillId);
|
|
1768
|
+
if (ts && this.isCurrentClientHubInstance(ts.hubInstanceId)) {
|
|
1769
|
+
return { source_skill_id: skillId, visibility: ts.visibility, group_id: ts.groupId };
|
|
1770
|
+
}
|
|
1771
|
+
return undefined;
|
|
1675
1772
|
}
|
|
1676
1773
|
handleDeleteSession(res, url) {
|
|
1677
1774
|
const key = url.searchParams.get("key");
|
|
@@ -1973,18 +2070,24 @@ class ViewerServer {
|
|
|
1973
2070
|
handleRetryJoin(req, res) {
|
|
1974
2071
|
this.readBody(req, async (_body) => {
|
|
1975
2072
|
if (!this.ctx)
|
|
1976
|
-
return this.jsonResponse(res, { ok: false, error: "sharing_unavailable" });
|
|
2073
|
+
return this.jsonResponse(res, { ok: false, error: "sharing_unavailable", errorCode: "sharing_unavailable" });
|
|
1977
2074
|
const sharing = this.ctx.config.sharing;
|
|
1978
2075
|
if (!sharing?.enabled || sharing.role !== "client") {
|
|
1979
|
-
return this.jsonResponse(res, { ok: false, error: "not_in_client_mode" });
|
|
2076
|
+
return this.jsonResponse(res, { ok: false, error: "not_in_client_mode", errorCode: "not_in_client_mode" });
|
|
1980
2077
|
}
|
|
1981
2078
|
const hubAddress = sharing.client?.hubAddress ?? "";
|
|
1982
2079
|
const teamToken = sharing.client?.teamToken ?? "";
|
|
1983
2080
|
if (!hubAddress || !teamToken) {
|
|
1984
|
-
return this.jsonResponse(res, { ok: false, error: "missing_hub_address_or_team_token" });
|
|
2081
|
+
return this.jsonResponse(res, { ok: false, error: "missing_hub_address_or_team_token", errorCode: "missing_config" });
|
|
2082
|
+
}
|
|
2083
|
+
const hubUrl = (0, hub_1.normalizeHubUrl)(hubAddress);
|
|
2084
|
+
try {
|
|
2085
|
+
await (0, hub_1.hubRequestJson)(hubUrl, "", "/api/v1/hub/info", { method: "GET" });
|
|
2086
|
+
}
|
|
2087
|
+
catch {
|
|
2088
|
+
return this.jsonResponse(res, { ok: false, error: "hub_unreachable", errorCode: "hub_unreachable" });
|
|
1985
2089
|
}
|
|
1986
2090
|
try {
|
|
1987
|
-
const hubUrl = (0, hub_1.normalizeHubUrl)(hubAddress);
|
|
1988
2091
|
const os = await Promise.resolve().then(() => __importStar(require("os")));
|
|
1989
2092
|
const nickname = sharing.client?.nickname;
|
|
1990
2093
|
const username = nickname || os.userInfo().username || "user";
|
|
@@ -1996,6 +2099,12 @@ class ViewerServer {
|
|
|
1996
2099
|
body: JSON.stringify({ teamToken, username, deviceName: hostname, reapply: true, identityKey: existingIdentityKey }),
|
|
1997
2100
|
});
|
|
1998
2101
|
const returnedIdentityKey = String(result.identityKey || existingIdentityKey || "");
|
|
2102
|
+
let hubInstanceId = persisted?.hubInstanceId || "";
|
|
2103
|
+
try {
|
|
2104
|
+
const info = await (0, hub_1.hubRequestJson)(hubUrl, "", "/api/v1/hub/info", { method: "GET" });
|
|
2105
|
+
hubInstanceId = String(info?.hubInstanceId ?? hubInstanceId);
|
|
2106
|
+
}
|
|
2107
|
+
catch { /* best-effort */ }
|
|
1999
2108
|
this.store.setClientHubConnection({
|
|
2000
2109
|
hubUrl,
|
|
2001
2110
|
userId: String(result.userId || ""),
|
|
@@ -2005,11 +2114,22 @@ class ViewerServer {
|
|
|
2005
2114
|
connectedAt: Date.now(),
|
|
2006
2115
|
identityKey: returnedIdentityKey,
|
|
2007
2116
|
lastKnownStatus: result.status || "",
|
|
2117
|
+
hubInstanceId,
|
|
2008
2118
|
});
|
|
2119
|
+
if (result.status === "blocked") {
|
|
2120
|
+
return this.jsonResponse(res, { ok: false, error: "blocked", errorCode: "blocked" });
|
|
2121
|
+
}
|
|
2009
2122
|
this.jsonResponse(res, { ok: true, status: result.status || "pending" });
|
|
2010
2123
|
}
|
|
2011
2124
|
catch (err) {
|
|
2012
|
-
|
|
2125
|
+
const errStr = String(err);
|
|
2126
|
+
if (errStr.includes("(409)") || errStr.includes("username_taken")) {
|
|
2127
|
+
return this.jsonResponse(res, { ok: false, error: "username_taken", errorCode: "username_taken" });
|
|
2128
|
+
}
|
|
2129
|
+
if (errStr.includes("(403)") || errStr.includes("invalid_team_token")) {
|
|
2130
|
+
return this.jsonResponse(res, { ok: false, error: "invalid_team_token", errorCode: "invalid_team_token" });
|
|
2131
|
+
}
|
|
2132
|
+
this.jsonResponse(res, { ok: false, error: errStr, errorCode: "unknown" });
|
|
2013
2133
|
}
|
|
2014
2134
|
});
|
|
2015
2135
|
}
|
|
@@ -2227,9 +2347,10 @@ class ViewerServer {
|
|
|
2227
2347
|
}),
|
|
2228
2348
|
});
|
|
2229
2349
|
const hubUserId = hubClient.userId;
|
|
2230
|
-
|
|
2350
|
+
const hubTaskId = String(response?.taskId ?? task.id);
|
|
2351
|
+
if (this.sharingRole === "hub" && hubUserId) {
|
|
2231
2352
|
this.store.upsertHubTask({
|
|
2232
|
-
id:
|
|
2353
|
+
id: hubTaskId,
|
|
2233
2354
|
sourceTaskId: task.id,
|
|
2234
2355
|
sourceUserId: hubUserId,
|
|
2235
2356
|
title: task.title,
|
|
@@ -2240,6 +2361,10 @@ class ViewerServer {
|
|
|
2240
2361
|
updatedAt: task.updatedAt ?? Date.now(),
|
|
2241
2362
|
});
|
|
2242
2363
|
}
|
|
2364
|
+
else {
|
|
2365
|
+
const conn = this.store.getClientHubConnection();
|
|
2366
|
+
this.store.markTaskShared(task.id, hubTaskId, chunks.length, visibility, groupId, conn?.hubInstanceId ?? "");
|
|
2367
|
+
}
|
|
2243
2368
|
this.jsonResponse(res, { ok: true, taskId, visibility, response });
|
|
2244
2369
|
}
|
|
2245
2370
|
catch (err) {
|
|
@@ -2263,8 +2388,12 @@ class ViewerServer {
|
|
|
2263
2388
|
body: JSON.stringify({ sourceTaskId: task.id }),
|
|
2264
2389
|
});
|
|
2265
2390
|
const hubUserId = hubClient.userId;
|
|
2266
|
-
if (hubUserId)
|
|
2391
|
+
if (this.sharingRole === "hub" && hubUserId)
|
|
2267
2392
|
this.store.deleteHubTaskBySource(hubUserId, task.id);
|
|
2393
|
+
else if (task.owner === "public")
|
|
2394
|
+
this.store.downgradeTeamSharedTaskToLocal(task.id);
|
|
2395
|
+
else
|
|
2396
|
+
this.store.unmarkTaskShared(task.id);
|
|
2268
2397
|
this.jsonResponse(res, { ok: true, taskId });
|
|
2269
2398
|
}
|
|
2270
2399
|
catch (err) {
|
|
@@ -2319,7 +2448,8 @@ class ViewerServer {
|
|
|
2319
2448
|
});
|
|
2320
2449
|
}
|
|
2321
2450
|
else if (hubClient.userId) {
|
|
2322
|
-
this.store.
|
|
2451
|
+
const conn = this.store.getClientHubConnection();
|
|
2452
|
+
this.store.upsertTeamSharedChunk(chunk.id, { hubMemoryId: mid, visibility, groupId, hubInstanceId: conn?.hubInstanceId ?? "" });
|
|
2323
2453
|
}
|
|
2324
2454
|
this.jsonResponse(res, { ok: true, chunkId, visibility, response });
|
|
2325
2455
|
}
|
|
@@ -2341,9 +2471,10 @@ class ViewerServer {
|
|
|
2341
2471
|
body: JSON.stringify({ sourceChunkId: chunkId }),
|
|
2342
2472
|
});
|
|
2343
2473
|
const hubUserId = hubClient.userId;
|
|
2344
|
-
if (hubUserId)
|
|
2474
|
+
if (this.sharingRole === "hub" && hubUserId)
|
|
2345
2475
|
this.store.deleteHubMemoryBySource(hubUserId, chunkId);
|
|
2346
|
-
|
|
2476
|
+
else
|
|
2477
|
+
this.store.deleteTeamSharedChunk(chunkId);
|
|
2347
2478
|
this.jsonResponse(res, { ok: true, chunkId });
|
|
2348
2479
|
}
|
|
2349
2480
|
catch (err) {
|
|
@@ -2391,7 +2522,7 @@ class ViewerServer {
|
|
|
2391
2522
|
}),
|
|
2392
2523
|
});
|
|
2393
2524
|
const hubUserId = hubClient.userId;
|
|
2394
|
-
if (hubUserId) {
|
|
2525
|
+
if (this.sharingRole === "hub" && hubUserId) {
|
|
2395
2526
|
const existing = this.store.getHubSkillBySource(hubUserId, skillId);
|
|
2396
2527
|
this.store.upsertHubSkill({
|
|
2397
2528
|
id: response?.skillId ?? existing?.id ?? node_crypto_1.default.randomUUID(),
|
|
@@ -2408,6 +2539,15 @@ class ViewerServer {
|
|
|
2408
2539
|
updatedAt: Date.now(),
|
|
2409
2540
|
});
|
|
2410
2541
|
}
|
|
2542
|
+
else {
|
|
2543
|
+
const conn = this.store.getClientHubConnection();
|
|
2544
|
+
this.store.upsertTeamSharedSkill(skillId, {
|
|
2545
|
+
hubSkillId: String(response?.skillId ?? ""),
|
|
2546
|
+
visibility,
|
|
2547
|
+
groupId,
|
|
2548
|
+
hubInstanceId: conn?.hubInstanceId ?? "",
|
|
2549
|
+
});
|
|
2550
|
+
}
|
|
2411
2551
|
this.jsonResponse(res, { ok: true, skillId, visibility, response });
|
|
2412
2552
|
}
|
|
2413
2553
|
catch (err) {
|
|
@@ -2431,8 +2571,10 @@ class ViewerServer {
|
|
|
2431
2571
|
body: JSON.stringify({ sourceSkillId: skill.id }),
|
|
2432
2572
|
});
|
|
2433
2573
|
const hubUserId = hubClient.userId;
|
|
2434
|
-
if (hubUserId)
|
|
2574
|
+
if (this.sharingRole === "hub" && hubUserId)
|
|
2435
2575
|
this.store.deleteHubSkillBySource(hubUserId, skill.id);
|
|
2576
|
+
else
|
|
2577
|
+
this.store.deleteTeamSharedSkill(skill.id);
|
|
2436
2578
|
this.jsonResponse(res, { ok: true, skillId });
|
|
2437
2579
|
}
|
|
2438
2580
|
catch (err) {
|
|
@@ -2939,18 +3081,20 @@ class ViewerServer {
|
|
|
2939
3081
|
const isClient = newEnabled && newRole === "client";
|
|
2940
3082
|
if (wasClient && !isClient) {
|
|
2941
3083
|
await this.withdrawOrLeaveHub();
|
|
3084
|
+
this.store.clearAllTeamSharingState();
|
|
2942
3085
|
this.store.clearClientHubConnection();
|
|
2943
|
-
this.log.info("Client hub connection cleared (sharing disabled or role changed)");
|
|
3086
|
+
this.log.info("Client hub connection and team sharing state cleared (sharing disabled or role changed)");
|
|
2944
3087
|
}
|
|
2945
3088
|
if (wasClient && isClient) {
|
|
2946
3089
|
const newClientAddr = String(merged.client?.hubAddress || "");
|
|
2947
3090
|
if (newClientAddr && oldClientHubAddress && (0, hub_1.normalizeHubUrl)(newClientAddr) !== (0, hub_1.normalizeHubUrl)(oldClientHubAddress)) {
|
|
2948
3091
|
this.notifyHubLeave();
|
|
3092
|
+
this.store.clearAllTeamSharingState();
|
|
2949
3093
|
const oldConn = this.store.getClientHubConnection();
|
|
2950
3094
|
if (oldConn) {
|
|
2951
|
-
this.store.setClientHubConnection({ ...oldConn, hubUrl: (0, hub_1.normalizeHubUrl)(newClientAddr), userToken: "", lastKnownStatus: "hub_changed" });
|
|
3095
|
+
this.store.setClientHubConnection({ ...oldConn, hubUrl: (0, hub_1.normalizeHubUrl)(newClientAddr), userToken: "", hubInstanceId: "", lastKnownStatus: "hub_changed" });
|
|
2952
3096
|
}
|
|
2953
|
-
this.log.info("Client hub connection
|
|
3097
|
+
this.log.info("Client hub connection and team sharing state cleared (switched to different Hub)");
|
|
2954
3098
|
}
|
|
2955
3099
|
}
|
|
2956
3100
|
if (merged.role === "hub") {
|
|
@@ -2970,14 +3114,23 @@ class ViewerServer {
|
|
|
2970
3114
|
const nowClient = Boolean(finalSharing?.enabled) && finalSharing?.role === "client";
|
|
2971
3115
|
const previouslyClient = oldSharingEnabled && oldSharingRole === "client";
|
|
2972
3116
|
let joinStatus;
|
|
3117
|
+
let joinError;
|
|
2973
3118
|
if (nowClient && !previouslyClient) {
|
|
2974
3119
|
try {
|
|
2975
3120
|
joinStatus = await this.autoJoinOnSave(finalSharing);
|
|
2976
3121
|
}
|
|
2977
3122
|
catch (e) {
|
|
2978
|
-
|
|
3123
|
+
const msg = String(e instanceof Error ? e.message : e);
|
|
3124
|
+
this.log.warn(`Auto-join on save failed: ${msg}`);
|
|
3125
|
+
if (msg === "hub_unreachable" || msg === "username_taken" || msg === "invalid_team_token") {
|
|
3126
|
+
joinError = msg;
|
|
3127
|
+
}
|
|
2979
3128
|
}
|
|
2980
3129
|
}
|
|
3130
|
+
if (joinError) {
|
|
3131
|
+
this.jsonResponse(res, { ok: true, joinError, restart: false });
|
|
3132
|
+
return;
|
|
3133
|
+
}
|
|
2981
3134
|
this.jsonResponse(res, { ok: true, joinStatus, restart: true });
|
|
2982
3135
|
setTimeout(() => {
|
|
2983
3136
|
this.log.info("config-save: triggering gateway restart via SIGUSR1...");
|
|
@@ -3003,17 +3156,42 @@ class ViewerServer {
|
|
|
3003
3156
|
if (!hubAddress || !teamToken)
|
|
3004
3157
|
return undefined;
|
|
3005
3158
|
const hubUrl = (0, hub_1.normalizeHubUrl)(hubAddress);
|
|
3159
|
+
try {
|
|
3160
|
+
await (0, hub_1.hubRequestJson)(hubUrl, "", "/api/v1/hub/info", { method: "GET" });
|
|
3161
|
+
}
|
|
3162
|
+
catch {
|
|
3163
|
+
throw new Error("hub_unreachable");
|
|
3164
|
+
}
|
|
3006
3165
|
const os = await Promise.resolve().then(() => __importStar(require("os")));
|
|
3007
3166
|
const nickname = String(clientCfg?.nickname || "");
|
|
3008
3167
|
const username = nickname || os.userInfo().username || "user";
|
|
3009
3168
|
const hostname = os.hostname() || "unknown";
|
|
3010
3169
|
const persisted = this.store.getClientHubConnection();
|
|
3011
3170
|
const existingIdentityKey = persisted?.identityKey || "";
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3171
|
+
let result;
|
|
3172
|
+
try {
|
|
3173
|
+
result = await (0, hub_1.hubRequestJson)(hubUrl, "", "/api/v1/hub/join", {
|
|
3174
|
+
method: "POST",
|
|
3175
|
+
body: JSON.stringify({ teamToken, username, deviceName: hostname, identityKey: existingIdentityKey }),
|
|
3176
|
+
});
|
|
3177
|
+
}
|
|
3178
|
+
catch (err) {
|
|
3179
|
+
const errStr = String(err);
|
|
3180
|
+
if (errStr.includes("(409)") || errStr.includes("username_taken")) {
|
|
3181
|
+
throw new Error("username_taken");
|
|
3182
|
+
}
|
|
3183
|
+
if (errStr.includes("(403)") || errStr.includes("invalid_team_token")) {
|
|
3184
|
+
throw new Error("invalid_team_token");
|
|
3185
|
+
}
|
|
3186
|
+
throw err;
|
|
3187
|
+
}
|
|
3016
3188
|
const returnedIdentityKey = String(result.identityKey || existingIdentityKey || "");
|
|
3189
|
+
let hubInstanceId = persisted?.hubInstanceId || "";
|
|
3190
|
+
try {
|
|
3191
|
+
const info = await (0, hub_1.hubRequestJson)(hubUrl, "", "/api/v1/hub/info", { method: "GET" });
|
|
3192
|
+
hubInstanceId = String(info?.hubInstanceId ?? hubInstanceId);
|
|
3193
|
+
}
|
|
3194
|
+
catch { /* best-effort */ }
|
|
3017
3195
|
this.store.setClientHubConnection({
|
|
3018
3196
|
hubUrl,
|
|
3019
3197
|
userId: String(result.userId || ""),
|
|
@@ -3023,6 +3201,7 @@ class ViewerServer {
|
|
|
3023
3201
|
connectedAt: Date.now(),
|
|
3024
3202
|
identityKey: returnedIdentityKey,
|
|
3025
3203
|
lastKnownStatus: result.status || "",
|
|
3204
|
+
hubInstanceId,
|
|
3026
3205
|
});
|
|
3027
3206
|
this.log.info(`Auto-join on save: status=${result.status}, userId=${result.userId}`);
|
|
3028
3207
|
if (result.userToken) {
|
|
@@ -3034,6 +3213,7 @@ class ViewerServer {
|
|
|
3034
3213
|
this.readBody(_req, async () => {
|
|
3035
3214
|
try {
|
|
3036
3215
|
await this.withdrawOrLeaveHub();
|
|
3216
|
+
this.store.clearAllTeamSharingState();
|
|
3037
3217
|
this.store.clearClientHubConnection();
|
|
3038
3218
|
const configPath = this.getOpenClawConfigPath();
|
|
3039
3219
|
if (configPath && node_fs_1.default.existsSync(configPath)) {
|
|
@@ -3233,17 +3413,45 @@ class ViewerServer {
|
|
|
3233
3413
|
}
|
|
3234
3414
|
}
|
|
3235
3415
|
catch { }
|
|
3236
|
-
const
|
|
3416
|
+
const baseUrl = hubUrl.replace(/\/+$/, "");
|
|
3417
|
+
const infoUrl = baseUrl + "/api/v1/hub/info";
|
|
3237
3418
|
const ctrl = new AbortController();
|
|
3238
3419
|
const timeout = setTimeout(() => ctrl.abort(), 8000);
|
|
3239
3420
|
try {
|
|
3240
|
-
const r = await fetch(
|
|
3421
|
+
const r = await fetch(infoUrl, { signal: ctrl.signal });
|
|
3241
3422
|
clearTimeout(timeout);
|
|
3242
3423
|
if (!r.ok) {
|
|
3243
3424
|
this.jsonResponse(res, { ok: false, error: `HTTP ${r.status}` });
|
|
3244
3425
|
return;
|
|
3245
3426
|
}
|
|
3246
3427
|
const info = await r.json();
|
|
3428
|
+
const { teamToken, nickname } = JSON.parse(body);
|
|
3429
|
+
if (teamToken) {
|
|
3430
|
+
const username = (typeof nickname === "string" && nickname.trim()) || node_os_1.default.userInfo().username || "user";
|
|
3431
|
+
const persisted = this.store.getClientHubConnection();
|
|
3432
|
+
const identityKey = persisted?.identityKey || "";
|
|
3433
|
+
try {
|
|
3434
|
+
const joinR = await fetch(baseUrl + "/api/v1/hub/join", {
|
|
3435
|
+
method: "POST",
|
|
3436
|
+
headers: { "content-type": "application/json" },
|
|
3437
|
+
body: JSON.stringify({ teamToken, username, identityKey, deviceName: node_os_1.default.hostname(), dryRun: true }),
|
|
3438
|
+
});
|
|
3439
|
+
const joinData = await joinR.json();
|
|
3440
|
+
if (!joinR.ok && joinData.error === "username_taken") {
|
|
3441
|
+
this.jsonResponse(res, { ok: false, error: "username_taken", teamName: info.teamName || "" });
|
|
3442
|
+
return;
|
|
3443
|
+
}
|
|
3444
|
+
if (!joinR.ok && joinData.error === "invalid_team_token") {
|
|
3445
|
+
this.jsonResponse(res, { ok: false, error: "invalid_team_token", teamName: info.teamName || "" });
|
|
3446
|
+
return;
|
|
3447
|
+
}
|
|
3448
|
+
if (joinR.ok && joinData.status === "blocked") {
|
|
3449
|
+
this.jsonResponse(res, { ok: false, error: "blocked", teamName: info.teamName || "" });
|
|
3450
|
+
return;
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
catch { /* join check is best-effort; connection itself is OK */ }
|
|
3454
|
+
}
|
|
3247
3455
|
this.jsonResponse(res, { ok: true, teamName: info.teamName || "", apiVersion: info.apiVersion || "" });
|
|
3248
3456
|
}
|
|
3249
3457
|
catch (e) {
|
|
@@ -3445,7 +3653,8 @@ class ViewerServer {
|
|
|
3445
3653
|
node_fs_1.default.renameSync(srcDir, extDir);
|
|
3446
3654
|
// Install dependencies
|
|
3447
3655
|
this.log.info(`update-install: installing dependencies...`);
|
|
3448
|
-
|
|
3656
|
+
const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
3657
|
+
(0, node_child_process_1.execFile)(npmCmd, ["install", "--omit=dev", "--ignore-scripts"], { cwd: extDir, timeout: 120_000 }, (npmErr, npmOut, npmStderr) => {
|
|
3449
3658
|
if (npmErr) {
|
|
3450
3659
|
try {
|
|
3451
3660
|
node_fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
@@ -3455,18 +3664,15 @@ class ViewerServer {
|
|
|
3455
3664
|
this.jsonResponse(res, { ok: false, error: `Dependency install failed: ${npmStderr || npmErr.message}` });
|
|
3456
3665
|
return;
|
|
3457
3666
|
}
|
|
3458
|
-
|
|
3459
|
-
(0, node_child_process_1.exec)(`cd ${extDir} && npm rebuild better-sqlite3`, { timeout: 60_000 }, (rebuildErr, rebuildOut, rebuildStderr) => {
|
|
3667
|
+
(0, node_child_process_1.execFile)(npmCmd, ["rebuild", "better-sqlite3"], { cwd: extDir, timeout: 60_000 }, (rebuildErr, rebuildOut, rebuildStderr) => {
|
|
3460
3668
|
if (rebuildErr) {
|
|
3461
3669
|
this.log.warn(`update-install: better-sqlite3 rebuild failed: ${rebuildErr.message}`);
|
|
3462
3670
|
const stderr = String(rebuildStderr || "").trim();
|
|
3463
3671
|
if (stderr)
|
|
3464
3672
|
this.log.warn(`update-install: rebuild stderr: ${stderr.slice(0, 500)}`);
|
|
3465
|
-
// Continue so postinstall.cjs can run (it will try rebuild again and show user guidance)
|
|
3466
3673
|
}
|
|
3467
|
-
// Run postinstall.cjs: legacy cleanup, skill install, version marker, and optional sqlite re-check
|
|
3468
3674
|
this.log.info(`update-install: running postinstall...`);
|
|
3469
|
-
(0, node_child_process_1.
|
|
3675
|
+
(0, node_child_process_1.execFile)(process.execPath, ["scripts/postinstall.cjs"], { cwd: extDir, timeout: 180_000 }, (postErr, postOut, postStderr) => {
|
|
3470
3676
|
try {
|
|
3471
3677
|
node_fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
|
|
3472
3678
|
}
|
|
@@ -3476,7 +3682,6 @@ class ViewerServer {
|
|
|
3476
3682
|
const postStderrStr = String(postStderr || "").trim();
|
|
3477
3683
|
if (postStderrStr)
|
|
3478
3684
|
this.log.warn(`update-install: postinstall stderr: ${postStderrStr.slice(0, 500)}`);
|
|
3479
|
-
// Still report success; plugin is updated, user can run postinstall manually if needed
|
|
3480
3685
|
}
|
|
3481
3686
|
// Read new version
|
|
3482
3687
|
let newVersion = "unknown";
|
|
@@ -3833,7 +4038,7 @@ class ViewerServer {
|
|
|
3833
4038
|
else if (this.migrationState.done) {
|
|
3834
4039
|
const evtName = this.migrationState.stopped ? "stopped" : "done";
|
|
3835
4040
|
res.write(`event: state\ndata: ${JSON.stringify(this.migrationState)}\n\n`);
|
|
3836
|
-
res.write(`event: ${evtName}\ndata: ${JSON.stringify({ ok:
|
|
4041
|
+
res.write(`event: ${evtName}\ndata: ${JSON.stringify({ ok: this.migrationState.success, ...this.migrationState })}\n\n`);
|
|
3837
4042
|
res.end();
|
|
3838
4043
|
}
|
|
3839
4044
|
else {
|
|
@@ -3872,22 +4077,11 @@ class ViewerServer {
|
|
|
3872
4077
|
res.on("close", () => {
|
|
3873
4078
|
this.migrationSSEClients = this.migrationSSEClients.filter(c => c !== res);
|
|
3874
4079
|
});
|
|
3875
|
-
this.
|
|
3876
|
-
this.migrationState = { phase: "", stored: 0, skipped: 0, merged: 0, errors: 0, processed: 0, total: 0, lastItem: null, done: false, stopped: false };
|
|
4080
|
+
this.migrationState = createInitialMigrationState();
|
|
3877
4081
|
const send = (event, data) => {
|
|
3878
4082
|
if (event === "item") {
|
|
3879
4083
|
const d = data;
|
|
3880
|
-
|
|
3881
|
-
this.migrationState.stored++;
|
|
3882
|
-
else if (d.status === "skipped" || d.status === "duplicate")
|
|
3883
|
-
this.migrationState.skipped++;
|
|
3884
|
-
else if (d.status === "merged")
|
|
3885
|
-
this.migrationState.merged++;
|
|
3886
|
-
else if (d.status === "error")
|
|
3887
|
-
this.migrationState.errors++;
|
|
3888
|
-
this.migrationState.processed = d.index ?? this.migrationState.processed + 1;
|
|
3889
|
-
this.migrationState.total = d.total ?? this.migrationState.total;
|
|
3890
|
-
this.migrationState.lastItem = d;
|
|
4084
|
+
applyMigrationItemToState(this.migrationState, d);
|
|
3891
4085
|
}
|
|
3892
4086
|
else if (event === "phase") {
|
|
3893
4087
|
this.migrationState.phase = data.phase;
|
|
@@ -3901,12 +4095,14 @@ class ViewerServer {
|
|
|
3901
4095
|
this.runMigration(send, opts.sources, concurrency).finally(() => {
|
|
3902
4096
|
this.migrationRunning = false;
|
|
3903
4097
|
this.migrationState.done = true;
|
|
4098
|
+
this.migrationState.success = computeMigrationSuccess(this.migrationState);
|
|
4099
|
+
const donePayload = { ok: this.migrationState.success, ...this.migrationState };
|
|
3904
4100
|
if (this.migrationAbort) {
|
|
3905
4101
|
this.migrationState.stopped = true;
|
|
3906
|
-
this.broadcastSSE("stopped",
|
|
4102
|
+
this.broadcastSSE("stopped", donePayload);
|
|
3907
4103
|
}
|
|
3908
4104
|
else {
|
|
3909
|
-
this.broadcastSSE("done",
|
|
4105
|
+
this.broadcastSSE("done", donePayload);
|
|
3910
4106
|
}
|
|
3911
4107
|
this.migrationAbort = false;
|
|
3912
4108
|
const clientsToClose = [...this.migrationSSEClients];
|
|
@@ -3992,12 +4188,25 @@ class ViewerServer {
|
|
|
3992
4188
|
continue;
|
|
3993
4189
|
}
|
|
3994
4190
|
try {
|
|
3995
|
-
const
|
|
4191
|
+
const stepFailures = [];
|
|
4192
|
+
let summary = "";
|
|
4193
|
+
try {
|
|
4194
|
+
summary = await summarizer.summarize(row.text);
|
|
4195
|
+
}
|
|
4196
|
+
catch (err) {
|
|
4197
|
+
stepFailures.push("summarization");
|
|
4198
|
+
this.log.warn(`Migration summarization failed: ${err}`);
|
|
4199
|
+
}
|
|
4200
|
+
if (!summary) {
|
|
4201
|
+
stepFailures.push("summarization");
|
|
4202
|
+
summary = row.text.slice(0, 200);
|
|
4203
|
+
}
|
|
3996
4204
|
let embedding = null;
|
|
3997
4205
|
try {
|
|
3998
4206
|
[embedding] = await this.embedder.embed([summary]);
|
|
3999
4207
|
}
|
|
4000
4208
|
catch (err) {
|
|
4209
|
+
stepFailures.push("embedding");
|
|
4001
4210
|
this.log.warn(`Migration embed failed: ${err}`);
|
|
4002
4211
|
}
|
|
4003
4212
|
let dedupStatus = "active";
|
|
@@ -4013,30 +4222,36 @@ class ViewerServer {
|
|
|
4013
4222
|
return { index: idx + 1, summary: chunk?.summary ?? "", chunkId: s.chunkId };
|
|
4014
4223
|
}).filter(c => c.summary);
|
|
4015
4224
|
if (candidates.length > 0) {
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4225
|
+
try {
|
|
4226
|
+
const dedupResult = await summarizer.judgeDedup(summary, candidates);
|
|
4227
|
+
if (dedupResult?.action === "DUPLICATE" && dedupResult.targetIndex) {
|
|
4228
|
+
const targetId = candidates[dedupResult.targetIndex - 1]?.chunkId;
|
|
4229
|
+
if (targetId) {
|
|
4230
|
+
dedupStatus = "duplicate";
|
|
4231
|
+
dedupTarget = targetId;
|
|
4232
|
+
dedupReason = dedupResult.reason;
|
|
4233
|
+
}
|
|
4023
4234
|
}
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4235
|
+
else if (dedupResult?.action === "UPDATE" && dedupResult.targetIndex && dedupResult.mergedSummary) {
|
|
4236
|
+
const targetId = candidates[dedupResult.targetIndex - 1]?.chunkId;
|
|
4237
|
+
if (targetId) {
|
|
4238
|
+
this.store.updateChunkSummaryAndContent(targetId, dedupResult.mergedSummary, row.text);
|
|
4239
|
+
try {
|
|
4240
|
+
const [newEmb] = await this.embedder.embed([dedupResult.mergedSummary]);
|
|
4241
|
+
if (newEmb)
|
|
4242
|
+
this.store.upsertEmbedding(targetId, newEmb);
|
|
4243
|
+
}
|
|
4244
|
+
catch { /* best-effort */ }
|
|
4245
|
+
dedupStatus = "merged";
|
|
4246
|
+
dedupTarget = targetId;
|
|
4247
|
+
dedupReason = dedupResult.reason;
|
|
4033
4248
|
}
|
|
4034
|
-
catch { /* best-effort */ }
|
|
4035
|
-
dedupStatus = "merged";
|
|
4036
|
-
dedupTarget = targetId;
|
|
4037
|
-
dedupReason = dedupResult.reason;
|
|
4038
4249
|
}
|
|
4039
4250
|
}
|
|
4251
|
+
catch (err) {
|
|
4252
|
+
stepFailures.push("dedup");
|
|
4253
|
+
this.log.warn(`Migration dedup judgment failed: ${err}`);
|
|
4254
|
+
}
|
|
4040
4255
|
}
|
|
4041
4256
|
}
|
|
4042
4257
|
}
|
|
@@ -4060,8 +4275,8 @@ class ViewerServer {
|
|
|
4060
4275
|
mergeCount: 0,
|
|
4061
4276
|
lastHitAt: null,
|
|
4062
4277
|
mergeHistory: "[]",
|
|
4063
|
-
createdAt:
|
|
4064
|
-
updatedAt:
|
|
4278
|
+
createdAt: Number(row.updated_at) < 1e12 ? Number(row.updated_at) * 1000 : Number(row.updated_at),
|
|
4279
|
+
updatedAt: Number(row.updated_at) < 1e12 ? Number(row.updated_at) * 1000 : Number(row.updated_at),
|
|
4065
4280
|
};
|
|
4066
4281
|
this.store.insertChunk(chunk);
|
|
4067
4282
|
if (embedding && dedupStatus === "active") {
|
|
@@ -4075,7 +4290,14 @@ class ViewerServer {
|
|
|
4075
4290
|
preview: row.text.slice(0, 120),
|
|
4076
4291
|
summary: summary.slice(0, 80),
|
|
4077
4292
|
source: file,
|
|
4293
|
+
stepFailures,
|
|
4078
4294
|
});
|
|
4295
|
+
if (stepFailures.length > 0) {
|
|
4296
|
+
this.log.warn(`[MIGRATION] sqlite item imported with step failures: ${stepFailures.join(",")}`);
|
|
4297
|
+
}
|
|
4298
|
+
else {
|
|
4299
|
+
this.log.info("[MIGRATION] sqlite item imported successfully (all steps)");
|
|
4300
|
+
}
|
|
4079
4301
|
}
|
|
4080
4302
|
catch (err) {
|
|
4081
4303
|
totalErrors++;
|
|
@@ -4216,12 +4438,25 @@ class ViewerServer {
|
|
|
4216
4438
|
continue;
|
|
4217
4439
|
}
|
|
4218
4440
|
try {
|
|
4219
|
-
const
|
|
4441
|
+
const stepFailures = [];
|
|
4442
|
+
let summary = "";
|
|
4443
|
+
try {
|
|
4444
|
+
summary = await summarizer.summarize(content);
|
|
4445
|
+
}
|
|
4446
|
+
catch (err) {
|
|
4447
|
+
stepFailures.push("summarization");
|
|
4448
|
+
this.log.warn(`Migration summarization failed: ${err}`);
|
|
4449
|
+
}
|
|
4450
|
+
if (!summary) {
|
|
4451
|
+
stepFailures.push("summarization");
|
|
4452
|
+
summary = content.slice(0, 200);
|
|
4453
|
+
}
|
|
4220
4454
|
let embedding = null;
|
|
4221
4455
|
try {
|
|
4222
4456
|
[embedding] = await this.embedder.embed([summary]);
|
|
4223
4457
|
}
|
|
4224
4458
|
catch (err) {
|
|
4459
|
+
stepFailures.push("embedding");
|
|
4225
4460
|
this.log.warn(`Migration embed failed: ${err}`);
|
|
4226
4461
|
}
|
|
4227
4462
|
let dedupStatus = "active";
|
|
@@ -4237,30 +4472,36 @@ class ViewerServer {
|
|
|
4237
4472
|
return { index: i + 1, summary: chunk?.summary ?? "", chunkId: s.chunkId };
|
|
4238
4473
|
}).filter(c => c.summary);
|
|
4239
4474
|
if (candidates.length > 0) {
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4475
|
+
try {
|
|
4476
|
+
const dedupResult = await summarizer.judgeDedup(summary, candidates);
|
|
4477
|
+
if (dedupResult?.action === "DUPLICATE" && dedupResult.targetIndex) {
|
|
4478
|
+
const targetId = candidates[dedupResult.targetIndex - 1]?.chunkId;
|
|
4479
|
+
if (targetId) {
|
|
4480
|
+
dedupStatus = "duplicate";
|
|
4481
|
+
dedupTarget = targetId;
|
|
4482
|
+
dedupReason = dedupResult.reason;
|
|
4483
|
+
}
|
|
4247
4484
|
}
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4485
|
+
else if (dedupResult?.action === "UPDATE" && dedupResult.targetIndex && dedupResult.mergedSummary) {
|
|
4486
|
+
const targetId = candidates[dedupResult.targetIndex - 1]?.chunkId;
|
|
4487
|
+
if (targetId) {
|
|
4488
|
+
this.store.updateChunkSummaryAndContent(targetId, dedupResult.mergedSummary, content);
|
|
4489
|
+
try {
|
|
4490
|
+
const [newEmb] = await this.embedder.embed([dedupResult.mergedSummary]);
|
|
4491
|
+
if (newEmb)
|
|
4492
|
+
this.store.upsertEmbedding(targetId, newEmb);
|
|
4493
|
+
}
|
|
4494
|
+
catch { /* best-effort */ }
|
|
4495
|
+
dedupStatus = "merged";
|
|
4496
|
+
dedupTarget = targetId;
|
|
4497
|
+
dedupReason = dedupResult.reason;
|
|
4257
4498
|
}
|
|
4258
|
-
catch { /* best-effort */ }
|
|
4259
|
-
dedupStatus = "merged";
|
|
4260
|
-
dedupTarget = targetId;
|
|
4261
|
-
dedupReason = dedupResult.reason;
|
|
4262
4499
|
}
|
|
4263
4500
|
}
|
|
4501
|
+
catch (err) {
|
|
4502
|
+
stepFailures.push("dedup");
|
|
4503
|
+
this.log.warn(`Migration dedup judgment failed: ${err}`);
|
|
4504
|
+
}
|
|
4264
4505
|
}
|
|
4265
4506
|
}
|
|
4266
4507
|
}
|
|
@@ -4277,7 +4518,13 @@ class ViewerServer {
|
|
|
4277
4518
|
if (embedding && dedupStatus === "active")
|
|
4278
4519
|
this.store.upsertEmbedding(chunkId, embedding);
|
|
4279
4520
|
totalStored++;
|
|
4280
|
-
send("item", { index: idx, total: totalMsgs, status: dedupStatus === "active" ? "stored" : dedupStatus, preview: content.slice(0, 120), summary: summary.slice(0, 80), source: file, agent: agentId, role: msgRole });
|
|
4521
|
+
send("item", { index: idx, total: totalMsgs, status: dedupStatus === "active" ? "stored" : dedupStatus, preview: content.slice(0, 120), summary: summary.slice(0, 80), source: file, agent: agentId, role: msgRole, stepFailures });
|
|
4522
|
+
if (stepFailures.length > 0) {
|
|
4523
|
+
this.log.warn(`[MIGRATION] session item imported with step failures: ${stepFailures.join(",")}`);
|
|
4524
|
+
}
|
|
4525
|
+
else {
|
|
4526
|
+
this.log.info("[MIGRATION] session item imported successfully (all steps)");
|
|
4527
|
+
}
|
|
4281
4528
|
}
|
|
4282
4529
|
catch (err) {
|
|
4283
4530
|
totalErrors++;
|
|
@@ -4320,7 +4567,14 @@ class ViewerServer {
|
|
|
4320
4567
|
}
|
|
4321
4568
|
}
|
|
4322
4569
|
send("progress", { total: totalProcessed, processed: totalProcessed, phase: "done" });
|
|
4323
|
-
send("summary", {
|
|
4570
|
+
send("summary", {
|
|
4571
|
+
totalProcessed,
|
|
4572
|
+
totalStored,
|
|
4573
|
+
totalSkipped,
|
|
4574
|
+
totalErrors,
|
|
4575
|
+
success: computeMigrationSuccess(this.migrationState),
|
|
4576
|
+
stepFailures: this.migrationState.stepFailures,
|
|
4577
|
+
});
|
|
4324
4578
|
}
|
|
4325
4579
|
// ─── Post-processing: independent task/skill generation ───
|
|
4326
4580
|
handlePostprocess(req, res) {
|