mcacp 0.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/LICENSE +190 -0
- package/README.md +195 -0
- package/dist/acp/agent-requests.d.ts +19 -0
- package/dist/acp/agent-requests.js +166 -0
- package/dist/acp/agent-requests.js.map +1 -0
- package/dist/acp/lifecycle.d.ts +50 -0
- package/dist/acp/lifecycle.js +127 -0
- package/dist/acp/lifecycle.js.map +1 -0
- package/dist/acp/status.d.ts +31 -0
- package/dist/acp/status.js +72 -0
- package/dist/acp/status.js.map +1 -0
- package/dist/acp/transport.d.ts +34 -0
- package/dist/acp/transport.js +175 -0
- package/dist/acp/transport.js.map +1 -0
- package/dist/config/index.d.ts +27 -0
- package/dist/config/index.js +162 -0
- package/dist/config/index.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/permissions/index.d.ts +16 -0
- package/dist/permissions/index.js +70 -0
- package/dist/permissions/index.js.map +1 -0
- package/dist/registry/index.d.ts +52 -0
- package/dist/registry/index.js +240 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/server/index.d.ts +5 -0
- package/dist/server/index.js +271 -0
- package/dist/server/index.js.map +1 -0
- package/dist/sessions/index.d.ts +91 -0
- package/dist/sessions/index.js +151 -0
- package/dist/sessions/index.js.map +1 -0
- package/dist/sessions/prompt.d.ts +78 -0
- package/dist/sessions/prompt.js +361 -0
- package/dist/sessions/prompt.js.map +1 -0
- package/dist/types/acp.d.ts +343 -0
- package/dist/types/acp.js +17 -0
- package/dist/types/acp.js.map +1 -0
- package/dist/types/config.d.ts +135 -0
- package/dist/types/config.js +43 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.js +4 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/tools.d.ts +619 -0
- package/dist/types/tools.js +441 -0
- package/dist/types/tools.js.map +1 -0
- package/docs/configuration.md +164 -0
- package/package.json +58 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { AcpTransport } from './transport.js';
|
|
2
|
+
import { getAgentConfig } from '../config/index.js';
|
|
3
|
+
export class LifecycleManager {
|
|
4
|
+
config;
|
|
5
|
+
agents = new Map();
|
|
6
|
+
installedAgents;
|
|
7
|
+
constructor(config, installedAgents) {
|
|
8
|
+
this.config = config;
|
|
9
|
+
this.installedAgents = installedAgents;
|
|
10
|
+
}
|
|
11
|
+
async initialize(agentId, protocolVersion, clientInfo, clientCapabilities) {
|
|
12
|
+
if (this.agents.has(agentId)) {
|
|
13
|
+
throw new Error(`Agent "${agentId}" is already running`);
|
|
14
|
+
}
|
|
15
|
+
const installed = this.installedAgents.get(agentId);
|
|
16
|
+
const agentConfig = getAgentConfig(this.config, agentId);
|
|
17
|
+
const command = agentConfig.command ?? installed?.command;
|
|
18
|
+
const args = agentConfig.args ?? installed?.args ?? [];
|
|
19
|
+
const env = agentConfig.env ?? installed?.env;
|
|
20
|
+
if (!command) {
|
|
21
|
+
throw new Error(`Agent "${agentId}" not installed and no command configured`);
|
|
22
|
+
}
|
|
23
|
+
const transport = new AcpTransport({ command, args, env, requestTimeoutMs: 60_000 });
|
|
24
|
+
transport.start();
|
|
25
|
+
transport.on('framingError', (line, err) => {
|
|
26
|
+
process.stderr.write(`[mcacp:${agentId}] Framing error: ${err instanceof Error ? err.message : err}\n`);
|
|
27
|
+
process.stderr.write(`[mcacp:${agentId}] raw: ${line.slice(0, 200)}\n`);
|
|
28
|
+
});
|
|
29
|
+
transport.on('invalidMessage', (msg) => {
|
|
30
|
+
process.stderr.write(`[mcacp:${agentId}] Invalid JSON-RPC message: ${JSON.stringify(msg).slice(0, 200)}\n`);
|
|
31
|
+
});
|
|
32
|
+
transport.on('stderr', (data) => {
|
|
33
|
+
process.stderr.write(`[mcacp:${agentId}] ${data}`);
|
|
34
|
+
});
|
|
35
|
+
try {
|
|
36
|
+
const params = {
|
|
37
|
+
protocolVersion: protocolVersion ?? 1,
|
|
38
|
+
clientCapabilities: clientCapabilities ?? {
|
|
39
|
+
fs: { readTextFile: true, writeTextFile: true },
|
|
40
|
+
terminal: true,
|
|
41
|
+
},
|
|
42
|
+
clientInfo: clientInfo ?? {
|
|
43
|
+
name: this.config.clientInfo.name,
|
|
44
|
+
version: this.config.clientInfo.version,
|
|
45
|
+
title: this.config.clientInfo.title,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
const result = await transport.request('initialize', params);
|
|
49
|
+
const handle = {
|
|
50
|
+
agentId,
|
|
51
|
+
transport,
|
|
52
|
+
capabilities: result.agentCapabilities,
|
|
53
|
+
agentInfo: result.agentInfo,
|
|
54
|
+
protocolVersion: result.protocolVersion,
|
|
55
|
+
activeSessions: new Set(),
|
|
56
|
+
startedAt: Date.now(),
|
|
57
|
+
lastActivityAt: Date.now(),
|
|
58
|
+
reapTimer: null,
|
|
59
|
+
status: { text: 'initialized', updatedAt: Date.now() },
|
|
60
|
+
};
|
|
61
|
+
const reapMs = agentConfig.autoReapMs;
|
|
62
|
+
if (reapMs > 0) {
|
|
63
|
+
this.resetReapTimer(handle, reapMs);
|
|
64
|
+
}
|
|
65
|
+
transport.on('exit', () => {
|
|
66
|
+
this.agents.delete(agentId);
|
|
67
|
+
});
|
|
68
|
+
this.agents.set(agentId, handle);
|
|
69
|
+
return {
|
|
70
|
+
protocolVersion: result.protocolVersion,
|
|
71
|
+
agentInfo: result.agentInfo,
|
|
72
|
+
agentCapabilities: result.agentCapabilities,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
await transport.close();
|
|
77
|
+
throw err;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async shutdown(agentId) {
|
|
81
|
+
const handle = this.agents.get(agentId);
|
|
82
|
+
if (!handle)
|
|
83
|
+
return; // Already shut down (e.g. by reap timer racing with manual shutdown)
|
|
84
|
+
if (handle.reapTimer) {
|
|
85
|
+
clearTimeout(handle.reapTimer);
|
|
86
|
+
handle.reapTimer = null;
|
|
87
|
+
}
|
|
88
|
+
this.agents.delete(agentId);
|
|
89
|
+
handle.activeSessions.clear();
|
|
90
|
+
await handle.transport.close();
|
|
91
|
+
}
|
|
92
|
+
getAgent(agentId) {
|
|
93
|
+
const handle = this.agents.get(agentId);
|
|
94
|
+
if (!handle)
|
|
95
|
+
throw new Error(`Agent "${agentId}" is not running. Call initialize first.`);
|
|
96
|
+
return handle;
|
|
97
|
+
}
|
|
98
|
+
isRunning(agentId) {
|
|
99
|
+
return this.agents.has(agentId);
|
|
100
|
+
}
|
|
101
|
+
getAllAgents() {
|
|
102
|
+
return Array.from(this.agents.values());
|
|
103
|
+
}
|
|
104
|
+
touchActivity(agentId) {
|
|
105
|
+
const handle = this.agents.get(agentId);
|
|
106
|
+
if (!handle)
|
|
107
|
+
return;
|
|
108
|
+
handle.lastActivityAt = Date.now();
|
|
109
|
+
const reapMs = getAgentConfig(this.config, agentId).autoReapMs;
|
|
110
|
+
if (reapMs > 0)
|
|
111
|
+
this.resetReapTimer(handle, reapMs);
|
|
112
|
+
}
|
|
113
|
+
updateInstalledAgents(installed) {
|
|
114
|
+
this.installedAgents = installed;
|
|
115
|
+
}
|
|
116
|
+
resetReapTimer(handle, reapMs) {
|
|
117
|
+
if (handle.reapTimer)
|
|
118
|
+
clearTimeout(handle.reapTimer);
|
|
119
|
+
handle.reapTimer = setTimeout(async () => {
|
|
120
|
+
handle.status = { text: 'auto-reaped (inactivity)', updatedAt: Date.now() };
|
|
121
|
+
await this.shutdown(handle.agentId).catch(() => { });
|
|
122
|
+
}, reapMs);
|
|
123
|
+
if (handle.reapTimer.unref)
|
|
124
|
+
handle.reapTimer.unref();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../src/acp/lifecycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { AgentHandle } from './lifecycle.js';
|
|
2
|
+
import type { McacpConfig } from '../types/config.js';
|
|
3
|
+
export interface AgentStatusSnapshot {
|
|
4
|
+
agentId: string;
|
|
5
|
+
status: string;
|
|
6
|
+
statusUpdatedAt: number;
|
|
7
|
+
alive: boolean;
|
|
8
|
+
startedAt: number;
|
|
9
|
+
lastActivityAt: number;
|
|
10
|
+
lastMessageAt: number;
|
|
11
|
+
activeSessions: string[];
|
|
12
|
+
uptime: number;
|
|
13
|
+
}
|
|
14
|
+
export interface DetailedAgentStatus extends AgentStatusSnapshot {
|
|
15
|
+
protocolVersion: number;
|
|
16
|
+
capabilities: Record<string, unknown>;
|
|
17
|
+
agentInfo?: {
|
|
18
|
+
name: string;
|
|
19
|
+
version: string;
|
|
20
|
+
title?: string;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export declare class StatusTracker {
|
|
24
|
+
private config;
|
|
25
|
+
constructor(config: McacpConfig);
|
|
26
|
+
getAll(agents: AgentHandle[]): AgentStatusSnapshot[];
|
|
27
|
+
getDetailed(handle: AgentHandle): DetailedAgentStatus;
|
|
28
|
+
isAlive(handle: AgentHandle): boolean;
|
|
29
|
+
setStatus(handle: AgentHandle, text: string): void;
|
|
30
|
+
updateFromSessionUpdate(handle: AgentHandle, update: Record<string, unknown>): void;
|
|
31
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export class StatusTracker {
|
|
2
|
+
config;
|
|
3
|
+
constructor(config) {
|
|
4
|
+
this.config = config;
|
|
5
|
+
}
|
|
6
|
+
getAll(agents) {
|
|
7
|
+
const now = Date.now();
|
|
8
|
+
return agents.map(h => ({
|
|
9
|
+
agentId: h.agentId,
|
|
10
|
+
status: h.status.text,
|
|
11
|
+
statusUpdatedAt: h.status.updatedAt,
|
|
12
|
+
alive: this.isAlive(h),
|
|
13
|
+
startedAt: h.startedAt,
|
|
14
|
+
lastActivityAt: h.lastActivityAt,
|
|
15
|
+
lastMessageAt: h.transport.lastMessageAt,
|
|
16
|
+
activeSessions: Array.from(h.activeSessions),
|
|
17
|
+
uptime: now - h.startedAt,
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
20
|
+
getDetailed(handle) {
|
|
21
|
+
const now = Date.now();
|
|
22
|
+
return {
|
|
23
|
+
agentId: handle.agentId,
|
|
24
|
+
status: handle.status.text,
|
|
25
|
+
statusUpdatedAt: handle.status.updatedAt,
|
|
26
|
+
alive: this.isAlive(handle),
|
|
27
|
+
startedAt: handle.startedAt,
|
|
28
|
+
lastActivityAt: handle.lastActivityAt,
|
|
29
|
+
lastMessageAt: handle.transport.lastMessageAt,
|
|
30
|
+
activeSessions: Array.from(handle.activeSessions),
|
|
31
|
+
uptime: now - handle.startedAt,
|
|
32
|
+
protocolVersion: handle.protocolVersion,
|
|
33
|
+
capabilities: handle.capabilities,
|
|
34
|
+
agentInfo: handle.agentInfo,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
isAlive(handle) {
|
|
38
|
+
if (handle.transport.closed)
|
|
39
|
+
return false;
|
|
40
|
+
return Date.now() - handle.transport.lastMessageAt < this.config.heartbeatTimeoutMs;
|
|
41
|
+
}
|
|
42
|
+
setStatus(handle, text) {
|
|
43
|
+
handle.status = { text, updatedAt: Date.now() };
|
|
44
|
+
}
|
|
45
|
+
updateFromSessionUpdate(handle, update) {
|
|
46
|
+
const type = update.sessionUpdate;
|
|
47
|
+
switch (type) {
|
|
48
|
+
case 'tool_call':
|
|
49
|
+
handle.status = { text: `Tool: ${update.title}`, updatedAt: Date.now() };
|
|
50
|
+
break;
|
|
51
|
+
case 'tool_call_update':
|
|
52
|
+
if (update.status === 'completed') {
|
|
53
|
+
handle.status = { text: 'Processing...', updatedAt: Date.now() };
|
|
54
|
+
}
|
|
55
|
+
break;
|
|
56
|
+
case 'agent_message_chunk':
|
|
57
|
+
handle.status = { text: 'Responding...', updatedAt: Date.now() };
|
|
58
|
+
break;
|
|
59
|
+
case 'agent_thought_chunk':
|
|
60
|
+
handle.status = { text: 'Thinking...', updatedAt: Date.now() };
|
|
61
|
+
break;
|
|
62
|
+
case 'plan': {
|
|
63
|
+
const entries = update.entries;
|
|
64
|
+
const active = entries?.find(e => e.status === 'in_progress');
|
|
65
|
+
if (active)
|
|
66
|
+
handle.status = { text: `Plan: ${active.content}`, updatedAt: Date.now() };
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/acp/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import type { RequestId } from '../types/acp.js';
|
|
3
|
+
export interface TransportOptions {
|
|
4
|
+
command: string;
|
|
5
|
+
args?: string[];
|
|
6
|
+
env?: Record<string, string>;
|
|
7
|
+
cwd?: string;
|
|
8
|
+
requestTimeoutMs?: number;
|
|
9
|
+
}
|
|
10
|
+
type IncomingRequestHandler = (method: string, params: unknown, id: RequestId) => Promise<unknown>;
|
|
11
|
+
type NotificationHandler = (method: string, params: unknown) => void;
|
|
12
|
+
export declare class AcpTransport extends EventEmitter {
|
|
13
|
+
private options;
|
|
14
|
+
private process;
|
|
15
|
+
private pendingRequests;
|
|
16
|
+
private nextId;
|
|
17
|
+
private requestTimeoutMs;
|
|
18
|
+
private onIncomingRequest;
|
|
19
|
+
private onNotification;
|
|
20
|
+
private _closed;
|
|
21
|
+
constructor(options: TransportOptions);
|
|
22
|
+
get closed(): boolean;
|
|
23
|
+
lastMessageAt: number;
|
|
24
|
+
start(): void;
|
|
25
|
+
setRequestHandler(handler: IncomingRequestHandler): void;
|
|
26
|
+
setNotificationHandler(handler: NotificationHandler): void;
|
|
27
|
+
request(method: string, params?: unknown): Promise<unknown>;
|
|
28
|
+
notify(method: string, params?: unknown): void;
|
|
29
|
+
close(killTimeoutMs?: number): Promise<void>;
|
|
30
|
+
private send;
|
|
31
|
+
private handleMessage;
|
|
32
|
+
private rejectAllPending;
|
|
33
|
+
}
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { EventEmitter } from 'node:events';
|
|
3
|
+
import { createInterface } from 'node:readline';
|
|
4
|
+
export class AcpTransport extends EventEmitter {
|
|
5
|
+
options;
|
|
6
|
+
process = null;
|
|
7
|
+
pendingRequests = new Map();
|
|
8
|
+
nextId = 1;
|
|
9
|
+
requestTimeoutMs;
|
|
10
|
+
onIncomingRequest = null;
|
|
11
|
+
onNotification = null;
|
|
12
|
+
_closed = false;
|
|
13
|
+
constructor(options) {
|
|
14
|
+
super();
|
|
15
|
+
this.options = options;
|
|
16
|
+
this.requestTimeoutMs = options.requestTimeoutMs ?? 300_000;
|
|
17
|
+
}
|
|
18
|
+
get closed() {
|
|
19
|
+
return this._closed;
|
|
20
|
+
}
|
|
21
|
+
lastMessageAt = Date.now();
|
|
22
|
+
start() {
|
|
23
|
+
const env = { ...process.env, ...this.options.env };
|
|
24
|
+
this.process = spawn(this.options.command, this.options.args ?? [], {
|
|
25
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
26
|
+
env,
|
|
27
|
+
cwd: this.options.cwd,
|
|
28
|
+
shell: process.platform === 'win32',
|
|
29
|
+
});
|
|
30
|
+
this.process.on('exit', (code, signal) => {
|
|
31
|
+
this._closed = true;
|
|
32
|
+
this.rejectAllPending(new Error(`Agent process exited (code=${code}, signal=${signal})`));
|
|
33
|
+
this.emit('exit', code, signal);
|
|
34
|
+
});
|
|
35
|
+
this.process.on('error', (err) => {
|
|
36
|
+
this._closed = true;
|
|
37
|
+
this.rejectAllPending(err);
|
|
38
|
+
this.emit('error', err);
|
|
39
|
+
});
|
|
40
|
+
if (this.process.stderr) {
|
|
41
|
+
this.process.stderr.on('data', (chunk) => {
|
|
42
|
+
this.emit('stderr', chunk.toString());
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
if (this.process.stdout) {
|
|
46
|
+
const rl = createInterface({ input: this.process.stdout });
|
|
47
|
+
rl.on('line', (line) => {
|
|
48
|
+
const trimmed = line.trim();
|
|
49
|
+
if (!trimmed)
|
|
50
|
+
return;
|
|
51
|
+
let msg;
|
|
52
|
+
try {
|
|
53
|
+
msg = JSON.parse(trimmed);
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
this.emit('framingError', trimmed, err);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
this.lastMessageAt = Date.now();
|
|
60
|
+
if (!this.handleMessage(msg)) {
|
|
61
|
+
this.emit('invalidMessage', msg);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
rl.on('close', () => {
|
|
65
|
+
this._closed = true;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
setRequestHandler(handler) {
|
|
70
|
+
this.onIncomingRequest = handler;
|
|
71
|
+
}
|
|
72
|
+
setNotificationHandler(handler) {
|
|
73
|
+
this.onNotification = handler;
|
|
74
|
+
}
|
|
75
|
+
async request(method, params) {
|
|
76
|
+
if (this._closed)
|
|
77
|
+
throw new Error('Transport is closed');
|
|
78
|
+
const id = this.nextId++;
|
|
79
|
+
const msg = { jsonrpc: '2.0', id, method, params };
|
|
80
|
+
return new Promise((resolve, reject) => {
|
|
81
|
+
const timer = setTimeout(() => {
|
|
82
|
+
this.pendingRequests.delete(id);
|
|
83
|
+
reject(new Error(`Request timed out: ${method} (id=${id})`));
|
|
84
|
+
}, this.requestTimeoutMs);
|
|
85
|
+
this.pendingRequests.set(id, { resolve, reject, timer });
|
|
86
|
+
this.send(msg);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
notify(method, params) {
|
|
90
|
+
if (this._closed)
|
|
91
|
+
throw new Error('Transport is closed');
|
|
92
|
+
const msg = { jsonrpc: '2.0', method, params };
|
|
93
|
+
this.send(msg);
|
|
94
|
+
}
|
|
95
|
+
async close(killTimeoutMs = 5000) {
|
|
96
|
+
if (this._closed)
|
|
97
|
+
return;
|
|
98
|
+
this._closed = true;
|
|
99
|
+
this.rejectAllPending(new Error('Transport closing'));
|
|
100
|
+
if (this.process && !this.process.killed) {
|
|
101
|
+
this.process.stdin?.end();
|
|
102
|
+
await new Promise((resolve) => {
|
|
103
|
+
const timer = setTimeout(() => {
|
|
104
|
+
this.process?.kill('SIGKILL');
|
|
105
|
+
resolve();
|
|
106
|
+
}, killTimeoutMs);
|
|
107
|
+
this.process.once('exit', () => {
|
|
108
|
+
clearTimeout(timer);
|
|
109
|
+
resolve();
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
send(msg) {
|
|
115
|
+
if (!this.process?.stdin?.writable) {
|
|
116
|
+
throw new Error('Cannot write to agent process');
|
|
117
|
+
}
|
|
118
|
+
this.process.stdin.write(JSON.stringify(msg) + '\n');
|
|
119
|
+
}
|
|
120
|
+
handleMessage(msg) {
|
|
121
|
+
if ('id' in msg && msg.id !== undefined && msg.id !== null && ('result' in msg || 'error' in msg)) {
|
|
122
|
+
const resp = msg;
|
|
123
|
+
const idKey = typeof resp.id === 'number' ? resp.id : String(resp.id);
|
|
124
|
+
const pending = this.pendingRequests.get(idKey);
|
|
125
|
+
if (pending) {
|
|
126
|
+
this.pendingRequests.delete(idKey);
|
|
127
|
+
clearTimeout(pending.timer);
|
|
128
|
+
if (resp.error) {
|
|
129
|
+
const err = new Error(resp.error.message);
|
|
130
|
+
err.code = resp.error.code;
|
|
131
|
+
err.data = resp.error.data;
|
|
132
|
+
pending.reject(err);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
pending.resolve(resp.result);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
if ('id' in msg && 'method' in msg && !('result' in msg) && !('error' in msg)) {
|
|
141
|
+
const req = msg;
|
|
142
|
+
if (this.onIncomingRequest) {
|
|
143
|
+
this.onIncomingRequest(req.method, req.params, req.id)
|
|
144
|
+
.then((result) => {
|
|
145
|
+
this.send({ jsonrpc: '2.0', id: req.id, result });
|
|
146
|
+
})
|
|
147
|
+
.catch((err) => {
|
|
148
|
+
const error = {
|
|
149
|
+
code: err.code ?? -32603,
|
|
150
|
+
message: err instanceof Error ? err.message : String(err),
|
|
151
|
+
};
|
|
152
|
+
this.send({ jsonrpc: '2.0', id: req.id, error });
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
if ('method' in msg && !('id' in msg)) {
|
|
158
|
+
const notif = msg;
|
|
159
|
+
if (this.onNotification) {
|
|
160
|
+
this.onNotification(notif.method, notif.params);
|
|
161
|
+
}
|
|
162
|
+
this.emit('notification', notif.method, notif.params);
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
rejectAllPending(error) {
|
|
168
|
+
for (const [, pending] of this.pendingRequests) {
|
|
169
|
+
clearTimeout(pending.timer);
|
|
170
|
+
pending.reject(error);
|
|
171
|
+
}
|
|
172
|
+
this.pendingRequests.clear();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/acp/transport.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type McacpConfig } from '../types/config.js';
|
|
2
|
+
/** Discovered agent from an editor config. */
|
|
3
|
+
export interface DiscoveredAgent {
|
|
4
|
+
name: string;
|
|
5
|
+
command: string;
|
|
6
|
+
args?: string[];
|
|
7
|
+
env?: Record<string, string>;
|
|
8
|
+
source: string;
|
|
9
|
+
configPath: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Load config from multiple scopes: host < user < project < explicit.
|
|
13
|
+
* agent_servers entries merge across scopes (project wins on conflict).
|
|
14
|
+
*/
|
|
15
|
+
export declare function loadConfig(explicitPath?: string): McacpConfig;
|
|
16
|
+
/** Resolve agent config by merging agent_servers entry with top-level defaults. */
|
|
17
|
+
export declare function getAgentConfig(config: McacpConfig, agentId: string): {
|
|
18
|
+
autoReapMs: number;
|
|
19
|
+
permissionPolicy: "elicit" | "allow_all" | "deny_all" | "operator";
|
|
20
|
+
installPath: string | undefined;
|
|
21
|
+
command: string | undefined;
|
|
22
|
+
args: string[] | undefined;
|
|
23
|
+
env: Record<string, string> | undefined;
|
|
24
|
+
};
|
|
25
|
+
/** Scan editor configs for agent_servers entries. */
|
|
26
|
+
export declare function discoverEditorAgents(): DiscoveredAgent[];
|
|
27
|
+
export type { McacpConfig, AgentServer } from '../types/config.js';
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { resolve, join } from 'node:path';
|
|
3
|
+
import { homedir, platform } from 'node:os';
|
|
4
|
+
import { McacpConfigSchema } from '../types/config.js';
|
|
5
|
+
const CONFIG_FILENAMES = ['mcacp.json', '.mcacprc.json'];
|
|
6
|
+
// ---- Config search paths per scope ----
|
|
7
|
+
function projectPaths() {
|
|
8
|
+
return [process.cwd()];
|
|
9
|
+
}
|
|
10
|
+
function userPaths() {
|
|
11
|
+
const home = homedir();
|
|
12
|
+
const paths = [];
|
|
13
|
+
if (platform() === 'win32') {
|
|
14
|
+
const appData = process.env.APPDATA || join(home, 'AppData', 'Roaming');
|
|
15
|
+
paths.push(join(appData, 'mcacp'));
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
paths.push(join(home, '.config', 'mcacp'));
|
|
19
|
+
}
|
|
20
|
+
paths.push(home); // legacy: ~/mcacp.json
|
|
21
|
+
return paths;
|
|
22
|
+
}
|
|
23
|
+
function hostPaths() {
|
|
24
|
+
if (platform() === 'win32') {
|
|
25
|
+
const programData = process.env.PROGRAMDATA || 'C:\\ProgramData';
|
|
26
|
+
return [join(programData, 'mcacp')];
|
|
27
|
+
}
|
|
28
|
+
return ['/etc/mcacp'];
|
|
29
|
+
}
|
|
30
|
+
// ---- Config loading ----
|
|
31
|
+
function findConfigFile(dirs) {
|
|
32
|
+
for (const dir of dirs) {
|
|
33
|
+
for (const filename of CONFIG_FILENAMES) {
|
|
34
|
+
const filepath = resolve(dir, filename);
|
|
35
|
+
if (existsSync(filepath))
|
|
36
|
+
return filepath;
|
|
37
|
+
}
|
|
38
|
+
// Also accept config.json in dedicated config dirs
|
|
39
|
+
const configJson = resolve(dir, 'config.json');
|
|
40
|
+
if (existsSync(configJson))
|
|
41
|
+
return configJson;
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
function loadRawConfig(filepath) {
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(readFileSync(filepath, 'utf-8'));
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
throw new Error(`Failed to parse config "${filepath}": ${err instanceof Error ? err.message : err}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** Deep-merge config objects. Later values win. agent_servers entries merge per-key. */
|
|
54
|
+
function mergeConfigs(...configs) {
|
|
55
|
+
const merged = {};
|
|
56
|
+
for (const config of configs) {
|
|
57
|
+
for (const [key, value] of Object.entries(config)) {
|
|
58
|
+
if (key === 'agent_servers' && typeof value === 'object' && value !== null) {
|
|
59
|
+
merged[key] = { ...(merged[key] ?? {}), ...value };
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
merged[key] = value;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return merged;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Load config from multiple scopes: host < user < project < explicit.
|
|
70
|
+
* agent_servers entries merge across scopes (project wins on conflict).
|
|
71
|
+
*/
|
|
72
|
+
export function loadConfig(explicitPath) {
|
|
73
|
+
const layers = [];
|
|
74
|
+
// Host scope (lowest priority)
|
|
75
|
+
const hostFile = findConfigFile(hostPaths());
|
|
76
|
+
if (hostFile)
|
|
77
|
+
layers.push(loadRawConfig(hostFile));
|
|
78
|
+
// Project scope
|
|
79
|
+
const projectFile = findConfigFile(projectPaths());
|
|
80
|
+
if (projectFile)
|
|
81
|
+
layers.push(loadRawConfig(projectFile));
|
|
82
|
+
// User scope
|
|
83
|
+
const userFile = findConfigFile(userPaths());
|
|
84
|
+
if (userFile)
|
|
85
|
+
layers.push(loadRawConfig(userFile));
|
|
86
|
+
// Explicit path (highest priority)
|
|
87
|
+
if (explicitPath) {
|
|
88
|
+
layers.push(loadRawConfig(explicitPath));
|
|
89
|
+
}
|
|
90
|
+
const merged = layers.length > 0 ? mergeConfigs(...layers) : {};
|
|
91
|
+
try {
|
|
92
|
+
return McacpConfigSchema.parse(merged);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
const sources = [hostFile, userFile, projectFile, explicitPath].filter(Boolean).join(', ');
|
|
96
|
+
throw new Error(`Invalid config (sources: ${sources || 'defaults'}): ${err instanceof Error ? err.message : err}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/** Resolve agent config by merging agent_servers entry with top-level defaults. */
|
|
100
|
+
export function getAgentConfig(config, agentId) {
|
|
101
|
+
const entry = config.agent_servers[agentId];
|
|
102
|
+
return {
|
|
103
|
+
autoReapMs: entry?.autoReapMs ?? config.defaultAutoReapMs,
|
|
104
|
+
permissionPolicy: entry?.permissionPolicy ?? config.defaultPermissionPolicy,
|
|
105
|
+
installPath: entry?.installPath,
|
|
106
|
+
command: entry?.command,
|
|
107
|
+
args: entry?.args,
|
|
108
|
+
env: entry?.env,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
// ---- Editor config discovery ----
|
|
112
|
+
/** Known editor config locations. */
|
|
113
|
+
function editorConfigPaths() {
|
|
114
|
+
const home = homedir();
|
|
115
|
+
const results = [];
|
|
116
|
+
// Zed
|
|
117
|
+
if (platform() === 'win32') {
|
|
118
|
+
const appData = process.env.APPDATA || join(home, 'AppData', 'Roaming');
|
|
119
|
+
results.push({ editor: 'zed', path: join(appData, 'Zed', 'settings.json') });
|
|
120
|
+
}
|
|
121
|
+
else if (platform() === 'darwin') {
|
|
122
|
+
results.push({ editor: 'zed', path: join(home, '.config', 'zed', 'settings.json') });
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
results.push({ editor: 'zed', path: join(home, '.config', 'zed', 'settings.json') });
|
|
126
|
+
}
|
|
127
|
+
// JetBrains
|
|
128
|
+
results.push({ editor: 'jetbrains', path: join(home, '.jetbrains', 'acp.json') });
|
|
129
|
+
return results;
|
|
130
|
+
}
|
|
131
|
+
/** Scan editor configs for agent_servers entries. */
|
|
132
|
+
export function discoverEditorAgents() {
|
|
133
|
+
const discovered = [];
|
|
134
|
+
for (const { editor, path: configPath } of editorConfigPaths()) {
|
|
135
|
+
if (!existsSync(configPath))
|
|
136
|
+
continue;
|
|
137
|
+
try {
|
|
138
|
+
const raw = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
139
|
+
const servers = raw?.agent_servers;
|
|
140
|
+
if (!servers || typeof servers !== 'object')
|
|
141
|
+
continue;
|
|
142
|
+
for (const [name, entry] of Object.entries(servers)) {
|
|
143
|
+
const e = entry;
|
|
144
|
+
if (!e.command || typeof e.command !== 'string')
|
|
145
|
+
continue;
|
|
146
|
+
discovered.push({
|
|
147
|
+
name,
|
|
148
|
+
command: e.command,
|
|
149
|
+
args: Array.isArray(e.args) ? e.args.filter((a) => typeof a === 'string') : undefined,
|
|
150
|
+
env: e.env && typeof e.env === 'object' ? e.env : undefined,
|
|
151
|
+
source: editor,
|
|
152
|
+
configPath,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// Skip unparseable configs
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return discovered;
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=index.js.map
|