@openclaw-cloud/agent-controller 1.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/agent-controller.js +11 -2
- package/dist/api.d.ts +4 -0
- package/dist/api.js +8 -0
- package/dist/api.js.map +1 -1
- package/dist/commands/channel-server.d.ts +17 -0
- package/dist/commands/channel-server.js +71 -0
- package/dist/commands/channel-server.js.map +1 -0
- package/dist/config-file.js +6 -0
- package/dist/config-file.js.map +1 -1
- package/dist/config.d.ts +10 -0
- package/dist/config.js +20 -0
- package/dist/config.js.map +1 -1
- package/dist/connection.d.ts +2 -0
- package/dist/connection.js +2 -0
- package/dist/connection.js.map +1 -1
- package/dist/handlers/board-handler.js +56 -20
- package/dist/handlers/board-handler.js.map +1 -1
- package/dist/handlers/chat.d.ts +17 -0
- package/dist/handlers/chat.js +93 -0
- package/dist/handlers/chat.js.map +1 -1
- package/dist/handlers/memory.d.ts +7 -0
- package/dist/handlers/memory.js +41 -0
- package/dist/handlers/memory.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +73 -1
- package/dist/index.js.map +1 -1
- package/dist/memory-mcp-server.d.ts +8 -0
- package/dist/memory-mcp-server.js +291 -0
- package/dist/memory-mcp-server.js.map +1 -0
- package/dist/providers/claude-code/channel-server.d.ts +60 -0
- package/dist/providers/claude-code/channel-server.js +155 -0
- package/dist/providers/claude-code/channel-server.js.map +1 -0
- package/dist/providers/claude-code/index.d.ts +68 -0
- package/dist/providers/claude-code/index.js +276 -0
- package/dist/providers/claude-code/index.js.map +1 -0
- package/dist/providers/claude-code/login-flow.d.ts +26 -0
- package/dist/providers/claude-code/login-flow.js +129 -0
- package/dist/providers/claude-code/login-flow.js.map +1 -0
- package/dist/providers/claude-code/settings-writer.d.ts +29 -0
- package/dist/providers/claude-code/settings-writer.js +79 -0
- package/dist/providers/claude-code/settings-writer.js.map +1 -0
- package/dist/providers/claude-code/socket-bridge.d.ts +88 -0
- package/dist/providers/claude-code/socket-bridge.js +302 -0
- package/dist/providers/claude-code/socket-bridge.js.map +1 -0
- package/dist/providers/claude-code/spawn-claude.d.ts +44 -0
- package/dist/providers/claude-code/spawn-claude.js +108 -0
- package/dist/providers/claude-code/spawn-claude.js.map +1 -0
- package/dist/providers/index.d.ts +3 -1
- package/dist/providers/index.js +12 -0
- package/dist/providers/index.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/utils/knowledge-graph.d.ts +66 -0
- package/dist/utils/knowledge-graph.js +163 -0
- package/dist/utils/knowledge-graph.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { createServer } from 'node:http';
|
|
3
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Zod schemas — exact match with @modelcontextprotocol/server-memory
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
const EntitySchema = z.object({
|
|
10
|
+
name: z.string().describe('The name of the entity'),
|
|
11
|
+
entityType: z.string().describe('The type of the entity'),
|
|
12
|
+
observations: z
|
|
13
|
+
.array(z.string())
|
|
14
|
+
.describe('An array of observation contents associated with the entity'),
|
|
15
|
+
});
|
|
16
|
+
const RelationSchema = z.object({
|
|
17
|
+
from: z.string().describe('The name of the entity where the relation starts'),
|
|
18
|
+
to: z.string().describe('The name of the entity where the relation ends'),
|
|
19
|
+
relationType: z.string().describe('The type of the relation'),
|
|
20
|
+
});
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// MCP server factory
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
export function createMemoryMcpServer(manager) {
|
|
25
|
+
const server = new McpServer({
|
|
26
|
+
name: 'agent-memory',
|
|
27
|
+
version: '1.0.0',
|
|
28
|
+
});
|
|
29
|
+
// --- create_entities ---
|
|
30
|
+
server.registerTool('create_entities', {
|
|
31
|
+
title: 'Create Entities',
|
|
32
|
+
description: 'Create multiple new entities in the knowledge graph',
|
|
33
|
+
inputSchema: { entities: z.array(EntitySchema) },
|
|
34
|
+
outputSchema: { entities: z.array(EntitySchema) },
|
|
35
|
+
}, async ({ entities }) => {
|
|
36
|
+
const result = await manager.createEntities(entities);
|
|
37
|
+
return {
|
|
38
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
39
|
+
structuredContent: { entities: result },
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
// --- create_relations ---
|
|
43
|
+
server.registerTool('create_relations', {
|
|
44
|
+
title: 'Create Relations',
|
|
45
|
+
description: 'Create multiple new relations between entities in the knowledge graph. Relations should be in active voice',
|
|
46
|
+
inputSchema: { relations: z.array(RelationSchema) },
|
|
47
|
+
outputSchema: { relations: z.array(RelationSchema) },
|
|
48
|
+
}, async ({ relations }) => {
|
|
49
|
+
const result = await manager.createRelations(relations);
|
|
50
|
+
return {
|
|
51
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
52
|
+
structuredContent: { relations: result },
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
// --- add_observations ---
|
|
56
|
+
server.registerTool('add_observations', {
|
|
57
|
+
title: 'Add Observations',
|
|
58
|
+
description: 'Add new observations to existing entities in the knowledge graph',
|
|
59
|
+
inputSchema: {
|
|
60
|
+
observations: z.array(z.object({
|
|
61
|
+
entityName: z.string().describe('The name of the entity to add the observations to'),
|
|
62
|
+
contents: z.array(z.string()).describe('An array of observation contents to add'),
|
|
63
|
+
})),
|
|
64
|
+
},
|
|
65
|
+
outputSchema: {
|
|
66
|
+
results: z.array(z.object({
|
|
67
|
+
entityName: z.string(),
|
|
68
|
+
addedObservations: z.array(z.string()),
|
|
69
|
+
})),
|
|
70
|
+
},
|
|
71
|
+
}, async ({ observations }) => {
|
|
72
|
+
const result = await manager.addObservations(observations);
|
|
73
|
+
return {
|
|
74
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
75
|
+
structuredContent: { results: result },
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
// --- delete_entities ---
|
|
79
|
+
server.registerTool('delete_entities', {
|
|
80
|
+
title: 'Delete Entities',
|
|
81
|
+
description: 'Delete multiple entities and their associated relations from the knowledge graph',
|
|
82
|
+
inputSchema: {
|
|
83
|
+
entityNames: z.array(z.string()).describe('An array of entity names to delete'),
|
|
84
|
+
},
|
|
85
|
+
outputSchema: {
|
|
86
|
+
success: z.boolean(),
|
|
87
|
+
message: z.string(),
|
|
88
|
+
},
|
|
89
|
+
}, async ({ entityNames }) => {
|
|
90
|
+
await manager.deleteEntities(entityNames);
|
|
91
|
+
return {
|
|
92
|
+
content: [{ type: 'text', text: 'Entities deleted successfully' }],
|
|
93
|
+
structuredContent: { success: true, message: 'Entities deleted successfully' },
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
// --- delete_observations ---
|
|
97
|
+
server.registerTool('delete_observations', {
|
|
98
|
+
title: 'Delete Observations',
|
|
99
|
+
description: 'Delete specific observations from entities in the knowledge graph',
|
|
100
|
+
inputSchema: {
|
|
101
|
+
deletions: z.array(z.object({
|
|
102
|
+
entityName: z.string().describe('The name of the entity containing the observations'),
|
|
103
|
+
observations: z.array(z.string()).describe('An array of observations to delete'),
|
|
104
|
+
})),
|
|
105
|
+
},
|
|
106
|
+
outputSchema: {
|
|
107
|
+
success: z.boolean(),
|
|
108
|
+
message: z.string(),
|
|
109
|
+
},
|
|
110
|
+
}, async ({ deletions }) => {
|
|
111
|
+
await manager.deleteObservations(deletions);
|
|
112
|
+
return {
|
|
113
|
+
content: [{ type: 'text', text: 'Observations deleted successfully' }],
|
|
114
|
+
structuredContent: { success: true, message: 'Observations deleted successfully' },
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
// --- delete_relations ---
|
|
118
|
+
server.registerTool('delete_relations', {
|
|
119
|
+
title: 'Delete Relations',
|
|
120
|
+
description: 'Delete multiple relations from the knowledge graph',
|
|
121
|
+
inputSchema: {
|
|
122
|
+
relations: z.array(RelationSchema).describe('An array of relations to delete'),
|
|
123
|
+
},
|
|
124
|
+
outputSchema: {
|
|
125
|
+
success: z.boolean(),
|
|
126
|
+
message: z.string(),
|
|
127
|
+
},
|
|
128
|
+
}, async ({ relations }) => {
|
|
129
|
+
await manager.deleteRelations(relations);
|
|
130
|
+
return {
|
|
131
|
+
content: [{ type: 'text', text: 'Relations deleted successfully' }],
|
|
132
|
+
structuredContent: { success: true, message: 'Relations deleted successfully' },
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
// --- read_graph ---
|
|
136
|
+
server.registerTool('read_graph', {
|
|
137
|
+
title: 'Read Graph',
|
|
138
|
+
description: 'Read the entire knowledge graph',
|
|
139
|
+
inputSchema: {},
|
|
140
|
+
outputSchema: {
|
|
141
|
+
entities: z.array(EntitySchema),
|
|
142
|
+
relations: z.array(RelationSchema),
|
|
143
|
+
},
|
|
144
|
+
}, async () => {
|
|
145
|
+
const graph = manager.readGraph();
|
|
146
|
+
return {
|
|
147
|
+
content: [{ type: 'text', text: JSON.stringify(graph, null, 2) }],
|
|
148
|
+
structuredContent: { ...graph },
|
|
149
|
+
};
|
|
150
|
+
});
|
|
151
|
+
// --- search_nodes ---
|
|
152
|
+
server.registerTool('search_nodes', {
|
|
153
|
+
title: 'Search Nodes',
|
|
154
|
+
description: 'Search for nodes in the knowledge graph based on a query',
|
|
155
|
+
inputSchema: {
|
|
156
|
+
query: z
|
|
157
|
+
.string()
|
|
158
|
+
.describe('The search query to match against entity names, types, and observation content'),
|
|
159
|
+
},
|
|
160
|
+
outputSchema: {
|
|
161
|
+
entities: z.array(EntitySchema),
|
|
162
|
+
relations: z.array(RelationSchema),
|
|
163
|
+
},
|
|
164
|
+
}, async ({ query }) => {
|
|
165
|
+
const graph = manager.searchNodes(query);
|
|
166
|
+
return {
|
|
167
|
+
content: [{ type: 'text', text: JSON.stringify(graph, null, 2) }],
|
|
168
|
+
structuredContent: { ...graph },
|
|
169
|
+
};
|
|
170
|
+
});
|
|
171
|
+
// --- open_nodes ---
|
|
172
|
+
server.registerTool('open_nodes', {
|
|
173
|
+
title: 'Open Nodes',
|
|
174
|
+
description: 'Open specific nodes in the knowledge graph by their names',
|
|
175
|
+
inputSchema: {
|
|
176
|
+
names: z.array(z.string()).describe('An array of entity names to retrieve'),
|
|
177
|
+
},
|
|
178
|
+
outputSchema: {
|
|
179
|
+
entities: z.array(EntitySchema),
|
|
180
|
+
relations: z.array(RelationSchema),
|
|
181
|
+
},
|
|
182
|
+
}, async ({ names }) => {
|
|
183
|
+
const graph = manager.openNodes(names);
|
|
184
|
+
return {
|
|
185
|
+
content: [{ type: 'text', text: JSON.stringify(graph, null, 2) }],
|
|
186
|
+
structuredContent: { ...graph },
|
|
187
|
+
};
|
|
188
|
+
});
|
|
189
|
+
return server;
|
|
190
|
+
}
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
// HTTP transport — Node.js http server on localhost:3457
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
const MEMORY_MCP_PORT = 3457;
|
|
195
|
+
/**
|
|
196
|
+
* Start the MCP memory server on localhost.
|
|
197
|
+
* Returns a close function for graceful shutdown.
|
|
198
|
+
*/
|
|
199
|
+
export async function startMemoryMcpServer(manager) {
|
|
200
|
+
// Map of sessionId -> { transport, server } for stateful session management.
|
|
201
|
+
// Each session gets its own McpServer instance because the SDK only allows
|
|
202
|
+
// one transport per McpServer (Protocol.connect throws on second call).
|
|
203
|
+
const sessions = new Map();
|
|
204
|
+
const httpServer = createServer(async (req, res) => {
|
|
205
|
+
const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
|
|
206
|
+
if (url.pathname !== '/mcp') {
|
|
207
|
+
res.writeHead(404);
|
|
208
|
+
res.end('Not found');
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
// Parse body for POST
|
|
212
|
+
const MAX_BODY_SIZE = 10 * 1024 * 1024; // 10MB
|
|
213
|
+
let body = undefined;
|
|
214
|
+
if (req.method === 'POST') {
|
|
215
|
+
const chunks = [];
|
|
216
|
+
let totalSize = 0;
|
|
217
|
+
let aborted = false;
|
|
218
|
+
for await (const chunk of req) {
|
|
219
|
+
const buf = typeof chunk === 'string' ? Buffer.from(chunk) : chunk;
|
|
220
|
+
totalSize += buf.length;
|
|
221
|
+
if (totalSize > MAX_BODY_SIZE) {
|
|
222
|
+
res.writeHead(413, { 'Content-Type': 'text/plain' });
|
|
223
|
+
res.end('Payload too large');
|
|
224
|
+
req.destroy();
|
|
225
|
+
aborted = true;
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
chunks.push(buf);
|
|
229
|
+
}
|
|
230
|
+
if (aborted)
|
|
231
|
+
return;
|
|
232
|
+
try {
|
|
233
|
+
body = JSON.parse(Buffer.concat(chunks).toString('utf-8'));
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
237
|
+
res.end(JSON.stringify({ error: 'Invalid JSON' }));
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
// Check for existing session
|
|
242
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
243
|
+
if (sessionId && sessions.has(sessionId)) {
|
|
244
|
+
const session = sessions.get(sessionId);
|
|
245
|
+
await session.transport.handleRequest(req, res, body);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
// Handle DELETE for session termination
|
|
249
|
+
if (req.method === 'DELETE') {
|
|
250
|
+
if (sessionId && sessions.has(sessionId)) {
|
|
251
|
+
const session = sessions.get(sessionId);
|
|
252
|
+
await session.transport.close();
|
|
253
|
+
await session.server.close();
|
|
254
|
+
sessions.delete(sessionId);
|
|
255
|
+
}
|
|
256
|
+
res.writeHead(200);
|
|
257
|
+
res.end();
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
// New session — create a fresh McpServer + transport pair
|
|
261
|
+
const mcpServer = createMemoryMcpServer(manager);
|
|
262
|
+
const transport = new StreamableHTTPServerTransport({
|
|
263
|
+
sessionIdGenerator: () => randomUUID(),
|
|
264
|
+
});
|
|
265
|
+
transport.onclose = () => {
|
|
266
|
+
if (transport.sessionId) {
|
|
267
|
+
sessions.delete(transport.sessionId);
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
await mcpServer.connect(transport);
|
|
271
|
+
await transport.handleRequest(req, res, body);
|
|
272
|
+
// sessionId is assigned during handleRequest (after initialize handshake)
|
|
273
|
+
if (transport.sessionId) {
|
|
274
|
+
sessions.set(transport.sessionId, { transport, server: mcpServer });
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
return new Promise((resolve) => {
|
|
278
|
+
httpServer.listen(MEMORY_MCP_PORT, '127.0.0.1', () => {
|
|
279
|
+
console.log(`[memory-mcp] MCP memory server listening on http://127.0.0.1:${MEMORY_MCP_PORT}/mcp`);
|
|
280
|
+
resolve(() => {
|
|
281
|
+
for (const session of sessions.values()) {
|
|
282
|
+
session.transport.close().catch(() => { });
|
|
283
|
+
session.server.close().catch(() => { });
|
|
284
|
+
}
|
|
285
|
+
sessions.clear();
|
|
286
|
+
httpServer.close();
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
//# sourceMappingURL=memory-mcp-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-mcp-server.js","sourceRoot":"","sources":["../src/memory-mcp-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
3
|
+
export interface ChannelReplyMeta {
|
|
4
|
+
chat_id?: string;
|
|
5
|
+
message_id?: string;
|
|
6
|
+
source?: string;
|
|
7
|
+
user?: string;
|
|
8
|
+
ts?: string;
|
|
9
|
+
[k: string]: unknown;
|
|
10
|
+
}
|
|
11
|
+
export interface ChannelReply {
|
|
12
|
+
text: string;
|
|
13
|
+
reply_to?: string;
|
|
14
|
+
files?: string[];
|
|
15
|
+
}
|
|
16
|
+
export interface ChannelMessageMeta {
|
|
17
|
+
chat_id: string;
|
|
18
|
+
source: string;
|
|
19
|
+
message_id?: string;
|
|
20
|
+
user?: string;
|
|
21
|
+
ts?: string;
|
|
22
|
+
file_path?: string;
|
|
23
|
+
}
|
|
24
|
+
export type ReplyHandler = (reply: ChannelReply, tool: 'reply' | 'edit_message') => void;
|
|
25
|
+
/**
|
|
26
|
+
* MCP channel server — stdio-based, modeled after anthropics/claude-plugins-official
|
|
27
|
+
* `fakechat/server.ts`. When Claude Code is launched with
|
|
28
|
+
* `--channels plugin:<plugin-name>` and a `.mcp.json` entry that spawns this
|
|
29
|
+
* process, it advertises the `claude/channel` experimental capability.
|
|
30
|
+
*
|
|
31
|
+
* Inbound (we → Claude): `pushChatMessage(content, meta)` emits an MCP
|
|
32
|
+
* notification `notifications/claude/channel`.
|
|
33
|
+
*
|
|
34
|
+
* Outbound (Claude → we): Claude calls our `reply` / `edit_message` tool.
|
|
35
|
+
* Listeners registered via `onReply(handler)` receive the call args.
|
|
36
|
+
*/
|
|
37
|
+
export declare class ClaudeChannelServer extends EventEmitter {
|
|
38
|
+
private readonly opts;
|
|
39
|
+
private server;
|
|
40
|
+
private transport;
|
|
41
|
+
private started;
|
|
42
|
+
constructor(opts?: {
|
|
43
|
+
name?: string;
|
|
44
|
+
version?: string;
|
|
45
|
+
instructions?: string;
|
|
46
|
+
});
|
|
47
|
+
private registerHandlers;
|
|
48
|
+
/**
|
|
49
|
+
* Start the MCP server on a given transport (stdio by default).
|
|
50
|
+
* Pass a custom transport for in-process tests.
|
|
51
|
+
*/
|
|
52
|
+
start(transport?: Transport): Promise<void>;
|
|
53
|
+
/** Emit a channel notification to Claude. */
|
|
54
|
+
pushChatMessage(content: string, meta: ChannelMessageMeta): Promise<void>;
|
|
55
|
+
/** Register a callback for incoming reply/edit tool calls from Claude. */
|
|
56
|
+
onReply(handler: ReplyHandler): () => void;
|
|
57
|
+
/** Graceful shutdown. */
|
|
58
|
+
stop(): Promise<void>;
|
|
59
|
+
isStarted(): boolean;
|
|
60
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
/**
|
|
6
|
+
* MCP channel server — stdio-based, modeled after anthropics/claude-plugins-official
|
|
7
|
+
* `fakechat/server.ts`. When Claude Code is launched with
|
|
8
|
+
* `--channels plugin:<plugin-name>` and a `.mcp.json` entry that spawns this
|
|
9
|
+
* process, it advertises the `claude/channel` experimental capability.
|
|
10
|
+
*
|
|
11
|
+
* Inbound (we → Claude): `pushChatMessage(content, meta)` emits an MCP
|
|
12
|
+
* notification `notifications/claude/channel`.
|
|
13
|
+
*
|
|
14
|
+
* Outbound (Claude → we): Claude calls our `reply` / `edit_message` tool.
|
|
15
|
+
* Listeners registered via `onReply(handler)` receive the call args.
|
|
16
|
+
*/
|
|
17
|
+
export class ClaudeChannelServer extends EventEmitter {
|
|
18
|
+
opts;
|
|
19
|
+
server;
|
|
20
|
+
transport = null;
|
|
21
|
+
started = false;
|
|
22
|
+
constructor(opts = {}) {
|
|
23
|
+
super();
|
|
24
|
+
this.opts = opts;
|
|
25
|
+
this.server = new Server({
|
|
26
|
+
name: opts.name ?? 'agent-controller',
|
|
27
|
+
version: opts.version ?? '1.0.0',
|
|
28
|
+
}, {
|
|
29
|
+
capabilities: {
|
|
30
|
+
tools: {},
|
|
31
|
+
experimental: { 'claude/channel': {} },
|
|
32
|
+
},
|
|
33
|
+
instructions: opts.instructions ??
|
|
34
|
+
'OpenClaw agent-controller channel. Inbound messages arrive as <channel source="..." chat_id="..."> tags. Always reply via the `reply` tool — your transcript output is not forwarded to the user.',
|
|
35
|
+
});
|
|
36
|
+
this.registerHandlers();
|
|
37
|
+
}
|
|
38
|
+
registerHandlers() {
|
|
39
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
40
|
+
tools: [
|
|
41
|
+
{
|
|
42
|
+
name: 'reply',
|
|
43
|
+
description: 'Send a message to the user. Pass `reply_to` for quote-reply, `files` for attachments.',
|
|
44
|
+
inputSchema: {
|
|
45
|
+
type: 'object',
|
|
46
|
+
properties: {
|
|
47
|
+
text: { type: 'string' },
|
|
48
|
+
reply_to: { type: 'string' },
|
|
49
|
+
files: { type: 'array', items: { type: 'string' } },
|
|
50
|
+
},
|
|
51
|
+
required: ['text'],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'edit_message',
|
|
56
|
+
description: 'Edit a previously-sent message in place.',
|
|
57
|
+
inputSchema: {
|
|
58
|
+
type: 'object',
|
|
59
|
+
properties: {
|
|
60
|
+
message_id: { type: 'string' },
|
|
61
|
+
text: { type: 'string' },
|
|
62
|
+
},
|
|
63
|
+
required: ['message_id', 'text'],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
}));
|
|
68
|
+
this.server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
69
|
+
const args = (req.params.arguments ?? {});
|
|
70
|
+
try {
|
|
71
|
+
switch (req.params.name) {
|
|
72
|
+
case 'reply': {
|
|
73
|
+
const reply = {
|
|
74
|
+
text: String(args.text ?? ''),
|
|
75
|
+
reply_to: typeof args.reply_to === 'string' ? args.reply_to : undefined,
|
|
76
|
+
files: Array.isArray(args.files) ? args.files : undefined,
|
|
77
|
+
};
|
|
78
|
+
this.emit('reply', reply, 'reply');
|
|
79
|
+
return { content: [{ type: 'text', text: 'ok' }] };
|
|
80
|
+
}
|
|
81
|
+
case 'edit_message': {
|
|
82
|
+
const reply = {
|
|
83
|
+
text: String(args.text ?? ''),
|
|
84
|
+
reply_to: typeof args.message_id === 'string' ? args.message_id : undefined,
|
|
85
|
+
};
|
|
86
|
+
this.emit('reply', reply, 'edit_message');
|
|
87
|
+
return { content: [{ type: 'text', text: 'ok' }] };
|
|
88
|
+
}
|
|
89
|
+
default:
|
|
90
|
+
return {
|
|
91
|
+
content: [{ type: 'text', text: `unknown tool: ${req.params.name}` }],
|
|
92
|
+
isError: true,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
return {
|
|
98
|
+
content: [
|
|
99
|
+
{
|
|
100
|
+
type: 'text',
|
|
101
|
+
text: `${req.params.name} failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
isError: true,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Start the MCP server on a given transport (stdio by default).
|
|
111
|
+
* Pass a custom transport for in-process tests.
|
|
112
|
+
*/
|
|
113
|
+
async start(transport) {
|
|
114
|
+
if (this.started)
|
|
115
|
+
return;
|
|
116
|
+
this.transport = transport ?? new StdioServerTransport();
|
|
117
|
+
await this.server.connect(this.transport);
|
|
118
|
+
this.started = true;
|
|
119
|
+
}
|
|
120
|
+
/** Emit a channel notification to Claude. */
|
|
121
|
+
async pushChatMessage(content, meta) {
|
|
122
|
+
await this.server.notification({
|
|
123
|
+
method: 'notifications/claude/channel',
|
|
124
|
+
params: {
|
|
125
|
+
content,
|
|
126
|
+
meta: {
|
|
127
|
+
...meta,
|
|
128
|
+
ts: meta.ts ?? new Date().toISOString(),
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
/** Register a callback for incoming reply/edit tool calls from Claude. */
|
|
134
|
+
onReply(handler) {
|
|
135
|
+
this.on('reply', handler);
|
|
136
|
+
return () => this.off('reply', handler);
|
|
137
|
+
}
|
|
138
|
+
/** Graceful shutdown. */
|
|
139
|
+
async stop() {
|
|
140
|
+
if (!this.started)
|
|
141
|
+
return;
|
|
142
|
+
try {
|
|
143
|
+
await this.server.close();
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
/* ignore */
|
|
147
|
+
}
|
|
148
|
+
this.started = false;
|
|
149
|
+
this.transport = null;
|
|
150
|
+
}
|
|
151
|
+
isStarted() {
|
|
152
|
+
return this.started;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=channel-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-server.js","sourceRoot":"","sources":["../../../src/providers/claude-code/channel-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { IAgentProvider, AgentHealthStatus, SendMessageParams } from '../types.js';
|
|
2
|
+
import type { ChatSession, ChatMessage } from '../chat-types.js';
|
|
3
|
+
import type { ChannelReply, ChannelReplyMeta, ChannelMessageMeta } from './channel-server.js';
|
|
4
|
+
export interface ClaudeCodeProviderConfig {
|
|
5
|
+
workspaceDir: string;
|
|
6
|
+
anthropicApiKey?: string;
|
|
7
|
+
anthropicAuthToken?: string;
|
|
8
|
+
claudeCodeOauthToken?: string;
|
|
9
|
+
/** Content for CLAUDE.md in the workspace (persona / bootstrap). */
|
|
10
|
+
claudeMdContent?: string;
|
|
11
|
+
/** Backend URL for openclaw-backend MCP entry. */
|
|
12
|
+
backendUrl?: string;
|
|
13
|
+
/** Agent token for openclaw-backend MCP auth. */
|
|
14
|
+
agentToken?: string;
|
|
15
|
+
/** Override socket path (tests). */
|
|
16
|
+
socketPath?: string;
|
|
17
|
+
/** Absolute path to the `agent-controller` executable used to spawn the
|
|
18
|
+
* channel-server worker. Defaults to `agent-controller` on PATH. */
|
|
19
|
+
agentControllerBin?: string;
|
|
20
|
+
}
|
|
21
|
+
export type ReplyPublisher = (reply: ChannelReply, tool: 'reply' | 'edit_message', meta: ChannelReplyMeta) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Claude Code provider — orchestrates a local Claude Code subprocess plus a
|
|
24
|
+
* channel-server worker that bridges Claude's stdio MCP channel into this
|
|
25
|
+
* controller process via a Unix domain socket.
|
|
26
|
+
*
|
|
27
|
+
* Architecture:
|
|
28
|
+
* [Claude Code] ── stdio MCP ──> [agent-controller channel-server worker]
|
|
29
|
+
* ── unix socket ──> [main agent-controller]
|
|
30
|
+
*
|
|
31
|
+
* See `socket-bridge.ts` for the wire protocol.
|
|
32
|
+
*/
|
|
33
|
+
export declare class ClaudeCodeProvider implements IAgentProvider {
|
|
34
|
+
private readonly cfg;
|
|
35
|
+
readonly name = "claude-code";
|
|
36
|
+
private _connected;
|
|
37
|
+
private bridge;
|
|
38
|
+
private claude;
|
|
39
|
+
private lastHeartbeatAt;
|
|
40
|
+
private pendingLoginUrl;
|
|
41
|
+
private pendingCodeResolvers;
|
|
42
|
+
private replyPublisher;
|
|
43
|
+
constructor(cfg: ClaudeCodeProviderConfig);
|
|
44
|
+
/** Register an outbound publisher; called by the chat handler at startup. */
|
|
45
|
+
setReplyPublisher(fn: ReplyPublisher): void;
|
|
46
|
+
/** Used by the chat handler to feed user-submitted codes into the login flow. */
|
|
47
|
+
submitLoginCode(code: string): boolean;
|
|
48
|
+
/** The chat handler checks this to short-circuit the first message with the login URL. */
|
|
49
|
+
getPendingLoginUrl(): string | null;
|
|
50
|
+
/** Push an inbound chat/board message to Claude via the channel worker. */
|
|
51
|
+
pushToChannel(content: string, meta: ChannelMessageMeta): void;
|
|
52
|
+
/** Internal — invoked by the bridge when Claude replies via the tool. */
|
|
53
|
+
handleChannelReply(content: string, meta: ChannelReplyMeta, tool: 'reply' | 'edit_message'): void;
|
|
54
|
+
connect(): Promise<void>;
|
|
55
|
+
disconnect(): void;
|
|
56
|
+
isConnected(): boolean;
|
|
57
|
+
healthCheck(): Promise<AgentHealthStatus>;
|
|
58
|
+
restart(): Promise<void>;
|
|
59
|
+
abort(): Promise<void>;
|
|
60
|
+
sendMessage(params: SendMessageParams): Promise<void>;
|
|
61
|
+
listSessions(): Promise<ChatSession[]>;
|
|
62
|
+
getHistory(sessionKey: string, limit?: number): Promise<ChatMessage[]>;
|
|
63
|
+
private hasCredentials;
|
|
64
|
+
private materializeWorkspace;
|
|
65
|
+
private startBridgeAndClaude;
|
|
66
|
+
private startLoginFlow;
|
|
67
|
+
private projectJsonlDir;
|
|
68
|
+
}
|