@openagents-org/agent-launcher 0.2.180 → 0.2.182
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/icons/antigravity.svg +1 -0
- package/icons/mini-swe-agent.svg +7 -0
- package/package.json +3 -2
- package/registry.json +1137 -622
- package/src/adapters/aider.js +10 -3
- package/src/adapters/amp.js +11 -3
- package/src/adapters/antigravity.js +10 -2
- package/src/adapters/base.js +46 -0
- package/src/adapters/claude.js +6 -1
- package/src/adapters/cline.js +5 -1
- package/src/adapters/codebuddy.js +6 -1
- package/src/adapters/codex.js +10 -2
- package/src/adapters/commandcode.js +5 -1
- package/src/adapters/copilot-stream-parser.js +58 -8
- package/src/adapters/copilot.js +20 -4
- package/src/adapters/cursor.js +5 -1
- package/src/adapters/deepseek.js +5 -1
- package/src/adapters/gemini.js +10 -2
- package/src/adapters/goose.js +6 -2
- package/src/adapters/hermes.js +23 -53
- package/src/adapters/kimi.js +5 -1
- package/src/adapters/llm-direct.js +6 -0
- package/src/adapters/mini.js +10 -3
- package/src/adapters/openclaw.js +5 -1
- package/src/adapters/opencode.js +6 -1
- package/src/adapters/openworker.js +15 -1
- package/src/adapters/pi.js +6 -1
- package/src/adapters/workspace-prompt.js +23 -12
- package/src/daemon.js +60 -1
- package/src/index.js +120 -0
- package/src/installer.js +57 -16
- package/src/paths.js +98 -34
- package/src/probe.js +4 -1
- package/src/workspace-client.js +38 -0
- package/src/wsl.js +579 -0
package/src/paths.js
CHANGED
|
@@ -12,6 +12,12 @@ const path = require('path');
|
|
|
12
12
|
const fs = require('fs');
|
|
13
13
|
const os = require('os');
|
|
14
14
|
const { execSync, execFileSync } = require('child_process');
|
|
15
|
+
const {
|
|
16
|
+
isRunningInWsl,
|
|
17
|
+
resolveWslBinary,
|
|
18
|
+
clearWslBinaryCache,
|
|
19
|
+
wslBinDirs,
|
|
20
|
+
} = require('./wsl');
|
|
15
21
|
|
|
16
22
|
const IS_WINDOWS = process.platform === 'win32';
|
|
17
23
|
const IS_MACOS = process.platform === 'darwin';
|
|
@@ -183,24 +189,84 @@ function getEnhancedEnv(baseEnv) {
|
|
|
183
189
|
|
|
184
190
|
/**
|
|
185
191
|
* Find a binary by name. Returns full path or null.
|
|
192
|
+
*
|
|
193
|
+
* On Windows the search ends INSIDE WSL when nothing native turned up: an agent
|
|
194
|
+
* CLI the user installed in their distro is a real, usable install, and the
|
|
195
|
+
* Linux path that comes back is what adapters hand to the wsl.js spawn bridge.
|
|
196
|
+
* It is deliberately the last thing tried — a native copy always wins — and
|
|
197
|
+
* `allowWsl: false` opts out for callers (installer.js) that have further
|
|
198
|
+
* native tiers of their own to run first.
|
|
199
|
+
*
|
|
200
|
+
* @param {string} name
|
|
201
|
+
* @param {{allowWsl?: boolean}} [opts]
|
|
186
202
|
*/
|
|
187
|
-
function whichBinary(name) {
|
|
203
|
+
function whichBinary(name, { allowWsl = true } = {}) {
|
|
188
204
|
if (!name) return null;
|
|
189
205
|
const currentPATH = process.env.PATH || '';
|
|
190
|
-
const cacheKey = `${name}\0${currentPATH}`;
|
|
206
|
+
const cacheKey = `${name}\0${currentPATH}\0${allowWsl ? 'wsl' : 'native'}`;
|
|
191
207
|
const cached = whichBinaryCache.get(cacheKey);
|
|
192
208
|
if (cached && Date.now() - cached.at < PATH_LOOKUP_CACHE_TTL_MS) {
|
|
193
209
|
return cached.value;
|
|
194
210
|
}
|
|
195
211
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
212
|
+
let value = _whichNative(name);
|
|
213
|
+
if (!value && allowWsl) value = resolveWslBinary(name);
|
|
214
|
+
whichBinaryCache.set(cacheKey, { value, at: Date.now() });
|
|
215
|
+
return value;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** The PATH lookup proper — `where`/`which`, this side of any boundary. */
|
|
219
|
+
function _whichNative(name) {
|
|
220
|
+
let hits = _runWhich(name);
|
|
221
|
+
|
|
222
|
+
if (isRunningInWsl()) {
|
|
223
|
+
// Interop puts the Windows PATH on the distro's PATH, so `which claude`
|
|
224
|
+
// happily answers /mnt/c/Users/…/AppData/Roaming/npm/claude — npm's
|
|
225
|
+
// extensionless POSIX shim, a sh script that execs node.exe with Windows
|
|
226
|
+
// paths. Linux cannot run it, and returning it turns "not installed" into
|
|
227
|
+
// the more confusing "installed but every run fails". Only a real PE image
|
|
228
|
+
// crosses back this way, which interop does exec with argv intact.
|
|
229
|
+
hits = hits.filter((h) => !_isWindowsMount(h) || /\.exe$/i.test(h));
|
|
230
|
+
// `which cursor-agent` can't see cursor-agent.exe — the name on disk
|
|
231
|
+
// carries the suffix — so ask for it explicitly before giving up.
|
|
232
|
+
if (!hits.length && !/\.exe$/i.test(name)) hits = _runWhich(`${name}.exe`);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
let value = hits[0] || null;
|
|
236
|
+
if (IS_WINDOWS && hits.length) {
|
|
237
|
+
// `where` can list npm's extensionless POSIX shim (node_modules/.bin/
|
|
238
|
+
// claude, a sh script) ahead of claude.cmd. Windows cannot execute that
|
|
239
|
+
// file — spawning it fails and the probe misreports an installed CLI as
|
|
240
|
+
// "not installed" while the chat adapters (which prefer .cmd) work fine.
|
|
241
|
+
// Prefer a hit Windows can run; failing that, a runnable sibling of the
|
|
242
|
+
// shim; only then fall back to the raw first hit.
|
|
243
|
+
const RUNNABLE = /\.(cmd|exe|bat|com)$/i;
|
|
244
|
+
const runnable = hits.find((h) => RUNNABLE.test(h));
|
|
245
|
+
if (runnable) {
|
|
246
|
+
value = runnable;
|
|
247
|
+
} else {
|
|
248
|
+
for (const h of hits) {
|
|
249
|
+
const sibling = ['.cmd', '.exe', '.bat'].map((e) => h + e).find((c) => fs.existsSync(c));
|
|
250
|
+
if (sibling) { value = sibling; break; }
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return value;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Existing paths `where`/`which` reports for a name, in its own order.
|
|
259
|
+
*
|
|
260
|
+
* On a non-English Windows the console OUTPUT codepage is OEM (e.g. 936/GBK on
|
|
261
|
+
* zh-CN), so `where` prints a path whose non-ASCII bytes don't match the utf-8
|
|
262
|
+
* decoding execSync does — a Chinese username comes back mangled (e.g.
|
|
263
|
+
* `C:\Users\??.?[\…`) and yields ENOENT downstream. We can't reliably re-encode
|
|
264
|
+
* it (and forcing `chcp` is unsafe under windowsHide's console-less cmd), so we
|
|
265
|
+
* existence-check every hit and only return ones that are real; a mangled path
|
|
266
|
+
* fails and the caller falls through to its Node-derived tiers (built from
|
|
267
|
+
* process.env / os.homedir, which the OS hands us as correct UTF-16).
|
|
268
|
+
*/
|
|
269
|
+
function _runWhich(name) {
|
|
204
270
|
const cmd = IS_WINDOWS ? `where ${name}` : `which ${name}`;
|
|
205
271
|
try {
|
|
206
272
|
const result = execSync(cmd, {
|
|
@@ -221,33 +287,21 @@ function whichBinary(name) {
|
|
|
221
287
|
const hit = line.trim();
|
|
222
288
|
if (hit && fs.existsSync(hit)) hits.push(hit);
|
|
223
289
|
}
|
|
224
|
-
|
|
225
|
-
if (IS_WINDOWS && hits.length) {
|
|
226
|
-
// `where` can list npm's extensionless POSIX shim (node_modules/.bin/
|
|
227
|
-
// claude, a sh script) ahead of claude.cmd. Windows cannot execute that
|
|
228
|
-
// file — spawning it fails and the probe misreports an installed CLI as
|
|
229
|
-
// "not installed" while the chat adapters (which prefer .cmd) work fine.
|
|
230
|
-
// Prefer a hit Windows can run; failing that, a runnable sibling of the
|
|
231
|
-
// shim; only then fall back to the raw first hit.
|
|
232
|
-
const RUNNABLE = /\.(cmd|exe|bat|com)$/i;
|
|
233
|
-
const runnable = hits.find((h) => RUNNABLE.test(h));
|
|
234
|
-
if (runnable) {
|
|
235
|
-
value = runnable;
|
|
236
|
-
} else {
|
|
237
|
-
for (const h of hits) {
|
|
238
|
-
const sibling = ['.cmd', '.exe', '.bat'].map((e) => h + e).find((c) => fs.existsSync(c));
|
|
239
|
-
if (sibling) { value = sibling; break; }
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
whichBinaryCache.set(cacheKey, { value, at: Date.now() });
|
|
244
|
-
return value;
|
|
290
|
+
return hits;
|
|
245
291
|
} catch {
|
|
246
|
-
|
|
247
|
-
return null;
|
|
292
|
+
return [];
|
|
248
293
|
}
|
|
249
294
|
}
|
|
250
295
|
|
|
296
|
+
/**
|
|
297
|
+
* A path on a Windows drive as mounted inside a distro (/mnt/c/…). /mnt is
|
|
298
|
+
* WSL's default automount root and the only one interop puts on PATH itself;
|
|
299
|
+
* a distro that moved it in /etc/wsl.conf simply gets the old behaviour.
|
|
300
|
+
*/
|
|
301
|
+
function _isWindowsMount(p) {
|
|
302
|
+
return /^\/mnt\/[a-z]\//i.test(String(p || ''));
|
|
303
|
+
}
|
|
304
|
+
|
|
251
305
|
/**
|
|
252
306
|
* Codepage-safe `where`/`which` lookup, shared by every CLI adapter.
|
|
253
307
|
*
|
|
@@ -916,6 +970,7 @@ function clearBinaryLookupCache() {
|
|
|
916
970
|
knownBinDirsCache = { value: null, at: 0 };
|
|
917
971
|
extraBinDirsCache = { value: null, at: 0, path: '' };
|
|
918
972
|
whichBinaryCache.clear();
|
|
973
|
+
clearWslBinaryCache();
|
|
919
974
|
// The login-shell / npm-prefix probes are deliberately NOT cleared: they each
|
|
920
975
|
// cost a process spawn, they answer a question about the machine rather than
|
|
921
976
|
// about any install, and this is called after every install/uninstall.
|
|
@@ -933,6 +988,11 @@ function primeBinaryLookup() {
|
|
|
933
988
|
try { loginShellDirs(); } catch {}
|
|
934
989
|
try { _npmPrefix(); } catch {}
|
|
935
990
|
try { getKnownBinDirs(); } catch {}
|
|
991
|
+
// The WSL side has the same shape of cost — one `wsl.exe -e bash -ilc` to
|
|
992
|
+
// learn the distro's login PATH — and it is paid by the first lookup that
|
|
993
|
+
// misses natively, which is the marketplace enumerating agents nobody has
|
|
994
|
+
// installed. Warm it here instead.
|
|
995
|
+
try { wslBinDirs(); } catch {}
|
|
936
996
|
}
|
|
937
997
|
|
|
938
998
|
/**
|
|
@@ -1035,7 +1095,11 @@ function resolveBinaryInKnownDirs(names, agentType) {
|
|
|
1035
1095
|
}
|
|
1036
1096
|
}
|
|
1037
1097
|
}
|
|
1038
|
-
|
|
1098
|
+
|
|
1099
|
+
// Nothing native anywhere: the last place left to look is inside WSL, whose
|
|
1100
|
+
// filesystem none of the dirs above can reach. Last by construction — a
|
|
1101
|
+
// Windows copy of the CLI is always preferred to an in-distro one.
|
|
1102
|
+
return resolveWslBinary(list);
|
|
1039
1103
|
}
|
|
1040
1104
|
|
|
1041
1105
|
|
package/src/probe.js
CHANGED
|
@@ -26,7 +26,10 @@
|
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
const os = require('os');
|
|
29
|
-
|
|
29
|
+
// spawn() here is the WSL bridge from ./wsl: same signature as
|
|
30
|
+
// child_process.spawn, and a straight pass-through unless the resolved CLI
|
|
31
|
+
// lives on the other side of the Windows/WSL boundary.
|
|
32
|
+
const { spawn } = require('./wsl');
|
|
30
33
|
const { getEnhancedEnv } = require('./paths');
|
|
31
34
|
const { shouldUseShellForBinary } = require('./adapters/health-status');
|
|
32
35
|
const { formatAuthGuidance } = require('./auth-guidance');
|
package/src/workspace-client.js
CHANGED
|
@@ -84,6 +84,40 @@ class WorkspaceClient {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Remove one agent from a workspace via
|
|
89
|
+
* DELETE /v1/workspaces/{workspaceId}/members/{agentName}.
|
|
90
|
+
*
|
|
91
|
+
* Unlike deleteWorkspace above this is NOT best-effort: the caller offered
|
|
92
|
+
* the user a choice ("also remove it from the workspace"), so a failure has
|
|
93
|
+
* to be reported rather than swallowed — silently leaving the member behind
|
|
94
|
+
* is the exact problem this exists to fix.
|
|
95
|
+
*/
|
|
96
|
+
async removeMember(workspaceId, token, agentName) {
|
|
97
|
+
await this._delete(
|
|
98
|
+
`/v1/workspaces/${workspaceId}/members/${encodeURIComponent(agentName)}`,
|
|
99
|
+
this._wsHeaders(token),
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Rename one agent's DISPLAY name in a workspace, via
|
|
105
|
+
* PATCH /v1/workspaces/{workspaceId}/members/{agentName}.
|
|
106
|
+
*
|
|
107
|
+
* The agent's identity (`agent_name`) is untouched — this is the label the
|
|
108
|
+
* workspace shows and accepts as an @-mention alias, which is why the server
|
|
109
|
+
* rejects one that clashes with another member. An empty string clears the
|
|
110
|
+
* label there, falling back to `agent_name`, which is exactly what clearing
|
|
111
|
+
* the display name on this side means too.
|
|
112
|
+
*/
|
|
113
|
+
async setMemberDisplayName(workspaceId, token, agentName, displayName) {
|
|
114
|
+
return this._patch(
|
|
115
|
+
`/v1/workspaces/${workspaceId}/members/${encodeURIComponent(agentName)}`,
|
|
116
|
+
{ display_name: displayName || '' },
|
|
117
|
+
this._wsHeaders(token),
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
87
121
|
/**
|
|
88
122
|
* Join a workspace via POST /v1/join.
|
|
89
123
|
*/
|
|
@@ -514,6 +548,10 @@ class WorkspaceClient {
|
|
|
514
548
|
const agents = (result && result.agents) || [];
|
|
515
549
|
return agents.map((a) => ({
|
|
516
550
|
agentName: (a.address || '').replace('openagents:', ''),
|
|
551
|
+
// The workspace's label for this agent, or null when it has none and
|
|
552
|
+
// falls back to agentName. Carried so a rename made in the workspace can
|
|
553
|
+
// be reflected back on the device.
|
|
554
|
+
displayName: a.display_name || null,
|
|
517
555
|
role: a.role || 'member',
|
|
518
556
|
status: a.status || 'offline',
|
|
519
557
|
enabledSkills: a.enabled_skills || null,
|