open-agents-ai 0.65.0 → 0.67.0
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 +676 -56
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -19129,14 +19129,14 @@ var require_extension = __commonJS({
|
|
|
19129
19129
|
var require_websocket = __commonJS({
|
|
19130
19130
|
"node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket.js"(exports, module) {
|
|
19131
19131
|
"use strict";
|
|
19132
|
-
var
|
|
19132
|
+
var EventEmitter5 = __require("events");
|
|
19133
19133
|
var https = __require("https");
|
|
19134
19134
|
var http = __require("http");
|
|
19135
19135
|
var net = __require("net");
|
|
19136
19136
|
var tls = __require("tls");
|
|
19137
|
-
var { randomBytes:
|
|
19137
|
+
var { randomBytes: randomBytes6, createHash: createHash2 } = __require("crypto");
|
|
19138
19138
|
var { Duplex, Readable } = __require("stream");
|
|
19139
|
-
var { URL:
|
|
19139
|
+
var { URL: URL3 } = __require("url");
|
|
19140
19140
|
var PerMessageDeflate = require_permessage_deflate();
|
|
19141
19141
|
var Receiver2 = require_receiver();
|
|
19142
19142
|
var Sender2 = require_sender();
|
|
@@ -19161,7 +19161,7 @@ var require_websocket = __commonJS({
|
|
|
19161
19161
|
var protocolVersions = [8, 13];
|
|
19162
19162
|
var readyStates = ["CONNECTING", "OPEN", "CLOSING", "CLOSED"];
|
|
19163
19163
|
var subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
|
|
19164
|
-
var WebSocket2 = class _WebSocket extends
|
|
19164
|
+
var WebSocket2 = class _WebSocket extends EventEmitter5 {
|
|
19165
19165
|
/**
|
|
19166
19166
|
* Create a new `WebSocket`.
|
|
19167
19167
|
*
|
|
@@ -19629,11 +19629,11 @@ var require_websocket = __commonJS({
|
|
|
19629
19629
|
);
|
|
19630
19630
|
}
|
|
19631
19631
|
let parsedUrl;
|
|
19632
|
-
if (address instanceof
|
|
19632
|
+
if (address instanceof URL3) {
|
|
19633
19633
|
parsedUrl = address;
|
|
19634
19634
|
} else {
|
|
19635
19635
|
try {
|
|
19636
|
-
parsedUrl = new
|
|
19636
|
+
parsedUrl = new URL3(address);
|
|
19637
19637
|
} catch (e) {
|
|
19638
19638
|
throw new SyntaxError(`Invalid URL: ${address}`);
|
|
19639
19639
|
}
|
|
@@ -19664,7 +19664,7 @@ var require_websocket = __commonJS({
|
|
|
19664
19664
|
}
|
|
19665
19665
|
}
|
|
19666
19666
|
const defaultPort = isSecure ? 443 : 80;
|
|
19667
|
-
const key =
|
|
19667
|
+
const key = randomBytes6(16).toString("base64");
|
|
19668
19668
|
const request = isSecure ? https.request : http.request;
|
|
19669
19669
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
19670
19670
|
let perMessageDeflate;
|
|
@@ -19770,7 +19770,7 @@ var require_websocket = __commonJS({
|
|
|
19770
19770
|
req.abort();
|
|
19771
19771
|
let addr;
|
|
19772
19772
|
try {
|
|
19773
|
-
addr = new
|
|
19773
|
+
addr = new URL3(location, address);
|
|
19774
19774
|
} catch (e) {
|
|
19775
19775
|
const err = new SyntaxError(`Invalid URL: ${location}`);
|
|
19776
19776
|
emitErrorAndClose(websocket, err);
|
|
@@ -20158,7 +20158,7 @@ var require_subprotocol = __commonJS({
|
|
|
20158
20158
|
var require_websocket_server = __commonJS({
|
|
20159
20159
|
"node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket-server.js"(exports, module) {
|
|
20160
20160
|
"use strict";
|
|
20161
|
-
var
|
|
20161
|
+
var EventEmitter5 = __require("events");
|
|
20162
20162
|
var http = __require("http");
|
|
20163
20163
|
var { Duplex } = __require("stream");
|
|
20164
20164
|
var { createHash: createHash2 } = __require("crypto");
|
|
@@ -20171,7 +20171,7 @@ var require_websocket_server = __commonJS({
|
|
|
20171
20171
|
var RUNNING = 0;
|
|
20172
20172
|
var CLOSING = 1;
|
|
20173
20173
|
var CLOSED = 2;
|
|
20174
|
-
var WebSocketServer2 = class extends
|
|
20174
|
+
var WebSocketServer2 = class extends EventEmitter5 {
|
|
20175
20175
|
/**
|
|
20176
20176
|
* Create a `WebSocketServer` instance.
|
|
20177
20177
|
*
|
|
@@ -22173,8 +22173,278 @@ var init_voice_session = __esm({
|
|
|
22173
22173
|
}
|
|
22174
22174
|
});
|
|
22175
22175
|
|
|
22176
|
-
// packages/cli/dist/tui/
|
|
22176
|
+
// packages/cli/dist/tui/expose.js
|
|
22177
|
+
import { createServer as createServer2, request as httpRequest } from "node:http";
|
|
22178
|
+
import { spawn as spawn11 } from "node:child_process";
|
|
22177
22179
|
import { EventEmitter as EventEmitter3 } from "node:events";
|
|
22180
|
+
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
22181
|
+
import { URL as URL2 } from "node:url";
|
|
22182
|
+
var DEFAULT_TARGETS, ExposeGateway;
|
|
22183
|
+
var init_expose = __esm({
|
|
22184
|
+
"packages/cli/dist/tui/expose.js"() {
|
|
22185
|
+
"use strict";
|
|
22186
|
+
init_render();
|
|
22187
|
+
DEFAULT_TARGETS = {
|
|
22188
|
+
ollama: "http://127.0.0.1:11434",
|
|
22189
|
+
vllm: "http://127.0.0.1:8000",
|
|
22190
|
+
llvm: "http://127.0.0.1:8080",
|
|
22191
|
+
custom: "http://127.0.0.1:11434"
|
|
22192
|
+
};
|
|
22193
|
+
ExposeGateway = class extends EventEmitter3 {
|
|
22194
|
+
options;
|
|
22195
|
+
server = null;
|
|
22196
|
+
cloudflaredProcess = null;
|
|
22197
|
+
_tunnelUrl = null;
|
|
22198
|
+
_authKey;
|
|
22199
|
+
_targetUrl;
|
|
22200
|
+
_kind;
|
|
22201
|
+
_stats = {
|
|
22202
|
+
status: "standby",
|
|
22203
|
+
totalRequests: 0,
|
|
22204
|
+
activeConnections: 0,
|
|
22205
|
+
errors: 0,
|
|
22206
|
+
modelUsage: /* @__PURE__ */ new Map()
|
|
22207
|
+
};
|
|
22208
|
+
get tunnelUrl() {
|
|
22209
|
+
return this._tunnelUrl;
|
|
22210
|
+
}
|
|
22211
|
+
get authKey() {
|
|
22212
|
+
return this._authKey;
|
|
22213
|
+
}
|
|
22214
|
+
get stats() {
|
|
22215
|
+
return this._stats;
|
|
22216
|
+
}
|
|
22217
|
+
get kind() {
|
|
22218
|
+
return this._kind;
|
|
22219
|
+
}
|
|
22220
|
+
get isActive() {
|
|
22221
|
+
return this._stats.status === "active";
|
|
22222
|
+
}
|
|
22223
|
+
constructor(options) {
|
|
22224
|
+
super();
|
|
22225
|
+
this.options = options;
|
|
22226
|
+
this._kind = options.kind;
|
|
22227
|
+
this._targetUrl = options.targetUrl ?? DEFAULT_TARGETS[options.kind];
|
|
22228
|
+
if (options.authKey === void 0 || options.authKey === "") {
|
|
22229
|
+
this._authKey = randomBytes5(24).toString("base64url");
|
|
22230
|
+
} else {
|
|
22231
|
+
this._authKey = options.authKey;
|
|
22232
|
+
}
|
|
22233
|
+
}
|
|
22234
|
+
// ── Lifecycle ───────────────────────────────────────────────────────────
|
|
22235
|
+
async start() {
|
|
22236
|
+
if (this.server)
|
|
22237
|
+
throw new Error("Gateway already running");
|
|
22238
|
+
const port = await this.findFreePort();
|
|
22239
|
+
this.server = this.createProxyServer(port);
|
|
22240
|
+
await new Promise((resolve28, reject) => {
|
|
22241
|
+
this.server.listen(port, "127.0.0.1", () => resolve28());
|
|
22242
|
+
this.server.on("error", reject);
|
|
22243
|
+
});
|
|
22244
|
+
this._tunnelUrl = await this.startCloudflared(port);
|
|
22245
|
+
this._stats.status = "active";
|
|
22246
|
+
this.emitStats();
|
|
22247
|
+
return this._tunnelUrl;
|
|
22248
|
+
}
|
|
22249
|
+
async stop() {
|
|
22250
|
+
if (this.cloudflaredProcess) {
|
|
22251
|
+
this.cloudflaredProcess.kill("SIGTERM");
|
|
22252
|
+
this.cloudflaredProcess = null;
|
|
22253
|
+
}
|
|
22254
|
+
if (this.server) {
|
|
22255
|
+
await new Promise((resolve28) => {
|
|
22256
|
+
this.server.close(() => resolve28());
|
|
22257
|
+
});
|
|
22258
|
+
this.server = null;
|
|
22259
|
+
}
|
|
22260
|
+
this._tunnelUrl = null;
|
|
22261
|
+
this._stats.status = "standby";
|
|
22262
|
+
this._stats.activeConnections = 0;
|
|
22263
|
+
this.emitStats();
|
|
22264
|
+
}
|
|
22265
|
+
// ── Proxy server ────────────────────────────────────────────────────────
|
|
22266
|
+
createProxyServer(localPort) {
|
|
22267
|
+
const target = new URL2(this._targetUrl);
|
|
22268
|
+
return createServer2((req, res) => {
|
|
22269
|
+
const authHeader = req.headers.authorization;
|
|
22270
|
+
const url = new URL2(req.url ?? "/", `http://127.0.0.1:${localPort}`);
|
|
22271
|
+
const queryKey = url.searchParams.get("key");
|
|
22272
|
+
const providedKey = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : queryKey;
|
|
22273
|
+
if (providedKey !== this._authKey) {
|
|
22274
|
+
this._stats.errors++;
|
|
22275
|
+
res.writeHead(401, { "Content-Type": "application/json" });
|
|
22276
|
+
res.end(JSON.stringify({ error: "Unauthorized \u2014 provide Bearer token or ?key= parameter" }));
|
|
22277
|
+
this.emitStats();
|
|
22278
|
+
return;
|
|
22279
|
+
}
|
|
22280
|
+
this._stats.totalRequests++;
|
|
22281
|
+
this._stats.activeConnections++;
|
|
22282
|
+
this.emitStats();
|
|
22283
|
+
this.extractModelFromRequest(req);
|
|
22284
|
+
url.searchParams.delete("key");
|
|
22285
|
+
const forwardPath = url.pathname + url.search;
|
|
22286
|
+
const proxyReq = httpRequest({
|
|
22287
|
+
hostname: target.hostname,
|
|
22288
|
+
port: target.port || (target.protocol === "https:" ? 443 : 80),
|
|
22289
|
+
path: forwardPath,
|
|
22290
|
+
method: req.method,
|
|
22291
|
+
headers: {
|
|
22292
|
+
...req.headers,
|
|
22293
|
+
host: target.host,
|
|
22294
|
+
// Remove auth header — the backend doesn't need it
|
|
22295
|
+
authorization: void 0
|
|
22296
|
+
}
|
|
22297
|
+
}, (proxyRes) => {
|
|
22298
|
+
res.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers);
|
|
22299
|
+
proxyRes.pipe(res);
|
|
22300
|
+
proxyRes.on("end", () => {
|
|
22301
|
+
this._stats.activeConnections = Math.max(0, this._stats.activeConnections - 1);
|
|
22302
|
+
this.emitStats();
|
|
22303
|
+
});
|
|
22304
|
+
});
|
|
22305
|
+
proxyReq.on("error", (err) => {
|
|
22306
|
+
this._stats.errors++;
|
|
22307
|
+
this._stats.activeConnections = Math.max(0, this._stats.activeConnections - 1);
|
|
22308
|
+
if (!res.headersSent) {
|
|
22309
|
+
res.writeHead(502, { "Content-Type": "application/json" });
|
|
22310
|
+
res.end(JSON.stringify({ error: `Backend unreachable: ${err.message}` }));
|
|
22311
|
+
}
|
|
22312
|
+
this._stats.status = "error";
|
|
22313
|
+
this.emitStats();
|
|
22314
|
+
setTimeout(() => {
|
|
22315
|
+
if (this.server && this._stats.status === "error") {
|
|
22316
|
+
this._stats.status = "active";
|
|
22317
|
+
this.emitStats();
|
|
22318
|
+
}
|
|
22319
|
+
}, 5e3);
|
|
22320
|
+
});
|
|
22321
|
+
req.pipe(proxyReq);
|
|
22322
|
+
});
|
|
22323
|
+
}
|
|
22324
|
+
/** Best-effort model name extraction from JSON body for usage tracking */
|
|
22325
|
+
extractModelFromRequest(req) {
|
|
22326
|
+
if (req.method !== "POST")
|
|
22327
|
+
return;
|
|
22328
|
+
const chunks = [];
|
|
22329
|
+
const origEmit = req.emit.bind(req);
|
|
22330
|
+
let peeked = false;
|
|
22331
|
+
const stats = this._stats;
|
|
22332
|
+
req.emit = function(event, ...args) {
|
|
22333
|
+
if (event === "data" && !peeked) {
|
|
22334
|
+
peeked = true;
|
|
22335
|
+
try {
|
|
22336
|
+
const chunk = args[0];
|
|
22337
|
+
chunks.push(chunk);
|
|
22338
|
+
const text = Buffer.concat(chunks).toString();
|
|
22339
|
+
const modelMatch = text.match(/"model"\s*:\s*"([^"]+)"/);
|
|
22340
|
+
if (modelMatch) {
|
|
22341
|
+
const model = modelMatch[1];
|
|
22342
|
+
const count = stats.modelUsage.get(model) ?? 0;
|
|
22343
|
+
stats.modelUsage.set(model, count + 1);
|
|
22344
|
+
}
|
|
22345
|
+
} catch {
|
|
22346
|
+
}
|
|
22347
|
+
}
|
|
22348
|
+
return origEmit(event, ...args);
|
|
22349
|
+
};
|
|
22350
|
+
}
|
|
22351
|
+
// ── Cloudflared ─────────────────────────────────────────────────────────
|
|
22352
|
+
startCloudflared(port) {
|
|
22353
|
+
return new Promise((resolve28, reject) => {
|
|
22354
|
+
const timeout = setTimeout(() => {
|
|
22355
|
+
reject(new Error("Cloudflared tunnel start timeout (30s)"));
|
|
22356
|
+
}, 3e4);
|
|
22357
|
+
this.cloudflaredProcess = spawn11("cloudflared", [
|
|
22358
|
+
"tunnel",
|
|
22359
|
+
"--url",
|
|
22360
|
+
`http://127.0.0.1:${port}`
|
|
22361
|
+
], {
|
|
22362
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
22363
|
+
});
|
|
22364
|
+
let urlFound = false;
|
|
22365
|
+
const handleOutput = (data) => {
|
|
22366
|
+
const text = data.toString();
|
|
22367
|
+
const urlMatch = text.match(/https:\/\/[a-z0-9-]+\.trycloudflare\.com/);
|
|
22368
|
+
if (urlMatch && !urlFound) {
|
|
22369
|
+
urlFound = true;
|
|
22370
|
+
clearTimeout(timeout);
|
|
22371
|
+
resolve28(urlMatch[0]);
|
|
22372
|
+
}
|
|
22373
|
+
};
|
|
22374
|
+
this.cloudflaredProcess.stdout?.on("data", handleOutput);
|
|
22375
|
+
this.cloudflaredProcess.stderr?.on("data", handleOutput);
|
|
22376
|
+
this.cloudflaredProcess.on("error", (err) => {
|
|
22377
|
+
clearTimeout(timeout);
|
|
22378
|
+
reject(new Error(`Failed to start cloudflared: ${err.message}`));
|
|
22379
|
+
});
|
|
22380
|
+
this.cloudflaredProcess.on("close", (code) => {
|
|
22381
|
+
if (!urlFound) {
|
|
22382
|
+
clearTimeout(timeout);
|
|
22383
|
+
reject(new Error(`Cloudflared exited with code ${code} before providing URL`));
|
|
22384
|
+
}
|
|
22385
|
+
if (this._stats.status === "active") {
|
|
22386
|
+
this._stats.status = "error";
|
|
22387
|
+
this.emitStats();
|
|
22388
|
+
}
|
|
22389
|
+
});
|
|
22390
|
+
});
|
|
22391
|
+
}
|
|
22392
|
+
// ── Helpers ─────────────────────────────────────────────────────────────
|
|
22393
|
+
findFreePort() {
|
|
22394
|
+
return new Promise((resolve28, reject) => {
|
|
22395
|
+
const srv = createServer2();
|
|
22396
|
+
srv.listen(0, "127.0.0.1", () => {
|
|
22397
|
+
const addr = srv.address();
|
|
22398
|
+
if (addr && typeof addr === "object") {
|
|
22399
|
+
const port = addr.port;
|
|
22400
|
+
srv.close(() => resolve28(port));
|
|
22401
|
+
} else {
|
|
22402
|
+
srv.close(() => reject(new Error("Could not find free port")));
|
|
22403
|
+
}
|
|
22404
|
+
});
|
|
22405
|
+
srv.on("error", reject);
|
|
22406
|
+
});
|
|
22407
|
+
}
|
|
22408
|
+
emitStats() {
|
|
22409
|
+
this.emit("stats", { ...this._stats, modelUsage: new Map(this._stats.modelUsage) });
|
|
22410
|
+
}
|
|
22411
|
+
/** Format connection info for display */
|
|
22412
|
+
formatConnectionInfo() {
|
|
22413
|
+
const lines = [];
|
|
22414
|
+
lines.push(` ${c2.green("\u25CF")} Expose gateway active`);
|
|
22415
|
+
lines.push(` ${c2.cyan("Backend".padEnd(12))} ${this._kind} (${this._targetUrl})`);
|
|
22416
|
+
lines.push(` ${c2.cyan("Tunnel".padEnd(12))} ${c2.bold(this._tunnelUrl ?? "starting...")}`);
|
|
22417
|
+
lines.push(` ${c2.cyan("Auth Key".padEnd(12))} ${c2.bold(this._authKey)}`);
|
|
22418
|
+
lines.push("");
|
|
22419
|
+
lines.push(` ${c2.dim("Remote users connect with:")}`);
|
|
22420
|
+
lines.push(` ${c2.bold(`/endpoint ${this._tunnelUrl ?? "<tunnel-url>"} --auth ${this._authKey}`)}`);
|
|
22421
|
+
lines.push("");
|
|
22422
|
+
lines.push(` ${c2.yellow("!")} ${c2.dim("Keep this key secure \u2014 it grants full access to your models.")}`);
|
|
22423
|
+
return lines.join("\n");
|
|
22424
|
+
}
|
|
22425
|
+
/** Format usage stats for display */
|
|
22426
|
+
formatStats() {
|
|
22427
|
+
const s = this._stats;
|
|
22428
|
+
const lines = [];
|
|
22429
|
+
lines.push(` ${c2.bold("Expose Gateway Stats")}`);
|
|
22430
|
+
lines.push(` ${c2.cyan("Status".padEnd(16))} ${s.status === "active" ? c2.green("active") : s.status === "error" ? c2.red("error") : c2.yellow("standby")}`);
|
|
22431
|
+
lines.push(` ${c2.cyan("Total requests".padEnd(16))} ${s.totalRequests}`);
|
|
22432
|
+
lines.push(` ${c2.cyan("Active conns".padEnd(16))} ${s.activeConnections}`);
|
|
22433
|
+
lines.push(` ${c2.cyan("Errors".padEnd(16))} ${s.errors}`);
|
|
22434
|
+
if (s.modelUsage.size > 0) {
|
|
22435
|
+
lines.push(` ${c2.cyan("Models used:")}`);
|
|
22436
|
+
for (const [model, count] of s.modelUsage) {
|
|
22437
|
+
lines.push(` ${model}: ${count} requests`);
|
|
22438
|
+
}
|
|
22439
|
+
}
|
|
22440
|
+
return lines.join("\n");
|
|
22441
|
+
}
|
|
22442
|
+
};
|
|
22443
|
+
}
|
|
22444
|
+
});
|
|
22445
|
+
|
|
22446
|
+
// packages/cli/dist/tui/call-agent.js
|
|
22447
|
+
import { EventEmitter as EventEmitter4 } from "node:events";
|
|
22178
22448
|
import crypto2 from "node:crypto";
|
|
22179
22449
|
function adaptTool(tool) {
|
|
22180
22450
|
return {
|
|
@@ -22234,7 +22504,7 @@ var init_call_agent = __esm({
|
|
|
22234
22504
|
}
|
|
22235
22505
|
};
|
|
22236
22506
|
_globalFeed = null;
|
|
22237
|
-
CallSubAgent = class extends
|
|
22507
|
+
CallSubAgent = class extends EventEmitter4 {
|
|
22238
22508
|
tier;
|
|
22239
22509
|
clientId;
|
|
22240
22510
|
runner = null;
|
|
@@ -23269,7 +23539,7 @@ var init_oa_directory = __esm({
|
|
|
23269
23539
|
|
|
23270
23540
|
// packages/cli/dist/tui/setup.js
|
|
23271
23541
|
import * as readline from "node:readline";
|
|
23272
|
-
import { execSync as execSync20, spawn as
|
|
23542
|
+
import { execSync as execSync20, spawn as spawn12 } from "node:child_process";
|
|
23273
23543
|
import { existsSync as existsSync22, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8 } from "node:fs";
|
|
23274
23544
|
import { join as join31 } from "node:path";
|
|
23275
23545
|
import { homedir as homedir10, platform } from "node:os";
|
|
@@ -23755,7 +24025,7 @@ async function doSetup(config, rl) {
|
|
|
23755
24025
|
${c2.cyan("\u25CF")} Starting ollama serve in background...
|
|
23756
24026
|
`);
|
|
23757
24027
|
try {
|
|
23758
|
-
const child =
|
|
24028
|
+
const child = spawn12("ollama", ["serve"], { stdio: "ignore", detached: true });
|
|
23759
24029
|
child.unref();
|
|
23760
24030
|
await new Promise((resolve28) => setTimeout(resolve28, 3e3));
|
|
23761
24031
|
try {
|
|
@@ -23783,7 +24053,7 @@ async function doSetup(config, rl) {
|
|
|
23783
24053
|
${c2.cyan("\u25CF")} Starting ollama serve...
|
|
23784
24054
|
`);
|
|
23785
24055
|
try {
|
|
23786
|
-
const child =
|
|
24056
|
+
const child = spawn12("ollama", ["serve"], { stdio: "ignore", detached: true });
|
|
23787
24057
|
child.unref();
|
|
23788
24058
|
await new Promise((resolve28) => setTimeout(resolve28, 3e3));
|
|
23789
24059
|
try {
|
|
@@ -24897,6 +25167,71 @@ async function handleSlashCommand(input, ctx) {
|
|
|
24897
25167
|
}
|
|
24898
25168
|
return "handled";
|
|
24899
25169
|
}
|
|
25170
|
+
case "expose": {
|
|
25171
|
+
if (!ctx.exposeStart) {
|
|
25172
|
+
renderWarning("Expose gateway not available in this context.");
|
|
25173
|
+
return "handled";
|
|
25174
|
+
}
|
|
25175
|
+
if (arg === "stop" || arg === "off") {
|
|
25176
|
+
if (ctx.isExposeActive?.()) {
|
|
25177
|
+
await ctx.exposeStop?.();
|
|
25178
|
+
renderInfo("Expose gateway stopped.");
|
|
25179
|
+
} else {
|
|
25180
|
+
renderWarning("No active expose gateway.");
|
|
25181
|
+
}
|
|
25182
|
+
return "handled";
|
|
25183
|
+
}
|
|
25184
|
+
if (arg === "status") {
|
|
25185
|
+
const info = ctx.getExposeInfo?.();
|
|
25186
|
+
if (info) {
|
|
25187
|
+
process.stdout.write("\n" + info.stats + "\n\n");
|
|
25188
|
+
} else {
|
|
25189
|
+
renderWarning("No active expose gateway.");
|
|
25190
|
+
}
|
|
25191
|
+
return "handled";
|
|
25192
|
+
}
|
|
25193
|
+
if (!arg) {
|
|
25194
|
+
const info = ctx.getExposeInfo?.();
|
|
25195
|
+
if (info && ctx.isExposeActive?.()) {
|
|
25196
|
+
process.stdout.write("\n" + info.stats + "\n\n");
|
|
25197
|
+
} else {
|
|
25198
|
+
renderInfo("Usage: /expose <backend> [--key <auth-key>]");
|
|
25199
|
+
renderInfo(" /expose ollama Expose local Ollama (11434)");
|
|
25200
|
+
renderInfo(" /expose vllm Expose local vLLM (8000)");
|
|
25201
|
+
renderInfo(" /expose llvm Expose local LLVM (8080)");
|
|
25202
|
+
renderInfo(" /expose host:port Expose custom address");
|
|
25203
|
+
renderInfo(" /expose stop Stop the gateway");
|
|
25204
|
+
renderInfo(" /expose status Show usage stats");
|
|
25205
|
+
renderInfo(" --key <key> Use specific auth key");
|
|
25206
|
+
renderInfo(" --key Auto-generate auth key");
|
|
25207
|
+
}
|
|
25208
|
+
return "handled";
|
|
25209
|
+
}
|
|
25210
|
+
if (ctx.isExposeActive?.()) {
|
|
25211
|
+
await ctx.exposeStop?.();
|
|
25212
|
+
}
|
|
25213
|
+
const exposeParts = arg.split(/\s+/);
|
|
25214
|
+
const kindOrUrl = exposeParts[0];
|
|
25215
|
+
let exposeAuthKey;
|
|
25216
|
+
const keyIdx = exposeParts.indexOf("--key");
|
|
25217
|
+
if (keyIdx !== -1) {
|
|
25218
|
+
exposeAuthKey = exposeParts[keyIdx + 1] ?? "";
|
|
25219
|
+
}
|
|
25220
|
+
try {
|
|
25221
|
+
const tunnelUrl = await ctx.exposeStart(kindOrUrl, exposeAuthKey);
|
|
25222
|
+
if (tunnelUrl) {
|
|
25223
|
+
const info = ctx.getExposeInfo?.();
|
|
25224
|
+
if (info) {
|
|
25225
|
+
process.stdout.write("\n" + info.stats + "\n\n");
|
|
25226
|
+
}
|
|
25227
|
+
} else {
|
|
25228
|
+
renderError("Failed to start expose gateway.");
|
|
25229
|
+
}
|
|
25230
|
+
} catch (err) {
|
|
25231
|
+
renderError(`Expose error: ${err instanceof Error ? err.message : String(err)}`);
|
|
25232
|
+
}
|
|
25233
|
+
return "handled";
|
|
25234
|
+
}
|
|
24900
25235
|
case "bruteforce":
|
|
24901
25236
|
case "brute": {
|
|
24902
25237
|
const isOn = ctx.bruteForceToggle();
|
|
@@ -25471,14 +25806,39 @@ async function handleUpdate(subcommand, ctx) {
|
|
|
25471
25806
|
async function switchModel(query, ctx, local = false) {
|
|
25472
25807
|
try {
|
|
25473
25808
|
const models = await fetchModels(ctx.config.backendUrl, ctx.config.apiKey);
|
|
25474
|
-
|
|
25809
|
+
let match = findModel(models, query);
|
|
25475
25810
|
if (!match) {
|
|
25476
|
-
|
|
25477
|
-
|
|
25478
|
-
|
|
25479
|
-
|
|
25811
|
+
if (ctx.config.backendType === "ollama") {
|
|
25812
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9._:/-]*$/.test(query)) {
|
|
25813
|
+
renderError(`Invalid model name: "${query}"`);
|
|
25814
|
+
return;
|
|
25815
|
+
}
|
|
25816
|
+
renderInfo(`Model "${query}" not found locally. Pulling from Ollama registry...`);
|
|
25817
|
+
try {
|
|
25818
|
+
pullModelWithAutoUpdate(query);
|
|
25819
|
+
const refreshedModels = await fetchModels(ctx.config.backendUrl, ctx.config.apiKey);
|
|
25820
|
+
match = findModel(refreshedModels, query);
|
|
25821
|
+
if (!match) {
|
|
25822
|
+
renderError(`Model "${query}" was pulled but could not be found.`);
|
|
25823
|
+
return;
|
|
25824
|
+
}
|
|
25825
|
+
} catch {
|
|
25826
|
+
renderError(`Model "${query}" could not be pulled from Ollama registry.`);
|
|
25827
|
+
renderInfo("Check that the model name is correct: https://ollama.com/library");
|
|
25828
|
+
renderInfo("Available local models:");
|
|
25829
|
+
for (const m of models.slice(0, 10)) {
|
|
25830
|
+
renderInfo(` ${m.name}`);
|
|
25831
|
+
}
|
|
25832
|
+
return;
|
|
25833
|
+
}
|
|
25834
|
+
} else {
|
|
25835
|
+
renderError(`Model not found: "${query}"`);
|
|
25836
|
+
renderInfo("Available models:");
|
|
25837
|
+
for (const m of models.slice(0, 10)) {
|
|
25838
|
+
renderInfo(` ${m.name}`);
|
|
25839
|
+
}
|
|
25840
|
+
return;
|
|
25480
25841
|
}
|
|
25481
|
-
return;
|
|
25482
25842
|
}
|
|
25483
25843
|
let finalModel = match.name;
|
|
25484
25844
|
if (ctx.config.backendType === "ollama") {
|
|
@@ -25512,6 +25872,7 @@ async function switchModel(query, ctx, local = false) {
|
|
|
25512
25872
|
const caps = await queryModelCapabilities(ctx.config.backendUrl, finalModel);
|
|
25513
25873
|
ctx.setCapabilities(caps);
|
|
25514
25874
|
}
|
|
25875
|
+
ctx.refreshModelCache?.();
|
|
25515
25876
|
} catch (err) {
|
|
25516
25877
|
renderError(`Failed to switch model: ${err instanceof Error ? err.message : String(err)}`);
|
|
25517
25878
|
}
|
|
@@ -33552,7 +33913,7 @@ var init_status_bar = __esm({
|
|
|
33552
33913
|
return this.toolCalls > 0;
|
|
33553
33914
|
}
|
|
33554
33915
|
};
|
|
33555
|
-
StatusBar = class {
|
|
33916
|
+
StatusBar = class _StatusBar {
|
|
33556
33917
|
metrics = {
|
|
33557
33918
|
promptTokens: 0,
|
|
33558
33919
|
completionTokens: 0,
|
|
@@ -33768,6 +34129,20 @@ var init_status_bar = __esm({
|
|
|
33768
34129
|
if (this.active)
|
|
33769
34130
|
this.renderFooterPreserveCursor();
|
|
33770
34131
|
}
|
|
34132
|
+
/** Expose gateway status — shown after capability emojis */
|
|
34133
|
+
_expose = null;
|
|
34134
|
+
/** Update expose gateway status */
|
|
34135
|
+
setExposeStatus(status) {
|
|
34136
|
+
this._expose = status;
|
|
34137
|
+
if (this.active)
|
|
34138
|
+
this.renderFooterPreserveCursor();
|
|
34139
|
+
}
|
|
34140
|
+
/** Clear expose gateway status */
|
|
34141
|
+
clearExposeStatus() {
|
|
34142
|
+
this._expose = null;
|
|
34143
|
+
if (this.active)
|
|
34144
|
+
this.renderFooterPreserveCursor();
|
|
34145
|
+
}
|
|
33771
34146
|
/** Update token metrics from a token_usage event */
|
|
33772
34147
|
updateMetrics(update) {
|
|
33773
34148
|
if (update.promptTokens !== void 0)
|
|
@@ -33937,68 +34312,239 @@ var init_status_bar = __esm({
|
|
|
33937
34312
|
const pos = this.rowPositions(rows);
|
|
33938
34313
|
process.stdout.write(`\x1B[${pos.inputStartRow};1H\x1B[2K`);
|
|
33939
34314
|
}
|
|
33940
|
-
/**
|
|
34315
|
+
/** Strip ANSI escape codes to measure visible character width */
|
|
34316
|
+
static visWidth(s) {
|
|
34317
|
+
const stripped = s.replace(/\x1B\[[0-9;]*m/g, "");
|
|
34318
|
+
let w = 0;
|
|
34319
|
+
for (const ch of stripped) {
|
|
34320
|
+
const cp = ch.codePointAt(0) ?? 0;
|
|
34321
|
+
if (cp >= 127744 && cp <= 129784)
|
|
34322
|
+
w += 2;
|
|
34323
|
+
else
|
|
34324
|
+
w += 1;
|
|
34325
|
+
}
|
|
34326
|
+
return w;
|
|
34327
|
+
}
|
|
34328
|
+
/** Format number compactly: 1234 → "1.2K", 567 → "567" */
|
|
34329
|
+
static compactNum(n) {
|
|
34330
|
+
if (n < 0)
|
|
34331
|
+
return "0";
|
|
34332
|
+
if (n < 1e3)
|
|
34333
|
+
return String(n);
|
|
34334
|
+
if (n < 1e4)
|
|
34335
|
+
return `${(n / 1e3).toFixed(1)}K`;
|
|
34336
|
+
if (n < 1e6)
|
|
34337
|
+
return `${Math.round(n / 1e3)}K`;
|
|
34338
|
+
return `${(n / 1e6).toFixed(1)}M`;
|
|
34339
|
+
}
|
|
34340
|
+
/** Build the metrics line string with adaptive compaction */
|
|
33941
34341
|
buildMetricsLine() {
|
|
33942
34342
|
const m = this.metrics;
|
|
34343
|
+
const termWidth = getTermWidth();
|
|
33943
34344
|
const pastel2 = (code, s) => `\x1B[38;5;${code}m${s}\x1B[0m`;
|
|
33944
34345
|
const pipe = pastel2(60, " \u2502 ");
|
|
33945
|
-
const
|
|
33946
|
-
const
|
|
34346
|
+
const pipeW = 3;
|
|
34347
|
+
const sections = [];
|
|
34348
|
+
const tokInRaw = m.promptTokens > 0 ? m.promptTokens : Math.max(m.estimatedContextTokens, 0);
|
|
34349
|
+
const tokInPrefix = m.promptTokens > 0 ? "" : "~";
|
|
34350
|
+
const tokInExpanded = pastel2(117, "In: ") + c2.bold(tokInPrefix + tokInRaw.toLocaleString());
|
|
34351
|
+
const tokInCompact = pastel2(117, "I:") + c2.bold(tokInPrefix + _StatusBar.compactNum(tokInRaw));
|
|
34352
|
+
sections.push({
|
|
34353
|
+
expanded: tokInExpanded,
|
|
34354
|
+
compact: tokInCompact,
|
|
34355
|
+
expandedW: 4 + (tokInPrefix + tokInRaw.toLocaleString()).length,
|
|
34356
|
+
compactW: 2 + (tokInPrefix + _StatusBar.compactNum(tokInRaw)).length,
|
|
34357
|
+
empty: false
|
|
34358
|
+
});
|
|
33947
34359
|
const effectiveOut = this.effectiveCompletionTokens;
|
|
33948
|
-
const
|
|
33949
|
-
const
|
|
34360
|
+
const tokOutRaw = effectiveOut > 0 ? effectiveOut : Math.ceil(m.totalTokens > 0 ? m.totalTokens - m.promptTokens : m.estimatedContextTokens * 0.3);
|
|
34361
|
+
const tokOutPrefix = effectiveOut > 0 ? "" : "~";
|
|
34362
|
+
const tokOutExpanded = pastel2(151, "Out: ") + c2.bold(tokOutPrefix + Math.max(0, tokOutRaw).toLocaleString());
|
|
34363
|
+
const tokOutCompact = pastel2(151, "O:") + c2.bold(tokOutPrefix + _StatusBar.compactNum(Math.max(0, tokOutRaw)));
|
|
34364
|
+
sections.push({
|
|
34365
|
+
expanded: tokOutExpanded,
|
|
34366
|
+
compact: tokOutCompact,
|
|
34367
|
+
expandedW: 5 + (tokOutPrefix + Math.max(0, tokOutRaw).toLocaleString()).length,
|
|
34368
|
+
compactW: 2 + (tokOutPrefix + _StatusBar.compactNum(Math.max(0, tokOutRaw))).length,
|
|
34369
|
+
empty: false
|
|
34370
|
+
});
|
|
33950
34371
|
const ctxUsed = m.estimatedContextTokens;
|
|
33951
34372
|
const ctxTotal = m.contextWindowSize;
|
|
33952
|
-
let
|
|
34373
|
+
let ctxExpanded;
|
|
34374
|
+
let ctxCompact;
|
|
34375
|
+
let ctxExpandedW;
|
|
34376
|
+
let ctxCompactW;
|
|
33953
34377
|
if (ctxTotal <= 0) {
|
|
33954
|
-
|
|
34378
|
+
ctxExpanded = pastel2(153, "Ctx: ") + c2.dim("--");
|
|
34379
|
+
ctxCompact = pastel2(153, "Ctx:") + c2.dim("--");
|
|
34380
|
+
ctxExpandedW = 7;
|
|
34381
|
+
ctxCompactW = 6;
|
|
33955
34382
|
} else {
|
|
33956
34383
|
const ctxPct = Math.max(0, Math.min(100, Math.round((1 - ctxUsed / ctxTotal) * 100)));
|
|
33957
34384
|
const ctxColor = ctxPct > 50 ? c2.green : ctxPct > 20 ? c2.yellow : c2.red;
|
|
33958
|
-
|
|
34385
|
+
const ctxFullStr = `${ctxUsed.toLocaleString()}/${ctxTotal.toLocaleString()}`;
|
|
34386
|
+
ctxExpanded = pastel2(153, "Ctx: ") + c2.bold(ctxFullStr) + ` ${ctxColor(`${ctxPct}%`)}`;
|
|
34387
|
+
ctxCompact = pastel2(153, "Ctx:") + ctxColor(`${ctxPct}%`);
|
|
34388
|
+
ctxExpandedW = 5 + ctxFullStr.length + 1 + `${ctxPct}%`.length;
|
|
34389
|
+
ctxCompactW = 4 + `${ctxPct}%`.length;
|
|
33959
34390
|
}
|
|
33960
|
-
|
|
33961
|
-
if (m.hasPricing && m.estimatedCost !== void 0) {
|
|
33962
|
-
const costStr = m.estimatedCost < 0.01 ? `$${m.estimatedCost.toFixed(4)}` : m.estimatedCost < 1 ? `$${m.estimatedCost.toFixed(3)}` : `$${m.estimatedCost.toFixed(2)}`;
|
|
33963
|
-
costLabel = pipe + pastel2(222, "Cost: ") + c2.bold(costStr);
|
|
33964
|
-
}
|
|
33965
|
-
let snrLabel = "";
|
|
34391
|
+
sections.push({ expanded: ctxExpanded, compact: ctxCompact, expandedW: ctxExpandedW, compactW: ctxCompactW, empty: false });
|
|
33966
34392
|
if (this._snr) {
|
|
33967
34393
|
const snrPct = Math.round(this._snr.ratio * 100);
|
|
33968
34394
|
const snrColor = snrPct >= 70 ? c2.green : snrPct >= 40 ? c2.yellow : c2.red;
|
|
33969
34395
|
const dPrimeStr = this._snr.dPrime.toFixed(1);
|
|
33970
34396
|
const capStr = this._snr.capacityWarning ? c2.red(" !CAP") : "";
|
|
33971
|
-
|
|
34397
|
+
const snrExpanded = pastel2(183, "SNR: ") + snrColor(c2.bold(`${snrPct}%`)) + c2.dim(` d'${dPrimeStr}`) + capStr;
|
|
34398
|
+
const snrCompact = snrColor(`${snrPct}%`);
|
|
34399
|
+
const expW = 5 + `${snrPct}%`.length + 3 + dPrimeStr.length + (this._snr.capacityWarning ? 5 : 0);
|
|
34400
|
+
sections.push({ expanded: snrExpanded, compact: snrCompact, expandedW: expW, compactW: `${snrPct}%`.length, empty: false });
|
|
33972
34401
|
}
|
|
33973
|
-
let emotionLabel = "";
|
|
33974
34402
|
if (this._emotion) {
|
|
33975
|
-
|
|
34403
|
+
const emotionExpanded = this._emotion.emoji + " " + pastel2(183, this._emotion.label);
|
|
34404
|
+
const emotionCompact = this._emotion.emoji;
|
|
34405
|
+
sections.push({
|
|
34406
|
+
expanded: emotionExpanded,
|
|
34407
|
+
compact: emotionCompact,
|
|
34408
|
+
expandedW: 2 + 1 + this._emotion.label.length,
|
|
34409
|
+
// emoji(2) + space + label
|
|
34410
|
+
compactW: 2,
|
|
34411
|
+
// just emoji
|
|
34412
|
+
empty: false
|
|
34413
|
+
});
|
|
33976
34414
|
}
|
|
33977
|
-
let speedLabel = "";
|
|
33978
34415
|
if (this._speedTracker.hasData) {
|
|
33979
34416
|
const ratio = this._speedTracker.getSpeedRatio();
|
|
33980
34417
|
if (ratio > 0) {
|
|
33981
34418
|
const ratioStr = ratio >= 10 ? `${Math.round(ratio)}x` : `${ratio.toFixed(1)}x`;
|
|
33982
34419
|
const ratioColor = ratio >= 2 ? c2.green : ratio >= 1 ? c2.yellow : c2.red;
|
|
33983
|
-
|
|
34420
|
+
const speedExpanded = pastel2(218, "Exp: ") + ratioColor(c2.bold(ratioStr));
|
|
34421
|
+
sections.push({
|
|
34422
|
+
expanded: speedExpanded,
|
|
34423
|
+
compact: speedExpanded,
|
|
34424
|
+
expandedW: 5 + ratioStr.length,
|
|
34425
|
+
compactW: 5 + ratioStr.length,
|
|
34426
|
+
empty: false
|
|
34427
|
+
});
|
|
33984
34428
|
}
|
|
33985
34429
|
}
|
|
33986
|
-
|
|
34430
|
+
if (m.hasPricing && m.estimatedCost !== void 0) {
|
|
34431
|
+
const costStr = m.estimatedCost < 0.01 ? `$${m.estimatedCost.toFixed(4)}` : m.estimatedCost < 1 ? `$${m.estimatedCost.toFixed(3)}` : `$${m.estimatedCost.toFixed(2)}`;
|
|
34432
|
+
const costExpanded = pastel2(222, "Cost: ") + c2.bold(costStr);
|
|
34433
|
+
const costCompact = pastel2(222, "") + c2.bold(costStr);
|
|
34434
|
+
sections.push({
|
|
34435
|
+
expanded: costExpanded,
|
|
34436
|
+
compact: costCompact,
|
|
34437
|
+
expandedW: 6 + costStr.length,
|
|
34438
|
+
compactW: costStr.length,
|
|
34439
|
+
empty: false
|
|
34440
|
+
});
|
|
34441
|
+
}
|
|
34442
|
+
if (this._modelName) {
|
|
34443
|
+
const capParts = [];
|
|
34444
|
+
if (this._caps.vision)
|
|
34445
|
+
capParts.push("\u{1F441}");
|
|
34446
|
+
if (this._caps.toolUse)
|
|
34447
|
+
capParts.push("\u{1F527}");
|
|
34448
|
+
if (this._caps.thinking)
|
|
34449
|
+
capParts.push("\u{1F9E0}");
|
|
34450
|
+
const capsStr = capParts.length > 0 ? " " + capParts.join(" ") : "";
|
|
34451
|
+
const capsVisW = capParts.length > 0 ? 1 + capParts.length * 2 + (capParts.length - 1) : 0;
|
|
34452
|
+
const paramMatch = this._modelName.match(/(\d+\.?\d*[bBmM])/);
|
|
34453
|
+
const paramStr = paramMatch ? paramMatch[1] : this._modelName.split(":")[0]?.split("/").pop() ?? this._modelName;
|
|
34454
|
+
const modelExpanded = pastel2(146, this._modelName) + (capsStr ? pastel2(60, " ") + pastel2(183, capParts.join(" ")) : "");
|
|
34455
|
+
const modelCompact = pastel2(146, paramStr) + (capsStr ? pastel2(60, " ") + pastel2(183, capParts.join(" ")) : "");
|
|
34456
|
+
sections.push({
|
|
34457
|
+
expanded: modelExpanded,
|
|
34458
|
+
compact: modelCompact,
|
|
34459
|
+
expandedW: this._modelName.length + capsVisW,
|
|
34460
|
+
compactW: paramStr.length + capsVisW,
|
|
34461
|
+
empty: false
|
|
34462
|
+
});
|
|
34463
|
+
} else {
|
|
34464
|
+
const capParts = [];
|
|
34465
|
+
if (this._caps.vision)
|
|
34466
|
+
capParts.push("\u{1F441}");
|
|
34467
|
+
if (this._caps.toolUse)
|
|
34468
|
+
capParts.push("\u{1F527}");
|
|
34469
|
+
if (this._caps.thinking)
|
|
34470
|
+
capParts.push("\u{1F9E0}");
|
|
34471
|
+
if (capParts.length > 0) {
|
|
34472
|
+
const capsStr = pastel2(183, capParts.join(" "));
|
|
34473
|
+
const capsVisW = capParts.length * 2 + (capParts.length - 1);
|
|
34474
|
+
sections.push({ expanded: capsStr, compact: capsStr, expandedW: capsVisW, compactW: capsVisW, empty: false });
|
|
34475
|
+
}
|
|
34476
|
+
}
|
|
34477
|
+
if (this._expose) {
|
|
34478
|
+
const statusEmoji = this._expose.status === "active" ? "\u{1F7E2}" : this._expose.status === "error" ? "\u{1F534}" : "\u{1F7E0}";
|
|
34479
|
+
const models = Array.from(this._expose.modelUsage.keys());
|
|
34480
|
+
const modelParams = models.map((mdl) => {
|
|
34481
|
+
const pm = mdl.match(/(\d+[bBmM])/);
|
|
34482
|
+
return pm ? pm[1] : mdl.split(":")[0]?.split("/").pop() ?? mdl;
|
|
34483
|
+
});
|
|
34484
|
+
const modelStr = modelParams.join(",");
|
|
34485
|
+
const reqStr = this._expose.totalRequests > 0 ? ` ${this._expose.totalRequests}req` : "";
|
|
34486
|
+
const connStr = this._expose.activeConnections > 0 ? ` ${this._expose.activeConnections}conn` : "";
|
|
34487
|
+
const exposeExpanded = statusEmoji + pastel2(183, (modelStr ? " " + modelStr : "") + reqStr + connStr);
|
|
34488
|
+
const exposeCompact = statusEmoji + (modelStr ? pastel2(183, " " + modelStr) : "");
|
|
34489
|
+
const expFullStr = (modelStr ? " " + modelStr : "") + reqStr + connStr;
|
|
34490
|
+
const expCompStr = modelStr ? " " + modelStr : "";
|
|
34491
|
+
sections.push({
|
|
34492
|
+
expanded: exposeExpanded,
|
|
34493
|
+
compact: exposeCompact,
|
|
34494
|
+
expandedW: 2 + expFullStr.length,
|
|
34495
|
+
// emoji(2) + text
|
|
34496
|
+
compactW: 2 + expCompStr.length,
|
|
34497
|
+
empty: false
|
|
34498
|
+
});
|
|
34499
|
+
}
|
|
33987
34500
|
if (this._recording) {
|
|
33988
34501
|
const dot = this._recBlink ? pastel2(210, "\u25CF") : " ";
|
|
33989
34502
|
const countdown = this._countdown > 0 ? c2.dim(` ${this._countdown}s`) : "";
|
|
33990
|
-
|
|
33991
|
-
|
|
33992
|
-
|
|
33993
|
-
|
|
33994
|
-
|
|
33995
|
-
|
|
33996
|
-
|
|
33997
|
-
|
|
33998
|
-
|
|
33999
|
-
|
|
34000
|
-
|
|
34001
|
-
|
|
34503
|
+
const recStr = dot + pastel2(210, " REC") + countdown;
|
|
34504
|
+
const recW = 1 + 4 + (this._countdown > 0 ? 1 + `${this._countdown}s`.length : 0);
|
|
34505
|
+
sections.push({ expanded: recStr, compact: recStr, expandedW: recW, compactW: recW, empty: false });
|
|
34506
|
+
}
|
|
34507
|
+
const activeIndices = sections.map((s, i) => !s.empty ? i : -1).filter((i) => i >= 0);
|
|
34508
|
+
const isCompact = new Array(sections.length).fill(false);
|
|
34509
|
+
const calcTotalWidth = () => {
|
|
34510
|
+
let w = 1;
|
|
34511
|
+
for (let j = 0; j < activeIndices.length; j++) {
|
|
34512
|
+
const idx = activeIndices[j];
|
|
34513
|
+
const sec = sections[idx];
|
|
34514
|
+
if (j > 0)
|
|
34515
|
+
w += pipeW;
|
|
34516
|
+
w += isCompact[idx] ? sec.compactW : sec.expandedW;
|
|
34517
|
+
}
|
|
34518
|
+
return w;
|
|
34519
|
+
};
|
|
34520
|
+
let totalW = calcTotalWidth();
|
|
34521
|
+
for (let j = activeIndices.length - 1; j >= 0 && totalW > termWidth; j--) {
|
|
34522
|
+
const idx = activeIndices[j];
|
|
34523
|
+
if (!isCompact[idx] && sections[idx].compactW < sections[idx].expandedW) {
|
|
34524
|
+
isCompact[idx] = true;
|
|
34525
|
+
totalW = calcTotalWidth();
|
|
34526
|
+
}
|
|
34527
|
+
}
|
|
34528
|
+
for (let j = 0; j < activeIndices.length; j++) {
|
|
34529
|
+
const idx = activeIndices[j];
|
|
34530
|
+
if (isCompact[idx]) {
|
|
34531
|
+
const savings = sections[idx].expandedW - sections[idx].compactW;
|
|
34532
|
+
if (totalW + savings <= termWidth) {
|
|
34533
|
+
isCompact[idx] = false;
|
|
34534
|
+
totalW += savings;
|
|
34535
|
+
} else {
|
|
34536
|
+
break;
|
|
34537
|
+
}
|
|
34538
|
+
}
|
|
34539
|
+
}
|
|
34540
|
+
let result = " ";
|
|
34541
|
+
for (let j = 0; j < activeIndices.length; j++) {
|
|
34542
|
+
const idx = activeIndices[j];
|
|
34543
|
+
if (j > 0)
|
|
34544
|
+
result += pipe;
|
|
34545
|
+
result += isCompact[idx] ? sections[idx].compact : sections[idx].expanded;
|
|
34546
|
+
}
|
|
34547
|
+
return result;
|
|
34002
34548
|
}
|
|
34003
34549
|
// -------------------------------------------------------------------------
|
|
34004
34550
|
// Private
|
|
@@ -35119,6 +35665,7 @@ async function startInteractive(config, repoPath) {
|
|
|
35119
35665
|
});
|
|
35120
35666
|
const voiceEngine = new VoiceEngine();
|
|
35121
35667
|
let voiceSession = null;
|
|
35668
|
+
let exposeGateway = null;
|
|
35122
35669
|
let adminSessionKey = null;
|
|
35123
35670
|
const callSubAgents = /* @__PURE__ */ new Map();
|
|
35124
35671
|
const streamRenderer = new StreamRenderer();
|
|
@@ -35279,6 +35826,7 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
|
|
|
35279
35826
|
"/pause",
|
|
35280
35827
|
"/stop",
|
|
35281
35828
|
"/resume",
|
|
35829
|
+
"/expose",
|
|
35282
35830
|
"/full-send-bless",
|
|
35283
35831
|
"/bless",
|
|
35284
35832
|
"/telegram",
|
|
@@ -35290,9 +35838,26 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
|
|
|
35290
35838
|
];
|
|
35291
35839
|
const discoveredSkillNames = discoverSkills(repoRoot).map((s) => `/${s.name}`);
|
|
35292
35840
|
const allCompletions = [.../* @__PURE__ */ new Set([...BUILTIN_COMMANDS, ...discoveredSkillNames])].sort();
|
|
35841
|
+
let cachedModelNames = [];
|
|
35842
|
+
function refreshModelCache() {
|
|
35843
|
+
fetchModels(config.backendUrl, config.apiKey).then((models) => {
|
|
35844
|
+
cachedModelNames = models.map((m) => m.name);
|
|
35845
|
+
}).catch(() => {
|
|
35846
|
+
});
|
|
35847
|
+
}
|
|
35848
|
+
refreshModelCache();
|
|
35293
35849
|
function completer(line) {
|
|
35294
35850
|
if (!line.startsWith("/"))
|
|
35295
35851
|
return [[], line];
|
|
35852
|
+
const modelMatch = line.match(/^\/model\s+(.*)$/i);
|
|
35853
|
+
if (modelMatch) {
|
|
35854
|
+
const partial = modelMatch[1].toLowerCase();
|
|
35855
|
+
const hits2 = cachedModelNames.filter((n) => n.toLowerCase().startsWith(partial));
|
|
35856
|
+
if (hits2.length === 0 && partial.length > 0) {
|
|
35857
|
+
return [[modelMatch[1]], modelMatch[1]];
|
|
35858
|
+
}
|
|
35859
|
+
return [hits2, modelMatch[1]];
|
|
35860
|
+
}
|
|
35296
35861
|
const lower = line.toLowerCase();
|
|
35297
35862
|
const hits = allCompletions.filter((c3) => c3.toLowerCase().startsWith(lower));
|
|
35298
35863
|
return [hits, line];
|
|
@@ -35948,6 +36513,55 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
35948
36513
|
getCallUrl() {
|
|
35949
36514
|
return voiceSession?.tunnelUrl ?? null;
|
|
35950
36515
|
},
|
|
36516
|
+
async exposeStart(kindOrUrl, authKey) {
|
|
36517
|
+
const knownKinds = ["ollama", "vllm", "llvm"];
|
|
36518
|
+
let kind;
|
|
36519
|
+
let targetUrl;
|
|
36520
|
+
if (knownKinds.includes(kindOrUrl)) {
|
|
36521
|
+
kind = kindOrUrl;
|
|
36522
|
+
} else {
|
|
36523
|
+
kind = "custom";
|
|
36524
|
+
targetUrl = kindOrUrl.includes("://") ? kindOrUrl : `http://${kindOrUrl}`;
|
|
36525
|
+
}
|
|
36526
|
+
exposeGateway = new ExposeGateway({ kind, targetUrl, authKey });
|
|
36527
|
+
exposeGateway.on("stats", (stats) => {
|
|
36528
|
+
statusBar.setExposeStatus({
|
|
36529
|
+
status: stats.status,
|
|
36530
|
+
totalRequests: stats.totalRequests,
|
|
36531
|
+
activeConnections: stats.activeConnections,
|
|
36532
|
+
modelUsage: stats.modelUsage
|
|
36533
|
+
});
|
|
36534
|
+
});
|
|
36535
|
+
try {
|
|
36536
|
+
const url = await exposeGateway.start();
|
|
36537
|
+
writeContent(() => {
|
|
36538
|
+
process.stdout.write("\n" + exposeGateway.formatConnectionInfo() + "\n\n");
|
|
36539
|
+
});
|
|
36540
|
+
return url;
|
|
36541
|
+
} catch (err) {
|
|
36542
|
+
exposeGateway = null;
|
|
36543
|
+
throw err;
|
|
36544
|
+
}
|
|
36545
|
+
},
|
|
36546
|
+
async exposeStop() {
|
|
36547
|
+
if (exposeGateway) {
|
|
36548
|
+
await exposeGateway.stop();
|
|
36549
|
+
statusBar.clearExposeStatus();
|
|
36550
|
+
exposeGateway = null;
|
|
36551
|
+
}
|
|
36552
|
+
},
|
|
36553
|
+
isExposeActive() {
|
|
36554
|
+
return exposeGateway?.isActive ?? false;
|
|
36555
|
+
},
|
|
36556
|
+
getExposeInfo() {
|
|
36557
|
+
if (!exposeGateway)
|
|
36558
|
+
return null;
|
|
36559
|
+
return {
|
|
36560
|
+
tunnelUrl: exposeGateway.tunnelUrl,
|
|
36561
|
+
authKey: exposeGateway.authKey,
|
|
36562
|
+
stats: exposeGateway.formatStats()
|
|
36563
|
+
};
|
|
36564
|
+
},
|
|
35951
36565
|
async sendCallButton(chatId, url) {
|
|
35952
36566
|
if (telegramBridge?.isActive) {
|
|
35953
36567
|
await telegramBridge.sendCallButton(chatId, url);
|
|
@@ -35965,6 +36579,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
35965
36579
|
resolvedCaps = caps;
|
|
35966
36580
|
statusBar.setCapabilities(caps);
|
|
35967
36581
|
},
|
|
36582
|
+
refreshModelCache,
|
|
35968
36583
|
hasActiveTask: () => activeTask !== null,
|
|
35969
36584
|
requestCompaction(strategy) {
|
|
35970
36585
|
if (!activeTask)
|
|
@@ -36613,6 +37228,10 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
|
|
|
36613
37228
|
showPrompt();
|
|
36614
37229
|
}
|
|
36615
37230
|
rl.on("close", () => {
|
|
37231
|
+
if (exposeGateway?.isActive) {
|
|
37232
|
+
exposeGateway.stop().catch(() => {
|
|
37233
|
+
});
|
|
37234
|
+
}
|
|
36616
37235
|
statusBar.deactivate();
|
|
36617
37236
|
const closeRows = process.stdout.rows ?? 24;
|
|
36618
37237
|
process.stdout.write(`\x1B[${closeRows - 4};1H\x1B[J
|
|
@@ -36701,6 +37320,7 @@ var init_interactive = __esm({
|
|
|
36701
37320
|
init_dist();
|
|
36702
37321
|
init_listen();
|
|
36703
37322
|
init_voice_session();
|
|
37323
|
+
init_expose();
|
|
36704
37324
|
init_call_agent();
|
|
36705
37325
|
init_config();
|
|
36706
37326
|
init_updater();
|
|
@@ -37364,7 +37984,7 @@ var serve_exports = {};
|
|
|
37364
37984
|
__export(serve_exports, {
|
|
37365
37985
|
serveCommand: () => serveCommand
|
|
37366
37986
|
});
|
|
37367
|
-
import { spawn as
|
|
37987
|
+
import { spawn as spawn13 } from "node:child_process";
|
|
37368
37988
|
async function serveCommand(opts, config) {
|
|
37369
37989
|
const backendType = config.backendType;
|
|
37370
37990
|
if (backendType === "ollama") {
|
|
@@ -37457,7 +38077,7 @@ async function serveVllm(opts, config) {
|
|
|
37457
38077
|
}
|
|
37458
38078
|
async function runVllmServer(args, verbose) {
|
|
37459
38079
|
return new Promise((resolve28, reject) => {
|
|
37460
|
-
const child =
|
|
38080
|
+
const child = spawn13("python", args, {
|
|
37461
38081
|
stdio: verbose ? "inherit" : ["ignore", "pipe", "pipe"],
|
|
37462
38082
|
env: { ...process.env }
|
|
37463
38083
|
});
|
package/package.json
CHANGED