cascade-ai 0.12.14 → 0.12.16

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/cli.cjs CHANGED
@@ -3,6 +3,7 @@
3
3
  var Anthropic = require('@anthropic-ai/sdk');
4
4
  var OpenAI = require('openai');
5
5
  var genai = require('@google/genai');
6
+ var dns = require('dns');
6
7
  var ink = require('ink');
7
8
  var commander = require('commander');
8
9
  var React2 = require('react');
@@ -24,7 +25,7 @@ var glob = require('glob');
24
25
  var util = require('util');
25
26
  var simpleGit = require('simple-git');
26
27
  var PDFDocument = require('pdfkit');
27
- var dns = require('dns/promises');
28
+ var dns2 = require('dns/promises');
28
29
  var net = require('net');
29
30
  var index_js = require('@modelcontextprotocol/sdk/client/index.js');
30
31
  var stdio_js = require('@modelcontextprotocol/sdk/client/stdio.js');
@@ -63,6 +64,7 @@ function _interopNamespace(e) {
63
64
 
64
65
  var Anthropic__default = /*#__PURE__*/_interopDefault(Anthropic);
65
66
  var OpenAI__default = /*#__PURE__*/_interopDefault(OpenAI);
67
+ var dns__default = /*#__PURE__*/_interopDefault(dns);
66
68
  var React2__default = /*#__PURE__*/_interopDefault(React2);
67
69
  var chalk9__default = /*#__PURE__*/_interopDefault(chalk9);
68
70
  var dotenv__default = /*#__PURE__*/_interopDefault(dotenv);
@@ -75,7 +77,7 @@ var _ignoreModule__namespace = /*#__PURE__*/_interopNamespace(_ignoreModule);
75
77
  var Database__default = /*#__PURE__*/_interopDefault(Database);
76
78
  var EventEmitter__default = /*#__PURE__*/_interopDefault(EventEmitter);
77
79
  var PDFDocument__default = /*#__PURE__*/_interopDefault(PDFDocument);
78
- var dns__default = /*#__PURE__*/_interopDefault(dns);
80
+ var dns2__default = /*#__PURE__*/_interopDefault(dns2);
79
81
  var net__default = /*#__PURE__*/_interopDefault(net);
80
82
  var Spinner__default = /*#__PURE__*/_interopDefault(Spinner);
81
83
  var ora__default = /*#__PURE__*/_interopDefault(ora);
@@ -101,7 +103,7 @@ var __export = (target, all) => {
101
103
  var CASCADE_VERSION, CASCADE_CONFIG_FILE, CASCADE_DB_FILE, CASCADE_DASHBOARD_SECRET_FILE, GLOBAL_CONFIG_DIR, GLOBAL_DB_FILE, GLOBAL_KEYSTORE_FILE, GLOBAL_RUNTIME_DB_FILE, DEFAULT_DASHBOARD_PORT, DEFAULT_CONTEXT_LIMIT, DEFAULT_AUTO_SUMMARIZE_AT, MODELS, T1_MODEL_PRIORITY, T2_MODEL_PRIORITY, T3_MODEL_PRIORITY, VISION_MODEL_PRIORITY, COMPLEXITY_T2_COUNT, THEME_NAMES, DEFAULT_THEME, OLLAMA_BASE_URL, LM_STUDIO_BASE_URL, AZURE_BASE_URL_TEMPLATE, TOOL_NAMES, DEFAULT_APPROVAL_REQUIRED;
102
104
  var init_constants = __esm({
103
105
  "src/constants.ts"() {
104
- CASCADE_VERSION = "0.12.14";
106
+ CASCADE_VERSION = "0.12.16";
105
107
  CASCADE_CONFIG_FILE = ".cascade/config.json";
106
108
  CASCADE_DB_FILE = ".cascade/memory.db";
107
109
  CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
@@ -1207,6 +1209,18 @@ var init_gemini = __esm({
1207
1209
  };
1208
1210
  }
1209
1211
  });
1212
+ function preferIpv4Host(url) {
1213
+ if (!url) return url;
1214
+ return url.replace(/^(https?:\/\/)localhost(?=[:/]|$)/i, "$1127.0.0.1");
1215
+ }
1216
+ var init_net = __esm({
1217
+ "src/utils/net.ts"() {
1218
+ try {
1219
+ dns__default.default.setDefaultResultOrder("ipv4first");
1220
+ } catch {
1221
+ }
1222
+ }
1223
+ });
1210
1224
 
1211
1225
  // src/providers/ollama.ts
1212
1226
  var ollama_exports = {};
@@ -1221,6 +1235,7 @@ var TOOL_CAPABLE_FAMILIES, OllamaProvider;
1221
1235
  var init_ollama = __esm({
1222
1236
  "src/providers/ollama.ts"() {
1223
1237
  init_constants();
1238
+ init_net();
1224
1239
  init_base();
1225
1240
  TOOL_CAPABLE_FAMILIES = [
1226
1241
  "llama3.1",
@@ -1238,7 +1253,7 @@ var init_ollama = __esm({
1238
1253
  baseUrl;
1239
1254
  constructor(config, model) {
1240
1255
  super(config, model);
1241
- this.baseUrl = config.baseUrl ?? OLLAMA_BASE_URL;
1256
+ this.baseUrl = preferIpv4Host(config.baseUrl ?? OLLAMA_BASE_URL);
1242
1257
  }
1243
1258
  async generate(options) {
1244
1259
  const chunks = [];
@@ -1435,37 +1450,61 @@ var OpenAICompatibleProvider;
1435
1450
  var init_openai_compatible = __esm({
1436
1451
  "src/providers/openai-compatible.ts"() {
1437
1452
  init_openai();
1453
+ init_net();
1438
1454
  OpenAICompatibleProvider = class extends OpenAIProvider {
1439
1455
  constructor(config, model) {
1440
1456
  super(config, model);
1441
1457
  this.client = new OpenAI__default.default({
1442
1458
  apiKey: config.apiKey ?? "not-required",
1443
- baseURL: config.baseUrl
1459
+ baseURL: preferIpv4Host(config.baseUrl)
1444
1460
  });
1445
1461
  }
1462
+ modelsUrl() {
1463
+ const base = (preferIpv4Host(this.config.baseUrl) ?? "").replace(/\/+$/, "");
1464
+ return base + "/models";
1465
+ }
1466
+ authHeaders() {
1467
+ const h = { Accept: "application/json" };
1468
+ if (this.config.apiKey) h["Authorization"] = `Bearer ${this.config.apiKey}`;
1469
+ return h;
1470
+ }
1471
+ // Discover models with a tolerant direct GET instead of the OpenAI SDK's typed
1472
+ // `models.list()`. Local servers (llama.cpp / LM Studio / vLLM) return
1473
+ // non-standard `/v1/models` payloads — an extra `models` array, filesystem
1474
+ // path ids (`C:\…\model.gguf`) — that can make the SDK's typed pagination
1475
+ // throw, which previously surfaced as a misleading "endpoint unreachable".
1476
+ // A plain fetch + lenient parse is robust and reports the real HTTP error.
1446
1477
  async listModels() {
1447
- try {
1448
- const response = await this.client.models.list();
1449
- return response.data.map((m) => ({
1450
- id: m.id,
1451
- name: m.id,
1452
- provider: "openai-compatible",
1453
- contextWindow: 32e3,
1454
- isVisionCapable: false,
1455
- inputCostPer1kTokens: 0,
1456
- outputCostPer1kTokens: 0,
1457
- maxOutputTokens: 4e3,
1458
- supportsStreaming: true,
1459
- isLocal: false
1460
- }));
1461
- } catch {
1462
- return [this.model];
1463
- }
1478
+ const res = await fetch(this.modelsUrl(), { headers: this.authHeaders() });
1479
+ if (!res.ok) throw new Error(`models endpoint ${this.modelsUrl()} returned HTTP ${res.status}`);
1480
+ const body = await res.json();
1481
+ const raw = Array.isArray(body?.data) ? body.data : Array.isArray(body?.models) ? body.models : [];
1482
+ const ids = raw.map((m) => {
1483
+ if (m && typeof m === "object") {
1484
+ const o = m;
1485
+ const v = o["id"] ?? o["name"] ?? o["model"];
1486
+ return typeof v === "string" ? v : void 0;
1487
+ }
1488
+ return typeof m === "string" ? m : void 0;
1489
+ }).filter((x) => typeof x === "string" && x.length > 0);
1490
+ if (ids.length === 0) return [this.model];
1491
+ return ids.map((id) => ({
1492
+ id,
1493
+ name: id,
1494
+ provider: "openai-compatible",
1495
+ contextWindow: 32e3,
1496
+ isVisionCapable: false,
1497
+ inputCostPer1kTokens: 0,
1498
+ outputCostPer1kTokens: 0,
1499
+ maxOutputTokens: 4e3,
1500
+ supportsStreaming: true,
1501
+ isLocal: false
1502
+ }));
1464
1503
  }
1465
1504
  async isAvailable() {
1466
1505
  try {
1467
- await this.client.models.list();
1468
- return true;
1506
+ const res = await fetch(this.modelsUrl(), { headers: this.authHeaders() });
1507
+ return res.ok;
1469
1508
  } catch {
1470
1509
  return false;
1471
1510
  }
@@ -4556,7 +4595,8 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
4556
4595
  for (const m of models) {
4557
4596
  this.selector.addDynamicModel(m);
4558
4597
  }
4559
- } catch {
4598
+ } catch (err) {
4599
+ console.warn("[router] OpenAI-compatible model discovery failed:", err instanceof Error ? err.message : err);
4560
4600
  }
4561
4601
  }
4562
4602
  ensureProvider(model, configs) {
@@ -8943,7 +8983,7 @@ async function assertPublicUrl(rawUrl) {
8943
8983
  }
8944
8984
  let addresses;
8945
8985
  try {
8946
- const records = await dns__default.default.lookup(host, { all: true });
8986
+ const records = await dns2__default.default.lookup(host, { all: true });
8947
8987
  addresses = records.map((r) => r.address);
8948
8988
  } catch {
8949
8989
  throw new SsrfBlockedError(`Could not resolve host "${host}".`);