@hachej/boring-agent 0.1.27 → 0.1.28
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/server/index.d.ts +4 -0
- package/dist/server/index.js +52 -16
- package/package.json +2 -2
package/dist/server/index.d.ts
CHANGED
|
@@ -134,6 +134,10 @@ declare function buildSnapshotRecipeHash(input: {
|
|
|
134
134
|
}): string;
|
|
135
135
|
declare function bakeSnapshotIfNeeded(opts: SnapshotBakeOptions): Promise<SnapshotBakeResult>;
|
|
136
136
|
|
|
137
|
+
/**
|
|
138
|
+
* uv setup for Python-family runtimes (e.g. Vercel `python3.13`), which already
|
|
139
|
+
* ship `pip` and usually `uv`. Verify-or-install via the on-PATH `uv`.
|
|
140
|
+
*/
|
|
137
141
|
declare const UV_SETUP_COMMANDS: readonly ["command -v uv >/dev/null 2>&1 || python3 -m pip install --upgrade uv", "uv --version"];
|
|
138
142
|
type DeploymentSnapshotStatus = 'skipped' | 'cache-hit' | 'baked' | 'failed';
|
|
139
143
|
interface DeploymentSnapshotRecipe {
|
package/dist/server/index.js
CHANGED
|
@@ -1357,7 +1357,7 @@ function shellQuote(value) {
|
|
|
1357
1357
|
function makeInstallCommand(binary, packages) {
|
|
1358
1358
|
const quoted = packages.map(shellQuote).join(" ");
|
|
1359
1359
|
if (binary === "dnf") {
|
|
1360
|
-
return `dnf install -y ${quoted}`;
|
|
1360
|
+
return `sudo dnf install -y ${quoted}`;
|
|
1361
1361
|
}
|
|
1362
1362
|
return `python3 -m pip install ${quoted}`;
|
|
1363
1363
|
}
|
|
@@ -1550,17 +1550,29 @@ async function bakeSnapshotIfNeeded(opts) {
|
|
|
1550
1550
|
}
|
|
1551
1551
|
|
|
1552
1552
|
// src/server/sandbox/snapshots/deploymentSnapshot.ts
|
|
1553
|
+
var VERCEL_UV_BIN = "/home/vercel-sandbox/.local/bin/uv";
|
|
1553
1554
|
var UV_SETUP_COMMANDS = [
|
|
1554
1555
|
"command -v uv >/dev/null 2>&1 || python3 -m pip install --upgrade uv",
|
|
1555
1556
|
"uv --version"
|
|
1556
1557
|
];
|
|
1558
|
+
var NODE_UV_SETUP_COMMANDS = [
|
|
1559
|
+
"command -v pip3 >/dev/null 2>&1 || sudo dnf install -y python3-pip",
|
|
1560
|
+
`[ -x ${VERCEL_UV_BIN} ] || python3 -m pip install --user --upgrade uv`,
|
|
1561
|
+
`${VERCEL_UV_BIN} --version`
|
|
1562
|
+
];
|
|
1563
|
+
function isNodeFamilyRuntime(runtime) {
|
|
1564
|
+
return typeof runtime === "string" && /^node/i.test(runtime.trim());
|
|
1565
|
+
}
|
|
1566
|
+
function uvSetupCommandsForRuntime(runtime) {
|
|
1567
|
+
return isNodeFamilyRuntime(runtime) ? NODE_UV_SETUP_COMMANDS : UV_SETUP_COMMANDS;
|
|
1568
|
+
}
|
|
1557
1569
|
function buildDeploymentSnapshotRecipe(opts = {}) {
|
|
1558
1570
|
return {
|
|
1559
1571
|
runtime: opts.runtime,
|
|
1560
1572
|
systemPackages: opts.systemPackages,
|
|
1561
1573
|
pythonPackages: opts.pythonPackages,
|
|
1562
1574
|
setupCommands: [
|
|
1563
|
-
...opts.includeUv === false ? [] :
|
|
1575
|
+
...opts.includeUv === false ? [] : uvSetupCommandsForRuntime(opts.runtime),
|
|
1564
1576
|
...opts.setupCommands ?? []
|
|
1565
1577
|
]
|
|
1566
1578
|
};
|
|
@@ -3150,6 +3162,17 @@ async function commandOutput2(adapter, command, args) {
|
|
|
3150
3162
|
return result?.stdout?.trim() ?? "";
|
|
3151
3163
|
}
|
|
3152
3164
|
async function ensureUv(options) {
|
|
3165
|
+
const explicitUvBin = options.explicitUvBin?.trim();
|
|
3166
|
+
if (explicitUvBin) {
|
|
3167
|
+
try {
|
|
3168
|
+
return {
|
|
3169
|
+
uvBin: explicitUvBin,
|
|
3170
|
+
uvVersion: await commandOutput2(options.adapter, explicitUvBin, ["--version"]) || "uv unknown",
|
|
3171
|
+
installedWorkspaceUv: false
|
|
3172
|
+
};
|
|
3173
|
+
} catch {
|
|
3174
|
+
}
|
|
3175
|
+
}
|
|
3153
3176
|
try {
|
|
3154
3177
|
return {
|
|
3155
3178
|
uvBin: "uv",
|
|
@@ -3619,7 +3642,11 @@ async function provisionWorkspaceRuntime(opts) {
|
|
|
3619
3642
|
run: () => ensurePythonRuntime({
|
|
3620
3643
|
adapter: opts.adapter,
|
|
3621
3644
|
runtimeLayout: opts.runtimeLayout,
|
|
3622
|
-
packages: pythonPackages
|
|
3645
|
+
packages: pythonPackages,
|
|
3646
|
+
// Provider-neutral seam: a deploy/provider (e.g. Vercel Node runtime) may
|
|
3647
|
+
// export the explicit uv path since it is not on the non-interactive exec
|
|
3648
|
+
// PATH. Unset for direct/local — they fall back to bare `uv`.
|
|
3649
|
+
explicitUvBin: getEnv("BORING_AGENT_UV_BIN")?.trim() || void 0
|
|
3623
3650
|
})
|
|
3624
3651
|
});
|
|
3625
3652
|
const result = {
|
|
@@ -3739,20 +3766,26 @@ var LOCAL_SANDBOX_WORKSPACE_ROOT = "/workspace";
|
|
|
3739
3766
|
function sourceToPath4(source) {
|
|
3740
3767
|
return source instanceof URL ? fileURLToPath5(source) : source;
|
|
3741
3768
|
}
|
|
3742
|
-
async function assertExistingInsideWorkspace(root, relPath) {
|
|
3769
|
+
async function assertExistingInsideWorkspace(root, relPath, enforceSymlinkBoundary) {
|
|
3743
3770
|
const absPath = validatePath(root, relPath);
|
|
3744
3771
|
try {
|
|
3745
|
-
|
|
3772
|
+
if (enforceSymlinkBoundary) {
|
|
3773
|
+
await assertRealPathWithinWorkspace(root, absPath);
|
|
3774
|
+
} else {
|
|
3775
|
+
await lstat3(absPath);
|
|
3776
|
+
}
|
|
3746
3777
|
return absPath;
|
|
3747
3778
|
} catch (error) {
|
|
3748
3779
|
if (error.code === "ENOENT") return null;
|
|
3749
3780
|
throw error;
|
|
3750
3781
|
}
|
|
3751
3782
|
}
|
|
3752
|
-
async function prepareWritablePath(root, relPath) {
|
|
3783
|
+
async function prepareWritablePath(root, relPath, enforceSymlinkBoundary) {
|
|
3753
3784
|
const absPath = validatePath(root, relPath);
|
|
3754
3785
|
await mkdir6(dirname9(absPath), { recursive: true });
|
|
3755
|
-
|
|
3786
|
+
if (enforceSymlinkBoundary) {
|
|
3787
|
+
await assertRealPathWithinWorkspace(root, dirname9(absPath));
|
|
3788
|
+
}
|
|
3756
3789
|
try {
|
|
3757
3790
|
const targetStat = await lstat3(absPath);
|
|
3758
3791
|
if (targetStat.isSymbolicLink()) {
|
|
@@ -3848,36 +3881,39 @@ async function copyExternalSourceIntoWorkspace(paths, sourcePath, opts) {
|
|
|
3848
3881
|
});
|
|
3849
3882
|
return `${LOCAL_SANDBOX_WORKSPACE_ROOT}/${relTarget}`;
|
|
3850
3883
|
}
|
|
3851
|
-
function createWorkspaceFs(workspaceRoot) {
|
|
3884
|
+
function createWorkspaceFs(workspaceRoot, opts) {
|
|
3885
|
+
const { enforceSymlinkBoundary } = opts;
|
|
3852
3886
|
return {
|
|
3853
3887
|
async exists(workspaceRelativePath) {
|
|
3854
|
-
const absPath = await assertExistingInsideWorkspace(workspaceRoot, workspaceRelativePath);
|
|
3888
|
+
const absPath = await assertExistingInsideWorkspace(workspaceRoot, workspaceRelativePath, enforceSymlinkBoundary);
|
|
3855
3889
|
if (!absPath) return false;
|
|
3856
3890
|
await lstat3(absPath);
|
|
3857
3891
|
return true;
|
|
3858
3892
|
},
|
|
3859
3893
|
async rm(workspaceRelativePath) {
|
|
3860
|
-
const absPath = await assertExistingInsideWorkspace(workspaceRoot, workspaceRelativePath);
|
|
3894
|
+
const absPath = await assertExistingInsideWorkspace(workspaceRoot, workspaceRelativePath, enforceSymlinkBoundary);
|
|
3861
3895
|
if (!absPath) return;
|
|
3862
3896
|
await rm3(absPath, { recursive: true, force: true });
|
|
3863
3897
|
},
|
|
3864
3898
|
async mkdir(workspaceRelativePath) {
|
|
3865
3899
|
const absPath = validatePath(workspaceRoot, workspaceRelativePath);
|
|
3866
3900
|
await mkdir6(absPath, { recursive: true });
|
|
3867
|
-
|
|
3901
|
+
if (enforceSymlinkBoundary) {
|
|
3902
|
+
await assertRealPathWithinWorkspace(workspaceRoot, absPath);
|
|
3903
|
+
}
|
|
3868
3904
|
},
|
|
3869
3905
|
async writeText(workspaceRelativePath, content) {
|
|
3870
|
-
const absPath = await prepareWritablePath(workspaceRoot, workspaceRelativePath);
|
|
3906
|
+
const absPath = await prepareWritablePath(workspaceRoot, workspaceRelativePath, enforceSymlinkBoundary);
|
|
3871
3907
|
await writeFile5(absPath, content, "utf8");
|
|
3872
3908
|
},
|
|
3873
3909
|
async readText(workspaceRelativePath) {
|
|
3874
|
-
const absPath = await assertExistingInsideWorkspace(workspaceRoot, workspaceRelativePath);
|
|
3910
|
+
const absPath = await assertExistingInsideWorkspace(workspaceRoot, workspaceRelativePath, enforceSymlinkBoundary);
|
|
3875
3911
|
if (!absPath) return null;
|
|
3876
3912
|
return await readFile6(absPath, "utf8");
|
|
3877
3913
|
},
|
|
3878
3914
|
async copyFromHost(hostSourcePath, workspaceRelativeTarget) {
|
|
3879
3915
|
const sourcePath = sourceToPath4(hostSourcePath);
|
|
3880
|
-
const absTarget = await prepareWritablePath(workspaceRoot, workspaceRelativeTarget);
|
|
3916
|
+
const absTarget = await prepareWritablePath(workspaceRoot, workspaceRelativeTarget, enforceSymlinkBoundary);
|
|
3881
3917
|
const sourceStat = await stat7(sourcePath);
|
|
3882
3918
|
await cp3(sourcePath, absTarget, {
|
|
3883
3919
|
recursive: sourceStat.isDirectory(),
|
|
@@ -3896,7 +3932,7 @@ function createDirectProvisioningAdapter(paths, runner = spawnCommand) {
|
|
|
3896
3932
|
async resolveInstallSource(source) {
|
|
3897
3933
|
return sourceToPath4(source);
|
|
3898
3934
|
},
|
|
3899
|
-
workspaceFs: createWorkspaceFs(paths.workspaceRoot),
|
|
3935
|
+
workspaceFs: createWorkspaceFs(paths.workspaceRoot, { enforceSymlinkBoundary: false }),
|
|
3900
3936
|
getRuntimeCacheRoot() {
|
|
3901
3937
|
return paths.cache;
|
|
3902
3938
|
}
|
|
@@ -3938,7 +3974,7 @@ function createLocalProvisioningAdapter(paths, runner = spawnCommand) {
|
|
|
3938
3974
|
}
|
|
3939
3975
|
return await copyExternalSourceIntoWorkspace(paths, realSource, opts);
|
|
3940
3976
|
},
|
|
3941
|
-
workspaceFs: createWorkspaceFs(paths.workspaceRoot),
|
|
3977
|
+
workspaceFs: createWorkspaceFs(paths.workspaceRoot, { enforceSymlinkBoundary: true }),
|
|
3942
3978
|
getRuntimeCacheRoot() {
|
|
3943
3979
|
return paths.cache;
|
|
3944
3980
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hachej/boring-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.28",
|
|
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.",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"use-stick-to-bottom": "^1.1.3",
|
|
76
76
|
"yaml": "^2.8.3",
|
|
77
77
|
"zod": "^3.25.76",
|
|
78
|
-
"@hachej/boring-ui-kit": "0.1.
|
|
78
|
+
"@hachej/boring-ui-kit": "0.1.28"
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
81
|
"@opentelemetry/api": "^1.9.1",
|