@openacp/cli 2026.403.3 → 2026.403.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1744 -1308
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +49 -8
- package/dist/index.js +969 -616
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -462,22 +462,22 @@ __export(config_registry_exports, {
|
|
|
462
462
|
});
|
|
463
463
|
import * as fs4 from "fs";
|
|
464
464
|
import * as path4 from "path";
|
|
465
|
-
function getFieldDef(
|
|
466
|
-
return CONFIG_REGISTRY.find((f) => f.path ===
|
|
465
|
+
function getFieldDef(path35) {
|
|
466
|
+
return CONFIG_REGISTRY.find((f) => f.path === path35);
|
|
467
467
|
}
|
|
468
468
|
function getSafeFields() {
|
|
469
469
|
return CONFIG_REGISTRY.filter((f) => f.scope === "safe");
|
|
470
470
|
}
|
|
471
|
-
function isHotReloadable(
|
|
472
|
-
const def = getFieldDef(
|
|
471
|
+
function isHotReloadable(path35) {
|
|
472
|
+
const def = getFieldDef(path35);
|
|
473
473
|
return def?.hotReload ?? false;
|
|
474
474
|
}
|
|
475
475
|
function resolveOptions(def, config) {
|
|
476
476
|
if (!def.options) return void 0;
|
|
477
477
|
return typeof def.options === "function" ? def.options(config) : def.options;
|
|
478
478
|
}
|
|
479
|
-
function getConfigValue(config,
|
|
480
|
-
const parts =
|
|
479
|
+
function getConfigValue(config, path35) {
|
|
480
|
+
const parts = path35.split(".");
|
|
481
481
|
let current = config;
|
|
482
482
|
for (const part of parts) {
|
|
483
483
|
if (current && typeof current === "object" && part in current) {
|
|
@@ -745,7 +745,11 @@ var init_config = __esm({
|
|
|
745
745
|
agents: z.record(z.string(), AgentSchema).optional().default({}),
|
|
746
746
|
defaultAgent: z.string(),
|
|
747
747
|
workspace: z.object({
|
|
748
|
-
baseDir: z.string().default("~/openacp-workspace")
|
|
748
|
+
baseDir: z.string().default("~/openacp-workspace"),
|
|
749
|
+
security: z.object({
|
|
750
|
+
allowedPaths: z.array(z.string()).default([]),
|
|
751
|
+
envWhitelist: z.array(z.string()).default([])
|
|
752
|
+
}).default({})
|
|
749
753
|
}).default({}),
|
|
750
754
|
security: z.object({
|
|
751
755
|
allowedUserIds: z.array(z.string()).default([]),
|
|
@@ -906,11 +910,25 @@ var init_config = __esm({
|
|
|
906
910
|
}
|
|
907
911
|
if (input2.startsWith("/") || input2.startsWith("~")) {
|
|
908
912
|
const resolved2 = expandHome3(input2);
|
|
909
|
-
|
|
910
|
-
|
|
913
|
+
const base = expandHome3(this.config.workspace.baseDir);
|
|
914
|
+
if (resolved2 === base || resolved2.startsWith(base + path5.sep)) {
|
|
915
|
+
fs5.mkdirSync(resolved2, { recursive: true });
|
|
916
|
+
return resolved2;
|
|
917
|
+
}
|
|
918
|
+
throw new Error(
|
|
919
|
+
`Workspace path "${input2}" is outside base directory "${this.config.workspace.baseDir}".`
|
|
920
|
+
);
|
|
921
|
+
}
|
|
922
|
+
const name = input2.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
923
|
+
if (name !== input2) {
|
|
924
|
+
throw new Error(
|
|
925
|
+
`Invalid workspace name: "${input2}". Only alphanumeric characters, hyphens, and underscores are allowed.`
|
|
926
|
+
);
|
|
911
927
|
}
|
|
912
|
-
const
|
|
913
|
-
|
|
928
|
+
const resolved = path5.join(
|
|
929
|
+
expandHome3(this.config.workspace.baseDir),
|
|
930
|
+
name.toLowerCase()
|
|
931
|
+
);
|
|
914
932
|
fs5.mkdirSync(resolved, { recursive: true });
|
|
915
933
|
return resolved;
|
|
916
934
|
}
|
|
@@ -1036,9 +1054,9 @@ var read_text_file_exports = {};
|
|
|
1036
1054
|
__export(read_text_file_exports, {
|
|
1037
1055
|
readTextFileWithRange: () => readTextFileWithRange
|
|
1038
1056
|
});
|
|
1039
|
-
import
|
|
1057
|
+
import fs7 from "fs";
|
|
1040
1058
|
async function readTextFileWithRange(filePath, options) {
|
|
1041
|
-
const content = await
|
|
1059
|
+
const content = await fs7.promises.readFile(filePath, "utf-8");
|
|
1042
1060
|
if (!options?.line && !options?.limit) return content;
|
|
1043
1061
|
const lines = content.split("\n");
|
|
1044
1062
|
const start = Math.max(0, (options.line ?? 1) - 1);
|
|
@@ -1078,8 +1096,8 @@ __export(agent_dependencies_exports, {
|
|
|
1078
1096
|
listAgentsWithIntegration: () => listAgentsWithIntegration
|
|
1079
1097
|
});
|
|
1080
1098
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
1081
|
-
import * as
|
|
1082
|
-
import * as
|
|
1099
|
+
import * as fs12 from "fs";
|
|
1100
|
+
import * as path11 from "path";
|
|
1083
1101
|
function getAgentSetup(registryId) {
|
|
1084
1102
|
return AGENT_SETUP[registryId];
|
|
1085
1103
|
}
|
|
@@ -1103,9 +1121,9 @@ function commandExists(cmd) {
|
|
|
1103
1121
|
}
|
|
1104
1122
|
let dir = process.cwd();
|
|
1105
1123
|
while (true) {
|
|
1106
|
-
const binPath =
|
|
1107
|
-
if (
|
|
1108
|
-
const parent =
|
|
1124
|
+
const binPath = path11.join(dir, "node_modules", ".bin", cmd);
|
|
1125
|
+
if (fs12.existsSync(binPath)) return true;
|
|
1126
|
+
const parent = path11.dirname(dir);
|
|
1109
1127
|
if (parent === dir) break;
|
|
1110
1128
|
dir = parent;
|
|
1111
1129
|
}
|
|
@@ -1360,8 +1378,270 @@ var init_agent_registry = __esm({
|
|
|
1360
1378
|
}
|
|
1361
1379
|
});
|
|
1362
1380
|
|
|
1381
|
+
// src/core/agents/agent-installer.ts
|
|
1382
|
+
import * as fs14 from "fs";
|
|
1383
|
+
import * as path13 from "path";
|
|
1384
|
+
import * as os5 from "os";
|
|
1385
|
+
import crypto from "crypto";
|
|
1386
|
+
function validateTarContents(entries, destDir) {
|
|
1387
|
+
for (const entry of entries) {
|
|
1388
|
+
const segments = entry.split("/");
|
|
1389
|
+
if (segments.includes("..")) {
|
|
1390
|
+
throw new Error(`Archive contains unsafe path traversal: ${entry}`);
|
|
1391
|
+
}
|
|
1392
|
+
if (entry.startsWith("/")) {
|
|
1393
|
+
throw new Error(`Archive contains unsafe absolute path: ${entry}`);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
function validateUninstallPath(binaryPath, agentsDir) {
|
|
1398
|
+
const realPath = path13.resolve(binaryPath);
|
|
1399
|
+
const realAgentsDir = path13.resolve(agentsDir);
|
|
1400
|
+
if (!realPath.startsWith(realAgentsDir + path13.sep) && realPath !== realAgentsDir) {
|
|
1401
|
+
throw new Error(`Refusing to delete path outside agents directory: ${realPath}`);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
function getPlatformKey() {
|
|
1405
|
+
const platform2 = PLATFORM_MAP[process.platform] ?? process.platform;
|
|
1406
|
+
const arch = ARCH_MAP[process.arch] ?? process.arch;
|
|
1407
|
+
return `${platform2}-${arch}`;
|
|
1408
|
+
}
|
|
1409
|
+
function resolveDistribution(agent) {
|
|
1410
|
+
const dist = agent.distribution;
|
|
1411
|
+
if (dist.npx) {
|
|
1412
|
+
return { type: "npx", package: dist.npx.package, args: dist.npx.args ?? [], env: dist.npx.env };
|
|
1413
|
+
}
|
|
1414
|
+
if (dist.uvx) {
|
|
1415
|
+
return { type: "uvx", package: dist.uvx.package, args: dist.uvx.args ?? [], env: dist.uvx.env };
|
|
1416
|
+
}
|
|
1417
|
+
if (dist.binary) {
|
|
1418
|
+
const platformKey = getPlatformKey();
|
|
1419
|
+
const target = dist.binary[platformKey];
|
|
1420
|
+
if (!target) return null;
|
|
1421
|
+
return { type: "binary", archive: target.archive, cmd: target.cmd, args: target.args ?? [], env: target.env };
|
|
1422
|
+
}
|
|
1423
|
+
return null;
|
|
1424
|
+
}
|
|
1425
|
+
function buildInstalledAgent(registryId, name, version, dist, binaryPath) {
|
|
1426
|
+
if (dist.type === "npx") {
|
|
1427
|
+
const npxPackage = stripPackageVersion(dist.package);
|
|
1428
|
+
return {
|
|
1429
|
+
registryId,
|
|
1430
|
+
name,
|
|
1431
|
+
version,
|
|
1432
|
+
distribution: "npx",
|
|
1433
|
+
command: "npx",
|
|
1434
|
+
args: [npxPackage, ...dist.args],
|
|
1435
|
+
env: dist.env ?? {},
|
|
1436
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1437
|
+
binaryPath: null
|
|
1438
|
+
};
|
|
1439
|
+
}
|
|
1440
|
+
if (dist.type === "uvx") {
|
|
1441
|
+
const uvxPackage = stripPythonPackageVersion(dist.package);
|
|
1442
|
+
return {
|
|
1443
|
+
registryId,
|
|
1444
|
+
name,
|
|
1445
|
+
version,
|
|
1446
|
+
distribution: "uvx",
|
|
1447
|
+
command: "uvx",
|
|
1448
|
+
args: [uvxPackage, ...dist.args],
|
|
1449
|
+
env: dist.env ?? {},
|
|
1450
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1451
|
+
binaryPath: null
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1454
|
+
const absCmd = path13.resolve(binaryPath, dist.cmd);
|
|
1455
|
+
return {
|
|
1456
|
+
registryId,
|
|
1457
|
+
name,
|
|
1458
|
+
version,
|
|
1459
|
+
distribution: "binary",
|
|
1460
|
+
command: absCmd,
|
|
1461
|
+
args: dist.args,
|
|
1462
|
+
env: dist.env ?? {},
|
|
1463
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1464
|
+
binaryPath
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1467
|
+
function stripPackageVersion(pkg) {
|
|
1468
|
+
if (pkg.startsWith("@")) {
|
|
1469
|
+
const afterScope = pkg.indexOf("/");
|
|
1470
|
+
if (afterScope === -1) return pkg;
|
|
1471
|
+
const versionAt = pkg.indexOf("@", afterScope + 1);
|
|
1472
|
+
return versionAt === -1 ? pkg : pkg.slice(0, versionAt);
|
|
1473
|
+
}
|
|
1474
|
+
const at = pkg.indexOf("@");
|
|
1475
|
+
return at === -1 ? pkg : pkg.slice(0, at);
|
|
1476
|
+
}
|
|
1477
|
+
function stripPythonPackageVersion(pkg) {
|
|
1478
|
+
const pyMatch = pkg.match(/^([^=@><!]+)/);
|
|
1479
|
+
if (pyMatch && pkg.includes("==")) return pyMatch[1];
|
|
1480
|
+
const at = pkg.indexOf("@");
|
|
1481
|
+
return at === -1 ? pkg : pkg.slice(0, at);
|
|
1482
|
+
}
|
|
1483
|
+
async function installAgent(agent, store, progress, agentsDir) {
|
|
1484
|
+
const agentKey = getAgentAlias(agent.id);
|
|
1485
|
+
await progress?.onStart(agent.id, agent.name);
|
|
1486
|
+
await progress?.onStep("Checking requirements...");
|
|
1487
|
+
const depResult = checkDependencies(agent.id);
|
|
1488
|
+
if (!depResult.available) {
|
|
1489
|
+
const hints = depResult.missing.map((m) => ` ${m.label}: ${m.installHint}`).join("\n");
|
|
1490
|
+
const msg = `${agent.name} needs some tools installed first:
|
|
1491
|
+
${hints}`;
|
|
1492
|
+
await progress?.onError(msg);
|
|
1493
|
+
return { ok: false, agentKey, error: msg };
|
|
1494
|
+
}
|
|
1495
|
+
const dist = resolveDistribution(agent);
|
|
1496
|
+
if (!dist) {
|
|
1497
|
+
const platformKey = getPlatformKey();
|
|
1498
|
+
const msg = `${agent.name} is not available for your system (${platformKey}). Check their website for other install options.`;
|
|
1499
|
+
await progress?.onError(msg);
|
|
1500
|
+
return { ok: false, agentKey, error: msg };
|
|
1501
|
+
}
|
|
1502
|
+
if (dist.type === "uvx" && !checkRuntimeAvailable("uvx")) {
|
|
1503
|
+
const msg = `${agent.name} requires Python's uvx tool.
|
|
1504
|
+
Install it with: pip install uv`;
|
|
1505
|
+
await progress?.onError(msg, "pip install uv");
|
|
1506
|
+
return { ok: false, agentKey, error: msg, hint: "pip install uv" };
|
|
1507
|
+
}
|
|
1508
|
+
let binaryPath;
|
|
1509
|
+
if (dist.type === "binary") {
|
|
1510
|
+
try {
|
|
1511
|
+
binaryPath = await downloadAndExtract(agent.id, dist.archive, progress, agentsDir);
|
|
1512
|
+
} catch (err) {
|
|
1513
|
+
const msg = `Failed to download ${agent.name}. Please try again or install manually.`;
|
|
1514
|
+
await progress?.onError(msg);
|
|
1515
|
+
return { ok: false, agentKey, error: msg };
|
|
1516
|
+
}
|
|
1517
|
+
} else {
|
|
1518
|
+
await progress?.onStep("Setting up... (will download on first use)");
|
|
1519
|
+
}
|
|
1520
|
+
const installed = buildInstalledAgent(agent.id, agent.name, agent.version, dist, binaryPath);
|
|
1521
|
+
store.addAgent(agentKey, installed);
|
|
1522
|
+
const setup = getAgentSetup(agent.id);
|
|
1523
|
+
await progress?.onSuccess(agent.name);
|
|
1524
|
+
return { ok: true, agentKey, setupSteps: setup?.setupSteps };
|
|
1525
|
+
}
|
|
1526
|
+
async function downloadAndExtract(agentId, archiveUrl, progress, agentsDir) {
|
|
1527
|
+
const destDir = path13.join(agentsDir ?? DEFAULT_AGENTS_DIR, agentId);
|
|
1528
|
+
fs14.mkdirSync(destDir, { recursive: true });
|
|
1529
|
+
await progress?.onStep("Downloading...");
|
|
1530
|
+
log11.info({ agentId, url: archiveUrl }, "Downloading agent binary");
|
|
1531
|
+
const response = await fetch(archiveUrl);
|
|
1532
|
+
if (!response.ok) {
|
|
1533
|
+
throw new Error(`Download failed: ${response.status} ${response.statusText}`);
|
|
1534
|
+
}
|
|
1535
|
+
const contentLength = Number(response.headers.get("content-length") || 0);
|
|
1536
|
+
const buffer = await readResponseWithProgress(response, contentLength, progress);
|
|
1537
|
+
await progress?.onStep("Extracting...");
|
|
1538
|
+
if (archiveUrl.endsWith(".zip")) {
|
|
1539
|
+
await extractZip(buffer, destDir);
|
|
1540
|
+
} else {
|
|
1541
|
+
await extractTarGz(buffer, destDir);
|
|
1542
|
+
}
|
|
1543
|
+
await progress?.onStep("Ready!");
|
|
1544
|
+
return destDir;
|
|
1545
|
+
}
|
|
1546
|
+
async function readResponseWithProgress(response, contentLength, progress) {
|
|
1547
|
+
if (!response.body) {
|
|
1548
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
1549
|
+
return Buffer.from(arrayBuffer);
|
|
1550
|
+
}
|
|
1551
|
+
const reader = response.body.getReader();
|
|
1552
|
+
const chunks = [];
|
|
1553
|
+
let received = 0;
|
|
1554
|
+
while (true) {
|
|
1555
|
+
const { done, value } = await reader.read();
|
|
1556
|
+
if (done) break;
|
|
1557
|
+
chunks.push(value);
|
|
1558
|
+
received += value.length;
|
|
1559
|
+
if (received > MAX_DOWNLOAD_SIZE) {
|
|
1560
|
+
throw new Error(`Download exceeds size limit of ${MAX_DOWNLOAD_SIZE} bytes`);
|
|
1561
|
+
}
|
|
1562
|
+
if (contentLength > 0) {
|
|
1563
|
+
await progress?.onDownloadProgress(Math.round(received / contentLength * 100));
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
return Buffer.concat(chunks);
|
|
1567
|
+
}
|
|
1568
|
+
function validateExtractedPaths(destDir) {
|
|
1569
|
+
const realDest = fs14.realpathSync(destDir);
|
|
1570
|
+
const entries = fs14.readdirSync(destDir, { recursive: true, withFileTypes: true });
|
|
1571
|
+
for (const entry of entries) {
|
|
1572
|
+
const dirent = entry;
|
|
1573
|
+
const parentPath = dirent.parentPath ?? dirent.path ?? destDir;
|
|
1574
|
+
const fullPath = path13.join(parentPath, entry.name);
|
|
1575
|
+
let realPath;
|
|
1576
|
+
try {
|
|
1577
|
+
realPath = fs14.realpathSync(fullPath);
|
|
1578
|
+
} catch {
|
|
1579
|
+
const linkTarget = fs14.readlinkSync(fullPath);
|
|
1580
|
+
realPath = path13.resolve(path13.dirname(fullPath), linkTarget);
|
|
1581
|
+
}
|
|
1582
|
+
if (!realPath.startsWith(realDest + path13.sep) && realPath !== realDest) {
|
|
1583
|
+
fs14.rmSync(destDir, { recursive: true, force: true });
|
|
1584
|
+
throw new Error(`Archive contains unsafe path: ${entry.name}`);
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
async function extractTarGz(buffer, destDir) {
|
|
1589
|
+
const { execFileSync: execFileSync7 } = await import("child_process");
|
|
1590
|
+
const tmpFile = path13.join(destDir, "_archive.tar.gz");
|
|
1591
|
+
fs14.writeFileSync(tmpFile, buffer);
|
|
1592
|
+
try {
|
|
1593
|
+
const listing = execFileSync7("tar", ["tf", tmpFile], { stdio: "pipe" }).toString().trim().split("\n").filter(Boolean);
|
|
1594
|
+
validateTarContents(listing, destDir);
|
|
1595
|
+
execFileSync7("tar", ["xzf", tmpFile, "-C", destDir], { stdio: "pipe" });
|
|
1596
|
+
} finally {
|
|
1597
|
+
fs14.unlinkSync(tmpFile);
|
|
1598
|
+
}
|
|
1599
|
+
validateExtractedPaths(destDir);
|
|
1600
|
+
}
|
|
1601
|
+
async function extractZip(buffer, destDir) {
|
|
1602
|
+
const { execFileSync: execFileSync7 } = await import("child_process");
|
|
1603
|
+
const tmpFile = path13.join(destDir, "_archive.zip");
|
|
1604
|
+
fs14.writeFileSync(tmpFile, buffer);
|
|
1605
|
+
try {
|
|
1606
|
+
execFileSync7("unzip", ["-o", tmpFile, "-d", destDir], { stdio: "pipe" });
|
|
1607
|
+
} finally {
|
|
1608
|
+
fs14.unlinkSync(tmpFile);
|
|
1609
|
+
}
|
|
1610
|
+
validateExtractedPaths(destDir);
|
|
1611
|
+
}
|
|
1612
|
+
async function uninstallAgent(agentKey, store, agentsDir) {
|
|
1613
|
+
const agent = store.getAgent(agentKey);
|
|
1614
|
+
if (!agent) return;
|
|
1615
|
+
if (agent.binaryPath && fs14.existsSync(agent.binaryPath)) {
|
|
1616
|
+
validateUninstallPath(agent.binaryPath, agentsDir ?? DEFAULT_AGENTS_DIR);
|
|
1617
|
+
fs14.rmSync(agent.binaryPath, { recursive: true, force: true });
|
|
1618
|
+
log11.info({ agentKey, binaryPath: agent.binaryPath }, "Deleted agent binary");
|
|
1619
|
+
}
|
|
1620
|
+
store.removeAgent(agentKey);
|
|
1621
|
+
}
|
|
1622
|
+
var log11, DEFAULT_AGENTS_DIR, MAX_DOWNLOAD_SIZE, ARCH_MAP, PLATFORM_MAP;
|
|
1623
|
+
var init_agent_installer = __esm({
|
|
1624
|
+
"src/core/agents/agent-installer.ts"() {
|
|
1625
|
+
"use strict";
|
|
1626
|
+
init_log();
|
|
1627
|
+
init_agent_dependencies();
|
|
1628
|
+
log11 = createChildLogger({ module: "agent-installer" });
|
|
1629
|
+
DEFAULT_AGENTS_DIR = path13.join(os5.homedir(), ".openacp", "agents");
|
|
1630
|
+
MAX_DOWNLOAD_SIZE = 500 * 1024 * 1024;
|
|
1631
|
+
ARCH_MAP = {
|
|
1632
|
+
arm64: "aarch64",
|
|
1633
|
+
x64: "x86_64"
|
|
1634
|
+
};
|
|
1635
|
+
PLATFORM_MAP = {
|
|
1636
|
+
darwin: "darwin",
|
|
1637
|
+
linux: "linux",
|
|
1638
|
+
win32: "windows"
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1641
|
+
});
|
|
1642
|
+
|
|
1363
1643
|
// src/core/doctor/checks/config.ts
|
|
1364
|
-
import * as
|
|
1644
|
+
import * as fs17 from "fs";
|
|
1365
1645
|
var configCheck;
|
|
1366
1646
|
var init_config2 = __esm({
|
|
1367
1647
|
"src/core/doctor/checks/config.ts"() {
|
|
@@ -1373,14 +1653,14 @@ var init_config2 = __esm({
|
|
|
1373
1653
|
order: 1,
|
|
1374
1654
|
async run(ctx) {
|
|
1375
1655
|
const results = [];
|
|
1376
|
-
if (!
|
|
1656
|
+
if (!fs17.existsSync(ctx.configPath)) {
|
|
1377
1657
|
results.push({ status: "fail", message: "Config file not found" });
|
|
1378
1658
|
return results;
|
|
1379
1659
|
}
|
|
1380
1660
|
results.push({ status: "pass", message: "Config file exists" });
|
|
1381
1661
|
let raw;
|
|
1382
1662
|
try {
|
|
1383
|
-
raw = JSON.parse(
|
|
1663
|
+
raw = JSON.parse(fs17.readFileSync(ctx.configPath, "utf-8"));
|
|
1384
1664
|
} catch (err) {
|
|
1385
1665
|
results.push({
|
|
1386
1666
|
status: "fail",
|
|
@@ -1399,7 +1679,7 @@ var init_config2 = __esm({
|
|
|
1399
1679
|
fixRisk: "safe",
|
|
1400
1680
|
fix: async () => {
|
|
1401
1681
|
applyMigrations(raw);
|
|
1402
|
-
|
|
1682
|
+
fs17.writeFileSync(ctx.configPath, JSON.stringify(raw, null, 2));
|
|
1403
1683
|
return { success: true, message: "applied migrations" };
|
|
1404
1684
|
}
|
|
1405
1685
|
});
|
|
@@ -1423,8 +1703,8 @@ var init_config2 = __esm({
|
|
|
1423
1703
|
|
|
1424
1704
|
// src/core/doctor/checks/agents.ts
|
|
1425
1705
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
1426
|
-
import * as
|
|
1427
|
-
import * as
|
|
1706
|
+
import * as fs18 from "fs";
|
|
1707
|
+
import * as path18 from "path";
|
|
1428
1708
|
function commandExists2(cmd) {
|
|
1429
1709
|
try {
|
|
1430
1710
|
execFileSync3("which", [cmd], { stdio: "pipe" });
|
|
@@ -1433,9 +1713,9 @@ function commandExists2(cmd) {
|
|
|
1433
1713
|
}
|
|
1434
1714
|
let dir = process.cwd();
|
|
1435
1715
|
while (true) {
|
|
1436
|
-
const binPath =
|
|
1437
|
-
if (
|
|
1438
|
-
const parent =
|
|
1716
|
+
const binPath = path18.join(dir, "node_modules", ".bin", cmd);
|
|
1717
|
+
if (fs18.existsSync(binPath)) return true;
|
|
1718
|
+
const parent = path18.dirname(dir);
|
|
1439
1719
|
if (parent === dir) break;
|
|
1440
1720
|
dir = parent;
|
|
1441
1721
|
}
|
|
@@ -1487,8 +1767,8 @@ var settings_manager_exports = {};
|
|
|
1487
1767
|
__export(settings_manager_exports, {
|
|
1488
1768
|
SettingsManager: () => SettingsManager
|
|
1489
1769
|
});
|
|
1490
|
-
import
|
|
1491
|
-
import
|
|
1770
|
+
import fs19 from "fs";
|
|
1771
|
+
import path19 from "path";
|
|
1492
1772
|
var SettingsManager, SettingsAPIImpl;
|
|
1493
1773
|
var init_settings_manager = __esm({
|
|
1494
1774
|
"src/core/plugin/settings-manager.ts"() {
|
|
@@ -1507,7 +1787,7 @@ var init_settings_manager = __esm({
|
|
|
1507
1787
|
async loadSettings(pluginName) {
|
|
1508
1788
|
const settingsPath = this.getSettingsPath(pluginName);
|
|
1509
1789
|
try {
|
|
1510
|
-
const content =
|
|
1790
|
+
const content = fs19.readFileSync(settingsPath, "utf-8");
|
|
1511
1791
|
return JSON.parse(content);
|
|
1512
1792
|
} catch {
|
|
1513
1793
|
return {};
|
|
@@ -1525,7 +1805,7 @@ var init_settings_manager = __esm({
|
|
|
1525
1805
|
};
|
|
1526
1806
|
}
|
|
1527
1807
|
getSettingsPath(pluginName) {
|
|
1528
|
-
return
|
|
1808
|
+
return path19.join(this.basePath, pluginName, "settings.json");
|
|
1529
1809
|
}
|
|
1530
1810
|
async getPluginSettings(pluginName) {
|
|
1531
1811
|
return this.loadSettings(pluginName);
|
|
@@ -1544,7 +1824,7 @@ var init_settings_manager = __esm({
|
|
|
1544
1824
|
readFile() {
|
|
1545
1825
|
if (this.cache !== null) return this.cache;
|
|
1546
1826
|
try {
|
|
1547
|
-
const content =
|
|
1827
|
+
const content = fs19.readFileSync(this.settingsPath, "utf-8");
|
|
1548
1828
|
this.cache = JSON.parse(content);
|
|
1549
1829
|
return this.cache;
|
|
1550
1830
|
} catch {
|
|
@@ -1553,9 +1833,9 @@ var init_settings_manager = __esm({
|
|
|
1553
1833
|
}
|
|
1554
1834
|
}
|
|
1555
1835
|
writeFile(data) {
|
|
1556
|
-
const dir =
|
|
1557
|
-
|
|
1558
|
-
|
|
1836
|
+
const dir = path19.dirname(this.settingsPath);
|
|
1837
|
+
fs19.mkdirSync(dir, { recursive: true });
|
|
1838
|
+
fs19.writeFileSync(this.settingsPath, JSON.stringify(data, null, 2));
|
|
1559
1839
|
this.cache = data;
|
|
1560
1840
|
}
|
|
1561
1841
|
async get(key) {
|
|
@@ -1590,7 +1870,7 @@ var init_settings_manager = __esm({
|
|
|
1590
1870
|
});
|
|
1591
1871
|
|
|
1592
1872
|
// src/core/doctor/checks/telegram.ts
|
|
1593
|
-
import * as
|
|
1873
|
+
import * as path20 from "path";
|
|
1594
1874
|
var BOT_TOKEN_REGEX, telegramCheck;
|
|
1595
1875
|
var init_telegram = __esm({
|
|
1596
1876
|
"src/core/doctor/checks/telegram.ts"() {
|
|
@@ -1606,7 +1886,7 @@ var init_telegram = __esm({
|
|
|
1606
1886
|
return results;
|
|
1607
1887
|
}
|
|
1608
1888
|
const { SettingsManager: SettingsManager2 } = await Promise.resolve().then(() => (init_settings_manager(), settings_manager_exports));
|
|
1609
|
-
const sm = new SettingsManager2(
|
|
1889
|
+
const sm = new SettingsManager2(path20.join(ctx.pluginsDir, "data"));
|
|
1610
1890
|
const ps = await sm.loadSettings("@openacp/telegram");
|
|
1611
1891
|
const legacyCh = ctx.config.channels.telegram;
|
|
1612
1892
|
const botToken = ps.botToken ?? legacyCh?.botToken;
|
|
@@ -1690,7 +1970,7 @@ var init_telegram = __esm({
|
|
|
1690
1970
|
});
|
|
1691
1971
|
|
|
1692
1972
|
// src/core/doctor/checks/storage.ts
|
|
1693
|
-
import * as
|
|
1973
|
+
import * as fs20 from "fs";
|
|
1694
1974
|
var storageCheck;
|
|
1695
1975
|
var init_storage = __esm({
|
|
1696
1976
|
"src/core/doctor/checks/storage.ts"() {
|
|
@@ -1700,28 +1980,28 @@ var init_storage = __esm({
|
|
|
1700
1980
|
order: 4,
|
|
1701
1981
|
async run(ctx) {
|
|
1702
1982
|
const results = [];
|
|
1703
|
-
if (!
|
|
1983
|
+
if (!fs20.existsSync(ctx.dataDir)) {
|
|
1704
1984
|
results.push({
|
|
1705
1985
|
status: "fail",
|
|
1706
1986
|
message: "Data directory ~/.openacp does not exist",
|
|
1707
1987
|
fixable: true,
|
|
1708
1988
|
fixRisk: "safe",
|
|
1709
1989
|
fix: async () => {
|
|
1710
|
-
|
|
1990
|
+
fs20.mkdirSync(ctx.dataDir, { recursive: true });
|
|
1711
1991
|
return { success: true, message: "created directory" };
|
|
1712
1992
|
}
|
|
1713
1993
|
});
|
|
1714
1994
|
} else {
|
|
1715
1995
|
try {
|
|
1716
|
-
|
|
1996
|
+
fs20.accessSync(ctx.dataDir, fs20.constants.W_OK);
|
|
1717
1997
|
results.push({ status: "pass", message: "Data directory exists and writable" });
|
|
1718
1998
|
} catch {
|
|
1719
1999
|
results.push({ status: "fail", message: "Data directory not writable" });
|
|
1720
2000
|
}
|
|
1721
2001
|
}
|
|
1722
|
-
if (
|
|
2002
|
+
if (fs20.existsSync(ctx.sessionsPath)) {
|
|
1723
2003
|
try {
|
|
1724
|
-
const content =
|
|
2004
|
+
const content = fs20.readFileSync(ctx.sessionsPath, "utf-8");
|
|
1725
2005
|
const data = JSON.parse(content);
|
|
1726
2006
|
if (typeof data === "object" && data !== null && "sessions" in data) {
|
|
1727
2007
|
results.push({ status: "pass", message: "Sessions file valid" });
|
|
@@ -1732,7 +2012,7 @@ var init_storage = __esm({
|
|
|
1732
2012
|
fixable: true,
|
|
1733
2013
|
fixRisk: "risky",
|
|
1734
2014
|
fix: async () => {
|
|
1735
|
-
|
|
2015
|
+
fs20.writeFileSync(ctx.sessionsPath, JSON.stringify({ version: 1, sessions: {} }, null, 2));
|
|
1736
2016
|
return { success: true, message: "reset sessions file" };
|
|
1737
2017
|
}
|
|
1738
2018
|
});
|
|
@@ -1744,7 +2024,7 @@ var init_storage = __esm({
|
|
|
1744
2024
|
fixable: true,
|
|
1745
2025
|
fixRisk: "risky",
|
|
1746
2026
|
fix: async () => {
|
|
1747
|
-
|
|
2027
|
+
fs20.writeFileSync(ctx.sessionsPath, JSON.stringify({ version: 1, sessions: {} }, null, 2));
|
|
1748
2028
|
return { success: true, message: "reset sessions file" };
|
|
1749
2029
|
}
|
|
1750
2030
|
});
|
|
@@ -1752,20 +2032,20 @@ var init_storage = __esm({
|
|
|
1752
2032
|
} else {
|
|
1753
2033
|
results.push({ status: "pass", message: "Sessions file not present yet (created on first session)" });
|
|
1754
2034
|
}
|
|
1755
|
-
if (!
|
|
2035
|
+
if (!fs20.existsSync(ctx.logsDir)) {
|
|
1756
2036
|
results.push({
|
|
1757
2037
|
status: "warn",
|
|
1758
2038
|
message: "Log directory does not exist",
|
|
1759
2039
|
fixable: true,
|
|
1760
2040
|
fixRisk: "safe",
|
|
1761
2041
|
fix: async () => {
|
|
1762
|
-
|
|
2042
|
+
fs20.mkdirSync(ctx.logsDir, { recursive: true });
|
|
1763
2043
|
return { success: true, message: "created log directory" };
|
|
1764
2044
|
}
|
|
1765
2045
|
});
|
|
1766
2046
|
} else {
|
|
1767
2047
|
try {
|
|
1768
|
-
|
|
2048
|
+
fs20.accessSync(ctx.logsDir, fs20.constants.W_OK);
|
|
1769
2049
|
results.push({ status: "pass", message: "Log directory exists and writable" });
|
|
1770
2050
|
} catch {
|
|
1771
2051
|
results.push({ status: "fail", message: "Log directory not writable" });
|
|
@@ -1778,7 +2058,7 @@ var init_storage = __esm({
|
|
|
1778
2058
|
});
|
|
1779
2059
|
|
|
1780
2060
|
// src/core/doctor/checks/workspace.ts
|
|
1781
|
-
import * as
|
|
2061
|
+
import * as fs21 from "fs";
|
|
1782
2062
|
var workspaceCheck;
|
|
1783
2063
|
var init_workspace = __esm({
|
|
1784
2064
|
"src/core/doctor/checks/workspace.ts"() {
|
|
@@ -1794,20 +2074,20 @@ var init_workspace = __esm({
|
|
|
1794
2074
|
return results;
|
|
1795
2075
|
}
|
|
1796
2076
|
const baseDir = expandHome3(ctx.config.workspace.baseDir);
|
|
1797
|
-
if (!
|
|
2077
|
+
if (!fs21.existsSync(baseDir)) {
|
|
1798
2078
|
results.push({
|
|
1799
2079
|
status: "warn",
|
|
1800
2080
|
message: `Workspace directory does not exist: ${baseDir}`,
|
|
1801
2081
|
fixable: true,
|
|
1802
2082
|
fixRisk: "safe",
|
|
1803
2083
|
fix: async () => {
|
|
1804
|
-
|
|
2084
|
+
fs21.mkdirSync(baseDir, { recursive: true });
|
|
1805
2085
|
return { success: true, message: "created directory" };
|
|
1806
2086
|
}
|
|
1807
2087
|
});
|
|
1808
2088
|
} else {
|
|
1809
2089
|
try {
|
|
1810
|
-
|
|
2090
|
+
fs21.accessSync(baseDir, fs21.constants.W_OK);
|
|
1811
2091
|
results.push({ status: "pass", message: `Workspace directory exists: ${baseDir}` });
|
|
1812
2092
|
} catch {
|
|
1813
2093
|
results.push({ status: "fail", message: `Workspace directory not writable: ${baseDir}` });
|
|
@@ -1820,8 +2100,8 @@ var init_workspace = __esm({
|
|
|
1820
2100
|
});
|
|
1821
2101
|
|
|
1822
2102
|
// src/core/doctor/checks/plugins.ts
|
|
1823
|
-
import * as
|
|
1824
|
-
import * as
|
|
2103
|
+
import * as fs22 from "fs";
|
|
2104
|
+
import * as path21 from "path";
|
|
1825
2105
|
var pluginsCheck;
|
|
1826
2106
|
var init_plugins = __esm({
|
|
1827
2107
|
"src/core/doctor/checks/plugins.ts"() {
|
|
@@ -1831,16 +2111,16 @@ var init_plugins = __esm({
|
|
|
1831
2111
|
order: 6,
|
|
1832
2112
|
async run(ctx) {
|
|
1833
2113
|
const results = [];
|
|
1834
|
-
if (!
|
|
2114
|
+
if (!fs22.existsSync(ctx.pluginsDir)) {
|
|
1835
2115
|
results.push({
|
|
1836
2116
|
status: "warn",
|
|
1837
2117
|
message: "Plugins directory does not exist",
|
|
1838
2118
|
fixable: true,
|
|
1839
2119
|
fixRisk: "safe",
|
|
1840
2120
|
fix: async () => {
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
2121
|
+
fs22.mkdirSync(ctx.pluginsDir, { recursive: true });
|
|
2122
|
+
fs22.writeFileSync(
|
|
2123
|
+
path21.join(ctx.pluginsDir, "package.json"),
|
|
1844
2124
|
JSON.stringify({ name: "openacp-plugins", private: true, dependencies: {} }, null, 2)
|
|
1845
2125
|
);
|
|
1846
2126
|
return { success: true, message: "initialized plugins directory" };
|
|
@@ -1849,15 +2129,15 @@ var init_plugins = __esm({
|
|
|
1849
2129
|
return results;
|
|
1850
2130
|
}
|
|
1851
2131
|
results.push({ status: "pass", message: "Plugins directory exists" });
|
|
1852
|
-
const pkgPath =
|
|
1853
|
-
if (!
|
|
2132
|
+
const pkgPath = path21.join(ctx.pluginsDir, "package.json");
|
|
2133
|
+
if (!fs22.existsSync(pkgPath)) {
|
|
1854
2134
|
results.push({
|
|
1855
2135
|
status: "warn",
|
|
1856
2136
|
message: "Plugins package.json missing",
|
|
1857
2137
|
fixable: true,
|
|
1858
2138
|
fixRisk: "safe",
|
|
1859
2139
|
fix: async () => {
|
|
1860
|
-
|
|
2140
|
+
fs22.writeFileSync(
|
|
1861
2141
|
pkgPath,
|
|
1862
2142
|
JSON.stringify({ name: "openacp-plugins", private: true, dependencies: {} }, null, 2)
|
|
1863
2143
|
);
|
|
@@ -1867,7 +2147,7 @@ var init_plugins = __esm({
|
|
|
1867
2147
|
return results;
|
|
1868
2148
|
}
|
|
1869
2149
|
try {
|
|
1870
|
-
const pkg = JSON.parse(
|
|
2150
|
+
const pkg = JSON.parse(fs22.readFileSync(pkgPath, "utf-8"));
|
|
1871
2151
|
const deps = pkg.dependencies || {};
|
|
1872
2152
|
const count = Object.keys(deps).length;
|
|
1873
2153
|
results.push({ status: "pass", message: `Plugins package.json valid (${count} plugins)` });
|
|
@@ -1878,7 +2158,7 @@ var init_plugins = __esm({
|
|
|
1878
2158
|
fixable: true,
|
|
1879
2159
|
fixRisk: "risky",
|
|
1880
2160
|
fix: async () => {
|
|
1881
|
-
|
|
2161
|
+
fs22.writeFileSync(
|
|
1882
2162
|
pkgPath,
|
|
1883
2163
|
JSON.stringify({ name: "openacp-plugins", private: true, dependencies: {} }, null, 2)
|
|
1884
2164
|
);
|
|
@@ -1893,7 +2173,7 @@ var init_plugins = __esm({
|
|
|
1893
2173
|
});
|
|
1894
2174
|
|
|
1895
2175
|
// src/core/doctor/checks/daemon.ts
|
|
1896
|
-
import * as
|
|
2176
|
+
import * as fs23 from "fs";
|
|
1897
2177
|
import * as net from "net";
|
|
1898
2178
|
function isProcessAlive(pid) {
|
|
1899
2179
|
try {
|
|
@@ -1923,8 +2203,8 @@ var init_daemon = __esm({
|
|
|
1923
2203
|
order: 7,
|
|
1924
2204
|
async run(ctx) {
|
|
1925
2205
|
const results = [];
|
|
1926
|
-
if (
|
|
1927
|
-
const content =
|
|
2206
|
+
if (fs23.existsSync(ctx.pidPath)) {
|
|
2207
|
+
const content = fs23.readFileSync(ctx.pidPath, "utf-8").trim();
|
|
1928
2208
|
const pid = parseInt(content, 10);
|
|
1929
2209
|
if (isNaN(pid)) {
|
|
1930
2210
|
results.push({
|
|
@@ -1933,7 +2213,7 @@ var init_daemon = __esm({
|
|
|
1933
2213
|
fixable: true,
|
|
1934
2214
|
fixRisk: "safe",
|
|
1935
2215
|
fix: async () => {
|
|
1936
|
-
|
|
2216
|
+
fs23.unlinkSync(ctx.pidPath);
|
|
1937
2217
|
return { success: true, message: "removed invalid PID file" };
|
|
1938
2218
|
}
|
|
1939
2219
|
});
|
|
@@ -1944,7 +2224,7 @@ var init_daemon = __esm({
|
|
|
1944
2224
|
fixable: true,
|
|
1945
2225
|
fixRisk: "safe",
|
|
1946
2226
|
fix: async () => {
|
|
1947
|
-
|
|
2227
|
+
fs23.unlinkSync(ctx.pidPath);
|
|
1948
2228
|
return { success: true, message: "removed stale PID file" };
|
|
1949
2229
|
}
|
|
1950
2230
|
});
|
|
@@ -1952,8 +2232,8 @@ var init_daemon = __esm({
|
|
|
1952
2232
|
results.push({ status: "pass", message: `Daemon running (PID ${pid})` });
|
|
1953
2233
|
}
|
|
1954
2234
|
}
|
|
1955
|
-
if (
|
|
1956
|
-
const content =
|
|
2235
|
+
if (fs23.existsSync(ctx.portFilePath)) {
|
|
2236
|
+
const content = fs23.readFileSync(ctx.portFilePath, "utf-8").trim();
|
|
1957
2237
|
const port = parseInt(content, 10);
|
|
1958
2238
|
if (isNaN(port)) {
|
|
1959
2239
|
results.push({
|
|
@@ -1962,7 +2242,7 @@ var init_daemon = __esm({
|
|
|
1962
2242
|
fixable: true,
|
|
1963
2243
|
fixRisk: "safe",
|
|
1964
2244
|
fix: async () => {
|
|
1965
|
-
|
|
2245
|
+
fs23.unlinkSync(ctx.portFilePath);
|
|
1966
2246
|
return { success: true, message: "removed invalid port file" };
|
|
1967
2247
|
}
|
|
1968
2248
|
});
|
|
@@ -1974,8 +2254,8 @@ var init_daemon = __esm({
|
|
|
1974
2254
|
const apiPort = ctx.config.api.port;
|
|
1975
2255
|
const inUse = await checkPortInUse(apiPort);
|
|
1976
2256
|
if (inUse) {
|
|
1977
|
-
if (
|
|
1978
|
-
const pid = parseInt(
|
|
2257
|
+
if (fs23.existsSync(ctx.pidPath)) {
|
|
2258
|
+
const pid = parseInt(fs23.readFileSync(ctx.pidPath, "utf-8").trim(), 10);
|
|
1979
2259
|
if (!isNaN(pid) && isProcessAlive(pid)) {
|
|
1980
2260
|
results.push({ status: "pass", message: `API port ${apiPort} in use by OpenACP daemon` });
|
|
1981
2261
|
} else {
|
|
@@ -1995,17 +2275,17 @@ var init_daemon = __esm({
|
|
|
1995
2275
|
});
|
|
1996
2276
|
|
|
1997
2277
|
// src/core/utils/install-binary.ts
|
|
1998
|
-
import
|
|
1999
|
-
import
|
|
2278
|
+
import fs24 from "fs";
|
|
2279
|
+
import path22 from "path";
|
|
2000
2280
|
import https from "https";
|
|
2001
2281
|
import os9 from "os";
|
|
2002
2282
|
import { execSync } from "child_process";
|
|
2003
2283
|
function downloadFile(url, dest) {
|
|
2004
2284
|
return new Promise((resolve6, reject) => {
|
|
2005
|
-
const file =
|
|
2285
|
+
const file = fs24.createWriteStream(dest);
|
|
2006
2286
|
const cleanup = () => {
|
|
2007
2287
|
try {
|
|
2008
|
-
if (
|
|
2288
|
+
if (fs24.existsSync(dest)) fs24.unlinkSync(dest);
|
|
2009
2289
|
} catch {
|
|
2010
2290
|
}
|
|
2011
2291
|
};
|
|
@@ -2024,6 +2304,18 @@ function downloadFile(url, dest) {
|
|
|
2024
2304
|
});
|
|
2025
2305
|
return;
|
|
2026
2306
|
}
|
|
2307
|
+
let totalBytes = 0;
|
|
2308
|
+
const request = response;
|
|
2309
|
+
response.on("data", (chunk) => {
|
|
2310
|
+
totalBytes += chunk.length;
|
|
2311
|
+
if (totalBytes > MAX_DOWNLOAD_SIZE) {
|
|
2312
|
+
request.destroy();
|
|
2313
|
+
file.close(() => {
|
|
2314
|
+
cleanup();
|
|
2315
|
+
reject(new Error(`Download exceeds size limit of ${MAX_DOWNLOAD_SIZE} bytes`));
|
|
2316
|
+
});
|
|
2317
|
+
}
|
|
2318
|
+
});
|
|
2027
2319
|
response.pipe(file);
|
|
2028
2320
|
file.on("finish", () => file.close(() => resolve6(dest)));
|
|
2029
2321
|
file.on("error", (err) => {
|
|
@@ -2052,34 +2344,36 @@ function getDownloadUrl(spec) {
|
|
|
2052
2344
|
async function ensureBinary(spec, binDir) {
|
|
2053
2345
|
const resolvedBinDir = binDir ?? DEFAULT_BIN_DIR;
|
|
2054
2346
|
const binName = IS_WINDOWS ? `${spec.name}.exe` : spec.name;
|
|
2055
|
-
const binPath =
|
|
2347
|
+
const binPath = path22.join(resolvedBinDir, binName);
|
|
2056
2348
|
if (commandExists(spec.name)) {
|
|
2057
2349
|
log17.debug({ name: spec.name }, "Found in PATH");
|
|
2058
2350
|
return spec.name;
|
|
2059
2351
|
}
|
|
2060
|
-
if (
|
|
2061
|
-
if (!IS_WINDOWS)
|
|
2352
|
+
if (fs24.existsSync(binPath)) {
|
|
2353
|
+
if (!IS_WINDOWS) fs24.chmodSync(binPath, "755");
|
|
2062
2354
|
log17.debug({ name: spec.name, path: binPath }, "Found in ~/.openacp/bin");
|
|
2063
2355
|
return binPath;
|
|
2064
2356
|
}
|
|
2065
2357
|
log17.info({ name: spec.name }, "Not found, downloading from GitHub...");
|
|
2066
|
-
|
|
2358
|
+
fs24.mkdirSync(resolvedBinDir, { recursive: true });
|
|
2067
2359
|
const url = getDownloadUrl(spec);
|
|
2068
2360
|
const isArchive = spec.isArchive?.(url) ?? false;
|
|
2069
|
-
const downloadDest = isArchive ?
|
|
2361
|
+
const downloadDest = isArchive ? path22.join(resolvedBinDir, `${spec.name}.tgz`) : binPath;
|
|
2070
2362
|
await downloadFile(url, downloadDest);
|
|
2071
2363
|
if (isArchive) {
|
|
2364
|
+
const listing = execSync(`tar -tf "${downloadDest}"`, { stdio: "pipe" }).toString().trim().split("\n").filter(Boolean);
|
|
2365
|
+
validateTarContents(listing, resolvedBinDir);
|
|
2072
2366
|
execSync(`tar -xzf "${downloadDest}" -C "${resolvedBinDir}"`, { stdio: "pipe" });
|
|
2073
2367
|
try {
|
|
2074
|
-
|
|
2368
|
+
fs24.unlinkSync(downloadDest);
|
|
2075
2369
|
} catch {
|
|
2076
2370
|
}
|
|
2077
2371
|
}
|
|
2078
|
-
if (!
|
|
2372
|
+
if (!fs24.existsSync(binPath)) {
|
|
2079
2373
|
throw new Error(`${spec.name}: binary not found at ${binPath} after download/extraction. The archive structure may have changed.`);
|
|
2080
2374
|
}
|
|
2081
2375
|
if (!IS_WINDOWS) {
|
|
2082
|
-
|
|
2376
|
+
fs24.chmodSync(binPath, "755");
|
|
2083
2377
|
}
|
|
2084
2378
|
log17.info({ name: spec.name, path: binPath }, "Installed successfully");
|
|
2085
2379
|
return binPath;
|
|
@@ -2090,8 +2384,9 @@ var init_install_binary = __esm({
|
|
|
2090
2384
|
"use strict";
|
|
2091
2385
|
init_log();
|
|
2092
2386
|
init_agent_dependencies();
|
|
2387
|
+
init_agent_installer();
|
|
2093
2388
|
log17 = createChildLogger({ module: "binary-installer" });
|
|
2094
|
-
DEFAULT_BIN_DIR =
|
|
2389
|
+
DEFAULT_BIN_DIR = path22.join(os9.homedir(), ".openacp", "bin");
|
|
2095
2390
|
IS_WINDOWS = os9.platform() === "win32";
|
|
2096
2391
|
}
|
|
2097
2392
|
});
|
|
@@ -2132,8 +2427,8 @@ var init_install_cloudflared = __esm({
|
|
|
2132
2427
|
});
|
|
2133
2428
|
|
|
2134
2429
|
// src/core/doctor/checks/tunnel.ts
|
|
2135
|
-
import * as
|
|
2136
|
-
import * as
|
|
2430
|
+
import * as fs25 from "fs";
|
|
2431
|
+
import * as path23 from "path";
|
|
2137
2432
|
import * as os10 from "os";
|
|
2138
2433
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
2139
2434
|
var tunnelCheck;
|
|
@@ -2157,9 +2452,9 @@ var init_tunnel = __esm({
|
|
|
2157
2452
|
results.push({ status: "pass", message: `Tunnel provider: ${provider}` });
|
|
2158
2453
|
if (provider === "cloudflare") {
|
|
2159
2454
|
const binName = os10.platform() === "win32" ? "cloudflared.exe" : "cloudflared";
|
|
2160
|
-
const binPath =
|
|
2455
|
+
const binPath = path23.join(ctx.dataDir, "bin", binName);
|
|
2161
2456
|
let found = false;
|
|
2162
|
-
if (
|
|
2457
|
+
if (fs25.existsSync(binPath)) {
|
|
2163
2458
|
found = true;
|
|
2164
2459
|
} else {
|
|
2165
2460
|
try {
|
|
@@ -2201,8 +2496,8 @@ var init_tunnel = __esm({
|
|
|
2201
2496
|
});
|
|
2202
2497
|
|
|
2203
2498
|
// src/core/doctor/index.ts
|
|
2204
|
-
import * as
|
|
2205
|
-
import * as
|
|
2499
|
+
import * as fs26 from "fs";
|
|
2500
|
+
import * as path24 from "path";
|
|
2206
2501
|
var ALL_CHECKS, CHECK_TIMEOUT_MS, DoctorEngine;
|
|
2207
2502
|
var init_doctor = __esm({
|
|
2208
2503
|
"src/core/doctor/index.ts"() {
|
|
@@ -2284,27 +2579,27 @@ var init_doctor = __esm({
|
|
|
2284
2579
|
}
|
|
2285
2580
|
async buildContext() {
|
|
2286
2581
|
const dataDir = this.dataDir;
|
|
2287
|
-
const configPath = process.env.OPENACP_CONFIG_PATH ||
|
|
2582
|
+
const configPath = process.env.OPENACP_CONFIG_PATH || path24.join(dataDir, "config.json");
|
|
2288
2583
|
let config = null;
|
|
2289
2584
|
let rawConfig = null;
|
|
2290
2585
|
try {
|
|
2291
|
-
const content =
|
|
2586
|
+
const content = fs26.readFileSync(configPath, "utf-8");
|
|
2292
2587
|
rawConfig = JSON.parse(content);
|
|
2293
2588
|
const cm = new ConfigManager(configPath);
|
|
2294
2589
|
await cm.load();
|
|
2295
2590
|
config = cm.get();
|
|
2296
2591
|
} catch {
|
|
2297
2592
|
}
|
|
2298
|
-
const logsDir = config ? expandHome3(config.logging.logDir) :
|
|
2593
|
+
const logsDir = config ? expandHome3(config.logging.logDir) : path24.join(dataDir, "logs");
|
|
2299
2594
|
return {
|
|
2300
2595
|
config,
|
|
2301
2596
|
rawConfig,
|
|
2302
2597
|
configPath,
|
|
2303
2598
|
dataDir,
|
|
2304
|
-
sessionsPath:
|
|
2305
|
-
pidPath:
|
|
2306
|
-
portFilePath:
|
|
2307
|
-
pluginsDir:
|
|
2599
|
+
sessionsPath: path24.join(dataDir, "sessions.json"),
|
|
2600
|
+
pidPath: path24.join(dataDir, "openacp.pid"),
|
|
2601
|
+
portFilePath: path24.join(dataDir, "api.port"),
|
|
2602
|
+
pluginsDir: path24.join(dataDir, "plugins"),
|
|
2308
2603
|
logsDir
|
|
2309
2604
|
};
|
|
2310
2605
|
}
|
|
@@ -2409,16 +2704,16 @@ __export(plugin_installer_exports, {
|
|
|
2409
2704
|
});
|
|
2410
2705
|
import { exec } from "child_process";
|
|
2411
2706
|
import { promisify } from "util";
|
|
2412
|
-
import * as
|
|
2707
|
+
import * as fs28 from "fs/promises";
|
|
2413
2708
|
import * as os12 from "os";
|
|
2414
|
-
import * as
|
|
2709
|
+
import * as path26 from "path";
|
|
2415
2710
|
import { pathToFileURL } from "url";
|
|
2416
2711
|
async function importFromDir(packageName, dir) {
|
|
2417
|
-
const pkgDir =
|
|
2418
|
-
const pkgJsonPath =
|
|
2712
|
+
const pkgDir = path26.join(dir, "node_modules", ...packageName.split("/"));
|
|
2713
|
+
const pkgJsonPath = path26.join(pkgDir, "package.json");
|
|
2419
2714
|
let pkgJson;
|
|
2420
2715
|
try {
|
|
2421
|
-
pkgJson = JSON.parse(await
|
|
2716
|
+
pkgJson = JSON.parse(await fs28.readFile(pkgJsonPath, "utf-8"));
|
|
2422
2717
|
} catch (err) {
|
|
2423
2718
|
throw new Error(`Cannot read package.json for "${packageName}" at ${pkgJsonPath}: ${err.message}`);
|
|
2424
2719
|
}
|
|
@@ -2431,9 +2726,9 @@ async function importFromDir(packageName, dir) {
|
|
|
2431
2726
|
} else {
|
|
2432
2727
|
entry = pkgJson.main ?? "index.js";
|
|
2433
2728
|
}
|
|
2434
|
-
const entryPath =
|
|
2729
|
+
const entryPath = path26.join(pkgDir, entry);
|
|
2435
2730
|
try {
|
|
2436
|
-
await
|
|
2731
|
+
await fs28.access(entryPath);
|
|
2437
2732
|
} catch {
|
|
2438
2733
|
throw new Error(`Entry point "${entry}" not found for "${packageName}" at ${entryPath}`);
|
|
2439
2734
|
}
|
|
@@ -2443,12 +2738,12 @@ async function installNpmPlugin(packageName, pluginsDir) {
|
|
|
2443
2738
|
if (!VALID_NPM_NAME.test(packageName)) {
|
|
2444
2739
|
throw new Error(`Invalid package name: "${packageName}". Must be a valid npm package name.`);
|
|
2445
2740
|
}
|
|
2446
|
-
const dir = pluginsDir ??
|
|
2741
|
+
const dir = pluginsDir ?? path26.join(os12.homedir(), ".openacp", "plugins");
|
|
2447
2742
|
try {
|
|
2448
2743
|
return await importFromDir(packageName, dir);
|
|
2449
2744
|
} catch {
|
|
2450
2745
|
}
|
|
2451
|
-
await execAsync(`npm install ${packageName} --prefix "${dir}" --save`, {
|
|
2746
|
+
await execAsync(`npm install ${packageName} --prefix "${dir}" --save --ignore-scripts`, {
|
|
2452
2747
|
timeout: 6e4
|
|
2453
2748
|
});
|
|
2454
2749
|
return await importFromDir(packageName, dir);
|
|
@@ -2525,10 +2820,10 @@ var install_context_exports = {};
|
|
|
2525
2820
|
__export(install_context_exports, {
|
|
2526
2821
|
createInstallContext: () => createInstallContext
|
|
2527
2822
|
});
|
|
2528
|
-
import
|
|
2823
|
+
import path27 from "path";
|
|
2529
2824
|
function createInstallContext(opts) {
|
|
2530
2825
|
const { pluginName, settingsManager, basePath, legacyConfig, instanceRoot } = opts;
|
|
2531
|
-
const dataDir =
|
|
2826
|
+
const dataDir = path27.join(basePath, pluginName, "data");
|
|
2532
2827
|
return {
|
|
2533
2828
|
pluginName,
|
|
2534
2829
|
terminal: createTerminalIO(),
|
|
@@ -2555,19 +2850,19 @@ __export(api_client_exports, {
|
|
|
2555
2850
|
readApiSecret: () => readApiSecret,
|
|
2556
2851
|
removeStalePortFile: () => removeStalePortFile
|
|
2557
2852
|
});
|
|
2558
|
-
import * as
|
|
2559
|
-
import * as
|
|
2853
|
+
import * as fs29 from "fs";
|
|
2854
|
+
import * as path28 from "path";
|
|
2560
2855
|
import * as os13 from "os";
|
|
2561
2856
|
function defaultPortFile(root) {
|
|
2562
|
-
return
|
|
2857
|
+
return path28.join(root ?? DEFAULT_ROOT, "api.port");
|
|
2563
2858
|
}
|
|
2564
2859
|
function defaultSecretFile(root) {
|
|
2565
|
-
return
|
|
2860
|
+
return path28.join(root ?? DEFAULT_ROOT, "api-secret");
|
|
2566
2861
|
}
|
|
2567
2862
|
function readApiPort(portFilePath, instanceRoot) {
|
|
2568
2863
|
const filePath = portFilePath ?? defaultPortFile(instanceRoot);
|
|
2569
2864
|
try {
|
|
2570
|
-
const content =
|
|
2865
|
+
const content = fs29.readFileSync(filePath, "utf-8").trim();
|
|
2571
2866
|
const port = parseInt(content, 10);
|
|
2572
2867
|
return isNaN(port) ? null : port;
|
|
2573
2868
|
} catch {
|
|
@@ -2577,7 +2872,7 @@ function readApiPort(portFilePath, instanceRoot) {
|
|
|
2577
2872
|
function readApiSecret(secretFilePath, instanceRoot) {
|
|
2578
2873
|
const filePath = secretFilePath ?? defaultSecretFile(instanceRoot);
|
|
2579
2874
|
try {
|
|
2580
|
-
const content =
|
|
2875
|
+
const content = fs29.readFileSync(filePath, "utf-8").trim();
|
|
2581
2876
|
return content || null;
|
|
2582
2877
|
} catch {
|
|
2583
2878
|
return null;
|
|
@@ -2586,7 +2881,7 @@ function readApiSecret(secretFilePath, instanceRoot) {
|
|
|
2586
2881
|
function removeStalePortFile(portFilePath, instanceRoot) {
|
|
2587
2882
|
const filePath = portFilePath ?? defaultPortFile(instanceRoot);
|
|
2588
2883
|
try {
|
|
2589
|
-
|
|
2884
|
+
fs29.unlinkSync(filePath);
|
|
2590
2885
|
} catch {
|
|
2591
2886
|
}
|
|
2592
2887
|
}
|
|
@@ -2602,7 +2897,7 @@ var DEFAULT_ROOT;
|
|
|
2602
2897
|
var init_api_client = __esm({
|
|
2603
2898
|
"src/cli/api-client.ts"() {
|
|
2604
2899
|
"use strict";
|
|
2605
|
-
DEFAULT_ROOT =
|
|
2900
|
+
DEFAULT_ROOT = path28.join(os13.homedir(), ".openacp");
|
|
2606
2901
|
}
|
|
2607
2902
|
});
|
|
2608
2903
|
|
|
@@ -2636,8 +2931,8 @@ var init_notification = __esm({
|
|
|
2636
2931
|
});
|
|
2637
2932
|
|
|
2638
2933
|
// src/plugins/file-service/file-service.ts
|
|
2639
|
-
import
|
|
2640
|
-
import
|
|
2934
|
+
import fs31 from "fs";
|
|
2935
|
+
import path31 from "path";
|
|
2641
2936
|
import { OggOpusDecoder } from "ogg-opus-decoder";
|
|
2642
2937
|
import wav from "node-wav";
|
|
2643
2938
|
function classifyMime(mimeType) {
|
|
@@ -2693,14 +2988,14 @@ var init_file_service = __esm({
|
|
|
2693
2988
|
const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1e3;
|
|
2694
2989
|
let removed = 0;
|
|
2695
2990
|
try {
|
|
2696
|
-
const entries = await
|
|
2991
|
+
const entries = await fs31.promises.readdir(this.baseDir, { withFileTypes: true });
|
|
2697
2992
|
for (const entry of entries) {
|
|
2698
2993
|
if (!entry.isDirectory()) continue;
|
|
2699
|
-
const dirPath =
|
|
2994
|
+
const dirPath = path31.join(this.baseDir, entry.name);
|
|
2700
2995
|
try {
|
|
2701
|
-
const stat = await
|
|
2996
|
+
const stat = await fs31.promises.stat(dirPath);
|
|
2702
2997
|
if (stat.mtimeMs < cutoff) {
|
|
2703
|
-
await
|
|
2998
|
+
await fs31.promises.rm(dirPath, { recursive: true, force: true });
|
|
2704
2999
|
removed++;
|
|
2705
3000
|
}
|
|
2706
3001
|
} catch {
|
|
@@ -2711,11 +3006,11 @@ var init_file_service = __esm({
|
|
|
2711
3006
|
return removed;
|
|
2712
3007
|
}
|
|
2713
3008
|
async saveFile(sessionId, fileName, data, mimeType) {
|
|
2714
|
-
const sessionDir =
|
|
2715
|
-
await
|
|
3009
|
+
const sessionDir = path31.join(this.baseDir, sessionId);
|
|
3010
|
+
await fs31.promises.mkdir(sessionDir, { recursive: true });
|
|
2716
3011
|
const safeName = `${Date.now()}-${fileName.replace(/[^a-zA-Z0-9._-]/g, "_")}`;
|
|
2717
|
-
const filePath =
|
|
2718
|
-
await
|
|
3012
|
+
const filePath = path31.join(sessionDir, safeName);
|
|
3013
|
+
await fs31.promises.writeFile(filePath, data);
|
|
2719
3014
|
return {
|
|
2720
3015
|
type: classifyMime(mimeType),
|
|
2721
3016
|
filePath,
|
|
@@ -2726,14 +3021,14 @@ var init_file_service = __esm({
|
|
|
2726
3021
|
}
|
|
2727
3022
|
async resolveFile(filePath) {
|
|
2728
3023
|
try {
|
|
2729
|
-
const stat = await
|
|
3024
|
+
const stat = await fs31.promises.stat(filePath);
|
|
2730
3025
|
if (!stat.isFile()) return null;
|
|
2731
|
-
const ext =
|
|
3026
|
+
const ext = path31.extname(filePath).toLowerCase();
|
|
2732
3027
|
const mimeType = EXT_TO_MIME[ext] || "application/octet-stream";
|
|
2733
3028
|
return {
|
|
2734
3029
|
type: classifyMime(mimeType),
|
|
2735
3030
|
filePath,
|
|
2736
|
-
fileName:
|
|
3031
|
+
fileName: path31.basename(filePath),
|
|
2737
3032
|
mimeType,
|
|
2738
3033
|
size: stat.size
|
|
2739
3034
|
};
|
|
@@ -2966,6 +3261,12 @@ function createAuthPreHandler(getSecret, getJwtSecret, tokenStore) {
|
|
|
2966
3261
|
const authHeader = request.headers.authorization;
|
|
2967
3262
|
const queryToken = request.query?.token;
|
|
2968
3263
|
const token = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : queryToken;
|
|
3264
|
+
if (queryToken && !authHeader) {
|
|
3265
|
+
log20.warn(
|
|
3266
|
+
{ url: request.url.replace(/([?&]token=)[^&]+/, "$1[REDACTED]") },
|
|
3267
|
+
"Token passed via URL query param \u2014 use Authorization: Bearer header to avoid token leakage in tunnel/proxy logs"
|
|
3268
|
+
);
|
|
3269
|
+
}
|
|
2969
3270
|
if (!token) {
|
|
2970
3271
|
throw new AuthError("UNAUTHORIZED", "Missing authentication token");
|
|
2971
3272
|
}
|
|
@@ -3015,12 +3316,15 @@ function requireRole(role) {
|
|
|
3015
3316
|
}
|
|
3016
3317
|
};
|
|
3017
3318
|
}
|
|
3319
|
+
var log20;
|
|
3018
3320
|
var init_auth = __esm({
|
|
3019
3321
|
"src/plugins/api-server/middleware/auth.ts"() {
|
|
3020
3322
|
"use strict";
|
|
3021
3323
|
init_error_handler();
|
|
3022
3324
|
init_jwt();
|
|
3023
3325
|
init_roles();
|
|
3326
|
+
init_log();
|
|
3327
|
+
log20 = createChildLogger({ module: "api-auth" });
|
|
3024
3328
|
}
|
|
3025
3329
|
});
|
|
3026
3330
|
|
|
@@ -3038,7 +3342,6 @@ async function createApiServer(options) {
|
|
|
3038
3342
|
const origin = request.headers.origin;
|
|
3039
3343
|
if (origin) {
|
|
3040
3344
|
reply.header("Access-Control-Allow-Origin", origin);
|
|
3041
|
-
reply.header("Access-Control-Allow-Credentials", "true");
|
|
3042
3345
|
}
|
|
3043
3346
|
if (request.method === "OPTIONS") {
|
|
3044
3347
|
reply.header("Access-Control-Allow-Methods", "GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS");
|
|
@@ -3046,7 +3349,25 @@ async function createApiServer(options) {
|
|
|
3046
3349
|
reply.status(204).send();
|
|
3047
3350
|
}
|
|
3048
3351
|
});
|
|
3049
|
-
await app.register(fastifyRateLimit, {
|
|
3352
|
+
await app.register(fastifyRateLimit, {
|
|
3353
|
+
max: 100,
|
|
3354
|
+
timeWindow: "1 minute",
|
|
3355
|
+
// When the server is reachable through a tunnel (Cloudflare, ngrok, etc.) all
|
|
3356
|
+
// requests arrive from the same tunnel-proxy IP address. Use the real client IP
|
|
3357
|
+
// from well-known forwarded headers so rate limits are enforced per-caller, not
|
|
3358
|
+
// per-tunnel. Only the first value in X-Forwarded-For is taken (the client); the
|
|
3359
|
+
// rest may be added by intermediate proxies and must not be trusted for limiting.
|
|
3360
|
+
keyGenerator: (request) => {
|
|
3361
|
+
const cfIp = request.headers["cf-connecting-ip"];
|
|
3362
|
+
if (cfIp && typeof cfIp === "string") return cfIp;
|
|
3363
|
+
const xff = request.headers["x-forwarded-for"];
|
|
3364
|
+
if (xff) {
|
|
3365
|
+
const first = (Array.isArray(xff) ? xff[0] : xff).split(",")[0]?.trim();
|
|
3366
|
+
if (first) return first;
|
|
3367
|
+
}
|
|
3368
|
+
return request.ip;
|
|
3369
|
+
}
|
|
3370
|
+
});
|
|
3050
3371
|
await app.register(fastifySwagger, {
|
|
3051
3372
|
openapi: {
|
|
3052
3373
|
info: { title: "OpenACP API", version: "1.0.0" },
|
|
@@ -3131,10 +3452,11 @@ var init_service = __esm({
|
|
|
3131
3452
|
});
|
|
3132
3453
|
|
|
3133
3454
|
// src/plugins/api-server/sse-manager.ts
|
|
3134
|
-
var SSEManager;
|
|
3455
|
+
var MAX_SSE_CONNECTIONS, SSEManager;
|
|
3135
3456
|
var init_sse_manager = __esm({
|
|
3136
3457
|
"src/plugins/api-server/sse-manager.ts"() {
|
|
3137
3458
|
"use strict";
|
|
3459
|
+
MAX_SSE_CONNECTIONS = 50;
|
|
3138
3460
|
SSEManager = class {
|
|
3139
3461
|
constructor(eventBus, getSessionStats, startedAt) {
|
|
3140
3462
|
this.eventBus = eventBus;
|
|
@@ -3176,6 +3498,11 @@ var init_sse_manager = __esm({
|
|
|
3176
3498
|
}, 15e3);
|
|
3177
3499
|
}
|
|
3178
3500
|
handleRequest(req, res) {
|
|
3501
|
+
if (this.sseConnections.size >= MAX_SSE_CONNECTIONS) {
|
|
3502
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
3503
|
+
res.end(JSON.stringify({ error: "Too many SSE connections" }));
|
|
3504
|
+
return;
|
|
3505
|
+
}
|
|
3179
3506
|
const parsedUrl = new URL(req.url || "", "http://localhost");
|
|
3180
3507
|
const sessionFilter = parsedUrl.searchParams.get("sessionId");
|
|
3181
3508
|
console.log(`[sse] +connection total=${this.sseConnections.size + 1}`);
|
|
@@ -3189,7 +3516,6 @@ var init_sse_manager = __esm({
|
|
|
3189
3516
|
};
|
|
3190
3517
|
if (origin) {
|
|
3191
3518
|
corsHeaders["Access-Control-Allow-Origin"] = origin;
|
|
3192
|
-
corsHeaders["Access-Control-Allow-Credentials"] = "true";
|
|
3193
3519
|
}
|
|
3194
3520
|
res.socket?.setNoDelay(true);
|
|
3195
3521
|
res.writeHead(200, corsHeaders);
|
|
@@ -3256,8 +3582,8 @@ data: ${JSON.stringify(data)}
|
|
|
3256
3582
|
});
|
|
3257
3583
|
|
|
3258
3584
|
// src/plugins/api-server/static-server.ts
|
|
3259
|
-
import * as
|
|
3260
|
-
import * as
|
|
3585
|
+
import * as fs32 from "fs";
|
|
3586
|
+
import * as path32 from "path";
|
|
3261
3587
|
import { fileURLToPath } from "url";
|
|
3262
3588
|
var MIME_TYPES, StaticServer;
|
|
3263
3589
|
var init_static_server = __esm({
|
|
@@ -3281,16 +3607,16 @@ var init_static_server = __esm({
|
|
|
3281
3607
|
this.uiDir = uiDir;
|
|
3282
3608
|
if (!this.uiDir) {
|
|
3283
3609
|
const __filename = fileURLToPath(import.meta.url);
|
|
3284
|
-
const candidate =
|
|
3285
|
-
if (
|
|
3610
|
+
const candidate = path32.resolve(path32.dirname(__filename), "../../ui/dist");
|
|
3611
|
+
if (fs32.existsSync(path32.join(candidate, "index.html"))) {
|
|
3286
3612
|
this.uiDir = candidate;
|
|
3287
3613
|
}
|
|
3288
3614
|
if (!this.uiDir) {
|
|
3289
|
-
const publishCandidate =
|
|
3290
|
-
|
|
3615
|
+
const publishCandidate = path32.resolve(
|
|
3616
|
+
path32.dirname(__filename),
|
|
3291
3617
|
"../ui"
|
|
3292
3618
|
);
|
|
3293
|
-
if (
|
|
3619
|
+
if (fs32.existsSync(path32.join(publishCandidate, "index.html"))) {
|
|
3294
3620
|
this.uiDir = publishCandidate;
|
|
3295
3621
|
}
|
|
3296
3622
|
}
|
|
@@ -3302,12 +3628,23 @@ var init_static_server = __esm({
|
|
|
3302
3628
|
serve(req, res) {
|
|
3303
3629
|
if (!this.uiDir) return false;
|
|
3304
3630
|
const urlPath = (req.url || "/").split("?")[0];
|
|
3305
|
-
const safePath =
|
|
3306
|
-
const filePath =
|
|
3307
|
-
if (!filePath.startsWith(this.uiDir +
|
|
3631
|
+
const safePath = path32.normalize(urlPath);
|
|
3632
|
+
const filePath = path32.join(this.uiDir, safePath);
|
|
3633
|
+
if (!filePath.startsWith(this.uiDir + path32.sep) && filePath !== this.uiDir)
|
|
3308
3634
|
return false;
|
|
3309
|
-
|
|
3310
|
-
|
|
3635
|
+
let realFilePath;
|
|
3636
|
+
try {
|
|
3637
|
+
realFilePath = fs32.realpathSync(filePath);
|
|
3638
|
+
} catch {
|
|
3639
|
+
realFilePath = null;
|
|
3640
|
+
}
|
|
3641
|
+
if (realFilePath !== null) {
|
|
3642
|
+
const realUiDir = fs32.realpathSync(this.uiDir);
|
|
3643
|
+
if (!realFilePath.startsWith(realUiDir + path32.sep) && realFilePath !== realUiDir)
|
|
3644
|
+
return false;
|
|
3645
|
+
}
|
|
3646
|
+
if (realFilePath !== null && fs32.existsSync(realFilePath) && fs32.statSync(realFilePath).isFile()) {
|
|
3647
|
+
const ext = path32.extname(filePath);
|
|
3311
3648
|
const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
3312
3649
|
const isHashed = /\.[a-zA-Z0-9]{8,}\.(js|css)$/.test(filePath);
|
|
3313
3650
|
const cacheControl = isHashed ? "public, max-age=31536000, immutable" : "no-cache";
|
|
@@ -3315,16 +3652,16 @@ var init_static_server = __esm({
|
|
|
3315
3652
|
"Content-Type": contentType,
|
|
3316
3653
|
"Cache-Control": cacheControl
|
|
3317
3654
|
});
|
|
3318
|
-
|
|
3655
|
+
fs32.createReadStream(realFilePath).pipe(res);
|
|
3319
3656
|
return true;
|
|
3320
3657
|
}
|
|
3321
|
-
const indexPath =
|
|
3322
|
-
if (
|
|
3658
|
+
const indexPath = path32.join(this.uiDir, "index.html");
|
|
3659
|
+
if (fs32.existsSync(indexPath)) {
|
|
3323
3660
|
res.writeHead(200, {
|
|
3324
3661
|
"Content-Type": "text/html; charset=utf-8",
|
|
3325
3662
|
"Cache-Control": "no-cache"
|
|
3326
3663
|
});
|
|
3327
|
-
|
|
3664
|
+
fs32.createReadStream(indexPath).pipe(res);
|
|
3328
3665
|
return true;
|
|
3329
3666
|
}
|
|
3330
3667
|
return false;
|
|
@@ -3479,9 +3816,9 @@ var init_exports = __esm({
|
|
|
3479
3816
|
});
|
|
3480
3817
|
|
|
3481
3818
|
// src/plugins/context/context-cache.ts
|
|
3482
|
-
import * as
|
|
3483
|
-
import * as
|
|
3484
|
-
import * as
|
|
3819
|
+
import * as fs33 from "fs";
|
|
3820
|
+
import * as path33 from "path";
|
|
3821
|
+
import * as crypto2 from "crypto";
|
|
3485
3822
|
var DEFAULT_TTL_MS, ContextCache;
|
|
3486
3823
|
var init_context_cache = __esm({
|
|
3487
3824
|
"src/plugins/context/context-cache.ts"() {
|
|
@@ -3491,29 +3828,29 @@ var init_context_cache = __esm({
|
|
|
3491
3828
|
constructor(cacheDir, ttlMs = DEFAULT_TTL_MS) {
|
|
3492
3829
|
this.cacheDir = cacheDir;
|
|
3493
3830
|
this.ttlMs = ttlMs;
|
|
3494
|
-
|
|
3831
|
+
fs33.mkdirSync(cacheDir, { recursive: true });
|
|
3495
3832
|
}
|
|
3496
3833
|
keyHash(repoPath, queryKey) {
|
|
3497
|
-
return
|
|
3834
|
+
return crypto2.createHash("sha256").update(`${repoPath}:${queryKey}`).digest("hex").slice(0, 16);
|
|
3498
3835
|
}
|
|
3499
3836
|
filePath(repoPath, queryKey) {
|
|
3500
|
-
return
|
|
3837
|
+
return path33.join(this.cacheDir, `${this.keyHash(repoPath, queryKey)}.json`);
|
|
3501
3838
|
}
|
|
3502
3839
|
get(repoPath, queryKey) {
|
|
3503
3840
|
const fp = this.filePath(repoPath, queryKey);
|
|
3504
3841
|
try {
|
|
3505
|
-
const stat =
|
|
3842
|
+
const stat = fs33.statSync(fp);
|
|
3506
3843
|
if (Date.now() - stat.mtimeMs > this.ttlMs) {
|
|
3507
|
-
|
|
3844
|
+
fs33.unlinkSync(fp);
|
|
3508
3845
|
return null;
|
|
3509
3846
|
}
|
|
3510
|
-
return JSON.parse(
|
|
3847
|
+
return JSON.parse(fs33.readFileSync(fp, "utf-8"));
|
|
3511
3848
|
} catch {
|
|
3512
3849
|
return null;
|
|
3513
3850
|
}
|
|
3514
3851
|
}
|
|
3515
3852
|
set(repoPath, queryKey, result) {
|
|
3516
|
-
|
|
3853
|
+
fs33.writeFileSync(this.filePath(repoPath, queryKey), JSON.stringify(result));
|
|
3517
3854
|
}
|
|
3518
3855
|
};
|
|
3519
3856
|
}
|
|
@@ -3521,7 +3858,7 @@ var init_context_cache = __esm({
|
|
|
3521
3858
|
|
|
3522
3859
|
// src/plugins/context/context-manager.ts
|
|
3523
3860
|
import * as os15 from "os";
|
|
3524
|
-
import * as
|
|
3861
|
+
import * as path34 from "path";
|
|
3525
3862
|
var ContextManager;
|
|
3526
3863
|
var init_context_manager = __esm({
|
|
3527
3864
|
"src/plugins/context/context-manager.ts"() {
|
|
@@ -3532,7 +3869,7 @@ var init_context_manager = __esm({
|
|
|
3532
3869
|
cache;
|
|
3533
3870
|
historyStore;
|
|
3534
3871
|
constructor(cachePath) {
|
|
3535
|
-
this.cache = new ContextCache(cachePath ??
|
|
3872
|
+
this.cache = new ContextCache(cachePath ?? path34.join(os15.homedir(), ".openacp", "cache", "entire"));
|
|
3536
3873
|
}
|
|
3537
3874
|
setHistoryStore(store) {
|
|
3538
3875
|
this.historyStore = store;
|
|
@@ -4433,8 +4770,8 @@ function formatToolSummary(name, rawInput, displaySummary) {
|
|
|
4433
4770
|
}
|
|
4434
4771
|
if (lowerName === "grep") {
|
|
4435
4772
|
const pattern = args.pattern ?? "";
|
|
4436
|
-
const
|
|
4437
|
-
return pattern ? `\u{1F50D} Grep "${pattern}"${
|
|
4773
|
+
const path35 = args.path ?? "";
|
|
4774
|
+
return pattern ? `\u{1F50D} Grep "${pattern}"${path35 ? ` in ${path35}` : ""}` : `\u{1F527} ${name}`;
|
|
4438
4775
|
}
|
|
4439
4776
|
if (lowerName === "glob") {
|
|
4440
4777
|
const pattern = args.pattern ?? "";
|
|
@@ -4470,8 +4807,8 @@ function formatToolTitle(name, rawInput, displayTitle) {
|
|
|
4470
4807
|
}
|
|
4471
4808
|
if (lowerName === "grep") {
|
|
4472
4809
|
const pattern = args.pattern ?? "";
|
|
4473
|
-
const
|
|
4474
|
-
return pattern ? `"${pattern}"${
|
|
4810
|
+
const path35 = args.path ?? "";
|
|
4811
|
+
return pattern ? `"${pattern}"${path35 ? ` in ${path35}` : ""}` : name;
|
|
4475
4812
|
}
|
|
4476
4813
|
if (lowerName === "glob") {
|
|
4477
4814
|
return String(args.pattern ?? name);
|
|
@@ -5735,7 +6072,7 @@ function setupDangerousModeCallbacks(bot, core) {
|
|
|
5735
6072
|
}).catch(() => {
|
|
5736
6073
|
});
|
|
5737
6074
|
}
|
|
5738
|
-
|
|
6075
|
+
log22.info({ sessionId, wantOn }, "Bypass permissions toggled via button");
|
|
5739
6076
|
try {
|
|
5740
6077
|
await ctx.editMessageText(buildSessionStatusText(session), {
|
|
5741
6078
|
parse_mode: "HTML",
|
|
@@ -5758,7 +6095,7 @@ function setupDangerousModeCallbacks(bot, core) {
|
|
|
5758
6095
|
const newDangerousMode = !(record.clientOverrides?.bypassPermissions ?? record.dangerousMode ?? false);
|
|
5759
6096
|
core.sessionManager.patchRecord(sessionId, { clientOverrides: { bypassPermissions: newDangerousMode } }).catch(() => {
|
|
5760
6097
|
});
|
|
5761
|
-
|
|
6098
|
+
log22.info(
|
|
5762
6099
|
{ sessionId, dangerousMode: newDangerousMode },
|
|
5763
6100
|
"Bypass permissions toggled via button (store-only, session not in memory)"
|
|
5764
6101
|
);
|
|
@@ -5943,14 +6280,14 @@ async function handleRestart(ctx, core) {
|
|
|
5943
6280
|
await new Promise((r) => setTimeout(r, 500));
|
|
5944
6281
|
await core.requestRestart();
|
|
5945
6282
|
}
|
|
5946
|
-
var
|
|
6283
|
+
var log22, OUTPUT_MODE_LABELS;
|
|
5947
6284
|
var init_admin = __esm({
|
|
5948
6285
|
"src/plugins/telegram/commands/admin.ts"() {
|
|
5949
6286
|
"use strict";
|
|
5950
6287
|
init_bypass_detection();
|
|
5951
6288
|
init_formatting();
|
|
5952
6289
|
init_log();
|
|
5953
|
-
|
|
6290
|
+
log22 = createChildLogger({ module: "telegram-cmd-admin" });
|
|
5954
6291
|
OUTPUT_MODE_LABELS = {
|
|
5955
6292
|
low: "\u{1F507} Low",
|
|
5956
6293
|
medium: "\u{1F4CA} Medium",
|
|
@@ -5965,7 +6302,7 @@ function botFromCtx(ctx) {
|
|
|
5965
6302
|
return { api: ctx.api };
|
|
5966
6303
|
}
|
|
5967
6304
|
async function createSessionDirect(ctx, core, chatId, agentName, workspace, onControlMessage) {
|
|
5968
|
-
|
|
6305
|
+
log23.info({ userId: ctx.from?.id, agentName, workspace }, "New session command (direct)");
|
|
5969
6306
|
let threadId;
|
|
5970
6307
|
try {
|
|
5971
6308
|
const topicName = `\u{1F504} New Session`;
|
|
@@ -5994,7 +6331,7 @@ async function createSessionDirect(ctx, core, chatId, agentName, workspace, onCo
|
|
|
5994
6331
|
onControlMessage?.(session.id, controlMsg.message_id);
|
|
5995
6332
|
return threadId ?? null;
|
|
5996
6333
|
} catch (err) {
|
|
5997
|
-
|
|
6334
|
+
log23.error({ err }, "Session creation failed");
|
|
5998
6335
|
if (threadId) {
|
|
5999
6336
|
try {
|
|
6000
6337
|
await ctx.api.deleteForumTopic(chatId, threadId);
|
|
@@ -6184,7 +6521,7 @@ Agent: <code>${escapeHtml(agentKey)}</code>
|
|
|
6184
6521
|
}
|
|
6185
6522
|
});
|
|
6186
6523
|
}
|
|
6187
|
-
var
|
|
6524
|
+
var log23, WS_CACHE_MAX, workspaceCache, nextWsId;
|
|
6188
6525
|
var init_new_session = __esm({
|
|
6189
6526
|
"src/plugins/telegram/commands/new-session.ts"() {
|
|
6190
6527
|
"use strict";
|
|
@@ -6192,7 +6529,7 @@ var init_new_session = __esm({
|
|
|
6192
6529
|
init_topics();
|
|
6193
6530
|
init_log();
|
|
6194
6531
|
init_admin();
|
|
6195
|
-
|
|
6532
|
+
log23 = createChildLogger({ module: "telegram-cmd-new-session" });
|
|
6196
6533
|
WS_CACHE_MAX = 50;
|
|
6197
6534
|
workspaceCache = /* @__PURE__ */ new Map();
|
|
6198
6535
|
nextWsId = 0;
|
|
@@ -6256,7 +6593,7 @@ ${lines.join("\n")}${truncated}`,
|
|
|
6256
6593
|
{ parse_mode: "HTML", reply_markup: keyboard }
|
|
6257
6594
|
);
|
|
6258
6595
|
} catch (err) {
|
|
6259
|
-
|
|
6596
|
+
log24.error({ err }, "handleTopics error");
|
|
6260
6597
|
await ctx.reply("\u274C Failed to list sessions.", { parse_mode: "HTML" }).catch(() => {
|
|
6261
6598
|
});
|
|
6262
6599
|
}
|
|
@@ -6277,13 +6614,13 @@ async function handleCleanup(ctx, core, chatId, statuses) {
|
|
|
6277
6614
|
try {
|
|
6278
6615
|
await ctx.api.deleteForumTopic(chatId, topicId);
|
|
6279
6616
|
} catch (err) {
|
|
6280
|
-
|
|
6617
|
+
log24.warn({ err, sessionId: record.sessionId, topicId }, "Failed to delete forum topic during cleanup");
|
|
6281
6618
|
}
|
|
6282
6619
|
}
|
|
6283
6620
|
await core.sessionManager.removeRecord(record.sessionId);
|
|
6284
6621
|
deleted++;
|
|
6285
6622
|
} catch (err) {
|
|
6286
|
-
|
|
6623
|
+
log24.error({ err, sessionId: record.sessionId }, "Failed to cleanup session");
|
|
6287
6624
|
failed++;
|
|
6288
6625
|
}
|
|
6289
6626
|
}
|
|
@@ -6354,7 +6691,7 @@ async function handleCleanupEverythingConfirmed(ctx, core, chatId, systemTopicId
|
|
|
6354
6691
|
try {
|
|
6355
6692
|
await core.sessionManager.cancelSession(record.sessionId);
|
|
6356
6693
|
} catch (err) {
|
|
6357
|
-
|
|
6694
|
+
log24.warn({ err, sessionId: record.sessionId }, "Failed to cancel session during cleanup");
|
|
6358
6695
|
}
|
|
6359
6696
|
}
|
|
6360
6697
|
const topicId = record.platform?.topicId;
|
|
@@ -6362,13 +6699,13 @@ async function handleCleanupEverythingConfirmed(ctx, core, chatId, systemTopicId
|
|
|
6362
6699
|
try {
|
|
6363
6700
|
await ctx.api.deleteForumTopic(chatId, topicId);
|
|
6364
6701
|
} catch (err) {
|
|
6365
|
-
|
|
6702
|
+
log24.warn({ err, sessionId: record.sessionId, topicId }, "Failed to delete forum topic during cleanup");
|
|
6366
6703
|
}
|
|
6367
6704
|
}
|
|
6368
6705
|
await core.sessionManager.removeRecord(record.sessionId);
|
|
6369
6706
|
deleted++;
|
|
6370
6707
|
} catch (err) {
|
|
6371
|
-
|
|
6708
|
+
log24.error({ err, sessionId: record.sessionId }, "Failed to cleanup session");
|
|
6372
6709
|
failed++;
|
|
6373
6710
|
}
|
|
6374
6711
|
}
|
|
@@ -6436,13 +6773,13 @@ async function handleArchiveConfirm(ctx, core, chatId) {
|
|
|
6436
6773
|
}
|
|
6437
6774
|
}
|
|
6438
6775
|
}
|
|
6439
|
-
var
|
|
6776
|
+
var log24;
|
|
6440
6777
|
var init_session = __esm({
|
|
6441
6778
|
"src/plugins/telegram/commands/session.ts"() {
|
|
6442
6779
|
"use strict";
|
|
6443
6780
|
init_formatting();
|
|
6444
6781
|
init_log();
|
|
6445
|
-
|
|
6782
|
+
log24 = createChildLogger({ module: "telegram-cmd-session" });
|
|
6446
6783
|
}
|
|
6447
6784
|
});
|
|
6448
6785
|
|
|
@@ -7085,7 +7422,7 @@ var init_agents2 = __esm({
|
|
|
7085
7422
|
// src/plugins/telegram/commands/resume.ts
|
|
7086
7423
|
function setupResumeCallbacks(_bot, _core, _chatId, _onControlMessage) {
|
|
7087
7424
|
}
|
|
7088
|
-
var
|
|
7425
|
+
var log25;
|
|
7089
7426
|
var init_resume = __esm({
|
|
7090
7427
|
"src/plugins/telegram/commands/resume.ts"() {
|
|
7091
7428
|
"use strict";
|
|
@@ -7095,7 +7432,7 @@ var init_resume = __esm({
|
|
|
7095
7432
|
init_topics();
|
|
7096
7433
|
init_admin();
|
|
7097
7434
|
init_log();
|
|
7098
|
-
|
|
7435
|
+
log25 = createChildLogger({ module: "telegram-cmd-resume" });
|
|
7099
7436
|
}
|
|
7100
7437
|
});
|
|
7101
7438
|
|
|
@@ -7280,7 +7617,7 @@ function setupSettingsCallbacks(bot, core, getAssistantSession) {
|
|
|
7280
7617
|
} catch {
|
|
7281
7618
|
}
|
|
7282
7619
|
} catch (err) {
|
|
7283
|
-
|
|
7620
|
+
log26.error({ err, fieldPath }, "Failed to toggle config");
|
|
7284
7621
|
try {
|
|
7285
7622
|
await ctx.answerCallbackQuery({ text: "\u274C Failed to update" });
|
|
7286
7623
|
} catch {
|
|
@@ -7363,7 +7700,7 @@ Tap to change:`, {
|
|
|
7363
7700
|
} catch {
|
|
7364
7701
|
}
|
|
7365
7702
|
} catch (err) {
|
|
7366
|
-
|
|
7703
|
+
log26.error({ err, fieldPath }, "Failed to set config");
|
|
7367
7704
|
try {
|
|
7368
7705
|
await ctx.answerCallbackQuery({ text: "\u274C Failed to update" });
|
|
7369
7706
|
} catch {
|
|
@@ -7421,13 +7758,13 @@ Tap to change:`, {
|
|
|
7421
7758
|
}
|
|
7422
7759
|
});
|
|
7423
7760
|
}
|
|
7424
|
-
var
|
|
7761
|
+
var log26;
|
|
7425
7762
|
var init_settings = __esm({
|
|
7426
7763
|
"src/plugins/telegram/commands/settings.ts"() {
|
|
7427
7764
|
"use strict";
|
|
7428
7765
|
init_config_registry();
|
|
7429
7766
|
init_log();
|
|
7430
|
-
|
|
7767
|
+
log26 = createChildLogger({ module: "telegram-settings" });
|
|
7431
7768
|
}
|
|
7432
7769
|
});
|
|
7433
7770
|
|
|
@@ -7474,7 +7811,7 @@ async function handleDoctor(ctx) {
|
|
|
7474
7811
|
reply_markup: keyboard
|
|
7475
7812
|
});
|
|
7476
7813
|
} catch (err) {
|
|
7477
|
-
|
|
7814
|
+
log27.error({ err }, "Doctor command failed");
|
|
7478
7815
|
await ctx.api.editMessageText(
|
|
7479
7816
|
ctx.chat.id,
|
|
7480
7817
|
statusMsg.message_id,
|
|
@@ -7523,7 +7860,7 @@ function setupDoctorCallbacks(bot) {
|
|
|
7523
7860
|
}
|
|
7524
7861
|
}
|
|
7525
7862
|
} catch (err) {
|
|
7526
|
-
|
|
7863
|
+
log27.error({ err, index }, "Doctor fix callback failed");
|
|
7527
7864
|
}
|
|
7528
7865
|
});
|
|
7529
7866
|
bot.callbackQuery("m:doctor", async (ctx) => {
|
|
@@ -7534,13 +7871,13 @@ function setupDoctorCallbacks(bot) {
|
|
|
7534
7871
|
await handleDoctor(ctx);
|
|
7535
7872
|
});
|
|
7536
7873
|
}
|
|
7537
|
-
var
|
|
7874
|
+
var log27, pendingFixesStore;
|
|
7538
7875
|
var init_doctor2 = __esm({
|
|
7539
7876
|
"src/plugins/telegram/commands/doctor.ts"() {
|
|
7540
7877
|
"use strict";
|
|
7541
7878
|
init_doctor();
|
|
7542
7879
|
init_log();
|
|
7543
|
-
|
|
7880
|
+
log27 = createChildLogger({ module: "telegram-cmd-doctor" });
|
|
7544
7881
|
pendingFixesStore = /* @__PURE__ */ new Map();
|
|
7545
7882
|
}
|
|
7546
7883
|
});
|
|
@@ -7599,13 +7936,13 @@ function setupTunnelCallbacks(bot, core) {
|
|
|
7599
7936
|
}
|
|
7600
7937
|
});
|
|
7601
7938
|
}
|
|
7602
|
-
var
|
|
7939
|
+
var log28;
|
|
7603
7940
|
var init_tunnel2 = __esm({
|
|
7604
7941
|
"src/plugins/telegram/commands/tunnel.ts"() {
|
|
7605
7942
|
"use strict";
|
|
7606
7943
|
init_formatting();
|
|
7607
7944
|
init_log();
|
|
7608
|
-
|
|
7945
|
+
log28 = createChildLogger({ module: "telegram-cmd-tunnel" });
|
|
7609
7946
|
}
|
|
7610
7947
|
});
|
|
7611
7948
|
|
|
@@ -7619,10 +7956,10 @@ async function executeSwitchAgent(ctx, core, sessionId, agentName) {
|
|
|
7619
7956
|
`Switched to <b>${escapeHtml(agentName)}</b> (${status})`,
|
|
7620
7957
|
{ parse_mode: "HTML" }
|
|
7621
7958
|
);
|
|
7622
|
-
|
|
7959
|
+
log29.info({ sessionId, agentName, resumed }, "Agent switched via /switch");
|
|
7623
7960
|
} catch (err) {
|
|
7624
7961
|
await ctx.reply(`Failed to switch agent: ${escapeHtml(String(err.message || err))}`);
|
|
7625
|
-
|
|
7962
|
+
log29.warn({ sessionId, agentName, err: err.message }, "Agent switch failed");
|
|
7626
7963
|
}
|
|
7627
7964
|
}
|
|
7628
7965
|
function setupSwitchCallbacks(bot, core) {
|
|
@@ -7667,13 +8004,13 @@ Switch to <b>${escapeHtml(agentName)}</b> anyway?`,
|
|
|
7667
8004
|
await executeSwitchAgent(ctx, core, session.id, data);
|
|
7668
8005
|
});
|
|
7669
8006
|
}
|
|
7670
|
-
var
|
|
8007
|
+
var log29;
|
|
7671
8008
|
var init_switch = __esm({
|
|
7672
8009
|
"src/plugins/telegram/commands/switch.ts"() {
|
|
7673
8010
|
"use strict";
|
|
7674
8011
|
init_formatting();
|
|
7675
8012
|
init_log();
|
|
7676
|
-
|
|
8013
|
+
log29 = createChildLogger({ module: "telegram-cmd-switch" });
|
|
7677
8014
|
}
|
|
7678
8015
|
});
|
|
7679
8016
|
|
|
@@ -8028,14 +8365,14 @@ var init_commands = __esm({
|
|
|
8028
8365
|
// src/plugins/telegram/permissions.ts
|
|
8029
8366
|
import { InlineKeyboard as InlineKeyboard11 } from "grammy";
|
|
8030
8367
|
import { nanoid as nanoid2 } from "nanoid";
|
|
8031
|
-
var
|
|
8368
|
+
var log30, PermissionHandler;
|
|
8032
8369
|
var init_permissions = __esm({
|
|
8033
8370
|
"src/plugins/telegram/permissions.ts"() {
|
|
8034
8371
|
"use strict";
|
|
8035
8372
|
init_formatting();
|
|
8036
8373
|
init_topics();
|
|
8037
8374
|
init_log();
|
|
8038
|
-
|
|
8375
|
+
log30 = createChildLogger({ module: "telegram-permissions" });
|
|
8039
8376
|
PermissionHandler = class {
|
|
8040
8377
|
constructor(bot, chatId, getSession, sendNotification) {
|
|
8041
8378
|
this.bot = bot;
|
|
@@ -8095,7 +8432,7 @@ ${escapeHtml(request.description)}`,
|
|
|
8095
8432
|
}
|
|
8096
8433
|
const session = this.getSession(pending.sessionId);
|
|
8097
8434
|
const isAllow = pending.options.find((o) => o.id === optionId)?.isAllow ?? false;
|
|
8098
|
-
|
|
8435
|
+
log30.info({ requestId: pending.requestId, optionId, isAllow }, "Permission responded");
|
|
8099
8436
|
if (session?.permissionGate.requestId === pending.requestId) {
|
|
8100
8437
|
session.permissionGate.resolve(optionId);
|
|
8101
8438
|
}
|
|
@@ -8115,7 +8452,7 @@ ${escapeHtml(request.description)}`,
|
|
|
8115
8452
|
});
|
|
8116
8453
|
|
|
8117
8454
|
// src/plugins/telegram/activity.ts
|
|
8118
|
-
var
|
|
8455
|
+
var log31, THINKING_REFRESH_MS, THINKING_MAX_MS, ThinkingIndicator, ToolCard, ActivityTracker2;
|
|
8119
8456
|
var init_activity = __esm({
|
|
8120
8457
|
"src/plugins/telegram/activity.ts"() {
|
|
8121
8458
|
"use strict";
|
|
@@ -8125,7 +8462,7 @@ var init_activity = __esm({
|
|
|
8125
8462
|
init_stream_accumulator();
|
|
8126
8463
|
init_stream_accumulator();
|
|
8127
8464
|
init_display_spec_builder();
|
|
8128
|
-
|
|
8465
|
+
log31 = createChildLogger({ module: "telegram:activity" });
|
|
8129
8466
|
THINKING_REFRESH_MS = 15e3;
|
|
8130
8467
|
THINKING_MAX_MS = 3 * 60 * 1e3;
|
|
8131
8468
|
ThinkingIndicator = class {
|
|
@@ -8166,7 +8503,7 @@ var init_activity = __esm({
|
|
|
8166
8503
|
}
|
|
8167
8504
|
}
|
|
8168
8505
|
} catch (err) {
|
|
8169
|
-
|
|
8506
|
+
log31.warn({ err }, "ThinkingIndicator.show() failed");
|
|
8170
8507
|
} finally {
|
|
8171
8508
|
this.sending = false;
|
|
8172
8509
|
}
|
|
@@ -8334,7 +8671,7 @@ var init_activity = __esm({
|
|
|
8334
8671
|
this.tracer?.log("telegram", { action: "telegram:delete:overflow", sessionId: this.sessionId, msgId: staleId });
|
|
8335
8672
|
}
|
|
8336
8673
|
} catch (err) {
|
|
8337
|
-
|
|
8674
|
+
log31.warn({ err }, "[ToolCard] send/edit failed");
|
|
8338
8675
|
}
|
|
8339
8676
|
}
|
|
8340
8677
|
};
|
|
@@ -8438,7 +8775,7 @@ var init_activity = __esm({
|
|
|
8438
8775
|
const entry = this.toolStateMap.merge(id, status, rawInput, content, viewerLinks, diffStats);
|
|
8439
8776
|
if (!existed || !entry) return;
|
|
8440
8777
|
if (viewerLinks || entry.viewerLinks) {
|
|
8441
|
-
|
|
8778
|
+
log31.debug({ toolId: id, status, hasIncomingLinks: !!viewerLinks, hasEntryLinks: !!entry.viewerLinks, entryLinks: entry.viewerLinks }, "toolUpdate: viewer links trace");
|
|
8442
8779
|
}
|
|
8443
8780
|
const spec = this.specBuilder.buildToolSpec(entry, this._outputMode, this.sessionContext);
|
|
8444
8781
|
this.toolCard.updateFromSpec(spec);
|
|
@@ -8757,13 +9094,13 @@ var init_draft_manager = __esm({
|
|
|
8757
9094
|
});
|
|
8758
9095
|
|
|
8759
9096
|
// src/plugins/telegram/skill-command-manager.ts
|
|
8760
|
-
var
|
|
9097
|
+
var log32, SkillCommandManager;
|
|
8761
9098
|
var init_skill_command_manager = __esm({
|
|
8762
9099
|
"src/plugins/telegram/skill-command-manager.ts"() {
|
|
8763
9100
|
"use strict";
|
|
8764
9101
|
init_commands();
|
|
8765
9102
|
init_log();
|
|
8766
|
-
|
|
9103
|
+
log32 = createChildLogger({ module: "skill-commands" });
|
|
8767
9104
|
SkillCommandManager = class {
|
|
8768
9105
|
// sessionId → pinned msgId
|
|
8769
9106
|
constructor(bot, chatId, sendQueue, sessionManager) {
|
|
@@ -8829,7 +9166,7 @@ var init_skill_command_manager = __esm({
|
|
|
8829
9166
|
disable_notification: true
|
|
8830
9167
|
});
|
|
8831
9168
|
} catch (err) {
|
|
8832
|
-
|
|
9169
|
+
log32.error({ err, sessionId }, "Failed to send skill commands");
|
|
8833
9170
|
}
|
|
8834
9171
|
}
|
|
8835
9172
|
async cleanup(sessionId) {
|
|
@@ -8950,7 +9287,7 @@ function patchedFetch(input2, init) {
|
|
|
8950
9287
|
}
|
|
8951
9288
|
return fetch(input2, init);
|
|
8952
9289
|
}
|
|
8953
|
-
var
|
|
9290
|
+
var log33, TelegramAdapter;
|
|
8954
9291
|
var init_adapter = __esm({
|
|
8955
9292
|
"src/plugins/telegram/adapter.ts"() {
|
|
8956
9293
|
"use strict";
|
|
@@ -8969,7 +9306,7 @@ var init_adapter = __esm({
|
|
|
8969
9306
|
init_messaging_adapter();
|
|
8970
9307
|
init_renderer2();
|
|
8971
9308
|
init_output_mode_resolver();
|
|
8972
|
-
|
|
9309
|
+
log33 = createChildLogger({ module: "telegram" });
|
|
8973
9310
|
TelegramAdapter = class extends MessagingAdapter {
|
|
8974
9311
|
name = "telegram";
|
|
8975
9312
|
renderer = new TelegramRenderer();
|
|
@@ -9002,6 +9339,8 @@ var init_adapter = __esm({
|
|
|
9002
9339
|
_pendingSkillCommands = /* @__PURE__ */ new Map();
|
|
9003
9340
|
/** Control message IDs per session (for updating status text/buttons) */
|
|
9004
9341
|
controlMsgIds = /* @__PURE__ */ new Map();
|
|
9342
|
+
_threadReadyHandler;
|
|
9343
|
+
_configChangedHandler;
|
|
9005
9344
|
/** Store control message ID in memory + persist to session record */
|
|
9006
9345
|
storeControlMsgId(sessionId, msgId) {
|
|
9007
9346
|
this.controlMsgIds.set(sessionId, msgId);
|
|
@@ -9088,7 +9427,7 @@ var init_adapter = __esm({
|
|
|
9088
9427
|
);
|
|
9089
9428
|
this.bot.catch((err) => {
|
|
9090
9429
|
const rootCause = err.error instanceof Error ? err.error : err;
|
|
9091
|
-
|
|
9430
|
+
log33.error({ err: rootCause }, "Telegram bot error");
|
|
9092
9431
|
});
|
|
9093
9432
|
this.bot.api.config.use(async (prev, method, payload, signal) => {
|
|
9094
9433
|
const maxRetries = 3;
|
|
@@ -9106,7 +9445,7 @@ var init_adapter = __esm({
|
|
|
9106
9445
|
if (rateLimitedMethods.includes(method)) {
|
|
9107
9446
|
this.sendQueue.onRateLimited();
|
|
9108
9447
|
}
|
|
9109
|
-
|
|
9448
|
+
log33.warn(
|
|
9110
9449
|
{ method, retryAfter, attempt: attempt + 1 },
|
|
9111
9450
|
"Rate limited by Telegram, retrying"
|
|
9112
9451
|
);
|
|
@@ -9301,7 +9640,7 @@ ${p}` : p;
|
|
|
9301
9640
|
}
|
|
9302
9641
|
);
|
|
9303
9642
|
this.permissionHandler.setupCallbackHandler();
|
|
9304
|
-
this.
|
|
9643
|
+
this._threadReadyHandler = ({ sessionId, channelId, threadId }) => {
|
|
9305
9644
|
if (channelId !== "telegram") return;
|
|
9306
9645
|
const session = this.core.sessionManager.getSession(sessionId);
|
|
9307
9646
|
if (!session) return;
|
|
@@ -9328,17 +9667,19 @@ ${p}` : p;
|
|
|
9328
9667
|
).then((msg) => {
|
|
9329
9668
|
if (msg) this.storeControlMsgId(sessionId, msg.message_id);
|
|
9330
9669
|
}).catch((err) => {
|
|
9331
|
-
|
|
9670
|
+
log33.warn({ err, sessionId }, "Failed to send initial messages for new session");
|
|
9332
9671
|
});
|
|
9333
|
-
}
|
|
9334
|
-
this.core.eventBus.on("session:
|
|
9672
|
+
};
|
|
9673
|
+
this.core.eventBus.on("session:threadReady", this._threadReadyHandler);
|
|
9674
|
+
this._configChangedHandler = ({ sessionId }) => {
|
|
9335
9675
|
this.updateControlMessage(sessionId).catch(() => {
|
|
9336
9676
|
});
|
|
9337
|
-
}
|
|
9677
|
+
};
|
|
9678
|
+
this.core.eventBus.on("session:configChanged", this._configChangedHandler);
|
|
9338
9679
|
this.setupRoutes();
|
|
9339
9680
|
this.bot.start({
|
|
9340
9681
|
allowed_updates: ["message", "callback_query"],
|
|
9341
|
-
onStart: () =>
|
|
9682
|
+
onStart: () => log33.info(
|
|
9342
9683
|
{ chatId: this.telegramConfig.chatId },
|
|
9343
9684
|
"Telegram bot started"
|
|
9344
9685
|
)
|
|
@@ -9365,12 +9706,12 @@ ${p}` : p;
|
|
|
9365
9706
|
)
|
|
9366
9707
|
});
|
|
9367
9708
|
} catch (err) {
|
|
9368
|
-
|
|
9709
|
+
log33.warn({ err }, "Failed to send welcome message");
|
|
9369
9710
|
}
|
|
9370
9711
|
try {
|
|
9371
9712
|
await this.core.assistantManager.spawn("telegram", String(this.assistantTopicId));
|
|
9372
9713
|
} catch (err) {
|
|
9373
|
-
|
|
9714
|
+
log33.error({ err }, "Failed to spawn assistant");
|
|
9374
9715
|
}
|
|
9375
9716
|
}
|
|
9376
9717
|
/**
|
|
@@ -9384,7 +9725,7 @@ ${p}` : p;
|
|
|
9384
9725
|
} catch (err) {
|
|
9385
9726
|
if (attempt === maxRetries) throw err;
|
|
9386
9727
|
const delay = baseDelayMs * Math.pow(2, attempt - 1);
|
|
9387
|
-
|
|
9728
|
+
log33.warn(
|
|
9388
9729
|
{ err, attempt, maxRetries, delayMs: delay, operation: label },
|
|
9389
9730
|
`${label} failed, retrying in ${delay}ms`
|
|
9390
9731
|
);
|
|
@@ -9404,7 +9745,7 @@ ${p}` : p;
|
|
|
9404
9745
|
}),
|
|
9405
9746
|
"setMyCommands"
|
|
9406
9747
|
).catch((err) => {
|
|
9407
|
-
|
|
9748
|
+
log33.warn({ err }, "Failed to register Telegram commands after retries (non-critical)");
|
|
9408
9749
|
});
|
|
9409
9750
|
}
|
|
9410
9751
|
async stop() {
|
|
@@ -9412,9 +9753,17 @@ ${p}` : p;
|
|
|
9412
9753
|
tracker.destroy();
|
|
9413
9754
|
}
|
|
9414
9755
|
this.sessionTrackers.clear();
|
|
9756
|
+
if (this._threadReadyHandler) {
|
|
9757
|
+
this.core.eventBus.off("session:threadReady", this._threadReadyHandler);
|
|
9758
|
+
this._threadReadyHandler = void 0;
|
|
9759
|
+
}
|
|
9760
|
+
if (this._configChangedHandler) {
|
|
9761
|
+
this.core.eventBus.off("session:configChanged", this._configChangedHandler);
|
|
9762
|
+
this._configChangedHandler = void 0;
|
|
9763
|
+
}
|
|
9415
9764
|
this.sendQueue.clear();
|
|
9416
9765
|
await this.bot.stop();
|
|
9417
|
-
|
|
9766
|
+
log33.info("Telegram bot stopped");
|
|
9418
9767
|
}
|
|
9419
9768
|
// --- CommandRegistry response rendering ---
|
|
9420
9769
|
async renderCommandResponse(response, chatId, topicId) {
|
|
@@ -9531,7 +9880,7 @@ ${lines.join("\n")}`;
|
|
|
9531
9880
|
threadId: String(threadId),
|
|
9532
9881
|
userId: String(ctx.from.id),
|
|
9533
9882
|
text: forwardText
|
|
9534
|
-
}).catch((err) =>
|
|
9883
|
+
}).catch((err) => log33.error({ err }, "handleMessage error"));
|
|
9535
9884
|
});
|
|
9536
9885
|
this.bot.on("message:photo", async (ctx) => {
|
|
9537
9886
|
const threadId = ctx.message.message_thread_id;
|
|
@@ -9620,7 +9969,7 @@ ${lines.join("\n")}`;
|
|
|
9620
9969
|
if (session.archiving) return;
|
|
9621
9970
|
const threadId = Number(session.threadId);
|
|
9622
9971
|
if (!threadId || isNaN(threadId)) {
|
|
9623
|
-
|
|
9972
|
+
log33.warn(
|
|
9624
9973
|
{ sessionId, threadId: session.threadId },
|
|
9625
9974
|
"Session has no valid threadId, skipping message"
|
|
9626
9975
|
);
|
|
@@ -9636,7 +9985,7 @@ ${lines.join("\n")}`;
|
|
|
9636
9985
|
this._sessionThreadIds.delete(sessionId);
|
|
9637
9986
|
}
|
|
9638
9987
|
}).catch((err) => {
|
|
9639
|
-
|
|
9988
|
+
log33.warn({ err, sessionId }, "Dispatch queue error");
|
|
9640
9989
|
});
|
|
9641
9990
|
this._dispatchQueues.set(sessionId, next);
|
|
9642
9991
|
await next;
|
|
@@ -9758,7 +10107,7 @@ ${lines.join("\n")}`;
|
|
|
9758
10107
|
);
|
|
9759
10108
|
usageMsgId = result?.message_id;
|
|
9760
10109
|
} catch (err) {
|
|
9761
|
-
|
|
10110
|
+
log33.warn({ err, sessionId }, "Failed to send usage message");
|
|
9762
10111
|
}
|
|
9763
10112
|
if (this.notificationTopicId && sessionId !== this.core.assistantManager?.get("telegram")?.id) {
|
|
9764
10113
|
const sess = this.core.sessionManager.getSession(sessionId);
|
|
@@ -9786,7 +10135,7 @@ Task completed.
|
|
|
9786
10135
|
if (!content.attachment) return;
|
|
9787
10136
|
const { attachment } = content;
|
|
9788
10137
|
if (attachment.size > 50 * 1024 * 1024) {
|
|
9789
|
-
|
|
10138
|
+
log33.warn(
|
|
9790
10139
|
{
|
|
9791
10140
|
sessionId,
|
|
9792
10141
|
fileName: attachment.fileName,
|
|
@@ -9830,7 +10179,7 @@ Task completed.
|
|
|
9830
10179
|
);
|
|
9831
10180
|
}
|
|
9832
10181
|
} catch (err) {
|
|
9833
|
-
|
|
10182
|
+
log33.error(
|
|
9834
10183
|
{ err, sessionId, fileName: attachment.fileName },
|
|
9835
10184
|
"Failed to send attachment"
|
|
9836
10185
|
);
|
|
@@ -9929,7 +10278,7 @@ Task completed.
|
|
|
9929
10278
|
}
|
|
9930
10279
|
async sendPermissionRequest(sessionId, request) {
|
|
9931
10280
|
this.getTracer(sessionId)?.log("telegram", { action: "permission:send", sessionId, requestId: request.id, description: request.description });
|
|
9932
|
-
|
|
10281
|
+
log33.info({ sessionId, requestId: request.id }, "Permission request sent");
|
|
9933
10282
|
const session = this.core.sessionManager.getSession(sessionId);
|
|
9934
10283
|
if (!session) return;
|
|
9935
10284
|
await this.sendQueue.enqueue(
|
|
@@ -9939,7 +10288,7 @@ Task completed.
|
|
|
9939
10288
|
async sendNotification(notification) {
|
|
9940
10289
|
this.getTracer(notification.sessionId)?.log("telegram", { action: "notification:send", sessionId: notification.sessionId, type: notification.type });
|
|
9941
10290
|
if (notification.sessionId === this.core.assistantManager?.get("telegram")?.id) return;
|
|
9942
|
-
|
|
10291
|
+
log33.info(
|
|
9943
10292
|
{ sessionId: notification.sessionId, type: notification.type },
|
|
9944
10293
|
"Notification sent"
|
|
9945
10294
|
);
|
|
@@ -9978,7 +10327,7 @@ Task completed.
|
|
|
9978
10327
|
}
|
|
9979
10328
|
async createSessionThread(sessionId, name) {
|
|
9980
10329
|
this.getTracer(sessionId)?.log("telegram", { action: "thread:create", sessionId, name });
|
|
9981
|
-
|
|
10330
|
+
log33.info({ sessionId, name }, "Session topic created");
|
|
9982
10331
|
return String(
|
|
9983
10332
|
await createSessionTopic(this.bot, this.telegramConfig.chatId, name)
|
|
9984
10333
|
);
|
|
@@ -9989,7 +10338,7 @@ Task completed.
|
|
|
9989
10338
|
if (!session) return;
|
|
9990
10339
|
const threadId = Number(session.threadId);
|
|
9991
10340
|
if (!threadId) {
|
|
9992
|
-
|
|
10341
|
+
log33.debug({ sessionId, newName }, "Cannot rename thread \u2014 threadId not set yet");
|
|
9993
10342
|
return;
|
|
9994
10343
|
}
|
|
9995
10344
|
await renameSessionTopic(
|
|
@@ -10008,7 +10357,7 @@ Task completed.
|
|
|
10008
10357
|
try {
|
|
10009
10358
|
await this.bot.api.deleteForumTopic(this.telegramConfig.chatId, topicId);
|
|
10010
10359
|
} catch (err) {
|
|
10011
|
-
|
|
10360
|
+
log33.warn(
|
|
10012
10361
|
{ err, sessionId, topicId },
|
|
10013
10362
|
"Failed to delete forum topic (may already be deleted)"
|
|
10014
10363
|
);
|
|
@@ -10052,7 +10401,7 @@ Task completed.
|
|
|
10052
10401
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
10053
10402
|
return { buffer, filePath: file.file_path };
|
|
10054
10403
|
} catch (err) {
|
|
10055
|
-
|
|
10404
|
+
log33.error({ err }, "Failed to download file from Telegram");
|
|
10056
10405
|
return null;
|
|
10057
10406
|
}
|
|
10058
10407
|
}
|
|
@@ -10073,7 +10422,7 @@ Task completed.
|
|
|
10073
10422
|
try {
|
|
10074
10423
|
buffer = await this.fileService.convertOggToWav(buffer);
|
|
10075
10424
|
} catch (err) {
|
|
10076
|
-
|
|
10425
|
+
log33.warn({ err }, "OGG\u2192WAV conversion failed, saving original OGG");
|
|
10077
10426
|
fileName = "voice.ogg";
|
|
10078
10427
|
mimeType = "audio/ogg";
|
|
10079
10428
|
originalFilePath = void 0;
|
|
@@ -10105,7 +10454,7 @@ Task completed.
|
|
|
10105
10454
|
userId: String(userId),
|
|
10106
10455
|
text: text3,
|
|
10107
10456
|
attachments: [att]
|
|
10108
|
-
}).catch((err) =>
|
|
10457
|
+
}).catch((err) => log33.error({ err }, "handleMessage error"));
|
|
10109
10458
|
}
|
|
10110
10459
|
async cleanupSkillCommands(sessionId) {
|
|
10111
10460
|
this._pendingSkillCommands.delete(sessionId);
|
|
@@ -10230,12 +10579,150 @@ var StderrCapture = class {
|
|
|
10230
10579
|
// src/core/index.ts
|
|
10231
10580
|
init_config();
|
|
10232
10581
|
|
|
10233
|
-
// src/core/agents/agent-instance.ts
|
|
10234
|
-
import { spawn as spawn2, execFileSync } from "child_process";
|
|
10235
|
-
import { Transform } from "stream";
|
|
10236
|
-
import
|
|
10237
|
-
import
|
|
10238
|
-
import { ClientSideConnection, ndJsonStream } from "@agentclientprotocol/sdk";
|
|
10582
|
+
// src/core/agents/agent-instance.ts
|
|
10583
|
+
import { spawn as spawn2, execFileSync } from "child_process";
|
|
10584
|
+
import { Transform } from "stream";
|
|
10585
|
+
import fs9 from "fs";
|
|
10586
|
+
import path8 from "path";
|
|
10587
|
+
import { ClientSideConnection, ndJsonStream } from "@agentclientprotocol/sdk";
|
|
10588
|
+
|
|
10589
|
+
// src/core/security/path-guard.ts
|
|
10590
|
+
import fs6 from "fs";
|
|
10591
|
+
import path6 from "path";
|
|
10592
|
+
import ignore from "ignore";
|
|
10593
|
+
var DEFAULT_DENY_PATTERNS = [
|
|
10594
|
+
".env",
|
|
10595
|
+
".env.*",
|
|
10596
|
+
"*.key",
|
|
10597
|
+
"*.pem",
|
|
10598
|
+
".ssh/",
|
|
10599
|
+
".aws/",
|
|
10600
|
+
".openacp/",
|
|
10601
|
+
"**/credentials*",
|
|
10602
|
+
"**/secrets*",
|
|
10603
|
+
"**/*.secret"
|
|
10604
|
+
];
|
|
10605
|
+
var PathGuard = class {
|
|
10606
|
+
cwd;
|
|
10607
|
+
allowedPaths;
|
|
10608
|
+
ig;
|
|
10609
|
+
constructor(options) {
|
|
10610
|
+
try {
|
|
10611
|
+
this.cwd = fs6.realpathSync(path6.resolve(options.cwd));
|
|
10612
|
+
} catch {
|
|
10613
|
+
this.cwd = path6.resolve(options.cwd);
|
|
10614
|
+
}
|
|
10615
|
+
this.allowedPaths = options.allowedPaths.map((p) => {
|
|
10616
|
+
try {
|
|
10617
|
+
return fs6.realpathSync(path6.resolve(p));
|
|
10618
|
+
} catch {
|
|
10619
|
+
return path6.resolve(p);
|
|
10620
|
+
}
|
|
10621
|
+
});
|
|
10622
|
+
this.ig = ignore();
|
|
10623
|
+
this.ig.add(DEFAULT_DENY_PATTERNS);
|
|
10624
|
+
if (options.ignorePatterns.length > 0) {
|
|
10625
|
+
this.ig.add(options.ignorePatterns);
|
|
10626
|
+
}
|
|
10627
|
+
}
|
|
10628
|
+
validatePath(targetPath, operation) {
|
|
10629
|
+
const resolved = path6.resolve(targetPath);
|
|
10630
|
+
let realPath;
|
|
10631
|
+
try {
|
|
10632
|
+
realPath = fs6.realpathSync(resolved);
|
|
10633
|
+
} catch {
|
|
10634
|
+
realPath = resolved;
|
|
10635
|
+
}
|
|
10636
|
+
if (operation === "write" && path6.basename(realPath) === ".openacpignore") {
|
|
10637
|
+
return { allowed: false, reason: "Cannot write to .openacpignore" };
|
|
10638
|
+
}
|
|
10639
|
+
const isWithinCwd = realPath === this.cwd || realPath.startsWith(this.cwd + path6.sep);
|
|
10640
|
+
const isWithinAllowed = this.allowedPaths.some(
|
|
10641
|
+
(ap) => realPath === ap || realPath.startsWith(ap + path6.sep)
|
|
10642
|
+
);
|
|
10643
|
+
if (!isWithinCwd && !isWithinAllowed) {
|
|
10644
|
+
return {
|
|
10645
|
+
allowed: false,
|
|
10646
|
+
reason: `Path is outside workspace boundary: ${realPath}`
|
|
10647
|
+
};
|
|
10648
|
+
}
|
|
10649
|
+
if (isWithinCwd) {
|
|
10650
|
+
const relativePath = path6.relative(this.cwd, realPath);
|
|
10651
|
+
if (relativePath === ".openacpignore") {
|
|
10652
|
+
return { allowed: true, reason: "" };
|
|
10653
|
+
}
|
|
10654
|
+
if (relativePath && this.ig.ignores(relativePath)) {
|
|
10655
|
+
return {
|
|
10656
|
+
allowed: false,
|
|
10657
|
+
reason: `Path matches ignore pattern: ${relativePath}`
|
|
10658
|
+
};
|
|
10659
|
+
}
|
|
10660
|
+
}
|
|
10661
|
+
return { allowed: true, reason: "" };
|
|
10662
|
+
}
|
|
10663
|
+
addAllowedPath(p) {
|
|
10664
|
+
try {
|
|
10665
|
+
this.allowedPaths.push(fs6.realpathSync(path6.resolve(p)));
|
|
10666
|
+
} catch {
|
|
10667
|
+
this.allowedPaths.push(path6.resolve(p));
|
|
10668
|
+
}
|
|
10669
|
+
}
|
|
10670
|
+
static loadIgnoreFile(cwd) {
|
|
10671
|
+
const ignorePath = path6.join(cwd, ".openacpignore");
|
|
10672
|
+
try {
|
|
10673
|
+
const content = fs6.readFileSync(ignorePath, "utf-8");
|
|
10674
|
+
return content.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
|
|
10675
|
+
} catch {
|
|
10676
|
+
return [];
|
|
10677
|
+
}
|
|
10678
|
+
}
|
|
10679
|
+
};
|
|
10680
|
+
|
|
10681
|
+
// src/core/security/env-filter.ts
|
|
10682
|
+
var DEFAULT_ENV_WHITELIST = [
|
|
10683
|
+
"PATH",
|
|
10684
|
+
"HOME",
|
|
10685
|
+
"SHELL",
|
|
10686
|
+
"LANG",
|
|
10687
|
+
"LC_*",
|
|
10688
|
+
"TERM",
|
|
10689
|
+
"USER",
|
|
10690
|
+
"LOGNAME",
|
|
10691
|
+
"TMPDIR",
|
|
10692
|
+
"XDG_*",
|
|
10693
|
+
"NODE_ENV",
|
|
10694
|
+
"EDITOR",
|
|
10695
|
+
// Git
|
|
10696
|
+
"GIT_*",
|
|
10697
|
+
"SSH_AUTH_SOCK",
|
|
10698
|
+
"SSH_AGENT_PID",
|
|
10699
|
+
// Terminal rendering
|
|
10700
|
+
"COLORTERM",
|
|
10701
|
+
"FORCE_COLOR",
|
|
10702
|
+
"NO_COLOR",
|
|
10703
|
+
"TERM_PROGRAM",
|
|
10704
|
+
"HOSTNAME"
|
|
10705
|
+
];
|
|
10706
|
+
function matchesPattern(key, pattern) {
|
|
10707
|
+
if (pattern.endsWith("*")) {
|
|
10708
|
+
return key.startsWith(pattern.slice(0, -1));
|
|
10709
|
+
}
|
|
10710
|
+
return key === pattern;
|
|
10711
|
+
}
|
|
10712
|
+
function filterEnv(processEnv, agentEnv, whitelist) {
|
|
10713
|
+
const patterns = whitelist ?? DEFAULT_ENV_WHITELIST;
|
|
10714
|
+
const result = {};
|
|
10715
|
+
for (const [key, value] of Object.entries(processEnv)) {
|
|
10716
|
+
if (value === void 0) continue;
|
|
10717
|
+
if (patterns.some((p) => matchesPattern(key, p))) {
|
|
10718
|
+
result[key] = value;
|
|
10719
|
+
}
|
|
10720
|
+
}
|
|
10721
|
+
if (agentEnv) {
|
|
10722
|
+
Object.assign(result, agentEnv);
|
|
10723
|
+
}
|
|
10724
|
+
return result;
|
|
10725
|
+
}
|
|
10239
10726
|
|
|
10240
10727
|
// src/core/utils/typed-emitter.ts
|
|
10241
10728
|
var TypedEmitter = class {
|
|
@@ -10357,7 +10844,7 @@ var TerminalManager = class {
|
|
|
10357
10844
|
}
|
|
10358
10845
|
const childProcess = spawn(termCommand, args, {
|
|
10359
10846
|
cwd: termCwd,
|
|
10360
|
-
env:
|
|
10847
|
+
env: filterEnv(process.env, env),
|
|
10361
10848
|
shell: false
|
|
10362
10849
|
});
|
|
10363
10850
|
const state = {
|
|
@@ -10466,24 +10953,24 @@ var McpManager = class {
|
|
|
10466
10953
|
};
|
|
10467
10954
|
|
|
10468
10955
|
// src/core/utils/debug-tracer.ts
|
|
10469
|
-
import
|
|
10470
|
-
import
|
|
10956
|
+
import fs8 from "fs";
|
|
10957
|
+
import path7 from "path";
|
|
10471
10958
|
var DEBUG_ENABLED = process.env.OPENACP_DEBUG === "true" || process.env.OPENACP_DEBUG === "1";
|
|
10472
10959
|
var DebugTracer = class {
|
|
10473
10960
|
constructor(sessionId, workingDirectory) {
|
|
10474
10961
|
this.sessionId = sessionId;
|
|
10475
10962
|
this.workingDirectory = workingDirectory;
|
|
10476
|
-
this.logDir =
|
|
10963
|
+
this.logDir = path7.join(workingDirectory, ".log");
|
|
10477
10964
|
}
|
|
10478
10965
|
dirCreated = false;
|
|
10479
10966
|
logDir;
|
|
10480
10967
|
log(layer, data) {
|
|
10481
10968
|
try {
|
|
10482
10969
|
if (!this.dirCreated) {
|
|
10483
|
-
|
|
10970
|
+
fs8.mkdirSync(this.logDir, { recursive: true });
|
|
10484
10971
|
this.dirCreated = true;
|
|
10485
10972
|
}
|
|
10486
|
-
const filePath =
|
|
10973
|
+
const filePath = path7.join(this.logDir, `${this.sessionId}_${layer}.jsonl`);
|
|
10487
10974
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
10488
10975
|
const line = JSON.stringify({ ts: Date.now(), ...data }, (_key, value) => {
|
|
10489
10976
|
if (typeof value === "object" && value !== null) {
|
|
@@ -10492,7 +10979,7 @@ var DebugTracer = class {
|
|
|
10492
10979
|
}
|
|
10493
10980
|
return value;
|
|
10494
10981
|
}) + "\n";
|
|
10495
|
-
|
|
10982
|
+
fs8.appendFileSync(filePath, line);
|
|
10496
10983
|
} catch {
|
|
10497
10984
|
}
|
|
10498
10985
|
}
|
|
@@ -10510,11 +10997,11 @@ init_log();
|
|
|
10510
10997
|
var log4 = createChildLogger({ module: "agent-instance" });
|
|
10511
10998
|
function findPackageRoot(startDir) {
|
|
10512
10999
|
let dir = startDir;
|
|
10513
|
-
while (dir !==
|
|
10514
|
-
if (
|
|
11000
|
+
while (dir !== path8.dirname(dir)) {
|
|
11001
|
+
if (fs9.existsSync(path8.join(dir, "package.json"))) {
|
|
10515
11002
|
return dir;
|
|
10516
11003
|
}
|
|
10517
|
-
dir =
|
|
11004
|
+
dir = path8.dirname(dir);
|
|
10518
11005
|
}
|
|
10519
11006
|
return startDir;
|
|
10520
11007
|
}
|
|
@@ -10526,26 +11013,26 @@ function resolveAgentCommand(cmd) {
|
|
|
10526
11013
|
}
|
|
10527
11014
|
for (const root of searchRoots) {
|
|
10528
11015
|
const packageDirs = [
|
|
10529
|
-
|
|
10530
|
-
|
|
11016
|
+
path8.resolve(root, "node_modules", "@zed-industries", cmd, "dist", "index.js"),
|
|
11017
|
+
path8.resolve(root, "node_modules", cmd, "dist", "index.js")
|
|
10531
11018
|
];
|
|
10532
11019
|
for (const jsPath of packageDirs) {
|
|
10533
|
-
if (
|
|
11020
|
+
if (fs9.existsSync(jsPath)) {
|
|
10534
11021
|
return { command: process.execPath, args: [jsPath] };
|
|
10535
11022
|
}
|
|
10536
11023
|
}
|
|
10537
11024
|
}
|
|
10538
11025
|
for (const root of searchRoots) {
|
|
10539
|
-
const localBin =
|
|
10540
|
-
if (
|
|
10541
|
-
const content =
|
|
11026
|
+
const localBin = path8.resolve(root, "node_modules", ".bin", cmd);
|
|
11027
|
+
if (fs9.existsSync(localBin)) {
|
|
11028
|
+
const content = fs9.readFileSync(localBin, "utf-8");
|
|
10542
11029
|
if (content.startsWith("#!/usr/bin/env node")) {
|
|
10543
11030
|
return { command: process.execPath, args: [localBin] };
|
|
10544
11031
|
}
|
|
10545
11032
|
const match = content.match(/"([^"]+\.js)"/);
|
|
10546
11033
|
if (match) {
|
|
10547
|
-
const target =
|
|
10548
|
-
if (
|
|
11034
|
+
const target = path8.resolve(path8.dirname(localBin), match[1]);
|
|
11035
|
+
if (fs9.existsSync(target)) {
|
|
10549
11036
|
return { command: process.execPath, args: [target] };
|
|
10550
11037
|
}
|
|
10551
11038
|
}
|
|
@@ -10554,7 +11041,7 @@ function resolveAgentCommand(cmd) {
|
|
|
10554
11041
|
try {
|
|
10555
11042
|
const fullPath = execFileSync("which", [cmd], { encoding: "utf-8" }).trim();
|
|
10556
11043
|
if (fullPath) {
|
|
10557
|
-
const content =
|
|
11044
|
+
const content = fs9.readFileSync(fullPath, "utf-8");
|
|
10558
11045
|
if (content.startsWith("#!/usr/bin/env node")) {
|
|
10559
11046
|
return { command: process.execPath, args: [fullPath] };
|
|
10560
11047
|
}
|
|
@@ -10570,6 +11057,7 @@ var AgentInstance = class _AgentInstance extends TypedEmitter {
|
|
|
10570
11057
|
terminalManager = new TerminalManager();
|
|
10571
11058
|
static mcpManager = new McpManager();
|
|
10572
11059
|
_destroying = false;
|
|
11060
|
+
pathGuard;
|
|
10573
11061
|
sessionId;
|
|
10574
11062
|
agentName;
|
|
10575
11063
|
promptCapabilities;
|
|
@@ -10578,6 +11066,10 @@ var AgentInstance = class _AgentInstance extends TypedEmitter {
|
|
|
10578
11066
|
initialSessionResponse;
|
|
10579
11067
|
middlewareChain;
|
|
10580
11068
|
debugTracer = null;
|
|
11069
|
+
/** Allow external callers (e.g. SessionFactory) to whitelist additional read paths */
|
|
11070
|
+
addAllowedPath(p) {
|
|
11071
|
+
this.pathGuard.addAllowedPath(p);
|
|
11072
|
+
}
|
|
10581
11073
|
// Callback — set by core when wiring events
|
|
10582
11074
|
onPermissionRequest = async () => "";
|
|
10583
11075
|
constructor(agentName) {
|
|
@@ -10595,13 +11087,24 @@ var AgentInstance = class _AgentInstance extends TypedEmitter {
|
|
|
10595
11087
|
},
|
|
10596
11088
|
"Resolved agent command"
|
|
10597
11089
|
);
|
|
11090
|
+
const ignorePatterns = PathGuard.loadIgnoreFile(workingDirectory);
|
|
11091
|
+
instance.pathGuard = new PathGuard({
|
|
11092
|
+
cwd: workingDirectory,
|
|
11093
|
+
// allowedPaths is wired from workspace.security.allowedPaths config;
|
|
11094
|
+
// spawnSubprocess would need to receive a SecurityConfig param to use it.
|
|
11095
|
+
// Tracked as follow-up: pass workspace security config through spawn/resume call chain.
|
|
11096
|
+
allowedPaths: [],
|
|
11097
|
+
ignorePatterns
|
|
11098
|
+
});
|
|
10598
11099
|
instance.child = spawn2(
|
|
10599
11100
|
resolved.command,
|
|
10600
11101
|
[...resolved.args, ...agentDef.args],
|
|
10601
11102
|
{
|
|
10602
11103
|
stdio: ["pipe", "pipe", "pipe"],
|
|
10603
11104
|
cwd: workingDirectory,
|
|
10604
|
-
|
|
11105
|
+
// envWhitelist from workspace.security.envWhitelist config would extend DEFAULT_ENV_WHITELIST.
|
|
11106
|
+
// Tracked as follow-up: pass workspace security config through spawn/resume call chain.
|
|
11107
|
+
env: filterEnv(process.env, agentDef.env)
|
|
10605
11108
|
}
|
|
10606
11109
|
);
|
|
10607
11110
|
await new Promise((resolve6, reject) => {
|
|
@@ -10912,6 +11415,10 @@ ${stderr}`
|
|
|
10912
11415
|
// ── File operations ──────────────────────────────────────────────────
|
|
10913
11416
|
async readTextFile(params) {
|
|
10914
11417
|
const p = params;
|
|
11418
|
+
const pathCheck = self.pathGuard.validatePath(p.path, "read");
|
|
11419
|
+
if (!pathCheck.allowed) {
|
|
11420
|
+
throw new Error(`[Access denied] ${pathCheck.reason}`);
|
|
11421
|
+
}
|
|
10915
11422
|
if (self.middlewareChain) {
|
|
10916
11423
|
const result = await self.middlewareChain.execute("fs:beforeRead", { sessionId: self.sessionId, path: p.path, line: p.line, limit: p.limit }, async (r) => r);
|
|
10917
11424
|
if (!result) return { content: "" };
|
|
@@ -10926,14 +11433,18 @@ ${stderr}`
|
|
|
10926
11433
|
async writeTextFile(params) {
|
|
10927
11434
|
let writePath = params.path;
|
|
10928
11435
|
let writeContent = params.content;
|
|
11436
|
+
const pathCheck = self.pathGuard.validatePath(writePath, "write");
|
|
11437
|
+
if (!pathCheck.allowed) {
|
|
11438
|
+
throw new Error(`[Access denied] ${pathCheck.reason}`);
|
|
11439
|
+
}
|
|
10929
11440
|
if (self.middlewareChain) {
|
|
10930
11441
|
const result = await self.middlewareChain.execute("fs:beforeWrite", { sessionId: self.sessionId, path: writePath, content: writeContent }, async (r) => r);
|
|
10931
11442
|
if (!result) return {};
|
|
10932
11443
|
writePath = result.path;
|
|
10933
11444
|
writeContent = result.content;
|
|
10934
11445
|
}
|
|
10935
|
-
await
|
|
10936
|
-
await
|
|
11446
|
+
await fs9.promises.mkdir(path8.dirname(writePath), { recursive: true });
|
|
11447
|
+
await fs9.promises.writeFile(writePath, writeContent, "utf-8");
|
|
10937
11448
|
return {};
|
|
10938
11449
|
},
|
|
10939
11450
|
// ── Terminal operations (delegated to TerminalManager) ─────────────
|
|
@@ -11008,10 +11519,24 @@ ${stderr}`
|
|
|
11008
11519
|
for (const att of attachments ?? []) {
|
|
11009
11520
|
const tooLarge = att.size > 10 * 1024 * 1024;
|
|
11010
11521
|
if (att.type === "image" && this.promptCapabilities?.image && !tooLarge && SUPPORTED_IMAGE_MIMES.has(att.mimeType)) {
|
|
11011
|
-
const
|
|
11522
|
+
const attCheck = this.pathGuard.validatePath(att.filePath, "read");
|
|
11523
|
+
if (!attCheck.allowed) {
|
|
11524
|
+
contentBlocks[0].text += `
|
|
11525
|
+
|
|
11526
|
+
[Attachment access denied: ${attCheck.reason}]`;
|
|
11527
|
+
continue;
|
|
11528
|
+
}
|
|
11529
|
+
const data = await fs9.promises.readFile(att.filePath);
|
|
11012
11530
|
contentBlocks.push({ type: "image", data: data.toString("base64"), mimeType: att.mimeType });
|
|
11013
11531
|
} else if (att.type === "audio" && this.promptCapabilities?.audio && !tooLarge) {
|
|
11014
|
-
const
|
|
11532
|
+
const attCheck = this.pathGuard.validatePath(att.filePath, "read");
|
|
11533
|
+
if (!attCheck.allowed) {
|
|
11534
|
+
contentBlocks[0].text += `
|
|
11535
|
+
|
|
11536
|
+
[Attachment access denied: ${attCheck.reason}]`;
|
|
11537
|
+
continue;
|
|
11538
|
+
}
|
|
11539
|
+
const data = await fs9.promises.readFile(att.filePath);
|
|
11015
11540
|
contentBlocks.push({ type: "audio", data: data.toString("base64"), mimeType: att.mimeType });
|
|
11016
11541
|
} else {
|
|
11017
11542
|
if ((att.type === "image" || att.type === "audio") && !tooLarge) {
|
|
@@ -11217,7 +11742,7 @@ var PermissionGate = class {
|
|
|
11217
11742
|
|
|
11218
11743
|
// src/core/sessions/session.ts
|
|
11219
11744
|
init_log();
|
|
11220
|
-
import * as
|
|
11745
|
+
import * as fs10 from "fs";
|
|
11221
11746
|
var moduleLog = createChildLogger({ module: "session" });
|
|
11222
11747
|
var TTS_PROMPT_INSTRUCTION = `
|
|
11223
11748
|
|
|
@@ -11440,7 +11965,7 @@ ${text3}`;
|
|
|
11440
11965
|
try {
|
|
11441
11966
|
const audioPath = att.originalFilePath || att.filePath;
|
|
11442
11967
|
const audioMime = att.originalFilePath ? "audio/ogg" : att.mimeType;
|
|
11443
|
-
const audioBuffer = await
|
|
11968
|
+
const audioBuffer = await fs10.promises.readFile(audioPath);
|
|
11444
11969
|
const result = await this.speechService.transcribe(audioBuffer, audioMime);
|
|
11445
11970
|
this.log.info({ provider: "stt", duration: result.duration }, "Voice transcribed");
|
|
11446
11971
|
this.emit("agent_event", {
|
|
@@ -11627,6 +12152,9 @@ ${result.text}` : result.text;
|
|
|
11627
12152
|
}
|
|
11628
12153
|
};
|
|
11629
12154
|
|
|
12155
|
+
// src/core/message-transformer.ts
|
|
12156
|
+
import * as path9 from "path";
|
|
12157
|
+
|
|
11630
12158
|
// src/core/utils/extract-file-info.ts
|
|
11631
12159
|
function extractFileInfo(name, kind, content, rawInput, meta) {
|
|
11632
12160
|
if (kind && !["read", "edit", "write"].includes(kind)) return null;
|
|
@@ -11752,6 +12280,41 @@ function parseContent(content) {
|
|
|
11752
12280
|
// src/core/message-transformer.ts
|
|
11753
12281
|
init_log();
|
|
11754
12282
|
var log5 = createChildLogger({ module: "message-transformer" });
|
|
12283
|
+
var BINARY_VIEWER_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
12284
|
+
".wav",
|
|
12285
|
+
".ogg",
|
|
12286
|
+
".mp3",
|
|
12287
|
+
".m4a",
|
|
12288
|
+
".aac",
|
|
12289
|
+
".flac",
|
|
12290
|
+
".opus",
|
|
12291
|
+
".weba",
|
|
12292
|
+
".mp4",
|
|
12293
|
+
".avi",
|
|
12294
|
+
".mov",
|
|
12295
|
+
".webm",
|
|
12296
|
+
".mkv",
|
|
12297
|
+
".jpg",
|
|
12298
|
+
".jpeg",
|
|
12299
|
+
".png",
|
|
12300
|
+
".gif",
|
|
12301
|
+
".webp",
|
|
12302
|
+
".bmp",
|
|
12303
|
+
".ico",
|
|
12304
|
+
".svg",
|
|
12305
|
+
".pdf",
|
|
12306
|
+
".zip",
|
|
12307
|
+
".gz",
|
|
12308
|
+
".tar",
|
|
12309
|
+
".7z",
|
|
12310
|
+
".rar",
|
|
12311
|
+
".bin",
|
|
12312
|
+
".exe",
|
|
12313
|
+
".dll",
|
|
12314
|
+
".so",
|
|
12315
|
+
".dylib",
|
|
12316
|
+
".wasm"
|
|
12317
|
+
]);
|
|
11755
12318
|
function computeLineDiff(oldStr, newStr) {
|
|
11756
12319
|
const oldLines = oldStr ? oldStr.split("\n") : [];
|
|
11757
12320
|
const newLines = newStr ? newStr.split("\n") : [];
|
|
@@ -11948,6 +12511,11 @@ var MessageTransformer = class {
|
|
|
11948
12511
|
);
|
|
11949
12512
|
return;
|
|
11950
12513
|
}
|
|
12514
|
+
const fileExt = path9.extname(fileInfo.filePath).toLowerCase();
|
|
12515
|
+
if (BINARY_VIEWER_EXTENSIONS.has(fileExt)) {
|
|
12516
|
+
log5.debug({ kind, filePath: fileInfo.filePath }, "enrichWithViewerLinks: skipping binary file");
|
|
12517
|
+
return;
|
|
12518
|
+
}
|
|
11951
12519
|
const publicUrl = this.tunnelService.getPublicUrl();
|
|
11952
12520
|
if (publicUrl.startsWith("http://localhost") || publicUrl.startsWith("http://127.0.0.1")) {
|
|
11953
12521
|
log5.debug({ kind, filePath: fileInfo.filePath }, "enrichWithViewerLinks: skipping (no public tunnel URL)");
|
|
@@ -12336,12 +12904,12 @@ var SessionBridge = class {
|
|
|
12336
12904
|
break;
|
|
12337
12905
|
case "image_content": {
|
|
12338
12906
|
if (this.deps.fileService) {
|
|
12339
|
-
const
|
|
12907
|
+
const fs34 = this.deps.fileService;
|
|
12340
12908
|
const sid = this.session.id;
|
|
12341
12909
|
const { data, mimeType } = event;
|
|
12342
12910
|
const buffer = Buffer.from(data, "base64");
|
|
12343
|
-
const ext =
|
|
12344
|
-
|
|
12911
|
+
const ext = fs34.extensionFromMime(mimeType);
|
|
12912
|
+
fs34.saveFile(sid, `agent-image${ext}`, buffer, mimeType).then((att) => {
|
|
12345
12913
|
this.sendMessage(sid, {
|
|
12346
12914
|
type: "attachment",
|
|
12347
12915
|
text: "",
|
|
@@ -12353,12 +12921,12 @@ var SessionBridge = class {
|
|
|
12353
12921
|
}
|
|
12354
12922
|
case "audio_content": {
|
|
12355
12923
|
if (this.deps.fileService) {
|
|
12356
|
-
const
|
|
12924
|
+
const fs34 = this.deps.fileService;
|
|
12357
12925
|
const sid = this.session.id;
|
|
12358
12926
|
const { data, mimeType } = event;
|
|
12359
12927
|
const buffer = Buffer.from(data, "base64");
|
|
12360
|
-
const ext =
|
|
12361
|
-
|
|
12928
|
+
const ext = fs34.extensionFromMime(mimeType);
|
|
12929
|
+
fs34.saveFile(sid, `agent-audio${ext}`, buffer, mimeType).then((att) => {
|
|
12362
12930
|
this.sendMessage(sid, {
|
|
12363
12931
|
type: "attachment",
|
|
12364
12932
|
text: "",
|
|
@@ -12445,18 +13013,8 @@ var SessionBridge = class {
|
|
|
12445
13013
|
this.emitAfterResolve(mw, permReq.id, optionId, "user", startTime);
|
|
12446
13014
|
return optionId;
|
|
12447
13015
|
}
|
|
12448
|
-
/** Check if a permission request should be auto-approved (
|
|
13016
|
+
/** Check if a permission request should be auto-approved (bypass mode only) */
|
|
12449
13017
|
checkAutoApprove(request) {
|
|
12450
|
-
if (request.description.toLowerCase().includes("openacp")) {
|
|
12451
|
-
const allowOption = request.options.find((o) => o.isAllow);
|
|
12452
|
-
if (allowOption) {
|
|
12453
|
-
log6.info(
|
|
12454
|
-
{ sessionId: this.session.id, requestId: request.id },
|
|
12455
|
-
"Auto-approving openacp command"
|
|
12456
|
-
);
|
|
12457
|
-
return allowOption.id;
|
|
12458
|
-
}
|
|
12459
|
-
}
|
|
12460
13018
|
const modeOption = this.session.getConfigByCategory("mode");
|
|
12461
13019
|
const isAgentBypass = modeOption && isPermissionBypass(
|
|
12462
13020
|
typeof modeOption.currentValue === "string" ? modeOption.currentValue : ""
|
|
@@ -12514,6 +13072,10 @@ var SessionFactory = class {
|
|
|
12514
13072
|
agentCatalog;
|
|
12515
13073
|
/** Injected by Core — needed for context-aware session creation */
|
|
12516
13074
|
getContextManager;
|
|
13075
|
+
/** Injected by Core — returns extra filesystem paths the agent is allowed to read.
|
|
13076
|
+
* Used to whitelist the file-service upload directory so agents can read attachments
|
|
13077
|
+
* saved outside the workspace (e.g. ~/.openacp/instances/main/files/). */
|
|
13078
|
+
getAgentAllowedPaths;
|
|
12517
13079
|
get speechService() {
|
|
12518
13080
|
return typeof this.speechServiceAccessor === "function" ? this.speechServiceAccessor() : this.speechServiceAccessor;
|
|
12519
13081
|
}
|
|
@@ -12605,6 +13167,10 @@ var SessionFactory = class {
|
|
|
12605
13167
|
});
|
|
12606
13168
|
throw err;
|
|
12607
13169
|
}
|
|
13170
|
+
const extraPaths = this.getAgentAllowedPaths?.() ?? [];
|
|
13171
|
+
for (const p of extraPaths) {
|
|
13172
|
+
agentInstance.addAllowedPath(p);
|
|
13173
|
+
}
|
|
12608
13174
|
agentInstance.middlewareChain = this.middlewareChain;
|
|
12609
13175
|
const session = new Session({
|
|
12610
13176
|
id: createParams.existingSessionId,
|
|
@@ -12821,13 +13387,13 @@ var SessionFactory = class {
|
|
|
12821
13387
|
};
|
|
12822
13388
|
|
|
12823
13389
|
// src/core/core.ts
|
|
12824
|
-
import
|
|
13390
|
+
import path17 from "path";
|
|
12825
13391
|
import os8 from "os";
|
|
12826
13392
|
|
|
12827
13393
|
// src/core/sessions/session-store.ts
|
|
12828
13394
|
init_log();
|
|
12829
|
-
import
|
|
12830
|
-
import
|
|
13395
|
+
import fs11 from "fs";
|
|
13396
|
+
import path10 from "path";
|
|
12831
13397
|
var log8 = createChildLogger({ module: "session-store" });
|
|
12832
13398
|
var DEBOUNCE_MS = 2e3;
|
|
12833
13399
|
var JsonFileSessionStore = class {
|
|
@@ -12898,9 +13464,9 @@ var JsonFileSessionStore = class {
|
|
|
12898
13464
|
version: 1,
|
|
12899
13465
|
sessions: Object.fromEntries(this.records)
|
|
12900
13466
|
};
|
|
12901
|
-
const dir =
|
|
12902
|
-
if (!
|
|
12903
|
-
|
|
13467
|
+
const dir = path10.dirname(this.filePath);
|
|
13468
|
+
if (!fs11.existsSync(dir)) fs11.mkdirSync(dir, { recursive: true });
|
|
13469
|
+
fs11.writeFileSync(this.filePath, JSON.stringify(data, null, 2));
|
|
12904
13470
|
}
|
|
12905
13471
|
destroy() {
|
|
12906
13472
|
if (this.debounceTimer) clearTimeout(this.debounceTimer);
|
|
@@ -12913,10 +13479,10 @@ var JsonFileSessionStore = class {
|
|
|
12913
13479
|
}
|
|
12914
13480
|
}
|
|
12915
13481
|
load() {
|
|
12916
|
-
if (!
|
|
13482
|
+
if (!fs11.existsSync(this.filePath)) return;
|
|
12917
13483
|
try {
|
|
12918
13484
|
const raw = JSON.parse(
|
|
12919
|
-
|
|
13485
|
+
fs11.readFileSync(this.filePath, "utf-8")
|
|
12920
13486
|
);
|
|
12921
13487
|
if (raw.version !== 1) {
|
|
12922
13488
|
log8.warn(
|
|
@@ -12932,7 +13498,7 @@ var JsonFileSessionStore = class {
|
|
|
12932
13498
|
} catch (err) {
|
|
12933
13499
|
log8.error({ err }, "Failed to load session store, backing up corrupt file");
|
|
12934
13500
|
try {
|
|
12935
|
-
|
|
13501
|
+
fs11.renameSync(this.filePath, `${this.filePath}.bak`);
|
|
12936
13502
|
} catch {
|
|
12937
13503
|
}
|
|
12938
13504
|
}
|
|
@@ -13025,12 +13591,16 @@ var AgentSwitchHandler = class {
|
|
|
13025
13591
|
await switchAdapter.cleanupSessionState(session.id);
|
|
13026
13592
|
}
|
|
13027
13593
|
const fromAgentSessionId = session.agentSessionId;
|
|
13594
|
+
const fileService = this.deps.getService("file-service");
|
|
13028
13595
|
try {
|
|
13029
13596
|
await session.switchAgent(toAgent, async () => {
|
|
13030
13597
|
if (canResume) {
|
|
13031
|
-
|
|
13598
|
+
const instance = await agentManager.resume(toAgent, session.workingDirectory, lastEntry.agentSessionId);
|
|
13599
|
+
if (fileService) instance.addAllowedPath(fileService.baseDir);
|
|
13600
|
+
return instance;
|
|
13032
13601
|
} else {
|
|
13033
13602
|
const instance = await agentManager.spawn(toAgent, session.workingDirectory);
|
|
13603
|
+
if (fileService) instance.addAllowedPath(fileService.baseDir);
|
|
13034
13604
|
try {
|
|
13035
13605
|
const contextService = this.deps.getService("context");
|
|
13036
13606
|
if (contextService) {
|
|
@@ -13125,14 +13695,14 @@ var AgentSwitchHandler = class {
|
|
|
13125
13695
|
};
|
|
13126
13696
|
|
|
13127
13697
|
// src/core/agents/agent-catalog.ts
|
|
13128
|
-
import * as
|
|
13129
|
-
import * as
|
|
13698
|
+
import * as fs15 from "fs";
|
|
13699
|
+
import * as path14 from "path";
|
|
13130
13700
|
import * as os6 from "os";
|
|
13131
13701
|
|
|
13132
13702
|
// src/core/agents/agent-store.ts
|
|
13133
13703
|
init_log();
|
|
13134
|
-
import * as
|
|
13135
|
-
import * as
|
|
13704
|
+
import * as fs13 from "fs";
|
|
13705
|
+
import * as path12 from "path";
|
|
13136
13706
|
import * as os4 from "os";
|
|
13137
13707
|
import { z as z2 } from "zod";
|
|
13138
13708
|
var log10 = createChildLogger({ module: "agent-store" });
|
|
@@ -13156,15 +13726,15 @@ var AgentStore = class {
|
|
|
13156
13726
|
data = { version: 1, installed: {} };
|
|
13157
13727
|
filePath;
|
|
13158
13728
|
constructor(filePath) {
|
|
13159
|
-
this.filePath = filePath ??
|
|
13729
|
+
this.filePath = filePath ?? path12.join(os4.homedir(), ".openacp", "agents.json");
|
|
13160
13730
|
}
|
|
13161
13731
|
load() {
|
|
13162
|
-
if (!
|
|
13732
|
+
if (!fs13.existsSync(this.filePath)) {
|
|
13163
13733
|
this.data = { version: 1, installed: {} };
|
|
13164
13734
|
return;
|
|
13165
13735
|
}
|
|
13166
13736
|
try {
|
|
13167
|
-
const raw = JSON.parse(
|
|
13737
|
+
const raw = JSON.parse(fs13.readFileSync(this.filePath, "utf-8"));
|
|
13168
13738
|
const result = AgentStoreSchema.safeParse(raw);
|
|
13169
13739
|
if (result.success) {
|
|
13170
13740
|
this.data = result.data;
|
|
@@ -13178,7 +13748,7 @@ var AgentStore = class {
|
|
|
13178
13748
|
}
|
|
13179
13749
|
}
|
|
13180
13750
|
exists() {
|
|
13181
|
-
return
|
|
13751
|
+
return fs13.existsSync(this.filePath);
|
|
13182
13752
|
}
|
|
13183
13753
|
getInstalled() {
|
|
13184
13754
|
return this.data.installed;
|
|
@@ -13198,244 +13768,15 @@ var AgentStore = class {
|
|
|
13198
13768
|
return key in this.data.installed;
|
|
13199
13769
|
}
|
|
13200
13770
|
save() {
|
|
13201
|
-
|
|
13771
|
+
fs13.mkdirSync(path12.dirname(this.filePath), { recursive: true });
|
|
13202
13772
|
const tmpPath = this.filePath + ".tmp";
|
|
13203
|
-
|
|
13204
|
-
|
|
13773
|
+
fs13.writeFileSync(tmpPath, JSON.stringify(this.data, null, 2), { mode: 384 });
|
|
13774
|
+
fs13.renameSync(tmpPath, this.filePath);
|
|
13205
13775
|
}
|
|
13206
13776
|
};
|
|
13207
13777
|
|
|
13208
|
-
// src/core/agents/agent-installer.ts
|
|
13209
|
-
init_log();
|
|
13210
|
-
init_agent_dependencies();
|
|
13211
|
-
import * as fs13 from "fs";
|
|
13212
|
-
import * as path11 from "path";
|
|
13213
|
-
import * as os5 from "os";
|
|
13214
|
-
var log11 = createChildLogger({ module: "agent-installer" });
|
|
13215
|
-
var DEFAULT_AGENTS_DIR = path11.join(os5.homedir(), ".openacp", "agents");
|
|
13216
|
-
var ARCH_MAP = {
|
|
13217
|
-
arm64: "aarch64",
|
|
13218
|
-
x64: "x86_64"
|
|
13219
|
-
};
|
|
13220
|
-
var PLATFORM_MAP = {
|
|
13221
|
-
darwin: "darwin",
|
|
13222
|
-
linux: "linux",
|
|
13223
|
-
win32: "windows"
|
|
13224
|
-
};
|
|
13225
|
-
function getPlatformKey() {
|
|
13226
|
-
const platform2 = PLATFORM_MAP[process.platform] ?? process.platform;
|
|
13227
|
-
const arch = ARCH_MAP[process.arch] ?? process.arch;
|
|
13228
|
-
return `${platform2}-${arch}`;
|
|
13229
|
-
}
|
|
13230
|
-
function resolveDistribution(agent) {
|
|
13231
|
-
const dist = agent.distribution;
|
|
13232
|
-
if (dist.npx) {
|
|
13233
|
-
return { type: "npx", package: dist.npx.package, args: dist.npx.args ?? [], env: dist.npx.env };
|
|
13234
|
-
}
|
|
13235
|
-
if (dist.uvx) {
|
|
13236
|
-
return { type: "uvx", package: dist.uvx.package, args: dist.uvx.args ?? [], env: dist.uvx.env };
|
|
13237
|
-
}
|
|
13238
|
-
if (dist.binary) {
|
|
13239
|
-
const platformKey = getPlatformKey();
|
|
13240
|
-
const target = dist.binary[platformKey];
|
|
13241
|
-
if (!target) return null;
|
|
13242
|
-
return { type: "binary", archive: target.archive, cmd: target.cmd, args: target.args ?? [], env: target.env };
|
|
13243
|
-
}
|
|
13244
|
-
return null;
|
|
13245
|
-
}
|
|
13246
|
-
function buildInstalledAgent(registryId, name, version, dist, binaryPath) {
|
|
13247
|
-
if (dist.type === "npx") {
|
|
13248
|
-
const npxPackage = stripPackageVersion(dist.package);
|
|
13249
|
-
return {
|
|
13250
|
-
registryId,
|
|
13251
|
-
name,
|
|
13252
|
-
version,
|
|
13253
|
-
distribution: "npx",
|
|
13254
|
-
command: "npx",
|
|
13255
|
-
args: [npxPackage, ...dist.args],
|
|
13256
|
-
env: dist.env ?? {},
|
|
13257
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13258
|
-
binaryPath: null
|
|
13259
|
-
};
|
|
13260
|
-
}
|
|
13261
|
-
if (dist.type === "uvx") {
|
|
13262
|
-
const uvxPackage = stripPythonPackageVersion(dist.package);
|
|
13263
|
-
return {
|
|
13264
|
-
registryId,
|
|
13265
|
-
name,
|
|
13266
|
-
version,
|
|
13267
|
-
distribution: "uvx",
|
|
13268
|
-
command: "uvx",
|
|
13269
|
-
args: [uvxPackage, ...dist.args],
|
|
13270
|
-
env: dist.env ?? {},
|
|
13271
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13272
|
-
binaryPath: null
|
|
13273
|
-
};
|
|
13274
|
-
}
|
|
13275
|
-
const absCmd = path11.resolve(binaryPath, dist.cmd);
|
|
13276
|
-
return {
|
|
13277
|
-
registryId,
|
|
13278
|
-
name,
|
|
13279
|
-
version,
|
|
13280
|
-
distribution: "binary",
|
|
13281
|
-
command: absCmd,
|
|
13282
|
-
args: dist.args,
|
|
13283
|
-
env: dist.env ?? {},
|
|
13284
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13285
|
-
binaryPath
|
|
13286
|
-
};
|
|
13287
|
-
}
|
|
13288
|
-
function stripPackageVersion(pkg) {
|
|
13289
|
-
if (pkg.startsWith("@")) {
|
|
13290
|
-
const afterScope = pkg.indexOf("/");
|
|
13291
|
-
if (afterScope === -1) return pkg;
|
|
13292
|
-
const versionAt = pkg.indexOf("@", afterScope + 1);
|
|
13293
|
-
return versionAt === -1 ? pkg : pkg.slice(0, versionAt);
|
|
13294
|
-
}
|
|
13295
|
-
const at = pkg.indexOf("@");
|
|
13296
|
-
return at === -1 ? pkg : pkg.slice(0, at);
|
|
13297
|
-
}
|
|
13298
|
-
function stripPythonPackageVersion(pkg) {
|
|
13299
|
-
const pyMatch = pkg.match(/^([^=@><!]+)/);
|
|
13300
|
-
if (pyMatch && pkg.includes("==")) return pyMatch[1];
|
|
13301
|
-
const at = pkg.indexOf("@");
|
|
13302
|
-
return at === -1 ? pkg : pkg.slice(0, at);
|
|
13303
|
-
}
|
|
13304
|
-
async function installAgent(agent, store, progress, agentsDir) {
|
|
13305
|
-
const agentKey = getAgentAlias(agent.id);
|
|
13306
|
-
await progress?.onStart(agent.id, agent.name);
|
|
13307
|
-
await progress?.onStep("Checking requirements...");
|
|
13308
|
-
const depResult = checkDependencies(agent.id);
|
|
13309
|
-
if (!depResult.available) {
|
|
13310
|
-
const hints = depResult.missing.map((m) => ` ${m.label}: ${m.installHint}`).join("\n");
|
|
13311
|
-
const msg = `${agent.name} needs some tools installed first:
|
|
13312
|
-
${hints}`;
|
|
13313
|
-
await progress?.onError(msg);
|
|
13314
|
-
return { ok: false, agentKey, error: msg };
|
|
13315
|
-
}
|
|
13316
|
-
const dist = resolveDistribution(agent);
|
|
13317
|
-
if (!dist) {
|
|
13318
|
-
const platformKey = getPlatformKey();
|
|
13319
|
-
const msg = `${agent.name} is not available for your system (${platformKey}). Check their website for other install options.`;
|
|
13320
|
-
await progress?.onError(msg);
|
|
13321
|
-
return { ok: false, agentKey, error: msg };
|
|
13322
|
-
}
|
|
13323
|
-
if (dist.type === "uvx" && !checkRuntimeAvailable("uvx")) {
|
|
13324
|
-
const msg = `${agent.name} requires Python's uvx tool.
|
|
13325
|
-
Install it with: pip install uv`;
|
|
13326
|
-
await progress?.onError(msg, "pip install uv");
|
|
13327
|
-
return { ok: false, agentKey, error: msg, hint: "pip install uv" };
|
|
13328
|
-
}
|
|
13329
|
-
let binaryPath;
|
|
13330
|
-
if (dist.type === "binary") {
|
|
13331
|
-
try {
|
|
13332
|
-
binaryPath = await downloadAndExtract(agent.id, dist.archive, progress, agentsDir);
|
|
13333
|
-
} catch (err) {
|
|
13334
|
-
const msg = `Failed to download ${agent.name}. Please try again or install manually.`;
|
|
13335
|
-
await progress?.onError(msg);
|
|
13336
|
-
return { ok: false, agentKey, error: msg };
|
|
13337
|
-
}
|
|
13338
|
-
} else {
|
|
13339
|
-
await progress?.onStep("Setting up... (will download on first use)");
|
|
13340
|
-
}
|
|
13341
|
-
const installed = buildInstalledAgent(agent.id, agent.name, agent.version, dist, binaryPath);
|
|
13342
|
-
store.addAgent(agentKey, installed);
|
|
13343
|
-
const setup = getAgentSetup(agent.id);
|
|
13344
|
-
await progress?.onSuccess(agent.name);
|
|
13345
|
-
return { ok: true, agentKey, setupSteps: setup?.setupSteps };
|
|
13346
|
-
}
|
|
13347
|
-
async function downloadAndExtract(agentId, archiveUrl, progress, agentsDir) {
|
|
13348
|
-
const destDir = path11.join(agentsDir ?? DEFAULT_AGENTS_DIR, agentId);
|
|
13349
|
-
fs13.mkdirSync(destDir, { recursive: true });
|
|
13350
|
-
await progress?.onStep("Downloading...");
|
|
13351
|
-
log11.info({ agentId, url: archiveUrl }, "Downloading agent binary");
|
|
13352
|
-
const response = await fetch(archiveUrl);
|
|
13353
|
-
if (!response.ok) {
|
|
13354
|
-
throw new Error(`Download failed: ${response.status} ${response.statusText}`);
|
|
13355
|
-
}
|
|
13356
|
-
const contentLength = Number(response.headers.get("content-length") || 0);
|
|
13357
|
-
const buffer = await readResponseWithProgress(response, contentLength, progress);
|
|
13358
|
-
await progress?.onStep("Extracting...");
|
|
13359
|
-
if (archiveUrl.endsWith(".zip")) {
|
|
13360
|
-
await extractZip(buffer, destDir);
|
|
13361
|
-
} else {
|
|
13362
|
-
await extractTarGz(buffer, destDir);
|
|
13363
|
-
}
|
|
13364
|
-
await progress?.onStep("Ready!");
|
|
13365
|
-
return destDir;
|
|
13366
|
-
}
|
|
13367
|
-
async function readResponseWithProgress(response, contentLength, progress) {
|
|
13368
|
-
if (!response.body || contentLength === 0) {
|
|
13369
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
13370
|
-
return Buffer.from(arrayBuffer);
|
|
13371
|
-
}
|
|
13372
|
-
const reader = response.body.getReader();
|
|
13373
|
-
const chunks = [];
|
|
13374
|
-
let received = 0;
|
|
13375
|
-
while (true) {
|
|
13376
|
-
const { done, value } = await reader.read();
|
|
13377
|
-
if (done) break;
|
|
13378
|
-
chunks.push(value);
|
|
13379
|
-
received += value.length;
|
|
13380
|
-
if (contentLength > 0) {
|
|
13381
|
-
await progress?.onDownloadProgress(Math.round(received / contentLength * 100));
|
|
13382
|
-
}
|
|
13383
|
-
}
|
|
13384
|
-
return Buffer.concat(chunks);
|
|
13385
|
-
}
|
|
13386
|
-
function validateExtractedPaths(destDir) {
|
|
13387
|
-
const realDest = fs13.realpathSync(destDir);
|
|
13388
|
-
const entries = fs13.readdirSync(destDir, { recursive: true, withFileTypes: true });
|
|
13389
|
-
for (const entry of entries) {
|
|
13390
|
-
const dirent = entry;
|
|
13391
|
-
const parentPath = dirent.parentPath ?? dirent.path ?? destDir;
|
|
13392
|
-
const fullPath = path11.join(parentPath, entry.name);
|
|
13393
|
-
let realPath;
|
|
13394
|
-
try {
|
|
13395
|
-
realPath = fs13.realpathSync(fullPath);
|
|
13396
|
-
} catch {
|
|
13397
|
-
const linkTarget = fs13.readlinkSync(fullPath);
|
|
13398
|
-
realPath = path11.resolve(path11.dirname(fullPath), linkTarget);
|
|
13399
|
-
}
|
|
13400
|
-
if (!realPath.startsWith(realDest + path11.sep) && realPath !== realDest) {
|
|
13401
|
-
fs13.rmSync(destDir, { recursive: true, force: true });
|
|
13402
|
-
throw new Error(`Archive contains unsafe path: ${entry.name}`);
|
|
13403
|
-
}
|
|
13404
|
-
}
|
|
13405
|
-
}
|
|
13406
|
-
async function extractTarGz(buffer, destDir) {
|
|
13407
|
-
const { execFileSync: execFileSync7 } = await import("child_process");
|
|
13408
|
-
const tmpFile = path11.join(destDir, "_archive.tar.gz");
|
|
13409
|
-
fs13.writeFileSync(tmpFile, buffer);
|
|
13410
|
-
try {
|
|
13411
|
-
execFileSync7("tar", ["xzf", tmpFile, "-C", destDir], { stdio: "pipe" });
|
|
13412
|
-
} finally {
|
|
13413
|
-
fs13.unlinkSync(tmpFile);
|
|
13414
|
-
}
|
|
13415
|
-
validateExtractedPaths(destDir);
|
|
13416
|
-
}
|
|
13417
|
-
async function extractZip(buffer, destDir) {
|
|
13418
|
-
const { execFileSync: execFileSync7 } = await import("child_process");
|
|
13419
|
-
const tmpFile = path11.join(destDir, "_archive.zip");
|
|
13420
|
-
fs13.writeFileSync(tmpFile, buffer);
|
|
13421
|
-
try {
|
|
13422
|
-
execFileSync7("unzip", ["-o", tmpFile, "-d", destDir], { stdio: "pipe" });
|
|
13423
|
-
} finally {
|
|
13424
|
-
fs13.unlinkSync(tmpFile);
|
|
13425
|
-
}
|
|
13426
|
-
validateExtractedPaths(destDir);
|
|
13427
|
-
}
|
|
13428
|
-
async function uninstallAgent(agentKey, store) {
|
|
13429
|
-
const agent = store.getAgent(agentKey);
|
|
13430
|
-
if (!agent) return;
|
|
13431
|
-
if (agent.binaryPath && fs13.existsSync(agent.binaryPath)) {
|
|
13432
|
-
fs13.rmSync(agent.binaryPath, { recursive: true, force: true });
|
|
13433
|
-
log11.info({ agentKey, binaryPath: agent.binaryPath }, "Deleted agent binary");
|
|
13434
|
-
}
|
|
13435
|
-
store.removeAgent(agentKey);
|
|
13436
|
-
}
|
|
13437
|
-
|
|
13438
13778
|
// src/core/agents/agent-catalog.ts
|
|
13779
|
+
init_agent_installer();
|
|
13439
13780
|
init_agent_dependencies();
|
|
13440
13781
|
init_log();
|
|
13441
13782
|
var log12 = createChildLogger({ module: "agent-catalog" });
|
|
@@ -13448,7 +13789,7 @@ var AgentCatalog = class {
|
|
|
13448
13789
|
agentsDir;
|
|
13449
13790
|
constructor(store, cachePath, agentsDir) {
|
|
13450
13791
|
this.store = store ?? new AgentStore();
|
|
13451
|
-
this.cachePath = cachePath ??
|
|
13792
|
+
this.cachePath = cachePath ?? path14.join(os6.homedir(), ".openacp", "registry-cache.json");
|
|
13452
13793
|
this.agentsDir = agentsDir;
|
|
13453
13794
|
}
|
|
13454
13795
|
load() {
|
|
@@ -13469,8 +13810,8 @@ var AgentCatalog = class {
|
|
|
13469
13810
|
ttlHours: DEFAULT_TTL_HOURS,
|
|
13470
13811
|
data
|
|
13471
13812
|
};
|
|
13472
|
-
|
|
13473
|
-
|
|
13813
|
+
fs15.mkdirSync(path14.dirname(this.cachePath), { recursive: true });
|
|
13814
|
+
fs15.writeFileSync(this.cachePath, JSON.stringify(cache, null, 2), { mode: 384 });
|
|
13474
13815
|
log12.info({ count: this.registryAgents.length }, "Registry updated");
|
|
13475
13816
|
} catch (err) {
|
|
13476
13817
|
log12.warn({ err }, "Failed to fetch registry, using cached data");
|
|
@@ -13569,6 +13910,14 @@ var AgentCatalog = class {
|
|
|
13569
13910
|
}
|
|
13570
13911
|
return installAgent(agent, this.store, progress, this.agentsDir);
|
|
13571
13912
|
}
|
|
13913
|
+
/**
|
|
13914
|
+
* Register an agent directly into the catalog store without going through
|
|
13915
|
+
* the registry installer. Used to pre-register bundled agents (e.g. Claude)
|
|
13916
|
+
* when their CLI dependency is not yet installed.
|
|
13917
|
+
*/
|
|
13918
|
+
registerFallbackAgent(key, data) {
|
|
13919
|
+
this.store.addAgent(key, data);
|
|
13920
|
+
}
|
|
13572
13921
|
async uninstall(key) {
|
|
13573
13922
|
if (!this.store.hasAgent(key)) {
|
|
13574
13923
|
return { ok: false, error: `"${key}" is not installed.` };
|
|
@@ -13630,9 +13979,9 @@ var AgentCatalog = class {
|
|
|
13630
13979
|
}
|
|
13631
13980
|
}
|
|
13632
13981
|
isCacheStale() {
|
|
13633
|
-
if (!
|
|
13982
|
+
if (!fs15.existsSync(this.cachePath)) return true;
|
|
13634
13983
|
try {
|
|
13635
|
-
const raw = JSON.parse(
|
|
13984
|
+
const raw = JSON.parse(fs15.readFileSync(this.cachePath, "utf-8"));
|
|
13636
13985
|
const fetchedAt = new Date(raw.fetchedAt).getTime();
|
|
13637
13986
|
const ttlMs = (raw.ttlHours ?? DEFAULT_TTL_HOURS) * 60 * 60 * 1e3;
|
|
13638
13987
|
return Date.now() - fetchedAt > ttlMs;
|
|
@@ -13641,9 +13990,9 @@ var AgentCatalog = class {
|
|
|
13641
13990
|
}
|
|
13642
13991
|
}
|
|
13643
13992
|
loadRegistryFromCacheOrSnapshot() {
|
|
13644
|
-
if (
|
|
13993
|
+
if (fs15.existsSync(this.cachePath)) {
|
|
13645
13994
|
try {
|
|
13646
|
-
const raw = JSON.parse(
|
|
13995
|
+
const raw = JSON.parse(fs15.readFileSync(this.cachePath, "utf-8"));
|
|
13647
13996
|
if (raw.data?.agents) {
|
|
13648
13997
|
this.registryAgents = raw.data.agents;
|
|
13649
13998
|
log12.debug({ count: this.registryAgents.length }, "Loaded registry from cache");
|
|
@@ -13655,13 +14004,13 @@ var AgentCatalog = class {
|
|
|
13655
14004
|
}
|
|
13656
14005
|
try {
|
|
13657
14006
|
const candidates = [
|
|
13658
|
-
|
|
13659
|
-
|
|
13660
|
-
|
|
14007
|
+
path14.join(import.meta.dirname, "data", "registry-snapshot.json"),
|
|
14008
|
+
path14.join(import.meta.dirname, "..", "data", "registry-snapshot.json"),
|
|
14009
|
+
path14.join(import.meta.dirname, "..", "..", "data", "registry-snapshot.json")
|
|
13661
14010
|
];
|
|
13662
14011
|
for (const candidate of candidates) {
|
|
13663
|
-
if (
|
|
13664
|
-
const raw = JSON.parse(
|
|
14012
|
+
if (fs15.existsSync(candidate)) {
|
|
14013
|
+
const raw = JSON.parse(fs15.readFileSync(candidate, "utf-8"));
|
|
13665
14014
|
this.registryAgents = raw.agents ?? [];
|
|
13666
14015
|
log12.debug({ count: this.registryAgents.length }, "Loaded registry from bundled snapshot");
|
|
13667
14016
|
return;
|
|
@@ -13922,31 +14271,31 @@ var ErrorTracker = class {
|
|
|
13922
14271
|
};
|
|
13923
14272
|
|
|
13924
14273
|
// src/core/plugin/plugin-context.ts
|
|
13925
|
-
import
|
|
14274
|
+
import path16 from "path";
|
|
13926
14275
|
import os7 from "os";
|
|
13927
14276
|
|
|
13928
14277
|
// src/core/plugin/plugin-storage.ts
|
|
13929
|
-
import
|
|
13930
|
-
import
|
|
14278
|
+
import fs16 from "fs";
|
|
14279
|
+
import path15 from "path";
|
|
13931
14280
|
var PluginStorageImpl = class {
|
|
13932
14281
|
kvPath;
|
|
13933
14282
|
dataDir;
|
|
13934
14283
|
writeChain = Promise.resolve();
|
|
13935
14284
|
constructor(baseDir) {
|
|
13936
|
-
this.dataDir =
|
|
13937
|
-
this.kvPath =
|
|
13938
|
-
|
|
14285
|
+
this.dataDir = path15.join(baseDir, "data");
|
|
14286
|
+
this.kvPath = path15.join(baseDir, "kv.json");
|
|
14287
|
+
fs16.mkdirSync(baseDir, { recursive: true });
|
|
13939
14288
|
}
|
|
13940
14289
|
readKv() {
|
|
13941
14290
|
try {
|
|
13942
|
-
const raw =
|
|
14291
|
+
const raw = fs16.readFileSync(this.kvPath, "utf-8");
|
|
13943
14292
|
return JSON.parse(raw);
|
|
13944
14293
|
} catch {
|
|
13945
14294
|
return {};
|
|
13946
14295
|
}
|
|
13947
14296
|
}
|
|
13948
14297
|
writeKv(data) {
|
|
13949
|
-
|
|
14298
|
+
fs16.writeFileSync(this.kvPath, JSON.stringify(data), "utf-8");
|
|
13950
14299
|
}
|
|
13951
14300
|
async get(key) {
|
|
13952
14301
|
const data = this.readKv();
|
|
@@ -13972,7 +14321,7 @@ var PluginStorageImpl = class {
|
|
|
13972
14321
|
return Object.keys(this.readKv());
|
|
13973
14322
|
}
|
|
13974
14323
|
getDataDir() {
|
|
13975
|
-
|
|
14324
|
+
fs16.mkdirSync(this.dataDir, { recursive: true });
|
|
13976
14325
|
return this.dataDir;
|
|
13977
14326
|
}
|
|
13978
14327
|
};
|
|
@@ -13996,7 +14345,7 @@ function createPluginContext(opts) {
|
|
|
13996
14345
|
config,
|
|
13997
14346
|
core
|
|
13998
14347
|
} = opts;
|
|
13999
|
-
const instanceRoot = opts.instanceRoot ??
|
|
14348
|
+
const instanceRoot = opts.instanceRoot ?? path16.join(os7.homedir(), ".openacp");
|
|
14000
14349
|
const registeredListeners = [];
|
|
14001
14350
|
const registeredCommands = [];
|
|
14002
14351
|
const noopLog = {
|
|
@@ -14017,7 +14366,7 @@ function createPluginContext(opts) {
|
|
|
14017
14366
|
}
|
|
14018
14367
|
};
|
|
14019
14368
|
const baseLog = opts.log ?? noopLog;
|
|
14020
|
-
const
|
|
14369
|
+
const log34 = typeof baseLog.child === "function" ? baseLog.child({ plugin: pluginName }) : baseLog;
|
|
14021
14370
|
const storageImpl = new PluginStorageImpl(storagePath);
|
|
14022
14371
|
const storage = {
|
|
14023
14372
|
async get(key) {
|
|
@@ -14044,7 +14393,7 @@ function createPluginContext(opts) {
|
|
|
14044
14393
|
const ctx = {
|
|
14045
14394
|
pluginName,
|
|
14046
14395
|
pluginConfig,
|
|
14047
|
-
log:
|
|
14396
|
+
log: log34,
|
|
14048
14397
|
storage,
|
|
14049
14398
|
on(event, handler) {
|
|
14050
14399
|
requirePermission(permissions, "events:read", "on()");
|
|
@@ -14079,7 +14428,7 @@ function createPluginContext(opts) {
|
|
|
14079
14428
|
const registry = serviceRegistry.get("command-registry");
|
|
14080
14429
|
if (registry && typeof registry.register === "function") {
|
|
14081
14430
|
registry.register(def, pluginName);
|
|
14082
|
-
|
|
14431
|
+
log34.debug(`Command '/${def.name}' registered`);
|
|
14083
14432
|
}
|
|
14084
14433
|
},
|
|
14085
14434
|
async sendMessage(_sessionId, _content) {
|
|
@@ -14826,7 +15175,7 @@ var OpenACPCore = class {
|
|
|
14826
15175
|
);
|
|
14827
15176
|
this.agentCatalog.load();
|
|
14828
15177
|
this.agentManager = new AgentManager(this.agentCatalog);
|
|
14829
|
-
const storePath = ctx?.paths.sessions ??
|
|
15178
|
+
const storePath = ctx?.paths.sessions ?? path17.join(os8.homedir(), ".openacp", "sessions.json");
|
|
14830
15179
|
this.sessionStore = new JsonFileSessionStore(
|
|
14831
15180
|
storePath,
|
|
14832
15181
|
config.sessionStore.ttlDays
|
|
@@ -14850,7 +15199,7 @@ var OpenACPCore = class {
|
|
|
14850
15199
|
sessions: this.sessionManager,
|
|
14851
15200
|
config: this.configManager,
|
|
14852
15201
|
core: this,
|
|
14853
|
-
storagePath: ctx?.paths.pluginsData ??
|
|
15202
|
+
storagePath: ctx?.paths.pluginsData ?? path17.join(os8.homedir(), ".openacp", "plugins", "data"),
|
|
14854
15203
|
instanceRoot: ctx?.root,
|
|
14855
15204
|
log: createChildLogger({ module: "plugin" })
|
|
14856
15205
|
});
|
|
@@ -14862,6 +15211,10 @@ var OpenACPCore = class {
|
|
|
14862
15211
|
this.sessionFactory.configManager = this.configManager;
|
|
14863
15212
|
this.sessionFactory.agentCatalog = this.agentCatalog;
|
|
14864
15213
|
this.sessionFactory.getContextManager = () => this.lifecycleManager.serviceRegistry.get("context");
|
|
15214
|
+
this.sessionFactory.getAgentAllowedPaths = () => {
|
|
15215
|
+
const fileService = this.lifecycleManager.serviceRegistry.get("file-service");
|
|
15216
|
+
return fileService ? [fileService.baseDir] : [];
|
|
15217
|
+
};
|
|
14865
15218
|
this.agentSwitchHandler = new AgentSwitchHandler({
|
|
14866
15219
|
sessionManager: this.sessionManager,
|
|
14867
15220
|
agentManager: this.agentManager,
|
|
@@ -14911,7 +15264,7 @@ var OpenACPCore = class {
|
|
|
14911
15264
|
);
|
|
14912
15265
|
registerCoreMenuItems(this.menuRegistry);
|
|
14913
15266
|
if (ctx?.root) {
|
|
14914
|
-
this.assistantRegistry.setInstanceRoot(
|
|
15267
|
+
this.assistantRegistry.setInstanceRoot(path17.dirname(ctx.root));
|
|
14915
15268
|
}
|
|
14916
15269
|
this.assistantRegistry.register(createSessionsSection(this));
|
|
14917
15270
|
this.assistantRegistry.register(createAgentsSection(this));
|
|
@@ -15413,19 +15766,19 @@ init_doctor();
|
|
|
15413
15766
|
init_config_registry();
|
|
15414
15767
|
|
|
15415
15768
|
// src/core/config/config-editor.ts
|
|
15416
|
-
import * as
|
|
15769
|
+
import * as path29 from "path";
|
|
15417
15770
|
import * as clack2 from "@clack/prompts";
|
|
15418
15771
|
|
|
15419
15772
|
// src/cli/autostart.ts
|
|
15420
15773
|
init_log();
|
|
15421
15774
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
15422
|
-
import * as
|
|
15423
|
-
import * as
|
|
15775
|
+
import * as fs27 from "fs";
|
|
15776
|
+
import * as path25 from "path";
|
|
15424
15777
|
import * as os11 from "os";
|
|
15425
15778
|
var log18 = createChildLogger({ module: "autostart" });
|
|
15426
15779
|
var LAUNCHD_LABEL = "com.openacp.daemon";
|
|
15427
|
-
var LAUNCHD_PLIST_PATH =
|
|
15428
|
-
var SYSTEMD_SERVICE_PATH =
|
|
15780
|
+
var LAUNCHD_PLIST_PATH = path25.join(os11.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
|
|
15781
|
+
var SYSTEMD_SERVICE_PATH = path25.join(os11.homedir(), ".config", "systemd", "user", "openacp.service");
|
|
15429
15782
|
function isAutoStartSupported() {
|
|
15430
15783
|
return process.platform === "darwin" || process.platform === "linux";
|
|
15431
15784
|
}
|
|
@@ -15437,7 +15790,7 @@ function escapeSystemdValue(str) {
|
|
|
15437
15790
|
return `"${escaped}"`;
|
|
15438
15791
|
}
|
|
15439
15792
|
function generateLaunchdPlist(nodePath, cliPath, logDir2) {
|
|
15440
|
-
const logFile =
|
|
15793
|
+
const logFile = path25.join(logDir2, "openacp.log");
|
|
15441
15794
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
15442
15795
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
15443
15796
|
<plist version="1.0">
|
|
@@ -15482,23 +15835,23 @@ function installAutoStart(logDir2) {
|
|
|
15482
15835
|
return { success: false, error: "Auto-start not supported on this platform" };
|
|
15483
15836
|
}
|
|
15484
15837
|
const nodePath = process.execPath;
|
|
15485
|
-
const cliPath =
|
|
15486
|
-
const resolvedLogDir = logDir2.startsWith("~") ?
|
|
15838
|
+
const cliPath = path25.resolve(process.argv[1]);
|
|
15839
|
+
const resolvedLogDir = logDir2.startsWith("~") ? path25.join(os11.homedir(), logDir2.slice(1)) : logDir2;
|
|
15487
15840
|
try {
|
|
15488
15841
|
if (process.platform === "darwin") {
|
|
15489
15842
|
const plist = generateLaunchdPlist(nodePath, cliPath, resolvedLogDir);
|
|
15490
|
-
const dir =
|
|
15491
|
-
|
|
15492
|
-
|
|
15843
|
+
const dir = path25.dirname(LAUNCHD_PLIST_PATH);
|
|
15844
|
+
fs27.mkdirSync(dir, { recursive: true });
|
|
15845
|
+
fs27.writeFileSync(LAUNCHD_PLIST_PATH, plist);
|
|
15493
15846
|
execFileSync5("launchctl", ["load", LAUNCHD_PLIST_PATH], { stdio: "pipe" });
|
|
15494
15847
|
log18.info("LaunchAgent installed");
|
|
15495
15848
|
return { success: true };
|
|
15496
15849
|
}
|
|
15497
15850
|
if (process.platform === "linux") {
|
|
15498
15851
|
const unit = generateSystemdUnit(nodePath, cliPath);
|
|
15499
|
-
const dir =
|
|
15500
|
-
|
|
15501
|
-
|
|
15852
|
+
const dir = path25.dirname(SYSTEMD_SERVICE_PATH);
|
|
15853
|
+
fs27.mkdirSync(dir, { recursive: true });
|
|
15854
|
+
fs27.writeFileSync(SYSTEMD_SERVICE_PATH, unit);
|
|
15502
15855
|
execFileSync5("systemctl", ["--user", "daemon-reload"], { stdio: "pipe" });
|
|
15503
15856
|
execFileSync5("systemctl", ["--user", "enable", "openacp"], { stdio: "pipe" });
|
|
15504
15857
|
log18.info("systemd user service installed");
|
|
@@ -15517,23 +15870,23 @@ function uninstallAutoStart() {
|
|
|
15517
15870
|
}
|
|
15518
15871
|
try {
|
|
15519
15872
|
if (process.platform === "darwin") {
|
|
15520
|
-
if (
|
|
15873
|
+
if (fs27.existsSync(LAUNCHD_PLIST_PATH)) {
|
|
15521
15874
|
try {
|
|
15522
15875
|
execFileSync5("launchctl", ["unload", LAUNCHD_PLIST_PATH], { stdio: "pipe" });
|
|
15523
15876
|
} catch {
|
|
15524
15877
|
}
|
|
15525
|
-
|
|
15878
|
+
fs27.unlinkSync(LAUNCHD_PLIST_PATH);
|
|
15526
15879
|
log18.info("LaunchAgent removed");
|
|
15527
15880
|
}
|
|
15528
15881
|
return { success: true };
|
|
15529
15882
|
}
|
|
15530
15883
|
if (process.platform === "linux") {
|
|
15531
|
-
if (
|
|
15884
|
+
if (fs27.existsSync(SYSTEMD_SERVICE_PATH)) {
|
|
15532
15885
|
try {
|
|
15533
15886
|
execFileSync5("systemctl", ["--user", "disable", "openacp"], { stdio: "pipe" });
|
|
15534
15887
|
} catch {
|
|
15535
15888
|
}
|
|
15536
|
-
|
|
15889
|
+
fs27.unlinkSync(SYSTEMD_SERVICE_PATH);
|
|
15537
15890
|
execFileSync5("systemctl", ["--user", "daemon-reload"], { stdio: "pipe" });
|
|
15538
15891
|
log18.info("systemd user service removed");
|
|
15539
15892
|
}
|
|
@@ -15548,10 +15901,10 @@ function uninstallAutoStart() {
|
|
|
15548
15901
|
}
|
|
15549
15902
|
function isAutoStartInstalled() {
|
|
15550
15903
|
if (process.platform === "darwin") {
|
|
15551
|
-
return
|
|
15904
|
+
return fs27.existsSync(LAUNCHD_PLIST_PATH);
|
|
15552
15905
|
}
|
|
15553
15906
|
if (process.platform === "linux") {
|
|
15554
|
-
return
|
|
15907
|
+
return fs27.existsSync(SYSTEMD_SERVICE_PATH);
|
|
15555
15908
|
}
|
|
15556
15909
|
return false;
|
|
15557
15910
|
}
|
|
@@ -15756,7 +16109,7 @@ async function editDiscord(_config, _updates) {
|
|
|
15756
16109
|
const { createInstallContext: createInstallContext2 } = await Promise.resolve().then(() => (init_install_context(), install_context_exports));
|
|
15757
16110
|
const { getGlobalRoot: getGlobalRoot2 } = await Promise.resolve().then(() => (init_instance_context(), instance_context_exports));
|
|
15758
16111
|
const root = getGlobalRoot2();
|
|
15759
|
-
const basePath =
|
|
16112
|
+
const basePath = path29.join(root, "plugins", "data");
|
|
15760
16113
|
const settingsManager = new SettingsManager2(basePath);
|
|
15761
16114
|
const ctx = createInstallContext2({
|
|
15762
16115
|
pluginName: plugin.name,
|
|
@@ -16277,17 +16630,17 @@ ${c.cyan}${c.bold}OpenACP Config Editor${c.reset}`);
|
|
|
16277
16630
|
async function sendConfigViaApi(port, updates) {
|
|
16278
16631
|
const { apiCall: call } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
|
|
16279
16632
|
const paths = flattenToPaths(updates);
|
|
16280
|
-
for (const { path:
|
|
16633
|
+
for (const { path: path35, value } of paths) {
|
|
16281
16634
|
const res = await call(port, "/api/config", {
|
|
16282
16635
|
method: "PATCH",
|
|
16283
16636
|
headers: { "Content-Type": "application/json" },
|
|
16284
|
-
body: JSON.stringify({ path:
|
|
16637
|
+
body: JSON.stringify({ path: path35, value })
|
|
16285
16638
|
});
|
|
16286
16639
|
const data = await res.json();
|
|
16287
16640
|
if (!res.ok) {
|
|
16288
|
-
console.log(warn(`Failed to update ${
|
|
16641
|
+
console.log(warn(`Failed to update ${path35}: ${data.error}`));
|
|
16289
16642
|
} else if (data.needsRestart) {
|
|
16290
|
-
console.log(warn(`${
|
|
16643
|
+
console.log(warn(`${path35} updated \u2014 restart required`));
|
|
16291
16644
|
}
|
|
16292
16645
|
}
|
|
16293
16646
|
}
|
|
@@ -16307,30 +16660,30 @@ function flattenToPaths(obj, prefix = "") {
|
|
|
16307
16660
|
// src/cli/daemon.ts
|
|
16308
16661
|
init_config();
|
|
16309
16662
|
import { spawn as spawn3 } from "child_process";
|
|
16310
|
-
import * as
|
|
16311
|
-
import * as
|
|
16663
|
+
import * as fs30 from "fs";
|
|
16664
|
+
import * as path30 from "path";
|
|
16312
16665
|
import * as os14 from "os";
|
|
16313
|
-
var DEFAULT_ROOT2 =
|
|
16666
|
+
var DEFAULT_ROOT2 = path30.join(os14.homedir(), ".openacp");
|
|
16314
16667
|
function getPidPath(root) {
|
|
16315
16668
|
const base = root ?? DEFAULT_ROOT2;
|
|
16316
|
-
return
|
|
16669
|
+
return path30.join(base, "openacp.pid");
|
|
16317
16670
|
}
|
|
16318
16671
|
function getLogDir(root) {
|
|
16319
16672
|
const base = root ?? DEFAULT_ROOT2;
|
|
16320
|
-
return
|
|
16673
|
+
return path30.join(base, "logs");
|
|
16321
16674
|
}
|
|
16322
16675
|
function getRunningMarker(root) {
|
|
16323
16676
|
const base = root ?? DEFAULT_ROOT2;
|
|
16324
|
-
return
|
|
16677
|
+
return path30.join(base, "running");
|
|
16325
16678
|
}
|
|
16326
16679
|
function writePidFile(pidPath, pid) {
|
|
16327
|
-
const dir =
|
|
16328
|
-
|
|
16329
|
-
|
|
16680
|
+
const dir = path30.dirname(pidPath);
|
|
16681
|
+
fs30.mkdirSync(dir, { recursive: true });
|
|
16682
|
+
fs30.writeFileSync(pidPath, String(pid));
|
|
16330
16683
|
}
|
|
16331
16684
|
function readPidFile(pidPath) {
|
|
16332
16685
|
try {
|
|
16333
|
-
const content =
|
|
16686
|
+
const content = fs30.readFileSync(pidPath, "utf-8").trim();
|
|
16334
16687
|
const pid = parseInt(content, 10);
|
|
16335
16688
|
return isNaN(pid) ? null : pid;
|
|
16336
16689
|
} catch {
|
|
@@ -16339,7 +16692,7 @@ function readPidFile(pidPath) {
|
|
|
16339
16692
|
}
|
|
16340
16693
|
function removePidFile(pidPath) {
|
|
16341
16694
|
try {
|
|
16342
|
-
|
|
16695
|
+
fs30.unlinkSync(pidPath);
|
|
16343
16696
|
} catch {
|
|
16344
16697
|
}
|
|
16345
16698
|
}
|
|
@@ -16372,12 +16725,12 @@ function startDaemon(pidPath = getPidPath(), logDir2, instanceRoot) {
|
|
|
16372
16725
|
return { error: `Already running (PID ${pid})` };
|
|
16373
16726
|
}
|
|
16374
16727
|
const resolvedLogDir = logDir2 ? expandHome3(logDir2) : getLogDir(instanceRoot);
|
|
16375
|
-
|
|
16376
|
-
const logFile =
|
|
16377
|
-
const cliPath =
|
|
16728
|
+
fs30.mkdirSync(resolvedLogDir, { recursive: true });
|
|
16729
|
+
const logFile = path30.join(resolvedLogDir, "openacp.log");
|
|
16730
|
+
const cliPath = path30.resolve(process.argv[1]);
|
|
16378
16731
|
const nodePath = process.execPath;
|
|
16379
|
-
const out =
|
|
16380
|
-
const err =
|
|
16732
|
+
const out = fs30.openSync(logFile, "a");
|
|
16733
|
+
const err = fs30.openSync(logFile, "a");
|
|
16381
16734
|
const child = spawn3(nodePath, [cliPath, "--daemon-child"], {
|
|
16382
16735
|
detached: true,
|
|
16383
16736
|
stdio: ["ignore", out, err],
|
|
@@ -16386,8 +16739,8 @@ function startDaemon(pidPath = getPidPath(), logDir2, instanceRoot) {
|
|
|
16386
16739
|
...instanceRoot ? { OPENACP_INSTANCE_ROOT: instanceRoot } : {}
|
|
16387
16740
|
}
|
|
16388
16741
|
});
|
|
16389
|
-
|
|
16390
|
-
|
|
16742
|
+
fs30.closeSync(out);
|
|
16743
|
+
fs30.closeSync(err);
|
|
16391
16744
|
if (!child.pid) {
|
|
16392
16745
|
return { error: "Failed to spawn daemon process" };
|
|
16393
16746
|
}
|
|
@@ -16458,12 +16811,12 @@ async function stopDaemon(pidPath = getPidPath(), instanceRoot) {
|
|
|
16458
16811
|
}
|
|
16459
16812
|
function markRunning(root) {
|
|
16460
16813
|
const marker = getRunningMarker(root);
|
|
16461
|
-
|
|
16462
|
-
|
|
16814
|
+
fs30.mkdirSync(path30.dirname(marker), { recursive: true });
|
|
16815
|
+
fs30.writeFileSync(marker, "");
|
|
16463
16816
|
}
|
|
16464
16817
|
function clearRunning(root) {
|
|
16465
16818
|
try {
|
|
16466
|
-
|
|
16819
|
+
fs30.unlinkSync(getRunningMarker(root));
|
|
16467
16820
|
} catch {
|
|
16468
16821
|
}
|
|
16469
16822
|
}
|
|
@@ -16479,7 +16832,7 @@ init_static_server();
|
|
|
16479
16832
|
|
|
16480
16833
|
// src/plugins/telegram/topic-manager.ts
|
|
16481
16834
|
init_log();
|
|
16482
|
-
var
|
|
16835
|
+
var log21 = createChildLogger({ module: "topic-manager" });
|
|
16483
16836
|
var TopicManager = class {
|
|
16484
16837
|
constructor(sessionManager, adapter, systemTopicIds) {
|
|
16485
16838
|
this.sessionManager = sessionManager;
|
|
@@ -16518,7 +16871,7 @@ var TopicManager = class {
|
|
|
16518
16871
|
try {
|
|
16519
16872
|
await this.adapter.deleteSessionThread?.(sessionId);
|
|
16520
16873
|
} catch (err) {
|
|
16521
|
-
|
|
16874
|
+
log21.warn({ err, sessionId, topicId }, "Failed to delete platform thread, removing record anyway");
|
|
16522
16875
|
}
|
|
16523
16876
|
}
|
|
16524
16877
|
await this.sessionManager.removeRecord(sessionId);
|
|
@@ -16541,7 +16894,7 @@ var TopicManager = class {
|
|
|
16541
16894
|
try {
|
|
16542
16895
|
await this.adapter.deleteSessionThread?.(record.sessionId);
|
|
16543
16896
|
} catch (err) {
|
|
16544
|
-
|
|
16897
|
+
log21.warn({ err, sessionId: record.sessionId }, "Failed to delete platform thread during cleanup");
|
|
16545
16898
|
}
|
|
16546
16899
|
}
|
|
16547
16900
|
await this.sessionManager.removeRecord(record.sessionId);
|