@hachej/boring-agent 0.1.12 → 0.1.13
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 +14 -2
- package/dist/server/index.js +44 -16
- package/package.json +2 -2
package/dist/server/index.d.ts
CHANGED
|
@@ -215,9 +215,16 @@ interface VercelSandboxWorkspaceOptions {
|
|
|
215
215
|
}
|
|
216
216
|
declare function createVercelSandboxWorkspace(sandbox: Sandbox$1, workspaceOpts?: VercelSandboxWorkspaceOptions): VercelSandboxWorkspace;
|
|
217
217
|
|
|
218
|
-
type
|
|
218
|
+
type BuiltinRuntimeModeId = 'direct' | 'local' | 'vercel-sandbox';
|
|
219
|
+
type RuntimeModeId = BuiltinRuntimeModeId | (string & {});
|
|
219
220
|
interface RuntimeModeAdapter {
|
|
220
221
|
readonly id: RuntimeModeId;
|
|
222
|
+
/**
|
|
223
|
+
* Declares whether the workspace files are strongly available on the host
|
|
224
|
+
* path before create() runs. Composition layers use this to decide whether
|
|
225
|
+
* host-side fs checks/prompts are safe without hard-coding sandbox IDs.
|
|
226
|
+
*/
|
|
227
|
+
readonly workspaceFsCapability?: Workspace['fsCapability'];
|
|
221
228
|
create(ctx: ModeContext): Promise<RuntimeBundle>;
|
|
222
229
|
dispose?(): Promise<void>;
|
|
223
230
|
}
|
|
@@ -260,6 +267,8 @@ interface CreateAgentAppOptions {
|
|
|
260
267
|
sessionId?: string;
|
|
261
268
|
templatePath?: string;
|
|
262
269
|
mode?: RuntimeModeId;
|
|
270
|
+
/** Supply a custom runtime adapter to plug in non-built-in sandbox/workspace modes. */
|
|
271
|
+
runtimeModeAdapter?: RuntimeModeAdapter;
|
|
263
272
|
authToken?: string;
|
|
264
273
|
version?: string;
|
|
265
274
|
logger?: boolean;
|
|
@@ -295,12 +304,15 @@ interface RegisterAgentRoutesOptions {
|
|
|
295
304
|
request?: FastifyRequest;
|
|
296
305
|
}) => string | undefined | Promise<string | undefined>;
|
|
297
306
|
mode?: RuntimeModeId;
|
|
307
|
+
/** Supply a custom runtime adapter to plug in non-built-in sandbox/workspace modes. */
|
|
308
|
+
runtimeModeAdapter?: RuntimeModeAdapter;
|
|
298
309
|
version?: string;
|
|
299
310
|
extraTools?: AgentTool[];
|
|
300
311
|
getExtraTools?: (ctx: {
|
|
301
312
|
workspaceId: string;
|
|
302
313
|
workspaceRoot: string;
|
|
303
314
|
runtimeMode: RuntimeModeId;
|
|
315
|
+
workspaceFsCapability?: Workspace['fsCapability'];
|
|
304
316
|
}) => AgentTool[] | Promise<AgentTool[]>;
|
|
305
317
|
systemPromptAppend?: string;
|
|
306
318
|
resourceLoaderOptions?: PiResourceLoaderOptions;
|
|
@@ -329,4 +341,4 @@ interface Logger {
|
|
|
329
341
|
}
|
|
330
342
|
declare function createLogger(prefix: string): Logger;
|
|
331
343
|
|
|
332
|
-
export { type CreateAgentAppOptions, type DeploymentSnapshotProvider, type DeploymentSnapshotRecipe, type DeploymentSnapshotResult, type DeploymentSnapshotStatus, FileHandleStore, type LogFields, type Logger, type ModeContext, PI_PACKAGE_RESOURCE_FILTERS, type PiPackageSource, type PiResourceLoaderOptions, type ProvisionRuntimeWorkspaceOptions, type RegisterAgentRoutesOptions, type RuntimeBundle, type RuntimeModeAdapter, type RuntimeModeId, type RuntimeProvisioningContribution, type RuntimePythonSpec, type RuntimeTemplateContribution, type RuntimeWorkspaceProvisioningResult, type SnapshotBakeOptions, type SnapshotBakeResult, UV_SETUP_COMMANDS, UV_SETUP_COMMANDS as VERCEL_UV_SETUP_COMMANDS, type VercelBakeClient, type VercelBakeSandbox, type VercelDeploymentSnapshotOptions, applyCspHeaders, autoDetectMode, bakeSnapshotIfNeeded, buildDeploymentSnapshotRecipe, buildPackageHash, buildSnapshotRecipeHash, compactPiPackages, createAgentApp, createBwrapSandbox, createDirectSandbox, createLogger, createNodeWorkspace, createVercelDeploymentSnapshotProvider, createVercelSandboxWorkspace, hasBwrap, mergePiPackageSources, piPackageSourceKey, prepareDeploymentSnapshot, prepareVercelDeploymentSnapshot, provisionRuntimeWorkspace, registerAgentRoutes, resolveMode, resolveSandboxHandle };
|
|
344
|
+
export { type BuiltinRuntimeModeId, type CreateAgentAppOptions, type DeploymentSnapshotProvider, type DeploymentSnapshotRecipe, type DeploymentSnapshotResult, type DeploymentSnapshotStatus, FileHandleStore, type LogFields, type Logger, type ModeContext, PI_PACKAGE_RESOURCE_FILTERS, type PiPackageSource, type PiResourceLoaderOptions, type ProvisionRuntimeWorkspaceOptions, type RegisterAgentRoutesOptions, type RuntimeBundle, type RuntimeModeAdapter, type RuntimeModeId, type RuntimeProvisioningContribution, type RuntimePythonSpec, type RuntimeTemplateContribution, type RuntimeWorkspaceProvisioningResult, type SnapshotBakeOptions, type SnapshotBakeResult, UV_SETUP_COMMANDS, UV_SETUP_COMMANDS as VERCEL_UV_SETUP_COMMANDS, type VercelBakeClient, type VercelBakeSandbox, type VercelDeploymentSnapshotOptions, applyCspHeaders, autoDetectMode, bakeSnapshotIfNeeded, buildDeploymentSnapshotRecipe, buildPackageHash, buildSnapshotRecipeHash, compactPiPackages, createAgentApp, createBwrapSandbox, createDirectSandbox, createLogger, createNodeWorkspace, createVercelDeploymentSnapshotProvider, createVercelSandboxWorkspace, hasBwrap, mergePiPackageSources, piPackageSourceKey, prepareDeploymentSnapshot, prepareVercelDeploymentSnapshot, provisionRuntimeWorkspace, registerAgentRoutes, resolveMode, resolveSandboxHandle };
|
package/dist/server/index.js
CHANGED
|
@@ -2164,6 +2164,7 @@ async function copyTemplate(templatePath, workspaceRoot) {
|
|
|
2164
2164
|
// src/server/runtime/modes/direct.ts
|
|
2165
2165
|
var directModeAdapter = {
|
|
2166
2166
|
id: "direct",
|
|
2167
|
+
workspaceFsCapability: "strong",
|
|
2167
2168
|
async create(ctx) {
|
|
2168
2169
|
await mkdir6(ctx.workspaceRoot, { recursive: true });
|
|
2169
2170
|
await copyTemplate(ctx.templatePath, ctx.workspaceRoot);
|
|
@@ -2182,6 +2183,7 @@ var directModeAdapter = {
|
|
|
2182
2183
|
import { mkdir as mkdir7 } from "fs/promises";
|
|
2183
2184
|
var localModeAdapter = {
|
|
2184
2185
|
id: "local",
|
|
2186
|
+
workspaceFsCapability: "strong",
|
|
2185
2187
|
async create(ctx) {
|
|
2186
2188
|
if (process.platform !== "linux") {
|
|
2187
2189
|
throw new Error("local mode requires Linux with bubblewrap");
|
|
@@ -2249,17 +2251,32 @@ function toRemotePath(value) {
|
|
|
2249
2251
|
}
|
|
2250
2252
|
return value;
|
|
2251
2253
|
}
|
|
2254
|
+
function escapeRegExp(value) {
|
|
2255
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2256
|
+
}
|
|
2257
|
+
var WORKSPACE_ALIAS_PREFIX_BOUNDARY = String.raw`(^|[\s"'=:;,\[({<>&|])`;
|
|
2258
|
+
var WORKSPACE_ALIAS_SUFFIX_BOUNDARY = String.raw`(?=/|$|[\s"'=:;,)\]}> &|])`;
|
|
2259
|
+
var WORKSPACE_ALIAS_PATTERN = new RegExp(
|
|
2260
|
+
`${WORKSPACE_ALIAS_PREFIX_BOUNDARY}${escapeRegExp(VERCEL_SANDBOX_WORKSPACE_ROOT)}${WORKSPACE_ALIAS_SUFFIX_BOUNDARY}`,
|
|
2261
|
+
"g"
|
|
2262
|
+
);
|
|
2263
|
+
function replaceWorkspaceAliases(value) {
|
|
2264
|
+
return value.replace(
|
|
2265
|
+
WORKSPACE_ALIAS_PATTERN,
|
|
2266
|
+
(_match, prefix) => `${prefix}${VERCEL_SANDBOX_REMOTE_ROOT}`
|
|
2267
|
+
);
|
|
2268
|
+
}
|
|
2252
2269
|
function toRemoteCwd(cwd) {
|
|
2253
2270
|
if (!cwd) return cwd;
|
|
2254
2271
|
return toRemotePath(cwd);
|
|
2255
2272
|
}
|
|
2256
2273
|
function toRemoteCommand(command) {
|
|
2257
|
-
return command
|
|
2274
|
+
return replaceWorkspaceAliases(command);
|
|
2258
2275
|
}
|
|
2259
2276
|
function toRemoteEnv(env) {
|
|
2260
2277
|
if (!env) return void 0;
|
|
2261
2278
|
return Object.fromEntries(
|
|
2262
|
-
Object.entries(env).map(([key, value]) => [key, toRemotePath(value)])
|
|
2279
|
+
Object.entries(env).map(([key, value]) => [key, replaceWorkspaceAliases(toRemotePath(value))])
|
|
2263
2280
|
);
|
|
2264
2281
|
}
|
|
2265
2282
|
function createVercelSandboxExec(sandbox, execOpts = {}) {
|
|
@@ -2590,6 +2607,7 @@ function createVercelSandboxModeAdapter(opts = {}) {
|
|
|
2590
2607
|
const snapshotScheduler = opts.snapshotScheduler ?? null;
|
|
2591
2608
|
return {
|
|
2592
2609
|
id: "vercel-sandbox",
|
|
2610
|
+
workspaceFsCapability: "best-effort",
|
|
2593
2611
|
async dispose() {
|
|
2594
2612
|
await snapshotScheduler?.shutdown();
|
|
2595
2613
|
},
|
|
@@ -2686,7 +2704,7 @@ var MODE_ADAPTERS = {
|
|
|
2686
2704
|
local: localModeAdapter,
|
|
2687
2705
|
"vercel-sandbox": vercelSandboxModeAdapter
|
|
2688
2706
|
};
|
|
2689
|
-
function
|
|
2707
|
+
function isBuiltinRuntimeModeId(value) {
|
|
2690
2708
|
return value === "direct" || value === "local" || value === "vercel-sandbox";
|
|
2691
2709
|
}
|
|
2692
2710
|
function hasBwrap() {
|
|
@@ -2696,7 +2714,7 @@ function hasBwrap() {
|
|
|
2696
2714
|
function autoDetectMode() {
|
|
2697
2715
|
const explicitMode = getEnv("BORING_AGENT_MODE");
|
|
2698
2716
|
if (explicitMode) {
|
|
2699
|
-
if (!
|
|
2717
|
+
if (!isBuiltinRuntimeModeId(explicitMode)) {
|
|
2700
2718
|
throw new Error(
|
|
2701
2719
|
`Invalid BORING_AGENT_MODE "${explicitMode}". Expected direct, local, or vercel-sandbox.`
|
|
2702
2720
|
);
|
|
@@ -2709,7 +2727,8 @@ function autoDetectMode() {
|
|
|
2709
2727
|
return "direct";
|
|
2710
2728
|
}
|
|
2711
2729
|
function resolveMode(mode = autoDetectMode()) {
|
|
2712
|
-
return MODE_ADAPTERS[mode];
|
|
2730
|
+
if (isBuiltinRuntimeModeId(mode)) return MODE_ADAPTERS[mode];
|
|
2731
|
+
throw new Error(`Runtime mode "${mode}" has no built-in adapter. Pass runtimeModeAdapter to use a custom sandbox mode.`);
|
|
2713
2732
|
}
|
|
2714
2733
|
|
|
2715
2734
|
// src/server/createAgentApp.ts
|
|
@@ -4554,22 +4573,29 @@ function boundFs(workspaceRoot) {
|
|
|
4554
4573
|
}
|
|
4555
4574
|
|
|
4556
4575
|
// src/server/tools/operations/vercel.ts
|
|
4557
|
-
import { relative as relative5 } from "path";
|
|
4576
|
+
import { isAbsolute as isAbsolute5, relative as relative5 } from "path";
|
|
4558
4577
|
function rootAliases(workspace) {
|
|
4559
4578
|
const aliases = [workspace.root];
|
|
4560
4579
|
if (workspace.root === VERCEL_SANDBOX_WORKSPACE_ROOT) aliases.push(VERCEL_SANDBOX_REMOTE_ROOT);
|
|
4561
4580
|
if (workspace.root === VERCEL_SANDBOX_REMOTE_ROOT) aliases.push(VERCEL_SANDBOX_WORKSPACE_ROOT);
|
|
4562
4581
|
return aliases;
|
|
4563
4582
|
}
|
|
4583
|
+
function isOutsideWorkspaceRel(rel) {
|
|
4584
|
+
return rel === ".." || rel.startsWith("../") || rel.startsWith("..\\") || isAbsolute5(rel);
|
|
4585
|
+
}
|
|
4564
4586
|
function toRelPath(workspace, absolutePath) {
|
|
4565
4587
|
for (const root of rootAliases(workspace)) {
|
|
4566
4588
|
const rel = relative5(root, absolutePath);
|
|
4567
|
-
if (!
|
|
4589
|
+
if (!isOutsideWorkspaceRel(rel)) return rel;
|
|
4568
4590
|
}
|
|
4569
4591
|
const skillMarker = "/.agents/skills/";
|
|
4570
4592
|
const skillIndex = absolutePath.indexOf(skillMarker);
|
|
4571
4593
|
if (skillIndex >= 0) {
|
|
4572
|
-
|
|
4594
|
+
const skillPath = absolutePath.slice(skillIndex + skillMarker.length);
|
|
4595
|
+
if (skillPath.includes("\0") || skillPath.split(/[\\/]+/).includes("..")) {
|
|
4596
|
+
throw new Error(`path "${absolutePath}" escapes the workspace skills directory`);
|
|
4597
|
+
}
|
|
4598
|
+
return `.agents/skills/${skillPath}`;
|
|
4573
4599
|
}
|
|
4574
4600
|
throw new Error(
|
|
4575
4601
|
`path "${absolutePath}" is outside workspace; use a path relative to the workspace root or under ${rootAliases(workspace).join(" / ")}`
|
|
@@ -4663,7 +4689,7 @@ function fallbackFindCommand(pattern, cwd, options) {
|
|
|
4663
4689
|
function isFdMissing(result) {
|
|
4664
4690
|
if (result.exitCode !== 127) return false;
|
|
4665
4691
|
const stderr = Buffer.from(result.stderr).toString("utf-8");
|
|
4666
|
-
return
|
|
4692
|
+
return /\bfd: (?:not found|command not found)\b/i.test(stderr);
|
|
4667
4693
|
}
|
|
4668
4694
|
function vercelFindOps(sandbox, workspace) {
|
|
4669
4695
|
return {
|
|
@@ -6998,14 +7024,15 @@ async function createAgentApp(opts = {}) {
|
|
|
6998
7024
|
const sessionId = opts.sessionId ?? DEFAULT_SESSION_ID;
|
|
6999
7025
|
const templatePath = opts.templatePath ?? getEnv("BORING_AGENT_TEMPLATE_PATH");
|
|
7000
7026
|
const app = Fastify({ logger: opts.logger ?? true, bodyLimit: 16 * 1024 * 1024 });
|
|
7001
|
-
const resolvedMode = opts.mode ?? autoDetectMode();
|
|
7002
|
-
const
|
|
7027
|
+
const resolvedMode = opts.runtimeModeAdapter?.id ?? opts.mode ?? autoDetectMode();
|
|
7028
|
+
const modeAdapter = opts.runtimeModeAdapter ?? resolveMode(resolvedMode);
|
|
7029
|
+
const runtimeBundle = await modeAdapter.create({
|
|
7003
7030
|
workspaceRoot,
|
|
7004
7031
|
sessionId,
|
|
7005
7032
|
templatePath
|
|
7006
7033
|
});
|
|
7007
7034
|
const pluginTools = [];
|
|
7008
|
-
if (
|
|
7035
|
+
if (modeAdapter.workspaceFsCapability === "strong") {
|
|
7009
7036
|
const pluginResult = await loadPlugins({ cwd: workspaceRoot });
|
|
7010
7037
|
if (pluginResult.errors.length > 0) {
|
|
7011
7038
|
for (const e of pluginResult.errors) {
|
|
@@ -7293,8 +7320,8 @@ var registerAgentRoutes = async (app, opts) => {
|
|
|
7293
7320
|
const workspaceRoot = opts.workspaceRoot ?? process.cwd();
|
|
7294
7321
|
const sessionId = opts.sessionId ?? DEFAULT_WORKSPACE_ID3;
|
|
7295
7322
|
const templatePath = opts.templatePath ?? getEnv("BORING_AGENT_TEMPLATE_PATH");
|
|
7296
|
-
const resolvedMode = opts.mode ?? autoDetectMode();
|
|
7297
|
-
const modeAdapter = selectRuntimeModeAdapter(resolvedMode, opts.sandboxHandleStore);
|
|
7323
|
+
const resolvedMode = opts.runtimeModeAdapter?.id ?? opts.mode ?? autoDetectMode();
|
|
7324
|
+
const modeAdapter = opts.runtimeModeAdapter ?? selectRuntimeModeAdapter(resolvedMode, opts.sandboxHandleStore);
|
|
7298
7325
|
app.addHook("onClose", async () => {
|
|
7299
7326
|
await modeAdapter.dispose?.();
|
|
7300
7327
|
});
|
|
@@ -7322,7 +7349,7 @@ var registerAgentRoutes = async (app, opts) => {
|
|
|
7322
7349
|
...buildUploadAgentTools(runtimeBundle)
|
|
7323
7350
|
];
|
|
7324
7351
|
const pluginTools = [];
|
|
7325
|
-
if (
|
|
7352
|
+
if (modeAdapter.workspaceFsCapability === "strong") {
|
|
7326
7353
|
const pluginResult = await loadPlugins({ cwd: root });
|
|
7327
7354
|
if (pluginResult.errors.length > 0) {
|
|
7328
7355
|
for (const e of pluginResult.errors) {
|
|
@@ -7339,7 +7366,8 @@ var registerAgentRoutes = async (app, opts) => {
|
|
|
7339
7366
|
const scopedExtraTools = opts.getExtraTools ? await opts.getExtraTools({
|
|
7340
7367
|
workspaceId,
|
|
7341
7368
|
workspaceRoot: root,
|
|
7342
|
-
runtimeMode: resolvedMode
|
|
7369
|
+
runtimeMode: resolvedMode,
|
|
7370
|
+
workspaceFsCapability: runtimeBundle.workspace.fsCapability
|
|
7343
7371
|
}) : [];
|
|
7344
7372
|
const tools = mergeTools({
|
|
7345
7373
|
standardTools,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hachej/boring-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
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.",
|
|
@@ -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.13"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
80
|
"@opentelemetry/api": "^1.9.1",
|