@thinkwell/conductor 0.5.5 → 0.5.6
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/conductor.js +627 -935
- package/dist/connectors/channel.js +75 -142
- package/dist/connectors/index.js +1 -5
- package/dist/connectors/stdio.js +100 -202
- package/dist/generated/features.d.ts +5 -0
- package/dist/generated/features.d.ts.map +1 -0
- package/dist/generated/features.js +4 -0
- package/dist/generated/features.js.map +1 -0
- package/dist/index.js +5 -73
- package/dist/instantiators.js +38 -171
- package/dist/logger.js +80 -148
- package/dist/mcp-bridge/http-listener.js +116 -212
- package/dist/mcp-bridge/index.js +0 -6
- package/dist/mcp-bridge/mcp-bridge.js +117 -164
- package/dist/mcp-bridge/types.js +0 -7
- package/dist/message-queue.js +49 -86
- package/dist/types.js +4 -11
- package/package.json +4 -3
|
@@ -1,155 +1,88 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ChannelConnector - in-memory bidirectional connection
|
|
3
|
-
*
|
|
4
|
-
* This connector creates an in-memory channel for testing or for
|
|
5
|
-
* embedded components that run in the same process.
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* A simple message channel that supports async iteration
|
|
9
|
-
*/
|
|
10
1
|
class MessageChannel {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return this.closed;
|
|
35
|
-
}
|
|
36
|
-
async *[Symbol.asyncIterator]() {
|
|
37
|
-
while (!this.closed) {
|
|
38
|
-
let message;
|
|
39
|
-
if (this.queue.length > 0) {
|
|
40
|
-
message = this.queue.shift();
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
message = await new Promise((resolve) => {
|
|
44
|
-
this.resolvers.push(resolve);
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
if (message === null) {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
yield message;
|
|
51
|
-
}
|
|
52
|
-
// Drain any remaining messages
|
|
53
|
-
while (this.queue.length > 0) {
|
|
54
|
-
yield this.queue.shift();
|
|
55
|
-
}
|
|
2
|
+
queue = [];
|
|
3
|
+
resolvers = [];
|
|
4
|
+
closed = !1;
|
|
5
|
+
push(message) {
|
|
6
|
+
this.closed || (this.resolvers.length > 0 ? this.resolvers.shift()(message) : this.queue.push(message));
|
|
7
|
+
}
|
|
8
|
+
close() {
|
|
9
|
+
this.closed = !0;
|
|
10
|
+
for (const resolve of this.resolvers)
|
|
11
|
+
resolve(null);
|
|
12
|
+
this.resolvers = [];
|
|
13
|
+
}
|
|
14
|
+
isClosed() {
|
|
15
|
+
return this.closed;
|
|
16
|
+
}
|
|
17
|
+
async *[Symbol.asyncIterator]() {
|
|
18
|
+
for (; !this.closed; ) {
|
|
19
|
+
let message;
|
|
20
|
+
if (this.queue.length > 0 ? message = this.queue.shift() : message = await new Promise((resolve) => {
|
|
21
|
+
this.resolvers.push(resolve);
|
|
22
|
+
}), message === null)
|
|
23
|
+
return;
|
|
24
|
+
yield message;
|
|
56
25
|
}
|
|
26
|
+
for (; this.queue.length > 0; )
|
|
27
|
+
yield this.queue.shift();
|
|
28
|
+
}
|
|
57
29
|
}
|
|
58
|
-
/**
|
|
59
|
-
* One side of a channel connection
|
|
60
|
-
*/
|
|
61
30
|
class ChannelConnectionEnd {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
this.receiveChannel.close();
|
|
77
|
-
}
|
|
31
|
+
sendChannel;
|
|
32
|
+
receiveChannel;
|
|
33
|
+
constructor(sendChannel, receiveChannel) {
|
|
34
|
+
this.sendChannel = sendChannel, this.receiveChannel = receiveChannel;
|
|
35
|
+
}
|
|
36
|
+
send(message) {
|
|
37
|
+
this.sendChannel.push(message);
|
|
38
|
+
}
|
|
39
|
+
get messages() {
|
|
40
|
+
return this.receiveChannel;
|
|
41
|
+
}
|
|
42
|
+
async close() {
|
|
43
|
+
this.sendChannel.close(), this.receiveChannel.close();
|
|
44
|
+
}
|
|
78
45
|
}
|
|
79
|
-
/**
|
|
80
|
-
* Create a connected pair of channel endpoints.
|
|
81
|
-
*
|
|
82
|
-
* This is useful for testing or for connecting in-process components.
|
|
83
|
-
*/
|
|
84
46
|
export function createChannelPair() {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
};
|
|
47
|
+
const leftToRight = new MessageChannel(), rightToLeft = new MessageChannel();
|
|
48
|
+
return {
|
|
49
|
+
left: new ChannelConnectionEnd(leftToRight, rightToLeft),
|
|
50
|
+
right: new ChannelConnectionEnd(rightToLeft, leftToRight)
|
|
51
|
+
};
|
|
91
52
|
}
|
|
92
|
-
/**
|
|
93
|
-
* Connector for in-memory channel connections.
|
|
94
|
-
*
|
|
95
|
-
* Each call to connect() returns a new channel pair. The "other" end
|
|
96
|
-
* must be retrieved via getOtherEnd() after calling connect().
|
|
97
|
-
*/
|
|
98
53
|
export class ChannelConnector {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
return end;
|
|
114
|
-
}
|
|
54
|
+
pendingOtherEnd = null;
|
|
55
|
+
async connect() {
|
|
56
|
+
const pair = createChannelPair();
|
|
57
|
+
return this.pendingOtherEnd = pair.right, pair.left;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get the other end of the most recently created channel.
|
|
61
|
+
*
|
|
62
|
+
* This must be called after connect() to get the paired endpoint.
|
|
63
|
+
*/
|
|
64
|
+
getOtherEnd() {
|
|
65
|
+
const end = this.pendingOtherEnd;
|
|
66
|
+
return this.pendingOtherEnd = null, end;
|
|
67
|
+
}
|
|
115
68
|
}
|
|
116
|
-
/**
|
|
117
|
-
* Create a connector that runs a handler function in-process.
|
|
118
|
-
*
|
|
119
|
-
* This is useful for testing or for embedding simple components.
|
|
120
|
-
*
|
|
121
|
-
* @param handler - Function that handles the component side of the connection
|
|
122
|
-
* @returns A connector that runs the handler when connect() is called
|
|
123
|
-
*/
|
|
124
69
|
export function inProcess(handler) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
},
|
|
134
|
-
};
|
|
70
|
+
return {
|
|
71
|
+
async connect() {
|
|
72
|
+
const pair = createChannelPair();
|
|
73
|
+
return handler(pair.right).catch((error) => {
|
|
74
|
+
console.error("In-process component error:", error);
|
|
75
|
+
}), pair.left;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
135
78
|
}
|
|
136
|
-
/**
|
|
137
|
-
* Create a simple echo component for testing.
|
|
138
|
-
*
|
|
139
|
-
* This component echoes back any request with the same params as the result.
|
|
140
|
-
*/
|
|
141
79
|
export function echoComponent() {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
});
|
|
80
|
+
return inProcess(async (connection) => {
|
|
81
|
+
for await (const message of connection.messages)
|
|
82
|
+
"method" in message && "id" in message && connection.send({
|
|
83
|
+
jsonrpc: "2.0",
|
|
84
|
+
id: message.id,
|
|
85
|
+
result: message.params
|
|
86
|
+
});
|
|
87
|
+
});
|
|
154
88
|
}
|
|
155
|
-
//# sourceMappingURL=channel.js.map
|
package/dist/connectors/index.js
CHANGED
|
@@ -1,6 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Component connectors - factories for creating component connections
|
|
3
|
-
*/
|
|
4
1
|
export { StdioConnector, stdio } from "./stdio.js";
|
|
5
|
-
export { ChannelConnector, createChannelPair, inProcess, echoComponent
|
|
6
|
-
//# sourceMappingURL=index.js.map
|
|
2
|
+
export { ChannelConnector, createChannelPair, inProcess, echoComponent } from "./channel.js";
|
package/dist/connectors/stdio.js
CHANGED
|
@@ -1,214 +1,112 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* StdioConnector - spawns a subprocess and communicates via stdin/stdout
|
|
3
|
-
*
|
|
4
|
-
* This connector starts a child process and establishes a bidirectional
|
|
5
|
-
* JSON-RPC connection using newline-delimited JSON over stdio.
|
|
6
|
-
*/
|
|
7
1
|
import { spawn } from "node:child_process";
|
|
8
2
|
import { createInterface } from "node:readline";
|
|
9
|
-
/**
|
|
10
|
-
* A connection to a subprocess via stdin/stdout
|
|
11
|
-
*/
|
|
12
3
|
class StdioConnection {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
*/
|
|
49
|
-
send(message) {
|
|
50
|
-
if (this.closed || !this.process.stdin) {
|
|
51
|
-
throw new Error("Connection is closed");
|
|
52
|
-
}
|
|
53
|
-
const json = JSON.stringify(message);
|
|
54
|
-
this.process.stdin.write(json + "\n");
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Async iterable of messages received from the subprocess
|
|
58
|
-
*/
|
|
59
|
-
get messages() {
|
|
60
|
-
const readline = this.readline;
|
|
61
|
-
const isClosed = () => this.closed;
|
|
62
|
-
return {
|
|
63
|
-
async *[Symbol.asyncIterator]() {
|
|
64
|
-
for await (const line of readline) {
|
|
65
|
-
if (isClosed()) {
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
if (!line.trim()) {
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
try {
|
|
72
|
-
const message = JSON.parse(line);
|
|
73
|
-
yield message;
|
|
74
|
-
}
|
|
75
|
-
catch (error) {
|
|
76
|
-
console.error("Failed to parse JSON-RPC message:", error);
|
|
77
|
-
console.error("Line:", line);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Close the connection and terminate the subprocess
|
|
85
|
-
*/
|
|
86
|
-
async close() {
|
|
87
|
-
if (this.closed) {
|
|
4
|
+
process;
|
|
5
|
+
readline;
|
|
6
|
+
closed = !1;
|
|
7
|
+
closingGracefully = !1;
|
|
8
|
+
constructor(process) {
|
|
9
|
+
if (this.process = process, !process.stdout || !process.stdin)
|
|
10
|
+
throw new Error("Process must have stdio pipes");
|
|
11
|
+
this.readline = createInterface({
|
|
12
|
+
input: process.stdout,
|
|
13
|
+
crlfDelay: 1 / 0
|
|
14
|
+
}), process.on("exit", (code, signal) => {
|
|
15
|
+
this.closed = !0, code !== 0 && code !== null && !this.closingGracefully && console.error(`Process exited with code ${code}`), signal && !this.closingGracefully && console.error(`Process killed by signal ${signal}`);
|
|
16
|
+
}), process.on("error", (error) => {
|
|
17
|
+
console.error("Process error:", error), this.closed = !0;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Send a JSON-RPC message to the subprocess
|
|
22
|
+
*/
|
|
23
|
+
send(message) {
|
|
24
|
+
if (this.closed || !this.process.stdin)
|
|
25
|
+
throw new Error("Connection is closed");
|
|
26
|
+
const json = JSON.stringify(message);
|
|
27
|
+
this.process.stdin.write(json + `
|
|
28
|
+
`);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Async iterable of messages received from the subprocess
|
|
32
|
+
*/
|
|
33
|
+
get messages() {
|
|
34
|
+
const readline = this.readline, isClosed = () => this.closed;
|
|
35
|
+
return {
|
|
36
|
+
async *[Symbol.asyncIterator]() {
|
|
37
|
+
for await (const line of readline) {
|
|
38
|
+
if (isClosed())
|
|
88
39
|
return;
|
|
40
|
+
if (line.trim())
|
|
41
|
+
try {
|
|
42
|
+
yield JSON.parse(line);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error("Failed to parse JSON-RPC message:", error), console.error("Line:", line);
|
|
45
|
+
}
|
|
89
46
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
resolve();
|
|
110
|
-
};
|
|
111
|
-
this.process.once("exit", onExit);
|
|
112
|
-
// Wait 250ms for graceful exit, then send SIGTERM
|
|
113
|
-
sigtermTimeout = setTimeout(() => {
|
|
114
|
-
this.process.kill("SIGTERM");
|
|
115
|
-
// Wait another 500ms, then force kill with SIGKILL
|
|
116
|
-
sigkillTimeout = setTimeout(() => {
|
|
117
|
-
this.process.kill("SIGKILL");
|
|
118
|
-
resolve();
|
|
119
|
-
}, 500);
|
|
120
|
-
}, 250);
|
|
121
|
-
});
|
|
122
|
-
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Close the connection and terminate the subprocess
|
|
52
|
+
*/
|
|
53
|
+
async close() {
|
|
54
|
+
this.closed || (this.closed = !0, this.closingGracefully = !0, this.readline.close(), this.process.stdout?.destroy(), this.process.stdin?.end(), await new Promise((resolve) => {
|
|
55
|
+
let sigtermTimeout = null, sigkillTimeout = null;
|
|
56
|
+
const onExit = () => {
|
|
57
|
+
sigtermTimeout && clearTimeout(sigtermTimeout), sigkillTimeout && clearTimeout(sigkillTimeout), resolve();
|
|
58
|
+
};
|
|
59
|
+
this.process.once("exit", onExit), sigtermTimeout = setTimeout(() => {
|
|
60
|
+
this.process.kill("SIGTERM"), sigkillTimeout = setTimeout(() => {
|
|
61
|
+
this.process.kill("SIGKILL"), resolve();
|
|
62
|
+
}, 500);
|
|
63
|
+
}, 250);
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
123
66
|
}
|
|
124
|
-
/**
|
|
125
|
-
* Connector that spawns a subprocess and communicates via stdio
|
|
126
|
-
*/
|
|
127
67
|
export class StdioConnector {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
reject(error);
|
|
161
|
-
};
|
|
162
|
-
const cleanup = () => {
|
|
163
|
-
process.removeListener("spawn", onSpawn);
|
|
164
|
-
process.removeListener("error", onError);
|
|
165
|
-
};
|
|
166
|
-
process.once("spawn", onSpawn);
|
|
167
|
-
process.once("error", onError);
|
|
168
|
-
});
|
|
169
|
-
return new StdioConnection(process);
|
|
170
|
-
}
|
|
68
|
+
options;
|
|
69
|
+
constructor(options) {
|
|
70
|
+
if (typeof options == "string") {
|
|
71
|
+
const parts = parseCommand(options);
|
|
72
|
+
this.options = {
|
|
73
|
+
command: parts[0],
|
|
74
|
+
args: parts.slice(1)
|
|
75
|
+
};
|
|
76
|
+
} else
|
|
77
|
+
this.options = options;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Spawn the subprocess and return a connection to it
|
|
81
|
+
*/
|
|
82
|
+
async connect() {
|
|
83
|
+
const { command, args = [], env, cwd } = this.options, process = spawn(command, args, {
|
|
84
|
+
stdio: ["pipe", "pipe", "inherit"],
|
|
85
|
+
// stdin, stdout piped; stderr inherited
|
|
86
|
+
env: env ? { ...globalThis.process.env, ...env } : void 0,
|
|
87
|
+
cwd
|
|
88
|
+
});
|
|
89
|
+
return await new Promise((resolve, reject) => {
|
|
90
|
+
const onSpawn = () => {
|
|
91
|
+
cleanup(), resolve();
|
|
92
|
+
}, onError = (error) => {
|
|
93
|
+
cleanup(), reject(error);
|
|
94
|
+
}, cleanup = () => {
|
|
95
|
+
process.removeListener("spawn", onSpawn), process.removeListener("error", onError);
|
|
96
|
+
};
|
|
97
|
+
process.once("spawn", onSpawn), process.once("error", onError);
|
|
98
|
+
}), new StdioConnection(process);
|
|
99
|
+
}
|
|
171
100
|
}
|
|
172
|
-
/**
|
|
173
|
-
* Parse a command string into command and arguments.
|
|
174
|
-
* Handles quoted strings.
|
|
175
|
-
*/
|
|
176
101
|
function parseCommand(command) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
inQuote = null;
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
current += char;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
else if (char === '"' || char === "'") {
|
|
191
|
-
inQuote = char;
|
|
192
|
-
}
|
|
193
|
-
else if (char === " " || char === "\t") {
|
|
194
|
-
if (current) {
|
|
195
|
-
parts.push(current);
|
|
196
|
-
current = "";
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
else {
|
|
200
|
-
current += char;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
if (current) {
|
|
204
|
-
parts.push(current);
|
|
205
|
-
}
|
|
206
|
-
return parts;
|
|
102
|
+
const parts = [];
|
|
103
|
+
let current = "", inQuote = null;
|
|
104
|
+
for (let i = 0; i < command.length; i++) {
|
|
105
|
+
const char = command[i];
|
|
106
|
+
inQuote ? char === inQuote ? inQuote = null : current += char : char === '"' || char === "'" ? inQuote = char : char === " " || char === " " ? current && (parts.push(current), current = "") : current += char;
|
|
107
|
+
}
|
|
108
|
+
return current && parts.push(current), parts;
|
|
207
109
|
}
|
|
208
|
-
/**
|
|
209
|
-
* Create a StdioConnector from a command string or options
|
|
210
|
-
*/
|
|
211
110
|
export function stdio(options) {
|
|
212
|
-
|
|
111
|
+
return new StdioConnector(options);
|
|
213
112
|
}
|
|
214
|
-
//# sourceMappingURL=stdio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../src/generated/features.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,QAAQ;;;CAGX,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features.js","sourceRoot":"","sources":["../../src/generated/features.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,eAAe,EAAE,KAAK;IACtB,oBAAoB,EAAE,KAAK;CACnB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,76 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @thinkwell/conductor - TypeScript conductor for ACP proxy chains
|
|
3
|
-
*
|
|
4
|
-
* The conductor orchestrates message routing between clients, proxies, and agents.
|
|
5
|
-
* It sits between every component, managing process lifecycle and message flow.
|
|
6
|
-
*
|
|
7
|
-
* ## Quick Start
|
|
8
|
-
*
|
|
9
|
-
* ```typescript
|
|
10
|
-
* import { Conductor, fromCommands, createChannelPair } from '@thinkwell/conductor';
|
|
11
|
-
*
|
|
12
|
-
* // Create a conductor that spawns an agent subprocess
|
|
13
|
-
* const conductor = new Conductor({
|
|
14
|
-
* instantiator: fromCommands(['my-agent']),
|
|
15
|
-
* });
|
|
16
|
-
*
|
|
17
|
-
* // Connect via a channel (for testing) or stdio (for production)
|
|
18
|
-
* const [clientEnd, conductorEnd] = createChannelPair();
|
|
19
|
-
* await conductor.connect(conductorEnd);
|
|
20
|
-
* ```
|
|
21
|
-
*
|
|
22
|
-
* ## Logging
|
|
23
|
-
*
|
|
24
|
-
* Enable logging to see what the conductor is doing:
|
|
25
|
-
*
|
|
26
|
-
* ```typescript
|
|
27
|
-
* const conductor = new Conductor({
|
|
28
|
-
* instantiator: fromCommands(['my-agent']),
|
|
29
|
-
* logging: {
|
|
30
|
-
* level: 'debug', // 'error' | 'warn' | 'info' | 'debug' | 'trace'
|
|
31
|
-
* name: 'my-app',
|
|
32
|
-
* },
|
|
33
|
-
* });
|
|
34
|
-
* ```
|
|
35
|
-
*
|
|
36
|
-
* ## JSONL Tracing
|
|
37
|
-
*
|
|
38
|
-
* Write all messages to a JSONL file for debugging:
|
|
39
|
-
*
|
|
40
|
-
* ```typescript
|
|
41
|
-
* const conductor = new Conductor({
|
|
42
|
-
* instantiator: fromCommands(['my-agent']),
|
|
43
|
-
* trace: {
|
|
44
|
-
* path: '/tmp/conductor-trace.jsonl',
|
|
45
|
-
* },
|
|
46
|
-
* });
|
|
47
|
-
* ```
|
|
48
|
-
*
|
|
49
|
-
* ## Architecture
|
|
50
|
-
*
|
|
51
|
-
* The conductor uses a central message queue to preserve ordering:
|
|
52
|
-
*
|
|
53
|
-
* ```
|
|
54
|
-
* Client ←→ Conductor ←→ [Proxy 0] ←→ [Proxy 1] ←→ ... ←→ Agent
|
|
55
|
-
* ```
|
|
56
|
-
*
|
|
57
|
-
* All messages flow through the conductor's event loop, ensuring that
|
|
58
|
-
* responses never overtake notifications.
|
|
59
|
-
*
|
|
60
|
-
* @module
|
|
61
|
-
*/
|
|
62
|
-
// Conductor
|
|
63
1
|
export { Conductor } from "./conductor.js";
|
|
64
|
-
|
|
65
|
-
export {
|
|
66
|
-
// Instantiators
|
|
67
|
-
export { fromCommands, fromConnectors, dynamic, staticInstantiator, } from "./instantiators.js";
|
|
2
|
+
export { createLogger, createNoopLogger, getLogger, setLogger } from "./logger.js";
|
|
3
|
+
export { fromCommands, fromConnectors, dynamic, staticInstantiator } from "./instantiators.js";
|
|
68
4
|
export { ROLE_COUNTERPART } from "./types.js";
|
|
69
|
-
// Message queue
|
|
70
5
|
export { MessageQueue } from "./message-queue.js";
|
|
71
|
-
|
|
72
|
-
export {
|
|
73
|
-
|
|
74
|
-
export { McpBridge, createHttpListener, } from "./mcp-bridge/index.js";
|
|
75
|
-
export { isJsonRpcRequest, isJsonRpcNotification, isJsonRpcResponse, createRequest, createNotification, createSuccessResponse, createErrorResponse, createResponder, } from "@thinkwell/protocol";
|
|
76
|
-
//# sourceMappingURL=index.js.map
|
|
6
|
+
export { StdioConnector, stdio, ChannelConnector, createChannelPair, inProcess, echoComponent } from "./connectors/index.js";
|
|
7
|
+
export { McpBridge, createHttpListener } from "./mcp-bridge/index.js";
|
|
8
|
+
export { isJsonRpcRequest, isJsonRpcNotification, isJsonRpcResponse, createRequest, createNotification, createSuccessResponse, createErrorResponse, createResponder } from "@thinkwell/protocol";
|