rol-websocket-channel 1.0.7 → 1.0.9
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/MQTT-API /345/256/232/346/227/266/344/273/273/345/212/241/346/233/264/346/226/260.md" +1656 -0
- package/index.ts +10 -0
- package/message-handler.ts +106 -0
- package/openclaw.plugin.json +21 -0
- package/package.json +1 -1
- package/src/admin/methods/cron.ts +327 -0
- package/src/admin/methods/index.ts +30 -0
- package/src/admin/methods/pairing.ts +54 -13
package/index.ts
CHANGED
|
@@ -99,11 +99,13 @@ const WebSocketChannel: ChannelPlugin<WebSocketChannelAccount> = {
|
|
|
99
99
|
authToken: { type: "string" },
|
|
100
100
|
},
|
|
101
101
|
},
|
|
102
|
+
pairingEndpoint: { type: "string" },
|
|
102
103
|
config: {
|
|
103
104
|
type: "object",
|
|
104
105
|
additionalProperties: false,
|
|
105
106
|
properties: {
|
|
106
107
|
enabled: { type: "boolean" },
|
|
108
|
+
pairingEndpoint: { type: "string" },
|
|
107
109
|
mqttUrl: { type: "string" },
|
|
108
110
|
mqttTopic: { type: "string" },
|
|
109
111
|
groupPolicy: {
|
|
@@ -133,10 +135,18 @@ const WebSocketChannel: ChannelPlugin<WebSocketChannelAccount> = {
|
|
|
133
135
|
label: "API Core Bot",
|
|
134
136
|
description: "Backend API endpoint and auth token used by the plugin",
|
|
135
137
|
},
|
|
138
|
+
pairingEndpoint: {
|
|
139
|
+
label: "Pairing Endpoint",
|
|
140
|
+
description: "Optional pairing API endpoint or base URL for staging/local environments",
|
|
141
|
+
},
|
|
136
142
|
config: {
|
|
137
143
|
label: "Configuration",
|
|
138
144
|
description: "MQTT connection configuration",
|
|
139
145
|
},
|
|
146
|
+
"config.pairingEndpoint": {
|
|
147
|
+
label: "Pairing Endpoint",
|
|
148
|
+
description: "Optional pairing API endpoint or base URL for staging/local environments",
|
|
149
|
+
},
|
|
140
150
|
"config.enabled": {
|
|
141
151
|
label: "Enabled",
|
|
142
152
|
description: "Enable this configuration",
|
package/message-handler.ts
CHANGED
|
@@ -7,6 +7,21 @@ import { getContext } from './src/shared/context.js';
|
|
|
7
7
|
import { wrapAdminCall } from './src/shared/wrapper.js';
|
|
8
8
|
import { getAgents, getConfig } from './src/admin/methods/admin.js';
|
|
9
9
|
import { createAgent, deleteAgent, listAgents, updateAgent } from './src/admin/methods/agents-extended.js';
|
|
10
|
+
import {
|
|
11
|
+
addCron,
|
|
12
|
+
disableCron,
|
|
13
|
+
enableCron,
|
|
14
|
+
getCronStatus,
|
|
15
|
+
listCron,
|
|
16
|
+
listCronRuns,
|
|
17
|
+
renameCron,
|
|
18
|
+
removeCron,
|
|
19
|
+
rescheduleCron,
|
|
20
|
+
runCron,
|
|
21
|
+
setCronContent,
|
|
22
|
+
updateCronMessage,
|
|
23
|
+
updateCronSystemEvent
|
|
24
|
+
} from './src/admin/methods/cron.js';
|
|
10
25
|
import { listSessions } from './src/admin/methods/sessions.js';
|
|
11
26
|
import { getSession, prepareMessage, attachSkill } from './src/admin/methods/sessions-extended.js';
|
|
12
27
|
import { getModels } from './src/admin/methods/models.js';
|
|
@@ -103,6 +118,97 @@ export class MessageHandler {
|
|
|
103
118
|
});
|
|
104
119
|
}
|
|
105
120
|
|
|
121
|
+
async cronList(data: any): Promise<any> {
|
|
122
|
+
return wrapAdminCall(async () => {
|
|
123
|
+
const context = getContext();
|
|
124
|
+
return await listCron(data, context);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async cronStatus(data: any): Promise<any> {
|
|
129
|
+
return wrapAdminCall(async () => {
|
|
130
|
+
const context = getContext();
|
|
131
|
+
return await getCronStatus(data, context);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async cronRuns(data: any): Promise<any> {
|
|
136
|
+
return wrapAdminCall(async () => {
|
|
137
|
+
const context = getContext();
|
|
138
|
+
return await listCronRuns(data, context);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async cronAdd(data: any): Promise<any> {
|
|
143
|
+
return wrapAdminCall(async () => {
|
|
144
|
+
const context = getContext();
|
|
145
|
+
return await addCron(data, context);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async cronRename(data: any): Promise<any> {
|
|
150
|
+
return wrapAdminCall(async () => {
|
|
151
|
+
const context = getContext();
|
|
152
|
+
return await renameCron(data, context);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async cronReschedule(data: any): Promise<any> {
|
|
157
|
+
return wrapAdminCall(async () => {
|
|
158
|
+
const context = getContext();
|
|
159
|
+
return await rescheduleCron(data, context);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async cronSetContent(data: any): Promise<any> {
|
|
164
|
+
return wrapAdminCall(async () => {
|
|
165
|
+
const context = getContext();
|
|
166
|
+
return await setCronContent(data, context);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async cronUpdateMessage(data: any): Promise<any> {
|
|
171
|
+
return wrapAdminCall(async () => {
|
|
172
|
+
const context = getContext();
|
|
173
|
+
return await updateCronMessage(data, context);
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async cronUpdateSystemEvent(data: any): Promise<any> {
|
|
178
|
+
return wrapAdminCall(async () => {
|
|
179
|
+
const context = getContext();
|
|
180
|
+
return await updateCronSystemEvent(data, context);
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async cronEnable(data: any): Promise<any> {
|
|
185
|
+
return wrapAdminCall(async () => {
|
|
186
|
+
const context = getContext();
|
|
187
|
+
return await enableCron(data, context);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async cronDisable(data: any): Promise<any> {
|
|
192
|
+
return wrapAdminCall(async () => {
|
|
193
|
+
const context = getContext();
|
|
194
|
+
return await disableCron(data, context);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async cronRun(data: any): Promise<any> {
|
|
199
|
+
return wrapAdminCall(async () => {
|
|
200
|
+
const context = getContext();
|
|
201
|
+
return await runCron(data, context);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async cronRemove(data: any): Promise<any> {
|
|
206
|
+
return wrapAdminCall(async () => {
|
|
207
|
+
const context = getContext();
|
|
208
|
+
return await removeCron(data, context);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
106
212
|
/**
|
|
107
213
|
* 列出 sessions
|
|
108
214
|
*/
|
package/openclaw.plugin.json
CHANGED
|
@@ -55,6 +55,9 @@
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
},
|
|
58
|
+
"pairingEndpoint": {
|
|
59
|
+
"type": "string"
|
|
60
|
+
},
|
|
58
61
|
"channels": {
|
|
59
62
|
"type": "object",
|
|
60
63
|
"additionalProperties": false,
|
|
@@ -99,6 +102,9 @@
|
|
|
99
102
|
}
|
|
100
103
|
}
|
|
101
104
|
},
|
|
105
|
+
"pairingEndpoint": {
|
|
106
|
+
"type": "string"
|
|
107
|
+
},
|
|
102
108
|
"dmPolicy": {
|
|
103
109
|
"type": "string",
|
|
104
110
|
"enum": ["pairing", "allowlist", "open", "disabled"]
|
|
@@ -116,6 +122,9 @@
|
|
|
116
122
|
"enabled": {
|
|
117
123
|
"type": "boolean"
|
|
118
124
|
},
|
|
125
|
+
"pairingEndpoint": {
|
|
126
|
+
"type": "string"
|
|
127
|
+
},
|
|
119
128
|
"mqttUrl": {
|
|
120
129
|
"type": "string"
|
|
121
130
|
},
|
|
@@ -149,6 +158,10 @@
|
|
|
149
158
|
"label": "API Core Bot",
|
|
150
159
|
"description": "Backend API endpoint and auth token used by the plugin"
|
|
151
160
|
},
|
|
161
|
+
"pairingEndpoint": {
|
|
162
|
+
"label": "Pairing Endpoint",
|
|
163
|
+
"description": "Optional pairing API endpoint or base URL for staging/local environments"
|
|
164
|
+
},
|
|
152
165
|
"dmPolicy": {
|
|
153
166
|
"label": "DM Policy",
|
|
154
167
|
"description": "Pairing/allowlist/open policy for direct messages"
|
|
@@ -175,6 +188,10 @@
|
|
|
175
188
|
"label": "API Core Bot",
|
|
176
189
|
"description": "Backend API endpoint and auth token used by the plugin"
|
|
177
190
|
},
|
|
191
|
+
"channels.rol-websocket-channel.pairingEndpoint": {
|
|
192
|
+
"label": "Pairing Endpoint",
|
|
193
|
+
"description": "Optional pairing API endpoint or base URL for staging/local environments"
|
|
194
|
+
},
|
|
178
195
|
"channels.rol-websocket-channel.dmPolicy": {
|
|
179
196
|
"label": "DM Policy",
|
|
180
197
|
"description": "Pairing/allowlist/open policy for direct messages"
|
|
@@ -191,6 +208,10 @@
|
|
|
191
208
|
"label": "Enabled",
|
|
192
209
|
"description": "Enable this configuration"
|
|
193
210
|
},
|
|
211
|
+
"channels.rol-websocket-channel.config.pairingEndpoint": {
|
|
212
|
+
"label": "Pairing Endpoint",
|
|
213
|
+
"description": "Optional pairing API endpoint or base URL for staging/local environments"
|
|
214
|
+
},
|
|
194
215
|
"channels.rol-websocket-channel.config.mqttUrl": {
|
|
195
216
|
"label": "MQTT Broker URL",
|
|
196
217
|
"placeholder": "ws://192.168.1.152:8083/mqtt",
|
package/package.json
CHANGED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
|
|
4
|
+
import { JsonRpcException, JSON_RPC_ERRORS } from '../jsonrpc.js';
|
|
5
|
+
import type { JsonValue, MethodContext, MethodHandler } from '../types.js';
|
|
6
|
+
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
8
|
+
|
|
9
|
+
type CronSession = 'main' | 'isolated';
|
|
10
|
+
type CronScheduleKey = 'at' | 'every' | 'cron';
|
|
11
|
+
|
|
12
|
+
export const listCron: MethodHandler = async (_params, context): Promise<JsonValue> => {
|
|
13
|
+
return await runOpenClawCronCommand(['cron', 'list'], context);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const getCronStatus: MethodHandler = async (_params, context): Promise<JsonValue> => {
|
|
17
|
+
return await runOpenClawCronCommand(['cron', 'status'], context);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const listCronRuns: MethodHandler = async (params, context): Promise<JsonValue> => {
|
|
21
|
+
const objectParams = expectObject(params);
|
|
22
|
+
const id = expectString(objectParams.id, 'id');
|
|
23
|
+
const args = ['cron', 'runs', '--id', id];
|
|
24
|
+
appendOptionalPositiveInteger(args, '--limit', objectParams.limit, 'limit');
|
|
25
|
+
return await runOpenClawCronCommand(args, context);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const addCron: MethodHandler = async (params, context): Promise<JsonValue> => {
|
|
29
|
+
const objectParams = expectObject(params);
|
|
30
|
+
const name = expectString(objectParams.name, 'name');
|
|
31
|
+
const scheduleKey = expectSingleSchedule(objectParams);
|
|
32
|
+
const scheduleValue = expectString(objectParams[scheduleKey], scheduleKey);
|
|
33
|
+
const session = expectSession(objectParams.session);
|
|
34
|
+
const contentKey = expectContentForSession(objectParams, session);
|
|
35
|
+
const contentValue = expectString(objectParams[contentKey], contentKey);
|
|
36
|
+
|
|
37
|
+
const args = [
|
|
38
|
+
'cron',
|
|
39
|
+
'add',
|
|
40
|
+
'--name',
|
|
41
|
+
name,
|
|
42
|
+
`--${scheduleKey}`,
|
|
43
|
+
scheduleValue,
|
|
44
|
+
'--session',
|
|
45
|
+
session,
|
|
46
|
+
`--${toKebabCase(contentKey)}`,
|
|
47
|
+
contentValue
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
appendOptionalString(args, '--description', objectParams.description);
|
|
51
|
+
appendOptionalString(args, '--agent', objectParams.agent);
|
|
52
|
+
appendOptionalString(args, '--model', objectParams.model);
|
|
53
|
+
appendOptionalString(args, '--thinking', objectParams.thinking);
|
|
54
|
+
appendOptionalString(args, '--channel', objectParams.channel);
|
|
55
|
+
appendOptionalString(args, '--to', objectParams.to);
|
|
56
|
+
appendOptionalString(args, '--tz', objectParams.tz);
|
|
57
|
+
appendOptionalPositiveInteger(args, '--timeout', objectParams.timeout, 'timeout');
|
|
58
|
+
appendOptionalTools(args, objectParams.tools);
|
|
59
|
+
appendOptionalBooleanFlag(args, '--announce', objectParams.announce);
|
|
60
|
+
appendOptionalBooleanFlag(args, '--disabled', objectParams.disabled);
|
|
61
|
+
|
|
62
|
+
return await runOpenClawCronCommand(args, context);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const renameCron: MethodHandler = async (params, context): Promise<JsonValue> => {
|
|
66
|
+
const objectParams = expectObject(params);
|
|
67
|
+
const id = expectString(objectParams.id, 'id');
|
|
68
|
+
const args = ['cron', 'edit', id];
|
|
69
|
+
appendOptionalString(args, '--name', objectParams.name);
|
|
70
|
+
appendOptionalString(args, '--description', objectParams.description);
|
|
71
|
+
assertHasPatchArgs(args, 3, "At least one field is required: 'name' or 'description'");
|
|
72
|
+
return await runOpenClawCronCommand(args, context);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const rescheduleCron: MethodHandler = async (params, context): Promise<JsonValue> => {
|
|
76
|
+
const objectParams = expectObject(params);
|
|
77
|
+
const id = expectString(objectParams.id, 'id');
|
|
78
|
+
const scheduleKey = expectSingleSchedule(objectParams);
|
|
79
|
+
const scheduleValue = expectString(objectParams[scheduleKey], scheduleKey);
|
|
80
|
+
const args = ['cron', 'edit', id, `--${scheduleKey}`, scheduleValue];
|
|
81
|
+
appendOptionalString(args, '--tz', objectParams.tz);
|
|
82
|
+
return await runOpenClawCronCommand(args, context);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const setCronContent: MethodHandler = async (params, context): Promise<JsonValue> => {
|
|
86
|
+
const objectParams = expectObject(params);
|
|
87
|
+
const id = expectString(objectParams.id, 'id');
|
|
88
|
+
const session = expectSession(objectParams.session);
|
|
89
|
+
const contentKey = expectContentForSession(objectParams, session);
|
|
90
|
+
const contentValue = expectString(objectParams[contentKey], contentKey);
|
|
91
|
+
const args = [
|
|
92
|
+
'cron',
|
|
93
|
+
'edit',
|
|
94
|
+
id,
|
|
95
|
+
'--session',
|
|
96
|
+
session,
|
|
97
|
+
`--${toKebabCase(contentKey)}`,
|
|
98
|
+
contentValue
|
|
99
|
+
];
|
|
100
|
+
return await runOpenClawCronCommand(args, context);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export const updateCronMessage: MethodHandler = async (params, context): Promise<JsonValue> => {
|
|
104
|
+
const objectParams = expectObject(params);
|
|
105
|
+
const id = expectString(objectParams.id, 'id');
|
|
106
|
+
const message = expectString(objectParams.message, 'message');
|
|
107
|
+
return await runOpenClawCronCommand([
|
|
108
|
+
'cron',
|
|
109
|
+
'edit',
|
|
110
|
+
id,
|
|
111
|
+
'--session',
|
|
112
|
+
'isolated',
|
|
113
|
+
'--message',
|
|
114
|
+
message
|
|
115
|
+
], context);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const updateCronSystemEvent: MethodHandler = async (params, context): Promise<JsonValue> => {
|
|
119
|
+
const objectParams = expectObject(params);
|
|
120
|
+
const id = expectString(objectParams.id, 'id');
|
|
121
|
+
const systemEvent = expectString(objectParams.systemEvent, 'systemEvent');
|
|
122
|
+
return await runOpenClawCronCommand([
|
|
123
|
+
'cron',
|
|
124
|
+
'edit',
|
|
125
|
+
id,
|
|
126
|
+
'--session',
|
|
127
|
+
'main',
|
|
128
|
+
'--system-event',
|
|
129
|
+
systemEvent
|
|
130
|
+
], context);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export const enableCron: MethodHandler = async (params, context): Promise<JsonValue> => {
|
|
134
|
+
return await runCronById('enable', params, context);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export const disableCron: MethodHandler = async (params, context): Promise<JsonValue> => {
|
|
138
|
+
return await runCronById('disable', params, context);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export const runCron: MethodHandler = async (params, context): Promise<JsonValue> => {
|
|
142
|
+
return await runCronById('run', params, context);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export const removeCron: MethodHandler = async (params, context): Promise<JsonValue> => {
|
|
146
|
+
return await runCronById('rm', params, context);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
async function runCronById(commandName: string, params: JsonValue | undefined, context: MethodContext): Promise<JsonValue> {
|
|
150
|
+
const objectParams = expectObject(params);
|
|
151
|
+
const id = expectString(objectParams.id, 'id');
|
|
152
|
+
return await runOpenClawCronCommand(['cron', commandName, id], context);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function runOpenClawCronCommand(args: string[], context: MethodContext): Promise<JsonValue> {
|
|
156
|
+
const command = process.env.OPENCLAW_BIN || 'openclaw';
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
const { stdout, stderr } = await execFileAsync(command, args, { cwd: context.projectRoot });
|
|
160
|
+
return {
|
|
161
|
+
ok: true,
|
|
162
|
+
command,
|
|
163
|
+
args,
|
|
164
|
+
stdout,
|
|
165
|
+
stderr,
|
|
166
|
+
parsed: parseJsonOutput(stdout)
|
|
167
|
+
};
|
|
168
|
+
} catch (err: any) {
|
|
169
|
+
throw new JsonRpcException(
|
|
170
|
+
JSON_RPC_ERRORS.internalError,
|
|
171
|
+
`OpenClaw cron command failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
172
|
+
{
|
|
173
|
+
command,
|
|
174
|
+
args,
|
|
175
|
+
stdout: typeof err?.stdout === 'string' ? err.stdout : '',
|
|
176
|
+
stderr: typeof err?.stderr === 'string' ? err.stderr : ''
|
|
177
|
+
}
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function expectObject(value: JsonValue | undefined): Record<string, JsonValue> {
|
|
183
|
+
if (!value || Array.isArray(value) || typeof value !== 'object') {
|
|
184
|
+
throw new JsonRpcException(JSON_RPC_ERRORS.invalidParams, 'Params must be an object');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return value as Record<string, JsonValue>;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function expectString(value: JsonValue | undefined, fieldName: string): string {
|
|
191
|
+
if (typeof value !== 'string' || value.trim().length === 0) {
|
|
192
|
+
throw new JsonRpcException(
|
|
193
|
+
JSON_RPC_ERRORS.invalidParams,
|
|
194
|
+
`Field '${fieldName}' must be a non-empty string`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return value.trim();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function expectSession(value: JsonValue | undefined): CronSession {
|
|
202
|
+
const session = expectString(value, 'session');
|
|
203
|
+
if (session !== 'main' && session !== 'isolated') {
|
|
204
|
+
throw new JsonRpcException(
|
|
205
|
+
JSON_RPC_ERRORS.invalidParams,
|
|
206
|
+
"Field 'session' must be one of: main, isolated"
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return session;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function expectSingleSchedule(params: Record<string, JsonValue>): CronScheduleKey {
|
|
214
|
+
const keys: CronScheduleKey[] = ['at', 'every', 'cron'];
|
|
215
|
+
const present = keys.filter((key) => typeof params[key] === 'string' && String(params[key]).trim().length > 0);
|
|
216
|
+
if (present.length !== 1) {
|
|
217
|
+
throw new JsonRpcException(
|
|
218
|
+
JSON_RPC_ERRORS.invalidParams,
|
|
219
|
+
"Exactly one schedule field is required: 'at', 'every', or 'cron'"
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return present[0];
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function expectContentForSession(
|
|
227
|
+
params: Record<string, JsonValue>,
|
|
228
|
+
session: CronSession
|
|
229
|
+
): 'message' | 'systemEvent' {
|
|
230
|
+
const requiredKey = session === 'isolated' ? 'message' : 'systemEvent';
|
|
231
|
+
const forbiddenKey = session === 'isolated' ? 'systemEvent' : 'message';
|
|
232
|
+
|
|
233
|
+
if (typeof params[requiredKey] !== 'string' || String(params[requiredKey]).trim().length === 0) {
|
|
234
|
+
throw new JsonRpcException(
|
|
235
|
+
JSON_RPC_ERRORS.invalidParams,
|
|
236
|
+
`Field '${requiredKey}' is required when session is '${session}'`
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (typeof params[forbiddenKey] === 'string' && String(params[forbiddenKey]).trim().length > 0) {
|
|
241
|
+
throw new JsonRpcException(
|
|
242
|
+
JSON_RPC_ERRORS.invalidParams,
|
|
243
|
+
`Field '${forbiddenKey}' cannot be used when session is '${session}'`
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return requiredKey;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function appendOptionalString(args: string[], flag: string, value: JsonValue | undefined): void {
|
|
251
|
+
if (typeof value !== 'string') {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const trimmed = value.trim();
|
|
256
|
+
if (trimmed.length > 0) {
|
|
257
|
+
args.push(flag, trimmed);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function appendOptionalPositiveInteger(args: string[], flag: string, value: JsonValue | undefined, fieldName: string): void {
|
|
262
|
+
if (value === undefined || value === null) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (typeof value !== 'number' || !Number.isInteger(value) || value <= 0) {
|
|
267
|
+
throw new JsonRpcException(
|
|
268
|
+
JSON_RPC_ERRORS.invalidParams,
|
|
269
|
+
`Field '${fieldName}' must be a positive integer`
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
args.push(flag, String(value));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function appendOptionalBooleanFlag(args: string[], flag: string, value: JsonValue | undefined): void {
|
|
277
|
+
if (value === true) {
|
|
278
|
+
args.push(flag);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function appendOptionalTools(args: string[], value: JsonValue | undefined): void {
|
|
283
|
+
if (value === undefined || value === null) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (typeof value === 'string') {
|
|
288
|
+
const trimmed = value.trim();
|
|
289
|
+
if (trimmed.length > 0) {
|
|
290
|
+
args.push('--tools', trimmed);
|
|
291
|
+
}
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (Array.isArray(value) && value.every((item) => typeof item === 'string' && item.trim().length > 0)) {
|
|
296
|
+
args.push('--tools', value.map((item) => String(item).trim()).join(','));
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
throw new JsonRpcException(
|
|
301
|
+
JSON_RPC_ERRORS.invalidParams,
|
|
302
|
+
"Field 'tools' must be a non-empty string or string array"
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function assertHasPatchArgs(args: string[], baseLength: number, message: string): void {
|
|
307
|
+
if (args.length <= baseLength) {
|
|
308
|
+
throw new JsonRpcException(JSON_RPC_ERRORS.invalidParams, message);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function parseJsonOutput(stdout: string): JsonValue | null {
|
|
313
|
+
const trimmed = stdout.trim();
|
|
314
|
+
if (!trimmed) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
try {
|
|
319
|
+
return JSON.parse(trimmed) as JsonValue;
|
|
320
|
+
} catch {
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function toKebabCase(value: string): string {
|
|
326
|
+
return value.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
|
|
327
|
+
}
|
|
@@ -2,6 +2,21 @@ import { JsonRpcException, JSON_RPC_ERRORS } from '../jsonrpc.ts';
|
|
|
2
2
|
import type { MethodHandler } from '../types.ts';
|
|
3
3
|
import { getAgents, getConfig } from './admin.ts';
|
|
4
4
|
import { createAgent, deleteAgent, listAgents, updateAgent } from './agents-extended.ts';
|
|
5
|
+
import {
|
|
6
|
+
addCron,
|
|
7
|
+
disableCron,
|
|
8
|
+
enableCron,
|
|
9
|
+
getCronStatus,
|
|
10
|
+
listCron,
|
|
11
|
+
listCronRuns,
|
|
12
|
+
renameCron,
|
|
13
|
+
removeCron,
|
|
14
|
+
rescheduleCron,
|
|
15
|
+
runCron,
|
|
16
|
+
setCronContent,
|
|
17
|
+
updateCronMessage,
|
|
18
|
+
updateCronSystemEvent
|
|
19
|
+
} from './cron.ts';
|
|
5
20
|
import {
|
|
6
21
|
backupMemory,
|
|
7
22
|
createMemoryBackupRecord,
|
|
@@ -49,6 +64,21 @@ const methods = new Map<string, MethodHandler>([
|
|
|
49
64
|
|
|
50
65
|
// Config
|
|
51
66
|
['config.get', getConfig],
|
|
67
|
+
|
|
68
|
+
// Cron
|
|
69
|
+
['cron.list', listCron],
|
|
70
|
+
['cron.status', getCronStatus],
|
|
71
|
+
['cron.runs', listCronRuns],
|
|
72
|
+
['cron.add', addCron],
|
|
73
|
+
['cron.rename', renameCron],
|
|
74
|
+
['cron.reschedule', rescheduleCron],
|
|
75
|
+
['cron.setContent', setCronContent],
|
|
76
|
+
['cron.updateMessage', updateCronMessage],
|
|
77
|
+
['cron.updateSystemEvent', updateCronSystemEvent],
|
|
78
|
+
['cron.enable', enableCron],
|
|
79
|
+
['cron.disable', disableCron],
|
|
80
|
+
['cron.run', runCron],
|
|
81
|
+
['cron.remove', removeCron],
|
|
52
82
|
|
|
53
83
|
// Sessions
|
|
54
84
|
['sessions.list', listSessions],
|