@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.
@@ -1,183 +1,50 @@
1
- /**
2
- * Component instantiator helpers
3
- *
4
- * These functions create ComponentInstantiator objects that determine how
5
- * components are created when the conductor receives an initialize request.
6
- *
7
- * ## Instantiation Modes
8
- *
9
- * - **Static**: Components are determined at construction time (e.g., from a list of commands)
10
- * - **Dynamic**: Components are determined at runtime based on the initialize request
11
- *
12
- * ## Lazy Instantiation
13
- *
14
- * All instantiators are lazy - components are only spawned when the first
15
- * `initialize` request arrives. This allows the conductor to be constructed
16
- * before the component processes need to exist.
17
- */
18
- /**
19
- * Create a component instantiator from static command specifications.
20
- *
21
- * This is the most common way to create an instantiator - provide the
22
- * commands for each component and they will be spawned when needed.
23
- *
24
- * @example
25
- * ```ts
26
- * // Simple string commands
27
- * const instantiator = staticInstantiator({
28
- * proxies: ['sparkle-acp'],
29
- * agent: 'claude-agent',
30
- * });
31
- *
32
- * // With options
33
- * const instantiator = staticInstantiator({
34
- * agent: {
35
- * command: 'my-agent',
36
- * args: ['--mode', 'production'],
37
- * env: { DEBUG: 'true' },
38
- * },
39
- * });
40
- * ```
41
- */
42
1
  export function staticInstantiator(config) {
43
- return {
44
- async instantiate() {
45
- // Dynamic import to avoid circular dependency
46
- const { StdioConnector } = await import("./connectors/stdio.js");
47
- const proxyConnectors = (config.proxies ?? []).map((spec) => new StdioConnector(normalizeCommandSpec(spec)));
48
- const agentConnector = new StdioConnector(normalizeCommandSpec(config.agent));
49
- return {
50
- proxies: proxyConnectors,
51
- agent: agentConnector,
52
- };
53
- },
54
- };
2
+ return {
3
+ async instantiate() {
4
+ const { StdioConnector } = await import("./connectors/stdio.js"), proxyConnectors = (config.proxies ?? []).map((spec) => new StdioConnector(normalizeCommandSpec(spec))), agentConnector = new StdioConnector(normalizeCommandSpec(config.agent));
5
+ return {
6
+ proxies: proxyConnectors,
7
+ agent: agentConnector
8
+ };
9
+ }
10
+ };
55
11
  }
56
- /**
57
- * Create a component instantiator from a simple list of commands.
58
- *
59
- * The last command is treated as the agent, all others as proxies.
60
- * This is a convenience wrapper around `staticInstantiator`.
61
- *
62
- * @example
63
- * ```ts
64
- * // Single agent (no proxies)
65
- * const instantiator = fromCommands(['claude-agent']);
66
- *
67
- * // Agent with proxies
68
- * const instantiator = fromCommands(['sparkle-acp', 'claude-agent']);
69
- * ```
70
- */
71
12
  export function fromCommands(commands) {
72
- if (commands.length === 0) {
73
- throw new Error("At least one command (the agent) is required");
74
- }
75
- return staticInstantiator({
76
- proxies: commands.slice(0, -1),
77
- agent: commands[commands.length - 1],
78
- });
13
+ if (commands.length === 0)
14
+ throw new Error("At least one command (the agent) is required");
15
+ return staticInstantiator({
16
+ proxies: commands.slice(0, -1),
17
+ agent: commands[commands.length - 1]
18
+ });
79
19
  }
80
- /**
81
- * Create a component instantiator from explicit connectors.
82
- *
83
- * This is useful when you have pre-configured connectors or want to use
84
- * in-memory connections for testing.
85
- *
86
- * @example
87
- * ```ts
88
- * const instantiator = fromConnectors(agentConnector, [proxyConnector]);
89
- * ```
90
- */
91
20
  export function fromConnectors(agent, proxies = []) {
92
- return {
93
- async instantiate() {
94
- return { proxies, agent };
95
- },
96
- };
21
+ return {
22
+ async instantiate() {
23
+ return { proxies, agent };
24
+ }
25
+ };
97
26
  }
98
- /**
99
- * Create a component instantiator with a dynamic factory function.
100
- *
101
- * The factory function is called when the first `initialize` request arrives,
102
- * allowing you to make decisions about what components to spawn based on
103
- * the request content.
104
- *
105
- * @example
106
- * ```ts
107
- * const instantiator = dynamic(async (initRequest) => {
108
- * // Choose agent based on client capabilities
109
- * const capabilities = initRequest.params?.capabilities ?? {};
110
- * const agentCommand = capabilities.advanced ? 'advanced-agent' : 'basic-agent';
111
- *
112
- * const { StdioConnector } = await import('./connectors/stdio.js');
113
- * return {
114
- * proxies: [],
115
- * agent: new StdioConnector(agentCommand),
116
- * };
117
- * });
118
- *
119
- * // Or use it to inspect MCP servers
120
- * const instantiator = dynamic(async (initRequest) => {
121
- * const mcpServers = initRequest.params?.mcpServers ?? [];
122
- * console.log('Client provided MCP servers:', mcpServers);
123
- *
124
- * // ... create appropriate components
125
- * });
126
- * ```
127
- */
128
27
  export function dynamic(factory) {
129
- return {
130
- instantiate: factory,
131
- };
28
+ return {
29
+ instantiate: factory
30
+ };
132
31
  }
133
- /**
134
- * Normalize a command specification to full options
135
- */
136
32
  function normalizeCommandSpec(spec) {
137
- if (typeof spec === "string") {
138
- // Parse command string into command and args
139
- const parts = parseCommand(spec);
140
- return {
141
- command: parts[0],
142
- args: parts.slice(1),
143
- };
144
- }
145
- return spec;
33
+ if (typeof spec == "string") {
34
+ const parts = parseCommand(spec);
35
+ return {
36
+ command: parts[0],
37
+ args: parts.slice(1)
38
+ };
39
+ }
40
+ return spec;
146
41
  }
147
- /**
148
- * Parse a command string into command and arguments.
149
- * Handles quoted strings.
150
- */
151
42
  function parseCommand(command) {
152
- const parts = [];
153
- let current = "";
154
- let inQuote = null;
155
- for (let i = 0; i < command.length; i++) {
156
- const char = command[i];
157
- if (inQuote) {
158
- if (char === inQuote) {
159
- inQuote = null;
160
- }
161
- else {
162
- current += char;
163
- }
164
- }
165
- else if (char === '"' || char === "'") {
166
- inQuote = char;
167
- }
168
- else if (char === " " || char === "\t") {
169
- if (current) {
170
- parts.push(current);
171
- current = "";
172
- }
173
- }
174
- else {
175
- current += char;
176
- }
177
- }
178
- if (current) {
179
- parts.push(current);
180
- }
181
- return parts;
43
+ const parts = [];
44
+ let current = "", inQuote = null;
45
+ for (let i = 0; i < command.length; i++) {
46
+ const char = command[i];
47
+ inQuote ? char === inQuote ? inQuote = null : current += char : char === '"' || char === "'" ? inQuote = char : char === " " || char === " " ? current && (parts.push(current), current = "") : current += char;
48
+ }
49
+ return current && parts.push(current), parts;
182
50
  }
183
- //# sourceMappingURL=instantiators.js.map
package/dist/logger.js CHANGED
@@ -1,162 +1,94 @@
1
- /**
2
- * Logging module for the conductor
3
- *
4
- * Provides structured logging with configurable levels and optional JSONL tracing.
5
- * All conductor logging goes through this module to enable consistent output
6
- * formatting and filtering.
7
- *
8
- * ## Log Levels
9
- *
10
- * - `error`: Errors that affect operation (component crashes, protocol violations)
11
- * - `warn`: Warnings about potential issues (missing responses, malformed messages)
12
- * - `info`: Key operational events (component start/stop, connections)
13
- * - `debug`: Detailed debugging information (message routing, state changes)
14
- * - `trace`: Very detailed tracing (every message, internal state)
15
- *
16
- * ## Usage
17
- *
18
- * ```typescript
19
- * const logger = createLogger({ level: 'debug', name: 'my-conductor' });
20
- * logger.info('Component started', { component: 'agent', pid: 1234 });
21
- * logger.error('Failed to connect', { error: err.message });
22
- * ```
23
- *
24
- * ## JSONL Tracing
25
- *
26
- * When enabled, all messages routed through the conductor are written to a
27
- * JSONL file for debugging and analysis:
28
- *
29
- * ```typescript
30
- * const logger = createLogger({
31
- * level: 'info',
32
- * trace: { path: '/tmp/conductor.jsonl' }
33
- * });
34
- * ```
35
- */
36
1
  import { createWriteStream } from "node:fs";
37
2
  const LOG_LEVELS = {
38
- error: 0,
39
- warn: 1,
40
- info: 2,
41
- debug: 3,
42
- trace: 4,
3
+ error: 0,
4
+ warn: 1,
5
+ info: 2,
6
+ debug: 3,
7
+ trace: 4
43
8
  };
44
- /**
45
- * Create a no-op logger that discards all output
46
- */
47
9
  export function createNoopLogger() {
48
- const noop = () => { };
49
- return {
50
- error: noop,
51
- warn: noop,
52
- info: noop,
53
- debug: noop,
54
- trace: noop,
55
- traceMessage: noop,
56
- isEnabled: () => false,
57
- child: () => createNoopLogger(),
58
- close: async () => { },
59
- };
10
+ const noop = () => {
11
+ };
12
+ return {
13
+ error: noop,
14
+ warn: noop,
15
+ info: noop,
16
+ debug: noop,
17
+ trace: noop,
18
+ traceMessage: noop,
19
+ isEnabled: () => !1,
20
+ child: () => createNoopLogger(),
21
+ close: async () => {
22
+ }
23
+ };
60
24
  }
61
- /**
62
- * Create a logger with the specified options
63
- */
64
25
  export function createLogger(options = {}) {
65
- const level = options.level ?? "info";
66
- const levelNum = LOG_LEVELS[level];
67
- const name = options.name;
68
- const useJson = options.json ?? false;
69
- // Set up trace file if configured
70
- let traceStream = null;
71
- if (options.trace) {
72
- traceStream = createWriteStream(options.trace.path, { flags: "a" });
73
- }
74
- function isEnabled(checkLevel) {
75
- return LOG_LEVELS[checkLevel] <= levelNum;
76
- }
77
- function log(logLevel, message, data) {
78
- if (!isEnabled(logLevel)) {
79
- return;
80
- }
81
- const entry = {
82
- timestamp: new Date().toISOString(),
83
- level: logLevel,
84
- name,
85
- message,
86
- data,
87
- };
88
- if (useJson) {
89
- // Output as JSON for machine parsing
90
- const output = logLevel === "error" || logLevel === "warn" ? console.error : console.log;
91
- output(JSON.stringify(entry));
92
- }
93
- else {
94
- // Human-readable format
95
- const prefix = name ? `[${name}]` : "";
96
- const levelStr = logLevel.toUpperCase().padEnd(5);
97
- const dataStr = data ? ` ${JSON.stringify(data)}` : "";
98
- const output = logLevel === "error" || logLevel === "warn" ? console.error : console.log;
99
- output(`${entry.timestamp} ${levelStr} ${prefix}${prefix ? " " : ""}${message}${dataStr}`);
100
- }
101
- }
102
- function traceMessage(entry) {
103
- if (!traceStream) {
104
- return;
105
- }
106
- const fullEntry = {
107
- timestamp: new Date().toISOString(),
108
- ...entry,
109
- };
110
- traceStream.write(JSON.stringify(fullEntry) + "\n");
111
- }
112
- function child(childName) {
113
- const fullName = name ? `${name}:${childName}` : childName;
114
- return createLogger({
115
- ...options,
116
- name: fullName,
117
- // Share trace stream with parent
118
- trace: undefined, // Don't create new stream
119
- });
120
- }
121
- async function close() {
122
- if (traceStream) {
123
- await new Promise((resolve, reject) => {
124
- traceStream.end((err) => {
125
- if (err)
126
- reject(err);
127
- else
128
- resolve();
129
- });
130
- });
131
- traceStream = null;
132
- }
26
+ const level = options.level ?? "info", levelNum = LOG_LEVELS[level], name = options.name, useJson = options.json ?? !1;
27
+ let traceStream = null;
28
+ options.trace && (traceStream = createWriteStream(options.trace.path, { flags: "a" }));
29
+ function isEnabled(checkLevel) {
30
+ return LOG_LEVELS[checkLevel] <= levelNum;
31
+ }
32
+ function log(logLevel, message, data) {
33
+ if (!isEnabled(logLevel))
34
+ return;
35
+ const entry = {
36
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
37
+ level: logLevel,
38
+ name,
39
+ message,
40
+ data
41
+ };
42
+ if (useJson)
43
+ (logLevel === "error" || logLevel === "warn" ? console.error : console.log)(JSON.stringify(entry));
44
+ else {
45
+ const prefix = name ? `[${name}]` : "", levelStr = logLevel.toUpperCase().padEnd(5), dataStr = data ? ` ${JSON.stringify(data)}` : "";
46
+ (logLevel === "error" || logLevel === "warn" ? console.error : console.log)(`${entry.timestamp} ${levelStr} ${prefix}${prefix ? " " : ""}${message}${dataStr}`);
133
47
  }
134
- return {
135
- error: (message, data) => log("error", message, data),
136
- warn: (message, data) => log("warn", message, data),
137
- info: (message, data) => log("info", message, data),
138
- debug: (message, data) => log("debug", message, data),
139
- trace: (message, data) => log("trace", message, data),
140
- traceMessage,
141
- isEnabled,
142
- child,
143
- close,
48
+ }
49
+ function traceMessage(entry) {
50
+ if (!traceStream)
51
+ return;
52
+ const fullEntry = {
53
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
54
+ ...entry
144
55
  };
56
+ traceStream.write(JSON.stringify(fullEntry) + `
57
+ `);
58
+ }
59
+ function child(childName) {
60
+ const fullName = name ? `${name}:${childName}` : childName;
61
+ return createLogger({
62
+ ...options,
63
+ name: fullName,
64
+ // Share trace stream with parent
65
+ trace: void 0
66
+ // Don't create new stream
67
+ });
68
+ }
69
+ async function close() {
70
+ traceStream && (await new Promise((resolve, reject) => {
71
+ traceStream.end((err) => {
72
+ err ? reject(err) : resolve();
73
+ });
74
+ }), traceStream = null);
75
+ }
76
+ return {
77
+ error: (message, data) => log("error", message, data),
78
+ warn: (message, data) => log("warn", message, data),
79
+ info: (message, data) => log("info", message, data),
80
+ debug: (message, data) => log("debug", message, data),
81
+ trace: (message, data) => log("trace", message, data),
82
+ traceMessage,
83
+ isEnabled,
84
+ child,
85
+ close
86
+ };
145
87
  }
146
- /**
147
- * Default logger instance (silent by default, can be replaced)
148
- */
149
88
  let defaultLogger = createNoopLogger();
150
- /**
151
- * Get the default logger
152
- */
153
89
  export function getLogger() {
154
- return defaultLogger;
90
+ return defaultLogger;
155
91
  }
156
- /**
157
- * Set the default logger
158
- */
159
92
  export function setLogger(logger) {
160
- defaultLogger = logger;
93
+ defaultLogger = logger;
161
94
  }
162
- //# sourceMappingURL=logger.js.map