@bike4mind/cli 0.2.29-feat-cli-sandbox.18843 → 0.2.29-feat-session-permission.18841
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/{chunk-JWEG53NG.js → chunk-LBTTUQJM.js} +3 -65
- package/dist/commands/mcpCommand.js +1 -2
- package/dist/index.js +51 -247
- package/package.json +6 -6
- package/dist/BubblewrapRuntime-HXTR3NQG.js +0 -68
- package/dist/SandboxOrchestrator-Z36SFJ7X.js +0 -110
- package/dist/SandboxRuntimeAdapter-5ZMU25Q3.js +0 -13
- package/dist/SeatbeltRuntime-SL7HVS3J.js +0 -96
- package/dist/chunk-AVAD2PPK.js +0 -44
- package/dist/chunk-W4TCH4ZT.js +0 -27
- package/dist/types-SMQKWHQK.js +0 -7
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
DEFAULT_SANDBOX_CONFIG
|
|
4
|
-
} from "./chunk-W4TCH4ZT.js";
|
|
5
2
|
|
|
6
3
|
// src/utils/Logger.ts
|
|
7
4
|
import fs from "fs/promises";
|
|
@@ -265,41 +262,6 @@ import path2 from "path";
|
|
|
265
262
|
import { homedir } from "os";
|
|
266
263
|
import { v4 as uuidv4 } from "uuid";
|
|
267
264
|
import { z } from "zod";
|
|
268
|
-
var SandboxFilesystemSchema = z.object({
|
|
269
|
-
allowedReadPaths: z.array(z.string()).default(DEFAULT_SANDBOX_CONFIG.filesystem.allowedReadPaths),
|
|
270
|
-
deniedPaths: z.array(z.string()).default(DEFAULT_SANDBOX_CONFIG.filesystem.deniedPaths),
|
|
271
|
-
writeOnlyToWorkingDir: z.boolean().default(true)
|
|
272
|
-
});
|
|
273
|
-
var SandboxPlatformSchema = z.object({
|
|
274
|
-
linux: z.object({
|
|
275
|
-
runtime: z.literal("bubblewrap").default("bubblewrap"),
|
|
276
|
-
seccompProfile: z.string().optional()
|
|
277
|
-
}).default({ runtime: "bubblewrap" }),
|
|
278
|
-
macos: z.object({
|
|
279
|
-
runtime: z.literal("seatbelt").default("seatbelt"),
|
|
280
|
-
profileTemplate: z.string().default("default")
|
|
281
|
-
}).default({ runtime: "seatbelt", profileTemplate: "default" })
|
|
282
|
-
});
|
|
283
|
-
var SandboxConfigSchema = z.object({
|
|
284
|
-
enabled: z.boolean().default(false),
|
|
285
|
-
mode: z.enum(["disabled", "auto-allow", "permissions"]).default("disabled"),
|
|
286
|
-
filesystem: SandboxFilesystemSchema.default(DEFAULT_SANDBOX_CONFIG.filesystem),
|
|
287
|
-
excludedCommands: z.array(z.string()).default(DEFAULT_SANDBOX_CONFIG.excludedCommands),
|
|
288
|
-
allowUnsandboxedCommands: z.boolean().default(true),
|
|
289
|
-
platform: SandboxPlatformSchema.default(DEFAULT_SANDBOX_CONFIG.platform)
|
|
290
|
-
});
|
|
291
|
-
var PartialSandboxConfigSchema = z.object({
|
|
292
|
-
enabled: z.boolean().optional(),
|
|
293
|
-
mode: z.enum(["disabled", "auto-allow", "permissions"]).optional(),
|
|
294
|
-
filesystem: z.object({
|
|
295
|
-
allowedReadPaths: z.array(z.string()).optional(),
|
|
296
|
-
deniedPaths: z.array(z.string()).optional(),
|
|
297
|
-
writeOnlyToWorkingDir: z.boolean().optional()
|
|
298
|
-
}).optional(),
|
|
299
|
-
excludedCommands: z.array(z.string()).optional(),
|
|
300
|
-
allowUnsandboxedCommands: z.boolean().optional(),
|
|
301
|
-
platform: SandboxPlatformSchema.optional()
|
|
302
|
-
}).optional();
|
|
303
265
|
var AuthTokensSchema = z.object({
|
|
304
266
|
accessToken: z.string(),
|
|
305
267
|
refreshToken: z.string(),
|
|
@@ -374,8 +336,7 @@ var CliConfigSchema = z.object({
|
|
|
374
336
|
disabled: z.array(z.string()),
|
|
375
337
|
config: z.record(z.any())
|
|
376
338
|
}),
|
|
377
|
-
trustedTools: z.array(z.string()).optional().default([])
|
|
378
|
-
sandbox: SandboxConfigSchema.optional()
|
|
339
|
+
trustedTools: z.array(z.string()).optional().default([])
|
|
379
340
|
});
|
|
380
341
|
var ProjectConfigSchema = z.object({
|
|
381
342
|
tools: z.object({
|
|
@@ -393,8 +354,7 @@ var ProjectConfigSchema = z.object({
|
|
|
393
354
|
theme: z.enum(["light", "dark"]).optional(),
|
|
394
355
|
exportFormat: z.enum(["markdown", "json"]).optional(),
|
|
395
356
|
enableSkillTool: z.boolean().optional()
|
|
396
|
-
}).optional()
|
|
397
|
-
sandbox: PartialSandboxConfigSchema
|
|
357
|
+
}).optional()
|
|
398
358
|
});
|
|
399
359
|
var ProjectLocalConfigSchema = z.object({
|
|
400
360
|
trustedTools: z.array(z.string()).optional(),
|
|
@@ -411,8 +371,7 @@ var ProjectLocalConfigSchema = z.object({
|
|
|
411
371
|
exportFormat: z.enum(["markdown", "json"]).optional(),
|
|
412
372
|
enableSkillTool: z.boolean().optional()
|
|
413
373
|
}).optional(),
|
|
414
|
-
mcpServers: McpServersSchema.optional()
|
|
415
|
-
sandbox: PartialSandboxConfigSchema
|
|
374
|
+
mcpServers: McpServersSchema.optional()
|
|
416
375
|
});
|
|
417
376
|
var DEFAULT_CONFIG = {
|
|
418
377
|
version: "0.1.0",
|
|
@@ -537,21 +496,6 @@ function mergeMcpServers(...serverArrays) {
|
|
|
537
496
|
}
|
|
538
497
|
return Array.from(serverMap.values());
|
|
539
498
|
}
|
|
540
|
-
function mergeSandboxConfig(base, override) {
|
|
541
|
-
const resolved = base ?? DEFAULT_SANDBOX_CONFIG;
|
|
542
|
-
if (!override) return resolved;
|
|
543
|
-
return {
|
|
544
|
-
enabled: override.enabled ?? resolved.enabled,
|
|
545
|
-
mode: override.mode ?? resolved.mode,
|
|
546
|
-
filesystem: {
|
|
547
|
-
...resolved.filesystem,
|
|
548
|
-
...override.filesystem ?? {}
|
|
549
|
-
},
|
|
550
|
-
excludedCommands: override.excludedCommands ?? resolved.excludedCommands,
|
|
551
|
-
allowUnsandboxedCommands: override.allowUnsandboxedCommands ?? resolved.allowUnsandboxedCommands,
|
|
552
|
-
platform: override.platform ?? resolved.platform
|
|
553
|
-
};
|
|
554
|
-
}
|
|
555
499
|
function mergeConfigs(global, project, local) {
|
|
556
500
|
const merged = { ...global };
|
|
557
501
|
if (project) {
|
|
@@ -578,9 +522,6 @@ function mergeConfigs(global, project, local) {
|
|
|
578
522
|
if (project.mcpServers) {
|
|
579
523
|
merged.mcpServers = mergeMcpServers(merged.mcpServers, project.mcpServers);
|
|
580
524
|
}
|
|
581
|
-
if (project.sandbox) {
|
|
582
|
-
merged.sandbox = mergeSandboxConfig(merged.sandbox, project.sandbox);
|
|
583
|
-
}
|
|
584
525
|
}
|
|
585
526
|
if (local) {
|
|
586
527
|
if (local.trustedTools) {
|
|
@@ -602,9 +543,6 @@ function mergeConfigs(global, project, local) {
|
|
|
602
543
|
if (local.mcpServers) {
|
|
603
544
|
merged.mcpServers = mergeMcpServers(merged.mcpServers, local.mcpServers);
|
|
604
545
|
}
|
|
605
|
-
if (local.sandbox) {
|
|
606
|
-
merged.sandbox = mergeSandboxConfig(merged.sandbox, local.sandbox);
|
|
607
|
-
}
|
|
608
546
|
}
|
|
609
547
|
return merged;
|
|
610
548
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./chunk-ELTTBWAQ.js";
|
|
3
2
|
import "./chunk-BPFEGDC7.js";
|
|
4
3
|
import "./chunk-BDQBOLYG.js";
|
|
5
4
|
import {
|
|
@@ -10,13 +9,13 @@ import {
|
|
|
10
9
|
import {
|
|
11
10
|
ConfigStore,
|
|
12
11
|
logger
|
|
13
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-LBTTUQJM.js";
|
|
14
13
|
import {
|
|
15
14
|
selectActiveBackgroundAgents,
|
|
16
15
|
useCliStore
|
|
17
16
|
} from "./chunk-TVW4ZESU.js";
|
|
18
|
-
import "./chunk-W4TCH4ZT.js";
|
|
19
17
|
import "./chunk-PKKJ44C5.js";
|
|
18
|
+
import "./chunk-ELTTBWAQ.js";
|
|
20
19
|
import {
|
|
21
20
|
BFLImageService,
|
|
22
21
|
BaseStorage,
|
|
@@ -646,24 +645,6 @@ var COMMANDS = [
|
|
|
646
645
|
name: "compact",
|
|
647
646
|
description: "Compact conversation into new session",
|
|
648
647
|
args: "[instructions]"
|
|
649
|
-
},
|
|
650
|
-
// Sandbox commands
|
|
651
|
-
{
|
|
652
|
-
name: "sandbox",
|
|
653
|
-
description: "Show sandbox status and configuration"
|
|
654
|
-
},
|
|
655
|
-
{
|
|
656
|
-
name: "sandbox:enable",
|
|
657
|
-
description: "Enable OS-level sandbox for bash commands"
|
|
658
|
-
},
|
|
659
|
-
{
|
|
660
|
-
name: "sandbox:disable",
|
|
661
|
-
description: "Disable sandbox mode"
|
|
662
|
-
},
|
|
663
|
-
{
|
|
664
|
-
name: "sandbox:mode",
|
|
665
|
-
description: "Set sandbox mode (auto-allow or permissions)",
|
|
666
|
-
args: "<auto-allow|permissions>"
|
|
667
648
|
}
|
|
668
649
|
];
|
|
669
650
|
function getAllCommandNames() {
|
|
@@ -1279,10 +1260,12 @@ function PermissionPrompt({
|
|
|
1279
1260
|
}) {
|
|
1280
1261
|
const items = canBeTrusted ? [
|
|
1281
1262
|
{ label: "\u2713 Allow once", value: "allow-once" },
|
|
1263
|
+
{ label: "\u2713 Allow for this session", value: "allow-session" },
|
|
1282
1264
|
{ label: "\u2713 Always allow (trust this tool)", value: "allow-always" },
|
|
1283
1265
|
{ label: "\u2717 Deny", value: "deny" }
|
|
1284
1266
|
] : [
|
|
1285
1267
|
{ label: "\u2713 Allow once", value: "allow-once" },
|
|
1268
|
+
{ label: "\u2713 Allow for this session", value: "allow-session" },
|
|
1286
1269
|
{ label: "\u2717 Deny", value: "deny" }
|
|
1287
1270
|
];
|
|
1288
1271
|
const [selectedIndex, setSelectedIndex] = useState3(0);
|
|
@@ -3711,9 +3694,6 @@ EXAMPLES:
|
|
|
3711
3694
|
Remember: Use context from previous messages to understand follow-up questions.${contextSection}`;
|
|
3712
3695
|
}
|
|
3713
3696
|
|
|
3714
|
-
// src/utils/toolsAdapter.ts
|
|
3715
|
-
import { rmSync } from "fs";
|
|
3716
|
-
|
|
3717
3697
|
// ../../b4m-core/packages/services/dist/src/referService/generateCodes.js
|
|
3718
3698
|
import { randomBytes } from "crypto";
|
|
3719
3699
|
import range from "lodash/range.js";
|
|
@@ -11732,60 +11712,20 @@ var CliLogger = class extends Logger {
|
|
|
11732
11712
|
log(...args) {
|
|
11733
11713
|
}
|
|
11734
11714
|
};
|
|
11735
|
-
function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient
|
|
11715
|
+
function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient) {
|
|
11736
11716
|
const originalFn = tool.toolFn;
|
|
11737
11717
|
const toolName = tool.toolSchema.name;
|
|
11738
11718
|
return {
|
|
11739
11719
|
...tool,
|
|
11740
11720
|
toolFn: async (args) => {
|
|
11741
|
-
|
|
11742
|
-
|
|
11743
|
-
if (toolName === "bash_execute" && args?.command && sandboxOrchestrator) {
|
|
11744
|
-
const pathMod = await import("path");
|
|
11745
|
-
const cwd = args.cwd ? pathMod.resolve(process.cwd(), args.cwd) : process.cwd();
|
|
11746
|
-
const decision = sandboxOrchestrator.shouldSandbox(args.command, cwd);
|
|
11747
|
-
if (decision.type === "blocked") {
|
|
11748
|
-
return `Command blocked by sandbox: ${decision.reason}`;
|
|
11749
|
-
}
|
|
11750
|
-
if (decision.type === "sandbox") {
|
|
11751
|
-
isSandboxed = true;
|
|
11752
|
-
sandboxedArgs = {
|
|
11753
|
-
...args,
|
|
11754
|
-
command: decision.wrappedCommand.commandString,
|
|
11755
|
-
// Store cleanup paths for post-execution cleanup
|
|
11756
|
-
_sandboxCleanup: decision.wrappedCommand.cleanupPaths
|
|
11757
|
-
};
|
|
11758
|
-
}
|
|
11759
|
-
}
|
|
11760
|
-
const effectiveArgs = isSandboxed ? sandboxedArgs : args;
|
|
11761
|
-
if (!permissionManager.needsPermission(toolName, { isSandboxed })) {
|
|
11762
|
-
let result2 = await executeTool(toolName, effectiveArgs, apiClient, originalFn);
|
|
11763
|
-
cleanupSandboxFiles(effectiveArgs?._sandboxCleanup);
|
|
11764
|
-
result2 = await retrySandboxFailure(
|
|
11765
|
-
isSandboxed,
|
|
11766
|
-
result2,
|
|
11767
|
-
toolName,
|
|
11768
|
-
args,
|
|
11769
|
-
apiClient,
|
|
11770
|
-
originalFn,
|
|
11771
|
-
showPermissionPrompt
|
|
11772
|
-
);
|
|
11721
|
+
if (!permissionManager.needsPermission(toolName)) {
|
|
11722
|
+
const result2 = await executeTool(toolName, args, apiClient, originalFn);
|
|
11773
11723
|
agentContext.observationQueue.push({ toolName, result: result2 });
|
|
11774
11724
|
return result2;
|
|
11775
11725
|
}
|
|
11776
11726
|
const { useCliStore: useCliStore2 } = await import("./store-FU6NDC2W.js");
|
|
11777
11727
|
if (useCliStore2.getState().autoAcceptEdits) {
|
|
11778
|
-
|
|
11779
|
-
cleanupSandboxFiles(effectiveArgs?._sandboxCleanup);
|
|
11780
|
-
result2 = await retrySandboxFailure(
|
|
11781
|
-
isSandboxed,
|
|
11782
|
-
result2,
|
|
11783
|
-
toolName,
|
|
11784
|
-
args,
|
|
11785
|
-
apiClient,
|
|
11786
|
-
originalFn,
|
|
11787
|
-
showPermissionPrompt
|
|
11788
|
-
);
|
|
11728
|
+
const result2 = await executeTool(toolName, args, apiClient, originalFn);
|
|
11789
11729
|
agentContext.observationQueue.push({ toolName, result: result2 });
|
|
11790
11730
|
return result2;
|
|
11791
11731
|
}
|
|
@@ -11820,13 +11760,15 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
|
|
|
11820
11760
|
} else if (toolName === "bash_execute" && args?.command) {
|
|
11821
11761
|
const cwd = args.cwd ? ` (in ${args.cwd})` : "";
|
|
11822
11762
|
const timeout = args.timeout ? ` [timeout: ${args.timeout}ms]` : "";
|
|
11823
|
-
|
|
11824
|
-
preview = `$ ${args.command}${cwd}${timeout}${sandboxLabel}`;
|
|
11763
|
+
preview = `$ ${args.command}${cwd}${timeout}`;
|
|
11825
11764
|
}
|
|
11826
|
-
const response = await showPermissionPrompt(toolName,
|
|
11765
|
+
const response = await showPermissionPrompt(toolName, args, preview);
|
|
11827
11766
|
if (response.action === "deny") {
|
|
11828
11767
|
throw new PermissionDeniedError(toolName, args);
|
|
11829
11768
|
}
|
|
11769
|
+
if (response.action === "allow-session") {
|
|
11770
|
+
permissionManager.trustToolForSession(toolName);
|
|
11771
|
+
}
|
|
11830
11772
|
if (response.action === "allow-always") {
|
|
11831
11773
|
const canTrust = permissionManager.trustTool(toolName);
|
|
11832
11774
|
if (canTrust) {
|
|
@@ -11848,50 +11790,12 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
|
|
|
11848
11790
|
}
|
|
11849
11791
|
}
|
|
11850
11792
|
}
|
|
11851
|
-
|
|
11852
|
-
cleanupSandboxFiles(effectiveArgs?._sandboxCleanup);
|
|
11853
|
-
result = await retrySandboxFailure(
|
|
11854
|
-
isSandboxed,
|
|
11855
|
-
result,
|
|
11856
|
-
toolName,
|
|
11857
|
-
args,
|
|
11858
|
-
apiClient,
|
|
11859
|
-
originalFn,
|
|
11860
|
-
showPermissionPrompt
|
|
11861
|
-
);
|
|
11793
|
+
const result = await executeTool(toolName, args, apiClient, originalFn);
|
|
11862
11794
|
agentContext.observationQueue.push({ toolName, result });
|
|
11863
11795
|
return result;
|
|
11864
11796
|
}
|
|
11865
11797
|
};
|
|
11866
11798
|
}
|
|
11867
|
-
function isSandboxFailure(isSandboxed, result) {
|
|
11868
|
-
if (!isSandboxed) return false;
|
|
11869
|
-
return result.includes("sandbox-exec:") || result.includes("bwrap:") || result.includes("Operation not permitted");
|
|
11870
|
-
}
|
|
11871
|
-
async function retrySandboxFailure(isSandboxed, result, toolName, originalArgs, apiClient, originalFn, showPermissionPrompt) {
|
|
11872
|
-
if (!isSandboxFailure(isSandboxed, result)) return result;
|
|
11873
|
-
const errorSnippet = result.slice(0, 200);
|
|
11874
|
-
const retryResponse = await showPermissionPrompt(
|
|
11875
|
-
toolName,
|
|
11876
|
-
originalArgs,
|
|
11877
|
-
`Sandbox execution failed. Retry without sandbox?
|
|
11878
|
-
|
|
11879
|
-
Error: ${errorSnippet}`
|
|
11880
|
-
);
|
|
11881
|
-
if (retryResponse.action !== "deny") {
|
|
11882
|
-
return executeTool(toolName, originalArgs, apiClient, originalFn);
|
|
11883
|
-
}
|
|
11884
|
-
return result;
|
|
11885
|
-
}
|
|
11886
|
-
function cleanupSandboxFiles(paths) {
|
|
11887
|
-
if (!paths || paths.length === 0) return;
|
|
11888
|
-
for (const p of paths) {
|
|
11889
|
-
try {
|
|
11890
|
-
rmSync(p, { recursive: true, force: true });
|
|
11891
|
-
} catch {
|
|
11892
|
-
}
|
|
11893
|
-
}
|
|
11894
|
-
}
|
|
11895
11799
|
function wrapToolWithHooks(tool, hooks, hookContext) {
|
|
11896
11800
|
const hasToolHooks = hooks?.PreToolUse || hooks?.PostToolUse || hooks?.PostToolUseFailure;
|
|
11897
11801
|
if (!hasToolHooks) {
|
|
@@ -11985,7 +11889,7 @@ function normalizeToolName(toolName) {
|
|
|
11985
11889
|
}
|
|
11986
11890
|
return TOOL_NAME_MAPPING[toolName] || toolName;
|
|
11987
11891
|
}
|
|
11988
|
-
function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient, toolFilter
|
|
11892
|
+
function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient, toolFilter) {
|
|
11989
11893
|
const logger2 = new CliLogger();
|
|
11990
11894
|
const storage = new NoOpStorage();
|
|
11991
11895
|
const user = {
|
|
@@ -12045,15 +11949,7 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
|
|
|
12045
11949
|
tools_to_generate
|
|
12046
11950
|
);
|
|
12047
11951
|
let tools = Object.entries(toolsMap).map(
|
|
12048
|
-
([_, tool]) => wrapToolWithPermission(
|
|
12049
|
-
tool,
|
|
12050
|
-
permissionManager,
|
|
12051
|
-
showPermissionPrompt,
|
|
12052
|
-
agentContext,
|
|
12053
|
-
configStore,
|
|
12054
|
-
apiClient,
|
|
12055
|
-
sandboxOrchestrator
|
|
12056
|
-
)
|
|
11952
|
+
([_, tool]) => wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, agentContext, configStore, apiClient)
|
|
12057
11953
|
);
|
|
12058
11954
|
if (toolFilter) {
|
|
12059
11955
|
const { allowedTools, deniedTools } = toolFilter;
|
|
@@ -12077,22 +11973,12 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
|
|
|
12077
11973
|
var PermissionManager = class {
|
|
12078
11974
|
constructor(trustedTools = [], customCategories, deniedTools) {
|
|
12079
11975
|
this.trustedTools = /* @__PURE__ */ new Set();
|
|
11976
|
+
this.sessionTrustedTools = /* @__PURE__ */ new Set();
|
|
12080
11977
|
this.deniedTools = /* @__PURE__ */ new Set();
|
|
12081
|
-
this._sandboxMode = "disabled";
|
|
12082
|
-
this._sandboxActive = false;
|
|
12083
11978
|
this.trustedTools = new Set(trustedTools);
|
|
12084
11979
|
this.customCategories = new Map(Object.entries(customCategories || {}));
|
|
12085
11980
|
this.deniedTools = new Set(deniedTools || []);
|
|
12086
11981
|
}
|
|
12087
|
-
/** Update sandbox state from the orchestrator */
|
|
12088
|
-
setSandboxState(mode, active) {
|
|
12089
|
-
this._sandboxMode = mode;
|
|
12090
|
-
this._sandboxActive = active;
|
|
12091
|
-
}
|
|
12092
|
-
/** Check if sandbox auto-allow is active */
|
|
12093
|
-
isSandboxAutoAllow() {
|
|
12094
|
-
return this._sandboxMode === "auto-allow" && this._sandboxActive;
|
|
12095
|
-
}
|
|
12096
11982
|
/**
|
|
12097
11983
|
* Check if a tool needs permission before execution
|
|
12098
11984
|
*
|
|
@@ -12103,13 +11989,8 @@ var PermissionManager = class {
|
|
|
12103
11989
|
*
|
|
12104
11990
|
* Note: prompt_always tools CANNOT be trusted, so they always need permission
|
|
12105
11991
|
* Note: denied tools from project config ALWAYS need permission (cannot be overridden locally)
|
|
12106
|
-
*
|
|
12107
|
-
* @param toolName - The tool being checked
|
|
12108
|
-
* @param options.isSandboxed - If true, the command will run inside the OS-level sandbox.
|
|
12109
|
-
* In auto-allow mode, sandboxed bash_execute commands skip the permission prompt
|
|
12110
|
-
* because the sandbox provides the security boundary.
|
|
12111
11992
|
*/
|
|
12112
|
-
needsPermission(toolName
|
|
11993
|
+
needsPermission(toolName) {
|
|
12113
11994
|
const categoryMap = Object.fromEntries(this.customCategories);
|
|
12114
11995
|
const category = getToolCategory(toolName, categoryMap);
|
|
12115
11996
|
if (this.deniedTools.has(toolName)) {
|
|
@@ -12118,7 +11999,7 @@ var PermissionManager = class {
|
|
|
12118
11999
|
if (category === "auto_approve") {
|
|
12119
12000
|
return false;
|
|
12120
12001
|
}
|
|
12121
|
-
if (
|
|
12002
|
+
if (this.sessionTrustedTools.has(toolName)) {
|
|
12122
12003
|
return false;
|
|
12123
12004
|
}
|
|
12124
12005
|
if (category === "prompt_always") {
|
|
@@ -12188,6 +12069,29 @@ var PermissionManager = class {
|
|
|
12188
12069
|
getDeniedTools() {
|
|
12189
12070
|
return Array.from(this.deniedTools).sort();
|
|
12190
12071
|
}
|
|
12072
|
+
/**
|
|
12073
|
+
* Trust a tool for the current session only (in-memory, no persistence)
|
|
12074
|
+
* Works for all tool categories including prompt_always, but NOT project-denied tools
|
|
12075
|
+
*/
|
|
12076
|
+
trustToolForSession(toolName) {
|
|
12077
|
+
if (this.deniedTools.has(toolName)) {
|
|
12078
|
+
return false;
|
|
12079
|
+
}
|
|
12080
|
+
this.sessionTrustedTools.add(toolName);
|
|
12081
|
+
return true;
|
|
12082
|
+
}
|
|
12083
|
+
/**
|
|
12084
|
+
* Check if a tool is trusted for the current session
|
|
12085
|
+
*/
|
|
12086
|
+
isSessionTrusted(toolName) {
|
|
12087
|
+
return this.sessionTrustedTools.has(toolName);
|
|
12088
|
+
}
|
|
12089
|
+
/**
|
|
12090
|
+
* Clear all session-scoped trust (called on exit or session reset)
|
|
12091
|
+
*/
|
|
12092
|
+
clearSessionTrust() {
|
|
12093
|
+
this.sessionTrustedTools.clear();
|
|
12094
|
+
}
|
|
12191
12095
|
/**
|
|
12192
12096
|
* Clear all trusted tools
|
|
12193
12097
|
*/
|
|
@@ -13689,7 +13593,7 @@ import { isAxiosError as isAxiosError2 } from "axios";
|
|
|
13689
13593
|
// package.json
|
|
13690
13594
|
var package_default = {
|
|
13691
13595
|
name: "@bike4mind/cli",
|
|
13692
|
-
version: "0.2.29-feat-
|
|
13596
|
+
version: "0.2.29-feat-session-permission.18841+6d9c4deb5",
|
|
13693
13597
|
type: "module",
|
|
13694
13598
|
description: "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
13695
13599
|
license: "UNLICENSED",
|
|
@@ -13799,10 +13703,10 @@ var package_default = {
|
|
|
13799
13703
|
},
|
|
13800
13704
|
devDependencies: {
|
|
13801
13705
|
"@bike4mind/agents": "0.1.0",
|
|
13802
|
-
"@bike4mind/common": "2.50.1-feat-
|
|
13803
|
-
"@bike4mind/mcp": "1.29.1-feat-
|
|
13804
|
-
"@bike4mind/services": "2.48.1-feat-
|
|
13805
|
-
"@bike4mind/utils": "2.5.1-feat-
|
|
13706
|
+
"@bike4mind/common": "2.50.1-feat-session-permission.18841+6d9c4deb5",
|
|
13707
|
+
"@bike4mind/mcp": "1.29.1-feat-session-permission.18841+6d9c4deb5",
|
|
13708
|
+
"@bike4mind/services": "2.48.1-feat-session-permission.18841+6d9c4deb5",
|
|
13709
|
+
"@bike4mind/utils": "2.5.1-feat-session-permission.18841+6d9c4deb5",
|
|
13806
13710
|
"@types/better-sqlite3": "^7.6.13",
|
|
13807
13711
|
"@types/diff": "^5.0.9",
|
|
13808
13712
|
"@types/jsonwebtoken": "^9.0.4",
|
|
@@ -13819,7 +13723,7 @@ var package_default = {
|
|
|
13819
13723
|
optionalDependencies: {
|
|
13820
13724
|
"@vscode/ripgrep": "^1.17.0"
|
|
13821
13725
|
},
|
|
13822
|
-
gitHead: "
|
|
13726
|
+
gitHead: "6d9c4deb5a83cfc1087043d22f407d3444a91cca"
|
|
13823
13727
|
};
|
|
13824
13728
|
|
|
13825
13729
|
// src/config/constants.ts
|
|
@@ -15627,8 +15531,7 @@ function CliApp() {
|
|
|
15627
15531
|
agentStore: null,
|
|
15628
15532
|
abortController: null,
|
|
15629
15533
|
contextContent: "",
|
|
15630
|
-
backgroundManager: null
|
|
15631
|
-
sandboxOrchestrator: null
|
|
15534
|
+
backgroundManager: null
|
|
15632
15535
|
});
|
|
15633
15536
|
const [isInitialized, setIsInitialized] = useState9(false);
|
|
15634
15537
|
const [initError, setInitError] = useState9(null);
|
|
@@ -15825,20 +15728,6 @@ function CliApp() {
|
|
|
15825
15728
|
config.tools.disabled || []
|
|
15826
15729
|
// denied tools from project config
|
|
15827
15730
|
);
|
|
15828
|
-
const { createSandboxRuntime } = await import("./SandboxRuntimeAdapter-5ZMU25Q3.js");
|
|
15829
|
-
const { SandboxOrchestrator } = await import("./SandboxOrchestrator-Z36SFJ7X.js");
|
|
15830
|
-
const { DEFAULT_SANDBOX_CONFIG } = await import("./types-SMQKWHQK.js");
|
|
15831
|
-
const sandboxRuntime = await createSandboxRuntime();
|
|
15832
|
-
const sandboxConfig = config.sandbox ?? DEFAULT_SANDBOX_CONFIG;
|
|
15833
|
-
const sandboxOrchestrator = new SandboxOrchestrator(sandboxConfig, sandboxRuntime);
|
|
15834
|
-
permissionManager.setSandboxState(sandboxConfig.mode, sandboxOrchestrator.isActive());
|
|
15835
|
-
if (sandboxConfig.enabled && sandboxConfig.mode !== "disabled") {
|
|
15836
|
-
if (sandboxRuntime) {
|
|
15837
|
-
console.log(`\u{1F512} Sandbox: ${sandboxConfig.mode} (${sandboxRuntime.name})`);
|
|
15838
|
-
} else {
|
|
15839
|
-
console.log("\u26A0\uFE0F Sandbox: enabled but runtime not available on this platform");
|
|
15840
|
-
}
|
|
15841
|
-
}
|
|
15842
15731
|
let permissionPromptCounter = 0;
|
|
15843
15732
|
const promptFn = (toolName, args, preview) => {
|
|
15844
15733
|
return new Promise((resolve3) => {
|
|
@@ -15864,10 +15753,7 @@ function CliApp() {
|
|
|
15864
15753
|
promptFn,
|
|
15865
15754
|
agentContext,
|
|
15866
15755
|
state.configStore,
|
|
15867
|
-
apiClient
|
|
15868
|
-
void 0,
|
|
15869
|
-
// toolFilter
|
|
15870
|
-
sandboxOrchestrator
|
|
15756
|
+
apiClient
|
|
15871
15757
|
);
|
|
15872
15758
|
startupLog.push(`\u{1F6E0}\uFE0F Loaded ${b4mTools2.length} B4M tool(s)`);
|
|
15873
15759
|
const mcpManager = new McpManager(config);
|
|
@@ -16025,10 +15911,8 @@ ${contextResult.mergedContent}` : "";
|
|
|
16025
15911
|
// Store agent store for agent management commands
|
|
16026
15912
|
contextContent: contextResult.mergedContent,
|
|
16027
15913
|
// Store raw context for compact instructions
|
|
16028
|
-
backgroundManager
|
|
15914
|
+
backgroundManager
|
|
16029
15915
|
// Store for grouped notification turn tracking
|
|
16030
|
-
sandboxOrchestrator
|
|
16031
|
-
// Store sandbox orchestrator for /sandbox commands
|
|
16032
15916
|
}));
|
|
16033
15917
|
setStoreSession(newSession);
|
|
16034
15918
|
const bannerLines = [
|
|
@@ -17497,86 +17381,6 @@ No usage data available for the last ${USAGE_DAYS} days.`);
|
|
|
17497
17381
|
}
|
|
17498
17382
|
break;
|
|
17499
17383
|
}
|
|
17500
|
-
// --- Sandbox commands ---
|
|
17501
|
-
case "sandbox": {
|
|
17502
|
-
if (!state.sandboxOrchestrator) {
|
|
17503
|
-
console.log("\nSandbox: Not initialized");
|
|
17504
|
-
break;
|
|
17505
|
-
}
|
|
17506
|
-
const status = state.sandboxOrchestrator.getStatus();
|
|
17507
|
-
console.log("\nSandbox Status:");
|
|
17508
|
-
console.log(` Enabled: ${status.enabled ? "Yes" : "No"}`);
|
|
17509
|
-
console.log(` Mode: ${status.mode}`);
|
|
17510
|
-
console.log(` Platform: ${status.platform ?? "N/A"}`);
|
|
17511
|
-
console.log(` Runtime: ${status.runtimeName ?? "N/A"}`);
|
|
17512
|
-
console.log(` Available: ${status.runtimeAvailable ? "Yes" : "No"}`);
|
|
17513
|
-
console.log("");
|
|
17514
|
-
console.log("Filesystem Config:");
|
|
17515
|
-
console.log(` Write only to CWD: ${status.config.filesystem.writeOnlyToWorkingDir}`);
|
|
17516
|
-
console.log(` Allowed reads: ${status.config.filesystem.allowedReadPaths.join(", ") || "(none)"}`);
|
|
17517
|
-
console.log(` Denied paths: ${status.config.filesystem.deniedPaths.join(", ") || "(none)"}`);
|
|
17518
|
-
console.log(` Excluded cmds: ${status.config.excludedCommands.join(", ") || "(none)"}`);
|
|
17519
|
-
console.log("");
|
|
17520
|
-
break;
|
|
17521
|
-
}
|
|
17522
|
-
case "sandbox:enable": {
|
|
17523
|
-
if (!state.sandboxOrchestrator) {
|
|
17524
|
-
console.log("Sandbox not initialized");
|
|
17525
|
-
break;
|
|
17526
|
-
}
|
|
17527
|
-
if (!state.sandboxOrchestrator.isAvailable()) {
|
|
17528
|
-
console.log("Sandbox runtime not available on this platform");
|
|
17529
|
-
break;
|
|
17530
|
-
}
|
|
17531
|
-
state.sandboxOrchestrator.setMode("auto-allow");
|
|
17532
|
-
state.permissionManager?.setSandboxState("auto-allow", true);
|
|
17533
|
-
const config = await state.configStore.get();
|
|
17534
|
-
await state.configStore.save({
|
|
17535
|
-
...config,
|
|
17536
|
-
sandbox: { ...state.sandboxOrchestrator.getConfig() }
|
|
17537
|
-
});
|
|
17538
|
-
console.log("Sandbox enabled (auto-allow mode)");
|
|
17539
|
-
break;
|
|
17540
|
-
}
|
|
17541
|
-
case "sandbox:disable": {
|
|
17542
|
-
if (!state.sandboxOrchestrator) {
|
|
17543
|
-
console.log("Sandbox not initialized");
|
|
17544
|
-
break;
|
|
17545
|
-
}
|
|
17546
|
-
state.sandboxOrchestrator.setMode("disabled");
|
|
17547
|
-
state.permissionManager?.setSandboxState("disabled", false);
|
|
17548
|
-
const disableConfig = await state.configStore.get();
|
|
17549
|
-
await state.configStore.save({
|
|
17550
|
-
...disableConfig,
|
|
17551
|
-
sandbox: { ...state.sandboxOrchestrator.getConfig() }
|
|
17552
|
-
});
|
|
17553
|
-
console.log("Sandbox disabled");
|
|
17554
|
-
break;
|
|
17555
|
-
}
|
|
17556
|
-
case "sandbox:mode": {
|
|
17557
|
-
if (!state.sandboxOrchestrator) {
|
|
17558
|
-
console.log("Sandbox not initialized");
|
|
17559
|
-
break;
|
|
17560
|
-
}
|
|
17561
|
-
const modeArg = args[0];
|
|
17562
|
-
if (modeArg !== "auto-allow" && modeArg !== "permissions") {
|
|
17563
|
-
console.log("Usage: /sandbox:mode <auto-allow|permissions>");
|
|
17564
|
-
break;
|
|
17565
|
-
}
|
|
17566
|
-
if (!state.sandboxOrchestrator.isAvailable()) {
|
|
17567
|
-
console.log("Sandbox runtime not available on this platform");
|
|
17568
|
-
break;
|
|
17569
|
-
}
|
|
17570
|
-
state.sandboxOrchestrator.setMode(modeArg);
|
|
17571
|
-
state.permissionManager?.setSandboxState(modeArg, state.sandboxOrchestrator.isActive());
|
|
17572
|
-
const modeConfig = await state.configStore.get();
|
|
17573
|
-
await state.configStore.save({
|
|
17574
|
-
...modeConfig,
|
|
17575
|
-
sandbox: { ...state.sandboxOrchestrator.getConfig() }
|
|
17576
|
-
});
|
|
17577
|
-
console.log(`Sandbox mode set to: ${modeArg}`);
|
|
17578
|
-
break;
|
|
17579
|
-
}
|
|
17580
17384
|
default:
|
|
17581
17385
|
console.log(`Unknown command: /${command}`);
|
|
17582
17386
|
console.log("Type /help for available commands");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bike4mind/cli",
|
|
3
|
-
"version": "0.2.29-feat-
|
|
3
|
+
"version": "0.2.29-feat-session-permission.18841+6d9c4deb5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Interactive CLI tool for Bike4Mind with ReAct agents",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -110,10 +110,10 @@
|
|
|
110
110
|
},
|
|
111
111
|
"devDependencies": {
|
|
112
112
|
"@bike4mind/agents": "0.1.0",
|
|
113
|
-
"@bike4mind/common": "2.50.1-feat-
|
|
114
|
-
"@bike4mind/mcp": "1.29.1-feat-
|
|
115
|
-
"@bike4mind/services": "2.48.1-feat-
|
|
116
|
-
"@bike4mind/utils": "2.5.1-feat-
|
|
113
|
+
"@bike4mind/common": "2.50.1-feat-session-permission.18841+6d9c4deb5",
|
|
114
|
+
"@bike4mind/mcp": "1.29.1-feat-session-permission.18841+6d9c4deb5",
|
|
115
|
+
"@bike4mind/services": "2.48.1-feat-session-permission.18841+6d9c4deb5",
|
|
116
|
+
"@bike4mind/utils": "2.5.1-feat-session-permission.18841+6d9c4deb5",
|
|
117
117
|
"@types/better-sqlite3": "^7.6.13",
|
|
118
118
|
"@types/diff": "^5.0.9",
|
|
119
119
|
"@types/jsonwebtoken": "^9.0.4",
|
|
@@ -130,5 +130,5 @@
|
|
|
130
130
|
"optionalDependencies": {
|
|
131
131
|
"@vscode/ripgrep": "^1.17.0"
|
|
132
132
|
},
|
|
133
|
-
"gitHead": "
|
|
133
|
+
"gitHead": "6d9c4deb5a83cfc1087043d22f407d3444a91cca"
|
|
134
134
|
}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
expandPath,
|
|
4
|
-
isBinaryAvailable
|
|
5
|
-
} from "./chunk-AVAD2PPK.js";
|
|
6
|
-
|
|
7
|
-
// src/sandbox/runtime/BubblewrapRuntime.ts
|
|
8
|
-
import os from "os";
|
|
9
|
-
var SYSTEM_RO_BINDS = ["/usr", "/bin", "/lib", "/lib64", "/sbin", "/etc"];
|
|
10
|
-
var SPECIAL_MOUNTS = {
|
|
11
|
-
dev: "/dev",
|
|
12
|
-
proc: "/proc",
|
|
13
|
-
tmp: "/tmp"
|
|
14
|
-
};
|
|
15
|
-
var BubblewrapRuntime = class {
|
|
16
|
-
constructor() {
|
|
17
|
-
this.platform = "linux";
|
|
18
|
-
this.name = "bubblewrap";
|
|
19
|
-
}
|
|
20
|
-
isAvailable() {
|
|
21
|
-
return isBinaryAvailable("bwrap");
|
|
22
|
-
}
|
|
23
|
-
wrapCommand(options) {
|
|
24
|
-
const { command, cwd, filesystemConfig, env } = options;
|
|
25
|
-
const expandedDenied = filesystemConfig.deniedPaths.map(expandPath);
|
|
26
|
-
const expandedAllowed = filesystemConfig.allowedReadPaths.map(expandPath);
|
|
27
|
-
const args = [];
|
|
28
|
-
for (const sysPath of SYSTEM_RO_BINDS) {
|
|
29
|
-
args.push("--ro-bind-try", sysPath, sysPath);
|
|
30
|
-
}
|
|
31
|
-
args.push("--dev", SPECIAL_MOUNTS.dev);
|
|
32
|
-
args.push("--proc", SPECIAL_MOUNTS.proc);
|
|
33
|
-
args.push("--tmpfs", SPECIAL_MOUNTS.tmp);
|
|
34
|
-
args.push("--bind", cwd, cwd);
|
|
35
|
-
const homeDir = os.homedir();
|
|
36
|
-
args.push("--ro-bind", homeDir, homeDir);
|
|
37
|
-
for (const allowedPath of expandedAllowed) {
|
|
38
|
-
if (!allowedPath.startsWith(homeDir)) {
|
|
39
|
-
args.push("--ro-bind-try", allowedPath, allowedPath);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
for (const deniedPath of expandedDenied) {
|
|
43
|
-
args.push("--tmpfs", deniedPath);
|
|
44
|
-
}
|
|
45
|
-
args.push("--unshare-all");
|
|
46
|
-
args.push("--share-net");
|
|
47
|
-
args.push("--die-with-parent");
|
|
48
|
-
if (options.seccompProfile) {
|
|
49
|
-
args.push("--seccomp", options.seccompProfile);
|
|
50
|
-
}
|
|
51
|
-
args.push("--chdir", cwd);
|
|
52
|
-
args.push("bash", "-c", command);
|
|
53
|
-
const commandString = ["bwrap", ...args.map(shellEscape)].join(" ");
|
|
54
|
-
return {
|
|
55
|
-
executable: "bwrap",
|
|
56
|
-
args,
|
|
57
|
-
env: env ?? {},
|
|
58
|
-
commandString
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
function shellEscape(str) {
|
|
63
|
-
if (/^[a-zA-Z0-9._/=:-]+$/.test(str)) return str;
|
|
64
|
-
return `'${str.replace(/'/g, "'\\''")}'`;
|
|
65
|
-
}
|
|
66
|
-
export {
|
|
67
|
-
BubblewrapRuntime
|
|
68
|
-
};
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
DEFAULT_SANDBOX_CONFIG
|
|
4
|
-
} from "./chunk-W4TCH4ZT.js";
|
|
5
|
-
|
|
6
|
-
// src/sandbox/SandboxOrchestrator.ts
|
|
7
|
-
var SandboxOrchestrator = class {
|
|
8
|
-
constructor(config, runtime) {
|
|
9
|
-
this.config = config ?? DEFAULT_SANDBOX_CONFIG;
|
|
10
|
-
this.runtime = runtime ?? null;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Determine whether a command should be sandboxed.
|
|
14
|
-
*
|
|
15
|
-
* Decision logic:
|
|
16
|
-
* 1. If sandbox is disabled → unsandboxed (no permission change)
|
|
17
|
-
* 2. If command matches an excluded command → unsandboxed (requires permission)
|
|
18
|
-
* 3. If runtime is not available → unsandboxed with warning
|
|
19
|
-
* 4. Otherwise → sandbox the command
|
|
20
|
-
*/
|
|
21
|
-
shouldSandbox(command, cwd) {
|
|
22
|
-
if (!this.config.enabled || this.config.mode === "disabled") {
|
|
23
|
-
return { type: "unsandboxed", requiresPermission: true };
|
|
24
|
-
}
|
|
25
|
-
const baseCommand = this.getBaseCommand(command);
|
|
26
|
-
if (this.isExcludedCommand(baseCommand)) {
|
|
27
|
-
if (!this.config.allowUnsandboxedCommands) {
|
|
28
|
-
return {
|
|
29
|
-
type: "blocked",
|
|
30
|
-
reason: `Command '${baseCommand}' is excluded from sandboxing and unsandboxed commands are not allowed`
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
return {
|
|
34
|
-
type: "unsandboxed",
|
|
35
|
-
requiresPermission: true,
|
|
36
|
-
reason: `Command '${baseCommand}' is excluded from sandboxing`
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
if (!this.runtime) {
|
|
40
|
-
return {
|
|
41
|
-
type: "unsandboxed",
|
|
42
|
-
requiresPermission: true,
|
|
43
|
-
reason: "Sandbox runtime not available on this platform"
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
const wrappedCommand = this.runtime.wrapCommand({
|
|
47
|
-
command,
|
|
48
|
-
cwd,
|
|
49
|
-
filesystemConfig: this.config.filesystem,
|
|
50
|
-
...this.runtime.platform === "linux" && this.config.platform.linux.seccompProfile && {
|
|
51
|
-
seccompProfile: this.config.platform.linux.seccompProfile
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
return { type: "sandbox", wrappedCommand };
|
|
55
|
-
}
|
|
56
|
-
/** Get the current sandbox mode */
|
|
57
|
-
getMode() {
|
|
58
|
-
return this.config.mode;
|
|
59
|
-
}
|
|
60
|
-
/** Set the sandbox mode (does not persist — caller must save config) */
|
|
61
|
-
setMode(mode) {
|
|
62
|
-
this.config.mode = mode;
|
|
63
|
-
this.config.enabled = mode !== "disabled";
|
|
64
|
-
}
|
|
65
|
-
/** Check if sandbox is enabled and runtime is available */
|
|
66
|
-
isAvailable() {
|
|
67
|
-
return this.runtime !== null && this.runtime.isAvailable();
|
|
68
|
-
}
|
|
69
|
-
/** Check if sandbox is currently active (enabled + available) */
|
|
70
|
-
isActive() {
|
|
71
|
-
return this.config.enabled && this.config.mode !== "disabled" && this.isAvailable();
|
|
72
|
-
}
|
|
73
|
-
/** Get the current sandbox configuration */
|
|
74
|
-
getConfig() {
|
|
75
|
-
return this.config;
|
|
76
|
-
}
|
|
77
|
-
/** Update config (does not persist — caller must save) */
|
|
78
|
-
updateConfig(config) {
|
|
79
|
-
this.config = config;
|
|
80
|
-
}
|
|
81
|
-
/** Get full status information for display */
|
|
82
|
-
getStatus() {
|
|
83
|
-
return {
|
|
84
|
-
mode: this.config.mode,
|
|
85
|
-
enabled: this.config.enabled,
|
|
86
|
-
platform: this.runtime?.platform ?? null,
|
|
87
|
-
runtimeAvailable: this.runtime?.isAvailable() ?? false,
|
|
88
|
-
runtimeName: this.runtime?.name ?? null,
|
|
89
|
-
config: this.config
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Extract the base command name from a full command string.
|
|
94
|
-
* e.g., "docker compose up -d" → "docker"
|
|
95
|
-
*/
|
|
96
|
-
getBaseCommand(command) {
|
|
97
|
-
const trimmed = command.trim();
|
|
98
|
-
const withoutEnv = trimmed.replace(/^(\w+=\S+\s+)*/, "");
|
|
99
|
-
const parts = withoutEnv.split(/\s+/);
|
|
100
|
-
const first = parts[0] || "";
|
|
101
|
-
return first.split("/").pop() || first;
|
|
102
|
-
}
|
|
103
|
-
/** Check if a command is in the excluded list */
|
|
104
|
-
isExcludedCommand(baseCommand) {
|
|
105
|
-
return this.config.excludedCommands.some((excluded) => baseCommand === excluded);
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
export {
|
|
109
|
-
SandboxOrchestrator
|
|
110
|
-
};
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
expandPath,
|
|
4
|
-
isBinaryAvailable
|
|
5
|
-
} from "./chunk-AVAD2PPK.js";
|
|
6
|
-
|
|
7
|
-
// src/sandbox/runtime/SeatbeltRuntime.ts
|
|
8
|
-
import { writeFileSync, mkdtempSync } from "fs";
|
|
9
|
-
import path from "path";
|
|
10
|
-
import os from "os";
|
|
11
|
-
function escapeSeatbeltPath(p) {
|
|
12
|
-
return p.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
13
|
-
}
|
|
14
|
-
var SeatbeltRuntime = class {
|
|
15
|
-
constructor() {
|
|
16
|
-
this.platform = "darwin";
|
|
17
|
-
this.name = "seatbelt";
|
|
18
|
-
}
|
|
19
|
-
isAvailable() {
|
|
20
|
-
return isBinaryAvailable("sandbox-exec");
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Generate a Seatbelt profile string from the filesystem config.
|
|
24
|
-
*
|
|
25
|
-
* Strategy:
|
|
26
|
-
* - Start with (allow default) to permit most operations
|
|
27
|
-
* - Deny all file writes globally
|
|
28
|
-
* - Allow file writes only to the working directory
|
|
29
|
-
* - Deny all access to explicitly denied paths
|
|
30
|
-
* - Allow read access to explicitly allowed paths
|
|
31
|
-
*/
|
|
32
|
-
generateProfile(options) {
|
|
33
|
-
const { cwd, filesystemConfig } = options;
|
|
34
|
-
const expandedDenied = filesystemConfig.deniedPaths.map(expandPath);
|
|
35
|
-
const expandedAllowed = filesystemConfig.allowedReadPaths.map(expandPath);
|
|
36
|
-
const lines = [
|
|
37
|
-
"(version 1)",
|
|
38
|
-
"",
|
|
39
|
-
"; Start with permissive defaults (process exec, network, sysctl, etc.)",
|
|
40
|
-
"(allow default)",
|
|
41
|
-
""
|
|
42
|
-
];
|
|
43
|
-
if (filesystemConfig.writeOnlyToWorkingDir) {
|
|
44
|
-
lines.push("; Deny all file writes globally");
|
|
45
|
-
lines.push("(deny file-write*)");
|
|
46
|
-
lines.push("");
|
|
47
|
-
lines.push("; Allow writes to working directory");
|
|
48
|
-
lines.push(`(allow file-write* (subpath "${escapeSeatbeltPath(cwd)}"))`);
|
|
49
|
-
lines.push("");
|
|
50
|
-
lines.push("; Allow writes to temp directories");
|
|
51
|
-
lines.push(`(allow file-write* (subpath "/tmp"))`);
|
|
52
|
-
lines.push(`(allow file-write* (subpath "/private/tmp"))`);
|
|
53
|
-
lines.push(`(allow file-write* (subpath "${escapeSeatbeltPath(os.tmpdir())}"))`);
|
|
54
|
-
lines.push("");
|
|
55
|
-
}
|
|
56
|
-
if (expandedDenied.length > 0) {
|
|
57
|
-
lines.push("; Deny access to sensitive paths");
|
|
58
|
-
for (const deniedPath of expandedDenied) {
|
|
59
|
-
lines.push(`(deny file-read* file-write* (subpath "${escapeSeatbeltPath(deniedPath)}"))`);
|
|
60
|
-
}
|
|
61
|
-
lines.push("");
|
|
62
|
-
}
|
|
63
|
-
if (expandedAllowed.length > 0) {
|
|
64
|
-
lines.push("; Explicitly allowed read paths");
|
|
65
|
-
for (const allowedPath of expandedAllowed) {
|
|
66
|
-
lines.push(`(allow file-read* (subpath "${escapeSeatbeltPath(allowedPath)}"))`);
|
|
67
|
-
}
|
|
68
|
-
lines.push("");
|
|
69
|
-
}
|
|
70
|
-
return lines.join("\n");
|
|
71
|
-
}
|
|
72
|
-
wrapCommand(options) {
|
|
73
|
-
try {
|
|
74
|
-
const profile = this.generateProfile(options);
|
|
75
|
-
const tmpDir = mkdtempSync(path.join(os.tmpdir(), "b4m-sandbox-"));
|
|
76
|
-
const profilePath = path.join(tmpDir, "sandbox.sb");
|
|
77
|
-
writeFileSync(profilePath, profile, "utf-8");
|
|
78
|
-
const args = ["-f", profilePath, "bash", "-c", options.command];
|
|
79
|
-
return {
|
|
80
|
-
executable: "sandbox-exec",
|
|
81
|
-
args,
|
|
82
|
-
env: options.env ?? {},
|
|
83
|
-
commandString: `sandbox-exec -f ${profilePath} bash -c ${shellEscape(options.command)}`,
|
|
84
|
-
cleanupPaths: [profilePath, tmpDir]
|
|
85
|
-
};
|
|
86
|
-
} catch (err) {
|
|
87
|
-
throw new Error(`Failed to create sandbox profile: ${err instanceof Error ? err.message : String(err)}`);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
function shellEscape(str) {
|
|
92
|
-
return `'${str.replace(/'/g, "'\\''")}'`;
|
|
93
|
-
}
|
|
94
|
-
export {
|
|
95
|
-
SeatbeltRuntime
|
|
96
|
-
};
|
package/dist/chunk-AVAD2PPK.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/sandbox/runtime/SandboxRuntimeAdapter.ts
|
|
4
|
-
import os from "os";
|
|
5
|
-
import { execSync } from "child_process";
|
|
6
|
-
function detectPlatform() {
|
|
7
|
-
const platform = os.platform();
|
|
8
|
-
if (platform === "darwin") return "darwin";
|
|
9
|
-
if (platform === "linux") return "linux";
|
|
10
|
-
return null;
|
|
11
|
-
}
|
|
12
|
-
function isBinaryAvailable(binary) {
|
|
13
|
-
try {
|
|
14
|
-
execSync(`which ${binary}`, { stdio: "ignore" });
|
|
15
|
-
return true;
|
|
16
|
-
} catch {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
function expandPath(pathStr) {
|
|
21
|
-
return pathStr.replace(/\$HOME/g, os.homedir()).replace(/\$USER/g, os.userInfo().username).replace(/~\//g, `${os.homedir()}/`);
|
|
22
|
-
}
|
|
23
|
-
async function createSandboxRuntime() {
|
|
24
|
-
const platform = detectPlatform();
|
|
25
|
-
if (!platform) return null;
|
|
26
|
-
if (platform === "darwin") {
|
|
27
|
-
const { SeatbeltRuntime } = await import("./SeatbeltRuntime-SL7HVS3J.js");
|
|
28
|
-
const runtime = new SeatbeltRuntime();
|
|
29
|
-
return runtime.isAvailable() ? runtime : null;
|
|
30
|
-
}
|
|
31
|
-
if (platform === "linux") {
|
|
32
|
-
const { BubblewrapRuntime } = await import("./BubblewrapRuntime-HXTR3NQG.js");
|
|
33
|
-
const runtime = new BubblewrapRuntime();
|
|
34
|
-
return runtime.isAvailable() ? runtime : null;
|
|
35
|
-
}
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export {
|
|
40
|
-
detectPlatform,
|
|
41
|
-
isBinaryAvailable,
|
|
42
|
-
expandPath,
|
|
43
|
-
createSandboxRuntime
|
|
44
|
-
};
|
package/dist/chunk-W4TCH4ZT.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/sandbox/types.ts
|
|
4
|
-
var DEFAULT_SANDBOX_CONFIG = {
|
|
5
|
-
enabled: false,
|
|
6
|
-
mode: "disabled",
|
|
7
|
-
filesystem: {
|
|
8
|
-
writeOnlyToWorkingDir: true,
|
|
9
|
-
allowedReadPaths: ["$HOME/.gitconfig", "$HOME/.npmrc", "$HOME/.node_modules"],
|
|
10
|
-
deniedPaths: ["$HOME/.ssh", "$HOME/.aws", "$HOME/.gnupg", "$HOME/.env", "/etc/shadow", "/etc/passwd"]
|
|
11
|
-
},
|
|
12
|
-
excludedCommands: ["docker", "watchman", "podman"],
|
|
13
|
-
allowUnsandboxedCommands: true,
|
|
14
|
-
platform: {
|
|
15
|
-
linux: {
|
|
16
|
-
runtime: "bubblewrap"
|
|
17
|
-
},
|
|
18
|
-
macos: {
|
|
19
|
-
runtime: "seatbelt",
|
|
20
|
-
profileTemplate: "default"
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export {
|
|
26
|
-
DEFAULT_SANDBOX_CONFIG
|
|
27
|
-
};
|