open-agents-ai 0.67.0 → 0.68.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 +1436 -278
- package/package.json +7 -2
package/dist/index.js
CHANGED
|
@@ -14626,10 +14626,10 @@ ${marker}` : marker);
|
|
|
14626
14626
|
if (!this._workingDirectory)
|
|
14627
14627
|
return;
|
|
14628
14628
|
try {
|
|
14629
|
-
const { mkdirSync:
|
|
14630
|
-
const { join:
|
|
14631
|
-
const sessionDir =
|
|
14632
|
-
|
|
14629
|
+
const { mkdirSync: mkdirSync17, writeFileSync: writeFileSync15 } = __require("node:fs");
|
|
14630
|
+
const { join: join46 } = __require("node:path");
|
|
14631
|
+
const sessionDir = join46(this._workingDirectory, ".oa", "session", this._sessionId);
|
|
14632
|
+
mkdirSync17(sessionDir, { recursive: true });
|
|
14633
14633
|
const checkpoint = {
|
|
14634
14634
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14635
14635
|
sessionId: this._sessionId,
|
|
@@ -14641,7 +14641,7 @@ ${marker}` : marker);
|
|
|
14641
14641
|
memexEntryCount: this._memexArchive.size,
|
|
14642
14642
|
fileRegistrySize: this._fileRegistry.size
|
|
14643
14643
|
};
|
|
14644
|
-
|
|
14644
|
+
writeFileSync15(join46(sessionDir, "checkpoint.json"), JSON.stringify(checkpoint, null, 2));
|
|
14645
14645
|
} catch {
|
|
14646
14646
|
}
|
|
14647
14647
|
}
|
|
@@ -19129,12 +19129,12 @@ 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 EventEmitter7 = __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: randomBytes8, createHash: createHash4 } = __require("crypto");
|
|
19138
19138
|
var { Duplex, Readable } = __require("stream");
|
|
19139
19139
|
var { URL: URL3 } = __require("url");
|
|
19140
19140
|
var PerMessageDeflate = require_permessage_deflate();
|
|
@@ -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 EventEmitter7 {
|
|
19165
19165
|
/**
|
|
19166
19166
|
* Create a new `WebSocket`.
|
|
19167
19167
|
*
|
|
@@ -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 = randomBytes8(16).toString("base64");
|
|
19668
19668
|
const request = isSecure ? https.request : http.request;
|
|
19669
19669
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
19670
19670
|
let perMessageDeflate;
|
|
@@ -19794,7 +19794,7 @@ var require_websocket = __commonJS({
|
|
|
19794
19794
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
19795
19795
|
return;
|
|
19796
19796
|
}
|
|
19797
|
-
const digest =
|
|
19797
|
+
const digest = createHash4("sha1").update(key + GUID).digest("base64");
|
|
19798
19798
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
19799
19799
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
19800
19800
|
return;
|
|
@@ -20158,10 +20158,10 @@ 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 EventEmitter7 = __require("events");
|
|
20162
20162
|
var http = __require("http");
|
|
20163
20163
|
var { Duplex } = __require("stream");
|
|
20164
|
-
var { createHash:
|
|
20164
|
+
var { createHash: createHash4 } = __require("crypto");
|
|
20165
20165
|
var extension = require_extension();
|
|
20166
20166
|
var PerMessageDeflate = require_permessage_deflate();
|
|
20167
20167
|
var subprotocol = require_subprotocol();
|
|
@@ -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 EventEmitter7 {
|
|
20175
20175
|
/**
|
|
20176
20176
|
* Create a `WebSocketServer` instance.
|
|
20177
20177
|
*
|
|
@@ -20462,7 +20462,7 @@ var require_websocket_server = __commonJS({
|
|
|
20462
20462
|
);
|
|
20463
20463
|
}
|
|
20464
20464
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
20465
|
-
const digest =
|
|
20465
|
+
const digest = createHash4("sha1").update(key + GUID).digest("base64");
|
|
20466
20466
|
const headers = [
|
|
20467
20467
|
"HTTP/1.1 101 Switching Protocols",
|
|
20468
20468
|
"Upgrade: websocket",
|
|
@@ -22283,17 +22283,14 @@ var init_expose = __esm({
|
|
|
22283
22283
|
this.extractModelFromRequest(req);
|
|
22284
22284
|
url.searchParams.delete("key");
|
|
22285
22285
|
const forwardPath = url.pathname + url.search;
|
|
22286
|
+
const forwardHeaders = { ...req.headers, host: target.host };
|
|
22287
|
+
delete forwardHeaders.authorization;
|
|
22286
22288
|
const proxyReq = httpRequest({
|
|
22287
22289
|
hostname: target.hostname,
|
|
22288
22290
|
port: target.port || (target.protocol === "https:" ? 443 : 80),
|
|
22289
22291
|
path: forwardPath,
|
|
22290
22292
|
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
|
-
}
|
|
22293
|
+
headers: forwardHeaders
|
|
22297
22294
|
}, (proxyRes) => {
|
|
22298
22295
|
res.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers);
|
|
22299
22296
|
proxyRes.pipe(res);
|
|
@@ -22443,8 +22440,958 @@ var init_expose = __esm({
|
|
|
22443
22440
|
}
|
|
22444
22441
|
});
|
|
22445
22442
|
|
|
22446
|
-
// packages/cli/dist/tui/
|
|
22443
|
+
// packages/cli/dist/tui/p2p/types.js
|
|
22444
|
+
function trustLevel(tier) {
|
|
22445
|
+
return TRUST_ORDER.indexOf(tier);
|
|
22446
|
+
}
|
|
22447
|
+
var TRUST_ORDER;
|
|
22448
|
+
var init_types = __esm({
|
|
22449
|
+
"packages/cli/dist/tui/p2p/types.js"() {
|
|
22450
|
+
"use strict";
|
|
22451
|
+
TRUST_ORDER = ["public", "verified", "tee", "local"];
|
|
22452
|
+
}
|
|
22453
|
+
});
|
|
22454
|
+
|
|
22455
|
+
// packages/cli/dist/tui/p2p/secret-vault.js
|
|
22456
|
+
import { createCipheriv, createDecipheriv, randomBytes as randomBytes6, scryptSync, createHash } from "node:crypto";
|
|
22457
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync7, existsSync as existsSync21, mkdirSync as mkdirSync7 } from "node:fs";
|
|
22458
|
+
import { join as join29, dirname as dirname11 } from "node:path";
|
|
22459
|
+
var PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, CIPHER_ALGO, SALT_LEN, IV_LEN, KEY_LEN, SecretVault;
|
|
22460
|
+
var init_secret_vault = __esm({
|
|
22461
|
+
"packages/cli/dist/tui/p2p/secret-vault.js"() {
|
|
22462
|
+
"use strict";
|
|
22463
|
+
init_types();
|
|
22464
|
+
PLACEHOLDER_PREFIX = "{{OA_VAR_";
|
|
22465
|
+
PLACEHOLDER_SUFFIX = "}}";
|
|
22466
|
+
CIPHER_ALGO = "aes-256-gcm";
|
|
22467
|
+
SALT_LEN = 16;
|
|
22468
|
+
IV_LEN = 12;
|
|
22469
|
+
KEY_LEN = 32;
|
|
22470
|
+
SecretVault = class {
|
|
22471
|
+
storePath;
|
|
22472
|
+
secrets = /* @__PURE__ */ new Map();
|
|
22473
|
+
/** Regex cache — rebuilt when secrets change */
|
|
22474
|
+
_redactRegex = null;
|
|
22475
|
+
/** Reverse map: placeholder → secret name (for injection) */
|
|
22476
|
+
_placeholderToName = /* @__PURE__ */ new Map();
|
|
22477
|
+
constructor(storePath) {
|
|
22478
|
+
this.storePath = storePath;
|
|
22479
|
+
}
|
|
22480
|
+
// ── Core API ────────────────────────────────────────────────────────────
|
|
22481
|
+
/**
|
|
22482
|
+
* Register a secret. The value will be redacted from all text sent to
|
|
22483
|
+
* peers below the specified trust tier.
|
|
22484
|
+
*/
|
|
22485
|
+
set(name, value, minTrust = "verified") {
|
|
22486
|
+
const normalized = name.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
|
|
22487
|
+
const placeholder = `${PLACEHOLDER_PREFIX}${normalized}${PLACEHOLDER_SUFFIX}`;
|
|
22488
|
+
this.secrets.set(normalized, {
|
|
22489
|
+
name: normalized,
|
|
22490
|
+
value,
|
|
22491
|
+
placeholder,
|
|
22492
|
+
minTrust,
|
|
22493
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
22494
|
+
});
|
|
22495
|
+
this._redactRegex = null;
|
|
22496
|
+
this._placeholderToName.set(placeholder, normalized);
|
|
22497
|
+
}
|
|
22498
|
+
/** Remove a secret from the vault */
|
|
22499
|
+
delete(name) {
|
|
22500
|
+
const normalized = name.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
|
|
22501
|
+
const entry = this.secrets.get(normalized);
|
|
22502
|
+
if (entry) {
|
|
22503
|
+
this._placeholderToName.delete(entry.placeholder);
|
|
22504
|
+
this.secrets.delete(normalized);
|
|
22505
|
+
this._redactRegex = null;
|
|
22506
|
+
return true;
|
|
22507
|
+
}
|
|
22508
|
+
return false;
|
|
22509
|
+
}
|
|
22510
|
+
/** Get a secret value by name */
|
|
22511
|
+
get(name) {
|
|
22512
|
+
const normalized = name.toUpperCase().replace(/[^A-Z0-9_]/g, "_");
|
|
22513
|
+
return this.secrets.get(normalized)?.value;
|
|
22514
|
+
}
|
|
22515
|
+
/** List all secret names (never exposes values) */
|
|
22516
|
+
list() {
|
|
22517
|
+
return Array.from(this.secrets.values()).map((s) => ({
|
|
22518
|
+
name: s.name,
|
|
22519
|
+
minTrust: s.minTrust,
|
|
22520
|
+
createdAt: s.createdAt
|
|
22521
|
+
}));
|
|
22522
|
+
}
|
|
22523
|
+
/** Number of registered secrets */
|
|
22524
|
+
get size() {
|
|
22525
|
+
return this.secrets.size;
|
|
22526
|
+
}
|
|
22527
|
+
// ── Redaction & Injection ───────────────────────────────────────────────
|
|
22528
|
+
/**
|
|
22529
|
+
* Redact all known secret values from text, replacing with placeholders.
|
|
22530
|
+
* Only redacts secrets whose minTrust is above the given peer trust level.
|
|
22531
|
+
*
|
|
22532
|
+
* @param text - The text to redact
|
|
22533
|
+
* @param peerTrust - The trust tier of the destination peer
|
|
22534
|
+
* @returns Redacted text with {{OA_VAR_*}} placeholders
|
|
22535
|
+
*/
|
|
22536
|
+
redact(text, peerTrust = "public") {
|
|
22537
|
+
if (this.secrets.size === 0)
|
|
22538
|
+
return text;
|
|
22539
|
+
const peerLevel = trustLevel(peerTrust);
|
|
22540
|
+
let result = text;
|
|
22541
|
+
const sorted = Array.from(this.secrets.values()).filter((s) => trustLevel(s.minTrust) > peerLevel).sort((a, b) => b.value.length - a.value.length);
|
|
22542
|
+
for (const secret of sorted) {
|
|
22543
|
+
if (secret.value.length < 3)
|
|
22544
|
+
continue;
|
|
22545
|
+
const escaped = secret.value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
22546
|
+
result = result.replace(new RegExp(escaped, "g"), secret.placeholder);
|
|
22547
|
+
}
|
|
22548
|
+
return result;
|
|
22549
|
+
}
|
|
22550
|
+
/**
|
|
22551
|
+
* Inject real secret values back into text containing placeholders.
|
|
22552
|
+
* This is the "last-mile" operation that happens ONLY on the trusted local machine.
|
|
22553
|
+
*
|
|
22554
|
+
* @param text - Text with {{OA_VAR_*}} placeholders
|
|
22555
|
+
* @returns Text with real secret values restored
|
|
22556
|
+
*/
|
|
22557
|
+
inject(text) {
|
|
22558
|
+
if (this.secrets.size === 0 || !text.includes(PLACEHOLDER_PREFIX))
|
|
22559
|
+
return text;
|
|
22560
|
+
let result = text;
|
|
22561
|
+
for (const [placeholder, name] of this._placeholderToName) {
|
|
22562
|
+
const secret = this.secrets.get(name);
|
|
22563
|
+
if (secret) {
|
|
22564
|
+
result = result.split(placeholder).join(secret.value);
|
|
22565
|
+
}
|
|
22566
|
+
}
|
|
22567
|
+
return result;
|
|
22568
|
+
}
|
|
22569
|
+
/**
|
|
22570
|
+
* Scan text for potential secrets that should be registered.
|
|
22571
|
+
* Returns patterns that look like API keys, tokens, passwords, etc.
|
|
22572
|
+
*/
|
|
22573
|
+
scan(text) {
|
|
22574
|
+
const patterns = [];
|
|
22575
|
+
const seen = /* @__PURE__ */ new Set();
|
|
22576
|
+
const regexes = [
|
|
22577
|
+
/\b(sk-[a-zA-Z0-9]{20,})\b/g,
|
|
22578
|
+
// OpenAI-style
|
|
22579
|
+
/\b(cpk_[a-zA-Z0-9]{20,})\b/g,
|
|
22580
|
+
// Chutes-style
|
|
22581
|
+
/\b(gsk_[a-zA-Z0-9]{20,})\b/g,
|
|
22582
|
+
// Groq-style
|
|
22583
|
+
/\b(xai-[a-zA-Z0-9]{20,})\b/g,
|
|
22584
|
+
// xAI-style
|
|
22585
|
+
/\b(ghp_[a-zA-Z0-9]{36,})\b/g,
|
|
22586
|
+
// GitHub PAT
|
|
22587
|
+
/\b(glpat-[a-zA-Z0-9_-]{20,})\b/g,
|
|
22588
|
+
// GitLab PAT
|
|
22589
|
+
/\b(AKIA[0-9A-Z]{16})\b/g,
|
|
22590
|
+
// AWS access key
|
|
22591
|
+
/\b(0x[a-fA-F0-9]{40})\b/g,
|
|
22592
|
+
// ETH address
|
|
22593
|
+
/\b(0x[a-fA-F0-9]{64})\b/g,
|
|
22594
|
+
// ETH private key
|
|
22595
|
+
/\b([a-zA-Z0-9+/]{40,}={0,2})\b/g,
|
|
22596
|
+
// Base64 tokens (40+ chars)
|
|
22597
|
+
/\b(eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,})/g
|
|
22598
|
+
// JWT tokens
|
|
22599
|
+
];
|
|
22600
|
+
for (const re of regexes) {
|
|
22601
|
+
let match;
|
|
22602
|
+
while ((match = re.exec(text)) !== null) {
|
|
22603
|
+
const val = match[1];
|
|
22604
|
+
if (!seen.has(val) && !this.isRegistered(val)) {
|
|
22605
|
+
seen.add(val);
|
|
22606
|
+
patterns.push(val);
|
|
22607
|
+
}
|
|
22608
|
+
}
|
|
22609
|
+
}
|
|
22610
|
+
return patterns;
|
|
22611
|
+
}
|
|
22612
|
+
/** Check if a value is already registered as a secret */
|
|
22613
|
+
isRegistered(value) {
|
|
22614
|
+
for (const s of this.secrets.values()) {
|
|
22615
|
+
if (s.value === value)
|
|
22616
|
+
return true;
|
|
22617
|
+
}
|
|
22618
|
+
return false;
|
|
22619
|
+
}
|
|
22620
|
+
// ── Auto-registration from environment ──────────────────────────────────
|
|
22621
|
+
/**
|
|
22622
|
+
* Auto-import secrets from environment variables matching common patterns.
|
|
22623
|
+
* Only imports vars that look like credentials (API keys, tokens, passwords).
|
|
22624
|
+
*/
|
|
22625
|
+
importFromEnv() {
|
|
22626
|
+
const envPatterns = [
|
|
22627
|
+
/^.*API[_-]?KEY$/i,
|
|
22628
|
+
/^.*SECRET$/i,
|
|
22629
|
+
/^.*TOKEN$/i,
|
|
22630
|
+
/^.*PASSWORD$/i,
|
|
22631
|
+
/^.*PRIVATE[_-]?KEY$/i,
|
|
22632
|
+
/^.*AUTH$/i,
|
|
22633
|
+
/^.*CREDENTIAL$/i
|
|
22634
|
+
];
|
|
22635
|
+
let count = 0;
|
|
22636
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
22637
|
+
if (!value || value.length < 8)
|
|
22638
|
+
continue;
|
|
22639
|
+
if (envPatterns.some((p) => p.test(key))) {
|
|
22640
|
+
if (!this.secrets.has(key)) {
|
|
22641
|
+
this.set(key, value, "verified");
|
|
22642
|
+
count++;
|
|
22643
|
+
}
|
|
22644
|
+
}
|
|
22645
|
+
}
|
|
22646
|
+
return count;
|
|
22647
|
+
}
|
|
22648
|
+
// ── Encrypted persistence ──────────────────────────────────────────────
|
|
22649
|
+
/**
|
|
22650
|
+
* Save vault to disk with AES-256-GCM encryption.
|
|
22651
|
+
* The passphrase is used to derive the encryption key via scrypt.
|
|
22652
|
+
*/
|
|
22653
|
+
save(passphrase) {
|
|
22654
|
+
if (!this.storePath)
|
|
22655
|
+
throw new Error("No store path configured");
|
|
22656
|
+
const data = JSON.stringify(Array.from(this.secrets.values()).map((s) => ({
|
|
22657
|
+
name: s.name,
|
|
22658
|
+
value: s.value,
|
|
22659
|
+
minTrust: s.minTrust,
|
|
22660
|
+
createdAt: s.createdAt
|
|
22661
|
+
})));
|
|
22662
|
+
const salt = randomBytes6(SALT_LEN);
|
|
22663
|
+
const key = scryptSync(passphrase, salt, KEY_LEN);
|
|
22664
|
+
const iv = randomBytes6(IV_LEN);
|
|
22665
|
+
const cipher = createCipheriv(CIPHER_ALGO, key, iv);
|
|
22666
|
+
const encrypted = Buffer.concat([cipher.update(data, "utf8"), cipher.final()]);
|
|
22667
|
+
const tag = cipher.getAuthTag();
|
|
22668
|
+
const blob = Buffer.concat([salt, iv, tag, encrypted]);
|
|
22669
|
+
const dir = dirname11(this.storePath);
|
|
22670
|
+
if (!existsSync21(dir))
|
|
22671
|
+
mkdirSync7(dir, { recursive: true });
|
|
22672
|
+
writeFileSync7(this.storePath, blob, { mode: 384 });
|
|
22673
|
+
}
|
|
22674
|
+
/**
|
|
22675
|
+
* Load vault from disk, decrypting with the given passphrase.
|
|
22676
|
+
* Returns the number of secrets loaded.
|
|
22677
|
+
*/
|
|
22678
|
+
load(passphrase) {
|
|
22679
|
+
if (!this.storePath || !existsSync21(this.storePath))
|
|
22680
|
+
return 0;
|
|
22681
|
+
const blob = readFileSync15(this.storePath);
|
|
22682
|
+
if (blob.length < SALT_LEN + IV_LEN + 16) {
|
|
22683
|
+
throw new Error("Vault file is corrupted (too small)");
|
|
22684
|
+
}
|
|
22685
|
+
const salt = blob.subarray(0, SALT_LEN);
|
|
22686
|
+
const iv = blob.subarray(SALT_LEN, SALT_LEN + IV_LEN);
|
|
22687
|
+
const tag = blob.subarray(SALT_LEN + IV_LEN, SALT_LEN + IV_LEN + 16);
|
|
22688
|
+
const encrypted = blob.subarray(SALT_LEN + IV_LEN + 16);
|
|
22689
|
+
const key = scryptSync(passphrase, salt, KEY_LEN);
|
|
22690
|
+
const decipher = createDecipheriv(CIPHER_ALGO, key, iv);
|
|
22691
|
+
decipher.setAuthTag(tag);
|
|
22692
|
+
let data;
|
|
22693
|
+
try {
|
|
22694
|
+
data = decipher.update(encrypted) + decipher.final("utf8");
|
|
22695
|
+
} catch {
|
|
22696
|
+
throw new Error("Failed to decrypt vault \u2014 wrong passphrase?");
|
|
22697
|
+
}
|
|
22698
|
+
const entries = JSON.parse(data);
|
|
22699
|
+
for (const entry of entries) {
|
|
22700
|
+
this.set(entry.name, entry.value, entry.minTrust);
|
|
22701
|
+
}
|
|
22702
|
+
return entries.length;
|
|
22703
|
+
}
|
|
22704
|
+
/** Generate a deterministic fingerprint of vault contents (for sync verification) */
|
|
22705
|
+
fingerprint() {
|
|
22706
|
+
const names = Array.from(this.secrets.keys()).sort();
|
|
22707
|
+
const hash = createHash("sha256");
|
|
22708
|
+
for (const name of names) {
|
|
22709
|
+
hash.update(name + ":");
|
|
22710
|
+
hash.update(this.secrets.get(name).value);
|
|
22711
|
+
hash.update("\n");
|
|
22712
|
+
}
|
|
22713
|
+
return hash.digest("base64url").slice(0, 16);
|
|
22714
|
+
}
|
|
22715
|
+
};
|
|
22716
|
+
}
|
|
22717
|
+
});
|
|
22718
|
+
|
|
22719
|
+
// packages/cli/dist/tui/p2p/peer-mesh.js
|
|
22447
22720
|
import { EventEmitter as EventEmitter4 } from "node:events";
|
|
22721
|
+
import { createServer as createServer3 } from "node:http";
|
|
22722
|
+
import { randomBytes as randomBytes7, createHash as createHash2, generateKeyPairSync } from "node:crypto";
|
|
22723
|
+
var PING_INTERVAL_MS, PEER_TIMEOUT_MS, GOSSIP_INTERVAL_MS, MAX_PEERS, PeerMesh;
|
|
22724
|
+
var init_peer_mesh = __esm({
|
|
22725
|
+
"packages/cli/dist/tui/p2p/peer-mesh.js"() {
|
|
22726
|
+
"use strict";
|
|
22727
|
+
init_wrapper();
|
|
22728
|
+
PING_INTERVAL_MS = 3e4;
|
|
22729
|
+
PEER_TIMEOUT_MS = 9e4;
|
|
22730
|
+
GOSSIP_INTERVAL_MS = 6e4;
|
|
22731
|
+
MAX_PEERS = 50;
|
|
22732
|
+
PeerMesh = class extends EventEmitter4 {
|
|
22733
|
+
options;
|
|
22734
|
+
/** This node's unique peer ID (hash of public key) */
|
|
22735
|
+
peerId;
|
|
22736
|
+
/** Ed25519 keypair for identity */
|
|
22737
|
+
privateKey;
|
|
22738
|
+
publicKey;
|
|
22739
|
+
/** Known peers (peerId → PeerInfo) */
|
|
22740
|
+
peers = /* @__PURE__ */ new Map();
|
|
22741
|
+
/** Active WebSocket connections (peerId → WebSocket) */
|
|
22742
|
+
connections = /* @__PURE__ */ new Map();
|
|
22743
|
+
/** Pending inference request callbacks (msgId → resolver) */
|
|
22744
|
+
pendingRequests = /* @__PURE__ */ new Map();
|
|
22745
|
+
server = null;
|
|
22746
|
+
wss = null;
|
|
22747
|
+
pingTimer = null;
|
|
22748
|
+
gossipTimer = null;
|
|
22749
|
+
_port = 0;
|
|
22750
|
+
_authKey;
|
|
22751
|
+
capabilities;
|
|
22752
|
+
displayName;
|
|
22753
|
+
get port() {
|
|
22754
|
+
return this._port;
|
|
22755
|
+
}
|
|
22756
|
+
get authKey() {
|
|
22757
|
+
return this._authKey;
|
|
22758
|
+
}
|
|
22759
|
+
get peerCount() {
|
|
22760
|
+
return this.peers.size;
|
|
22761
|
+
}
|
|
22762
|
+
get connectedCount() {
|
|
22763
|
+
return this.connections.size;
|
|
22764
|
+
}
|
|
22765
|
+
constructor(options) {
|
|
22766
|
+
super();
|
|
22767
|
+
this.options = options;
|
|
22768
|
+
const { publicKey, privateKey } = generateKeyPairSync("ed25519");
|
|
22769
|
+
this.publicKey = publicKey.export({ type: "spki", format: "der" });
|
|
22770
|
+
this.privateKey = privateKey.export({ type: "pkcs8", format: "der" });
|
|
22771
|
+
this.peerId = createHash2("sha256").update(this.publicKey).digest("base64url").slice(0, 22);
|
|
22772
|
+
this.capabilities = options.capabilities;
|
|
22773
|
+
this.displayName = options.displayName;
|
|
22774
|
+
this._authKey = options.authKey ?? randomBytes7(24).toString("base64url");
|
|
22775
|
+
}
|
|
22776
|
+
// ── Lifecycle ───────────────────────────────────────────────────────────
|
|
22777
|
+
/** Start the mesh node — creates WS server and connects to bootstrap peers */
|
|
22778
|
+
async start() {
|
|
22779
|
+
const port = await this.findFreePort();
|
|
22780
|
+
this._port = port;
|
|
22781
|
+
this.server = createServer3();
|
|
22782
|
+
this.wss = new import_websocket_server.default({ server: this.server });
|
|
22783
|
+
this.wss.on("connection", (ws, req) => {
|
|
22784
|
+
this.handleInboundConnection(ws, req.url ?? "");
|
|
22785
|
+
});
|
|
22786
|
+
await new Promise((resolve28, reject) => {
|
|
22787
|
+
this.server.listen(port, "127.0.0.1", () => resolve28());
|
|
22788
|
+
this.server.on("error", reject);
|
|
22789
|
+
});
|
|
22790
|
+
this.pingTimer = setInterval(() => this.pingAll(), PING_INTERVAL_MS);
|
|
22791
|
+
this.gossipTimer = setInterval(() => this.gossip(), GOSSIP_INTERVAL_MS);
|
|
22792
|
+
if (this.options.bootstrapPeers) {
|
|
22793
|
+
for (const endpoint of this.options.bootstrapPeers) {
|
|
22794
|
+
this.connectToPeer(endpoint).catch(() => {
|
|
22795
|
+
});
|
|
22796
|
+
}
|
|
22797
|
+
}
|
|
22798
|
+
this.emit("started", port);
|
|
22799
|
+
return port;
|
|
22800
|
+
}
|
|
22801
|
+
/** Stop the mesh node */
|
|
22802
|
+
async stop() {
|
|
22803
|
+
if (this.pingTimer) {
|
|
22804
|
+
clearInterval(this.pingTimer);
|
|
22805
|
+
this.pingTimer = null;
|
|
22806
|
+
}
|
|
22807
|
+
if (this.gossipTimer) {
|
|
22808
|
+
clearInterval(this.gossipTimer);
|
|
22809
|
+
this.gossipTimer = null;
|
|
22810
|
+
}
|
|
22811
|
+
for (const [peerId, ws] of this.connections) {
|
|
22812
|
+
this.sendMsg(ws, "bye", {});
|
|
22813
|
+
ws.close(1e3, "shutting down");
|
|
22814
|
+
}
|
|
22815
|
+
this.connections.clear();
|
|
22816
|
+
for (const [, pending] of this.pendingRequests) {
|
|
22817
|
+
clearTimeout(pending.timeout);
|
|
22818
|
+
pending.reject(new Error("Mesh shutting down"));
|
|
22819
|
+
}
|
|
22820
|
+
this.pendingRequests.clear();
|
|
22821
|
+
if (this.wss) {
|
|
22822
|
+
this.wss.close();
|
|
22823
|
+
this.wss = null;
|
|
22824
|
+
}
|
|
22825
|
+
if (this.server) {
|
|
22826
|
+
await new Promise((resolve28) => this.server.close(() => resolve28()));
|
|
22827
|
+
this.server = null;
|
|
22828
|
+
}
|
|
22829
|
+
this.emit("stopped");
|
|
22830
|
+
}
|
|
22831
|
+
// ── Peer connections ────────────────────────────────────────────────────
|
|
22832
|
+
/** Connect to a remote peer by endpoint URL */
|
|
22833
|
+
async connectToPeer(endpoint, trust = "public") {
|
|
22834
|
+
let wsUrl = endpoint;
|
|
22835
|
+
if (wsUrl.startsWith("https://"))
|
|
22836
|
+
wsUrl = wsUrl.replace("https://", "wss://");
|
|
22837
|
+
else if (wsUrl.startsWith("http://"))
|
|
22838
|
+
wsUrl = wsUrl.replace("http://", "ws://");
|
|
22839
|
+
else if (!wsUrl.startsWith("ws://") && !wsUrl.startsWith("wss://"))
|
|
22840
|
+
wsUrl = `ws://${wsUrl}`;
|
|
22841
|
+
if (!wsUrl.includes("/p2p"))
|
|
22842
|
+
wsUrl += "/p2p";
|
|
22843
|
+
wsUrl += `?key=${encodeURIComponent(this._authKey)}`;
|
|
22844
|
+
return new Promise((resolve28, reject) => {
|
|
22845
|
+
const ws = new import_websocket.default(wsUrl, { handshakeTimeout: 1e4 });
|
|
22846
|
+
let resolved = false;
|
|
22847
|
+
const timeout = setTimeout(() => {
|
|
22848
|
+
if (!resolved) {
|
|
22849
|
+
resolved = true;
|
|
22850
|
+
ws.close();
|
|
22851
|
+
reject(new Error("Connection timeout"));
|
|
22852
|
+
}
|
|
22853
|
+
}, 15e3);
|
|
22854
|
+
ws.on("open", () => {
|
|
22855
|
+
this.sendMsg(ws, "hello", {
|
|
22856
|
+
peerId: this.peerId,
|
|
22857
|
+
displayName: this.displayName,
|
|
22858
|
+
capabilities: this.capabilities,
|
|
22859
|
+
endpoint,
|
|
22860
|
+
publicKey: this.publicKey.toString("base64url")
|
|
22861
|
+
});
|
|
22862
|
+
});
|
|
22863
|
+
ws.on("message", (data) => {
|
|
22864
|
+
try {
|
|
22865
|
+
const msg = JSON.parse(data.toString());
|
|
22866
|
+
if (msg.type === "hello_ack" && !resolved) {
|
|
22867
|
+
resolved = true;
|
|
22868
|
+
clearTimeout(timeout);
|
|
22869
|
+
const payload = msg.payload;
|
|
22870
|
+
const peer = {
|
|
22871
|
+
peerId: payload.peerId,
|
|
22872
|
+
displayName: payload.displayName,
|
|
22873
|
+
endpoint,
|
|
22874
|
+
trust,
|
|
22875
|
+
capabilities: payload.capabilities,
|
|
22876
|
+
lastSeen: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22877
|
+
latencyMs: 0,
|
|
22878
|
+
connected: true
|
|
22879
|
+
};
|
|
22880
|
+
this.peers.set(peer.peerId, peer);
|
|
22881
|
+
this.connections.set(peer.peerId, ws);
|
|
22882
|
+
this.setupPeerHandlers(ws, peer.peerId);
|
|
22883
|
+
this.emit("peer_connected", peer);
|
|
22884
|
+
resolve28(peer);
|
|
22885
|
+
} else {
|
|
22886
|
+
this.handleMessage(msg, ws);
|
|
22887
|
+
}
|
|
22888
|
+
} catch {
|
|
22889
|
+
}
|
|
22890
|
+
});
|
|
22891
|
+
ws.on("error", (err) => {
|
|
22892
|
+
if (!resolved) {
|
|
22893
|
+
resolved = true;
|
|
22894
|
+
clearTimeout(timeout);
|
|
22895
|
+
reject(err);
|
|
22896
|
+
}
|
|
22897
|
+
});
|
|
22898
|
+
ws.on("close", () => {
|
|
22899
|
+
if (!resolved) {
|
|
22900
|
+
resolved = true;
|
|
22901
|
+
clearTimeout(timeout);
|
|
22902
|
+
reject(new Error("Connection closed before handshake"));
|
|
22903
|
+
}
|
|
22904
|
+
});
|
|
22905
|
+
});
|
|
22906
|
+
}
|
|
22907
|
+
/** Set the trust tier for a known peer */
|
|
22908
|
+
setTrust(peerId, trust) {
|
|
22909
|
+
const peer = this.peers.get(peerId);
|
|
22910
|
+
if (peer) {
|
|
22911
|
+
peer.trust = trust;
|
|
22912
|
+
return true;
|
|
22913
|
+
}
|
|
22914
|
+
return false;
|
|
22915
|
+
}
|
|
22916
|
+
/** Get all known peers */
|
|
22917
|
+
getPeers() {
|
|
22918
|
+
return Array.from(this.peers.values());
|
|
22919
|
+
}
|
|
22920
|
+
/** Get connected peers */
|
|
22921
|
+
getConnectedPeers() {
|
|
22922
|
+
return Array.from(this.peers.values()).filter((p) => p.connected);
|
|
22923
|
+
}
|
|
22924
|
+
/** Find peers that can serve a given model */
|
|
22925
|
+
findPeersForModel(model) {
|
|
22926
|
+
return this.getConnectedPeers().filter((p) => p.capabilities.models.some((m) => m === model || m.startsWith(model)));
|
|
22927
|
+
}
|
|
22928
|
+
// ── Inference routing ──────────────────────────────────────────────────
|
|
22929
|
+
/**
|
|
22930
|
+
* Send an inference request to a specific peer.
|
|
22931
|
+
* Returns a promise that resolves when the response arrives.
|
|
22932
|
+
*/
|
|
22933
|
+
async requestInference(peerId, request, timeoutMs = 12e4) {
|
|
22934
|
+
const ws = this.connections.get(peerId);
|
|
22935
|
+
if (!ws || ws.readyState !== import_websocket.default.OPEN) {
|
|
22936
|
+
throw new Error(`Peer ${peerId} not connected`);
|
|
22937
|
+
}
|
|
22938
|
+
const msgId = randomBytes7(8).toString("hex");
|
|
22939
|
+
return new Promise((resolve28, reject) => {
|
|
22940
|
+
const timeout = setTimeout(() => {
|
|
22941
|
+
this.pendingRequests.delete(msgId);
|
|
22942
|
+
reject(new Error(`Inference timeout (${timeoutMs}ms)`));
|
|
22943
|
+
}, timeoutMs);
|
|
22944
|
+
this.pendingRequests.set(msgId, { resolve: resolve28, reject, timeout, chunks: [] });
|
|
22945
|
+
this.sendMsg(ws, "infer_request", request, msgId);
|
|
22946
|
+
});
|
|
22947
|
+
}
|
|
22948
|
+
// ── Inbound connection handling ─────────────────────────────────────────
|
|
22949
|
+
handleInboundConnection(ws, url) {
|
|
22950
|
+
const urlObj = new URL(url, "http://localhost");
|
|
22951
|
+
const key = urlObj.searchParams.get("key");
|
|
22952
|
+
if (key !== this._authKey) {
|
|
22953
|
+
ws.close(4001, "Unauthorized");
|
|
22954
|
+
return;
|
|
22955
|
+
}
|
|
22956
|
+
const helloTimeout = setTimeout(() => ws.close(4002, "Hello timeout"), 1e4);
|
|
22957
|
+
ws.once("message", (data) => {
|
|
22958
|
+
clearTimeout(helloTimeout);
|
|
22959
|
+
try {
|
|
22960
|
+
const msg = JSON.parse(data.toString());
|
|
22961
|
+
if (msg.type !== "hello") {
|
|
22962
|
+
ws.close(4003, "Expected hello");
|
|
22963
|
+
return;
|
|
22964
|
+
}
|
|
22965
|
+
const payload = msg.payload;
|
|
22966
|
+
if (payload.peerId === this.peerId) {
|
|
22967
|
+
ws.close(4004, "Self-connection");
|
|
22968
|
+
return;
|
|
22969
|
+
}
|
|
22970
|
+
const peer = {
|
|
22971
|
+
peerId: payload.peerId,
|
|
22972
|
+
displayName: payload.displayName,
|
|
22973
|
+
endpoint: payload.endpoint,
|
|
22974
|
+
trust: "public",
|
|
22975
|
+
// inbound connections start at public trust
|
|
22976
|
+
capabilities: payload.capabilities,
|
|
22977
|
+
lastSeen: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22978
|
+
latencyMs: 0,
|
|
22979
|
+
connected: true
|
|
22980
|
+
};
|
|
22981
|
+
const existing = this.peers.get(peer.peerId);
|
|
22982
|
+
if (existing) {
|
|
22983
|
+
peer.trust = existing.trust;
|
|
22984
|
+
}
|
|
22985
|
+
this.peers.set(peer.peerId, peer);
|
|
22986
|
+
this.connections.set(peer.peerId, ws);
|
|
22987
|
+
this.sendMsg(ws, "hello_ack", {
|
|
22988
|
+
peerId: this.peerId,
|
|
22989
|
+
displayName: this.displayName,
|
|
22990
|
+
capabilities: this.capabilities
|
|
22991
|
+
});
|
|
22992
|
+
this.setupPeerHandlers(ws, peer.peerId);
|
|
22993
|
+
this.emit("peer_connected", peer);
|
|
22994
|
+
} catch {
|
|
22995
|
+
ws.close(4005, "Malformed hello");
|
|
22996
|
+
}
|
|
22997
|
+
});
|
|
22998
|
+
}
|
|
22999
|
+
setupPeerHandlers(ws, peerId) {
|
|
23000
|
+
ws.on("message", (data) => {
|
|
23001
|
+
try {
|
|
23002
|
+
const msg = JSON.parse(data.toString());
|
|
23003
|
+
this.handleMessage(msg, ws);
|
|
23004
|
+
} catch {
|
|
23005
|
+
}
|
|
23006
|
+
});
|
|
23007
|
+
ws.on("close", () => {
|
|
23008
|
+
this.connections.delete(peerId);
|
|
23009
|
+
const peer = this.peers.get(peerId);
|
|
23010
|
+
if (peer) {
|
|
23011
|
+
peer.connected = false;
|
|
23012
|
+
this.emit("peer_disconnected", peer);
|
|
23013
|
+
}
|
|
23014
|
+
});
|
|
23015
|
+
ws.on("error", () => {
|
|
23016
|
+
this.connections.delete(peerId);
|
|
23017
|
+
const peer = this.peers.get(peerId);
|
|
23018
|
+
if (peer) {
|
|
23019
|
+
peer.connected = false;
|
|
23020
|
+
this.emit("peer_error", peer);
|
|
23021
|
+
}
|
|
23022
|
+
});
|
|
23023
|
+
}
|
|
23024
|
+
// ── Message handling ────────────────────────────────────────────────────
|
|
23025
|
+
handleMessage(msg, ws) {
|
|
23026
|
+
const peer = this.peers.get(msg.from);
|
|
23027
|
+
if (peer)
|
|
23028
|
+
peer.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
|
|
23029
|
+
switch (msg.type) {
|
|
23030
|
+
case "ping":
|
|
23031
|
+
this.sendMsg(ws, "pong", { ts: msg.ts }, msg.msgId);
|
|
23032
|
+
break;
|
|
23033
|
+
case "pong":
|
|
23034
|
+
if (peer) {
|
|
23035
|
+
const sent = new Date(msg.ts).getTime();
|
|
23036
|
+
peer.latencyMs = Date.now() - sent;
|
|
23037
|
+
}
|
|
23038
|
+
break;
|
|
23039
|
+
case "capabilities":
|
|
23040
|
+
if (peer) {
|
|
23041
|
+
peer.capabilities = msg.payload;
|
|
23042
|
+
this.emit("peer_capabilities", peer);
|
|
23043
|
+
}
|
|
23044
|
+
break;
|
|
23045
|
+
case "peer_list": {
|
|
23046
|
+
const peerList = msg.payload;
|
|
23047
|
+
for (const p of peerList) {
|
|
23048
|
+
if (p.peerId === this.peerId)
|
|
23049
|
+
continue;
|
|
23050
|
+
if (this.peers.has(p.peerId))
|
|
23051
|
+
continue;
|
|
23052
|
+
if (this.peers.size >= MAX_PEERS)
|
|
23053
|
+
break;
|
|
23054
|
+
this.connectToPeer(p.endpoint).catch(() => {
|
|
23055
|
+
});
|
|
23056
|
+
}
|
|
23057
|
+
break;
|
|
23058
|
+
}
|
|
23059
|
+
case "infer_request":
|
|
23060
|
+
this.emit("infer_request", {
|
|
23061
|
+
from: msg.from,
|
|
23062
|
+
msgId: msg.msgId,
|
|
23063
|
+
request: msg.payload,
|
|
23064
|
+
respond: (response) => {
|
|
23065
|
+
this.sendMsg(ws, "infer_response", response, msg.msgId);
|
|
23066
|
+
},
|
|
23067
|
+
error: (error) => {
|
|
23068
|
+
this.sendMsg(ws, "infer_error", { error }, msg.msgId);
|
|
23069
|
+
}
|
|
23070
|
+
});
|
|
23071
|
+
break;
|
|
23072
|
+
case "infer_response": {
|
|
23073
|
+
const pending = this.pendingRequests.get(msg.msgId);
|
|
23074
|
+
if (pending) {
|
|
23075
|
+
clearTimeout(pending.timeout);
|
|
23076
|
+
this.pendingRequests.delete(msg.msgId);
|
|
23077
|
+
pending.resolve(msg.payload);
|
|
23078
|
+
}
|
|
23079
|
+
break;
|
|
23080
|
+
}
|
|
23081
|
+
case "infer_stream": {
|
|
23082
|
+
const streamPending = this.pendingRequests.get(msg.msgId);
|
|
23083
|
+
if (streamPending) {
|
|
23084
|
+
const chunk = msg.payload;
|
|
23085
|
+
streamPending.chunks.push(chunk.content);
|
|
23086
|
+
this.emit("infer_stream", { msgId: msg.msgId, chunk });
|
|
23087
|
+
if (chunk.done) {
|
|
23088
|
+
clearTimeout(streamPending.timeout);
|
|
23089
|
+
this.pendingRequests.delete(msg.msgId);
|
|
23090
|
+
streamPending.resolve({
|
|
23091
|
+
content: streamPending.chunks.join(""),
|
|
23092
|
+
usage: chunk.usage,
|
|
23093
|
+
done: true
|
|
23094
|
+
});
|
|
23095
|
+
}
|
|
23096
|
+
}
|
|
23097
|
+
break;
|
|
23098
|
+
}
|
|
23099
|
+
case "infer_error": {
|
|
23100
|
+
const errPending = this.pendingRequests.get(msg.msgId);
|
|
23101
|
+
if (errPending) {
|
|
23102
|
+
clearTimeout(errPending.timeout);
|
|
23103
|
+
this.pendingRequests.delete(msg.msgId);
|
|
23104
|
+
const payload = msg.payload;
|
|
23105
|
+
errPending.reject(new Error(payload.error));
|
|
23106
|
+
}
|
|
23107
|
+
break;
|
|
23108
|
+
}
|
|
23109
|
+
case "bye":
|
|
23110
|
+
ws.close(1e3, "Peer disconnected");
|
|
23111
|
+
break;
|
|
23112
|
+
default:
|
|
23113
|
+
break;
|
|
23114
|
+
}
|
|
23115
|
+
}
|
|
23116
|
+
// ── Heartbeat & gossip ─────────────────────────────────────────────────
|
|
23117
|
+
pingAll() {
|
|
23118
|
+
const now = Date.now();
|
|
23119
|
+
for (const [peerId, ws] of this.connections) {
|
|
23120
|
+
if (ws.readyState === import_websocket.default.OPEN) {
|
|
23121
|
+
this.sendMsg(ws, "ping", { ts: (/* @__PURE__ */ new Date()).toISOString() });
|
|
23122
|
+
}
|
|
23123
|
+
const peer = this.peers.get(peerId);
|
|
23124
|
+
if (peer) {
|
|
23125
|
+
const lastSeen = new Date(peer.lastSeen).getTime();
|
|
23126
|
+
if (now - lastSeen > PEER_TIMEOUT_MS) {
|
|
23127
|
+
peer.connected = false;
|
|
23128
|
+
ws.close(4006, "Timeout");
|
|
23129
|
+
this.connections.delete(peerId);
|
|
23130
|
+
this.emit("peer_timeout", peer);
|
|
23131
|
+
}
|
|
23132
|
+
}
|
|
23133
|
+
}
|
|
23134
|
+
}
|
|
23135
|
+
gossip() {
|
|
23136
|
+
const peerList = Array.from(this.peers.values()).filter((p) => p.connected).map((p) => ({ endpoint: p.endpoint, peerId: p.peerId, displayName: p.displayName }));
|
|
23137
|
+
for (const [, ws] of this.connections) {
|
|
23138
|
+
if (ws.readyState === import_websocket.default.OPEN) {
|
|
23139
|
+
this.sendMsg(ws, "peer_list", peerList);
|
|
23140
|
+
}
|
|
23141
|
+
}
|
|
23142
|
+
}
|
|
23143
|
+
/** Update this node's capabilities and broadcast to all peers */
|
|
23144
|
+
updateCapabilities(caps) {
|
|
23145
|
+
Object.assign(this.capabilities, caps);
|
|
23146
|
+
for (const [, ws] of this.connections) {
|
|
23147
|
+
if (ws.readyState === import_websocket.default.OPEN) {
|
|
23148
|
+
this.sendMsg(ws, "capabilities", this.capabilities);
|
|
23149
|
+
}
|
|
23150
|
+
}
|
|
23151
|
+
}
|
|
23152
|
+
// ── Helpers ─────────────────────────────────────────────────────────────
|
|
23153
|
+
sendMsg(ws, type, payload, msgId) {
|
|
23154
|
+
if (ws.readyState !== import_websocket.default.OPEN)
|
|
23155
|
+
return;
|
|
23156
|
+
const msg = {
|
|
23157
|
+
type,
|
|
23158
|
+
from: this.peerId,
|
|
23159
|
+
msgId: msgId ?? randomBytes7(8).toString("hex"),
|
|
23160
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
23161
|
+
payload
|
|
23162
|
+
};
|
|
23163
|
+
ws.send(JSON.stringify(msg));
|
|
23164
|
+
}
|
|
23165
|
+
findFreePort() {
|
|
23166
|
+
return new Promise((resolve28, reject) => {
|
|
23167
|
+
const srv = createServer3();
|
|
23168
|
+
srv.listen(0, "127.0.0.1", () => {
|
|
23169
|
+
const addr = srv.address();
|
|
23170
|
+
if (addr && typeof addr === "object") {
|
|
23171
|
+
const port = addr.port;
|
|
23172
|
+
srv.close(() => resolve28(port));
|
|
23173
|
+
} else {
|
|
23174
|
+
srv.close(() => reject(new Error("Could not find free port")));
|
|
23175
|
+
}
|
|
23176
|
+
});
|
|
23177
|
+
srv.on("error", reject);
|
|
23178
|
+
});
|
|
23179
|
+
}
|
|
23180
|
+
};
|
|
23181
|
+
}
|
|
23182
|
+
});
|
|
23183
|
+
|
|
23184
|
+
// packages/cli/dist/tui/p2p/inference-router.js
|
|
23185
|
+
import { EventEmitter as EventEmitter5 } from "node:events";
|
|
23186
|
+
var TRUST_WEIGHTS, InferenceRouter;
|
|
23187
|
+
var init_inference_router = __esm({
|
|
23188
|
+
"packages/cli/dist/tui/p2p/inference-router.js"() {
|
|
23189
|
+
"use strict";
|
|
23190
|
+
init_types();
|
|
23191
|
+
TRUST_WEIGHTS = {
|
|
23192
|
+
local: 10,
|
|
23193
|
+
tee: 8,
|
|
23194
|
+
verified: 5,
|
|
23195
|
+
public: 2
|
|
23196
|
+
};
|
|
23197
|
+
InferenceRouter = class extends EventEmitter5 {
|
|
23198
|
+
mesh;
|
|
23199
|
+
vault;
|
|
23200
|
+
defaultTimeoutMs;
|
|
23201
|
+
minTrust;
|
|
23202
|
+
/** Stats */
|
|
23203
|
+
_totalRouted = 0;
|
|
23204
|
+
_totalRedacted = 0;
|
|
23205
|
+
_totalErrors = 0;
|
|
23206
|
+
get totalRouted() {
|
|
23207
|
+
return this._totalRouted;
|
|
23208
|
+
}
|
|
23209
|
+
get totalRedacted() {
|
|
23210
|
+
return this._totalRedacted;
|
|
23211
|
+
}
|
|
23212
|
+
get totalErrors() {
|
|
23213
|
+
return this._totalErrors;
|
|
23214
|
+
}
|
|
23215
|
+
constructor(options) {
|
|
23216
|
+
super();
|
|
23217
|
+
this.mesh = options.mesh;
|
|
23218
|
+
this.vault = options.vault;
|
|
23219
|
+
this.defaultTimeoutMs = options.defaultTimeoutMs ?? 12e4;
|
|
23220
|
+
this.minTrust = options.minTrust ?? "public";
|
|
23221
|
+
if (options.requireAuth) {
|
|
23222
|
+
this.minTrust = "verified";
|
|
23223
|
+
}
|
|
23224
|
+
}
|
|
23225
|
+
// ── Route discovery ─────────────────────────────────────────────────────
|
|
23226
|
+
/**
|
|
23227
|
+
* Find all available routes for a given model, ranked by score.
|
|
23228
|
+
*/
|
|
23229
|
+
findRoutes(model) {
|
|
23230
|
+
const peers = this.mesh.findPeersForModel(model);
|
|
23231
|
+
const minLevel = trustLevel(this.minTrust);
|
|
23232
|
+
return peers.filter((p) => trustLevel(p.trust) >= minLevel).map((p) => this.scoreRoute(p, model)).sort((a, b) => b.score - a.score);
|
|
23233
|
+
}
|
|
23234
|
+
/**
|
|
23235
|
+
* Find the best route for a given model.
|
|
23236
|
+
*/
|
|
23237
|
+
bestRoute(model) {
|
|
23238
|
+
const routes = this.findRoutes(model);
|
|
23239
|
+
return routes.length > 0 ? routes[0] : null;
|
|
23240
|
+
}
|
|
23241
|
+
// ── Inference ──────────────────────────────────────────────────────────
|
|
23242
|
+
/**
|
|
23243
|
+
* Route an inference request to the best available peer.
|
|
23244
|
+
*
|
|
23245
|
+
* This is the main entry point. It handles:
|
|
23246
|
+
* 1. Peer selection
|
|
23247
|
+
* 2. Secret redaction based on peer trust
|
|
23248
|
+
* 3. Request dispatch
|
|
23249
|
+
* 4. Secret injection on response
|
|
23250
|
+
*/
|
|
23251
|
+
async infer(model, messages, options) {
|
|
23252
|
+
let peer;
|
|
23253
|
+
if (options?.peerId) {
|
|
23254
|
+
const found = this.mesh.getPeers().find((p) => p.peerId === options.peerId);
|
|
23255
|
+
if (!found || !found.connected) {
|
|
23256
|
+
throw new Error(`Peer ${options.peerId} not available`);
|
|
23257
|
+
}
|
|
23258
|
+
peer = found;
|
|
23259
|
+
} else {
|
|
23260
|
+
const minTrust = options?.minTrust ?? this.minTrust;
|
|
23261
|
+
const routes = this.findRoutes(model);
|
|
23262
|
+
const filtered = routes.filter((r) => trustLevel(r.trust) >= trustLevel(minTrust));
|
|
23263
|
+
if (filtered.length === 0) {
|
|
23264
|
+
throw new Error(`No available peers for model "${model}" at trust >= ${minTrust}`);
|
|
23265
|
+
}
|
|
23266
|
+
peer = filtered[0].peer;
|
|
23267
|
+
}
|
|
23268
|
+
const requiresRedaction = trustLevel(peer.trust) < trustLevel("local");
|
|
23269
|
+
let redactedCount = 0;
|
|
23270
|
+
const redactedMessages = messages.map((msg) => {
|
|
23271
|
+
const original = msg.content;
|
|
23272
|
+
const redacted = requiresRedaction ? this.vault.redact(original, peer.trust) : original;
|
|
23273
|
+
if (redacted !== original)
|
|
23274
|
+
redactedCount++;
|
|
23275
|
+
return { ...msg, content: redacted };
|
|
23276
|
+
});
|
|
23277
|
+
let redactedTools = options?.tools;
|
|
23278
|
+
if (requiresRedaction && redactedTools) {
|
|
23279
|
+
redactedTools = redactedTools.map((t) => ({
|
|
23280
|
+
...t,
|
|
23281
|
+
function: {
|
|
23282
|
+
...t.function,
|
|
23283
|
+
description: this.vault.redact(t.function.description, peer.trust)
|
|
23284
|
+
}
|
|
23285
|
+
}));
|
|
23286
|
+
}
|
|
23287
|
+
const request = {
|
|
23288
|
+
model,
|
|
23289
|
+
messages: redactedMessages,
|
|
23290
|
+
tools: redactedTools,
|
|
23291
|
+
temperature: options?.temperature,
|
|
23292
|
+
maxTokens: options?.maxTokens,
|
|
23293
|
+
stream: options?.stream
|
|
23294
|
+
};
|
|
23295
|
+
this.emit("routing", { peer, redactedCount, model });
|
|
23296
|
+
this._totalRouted++;
|
|
23297
|
+
if (redactedCount > 0)
|
|
23298
|
+
this._totalRedacted++;
|
|
23299
|
+
try {
|
|
23300
|
+
const response = await this.mesh.requestInference(peer.peerId, request, options?.timeoutMs ?? this.defaultTimeoutMs);
|
|
23301
|
+
const injectedContent = this.vault.inject(response.content);
|
|
23302
|
+
const injectedToolCalls = response.toolCalls?.map((tc) => ({
|
|
23303
|
+
...tc,
|
|
23304
|
+
function: {
|
|
23305
|
+
...tc.function,
|
|
23306
|
+
arguments: this.vault.inject(tc.function.arguments)
|
|
23307
|
+
}
|
|
23308
|
+
}));
|
|
23309
|
+
const result = {
|
|
23310
|
+
content: injectedContent,
|
|
23311
|
+
toolCalls: injectedToolCalls,
|
|
23312
|
+
usage: response.usage,
|
|
23313
|
+
peer,
|
|
23314
|
+
wasRedacted: redactedCount > 0,
|
|
23315
|
+
redactedCount
|
|
23316
|
+
};
|
|
23317
|
+
this.emit("completed", result);
|
|
23318
|
+
return result;
|
|
23319
|
+
} catch (err) {
|
|
23320
|
+
this._totalErrors++;
|
|
23321
|
+
this.emit("error", { peer, error: err });
|
|
23322
|
+
throw err;
|
|
23323
|
+
}
|
|
23324
|
+
}
|
|
23325
|
+
/**
|
|
23326
|
+
* Handle an inbound inference request from a peer.
|
|
23327
|
+
* Called by the mesh node when it receives an infer_request.
|
|
23328
|
+
*
|
|
23329
|
+
* This is the "server side" — we perform inference locally and return results.
|
|
23330
|
+
* The response text may contain placeholders from the requester's vault,
|
|
23331
|
+
* which is fine — we don't have their secrets and don't need them.
|
|
23332
|
+
*/
|
|
23333
|
+
async handleInboundRequest(request, inferFn) {
|
|
23334
|
+
for (const msg of request.messages) {
|
|
23335
|
+
const found = this.vault.scan(msg.content);
|
|
23336
|
+
if (found.length > 0) {
|
|
23337
|
+
this.emit("leaked_secrets_detected", {
|
|
23338
|
+
count: found.length,
|
|
23339
|
+
patterns: found.map((f) => f.slice(0, 4) + "***")
|
|
23340
|
+
});
|
|
23341
|
+
}
|
|
23342
|
+
}
|
|
23343
|
+
return inferFn(request);
|
|
23344
|
+
}
|
|
23345
|
+
// ── Scoring ────────────────────────────────────────────────────────────
|
|
23346
|
+
scoreRoute(peer, model) {
|
|
23347
|
+
const trust = peer.trust;
|
|
23348
|
+
const trustWeight = TRUST_WEIGHTS[trust];
|
|
23349
|
+
const latencyFactor = 1 / (1 + peer.latencyMs / 100);
|
|
23350
|
+
const loadFactor = 1 - peer.capabilities.load;
|
|
23351
|
+
const modelExact = peer.capabilities.models.includes(model) ? 1 : 0.8;
|
|
23352
|
+
const score = trustWeight * latencyFactor * loadFactor * modelExact;
|
|
23353
|
+
return {
|
|
23354
|
+
peer,
|
|
23355
|
+
trust,
|
|
23356
|
+
latencyMs: peer.latencyMs,
|
|
23357
|
+
requiresRedaction: trustLevel(trust) < trustLevel("local"),
|
|
23358
|
+
score
|
|
23359
|
+
};
|
|
23360
|
+
}
|
|
23361
|
+
// ── Utility ────────────────────────────────────────────────────────────
|
|
23362
|
+
/** Get a summary of routing statistics */
|
|
23363
|
+
getStats() {
|
|
23364
|
+
const peers = this.mesh.getConnectedPeers();
|
|
23365
|
+
const models = /* @__PURE__ */ new Set();
|
|
23366
|
+
for (const p of peers) {
|
|
23367
|
+
for (const m of p.capabilities.models)
|
|
23368
|
+
models.add(m);
|
|
23369
|
+
}
|
|
23370
|
+
return {
|
|
23371
|
+
totalRouted: this._totalRouted,
|
|
23372
|
+
totalRedacted: this._totalRedacted,
|
|
23373
|
+
totalErrors: this._totalErrors,
|
|
23374
|
+
connectedPeers: peers.length,
|
|
23375
|
+
availableModels: Array.from(models)
|
|
23376
|
+
};
|
|
23377
|
+
}
|
|
23378
|
+
};
|
|
23379
|
+
}
|
|
23380
|
+
});
|
|
23381
|
+
|
|
23382
|
+
// packages/cli/dist/tui/p2p/index.js
|
|
23383
|
+
var init_p2p = __esm({
|
|
23384
|
+
"packages/cli/dist/tui/p2p/index.js"() {
|
|
23385
|
+
"use strict";
|
|
23386
|
+
init_secret_vault();
|
|
23387
|
+
init_peer_mesh();
|
|
23388
|
+
init_inference_router();
|
|
23389
|
+
init_types();
|
|
23390
|
+
}
|
|
23391
|
+
});
|
|
23392
|
+
|
|
23393
|
+
// packages/cli/dist/tui/call-agent.js
|
|
23394
|
+
import { EventEmitter as EventEmitter6 } from "node:events";
|
|
22448
23395
|
import crypto2 from "node:crypto";
|
|
22449
23396
|
function adaptTool(tool) {
|
|
22450
23397
|
return {
|
|
@@ -22504,7 +23451,7 @@ var init_call_agent = __esm({
|
|
|
22504
23451
|
}
|
|
22505
23452
|
};
|
|
22506
23453
|
_globalFeed = null;
|
|
22507
|
-
CallSubAgent = class extends
|
|
23454
|
+
CallSubAgent = class extends EventEmitter6 {
|
|
22508
23455
|
tier;
|
|
22509
23456
|
clientId;
|
|
22510
23457
|
runner = null;
|
|
@@ -23105,7 +24052,7 @@ Approach this task thoughtfully:
|
|
|
23105
24052
|
});
|
|
23106
24053
|
|
|
23107
24054
|
// packages/prompts/dist/index.js
|
|
23108
|
-
import { join as
|
|
24055
|
+
import { join as join30, dirname as dirname12 } from "node:path";
|
|
23109
24056
|
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
23110
24057
|
var _dir, _packageRoot;
|
|
23111
24058
|
var init_dist6 = __esm({
|
|
@@ -23115,27 +24062,27 @@ var init_dist6 = __esm({
|
|
|
23115
24062
|
init_render2();
|
|
23116
24063
|
init_task_templates();
|
|
23117
24064
|
init_render2();
|
|
23118
|
-
_dir =
|
|
23119
|
-
_packageRoot =
|
|
24065
|
+
_dir = dirname12(fileURLToPath8(import.meta.url));
|
|
24066
|
+
_packageRoot = join30(_dir, "..");
|
|
23120
24067
|
}
|
|
23121
24068
|
});
|
|
23122
24069
|
|
|
23123
24070
|
// packages/cli/dist/tui/oa-directory.js
|
|
23124
|
-
import { existsSync as
|
|
23125
|
-
import { join as
|
|
24071
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync8, readFileSync as readFileSync16, writeFileSync as writeFileSync8, readdirSync as readdirSync7, statSync as statSync9, unlinkSync as unlinkSync3 } from "node:fs";
|
|
24072
|
+
import { join as join31, relative as relative2, basename as basename9, extname as extname8 } from "node:path";
|
|
23126
24073
|
import { homedir as homedir9 } from "node:os";
|
|
23127
24074
|
function initOaDirectory(repoRoot) {
|
|
23128
|
-
const oaPath =
|
|
24075
|
+
const oaPath = join31(repoRoot, OA_DIR);
|
|
23129
24076
|
for (const sub of SUBDIRS) {
|
|
23130
|
-
|
|
24077
|
+
mkdirSync8(join31(oaPath, sub), { recursive: true });
|
|
23131
24078
|
}
|
|
23132
24079
|
try {
|
|
23133
|
-
const gitignorePath =
|
|
24080
|
+
const gitignorePath = join31(repoRoot, ".gitignore");
|
|
23134
24081
|
const settingsPattern = ".oa/settings.json";
|
|
23135
|
-
if (
|
|
23136
|
-
const content =
|
|
24082
|
+
if (existsSync22(gitignorePath)) {
|
|
24083
|
+
const content = readFileSync16(gitignorePath, "utf-8");
|
|
23137
24084
|
if (!content.includes(settingsPattern)) {
|
|
23138
|
-
|
|
24085
|
+
writeFileSync8(gitignorePath, content.trimEnd() + "\n" + settingsPattern + "\n", "utf-8");
|
|
23139
24086
|
}
|
|
23140
24087
|
}
|
|
23141
24088
|
} catch {
|
|
@@ -23143,41 +24090,41 @@ function initOaDirectory(repoRoot) {
|
|
|
23143
24090
|
return oaPath;
|
|
23144
24091
|
}
|
|
23145
24092
|
function hasOaDirectory(repoRoot) {
|
|
23146
|
-
return
|
|
24093
|
+
return existsSync22(join31(repoRoot, OA_DIR, "index"));
|
|
23147
24094
|
}
|
|
23148
24095
|
function loadProjectSettings(repoRoot) {
|
|
23149
|
-
const settingsPath =
|
|
24096
|
+
const settingsPath = join31(repoRoot, OA_DIR, "settings.json");
|
|
23150
24097
|
try {
|
|
23151
|
-
if (
|
|
23152
|
-
return JSON.parse(
|
|
24098
|
+
if (existsSync22(settingsPath)) {
|
|
24099
|
+
return JSON.parse(readFileSync16(settingsPath, "utf-8"));
|
|
23153
24100
|
}
|
|
23154
24101
|
} catch {
|
|
23155
24102
|
}
|
|
23156
24103
|
return {};
|
|
23157
24104
|
}
|
|
23158
24105
|
function saveProjectSettings(repoRoot, settings) {
|
|
23159
|
-
const oaPath =
|
|
23160
|
-
|
|
24106
|
+
const oaPath = join31(repoRoot, OA_DIR);
|
|
24107
|
+
mkdirSync8(oaPath, { recursive: true });
|
|
23161
24108
|
const existing = loadProjectSettings(repoRoot);
|
|
23162
24109
|
const merged = { ...existing, ...settings };
|
|
23163
|
-
|
|
24110
|
+
writeFileSync8(join31(oaPath, "settings.json"), JSON.stringify(merged, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
|
|
23164
24111
|
}
|
|
23165
24112
|
function loadGlobalSettings() {
|
|
23166
|
-
const settingsPath =
|
|
24113
|
+
const settingsPath = join31(homedir9(), ".open-agents", "settings.json");
|
|
23167
24114
|
try {
|
|
23168
|
-
if (
|
|
23169
|
-
return JSON.parse(
|
|
24115
|
+
if (existsSync22(settingsPath)) {
|
|
24116
|
+
return JSON.parse(readFileSync16(settingsPath, "utf-8"));
|
|
23170
24117
|
}
|
|
23171
24118
|
} catch {
|
|
23172
24119
|
}
|
|
23173
24120
|
return {};
|
|
23174
24121
|
}
|
|
23175
24122
|
function saveGlobalSettings(settings) {
|
|
23176
|
-
const dir =
|
|
23177
|
-
|
|
24123
|
+
const dir = join31(homedir9(), ".open-agents");
|
|
24124
|
+
mkdirSync8(dir, { recursive: true });
|
|
23178
24125
|
const existing = loadGlobalSettings();
|
|
23179
24126
|
const merged = { ...existing, ...settings };
|
|
23180
|
-
|
|
24127
|
+
writeFileSync8(join31(dir, "settings.json"), JSON.stringify(merged, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
|
|
23181
24128
|
}
|
|
23182
24129
|
function resolveSettings(repoRoot) {
|
|
23183
24130
|
const global = loadGlobalSettings();
|
|
@@ -23192,12 +24139,12 @@ function discoverContextFiles(repoRoot, maxContentLen = 8e3) {
|
|
|
23192
24139
|
while (dir && !visited.has(dir)) {
|
|
23193
24140
|
visited.add(dir);
|
|
23194
24141
|
for (const name of CONTEXT_FILES) {
|
|
23195
|
-
const filePath =
|
|
24142
|
+
const filePath = join31(dir, name);
|
|
23196
24143
|
const normalizedName = name.toLowerCase();
|
|
23197
|
-
if (
|
|
24144
|
+
if (existsSync22(filePath) && !seen.has(filePath)) {
|
|
23198
24145
|
seen.add(filePath);
|
|
23199
24146
|
try {
|
|
23200
|
-
let content =
|
|
24147
|
+
let content = readFileSync16(filePath, "utf-8");
|
|
23201
24148
|
if (content.length > maxContentLen) {
|
|
23202
24149
|
content = content.slice(0, maxContentLen) + "\n\n...(truncated)";
|
|
23203
24150
|
}
|
|
@@ -23211,11 +24158,11 @@ function discoverContextFiles(repoRoot, maxContentLen = 8e3) {
|
|
|
23211
24158
|
}
|
|
23212
24159
|
}
|
|
23213
24160
|
}
|
|
23214
|
-
const projectMap =
|
|
23215
|
-
if (
|
|
24161
|
+
const projectMap = join31(dir, OA_DIR, "context", "project-map.md");
|
|
24162
|
+
if (existsSync22(projectMap) && !seen.has(projectMap)) {
|
|
23216
24163
|
seen.add(projectMap);
|
|
23217
24164
|
try {
|
|
23218
|
-
let content =
|
|
24165
|
+
let content = readFileSync16(projectMap, "utf-8");
|
|
23219
24166
|
if (content.length > maxContentLen) {
|
|
23220
24167
|
content = content.slice(0, maxContentLen) + "\n\n...(truncated)";
|
|
23221
24168
|
}
|
|
@@ -23227,7 +24174,7 @@ function discoverContextFiles(repoRoot, maxContentLen = 8e3) {
|
|
|
23227
24174
|
} catch {
|
|
23228
24175
|
}
|
|
23229
24176
|
}
|
|
23230
|
-
const parent =
|
|
24177
|
+
const parent = join31(dir, "..");
|
|
23231
24178
|
if (parent === dir)
|
|
23232
24179
|
break;
|
|
23233
24180
|
dir = parent;
|
|
@@ -23245,9 +24192,9 @@ function discoverContextFiles(repoRoot, maxContentLen = 8e3) {
|
|
|
23245
24192
|
return found;
|
|
23246
24193
|
}
|
|
23247
24194
|
function readIndexMeta(repoRoot) {
|
|
23248
|
-
const metaPath =
|
|
24195
|
+
const metaPath = join31(repoRoot, OA_DIR, "index", "meta.json");
|
|
23249
24196
|
try {
|
|
23250
|
-
return JSON.parse(
|
|
24197
|
+
return JSON.parse(readFileSync16(metaPath, "utf-8"));
|
|
23251
24198
|
} catch {
|
|
23252
24199
|
return null;
|
|
23253
24200
|
}
|
|
@@ -23298,28 +24245,28 @@ ${tree}\`\`\`
|
|
|
23298
24245
|
sections.push("");
|
|
23299
24246
|
}
|
|
23300
24247
|
const content = sections.join("\n");
|
|
23301
|
-
const contextDir =
|
|
23302
|
-
|
|
23303
|
-
|
|
24248
|
+
const contextDir = join31(repoRoot, OA_DIR, "context");
|
|
24249
|
+
mkdirSync8(contextDir, { recursive: true });
|
|
24250
|
+
writeFileSync8(join31(contextDir, "project-map.md"), content, "utf-8");
|
|
23304
24251
|
return content;
|
|
23305
24252
|
}
|
|
23306
24253
|
function saveSession(repoRoot, session) {
|
|
23307
|
-
const historyDir =
|
|
23308
|
-
|
|
23309
|
-
|
|
24254
|
+
const historyDir = join31(repoRoot, OA_DIR, "history");
|
|
24255
|
+
mkdirSync8(historyDir, { recursive: true });
|
|
24256
|
+
writeFileSync8(join31(historyDir, `${session.id}.json`), JSON.stringify(session, null, 2), "utf-8");
|
|
23310
24257
|
}
|
|
23311
24258
|
function loadRecentSessions(repoRoot, limit = 5) {
|
|
23312
|
-
const historyDir =
|
|
23313
|
-
if (!
|
|
24259
|
+
const historyDir = join31(repoRoot, OA_DIR, "history");
|
|
24260
|
+
if (!existsSync22(historyDir))
|
|
23314
24261
|
return [];
|
|
23315
24262
|
try {
|
|
23316
24263
|
const files = readdirSync7(historyDir).filter((f) => f.endsWith(".json") && f !== "pending-task.json").map((f) => {
|
|
23317
|
-
const stat5 = statSync9(
|
|
24264
|
+
const stat5 = statSync9(join31(historyDir, f));
|
|
23318
24265
|
return { file: f, mtime: stat5.mtimeMs };
|
|
23319
24266
|
}).sort((a, b) => b.mtime - a.mtime).slice(0, limit);
|
|
23320
24267
|
return files.map((f) => {
|
|
23321
24268
|
try {
|
|
23322
|
-
return JSON.parse(
|
|
24269
|
+
return JSON.parse(readFileSync16(join31(historyDir, f.file), "utf-8"));
|
|
23323
24270
|
} catch {
|
|
23324
24271
|
return null;
|
|
23325
24272
|
}
|
|
@@ -23329,16 +24276,16 @@ function loadRecentSessions(repoRoot, limit = 5) {
|
|
|
23329
24276
|
}
|
|
23330
24277
|
}
|
|
23331
24278
|
function savePendingTask(repoRoot, task) {
|
|
23332
|
-
const historyDir =
|
|
23333
|
-
|
|
23334
|
-
|
|
24279
|
+
const historyDir = join31(repoRoot, OA_DIR, "history");
|
|
24280
|
+
mkdirSync8(historyDir, { recursive: true });
|
|
24281
|
+
writeFileSync8(join31(historyDir, PENDING_TASK_FILE), JSON.stringify(task, null, 2) + "\n", "utf-8");
|
|
23335
24282
|
}
|
|
23336
24283
|
function loadPendingTask(repoRoot) {
|
|
23337
|
-
const filePath =
|
|
24284
|
+
const filePath = join31(repoRoot, OA_DIR, "history", PENDING_TASK_FILE);
|
|
23338
24285
|
try {
|
|
23339
|
-
if (!
|
|
24286
|
+
if (!existsSync22(filePath))
|
|
23340
24287
|
return null;
|
|
23341
|
-
const data = JSON.parse(
|
|
24288
|
+
const data = JSON.parse(readFileSync16(filePath, "utf-8"));
|
|
23342
24289
|
try {
|
|
23343
24290
|
unlinkSync3(filePath);
|
|
23344
24291
|
} catch {
|
|
@@ -23349,13 +24296,13 @@ function loadPendingTask(repoRoot) {
|
|
|
23349
24296
|
}
|
|
23350
24297
|
}
|
|
23351
24298
|
function saveSessionContext(repoRoot, entry) {
|
|
23352
|
-
const contextDir =
|
|
23353
|
-
|
|
23354
|
-
const filePath =
|
|
24299
|
+
const contextDir = join31(repoRoot, OA_DIR, "context");
|
|
24300
|
+
mkdirSync8(contextDir, { recursive: true });
|
|
24301
|
+
const filePath = join31(contextDir, CONTEXT_SAVE_FILE);
|
|
23355
24302
|
let ctx;
|
|
23356
24303
|
try {
|
|
23357
|
-
if (
|
|
23358
|
-
ctx = JSON.parse(
|
|
24304
|
+
if (existsSync22(filePath)) {
|
|
24305
|
+
ctx = JSON.parse(readFileSync16(filePath, "utf-8"));
|
|
23359
24306
|
} else {
|
|
23360
24307
|
ctx = { entries: [], maxEntries: MAX_CONTEXT_ENTRIES, updatedAt: "" };
|
|
23361
24308
|
}
|
|
@@ -23367,14 +24314,14 @@ function saveSessionContext(repoRoot, entry) {
|
|
|
23367
24314
|
ctx.entries = ctx.entries.slice(-ctx.maxEntries);
|
|
23368
24315
|
}
|
|
23369
24316
|
ctx.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
23370
|
-
|
|
24317
|
+
writeFileSync8(filePath, JSON.stringify(ctx, null, 2) + "\n", "utf-8");
|
|
23371
24318
|
}
|
|
23372
24319
|
function loadSessionContext(repoRoot) {
|
|
23373
|
-
const filePath =
|
|
24320
|
+
const filePath = join31(repoRoot, OA_DIR, "context", CONTEXT_SAVE_FILE);
|
|
23374
24321
|
try {
|
|
23375
|
-
if (!
|
|
24322
|
+
if (!existsSync22(filePath))
|
|
23376
24323
|
return null;
|
|
23377
|
-
return JSON.parse(
|
|
24324
|
+
return JSON.parse(readFileSync16(filePath, "utf-8"));
|
|
23378
24325
|
} catch {
|
|
23379
24326
|
return null;
|
|
23380
24327
|
}
|
|
@@ -23421,12 +24368,12 @@ function detectManifests(repoRoot) {
|
|
|
23421
24368
|
{ file: "docker-compose.yaml", type: "Docker Compose" }
|
|
23422
24369
|
];
|
|
23423
24370
|
for (const check of checks) {
|
|
23424
|
-
const filePath =
|
|
23425
|
-
if (
|
|
24371
|
+
const filePath = join31(repoRoot, check.file);
|
|
24372
|
+
if (existsSync22(filePath)) {
|
|
23426
24373
|
let name;
|
|
23427
24374
|
if (check.nameField) {
|
|
23428
24375
|
try {
|
|
23429
|
-
const data = JSON.parse(
|
|
24376
|
+
const data = JSON.parse(readFileSync16(filePath, "utf-8"));
|
|
23430
24377
|
name = data[check.nameField];
|
|
23431
24378
|
} catch {
|
|
23432
24379
|
}
|
|
@@ -23455,7 +24402,7 @@ function findKeyFiles(repoRoot) {
|
|
|
23455
24402
|
{ pattern: "CLAUDE.md", description: "Claude Code context" }
|
|
23456
24403
|
];
|
|
23457
24404
|
for (const check of checks) {
|
|
23458
|
-
if (
|
|
24405
|
+
if (existsSync22(join31(repoRoot, check.pattern))) {
|
|
23459
24406
|
keyFiles.push({ path: check.pattern, description: check.description });
|
|
23460
24407
|
}
|
|
23461
24408
|
}
|
|
@@ -23481,12 +24428,12 @@ function buildDirTree(root, maxDepth, prefix = "", depth = 0) {
|
|
|
23481
24428
|
if (entry.isDirectory()) {
|
|
23482
24429
|
let fileCount = 0;
|
|
23483
24430
|
try {
|
|
23484
|
-
fileCount = readdirSync7(
|
|
24431
|
+
fileCount = readdirSync7(join31(root, entry.name)).filter((f) => !f.startsWith(".")).length;
|
|
23485
24432
|
} catch {
|
|
23486
24433
|
}
|
|
23487
24434
|
result += `${prefix}${connector}${entry.name}/ (${fileCount})
|
|
23488
24435
|
`;
|
|
23489
|
-
result += buildDirTree(
|
|
24436
|
+
result += buildDirTree(join31(root, entry.name), maxDepth, childPrefix, depth + 1);
|
|
23490
24437
|
} else if (depth < maxDepth) {
|
|
23491
24438
|
result += `${prefix}${connector}${entry.name}
|
|
23492
24439
|
`;
|
|
@@ -23540,8 +24487,8 @@ var init_oa_directory = __esm({
|
|
|
23540
24487
|
// packages/cli/dist/tui/setup.js
|
|
23541
24488
|
import * as readline from "node:readline";
|
|
23542
24489
|
import { execSync as execSync20, spawn as spawn12 } from "node:child_process";
|
|
23543
|
-
import { existsSync as
|
|
23544
|
-
import { join as
|
|
24490
|
+
import { existsSync as existsSync23, writeFileSync as writeFileSync9, mkdirSync as mkdirSync9 } from "node:fs";
|
|
24491
|
+
import { join as join32 } from "node:path";
|
|
23545
24492
|
import { homedir as homedir10, platform } from "node:os";
|
|
23546
24493
|
function detectSystemSpecs() {
|
|
23547
24494
|
let totalRamGB = 0;
|
|
@@ -23736,7 +24683,7 @@ async function installOllamaMac(rl) {
|
|
|
23736
24683
|
execSync20('/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"', { stdio: "inherit", timeout: 6e5 });
|
|
23737
24684
|
if (!hasCmd("brew")) {
|
|
23738
24685
|
try {
|
|
23739
|
-
const brewPrefix =
|
|
24686
|
+
const brewPrefix = existsSync23("/opt/homebrew/bin/brew") ? "/opt/homebrew" : "/usr/local";
|
|
23740
24687
|
process.env["PATH"] = `${brewPrefix}/bin:${process.env["PATH"]}`;
|
|
23741
24688
|
} catch {
|
|
23742
24689
|
}
|
|
@@ -24210,10 +25157,10 @@ async function doSetup(config, rl) {
|
|
|
24210
25157
|
`PARAMETER num_predict ${numPredict}`,
|
|
24211
25158
|
`PARAMETER stop "<|endoftext|>"`
|
|
24212
25159
|
].join("\n");
|
|
24213
|
-
const modelDir2 =
|
|
24214
|
-
|
|
24215
|
-
const modelfilePath =
|
|
24216
|
-
|
|
25160
|
+
const modelDir2 = join32(homedir10(), ".open-agents", "models");
|
|
25161
|
+
mkdirSync9(modelDir2, { recursive: true });
|
|
25162
|
+
const modelfilePath = join32(modelDir2, `Modelfile.${customName}`);
|
|
25163
|
+
writeFileSync9(modelfilePath, modelfileContent + "\n", "utf8");
|
|
24217
25164
|
process.stdout.write(` ${c2.dim("Creating model...")} `);
|
|
24218
25165
|
execSync20(`ollama create ${customName} -f ${modelfilePath}`, {
|
|
24219
25166
|
stdio: "pipe",
|
|
@@ -24258,7 +25205,7 @@ async function isModelAvailable(config) {
|
|
|
24258
25205
|
}
|
|
24259
25206
|
function isFirstRun() {
|
|
24260
25207
|
try {
|
|
24261
|
-
return !
|
|
25208
|
+
return !existsSync23(join32(homedir10(), ".open-agents", "config.json"));
|
|
24262
25209
|
} catch {
|
|
24263
25210
|
return true;
|
|
24264
25211
|
}
|
|
@@ -24295,7 +25242,7 @@ function detectPkgManager() {
|
|
|
24295
25242
|
return null;
|
|
24296
25243
|
}
|
|
24297
25244
|
function getVenvDir() {
|
|
24298
|
-
return
|
|
25245
|
+
return join32(homedir10(), ".open-agents", "venv");
|
|
24299
25246
|
}
|
|
24300
25247
|
function hasVenvModule() {
|
|
24301
25248
|
try {
|
|
@@ -24307,8 +25254,8 @@ function hasVenvModule() {
|
|
|
24307
25254
|
}
|
|
24308
25255
|
function ensureVenv(log) {
|
|
24309
25256
|
const venvDir = getVenvDir();
|
|
24310
|
-
const venvPip =
|
|
24311
|
-
if (
|
|
25257
|
+
const venvPip = join32(venvDir, "bin", "pip");
|
|
25258
|
+
if (existsSync23(venvPip))
|
|
24312
25259
|
return venvDir;
|
|
24313
25260
|
log("Creating Python venv for vision deps...");
|
|
24314
25261
|
if (!hasCmd("python3")) {
|
|
@@ -24320,9 +25267,9 @@ function ensureVenv(log) {
|
|
|
24320
25267
|
return null;
|
|
24321
25268
|
}
|
|
24322
25269
|
try {
|
|
24323
|
-
|
|
25270
|
+
mkdirSync9(join32(homedir10(), ".open-agents"), { recursive: true });
|
|
24324
25271
|
execSync20(`python3 -m venv "${venvDir}"`, { stdio: "pipe", timeout: 3e4 });
|
|
24325
|
-
execSync20(`"${
|
|
25272
|
+
execSync20(`"${join32(venvDir, "bin", "pip")}" install --upgrade pip`, {
|
|
24326
25273
|
stdio: "pipe",
|
|
24327
25274
|
timeout: 6e4
|
|
24328
25275
|
});
|
|
@@ -24531,15 +25478,15 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
|
|
|
24531
25478
|
}
|
|
24532
25479
|
}
|
|
24533
25480
|
const venvDir = getVenvDir();
|
|
24534
|
-
const venvBin =
|
|
24535
|
-
const venvMoondream =
|
|
25481
|
+
const venvBin = join32(venvDir, "bin");
|
|
25482
|
+
const venvMoondream = join32(venvBin, "moondream-station");
|
|
24536
25483
|
const venv = ensureVenv(log);
|
|
24537
|
-
if (venv && !hasCmd("moondream-station") && !
|
|
24538
|
-
const venvPip =
|
|
25484
|
+
if (venv && !hasCmd("moondream-station") && !existsSync23(venvMoondream)) {
|
|
25485
|
+
const venvPip = join32(venvBin, "pip");
|
|
24539
25486
|
log("Installing moondream-station in ~/.open-agents/venv...");
|
|
24540
25487
|
try {
|
|
24541
25488
|
execSync20(`"${venvPip}" install moondream-station`, { stdio: "pipe", timeout: 3e5 });
|
|
24542
|
-
if (
|
|
25489
|
+
if (existsSync23(venvMoondream)) {
|
|
24543
25490
|
log("moondream-station installed successfully.");
|
|
24544
25491
|
} else {
|
|
24545
25492
|
try {
|
|
@@ -24556,8 +25503,8 @@ async function ensureVisionDeps(onInfo, getSudoPassword) {
|
|
|
24556
25503
|
}
|
|
24557
25504
|
}
|
|
24558
25505
|
if (venv) {
|
|
24559
|
-
const venvPython =
|
|
24560
|
-
const venvPip2 =
|
|
25506
|
+
const venvPython = join32(venvBin, "python");
|
|
25507
|
+
const venvPip2 = join32(venvBin, "pip");
|
|
24561
25508
|
let ocrStackInstalled = false;
|
|
24562
25509
|
try {
|
|
24563
25510
|
execSync20(`"${venvPython}" -c "import cv2, pytesseract, numpy, PIL"`, { stdio: "pipe", timeout: 1e4 });
|
|
@@ -24666,10 +25613,10 @@ function createExpandedVariant(baseModel, specs, sizeGB) {
|
|
|
24666
25613
|
`PARAMETER num_predict ${numPredict}`,
|
|
24667
25614
|
`PARAMETER stop "<|endoftext|>"`
|
|
24668
25615
|
].join("\n");
|
|
24669
|
-
const modelDir2 =
|
|
24670
|
-
|
|
24671
|
-
const modelfilePath =
|
|
24672
|
-
|
|
25616
|
+
const modelDir2 = join32(homedir10(), ".open-agents", "models");
|
|
25617
|
+
mkdirSync9(modelDir2, { recursive: true });
|
|
25618
|
+
const modelfilePath = join32(modelDir2, `Modelfile.${customName}`);
|
|
25619
|
+
writeFileSync9(modelfilePath, modelfileContent + "\n", "utf8");
|
|
24673
25620
|
execSync20(`ollama create ${customName} -f ${modelfilePath}`, {
|
|
24674
25621
|
stdio: "pipe",
|
|
24675
25622
|
timeout: 12e4
|
|
@@ -25232,6 +26179,130 @@ async function handleSlashCommand(input, ctx) {
|
|
|
25232
26179
|
}
|
|
25233
26180
|
return "handled";
|
|
25234
26181
|
}
|
|
26182
|
+
case "p2p": {
|
|
26183
|
+
if (!ctx.p2pStart) {
|
|
26184
|
+
renderWarning("P2P mesh not available in this context.");
|
|
26185
|
+
return "handled";
|
|
26186
|
+
}
|
|
26187
|
+
const p2pSub = arg?.split(/\s+/)[0] ?? "";
|
|
26188
|
+
const p2pRest = arg?.slice(p2pSub.length).trim() ?? "";
|
|
26189
|
+
if (p2pSub === "stop" || p2pSub === "off") {
|
|
26190
|
+
if (ctx.isP2PActive?.()) {
|
|
26191
|
+
await ctx.p2pStop?.();
|
|
26192
|
+
renderInfo("P2P mesh stopped.");
|
|
26193
|
+
} else {
|
|
26194
|
+
renderWarning("P2P mesh not running.");
|
|
26195
|
+
}
|
|
26196
|
+
return "handled";
|
|
26197
|
+
}
|
|
26198
|
+
if (p2pSub === "status" || !p2pSub && ctx.isP2PActive?.()) {
|
|
26199
|
+
if (ctx.isP2PActive?.()) {
|
|
26200
|
+
process.stdout.write("\n" + (ctx.p2pStatus?.() ?? "") + "\n\n");
|
|
26201
|
+
} else {
|
|
26202
|
+
renderWarning("P2P mesh not running. Start with: /p2p start");
|
|
26203
|
+
}
|
|
26204
|
+
return "handled";
|
|
26205
|
+
}
|
|
26206
|
+
if (p2pSub === "connect") {
|
|
26207
|
+
if (!ctx.isP2PActive?.()) {
|
|
26208
|
+
renderWarning("Start mesh first: /p2p start");
|
|
26209
|
+
return "handled";
|
|
26210
|
+
}
|
|
26211
|
+
if (!p2pRest) {
|
|
26212
|
+
renderInfo("Usage: /p2p connect <endpoint> [--trust verified|tee|public]");
|
|
26213
|
+
return "handled";
|
|
26214
|
+
}
|
|
26215
|
+
const parts = p2pRest.split(/\s+/);
|
|
26216
|
+
const endpoint = parts[0];
|
|
26217
|
+
const trustIdx = parts.indexOf("--trust");
|
|
26218
|
+
const trust = trustIdx !== -1 ? parts[trustIdx + 1] : void 0;
|
|
26219
|
+
try {
|
|
26220
|
+
const peerId = await ctx.p2pConnect(endpoint, trust);
|
|
26221
|
+
if (peerId) {
|
|
26222
|
+
renderInfo(`Connected to peer: ${c2.bold(peerId)}`);
|
|
26223
|
+
} else {
|
|
26224
|
+
renderError("Failed to connect to peer.");
|
|
26225
|
+
}
|
|
26226
|
+
} catch (err) {
|
|
26227
|
+
renderError(`Connection failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
26228
|
+
}
|
|
26229
|
+
return "handled";
|
|
26230
|
+
}
|
|
26231
|
+
if (p2pSub === "start" || !p2pSub) {
|
|
26232
|
+
if (ctx.isP2PActive?.()) {
|
|
26233
|
+
process.stdout.write("\n" + (ctx.p2pStatus?.() ?? "") + "\n\n");
|
|
26234
|
+
return "handled";
|
|
26235
|
+
}
|
|
26236
|
+
try {
|
|
26237
|
+
const bootstrapPeers = p2pRest ? p2pRest.split(/[,\s]+/).filter(Boolean) : void 0;
|
|
26238
|
+
const result = await ctx.p2pStart(bootstrapPeers);
|
|
26239
|
+
renderInfo(`P2P mesh started. Peer ID: ${c2.bold(result.peerId)} (port ${result.port})`);
|
|
26240
|
+
renderInfo("Connect peers with: /p2p connect <endpoint>");
|
|
26241
|
+
} catch (err) {
|
|
26242
|
+
renderError(`P2P start failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
26243
|
+
}
|
|
26244
|
+
return "handled";
|
|
26245
|
+
}
|
|
26246
|
+
renderInfo("Usage: /p2p start [bootstrap-peers...] | /p2p connect <endpoint> | /p2p status | /p2p stop");
|
|
26247
|
+
return "handled";
|
|
26248
|
+
}
|
|
26249
|
+
case "secrets":
|
|
26250
|
+
case "secret":
|
|
26251
|
+
case "vault": {
|
|
26252
|
+
if (!ctx.secretSet) {
|
|
26253
|
+
renderWarning("Secret vault not available in this context.");
|
|
26254
|
+
return "handled";
|
|
26255
|
+
}
|
|
26256
|
+
const secSub = arg?.split(/\s+/)[0] ?? "";
|
|
26257
|
+
const secRest = arg?.slice(secSub.length).trim() ?? "";
|
|
26258
|
+
if (secSub === "set") {
|
|
26259
|
+
const parts = secRest.split(/\s+/);
|
|
26260
|
+
const name = parts[0];
|
|
26261
|
+
const value = parts.slice(1).join(" ");
|
|
26262
|
+
if (!name || !value) {
|
|
26263
|
+
renderInfo("Usage: /secrets set <NAME> <value>");
|
|
26264
|
+
return "handled";
|
|
26265
|
+
}
|
|
26266
|
+
ctx.secretSet(name, value);
|
|
26267
|
+
renderInfo(`Secret "${name}" registered.`);
|
|
26268
|
+
return "handled";
|
|
26269
|
+
}
|
|
26270
|
+
if (secSub === "delete" || secSub === "rm") {
|
|
26271
|
+
if (!secRest) {
|
|
26272
|
+
renderInfo("Usage: /secrets delete <NAME>");
|
|
26273
|
+
return "handled";
|
|
26274
|
+
}
|
|
26275
|
+
const deleted = ctx.secretDelete(secRest);
|
|
26276
|
+
if (deleted)
|
|
26277
|
+
renderInfo(`Secret "${secRest}" removed.`);
|
|
26278
|
+
else
|
|
26279
|
+
renderWarning(`Secret "${secRest}" not found.`);
|
|
26280
|
+
return "handled";
|
|
26281
|
+
}
|
|
26282
|
+
if (secSub === "import-env") {
|
|
26283
|
+
const count = ctx.secretImportEnv();
|
|
26284
|
+
renderInfo(`Imported ${count} secrets from environment variables.`);
|
|
26285
|
+
return "handled";
|
|
26286
|
+
}
|
|
26287
|
+
const secrets = ctx.secretList();
|
|
26288
|
+
if (secrets.length === 0) {
|
|
26289
|
+
renderInfo("No secrets registered. Use: /secrets set <NAME> <value>");
|
|
26290
|
+
} else {
|
|
26291
|
+
process.stdout.write(`
|
|
26292
|
+
${c2.bold("Registered Secrets")} (${secrets.length})
|
|
26293
|
+
|
|
26294
|
+
`);
|
|
26295
|
+
for (const s of secrets) {
|
|
26296
|
+
process.stdout.write(` ${c2.cyan(s.name.padEnd(24))} min trust: ${c2.bold(s.minTrust)}
|
|
26297
|
+
`);
|
|
26298
|
+
}
|
|
26299
|
+
process.stdout.write(`
|
|
26300
|
+
${c2.dim("Secrets are redacted from prompts sent to peers below their trust tier.")}
|
|
26301
|
+
|
|
26302
|
+
`);
|
|
26303
|
+
}
|
|
26304
|
+
return "handled";
|
|
26305
|
+
}
|
|
25235
26306
|
case "bruteforce":
|
|
25236
26307
|
case "brute": {
|
|
25237
26308
|
const isOn = ctx.bruteForceToggle();
|
|
@@ -25682,17 +26753,17 @@ async function handleUpdate(subcommand, ctx) {
|
|
|
25682
26753
|
try {
|
|
25683
26754
|
const { createRequire: createRequire4 } = await import("node:module");
|
|
25684
26755
|
const { fileURLToPath: fileURLToPath11 } = await import("node:url");
|
|
25685
|
-
const { dirname:
|
|
25686
|
-
const { existsSync:
|
|
26756
|
+
const { dirname: dirname15, join: join46 } = await import("node:path");
|
|
26757
|
+
const { existsSync: existsSync33 } = await import("node:fs");
|
|
25687
26758
|
const req = createRequire4(import.meta.url);
|
|
25688
|
-
const thisDir =
|
|
26759
|
+
const thisDir = dirname15(fileURLToPath11(import.meta.url));
|
|
25689
26760
|
const candidates = [
|
|
25690
|
-
|
|
25691
|
-
|
|
25692
|
-
|
|
26761
|
+
join46(thisDir, "..", "package.json"),
|
|
26762
|
+
join46(thisDir, "..", "..", "package.json"),
|
|
26763
|
+
join46(thisDir, "..", "..", "..", "package.json")
|
|
25693
26764
|
];
|
|
25694
26765
|
for (const pkgPath of candidates) {
|
|
25695
|
-
if (
|
|
26766
|
+
if (existsSync33(pkgPath)) {
|
|
25696
26767
|
const pkg = req(pkgPath);
|
|
25697
26768
|
if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli") {
|
|
25698
26769
|
currentVersion = pkg.version ?? "0.0.0";
|
|
@@ -25895,8 +26966,8 @@ var init_commands = __esm({
|
|
|
25895
26966
|
});
|
|
25896
26967
|
|
|
25897
26968
|
// packages/cli/dist/tui/project-context.js
|
|
25898
|
-
import { existsSync as
|
|
25899
|
-
import { join as
|
|
26969
|
+
import { existsSync as existsSync24, readFileSync as readFileSync17, readdirSync as readdirSync8 } from "node:fs";
|
|
26970
|
+
import { join as join33, basename as basename10 } from "node:path";
|
|
25900
26971
|
import { execSync as execSync21 } from "node:child_process";
|
|
25901
26972
|
import { homedir as homedir11, platform as platform2, release } from "node:os";
|
|
25902
26973
|
function getModelTier(modelName) {
|
|
@@ -25931,10 +27002,10 @@ function loadProjectMap(repoRoot) {
|
|
|
25931
27002
|
if (!hasOaDirectory(repoRoot)) {
|
|
25932
27003
|
initOaDirectory(repoRoot);
|
|
25933
27004
|
}
|
|
25934
|
-
const mapPath =
|
|
25935
|
-
if (
|
|
27005
|
+
const mapPath = join33(repoRoot, OA_DIR, "context", "project-map.md");
|
|
27006
|
+
if (existsSync24(mapPath)) {
|
|
25936
27007
|
try {
|
|
25937
|
-
const content =
|
|
27008
|
+
const content = readFileSync17(mapPath, "utf-8");
|
|
25938
27009
|
return content;
|
|
25939
27010
|
} catch {
|
|
25940
27011
|
}
|
|
@@ -25975,31 +27046,31 @@ ${log}`);
|
|
|
25975
27046
|
}
|
|
25976
27047
|
function loadMemoryContext(repoRoot) {
|
|
25977
27048
|
const sections = [];
|
|
25978
|
-
const oaMemDir =
|
|
27049
|
+
const oaMemDir = join33(repoRoot, OA_DIR, "memory");
|
|
25979
27050
|
const oaEntries = loadMemoryDir(oaMemDir, "project");
|
|
25980
27051
|
if (oaEntries)
|
|
25981
27052
|
sections.push(oaEntries);
|
|
25982
|
-
const legacyMemDir =
|
|
25983
|
-
if (legacyMemDir !== oaMemDir &&
|
|
27053
|
+
const legacyMemDir = join33(repoRoot, ".open-agents", "memory");
|
|
27054
|
+
if (legacyMemDir !== oaMemDir && existsSync24(legacyMemDir)) {
|
|
25984
27055
|
const legacyEntries = loadMemoryDir(legacyMemDir, "project/legacy");
|
|
25985
27056
|
if (legacyEntries)
|
|
25986
27057
|
sections.push(legacyEntries);
|
|
25987
27058
|
}
|
|
25988
|
-
const globalMemDir =
|
|
27059
|
+
const globalMemDir = join33(homedir11(), ".open-agents", "memory");
|
|
25989
27060
|
const globalEntries = loadMemoryDir(globalMemDir, "global");
|
|
25990
27061
|
if (globalEntries)
|
|
25991
27062
|
sections.push(globalEntries);
|
|
25992
27063
|
return sections.join("\n\n");
|
|
25993
27064
|
}
|
|
25994
27065
|
function loadMemoryDir(memDir, scope) {
|
|
25995
|
-
if (!
|
|
27066
|
+
if (!existsSync24(memDir))
|
|
25996
27067
|
return "";
|
|
25997
27068
|
const lines = [];
|
|
25998
27069
|
try {
|
|
25999
27070
|
const files = readdirSync8(memDir).filter((f) => f.endsWith(".json"));
|
|
26000
27071
|
for (const file of files.slice(0, 10)) {
|
|
26001
27072
|
try {
|
|
26002
|
-
const raw =
|
|
27073
|
+
const raw = readFileSync17(join33(memDir, file), "utf-8");
|
|
26003
27074
|
const entries = JSON.parse(raw);
|
|
26004
27075
|
const topic = basename10(file, ".json");
|
|
26005
27076
|
const keys = Object.keys(entries);
|
|
@@ -27026,22 +28097,22 @@ var init_carousel = __esm({
|
|
|
27026
28097
|
});
|
|
27027
28098
|
|
|
27028
28099
|
// packages/cli/dist/tui/carousel-descriptors.js
|
|
27029
|
-
import { existsSync as
|
|
27030
|
-
import { join as
|
|
28100
|
+
import { existsSync as existsSync25, readFileSync as readFileSync18, writeFileSync as writeFileSync10, mkdirSync as mkdirSync10, readdirSync as readdirSync9 } from "node:fs";
|
|
28101
|
+
import { join as join34, basename as basename11 } from "node:path";
|
|
27031
28102
|
function loadToolProfile(repoRoot) {
|
|
27032
|
-
const filePath =
|
|
28103
|
+
const filePath = join34(repoRoot, OA_DIR, "context", TOOL_PROFILE_FILE);
|
|
27033
28104
|
try {
|
|
27034
|
-
if (!
|
|
28105
|
+
if (!existsSync25(filePath))
|
|
27035
28106
|
return null;
|
|
27036
|
-
return JSON.parse(
|
|
28107
|
+
return JSON.parse(readFileSync18(filePath, "utf-8"));
|
|
27037
28108
|
} catch {
|
|
27038
28109
|
return null;
|
|
27039
28110
|
}
|
|
27040
28111
|
}
|
|
27041
28112
|
function saveToolProfile(repoRoot, profile) {
|
|
27042
|
-
const contextDir =
|
|
27043
|
-
|
|
27044
|
-
|
|
28113
|
+
const contextDir = join34(repoRoot, OA_DIR, "context");
|
|
28114
|
+
mkdirSync10(contextDir, { recursive: true });
|
|
28115
|
+
writeFileSync10(join34(contextDir, TOOL_PROFILE_FILE), JSON.stringify(profile, null, 2), "utf-8");
|
|
27045
28116
|
}
|
|
27046
28117
|
function categorizeToolCall(toolName) {
|
|
27047
28118
|
for (const cat of TOOL_CATEGORIES) {
|
|
@@ -27099,25 +28170,25 @@ function weightedColor(profile) {
|
|
|
27099
28170
|
return selectedCat.colors[Math.floor(Math.random() * selectedCat.colors.length)];
|
|
27100
28171
|
}
|
|
27101
28172
|
function loadCachedDescriptors(repoRoot) {
|
|
27102
|
-
const filePath =
|
|
28173
|
+
const filePath = join34(repoRoot, OA_DIR, "context", DESCRIPTOR_FILE);
|
|
27103
28174
|
try {
|
|
27104
|
-
if (!
|
|
28175
|
+
if (!existsSync25(filePath))
|
|
27105
28176
|
return null;
|
|
27106
|
-
const cached = JSON.parse(
|
|
28177
|
+
const cached = JSON.parse(readFileSync18(filePath, "utf-8"));
|
|
27107
28178
|
return cached.phrases.length > 0 ? cached.phrases : null;
|
|
27108
28179
|
} catch {
|
|
27109
28180
|
return null;
|
|
27110
28181
|
}
|
|
27111
28182
|
}
|
|
27112
28183
|
function saveCachedDescriptors(repoRoot, phrases, sourceHash) {
|
|
27113
|
-
const contextDir =
|
|
27114
|
-
|
|
28184
|
+
const contextDir = join34(repoRoot, OA_DIR, "context");
|
|
28185
|
+
mkdirSync10(contextDir, { recursive: true });
|
|
27115
28186
|
const cached = {
|
|
27116
28187
|
phrases,
|
|
27117
28188
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27118
28189
|
sourceHash
|
|
27119
28190
|
};
|
|
27120
|
-
|
|
28191
|
+
writeFileSync10(join34(contextDir, DESCRIPTOR_FILE), JSON.stringify(cached, null, 2), "utf-8");
|
|
27121
28192
|
}
|
|
27122
28193
|
function generateDescriptors(repoRoot) {
|
|
27123
28194
|
const profile = loadToolProfile(repoRoot);
|
|
@@ -27165,11 +28236,11 @@ function generateDescriptors(repoRoot) {
|
|
|
27165
28236
|
return phrases;
|
|
27166
28237
|
}
|
|
27167
28238
|
function extractFromPackageJson(repoRoot, tags) {
|
|
27168
|
-
const pkgPath =
|
|
28239
|
+
const pkgPath = join34(repoRoot, "package.json");
|
|
27169
28240
|
try {
|
|
27170
|
-
if (!
|
|
28241
|
+
if (!existsSync25(pkgPath))
|
|
27171
28242
|
return;
|
|
27172
|
-
const pkg = JSON.parse(
|
|
28243
|
+
const pkg = JSON.parse(readFileSync18(pkgPath, "utf-8"));
|
|
27173
28244
|
if (pkg.name && typeof pkg.name === "string") {
|
|
27174
28245
|
const parts = pkg.name.replace(/^@/, "").split("/");
|
|
27175
28246
|
for (const p of parts)
|
|
@@ -27213,7 +28284,7 @@ function extractFromManifests(repoRoot, tags) {
|
|
|
27213
28284
|
{ file: ".github/workflows", tag: "ci/cd" }
|
|
27214
28285
|
];
|
|
27215
28286
|
for (const check of manifestChecks) {
|
|
27216
|
-
if (
|
|
28287
|
+
if (existsSync25(join34(repoRoot, check.file))) {
|
|
27217
28288
|
tags.push(check.tag);
|
|
27218
28289
|
}
|
|
27219
28290
|
}
|
|
@@ -27235,16 +28306,16 @@ function extractFromSessions(repoRoot, tags) {
|
|
|
27235
28306
|
}
|
|
27236
28307
|
}
|
|
27237
28308
|
function extractFromMemory(repoRoot, tags) {
|
|
27238
|
-
const memoryDir =
|
|
28309
|
+
const memoryDir = join34(repoRoot, OA_DIR, "memory");
|
|
27239
28310
|
try {
|
|
27240
|
-
if (!
|
|
28311
|
+
if (!existsSync25(memoryDir))
|
|
27241
28312
|
return;
|
|
27242
28313
|
const files = readdirSync9(memoryDir).filter((f) => f.endsWith(".json"));
|
|
27243
28314
|
for (const file of files) {
|
|
27244
28315
|
const topic = file.replace(/\.json$/, "").replace(/[-_]/g, " ");
|
|
27245
28316
|
tags.push(topic);
|
|
27246
28317
|
try {
|
|
27247
|
-
const data = JSON.parse(
|
|
28318
|
+
const data = JSON.parse(readFileSync18(join34(memoryDir, file), "utf-8"));
|
|
27248
28319
|
if (data && typeof data === "object") {
|
|
27249
28320
|
const keys = Object.keys(data).slice(0, 3);
|
|
27250
28321
|
for (const key of keys) {
|
|
@@ -27379,25 +28450,25 @@ var init_carousel_descriptors = __esm({
|
|
|
27379
28450
|
});
|
|
27380
28451
|
|
|
27381
28452
|
// packages/cli/dist/tui/voice.js
|
|
27382
|
-
import { existsSync as
|
|
27383
|
-
import { join as
|
|
28453
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync11, writeFileSync as writeFileSync11, readFileSync as readFileSync19, unlinkSync as unlinkSync4 } from "node:fs";
|
|
28454
|
+
import { join as join35 } from "node:path";
|
|
27384
28455
|
import { homedir as homedir12, tmpdir as tmpdir6, platform as platform3 } from "node:os";
|
|
27385
28456
|
import { execSync as execSync22, spawn as nodeSpawn } from "node:child_process";
|
|
27386
28457
|
import { createRequire } from "node:module";
|
|
27387
28458
|
function voiceDir() {
|
|
27388
|
-
return
|
|
28459
|
+
return join35(homedir12(), ".open-agents", "voice");
|
|
27389
28460
|
}
|
|
27390
28461
|
function modelsDir() {
|
|
27391
|
-
return
|
|
28462
|
+
return join35(voiceDir(), "models");
|
|
27392
28463
|
}
|
|
27393
28464
|
function modelDir(id) {
|
|
27394
|
-
return
|
|
28465
|
+
return join35(modelsDir(), id);
|
|
27395
28466
|
}
|
|
27396
28467
|
function modelOnnxPath(id) {
|
|
27397
|
-
return
|
|
28468
|
+
return join35(modelDir(id), "model.onnx");
|
|
27398
28469
|
}
|
|
27399
28470
|
function modelConfigPath(id) {
|
|
27400
|
-
return
|
|
28471
|
+
return join35(modelDir(id), "config.json");
|
|
27401
28472
|
}
|
|
27402
28473
|
function resetNarrationContext() {
|
|
27403
28474
|
narration.toolCount = 0;
|
|
@@ -28075,7 +29146,7 @@ var init_voice = __esm({
|
|
|
28075
29146
|
}
|
|
28076
29147
|
this.onPCMOutput(Buffer.from(int16.buffer), this.config.audio.sample_rate);
|
|
28077
29148
|
}
|
|
28078
|
-
const wavPath =
|
|
29149
|
+
const wavPath = join35(tmpdir6(), `oa-voice-${Date.now()}.wav`);
|
|
28079
29150
|
this.writeWav(audioData, this.config.audio.sample_rate, wavPath);
|
|
28080
29151
|
await this.playWav(wavPath);
|
|
28081
29152
|
try {
|
|
@@ -28159,7 +29230,7 @@ var init_voice = __esm({
|
|
|
28159
29230
|
return buffer;
|
|
28160
29231
|
}
|
|
28161
29232
|
writeWav(samples, sampleRate, path) {
|
|
28162
|
-
|
|
29233
|
+
writeFileSync11(path, this.buildWavBuffer(samples, sampleRate));
|
|
28163
29234
|
}
|
|
28164
29235
|
// -------------------------------------------------------------------------
|
|
28165
29236
|
// Audio playback (system default speakers)
|
|
@@ -28233,30 +29304,30 @@ var init_voice = __esm({
|
|
|
28233
29304
|
return;
|
|
28234
29305
|
const arch = process.arch;
|
|
28235
29306
|
const isArmLinux = (arch === "arm64" || arch === "arm") && process.platform === "linux";
|
|
28236
|
-
|
|
28237
|
-
const pkgPath =
|
|
29307
|
+
mkdirSync11(voiceDir(), { recursive: true });
|
|
29308
|
+
const pkgPath = join35(voiceDir(), "package.json");
|
|
28238
29309
|
const expectedDeps = {
|
|
28239
29310
|
"onnxruntime-node": "^1.21.0",
|
|
28240
29311
|
"phonemizer": "^1.2.1"
|
|
28241
29312
|
};
|
|
28242
|
-
if (
|
|
29313
|
+
if (existsSync26(pkgPath)) {
|
|
28243
29314
|
try {
|
|
28244
|
-
const existing = JSON.parse(
|
|
29315
|
+
const existing = JSON.parse(readFileSync19(pkgPath, "utf8"));
|
|
28245
29316
|
if (!existing.dependencies?.["phonemizer"]) {
|
|
28246
29317
|
existing.dependencies = { ...existing.dependencies, ...expectedDeps };
|
|
28247
|
-
|
|
29318
|
+
writeFileSync11(pkgPath, JSON.stringify(existing, null, 2));
|
|
28248
29319
|
}
|
|
28249
29320
|
} catch {
|
|
28250
29321
|
}
|
|
28251
29322
|
}
|
|
28252
|
-
if (!
|
|
28253
|
-
|
|
29323
|
+
if (!existsSync26(pkgPath)) {
|
|
29324
|
+
writeFileSync11(pkgPath, JSON.stringify({
|
|
28254
29325
|
name: "open-agents-voice",
|
|
28255
29326
|
private: true,
|
|
28256
29327
|
dependencies: expectedDeps
|
|
28257
29328
|
}, null, 2));
|
|
28258
29329
|
}
|
|
28259
|
-
const voiceRequire = createRequire(
|
|
29330
|
+
const voiceRequire = createRequire(join35(voiceDir(), "index.js"));
|
|
28260
29331
|
try {
|
|
28261
29332
|
this.ort = voiceRequire("onnxruntime-node");
|
|
28262
29333
|
} catch {
|
|
@@ -28310,18 +29381,18 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
|
28310
29381
|
const dir = modelDir(id);
|
|
28311
29382
|
const onnxPath = modelOnnxPath(id);
|
|
28312
29383
|
const configPath = modelConfigPath(id);
|
|
28313
|
-
if (
|
|
29384
|
+
if (existsSync26(onnxPath) && existsSync26(configPath))
|
|
28314
29385
|
return;
|
|
28315
|
-
|
|
28316
|
-
if (!
|
|
29386
|
+
mkdirSync11(dir, { recursive: true });
|
|
29387
|
+
if (!existsSync26(configPath)) {
|
|
28317
29388
|
renderInfo(`Downloading ${model.label} voice config...`);
|
|
28318
29389
|
const configResp = await fetch(model.configUrl);
|
|
28319
29390
|
if (!configResp.ok)
|
|
28320
29391
|
throw new Error(`Failed to download config: HTTP ${configResp.status}`);
|
|
28321
29392
|
const configText = await configResp.text();
|
|
28322
|
-
|
|
29393
|
+
writeFileSync11(configPath, configText);
|
|
28323
29394
|
}
|
|
28324
|
-
if (!
|
|
29395
|
+
if (!existsSync26(onnxPath)) {
|
|
28325
29396
|
renderInfo(`Downloading ${model.label} voice model (this may take a minute)...`);
|
|
28326
29397
|
const onnxResp = await fetch(model.onnxUrl);
|
|
28327
29398
|
if (!onnxResp.ok)
|
|
@@ -28345,7 +29416,7 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
|
28345
29416
|
}
|
|
28346
29417
|
process.stdout.write("\r" + " ".repeat(60) + "\r");
|
|
28347
29418
|
const fullBuffer = Buffer.concat(chunks);
|
|
28348
|
-
|
|
29419
|
+
writeFileSync11(onnxPath, fullBuffer);
|
|
28349
29420
|
renderInfo(`${model.label} model downloaded (${formatBytes2(fullBuffer.length)}).`);
|
|
28350
29421
|
}
|
|
28351
29422
|
}
|
|
@@ -28357,10 +29428,10 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
|
28357
29428
|
throw new Error("ONNX runtime not loaded");
|
|
28358
29429
|
const onnxPath = modelOnnxPath(this.modelId);
|
|
28359
29430
|
const configPath = modelConfigPath(this.modelId);
|
|
28360
|
-
if (!
|
|
29431
|
+
if (!existsSync26(onnxPath) || !existsSync26(configPath)) {
|
|
28361
29432
|
throw new Error(`Model files not found for ${this.modelId}`);
|
|
28362
29433
|
}
|
|
28363
|
-
this.config = JSON.parse(
|
|
29434
|
+
this.config = JSON.parse(readFileSync19(configPath, "utf8"));
|
|
28364
29435
|
renderInfo("Loading voice model...");
|
|
28365
29436
|
this.session = await this.ort.InferenceSession.create(onnxPath, {
|
|
28366
29437
|
executionProviders: ["cpu"],
|
|
@@ -29223,13 +30294,13 @@ var init_stream_renderer = __esm({
|
|
|
29223
30294
|
});
|
|
29224
30295
|
|
|
29225
30296
|
// packages/cli/dist/tui/edit-history.js
|
|
29226
|
-
import { appendFileSync as appendFileSync2, mkdirSync as
|
|
29227
|
-
import { join as
|
|
30297
|
+
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync12 } from "node:fs";
|
|
30298
|
+
import { join as join36 } from "node:path";
|
|
29228
30299
|
function createEditHistoryLogger(repoRoot, sessionId) {
|
|
29229
|
-
const historyDir =
|
|
29230
|
-
const logPath =
|
|
30300
|
+
const historyDir = join36(repoRoot, ".oa", "history");
|
|
30301
|
+
const logPath = join36(historyDir, "edits.jsonl");
|
|
29231
30302
|
try {
|
|
29232
|
-
|
|
30303
|
+
mkdirSync12(historyDir, { recursive: true });
|
|
29233
30304
|
} catch {
|
|
29234
30305
|
}
|
|
29235
30306
|
function logToolCall(toolName, toolArgs, success) {
|
|
@@ -29338,15 +30409,15 @@ var init_edit_history = __esm({
|
|
|
29338
30409
|
});
|
|
29339
30410
|
|
|
29340
30411
|
// packages/cli/dist/tui/dream-engine.js
|
|
29341
|
-
import { mkdirSync as
|
|
29342
|
-
import { join as
|
|
30412
|
+
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync12, readFileSync as readFileSync20, existsSync as existsSync27, cpSync, rmSync, readdirSync as readdirSync10 } from "node:fs";
|
|
30413
|
+
import { join as join37, basename as basename12 } from "node:path";
|
|
29343
30414
|
import { execSync as execSync23 } from "node:child_process";
|
|
29344
30415
|
function loadAutoresearchMemory(repoRoot) {
|
|
29345
|
-
const memoryPath =
|
|
29346
|
-
if (!
|
|
30416
|
+
const memoryPath = join37(repoRoot, ".oa", "memory", "autoresearch.json");
|
|
30417
|
+
if (!existsSync27(memoryPath))
|
|
29347
30418
|
return "";
|
|
29348
30419
|
try {
|
|
29349
|
-
const raw =
|
|
30420
|
+
const raw = readFileSync20(memoryPath, "utf-8");
|
|
29350
30421
|
const data = JSON.parse(raw);
|
|
29351
30422
|
const sections = [];
|
|
29352
30423
|
for (const key of AUTORESEARCH_MEMORY_KEYS) {
|
|
@@ -29566,14 +30637,14 @@ var init_dream_engine = __esm({
|
|
|
29566
30637
|
const content = String(args["content"] ?? "");
|
|
29567
30638
|
if (!rawPath)
|
|
29568
30639
|
return { success: false, output: "", error: "path is required", durationMs: Date.now() - start };
|
|
29569
|
-
const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".oa/autoresearch") ?
|
|
30640
|
+
const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".oa/autoresearch") ? join37(this.autoresearchDir, basename12(rawPath)) : join37(this.autoresearchDir, rawPath);
|
|
29570
30641
|
if (!targetPath.startsWith(this.autoresearchDir)) {
|
|
29571
30642
|
return { success: false, output: "", error: "Autoresearch mode: writes are confined to .oa/autoresearch/", durationMs: Date.now() - start };
|
|
29572
30643
|
}
|
|
29573
30644
|
try {
|
|
29574
|
-
const dir =
|
|
29575
|
-
|
|
29576
|
-
|
|
30645
|
+
const dir = join37(targetPath, "..");
|
|
30646
|
+
mkdirSync13(dir, { recursive: true });
|
|
30647
|
+
writeFileSync12(targetPath, content, "utf-8");
|
|
29577
30648
|
return { success: true, output: `Wrote ${content.length} bytes to ${rawPath}`, durationMs: Date.now() - start };
|
|
29578
30649
|
} catch (err) {
|
|
29579
30650
|
return { success: false, output: "", error: String(err), durationMs: Date.now() - start };
|
|
@@ -29601,20 +30672,20 @@ var init_dream_engine = __esm({
|
|
|
29601
30672
|
const rawPath = String(args["path"] ?? "");
|
|
29602
30673
|
const oldStr = String(args["old_string"] ?? "");
|
|
29603
30674
|
const newStr = String(args["new_string"] ?? "");
|
|
29604
|
-
const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".oa/autoresearch") ?
|
|
30675
|
+
const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".oa/autoresearch") ? join37(this.autoresearchDir, basename12(rawPath)) : join37(this.autoresearchDir, rawPath);
|
|
29605
30676
|
if (!targetPath.startsWith(this.autoresearchDir)) {
|
|
29606
30677
|
return { success: false, output: "", error: "Autoresearch mode: edits are confined to .oa/autoresearch/", durationMs: Date.now() - start };
|
|
29607
30678
|
}
|
|
29608
30679
|
try {
|
|
29609
|
-
if (!
|
|
30680
|
+
if (!existsSync27(targetPath)) {
|
|
29610
30681
|
return { success: false, output: "", error: `File not found: ${rawPath}`, durationMs: Date.now() - start };
|
|
29611
30682
|
}
|
|
29612
|
-
let content =
|
|
30683
|
+
let content = readFileSync20(targetPath, "utf-8");
|
|
29613
30684
|
if (!content.includes(oldStr)) {
|
|
29614
30685
|
return { success: false, output: "", error: "old_string not found in file", durationMs: Date.now() - start };
|
|
29615
30686
|
}
|
|
29616
30687
|
content = content.replace(oldStr, newStr);
|
|
29617
|
-
|
|
30688
|
+
writeFileSync12(targetPath, content, "utf-8");
|
|
29618
30689
|
return { success: true, output: `Edited ${rawPath}`, durationMs: Date.now() - start };
|
|
29619
30690
|
} catch (err) {
|
|
29620
30691
|
return { success: false, output: "", error: String(err), durationMs: Date.now() - start };
|
|
@@ -29655,14 +30726,14 @@ var init_dream_engine = __esm({
|
|
|
29655
30726
|
const content = String(args["content"] ?? "");
|
|
29656
30727
|
if (!rawPath)
|
|
29657
30728
|
return { success: false, output: "", error: "path is required", durationMs: Date.now() - start };
|
|
29658
|
-
const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".oa/dreams") ?
|
|
30729
|
+
const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".oa/dreams") ? join37(this.dreamsDir, basename12(rawPath)) : join37(this.dreamsDir, rawPath);
|
|
29659
30730
|
if (!targetPath.startsWith(this.dreamsDir)) {
|
|
29660
30731
|
return { success: false, output: "", error: "Dream mode: writes are confined to .oa/dreams/", durationMs: Date.now() - start };
|
|
29661
30732
|
}
|
|
29662
30733
|
try {
|
|
29663
|
-
const dir =
|
|
29664
|
-
|
|
29665
|
-
|
|
30734
|
+
const dir = join37(targetPath, "..");
|
|
30735
|
+
mkdirSync13(dir, { recursive: true });
|
|
30736
|
+
writeFileSync12(targetPath, content, "utf-8");
|
|
29666
30737
|
return { success: true, output: `Wrote ${content.length} bytes to ${rawPath}`, durationMs: Date.now() - start };
|
|
29667
30738
|
} catch (err) {
|
|
29668
30739
|
return { success: false, output: "", error: String(err), durationMs: Date.now() - start };
|
|
@@ -29690,20 +30761,20 @@ var init_dream_engine = __esm({
|
|
|
29690
30761
|
const rawPath = String(args["path"] ?? "");
|
|
29691
30762
|
const oldStr = String(args["old_string"] ?? "");
|
|
29692
30763
|
const newStr = String(args["new_string"] ?? "");
|
|
29693
|
-
const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".oa/dreams") ?
|
|
30764
|
+
const targetPath = rawPath.startsWith("/") || rawPath.startsWith(".oa/dreams") ? join37(this.dreamsDir, basename12(rawPath)) : join37(this.dreamsDir, rawPath);
|
|
29694
30765
|
if (!targetPath.startsWith(this.dreamsDir)) {
|
|
29695
30766
|
return { success: false, output: "", error: "Dream mode: edits are confined to .oa/dreams/", durationMs: Date.now() - start };
|
|
29696
30767
|
}
|
|
29697
30768
|
try {
|
|
29698
|
-
if (!
|
|
30769
|
+
if (!existsSync27(targetPath)) {
|
|
29699
30770
|
return { success: false, output: "", error: `File not found: ${rawPath}`, durationMs: Date.now() - start };
|
|
29700
30771
|
}
|
|
29701
|
-
let content =
|
|
30772
|
+
let content = readFileSync20(targetPath, "utf-8");
|
|
29702
30773
|
if (!content.includes(oldStr)) {
|
|
29703
30774
|
return { success: false, output: "", error: "old_string not found in file", durationMs: Date.now() - start };
|
|
29704
30775
|
}
|
|
29705
30776
|
content = content.replace(oldStr, newStr);
|
|
29706
|
-
|
|
30777
|
+
writeFileSync12(targetPath, content, "utf-8");
|
|
29707
30778
|
return { success: true, output: `Edited ${rawPath}`, durationMs: Date.now() - start };
|
|
29708
30779
|
} catch (err) {
|
|
29709
30780
|
return { success: false, output: "", error: String(err), durationMs: Date.now() - start };
|
|
@@ -29757,7 +30828,7 @@ var init_dream_engine = __esm({
|
|
|
29757
30828
|
constructor(config, repoRoot) {
|
|
29758
30829
|
this.config = config;
|
|
29759
30830
|
this.repoRoot = repoRoot;
|
|
29760
|
-
this.dreamsDir =
|
|
30831
|
+
this.dreamsDir = join37(repoRoot, ".oa", "dreams");
|
|
29761
30832
|
this.state = {
|
|
29762
30833
|
mode: "default",
|
|
29763
30834
|
active: false,
|
|
@@ -29788,7 +30859,7 @@ var init_dream_engine = __esm({
|
|
|
29788
30859
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
29789
30860
|
results: []
|
|
29790
30861
|
};
|
|
29791
|
-
|
|
30862
|
+
mkdirSync13(this.dreamsDir, { recursive: true });
|
|
29792
30863
|
this.saveDreamState();
|
|
29793
30864
|
try {
|
|
29794
30865
|
for (let cycle = 1; cycle <= totalCycles; cycle++) {
|
|
@@ -29841,8 +30912,8 @@ ${result.summary}`;
|
|
|
29841
30912
|
if (mode !== "default" || cycle === totalCycles) {
|
|
29842
30913
|
renderDreamContraction(cycle);
|
|
29843
30914
|
const cycleSummary = this.buildCycleSummary(cycle, previousFindings);
|
|
29844
|
-
const summaryPath =
|
|
29845
|
-
|
|
30915
|
+
const summaryPath = join37(this.dreamsDir, `cycle-${cycle}-summary.md`);
|
|
30916
|
+
writeFileSync12(summaryPath, cycleSummary, "utf-8");
|
|
29846
30917
|
}
|
|
29847
30918
|
if (mode === "lucid" && !this.abortController.signal.aborted) {
|
|
29848
30919
|
this.saveVersionCheckpoint(cycle);
|
|
@@ -30054,7 +31125,7 @@ After synthesis, call task_complete with the final prioritized summary.`, toolMo
|
|
|
30054
31125
|
}
|
|
30055
31126
|
/** Build role-specific tool sets for swarm agents */
|
|
30056
31127
|
buildSwarmTools(role, _workspace) {
|
|
30057
|
-
const autoresearchDir =
|
|
31128
|
+
const autoresearchDir = join37(this.repoRoot, ".oa", "autoresearch");
|
|
30058
31129
|
const taskComplete = this.createSwarmTaskCompleteTool(role);
|
|
30059
31130
|
switch (role) {
|
|
30060
31131
|
case "researcher": {
|
|
@@ -30418,7 +31489,7 @@ INSTRUCTIONS:
|
|
|
30418
31489
|
2. Summarize the key learnings and next steps
|
|
30419
31490
|
|
|
30420
31491
|
Call task_complete with a human-readable summary of the autoresearch session.`, workspace, onEvent);
|
|
30421
|
-
const reportPath =
|
|
31492
|
+
const reportPath = join37(this.dreamsDir, `cycle-${cycleNum}-autoresearch-report.md`);
|
|
30422
31493
|
const report = `# Autoresearch Swarm Report \u2014 Cycle ${cycleNum}
|
|
30423
31494
|
|
|
30424
31495
|
**Date**: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}
|
|
@@ -30440,8 +31511,8 @@ ${summaryResult}
|
|
|
30440
31511
|
*Generated by open-agents autoresearch swarm*
|
|
30441
31512
|
`;
|
|
30442
31513
|
try {
|
|
30443
|
-
|
|
30444
|
-
|
|
31514
|
+
mkdirSync13(this.dreamsDir, { recursive: true });
|
|
31515
|
+
writeFileSync12(reportPath, report, "utf-8");
|
|
30445
31516
|
} catch {
|
|
30446
31517
|
}
|
|
30447
31518
|
renderSwarmComplete(workspace);
|
|
@@ -30507,9 +31578,9 @@ ${summaryResult}
|
|
|
30507
31578
|
}
|
|
30508
31579
|
/** Save workspace backup for lucid mode */
|
|
30509
31580
|
saveVersionCheckpoint(cycle) {
|
|
30510
|
-
const checkpointDir =
|
|
31581
|
+
const checkpointDir = join37(this.dreamsDir, "checkpoints", `cycle-${cycle}`);
|
|
30511
31582
|
try {
|
|
30512
|
-
|
|
31583
|
+
mkdirSync13(checkpointDir, { recursive: true });
|
|
30513
31584
|
try {
|
|
30514
31585
|
const gitStatus = execSync23("git status --porcelain", {
|
|
30515
31586
|
cwd: this.repoRoot,
|
|
@@ -30526,10 +31597,10 @@ ${summaryResult}
|
|
|
30526
31597
|
encoding: "utf-8",
|
|
30527
31598
|
timeout: 5e3
|
|
30528
31599
|
}).trim();
|
|
30529
|
-
|
|
30530
|
-
|
|
30531
|
-
|
|
30532
|
-
|
|
31600
|
+
writeFileSync12(join37(checkpointDir, "git-status.txt"), gitStatus, "utf-8");
|
|
31601
|
+
writeFileSync12(join37(checkpointDir, "git-diff.patch"), gitDiff, "utf-8");
|
|
31602
|
+
writeFileSync12(join37(checkpointDir, "git-hash.txt"), gitHash, "utf-8");
|
|
31603
|
+
writeFileSync12(join37(checkpointDir, "checkpoint.json"), JSON.stringify({
|
|
30533
31604
|
cycle,
|
|
30534
31605
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
30535
31606
|
gitHash,
|
|
@@ -30537,7 +31608,7 @@ ${summaryResult}
|
|
|
30537
31608
|
}, null, 2), "utf-8");
|
|
30538
31609
|
renderInfo(`Checkpoint saved: cycle ${cycle} (${gitHash.slice(0, 8)})`);
|
|
30539
31610
|
} catch {
|
|
30540
|
-
|
|
31611
|
+
writeFileSync12(join37(checkpointDir, "checkpoint.json"), JSON.stringify({ cycle, timestamp: (/* @__PURE__ */ new Date()).toISOString(), mode: this.state.mode }, null, 2), "utf-8");
|
|
30541
31612
|
renderInfo(`Checkpoint saved: cycle ${cycle} (no git)`);
|
|
30542
31613
|
}
|
|
30543
31614
|
} catch (err) {
|
|
@@ -30595,14 +31666,14 @@ ${files.map((f) => `- [\`${f}\`](./${f})`).join("\n")}
|
|
|
30595
31666
|
---
|
|
30596
31667
|
*Auto-generated by open-agents dream engine*
|
|
30597
31668
|
`;
|
|
30598
|
-
|
|
31669
|
+
writeFileSync12(join37(this.dreamsDir, "PROPOSAL-INDEX.md"), index, "utf-8");
|
|
30599
31670
|
} catch {
|
|
30600
31671
|
}
|
|
30601
31672
|
}
|
|
30602
31673
|
/** Save dream state for resume/inspection */
|
|
30603
31674
|
saveDreamState() {
|
|
30604
31675
|
try {
|
|
30605
|
-
|
|
31676
|
+
writeFileSync12(join37(this.dreamsDir, "dream-state.json"), JSON.stringify(this.state, null, 2) + "\n", "utf-8");
|
|
30606
31677
|
} catch {
|
|
30607
31678
|
}
|
|
30608
31679
|
}
|
|
@@ -30755,8 +31826,8 @@ var init_bless_engine = __esm({
|
|
|
30755
31826
|
});
|
|
30756
31827
|
|
|
30757
31828
|
// packages/cli/dist/tui/dmn-engine.js
|
|
30758
|
-
import { existsSync as
|
|
30759
|
-
import { join as
|
|
31829
|
+
import { existsSync as existsSync28, readFileSync as readFileSync21, writeFileSync as writeFileSync13, mkdirSync as mkdirSync14, readdirSync as readdirSync11, unlinkSync as unlinkSync5 } from "node:fs";
|
|
31830
|
+
import { join as join38, basename as basename13 } from "node:path";
|
|
30760
31831
|
function buildDMNGatherPrompt(recentTaskSummaries, dueReminders, attentionItems, memoryTopics, capabilities, competence, reflectionBuffer) {
|
|
30761
31832
|
const competenceReport = competence.length > 0 ? competence.map((c3) => {
|
|
30762
31833
|
const rate = c3.attempts > 0 ? Math.round(c3.successes / c3.attempts * 100) : 0;
|
|
@@ -30988,9 +32059,9 @@ var init_dmn_engine = __esm({
|
|
|
30988
32059
|
constructor(config, repoRoot) {
|
|
30989
32060
|
this.config = config;
|
|
30990
32061
|
this.repoRoot = repoRoot;
|
|
30991
|
-
this.stateDir =
|
|
30992
|
-
this.historyDir =
|
|
30993
|
-
|
|
32062
|
+
this.stateDir = join38(repoRoot, ".oa", "dmn");
|
|
32063
|
+
this.historyDir = join38(repoRoot, ".oa", "dmn", "cycles");
|
|
32064
|
+
mkdirSync14(this.historyDir, { recursive: true });
|
|
30994
32065
|
this.loadState();
|
|
30995
32066
|
}
|
|
30996
32067
|
get stats() {
|
|
@@ -31579,11 +32650,11 @@ OUTPUT: Call task_complete with JSON:
|
|
|
31579
32650
|
async gatherMemoryTopics() {
|
|
31580
32651
|
const topics = [];
|
|
31581
32652
|
const dirs = [
|
|
31582
|
-
|
|
31583
|
-
|
|
32653
|
+
join38(this.repoRoot, ".oa", "memory"),
|
|
32654
|
+
join38(this.repoRoot, ".open-agents", "memory")
|
|
31584
32655
|
];
|
|
31585
32656
|
for (const dir of dirs) {
|
|
31586
|
-
if (!
|
|
32657
|
+
if (!existsSync28(dir))
|
|
31587
32658
|
continue;
|
|
31588
32659
|
try {
|
|
31589
32660
|
const files = readdirSync11(dir).filter((f) => f.endsWith(".json"));
|
|
@@ -31599,29 +32670,29 @@ OUTPUT: Call task_complete with JSON:
|
|
|
31599
32670
|
}
|
|
31600
32671
|
// ── State persistence ─────────────────────────────────────────────────
|
|
31601
32672
|
loadState() {
|
|
31602
|
-
const path =
|
|
31603
|
-
if (
|
|
32673
|
+
const path = join38(this.stateDir, "state.json");
|
|
32674
|
+
if (existsSync28(path)) {
|
|
31604
32675
|
try {
|
|
31605
|
-
this.state = JSON.parse(
|
|
32676
|
+
this.state = JSON.parse(readFileSync21(path, "utf-8"));
|
|
31606
32677
|
} catch {
|
|
31607
32678
|
}
|
|
31608
32679
|
}
|
|
31609
32680
|
}
|
|
31610
32681
|
saveState() {
|
|
31611
32682
|
try {
|
|
31612
|
-
|
|
32683
|
+
writeFileSync13(join38(this.stateDir, "state.json"), JSON.stringify(this.state, null, 2) + "\n", "utf-8");
|
|
31613
32684
|
} catch {
|
|
31614
32685
|
}
|
|
31615
32686
|
}
|
|
31616
32687
|
saveCycleResult(result) {
|
|
31617
32688
|
try {
|
|
31618
32689
|
const filename = `cycle-${result.cycleNumber}-${Date.now()}.json`;
|
|
31619
|
-
|
|
32690
|
+
writeFileSync13(join38(this.historyDir, filename), JSON.stringify(result, null, 2) + "\n", "utf-8");
|
|
31620
32691
|
const files = readdirSync11(this.historyDir).filter((f) => f.startsWith("cycle-") && f.endsWith(".json")).sort();
|
|
31621
32692
|
if (files.length > 50) {
|
|
31622
32693
|
for (const old of files.slice(0, files.length - 50)) {
|
|
31623
32694
|
try {
|
|
31624
|
-
unlinkSync5(
|
|
32695
|
+
unlinkSync5(join38(this.historyDir, old));
|
|
31625
32696
|
} catch {
|
|
31626
32697
|
}
|
|
31627
32698
|
}
|
|
@@ -31634,8 +32705,8 @@ OUTPUT: Call task_complete with JSON:
|
|
|
31634
32705
|
});
|
|
31635
32706
|
|
|
31636
32707
|
// packages/cli/dist/tui/snr-engine.js
|
|
31637
|
-
import { existsSync as
|
|
31638
|
-
import { join as
|
|
32708
|
+
import { existsSync as existsSync29, readdirSync as readdirSync12, readFileSync as readFileSync22 } from "node:fs";
|
|
32709
|
+
import { join as join39, basename as basename14 } from "node:path";
|
|
31639
32710
|
function computeDPrime(signalScores, noiseScores) {
|
|
31640
32711
|
if (signalScores.length === 0 || noiseScores.length === 0)
|
|
31641
32712
|
return 0;
|
|
@@ -31882,11 +32953,11 @@ Call task_complete with the JSON array when done.`, onEvent)
|
|
|
31882
32953
|
loadMemoryEntries(topics) {
|
|
31883
32954
|
const entries = [];
|
|
31884
32955
|
const dirs = [
|
|
31885
|
-
|
|
31886
|
-
|
|
32956
|
+
join39(this.repoRoot, ".oa", "memory"),
|
|
32957
|
+
join39(this.repoRoot, ".open-agents", "memory")
|
|
31887
32958
|
];
|
|
31888
32959
|
for (const dir of dirs) {
|
|
31889
|
-
if (!
|
|
32960
|
+
if (!existsSync29(dir))
|
|
31890
32961
|
continue;
|
|
31891
32962
|
try {
|
|
31892
32963
|
const files = readdirSync12(dir).filter((f) => f.endsWith(".json"));
|
|
@@ -31895,7 +32966,7 @@ Call task_complete with the JSON array when done.`, onEvent)
|
|
|
31895
32966
|
if (topics.length > 0 && !topics.includes(topic))
|
|
31896
32967
|
continue;
|
|
31897
32968
|
try {
|
|
31898
|
-
const data = JSON.parse(
|
|
32969
|
+
const data = JSON.parse(readFileSync22(join39(dir, f), "utf-8"));
|
|
31899
32970
|
for (const [key, val] of Object.entries(data)) {
|
|
31900
32971
|
const value = typeof val === "object" && val !== null && "value" in val ? String(val.value) : String(val);
|
|
31901
32972
|
entries.push({ topic, key, value });
|
|
@@ -32348,8 +33419,8 @@ var init_tool_policy = __esm({
|
|
|
32348
33419
|
});
|
|
32349
33420
|
|
|
32350
33421
|
// packages/cli/dist/tui/telegram-bridge.js
|
|
32351
|
-
import { mkdirSync as
|
|
32352
|
-
import { join as
|
|
33422
|
+
import { mkdirSync as mkdirSync15, existsSync as existsSync30, unlinkSync as unlinkSync6, readdirSync as readdirSync13, statSync as statSync10 } from "node:fs";
|
|
33423
|
+
import { join as join40, resolve as resolve24 } from "node:path";
|
|
32353
33424
|
import { writeFile as writeFileAsync } from "node:fs/promises";
|
|
32354
33425
|
function convertMarkdownToTelegramHTML(md) {
|
|
32355
33426
|
let html = md;
|
|
@@ -32677,7 +33748,7 @@ with summary "no_reply" to silently skip without responding.
|
|
|
32677
33748
|
this.polling = true;
|
|
32678
33749
|
this.abortController = new AbortController();
|
|
32679
33750
|
try {
|
|
32680
|
-
|
|
33751
|
+
mkdirSync15(this.mediaCacheDir, { recursive: true });
|
|
32681
33752
|
} catch {
|
|
32682
33753
|
}
|
|
32683
33754
|
this.mediaCacheCleanupTimer = setInterval(() => this.cleanupMediaCache(), 5 * 60 * 1e3);
|
|
@@ -33101,7 +34172,7 @@ Telegram admin: @${msg.username}` : `Telegram ${isGroup ? "group" : "public"} ch
|
|
|
33101
34172
|
return null;
|
|
33102
34173
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
33103
34174
|
const fileName = `${Date.now()}-${fileId.slice(0, 8)}${extension}`;
|
|
33104
|
-
const localPath =
|
|
34175
|
+
const localPath = join40(this.mediaCacheDir, fileName);
|
|
33105
34176
|
await writeFileAsync(localPath, buffer);
|
|
33106
34177
|
return localPath;
|
|
33107
34178
|
} catch {
|
|
@@ -34823,11 +35894,11 @@ var init_status_bar = __esm({
|
|
|
34823
35894
|
import * as readline2 from "node:readline";
|
|
34824
35895
|
import { Writable } from "node:stream";
|
|
34825
35896
|
import { cwd } from "node:process";
|
|
34826
|
-
import { resolve as resolve25, join as
|
|
35897
|
+
import { resolve as resolve25, join as join41, dirname as dirname13, extname as extname9 } from "node:path";
|
|
34827
35898
|
import { createRequire as createRequire2 } from "node:module";
|
|
34828
35899
|
import { fileURLToPath as fileURLToPath9 } from "node:url";
|
|
34829
|
-
import { readFileSync as
|
|
34830
|
-
import { existsSync as
|
|
35900
|
+
import { readFileSync as readFileSync23, rmSync as rmSync2, readdirSync as readdirSync14 } from "node:fs";
|
|
35901
|
+
import { existsSync as existsSync31 } from "node:fs";
|
|
34831
35902
|
function formatTimeAgo(date) {
|
|
34832
35903
|
const seconds = Math.floor((Date.now() - date.getTime()) / 1e3);
|
|
34833
35904
|
if (seconds < 60)
|
|
@@ -34844,14 +35915,14 @@ function formatTimeAgo(date) {
|
|
|
34844
35915
|
function getVersion() {
|
|
34845
35916
|
try {
|
|
34846
35917
|
const require2 = createRequire2(import.meta.url);
|
|
34847
|
-
const thisDir =
|
|
35918
|
+
const thisDir = dirname13(fileURLToPath9(import.meta.url));
|
|
34848
35919
|
const candidates = [
|
|
34849
|
-
|
|
34850
|
-
|
|
34851
|
-
|
|
35920
|
+
join41(thisDir, "..", "package.json"),
|
|
35921
|
+
join41(thisDir, "..", "..", "package.json"),
|
|
35922
|
+
join41(thisDir, "..", "..", "..", "package.json")
|
|
34852
35923
|
];
|
|
34853
35924
|
for (const pkgPath of candidates) {
|
|
34854
|
-
if (
|
|
35925
|
+
if (existsSync31(pkgPath)) {
|
|
34855
35926
|
const pkg = require2(pkgPath);
|
|
34856
35927
|
if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli") {
|
|
34857
35928
|
return pkg.version ?? "0.0.0";
|
|
@@ -35045,15 +36116,15 @@ Use task_status("${taskId}") or task_output("${taskId}") to check progress.`
|
|
|
35045
36116
|
function gatherMemorySnippets(root) {
|
|
35046
36117
|
const snippets = [];
|
|
35047
36118
|
const dirs = [
|
|
35048
|
-
|
|
35049
|
-
|
|
36119
|
+
join41(root, ".oa", "memory"),
|
|
36120
|
+
join41(root, ".open-agents", "memory")
|
|
35050
36121
|
];
|
|
35051
36122
|
for (const dir of dirs) {
|
|
35052
|
-
if (!
|
|
36123
|
+
if (!existsSync31(dir))
|
|
35053
36124
|
continue;
|
|
35054
36125
|
try {
|
|
35055
36126
|
for (const f of readdirSync14(dir).filter((f2) => f2.endsWith(".json"))) {
|
|
35056
|
-
const data = JSON.parse(
|
|
36127
|
+
const data = JSON.parse(readFileSync23(join41(dir, f), "utf-8"));
|
|
35057
36128
|
for (const val of Object.values(data)) {
|
|
35058
36129
|
const v = typeof val === "object" && val !== null && "value" in val ? String(val.value) : String(val);
|
|
35059
36130
|
if (v.length > 10)
|
|
@@ -35666,6 +36737,9 @@ async function startInteractive(config, repoPath) {
|
|
|
35666
36737
|
const voiceEngine = new VoiceEngine();
|
|
35667
36738
|
let voiceSession = null;
|
|
35668
36739
|
let exposeGateway = null;
|
|
36740
|
+
let peerMesh = null;
|
|
36741
|
+
let inferenceRouter = null;
|
|
36742
|
+
const secretVault = new SecretVault(join41(repoRoot, ".oa", "vault.enc"));
|
|
35669
36743
|
let adminSessionKey = null;
|
|
35670
36744
|
const callSubAgents = /* @__PURE__ */ new Map();
|
|
35671
36745
|
const streamRenderer = new StreamRenderer();
|
|
@@ -35827,6 +36901,9 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
|
|
|
35827
36901
|
"/stop",
|
|
35828
36902
|
"/resume",
|
|
35829
36903
|
"/expose",
|
|
36904
|
+
"/p2p",
|
|
36905
|
+
"/secrets",
|
|
36906
|
+
"/vault",
|
|
35830
36907
|
"/full-send-bless",
|
|
35831
36908
|
"/bless",
|
|
35832
36909
|
"/telegram",
|
|
@@ -36562,6 +37639,80 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
36562
37639
|
stats: exposeGateway.formatStats()
|
|
36563
37640
|
};
|
|
36564
37641
|
},
|
|
37642
|
+
// ── P2P mesh controls ────────────────────────────────────────────────
|
|
37643
|
+
async p2pStart(bootstrapPeers) {
|
|
37644
|
+
if (peerMesh)
|
|
37645
|
+
throw new Error("P2P mesh is already running");
|
|
37646
|
+
let models = [];
|
|
37647
|
+
try {
|
|
37648
|
+
const resp = await fetch(`${commandCtx.config.backendUrl ?? "http://127.0.0.1:11434"}/api/tags`);
|
|
37649
|
+
const data = await resp.json();
|
|
37650
|
+
models = data.models?.map((m) => m.name) ?? [];
|
|
37651
|
+
} catch {
|
|
37652
|
+
}
|
|
37653
|
+
peerMesh = new PeerMesh({
|
|
37654
|
+
displayName: commandCtx.config.model ?? "open-agents-node",
|
|
37655
|
+
capabilities: { models, gpuVramGB: 0, teeAttested: false, maxContextTokens: 32768, load: 0 },
|
|
37656
|
+
bootstrapPeers
|
|
37657
|
+
});
|
|
37658
|
+
const port = await peerMesh.start();
|
|
37659
|
+
inferenceRouter = new InferenceRouter({
|
|
37660
|
+
mesh: peerMesh,
|
|
37661
|
+
vault: secretVault
|
|
37662
|
+
});
|
|
37663
|
+
return { peerId: peerMesh.peerId, port };
|
|
37664
|
+
},
|
|
37665
|
+
async p2pStop() {
|
|
37666
|
+
if (peerMesh) {
|
|
37667
|
+
await peerMesh.stop();
|
|
37668
|
+
peerMesh = null;
|
|
37669
|
+
inferenceRouter = null;
|
|
37670
|
+
}
|
|
37671
|
+
},
|
|
37672
|
+
async p2pConnect(endpoint, trust) {
|
|
37673
|
+
if (!peerMesh)
|
|
37674
|
+
throw new Error("P2P mesh is not running \u2014 use /p2p start first");
|
|
37675
|
+
const trustTier = trust ?? "public";
|
|
37676
|
+
const peer = await peerMesh.connectToPeer(endpoint, trustTier);
|
|
37677
|
+
return peer?.peerId ?? null;
|
|
37678
|
+
},
|
|
37679
|
+
p2pStatus() {
|
|
37680
|
+
if (!peerMesh)
|
|
37681
|
+
return "P2P mesh is not running.";
|
|
37682
|
+
const peers = peerMesh.getPeers();
|
|
37683
|
+
const connected = peers.filter((p) => p.connected);
|
|
37684
|
+
const lines = [
|
|
37685
|
+
`Node: ${peerMesh.peerId} (port ${peerMesh.port})`,
|
|
37686
|
+
`Peers: ${connected.length} connected / ${peers.length} known`
|
|
37687
|
+
];
|
|
37688
|
+
if (inferenceRouter) {
|
|
37689
|
+
const stats = inferenceRouter.getStats();
|
|
37690
|
+
lines.push(`Routed: ${stats.totalRouted} | Redacted: ${stats.totalRedacted} | Errors: ${stats.totalErrors}`);
|
|
37691
|
+
if (stats.availableModels.length > 0) {
|
|
37692
|
+
lines.push(`Models: ${stats.availableModels.join(", ")}`);
|
|
37693
|
+
}
|
|
37694
|
+
}
|
|
37695
|
+
for (const p of connected) {
|
|
37696
|
+
lines.push(` ${p.peerId.slice(0, 8)}\u2026 [${p.trust}] ${p.latencyMs}ms \u2014 ${p.capabilities.models.join(", ") || "no models"}`);
|
|
37697
|
+
}
|
|
37698
|
+
return lines.join("\n");
|
|
37699
|
+
},
|
|
37700
|
+
isP2PActive() {
|
|
37701
|
+
return peerMesh !== null;
|
|
37702
|
+
},
|
|
37703
|
+
// ── Secret vault controls ────────────────────────────────────────────
|
|
37704
|
+
secretSet(name, value, minTrust) {
|
|
37705
|
+
secretVault.set(name, value, minTrust ?? "verified");
|
|
37706
|
+
},
|
|
37707
|
+
secretDelete(name) {
|
|
37708
|
+
return secretVault.delete(name);
|
|
37709
|
+
},
|
|
37710
|
+
secretList() {
|
|
37711
|
+
return secretVault.list();
|
|
37712
|
+
},
|
|
37713
|
+
secretImportEnv() {
|
|
37714
|
+
return secretVault.importFromEnv();
|
|
37715
|
+
},
|
|
36565
37716
|
async sendCallButton(chatId, url) {
|
|
36566
37717
|
if (telegramBridge?.isActive) {
|
|
36567
37718
|
await telegramBridge.sendCallButton(chatId, url);
|
|
@@ -36631,8 +37782,8 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
36631
37782
|
return true;
|
|
36632
37783
|
},
|
|
36633
37784
|
destroyProject() {
|
|
36634
|
-
const oaPath =
|
|
36635
|
-
if (
|
|
37785
|
+
const oaPath = join41(repoRoot, OA_DIR);
|
|
37786
|
+
if (existsSync31(oaPath)) {
|
|
36636
37787
|
try {
|
|
36637
37788
|
rmSync2(oaPath, { recursive: true, force: true });
|
|
36638
37789
|
writeContent(() => renderInfo(`Removed ${OA_DIR}/ directory.`));
|
|
@@ -36916,13 +38067,13 @@ Execute this skill now. Follow the behavioral guidance above.`;
|
|
|
36916
38067
|
}
|
|
36917
38068
|
}
|
|
36918
38069
|
const cleanPath = input.replace(/^['"]|['"]$/g, "").trim();
|
|
36919
|
-
const isImage = isImagePath(cleanPath) &&
|
|
36920
|
-
const isMedia = !isImage && isTranscribablePath(cleanPath) &&
|
|
38070
|
+
const isImage = isImagePath(cleanPath) && existsSync31(resolve25(repoRoot, cleanPath));
|
|
38071
|
+
const isMedia = !isImage && isTranscribablePath(cleanPath) && existsSync31(resolve25(repoRoot, cleanPath));
|
|
36921
38072
|
if (activeTask) {
|
|
36922
38073
|
if (isImage) {
|
|
36923
38074
|
try {
|
|
36924
38075
|
const imgPath = resolve25(repoRoot, cleanPath);
|
|
36925
|
-
const imgBuffer =
|
|
38076
|
+
const imgBuffer = readFileSync23(imgPath);
|
|
36926
38077
|
const base64 = imgBuffer.toString("base64");
|
|
36927
38078
|
const ext = extname9(cleanPath).toLowerCase();
|
|
36928
38079
|
const mime = ext === ".png" ? "image/png" : ext === ".gif" ? "image/gif" : ext === ".webp" ? "image/webp" : "image/jpeg";
|
|
@@ -37105,13 +38256,13 @@ NEW TASK: ${fullInput}`;
|
|
|
37105
38256
|
writeContent(() => renderError(errMsg));
|
|
37106
38257
|
if (failureStore) {
|
|
37107
38258
|
try {
|
|
37108
|
-
const { createHash:
|
|
38259
|
+
const { createHash: createHash4 } = await import("node:crypto");
|
|
37109
38260
|
failureStore.insert({
|
|
37110
38261
|
taskId: "",
|
|
37111
38262
|
sessionId: `${Date.now()}`,
|
|
37112
38263
|
repoRoot,
|
|
37113
38264
|
failureType: "runtime-error",
|
|
37114
|
-
fingerprint:
|
|
38265
|
+
fingerprint: createHash4("sha256").update(errMsg.slice(0, 200)).digest("hex").slice(0, 16),
|
|
37115
38266
|
filePath: null,
|
|
37116
38267
|
errorMessage: errMsg.slice(0, 500),
|
|
37117
38268
|
context: null,
|
|
@@ -37232,6 +38383,12 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
|
|
|
37232
38383
|
exposeGateway.stop().catch(() => {
|
|
37233
38384
|
});
|
|
37234
38385
|
}
|
|
38386
|
+
if (peerMesh) {
|
|
38387
|
+
peerMesh.stop().catch(() => {
|
|
38388
|
+
});
|
|
38389
|
+
peerMesh = null;
|
|
38390
|
+
inferenceRouter = null;
|
|
38391
|
+
}
|
|
37235
38392
|
statusBar.deactivate();
|
|
37236
38393
|
const closeRows = process.stdout.rows ?? 24;
|
|
37237
38394
|
process.stdout.write(`\x1B[${closeRows - 4};1H\x1B[J
|
|
@@ -37321,6 +38478,7 @@ var init_interactive = __esm({
|
|
|
37321
38478
|
init_listen();
|
|
37322
38479
|
init_voice_session();
|
|
37323
38480
|
init_expose();
|
|
38481
|
+
init_p2p();
|
|
37324
38482
|
init_call_agent();
|
|
37325
38483
|
init_config();
|
|
37326
38484
|
init_updater();
|
|
@@ -37378,8 +38536,8 @@ var init_run = __esm({
|
|
|
37378
38536
|
import { glob } from "glob";
|
|
37379
38537
|
import ignore from "ignore";
|
|
37380
38538
|
import { readFile as readFile14, stat as stat4 } from "node:fs/promises";
|
|
37381
|
-
import { createHash } from "node:crypto";
|
|
37382
|
-
import { join as
|
|
38539
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
38540
|
+
import { join as join42, relative as relative3, extname as extname10, basename as basename15 } from "node:path";
|
|
37383
38541
|
var DEFAULT_EXCLUDE, LANGUAGE_MAP, CodebaseIndexer;
|
|
37384
38542
|
var init_codebase_indexer = __esm({
|
|
37385
38543
|
"packages/indexer/dist/codebase-indexer.js"() {
|
|
@@ -37423,7 +38581,7 @@ var init_codebase_indexer = __esm({
|
|
|
37423
38581
|
const ig = ignore.default();
|
|
37424
38582
|
if (this.config.respectGitignore) {
|
|
37425
38583
|
try {
|
|
37426
|
-
const gitignoreContent = await readFile14(
|
|
38584
|
+
const gitignoreContent = await readFile14(join42(this.config.rootDir, ".gitignore"), "utf-8");
|
|
37427
38585
|
ig.add(gitignoreContent);
|
|
37428
38586
|
} catch {
|
|
37429
38587
|
}
|
|
@@ -37438,13 +38596,13 @@ var init_codebase_indexer = __esm({
|
|
|
37438
38596
|
for (const relativePath of files) {
|
|
37439
38597
|
if (ig.ignores(relativePath))
|
|
37440
38598
|
continue;
|
|
37441
|
-
const fullPath =
|
|
38599
|
+
const fullPath = join42(this.config.rootDir, relativePath);
|
|
37442
38600
|
try {
|
|
37443
38601
|
const fileStat = await stat4(fullPath);
|
|
37444
38602
|
if (fileStat.size > this.config.maxFileSize)
|
|
37445
38603
|
continue;
|
|
37446
38604
|
const content = await readFile14(fullPath);
|
|
37447
|
-
const hash =
|
|
38605
|
+
const hash = createHash3("sha256").update(content).digest("hex");
|
|
37448
38606
|
const ext = extname10(relativePath);
|
|
37449
38607
|
indexed.push({
|
|
37450
38608
|
path: fullPath,
|
|
@@ -37484,7 +38642,7 @@ var init_codebase_indexer = __esm({
|
|
|
37484
38642
|
if (!child) {
|
|
37485
38643
|
child = {
|
|
37486
38644
|
name: part,
|
|
37487
|
-
path:
|
|
38645
|
+
path: join42(current.path, part),
|
|
37488
38646
|
type: "directory",
|
|
37489
38647
|
children: []
|
|
37490
38648
|
};
|
|
@@ -37567,13 +38725,13 @@ __export(index_repo_exports, {
|
|
|
37567
38725
|
indexRepoCommand: () => indexRepoCommand
|
|
37568
38726
|
});
|
|
37569
38727
|
import { resolve as resolve26 } from "node:path";
|
|
37570
|
-
import { existsSync as
|
|
38728
|
+
import { existsSync as existsSync32, statSync as statSync11 } from "node:fs";
|
|
37571
38729
|
import { cwd as cwd2 } from "node:process";
|
|
37572
38730
|
async function indexRepoCommand(opts, _config) {
|
|
37573
38731
|
const repoRoot = resolve26(opts.repoPath ?? cwd2());
|
|
37574
38732
|
printHeader("Index Repository");
|
|
37575
38733
|
printInfo(`Indexing: ${repoRoot}`);
|
|
37576
|
-
if (!
|
|
38734
|
+
if (!existsSync32(repoRoot)) {
|
|
37577
38735
|
printError(`Path does not exist: ${repoRoot}`);
|
|
37578
38736
|
process.exit(1);
|
|
37579
38737
|
}
|
|
@@ -37825,7 +38983,7 @@ var config_exports = {};
|
|
|
37825
38983
|
__export(config_exports, {
|
|
37826
38984
|
configCommand: () => configCommand
|
|
37827
38985
|
});
|
|
37828
|
-
import { join as
|
|
38986
|
+
import { join as join43, resolve as resolve27 } from "node:path";
|
|
37829
38987
|
import { homedir as homedir13 } from "node:os";
|
|
37830
38988
|
import { cwd as cwd3 } from "node:process";
|
|
37831
38989
|
function redactIfSensitive(key, value) {
|
|
@@ -37884,7 +39042,7 @@ function handleShow(opts, config) {
|
|
|
37884
39042
|
}
|
|
37885
39043
|
}
|
|
37886
39044
|
printSection("Config File");
|
|
37887
|
-
printInfo(`~/.open-agents/config.json (${
|
|
39045
|
+
printInfo(`~/.open-agents/config.json (${join43(homedir13(), ".open-agents", "config.json")})`);
|
|
37888
39046
|
printSection("Priority Chain");
|
|
37889
39047
|
printInfo(" 1. CLI flags (--model, --backend-url, etc.)");
|
|
37890
39048
|
printInfo(" 2. Project .oa/settings.json (--local)");
|
|
@@ -37923,7 +39081,7 @@ function handleSet(opts, _config) {
|
|
|
37923
39081
|
const coerced = coerceForSettings(key, value);
|
|
37924
39082
|
saveProjectSettings(repoRoot, { [key]: coerced });
|
|
37925
39083
|
printSuccess(`Project override set: ${key} = ${redactIfSensitive(key, value)}`);
|
|
37926
|
-
printInfo(`Saved to ${
|
|
39084
|
+
printInfo(`Saved to ${join43(repoRoot, ".oa", "settings.json")}`);
|
|
37927
39085
|
printInfo("This override applies only when running in this workspace.");
|
|
37928
39086
|
} catch (err) {
|
|
37929
39087
|
printError(`Failed to save: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -38142,8 +39300,8 @@ __export(eval_exports, {
|
|
|
38142
39300
|
evalCommand: () => evalCommand
|
|
38143
39301
|
});
|
|
38144
39302
|
import { tmpdir as tmpdir7 } from "node:os";
|
|
38145
|
-
import { mkdirSync as
|
|
38146
|
-
import { join as
|
|
39303
|
+
import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync14 } from "node:fs";
|
|
39304
|
+
import { join as join44 } from "node:path";
|
|
38147
39305
|
async function evalCommand(opts, config) {
|
|
38148
39306
|
const suiteName = opts.suite ?? "basic";
|
|
38149
39307
|
const suite = SUITES[suiteName];
|
|
@@ -38264,9 +39422,9 @@ async function evalCommand(opts, config) {
|
|
|
38264
39422
|
process.exit(failed > 0 ? 1 : 0);
|
|
38265
39423
|
}
|
|
38266
39424
|
function createTempEvalRepo() {
|
|
38267
|
-
const dir =
|
|
38268
|
-
|
|
38269
|
-
|
|
39425
|
+
const dir = join44(tmpdir7(), `open-agents-eval-${Date.now()}`);
|
|
39426
|
+
mkdirSync16(dir, { recursive: true });
|
|
39427
|
+
writeFileSync14(join44(dir, "package.json"), JSON.stringify({ name: "eval-repo", version: "0.0.0" }, null, 2) + "\n", "utf8");
|
|
38270
39428
|
return dir;
|
|
38271
39429
|
}
|
|
38272
39430
|
var BASIC_SUITE, FULL_SUITE, SUITES;
|
|
@@ -38326,7 +39484,7 @@ init_updater();
|
|
|
38326
39484
|
import { parseArgs as nodeParseArgs2 } from "node:util";
|
|
38327
39485
|
import { createRequire as createRequire3 } from "node:module";
|
|
38328
39486
|
import { fileURLToPath as fileURLToPath10 } from "node:url";
|
|
38329
|
-
import { dirname as
|
|
39487
|
+
import { dirname as dirname14, join as join45 } from "node:path";
|
|
38330
39488
|
|
|
38331
39489
|
// packages/cli/dist/cli.js
|
|
38332
39490
|
import { createInterface } from "node:readline";
|
|
@@ -38433,7 +39591,7 @@ init_output();
|
|
|
38433
39591
|
function getVersion2() {
|
|
38434
39592
|
try {
|
|
38435
39593
|
const require2 = createRequire3(import.meta.url);
|
|
38436
|
-
const pkgPath =
|
|
39594
|
+
const pkgPath = join45(dirname14(fileURLToPath10(import.meta.url)), "..", "package.json");
|
|
38437
39595
|
const pkg = require2(pkgPath);
|
|
38438
39596
|
return pkg.version;
|
|
38439
39597
|
} catch {
|