@rivus/agent 0.1.0 → 0.3.1
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/README.md +18 -3
- package/dist/cli.js +504 -5
- package/dist/index.d.ts +11 -9
- package/dist/index.js +70 -33
- package/dist/rivus-daemon-cli.js +547 -520
- package/dist/rivus-plugin-testkit.js +2 -2
- package/examples/pi-feishu-deployment.bootstrap.ts +2 -2
- package/examples/rivus-starter.plugin.mjs +45 -0
- package/package.json +11 -6
package/dist/rivus-daemon-cli.js
CHANGED
|
@@ -6,6 +6,489 @@ import { Effect } from "effect";
|
|
|
6
6
|
import { open, readFile, realpath, stat } from "node:fs/promises";
|
|
7
7
|
import { createHash, randomUUID } from "node:crypto";
|
|
8
8
|
import { constants } from "node:fs";
|
|
9
|
+
//#region src/application/plugin/rivus-automation-runtime-definition.ts
|
|
10
|
+
function resolveRivusAutomationRuntimeDefinition(definition, requestedToolIds, requestedSkillIds) {
|
|
11
|
+
const toolIds = Object.freeze([...new Set(requestedToolIds)].sort());
|
|
12
|
+
const toolsById = new Map(definition.tools.map((tool) => [tool.id, tool]));
|
|
13
|
+
const tools = Object.freeze(toolIds.map((toolId) => {
|
|
14
|
+
const tool = toolsById.get(toolId);
|
|
15
|
+
if (!tool) throw new Error(`Automation Runtime requests ungranted tool: ${toolId}`);
|
|
16
|
+
return tool;
|
|
17
|
+
}));
|
|
18
|
+
const skillIds = Object.freeze([...new Set(requestedSkillIds)].sort());
|
|
19
|
+
const skillsById = new Map(definition.skills.map((skill) => [skill.id, skill]));
|
|
20
|
+
const skills = Object.freeze(skillIds.map((skillId) => {
|
|
21
|
+
const skill = skillsById.get(skillId);
|
|
22
|
+
if (!skill) throw new Error(`Automation Runtime requests ungranted skill: ${skillId}`);
|
|
23
|
+
return skill;
|
|
24
|
+
}));
|
|
25
|
+
return deepFreeze({
|
|
26
|
+
...definition,
|
|
27
|
+
memory: {
|
|
28
|
+
scopes: [],
|
|
29
|
+
tool: false
|
|
30
|
+
},
|
|
31
|
+
skillGrantSet: {
|
|
32
|
+
revision: createHash("sha256").update(JSON.stringify({
|
|
33
|
+
parentRevision: definition.skillGrantSet.revision,
|
|
34
|
+
skillIds
|
|
35
|
+
})).digest("hex"),
|
|
36
|
+
skillIds
|
|
37
|
+
},
|
|
38
|
+
skills,
|
|
39
|
+
toolGrantSet: {
|
|
40
|
+
revision: createHash("sha256").update(JSON.stringify({
|
|
41
|
+
parentRevision: definition.toolGrantSet.revision,
|
|
42
|
+
toolIds
|
|
43
|
+
})).digest("hex"),
|
|
44
|
+
toolIds
|
|
45
|
+
},
|
|
46
|
+
tools
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/application/plugin/rivus-plugin-loader.ts
|
|
51
|
+
var RivusPluginLoadError = class extends Error {
|
|
52
|
+
pluginId;
|
|
53
|
+
moduleSpecifier;
|
|
54
|
+
name = "RivusPluginLoadError";
|
|
55
|
+
constructor(pluginId, moduleSpecifier, message, options) {
|
|
56
|
+
super(message, options);
|
|
57
|
+
this.pluginId = pluginId;
|
|
58
|
+
this.moduleSpecifier = moduleSpecifier;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
async function loadRivusDeployment(options) {
|
|
62
|
+
validateRivusDeploymentManifest(options.manifest);
|
|
63
|
+
const catalog = createRivusPluginCatalog();
|
|
64
|
+
const pluginStatuses = [];
|
|
65
|
+
const statusByPlugin = /* @__PURE__ */ new Map();
|
|
66
|
+
for (const declaration of options.manifest.plugins) try {
|
|
67
|
+
const plugin = await resolvePluginExport(await options.loadModule({
|
|
68
|
+
deploymentRoot: options.deploymentRoot,
|
|
69
|
+
module: declaration.module,
|
|
70
|
+
pluginId: declaration.id
|
|
71
|
+
}));
|
|
72
|
+
if (plugin.manifest.id !== declaration.id) throw new Error(`plugin manifest id ${plugin.manifest.id} does not match declaration ${declaration.id}`);
|
|
73
|
+
catalog.registerPlugin(plugin);
|
|
74
|
+
const status = Object.freeze({
|
|
75
|
+
id: declaration.id,
|
|
76
|
+
module: declaration.module,
|
|
77
|
+
required: declaration.required,
|
|
78
|
+
status: "loaded",
|
|
79
|
+
version: plugin.manifest.version
|
|
80
|
+
});
|
|
81
|
+
pluginStatuses.push(status);
|
|
82
|
+
statusByPlugin.set(declaration.id, status);
|
|
83
|
+
} catch (cause) {
|
|
84
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
85
|
+
if (declaration.required) throw new RivusPluginLoadError(declaration.id, declaration.module, `required plugin ${declaration.id} failed to load: ${message}`, { cause });
|
|
86
|
+
const status = Object.freeze({
|
|
87
|
+
error: message,
|
|
88
|
+
id: declaration.id,
|
|
89
|
+
module: declaration.module,
|
|
90
|
+
required: false,
|
|
91
|
+
status: "failed"
|
|
92
|
+
});
|
|
93
|
+
pluginStatuses.push(status);
|
|
94
|
+
statusByPlugin.set(declaration.id, status);
|
|
95
|
+
}
|
|
96
|
+
const agentStatuses = [];
|
|
97
|
+
const definitions = [];
|
|
98
|
+
for (const agent of options.manifest.agents) {
|
|
99
|
+
const pluginStatus = statusByPlugin.get(agent.pluginId);
|
|
100
|
+
if (pluginStatus.status === "failed") {
|
|
101
|
+
agentStatuses.push(Object.freeze({
|
|
102
|
+
agentId: agent.agentId,
|
|
103
|
+
pluginId: agent.pluginId,
|
|
104
|
+
profileId: agent.profileId,
|
|
105
|
+
reason: `plugin ${agent.pluginId} is unavailable: ${pluginStatus.error}`,
|
|
106
|
+
status: "disabled"
|
|
107
|
+
}));
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
const definition = resolveRivusAgentDefinition(catalog, agent);
|
|
112
|
+
definitions.push(definition);
|
|
113
|
+
agentStatuses.push(Object.freeze({
|
|
114
|
+
agentId: agent.agentId,
|
|
115
|
+
definition,
|
|
116
|
+
pluginId: agent.pluginId,
|
|
117
|
+
profileId: agent.profileId,
|
|
118
|
+
status: "enabled"
|
|
119
|
+
}));
|
|
120
|
+
} catch (cause) {
|
|
121
|
+
const declaration = options.manifest.plugins.find(({ id }) => id === agent.pluginId);
|
|
122
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
123
|
+
if (declaration.required) throw new RivusPluginLoadError(declaration.id, declaration.module, `deployment ${agent.agentId} failed to resolve: ${message}`, { cause });
|
|
124
|
+
agentStatuses.push(Object.freeze({
|
|
125
|
+
agentId: agent.agentId,
|
|
126
|
+
pluginId: agent.pluginId,
|
|
127
|
+
profileId: agent.profileId,
|
|
128
|
+
reason: message,
|
|
129
|
+
status: "disabled"
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const agentStatusById = new Map(agentStatuses.map((agent) => [agent.agentId, agent]));
|
|
134
|
+
const automationTemplates = new Map(catalog.snapshot().automations.map((template) => [template.id, template]));
|
|
135
|
+
const automationDefinitions = [];
|
|
136
|
+
for (const automation of options.manifest.automations ?? []) {
|
|
137
|
+
const agent = agentStatusById.get(automation.agentId);
|
|
138
|
+
if (!agent || agent.status === "disabled" || !agent.definition) continue;
|
|
139
|
+
const template = automationTemplates.get(automation.templateId);
|
|
140
|
+
if (!template) throw new Error(`automation ${automation.id} references unknown template: ${automation.templateId}`);
|
|
141
|
+
if (template.pluginId !== agent.pluginId || template.profileId !== agent.profileId) throw new Error(`automation ${automation.id} template is not owned by agent profile ${agent.profileId}`);
|
|
142
|
+
automationDefinitions.push(deepFreeze({
|
|
143
|
+
...automation,
|
|
144
|
+
runtimeDefinition: resolveRivusAutomationRuntimeDefinition(agent.definition, template.requestedToolIds, template.requestedSkillIds),
|
|
145
|
+
template
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
148
|
+
return Object.freeze({
|
|
149
|
+
agents: Object.freeze(agentStatuses),
|
|
150
|
+
automationDefinitions: Object.freeze(automationDefinitions),
|
|
151
|
+
catalog,
|
|
152
|
+
definitions: Object.freeze(definitions),
|
|
153
|
+
manifest: options.manifest,
|
|
154
|
+
plugins: Object.freeze(pluginStatuses)
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
function validateRivusDeploymentManifest(manifest) {
|
|
158
|
+
const pluginIds = /* @__PURE__ */ new Set();
|
|
159
|
+
for (const plugin of manifest.plugins) {
|
|
160
|
+
if (pluginIds.has(plugin.id)) throw new Error(`duplicate plugin declaration: ${plugin.id}`);
|
|
161
|
+
validateModuleSpecifier(plugin.module);
|
|
162
|
+
pluginIds.add(plugin.id);
|
|
163
|
+
}
|
|
164
|
+
const agentIds = /* @__PURE__ */ new Set();
|
|
165
|
+
const agentById = /* @__PURE__ */ new Map();
|
|
166
|
+
for (const agent of manifest.agents) {
|
|
167
|
+
if (agentIds.has(agent.agentId)) throw new Error(`duplicate agent deployment: ${agent.agentId}`);
|
|
168
|
+
agentIds.add(agent.agentId);
|
|
169
|
+
if (!pluginIds.has(agent.pluginId)) throw new Error(`agent ${agent.agentId} references undeclared plugin: ${agent.pluginId}`);
|
|
170
|
+
agentById.set(agent.agentId, agent);
|
|
171
|
+
}
|
|
172
|
+
const automationIds = /* @__PURE__ */ new Set();
|
|
173
|
+
for (const automation of manifest.automations ?? []) {
|
|
174
|
+
if (automationIds.has(automation.id)) throw new Error(`duplicate automation binding: ${automation.id}`);
|
|
175
|
+
automationIds.add(automation.id);
|
|
176
|
+
const agent = agentById.get(automation.agentId);
|
|
177
|
+
if (!agent) throw new Error(`automation ${automation.id} references unknown agent: ${automation.agentId}`);
|
|
178
|
+
const endpoint = manifest.endpoints.find(({ id }) => id === automation.delivery.endpointId);
|
|
179
|
+
if (!endpoint) throw new Error(`automation ${automation.id} references unknown delivery endpoint: ${automation.delivery.endpointId}`);
|
|
180
|
+
if (endpoint.agentId !== agent.agentId) throw new Error(`automation ${automation.id} delivery endpoint is bound to a different agent`);
|
|
181
|
+
}
|
|
182
|
+
const endpointIds = /* @__PURE__ */ new Set();
|
|
183
|
+
const sessionNamespaces = /* @__PURE__ */ new Set();
|
|
184
|
+
for (const endpoint of manifest.endpoints) {
|
|
185
|
+
if (endpointIds.has(endpoint.id)) throw new Error(`duplicate endpoint binding: ${endpoint.id}`);
|
|
186
|
+
endpointIds.add(endpoint.id);
|
|
187
|
+
if (sessionNamespaces.has(endpoint.sessionNamespace)) throw new Error(`duplicate endpoint session namespace: ${endpoint.sessionNamespace}`);
|
|
188
|
+
sessionNamespaces.add(endpoint.sessionNamespace);
|
|
189
|
+
const agent = agentById.get(endpoint.agentId);
|
|
190
|
+
if (!agent) throw new Error(`endpoint ${endpoint.id} references unknown agent: ${endpoint.agentId}`);
|
|
191
|
+
if (!agent.endpointIds.includes(endpoint.id)) throw new Error(`endpoint ${endpoint.id} is not declared by agent ${endpoint.agentId}`);
|
|
192
|
+
}
|
|
193
|
+
for (const agent of manifest.agents) for (const endpointId of agent.endpointIds) {
|
|
194
|
+
const endpoint = manifest.endpoints.find(({ id }) => id === endpointId);
|
|
195
|
+
if (!endpoint) throw new Error(`agent ${agent.agentId} references unknown endpoint: ${endpointId}`);
|
|
196
|
+
if (endpoint.agentId !== agent.agentId) throw new Error(`endpoint ${endpointId} is bound to a different agent`);
|
|
197
|
+
}
|
|
198
|
+
const defaultAgent = agentById.get(manifest.defaultAgentId);
|
|
199
|
+
if (!defaultAgent) throw new Error(`default agent does not exist: ${manifest.defaultAgentId}`);
|
|
200
|
+
const defaultEndpoint = manifest.endpoints.find(({ id }) => id === manifest.defaultEndpointId);
|
|
201
|
+
if (!defaultEndpoint) throw new Error(`default endpoint does not exist: ${manifest.defaultEndpointId}`);
|
|
202
|
+
if (defaultEndpoint.agentId !== defaultAgent.agentId) throw new Error("default endpoint is not bound to the default agent");
|
|
203
|
+
if (!defaultEndpoint.enabled) throw new Error("default endpoint must be enabled");
|
|
204
|
+
}
|
|
205
|
+
function validateModuleSpecifier(moduleSpecifier) {
|
|
206
|
+
if (moduleSpecifier.trim() === "" || isAbsolute(moduleSpecifier) || /^[a-z][a-z+.-]*:/i.test(moduleSpecifier) || moduleSpecifier.includes("\0")) throw new Error(`invalid plugin module specifier: ${moduleSpecifier}`);
|
|
207
|
+
if ((moduleSpecifier.startsWith("./") || moduleSpecifier.startsWith("../")) && moduleSpecifier.split(/[\\/]/).includes("..")) throw new Error(`plugin module escapes deployment root: ${moduleSpecifier}`);
|
|
208
|
+
}
|
|
209
|
+
async function resolvePluginExport(module) {
|
|
210
|
+
const candidate = "default" in module ? module.default : module;
|
|
211
|
+
const plugin = typeof candidate === "function" ? await candidate() : candidate;
|
|
212
|
+
if (plugin === null || typeof plugin !== "object" || !("manifest" in plugin) || !("register" in plugin) || typeof plugin.register !== "function") throw new Error("plugin module default export is not a RivusPlugin or factory");
|
|
213
|
+
return plugin;
|
|
214
|
+
}
|
|
215
|
+
//#endregion
|
|
216
|
+
//#region src/infrastructure/config/local-env-file.ts
|
|
217
|
+
var LocalEnvFileError = class extends Error {
|
|
218
|
+
constructor(message) {
|
|
219
|
+
super(message);
|
|
220
|
+
this.name = "LocalEnvFileError";
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
async function loadMergedLocalEnvFile(filePath, overrideEnv) {
|
|
224
|
+
return mergeRivusDaemonEnv(await loadLocalEnvFile(filePath), overrideEnv);
|
|
225
|
+
}
|
|
226
|
+
async function loadLocalEnvFile(filePath) {
|
|
227
|
+
return parseLocalEnvFile(await readFile(filePath, "utf8"));
|
|
228
|
+
}
|
|
229
|
+
function parseLocalEnvFile(contents) {
|
|
230
|
+
const env = {};
|
|
231
|
+
const lines = contents.split(/\r?\n/);
|
|
232
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
233
|
+
const trimmed = lines[index].trim();
|
|
234
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
235
|
+
const match = (trimmed.startsWith("export ") ? trimmed.slice(7).trimStart() : trimmed).match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
|
|
236
|
+
if (!match) throw new LocalEnvFileError(`Invalid env file line ${index + 1}`);
|
|
237
|
+
const key = match[1];
|
|
238
|
+
const rawValue = match[2];
|
|
239
|
+
env[key] = parseEnvValue(rawValue, index + 1);
|
|
240
|
+
}
|
|
241
|
+
return env;
|
|
242
|
+
}
|
|
243
|
+
function mergeRivusDaemonEnv(fileEnv, overrideEnv) {
|
|
244
|
+
const merged = { ...fileEnv };
|
|
245
|
+
for (const [key, value] of Object.entries(overrideEnv)) if (value !== void 0) merged[key] = value;
|
|
246
|
+
return merged;
|
|
247
|
+
}
|
|
248
|
+
function parseEnvValue(rawValue, lineNumber) {
|
|
249
|
+
const value = rawValue.trim();
|
|
250
|
+
if (!value) return "";
|
|
251
|
+
if (value.startsWith("'")) {
|
|
252
|
+
if (!value.endsWith("'")) throw new LocalEnvFileError(`Invalid single-quoted env value on line ${lineNumber}`);
|
|
253
|
+
return value.slice(1, -1).replaceAll("'\\''", "'");
|
|
254
|
+
}
|
|
255
|
+
if (value.startsWith("\"")) {
|
|
256
|
+
if (!value.endsWith("\"")) throw new LocalEnvFileError(`Invalid double-quoted env value on line ${lineNumber}`);
|
|
257
|
+
return unescapeDoubleQuotedValue(value.slice(1, -1));
|
|
258
|
+
}
|
|
259
|
+
return value;
|
|
260
|
+
}
|
|
261
|
+
function unescapeDoubleQuotedValue(value) {
|
|
262
|
+
return value.replace(/\\(["\\nrt])/g, (_match, escaped) => {
|
|
263
|
+
switch (escaped) {
|
|
264
|
+
case "n": return "\n";
|
|
265
|
+
case "r": return "\r";
|
|
266
|
+
case "t": return " ";
|
|
267
|
+
default: return escaped;
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
//#endregion
|
|
272
|
+
//#region src/infrastructure/config/feishu-endpoint-credentials.ts
|
|
273
|
+
var FeishuEndpointCredentialError = class extends Error {
|
|
274
|
+
name = "FeishuEndpointCredentialError";
|
|
275
|
+
};
|
|
276
|
+
function resolveFeishuEndpointCredentials(credentialRef, env) {
|
|
277
|
+
if (!credentialRef.startsWith("env:")) throw new FeishuEndpointCredentialError("Feishu endpoint credentialRef must use env:<PREFIX>");
|
|
278
|
+
const prefix = credentialRef.slice(4);
|
|
279
|
+
if (!/^[A-Z][A-Z0-9_]*$/.test(prefix)) throw new FeishuEndpointCredentialError(`Invalid environment prefix in credentialRef: ${credentialRef}`);
|
|
280
|
+
return Object.freeze({
|
|
281
|
+
appId: required$1(env, `${prefix}_APP_ID`),
|
|
282
|
+
appSecret: required$1(env, `${prefix}_APP_SECRET`)
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
function required$1(env, variable) {
|
|
286
|
+
const value = env[variable]?.trim();
|
|
287
|
+
if (!value) throw new FeishuEndpointCredentialError(`${variable} is required`);
|
|
288
|
+
return value;
|
|
289
|
+
}
|
|
290
|
+
//#endregion
|
|
291
|
+
//#region src/infrastructure/config/rivus-deployment-manifest.ts
|
|
292
|
+
var RivusDeploymentManifestError = class extends Error {
|
|
293
|
+
manifestPath;
|
|
294
|
+
name = "RivusDeploymentManifestError";
|
|
295
|
+
constructor(manifestPath, message, options) {
|
|
296
|
+
super(message, options);
|
|
297
|
+
this.manifestPath = manifestPath;
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
async function loadRivusDeploymentManifest(manifestPath, options = {}) {
|
|
301
|
+
const maxBytes = options.maxBytes ?? 1024 * 1024;
|
|
302
|
+
try {
|
|
303
|
+
const metadata = await stat(manifestPath);
|
|
304
|
+
if (!metadata.isFile()) throw new Error("deployment manifest must be a regular file");
|
|
305
|
+
if (metadata.size > maxBytes) throw new Error(`deployment manifest exceeds ${maxBytes} byte limit`);
|
|
306
|
+
const bytes = await readFile(manifestPath);
|
|
307
|
+
const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
308
|
+
return parseManifest(JSON.parse(text));
|
|
309
|
+
} catch (cause) {
|
|
310
|
+
if (cause instanceof RivusDeploymentManifestError) throw cause;
|
|
311
|
+
throw new RivusDeploymentManifestError(manifestPath, `failed to load Rivus deployment manifest: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
function parseManifest(value) {
|
|
315
|
+
const root = record(value, "manifest");
|
|
316
|
+
exactKeys(root, [
|
|
317
|
+
"agents",
|
|
318
|
+
"automations",
|
|
319
|
+
"defaultAgentId",
|
|
320
|
+
"defaultEndpointId",
|
|
321
|
+
"endpoints",
|
|
322
|
+
"plugins"
|
|
323
|
+
], "manifest", ["automations"]);
|
|
324
|
+
const plugins = array(root.plugins, "manifest.plugins").map((entry, index) => {
|
|
325
|
+
const plugin = record(entry, `manifest.plugins[${index}]`);
|
|
326
|
+
exactKeys(plugin, [
|
|
327
|
+
"id",
|
|
328
|
+
"module",
|
|
329
|
+
"required"
|
|
330
|
+
], `manifest.plugins[${index}]`);
|
|
331
|
+
return Object.freeze({
|
|
332
|
+
id: string(plugin.id, `manifest.plugins[${index}].id`),
|
|
333
|
+
module: string(plugin.module, `manifest.plugins[${index}].module`),
|
|
334
|
+
required: boolean(plugin.required, `manifest.plugins[${index}].required`)
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
const agents = array(root.agents, "manifest.agents").map((entry, index) => {
|
|
338
|
+
const agent = record(entry, `manifest.agents[${index}]`);
|
|
339
|
+
exactKeys(agent, [
|
|
340
|
+
"agentId",
|
|
341
|
+
"endpointIds",
|
|
342
|
+
"memory",
|
|
343
|
+
"pluginId",
|
|
344
|
+
"profileId",
|
|
345
|
+
"skills",
|
|
346
|
+
"tools"
|
|
347
|
+
], `manifest.agents[${index}]`, ["memory"]);
|
|
348
|
+
const memory = agent.memory === void 0 ? void 0 : record(agent.memory, `manifest.agents[${index}].memory`);
|
|
349
|
+
if (memory) exactKeys(memory, ["scopes", "tool"], `manifest.agents[${index}].memory`);
|
|
350
|
+
const skills = record(agent.skills, `manifest.agents[${index}].skills`);
|
|
351
|
+
exactKeys(skills, ["allow"], `manifest.agents[${index}].skills`);
|
|
352
|
+
const tools = record(agent.tools, `manifest.agents[${index}].tools`);
|
|
353
|
+
exactKeys(tools, ["allow"], `manifest.agents[${index}].tools`);
|
|
354
|
+
return Object.freeze({
|
|
355
|
+
agentId: string(agent.agentId, `manifest.agents[${index}].agentId`),
|
|
356
|
+
endpointIds: Object.freeze(array(agent.endpointIds, `manifest.agents[${index}].endpointIds`).map((item, itemIndex) => string(item, `manifest.agents[${index}].endpointIds[${itemIndex}]`))),
|
|
357
|
+
...memory ? { memory: Object.freeze({
|
|
358
|
+
scopes: Object.freeze(array(memory.scopes, `manifest.agents[${index}].memory.scopes`).map((item, itemIndex) => memoryScope(item, `manifest.agents[${index}].memory.scopes[${itemIndex}]`))),
|
|
359
|
+
tool: boolean(memory.tool, `manifest.agents[${index}].memory.tool`)
|
|
360
|
+
}) } : {},
|
|
361
|
+
pluginId: string(agent.pluginId, `manifest.agents[${index}].pluginId`),
|
|
362
|
+
profileId: string(agent.profileId, `manifest.agents[${index}].profileId`),
|
|
363
|
+
skills: Object.freeze({ allow: Object.freeze(array(skills.allow, `manifest.agents[${index}].skills.allow`).map((item, itemIndex) => string(item, `manifest.agents[${index}].skills.allow[${itemIndex}]`))) }),
|
|
364
|
+
tools: Object.freeze({ allow: Object.freeze(array(tools.allow, `manifest.agents[${index}].tools.allow`).map((item, itemIndex) => string(item, `manifest.agents[${index}].tools.allow[${itemIndex}]`))) })
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
const endpoints = array(root.endpoints, "manifest.endpoints").map((entry, index) => {
|
|
368
|
+
const endpoint = record(entry, `manifest.endpoints[${index}]`);
|
|
369
|
+
exactKeys(endpoint, [
|
|
370
|
+
"agentId",
|
|
371
|
+
"baseUrl",
|
|
372
|
+
"credentialRef",
|
|
373
|
+
"enabled",
|
|
374
|
+
"experimental",
|
|
375
|
+
"groupPolicy",
|
|
376
|
+
"id",
|
|
377
|
+
"required",
|
|
378
|
+
"sessionNamespace",
|
|
379
|
+
"streamMinIntervalMs"
|
|
380
|
+
], `manifest.endpoints[${index}]`, ["experimental"]);
|
|
381
|
+
const experimental = endpoint.experimental === void 0 ? void 0 : record(endpoint.experimental, `manifest.endpoints[${index}].experimental`);
|
|
382
|
+
if (experimental) exactKeys(experimental, ["cotMessages"], `manifest.endpoints[${index}].experimental`);
|
|
383
|
+
return Object.freeze({
|
|
384
|
+
agentId: string(endpoint.agentId, `manifest.endpoints[${index}].agentId`),
|
|
385
|
+
baseUrl: string(endpoint.baseUrl, `manifest.endpoints[${index}].baseUrl`),
|
|
386
|
+
credentialRef: string(endpoint.credentialRef, `manifest.endpoints[${index}].credentialRef`),
|
|
387
|
+
enabled: boolean(endpoint.enabled, `manifest.endpoints[${index}].enabled`),
|
|
388
|
+
...experimental ? { experimental: Object.freeze({ cotMessages: boolean(experimental.cotMessages, `manifest.endpoints[${index}].experimental.cotMessages`) }) } : {},
|
|
389
|
+
groupPolicy: groupPolicy(endpoint.groupPolicy, `manifest.endpoints[${index}].groupPolicy`),
|
|
390
|
+
id: string(endpoint.id, `manifest.endpoints[${index}].id`),
|
|
391
|
+
required: boolean(endpoint.required, `manifest.endpoints[${index}].required`),
|
|
392
|
+
sessionNamespace: string(endpoint.sessionNamespace, `manifest.endpoints[${index}].sessionNamespace`),
|
|
393
|
+
streamMinIntervalMs: positiveInteger(endpoint.streamMinIntervalMs, `manifest.endpoints[${index}].streamMinIntervalMs`)
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
const automations = array(root.automations ?? [], "manifest.automations").map((entry, index) => {
|
|
397
|
+
const automation = record(entry, `manifest.automations[${index}]`);
|
|
398
|
+
exactKeys(automation, [
|
|
399
|
+
"agentId",
|
|
400
|
+
"delivery",
|
|
401
|
+
"enabled",
|
|
402
|
+
"id",
|
|
403
|
+
"required",
|
|
404
|
+
"schedule",
|
|
405
|
+
"templateId",
|
|
406
|
+
"timeZone"
|
|
407
|
+
], `manifest.automations[${index}]`);
|
|
408
|
+
const delivery = record(automation.delivery, `manifest.automations[${index}].delivery`);
|
|
409
|
+
exactKeys(delivery, [
|
|
410
|
+
"endpointId",
|
|
411
|
+
"targetRef",
|
|
412
|
+
"targetType"
|
|
413
|
+
], `manifest.automations[${index}].delivery`);
|
|
414
|
+
return Object.freeze({
|
|
415
|
+
agentId: string(automation.agentId, `manifest.automations[${index}].agentId`),
|
|
416
|
+
delivery: Object.freeze({
|
|
417
|
+
endpointId: string(delivery.endpointId, `manifest.automations[${index}].delivery.endpointId`),
|
|
418
|
+
targetRef: string(delivery.targetRef, `manifest.automations[${index}].delivery.targetRef`),
|
|
419
|
+
targetType: automationTargetType(delivery.targetType, `manifest.automations[${index}].delivery.targetType`)
|
|
420
|
+
}),
|
|
421
|
+
enabled: boolean(automation.enabled, `manifest.automations[${index}].enabled`),
|
|
422
|
+
id: string(automation.id, `manifest.automations[${index}].id`),
|
|
423
|
+
required: boolean(automation.required, `manifest.automations[${index}].required`),
|
|
424
|
+
schedule: string(automation.schedule, `manifest.automations[${index}].schedule`),
|
|
425
|
+
templateId: string(automation.templateId, `manifest.automations[${index}].templateId`),
|
|
426
|
+
timeZone: string(automation.timeZone, `manifest.automations[${index}].timeZone`)
|
|
427
|
+
});
|
|
428
|
+
});
|
|
429
|
+
return Object.freeze({
|
|
430
|
+
agents: Object.freeze(agents),
|
|
431
|
+
automations: Object.freeze(automations),
|
|
432
|
+
defaultAgentId: string(root.defaultAgentId, "manifest.defaultAgentId"),
|
|
433
|
+
defaultEndpointId: string(root.defaultEndpointId, "manifest.defaultEndpointId"),
|
|
434
|
+
endpoints: Object.freeze(endpoints),
|
|
435
|
+
plugins: Object.freeze(plugins)
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
function record(value, path) {
|
|
439
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error(`${path} must be an object`);
|
|
440
|
+
return value;
|
|
441
|
+
}
|
|
442
|
+
function array(value, path) {
|
|
443
|
+
if (!Array.isArray(value)) throw new Error(`${path} must be an array`);
|
|
444
|
+
return value;
|
|
445
|
+
}
|
|
446
|
+
function string(value, path) {
|
|
447
|
+
if (typeof value !== "string" || value.trim() === "") throw new Error(`${path} must be a non-empty string`);
|
|
448
|
+
return value;
|
|
449
|
+
}
|
|
450
|
+
function boolean(value, path) {
|
|
451
|
+
if (typeof value !== "boolean") throw new Error(`${path} must be a boolean`);
|
|
452
|
+
return value;
|
|
453
|
+
}
|
|
454
|
+
function positiveInteger(value, path) {
|
|
455
|
+
if (!Number.isSafeInteger(value) || value <= 0) throw new Error(`${path} must be a positive integer`);
|
|
456
|
+
return value;
|
|
457
|
+
}
|
|
458
|
+
function memoryScope(value, path) {
|
|
459
|
+
if (typeof value !== "string" || !MEMORY_SCOPES.includes(value)) throw new Error(`${path} must be conversation, agent-private, or shared-user-profile`);
|
|
460
|
+
return value;
|
|
461
|
+
}
|
|
462
|
+
function groupPolicy(value, path) {
|
|
463
|
+
if (value !== "mention-only" && value !== "ignore-unmentioned" && value !== "default-responder") throw new Error(`${path} must be mention-only, ignore-unmentioned, or default-responder`);
|
|
464
|
+
return value;
|
|
465
|
+
}
|
|
466
|
+
function automationTargetType(value, path) {
|
|
467
|
+
if (value !== "chat_id" && value !== "open_id" && value !== "user_id" && value !== "union_id" && value !== "email") throw new Error(`${path} must be chat_id, open_id, user_id, union_id, or email`);
|
|
468
|
+
return value;
|
|
469
|
+
}
|
|
470
|
+
function exactKeys(value, allowed, path, optional = []) {
|
|
471
|
+
const unexpected = Object.keys(value).find((key) => !allowed.includes(key));
|
|
472
|
+
if (unexpected) throw new Error(`${path} contains unsupported field: ${unexpected}`);
|
|
473
|
+
const missing = allowed.find((key) => !optional.includes(key) && !Object.hasOwn(value, key));
|
|
474
|
+
if (missing) throw new Error(`${path} is missing required field: ${missing}`);
|
|
475
|
+
}
|
|
476
|
+
//#endregion
|
|
477
|
+
//#region src/infrastructure/plugin/node-rivus-plugin-module-loader.ts
|
|
478
|
+
async function loadNodeRivusPluginModule(request) {
|
|
479
|
+
return await import(pathToFileURL(await resolveNodeRivusPluginModulePath(request)).href);
|
|
480
|
+
}
|
|
481
|
+
async function resolveNodeRivusPluginModulePath(request) {
|
|
482
|
+
const deploymentRoot = await realpath(request.deploymentRoot);
|
|
483
|
+
const resolvedRealpath = await realpath(createRequire(join(deploymentRoot, "package.json")).resolve(request.module));
|
|
484
|
+
if (!isWithin(deploymentRoot, resolvedRealpath)) throw new Error(`plugin module ${request.module} resolves outside deployment root: ${resolvedRealpath}`);
|
|
485
|
+
return resolvedRealpath;
|
|
486
|
+
}
|
|
487
|
+
function isWithin(root, candidate) {
|
|
488
|
+
const child = relative(root, candidate);
|
|
489
|
+
return child === "" || !child.startsWith(`..${sep}`) && child !== ".." && !isAbsolute(child);
|
|
490
|
+
}
|
|
491
|
+
//#endregion
|
|
9
492
|
//#region src/infrastructure/config/rivus-daemon-config.ts
|
|
10
493
|
var RivusDaemonConfigError = class {
|
|
11
494
|
variable;
|
|
@@ -86,69 +569,13 @@ function optionalPositiveInteger(value, variable, fallback) {
|
|
|
86
569
|
function required(env, variable) {
|
|
87
570
|
const value = optional(env[variable]);
|
|
88
571
|
if (value) return Effect.succeed(value);
|
|
89
|
-
return Effect.fail(new RivusDaemonConfigError(variable, `${variable} is required`));
|
|
90
|
-
}
|
|
91
|
-
function optionalThinkingLevel(value) {
|
|
92
|
-
const normalized = optional(value);
|
|
93
|
-
if (!normalized) return Effect.succeed(void 0);
|
|
94
|
-
if (THINKING_LEVELS$1.has(normalized)) return Effect.succeed(normalized);
|
|
95
|
-
return Effect.fail(new RivusDaemonConfigError("PI_THINKING_LEVEL", "PI_THINKING_LEVEL must be one of off, minimal, low, medium, high, xhigh"));
|
|
96
|
-
}
|
|
97
|
-
//#endregion
|
|
98
|
-
//#region src/infrastructure/config/local-env-file.ts
|
|
99
|
-
var LocalEnvFileError = class extends Error {
|
|
100
|
-
constructor(message) {
|
|
101
|
-
super(message);
|
|
102
|
-
this.name = "LocalEnvFileError";
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
async function loadMergedLocalEnvFile(filePath, overrideEnv) {
|
|
106
|
-
return mergeRivusDaemonEnv(await loadLocalEnvFile(filePath), overrideEnv);
|
|
107
|
-
}
|
|
108
|
-
async function loadLocalEnvFile(filePath) {
|
|
109
|
-
return parseLocalEnvFile(await readFile(filePath, "utf8"));
|
|
110
|
-
}
|
|
111
|
-
function parseLocalEnvFile(contents) {
|
|
112
|
-
const env = {};
|
|
113
|
-
const lines = contents.split(/\r?\n/);
|
|
114
|
-
for (let index = 0; index < lines.length; index += 1) {
|
|
115
|
-
const trimmed = lines[index].trim();
|
|
116
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
117
|
-
const match = (trimmed.startsWith("export ") ? trimmed.slice(7).trimStart() : trimmed).match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
|
|
118
|
-
if (!match) throw new LocalEnvFileError(`Invalid env file line ${index + 1}`);
|
|
119
|
-
const key = match[1];
|
|
120
|
-
const rawValue = match[2];
|
|
121
|
-
env[key] = parseEnvValue(rawValue, index + 1);
|
|
122
|
-
}
|
|
123
|
-
return env;
|
|
124
|
-
}
|
|
125
|
-
function mergeRivusDaemonEnv(fileEnv, overrideEnv) {
|
|
126
|
-
const merged = { ...fileEnv };
|
|
127
|
-
for (const [key, value] of Object.entries(overrideEnv)) if (value !== void 0) merged[key] = value;
|
|
128
|
-
return merged;
|
|
129
|
-
}
|
|
130
|
-
function parseEnvValue(rawValue, lineNumber) {
|
|
131
|
-
const value = rawValue.trim();
|
|
132
|
-
if (!value) return "";
|
|
133
|
-
if (value.startsWith("'")) {
|
|
134
|
-
if (!value.endsWith("'")) throw new LocalEnvFileError(`Invalid single-quoted env value on line ${lineNumber}`);
|
|
135
|
-
return value.slice(1, -1).replaceAll("'\\''", "'");
|
|
136
|
-
}
|
|
137
|
-
if (value.startsWith("\"")) {
|
|
138
|
-
if (!value.endsWith("\"")) throw new LocalEnvFileError(`Invalid double-quoted env value on line ${lineNumber}`);
|
|
139
|
-
return unescapeDoubleQuotedValue(value.slice(1, -1));
|
|
140
|
-
}
|
|
141
|
-
return value;
|
|
142
|
-
}
|
|
143
|
-
function unescapeDoubleQuotedValue(value) {
|
|
144
|
-
return value.replace(/\\(["\\nrt])/g, (_match, escaped) => {
|
|
145
|
-
switch (escaped) {
|
|
146
|
-
case "n": return "\n";
|
|
147
|
-
case "r": return "\r";
|
|
148
|
-
case "t": return " ";
|
|
149
|
-
default: return escaped;
|
|
150
|
-
}
|
|
151
|
-
});
|
|
572
|
+
return Effect.fail(new RivusDaemonConfigError(variable, `${variable} is required`));
|
|
573
|
+
}
|
|
574
|
+
function optionalThinkingLevel(value) {
|
|
575
|
+
const normalized = optional(value);
|
|
576
|
+
if (!normalized) return Effect.succeed(void 0);
|
|
577
|
+
if (THINKING_LEVELS$1.has(normalized)) return Effect.succeed(normalized);
|
|
578
|
+
return Effect.fail(new RivusDaemonConfigError("PI_THINKING_LEVEL", "PI_THINKING_LEVEL must be one of off, minimal, low, medium, high, xhigh"));
|
|
152
579
|
}
|
|
153
580
|
//#endregion
|
|
154
581
|
//#region src/infrastructure/config/openclaw-env-import.ts
|
|
@@ -294,265 +721,58 @@ function readModelId(modelReference) {
|
|
|
294
721
|
const separator = modelReference.indexOf("/");
|
|
295
722
|
return separator >= 0 && separator < modelReference.length - 1 ? modelReference.slice(separator + 1) : void 0;
|
|
296
723
|
}
|
|
297
|
-
function inferFeishuBaseUrl(domain) {
|
|
298
|
-
return domain === "lark" ? DEFAULT_LARK_BASE_URL : DEFAULT_FEISHU_BASE_URL;
|
|
299
|
-
}
|
|
300
|
-
function quoteEnvValue(value) {
|
|
301
|
-
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
302
|
-
}
|
|
303
|
-
function requiredString(record, key, path) {
|
|
304
|
-
const value = optionalString(record[key]);
|
|
305
|
-
if (!value) throw new OpenClawEnvImportError(`${path} is required`);
|
|
306
|
-
return value;
|
|
307
|
-
}
|
|
308
|
-
function optionalString(value) {
|
|
309
|
-
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
310
|
-
}
|
|
311
|
-
function asRecord(value, path) {
|
|
312
|
-
const record = optionalRecord(value);
|
|
313
|
-
if (!record) throw new OpenClawEnvImportError(`${path} must be an object`);
|
|
314
|
-
return record;
|
|
315
|
-
}
|
|
316
|
-
function optionalRecord(value) {
|
|
317
|
-
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
318
|
-
}
|
|
319
|
-
function readPath(record, path) {
|
|
320
|
-
let value = record;
|
|
321
|
-
for (const segment of path) {
|
|
322
|
-
const current = optionalRecord(value);
|
|
323
|
-
if (!current) return;
|
|
324
|
-
value = current[segment];
|
|
325
|
-
}
|
|
326
|
-
return value;
|
|
327
|
-
}
|
|
328
|
-
//#endregion
|
|
329
|
-
//#region src/application/daemon/rivus-daemon-shutdown-controller.ts
|
|
330
|
-
const DEFAULT_SIGNALS = ["SIGINT", "SIGTERM"];
|
|
331
|
-
function createRivusDaemonShutdownController(options) {
|
|
332
|
-
let shutdown;
|
|
333
|
-
const handle = (signal) => {
|
|
334
|
-
shutdown ??= Effect.runPromise(options.daemon.stop()).then(() => options.onStopped?.(signal)).catch(async (error) => {
|
|
335
|
-
await options.onError?.(error, signal);
|
|
336
|
-
throw error;
|
|
337
|
-
});
|
|
338
|
-
return shutdown;
|
|
339
|
-
};
|
|
340
|
-
return {
|
|
341
|
-
handle,
|
|
342
|
-
install: () => {
|
|
343
|
-
for (const signal of options.signals ?? DEFAULT_SIGNALS) options.signalSource.on(signal, () => {
|
|
344
|
-
handle(signal);
|
|
345
|
-
});
|
|
346
|
-
},
|
|
347
|
-
stopping: () => shutdown !== void 0
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
//#endregion
|
|
351
|
-
//#region src/application/plugin/rivus-automation-runtime-definition.ts
|
|
352
|
-
function resolveRivusAutomationRuntimeDefinition(definition, requestedToolIds, requestedSkillIds) {
|
|
353
|
-
const toolIds = Object.freeze([...new Set(requestedToolIds)].sort());
|
|
354
|
-
const toolsById = new Map(definition.tools.map((tool) => [tool.id, tool]));
|
|
355
|
-
const tools = Object.freeze(toolIds.map((toolId) => {
|
|
356
|
-
const tool = toolsById.get(toolId);
|
|
357
|
-
if (!tool) throw new Error(`Automation Runtime requests ungranted tool: ${toolId}`);
|
|
358
|
-
return tool;
|
|
359
|
-
}));
|
|
360
|
-
const skillIds = Object.freeze([...new Set(requestedSkillIds)].sort());
|
|
361
|
-
const skillsById = new Map(definition.skills.map((skill) => [skill.id, skill]));
|
|
362
|
-
const skills = Object.freeze(skillIds.map((skillId) => {
|
|
363
|
-
const skill = skillsById.get(skillId);
|
|
364
|
-
if (!skill) throw new Error(`Automation Runtime requests ungranted skill: ${skillId}`);
|
|
365
|
-
return skill;
|
|
366
|
-
}));
|
|
367
|
-
return deepFreeze({
|
|
368
|
-
...definition,
|
|
369
|
-
memory: {
|
|
370
|
-
scopes: [],
|
|
371
|
-
tool: false
|
|
372
|
-
},
|
|
373
|
-
skillGrantSet: {
|
|
374
|
-
revision: createHash("sha256").update(JSON.stringify({
|
|
375
|
-
parentRevision: definition.skillGrantSet.revision,
|
|
376
|
-
skillIds
|
|
377
|
-
})).digest("hex"),
|
|
378
|
-
skillIds
|
|
379
|
-
},
|
|
380
|
-
skills,
|
|
381
|
-
toolGrantSet: {
|
|
382
|
-
revision: createHash("sha256").update(JSON.stringify({
|
|
383
|
-
parentRevision: definition.toolGrantSet.revision,
|
|
384
|
-
toolIds
|
|
385
|
-
})).digest("hex"),
|
|
386
|
-
toolIds
|
|
387
|
-
},
|
|
388
|
-
tools
|
|
389
|
-
});
|
|
390
|
-
}
|
|
391
|
-
//#endregion
|
|
392
|
-
//#region src/application/plugin/rivus-plugin-loader.ts
|
|
393
|
-
var RivusPluginLoadError = class extends Error {
|
|
394
|
-
pluginId;
|
|
395
|
-
moduleSpecifier;
|
|
396
|
-
name = "RivusPluginLoadError";
|
|
397
|
-
constructor(pluginId, moduleSpecifier, message, options) {
|
|
398
|
-
super(message, options);
|
|
399
|
-
this.pluginId = pluginId;
|
|
400
|
-
this.moduleSpecifier = moduleSpecifier;
|
|
401
|
-
}
|
|
402
|
-
};
|
|
403
|
-
async function loadRivusDeployment(options) {
|
|
404
|
-
validateRivusDeploymentManifest(options.manifest);
|
|
405
|
-
const catalog = createRivusPluginCatalog();
|
|
406
|
-
const pluginStatuses = [];
|
|
407
|
-
const statusByPlugin = /* @__PURE__ */ new Map();
|
|
408
|
-
for (const declaration of options.manifest.plugins) try {
|
|
409
|
-
const plugin = await resolvePluginExport(await options.loadModule({
|
|
410
|
-
deploymentRoot: options.deploymentRoot,
|
|
411
|
-
module: declaration.module,
|
|
412
|
-
pluginId: declaration.id
|
|
413
|
-
}));
|
|
414
|
-
if (plugin.manifest.id !== declaration.id) throw new Error(`plugin manifest id ${plugin.manifest.id} does not match declaration ${declaration.id}`);
|
|
415
|
-
catalog.registerPlugin(plugin);
|
|
416
|
-
const status = Object.freeze({
|
|
417
|
-
id: declaration.id,
|
|
418
|
-
module: declaration.module,
|
|
419
|
-
required: declaration.required,
|
|
420
|
-
status: "loaded",
|
|
421
|
-
version: plugin.manifest.version
|
|
422
|
-
});
|
|
423
|
-
pluginStatuses.push(status);
|
|
424
|
-
statusByPlugin.set(declaration.id, status);
|
|
425
|
-
} catch (cause) {
|
|
426
|
-
const message = cause instanceof Error ? cause.message : String(cause);
|
|
427
|
-
if (declaration.required) throw new RivusPluginLoadError(declaration.id, declaration.module, `required plugin ${declaration.id} failed to load: ${message}`, { cause });
|
|
428
|
-
const status = Object.freeze({
|
|
429
|
-
error: message,
|
|
430
|
-
id: declaration.id,
|
|
431
|
-
module: declaration.module,
|
|
432
|
-
required: false,
|
|
433
|
-
status: "failed"
|
|
434
|
-
});
|
|
435
|
-
pluginStatuses.push(status);
|
|
436
|
-
statusByPlugin.set(declaration.id, status);
|
|
437
|
-
}
|
|
438
|
-
const agentStatuses = [];
|
|
439
|
-
const definitions = [];
|
|
440
|
-
for (const agent of options.manifest.agents) {
|
|
441
|
-
const pluginStatus = statusByPlugin.get(agent.pluginId);
|
|
442
|
-
if (pluginStatus.status === "failed") {
|
|
443
|
-
agentStatuses.push(Object.freeze({
|
|
444
|
-
agentId: agent.agentId,
|
|
445
|
-
pluginId: agent.pluginId,
|
|
446
|
-
profileId: agent.profileId,
|
|
447
|
-
reason: `plugin ${agent.pluginId} is unavailable: ${pluginStatus.error}`,
|
|
448
|
-
status: "disabled"
|
|
449
|
-
}));
|
|
450
|
-
continue;
|
|
451
|
-
}
|
|
452
|
-
try {
|
|
453
|
-
const definition = resolveRivusAgentDefinition(catalog, agent);
|
|
454
|
-
definitions.push(definition);
|
|
455
|
-
agentStatuses.push(Object.freeze({
|
|
456
|
-
agentId: agent.agentId,
|
|
457
|
-
definition,
|
|
458
|
-
pluginId: agent.pluginId,
|
|
459
|
-
profileId: agent.profileId,
|
|
460
|
-
status: "enabled"
|
|
461
|
-
}));
|
|
462
|
-
} catch (cause) {
|
|
463
|
-
const declaration = options.manifest.plugins.find(({ id }) => id === agent.pluginId);
|
|
464
|
-
const message = cause instanceof Error ? cause.message : String(cause);
|
|
465
|
-
if (declaration.required) throw new RivusPluginLoadError(declaration.id, declaration.module, `deployment ${agent.agentId} failed to resolve: ${message}`, { cause });
|
|
466
|
-
agentStatuses.push(Object.freeze({
|
|
467
|
-
agentId: agent.agentId,
|
|
468
|
-
pluginId: agent.pluginId,
|
|
469
|
-
profileId: agent.profileId,
|
|
470
|
-
reason: message,
|
|
471
|
-
status: "disabled"
|
|
472
|
-
}));
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
const agentStatusById = new Map(agentStatuses.map((agent) => [agent.agentId, agent]));
|
|
476
|
-
const automationTemplates = new Map(catalog.snapshot().automations.map((template) => [template.id, template]));
|
|
477
|
-
const automationDefinitions = [];
|
|
478
|
-
for (const automation of options.manifest.automations ?? []) {
|
|
479
|
-
const agent = agentStatusById.get(automation.agentId);
|
|
480
|
-
if (!agent || agent.status === "disabled" || !agent.definition) continue;
|
|
481
|
-
const template = automationTemplates.get(automation.templateId);
|
|
482
|
-
if (!template) throw new Error(`automation ${automation.id} references unknown template: ${automation.templateId}`);
|
|
483
|
-
if (template.pluginId !== agent.pluginId || template.profileId !== agent.profileId) throw new Error(`automation ${automation.id} template is not owned by agent profile ${agent.profileId}`);
|
|
484
|
-
automationDefinitions.push(deepFreeze({
|
|
485
|
-
...automation,
|
|
486
|
-
runtimeDefinition: resolveRivusAutomationRuntimeDefinition(agent.definition, template.requestedToolIds, template.requestedSkillIds),
|
|
487
|
-
template
|
|
488
|
-
}));
|
|
489
|
-
}
|
|
490
|
-
return Object.freeze({
|
|
491
|
-
agents: Object.freeze(agentStatuses),
|
|
492
|
-
automationDefinitions: Object.freeze(automationDefinitions),
|
|
493
|
-
catalog,
|
|
494
|
-
definitions: Object.freeze(definitions),
|
|
495
|
-
manifest: options.manifest,
|
|
496
|
-
plugins: Object.freeze(pluginStatuses)
|
|
497
|
-
});
|
|
498
|
-
}
|
|
499
|
-
function validateRivusDeploymentManifest(manifest) {
|
|
500
|
-
const pluginIds = /* @__PURE__ */ new Set();
|
|
501
|
-
for (const plugin of manifest.plugins) {
|
|
502
|
-
if (pluginIds.has(plugin.id)) throw new Error(`duplicate plugin declaration: ${plugin.id}`);
|
|
503
|
-
validateModuleSpecifier(plugin.module);
|
|
504
|
-
pluginIds.add(plugin.id);
|
|
505
|
-
}
|
|
506
|
-
const agentIds = /* @__PURE__ */ new Set();
|
|
507
|
-
const agentById = /* @__PURE__ */ new Map();
|
|
508
|
-
for (const agent of manifest.agents) {
|
|
509
|
-
if (agentIds.has(agent.agentId)) throw new Error(`duplicate agent deployment: ${agent.agentId}`);
|
|
510
|
-
agentIds.add(agent.agentId);
|
|
511
|
-
if (!pluginIds.has(agent.pluginId)) throw new Error(`agent ${agent.agentId} references undeclared plugin: ${agent.pluginId}`);
|
|
512
|
-
agentById.set(agent.agentId, agent);
|
|
513
|
-
}
|
|
514
|
-
const automationIds = /* @__PURE__ */ new Set();
|
|
515
|
-
for (const automation of manifest.automations ?? []) {
|
|
516
|
-
if (automationIds.has(automation.id)) throw new Error(`duplicate automation binding: ${automation.id}`);
|
|
517
|
-
automationIds.add(automation.id);
|
|
518
|
-
const agent = agentById.get(automation.agentId);
|
|
519
|
-
if (!agent) throw new Error(`automation ${automation.id} references unknown agent: ${automation.agentId}`);
|
|
520
|
-
const endpoint = manifest.endpoints.find(({ id }) => id === automation.delivery.endpointId);
|
|
521
|
-
if (!endpoint) throw new Error(`automation ${automation.id} references unknown delivery endpoint: ${automation.delivery.endpointId}`);
|
|
522
|
-
if (endpoint.agentId !== agent.agentId) throw new Error(`automation ${automation.id} delivery endpoint is bound to a different agent`);
|
|
523
|
-
}
|
|
524
|
-
const endpointIds = /* @__PURE__ */ new Set();
|
|
525
|
-
const sessionNamespaces = /* @__PURE__ */ new Set();
|
|
526
|
-
for (const endpoint of manifest.endpoints) {
|
|
527
|
-
if (endpointIds.has(endpoint.id)) throw new Error(`duplicate endpoint binding: ${endpoint.id}`);
|
|
528
|
-
endpointIds.add(endpoint.id);
|
|
529
|
-
if (sessionNamespaces.has(endpoint.sessionNamespace)) throw new Error(`duplicate endpoint session namespace: ${endpoint.sessionNamespace}`);
|
|
530
|
-
sessionNamespaces.add(endpoint.sessionNamespace);
|
|
531
|
-
const agent = agentById.get(endpoint.agentId);
|
|
532
|
-
if (!agent) throw new Error(`endpoint ${endpoint.id} references unknown agent: ${endpoint.agentId}`);
|
|
533
|
-
if (!agent.endpointIds.includes(endpoint.id)) throw new Error(`endpoint ${endpoint.id} is not declared by agent ${endpoint.agentId}`);
|
|
534
|
-
}
|
|
535
|
-
for (const agent of manifest.agents) for (const endpointId of agent.endpointIds) {
|
|
536
|
-
const endpoint = manifest.endpoints.find(({ id }) => id === endpointId);
|
|
537
|
-
if (!endpoint) throw new Error(`agent ${agent.agentId} references unknown endpoint: ${endpointId}`);
|
|
538
|
-
if (endpoint.agentId !== agent.agentId) throw new Error(`endpoint ${endpointId} is bound to a different agent`);
|
|
539
|
-
}
|
|
540
|
-
const defaultAgent = agentById.get(manifest.defaultAgentId);
|
|
541
|
-
if (!defaultAgent) throw new Error(`default agent does not exist: ${manifest.defaultAgentId}`);
|
|
542
|
-
const defaultEndpoint = manifest.endpoints.find(({ id }) => id === manifest.defaultEndpointId);
|
|
543
|
-
if (!defaultEndpoint) throw new Error(`default endpoint does not exist: ${manifest.defaultEndpointId}`);
|
|
544
|
-
if (defaultEndpoint.agentId !== defaultAgent.agentId) throw new Error("default endpoint is not bound to the default agent");
|
|
545
|
-
if (!defaultEndpoint.enabled) throw new Error("default endpoint must be enabled");
|
|
724
|
+
function inferFeishuBaseUrl(domain) {
|
|
725
|
+
return domain === "lark" ? DEFAULT_LARK_BASE_URL : DEFAULT_FEISHU_BASE_URL;
|
|
546
726
|
}
|
|
547
|
-
function
|
|
548
|
-
|
|
549
|
-
if ((moduleSpecifier.startsWith("./") || moduleSpecifier.startsWith("../")) && moduleSpecifier.split(/[\\/]/).includes("..")) throw new Error(`plugin module escapes deployment root: ${moduleSpecifier}`);
|
|
727
|
+
function quoteEnvValue(value) {
|
|
728
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
550
729
|
}
|
|
551
|
-
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
730
|
+
function requiredString(record, key, path) {
|
|
731
|
+
const value = optionalString(record[key]);
|
|
732
|
+
if (!value) throw new OpenClawEnvImportError(`${path} is required`);
|
|
733
|
+
return value;
|
|
734
|
+
}
|
|
735
|
+
function optionalString(value) {
|
|
736
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
737
|
+
}
|
|
738
|
+
function asRecord(value, path) {
|
|
739
|
+
const record = optionalRecord(value);
|
|
740
|
+
if (!record) throw new OpenClawEnvImportError(`${path} must be an object`);
|
|
741
|
+
return record;
|
|
742
|
+
}
|
|
743
|
+
function optionalRecord(value) {
|
|
744
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
745
|
+
}
|
|
746
|
+
function readPath(record, path) {
|
|
747
|
+
let value = record;
|
|
748
|
+
for (const segment of path) {
|
|
749
|
+
const current = optionalRecord(value);
|
|
750
|
+
if (!current) return;
|
|
751
|
+
value = current[segment];
|
|
752
|
+
}
|
|
753
|
+
return value;
|
|
754
|
+
}
|
|
755
|
+
//#endregion
|
|
756
|
+
//#region src/application/daemon/rivus-daemon-shutdown-controller.ts
|
|
757
|
+
const DEFAULT_SIGNALS = ["SIGINT", "SIGTERM"];
|
|
758
|
+
function createRivusDaemonShutdownController(options) {
|
|
759
|
+
let shutdown;
|
|
760
|
+
const handle = (signal) => {
|
|
761
|
+
shutdown ??= Effect.runPromise(options.daemon.stop()).then(() => options.onStopped?.(signal)).catch(async (error) => {
|
|
762
|
+
await options.onError?.(error, signal);
|
|
763
|
+
throw error;
|
|
764
|
+
});
|
|
765
|
+
return shutdown;
|
|
766
|
+
};
|
|
767
|
+
return {
|
|
768
|
+
handle,
|
|
769
|
+
install: () => {
|
|
770
|
+
for (const signal of options.signals ?? DEFAULT_SIGNALS) options.signalSource.on(signal, () => {
|
|
771
|
+
handle(signal);
|
|
772
|
+
});
|
|
773
|
+
},
|
|
774
|
+
stopping: () => shutdown !== void 0
|
|
775
|
+
};
|
|
556
776
|
}
|
|
557
777
|
//#endregion
|
|
558
778
|
//#region src/application/support/stable-id.ts
|
|
@@ -781,10 +1001,11 @@ async function createRivusDeploymentDaemon(options) {
|
|
|
781
1001
|
const slotById = new Map(slots.map((slot) => [slot.definition.id, slot]));
|
|
782
1002
|
let lifecycle = "stopped";
|
|
783
1003
|
const canRunIntake = () => lifecycle === "running" || lifecycle === "degraded";
|
|
1004
|
+
const canRunEndpointIntake = (slot) => (canRunIntake() || lifecycle === "starting") && (slot.lifecycle === "running" || slot.lifecycle === "starting") && (slot.adapter?.running() ?? false);
|
|
784
1005
|
const handleEndpoint = async (endpointId, input) => {
|
|
785
1006
|
const slot = slotById.get(endpointId);
|
|
786
1007
|
if (!slot) throw new RivusDeploymentDaemonLifecycleError(`unknown endpoint: ${endpointId}`);
|
|
787
|
-
if (!
|
|
1008
|
+
if (!canRunEndpointIntake(slot)) throw new RivusDeploymentDaemonLifecycleError(`endpoint ${endpointId} cannot accept intake while ${slot.lifecycle}`);
|
|
788
1009
|
return host.handleEndpoint(endpointId, input);
|
|
789
1010
|
};
|
|
790
1011
|
const endpointStatus = (slot) => Object.freeze({
|
|
@@ -946,204 +1167,6 @@ async function stopSlots(slots, errors) {
|
|
|
946
1167
|
}
|
|
947
1168
|
}
|
|
948
1169
|
//#endregion
|
|
949
|
-
//#region src/infrastructure/config/rivus-deployment-manifest.ts
|
|
950
|
-
var RivusDeploymentManifestError = class extends Error {
|
|
951
|
-
manifestPath;
|
|
952
|
-
name = "RivusDeploymentManifestError";
|
|
953
|
-
constructor(manifestPath, message, options) {
|
|
954
|
-
super(message, options);
|
|
955
|
-
this.manifestPath = manifestPath;
|
|
956
|
-
}
|
|
957
|
-
};
|
|
958
|
-
async function loadRivusDeploymentManifest(manifestPath, options = {}) {
|
|
959
|
-
const maxBytes = options.maxBytes ?? 1024 * 1024;
|
|
960
|
-
try {
|
|
961
|
-
const metadata = await stat(manifestPath);
|
|
962
|
-
if (!metadata.isFile()) throw new Error("deployment manifest must be a regular file");
|
|
963
|
-
if (metadata.size > maxBytes) throw new Error(`deployment manifest exceeds ${maxBytes} byte limit`);
|
|
964
|
-
const bytes = await readFile(manifestPath);
|
|
965
|
-
const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
966
|
-
return parseManifest(JSON.parse(text));
|
|
967
|
-
} catch (cause) {
|
|
968
|
-
if (cause instanceof RivusDeploymentManifestError) throw cause;
|
|
969
|
-
throw new RivusDeploymentManifestError(manifestPath, `failed to load Rivus deployment manifest: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
function parseManifest(value) {
|
|
973
|
-
const root = record(value, "manifest");
|
|
974
|
-
exactKeys(root, [
|
|
975
|
-
"agents",
|
|
976
|
-
"automations",
|
|
977
|
-
"defaultAgentId",
|
|
978
|
-
"defaultEndpointId",
|
|
979
|
-
"endpoints",
|
|
980
|
-
"plugins"
|
|
981
|
-
], "manifest", ["automations"]);
|
|
982
|
-
const plugins = array(root.plugins, "manifest.plugins").map((entry, index) => {
|
|
983
|
-
const plugin = record(entry, `manifest.plugins[${index}]`);
|
|
984
|
-
exactKeys(plugin, [
|
|
985
|
-
"id",
|
|
986
|
-
"module",
|
|
987
|
-
"required"
|
|
988
|
-
], `manifest.plugins[${index}]`);
|
|
989
|
-
return Object.freeze({
|
|
990
|
-
id: string(plugin.id, `manifest.plugins[${index}].id`),
|
|
991
|
-
module: string(plugin.module, `manifest.plugins[${index}].module`),
|
|
992
|
-
required: boolean(plugin.required, `manifest.plugins[${index}].required`)
|
|
993
|
-
});
|
|
994
|
-
});
|
|
995
|
-
const agents = array(root.agents, "manifest.agents").map((entry, index) => {
|
|
996
|
-
const agent = record(entry, `manifest.agents[${index}]`);
|
|
997
|
-
exactKeys(agent, [
|
|
998
|
-
"agentId",
|
|
999
|
-
"endpointIds",
|
|
1000
|
-
"memory",
|
|
1001
|
-
"pluginId",
|
|
1002
|
-
"profileId",
|
|
1003
|
-
"skills",
|
|
1004
|
-
"tools"
|
|
1005
|
-
], `manifest.agents[${index}]`, ["memory"]);
|
|
1006
|
-
const memory = agent.memory === void 0 ? void 0 : record(agent.memory, `manifest.agents[${index}].memory`);
|
|
1007
|
-
if (memory) exactKeys(memory, ["scopes", "tool"], `manifest.agents[${index}].memory`);
|
|
1008
|
-
const skills = record(agent.skills, `manifest.agents[${index}].skills`);
|
|
1009
|
-
exactKeys(skills, ["allow"], `manifest.agents[${index}].skills`);
|
|
1010
|
-
const tools = record(agent.tools, `manifest.agents[${index}].tools`);
|
|
1011
|
-
exactKeys(tools, ["allow"], `manifest.agents[${index}].tools`);
|
|
1012
|
-
return Object.freeze({
|
|
1013
|
-
agentId: string(agent.agentId, `manifest.agents[${index}].agentId`),
|
|
1014
|
-
endpointIds: Object.freeze(array(agent.endpointIds, `manifest.agents[${index}].endpointIds`).map((item, itemIndex) => string(item, `manifest.agents[${index}].endpointIds[${itemIndex}]`))),
|
|
1015
|
-
...memory ? { memory: Object.freeze({
|
|
1016
|
-
scopes: Object.freeze(array(memory.scopes, `manifest.agents[${index}].memory.scopes`).map((item, itemIndex) => memoryScope(item, `manifest.agents[${index}].memory.scopes[${itemIndex}]`))),
|
|
1017
|
-
tool: boolean(memory.tool, `manifest.agents[${index}].memory.tool`)
|
|
1018
|
-
}) } : {},
|
|
1019
|
-
pluginId: string(agent.pluginId, `manifest.agents[${index}].pluginId`),
|
|
1020
|
-
profileId: string(agent.profileId, `manifest.agents[${index}].profileId`),
|
|
1021
|
-
skills: Object.freeze({ allow: Object.freeze(array(skills.allow, `manifest.agents[${index}].skills.allow`).map((item, itemIndex) => string(item, `manifest.agents[${index}].skills.allow[${itemIndex}]`))) }),
|
|
1022
|
-
tools: Object.freeze({ allow: Object.freeze(array(tools.allow, `manifest.agents[${index}].tools.allow`).map((item, itemIndex) => string(item, `manifest.agents[${index}].tools.allow[${itemIndex}]`))) })
|
|
1023
|
-
});
|
|
1024
|
-
});
|
|
1025
|
-
const endpoints = array(root.endpoints, "manifest.endpoints").map((entry, index) => {
|
|
1026
|
-
const endpoint = record(entry, `manifest.endpoints[${index}]`);
|
|
1027
|
-
exactKeys(endpoint, [
|
|
1028
|
-
"agentId",
|
|
1029
|
-
"baseUrl",
|
|
1030
|
-
"credentialRef",
|
|
1031
|
-
"enabled",
|
|
1032
|
-
"experimental",
|
|
1033
|
-
"groupPolicy",
|
|
1034
|
-
"id",
|
|
1035
|
-
"required",
|
|
1036
|
-
"sessionNamespace",
|
|
1037
|
-
"streamMinIntervalMs"
|
|
1038
|
-
], `manifest.endpoints[${index}]`, ["experimental"]);
|
|
1039
|
-
const experimental = endpoint.experimental === void 0 ? void 0 : record(endpoint.experimental, `manifest.endpoints[${index}].experimental`);
|
|
1040
|
-
if (experimental) exactKeys(experimental, ["cotMessages"], `manifest.endpoints[${index}].experimental`);
|
|
1041
|
-
return Object.freeze({
|
|
1042
|
-
agentId: string(endpoint.agentId, `manifest.endpoints[${index}].agentId`),
|
|
1043
|
-
baseUrl: string(endpoint.baseUrl, `manifest.endpoints[${index}].baseUrl`),
|
|
1044
|
-
credentialRef: string(endpoint.credentialRef, `manifest.endpoints[${index}].credentialRef`),
|
|
1045
|
-
enabled: boolean(endpoint.enabled, `manifest.endpoints[${index}].enabled`),
|
|
1046
|
-
...experimental ? { experimental: Object.freeze({ cotMessages: boolean(experimental.cotMessages, `manifest.endpoints[${index}].experimental.cotMessages`) }) } : {},
|
|
1047
|
-
groupPolicy: groupPolicy(endpoint.groupPolicy, `manifest.endpoints[${index}].groupPolicy`),
|
|
1048
|
-
id: string(endpoint.id, `manifest.endpoints[${index}].id`),
|
|
1049
|
-
required: boolean(endpoint.required, `manifest.endpoints[${index}].required`),
|
|
1050
|
-
sessionNamespace: string(endpoint.sessionNamespace, `manifest.endpoints[${index}].sessionNamespace`),
|
|
1051
|
-
streamMinIntervalMs: positiveInteger(endpoint.streamMinIntervalMs, `manifest.endpoints[${index}].streamMinIntervalMs`)
|
|
1052
|
-
});
|
|
1053
|
-
});
|
|
1054
|
-
const automations = array(root.automations ?? [], "manifest.automations").map((entry, index) => {
|
|
1055
|
-
const automation = record(entry, `manifest.automations[${index}]`);
|
|
1056
|
-
exactKeys(automation, [
|
|
1057
|
-
"agentId",
|
|
1058
|
-
"delivery",
|
|
1059
|
-
"enabled",
|
|
1060
|
-
"id",
|
|
1061
|
-
"required",
|
|
1062
|
-
"schedule",
|
|
1063
|
-
"templateId",
|
|
1064
|
-
"timeZone"
|
|
1065
|
-
], `manifest.automations[${index}]`);
|
|
1066
|
-
const delivery = record(automation.delivery, `manifest.automations[${index}].delivery`);
|
|
1067
|
-
exactKeys(delivery, [
|
|
1068
|
-
"endpointId",
|
|
1069
|
-
"targetRef",
|
|
1070
|
-
"targetType"
|
|
1071
|
-
], `manifest.automations[${index}].delivery`);
|
|
1072
|
-
return Object.freeze({
|
|
1073
|
-
agentId: string(automation.agentId, `manifest.automations[${index}].agentId`),
|
|
1074
|
-
delivery: Object.freeze({
|
|
1075
|
-
endpointId: string(delivery.endpointId, `manifest.automations[${index}].delivery.endpointId`),
|
|
1076
|
-
targetRef: string(delivery.targetRef, `manifest.automations[${index}].delivery.targetRef`),
|
|
1077
|
-
targetType: automationTargetType(delivery.targetType, `manifest.automations[${index}].delivery.targetType`)
|
|
1078
|
-
}),
|
|
1079
|
-
enabled: boolean(automation.enabled, `manifest.automations[${index}].enabled`),
|
|
1080
|
-
id: string(automation.id, `manifest.automations[${index}].id`),
|
|
1081
|
-
required: boolean(automation.required, `manifest.automations[${index}].required`),
|
|
1082
|
-
schedule: string(automation.schedule, `manifest.automations[${index}].schedule`),
|
|
1083
|
-
templateId: string(automation.templateId, `manifest.automations[${index}].templateId`),
|
|
1084
|
-
timeZone: string(automation.timeZone, `manifest.automations[${index}].timeZone`)
|
|
1085
|
-
});
|
|
1086
|
-
});
|
|
1087
|
-
return Object.freeze({
|
|
1088
|
-
agents: Object.freeze(agents),
|
|
1089
|
-
automations: Object.freeze(automations),
|
|
1090
|
-
defaultAgentId: string(root.defaultAgentId, "manifest.defaultAgentId"),
|
|
1091
|
-
defaultEndpointId: string(root.defaultEndpointId, "manifest.defaultEndpointId"),
|
|
1092
|
-
endpoints: Object.freeze(endpoints),
|
|
1093
|
-
plugins: Object.freeze(plugins)
|
|
1094
|
-
});
|
|
1095
|
-
}
|
|
1096
|
-
function record(value, path) {
|
|
1097
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error(`${path} must be an object`);
|
|
1098
|
-
return value;
|
|
1099
|
-
}
|
|
1100
|
-
function array(value, path) {
|
|
1101
|
-
if (!Array.isArray(value)) throw new Error(`${path} must be an array`);
|
|
1102
|
-
return value;
|
|
1103
|
-
}
|
|
1104
|
-
function string(value, path) {
|
|
1105
|
-
if (typeof value !== "string" || value.trim() === "") throw new Error(`${path} must be a non-empty string`);
|
|
1106
|
-
return value;
|
|
1107
|
-
}
|
|
1108
|
-
function boolean(value, path) {
|
|
1109
|
-
if (typeof value !== "boolean") throw new Error(`${path} must be a boolean`);
|
|
1110
|
-
return value;
|
|
1111
|
-
}
|
|
1112
|
-
function positiveInteger(value, path) {
|
|
1113
|
-
if (!Number.isSafeInteger(value) || value <= 0) throw new Error(`${path} must be a positive integer`);
|
|
1114
|
-
return value;
|
|
1115
|
-
}
|
|
1116
|
-
function memoryScope(value, path) {
|
|
1117
|
-
if (typeof value !== "string" || !MEMORY_SCOPES.includes(value)) throw new Error(`${path} must be conversation, agent-private, or shared-user-profile`);
|
|
1118
|
-
return value;
|
|
1119
|
-
}
|
|
1120
|
-
function groupPolicy(value, path) {
|
|
1121
|
-
if (value !== "mention-only" && value !== "ignore-unmentioned" && value !== "default-responder") throw new Error(`${path} must be mention-only, ignore-unmentioned, or default-responder`);
|
|
1122
|
-
return value;
|
|
1123
|
-
}
|
|
1124
|
-
function automationTargetType(value, path) {
|
|
1125
|
-
if (value !== "chat_id" && value !== "open_id" && value !== "user_id" && value !== "union_id" && value !== "email") throw new Error(`${path} must be chat_id, open_id, user_id, union_id, or email`);
|
|
1126
|
-
return value;
|
|
1127
|
-
}
|
|
1128
|
-
function exactKeys(value, allowed, path, optional = []) {
|
|
1129
|
-
const unexpected = Object.keys(value).find((key) => !allowed.includes(key));
|
|
1130
|
-
if (unexpected) throw new Error(`${path} contains unsupported field: ${unexpected}`);
|
|
1131
|
-
const missing = allowed.find((key) => !optional.includes(key) && !Object.hasOwn(value, key));
|
|
1132
|
-
if (missing) throw new Error(`${path} is missing required field: ${missing}`);
|
|
1133
|
-
}
|
|
1134
|
-
//#endregion
|
|
1135
|
-
//#region src/infrastructure/plugin/node-rivus-plugin-module-loader.ts
|
|
1136
|
-
async function loadNodeRivusPluginModule(request) {
|
|
1137
|
-
const deploymentRoot = await realpath(request.deploymentRoot);
|
|
1138
|
-
const resolvedRealpath = await realpath(createRequire(join(deploymentRoot, "package.json")).resolve(request.module));
|
|
1139
|
-
if (!isWithin(deploymentRoot, resolvedRealpath)) throw new Error(`plugin module ${request.module} resolves outside deployment root: ${resolvedRealpath}`);
|
|
1140
|
-
return await import(pathToFileURL(resolvedRealpath).href);
|
|
1141
|
-
}
|
|
1142
|
-
function isWithin(root, candidate) {
|
|
1143
|
-
const child = relative(root, candidate);
|
|
1144
|
-
return child === "" || !child.startsWith(`..${sep}`) && child !== ".." && !isAbsolute(child);
|
|
1145
|
-
}
|
|
1146
|
-
//#endregion
|
|
1147
1170
|
//#region src/infrastructure/runtime/configured-rivus-deployment-daemon.ts
|
|
1148
1171
|
async function createConfiguredRivusDeploymentDaemon(options) {
|
|
1149
1172
|
const manifest = await loadRivusDeploymentManifest(options.manifestPath, options.manifestOptions);
|
|
@@ -1452,6 +1475,10 @@ const USAGE = `Usage: rivus --bootstrap <module> [--manifest <rivus.config.json>
|
|
|
1452
1475
|
|
|
1453
1476
|
Starts a local Rivus Agent daemon from an injected bootstrap module.
|
|
1454
1477
|
|
|
1478
|
+
Project commands:
|
|
1479
|
+
rivus init [directory]
|
|
1480
|
+
rivus doctor [directory] [--env-file <path>]
|
|
1481
|
+
|
|
1455
1482
|
Options:
|
|
1456
1483
|
--bootstrap <module> Module exporting createRivusDaemonProcess(context)
|
|
1457
1484
|
--manifest <path> Start the manifest-driven multi-agent deployment; bootstrap exports createRivusDeploymentAdapters(context)
|
|
@@ -2596,4 +2623,4 @@ function hasRecoveryRunner(daemon) {
|
|
|
2596
2623
|
return typeof daemon.openRecoveryControl === "function";
|
|
2597
2624
|
}
|
|
2598
2625
|
//#endregion
|
|
2599
|
-
export {
|
|
2626
|
+
export { loadRivusDeployment as A, resolveNodeRivusPluginModulePath as C, resolveFeishuEndpointCredentials as D, FeishuEndpointCredentialError as E, loadMergedLocalEnvFile as O, loadNodeRivusPluginModule as S, loadRivusDeploymentManifest as T, OpenClawEnvImportError as _, RivusDeploymentDaemonLifecycleError as a, RivusDaemonConfigError as b, InvalidRivusEndpointBinding as c, AgentRuntimeDisposed as d, createAgentRuntimePool as f, createRivusDaemonShutdownController as g, createStableId as h, RivusDeploymentAutomationReadinessError as i, validateRivusDeploymentManifest as j, RivusPluginLoadError as k, createRivusAgentHost as l, createAgentInstanceRegistry as m, createRivusDeploymentCliProcess as n, RivusDeploymentReadinessError as o, AgentInstanceConflict as p, createConfiguredRivusDeploymentDaemon as r, createRivusDeploymentDaemon as s, runRivusDaemonCli as t, AgentInstanceBusy as u, createRivusEnvFromOpenClawConfig as v, RivusDeploymentManifestError as w, loadRivusDaemonConfig as x, formatRivusEnvFile as y };
|