open-agents-ai 0.103.22 → 0.103.24

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.
Files changed (2) hide show
  1. package/dist/index.js +169 -43
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -27508,7 +27508,30 @@ var init_expose = __esm({
27508
27508
  this.server.listen(port, "127.0.0.1", () => resolve31());
27509
27509
  this.server.on("error", reject);
27510
27510
  });
27511
- this._tunnelUrl = await this.startCloudflared(port);
27511
+ let lastStartErr;
27512
+ for (let attempt = 0; attempt < 3; attempt++) {
27513
+ try {
27514
+ this._tunnelUrl = await this.startCloudflared(port);
27515
+ break;
27516
+ } catch (err) {
27517
+ lastStartErr = err instanceof Error ? err : new Error(String(err));
27518
+ const is429 = lastStartErr.message.includes("429") || lastStartErr.message.includes("Too Many Requests");
27519
+ if (is429 && attempt < 2) {
27520
+ const delaySec = (attempt + 1) * 15;
27521
+ this.options.onInfo?.(`Cloudflare rate limited \u2014 retrying in ${delaySec}s (attempt ${attempt + 2}/3)...`);
27522
+ await new Promise((r) => setTimeout(r, delaySec * 1e3));
27523
+ continue;
27524
+ }
27525
+ if (this.server) {
27526
+ await new Promise((r) => this.server.close(() => r()));
27527
+ this.server = null;
27528
+ }
27529
+ throw lastStartErr;
27530
+ }
27531
+ }
27532
+ if (!this._tunnelUrl) {
27533
+ throw lastStartErr ?? new Error("Cloudflared tunnel start failed after retries");
27534
+ }
27512
27535
  this._stats.status = "active";
27513
27536
  this.emitStats();
27514
27537
  if (this._stateDir) {
@@ -27931,25 +27954,37 @@ var init_expose = __esm({
27931
27954
  async restartCloudflared() {
27932
27955
  if (!this.server || this._proxyPort === 0)
27933
27956
  return;
27934
- try {
27935
- this._tunnelUrl = await this.startCloudflared(this._proxyPort);
27936
- this._stats.status = "active";
27937
- this.emitStats();
27938
- this.options.onInfo?.(`Tunnel restarted with new URL \u2014 share with consumers:
27957
+ for (let attempt = 0; attempt < 3; attempt++) {
27958
+ try {
27959
+ this._tunnelUrl = await this.startCloudflared(this._proxyPort);
27960
+ this._stats.status = "active";
27961
+ this.emitStats();
27962
+ this.options.onInfo?.(`Tunnel restarted with new URL \u2014 share with consumers:
27939
27963
  ${this.formatConnectionInfo()}`);
27940
- if (this._stateDir) {
27941
- writeExposeState(this._stateDir, {
27942
- pid: this._cloudflaredPid,
27943
- tunnelUrl: this._tunnelUrl,
27944
- authKey: this._authKey,
27945
- proxyPort: this._proxyPort,
27946
- targetUrl: this._targetUrl,
27947
- kind: this._kind,
27948
- startedAt: (/* @__PURE__ */ new Date()).toISOString()
27949
- });
27964
+ if (this._stateDir) {
27965
+ writeExposeState(this._stateDir, {
27966
+ pid: this._cloudflaredPid,
27967
+ tunnelUrl: this._tunnelUrl,
27968
+ authKey: this._authKey,
27969
+ proxyPort: this._proxyPort,
27970
+ targetUrl: this._targetUrl,
27971
+ kind: this._kind,
27972
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
27973
+ });
27974
+ }
27975
+ return;
27976
+ } catch (err) {
27977
+ const msg = err instanceof Error ? err.message : String(err);
27978
+ const is429 = msg.includes("429") || msg.includes("Too Many Requests");
27979
+ if (is429 && attempt < 2) {
27980
+ const delaySec = (attempt + 1) * 15;
27981
+ this.options.onInfo?.(`Cloudflare rate limited \u2014 retrying in ${delaySec}s...`);
27982
+ await new Promise((r) => setTimeout(r, delaySec * 1e3));
27983
+ continue;
27984
+ }
27985
+ this.options.onError?.(`Tunnel restart failed: ${msg}`);
27986
+ return;
27950
27987
  }
27951
- } catch (err) {
27952
- this.options.onError?.(`Tunnel restart failed: ${err instanceof Error ? err.message : String(err)}`);
27953
27988
  }
27954
27989
  }
27955
27990
  /**
@@ -28143,7 +28178,7 @@ ${this.formatConnectionInfo()}`);
28143
28178
  if (!connectResult.success) {
28144
28179
  throw new Error(`Nexus daemon connect failed: ${connectResult.error}`);
28145
28180
  }
28146
- await new Promise((r) => setTimeout(r, 2e3));
28181
+ await new Promise((r) => setTimeout(r, 500));
28147
28182
  this._onInfo?.("Registering inference capabilities...");
28148
28183
  let exposeResult = await this._nexusTool.execute({
28149
28184
  action: "expose",
@@ -28152,7 +28187,7 @@ ${this.formatConnectionInfo()}`);
28152
28187
  auth_key: this._authKey
28153
28188
  });
28154
28189
  if (!exposeResult.success && exposeResult.error?.includes("not running")) {
28155
- await new Promise((r) => setTimeout(r, 3e3));
28190
+ await new Promise((r) => setTimeout(r, 1500));
28156
28191
  exposeResult = await this._nexusTool.execute({
28157
28192
  action: "expose",
28158
28193
  ollama_url: this._targetUrl,
@@ -28165,7 +28200,7 @@ ${this.formatConnectionInfo()}`);
28165
28200
  }
28166
28201
  const nexusDir = this._nexusTool.getNexusDir();
28167
28202
  const statusPath = join35(nexusDir, "status.json");
28168
- for (let i = 0; i < 60; i++) {
28203
+ for (let i = 0; i < 80; i++) {
28169
28204
  try {
28170
28205
  const raw = readFileSync19(statusPath, "utf8");
28171
28206
  if (raw.length > 10) {
@@ -28179,7 +28214,7 @@ ${this.formatConnectionInfo()}`);
28179
28214
  }
28180
28215
  } catch {
28181
28216
  }
28182
- await new Promise((r) => setTimeout(r, 500));
28217
+ await new Promise((r) => setTimeout(r, i < 20 ? 100 : 250));
28183
28218
  }
28184
28219
  this._stats.status = "active";
28185
28220
  this._stats.startedAt = Date.now();
@@ -29721,8 +29756,26 @@ async function fetchOpenAIModels(baseUrl, apiKey) {
29721
29756
  async function fetchPeerModels(peerId) {
29722
29757
  try {
29723
29758
  const { NexusTool: NexusTool2 } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
29759
+ const { existsSync: existsSync42, readFileSync: readFileSync31 } = await import("node:fs");
29760
+ const { join: join55 } = await import("node:path");
29724
29761
  const cwd4 = process.cwd();
29725
29762
  const nexusTool = new NexusTool2(cwd4);
29763
+ const pricingPath = join55(nexusTool.getNexusDir(), "pricing.json");
29764
+ if (existsSync42(pricingPath)) {
29765
+ try {
29766
+ const pricing = JSON.parse(readFileSync31(pricingPath, "utf8"));
29767
+ const localModels = (pricing.models || []).map((m) => ({
29768
+ name: m.model || "unknown",
29769
+ size: m.parameterSize || "",
29770
+ modified: "",
29771
+ sizeBytes: 0,
29772
+ parameterSize: m.parameterSize || "remote"
29773
+ }));
29774
+ if (localModels.length > 0)
29775
+ return localModels;
29776
+ } catch {
29777
+ }
29778
+ }
29726
29779
  const result = await nexusTool.execute({
29727
29780
  action: "find_agent",
29728
29781
  peer_id: peerId
@@ -29744,19 +29797,6 @@ async function fetchPeerModels(peerId) {
29744
29797
  if (models.length > 0)
29745
29798
  return models;
29746
29799
  }
29747
- const { existsSync: existsSync42, readFileSync: readFileSync31 } = await import("node:fs");
29748
- const { join: join55 } = await import("node:path");
29749
- const pricingPath = join55(nexusTool.getNexusDir(), "pricing.json");
29750
- if (existsSync42(pricingPath)) {
29751
- const pricing = JSON.parse(readFileSync31(pricingPath, "utf8"));
29752
- return (pricing.models || []).map((m) => ({
29753
- name: m.model || "unknown",
29754
- size: m.parameterSize || "",
29755
- modified: "",
29756
- sizeBytes: 0,
29757
- parameterSize: m.parameterSize || "remote"
29758
- }));
29759
- }
29760
29800
  return [];
29761
29801
  } catch {
29762
29802
  return [];
@@ -30565,6 +30605,20 @@ function loadUsageHistory(kind, repoRoot) {
30565
30605
  }
30566
30606
  return Array.from(map.values()).sort((a, b) => new Date(b.lastUsed).getTime() - new Date(a.lastUsed).getTime());
30567
30607
  }
30608
+ function deleteUsageRecord(kind, value, repoRoot) {
30609
+ const remove = (filePath) => {
30610
+ const data = loadUsageFile(filePath);
30611
+ const before = data.records.length;
30612
+ data.records = data.records.filter((r) => !(r.kind === kind && r.value === value));
30613
+ if (data.records.length < before) {
30614
+ saveUsageFile(filePath, data);
30615
+ }
30616
+ };
30617
+ remove(join39(homedir9(), ".open-agents", USAGE_HISTORY_FILE));
30618
+ if (repoRoot) {
30619
+ remove(join39(repoRoot, OA_DIR, USAGE_HISTORY_FILE));
30620
+ }
30621
+ }
30568
30622
  var OA_DIR, SUBDIRS, CONTEXT_FILES, PENDING_TASK_FILE, CONTEXT_SAVE_FILE, MAX_CONTEXT_ENTRIES, SKIP_DIRS, USAGE_HISTORY_FILE, MAX_HISTORY_RECORDS;
30569
30623
  var init_oa_directory = __esm({
30570
30624
  "packages/cli/dist/tui/oa-directory.js"() {
@@ -32208,6 +32262,8 @@ function tuiSelect(opts) {
32208
32262
  const isSkippable = (idx) => skipSet.has(items[idx].key);
32209
32263
  let filter = "";
32210
32264
  let matchSet = /* @__PURE__ */ new Set();
32265
+ let deleteConfirmIdx = -1;
32266
+ let deleteConfirmSel = false;
32211
32267
  function updateFilter() {
32212
32268
  if (!filter) {
32213
32269
  matchSet = new Set(items.map((_, i) => i));
@@ -32332,7 +32388,11 @@ function tuiSelect(opts) {
32332
32388
  }
32333
32389
  const focused = idx === cursor;
32334
32390
  const isActive = item.key === activeKey;
32335
- if (filter) {
32391
+ if (deleteConfirmIdx === idx) {
32392
+ const yesLabel = deleteConfirmSel ? selectColors.bold(selectColors.green("[Yes]")) : selectColors.dim("[Yes]");
32393
+ const noLabel = !deleteConfirmSel ? selectColors.bold(selectColors.orange("[No]")) : selectColors.dim("[No]");
32394
+ lines.push(` ${ansi3("31", "\u2715")} ${ansi3("31", stripAnsi(item.label))} Delete? ${yesLabel} ${noLabel}`);
32395
+ } else if (filter) {
32336
32396
  lines.push(matchRow(item, focused, isActive));
32337
32397
  } else {
32338
32398
  lines.push(renderRow(item, focused, isActive));
@@ -32343,8 +32403,13 @@ function tuiSelect(opts) {
32343
32403
  lines.push(` ${selectColors.dim(` \u25BC ${remaining} more`)}`);
32344
32404
  }
32345
32405
  lines.push("");
32346
- const actionHint = opts.onAction ? " \u2190/\u2192/Space toggle" : "";
32347
- lines.push(` ${selectColors.dim("\u2191/\u2193 navigate Enter select" + actionHint + " Esc " + (filter ? "clear filter" : "cancel") + " Type to filter")}`);
32406
+ if (deleteConfirmIdx >= 0) {
32407
+ lines.push(` ${selectColors.dim("\u2190/\u2192 select Enter confirm Esc cancel")}`);
32408
+ } else {
32409
+ const actionHint = opts.onAction ? " \u2190/\u2192/Space toggle" : "";
32410
+ const deleteHint = opts.onDelete ? " Del remove" : "";
32411
+ lines.push(` ${selectColors.dim("\u2191/\u2193 navigate Enter select" + actionHint + deleteHint + " Esc " + (filter ? "clear filter" : "cancel") + " Type to filter")}`);
32412
+ }
32348
32413
  lines.push("");
32349
32414
  const output = lines.join("\n");
32350
32415
  process.stdout.write(output);
@@ -32374,6 +32439,48 @@ function tuiSelect(opts) {
32374
32439
  }
32375
32440
  function onData(chunk) {
32376
32441
  const seq = chunk.toString("utf8");
32442
+ if (deleteConfirmIdx >= 0) {
32443
+ if (seq === "\x1B[D") {
32444
+ deleteConfirmSel = true;
32445
+ render();
32446
+ } else if (seq === "\x1B[C") {
32447
+ deleteConfirmSel = false;
32448
+ render();
32449
+ } else if (seq === "\r" || seq === "\n") {
32450
+ if (deleteConfirmSel && opts.onDelete) {
32451
+ const deletedIdx = deleteConfirmIdx;
32452
+ deleteConfirmIdx = -1;
32453
+ deleteConfirmSel = false;
32454
+ opts.onDelete(items[deletedIdx], (removed) => {
32455
+ if (removed) {
32456
+ items.splice(deletedIdx, 1);
32457
+ if (items.length === 0) {
32458
+ cleanup();
32459
+ resolve31({ confirmed: false, key: null, index: -1 });
32460
+ return;
32461
+ }
32462
+ updateFilter();
32463
+ if (cursor >= items.length)
32464
+ cursor = items.length - 1;
32465
+ const valid = findSelectable(cursor, 1) ?? findSelectable(cursor, -1);
32466
+ if (valid >= 0)
32467
+ cursor = valid;
32468
+ scrollOffset = 0;
32469
+ }
32470
+ render();
32471
+ });
32472
+ } else {
32473
+ deleteConfirmIdx = -1;
32474
+ deleteConfirmSel = false;
32475
+ render();
32476
+ }
32477
+ } else if (seq === "\x1B" || seq === "\x1B\x1B" || seq === "") {
32478
+ deleteConfirmIdx = -1;
32479
+ deleteConfirmSel = false;
32480
+ render();
32481
+ }
32482
+ return;
32483
+ }
32377
32484
  if (seq === "\x1B[A") {
32378
32485
  const next = findSelectable(cursor - 1, -1);
32379
32486
  if (next >= 0 && next !== cursor) {
@@ -32414,6 +32521,12 @@ function tuiSelect(opts) {
32414
32521
  cursor = last;
32415
32522
  render();
32416
32523
  }
32524
+ } else if (seq === "\x1B[3~" && opts.onDelete) {
32525
+ if (!isSkippable(cursor) && matchSet.has(cursor) && items[cursor].key !== activeKey) {
32526
+ deleteConfirmIdx = cursor;
32527
+ deleteConfirmSel = false;
32528
+ render();
32529
+ }
32417
32530
  } else if (seq === "\x1B[D" && opts.onAction) {
32418
32531
  if (!isSkippable(cursor) && matchSet.has(cursor)) {
32419
32532
  if (opts.onAction(items[cursor], "left"))
@@ -33815,20 +33928,28 @@ async function handleEndpoint(arg, ctx, local = false) {
33815
33928
  const items = history.map((h) => {
33816
33929
  const provider2 = h.meta?.provider ?? detectProvider(h.value).label;
33817
33930
  const uses = h.localUses > 0 ? `${h.useCount} uses (${h.localUses} local)` : `${h.useCount} uses`;
33931
+ const auth = h.meta?.authHint ? ` Auth: ${h.meta.authHint}` : "";
33818
33932
  return {
33819
33933
  key: h.value,
33820
33934
  label: h.value,
33821
- detail: `${provider2} ${uses}`
33935
+ detail: `${provider2} ${uses}${auth}`
33822
33936
  };
33823
33937
  });
33824
33938
  const result = await tuiSelect({
33825
33939
  items,
33826
33940
  activeKey: ctx.config.backendUrl,
33827
33941
  title: "Select Endpoint",
33828
- rl: ctx.rl
33942
+ rl: ctx.rl,
33943
+ onDelete: (item, done) => {
33944
+ deleteUsageRecord("endpoint", item.key, ctx.repoRoot);
33945
+ done(true);
33946
+ }
33829
33947
  });
33830
33948
  if (result.confirmed && result.key) {
33831
- await handleEndpoint(result.key, ctx, local);
33949
+ const selectedRecord = history.find((h) => h.value === result.key);
33950
+ const savedAuth = selectedRecord?.meta?.authKey;
33951
+ const endpointArg = savedAuth ? `${result.key} --auth ${savedAuth}` : result.key;
33952
+ await handleEndpoint(endpointArg, ctx, local);
33832
33953
  return;
33833
33954
  }
33834
33955
  renderInfo("Endpoint selection cancelled.");
@@ -33902,6 +34023,11 @@ async function handleEndpoint(arg, ctx, local = false) {
33902
34023
  await handlePeerEndpoint(url, apiKey, ctx, local);
33903
34024
  return;
33904
34025
  }
34026
+ if (url.startsWith("peer://")) {
34027
+ const extractedPeerId = url.slice(7);
34028
+ await handlePeerEndpoint(extractedPeerId, apiKey, ctx, local);
34029
+ return;
34030
+ }
33905
34031
  try {
33906
34032
  new URL(url);
33907
34033
  } catch {
@@ -33975,7 +34101,7 @@ async function handleEndpoint(arg, ctx, local = false) {
33975
34101
  meta: {
33976
34102
  provider: provider.label,
33977
34103
  backendType,
33978
- ...apiKey ? { authHint: apiKey.slice(0, 4) + "..." } : {}
34104
+ ...apiKey ? { authHint: apiKey.slice(0, 4) + "...", authKey: apiKey } : {}
33979
34105
  }
33980
34106
  });
33981
34107
  process.stdout.write(`
@@ -34067,7 +34193,7 @@ async function handlePeerEndpoint(peerId, authKey, ctx, local) {
34067
34193
  provider: "libp2p-peer",
34068
34194
  backendType: "nexus",
34069
34195
  peerId,
34070
- ...authKey ? { authHint: authKey.slice(0, 4) + "..." } : {}
34196
+ ...authKey ? { authHint: authKey.slice(0, 4) + "...", authKey } : {}
34071
34197
  }
34072
34198
  });
34073
34199
  process.stdout.write(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.103.22",
3
+ "version": "0.103.24",
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",