@openape/ape-agent 2.8.15 → 2.9.1
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/bridge.mjs +72 -206
- package/package.json +4 -4
package/dist/bridge.mjs
CHANGED
|
@@ -1398,117 +1398,9 @@ function createHeuristicDetector() {
|
|
|
1398
1398
|
import { decodeJwt } from "jose";
|
|
1399
1399
|
import WebSocket from "ws";
|
|
1400
1400
|
|
|
1401
|
-
// src/chat-api.ts
|
|
1402
|
-
import { ofetch as ofetch6 } from "ofetch";
|
|
1403
|
-
var MAX_BODY = 1e4;
|
|
1404
|
-
var ChatApi = class {
|
|
1405
|
-
constructor(endpoint, bearer) {
|
|
1406
|
-
this.endpoint = endpoint;
|
|
1407
|
-
this.bearer = bearer;
|
|
1408
|
-
}
|
|
1409
|
-
endpoint;
|
|
1410
|
-
bearer;
|
|
1411
|
-
async postMessage(roomId, body, opts = {}) {
|
|
1412
|
-
const bodyForServer = opts.streaming ? body : clamp(body, MAX_BODY);
|
|
1413
|
-
const url = `${this.endpoint}/api/rooms/${encodeURIComponent(roomId)}/messages`;
|
|
1414
|
-
const payload = { body: bodyForServer };
|
|
1415
|
-
if (opts.replyTo) payload.reply_to = opts.replyTo;
|
|
1416
|
-
if (opts.threadId) payload.thread_id = opts.threadId;
|
|
1417
|
-
if (opts.streaming) payload.streaming = true;
|
|
1418
|
-
const result = await ofetch6(url, {
|
|
1419
|
-
method: "POST",
|
|
1420
|
-
headers: { Authorization: await this.bearer() },
|
|
1421
|
-
body: payload
|
|
1422
|
-
});
|
|
1423
|
-
return result;
|
|
1424
|
-
}
|
|
1425
|
-
/**
|
|
1426
|
-
* Fetch the most recent `limit` messages in a thread, oldest-first.
|
|
1427
|
-
* Used by ThreadSession to seed `history` so the agent has the
|
|
1428
|
-
* full conversation context after a bridge restart — otherwise it
|
|
1429
|
-
* only sees messages that arrived via WS since the process boot.
|
|
1430
|
-
*/
|
|
1431
|
-
async listMessages(roomId, threadId, limit = 50) {
|
|
1432
|
-
const url = `${this.endpoint}/api/rooms/${encodeURIComponent(roomId)}/messages?thread_id=${encodeURIComponent(threadId)}&limit=${limit}`;
|
|
1433
|
-
return await ofetch6(url, {
|
|
1434
|
-
method: "GET",
|
|
1435
|
-
headers: { Authorization: await this.bearer() }
|
|
1436
|
-
});
|
|
1437
|
-
}
|
|
1438
|
-
async requestContact(peerEmail) {
|
|
1439
|
-
const url = `${this.endpoint}/api/contacts`;
|
|
1440
|
-
return await ofetch6(url, {
|
|
1441
|
-
method: "POST",
|
|
1442
|
-
headers: { Authorization: await this.bearer() },
|
|
1443
|
-
body: { email: peerEmail }
|
|
1444
|
-
});
|
|
1445
|
-
}
|
|
1446
|
-
async listContacts() {
|
|
1447
|
-
const url = `${this.endpoint}/api/contacts`;
|
|
1448
|
-
return await ofetch6(url, {
|
|
1449
|
-
method: "GET",
|
|
1450
|
-
headers: { Authorization: await this.bearer() }
|
|
1451
|
-
});
|
|
1452
|
-
}
|
|
1453
|
-
async acceptContact(peerEmail) {
|
|
1454
|
-
const url = `${this.endpoint}/api/contacts/${encodeURIComponent(peerEmail)}/accept`;
|
|
1455
|
-
return await ofetch6(url, {
|
|
1456
|
-
method: "POST",
|
|
1457
|
-
headers: { Authorization: await this.bearer() }
|
|
1458
|
-
});
|
|
1459
|
-
}
|
|
1460
|
-
/**
|
|
1461
|
-
* Create a named thread in a room. Used by the cron-runner to drop
|
|
1462
|
-
* each task's runs into its own thread so the chat sidebar shows
|
|
1463
|
-
* one thread per task instead of all task DMs piling into the
|
|
1464
|
-
* main thread.
|
|
1465
|
-
*/
|
|
1466
|
-
async createThread(roomId, name) {
|
|
1467
|
-
const url = `${this.endpoint}/api/rooms/${encodeURIComponent(roomId)}/threads`;
|
|
1468
|
-
return await ofetch6(url, {
|
|
1469
|
-
method: "POST",
|
|
1470
|
-
headers: { Authorization: await this.bearer() },
|
|
1471
|
-
body: { name: name.slice(0, 100) }
|
|
1472
|
-
});
|
|
1473
|
-
}
|
|
1474
|
-
/**
|
|
1475
|
-
* Update an in-flight or completed message. The server differentiates
|
|
1476
|
-
* three modes via the message's current `streaming` state and the
|
|
1477
|
-
* `streaming` field in this call:
|
|
1478
|
-
*
|
|
1479
|
-
* - Stream tick: pass `body` only (current accumulated text).
|
|
1480
|
-
* Server keeps streaming=true and does NOT bump edited_at.
|
|
1481
|
-
* - Stream end: pass `body` + `streaming: false`. Server clears
|
|
1482
|
-
* the streaming flag and triggers the user-facing push.
|
|
1483
|
-
* - Tool-call status: pass `streamingStatus` only (no body).
|
|
1484
|
-
* Renders as "🔧 time.now" in the typing-subtitle.
|
|
1485
|
-
* - Tool-call cleared: pass `streamingStatus: null`.
|
|
1486
|
-
*/
|
|
1487
|
-
async patchMessage(messageId, opts = {}) {
|
|
1488
|
-
const url = `${this.endpoint}/api/messages/${encodeURIComponent(messageId)}`;
|
|
1489
|
-
const payload = {};
|
|
1490
|
-
if (opts.body !== void 0) {
|
|
1491
|
-
payload.body = opts.streaming === false && opts.body.trim().length === 0 ? clamp(opts.body, MAX_BODY) : opts.body.length <= MAX_BODY ? opts.body : `${opts.body.slice(0, MAX_BODY - 1)}\u2026`;
|
|
1492
|
-
}
|
|
1493
|
-
if (opts.streaming !== void 0) payload.streaming = opts.streaming;
|
|
1494
|
-
if (opts.streamingStatus !== void 0) payload.streaming_status = opts.streamingStatus;
|
|
1495
|
-
if (Object.keys(payload).length === 0) return;
|
|
1496
|
-
await ofetch6(url, {
|
|
1497
|
-
method: "PATCH",
|
|
1498
|
-
headers: { Authorization: await this.bearer() },
|
|
1499
|
-
body: payload
|
|
1500
|
-
});
|
|
1501
|
-
}
|
|
1502
|
-
};
|
|
1503
|
-
function clamp(s2, max) {
|
|
1504
|
-
if (s2.trim().length === 0) return "\u2026";
|
|
1505
|
-
if (s2.length <= max) return s2;
|
|
1506
|
-
return `${s2.slice(0, max - 1)}\u2026`;
|
|
1507
|
-
}
|
|
1508
|
-
|
|
1509
1401
|
// src/troop-chat-api.ts
|
|
1510
|
-
import { ofetch as
|
|
1511
|
-
var
|
|
1402
|
+
import { ofetch as ofetch6 } from "ofetch";
|
|
1403
|
+
var MAX_BODY = 64 * 1024;
|
|
1512
1404
|
var SYNTHETIC_THREAD_ID = "main";
|
|
1513
1405
|
function asHistory(msg, agentEmail, ownerEmail) {
|
|
1514
1406
|
return {
|
|
@@ -1542,7 +1434,7 @@ var TroopChatApi = class {
|
|
|
1542
1434
|
/** Resolve + cache the agent's chat row (lazy fetch on first use). */
|
|
1543
1435
|
async getBootstrap() {
|
|
1544
1436
|
if (this.bootstrap) return this.bootstrap;
|
|
1545
|
-
this.bootstrap = await
|
|
1437
|
+
this.bootstrap = await ofetch6(`${this.endpoint}/api/agents/me/chat`, {
|
|
1546
1438
|
method: "GET",
|
|
1547
1439
|
headers: { Authorization: await this.bearer() }
|
|
1548
1440
|
});
|
|
@@ -1557,11 +1449,11 @@ var TroopChatApi = class {
|
|
|
1557
1449
|
void roomId;
|
|
1558
1450
|
void opts.threadId;
|
|
1559
1451
|
const payload = {
|
|
1560
|
-
body: body.length >
|
|
1452
|
+
body: body.length > MAX_BODY ? `${body.slice(0, MAX_BODY - 1)}\u2026` : body
|
|
1561
1453
|
};
|
|
1562
1454
|
if (opts.replyTo) payload.reply_to = opts.replyTo;
|
|
1563
1455
|
if (opts.streaming) payload.streaming = true;
|
|
1564
|
-
const msg = await
|
|
1456
|
+
const msg = await ofetch6(`${this.endpoint}/api/agents/me/chat/messages`, {
|
|
1565
1457
|
method: "POST",
|
|
1566
1458
|
headers: { Authorization: await this.bearer() },
|
|
1567
1459
|
body: payload
|
|
@@ -1572,7 +1464,7 @@ var TroopChatApi = class {
|
|
|
1572
1464
|
void roomId;
|
|
1573
1465
|
void threadId;
|
|
1574
1466
|
void limit;
|
|
1575
|
-
const fresh = await
|
|
1467
|
+
const fresh = await ofetch6(`${this.endpoint}/api/agents/me/chat`, {
|
|
1576
1468
|
method: "GET",
|
|
1577
1469
|
headers: { Authorization: await this.bearer() }
|
|
1578
1470
|
});
|
|
@@ -1582,12 +1474,12 @@ var TroopChatApi = class {
|
|
|
1582
1474
|
async patchMessage(messageId, opts = {}) {
|
|
1583
1475
|
const payload = {};
|
|
1584
1476
|
if (opts.body !== void 0) {
|
|
1585
|
-
payload.body = opts.body.length >
|
|
1477
|
+
payload.body = opts.body.length > MAX_BODY ? `${opts.body.slice(0, MAX_BODY - 1)}\u2026` : opts.body;
|
|
1586
1478
|
}
|
|
1587
1479
|
if (opts.streaming !== void 0) payload.streaming = opts.streaming;
|
|
1588
1480
|
if (opts.streamingStatus !== void 0) payload.streaming_status = opts.streamingStatus;
|
|
1589
1481
|
if (Object.keys(payload).length === 0) return;
|
|
1590
|
-
await
|
|
1482
|
+
await ofetch6(`${this.endpoint}/api/agents/me/chat/messages/${encodeURIComponent(messageId)}`, {
|
|
1591
1483
|
method: "PATCH",
|
|
1592
1484
|
headers: { Authorization: await this.bearer() },
|
|
1593
1485
|
body: payload
|
|
@@ -1649,7 +1541,62 @@ import { basename, join as join22 } from "path";
|
|
|
1649
1541
|
|
|
1650
1542
|
// ../../packages/core/dist/index.js
|
|
1651
1543
|
import * as jose from "jose";
|
|
1544
|
+
import { lookup } from "dns/promises";
|
|
1545
|
+
import { isIP } from "net";
|
|
1652
1546
|
var HKDF_INFO = new TextEncoder().encode("openape-sealed-box-v1");
|
|
1547
|
+
function isBlockedAddress(ip) {
|
|
1548
|
+
const fam = isIP(ip);
|
|
1549
|
+
if (fam === 4) {
|
|
1550
|
+
const o3 = ip.split(".");
|
|
1551
|
+
const a2 = Number(o3[0]);
|
|
1552
|
+
const b2 = Number(o3[1]);
|
|
1553
|
+
if (a2 === 0) return true;
|
|
1554
|
+
if (a2 === 127) return true;
|
|
1555
|
+
if (a2 === 10) return true;
|
|
1556
|
+
if (a2 === 172 && b2 >= 16 && b2 <= 31) return true;
|
|
1557
|
+
if (a2 === 192 && b2 === 168) return true;
|
|
1558
|
+
if (a2 === 169 && b2 === 254) return true;
|
|
1559
|
+
if (a2 === 100 && b2 >= 64 && b2 <= 127) return true;
|
|
1560
|
+
return false;
|
|
1561
|
+
}
|
|
1562
|
+
const low = ip.toLowerCase().replace(/^\[|\]$/g, "");
|
|
1563
|
+
if (low === "::" || low === "::1") return true;
|
|
1564
|
+
if (low.startsWith("fe80")) return true;
|
|
1565
|
+
if (low.startsWith("fc") || low.startsWith("fd")) return true;
|
|
1566
|
+
const mapped = low.match(/^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/);
|
|
1567
|
+
if (mapped) return isBlockedAddress(mapped[1]);
|
|
1568
|
+
return false;
|
|
1569
|
+
}
|
|
1570
|
+
async function assertPublicUrl(rawUrl, opts = {}) {
|
|
1571
|
+
let url;
|
|
1572
|
+
try {
|
|
1573
|
+
url = new URL(rawUrl);
|
|
1574
|
+
} catch {
|
|
1575
|
+
throw new Error(`Invalid URL: ${rawUrl}`);
|
|
1576
|
+
}
|
|
1577
|
+
const allowedSchemes = opts.allowHttp === true ? /* @__PURE__ */ new Set(["https:", "http:"]) : /* @__PURE__ */ new Set(["https:"]);
|
|
1578
|
+
if (!allowedSchemes.has(url.protocol)) {
|
|
1579
|
+
const expected = opts.allowHttp === true ? "http(s)" : "https";
|
|
1580
|
+
throw new Error(`URL must use ${expected}:// (got ${url.protocol}//) \u2014 ${rawUrl}`);
|
|
1581
|
+
}
|
|
1582
|
+
const host = url.hostname.replace(/^\[|\]$/g, "");
|
|
1583
|
+
const addresses = [];
|
|
1584
|
+
if (isIP(host)) {
|
|
1585
|
+
addresses.push(host);
|
|
1586
|
+
} else {
|
|
1587
|
+
const results = await lookup(host, { all: true });
|
|
1588
|
+
for (const r3 of results) addresses.push(r3.address);
|
|
1589
|
+
}
|
|
1590
|
+
if (addresses.length === 0) {
|
|
1591
|
+
throw new Error(`Host did not resolve: ${host}`);
|
|
1592
|
+
}
|
|
1593
|
+
for (const addr of addresses) {
|
|
1594
|
+
if (isBlockedAddress(addr)) {
|
|
1595
|
+
throw new Error(`Refusing to fetch a private/loopback address (${addr}) for ${rawUrl}`);
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
return url;
|
|
1599
|
+
}
|
|
1653
1600
|
|
|
1654
1601
|
// ../../packages/grants/dist/index.js
|
|
1655
1602
|
function normalizeSelector(selector) {
|
|
@@ -3198,8 +3145,6 @@ import { dirname, normalize, resolve } from "path";
|
|
|
3198
3145
|
import { homedir as homedir23 } from "os";
|
|
3199
3146
|
import { resolve as resolve2 } from "path";
|
|
3200
3147
|
import process2 from "process";
|
|
3201
|
-
import { lookup } from "dns/promises";
|
|
3202
|
-
import { isIP } from "net";
|
|
3203
3148
|
import { execFileSync } from "child_process";
|
|
3204
3149
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
3205
3150
|
var DEFAULT_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
@@ -3692,59 +3637,10 @@ var gitWorktreeTools = [
|
|
|
3692
3637
|
}
|
|
3693
3638
|
}
|
|
3694
3639
|
];
|
|
3695
|
-
function isBlockedAddress(ip) {
|
|
3696
|
-
const fam = isIP(ip);
|
|
3697
|
-
if (fam === 4) {
|
|
3698
|
-
const o3 = ip.split(".");
|
|
3699
|
-
const a2 = Number(o3[0]);
|
|
3700
|
-
const b2 = Number(o3[1]);
|
|
3701
|
-
if (a2 === 0) return true;
|
|
3702
|
-
if (a2 === 127) return true;
|
|
3703
|
-
if (a2 === 10) return true;
|
|
3704
|
-
if (a2 === 172 && b2 >= 16 && b2 <= 31) return true;
|
|
3705
|
-
if (a2 === 192 && b2 === 168) return true;
|
|
3706
|
-
if (a2 === 169 && b2 === 254) return true;
|
|
3707
|
-
if (a2 === 100 && b2 >= 64 && b2 <= 127) return true;
|
|
3708
|
-
return false;
|
|
3709
|
-
}
|
|
3710
|
-
const low = ip.toLowerCase().replace(/^\[|\]$/g, "");
|
|
3711
|
-
if (low === "::" || low === "::1") return true;
|
|
3712
|
-
if (low.startsWith("fe80")) return true;
|
|
3713
|
-
if (low.startsWith("fc") || low.startsWith("fd")) return true;
|
|
3714
|
-
const mapped = low.match(/^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/);
|
|
3715
|
-
if (mapped) return isBlockedAddress(mapped[1]);
|
|
3716
|
-
return false;
|
|
3717
|
-
}
|
|
3718
|
-
async function assertPublicUrl(rawUrl) {
|
|
3719
|
-
let url;
|
|
3720
|
-
try {
|
|
3721
|
-
url = new URL(rawUrl);
|
|
3722
|
-
} catch {
|
|
3723
|
-
throw new Error(`Invalid URL: ${rawUrl}`);
|
|
3724
|
-
}
|
|
3725
|
-
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
3726
|
-
throw new Error(`url must be http(s) (got ${url.protocol})`);
|
|
3727
|
-
}
|
|
3728
|
-
const host = url.hostname.replace(/^\[|\]$/g, "");
|
|
3729
|
-
const addresses = [];
|
|
3730
|
-
if (isIP(host)) {
|
|
3731
|
-
addresses.push(host);
|
|
3732
|
-
} else {
|
|
3733
|
-
const results = await lookup(host, { all: true });
|
|
3734
|
-
for (const r3 of results) addresses.push(r3.address);
|
|
3735
|
-
}
|
|
3736
|
-
if (addresses.length === 0) throw new Error(`Host did not resolve: ${host}`);
|
|
3737
|
-
for (const addr of addresses) {
|
|
3738
|
-
if (isBlockedAddress(addr)) {
|
|
3739
|
-
throw new Error(`Refusing to fetch a private/loopback address (${addr}) for ${rawUrl}`);
|
|
3740
|
-
}
|
|
3741
|
-
}
|
|
3742
|
-
return url;
|
|
3743
|
-
}
|
|
3744
3640
|
async function safeFetch(rawUrl, init2 = {}, maxRedirects = 5) {
|
|
3745
3641
|
let current = rawUrl;
|
|
3746
3642
|
for (let hop = 0; hop <= maxRedirects; hop += 1) {
|
|
3747
|
-
await assertPublicUrl(current);
|
|
3643
|
+
await assertPublicUrl(current, { allowHttp: true });
|
|
3748
3644
|
const res = await fetch(current, { ...init2, redirect: "manual" });
|
|
3749
3645
|
if (res.status >= 300 && res.status < 400) {
|
|
3750
3646
|
const location = res.headers.get("location");
|
|
@@ -5014,7 +4910,7 @@ function resolveTools(envFallback) {
|
|
|
5014
4910
|
}
|
|
5015
4911
|
return envFallback;
|
|
5016
4912
|
}
|
|
5017
|
-
var DEFAULT_ENDPOINT = "https://
|
|
4913
|
+
var DEFAULT_ENDPOINT = "https://troop.openape.ai";
|
|
5018
4914
|
var DEFAULT_APES_BIN = "apes";
|
|
5019
4915
|
var DEFAULT_MAX_STEPS = 10;
|
|
5020
4916
|
var DEFAULT_SYSTEM_PROMPT = `You are a helpful assistant in a 1:1 chat. Be concise and friendly. When asked for facts, say "I don't know" rather than guess.`;
|
|
@@ -5022,28 +4918,7 @@ var PING_INTERVAL_MS = 3e4;
|
|
|
5022
4918
|
var RECONNECT_BASE_MS = 1e3;
|
|
5023
4919
|
var RECONNECT_MAX_MS = 3e4;
|
|
5024
4920
|
var ALLOWLIST_POLL_INTERVAL_MS = 3e4;
|
|
5025
|
-
function loadBridgeEnvFile() {
|
|
5026
|
-
const path = join8(homedir9(), "Library", "Application Support", "openape", "bridge", ".env");
|
|
5027
|
-
if (!existsSync6(path)) return;
|
|
5028
|
-
try {
|
|
5029
|
-
const raw = readFileSync8(path, "utf8");
|
|
5030
|
-
for (const line of raw.split(/\r?\n/)) {
|
|
5031
|
-
const trimmed = line.trim();
|
|
5032
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
5033
|
-
const eq = trimmed.indexOf("=");
|
|
5034
|
-
if (eq < 0) continue;
|
|
5035
|
-
const key = trimmed.slice(0, eq).trim();
|
|
5036
|
-
const value = trimmed.slice(eq + 1).trim().replace(/^["']|["']$/g, "");
|
|
5037
|
-
if (!key) continue;
|
|
5038
|
-
if (process3.env[key] === void 0) {
|
|
5039
|
-
process3.env[key] = value;
|
|
5040
|
-
}
|
|
5041
|
-
}
|
|
5042
|
-
} catch {
|
|
5043
|
-
}
|
|
5044
|
-
}
|
|
5045
4921
|
function readConfig() {
|
|
5046
|
-
loadBridgeEnvFile();
|
|
5047
4922
|
const toolsRaw = process3.env.APE_CHAT_BRIDGE_TOOLS ?? "";
|
|
5048
4923
|
const tools = toolsRaw.split(",").map((s2) => s2.trim()).filter(Boolean);
|
|
5049
4924
|
const maxStepsRaw = process3.env.APE_CHAT_BRIDGE_MAX_STEPS;
|
|
@@ -5051,20 +4926,17 @@ function readConfig() {
|
|
|
5051
4926
|
const model = process3.env.APE_CHAT_BRIDGE_MODEL;
|
|
5052
4927
|
if (!model) {
|
|
5053
4928
|
throw new Error(
|
|
5054
|
-
"APE_CHAT_BRIDGE_MODEL is not set. Set it in the
|
|
4929
|
+
"APE_CHAT_BRIDGE_MODEL is not set. Set it in the container env (compose environment: block) or globally in `~/litellm/.env`. Common values: `gpt-5.4` (ChatGPT-only LiteLLM proxy), `claude-haiku-4-5` (Anthropic-only)."
|
|
5055
4930
|
);
|
|
5056
4931
|
}
|
|
5057
|
-
const targetRaw = (process3.env.OPENAPE_BRIDGE_TARGET ?? "chat").toLowerCase();
|
|
5058
|
-
const target = targetRaw === "troop" ? "troop" : "chat";
|
|
5059
4932
|
return {
|
|
5060
|
-
endpoint: (process3.env.
|
|
4933
|
+
endpoint: (process3.env.OPENAPE_TROOP_URL ?? DEFAULT_ENDPOINT).replace(/\/$/, ""),
|
|
5061
4934
|
apesBin: process3.env.APE_CHAT_BRIDGE_APES_BIN ?? DEFAULT_APES_BIN,
|
|
5062
4935
|
model,
|
|
5063
4936
|
systemPrompt: process3.env.APE_CHAT_BRIDGE_SYSTEM_PROMPT ?? DEFAULT_SYSTEM_PROMPT,
|
|
5064
4937
|
tools,
|
|
5065
4938
|
maxSteps: Number.isFinite(maxSteps) && maxSteps > 0 ? maxSteps : DEFAULT_MAX_STEPS,
|
|
5066
|
-
roomFilter: process3.env.APE_CHAT_BRIDGE_ROOM
|
|
5067
|
-
target
|
|
4939
|
+
roomFilter: process3.env.APE_CHAT_BRIDGE_ROOM
|
|
5068
4940
|
};
|
|
5069
4941
|
}
|
|
5070
4942
|
async function getIdentity() {
|
|
@@ -5100,13 +4972,13 @@ var Bridge = class {
|
|
|
5100
4972
|
const idp = await ensureFreshIdpAuth();
|
|
5101
4973
|
return `Bearer ${idp.access_token}`;
|
|
5102
4974
|
};
|
|
5103
|
-
this.chat =
|
|
4975
|
+
this.chat = new TroopChatApi(this.cfg.endpoint, this.bearer);
|
|
5104
4976
|
this.cron = new CronRunner({
|
|
5105
4977
|
runtimeConfig: this.runtimeConfig(),
|
|
5106
4978
|
chat: this.chat,
|
|
5107
4979
|
ownerEmail: this.ownerEmail,
|
|
5108
4980
|
log,
|
|
5109
|
-
troopUrl:
|
|
4981
|
+
troopUrl: this.cfg.endpoint,
|
|
5110
4982
|
bearer: this.bearer
|
|
5111
4983
|
});
|
|
5112
4984
|
this.cron.start();
|
|
@@ -5118,11 +4990,6 @@ var Bridge = class {
|
|
|
5118
4990
|
// its own message history and calls @openape/apes' runLoop directly
|
|
5119
4991
|
// (no stdio JSON-RPC subprocess — see thread-session.ts).
|
|
5120
4992
|
threads = /* @__PURE__ */ new Map();
|
|
5121
|
-
// ChatApi and TroopChatApi expose the same surface (postMessage /
|
|
5122
|
-
// listMessages / patchMessage / listContacts / requestContact /
|
|
5123
|
-
// acceptContact / createThread) so the rest of the bridge calls
|
|
5124
|
-
// through a structurally-typed reference without caring which
|
|
5125
|
-
// backend is in play. Picked at construction time from cfg.target.
|
|
5126
4993
|
chat;
|
|
5127
4994
|
bearer;
|
|
5128
4995
|
cron;
|
|
@@ -5268,8 +5135,7 @@ var Bridge = class {
|
|
|
5268
5135
|
}
|
|
5269
5136
|
async pumpOnce() {
|
|
5270
5137
|
const bearer = await this.bearer();
|
|
5271
|
-
const
|
|
5272
|
-
const wsUrl = `${this.cfg.endpoint.replace(/^http/, "ws")}${wsPath}?token=${encodeURIComponent(bearer.replace(/^Bearer\s+/i, ""))}`;
|
|
5138
|
+
const wsUrl = `${this.cfg.endpoint.replace(/^http/, "ws")}/_ws/chat?token=${encodeURIComponent(bearer.replace(/^Bearer\s+/i, ""))}`;
|
|
5273
5139
|
const ws = new WebSocket(wsUrl);
|
|
5274
5140
|
return new Promise((resolve4, reject) => {
|
|
5275
5141
|
let pingTimer;
|
|
@@ -5299,7 +5165,7 @@ var Bridge = class {
|
|
|
5299
5165
|
return;
|
|
5300
5166
|
}
|
|
5301
5167
|
if (frame.type !== "message" || !frame.payload) return;
|
|
5302
|
-
const msg = this.
|
|
5168
|
+
const msg = this.translateTroopPayload(frame.chat_id ?? "", frame.payload);
|
|
5303
5169
|
void this.handleInbound(msg);
|
|
5304
5170
|
});
|
|
5305
5171
|
ws.on("close", () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openape/ape-agent",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.1",
|
|
4
4
|
"description": "OpenApe agent runtime: per-agent process that connects to chat.openape.ai, runs the LLM loop with tools + cron tasks, and streams replies back to owners.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
"ofetch": "^1.4.1",
|
|
24
24
|
"ws": "^8.18.0",
|
|
25
25
|
"yaml": "^2.8.0",
|
|
26
|
-
"@openape/apes": "1.29.
|
|
27
|
-
"@openape/
|
|
28
|
-
"@openape/
|
|
26
|
+
"@openape/apes": "1.29.1",
|
|
27
|
+
"@openape/cli-auth": "0.5.0",
|
|
28
|
+
"@openape/prompt-injection-detector": "0.1.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@antfu/eslint-config": "^7.6.1",
|