rol-websocket-channel 1.0.0

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 (58) hide show
  1. package/MQTT-API.md +967 -0
  2. package/dist/index.js +430 -0
  3. package/dist/message-handler.js +327 -0
  4. package/dist/src/admin/cli.js +43 -0
  5. package/dist/src/admin/jsonrpc.js +60 -0
  6. package/dist/src/admin/lib/fs.js +30 -0
  7. package/dist/src/admin/lib/paths.js +46 -0
  8. package/dist/src/admin/methods/admin.js +60 -0
  9. package/dist/src/admin/methods/agents-extended.js +235 -0
  10. package/dist/src/admin/methods/index.js +69 -0
  11. package/dist/src/admin/methods/memory.js +360 -0
  12. package/dist/src/admin/methods/models-extended.js +107 -0
  13. package/dist/src/admin/methods/models.js +39 -0
  14. package/dist/src/admin/methods/sessions-extended.js +207 -0
  15. package/dist/src/admin/methods/sessions.js +64 -0
  16. package/dist/src/admin/methods/skills-extended.js +157 -0
  17. package/dist/src/admin/methods/skills-toggle.js +182 -0
  18. package/dist/src/admin/methods/skills.js +384 -0
  19. package/dist/src/admin/methods/system.js +178 -0
  20. package/dist/src/admin/methods/usage.js +1170 -0
  21. package/dist/src/admin/types.js +1 -0
  22. package/dist/src/mqtt/connection-manager.js +155 -0
  23. package/dist/src/mqtt/index.js +5 -0
  24. package/dist/src/mqtt/mqtt-client.js +86 -0
  25. package/dist/src/mqtt/types.js +2 -0
  26. package/dist/src/shared/context.js +24 -0
  27. package/dist/src/shared/wrapper.js +23 -0
  28. package/index.ts +514 -0
  29. package/message-handler.ts +415 -0
  30. package/openclaw.plugin.json +84 -0
  31. package/package.json +35 -0
  32. package/readme.md +32 -0
  33. package/src/admin/cli.ts +60 -0
  34. package/src/admin/jsonrpc.ts +88 -0
  35. package/src/admin/lib/fs.ts +35 -0
  36. package/src/admin/lib/paths.ts +61 -0
  37. package/src/admin/methods/admin.ts +95 -0
  38. package/src/admin/methods/agents-extended.ts +310 -0
  39. package/src/admin/methods/index.ts +103 -0
  40. package/src/admin/methods/memory.ts +546 -0
  41. package/src/admin/methods/models-extended.ts +191 -0
  42. package/src/admin/methods/models.ts +103 -0
  43. package/src/admin/methods/sessions-extended.ts +313 -0
  44. package/src/admin/methods/sessions.ts +122 -0
  45. package/src/admin/methods/skills-extended.ts +249 -0
  46. package/src/admin/methods/skills-toggle.ts +235 -0
  47. package/src/admin/methods/skills.ts +651 -0
  48. package/src/admin/methods/system.ts +203 -0
  49. package/src/admin/methods/usage.ts +1491 -0
  50. package/src/admin/types.ts +46 -0
  51. package/src/mqtt/connection-manager.ts +188 -0
  52. package/src/mqtt/index.ts +6 -0
  53. package/src/mqtt/mqtt-client.ts +119 -0
  54. package/src/mqtt/types.ts +36 -0
  55. package/src/shared/context.ts +33 -0
  56. package/src/shared/wrapper.ts +35 -0
  57. package/tsconfig.json +16 -0
  58. package/types/openclaw.d.ts +74 -0
@@ -0,0 +1,35 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+
4
+ export async function readJsonFile<T>(filePath: string): Promise<T> {
5
+ const raw = await fs.readFile(filePath, 'utf8');
6
+ return JSON.parse(raw) as T;
7
+ }
8
+
9
+ export async function writeJsonFile(filePath: string, data: any): Promise<void> {
10
+ const json = JSON.stringify(data, null, 2);
11
+ await fs.writeFile(filePath, json, 'utf8');
12
+ }
13
+
14
+ export async function pathExists(targetPath: string): Promise<boolean> {
15
+ try {
16
+ await fs.access(targetPath);
17
+ return true;
18
+ } catch {
19
+ return false;
20
+ }
21
+ }
22
+
23
+ export async function ensureDir(targetDir: string): Promise<void> {
24
+ await fs.mkdir(targetDir, { recursive: true });
25
+ }
26
+
27
+ export async function copyIfExists(sourcePath: string, destPath: string): Promise<boolean> {
28
+ if (!(await pathExists(sourcePath))) {
29
+ return false;
30
+ }
31
+
32
+ await ensureDir(path.dirname(destPath));
33
+ await fs.copyFile(sourcePath, destPath);
34
+ return true;
35
+ }
@@ -0,0 +1,61 @@
1
+ import path from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+
4
+ import { JsonRpcException, JSON_RPC_ERRORS } from '../jsonrpc.ts';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+
9
+ export function getProjectRoot(): string {
10
+ // __dirname 是 /path/to/rol-websocket-channel/src/admin/lib
11
+ // 需要返回 /path/to/rol-websocket-channel
12
+ const root = path.resolve(__dirname, '..', '..', '..');
13
+ console.log('[paths] __dirname:', __dirname);
14
+ console.log('[paths] getProjectRoot:', root);
15
+ return root;
16
+ }
17
+
18
+ export function getOpenClawRoot(): string {
19
+ if (process.env.OPENCLAW_HOME) {
20
+ console.log('[paths] getOpenClawRoot from env:', process.env.OPENCLAW_HOME);
21
+ return path.resolve(process.env.OPENCLAW_HOME);
22
+ }
23
+
24
+ // 插件在 ~/.openclaw/extensions/rol-websocket-channel
25
+ // 需要返回 ~/.openclaw
26
+ const projectRoot = getProjectRoot();
27
+ const parentDir = path.dirname(projectRoot);
28
+ const parentName = path.basename(parentDir);
29
+
30
+ console.log('[paths] projectRoot:', projectRoot);
31
+ console.log('[paths] parentDir:', parentDir);
32
+ console.log('[paths] parentName:', parentName);
33
+
34
+ if (parentName === 'extensions') {
35
+ // parentDir 是 ~/.openclaw/extensions
36
+ // 返回 ~/.openclaw
37
+ const openclawRoot = path.dirname(parentDir);
38
+ console.log('[paths] getOpenClawRoot (extensions):', openclawRoot);
39
+ return openclawRoot;
40
+ }
41
+
42
+ // 兜底:假设插件在 openclaw 根目录的子目录
43
+ const fallbackRoot = path.resolve(projectRoot, '..');
44
+ console.log('[paths] getOpenClawRoot (fallback):', fallbackRoot);
45
+ return fallbackRoot;
46
+ }
47
+
48
+ export function ensureInside(parentDir: string, targetPath: string): string {
49
+ const parent = path.resolve(parentDir);
50
+ const target = path.resolve(targetPath);
51
+
52
+ if (target !== parent && !target.startsWith(`${parent}${path.sep}`)) {
53
+ throw new JsonRpcException(
54
+ JSON_RPC_ERRORS.invalidParams,
55
+ 'Path escapes allowed directory',
56
+ { parent, target }
57
+ );
58
+ }
59
+
60
+ return target;
61
+ }
@@ -0,0 +1,95 @@
1
+ import path from 'node:path';
2
+
3
+ import { readJsonFile } from '../lib/fs.ts';
4
+ import type { JsonValue, MethodHandler } from '../types.ts';
5
+
6
+ interface OpenClawConfig {
7
+ agents?: {
8
+ defaults?: Record<string, JsonValue>;
9
+ list?: JsonValue[];
10
+ };
11
+ plugins?: {
12
+ allow?: string[];
13
+ entries?: Record<string, JsonValue>;
14
+ };
15
+ tools?: Record<string, JsonValue>;
16
+ gateway?: Record<string, JsonValue>;
17
+ session?: Record<string, JsonValue>;
18
+ models?: Record<string, JsonValue>;
19
+ skills?: Record<string, JsonValue>;
20
+ diagnostics?: Record<string, JsonValue>;
21
+ [key: string]: JsonValue | undefined;
22
+ }
23
+
24
+ export const getAgents: MethodHandler = async (_params, context): Promise<JsonValue> => {
25
+ const configPath = path.join(context.openclawRoot, 'openclaw.json');
26
+ const config = await readJsonFile<OpenClawConfig>(configPath);
27
+
28
+ return {
29
+ sourceConfigFile: configPath,
30
+ defaultAgentConfig: config.agents?.defaults ?? {},
31
+ namedAgents: config.agents?.list ?? [],
32
+ activeToolProfile: (config.tools?.profile as JsonValue | undefined) ?? null
33
+ };
34
+ };
35
+
36
+ export const getConfig: MethodHandler = async (params, context): Promise<JsonValue> => {
37
+ const configPath = path.join(context.openclawRoot, 'openclaw.json');
38
+ const config = await readJsonFile<OpenClawConfig>(configPath);
39
+ const objectParams = isObject(params) ? params : {};
40
+ const section = typeof objectParams.section === 'string' ? objectParams.section : null;
41
+
42
+ if (!section) {
43
+ return {
44
+ sourceConfigFile: configPath,
45
+ config: redactSecrets(config as unknown as JsonValue)
46
+ };
47
+ }
48
+
49
+ const value = config[section];
50
+ return {
51
+ sourceConfigFile: configPath,
52
+ section,
53
+ sectionValue: value === undefined ? null : redactSecrets(value)
54
+ };
55
+ };
56
+
57
+ function isObject(value: JsonValue | undefined): value is Record<string, JsonValue> {
58
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
59
+ }
60
+
61
+ function redactSecrets(value: JsonValue): JsonValue {
62
+ if (Array.isArray(value)) {
63
+ return value.map(redactSecrets);
64
+ }
65
+
66
+ if (!value || typeof value !== 'object') {
67
+ return value;
68
+ }
69
+
70
+ const result: Record<string, JsonValue> = {};
71
+
72
+ for (const [key, nestedValue] of Object.entries(value)) {
73
+ if (isSecretKey(key) && typeof nestedValue === 'string') {
74
+ result[key] = redactString(nestedValue);
75
+ continue;
76
+ }
77
+
78
+ result[key] = redactSecrets(nestedValue);
79
+ }
80
+
81
+ return result;
82
+ }
83
+
84
+ function isSecretKey(key: string): boolean {
85
+ const normalized = key.toLowerCase();
86
+ return normalized.includes('apikey') || normalized.includes('token') || normalized.includes('secret');
87
+ }
88
+
89
+ function redactString(value: string): string {
90
+ if (value.length <= 8) {
91
+ return '********';
92
+ }
93
+
94
+ return `${value.slice(0, 4)}***${value.slice(-4)}`;
95
+ }
@@ -0,0 +1,310 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+
4
+ import { pathExists, readJsonFile, writeJsonFile } from '../lib/fs.ts';
5
+ import { JsonRpcException, JSON_RPC_ERRORS } from '../jsonrpc.ts';
6
+ import type { JsonValue, MethodHandler } from '../types.ts';
7
+
8
+ interface OpenClawConfig {
9
+ agents?: {
10
+ defaults?: Record<string, any>;
11
+ list?: NamedAgentRecord[];
12
+ };
13
+ [key: string]: any;
14
+ }
15
+
16
+ interface NamedAgentRecord {
17
+ id: string;
18
+ name?: string;
19
+ workspace?: string;
20
+ agentDir?: string;
21
+ model?: Record<string, any>;
22
+ skills?: string[];
23
+ tools?: Record<string, any>;
24
+ behavior?: JsonValue;
25
+ [key: string]: any;
26
+ }
27
+
28
+ const ALLOWED_AGENT_UPDATES: Record<string, boolean> = {
29
+ 'name': true,
30
+ 'workspace': true,
31
+ 'agentDir': true,
32
+ 'model.primary': true,
33
+ 'model.provider': true,
34
+ 'skills': true,
35
+ 'tools.profile': true,
36
+ 'behavior': true,
37
+ };
38
+
39
+ export const listAgents: MethodHandler = async (_params, context): Promise<JsonValue> => {
40
+ const configPath = path.join(context.openclawRoot, 'openclaw.json');
41
+ const config = await loadConfig(context.openclawRoot);
42
+ const items = normalizeAgentList(config);
43
+
44
+ return {
45
+ sourceConfigFile: configPath,
46
+ count: items.length,
47
+ items
48
+ };
49
+ };
50
+
51
+ export const createAgent: MethodHandler = async (params, context): Promise<JsonValue> => {
52
+ const objectParams = expectObject(params);
53
+ const rawAgentId = typeof objectParams.agentId === 'string'
54
+ ? objectParams.agentId
55
+ : typeof objectParams.id === 'string'
56
+ ? objectParams.id
57
+ : undefined;
58
+ const agentId = sanitizeAgentId(expectString(rawAgentId, 'agentId'));
59
+ const configPath = path.join(context.openclawRoot, 'openclaw.json');
60
+ const config = await loadConfig(context.openclawRoot);
61
+ const agents = ensureAgentList(config);
62
+
63
+ if (findNamedAgent(agents, agentId)) {
64
+ throw new JsonRpcException(
65
+ JSON_RPC_ERRORS.invalidParams,
66
+ `Agent already exists: ${agentId}`
67
+ );
68
+ }
69
+
70
+ const workspace = typeof objectParams.workspace === 'string' && objectParams.workspace.trim().length > 0
71
+ ? objectParams.workspace.trim()
72
+ : path.join(context.openclawRoot, 'workspace', agentId);
73
+ const agentDir = typeof objectParams.agentDir === 'string' && objectParams.agentDir.trim().length > 0
74
+ ? objectParams.agentDir.trim()
75
+ : path.join(context.openclawRoot, 'agents', agentId, 'agent');
76
+
77
+ const created: NamedAgentRecord = {
78
+ id: agentId,
79
+ name: typeof objectParams.name === 'string' && objectParams.name.trim().length > 0
80
+ ? objectParams.name.trim()
81
+ : agentId,
82
+ workspace,
83
+ agentDir
84
+ };
85
+
86
+ if (typeof objectParams.modelPrimary === 'string' && objectParams.modelPrimary.trim().length > 0) {
87
+ created.model = { ...(created.model ?? {}), primary: objectParams.modelPrimary.trim() };
88
+ }
89
+ if (typeof objectParams.modelProvider === 'string' && objectParams.modelProvider.trim().length > 0) {
90
+ created.model = { ...(created.model ?? {}), provider: objectParams.modelProvider.trim() };
91
+ }
92
+ if (Array.isArray(objectParams.skills)) {
93
+ created.skills = objectParams.skills.filter((item): item is string => typeof item === 'string');
94
+ }
95
+ if (typeof objectParams.toolsProfile === 'string' && objectParams.toolsProfile.trim().length > 0) {
96
+ created.tools = { ...(created.tools ?? {}), profile: objectParams.toolsProfile.trim() };
97
+ }
98
+ if (isObject(objectParams.behavior)) {
99
+ created.behavior = objectParams.behavior;
100
+ }
101
+
102
+ agents.push(created);
103
+
104
+ await fs.mkdir(workspace, { recursive: true });
105
+ await fs.mkdir(agentDir, { recursive: true });
106
+ await writeJsonFile(configPath, config);
107
+
108
+ return {
109
+ success: true,
110
+ configFile: configPath,
111
+ created
112
+ };
113
+ };
114
+
115
+ export const deleteAgent: MethodHandler = async (params, context): Promise<JsonValue> => {
116
+ const objectParams = expectObject(params);
117
+ const rawAgentId = typeof objectParams.agentId === 'string'
118
+ ? objectParams.agentId
119
+ : typeof objectParams.id === 'string'
120
+ ? objectParams.id
121
+ : undefined;
122
+ const agentId = sanitizeAgentId(expectString(rawAgentId, 'agentId'));
123
+ const purgeFiles = objectParams.purgeFiles === true;
124
+
125
+ const configPath = path.join(context.openclawRoot, 'openclaw.json');
126
+ const config = await loadConfig(context.openclawRoot);
127
+ const agents = ensureAgentList(config);
128
+ const index = agents.findIndex((agent) => isMatchingAgent(agent, agentId));
129
+
130
+ if (index === -1) {
131
+ throw new JsonRpcException(
132
+ JSON_RPC_ERRORS.invalidParams,
133
+ `Agent not found: ${agentId}`
134
+ );
135
+ }
136
+
137
+ const [removed] = agents.splice(index, 1);
138
+ await writeJsonFile(configPath, config);
139
+
140
+ const removedPaths: string[] = [];
141
+ if (purgeFiles) {
142
+ const workspaceRoot = path.normalize(path.join(context.openclawRoot, 'workspace'));
143
+ const agentsRoot = path.normalize(path.join(context.openclawRoot, 'agents'));
144
+ const purgeCandidates = [
145
+ typeof removed.workspace === 'string' ? removed.workspace : null,
146
+ typeof removed.agentDir === 'string' ? path.dirname(removed.agentDir) : null
147
+ ].filter((value): value is string => Boolean(value));
148
+
149
+ for (const candidate of purgeCandidates) {
150
+ const normalized = path.normalize(candidate);
151
+ const insideWorkspace = normalized === workspaceRoot || normalized.startsWith(`${workspaceRoot}${path.sep}`);
152
+ const insideAgents = normalized === agentsRoot || normalized.startsWith(`${agentsRoot}${path.sep}`);
153
+ if (!insideWorkspace && !insideAgents) {
154
+ continue;
155
+ }
156
+ if (await pathExists(candidate)) {
157
+ await fs.rm(candidate, { recursive: true, force: true });
158
+ removedPaths.push(candidate);
159
+ }
160
+ }
161
+ }
162
+
163
+ return {
164
+ success: true,
165
+ configFile: configPath,
166
+ removed,
167
+ purgeFiles,
168
+ removedPaths
169
+ };
170
+ };
171
+
172
+ export const updateAgent: MethodHandler = async (params, context): Promise<JsonValue> => {
173
+ const objectParams = isObject(params) ? params : {};
174
+ const updates = isObject(objectParams.updates) ? objectParams.updates : null;
175
+ const rawAgentId = typeof objectParams.agentId === 'string'
176
+ ? objectParams.agentId
177
+ : typeof objectParams.agentName === 'string'
178
+ ? objectParams.agentName
179
+ : typeof objectParams.id === 'string'
180
+ ? objectParams.id
181
+ : 'defaults';
182
+ const agentId = typeof rawAgentId === 'string' && rawAgentId !== 'defaults'
183
+ ? sanitizeAgentId(rawAgentId)
184
+ : rawAgentId;
185
+
186
+ if (!updates) {
187
+ throw new JsonRpcException(
188
+ JSON_RPC_ERRORS.invalidParams,
189
+ 'Missing required parameter: updates (object with field paths and values)'
190
+ );
191
+ }
192
+
193
+ const configPath = path.join(context.openclawRoot, 'openclaw.json');
194
+ const config = await loadConfig(context.openclawRoot);
195
+ const target = resolveAgentTarget(config, agentId);
196
+ const changes: string[] = [];
197
+
198
+ for (const [fieldPath, value] of Object.entries(updates)) {
199
+ if (!ALLOWED_AGENT_UPDATES[fieldPath]) {
200
+ throw new JsonRpcException(
201
+ JSON_RPC_ERRORS.invalidParams,
202
+ `Field not allowed for update: ${fieldPath}. Allowed fields: ${Object.keys(ALLOWED_AGENT_UPDATES).join(', ')}`
203
+ );
204
+ }
205
+
206
+ setNestedValue(target, fieldPath, value);
207
+ changes.push(`Updated ${fieldPath} to: ${JSON.stringify(value)}`);
208
+ }
209
+
210
+ await writeJsonFile(configPath, config);
211
+
212
+ return {
213
+ success: true,
214
+ configFile: configPath,
215
+ agentId,
216
+ changes,
217
+ updatedConfig: target
218
+ };
219
+ };
220
+
221
+ function setNestedValue(obj: any, pathExpression: string, value: any): void {
222
+ const keys = pathExpression.split('.');
223
+ let current = obj;
224
+
225
+ for (let index = 0; index < keys.length - 1; index += 1) {
226
+ const key = keys[index];
227
+ if (!current[key] || typeof current[key] !== 'object') {
228
+ current[key] = {};
229
+ }
230
+ current = current[key];
231
+ }
232
+
233
+ current[keys[keys.length - 1]] = value;
234
+ }
235
+
236
+ function isObject(value: JsonValue | undefined): value is Record<string, JsonValue> {
237
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
238
+ }
239
+
240
+ function expectObject(value: JsonValue | undefined): Record<string, JsonValue> {
241
+ if (!value || Array.isArray(value) || typeof value !== 'object') {
242
+ throw new JsonRpcException(JSON_RPC_ERRORS.invalidParams, 'Params must be an object');
243
+ }
244
+ return value as Record<string, JsonValue>;
245
+ }
246
+
247
+ function expectString(value: JsonValue | undefined, fieldName: string): string {
248
+ if (typeof value !== 'string' || value.trim().length === 0) {
249
+ throw new JsonRpcException(
250
+ JSON_RPC_ERRORS.invalidParams,
251
+ `Field '${fieldName}' must be a non-empty string`
252
+ );
253
+ }
254
+ return value.trim();
255
+ }
256
+
257
+ async function loadConfig(openclawRoot: string): Promise<OpenClawConfig> {
258
+ return await readJsonFile<OpenClawConfig>(path.join(openclawRoot, 'openclaw.json'));
259
+ }
260
+
261
+ function ensureAgentList(config: OpenClawConfig): NamedAgentRecord[] {
262
+ if (!config.agents) config.agents = {};
263
+ if (!Array.isArray(config.agents.list)) config.agents.list = [];
264
+ return config.agents.list;
265
+ }
266
+
267
+ function normalizeAgentList(config: OpenClawConfig): NamedAgentRecord[] {
268
+ const defaults: NamedAgentRecord = {
269
+ id: 'defaults',
270
+ name: 'defaults',
271
+ ...(config.agents?.defaults ?? {})
272
+ };
273
+ const named = Array.isArray(config.agents?.list) ? config.agents.list : [];
274
+ return [defaults, ...named];
275
+ }
276
+
277
+ function resolveAgentTarget(config: OpenClawConfig, agentName: string): Record<string, any> {
278
+ if (agentName === 'defaults') {
279
+ if (!config.agents) config.agents = {};
280
+ if (!config.agents.defaults) config.agents.defaults = {};
281
+ return config.agents.defaults;
282
+ }
283
+
284
+ const agents = ensureAgentList(config);
285
+ const found = findNamedAgent(agents, agentName);
286
+ if (!found) {
287
+ throw new JsonRpcException(
288
+ JSON_RPC_ERRORS.invalidParams,
289
+ `Agent not found: ${agentName}`
290
+ );
291
+ }
292
+ return found;
293
+ }
294
+
295
+ function findNamedAgent(agents: NamedAgentRecord[], agentName: string): NamedAgentRecord | null {
296
+ return agents.find((agent) => isMatchingAgent(agent, agentName)) ?? null;
297
+ }
298
+
299
+ function isMatchingAgent(agent: NamedAgentRecord, agentName: string): boolean {
300
+ return agent.id === agentName || agent.name === agentName;
301
+ }
302
+
303
+ function sanitizeAgentId(value: string): string {
304
+ return value
305
+ .trim()
306
+ .replace(/[^a-zA-Z0-9._-]+/g, '-')
307
+ .replace(/-+/g, '-')
308
+ .replace(/^-|-$/g, '')
309
+ .toLowerCase();
310
+ }
@@ -0,0 +1,103 @@
1
+ import { JsonRpcException, JSON_RPC_ERRORS } from '../jsonrpc.ts';
2
+ import type { MethodHandler } from '../types.ts';
3
+ import { getAgents, getConfig } from './admin.ts';
4
+ import { createAgent, deleteAgent, listAgents, updateAgent } from './agents-extended.ts';
5
+ import {
6
+ backupMemory,
7
+ createMemoryBackupRecord,
8
+ exportMemoryZip,
9
+ getMemoryPresignedPost,
10
+ getMemoryFile,
11
+ importMemoryZip,
12
+ listMemoryFiles
13
+ } from './memory.ts';
14
+ import { getModels } from './models.ts';
15
+ import { updateModels } from './models-extended.ts';
16
+ import { listSessions } from './sessions.ts';
17
+ import { getSession, prepareMessage, attachSkill } from './sessions-extended.ts';
18
+ import {
19
+ installSkillFromClawHub,
20
+ installSkillFromNpm,
21
+ listInstalledSkills,
22
+ searchClawHubSkills,
23
+ updateSkillFromClawHub
24
+ } from './skills.ts';
25
+ import { getInstalledSkill, uninstallSkill } from './skills-extended.ts';
26
+ import { ping, restart, stop, doctorFix, logs } from './system.ts';
27
+ import {
28
+ getUsageBreakdown,
29
+ getUsagePageSummary,
30
+ getUsageSummary,
31
+ getUsageTimeseries
32
+ } from './usage.ts';
33
+
34
+ const methods = new Map<string, MethodHandler>([
35
+ // System
36
+ ['system.ping', ping],
37
+ ['system.restart', restart],
38
+ ['system.stop', stop],
39
+ ['system.doctorFix', doctorFix],
40
+ ['system.logs', logs],
41
+
42
+ // Agents
43
+ ['agents.get', getAgents],
44
+ ['agents.list', listAgents],
45
+ ['agents.create', createAgent],
46
+ ['agents.delete', deleteAgent],
47
+ ['agents.update', updateAgent],
48
+
49
+ // Config
50
+ ['config.get', getConfig],
51
+
52
+ // Sessions
53
+ ['sessions.list', listSessions],
54
+ ['sessions.get', getSession],
55
+ ['sessions.prepareMessage', prepareMessage],
56
+ ['sessions.attachSkill', attachSkill],
57
+
58
+ // Models
59
+ ['models.get', getModels],
60
+ ['models.update', updateModels],
61
+
62
+ // Usage
63
+ ['usage.summary', getUsageSummary],
64
+ ['adminBridge.usagePageSummary', getUsagePageSummary],
65
+ ['adminBridge.usageTimeseries', getUsageTimeseries],
66
+ ['adminBridge.usageBreakdown', getUsageBreakdown],
67
+ ['admin.usagePageSummary', getUsagePageSummary],
68
+ ['admin.usageTimeseries', getUsageTimeseries],
69
+ ['admin.usageBreakdown', getUsageBreakdown],
70
+
71
+ // Skills
72
+ ['skills.listInstalled', listInstalledSkills],
73
+ ['skills.installFromNpm', installSkillFromNpm],
74
+ ['skills.searchClawHub', searchClawHubSkills],
75
+ ['skills.installFromClawHub', installSkillFromClawHub],
76
+ ['skills.updateFromClawHub', updateSkillFromClawHub],
77
+ ['skills.getInstalled', getInstalledSkill],
78
+ ['skills.uninstall', uninstallSkill],
79
+
80
+ // Memory
81
+ ['memory.listFiles', listMemoryFiles],
82
+ ['memory.getFile', getMemoryFile],
83
+ ['memory.backup', backupMemory],
84
+ ['memory.exportZip', exportMemoryZip],
85
+ ['memory.getPresignedPost', getMemoryPresignedPost],
86
+ ['memory.createBackupRecord', createMemoryBackupRecord],
87
+ ['memory.importZip', importMemoryZip]
88
+ ]);
89
+
90
+ export function getMethod(methodName: string): MethodHandler {
91
+ const handler = methods.get(methodName);
92
+ if (!handler) {
93
+ throw new JsonRpcException(JSON_RPC_ERRORS.methodNotFound, 'Method not found', {
94
+ method: methodName
95
+ });
96
+ }
97
+
98
+ return handler;
99
+ }
100
+
101
+ export function listMethods(): string[] {
102
+ return [...methods.keys()];
103
+ }