@memtensor/memos-local-openclaw-plugin 1.0.4-beta.4 → 1.0.4-beta.6

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.
Files changed (152) hide show
  1. package/README.md +22 -39
  2. package/dist/capture/index.d.ts.map +1 -1
  3. package/dist/capture/index.js +6 -0
  4. package/dist/capture/index.js.map +1 -1
  5. package/dist/config.d.ts +1 -2
  6. package/dist/config.d.ts.map +1 -1
  7. package/dist/config.js +3 -72
  8. package/dist/config.js.map +1 -1
  9. package/dist/embedding/index.d.ts +2 -4
  10. package/dist/embedding/index.d.ts.map +1 -1
  11. package/dist/embedding/index.js +1 -17
  12. package/dist/embedding/index.js.map +1 -1
  13. package/dist/index.d.ts +0 -2
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +3 -4
  16. package/dist/index.js.map +1 -1
  17. package/dist/ingest/providers/index.d.ts +2 -10
  18. package/dist/ingest/providers/index.d.ts.map +1 -1
  19. package/dist/ingest/providers/index.js +43 -209
  20. package/dist/ingest/providers/index.js.map +1 -1
  21. package/dist/ingest/providers/openai.d.ts +0 -1
  22. package/dist/ingest/providers/openai.d.ts.map +1 -1
  23. package/dist/ingest/providers/openai.js +0 -1
  24. package/dist/ingest/providers/openai.js.map +1 -1
  25. package/dist/ingest/task-processor.js +1 -1
  26. package/dist/ingest/task-processor.js.map +1 -1
  27. package/dist/recall/engine.js +1 -1
  28. package/dist/recall/engine.js.map +1 -1
  29. package/dist/shared/llm-call.d.ts +2 -4
  30. package/dist/shared/llm-call.d.ts.map +1 -1
  31. package/dist/shared/llm-call.js +81 -20
  32. package/dist/shared/llm-call.js.map +1 -1
  33. package/dist/skill/evaluator.d.ts.map +1 -1
  34. package/dist/skill/evaluator.js +2 -2
  35. package/dist/skill/evaluator.js.map +1 -1
  36. package/dist/skill/evolver.d.ts +2 -0
  37. package/dist/skill/evolver.d.ts.map +1 -1
  38. package/dist/skill/evolver.js +3 -0
  39. package/dist/skill/evolver.js.map +1 -1
  40. package/dist/skill/generator.d.ts.map +1 -1
  41. package/dist/skill/generator.js +4 -4
  42. package/dist/skill/generator.js.map +1 -1
  43. package/dist/skill/upgrader.js +1 -1
  44. package/dist/skill/upgrader.js.map +1 -1
  45. package/dist/skill/validator.js +1 -1
  46. package/dist/skill/validator.js.map +1 -1
  47. package/dist/storage/ensure-binding.d.ts.map +1 -1
  48. package/dist/storage/ensure-binding.js +1 -3
  49. package/dist/storage/ensure-binding.js.map +1 -1
  50. package/dist/storage/sqlite.d.ts +0 -294
  51. package/dist/storage/sqlite.d.ts.map +1 -1
  52. package/dist/storage/sqlite.js +0 -821
  53. package/dist/storage/sqlite.js.map +1 -1
  54. package/dist/telemetry.d.ts +12 -5
  55. package/dist/telemetry.d.ts.map +1 -1
  56. package/dist/telemetry.js +135 -38
  57. package/dist/telemetry.js.map +1 -1
  58. package/dist/tools/index.d.ts +0 -1
  59. package/dist/tools/index.d.ts.map +1 -1
  60. package/dist/tools/index.js +1 -3
  61. package/dist/tools/index.js.map +1 -1
  62. package/dist/tools/memory-search.d.ts +2 -3
  63. package/dist/tools/memory-search.d.ts.map +1 -1
  64. package/dist/tools/memory-search.js +7 -48
  65. package/dist/tools/memory-search.js.map +1 -1
  66. package/dist/types.d.ts +2 -49
  67. package/dist/types.d.ts.map +1 -1
  68. package/dist/types.js.map +1 -1
  69. package/dist/viewer/html.d.ts.map +1 -1
  70. package/dist/viewer/html.js +471 -2974
  71. package/dist/viewer/html.js.map +1 -1
  72. package/dist/viewer/server.d.ts +0 -45
  73. package/dist/viewer/server.d.ts.map +1 -1
  74. package/dist/viewer/server.js +18 -1155
  75. package/dist/viewer/server.js.map +1 -1
  76. package/index.ts +42 -430
  77. package/openclaw.plugin.json +1 -2
  78. package/package.json +3 -4
  79. package/scripts/postinstall.cjs +46 -283
  80. package/skill/memos-memory-guide/SKILL.md +2 -26
  81. package/src/capture/index.ts +8 -0
  82. package/src/config.ts +3 -94
  83. package/src/embedding/index.ts +1 -21
  84. package/src/index.ts +4 -7
  85. package/src/ingest/providers/index.ts +46 -246
  86. package/src/ingest/providers/openai.ts +1 -1
  87. package/src/ingest/task-processor.ts +1 -1
  88. package/src/recall/engine.ts +1 -1
  89. package/src/shared/llm-call.ts +95 -23
  90. package/src/skill/evaluator.ts +2 -3
  91. package/src/skill/evolver.ts +5 -0
  92. package/src/skill/generator.ts +4 -6
  93. package/src/skill/upgrader.ts +1 -1
  94. package/src/skill/validator.ts +1 -1
  95. package/src/storage/ensure-binding.ts +1 -3
  96. package/src/storage/sqlite.ts +0 -1085
  97. package/src/telemetry.ts +152 -39
  98. package/src/tools/index.ts +0 -1
  99. package/src/tools/memory-search.ts +8 -57
  100. package/src/types.ts +2 -44
  101. package/src/viewer/html.ts +471 -2974
  102. package/src/viewer/server.ts +21 -1070
  103. package/dist/client/connector.d.ts +0 -30
  104. package/dist/client/connector.d.ts.map +0 -1
  105. package/dist/client/connector.js +0 -219
  106. package/dist/client/connector.js.map +0 -1
  107. package/dist/client/hub.d.ts +0 -61
  108. package/dist/client/hub.d.ts.map +0 -1
  109. package/dist/client/hub.js +0 -148
  110. package/dist/client/hub.js.map +0 -1
  111. package/dist/client/skill-sync.d.ts +0 -29
  112. package/dist/client/skill-sync.d.ts.map +0 -1
  113. package/dist/client/skill-sync.js +0 -216
  114. package/dist/client/skill-sync.js.map +0 -1
  115. package/dist/hub/auth.d.ts +0 -19
  116. package/dist/hub/auth.d.ts.map +0 -1
  117. package/dist/hub/auth.js +0 -70
  118. package/dist/hub/auth.js.map +0 -1
  119. package/dist/hub/server.d.ts +0 -41
  120. package/dist/hub/server.d.ts.map +0 -1
  121. package/dist/hub/server.js +0 -747
  122. package/dist/hub/server.js.map +0 -1
  123. package/dist/hub/user-manager.d.ts +0 -29
  124. package/dist/hub/user-manager.d.ts.map +0 -1
  125. package/dist/hub/user-manager.js +0 -125
  126. package/dist/hub/user-manager.js.map +0 -1
  127. package/dist/openclaw-api.d.ts +0 -53
  128. package/dist/openclaw-api.d.ts.map +0 -1
  129. package/dist/openclaw-api.js +0 -189
  130. package/dist/openclaw-api.js.map +0 -1
  131. package/dist/sharing/types.contract.d.ts +0 -2
  132. package/dist/sharing/types.contract.d.ts.map +0 -1
  133. package/dist/sharing/types.contract.js +0 -3
  134. package/dist/sharing/types.contract.js.map +0 -1
  135. package/dist/sharing/types.d.ts +0 -80
  136. package/dist/sharing/types.d.ts.map +0 -1
  137. package/dist/sharing/types.js +0 -3
  138. package/dist/sharing/types.js.map +0 -1
  139. package/dist/tools/network-memory-detail.d.ts +0 -4
  140. package/dist/tools/network-memory-detail.d.ts.map +0 -1
  141. package/dist/tools/network-memory-detail.js +0 -34
  142. package/dist/tools/network-memory-detail.js.map +0 -1
  143. package/src/client/connector.ts +0 -218
  144. package/src/client/hub.ts +0 -189
  145. package/src/client/skill-sync.ts +0 -202
  146. package/src/hub/auth.ts +0 -78
  147. package/src/hub/server.ts +0 -740
  148. package/src/hub/user-manager.ts +0 -139
  149. package/src/openclaw-api.ts +0 -287
  150. package/src/sharing/types.contract.ts +0 -40
  151. package/src/sharing/types.ts +0 -102
  152. package/src/tools/network-memory-detail.ts +0 -34
@@ -1,34 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createNetworkMemoryDetailTool = createNetworkMemoryDetailTool;
4
- const hub_1 = require("../client/hub");
5
- function createNetworkMemoryDetailTool(store, ctx) {
6
- return {
7
- name: "network_memory_detail",
8
- description: "Fetch the full detail for one Hub search hit using its remoteHitId. Use this after memory_search(scope=group|all) when you need the full shared content.",
9
- inputSchema: {
10
- type: "object",
11
- properties: {
12
- remoteHitId: {
13
- type: "string",
14
- description: "The remoteHitId returned by memory_search hub results.",
15
- },
16
- hubAddress: {
17
- type: "string",
18
- description: "Optional hub address override for integration tests or manual routing.",
19
- },
20
- userToken: {
21
- type: "string",
22
- description: "Optional hub bearer token override for integration tests.",
23
- },
24
- },
25
- required: ["remoteHitId"],
26
- },
27
- handler: async (input) => (0, hub_1.hubGetMemoryDetail)(store, ctx, {
28
- remoteHitId: String(input.remoteHitId ?? ""),
29
- hubAddress: input.hubAddress,
30
- userToken: input.userToken,
31
- }),
32
- };
33
- }
34
- //# sourceMappingURL=network-memory-detail.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"network-memory-detail.js","sourceRoot":"","sources":["../../src/tools/network-memory-detail.ts"],"names":[],"mappings":";;AAIA,sEA6BC;AAjCD,uCAAmD;AAInD,SAAgB,6BAA6B,CAAC,KAAkB,EAAE,GAAkB;IAClF,OAAO;QACL,IAAI,EAAE,uBAAuB;QAC7B,WAAW,EACT,0JAA0J;QAC5J,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,WAAW,EAAE;oBACX,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,wDAAwD;iBACtE;gBACD,UAAU,EAAE;oBACV,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,wEAAwE;iBACtF;gBACD,SAAS,EAAE;oBACT,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,2DAA2D;iBACzE;aACF;YACD,QAAQ,EAAE,CAAC,aAAa,CAAC;SAC1B;QACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,IAAA,wBAAkB,EAAC,KAAK,EAAE,GAAG,EAAE;YACvD,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC;YAC5C,UAAU,EAAE,KAAK,CAAC,UAAgC;YAClD,SAAS,EAAE,KAAK,CAAC,SAA+B;SACjD,CAAC;KACH,CAAC;AACJ,CAAC"}
@@ -1,218 +0,0 @@
1
- import type { Logger, MemosLocalConfig } from "../types";
2
- import type { GroupInfo, UserRole, UserStatus } from "../sharing/types";
3
- import type { SqliteStore } from "../storage/sqlite";
4
- import { hubRequestJson, normalizeHubUrl } from "./hub";
5
-
6
- export interface HubSessionInfo {
7
- hubUrl: string;
8
- userId: string;
9
- username: string;
10
- userToken: string;
11
- role: UserRole;
12
- connectedAt: number;
13
- }
14
-
15
- export interface HubStatusInfo {
16
- connected: boolean;
17
- hubUrl?: string;
18
- user: null | {
19
- id: string;
20
- username: string;
21
- role: UserRole;
22
- status: UserStatus | string;
23
- groups: GroupInfo[];
24
- };
25
- }
26
-
27
- export async function connectToHub(store: SqliteStore, config: MemosLocalConfig, log?: Logger): Promise<HubSessionInfo> {
28
- const hubAddress = config.sharing?.client?.hubAddress ?? "";
29
- let userToken = config.sharing?.client?.userToken ?? "";
30
-
31
- if (!userToken) {
32
- const persisted = store.getClientHubConnection();
33
- if (persisted?.userToken) userToken = persisted.userToken;
34
- }
35
-
36
- if (!userToken && config.sharing?.client?.teamToken) {
37
- if (!log) throw new Error("hub client connection is not configured (no userToken, has teamToken but no logger for auto-join)");
38
- return autoJoinHub(store, config, log);
39
- }
40
-
41
- if (!hubAddress || !userToken) {
42
- throw new Error("hub client connection is not configured");
43
- }
44
-
45
- const hubUrl = normalizeHubUrl(hubAddress);
46
- const me = await hubRequestJson(hubUrl, userToken, "/api/v1/hub/me", { method: "GET" }) as any;
47
- store.setClientHubConnection({
48
- hubUrl,
49
- userId: String(me.id),
50
- username: String(me.username ?? ""),
51
- userToken,
52
- role: String(me.role ?? "member") as UserRole,
53
- connectedAt: Date.now(),
54
- });
55
- return store.getClientHubConnection()!;
56
- }
57
-
58
- export async function getHubStatus(store: SqliteStore, config: MemosLocalConfig): Promise<HubStatusInfo> {
59
- const conn = store.getClientHubConnection();
60
- const hubAddress = conn?.hubUrl || config.sharing?.client?.hubAddress || "";
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
-
130
- if (!hubAddress || !userToken) {
131
- return { connected: false, user: null };
132
- }
133
-
134
- try {
135
- const me = await hubRequestJson(normalizeHubUrl(hubAddress), userToken, "/api/v1/hub/me", { method: "GET" }) as any;
136
- return {
137
- connected: true,
138
- hubUrl: normalizeHubUrl(hubAddress),
139
- user: {
140
- id: String(me.id),
141
- username: String(me.username ?? ""),
142
- role: String(me.role ?? "member") as UserRole,
143
- status: String(me.status ?? "active"),
144
- groups: Array.isArray(me.groups)
145
- ? me.groups.map((group: any) => ({
146
- id: String(group.id),
147
- name: String(group.name),
148
- description: typeof group.description === "string" ? group.description : undefined,
149
- }))
150
- : [],
151
- },
152
- };
153
- } catch {
154
- return { connected: false, user: null };
155
- }
156
- }
157
-
158
- export async function autoJoinHub(
159
- store: SqliteStore,
160
- config: MemosLocalConfig,
161
- log: Logger,
162
- ): Promise<HubSessionInfo> {
163
- const hubAddress = config.sharing?.client?.hubAddress ?? "";
164
- const teamToken = config.sharing?.client?.teamToken ?? "";
165
- if (!hubAddress || !teamToken) {
166
- throw new Error("hubAddress and teamToken are required for auto-join");
167
- }
168
- const hubUrl = normalizeHubUrl(hubAddress);
169
- const hostname = typeof globalThis.process !== "undefined" ? (await import("os")).hostname() : "unknown";
170
- const username = typeof globalThis.process !== "undefined" ? (await import("os")).userInfo().username : "user";
171
-
172
- log.info(`Joining Hub at ${hubUrl} as "${username}"...`);
173
- const result = await hubRequestJson(hubUrl, "", "/api/v1/hub/join", {
174
- method: "POST",
175
- body: JSON.stringify({ teamToken, username, deviceName: hostname }),
176
- }) as any;
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
-
195
- if (!result.userToken) {
196
- throw new Error(`Hub join failed: ${JSON.stringify(result)}`);
197
- }
198
-
199
- log.info(`Joined Hub successfully! userId=${result.userId}`);
200
- store.setClientHubConnection({
201
- hubUrl,
202
- userId: String(result.userId),
203
- username,
204
- userToken: result.userToken,
205
- role: "member",
206
- connectedAt: Date.now(),
207
- });
208
- return store.getClientHubConnection()!;
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/client/hub.ts DELETED
@@ -1,189 +0,0 @@
1
- import type { PluginContext } from "../types";
2
- import type { SqliteStore } from "../storage/sqlite";
3
- import type { HubMemoryDetail, HubScope, HubSearchResult, HubSkillSearchResult } from "../sharing/types";
4
-
5
- export interface ResolvedHubClient {
6
- hubUrl: string;
7
- userToken: string;
8
- userId: string;
9
- username: string;
10
- role: string;
11
- }
12
-
13
- export async function resolveHubClient(store: SqliteStore, ctx: PluginContext, overrides?: { hubAddress?: string; userToken?: string }): Promise<ResolvedHubClient> {
14
- const persisted = store.getClientHubConnection() as any;
15
- if (persisted?.hubUrl && persisted?.userToken) {
16
- return {
17
- hubUrl: normalizeHubUrl(String(persisted.hubUrl)),
18
- userToken: String(persisted.userToken),
19
- userId: String(persisted.userId),
20
- username: String(persisted.username ?? ""),
21
- role: String(persisted.role ?? "member"),
22
- };
23
- }
24
-
25
- const hubAddress = overrides?.hubAddress ?? ctx.config.sharing?.client?.hubAddress ?? "";
26
- const userToken = overrides?.userToken ?? ctx.config.sharing?.client?.userToken ?? "";
27
- if (!hubAddress || !userToken) {
28
- throw new Error("hub client connection is not configured");
29
- }
30
-
31
- const hubUrl = normalizeHubUrl(hubAddress);
32
- const me = await hubRequestJson(hubUrl, userToken, "/api/v1/hub/me", { method: "GET" }) as any;
33
-
34
- return {
35
- hubUrl,
36
- userToken,
37
- userId: String(me.id),
38
- username: String(me.username ?? ""),
39
- role: String(me.role ?? "member"),
40
- };
41
- }
42
-
43
- export async function hubListMemories(
44
- store: SqliteStore,
45
- ctx: PluginContext,
46
- input?: { limit?: number; hubAddress?: string; userToken?: string },
47
- ): Promise<{ memories: Array<any> }> {
48
- const client = await resolveHubClient(store, ctx, { hubAddress: input?.hubAddress, userToken: input?.userToken });
49
- const url = new URL(`${client.hubUrl}/api/v1/hub/memories`);
50
- if (input?.limit != null) url.searchParams.set("limit", String(input.limit));
51
- return hubRequestJson(url.origin, client.userToken, `${url.pathname}${url.search}`, {
52
- method: "GET",
53
- }) as Promise<{ memories: Array<any> }>;
54
- }
55
-
56
- export async function hubListTasks(
57
- store: SqliteStore,
58
- ctx: PluginContext,
59
- input?: { limit?: number; hubAddress?: string; userToken?: string },
60
- ): Promise<{ tasks: Array<any> }> {
61
- const client = await resolveHubClient(store, ctx, { hubAddress: input?.hubAddress, userToken: input?.userToken });
62
- const url = new URL(`${client.hubUrl}/api/v1/hub/tasks`);
63
- if (input?.limit != null) url.searchParams.set("limit", String(input.limit));
64
- return hubRequestJson(url.origin, client.userToken, `${url.pathname}${url.search}`, {
65
- method: "GET",
66
- }) as Promise<{ tasks: Array<any> }>;
67
- }
68
-
69
- export async function hubListSkills(
70
- store: SqliteStore,
71
- ctx: PluginContext,
72
- input?: { limit?: number; hubAddress?: string; userToken?: string },
73
- ): Promise<{ skills: Array<any> }> {
74
- const client = await resolveHubClient(store, ctx, { hubAddress: input?.hubAddress, userToken: input?.userToken });
75
- const url = new URL(`${client.hubUrl}/api/v1/hub/skills/list`);
76
- if (input?.limit != null) url.searchParams.set("limit", String(input.limit));
77
- return hubRequestJson(url.origin, client.userToken, `${url.pathname}${url.search}`, {
78
- method: "GET",
79
- }) as Promise<{ skills: Array<any> }>;
80
- }
81
-
82
- export async function hubSearchMemories(
83
- store: SqliteStore,
84
- ctx: PluginContext,
85
- input: { query: string; maxResults?: number; scope?: HubScope; hubAddress?: string; userToken?: string },
86
- ): Promise<HubSearchResult> {
87
- const client = await resolveHubClient(store, ctx, { hubAddress: input.hubAddress, userToken: input.userToken });
88
- return hubRequestJson(client.hubUrl, client.userToken, "/api/v1/hub/search", {
89
- method: "POST",
90
- body: JSON.stringify({
91
- query: input.query,
92
- maxResults: input.maxResults,
93
- scope: input.scope,
94
- }),
95
- }) as Promise<HubSearchResult>;
96
- }
97
-
98
-
99
- export async function hubSearchSkills(
100
- store: SqliteStore,
101
- ctx: PluginContext,
102
- input: { query: string; maxResults?: number; hubAddress?: string; userToken?: string },
103
- ): Promise<HubSkillSearchResult> {
104
- const client = await resolveHubClient(store, ctx, { hubAddress: input.hubAddress, userToken: input.userToken });
105
- const url = new URL(`${client.hubUrl}/api/v1/hub/skills`);
106
- url.searchParams.set("query", input.query);
107
- if (input.maxResults != null) url.searchParams.set("maxResults", String(input.maxResults));
108
- return hubRequestJson(url.origin, client.userToken, `${url.pathname}${url.search}`, {
109
- method: "GET",
110
- }) as Promise<HubSkillSearchResult>;
111
- }
112
-
113
- export async function hubGetMemoryDetail(
114
- store: SqliteStore,
115
- ctx: PluginContext,
116
- input: { remoteHitId: string; hubAddress?: string; userToken?: string },
117
- ): Promise<HubMemoryDetail> {
118
- const client = await resolveHubClient(store, ctx, { hubAddress: input.hubAddress, userToken: input.userToken });
119
- const detail = await hubRequestJson(client.hubUrl, client.userToken, "/api/v1/hub/memory-detail", {
120
- method: "POST",
121
- body: JSON.stringify({
122
- remoteHitId: input.remoteHitId,
123
- }),
124
- }) as Omit<HubMemoryDetail, "remoteHitId">;
125
-
126
- return {
127
- remoteHitId: input.remoteHitId,
128
- content: String(detail.content ?? ""),
129
- summary: String(detail.summary ?? ""),
130
- source: {
131
- ts: Number(detail.source?.ts ?? 0),
132
- role: String(detail.source?.role ?? "assistant") as any,
133
- },
134
- };
135
- }
136
-
137
- export async function hubUpdateUsername(
138
- store: SqliteStore,
139
- ctx: PluginContext,
140
- newUsername: string,
141
- ): Promise<{ ok: boolean; username: string; userToken: string }> {
142
- const client = await resolveHubClient(store, ctx);
143
- const result = await hubRequestJson(client.hubUrl, client.userToken, "/api/v1/hub/me/update-profile", {
144
- method: "POST",
145
- body: JSON.stringify({ username: newUsername }),
146
- }) as { ok: boolean; username: string; userToken: string };
147
- if (result.ok && result.userToken) {
148
- store.setClientHubConnection({
149
- hubUrl: client.hubUrl,
150
- userId: client.userId,
151
- username: result.username,
152
- userToken: result.userToken,
153
- role: client.role as "admin" | "member",
154
- connectedAt: Date.now(),
155
- });
156
- }
157
- return result;
158
- }
159
-
160
- export async function hubRequestJson(
161
- hubUrl: string,
162
- userToken: string,
163
- route: string,
164
- init: RequestInit = {},
165
- ): Promise<unknown> {
166
- const res = await fetch(`${normalizeHubUrl(hubUrl)}${route}`, {
167
- ...init,
168
- headers: {
169
- authorization: `Bearer ${userToken}`,
170
- ...(init.body ? { "content-type": "application/json" } : {}),
171
- ...(init.headers ?? {}),
172
- },
173
- });
174
-
175
- if (!res.ok) {
176
- const body = await res.text();
177
- throw new Error(`hub request failed (${res.status}): ${body || res.statusText}`);
178
- }
179
-
180
- if (res.status === 204) return null;
181
- return res.json();
182
- }
183
-
184
- export function normalizeHubUrl(hubAddress: string): string {
185
- const trimmed = hubAddress.trim().replace(/\/+$/, "");
186
- if (!trimmed) return "";
187
- if (/^https?:\/\//i.test(trimmed)) return trimmed;
188
- return `http://${trimmed}`;
189
- }
@@ -1,202 +0,0 @@
1
- import * as fs from "fs";
2
- import * as path from "path";
3
- import { randomUUID } from "crypto";
4
- import type { PluginContext, Skill, SkillVersion } from "../types";
5
- import type { SqliteStore } from "../storage/sqlite";
6
- import type { SkillGenerateOutput } from "../types";
7
- import type { SkillBundle } from "../sharing/types";
8
- import { resolveHubClient, hubRequestJson } from "./hub";
9
-
10
- export function buildSkillBundleForHub(store: SqliteStore, skillId: string): SkillBundle {
11
- const skill = store.getSkill(skillId);
12
- if (!skill) {
13
- throw new Error(`Skill not found: ${skillId}`);
14
- }
15
-
16
- const latestVersion = store.getLatestSkillVersion(skillId);
17
- const skillMd = readSkillMarkdown(skill.dirPath, latestVersion?.content ?? "");
18
-
19
- return {
20
- metadata: {
21
- id: skill.id,
22
- name: skill.name,
23
- description: skill.description,
24
- version: skill.version,
25
- qualityScore: skill.qualityScore,
26
- },
27
- bundle: {
28
- skill_md: skillMd,
29
- scripts: readCompanionFiles(path.join(skill.dirPath, "scripts")),
30
- references: readCompanionFiles(path.join(skill.dirPath, "references")),
31
- evals: readEvals(path.join(skill.dirPath, "evals", "evals.json")),
32
- } satisfies SkillGenerateOutput,
33
- };
34
- }
35
-
36
- function readSkillMarkdown(dirPath: string, fallback: string): string {
37
- const skillMdPath = path.join(dirPath, "SKILL.md");
38
- if (fs.existsSync(skillMdPath)) {
39
- return fs.readFileSync(skillMdPath, "utf8");
40
- }
41
- return fallback;
42
- }
43
-
44
- function readCompanionFiles(dirPath: string): Array<{ filename: string; content: string }> {
45
- if (!fs.existsSync(dirPath)) return [];
46
-
47
- const out: Array<{ filename: string; content: string }> = [];
48
- walkFiles(dirPath, dirPath, out);
49
- return out.sort((left, right) => left.filename.localeCompare(right.filename));
50
- }
51
-
52
- function walkFiles(rootDir: string, currentDir: string, out: Array<{ filename: string; content: string }>): void {
53
- for (const entry of fs.readdirSync(currentDir, { withFileTypes: true })) {
54
- const fullPath = path.join(currentDir, entry.name);
55
- if (entry.isDirectory()) {
56
- walkFiles(rootDir, fullPath, out);
57
- continue;
58
- }
59
- if (!entry.isFile()) continue;
60
- out.push({
61
- filename: path.relative(rootDir, fullPath).replace(/\\/g, "/"),
62
- content: fs.readFileSync(fullPath, "utf8"),
63
- });
64
- }
65
- }
66
-
67
- function readEvals(evalsPath: string): Array<{ id: number; prompt: string; expectations: string[] }> {
68
- if (!fs.existsSync(evalsPath)) return [];
69
- const raw = JSON.parse(fs.readFileSync(evalsPath, "utf8")) as { evals?: Array<{ id: number; prompt: string; expectations: string[] }> };
70
- return Array.isArray(raw.evals) ? raw.evals : [];
71
- }
72
-
73
-
74
- export async function publishSkillBundleToHub(
75
- store: SqliteStore,
76
- ctx: PluginContext,
77
- input: { skillId: string; visibility: "public" | "group"; groupId?: string; hubAddress?: string; userToken?: string },
78
- ): Promise<{ skillId: string; visibility: "public" | "group" }> {
79
- const bundle = buildSkillBundleForHub(store, input.skillId);
80
- const client = await resolveHubClient(store, ctx, { hubAddress: input.hubAddress, userToken: input.userToken });
81
- return hubRequestJson(client.hubUrl, client.userToken, "/api/v1/hub/skills/publish", {
82
- method: "POST",
83
- body: JSON.stringify({
84
- visibility: input.visibility,
85
- groupId: input.groupId,
86
- metadata: bundle.metadata,
87
- bundle: bundle.bundle,
88
- }),
89
- }) as Promise<{ skillId: string; visibility: "public" | "group" }>;
90
- }
91
-
92
- export async function fetchHubSkillBundle(
93
- store: SqliteStore,
94
- ctx: PluginContext,
95
- input: { skillId: string; hubAddress?: string; userToken?: string },
96
- ): Promise<SkillBundle & { skillId: string }> {
97
- const client = await resolveHubClient(store, ctx, { hubAddress: input.hubAddress, userToken: input.userToken });
98
- return hubRequestJson(client.hubUrl, client.userToken, `/api/v1/hub/skills/${encodeURIComponent(input.skillId)}/bundle`, {
99
- method: "GET",
100
- }) as Promise<SkillBundle & { skillId: string }>;
101
- }
102
-
103
- export function restoreSkillBundleFromHub(
104
- store: SqliteStore,
105
- ctx: PluginContext,
106
- payload: SkillBundle & { skillId?: string },
107
- ): { localSkillId: string; localName: string; dirPath: string } {
108
- validateBundle(payload.bundle);
109
-
110
- const skillsStoreDir = path.join(ctx.stateDir, "skills-store");
111
- fs.mkdirSync(skillsStoreDir, { recursive: true });
112
-
113
- const baseName = sanitizeName(payload.metadata.name) || `hub-skill-${(payload.skillId ?? payload.metadata.id).slice(0, 8)}`;
114
- const resolvedName = resolveLocalSkillName(store, baseName, payload.skillId ?? payload.metadata.id);
115
- const dirPath = path.join(skillsStoreDir, resolvedName);
116
- fs.mkdirSync(dirPath, { recursive: true });
117
- fs.writeFileSync(path.join(dirPath, "SKILL.md"), payload.bundle.skill_md, "utf8");
118
-
119
- writeCompanionFiles(dirPath, "scripts", payload.bundle.scripts);
120
- writeCompanionFiles(dirPath, "references", payload.bundle.references);
121
- if (payload.bundle.evals.length > 0) {
122
- const evalDir = path.join(dirPath, "evals");
123
- fs.mkdirSync(evalDir, { recursive: true });
124
- fs.writeFileSync(path.join(evalDir, "evals.json"), JSON.stringify({ skill_name: payload.metadata.name, evals: payload.bundle.evals }, null, 2), "utf8");
125
- }
126
-
127
- const now = Date.now();
128
- const localSkillId = randomUUID();
129
- const skill: Skill = {
130
- id: localSkillId,
131
- name: resolvedName,
132
- description: payload.metadata.description,
133
- version: payload.metadata.version,
134
- status: "active",
135
- tags: JSON.stringify(["hub-import"]),
136
- sourceType: "manual",
137
- dirPath,
138
- installed: 0,
139
- owner: "agent:main",
140
- visibility: "private",
141
- qualityScore: payload.metadata.qualityScore,
142
- createdAt: now,
143
- updatedAt: now,
144
- };
145
- const version: SkillVersion = {
146
- id: randomUUID(),
147
- skillId: localSkillId,
148
- version: payload.metadata.version,
149
- content: payload.bundle.skill_md,
150
- changelog: "Imported from hub",
151
- changeSummary: "Imported from hub",
152
- upgradeType: "create",
153
- sourceTaskId: null,
154
- metrics: "{}",
155
- qualityScore: payload.metadata.qualityScore,
156
- createdAt: now,
157
- };
158
-
159
- store.insertSkill(skill);
160
- store.insertSkillVersion(version);
161
- return { localSkillId, localName: resolvedName, dirPath };
162
- }
163
-
164
- function validateBundle(bundle: SkillGenerateOutput): void {
165
- const allowedExtensions = new Set([".md", ".ts", ".js", ".sh", ".json", ".yaml", ".yml", ".txt"]);
166
- const files = [...bundle.scripts, ...bundle.references];
167
- if (Buffer.byteLength(bundle.skill_md, "utf8") > 100 * 1024) throw new Error("SKILL.md exceeds size limit");
168
- if (files.length > 50) throw new Error("bundle contains too many files");
169
-
170
- let totalBytes = Buffer.byteLength(bundle.skill_md, "utf8");
171
- for (const file of files) {
172
- const name = file.filename;
173
- if (!name || path.isAbsolute(name) || name.startsWith("/") || name.includes("..")) throw new Error(`unsafe filename: ${name}`);
174
- if (!/^[A-Za-z0-9._/-]+$/.test(name)) throw new Error(`invalid filename: ${name}`);
175
- const ext = path.extname(name).toLowerCase();
176
- if (!allowedExtensions.has(ext)) throw new Error(`unsupported file type: ${name}`);
177
- const fileSize = Buffer.byteLength(file.content, "utf8");
178
- if (fileSize > 512 * 1024) throw new Error(`file exceeds size limit: ${name}`);
179
- totalBytes += fileSize;
180
- }
181
- if (totalBytes > 5 * 1024 * 1024) throw new Error("bundle exceeds size limit");
182
- }
183
-
184
- function writeCompanionFiles(dirPath: string, root: "scripts" | "references", files: Array<{ filename: string; content: string }>): void {
185
- if (files.length === 0) return;
186
- const rootDir = path.join(dirPath, root);
187
- fs.mkdirSync(rootDir, { recursive: true });
188
- for (const file of files) {
189
- const target = path.join(rootDir, file.filename);
190
- fs.mkdirSync(path.dirname(target), { recursive: true });
191
- fs.writeFileSync(target, file.content, "utf8");
192
- }
193
- }
194
-
195
- function sanitizeName(input: string): string {
196
- return input.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
197
- }
198
-
199
- function resolveLocalSkillName(store: SqliteStore, baseName: string, sourceId: string): string {
200
- if (!store.getSkillByName(baseName)) return baseName;
201
- return `${baseName}-hub-${sourceId.slice(0, 8)}`;
202
- }