@openagents-org/agent-connector 0.2.8 → 0.2.9
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/package.json +1 -1
- package/src/installer.js +134 -15
- package/src/paths.js +18 -2
package/package.json
CHANGED
package/src/installer.js
CHANGED
|
@@ -94,6 +94,60 @@ class Installer {
|
|
|
94
94
|
return { success: true, output };
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Install with streaming output via callback.
|
|
99
|
+
* @param {string} agentType
|
|
100
|
+
* @param {function(string)} onData - called with each chunk of output
|
|
101
|
+
* @returns {Promise<{success: boolean, command: string}>}
|
|
102
|
+
*/
|
|
103
|
+
async installStreaming(agentType, onData) {
|
|
104
|
+
const { spawn } = require('child_process');
|
|
105
|
+
const entry = this.registry.getEntry(agentType);
|
|
106
|
+
if (!entry || !entry.install) {
|
|
107
|
+
throw new Error(`No install definition for agent type: ${agentType}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let cmd = this._getInstallCommand(entry.install);
|
|
111
|
+
if (!cmd) {
|
|
112
|
+
throw new Error(`No install command for ${agentType} on ${Installer.platform()}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Add verbose logging for npm so user sees download progress on stderr
|
|
116
|
+
if (cmd.includes('npm install') && !cmd.includes('--loglevel')) {
|
|
117
|
+
cmd = cmd.replace('npm install', 'npm install --loglevel=verbose');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (onData) onData(`$ ${cmd}\n\n`);
|
|
121
|
+
|
|
122
|
+
const env = this._buildShellEnv();
|
|
123
|
+
const shell = process.platform === 'win32'
|
|
124
|
+
? (process.env.ComSpec || 'C:\\Windows\\System32\\cmd.exe')
|
|
125
|
+
: true;
|
|
126
|
+
|
|
127
|
+
return new Promise((resolve, reject) => {
|
|
128
|
+
const proc = spawn(cmd, [], { shell, env, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
129
|
+
|
|
130
|
+
if (proc.stdout) proc.stdout.setEncoding('utf-8');
|
|
131
|
+
if (proc.stderr) proc.stderr.setEncoding('utf-8');
|
|
132
|
+
|
|
133
|
+
if (proc.stdout) proc.stdout.on('data', (d) => { if (onData) onData(d); });
|
|
134
|
+
if (proc.stderr) proc.stderr.on('data', (d) => { if (onData) onData(d); });
|
|
135
|
+
|
|
136
|
+
proc.on('error', (err) => reject(err));
|
|
137
|
+
proc.on('close', (code) => {
|
|
138
|
+
if (code === 0) {
|
|
139
|
+
this._markInstalled(agentType);
|
|
140
|
+
if (onData) onData(`\nDone! ${agentType} is now installed.\n`);
|
|
141
|
+
resolve({ success: true, command: cmd });
|
|
142
|
+
} else {
|
|
143
|
+
const msg = `Install failed with exit code ${code}`;
|
|
144
|
+
if (onData) onData(`\n${msg}\n`);
|
|
145
|
+
reject(new Error(msg));
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
97
151
|
/**
|
|
98
152
|
* Uninstall an agent runtime.
|
|
99
153
|
* @returns {Promise<{success: boolean, output: string}>}
|
|
@@ -115,6 +169,58 @@ class Installer {
|
|
|
115
169
|
return { success: true, output };
|
|
116
170
|
}
|
|
117
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Uninstall with streaming output via callback.
|
|
174
|
+
*/
|
|
175
|
+
async uninstallStreaming(agentType, onData) {
|
|
176
|
+
const { spawn } = require('child_process');
|
|
177
|
+
const entry = this.registry.getEntry(agentType);
|
|
178
|
+
if (!entry || !entry.install) {
|
|
179
|
+
throw new Error(`No install definition for agent type: ${agentType}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const installCmd = this._getInstallCommand(entry.install);
|
|
183
|
+
let cmd = this._deriveUninstallCommand(installCmd);
|
|
184
|
+
if (!cmd) {
|
|
185
|
+
throw new Error(`Cannot derive uninstall command for ${agentType}`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Add verbose logging for npm so user sees progress on stderr
|
|
189
|
+
if (cmd.includes('npm uninstall') && !cmd.includes('--loglevel')) {
|
|
190
|
+
cmd = cmd.replace('npm uninstall', 'npm uninstall --loglevel=verbose');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (onData) onData(`$ ${cmd}\n\n`);
|
|
194
|
+
|
|
195
|
+
const env = this._buildShellEnv();
|
|
196
|
+
const shell = process.platform === 'win32'
|
|
197
|
+
? (process.env.ComSpec || 'C:\\Windows\\System32\\cmd.exe')
|
|
198
|
+
: true;
|
|
199
|
+
|
|
200
|
+
return new Promise((resolve, reject) => {
|
|
201
|
+
const proc = spawn(cmd, [], { shell, env, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
202
|
+
|
|
203
|
+
if (proc.stdout) proc.stdout.setEncoding('utf-8');
|
|
204
|
+
if (proc.stderr) proc.stderr.setEncoding('utf-8');
|
|
205
|
+
|
|
206
|
+
if (proc.stdout) proc.stdout.on('data', (d) => { if (onData) onData(d); });
|
|
207
|
+
if (proc.stderr) proc.stderr.on('data', (d) => { if (onData) onData(d); });
|
|
208
|
+
|
|
209
|
+
proc.on('error', (err) => reject(err));
|
|
210
|
+
proc.on('close', (code) => {
|
|
211
|
+
if (code === 0) {
|
|
212
|
+
this._markUninstalled(agentType);
|
|
213
|
+
if (onData) onData(`\nDone! ${agentType} has been uninstalled.\n`);
|
|
214
|
+
resolve({ success: true, command: cmd });
|
|
215
|
+
} else {
|
|
216
|
+
const msg = `Uninstall failed with exit code ${code}`;
|
|
217
|
+
if (onData) onData(`\n${msg}\n`);
|
|
218
|
+
reject(new Error(msg));
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
118
224
|
/**
|
|
119
225
|
* Get install command for current platform.
|
|
120
226
|
*/
|
|
@@ -223,30 +329,43 @@ class Installer {
|
|
|
223
329
|
} catch {}
|
|
224
330
|
}
|
|
225
331
|
|
|
226
|
-
// -- Shell exec --
|
|
332
|
+
// -- Shell env + exec --
|
|
333
|
+
|
|
334
|
+
_buildShellEnv() {
|
|
335
|
+
const env = { ...process.env };
|
|
336
|
+
const sep = process.platform === 'win32' ? ';' : ':';
|
|
337
|
+
const extraDirs = [];
|
|
338
|
+
try { extraDirs.push(path.dirname(process.execPath)); } catch {}
|
|
339
|
+
if (process.platform === 'win32') {
|
|
340
|
+
const appData = env.APPDATA || '';
|
|
341
|
+
if (appData) extraDirs.push(path.join(appData, 'npm'));
|
|
342
|
+
extraDirs.push(env.ProgramFiles ? path.join(env.ProgramFiles, 'nodejs') : 'C:\\Program Files\\nodejs');
|
|
343
|
+
extraDirs.push(env.SystemRoot ? path.join(env.SystemRoot, 'System32') : 'C:\\Windows\\System32');
|
|
344
|
+
extraDirs.push(env.ProgramFiles ? path.join(env.ProgramFiles, 'Git', 'cmd') : 'C:\\Program Files\\Git\\cmd');
|
|
345
|
+
} else {
|
|
346
|
+
extraDirs.push('/usr/local/bin', '/opt/homebrew/bin');
|
|
347
|
+
}
|
|
348
|
+
for (const d of extraDirs) {
|
|
349
|
+
if (d && !(env.PATH || '').includes(d)) {
|
|
350
|
+
env.PATH = d + sep + (env.PATH || '');
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return env;
|
|
354
|
+
}
|
|
227
355
|
|
|
228
356
|
_execShell(cmd, timeoutMs = 300000) {
|
|
229
357
|
return new Promise((resolve, reject) => {
|
|
230
|
-
const env =
|
|
231
|
-
// Also include Electron's own binary dir
|
|
232
|
-
const execDir = path.dirname(process.execPath);
|
|
233
|
-
if (execDir && !(env.PATH || '').includes(execDir)) {
|
|
234
|
-
const sep = process.platform === 'win32' ? ';' : ':';
|
|
235
|
-
env.PATH = execDir + sep + (env.PATH || '');
|
|
236
|
-
}
|
|
358
|
+
const env = this._buildShellEnv();
|
|
237
359
|
|
|
238
|
-
|
|
239
|
-
let shellCmd = cmd;
|
|
240
|
-
let shellOpt = true;
|
|
360
|
+
let shell = true;
|
|
241
361
|
if (process.platform === 'win32') {
|
|
242
|
-
|
|
243
|
-
shellOpt = false; // we're specifying the shell ourselves
|
|
362
|
+
shell = env.ComSpec || 'C:\\Windows\\System32\\cmd.exe';
|
|
244
363
|
}
|
|
245
364
|
|
|
246
|
-
exec(
|
|
365
|
+
exec(cmd, {
|
|
247
366
|
encoding: 'utf-8',
|
|
248
367
|
timeout: timeoutMs,
|
|
249
|
-
shell
|
|
368
|
+
shell,
|
|
250
369
|
env,
|
|
251
370
|
}, (error, stdout, stderr) => {
|
|
252
371
|
const output = ((stdout || '') + '\n' + (stderr || '')).trim();
|
package/src/paths.js
CHANGED
|
@@ -36,11 +36,19 @@ function getExtraBinDirs() {
|
|
|
36
36
|
// Common: ~/.local/bin (pipx, user installs)
|
|
37
37
|
_push(dirs, path.join(HOME, '.local', 'bin'));
|
|
38
38
|
|
|
39
|
+
// Also add the directory containing the current node binary
|
|
40
|
+
try {
|
|
41
|
+
const nodeDir = path.dirname(process.execPath);
|
|
42
|
+
if (nodeDir) _push(dirs, nodeDir);
|
|
43
|
+
} catch {}
|
|
44
|
+
|
|
39
45
|
// Filter to existing directories only, deduplicate
|
|
40
46
|
const seen = new Set();
|
|
41
47
|
const currentPATH = process.env.PATH || '';
|
|
42
48
|
return dirs.filter(d => {
|
|
43
|
-
if (!d || seen.has(d)
|
|
49
|
+
if (!d || seen.has(d)) return false;
|
|
50
|
+
// Skip if already in PATH (case-insensitive on Windows)
|
|
51
|
+
if (IS_WINDOWS ? currentPATH.toLowerCase().includes(d.toLowerCase()) : currentPATH.includes(d)) return false;
|
|
44
52
|
seen.add(d);
|
|
45
53
|
try {
|
|
46
54
|
return fs.statSync(d).isDirectory();
|
|
@@ -71,10 +79,14 @@ function getEnhancedEnv(baseEnv) {
|
|
|
71
79
|
}
|
|
72
80
|
if (IS_WINDOWS) {
|
|
73
81
|
// Force UTF-8 output from child processes on non-English Windows locales
|
|
74
|
-
// (prevents GBK/Shift-JIS garbled text in error messages)
|
|
75
82
|
env.PYTHONIOENCODING = env.PYTHONIOENCODING || 'utf-8';
|
|
76
83
|
env.PYTHONUTF8 = env.PYTHONUTF8 || '1';
|
|
77
84
|
env.LANG = env.LANG || 'en_US.UTF-8';
|
|
85
|
+
// Ensure ComSpec points to cmd.exe (Electron may not set it)
|
|
86
|
+
if (!env.ComSpec) {
|
|
87
|
+
const sysRoot = env.SystemRoot || 'C:\\Windows';
|
|
88
|
+
env.ComSpec = path.join(sysRoot, 'System32', 'cmd.exe');
|
|
89
|
+
}
|
|
78
90
|
}
|
|
79
91
|
return env;
|
|
80
92
|
}
|
|
@@ -104,6 +116,10 @@ function _addWindowsPaths(dirs) {
|
|
|
104
116
|
const appData = process.env.APPDATA || '';
|
|
105
117
|
const localAppData = process.env.LOCALAPPDATA || '';
|
|
106
118
|
const programFiles = process.env.ProgramFiles || 'C:\\Program Files';
|
|
119
|
+
const sysRoot = process.env.SystemRoot || 'C:\\Windows';
|
|
120
|
+
|
|
121
|
+
// System32 (cmd.exe, powershell, etc) — Electron may not have it
|
|
122
|
+
_push(dirs, path.join(sysRoot, 'System32'));
|
|
107
123
|
|
|
108
124
|
// npm global bin
|
|
109
125
|
if (appData) _push(dirs, path.join(appData, 'npm'));
|