moflo 4.8.59 → 4.8.61
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/package.json +2 -2
- package/src/modules/cli/dist/src/version.js +1 -1
- package/src/modules/cli/package.json +1 -1
- package/src/modules/spells/dist/core/bwrap-sandbox.js +18 -4
- package/src/modules/spells/dist/core/platform-sandbox.js +21 -1
- package/src/modules/spells/dist/factory/runner-bridge.js +17 -0
- package/src/modules/spells/dist/index.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "moflo",
|
|
3
|
-
"version": "4.8.
|
|
3
|
+
"version": "4.8.61",
|
|
4
4
|
"description": "MoFlo — AI agent orchestration for Claude Code. Forked from ruflo/claude-flow with patches applied to source, plus feature-level orchestration.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"@types/js-yaml": "^4.0.9",
|
|
112
112
|
"@types/node": "^20.19.37",
|
|
113
113
|
"eslint": "^8.0.0",
|
|
114
|
-
"moflo": "^4.8.
|
|
114
|
+
"moflo": "^4.8.60",
|
|
115
115
|
"tsx": "^4.21.0",
|
|
116
116
|
"typescript": "^5.9.3",
|
|
117
117
|
"vitest": "^4.0.0"
|
|
@@ -58,9 +58,12 @@ function needsToolHomeAccess(level) {
|
|
|
58
58
|
* - fs:write unscoped -> --bind (read-write) for projectRoot
|
|
59
59
|
* - net -> omit --unshare-net
|
|
60
60
|
*
|
|
61
|
-
* When `options.permissionLevel` is `elevated` or `autonomous`, also
|
|
62
|
-
* narrow allowlist of CLI-tool home paths writable via `--bind-try`
|
|
63
|
-
* spawned subcommands (claude, gh, git, npm) can persist
|
|
61
|
+
* When `options.permissionLevel` is `elevated` or `autonomous`, also:
|
|
62
|
+
* - Bind a narrow allowlist of CLI-tool home paths writable via `--bind-try`
|
|
63
|
+
* so spawned subcommands (claude, gh, git, npm) can persist state.
|
|
64
|
+
* - Share the host network (omit `--unshare-net`) so those tools can reach
|
|
65
|
+
* their APIs (api.anthropic.com, api.github.com, etc.). Without this,
|
|
66
|
+
* `claude -p` and similar commands fail with DNS/connection errors.
|
|
64
67
|
*/
|
|
65
68
|
export function buildBwrapArgs(command, capabilities, projectRoot, options = {}) {
|
|
66
69
|
const args = [];
|
|
@@ -117,12 +120,23 @@ export function buildBwrapArgs(command, capabilities, projectRoot, options = {})
|
|
|
117
120
|
}
|
|
118
121
|
}
|
|
119
122
|
// ── Network isolation ───────────────────────────────────────────────
|
|
123
|
+
// Elevated/autonomous steps spawn CLI tools (claude, gh, git, npm) that
|
|
124
|
+
// need network to reach their APIs. Keep the host network for those,
|
|
125
|
+
// mirroring the tool-home-paths policy.
|
|
120
126
|
const hasNet = capabilities.some(c => c.type === 'net');
|
|
121
|
-
if (!hasNet) {
|
|
127
|
+
if (!hasNet && !needsToolHomeAccess(options.permissionLevel)) {
|
|
122
128
|
args.push('--unshare-net');
|
|
123
129
|
}
|
|
124
130
|
// ── PID isolation (always) ──────────────────────────────────────────
|
|
125
131
|
args.push('--unshare-pid');
|
|
132
|
+
// ── Lifetime bound to parent ────────────────────────────────────────
|
|
133
|
+
// Without this, child processes spawned by the sandboxed command (e.g.
|
|
134
|
+
// node workers from `claude -p`) keep the PID namespace alive past the
|
|
135
|
+
// entry script's exit — bwrap waits for the namespace to drain even
|
|
136
|
+
// though the user-visible work is done. --die-with-parent makes bwrap
|
|
137
|
+
// and everything inside terminate when the spawning runner exits or
|
|
138
|
+
// sends a signal, guaranteeing cleanup on success and on timeout.
|
|
139
|
+
args.push('--die-with-parent');
|
|
126
140
|
// ── Command ─────────────────────────────────────────────────────────
|
|
127
141
|
args.push('bash', '-c', command);
|
|
128
142
|
return args;
|
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
* @see https://github.com/eric-cielo/moflo/issues/409
|
|
13
13
|
*/
|
|
14
14
|
import { execSync } from 'node:child_process';
|
|
15
|
-
import { existsSync } from 'node:fs';
|
|
15
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
16
16
|
import { platform } from 'node:os';
|
|
17
|
+
import { join } from 'node:path';
|
|
17
18
|
export const DEFAULT_SANDBOX_CONFIG = {
|
|
18
19
|
enabled: false,
|
|
19
20
|
tier: 'auto',
|
|
@@ -129,6 +130,25 @@ export function resolveSandboxConfig(raw) {
|
|
|
129
130
|
function isValidTier(value) {
|
|
130
131
|
return value === 'auto' || value === 'denylist-only' || value === 'full';
|
|
131
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* Load sandbox config from a project's moflo.yaml.
|
|
135
|
+
* Returns DEFAULT_SANDBOX_CONFIG on any failure (missing file, parse error, etc.).
|
|
136
|
+
*
|
|
137
|
+
* Lets the spell engine auto-discover sandbox settings from the project root
|
|
138
|
+
* without forcing every caller (MCP tools, CLI adapters) to load moflo.yaml.
|
|
139
|
+
*/
|
|
140
|
+
export async function loadSandboxConfigFromProject(projectRoot) {
|
|
141
|
+
try {
|
|
142
|
+
const content = readFileSync(join(projectRoot, 'moflo.yaml'), 'utf-8');
|
|
143
|
+
const mod = await import('js-yaml');
|
|
144
|
+
const yaml = mod.default ?? mod;
|
|
145
|
+
const raw = yaml.load(content);
|
|
146
|
+
return resolveSandboxConfig(raw?.sandbox);
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return DEFAULT_SANDBOX_CONFIG;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
132
152
|
/**
|
|
133
153
|
* Combine detected capability with user config to determine effective sandbox behavior.
|
|
134
154
|
*
|
|
@@ -8,7 +8,20 @@
|
|
|
8
8
|
* This module is the integration point between MCP spell tools
|
|
9
9
|
* and the SpellCaster engine.
|
|
10
10
|
*/
|
|
11
|
+
import { loadSandboxConfigFromProject } from '../core/platform-sandbox.js';
|
|
11
12
|
import { createRunner, runSpellFromContent } from './runner-factory.js';
|
|
13
|
+
/**
|
|
14
|
+
* Resolve sandbox config: prefer caller-supplied; fall back to auto-loading
|
|
15
|
+
* from moflo.yaml at projectRoot. Returns undefined when neither is available
|
|
16
|
+
* (runner falls back to DEFAULT_SANDBOX_CONFIG with denylist-only).
|
|
17
|
+
*/
|
|
18
|
+
async function resolveSandbox(explicit, projectRoot) {
|
|
19
|
+
if (explicit)
|
|
20
|
+
return explicit;
|
|
21
|
+
if (!projectRoot)
|
|
22
|
+
return undefined;
|
|
23
|
+
return loadSandboxConfigFromProject(projectRoot);
|
|
24
|
+
}
|
|
12
25
|
// Track active spells for cancellation
|
|
13
26
|
const activeSpells = new Map();
|
|
14
27
|
// ============================================================================
|
|
@@ -22,6 +35,7 @@ export async function bridgeRunSpell(content, sourceFile, args, options = {}) {
|
|
|
22
35
|
const controller = new AbortController();
|
|
23
36
|
activeSpells.set(spellId, controller);
|
|
24
37
|
try {
|
|
38
|
+
const sandboxConfig = await resolveSandbox(options.sandboxConfig, options.projectRoot);
|
|
25
39
|
const result = await runSpellFromContent(content, sourceFile, {
|
|
26
40
|
spellId,
|
|
27
41
|
args,
|
|
@@ -30,6 +44,7 @@ export async function bridgeRunSpell(content, sourceFile, args, options = {}) {
|
|
|
30
44
|
memory: options.memory,
|
|
31
45
|
credentials: options.credentials,
|
|
32
46
|
...(options.projectRoot ? { projectRoot: options.projectRoot } : {}),
|
|
47
|
+
...(sandboxConfig ? { sandboxConfig } : {}),
|
|
33
48
|
});
|
|
34
49
|
return result;
|
|
35
50
|
}
|
|
@@ -45,11 +60,13 @@ export async function bridgeExecuteSpell(definition, args, options = {}) {
|
|
|
45
60
|
const controller = new AbortController();
|
|
46
61
|
activeSpells.set(spellId, controller);
|
|
47
62
|
try {
|
|
63
|
+
const sandboxConfig = await resolveSandbox(options.sandboxConfig, options.projectRoot);
|
|
48
64
|
const runner = createRunner({ memory: options.memory, credentials: options.credentials });
|
|
49
65
|
return await runner.run(definition, args, {
|
|
50
66
|
spellId,
|
|
51
67
|
signal: controller.signal,
|
|
52
68
|
...(options.projectRoot ? { projectRoot: options.projectRoot } : {}),
|
|
69
|
+
...(sandboxConfig ? { sandboxConfig } : {}),
|
|
53
70
|
});
|
|
54
71
|
}
|
|
55
72
|
finally {
|
|
@@ -19,7 +19,7 @@ export { GatedConnectorAccessor } from './core/gated-connector-accessor.js';
|
|
|
19
19
|
export { checkCapabilities, } from './core/capability-validator.js';
|
|
20
20
|
export { CapabilityGateway, CapabilityDeniedError, DenyAllGateway, DENY_ALL_GATEWAY, discloseStep, discloseSpell, formatStepDisclosure, formatSpellDisclosure, } from './core/capability-gateway.js';
|
|
21
21
|
export { collectPrerequisites, checkPrerequisites, formatPrerequisiteErrors, commandExists, } from './core/prerequisite-checker.js';
|
|
22
|
-
export { detectSandboxCapability, resetSandboxCache, resolveSandboxConfig, resolveEffectiveSandbox, formatSandboxLog, DEFAULT_SANDBOX_CONFIG, } from './core/platform-sandbox.js';
|
|
22
|
+
export { detectSandboxCapability, resetSandboxCache, resolveSandboxConfig, resolveEffectiveSandbox, formatSandboxLog, loadSandboxConfigFromProject, DEFAULT_SANDBOX_CONFIG, } from './core/platform-sandbox.js';
|
|
23
23
|
export { resolveScopePath, } from './core/sandbox-utils.js';
|
|
24
24
|
export { generateSandboxProfile, wrapWithSandboxExec, } from './core/sandbox-profile.js';
|
|
25
25
|
export { buildBwrapArgs, wrapWithBwrap, } from './core/bwrap-sandbox.js';
|