@qlucent/fishi-core 0.6.0 → 0.7.0

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/index.d.ts CHANGED
@@ -568,6 +568,62 @@ declare function getAgentSummary(projectDir: string): Record<string, {
568
568
  filesChanged: number;
569
569
  }>;
570
570
 
571
+ type SandboxMode = 'docker' | 'process';
572
+ interface SandboxConfig {
573
+ mode: SandboxMode;
574
+ dockerAvailable: boolean;
575
+ }
576
+ interface SandboxPolicy {
577
+ networkAllow: string[];
578
+ envPassthrough: string[];
579
+ timeout: number;
580
+ memory: string;
581
+ cpus: number;
582
+ }
583
+ interface SandboxRunResult {
584
+ stdout: string;
585
+ stderr: string;
586
+ exitCode: number;
587
+ timedOut: boolean;
588
+ }
589
+ /**
590
+ * Detect if Docker is installed and running.
591
+ */
592
+ declare function detectDocker(): boolean;
593
+ /**
594
+ * Read sandbox config from .fishi/fishi.yaml
595
+ */
596
+ declare function readSandboxConfig(projectDir: string): SandboxConfig;
597
+ /**
598
+ * Read sandbox policy from .fishi/sandbox-policy.yaml, or return defaults.
599
+ */
600
+ declare function readSandboxPolicy(projectDir: string): SandboxPolicy;
601
+ /**
602
+ * Build a restricted env object for process mode.
603
+ * Strips all env vars except explicitly allowed ones + essentials.
604
+ */
605
+ declare function buildSandboxEnv(policy: SandboxPolicy): Record<string, string>;
606
+ /**
607
+ * Run a command in process-mode sandbox.
608
+ */
609
+ declare function runInProcessSandbox(command: string, args: string[], worktreePath: string, policy: SandboxPolicy): SandboxRunResult;
610
+ /**
611
+ * Run a command in Docker sandbox.
612
+ */
613
+ declare function runInDockerSandbox(command: string, args: string[], worktreePath: string, policy: SandboxPolicy, options?: {
614
+ nodeModulesPath?: string;
615
+ }): SandboxRunResult;
616
+ /**
617
+ * Run a command in the configured sandbox mode.
618
+ */
619
+ declare function runInSandbox(command: string, args: string[], worktreePath: string, projectDir: string, options?: {
620
+ nodeModulesPath?: string;
621
+ }): SandboxRunResult;
622
+
623
+ declare function getSandboxPolicyTemplate(): string;
624
+
625
+ declare function getDockerfileTemplate(): string;
626
+
571
627
  declare function getDashboardHtml(): string;
572
628
 
573
- export { type AgentDefinition, type AgentRole, type AgentTemplate, type BackupManifest, type BrownfieldAnalysisData, type ClaudeMdOptions, type CommandTemplate, type ConflictCategory, type ConflictMap, type ConflictResolution, type CostMode, type DetectionCheck, type DetectionResult, type DynamicAgent, type DynamicAgentConfig, type ExecutionConfig, type FileConflict, type FileResolutionMap, type FishiConfig, type FishiYamlOptions, type GateConfig, type GateStatus, type GitConfig, type HookTemplate, type InitOptions, type McpConfig, type McpServerConfig, type ModelRoutingConfig, type ModelTier, type MonitorEvent, type MonitorState, type MonitorSummary, type ProjectConfig, type ProjectType, type ProjectYamlOptions, type ScaffoldOptions, type ScaffoldResult, type SkillTemplate, type StateConfig, type TaskStatus, type TaskboardConfig, type TemplateContext, architectAgentTemplate, backendAgentTemplate, createBackup, detectConflicts, devLeadTemplate, devopsAgentTemplate, docsAgentTemplate, emitEvent, frontendAgentTemplate, fullstackAgentTemplate, generateScaffold, getAdaptiveTaskGraphSkill, getAgentCompleteHook, getAgentFactoryTemplate, getAgentRegistryTemplate, getAgentSummary, getApiDesignSkill, getAutoCheckpointHook, getBoardCommand, getBrainstormingSkill, getBrownfieldAnalysisSkill, getBrownfieldDiscoverySkill, getClaudeMdTemplate, getCodeGenSkill, getCoordinatorFactoryTemplate, getDashboardHtml, getDebuggingSkill, getDeploymentSkill, getDocCheckerScript, getDocumentationSkill, getFishiYamlTemplate, getGateCommand, getGateManagerScript, getGitignoreAdditions, getInitCommand, getLearningsManagerScript, getMasterOrchestratorTemplate, getMcpJsonTemplate, getMemoryManagerScript, getModelRoutingReference, getMonitorEmitterScript, getPhaseRunnerScript, getPostEditHook, getPrdCommand, getPrdSkill, getProjectYamlTemplate, getResetCommand, getResumeCommand, getSafetyCheckHook, getSessionStartHook, getSettingsJsonTemplate, getSprintCommand, getStatusCommand, getTaskboardOpsSkill, getTaskboardUpdateHook, getTestingSkill, getTodoManagerScript, getValidateScaffoldScript, getWorktreeManagerScript, getWorktreeSetupHook, marketingAgentTemplate, mergeClaudeMd, mergeClaudeMdTop, mergeGitignore, mergeMcpJson, mergeSettingsJson, opsLeadTemplate, planningAgentTemplate, planningLeadTemplate, qualityLeadTemplate, readMonitorState, researchAgentTemplate, securityAgentTemplate, testingAgentTemplate, uiuxAgentTemplate, writingAgentTemplate };
629
+ export { type AgentDefinition, type AgentRole, type AgentTemplate, type BackupManifest, type BrownfieldAnalysisData, type ClaudeMdOptions, type CommandTemplate, type ConflictCategory, type ConflictMap, type ConflictResolution, type CostMode, type DetectionCheck, type DetectionResult, type DynamicAgent, type DynamicAgentConfig, type ExecutionConfig, type FileConflict, type FileResolutionMap, type FishiConfig, type FishiYamlOptions, type GateConfig, type GateStatus, type GitConfig, type HookTemplate, type InitOptions, type McpConfig, type McpServerConfig, type ModelRoutingConfig, type ModelTier, type MonitorEvent, type MonitorState, type MonitorSummary, type ProjectConfig, type ProjectType, type ProjectYamlOptions, type SandboxConfig, type SandboxMode, type SandboxPolicy, type SandboxRunResult, type ScaffoldOptions, type ScaffoldResult, type SkillTemplate, type StateConfig, type TaskStatus, type TaskboardConfig, type TemplateContext, architectAgentTemplate, backendAgentTemplate, buildSandboxEnv, createBackup, detectConflicts, detectDocker, devLeadTemplate, devopsAgentTemplate, docsAgentTemplate, emitEvent, frontendAgentTemplate, fullstackAgentTemplate, generateScaffold, getAdaptiveTaskGraphSkill, getAgentCompleteHook, getAgentFactoryTemplate, getAgentRegistryTemplate, getAgentSummary, getApiDesignSkill, getAutoCheckpointHook, getBoardCommand, getBrainstormingSkill, getBrownfieldAnalysisSkill, getBrownfieldDiscoverySkill, getClaudeMdTemplate, getCodeGenSkill, getCoordinatorFactoryTemplate, getDashboardHtml, getDebuggingSkill, getDeploymentSkill, getDocCheckerScript, getDockerfileTemplate, getDocumentationSkill, getFishiYamlTemplate, getGateCommand, getGateManagerScript, getGitignoreAdditions, getInitCommand, getLearningsManagerScript, getMasterOrchestratorTemplate, getMcpJsonTemplate, getMemoryManagerScript, getModelRoutingReference, getMonitorEmitterScript, getPhaseRunnerScript, getPostEditHook, getPrdCommand, getPrdSkill, getProjectYamlTemplate, getResetCommand, getResumeCommand, getSafetyCheckHook, getSandboxPolicyTemplate, getSessionStartHook, getSettingsJsonTemplate, getSprintCommand, getStatusCommand, getTaskboardOpsSkill, getTaskboardUpdateHook, getTestingSkill, getTodoManagerScript, getValidateScaffoldScript, getWorktreeManagerScript, getWorktreeSetupHook, marketingAgentTemplate, mergeClaudeMd, mergeClaudeMdTop, mergeGitignore, mergeMcpJson, mergeSettingsJson, opsLeadTemplate, planningAgentTemplate, planningLeadTemplate, qualityLeadTemplate, readMonitorState, readSandboxConfig, readSandboxPolicy, researchAgentTemplate, runInDockerSandbox, runInProcessSandbox, runInSandbox, securityAgentTemplate, testingAgentTemplate, uiuxAgentTemplate, writingAgentTemplate };
package/dist/index.js CHANGED
@@ -11111,7 +11111,7 @@ async function createBackup(targetDir, conflictingFiles) {
11111
11111
  manifestFiles.push({ path: relPath, size: stat.size });
11112
11112
  }
11113
11113
  }
11114
- const fishiVersion = "0.6.0";
11114
+ const fishiVersion = "0.7.0";
11115
11115
  const manifest = {
11116
11116
  timestamp: now.toISOString(),
11117
11117
  fishi_version: fishiVersion,
@@ -11218,6 +11218,204 @@ function getAgentSummary(projectDir) {
11218
11218
  return agents;
11219
11219
  }
11220
11220
 
11221
+ // src/generators/sandbox.ts
11222
+ import { execSync, execFileSync } from "child_process";
11223
+ import { existsSync as existsSync5, readFileSync as readFileSync2 } from "fs";
11224
+ import { join as join5 } from "path";
11225
+ var DEFAULT_POLICY = {
11226
+ networkAllow: ["registry.npmjs.org", "localhost", "127.0.0.1"],
11227
+ envPassthrough: [],
11228
+ timeout: 600,
11229
+ // 10 minutes
11230
+ memory: "2g",
11231
+ cpus: 2
11232
+ };
11233
+ function detectDocker() {
11234
+ try {
11235
+ execSync("docker info", { stdio: "pipe", timeout: 5e3 });
11236
+ return true;
11237
+ } catch {
11238
+ return false;
11239
+ }
11240
+ }
11241
+ function readSandboxConfig(projectDir) {
11242
+ const yamlPath = join5(projectDir, ".fishi", "fishi.yaml");
11243
+ if (!existsSync5(yamlPath)) {
11244
+ return { mode: "process", dockerAvailable: false };
11245
+ }
11246
+ const content = readFileSync2(yamlPath, "utf-8");
11247
+ const modeMatch = content.match(/^\s*mode:\s*(docker|process)/m);
11248
+ const dockerMatch = content.match(/^\s*docker_available:\s*(true|false)/m);
11249
+ return {
11250
+ mode: modeMatch?.[1] || "process",
11251
+ dockerAvailable: dockerMatch?.[1] === "true"
11252
+ };
11253
+ }
11254
+ function readSandboxPolicy(projectDir) {
11255
+ const policyPath = join5(projectDir, ".fishi", "sandbox-policy.yaml");
11256
+ if (!existsSync5(policyPath)) return { ...DEFAULT_POLICY };
11257
+ const content = readFileSync2(policyPath, "utf-8");
11258
+ const networkAllow = extractYamlList(content, "network_allow") || DEFAULT_POLICY.networkAllow;
11259
+ const envPassthrough = extractYamlList(content, "env_passthrough") || DEFAULT_POLICY.envPassthrough;
11260
+ const timeoutMatch = content.match(/^\s*timeout:\s*(\d+)/m);
11261
+ const memoryMatch = content.match(/^\s*memory:\s*["']?(\S+?)["']?\s*$/m);
11262
+ const cpusMatch = content.match(/^\s*cpus:\s*(\d+)/m);
11263
+ return {
11264
+ networkAllow,
11265
+ envPassthrough,
11266
+ timeout: timeoutMatch ? parseInt(timeoutMatch[1], 10) : DEFAULT_POLICY.timeout,
11267
+ memory: memoryMatch?.[1] || DEFAULT_POLICY.memory,
11268
+ cpus: cpusMatch ? parseInt(cpusMatch[1], 10) : DEFAULT_POLICY.cpus
11269
+ };
11270
+ }
11271
+ function extractYamlList(content, key) {
11272
+ const regex = new RegExp(`^\\s*${key}:\\s*\\n((?:\\s+-\\s*.+\\n?)*)`, "m");
11273
+ const match = content.match(regex);
11274
+ if (!match) return null;
11275
+ return match[1].split("\n").map((line) => {
11276
+ const m = line.match(/^\s*-\s*["']?(.+?)["']?\s*$/);
11277
+ return m ? m[1] : "";
11278
+ }).filter(Boolean);
11279
+ }
11280
+ function buildSandboxEnv(policy) {
11281
+ const env = {
11282
+ PATH: process.env.PATH || "",
11283
+ HOME: process.env.HOME || process.env.USERPROFILE || "",
11284
+ NODE_ENV: "development",
11285
+ FISHI_SANDBOX: "true"
11286
+ };
11287
+ for (const key of policy.envPassthrough) {
11288
+ if (process.env[key]) {
11289
+ env[key] = process.env[key];
11290
+ }
11291
+ }
11292
+ return env;
11293
+ }
11294
+ function runInProcessSandbox(command, args, worktreePath, policy) {
11295
+ const env = buildSandboxEnv(policy);
11296
+ try {
11297
+ const stdout = execFileSync(command, args, {
11298
+ cwd: worktreePath,
11299
+ encoding: "utf-8",
11300
+ timeout: policy.timeout * 1e3,
11301
+ env,
11302
+ stdio: "pipe",
11303
+ maxBuffer: 10 * 1024 * 1024
11304
+ // 10MB
11305
+ });
11306
+ return { stdout, stderr: "", exitCode: 0, timedOut: false };
11307
+ } catch (e) {
11308
+ if (e.killed || e.signal === "SIGTERM") {
11309
+ return { stdout: e.stdout || "", stderr: e.stderr || "", exitCode: 1, timedOut: true };
11310
+ }
11311
+ return {
11312
+ stdout: e.stdout || "",
11313
+ stderr: e.stderr || "",
11314
+ exitCode: e.status ?? 1,
11315
+ timedOut: false
11316
+ };
11317
+ }
11318
+ }
11319
+ function runInDockerSandbox(command, args, worktreePath, policy, options = {}) {
11320
+ const dockerArgs = [
11321
+ "run",
11322
+ "--rm",
11323
+ "--workdir",
11324
+ "/workspace",
11325
+ "-v",
11326
+ `${worktreePath}:/workspace`
11327
+ ];
11328
+ if (options.nodeModulesPath && existsSync5(options.nodeModulesPath)) {
11329
+ dockerArgs.push("-v", `${options.nodeModulesPath}:/workspace/node_modules:ro`);
11330
+ }
11331
+ dockerArgs.push("--memory", policy.memory);
11332
+ dockerArgs.push("--cpus", String(policy.cpus));
11333
+ dockerArgs.push("-e", "FISHI_SANDBOX=true");
11334
+ dockerArgs.push("-e", "NODE_ENV=development");
11335
+ for (const key of policy.envPassthrough) {
11336
+ if (process.env[key]) {
11337
+ dockerArgs.push("-e", `${key}=${process.env[key]}`);
11338
+ }
11339
+ }
11340
+ dockerArgs.push("--network", "host");
11341
+ dockerArgs.push("fishi-sandbox:latest", command, ...args);
11342
+ try {
11343
+ const stdout = execFileSync("docker", dockerArgs, {
11344
+ encoding: "utf-8",
11345
+ timeout: policy.timeout * 1e3,
11346
+ stdio: "pipe",
11347
+ maxBuffer: 10 * 1024 * 1024
11348
+ });
11349
+ return { stdout, stderr: "", exitCode: 0, timedOut: false };
11350
+ } catch (e) {
11351
+ if (e.killed || e.signal === "SIGTERM") {
11352
+ return { stdout: e.stdout || "", stderr: e.stderr || "", exitCode: 1, timedOut: true };
11353
+ }
11354
+ return {
11355
+ stdout: e.stdout || "",
11356
+ stderr: e.stderr || "",
11357
+ exitCode: e.status ?? 1,
11358
+ timedOut: false
11359
+ };
11360
+ }
11361
+ }
11362
+ function runInSandbox(command, args, worktreePath, projectDir, options = {}) {
11363
+ const config = readSandboxConfig(projectDir);
11364
+ const policy = readSandboxPolicy(projectDir);
11365
+ if (config.mode === "docker" && config.dockerAvailable) {
11366
+ return runInDockerSandbox(command, args, worktreePath, policy, options);
11367
+ }
11368
+ return runInProcessSandbox(command, args, worktreePath, policy);
11369
+ }
11370
+
11371
+ // src/templates/configs/sandbox-policy.ts
11372
+ function getSandboxPolicyTemplate() {
11373
+ return `# FISHI Sandbox Policy
11374
+ # Controls what agents can access inside their sandboxed worktrees
11375
+
11376
+ # Network domains agents are allowed to reach
11377
+ network_allow:
11378
+ - registry.npmjs.org
11379
+ - localhost
11380
+ - 127.0.0.1
11381
+
11382
+ # Environment variables passed into the sandbox
11383
+ # Add secrets your agents need (e.g., DATABASE_URL, API_KEY)
11384
+ env_passthrough: []
11385
+
11386
+ # Maximum time (seconds) a single agent command can run
11387
+ timeout: 600
11388
+
11389
+ # Docker resource limits (only applies in docker mode)
11390
+ memory: "2g"
11391
+ cpus: 2
11392
+ `;
11393
+ }
11394
+
11395
+ // src/templates/docker/Dockerfile.ts
11396
+ function getDockerfileTemplate() {
11397
+ return `# FISHI Sandbox Runtime
11398
+ # Minimal Node.js image for agent execution
11399
+ FROM node:22-slim
11400
+
11401
+ # Install git and common build tools
11402
+ RUN apt-get update && apt-get install -y --no-install-recommends \\
11403
+ git \\
11404
+ ca-certificates \\
11405
+ && rm -rf /var/lib/apt/lists/*
11406
+
11407
+ # Set working directory
11408
+ WORKDIR /workspace
11409
+
11410
+ # Non-root user for additional safety
11411
+ RUN groupadd -r fishi && useradd -r -g fishi -m fishi
11412
+ USER fishi
11413
+
11414
+ # Default command
11415
+ CMD ["node"]
11416
+ `;
11417
+ }
11418
+
11221
11419
  // src/templates/dashboard/index-html.ts
11222
11420
  function getDashboardHtml() {
11223
11421
  return `<!DOCTYPE html>
@@ -11564,8 +11762,10 @@ function getDashboardHtml() {
11564
11762
  export {
11565
11763
  architectAgentTemplate,
11566
11764
  backendAgentTemplate,
11765
+ buildSandboxEnv,
11567
11766
  createBackup,
11568
11767
  detectConflicts,
11768
+ detectDocker,
11569
11769
  devLeadTemplate,
11570
11770
  devopsAgentTemplate,
11571
11771
  docsAgentTemplate,
@@ -11591,6 +11791,7 @@ export {
11591
11791
  getDebuggingSkill,
11592
11792
  getDeploymentSkill,
11593
11793
  getDocCheckerScript,
11794
+ getDockerfileTemplate,
11594
11795
  getDocumentationSkill,
11595
11796
  getFishiYamlTemplate,
11596
11797
  getGateCommand,
@@ -11611,6 +11812,7 @@ export {
11611
11812
  getResetCommand,
11612
11813
  getResumeCommand,
11613
11814
  getSafetyCheckHook,
11815
+ getSandboxPolicyTemplate,
11614
11816
  getSessionStartHook,
11615
11817
  getSettingsJsonTemplate,
11616
11818
  getSprintCommand,
@@ -11633,7 +11835,12 @@ export {
11633
11835
  planningLeadTemplate,
11634
11836
  qualityLeadTemplate,
11635
11837
  readMonitorState,
11838
+ readSandboxConfig,
11839
+ readSandboxPolicy,
11636
11840
  researchAgentTemplate,
11841
+ runInDockerSandbox,
11842
+ runInProcessSandbox,
11843
+ runInSandbox,
11637
11844
  securityAgentTemplate,
11638
11845
  testingAgentTemplate,
11639
11846
  uiuxAgentTemplate,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qlucent/fishi-core",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Shared templates, types, and generators for the FISHI framework",
5
5
  "license": "MIT",
6
6
  "type": "module",