@qlucent/fishi-core 0.6.0 → 0.8.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 +80 -1
- package/dist/index.js +309 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ChildProcess } from 'child_process';
|
|
2
|
+
|
|
1
3
|
type ProjectType = 'greenfield' | 'brownfield' | 'hybrid';
|
|
2
4
|
type CostMode = 'performance' | 'balanced' | 'economy';
|
|
3
5
|
type ModelTier = 'opus' | 'sonnet' | 'haiku';
|
|
@@ -568,6 +570,83 @@ declare function getAgentSummary(projectDir: string): Record<string, {
|
|
|
568
570
|
filesChanged: number;
|
|
569
571
|
}>;
|
|
570
572
|
|
|
573
|
+
type SandboxMode = 'docker' | 'process';
|
|
574
|
+
interface SandboxConfig {
|
|
575
|
+
mode: SandboxMode;
|
|
576
|
+
dockerAvailable: boolean;
|
|
577
|
+
}
|
|
578
|
+
interface SandboxPolicy {
|
|
579
|
+
networkAllow: string[];
|
|
580
|
+
envPassthrough: string[];
|
|
581
|
+
timeout: number;
|
|
582
|
+
memory: string;
|
|
583
|
+
cpus: number;
|
|
584
|
+
}
|
|
585
|
+
interface SandboxRunResult {
|
|
586
|
+
stdout: string;
|
|
587
|
+
stderr: string;
|
|
588
|
+
exitCode: number;
|
|
589
|
+
timedOut: boolean;
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Detect if Docker is installed and running.
|
|
593
|
+
*/
|
|
594
|
+
declare function detectDocker(): boolean;
|
|
595
|
+
/**
|
|
596
|
+
* Read sandbox config from .fishi/fishi.yaml
|
|
597
|
+
*/
|
|
598
|
+
declare function readSandboxConfig(projectDir: string): SandboxConfig;
|
|
599
|
+
/**
|
|
600
|
+
* Read sandbox policy from .fishi/sandbox-policy.yaml, or return defaults.
|
|
601
|
+
*/
|
|
602
|
+
declare function readSandboxPolicy(projectDir: string): SandboxPolicy;
|
|
603
|
+
/**
|
|
604
|
+
* Build a restricted env object for process mode.
|
|
605
|
+
* Strips all env vars except explicitly allowed ones + essentials.
|
|
606
|
+
*/
|
|
607
|
+
declare function buildSandboxEnv(policy: SandboxPolicy): Record<string, string>;
|
|
608
|
+
/**
|
|
609
|
+
* Run a command in process-mode sandbox.
|
|
610
|
+
*/
|
|
611
|
+
declare function runInProcessSandbox(command: string, args: string[], worktreePath: string, policy: SandboxPolicy): SandboxRunResult;
|
|
612
|
+
/**
|
|
613
|
+
* Run a command in Docker sandbox.
|
|
614
|
+
*/
|
|
615
|
+
declare function runInDockerSandbox(command: string, args: string[], worktreePath: string, policy: SandboxPolicy, options?: {
|
|
616
|
+
nodeModulesPath?: string;
|
|
617
|
+
}): SandboxRunResult;
|
|
618
|
+
/**
|
|
619
|
+
* Run a command in the configured sandbox mode.
|
|
620
|
+
*/
|
|
621
|
+
declare function runInSandbox(command: string, args: string[], worktreePath: string, projectDir: string, options?: {
|
|
622
|
+
nodeModulesPath?: string;
|
|
623
|
+
}): SandboxRunResult;
|
|
624
|
+
|
|
625
|
+
interface DevServerConfig {
|
|
626
|
+
command: string;
|
|
627
|
+
args: string[];
|
|
628
|
+
port: number;
|
|
629
|
+
framework: string;
|
|
630
|
+
detected: boolean;
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Detect the dev server command from package.json scripts or framework detection.
|
|
634
|
+
*/
|
|
635
|
+
declare function detectDevServer(projectDir: string, customCmd?: string): DevServerConfig;
|
|
636
|
+
/**
|
|
637
|
+
* Start the dev server as a background process.
|
|
638
|
+
* Returns the child process handle.
|
|
639
|
+
*/
|
|
640
|
+
declare function startDevServer(projectDir: string, config: DevServerConfig): ChildProcess;
|
|
641
|
+
/**
|
|
642
|
+
* Get the vibe mode config for fishi.yaml.
|
|
643
|
+
*/
|
|
644
|
+
declare function getVibeModeConfig(enabled: boolean): string;
|
|
645
|
+
|
|
646
|
+
declare function getSandboxPolicyTemplate(): string;
|
|
647
|
+
|
|
648
|
+
declare function getDockerfileTemplate(): string;
|
|
649
|
+
|
|
571
650
|
declare function getDashboardHtml(): string;
|
|
572
651
|
|
|
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 };
|
|
652
|
+
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 DevServerConfig, 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, detectDevServer, 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, getVibeModeConfig, getWorktreeManagerScript, getWorktreeSetupHook, marketingAgentTemplate, mergeClaudeMd, mergeClaudeMdTop, mergeGitignore, mergeMcpJson, mergeSettingsJson, opsLeadTemplate, planningAgentTemplate, planningLeadTemplate, qualityLeadTemplate, readMonitorState, readSandboxConfig, readSandboxPolicy, researchAgentTemplate, runInDockerSandbox, runInProcessSandbox, runInSandbox, securityAgentTemplate, startDevServer, 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.
|
|
11114
|
+
const fishiVersion = "0.8.0";
|
|
11115
11115
|
const manifest = {
|
|
11116
11116
|
timestamp: now.toISOString(),
|
|
11117
11117
|
fishi_version: fishiVersion,
|
|
@@ -11218,6 +11218,302 @@ 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/generators/preview-server.ts
|
|
11372
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
|
|
11373
|
+
import { join as join6 } from "path";
|
|
11374
|
+
import { spawn as spawn2 } from "child_process";
|
|
11375
|
+
function detectDevServer(projectDir, customCmd) {
|
|
11376
|
+
if (customCmd) {
|
|
11377
|
+
const parts = customCmd.split(/\s+/);
|
|
11378
|
+
return {
|
|
11379
|
+
command: parts[0],
|
|
11380
|
+
args: parts.slice(1),
|
|
11381
|
+
port: 3e3,
|
|
11382
|
+
framework: "custom",
|
|
11383
|
+
detected: true
|
|
11384
|
+
};
|
|
11385
|
+
}
|
|
11386
|
+
const pkgPath = join6(projectDir, "package.json");
|
|
11387
|
+
if (!existsSync6(pkgPath)) {
|
|
11388
|
+
if (existsSync6(join6(projectDir, "manage.py"))) {
|
|
11389
|
+
return { command: "python", args: ["manage.py", "runserver"], port: 8e3, framework: "django", detected: true };
|
|
11390
|
+
}
|
|
11391
|
+
if (existsSync6(join6(projectDir, "requirements.txt"))) {
|
|
11392
|
+
const reqs = readFileSync3(join6(projectDir, "requirements.txt"), "utf-8");
|
|
11393
|
+
if (reqs.includes("flask")) return { command: "python", args: ["-m", "flask", "run"], port: 5e3, framework: "flask", detected: true };
|
|
11394
|
+
if (reqs.includes("fastapi")) return { command: "uvicorn", args: ["main:app", "--reload"], port: 8e3, framework: "fastapi", detected: true };
|
|
11395
|
+
}
|
|
11396
|
+
return { command: "", args: [], port: 3e3, framework: "unknown", detected: false };
|
|
11397
|
+
}
|
|
11398
|
+
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
11399
|
+
const scripts = pkg.scripts || {};
|
|
11400
|
+
if (scripts.dev) {
|
|
11401
|
+
const devCmd = scripts.dev;
|
|
11402
|
+
const port = extractPort(devCmd) || 3e3;
|
|
11403
|
+
const framework = detectFrameworkFromCmd(devCmd, pkg);
|
|
11404
|
+
return { command: "npm", args: ["run", "dev"], port, framework, detected: true };
|
|
11405
|
+
}
|
|
11406
|
+
if (scripts.start) {
|
|
11407
|
+
const port = extractPort(scripts.start) || 3e3;
|
|
11408
|
+
const framework = detectFrameworkFromCmd(scripts.start, pkg);
|
|
11409
|
+
return { command: "npm", args: ["run", "start"], port, framework, detected: true };
|
|
11410
|
+
}
|
|
11411
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
11412
|
+
if (deps["next"]) return { command: "npx", args: ["next", "dev"], port: 3e3, framework: "nextjs", detected: true };
|
|
11413
|
+
if (deps["vite"]) return { command: "npx", args: ["vite"], port: 5173, framework: "vite", detected: true };
|
|
11414
|
+
if (deps["astro"]) return { command: "npx", args: ["astro", "dev"], port: 4321, framework: "astro", detected: true };
|
|
11415
|
+
if (deps["nuxt"]) return { command: "npx", args: ["nuxt", "dev"], port: 3e3, framework: "nuxt", detected: true };
|
|
11416
|
+
if (deps["svelte-kit"] || deps["@sveltejs/kit"]) return { command: "npx", args: ["vite", "dev"], port: 5173, framework: "sveltekit", detected: true };
|
|
11417
|
+
if (deps["gatsby"]) return { command: "npx", args: ["gatsby", "develop"], port: 8e3, framework: "gatsby", detected: true };
|
|
11418
|
+
if (deps["remix"] || deps["@remix-run/dev"]) return { command: "npx", args: ["remix", "vite:dev"], port: 5173, framework: "remix", detected: true };
|
|
11419
|
+
if (deps["express"]) return { command: "node", args: ["index.js"], port: 3e3, framework: "express", detected: true };
|
|
11420
|
+
if (existsSync6(join6(projectDir, "manage.py"))) {
|
|
11421
|
+
return { command: "python", args: ["manage.py", "runserver"], port: 8e3, framework: "django", detected: true };
|
|
11422
|
+
}
|
|
11423
|
+
if (existsSync6(join6(projectDir, "requirements.txt"))) {
|
|
11424
|
+
const reqs = readFileSync3(join6(projectDir, "requirements.txt"), "utf-8");
|
|
11425
|
+
if (reqs.includes("flask")) return { command: "python", args: ["-m", "flask", "run"], port: 5e3, framework: "flask", detected: true };
|
|
11426
|
+
if (reqs.includes("fastapi")) return { command: "uvicorn", args: ["main:app", "--reload"], port: 8e3, framework: "fastapi", detected: true };
|
|
11427
|
+
}
|
|
11428
|
+
return { command: "", args: [], port: 3e3, framework: "unknown", detected: false };
|
|
11429
|
+
}
|
|
11430
|
+
function extractPort(cmd) {
|
|
11431
|
+
const portMatch = cmd.match(/(?:--port|-p)\s+(\d+)/) || cmd.match(/PORT=(\d+)/) || cmd.match(/:(\d{4,5})/);
|
|
11432
|
+
return portMatch ? parseInt(portMatch[1], 10) : null;
|
|
11433
|
+
}
|
|
11434
|
+
function detectFrameworkFromCmd(cmd, _pkg) {
|
|
11435
|
+
if (cmd.includes("next")) return "nextjs";
|
|
11436
|
+
if (cmd.includes("vite")) return "vite";
|
|
11437
|
+
if (cmd.includes("astro")) return "astro";
|
|
11438
|
+
if (cmd.includes("nuxt")) return "nuxt";
|
|
11439
|
+
if (cmd.includes("gatsby")) return "gatsby";
|
|
11440
|
+
if (cmd.includes("remix")) return "remix";
|
|
11441
|
+
if (cmd.includes("svelte")) return "sveltekit";
|
|
11442
|
+
if (cmd.includes("express") || cmd.includes("node server")) return "express";
|
|
11443
|
+
if (cmd.includes("flask")) return "flask";
|
|
11444
|
+
if (cmd.includes("django") || cmd.includes("manage.py")) return "django";
|
|
11445
|
+
if (cmd.includes("uvicorn") || cmd.includes("fastapi")) return "fastapi";
|
|
11446
|
+
return "unknown";
|
|
11447
|
+
}
|
|
11448
|
+
function startDevServer(projectDir, config) {
|
|
11449
|
+
const child = spawn2(config.command, config.args, {
|
|
11450
|
+
cwd: projectDir,
|
|
11451
|
+
stdio: "pipe",
|
|
11452
|
+
detached: false,
|
|
11453
|
+
env: { ...process.env, PORT: String(config.port) },
|
|
11454
|
+
shell: true
|
|
11455
|
+
});
|
|
11456
|
+
return child;
|
|
11457
|
+
}
|
|
11458
|
+
function getVibeModeConfig(enabled) {
|
|
11459
|
+
return `
|
|
11460
|
+
vibe_mode:
|
|
11461
|
+
enabled: ${enabled}
|
|
11462
|
+
auto_approve_gates: ${enabled}
|
|
11463
|
+
auto_generate_prd: ${enabled}
|
|
11464
|
+
background_testing: ${enabled}
|
|
11465
|
+
dev_server_autostart: ${enabled}
|
|
11466
|
+
`;
|
|
11467
|
+
}
|
|
11468
|
+
|
|
11469
|
+
// src/templates/configs/sandbox-policy.ts
|
|
11470
|
+
function getSandboxPolicyTemplate() {
|
|
11471
|
+
return `# FISHI Sandbox Policy
|
|
11472
|
+
# Controls what agents can access inside their sandboxed worktrees
|
|
11473
|
+
|
|
11474
|
+
# Network domains agents are allowed to reach
|
|
11475
|
+
network_allow:
|
|
11476
|
+
- registry.npmjs.org
|
|
11477
|
+
- localhost
|
|
11478
|
+
- 127.0.0.1
|
|
11479
|
+
|
|
11480
|
+
# Environment variables passed into the sandbox
|
|
11481
|
+
# Add secrets your agents need (e.g., DATABASE_URL, API_KEY)
|
|
11482
|
+
env_passthrough: []
|
|
11483
|
+
|
|
11484
|
+
# Maximum time (seconds) a single agent command can run
|
|
11485
|
+
timeout: 600
|
|
11486
|
+
|
|
11487
|
+
# Docker resource limits (only applies in docker mode)
|
|
11488
|
+
memory: "2g"
|
|
11489
|
+
cpus: 2
|
|
11490
|
+
`;
|
|
11491
|
+
}
|
|
11492
|
+
|
|
11493
|
+
// src/templates/docker/Dockerfile.ts
|
|
11494
|
+
function getDockerfileTemplate() {
|
|
11495
|
+
return `# FISHI Sandbox Runtime
|
|
11496
|
+
# Minimal Node.js image for agent execution
|
|
11497
|
+
FROM node:22-slim
|
|
11498
|
+
|
|
11499
|
+
# Install git and common build tools
|
|
11500
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \\
|
|
11501
|
+
git \\
|
|
11502
|
+
ca-certificates \\
|
|
11503
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
11504
|
+
|
|
11505
|
+
# Set working directory
|
|
11506
|
+
WORKDIR /workspace
|
|
11507
|
+
|
|
11508
|
+
# Non-root user for additional safety
|
|
11509
|
+
RUN groupadd -r fishi && useradd -r -g fishi -m fishi
|
|
11510
|
+
USER fishi
|
|
11511
|
+
|
|
11512
|
+
# Default command
|
|
11513
|
+
CMD ["node"]
|
|
11514
|
+
`;
|
|
11515
|
+
}
|
|
11516
|
+
|
|
11221
11517
|
// src/templates/dashboard/index-html.ts
|
|
11222
11518
|
function getDashboardHtml() {
|
|
11223
11519
|
return `<!DOCTYPE html>
|
|
@@ -11564,8 +11860,11 @@ function getDashboardHtml() {
|
|
|
11564
11860
|
export {
|
|
11565
11861
|
architectAgentTemplate,
|
|
11566
11862
|
backendAgentTemplate,
|
|
11863
|
+
buildSandboxEnv,
|
|
11567
11864
|
createBackup,
|
|
11568
11865
|
detectConflicts,
|
|
11866
|
+
detectDevServer,
|
|
11867
|
+
detectDocker,
|
|
11569
11868
|
devLeadTemplate,
|
|
11570
11869
|
devopsAgentTemplate,
|
|
11571
11870
|
docsAgentTemplate,
|
|
@@ -11591,6 +11890,7 @@ export {
|
|
|
11591
11890
|
getDebuggingSkill,
|
|
11592
11891
|
getDeploymentSkill,
|
|
11593
11892
|
getDocCheckerScript,
|
|
11893
|
+
getDockerfileTemplate,
|
|
11594
11894
|
getDocumentationSkill,
|
|
11595
11895
|
getFishiYamlTemplate,
|
|
11596
11896
|
getGateCommand,
|
|
@@ -11611,6 +11911,7 @@ export {
|
|
|
11611
11911
|
getResetCommand,
|
|
11612
11912
|
getResumeCommand,
|
|
11613
11913
|
getSafetyCheckHook,
|
|
11914
|
+
getSandboxPolicyTemplate,
|
|
11614
11915
|
getSessionStartHook,
|
|
11615
11916
|
getSettingsJsonTemplate,
|
|
11616
11917
|
getSprintCommand,
|
|
@@ -11620,6 +11921,7 @@ export {
|
|
|
11620
11921
|
getTestingSkill,
|
|
11621
11922
|
getTodoManagerScript,
|
|
11622
11923
|
getValidateScaffoldScript,
|
|
11924
|
+
getVibeModeConfig,
|
|
11623
11925
|
getWorktreeManagerScript,
|
|
11624
11926
|
getWorktreeSetupHook,
|
|
11625
11927
|
marketingAgentTemplate,
|
|
@@ -11633,8 +11935,14 @@ export {
|
|
|
11633
11935
|
planningLeadTemplate,
|
|
11634
11936
|
qualityLeadTemplate,
|
|
11635
11937
|
readMonitorState,
|
|
11938
|
+
readSandboxConfig,
|
|
11939
|
+
readSandboxPolicy,
|
|
11636
11940
|
researchAgentTemplate,
|
|
11941
|
+
runInDockerSandbox,
|
|
11942
|
+
runInProcessSandbox,
|
|
11943
|
+
runInSandbox,
|
|
11637
11944
|
securityAgentTemplate,
|
|
11945
|
+
startDevServer,
|
|
11638
11946
|
testingAgentTemplate,
|
|
11639
11947
|
uiuxAgentTemplate,
|
|
11640
11948
|
writingAgentTemplate
|