open-agents-ai 0.187.459 → 0.187.461

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/index.js CHANGED
@@ -542151,7 +542151,10 @@ async function readExpandedVariantState(backendUrl, modelName) {
542151
542151
  const showData = await showRes.json();
542152
542152
  const currentNumCtx = parseShowNumCtx2(showData);
542153
542153
  const fromMatch = showData.modelfile?.match(/^FROM\s+(.+)$/m);
542154
- const baseModel = fromMatch?.[1]?.trim() ?? null;
542154
+ let baseModel = fromMatch?.[1]?.trim() ?? null;
542155
+ if (baseModel && (baseModel.startsWith("/") || /blobs\/sha256[-:]/.test(baseModel))) {
542156
+ baseModel = null;
542157
+ }
542155
542158
  return { currentNumCtx, baseModel };
542156
542159
  } catch {
542157
542160
  return null;
@@ -542161,6 +542164,11 @@ function stripVariantTag(modelName) {
542161
542164
  return modelName.replace(/:latest$/i, "");
542162
542165
  }
542163
542166
  function createExpandedVariantContent(baseModel, numCtx) {
542167
+ if (baseModel.startsWith("/") || /blobs\/sha256[-:]/.test(baseModel)) {
542168
+ throw new Error(
542169
+ `createExpandedVariantContent: refusing to use blob-path base "${baseModel}". Pass the user-facing model name (e.g. "qwen3.6:latest") instead.`
542170
+ );
542171
+ }
542164
542172
  const numPredict = Math.min(16384, Math.max(2048, Math.floor(numCtx * 0.25)));
542165
542173
  return [
542166
542174
  `FROM ${baseModel}`,
@@ -542284,6 +542292,23 @@ async function ensureExpandedContext(modelName, backendUrl) {
542284
542292
  return { model: modelName, created: false, contextLabel: "", numCtx: 0 };
542285
542293
  }
542286
542294
  if (typeof existing === "string") {
542295
+ const lostTools = await wrapperLacksToolsCapability(backendUrl, existing).catch(() => false);
542296
+ if (lostTools) {
542297
+ try {
542298
+ const rebuilt = await createExpandedVariantNamedAsync(
542299
+ stripVariantTag(existing),
542300
+ modelName,
542301
+ specs,
542302
+ sizeGB,
542303
+ kvInfo?.kvBytesPerToken,
542304
+ kvInfo?.archMax
542305
+ );
542306
+ if (rebuilt) {
542307
+ return { model: rebuilt, created: true, contextLabel: ctx3.label, numCtx: ctx3.numCtx };
542308
+ }
542309
+ } catch {
542310
+ }
542311
+ }
542287
542312
  const repair = await repairExpandedVariantIfStale(
542288
542313
  existing,
542289
542314
  modelName,
@@ -542302,6 +542327,38 @@ async function ensureExpandedContext(modelName, backendUrl) {
542302
542327
  }
542303
542328
  return { model: modelName, created: false, contextLabel: ctx3.label, numCtx: ctx3.numCtx };
542304
542329
  }
542330
+ function guessBaseFromVariant(variantName, models) {
542331
+ const stripped = stripVariantTag(variantName);
542332
+ for (const m2 of models) {
542333
+ if (/^open-agents-/i.test(m2.name)) continue;
542334
+ const candidates = expandedModelCandidates(stripVariantTag(m2.name));
542335
+ if (candidates.includes(stripped)) {
542336
+ return m2.name;
542337
+ }
542338
+ }
542339
+ return null;
542340
+ }
542341
+ async function wrapperLacksToolsCapability(backendUrl, modelName) {
542342
+ try {
542343
+ const normalized = backendUrl.replace(/\/+$/, "");
542344
+ const showRes = await fetch(`${normalized}/api/show`, {
542345
+ method: "POST",
542346
+ headers: { "Content-Type": "application/json" },
542347
+ body: JSON.stringify({ name: modelName }),
542348
+ signal: AbortSignal.timeout(5e3)
542349
+ });
542350
+ if (!showRes.ok) return false;
542351
+ const showData = await showRes.json();
542352
+ const caps = Array.isArray(showData.capabilities) ? showData.capabilities : [];
542353
+ const hasTools = caps.some((c9) => /^tools?$/i.test(c9));
542354
+ if (hasTools) return false;
542355
+ const fromMatch = showData.modelfile?.match(/^FROM\s+(.+)$/m);
542356
+ const fromVal = fromMatch?.[1]?.trim() ?? "";
542357
+ return fromVal.startsWith("/") || /blobs\/sha256[-:]/.test(fromVal);
542358
+ } catch {
542359
+ return false;
542360
+ }
542361
+ }
542305
542362
  async function repairAllExpandedVariants(backendUrl) {
542306
542363
  const specs = await detectSystemSpecsAsync();
542307
542364
  const models = await fetchOllamaModels(backendUrl);
@@ -542319,25 +542376,47 @@ async function repairAllExpandedVariants(backendUrl) {
542319
542376
  summary.failed.push({ model: variant.name, reason: "missing-state" });
542320
542377
  continue;
542321
542378
  }
542322
- const baseModel = state.baseModel && !state.baseModel.startsWith("open-agents-") ? state.baseModel : null;
542379
+ let baseModel = state.baseModel && !state.baseModel.startsWith("open-agents-") ? state.baseModel : null;
542380
+ if (!baseModel) {
542381
+ baseModel = guessBaseFromVariant(variant.name, models);
542382
+ }
542323
542383
  if (!baseModel) {
542324
542384
  summary.failed.push({ model: variant.name, reason: "missing-base-model" });
542325
542385
  continue;
542326
542386
  }
542327
- const sizeGB = modelSizeGB(models, baseModel || variant.name);
542387
+ const sizeGB = modelSizeGB(models, baseModel);
542328
542388
  const kvInfo = await queryModelKVInfo(backendUrl, baseModel);
542329
542389
  const target = calculateExpandedVariantContextWindow(specs, sizeGB, kvInfo?.kvBytesPerToken, kvInfo?.archMax);
542390
+ const lostTools = await wrapperLacksToolsCapability(backendUrl, variant.name);
542330
542391
  try {
542331
- const result = await repairExpandedVariantIfStale(
542332
- variant.name,
542333
- baseModel,
542334
- backendUrl,
542335
- specs,
542336
- sizeGB,
542337
- target.numCtx,
542338
- kvInfo?.kvBytesPerToken,
542339
- kvInfo?.archMax
542340
- );
542392
+ let result;
542393
+ if (lostTools) {
542394
+ const created = await createExpandedVariantNamedAsync(
542395
+ stripVariantTag(variant.name),
542396
+ baseModel,
542397
+ specs,
542398
+ sizeGB,
542399
+ kvInfo?.kvBytesPerToken,
542400
+ kvInfo?.archMax
542401
+ );
542402
+ result = {
542403
+ repaired: !!created,
542404
+ currentNumCtx: state.currentNumCtx,
542405
+ baseModel,
542406
+ resolvedModel: created ?? variant.name
542407
+ };
542408
+ } else {
542409
+ result = await repairExpandedVariantIfStale(
542410
+ variant.name,
542411
+ baseModel,
542412
+ backendUrl,
542413
+ specs,
542414
+ sizeGB,
542415
+ target.numCtx,
542416
+ kvInfo?.kvBytesPerToken,
542417
+ kvInfo?.archMax
542418
+ );
542419
+ }
542341
542420
  if (result.repaired) {
542342
542421
  summary.repaired.push({
542343
542422
  model: result.resolvedModel,
package/dist/launcher.cjs CHANGED
@@ -1,431 +1,78 @@
1
1
  #!/usr/bin/env node
2
- // Node version gate uses only ES5 syntax so it parses on any Node version.
3
- // On old Node: walks through curl/wget install, nvm install, then Node 22, then reinstall.
4
- var nodeVersion = parseInt(process.versions.node, 10);
5
-
6
- if (nodeVersion < 22) {
7
- // Prevent infinite loop: if we're inside a self-triggered reinstall, bail
8
- if (process.env.OA_SELF_UPGRADE === "1") {
9
- console.error(" [launcher] Skipping — already inside a self-upgrade.");
10
- console.error(" Open a new terminal with Node 22 and run: oa");
11
- process.exit(1);
12
- }
13
-
14
- var os = require("os");
15
- var path = require("path");
16
- var fs = require("fs");
17
- var childProcess = require("child_process");
18
- var readline = require("readline");
19
-
20
- var platform = os.platform();
21
- var arch = os.arch();
22
-
23
- console.log("");
24
- console.log(" ┌─────────────────────────────────────────────────┐");
25
- console.log(" │ open-agents — Node.js Upgrade │");
26
- console.log(" └─────────────────────────────────────────────────┘");
27
- console.log("");
28
- console.log(" Your Node.js: " + process.version);
29
- console.log(" Required: >= 22.0.0");
30
- console.log(" Platform: " + platform + "/" + arch);
31
- console.log("");
32
-
33
- // Detect if nvm is already installed
34
- var nvmDir = process.env.NVM_DIR || path.join(os.homedir(), ".nvm");
35
- var hasNvm = false;
36
- try { fs.statSync(path.join(nvmDir, "nvm.sh")); hasNvm = true; } catch (e) {}
37
-
38
- var rl = readline.createInterface({ input: process.stdin, output: process.stdout });
39
-
40
- function ask(question, cb) {
41
- rl.question(question, function(answer) {
42
- cb(answer.trim().toLowerCase());
43
- });
44
- }
45
-
46
- function hasCmd(cmd) {
47
- try {
48
- childProcess.execSync("which " + cmd, { stdio: "pipe", timeout: 3000 });
49
- return true;
50
- } catch (e) {
51
- return false;
52
- }
53
- }
54
-
55
- function run(cmd) {
56
- console.log(" $ " + cmd);
57
- try {
58
- childProcess.execSync(cmd, { stdio: "inherit" });
59
- return true;
60
- } catch (e) {
61
- console.error(" Command failed: " + (e.message || e));
62
- return false;
63
- }
64
- }
65
-
66
- function bail(msg) {
67
- console.log(msg);
68
- rl.close();
69
- process.exit(1);
70
- }
71
-
72
- // Returns "curl -o-" or "wget -qO-" or null
73
- function getDownloader() {
74
- if (hasCmd("curl")) return "curl -o-";
75
- if (hasCmd("wget")) return "wget -qO-";
76
- return null;
77
- }
78
-
79
- // Try to install curl if neither curl nor wget is available
80
- function ensureDownloader(next) {
81
- var dl = getDownloader();
82
- if (dl) { next(dl); return; }
83
-
84
- console.log(" Neither curl nor wget found.");
85
- console.log("");
86
-
87
- ask(" Install curl now? [Y/n] ", function(a) {
88
- if (a === "n" || a === "no") {
89
- bail(
90
- "\n Install manually:\n" +
91
- " sudo apt install curl (Debian/Ubuntu)\n" +
92
- " sudo dnf install curl (Fedora)\n" +
93
- " sudo pacman -S curl (Arch)\n"
94
- );
95
- return;
96
- }
97
- console.log("");
98
- var ok = false;
99
- if (hasCmd("apt-get")) {
100
- ok = run("sudo -n apt-get install -y curl 2>/dev/null || sudo apt-get install -y curl");
101
- } else if (hasCmd("dnf")) {
102
- ok = run("sudo -n dnf install -y curl 2>/dev/null || sudo dnf install -y curl");
103
- } else if (hasCmd("pacman")) {
104
- ok = run("sudo -n pacman -S --noconfirm curl 2>/dev/null || sudo pacman -S --noconfirm curl");
105
- } else if (hasCmd("apk")) {
106
- ok = run("apk add curl");
107
- } else {
108
- console.log(" No supported package manager found (tried apt, dnf, pacman, apk).");
109
- }
110
-
111
- if (ok && hasCmd("curl")) {
112
- console.log("");
113
- next("curl -o-");
114
- } else {
115
- bail(
116
- "\n Could not install curl. Install it manually, then re-run:\n" +
117
- " npm i -g open-agents-ai\n"
118
- );
119
- }
120
- });
121
- }
122
-
123
- function doInstallNvm(dl, next) {
124
- if (hasNvm) {
125
- console.log(" nvm found at " + nvmDir);
126
- console.log("");
127
- next();
128
- return;
129
- }
130
- ask(" nvm is not installed. Install nvm now? [Y/n] ", function(a) {
131
- if (a === "n" || a === "no") {
132
- bail(
133
- "\n Install nvm manually:\n" +
134
- " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
135
- " # or: wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
136
- " source ~/.bashrc\n" +
137
- " nvm install 22\n" +
138
- " npm i -g open-agents-ai\n"
139
- );
140
- return;
141
- }
142
- console.log("");
143
- var ok = run(dl + " https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash");
144
- if (!ok) {
145
- bail("\n nvm install failed. See https://github.com/nvm-sh/nvm#installing-and-updating\n");
146
- return;
147
- }
148
- nvmDir = process.env.NVM_DIR || path.join(os.homedir(), ".nvm");
149
- hasNvm = true;
150
- console.log("");
151
- next();
152
- });
153
- }
154
-
155
- function doInstallNode(next) {
156
- ask(" Install Node.js 22 via nvm? [Y/n] ", function(a) {
157
- if (a === "n" || a === "no") {
158
- bail(
159
- "\n Install manually:\n" +
160
- " nvm install 22 && nvm alias default 22\n" +
161
- " npm i -g open-agents-ai\n"
162
- );
163
- return;
164
- }
165
- console.log("");
166
- var shell = process.env.SHELL || "/bin/bash";
167
- var cmd = 'export NVM_DIR="' + nvmDir + '" && ' +
168
- '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" && ' +
169
- "nvm install 22 && nvm use 22 && nvm alias default 22";
170
- var ok = run(shell + " -c '" + cmd + "'");
171
- if (!ok) {
172
- bail("\n Node 22 install failed.\n");
173
- return;
174
- }
175
- console.log("");
176
- next();
177
- });
178
- }
179
-
180
- // Find the Node 22 binary installed by nvm
181
- function findNode22Bin() {
182
- // Check nvm versions directory for a v22.x binary
183
- var versionsDir = path.join(nvmDir, "versions", "node");
184
- try {
185
- var versions = fs.readdirSync(versionsDir).filter(function(v) {
186
- return v.startsWith("v22");
187
- }).sort().reverse();
188
- if (versions.length > 0) {
189
- var bin = path.join(versionsDir, versions[0], "bin", "node");
190
- try { fs.statSync(bin); return bin; } catch (e) {}
191
- }
192
- } catch (e) {}
193
- // Fallback: ask nvm which node 22 is
194
- try {
195
- var shell = process.env.SHELL || "/bin/bash";
196
- var result = childProcess.execSync(
197
- shell + ' -c \'export NVM_DIR="' + nvmDir + '" && [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" && nvm which 22\'',
198
- { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 10000 }
199
- ).trim();
200
- if (result && fs.existsSync(result)) return result;
201
- } catch (e) {}
202
- return null;
2
+ // Robust launcher for open-agents CLI.
3
+ // - Runs the ESM entry as a child process
4
+ // - On exit code 120 (update), resets terminal and restarts child
5
+ // - Prevents raw-mode/mouse-tracking bleedthrough on restart or crash
6
+
7
+ const { spawn, spawnSync } = require('node:child_process');
8
+ const { resolve } = require('node:path');
9
+
10
+ function resetTerminal() {
11
+ try { if (process.stdin.isTTY && typeof process.stdin.setRawMode === 'function') process.stdin.setRawMode(false); } catch {}
12
+ // Disable mouse tracking, bracketed paste, show cursor, reset attrs, exit alt screen if active
13
+ const ESC = '\x1B';
14
+ try {
15
+ process.stdout.write(
16
+ ESC + '[?25h' + // show cursor
17
+ ESC + '[?1000l' + // X10 mouse off
18
+ ESC + '[?1002l' + // button-event mouse off
19
+ ESC + '[?1003l' + // any-event mouse off
20
+ ESC + '[?1006l' + // SGR mouse off
21
+ ESC + '[?1015l' + // urxvt mouse off
22
+ ESC + '[?2004l' + // bracketed paste off
23
+ ESC + '[?1049l' + // exit alt screen
24
+ ESC + '[0m' // reset attributes
25
+ );
26
+ } catch {}
27
+ // stty sane (POSIX)
28
+ if (process.platform !== 'win32' && process.stdin.isTTY) {
29
+ try { spawnSync('stty', ['sane'], { stdio: 'inherit' }); } catch {}
203
30
  }
31
+ }
204
32
 
205
- // Find the oa launcher script under a given node prefix
206
- function findOaBin(nodeBin) {
207
- // nodeBin = .../bin/node → prefix = .../
208
- var binDir = path.dirname(nodeBin);
209
- var candidates = [
210
- path.join(binDir, "oa"),
211
- path.join(binDir, "open-agents"),
212
- ];
213
- for (var i = 0; i < candidates.length; i++) {
214
- try { fs.statSync(candidates[i]); return candidates[i]; } catch (e) {}
215
- }
216
- return null;
217
- }
33
+ function runChild() {
34
+ const entry = resolve(__dirname, 'index.js');
35
+ const args = [entry, ...process.argv.slice(2)];
36
+ const child = spawn(process.execPath, args, {
37
+ stdio: 'inherit',
38
+ env: process.env,
39
+ });
40
+ return child;
41
+ }
218
42
 
219
- function doReinstall() {
220
- ask(" Reinstall open-agents under Node 22? [Y/n] ", function(a) {
221
- if (a === "n" || a === "no") {
222
- console.log(
223
- "\n Open a new terminal, then run:\n" +
224
- " npm i -g open-agents-ai\n"
225
- );
226
- rl.close();
227
- process.exit(0);
43
+ (async () => {
44
+ let restarts = 0;
45
+ const MAX_RESTARTS = 3;
46
+ let child = runChild();
47
+
48
+ const forward = (sig) => {
49
+ try { child && child.kill(sig); } catch {}
50
+ };
51
+ process.on('SIGINT', () => forward('SIGINT'));
52
+ process.on('SIGTERM', () => forward('SIGTERM'));
53
+
54
+ function attach(childProc) {
55
+ childProc.on('exit', (code, signal) => {
56
+ if (signal) {
57
+ resetTerminal();
58
+ process.kill(process.pid, signal);
228
59
  return;
229
60
  }
230
- console.log("");
231
- var shell = process.env.SHELL || "/bin/bash";
232
- var cmd = 'export OA_SELF_UPGRADE=1 && export NVM_DIR="' + nvmDir + '" && ' +
233
- '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" && ' +
234
- "nvm use 22 && npm i -g open-agents-ai";
235
- var ok = run(shell + " -c '" + cmd + "'");
236
- if (!ok) {
237
- console.log(
238
- "\n Reinstall failed. Open a new terminal, then run:\n" +
239
- " npm i -g open-agents-ai\n"
240
- );
241
- rl.close();
242
- process.exit(1);
61
+ if (code === 120 && restarts < MAX_RESTARTS) {
62
+ // Update-triggered restart
63
+ resetTerminal();
64
+ restarts += 1;
65
+ setTimeout(() => {
66
+ child = runChild();
67
+ attach(child);
68
+ }, 300);
243
69
  return;
244
70
  }
245
- console.log("");
246
- rl.close();
247
-
248
- // Try to auto-relaunch under Node 22 — no new terminal needed
249
- var node22 = findNode22Bin();
250
- if (node22) {
251
- var oaBin = findOaBin(node22);
252
- if (oaBin) {
253
- console.log(" ┌─────────────────────────────────────────────────┐");
254
- console.log(" │ Done! Launching open-agents with Node 22... │");
255
- console.log(" └─────────────────────────────────────────────────┘");
256
- console.log("");
257
- // Replace this process with Node 22 running the new oa launcher
258
- try {
259
- childProcess.execFileSync(node22, [oaBin], {
260
- stdio: "inherit",
261
- env: process.env,
262
- });
263
- } catch (e) {
264
- // execFileSync throws when the child exits — that's normal
265
- }
266
- process.exit(0);
267
- }
268
- }
269
-
270
- // Fallback if we can't find Node 22 binary or oa script
271
- console.log(" ┌─────────────────────────────────────────────────┐");
272
- console.log(" │ Done! Open a new terminal and run: oa │");
273
- console.log(" └─────────────────────────────────────────────────┘");
274
- console.log("");
275
- process.exit(0);
276
- });
277
- }
278
-
279
- // Walk through the steps
280
- ask(" Upgrade to Node.js 22 now? [Y/n] ", function(a) {
281
- if (a === "n" || a === "no") {
282
- bail(
283
- "\n To install manually:\n" +
284
- " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
285
- " # or: wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
286
- " source ~/.bashrc\n" +
287
- " nvm install 22\n" +
288
- " nvm alias default 22\n" +
289
- " npm i -g open-agents-ai\n"
290
- );
291
- return;
292
- }
293
- console.log("");
294
- // Ensure curl or wget is available first
295
- ensureDownloader(function(dl) {
296
- doInstallNvm(dl, function() {
297
- doInstallNode(function() {
298
- doReinstall();
299
- });
300
- });
301
- });
302
- });
303
- } else {
304
- // Node >= 22 — check sub-dependencies before loading the ESM bundle
305
- var _path = require("path");
306
- var _fs = require("fs");
307
- var _cp = require("child_process");
308
-
309
- // Resolve the package root (where package.json lives)
310
- var _pkgDir = _path.dirname(__dirname.endsWith("dist") ? _path.dirname(__dirname) : __dirname);
311
- // If we're in .../lib/node_modules/open-agents-ai/dist/, go up to the package root
312
- var _pkgJson = _path.join(_pkgDir, "package.json");
313
- if (!_fs.existsSync(_pkgJson)) {
314
- _pkgDir = _path.resolve(__dirname, "..");
315
- _pkgJson = _path.join(_pkgDir, "package.json");
316
- }
317
-
318
- // Dependencies to verify — both required and optional
319
- var _criticalDeps = ["better-sqlite3", "ws", "zod", "glob", "ignore"];
320
- var _optionalDeps = ["open-agents-nexus", "nats.ws", "moondream", "aiwg"];
321
- var _missing = [];
322
- var _missingOptional = [];
323
-
324
- function _canResolve(name) {
325
- try {
326
- // Check if the module exists in the package's node_modules
327
- var modDir = _path.join(_pkgDir, "node_modules", name);
328
- if (_fs.existsSync(modDir)) return true;
329
- // Also try require.resolve from the package dir
330
- require.resolve(name, { paths: [_pkgDir] });
331
- return true;
332
- } catch (e) {
333
- return false;
334
- }
335
- }
336
-
337
- for (var _i = 0; _i < _criticalDeps.length; _i++) {
338
- if (!_canResolve(_criticalDeps[_i])) _missing.push(_criticalDeps[_i]);
339
- }
340
- for (var _j = 0; _j < _optionalDeps.length; _j++) {
341
- if (!_canResolve(_optionalDeps[_j])) _missingOptional.push(_optionalDeps[_j]);
342
- }
343
-
344
- var _allMissing = _missing.concat(_missingOptional);
345
-
346
- if (_allMissing.length > 0) {
347
- var _label = _missing.length > 0 ? "required" : "optional";
348
- console.log(" Installing " + _allMissing.length + " missing " + _label + " sub-dependencies...");
349
- console.log(" Missing: " + _allMissing.join(", "));
350
- console.log("");
351
-
352
- // Run npm install in the package directory to restore all deps
353
- try {
354
- _cp.execSync("npm install --no-audit --no-fund", {
355
- cwd: _pkgDir,
356
- stdio: ["pipe", "pipe", "pipe"],
357
- timeout: 120000,
358
- env: Object.assign({}, process.env, { OA_SELF_UPGRADE: "1" }),
359
- });
360
- console.log(" Sub-dependencies installed.");
361
- console.log("");
362
- } catch (e) {
363
- // If full install fails, try installing each missing dep individually
364
- for (var _k = 0; _k < _allMissing.length; _k++) {
365
- var _dep = _allMissing[_k];
366
- try {
367
- var _isOpt = _missingOptional.indexOf(_dep) !== -1;
368
- console.log(" Installing " + _dep + (_isOpt ? " (optional)" : "") + "...");
369
- _cp.execSync("npm install " + _dep + " --no-audit --no-fund" + (_isOpt ? " --save-optional" : ""), {
370
- cwd: _pkgDir,
371
- stdio: ["pipe", "pipe", "pipe"],
372
- timeout: 60000,
373
- });
374
- } catch (e2) {
375
- if (_missing.indexOf(_dep) !== -1) {
376
- console.error(" WARNING: Failed to install required dep: " + _dep);
377
- }
378
- // Optional deps failing is fine
379
- }
380
- }
381
- }
382
- }
383
-
384
- // Restart-aware launcher loop.
385
- // Exit code 120 = restart requested (from /update).
386
- // The launcher spawns the ESM bundle as a child process. If it exits with
387
- // code 120, the launcher resets the terminal and spawns again. This way the
388
- // restart happens in a CLEAN new Node process with zero inherited state —
389
- // no mouse tracking, no raw mode, no buffered stdin, no timers.
390
- var cp = require("child_process");
391
- var path = require("path");
392
-
393
- function runOA() {
394
- var bundlePath = path.join(__dirname, "index.js");
395
- var child = cp.fork(bundlePath, process.argv.slice(2), {
396
- stdio: "inherit",
397
- env: process.env,
398
- });
399
-
400
- child.on("exit", function(code) {
401
- if (code === 120) {
402
- // Restart requested — reset terminal then spawn again
403
- if (process.stdout.isTTY) {
404
- process.stdout.write(
405
- "\x1B[?1000l\x1B[?1002l\x1B[?1003l\x1B[?1006l\x1B[?1015l" +
406
- "\x1B[0m\x1B[r\x1B[?25h\x1B[?1049l\x1B[2J\x1B[3J\x1B[H"
407
- );
408
- }
409
- // Reset terminal line discipline
410
- try { require("child_process").execSync("stty sane 2>/dev/null", { stdio: "pipe" }); } catch(e) {}
411
- // Drain any buffered stdin
412
- if (process.stdin.isTTY && typeof process.stdin.setRawMode === "function") {
413
- try { process.stdin.setRawMode(false); } catch(e) {}
414
- }
415
- // Set __OA_RESUMED for the new instance
416
- process.env.__OA_RESUMED = "update-only";
417
- // Brief pause then restart
418
- setTimeout(runOA, 500);
419
- } else {
420
- process.exit(code || 0);
421
- }
422
- });
423
-
424
- child.on("error", function(err) {
425
- console.error("Failed to start open-agents:", err.message || err);
426
- process.exit(1);
71
+ // Normal exit or too many restarts
72
+ resetTerminal();
73
+ process.exit(typeof code === 'number' ? code : 0);
427
74
  });
428
75
  }
429
76
 
430
- runOA();
431
- }
77
+ attach(child);
78
+ })();
@@ -401,10 +401,172 @@ function waitForHealth(timeoutMs, cb) {
401
401
 
402
402
  // ─── Main ───────────────────────────────────────────────────────────────────
403
403
 
404
+ // ─── Wrapper-modelfile self-heal ───────────────────────────────────────────
405
+ // Old OA versions wrote `FROM <blob-path>` into open-agents-* wrappers,
406
+ // which strips TEMPLATE/RENDERER/PARSER metadata and breaks tools support.
407
+ // On upgrade, scan local Ollama for stale wrappers and rebuild them with
408
+ // `FROM <baseModel>`. Best-effort, silent on failure — postinstall must not
409
+ // fail npm install.
410
+ function repairBrokenWrappers() {
411
+ // Independent opt-out from daemon install — wrapper repair is a different
412
+ // concern and runs even when OA_SKIP_DAEMON_INSTALL=1, unless explicitly
413
+ // disabled.
414
+ if (process.env.OA_SKIP_WRAPPER_REPAIR === "1") return;
415
+ // Skip if ollama isn't on PATH.
416
+ var ollamaBin = "";
417
+ try {
418
+ ollamaBin = cp.execSync("command -v ollama 2>/dev/null || which ollama 2>/dev/null", {
419
+ encoding: "utf8", stdio: "pipe", timeout: 5000,
420
+ }).trim();
421
+ } catch (e) { /* ignore */ }
422
+ if (!ollamaBin) return;
423
+
424
+ // List models. Format is one per line: NAME ID SIZE MODIFIED.
425
+ var listOut = "";
426
+ try {
427
+ listOut = cp.execSync(ollamaBin + " list 2>/dev/null", {
428
+ encoding: "utf8", stdio: "pipe", timeout: 8000,
429
+ });
430
+ } catch (e) { return; }
431
+ var lines = listOut.split(/\r?\n/).filter(function (l) { return l.trim().length > 0; });
432
+ // Skip header row if present.
433
+ if (lines.length > 0 && /^NAME\s/i.test(lines[0])) lines = lines.slice(1);
434
+ var allNames = lines.map(function (l) { return l.split(/\s+/)[0]; });
435
+ var wrappers = allNames.filter(function (n) { return /^open-agents-/i.test(n); });
436
+ var bases = allNames.filter(function (n) { return !/^open-agents-/i.test(n); });
437
+ if (wrappers.length === 0) return;
438
+
439
+ function expandedName(base) {
440
+ // Mirror packages/cli/src/tui/setup.ts:expandedModelName.
441
+ var canonical = base.replace(/:latest$/i, "");
442
+ return "open-agents-" + canonical.replace(":", "-").replace(/\./g, "");
443
+ }
444
+ function legacyExpandedName(base) {
445
+ return "open-agents-" + base.replace(":", "-").replace(/\./g, "");
446
+ }
447
+ function stripTag(n) { return (n || "").replace(/:[^:]+$/, ""); }
448
+ function guessBase(wrapper) {
449
+ var stripped = stripTag(wrapper);
450
+ // Try the FULL base name first (e.g. "qwen3.6:27b") so size-tagged
451
+ // variants don't collide with each other. expandedName/legacyExpandedName
452
+ // handle the tag internally.
453
+ for (var i = 0; i < bases.length; i++) {
454
+ var name = bases[i];
455
+ if (expandedName(name) === stripped || legacyExpandedName(name) === stripped) {
456
+ return name;
457
+ }
458
+ }
459
+ // Then fall back to stripped-tag base — this is the only way to match
460
+ // wrappers that were built from `<base>:latest` and got their canonical
461
+ // form (`open-agents-<base-stripped>`). Prefer bases tagged ":latest"
462
+ // so that ambiguity (qwen3.6:27b vs qwen3.6:35b vs qwen3.6:latest)
463
+ // resolves to the user-facing default.
464
+ var latestFirst = bases.slice().sort(function (a, b) {
465
+ var aL = /:latest$/i.test(a) ? 0 : 1;
466
+ var bL = /:latest$/i.test(b) ? 0 : 1;
467
+ return aL - bL;
468
+ });
469
+ for (var j = 0; j < latestFirst.length; j++) {
470
+ var basis = stripTag(latestFirst[j]);
471
+ if (expandedName(basis) === stripped || legacyExpandedName(basis) === stripped) {
472
+ return latestFirst[j];
473
+ }
474
+ }
475
+ return null;
476
+ }
477
+ function showCapabilities(name) {
478
+ try {
479
+ var out = cp.execSync(ollamaBin + " show " + name + " 2>/dev/null", {
480
+ encoding: "utf8", stdio: "pipe", timeout: 8000,
481
+ });
482
+ // Parse capabilities section
483
+ var inCaps = false;
484
+ var caps = [];
485
+ var rows = out.split(/\r?\n/);
486
+ for (var i = 0; i < rows.length; i++) {
487
+ var t = rows[i];
488
+ if (/^\s*Capabilities\s*$/.test(t)) { inCaps = true; continue; }
489
+ if (inCaps) {
490
+ if (/^\s*[A-Z][a-z]/.test(t) || t.trim() === "") { inCaps = false; continue; }
491
+ var v = t.trim();
492
+ if (v) caps.push(v.toLowerCase());
493
+ }
494
+ }
495
+ return caps;
496
+ } catch (e) { return null; }
497
+ }
498
+ function modelfileLooksBlob(name) {
499
+ try {
500
+ var mf = cp.execSync(ollamaBin + " show --modelfile " + name + " 2>/dev/null", {
501
+ encoding: "utf8", stdio: "pipe", timeout: 8000,
502
+ });
503
+ var m = mf.match(/^FROM\s+(.+)$/m);
504
+ if (!m) return false;
505
+ var f = m[1].trim();
506
+ return f.charAt(0) === "/" || /blobs\/sha256[-:]/.test(f);
507
+ } catch (e) { return false; }
508
+ }
509
+
510
+ var modelDir = path.join(HOME, ".open-agents", "models");
511
+ try { fs.mkdirSync(modelDir, { recursive: true }); } catch (e) {}
512
+
513
+ var rebuilt = 0;
514
+ var skipped = 0;
515
+ for (var i = 0; i < wrappers.length; i++) {
516
+ var w = wrappers[i];
517
+ var caps = showCapabilities(w);
518
+ var hasTools = caps !== null ? caps.indexOf("tools") !== -1 : true;
519
+ var blobby = modelfileLooksBlob(w);
520
+ if (hasTools && !blobby) { skipped++; continue; }
521
+ var base = guessBase(w);
522
+ if (!base) { skipped++; continue; }
523
+
524
+ // Read current num_ctx so we don't shrink it below what's already baked.
525
+ var numCtx = 32768;
526
+ try {
527
+ var pa = cp.execSync(ollamaBin + " show --parameters " + w + " 2>/dev/null", {
528
+ encoding: "utf8", stdio: "pipe", timeout: 5000,
529
+ });
530
+ var nm = pa.match(/num_ctx\s+(\d+)/);
531
+ if (nm) numCtx = Math.max(numCtx, parseInt(nm[1], 10));
532
+ } catch (e) {}
533
+ var numPredict = Math.min(16384, Math.max(2048, Math.floor(numCtx * 0.25)));
534
+ var content =
535
+ "FROM " + base + "\n" +
536
+ "PARAMETER num_ctx " + numCtx + "\n" +
537
+ "PARAMETER temperature 0\n" +
538
+ "PARAMETER num_predict " + numPredict + "\n" +
539
+ 'PARAMETER stop "<|endoftext|>"\n';
540
+ var mfPath = path.join(modelDir, "Modelfile." + stripTag(w));
541
+ try { fs.writeFileSync(mfPath, content, "utf8"); } catch (e) { skipped++; continue; }
542
+
543
+ try {
544
+ cp.execSync(ollamaBin + " create " + stripTag(w) + " -f " + JSON.stringify(mfPath) + " 2>&1", {
545
+ stdio: "pipe", timeout: 180000,
546
+ });
547
+ log("repaired wrapper: " + w + " (FROM " + base + ", num_ctx=" + numCtx + ")");
548
+ rebuilt++;
549
+ } catch (e) {
550
+ warn("failed to rebuild " + w + ": " + (e && e.message ? e.message.slice(0, 200) : "unknown"));
551
+ skipped++;
552
+ }
553
+ }
554
+
555
+ if (rebuilt > 0) {
556
+ log("Rebuilt " + rebuilt + " stale OA wrapper(s); " + skipped + " unchanged.");
557
+ }
558
+ }
559
+
404
560
  function main() {
405
561
  // Always do the nexus cleanup first, regardless of opt-out.
406
562
  cleanNexus();
407
563
 
564
+ // Auto-repair stale OA model wrappers BEFORE restarting the daemon, so the
565
+ // freshly-restarted daemon picks up the rebuilt models on first inference.
566
+ try { repairBrokenWrappers(); } catch (e) {
567
+ warn("wrapper auto-repair crashed (non-fatal): " + (e && e.message));
568
+ }
569
+
408
570
  if (process.env.OA_SKIP_DAEMON_INSTALL === "1") {
409
571
  log("OA_SKIP_DAEMON_INSTALL=1 — skipping daemon service install.");
410
572
  return safeExit(0);
@@ -1,508 +1,4 @@
1
1
  #!/usr/bin/env node
2
- // preinstall hook — runs BEFORE npm installs dependencies.
3
- // Pure CJS (no ESM, no backticks) so it works on any Node version.
4
- // Cross-platform: Linux, macOS, Windows (cmd, PowerShell, Git Bash).
5
- // If Node is too old, guides the user through installing Node 22.
6
- var nodeVersion = parseInt(process.versions.node, 10);
2
+ // Preinstall hook shim no-op by default
3
+ process.exit(0);
7
4
 
8
- if (nodeVersion >= 22) {
9
- // Good to go — let npm continue installing deps normally
10
- process.exit(0);
11
- }
12
-
13
- // Prevent infinite loop: if we're inside a self-triggered reinstall, bail
14
- if (process.env.OA_SELF_UPGRADE === "1") {
15
- console.log(" [preinstall] Skipping — already inside a self-upgrade.");
16
- console.log(" If this Node is still too old, open a new terminal with Node 22 and run:");
17
- console.log(" npm i -g open-agents-ai");
18
- process.exit(1);
19
- }
20
-
21
- var os = require("os");
22
- var path = require("path");
23
- var fs = require("fs");
24
- var childProcess = require("child_process");
25
- var readline = require("readline");
26
- var https = require("https");
27
- var http = require("http");
28
-
29
- var platform = os.platform(); // "win32", "linux", "darwin"
30
- var arch = os.arch();
31
- var isWindows = platform === "win32";
32
-
33
- console.log("");
34
- console.log(" +---------------------------------------------------+");
35
- console.log(" | open-agents -- Node.js Check |");
36
- console.log(" +---------------------------------------------------+");
37
- console.log("");
38
- console.log(" Your Node.js: " + process.version);
39
- console.log(" Required: >= 22.0.0");
40
- console.log(" Platform: " + platform + "/" + arch);
41
- console.log("");
42
-
43
- // ── Cross-platform helpers ───────────────────────────────────────────────
44
-
45
- function hasCmd(cmd) {
46
- try {
47
- if (isWindows) {
48
- childProcess.execSync("where " + cmd, { stdio: "pipe", timeout: 5000 });
49
- } else {
50
- childProcess.execSync("which " + cmd, { stdio: "pipe", timeout: 3000 });
51
- }
52
- return true;
53
- } catch (e) {
54
- return false;
55
- }
56
- }
57
-
58
- function run(cmd, opts) {
59
- console.log(" $ " + cmd);
60
- try {
61
- childProcess.execSync(cmd, Object.assign({ stdio: "inherit" }, opts || {}));
62
- return true;
63
- } catch (e) {
64
- return false;
65
- }
66
- }
67
-
68
- var rl = readline.createInterface({ input: process.stdin, output: process.stdout });
69
-
70
- function ask(question, cb) {
71
- rl.question(question, function(answer) {
72
- cb(answer.trim().toLowerCase());
73
- });
74
- }
75
-
76
- function bail(msg) {
77
- console.log(msg);
78
- rl.close();
79
- process.exit(1);
80
- }
81
-
82
- // ── Node.js-native HTTPS downloader (works everywhere, no curl needed) ──
83
-
84
- function download(url, cb) {
85
- var mod = url.indexOf("https:") === 0 ? https : http;
86
- mod.get(url, function(res) {
87
- // Follow redirects (301/302/307/308)
88
- if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
89
- download(res.headers.location, cb);
90
- return;
91
- }
92
- if (res.statusCode !== 200) {
93
- cb(new Error("HTTP " + res.statusCode + " from " + url), null);
94
- return;
95
- }
96
- var chunks = [];
97
- res.on("data", function(chunk) { chunks.push(chunk); });
98
- res.on("end", function() { cb(null, Buffer.concat(chunks)); });
99
- res.on("error", function(err) { cb(err, null); });
100
- }).on("error", function(err) { cb(err, null); });
101
- }
102
-
103
- function downloadToFile(url, dest, cb) {
104
- download(url, function(err, data) {
105
- if (err) { cb(err); return; }
106
- try {
107
- fs.writeFileSync(dest, data);
108
- cb(null);
109
- } catch (e) {
110
- cb(e);
111
- }
112
- });
113
- }
114
-
115
- // ── Shell / downloader detection ─────────────────────────────────────────
116
-
117
- // Returns a shell command prefix for piped downloads, or null.
118
- // On Windows, we prefer the Node.js-native download() function instead.
119
- function getShellDownloader() {
120
- if (hasCmd("curl")) return "curl -fsSL";
121
- if (hasCmd("wget")) return "wget -qO-";
122
- return null;
123
- }
124
-
125
- function getShell() {
126
- if (isWindows) return null; // Windows doesn't use bash shells for nvm
127
- return process.env.SHELL || "/bin/bash";
128
- }
129
-
130
- // ── nvm detection (Unix) ─────────────────────────────────────────────────
131
-
132
- var nvmDir = process.env.NVM_DIR || path.join(os.homedir(), ".nvm");
133
- var hasNvm = false;
134
- try { fs.statSync(path.join(nvmDir, "nvm.sh")); hasNvm = true; } catch (e) {}
135
-
136
- // nvm-windows detection
137
- var nvmWinDir = process.env.NVM_HOME || "";
138
- var hasNvmWin = false;
139
- if (isWindows) {
140
- if (!nvmWinDir) {
141
- // Common install location
142
- var appData = process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
143
- nvmWinDir = path.join(appData, "nvm");
144
- }
145
- try { fs.statSync(path.join(nvmWinDir, "nvm.exe")); hasNvmWin = true; } catch (e) {}
146
- if (!hasNvmWin) { try { hasNvmWin = hasCmd("nvm"); } catch (e) {} }
147
- }
148
-
149
- // ── Windows flow ─────────────────────────────────────────────────────────
150
-
151
- function windowsInstallNode(next) {
152
- console.log(" Windows detected. Checking Node.js install options...");
153
- console.log("");
154
-
155
- // Option 1: nvm-windows is installed
156
- if (hasNvmWin) {
157
- console.log(" nvm-windows found.");
158
- ask(" Install Node.js 22 via nvm? [Y/n] ", function(a) {
159
- if (a === "n" || a === "no") {
160
- bail("\n Install manually:\n nvm install 22\n nvm use 22\n npm i -g open-agents-ai\n");
161
- return;
162
- }
163
- var ok = run("nvm install 22") && run("nvm use 22");
164
- if (!ok) {
165
- bail("\n nvm install failed. Try manually:\n nvm install 22\n nvm use 22\n npm i -g open-agents-ai\n");
166
- return;
167
- }
168
- next();
169
- });
170
- return;
171
- }
172
-
173
- // Option 2: winget available (Windows 10 1709+ / Windows 11)
174
- if (hasCmd("winget")) {
175
- ask(" Install Node.js 22 via winget? [Y/n] ", function(a) {
176
- if (a === "n" || a === "no") {
177
- windowsFallbackInstructions();
178
- return;
179
- }
180
- var ok = run("winget install -e --id OpenJS.NodeJS.LTS --accept-source-agreements --accept-package-agreements");
181
- if (ok) {
182
- console.log("");
183
- console.log(" Node.js 22 installed via winget.");
184
- console.log(" Please close and reopen your terminal, then run:");
185
- console.log(" npm i -g open-agents-ai");
186
- console.log("");
187
- rl.close();
188
- process.exit(0);
189
- return;
190
- }
191
- console.log(" winget install failed, trying alternative...");
192
- windowsDirectDownload(next);
193
- });
194
- return;
195
- }
196
-
197
- // Option 3: choco available
198
- if (hasCmd("choco")) {
199
- ask(" Install Node.js 22 via Chocolatey? [Y/n] ", function(a) {
200
- if (a === "n" || a === "no") {
201
- windowsFallbackInstructions();
202
- return;
203
- }
204
- var ok = run("choco install nodejs-lts -y");
205
- if (ok) {
206
- console.log("");
207
- console.log(" Node.js installed via Chocolatey.");
208
- console.log(" Please close and reopen your terminal, then run:");
209
- console.log(" npm i -g open-agents-ai");
210
- console.log("");
211
- rl.close();
212
- process.exit(0);
213
- return;
214
- }
215
- console.log(" choco install failed, trying alternative...");
216
- windowsDirectDownload(next);
217
- });
218
- return;
219
- }
220
-
221
- // Option 4: scoop available
222
- if (hasCmd("scoop")) {
223
- ask(" Install Node.js 22 via scoop? [Y/n] ", function(a) {
224
- if (a === "n" || a === "no") {
225
- windowsFallbackInstructions();
226
- return;
227
- }
228
- var ok = run("scoop install nodejs-lts");
229
- if (ok) {
230
- console.log("");
231
- console.log(" Node.js installed via scoop.");
232
- console.log(" Please close and reopen your terminal, then run:");
233
- console.log(" npm i -g open-agents-ai");
234
- console.log("");
235
- rl.close();
236
- process.exit(0);
237
- return;
238
- }
239
- console.log(" scoop install failed, trying direct download...");
240
- windowsDirectDownload(next);
241
- });
242
- return;
243
- }
244
-
245
- // Option 5: direct MSI download via Node.js native https
246
- windowsDirectDownload(next);
247
- }
248
-
249
- function windowsDirectDownload(next) {
250
- var msiArch = arch === "x64" ? "x64" : arch === "arm64" ? "arm64" : "x86";
251
- var msiUrl = "https://nodejs.org/dist/v22.16.0/node-v22.16.0-" + msiArch + ".msi";
252
- var msiDest = path.join(os.tmpdir(), "node-v22-installer.msi");
253
-
254
- ask(" Download Node.js 22 installer directly? [Y/n] ", function(a) {
255
- if (a === "n" || a === "no") {
256
- windowsFallbackInstructions();
257
- return;
258
- }
259
- console.log("");
260
- console.log(" Downloading Node.js 22 from nodejs.org...");
261
- console.log(" URL: " + msiUrl);
262
- console.log(" (this may take a moment)");
263
- console.log("");
264
-
265
- downloadToFile(msiUrl, msiDest, function(err) {
266
- if (err) {
267
- console.log(" Download failed: " + (err.message || err));
268
- console.log("");
269
- windowsFallbackInstructions();
270
- return;
271
- }
272
- console.log(" Downloaded to: " + msiDest);
273
- console.log("");
274
- console.log(" Launching installer...");
275
- var ok = run("msiexec /i \"" + msiDest + "\"");
276
- if (ok) {
277
- console.log("");
278
- console.log(" Node.js 22 installer completed.");
279
- console.log(" Close and reopen your terminal, then run:");
280
- console.log(" npm i -g open-agents-ai");
281
- console.log("");
282
- } else {
283
- console.log("");
284
- console.log(" Installer may have been cancelled or requires admin privileges.");
285
- console.log(" Run the installer manually:");
286
- console.log(" " + msiDest);
287
- console.log("");
288
- console.log(" Then reopen your terminal and run:");
289
- console.log(" npm i -g open-agents-ai");
290
- console.log("");
291
- }
292
- rl.close();
293
- process.exit(0);
294
- });
295
- });
296
- }
297
-
298
- function windowsFallbackInstructions() {
299
- bail(
300
- "\n Install Node.js 22 manually:\n\n" +
301
- " Option 1 — Download from nodejs.org:\n" +
302
- " https://nodejs.org/en/download/\n\n" +
303
- " Option 2 — winget (Windows 10/11):\n" +
304
- " winget install OpenJS.NodeJS.LTS\n\n" +
305
- " Option 3 — Chocolatey:\n" +
306
- " choco install nodejs-lts\n\n" +
307
- " Option 4 — nvm-windows:\n" +
308
- " https://github.com/coreybutler/nvm-windows/releases\n" +
309
- " nvm install 22 && nvm use 22\n\n" +
310
- " Then re-run: npm i -g open-agents-ai\n"
311
- );
312
- }
313
-
314
- // ── Unix flow (Linux / macOS) ────────────────────────────────────────────
315
-
316
- function unixInstallNvm(next) {
317
- if (hasNvm) {
318
- console.log(" nvm found at " + nvmDir);
319
- console.log("");
320
- next();
321
- return;
322
- }
323
-
324
- ask(" nvm is not installed. Install nvm now? [Y/n] ", function(a) {
325
- if (a === "n" || a === "no") {
326
- bail(
327
- "\n Install nvm manually:\n" +
328
- " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
329
- " source ~/.bashrc\n" +
330
- " nvm install 22\n" +
331
- " npm i -g open-agents-ai\n"
332
- );
333
- return;
334
- }
335
- console.log("");
336
-
337
- // Try shell downloader first (curl/wget), fall back to Node.js native https
338
- var dl = getShellDownloader();
339
- if (dl) {
340
- var ok = run(dl + " https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash");
341
- if (ok) {
342
- nvmDir = process.env.NVM_DIR || path.join(os.homedir(), ".nvm");
343
- hasNvm = true;
344
- console.log("");
345
- next();
346
- return;
347
- }
348
- }
349
-
350
- // Fallback: download nvm install script via Node.js https module
351
- console.log(" Downloading nvm install script via Node.js...");
352
- download("https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh", function(err, data) {
353
- if (err) {
354
- bail("\n Could not download nvm install script: " + (err.message || err) +
355
- "\n Install nvm manually: https://github.com/nvm-sh/nvm#installing-and-updating\n");
356
- return;
357
- }
358
- var scriptPath = path.join(os.tmpdir(), "nvm-install.sh");
359
- fs.writeFileSync(scriptPath, data, { mode: 0o755 });
360
- var shell = getShell();
361
- var ok = run(shell + " " + scriptPath);
362
- try { fs.unlinkSync(scriptPath); } catch (e) {}
363
- if (!ok) {
364
- bail("\n nvm install failed. See https://github.com/nvm-sh/nvm#installing-and-updating\n");
365
- return;
366
- }
367
- nvmDir = process.env.NVM_DIR || path.join(os.homedir(), ".nvm");
368
- hasNvm = true;
369
- console.log("");
370
- next();
371
- });
372
- });
373
- }
374
-
375
- function unixInstallNode(next) {
376
- ask(" Install Node.js 22 via nvm? [Y/n] ", function(a) {
377
- if (a === "n" || a === "no") {
378
- bail(
379
- "\n Install manually:\n" +
380
- " nvm install 22 && nvm alias default 22\n" +
381
- " npm i -g open-agents-ai\n"
382
- );
383
- return;
384
- }
385
- console.log("");
386
- var shell = getShell();
387
- var cmd = 'export NVM_DIR="' + nvmDir + '" && ' +
388
- '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" && ' +
389
- "nvm install 22 && nvm alias default 22";
390
- var ok = run(shell + " -c '" + cmd + "'");
391
- if (!ok) {
392
- bail("\n Node 22 install failed.\n");
393
- return;
394
- }
395
- console.log("");
396
- next();
397
- });
398
- }
399
-
400
- // Find the Node 22 binary installed by nvm (Unix only)
401
- function findNode22Bin() {
402
- var versionsDir = path.join(nvmDir, "versions", "node");
403
- try {
404
- var versions = fs.readdirSync(versionsDir).filter(function(v) {
405
- return v.startsWith("v22");
406
- }).sort().reverse();
407
- if (versions.length > 0) {
408
- var bin = path.join(versionsDir, versions[0], "bin", "node");
409
- try { fs.statSync(bin); return bin; } catch (e) {}
410
- }
411
- } catch (e) {}
412
- try {
413
- var shell = getShell();
414
- var result = childProcess.execSync(
415
- shell + ' -c \'export NVM_DIR="' + nvmDir + '" && [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" && nvm which 22\'',
416
- { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 10000 }
417
- ).trim();
418
- if (result && fs.existsSync(result)) return result;
419
- } catch (e) {}
420
- return null;
421
- }
422
-
423
- function unixReinstall() {
424
- ask(" Reinstall open-agents under Node 22? [Y/n] ", function(a) {
425
- if (a === "n" || a === "no") {
426
- console.log(
427
- "\n Open a new terminal, then run:\n" +
428
- " npm i -g open-agents-ai\n"
429
- );
430
- rl.close();
431
- process.exit(1);
432
- return;
433
- }
434
- console.log("");
435
- var shell = getShell();
436
- var cmd = 'export OA_SELF_UPGRADE=1 && export NVM_DIR="' + nvmDir + '" && ' +
437
- '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" && ' +
438
- "nvm use 22 && npm i -g open-agents-ai";
439
- var ok = run(shell + " -c '" + cmd + "'");
440
- if (!ok) {
441
- console.log(
442
- "\n Reinstall failed. Open a new terminal, then run:\n" +
443
- " npm i -g open-agents-ai\n"
444
- );
445
- rl.close();
446
- process.exit(1);
447
- return;
448
- }
449
- console.log("");
450
-
451
- // Tell the user exactly how to launch
452
- var node22 = findNode22Bin();
453
- var binHint = node22 ? path.dirname(node22) : "~/.nvm/versions/node/v22.x.x/bin";
454
- console.log(" +---------------------------------------------------+");
455
- console.log(" | Done! Node 22 installed + open-agents ready. |");
456
- console.log(" +---------------------------------------------------+");
457
- console.log("");
458
- console.log(" To launch immediately (no new terminal needed):");
459
- console.log(" " + binHint + "/oa");
460
- console.log("");
461
- console.log(" Or source your shell config first:");
462
- var rcFile = (process.env.SHELL || "").indexOf("zsh") !== -1 ? "~/.zshrc" : "~/.bashrc";
463
- console.log(" source " + rcFile + " && oa");
464
- console.log("");
465
- rl.close();
466
- process.exit(0);
467
- });
468
- }
469
-
470
- // ── Main flow — branch on platform ───────────────────────────────────────
471
-
472
- ask(" Install Node.js 22 now? [Y/n] ", function(a) {
473
- if (a === "n" || a === "no") {
474
- if (isWindows) {
475
- windowsFallbackInstructions();
476
- } else {
477
- bail(
478
- "\n To install manually:\n" +
479
- " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash\n" +
480
- " source ~/.bashrc\n" +
481
- " nvm install 22\n" +
482
- " nvm alias default 22\n" +
483
- " npm i -g open-agents-ai\n"
484
- );
485
- }
486
- return;
487
- }
488
- console.log("");
489
-
490
- if (isWindows) {
491
- windowsInstallNode(function() {
492
- console.log("");
493
- console.log(" Node.js 22 should now be available.");
494
- console.log(" Close and reopen your terminal, then run:");
495
- console.log(" npm i -g open-agents-ai");
496
- console.log("");
497
- rl.close();
498
- process.exit(0);
499
- });
500
- } else {
501
- // Unix flow: ensure downloader -> nvm -> node 22 -> reinstall
502
- unixInstallNvm(function() {
503
- unixInstallNode(function() {
504
- unixReinstall();
505
- });
506
- });
507
- }
508
- });
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.187.459",
3
+ "version": "0.187.461",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "open-agents-ai",
9
- "version": "0.187.459",
9
+ "version": "0.187.461",
10
10
  "hasInstallScript": true,
11
11
  "license": "CC-BY-NC-4.0",
12
12
  "dependencies": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.187.459",
3
+ "version": "0.187.461",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",