@agentconnect/sdk 0.1.1 → 0.1.3

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/host.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ export type EnsureHostOptions = {
2
+ host?: string;
3
+ port?: number;
4
+ appPath?: string;
5
+ cliPackage?: string;
6
+ runtime?: string;
7
+ timeoutMs?: number;
8
+ debug?: boolean;
9
+ };
10
+ export declare function ensureAgentConnectHost(options?: EnsureHostOptions): Promise<void>;
package/dist/host.js ADDED
@@ -0,0 +1,184 @@
1
+ import fs from 'fs';
2
+ import net from 'net';
3
+ import path from 'path';
4
+ import { spawn } from 'child_process';
5
+ const HOST_KEY = '__agentconnectHostState__';
6
+ function getState() {
7
+ const globalAny = globalThis;
8
+ if (!globalAny[HOST_KEY]) {
9
+ globalAny[HOST_KEY] = { status: 'idle' };
10
+ }
11
+ return globalAny[HOST_KEY];
12
+ }
13
+ function findExecutable(name) {
14
+ const envPath = process.env.PATH || '';
15
+ const extensions = process.platform === 'win32' ? ['.exe', '.cmd', '.bat', ''] : [''];
16
+ for (const entry of envPath.split(path.delimiter)) {
17
+ if (!entry)
18
+ continue;
19
+ for (const ext of extensions) {
20
+ const candidate = path.join(entry, `${name}${ext}`);
21
+ try {
22
+ fs.accessSync(candidate, fs.constants.X_OK);
23
+ return candidate;
24
+ }
25
+ catch {
26
+ continue;
27
+ }
28
+ }
29
+ }
30
+ return null;
31
+ }
32
+ function findPackageRoot(startDir, pkgName) {
33
+ let current = startDir;
34
+ while (true) {
35
+ const candidate = path.join(current, 'node_modules', pkgName, 'package.json');
36
+ if (fs.existsSync(candidate))
37
+ return path.dirname(candidate);
38
+ const parent = path.dirname(current);
39
+ if (parent === current)
40
+ break;
41
+ current = parent;
42
+ }
43
+ return null;
44
+ }
45
+ function resolveCliEntry(appPath, cliPackage) {
46
+ const pkgRoot = findPackageRoot(appPath, cliPackage);
47
+ if (!pkgRoot) {
48
+ throw new Error(`AgentConnect CLI not found. Install ${cliPackage} and retry.`);
49
+ }
50
+ const pkgJsonPath = path.join(pkgRoot, 'package.json');
51
+ const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));
52
+ let entry = pkg.main || 'dist/index.js';
53
+ if (typeof pkg.bin === 'string') {
54
+ entry = pkg.bin;
55
+ }
56
+ else if (pkg.bin && typeof pkg.bin === 'object') {
57
+ entry = pkg.bin.agentconnect || entry;
58
+ }
59
+ const resolved = path.join(pkgRoot, entry);
60
+ if (!fs.existsSync(resolved)) {
61
+ throw new Error(`AgentConnect CLI entry not found at ${resolved}`);
62
+ }
63
+ return resolved;
64
+ }
65
+ function resolveRuntime(runtime) {
66
+ if (runtime)
67
+ return runtime;
68
+ const override = process.env.AGENTCONNECT_NODE;
69
+ if (override)
70
+ return override;
71
+ if (!process.execPath.toLowerCase().includes('bun'))
72
+ return process.execPath;
73
+ return findExecutable('node') || 'node';
74
+ }
75
+ function isPortOpen(host, port, timeoutMs = 300) {
76
+ return new Promise((resolve) => {
77
+ const socket = new net.Socket();
78
+ const finalize = (result) => {
79
+ socket.destroy();
80
+ resolve(result);
81
+ };
82
+ socket.setTimeout(timeoutMs);
83
+ socket.once('error', () => finalize(false));
84
+ socket.once('timeout', () => finalize(false));
85
+ socket.connect(port, host, () => finalize(true));
86
+ });
87
+ }
88
+ async function waitForPort(host, port, child, timeoutMs) {
89
+ const start = Date.now();
90
+ while (Date.now() - start < timeoutMs) {
91
+ if (await isPortOpen(host, port))
92
+ return true;
93
+ if (child?.exitCode !== null)
94
+ return false;
95
+ await new Promise((resolve) => setTimeout(resolve, 250));
96
+ }
97
+ return isPortOpen(host, port);
98
+ }
99
+ function registerCleanup(state) {
100
+ if (state.cleanupRegistered)
101
+ return;
102
+ state.cleanupRegistered = true;
103
+ process.on('exit', () => {
104
+ state.child?.kill('SIGTERM');
105
+ });
106
+ process.on('SIGINT', () => {
107
+ state.child?.kill('SIGTERM');
108
+ process.exit(0);
109
+ });
110
+ }
111
+ async function startHost(options, state) {
112
+ const cliPath = resolveCliEntry(options.appPath, options.cliPackage);
113
+ const args = [
114
+ cliPath,
115
+ 'dev',
116
+ '--host',
117
+ options.host,
118
+ '--port',
119
+ String(options.port),
120
+ '--app',
121
+ options.appPath,
122
+ ];
123
+ const runtime = resolveRuntime(options.runtime);
124
+ if (options.debug) {
125
+ console.info('[AgentConnect][Host] starting', { runtime, args });
126
+ }
127
+ const child = spawn(runtime, args, { stdio: options.debug ? 'pipe' : 'ignore' });
128
+ state.child = child;
129
+ state.status = 'starting';
130
+ registerCleanup(state);
131
+ if (options.debug) {
132
+ child.stdout?.on('data', (chunk) => {
133
+ console.info('[AgentConnect][Host]', String(chunk).trim());
134
+ });
135
+ child.stderr?.on('data', (chunk) => {
136
+ console.error('[AgentConnect][Host]', String(chunk).trim());
137
+ });
138
+ }
139
+ child.on('error', (error) => {
140
+ if (options.debug) {
141
+ console.error('[AgentConnect][Host] spawn error', error);
142
+ }
143
+ });
144
+ child.on('exit', () => {
145
+ if (state.child === child) {
146
+ state.status = 'idle';
147
+ state.child = undefined;
148
+ }
149
+ });
150
+ const started = await waitForPort(options.host, options.port, child, options.timeoutMs);
151
+ if (!started) {
152
+ child.kill('SIGTERM');
153
+ throw new Error('AgentConnect host failed to start. Ensure Node.js 20+ is installed.');
154
+ }
155
+ state.status = 'running';
156
+ }
157
+ export async function ensureAgentConnectHost(options = {}) {
158
+ const state = getState();
159
+ const resolvedOptions = {
160
+ host: options.host || process.env.AGENTCONNECT_HOST || '127.0.0.1',
161
+ port: options.port || Number(process.env.AGENTCONNECT_PORT || 9630),
162
+ appPath: options.appPath || process.env.AGENTCONNECT_APP_PATH || process.cwd(),
163
+ cliPackage: options.cliPackage || process.env.AGENTCONNECT_CLI_PACKAGE || '@agentconnect/cli',
164
+ runtime: options.runtime || '',
165
+ timeoutMs: options.timeoutMs || 8000,
166
+ debug: options.debug ?? process.env.AGENTCONNECT_DEBUG === '1',
167
+ };
168
+ if (await isPortOpen(resolvedOptions.host, resolvedOptions.port)) {
169
+ state.status = 'running';
170
+ return;
171
+ }
172
+ if (state.startPromise)
173
+ return state.startPromise;
174
+ state.startPromise = startHost(resolvedOptions, state)
175
+ .catch((error) => {
176
+ state.status = 'idle';
177
+ state.child = undefined;
178
+ throw error;
179
+ })
180
+ .finally(() => {
181
+ state.startPromise = undefined;
182
+ });
183
+ return state.startPromise;
184
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentconnect/sdk",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "homepage": "https://github.com/rayzhudev/agent-connect",
@@ -19,6 +19,11 @@
19
19
  "import": "./dist/index.js",
20
20
  "types": "./dist/index.d.ts",
21
21
  "default": "./dist/index.js"
22
+ },
23
+ "./host": {
24
+ "import": "./dist/host.js",
25
+ "types": "./dist/host.d.ts",
26
+ "default": "./dist/host.js"
22
27
  }
23
28
  },
24
29
  "files": [
@@ -29,6 +34,7 @@
29
34
  "dev": "tsc --watch"
30
35
  },
31
36
  "devDependencies": {
37
+ "@types/node": "^20.0.0",
32
38
  "typescript": "^5.6.2"
33
39
  },
34
40
  "engines": {