@agentvault/agentvault 0.17.3 → 0.17.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +141 -4
- package/dist/cli.js.map +4 -4
- package/dist/index.js +159 -16
- package/dist/index.js.map +4 -4
- package/dist/openclaw-entry.js +598 -142
- package/dist/openclaw-entry.js.map +4 -4
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/dist/_cp.d.ts +0 -10
- package/dist/_cp.d.ts.map +0 -1
- package/dist/account-config.d.ts +0 -20
- package/dist/account-config.d.ts.map +0 -1
- package/dist/channel.d.ts +0 -350
- package/dist/channel.d.ts.map +0 -1
- package/dist/cli.d.ts +0 -2
- package/dist/cli.d.ts.map +0 -1
- package/dist/create-agent.d.ts +0 -28
- package/dist/create-agent.d.ts.map +0 -1
- package/dist/crypto-helpers.d.ts +0 -2
- package/dist/crypto-helpers.d.ts.map +0 -1
- package/dist/doctor.d.ts +0 -41
- package/dist/doctor.d.ts.map +0 -1
- package/dist/fetch-interceptor.d.ts +0 -32
- package/dist/fetch-interceptor.d.ts.map +0 -1
- package/dist/gateway-send.d.ts +0 -98
- package/dist/gateway-send.d.ts.map +0 -1
- package/dist/http-handlers.d.ts +0 -42
- package/dist/http-handlers.d.ts.map +0 -1
- package/dist/index.d.ts +0 -25
- package/dist/index.d.ts.map +0 -1
- package/dist/mcp-handlers.d.ts +0 -26
- package/dist/mcp-handlers.d.ts.map +0 -1
- package/dist/mcp-server.d.ts +0 -88
- package/dist/mcp-server.d.ts.map +0 -1
- package/dist/openclaw-compat.d.ts +0 -33
- package/dist/openclaw-compat.d.ts.map +0 -1
- package/dist/openclaw-entry.d.ts +0 -27
- package/dist/openclaw-entry.d.ts.map +0 -1
- package/dist/openclaw-plugin.d.ts +0 -102
- package/dist/openclaw-plugin.d.ts.map +0 -1
- package/dist/openclaw-types.d.ts +0 -155
- package/dist/openclaw-types.d.ts.map +0 -1
- package/dist/policy-enforcer.d.ts +0 -78
- package/dist/policy-enforcer.d.ts.map +0 -1
- package/dist/setup.d.ts +0 -27
- package/dist/setup.d.ts.map +0 -1
- package/dist/skill-invoker.d.ts +0 -30
- package/dist/skill-invoker.d.ts.map +0 -1
- package/dist/skill-manifest.d.ts +0 -25
- package/dist/skill-manifest.d.ts.map +0 -1
- package/dist/skill-telemetry.d.ts +0 -36
- package/dist/skill-telemetry.d.ts.map +0 -1
- package/dist/skills-publish.d.ts +0 -8
- package/dist/skills-publish.d.ts.map +0 -1
- package/dist/state.d.ts +0 -32
- package/dist/state.d.ts.map +0 -1
- package/dist/transport.d.ts +0 -24
- package/dist/transport.d.ts.map +0 -1
- package/dist/types.d.ts +0 -379
- package/dist/types.d.ts.map +0 -1
- package/dist/workspace-handlers.d.ts +0 -62
- package/dist/workspace-handlers.d.ts.map +0 -1
package/dist/openclaw-entry.js
CHANGED
|
@@ -14,6 +14,178 @@ var __export = (target, all) => {
|
|
|
14
14
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
// src/http-handlers.ts
|
|
18
|
+
var http_handlers_exports = {};
|
|
19
|
+
__export(http_handlers_exports, {
|
|
20
|
+
handleActionRequest: () => handleActionRequest,
|
|
21
|
+
handleDecisionRequest: () => handleDecisionRequest,
|
|
22
|
+
handleMcpConfigRequest: () => handleMcpConfigRequest,
|
|
23
|
+
handleSendRequest: () => handleSendRequest,
|
|
24
|
+
handleStatusRequest: () => handleStatusRequest,
|
|
25
|
+
handleTargetsRequest: () => handleTargetsRequest
|
|
26
|
+
});
|
|
27
|
+
async function handleSendRequest(parsed, channel) {
|
|
28
|
+
const text = parsed.text;
|
|
29
|
+
if (!text || typeof text !== "string") {
|
|
30
|
+
return { status: 400, body: { ok: false, error: "Missing 'text' field" } };
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
let target;
|
|
34
|
+
let a2aTarget = parsed.hub_address ?? parsed.a2a_address ?? parsed.a2aAddress;
|
|
35
|
+
if (!a2aTarget && parsed.channel_id && typeof parsed.channel_id === "string") {
|
|
36
|
+
const hubAddr = channel.resolveA2AChannelHub(parsed.channel_id);
|
|
37
|
+
if (hubAddr) a2aTarget = hubAddr;
|
|
38
|
+
}
|
|
39
|
+
if (a2aTarget && typeof a2aTarget === "string") {
|
|
40
|
+
target = { kind: "a2a", hubAddress: a2aTarget };
|
|
41
|
+
} else if (typeof parsed.room_id === "string") {
|
|
42
|
+
target = { kind: "room", roomId: parsed.room_id };
|
|
43
|
+
} else if (parsed.target === "context") {
|
|
44
|
+
target = { kind: "context" };
|
|
45
|
+
} else if (parsed.file_path && typeof parsed.file_path === "string") {
|
|
46
|
+
const receipt2 = await channel.deliver(
|
|
47
|
+
{ kind: "owner" },
|
|
48
|
+
{ type: "attachment", text, filePath: parsed.file_path },
|
|
49
|
+
{ topicId: parsed.topicId }
|
|
50
|
+
);
|
|
51
|
+
return {
|
|
52
|
+
status: receipt2.ok ? 200 : 500,
|
|
53
|
+
body: {
|
|
54
|
+
ok: receipt2.ok,
|
|
55
|
+
destination: receipt2.destination,
|
|
56
|
+
...receipt2.error ? { error: receipt2.error } : {}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
} else {
|
|
60
|
+
target = { kind: "owner" };
|
|
61
|
+
}
|
|
62
|
+
const receipt = await channel.deliver(
|
|
63
|
+
target,
|
|
64
|
+
{ type: "text", text },
|
|
65
|
+
{
|
|
66
|
+
topicId: parsed.topicId,
|
|
67
|
+
priority: parsed.priority,
|
|
68
|
+
metadata: {
|
|
69
|
+
...parsed.metadata,
|
|
70
|
+
...parsed.message_type ? { message_type: parsed.message_type } : {}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
return {
|
|
75
|
+
status: receipt.ok ? 200 : 500,
|
|
76
|
+
body: {
|
|
77
|
+
ok: receipt.ok,
|
|
78
|
+
destination: receipt.destination,
|
|
79
|
+
...receipt.error ? { error: receipt.error } : {}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
} catch (err) {
|
|
83
|
+
return { status: 500, body: { ok: false, error: String(err) } };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async function handleActionRequest(parsed, channel) {
|
|
87
|
+
if (!parsed.action || typeof parsed.action !== "string") {
|
|
88
|
+
return { status: 400, body: { ok: false, error: "Missing 'action' field" } };
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const confirmation = {
|
|
92
|
+
action: parsed.action,
|
|
93
|
+
status: parsed.status ?? "completed",
|
|
94
|
+
decisionId: parsed.decision_id,
|
|
95
|
+
detail: parsed.detail,
|
|
96
|
+
estimated_cost: parsed.estimated_cost
|
|
97
|
+
};
|
|
98
|
+
const target = parsed.room_id && typeof parsed.room_id === "string" ? { kind: "room", roomId: parsed.room_id } : { kind: "owner" };
|
|
99
|
+
const receipt = await channel.deliver(
|
|
100
|
+
target,
|
|
101
|
+
{ type: "action_confirmation", confirmation }
|
|
102
|
+
);
|
|
103
|
+
return {
|
|
104
|
+
status: receipt.ok ? 200 : 500,
|
|
105
|
+
body: {
|
|
106
|
+
ok: receipt.ok,
|
|
107
|
+
destination: receipt.destination,
|
|
108
|
+
...receipt.error ? { error: receipt.error } : {}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
} catch (err) {
|
|
112
|
+
return { status: 500, body: { ok: false, error: String(err) } };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function handleDecisionRequest(parsed, channel) {
|
|
116
|
+
const title = parsed.title;
|
|
117
|
+
if (!title || typeof title !== "string") {
|
|
118
|
+
return { status: 400, body: { ok: false, error: "Missing 'title' field" } };
|
|
119
|
+
}
|
|
120
|
+
const options = parsed.options;
|
|
121
|
+
if (!Array.isArray(options) || options.length < 2) {
|
|
122
|
+
return { status: 400, body: { ok: false, error: "'options' must be an array with at least 2 items" } };
|
|
123
|
+
}
|
|
124
|
+
for (const opt of options) {
|
|
125
|
+
if (!opt || typeof opt !== "object" || !opt.option_id || !opt.label) {
|
|
126
|
+
return { status: 400, body: { ok: false, error: "Each option must have 'option_id' and 'label'" } };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
const decision_id = await channel.sendDecisionRequest({
|
|
131
|
+
title,
|
|
132
|
+
description: parsed.description,
|
|
133
|
+
options,
|
|
134
|
+
context_refs: parsed.context_refs,
|
|
135
|
+
deadline: parsed.deadline,
|
|
136
|
+
auto_action: parsed.auto_action
|
|
137
|
+
});
|
|
138
|
+
return { status: 200, body: { ok: true, decision_id } };
|
|
139
|
+
} catch (err) {
|
|
140
|
+
return { status: 500, body: { ok: false, error: String(err) } };
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function handleStatusRequest(channel) {
|
|
144
|
+
return {
|
|
145
|
+
status: 200,
|
|
146
|
+
body: {
|
|
147
|
+
ok: true,
|
|
148
|
+
state: channel.state,
|
|
149
|
+
deviceId: channel.deviceId ?? void 0,
|
|
150
|
+
sessions: channel.sessionCount
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function handleTargetsRequest(channel) {
|
|
155
|
+
return {
|
|
156
|
+
status: 200,
|
|
157
|
+
body: {
|
|
158
|
+
ok: true,
|
|
159
|
+
targets: channel.listTargets(),
|
|
160
|
+
context: channel.lastInboundRoomId ? { kind: "room", roomId: channel.lastInboundRoomId } : { kind: "owner" }
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function handleMcpConfigRequest(agentName, port, mcpSkillCount) {
|
|
165
|
+
return {
|
|
166
|
+
status: 200,
|
|
167
|
+
body: {
|
|
168
|
+
mcpServers: {
|
|
169
|
+
[`agentvault-${agentName}`]: {
|
|
170
|
+
url: `http://127.0.0.1:${port}/mcp`
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
_meta: {
|
|
174
|
+
agent: agentName,
|
|
175
|
+
skills_count: mcpSkillCount,
|
|
176
|
+
transport: "streamable-http",
|
|
177
|
+
auth: "none (localhost)",
|
|
178
|
+
instructions: "Add the mcpServers block to your MCP configuration file (e.g., .mcp.json or mcp_config.json)"
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
var init_http_handlers = __esm({
|
|
184
|
+
"src/http-handlers.ts"() {
|
|
185
|
+
"use strict";
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
17
189
|
// src/openclaw-compat.ts
|
|
18
190
|
var openclaw_compat_exports = {};
|
|
19
191
|
__export(openclaw_compat_exports, {
|
|
@@ -90,8 +262,385 @@ var init_openclaw_compat = __esm({
|
|
|
90
262
|
}
|
|
91
263
|
});
|
|
92
264
|
|
|
265
|
+
// src/mcp-server.ts
|
|
266
|
+
var mcp_server_exports = {};
|
|
267
|
+
__export(mcp_server_exports, {
|
|
268
|
+
AgentVaultMcpServer: () => AgentVaultMcpServer
|
|
269
|
+
});
|
|
270
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
271
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
272
|
+
var AgentVaultMcpServer;
|
|
273
|
+
var init_mcp_server = __esm({
|
|
274
|
+
"src/mcp-server.ts"() {
|
|
275
|
+
"use strict";
|
|
276
|
+
AgentVaultMcpServer = class {
|
|
277
|
+
server;
|
|
278
|
+
skills = /* @__PURE__ */ new Map();
|
|
279
|
+
opts;
|
|
280
|
+
initialized = false;
|
|
281
|
+
constructor(opts) {
|
|
282
|
+
this.opts = opts;
|
|
283
|
+
this.server = new McpServer({
|
|
284
|
+
name: `agentvault-${opts.agentName}`,
|
|
285
|
+
version: "1.0.0"
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
/* ---- Skill registration ------------------------------------------------ */
|
|
289
|
+
/**
|
|
290
|
+
* Register a skill that will be exposed as an MCP tool.
|
|
291
|
+
* Must be called *before* `initialize()`.
|
|
292
|
+
*/
|
|
293
|
+
registerSkill(skill) {
|
|
294
|
+
this.skills.set(skill.name, skill);
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Register all skills as MCP tools / resources / prompts.
|
|
298
|
+
* Called lazily on first request if not called explicitly.
|
|
299
|
+
*/
|
|
300
|
+
initialize() {
|
|
301
|
+
if (this.initialized) return;
|
|
302
|
+
for (const [name, skill] of this.skills) {
|
|
303
|
+
this.registerToolForSkill(name, skill);
|
|
304
|
+
}
|
|
305
|
+
this.server.resource(
|
|
306
|
+
"skills-registry",
|
|
307
|
+
"skills://registry",
|
|
308
|
+
{ description: "List of all registered agent skills", mimeType: "application/json" },
|
|
309
|
+
async () => {
|
|
310
|
+
const registry = Array.from(this.skills.values()).map((s) => ({
|
|
311
|
+
name: s.name,
|
|
312
|
+
version: s.version,
|
|
313
|
+
description: s.description,
|
|
314
|
+
tags: s.tags,
|
|
315
|
+
sla: s.slaDefinition,
|
|
316
|
+
hasSchema: !!s.inputSchema,
|
|
317
|
+
hasInstructions: !!s.instructions,
|
|
318
|
+
certificationTier: s.certificationTier,
|
|
319
|
+
modelRouting: s.modelRouting,
|
|
320
|
+
allowedModels: s.allowedModels,
|
|
321
|
+
hasToolPolicy: !!(s.toolsAllowed || s.toolsDenied),
|
|
322
|
+
hasOutputSchema: !!s.outputSchema,
|
|
323
|
+
requiredPolicies: s.requiredPolicies
|
|
324
|
+
}));
|
|
325
|
+
return {
|
|
326
|
+
contents: [{
|
|
327
|
+
uri: "skills://registry",
|
|
328
|
+
mimeType: "application/json",
|
|
329
|
+
text: JSON.stringify(registry, null, 2)
|
|
330
|
+
}]
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
);
|
|
334
|
+
for (const [name, skill] of this.skills) {
|
|
335
|
+
if (skill.instructions) {
|
|
336
|
+
this.server.prompt(
|
|
337
|
+
name,
|
|
338
|
+
skill.description ?? `Instructions for ${name}`,
|
|
339
|
+
() => ({
|
|
340
|
+
messages: [{
|
|
341
|
+
role: "user",
|
|
342
|
+
content: { type: "text", text: skill.instructions }
|
|
343
|
+
}]
|
|
344
|
+
})
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
this.initialized = true;
|
|
349
|
+
}
|
|
350
|
+
/* ---- HTTP request handler ---------------------------------------------- */
|
|
351
|
+
/**
|
|
352
|
+
* Handle an incoming HTTP request to the /mcp endpoint.
|
|
353
|
+
*
|
|
354
|
+
* Supports the Streamable HTTP transport protocol:
|
|
355
|
+
* - POST for JSON-RPC messages
|
|
356
|
+
* - GET for SSE notification stream
|
|
357
|
+
* - DELETE for session close
|
|
358
|
+
*
|
|
359
|
+
* Each request gets a fresh stateless transport; after the response is
|
|
360
|
+
* flushed the transport + underlying protocol connection are torn down
|
|
361
|
+
* so the single McpServer instance is ready for the next caller.
|
|
362
|
+
*
|
|
363
|
+
* Local requests from 127.0.0.1/::1 bypass SPT validation (owner access).
|
|
364
|
+
*/
|
|
365
|
+
async handleRequest(req, res) {
|
|
366
|
+
if (!this.initialized) {
|
|
367
|
+
this.initialize();
|
|
368
|
+
}
|
|
369
|
+
const remote = req.socket?.remoteAddress;
|
|
370
|
+
const isLocal = remote === "127.0.0.1" || remote === "::1" || remote === "::ffff:127.0.0.1";
|
|
371
|
+
if (!isLocal) {
|
|
372
|
+
const authHeader = req.headers.authorization;
|
|
373
|
+
if (!authHeader?.startsWith("Bearer ")) {
|
|
374
|
+
res.writeHead(401, { "Content-Type": "application/json" });
|
|
375
|
+
res.end(JSON.stringify({ error: "Missing or invalid Authorization header" }));
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const token = authHeader.slice(7);
|
|
379
|
+
const valid = await this.validateSpt(token);
|
|
380
|
+
if (!valid) {
|
|
381
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
382
|
+
res.end(JSON.stringify({ error: "Invalid or expired SPT token" }));
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
const transport = new StreamableHTTPServerTransport({
|
|
387
|
+
sessionIdGenerator: void 0
|
|
388
|
+
// stateless
|
|
389
|
+
});
|
|
390
|
+
await this.server.connect(transport);
|
|
391
|
+
try {
|
|
392
|
+
await transport.handleRequest(req, res);
|
|
393
|
+
} finally {
|
|
394
|
+
await transport.close();
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
/* ---- Private helpers --------------------------------------------------- */
|
|
398
|
+
/**
|
|
399
|
+
* Register a single skill as an MCP tool.
|
|
400
|
+
*
|
|
401
|
+
* The MCP SDK's `tool()` overloads that accept a schema expect Zod types.
|
|
402
|
+
* Since our skill definitions use raw JSON Schema we register without
|
|
403
|
+
* schema validation (name + description + handler) and let the handler
|
|
404
|
+
* receive the raw args object.
|
|
405
|
+
*/
|
|
406
|
+
registerToolForSkill(name, skill) {
|
|
407
|
+
const description = skill.description ?? `AgentVault skill: ${name}`;
|
|
408
|
+
this.server.tool(
|
|
409
|
+
name,
|
|
410
|
+
description,
|
|
411
|
+
async (args) => {
|
|
412
|
+
if (!this.opts.onInvoke) {
|
|
413
|
+
return {
|
|
414
|
+
content: [{ type: "text", text: `Skill "${name}" invoked but no handler registered` }]
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
try {
|
|
418
|
+
const result = await this.opts.onInvoke(name, args);
|
|
419
|
+
const text = typeof result === "string" ? result : JSON.stringify(result, null, 2);
|
|
420
|
+
return {
|
|
421
|
+
content: [{ type: "text", text }]
|
|
422
|
+
};
|
|
423
|
+
} catch (err) {
|
|
424
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
425
|
+
return {
|
|
426
|
+
content: [{ type: "text", text: `Error invoking skill "${name}": ${message}` }],
|
|
427
|
+
isError: true
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Validate a Service Provider Token against the AgentVault backend.
|
|
435
|
+
*/
|
|
436
|
+
async validateSpt(token) {
|
|
437
|
+
try {
|
|
438
|
+
const url = `${this.opts.apiUrl}/api/v1/capabilities/introspect`;
|
|
439
|
+
const headers = {
|
|
440
|
+
"Content-Type": "application/json"
|
|
441
|
+
};
|
|
442
|
+
if (this.opts.apiKey) {
|
|
443
|
+
headers["Authorization"] = `Bearer ${this.opts.apiKey}`;
|
|
444
|
+
}
|
|
445
|
+
const resp = await fetch(url, {
|
|
446
|
+
method: "POST",
|
|
447
|
+
headers,
|
|
448
|
+
body: JSON.stringify({ token })
|
|
449
|
+
});
|
|
450
|
+
if (!resp.ok) return false;
|
|
451
|
+
const data = await resp.json();
|
|
452
|
+
return data.active === true;
|
|
453
|
+
} catch {
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
/* ---- Accessors --------------------------------------------------------- */
|
|
458
|
+
get skillCount() {
|
|
459
|
+
return this.skills.size;
|
|
460
|
+
}
|
|
461
|
+
get isInitialized() {
|
|
462
|
+
return this.initialized;
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
// src/skill-manifest.ts
|
|
469
|
+
var skill_manifest_exports = {};
|
|
470
|
+
__export(skill_manifest_exports, {
|
|
471
|
+
loadSkillsFromApi: () => loadSkillsFromApi,
|
|
472
|
+
loadSkillsFromDirectory: () => loadSkillsFromDirectory,
|
|
473
|
+
mergeSkills: () => mergeSkills,
|
|
474
|
+
parseSkillMd: () => parseSkillMd
|
|
475
|
+
});
|
|
476
|
+
import { readFileSync, existsSync, readdirSync } from "node:fs";
|
|
477
|
+
import { resolve, join } from "node:path";
|
|
478
|
+
function parseSkillMd(content) {
|
|
479
|
+
const lines = content.split("\n");
|
|
480
|
+
if (lines[0]?.trim() !== "---") return null;
|
|
481
|
+
let endIdx = -1;
|
|
482
|
+
for (let i = 1; i < lines.length; i++) {
|
|
483
|
+
if (lines[i]?.trim() === "---") {
|
|
484
|
+
endIdx = i;
|
|
485
|
+
break;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
if (endIdx === -1) return null;
|
|
489
|
+
const frontmatterLines = lines.slice(1, endIdx);
|
|
490
|
+
const frontmatter = parseSimpleYaml(frontmatterLines.join("\n"));
|
|
491
|
+
if (!frontmatter.name) return null;
|
|
492
|
+
const instructionLines = lines.slice(endIdx + 1);
|
|
493
|
+
const instructions = instructionLines.join("\n").trim();
|
|
494
|
+
const skill = {
|
|
495
|
+
name: frontmatter.name,
|
|
496
|
+
version: frontmatter.version,
|
|
497
|
+
description: frontmatter.description,
|
|
498
|
+
tags: frontmatter.tags,
|
|
499
|
+
inputSchema: frontmatter.schema,
|
|
500
|
+
slaDefinition: frontmatter.sla,
|
|
501
|
+
instructions: instructions || void 0
|
|
502
|
+
};
|
|
503
|
+
if (frontmatter.agentVault) {
|
|
504
|
+
const av = frontmatter.agentVault;
|
|
505
|
+
if (av.certification) skill.certificationTier = av.certification;
|
|
506
|
+
if (av.runtime?.capabilities) skill.toolsAllowed = av.runtime.capabilities;
|
|
507
|
+
if (av.runtime?.forbidden) skill.toolsDenied = av.runtime.forbidden;
|
|
508
|
+
if (av.runtime?.output_schema) skill.outputSchema = av.runtime.output_schema;
|
|
509
|
+
if (av.model?.routing) skill.modelRouting = av.model.routing;
|
|
510
|
+
if (av.model?.allowed) skill.allowedModels = av.model.allowed;
|
|
511
|
+
if (av.model?.default) skill.defaultModel = av.model.default;
|
|
512
|
+
if (av.integrity) skill.integrity = av.integrity;
|
|
513
|
+
if (av.requiredPolicies) skill.requiredPolicies = av.requiredPolicies;
|
|
514
|
+
}
|
|
515
|
+
return skill;
|
|
516
|
+
}
|
|
517
|
+
function parseSimpleYaml(yaml) {
|
|
518
|
+
const result = {};
|
|
519
|
+
const lines = yaml.split("\n");
|
|
520
|
+
const stack = [];
|
|
521
|
+
let currentObj = result;
|
|
522
|
+
function parseValue(raw) {
|
|
523
|
+
const value = raw.replace(/^["']|["']$/g, "");
|
|
524
|
+
const num = Number(value);
|
|
525
|
+
if (!isNaN(num) && value !== "") return num;
|
|
526
|
+
if (value === "true") return true;
|
|
527
|
+
if (value === "false") return false;
|
|
528
|
+
return value;
|
|
529
|
+
}
|
|
530
|
+
for (const line of lines) {
|
|
531
|
+
const trimmed = line.trim();
|
|
532
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
533
|
+
const indent = line.length - line.trimStart().length;
|
|
534
|
+
while (stack.length > 0 && indent <= stack[stack.length - 1].indent) {
|
|
535
|
+
const popped = stack.pop();
|
|
536
|
+
currentObj = stack.length > 0 ? stack[stack.length - 1].obj : result;
|
|
537
|
+
currentObj[popped.key] = popped.obj;
|
|
538
|
+
}
|
|
539
|
+
const inlineArrayMatch = trimmed.match(/^(\w[\w_-]*)\s*:\s*\[(.+)\]$/);
|
|
540
|
+
if (inlineArrayMatch) {
|
|
541
|
+
const key = inlineArrayMatch[1];
|
|
542
|
+
const values = inlineArrayMatch[2].split(",").map((v) => v.trim().replace(/^["']|["']$/g, ""));
|
|
543
|
+
if (stack.length > 0) {
|
|
544
|
+
stack[stack.length - 1].obj[key] = values;
|
|
545
|
+
} else {
|
|
546
|
+
currentObj[key] = values;
|
|
547
|
+
}
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
const kvMatch = trimmed.match(/^(\w[\w_-]*)\s*:\s*(.+)$/);
|
|
551
|
+
if (kvMatch) {
|
|
552
|
+
const key = kvMatch[1];
|
|
553
|
+
const val = parseValue(kvMatch[2]);
|
|
554
|
+
if (stack.length > 0) {
|
|
555
|
+
stack[stack.length - 1].obj[key] = val;
|
|
556
|
+
} else {
|
|
557
|
+
currentObj[key] = val;
|
|
558
|
+
}
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
const nestedMatch = trimmed.match(/^(\w[\w_-]*)\s*:$/);
|
|
562
|
+
if (nestedMatch) {
|
|
563
|
+
const key = nestedMatch[1];
|
|
564
|
+
const newObj = {};
|
|
565
|
+
stack.push({ key, obj: newObj, indent });
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
while (stack.length > 0) {
|
|
570
|
+
const popped = stack.pop();
|
|
571
|
+
const parent = stack.length > 0 ? stack[stack.length - 1].obj : result;
|
|
572
|
+
parent[popped.key] = popped.obj;
|
|
573
|
+
}
|
|
574
|
+
return result;
|
|
575
|
+
}
|
|
576
|
+
function loadSkillsFromDirectory(dir) {
|
|
577
|
+
const skills = [];
|
|
578
|
+
const absDir = resolve(dir);
|
|
579
|
+
if (!existsSync(absDir)) return skills;
|
|
580
|
+
const rootSkill = join(absDir, "SKILL.md");
|
|
581
|
+
if (existsSync(rootSkill)) {
|
|
582
|
+
const content = readFileSync(rootSkill, "utf-8");
|
|
583
|
+
const skill = parseSkillMd(content);
|
|
584
|
+
if (skill) skills.push(skill);
|
|
585
|
+
}
|
|
586
|
+
try {
|
|
587
|
+
const entries = readdirSync(absDir, { withFileTypes: true });
|
|
588
|
+
for (const entry of entries) {
|
|
589
|
+
if (entry.isDirectory()) {
|
|
590
|
+
const subSkill = join(absDir, entry.name, "SKILL.md");
|
|
591
|
+
if (existsSync(subSkill)) {
|
|
592
|
+
const content = readFileSync(subSkill, "utf-8");
|
|
593
|
+
const skill = parseSkillMd(content);
|
|
594
|
+
if (skill) skills.push(skill);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
} catch {
|
|
599
|
+
}
|
|
600
|
+
return skills;
|
|
601
|
+
}
|
|
602
|
+
async function loadSkillsFromApi(apiUrl, apiKey, hubId) {
|
|
603
|
+
try {
|
|
604
|
+
const res = await fetch(`${apiUrl}/api/v1/hub/identities/${hubId}/skills`, {
|
|
605
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
606
|
+
});
|
|
607
|
+
if (!res.ok) return [];
|
|
608
|
+
const data = await res.json();
|
|
609
|
+
return data.map((cap) => ({
|
|
610
|
+
name: cap.capability_name,
|
|
611
|
+
version: cap.capability_version,
|
|
612
|
+
description: cap.description,
|
|
613
|
+
inputSchema: cap.schema_definition,
|
|
614
|
+
slaDefinition: cap.sla_definition,
|
|
615
|
+
tags: cap.tags,
|
|
616
|
+
instructions: cap.instructions
|
|
617
|
+
}));
|
|
618
|
+
} catch {
|
|
619
|
+
return [];
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
function mergeSkills(local, remote) {
|
|
623
|
+
const byName = /* @__PURE__ */ new Map();
|
|
624
|
+
for (const skill of remote) {
|
|
625
|
+
byName.set(skill.name, skill);
|
|
626
|
+
}
|
|
627
|
+
for (const skill of local) {
|
|
628
|
+
byName.set(skill.name, skill);
|
|
629
|
+
}
|
|
630
|
+
const source = local.length > 0 && remote.length > 0 ? "merged" : local.length > 0 ? "local" : "remote";
|
|
631
|
+
return {
|
|
632
|
+
skills: Array.from(byName.values()),
|
|
633
|
+
source
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
var init_skill_manifest = __esm({
|
|
637
|
+
"src/skill-manifest.ts"() {
|
|
638
|
+
"use strict";
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
|
|
93
642
|
// src/openclaw-entry.ts
|
|
94
|
-
import { resolve } from "node:path";
|
|
643
|
+
import { resolve as pathResolve } from "node:path";
|
|
95
644
|
import { randomBytes } from "node:crypto";
|
|
96
645
|
|
|
97
646
|
// src/account-config.ts
|
|
@@ -289,146 +838,8 @@ function runWithTraceContext(ctx, fn) {
|
|
|
289
838
|
return traceStore.run(ctx, fn);
|
|
290
839
|
}
|
|
291
840
|
|
|
292
|
-
// src/http-handlers.ts
|
|
293
|
-
async function handleSendRequest(parsed, channel) {
|
|
294
|
-
const text = parsed.text;
|
|
295
|
-
if (!text || typeof text !== "string") {
|
|
296
|
-
return { status: 400, body: { ok: false, error: "Missing 'text' field" } };
|
|
297
|
-
}
|
|
298
|
-
try {
|
|
299
|
-
let target;
|
|
300
|
-
let a2aTarget = parsed.hub_address ?? parsed.a2a_address ?? parsed.a2aAddress;
|
|
301
|
-
if (!a2aTarget && parsed.channel_id && typeof parsed.channel_id === "string") {
|
|
302
|
-
const hubAddr = channel.resolveA2AChannelHub(parsed.channel_id);
|
|
303
|
-
if (hubAddr) a2aTarget = hubAddr;
|
|
304
|
-
}
|
|
305
|
-
if (a2aTarget && typeof a2aTarget === "string") {
|
|
306
|
-
target = { kind: "a2a", hubAddress: a2aTarget };
|
|
307
|
-
} else if (typeof parsed.room_id === "string") {
|
|
308
|
-
target = { kind: "room", roomId: parsed.room_id };
|
|
309
|
-
} else if (parsed.target === "context") {
|
|
310
|
-
target = { kind: "context" };
|
|
311
|
-
} else if (parsed.file_path && typeof parsed.file_path === "string") {
|
|
312
|
-
const receipt2 = await channel.deliver(
|
|
313
|
-
{ kind: "owner" },
|
|
314
|
-
{ type: "attachment", text, filePath: parsed.file_path },
|
|
315
|
-
{ topicId: parsed.topicId }
|
|
316
|
-
);
|
|
317
|
-
return {
|
|
318
|
-
status: receipt2.ok ? 200 : 500,
|
|
319
|
-
body: {
|
|
320
|
-
ok: receipt2.ok,
|
|
321
|
-
destination: receipt2.destination,
|
|
322
|
-
...receipt2.error ? { error: receipt2.error } : {}
|
|
323
|
-
}
|
|
324
|
-
};
|
|
325
|
-
} else {
|
|
326
|
-
target = { kind: "owner" };
|
|
327
|
-
}
|
|
328
|
-
const receipt = await channel.deliver(
|
|
329
|
-
target,
|
|
330
|
-
{ type: "text", text },
|
|
331
|
-
{
|
|
332
|
-
topicId: parsed.topicId,
|
|
333
|
-
priority: parsed.priority,
|
|
334
|
-
metadata: {
|
|
335
|
-
...parsed.metadata,
|
|
336
|
-
...parsed.message_type ? { message_type: parsed.message_type } : {}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
);
|
|
340
|
-
return {
|
|
341
|
-
status: receipt.ok ? 200 : 500,
|
|
342
|
-
body: {
|
|
343
|
-
ok: receipt.ok,
|
|
344
|
-
destination: receipt.destination,
|
|
345
|
-
...receipt.error ? { error: receipt.error } : {}
|
|
346
|
-
}
|
|
347
|
-
};
|
|
348
|
-
} catch (err) {
|
|
349
|
-
return { status: 500, body: { ok: false, error: String(err) } };
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
async function handleActionRequest(parsed, channel) {
|
|
353
|
-
if (!parsed.action || typeof parsed.action !== "string") {
|
|
354
|
-
return { status: 400, body: { ok: false, error: "Missing 'action' field" } };
|
|
355
|
-
}
|
|
356
|
-
try {
|
|
357
|
-
const confirmation = {
|
|
358
|
-
action: parsed.action,
|
|
359
|
-
status: parsed.status ?? "completed",
|
|
360
|
-
decisionId: parsed.decision_id,
|
|
361
|
-
detail: parsed.detail,
|
|
362
|
-
estimated_cost: parsed.estimated_cost
|
|
363
|
-
};
|
|
364
|
-
const target = parsed.room_id && typeof parsed.room_id === "string" ? { kind: "room", roomId: parsed.room_id } : { kind: "owner" };
|
|
365
|
-
const receipt = await channel.deliver(
|
|
366
|
-
target,
|
|
367
|
-
{ type: "action_confirmation", confirmation }
|
|
368
|
-
);
|
|
369
|
-
return {
|
|
370
|
-
status: receipt.ok ? 200 : 500,
|
|
371
|
-
body: {
|
|
372
|
-
ok: receipt.ok,
|
|
373
|
-
destination: receipt.destination,
|
|
374
|
-
...receipt.error ? { error: receipt.error } : {}
|
|
375
|
-
}
|
|
376
|
-
};
|
|
377
|
-
} catch (err) {
|
|
378
|
-
return { status: 500, body: { ok: false, error: String(err) } };
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
async function handleDecisionRequest(parsed, channel) {
|
|
382
|
-
const title = parsed.title;
|
|
383
|
-
if (!title || typeof title !== "string") {
|
|
384
|
-
return { status: 400, body: { ok: false, error: "Missing 'title' field" } };
|
|
385
|
-
}
|
|
386
|
-
const options = parsed.options;
|
|
387
|
-
if (!Array.isArray(options) || options.length < 2) {
|
|
388
|
-
return { status: 400, body: { ok: false, error: "'options' must be an array with at least 2 items" } };
|
|
389
|
-
}
|
|
390
|
-
for (const opt of options) {
|
|
391
|
-
if (!opt || typeof opt !== "object" || !opt.option_id || !opt.label) {
|
|
392
|
-
return { status: 400, body: { ok: false, error: "Each option must have 'option_id' and 'label'" } };
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
try {
|
|
396
|
-
const decision_id = await channel.sendDecisionRequest({
|
|
397
|
-
title,
|
|
398
|
-
description: parsed.description,
|
|
399
|
-
options,
|
|
400
|
-
context_refs: parsed.context_refs,
|
|
401
|
-
deadline: parsed.deadline,
|
|
402
|
-
auto_action: parsed.auto_action
|
|
403
|
-
});
|
|
404
|
-
return { status: 200, body: { ok: true, decision_id } };
|
|
405
|
-
} catch (err) {
|
|
406
|
-
return { status: 500, body: { ok: false, error: String(err) } };
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
function handleStatusRequest(channel) {
|
|
410
|
-
return {
|
|
411
|
-
status: 200,
|
|
412
|
-
body: {
|
|
413
|
-
ok: true,
|
|
414
|
-
state: channel.state,
|
|
415
|
-
deviceId: channel.deviceId ?? void 0,
|
|
416
|
-
sessions: channel.sessionCount
|
|
417
|
-
}
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
function handleTargetsRequest(channel) {
|
|
421
|
-
return {
|
|
422
|
-
status: 200,
|
|
423
|
-
body: {
|
|
424
|
-
ok: true,
|
|
425
|
-
targets: channel.listTargets(),
|
|
426
|
-
context: channel.lastInboundRoomId ? { kind: "room", roomId: channel.lastInboundRoomId } : { kind: "owner" }
|
|
427
|
-
}
|
|
428
|
-
};
|
|
429
|
-
}
|
|
430
|
-
|
|
431
841
|
// src/openclaw-entry.ts
|
|
842
|
+
init_http_handlers();
|
|
432
843
|
init_openclaw_compat();
|
|
433
844
|
|
|
434
845
|
// src/types.ts
|
|
@@ -1104,7 +1515,7 @@ var agentVaultPlugin = {
|
|
|
1104
1515
|
"AgentVault channel not configured. Run: npx @agentvault/agentvault setup --token=av_tok_...\nThen restart OpenClaw."
|
|
1105
1516
|
);
|
|
1106
1517
|
}
|
|
1107
|
-
const dataDir =
|
|
1518
|
+
const dataDir = pathResolve(account.dataDir.replace(/^~/, __require("node:os").homedir()));
|
|
1108
1519
|
_log?.(`[AgentVault] starting (dataDir=${dataDir})`);
|
|
1109
1520
|
await new Promise((resolve2, reject) => {
|
|
1110
1521
|
let channel;
|
|
@@ -1114,7 +1525,7 @@ var agentVaultPlugin = {
|
|
|
1114
1525
|
resolve2();
|
|
1115
1526
|
};
|
|
1116
1527
|
abortSignal?.addEventListener("abort", () => void onAbort());
|
|
1117
|
-
import("./index.js").then(({ SecureChannel }) => {
|
|
1528
|
+
import("./index.js").then(async ({ SecureChannel }) => {
|
|
1118
1529
|
channel = new SecureChannel({
|
|
1119
1530
|
inviteToken: "",
|
|
1120
1531
|
dataDir,
|
|
@@ -1193,6 +1604,38 @@ var agentVaultPlugin = {
|
|
|
1193
1604
|
channel.on("error", (err) => {
|
|
1194
1605
|
_log?.(`[AgentVault] channel error (non-fatal): ${String(err)}`);
|
|
1195
1606
|
});
|
|
1607
|
+
try {
|
|
1608
|
+
const { AgentVaultMcpServer: AgentVaultMcpServer2 } = await Promise.resolve().then(() => (init_mcp_server(), mcp_server_exports));
|
|
1609
|
+
const { loadSkillsFromDirectory: loadSkillsFromDirectory2 } = await Promise.resolve().then(() => (init_skill_manifest(), skill_manifest_exports));
|
|
1610
|
+
const mcpServer = new AgentVaultMcpServer2({
|
|
1611
|
+
agentName: account.agentName ?? "agent",
|
|
1612
|
+
apiUrl: account.apiUrl ?? "https://api.agentvault.chat",
|
|
1613
|
+
onInvoke: async (skillName, args) => {
|
|
1614
|
+
const text = JSON.stringify({ skill: skillName, args });
|
|
1615
|
+
const receipt = await channel.deliver(
|
|
1616
|
+
{ kind: "owner" },
|
|
1617
|
+
{ type: "text", text },
|
|
1618
|
+
{ metadata: { message_type: "skill_invocation", skill_name: skillName } }
|
|
1619
|
+
);
|
|
1620
|
+
return { invoked: true, skill: skillName, delivered: receipt.ok };
|
|
1621
|
+
}
|
|
1622
|
+
});
|
|
1623
|
+
const workspaceDir = dataDir;
|
|
1624
|
+
const skills = loadSkillsFromDirectory2(workspaceDir);
|
|
1625
|
+
for (const skill of skills) {
|
|
1626
|
+
mcpServer.registerSkill(skill);
|
|
1627
|
+
}
|
|
1628
|
+
const skillsSubDir = pathResolve(dataDir, "skills");
|
|
1629
|
+
const subDirSkills = loadSkillsFromDirectory2(skillsSubDir);
|
|
1630
|
+
for (const skill of subDirSkills) {
|
|
1631
|
+
mcpServer.registerSkill(skill);
|
|
1632
|
+
}
|
|
1633
|
+
mcpServer.initialize();
|
|
1634
|
+
channel.setMcpServer(mcpServer);
|
|
1635
|
+
_log?.(`[AgentVault] MCP server activated with ${mcpServer.skillCount} skill(s)`);
|
|
1636
|
+
} catch (err) {
|
|
1637
|
+
_log?.(`[AgentVault] MCP server activation failed (non-fatal): ${String(err)}`);
|
|
1638
|
+
}
|
|
1196
1639
|
const httpPort = account.httpPort ?? 18790;
|
|
1197
1640
|
channel.on("ready", () => {
|
|
1198
1641
|
channel.startHttpServer(httpPort);
|
|
@@ -1399,6 +1842,19 @@ var openclaw_entry_default = {
|
|
|
1399
1842
|
return { status: result.status, body: result.body };
|
|
1400
1843
|
}
|
|
1401
1844
|
});
|
|
1845
|
+
api.registerHttpRoute({
|
|
1846
|
+
path: "/agentvault/mcp-config",
|
|
1847
|
+
method: "GET",
|
|
1848
|
+
handler: async () => {
|
|
1849
|
+
const ch = _channels.values().next().value;
|
|
1850
|
+
if (!ch) return { status: 503, body: { ok: false, error: "Channel not started" } };
|
|
1851
|
+
const { handleMcpConfigRequest: handleMcpConfigRequest2 } = await Promise.resolve().then(() => (init_http_handlers(), http_handlers_exports));
|
|
1852
|
+
const agentName = ch.config?.agentName ?? "agent";
|
|
1853
|
+
const mcpSkillCount = ch.mcpServer?.skillCount ?? 0;
|
|
1854
|
+
const result = handleMcpConfigRequest2(agentName, 18790, mcpSkillCount);
|
|
1855
|
+
return { status: result.status, body: result.body };
|
|
1856
|
+
}
|
|
1857
|
+
});
|
|
1402
1858
|
isUsingManagedRoutes = true;
|
|
1403
1859
|
} catch {
|
|
1404
1860
|
}
|