moflo 4.8.72 → 4.8.74

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moflo",
3
- "version": "4.8.72",
3
+ "version": "4.8.74",
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",
@@ -112,7 +112,7 @@
112
112
  "@types/js-yaml": "^4.0.9",
113
113
  "@types/node": "^20.19.37",
114
114
  "eslint": "^8.0.0",
115
- "moflo": "^4.8.71",
115
+ "moflo": "^4.8.73",
116
116
  "tsx": "^4.21.0",
117
117
  "typescript": "^5.9.3",
118
118
  "vitest": "^4.0.0"
@@ -2,5 +2,5 @@
2
2
  * Auto-generated by build. Do not edit manually.
3
3
  * Source of truth: root package.json → scripts/sync-version.mjs
4
4
  */
5
- export const VERSION = '4.8.72';
5
+ export const VERSION = '4.8.74';
6
6
  //# sourceMappingURL=version.js.map
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moflo/cli",
3
- "version": "4.8.72",
3
+ "version": "4.8.74",
4
4
  "type": "module",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -15,12 +15,13 @@ import { execSync } from 'node:child_process';
15
15
  import { existsSync, readFileSync } from 'node:fs';
16
16
  import { platform } from 'node:os';
17
17
  import { join } from 'node:path';
18
+ import { escapeShellArg } from './shell.js';
18
19
  export const DEFAULT_SANDBOX_CONFIG = {
19
20
  enabled: false,
20
21
  tier: 'auto',
21
22
  };
22
23
  /** Recommended image for first-time Windows Docker sandbox setup. */
23
- export const RECOMMENDED_DOCKER_IMAGE = 'node:20-bookworm';
24
+ export const RECOMMENDED_DOCKER_IMAGE = 'ghcr.io/eric-cielo/moflo-sandbox:latest';
24
25
  // ============================================================================
25
26
  // Detection (cached)
26
27
  // ============================================================================
@@ -40,6 +41,7 @@ export function detectSandboxCapability() {
40
41
  /** Reset the cached result (for testing). */
41
42
  export function resetSandboxCache() {
42
43
  _cached = undefined;
44
+ _imageExistsCache.clear();
43
45
  }
44
46
  function detectUncached() {
45
47
  const os = platform();
@@ -165,12 +167,13 @@ export async function loadSandboxConfigFromProject(projectRoot) {
165
167
  * @throws Error if tier is 'full' but no OS sandbox is available.
166
168
  */
167
169
  export function resolveEffectiveSandbox(config, capability = detectSandboxCapability()) {
170
+ let resolved = config;
168
171
  // Config disabled or tier is denylist-only => no OS sandbox
169
- if (!config.enabled || config.tier === 'denylist-only') {
172
+ if (!resolved.enabled || resolved.tier === 'denylist-only') {
170
173
  return {
171
174
  useOsSandbox: false,
172
175
  capability,
173
- config,
176
+ config: resolved,
174
177
  displayStatus: `OS sandbox: disabled (denylist active)`,
175
178
  };
176
179
  }
@@ -181,17 +184,16 @@ export function resolveEffectiveSandbox(config, capability = detectSandboxCapabi
181
184
  if (!capability.available) {
182
185
  throw new Error(formatWindowsDockerNotReadyMessage());
183
186
  }
184
- const image = config.dockerImage || RECOMMENDED_DOCKER_IMAGE;
185
- if (!config.dockerImage) {
186
- // Mutate config to carry the defaulted image through the rest of the pipeline
187
- config = { ...config, dockerImage: image };
187
+ const image = resolved.dockerImage || RECOMMENDED_DOCKER_IMAGE;
188
+ if (!resolved.dockerImage) {
189
+ resolved = { ...resolved, dockerImage: image };
188
190
  }
189
191
  if (!dockerImageExists(image)) {
190
192
  dockerPullImage(image);
191
193
  }
192
194
  }
193
195
  // tier: full — require OS sandbox on non-Windows platforms
194
- if (config.tier === 'full' && !capability.available) {
196
+ if (resolved.tier === 'full' && !capability.available) {
195
197
  throw new Error(`Sandbox tier "full" requires an OS sandbox but none was detected on ${capability.platform}. ` +
196
198
  `Install bubblewrap (Linux) or set sandbox.tier to "auto".`);
197
199
  }
@@ -199,24 +201,29 @@ export function resolveEffectiveSandbox(config, capability = detectSandboxCapabi
199
201
  return {
200
202
  useOsSandbox: false,
201
203
  capability,
202
- config,
204
+ config: resolved,
203
205
  displayStatus: `OS sandbox: not available (${capability.platform})`,
204
206
  };
205
207
  }
206
208
  return {
207
209
  useOsSandbox: true,
208
210
  capability,
209
- config,
211
+ config: resolved,
210
212
  displayStatus: `OS sandbox: ${capability.tool} (${capability.platform})`,
211
213
  };
212
214
  }
215
+ const _imageExistsCache = new Map();
213
216
  /**
214
217
  * Check whether a Docker image is available locally (already pulled).
215
- * Returns false on any error (daemon down, image missing, docker not in PATH).
218
+ * Result is cached per image name for the process lifetime.
216
219
  */
217
220
  function dockerImageExists(image) {
221
+ const cached = _imageExistsCache.get(image);
222
+ if (cached !== undefined)
223
+ return cached;
218
224
  try {
219
- execSync(`docker image inspect ${shellQuote(image)}`, { stdio: 'ignore', timeout: 5000 });
225
+ execSync(`docker image inspect ${escapeShellArg(image)}`, { stdio: 'ignore', timeout: 5000 });
226
+ _imageExistsCache.set(image, true);
220
227
  return true;
221
228
  }
222
229
  catch {
@@ -231,7 +238,8 @@ function dockerPullImage(image) {
231
238
  console.log(`[spell] One-time setup: pulling Docker image ${image} for sandboxing...\n` +
232
239
  ` This only happens once — Docker caches the image afterwards.`);
233
240
  try {
234
- execSync(`docker pull ${shellQuote(image)}`, { stdio: 'inherit', timeout: 300_000 });
241
+ execSync(`docker pull ${escapeShellArg(image)}`, { stdio: 'inherit', timeout: 300_000 });
242
+ _imageExistsCache.set(image, true);
235
243
  console.log(`[spell] Docker image ${image} is ready.`);
236
244
  }
237
245
  catch {
@@ -239,10 +247,6 @@ function dockerPullImage(image) {
239
247
  'Make sure Docker Desktop is running and you have internet access, then try again.');
240
248
  }
241
249
  }
242
- /** Minimal shell quoting for image names — keeps the execSync call safe. */
243
- function shellQuote(value) {
244
- return `"${value.replace(/"/g, '\\"')}"`;
245
- }
246
250
  // ── Beginner-friendly setup messages (Windows) ──────────────────────────
247
251
  function formatWindowsDockerNotReadyMessage() {
248
252
  return [
@@ -258,7 +262,7 @@ function formatWindowsDockerNotReadyMessage() {
258
262
  ' Wait for the whale icon in your system tray to stop animating —',
259
263
  ' that means Docker is ready.',
260
264
  '',
261
- `MoFlo will auto-pull the default image (${RECOMMENDED_DOCKER_IMAGE}) on`,
265
+ `MoFlo will auto-pull the sandbox image (${RECOMMENDED_DOCKER_IMAGE}) on`,
262
266
  'the first spell run.',
263
267
  '',
264
268
  'Not ready to set this up? Turn sandboxing off by setting',