@hachej/boring-agent 0.1.0 → 0.1.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/front/index.js +1 -1
- package/dist/server/index.js +83 -57
- package/package.json +3 -3
package/dist/front/index.js
CHANGED
|
@@ -236,7 +236,7 @@ function useAgentChat(opts) {
|
|
|
236
236
|
}, [sessionId, cacheKey, setMessages]);
|
|
237
237
|
const messages = chat.messages;
|
|
238
238
|
useEffect4(() => {
|
|
239
|
-
if (!hydrated || !cacheKey) return;
|
|
239
|
+
if (!hydrated || !cacheKey || messages.length === 0) return;
|
|
240
240
|
try {
|
|
241
241
|
globalThis.localStorage?.setItem(cacheKey, JSON.stringify(messages));
|
|
242
242
|
} catch {
|
package/dist/server/index.js
CHANGED
|
@@ -180,8 +180,10 @@ function createDirectSandbox() {
|
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
// src/server/sandbox/bwrap/createBwrapSandbox.ts
|
|
183
|
+
import { access } from "fs/promises";
|
|
184
|
+
import { constants } from "fs";
|
|
183
185
|
import { spawn as spawn2 } from "child_process";
|
|
184
|
-
import { isAbsolute as isAbsolute2, relative, resolve, sep } from "path";
|
|
186
|
+
import { dirname, isAbsolute as isAbsolute2, join as join2, relative, resolve, sep } from "path";
|
|
185
187
|
|
|
186
188
|
// src/server/sandbox/bwrap/buildBwrapArgs.ts
|
|
187
189
|
import { isAbsolute } from "path";
|
|
@@ -250,9 +252,12 @@ function buildBwrapArgs(workspaceRoot, options) {
|
|
|
250
252
|
SANDBOX_HOME,
|
|
251
253
|
"--setenv",
|
|
252
254
|
"HOME",
|
|
253
|
-
SANDBOX_HOME
|
|
254
|
-
"--"
|
|
255
|
+
SANDBOX_HOME
|
|
255
256
|
);
|
|
257
|
+
if (options?.postWorkspaceArgs) {
|
|
258
|
+
args.push(...options.postWorkspaceArgs);
|
|
259
|
+
}
|
|
260
|
+
args.push("--");
|
|
256
261
|
return args;
|
|
257
262
|
}
|
|
258
263
|
|
|
@@ -347,6 +352,26 @@ async function assertBwrapAvailable() {
|
|
|
347
352
|
});
|
|
348
353
|
});
|
|
349
354
|
}
|
|
355
|
+
async function dirAccessible(path4) {
|
|
356
|
+
try {
|
|
357
|
+
await access(path4, constants.F_OK);
|
|
358
|
+
return true;
|
|
359
|
+
} catch {
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
async function buildGlobalToolMounts(workspaceRoot) {
|
|
364
|
+
const globalRoot = dirname(workspaceRoot);
|
|
365
|
+
if (globalRoot === workspaceRoot) return [];
|
|
366
|
+
const args = [];
|
|
367
|
+
if (await dirAccessible(join2(globalRoot, ".boring-agent"))) {
|
|
368
|
+
args.push("--ro-bind", join2(globalRoot, ".boring-agent"), `${SANDBOX_HOME2}/.boring-agent`);
|
|
369
|
+
}
|
|
370
|
+
if (await dirAccessible(join2(globalRoot, ".venv"))) {
|
|
371
|
+
args.push("--ro-bind", join2(globalRoot, ".venv"), `${SANDBOX_HOME2}/.venv`);
|
|
372
|
+
}
|
|
373
|
+
return args;
|
|
374
|
+
}
|
|
350
375
|
function createBwrapSandbox() {
|
|
351
376
|
let workspace = null;
|
|
352
377
|
return {
|
|
@@ -367,7 +392,8 @@ function createBwrapSandbox() {
|
|
|
367
392
|
const maxOutputBytes = opts?.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES2;
|
|
368
393
|
const workspaceRoot = workspace.root;
|
|
369
394
|
const sandboxCwd = computeSandboxCwd(workspaceRoot, opts?.cwd);
|
|
370
|
-
const
|
|
395
|
+
const postWorkspaceArgs = await buildGlobalToolMounts(workspaceRoot);
|
|
396
|
+
const baseArgs = buildBwrapArgs(workspaceRoot, { postWorkspaceArgs });
|
|
371
397
|
const args = [
|
|
372
398
|
...withSandboxCwd(baseArgs, sandboxCwd),
|
|
373
399
|
"bash",
|
|
@@ -1182,12 +1208,12 @@ async function prepareVercelDeploymentSnapshot(opts) {
|
|
|
1182
1208
|
|
|
1183
1209
|
// src/server/workspace/createNodeWorkspace.ts
|
|
1184
1210
|
import { lstat as lstat2, mkdir as mkdir3, readdir, readFile as readFile3, rename as rename3, rmdir, stat as stat2, unlink as unlink3, writeFile as writeFile3 } from "fs/promises";
|
|
1185
|
-
import { dirname as
|
|
1211
|
+
import { dirname as dirname3, relative as relative3, sep as sep2 } from "path";
|
|
1186
1212
|
import chokidar from "chokidar";
|
|
1187
1213
|
|
|
1188
1214
|
// src/server/workspace/paths.ts
|
|
1189
1215
|
import { lstat, realpath, stat } from "fs/promises";
|
|
1190
|
-
import { dirname, isAbsolute as isAbsolute3, relative as relative2, resolve as resolve2 } from "path";
|
|
1216
|
+
import { dirname as dirname2, isAbsolute as isAbsolute3, relative as relative2, resolve as resolve2 } from "path";
|
|
1191
1217
|
function createPathValidationError(reason, requestedPath, message) {
|
|
1192
1218
|
return Object.assign(new Error(message), {
|
|
1193
1219
|
statusCode: 400,
|
|
@@ -1247,7 +1273,7 @@ async function ensureExistingWorkspacePath(workspaceRoot, relPath) {
|
|
|
1247
1273
|
}
|
|
1248
1274
|
async function ensureWritableWorkspacePath(workspaceRoot, relPath) {
|
|
1249
1275
|
const absPath = validatePath(workspaceRoot, relPath);
|
|
1250
|
-
const parentPath =
|
|
1276
|
+
const parentPath = dirname2(absPath);
|
|
1251
1277
|
await stat(parentPath);
|
|
1252
1278
|
await assertRealPathWithinWorkspace(workspaceRoot, parentPath);
|
|
1253
1279
|
try {
|
|
@@ -1419,7 +1445,7 @@ function createNodeWorkspace(root) {
|
|
|
1419
1445
|
} catch (error) {
|
|
1420
1446
|
const code = error.code;
|
|
1421
1447
|
if (code !== "ENOENT") throw error;
|
|
1422
|
-
const parent =
|
|
1448
|
+
const parent = dirname3(existingAncestor);
|
|
1423
1449
|
if (parent === existingAncestor) throw error;
|
|
1424
1450
|
existingAncestor = parent;
|
|
1425
1451
|
}
|
|
@@ -1438,9 +1464,9 @@ function createNodeWorkspace(root) {
|
|
|
1438
1464
|
|
|
1439
1465
|
// src/server/workspace/provisionRuntime.ts
|
|
1440
1466
|
import { createHash as createHash3 } from "crypto";
|
|
1441
|
-
import { constants } from "fs";
|
|
1442
|
-
import { access, chmod as chmod3, cp, mkdir as mkdir4, readdir as readdir2, readFile as readFile4, stat as stat3, writeFile as writeFile4 } from "fs/promises";
|
|
1443
|
-
import { dirname as
|
|
1467
|
+
import { constants as constants2 } from "fs";
|
|
1468
|
+
import { access as access2, chmod as chmod3, cp, mkdir as mkdir4, readdir as readdir2, readFile as readFile4, stat as stat3, writeFile as writeFile4 } from "fs/promises";
|
|
1469
|
+
import { dirname as dirname4, join as join3, resolve as resolve3 } from "path";
|
|
1444
1470
|
import { fileURLToPath } from "url";
|
|
1445
1471
|
import { execFile } from "child_process";
|
|
1446
1472
|
import { promisify } from "util";
|
|
@@ -1451,7 +1477,7 @@ function toPath(value) {
|
|
|
1451
1477
|
}
|
|
1452
1478
|
async function exists(path4) {
|
|
1453
1479
|
try {
|
|
1454
|
-
await
|
|
1480
|
+
await access2(path4, constants2.F_OK);
|
|
1455
1481
|
return true;
|
|
1456
1482
|
} catch {
|
|
1457
1483
|
return false;
|
|
@@ -1480,7 +1506,7 @@ async function hashPath(path4, hash) {
|
|
|
1480
1506
|
for (const entry of entries) {
|
|
1481
1507
|
hash.update(`dir:${entry}
|
|
1482
1508
|
`);
|
|
1483
|
-
await hashPath(
|
|
1509
|
+
await hashPath(join3(path4, entry), hash);
|
|
1484
1510
|
}
|
|
1485
1511
|
return;
|
|
1486
1512
|
}
|
|
@@ -1507,7 +1533,7 @@ async function fingerprint(contributions) {
|
|
|
1507
1533
|
`);
|
|
1508
1534
|
hash.update(JSON.stringify(spec.extraLibs ?? []));
|
|
1509
1535
|
hash.update(JSON.stringify(Object.fromEntries(Object.entries(spec.env ?? {}).map(([k, v]) => [k, String(v)]))));
|
|
1510
|
-
if (await exists(projectFile)) await hashPath(
|
|
1536
|
+
if (await exists(projectFile)) await hashPath(dirname4(projectFile), hash);
|
|
1511
1537
|
}
|
|
1512
1538
|
}
|
|
1513
1539
|
return `sha256:${hash.digest("hex")}`;
|
|
@@ -1536,14 +1562,14 @@ async function seedTemplates(workspaceRoot, contributions) {
|
|
|
1536
1562
|
}
|
|
1537
1563
|
async function ensurePython(workspaceRoot, specs) {
|
|
1538
1564
|
if (specs.length === 0) return;
|
|
1539
|
-
const venvPython =
|
|
1565
|
+
const venvPython = join3(workspaceRoot, ".venv", "bin", "python");
|
|
1540
1566
|
const uv = await commandExists("uv");
|
|
1541
1567
|
if (!await exists(venvPython)) {
|
|
1542
1568
|
if (uv) await run("uv", ["venv", ".venv"], workspaceRoot);
|
|
1543
1569
|
else await run("/usr/bin/python3", ["-m", "venv", ".venv"], workspaceRoot);
|
|
1544
1570
|
}
|
|
1545
1571
|
for (const spec of specs) {
|
|
1546
|
-
const projectDir =
|
|
1572
|
+
const projectDir = dirname4(toPath(spec.projectFile));
|
|
1547
1573
|
if (uv) {
|
|
1548
1574
|
await run("uv", ["pip", "install", "--python", venvPython, projectDir], workspaceRoot);
|
|
1549
1575
|
if (spec.extraLibs?.length) {
|
|
@@ -1569,12 +1595,12 @@ function assertEnvKey(key) {
|
|
|
1569
1595
|
}
|
|
1570
1596
|
async function isRuntimeMaterialized(workspaceRoot, contributions) {
|
|
1571
1597
|
const hasPython = contributions.some(({ provisioning }) => (provisioning.python ?? []).length > 0);
|
|
1572
|
-
if (hasPython && !await exists(
|
|
1598
|
+
if (hasPython && !await exists(join3(workspaceRoot, ".venv", "bin", "python"))) return false;
|
|
1573
1599
|
return true;
|
|
1574
1600
|
}
|
|
1575
1601
|
async function writeShims(workspaceRoot, env) {
|
|
1576
|
-
const shimDir =
|
|
1577
|
-
const venvBin =
|
|
1602
|
+
const shimDir = join3(workspaceRoot, ".boring-agent", "bin");
|
|
1603
|
+
const venvBin = join3(workspaceRoot, ".venv", "bin");
|
|
1578
1604
|
await mkdir4(shimDir, { recursive: true });
|
|
1579
1605
|
const exports = Object.entries(env).map(([key, value]) => {
|
|
1580
1606
|
assertEnvKey(key);
|
|
@@ -1588,21 +1614,21 @@ export BORING_AGENT_WORKSPACE_ROOT="$WORKSPACE_ROOT"
|
|
|
1588
1614
|
${exports}
|
|
1589
1615
|
VENV_BIN="$WORKSPACE_ROOT/.venv/bin"
|
|
1590
1616
|
`;
|
|
1591
|
-
await writeExecutable(
|
|
1617
|
+
await writeExecutable(join3(shimDir, "python"), `${base}exec "$VENV_BIN/python" "$@"
|
|
1592
1618
|
`);
|
|
1593
|
-
await writeExecutable(
|
|
1619
|
+
await writeExecutable(join3(shimDir, "python3"), `${base}exec "$VENV_BIN/python" "$@"
|
|
1594
1620
|
`);
|
|
1595
|
-
await writeExecutable(
|
|
1621
|
+
await writeExecutable(join3(shimDir, "pip"), `${base}exec "$VENV_BIN/python" -m pip "$@"
|
|
1596
1622
|
`);
|
|
1597
|
-
await writeExecutable(
|
|
1623
|
+
await writeExecutable(join3(shimDir, "pip3"), `${base}exec "$VENV_BIN/python" -m pip "$@"
|
|
1598
1624
|
`);
|
|
1599
1625
|
if (await exists(venvBin)) {
|
|
1600
1626
|
for (const entry of await readdir2(venvBin)) {
|
|
1601
1627
|
if (["python", "python3", "pip", "pip3"].includes(entry)) continue;
|
|
1602
|
-
const full =
|
|
1628
|
+
const full = join3(venvBin, entry);
|
|
1603
1629
|
const info = await stat3(full).catch(() => null);
|
|
1604
1630
|
if (!info?.isFile()) continue;
|
|
1605
|
-
await writeExecutable(
|
|
1631
|
+
await writeExecutable(join3(shimDir, entry), `${base}TARGET="$VENV_BIN"/${bashSingleQuote(entry)}
|
|
1606
1632
|
SHEBANG="$(head -n 1 "$TARGET" 2>/dev/null || true)"
|
|
1607
1633
|
case "$SHEBANG" in
|
|
1608
1634
|
*python*) exec "$VENV_BIN/python" "$TARGET" "$@" ;;
|
|
@@ -1625,8 +1651,8 @@ async function provisionRuntimeWorkspace({
|
|
|
1625
1651
|
await mkdir4(workspaceRoot, { recursive: true });
|
|
1626
1652
|
const env = collectEnv(active);
|
|
1627
1653
|
const hash = await fingerprint(active);
|
|
1628
|
-
const markerPath =
|
|
1629
|
-
const binDir =
|
|
1654
|
+
const markerPath = join3(workspaceRoot, ".boring-agent", "provisioning.json");
|
|
1655
|
+
const binDir = join3(workspaceRoot, ".boring-agent", "bin");
|
|
1630
1656
|
if (!force && await exists(markerPath)) {
|
|
1631
1657
|
try {
|
|
1632
1658
|
const marker = JSON.parse(await readFile4(markerPath, "utf8"));
|
|
@@ -1640,7 +1666,7 @@ async function provisionRuntimeWorkspace({
|
|
|
1640
1666
|
await seedTemplates(workspaceRoot, active);
|
|
1641
1667
|
await ensurePython(workspaceRoot, active.flatMap(({ provisioning }) => provisioning.python ?? []));
|
|
1642
1668
|
const actualBinDir = await writeShims(workspaceRoot, env);
|
|
1643
|
-
await mkdir4(
|
|
1669
|
+
await mkdir4(dirname4(markerPath), { recursive: true });
|
|
1644
1670
|
await writeFile4(markerPath, JSON.stringify({ v: PROVISIONING_VERSION, fingerprint: hash }, null, 2), "utf8");
|
|
1645
1671
|
return { fingerprint: hash, changed: true, env, binDir: actualBinDir };
|
|
1646
1672
|
}
|
|
@@ -2011,7 +2037,7 @@ function createServerFileSearch(workspace, sandbox) {
|
|
|
2011
2037
|
|
|
2012
2038
|
// src/server/workspace/provision.ts
|
|
2013
2039
|
import { cp as cp2, mkdir as mkdir5, stat as stat4, writeFile as writeFile5 } from "fs/promises";
|
|
2014
|
-
import { dirname as
|
|
2040
|
+
import { dirname as dirname5, join as join4 } from "path";
|
|
2015
2041
|
var PROVISION_MARKER_REL_PATH = ".boring-agent/provisioned";
|
|
2016
2042
|
async function fileExists(targetPath) {
|
|
2017
2043
|
try {
|
|
@@ -2026,7 +2052,7 @@ async function fileExists(targetPath) {
|
|
|
2026
2052
|
}
|
|
2027
2053
|
async function copyTemplate(templatePath, workspaceRoot) {
|
|
2028
2054
|
if (!templatePath) return;
|
|
2029
|
-
const markerPath =
|
|
2055
|
+
const markerPath = join4(workspaceRoot, PROVISION_MARKER_REL_PATH);
|
|
2030
2056
|
if (await fileExists(markerPath)) return;
|
|
2031
2057
|
try {
|
|
2032
2058
|
await cp2(templatePath, workspaceRoot, {
|
|
@@ -2040,7 +2066,7 @@ async function copyTemplate(templatePath, workspaceRoot) {
|
|
|
2040
2066
|
{ cause: error }
|
|
2041
2067
|
);
|
|
2042
2068
|
}
|
|
2043
|
-
await mkdir5(
|
|
2069
|
+
await mkdir5(dirname5(markerPath), { recursive: true });
|
|
2044
2070
|
await writeFile5(markerPath, (/* @__PURE__ */ new Date()).toISOString(), "utf-8");
|
|
2045
2071
|
}
|
|
2046
2072
|
|
|
@@ -2792,7 +2818,7 @@ import {
|
|
|
2792
2818
|
utimes
|
|
2793
2819
|
} from "fs/promises";
|
|
2794
2820
|
import { readFileSync, readdirSync } from "fs";
|
|
2795
|
-
import { join as
|
|
2821
|
+
import { join as join5 } from "path";
|
|
2796
2822
|
import { homedir as homedir3 } from "os";
|
|
2797
2823
|
import {
|
|
2798
2824
|
parseSessionEntries,
|
|
@@ -2801,7 +2827,7 @@ import {
|
|
|
2801
2827
|
} from "@mariozechner/pi-coding-agent";
|
|
2802
2828
|
function defaultSessionDir(cwd) {
|
|
2803
2829
|
const safePath = `--${cwd.replace(/^[/\\]/, "").replace(/[/\\:]/g, "-")}--`;
|
|
2804
|
-
return
|
|
2830
|
+
return join5(homedir3(), ".pi", "agent", "sessions", safePath);
|
|
2805
2831
|
}
|
|
2806
2832
|
var SAFE_ID = /^[a-zA-Z0-9_-]+$/;
|
|
2807
2833
|
var PiSessionStore = class {
|
|
@@ -2815,7 +2841,7 @@ var PiSessionStore = class {
|
|
|
2815
2841
|
const files = await readdir4(this.sessionDir).catch(() => []);
|
|
2816
2842
|
const jsonlFiles = files.filter((f) => f.endsWith(".jsonl"));
|
|
2817
2843
|
const summaries = await Promise.all(
|
|
2818
|
-
jsonlFiles.map((f) => this.summarizeFile(
|
|
2844
|
+
jsonlFiles.map((f) => this.summarizeFile(join5(this.sessionDir, f)))
|
|
2819
2845
|
);
|
|
2820
2846
|
return summaries.filter((s) => s !== null).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
2821
2847
|
}
|
|
@@ -2841,7 +2867,7 @@ var PiSessionStore = class {
|
|
|
2841
2867
|
};
|
|
2842
2868
|
lines.push(JSON.stringify(infoEntry));
|
|
2843
2869
|
}
|
|
2844
|
-
const filepath =
|
|
2870
|
+
const filepath = join5(this.sessionDir, `${id}.jsonl`);
|
|
2845
2871
|
await writeFile6(filepath, lines.join("\n") + "\n", "utf-8");
|
|
2846
2872
|
return {
|
|
2847
2873
|
id,
|
|
@@ -2898,7 +2924,7 @@ var PiSessionStore = class {
|
|
|
2898
2924
|
loadPiSessionFileSync(sessionId) {
|
|
2899
2925
|
if (!SAFE_ID.test(sessionId)) return null;
|
|
2900
2926
|
try {
|
|
2901
|
-
const direct =
|
|
2927
|
+
const direct = join5(this.sessionDir, `${sessionId}.jsonl`);
|
|
2902
2928
|
let content;
|
|
2903
2929
|
try {
|
|
2904
2930
|
content = readFileSync(direct, "utf-8");
|
|
@@ -2907,7 +2933,7 @@ var PiSessionStore = class {
|
|
|
2907
2933
|
(f) => f.endsWith(`_${sessionId}.jsonl`) || f === `${sessionId}.jsonl`
|
|
2908
2934
|
);
|
|
2909
2935
|
if (files.length === 0) return null;
|
|
2910
|
-
content = readFileSync(
|
|
2936
|
+
content = readFileSync(join5(this.sessionDir, files[0]), "utf-8");
|
|
2911
2937
|
}
|
|
2912
2938
|
const entries = safeParseEntries(content);
|
|
2913
2939
|
let piFilePath = null;
|
|
@@ -2975,7 +3001,7 @@ var PiSessionStore = class {
|
|
|
2975
3001
|
if (!SAFE_ID.test(sessionId)) {
|
|
2976
3002
|
throw new Error(`Session not found: ${sessionId}`);
|
|
2977
3003
|
}
|
|
2978
|
-
const direct =
|
|
3004
|
+
const direct = join5(this.sessionDir, `${sessionId}.jsonl`);
|
|
2979
3005
|
try {
|
|
2980
3006
|
await fsStat(direct);
|
|
2981
3007
|
return direct;
|
|
@@ -2986,7 +3012,7 @@ var PiSessionStore = class {
|
|
|
2986
3012
|
(f) => f.endsWith(`_${sessionId}.jsonl`) || f === `${sessionId}.jsonl`
|
|
2987
3013
|
);
|
|
2988
3014
|
if (!match) throw new Error(`Session not found: ${sessionId}`);
|
|
2989
|
-
return
|
|
3015
|
+
return join5(this.sessionDir, match);
|
|
2990
3016
|
}
|
|
2991
3017
|
async summarizeFile(filepath) {
|
|
2992
3018
|
try {
|
|
@@ -3913,11 +3939,11 @@ function createPiCodingAgentHarness(opts) {
|
|
|
3913
3939
|
|
|
3914
3940
|
// src/server/harness/pi-coding-agent/pluginLoader.ts
|
|
3915
3941
|
import { readdir as readdir5, stat as stat5, readFile as readFile7 } from "fs/promises";
|
|
3916
|
-
import { join as
|
|
3942
|
+
import { join as join6, extname, resolve as resolve4, sep as sep3 } from "path";
|
|
3917
3943
|
import { homedir as homedir4 } from "os";
|
|
3918
3944
|
import { pathToFileURL } from "url";
|
|
3919
3945
|
var VALID_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs"]);
|
|
3920
|
-
var GLOBAL_DIR =
|
|
3946
|
+
var GLOBAL_DIR = join6(homedir4(), ".pi", "agent", "extensions");
|
|
3921
3947
|
var LOCAL_DIR = ".pi/extensions";
|
|
3922
3948
|
var EXTENSIONS_JSON = ".pi/extensions.json";
|
|
3923
3949
|
async function dirExists(path4) {
|
|
@@ -3939,7 +3965,7 @@ async function fileExists2(path4) {
|
|
|
3939
3965
|
async function discoverFromDir(dir, source) {
|
|
3940
3966
|
if (!await dirExists(dir)) return [];
|
|
3941
3967
|
const entries = await readdir5(dir);
|
|
3942
|
-
return entries.filter((e) => VALID_EXTENSIONS.has(extname(e))).map((e) => ({ path:
|
|
3968
|
+
return entries.filter((e) => VALID_EXTENSIONS.has(extname(e))).map((e) => ({ path: join6(dir, e), source }));
|
|
3943
3969
|
}
|
|
3944
3970
|
function extractTools(mod) {
|
|
3945
3971
|
const tools = [];
|
|
@@ -3969,17 +3995,17 @@ async function loadModule(filePath, importFn) {
|
|
|
3969
3995
|
return extractTools(mod);
|
|
3970
3996
|
}
|
|
3971
3997
|
async function discoverNpmPlugins(cwd) {
|
|
3972
|
-
const nodeModulesDir =
|
|
3998
|
+
const nodeModulesDir = join6(cwd, "node_modules");
|
|
3973
3999
|
if (!await dirExists(nodeModulesDir)) return [];
|
|
3974
4000
|
try {
|
|
3975
4001
|
const entries = await readdir5(nodeModulesDir);
|
|
3976
|
-
return entries.filter((e) => e.startsWith("pi-plugin-")).map((e) =>
|
|
4002
|
+
return entries.filter((e) => e.startsWith("pi-plugin-")).map((e) => join6(nodeModulesDir, e));
|
|
3977
4003
|
} catch {
|
|
3978
4004
|
return [];
|
|
3979
4005
|
}
|
|
3980
4006
|
}
|
|
3981
4007
|
async function loadNpmPlugin(pkgDir, importFn) {
|
|
3982
|
-
const pkgJsonPath =
|
|
4008
|
+
const pkgJsonPath = join6(pkgDir, "package.json");
|
|
3983
4009
|
if (!await fileExists2(pkgJsonPath)) return [];
|
|
3984
4010
|
const pkgJson = JSON.parse(await readFile7(pkgJsonPath, "utf-8"));
|
|
3985
4011
|
const main = pkgJson.main ?? "index.js";
|
|
@@ -3991,7 +4017,7 @@ async function loadNpmPlugin(pkgDir, importFn) {
|
|
|
3991
4017
|
return loadModule(resolvedMain, importFn);
|
|
3992
4018
|
}
|
|
3993
4019
|
async function loadExtensionsJson(cwd) {
|
|
3994
|
-
const configPath =
|
|
4020
|
+
const configPath = join6(cwd, EXTENSIONS_JSON);
|
|
3995
4021
|
if (!await fileExists2(configPath)) return null;
|
|
3996
4022
|
try {
|
|
3997
4023
|
const raw = await readFile7(configPath, "utf-8");
|
|
@@ -4009,7 +4035,7 @@ async function loadPlugins(options) {
|
|
|
4009
4035
|
const globals = await discoverFromDir(GLOBAL_DIR, "global");
|
|
4010
4036
|
candidates.push(...globals);
|
|
4011
4037
|
}
|
|
4012
|
-
const localDir =
|
|
4038
|
+
const localDir = join6(options.cwd, LOCAL_DIR);
|
|
4013
4039
|
const locals = await discoverFromDir(localDir, "local");
|
|
4014
4040
|
candidates.push(...locals);
|
|
4015
4041
|
const npmDirs = await discoverNpmPlugins(options.cwd);
|
|
@@ -4019,7 +4045,7 @@ async function loadPlugins(options) {
|
|
|
4019
4045
|
const config = await loadExtensionsJson(options.cwd);
|
|
4020
4046
|
if (config?.npm) {
|
|
4021
4047
|
for (const pkg of config.npm) {
|
|
4022
|
-
const pkgDir =
|
|
4048
|
+
const pkgDir = join6(options.cwd, "node_modules", pkg);
|
|
4023
4049
|
if (await dirExists(pkgDir)) {
|
|
4024
4050
|
const already = candidates.some((c) => c.path === pkgDir);
|
|
4025
4051
|
if (!already) {
|
|
@@ -4067,9 +4093,9 @@ import {
|
|
|
4067
4093
|
} from "@mariozechner/pi-coding-agent";
|
|
4068
4094
|
|
|
4069
4095
|
// src/server/tools/operations/bound.ts
|
|
4070
|
-
import { constants as
|
|
4071
|
-
import { access as
|
|
4072
|
-
import { dirname as
|
|
4096
|
+
import { constants as constants3 } from "fs";
|
|
4097
|
+
import { access as access3, lstat as lstat3, mkdir as mkdir9, readFile as readFile8, readdir as readdir6, readlink, realpath as realpath2, stat as stat6, writeFile as writeFile7 } from "fs/promises";
|
|
4098
|
+
import { dirname as dirname6, isAbsolute as isAbsolute4, relative as relative4, resolve as resolve5 } from "path";
|
|
4073
4099
|
function toPosixPath(value) {
|
|
4074
4100
|
return value.split("\\").join("/");
|
|
4075
4101
|
}
|
|
@@ -4143,7 +4169,7 @@ async function findNearestExistingAncestor(absPath) {
|
|
|
4143
4169
|
await stat6(current);
|
|
4144
4170
|
return current;
|
|
4145
4171
|
} catch {
|
|
4146
|
-
const parent =
|
|
4172
|
+
const parent = dirname6(current);
|
|
4147
4173
|
if (parent === current) return current;
|
|
4148
4174
|
current = parent;
|
|
4149
4175
|
}
|
|
@@ -4155,7 +4181,7 @@ async function assertWithinWorkspace(workspaceRoot, absPath) {
|
|
|
4155
4181
|
const s = await lstat3(absPath);
|
|
4156
4182
|
if (s.isSymbolicLink()) {
|
|
4157
4183
|
const target = await readlink(absPath);
|
|
4158
|
-
const resolvedTarget = resolve5(
|
|
4184
|
+
const resolvedTarget = resolve5(dirname6(absPath), target);
|
|
4159
4185
|
const nearestAncestor = await findNearestExistingAncestor(resolvedTarget);
|
|
4160
4186
|
const realAncestor = await realpath2(nearestAncestor);
|
|
4161
4187
|
const rel2 = relative4(realRoot, realAncestor);
|
|
@@ -4177,7 +4203,7 @@ async function assertWithinWorkspace(workspaceRoot, absPath) {
|
|
|
4177
4203
|
} catch (err) {
|
|
4178
4204
|
const code = err.code;
|
|
4179
4205
|
if (code === "ENOENT") {
|
|
4180
|
-
const nearestAncestor = await findNearestExistingAncestor(
|
|
4206
|
+
const nearestAncestor = await findNearestExistingAncestor(dirname6(absPath));
|
|
4181
4207
|
const realAncestor = await realpath2(nearestAncestor);
|
|
4182
4208
|
const rel2 = relative4(realRoot, realAncestor);
|
|
4183
4209
|
if (rel2.startsWith("..") || isAbsolute4(rel2)) {
|
|
@@ -4200,13 +4226,13 @@ function boundFs(workspaceRoot) {
|
|
|
4200
4226
|
},
|
|
4201
4227
|
async access(absolutePath) {
|
|
4202
4228
|
await assertWithinWorkspace(workspaceRoot, absolutePath);
|
|
4203
|
-
await
|
|
4229
|
+
await access3(absolutePath, constants3.R_OK);
|
|
4204
4230
|
}
|
|
4205
4231
|
};
|
|
4206
4232
|
const write = {
|
|
4207
4233
|
async writeFile(absolutePath, content) {
|
|
4208
4234
|
await assertWithinWorkspace(workspaceRoot, absolutePath);
|
|
4209
|
-
await mkdir9(
|
|
4235
|
+
await mkdir9(dirname6(absolutePath), { recursive: true });
|
|
4210
4236
|
await writeFile7(absolutePath, content);
|
|
4211
4237
|
},
|
|
4212
4238
|
async mkdir(dir) {
|
|
@@ -4225,7 +4251,7 @@ function boundFs(workspaceRoot) {
|
|
|
4225
4251
|
},
|
|
4226
4252
|
async access(absolutePath) {
|
|
4227
4253
|
await assertWithinWorkspace(workspaceRoot, absolutePath);
|
|
4228
|
-
await
|
|
4254
|
+
await access3(absolutePath, constants3.R_OK | constants3.W_OK);
|
|
4229
4255
|
}
|
|
4230
4256
|
};
|
|
4231
4257
|
const find = {
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hachej/boring-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Pane-embeddable coding agent. Ships direct/local/vercel-sandbox execution modes behind one interface.",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "https://github.com/hachej/boring-ui
|
|
9
|
+
"url": "https://github.com/hachej/boring-ui"
|
|
10
10
|
},
|
|
11
11
|
"homepage": "https://github.com/hachej/boring-ui",
|
|
12
12
|
"exports": {
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"use-stick-to-bottom": "^1.1.3",
|
|
75
75
|
"yaml": "^2.8.3",
|
|
76
76
|
"zod": "^3.25.76",
|
|
77
|
-
"@hachej/boring-ui-kit": "0.1.
|
|
77
|
+
"@hachej/boring-ui-kit": "0.1.5"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
80
|
"@opentelemetry/api": "^1.9.1",
|