nexus-agentd 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/README.md +253 -0
- package/dist/acp/runtime.d.ts +26 -0
- package/dist/acp/runtime.js +420 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +24 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +115 -0
- package/dist/drivers/claude.d.ts +2 -0
- package/dist/drivers/claude.js +9 -0
- package/dist/drivers/codex.d.ts +2 -0
- package/dist/drivers/codex.js +9 -0
- package/dist/drivers/index.d.ts +4 -0
- package/dist/drivers/index.js +30 -0
- package/dist/drivers/openclaw.d.ts +2 -0
- package/dist/drivers/openclaw.js +13 -0
- package/dist/drivers/opencode.d.ts +2 -0
- package/dist/drivers/opencode.js +9 -0
- package/dist/drivers/pi.d.ts +13 -0
- package/dist/drivers/pi.js +31 -0
- package/dist/drivers/stdio.d.ts +13 -0
- package/dist/drivers/stdio.js +114 -0
- package/dist/drivers/types.d.ts +14 -0
- package/dist/drivers/types.js +1 -0
- package/dist/events.d.ts +13 -0
- package/dist/events.js +40 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +48 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.js +170 -0
- package/dist/session-contract.d.ts +12 -0
- package/dist/session-contract.js +1 -0
- package/dist/session.d.ts +26 -0
- package/dist/session.js +210 -0
- package/dist/types.d.ts +80 -0
- package/dist/types.js +7 -0
- package/dist/workspace.d.ts +7 -0
- package/dist/workspace.js +34 -0
- package/nexus-agentd.example.json +43 -0
- package/package.json +52 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { startAgentd } from './index.js';
|
|
4
|
+
const configPath = resolveConfigPath(process.argv.slice(2));
|
|
5
|
+
const runtime = await startAgentd(configPath);
|
|
6
|
+
const address = runtime.server.address();
|
|
7
|
+
const label = typeof address === 'object' && address
|
|
8
|
+
? `${address.address}:${address.port}`
|
|
9
|
+
: String(address);
|
|
10
|
+
console.log(`nexus-agentd listening on ${label}`);
|
|
11
|
+
let closing = false;
|
|
12
|
+
const close = async () => {
|
|
13
|
+
if (closing)
|
|
14
|
+
return;
|
|
15
|
+
closing = true;
|
|
16
|
+
await runtime.close();
|
|
17
|
+
};
|
|
18
|
+
process.once('SIGINT', () => void close().finally(() => process.exit(0)));
|
|
19
|
+
process.once('SIGTERM', () => void close().finally(() => process.exit(0)));
|
|
20
|
+
function resolveConfigPath(args) {
|
|
21
|
+
const index = args.indexOf('--config');
|
|
22
|
+
const value = index >= 0 ? args[index + 1] : process.env.NEXUS_AGENTD_CONFIG;
|
|
23
|
+
return path.resolve(value || 'nexus-agentd.json');
|
|
24
|
+
}
|
package/dist/config.d.ts
ADDED
package/dist/config.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { agentdDriverKinds } from './types.js';
|
|
4
|
+
export async function loadAgentdConfig(filePath) {
|
|
5
|
+
const absolute = path.resolve(filePath);
|
|
6
|
+
const raw = await readFile(absolute, 'utf8');
|
|
7
|
+
let value;
|
|
8
|
+
try {
|
|
9
|
+
value = JSON.parse(raw);
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
throw new Error(`Invalid nexus-agentd config JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
13
|
+
}
|
|
14
|
+
if (!isRecord(value))
|
|
15
|
+
throw new Error('nexus-agentd config root must be an object');
|
|
16
|
+
const listen = isRecord(value.listen) ? value.listen : {};
|
|
17
|
+
const host = stringValue(listen.host) || '127.0.0.1';
|
|
18
|
+
const port = numberValue(listen.port, 8787, 1, 65535);
|
|
19
|
+
const authToken = resolveSecret(stringValue(value.authToken));
|
|
20
|
+
if (!authToken)
|
|
21
|
+
throw new Error('nexus-agentd authToken is required');
|
|
22
|
+
const workspaceRoots = arrayOfStrings(value.workspaceRoots);
|
|
23
|
+
if (!workspaceRoots.length) {
|
|
24
|
+
throw new Error('nexus-agentd workspaceRoots must contain at least one path');
|
|
25
|
+
}
|
|
26
|
+
if (!isRecord(value.agents)) {
|
|
27
|
+
throw new Error('nexus-agentd agents must be an object');
|
|
28
|
+
}
|
|
29
|
+
const agents = {};
|
|
30
|
+
for (const [id, input] of Object.entries(value.agents)) {
|
|
31
|
+
if (!isRecord(input))
|
|
32
|
+
throw new Error(`Agent config must be an object: ${id}`);
|
|
33
|
+
const driver = stringValue(input.driver) || inferDriver(id);
|
|
34
|
+
if (!isAgentdDriverKind(driver)) {
|
|
35
|
+
throw new Error(`Unsupported nexus-agentd driver: ${driver || id}`);
|
|
36
|
+
}
|
|
37
|
+
const permissionPolicy = stringValue(input.permissionPolicy) || 'ask';
|
|
38
|
+
if (permissionPolicy !== 'ask' && permissionPolicy !== 'deny') {
|
|
39
|
+
throw new Error(`Invalid permissionPolicy for ${id}`);
|
|
40
|
+
}
|
|
41
|
+
agents[id] = {
|
|
42
|
+
driver,
|
|
43
|
+
name: optionalString(input.name),
|
|
44
|
+
description: optionalString(input.description),
|
|
45
|
+
enabled: input.enabled !== false,
|
|
46
|
+
command: optionalString(input.command),
|
|
47
|
+
args: input.args === undefined ? undefined : arrayOfStrings(input.args),
|
|
48
|
+
inheritEnv: input.inheritEnv === undefined
|
|
49
|
+
? undefined
|
|
50
|
+
: arrayOfStrings(input.inheritEnv),
|
|
51
|
+
env: recordOfStrings(input.env),
|
|
52
|
+
permissionPolicy,
|
|
53
|
+
permissionTimeoutMs: numberValue(input.permissionTimeoutMs, 15 * 60 * 1000, 1000, 24 * 60 * 60 * 1000)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
listen: { host, port },
|
|
58
|
+
authToken,
|
|
59
|
+
workspaceRoots,
|
|
60
|
+
maxRequestBytes: numberValue(value.maxRequestBytes, 1024 * 1024, 1024, 16 * 1024 * 1024),
|
|
61
|
+
maxEventsPerSession: numberValue(value.maxEventsPerSession, 2048, 64, 100_000),
|
|
62
|
+
maxOutputChars: numberValue(value.maxOutputChars, 512 * 1024, 16 * 1024, 16 * 1024 * 1024),
|
|
63
|
+
sessionTtlMs: numberValue(value.sessionTtlMs, 24 * 60 * 60 * 1000, 60_000, 30 * 24 * 60 * 60 * 1000),
|
|
64
|
+
agents
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export function resolveSecret(value) {
|
|
68
|
+
if (!value.startsWith('env:'))
|
|
69
|
+
return value;
|
|
70
|
+
const name = value.slice(4).trim();
|
|
71
|
+
if (!name)
|
|
72
|
+
throw new Error('Secret environment variable name is empty');
|
|
73
|
+
const secret = process.env[name];
|
|
74
|
+
if (!secret)
|
|
75
|
+
throw new Error(`Secret environment variable is missing: ${name}`);
|
|
76
|
+
return secret;
|
|
77
|
+
}
|
|
78
|
+
function inferDriver(id) {
|
|
79
|
+
const value = id.trim().toLowerCase();
|
|
80
|
+
return isAgentdDriverKind(value) ? value : '';
|
|
81
|
+
}
|
|
82
|
+
function isAgentdDriverKind(value) {
|
|
83
|
+
return agentdDriverKinds.includes(value);
|
|
84
|
+
}
|
|
85
|
+
function isRecord(value) {
|
|
86
|
+
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
87
|
+
}
|
|
88
|
+
function stringValue(value) {
|
|
89
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
90
|
+
}
|
|
91
|
+
function optionalString(value) {
|
|
92
|
+
return stringValue(value) || undefined;
|
|
93
|
+
}
|
|
94
|
+
function arrayOfStrings(value) {
|
|
95
|
+
if (!Array.isArray(value))
|
|
96
|
+
return [];
|
|
97
|
+
return value.map(stringValue).filter(Boolean);
|
|
98
|
+
}
|
|
99
|
+
function recordOfStrings(value) {
|
|
100
|
+
if (!isRecord(value))
|
|
101
|
+
return undefined;
|
|
102
|
+
const result = {};
|
|
103
|
+
for (const [key, item] of Object.entries(value)) {
|
|
104
|
+
if (typeof item !== 'string')
|
|
105
|
+
continue;
|
|
106
|
+
result[key] = item;
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
function numberValue(value, fallback, min, max) {
|
|
111
|
+
const number = Number(value);
|
|
112
|
+
if (!Number.isFinite(number))
|
|
113
|
+
return fallback;
|
|
114
|
+
return Math.min(max, Math.max(min, Math.trunc(number)));
|
|
115
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createStdioAcpDriver } from './stdio.js';
|
|
2
|
+
export function createClaudeDriver(id, config) {
|
|
3
|
+
return createStdioAcpDriver(id, config, {
|
|
4
|
+
name: 'Claude Code',
|
|
5
|
+
description: 'Claude Code through the official Claude Agent ACP adapter',
|
|
6
|
+
command: 'claude-agent-acp',
|
|
7
|
+
args: []
|
|
8
|
+
});
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createStdioAcpDriver } from './stdio.js';
|
|
2
|
+
export function createCodexDriver(id, config) {
|
|
3
|
+
return createStdioAcpDriver(id, config, {
|
|
4
|
+
name: 'Codex',
|
|
5
|
+
description: 'Codex CLI through the official Codex ACP adapter',
|
|
6
|
+
command: 'codex-acp',
|
|
7
|
+
args: []
|
|
8
|
+
});
|
|
9
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createClaudeDriver } from './claude.js';
|
|
2
|
+
import { createCodexDriver } from './codex.js';
|
|
3
|
+
import { createOpenCodeDriver } from './opencode.js';
|
|
4
|
+
import { createOpenClawDriver } from './openclaw.js';
|
|
5
|
+
import { createPiDriver } from './pi.js';
|
|
6
|
+
export function createDriverRegistry(config) {
|
|
7
|
+
const drivers = new Map();
|
|
8
|
+
for (const [id, driver] of Object.entries(config.agents)) {
|
|
9
|
+
if (driver.enabled === false)
|
|
10
|
+
continue;
|
|
11
|
+
switch (driver.driver) {
|
|
12
|
+
case 'opencode':
|
|
13
|
+
drivers.set(id, createOpenCodeDriver(id, driver));
|
|
14
|
+
break;
|
|
15
|
+
case 'claude':
|
|
16
|
+
drivers.set(id, createClaudeDriver(id, driver));
|
|
17
|
+
break;
|
|
18
|
+
case 'codex':
|
|
19
|
+
drivers.set(id, createCodexDriver(id, driver));
|
|
20
|
+
break;
|
|
21
|
+
case 'pi':
|
|
22
|
+
drivers.set(id, createPiDriver(id, driver));
|
|
23
|
+
break;
|
|
24
|
+
case 'openclaw':
|
|
25
|
+
drivers.set(id, createOpenClawDriver(id, driver));
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return drivers;
|
|
30
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createStdioAcpDriver } from './stdio.js';
|
|
2
|
+
export function createOpenClawDriver(id, config) {
|
|
3
|
+
return createStdioAcpDriver(id, config, {
|
|
4
|
+
name: 'OpenClaw',
|
|
5
|
+
description: 'OpenClaw Gateway through its native ACP stdio bridge',
|
|
6
|
+
command: 'openclaw',
|
|
7
|
+
args: ['acp'],
|
|
8
|
+
env: {
|
|
9
|
+
OPENCLAW_HIDE_BANNER: '1',
|
|
10
|
+
OPENCLAW_SUPPRESS_NOTES: '1'
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createStdioAcpDriver } from './stdio.js';
|
|
2
|
+
export function createOpenCodeDriver(id, config) {
|
|
3
|
+
return createStdioAcpDriver(id, config, {
|
|
4
|
+
name: 'OpenCode',
|
|
5
|
+
description: 'OpenCode Coding Agent through its native ACP stdio server',
|
|
6
|
+
command: 'opencode',
|
|
7
|
+
args: ['acp']
|
|
8
|
+
});
|
|
9
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AgentdDriverConfig } from '../types.js';
|
|
2
|
+
export declare function createPiDriver(id: string, config: AgentdDriverConfig): {
|
|
3
|
+
probe(): Promise<import("../types.js").AgentdAgentView>;
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
command: string;
|
|
8
|
+
args: string[];
|
|
9
|
+
env: NodeJS.ProcessEnv;
|
|
10
|
+
permissionPolicy: import("../types.js").PermissionPolicy;
|
|
11
|
+
permissionTimeoutMs: number;
|
|
12
|
+
spawn(workspace: string): import("child_process").ChildProcessWithoutNullStreams;
|
|
13
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createStdioAcpDriver, probeCommand } from './stdio.js';
|
|
2
|
+
export function createPiDriver(id, config) {
|
|
3
|
+
const driver = createStdioAcpDriver(id, config, {
|
|
4
|
+
name: 'Pi',
|
|
5
|
+
description: 'Pi coding agent through the pi-acp adapter',
|
|
6
|
+
command: 'pi-acp',
|
|
7
|
+
args: []
|
|
8
|
+
});
|
|
9
|
+
const probeAdapter = driver.probe.bind(driver);
|
|
10
|
+
return {
|
|
11
|
+
...driver,
|
|
12
|
+
async probe() {
|
|
13
|
+
const adapter = await probeAdapter();
|
|
14
|
+
if (!adapter.ready)
|
|
15
|
+
return adapter;
|
|
16
|
+
const command = driver.env.PI_ACP_PI_COMMAND || 'pi';
|
|
17
|
+
try {
|
|
18
|
+
const version = await probeCommand(command, ['--version'], driver.env, 'Pi runtime');
|
|
19
|
+
return { ...adapter, version };
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
return {
|
|
23
|
+
...adapter,
|
|
24
|
+
ready: false,
|
|
25
|
+
version: undefined,
|
|
26
|
+
error: error instanceof Error ? error.message : String(error)
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AgentdDriverConfig } from '../types.js';
|
|
2
|
+
import type { AgentDriver } from './types.js';
|
|
3
|
+
export interface StdioAcpDriverDefaults {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
command: string;
|
|
7
|
+
args: string[];
|
|
8
|
+
probeArgs?: string[];
|
|
9
|
+
env?: Record<string, string>;
|
|
10
|
+
}
|
|
11
|
+
export declare function createStdioAcpDriver(id: string, config: AgentdDriverConfig, defaults: StdioAcpDriverDefaults): AgentDriver;
|
|
12
|
+
export declare function buildEnvironment(config: AgentdDriverConfig, defaults?: Record<string, string>): NodeJS.ProcessEnv;
|
|
13
|
+
export declare function probeCommand(command: string, args: string[], env: NodeJS.ProcessEnv, label: string): Promise<string>;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { resolveSecret } from '../config.js';
|
|
3
|
+
const BASE_ENV_KEYS = [
|
|
4
|
+
'PATH',
|
|
5
|
+
'HOME',
|
|
6
|
+
'USER',
|
|
7
|
+
'XDG_CONFIG_HOME',
|
|
8
|
+
'XDG_DATA_HOME',
|
|
9
|
+
'SHELL',
|
|
10
|
+
'TMP',
|
|
11
|
+
'TMPDIR',
|
|
12
|
+
'TEMP',
|
|
13
|
+
'LANG',
|
|
14
|
+
'LC_ALL'
|
|
15
|
+
];
|
|
16
|
+
export function createStdioAcpDriver(id, config, defaults) {
|
|
17
|
+
const command = config.command || defaults.command;
|
|
18
|
+
const args = config.args === undefined ? [...defaults.args] : [...config.args];
|
|
19
|
+
const env = buildEnvironment(config, defaults.env);
|
|
20
|
+
const name = config.name || defaults.name;
|
|
21
|
+
const description = config.description || defaults.description;
|
|
22
|
+
return {
|
|
23
|
+
id,
|
|
24
|
+
name,
|
|
25
|
+
description,
|
|
26
|
+
command,
|
|
27
|
+
args,
|
|
28
|
+
env,
|
|
29
|
+
permissionPolicy: config.permissionPolicy || 'ask',
|
|
30
|
+
permissionTimeoutMs: config.permissionTimeoutMs || 15 * 60 * 1000,
|
|
31
|
+
async probe() {
|
|
32
|
+
try {
|
|
33
|
+
const version = await probeCommand(command, defaults.probeArgs || ['--version'], env, name);
|
|
34
|
+
return {
|
|
35
|
+
id,
|
|
36
|
+
name,
|
|
37
|
+
description,
|
|
38
|
+
protocol: 'acp',
|
|
39
|
+
ready: true,
|
|
40
|
+
version
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
return {
|
|
45
|
+
id,
|
|
46
|
+
name,
|
|
47
|
+
description,
|
|
48
|
+
protocol: 'acp',
|
|
49
|
+
ready: false,
|
|
50
|
+
error: error instanceof Error ? error.message : String(error)
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
spawn(workspace) {
|
|
55
|
+
return spawn(command, args, {
|
|
56
|
+
cwd: workspace,
|
|
57
|
+
env,
|
|
58
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export function buildEnvironment(config, defaults = {}) {
|
|
64
|
+
const result = {};
|
|
65
|
+
for (const key of new Set([...BASE_ENV_KEYS, ...(config.inheritEnv || [])])) {
|
|
66
|
+
const value = process.env[key];
|
|
67
|
+
if (value !== undefined)
|
|
68
|
+
result[key] = value;
|
|
69
|
+
}
|
|
70
|
+
Object.assign(result, defaults);
|
|
71
|
+
for (const [key, value] of Object.entries(config.env || {})) {
|
|
72
|
+
result[key] = resolveSecret(value);
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
export function probeCommand(command, args, env, label) {
|
|
77
|
+
return new Promise((resolve, reject) => {
|
|
78
|
+
const child = spawn(command, args, {
|
|
79
|
+
env,
|
|
80
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
81
|
+
});
|
|
82
|
+
let output = '';
|
|
83
|
+
let settled = false;
|
|
84
|
+
const finish = (error) => {
|
|
85
|
+
if (settled)
|
|
86
|
+
return;
|
|
87
|
+
settled = true;
|
|
88
|
+
clearTimeout(timer);
|
|
89
|
+
if (error)
|
|
90
|
+
reject(error);
|
|
91
|
+
else
|
|
92
|
+
resolve(output.trim().split(/\r?\n/)[0] || 'unknown');
|
|
93
|
+
};
|
|
94
|
+
const timer = setTimeout(() => {
|
|
95
|
+
child.kill();
|
|
96
|
+
finish(new Error(`${label} probe timed out`));
|
|
97
|
+
}, 5000);
|
|
98
|
+
child.stdout.on('data', (chunk) => {
|
|
99
|
+
output = `${output}${String(chunk)}`.slice(-8192);
|
|
100
|
+
});
|
|
101
|
+
child.stderr.on('data', (chunk) => {
|
|
102
|
+
output = `${output}${String(chunk)}`.slice(-8192);
|
|
103
|
+
});
|
|
104
|
+
child.once('error', (error) => finish(error));
|
|
105
|
+
child.once('exit', (code, signal) => {
|
|
106
|
+
if (code === 0)
|
|
107
|
+
finish();
|
|
108
|
+
else {
|
|
109
|
+
finish(new Error(output.trim() ||
|
|
110
|
+
`${label} probe exited with ${signal || code || 'unknown'}`));
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ChildProcessWithoutNullStreams } from 'node:child_process';
|
|
2
|
+
import type { AgentdAgentView, PermissionPolicy } from '../types.js';
|
|
3
|
+
export interface AgentDriver {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
command: string;
|
|
8
|
+
args: string[];
|
|
9
|
+
env: NodeJS.ProcessEnv;
|
|
10
|
+
permissionPolicy: PermissionPolicy;
|
|
11
|
+
permissionTimeoutMs: number;
|
|
12
|
+
probe(): Promise<AgentdAgentView>;
|
|
13
|
+
spawn(workspace: string): ChildProcessWithoutNullStreams;
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AgentdEvent, AgentdEventType } from './types.js';
|
|
2
|
+
export declare class SessionEventLog {
|
|
3
|
+
private readonly sessionId;
|
|
4
|
+
private readonly maxEvents;
|
|
5
|
+
private sequence;
|
|
6
|
+
private events;
|
|
7
|
+
private listeners;
|
|
8
|
+
constructor(sessionId: string, maxEvents: number);
|
|
9
|
+
append(type: AgentdEventType, data?: unknown): AgentdEvent;
|
|
10
|
+
after(id?: string): AgentdEvent[];
|
|
11
|
+
subscribe(listener: (event: AgentdEvent) => void): () => boolean;
|
|
12
|
+
get lastId(): string | undefined;
|
|
13
|
+
}
|
package/dist/events.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export class SessionEventLog {
|
|
2
|
+
sessionId;
|
|
3
|
+
maxEvents;
|
|
4
|
+
sequence = 0;
|
|
5
|
+
events = [];
|
|
6
|
+
listeners = new Set();
|
|
7
|
+
constructor(sessionId, maxEvents) {
|
|
8
|
+
this.sessionId = sessionId;
|
|
9
|
+
this.maxEvents = maxEvents;
|
|
10
|
+
}
|
|
11
|
+
append(type, data) {
|
|
12
|
+
const event = {
|
|
13
|
+
id: String(++this.sequence),
|
|
14
|
+
sessionId: this.sessionId,
|
|
15
|
+
type,
|
|
16
|
+
timestamp: Date.now(),
|
|
17
|
+
data
|
|
18
|
+
};
|
|
19
|
+
this.events.push(event);
|
|
20
|
+
if (this.events.length > this.maxEvents) {
|
|
21
|
+
this.events.splice(0, this.events.length - this.maxEvents);
|
|
22
|
+
}
|
|
23
|
+
for (const listener of this.listeners)
|
|
24
|
+
listener(structuredClone(event));
|
|
25
|
+
return event;
|
|
26
|
+
}
|
|
27
|
+
after(id) {
|
|
28
|
+
const sequence = Number(id || 0);
|
|
29
|
+
return this.events
|
|
30
|
+
.filter((event) => Number(event.id) > sequence)
|
|
31
|
+
.map((event) => structuredClone(event));
|
|
32
|
+
}
|
|
33
|
+
subscribe(listener) {
|
|
34
|
+
this.listeners.add(listener);
|
|
35
|
+
return () => this.listeners.delete(listener);
|
|
36
|
+
}
|
|
37
|
+
get lastId() {
|
|
38
|
+
return this.events.at(-1)?.id;
|
|
39
|
+
}
|
|
40
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Server } from 'node:http';
|
|
2
|
+
import { SessionManager } from './session.js';
|
|
3
|
+
export declare function startAgentd(configPath: string): Promise<{
|
|
4
|
+
config: import("./types.js").AgentdConfig;
|
|
5
|
+
server: Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>;
|
|
6
|
+
sessions: SessionManager;
|
|
7
|
+
close(): Promise<void>;
|
|
8
|
+
}>;
|
|
9
|
+
export * from './config.js';
|
|
10
|
+
export * from './server.js';
|
|
11
|
+
export * from './session.js';
|
|
12
|
+
export * from './types.js';
|
|
13
|
+
export * from './workspace.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { loadAgentdConfig } from './config.js';
|
|
2
|
+
import { createDriverRegistry } from './drivers/index.js';
|
|
3
|
+
import { createAgentdServer } from './server.js';
|
|
4
|
+
import { SessionManager } from './session.js';
|
|
5
|
+
import { WorkspacePolicy } from './workspace.js';
|
|
6
|
+
export async function startAgentd(configPath) {
|
|
7
|
+
const config = await loadAgentdConfig(configPath);
|
|
8
|
+
const workspacePolicy = await WorkspacePolicy.create(config.workspaceRoots);
|
|
9
|
+
const sessions = new SessionManager(config, workspacePolicy, createDriverRegistry(config));
|
|
10
|
+
sessions.startCleanup();
|
|
11
|
+
const server = createAgentdServer(config, sessions);
|
|
12
|
+
await listen(server, config.listen.port, config.listen.host);
|
|
13
|
+
return {
|
|
14
|
+
config,
|
|
15
|
+
server,
|
|
16
|
+
sessions,
|
|
17
|
+
async close() {
|
|
18
|
+
await closeServer(server);
|
|
19
|
+
await sessions.shutdown();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function listen(server, port, host) {
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
const error = (value) => {
|
|
26
|
+
server.off('listening', listening);
|
|
27
|
+
reject(value);
|
|
28
|
+
};
|
|
29
|
+
const listening = () => {
|
|
30
|
+
server.off('error', error);
|
|
31
|
+
resolve();
|
|
32
|
+
};
|
|
33
|
+
server.once('error', error);
|
|
34
|
+
server.once('listening', listening);
|
|
35
|
+
server.listen(port, host);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function closeServer(server) {
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
server.close((error) => (error ? reject(error) : resolve()));
|
|
41
|
+
server.closeIdleConnections?.();
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
export * from './config.js';
|
|
45
|
+
export * from './server.js';
|
|
46
|
+
export * from './session.js';
|
|
47
|
+
export * from './types.js';
|
|
48
|
+
export * from './workspace.js';
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
import type { AgentdConfig } from './types.js';
|
|
3
|
+
import { SessionManager } from './session.js';
|
|
4
|
+
export declare function createAgentdServer(config: AgentdConfig, sessions: SessionManager): http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
|