@memtensor/memos-local-openclaw-plugin 1.0.4-beta.0 → 1.0.4-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/client/connector.d.ts +4 -0
- package/dist/client/connector.d.ts.map +1 -1
- package/dist/client/connector.js +92 -0
- package/dist/client/connector.js.map +1 -1
- package/dist/config.js +9 -9
- package/dist/config.js.map +1 -1
- package/dist/hub/server.d.ts.map +1 -1
- package/dist/hub/server.js +27 -22
- package/dist/hub/server.js.map +1 -1
- package/dist/hub/user-manager.d.ts +1 -0
- package/dist/hub/user-manager.d.ts.map +1 -1
- package/dist/hub/user-manager.js +13 -0
- package/dist/hub/user-manager.js.map +1 -1
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +20 -94
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/viewer/html.d.ts.map +1 -1
- package/dist/viewer/html.js +393 -211
- package/dist/viewer/html.js.map +1 -1
- package/dist/viewer/server.d.ts +2 -0
- package/dist/viewer/server.d.ts.map +1 -1
- package/dist/viewer/server.js +104 -15
- package/dist/viewer/server.js.map +1 -1
- package/package.json +1 -1
- package/src/client/connector.ts +94 -0
- package/src/config.ts +9 -9
- package/src/hub/server.ts +28 -22
- package/src/hub/user-manager.ts +13 -0
- package/src/storage/sqlite.ts +20 -94
- package/src/viewer/html.ts +393 -211
- package/src/viewer/server.ts +98 -15
package/package.json
CHANGED
package/src/client/connector.ts
CHANGED
|
@@ -59,6 +59,74 @@ export async function getHubStatus(store: SqliteStore, config: MemosLocalConfig)
|
|
|
59
59
|
const conn = store.getClientHubConnection();
|
|
60
60
|
const hubAddress = conn?.hubUrl || config.sharing?.client?.hubAddress || "";
|
|
61
61
|
const userToken = conn?.userToken || config.sharing?.client?.userToken || "";
|
|
62
|
+
|
|
63
|
+
if (conn && conn.userId && (!userToken || userToken === "")) {
|
|
64
|
+
const teamToken = config.sharing?.client?.teamToken ?? "";
|
|
65
|
+
if (hubAddress && teamToken) {
|
|
66
|
+
try {
|
|
67
|
+
const result = await hubRequestJson(normalizeHubUrl(hubAddress), "", "/api/v1/hub/join", {
|
|
68
|
+
method: "POST",
|
|
69
|
+
body: JSON.stringify({ teamToken, username: conn.username || "user" }),
|
|
70
|
+
}) as any;
|
|
71
|
+
if (result.status === "pending") {
|
|
72
|
+
return {
|
|
73
|
+
connected: false,
|
|
74
|
+
hubUrl: normalizeHubUrl(hubAddress),
|
|
75
|
+
user: {
|
|
76
|
+
id: conn.userId,
|
|
77
|
+
username: conn.username || "",
|
|
78
|
+
role: "member",
|
|
79
|
+
status: "pending",
|
|
80
|
+
groups: [],
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
if (result.status === "active" && result.userToken) {
|
|
85
|
+
store.setClientHubConnection({
|
|
86
|
+
hubUrl: normalizeHubUrl(hubAddress),
|
|
87
|
+
userId: String(result.userId),
|
|
88
|
+
username: conn.username || "",
|
|
89
|
+
userToken: result.userToken,
|
|
90
|
+
role: "member",
|
|
91
|
+
connectedAt: Date.now(),
|
|
92
|
+
});
|
|
93
|
+
const me = await hubRequestJson(normalizeHubUrl(hubAddress), result.userToken, "/api/v1/hub/me", { method: "GET" }) as any;
|
|
94
|
+
return {
|
|
95
|
+
connected: true,
|
|
96
|
+
hubUrl: normalizeHubUrl(hubAddress),
|
|
97
|
+
user: {
|
|
98
|
+
id: String(me.id),
|
|
99
|
+
username: String(me.username ?? ""),
|
|
100
|
+
role: String(me.role ?? "member") as UserRole,
|
|
101
|
+
status: String(me.status ?? "active"),
|
|
102
|
+
groups: Array.isArray(me.groups)
|
|
103
|
+
? me.groups.map((group: any) => ({
|
|
104
|
+
id: String(group.id),
|
|
105
|
+
name: String(group.name),
|
|
106
|
+
description: typeof group.description === "string" ? group.description : undefined,
|
|
107
|
+
}))
|
|
108
|
+
: [],
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (result.status === "rejected") {
|
|
113
|
+
return {
|
|
114
|
+
connected: false,
|
|
115
|
+
hubUrl: normalizeHubUrl(hubAddress),
|
|
116
|
+
user: {
|
|
117
|
+
id: conn.userId,
|
|
118
|
+
username: conn.username || "",
|
|
119
|
+
role: "member",
|
|
120
|
+
status: "rejected",
|
|
121
|
+
groups: [],
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
} catch { /* fall through */ }
|
|
126
|
+
}
|
|
127
|
+
return { connected: false, user: null };
|
|
128
|
+
}
|
|
129
|
+
|
|
62
130
|
if (!hubAddress || !userToken) {
|
|
63
131
|
return { connected: false, user: null };
|
|
64
132
|
}
|
|
@@ -107,6 +175,23 @@ export async function autoJoinHub(
|
|
|
107
175
|
body: JSON.stringify({ teamToken, username, deviceName: hostname }),
|
|
108
176
|
}) as any;
|
|
109
177
|
|
|
178
|
+
if (result.status === "pending") {
|
|
179
|
+
log.info(`Join request submitted, awaiting admin approval. userId=${result.userId}`);
|
|
180
|
+
store.setClientHubConnection({
|
|
181
|
+
hubUrl,
|
|
182
|
+
userId: String(result.userId),
|
|
183
|
+
username,
|
|
184
|
+
userToken: "",
|
|
185
|
+
role: "member",
|
|
186
|
+
connectedAt: Date.now(),
|
|
187
|
+
});
|
|
188
|
+
throw new PendingApprovalError(result.userId);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (result.status === "rejected") {
|
|
192
|
+
throw new Error(`Join request was rejected by the Hub admin.`);
|
|
193
|
+
}
|
|
194
|
+
|
|
110
195
|
if (!result.userToken) {
|
|
111
196
|
throw new Error(`Hub join failed: ${JSON.stringify(result)}`);
|
|
112
197
|
}
|
|
@@ -122,3 +207,12 @@ export async function autoJoinHub(
|
|
|
122
207
|
});
|
|
123
208
|
return store.getClientHubConnection()!;
|
|
124
209
|
}
|
|
210
|
+
|
|
211
|
+
export class PendingApprovalError extends Error {
|
|
212
|
+
public readonly userId: string;
|
|
213
|
+
constructor(userId: string) {
|
|
214
|
+
super("Awaiting admin approval");
|
|
215
|
+
this.name = "PendingApprovalError";
|
|
216
|
+
this.userId = userId;
|
|
217
|
+
}
|
|
218
|
+
}
|
package/src/config.ts
CHANGED
|
@@ -117,22 +117,22 @@ export function resolveConfig(raw: Partial<MemosLocalConfig> | undefined, stateD
|
|
|
117
117
|
: undefined;
|
|
118
118
|
})(),
|
|
119
119
|
} : undefined,
|
|
120
|
-
sharing: {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
hub
|
|
120
|
+
sharing: (() => {
|
|
121
|
+
const role = cfg.sharing?.role ?? "client";
|
|
122
|
+
const enabled = cfg.sharing?.enabled ?? false;
|
|
123
|
+
const hub = role === "hub" ? {
|
|
124
124
|
port: cfg.sharing?.hub?.port ?? 18800,
|
|
125
125
|
teamName: cfg.sharing?.hub?.teamName ?? "",
|
|
126
126
|
teamToken: cfg.sharing?.hub?.teamToken ?? "",
|
|
127
|
-
},
|
|
128
|
-
client
|
|
127
|
+
} : { port: 18800, teamName: "", teamToken: "" };
|
|
128
|
+
const client = role === "client" ? {
|
|
129
129
|
hubAddress: cfg.sharing?.client?.hubAddress ?? "",
|
|
130
130
|
userToken: cfg.sharing?.client?.userToken ?? "",
|
|
131
131
|
teamToken: cfg.sharing?.client?.teamToken ?? "",
|
|
132
132
|
pendingUserId: cfg.sharing?.client?.pendingUserId ?? "",
|
|
133
|
-
},
|
|
134
|
-
capabilities: sharingCapabilities
|
|
135
|
-
},
|
|
133
|
+
} : { hubAddress: "", userToken: "", teamToken: "", pendingUserId: "" };
|
|
134
|
+
return { enabled, role, hub, client, capabilities: sharingCapabilities };
|
|
135
|
+
})(),
|
|
136
136
|
};
|
|
137
137
|
}
|
|
138
138
|
|
package/src/hub/server.ts
CHANGED
|
@@ -192,17 +192,32 @@ export class HubServer {
|
|
|
192
192
|
return this.json(res, 403, { error: "invalid_team_token" });
|
|
193
193
|
}
|
|
194
194
|
const username = String(body.username || `user-${randomUUID().slice(0, 8)}`);
|
|
195
|
+
const existingUsers = this.opts.store.listHubUsers();
|
|
196
|
+
const existingUser = existingUsers.find(u => u.username === username);
|
|
197
|
+
if (existingUser) {
|
|
198
|
+
if (existingUser.status === "active") {
|
|
199
|
+
const token = issueUserToken(
|
|
200
|
+
{ userId: existingUser.id, username: existingUser.username, role: existingUser.role, status: "active" },
|
|
201
|
+
this.authSecret,
|
|
202
|
+
);
|
|
203
|
+
this.userManager.approveUser(existingUser.id, token);
|
|
204
|
+
return this.json(res, 200, { status: "active", userId: existingUser.id, userToken: token });
|
|
205
|
+
}
|
|
206
|
+
if (existingUser.status === "pending") {
|
|
207
|
+
return this.json(res, 200, { status: "pending", userId: existingUser.id });
|
|
208
|
+
}
|
|
209
|
+
if (existingUser.status === "rejected") {
|
|
210
|
+
this.userManager.resetToPending(existingUser.id);
|
|
211
|
+
this.opts.log.info(`Hub: rejected user "${username}" (${existingUser.id}) re-applied, reset to pending`);
|
|
212
|
+
return this.json(res, 200, { status: "pending", userId: existingUser.id });
|
|
213
|
+
}
|
|
214
|
+
}
|
|
195
215
|
const user = this.userManager.createPendingUser({
|
|
196
216
|
username,
|
|
197
217
|
deviceName: typeof body.deviceName === "string" ? body.deviceName : undefined,
|
|
198
218
|
});
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
this.authSecret,
|
|
202
|
-
);
|
|
203
|
-
this.userManager.approveUser(user.id, token);
|
|
204
|
-
this.opts.log.info(`Hub: auto-approved user "${username}" (${user.id})`);
|
|
205
|
-
return this.json(res, 200, { status: "active", userId: user.id, userToken: token });
|
|
219
|
+
this.opts.log.info(`Hub: user "${username}" (${user.id}) registered as pending, awaiting admin approval`);
|
|
220
|
+
return this.json(res, 200, { status: "pending", userId: user.id });
|
|
206
221
|
}
|
|
207
222
|
|
|
208
223
|
if (req.method === "POST" && routePath === "/api/v1/hub/registration-status") {
|
|
@@ -297,6 +312,7 @@ export class HubServer {
|
|
|
297
312
|
// ── Group management ──
|
|
298
313
|
|
|
299
314
|
if (req.method === "GET" && routePath === "/api/v1/hub/groups") {
|
|
315
|
+
if (auth.role !== "admin") return this.json(res, 403, { error: "forbidden" });
|
|
300
316
|
const groups = this.opts.store.listHubGroups();
|
|
301
317
|
return this.json(res, 200, { groups });
|
|
302
318
|
}
|
|
@@ -321,6 +337,7 @@ export class HubServer {
|
|
|
321
337
|
const groupId = decodeURIComponent(groupDetailMatch[1]);
|
|
322
338
|
|
|
323
339
|
if (req.method === "GET") {
|
|
340
|
+
if (auth.role !== "admin") return this.json(res, 403, { error: "forbidden" });
|
|
324
341
|
const group = this.opts.store.getHubGroupById(groupId);
|
|
325
342
|
if (!group) return this.json(res, 404, { error: "not_found" });
|
|
326
343
|
const members = this.opts.store.listHubGroupMembers(groupId);
|
|
@@ -408,15 +425,8 @@ export class HubServer {
|
|
|
408
425
|
if (!sourceChunkId) return this.json(res, 400, { error: "missing_source_chunk_id" });
|
|
409
426
|
const existing = this.opts.store.getHubMemoryBySource(auth.userId, sourceChunkId);
|
|
410
427
|
const memoryId = existing?.id ?? randomUUID();
|
|
411
|
-
const visibility =
|
|
412
|
-
|
|
413
|
-
if (visibility === "group") {
|
|
414
|
-
const gid = String(m.groupId || "");
|
|
415
|
-
if (!gid) return this.json(res, 400, { error: "missing_group_id" });
|
|
416
|
-
const group = this.opts.store.getHubGroupById(gid);
|
|
417
|
-
if (!group) return this.json(res, 404, { error: "group_not_found" });
|
|
418
|
-
resolvedGroupId = gid;
|
|
419
|
-
}
|
|
428
|
+
const visibility = "public";
|
|
429
|
+
const resolvedGroupId: string | null = null;
|
|
420
430
|
const now = Date.now();
|
|
421
431
|
this.opts.store.upsertHubMemory({
|
|
422
432
|
id: memoryId,
|
|
@@ -576,7 +586,7 @@ export class HubServer {
|
|
|
576
586
|
if (!sourceSkillId) return this.json(res, 400, { error: "missing_skill_id" });
|
|
577
587
|
const existing = this.opts.store.getHubSkillBySource(auth.userId, sourceSkillId);
|
|
578
588
|
const skillId = existing?.id ?? randomUUID();
|
|
579
|
-
const visibility =
|
|
589
|
+
const visibility = "public";
|
|
580
590
|
this.opts.store.upsertHubSkill({
|
|
581
591
|
id: skillId,
|
|
582
592
|
sourceSkillId,
|
|
@@ -584,7 +594,7 @@ export class HubServer {
|
|
|
584
594
|
name: String(metadata.name || sourceSkillId),
|
|
585
595
|
description: String(metadata.description || ""),
|
|
586
596
|
version: Number(metadata.version || 1),
|
|
587
|
-
groupId:
|
|
597
|
+
groupId: null,
|
|
588
598
|
visibility,
|
|
589
599
|
bundle: JSON.stringify(body?.bundle ?? {}),
|
|
590
600
|
qualityScore: metadata.qualityScore == null ? null : Number(metadata.qualityScore),
|
|
@@ -598,10 +608,6 @@ export class HubServer {
|
|
|
598
608
|
if (skillBundleMatch) {
|
|
599
609
|
const skill = this.opts.store.getHubSkillById(decodeURIComponent(skillBundleMatch[1]));
|
|
600
610
|
if (!skill) return this.json(res, 404, { error: "not_found" });
|
|
601
|
-
const user = this.opts.store.getHubUser(auth.userId);
|
|
602
|
-
const groups = new Set((user?.groups ?? []).map((group) => group.id));
|
|
603
|
-
const allowed = skill.visibility === "public" || (skill.groupId != null && groups.has(skill.groupId));
|
|
604
|
-
if (!allowed) return this.json(res, 403, { error: "forbidden" });
|
|
605
611
|
return this.json(res, 200, {
|
|
606
612
|
skillId: skill.id,
|
|
607
613
|
metadata: {
|
package/src/hub/user-manager.ts
CHANGED
|
@@ -123,4 +123,17 @@ export class HubUserManager {
|
|
|
123
123
|
this.store.upsertHubUser(updated);
|
|
124
124
|
return updated;
|
|
125
125
|
}
|
|
126
|
+
|
|
127
|
+
resetToPending(userId: string): ManagedHubUser | null {
|
|
128
|
+
const user = this.store.getHubUser(userId);
|
|
129
|
+
if (!user) return null;
|
|
130
|
+
const updated = {
|
|
131
|
+
...user,
|
|
132
|
+
status: "pending" as const,
|
|
133
|
+
tokenHash: "",
|
|
134
|
+
approvedAt: null,
|
|
135
|
+
};
|
|
136
|
+
this.store.upsertHubUser(updated);
|
|
137
|
+
return updated;
|
|
138
|
+
}
|
|
126
139
|
}
|
package/src/storage/sqlite.ts
CHANGED
|
@@ -1874,24 +1874,16 @@ export class SqliteStore {
|
|
|
1874
1874
|
const limit = options?.maxResults ?? 10;
|
|
1875
1875
|
const userId = options?.userId ?? "";
|
|
1876
1876
|
const rows = this.db.prepare(`
|
|
1877
|
-
SELECT hc.id, hc.content, hc.summary, hc.role, hc.created_at, ht.title as task_title, ht.visibility,
|
|
1877
|
+
SELECT hc.id, hc.content, hc.summary, hc.role, hc.created_at, ht.title as task_title, ht.visibility, '' as group_name, hu.username as owner_name,
|
|
1878
1878
|
bm25(hub_chunks_fts) as rank
|
|
1879
1879
|
FROM hub_chunks_fts f
|
|
1880
1880
|
JOIN hub_chunks hc ON hc.rowid = f.rowid
|
|
1881
1881
|
JOIN hub_tasks ht ON ht.id = hc.hub_task_id
|
|
1882
|
-
LEFT JOIN hub_groups hg ON hg.id = ht.group_id
|
|
1883
1882
|
LEFT JOIN hub_users hu ON hu.id = ht.source_user_id
|
|
1884
1883
|
WHERE hub_chunks_fts MATCH ?
|
|
1885
|
-
AND (
|
|
1886
|
-
ht.visibility = 'public'
|
|
1887
|
-
OR EXISTS (
|
|
1888
|
-
SELECT 1 FROM hub_group_members gm
|
|
1889
|
-
WHERE gm.group_id = ht.group_id AND gm.user_id = ?
|
|
1890
|
-
)
|
|
1891
|
-
)
|
|
1892
1884
|
ORDER BY rank
|
|
1893
1885
|
LIMIT ?
|
|
1894
|
-
`).all(sanitizeFtsQuery(query),
|
|
1886
|
+
`).all(sanitizeFtsQuery(query), limit) as HubSearchRow[];
|
|
1895
1887
|
return rows.map((row, idx) => ({ hit: row, rank: idx + 1 }));
|
|
1896
1888
|
}
|
|
1897
1889
|
|
|
@@ -1916,12 +1908,7 @@ export class SqliteStore {
|
|
|
1916
1908
|
FROM hub_embeddings he
|
|
1917
1909
|
JOIN hub_chunks hc ON hc.id = he.chunk_id
|
|
1918
1910
|
JOIN hub_tasks ht ON ht.id = hc.hub_task_id
|
|
1919
|
-
|
|
1920
|
-
OR EXISTS (
|
|
1921
|
-
SELECT 1 FROM hub_group_members gm
|
|
1922
|
-
WHERE gm.group_id = ht.group_id AND gm.user_id = ?
|
|
1923
|
-
)
|
|
1924
|
-
`).all(userId) as Array<{ chunk_id: string; vector: Buffer; dimensions: number }>;
|
|
1911
|
+
`).all() as Array<{ chunk_id: string; vector: Buffer; dimensions: number }>;
|
|
1925
1912
|
return rows.map(r => ({
|
|
1926
1913
|
chunkId: r.chunk_id,
|
|
1927
1914
|
vector: new Float32Array(r.vector.buffer, r.vector.byteOffset, r.dimensions),
|
|
@@ -1930,22 +1917,14 @@ export class SqliteStore {
|
|
|
1930
1917
|
|
|
1931
1918
|
getVisibleHubSearchHitByChunkId(chunkId: string, userId: string): HubSearchRow | null {
|
|
1932
1919
|
const row = this.db.prepare(`
|
|
1933
|
-
SELECT hc.id, hc.content, hc.summary, hc.role, hc.created_at, ht.title as task_title, ht.visibility,
|
|
1920
|
+
SELECT hc.id, hc.content, hc.summary, hc.role, hc.created_at, ht.title as task_title, ht.visibility, '' as group_name, hu.username as owner_name,
|
|
1934
1921
|
0 as rank
|
|
1935
1922
|
FROM hub_chunks hc
|
|
1936
1923
|
JOIN hub_tasks ht ON ht.id = hc.hub_task_id
|
|
1937
|
-
LEFT JOIN hub_groups hg ON hg.id = ht.group_id
|
|
1938
1924
|
LEFT JOIN hub_users hu ON hu.id = ht.source_user_id
|
|
1939
1925
|
WHERE hc.id = ?
|
|
1940
|
-
AND (
|
|
1941
|
-
ht.visibility = 'public'
|
|
1942
|
-
OR EXISTS (
|
|
1943
|
-
SELECT 1 FROM hub_group_members gm
|
|
1944
|
-
WHERE gm.group_id = ht.group_id AND gm.user_id = ?
|
|
1945
|
-
)
|
|
1946
|
-
)
|
|
1947
1926
|
LIMIT 1
|
|
1948
|
-
`).get(chunkId
|
|
1927
|
+
`).get(chunkId) as HubSearchRow | undefined;
|
|
1949
1928
|
return row ?? null;
|
|
1950
1929
|
}
|
|
1951
1930
|
|
|
@@ -1961,38 +1940,24 @@ export class SqliteStore {
|
|
|
1961
1940
|
let rows: HubSkillSearchRow[];
|
|
1962
1941
|
if (sanitized) {
|
|
1963
1942
|
rows = this.db.prepare(`
|
|
1964
|
-
SELECT hs.id, hs.name, hs.description, hs.version, hs.visibility,
|
|
1943
|
+
SELECT hs.id, hs.name, hs.description, hs.version, hs.visibility, '' AS group_name, hu.username AS owner_name, hs.quality_score,
|
|
1965
1944
|
bm25(hub_skills_fts) as rank
|
|
1966
1945
|
FROM hub_skills_fts f
|
|
1967
1946
|
JOIN hub_skills hs ON hs.rowid = f.rowid
|
|
1968
|
-
LEFT JOIN hub_groups hg ON hg.id = hs.group_id
|
|
1969
1947
|
LEFT JOIN hub_users hu ON hu.id = hs.source_user_id
|
|
1970
1948
|
WHERE hub_skills_fts MATCH ?
|
|
1971
|
-
AND (
|
|
1972
|
-
hs.visibility = 'public'
|
|
1973
|
-
OR EXISTS (
|
|
1974
|
-
SELECT 1 FROM hub_group_members gm
|
|
1975
|
-
WHERE gm.group_id = hs.group_id AND gm.user_id = ?
|
|
1976
|
-
)
|
|
1977
|
-
)
|
|
1978
1949
|
ORDER BY rank
|
|
1979
1950
|
LIMIT ?
|
|
1980
|
-
`).all(sanitized,
|
|
1951
|
+
`).all(sanitized, limit) as HubSkillSearchRow[];
|
|
1981
1952
|
} else {
|
|
1982
1953
|
rows = this.db.prepare(`
|
|
1983
|
-
SELECT hs.id, hs.name, hs.description, hs.version, hs.visibility,
|
|
1954
|
+
SELECT hs.id, hs.name, hs.description, hs.version, hs.visibility, '' AS group_name, hu.username AS owner_name, hs.quality_score,
|
|
1984
1955
|
0 as rank
|
|
1985
1956
|
FROM hub_skills hs
|
|
1986
|
-
LEFT JOIN hub_groups hg ON hg.id = hs.group_id
|
|
1987
1957
|
LEFT JOIN hub_users hu ON hu.id = hs.source_user_id
|
|
1988
|
-
WHERE hs.visibility = 'public'
|
|
1989
|
-
OR EXISTS (
|
|
1990
|
-
SELECT 1 FROM hub_group_members gm
|
|
1991
|
-
WHERE gm.group_id = hs.group_id AND gm.user_id = ?
|
|
1992
|
-
)
|
|
1993
1958
|
ORDER BY hs.updated_at DESC
|
|
1994
1959
|
LIMIT ?
|
|
1995
|
-
`).all(
|
|
1960
|
+
`).all(limit) as HubSkillSearchRow[];
|
|
1996
1961
|
}
|
|
1997
1962
|
return rows.map((row, idx) => ({ hit: row, rank: idx + 1 }));
|
|
1998
1963
|
}
|
|
@@ -2003,19 +1968,13 @@ export class SqliteStore {
|
|
|
2003
1968
|
|
|
2004
1969
|
listVisibleHubTasks(userId: string, limit = 40): Array<{ id: string; sourceTaskId: string; sourceUserId: string; title: string; summary: string; groupId: string | null; groupName: string | null; visibility: string; ownerName: string; chunkCount: number; createdAt: number; updatedAt: number }> {
|
|
2005
1970
|
const rows = this.db.prepare(`
|
|
2006
|
-
SELECT t.*, u.username AS owner_name,
|
|
1971
|
+
SELECT t.*, u.username AS owner_name, NULL AS group_name,
|
|
2007
1972
|
(SELECT COUNT(*) FROM hub_chunks c WHERE c.hub_task_id = t.id) AS chunk_count
|
|
2008
1973
|
FROM hub_tasks t
|
|
2009
1974
|
LEFT JOIN hub_users u ON u.id = t.source_user_id
|
|
2010
|
-
LEFT JOIN hub_groups g ON g.id = t.group_id
|
|
2011
|
-
WHERE t.visibility = 'public'
|
|
2012
|
-
OR EXISTS (
|
|
2013
|
-
SELECT 1 FROM hub_group_members gm
|
|
2014
|
-
WHERE gm.group_id = t.group_id AND gm.user_id = ?
|
|
2015
|
-
)
|
|
2016
1975
|
ORDER BY t.updated_at DESC
|
|
2017
1976
|
LIMIT ?
|
|
2018
|
-
`).all(
|
|
1977
|
+
`).all(limit) as any[];
|
|
2019
1978
|
return rows.map(r => ({
|
|
2020
1979
|
id: r.id, sourceTaskId: r.source_task_id, sourceUserId: r.source_user_id,
|
|
2021
1980
|
title: r.title, summary: r.summary, groupId: r.group_id, groupName: r.group_name ?? null,
|
|
@@ -2048,18 +2007,12 @@ export class SqliteStore {
|
|
|
2048
2007
|
|
|
2049
2008
|
listVisibleHubSkills(userId: string, limit = 40): Array<{ id: string; sourceSkillId: string; sourceUserId: string; name: string; description: string; version: number; groupId: string | null; groupName: string | null; visibility: string; ownerName: string; qualityScore: number | null; createdAt: number; updatedAt: number }> {
|
|
2050
2009
|
const rows = this.db.prepare(`
|
|
2051
|
-
SELECT s.*, u.username AS owner_name,
|
|
2010
|
+
SELECT s.*, u.username AS owner_name, NULL AS group_name
|
|
2052
2011
|
FROM hub_skills s
|
|
2053
2012
|
LEFT JOIN hub_users u ON u.id = s.source_user_id
|
|
2054
|
-
LEFT JOIN hub_groups g ON g.id = s.group_id
|
|
2055
|
-
WHERE s.visibility = 'public'
|
|
2056
|
-
OR EXISTS (
|
|
2057
|
-
SELECT 1 FROM hub_group_members gm
|
|
2058
|
-
WHERE gm.group_id = s.group_id AND gm.user_id = ?
|
|
2059
|
-
)
|
|
2060
2013
|
ORDER BY s.updated_at DESC
|
|
2061
2014
|
LIMIT ?
|
|
2062
|
-
`).all(
|
|
2015
|
+
`).all(limit) as any[];
|
|
2063
2016
|
return rows.map(r => ({
|
|
2064
2017
|
id: r.id, sourceSkillId: r.source_skill_id, sourceUserId: r.source_user_id,
|
|
2065
2018
|
name: r.name, description: r.description, version: r.version,
|
|
@@ -2149,23 +2102,15 @@ export class SqliteStore {
|
|
|
2149
2102
|
const sanitized = sanitizeFtsQuery(query);
|
|
2150
2103
|
if (!sanitized) return [];
|
|
2151
2104
|
const rows = this.db.prepare(`
|
|
2152
|
-
SELECT hm.id, hm.content, hm.summary, hm.role, hm.created_at, hm.visibility,
|
|
2105
|
+
SELECT hm.id, hm.content, hm.summary, hm.role, hm.created_at, hm.visibility, '' as group_name, hu.username as owner_name,
|
|
2153
2106
|
bm25(hub_memories_fts) as rank
|
|
2154
2107
|
FROM hub_memories_fts f
|
|
2155
2108
|
JOIN hub_memories hm ON hm.rowid = f.rowid
|
|
2156
|
-
LEFT JOIN hub_groups hg ON hg.id = hm.group_id
|
|
2157
2109
|
LEFT JOIN hub_users hu ON hu.id = hm.source_user_id
|
|
2158
2110
|
WHERE hub_memories_fts MATCH ?
|
|
2159
|
-
AND (
|
|
2160
|
-
hm.visibility = 'public'
|
|
2161
|
-
OR EXISTS (
|
|
2162
|
-
SELECT 1 FROM hub_group_members gm
|
|
2163
|
-
WHERE gm.group_id = hm.group_id AND gm.user_id = ?
|
|
2164
|
-
)
|
|
2165
|
-
)
|
|
2166
2111
|
ORDER BY rank
|
|
2167
2112
|
LIMIT ?
|
|
2168
|
-
`).all(sanitized,
|
|
2113
|
+
`).all(sanitized, limit) as HubMemorySearchRow[];
|
|
2169
2114
|
return rows.map((row, idx) => ({ hit: row, rank: idx + 1 }));
|
|
2170
2115
|
}
|
|
2171
2116
|
|
|
@@ -2174,12 +2119,7 @@ export class SqliteStore {
|
|
|
2174
2119
|
SELECT hme.memory_id, hme.vector, hme.dimensions
|
|
2175
2120
|
FROM hub_memory_embeddings hme
|
|
2176
2121
|
JOIN hub_memories hm ON hm.id = hme.memory_id
|
|
2177
|
-
|
|
2178
|
-
OR EXISTS (
|
|
2179
|
-
SELECT 1 FROM hub_group_members gm
|
|
2180
|
-
WHERE gm.group_id = hm.group_id AND gm.user_id = ?
|
|
2181
|
-
)
|
|
2182
|
-
`).all(userId) as Array<{ memory_id: string; vector: Buffer; dimensions: number }>;
|
|
2122
|
+
`).all() as Array<{ memory_id: string; vector: Buffer; dimensions: number }>;
|
|
2183
2123
|
return rows.map(r => ({
|
|
2184
2124
|
memoryId: r.memory_id,
|
|
2185
2125
|
vector: new Float32Array(r.vector.buffer, r.vector.byteOffset, r.dimensions),
|
|
@@ -2188,38 +2128,24 @@ export class SqliteStore {
|
|
|
2188
2128
|
|
|
2189
2129
|
getVisibleHubSearchHitByMemoryId(memoryId: string, userId: string): HubMemorySearchRow | null {
|
|
2190
2130
|
const row = this.db.prepare(`
|
|
2191
|
-
SELECT hm.id, hm.content, hm.summary, hm.role, hm.created_at, hm.visibility,
|
|
2131
|
+
SELECT hm.id, hm.content, hm.summary, hm.role, hm.created_at, hm.visibility, '' as group_name, hu.username as owner_name,
|
|
2192
2132
|
0 as rank
|
|
2193
2133
|
FROM hub_memories hm
|
|
2194
|
-
LEFT JOIN hub_groups hg ON hg.id = hm.group_id
|
|
2195
2134
|
LEFT JOIN hub_users hu ON hu.id = hm.source_user_id
|
|
2196
2135
|
WHERE hm.id = ?
|
|
2197
|
-
AND (
|
|
2198
|
-
hm.visibility = 'public'
|
|
2199
|
-
OR EXISTS (
|
|
2200
|
-
SELECT 1 FROM hub_group_members gm
|
|
2201
|
-
WHERE gm.group_id = hm.group_id AND gm.user_id = ?
|
|
2202
|
-
)
|
|
2203
|
-
)
|
|
2204
2136
|
LIMIT 1
|
|
2205
|
-
`).get(memoryId
|
|
2137
|
+
`).get(memoryId) as HubMemorySearchRow | undefined;
|
|
2206
2138
|
return row ?? null;
|
|
2207
2139
|
}
|
|
2208
2140
|
|
|
2209
2141
|
listVisibleHubMemories(userId: string, limit = 40): Array<{ id: string; sourceChunkId: string; sourceUserId: string; role: string; summary: string; kind: string; groupId: string | null; groupName: string | null; visibility: string; ownerName: string; createdAt: number; updatedAt: number }> {
|
|
2210
2142
|
const rows = this.db.prepare(`
|
|
2211
|
-
SELECT m.*, u.username AS owner_name,
|
|
2143
|
+
SELECT m.*, u.username AS owner_name, NULL AS group_name
|
|
2212
2144
|
FROM hub_memories m
|
|
2213
2145
|
LEFT JOIN hub_users u ON u.id = m.source_user_id
|
|
2214
|
-
LEFT JOIN hub_groups g ON g.id = m.group_id
|
|
2215
|
-
WHERE m.visibility = 'public'
|
|
2216
|
-
OR EXISTS (
|
|
2217
|
-
SELECT 1 FROM hub_group_members gm
|
|
2218
|
-
WHERE gm.group_id = m.group_id AND gm.user_id = ?
|
|
2219
|
-
)
|
|
2220
2146
|
ORDER BY m.updated_at DESC
|
|
2221
2147
|
LIMIT ?
|
|
2222
|
-
`).all(
|
|
2148
|
+
`).all(limit) as any[];
|
|
2223
2149
|
return rows.map(r => ({
|
|
2224
2150
|
id: r.id, sourceChunkId: r.source_chunk_id, sourceUserId: r.source_user_id,
|
|
2225
2151
|
role: r.role, summary: r.summary, kind: r.kind,
|